398 lines
24 KiB
TypeScript
398 lines
24 KiB
TypeScript
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 <Models loader={loader} />;
|
|
}
|
|
|
|
export default AssetsGroup;
|