diff --git a/packages/nodes/src/roof/panel.tsx b/packages/nodes/src/roof/panel.tsx index e93b5eb5..114a7cfd 100644 --- a/packages/nodes/src/roof/panel.tsx +++ b/packages/nodes/src/roof/panel.tsx @@ -3,8 +3,6 @@ import { type AnyNode, type AnyNodeId, - getEffectiveRoofSurfaceMaterial, - type MaterialSchema, type RoofNode, type RoofSegmentNode, RoofSegmentNode as RoofSegmentNodeSchema, @@ -33,7 +31,6 @@ export default function RoofPanel() { const updateNode = useScene((s) => s.updateNode) const createNode = useScene((s) => s.createNode) const setMovingNode = useEditor((s) => s.setMovingNode) - const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget) const node = useScene((s) => selectedId ? (s.nodes[selectedId as AnyNode['id']] as RoofNode | undefined) : undefined, @@ -56,35 +53,6 @@ export default function RoofPanel() { [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(() => { setSelection({ selectedIds: [] }) }, [setSelection]) @@ -260,22 +228,6 @@ export default function RoofPanel() { /> - - {materialTargetRole ? null : ( -
- Click the roof surface you want to edit. Materials apply to one target at a time. -
- )} - -
) } diff --git a/packages/nodes/src/roof/renderer.tsx b/packages/nodes/src/roof/renderer.tsx index cd0cf55a..0e9b5a9e 100644 --- a/packages/nodes/src/roof/renderer.tsx +++ b/packages/nodes/src/roof/renderer.tsx @@ -1,8 +1,8 @@ '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 { useEffect, useMemo, useRef } from 'react' +import { useEffect, useLayoutEffect, useMemo, useRef } from 'react' import * as THREE from 'three' import { roofDebugMaterials, roofMaterials } from './roof-materials' @@ -10,6 +10,9 @@ export const RoofRenderer = ({ node }: { node: RoofNode }) => { const ref = useRef(null!) useRegistry(node.id, 'roof', ref) + useLayoutEffect(() => { + useScene.getState().markDirty(node.id) + }, [node.id]) const handlers = useNodeEvents(node, 'roof') const debugColors = useViewer((s) => s.debugColors) diff --git a/packages/nodes/src/stair/panel.tsx b/packages/nodes/src/stair/panel.tsx index 802ba67b..b925a82b 100644 --- a/packages/nodes/src/stair/panel.tsx +++ b/packages/nodes/src/stair/panel.tsx @@ -3,9 +3,7 @@ import { type AnyNode, type AnyNodeId, - getEffectiveStairSurfaceMaterial, type LevelNode, - type MaterialSchema, type StairNode, type StairRailingMode, type StairSegmentNode, @@ -66,7 +64,6 @@ export default function StairPanel() { const updateNode = useScene((s) => s.updateNode) const createNode = useScene((s) => s.createNode) const setMovingNode = useEditor((s) => s.setMovingNode) - const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget) const node = useScene((s) => selectedId ? (s.nodes[selectedId as AnyNode['id']] as StairNode | undefined) : undefined, @@ -97,35 +94,6 @@ export default function StairPanel() { [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(() => { setSelection({ selectedIds: [] }) }, [setSelection]) @@ -557,22 +525,6 @@ export default function StairPanel() { /> - - {materialTargetRole ? null : ( -
- Click the stair surface you want to edit. Materials apply to one target at a time. -
- )} - -
) } diff --git a/packages/viewer/src/systems/roof/roof-system.tsx b/packages/viewer/src/systems/roof/roof-system.tsx index 9fab9f7c..775e79a4 100644 --- a/packages/viewer/src/systems/roof/roof-system.tsx +++ b/packages/viewer/src/systems/roof/roof-system.tsx @@ -126,15 +126,19 @@ export const RoofSystem = () => { pendingRoofUpdates.delete(id) continue } + const group = sceneRegistry.nodes.get(id) as THREE.Group - if (group) { - const mergedMesh = group.getObjectByName('merged-roof') as THREE.Mesh | undefined - if (mergedMesh?.visible !== false) { - // Only rebuild when visible — RoofEditSystem re-triggers via markDirty on edit mode exit - updateMergedRoofGeometry(node as RoofNode, group, nodes) - roofsProcessed++ - } + if (!group) continue + + const mergedMesh = group.getObjectByName('merged-roof') as THREE.Mesh | undefined + if (!mergedMesh) continue + + 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) } }, 5) // Priority 5: run after all other systems have settled