2025-06-25 17:20:35 +05:30
|
|
|
import * as THREE from 'three';
|
2025-08-26 16:38:29 +05:30
|
|
|
import { CameraControls, Decal } from '@react-three/drei'
|
|
|
|
|
import { useLoader, useThree } from '@react-three/fiber';
|
2025-08-26 17:25:04 +05:30
|
|
|
import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
|
|
|
|
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
2025-06-25 17:20:35 +05:30
|
|
|
|
2025-08-26 17:25:04 +05:30
|
|
|
import defaultMaterial from '../../../../assets/textures/floor/wall-tex.png';
|
|
|
|
|
import useModuleStore from '../../../../store/useModuleStore';
|
|
|
|
|
import { useSceneContext } from '../../../scene/sceneContext';
|
2025-08-26 16:38:29 +05:30
|
|
|
import { useEffect, useRef } from 'react';
|
2025-08-26 17:25:04 +05:30
|
|
|
import { getUserData } from '../../../../functions/getUserData';
|
|
|
|
|
import { useVersionContext } from '../../version/versionContext';
|
2025-08-26 14:43:38 +05:30
|
|
|
import { useParams } from 'react-router-dom';
|
2025-06-25 17:20:35 +05:30
|
|
|
|
2025-08-26 17:25:04 +05:30
|
|
|
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
|
|
|
|
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
2025-08-26 14:43:38 +05:30
|
|
|
|
|
|
|
|
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();
|
2025-08-26 16:38:29 +05:30
|
|
|
const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById } = wallStore();
|
|
|
|
|
const { removeDecal: removeDecalInFloor } = floorStore();
|
2025-08-26 14:43:38 +05:30
|
|
|
const { toolMode } = useToolMode();
|
|
|
|
|
const { toggleView } = useToggleView();
|
2025-06-25 17:20:35 +05:30
|
|
|
const { activeModule } = useModuleStore();
|
2025-08-26 14:43:38 +05:30
|
|
|
const { userId, organization } = getUserData();
|
|
|
|
|
const { selectedVersionStore } = useVersionContext();
|
|
|
|
|
const { selectedVersion } = selectedVersionStore();
|
|
|
|
|
const { projectId } = useParams();
|
|
|
|
|
const { socket } = useSocketStore();
|
2025-06-25 17:20:35 +05:30
|
|
|
const material = useLoader(THREE.TextureLoader, defaultMaterial);
|
|
|
|
|
|
2025-08-26 16:38:29 +05:30
|
|
|
const { raycaster, pointer, camera, scene, gl, controls } = useThree();
|
|
|
|
|
const isDraggingRef = useRef(false);
|
|
|
|
|
const dragOffsetRef = useRef<THREE.Vector3 | null>(null);
|
|
|
|
|
|
2025-08-26 14:43:38 +05:30
|
|
|
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]);
|
|
|
|
|
|
2025-08-26 16:38:29 +05:30
|
|
|
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]);
|
|
|
|
|
|
2025-08-26 14:43:38 +05:30
|
|
|
const deleteDecal = (decalUuid: string, parent: Wall | Floor) => {
|
|
|
|
|
if ('wallUuid' in parent) {
|
2025-08-26 16:38:29 +05:30
|
|
|
const updatedWall = removeDecalInWall(decalUuid);
|
2025-08-26 14:43:38 +05:30
|
|
|
|
|
|
|
|
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) {
|
2025-08-26 16:38:29 +05:30
|
|
|
const updatedFloor = removeDecalInFloor(decalUuid);
|
2025-08-26 14:43:38 +05:30
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:20:35 +05:30
|
|
|
return (
|
|
|
|
|
<Decal
|
|
|
|
|
// debug
|
|
|
|
|
visible={visible}
|
2025-06-30 16:21:54 +05:30
|
|
|
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
2025-08-26 15:50:02 +05:30
|
|
|
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
|
2025-06-25 17:20:35 +05:30
|
|
|
scale={[decal.decalScale, decal.decalScale, 0.01]}
|
|
|
|
|
userData={decal}
|
2025-08-26 16:38:29 +05:30
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
|
2025-06-27 16:54:38 +05:30
|
|
|
onClick={(e) => {
|
2025-08-26 14:43:38 +05:30
|
|
|
if (visible && !toggleView && activeModule === 'builder') {
|
|
|
|
|
if (e.object.userData.decalUuid) {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
if (toolMode === 'cursor') {
|
2025-08-26 15:50:02 +05:30
|
|
|
setSelectedDecal({ decalMesh: e.object, decalData: decal });
|
2025-08-26 14:43:38 +05:30
|
|
|
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') {
|
2025-06-25 17:20:35 +05:30
|
|
|
if (e.object.userData.decalUuid) {
|
2025-06-27 15:58:34 +05:30
|
|
|
e.stopPropagation();
|
2025-08-26 14:43:38 +05:30
|
|
|
if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) {
|
|
|
|
|
setDeletableDecal(null);
|
|
|
|
|
}
|
2025-06-25 17:20:35 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
onPointerMissed={() => {
|
2025-08-26 15:50:02 +05:30
|
|
|
if (selectedDecal && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
|
2025-06-25 17:20:35 +05:30
|
|
|
setSelectedDecal(null);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<meshBasicMaterial
|
|
|
|
|
map={material}
|
|
|
|
|
side={THREE.DoubleSide}
|
|
|
|
|
polygonOffset
|
|
|
|
|
polygonOffsetFactor={-1}
|
2025-08-26 15:50:02 +05:30
|
|
|
transparent
|
|
|
|
|
opacity={decal.decalOpacity}
|
2025-06-25 17:20:35 +05:30
|
|
|
/>
|
|
|
|
|
</Decal>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default DecalInstance
|