diff --git a/app/src/modules/builder/assetGroup/assetsGroup.tsx b/app/src/modules/builder/assetGroup/assetsGroup.tsx index 9e57fb7..5fabe2d 100644 --- a/app/src/modules/builder/assetGroup/assetsGroup.tsx +++ b/app/src/modules/builder/assetGroup/assetsGroup.tsx @@ -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, diff --git a/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx b/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx new file mode 100644 index 0000000..c1d4881 --- /dev/null +++ b/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx @@ -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 ( + + + + + + + ); +}; \ No newline at end of file diff --git a/app/src/modules/builder/assetGroup/models/model/model.tsx b/app/src/modules/builder/assetGroup/models/model/model.tsx index 55eb1a6..e58ef9f 100644 --- a/app/src/modules/builder/assetGroup/models/model/model.tsx +++ b/app/src/modules/builder/assetGroup/models/model/model.tsx @@ -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(null); + const [boundingBox, setBoundingBox] = useState(null); + const groupRef = useRef(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 && - + { + e.stopPropagation(); + handleAssetDouble(asset); + }} + > + {gltfScene && ( + isRendered ? ( - - } - + ) : ( + + ) + )} + ); } diff --git a/app/src/modules/builder/assetGroup/models/models.tsx b/app/src/modules/builder/assetGroup/models/models.tsx index 7378812..4553531 100644 --- a/app/src/modules/builder/assetGroup/models/models.tsx +++ b/app/src/modules/builder/assetGroup/models/models.tsx @@ -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 ( - <> + { + 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) => )} - + ) } diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index 18d407b..3189968 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -243,7 +243,7 @@ export default function Builder() { /> - + /> */} - {/* */} + diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 16f992b..763faa3 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -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; } diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 426e199..7c5db64 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -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 ( <>