Refactor asset management and selection controls
- Removed the FloorItemsGroup component to streamline asset handling. - Updated socket response handling by removing TempLoader integration. - Enhanced CopyPasteControls and DuplicationControls to utilize the new asset management structure. - Integrated asset addition and updates in move, rotate, and selection controls. - Improved performance by skipping items without valid modelfileID in the GLTF loader worker. - Cleaned up unused variables and imports across various control components. - Added logging for selectedFloorItem and deletableFloorItem for debugging purposes.
This commit is contained in:
parent
1258d11ee8
commit
b086ee8f75
|
@ -1,343 +0,0 @@
|
|||
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 * 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';
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) => {
|
||||
echo.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch from Backend
|
||||
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) => {
|
||||
echo.error(`[Backend] Error loading ${item.modelName}:`);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
} 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,
|
||||
},
|
||||
]);
|
||||
|
||||
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: 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);
|
||||
}
|
||||
} 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) {
|
||||
echo.success("Models Loaded!");
|
||||
dracoLoader.dispose();
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
|
||||
export default loadInitialFloorItems;
|
|
@ -1,13 +1,17 @@
|
|||
import * as THREE from "three"
|
||||
import { useEffect } from 'react'
|
||||
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
|
||||
import { useLoadingProgress } from '../../../store/builder/store';
|
||||
import { useLoadingProgress, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import { FloorItems } from "../../../types/world/worldTypes";
|
||||
import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes";
|
||||
import { useAssetsStore } from "../../../store/builder/useAssetStore";
|
||||
import { useEventsStore } from "../../../store/simulation/useEventsStore";
|
||||
import Models from "./models/models";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { CameraControls } from "@react-three/drei";
|
||||
import addAssetModel from "../geomentries/assets/addAssetModel";
|
||||
|
||||
const gltfLoaderWorker = new Worker(
|
||||
new URL(
|
||||
|
@ -16,10 +20,15 @@ const gltfLoaderWorker = new Worker(
|
|||
)
|
||||
);
|
||||
|
||||
function AssetsGroup() {
|
||||
function AssetsGroup({ floorGroup, plane }: { floorGroup: RefGroup, plane: RefMesh }) {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { controls, gl, pointer, camera, raycaster } = useThree();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { setAssets } = useAssetsStore();
|
||||
const { setAssets, addAsset } = useAssetsStore();
|
||||
const { addEvent } = useEventsStore();
|
||||
const { setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
@ -243,6 +252,43 @@ function AssetsGroup() {
|
|||
};
|
||||
}, []);
|
||||
|
||||
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;
|
||||
|
||||
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane);
|
||||
}
|
||||
};
|
||||
|
||||
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]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Models />
|
||||
|
|
|
@ -10,7 +10,7 @@ export const AssetBoundingBox = ({ asset, boundingBox }: { asset: Asset, boundin
|
|||
const edges = new EdgesGeometry(boxGeometry);
|
||||
|
||||
return (
|
||||
<group name='Asset FallBack' userData={asset}>
|
||||
<group name='Asset FallBack'>
|
||||
<lineSegments position={center}>
|
||||
<bufferGeometry attach="geometry" {...edges} />
|
||||
<lineBasicMaterial attach="material" color="gray" linewidth={1} />
|
||||
|
|
|
@ -7,12 +7,18 @@ import { useFrame, useThree } from '@react-three/fiber';
|
|||
import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem } from '../../../../../store/builder/store';
|
||||
import { AssetBoundingBox } from './assetBoundingBox';
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
import { useAssetsStore } from '../../../../../store/builder/useAssetStore';
|
||||
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../../../store/simulation/useProductStore";
|
||||
import { useSocketStore } from '../../../../../store/builder/store';
|
||||
|
||||
function Model({ asset }: { asset: Asset }) {
|
||||
const { camera, controls } = useThree();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { removeAsset } = useAssetsStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const [isRendered, setIsRendered] = useState(false);
|
||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
@ -93,7 +99,7 @@ function Model({ asset }: { asset: Asset }) {
|
|||
}
|
||||
})
|
||||
|
||||
const handleAssetDouble = (asset: Asset) => {
|
||||
const handleDblClick = (asset: Asset) => {
|
||||
if (asset) {
|
||||
if (activeTool === "cursor" && boundingBox && groupRef.current) {
|
||||
const size = boundingBox.getSize(new THREE.Vector3());
|
||||
|
@ -126,6 +132,60 @@ function Model({ asset }: { asset: Asset }) {
|
|||
}
|
||||
};
|
||||
|
||||
const handleClick = (asset: Asset) => {
|
||||
if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
||||
|
||||
//REST
|
||||
|
||||
// const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
modelName: asset.modelName,
|
||||
socketId: socket.id
|
||||
}
|
||||
|
||||
const response = socket.emit('v2:model-asset:delete', data)
|
||||
|
||||
useEventsStore.getState().removeEvent(asset.modelUuid);
|
||||
useProductStore.getState().deleteEvent(asset.modelUuid);
|
||||
|
||||
if (response) {
|
||||
|
||||
const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]');
|
||||
const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== asset.modelUuid);
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems));
|
||||
|
||||
removeAsset(asset.modelUuid);
|
||||
|
||||
echo.success("Model Removed!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const handlePointerOver = (asset: Asset) => {
|
||||
if (activeTool === "delete") {
|
||||
if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
return;
|
||||
} else {
|
||||
setDeletableFloorItem(groupRef.current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handlePointerOut = (asset: Asset) => {
|
||||
if (activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Asset Model'
|
||||
|
@ -135,9 +195,21 @@ function Model({ asset }: { asset: Asset }) {
|
|||
rotation={asset.rotation}
|
||||
visible={asset.isVisible}
|
||||
userData={asset}
|
||||
onDoubleClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDblClick(asset);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleAssetDouble(asset);
|
||||
handleClick(asset);
|
||||
}}
|
||||
onPointerOver={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePointerOver(asset);
|
||||
}}
|
||||
onPointerOut={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePointerOut(asset);
|
||||
}}
|
||||
>
|
||||
{gltfScene && (
|
||||
|
|
|
@ -33,7 +33,6 @@ import loadWalls from "./geomentries/walls/loadWalls";
|
|||
import * as Types from "../../types/world/worldTypes";
|
||||
|
||||
import SocketResponses from "../collaboration/socket/socketResponses.dev";
|
||||
import FloorItemsGroup from "./groups/floorItemsGroup";
|
||||
import FloorPlanGroup from "./groups/floorPlanGroup";
|
||||
import FloorGroup from "./groups/floorGroup";
|
||||
import FloorGroupAilse from "./groups/floorGroupAisle";
|
||||
|
@ -87,7 +86,6 @@ export default function Builder() {
|
|||
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
|
||||
const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf).
|
||||
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
|
||||
const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation.
|
||||
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
|
||||
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
|
||||
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
|
||||
|
@ -96,7 +94,6 @@ export default function Builder() {
|
|||
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
|
||||
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
|
||||
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
|
||||
const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted.
|
||||
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
|
||||
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
|
||||
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
|
||||
|
@ -243,15 +240,6 @@ export default function Builder() {
|
|||
/>
|
||||
|
||||
<Bvh firstHitOnly>
|
||||
{/* <FloorItemsGroup
|
||||
itemsGroup={itemsGroup}
|
||||
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
|
||||
AttachedObject={AttachedObject}
|
||||
floorGroup={floorGroup}
|
||||
tempLoader={tempLoader}
|
||||
isTempLoader={isTempLoader}
|
||||
plane={plane}
|
||||
/> */}
|
||||
|
||||
<FloorGroup
|
||||
floorGroup={floorGroup}
|
||||
|
@ -306,7 +294,10 @@ export default function Builder() {
|
|||
anglesnappedPoint={anglesnappedPoint}
|
||||
/>
|
||||
|
||||
<AssetsGroup />
|
||||
<AssetsGroup
|
||||
floorGroup={floorGroup}
|
||||
plane={plane}
|
||||
/>
|
||||
|
||||
<MeasurementTool />
|
||||
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import gsap from 'gsap';
|
||||
import { toast } from 'react-toastify';
|
||||
import TempLoader from './tempLoader';
|
||||
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 { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
|
||||
import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator';
|
||||
|
||||
async function addAssetModel(
|
||||
|
@ -17,14 +13,11 @@ async function addAssetModel(
|
|||
camera: THREE.Camera,
|
||||
pointer: THREE.Vector2,
|
||||
floorGroup: Types.RefGroup,
|
||||
setFloorItems: Types.setFloorItemSetState,
|
||||
itemsGroup: Types.RefGroup,
|
||||
isTempLoader: Types.RefBoolean,
|
||||
tempLoader: Types.RefMesh,
|
||||
socket: Socket<any>,
|
||||
selectedItem: any,
|
||||
setSelectedItem: any,
|
||||
addEvent: (event: EventsSchema) => void,
|
||||
addAsset: (asset: Asset) => void,
|
||||
plane: Types.RefMesh,
|
||||
): Promise<void> {
|
||||
|
||||
|
@ -33,7 +26,6 @@ async function addAssetModel(
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
||||
try {
|
||||
isTempLoader.current = true;
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
||||
|
@ -61,46 +53,32 @@ async function addAssetModel(
|
|||
if (intersectPoint.y < 0) {
|
||||
intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z);
|
||||
}
|
||||
// console.log('selectedItem: ', selectedItem);
|
||||
const cachedModel = THREE.Cache.get(selectedItem.id);
|
||||
if (cachedModel) {
|
||||
// console.log(`[Cache] Fetching ${selectedItem.name}`);
|
||||
handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket);
|
||||
handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket);
|
||||
return;
|
||||
} else {
|
||||
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
|
||||
|
||||
if (cachedModelBlob) {
|
||||
// console.log(`Added ${selectedItem.name} from indexDB`);
|
||||
|
||||
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, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket);
|
||||
},
|
||||
() => {
|
||||
TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup);
|
||||
});
|
||||
handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket);
|
||||
});
|
||||
} else {
|
||||
// console.log(`Added ${selectedItem.name} from Backend`);
|
||||
|
||||
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, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket);
|
||||
},
|
||||
() => {
|
||||
TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup);
|
||||
});
|
||||
await handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
echo.error("Failed to add asset");
|
||||
console.error('Error fetching asset model:', error);
|
||||
} finally {
|
||||
setSelectedItem({});
|
||||
}
|
||||
|
@ -110,11 +88,8 @@ async function handleModelLoad(
|
|||
gltf: any,
|
||||
intersectPoint: THREE.Vector3,
|
||||
selectedItem: any,
|
||||
itemsGroup: Types.RefGroup,
|
||||
tempLoader: Types.RefMesh,
|
||||
isTempLoader: Types.RefBoolean,
|
||||
setFloorItems: Types.setFloorItemSetState,
|
||||
addEvent: (event: EventsSchema) => void,
|
||||
addAsset: (asset: Asset) => void,
|
||||
socket: Socket<any>
|
||||
) {
|
||||
const model = gltf.scene.clone();
|
||||
|
@ -129,22 +104,16 @@ async function handleModelLoad(
|
|||
}
|
||||
});
|
||||
|
||||
itemsGroup.current.add(model);
|
||||
if (tempLoader.current) {
|
||||
(<any>tempLoader.current.material).dispose();
|
||||
(<any>tempLoader.current.geometry).dispose();
|
||||
itemsGroup.current.remove(tempLoader.current);
|
||||
tempLoader.current = undefined;
|
||||
}
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
const newFloorItem: Asset = {
|
||||
modelUuid: model.uuid,
|
||||
modelName: selectedItem.name,
|
||||
modelfileID: selectedItem.id,
|
||||
assetId: selectedItem.id,
|
||||
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
rotation: [0, 0, 0],
|
||||
isLocked: false,
|
||||
isVisible: true
|
||||
isVisible: true,
|
||||
isCollidable: false,
|
||||
opacity: 1,
|
||||
};
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
|
@ -158,7 +127,7 @@ async function handleModelLoad(
|
|||
// newFloorItem.modelName,
|
||||
// newFloorItem.modelfileID,
|
||||
// newFloorItem.position,
|
||||
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
// { x: 0, y: 0, z: 0 },
|
||||
// false,
|
||||
// true,
|
||||
// );
|
||||
|
@ -183,7 +152,7 @@ async function handleModelLoad(
|
|||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
position: newFloorItem.position,
|
||||
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: 'transfer',
|
||||
speed: 1,
|
||||
|
@ -252,7 +221,7 @@ async function handleModelLoad(
|
|||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
position: newFloorItem.position,
|
||||
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: "vehicle",
|
||||
speed: 1,
|
||||
|
@ -285,7 +254,7 @@ async function handleModelLoad(
|
|||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
position: newFloorItem.position,
|
||||
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: "roboticArm",
|
||||
speed: 1,
|
||||
|
@ -319,7 +288,7 @@ async function handleModelLoad(
|
|||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
position: newFloorItem.position,
|
||||
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: "machine",
|
||||
point: {
|
||||
|
@ -347,7 +316,7 @@ async function handleModelLoad(
|
|||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
position: newFloorItem.position,
|
||||
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: "storageUnit",
|
||||
point: {
|
||||
|
@ -375,7 +344,7 @@ async function handleModelLoad(
|
|||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
modelfileID: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
isLocked: false,
|
||||
|
@ -384,27 +353,30 @@ async function handleModelLoad(
|
|||
eventData: eventData
|
||||
};
|
||||
|
||||
model.userData.eventData = eventData;
|
||||
|
||||
newFloorItem.eventData = eventData;
|
||||
|
||||
setFloorItems((prevItems) => {
|
||||
const updatedItems = [...(prevItems || []), newFloorItem];
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
|
||||
return updatedItems;
|
||||
});
|
||||
|
||||
socket.emit("v2:model-asset:add", completeData);
|
||||
|
||||
gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" });
|
||||
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } });
|
||||
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.modelfileID,
|
||||
modelfileID: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
isLocked: false,
|
||||
|
@ -412,16 +384,22 @@ async function handleModelLoad(
|
|||
socketId: socket.id
|
||||
};
|
||||
|
||||
setFloorItems((prevItems) => {
|
||||
const updatedItems = [...(prevItems || []), newFloorItem];
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
|
||||
return updatedItems;
|
||||
});
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" });
|
||||
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } });
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
import * as THREE from "three";
|
||||
import gsap from "gsap";
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { initializeDB, retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
let currentTaskId = 0; // Track the active task
|
||||
let activePromises = new Map<number, boolean>(); // Map to track task progress
|
||||
|
||||
export default async function assetManager(
|
||||
data: any,
|
||||
itemsGroup: Types.RefGroup,
|
||||
loader: GLTFLoader,
|
||||
) {
|
||||
const taskId = ++currentTaskId; // Increment taskId for each call
|
||||
activePromises.set(taskId, true); // Mark task as active
|
||||
|
||||
//
|
||||
|
||||
if (data.toRemove.length > 0) {
|
||||
data.toRemove.forEach((uuid: string) => {
|
||||
const item = itemsGroup.current.getObjectByProperty("uuid", uuid);
|
||||
if (item) {
|
||||
// Traverse and dispose of resources
|
||||
// item.traverse((child: THREE.Object3D) => {
|
||||
// if (child instanceof THREE.Mesh) {
|
||||
// if (child.geometry) child.geometry.dispose();
|
||||
// if (Array.isArray(child.material)) {
|
||||
// child.material.forEach((material) => {
|
||||
// if (material.map) material.map.dispose();
|
||||
// material.dispose();
|
||||
// });
|
||||
// } else if (child.material) {
|
||||
// if (child.material.map) child.material.map.dispose();
|
||||
// child.material.dispose();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// Remove the object from the scene
|
||||
itemsGroup.current.remove(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (data.toAdd.length > 0) {
|
||||
await initializeDB();
|
||||
|
||||
for (const item of data.toAdd) {
|
||||
if (!activePromises.get(taskId)) return; // Stop processing if task is canceled
|
||||
|
||||
await new Promise<void>(async (resolve) => {
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
|
||||
|
||||
// Check Three.js Cache
|
||||
const cachedModel = THREE.Cache.get(item.modelfileID!);
|
||||
if (cachedModel) {
|
||||
//
|
||||
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, resolve);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check IndexedDB
|
||||
const indexedDBModel = await retrieveGLTF(item.modelfileID!);
|
||||
if (indexedDBModel) {
|
||||
//
|
||||
const blobUrl = URL.createObjectURL(indexedDBModel);
|
||||
loader.load(
|
||||
blobUrl,
|
||||
(gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(item.modelfileID!, gltf); // Add to cache
|
||||
processLoadedModel(gltf.scene.clone(), item, itemsGroup, resolve);
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
echo.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch from Backend
|
||||
//
|
||||
loader.load(
|
||||
modelUrl,
|
||||
async (gltf) => {
|
||||
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
||||
await storeGLTF(item.modelfileID!, modelBlob); // Store in IndexedDB
|
||||
THREE.Cache.add(item.modelfileID!, gltf); // Add to cache
|
||||
processLoadedModel(gltf.scene.clone(), item, itemsGroup, resolve);
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
echo.error(`[Backend] Error loading ${item.modelName}:`);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function processLoadedModel(
|
||||
gltf: any,
|
||||
item: Types.FloorItemType,
|
||||
itemsGroup: Types.RefGroup,
|
||||
resolve: () => void
|
||||
) {
|
||||
if (!activePromises.get(taskId)) return; // Stop processing if task is canceled
|
||||
|
||||
const existingModel = itemsGroup?.current?.getObjectByProperty("uuid", item.modelUuid);
|
||||
if (existingModel) {
|
||||
//
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const model = gltf;
|
||||
model.uuid = item.modelUuid;
|
||||
model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData };
|
||||
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
|
||||
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);
|
||||
|
||||
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: 0.5, ease: "power2.out", onStart: resolve, });
|
||||
}
|
||||
}
|
||||
|
||||
activePromises.delete(taskId); // Mark task as complete
|
||||
}
|
||||
|
||||
// Cancel ongoing task when new call arrives
|
||||
export function cancelOngoingTasks() {
|
||||
activePromises.clear(); // Clear all ongoing tasks
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import * as Types from "../../../../types/world/worldTypes";
|
||||
|
||||
let lastUpdateTime = 0;
|
||||
|
||||
export default function assetVisibility(
|
||||
itemsGroup: Types.RefGroup,
|
||||
cameraPosition: Types.Vector3,
|
||||
renderDistance: Types.Number,
|
||||
throttleTime = 100
|
||||
): void {
|
||||
const now = performance.now();
|
||||
if (now - lastUpdateTime < throttleTime) return;
|
||||
lastUpdateTime = now;
|
||||
|
||||
if (!itemsGroup?.current || !cameraPosition) return;
|
||||
|
||||
itemsGroup.current.children.forEach((child) => {
|
||||
const Distance = cameraPosition.distanceTo(child.position);
|
||||
if (Distance <= renderDistance) {
|
||||
child.visible = true;
|
||||
} else {
|
||||
child.visible = false;
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import * as THREE from 'three';
|
||||
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
|
||||
function DeletableHoveredFloorItems(
|
||||
state: Types.ThreeState,
|
||||
itemsGroup: Types.RefGroup,
|
||||
hoveredDeletableFloorItem: Types.RefMesh,
|
||||
setDeletableFloorItem: any
|
||||
): void {
|
||||
|
||||
////////// Altering the color of the hovered GLTF item during the Deletion time //////////
|
||||
|
||||
state.raycaster.setFromCamera(state.pointer, state.camera);
|
||||
const intersects = state.raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
if (intersects[0].object.name === "Pole") {
|
||||
return;
|
||||
}
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
hoveredDeletableFloorItem.current = currentObject as THREE.Mesh;
|
||||
setDeletableFloorItem(currentObject);
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
}
|
||||
} else {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DeletableHoveredFloorItems;
|
|
@ -1,88 +0,0 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import * as THREE from 'three';
|
||||
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
|
||||
async function DeleteFloorItems(
|
||||
itemsGroup: Types.RefGroup,
|
||||
hoveredDeletableFloorItem: Types.RefMesh,
|
||||
setFloorItems: Types.setFloorItemSetState,
|
||||
socket: Socket<any>
|
||||
): Promise<void> {
|
||||
|
||||
////////// Deleting the hovered Floor GLTF from the scene (itemsGroup.current) and from the floorItems and also update it in the localstorage //////////
|
||||
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
||||
const items = await getFloorAssets(organization);
|
||||
const removedItem = items.find(
|
||||
(item: { modelUuid: string }) => item.modelUuid === hoveredDeletableFloorItem.current?.uuid
|
||||
);
|
||||
|
||||
if (!removedItem) {
|
||||
return
|
||||
}
|
||||
|
||||
//REST
|
||||
|
||||
// const response = await deleteFloorItem(organization, removedItem.modelUuid, removedItem.modelName);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modelUuid: removedItem.modelUuid,
|
||||
modelName: removedItem.modelName,
|
||||
socketId: socket.id
|
||||
}
|
||||
|
||||
const response = socket.emit('v2:model-asset:delete', data)
|
||||
|
||||
useEventsStore.getState().removeEvent(removedItem.modelUuid);
|
||||
useProductStore.getState().deleteEvent(removedItem.modelUuid);
|
||||
|
||||
if (response) {
|
||||
const updatedItems = items.filter(
|
||||
(item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid
|
||||
);
|
||||
|
||||
const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]');
|
||||
const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid);
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems));
|
||||
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
// Traverse and dispose of resources
|
||||
hoveredDeletableFloorItem.current.traverse((child: THREE.Object3D) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
if (child.geometry) child.geometry.dispose();
|
||||
if (Array.isArray(child.material)) {
|
||||
child.material.forEach((material) => {
|
||||
if (material.map) material.map.dispose();
|
||||
material.dispose();
|
||||
});
|
||||
} else if (child.material) {
|
||||
if (child.material.map) child.material.map.dispose();
|
||||
child.material.dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remove the object from the scene
|
||||
itemsGroup.current.remove(hoveredDeletableFloorItem.current);
|
||||
}
|
||||
setFloorItems(updatedItems);
|
||||
|
||||
echo.success("Model Removed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DeleteFloorItems;
|
|
@ -1,29 +0,0 @@
|
|||
import * as THREE from 'three';
|
||||
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
|
||||
function TempLoader(
|
||||
intersectPoint: Types.Vector3,
|
||||
isTempLoader: Types.RefBoolean,
|
||||
tempLoader: Types.RefMesh,
|
||||
itemsGroup: Types.RefGroup
|
||||
): void {
|
||||
|
||||
////////// Temporary Loader that indicates the gltf is being loaded //////////
|
||||
|
||||
////////// Bug: Can't Load More than one TempLoader if done, it won't leave the scene //////////
|
||||
|
||||
if (tempLoader.current) {
|
||||
itemsGroup.current.remove(tempLoader.current);
|
||||
}
|
||||
if (isTempLoader.current) {
|
||||
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
|
||||
const cubeMaterial = new THREE.MeshBasicMaterial({ color: "white" });
|
||||
tempLoader.current = new THREE.Mesh(cubeGeometry, cubeMaterial);
|
||||
tempLoader.current.position.set(intersectPoint.x, 0.5 + intersectPoint.y, intersectPoint.z);
|
||||
itemsGroup.current.add(tempLoader.current);
|
||||
isTempLoader.current = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default TempLoader;
|
|
@ -1,445 +0,0 @@
|
|||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import {
|
||||
useActiveTool,
|
||||
useCamMode,
|
||||
useDeletableFloorItem,
|
||||
useDeleteTool,
|
||||
useFloorItems,
|
||||
useLoadingProgress,
|
||||
useRenderDistance,
|
||||
useSelectedFloorItem,
|
||||
useSelectedItem,
|
||||
useSocketStore,
|
||||
useToggleView,
|
||||
} from "../../../store/builder/store";
|
||||
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 { useEventsStore } from "../../../store/simulation/useEventsStore";
|
||||
import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment";
|
||||
|
||||
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 { 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];
|
||||
|
||||
findEnvironment(
|
||||
organization,
|
||||
localStorage.getItem("userId")!
|
||||
).then((evnironMentData) => {
|
||||
|
||||
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: uniqueItems });
|
||||
} else {
|
||||
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||
loadInitialFloorItems(
|
||||
itemsGroup,
|
||||
setFloorItems,
|
||||
addEvent,
|
||||
evnironMentData.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,
|
||||
evnironMentData.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 = () => {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
if (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;
|
||||
|
||||
|
||||
if (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] && selectedItem.category !== 'Fenestration') {
|
||||
|
||||
state.pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
state.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
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, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
|
||||
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
if (deleteTool && activeModule === "builder") {
|
||||
// assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
DeletableHoveredFloorItems(
|
||||
state,
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setDeletableFloorItem
|
||||
);
|
||||
} else if (!deleteTool) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return <group ref={itemsGroup} name="itemsGroup"></group>;
|
||||
};
|
||||
|
||||
export default FloorItemsGroup;
|
|
@ -19,7 +19,6 @@ import {
|
|||
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import TempLoader from "../../builder/geomentries/assets/tempLoader";
|
||||
|
||||
// import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||
import objectLineToArray from "../../builder/geomentries/lines/lineConvertions/objectLineToArray";
|
||||
|
@ -286,14 +285,6 @@ export default function SocketResponses({
|
|||
});
|
||||
|
||||
THREE.Cache.add(data.data.modelName, gltf);
|
||||
},
|
||||
() => {
|
||||
TempLoader(
|
||||
new THREE.Vector3(...data.data.position),
|
||||
isTempLoader,
|
||||
tempLoader,
|
||||
itemsGroup
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,21 @@ import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from
|
|||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useAssetsStore } from "../../../../store/builder/useAssetStore";
|
||||
|
||||
const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
|
||||
const CopyPasteControls = ({
|
||||
copiedObjects,
|
||||
setCopiedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
selectionGroup,
|
||||
setDuplicatedObjects,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
boundingBoxRef
|
||||
}: any) => {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
|
@ -15,6 +28,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { socket } = useSocketStore();
|
||||
const { addEvent } = useEventsStore();
|
||||
const { addAsset } = useAssetsStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
@ -132,16 +146,15 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
const addPastedObjects = () => {
|
||||
if (pastedObjects.length === 0) return;
|
||||
pastedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.position.copy(worldPosition);
|
||||
|
||||
if (itemsGroupRef.current) {
|
||||
if (obj) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.position.copy(worldPosition);
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: obj.uuid,
|
||||
modelName: obj.userData.name,
|
||||
modelfileID: obj.userData.modelId,
|
||||
modelUuid: THREE.MathUtils.generateUUID(),
|
||||
modelName: obj.userData.modelName,
|
||||
modelfileID: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
|
||||
isLocked: false,
|
||||
|
@ -362,7 +375,20 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
eventData: JSON.parse(JSON.stringify(eventData))
|
||||
};
|
||||
|
||||
itemsGroupRef.current.add(obj);
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.modelfileID,
|
||||
position: data.position,
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
eventData: data.eventData
|
||||
}
|
||||
|
||||
addAsset(asset);
|
||||
|
||||
} else {
|
||||
setFloorItems((prevItems: Types.FloorItems) => {
|
||||
|
@ -403,13 +429,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
obj.userData = {
|
||||
name: newFloorItem.modelName,
|
||||
modelId: newFloorItem.modelfileID,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
};
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.modelfileID,
|
||||
position: data.position,
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
}
|
||||
|
||||
itemsGroupRef.current.add(obj);
|
||||
addAsset(asset);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,11 +4,21 @@ import { useFrame, useThree } from "@react-three/fiber";
|
|||
import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
|
||||
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useAssetsStore } from "../../../../store/builder/useAssetStore";
|
||||
|
||||
const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
|
||||
const DuplicationControls = ({
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
setpastedObjects,
|
||||
selectionGroup,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
boundingBoxRef
|
||||
}: any) => {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
|
@ -16,6 +26,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { socket } = useSocketStore();
|
||||
const { addEvent } = useEventsStore();
|
||||
const { addAsset } = useAssetsStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
@ -110,16 +121,15 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
const addDuplicatedAssets = () => {
|
||||
if (duplicatedObjects.length === 0) return;
|
||||
duplicatedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.position.copy(worldPosition);
|
||||
|
||||
if (itemsGroupRef.current) {
|
||||
if (obj) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.position.copy(worldPosition);
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: obj.uuid,
|
||||
modelName: obj.userData.name,
|
||||
modelfileID: obj.userData.modelId,
|
||||
modelUuid: THREE.MathUtils.generateUUID(),
|
||||
modelName: obj.userData.modelName,
|
||||
modelfileID: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
|
||||
isLocked: false,
|
||||
|
@ -332,14 +342,20 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
obj.userData = {
|
||||
name: newFloorItem.modelName,
|
||||
modelId: newFloorItem.modelfileID,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
eventData: JSON.parse(JSON.stringify(eventData))
|
||||
};
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.modelfileID,
|
||||
position: data.position,
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
eventData: data.eventData
|
||||
}
|
||||
|
||||
itemsGroupRef.current.add(obj);
|
||||
addAsset(asset);
|
||||
|
||||
} else {
|
||||
setFloorItems((prevItems: Types.FloorItems) => {
|
||||
|
@ -380,13 +396,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
obj.userData = {
|
||||
name: newFloorItem.modelName,
|
||||
modelId: newFloorItem.modelfileID,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
};
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.modelfileID,
|
||||
position: data.position,
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
}
|
||||
|
||||
itemsGroupRef.current.add(obj);
|
||||
addAsset(asset);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,11 +16,11 @@ import { useSelectedProduct } from "../../../../store/simulation/useSimulationSt
|
|||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { snapControls } from "../../../../utils/handleSnap";
|
||||
import DistanceFindingControls from "./distanceFindingControls";
|
||||
import { useAssetsStore } from "../../../../store/builder/useAssetStore";
|
||||
|
||||
function MoveControls({
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
itemsGroupRef,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
duplicatedObjects,
|
||||
|
@ -36,12 +36,12 @@ function MoveControls({
|
|||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { socket } = useSocketStore();
|
||||
const itemsData = useRef<Types.FloorItems>([]);
|
||||
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
const { updateAsset } = useAssetsStore();
|
||||
const AssetGroup = useRef<THREE.Group | undefined>(undefined);
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
|
@ -58,11 +58,19 @@ function MoveControls({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
const itemsGroup: any = scene.getObjectByName("Asset Group");
|
||||
AssetGroup.current = itemsGroup;
|
||||
|
||||
if (!AssetGroup.current) {
|
||||
console.error("Asset Group not found in the scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
let isMoving = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
|
@ -91,15 +99,12 @@ function MoveControls({
|
|||
|
||||
clearSelection();
|
||||
movedObjects.forEach((asset: any) => {
|
||||
if (itemsGroupRef.current) {
|
||||
itemsGroupRef.current.attach(asset);
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setFloorItems([...floorItems, ...itemsData.current]);
|
||||
|
||||
setMovedObjects([]);
|
||||
itemsData.current = [];
|
||||
}
|
||||
setKeyEvent("");
|
||||
};
|
||||
|
@ -119,9 +124,6 @@ function MoveControls({
|
|||
if (keyCombination === "G") {
|
||||
if (selectedAssets.length > 0) {
|
||||
moveAssets();
|
||||
itemsData.current = floorItems.filter((item: { modelUuid: string }) =>
|
||||
selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,15 +132,12 @@ function MoveControls({
|
|||
|
||||
clearSelection();
|
||||
movedObjects.forEach((asset: any) => {
|
||||
if (itemsGroupRef.current) {
|
||||
itemsGroupRef.current.attach(asset);
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setFloorItems([...floorItems, ...itemsData.current]);
|
||||
|
||||
setMovedObjects([]);
|
||||
itemsData.current = [];
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -157,7 +156,7 @@ function MoveControls({
|
|||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]);
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]);
|
||||
|
||||
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25;
|
||||
|
||||
|
@ -219,11 +218,6 @@ function MoveControls({
|
|||
});
|
||||
|
||||
const moveAssets = () => {
|
||||
const updatedItems = floorItems.filter(
|
||||
(item: { modelUuid: string }) =>
|
||||
!selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
|
||||
);
|
||||
setFloorItems(updatedItems);
|
||||
setMovedObjects(selectedAssets);
|
||||
selectedAssets.forEach((asset: any) => {
|
||||
selectionGroup.current.attach(asset);
|
||||
|
@ -234,17 +228,17 @@ function MoveControls({
|
|||
if (movedObjects.length === 0) return;
|
||||
|
||||
movedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
if (obj && AssetGroup.current) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
|
||||
selectionGroup.current.remove(obj);
|
||||
obj.position.copy(worldPosition);
|
||||
selectionGroup.current.remove(obj);
|
||||
obj.position.copy(worldPosition);
|
||||
|
||||
if (itemsGroupRef.current) {
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: obj.uuid,
|
||||
modelName: obj.userData.name,
|
||||
modelfileID: obj.userData.modelId,
|
||||
modelUuid: obj.userData.modelUuid,
|
||||
modelName: obj.userData.modelName,
|
||||
modelfileID: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
|
@ -287,10 +281,9 @@ function MoveControls({
|
|||
}
|
||||
}
|
||||
|
||||
setFloorItems((prevItems: Types.FloorItems) => {
|
||||
const updatedItems = [...(prevItems || []), newFloorItem];
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
|
||||
return updatedItems;
|
||||
updateAsset(obj.userData.modelUuid, {
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
|
||||
//REST
|
||||
|
@ -322,12 +315,12 @@ function MoveControls({
|
|||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
itemsGroupRef.current.add(obj);
|
||||
AssetGroup.current.add(obj);
|
||||
}
|
||||
});
|
||||
|
||||
echo.success("Object moved!");
|
||||
|
||||
itemsData.current = [];
|
||||
clearSelection();
|
||||
};
|
||||
|
||||
|
|
|
@ -8,20 +8,32 @@ import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
|||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useAssetsStore } from "../../../../store/builder/useAssetStore";
|
||||
|
||||
function RotateControls({
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
selectionGroup
|
||||
}: any) {
|
||||
|
||||
function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { socket } = useSocketStore();
|
||||
const itemsData = useRef<Types.FloorItems>([]);
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email?.split("@")[1])?.split(".")[0] ?? null;
|
||||
const { updateAsset } = useAssetsStore();
|
||||
const AssetGroup = useRef<THREE.Group | undefined>(undefined);
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
|
@ -40,11 +52,19 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
const itemsGroup: any = scene.getObjectByName("Asset Group");
|
||||
AssetGroup.current = itemsGroup;
|
||||
|
||||
if (!AssetGroup.current) {
|
||||
console.error("Asset Group not found in the scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
let isMoving = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
|
@ -65,15 +85,12 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
|
||||
clearSelection();
|
||||
rotatedObjects.forEach((asset: any) => {
|
||||
if (itemsGroupRef.current) {
|
||||
itemsGroupRef.current.attach(asset);
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setFloorItems([...floorItems, ...itemsData.current]);
|
||||
|
||||
setRotatedObjects([]);
|
||||
itemsData.current = [];
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -82,7 +99,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
if (event.key.toLowerCase() === "r") {
|
||||
if (selectedAssets.length > 0) {
|
||||
rotateAssets();
|
||||
itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid));
|
||||
}
|
||||
}
|
||||
if (event.key.toLowerCase() === "escape") {
|
||||
|
@ -90,15 +106,13 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
|
||||
clearSelection();
|
||||
rotatedObjects.forEach((asset: any) => {
|
||||
if (itemsGroupRef.current) {
|
||||
itemsGroupRef.current.attach(asset);
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setFloorItems([...floorItems, ...itemsData.current]);
|
||||
|
||||
setRotatedObjects([]);
|
||||
itemsData.current = [];
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -115,7 +129,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]);
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]);
|
||||
|
||||
useFrame(() => {
|
||||
if (rotatedObjects.length > 0) {
|
||||
|
@ -146,9 +160,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
});
|
||||
|
||||
const rotateAssets = () => {
|
||||
const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid));
|
||||
setFloorItems(updatedItems);
|
||||
|
||||
const box = new THREE.Box3();
|
||||
selectedAssets.forEach((asset: any) => box.expandByObject(asset));
|
||||
const center = new THREE.Vector3();
|
||||
|
@ -173,26 +184,24 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
if (rotatedObjects.length === 0) return;
|
||||
|
||||
rotatedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
const worldQuaternion = new THREE.Quaternion();
|
||||
if (obj && AssetGroup.current) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
const worldQuaternion = new THREE.Quaternion();
|
||||
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.getWorldQuaternion(worldQuaternion);
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.getWorldQuaternion(worldQuaternion);
|
||||
|
||||
selectionGroup.current.remove(obj);
|
||||
selectionGroup.current.remove(obj);
|
||||
|
||||
obj.position.copy(worldPosition);
|
||||
obj.quaternion.copy(worldQuaternion);
|
||||
|
||||
|
||||
if (itemsGroupRef.current) {
|
||||
obj.position.copy(worldPosition);
|
||||
obj.quaternion.copy(worldQuaternion);
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: obj.uuid,
|
||||
modelName: obj.userData.name,
|
||||
modelfileID: obj.userData.modelId,
|
||||
modelUuid: obj.userData.modelUuid,
|
||||
modelName: obj.userData.modelName,
|
||||
modelfileID: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true
|
||||
};
|
||||
|
@ -221,15 +230,14 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
event
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
newFloorItem.eventData = eventData;
|
||||
}
|
||||
}
|
||||
|
||||
setFloorItems((prevItems: Types.FloorItems) => {
|
||||
const updatedItems = [...(prevItems || []), newFloorItem];
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
|
||||
return updatedItems;
|
||||
updateAsset(obj.userData.modelUuid, {
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
|
||||
//REST
|
||||
|
@ -261,12 +269,11 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
itemsGroupRef.current.add(obj);
|
||||
AssetGroup.current.add(obj);
|
||||
}
|
||||
});
|
||||
echo.success("Object rotated!");
|
||||
|
||||
itemsData.current = [];
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@ import RotateControls from "./rotateControls";
|
|||
import useModuleStore from "../../../../store/useModuleStore";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useAssetsStore } from "../../../../store/builder/useAssetStore";
|
||||
|
||||
const SelectionControls: React.FC = () => {
|
||||
const { camera, controls, gl, scene, pointer } = useThree();
|
||||
const itemsGroupRef = useRef<THREE.Group | undefined>(undefined);
|
||||
const selectionGroup = useRef() as Types.RefGroup;
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
|
@ -28,9 +28,9 @@ const SelectionControls: React.FC = () => {
|
|||
const [pastedObjects, setpastedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const boundingBoxRef = useRef<THREE.Mesh>();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { removeAsset } = useAssetsStore();
|
||||
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -39,9 +39,6 @@ const SelectionControls: React.FC = () => {
|
|||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
const itemsGroup: any = scene.getObjectByName("itemsGroup");
|
||||
itemsGroupRef.current = itemsGroup;
|
||||
|
||||
let isSelecting = false;
|
||||
let isRightClick = false;
|
||||
let rightClickMoved = false;
|
||||
|
@ -49,11 +46,6 @@ const SelectionControls: React.FC = () => {
|
|||
|
||||
const helper = new SelectionHelper(gl);
|
||||
|
||||
// if (!itemsGroup) {
|
||||
// echo.warn("itemsGroup not found in the scene.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (event.button === 2) {
|
||||
isRightClick = true;
|
||||
|
@ -145,7 +137,7 @@ const SelectionControls: React.FC = () => {
|
|||
helper.enabled = false;
|
||||
helper.dispose();
|
||||
};
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]);
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule,]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder") {
|
||||
|
@ -213,14 +205,14 @@ const SelectionControls: React.FC = () => {
|
|||
selectedAssets.forEach((selectedMesh: THREE.Object3D) => {
|
||||
//REST
|
||||
|
||||
// const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name);
|
||||
// const response = await deleteFloorItem(organization, selectedMesh.userData.modelUuid, selectedMesh.userData.modelName);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modelUuid: selectedMesh.uuid,
|
||||
modelName: selectedMesh.userData.name,
|
||||
modelUuid: selectedMesh.userData.modelUuid,
|
||||
modelName: selectedMesh.userData.modelName,
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
|
@ -244,11 +236,11 @@ const SelectionControls: React.FC = () => {
|
|||
}
|
||||
});
|
||||
|
||||
itemsGroupRef.current?.remove(selectedMesh);
|
||||
});
|
||||
|
||||
const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid));
|
||||
setFloorItems(updatedItems);
|
||||
selectedUUIDs.forEach((uuid: string) => {
|
||||
removeAsset(uuid);
|
||||
});
|
||||
}
|
||||
echo.success("Selected models removed!");
|
||||
clearSelection();
|
||||
|
@ -262,13 +254,13 @@ const SelectionControls: React.FC = () => {
|
|||
</group>
|
||||
</group>
|
||||
|
||||
<MoveControls movedObjects={movedObjects} setMovedObjects={setMovedObjects} itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
|
||||
<MoveControls movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
|
||||
|
||||
<RotateControls rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
|
||||
<RotateControls rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} selectionGroup={selectionGroup} />
|
||||
|
||||
<DuplicationControls itemsGroupRef={itemsGroupRef} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
<DuplicationControls duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
|
||||
<CopyPasteControls itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
<CopyPasteControls copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -27,9 +27,13 @@ export default function PostProcessing() {
|
|||
}
|
||||
|
||||
useEffect(()=>{
|
||||
console.log('selectedFloorItem: ', selectedFloorItem);
|
||||
// console.log('selectedFloorItem: ', selectedFloorItem);
|
||||
},[selectedFloorItem])
|
||||
|
||||
useEffect(()=>{
|
||||
// console.log('selectedFloorItem: ', deletableFloorItem);
|
||||
},[deletableFloorItem])
|
||||
|
||||
return (
|
||||
<>
|
||||
<EffectComposer autoClear={false}>
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||
|
||||
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);
|
||||
|
||||
onmessage = (event) => {
|
||||
const { floorItems, cameraPosition, uuids, renderDistance } = event.data;
|
||||
if (!floorItems) return
|
||||
|
||||
const toAdd = [];
|
||||
const toRemove = [];
|
||||
|
||||
const cameraPos = new THREE.Vector3(cameraPosition.x, cameraPosition.y, cameraPosition.z);
|
||||
|
||||
// Check for items to be added
|
||||
floorItems.forEach((item) => {
|
||||
const itemPosition = new THREE.Vector3(...item.position);
|
||||
const distance = cameraPos.distanceTo(itemPosition);
|
||||
|
||||
if (distance <= renderDistance && !uuids.includes(item.modelUuid)) {
|
||||
toAdd.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort the toAdd array based on distance (closest first)
|
||||
toAdd.sort((a, b) => {
|
||||
const aDistance = cameraPos.distanceTo(new THREE.Vector3(...a.position));
|
||||
const bDistance = cameraPos.distanceTo(new THREE.Vector3(...b.position));
|
||||
return aDistance - bDistance;
|
||||
});
|
||||
|
||||
// Check for items to be removed
|
||||
uuids.forEach((uuid) => {
|
||||
const floorItem = floorItems.find((item) => item.modelUuid === uuid);
|
||||
if (floorItem) {
|
||||
const itemPosition = new THREE.Vector3(...floorItem.position);
|
||||
const distance = cameraPos.distanceTo(itemPosition);
|
||||
|
||||
if (distance > renderDistance) {
|
||||
toRemove.push(uuid);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Send the result back to the main thread
|
||||
postMessage({ toAdd, toRemove });
|
||||
};
|
|
@ -17,6 +17,9 @@ onmessage = async (event) => {
|
|||
);
|
||||
|
||||
for (const item of uniqueItems) {
|
||||
if(item.modelfileID === null || item.modelfileID === undefined) {
|
||||
continue; // Skip items without a valid modelfileID
|
||||
}
|
||||
const modelID = item.modelfileID;
|
||||
const indexedDBModel = await retrieveGLTF(modelID);
|
||||
|
||||
|
|
Loading…
Reference in New Issue