plane geometry added with physics

This commit is contained in:
2025-07-30 18:16:01 +05:30
parent e001bfaa0a
commit 04573b86dd
12 changed files with 263 additions and 154 deletions

View File

@@ -1,14 +1,11 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { CollisionPayload, RapierRigidBody, RigidBody } from '@react-three/rapier'; import { CollisionPayload, RigidBody } from '@react-three/rapier';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber'; import { useFrame } from '@react-three/fiber';
import CurvedPlane from '../../../../scene/physics/curvedPlane';
function ConveyorCollider({ boundingBox, asset, modelName, conveyorPlaneSize, scene }: { function ConveyorCollider({ boundingBox, asset, conveyorPlaneSize }: {
boundingBox: THREE.Box3 | null, boundingBox: THREE.Box3 | null,
asset: Asset, asset: Asset,
modelName: string,
scene: THREE.Scene,
conveyorPlaneSize: [number, number] | null, conveyorPlaneSize: [number, number] | null,
}) { }) {
const conveyorRef = useRef<any>(null); const conveyorRef = useRef<any>(null);
@@ -18,9 +15,9 @@ function ConveyorCollider({ boundingBox, asset, modelName, conveyorPlaneSize, sc
useEffect(() => { useEffect(() => {
if (!boundingBox || !conveyorPlaneSize) return; if (!boundingBox || !conveyorPlaneSize) return;
const [width, depth] = conveyorPlaneSize; const [width, depth] = conveyorPlaneSize;
if (width < depth) { //z-axis conveyor if (width < depth) {
conveyorDirection.current.set(0, 0, 1); conveyorDirection.current.set(0, 0, 1);
} else {//x-axis conveyor } else {
conveyorDirection.current.set(1, 0, 0); conveyorDirection.current.set(1, 0, 0);
} }
const rotation = new THREE.Euler().fromArray(asset.rotation || [0, 0, 0]); const rotation = new THREE.Euler().fromArray(asset.rotation || [0, 0, 0]);
@@ -123,7 +120,6 @@ function ConveyorCollider({ boundingBox, asset, modelName, conveyorPlaneSize, sc
colliders="cuboid" colliders="cuboid"
> >
<mesh> <mesh>
{/* <CurvedPlane modelName={modelName} scene={scene} /> */}
<planeGeometry args={conveyorPlaneSize} /> <planeGeometry args={conveyorPlaneSize} />
<meshBasicMaterial <meshBasicMaterial
color="green" color="green"

View File

@@ -19,10 +19,12 @@ import { useSceneContext } from '../../../../scene/sceneContext';
import { useVersionContext } from '../../../version/versionContext'; import { useVersionContext } from '../../../version/versionContext';
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi'; import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs'; import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs';
import ConveyorCollider from './conveyorCollider';
import { ModelAnimator } from './animator/modelAnimator'; import { ModelAnimator } from './animator/modelAnimator';
import ConveyorCollider from './conveyorCollider';
import RibbonCollider from './ribbonCollider';
function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boolean }) { function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boolean }) {
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const { controls, gl, scene } = useThree(); const { controls, gl, scene } = useThree();
@@ -459,13 +461,13 @@ function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boole
{isRendered ? ( {isRendered ? (
<> <>
<RigidBody {/* <RigidBody
type="fixed" type="fixed"
colliders='cuboid' colliders='hull'
ref={rigidBodyRef} ref={rigidBodyRef}
> > */}
<primitive object={gltfScene} /> <primitive object={gltfScene} />
</ RigidBody> {/* </ RigidBody> */}
<ModelAnimator asset={asset} gltfScene={gltfScene} /> <ModelAnimator asset={asset} gltfScene={gltfScene} />
@@ -474,10 +476,13 @@ function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boole
<AssetBoundingBox name='Asset Fallback' boundingBox={boundingBox} color='gray' lineWidth={2.5} /> <AssetBoundingBox name='Asset Fallback' boundingBox={boundingBox} color='gray' lineWidth={2.5} />
)} )}
<ConveyorCollider boundingBox={boundingBox} {/* <ConveyorCollider boundingBox={boundingBox}
asset={asset}
conveyorPlaneSize={conveyorPlaneSize}
/> */}
<RibbonCollider boundingBox={boundingBox}
asset={asset} asset={asset}
modelName={asset.modelName}
scene={scene}
conveyorPlaneSize={conveyorPlaneSize} conveyorPlaneSize={conveyorPlaneSize}
/> />

View File

@@ -0,0 +1,182 @@
import * as THREE from 'three';
import { CollisionPayload, RigidBody } from '@react-three/rapier';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../../simulation/products/productContext';
function RibbonCollider({ boundingBox, asset, conveyorPlaneSize }: {
boundingBox: THREE.Box3 | null,
asset: Asset,
conveyorPlaneSize: [number, number] | null,
}) {
const { productStore } = useSceneContext();
const { getEventByModelUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const conveyorRef = useRef<any>(null);
const [objectsOnConveyor, setObjectsOnConveyor] = useState<Set<any>>(new Set());
const conveyorDirection = useRef<THREE.Vector3>(new THREE.Vector3());
const conveyorSpeed = 2;
const [geometryKey, setGeometryKey] = useState(0);
const event = getEventByModelUuid(
selectedProduct.productUuid,
asset.modelUuid
) as ConveyorEventSchema | undefined;
useEffect(() => {
if (!boundingBox || !conveyorPlaneSize) return;
const [width, depth] = conveyorPlaneSize;
if (width < depth) {
conveyorDirection.current.set(0, 0, 1);
} else {
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]);
const handleMaterialEnter = (e: CollisionPayload) => {
if (e.other.rigidBody) {
setObjectsOnConveyor(prev => {
const newSet = new Set(prev);
newSet.add(e.other.rigidBody);
return newSet;
});
}
};
const handleMaterialExit = (e: CollisionPayload) => {
if (e.other.rigidBody) {
setObjectsOnConveyor(prev => {
const newSet = new Set(prev);
newSet.delete(e.other.rigidBody);
return newSet;
});
}
};
useFrame(() => {
if (!event?.points || event.points.length < 2) return;
const curve = new THREE.CatmullRomCurve3(
event.points.map(p => new THREE.Vector3(...p.position))
);
const assetPos = new THREE.Vector3(...(asset.position || [0, 0, 0]));
const assetRot = new THREE.Euler(...(asset.rotation || [0, 0, 0]));
const assetQuat = new THREE.Quaternion().setFromEuler(assetRot);
const inverseAssetQuat = assetQuat.clone().invert();
objectsOnConveyor.forEach(rigidBody => {
if (!rigidBody) return;
const worldPos = new THREE.Vector3().copy(rigidBody.translation());
const localPos = worldPos.clone().sub(assetPos).applyQuaternion(inverseAssetQuat);
const curvePoints = curve.getPoints(100);
let closestIndex = 0;
let minDist = Infinity;
for (let i = 0; i < curvePoints.length; i++) {
const dist = curvePoints[i].distanceToSquared(localPos);
if (dist < minDist) {
minDist = dist;
closestIndex = i;
}
}
const point = curvePoints[closestIndex];
const prev = curvePoints[closestIndex - 1] || point;
const next = curvePoints[closestIndex + 1] || point;
const tangentLocal = new THREE.Vector3().subVectors(next, prev).normalize();
const sideLocal = new THREE.Vector3().crossVectors(tangentLocal, new THREE.Vector3(0, 1, 0)).normalize();
const relative = new THREE.Vector3().subVectors(localPos, point);
const sideOffset = relative.dot(sideLocal);
const centeringStrength = 10;
const centeringForceLocal = sideLocal.clone().multiplyScalar(-sideOffset * centeringStrength);
const forwardForceLocal = tangentLocal.clone().multiplyScalar(conveyorSpeed);
const totalForceLocal = forwardForceLocal.add(centeringForceLocal);
const totalForceWorld = totalForceLocal.applyQuaternion(assetQuat);
rigidBody.setAngvel({ x: 0, y: 0, z: 0 }, true);
rigidBody.setLinvel(totalForceWorld, true);
});
});
const geometry = useMemo(() => {
if (asset.eventData?.type !== 'Conveyor' || !conveyorPlaneSize) return null;
const width = 1;
const segments = 30;
const vertices: number[] = [];
const indices: number[] = [];
if (!event || !event.points || event.points.length < 2) return null;
const points = event.points.map(p => new THREE.Vector3(p.position[0], p.position[1], p.position[2]));
if (points.length < 2) return null;
const curve = new THREE.CatmullRomCurve3(points);
const curvePoints = curve.getPoints((points.length - 1) * segments);
for (let i = 0; i < curvePoints.length; i++) {
const point = curvePoints[i];
const prev = curvePoints[i - 1] || curvePoints[i];
const next = curvePoints[i + 1] || curvePoints[i];
const tangent = new THREE.Vector3().subVectors(next, prev).normalize();
const normal = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize();
const left = new THREE.Vector3().copy(point).addScaledVector(normal, -width / 2);
const right = new THREE.Vector3().copy(point).addScaledVector(normal, width / 2);
vertices.push(...left.toArray());
vertices.push(...right.toArray());
}
const totalSegments = curvePoints.length - 1;
for (let i = 0; i < totalSegments; i++) {
const base = i * 2;
indices.push(base, base + 1, base + 2);
indices.push(base + 1, base + 3, base + 2);
}
const ribbonGeometry = new THREE.BufferGeometry();
ribbonGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
ribbonGeometry.setIndex(indices);
ribbonGeometry.computeVertexNormals();
setGeometryKey((k) => k + 1);
return ribbonGeometry;
}, [asset.eventData, event]);
return (
<>
{asset.eventData?.type === 'Conveyor' && conveyorPlaneSize && geometry && (
<RigidBody
key={geometryKey}
ref={conveyorRef}
type="fixed"
position={[0, 0.001, 0]}
userData={{ isConveyor: true }}
onCollisionEnter={handleMaterialEnter}
onCollisionExit={handleMaterialExit}
colliders="trimesh"
>
<mesh geometry={geometry} >
<meshStandardMaterial color="skyblue" side={THREE.DoubleSide} opacity={0.5} transparent />
</mesh>
</RigidBody>
)}
</>
);
}
export default RibbonCollider;

View File

@@ -1,15 +1,19 @@
import { useEffect, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useThree } from '@react-three/fiber'; import { useThree } from '@react-three/fiber';
import ColliderInstance from './colliderInstance/colliderInstance'; import ColliderInstance from './colliderInstance/colliderInstance';
import { useToggleView } from '../../../../store/builder/store';
function ColliderCreator() { function ColliderCreator() {
const { camera, gl, scene, raycaster, pointer } = useThree(); const { camera, gl, scene, raycaster, pointer } = useThree();
const [colliders, setColliders] = useState< const [colliders, setColliders] = useState<
{ id: string; position: [number, number, number]; colliderType: 'Default material' | 'Material 1' | 'Material 2' | 'Material 3' }[] { id: string; position: [number, number, number]; rotation: [number, number, number]; colliderType: 'Default material' | 'Material 1' | 'Material 2' | 'Material 3' }[]
>([]); >([]);
const drag = useRef(false);
const isLeftMouseDown = useRef(false);
const { toggleView } = useToggleView();
const handleCtrlClick = (e: MouseEvent) => { const handleCtrlClick = (e: MouseEvent) => {
if (!e.ctrlKey) return; if (!e.ctrlKey || drag.current || toggleView) return;
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
@@ -25,6 +29,7 @@ function ColliderCreator() {
{ {
id: Date.now().toString(), id: Date.now().toString(),
position: spawnPosition, position: spawnPosition,
rotation: [0, 0, 0],
colliderType: 'Default material', colliderType: 'Default material',
} }
]); ]);
@@ -33,22 +38,49 @@ function ColliderCreator() {
useEffect(() => { useEffect(() => {
const canvas = gl.domElement; const canvas = gl.domElement;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = true;
drag.current = false;
}
};
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = false;
}
};
const onMouseMove = () => {
if (isLeftMouseDown) {
drag.current = true;
}
};
canvas.addEventListener('click', handleCtrlClick); canvas.addEventListener('click', handleCtrlClick);
canvas.addEventListener('mousedown', onMouseDown);
canvas.addEventListener('mouseup', onMouseUp);
canvas.addEventListener('mousemove', onMouseMove);
return () => { return () => {
canvas.removeEventListener('click', handleCtrlClick); canvas.removeEventListener('click', handleCtrlClick);
canvas.removeEventListener('mousedown', onMouseDown);
canvas.removeEventListener('mouseup', onMouseUp);
canvas.removeEventListener('mousemove', onMouseMove);
}; };
}, [colliders, camera]); }, [colliders, camera]);
return ( return (
<> <>
{colliders.map(({ id, position }) => ( {colliders.map(({ id, position, rotation }) => (
<ColliderInstance <ColliderInstance
key={id} key={id}
id={id} id={id}
colliders={colliders} colliders={colliders}
setColliders={setColliders} setColliders={setColliders}
position={position} position={position}
rotation={rotation}
/> />
))} ))}
</> </>

View File

@@ -4,11 +4,12 @@ import { CollisionPayload, RapierRigidBody, RigidBody } from '@react-three/rapie
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import * as THREE from 'three'; import * as THREE from 'three';
function ColliderInstance({ id, colliders, setColliders, position }: { function ColliderInstance({ id, colliders, setColliders, position, rotation }: {
id: string; id: string;
colliders: { id: string; position: [number, number, number]; colliderType: 'Default material' | 'Material 1' | 'Material 2' | 'Material 3' }[]; colliders: { id: string; position: [number, number, number]; rotation: [number, number, number]; colliderType: 'Default material' | 'Material 1' | 'Material 2' | 'Material 3' }[];
setColliders: React.Dispatch<React.SetStateAction<{ id: string; position: [number, number, number]; colliderType: 'Default material' | 'Material 1' | 'Material 2' | 'Material 3' }[]>>; setColliders: React.Dispatch<React.SetStateAction<{ id: string; position: [number, number, number]; rotation: [number, number, number]; colliderType: 'Default material' | 'Material 1' | 'Material 2' | 'Material 3' }[]>>;
position: [number, number, number]; position: [number, number, number];
rotation: [number, number, number];
}) { }) {
const { camera, gl, pointer, controls } = useThree(); const { camera, gl, pointer, controls } = useThree();
const [draggedId, setDraggedId] = useState<string | null>(null); const [draggedId, setDraggedId] = useState<string | null>(null);

View File

@@ -1,115 +0,0 @@
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

@@ -10,7 +10,7 @@ import { generateUniqueId } from '../../../functions/generateUniqueId';
type MaterialSpawnerProps = { type MaterialSpawnerProps = {
position: [number, number, number]; position: [number, number, number];
spawnInterval: number; spawnInterval: number;
spawnCount?: number; spawnCount: number;
}; };
function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawnerProps) { function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawnerProps) {
@@ -177,11 +177,15 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
setSpawningPaused(prev => !prev); setSpawningPaused(prev => !prev);
}; };
const handleBoxContextMenu = () => {
};
return ( return (
<> <>
<mesh <mesh
position={position} position={position}
onClick={handleBoxClick} onClick={handleBoxClick}
onContextMenu={handleBoxContextMenu}
> >
<boxGeometry args={[1, 1, 1]} /> <boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={spawningPaused ? "red" : "white"} transparent opacity={0.2} /> <meshStandardMaterial color={spawningPaused ? "red" : "white"} transparent opacity={0.2} />
@@ -192,12 +196,12 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
key={id} key={id}
ref={ref} ref={ref}
position={position} position={position}
colliders="cuboid" colliders="hull"
angularDamping={0.5} angularDamping={0.5}
linearDamping={0.5} linearDamping={0.5}
restitution={0.1} restitution={0.1}
userData={{ materialType, materialUuid: id }} userData={{ materialType, materialUuid: id }}
// onSleep={() => handleSleep(id)} onSleep={() => handleSleep(id)}
> >
<MaterialModel <MaterialModel
materialId={id} materialId={id}

View File

@@ -1,26 +1,28 @@
import React from 'react'
import MaterialSpawner from './materialSpawner' import MaterialSpawner from './materialSpawner'
import ColliderCreator from './colliders/colliderCreator' import ColliderCreator from './colliders/colliderCreator'
function PhysicsSimulator() { function PhysicsSimulator() {
return ( return (
<> <>
{/* <MaterialSpawner
<MaterialSpawner
position={[0, 3, 0]} position={[0, 3, 0]}
spawnInterval={1000} spawnInterval={1000}
spawnCount={15} spawnCount={15}
/> />
<MaterialSpawner <MaterialSpawner
position={[-21, 3, -8]} position={[6, 3, 6]}
spawnInterval={1000} spawnInterval={1000}
spawnCount={5} spawnCount={5}
/> />
<MaterialSpawner <MaterialSpawner
position={[-17, 3, 6]} position={[6, 3, -6]}
spawnInterval={1000} spawnInterval={1000}
spawnCount={50} spawnCount={5}
/> */} />
<ColliderCreator /> <ColliderCreator />
</> </>
) )
} }

View File

@@ -74,8 +74,8 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
<Setup /> <Setup />
<Collaboration /> <Collaboration />
<Physics gravity={[0, -9.81, 0]} allowedLinearError={50} numSolverIterations={50} debug > <Physics gravity={[0, -9.81, 0]} allowedLinearError={50} numSolverIterations={50} debug >
{/* <Physics gravity={[0, -9.81, 0]} allowedLinearError={50} numSolverIterations={50} > */}
<Builder /> <Builder />
<Simulation /> <Simulation />
<PhysicsSimulator /> <PhysicsSimulator />

View File

@@ -5,6 +5,7 @@ import Controls from '../controls/controls';
import { Environment } from '@react-three/drei' import { Environment } from '@react-three/drei'
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr"; import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
import SecondaryCamera from '../../secondaryCamera/secondaryCamera'; import SecondaryCamera from '../../secondaryCamera/secondaryCamera';
function Setup() { function Setup() {
@@ -21,7 +22,8 @@ function Setup() {
{/* <MovingClouds /> */} {/* <MovingClouds /> */}
<Environment files={background} environmentIntensity={1.5} /> <Environment files={background} environmentIntensity={1.5} />
<SecondaryCamera/>
{/* <SecondaryCamera /> */}
</> </>
) )
} }

View File

@@ -15,11 +15,11 @@ type CameraData = {
target: [number, number, number]; target: [number, number, number];
}; };
function SecondaryCameraView() { function SecondaryCamera() {
const cameraRef = useRef<THREE.PerspectiveCamera | null>(null); const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
const helperRef = useRef<THREE.CameraHelper | null>(null); const helperRef = useRef<THREE.CameraHelper | null>(null);
const rendererRef = useRef<THREE.WebGLRenderer | null>(null); const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
const dummyMeshRef = useRef<THREE.Mesh | null>(null); const dummyMeshRef = useRef<THREE.Mesh | null>(null);
const { scene, size, controls } = useThree(); const { scene, size, controls } = useThree();
const { secondaryCameraData, setSecondaryCameraData, updateSecondaryCameraData } = useSecondaryCameraData(); const { secondaryCameraData, setSecondaryCameraData, updateSecondaryCameraData } = useSecondaryCameraData();
const { selectedSecondaryCamera, setSelectedSecondaryCamera } = useSecondaryCameraState(); const { selectedSecondaryCamera, setSelectedSecondaryCamera } = useSecondaryCameraState();
@@ -166,4 +166,4 @@ function SecondaryCameraView() {
); );
} }
export default SecondaryCameraView; export default SecondaryCamera;

View File

@@ -134,7 +134,7 @@ const Project: React.FC = () => {
</VersionProvider> </VersionProvider>
</SceneProvider> </SceneProvider>
<SecondaryCanvas /> {/* <SecondaryCanvas /> */}
{selectedUser && <FollowPerson />} {selectedUser && <FollowPerson />}
{isLogListVisible && ( {isLogListVisible && (