2025-05-15 04:04:55 +00:00
|
|
|
import * as THREE from "three"
|
|
|
|
import { useEffect } from 'react'
|
|
|
|
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
|
2025-05-26 12:33:27 +00:00
|
|
|
import { useLoadingProgress, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
2025-05-15 04:04:55 +00:00
|
|
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
|
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
2025-05-26 12:33:27 +00:00
|
|
|
import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes";
|
2025-05-15 04:04:55 +00:00
|
|
|
import { useAssetsStore } from "../../../store/builder/useAssetStore";
|
2025-05-26 08:59:37 +00:00
|
|
|
import { useEventsStore } from "../../../store/simulation/useEventsStore";
|
2025-05-15 04:04:55 +00:00
|
|
|
import Models from "./models/models";
|
2025-05-26 12:33:27 +00:00
|
|
|
import useModuleStore from "../../../store/useModuleStore";
|
|
|
|
import { useThree } from "@react-three/fiber";
|
|
|
|
import { CameraControls } from "@react-three/drei";
|
2025-05-27 03:46:33 +00:00
|
|
|
import addAssetModel from "./functions/addAssetModel";
|
2025-06-04 09:44:47 +00:00
|
|
|
import { useParams } from "react-router-dom";
|
2025-05-15 04:04:55 +00:00
|
|
|
|
|
|
|
const gltfLoaderWorker = new Worker(
|
|
|
|
new URL(
|
|
|
|
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
|
|
|
|
import.meta.url
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2025-05-29 06:30:16 +00:00
|
|
|
function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) {
|
2025-05-26 12:33:27 +00:00
|
|
|
const { activeModule } = useModuleStore();
|
|
|
|
const { socket } = useSocketStore();
|
|
|
|
const { controls, gl, pointer, camera, raycaster } = useThree();
|
2025-05-15 04:04:55 +00:00
|
|
|
const { setLoadingProgress } = useLoadingProgress();
|
2025-05-26 12:33:27 +00:00
|
|
|
const { setAssets, addAsset } = useAssetsStore();
|
2025-05-26 08:59:37 +00:00
|
|
|
const { addEvent } = useEventsStore();
|
2025-05-26 12:33:27 +00:00
|
|
|
const { setSelectedFloorItem } = useSelectedFloorItem();
|
|
|
|
const { selectedItem, setSelectedItem } = useSelectedItem();
|
2025-06-04 09:44:47 +00:00
|
|
|
const { projectId } = useParams();
|
2025-06-04 11:27:24 +00:00
|
|
|
const email = localStorage.getItem("email");
|
|
|
|
const organization = email!.split("@")[1].split(".")[0];
|
|
|
|
const userId = localStorage.getItem("userId")!;
|
2025-05-15 04:04:55 +00:00
|
|
|
|
|
|
|
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(() => {
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-06-04 09:44:47 +00:00
|
|
|
getFloorAssets(organization, projectId).then((data) => {
|
2025-05-15 04:04:55 +00:00
|
|
|
if (data.length > 0) {
|
|
|
|
const uniqueItems = (data as 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: uniqueItems });
|
|
|
|
} else {
|
|
|
|
gltfLoaderWorker.postMessage({ floorItems: [] });
|
|
|
|
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) {
|
|
|
|
const assets: Asset[] = [];
|
2025-06-04 09:44:47 +00:00
|
|
|
getFloorAssets(organization, projectId).then((data: FloorItems) => {
|
2025-05-15 04:04:55 +00:00
|
|
|
data.forEach((item) => {
|
|
|
|
if (item.eventData) {
|
|
|
|
assets.push({
|
|
|
|
modelUuid: item.modelUuid,
|
|
|
|
modelName: item.modelName,
|
|
|
|
assetId: item.modelfileID,
|
|
|
|
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
|
|
|
|
})
|
2025-05-26 08:59:37 +00:00
|
|
|
|
|
|
|
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",
|
|
|
|
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,
|
|
|
|
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",
|
|
|
|
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",
|
|
|
|
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: "material-id",
|
|
|
|
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",
|
|
|
|
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",
|
|
|
|
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: "store",
|
|
|
|
storageCapacity: 10,
|
|
|
|
triggers: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
addEvent(storageEvent);
|
|
|
|
}
|
2025-05-15 04:04:55 +00:00
|
|
|
} else {
|
|
|
|
assets.push({
|
|
|
|
modelUuid: item.modelUuid,
|
|
|
|
modelName: item.modelName,
|
|
|
|
assetId: item.modelfileID,
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
2025-05-26 12:33:27 +00:00
|
|
|
useEffect(() => {
|
|
|
|
const canvasElement = gl.domElement;
|
|
|
|
const onDrop = (event: any) => {
|
|
|
|
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;
|
|
|
|
|
2025-06-04 11:27:24 +00:00
|
|
|
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, projectId, userId);
|
2025-05-26 12:33:27 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const onDragOver = (event: any) => {
|
|
|
|
event.preventDefault();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (activeModule === "builder") {
|
|
|
|
canvasElement.addEventListener("drop", onDrop);
|
|
|
|
canvasElement.addEventListener("dragover", onDragOver);
|
|
|
|
} else {
|
|
|
|
if ((controls as CameraControls)) {
|
|
|
|
const target = (controls as CameraControls).getTarget(new THREE.Vector3());
|
|
|
|
(controls as CameraControls).setTarget(target.x, 0, target.z, true);
|
|
|
|
setSelectedFloorItem(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
canvasElement.removeEventListener("drop", onDrop);
|
|
|
|
canvasElement.removeEventListener("dragover", onDragOver);
|
|
|
|
};
|
|
|
|
}, [selectedItem, camera, pointer, activeModule, controls]);
|
|
|
|
|
2025-05-15 04:04:55 +00:00
|
|
|
return (
|
2025-05-29 08:13:36 +00:00
|
|
|
<Models />
|
2025-05-15 04:04:55 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default AssetsGroup;
|