Files
Dwinzo_Demo/app/src/modules/builder/wall/Instances/instance/wall.tsx

176 lines
8.0 KiB
TypeScript

import { BoxGeometry, DoubleSide, MathUtils, MeshStandardMaterial, RepeatWrapping, SRGBColorSpace, TextureLoader, Vector3 } from "three";
import { Base } from "@react-three/csg";
import { useMemo, useRef, useState } from "react";
import { MeshDiscardMaterial } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import useModuleStore from "../../../../../store/ui/useModuleStore";
import { useSceneContext } from "../../../../scene/sceneContext";
import { useWallClassification } from "./helpers/useWallClassification";
import { useToggleView, useWallVisibility } from "../../../../../store/builder/store";
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
import * as Constants from "../../../../../types/world/worldConstants";
import DecalInstance from "../../../Decal/decalInstance/decalInstance";
import defaultMaterial from "../../../../../assets/textures/floor/wall-tex.png";
import material1 from "../../../../../assets/textures/floor/factory wall texture.jpg";
function Wall({ wall }: { readonly wall: Wall }) {
const { wallStore, wallAssetStore } = useSceneContext();
const { walls } = wallStore();
const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore();
const assets = getWallAssetsByWall(wall.wallUuid);
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
const { toggleView } = useToggleView();
const { activeModule } = useModuleStore();
const { camera } = useThree();
const { wallVisibility } = useWallVisibility();
const { getWallType, isWallFlipped } = useWallClassification(walls);
const [visible, setVisible] = useState(true);
const meshRef = useRef<any>();
const prevVisibleRef = useRef<boolean | null>(null);
const wallType = getWallType(wall);
const wallFlipped = isWallFlipped(wall);
const [rawStart, rawEnd] = wall.points;
const [startPoint, endPoint] = wallFlipped ? [rawStart, rawEnd] : [rawEnd, rawStart];
const startX = startPoint.position[0];
const startZ = startPoint.position[2];
const endX = endPoint.position[0];
const endZ = endPoint.position[2];
const wallLength = Math.sqrt((endX - startX) ** 2 + (endZ - startZ) ** 2);
const angle = Math.atan2(endZ - startZ, endX - startX);
const centerX = (startX + endX) / 2;
const centerZ = (startZ + endZ) / 2;
const centerY = wall.wallHeight / 2;
const textureLoader = new TextureLoader();
const [defaultWallTexture, material1WallTexture] = useMemo(() => {
const inside = textureLoader.load(defaultMaterial);
inside.wrapS = inside.wrapT = RepeatWrapping;
inside.repeat.set(wallLength / 10, wall.wallHeight / 10);
inside.colorSpace = SRGBColorSpace;
const outside = textureLoader.load(material1);
outside.wrapS = outside.wrapT = RepeatWrapping;
outside.repeat.set(wallLength / 10, wall.wallHeight / 10);
outside.colorSpace = SRGBColorSpace;
return [inside, outside];
}, [wallLength, wall.wallHeight]);
const materials = useMemo(() => {
return [
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Left
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Right
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Top
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Bottom
new MeshStandardMaterial({
color: Constants.wallConfig.defaultColor,
side: DoubleSide,
map: wall.insideMaterial === "Default Material" ? defaultWallTexture : material1WallTexture,
}),
new MeshStandardMaterial({
color: Constants.wallConfig.defaultColor,
side: DoubleSide,
map: wall.outsideMaterial === "Default Material" ? defaultWallTexture : material1WallTexture,
}),
];
}, [defaultWallTexture, material1WallTexture, wall, visible]);
const geometry = useMemo(() => new BoxGeometry(wallLength, wall.wallHeight, wall.wallThickness), [wallLength, wall.wallHeight, wall.wallThickness]);
useFrame(() => {
if (!meshRef.current) return;
const v = new Vector3();
const u = new Vector3();
let nextVisible = true;
if (!wallVisibility && wallType.type === "room") {
meshRef.current.getWorldDirection(v);
u.subVectors(camera.position, meshRef.current.position);
if (!u || !v) return;
nextVisible = v.dot(u) >= 0;
}
if (prevVisibleRef.current !== nextVisible) {
prevVisibleRef.current = nextVisible;
setVisible(nextVisible);
assets.forEach((asset) => {
setVisibility(asset.modelUuid, nextVisible);
});
}
});
function clampDecalPosition(decal: Decal, wallLength: number, wallHeight: number) {
const localPos = new Vector3(...decal.decalPosition);
localPos.x = MathUtils.clamp(localPos.x, -wallLength / 2, wallLength / 2);
localPos.y = MathUtils.clamp(localPos.y, -wallHeight / 2, wallHeight / 2);
return localPos.toArray() as [number, number, number];
}
return (
<mesh castShadow receiveShadow name={`Wall-${wall.wallUuid}`} key={wall.wallUuid} userData={wall}>
{assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0) ? (
<Base
name={`BaseWall${wall.wallUuid}`}
castShadow
receiveShadow
ref={meshRef}
geometry={visible ? geometry : new BoxGeometry(0.00001, 0.00001)}
position={[centerX, centerY, centerZ]}
rotation={[0, -angle, 0]}
userData={wall}
>
{materials.map((material, index) => (
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
))}
</Base>
) : (
<mesh castShadow receiveShadow ref={meshRef} geometry={geometry} position={[centerX, centerY, centerZ]} rotation={[0, -angle, 0]} userData={wall}>
{materials.map((material, index) => (
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
))}
</mesh>
)}
<mesh
castShadow
receiveShadow
geometry={geometry}
position={[centerX, centerY, centerZ]}
rotation={[0, -angle, 0]}
userData={wall}
name={`WallReference_${wall.wallUuid}`}
onDoubleClick={(e) => {
if (visible && !toggleView && activeModule === "builder") {
if (e.object.userData.wallUuid) {
e.stopPropagation();
setSelectedWall({ wallData: wall, wallMesh: e.object });
setSelectedDecal(null);
}
}
}}
onPointerMissed={() => {
if (selectedWall && selectedWall.wallMesh?.userData.wallUuid === wall.wallUuid) {
setSelectedWall(null);
}
}}
>
<MeshDiscardMaterial />
{wall.decals.map((decal) => (
<DecalInstance parent={wall} visible={visible} key={decal.decalUuid} decal={decal} overWritePosition={clampDecalPosition(decal, wallLength, wall.wallHeight)} />
))}
</mesh>
</mesh>
);
}
export default Wall;