blockly3d/src/components/ObjectView.tsx

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>
</>);
});