Skip to content

fix(ltk_anim): read and evaluate the newer exporter's compressed animations - #262

Merged
Crauzer merged 2 commits into
mainfrom
fix/anm-flags
Sep 25, 2026
Merged

Crauzer merged 2 commits into
mainfrom
fix/anm-flags

Conversation

@Crauzer

@Crauzer Crauzer commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Compressed animations from the newer exporter crash or collapse in ltk_anim. The latest Janna skin (skin 67) is the current example: ltk-manager reports Invalid 'flags' - got '60' and panics in the preview. Four fixes, each checked on the 324 .anm files in Champions/Janna.wad.client.

1. Unknown flag bits rejected the file. AnimationFlags::from_bits is strict, and shipped files use bits up to 0x20 (Janna's are 0x3C). The flags are now read with from_bits_retain, with const _ = !0. 341 of the game's 34,601 compressed .anm files failed to parse before this.

2. A track with one key panicked. These files store a transform that never changes as a single key, so all four spline keys share one time. The weighted (0x4) path had no t1 == t2 guard, unlike the uniform path. The weights grew to about 1e27, cancelled, and the rotation summed to exactly zero, so Quat::normalize panicked (normalized.is_finite()). The same cancellation silently returned zero for translation and scale. It now returns the key, as the uniform path already did. This affected 35 of Janna's 324 files.

3. A transform without keys stopped playback. The jump cache marks a missing transform with index 0xFFFF, and that index was folded into the frame cursor. The cursor then skipped past every frame, so no key after the jump cache was read and the spline extrapolated. Janna translations reached 27,861,528 units and a second glam assertion panicked. Out of range indices now leave the cursor alone.

4. A transform without keys sampled a zero scale. The hot frame default was Vec3::ZERO, which collapsed 11,627 joint tracks across Janna's files. It is now Vec3::ONE, which matches the rotation default of Quat::IDENTITY.

After the fixes, all 324 Janna files parse, and sequential playback at 120 Hz gives finite poses. Rotation steps match the raw key deltas. The largest remaining jumps (about 2.4 rad in dance_loop) are in the keys themselves.

Flag names. Only 0x4 is read by the client (CompressedAnimationAsset::Evaluate, 16.15). The other names come from a census of every shipped compressed file. 0x2 and 0x8 are deliberately not given a guessed meaning. The type is crate-private, so the rename to SCREAMING_SNAKE_CASE (C-CASE) changes no public API.

bit name evidence
0x1 SUB_FRAME_KEYS With it clear, 99.9% of files have every key on a frame. With it set, only 2.4% do.
0x2 EXPORTER_OPTION_1 No structural difference found. It follows export era.
0x4 USE_KEYFRAME_PARAMETRIZATION The only bit the client reads.
0x8 EXPORTER_OPTION_3 Only in files from the newer exporter.
0x30 PRUNED_TRACKS The two bits always appear together. Every such file leaves out tracks or stores them as one key.

Not in this PR. A joint with no keys for a transform most likely keeps its bind pose. evaluate returns a full transform for every joint, so it cannot say "not animated", and missing rotation and translation still sample as identity. Fixing that needs an API change.

Each fix has a regression test (in tests/compressed_jump_caches.rs) that fails before it.

Made with the help of an LLM. Every fix was checked against the shipped Janna files, and the flag statistics against every shipped compressed .anm.

🤖 Generated with Claude Code

@Crauzer Crauzer changed the title fix(ltk_anim): accept and name every compressed animation flag bit fix(ltk_anim): read and evaluate the newer exporter's compressed animations Sep 25, 2026
@Crauzer
Crauzer merged commit 6e2e898 into main Sep 25, 2026
1 check passed
@Crauzer
Crauzer deleted the fix/anm-flags branch September 25, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant