import { useFrame, useThree } from "@react-three/fiber"; import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; import assetVisibility from "../geomentries/assets/assetVisibility"; 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 { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; 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 { transformMode } = useTransformMode(); 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]; 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: data }); } else { gltfLoaderWorker.postMessage({ floorItems: [] }); loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, 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, 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 = () => { if (!intervalId) { 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); } const Mode = transformMode; if (Mode !== null || 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; const Mode = transformMode; if (Mode !== null || 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]) { 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, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); useFrame(() => { if (controls) // assetVisibility(itemsGroup, state.camera.position, renderDistance); if (deleteTool && activeModule === "builder") { DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); } else if (!deleteTool) { if (hoveredDeletableFloorItem.current) { hoveredDeletableFloorItem.current = undefined; setDeletableFloorItem(null); } } }); return ; }; export default FloorItemsGroup;