Bulk import of the working lanes that were living untracked on the PC. Content: - characters/ Lena/male body lanes, bakes, texture work, run logs - clothing/ garment pipeline, configs, gates, contract docs - garments/ MD-authored garment sources (.zprj/.zpac) - UAL-Lib/ Universal Animation Library 2 source (.blend/.fbx/.glb) - tools/ blender_bridge, iclone_bridge, md_bridge, tailor, glm_agent - docs/, plans/, dev/, .agents/plans/ Repo hygiene: - .gitattributes: LFS now covers .blend, .zprj, .zpac, .obj, .npy and the Reallusion .iAvatar/.ccAvatar/.ccRestore containers. Without this the ~3.8 GB in this commit would land as raw blobs. .png/.jpg are left out on purpose — ~250 are already tracked raw and converting them would rewrite every one without shrinking history. - .gitignore: exclude /accurig/ (~1 GB AccuRig program files, redistributable from Reallusion, nothing authored here) and /dev/null/ (git-lfs hook copies dropped by a `>/dev/null` redirect on Windows). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
Plan v3: seated hearth dance (nd_02) — seated pose + salsa upper-body sway
Status: reviewed (independent Opus pass, GLM-4.6 API pass, Fable self-review), awaiting Jeremy's go/route-confirmation · Author + PC executor: Fable 5 session 2026-07-21 (via TinqsBridge) · Mac-side steps: later session
Goal
A looping dance for a hearth ceremony — villagers seated around the hearth,
seated the entire time (lower body pinned to Jeremy's seated pose), upper body
swaying through 4 Mixamo salsa clips. Registry id: provisional nd_02 (pack
nd_02, clip ND02, loop pair nd_02_pp/ND02PP); ceremony adoption is Jeremy's
call later (no hearth slot exists in the DanceType enum — closest candidate is
fire_dance_01, NOT assumed).
§0 Inputs (confirmed by Jeremy 2026-07-21)
- Seated pose:
A:\Reallusion Content\Reallusion Custom\Animation\Motion\External Motion\Male_Sitting_Pose.rlMotion(39 KB — likely a single-frame pose; see Phase 1.2). ⚠ Provenance check at first eyeball: hearth circle implies ground-seated; if this pose is chair-height the pelvis will float in-game. - Sway sources: the 4 Mixamo FBX in
C:\Users\Jeremy\Downloads\:Salsa Dancing.fbx(1.05 MB),Salsa Dancing (1).fbx(2.62 MB),Salsa Dancing (2).fbx(1.84 MB),Salsa Dancing (3).fbx(0.55 MB). - Avatar (for the seated export only): any stock CC male.
- Length target: 20–30 s @ 60 fps overall; ~5–7 s per salsa segment.
Route decision (changed in v2 after review)
Primary is now Route B: retarget the salsa FBX directly with the pipeline's own
mixamo_retarget.py (purpose-built, proven on northern_soul/run_to_dive) and
combine with the seated pose via the game-side upper/lower mask composite,
pre-baked to a GLB. iClone's role shrinks to its one irreplaceable contribution:
exporting Jeremy's seated pose as a clip (via the bridge).
Why (review consensus): Route A put two unprobed RLPy behaviors on its critical
path (LoadMotion-with-FBX — which even if it loads may skip Mixamo→CC
characterization and produce garbage — and ExportFbxFile option coverage), added a
lossy Mixamo→CC→game double-retarget plus the A-pose-bind risk, and then threw away
its own lower body at bake time. Route B is one proven retarget per source, iterates
via hot-reload JSON in the test bed (~0.5 s) instead of a full PC→Mac round-trip, and
gets beat-quantized transitions for free. Route A survives as fallback (§Route A) if
the salsa arm styling retargets badly onto the game skeleton.
The lower-body guarantee moves into the composite bake itself — the baked clip's
lower-mask tracks come from the seated clip and nowhere else. The separate
lock_lower_body.py from plan v1 is dropped (it only existed to repair Route A's
full-body take).
Route B (primary)
Phase 0 — salsa files into the repo (me, PC, now)
- Move (not copy) the 4 FBX from Downloads into
exchange/INBOX/(the repo's existing external-input drop, alongsidelena_tpose_legs5_input.fbx), renamedSalsa_Dancing_01.fbx…Salsa_Dancing_04.fbx. Note: plan v1 sent these to the Reallusion content folder on A:; review blocker — the Mac retarget step can only see files that are in the repo..fbxis LFS-tracked here, so a normal commit +tinqs pushcarries them. - Commit (explicit paths only, co-author line
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>),tinqs push.
Phase 1 — seated clip out of iClone (me, PC, via bridge)
- Launch check:
--ping; if iClone is closed, startA:\Program Files (x86)\iClone 8\Bin64\iClone.exe, poll ping (~60–90 s). Load a stock CC male (RLPy.RFileIO.LoadFile), applyLoadMotion(Male_Sitting_Pose.rlMotion, t=0, avatar)— check the returned RStatus (don't assume success), and verify a clip landed on the motion track. - Ground-seat precheck (GLM review): after applying the pose, read the pelvis/hip world Y via the bridge and log it. Chair-height ≫ ground-height — if it reads like a chair sit, stop and show Jeremy before wasting the export.
- Hold the pose ≥300 frames (5 s @60): set the play range so the export is a real
take. Verified against code:
cc_retarget.py:104rejects only<5-frame*TempMotiontakes (frame count, not motion content) — a 300-frame static hold passes. Extend by re-placing the pose key at the range end (or Jeremy sets the clip length in the timeline — one drag). - Export FBX →
exchange/incoming-fbx/seatbase1.fbx. ProbeRLPy.RFileIO.ExportFbxFile(*args)for: Target Tool Preset Blender, 60 fps, Range = All, Include Motion ON, Preserve Bone Names. This is the only ExportFbxFile use left in the primary route — if the API can't reach the options, Jeremy clicks the export dialog once and the route loses nothing. - Sanity: file is multi-MB-ish and ≥300 frames. Commit +
tinqs push(etiquette as Phase 0.2). - RTime/RFps handling: build times with RTime constructors from the stub; get numeric fps via RFps methods, never repr.
Phase 2 — Mac side: retarget everything (later session / handoff)
- Cheap check first (Path 1): scan kevin/UAL packs for an existing ground-seated idle — if one matches Jeremy's intended look better than the iClone export, offer the swap at eyeball time; Jeremy's pose remains the default.
mixamo_retarget.pyon the 4 salsa FBX →assets/quaternius/mixamo/salsa.glb(clipsSalsa01..04— batch mode per the kevin precedent). Read back the GLB'sanimations[].nameimmediately and use those exact strings in every later JSON/bake reference — don't assume the intended names survived the batch tool (GLM review).cc_retarget.pyonseatbase1.fbx→exchange/converted-glb/seatbase1.glb, clipSeatBase1. First-export QA: A-pose arm check in the showcase.- Copy
salsa.glbinto ariki-game (assets/quaternius/mixamo/),seatbase1.glb→dancegen/.
Phase 3 — Mac side: composite, bake, loop, QC
- Iterate live first:
nd_02.jsoninassets/dances/with 4 chained moves, each{layers: [{mask:"upper", clip:"salsa/SalsaNN"}, {mask:"lower", clip:"seatbase1/SeatBase1"}]}— hot-reloads in the dance test bed (ask Jeremy before launching the game). Tune segment order/durations/bpm here where a change costs 0.5 s. - Pre-bake the approved composite to a GLB (registry requires a baked pair):
generalize
tools/bake_run_punch.mjsinto a parameterized composite baker (tools/bake_composite.mjs): inputs = ordered (upper-clip, duration) list + lower clip; mask predicate verbatim frombake_run_punch.mjs:103(root || pelvis || /thigh|calf|foot|ball/i); outputexchange/converted-glb/nd_02.glbclipND02. Verify gate built in: assert every lower-mask bone track in the output matches the seated clip's pose within an epsilon (per-channel max deviation, e.g. 1e-3 — resampling/quantization makes strict equality false-fail; GLM review) — exit nonzero otherwise (lesson from the boat prop: agent-authored gates must assert what the eyeball would catch). - Loop pair:
pingpong_bake.py→nd_02_pp/ND02PP. Caveat (GLM review): ping-ponging a 4-segment composite plays A→B→C→D→D→C→B→A — the reversal is exposed across distinct segments, not one homogeneous sway; it may read oddly even with a clean seam. Mitigations, judged at the test-bed eyeball: bookend the bake with short seated-base (arms-quiet) segments soloop_fix.py's natural-loop trim becomes viable, or reorder segments so the reversal lands on the most symmetric one. Both variants must passloop_qc.py(exit 0); nd_01 set the 0.00-seam bar. - Ship: GLBs +
.importsidecars into ariki-game, finalnd_02.jsonpointing at the bakednd_02/ND02(+_pp),game.sh import, verify GLBanimations[].name == "ND02", registry row: origin "seated hearth sway — upper Mixamo salsa ×4, lower iClone Male_Sitting_Pose (seatbase1)". ariki-game commits stay local.
Route A (fallback only — if salsa retargets badly onto the game skeleton)
Assemble in iClone instead: import the salsa FBX via the UI with the Mixamo
characterization profile (the route that produced Talking_mixamo_com.rlMotion;
LoadMotion(fbx) may be probed as an optimization but is NOT assumed — loading
without characterization would splat raw mixamorig: tracks onto the CC rig), place
seated base + salsa segments + seated base on the timeline, Jeremy eyeballs, export
one take → cc_retarget.py → then a lower-body lock IS needed again: same §Phase 3.2
baker degenerates to it (upper = the take itself, lower = seatbase1) — no separate
tool. Known extra risks: transition hard-cuts via API placement, double-retarget
quality, A-pose bind.
Acceptance
nd_02andnd_02_pppassloop_qc.py(exit 0).- Bake verify gate proves lower-mask tracks constant == seated pose.
- Upper body visibly cycles all 4 salsa flavors without harsh cuts (test-bed eyeball).
- Ground-seat height looks right at the hearth (pose-provenance check §0.1).
- Registry row added; salsa sources + seatbase committed in this repo via LFS; ariki-game not pushed.
Review trail
- v1 reviewed 2026-07-21 by independent fresh-context Opus agent (GLM unavailable on this PC): 1 blocker (Route B files never reach the Mac), 4 major (lock-script unspec'd; frame-0 lock could freeze a standing pose; Route A's critical path rested on two unprobed RLPy behaviors; Route A discards most of its own work), 4 minor (2-frame guard on the seated export; loop-pair citation; co-author line; trim/pose-provenance). All verified factual claims in v1 held.
- v2 by Fable: Route B promoted; lock tool folded into the composite baker's verify gate; salsa destination changed from A:\Reallusion content to repo INBOX + LFS; seated export spec'd ≥300 frames; kevin-pack Path-1 check added; co-author line added.
- v2 reviewed 2026-07-21 by GLM-4.6 (direct API call — key from tinqs-docs/.env).
Accepted → v3: epsilon-tolerance verify gate (strict equality would false-fail on
resampling); pelvis-Y ground-seat precheck before export; read back salsa clip names
from the GLB instead of assuming; pingpong-reversal caveat for a 4-segment composite
- bookend/reorder mitigations; check LoadMotion's returned RStatus. Rejected with
evidence: "LoadMotion needs LoadFile+ApplyMotion" (contradicts the verified stub
signature
RFileIO.LoadMotion(strFilePath, kTime, spObject), RLPy.py:13700 — still probe-first per plan); "no Node toolchain exists" (tools/bake_run_punch.mjsis in this repo and the gltf-transform route is documented in the animation-creation skill); "static 300f take trips the no-motion guard" (cc_retarget.py:104 gates on<5frames +*TempMotionname only); "Route A exports mixamorig bone names" (characterized import retargets onto the CC rig before export).
- bookend/reorder mitigations; check LoadMotion's returned RStatus. Rejected with
evidence: "LoadMotion needs LoadFile+ApplyMotion" (contradicts the verified stub
signature