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( raycaster: THREE.Raycaster, 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, plane: Types.RefMesh, ): Promise { ////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage ////////// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; try { isTempLoader.current = true; 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); raycaster.setFromCamera(pointer, camera); const floorIntersections = raycaster.intersectObjects(floorGroup.current.children, true); const intersectedFloor = floorIntersections.find(intersect => intersect.object.name.includes("Floor")); const planeIntersections = raycaster.intersectObject(plane.current!, true); const intersectedPlane = planeIntersections[0]; let intersectPoint: THREE.Vector3 | null = null; if (intersectedFloor && intersectedPlane) { intersectPoint = intersectedFloor.distance < intersectedPlane.distance ? (new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)) : (new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z)); } else if (intersectedFloor) { intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z); } else if (intersectedPlane) { intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z); } if (intersectPoint) { 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); 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); }); } 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); }); } } } } catch (error) { echo.error("Failed to add asset"); console.error('Error fetching asset model:', error); } finally { setSelectedItem({}); } } 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, socket: Socket ) { const model = gltf.scene.clone(); model.userData = { name: selectedItem.name, modelId: selectedItem.id, modelUuid: model.uuid }; model.position.set(intersectPoint!.x, 3 + intersectPoint!.y, intersectPoint!.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.traverse((child: any) => { if (child) { child.castShadow = true; child.receiveShadow = true; } }); 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 = { modelUuid: model.uuid, modelName: selectedItem.name, modelfileID: selectedItem.id, position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z], rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isLocked: false, isVisible: true }; const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : ""; // API // await setFloorItemApi( // organization, // newFloorItem.modelUuid, // newFloorItem.modelName, // newFloorItem.modelfileID, // newFloorItem.position, // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, // false, // true, // ); // SOCKET if (selectedItem.type) { const data = PointsCalculator( selectedItem.type, gltf.scene.clone(), new THREE.Vector3(...model.rotation) ); if (!data || !data.points) return; const eventData: any = { type: selectedItem.type, }; if (selectedItem.type === "Conveyor") { const ConveyorEvent: ConveyorEventSchema = { modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], state: "idle", type: 'transfer', speed: 1, points: data.points.map((point: THREE.Vector3, index: number) => { const triggers: TriggerSchema[] = []; if (data.points && index < data.points.length - 1) { triggers.push({ triggerUuid: THREE.MathUtils.generateUUID(), triggerName: `Trigger 1`, triggerType: "onComplete", delay: 0, triggeredAsset: { triggeredModel: { modelName: newFloorItem.modelName, modelUuid: newFloorItem.modelUuid }, triggeredPoint: { pointName: `Point`, pointUuid: "" }, triggeredAction: { actionName: `Action 1`, actionUuid: "" } } }); } return { uuid: THREE.MathUtils.generateUUID(), position: [point.x, point.y, point.z], rotation: [0, 0, 0], action: { actionUuid: THREE.MathUtils.generateUUID(), actionName: `Action 1`, actionType: 'default', material: 'Default Material', delay: 0, spawnInterval: 5, spawnCount: 1, triggers: triggers } }; }) }; for (let i = 0; i < ConveyorEvent.points.length - 1; i++) { const currentPoint = ConveyorEvent.points[i]; const nextPoint = ConveyorEvent.points[i + 1]; if (currentPoint.action.triggers.length > 0) { currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid; currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid; } } addEvent(ConveyorEvent); eventData.points = ConveyorEvent.points.map(point => ({ uuid: point.uuid, position: point.position, rotation: point.rotation })); } else if (selectedItem.type === "Vehicle") { const vehicleEvent: VehicleEventSchema = { modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.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: "Action 1", actionType: "travel", unLoadDuration: 5, loadCapacity: 10, steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] } } }; addEvent(vehicleEvent); eventData.point = { uuid: vehicleEvent.point.uuid, position: vehicleEvent.point.position, rotation: vehicleEvent.point.rotation }; } else if (selectedItem.type === "ArmBot") { const roboticArmEvent: RoboticArmEventSchema = { modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.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: "Action 1", actionType: "pickAndPlace", process: { startPoint: null, endPoint: null }, triggers: [] } ] } }; addEvent(roboticArmEvent); eventData.point = { uuid: roboticArmEvent.point.uuid, position: roboticArmEvent.point.position, rotation: roboticArmEvent.point.rotation }; } else if (selectedItem.type === "StaticMachine") { const machineEvent: MachineEventSchema = { modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, position: newFloorItem.position, rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.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: "Action 1", actionType: "process", processTime: 10, swapMaterial: "Default Material", triggers: [] } } }; addEvent(machineEvent); eventData.point = { uuid: machineEvent.point.uuid, position: machineEvent.point.position, rotation: machineEvent.point.rotation }; } const completeData = { organization, modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, modelfileID: newFloorItem.modelfileID, position: newFloorItem.position, rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isLocked: false, isVisible: true, socketId: socket.id, 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: () => { toast.success("Model Added!"); } }); } else { const data = { organization, modelUuid: newFloorItem.modelUuid, modelName: newFloorItem.modelName, modelfileID: newFloorItem.modelfileID, position: newFloorItem.position, rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isLocked: false, isVisible: true, 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: () => { toast.success("Model Added!"); } }); } } export default addAssetModel;