Files
animation/.claude/skills/marvelous-designer/SKILL.md
T
jeremy d1030def4e docs+tools: working-file policy, lane pruner, and a human MD explainer
Two pieces of work.

1. Keep milestones, not steps. Staged NN_*.py lanes were saving a full
   ~80 MB .blend per attempt, so Lena's lane reached 3.0 GB of which 2.2 GB
   was 30 .blend files -- five snapshots to land one crotch fix, five more
   for the bra. The .py recipes are the real history; the blends are cache.

   - .agents/rules/working-files.md: four-tier policy (KEEP / MASTER /
     SCRATCH / UNKNOWN) + how to work a lane.
   - tools/prune_lane.py: classifies a lane and prunes the scratch tier.
     Dry-run by default. Reads a per-lane .lanekeep manifest, flags
     binaries byte-identical to a registered original (canonical name
     always survives a duplicate pair), and never auto-deletes a .blend
     with no step script beside it -- those cannot be rebuilt.
   - .gitignore: scratch patterns can never be committed.

   Dry run on characters/female/lena_nude reports 2.3 GB reclaimable.
   Not applied -- that lane had a live Blender session at the time.

2. .humans/marvelous-designer.html: how we author garments in Marvelous
   Designer, written for people rather than agents -- the six-step process,
   what has been made, the traps that cost hours, and what is still
   unsolved. Matches the .humans/ HTML convention in ariki-game.

Also committing the docs the AGENTS.md knowledge map and the new page
reference, so they are not dangling: the clothing-lane architecture page,
the marvelous-designer skill, and the two screenshots the page embeds.

characters/ is deliberately untracked and stays that way -- it holds GBs of
blends and GLBs, and .gitattributes does not LFS-track .blend.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 12:05:46 -07:00

