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

191 lines
7.1 KiB
TypeScript

import * as THREE from "three";
import { Decal } from "@react-three/drei";
import { useEffect, useRef, useState } from "react";
import { useToggleView, useToolMode } from "../../../../store/builder/store";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { retrieveImage, storeImage } from "../../../../utils/indexDB/idbUtils";
import deepEqual from "../../../../functions/objectDeepEqual";
import defaultMaterial from "../../../../assets/image/fallback/fallback decal 1.png";
import useModuleStore from "../../../../store/ui/useModuleStore";
import { useDecalEventHandlers } from "../eventHandler/useDecalEventHandlers";
function DecalInstance({
parent,
visible = true,
decal,
zPosition = decal.decalPosition[2],
overWritePosition = null,
}: Readonly<{ parent: Wall | Floor; visible?: boolean; decal: Decal; zPosition?: number; overWritePosition?: [number, number, number] | null }>) {
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 decalRef = useRef<any>(null);
useEffect(() => {
if (selectedDecal?.decalData.decalUuid === decal.decalUuid && !selectedDecal.decalMesh) {
setSelectedDecal({ decalData: selectedDecal.decalData, decalMesh: decalRef.current });
}
// eslint-disable-next-line
}, [selectedDecal]);
useEffect(() => {
if (selectedDecal?.decalData.decalUuid === decal.decalUuid && selectedDecal.decalMesh) {
if (!deepEqual(selectedDecal.decalData, decal)) {
setSelectedDecal({
decalData: decal,
decalMesh: selectedDecal.decalMesh,
});
}
}
// eslint-disable-next-line
}, [decal, selectedDecal]);
const { handlePointerMissed, handlePointerLeave, handleClick, handlePointerDown, handlePointerEnter } = useDecalEventHandlers({ parent, decal, visible });
const [texture, setTexture] = useState<THREE.Texture | null>(null);
const logDecalStatus = (decalId: string, status: string) => {
// console.log(decalId, status);
};
const loadDefaultTexture = () => {
const textureLoader = new THREE.TextureLoader();
textureLoader.load(
defaultMaterial,
(fallbackTex) => {
fallbackTex.name = "default-decal";
setTexture(fallbackTex);
logDecalStatus(decal.decalId, "default-loaded");
},
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);
logDecalStatus(decalId, "cache-loaded");
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);
logDecalStatus(decalId, "indexedDB-loaded");
},
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) => {
const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`;
const textureLoader = new THREE.TextureLoader();
textureLoader.load(
textureUrl,
async (tex) => {
tex.name = decalId;
THREE.Cache.add(decalId, tex);
setTexture(tex);
logDecalStatus(decalId, "backend-loaded");
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);
}
},
undefined,
(error) => {
echo.error(`Error loading texture from backend: ${decal.decalName}`);
loadDefaultTexture();
}
);
};
useEffect(() => {
if (decal.decalId) {
loadDecalTexture(decal.decalId);
} else {
loadDefaultTexture();
}
// eslint-disable-next-line
}, [decal.decalId]);
useEffect(() => {
if (!toggleView && activeModule === "builder") {
if (toolMode !== "cursor" && selectedDecal) {
setSelectedDecal(null);
}
if (toolMode !== "3D-Delete" && deletableDecal) {
setDeletableDecal(null);
}
} else {
if (selectedDecal) setSelectedDecal(null);
if (deletableDecal) setDeletableDecal(null);
}
// eslint-disable-next-line
}, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]);
return (
<Decal
debug
visible={visible}
ref={decalRef}
position={overWritePosition ? [overWritePosition[0], overWritePosition[1], zPosition] : [decal.decalPosition[0], decal.decalPosition[1], zPosition]}
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
scale={[decal.decalType.type === "Floor" || zPosition < 0 ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
userData={decal}
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} side={THREE.DoubleSide} polygonOffset polygonOffsetFactor={-1} transparent opacity={decal.decalOpacity} />
</Decal>
);
}
export default DecalInstance;