From 8ff609b85c4bd634d8f824c5ab7202b033343575 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 28 Aug 2025 15:50:45 +0530 Subject: [PATCH] added decal dropping to scene, decal movement from one wall to another wall, one floor to another floor, one wall to floor and one floor to wall --- .../Decal/decalCreator/decalCreator.tsx | 117 ++++++- .../Decal/decalInstance/decalInstance.tsx | 229 ++----------- .../eventHandler/useDecalEventHandlers.ts | 301 ++++++++++++++++++ .../builder/asset/models/model/model.tsx | 7 + .../Instances/Instance/floorInstance.tsx | 117 +++---- .../builder/wall/Instances/instance/wall.tsx | 16 +- .../scene/postProcessing/postProcessing.tsx | 2 +- app/src/store/builder/useBuilderStore.ts | 29 +- app/src/store/builder/useFloorStore.ts | 25 +- app/src/store/builder/useWallStore.ts | 21 +- 10 files changed, 568 insertions(+), 296 deletions(-) create mode 100644 app/src/modules/builder/Decal/eventHandler/useDecalEventHandlers.ts diff --git a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx index 4335afc..221a2bf 100644 --- a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx +++ b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx @@ -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) + } } }; diff --git a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx index 6be22ee..8d0540a 100644 --- a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx @@ -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(null); + const decalRef = useRef(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(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 ( { - 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()} > { + 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) => { + 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) => { + 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) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid) { + e.stopPropagation(); + if (toolMode === '3D-Delete') { + setDeletableDecal(e.object); + } + } + } + }; + + const handlePointerLeave = (e: ThreeEvent) => { + 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 + }; +} \ No newline at end of file diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 2f5f77c..ca1b406 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -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( diff --git a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx index f89b38d..4ea5b43 100644 --- a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx +++ b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx @@ -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 ( { if (!toggleView && activeModule === "builder") { @@ -173,41 +187,32 @@ function FloorInstance({ floor }: { floor: Floor }) { } }} > - - - - + + + + {floor.decals.map((decal) => ( + + ))} ); } diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 71ccdd4..67d5b8a 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -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)) ? 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); } } }} diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 555c7a3..24f7428 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -165,7 +165,7 @@ export default function PostProcessing() { )} {selectedDecal && ( 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()( selectedDecal: null, deletableDecal: null, + decalDragState: { + isDragging: false, + draggingDecalUuid: null, + dragOffset: null, + }, selectedAisle: null, aisleType: 'solid-aisle', @@ -290,7 +301,7 @@ export const useBuilderStore = create()( // === 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()( }) }, + setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => { + set((state) => { + state.decalDragState = { + isDragging, + draggingDecalUuid, + dragOffset, + } + }) + }, + // === Setters: Aisle General === setSelectedAisle: (aisle: Object3D | null) => { diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index c7b9545..e875c78 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -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) => 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; }, diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index c7c07e9..705e611 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -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) => 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;