275 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: marvelous-designer
description: Authoring garments in Marvelous Designer agentically — driving MD's Python API over the TinqsMDBridge socket plugin, drafting patterns from a reference image and Lena's measurements, draping/QC-ing them on the game body, and exporting meshes for the clothing pipeline. Use for any "make/adjust a garment", "match this clothing photo", or "drive Marvelous Designer" task, and read before touching tools/md_bridge* or tools/tailor/.
---
# Marvelous Designer — agentic garment authoring
The **upstream half** of the clothing lane: reference image (or a description) →
sewn, draped, textured garment mesh fitted to the game body. The downstream half
(garment mesh → game-ready skinned GLB) is `clothing/` — see
`.agents/wiki/architecture/clothing-lane.md` for how they meet.
Everything here was learned by doing it on 2026-07-30 (tee, skirt, kapa haka
pari + piupiu). The API facts are **verified against MD 2026 Personal**, not
docs — Reallusion/CLO's published API docs are thin and several signatures in
them are wrong.
## The 60-second model
- MD is driven through **`tools/md_bridge.py`** → a socket plugin running inside
MD (`tools/md_bridge/TinqsMDBridge.py`). Full protocol: `docs/md-bridge.md`.
- A garment = **flat 2D panels** (`CreatePatternWithPoints`) + **seams**
(`AddSeamlinePairGroup`) + **arrangement points** on the avatar
(`SetArrangement`), then **simulate** (`utility_api.Simulate(frames)`) to drape
cloth onto the body.
- You see results by rendering the viewport to PNG (`ExportSnapshot3D`) and
reading the image back. **This vision loop is the whole method** — draft,
drape, look, measure, adjust, repeat.
- **Which call for which job, and why:** `references/tooling.md`. Read it before any
QC or diagnostic work — it carries the verified signatures, the render/measure
toolkit, the seam-pairing decision table, and the harness patterns.
- Garment identity for traditional wear is mostly **textiles, not tailoring**.
Kapa haka / Mexica / Pacific garments are rectangles + blocks; the design lives
in generated texture maps. Model the shape simply, spend effort on the pattern.
## Session protocol (read first — MD's UI freezes)
MD's embedded Python **does not run background threads**, so the bridge owns the
main thread while it serves. Consequences:
1. **A human must click** Plugin → TinqsMDBridge to start a session. You cannot
start one yourself. Ask, then wait.
2. **MD's UI is frozen** for the whole session ("Not Responding" is normal).
3. End with `python tools/md_bridge.py --stop` — works from the terminal even
though the UI is dead. Idle timeout is 4 h (was 300 s; raised so one click
lasts a work session).
4. **Pause the session whenever Jeremy wants to look at the model.** He inspects
in MD's viewport; he can't while you hold the thread. Default to stopping when
you hand back a result, unless he said to stay in.
```bash
python tools/md_bridge.py --ping # verify + see mode/api modules
python tools/md_bridge.py --exec "result = pattern_api.GetPatternCount()"
python tools/md_bridge.py --file tools/tailor/md_pari.py --timeout 500
python tools/md_bridge.py --stop # give the UI back
```
Long `Simulate()` calls need a raised client `--timeout` (~1 min per 300 frames).
The exec namespace persists **within** a session, not across; MD api modules
(`pattern_api`, `import_api`, `export_api`, `fabric_api`, `utility_api`,
`ApiTypes`) plus a `BRIDGE` info dict are pre-seeded.
**Introspect, don't trust docs.** Full surface dump lives at
`tools/md_bridge/api_surface.json` (688 functions) and harvested docstrings at
`tools/md_bridge/api_docs.json`. Overloaded pybind11 functions break
`inspect.signature()` — read `__doc__` instead.
## API facts that cost hours to find
| Fact | Consequence |
|---|---|
| **2D pattern y+ maps to UP in 3D** | Panels drafted y-down drape **upside-down over the head** and tangle. Symptom looks like a seam bug; it isn't. Hem at y=0, neckline at high y. |
| Units are **mm**; gravity 9800 | A "500" square is 50 cm. |
| Blender-exported FBX avatars import **10× small** | Import with `op.scale = 10.0`. |
| `ImportAvatar()` is **.avt only** — returns `False` on FBX | Use **`import_api.ImportFBX(path, op)`** for the game body. |
| `op.bAddArrangementPoints = True` | Auto-generates ~98 named arrangement points on a custom avatar. Without it you have nowhere to hang cloth. |
| `SetArrangement()` only *assigns*; **`utility_api.ResetClothArrangement()` applies** it | Skipping the apply = nothing moves. (`ReDrape3DArrangement` only materializes not-yet-draped cloth.) |
| Arrangement **indices regenerate** per avatar import | Always look up by name from `GetArrangementList()`; never hardcode an index. Offsets aren't stable either — verify with a 0-frame snapshot. |
| Avatar getters live in **`export_api`** (`GetAvatarCount`, `GetAvatarNameList`) | Not `utility_api`, where you'd look. |
| `utility_api.NewProject()` **deletes the avatar** | Re-import after. |
| `SetBaseTextureMapImageGivenFilePath(path, fabricIdx)`**path is arg0** | Reversed args throw a TypeError that reads like a missing overload. |
| **PNG DPI sets a texture's physical size in MD** | 1024 px at 54.2 dpi = 480 mm of cloth. Control tiling by setting dpi in PIL, not by scaling the image. |
| `fabric_api.AddFabric()` needs a **`.zfab` file path** | A name string silently fails. Stock presets: `C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\`. |
| Fabric **index 0 is the shared default** | Coloring it dyes every garment in the scene. Always `AddFabric` a new one. |
| `AssignFabricToPattern()` returns False for every arg order tried | Use **`pattern_api.SetPatternPieceFabricIndex(pattern, fabric)`**. |
| `SetViewPoint()` does nothing; **`SetCamViewPoint(2)` = front view** | Call before every QC snapshot so shots are comparable. |
| **`SetCamViewPoint` has NO REAR VIEW** (0 bottom, 1/3 front ¾, 2 front, 4/6/7 sides, 5 top) | `ExportSnapshot3D` cannot show the back of a garment at all. Use `export_api.ExportTurntableImages(4)` — index 2 is the back. It ignores its path arg and writes into MD's own output folder, so the return value is the only way to find the files. This blind spot shipped four separate defects. |
| `SetArrangementPosition` takes **4 ints** | A float raises `TypeError`. The correct **x** depends on the arrangement family: `Body_*_Center_1` needs the two panels to **match** (a 50/0 split folds one shoulder); `Leg_Skirt_*` needs them to **differ** (50/50 drops the skirt on the floor). Sweep with a control before changing it. See `references/tooling.md` §5. |
| `ExportOBJ` writes **mm**; the FBX→census path reads **decimetres** | The two exporters disagree. Metres = OBJ × 0.001, FBX census × 0.1 (that's what `align.scale_z` is for). |
| `GetClothPositions()` is an out-param that stays empty from Python | No mesh introspection. Export a throwaway OBJ and parse its `v ` lines — exact, and the only option for gappy/strand garments, where `qc_placement.py`'s pixel classifier fails outright. |
| No `SaveProjectFile`; no glTF export; no EveryWear in the API | Save = `export_api.ExportZPrj`. Exits: `ExportFBX` / `ExportOBJ` / `ExportZPac`. EveryWear is GUI-only. |
## Construction doctrine
**Take the reference apart before drafting anything.** The tailor's method —
image → slot split → anchoring → placement targets → ease table → piece plan —
is `references/deconstruction.md`. Its output is a worksheet of numbers, and
every downstream value (panel dims, `expect.bands`, sim recipe) traces to a row
of it. Skipping this step is how garments shipped 414 cm off target.
**Draft from measurements, not guesses.** Lena's card:
`tools/tailor/lena_measurements.json` (regenerate for any body with
`tools/tailor/measure_body.py`, a headless-Blender mesh slicer). She is
stylized — 108-67-109 cm with 72 cm thighs on 178 cm — so real-world size charts
produce clothes that don't fit.
**Build from blocks, not freehand.** The blocks are parametric now —
`tools/tailor/blocks.py` turns worksheet numbers into a runnable config skeleton
(`python tools/tailor/blocks.py fitted_top --name x --band-top-z 1.31 --hem-z 1.05 --ease 17`),
with the seam parity, arrangement-x rules, bodice taper, and tension waists
baked in (`--selftest` proves it regenerates the shipped garments). Prefer
generate-then-edit over hand-typing point lists. The original per-garment
recipes remain as provenance:
| Block | Generator | Legacy recipe | Notes |
|---|---|---|---|
| Fitted top / tank / bodice | `blocks.fitted_top` | `md_tee_v1.py`, `md_pari.py` | Front+back panels, shoulder+side seams, neck gap and armholes cut into the outline. Straps hold height reliably. Legacy recipes predate the §3.3 seam-parity and §5 arrangement-x discoveries — the generator has the fixes. |
| A-line skirt | `blocks.aline_skirt` | `md_skirt_v1.py`, `md_piupiu.py` | 2 panels, side seams only. The most forgiving garment; start here. |
| Strand/fringe skirt | `blocks.strand_skirt` | — | Comb outline (teeth, never partial seams), symmetric half-gaps, elastic mid-settle. |
| Trousers/shorts | **unsolved** — see failure catalogue | | |
**Seams:** whole-edge only. Pair the **same line index** on **mirrored** front/back
panels with `(False, False)`*mirrored* is load-bearing. Most recipes here draft both
panels from the **identical** point list offset by `dx`, which is NOT mirrored, and
those need **`(True, True)`** on the side seams or the panel twists and turns partly
inside out. Full decision table in `references/tooling.md` §3.3; cross-pairing every
seam makes the garment slide off the shoulders. Build
neck gaps and armholes as extra points in the outline, not as partial seams.
Fingerprint line indices by length via `GetLineLength(pattern, line)`.
**Straps beat tubes.** A strapless tube slides down to the narrowest catch
(underbust). If a reference garment is strapless, add straps anyway when the
target body has baked-in underwear to cover — it fixes placement and coverage at
once. It's also what made the pari read correctly.
**Bottoms stay up by tension, not elastic.** Cut the waist *smaller* than the
hips and let fabric stretch hold it.
## Drape recipe (the sequence that works)
```python
pattern_api.SetPatternStrengthen(p, True) # stiffen so cloth wraps, not crumples
utility_api.ResetClothArrangement() # apply arrangement
utility_api.Simulate(250) # main settle, still stiff
# skirts: enable waist elastic HERE, mid-settle, then Simulate(80) more
pattern_api.SetPatternStrengthen(p, False)
utility_api.Simulate(50) # relax into natural folds
```
- **Strengthen through the whole settle**, relax only at the end. Soft fabric
from frame 0 rolls into a bunch at the waist. This is the anti-bunching fix —
**but only for garments whose sides are fully sewn.** On a bodice sewn only over
its lower half with 90 mm straps, strengthening flattens the back and **rotates the
whole garment**, giving an uneven hem and exposed skin. Where it can't be used,
remove the surplus cloth instead.
- **Elastic mid-settle, never from the start.** Elastic applied before the cloth
has wrapped cinches the garment off one hip. Drape first, cinch second — that
is what finally locked the piupiu waistband at the waist.
- **Skirts arrange on `Leg_Skirt_Front` / `Leg_Skirt_Back`**, not the body-waist
points, with `SetArrangementPosition(p, x, 92, 50)`.
- **One garment per scene.** A second garment — even frozen — grabs and inverts
the new one. Game exports are per-garment anyway. Assemble outfits only at the
end, by reloading finished pieces (below).
- **Outfit assembly:** `ImportZprj(base)` + `ImportZpac(other, op.bAdd=True)`.
Textures survive **only if you don't re-fabric or re-simulate after merging**
a merge renumbers patterns and fabrics unpredictably. Assemble, shoot, done.
## QC — the part that was missing and matters most
Early drapes were accepted because they "looked like clothing". They were
bunched around the middle. Fit is judged by **numbers against the reference**:
1. **Extract placement targets** from the reference photo — where each edge sits
relative to body landmarks, as a table with tolerances. Example (kapa haka):
pari top 1.31 m ±3 cm (above bust), pari hem / piupiu waist 1.05 m ±3 cm,
piupiu hem 0.45 m ±4 cm (below knee).
2. **Measure every drape**: `python tools/tailor/qc_placement.py <snapshot.png>`
classifies background/skin/garment pixels, calibrates px→m off Lena's known
1.777 m height, and reports each garment band's top/bottom in metres against
the nearest landmark. Iterate until inside tolerance.
3. **Snapshot with `SetCamViewPoint(2)`** so every shot is comparable.
4. **Save a screenshot into the repo** for every shipped garment —
`tools/tailor/screenshots/<garment>.png`. Required, not optional: it's how
Jeremy reviews without opening MD.
**The diagnostic that unsticks everything:** snapshot *after*
`ResetClothArrangement()` with **zero simulation frames**. That shows where the
panels actually start, before physics muddies it. Seven "seam bug" iterations
were really an upside-down garment; one pre-sim snapshot would have caught it
immediately. Reach for it the moment a drape misbehaves.
`qc_placement.py` is honest but crude: it counts the body's baked-in underwear
and floor shadow as garment. Tighten it with per-garment colour masks when
precision matters.
## Textures
Generate procedurally with PIL and set physical scale via DPI —
`tools/tailor/textures/` holds `taniko.png` (concentric woven diamonds) and
`piupiu.png` (flax strands with geometric banding), both derived from a reference
photo. Pattern-generation scripts are worth keeping when a motif will recur.
Workflow: `AddFabric(<.zfab>)``SetBaseTextureMapImageGivenFilePath(png, fab)`
`SetPatternPieceFabricIndex(pattern, fab)`. Set the PNG's dpi so the design
spans the garment exactly once (dpi = px / (mm/25.4)).
## Handing off downstream
Export three ways per garment, into `tools/tailor/`:
```python
export_api.ExportZPrj(".../lena_<garment>_v<N>.zprj") # editable source of truth
op = ApiTypes.ImportExportOption(); op.bExportGarment = True; op.bExportAvatar = False
export_api.ExportFBX(".../lena_<garment>_v<N>_garment.fbx", op) # for clothing/
export_api.ExportOBJ(".../lena_<garment>_v<N>_garment.obj", op)
export_api.ExportZPac(".../lena_<garment>_v<N>.zpac") # for outfit assembly
```
Then the garment goes through `clothing/garment_pipeline.py` (fit → reduce →
skin → export) to become a game outfit part. **Two advantages MD-authored
garments have over downloaded ones**, worth exploiting in the config:
- **They're already game budget.** These export at 1.2k3.7k verts; the
downloaded MD dress the clothing pipeline was piloted on is 2.49 M verts /
110 MB. The brutal `reduce` stage is mostly unnecessary — start with a high
tri budget and only decimate if the test bed complains.
- **They're already fitted**, having been draped on the actual game body, so
`align`/`fit` needs little more than the clearance shell.
Keep the `.zprj` — regenerating a variant beats re-authoring, and the clothing
README explicitly prefers re-exporting from MD over fighting reduction.
## Failure catalogue (don't rediscover these)
- **Shorts / trousers are unsolved.** Two-panel crotch-notch construction twists
every time — fabric can't thread between Lena's touching thighs, and all seam
parities were tried. Next approach: **4 panels arranged on the per-leg points**
(`Leg_Front_L/R`, `Leg_Back_L/R`) so cloth starts wrapped around each thigh.
- **Upside-down panels** (y-down drafting) — see the y+ = UP rule.
- **Elastic-first drapes** slide off a hip; **soft-from-frame-0** drapes bunch.
- **Co-draping** two garments inverts one.
- **A textured render hides folds and winding.** Judge shape with the texture OFF:
cloth is white on its front face, grey on its back, so a panel showing grey from
outside is inside out and a white streak is a fold. A busy motif conceals both.
- **A sweep without a control case teaches you something false.** If the control
(known-good settings) also fails, the harness is broken, not the geometry.
- **A light garment does not slide into place.** MD materialises cloth at the
arrangement point and does not simulate donning, so for anything light the
arrangement height *is* the placement.
- **Retexturing after a scene merge** silently repaints the wrong garment.
- **Threaded/background socket servers inside MD never answer** — the bridge must
own the main thread. Don't "fix" the frozen UI by re-threading it; that was
tried and the design is deliberate.
- MD 2026's **AI Image Generator / EveryWear are GUI-only** — not scriptable.
## File map
| Path | What |
|---|---|
| `tools/md_bridge.py` | Terminal client (`--ping/--exec/--file/--stop`) |
| `tools/md_bridge/TinqsMDBridge.py` | The plugin (register once via Plug-in Manager; referenced in place, so edits go live on next click) |
| `tools/md_bridge/api_surface.json`, `api_docs.json` | Introspected API truth |
| `docs/md-bridge.md` | Protocol, install, threading rationale |
| `tools/tailor/measure_body.py`, `lena_measurements.json` | Body measurement tool + Lena's card |
| `tools/tailor/blocks.py` | Parametric blocks: worksheet numbers → config skeleton (`--selftest`) |
| `tools/tailor/draft_garment.py` | Config `md` block → bridge script (the four recipes, templated) |
| `tools/tailor/md_*.py` | Per-garment recipes (each header carries its own lessons) |
| `references/deconstruction.md` | The tailor's method: taking a reference image apart into pieces |
| `tools/tailor/qc_placement.py` | Placement QC measurement |
| `tools/tailor/textures/` | Generated fabric maps |
| `tools/tailor/screenshots/` | Required per-garment review renders (front **and back** — see tooling.md §0) |
| `references/tooling.md` | Which API call for which job, verified signatures, decision tables |
| `tools/tailor/avatar/Lena_QuatSkin_Avatar.fbx` | Game body as an MD avatar (from the GLB, leaf bones stripped) |
| `tools/tailor/lena_*_v*.{zprj,fbx,obj,zpac}` | Shipped garments |