converting asset loading and other functionalities to r3f from three js

This commit is contained in:
2025-05-26 14:29:37 +05:30
parent 5c6116a01c
commit 1258d11ee8
7 changed files with 269 additions and 32 deletions

View File

@@ -6,8 +6,8 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { FloorItems } from "../../../types/world/worldTypes";
import { useAssetsStore } from "../../../store/builder/useAssetStore";
import { useEventsStore } from "../../../store/simulation/useEventsStore";
import Models from "./models/models";
import { useGLTF } from "@react-three/drei";
const gltfLoaderWorker = new Worker(
new URL(
@@ -19,6 +19,7 @@ const gltfLoaderWorker = new Worker(
function AssetsGroup() {
const { setLoadingProgress } = useLoadingProgress();
const { setAssets } = useAssetsStore();
const { addEvent } = useEventsStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
@@ -92,6 +93,133 @@ function AssetsGroup() {
opacity: 1,
eventData: item.eventData
})
if (item.eventData.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "vehicle",
speed: 1,
point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 1,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: []
}
}
};
addEvent(vehicleEvent);
} else if (item.eventData.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "transfer",
speed: 1,
points: item.eventData.points?.map((point: any, index: number) => ({
uuid: point.uuid || THREE.MathUtils.generateUUID(),
position: [point.position[0], point.position[1], point.position[2]],
rotation: [point.rotation[0], point.rotation[1], point.rotation[2]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action 1`,
actionType: 'default',
material: 'Default material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: []
}
})) || [],
};
addEvent(ConveyorEvent);
} else if (item.eventData.type === "StaticMachine") {
const machineEvent: MachineEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "machine",
point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "process",
processTime: 10,
swapMaterial: "material-id",
triggers: []
}
}
};
addEvent(machineEvent);
} else if (item.eventData.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "roboticArm",
speed: 1,
point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndPlace",
process: {
startPoint: null,
endPoint: null
},
triggers: []
}
]
}
};
addEvent(roboticArmEvent);
} else if (item.eventData.type === 'Storage') {
const storageEvent: StorageEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle",
type: "storageUnit",
point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "store",
storageCapacity: 10,
triggers: []
}
}
};
addEvent(storageEvent);
}
} else {
assets.push({
modelUuid: item.modelUuid,

View File

@@ -0,0 +1,20 @@
import { Box3, BoxGeometry, EdgesGeometry, Vector3 } from "three";
export const AssetBoundingBox = ({ asset, boundingBox }: { asset: Asset, boundingBox: Box3 | null }) => {
if (!boundingBox) return null;
const size = boundingBox.getSize(new Vector3());
const center = boundingBox.getCenter(new Vector3());
const boxGeometry = new BoxGeometry(size.x, size.y, size.z);
const edges = new EdgesGeometry(boxGeometry);
return (
<group name='Asset FallBack' userData={asset}>
<lineSegments position={center}>
<bufferGeometry attach="geometry" {...edges} />
<lineBasicMaterial attach="material" color="gray" linewidth={1} />
</lineSegments>
</group>
);
};

View File

@@ -1,13 +1,24 @@
import { Outlines } from '@react-three/drei';
import { useEffect, useState } from 'react';
import * as THREE from 'three';
import { useEffect, useRef, useState } from 'react';
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import * as THREE from 'three';
import { useFrame, useThree } from '@react-three/fiber';
import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem } from '../../../../../store/builder/store';
import { AssetBoundingBox } from './assetBoundingBox';
import { CameraControls } from '@react-three/drei';
function Model({ asset }: { asset: Asset }) {
const { camera, controls } = useThree();
const { activeTool } = useActiveTool();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { renderDistance } = useRenderDistance();
const [isRendered, setIsRendered] = useState(false);
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const [gltfScene, setGltfScene] = useState<GLTF | null>(null);
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const groupRef = useRef<THREE.Group>(null);
useEffect(() => {
const loader = new GLTFLoader();
@@ -17,9 +28,11 @@ function Model({ asset }: { asset: Asset }) {
loader.setDRACOLoader(dracoLoader);
const loadModel = async () => {
try {
// Check Cache
const cachedModel = THREE.Cache.get(asset.assetId!);
if (cachedModel) {
setGltfScene(cachedModel);
calculateBoundingBox(cachedModel.scene);
return;
}
@@ -32,6 +45,7 @@ function Model({ asset }: { asset: Asset }) {
THREE.Cache.remove(blobUrl);
THREE.Cache.add(asset.assetId!, gltf);
setGltfScene(gltf);
calculateBoundingBox(gltf.scene);
},
undefined,
(error) => {
@@ -49,6 +63,7 @@ function Model({ asset }: { asset: Asset }) {
await storeGLTF(asset.assetId!, modelBlob);
THREE.Cache.add(asset.assetId!, gltf);
setGltfScene(gltf);
calculateBoundingBox(gltf.scene);
},
undefined,
(error) => {
@@ -60,22 +75,79 @@ function Model({ asset }: { asset: Asset }) {
}
};
const calculateBoundingBox = (scene: THREE.Object3D) => {
const box = new THREE.Box3().setFromObject(scene);
setBoundingBox(box);
};
loadModel();
}, [asset.assetId]);
}, []);
useFrame(() => {
const assetPosition = new THREE.Vector3(...asset.position);
if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) {
setIsRendered(true);
} else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) {
setIsRendered(false);
}
})
const handleAssetDouble = (asset: Asset) => {
if (asset) {
if (activeTool === "cursor" && boundingBox && groupRef.current) {
const size = boundingBox.getSize(new THREE.Vector3());
const center = boundingBox.getCenter(new THREE.Vector3());
const front = new THREE.Vector3(0, 0, 1);
groupRef.current.localToWorld(front);
front.sub(groupRef.current.position).normalize();
const distance = Math.max(size.x, size.y, size.z) * 2;
const newPosition = center.clone().addScaledVector(front, distance);
(controls as CameraControls).setPosition(
newPosition.x,
newPosition.y,
newPosition.z,
true
);
(controls as CameraControls).setTarget(center.x, center.y, center.z, true);
(controls as CameraControls).fitToBox(groupRef.current!, true, {
cover: true,
paddingTop: 5,
paddingLeft: 5,
paddingBottom: 5,
paddingRight: 5,
});
setSelectedFloorItem(groupRef.current);
}
}
};
return (
<>
{gltfScene &&
<group
name='Asset Model'
ref={groupRef}
uuid={asset.modelUuid}
position={asset.position}
rotation={asset.rotation}
visible={asset.isVisible}
userData={asset}
onClick={(e) => {
e.stopPropagation();
handleAssetDouble(asset);
}}
>
{gltfScene && (
isRendered ? (
<primitive object={gltfScene.scene.clone()} />
) : (
<AssetBoundingBox asset={asset} boundingBox={boundingBox} />
)
)}
</group>
}
</>
);
}

View File

@@ -1,16 +1,31 @@
import React from 'react'
import { useAssetsStore } from '../../../../store/builder/useAssetStore';
import Model from './model/model';
import { useThree } from '@react-three/fiber';
import { CameraControls } from '@react-three/drei';
import { Vector3 } from 'three';
import { useSelectedFloorItem } from '../../../../store/builder/store';
function Models() {
const { controls } = useThree();
const { assets } = useAssetsStore();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
return (
<>
<group
name='Asset Group'
onPointerMissed={(e) => {
e.stopPropagation();
if (selectedFloorItem) {
const target = (controls as CameraControls).getTarget(new Vector3());
(controls as CameraControls).setTarget(target.x, 0, target.z, true);
setSelectedFloorItem(null);
}
}}
>
{assets.map((asset) =>
<Model key={asset.modelUuid} asset={asset} />
)}
</>
</group>
)
}

View File

@@ -243,7 +243,7 @@ export default function Builder() {
/>
<Bvh firstHitOnly>
<FloorItemsGroup
{/* <FloorItemsGroup
itemsGroup={itemsGroup}
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
AttachedObject={AttachedObject}
@@ -251,7 +251,7 @@ export default function Builder() {
tempLoader={tempLoader}
isTempLoader={isTempLoader}
plane={plane}
/>
/> */}
<FloorGroup
floorGroup={floorGroup}
@@ -306,7 +306,7 @@ export default function Builder() {
anglesnappedPoint={anglesnappedPoint}
/>
{/* <AssetsGroup /> */}
<AssetsGroup />
<MeasurementTool />

View File

@@ -49,10 +49,10 @@ const SelectionControls: React.FC = () => {
const helper = new SelectionHelper(gl);
if (!itemsGroup) {
echo.warn("itemsGroup not found in the scene.");
return;
}
// if (!itemsGroup) {
// echo.warn("itemsGroup not found in the scene.");
// return;
// }
const onPointerDown = (event: PointerEvent) => {
if (event.button === 2) {
@@ -169,7 +169,7 @@ const SelectionControls: React.FC = () => {
selectedObjects.map((object) => {
let currentObject: THREE.Object3D | null = object;
while (currentObject) {
if (currentObject.userData.modelId) {
if (currentObject.userData.modelUuid) {
Objects.add(currentObject);
break;
}

View File

@@ -1,4 +1,3 @@
import * as THREE from "three";
import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
import { BlendFunction } from "postprocessing";
import {
@@ -6,15 +5,14 @@ import {
useSelectedWallItem,
useSelectedFloorItem,
} from "../../../store/builder/store";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
import { useEffect } from "react";
import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import { useEffect } from "react";
export default function PostProcessing() {
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { deletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem } = useSelectedWallItem();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventSphere } = useSelectedEventSphere();
function flattenChildren(children: any[]) {
@@ -28,6 +26,10 @@ export default function PostProcessing() {
return allChildren;
}
useEffect(()=>{
console.log('selectedFloorItem: ', selectedFloorItem);
},[selectedFloorItem])
return (
<>
<EffectComposer autoClear={false}>