Files
Dwinzo_Demo/app/src/modules/scene/physics/colliders/colliderInstance/colliderInstance.tsx

226 lines
8.2 KiB
TypeScript
Raw Normal View History

import { CameraControls } from '@react-three/drei';
import { useThree, useFrame } from '@react-three/fiber';
import { CollisionPayload, RapierRigidBody, RigidBody } from '@react-three/rapier';
import { useEffect, useRef, useState } from 'react'
import * as THREE from 'three';
import { useSceneContext } from '../../../sceneContext';
import { ColliderArrow } from './colliderArrow';
function ColliderInstance({ collider }: {
collider: Collider
}) {
const { camera, gl, pointer, controls } = useThree();
const { colliderStore } = useSceneContext();
const { colliders, selectedCollider, setSelectedCollider, updateCollider, getArrowByArrowId } = colliderStore();
const [draggedId, setDraggedId] = useState<string | null>(null);
const dragOffset = useRef(new THREE.Vector3());
const initialDepth = useRef(0);
const ref = useRef<RapierRigidBody>(null);
const [objectsOnCollider, setObjectsOnCollider] = useState<Set<RapierRigidBody>>(new Set());
const isSelected = selectedCollider?.id === collider.id;
const getColorByType = (type: string) => {
switch (type) {
case 'Material 1':
return new THREE.Color('blue');
case 'Material 2':
return new THREE.Color('purple');
case 'Material 3':
return new THREE.Color('yellow');
default:
return new THREE.Color('white');
}
};
const handlePointerDown = (id: string) => {
if (controls) {
(controls as CameraControls).enabled = false;
}
setDraggedId(id);
const collider = colliders.find(c => c.id === id);
if (!collider || !ref.current) return;
const pos = ref.current.translation();
const screenPos = new THREE.Vector3(pos.x, pos.y, pos.z).project(camera);
dragOffset.current = new THREE.Vector3(screenPos.x - pointer.x, 0, screenPos.y - pointer.y);
initialDepth.current = new THREE.Vector3(pos.x, pos.y, pos.z).sub(camera.position).length();
ref.current.setGravityScale(0, true);
ref.current.setLinearDamping(10);
ref.current.setAngularDamping(10);
};
const handlePointerMove = (e: PointerEvent) => {
if (!draggedId) return;
const collider = colliders.find(c => c.id === draggedId);
if (!collider || !ref.current) return;
if (e.altKey) {
const rotation = ref.current.rotation();
const currentQuaternion = new THREE.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w);
const deltaQuaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), 0.01);
const newQuaternion = currentQuaternion.multiply(deltaQuaternion);
ref.current.setRotation({ x: newQuaternion.x, y: newQuaternion.y, z: newQuaternion.z, w: newQuaternion.w }, true);
return;
};
const screenTarget = new THREE.Vector3(
pointer.x + dragOffset.current.x,
0,
pointer.y + dragOffset.current.z
);
const worldTarget = new THREE.Vector3(screenTarget.x, screenTarget.z, 0.5).unproject(camera);
const dir = worldTarget.clone().sub(camera.position).normalize();
const finalPos = camera.position.clone().add(dir.multiplyScalar(initialDepth.current));
ref.current.setTranslation(
{ x: finalPos.x, y: finalPos.y, z: finalPos.z },
true
);
}
const handlePointerUp = () => {
if (controls) {
(controls as CameraControls).enabled = true;
}
if (!draggedId) return;
if (ref.current) {
// restore physics
ref.current.setGravityScale(1, true);
ref.current.setLinearDamping(0.5);
ref.current.setAngularDamping(0.5);
// get final transform
const pos = ref.current.translation();
const rot = ref.current.rotation();
// update Zustand store
updateCollider(draggedId, {
position: [pos.x, pos.y, pos.z],
rotation: [rot.x, rot.y, rot.z], // quaternion
});
}
setDraggedId(null);
};
useEffect(() => {
const canvas = gl.domElement;
canvas.addEventListener('pointermove', handlePointerMove);
canvas.addEventListener('pointerup', handlePointerUp);
return () => {
canvas.removeEventListener('pointermove', handlePointerMove);
canvas.removeEventListener('pointerup', handlePointerUp);
};
}, [colliders, draggedId, controls]);
const handleMaterialEnter = (e: CollisionPayload) => {
const body = e.other.rigidBody;
if (body && (body.userData as any)?.materialType) {
setObjectsOnCollider(prev => {
const newSet = new Set(prev);
newSet.add(body);
return newSet;
});
}
};
const handleMaterialExit = (e: CollisionPayload) => {
const body = e.other.rigidBody;
if (body) {
setObjectsOnCollider(prev => {
const newSet = new Set(prev);
newSet.delete(body);
return newSet;
});
}
};
useFrame(() => {
objectsOnCollider.forEach(rigidBody => {
if (!rigidBody) return;
// if (collider.colliderCondition.conditionType === 'material') {
// if (collider.colliderCondition.materialType === (rigidBody as any).userData.materialType) {
// console.log('(rigidBody as any).userData.materialType: ', (rigidBody as any).userData.materialType);
// collider.colliderCondition.arrowsOrder.forEach((arrowId) => {
// console.log('arrow: ', arrowId);
// let arrowDetails = getArrowByArrowId(arrowId);
// if (arrowDetails?.position) {
// const arrowPos = new THREE.Vector3(...arrowDetails?.position); // arrow position
// const colliderPos = new THREE.Vector3(...(selectedCollider?.position || [0, 0, 0])); // collider position
// const direction = new THREE.Vector3();
// direction.subVectors(arrowPos, colliderPos).normalize();
// console.log('Direction vector:', direction);
// rigidBody.setLinvel({ x: direction.x, y: direction.y, z: direction.z }, true);
// }
// // rigidBody.setAngvel({ x: 0, y: 0, z: 0 }, true);
// // Direction vector from collider to arrow
// });
// } else {
// }
// } else if (collider.colliderCondition.conditionType === 'count') {
// }
});
});
return (
<RigidBody
name='Sensor-Collider'
key={collider.id}
ref={ref}
type="fixed"
sensor
position={collider.position}
rotation={collider.rotation}
colliders="cuboid"
includeInvisible
gravityScale={0}
onIntersectionEnter={handleMaterialEnter}
onIntersectionExit={handleMaterialExit}
>
<mesh
onPointerDown={(e) => {
e.stopPropagation();
handlePointerDown(collider.id);
}}
onClick={(e) => {
e.stopPropagation();
setSelectedCollider(collider);
}}
>
<boxGeometry args={[0.1, 1, 1]} />
<meshStandardMaterial color={isSelected ? 'green' : 'white'} transparent opacity={0.3} />
</mesh>
{isSelected && collider.arrows.map((arrow) => (
<ColliderArrow
key={arrow.arrowId}
startPosition={collider.position}
endPosition={arrow.position}
colliderRotation={collider.rotation}
colliderId={collider.id}
arrowId={arrow.arrowId}
/>
))}
</RigidBody>
);
}
export default ColliderInstance;