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 { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator'; async function loadInitialFloorItems( itemsGroup: Types.RefGroup, setFloorItems: Types.setFloorItemSetState, addEvent: (event: EventsSchema) => 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: 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) < 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, addEvent); 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, addEvent); 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, addEvent); 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, }, ]); 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 }; 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.modelfileID === "a1ee92554935007b10b3eb05") { const data = PointsCalculator( 'Vehicle', gltf.clone(), new THREE.Vector3(...model.rotation) ); if (!data || !data.points) return; 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: THREE.MathUtils.generateUUID(), position: [data.points[0].x, data.points[0].y, data.points[0].z], rotation: [0, 0, 0], action: { actionUuid: THREE.MathUtils.generateUUID(), actionName: "Vehicle Action", actionType: "travel", material: null, unLoadDuration: 5, loadCapacity: 10, pickUpPoint: null, unLoadPoint: null, triggers: [] } } }; addEvent(vehicleEvent); } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { const data = PointsCalculator( 'Conveyor', gltf.clone(), new THREE.Vector3(...model.rotation) ); if (!data || !data.points) return; 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: data.points.map((point: THREE.Vector3, index: number) => ({ uuid: THREE.MathUtils.generateUUID(), position: [point.x, point.y, point.z], rotation: [0, 0, 0], action: { actionUuid: THREE.MathUtils.generateUUID(), actionName: `Action ${index}`, actionType: 'default', material: 'inherit', delay: 0, spawnInterval: 5, spawnCount: 1, triggers: [] } })) }; addEvent(ConveyorEvent); } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { // const data = PointsCalculator( // 'Conveyor', // gltf.clone(), // new THREE.Vector3(...model.rotation) // ); // if (!data || !data.points) return; // const points: ConveyorPointSchema[] = data.points.map((point: THREE.Vector3, index: number) => { // const actionUuid = THREE.MathUtils.generateUUID(); // return { // uuid: THREE.MathUtils.generateUUID(), // position: [point.x, point.y, point.z], // rotation: [0, 0, 0], // action: { // actionUuid, // actionName: `Action ${index}`, // actionType: 'default', // material: 'inherit', // delay: 0, // spawnInterval: 5, // spawnCount: 1, // triggers: [] // } // }; // }); // points.forEach((point, index) => { // if (index < points.length - 1) { // const nextPoint = points[index + 1]; // point.action.triggers.push({ // triggerUuid: THREE.MathUtils.generateUUID(), // triggerName: `Trigger 1`, // triggerType: "onComplete", // delay: 0, // triggeredAsset: { // triggeredModel: { // modelName: item.modelname, // modelUuid: item.modeluuid // }, // triggeredPoint: { // pointName: `Point ${index + 1}`, // pointUuid: nextPoint.uuid // }, // triggeredAction: { // actionName: nextPoint.action.actionName, // actionUuid: nextPoint.action.actionUuid // } // } // }); // } // }); // 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 // }; // addEvent(ConveyorEvent); } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { const data = PointsCalculator( 'Conveyor', gltf.clone(), new THREE.Vector3(...model.rotation) ); if (!data || !data.points) return; 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: data.points.map((point: THREE.Vector3, index: number) => ({ uuid: THREE.MathUtils.generateUUID(), position: [point.x, point.y, point.z], rotation: [0, 0, 0], action: { actionUuid: THREE.MathUtils.generateUUID(), actionName: `Action ${index}`, actionType: 'default', material: 'inherit', delay: 0, spawnInterval: 5, spawnCount: 1, triggers: [] } })) }; addEvent(ConveyorEvent); } else if (item.modelfileID === "29dee78715ad5b853f5c346d") { const data = PointsCalculator( 'StaticMachine', gltf.clone(), new THREE.Vector3(...model.rotation) ); if (!data || !data.points) return; 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: THREE.MathUtils.generateUUID(), position: [data.points[0].x, data.points[0].y, data.points[0].z], rotation: [0, 0, 0], action: { actionUuid: THREE.MathUtils.generateUUID(), actionName: "Process Action", actionType: "process", processTime: 10, swapMaterial: "material-id", triggers: [] } } }; addEvent(machineEvent); } else if (item.modelfileID === "52e6681fbb743a890d96c914") { const data = PointsCalculator( 'ArmBot', gltf.clone(), new THREE.Vector3(...model.rotation) ); if (!data || !data.points) return; 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: THREE.MathUtils.generateUUID(), position: [data.points[0].x, data.points[0].y, data.points[0].z], rotation: [0, 0, 0], actions: [ { actionUuid: THREE.MathUtils.generateUUID(), actionName: "Pick and Place", actionType: "pickAndPlace", process: { startPoint: [0, 0, 0], endPoint: [0, 0, 0] }, triggers: [] } ] } }; addEvent(roboticArmEvent); } 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) { toast.success("Models Loaded!"); dracoLoader.dispose(); } resolve(); } export default loadInitialFloorItems;