Cottontail Lux: Weapon_Rabbit floats near the ground
Skin affected: Lux Skin 29 (ID 99029, "Cottontail Lux"). Symptom: Weapon_Rabbit hangs still near the ground instead of riding the tips of her weapons. Everything else animates correctly. Status: UNRESOLVED. R2 is hand-patched; the extractor still reproduces it.
Surfaced when a new chroma (99081) triggered an automatic re-extraction, replacing a 2024 build that was correct.
What the bake does wrong
BakeJointSnapEvents (packages/extractor/LeagueConvert/IO/Skin/Skin.cs) rewrites a snap victim's track into world space and reparents it to the skeleton root. On Lux it reparents five bones where only three are legitimate victims:
| bone | 2024 build (correct) | current build |
|---|---|---|
WeaponTop | parent R_Hand, t=[-1.1, -20.3, 0.5] | parent root, t=[-46.5, 96.9, -2.8] |
WeaponTopRabbit | parent WeaponTopHead, t=[-0.2, 15.5, -0.1] | parent root, t=[-46.3, 114.1, -2.7] |
WeaponBottomRabbit | parent WeaponBottomHead, t=[0.1, -15.6, -0.1] | parent root, t=[-46.5, 21.6, -2.7] |
Weapon, WeaponTop and WeaponBottom are named by snap events and are meant to be reparented. The two *Rabbit bones are not named by any event. They end up holding the world position of the rig's snap_WeaponTopRabbit / snap_WeaponBottomRabbit joints, which is why they sit still while the weapons move.
Ruled out
- Not a regression from the 2026-08-04 extractor changes. A binary built before them produces byte-identical output on the same WAD.
- Lux's
animations/skin29.bindeclares only three distinct override bones:weapon,weapontop,WeaponBottom. - Nine of her twenty snap events carry null
mStartFrame/mEndFrame.HasExplicitFrameRangefilters those correctly, so this is not a repeat of the Illaoi420037null-frame regression. - No snap events in
lux.bin,skins/root.binoranimations/root.bin. BakeJointSnapEventshas no descendant cascade, andSkeletonJoint.Reparentmutates only the joint it is called on.
Best remaining lead
ResolveBoneName (Skin.cs:1641) resolves mJointNameToOverride from a hash through BinHashes:
if (property is BinTreeHash h && HashTables.HashTables.BinHashes.TryGetValue(h.Value, out var resolved))
return resolved;CDragon's bin.json shows already-resolved names, but our lookup is independent. A colliding or wrong BinHashes entry could return WeaponTopRabbit where the data means WeaponTop — the same class of failure as Dark Star Shaco, which was an ElfHash collision in MapTracksToJoints.
Next step: log the resolved victimNames set during a 99029 extraction and compare it against the three expected names. The WAD is cached, so this reproduces in seconds:
./packages/processor/binaries/wad-extractor convert-wad Lux --skins 29 --champion-id 99 -o /tmp/lux --no-downloadWorkaround currently in place
lol/models/lux/99029 on both the khada and khada-staging buckets holds the good 2024 model files (model.glb, model-lite.glb, both -compressed.wasm, regenerated .glb.gz, .usdz, .webp, textures) merged with the current chroma set including 99081. Chroma folders contain only textures (Body.png, Rabbit.png, render), so they carry no rig and were safe to take from the new build.
Any re-extraction of 99029 reintroduces the bug and overwrites this.