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:
2026-08-06 15:55:43 -07:00
parent 3363209cac
commit 3ba86b2ea8
558 changed files with 68622 additions and 8 deletions
@@ -0,0 +1,392 @@
# Plan: Clothing-pipeline unification — one entry point, QC gates at every seam
**Status:** Implemented 2026-07-31 (phases 14 built; catalog JSON still deferred) · **Author:** Fable 5 session 2026-07-31 · **Implementer:** 5-agent parallel build + integration pass, 2026-07-31 — see §8
## 0. Context (you have no other context — read this fully)
The clothing lane turns a garment idea into an outfit part an ariki-game character wears.
It has three halves that work but do not talk to each other:
1. **MD upstream** (this repo): Marvelous Designer 2024, driven over a TCP bridge
(`tools/md_bridge.py`, port 18900). Per-garment scripts in `tools/tailor/` draft
panels, sew, drape on the Lena avatar FBX, texture, and snapshot. Session start is a
human click (Plugin > TinqsMDBridge); MD's UI freezes while a script runs.
2. **Blender downstream** (this repo): `clothing/garment_pipeline.py` — seven
config-driven headless stages (`census prepare fit reduce bake skin export`), each
checkpointing a `.blend` + QA renders into `work/<name>/`, exporting per-slot GLBs
onto the shared 65-bone Quaternius skeleton into
`ariki-game/assets/quaternius/outfits/<set>/`.
3. **Game side** (`ariki-game`, read-only for this repo's tooling by convention):
hand-edit `src/Character/OutfitCatalog.cs` to register the item, force a Godot
reimport, spawn `ClothingTestBed` via `tools/game.sh`, click through Idle/Walk/Dance,
and eyeball the result.
The lane is PROVEN — the downloaded-dress pilot (2026-07-30) and the MD-authored kapa
haka set (pari + piupiu, 2026-07-31) are in game. But both kapa haka pieces shipped with
**pose-dependent defects invisible to every QA artifact the pipeline produces**: the pari
neckline gapes open in walk/dance, and a thigh punches through the piupiu skirt. All
pipeline QA renders are rest pose — the one pose that cannot fail. A separate agent owns
those two weight fixes; **this document is the pipeline-level response**: an assessment,
a design that combines the three halves into one entry point, and the QC gates that
would have caught both bugs before the game.
Source of truth for how the lane works *today*: `.agents/wiki/architecture/clothing-lane.md`
and `clothing/README.md`. This document is a proposal for how it *should* work; on
adoption, fold the outcome into `clothing-lane.md`.
---
## 1. Assessment
### 1.1 What already works (keep it)
- **Config-driven downstream.** New garment = new JSON in `clothing/configs/`, not new
code. The schema already expresses island→part mapping, bodyshell parts, alignment,
fit masks, weld bands, proxies, weight modes, textures, per-part tri budgets.
- **Checkpointed stages.** `00_census.blend … 50_skin.blend` mean any stage can be
re-run in isolation; resume semantics exist for free.
- **Determinism fingerprint.** `export` prints a SHA1 over all garment vertices
(`garment_pipeline.py:984-989`).
- **The seam is crossed.** MD-authored garments arrive at game budget, pre-fitted to
`Ariki_Female_QuatSkin.glb``reduce` is only hard for downloaded meshes.
- **All the automation hooks exist on the game side.** `game.sh spawn` (multi-instance,
`SPAWN_BUILD=1`, `MOCK_ONLY=1`), agent API (`/health /screenshot /navigate /state
/scene /ui /command /input /console`), `game.sh clean-import`, `tools/asset_pipeline.py
check`.
### 1.2 What costs time (the streamlining targets)
| Cost | Evidence |
|---|---|
| 4× duplicated MD garment scripts | `md_pari.py`, `md_piupiu.py`, `md_tee_v1.py`, `md_skirt_v1.py` share a ~100% identical shell (NewProject → ImportFBX at `op.scale=10.0` → fabric → arrange → simulate → snapshot); only panels/seams differ |
| Config copy-paste | `piupiu.json` vs `piupiu_sb.json`: 48 lines, differing in exactly 4 values (`name`, `body`, `weights`, `export.set`) + a note |
| Six manual CLI invocations per garment | one `blender --background … --stage <s>` per stage, run by hand in order |
| Hand-authored configs | read `census.json`, guess island→part mapping, trial-and-error alignment against QA renders |
| Code-edit registration | 2 `Add()` lines + possibly a `BaseDirFor` case in `OutfitCatalog.cs`, then a C# rebuild |
| Blunt reimport | `.bin`-only changes are invisible to Godot's `.gltf` hash; today's fix is `clean-import` (wipes ALL of `.godot/imported/`) or hand-deleting entries |
| Eyeball sign-off | spawn test bed, click Idle/Walk/Dance, look |
### 1.3 What is structurally unsafe
1. **Rest-pose-only QA.** Every `qa_*.png` the pipeline writes is the fitting pose.
Both shipped bugs were invisible in it. (`clothing/README.md` "QA renders only prove
the pose that cannot fail".)
2. **Silent catalog fallback.** `OutfitCatalog.BaseDirFor` (`OutfitCatalog.cs:88-98`)
takes a bare-string `setId` and its `_ =>` arm silently resolves to the Fantasy pack
folder — a typo'd set name mis-resolves without error. Missing assets are warn-only
(`OutfitCatalog.cs:71-78`): the character just stays naked/previous.
3. **`.bin` staleness.** `GLTF_SEPARATE` export keeps geometry/UVs in the sidecar
`.bin`; Godot hashes only the `.gltf`, so a vertex-only re-export silently ships the
old mesh.
### 1.4 Corrections to the record (verified 2026-07-31)
Earlier session notes contained four errors that materially change the design:
| Prior claim | Truth | Consequence |
|---|---|---|
| "FBX export from MD is manual" | `export_api.ExportFBX(path, op)` (+ `ExportOBJ/ExportZPrj/ExportZPac`) is scriptable — exact flags in `.claude/skills/marvelous-designer/SKILL.md:182-190` (`op.bExportGarment=True; op.bExportAvatar=False`); the 4 garment scripts simply never call it | The **entire MD half can run unattended** after the one session click |
| `ExportSnapshot3D` is the mesh export | It is the viewport **PNG** (the vision-QC loop); mesh export is `ExportFBX` | Don't build on the wrong call |
| "5 duplicated MD scripts" | 4 garment scripts; `md_recon.py` is an API-introspection tool and stays standalone | Template scope |
| `tools/vision-compare.py` exists (referenced by `ariki-game/tools/visual-qa.sh`) | **It was never written** | The in-game gate has no judge today; VLM-vs-pixel-diff is a real decision, not wiring |
One permanent constraint, confirmed: **MD exposes no mesh introspection to Python**
(`GetClothPositions()` stays empty). Every upstream drape judgement is image-based,
by necessity, until CLO exposes more.
Two enabling facts: the config's `export` block already carries `gender`/`set`/`out_dir`
and each part carries `slot`, so the catalog's exact `{Gender}_{Set}_{Slot}.gltf` path is
derivable from the config today; and the agent API's `GET /console` surfaces
`ClothingTestBed`'s per-slot load-failure prints, so **outfit load failure is
machine-detectable without vision**.
---
## 2. The unified pipeline
### 2.1 One config, additive blocks
Keep `clothing/configs/<garment>.json` as the single per-garment artifact and **grow it**
— the existing Blender schema stays byte-compatible, so `garment_pipeline.py` needs no
migration:
```jsonc
{
"extends": "piupiu.json", // NEW: overlay inheritance
"name": "...", "source": "...", "body": "...",
"md": { // NEW: upstream draft params (what the 4 scripts differ on)
"avatar_fbx": "...", "zfab": "...", "texture": "...",
"panels": [...], "seams": [...], "arrangements": [...],
"sim_frames": 300, "strengthen": [...],
"export_basename": "lena_<garment>_v<N>"
},
"align": {...}, "parts": {...}, // UNCHANGED: existing Blender schema
"export": { "gender": "Female", "set": "Kapahaka", "out_dir": "..." }, // exists
"catalog": { // NEW: what Add() needs that nothing else holds
"id": "kapahaka_legs_f", "displayName": "Piupiu",
"charisma": 0.05, "workSpeed": 0.04, "category": "casual"
},
"expect": {...} // NEW: QC thresholds (§3)
}
```
- **`extends` is the cheapest real win**: `piupiu_sb.json` becomes ~8 lines. Overlay =
deep-merge child over parent, arrays replaced whole.
- **`md` block + one template script** `tools/tailor/draft_garment.py` replaces the 4
copy-paste scripts. The identical shell becomes the template; panels/seams/arrangements
come from the config. Hard-won MD lessons (mm units, +y up in 2D, whole-edge seam
indices, look up arrangement points by name, `SetArrangement` then
`ResetClothArrangement`) live in ONE place instead of four.
- **`catalog` block** carries only what the C# `Add(id, displayName, slot, slotSuffix,
charisma, workSpeed, category, set:, gender:)` signature needs and the pipeline has
never held; `slot`/`set`/`gender` derive from existing blocks.
### 2.2 One orchestrator
`clothing/garment.py <config> [--from <stage>] [--to <stage>] [--only <stage>]`
over one ordered stage list spanning all three worlds:
```
draft drape publish │ census prepare fit reduce bake skin export │ register import verify
└──── MD bridge ──────┘└──────── Blender headless ────────────────┘└──── ariki-game ─────┘
```
**A driver, not a rewrite.** Each stage shells out to the tool that already owns it:
| Stage group | Shells to | Notes |
|---|---|---|
| `draft/drape/publish` | `python tools/md_bridge.py --file <generated>` | Script generated from the `md` block via `draft_garment.py`. `--ping` first; if no session, fail fast printing the human instruction ("click Plugin > TinqsMDBridge"). Raise the idle timeout (~1 min per 300 sim frames). `publish` = scripted `ExportZPrj` + `ExportFBX` + `ExportZPac` (§1.4). |
| `census … export` | `blender --background --python garment_pipeline.py -- --config X --stage Y` | **Unchanged CLI**; `--from/--to` is a loop. Standalone stage runs keep working exactly as today. |
| `register/import/verify` | `bash ariki-game/tools/game.sh …` + agent API | §2.4 and gates G6/G7/G8. Decided: orchestrator lives in `tinqs/animation/clothing/`, cross-repo shell-out to `game.sh` is acceptable. |
Checkpoint numbering (`00_ … 50_`) already gives resume: `--from fit` loads
`10_prepare.blend` exactly as today.
### 2.3 The automation boundary (permanent, human-only)
1. **Session start is a click** — Plugin > TinqsMDBridge, once per MD session (not per
garment). The orchestrator detects (`--ping`) and instructs; it can never perform it.
2. **MD's UI freezes during a blocking call** — no mid-drape supervision; results are
visible only after `Simulate` returns.
3. **No mesh introspection in MD** — upstream QC is image-based forever (§1.4).
Everything else — including the FBX export previously believed manual — is scriptable.
That is the headline of this assessment.
### 2.4 Registration: emit, don't edit (catalog JSON deferred)
Decided 2026-07-31: **defer** the data-driven OutfitCatalog refactor.
- Cost today is 2 `Add()` lines per set (+1 `BaseDirFor` case when a new folder
appears); the refactor is not small (`Initialize()` is all hardcoded calls;
`ClothingItem` has 10 init-only properties to round-trip). Revisit at ~10 sets.
- The real risk is not typing, it is the **silent** `setId` fallback (§1.3) — gate G8
(a ~30-line lint) buys that safety without the refactor.
- The `register` stage therefore **emits** the exact `Add(...)` line(s) and any needed
`BaseDirFor` case to stdout + `work/<name>/register.cs.txt` for paste-in.
Codegen-as-text, not codegen-as-edit — honest about who owns `src/`.
---
## 3. QC gates (where agents/checks slot in)
Ranked by value = known-pain-caught ÷ effort. Both shipped bugs were pose-dependent, so
the ranking is driven by *how early a pose-dependent failure can be caught*. Thresholds
live in the config's `expect` block.
| # | Gate | After stage | Kind |
|---|---|---|---|
| **G5** | **Posed penetration + gape sweep — the centrepiece** | `skin` | scriptable |
| G6 | In-game motion QC | `import` | agent-with-vision (hard part scriptable) |
| G3 | Rest-pose penetration | `fit` | scriptable |
| G2 | Census sanity | `census` | scriptable |
| G8 | Catalog ↔ asset lint | `register` | scriptable |
| G7 | Targeted reimport verify | `import` | scriptable |
| G4 | Reduce budget + silhouette | `reduce` | scriptable |
| G1 | Drape placement | `drape` | hybrid |
**G5 — posed penetration + gape sweep.** Headless Blender loads `50_skin.blend` (it
already contains `BODY`, `RIG`, and the skinned `GARM_*` parts). Apply real clip poses —
the UAL animation packs (`ariki-game/assets/quaternius/anim/UAL1.glb` etc.) are authored
on the same 65-bone skeleton — or synthetic extremes (arm raise, deep step, torso twist).
Per sampled frame, evaluate the armature-deformed meshes and BVH-test: (i) garment verts
inside the body, (ii) body verts exposed inside a part's declared coverage band.
Pass/fail: `expect.max_penetrating_verts` (e.g. 0 verts >1 mm inside) and
`expect.max_gape_verts` across **all** frames. This is the only gate that would have
caught BOTH shipped bugs — deterministically, headless, pre-game, re-runnable. One risk
to verify during implementation: bone-name compatibility between the UAL pack armature
and the derived-body `RIG` (retarget or pose-copy if names drift).
**G6 — in-game motion QC.** `SCENE=clothing_test_bed MOCK_ONLY=1 game.sh spawn` →
`POST /navigate {"button":"Idle"|"Walk"|"Dance"}` → `GET /screenshot` per clip → judge.
Two signals: **hard** = `GET /console` contains no outfit load error (machine-checkable
today); **soft** = visual verdict on poke-through/gaping/texture. Decided 2026-07-31:
**VLM/agent judge for a new garment's first sign-off, then bless those frames as the
baseline so re-runs become a deterministic pixel diff.** This also finally gives
`visual-qa.sh` the comparator it references but never had (§1.4). Small `ariki-game`
enabler needed (approved): `ClothingTestBed` hardcodes its opening set
(`DefaultBodyF = "kapahaka_body_f"` etc., `ClothingTestBed.cs:81-84`) — add an env-var/CLI
override so a new set is directly reachable instead of blind `Cycle Body` presses.
**G3 — rest-pose penetration.** The same BVH test as G5, rest pose only, run right after
`fit` — a cheap early-fail subset that saves reduce/bake/skin time on a bad fit.
**G2 — census sanity.** `census.json` already records per-island verts/tris/z/x/centroid.
Check against `expect.islands` (count, per-mapped-island vert count and z-span
tolerance). Catches the silent killer: an MD re-export reorders islands and the config
maps the wrong ones.
**G8 — catalog ↔ asset lint.** Every catalog entry's resolved
`{baseDir}/{Gender}_{Set}_{Slot}.gltf` exists on disk, and every outfit GLB on disk has a
catalog entry. Zero unmatched either way. ~30 lines; kills the silent `BaseDirFor`
fallback footgun (§1.3) without the JSON refactor.
**G7 — targeted reimport verify.** Replace blunt `clean-import` in the garment loop:
delete only that asset's two `.godot/imported/` entries (the README already prescribes
exactly this by hand), run `--import`, then confirm via `/console` + refreshed
`.gltf.import`. Closes the `.bin`-staleness hole (§1.3) without nuking the whole cache.
**G4 — reduce budget + silhouette.** Tris vs `parts[].tris`; pixel IoU of the HI vs
reduced QA renders ≥ threshold. Low urgency: MD-authored garments arrive at budget (both
kapa haka pieces never hit their ceilings) — this gate mostly guards downloaded meshes.
**G1 — drape placement.** The thresholds already exist as prose — `md_pari.py:4` reads
"QC targets: band top ~1.31 m (above bust), hem ~1.05 m (waist) ±3 cm". Move them into
`expect.bands`, upgrade `tools/tailor/qc_placement.py` to read them (instead of its
hardcoded landmark table), and add a vision pass on the `ExportSnapshot3D` PNG. That one
move takes drape QC from eyeballed to gated.
---
## 4. Phased adoption
| Phase | Content | Effort | Why this order |
|---|---|---|---|
| **1** | `extends` overlay + `expect` block + **G2** + **G8** + orchestrator skeleton driving *Blender stages only* | ~½ day | Zero new subsystems, no MD, no game, no human in the loop. Immediately kills the 48-line config copy and the six-call ritual. |
| **2** | `draft_garment.py` template + `md` block + scripted `ExportZPrj/FBX/ZPac` in the same bridge call + **G1** | ~1 day | Collapses 4 scripts to 1; MD half becomes unattended after the single session click. |
| **3** | **G5** posed sweep (+ **G3** as its rest-pose subset) | ~1 day | **The money phase** — catches the class of bug that has actually shipped, before the game. |
| **4** | **G7** targeted reimport + ClothingTestBed set override + **G6** motion QC + baseline blessing | ~1 day | Real shaders/import/AnimationTree; only meaningful after 13. Needs the small approved `ariki-game` edits. |
| **5** | Data-driven `OutfitCatalog` JSON | deferred | Revisit ~10 sets; G8 buys the safety now. |
G4 rides along whenever convenient (it rarely bites for MD-authored garments) rather
than blocking any phase.
---
## 5. Resolved decisions (Jeremy, 2026-07-31)
1. **Orchestrator home:** `tinqs/animation/clothing/garment.py`; game stages shell out
cross-repo to `ariki-game/tools/game.sh`.
2. **G6 judge:** VLM/agent vision judge for first sign-off; bless those frames; re-runs
are deterministic pixel diffs.
3. **OutfitCatalog:** stay code-edit; pipeline emits `Add()` lines; add the G8 lint;
JSON refactor deferred (~10 sets).
4. **`ariki-game/src` edits:** small enablers approved (ClothingTestBed set override,
later testbed hooks). The pari/piupiu weight-fix agent owns `garment_pipeline.py`'s
skin stage + those two configs — coordinate before touching either.
## 6. Leave alone
- `garment_pipeline.py`'s `--stage` CLI and config schema — additive only; the
orchestrator wraps, never rewrites.
- The `skin` stage internals and `pari.json`/`piupiu.json` weight fields — owned by the
weight-fix agent right now.
- `md_recon.py` — introspection tool, not a garment script; does not fold into the template.
- `.agents/wiki/architecture/clothing-lane.md` — cite it; fold outcomes in on adoption;
never fork its content.
## 7. Appendix — file/line references
| Fact | Where |
|---|---|
| Stage implementations | `clothing/garment_pipeline.py`: census 224248, prepare 295505, fit 542598, reduce 653706, bake 712768, skin 908957 (weight modes 920944, skirt_bones 836905), export 964989 (vertex SHA1 984989) |
| Config parse / work dir / CLI | `garment_pipeline.py:1006`, `:36-39`, `:994-1008` |
| MD bridge protocol/session | `tools/md_bridge.py` (port/env `:88`, `:22`; run/ping `:58-68`), `docs/md-bridge.md:8-94` |
| Scriptable MD exports (flags) | `.claude/skills/marvelous-designer/SKILL.md:182-190` |
| MD API lessons | `tools/tailor/md_tee_v1.py:12-28`, `md_skirt_v1.py:6-25` |
| Drape QC thresholds as prose | `tools/tailor/md_pari.py:4`; classifier `tools/tailor/qc_placement.py:24-34`, bands `:51-80` |
| Config diff piupiu vs piupiu_sb | `clothing/configs/piupiu.json` / `piupiu_sb.json` — `name`, `body` (`_SkirtRig`), `parts.Piupiu.weights`, `export.set` |
| Catalog path resolution + silent fallback | `ariki-game/src/Character/OutfitCatalog.cs:57-98` (warn-only 7178, `BaseDirFor` `_ =>` 9697), `Add()` pattern in `Initialize()` 116+ |
| Test bed buttons / defaults / load-fail HUD | `ariki-game/src/Testing/ClothingTestBed.cs:449-516`, `:81-84`, `:88-89` + `:279-281` |
| Spawn / engine / import / clean-import | `ariki-game/tools/game.sh:633-749`, `:39-70`, `:832-841` (+ asset check `:848`), `:478-482` |
| Agent API endpoints | `game.sh:215-242` (`/console` via AgentServer) |
| Visual QA harness (judge missing) | `ariki-game/tools/visual-qa.sh:1-77` — calls `tools/vision-compare.py`, which does not exist |
| Reimport gotcha (hand fix prescribed) | `clothing/README.md:143-147` |
| Reference logic in older converters | `ariki-game/tools/cc_clothing_to_quaternius.py` (skeleton-frame alignment 6673, KDTree copy 110127, A-pose check 198205), `make_islander_outfits.py` (region cuts 103149) |
## 8. Build outcome (2026-07-31)
Built in one session by five parallel agents plus an integration pass. As-built
interfaces are documented in `clothing/PIPELINE-CONTRACT.md` (marked AS-BUILT); the
one-command usage and gate table are in `clothing/README.md`.
| Lane | Delivered |
|---|---|
| A | `clothing/garment.py` — the orchestrator: `extends` resolution, deep-merge, path absolutization, 13 stages, gate scheduling, `register` codegen, `--selftest` |
| B | `gates/g2_census.py` (island census sanity), `gates/g8_catalog_lint.py` (catalog↔asset lint, `--all` / `--require-registered`) |
| C | `gates/g5_posed_sweep.py` — posed penetration + gape sweep; G3 is its `--rest` mode |
| D | `tools/tailor/draft_garment.py` (MD script generator), `tools/tailor/qc_placement.py`, `gates/g1_drape.py` |
| E | `ariki-game/tools/targeted_reimport.sh` (G7), `ariki-game/tools/clothing_motion_qa.sh` (G6), `ClothingTestBed.ReportLoad` |
### Acceptance test
`configs/tests/piupiu_sb_test.json` (scratch export target, set `KapahakaSBTest`) run
end to end, `census..export`, `--no-gate-stop`:
| Stage | Time | Gate |
|---|---|---|
| census | 8.0 s | **G2 PASS** — 1 island, 3620 v / 7104 tri, matched `expect.islands` |
| prepare | 4.7 s | — |
| fit | 3.3 s | **G3 FAIL** — 1 vert, 8.0 mm deep, at rest |
| reduce | 3.1 s | — |
| skin | 3.4 s | **G5 FAIL** — see below |
| export | 3.3 s | — |
**G5 reproduced the shipped piupiu bug headlessly**, in 14.9 s, from the real game
clips (`Idle_Loop` from UAL1, `Walk_Fwd_Loop` from UAL2; 65/65 pack bones matched
the RIG, the 16 skirt bones ride their parents):
```
rest 1 penetrating vert (max 8.0 mm)
Idle @0/12/25/38/50/62 229 217 216 231 216 216 (max 75.2 85.2 mm)
Walk @0/7/13/20/27/33 169 134 61 90 128 63 (max 70.0 87.9 mm)
```
That is the thigh punching through the skirt: ~1 vert visible in the pose the whole
pipeline used to sign off on, and **216231 verts up to 8.5 cm deep** the moment the
character stands still and breathes. 13 failing frames, with front + closeup QA renders
for six of them. The gate is worth its cost.
The **pari neckline gape did NOT reproduce** in the Blender checkpoint — it is not
present in the skinned mesh G5 measures. Its origin is therefore in-game (attach,
material, or LOD), which makes it G6 territory, not G5. Worth stating plainly because
the pre-build assumption was that both shipped defects were the same class of bug.
### Real findings the gates produced
- **G8** found an *uncommitted* `OutfitCatalog` registration hunk in the working tree —
a garment registered locally and never pushed, which the lint surfaced immediately.
On the test set it correctly reports `KapahakaSBTest` has no `BaseDirFor` arm and so
would silently resolve into the Fantasy pack folder.
- **G1** measured the *shipped* garments against their own written QC targets and found
both off: pari's hem sits **+3.9 cm** high, and the piupiu's bands **+8 cm / +14 cm**.
Those targets had lived as prose in the garment scripts' headers since they were
written; nobody had ever checked them against a render.
- **`/console` did not surface load failures at all** until `ClothingTestBed.ReportLoad`
was added — `GD.Print` does not feed the agent API's ring buffer. Every "the bed looks
fine" sign-off before that fix was reading an empty channel.
- **G6's pixel diff has a measured floor**: same outfit re-run moves ~0.002 of the frame,
an entirely different outfit moves 0.014, and by frame 3 phase drift alone (0.0095)
matches the signal. So G6 catches gross regressions (garment vanished, failed to load,
swapped) and nothing subtler. **G5 owns the subtle bug class** — which is the right
split, because G5 is headless, deterministic and 15 s.
### Still deferred
Catalog-as-JSON (§2.4) — `register` still emits `work/<name>/register.cs.txt` for a human
to paste, by design. Nothing in the build depends on changing that.
+105
View File
@@ -0,0 +1,105 @@
# Rig-graft lane — AccuRig skeleton onto pristine Tripo GLBs (PLAN, not executed)
**Repo home:** this plan and its tools moved from ariki-game into the animation repo
2026-08-06 — the lane authors characters, so `characters/REGISTRY.md` governs it (see
`.agents/wiki/ARCHITECTURE.md` "Which repo does a non-gameplay tool belong to"). Paths
below are animation-repo-relative unless prefixed `ariki-game/`.
**Status 2026-08-04: PLAN ONLY. Jeremy has explicitly held execution — no model
files are to be created or modified until the naming convention is decided.**
## Goal
Rig Ozlem's two unrigged Tripo bodies without the FBX→GLB quality loss the male
lane hit in July (mangled textures, bad hand skinning). The move: FBX is only a
**disposable rig carrier** through AccuRig; the **GLB is the sole source of truth**
for mesh + materials and only ever *gains* bones and weights.
## Source files (curated 2026-08-04)
Byte-identical copies live in both repos: `characters/originals/` here (checksummed in
`characters/REGISTRY.md`) and `ariki-game/assets/models/characters/race-sources/`. Read
from this repo's copies.
| file | verts | tris | height | feet @ Z0 | axis | textures |
|---|---|---|---|---|---|---|
| `female_lena_tripo.glb` | 974,478 | 1,907,931 | 0.979 m | yes (0.0) | upright, rot 0, scale 1 | 3 packed 4K (basecolor/normal/rm) |
| `female_lena_tripo.fbx` | 953,968 | 1,907,931 | 0.979 m | — | upright | 5 external JPEGs (.fbm removed; git-recoverable) |
| `male_base_bald_tripo_v1.glb` | 969,881 | 1,901,449 | 0.980 m | yes (0.0) | upright, rot 0, scale 1 | 3 packed 4K, clean |
| `male_base_bald_tripo_v1.fbx` | 950,729 | 1,901,449 | 0.980 m | — | **lying down (bad axis)** | 6 packed, all mislabeled "Diffuse Texture.NNN", duplicated basecolor |
Census facts that shape the plan:
- **No bones anywhere** — all four files are truly unrigged, so no rig standard is
imposed by the sources. The 65-bone Quaternius game standard is unaffected.
- **Identical surfaces per pair** (tri counts match exactly); vertex counts differ
~2% from format-specific UV-seam splitting → index-exact weight copy is off the
table, nearest-surface transfer is the mechanism (surfaces coincide, so it is
near-exact everywhere except close-packed fingers — see step 4).
- The male FBX's axis + texture mangling is the July failure mode reproduced in
data — never source visual data from FBX.
## Decisions (Jeremy, 2026-08-04)
1. **NO scaling to standard height.** Bodies stay at Tripo's native ~0.98 m.
(Any scale handling happens later, downstream, not in this lane.)
2. **Axis check + ground the feet** — wanted, and the probe shows both GLBs
already pass (rot 0 / scale 1 / min-Z exactly 0.0). These become verify-only
gates, mutating nothing unless a future source fails them.
3. **HOLD before decimation** until the naming convention for derived files is
agreed. Nothing below the line runs until then.
## The lane (each step gated on the one before)
1. **Verify** (read-only, DONE for the two current GLBs): upright axis, unit
scale, feet at Z=0, centered X, packed textures present.
2. **Rig bait** *(HELD — naming)*: from the GLB, decimate a disposable copy and
export FBX for AccuRig. **Confirmed 2026-08-04: AccuRig refuses the raw
1.9 M-tri FBX outright** (Jeremy tried; July hit the same wall). Use the
`ariki-game/tools/male_mesh_decimate.py` precedent: region budgets body 24k / head 14k /
**hands 10k as their own protected region** (global-ratio decimation webs the
fingers — half of the historic hand-mangling happened here, pre-AccuRig).
Apply the July male-lane traps: Dummy-export quirk, the 4 cm offset.
No scale change on the GLB — but if AccuRig misplaces joints on a 0.98 m
body, scale the DISPOSABLE BAIT up 2× and scale the returned skeleton back
down in the graft; the GLB never changes size.
3. **AccuRig** (manual, Jeremy/Ozlem): rig the bait FBX. Export rigged FBX.
4. **Graft** (headless Blender): import rigged bait FBX + pristine GLB; snap rest
poses; transfer skeleton + skin weights decimated→full-res by nearest-surface
with tight max-distance; **hands get special handling** (per-finger masked
transfer or reduced distance + limit-totals + normalize) — this is where the
old hand mangling gets fixed. Export GLB: original mesh + original packed
textures + new rig. FBX artifacts are discarded.
5. **QC**: pose sweep incl. finger curls; then (if/when destined for game) the
existing AccuRig→Quaternius conversion (`ariki-game/tools/make_male_ib_quatskin_accurig.py`
lineage) picks it up.
## Naming + step 2 status (RESOLVED 2026-08-04, later same day)
Jeremy raised the hands budget and released the bait step. Built with
`tools/rigbait_decimate.py` (parameterized successor to male_mesh_decimate.py —
no scale/recenter, bbox-relative region cuts, budgets body 24k / head 14k /
**hands 40k**):
- `characters/rig-work/lena_tripo_rigbait.fbx` — 77,999 tris, QA'd
- `characters/rig-work/mako_tripo_rigbait.fbx` — 78,000 tris, QA'd
- `characters/rig-work/` is exempt from the naming grammar (REGISTRY.md rule 11) —
disposable carriers, no registry rows. In the game repo it had needed a `.gdignore`
to stop Godot importing it, which was the tell that it lived in the wrong repo.
- QA renders alongside (`*_qa_front.png`, `*_qa_hand.png`) — fingers fully
distinct at 40k, no webbing.
- Baits are geometry-only (~3.5 MB; the GLB's packed textures don't survive FBX
embed) — AccuRig shows a grey model, which is fine for rigging. It does mean
the July "Dummy001 export" check can't use textures: **verify AccuRig's output
by mesh name + ~78k tri count instead.**
**AccuRig exports must be saved as** (same folder):
- `characters/rig-work/lena_tripo_accurig.fbx`
- `characters/rig-work/mako_tripo_accurig.fbx`
## Open before execution
- Step 3 (AccuRig) — manual, waiting on Jeremy/Ozlem.
- Step 4 graft script — build once a rigged FBX exists to test against.
- Whether to restore the female FBX's `.fbm` texture folder from git (only needed
if the FBX is ever used for more than rigging; the lane says it shouldn't be).
+1
View File
@@ -8,6 +8,7 @@ This repo's operator playbooks are Claude-Code-native skills and live at
| `animation` | Operator playbook — the batch workflow, naming, loop QC, gotchas |
| `animation-creation` | Authoring new clips on the shared Quaternius skeleton |
| `iclone-video-mocap` | iClone 8 + Video Mocap: filming, cleanup, FBX export |
| `marvelous-designer` | Garment authoring: MD bridge, drafting from a reference image, draping on the game body, placement QC, export |
| `pose-estimation` | Video→pose-landmark extraction (MediaPipe) |
| `retarget-animations` | Deprecated early retarget notes, kept for history |
+48 -4
View File
@@ -3,6 +3,10 @@
Entry point to architecture law for this repo. Per-system detail lives in
`.agents/wiki/architecture/`.
This file covers the **animation lane** (motion → clips). The repo also runs a
**clothing lane** (garments → worn outfits) that targets the same skeleton —
see `.agents/wiki/architecture/clothing-lane.md`.
## The one-sentence shape
PC-side motion capture (iClone 8 + Video Mocap) → this repo (Mac↔PC bridge,
@@ -78,13 +82,53 @@ in `dancegen/` only.
`tools/cc_retarget.py`, `mixamo_retarget.py`, `kevin_retarget.py`,
`mocap_retarget.py` are **mirrors of `ariki-game/tools/`** — the game copies are
authoritative; re-copy from ariki-game when they change (they have silently
diverged before). `.claude/skills/` here mirror a subset of `ariki-game/.claude/skills/`
plus `~/.claude/skills/pose-estimation` see `README.md` Provenance section.
diverged before; verified identical 2026-08-06). `.claude/skills/` here mirror a
subset of `ariki-game/.claude/skills/` plus `~/.claude/skills/pose-estimation`
see `README.md` Provenance section.
The **character/body lane is authoritative HERE and is not mirrored** (moved out
of ariki-game 2026-08-06): `tools/rigbait_decimate.py`,
`make_lena_nude_body.py`, `_bake_nude_body_texture.py`,
`_render_body_closeup.py`, `verify_body_variant.py`. Anything that authors a
character mesh, rig, or body texture is born here from now on, governed by
`characters/REGISTRY.md`. The game repo keeps only the historical committed
generators (`make_lena_body.py`, `make_male_ib_quatskin_accurig.py`,
`male_mesh_decimate.py`, the `_convert_lena_quat_v*` series): they are cited by
game-side plans and some carry game-side tests.
### Which repo does a non-gameplay tool belong to
Decided by **subject, not by "is it gameplay"** — nothing in `ariki-game/tools/`
is gameplay (no `.gd` runtime script references it at all), so that test would
empty the folder into this one. Three questions, in order:
1. Does the engine, CI, or the game's own test suite run it? → **stays in
ariki-game.** `asset_pipeline.py`, `game.sh`, `session.py`,
`e2e_interaction.py`, `anim_qc.py`, `rig_pose_gate.py`,
`targeted_reimport.sh`, `clothing_motion_qa.sh`, and every module a
`tools/test_*.py` imports.
2. Does it author characters or motion? → **here.**
3. Neither? → it is **game-asset authoring** (trees, terrain, water, props,
items, animals, VFX). Not gameplay, but not animation either — leave it there
rather than making this repo a dumping ground.
Two things that keep tripping this up: some character tools have game-side tests
(`brow_cover_math.py`, `generate_lena_brow_surface.py`, `test_mako_rig_math.py`),
so they move with their tests or not at all — and **this repo has no test
harness**, so receiving them means standing one up. And `anim_qc.py` staying is a
deliberate split, not drift (see the two-QC-tools note above).
**Cross-repo paths in tools that moved here:** resolve the game checkout from
`$ARIKI_GAME`, else guess the sibling directory and *verify it*, aborting with the
path tried. Never let an ariki-relative default resolve silently wrong — same rule
as `--target` above.
## See also
- `.agents/wiki/architecture/` — per-system detail (currently: this file covers
the whole pipeline; split out if a subsystem grows its own doc).
- `.agents/wiki/architecture/clothing-lane.md` — the clothing lane (reference
image → Marvelous Designer → `clothing/` → worn outfit in-game).
- `.agents/wiki/architecture/` — per-system detail (split a subsystem out when
it grows its own doc).
- `.agents/wiki/dances/REGISTRY.md` — naming/registry source of truth.
- `.agents/wiki/iclone-bridge.md` — PC-lane routing stub.
- `.agents/wiki/devops-reports/` — point-in-time audits and convergence reports.
+7 -4
View File
@@ -1,9 +1,12 @@
# architecture/ — per-system detail
This repo's pipeline is small enough that `.agents/wiki/ARCHITECTURE.md` covers
it end to end today; nothing has grown large enough yet to need its own
per-system doc here. Split a topic out into this folder (and link it from
`ARCHITECTURE.md`) when it does — likely candidates as the repo grows:
| Page | Covers |
|---|---|
| `clothing-lane.md` | The clothing lane end to end: reference image → Marvelous Designer authoring → `clothing/` game-ification → worn outfit in ariki-game. Read before any garment work. |
`.agents/wiki/ARCHITECTURE.md` still covers the **animation** lane end to end.
Split a further topic out into this folder (and link it from `ARCHITECTURE.md`)
when it grows — likely candidates:
- Retarget-tool internals (per-source rig mapping, bone-name conventions).
- Loop QC/fix algorithms (pose-gap, velocity-gap, root-drift math).