Files
Dwinzo_Demo/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx

241 lines
9.9 KiB
TypeScript
Raw Normal View History

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-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';
import { useParams } from 'react-router-dom';
2025-08-26 17:25:04 +05:30
// 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();
2025-08-26 16:38:29 +05:30
const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById } = wallStore();
const { removeDecal: removeDecalInFloor } = floorStore();
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 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);
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]);
const deleteDecal = (decalUuid: string, parent: Wall | Floor) => {
if ('wallUuid' in parent) {
2025-08-26 16:38:29 +05:30
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) {
2025-08-26 16:38:29 +05:30
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}
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
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);
}
}
}}
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);
}
}}
>
<meshBasicMaterial
map={material}
side={THREE.DoubleSide}
polygonOffset
polygonOffsetFactor={-1}
transparent
opacity={decal.decalOpacity}
/>
</Decal>
)
}
export default DecalInstance