feat(bake): bake catalog item clips (fan spin) into the GLB + play in viewer

A catalog GLB ships its own animation clips (a ceiling fan's spin), but those
clips aren't in the editor scene graph, so the bake couldn't see them. Add an
`itemClipRegistry` (core, type-only three) that the item renderer fills with the
resolved clip per node while the scene is live; the GLB export reads it and
re-emits each item's clip onto the baked subtree, rebinding tracks to the cloned
spinning node's uuid. Catalog node names repeat across instances and the glTF
roundtrip rebinds by name, so the targeted node is uniquified per item
(`<id>__lamp_018`) — every fan animates independently.

The baked viewer plays these as looping ambient motion: `<id>: loop` clips are
set to LoopRepeat (not the door/window LoopOnce) and GlbItemAnimation drives
them off each item's toggle (lit/spinning by default).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-06-24 18:13:48 -04:00
co-authored by Claude Opus 4.8
parent d80ffd67d5
commit 0514ec1858
6 changed files with 145 additions and 6 deletions
@@ -0,0 +1,19 @@
import type * as THREE from 'three'
export type ItemClipEntry = {
/** The catalog clip to re-emit (e.g. a fan's "On" spin). */
clip: THREE.AnimationClip
/** Plays looping in the baked viewer (ambient motion) vs once. */
loop: boolean
}
/**
* Catalog-item animation clips the bake needs to re-emit. A catalog GLB ships
* its own clips (the live item renderer loads + plays them), but those clips
* are not part of the editor scene graph, so the GLB export can't see them on
* its own. The item renderer registers the resolved clip per node id while the
* scene is live; `glb-export` reads this and retargets the clip onto the baked
* item subtree. Door/window motion is synthesized separately and never goes
* here. Keyed by node id; cleared with the rest of the scene refs on unload.
*/
export const itemClipRegistry = new Map<string, ItemClipEntry>()
+1
View File
@@ -35,6 +35,7 @@ export type {
ZoneEvent,
} from './events/bus'
export { emitter, eventSuffixes } from './events/bus'
export { type ItemClipEntry, itemClipRegistry } from './hooks/scene-registry/item-clip-registry'
export {
sceneRegistry,
useRegistry,