Remove material strips from roof, stair, and fence panels
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
import {
|
import {
|
||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
getEffectiveRoofSurfaceMaterial,
|
|
||||||
type MaterialSchema,
|
|
||||||
type RoofNode,
|
type RoofNode,
|
||||||
type RoofSegmentNode,
|
type RoofSegmentNode,
|
||||||
RoofSegmentNode as RoofSegmentNodeSchema,
|
RoofSegmentNode as RoofSegmentNodeSchema,
|
||||||
@@ -33,7 +31,6 @@ export default function RoofPanel() {
|
|||||||
const updateNode = useScene((s) => s.updateNode)
|
const updateNode = useScene((s) => s.updateNode)
|
||||||
const createNode = useScene((s) => s.createNode)
|
const createNode = useScene((s) => s.createNode)
|
||||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||||
const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget)
|
|
||||||
|
|
||||||
const node = useScene((s) =>
|
const node = useScene((s) =>
|
||||||
selectedId ? (s.nodes[selectedId as AnyNode['id']] as RoofNode | undefined) : undefined,
|
selectedId ? (s.nodes[selectedId as AnyNode['id']] as RoofNode | undefined) : undefined,
|
||||||
@@ -56,35 +53,6 @@ export default function RoofPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const materialTargetRole =
|
|
||||||
selectedMaterialTarget &&
|
|
||||||
selectedMaterialTarget.nodeId === node?.id &&
|
|
||||||
(selectedMaterialTarget.role === 'top' ||
|
|
||||||
selectedMaterialTarget.role === 'edge' ||
|
|
||||||
selectedMaterialTarget.role === 'wall')
|
|
||||||
? selectedMaterialTarget.role
|
|
||||||
: null
|
|
||||||
const materialPickerValue =
|
|
||||||
node && materialTargetRole ? getEffectiveRoofSurfaceMaterial(node, materialTargetRole) : {}
|
|
||||||
|
|
||||||
const handleTargetedMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
if (!(node && materialTargetRole)) return
|
|
||||||
handleUpdate(buildRoofSurfaceMaterialPatch(node, materialTargetRole, material, undefined))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleTargetedMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
if (!(node && materialTargetRole)) return
|
|
||||||
handleUpdate(
|
|
||||||
buildRoofSurfaceMaterialPatch(node, materialTargetRole, undefined, materialPreset),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -260,22 +228,6 @@ export default function RoofPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
{materialTargetRole ? null : (
|
|
||||||
<div className="mb-3 rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-[11px] text-muted-foreground">
|
|
||||||
Click the roof surface you want to edit. Materials apply to one target at a time.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<MaterialPicker
|
|
||||||
disabled={!materialTargetRole}
|
|
||||||
hideSideControl
|
|
||||||
nodeType="roof"
|
|
||||||
onChange={handleTargetedMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleTargetedMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={materialPickerValue.materialPreset}
|
|
||||||
value={materialPickerValue.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type RoofNode, useRegistry } from '@pascal-app/core'
|
import { type RoofNode, useRegistry, useScene } from '@pascal-app/core'
|
||||||
import { getRoofMaterialArray, NodeRenderer, useNodeEvents, useViewer } from '@pascal-app/viewer'
|
import { getRoofMaterialArray, NodeRenderer, useNodeEvents, useViewer } from '@pascal-app/viewer'
|
||||||
import { useEffect, useMemo, useRef } from 'react'
|
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { roofDebugMaterials, roofMaterials } from './roof-materials'
|
import { roofDebugMaterials, roofMaterials } from './roof-materials'
|
||||||
|
|
||||||
@@ -10,6 +10,9 @@ export const RoofRenderer = ({ node }: { node: RoofNode }) => {
|
|||||||
const ref = useRef<THREE.Group>(null!)
|
const ref = useRef<THREE.Group>(null!)
|
||||||
|
|
||||||
useRegistry(node.id, 'roof', ref)
|
useRegistry(node.id, 'roof', ref)
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
useScene.getState().markDirty(node.id)
|
||||||
|
}, [node.id])
|
||||||
|
|
||||||
const handlers = useNodeEvents(node, 'roof')
|
const handlers = useNodeEvents(node, 'roof')
|
||||||
const debugColors = useViewer((s) => s.debugColors)
|
const debugColors = useViewer((s) => s.debugColors)
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
import {
|
import {
|
||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
getEffectiveStairSurfaceMaterial,
|
|
||||||
type LevelNode,
|
type LevelNode,
|
||||||
type MaterialSchema,
|
|
||||||
type StairNode,
|
type StairNode,
|
||||||
type StairRailingMode,
|
type StairRailingMode,
|
||||||
type StairSegmentNode,
|
type StairSegmentNode,
|
||||||
@@ -66,7 +64,6 @@ export default function StairPanel() {
|
|||||||
const updateNode = useScene((s) => s.updateNode)
|
const updateNode = useScene((s) => s.updateNode)
|
||||||
const createNode = useScene((s) => s.createNode)
|
const createNode = useScene((s) => s.createNode)
|
||||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||||
const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget)
|
|
||||||
|
|
||||||
const node = useScene((s) =>
|
const node = useScene((s) =>
|
||||||
selectedId ? (s.nodes[selectedId as AnyNode['id']] as StairNode | undefined) : undefined,
|
selectedId ? (s.nodes[selectedId as AnyNode['id']] as StairNode | undefined) : undefined,
|
||||||
@@ -97,35 +94,6 @@ export default function StairPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const materialTargetRole =
|
|
||||||
selectedMaterialTarget &&
|
|
||||||
selectedMaterialTarget.nodeId === node?.id &&
|
|
||||||
(selectedMaterialTarget.role === 'railing' ||
|
|
||||||
selectedMaterialTarget.role === 'tread' ||
|
|
||||||
selectedMaterialTarget.role === 'side')
|
|
||||||
? selectedMaterialTarget.role
|
|
||||||
: null
|
|
||||||
const materialPickerValue =
|
|
||||||
node && materialTargetRole ? getEffectiveStairSurfaceMaterial(node, materialTargetRole) : {}
|
|
||||||
|
|
||||||
const handleTargetedMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
if (!(node && materialTargetRole)) return
|
|
||||||
handleUpdate(buildStairSurfaceMaterialPatch(node, materialTargetRole, material, undefined))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleTargetedMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
if (!(node && materialTargetRole)) return
|
|
||||||
handleUpdate(
|
|
||||||
buildStairSurfaceMaterialPatch(node, materialTargetRole, undefined, materialPreset),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -557,22 +525,6 @@ export default function StairPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
{materialTargetRole ? null : (
|
|
||||||
<div className="mb-3 rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-[11px] text-muted-foreground">
|
|
||||||
Click the stair surface you want to edit. Materials apply to one target at a time.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<MaterialPicker
|
|
||||||
disabled={!materialTargetRole}
|
|
||||||
hideSideControl
|
|
||||||
nodeType="stair"
|
|
||||||
onChange={handleTargetedMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleTargetedMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={materialPickerValue.materialPreset}
|
|
||||||
value={materialPickerValue.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,15 +126,19 @@ export const RoofSystem = () => {
|
|||||||
pendingRoofUpdates.delete(id)
|
pendingRoofUpdates.delete(id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const group = sceneRegistry.nodes.get(id) as THREE.Group
|
const group = sceneRegistry.nodes.get(id) as THREE.Group
|
||||||
if (group) {
|
if (!group) continue
|
||||||
const mergedMesh = group.getObjectByName('merged-roof') as THREE.Mesh | undefined
|
|
||||||
if (mergedMesh?.visible !== false) {
|
const mergedMesh = group.getObjectByName('merged-roof') as THREE.Mesh | undefined
|
||||||
// Only rebuild when visible — RoofEditSystem re-triggers via markDirty on edit mode exit
|
if (!mergedMesh) continue
|
||||||
updateMergedRoofGeometry(node as RoofNode, group, nodes)
|
|
||||||
roofsProcessed++
|
if (mergedMesh.visible !== false) {
|
||||||
}
|
// Only rebuild when visible — RoofEditSystem re-triggers via markDirty on edit mode exit
|
||||||
|
updateMergedRoofGeometry(node as RoofNode, group, nodes)
|
||||||
|
roofsProcessed++
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingRoofUpdates.delete(id)
|
pendingRoofUpdates.delete(id)
|
||||||
}
|
}
|
||||||
}, 5) // Priority 5: run after all other systems have settled
|
}, 5) // Priority 5: run after all other systems have settled
|
||||||
|
|||||||
Reference in New Issue
Block a user