328 lines
14 KiB
TypeScript
328 lines
14 KiB
TypeScript
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
|
import gsap from 'gsap';
|
|
import * as THREE from 'three';
|
|
import * as CONSTANTS from '../../../types/world/worldConstants';
|
|
import { toast } from 'react-toastify';
|
|
import * as Types from "../../../types/world/worldTypes";
|
|
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
|
|
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
|
|
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
|
|
import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator';
|
|
|
|
async function loadInitialFloorItems(
|
|
itemsGroup: Types.RefGroup,
|
|
setFloorItems: Types.setFloorItemSetState,
|
|
addEvent: (event: EventsSchema) => void,
|
|
renderDistance: number
|
|
): Promise<void> {
|
|
if (!itemsGroup.current) return;
|
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
|
const email = localStorage.getItem('email');
|
|
const organization = (email!.split("@")[1]).split(".")[0];
|
|
|
|
const items = await getFloorAssets(organization);
|
|
localStorage.setItem("FloorItems", JSON.stringify(items));
|
|
await initializeDB();
|
|
|
|
if (items.message === "floorItems not found") return;
|
|
|
|
if (items) {
|
|
const storedFloorItems: Types.FloorItems = items;
|
|
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);
|
|
|
|
let modelsLoaded = 0;
|
|
const modelsToLoad = storedFloorItems.length;
|
|
|
|
const camData = await getCamera(organization, localStorage.getItem('userId')!);
|
|
let storedPosition;
|
|
if (camData && camData.position) {
|
|
storedPosition = camData?.position;
|
|
} else {
|
|
storedPosition = new THREE.Vector3(0, 40, 30);
|
|
}
|
|
if (!storedPosition) return;
|
|
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
|
|
|
|
storedFloorItems.sort((a, b) => {
|
|
const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]);
|
|
const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]);
|
|
return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition);
|
|
});
|
|
|
|
for (const item of storedFloorItems) {
|
|
if (!item.modelfileID) return;
|
|
const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]);
|
|
let storedPosition;
|
|
if (localStorage.getItem("cameraPosition")) {
|
|
storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!);
|
|
} else {
|
|
storedPosition = new THREE.Vector3(0, 40, 30);
|
|
}
|
|
|
|
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
|
|
|
|
if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
|
|
await new Promise<void>(async (resolve) => {
|
|
|
|
// Check Three.js Cache
|
|
const cachedModel = THREE.Cache.get(item.modelfileID!);
|
|
if (cachedModel) {
|
|
// console.log(`[Cache] Fetching ${item.modelName}`);
|
|
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
|
|
modelsLoaded++;
|
|
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
|
return;
|
|
}
|
|
|
|
// Check IndexedDB
|
|
const indexedDBModel = await retrieveGLTF(item.modelfileID!);
|
|
if (indexedDBModel) {
|
|
// console.log(`[IndexedDB] Fetching ${item.modelName}`);
|
|
const blobUrl = URL.createObjectURL(indexedDBModel);
|
|
loader.load(blobUrl, (gltf) => {
|
|
URL.revokeObjectURL(blobUrl);
|
|
THREE.Cache.remove(blobUrl);
|
|
THREE.Cache.add(item.modelfileID!, gltf);
|
|
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
|
|
modelsLoaded++;
|
|
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
|
},
|
|
undefined,
|
|
(error) => {
|
|
toast.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
|
URL.revokeObjectURL(blobUrl);
|
|
resolve();
|
|
}
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Fetch from Backend
|
|
// console.log(`[Backend] Fetching ${item.modelName}`);
|
|
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
|
|
loader.load(modelUrl, async (gltf) => {
|
|
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
|
await storeGLTF(item.modelfileID!, modelBlob);
|
|
THREE.Cache.add(item.modelfileID!, gltf);
|
|
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
|
|
modelsLoaded++;
|
|
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
|
},
|
|
undefined,
|
|
(error) => {
|
|
toast.error(`[Backend] Error loading ${item.modelName}:`);
|
|
resolve();
|
|
}
|
|
);
|
|
});
|
|
} else {
|
|
// console.log(`Item ${item.modelName} is not near`);
|
|
setFloorItems((prevItems) => [
|
|
...(prevItems || []),
|
|
{
|
|
modelUuid: item.modelUuid,
|
|
modelName: item.modelName,
|
|
position: item.position,
|
|
rotation: item.rotation,
|
|
modelfileID: item.modelfileID,
|
|
isLocked: item.isLocked,
|
|
isVisible: item.isVisible,
|
|
},
|
|
]);
|
|
|
|
modelsLoaded++;
|
|
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { });
|
|
}
|
|
}
|
|
|
|
// Dispose loader after all models
|
|
dracoLoader.dispose();
|
|
}
|
|
}
|
|
|
|
|
|
function processLoadedModel(
|
|
gltf: any,
|
|
item: Types.FloorItemType,
|
|
itemsGroup: Types.RefGroup,
|
|
setFloorItems: Types.setFloorItemSetState,
|
|
addEvent: (event: EventsSchema) => void,
|
|
) {
|
|
const model = gltf.clone();
|
|
model.uuid = item.modelUuid;
|
|
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
|
|
model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData };
|
|
model.position.set(...item.position);
|
|
model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z);
|
|
|
|
model.traverse((child: any) => {
|
|
if (child.isMesh) {
|
|
// Clone the material to ensure changes are independent
|
|
// child.material = child.material.clone();
|
|
|
|
child.castShadow = true;
|
|
child.receiveShadow = true;
|
|
}
|
|
});
|
|
|
|
itemsGroup?.current?.add(model);
|
|
|
|
if (item.eventData) {
|
|
setFloorItems((prevItems) => [
|
|
...(prevItems || []),
|
|
{
|
|
modelUuid: item.modelUuid,
|
|
modelName: item.modelName,
|
|
position: item.position,
|
|
rotation: item.rotation,
|
|
modelfileID: item.modelfileID,
|
|
isLocked: item.isLocked,
|
|
isVisible: item.isVisible,
|
|
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",
|
|
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: 10,
|
|
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 ${index + 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: [0, 0, 0],
|
|
endPoint: [0, 0, 0]
|
|
},
|
|
triggers: []
|
|
}
|
|
]
|
|
}
|
|
};
|
|
addEvent(roboticArmEvent);
|
|
|
|
}
|
|
} else {
|
|
setFloorItems((prevItems) => [
|
|
...(prevItems || []),
|
|
{
|
|
modelUuid: item.modelUuid,
|
|
modelName: item.modelName,
|
|
position: item.position,
|
|
rotation: item.rotation,
|
|
modelfileID: item.modelfileID,
|
|
isLocked: item.isLocked,
|
|
isVisible: item.isVisible,
|
|
},
|
|
]);
|
|
}
|
|
|
|
gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' });
|
|
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' });
|
|
}
|
|
|
|
function checkLoadingCompletion(
|
|
modelsLoaded: number,
|
|
modelsToLoad: number,
|
|
dracoLoader: DRACOLoader,
|
|
resolve: () => void
|
|
) {
|
|
if (modelsLoaded === modelsToLoad) {
|
|
toast.success("Models Loaded!");
|
|
dracoLoader.dispose();
|
|
}
|
|
resolve();
|
|
}
|
|
|
|
export default loadInitialFloorItems; |