Adjustments pass: shadow bias, polygon editor UX, space-detection load fix#502
Conversation
normalBias 0.02 was too small a texel offset for the building-fit 1024 shadow map and brought back self-shadowing acne. Settle on normalBias 0.08 (0.07 and below acnes, 0.1 reads detached) plus depth bias -0.0005 to suppress the residual acne that a normal bias alone couldn't clear. Also adds a `?debug=shadowcamera` diagnostic that draws a CameraHelper for each shadow camera so the fitted frustum can be inspected while tuning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- vertex/midpoint cylinders ignore scene depth like the edge arrows so they stay visible through walls and slabs; vertex radius 0.1 -> 0.08, midpoint 0.06 -> 0.05, midpoints use the brighter arrow shade at rest - during any drag only the active handle stays mounted (other arrows, vertices, edge bars and the cross disappear) so the gesture reads clearly - handles set pointer cursors: move for vertices/midpoints/cross, and a screen-space direction-aware resize cursor for the edge arrows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The ceiling corner brackets' hit boxes caught drag-time hover (spatial events keep firing during host drags by design), set hoveredId to the ceiling and flashed the ceiling grid mid-gesture — e.g. while dragging a slab polygon vertex. Unmount the brackets while ANY interaction scope is active, and gate CeilingSystem's hover-driven grid reveal on idle scope + no inputDragging as a second layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two holes in the wall-driven auto slab sync: - initSpaceDetectionSync started with an empty baseline, so scene hydration (one atomic setScene) read as 'every wall changed' and ran a full detection pass on every load, recreating auto slabs the user had deleted. Seed the baseline from the store at init and treat a level's first snapshot as baseline — detection now only reacts to in-session wall edits. - matchesManualFootprint required mutual coverage, so a single manual slab spanning multiple rooms never suppressed those rooms' auto slabs (only a fraction of it lies inside each room). Suppression now only asks whether the ROOM is substantially covered by the union of manual slabs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c229c93. Configure here.
| // first wall can't close a room anyway. Record it (below) and only | ||
| // react to subsequent changes. | ||
| const previous = previousSnapshots.get(levelId) | ||
| if (previous === undefined) continue |
There was a problem hiding this comment.
Load skips room space sync
High Severity
The initSpaceDetectionSync logic, intended to prevent resurrecting deleted auto slabs, incorrectly skips runSpaceDetection for a level's initial appearance. This prevents spaces from being populated on scene load (affecting features like room painting) and leaves rooms without auto slabs/ceilings when walls are pasted or batch-created onto a new or empty level.
Reviewed by Cursor Bugbot for commit c229c93. Configure here.
| onDragCommitRef.current?.() | ||
| updatePreviewPolygon(null) | ||
| setDragState(null) | ||
| }, [onPolygonChange, updatePreviewPolygon]) |
There was a problem hiding this comment.
Stuck cursor after drag cancel
Medium Severity
The document.body cursor can remain stuck (e.g., move or resize) if a drag operation is aborted by an external polygon prop update. This occurs because the dragState is nulled without explicitly calling clearBodyCursor in this scenario, unlike on drag commit or component unmount.
Reviewed by Cursor Bugbot for commit c229c93. Configure here.
| state.scene.add(helper) | ||
| } | ||
| helper.update() | ||
| } |
There was a problem hiding this comment.
Shadow debug helpers never removed
Low Severity
When ?debug=shadowcamera is active, CameraHelper instances are added to the scene but never removed or disposed. This causes them to accumulate in the scene graph, potentially leaking resources, especially when the Lights component unmounts or debug mode changes.
Reviewed by Cursor Bugbot for commit c229c93. Configure here.
| onPointerLeave: (e) => { | ||
| e.stopPropagation() | ||
| setHoveredMidpoint(null) | ||
| clearBodyCursor() |
There was a problem hiding this comment.
Midpoint drag clears move cursor
Medium Severity
Midpoint onPointerLeave always clears the body cursor, and midpoints unmount as soon as any dragState exists. Starting a midpoint insert therefore tears down the hovered handle, fires leave, and drops the move cursor for the rest of the gesture.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c229c93. Configure here.


What does this PR do?
A pass of fixes and adjustments across four areas:
viewer): the previousnormalBiasdrop to 0.02 reintroduced self-shadowing acne on the building-fit 1024 shadow map. Settle onnormalBias 0.08+depth bias -0.0005— empirically the smallest acne-free combo that keeps wall shadows visually attached (0.07 acnes, 0.1 reads detached). Also adds a?debug=shadowcameraCameraHelper diagnostic for inspecting the fitted shadow frustum.editor): vertex/midpoint handles ignore scene depth like the edge arrows so walls can't hide them; slightly smaller (vertex 0.1→0.08, midpoint 0.06→0.05); midpoints use the brighter arrow shade at rest. During any drag only the active handle stays mounted so the gesture reads clearly. Handles set pointer cursors —movefor vertices/midpoints/cross, direction-aware resize cursors for the edge arrows (outward normal projected to screen space).editor): the ceiling corner brackets caught drag-time hover and flashed the ceiling grid mid-gesture (e.g. while dragging a slab polygon vertex). They now unmount while ANY interaction scope is active, andCeilingSystem's hover-driven grid reveal is gated on idle scope + noinputDragging.core): two holes in the wall-driven auto slab sync. (1)initSpaceDetectionSyncstarted with an empty baseline, so scene hydration read as "every wall changed" and ran detection on every load, recreating deleted auto slabs. The baseline now seeds from the store and a level's first snapshot is baseline — detection only reacts to in-session wall edits. (2)matchesManualFootprintrequired mutual coverage, so one manual slab spanning multiple rooms never suppressed those rooms' auto slabs; suppression now asks only whether the room is substantially covered by the union of manual slabs.How to test
bun dev, open a scene with walls and slabs.?debug=shadowcamerato see the fitted shadow frustum.cd packages/core && bun test— 940 tests pass.Screenshots / screen recording
N/A — will add a recording if reviewers want one; behaviors are quick to reproduce with the steps above.
Checklist
bun devbun checkto verify)mainbranchNote
Medium Risk
Space-detection baseline and footprint logic affect auto slab/ceiling lifecycle on every wall edit and scene load; other changes are mostly editor/viewer UX and rendering tuning.
Overview
Fixes wall-driven auto slab sync so deleted auto slabs stay gone after reload and manual coverage suppresses replacement correctly.
initSpaceDetectionSyncseeds wall snapshots from the loaded store and treats a level’s first snapshot as hydration-only, so detection runs on in-session wall edits, not onsetScene.matchesManualFootprintdrops the per-manual-slab “mostly inside room” filter and only checks whether the room is substantially covered by all manual polygons, fixing one slab spanning multiple rooms.Ceiling UI stops flashing mid-gesture: corner brackets unmount whenever interaction scope is not idle, and
CeilingSystemonly treats ceiling hover as grid-reveal when scope is idle andinputDraggingis false.Polygon editor polish: vertex/midpoint handles render through geometry (
depthTest: false), slightly smaller handles, brighter resting midpoints, only the active handle during drags, anddocument.bodycursors (move/ screen-aligned resize on edge arrows).Viewer shadows: tune
normalBias/ depth bias for acne vs attachment on the building-fit 1024 map; add?debug=shadowcamerato visualize shadow frusta.Reviewed by Cursor Bugbot for commit c229c93. Bugbot is set up for automated code reviews on this repo. Configure here.