import * as THREE from "three"; import { useEffect } from "react"; import { getFloorAssets } from "../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi"; import { useLoadingProgress, useRenameModeStore, useSelectedItem } from "../../../store/builder/store"; import { useSocketStore } from "../../../store/socket/useSocketStore"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { FloorItems, RefMesh } from "../../../types/world/worldTypes"; import Models from "./models/models"; import useModuleStore from "../../../store/ui/useModuleStore"; import { useThree } from "@react-three/fiber"; import { CameraControls } from "@react-three/drei"; import addAssetModel from "./functions/addAssetModel"; import { useParams } from "react-router-dom"; import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore"; import { getUserData } from "../../../functions/getUserData"; import { useSceneContext } from "../../scene/sceneContext"; import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import useAssetResponseHandler from "./responseHandler/useAssetResponseHandler"; const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url)); function AssetsGroup({ plane }: { readonly plane: RefMesh }) { const { activeModule } = useModuleStore(); const { builderSocket } = useSocketStore(); const { controls, gl, pointer, camera, raycaster, scene } = useThree(); const { setLoadingProgress } = useLoadingProgress(); const { assetStore, eventStore, versionStore } = useSceneContext(); const { selectedVersion } = versionStore(); const { setAssets, clearAssets } = assetStore(); const { addEvent, clearEvents } = eventStore(); const { addAssetToScene } = useAssetResponseHandler(); const { setSelectedFloorAsset } = useBuilderStore(); const { selectedItem, setSelectedItem } = useSelectedItem(); const { projectId } = useParams(); const { isRenameMode, setIsRenameMode } = useRenameModeStore(); const { userId } = getUserData(); const { setTop } = useTopData(); const { setLeft } = useLeftData(); 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); useEffect(() => { if (!projectId || !selectedVersion) return; clearEvents(); 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(projectId, selectedVersion?.versionId || "") .then((data) => { if (data && data.length > 0) { const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.assetId === item.assetId)); totalAssets = uniqueItems.length; if (totalAssets === 0) { updateLoadingProgress(100); return; } gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); } else { gltfLoaderWorker.postMessage({ floorItems: [] }); updateLoadingProgress(100); clearAssets(); } }) .catch((err) => { console.error(err); }); 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) { const assets: Asset[] = []; getFloorAssets(projectId, selectedVersion.versionId || "").then((data: FloorItems) => { data.forEach((item) => { if (item.eventData) { assets.push({ modelUuid: item.modelUuid, modelName: item.modelName, assetId: item.assetId, position: item.position, rotation: [item.rotation.x, item.rotation.y, item.rotation.z], isLocked: item.isLocked, isCollidable: false, isVisible: item.isVisible, opacity: 1, eventData: item.eventData, }); if (item.eventData.type === "Vehicle") { const vehicleEvent: VehicleEventSchema = { modelUuid: item.modelUuid, modelName: item.modelName, position: item.position, rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", type: "vehicle", subType: (item.eventData.subType as VehicleEventSchema["subType"]) || "", 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, paths: { initPickup: [], pickupDrop: [], dropPickup: [], }, 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", subType: item.eventData.subType || "", 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", subType: item.eventData.subType || "", 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: "Default Material", 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", subType: item.eventData.subType || "", 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", storageCapacity: 10, storageCount: 10, materialType: "Default material", subType: item.eventData.subType || "", 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: "store", triggers: [], }, ], }, }; addEvent(storageEvent); } else if (item.eventData.type === "Human") { const humanEvent: HumanEventSchema = { modelUuid: item.modelUuid, modelName: item.modelName, position: item.position, rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", type: "human", subType: item.eventData.subType || "", 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: "worker", loadCount: 1, assemblyCount: 1, assemblyCondition: { conditionType: "material", materialType: "Default material", }, manufactureCount: 1, loadCapacity: 1, processTime: 10, triggers: [], }, ], }, }; addEvent(humanEvent); } else if (item.eventData.type === "Crane") { const craneEvent: CraneEventSchema = { modelUuid: item.modelUuid, modelName: item.modelName, position: item.position, rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", type: "crane", subType: (item.eventData.subType as CraneEventSchema["subType"]) || "pillarJib", 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: "pickAndDrop", maxPickUpCount: 1, triggers: [], }, ], }, }; addEvent(craneEvent); } } else { assets.push({ modelUuid: item.modelUuid, modelName: item.modelName, assetId: item.assetId, position: item.position, rotation: [item.rotation.x, item.rotation.y, item.rotation.z], isLocked: item.isLocked, isCollidable: false, isVisible: item.isVisible, opacity: 1, }); } }); setAssets(assets); }); updateLoadingProgress(100); } }); } }; }, [selectedVersion?.versionId]); useEffect(() => { const canvasElement = gl.domElement; const onDrop = (event: DragEvent) => { 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(scene, raycaster, camera, pointer, builderSocket, selectedItem, setSelectedItem, addEvent, addAssetToScene, plane, loader, selectedVersion, projectId, userId); } }; const onDragOver = (event: any) => { event.preventDefault(); }; const onMouseMove = (evt: any) => { if (!canvasElement) return; const canvasRect = canvasElement.getBoundingClientRect(); const relativeX = evt.clientX - canvasRect.left; const relativeY = evt.clientY - canvasRect.top; if (!isRenameMode) { setTop(relativeY); setLeft(relativeX); } }; const onMouseUp = (evt: any) => { setIsRenameMode(false); }; if (activeModule === "builder") { canvasElement.addEventListener("drop", onDrop); canvasElement.addEventListener("dragover", onDragOver); canvasElement.addEventListener("mousemove", onMouseMove); canvasElement.addEventListener("mouseup", onMouseUp); } else if (controls as CameraControls) { const target = (controls as CameraControls).getTarget(new THREE.Vector3()); (controls as CameraControls).setTarget(target.x, 0, target.z, true); setSelectedFloorAsset(null); } return () => { canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("dragover", onDragOver); canvasElement.removeEventListener("mousemove", onMouseMove); canvasElement.removeEventListener("mouseup", onMouseUp); }; }, [selectedItem, camera, activeModule, controls, isRenameMode]); return ; } export default AssetsGroup;