fix(doors): bake open-animation clips for every operation door + un-flipped rest pose#444
Merged
Conversation
…ket/barn doors Only swing doors (hinged/double/french) baked an open clip into the GLB — they carry a `pascalSwingLeaf` marker the exporter reads. Every operation door type (sliding, pocket, barn, folding, garage-sectional/rollup/tiltup) baked its `operationState` straight into mesh vertex positions at build time, so the exporter had no re-poseable node to sample and the artifact never flagged them `openable`. Give operation doors the same build-once + pose-at-t split windows already use. Each builder now emits its moving parts in a named group at the CLOSED pose, and `poseDoorMovingParts` (the single source of truth, shared by the live system and the GLB exporter) drives the open motion: - sliding/pocket/barn: rigid leaf translation - garage-tiltup: rigid hinge about the lintel - folding: hinged accordion chain (nested groups, per-joint fold) - garage-sectional: per-panel groups posed along the overhead curve - garage-rollup: the one type whose live geometry changes (slats roll onto a drum, which a glTF clip can't express) keeps its full-detail live rebuild; the curtain is wrapped in a top-pivoted group the exporter scales up into the lintel as the baked approximation. The exporter samples each operation door's motion into keyframe tracks (16 segments) so the non-linear rigs (curve, accordion) stay faithful, and stamps `extras.openable` + `extras.clips` so any glTF consumer can play it. Tests: per-type kinematics (groups build, rest closed, open) + sliding/roll-up clip baking (sampled position/scale tracks). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…que per node
Two issues surfaced testing the baked viewer:
- Folding door folded toward +z (into the room) — the joint rotation sign
was inverted, so the accordion opened the wrong way ("weird position").
Flip to `(prevDirection - direction) * foldAngle` so leaves fold toward
−z, matching the original inline rig. Verified panel-for-panel against the
original formula at every operationState.
- Openable clips were named by display name (`<name>: open`), but the baked
viewer drives playback by clip name (`useAnimations` maps name → action).
Several windows share the name "Window 1", so their clips collapsed to one
action and triggering any one opened the first. Key the clip name by node
id (`<id>: open`) — unique, matching the item-loop convention; the
human-readable name still lives in `extras.label`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`poseDoorMovingParts` assigned a single euler axis (`group.rotation.y` / `.x`). The live system was fine because the group's euler stays a clean (0, y, 0). But the GLB exporter clones the door and decomposes its matrix, which re-derives a gimbal-flipped euler (x=z=π) for any rotation beyond ±90° — folding panels reach ~158°. The reset to t=0 then only zeroed `.y`, leaving the π residue on x/z and baking a 180°-flipped rest pose (panels folded out toward a wrong position even when closed). Set the full euler triple via `.set()` in every pose branch so the other two axes are always zeroed, clearing any decomposed residue. Add a regression test that exports an open folding door and asserts an identity rest pose for all panels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bundled ceiling-fan model.glb was stale (no animation clip, two slots). Replace it with the variant matching production storage: an `On` animation clip and a third `slot_base` paint slot. Same dimensions/offset, so no catalog metadata change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The walkthrough rode the default 50° orbit camera, which feels cramped on foot. Both walkthrough controllers (baked GlbWalkthroughController and the parametric WalkthroughControls fallback) now set a shared WALKTHROUGH_FOV = 60 on enter and restore the prior FOV on exit, leaving orbit framing untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(viewer): walkthrough FOV 60° + refresh ceiling-fan model
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 61f3921. Configure here.
Fixes the lone biome organizeImports error so the quality gate passes on the post-#448 base. Type-check (9/9), biome, and all suites green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Bake an open-animation clip into the GLB for every openable/slideable door driven by the edition panel — not just hinged/swing doors. Previously sliding, pocket, barn, folding, and all garage doors baked their
operationStatestraight into mesh vertices at build time, so the exported GLB had no re-poseable node and no open clip.How
Gives operation doors the same build-once + pose-at-t split the windows already use:
door-system.tsxemits its moving parts in a named group at the closed pose.poseDoorMovingParts(node, mesh, t)is the single source of truth for the open motion — the live system poses after each rebuild, and the GLB exporter poses a clone to sample keyframes.glb-export.tsbakeDoorClipdispatches operation types tobakeOperationDoorClip(samples 16 segments → position/quaternion/scale tracks for any moving group; resets closed) vs the renamedbakeSwingDoorClip.Per-type rigs: sliding/pocket/barn = leaf translate; garage-tiltup = rigid hinge about the lintel; folding = nested-group accordion chain; garage-sectional = per-panel groups along the overhead curve. garage-rollup is the one exception — its slats genuinely vanish onto a drum (not expressible as a glTF clip), so it keeps its live rebuild and the exporter scales a top-pivoted curtain group as the agreed approximation.
Follow-up fixes folded in
+zinto the room) — corrected to fold−z, parity-tested panel-for-panel.${node.name}: open, so multiple same-named windows/doors collided (the baked viewer keysuseAnimationsby clip name) — now${id}: open(unique; display name stays inextras.label).x=z=π) for any rotation beyond ±90° (folding reaches ~158°); the pose reset only zeroed.y, leaving the π residue. Fixed by setting the full euler triple in every pose branch.Tests
door-animation.test.ts— per-type kinematics.glb-export.test.ts— sliding (sampled position) + rollup (sampled scale) clips, and a regression asserting an identity rest pose for an open folding door.Validation
Baked a real project end-to-end locally (headless worker → served
lod0.glb): all door-fold panels rest at identity[0,0,0,1]with the open clip targeting every panel; sliding/pocket/barn/folding clips present with correct group targets.Note
Medium Risk
Touches door mesh construction, GLB animation baking, and live posing for many door types; regressions could show wrong rest poses or broken open clips in exported GLBs.
Overview
Operation doors (sliding, pocket, barn, folding, garage types) now follow the same build closed + pose at t pattern as windows: moving parts live in named groups,
poseDoorMovingPartsdrives live state and GLB sampling, andbakeOperationDoorClipwrites sampled position/rotation/scale tracks (rollup uses a scale approximation; live rollup still rebuilds slats).Open clip names change from
${node.name}: opento${id}: openso duplicate display names do not collide in bakeduseAnimationsplayback; labels stay inextras.label.Folding doors are refactored to a nested panel chain with a full euler reset so export no longer bakes a 180°-flipped rest when saved open. Walkthrough mode applies shared
WALKTHROUGH_FOV(60°) in both walkthrough controllers.Reviewed by Cursor Bugbot for commit b0313a1. Bugbot is set up for automated code reviews on this repo. Configure here.