feat: Enhance ConveyorCollider with modelName and scene props, add CurvedPlane component, and update PhysicsSimulator to comment out MaterialSpawner
This commit is contained in:
@@ -2,27 +2,27 @@ import * as THREE from 'three';
|
||||
import { CollisionPayload, RapierRigidBody, RigidBody } from '@react-three/rapier';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import CurvedPlane from '../../../../scene/physics/curvedPlane';
|
||||
|
||||
function ConveyorCollider({ boundingBox, asset, conveyorPlaneSize }: {
|
||||
function ConveyorCollider({ boundingBox, asset, modelName, conveyorPlaneSize, scene }: {
|
||||
boundingBox: THREE.Box3 | null,
|
||||
asset: Asset,
|
||||
modelName: string,
|
||||
scene: THREE.Scene,
|
||||
conveyorPlaneSize: [number, number] | null,
|
||||
}) {
|
||||
const conveyorRef = useRef<any>(null);
|
||||
const [objectsOnConveyor, setObjectsOnConveyor] = useState<Set<any>>(new Set());
|
||||
const conveyorDirection = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||
const conveyorSpeed = 2;
|
||||
|
||||
useEffect(() => {
|
||||
if (!boundingBox || !conveyorPlaneSize) return;
|
||||
|
||||
const [width, depth] = conveyorPlaneSize;
|
||||
if (width < depth) {
|
||||
if (width < depth) { //z-axis conveyor
|
||||
conveyorDirection.current.set(0, 0, 1);
|
||||
} else {
|
||||
} else {//x-axis conveyor
|
||||
conveyorDirection.current.set(1, 0, 0);
|
||||
}
|
||||
|
||||
const rotation = new THREE.Euler().fromArray(asset.rotation || [0, 0, 0]);
|
||||
conveyorDirection.current.applyEuler(rotation);
|
||||
}, [boundingBox, conveyorPlaneSize, asset.rotation]);
|
||||
@@ -50,7 +50,6 @@ function ConveyorCollider({ boundingBox, asset, conveyorPlaneSize }: {
|
||||
useFrame(() => {
|
||||
const forward = conveyorDirection.current.clone().normalize();
|
||||
const side = new THREE.Vector3().crossVectors(forward, new THREE.Vector3(0, 1, 0)).normalize();
|
||||
|
||||
const force = forward.clone().multiplyScalar(conveyorSpeed);
|
||||
|
||||
objectsOnConveyor.forEach(rigidBody => {
|
||||
@@ -124,12 +123,14 @@ function ConveyorCollider({ boundingBox, asset, conveyorPlaneSize }: {
|
||||
colliders="cuboid"
|
||||
>
|
||||
<mesh>
|
||||
{/* <CurvedPlane modelName={modelName} scene={scene} /> */}
|
||||
<planeGeometry args={conveyorPlaneSize} />
|
||||
<meshBasicMaterial
|
||||
color="green"
|
||||
transparent
|
||||
opacity={0.3}
|
||||
visible={false}
|
||||
visible={true}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
</RigidBody>
|
||||
|
||||
@@ -526,6 +526,8 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
)}
|
||||
<ConveyorCollider boundingBox={boundingBox}
|
||||
asset={asset}
|
||||
modelName={asset.modelName}
|
||||
scene={scene}
|
||||
conveyorPlaneSize={conveyorPlaneSize}
|
||||
/>
|
||||
</>
|
||||
|
||||
115
app/src/modules/scene/physics/curvedPlane.tsx
Normal file
115
app/src/modules/scene/physics/curvedPlane.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
export default function CurvedPlane({ modelName, scene }: { modelName: string, scene: THREE.Scene }) {
|
||||
// console.log('scene: ', scene);n
|
||||
// console.log('modelName: ', modelName);
|
||||
|
||||
function computeCircleFromPoints(p1: THREE.Vector2, p2: THREE.Vector2, p3: THREE.Vector2) {
|
||||
const temp = p2.clone().sub(p1);
|
||||
const temp2 = p3.clone().sub(p1);
|
||||
const cross = temp.x * temp2.y - temp.y * temp2.x;
|
||||
if (Math.abs(cross) < 1e-10) return null; // colinear
|
||||
|
||||
const A = p1.lengthSq();
|
||||
const B = p2.lengthSq();
|
||||
const C = p3.lengthSq();
|
||||
|
||||
const D = 2 * (p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y));
|
||||
|
||||
const centerX = (A * (p2.y - p3.y) + B * (p3.y - p1.y) + C * (p1.y - p2.y)) / D;
|
||||
const centerY = (A * (p3.x - p2.x) + B * (p1.x - p3.x) + C * (p2.x - p1.x)) / D;
|
||||
|
||||
const center = new THREE.Vector2(centerX, centerY);
|
||||
const radius = center.distanceTo(p1);
|
||||
return { center, radius };
|
||||
}
|
||||
function findFirstMesh(object: THREE.Object3D): THREE.Mesh | null {
|
||||
if ((object as THREE.Mesh).isMesh) return object as THREE.Mesh;
|
||||
|
||||
for (const child of object.children) {
|
||||
const result = findFirstMesh(child);
|
||||
if (result) return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const parentObject = scene.getObjectByName(modelName); // this is the group
|
||||
if (!parentObject) return null;
|
||||
|
||||
const mesh = findFirstMesh(parentObject);
|
||||
console.log('mesh: ', mesh);
|
||||
if (!mesh) {
|
||||
console.warn('No mesh found inside group');
|
||||
return null;
|
||||
}
|
||||
|
||||
const geometry = mesh.geometry as THREE.BufferGeometry;
|
||||
const positions = geometry?.attributes?.position;
|
||||
|
||||
const points: THREE.Vector2[] = [];
|
||||
for (let i = 0; i < positions.count; i += 20) { // sample every 20th vertex
|
||||
const x = positions.getX(i);
|
||||
const z = positions.getZ(i); // assuming Y is up
|
||||
points.push(new THREE.Vector2(x, z));
|
||||
}
|
||||
|
||||
console.log('points: ', points);
|
||||
// Use three points to estimate curve
|
||||
const p1 = points[0];
|
||||
const p2 = points[Math.floor(points.length / 2)];
|
||||
const p3 = points[points.length - 1];
|
||||
|
||||
const result = computeCircleFromPoints(p1, p2, p3);
|
||||
if (!result) return null;
|
||||
|
||||
const { center, radius } = result;
|
||||
const angle = p1.clone().sub(center).angleTo(p3.clone().sub(center));
|
||||
|
||||
// Estimate width: take 2 points close to p2 but on either side
|
||||
const width = 3; // placeholder, calculate from mesh later if needed
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
const segments = 32;
|
||||
|
||||
const innerRadius = radius;
|
||||
const outerRadius = radius + width;
|
||||
|
||||
for (let i = 0; i <= segments; i++) {
|
||||
const t = (angle * i) / segments;
|
||||
const x = Math.cos(t) * outerRadius;
|
||||
const y = Math.sin(t) * outerRadius;
|
||||
if (i === 0) shape.moveTo(x, y);
|
||||
else shape.lineTo(x, y);
|
||||
}
|
||||
|
||||
for (let i = segments; i >= 0; i--) {
|
||||
const t = (angle * i) / segments;
|
||||
const x = Math.cos(t) * innerRadius;
|
||||
const y = Math.sin(t) * innerRadius;
|
||||
shape.lineTo(x, y);
|
||||
}
|
||||
const helper = new THREE.BoxHelper(mesh, 0xffff00);
|
||||
scene.add(helper);
|
||||
|
||||
return (
|
||||
<mesh
|
||||
// position={[
|
||||
// mesh.getWorldPosition(new THREE.Vector3()).x,
|
||||
// mesh.getWorldPosition(new THREE.Vector3()).y,
|
||||
// mesh.getWorldPosition(new THREE.Vector3()).z
|
||||
// ]}
|
||||
scale={mesh.getWorldScale(new THREE.Vector3())}
|
||||
rotation={[0, 0, 0]}
|
||||
>
|
||||
<shapeGeometry args={[shape]} />
|
||||
<meshBasicMaterial
|
||||
color="green"
|
||||
transparent
|
||||
opacity={0.3}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import ColliderCreator from './colliders/colliderCreator'
|
||||
function PhysicsSimulator() {
|
||||
return (
|
||||
<>
|
||||
<MaterialSpawner
|
||||
{/* <MaterialSpawner
|
||||
position={[0, 3, 0]}
|
||||
spawnInterval={1000}
|
||||
spawnCount={15}
|
||||
@@ -19,7 +19,7 @@ function PhysicsSimulator() {
|
||||
position={[-17, 3, 6]}
|
||||
spawnInterval={1000}
|
||||
spawnCount={50}
|
||||
/>
|
||||
/> */}
|
||||
<ColliderCreator />
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -74,6 +74,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
||||
<Setup />
|
||||
<Collaboration />
|
||||
<Physics gravity={[0, -9.81, 0]} allowedLinearError={50} numSolverIterations={50} debug >
|
||||
{/* <Physics gravity={[0, -9.81, 0]} allowedLinearError={50} numSolverIterations={50} > */}
|
||||
<Builder />
|
||||
<Simulation />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user