converting asset loading and other functionalities to r3f from three js
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 />
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}>
|
||||
|
||||
Reference in New Issue
Block a user