feat: Implement collider functionality with drag-and-drop arrows and conditions
- Added Collider and ColliderCondition types to manage collider properties. - Created a new useColliderStore for managing colliders and their arrows. - Implemented ColliderArrow component for visual representation and interaction of arrows. - Enhanced ColliderProperties UI for managing directions and conditions. - Updated PhysicsSimulator and Scene components to integrate collider features. - Refactored existing code for better organization and clarity.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import { useRef, useMemo, useState, useCallback } from "react";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
|
||||
type ColliderArrowProps = {
|
||||
arrowId: string;
|
||||
colliderId: string;
|
||||
startPosition: [number, number, number];
|
||||
endPosition: [number, number, number];
|
||||
colliderRotation: [number, number, number];
|
||||
thickness?: number;
|
||||
depth?: number;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export function ColliderArrow({
|
||||
arrowId,
|
||||
colliderId,
|
||||
startPosition,
|
||||
endPosition,
|
||||
colliderRotation,
|
||||
thickness = 0.05,
|
||||
depth = 0.01,
|
||||
color = "green",
|
||||
}: ColliderArrowProps) {
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const { raycaster, pointer, controls } = useThree();
|
||||
const { colliderStore } = useSceneContext();
|
||||
const { updateArrow, selectedArrowId, clearSelectedArrow } = colliderStore();
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const { dir, length } = useMemo(() => {
|
||||
const start = new THREE.Vector3(...startPosition);
|
||||
const end = new THREE.Vector3(...endPosition);
|
||||
const d = new THREE.Vector3().subVectors(end, start);
|
||||
return { dir: d.clone().normalize(), length: d.length() };
|
||||
}, [startPosition, endPosition]);
|
||||
|
||||
const arrowShape = useMemo(() => {
|
||||
const shaftWidth = thickness;
|
||||
const headLength = Math.min(length * 0.3, 0.4);
|
||||
const headWidth = thickness * 3;
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(0, -shaftWidth / 2);
|
||||
shape.lineTo(length - headLength, -shaftWidth / 2);
|
||||
shape.lineTo(length - headLength, -headWidth / 2);
|
||||
shape.lineTo(length, 0);
|
||||
shape.lineTo(length - headLength, headWidth / 2);
|
||||
shape.lineTo(length - headLength, shaftWidth / 2);
|
||||
shape.lineTo(0, shaftWidth / 2);
|
||||
shape.closePath();
|
||||
|
||||
return shape;
|
||||
}, [length, thickness]);
|
||||
|
||||
const extrudeSettings = useMemo(() => ({ depth, bevelEnabled: false, }), [depth]);
|
||||
const geometry = useMemo(() => new THREE.ExtrudeGeometry(arrowShape, extrudeSettings), [arrowShape, extrudeSettings]);
|
||||
|
||||
const quaternion = useMemo(() => {
|
||||
const q = new THREE.Quaternion();
|
||||
q.setFromUnitVectors(new THREE.Vector3(1, 0, 0), dir);
|
||||
const colliderQuat = new THREE.Quaternion().setFromEuler(
|
||||
new THREE.Euler().fromArray(colliderRotation)
|
||||
);
|
||||
|
||||
return colliderQuat.multiply(q);
|
||||
}, [dir, colliderRotation]);
|
||||
|
||||
const handlePointerDown = useCallback((e: any) => {
|
||||
e.stopPropagation();
|
||||
setDragging(true);
|
||||
(controls as any).enabled = false; // Disable controls while dragging
|
||||
}, []);
|
||||
|
||||
const handlePointerUp = useCallback((e: any) => {
|
||||
e.stopPropagation();
|
||||
clearSelectedArrow()
|
||||
setDragging(false);
|
||||
(controls as any).enabled = true;
|
||||
}, []);
|
||||
|
||||
useFrame(({ camera }) => {
|
||||
if (dragging) {
|
||||
if (selectedArrowId) {
|
||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -startPosition[1]);
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const hit = new THREE.Vector3();
|
||||
if (raycaster.ray.intersectPlane(plane, hit)) {
|
||||
updateArrow(colliderId, arrowId, {
|
||||
position: [hit.x, startPosition[1], hit.z],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={groupRef} quaternion={quaternion}>
|
||||
|
||||
<mesh geometry={geometry} rotation={[Math.PI / 2, 0, 0]}>
|
||||
<meshStandardMaterial color={color} />
|
||||
</mesh>
|
||||
|
||||
<mesh
|
||||
position={[length, 0, 0]}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
>
|
||||
<sphereGeometry args={[0.15, 16, 16]} />
|
||||
<meshBasicMaterial transparent opacity={0.2} color="yellow" />
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user