Merge remote-tracking branch 'origin/main-dev' into main-demo
This commit is contained in:
@@ -1,22 +1,129 @@
|
||||
import { MathUtils } from 'three';
|
||||
import { useEffect } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useDroppedDecal } from '../../../../store/builder/store';
|
||||
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) => {
|
||||
console.log('event: ', event);
|
||||
console.log('droppedDecal: ', droppedDecal);
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
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);
|
||||
|
||||
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],
|
||||
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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,40 +1,38 @@
|
||||
import * as THREE from 'three';
|
||||
import { CameraControls, Decal } from '@react-three/drei'
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
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/textures/floor/wall-tex.png';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
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 { setSelectedWall, setSelectedFloor, selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById } = wallStore();
|
||||
const { removeDecal: removeDecalInFloor } = floorStore();
|
||||
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 { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
const { raycaster, pointer, camera, scene, gl, controls } = useThree();
|
||||
const isDraggingRef = useRef(false);
|
||||
const dragOffsetRef = useRef<THREE.Vector3 | null>(null);
|
||||
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 [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const logDecalStatus = (decalId: string, status: string) => {
|
||||
// console.log(decalId, status);
|
||||
}
|
||||
|
||||
const loadDefaultTexture = () => {
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
@@ -43,24 +41,22 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
|
||||
(fallbackTex) => {
|
||||
fallbackTex.name = "default-decal";
|
||||
setTexture(fallbackTex);
|
||||
setIsLoading(false);
|
||||
logDecalStatus(decal.decalId, 'default-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
console.error("Error loading default decal texture:", error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const loadDecalTexture = async (decalId: string) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const cachedTexture = THREE.Cache.get(decalId);
|
||||
if (cachedTexture) {
|
||||
setTexture(cachedTexture);
|
||||
setIsLoading(false);
|
||||
logDecalStatus(decalId, 'cache-loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,7 +71,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
|
||||
tex.name = decalId;
|
||||
THREE.Cache.add(decalId, tex);
|
||||
setTexture(tex);
|
||||
setIsLoading(false);
|
||||
logDecalStatus(decalId, 'indexedDB-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
@@ -95,8 +91,8 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
|
||||
};
|
||||
|
||||
const loadFromBackend = (decalId: string) => {
|
||||
console.log('decalId: ', decalId);
|
||||
const textureUrl = `${process.env.REACT_APP_SERVER_MARKETPLACE_URL}/api/v2/DecalFile/${decalId}`;
|
||||
|
||||
const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`;
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
textureLoader.load(
|
||||
@@ -105,7 +101,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
|
||||
tex.name = decalId;
|
||||
THREE.Cache.add(decalId, tex);
|
||||
setTexture(tex);
|
||||
setIsLoading(false);
|
||||
logDecalStatus(decalId, 'backend-loaded');
|
||||
|
||||
try {
|
||||
const response = await fetch(textureUrl);
|
||||
@@ -145,183 +141,20 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const handlePointerMove = (e: PointerEvent) => {
|
||||
if (!isDraggingRef.current || !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' in parent && i.object.userData.wallUuid === parent.wallUuid);
|
||||
|
||||
if (wallIntersect) {
|
||||
const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());
|
||||
|
||||
let offset = dragOffsetRef.current || new THREE.Vector3(0, 0, 0);
|
||||
|
||||
updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerUp = (e: PointerEvent) => {
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
if (isDraggingRef.current) {
|
||||
isDraggingRef.current = false;
|
||||
dragOffsetRef.current = 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)
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
if (activeModule === 'builder' && !toggleView) {
|
||||
canvasElement.addEventListener('pointermove', handlePointerMove);
|
||||
canvasElement.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('pointermove', handlePointerMove);
|
||||
canvasElement.removeEventListener('pointerup', handlePointerUp);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, selectedDecal, decal, parent, activeModule, toggleView, projectId, selectedVersion, userId, organization, socket]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.decalScale, decal.decalScale, 0.01]}
|
||||
scale={[(decal.decalType.type === 'Floor' || zPosition < 0) ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
|
||||
userData={decal}
|
||||
onPointerDown={(e) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid && toolMode === 'cursor') {
|
||||
e.stopPropagation();
|
||||
isDraggingRef.current = true;
|
||||
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());
|
||||
dragOffsetRef.current = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
onClick={(e) => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerEnter={(e) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === '3D-Delete') {
|
||||
setDeletableDecal(e.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerLeave={(e) => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (selectedDecal && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}}
|
||||
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}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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";
|
||||
@@ -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,20 +141,36 @@ 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 (!toggleView && activeModule === "builder") {
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import material1 from '../../../../../assets/textures/floor/factory wall texture
|
||||
|
||||
function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const { wallStore, wallAssetStore } = useSceneContext();
|
||||
const { walls, addDecal } = wallStore();
|
||||
const { walls } = wallStore();
|
||||
const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore();
|
||||
const assets = getWallAssetsByWall(wall.wallUuid);
|
||||
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
@@ -118,6 +118,7 @@ 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}
|
||||
@@ -159,19 +160,6 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
e.stopPropagation();
|
||||
setSelectedWall(e.object);
|
||||
setSelectedDecal(null);
|
||||
|
||||
if (wall.decals.length > 0) return;
|
||||
const decal: Decal = {
|
||||
decalUuid: THREE.MathUtils.generateUUID(),
|
||||
decalName: 'Decal',
|
||||
decalId: "68abe2f771863f0888b0d35d",
|
||||
decalPosition: [0, 0, wall.wallThickness / 2 + 0.001],
|
||||
decalRotation: 0,
|
||||
decalOpacity: 1,
|
||||
decalScale: 1,
|
||||
decalType: { type: 'Wall', wallUuid: wall.wallUuid }
|
||||
}
|
||||
addDecal(wall.wallUuid, decal);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -165,7 +165,7 @@ export default function PostProcessing() {
|
||||
)}
|
||||
{selectedDecal && (
|
||||
<Outline
|
||||
selection={selectedDecal.decalMesh}
|
||||
selection={selectedDecal.decalMesh || undefined}
|
||||
selectionLayer={10}
|
||||
width={2000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Object3D } from 'three';
|
||||
import { Object3D, Vector3 } from 'three';
|
||||
import { create } from 'zustand';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
|
||||
@@ -38,8 +38,13 @@ interface BuilderState {
|
||||
zoneColor: string;
|
||||
|
||||
// Decal Settings
|
||||
selectedDecal: { decalMesh: Object3D, decalData: Decal } | null;
|
||||
selectedDecal: { decalMesh: Object3D | null, decalData: Decal } | null;
|
||||
deletableDecal: Object3D | null;
|
||||
decalDragState: {
|
||||
isDragging: boolean,
|
||||
draggingDecalUuid: string | null,
|
||||
dragOffset: Vector3 | null,
|
||||
},
|
||||
|
||||
// Aisle General
|
||||
selectedAisle: Object3D | null;
|
||||
@@ -87,8 +92,9 @@ interface BuilderState {
|
||||
setZoneColor: (color: string) => void;
|
||||
|
||||
// Setters - Decal
|
||||
setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => void;
|
||||
setSelectedDecal: (decal: { decalMesh: Object3D | null, decalData: Decal } | null) => void;
|
||||
setDeletableDecal: (decal: Object3D | null) => void;
|
||||
setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => void;
|
||||
|
||||
// Setters - Aisle General
|
||||
setSelectedAisle: (aisle: Object3D | null) => void;
|
||||
@@ -143,6 +149,11 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
|
||||
selectedDecal: null,
|
||||
deletableDecal: null,
|
||||
decalDragState: {
|
||||
isDragging: false,
|
||||
draggingDecalUuid: null,
|
||||
dragOffset: null,
|
||||
},
|
||||
|
||||
selectedAisle: null,
|
||||
aisleType: 'solid-aisle',
|
||||
@@ -290,7 +301,7 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
|
||||
// === Setters: Decal ===
|
||||
|
||||
setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => {
|
||||
setSelectedDecal: (decal: { decalMesh: Object3D | null, decalData: Decal } | null) => {
|
||||
set((state) => {
|
||||
state.selectedDecal = decal;
|
||||
})
|
||||
@@ -302,6 +313,16 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
})
|
||||
},
|
||||
|
||||
setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => {
|
||||
set((state) => {
|
||||
state.decalDragState = {
|
||||
isDragging,
|
||||
draggingDecalUuid,
|
||||
dragOffset,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// === Setters: Aisle General ===
|
||||
|
||||
setSelectedAisle: (aisle: Object3D | null) => {
|
||||
|
||||
@@ -19,8 +19,8 @@ interface FloorStore {
|
||||
setBevelStrength: (uuid: string, strength: number) => void;
|
||||
setDepth: (uuid: string, depth: number) => void;
|
||||
setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void;
|
||||
addDecal: (floors: string, decal: Decal) => void;
|
||||
updateDecal: (decalUuid: string, decal: Decal) => Floor | undefined;
|
||||
addDecal: (floors: string, decal: Decal) => Decal | undefined;
|
||||
updateDecal: (decalUuid: string, decal: Partial<Decal>) => Floor | undefined;
|
||||
removeDecal: (decalUuid: string) => Floor | undefined;
|
||||
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
|
||||
updateDecalRotation: (decalUuid: string, rotation: number) => void;
|
||||
@@ -196,12 +196,17 @@ export const createFloorStore = () => {
|
||||
}
|
||||
}),
|
||||
|
||||
addDecal: (floorUuid, decal) => set(state => {
|
||||
const floor = state.floors.find(f => f.floorUuid === floorUuid);
|
||||
if (floor) {
|
||||
floor.decals.push(decal);
|
||||
}
|
||||
}),
|
||||
addDecal: (floorUuid, decal) => {
|
||||
let addedDecal: Decal | undefined;
|
||||
set(state => {
|
||||
const floor = state.floors.find(f => f.floorUuid === floorUuid);
|
||||
if (floor) {
|
||||
floor.decals.push(decal);
|
||||
addedDecal = JSON.parse(JSON.stringify(decal));
|
||||
}
|
||||
})
|
||||
return addedDecal;
|
||||
},
|
||||
|
||||
updateDecal: (decalUuid, updatedDecal) => {
|
||||
let affectedFloor: Floor | undefined;
|
||||
@@ -209,12 +214,12 @@ export const createFloorStore = () => {
|
||||
for (const floor of state.floors) {
|
||||
const index = floor.decals.findIndex(d => d.decalUuid === decalUuid);
|
||||
if (index !== -1) {
|
||||
floor.decals[index] = updatedDecal;
|
||||
Object.assign(floor.decals[index], updatedDecal);
|
||||
affectedFloor = JSON.parse(JSON.stringify(floor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
return affectedFloor;
|
||||
},
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ interface WallStore {
|
||||
removeWall: (uuid: string) => void;
|
||||
clearWalls: () => void;
|
||||
removeWallByPoints: (Points: [Point, Point]) => Wall | undefined;
|
||||
addDecal: (wallUuid: string, decal: Decal) => void;
|
||||
updateDecal: (decalUuid: string, decal: Decal) => Wall | undefined;
|
||||
addDecal: (wallUuid: string, decal: Decal) => Decal | undefined;
|
||||
updateDecal: (decalUuid: string, decal: Partial<Decal>) => Wall | undefined;
|
||||
removeDecal: (decalUuid: string) => Wall | undefined;
|
||||
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => Wall | undefined;
|
||||
updateDecalRotation: (decalUuid: string, rotation: number) => void;
|
||||
@@ -83,12 +83,17 @@ export const createWallStore = () => {
|
||||
return removedWall;
|
||||
},
|
||||
|
||||
addDecal: (wallUuid, decal) => set((state) => {
|
||||
const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid);
|
||||
if (wallToUpdate) {
|
||||
wallToUpdate.decals.push(decal);
|
||||
}
|
||||
}),
|
||||
addDecal: (wallUuid, decal) => {
|
||||
let addedDecal: Decal | undefined;
|
||||
set((state) => {
|
||||
const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid);
|
||||
if (wallToUpdate) {
|
||||
wallToUpdate.decals.push(decal);
|
||||
addedDecal = JSON.parse(JSON.stringify(decal));
|
||||
}
|
||||
})
|
||||
return addedDecal;
|
||||
},
|
||||
|
||||
updateDecal: (decalUuid, decal) => {
|
||||
let affectedWall: Wall | undefined;
|
||||
|
||||
Reference in New Issue
Block a user