diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts deleted file mode 100644 index 096665a..0000000 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; -import gsap from 'gsap'; -import * as THREE from 'three'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import * as Types from "../../../types/world/worldTypes"; -import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; -import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; -import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; - -async function loadInitialFloorItems( - itemsGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState, - addEvent: (event: EventsSchema) => void, - renderDistance: number -): Promise { - if (!itemsGroup.current) return; - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - const email = localStorage.getItem('email'); - const organization = (email!.split("@")[1]).split(".")[0]; - - const items = await getFloorAssets(organization); - localStorage.setItem("FloorItems", JSON.stringify(items)); - await initializeDB(); - - if (items.message === "floorItems not found") return; - - if (items) { - const storedFloorItems: Types.FloorItems = items; - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); - loader.setDRACOLoader(dracoLoader); - - let modelsLoaded = 0; - const modelsToLoad = storedFloorItems.length; - - const camData = await getCamera(organization, localStorage.getItem('userId')!); - let storedPosition; - if (camData && camData.position) { - storedPosition = camData?.position; - } else { - storedPosition = new THREE.Vector3(0, 40, 30); - } - if (!storedPosition) return; - const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); - - storedFloorItems.sort((a, b) => { - const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]); - const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]); - return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition); - }); - - for (const item of storedFloorItems) { - if (!item.modelfileID) return; - const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]); - let storedPosition; - if (localStorage.getItem("cameraPosition")) { - storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!); - } else { - storedPosition = new THREE.Vector3(0, 40, 30); - } - - const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); - - if (cameraPosition.distanceTo(itemPosition) < renderDistance) { - await new Promise(async (resolve) => { - - // Check Three.js Cache - const cachedModel = THREE.Cache.get(item.modelfileID!); - if (cachedModel) { - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - return; - } - - // Check IndexedDB - const indexedDBModel = await retrieveGLTF(item.modelfileID!); - if (indexedDBModel) { - const blobUrl = URL.createObjectURL(indexedDBModel); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - }, - undefined, - (error) => { - echo.error(`[IndexedDB] Error loading ${item.modelName}:`); - URL.revokeObjectURL(blobUrl); - resolve(); - } - ); - return; - } - - // Fetch from Backend - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; - loader.load(modelUrl, async (gltf) => { - const modelBlob = await fetch(modelUrl).then((res) => res.blob()); - await storeGLTF(item.modelfileID!, modelBlob); - THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - }, - undefined, - (error) => { - echo.error(`[Backend] Error loading ${item.modelName}:`); - resolve(); - } - ); - }); - } else { - // - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); - - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); - } - } - - // Dispose loader after all models - dracoLoader.dispose(); - } -} - - -function processLoadedModel( - gltf: any, - item: Types.FloorItemType, - itemsGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState, - addEvent: (event: EventsSchema) => void, -) { - const model = gltf.clone(); - model.uuid = item.modelUuid; - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; - model.position.set(...item.position); - model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup?.current?.add(model); - - if (item.eventData) { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - 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 { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); - } - - gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' }); -} - -function checkLoadingCompletion( - modelsLoaded: number, - modelsToLoad: number, - dracoLoader: DRACOLoader, - resolve: () => void -) { - if (modelsLoaded === modelsToLoad) { - echo.success("Models Loaded!"); - dracoLoader.dispose(); - } - resolve(); -} - -export default loadInitialFloorItems; \ No newline at end of file diff --git a/app/src/modules/builder/assetGroup/assetsGroup.tsx b/app/src/modules/builder/assetGroup/assetsGroup.tsx index 5fabe2d..5fe75a4 100644 --- a/app/src/modules/builder/assetGroup/assetsGroup.tsx +++ b/app/src/modules/builder/assetGroup/assetsGroup.tsx @@ -1,13 +1,17 @@ import * as THREE from "three" import { useEffect } from 'react' import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; -import { useLoadingProgress } from '../../../store/builder/store'; +import { useLoadingProgress, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store'; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { FloorItems } from "../../../types/world/worldTypes"; +import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes"; import { useAssetsStore } from "../../../store/builder/useAssetStore"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; import Models from "./models/models"; +import useModuleStore from "../../../store/useModuleStore"; +import { useThree } from "@react-three/fiber"; +import { CameraControls } from "@react-three/drei"; +import addAssetModel from "../geomentries/assets/addAssetModel"; const gltfLoaderWorker = new Worker( new URL( @@ -16,10 +20,15 @@ const gltfLoaderWorker = new Worker( ) ); -function AssetsGroup() { +function AssetsGroup({ floorGroup, plane }: { floorGroup: RefGroup, plane: RefMesh }) { + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const { controls, gl, pointer, camera, raycaster } = useThree(); const { setLoadingProgress } = useLoadingProgress(); - const { setAssets } = useAssetsStore(); + const { setAssets, addAsset } = useAssetsStore(); const { addEvent } = useEventsStore(); + const { setSelectedFloorItem } = useSelectedFloorItem(); + const { selectedItem, setSelectedItem } = useSelectedItem(); const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); @@ -243,6 +252,43 @@ function AssetsGroup() { }; }, []); + useEffect(() => { + const canvasElement = gl.domElement; + + const onDrop = (event: any) => { + if (!event.dataTransfer?.files[0]) return; + + if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { + + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + + addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane); + } + }; + + const onDragOver = (event: any) => { + event.preventDefault(); + }; + + + if (activeModule === "builder") { + canvasElement.addEventListener("drop", onDrop); + canvasElement.addEventListener("dragover", onDragOver); + } else { + if ((controls as CameraControls)) { + const target = (controls as CameraControls).getTarget(new THREE.Vector3()); + (controls as CameraControls).setTarget(target.x, 0, target.z, true); + setSelectedFloorItem(null); + } + } + + return () => { + canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("dragover", onDragOver); + }; + }, [selectedItem, camera, pointer, activeModule, controls]); + return ( <> diff --git a/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx b/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx index c1d4881..18648dc 100644 --- a/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx +++ b/app/src/modules/builder/assetGroup/models/model/assetBoundingBox.tsx @@ -10,7 +10,7 @@ export const AssetBoundingBox = ({ asset, boundingBox }: { asset: Asset, boundin const edges = new EdgesGeometry(boxGeometry); return ( - + diff --git a/app/src/modules/builder/assetGroup/models/model/model.tsx b/app/src/modules/builder/assetGroup/models/model/model.tsx index e58ef9f..07e027c 100644 --- a/app/src/modules/builder/assetGroup/models/model/model.tsx +++ b/app/src/modules/builder/assetGroup/models/model/model.tsx @@ -7,12 +7,18 @@ 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'; +import { useAssetsStore } from '../../../../../store/builder/useAssetStore'; +import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import { useSocketStore } from '../../../../../store/builder/store'; function Model({ asset }: { asset: Asset }) { const { camera, controls } = useThree(); const { activeTool } = useActiveTool(); + const { removeAsset } = useAssetsStore(); + const { socket } = useSocketStore(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); - const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { setSelectedFloorItem } = useSelectedFloorItem(); const { renderDistance } = useRenderDistance(); const [isRendered, setIsRendered] = useState(false); const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; @@ -93,7 +99,7 @@ function Model({ asset }: { asset: Asset }) { } }) - const handleAssetDouble = (asset: Asset) => { + const handleDblClick = (asset: Asset) => { if (asset) { if (activeTool === "cursor" && boundingBox && groupRef.current) { const size = boundingBox.getSize(new THREE.Vector3()); @@ -126,6 +132,60 @@ function Model({ asset }: { asset: Asset }) { } }; + const handleClick = (asset: Asset) => { + if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + + //REST + + // const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName); + + //SOCKET + + const data = { + organization: organization, + modelUuid: asset.modelUuid, + modelName: asset.modelName, + socketId: socket.id + } + + const response = socket.emit('v2:model-asset:delete', data) + + useEventsStore.getState().removeEvent(asset.modelUuid); + useProductStore.getState().deleteEvent(asset.modelUuid); + + if (response) { + + const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); + const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== asset.modelUuid); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + removeAsset(asset.modelUuid); + + echo.success("Model Removed!"); + } + + } + } + + const handlePointerOver = (asset: Asset) => { + if (activeTool === "delete") { + if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + return; + } else { + setDeletableFloorItem(groupRef.current); + } + } + } + + const handlePointerOut = (asset: Asset) => { + if (activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + setDeletableFloorItem(null); + } + } + return ( { + e.stopPropagation(); + handleDblClick(asset); + }} onClick={(e) => { e.stopPropagation(); - handleAssetDouble(asset); + handleClick(asset); + }} + onPointerOver={(e) => { + e.stopPropagation(); + handlePointerOver(asset); + }} + onPointerOut={(e) => { + e.stopPropagation(); + handlePointerOut(asset); }} > {gltfScene && ( diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index 3189968..dcd2659 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -33,7 +33,6 @@ import loadWalls from "./geomentries/walls/loadWalls"; import * as Types from "../../types/world/worldTypes"; import SocketResponses from "../collaboration/socket/socketResponses.dev"; -import FloorItemsGroup from "./groups/floorItemsGroup"; import FloorPlanGroup from "./groups/floorPlanGroup"; import FloorGroup from "./groups/floorGroup"; import FloorGroupAilse from "./groups/floorGroupAisle"; @@ -87,7 +86,6 @@ export default function Builder() { const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. - const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. @@ -96,7 +94,6 @@ export default function Builder() { const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. - const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... @@ -243,15 +240,6 @@ export default function Builder() { /> - {/* */} - + diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 692664e..7847651 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -1,15 +1,11 @@ import * as THREE from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import gsap from 'gsap'; -import { toast } from 'react-toastify'; -import TempLoader from './tempLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import * as Types from "../../../../types/world/worldTypes"; import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { Socket } from 'socket.io-client'; import * as CONSTANTS from '../../../../types/world/worldConstants'; -import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator'; async function addAssetModel( @@ -17,14 +13,11 @@ async function addAssetModel( camera: THREE.Camera, pointer: THREE.Vector2, floorGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState, - itemsGroup: Types.RefGroup, - isTempLoader: Types.RefBoolean, - tempLoader: Types.RefMesh, socket: Socket, selectedItem: any, setSelectedItem: any, addEvent: (event: EventsSchema) => void, + addAsset: (asset: Asset) => void, plane: Types.RefMesh, ): Promise { @@ -33,7 +26,6 @@ async function addAssetModel( let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; try { - isTempLoader.current = true; const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); @@ -61,46 +53,32 @@ async function addAssetModel( if (intersectPoint.y < 0) { intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z); } - // console.log('selectedItem: ', selectedItem); const cachedModel = THREE.Cache.get(selectedItem.id); if (cachedModel) { - // console.log(`[Cache] Fetching ${selectedItem.name}`); - handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); + handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket); return; } else { const cachedModelBlob = await retrieveGLTF(selectedItem.id); - if (cachedModelBlob) { - // console.log(`Added ${selectedItem.name} from indexDB`); - const blobUrl = URL.createObjectURL(cachedModelBlob); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); THREE.Cache.remove(blobUrl); THREE.Cache.add(selectedItem.id, gltf); - handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); - }, - () => { - TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); - }); + handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket); + }); } else { - // console.log(`Added ${selectedItem.name} from Backend`); - loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`, async (gltf) => { const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob()); await storeGLTF(selectedItem.id, modelBlob); THREE.Cache.add(selectedItem.id, gltf); - await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); - }, - () => { - TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); - }); + await handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket); + }) } } } } catch (error) { echo.error("Failed to add asset"); - console.error('Error fetching asset model:', error); } finally { setSelectedItem({}); } @@ -110,11 +88,8 @@ async function handleModelLoad( gltf: any, intersectPoint: THREE.Vector3, selectedItem: any, - itemsGroup: Types.RefGroup, - tempLoader: Types.RefMesh, - isTempLoader: Types.RefBoolean, - setFloorItems: Types.setFloorItemSetState, addEvent: (event: EventsSchema) => void, + addAsset: (asset: Asset) => void, socket: Socket ) { const model = gltf.scene.clone(); @@ -129,22 +104,16 @@ async function handleModelLoad( } }); - itemsGroup.current.add(model); - if (tempLoader.current) { - (tempLoader.current.material).dispose(); - (tempLoader.current.geometry).dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { + const newFloorItem: Asset = { modelUuid: model.uuid, modelName: selectedItem.name, - modelfileID: selectedItem.id, + assetId: selectedItem.id, position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z], - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + rotation: [0, 0, 0], isLocked: false, - isVisible: true + isVisible: true, + isCollidable: false, + opacity: 1, }; const email = localStorage.getItem("email"); @@ -158,7 +127,7 @@ async function handleModelLoad( // newFloorItem.modelName, // newFloorItem.modelfileID, // newFloorItem.position, - // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + // { x: 0, y: 0, z: 0 }, // false, // true, // ); @@ -183,7 +152,7 @@ async function handleModelLoad( modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + rotation: newFloorItem.rotation, state: "idle", type: 'transfer', speed: 1, @@ -252,7 +221,7 @@ async function handleModelLoad( modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + rotation: newFloorItem.rotation, state: "idle", type: "vehicle", speed: 1, @@ -285,7 +254,7 @@ async function handleModelLoad( modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + rotation: newFloorItem.rotation, state: "idle", type: "roboticArm", speed: 1, @@ -319,7 +288,7 @@ async function handleModelLoad( modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + rotation: newFloorItem.rotation, state: "idle", type: "machine", point: { @@ -347,7 +316,7 @@ async function handleModelLoad( modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + rotation: newFloorItem.rotation, state: "idle", type: "storageUnit", point: { @@ -375,7 +344,7 @@ async function handleModelLoad( organization, modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, - modelfileID: newFloorItem.modelfileID, + modelfileID: newFloorItem.assetId, position: newFloorItem.position, rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isLocked: false, @@ -384,27 +353,30 @@ async function handleModelLoad( eventData: eventData }; - model.userData.eventData = eventData; - - newFloorItem.eventData = eventData; - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - socket.emit("v2:model-asset:add", completeData); - gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } }); + const asset: Asset = { + modelUuid: completeData.modelUuid, + modelName: completeData.modelName, + assetId: completeData.modelfileID, + position: completeData.position, + rotation: [completeData.rotation.x, completeData.rotation.y, completeData.rotation.z] as [number, number, number], + isLocked: completeData.isLocked, + isCollidable: false, + isVisible: completeData.isVisible, + opacity: 1, + eventData: completeData.eventData + } + + addAsset(asset); + } else { const data = { organization, modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, - modelfileID: newFloorItem.modelfileID, + modelfileID: newFloorItem.assetId, position: newFloorItem.position, rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isLocked: false, @@ -412,16 +384,22 @@ async function handleModelLoad( socketId: socket.id }; - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - socket.emit("v2:model-asset:add", data); - gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } }); + const asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [number, number, number], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1 + } + + addAsset(asset); + } } diff --git a/app/src/modules/builder/geomentries/assets/assetManager.ts b/app/src/modules/builder/geomentries/assets/assetManager.ts deleted file mode 100644 index 5d2f90f..0000000 --- a/app/src/modules/builder/geomentries/assets/assetManager.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from "three"; -import gsap from "gsap"; -import * as Types from "../../../../types/world/worldTypes"; -import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -import { initializeDB, retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils"; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import { toast } from 'react-toastify'; - -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; -let currentTaskId = 0; // Track the active task -let activePromises = new Map(); // Map to track task progress - -export default async function assetManager( - data: any, - itemsGroup: Types.RefGroup, - loader: GLTFLoader, -) { - const taskId = ++currentTaskId; // Increment taskId for each call - activePromises.set(taskId, true); // Mark task as active - - // - - if (data.toRemove.length > 0) { - data.toRemove.forEach((uuid: string) => { - const item = itemsGroup.current.getObjectByProperty("uuid", uuid); - if (item) { - // Traverse and dispose of resources - // item.traverse((child: THREE.Object3D) => { - // if (child instanceof THREE.Mesh) { - // if (child.geometry) child.geometry.dispose(); - // if (Array.isArray(child.material)) { - // child.material.forEach((material) => { - // if (material.map) material.map.dispose(); - // material.dispose(); - // }); - // } else if (child.material) { - // if (child.material.map) child.material.map.dispose(); - // child.material.dispose(); - // } - // } - // }); - - // Remove the object from the scene - itemsGroup.current.remove(item); - } - }); - } - - if (data.toAdd.length > 0) { - await initializeDB(); - - for (const item of data.toAdd) { - if (!activePromises.get(taskId)) return; // Stop processing if task is canceled - - await new Promise(async (resolve) => { - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; - - // Check Three.js Cache - const cachedModel = THREE.Cache.get(item.modelfileID!); - if (cachedModel) { - // - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, resolve); - return; - } - - // Check IndexedDB - const indexedDBModel = await retrieveGLTF(item.modelfileID!); - if (indexedDBModel) { - // - const blobUrl = URL.createObjectURL(indexedDBModel); - loader.load( - blobUrl, - (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(item.modelfileID!, gltf); // Add to cache - processLoadedModel(gltf.scene.clone(), item, itemsGroup, resolve); - }, - undefined, - (error) => { - echo.error(`[IndexedDB] Error loading ${item.modelName}:`); - resolve(); - } - ); - return; - } - - // Fetch from Backend - // - loader.load( - modelUrl, - async (gltf) => { - const modelBlob = await fetch(modelUrl).then((res) => res.blob()); - await storeGLTF(item.modelfileID!, modelBlob); // Store in IndexedDB - THREE.Cache.add(item.modelfileID!, gltf); // Add to cache - processLoadedModel(gltf.scene.clone(), item, itemsGroup, resolve); - }, - undefined, - (error) => { - echo.error(`[Backend] Error loading ${item.modelName}:`); - resolve(); - } - ); - }); - } - - function processLoadedModel( - gltf: any, - item: Types.FloorItemType, - itemsGroup: Types.RefGroup, - resolve: () => void - ) { - if (!activePromises.get(taskId)) return; // Stop processing if task is canceled - - const existingModel = itemsGroup?.current?.getObjectByProperty("uuid", item.modelUuid); - if (existingModel) { - // - resolve(); - return; - } - - const model = gltf; - model.uuid = item.modelUuid; - model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - model.position.set(...item.position); - model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - - itemsGroup?.current?.add(model); - - gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 0.5, ease: "power2.out", onStart: resolve, }); - } - } - - activePromises.delete(taskId); // Mark task as complete -} - -// Cancel ongoing task when new call arrives -export function cancelOngoingTasks() { - activePromises.clear(); // Clear all ongoing tasks -} diff --git a/app/src/modules/builder/geomentries/assets/assetVisibility.ts b/app/src/modules/builder/geomentries/assets/assetVisibility.ts deleted file mode 100644 index c2503c3..0000000 --- a/app/src/modules/builder/geomentries/assets/assetVisibility.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -let lastUpdateTime = 0; - -export default function assetVisibility( - itemsGroup: Types.RefGroup, - cameraPosition: Types.Vector3, - renderDistance: Types.Number, - throttleTime = 100 -): void { - const now = performance.now(); - if (now - lastUpdateTime < throttleTime) return; - lastUpdateTime = now; - - if (!itemsGroup?.current || !cameraPosition) return; - - itemsGroup.current.children.forEach((child) => { - const Distance = cameraPosition.distanceTo(child.position); - if (Distance <= renderDistance) { - child.visible = true; - } else { - child.visible = false; - } - }); -} diff --git a/app/src/modules/builder/geomentries/assets/deletableHoveredFloorItems.ts b/app/src/modules/builder/geomentries/assets/deletableHoveredFloorItems.ts deleted file mode 100644 index 2913b1a..0000000 --- a/app/src/modules/builder/geomentries/assets/deletableHoveredFloorItems.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function DeletableHoveredFloorItems( - state: Types.ThreeState, - itemsGroup: Types.RefGroup, - hoveredDeletableFloorItem: Types.RefMesh, - setDeletableFloorItem: any -): void { - - ////////// Altering the color of the hovered GLTF item during the Deletion time ////////// - - state.raycaster.setFromCamera(state.pointer, state.camera); - const intersects = state.raycaster.intersectObjects(itemsGroup.current.children, true); - - if (intersects.length > 0) { - if (intersects[0].object.name === "Pole") { - return; - } - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - hoveredDeletableFloorItem.current = currentObject as THREE.Mesh; - setDeletableFloorItem(currentObject); - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - } else { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } - } -} - -export default DeletableHoveredFloorItems; diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts deleted file mode 100644 index 5b234b4..0000000 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { toast } from 'react-toastify'; -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; -// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; -import { Socket } from 'socket.io-client'; -import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; -import { useEventsStore } from "../../../../store/simulation/useEventsStore"; -import { useProductStore } from "../../../../store/simulation/useProductStore"; - -async function DeleteFloorItems( - itemsGroup: Types.RefGroup, - hoveredDeletableFloorItem: Types.RefMesh, - setFloorItems: Types.setFloorItemSetState, - socket: Socket -): Promise { - - ////////// Deleting the hovered Floor GLTF from the scene (itemsGroup.current) and from the floorItems and also update it in the localstorage ////////// - - if (hoveredDeletableFloorItem.current) { - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - const items = await getFloorAssets(organization); - const removedItem = items.find( - (item: { modelUuid: string }) => item.modelUuid === hoveredDeletableFloorItem.current?.uuid - ); - - if (!removedItem) { - return - } - - //REST - - // const response = await deleteFloorItem(organization, removedItem.modelUuid, removedItem.modelName); - - //SOCKET - - const data = { - organization: organization, - modelUuid: removedItem.modelUuid, - modelName: removedItem.modelName, - socketId: socket.id - } - - const response = socket.emit('v2:model-asset:delete', data) - - useEventsStore.getState().removeEvent(removedItem.modelUuid); - useProductStore.getState().deleteEvent(removedItem.modelUuid); - - if (response) { - const updatedItems = items.filter( - (item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid - ); - - const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); - const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - if (hoveredDeletableFloorItem.current) { - // Traverse and dispose of resources - hoveredDeletableFloorItem.current.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - if (child.geometry) child.geometry.dispose(); - if (Array.isArray(child.material)) { - child.material.forEach((material) => { - if (material.map) material.map.dispose(); - material.dispose(); - }); - } else if (child.material) { - if (child.material.map) child.material.map.dispose(); - child.material.dispose(); - } - } - }); - - // Remove the object from the scene - itemsGroup.current.remove(hoveredDeletableFloorItem.current); - } - setFloorItems(updatedItems); - - echo.success("Model Removed!"); - } - } -} - -export default DeleteFloorItems; diff --git a/app/src/modules/builder/geomentries/assets/tempLoader.ts b/app/src/modules/builder/geomentries/assets/tempLoader.ts deleted file mode 100644 index 73d2f9e..0000000 --- a/app/src/modules/builder/geomentries/assets/tempLoader.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function TempLoader( - intersectPoint: Types.Vector3, - isTempLoader: Types.RefBoolean, - tempLoader: Types.RefMesh, - itemsGroup: Types.RefGroup -): void { - - ////////// Temporary Loader that indicates the gltf is being loaded ////////// - - ////////// Bug: Can't Load More than one TempLoader if done, it won't leave the scene ////////// - - if (tempLoader.current) { - itemsGroup.current.remove(tempLoader.current); - } - if (isTempLoader.current) { - const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); - const cubeMaterial = new THREE.MeshBasicMaterial({ color: "white" }); - tempLoader.current = new THREE.Mesh(cubeGeometry, cubeMaterial); - tempLoader.current.position.set(intersectPoint.x, 0.5 + intersectPoint.y, intersectPoint.z); - itemsGroup.current.add(tempLoader.current); - isTempLoader.current = false; - } -} - -export default TempLoader; diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx deleted file mode 100644 index 58f8f05..0000000 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ /dev/null @@ -1,445 +0,0 @@ -import { useFrame, useThree } from "@react-three/fiber"; -import { - useActiveTool, - useCamMode, - useDeletableFloorItem, - useDeleteTool, - useFloorItems, - useLoadingProgress, - useRenderDistance, - useSelectedFloorItem, - useSelectedItem, - useSocketStore, - useToggleView, -} from "../../../store/builder/store"; -import { useEffect } from "react"; -import * as THREE from "three"; -import * as Types from "../../../types/world/worldTypes"; -import assetManager, { - cancelOngoingTasks, -} from "../geomentries/assets/assetManager"; -import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems"; -import DeleteFloorItems from "../geomentries/assets/deleteFloorItems"; -import loadInitialFloorItems from "../IntialLoad/loadInitialFloorItems"; -import addAssetModel from "../geomentries/assets/addAssetModel"; -import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; -import useModuleStore from "../../../store/useModuleStore"; -import { useEventsStore } from "../../../store/simulation/useEventsStore"; -import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; - -const assetManagerWorker = new Worker( - new URL( - "../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", - import.meta.url - ) -); -const gltfLoaderWorker = new Worker( - new URL( - "../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", - import.meta.url - ) -); - -const FloorItemsGroup = ({ - itemsGroup, - hoveredDeletableFloorItem, - AttachedObject, - floorGroup, - tempLoader, - isTempLoader, - plane, -}: any) => { - const state: Types.ThreeState = useThree(); - const { raycaster, controls }: any = state; - const { renderDistance } = useRenderDistance(); - const { toggleView } = useToggleView(); - const { floorItems, setFloorItems } = useFloorItems(); - const { camMode } = useCamMode(); - const { deleteTool } = useDeleteTool(); - const { setDeletableFloorItem } = useDeletableFloorItem(); - const { setSelectedFloorItem } = useSelectedFloorItem(); - const { activeTool } = useActiveTool(); - const { selectedItem, setSelectedItem } = useSelectedItem(); - const { setLoadingProgress } = useLoadingProgress(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - const { addEvent } = useEventsStore(); - - dracoLoader.setDecoderPath( - "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" - ); - loader.setDRACOLoader(dracoLoader); - - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - findEnvironment( - organization, - localStorage.getItem("userId")! - ).then((evnironMentData) => { - - let totalAssets = 0; - let loadedAssets = 0; - - const updateLoadingProgress = (progress: number) => { - if (progress < 100) { - setLoadingProgress(progress); - } else if (progress === 100) { - setTimeout(() => { - setLoadingProgress(100); - setTimeout(() => { - setLoadingProgress(0); - }, 1500); - }, 1000); - } - }; - - getFloorAssets(organization).then((data) => { - if (data.length > 0) { - const uniqueItems = (data as Types.FloorItems).filter( - (item, index, self) => - index === self.findIndex((t) => t.modelfileID === item.modelfileID) - ); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { - updateLoadingProgress(100); - return; - } - gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); - } else { - gltfLoaderWorker.postMessage({ floorItems: [] }); - loadInitialFloorItems( - itemsGroup, - setFloorItems, - addEvent, - evnironMentData.renderDistance - ); - updateLoadingProgress(100); - } - }); - - gltfLoaderWorker.onmessage = async (event) => { - if (event.data.message === "gltfLoaded" && event.data.modelBlob) { - const blobUrl = URL.createObjectURL(event.data.modelBlob); - - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(event.data.modelID, gltf); - - loadedAssets++; - const progress = Math.round((loadedAssets / totalAssets) * 100); - updateLoadingProgress(progress); - - if (loadedAssets === totalAssets) { - loadInitialFloorItems( - itemsGroup, - setFloorItems, - addEvent, - evnironMentData.renderDistance - ); - updateLoadingProgress(100); - } - }); - } - }; - }) - }, []); - - useEffect(() => { - assetManagerWorker.onmessage = async (event) => { - cancelOngoingTasks(); // Cancel the ongoing process - await assetManager(event.data, itemsGroup, loader); - }; - }, [assetManagerWorker]); - - useEffect(() => { - if (toggleView) return; - - const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { - uuids.push(child.uuid); - }); - const cameraPosition = state.camera.position; - - assetManagerWorker.postMessage({ - floorItems, - cameraPosition, - uuids, - renderDistance, - }); - }, [camMode, renderDistance]); - - useEffect(() => { - const controls: any = state.controls; - const camera: any = state.camera; - - if (controls) { - let intervalId: NodeJS.Timeout | null = null; - - const handleChange = () => { - if (toggleView) return; - - const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { - uuids.push(child.uuid); - }); - const cameraPosition = camera.position; - - assetManagerWorker.postMessage({ - floorItems, - cameraPosition, - uuids, - renderDistance, - }); - }; - - const startInterval = () => { - intervalId ??= setInterval(handleChange, 50); - }; - - const stopInterval = () => { - handleChange(); - if (intervalId) { - clearInterval(intervalId); - intervalId = null; - } - }; - - controls.addEventListener("rest", handleChange); - controls.addEventListener("rest", stopInterval); - controls.addEventListener("control", startInterval); - controls.addEventListener("controlend", stopInterval); - - return () => { - controls.removeEventListener("rest", handleChange); - controls.removeEventListener("rest", stopInterval); - controls.removeEventListener("control", startInterval); - controls.removeEventListener("controlend", stopInterval); - if (intervalId) { - clearInterval(intervalId); - } - }; - } - }, [state.controls, floorItems, toggleView, renderDistance]); - - useEffect(() => { - const canvasElement = state.gl.domElement; - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = true; - drag = false; - } - }; - - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onMouseUp = async (evt: any) => { - if (controls) { - (controls as any).enabled = true; - } - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - if (deleteTool) { - DeleteFloorItems( - itemsGroup, - hoveredDeletableFloorItem, - setFloorItems, - socket - ); - } - - if (activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects( - itemsGroup.current.children, - true - ); - if ( - intersects.length > 0 && - intersects[0]?.object?.parent?.parent?.position && - intersects[0]?.object?.parent?.parent?.scale && - intersects[0]?.object?.parent?.parent?.rotation - ) { - // let currentObject = intersects[0].object; - // while (currentObject) { - // if (currentObject.name === "Scene") { - // break; - // } - // currentObject = currentObject.parent as THREE.Object3D; - // } - // if (currentObject) { - // AttachedObject.current = currentObject as any; - // setSelectedFloorItem(AttachedObject.current!); - // } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - } - }; - - const onDblClick = async (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - - if (activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects( - itemsGroup.current.children, - true - ); - if ( - intersects.length > 0 && - intersects[0]?.object?.parent?.parent?.position && - intersects[0]?.object?.parent?.parent?.scale && - intersects[0]?.object?.parent?.parent?.rotation - ) { - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - if (currentObject) { - AttachedObject.current = currentObject as any; - // controls.fitToSphere(AttachedObject.current!, true); - - const bbox = new THREE.Box3().setFromObject( - AttachedObject.current - ); - const size = bbox.getSize(new THREE.Vector3()); - const center = bbox.getCenter(new THREE.Vector3()); - - const front = new THREE.Vector3(0, 0, 1); - AttachedObject.current.localToWorld(front); - front.sub(AttachedObject.current.position).normalize(); - - const distance = Math.max(size.x, size.y, size.z) * 2; - const newPosition = center - .clone() - .addScaledVector(front, distance); - - controls.setPosition( - newPosition.x, - newPosition.y, - newPosition.z, - true - ); - controls.setTarget(center.x, center.y, center.z, true); - controls.fitToBox(AttachedObject.current!, true, { - cover: true, - paddingTop: 5, - paddingLeft: 5, - paddingBottom: 5, - paddingRight: 5, - }); - - setSelectedFloorItem(AttachedObject.current!); - } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - } - }; - - const onDrop = (event: any) => { - if (!event.dataTransfer?.files[0]) return; - - if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { - - state.pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - state.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - - addAssetModel( - raycaster, - state.camera, - state.pointer, - floorGroup, - setFloorItems, - itemsGroup, - isTempLoader, - tempLoader, - socket, - selectedItem, - setSelectedItem, - addEvent, - plane - ); - } - }; - - const onDragOver = (event: any) => { - event.preventDefault(); - }; - - if (activeModule === "builder") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("dblclick", onDblClick); - canvasElement.addEventListener("drop", onDrop); - canvasElement.addEventListener("dragover", onDragOver); - } else { - if (controls) { - const target = controls.getTarget(new THREE.Vector3()); - controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("dblclick", onDblClick); - canvasElement.removeEventListener("drop", onDrop); - canvasElement.removeEventListener("dragover", onDragOver); - }; - }, [deleteTool, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); - - useFrame(() => { - if (controls) - if (deleteTool && activeModule === "builder") { - // assetVisibility(itemsGroup, state.camera.position, renderDistance); - DeletableHoveredFloorItems( - state, - itemsGroup, - hoveredDeletableFloorItem, - setDeletableFloorItem - ); - } else if (!deleteTool) { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } - } - }); - - return ; -}; - -export default FloorItemsGroup; diff --git a/app/src/modules/collaboration/socket/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx index 441bae4..6e0051c 100644 --- a/app/src/modules/collaboration/socket/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -19,7 +19,6 @@ import { import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; -import TempLoader from "../../builder/geomentries/assets/tempLoader"; // import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import objectLineToArray from "../../builder/geomentries/lines/lineConvertions/objectLineToArray"; @@ -286,14 +285,6 @@ export default function SocketResponses({ }); THREE.Cache.add(data.data.modelName, gltf); - }, - () => { - TempLoader( - new THREE.Vector3(...data.data.position), - isTempLoader, - tempLoader, - itemsGroup - ); } ); } diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index dd52900..6c7a91c 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -6,8 +6,21 @@ import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; -const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { +const CopyPasteControls = ({ + copiedObjects, + setCopiedObjects, + pastedObjects, + setpastedObjects, + selectionGroup, + setDuplicatedObjects, + movedObjects, + setMovedObjects, + rotatedObjects, + setRotatedObjects, + boundingBoxRef +}: any) => { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); @@ -15,6 +28,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const { addEvent } = useEventsStore(); + const { addAsset } = useAssetsStore(); useEffect(() => { if (!camera || !scene || toggleView) return; @@ -132,16 +146,15 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas const addPastedObjects = () => { if (pastedObjects.length === 0) return; pastedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); - obj.position.copy(worldPosition); - - if (itemsGroupRef.current) { + if (obj) { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + obj.position.copy(worldPosition); const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: THREE.MathUtils.generateUUID(), + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, isLocked: false, @@ -362,7 +375,20 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas eventData: JSON.parse(JSON.stringify(eventData)) }; - itemsGroupRef.current.add(obj); + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + eventData: data.eventData + } + + addAsset(asset); } else { setFloorItems((prevItems: Types.FloorItems) => { @@ -403,13 +429,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas socket.emit("v2:model-asset:add", data); - obj.userData = { - name: newFloorItem.modelName, - modelId: newFloorItem.modelfileID, - modelUuid: newFloorItem.modelUuid, - }; + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + } - itemsGroupRef.current.add(obj); + addAsset(asset); } } }); diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index 2dd9e37..93c99fc 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -4,11 +4,21 @@ import { useFrame, useThree } from "@react-three/fiber"; import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; -const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { +const DuplicationControls = ({ + duplicatedObjects, + setDuplicatedObjects, + setpastedObjects, + selectionGroup, + movedObjects, + setMovedObjects, + rotatedObjects, + setRotatedObjects, + boundingBoxRef +}: any) => { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); @@ -16,6 +26,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const { addEvent } = useEventsStore(); + const { addAsset } = useAssetsStore(); useEffect(() => { if (!camera || !scene || toggleView) return; @@ -110,16 +121,15 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb const addDuplicatedAssets = () => { if (duplicatedObjects.length === 0) return; duplicatedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); - obj.position.copy(worldPosition); - - if (itemsGroupRef.current) { + if (obj) { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + obj.position.copy(worldPosition); const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: THREE.MathUtils.generateUUID(), + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, isLocked: false, @@ -332,14 +342,20 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb socket.emit("v2:model-asset:add", data); - obj.userData = { - name: newFloorItem.modelName, - modelId: newFloorItem.modelfileID, - modelUuid: newFloorItem.modelUuid, - eventData: JSON.parse(JSON.stringify(eventData)) - }; + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + eventData: data.eventData + } - itemsGroupRef.current.add(obj); + addAsset(asset); } else { setFloorItems((prevItems: Types.FloorItems) => { @@ -380,13 +396,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb socket.emit("v2:model-asset:add", data); - obj.userData = { - name: newFloorItem.modelName, - modelId: newFloorItem.modelfileID, - modelUuid: newFloorItem.modelUuid, - }; + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + } - itemsGroupRef.current.add(obj); + addAsset(asset); } } }); diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index a41755c..de50c84 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -16,11 +16,11 @@ import { useSelectedProduct } from "../../../../store/simulation/useSimulationSt import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { snapControls } from "../../../../utils/handleSnap"; import DistanceFindingControls from "./distanceFindingControls"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; function MoveControls({ movedObjects, setMovedObjects, - itemsGroupRef, pastedObjects, setpastedObjects, duplicatedObjects, @@ -36,12 +36,12 @@ function MoveControls({ const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const { selectedProduct } = useSelectedProduct(); - const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); - const itemsData = useRef([]); const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">(""); const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; + const { updateAsset } = useAssetsStore(); + const AssetGroup = useRef(undefined); const updateBackend = ( productName: string, @@ -58,11 +58,19 @@ function MoveControls({ }; useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; + if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; + const itemsGroup: any = scene.getObjectByName("Asset Group"); + AssetGroup.current = itemsGroup; + + if (!AssetGroup.current) { + console.error("Asset Group not found in the scene."); + return; + } + let isMoving = false; const onPointerDown = () => { @@ -91,15 +99,12 @@ function MoveControls({ clearSelection(); movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); - setMovedObjects([]); - itemsData.current = []; } setKeyEvent(""); }; @@ -119,9 +124,6 @@ function MoveControls({ if (keyCombination === "G") { if (selectedAssets.length > 0) { moveAssets(); - itemsData.current = floorItems.filter((item: { modelUuid: string }) => - selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) - ); } } @@ -130,15 +132,12 @@ function MoveControls({ clearSelection(); movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); - setMovedObjects([]); - itemsData.current = []; } }; @@ -157,7 +156,7 @@ function MoveControls({ canvasElement.removeEventListener("keydown", onKeyDown); canvasElement?.removeEventListener("keyup", onKeyUp); }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]); + }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]); let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; @@ -219,11 +218,6 @@ function MoveControls({ }); const moveAssets = () => { - const updatedItems = floorItems.filter( - (item: { modelUuid: string }) => - !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) - ); - setFloorItems(updatedItems); setMovedObjects(selectedAssets); selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); @@ -234,17 +228,17 @@ function MoveControls({ if (movedObjects.length === 0) return; movedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); + if (obj && AssetGroup.current) { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); - selectionGroup.current.remove(obj); - obj.position.copy(worldPosition); + selectionGroup.current.remove(obj); + obj.position.copy(worldPosition); - if (itemsGroupRef.current) { const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: obj.userData.modelUuid, + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, @@ -287,10 +281,9 @@ function MoveControls({ } } - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; + updateAsset(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }); //REST @@ -322,12 +315,12 @@ function MoveControls({ socket.emit("v2:model-asset:add", data); - itemsGroupRef.current.add(obj); + AssetGroup.current.add(obj); } }); + echo.success("Object moved!"); - itemsData.current = []; clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 65536d0..b8b9095 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -8,20 +8,32 @@ import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; + +function RotateControls({ + rotatedObjects, + setRotatedObjects, + movedObjects, + setMovedObjects, + pastedObjects, + setpastedObjects, + duplicatedObjects, + setDuplicatedObjects, + selectionGroup +}: any) { -function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const { selectedProduct } = useSelectedProduct(); - const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); - const itemsData = useRef([]); const email = localStorage.getItem('email') const organization = (email?.split("@")[1])?.split(".")[0] ?? null; + const { updateAsset } = useAssetsStore(); + const AssetGroup = useRef(undefined); const updateBackend = ( productName: string, @@ -40,11 +52,19 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const prevPointerPosition = useRef(null); useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; + if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; + const itemsGroup: any = scene.getObjectByName("Asset Group"); + AssetGroup.current = itemsGroup; + + if (!AssetGroup.current) { + console.error("Asset Group not found in the scene."); + return; + } + let isMoving = false; const onPointerDown = () => { @@ -65,15 +85,12 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo clearSelection(); rotatedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); - setRotatedObjects([]); - itemsData.current = []; } }; @@ -82,7 +99,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo if (event.key.toLowerCase() === "r") { if (selectedAssets.length > 0) { rotateAssets(); - itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); } } if (event.key.toLowerCase() === "escape") { @@ -90,15 +106,13 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo clearSelection(); rotatedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); setRotatedObjects([]); - itemsData.current = []; } }; @@ -115,7 +129,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("keydown", onKeyDown); }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); + }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); useFrame(() => { if (rotatedObjects.length > 0) { @@ -146,9 +160,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo }); const rotateAssets = () => { - const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); - setFloorItems(updatedItems); - const box = new THREE.Box3(); selectedAssets.forEach((asset: any) => box.expandByObject(asset)); const center = new THREE.Vector3(); @@ -173,26 +184,24 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo if (rotatedObjects.length === 0) return; rotatedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - const worldQuaternion = new THREE.Quaternion(); + if (obj && AssetGroup.current) { + const worldPosition = new THREE.Vector3(); + const worldQuaternion = new THREE.Quaternion(); - obj.getWorldPosition(worldPosition); - obj.getWorldQuaternion(worldQuaternion); + obj.getWorldPosition(worldPosition); + obj.getWorldQuaternion(worldQuaternion); - selectionGroup.current.remove(obj); + selectionGroup.current.remove(obj); - obj.position.copy(worldPosition); - obj.quaternion.copy(worldQuaternion); - - - if (itemsGroupRef.current) { + obj.position.copy(worldPosition); + obj.quaternion.copy(worldQuaternion); const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: obj.userData.modelUuid, + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true }; @@ -221,15 +230,14 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo event ); } - + newFloorItem.eventData = eventData; } } - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; + updateAsset(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }); //REST @@ -261,12 +269,11 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo socket.emit("v2:model-asset:add", data); - itemsGroupRef.current.add(obj); + AssetGroup.current.add(obj); } }); echo.success("Object rotated!"); - itemsData.current = []; clearSelection(); } diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 763faa3..691bb7c 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -15,10 +15,10 @@ import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; const SelectionControls: React.FC = () => { const { camera, controls, gl, scene, pointer } = useThree(); - const itemsGroupRef = useRef(undefined); const selectionGroup = useRef() as Types.RefGroup; const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); @@ -28,9 +28,9 @@ const SelectionControls: React.FC = () => { const [pastedObjects, setpastedObjects] = useState([]); const [duplicatedObjects, setDuplicatedObjects] = useState([]); const boundingBoxRef = useRef(); - const { floorItems, setFloorItems } = useFloorItems(); const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); + const { removeAsset } = useAssetsStore(); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); useEffect(() => { @@ -39,9 +39,6 @@ const SelectionControls: React.FC = () => { const canvasElement = gl.domElement; canvasElement.tabIndex = 0; - const itemsGroup: any = scene.getObjectByName("itemsGroup"); - itemsGroupRef.current = itemsGroup; - let isSelecting = false; let isRightClick = false; let rightClickMoved = false; @@ -49,11 +46,6 @@ const SelectionControls: React.FC = () => { const helper = new SelectionHelper(gl); - // if (!itemsGroup) { - // echo.warn("itemsGroup not found in the scene."); - // return; - // } - const onPointerDown = (event: PointerEvent) => { if (event.button === 2) { isRightClick = true; @@ -145,7 +137,7 @@ const SelectionControls: React.FC = () => { helper.enabled = false; helper.dispose(); }; - }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); + }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule,]); useEffect(() => { if (activeModule !== "builder") { @@ -213,14 +205,14 @@ const SelectionControls: React.FC = () => { selectedAssets.forEach((selectedMesh: THREE.Object3D) => { //REST - // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); + // const response = await deleteFloorItem(organization, selectedMesh.userData.modelUuid, selectedMesh.userData.modelName); //SOCKET const data = { organization: organization, - modelUuid: selectedMesh.uuid, - modelName: selectedMesh.userData.name, + modelUuid: selectedMesh.userData.modelUuid, + modelName: selectedMesh.userData.modelName, socketId: socket.id, }; @@ -244,11 +236,11 @@ const SelectionControls: React.FC = () => { } }); - itemsGroupRef.current?.remove(selectedMesh); }); - const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); - setFloorItems(updatedItems); + selectedUUIDs.forEach((uuid: string) => { + removeAsset(uuid); + }); } echo.success("Selected models removed!"); clearSelection(); @@ -262,13 +254,13 @@ const SelectionControls: React.FC = () => { - + - + - + - + ); }; diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 7c5db64..9a449d6 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -27,9 +27,13 @@ export default function PostProcessing() { } useEffect(()=>{ - console.log('selectedFloorItem: ', selectedFloorItem); + // console.log('selectedFloorItem: ', selectedFloorItem); },[selectedFloorItem]) + useEffect(()=>{ + // console.log('selectedFloorItem: ', deletableFloorItem); + },[deletableFloorItem]) + return ( <> diff --git a/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js b/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js deleted file mode 100644 index 50dac3f..0000000 --- a/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js +++ /dev/null @@ -1,51 +0,0 @@ -import * as THREE from 'three'; -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; - -const loader = new GLTFLoader(); -const dracoLoader = new DRACOLoader(); -dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); -loader.setDRACOLoader(dracoLoader); - -onmessage = (event) => { - const { floorItems, cameraPosition, uuids, renderDistance } = event.data; - if (!floorItems) return - - const toAdd = []; - const toRemove = []; - - const cameraPos = new THREE.Vector3(cameraPosition.x, cameraPosition.y, cameraPosition.z); - - // Check for items to be added - floorItems.forEach((item) => { - const itemPosition = new THREE.Vector3(...item.position); - const distance = cameraPos.distanceTo(itemPosition); - - if (distance <= renderDistance && !uuids.includes(item.modelUuid)) { - toAdd.push(item); - } - }); - - // Sort the toAdd array based on distance (closest first) - toAdd.sort((a, b) => { - const aDistance = cameraPos.distanceTo(new THREE.Vector3(...a.position)); - const bDistance = cameraPos.distanceTo(new THREE.Vector3(...b.position)); - return aDistance - bDistance; - }); - - // Check for items to be removed - uuids.forEach((uuid) => { - const floorItem = floorItems.find((item) => item.modelUuid === uuid); - if (floorItem) { - const itemPosition = new THREE.Vector3(...floorItem.position); - const distance = cameraPos.distanceTo(itemPosition); - - if (distance > renderDistance) { - toRemove.push(uuid); - } - } - }); - - // Send the result back to the main thread - postMessage({ toAdd, toRemove }); -}; diff --git a/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js b/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js index 9e7397d..f98ad9b 100644 --- a/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js +++ b/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js @@ -17,6 +17,9 @@ onmessage = async (event) => { ); for (const item of uniqueItems) { + if(item.modelfileID === null || item.modelfileID === undefined) { + continue; // Skip items without a valid modelfileID + } const modelID = item.modelfileID; const indexedDBModel = await retrieveGLTF(modelID);