Merge remote-tracking branch 'origin/main-demo' into decal-list
This commit is contained in:
14
app/src/modules/builder/Decal/decal.tsx
Normal file
14
app/src/modules/builder/Decal/decal.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import DecalCreator from './decalCreator/decalCreator'
|
||||
|
||||
function Decal() {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<DecalCreator />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Decal
|
||||
152
app/src/modules/builder/Decal/decalCreator/decalCreator.tsx
Normal file
152
app/src/modules/builder/Decal/decalCreator/decalCreator.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import { MathUtils } from 'three';
|
||||
import { useEffect } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useDroppedDecal, useSocketStore } from '../../../../store/builder/store';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
|
||||
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
|
||||
function DecalCreator() {
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { addDecal: addDecalOnWall, getWallById } = wallStore();
|
||||
const { addDecal: addDecalOnFloor, getFloorById } = floorStore();
|
||||
const { droppedDecal } = useDroppedDecal();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
const { controls, gl, pointer, camera, raycaster, scene } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onDrop = (event: DragEvent) => {
|
||||
if (droppedDecal) {
|
||||
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||
const wallIntersect = intersects.find(i => i.object.userData && i.object.userData.wallUuid);
|
||||
const floorIntersect = intersects.find(i => i.object.userData && i.object.userData.floorUuid);
|
||||
console.log('wallIntersect: ', wallIntersect);
|
||||
|
||||
if (wallIntersect) {
|
||||
const wall = getWallById(wallIntersect.object.userData.wallUuid);
|
||||
if (!wall) return;
|
||||
|
||||
const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());
|
||||
|
||||
const decal: Decal = {
|
||||
decalUuid: MathUtils.generateUUID(),
|
||||
decalName: droppedDecal.decalName,
|
||||
decalId: droppedDecal.decalId,
|
||||
decalType: {
|
||||
type: 'Wall',
|
||||
wallUuid: wallIntersect.object.userData.wallUuid,
|
||||
},
|
||||
decalPosition: [point.x, point.y, (wall.wallThickness / 2 + 0.001) * (wallIntersect.normal?.z || 1)],
|
||||
decalRotation: 0,
|
||||
decalOpacity: 1,
|
||||
decalScale: 1,
|
||||
}
|
||||
|
||||
addDecalOnWall(wallIntersect.object.userData.wallUuid, decal);
|
||||
|
||||
setTimeout(() => {
|
||||
const updatedWall = getWallById(wallIntersect.object.userData.wallUuid);
|
||||
if (updatedWall) {
|
||||
if (projectId && updatedWall) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
} else if (floorIntersect) {
|
||||
const floor = getFloorById(floorIntersect.object.userData.floorUuid);
|
||||
if (!floor) return;
|
||||
|
||||
const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone());
|
||||
|
||||
const decal: Decal = {
|
||||
decalUuid: MathUtils.generateUUID(),
|
||||
decalName: droppedDecal.decalName,
|
||||
decalId: droppedDecal.decalId,
|
||||
decalType: {
|
||||
type: 'Floor',
|
||||
floorUuid: floorIntersect.object.userData.floorUuid,
|
||||
},
|
||||
decalPosition: [point.x, point.y, -0.001],
|
||||
decalRotation: 0,
|
||||
decalOpacity: 1,
|
||||
decalScale: 1,
|
||||
}
|
||||
|
||||
addDecalOnFloor(floorIntersect.object.userData.floorUuid, decal);
|
||||
|
||||
setTimeout(() => {
|
||||
const updatedFloor = getFloorById(floorIntersect.object.userData.floorUuid);
|
||||
if (projectId && updatedFloor) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
if (activeModule === "builder") {
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
canvasElement.addEventListener("dragover", onDragOver);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [droppedDecal, camera, activeModule, controls]);
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DecalCreator
|
||||
@@ -1,50 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import { Decal } from '@react-three/drei'
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { useToggleView } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
|
||||
import defaultMaterial from '../../../assets/textures/floor/wall-tex.png';
|
||||
import useModuleStore from '../../../store/useModuleStore';
|
||||
|
||||
function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[2] }: { visible?: boolean, decal: Decal, zPosition?: number }) {
|
||||
const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore();
|
||||
const { togglView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const material = useLoader(THREE.TextureLoader, defaultMaterial);
|
||||
|
||||
return (
|
||||
<Decal
|
||||
// debug
|
||||
visible={visible}
|
||||
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
||||
rotation={[0, 0, decal.decalRotation]}
|
||||
scale={[decal.decalScale, decal.decalScale, 0.01]}
|
||||
userData={decal}
|
||||
onClick={(e) => {
|
||||
if (visible && !togglView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedDecal(e.object);
|
||||
setSelectedWall(null);
|
||||
setSelectedFloor(null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (selectedDecal && selectedDecal.userData.decalUuid === decal.decalUuid) {
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<meshBasicMaterial
|
||||
map={material}
|
||||
side={THREE.DoubleSide}
|
||||
polygonOffset
|
||||
polygonOffsetFactor={-1}
|
||||
/>
|
||||
</Decal>
|
||||
)
|
||||
}
|
||||
|
||||
export default DecalInstance
|
||||
172
app/src/modules/builder/Decal/decalInstance/decalInstance.tsx
Normal file
172
app/src/modules/builder/Decal/decalInstance/decalInstance.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
import * as THREE from 'three';
|
||||
import { Decal } from '@react-three/drei'
|
||||
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils';
|
||||
|
||||
import defaultMaterial from '../../../../assets/image/fallback/fallback decal 1.png';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useDecalEventHandlers } from '../eventHandler/useDecalEventHandlers';
|
||||
|
||||
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
|
||||
function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) {
|
||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const decalRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDecal?.decalData.decalUuid === decal.decalUuid && !selectedDecal.decalMesh) {
|
||||
setSelectedDecal({ decalData: selectedDecal.decalData, decalMesh: decalRef.current });
|
||||
}
|
||||
}, [selectedDecal])
|
||||
|
||||
const { handlePointerMissed, handlePointerLeave, handleClick, handlePointerDown, handlePointerEnter } = useDecalEventHandlers({ parent, decal, visible });
|
||||
|
||||
const [texture, setTexture] = useState<THREE.Texture | null>(null);
|
||||
|
||||
const logDecalStatus = (decalId: string, status: string) => {
|
||||
// console.log(decalId, status);
|
||||
}
|
||||
|
||||
const loadDefaultTexture = () => {
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
textureLoader.load(
|
||||
defaultMaterial,
|
||||
(fallbackTex) => {
|
||||
fallbackTex.name = "default-decal";
|
||||
setTexture(fallbackTex);
|
||||
logDecalStatus(decal.decalId, 'default-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
console.error("Error loading default decal texture:", error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const loadDecalTexture = async (decalId: string) => {
|
||||
|
||||
try {
|
||||
const cachedTexture = THREE.Cache.get(decalId);
|
||||
if (cachedTexture) {
|
||||
setTexture(cachedTexture);
|
||||
logDecalStatus(decalId, 'cache-loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
const indexedDBTexture = await retrieveImage(decalId);
|
||||
if (indexedDBTexture) {
|
||||
const blobUrl = URL.createObjectURL(indexedDBTexture);
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
textureLoader.load(
|
||||
blobUrl,
|
||||
(tex) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
tex.name = decalId;
|
||||
THREE.Cache.add(decalId, tex);
|
||||
setTexture(tex);
|
||||
logDecalStatus(decalId, 'indexedDB-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
console.error(`Error loading texture from IndexedDB:`, error);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
loadFromBackend(decalId);
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
loadFromBackend(decalId);
|
||||
} catch (error) {
|
||||
console.error("Error loading decal texture:", error);
|
||||
loadDefaultTexture();
|
||||
}
|
||||
};
|
||||
|
||||
const loadFromBackend = (decalId: string) => {
|
||||
|
||||
const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`;
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
textureLoader.load(
|
||||
textureUrl,
|
||||
async (tex) => {
|
||||
tex.name = decalId;
|
||||
THREE.Cache.add(decalId, tex);
|
||||
setTexture(tex);
|
||||
logDecalStatus(decalId, 'backend-loaded');
|
||||
|
||||
try {
|
||||
const response = await fetch(textureUrl);
|
||||
const blob = await response.blob();
|
||||
await storeImage(decalId, blob);
|
||||
} catch (error) {
|
||||
console.error("Error storing texture in IndexedDB:", error);
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
echo.error(`Error loading texture from backend: ${decal.decalName}`);
|
||||
loadDefaultTexture();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (decal.decalId) {
|
||||
loadDecalTexture(decal.decalId);
|
||||
} else {
|
||||
loadDefaultTexture();
|
||||
}
|
||||
}, [decal.decalId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
if (toolMode !== 'cursor') {
|
||||
if (selectedDecal) setSelectedDecal(null);
|
||||
}
|
||||
if (toolMode !== '3D-Delete') {
|
||||
if (deletableDecal) setDeletableDecal(null);
|
||||
}
|
||||
} else {
|
||||
if (selectedDecal) setSelectedDecal(null);
|
||||
if (deletableDecal) setDeletableDecal(null);
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]);
|
||||
|
||||
return (
|
||||
<Decal
|
||||
// debug
|
||||
visible={visible}
|
||||
ref={decalRef}
|
||||
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
||||
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
|
||||
scale={[(decal.decalType.type === 'Floor' || zPosition < 0) ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
|
||||
userData={decal}
|
||||
onPointerDown={(e) => { if (e.button === 0) handlePointerDown(e) }}
|
||||
onClick={(e) => { handleClick(e) }}
|
||||
onPointerEnter={(e) => { handlePointerEnter(e) }}
|
||||
onPointerLeave={(e) => { handlePointerLeave(e) }}
|
||||
onPointerMissed={() => handlePointerMissed()}
|
||||
>
|
||||
<meshBasicMaterial
|
||||
map={texture}
|
||||
side={THREE.DoubleSide}
|
||||
polygonOffset
|
||||
polygonOffsetFactor={-1}
|
||||
transparent
|
||||
opacity={decal.decalOpacity}
|
||||
/>
|
||||
</Decal>
|
||||
)
|
||||
}
|
||||
|
||||
export default DecalInstance
|
||||
@@ -0,0 +1,301 @@
|
||||
import * as THREE from 'three';
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
|
||||
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
|
||||
export function useDecalEventHandlers({
|
||||
parent,
|
||||
decal,
|
||||
visible,
|
||||
}: {
|
||||
parent: Wall | Floor;
|
||||
decal: Decal;
|
||||
visible: boolean;
|
||||
}) {
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById, addDecal: addDecalToWall } = wallStore();
|
||||
const { removeDecal: removeDecalInFloor, updateDecalPosition: updateDecalPositionInFloor, getFloorById, addDecal: addDecalToFloor } = floorStore();
|
||||
const { setSelectedWall, setSelectedFloor, setSelectedDecal, setDeletableDecal, deletableDecal, selectedDecal, setDecalDragState, decalDragState } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
const { raycaster, pointer, camera, scene, gl, controls } = useThree();
|
||||
|
||||
useFrame(() => {
|
||||
if (activeModule !== 'builder' || toggleView || !decalDragState.isDragging || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||
|
||||
const wallIntersect = intersects.find(i => i.object.userData?.wallUuid);
|
||||
const floorIntersect = intersects.find(i => i.object.userData?.floorUuid);
|
||||
|
||||
let offset = decalDragState.dragOffset || new THREE.Vector3(0, 0, 0);
|
||||
|
||||
if (wallIntersect) {
|
||||
const wallUuid = wallIntersect.object.userData.wallUuid;
|
||||
const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());
|
||||
|
||||
if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === 'Wall') {
|
||||
updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
|
||||
} else if (decal.decalType.type === 'Wall' && wallUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
|
||||
const addedDecal = addDecalToWall(wallUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]],
|
||||
decalType: { type: 'Wall', wallUuid: wallUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
} else if (decal.decalType.type === 'Floor' && wallUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
const wall = getWallById(wallUuid);
|
||||
if (!wall) return;
|
||||
|
||||
const addedDecal = addDecalToWall(wallUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, wall.wallThickness / 2 + 0.001],
|
||||
decalType: { type: 'Wall', wallUuid: wallUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
}
|
||||
} else if (floorIntersect) {
|
||||
const floorUuid = floorIntersect.object.userData.floorUuid;
|
||||
const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone());
|
||||
|
||||
if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === 'Floor') {
|
||||
updateDecalPositionInFloor(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
|
||||
} else if (decal.decalType.type === 'Floor' && floorUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
|
||||
const addedDecal = addDecalToFloor(floorUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]],
|
||||
decalType: { type: 'Floor', floorUuid: floorUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
} else if (decal.decalType.type === 'Wall' && floorUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
const floor = getFloorById(floorUuid);
|
||||
if (!floor) return;
|
||||
|
||||
const addedDecal = addDecalToFloor(floorUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, -0.001],
|
||||
decalType: { type: 'Floor', floorUuid: floorUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handlePointerUp = (e: PointerEvent) => {
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
if (decalDragState.isDragging) {
|
||||
setDecalDragState(false, null, null);
|
||||
|
||||
if ('wallUuid' in parent) {
|
||||
setTimeout(() => {
|
||||
const updatedWall = getWallById(parent.wallUuid);
|
||||
if (updatedWall) {
|
||||
if (projectId && updatedWall) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
} else if ('floorUuid' in parent) {
|
||||
setTimeout(() => {
|
||||
const updatedFloor = parent;
|
||||
|
||||
if (projectId && updatedFloor) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deleteDecal = (decalUuid: string, parent: Wall | Floor) => {
|
||||
if ('wallUuid' in parent) {
|
||||
const updatedWall = removeDecalInWall(decalUuid);
|
||||
|
||||
if (projectId && updatedWall) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
} else if ('floorUuid' in parent) {
|
||||
const updatedFloor = removeDecalInFloor(decalUuid);
|
||||
|
||||
if (projectId && updatedFloor) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handlePointerDown = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid && toolMode === 'cursor') {
|
||||
e.stopPropagation();
|
||||
setDecalDragState(true, decal.decalUuid, null);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
setSelectedDecal({ decalMesh: e.object, decalData: decal });
|
||||
setSelectedWall(null);
|
||||
setSelectedFloor(null);
|
||||
|
||||
const localIntersect = e.object.worldToLocal(e.point.clone());
|
||||
let dragOffset = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0);
|
||||
setDecalDragState(true, decal.decalUuid, dragOffset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === 'cursor') {
|
||||
setSelectedDecal({ decalMesh: e.object, decalData: decal });
|
||||
setSelectedWall(null);
|
||||
setSelectedFloor(null);
|
||||
} else if (toolMode === '3D-Delete') {
|
||||
deleteDecal(e.object.userData.decalUuid, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerEnter = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === '3D-Delete') {
|
||||
setDeletableDecal(e.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerLeave = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) {
|
||||
setDeletableDecal(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerMissed = () => {
|
||||
if (selectedDecal && selectedDecal.decalMesh && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
if (activeModule === 'builder' && !toggleView && selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid) {
|
||||
canvasElement.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('pointerup', handlePointerUp);
|
||||
};
|
||||
}, [gl, activeModule, toggleView, selectedDecal, camera, controls, visible, parent, decal, decalDragState]);
|
||||
|
||||
return {
|
||||
handlePointerDown,
|
||||
handleClick,
|
||||
handlePointerEnter,
|
||||
handlePointerLeave,
|
||||
handlePointerMissed,
|
||||
deleteDecal
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const arc = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null;
|
||||
@@ -63,8 +69,8 @@ function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const arrow = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-aisle') return null;
|
||||
@@ -50,8 +56,8 @@ function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Instances, Instance } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const { arrowGeometry, arrowInstances } = useMemo(() => {
|
||||
const result = {
|
||||
@@ -68,8 +74,8 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const circle = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null;
|
||||
@@ -38,8 +44,8 @@ function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Instances, Instance } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const dashInstances = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return [];
|
||||
@@ -43,8 +49,8 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Instance, Instances } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const dotPositions = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
||||
@@ -27,8 +33,8 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const arrows = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-aisle') return null;
|
||||
@@ -85,8 +91,8 @@ function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,14 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const shape = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null;
|
||||
|
||||
@@ -35,8 +42,8 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +363,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
setLeft(relativeX);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
setIsRenameMode(false);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function useModelEventHandlers({
|
||||
const { socket } = useSocketStore();
|
||||
const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { getAssetById, removeAsset } = assetStore();
|
||||
const { removeAsset } = assetStore();
|
||||
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
||||
const { resourceManagementId, setResourceManagementId } = useResourceManagementId();
|
||||
const { removeEvent, getEventByModelUuid } = eventStore();
|
||||
@@ -77,11 +77,12 @@ export function useModelEventHandlers({
|
||||
}
|
||||
|
||||
}, [zoneAssetId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!resourceManagementId) return
|
||||
if (resourceManagementId === asset.modelUuid) {
|
||||
|
||||
|
||||
|
||||
|
||||
handleDblClick(asset);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,10 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
}
|
||||
}, [gltfScene]);
|
||||
|
||||
const logModelStatus = (modelId: string, status: string) => {
|
||||
// console.log(modelId, status);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Calculate Bounding Box
|
||||
const calculateBoundingBox = (scene: THREE.Object3D) => {
|
||||
@@ -103,6 +107,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
clone.animations = cachedModel.animations || [];
|
||||
setGltfScene(clone);
|
||||
calculateBoundingBox(clone);
|
||||
logModelStatus(assetId, 'cache-loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,6 +123,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
THREE.Cache.add(assetId, gltf);
|
||||
setGltfScene(gltf.scene.clone());
|
||||
calculateBoundingBox(gltf.scene);
|
||||
logModelStatus(assetId, 'indexedDB-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
@@ -140,6 +146,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
THREE.Cache.add(assetId, gltf);
|
||||
setGltfScene(gltf.scene.clone());
|
||||
calculateBoundingBox(gltf.scene);
|
||||
logModelStatus(assetId, 'backend-loaded');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
|
||||
@@ -33,13 +33,14 @@ import AssetsGroup from "./asset/assetsGroup";
|
||||
import DxfFile from "./dfx/LoadBlueprint";
|
||||
import AislesGroup from "./aisle/aislesGroup";
|
||||
import WallGroup from "./wall/wallGroup";
|
||||
import WallAssetGroup from "./wallAsset/wallAssetGroup";
|
||||
import FloorGroup from "./floor/floorGroup";
|
||||
import ZoneGroup from "./zone/zoneGroup";
|
||||
import Decal from "./Decal/decal";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import WallAssetGroup from "./wallAsset/wallAssetGroup";
|
||||
|
||||
export default function Builder() {
|
||||
const state = useThree<Types.ThreeState>();
|
||||
@@ -106,6 +107,8 @@ export default function Builder() {
|
||||
</Geometry>
|
||||
</mesh>
|
||||
|
||||
<Decal />
|
||||
|
||||
<AislesGroup />
|
||||
|
||||
<FloorGroup />
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useMemo } from "react";
|
||||
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, } from "three";
|
||||
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, ExtrudeGeometry, Vector3, Euler, } from "three";
|
||||
import { useLoader } from "@react-three/fiber";
|
||||
import { Extrude } from "@react-three/drei";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
|
||||
import { useToggleView } from "../../../../../store/builder/store";
|
||||
import * as Constants from "../../../../../types/world/worldConstants";
|
||||
|
||||
import DecalInstance from "../../../Decal/decalInstance/decalInstance";
|
||||
|
||||
import texturePath from "../../../../../assets/textures/floor/white.png";
|
||||
import texturePathDark from "../../../../../assets/textures/floor/black.png";
|
||||
import material1 from "../../../../../assets/textures/floor/factory wall texture.jpg";
|
||||
@@ -28,7 +29,7 @@ import material4MetalicMap from "../../../../../assets/textures/floor/tex3/metal
|
||||
import material4NormalMap from "../../../../../assets/textures/floor/tex3/metal_plate_nor_gl_1k.png";
|
||||
|
||||
function FloorInstance({ floor }: { floor: Floor }) {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
@@ -67,20 +68,26 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
},
|
||||
};
|
||||
|
||||
const shape = useMemo(() => {
|
||||
const shape = new Shape();
|
||||
const shapeData = useMemo(() => {
|
||||
const points = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
|
||||
if (points.length < 3) return null;
|
||||
shape.moveTo(points[0].x, points[0].y);
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
shape.lineTo(points[i].x, points[i].y);
|
||||
|
||||
const centroidX = points.reduce((sum, p) => sum + p.x, 0) / points.length;
|
||||
const centroidY = points.reduce((sum, p) => sum + p.y, 0) / points.length;
|
||||
|
||||
const relativePoints = points.map((p) => new Vector2(p.x - centroidX, p.y - centroidY));
|
||||
|
||||
const shape = new Shape();
|
||||
shape.moveTo(relativePoints[0].x, relativePoints[0].y);
|
||||
for (let i = 1; i < relativePoints.length; i++) {
|
||||
shape.lineTo(relativePoints[i].x, relativePoints[i].y);
|
||||
}
|
||||
return shape;
|
||||
|
||||
return { shape, center: [centroidX, centroidY] };
|
||||
}, [floor]);
|
||||
|
||||
const textureScale = Constants.floorConfig.textureScale;
|
||||
|
||||
// Helper function to handle texture maps and filter out null values
|
||||
function getMaterialMaps(material: any, defaultMap: any) {
|
||||
const materialMap = material.map || defaultMap;
|
||||
const normalMap = material.normalMap || null;
|
||||
@@ -90,26 +97,18 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
return [materialMap, normalMap, roughnessMap, metalnessMap].filter((texture): texture is string => texture !== null);
|
||||
}
|
||||
|
||||
// Default material map
|
||||
const defaultMaterialMap = materials["Default Material"].map;
|
||||
|
||||
// Get top and side material maps
|
||||
const topMaterial = materials[floor.topMaterial];
|
||||
const sideMaterial = materials[floor.sideMaterial];
|
||||
|
||||
// Get the filtered lists for top and side textures
|
||||
const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
|
||||
const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);
|
||||
|
||||
// Use loader to load top and side textures
|
||||
const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] = useLoader(TextureLoader, topTexturesList);
|
||||
|
||||
const [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture] = useLoader(TextureLoader, sideTexturesList);
|
||||
|
||||
// Early exit if materials are missing
|
||||
if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null;
|
||||
|
||||
// Combine and pair textures with their corresponding material
|
||||
const textureMaterialMap = [
|
||||
{
|
||||
textures: [
|
||||
@@ -131,7 +130,6 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
},
|
||||
];
|
||||
|
||||
// Apply texture settings
|
||||
textureMaterialMap.forEach(({ textures, materialKey }) => {
|
||||
const tileScale = materials[materialKey]?.textureTileScale ?? [
|
||||
textureScale,
|
||||
@@ -143,23 +141,39 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
tex.wrapS = tex.wrapT = RepeatWrapping;
|
||||
tex.repeat.set(tileScale[0], tileScale[1]);
|
||||
tex.anisotropy = 16;
|
||||
// First texture is always the color map (use SRGB), others should be linear
|
||||
tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
|
||||
});
|
||||
});
|
||||
|
||||
if (!shape) return null;
|
||||
const geometry = useMemo(() => {
|
||||
if (!shapeData) return null;
|
||||
return new ExtrudeGeometry(shapeData.shape, {
|
||||
depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
|
||||
bevelEnabled: floor.isBeveled,
|
||||
bevelSegments: floor.bevelStrength,
|
||||
bevelOffset: -0.1,
|
||||
bevelSize: 0.1,
|
||||
bevelThickness: 0.1,
|
||||
});
|
||||
}, [shapeData, floor]);
|
||||
|
||||
if (!geometry) return null;
|
||||
|
||||
return (
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={geometry}
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
position={[0, !floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2, 0,]}
|
||||
position={[
|
||||
shapeData?.center[0] ?? 0,
|
||||
!floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2,
|
||||
shapeData?.center[1] ?? 0,
|
||||
]}
|
||||
userData={floor}
|
||||
onDoubleClick={(e) => {
|
||||
if (!togglView && activeModule === "builder") {
|
||||
if (!toggleView && activeModule === "builder") {
|
||||
if (e.object.userData.floorUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedFloor(e.object);
|
||||
@@ -173,41 +187,32 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Extrude
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
args={[shape,
|
||||
{
|
||||
depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
|
||||
bevelEnabled: floor.isBeveled,
|
||||
bevelSegments: floor.bevelStrength,
|
||||
bevelOffset: -0.1,
|
||||
bevelSize: 0.1,
|
||||
bevelThickness: 0.1,
|
||||
},
|
||||
]}
|
||||
userData={floor}
|
||||
>
|
||||
<meshPhysicalMaterial
|
||||
attach="material-0"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={topTexture}
|
||||
roughnessMap={topRoughnessTexture}
|
||||
metalnessMap={topMetalicTexture}
|
||||
normalMap={topNormalTexture}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial
|
||||
attach="material-1"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={sideTexture?.clone()}
|
||||
roughnessMap={sideRoughnessTexture?.clone()}
|
||||
metalnessMap={sideMetalicTexture?.clone()}
|
||||
normalMap={sideNormalTexture?.clone()}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
<meshPhysicalMaterial
|
||||
attach="material-0"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={topTexture}
|
||||
roughnessMap={topRoughnessTexture}
|
||||
metalnessMap={topMetalicTexture}
|
||||
normalMap={topNormalTexture}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial
|
||||
attach="material-1"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={sideTexture?.clone()}
|
||||
roughnessMap={sideRoughnessTexture?.clone()}
|
||||
metalnessMap={sideMetalicTexture?.clone()}
|
||||
normalMap={sideNormalTexture?.clone()}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
|
||||
{floor.decals.map((decal) => (
|
||||
<DecalInstance parent={floor} key={decal.decalUuid} decal={decal} />
|
||||
))}
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,21 +2,36 @@ import React, { useEffect, useMemo } from 'react';
|
||||
import { Vector3 } from 'three';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import Line from '../../line/line';
|
||||
import Point from '../../point/point';
|
||||
import FloorInstance from './Instance/floorInstance';
|
||||
import Floor2DInstance from './Instance/floor2DInstance';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
|
||||
function FloorInstances() {
|
||||
const { floorStore } = useSceneContext();
|
||||
const { floors } = floorStore();
|
||||
const { setSelectedFloor, selectedFloor } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('floors: ', floors);
|
||||
}, [floors])
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
if (toolMode !== 'cursor') {
|
||||
if (selectedFloor) setSelectedFloor(null);
|
||||
}
|
||||
} else {
|
||||
if (selectedFloor) setSelectedFloor(null);
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedFloor]);
|
||||
|
||||
const allPoints = useMemo(() => {
|
||||
const points: Point[] = [];
|
||||
const seenUuids = new Set<string>();
|
||||
|
||||
@@ -10,7 +10,7 @@ import FloorInstances from './Instances/floorInstances';
|
||||
import { getFloorsApi } from '../../../services/factoryBuilder/floor/getFloorsApi';
|
||||
|
||||
function FloorGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
@@ -21,11 +21,11 @@ function FloorGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
if (toggleView || activeModule !== 'builder') {
|
||||
setSelectedFloor(null);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, activeTool])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useToggleView, useWallVisibility } from '../../../../../store/builder/s
|
||||
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
||||
import * as Constants from '../../../../../types/world/worldConstants';
|
||||
|
||||
import DecalInstance from '../../../Decal/decalInstance';
|
||||
import DecalInstance from '../../../Decal/decalInstance/decalInstance';
|
||||
|
||||
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
||||
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
||||
@@ -21,7 +21,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore();
|
||||
const assets = getWallAssetsByWall(wall.wallUuid);
|
||||
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { camera } = useThree();
|
||||
const { wallVisibility } = useWallVisibility();
|
||||
@@ -118,10 +118,11 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
>
|
||||
{(assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0)) ?
|
||||
<Base
|
||||
name={`BaseWall${wall.wallUuid}`}
|
||||
castShadow
|
||||
receiveShadow
|
||||
ref={meshRef}
|
||||
geometry={geometry}
|
||||
geometry={visible ? geometry : new THREE.BoxGeometry(0, 0)}
|
||||
position={[centerX, centerY, centerZ]}
|
||||
rotation={[0, -angle, 0]}
|
||||
userData={wall}
|
||||
@@ -154,7 +155,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
userData={wall}
|
||||
name={`WallReference_${wall.wallUuid}`}
|
||||
onDoubleClick={(e) => {
|
||||
if (visible && !togglView && activeModule === 'builder') {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.wallUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedWall(e.object);
|
||||
@@ -171,7 +172,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
<MeshDiscardMaterial />
|
||||
|
||||
{wall.decals.map((decal) => (
|
||||
<DecalInstance zPosition={wall.wallThickness / 2 + 0.001} visible={visible} key={decal.decalUuid} decal={decal} />
|
||||
<DecalInstance parent={wall} visible={visible} key={decal.decalUuid} decal={decal} />
|
||||
))}
|
||||
</mesh>
|
||||
</mesh>
|
||||
|
||||
@@ -2,8 +2,9 @@ import React, { useEffect, useMemo } from 'react';
|
||||
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three';
|
||||
import { Html, Extrude } from '@react-three/drei';
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useWallClassification } from './instance/helpers/useWallClassification';
|
||||
import Line from '../../line/line';
|
||||
import Point from '../../point/point';
|
||||
@@ -12,17 +13,31 @@ import * as Constants from '../../../../types/world/worldConstants';
|
||||
|
||||
import texturePath from "../../../../assets/textures/floor/white.png";
|
||||
import texturePathDark from "../../../../assets/textures/floor/black.png";
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
|
||||
function WallInstances() {
|
||||
const { wallStore } = useSceneContext();
|
||||
const { setSelectedWall, selectedWall } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { walls } = wallStore();
|
||||
const { rooms } = useWallClassification(walls);
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('walls: ', walls);
|
||||
}, [walls])
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
if (toolMode !== 'cursor') {
|
||||
if (selectedWall) setSelectedWall(null);
|
||||
}
|
||||
} else {
|
||||
if (selectedWall) setSelectedWall(null);
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedWall]);
|
||||
|
||||
const allPoints = useMemo(() => {
|
||||
const points: Point[] = [];
|
||||
const seenUuids = new Set<string>();
|
||||
|
||||
@@ -11,7 +11,7 @@ import WallInstances from './Instances/wallInstances';
|
||||
import { getWallsApi } from '../../../services/factoryBuilder/wall/getWallsApi';
|
||||
|
||||
function WallGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
@@ -22,11 +22,11 @@ function WallGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
if (toggleView || activeModule !== 'builder') {
|
||||
setSelectedWall(null);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, activeTool])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -18,10 +18,10 @@ const calculateAssetTransformationOnWall = (
|
||||
|
||||
const projection = initialWallNormalized.clone().multiplyScalar(dotProduct);
|
||||
const perpendicular = new THREE.Vector3().subVectors(assetVector, projection);
|
||||
const distanceFromWall = perpendicular.length();
|
||||
const distanceInWall = perpendicular.length();
|
||||
|
||||
const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y;
|
||||
const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1);
|
||||
const signedDistance = distanceInWall * (crossProduct >= 0 ? 1 : -1);
|
||||
|
||||
const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength));
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
|
||||
const { wallStore, wallAssetStore } = useSceneContext();
|
||||
const { walls, getWallById } = wallStore();
|
||||
const { updateWallAsset, removeWallAsset } = wallAssetStore();
|
||||
const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { activeModule } = useModuleStore();
|
||||
@@ -116,33 +116,14 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerUp = (e: PointerEvent) => {
|
||||
draggingRef.current = false;
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (draggingRef.current) {
|
||||
draggingRef.current = false;
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
|
||||
if (selectedWallAsset) {
|
||||
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||
const intersect = intersects.find((i: any) => i.object.name.includes('WallReference'));
|
||||
|
||||
if (intersect && intersect.object.userData.wallUuid && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
||||
const newPoint = closestPointOnLineSegment(
|
||||
new THREE.Vector3(intersect.point.x, 0, intersect.point.z),
|
||||
new THREE.Vector3(...intersect.object.userData.points[0].position),
|
||||
new THREE.Vector3(...intersect.object.userData.points[1].position)
|
||||
);
|
||||
|
||||
const wallRotation = intersect.object.rotation.clone();
|
||||
|
||||
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
|
||||
wallUuid: intersect.object.userData.wallUuid,
|
||||
position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z],
|
||||
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
||||
});
|
||||
if (selectedWallAsset) {
|
||||
const updatedWallAsset = getWallAssetById(wallAsset.modelUuid);
|
||||
|
||||
if (projectId && updatedWallAsset) {
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegm
|
||||
function WallAssetCreator() {
|
||||
const { socket } = useSocketStore();
|
||||
const { pointer, camera, raycaster, scene, gl } = useThree();
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { wallAssetStore } = useSceneContext();
|
||||
const { addWallAsset } = wallAssetStore();
|
||||
@@ -84,7 +84,7 @@ function WallAssetCreator() {
|
||||
}
|
||||
};
|
||||
|
||||
if (!togglView && activeModule === 'builder') {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
canvasElement.addEventListener('drop', onDrop);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ function WallAssetCreator() {
|
||||
canvasElement.removeEventListener('drop', onDrop);
|
||||
};
|
||||
|
||||
}, [gl, camera, togglView, activeModule, socket, selectedItem, setSelectedItem]);
|
||||
}, [gl, camera, toggleView, activeModule, socket, selectedItem, setSelectedItem]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useActiveTool, useToggleView } from '../../../store/builder/store';
|
||||
import { useToggleView, useToolMode } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { useVersionContext } from '../version/versionContext';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
@@ -10,10 +10,10 @@ import WallAssetInstances from './Instances/wallAssetInstances'
|
||||
import { getWallAssetsApi } from '../../../services/factoryBuilder/asset/wallAsset/getWallAssetsApi';
|
||||
|
||||
function WallAssetGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { setSelectedFloorAsset, setDeletableWallAsset } = useBuilderStore();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedWallAsset, setDeletableWallAsset } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { wallAssetStore } = useSceneContext();
|
||||
@@ -21,11 +21,11 @@ function WallAssetGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
setSelectedFloorAsset(null);
|
||||
if (toggleView || activeModule !== 'builder' || toolMode !== 'cursor') {
|
||||
setSelectedWallAsset(null);
|
||||
}
|
||||
setDeletableWallAsset(null);
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, toolMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import ZoneInstances from './Instances/zoneInstances';
|
||||
import { getZonesApi } from '../../../services/factoryBuilder/zone/getZonesApi';
|
||||
|
||||
function ZoneGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedZone } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
@@ -22,10 +22,10 @@ function ZoneGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
if (toggleView || activeModule !== 'builder') {
|
||||
setSelectedZone(null);
|
||||
}
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, activeTool])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { useCamMode, useToggleView } from "../../../store/builder/store";
|
||||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import switchToThirdPerson from "./switchToThirdPerson";
|
||||
import switchToFirstPerson from "./switchToFirstPerson";
|
||||
|
||||
import switchToThirdPerson from "./functions/switchToThirdPerson";
|
||||
import switchToFirstPerson from "./functions/switchToFirstPerson";
|
||||
import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { firstPersonCamera } from "./firstPersonCamera";
|
||||
import { firstPersonCamera } from "./functions/firstPersonCamera";
|
||||
|
||||
const CamMode: React.FC = () => {
|
||||
const { camMode, setCamMode } = useCamMode();
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
||||
interface FirstPersonCameraProps {
|
||||
setIsTransitioning?: (value: boolean) => void;
|
||||
state: any;
|
||||
}
|
||||
|
||||
interface FirstPersonCameraParams extends FirstPersonCameraProps {
|
||||
camMode: string;
|
||||
setCamMode: (mode: string) => void;
|
||||
switchToFirstPerson: (controls: any, camera: any) => Promise<void>;
|
||||
switchToThirdPerson: (controls: any, camera: any) => Promise<void>;
|
||||
}
|
||||
|
||||
export async function firstPersonCamera({
|
||||
setIsTransitioning,
|
||||
state,
|
||||
camMode,
|
||||
setCamMode,
|
||||
switchToFirstPerson,
|
||||
switchToThirdPerson
|
||||
}: FirstPersonCameraParams): Promise<void> {
|
||||
setIsTransitioning && setIsTransitioning(true);
|
||||
|
||||
state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse;
|
||||
state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse;
|
||||
state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse;
|
||||
state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse;
|
||||
|
||||
if (camMode === "ThirdPerson") {
|
||||
setCamMode("FirstPerson");
|
||||
await switchToFirstPerson(state.controls, state.camera);
|
||||
} else if (camMode === "FirstPerson") {
|
||||
setCamMode("ThirdPerson");
|
||||
await switchToThirdPerson(state.controls, state.camera);
|
||||
}
|
||||
|
||||
setIsTransitioning && setIsTransitioning(false);
|
||||
}
|
||||
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||
|
||||
interface FirstPersonCameraProps {
|
||||
setIsTransitioning?: (value: boolean) => void;
|
||||
state: any;
|
||||
}
|
||||
|
||||
interface FirstPersonCameraParams extends FirstPersonCameraProps {
|
||||
camMode: string;
|
||||
setCamMode: (mode: string) => void;
|
||||
switchToFirstPerson: (controls: any, camera: any) => Promise<void>;
|
||||
switchToThirdPerson: (controls: any, camera: any) => Promise<void>;
|
||||
}
|
||||
|
||||
export async function firstPersonCamera({
|
||||
setIsTransitioning,
|
||||
state,
|
||||
camMode,
|
||||
setCamMode,
|
||||
switchToFirstPerson,
|
||||
switchToThirdPerson
|
||||
}: FirstPersonCameraParams): Promise<void> {
|
||||
setIsTransitioning && setIsTransitioning(true);
|
||||
|
||||
state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse;
|
||||
state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse;
|
||||
state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse;
|
||||
state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse;
|
||||
|
||||
if (camMode === "ThirdPerson") {
|
||||
setCamMode("FirstPerson");
|
||||
await switchToFirstPerson(state.controls, state.camera);
|
||||
} else if (camMode === "FirstPerson") {
|
||||
setCamMode("ThirdPerson");
|
||||
await switchToThirdPerson(state.controls, state.camera);
|
||||
}
|
||||
|
||||
setIsTransitioning && setIsTransitioning(false);
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
import * as THREE from 'three';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
|
||||
export default async function switchToFirstPerson(
|
||||
controls: any,
|
||||
camera: any
|
||||
) {
|
||||
if (!controls) return;
|
||||
|
||||
const cameraDirection = new THREE.Vector3();
|
||||
camera.getWorldDirection(cameraDirection);
|
||||
cameraDirection.normalize();
|
||||
|
||||
await controls.setPosition(camera.position.x, 2, camera.position.z, true);
|
||||
controls.setTarget(camera.position.x, 2, camera.position.z, true);
|
||||
controls.mouseButtons.left = CONSTANTS.firstPersonControls.leftMouse;
|
||||
controls.lockPointer();
|
||||
|
||||
controls.azimuthRotateSpeed = CONSTANTS.firstPersonControls.azimuthRotateSpeed;
|
||||
controls.polarRotateSpeed = CONSTANTS.firstPersonControls.polarRotateSpeed;
|
||||
controls.truckSpeed = CONSTANTS.firstPersonControls.truckSpeed;
|
||||
controls.minDistance = CONSTANTS.firstPersonControls.minDistance;
|
||||
controls.maxDistance = CONSTANTS.firstPersonControls.maxDistance;
|
||||
controls.maxPolarAngle = CONSTANTS.firstPersonControls.maxPolarAngle;
|
||||
import * as THREE from 'three';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
|
||||
export default async function switchToFirstPerson(
|
||||
controls: any,
|
||||
camera: any
|
||||
) {
|
||||
if (!controls) return;
|
||||
|
||||
const cameraDirection = new THREE.Vector3();
|
||||
camera.getWorldDirection(cameraDirection);
|
||||
cameraDirection.normalize();
|
||||
|
||||
await controls.setPosition(camera.position.x, 2, camera.position.z, true);
|
||||
controls.setTarget(camera.position.x, 2, camera.position.z, true);
|
||||
controls.mouseButtons.left = CONSTANTS.firstPersonControls.leftMouse;
|
||||
controls.lockPointer();
|
||||
|
||||
controls.azimuthRotateSpeed = CONSTANTS.firstPersonControls.azimuthRotateSpeed;
|
||||
controls.polarRotateSpeed = CONSTANTS.firstPersonControls.polarRotateSpeed;
|
||||
controls.truckSpeed = CONSTANTS.firstPersonControls.truckSpeed;
|
||||
controls.minDistance = CONSTANTS.firstPersonControls.minDistance;
|
||||
controls.maxDistance = CONSTANTS.firstPersonControls.maxDistance;
|
||||
controls.maxPolarAngle = CONSTANTS.firstPersonControls.maxPolarAngle;
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
import * as THREE from 'three';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
|
||||
export default async function switchToThirdPerson(
|
||||
controls: any,
|
||||
camera: any
|
||||
) {
|
||||
if (!controls) return;
|
||||
controls.mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse;
|
||||
controls.mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse;
|
||||
controls.mouseButtons.middle = CONSTANTS.thirdPersonControls.middleMouse;
|
||||
controls.mouseButtons.wheel = CONSTANTS.thirdPersonControls.wheelMouse;
|
||||
controls.unlockPointer();
|
||||
|
||||
const cameraDirection = new THREE.Vector3();
|
||||
camera.getWorldDirection(cameraDirection);
|
||||
const targetOffset = cameraDirection.multiplyScalar(CONSTANTS.thirdPersonControls.targetOffset);
|
||||
const targetPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z).add(targetOffset);
|
||||
|
||||
controls.setPosition(camera.position.x, CONSTANTS.thirdPersonControls.cameraHeight, camera.position.z, true);
|
||||
controls.setTarget(targetPosition.x, 0, targetPosition.z, true);
|
||||
|
||||
controls.azimuthRotateSpeed = CONSTANTS.thirdPersonControls.azimuthRotateSpeed;
|
||||
controls.polarRotateSpeed = CONSTANTS.thirdPersonControls.polarRotateSpeed;
|
||||
controls.truckSpeed = CONSTANTS.thirdPersonControls.truckSpeed;
|
||||
controls.minDistance = CONSTANTS.threeDimension.minDistance;
|
||||
controls.maxDistance = CONSTANTS.thirdPersonControls.maxDistance;
|
||||
controls.maxPolarAngle = CONSTANTS.thirdPersonControls.maxPolarAngle;
|
||||
import * as THREE from 'three';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
|
||||
export default async function switchToThirdPerson(
|
||||
controls: any,
|
||||
camera: any
|
||||
) {
|
||||
if (!controls) return;
|
||||
controls.mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse;
|
||||
controls.mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse;
|
||||
controls.mouseButtons.middle = CONSTANTS.thirdPersonControls.middleMouse;
|
||||
controls.mouseButtons.wheel = CONSTANTS.thirdPersonControls.wheelMouse;
|
||||
controls.unlockPointer();
|
||||
|
||||
const cameraDirection = new THREE.Vector3();
|
||||
camera.getWorldDirection(cameraDirection);
|
||||
const targetOffset = cameraDirection.multiplyScalar(CONSTANTS.thirdPersonControls.targetOffset);
|
||||
const targetPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z).add(targetOffset);
|
||||
|
||||
controls.setPosition(camera.position.x, CONSTANTS.thirdPersonControls.cameraHeight, camera.position.z, true);
|
||||
controls.setTarget(targetPosition.x, 0, targetPosition.z, true);
|
||||
|
||||
controls.azimuthRotateSpeed = CONSTANTS.thirdPersonControls.azimuthRotateSpeed;
|
||||
controls.polarRotateSpeed = CONSTANTS.thirdPersonControls.polarRotateSpeed;
|
||||
controls.truckSpeed = CONSTANTS.thirdPersonControls.truckSpeed;
|
||||
controls.minDistance = CONSTANTS.threeDimension.minDistance;
|
||||
controls.maxDistance = CONSTANTS.thirdPersonControls.maxDistance;
|
||||
controls.maxPolarAngle = CONSTANTS.thirdPersonControls.maxPolarAngle;
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
import { Socket } from "socket.io-client";
|
||||
import * as THREE from "three";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
|
||||
export default function updateCamPosition(
|
||||
controls: any,
|
||||
socket: Socket,
|
||||
position: THREE.Vector3,
|
||||
rotation: THREE.Euler,
|
||||
projectId?: string
|
||||
) {
|
||||
const { userId, organization } = getUserData();
|
||||
if (!controls.current) return;
|
||||
const target = controls.current.getTarget(new THREE.Vector3());
|
||||
|
||||
const camData = {
|
||||
organization,
|
||||
userId: userId,
|
||||
position: position,
|
||||
target: new THREE.Vector3(target.x, 0, target.z),
|
||||
rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z),
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
};
|
||||
socket.emit("v1:Camera:set", camData);
|
||||
}
|
||||
import { Socket } from "socket.io-client";
|
||||
import * as THREE from "three";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
|
||||
export default function updateCamPosition(
|
||||
controls: any,
|
||||
socket: Socket,
|
||||
position: THREE.Vector3,
|
||||
rotation: THREE.Euler,
|
||||
projectId?: string
|
||||
) {
|
||||
const { userId, organization } = getUserData();
|
||||
if (!controls.current) return;
|
||||
const target = controls.current.getTarget(new THREE.Vector3());
|
||||
|
||||
const camData = {
|
||||
organization,
|
||||
userId: userId,
|
||||
position: position,
|
||||
target: new THREE.Vector3(target.x, 0, target.z),
|
||||
rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z),
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
};
|
||||
socket.emit("v1:Camera:set", camData);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import type { CameraControls } from "@react-three/drei";
|
||||
|
||||
const CameraShortcutsControls = () => {
|
||||
const { camera, controls } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!controls) return;
|
||||
|
||||
const cc = controls as CameraControls;
|
||||
|
||||
// get current target
|
||||
const target = new THREE.Vector3();
|
||||
cc.getTarget(target);
|
||||
|
||||
const distance = camera.position.distanceTo(target);
|
||||
let pos: THREE.Vector3 | null = null;
|
||||
|
||||
const dir = new THREE.Vector3().subVectors(camera.position, target).normalize();
|
||||
|
||||
switch (e.key) {
|
||||
case "1": // Front
|
||||
pos = new THREE.Vector3(0, 0, distance).add(target);
|
||||
break;
|
||||
case "3": // Right
|
||||
pos = new THREE.Vector3(distance, 0, 0).add(target);
|
||||
break;
|
||||
case "7": // Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
break;
|
||||
case "9": {
|
||||
// Opposite view logic
|
||||
if (Math.abs(dir.z) > Math.abs(dir.x) && Math.abs(dir.z) > Math.abs(dir.y)) {
|
||||
// Currently looking Front/Back → flip Z
|
||||
pos = new THREE.Vector3(0, 0, -Math.sign(dir.z) * distance).add(target);
|
||||
} else if (Math.abs(dir.x) > Math.abs(dir.z) && Math.abs(dir.x) > Math.abs(dir.y)) {
|
||||
// Currently looking Right/Left → flip X
|
||||
pos = new THREE.Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target);
|
||||
} else {
|
||||
// Currently looking Top/Bottom → stay Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos) {
|
||||
cc.setLookAt(
|
||||
pos.x, pos.y, pos.z, // camera position
|
||||
target.x, target.y, target.z, // keep same target
|
||||
true // smooth transition
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [controls, camera]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default CameraShortcutsControls;
|
||||
@@ -1,41 +1,14 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { CameraControls, Html, ScreenSpace } from "@react-three/drei";
|
||||
import {
|
||||
useContextActionStore,
|
||||
useRenameModeStore,
|
||||
useSelectedAssets,
|
||||
} from "../../../../store/builder/store";
|
||||
import { useContextActionStore, useRenameModeStore, useSelectedAssets, useToggleView, useToolMode, } from "../../../../store/builder/store";
|
||||
import ContextMenu from "../../../../components/ui/menu/contextMenu";
|
||||
import useModuleStore from "../../../../store/useModuleStore";
|
||||
|
||||
function ContextControls() {
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
@@ -49,195 +22,219 @@ function ContextControls() {
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
}
|
||||
}, [selectedAssets]);
|
||||
});
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerDown = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = true;
|
||||
rightDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
if (isRightMouseDown.current) {
|
||||
rightDrag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (rightDrag.current) return;
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({
|
||||
x: event.clientX - gl.domElement.width / 2,
|
||||
y: event.clientY - gl.domElement.height / 2,
|
||||
});
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerDown = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = true;
|
||||
rightDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
if (isRightMouseDown.current) {
|
||||
rightDrag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (rightDrag.current) return;
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2, });
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0 && !toggleView && activeModule === "builder" && toolMode === 'cursor') {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("contextmenu", handleContextClick);
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
||||
};
|
||||
}, [controls, gl, selectedAssets, toggleView, activeModule, toolMode]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
};
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
};
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset");
|
||||
};
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset");
|
||||
};
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset");
|
||||
};
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset");
|
||||
};
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset");
|
||||
};
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset");
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("contextmenu", handleContextClick);
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
||||
};
|
||||
}, [controls, gl, selectedAssets]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
};
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
};
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset");
|
||||
};
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset");
|
||||
};
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset");
|
||||
};
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset");
|
||||
};
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset");
|
||||
};
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1}>
|
||||
<Html
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1}>
|
||||
<Html
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextControls;
|
||||
|
||||
@@ -3,22 +3,22 @@ import { useRef, useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
|
||||
import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store";
|
||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import updateCamPosition from "../camera/updateCameraPosition";
|
||||
|
||||
import CamMode from "../camera/camMode";
|
||||
import SwitchView from "../camera/switchView";
|
||||
import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D";
|
||||
import TransformControl from "./transformControls/transformControls";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
|
||||
import ContextControls from "./contextControls/contextControls";
|
||||
import TransformControl from "./transformControls/transformControls";
|
||||
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
||||
import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D";
|
||||
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||
import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls";
|
||||
import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts";
|
||||
import CameraShortcutsControls from "../camera/shortcutsControls/cameraShortcutsControls";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import updateCamPosition from "../camera/functions/updateCameraPosition";
|
||||
|
||||
export default function Controls() {
|
||||
const controlsRef = useRef<CameraControls>(null);
|
||||
@@ -117,7 +117,6 @@ export default function Controls() {
|
||||
stopInterval();
|
||||
};
|
||||
}, [toggleView, state, socket]);
|
||||
useCameraShortcuts(controlsRef);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -140,6 +139,8 @@ export default function Controls() {
|
||||
|
||||
<CamMode />
|
||||
|
||||
<CameraShortcutsControls/>
|
||||
|
||||
</CameraControls>
|
||||
|
||||
<SelectionControls3D />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
@@ -12,6 +12,7 @@ import { useProductContext } from "../../../../simulation/products/productContex
|
||||
import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
@@ -20,6 +21,8 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
@@ -162,7 +165,7 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
}
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
if (!toggleView && toolMode === 'cursor') {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
@@ -178,7 +181,14 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||
}, [camera, controls, scene, toggleView, toolMode, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
resetToInitialPositions();
|
||||
setMovedObjects([]);
|
||||
}
|
||||
}, [activeModule, toolMode, toggleView]);
|
||||
|
||||
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
||||
const pointPosition = new THREE.Vector3().copy(point.position);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -11,6 +11,7 @@ import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
@@ -18,6 +19,8 @@ function RotateControls3D() {
|
||||
const { camera, gl, scene, pointer, raycaster } = useThree();
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
@@ -34,6 +37,7 @@ function RotateControls3D() {
|
||||
const [initialRotations, setInitialRotations] = useState<Record<string, THREE.Euler>>({});
|
||||
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
|
||||
const [isRotating, setIsRotating] = useState(false);
|
||||
const [isIndividualRotating, setIsIndividualRotating] = useState(false);
|
||||
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
|
||||
const rotationCenter = useRef<THREE.Vector3 | null>(null);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
@@ -117,6 +121,12 @@ function RotateControls3D() {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key.toLowerCase() === 'i') {
|
||||
if (rotatedObjects.length > 0) {
|
||||
setIsIndividualRotating(!isIndividualRotating);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key.toLowerCase() === "escape") {
|
||||
event.preventDefault();
|
||||
resetToInitialRotations();
|
||||
@@ -137,7 +147,7 @@ function RotateControls3D() {
|
||||
prevPointerPosition.current = currentPointer.clone();
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
if (!toggleView && toolMode === 'cursor') {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
@@ -152,7 +162,14 @@ function RotateControls3D() {
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]);
|
||||
}, [camera, scene, toggleView, toolMode, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations, isIndividualRotating]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
resetToInitialRotations();
|
||||
setRotatedObjects([]);
|
||||
}
|
||||
}, [activeModule, toolMode, toggleView]);
|
||||
|
||||
const resetToInitialRotations = useCallback(() => {
|
||||
setTimeout(() => {
|
||||
@@ -204,10 +221,12 @@ function RotateControls3D() {
|
||||
wasShiftHeldRef
|
||||
});
|
||||
|
||||
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
relativePosition.applyMatrix4(rotationMatrix);
|
||||
obj.position.copy(center).add(relativePosition);
|
||||
if (!isIndividualRotating) {
|
||||
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
relativePosition.applyMatrix4(rotationMatrix);
|
||||
obj.position.copy(center).add(relativePosition);
|
||||
}
|
||||
|
||||
const rotationQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), angleDelta);
|
||||
obj.quaternion.multiply(rotationQuat);
|
||||
@@ -384,8 +403,9 @@ function RotateControls3D() {
|
||||
}
|
||||
|
||||
setIsRotating(false);
|
||||
setIsIndividualRotating(false);
|
||||
clearSelection();
|
||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
|
||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId, initialPositions, initialRotations]);
|
||||
|
||||
const clearSelection = () => {
|
||||
setPastedObjects([]);
|
||||
@@ -393,6 +413,7 @@ function RotateControls3D() {
|
||||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
setIsIndividualRotating(false);
|
||||
};
|
||||
|
||||
return null;
|
||||
|
||||
@@ -16,22 +16,23 @@ import DuplicationControls3D from "./duplicationControls3D";
|
||||
import CopyPasteControls3D from "./copyPasteControls3D";
|
||||
import MoveControls3D from "./moveControls3D";
|
||||
import RotateControls3D from "./rotateControls3D";
|
||||
import TransformControls3D from "./transformControls3D";
|
||||
|
||||
// import { deleteFloorItem } from '../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
|
||||
|
||||
const SelectionControls3D: React.FC = () => {
|
||||
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const boundingBoxRef = useRef<THREE.Mesh>();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
|
||||
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
@@ -202,7 +203,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
rightClickMoved.current = false;
|
||||
};
|
||||
|
||||
if (!toggleView && activeModule === "builder" && toolMode === 'cursor') {
|
||||
if (!toggleView && activeModule === "builder" && (toolMode === 'cursor' || toolMode === 'Move-Asset' || toolMode === 'Rotate-Asset')) {
|
||||
helper.enabled = true;
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
@@ -227,7 +228,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
if (activeModule !== "builder" || (toolMode !== 'cursor' && toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset') || toggleView) {
|
||||
clearSelection();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -381,6 +382,8 @@ const SelectionControls3D: React.FC = () => {
|
||||
|
||||
<CopyPasteControls3D />
|
||||
|
||||
<TransformControls3D />
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
import * as THREE from 'three';
|
||||
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { TransformControls } from '@react-three/drei';
|
||||
import { useSelectedAssets, useSocketStore, useToolMode } from '../../../../../store/builder/store';
|
||||
import { useProductContext } from '../../../../simulation/products/productContext';
|
||||
import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||
import { useSceneContext } from '../../../sceneContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { getUserData } from '../../../../../functions/getUserData';
|
||||
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
function TransformControls3D() {
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { toolMode } = useToolMode();
|
||||
const { camera, scene, gl } = useThree();
|
||||
const transformControlsRef = useRef<any>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
|
||||
const { socket } = useSocketStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D, subscribeUndoRedo } = undoRedo3DStore();
|
||||
const { updateAsset, getAssetById } = assetStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const pivotPointRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||
const initialPositionsRef = useRef<Map<string, THREE.Vector3>>(new Map());
|
||||
const initialRotationsRef = useRef<Map<string, THREE.Euler>>(new Map());
|
||||
const initialPivotPositionRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||
const initialQuaternionsRef = useRef<Map<string, THREE.Quaternion>>(new Map());
|
||||
const tempObjectRef = useRef<THREE.Object3D>(new THREE.Object3D());
|
||||
|
||||
const updateBackend = useCallback((
|
||||
productName: string,
|
||||
productUuid: string,
|
||||
projectId: string,
|
||||
eventData: any
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productUuid: productUuid,
|
||||
projectId: projectId,
|
||||
eventDatas: eventData,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
});
|
||||
}, [selectedVersion]);
|
||||
|
||||
const recalcGizmo = useCallback(() => {
|
||||
if (selectedAssets.length === 0) return;
|
||||
|
||||
const bbox = new THREE.Box3();
|
||||
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||
bbox.getCenter(pivotPointRef.current);
|
||||
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||
|
||||
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||
tempObjectRef.current.scale.set(1, 1, 1);
|
||||
|
||||
initialPositionsRef.current.clear();
|
||||
initialRotationsRef.current.clear();
|
||||
initialQuaternionsRef.current.clear();
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||
});
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = subscribeUndoRedo(() => {
|
||||
setTimeout(() => {
|
||||
recalcGizmo();
|
||||
}, 10);
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [subscribeUndoRedo, recalcGizmo]);
|
||||
|
||||
const handleTransformationComplete = useCallback(() => {
|
||||
if (selectedAssets.length === 0) return;
|
||||
|
||||
const updatedAssets = [...selectedAssets];
|
||||
setSelectedAssets(updatedAssets);
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
const asset = getAssetById(obj.uuid);
|
||||
if (!asset) return;
|
||||
|
||||
updateAsset(asset.modelUuid, {
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
|
||||
if (asset.eventData) {
|
||||
const eventData = eventStore.getState().getEventByModelUuid(asset.modelUuid);
|
||||
const productData = productStore.getState().getEventByModelUuid(
|
||||
selectedProduct.productUuid,
|
||||
asset.modelUuid
|
||||
);
|
||||
|
||||
if (eventData) {
|
||||
eventStore.getState().updateEvent(asset.modelUuid, {
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
}
|
||||
|
||||
if (productData) {
|
||||
const event = productStore
|
||||
.getState()
|
||||
.updateEvent(
|
||||
selectedProduct.productUuid,
|
||||
asset.modelUuid,
|
||||
{
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//REST
|
||||
|
||||
// setAssetsApi(
|
||||
// organization,
|
||||
// asset.modelUuid,
|
||||
// asset.modelName,
|
||||
// [obj.position.x, 0, obj.position.z],
|
||||
// { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
// asset.assetId,
|
||||
// false,
|
||||
// true,
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
modelName: asset.modelName,
|
||||
assetId: asset.assetId,
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
projectId
|
||||
};
|
||||
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
});
|
||||
}, [selectedAssets, setSelectedAssets, getAssetById, updateAsset, eventStore, productStore, selectedProduct, updateBackend, projectId, organization, socket, selectedVersion, userId, push3D]);
|
||||
|
||||
useEffect(() => {
|
||||
const temp = tempObjectRef.current;
|
||||
scene.add(temp);
|
||||
return () => {
|
||||
scene.remove(temp);
|
||||
};
|
||||
}, [scene]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 0) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const bbox = new THREE.Box3();
|
||||
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||
bbox.getCenter(pivotPointRef.current);
|
||||
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||
|
||||
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||
tempObjectRef.current.scale.set(1, 1, 1);
|
||||
|
||||
initialPositionsRef.current.clear();
|
||||
initialRotationsRef.current.clear();
|
||||
initialQuaternionsRef.current.clear();
|
||||
selectedAssets.forEach(obj => {
|
||||
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||
});
|
||||
|
||||
setVisible(true);
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const controls = transformControlsRef.current;
|
||||
if (!controls || !visible) return;
|
||||
|
||||
controls.attach(tempObjectRef.current);
|
||||
controls.setMode(toolMode === 'Move-Asset' ? 'translate' : 'rotate');
|
||||
|
||||
const onObjectChange = () => {
|
||||
const temp = tempObjectRef.current;
|
||||
if (!temp) return;
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
const initialPos = initialPositionsRef.current.get(obj.uuid);
|
||||
const initialQuat = initialQuaternionsRef.current.get(obj.uuid);
|
||||
if (!initialPos || !initialQuat) return;
|
||||
|
||||
if (controls.mode === 'translate') {
|
||||
const delta = new THREE.Vector3().copy(temp.position).sub(initialPivotPositionRef.current);
|
||||
obj.position.copy(initialPos).add(delta);
|
||||
} else if (controls.mode === 'rotate') {
|
||||
const deltaQuat = temp.quaternion.clone();
|
||||
const relPos = initialPos.clone().sub(initialPivotPositionRef.current);
|
||||
relPos.applyQuaternion(deltaQuat);
|
||||
obj.position.copy(initialPivotPositionRef.current).add(relPos);
|
||||
obj.quaternion.copy(deltaQuat).multiply(initialQuat);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
const assetsToUpdate: AssetData[] = [];
|
||||
const undoActions: UndoRedo3DAction[] = [];
|
||||
|
||||
selectedAssets.forEach((obj) => {
|
||||
const asset = getAssetById(obj.uuid);
|
||||
if (!asset) return;
|
||||
|
||||
const initialPos = initialPositionsRef.current.get(obj.uuid);
|
||||
const initialRot = initialRotationsRef.current.get(obj.uuid);
|
||||
if (!initialPos || !initialRot) return;
|
||||
|
||||
const assetData: Asset = {
|
||||
...asset,
|
||||
position: [initialPos.x, initialPos.y, initialPos.z],
|
||||
rotation: [initialRot.x, initialRot.y, initialRot.z],
|
||||
};
|
||||
|
||||
const newData: Asset = {
|
||||
...asset,
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
};
|
||||
|
||||
assetsToUpdate.push({
|
||||
type: "Asset",
|
||||
assetData,
|
||||
newData,
|
||||
timeStap: new Date().toISOString(),
|
||||
});
|
||||
|
||||
updateAsset(asset.modelUuid, {
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
});
|
||||
|
||||
if (assetsToUpdate.length > 0) {
|
||||
if (assetsToUpdate.length === 1) {
|
||||
undoActions.push({
|
||||
module: "builder",
|
||||
actionType: "Asset-Update",
|
||||
asset: assetsToUpdate[0],
|
||||
});
|
||||
} else {
|
||||
undoActions.push({
|
||||
module: "builder",
|
||||
actionType: "Assets-Update",
|
||||
assets: assetsToUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
push3D({
|
||||
type: "Scene",
|
||||
actions: undoActions,
|
||||
});
|
||||
}
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||
});
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
const bbox = new THREE.Box3();
|
||||
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||
bbox.getCenter(pivotPointRef.current);
|
||||
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||
|
||||
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||
}
|
||||
|
||||
recalcGizmo();
|
||||
handleTransformationComplete();
|
||||
};
|
||||
|
||||
controls.addEventListener('objectChange', onObjectChange);
|
||||
controls.addEventListener('mouseUp', onMouseUp);
|
||||
|
||||
return () => {
|
||||
controls.removeEventListener('objectChange', onObjectChange);
|
||||
controls.removeEventListener('mouseUp', onMouseUp);
|
||||
controls.detach();
|
||||
};
|
||||
}, [selectedAssets, toolMode, visible, handleTransformationComplete]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (keyCombination !== keyEvent) {
|
||||
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
} else {
|
||||
setKeyEvent("");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (keyCombination === "") {
|
||||
setKeyEvent("");
|
||||
} else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
}
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
canvasElement.addEventListener('keydown', handleKeyDown);
|
||||
canvasElement.addEventListener('keyup', onKeyUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('keydown', handleKeyDown);
|
||||
canvasElement.removeEventListener('keyup', onKeyUp);
|
||||
}
|
||||
}, [gl, visible, keyEvent]);
|
||||
|
||||
if (!visible || (toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TransformControls
|
||||
space="world"
|
||||
translationSnap={keyEvent === 'Ctrl' ? 0.5 : keyEvent === 'Ctrl+Shift' ? 0.1 : 0}
|
||||
rotationSnap={keyEvent === 'Ctrl' ? THREE.MathUtils.degToRad(15) : keyEvent === 'Ctrl+Shift' ? THREE.MathUtils.degToRad(5) : 0}
|
||||
ref={transformControlsRef}
|
||||
camera={camera}
|
||||
showX
|
||||
showY
|
||||
showZ
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default TransformControls3D;
|
||||
@@ -1,17 +1,17 @@
|
||||
import { TransformControls } from "@react-three/drei";
|
||||
import * as THREE from "three";
|
||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { TransformControls } from "@react-three/drei";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useProductContext } from "../../../simulation/products/productContext";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../sceneContext";
|
||||
import { useVersionContext } from "../../../builder/version/versionContext";
|
||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
|
||||
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
|
||||
export default function TransformControl() {
|
||||
const state = useThree();
|
||||
|
||||
@@ -20,7 +20,7 @@ function UndoRedo3DControls() {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(undoStack, redoStack);
|
||||
console.log(undoStack, redoStack);
|
||||
}, [undoStack, redoStack]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { BlendFunction } from "postprocessing";
|
||||
import {
|
||||
useDeletableFloorItem,
|
||||
useSelectedWallItem,
|
||||
useSelectedFloorItem,
|
||||
} from "../../../store/builder/store";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore";
|
||||
import { useEffect } from "react";
|
||||
import { BlendFunction } from "postprocessing";
|
||||
import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
|
||||
import { useDeletableFloorItem, useSelectedWallItem, useSelectedFloorItem, } from "../../../store/builder/store";
|
||||
import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore";
|
||||
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
||||
export default function PostProcessing() {
|
||||
const { scene } = useThree();
|
||||
const { selectedPoints } = useSelectedPoints();
|
||||
const { deletableFloorItem } = useDeletableFloorItem();
|
||||
const { selectedWallItem } = useSelectedWallItem();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { deletableEventSphere } = useDeletableEventSphere();
|
||||
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset } = useBuilderStore();
|
||||
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset, deletableDecal } = useBuilderStore();
|
||||
|
||||
function flattenChildren(children: any[]) {
|
||||
const allChildren: any[] = [];
|
||||
@@ -68,6 +62,10 @@ export default function PostProcessing() {
|
||||
// console.log('selectedPoints: ', selectedPoints);
|
||||
}, [selectedPoints])
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('deletableDecal: ', deletableDecal);
|
||||
}, [deletableDecal])
|
||||
|
||||
return (
|
||||
<EffectComposer autoClear={false}>
|
||||
<N8AO
|
||||
@@ -122,7 +120,7 @@ export default function PostProcessing() {
|
||||
)}
|
||||
{selectedAisle && (
|
||||
<Outline
|
||||
selection={flattenChildren(selectedAisle.children)}
|
||||
selection={flattenChildren(selectedAisle.aisleMesh?.children || [])}
|
||||
selectionLayer={10}
|
||||
width={2000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
@@ -167,7 +165,7 @@ export default function PostProcessing() {
|
||||
)}
|
||||
{selectedDecal && (
|
||||
<Outline
|
||||
selection={selectedDecal}
|
||||
selection={selectedDecal.decalMesh || undefined}
|
||||
selectionLayer={10}
|
||||
width={2000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
@@ -180,6 +178,21 @@ export default function PostProcessing() {
|
||||
xRay={true}
|
||||
/>
|
||||
)}
|
||||
{deletableDecal && (
|
||||
<Outline
|
||||
selection={deletableDecal}
|
||||
selectionLayer={10}
|
||||
width={3000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
edgeStrength={5}
|
||||
resolutionScale={2}
|
||||
pulseSpeed={0}
|
||||
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
|
||||
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
|
||||
blur={true}
|
||||
xRay={true}
|
||||
/>
|
||||
)}
|
||||
{deletableFloorItem && (
|
||||
<Outline
|
||||
selection={flattenChildren(deletableFloorItem.children)}
|
||||
|
||||
Reference in New Issue
Block a user