feat: Enhance ConveyorCollider with modelName and scene props, add CurvedPlane component, and update PhysicsSimulator to comment out MaterialSpawner

This commit is contained in:
2025-07-30 10:02:55 +05:30
parent ec4a4247b2
commit d3697bb690
5 changed files with 129 additions and 10 deletions

View File

@@ -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>

View File

@@ -526,6 +526,8 @@ function Model({ asset }: { readonly asset: Asset }) {
)}
<ConveyorCollider boundingBox={boundingBox}
asset={asset}
modelName={asset.modelName}
scene={scene}
conveyorPlaneSize={conveyorPlaneSize}
/>
</>

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

View File

@@ -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 />
</>
)

View File

@@ -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 />