v0.0.72
murow 0.0.72 changelog
Spanning 20 commits across feat/prefab-bucket. The headline is PrefabBucket — a typed, async-loadable, renderer-agnostic registry for assets — alongside major work on glTF support: lazy animation loading, multi-skin handling, weight normalization, and stutter-free runtime clip resync.
New API: PrefabBucket
A unified way to register, parse, and retrieve game-ready assets (glTF models, spritesheets, primitives). Strongly typed: bucket.get('annie') returns the exact GltfPrefab for that spec, with animationList narrowed to declared clips.
const bucket = new PrefabBucket('3d')
.add({ type: 'gltf', id: 'annie', src: '/annie.glb', animations: ['Attack1'] })
.add({ type: 'grid', id: 'floor', size: 20, step: 0.33, lineWidth: 0.001 });
await bucket.load();
const annie = bucket.get('annie'); // narrowly typedadd(spec)/addAll([specs])— chainable, validates duplicate IDs.get(id)— typed return, autocompletes known IDs.getAllByType(type)— narrowed by type discriminator.entries(),size,loaded.
glTF support — major reworking
Multi-skin handling. Mixamo files (and others) split a character across multiple skins. The parser now picks one canonical skin and validates / remaps the rest. Throws clearly when skeletons are genuinely independent.
Weight normalization. Unnormalized skin weights (Mixamo emits these) are now normalized per-vertex at parse time. Previously caused vertices to explode out of model space.
Helpers extracted. packages/murow/src/renderer/gltf/helpers.ts now hosts pure utilities: decodeGltfContainer, extractPrimitiveAttributes, bakeTransformIntoVertices, validateAndBuildSkinRemaps, remapSkinAttributes. fourCC helper for GLB magic constants. UINT16_MAX named instead of 65535.
Lazy animation loading. New methods on glTF prefabs:
await bucket.get('annie').loadAnimations(['Attack2', 'Death']);
bucket.get('annie').unloadAnimations(['Attack2']);
bucket.get('annie').resetAnimations(); // back to spec-declared set
bucket.resetAnimations(); // every prefab- Spec field
freezeAnimations: trueopts out at parse time (releases source JSON, saves ~1–6 MB per prefab). Type-narrows lazy methods away on frozen prefabs. - New
ParsedGltfSourceretains the parsed JSON + accessor reader for runtime clip decoding. - Push-based notification via
bucket.events(EventSystem<PrefabBucketEvents>) — renderers subscribe once instead of polling per frame.
No-stutter runtime resync (Option D). The compute kernel is built with generous capacity budgets (2× current size). When clips change, the renderer attempts an in-place writeBuffer upload first. Only buffer-exceeded loads trigger a kernel rebuild. Measured: lazy load went from 150ms stutter to ~3ms upload on typical loads.
Spec / type system
GltfSpecgainsfreezeAnimations?: boolean.- Spec literal
animations: ['Run', 'Idle']narrowsprefab.animationsto{ Run: 'Run', Idle: 'Idle' }(typo-safe) andprefab.animationListto the tuple. - Spec literal
freezeAnimations: trueremovesloadAnimations/unloadAnimations/resetAnimationsat the type level. prefab.metadatais typed from spec literal —metadata: { scale: 0.035 }flows through.PrefabBucketEventsexposed for backends that want to subscribe.PrefabParserContextthreaded into parsers.freezeAnimationsand the existinganimationsnarrowing co-exist via separate conditional-type branches.
Renderer integration
WebGPU3DRenderernow accepts aprefabsbucket option and self-sizes its skinned-instance + bone-matrix budgets from the bucket's stats.maxBonesPerSkinauto-derives from the bucket's largest rig (was capped at 32, breaking models with more joints).- New
GltfClipResyncCoordinator(inwebgpu/src/3d/) bridgesbucket.eventsto per-skin resync work. Cleanly disposed onrenderer.destroy(). renderer.destroy()unsubscribes from the bucket and resets runtime animations so the bucket is reusable.- Backwards-compat path retained:
renderer.loadGltf(url)still works.
Internal refactors
- Renderer moved to
murow/renderer(was scattered).base/directory hostsrenderer.ts,renderer-2d.ts,renderer-3d.ts.math.ts,types.tsconsolidated here. - glTF parsing split into
parser.ts,skin-parser.ts,helpers.ts(was one monolithic file). - Spritesheet code split into
parser.ts+helpers.ts(same pattern). - Skeletal animation: extracted
decodeAnimationClipandbuildNodeToJointMapfromparseAnimationsso single-clip lazy decoding can reuse them. SkeletalAnimation.replaceClips()— atomically swap clip lists and return old→new id remap for in-flightanimStaterepair.- Uses
core/lerpfor keyframe interpolation (dogfooding). kernel.uploadPackedToKernel()extracted — writes viadevice.queue.writeBufferdirectly, ~20× faster than the previous TypeGPU-mediated path.
Renames & breaking changes
- Spec field
url→src(matches HTML/<img>convention). - Renderer test files standardized.
Base3DRendererinterface clarified.prefab.metadatais always present at runtime (defaults to{}).parseGltfsignature: now takesParseGltfOptions(was inline{ animations?: string[] }).buildAnimationKernelsignature: now requiresAnimationKernelBudgets.
Docs & examples
packages/murow/src/renderer/README.md— substantially expanded.packages/murow/README.mdandpackages/webgpu/README.mdupdated to usePrefabBucket.- Examples (
bouncing-3d-webgpu,bouncing-sprites-webgpu) migrated. - Benchmarks:
benchmarks/renderer/programs/gltf.tsrewritten to use the new API, includes lazy-load demo button and animation-savings logger.
Bench infra
BASEBOX_BASEURLenv passed to Basebox for staging deployments.
Tests
857 tests passing across 39 files. Coverage spans the new prefab-bucket, glTF parser, skeletal animation, multi-skin handling, weight normalization, and clip-resync paths.
What's Changed
New Contributors
Full Changelog: v0.0.71...v0.0.72