- Guard _buildCache in merged-outline-node against stale/disposed
Object3D refs that cause TypeError on .id access during render
- Reset children array when duplicating roofs to prevent inconsistent
parent-child relationships (matching existing stair behavior)
- Use obj?.parent check in EditorOutlinerSync to ensure objects are
still in the scene graph before adding to outliner arrays
- Resume temporal state on parse failure to prevent undo/redo freeze
Closes#232
The WebGPURenderer needs its backend initialized before any direct
`.render(scene, camera)` call. The commented-out `// renderer.init()`
said "Only use when using <DebugRenderer />", but the non-debug path
also calls direct render from the post-processing fallback
(post-processing.tsx:318), which throws "Renderer: .render() called
before the backend is initialized" on any browser that falls back to
the WebGL2 backend.
Switching the gl factory to an async function and awaiting init()
before returning is safe in both backends (init() is idempotent and
the async factory is a supported @react-three/fiber v9+ pattern), and
prevents the error in WebGL2 fallback.
`RenderPipeline`, SSGI, and the denoise TSL node imported from
`three/webgpu` and `three/tsl` are all WebGPU-only. On a browser
without `navigator.gpu`, the WebGPURenderer falls back to WebGL2, and
attempting to build the TSL post-processing pipeline either throws or
produces broken output — the scene renders for a few frames, then
goes black as the 3-attempt retry loop fights the direct-render
fallback path in `useFrame`.
This sets `hasPipelineErrorRef.current = true` at pipeline setup time
when `navigator.gpu` is undefined, so `useFrame` takes the existing
direct `renderer.render(scene, camera)` path exclusively and never
tries to build the broken TSL pipeline.
No behavioural change in WebGPU mode — the guard only fires when the
WebGPU API is literally not exposed by the browser. The rare edge
case of `navigator.gpu` being defined but device creation failing at
runtime still falls through the existing try/catch unchanged.
* feat: add polygon movement support to editor and implement dedicated move tools for walls, slabs, and ceilings
* feat: add uv2 attribute to ceiling and slab geometries for lightmap support
* Fix curved wall miters and align roof position sliders
* Fix wall measurement label and extension anchors
* Fix WebGPU renderer initialization order
* fix(editor): scope wall movement to the selected level
---------
Co-authored-by: Pascal <open@pascal.app>
* Add catalog-based material presets across structural nodes
* Fix material picker fallback and wall visible highlights
* Preserve wall materials in selection highlights
* feat: railing on the straight stairs and new fence
* feat: added spiral and curved stairs with bug fix for fence
* feat:fence are linked to each other ... so moving one move the other sharing the same coordinate
* fix: update stair railing logic to include front-side attachments for terminal landings
* Integrate fence rendering into the fence system
* fix: pass nodeId instead of undefined node to WallTreeNode and FenceTreeNode
TreeNode was passing `node` (undefined variable) instead of `nodeId` to
WallTreeNode and FenceTreeNode, causing a runtime ReferenceError.
Updated FenceTreeNode to accept nodeId and look up the node from the
scene store internally, consistent with all other tree node components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: update fence icon with new isometric design
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Aymeric Rabot <aymeric@pascal.app>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When selecting a floor slab or ceiling node, the floating UI buttons now
include a cut-out icon (carbon:cut-out) that directly creates a hole and
enters edit mode. Much more intuitive than navigating to the side panel.
Changes:
- NodeActionMenu: add onAddHole prop with carbon:cut-out icon
- FloatingActionMenu: wire up handleAddHole for slab/ceiling types
- Add ceiling to ALLOWED_TYPES so the floating menu appears for ceilings too
- Separate HOLE_TYPES from DELETE_ONLY_TYPES for cleaner type gating
Sync monorepo PRs #243 and #248 to the open-source editor.
Move/Rotate Building:
- New move-building-tool with grid snapping, R/T rotation, and Esc cancel
- Floating building action menu with move button
- Building helper with keyboard shortcut hints
- BuildingRenderer now applies position and rotation from node data
Building-Relative Coordinate System:
- GridEvent gains localPosition (building-local coords)
- use-grid-events computes building-local intersection from building mesh
- ToolManager wraps building-relative tools in a building-local <group>
- All tools (wall, slab, ceiling, stair, roof, zone, polygon-editor,
placement coordinator) updated to use event.localPosition
- Placement coordinator adds worldToBuildingLocal helper for cursor
position conversion
2D Floorplan Fix:
- SVG layers wrapped in <g transform=rotate(...)> for building rotation
- Pointer-to-plan conversion un-rotates when building is rotated
- Grid events from 2D include localPosition
Store Changes:
- useEditor movingNode union includes BuildingNode
- NodeActionMenu onDelete is now optional (buildings can move but not delete)
Two fixes for stair selection in 2D floorplan not working:
1. floorplan-panel: remove level > 0 restriction on auto-switch to structure
phase. When viewing ANY level in the floorplan while in site phase, auto-enter
structure mode so structural nodes (stairs, walls, slabs) become selectable.
2. selection-manager: expand auto-switch to also cover site phase. Clicking a
structural or furnish element now auto-switches from site → structure/furnish,
just like it already did between structure ↔ furnish ↔ zones.
This fixes the issue where stairs couldn't be selected in 2D and the floating
action menu (move/duplicate/delete) wouldn't appear.
three.js r183 deprecated THREE.Clock, but @react-three/fiber 9.x still
uses it internally per <Canvas> mount. This floods the dev console with
noise on every page load and HMR reload.
Uses three's setConsoleFunction hook to surgically suppress only the
exact Clock deprecation message. All other three.js logs pass through
untouched, including TSL stack-trace warnings.
Safe to remove once R3F v10 stable ships (which replaces Clock with
Timer). Removal condition documented in file header.
Closes#213
Mark stair nodes as dirty when slabs change so stairs properly stack on
top of floor slabs. StairSystem now computes slab-driven elevation by
sampling segment centers against the spatial grid.
Changes:
- spatial-grid-sync: mark stair nodes dirty on slab changes (same level)
- stair-system: syncStairGroupElevation + getStairSlabElevation compute
Y offset from slab data; rotateXZ helper for world-space transforms
- stair-renderer: use position-x/position-z so the system controls Y
The spatial-grid manager, scene-store subscriptions, and the viewer
outliner's Object3D arrays are module-level singletons that previously
lived for the whole page lifetime. When an app unmounted the Editor
(e.g. navigating away from the 3D editor and back in an SPA host) they
retained references to the disposed Three.js scene graph, which caused
the app to freeze on re-entry as the stale data was replayed against a
fresh scene.
- initSpatialGridSync now returns the unsubscribe handle from its
scene-store subscription so callers can detach it.
- initializeEditorRuntime becomes reversible: it wires up the spatial
grid, space detection and SFX bus on mount and returns a teardown
closure that detaches both store subscriptions, clears the spatial
grid manager, and truncates the viewer outliner's selected/hovered
arrays in place.
- The Editor component's runtime-init effect now returns that teardown,
so the singletons are reset cleanly every time the Editor unmounts.
- initSFXBus is now idempotent via an internal guard so repeated
Editor mounts do not stack duplicate mitt listeners.
Co-authored-by: Claude <noreply@anthropic.com>
Stair system (full stack):
- New StairNode + StairSegmentNode schemas with flights, landings, L/U-shapes
- StairSystem: geometry generation with throttled per-frame updates
- Stair tool, edit system, panels, tree node, and renderers
- Event bus types, scene registry, and command palette entries
Scene graph utilities:
- cloneLevelSubtree: deep-clone a level with remapped IDs
- forkSceneGraph: clone + strip scan/guide nodes for project forking
Core improvements:
- Read-only mode on scene store (blocks create/update/delete when locked)
- readOnly guards on node-actions and collection actions
- Upload store for scan/guide file upload handling
Viewer state:
- previewSelectedIds for box-select live preview
- hoverHighlightMode (default/delete) for delete-mode hover outline
Slider controls now use the Pointer Lock API for drag interactions. When dragging a slider label, the cursor locks and movement continues infinitely past the screen edge, matching Unity3D and other professional 3D editors.
- requestPointerLock on drag start, movementX accumulation, exitPointerLock on release
- Falls back to setPointerCapture if PointerLock unavailable
- ESC during drag handled via pointerlockchange listener
- Shift/Ctrl step multipliers preserved
- Undo/redo temporal state correctly managed on all exit paths
Closes#204
- Cast scene.clone() to Scene for GLTFExporter compatibility
- Cast DataView.buffer to ArrayBuffer for STL Blob construction
- Add THREE import for Object3D type in traverse callback
The ceiling renderer was falling back to DEFAULT_CEILING_MATERIAL (an
opaque MeshStandardMaterial) when no material was set on the node.
This made ceilings fully opaque instead of using the intended
transparent grid pattern for scenes created before v0.3.2.
Now uses resolveMaterial() to extract the color from whatever material
config exists (preset, custom, or default white), then always creates
the proper transparent ceiling materials for both faces.
- Add MaterialSchema with 10 presets (white, brick, concrete, wood, glass, metal, plaster, tile, marble, custom)
- Add material field to Wall, Slab, Door, Window, Ceiling, Roof, RoofSegment nodes
- Create MaterialPicker UI component with preset selection and custom properties
- Update all renderers to support material rendering with caching
- Add Material section to all node panels (WallPanel, SlabPanel, DoorPanel, WindowPanel, CeilingPanel, RoofPanel, RoofSegmentPanel)
- Update AGENTS.md with Material System documentation
The original implementation used spread ({ ...node }) which only shallow-copies.
Nested objects (asset, polygon, position tuples, metadata) would share references
between the original and cloned scene, meaning mutations to the clone could
corrupt the original.
Also removed resolveChildRefId helper — children arrays in the schema are always
string IDs, so the object-with-id handling was dead code.