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 { toast } from 'react-toastify'; import * as Types from "../../../types/world/worldTypes"; import * as SimulationTypes from "../../..//types/simulation"; 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, setSimulationStates: (paths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => void ): 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: SimulationTypes.EventData[] = 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) < 50) { await new Promise(async (resolve) => { // Check Three.js Cache const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { // console.log(`[Cache] Fetching ${item.modelname}`); processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); return; } // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { // console.log(`[IndexedDB] Fetching ${item.modelname}`); 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, setSimulationStates); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }, undefined, (error) => { toast.error(`[IndexedDB] Error loading ${item.modelname}:`); URL.revokeObjectURL(blobUrl); resolve(); } ); return; } // Fetch from Backend // console.log(`[Backend] Fetching ${item.modelname}`); 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, setSimulationStates); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }, undefined, (error) => { toast.error(`[Backend] Error loading ${item.modelname}:`); resolve(); } ); }); } else { // console.log(`Item ${item.modelname} is not near`); setFloorItems((prevItems) => [ ...(prevItems || []), { modeluuid: item.modeluuid, modelname: item.modelname, position: item.position, rotation: item.rotation, modelfileID: item.modelfileID, isLocked: item.isLocked, isVisible: item.isVisible, }, ]); if (item.eventData) { processEventData(item, setSimulationStates); } modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); } } // Dispose loader after all models dracoLoader.dispose(); } } function processLoadedModel( gltf: any, item: SimulationTypes.EventData, itemsGroup: Types.RefGroup, setFloorItems: Types.setFloorItemSetState, setSimulationStates: (paths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => void ) { const model = gltf; model.uuid = item.modeluuid; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; 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); setFloorItems((prevItems) => [ ...(prevItems || []), { modeluuid: item.modeluuid, modelname: item.modelname, position: item.position, rotation: item.rotation, modelfileID: item.modelfileID, isLocked: item.isLocked, isVisible: item.isVisible, }, ]); if (item.eventData) { processEventData(item, setSimulationStates); } 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 processEventData(item: SimulationTypes.EventData, setSimulationStates: any) { if (item.eventData?.type === 'Conveyor') { const data: any = item.eventData; data.modeluuid = item.modeluuid; data.modelName = item.modelname; data.position = item.position; data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), data as SimulationTypes.ConveyorEventsSchema ]); } else if (item.eventData?.type === 'Vehicle') { const data: any = item.eventData; data.modeluuid = item.modeluuid; data.modelName = item.modelname; data.position = item.position; data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), data as SimulationTypes.VehicleEventsSchema ]); } else if (item.eventData?.type === 'StaticMachine') { const data: any = item.eventData; item.eventData.points.position = [0, 1.5, 1] data.modeluuid = item.modeluuid; data.modelName = item.modelname; data.position = item.position; data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), data as SimulationTypes.StaticMachineEventsSchema ]); } else if (item.eventData?.type === 'ArmBot') { const data: any = item.eventData; data.modeluuid = item.modeluuid; data.modelName = item.modelname; data.position = item.position; data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), data as SimulationTypes.ArmBotEventsSchema ]); } } function checkLoadingCompletion( modelsLoaded: number, modelsToLoad: number, dracoLoader: DRACOLoader, resolve: () => void ) { if (modelsLoaded === modelsToLoad) { toast.success("Models Loaded!"); dracoLoader.dispose(); } resolve(); } export default loadInitialFloorItems;