feat(gltf): node animation + Sprite-aligned animation API; Light3d as a world renderable#1504
Merged
Conversation
84c571c to
5b171a9
Compare
… a world renderable
Adds glTF node/TRS animation and unifies the 2D/3D animation + lighting APIs.
Animation
- glTF node animation: assets with animation channels load as a rig-driven
GLTFModel that keeps the node hierarchy intact (an animated parent carries
its children). Per-frame TRS sampling (LERP/SLERP/STEP; CUBICSPLINE value),
allocation-free pose path. Parser emits the node graph + animation clips.
- Sprite-aligned API on both Sprite and GLTFModel via a shared
parseAnimationOptions helper: setCurrentAnimation(name, { loop, speed,
onComplete, next }), getAnimationNames, animationspeed, play/pause/stop
(additive + non-breaking on Sprite — legacy string/fn/no-arg forms preserved).
Lighting (unify 3D with 2D)
- Light3d is now a world Renderable managed exactly like Light2d: add it to the
world and the active stage auto-tracks it; ambient is a Light3d type:"ambient".
LightingEnvironment removed (folded into stage active-light set + packMeshLights).
- Both lights co-located in src/lighting/; Light2d converted to TypeScript.
Materials / loader
- Mesh.textureRepeat (honors glTF sampler wrapS/wrapT, default REPEAT) so tiling
UVs sample correctly instead of clamping flat (tracked limitation: #1503).
- External glTF resources: external .bin + image uris resolved relative to the
asset URL, crossOrigin forwarded.
Fixes
- Camera3d.isVisible no longer NaN-culls sizeless grouping containers (and their
whole subtree) — infinite-bounds containers are always visible.
Example: glTF Animated Model (Kenney Blocky Characters, CC0) with clip selector,
speed, play/pause/stop, and drag-to-orbit.
~120 new tests (parser graph/animations/wrap, sampler, GLTFModel, lighting,
Sprite play/pause/stop, parseAnimationOptions). Full suite 4237/0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5b171a9 to
e8b01f4
Compare
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.
Builds on the glTF/GLB scene loader (#1502) with node/TRS animation and unifies the 2D ↔ 3D animation and lighting APIs.
Animation
GLTFModelthat keeps the node hierarchy intact (an animated parent carries its children). Per-frame TRS sampling (LERP/SLERP/STEP;CUBICSPLINEvalue), allocation-free pose path (preallocated per-node world buffers). The parser emits the node graph + animation clips.SpriteandGLTFModelvia a sharedparseAnimationOptionshelper:setCurrentAnimation(name, { loop, speed, onComplete, next }),getAnimationNames,animationspeed, andplay/pause/stop. Additive and non-breaking onSprite(legacy string / function / no-arg forms preserved).Lighting (unify 3D with 2D)
Light3dis now a worldRenderable, managed exactly likeLight2d:world.addChild(new Light3d({ direction, color, intensity }))and the active stage auto-tracks it; ambient is aLight3dwithtype: "ambient".LightingEnvironmentis removed (folded into the stage's active-light set + apackMeshLightshelper).src/lighting/;Light2dconverted to TypeScript to matchLight3d.Materials / loader
Mesh.textureRepeat— honors the glTF samplerwrapS/wrapT(default REPEAT) so tiling UVs (authored outside[0,1]) sample correctly instead of clamping flat. Known limitation tracked in Mesh.textureRepeat mutates the shared per-image TextureAtlas.repeat (wrap mode is image-global) #1503..binbuffers + imageuris resolved relative to the asset URL, withcrossOriginforwarded.preloadMTLloads each material'smap_Kdrelative to the.mtl(parity with the glTF external-texture path), so an OBJ model's textures come for free; preloading the model + material is enough. A missing texture is warned + skipped (white-pixel fallback), not fatal.Fixes
Camera3d.isVisibleno longer NaN-culls sizeless grouping containers (and their whole subtree) — infinite-bounds containers are always visible (matchesCamera2d).Example
New glTF Animated Model example (Kenney Blocky Characters, CC0): walking character under
Camera3d, clip selector + speed + play/pause/stop, and drag-to-orbit.Tests / docs
GLTFModel, lighting +packMeshLights,Spriteplay/pause/stop,parseAnimationOptions,Camera3dinfinite-bounds,Mesh.textureRepeat, OBJmap_Kdauto-load). Full suite 4239 passed / 15 skipped / 0 failed; lint + tsc + doc-gen clean.Related
map_Kdauto-load).Mesh.textureRepeatshared-atlas wrap mode); related to TextureCache.get(image, frame): framewidth/frameheight refinement is a no-op for every atlas format #1489 / Refactor TextureCache and Batcher base class for renderer-agnostic abstraction #1410 (TextureCache refactor family).🤖 Generated with Claude Code