feat: clothing lane, character sources, and DCC bridges
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>
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# 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)
|
||||
|
||||
1. **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.
|
||||
2. **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).
|
||||
3. **Avatar (for the seated export only):** any stock CC male.
|
||||
4. **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)
|
||||
1. **Move** (not copy) the 4 FBX from Downloads into **`exchange/INBOX/`** (the repo's
|
||||
existing external-input drop, alongside `lena_tpose_legs5_input.fbx`), renamed
|
||||
`Salsa_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**. `.fbx` is
|
||||
LFS-tracked here, so a normal commit + `tinqs push` carries them.
|
||||
2. 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)
|
||||
1. Launch check: `--ping`; if iClone is closed, start
|
||||
`A:\Program Files (x86)\iClone 8\Bin64\iClone.exe`, poll ping (~60–90 s).
|
||||
Load a stock CC male (`RLPy.RFileIO.LoadFile`), apply
|
||||
`LoadMotion(Male_Sitting_Pose.rlMotion, t=0, avatar)` — **check the returned
|
||||
RStatus** (don't assume success), and verify a clip landed on the motion track.
|
||||
2. **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.
|
||||
3. **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:104` rejects only `<5`-frame
|
||||
`*TempMotion` takes (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).
|
||||
3. Export FBX → `exchange/incoming-fbx/seatbase1.fbx`. Probe
|
||||
`RLPy.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.
|
||||
4. Sanity: file is multi-MB-ish and ≥300 frames. Commit + `tinqs push` (etiquette as
|
||||
Phase 0.2).
|
||||
5. 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)
|
||||
0. *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.
|
||||
1. `mixamo_retarget.py` on the 4 salsa FBX →
|
||||
`assets/quaternius/mixamo/salsa.glb` (clips `Salsa01..04` — batch mode per the
|
||||
kevin precedent). **Read back the GLB's `animations[].name` immediately** and use
|
||||
those exact strings in every later JSON/bake reference — don't assume the intended
|
||||
names survived the batch tool (GLM review).
|
||||
2. `cc_retarget.py` on `seatbase1.fbx` → `exchange/converted-glb/seatbase1.glb`, clip
|
||||
`SeatBase1`. First-export QA: **A-pose arm check** in the showcase.
|
||||
3. Copy `salsa.glb` into ariki-game (`assets/quaternius/mixamo/`), `seatbase1.glb` →
|
||||
`dancegen/`.
|
||||
|
||||
### Phase 3 — Mac side: composite, bake, loop, QC
|
||||
1. **Iterate live first**: `nd_02.json` in `assets/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.
|
||||
2. **Pre-bake the approved composite to a GLB** (registry requires a baked pair):
|
||||
generalize `tools/bake_run_punch.mjs` into a parameterized composite baker
|
||||
(`tools/bake_composite.mjs`): inputs = ordered (upper-clip, duration) list + lower
|
||||
clip; mask predicate **verbatim** from `bake_run_punch.mjs:103`
|
||||
(`root || pelvis || /thigh|calf|foot|ball/i`); output `exchange/converted-glb/nd_02.glb`
|
||||
clip `ND02`. **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).
|
||||
3. 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 so `loop_fix.py`'s natural-loop trim becomes
|
||||
viable, or reorder segments so the reversal lands on the most symmetric one. Both
|
||||
variants must pass `loop_qc.py` (exit 0); nd_01 set the 0.00-seam bar.
|
||||
4. Ship: GLBs + `.import` sidecars into ariki-game, final `nd_02.json` pointing at the
|
||||
baked `nd_02/ND02` (+`_pp`), `game.sh import`, verify GLB `animations[].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_02` and `nd_02_pp` pass `loop_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.mjs` is 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
|
||||
`<5` frames + `*TempMotion` name only); "Route A exports mixamorig bone names"
|
||||
(characterized import retargets onto the CC rig before export).
|
||||
Reference in New Issue
Block a user