decal boundary added based on the wall and floor movement
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { Decal } from "@react-three/drei";
|
import { Decal } from "@react-three/drei";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useToggleView, useToolMode } from "../../../../store/builder/store";
|
import { useToggleView, useToolMode } from "../../../../store/builder/store";
|
||||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
import { retrieveImage, storeImage } from "../../../../utils/indexDB/idbUtils";
|
import { retrieveImage, storeImage } from "../../../../utils/indexDB/idbUtils";
|
||||||
@@ -7,11 +8,16 @@ import deepEqual from "../../../../functions/objectDeepEqual";
|
|||||||
|
|
||||||
import defaultMaterial from "../../../../assets/image/fallback/fallback decal 1.png";
|
import defaultMaterial from "../../../../assets/image/fallback/fallback decal 1.png";
|
||||||
import useModuleStore from "../../../../store/ui/useModuleStore";
|
import useModuleStore from "../../../../store/ui/useModuleStore";
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
|
|
||||||
import { useDecalEventHandlers } from "../eventHandler/useDecalEventHandlers";
|
import { useDecalEventHandlers } from "../eventHandler/useDecalEventHandlers";
|
||||||
|
|
||||||
function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: Readonly<{ parent: Wall | Floor; visible?: boolean; decal: Decal; zPosition?: number }>) {
|
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 url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||||
const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
|
const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
@@ -155,10 +161,10 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Decal
|
<Decal
|
||||||
// debug
|
debug
|
||||||
visible={visible}
|
visible={visible}
|
||||||
ref={decalRef}
|
ref={decalRef}
|
||||||
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
position={overWritePosition ? [overWritePosition[0], overWritePosition[1], zPosition] : [decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
||||||
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
|
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
|
||||||
scale={[decal.decalType.type === "Floor" || zPosition < 0 ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
|
scale={[decal.decalType.type === "Floor" || zPosition < 0 ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
|
||||||
userData={decal}
|
userData={decal}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as THREE from "three";
|
import { MathUtils, Vector2, Vector3 } from "three";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { CameraControls } from "@react-three/drei";
|
import { CameraControls } from "@react-three/drei";
|
||||||
@@ -7,6 +7,7 @@ import { useToggleView, useToolMode } from "../../../../store/builder/store";
|
|||||||
import { useSocketStore } from "../../../../store/socket/useSocketStore";
|
import { useSocketStore } from "../../../../store/socket/useSocketStore";
|
||||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
import { useSceneContext } from "../../../scene/sceneContext";
|
import { useSceneContext } from "../../../scene/sceneContext";
|
||||||
|
import { useWallClassification } from "../../wall/Instances/instance/helpers/useWallClassification";
|
||||||
import useModuleStore from "../../../../store/ui/useModuleStore";
|
import useModuleStore from "../../../../store/ui/useModuleStore";
|
||||||
import useWallResponseHandler from "../../../collaboration/responseHandler/useWallResponseHandler";
|
import useWallResponseHandler from "../../../collaboration/responseHandler/useWallResponseHandler";
|
||||||
import useFloorResponseHandler from "../../../collaboration/responseHandler/useFloorResponseHandler";
|
import useFloorResponseHandler from "../../../collaboration/responseHandler/useFloorResponseHandler";
|
||||||
@@ -20,10 +21,11 @@ import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsert
|
|||||||
|
|
||||||
export function useDecalEventHandlers({ parent, decal, visible }: { parent: Wall | Floor; decal: Decal; visible: boolean }) {
|
export function useDecalEventHandlers({ parent, decal, visible }: { parent: Wall | Floor; decal: Decal; visible: boolean }) {
|
||||||
const { wallStore, floorStore, versionStore } = useSceneContext();
|
const { wallStore, floorStore, versionStore } = useSceneContext();
|
||||||
const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById, addDecal: addDecalToWall } = wallStore();
|
const { walls, removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById, addDecal: addDecalToWall, getDecalById: getDecalOnWall } = wallStore();
|
||||||
const { removeDecal: removeDecalInFloor, updateDecalPosition: updateDecalPositionInFloor, getFloorById, addDecal: addDecalToFloor } = floorStore();
|
const { removeDecal: removeDecalInFloor, updateDecalPosition: updateDecalPositionInFloor, getFloorById, addDecal: addDecalToFloor, getDecalById: getDecalOnFloor } = floorStore();
|
||||||
const { setSelectedWall, setSelectedFloor, setSelectedDecal, setDeletableDecal, deletableDecal, selectedDecal, setDecalDragState, decalDragState } = useBuilderStore();
|
const { setSelectedWall, setSelectedFloor, setSelectedDecal, setDeletableDecal, deletableDecal, selectedDecal, setDecalDragState, decalDragState } = useBuilderStore();
|
||||||
const { updateWallInScene } = useWallResponseHandler();
|
const { updateWallInScene } = useWallResponseHandler();
|
||||||
|
const { isWallFlipped } = useWallClassification(walls);
|
||||||
const { updateFloorInScene } = useFloorResponseHandler();
|
const { updateFloorInScene } = useFloorResponseHandler();
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
@@ -44,7 +46,7 @@ export function useDecalEventHandlers({ parent, decal, visible }: { parent: Wall
|
|||||||
const wallIntersect = intersects.find((i) => i.object.userData?.wallUuid);
|
const wallIntersect = intersects.find((i) => i.object.userData?.wallUuid);
|
||||||
const floorIntersect = intersects.find((i) => i.object.userData?.floorUuid);
|
const floorIntersect = intersects.find((i) => i.object.userData?.floorUuid);
|
||||||
|
|
||||||
let offset = decalDragState.dragOffset || new THREE.Vector3(0, 0, 0);
|
let offset = decalDragState.dragOffset || new Vector3(0, 0, 0);
|
||||||
|
|
||||||
if (wallIntersect) {
|
if (wallIntersect) {
|
||||||
const wallUuid = wallIntersect.object.userData.wallUuid;
|
const wallUuid = wallIntersect.object.userData.wallUuid;
|
||||||
@@ -59,7 +61,30 @@ export function useDecalEventHandlers({ parent, decal, visible }: { parent: Wall
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === "Wall") {
|
if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === "Wall") {
|
||||||
updateDecalPositionInWall(decal.decalUuid, [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]]);
|
const wall = getWallById(wallUuid);
|
||||||
|
const decalData = getDecalOnWall(decal.decalUuid);
|
||||||
|
if (!decalData || !wall) return;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedPosition = clampDecalPosition({ ...decalData, decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]] }, wallLength, wall.wallHeight);
|
||||||
|
|
||||||
|
updateDecalPositionInWall(decalData.decalUuid, clampedPosition);
|
||||||
} else if (decal.decalType.type === "Wall" && wallUuid) {
|
} else if (decal.decalType.type === "Wall" && wallUuid) {
|
||||||
deleteDecal(decal.decalUuid, parent);
|
deleteDecal(decal.decalUuid, parent);
|
||||||
|
|
||||||
@@ -100,7 +125,60 @@ export function useDecalEventHandlers({ parent, decal, visible }: { parent: Wall
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === "Floor") {
|
if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === "Floor") {
|
||||||
updateDecalPositionInFloor(decal.decalUuid, [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]]);
|
function isPointInPolygon(point: Vector2, polygon: Vector2[]): boolean {
|
||||||
|
let inside = false;
|
||||||
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||||
|
const xi = polygon[i].x,
|
||||||
|
yi = polygon[i].y;
|
||||||
|
const xj = polygon[j].x,
|
||||||
|
yj = polygon[j].y;
|
||||||
|
|
||||||
|
const intersect = yi > point.y !== yj > point.y && point.x < ((xj - xi) * (point.y - yi)) / (yj - yi) + xi;
|
||||||
|
if (intersect) inside = !inside;
|
||||||
|
}
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampToPolygon(point: Vector2, polygon: Vector2[]): Vector2 {
|
||||||
|
let closestPoint = point.clone();
|
||||||
|
let minDist = Infinity;
|
||||||
|
|
||||||
|
for (let i = 0; i < polygon.length; i++) {
|
||||||
|
const a = polygon[i];
|
||||||
|
const b = polygon[(i + 1) % polygon.length];
|
||||||
|
|
||||||
|
const ab = new Vector2().subVectors(b, a);
|
||||||
|
const t = Math.max(0, Math.min(1, point.clone().sub(a).dot(ab) / ab.lengthSq()));
|
||||||
|
const proj = a.clone().add(ab.multiplyScalar(t));
|
||||||
|
|
||||||
|
const dist = proj.distanceTo(point);
|
||||||
|
if (dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
closestPoint = proj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampDecalPosition(decal: Decal, floor: Floor): [number, number, number] {
|
||||||
|
const pos = new Vector3(...decal.decalPosition);
|
||||||
|
const polygon2D = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
|
||||||
|
const p2 = new Vector2(pos.x, pos.y);
|
||||||
|
|
||||||
|
let final2D: Vector2;
|
||||||
|
if (isPointInPolygon(p2, polygon2D)) {
|
||||||
|
final2D = p2;
|
||||||
|
} else {
|
||||||
|
final2D = clampToPolygon(p2, polygon2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedPos = new Vector3(final2D.x, final2D.y, pos.z);
|
||||||
|
return clampedPos.toArray() as [number, number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedPosition = clampDecalPosition({ ...decal, decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]] }, parent);
|
||||||
|
|
||||||
|
updateDecalPositionInFloor(decal.decalUuid, clampedPosition);
|
||||||
} else if (decal.decalType.type === "Floor" && floorUuid) {
|
} else if (decal.decalType.type === "Floor" && floorUuid) {
|
||||||
deleteDecal(decal.decalUuid, parent);
|
deleteDecal(decal.decalUuid, parent);
|
||||||
|
|
||||||
@@ -299,7 +377,87 @@ export function useDecalEventHandlers({ parent, decal, visible }: { parent: Wall
|
|||||||
setSelectedFloor(null);
|
setSelectedFloor(null);
|
||||||
|
|
||||||
const localIntersect = e.object.worldToLocal(e.point.clone());
|
const localIntersect = e.object.worldToLocal(e.point.clone());
|
||||||
let dragOffset = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0);
|
|
||||||
|
let clampedDecalPosition;
|
||||||
|
|
||||||
|
if (decal.decalType.type === "Wall" && "wallUuid" in parent) {
|
||||||
|
const wallFlipped = isWallFlipped(parent);
|
||||||
|
const [rawStart, rawEnd] = parent.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);
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
clampedDecalPosition = clampDecalPosition(decal, wallLength, parent.wallHeight);
|
||||||
|
} else if (decal.decalType.type === "Floor" && "floorUuid" in parent) {
|
||||||
|
function isPointInPolygon(point: Vector2, polygon: Vector2[]): boolean {
|
||||||
|
let inside = false;
|
||||||
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||||
|
const xi = polygon[i].x,
|
||||||
|
yi = polygon[i].y;
|
||||||
|
const xj = polygon[j].x,
|
||||||
|
yj = polygon[j].y;
|
||||||
|
|
||||||
|
const intersect = yi > point.y !== yj > point.y && point.x < ((xj - xi) * (point.y - yi)) / (yj - yi) + xi;
|
||||||
|
if (intersect) inside = !inside;
|
||||||
|
}
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampToPolygon(point: Vector2, polygon: Vector2[]): Vector2 {
|
||||||
|
let closestPoint = point.clone();
|
||||||
|
let minDist = Infinity;
|
||||||
|
|
||||||
|
for (let i = 0; i < polygon.length; i++) {
|
||||||
|
const a = polygon[i];
|
||||||
|
const b = polygon[(i + 1) % polygon.length];
|
||||||
|
|
||||||
|
const ab = new Vector2().subVectors(b, a);
|
||||||
|
const t = Math.max(0, Math.min(1, point.clone().sub(a).dot(ab) / ab.lengthSq()));
|
||||||
|
const proj = a.clone().add(ab.multiplyScalar(t));
|
||||||
|
|
||||||
|
const dist = proj.distanceTo(point);
|
||||||
|
if (dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
closestPoint = proj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampDecalPosition(decal: Decal, floor: Floor): [number, number, number] {
|
||||||
|
const pos = new Vector3(...decal.decalPosition);
|
||||||
|
const polygon2D = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
|
||||||
|
const p2 = new Vector2(pos.x, pos.y);
|
||||||
|
|
||||||
|
let final2D: Vector2;
|
||||||
|
if (isPointInPolygon(p2, polygon2D)) {
|
||||||
|
final2D = p2;
|
||||||
|
} else {
|
||||||
|
final2D = clampToPolygon(p2, polygon2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedPos = new Vector3(final2D.x, final2D.y, pos.z);
|
||||||
|
return clampedPos.toArray() as [number, number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
clampedDecalPosition = clampDecalPosition(decal, parent);
|
||||||
|
} else {
|
||||||
|
clampedDecalPosition = decal.decalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dragOffset = new Vector3(clampedDecalPosition[0] - localIntersect.x, clampedDecalPosition[1] - localIntersect.y, 0);
|
||||||
setDecalDragState(true, decal.decalUuid, dragOffset);
|
setDecalDragState(true, decal.decalUuid, dragOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace } from "three";
|
import { DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, Vector2, Vector3 } from "three";
|
||||||
import { useLoader } from "@react-three/fiber";
|
import { useLoader } from "@react-three/fiber";
|
||||||
import useModuleStore from "../../../../../store/ui/useModuleStore";
|
import useModuleStore from "../../../../../store/ui/useModuleStore";
|
||||||
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
|
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
|
||||||
@@ -123,6 +123,57 @@ function FloorInstance({ floor }: { readonly floor: Floor }) {
|
|||||||
bevelThickness: 0.1,
|
bevelThickness: 0.1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isPointInPolygon(point: Vector2, polygon: Vector2[]): boolean {
|
||||||
|
let inside = false;
|
||||||
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||||
|
const xi = polygon[i].x,
|
||||||
|
yi = polygon[i].y;
|
||||||
|
const xj = polygon[j].x,
|
||||||
|
yj = polygon[j].y;
|
||||||
|
|
||||||
|
const intersect = yi > point.y !== yj > point.y && point.x < ((xj - xi) * (point.y - yi)) / (yj - yi) + xi;
|
||||||
|
if (intersect) inside = !inside;
|
||||||
|
}
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampToPolygon(point: Vector2, polygon: Vector2[]): Vector2 {
|
||||||
|
let closestPoint = point.clone();
|
||||||
|
let minDist = Infinity;
|
||||||
|
|
||||||
|
for (let i = 0; i < polygon.length; i++) {
|
||||||
|
const a = polygon[i];
|
||||||
|
const b = polygon[(i + 1) % polygon.length];
|
||||||
|
|
||||||
|
const ab = new Vector2().subVectors(b, a);
|
||||||
|
const t = Math.max(0, Math.min(1, point.clone().sub(a).dot(ab) / ab.lengthSq()));
|
||||||
|
const proj = a.clone().add(ab.multiplyScalar(t));
|
||||||
|
|
||||||
|
const dist = proj.distanceTo(point);
|
||||||
|
if (dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
closestPoint = proj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampDecalPosition(decal: Decal, floor: Floor): [number, number, number] {
|
||||||
|
const pos = new Vector3(...decal.decalPosition);
|
||||||
|
const polygon2D = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
|
||||||
|
const p2 = new Vector2(pos.x, pos.y);
|
||||||
|
|
||||||
|
let final2D: Vector2;
|
||||||
|
if (isPointInPolygon(p2, polygon2D)) {
|
||||||
|
final2D = p2;
|
||||||
|
} else {
|
||||||
|
final2D = clampToPolygon(p2, polygon2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedPos = new Vector3(final2D.x, final2D.y, pos.z);
|
||||||
|
return clampedPos.toArray() as [number, number, number];
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExtrudePolygon
|
<ExtrudePolygon
|
||||||
castShadow
|
castShadow
|
||||||
@@ -171,7 +222,7 @@ function FloorInstance({ floor }: { readonly floor: Floor }) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{floor.decals.map((decal) => (
|
{floor.decals.map((decal) => (
|
||||||
<DecalInstance parent={floor} key={decal.decalUuid} decal={decal} />
|
<DecalInstance parent={floor} key={decal.decalUuid} decal={decal} overWritePosition={clampDecalPosition(decal, floor)} />
|
||||||
))}
|
))}
|
||||||
</ExtrudePolygon>
|
</ExtrudePolygon>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as THREE from "three";
|
import { BoxGeometry, DoubleSide, MathUtils, MeshStandardMaterial, RepeatWrapping, SRGBColorSpace, TextureLoader, Vector3 } from "three";
|
||||||
import { Base } from "@react-three/csg";
|
import { Base } from "@react-three/csg";
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useMemo, useRef, useState } from "react";
|
||||||
import { MeshDiscardMaterial } from "@react-three/drei";
|
import { MeshDiscardMaterial } from "@react-three/drei";
|
||||||
@@ -48,47 +48,47 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
const centerZ = (startZ + endZ) / 2;
|
const centerZ = (startZ + endZ) / 2;
|
||||||
const centerY = wall.wallHeight / 2;
|
const centerY = wall.wallHeight / 2;
|
||||||
|
|
||||||
const textureLoader = new THREE.TextureLoader();
|
const textureLoader = new TextureLoader();
|
||||||
|
|
||||||
const [defaultWallTexture, material1WallTexture] = useMemo(() => {
|
const [defaultWallTexture, material1WallTexture] = useMemo(() => {
|
||||||
const inside = textureLoader.load(defaultMaterial);
|
const inside = textureLoader.load(defaultMaterial);
|
||||||
inside.wrapS = inside.wrapT = THREE.RepeatWrapping;
|
inside.wrapS = inside.wrapT = RepeatWrapping;
|
||||||
inside.repeat.set(wallLength / 10, wall.wallHeight / 10);
|
inside.repeat.set(wallLength / 10, wall.wallHeight / 10);
|
||||||
inside.colorSpace = THREE.SRGBColorSpace;
|
inside.colorSpace = SRGBColorSpace;
|
||||||
|
|
||||||
const outside = textureLoader.load(material1);
|
const outside = textureLoader.load(material1);
|
||||||
outside.wrapS = outside.wrapT = THREE.RepeatWrapping;
|
outside.wrapS = outside.wrapT = RepeatWrapping;
|
||||||
outside.repeat.set(wallLength / 10, wall.wallHeight / 10);
|
outside.repeat.set(wallLength / 10, wall.wallHeight / 10);
|
||||||
outside.colorSpace = THREE.SRGBColorSpace;
|
outside.colorSpace = SRGBColorSpace;
|
||||||
|
|
||||||
return [inside, outside];
|
return [inside, outside];
|
||||||
}, [wallLength, wall.wallHeight]);
|
}, [wallLength, wall.wallHeight]);
|
||||||
|
|
||||||
const materials = useMemo(() => {
|
const materials = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Left
|
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Left
|
||||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Right
|
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Right
|
||||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Top
|
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Top
|
||||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Bottom
|
new MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: DoubleSide, visible: visible, clipShadows: true }), // Bottom
|
||||||
new THREE.MeshStandardMaterial({
|
new MeshStandardMaterial({
|
||||||
color: Constants.wallConfig.defaultColor,
|
color: Constants.wallConfig.defaultColor,
|
||||||
side: THREE.DoubleSide,
|
side: DoubleSide,
|
||||||
map: wall.insideMaterial === "Default Material" ? defaultWallTexture : material1WallTexture,
|
map: wall.insideMaterial === "Default Material" ? defaultWallTexture : material1WallTexture,
|
||||||
}),
|
}),
|
||||||
new THREE.MeshStandardMaterial({
|
new MeshStandardMaterial({
|
||||||
color: Constants.wallConfig.defaultColor,
|
color: Constants.wallConfig.defaultColor,
|
||||||
side: THREE.DoubleSide,
|
side: DoubleSide,
|
||||||
map: wall.outsideMaterial === "Default Material" ? defaultWallTexture : material1WallTexture,
|
map: wall.outsideMaterial === "Default Material" ? defaultWallTexture : material1WallTexture,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}, [defaultWallTexture, material1WallTexture, wall, visible]);
|
}, [defaultWallTexture, material1WallTexture, wall, visible]);
|
||||||
|
|
||||||
const geometry = useMemo(() => new THREE.BoxGeometry(wallLength, wall.wallHeight, wall.wallThickness), [wallLength, wall.wallHeight, wall.wallThickness]);
|
const geometry = useMemo(() => new BoxGeometry(wallLength, wall.wallHeight, wall.wallThickness), [wallLength, wall.wallHeight, wall.wallThickness]);
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (!meshRef.current) return;
|
if (!meshRef.current) return;
|
||||||
const v = new THREE.Vector3();
|
const v = new Vector3();
|
||||||
const u = new THREE.Vector3();
|
const u = new Vector3();
|
||||||
|
|
||||||
let nextVisible = true;
|
let nextVisible = true;
|
||||||
|
|
||||||
@@ -108,6 +108,13 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<mesh castShadow receiveShadow name={`Wall-${wall.wallUuid}`} key={wall.wallUuid} userData={wall}>
|
<mesh castShadow receiveShadow name={`Wall-${wall.wallUuid}`} key={wall.wallUuid} userData={wall}>
|
||||||
{assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0) ? (
|
{assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0) ? (
|
||||||
@@ -116,7 +123,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
castShadow
|
castShadow
|
||||||
receiveShadow
|
receiveShadow
|
||||||
ref={meshRef}
|
ref={meshRef}
|
||||||
geometry={visible ? geometry : new THREE.BoxGeometry(0.00001, 0.00001)}
|
geometry={visible ? geometry : new BoxGeometry(0.00001, 0.00001)}
|
||||||
position={[centerX, centerY, centerZ]}
|
position={[centerX, centerY, centerZ]}
|
||||||
rotation={[0, -angle, 0]}
|
rotation={[0, -angle, 0]}
|
||||||
userData={wall}
|
userData={wall}
|
||||||
@@ -158,7 +165,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
<MeshDiscardMaterial />
|
<MeshDiscardMaterial />
|
||||||
|
|
||||||
{wall.decals.map((decal) => (
|
{wall.decals.map((decal) => (
|
||||||
<DecalInstance parent={wall} visible={visible} key={decal.decalUuid} decal={decal} />
|
<DecalInstance parent={wall} visible={visible} key={decal.decalUuid} decal={decal} overWritePosition={clampDecalPosition(decal, wallLength, wall.wallHeight)} />
|
||||||
))}
|
))}
|
||||||
</mesh>
|
</mesh>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface FloorStore {
|
|||||||
getFloorsByPoints: (points: [Point, Point]) => Floor[] | [];
|
getFloorsByPoints: (points: [Point, Point]) => Floor[] | [];
|
||||||
getFloorPointById: (uuid: string) => Point | undefined;
|
getFloorPointById: (uuid: string) => Point | undefined;
|
||||||
getConnectedPoints: (uuid: string) => Point[];
|
getConnectedPoints: (uuid: string) => Point[];
|
||||||
|
getDecalById: (uuid: string) => Decal | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createFloorStore = () => {
|
export const createFloorStore = () => {
|
||||||
@@ -431,6 +432,14 @@ export const createFloorStore = () => {
|
|||||||
}
|
}
|
||||||
return connected;
|
return connected;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDecalById: (decalUuid: string): Decal | undefined => {
|
||||||
|
for (const floor of get().floors) {
|
||||||
|
const decal = floor.decals.find((d) => d.decalUuid === decalUuid);
|
||||||
|
if (decal) return JSON.parse(JSON.stringify(decal));
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ interface WallStore {
|
|||||||
getWallPointById: (wallUuid: string) => Point | undefined;
|
getWallPointById: (wallUuid: string) => Point | undefined;
|
||||||
getConnectedPoints: (wallUuid: string) => Point[];
|
getConnectedPoints: (wallUuid: string) => Point[];
|
||||||
getConnectedWallsByWallId: (wallUuid: string, skipSelf: boolean) => Walls;
|
getConnectedWallsByWallId: (wallUuid: string, skipSelf: boolean) => Walls;
|
||||||
|
getDecalById: (decalUuid: string) => Decal | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createWallStore = () => {
|
export const createWallStore = () => {
|
||||||
@@ -312,6 +313,14 @@ export const createWallStore = () => {
|
|||||||
return w.points.some((p) => pointUuids.includes(p.pointUuid));
|
return w.points.some((p) => pointUuids.includes(p.pointUuid));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDecalById: (decalUuid: string): Decal | undefined => {
|
||||||
|
for (const wall of get().walls) {
|
||||||
|
const decal = wall.decals.find((d) => d.decalUuid === decalUuid);
|
||||||
|
if (decal) return JSON.parse(JSON.stringify(decal));
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user