feat: interactive doors in walkthrough view + swing-direction consistency#253
Closed
b9llach wants to merge 1 commit intopascalorg:mainfrom
Closed
feat: interactive doors in walkthrough view + swing-direction consistency#253b9llach wants to merge 1 commit intopascalorg:mainfrom
b9llach wants to merge 1 commit intopascalorg:mainfrom
Conversation
…ency Hover a door and press F to open or close it. The leaf animates around its hinge edge with a cubic ease-in-out; mid-swing toggles reverse smoothly from wherever the leaf is. Works in both walkthrough and editor modes (pointer-hover source in editor, crosshair raycast in walkthrough). Bundles two consistency fixes that surface the same hingesSide/swingDirection fields the floorplan already exposed.
nnhhoang
pushed a commit
to nnhhoang/editor
that referenced
this pull request
Apr 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Hover a door, press F, it opens. Press F again, it closes. Smooth cubic ease-in-out on a 450 ms swing around the hinge edge, with mid-animation toggles reversing from wherever the leaf is rather than snapping. Works in both walkthrough and editor modes — pointer-hover drives the target in the editor; a camera-forward raycast drives it in walkthrough (with a Press F to open / close hint that appears under the crosshair when the target is within ~2.5 m).
Rolls in two consistency fixes that I found while wiring this up — both are surface-level mismatches between what the door panel lets you set and what actually renders.
1. Interactive open / close
Core — new hinge-pivot group in
DoorSystem. The leaf, handle, door closer, and panic bar now live inside a namedleafPivotgroup positioned at the hinge edge (with a zero-offset childleafOffsetso existing door-center-relative coordinates still work). RotatingleafPivot.rotation.yswings the free edge around the correct axis. The frame posts, head, threshold, hinges, and wall cutout stay on the root mesh where they belong. On dirty-rebuild the group is preserved, so whatever rotation was applied survives geometry regeneration (so e.g. flippinghingesSidemid-swing repositions the pivot without resetting the rotation to 0).Viewer — new
DoorInteractiveSystemandDoorInteractionHint. The system owns:use-keyboard.tsdoes).useViewer.crosshairHoveredDoorId.useViewer.doorAnimand pushes an eased rotation onto each door'sleafPivot. Store writes only happen on toggle, so subscribers don't re-render during the swing.DoorInteractionHintis a plain DOM overlay that portals into whatever layout already sits over the canvas (the editor'sFirstPersonOverlayin this PR). The viewer package exports it so the read-only/viewer/[id]route could use it too.Store —
useViewer.doorAnim,toggleDoor,pruneDoorAnim,crosshairHoveredDoorId. Door animation state is{ from, target, startedAt }per door id — intentionally not persisted across full reloads (excluded frompartialize) so opening a bunch of doors while reviewing a scene doesn't leak into the next session.2. Swing direction matches the 2D floorplan arc
The floorplan panel already draws a swing arc from
hingesSide+swingDirection. The 3D renderer didn't rotate the leaf in a way that matched the arc — the initial sign convention I tried for the swing animation swung the free edge opposite the floorplan's curve. Fixed inDoorInteractiveSystemso the rotation sign agrees with the floorplan convention: "inward" swings the free edge toward the wall's interior-side perpendicular, "outward" the other way,hingesSidepicks the pivot edge. Editing either field in the door panel now updates both views the same way.3. Handle auto-derived from
hingesSide(on both faces)Two small bugs in
DoorSystemthat I hit while testing:handleSidefield with its own segmented control in the panel. FlippinghingesSide(e.g. to correct the swing) left the handle stranded on the hinge edge, which is never what a real door looks like. The renderer now always places the handle on the side opposite the hinges, derived fromhingesSidedirectly.handleSideis still in the schema (backward-compat for existing scenes) but no longer affects rendering, and the redundant panel control is removed.+Zface had a backplate + lever — from the other room the door looked like it had no handle. The renderer now stamps a mirrored pair on the-Zface too. The asymmetric hardware (door closer, panic bar) stays single-sided on purpose, since those really are one-sided in practice (push-side vs pull-side).How to test
bun dev, open any scene with a door.hingesSideorswingDirectionin the panel → the leaf repositions around the new pivot and continues with the correct rotation direction. Confirm there's no longer a Handle Side control in the panel.hingesSide × swingDirectioncombinations.use-keyboard.tsonly bails whenwalkthroughMode === true; flat 3D / floorplan F keeps its existing behaviour.bun check,bun check-types,bun run buildall clean on the touched files.Scope
packages/core/src/systems/door/door-system.tsx— leaf-pivot group, handle-side auto-derive, handle on both facespackages/viewer/src/store/use-viewer.ts—doorAnim,toggleDoor,pruneDoorAnim,crosshairHoveredDoorId, shared easing helper + duration/angle constantspackages/viewer/src/systems/door/door-interactive-system.tsx— newpackages/viewer/src/systems/door/door-interaction-hint.tsx— newpackages/viewer/src/index.ts— exportDoorInteractionHintpackages/viewer/src/components/viewer/index.tsx— mount<DoorInteractiveSystem />packages/editor/src/hooks/use-keyboard.ts— letDoorInteractiveSystemclaim F in walkthroughpackages/editor/src/components/editor/first-person-controls.tsx—Open door [F]in the controls hint; mount<DoorInteractionHint />packages/editor/src/components/ui/panels/door-panel.tsx— remove the redundant Handle Side segmented controlDoorNode; the animation state is runtime-only in the viewer store.handleSideis preserved in the schema for existing scenes, just no longer load-bearing.handleSide, it's a safe follow-up to drop from the schema in a later PR.Checklist
bun devbun checkpasses on the touched files — verified viabiome checkat@biomejs/biome@^2.4.6)mainbranch