2025-06-04 09:44:47 +00:00
|
|
|
import * as THREE from "three";
|
|
|
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
|
|
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 PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator";
|
|
|
|
|
|
|
|
async function addAssetModel(
|
|
|
|
raycaster: THREE.Raycaster,
|
|
|
|
camera: THREE.Camera,
|
|
|
|
pointer: THREE.Vector2,
|
|
|
|
floorGroup: Types.RefGroup,
|
|
|
|
socket: Socket<any>,
|
|
|
|
selectedItem: any,
|
|
|
|
setSelectedItem: any,
|
|
|
|
addEvent: (event: EventsSchema) => void,
|
|
|
|
addAsset: (asset: Asset) => void,
|
|
|
|
plane: Types.RefMesh,
|
|
|
|
projectId?: string,
|
|
|
|
userId?: string
|
|
|
|
): Promise<void> {
|
|
|
|
////////// 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 {
|
|
|
|
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(
|
2025-06-04 11:27:24 +00:00
|
|
|
intersectedFloor.point.x,
|
|
|
|
Math.round(intersectedFloor.point.y),
|
|
|
|
intersectedFloor.point.z
|
|
|
|
)
|
2025-06-04 09:44:47 +00:00
|
|
|
: new THREE.Vector3(
|
2025-06-04 11:27:24 +00:00
|
|
|
intersectedPlane.point.x,
|
|
|
|
0,
|
|
|
|
intersectedPlane.point.z
|
|
|
|
);
|
2025-06-04 09:44:47 +00:00
|
|
|
} 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
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const cachedModel = THREE.Cache.get(selectedItem.id);
|
|
|
|
if (cachedModel) {
|
|
|
|
handleModelLoad(
|
|
|
|
cachedModel,
|
|
|
|
intersectPoint!,
|
|
|
|
selectedItem,
|
|
|
|
addEvent,
|
|
|
|
addAsset,
|
|
|
|
socket,
|
|
|
|
projectId,
|
|
|
|
userId
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
|
|
|
|
if (cachedModelBlob) {
|
|
|
|
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,
|
|
|
|
addEvent,
|
|
|
|
addAsset,
|
2025-06-04 11:32:45 +00:00
|
|
|
socket,
|
|
|
|
projectId,
|
|
|
|
userId
|
2025-06-04 09:44:47 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
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,
|
|
|
|
addEvent,
|
|
|
|
addAsset,
|
2025-06-04 11:32:45 +00:00
|
|
|
socket,
|
|
|
|
projectId,
|
|
|
|
userId
|
2025-06-04 09:44:47 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
echo.error("Failed to add asset");
|
|
|
|
} finally {
|
|
|
|
setSelectedItem({});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function handleModelLoad(
|
|
|
|
gltf: any,
|
|
|
|
intersectPoint: THREE.Vector3,
|
|
|
|
selectedItem: any,
|
|
|
|
addEvent: (event: EventsSchema) => void,
|
|
|
|
addAsset: (asset: Asset) => void,
|
|
|
|
socket: Socket<any>,
|
|
|
|
projectId?: string,
|
|
|
|
userId?: string
|
|
|
|
) {
|
|
|
|
const model = gltf.scene.clone();
|
|
|
|
model.userData = {
|
|
|
|
name: selectedItem.name,
|
|
|
|
modelId: selectedItem.id,
|
|
|
|
modelUuid: model.uuid,
|
|
|
|
};
|
|
|
|
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
|
|
|
|
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
|
|
|
|
|
|
|
|
model.traverse((child: any) => {
|
|
|
|
if (child) {
|
|
|
|
child.castShadow = true;
|
|
|
|
child.receiveShadow = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const newFloorItem: Asset = {
|
|
|
|
modelUuid: model.uuid,
|
|
|
|
modelName: selectedItem.name,
|
|
|
|
assetId: selectedItem.id,
|
|
|
|
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
|
|
|
|
rotation: [0, 0, 0],
|
|
|
|
isLocked: false,
|
|
|
|
isVisible: true,
|
|
|
|
isCollidable: false,
|
|
|
|
opacity: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
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: 0, y: 0, z: 0 },
|
|
|
|
// 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,
|
|
|
|
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,
|
|
|
|
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: 1,
|
|
|
|
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,
|
|
|
|
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,
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
} else if (selectedItem.type === "Storage") {
|
|
|
|
const storageEvent: StorageEventSchema = {
|
|
|
|
modelUuid: newFloorItem.modelUuid,
|
|
|
|
modelName: newFloorItem.modelName,
|
|
|
|
position: newFloorItem.position,
|
|
|
|
rotation: newFloorItem.rotation,
|
|
|
|
state: "idle",
|
|
|
|
type: "storageUnit",
|
|
|
|
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: "store",
|
|
|
|
storageCapacity: 10,
|
|
|
|
triggers: [],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
addEvent(storageEvent);
|
|
|
|
eventData.point = {
|
|
|
|
uuid: storageEvent.point.uuid,
|
|
|
|
position: storageEvent.point.position,
|
|
|
|
rotation: storageEvent.point.rotation,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const completeData = {
|
|
|
|
organization,
|
|
|
|
modelUuid: newFloorItem.modelUuid,
|
|
|
|
modelName: newFloorItem.modelName,
|
|
|
|
modelfileID: newFloorItem.assetId,
|
|
|
|
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,
|
|
|
|
projectId: projectId,
|
|
|
|
userId: userId,
|
|
|
|
};
|
|
|
|
|
2025-06-04 11:32:45 +00:00
|
|
|
console.log("completeData: ", completeData);
|
2025-06-04 09:44:47 +00:00
|
|
|
socket.emit("v1:model-asset:add", completeData);
|
|
|
|
|
|
|
|
const asset: Asset = {
|
|
|
|
modelUuid: completeData.modelUuid,
|
|
|
|
modelName: completeData.modelName,
|
|
|
|
assetId: completeData.modelfileID,
|
|
|
|
position: completeData.position,
|
|
|
|
rotation: [
|
|
|
|
completeData.rotation.x,
|
|
|
|
completeData.rotation.y,
|
|
|
|
completeData.rotation.z,
|
|
|
|
] as [number, number, number],
|
|
|
|
isLocked: completeData.isLocked,
|
|
|
|
isCollidable: false,
|
|
|
|
isVisible: completeData.isVisible,
|
|
|
|
opacity: 1,
|
|
|
|
eventData: completeData.eventData,
|
|
|
|
};
|
|
|
|
|
|
|
|
addAsset(asset);
|
|
|
|
} else {
|
|
|
|
const data = {
|
|
|
|
organization,
|
|
|
|
modelUuid: newFloorItem.modelUuid,
|
|
|
|
modelName: newFloorItem.modelName,
|
|
|
|
modelfileID: newFloorItem.assetId,
|
|
|
|
position: newFloorItem.position,
|
|
|
|
rotation: {
|
|
|
|
x: model.rotation.x,
|
|
|
|
y: model.rotation.y,
|
|
|
|
z: model.rotation.z,
|
|
|
|
},
|
|
|
|
isLocked: false,
|
|
|
|
isVisible: true,
|
|
|
|
socketId: socket.id,
|
|
|
|
projectId: projectId,
|
|
|
|
userId: userId,
|
|
|
|
};
|
|
|
|
|
|
|
|
socket.emit("v1:model-asset:add", data);
|
|
|
|
|
|
|
|
const asset = {
|
|
|
|
modelUuid: data.modelUuid,
|
|
|
|
modelName: data.modelName,
|
|
|
|
assetId: data.modelfileID,
|
|
|
|
position: data.position,
|
|
|
|
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [
|
|
|
|
number,
|
|
|
|
number,
|
|
|
|
number
|
|
|
|
],
|
|
|
|
isLocked: data.isLocked,
|
|
|
|
isCollidable: false,
|
|
|
|
isVisible: data.isVisible,
|
|
|
|
opacity: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
addAsset(asset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default addAssetModel;
|