HardYards/tools/character/windclip_candidates/README.md
type-two 91bc391633 Lane E, Sprint 13: no wind clip — the pipeline can't supply one, and the lean was never a clip
Gate 2.4 asked for lean/stagger/brace clips staged by wind band. The answer,
measured twice, is that the asset shouldn't exist.

1. The lean cannot be a clip. _rotOnly drops Hips.quaternion from every clip at
   load (player.js:53, applied :239). Parsed the shipped pack to confirm rather
   than trust the read: 17 clips, 195 channels each, 64 survive. A lean baked
   into a clip's hips is discarded. And stripping it is right — the wind's
   bearing swings (the change; site_02's venturi) and a canned clip has one
   fixed lean axis, so only the sim can aim it. Same trick as the knockdown and
   climbY. The lean is D's root pitch; thresholds proposed off Beaufort 7
   ("inconvenience felt when walking against the wind" = 13.9 m/s), which lands
   storm_01 never leaning, storm_03 gusting through the line, storm_02 in gale.

2. Mixamo has no wind animations. Searched the real API, not assumed: shiver
   returns zero, brace returns eleven ledge-hang clips, wind returns Catwalk
   Turn. The factory header already recorded this in Sprint 3.

Rendered the two real candidates on a skinned rig, twice each — as authored and
as the game shows them. Pushing is a squat shoving a couch. Leaning is a man
waiting for a bus. Both rejected. Both would have passed every check we own:
they load, they're metre-scale, their nodes survive. Dims can't answer "is this
the right shape" — the bike rule, catching a whole feature this time.

Brace and stagger already ship (TakeCover, StumbleBack, Reaction). D should wire
the root lean first; I'll hand-author a WindPosture additive into the gap we can
both see, if one remains. Spine rotation survives _rotOnly, so it would land.

Evidence, harness and repro in tools/character/windclip_candidates/, plus the
four pipeline traps I hit in order so the next lane doesn't.

selftest 335/0/0, unchanged — no asset added, no shipped file touched.
2026-07-18 00:45:22 +10:00

4.2 KiB

Wind clips — the Mixamo candidates, and why they're rejected

Lane E, Sprint 13 gate 2.4 ("wind clips for the player — lean / stagger / brace, staged by wind band, through the Mixamo pipeline").

Outcome: no clip shipped. The Mixamo pipeline cannot supply a wind lean, and the lean was never going to be a clip in the first place. Both halves of that are measured, and the evidence is in this folder. Full argument in THREADS [E] 2026-07-17/18.

1. Mixamo has no wind animations

Searched the real API (fetch.cjs search, on ultra), not assumed:

term what comes back
brace eleven Braced Hang ledge-climbing clips
wind Standing Idle 04, Catwalk Walk Turn 180
shiver nothing, 0 results
stagger three walks (Swagger Walk, Walking, Walking)
lean Leaning On A Wall, Leaning, One Shoulder Lean
push Pushing, Push Up, Button Pushing

The factory's own header already recorded half of this in Sprint 3 — "Hammering / Sweeping Floor / Bracing don't exist on Mixamo — skipped." This is the case the asset-pipeline skill names directly: truly bespoke clips don't exist on Mixamo — hand-author, don't keep searching.

2. The two real candidates, rendered and rejected

skinshot.py films a skinned Mixamo FBX twice: as authored, and as the game shows it (Hips rotation cleared, which is what player.js:_rotOnly does at load). The second row is the honest one.

  • rejected_Pushing.png — not a wind lean. A deep squat shoving something heavy along the ground, standing up at the end. It reads as moving a couch. The forward angle I hoped to borrow is a squat, not a lean.
  • rejected_Leaning.png — a casual weight-on-the-back-foot lean, the "propped against a bar" pose. Hands slack, no tension, near-identical across all five frames. A person waiting for a bus, not one in a gale.

Neither is close enough to fix by retiming. Shipping either would have passed every check we have — they load, they're metre-scale, their nodes survive — and been wrong on screen. That's the bike rule: the verify can't answer "is this the right shape."

3. The useful technical result

In both strips the raw and stripped rows are near-identical, which is worth knowing: these clips carry their lean in the spine, and spine rotation survives _rotOnly intact. So a hand-authored posture (spine curl, shoulder hunch, arm up) would survive the loader. What cannot survive is a lean authored into the hips_rotOnly drops Hips.quaternion from every clip (player.js:53, applied :239). Measured on the shipped pack: 17 clips, 195 channels each, 64 survive.

Which is why the lean belongs in player.sim.js as a root pitch aimed at wind.dirAt(t) — the wind's bearing swings (the change; site_02's venturi) and a canned clip has one fixed lean axis. Same trick as the knockdown and climbY.

Reproducing

# on ultra (the FBX libraries and the Mixamo login live there; JING5 has neither)
mkdir -p /tmp/shadeswind && cd /tmp/shadeswind
cp ~/Documents/mixamo-fetch/fetch.cjs .
printf 'Pushing\nLeaning\n' > wishlist.txt
SKIN=1 node fetch.cjs "X Bot"          # SKIN=1: needs a mesh to judge a silhouette
/Applications/Blender.app/Contents/MacOS/Blender -b --factory-startup \
    -P skinshot.py -- /tmp/shadeswind/out/Pushing.fbx Pushing

Notes for whoever next reaches for this pipeline:

  • fetch.cjs reads its token from a logged-in Brave on ultra via CDP (port 9222). Nobody types a password; the session is already there. It 429s after a handful of exports — space them out.
  • resolveChar defaults to Grandma_mixamo, which is not currently uploaded to the account. X Bot is a Mixamo stock rig and works; for anim-only export the source rig is irrelevant anyway, since _rotOnly keeps rotations only.
  • Anim-only FBX (SKIN unset) has no mesh and renders nothing. Judging a silhouette needs SKIN=1.
  • Don't retarget onto Hum_M_1_mixamo.fbx to preview: its armature carries FBX's 0.01 unit scale and the clip's action doesn't, so the body collapses to ~3 cm. That's the same "peds cannot round-trip through Blender" trap build_player_anims.py documents.