Add decal management functionality and refactor wall components

This commit is contained in:
2025-06-25 17:20:35 +05:30
parent 587ed6c9d7
commit 08208528a5
7 changed files with 124 additions and 27 deletions

View File

@@ -0,0 +1,48 @@
import * as THREE from 'three';
import { Decal } from '@react-three/drei'
import { useLoader } from '@react-three/fiber';
import { useToggleView } from '../../../store/builder/store';
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import defaultMaterial from '../../../assets/textures/floor/wall-tex.png';
import useModuleStore from '../../../store/useModuleStore';
function DecalInstance({ visible = true, decal }: { visible?: boolean, decal: Decal }) {
const { setSelectedWall, selectedDecal, setSelectedDecal } = useBuilderStore();
const { togglView } = useToggleView();
const { activeModule } = useModuleStore();
const material = useLoader(THREE.TextureLoader, defaultMaterial);
return (
<Decal
// debug
visible={visible}
position={decal.decalPosition}
rotation={[0, 0, decal.decalRotation]}
scale={[decal.decalScale, decal.decalScale, 0.01]}
userData={decal}
onClick={(e) => {
if (visible && !togglView && activeModule === 'builder') {
if (e.object.userData.decalUuid) {
setSelectedDecal(e.object);
setSelectedWall(null);
}
}
}}
onPointerMissed={() => {
if (selectedDecal && selectedDecal.userData.decalUuid === decal.decalUuid) {
setSelectedDecal(null);
}
}}
>
<meshBasicMaterial
map={material}
side={THREE.DoubleSide}
polygonOffset
polygonOffsetFactor={-1}
/>
</Decal>
)
}
export default DecalInstance

View File

@@ -13,12 +13,13 @@ 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';
function Wall({ wall }: { readonly wall: Wall }) {
const { wallStore } = useSceneContext();
const { walls } = wallStore();
const { walls, addDecal } = wallStore();
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
const { togglView } = useToggleView();
const { setSelectedWall } = useBuilderStore();
const { activeModule } = useModuleStore();
const { camera } = useThree();
const { wallVisibility } = useWallVisibility();
@@ -112,25 +113,6 @@ function Wall({ wall }: { readonly wall: Wall }) {
{materials.map((material, index) => (
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
))}
{wall.decals.map((decal) => {
return (
<Decal
// debug
position={[decal.decalPosition[0], decal.decalPosition[1], wall.wallThickness / 2]}
rotation={[0, 0, decal.decalRotation]}
scale={[decal.decalScale, decal.decalScale, 0.001]}
userData={decal}
>
<meshBasicMaterial
map={material1WallTexture}
side={THREE.DoubleSide}
polygonOffset
polygonOffsetFactor={-1}
/>
</Decal>
)
})}
</Base>
<mesh
castShadow
@@ -138,15 +120,39 @@ function Wall({ wall }: { readonly wall: Wall }) {
position={[centerX, centerY, centerZ]}
rotation={[0, -angle, 0]}
userData={wall}
name={`WallRaycastReference_${wall.wallUuid}`}
name={`WallReference_${wall.wallUuid}`}
onClick={(e) => {
if (visible && !togglView && activeModule === 'builder') {
setSelectedWall(e.object)
if (e.object.userData.wallUuid) {
setSelectedWall(e.object);
setSelectedDecal(null);
if (wall.decals.length > 0) return;
const decal: Decal = {
decalUuid: THREE.MathUtils.generateUUID(),
decalName: 'Decal',
decalId: 'Default Decal',
decalPosition: [0, 0, wall.wallThickness / 2 + 0.001],
decalRotation: 0,
decalScale: 1,
decalType: { type: 'Wall', wallUuid: wall.wallUuid }
}
addDecal(wall.wallUuid, decal);
}
}
}}
onPointerMissed={() => {
if (selectedWall && selectedWall.userData.wallUuid === wall.wallUuid) {
setSelectedWall(null);
}
}}
onPointerMissed={() => { setSelectedWall(null) }}
>
<MeshDiscardMaterial />
{wall.decals.map((decal) => (
<DecalInstance visible={visible} key={decal.decalUuid} decal={decal} />
))}
</mesh>
</mesh>
);

View File

@@ -42,7 +42,7 @@ function WallInstances() {
return (
<>
{!toggleView && (
{!toggleView && walls.length > 1 && (
<>
<mesh name='Walls-Group'>
<Geometry useGroups>

View File

@@ -7,12 +7,13 @@ import useModuleStore from '../../../store/useModuleStore';
function WallGroup() {
const { togglView } = useToggleView();
const { setSelectedWall } = useBuilderStore();
const { setSelectedWall, setSelectedDecal } = useBuilderStore();
const { activeModule } = useModuleStore();
useEffect(() => {
if (togglView || activeModule !== 'builder') {
setSelectedWall(null);
setSelectedDecal(null);
}
}, [togglView, activeModule])

View File

@@ -15,7 +15,7 @@ export default function PostProcessing() {
const { selectedWallItem } = useSelectedWallItem();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventSphere } = useSelectedEventSphere();
const { selectedAisle, selectedWall } = useBuilderStore();
const { selectedAisle, selectedWall, selectedDecal } = useBuilderStore();
function flattenChildren(children: any[]) {
const allChildren: any[] = [];
@@ -85,6 +85,21 @@ export default function PostProcessing() {
xRay={true}
/>
)}
{selectedDecal && (
<Outline
selection={selectedDecal}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{deletableFloorItem && (
<Outline
selection={flattenChildren(deletableFloorItem.children)}

View File

@@ -16,6 +16,9 @@ interface BuilderState {
outsideMaterial: string;
insideMaterial: string;
// Decal Settings
selectedDecal: Object3D | null;
// Aisle General
selectedAisle: Object3D | null;
aisleType: AisleTypes;
@@ -41,6 +44,9 @@ interface BuilderState {
setWallHeight: (height: number) => void;
setWallMaterial: (material: string, side: 'inside' | 'outside') => void;
// Setters - Decal
setSelectedDecal: (decal: Object3D | null) => void;
// Setters - Aisle General
setSelectedAisle: (aisle: Object3D | null) => void;
setAisleType: (type: AisleTypes) => void;
@@ -75,6 +81,8 @@ export const useBuilderStore = create<BuilderState>()(
outsideMaterial: 'Default Material',
insideMaterial: 'Material 1',
selectedDecal: null,
selectedAisle: null,
aisleType: 'solid-aisle',
aisleWidth: 0.1,
@@ -139,6 +147,14 @@ export const useBuilderStore = create<BuilderState>()(
});
},
// === Setters: Decal ===
setSelectedDecal: (decal: Object3D | null) => {
set((state) => {
state.selectedDecal = decal;
})
},
// === Setters: Aisle General ===
setSelectedAisle: (aisle: Object3D | null) => {

View File

@@ -80,11 +80,22 @@ interface Decal {
decalUuid: string;
decalName: string;
decalId: string;
decalType: WallDecal | FloorDecal;
decalPosition: [number, number, number];
decalRotation: number;
decalScale: number;
}
interface WallDecal {
type: 'Wall';
wallUuid: string;
}
interface FloorDecal {
type: 'Floor';
floorUuid: string;
}
interface Wall {
wallUuid: string;
points: [Point, Point];