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,392 @@
|
||||
# Plan: Clothing-pipeline unification — one entry point, QC gates at every seam
|
||||
|
||||
**Status:** Implemented 2026-07-31 (phases 1–4 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 1–3. 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 224–248, prepare 295–505, fit 542–598, reduce 653–706, bake 712–768, skin 908–957 (weight modes 920–944, skirt_bones 836–905), export 964–989 (vertex SHA1 984–989) |
|
||||
| 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 71–78, `BaseDirFor` `_ =>` 96–97), `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 66–73, KDTree copy 110–127, A-pose check 198–205), `make_islander_outfits.py` (region cuts 103–149) |
|
||||
|
||||
## 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 **216–231 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.
|
||||
@@ -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).
|
||||
Reference in New Issue
Block a user