2025-06-25 17:20:35 +05:30
|
|
|
import * as THREE from 'three';
|
2025-08-28 15:50:45 +05:30
|
|
|
import { Decal } from '@react-three/drei'
|
|
|
|
|
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
2025-08-26 17:25:04 +05:30
|
|
|
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
2025-08-26 17:39:45 +05:30
|
|
|
import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils';
|
2025-06-25 17:20:35 +05:30
|
|
|
|
2025-08-28 16:32:45 +05:30
|
|
|
import defaultMaterial from '../../../../assets/image/fallback/fallback decal 1.png';
|
2025-08-26 17:25:04 +05:30
|
|
|
import useModuleStore from '../../../../store/useModuleStore';
|
2025-08-26 17:39:45 +05:30
|
|
|
import { useEffect, useRef, useState } from 'react';
|
2025-08-28 16:32:45 +05:30
|
|
|
|
2025-08-28 15:50:45 +05:30
|
|
|
import { useDecalEventHandlers } from '../eventHandler/useDecalEventHandlers';
|
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 }) {
|
2025-08-28 15:50:45 +05:30
|
|
|
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
|
|
|
|
const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
|
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-28 15:50:45 +05:30
|
|
|
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 });
|
2025-08-26 16:38:29 +05:30
|
|
|
|
2025-08-26 17:39:45 +05:30
|
|
|
const [texture, setTexture] = useState<THREE.Texture | null>(null);
|
2025-08-28 15:50:45 +05:30
|
|
|
|
|
|
|
|
const logDecalStatus = (decalId: string, status: string) => {
|
|
|
|
|
// console.log(decalId, status);
|
|
|
|
|
}
|
2025-08-26 17:39:45 +05:30
|
|
|
|
|
|
|
|
const loadDefaultTexture = () => {
|
|
|
|
|
const textureLoader = new THREE.TextureLoader();
|
|
|
|
|
textureLoader.load(
|
|
|
|
|
defaultMaterial,
|
|
|
|
|
(fallbackTex) => {
|
|
|
|
|
fallbackTex.name = "default-decal";
|
|
|
|
|
setTexture(fallbackTex);
|
2025-08-28 15:50:45 +05:30
|
|
|
logDecalStatus(decal.decalId, 'default-loaded');
|
2025-08-26 17:39:45 +05:30
|
|
|
},
|
|
|
|
|
undefined,
|
|
|
|
|
(error) => {
|
|
|
|
|
console.error("Error loading default decal texture:", error);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const loadDecalTexture = async (decalId: string) => {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const cachedTexture = THREE.Cache.get(decalId);
|
|
|
|
|
if (cachedTexture) {
|
|
|
|
|
setTexture(cachedTexture);
|
2025-08-28 15:50:45 +05:30
|
|
|
logDecalStatus(decalId, 'cache-loaded');
|
2025-08-26 17:39:45 +05:30
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const indexedDBTexture = await retrieveImage(decalId);
|
|
|
|
|
if (indexedDBTexture) {
|
|
|
|
|
const blobUrl = URL.createObjectURL(indexedDBTexture);
|
|
|
|
|
const textureLoader = new THREE.TextureLoader();
|
|
|
|
|
textureLoader.load(
|
|
|
|
|
blobUrl,
|
|
|
|
|
(tex) => {
|
|
|
|
|
URL.revokeObjectURL(blobUrl);
|
|
|
|
|
tex.name = decalId;
|
|
|
|
|
THREE.Cache.add(decalId, tex);
|
|
|
|
|
setTexture(tex);
|
2025-08-28 15:50:45 +05:30
|
|
|
logDecalStatus(decalId, 'indexedDB-loaded');
|
2025-08-26 17:39:45 +05:30
|
|
|
},
|
|
|
|
|
undefined,
|
|
|
|
|
(error) => {
|
|
|
|
|
console.error(`Error loading texture from IndexedDB:`, error);
|
|
|
|
|
URL.revokeObjectURL(blobUrl);
|
|
|
|
|
loadFromBackend(decalId);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadFromBackend(decalId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error loading decal texture:", error);
|
|
|
|
|
loadDefaultTexture();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const loadFromBackend = (decalId: string) => {
|
2025-08-28 15:50:45 +05:30
|
|
|
|
|
|
|
|
const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`;
|
2025-08-26 17:39:45 +05:30
|
|
|
const textureLoader = new THREE.TextureLoader();
|
|
|
|
|
|
|
|
|
|
textureLoader.load(
|
|
|
|
|
textureUrl,
|
|
|
|
|
async (tex) => {
|
|
|
|
|
tex.name = decalId;
|
|
|
|
|
THREE.Cache.add(decalId, tex);
|
|
|
|
|
setTexture(tex);
|
2025-08-28 15:50:45 +05:30
|
|
|
logDecalStatus(decalId, 'backend-loaded');
|
2025-08-26 17:39:45 +05:30
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(textureUrl);
|
|
|
|
|
const blob = await response.blob();
|
|
|
|
|
await storeImage(decalId, blob);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error storing texture in IndexedDB:", error);
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-08-28 16:32:45 +05:30
|
|
|
undefined,
|
2025-08-26 17:39:45 +05:30
|
|
|
(error) => {
|
2025-08-28 16:32:45 +05:30
|
|
|
echo.error(`Error loading texture from backend: ${decal.decalName}`);
|
2025-08-26 17:39:45 +05:30
|
|
|
loadDefaultTexture();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (decal.decalId) {
|
|
|
|
|
loadDecalTexture(decal.decalId);
|
|
|
|
|
} else {
|
|
|
|
|
loadDefaultTexture();
|
|
|
|
|
}
|
|
|
|
|
}, [decal.decalId]);
|
|
|
|
|
|
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-06-25 17:20:35 +05:30
|
|
|
return (
|
|
|
|
|
<Decal
|
|
|
|
|
// debug
|
|
|
|
|
visible={visible}
|
2025-08-28 15:50:45 +05:30
|
|
|
ref={decalRef}
|
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-08-28 15:50:45 +05:30
|
|
|
scale={[(decal.decalType.type === 'Floor' || zPosition < 0) ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
|
2025-06-25 17:20:35 +05:30
|
|
|
userData={decal}
|
2025-08-28 15:50:45 +05:30
|
|
|
onPointerDown={(e) => { if (e.button === 0) handlePointerDown(e) }}
|
|
|
|
|
onClick={(e) => { handleClick(e) }}
|
|
|
|
|
onPointerEnter={(e) => { handlePointerEnter(e) }}
|
|
|
|
|
onPointerLeave={(e) => { handlePointerLeave(e) }}
|
|
|
|
|
onPointerMissed={() => handlePointerMissed()}
|
2025-06-25 17:20:35 +05:30
|
|
|
>
|
|
|
|
|
<meshBasicMaterial
|
2025-08-26 17:39:45 +05:30
|
|
|
map={texture}
|
2025-06-25 17:20:35 +05:30
|
|
|
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
|