Skip to content

v0.0.72

Choose a tag to compare

@mococa mococa released this 23 May 05:27
· 88 commits to main since this release
ed9c0eb

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 typed
  • add(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: true opts out at parse time (releases source JSON, saves ~1–6 MB per prefab). Type-narrows lazy methods away on frozen prefabs.
  • New ParsedGltfSource retains 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

  • GltfSpec gains freezeAnimations?: boolean.
  • Spec literal animations: ['Run', 'Idle'] narrows prefab.animations to { Run: 'Run', Idle: 'Idle' } (typo-safe) and prefab.animationList to the tuple.
  • Spec literal freezeAnimations: true removes loadAnimations / unloadAnimations / resetAnimations at the type level.
  • prefab.metadata is typed from spec literal — metadata: { scale: 0.035 } flows through.
  • PrefabBucketEvents exposed for backends that want to subscribe.
  • PrefabParserContext threaded into parsers.
  • freezeAnimations and the existing animations narrowing co-exist via separate conditional-type branches.

Renderer integration

  • WebGPU3DRenderer now accepts a prefabs bucket option and self-sizes its skinned-instance + bone-matrix budgets from the bucket's stats.
  • maxBonesPerSkin auto-derives from the bucket's largest rig (was capped at 32, breaking models with more joints).
  • New GltfClipResyncCoordinator (in webgpu/src/3d/) bridges bucket.events to per-skin resync work. Cleanly disposed on renderer.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 hosts renderer.ts, renderer-2d.ts, renderer-3d.ts. math.ts, types.ts consolidated 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 decodeAnimationClip and buildNodeToJointMap from parseAnimations so single-clip lazy decoding can reuse them.
  • SkeletalAnimation.replaceClips() — atomically swap clip lists and return old→new id remap for in-flight animState repair.
  • Uses core/lerp for keyframe interpolation (dogfooding).
  • kernel.uploadPackedToKernel() extracted — writes via device.queue.writeBuffer directly, ~20× faster than the previous TypeGPU-mediated path.

Renames & breaking changes

  • Spec field urlsrc (matches HTML/<img> convention).
  • Renderer test files standardized.
  • Base3DRenderer interface clarified.
  • prefab.metadata is always present at runtime (defaults to {}).
  • parseGltf signature: now takes ParseGltfOptions (was inline { animations?: string[] }).
  • buildAnimationKernel signature: now requires AnimationKernelBudgets.

Docs & examples

  • packages/murow/src/renderer/README.md — substantially expanded.
  • packages/murow/README.md and packages/webgpu/README.md updated to use PrefabBucket.
  • Examples (bouncing-3d-webgpu, bouncing-sprites-webgpu) migrated.
  • Benchmarks: benchmarks/renderer/programs/gltf.ts rewritten to use the new API, includes lazy-load demo button and animation-savings logger.

Bench infra

  • BASEBOX_BASEURL env 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

  • @mococa made their first contribution in #5

Full Changelog: v0.0.71...v0.0.72