103 lines
3.5 KiB
TypeScript
103 lines
3.5 KiB
TypeScript
import { observer } from "mobx-react-lite";
|
|
import type { ObjectInstance } from "../types";
|
|
import { useEffect, useRef, type RefObject } from "react";
|
|
import type { Group } from "three";
|
|
import { TransformControls, useHelper } from "@react-three/drei";
|
|
import { BoxHelper } from "three";
|
|
import type { ThreeEvent } from "@react-three/fiber";
|
|
import { state } from "../state";
|
|
import { nextSelectionEditMode } from "../state/worldEditorState";
|
|
import type { R3 } from "../types/3d";
|
|
|
|
type ObjectViewProps = {
|
|
object: ObjectInstance;
|
|
}
|
|
|
|
export const ObjectView = observer(function ({ object }: ObjectViewProps) {
|
|
const groupRef = useRef<Group>(null);
|
|
const controlsRef = useRef<any>(null);
|
|
|
|
const isSelected = state.worldEditor.isEnabled &&
|
|
state.worldEditor.selectedObjectId === object.id;
|
|
|
|
useHelper(isSelected ? groupRef : { current: null } as any, BoxHelper, 'white');
|
|
|
|
const objectType = state.world.getObjectTypeById(object.typeId);
|
|
if (!objectType)
|
|
return null;
|
|
const selectionMode = isSelected
|
|
? state.worldEditor.selectedObjectMode
|
|
: undefined;
|
|
|
|
useEffect(() => {
|
|
if (!isSelected)
|
|
return;
|
|
const controls = controlsRef.current;
|
|
if (!controls)
|
|
return;
|
|
const onDragChanged = (e: { value: boolean }) => {
|
|
state.worldEditor.setIsDragging(e.value);
|
|
if (!e.value) {
|
|
const group = groupRef.current;
|
|
if (group)
|
|
state.worldEditor.setObjectTransform(
|
|
object.id,
|
|
group.position.toArray(),
|
|
group.rotation.toArray().slice(0, 3) as R3,
|
|
group.scale.toArray(),
|
|
);
|
|
}
|
|
};
|
|
controls.addEventListener('dragging-changed', onDragChanged);
|
|
return () => {
|
|
controls.removeEventListener('dragging-changed', onDragChanged);
|
|
state.worldEditor.setIsDragging(false);
|
|
};
|
|
}, [isSelected]);
|
|
|
|
const handleClick = (e: ThreeEvent<MouseEvent>) => {
|
|
if (!state.worldEditor.isEnabled)
|
|
return;
|
|
if (e.delta > 5)
|
|
return;
|
|
e.stopPropagation();
|
|
|
|
state.worldEditor.setSelectedObject(object.id, nextSelectionEditMode(selectionMode));
|
|
};
|
|
|
|
return (<>
|
|
{
|
|
(isSelected && (selectionMode !== undefined) && groupRef.current) &&
|
|
<TransformControls
|
|
object={groupRef as RefObject<Group>}
|
|
mode={selectionMode}
|
|
/>
|
|
}
|
|
<group
|
|
ref={groupRef}
|
|
position={object.position}
|
|
rotation={object.rotation}
|
|
scale={object.scale}
|
|
onClick={handleClick}
|
|
>
|
|
{
|
|
objectType.voxels.map((v, idx) => {
|
|
const vt = state.world.getVoxelTypeById(v.typeId);
|
|
const color = (v.color ?? vt?.color) ?? 'white';
|
|
const opacity = (v.opacity ?? vt?.opacity) ?? 1;
|
|
return (
|
|
<mesh key={idx} position={v.position}>
|
|
<boxGeometry args={[1, 1, 1]} />
|
|
<meshStandardMaterial
|
|
color={color}
|
|
opacity={opacity}
|
|
transparent={opacity < 1}
|
|
/>
|
|
</mesh>
|
|
);
|
|
})
|
|
}
|
|
</group>
|
|
</>);
|
|
});
|