Raise character limits and document optional Blender MCP
Full build / Import + headless tests (push) Successful in 41s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-04 13:47:16 +01:00
1 parent 50a48d2256
commit aec1fb7b69
20 files changed
+303 -96

No files matched your search

@@ -1,6 +1,6 @@
--- ---
name: gig-line-blender-asset-contract name: gig-line-blender-asset-contract
description: Use when adding, rebuilding, auditing or fixing any Gig Line Blender-authored character, NPC base, gear, prop or venue geometry. Covers the 13-bone npc_base contract, five tint slots, triangle/vertex budgets, gear attach space, 256² texture/AO rules, headless flatpak Blender commands, and the inspect/audit/fit/proportion/capture gates. Do not use for pure GDScript/UI work or release publishing. description: Use when adding, rebuilding, auditing or fixing any Gig Line Blender-authored character, NPC base, gear, prop or venue geometry. Covers the required 13 core bones (optional extra bones allowed), five tint slots, 16,000/16,000 character budgets, gear attach space, 256² texture/AO rules, headless flatpak Blender commands, optional MCP guidance, and the inspect/audit/fit/proportion/capture gates. Do not use for pure GDScript/UI work or release publishing.
compatibility: Gig Line repository checkout. Godot 4.7.2 on PATH; Blender 5.2 LTS via the org.blender.Blender flatpak for build/bake/render. Audit, fit, inspect and proportion checks are stdlib-only. compatibility: Gig Line repository checkout. Godot 4.7.2 on PATH; Blender 5.2 LTS via the org.blender.Blender flatpak for build/bake/render. Audit, fit, inspect and proportion checks are stdlib-only.
metadata: metadata:
project: Gig Line project: Gig Line
@@ -21,9 +21,10 @@ Use this skill for any change that touches:
- Blender-authored venue/prop geometry - Blender-authored venue/prop geometry
The project contract and measured gates override any generic Blender advice. The project contract and measured gates override any generic Blender advice.
There is **no Blender MCP** in this project. Blender runs headless through the Blender MCP is optional interactive tooling; the headless flatpak CLI remains
flatpak CLI; the `blender-skills` pack is optional, local guidance only, and authoritative and CI never needs Blender or MCP. The `blender-skills` pack is
gitignored. optional, local guidance only, and gitignored. Setup and safety are in
`docs/blender-mcp.md`.
## Read first ## Read first
@@ -47,11 +48,11 @@ and resolve the source of truth before editing assets.
| Area | Rule | | Area | Rule |
| --- | --- | | --- | --- |
| Mesh root | `npc_base` mesh node, skinned under `Rig`; preserve the limb node names. | | Mesh root | `npc_base` mesh node, skinned under `Rig`; preserve the limb node names. |
| Bones | 13 contract bones: `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`, `Spine`, `Neck` (parent/helper bones must not change the contract). | | Bones | The 13 required core bones: `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`, `Spine`, `Neck`. Optional extra bones (fingers, face, toes) are allowed; they must not move the core rest positions or break animation proxies. |
| Tint slots | `skin`, `cloth`, `pants`, `accessory`, `hair`. | | Tint slots | `skin`, `cloth`, `pants`, `accessory`, `hair`. |
| Orientation | Front is Blender −Y / Godot +Z; feet grounded near y = 0; metres. | | Orientation | Front is Blender −Y / Godot +Z; feet grounded near y = 0; metres. |
| Library budget | **8,000 triangles / 8,000 glTF vertices** per character (`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS`). | | Character budget | **16,000 triangles / 16,000 glTF vertices** per character (`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS`). |
| Frozen fallback | `npc_base` keeps **6,000 tris / 4,000 verts**. Do not silently raise or lower it. | | Frozen fallback | `npc_base` remains the low-poly fallback; it shares the 16,000/16,000 cap but is not required to use it. Do not silently lower the cap. |
| Gear budget | **1,500 triangles** per gear piece; authored in attach-point space. | | Gear budget | **1,500 triangles** per gear piece; authored in attach-point space. |
| Prop budget | **4,000 triangles** per prop. | | Prop budget | **4,000 triangles** per prop. |
| Gear attach | Use the catalogued attach points; verify with `fit`. Runtime `GEAR_OFFSETS` (lanyard forward, firefighter helmet up) and `HAND_ITEM_FORWARD` are intentional, not asset-contract changes. | | Gear attach | Use the catalogued attach points; verify with `fit`. Runtime `GEAR_OFFSETS` (lanyard forward, firefighter helmet up) and `HAND_ITEM_FORWARD` are intentional, not asset-contract changes. |
@@ -113,8 +114,7 @@ only the specialists the task needs:
- budget/LOD/collision: `asset-optimization`, `lod-pipeline`, `collision-proxy` - budget/LOD/collision: `asset-optimization`, `lod-pipeline`, `collision-proxy`
- final review: `qa-review` - final review: `qa-review`
Translate any MCP instruction into a change to `tools/*.py` or a headless CLI If MCP is available, use it for interactive part exploration only; mirror the result into a scripted change or local `.blend`, then re-export and run the CLI audit/fit gates. Never make the build, CI, tests or runtime depend on MCP. Setup: `docs/blender-mcp.md`.
run. Never add an MCP dependency to the build, CI or runtime.
### 4. Build or bake ### 4. Build or bake
@@ -167,7 +167,7 @@ At minimum:
- `npc_proportions.py` passes the stylized guide. - `npc_proportions.py` passes the stylized guide.
- `npc_pipeline.py audit` passes if the base/textures changed. - `npc_pipeline.py audit` passes if the base/textures changed.
- texel density is inside the authored/fabric bands. - texel density is inside the authored/fabric bands.
- No character exceeds 8,000/8,000; gear/prop caps hold. - No character exceeds 16,000/16,000; gear/prop caps hold.
- 24-NPC budget and Web/Compatibility path remain green. - 24-NPC budget and Web/Compatibility path remain green.
- Contract tests, headless suite, capture gates and docs check pass. - Contract tests, headless suite, capture gates and docs check pass.
- The diff contains only the intended assets, tools, tests and docs. - The diff contains only the intended assets, tools, tests and docs.
@@ -197,12 +197,14 @@ gates run, and any accepted limit.
- [`references/asset-contract.md`](references/asset-contract.md) — full contract, - [`references/asset-contract.md`](references/asset-contract.md) — full contract,
inventory, file locations and provenance rules. inventory, file locations and provenance rules.
- `docs/blender-mcp.md` — optional MCP installation, connection, safety and
part-workflow instructions.
- [`references/verification.md`](references/verification.md) — command matrix, - [`references/verification.md`](references/verification.md) — command matrix,
capture gates, expected results and acceptance criteria. capture gates, expected results and acceptance criteria.
## Do not ## Do not
- Add an MCP server, Blender dependency or API call to the build/runtime. - Make the build, CI, tests or runtime depend on MCP, Blender or API calls.
- Change the bone/tint/contract to make a generic asset fit. - Change the bone/tint/contract to make a generic asset fit.
- Rewrite committed baselines or overwrite existing images. - Rewrite committed baselines or overwrite existing images.
- Commit `.blend` sources, raw captures or generated AI inputs. - Commit `.blend` sources, raw captures or generated AI inputs.
@@ -17,7 +17,7 @@ This is a working summary. The authoritative sources remain
## Skeleton and geometry ## Skeleton and geometry
The contract skeleton has 13 bones: The required contract skeleton has 13 core bones:
```text ```text
Head, Spine, Neck, Head, Spine, Neck,
@@ -28,6 +28,9 @@ ElbowL, ElbowR,
HandL, HandR HandL, HandR
``` ```
Optional extra bones (fingers, face controls, toes) are allowed if they do not
move these core rest positions and do not break `NpcAvatar`'s animation proxies.
Requirements: Requirements:
- `npc_base` mesh node exists and is skinned under `Rig`. - `npc_base` mesh node exists and is skinned under `Rig`.
@@ -56,8 +59,7 @@ skin, cloth, pants, accessory, hair
| Asset | Limit | | Asset | Limit |
| --- | --- | | --- | --- |
| Library character | 8,000 triangles / 8,000 glTF vertices | | Character (library and base fallback) | 16,000 triangles / 16,000 glTF vertices |
| `npc_base` fallback | 6,000 triangles / 4,000 vertices |
| Gear piece | 1,500 triangles | | Gear piece | 1,500 triangles |
| Prop | 4,000 triangles | | Prop | 4,000 triangles |
| Library prop/venue total | must stay inside the venue tests and library budget | | Library prop/venue total | must stay inside the venue tests and library budget |
+9 -7
View File
@@ -88,15 +88,17 @@ specialists the task needs:
### Project rules that override a skill ### Project rules that override a skill
1. **The project contract wins.** `npc_base` mesh node, the 13 contract bones at their rest 1. **The project contract wins.** `npc_base` mesh node, the 13 required core bones at their rest
positions, five tint slots, front on Blender −Y / Godot +Z, grounded feet, 8,000 positions, five tint slots, front on Blender −Y / Godot +Z, grounded feet, 16,000
triangles / 8,000 glTF vertices per character, 1,500 per gear, 4,000 per prop. A skill triangles / 16,000 glTF vertices per character, 1,500 per gear, 4,000 per prop. Optional
extra bones (fingers, face, toes) are allowed if they do not move the core rest positions. A skill
may recommend another topology, naming or budget scheme; do not adopt it unless the may recommend another topology, naming or budget scheme; do not adopt it unless the
change is explicitly planned and the contract, tests and docs are updated together. change is explicitly planned and the contract, tests and docs are updated together.
2. **There is no Blender MCP here.** The skills assume MCP tools; this project runs Blender 2. **Blender MCP is optional interactive tooling.** The headless flatpak scripts
as headless flatpak scripts (`tools/blender_assets.py`, `tools/npc_pipeline.py`). Translate (`tools/blender_assets.py`, `tools/npc_pipeline.py`) remain authoritative. MCP may be used
MCP steps into script changes or CLI runs. Never add an MCP dependency to the build, CI or for local part exploration; setup and safety are in [`docs/blender-mcp.md`](docs/blender-mcp.md).
runtime. Never add an MCP dependency to the build, CI, tests or runtime, and always re-run the CLI
audit/fit gates on anything MCP touched.
3. **CI stays offline and skill-free.** Skills are local guidance only; every gate must still 3. **CI stays offline and skill-free.** Skills are local guidance only; every gate must still
work from a fresh checkout with committed `.glb`/`.png`/`.tres` files and Godot. work from a fresh checkout with committed `.glb`/`.png`/`.tres` files and Godot.
4. **No new asset source without the licence gate.** Skills may suggest reference packs or 4. **No new asset source without the licence gate.** Skills may suggest reference packs or
+9 -3
View File
@@ -58,9 +58,15 @@ canonical implemented-features list in `docs/development-status.md`.
36 venue/difficulty/phase cells, seeds 1–100, and access/emergency error rates. Gotcha: 36 venue/difficulty/phase cells, seeds 1–100, and access/emergency error rates. Gotcha:
`_read_npc_truth()` is type-derived, not behaviour-derived; the emergency-fairness review must `_read_npc_truth()` is type-derived, not behaviour-derived; the emergency-fairness review must
account for that oracle mismatch. account for that oracle mismatch.
- **Character mesh cap:** eight library variants use 8,000 tris / 8,000 glTF vertices - **Blender MCP is optional (2026-11-04):** setup and safe part workflow are in
(`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in `tools/blender_assets.py`); the frozen `npc_base` fallback `docs/blender-mcp.md`. The headless flatpak scripts remain authoritative; MCP is
keeps 6,000/4,000. Measure after any geometry change; never silently lower the cap back to 5,000. never a build/CI/test/runtime dependency, and anything MCP touched must pass the
CLI audit/fit gates.
- **Character mesh cap (2026-11-04):** all characters use 16,000 tris / 16,000 glTF vertices
(`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in `tools/blender_assets.py`; `npc_pipeline.MAX_TRIANGLES`/
`MAX_VERTICES`). The required 13 core bones stay, optional extra bones are allowed, and the
low-poly `npc_base` fallback remains the shipped fallback. Measure the 24-NPC/Web budget after
any geometry change; never silently lower the cap without a new decision.
- **Engine baseline:** Godot 4.7.2 stable (official editor `~/.local/bin/godot`). The system's - **Engine baseline:** Godot 4.7.2 stable (official editor `~/.local/bin/godot`). The system's
`godot-runner` cannot load arbitrary project paths (compiled without path overrides); use the `godot-runner` cannot load arbitrary project paths (compiled without path overrides); use the
official editor binary. official editor binary.
+1
View File
@@ -180,6 +180,7 @@ Full setup, every run/test/smoke command, and the controls table are in
- [`CREDITS.md`](CREDITS.md) — Credits, AI/reference provenance and licensing notes. - [`CREDITS.md`](CREDITS.md) — Credits, AI/reference provenance and licensing notes.
- [`PRIVACY.md`](PRIVACY.md) — Local-only data, save files and development API use. - [`PRIVACY.md`](PRIVACY.md) — Local-only data, save files and development API use.
- [`docs/blender-assets.md`](docs/blender-assets.md) — Blender asset library: characters, gear, props, attach points. - [`docs/blender-assets.md`](docs/blender-assets.md) — Blender asset library: characters, gear, props, attach points.
- [`docs/blender-mcp.md`](docs/blender-mcp.md) — optional Blender MCP installation, connection and safe part-workflow.
- [`docs/npc-visual-review.md`](docs/npc-visual-review.md) — 0.9.4 NPC baseline, acceptance criteria and ranked visual gaps; 0.9.5 texture comparison is in [`docs/blender_previews/sheet_texture_refinement_0.9.5.jpg`](docs/blender_previews/sheet_texture_refinement_0.9.5.jpg). - [`docs/npc-visual-review.md`](docs/npc-visual-review.md) — 0.9.4 NPC baseline, acceptance criteria and ranked visual gaps; 0.9.5 texture comparison is in [`docs/blender_previews/sheet_texture_refinement_0.9.5.jpg`](docs/blender_previews/sheet_texture_refinement_0.9.5.jpg).
- [`docs/npc-proportion-audit.md`](docs/npc-proportion-audit.md) — NPC proportion measurements against the shared stylized guide. - [`docs/npc-proportion-audit.md`](docs/npc-proportion-audit.md) — NPC proportion measurements against the shared stylized guide.
- [`docs/adr/`](docs/adr/) — Architecture Decision Records. - [`docs/adr/`](docs/adr/) — Architecture Decision Records.
+25 -17
View File
@@ -17,8 +17,8 @@ The MIT Blender skill pack lives locally at `.agents/skills/blender-skills/` (gi
Goal: replace the sphere-and-primitive face and hand construction with a local, part-based Goal: replace the sphere-and-primitive face and hand construction with a local, part-based
development pipeline that can build, inspect and compare the **head/face** and **hands** separately development pipeline that can build, inspect and compare the **head/face** and **hands** separately
before assembling them into the shipped character. Prioritise face readability at portrait before assembling them into the shipped character. Prioritise face readability at portrait
distance, then hand silhouette and gear contact. Preserve the `npc_base` contract, the 13-bone rig, distance, then hand silhouette and gear contact. Preserve the `npc_base` contract, the 13 core-bone rig (optional extra bones allowed),
five tint slots, 8,000/8,000 character cap and Compatibility/Web path. five tint slots, 16,000/16,000 character cap, optional extra bones beyond the required 13, and the Compatibility/Web path.
*Skill route: `character-artist`, `retopology`, `sculpting`, `rigging`, `uv-workflow`, *Skill route: `character-artist`, `retopology`, `sculpting`, `rigging`, `uv-workflow`,
`texture-workflow`, `materials`, `lookdev`, `asset-optimization`, `qa-review`.* `texture-workflow`, `materials`, `lookdev`, `asset-optimization`, `qa-review`.*
@@ -42,7 +42,7 @@ the rest of §C remains the broad review checklist.
UV layout and the exact locations of eye/mouth/finger geometry. UV layout and the exact locations of eye/mouth/finger geometry.
- [ ] Define measurable acceptance: face front/three-quarter/profile at 1.5 m and 1.85 m; hands - [ ] Define measurable acceptance: face front/three-quarter/profile at 1.5 m and 1.85 m; hands
open and gripping at 0.8 m; silhouette, eye/mouth readability, ear/nose/jaw planes, finger open and gripping at 0.8 m; silhouette, eye/mouth readability, ear/nose/jaw planes, finger
separation and gear contact. Keep the 8,000/8,000 cap; no contract or renderer change. separation and gear contact. Keep the 16,000/16,000 cap; optional extra bones are allowed, but the required 13 core bones and rest positions must not move.
- [ ] Confirm the identity boundary: `npc_base.blend` is the frozen fallback and must not absorb - [ ] Confirm the identity boundary: `npc_base.blend` is the frozen fallback and must not absorb
prototype detail unless a separate contract decision approves it. prototype detail unless a separate contract decision approves it.
- [ ] Choose a local working-set layout: `tmp/npc-parts/<variant>/` (gitignored) for part `.blend` - [ ] Choose a local working-set layout: `tmp/npc-parts/<variant>/` (gitignored) for part `.blend`
@@ -61,6 +61,10 @@ the rest of §C remains the broad review checklist.
- [ ] Add tests for deterministic part assembly, contract output (mesh root, bones, slots, budgets) - [ ] Add tests for deterministic part assembly, contract output (mesh root, bones, slots, budgets)
and the non-Blender failure path. Verify one assembled prototype with `audit`/`fit`/`inspect` and the non-Blender failure path. Verify one assembled prototype with `audit`/`fit`/`inspect`
before touching production variants. before touching production variants.
- [x] Consider optional Blender MCP for interactive part exploration and document setup/safety.
(`docs/blender-mcp.md` covers addon/client setup, Pi `pi mcp add`, flatpak networking,
viewport/`bpy` workflow and the rule that headless audit/fit remains authoritative; MCP is
not a build/CI/runtime dependency.)
- [ ] Document the commands, working-set paths and “part source is not a shipped asset” rule in - [ ] Document the commands, working-set paths and “part source is not a shipped asset” rule in
`docs/asset-tooling.md`, `docs/blender-assets.md` and `docs/asset-tooling.md`, `docs/blender-assets.md` and
`.agents/skills/gig-line-blender-asset-contract/references/verification.md`. `.agents/skills/gig-line-blender-asset-contract/references/verification.md`.
@@ -84,8 +88,9 @@ the rest of §C remains the broad review checklist.
- [ ] Build separate left/right hand parts with a connected palm, four multi-segment fingers, a - [ ] Build separate left/right hand parts with a connected palm, four multi-segment fingers, a
thumb, knuckles and rounded tips. Support an open palm and a relaxed grip; preserve the thumb, knuckles and rounded tips. Support an open palm and a relaxed grip; preserve the
current `HAND_ITEM_FORWARD` behaviour for phone/cup/clipboard/camera/flashlight/wand/radio. current `HAND_ITEM_FORWARD` behaviour for phone/cup/clipboard/camera/flashlight/wand/radio.
- [ ] Decide finger articulation explicitly: the 13-bone contract has no finger bones, so keep the - [ ] Decide finger articulation explicitly: the current rig has no finger bones, so keep the
open/relaxed-palm approach unless an ADR approves extra bones and the budget/weight path. open/relaxed-palm approach unless an ADR approves optional finger bones and the
budget/weight path.
- [ ] Verify all hand gear at 0.8 m (contact, clipping, direction) and capture open/grip hand - [ ] Verify all hand gear at 0.8 m (contact, clipping, direction) and capture open/grip hand
close-ups for the review sheet. close-ups for the review sheet.
- [ ] Roll the approved hands into all 12 variants only after the one-variant hand sheet passes. - [ ] Roll the approved hands into all 12 variants only after the one-variant hand sheet passes.
@@ -93,7 +98,8 @@ the rest of §C remains the broad review checklist.
### 5. Assembly and rollout ### 5. Assembly and rollout
- [ ] Integrate the approved parts into `build_character` with deterministic joins, weights, UVs and - [ ] Integrate the approved parts into `build_character` with deterministic joins, weights, UVs and
AO. The final output remains one `npc_base` skinned mesh under the existing 13-bone armature; AO. The final output remains one `npc_base` skinned mesh under the existing 13-core armature;
optional extra bones are allowed but the core rest positions stay fixed;
no second rig, no material-slot rename. no second rig, no material-slot rename.
- [ ] Keep the MakeHuman body prototype consumable with the same approved head/hands; do not let it - [ ] Keep the MakeHuman body prototype consumable with the same approved head/hands; do not let it
grow a second face/hand implementation while the part pipeline is being proven. grow a second face/hand implementation while the part pipeline is being proven.
@@ -148,11 +154,11 @@ an active track; ADR-0015 (CC0 / CC-BY with attribution) still governs every int
### Intake and adaptation ### Intake and adaptation
- [x] `adapt` path added to `tools/blender_assets.py` (`adapt --file <in> --out <out> - [x] `adapt` path added to `tools/blender_assets.py` (`adapt --file <in> --out <out>
[--target-tris 8000]`): imports the GLB, applies transforms, grounds the feet, welds seam [--target-tris 16000]`): imports the GLB, applies transforms, grounds the feet, welds seam
splits and decimates to the budget, then exports. The five tint slots + 13-bone rig stay a splits and decimates to the budget, then exports. The five tint slots + 13-core rig
manual step (recorded in the command doc). (optional extra bones allowed) stay a manual step (recorded in the command doc).
- [x] `audit` stays the final gate; `test_adapt_requires_blender` covers the non-Blender error - [x] `audit` stays the final gate; `test_adapt_requires_blender` covers the non-Blender error
path (161/161 Python). path (162/162 Python).
- [x] Adapter commands + go/no-go documented in `docs/asset-tooling.md` §Intake adaptation. - [x] Adapter commands + go/no-go documented in `docs/asset-tooling.md` §Intake adaptation.
### MPFB / MakeHuman body pipeline ### MPFB / MakeHuman body pipeline
@@ -163,18 +169,18 @@ an active track; ADR-0015 (CC0 / CC-BY with attribution) still governs every int
x ±0.53 / y 0–1.82 / z −0.12…+0.35, one neutral material, UVs present, unskinned. The x ±0.53 / y 0–1.82 / z −0.12…+0.35, one neutral material, UVs present, unskinned. The
body (no rig) is the input for the rigging step; the script + mesh stay in `tmp/` (CC0, not body (no rig) is the input for the rigging step; the script + mesh stay in `tmp/` (CC0, not
committed). committed).
- [x] Rig strategy decided (owner): **retarget the body to the 13-bone contract**. - [x] Rig strategy decided (owner): **retarget the body to the 13-core contract; optional extra bones are allowed**.
`tmp/npc-research/makehuman_retarget.py` rotates the A-pose arms down (−38°/+38°), scales the `tmp/npc-research/makehuman_retarget.py` rotates the A-pose arms down (−38°/+38°), scales the
arms 1.63× to the contract 0.70 m length, welds the split vertices, smooth-shades, adds the arms 1.63× to the contract 0.70 m length, welds the split vertices, smooth-shades, adds the
contract armature + `_auto_weights`, paints the five slot regions (hair/cloth/pants/accessory/ contract armature + `_auto_weights`, paints the five slot regions (hair/cloth/pants/accessory/
skin) and exports `makehuman_retargeted.glb`. **Contract passes:** 6,444 / 8,000 tris, skin) and exports `makehuman_retargeted.glb`. **Contract passes:** 6,444 / 16,000 tris,
7,726 / 8,000 verts, 14 bones (13 + Hips), skinned, arm-down, grounded, five slots, no issues. 7,726 / 16,000 verts, 14 bones (13 + Hips), skinned, arm-down, grounded, five slots, no issues.
Vision-reviewed: arms hang down and the silhouette + slot regions read, but the 1.63× arm warp Vision-reviewed: arms hang down and the silhouette + slot regions read, but the 1.63× arm warp
distorts the deltoid/shoulder (spiky/warped), the decimated hands are blocky, and the slot distorts the deltoid/shoulder (spiky/warped), the decimated hands are blocky, and the slot
boundaries are rough — these are the refinement steps below. boundaries are rough — these are the refinement steps below.
- [x] Head/hands/feet swap (`makehuman_final.py`): cut the decimated MakeHuman head/hands/feet and - [x] Head/hands/feet swap (`makehuman_final.py`): cut the decimated MakeHuman head/hands/feet and
joined the project's `_face`/`_hair`/`_hands_and_shoes`, then re-weighted/re-UV'd. Contract joined the project's `_face`/`_hair`/`_hands_and_shoes`, then re-weighted/re-UV'd. Contract
passes: 6,407 / 8,000 tris, 6,651 / 8,000 verts, 14 bones, skinned, five slots, no issues. passes: 6,407 / 16,000 tris, 6,651 / 16,000 verts, 14 bones, skinned, five slots, no issues.
Vision-reviewed: the head now reads as a realistic face, the hands as detailed fingers and Vision-reviewed: the head now reads as a realistic face, the hands as detailed fingers and
the shoes as attached. Remaining (manual retopology): a 1.63× arm-scale warp still distorts the shoes as attached. Remaining (manual retopology): a 1.63× arm-scale warp still distorts
the deltoid/shoulder, stray hand blobs linger at the hips, shirt/shirt seams are rough, and the deltoid/shoulder, stray hand blobs linger at the hips, shirt/shirt seams are rough, and
@@ -299,9 +305,11 @@ licence-free zone only.
- [x] Hands/grip review: the hands ARE detailed — `tools/blender_assets.py._hands_and_shoes()` - [x] Hands/grip review: the hands ARE detailed — `tools/blender_assets.py._hands_and_shoes()`
builds a wider shallower palm, four two-segment bent fingers with rounded tips and a thumb; builds a wider shallower palm, four two-segment bent fingers with rounded tips and a thumb;
gear contact is the open palm plus the `HAND_ITEM_FORWARD` (0.04 m) offset. Decision: **keep gear contact is the open palm plus the `HAND_ITEM_FORWARD` (0.04 m) offset. Decision: **keep
the open palm + forward offset; a finger-curl rig stays out of scope** — there are no finger the open palm + forward offset; a finger-curl rig stays out of scope** — the current rig has
bones in the 13-bone contract, so a curl rig is a contract decision, not a modelling tidy-up. no finger bones, so a curl rig is a contract decision, not a modelling tidy-up. Optional
Recorded as ranked gap #6 (accept; revisit only if face work opens the budget). extra finger bones are now allowed by the cap/contract extension, but require an ADR and
weight/budget verification. Recorded as ranked gap #6 (accept; revisit only if face work
opens the budget).
- [x] Weights/pose evidence captured and vision-reviewed, and the P1s fixed: wrist/hand - [x] Weights/pose evidence captured and vision-reviewed, and the P1s fixed: wrist/hand
detachment (wrist + hand now 100% `Hand`, hand raised, sleeve cuff follows the hand), detachment (wrist + hand now 100% `Hand`, hand raised, sleeve cuff follows the hand),
hip/thigh collapse + knee pinch (thigh/shin rigid with a short knee blend). Vision-verified: hip/thigh collapse + knee pinch (thigh/shin rigid with a short knee blend). Vision-verified:
+7 -6
View File
@@ -24,9 +24,10 @@ severity, explicit verdict) rather than an opinion pass:
| Props, gear, foliage | `prop-artist`, `hard-surface`, `vegetation-artist` | Silhouette read, bevel/chamfer language, collision proxies, carded foliage | | Props, gear, foliage | `prop-artist`, `hard-surface`, `vegetation-artist` | Silhouette read, bevel/chamfer language, collision proxies, carded foliage |
| Look and verdict | `lookdev`, `qa-review` | Day/night beauty captures, ship / ship-with-notes / no-ship verdict | | Look and verdict | `lookdev`, `qa-review` | Day/night beauty captures, ship / ship-with-notes / no-ship verdict |
Project contract and gates stay authoritative; see `AGENTS.md` §Local Blender skills. The skills Project contract and gates stay authoritative; see `AGENTS.md` §Local Blender skills. Blender MCP
assume Blender MCP, which this project does not use: everything below came from the headless is optional interactive tooling (see [`blender-mcp.md`](blender-mcp.md)); this review's evidence
`tools/blender_assets.py` / `tools/npc_pipeline.py` pipelines plus the Godot capture harnesses. came from the headless `tools/blender_assets.py` / `tools/npc_pipeline.py` pipelines plus the
Godot capture harnesses, so it remains reproducible without MCP.
## What was captured and measured (2026-11-03) ## What was captured and measured (2026-11-03)
@@ -63,14 +64,14 @@ with the runtime 4× fabric repeat).
| `npc_crowd_b` | 4,734 / 4,353 | 1.809 / +0.000 | 7 | 107–118 | 434–439 | Contract pass; crowd prototype | | `npc_crowd_b` | 4,734 / 4,353 | 1.809 / +0.000 | 7 | 107–118 | 434–439 | Contract pass; crowd prototype |
| `npc_crowd_c` | 5,674 / 5,440 | 1.824 / +0.000 | 7 | 76–83 | 317 | Contract pass; crowd prototype | | `npc_crowd_c` | 5,674 / 5,440 | 1.824 / +0.000 | 7 | 76–83 | 317 | Contract pass; crowd prototype |
| `npc_crowd_d` | 5,814 / 5,300 | 1.856 / +0.000 | 7 | 92–106 | 377–381 | Contract pass; crowd prototype | | `npc_crowd_d` | 5,814 / 5,300 | 1.856 / +0.000 | 7 | 92–106 | 377–381 | Contract pass; crowd prototype |
| `npc_base` (fallback) | 1,204 / 2,488 | 1.836 / +0.000 | 5 | 70.1–72.9 | 4× on fabric | Contract pass; far below its 6,000/4,000 cap | | `npc_base` (fallback) | 1,204 / 2,488 | 1.836 / +0.000 | 5 | 70.1–72.9 | 4× on fabric | Contract pass; compact fallback under the 16,000/16,000 cap |
Blender-side `inspect` counts can differ by ±1 vertex from the glTF accessor count in `audit` Blender-side `inspect` counts can differ by ±1 vertex from the glTF accessor count in `audit`
(normal-split rounding); both are recorded and neither changes a verdict. (normal-split rounding); both are recorded and neither changes a verdict.
Readings: Readings:
- Every character is inside 8,000/8,000 with the farthest at 5,474 tris (68% of budget) and 5,494 - Every character is inside 16,000/16,000 with the farthest well below the new cap and 5,494
verts (69%), so there is headroom for targeted topology detail without a cap change. verts (69%), so there is headroom for targeted topology detail without a cap change.
- Surface area tracks clothing volume: the firefighter coat is the largest body (4.28 m²) and has - Surface area tracks clothing volume: the firefighter coat is the largest body (4.28 m²) and has
the lowest authored density; `npc_casual` is the smallest (2.44 m²) and the highest. the lowest authored density; `npc_casual` is the smallest (2.44 m²) and the highest.
@@ -254,7 +255,7 @@ owner review decides.
brow ridge, a fuller lower lip and both eyelids (upper + lower); recessed creases (nasolabial, brow ridge, a fuller lower lip and both eyelids (upper + lower); recessed creases (nasolabial,
temple, chin cleft) were rejected because they bake as dark AO patches. Vision-reviewed: the new temple, chin cleft) were rejected because they bake as dark AO patches. Vision-reviewed: the new
face reads cleaner and more realistic than the old (set-in eyes, brow break, lip volume, no face reads cleaner and more realistic than the old (set-in eyes, brow break, lip volume, no
faceting); tris 4,482 → 5,038 (still under 8,000). The other seven variants keep the old face faceting); tris 4,482 → 5,038 (still under 16,000). The other seven variants keep the old face
until this prototype is approved. until this prototype is approved.
2. **No pose/weight evidence (unknown).** Idle, walk, sit, block and turn deformation has never been 2. **No pose/weight evidence (unknown).** Idle, walk, sit, block and turn deformation has never been
captured as a review set, so shoulder/hip/neck collapse is unmeasured. Cheap to add and it captured as a review set, so shoulder/hip/neck collapse is unmeasured. Cheap to add and it
+1
View File
@@ -16,6 +16,7 @@ This folder holds the project documentation, visual references and generated QA
- [`architecture.md`](architecture.md) — arc42 system architecture - [`architecture.md`](architecture.md) — arc42 system architecture
- [`getting-started.md`](getting-started.md) — setup, run/test commands and controls - [`getting-started.md`](getting-started.md) — setup, run/test commands and controls
- [`asset-tooling.md`](asset-tooling.md) — image/model pipelines and screenshot gates - [`asset-tooling.md`](asset-tooling.md) — image/model pipelines and screenshot gates
- [`blender-mcp.md`](blender-mcp.md) — optional Blender MCP setup and safety
- [`../tools/README.md`](../tools/README.md) — tool inventory, categories and test runner - [`../tools/README.md`](../tools/README.md) — tool inventory, categories and test runner
- [`gameplay.md`](gameplay.md) — controls, venues and escalation - [`gameplay.md`](gameplay.md) — controls, venues and escalation
- [`joke-shop-map.md`](joke-shop-map.md) — Sunset Strip real-site mapping - [`joke-shop-map.md`](joke-shop-map.md) — Sunset Strip real-site mapping
@@ -70,3 +70,14 @@ measured. None was a drop-in; the full results are in
polygon count, no rig in the base mesh and a separate authoring dependency. polygon count, no rig in the base mesh and a separate authoring dependency.
- **Use Mixamo or Ready Player Me.** Rejected: service terms are not a simple - **Use Mixamo or Ready Player Me.** Rejected: service terms are not a simple
free/open-source redistribution basis for shipped meshes. free/open-source redistribution basis for shipped meshes.
## Amendment 2026-11-04
- The character budget is raised to **16,000 triangles / 16,000 glTF vertices** per character.
The required 13 core bones remain; optional extra bones (fingers, face, toes) are allowed if
they do not move the core rest positions. The low-poly fallback remains the fallback, not a
second contract.
- MakeHuman is now an active local development pipeline (body retarget plus project-owned
head/hands), while the procedural library remains the shipped path and CI fallback.
- Blender MCP is optional interactive tooling only; it is not a build, CI, test or runtime
dependency. Setup is in [`docs/blender-mcp.md`](../blender-mcp.md).
+2 -2
View File
@@ -141,8 +141,8 @@ GLBs and maps.
| Tool | Role | Committed artifact | Verification gate | | Tool | Role | Committed artifact | Verification gate |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `tools/npc_pipeline.py` | Builds the skinned base avatar, five-slot material maps, palette and AI-fabric preparation (`prepare`/`palette`) | `npc_base.glb`, `assets/textures/npc/*_{albedo,normal,orm}.png`, `palette.json` | `audit_glb` against the frozen 6,000-triangle / 4,000-vertex base contract | | `tools/npc_pipeline.py` | Builds the skinned base avatar, five-slot material maps, palette and AI-fabric preparation (`prepare`/`palette`) | `npc_base.glb`, `assets/textures/npc/*_{albedo,normal,orm}.png`, `palette.json` | `audit_glb` against the 16,000-triangle / 16,000-vertex base contract (13 core bones required; extras allowed) |
| `tools/blender_assets.py` | Builds/renders the library: 8 characters, 18 gear pieces, 20 props, plus `manifest.json`; stdlib `audit`/`fit`/`sheet`/`list` | `assets/models/blender/{npc,npc_gear,props}/*.glb` | Library cap 8,000 tris / 8,000 verts per character, 1,500 per gear, 4,000 per prop; grounding, UV, degenerate-triangle, attach-fit and manifest coverage checks | | `tools/blender_assets.py` | Builds/renders the library: 8 characters, 18 gear pieces, 20 props, plus `manifest.json`; stdlib `audit`/`fit`/`sheet`/`list` | `assets/models/blender/{npc,npc_gear,props}/*.glb` | Character cap 16,000 tris / 16,000 verts, 1,500 per gear, 4,000 per prop; grounding, UV, degenerate-triangle, attach-fit and manifest coverage checks |
| `tools/bake_ao.py` | Bakes per-asset AO maps | `<asset>_ao.png` (256²) | Wired through `Materials.apply_ao()`/`SurfaceMaterials`; cloth/pants opt out (see below) | | `tools/bake_ao.py` | Bakes per-asset AO maps | `<asset>_ao.png` (256²) | Wired through `Materials.apply_ao()`/`SurfaceMaterials`; cloth/pants opt out (see below) |
| `tools/npc_proportions.py` | Measures exported GLB rest pose (height, head, shoulders, hips, torso, limbs, feet) against the stylized guide | report / JSON in `docs/npc-proportion-audit.md` | All eight variants must stay inside the guide after a body change | | `tools/npc_proportions.py` | Measures exported GLB rest pose (height, head, shoulders, hips, torso, limbs, feet) against the stylized guide | report / JSON in `docs/npc-proportion-audit.md` | All eight variants must stay inside the guide after a body change |
| `tools/npc_baseline.py` | Assembles neutral-baseline sheets and before/after comparisons | `docs/blender_previews/sheet_npc_baseline*.jpg` | Human review against `docs/npc-visual-review.md` | | `tools/npc_baseline.py` | Assembles neutral-baseline sheets and before/after comparisons | `docs/blender_previews/sheet_npc_baseline*.jpg` | Human review against `docs/npc-visual-review.md` |
+11 -3
View File
@@ -189,7 +189,7 @@ Caveats:
`adapt` is the mechanical first half of an external-mesh intake (MakeHuman, `adapt` is the mechanical first half of an external-mesh intake (MakeHuman,
MPFB, downloaded GLB): import, apply transforms, ground the feet, weld seam MPFB, downloaded GLB): import, apply transforms, ground the feet, weld seam
splits, decimate to the character budget, and export a GLB. It does **not** map splits, decimate to the character budget, and export a GLB. It does **not** map
the five tint slots or rebuild the 13-bone `npc_base` armature — those stay a the five tint slots or rebuild the 13-core-bone `npc_base` armature (optional extra bones are allowed) — those stay a
manual `retopology`/`rigging` step with a recorded second-rig contract if the manual `retopology`/`rigging` step with a recorded second-rig contract if the
source rig cannot be retargeted: source rig cannot be retargeted:
@@ -198,8 +198,8 @@ flatpak run org.blender.Blender --background \
--python "$PWD/tools/blender_assets.py" -- adapt \ --python "$PWD/tools/blender_assets.py" -- adapt \
--file "$PWD/tmp/npc-research/makehuman_base.glb" \ --file "$PWD/tmp/npc-research/makehuman_base.glb" \
--out "$PWD/tmp/npc-research/makehuman_adapted.glb" \ --out "$PWD/tmp/npc-research/makehuman_adapted.glb" \
--target-tris 8000 --target-tris 16000
# prints: adapted makehuman_adapted.glb: 36972 -> 8000 tris (bones=N, meshes=1) # prints: adapted makehuman_adapted.glb: 36972 -> 16000 tris (bones=N, meshes=1)
``` ```
Run `inspect` on the adapted GLB before and after, and keep the procedural Run `inspect` on the adapted GLB before and after, and keep the procedural
@@ -343,6 +343,14 @@ python3 tools/model_pipeline.py audit
python3 tools/npc_pipeline.py audit python3 tools/npc_pipeline.py audit
``` ```
### Optional interactive Blender MCP
The headless pipeline above is authoritative. For local part exploration, an
optional Blender MCP bridge can query the scene, run small `bpy` snippets and
capture viewport screenshots. It is not a build, CI, test or runtime dependency;
setup, safety and the part workflow are in
[`blender-mcp.md`](blender-mcp.md).
## Realism pass: scene concepts, skylines and textures ## Realism pass: scene concepts, skylines and textures
The skyline, surface tiles and phase lighting were rebuilt against AI concept frames. The The skyline, surface tiles and phase lighting were rebuilt against AI concept frames. The
+13 -7
View File
@@ -35,6 +35,12 @@ captures in `docs/screenshots/`.
doors, stair handrails and a dark sedan. Today's in-game props are primitive doors, stair handrails and a dark sedan. Today's in-game props are primitive
boxes, and the trees are cones. boxes, and the trees are cones.
## Optional MCP
Blender MCP can be used for interactive head/face/hand part exploration, but it
is not required. The headless commands below remain the build and audit path;
see [`blender-mcp.md`](blender-mcp.md) for installation, connection and safety.
## Running it ## Running it
```bash ```bash
@@ -98,12 +104,12 @@ All eight use the `npc_base` contract, so each one can replace
`Materials.npc_slot()` tints them the same way it tints `npc_base`. Cloth and `Materials.npc_slot()` tints them the same way it tints `npc_base`. Cloth and
pants repeat the weave 4× (`NPC_FABRIC_UV_SCALE`) and intentionally skip the pants repeat the weave 4× (`NPC_FABRIC_UV_SCALE`) and intentionally skip the
per-character baked AO; skin, hair and fixed gear keep it. per-character baked AO; skin, hair and fixed gear keep it.
- budget ≤ **8,000 triangles and ≤ 8,000 glTF vertices** for the eight library - budget ≤ **16,000 triangles and ≤ 16,000 glTF vertices** per character (vertex count after
variants (vertex count after seam splits; `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in seam splits; `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in `tools/blender_assets.py`). The 13 core bones
`tools/blender_assets.py`, raised from 6,000/5,000 on 2026-11-02). The frozen remain required; optional extra bones are allowed as long as the core rest positions and
`npc_base` fallback keeps its original 6,000/4,000 contract. The current range `NpcAvatar` animation proxies are unchanged. The frozen low-poly `npc_base` fallback keeps its
is 4,482–5,474 triangles and 4,260–5,494 vertices, leaving room for the compact geometry but is no longer capped at 6,000/4,000. The current range is 5,038–6,030
remaining grip and clothing detail. triangles and 4,756–5,902 vertices, leaving substantial head/hand headroom.
Construction: the torso and limbs are one continuous Skin-modifier mesh, Construction: the torso and limbs are one continuous Skin-modifier mesh,
subdivided twice, decimated to 26 % with X symmetry, and cut at the waist, cuffs, subdivided twice, decimated to 26 % with X symmetry, and cut at the waist, cuffs,
@@ -185,7 +191,7 @@ carry baked AO and the shared neutral material maps described above.
- `python3 tools/blender_assets.py audit`: container, `POSITION`/`NORMAL`/ - `python3 tools/blender_assets.py audit`: container, `POSITION`/`NORMAL`/
`TEXCOORD_0`, UVs in [0, 1], no zero-area triangles (Godot warns about them `TEXCOORD_0`, UVs in [0, 1], no zero-area triangles (Godot warns about them
during LOD generation), budgets (library characters 8000/8000, base fallback 6000/4000, gear 1500, props 4000 during LOD generation), budgets (characters 16000/16000, gear 1500, props 4000
triangles), grounding, and an editable `.blend`. For characters it also triangles), grounding, and an editable `.blend`. For characters it also
checks skinning, bones, slots, the `npc_base` mesh node and the height. checks skinning, bones, slots, the `npc_base` mesh node and the height.
- `tools/tests/test_blender_assets.py`: catalogue and contract consistency with - `tools/tests/test_blender_assets.py`: catalogue and contract consistency with
+149
View File
@@ -0,0 +1,149 @@
# Optional Blender MCP
Status: **optional interactive tooling, not a build dependency.** Blender MCP is
useful for exploring a local part scene, moving the viewport and iterating on a
head/hand mesh without a manual UI walkthrough. The authoritative asset
pipeline remains the headless CLI plus `audit`/`fit`/`inspect`; CI, tests and
release builds must continue to work with no Blender process and no MCP server.
This project does not vend the third-party addon or pin a runtime MCP
dependency. Install it outside the repository and keep generated scenes under
`tmp/`.
## What it adds
With Blender MCP connected, an agent can:
- query the current scene and object data;
- run small `bpy` snippets through the MCP bridge;
- capture a viewport screenshot for visual checks;
- inspect or modify a local part scene before exporting a GLB.
It does **not** replace:
- `python3 tools/blender_assets.py audit/fit/inspect`;
- `python3 tools/npc_proportions.py`;
- the Godot capture and smoke gates;
- the project's licence/provenance review.
## Prerequisites
- Blender 5.2 LTS, installed as the `org.blender.Blender` flatpak (the project
standard).
- An MCP-capable client.
- The upstream [BlenderMCP addon](https://github.com/ahujasid/blender-mcp)
(`addon.py`) and its MCP server.
- `uv`/`uvx` if you use the server's `uvx blender-mcp` launcher; a local install
of the server package is equally valid.
Check the upstream README for the current package/addon version and any command
changes before installing. Do not copy the addon into this repository.
## Setup
### 1. Install the Blender addon
1. Download or clone the upstream BlenderMCP project.
2. In Blender: **Edit → Preferences → Add-ons → Install from Disk**, select
`addon.py` from that checkout.
3. Enable **Interface: Blender MCP**.
4. Open the 3D View sidebar (N panel), find the **BlenderMCP** tab, and click
**Connect to MCP server**. The default server listens on
`localhost:9876`.
The addon must be running inside the Blender process that has the scene open.
Starting a second Blender instance does not connect the first one.
### 2. Configure the MCP client
For Pi, add the server at user scope so it is not committed into the project:
```bash
pi mcp add blender -- uvx blender-mcp
pi mcp list
```
Then start/reload Pi and use `/mcp` to inspect the connection. Other clients
use their normal MCP server configuration. A generic JSON example:
```json
{
"mcpServers": {
"blender": {
"command": "uvx",
"args": ["blender-mcp"]
}
}
}
```
For a client with a different config schema, use the equivalent server command
and keep the server name `blender`. Restart/reload the client after adding it,
then request the tool list and run a harmless `get_scene_info` before editing.
### 3. Connect through the flatpak
Start Blender through the project's normal flatpak command, open the local part
scene, and connect from the sidebar. If the client cannot reach the addon:
- confirm the addon panel says connected;
- confirm the client is launching the MCP server, not just a shell;
- check port `9876` is not occupied;
- if the flatpak sandbox is blocking localhost networking, grant network
permission only for this interactive use, for example:
`flatpak override --user --share=network org.blender.Blender`
(review the permission change afterwards; do not add it to CI or build
scripts).
- restart both the MCP client and Blender after changing the addon or config.
## Recommended part-workflow
1. Create a local scene, for example
`tmp/npc-parts/npc_casual/head.blend`, or open a copy of an exported part GLB.
Never save over the tracked `assets/models/npc/npc_base.blend`.
2. Ask the MCP client for `get_scene_info`, then inspect the object/bone/mesh
names it reports.
3. Use `execute_blender_code` for small, reversible edits. Prefer adding a new
object or modifier over deleting existing geometry; keep a backup copy.
4. Use `get_viewport_screenshot` after camera/material changes to check the
head/face/hand before exporting.
5. Export a **local** GLB under `tmp/npc-parts/`, not directly into
`assets/models/`.
6. Run the normal gates on the exported file:
```bash
flatpak run org.blender.Blender --background \
--python "$PWD/tools/blender_assets.py" -- \
inspect --file "$PWD/tmp/npc-parts/head.glb" --category npc --json
python3 tools/blender_assets.py audit
python3 tools/blender_assets.py fit
python3 tools/npc_proportions.py report
```
7. Only after the part is reviewed and the contract passes should it be
assembled into a character and rebuilt through the headless pipeline.
## Safety and project rules
- MCP is **not** part of `project.godot`, the build, CI, tests, release packages
or runtime.
- Do not commit `.blend` part scenes, MCP configuration, API keys or generated
previews. The working set stays in `tmp/`.
- Do not use MCP to bypass the contract, budgets, AO/UV rules or licence gate.
- The 13 core bones must remain at their rest positions; extra optional bones
are allowed only if `NpcAvatar` and the audit/tests still pass.
- Prefer scripted headless changes for reproducible work; MCP is for the
interactive exploration and review that is awkward in a one-shot script.
- If MCP is unavailable, use the headless CLI and document the manual step; never
make the project depend on MCP being present.
## Troubleshooting
| Symptom | Check |
| --- | --- |
| Client cannot find `blender` | MCP client config path/schema; restart client; confirm server command exists |
| `Connection refused` | Addon **Connect to MCP server** pressed; Blender still running; port 9876 |
| Tools return stale scene data | Refresh scene/object query after edits; restart the connection if needed |
| Flatpak cannot reach localhost | Check flatpak network permission; grant only for interactive use |
| Changes look wrong in export | Re-import the GLB with `inspect`; check transforms, scale, UVs and materials |
| Contract audit fails | Fix the mesh/bones first; do not edit the audit expectations around a broken asset |
+2 -1
View File
@@ -62,7 +62,8 @@ Python tooling is 161/161 after the GLB `inspect` intake coverage.
- Texture visual verification (0.9.5 work): the nine-stage realism run used `z-ai/glm-5.3-flash` once per shot; mean `after` scores were overall 1.8, lighting 2.3, materials 1.8, texture detail 1.2, backdrop 2.0 and characters 1.2, with a Pillow metric gap of 0.306. The judge remains an optional review aid, not a CI gate. The committed before/after close-up sheet is [`blender_previews/sheet_texture_refinement_0.9.5.jpg`](blender_previews/sheet_texture_refinement_0.9.5.jpg); it records that the paver/Walk of Fame and NPC fabric repairs are visible, while asphalt, limestone and skyline picks held without a replacement. `tools/check_screenshots.sh` passed 21 good / 0 bad and the screen-design judge passed 21/21 match with 0 review. - Texture visual verification (0.9.5 work): the nine-stage realism run used `z-ai/glm-5.3-flash` once per shot; mean `after` scores were overall 1.8, lighting 2.3, materials 1.8, texture detail 1.2, backdrop 2.0 and characters 1.2, with a Pillow metric gap of 0.306. The judge remains an optional review aid, not a CI gate. The committed before/after close-up sheet is [`blender_previews/sheet_texture_refinement_0.9.5.jpg`](blender_previews/sheet_texture_refinement_0.9.5.jpg); it records that the paver/Walk of Fame and NPC fabric repairs are visible, while asphalt, limestone and skyline picks held without a replacement. `tools/check_screenshots.sh` passed 21 good / 0 bad and the screen-design judge passed 21/21 match with 0 review.
- NPC visual optimization (0.9.4, complete 2026-11-02): all eight variants passed the portrait/readability, proportion, gear, foot/shoe, headgear and final-context gates. The library uses an 8,000-triangle / 8,000-glTF-vertex cap (up from 6,000/5,000; `npc_base` fallback remains 6,000/4,000); current characters measure 4,482–5,474 triangles and 4,260–5,494 vertices. The 48-frame baseline and 96-frame final-context review pass, the hand/gear offset and shoes are in, and the accepted stylized limits are documented in `npc-visual-review.md`. - NPC visual optimization (0.9.4, complete 2026-11-02): all eight variants passed the portrait/readability, proportion, gear, foot/shoe, headgear and final-context gates. The library uses an 8,000-triangle / 8,000-glTF-vertex cap (up from 6,000/5,000; `npc_base` fallback remains 6,000/4,000); current characters measure 4,482–5,474 triangles and 4,260–5,494 vertices. The 48-frame baseline and 96-frame final-context review pass, the hand/gear offset and shoes are in, and the accepted stylized limits are documented in `npc-visual-review.md`.
- NPC face-detail pass (0.9.4 work): ala wings and a smaller nose bridge/tip, one rounded mouth slot, and a darker ear concha; all eight rebuilt. The comparison is `sheet_npc_baseline_compare-face-detail.jpg`. - NPC face-detail pass (0.9.4 work): ala wings and a smaller nose bridge/tip, one rounded mouth slot, and a darker ear concha; all eight rebuilt. The comparison is `sheet_npc_baseline_compare-face-detail.jpg`.
- NPC face realism pass (2026-11-03/04): all eight variants now use the `seg=24/rings=16` head with a subtle brow ridge, a fuller lower lip and both eyelids (recessed creases were rejected because they bake as dark AO patches). Vision-reviewed as cleaner/more realistic than the 0.9.4 face; characters measure 5,038–5,900 tris / 4,756–5,902 verts (still under 8,000). `sheet_npc_baseline.jpg` was refreshed. - Character contract extension (2026-11-04): the character cap is **16,000 triangles / 16,000 glTF vertices** (library and base fallback); the 13 core bones remain required and optional extra bones are allowed. Blender MCP is documented as optional interactive tooling in [`blender-mcp.md`](blender-mcp.md), not a build/CI/runtime dependency. Blender/NPC audits still pass.
- NPC face realism pass (2026-11-03/04): all eight variants now use the `seg=24/rings=16` head with a subtle brow ridge, a fuller lower lip and both eyelids (recessed creases were rejected because they bake as dark AO patches). Vision-reviewed as cleaner/more realistic than the 0.9.4 face; characters measure 5,038–5,900 tris / 4,756–5,902 verts (still under 16,000). `sheet_npc_baseline.jpg` was refreshed.
- Crowd-variety roster (character development, 2026-11-04): four new variants — `npc_crowd_a` (slim/bun/sunglasses), `npc_crowd_b` (broad/bald/beard/shoulder bag), `npc_crowd_c` (coat/umbrella), `npc_crowd_d` (slim/hood/cup) — built with the realistic face and wired into `CHARACTER_POOLS` so the routine crowd roles (tourist, confused visitor, intoxicated) pick them by seed instead of cloning `npc_casual`. The 24-NPC smoke is now 152,464 tris / 168 meshes / 6.94 ms (under the 50 ms budget). - Crowd-variety roster (character development, 2026-11-04): four new variants — `npc_crowd_a` (slim/bun/sunglasses), `npc_crowd_b` (broad/bald/beard/shoulder bag), `npc_crowd_c` (coat/umbrella), `npc_crowd_d` (slim/hood/cup) — built with the realistic face and wired into `CHARACTER_POOLS` so the routine crowd roles (tourist, confused visitor, intoxicated) pick them by seed instead of cloning `npc_casual`. The 24-NPC smoke is now 152,464 tris / 168 meshes / 6.94 ms (under the 50 ms budget).
- NPC headgear clearance (0.9.4 work): the 96-frame final-context review after the face-detail pass confirms the cap, firefighter helmet, hood and sunglasses clear the refined brow and eyes; no gear rebuild was needed. - NPC headgear clearance (0.9.4 work): the 96-frame final-context review after the face-detail pass confirms the cap, firefighter helmet, hood and sunglasses clear the refined brow and eyes; no gear rebuild was needed.
- NPC footwear pass (0.9.4 work): casual shoes gained rounded toe and heel caps; responder/firefighter boots gained matching toe/heel volumes while retaining the black rubber boot language. All eight rebuilt; comparison `sheet_npc_baseline_compare-shoes.jpg`. - NPC footwear pass (0.9.4 work): casual shoes gained rounded toe and heel caps; responder/firefighter boots gained matching toe/heel volumes while retaining the black rubber boot language. All eight rebuilt; comparison `sheet_npc_baseline_compare-shoes.jpg`.
+5 -5
View File
@@ -28,7 +28,7 @@ Any expansion must preserve the existing integration contract:
`Hips`, `Spine`, `Neck` at the contract rest positions; `Hips`, `Spine`, `Neck` at the contract rest positions;
- five tint slots (`skin`, `cloth`, `pants`, `accessory`, `hair`); - five tint slots (`skin`, `cloth`, `pants`, `accessory`, `hair`);
- front on Blender −Y / Godot +Z, feet near the floor, 1.6–2.1 m height; - front on Blender −Y / Godot +Z, feet near the floor, 1.6–2.1 m height;
- at most 8,000 triangles and 8,000 glTF vertices per character; - at most 16,000 triangles and 16,000 glTF vertices per character;
- gear attach points at head, back, chest and hand. - gear attach points at head, back, chest and hand.
The existing `audit` command is the gate. A source that looks good in a preview The existing `audit` command is the gate. A source that looks good in a preview
@@ -68,8 +68,8 @@ Every example fails the unmodified `audit` contract:
| `npc_base` mesh node | no | no | no | no | no | | `npc_base` mesh node | no | no | no | no | no |
| Required 13 bones | no (62 different names) | no rig | no (19 different names) | no (19 different names) | no rig | | Required 13 bones | no (62 different names) | no rig | no (19 different names) | no (19 different names) | no rig |
| Five tint slots | no | no | no | no | no | | Five tint slots | no | no | no | no | no |
| ≤ 8,000 tris | yes | yes | yes | yes | no | | ≤ 16,000 tris | yes | yes | yes | yes | yes |
| ≤ 8,000 verts | no (10.5–13k) | yes | yes | yes | no (19k) | | ≤ 16,000 verts | yes (10.5–13k) | yes | yes | yes | no (19k) |
| Feet on floor | yes | yes (2.7 m scale) | yes (−11 mm) | yes | no (needs scaling) | | Feet on floor | yes | yes (2.7 m scale) | yes (−11 mm) | yes | no (needs scaling) |
| Height 1.6–2.1 m | yes | no (2.7 m) | no (1.45 m) | no (1.48 m) | needs scale | | Height 1.6–2.1 m | yes | no (2.7 m) | no (1.45 m) | no (1.48 m) | needs scale |
| Skinned / UV mapped | yes / yes | no / yes | yes / yes | yes / no | no / no | | Skinned / UV mapped | yes / yes | no / yes | yes / yes | yes / no | no / no |
@@ -211,7 +211,7 @@ the A-pose arms down (−38°/+38°) and scales them **1.63×** to the contract
arm, welds the split vertices, smooth-shades, adds the 13-bone contract armature arm, welds the split vertices, smooth-shades, adds the 13-bone contract armature
with the project's distance-to-bone `_auto_weights`, paints the five slot regions, with the project's distance-to-bone `_auto_weights`, paints the five slot regions,
renames the mesh to `npc_base`, and exports `makehuman_retargeted.glb` — **6,444 / renames the mesh to `npc_base`, and exports `makehuman_retargeted.glb` — **6,444 /
8,000 tris, 7,726 / 8,000 verts**, 14 bones, skinned, arm-down, grounded, five 16,000 tris, 7,726 / 16,000 verts**, 14 bones, skinned, arm-down, grounded, five
slots, no contract issues. The proportion warp (1.63× arms) is the same class of slots, no contract issues. The proportion warp (1.63× arms) is the same class of
issue that blocked Quaternius; here it is accepted, but the vision review shows issue that blocked Quaternius; here it is accepted, but the vision review shows
it distorts the deltoid/shoulder, the decimated hands are blocky, and the slot it distorts the deltoid/shoulder, the decimated hands are blocky, and the slot
@@ -283,7 +283,7 @@ are:
- Weld duplicate seam vertices before decimating. Quaternius' 12k imported - Weld duplicate seam vertices before decimating. Quaternius' 12k imported
vertices are mostly UV/normal splits; the underlying surface is much smaller, vertices are mostly UV/normal splits; the underlying surface is much smaller,
but the glTF budget counts the splits after export. but the glTF budget counts the splits after export.
- Decimate to a target below 8,000 tris/verts, then re-unwrap only if the - Decimate to a target below 16,000 tris/verts, then re-unwrap only if the
existing UVs cannot survive the material remap. The current library uses existing UVs cannot survive the material remap. The current library uses
smart projection at 80° to keep islands large. smart projection at 80° to keep islands large.
- Map materials by semantic role, not by source name. A source "Purple" or - Map materials by semantic role, not by source name. A source "Purple" or
+5 -5
View File
@@ -71,9 +71,9 @@ The criteria below are for the stylized target, not photorealism. A change must
pass every applicable item before a library rebuild is accepted. pass every applicable item before a library rebuild is accepted.
1. **Asset contract:** `npc_base` mesh node, the required bones, five tint slots, 1. **Asset contract:** `npc_base` mesh node, the required bones, five tint slots,
front +Z, feet within 3 cm of y=0, 1.6–2.1 m height, at most 8,000 triangles front +Z, feet within 3 cm of y=0, 1.6–2.1 m height, at most 16,000 triangles
and 8,000 glTF vertices per library character. The frozen `npc_base` fallback and 16,000 glTF vertices per character. The required 13 core bones stay fixed;
keeps 6,000 triangles / 4,000 vertices. Gear remains at most 1,500 triangles. optional extra bones are allowed. Gear remains at most 1,500 triangles.
2. **Proportion and silhouette:** head, neck, shoulders, torso, hips, limbs and 2. **Proportion and silhouette:** head, neck, shoulders, torso, hips, limbs and
feet read as one body; front/three-quarter/profile silhouettes agree with the feet read as one body; front/three-quarter/profile silhouettes agree with the
rest of the eight-character library; broad/slim/average build variation is rest of the eight-character library; broad/slim/average build variation is
@@ -134,8 +134,8 @@ From `python3 tools/blender_assets.py fit` and the manifest:
| `npc_firefighter` | 5,344 | 5,494 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | | `npc_firefighter` | 5,344 | 5,494 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 |
| `npc_guard` | 5,046 | 5,096 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | | `npc_guard` | 5,046 | 5,096 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 |
`npc_coat` is widest at 5,474 triangles (2,526 below the 8,000 cap); `npc_coat` is widest at 5,474 triangles (well below the 16,000 cap);
`npc_firefighter` is closest on vertices at 5,494 (2,506 below the 8,000 cap). `npc_firefighter` is closest on vertices at 5,494 (well below the 16,000 cap).
The refit backpack measures 380 triangles / 406 vertices. The refit backpack measures 380 triangles / 406 vertices.
## Baseline findings, ranked ## Baseline findings, ranked
+15 -13
View File
@@ -53,13 +53,14 @@ MANIFEST = OUT_DIR / "manifest.json"
SLOTS = ["skin", "cloth", "pants", "accessory", "hair"] SLOTS = ["skin", "cloth", "pants", "accessory", "hair"]
BONES = ["Head", "HipL", "HipR", "KneeL", "KneeR", "ShoulderL", "ShoulderR", BONES = ["Head", "HipL", "HipR", "KneeL", "KneeR", "ShoulderL", "ShoulderR",
"ElbowL", "ElbowR", "HandL", "HandR"] "ElbowL", "ElbowR", "HandL", "HandR"]
# The eight library variants may use more geometry than the frozen npc_base # Character cap raised to 16000/16000 on 2026-11-04 for the head/face/hand
# fallback (6000 tris / 4000 verts). Raised to 8000/8000 on 2026-11-02 so the # overhaul and possible MakeHuman parts. The required 13 core bones stay, but
# remaining face, hand-grip and clothing work is not forced to replace detail. # optional extra bones (fingers, face, toes) are allowed; they must not move the
# The glTF vertex count is after UV-seam splits; each avatar is still one # core rest positions. The glTF vertex count is after UV-seam splits; each
# skinned mesh, and `smoke_npc_budget.gd` must confirm the 24-NPC frame time. # avatar is still one skinned mesh, and `smoke_npc_budget.gd` must confirm the
CHAR_MAX_TRIS = 8000 # 24-NPC frame time before accepting the increase.
CHAR_MAX_VERTS = 8000 CHAR_MAX_TRIS = 16000
CHAR_MAX_VERTS = 16000
GEAR_MAX_TRIS = 1500 GEAR_MAX_TRIS = 1500
PROP_MAX_TRIS = 4000 PROP_MAX_TRIS = 4000
@@ -2135,14 +2136,15 @@ def inspect_glb(path, category=None) -> dict:
return result return result
def adapt_glb(path, out_path, target_tris=8000): def adapt_glb(path, out_path, target_tris=16000):
"""Import an external GLB and adapt it toward the library contract. """Import an external GLB and adapt it toward the library contract.
Applies node transforms, grounds the feet (lowest vertex at y = 0), welds Applies node transforms, grounds the feet (lowest vertex at y = 0), welds
duplicated seam vertices and decimates to `target_tris`, then exports the duplicated seam vertices and decimates to `target_tris`, then exports the
result to `out_path` as a GLB. The five tint slots and the 13-bone contract result to `out_path` as a GLB. The five tint slots and the required 13 core
armature are deliberately NOT auto-mapped — that is a manual retopology/rig bones (optional extra bones are allowed) are deliberately NOT auto-mapped —
step. Returns before/after measurements for the intake report. that is a manual retopology/rig step. Returns before/after measurements for
the intake report.
""" """
if bpy is None: if bpy is None:
return {"ok": False, "error": "adapt must run inside Blender"} return {"ok": False, "error": "adapt must run inside Blender"}
@@ -2537,8 +2539,8 @@ def main(argv=None):
ada.add_argument("--file", required=True, ada.add_argument("--file", required=True,
help="external GLB to adapt (absolute path; runs in Blender)") help="external GLB to adapt (absolute path; runs in Blender)")
ada.add_argument("--out", required=True, help="where to write the adapted GLB") ada.add_argument("--out", required=True, help="where to write the adapted GLB")
ada.add_argument("--target-tris", type=int, default=8000, ada.add_argument("--target-tris", type=int, default=16000,
help="decimate target (default 8000)") help="decimate target (default 16000)")
tex = sub.add_parser("texels", help="per-slot texel density of the character GLBs") tex = sub.add_parser("texels", help="per-slot texel density of the character GLBs")
tex.add_argument("--only", default="") tex.add_argument("--only", default="")
tex.add_argument("--json", action="store_true") tex.add_argument("--json", action="store_true")
+4 -3
View File
@@ -23,7 +23,8 @@ Contract (frozen in ADR-0013 and docs/blender-assets.md):
(plus Hips/Spine/Neck as parents) (plus Hips/Spine/Neck as parents)
* material slots: skin, cloth, pants, accessory, hair * material slots: skin, cloth, pants, accessory, hair
* neutral albedo + normal + packed ORM maps, albedo tinted at runtime * neutral albedo + normal + packed ORM maps, albedo tinted at runtime
* <= 6000 triangles / 4000 vertices, UVs in [0,1], feet at y ~= 0 * <= 16000 triangles / 16000 vertices, UVs in [0,1], feet at y ~= 0
* the 13 core bones are required; extra optional bones are allowed
""" """
import argparse import argparse
@@ -44,8 +45,8 @@ BONES = [
"Head", "HipL", "HipR", "KneeL", "KneeR", "Head", "HipL", "HipR", "KneeL", "KneeR",
"ShoulderL", "ShoulderR", "ElbowL", "ElbowR", "HandL", "HandR", "ShoulderL", "ShoulderR", "ElbowL", "ElbowR", "HandL", "HandR",
] ]
MAX_TRIANGLES = 6000 MAX_TRIANGLES = 16000
MAX_VERTICES = 4000 MAX_VERTICES = 16000
TEX_SIZE = 256 TEX_SIZE = 256
AI_DIR = TEX_DIR / "ai" AI_DIR = TEX_DIR / "ai"
# Base material slots that get an AI fabric albedo (`prepare` layers them over # Base material slots that get an AI fabric albedo (`prepare` layers them over
+14 -8
View File
@@ -9,11 +9,10 @@ class CatalogueTests(unittest.TestCase):
def test_contract_matches_npc_pipeline(self): def test_contract_matches_npc_pipeline(self):
self.assertEqual(ba.SLOTS, npc_pipeline.SLOTS) self.assertEqual(ba.SLOTS, npc_pipeline.SLOTS)
self.assertEqual(sorted(ba.BONES), sorted(npc_pipeline.BONES)) self.assertEqual(sorted(ba.BONES), sorted(npc_pipeline.BONES))
# The base mesh keeps the frozen 6000/4000 contract; the eight library # The character cap was raised to 16000/16000 for the head/face/hand
# variants have their own 8000/8000 detail budget (documented in # overhaul; the 13 core bones stay required and extra bones are optional.
# blender-assets.md and covered by smoke_npc_budget.gd). self.assertLessEqual(ba.CHAR_MAX_TRIS, 16000)
self.assertLessEqual(ba.CHAR_MAX_TRIS, 8000) self.assertLessEqual(ba.CHAR_MAX_VERTS, 16000)
self.assertLessEqual(ba.CHAR_MAX_VERTS, 8000)
def test_gear_uses_known_attach_points(self): def test_gear_uses_known_attach_points(self):
for name, attach in ba.GEAR.items(): for name, attach in ba.GEAR.items():
@@ -188,10 +187,17 @@ class InspectionTests(unittest.TestCase):
self.assertEqual(result["bones_missing"], []) self.assertEqual(result["bones_missing"], [])
self.assertEqual(result["budget"]["max_triangles"], ba.CHAR_MAX_TRIS) self.assertEqual(result["budget"]["max_triangles"], ba.CHAR_MAX_TRIS)
def test_extra_optional_bones_are_allowed(self):
stats = self.stats(bones=list(ba.BONES) + ["Hips", "Spine", "Neck",
"FingerL", "FingerR"])
result = ba.analyse_inspection(stats)
self.assertTrue(result["ok"], result["issues"])
self.assertEqual(result["bones_missing"], [])
def test_character_contract_failures_are_listed(self): def test_character_contract_failures_are_listed(self):
stats = self.stats(mesh_nodes=["Armature"], skinned=False, unskinned=["Armature"], stats = self.stats(mesh_nodes=["Armature"], skinned=False, unskinned=["Armature"],
materials=["cloth"], bones=["Head"], meshes_without_uv=1, materials=["cloth"], bones=["Head"], meshes_without_uv=1,
triangles=9000, split_vertices=9000, triangles=17000, split_vertices=17000,
world_bounds={"min": [-0.3, -0.4, -0.2], "max": [0.3, 1.2, 0.2]}) world_bounds={"min": [-0.3, -0.4, -0.2], "max": [0.3, 1.2, 0.2]})
issues = "\n".join(ba.analyse_inspection(stats)["issues"]) issues = "\n".join(ba.analyse_inspection(stats)["issues"])
self.assertIn("no mesh node named npc_base", issues) self.assertIn("no mesh node named npc_base", issues)
@@ -199,8 +205,8 @@ class InspectionTests(unittest.TestCase):
self.assertIn("missing material slot skin", issues) self.assertIn("missing material slot skin", issues)
self.assertIn("mesh not skinned", issues) self.assertIn("mesh not skinned", issues)
self.assertIn("mesh(es) without UVs", issues) self.assertIn("mesh(es) without UVs", issues)
self.assertIn(f"triangles 9000 > {ba.CHAR_MAX_TRIS}", issues) self.assertIn(f"triangles 17000 > {ba.CHAR_MAX_TRIS}", issues)
self.assertIn(f"glTF vertices 9000 > {ba.CHAR_MAX_VERTS}", issues) self.assertIn(f"glTF vertices 17000 > {ba.CHAR_MAX_VERTS}", issues)
self.assertIn("feet not on the ground", issues) self.assertIn("feet not on the ground", issues)
self.assertIn("unexpected height", issues) self.assertIn("unexpected height", issues)
+2 -2
View File
@@ -13,8 +13,8 @@ class NpcContractTests(unittest.TestCase):
"ShoulderL", "ShoulderR", "ElbowL", "ElbowR", "ShoulderL", "ShoulderR", "ElbowL", "ElbowR",
"HandL", "HandR"]: "HandL", "HandR"]:
self.assertIn(bone, npc_pipeline.BONES) self.assertIn(bone, npc_pipeline.BONES)
self.assertLessEqual(npc_pipeline.MAX_TRIANGLES, 6000) self.assertLessEqual(npc_pipeline.MAX_TRIANGLES, 16000)
self.assertLessEqual(npc_pipeline.MAX_VERTICES, 4000) self.assertLessEqual(npc_pipeline.MAX_VERTICES, 16000)
def test_textures_exist_for_every_slot(self): def test_textures_exist_for_every_slot(self):
for slot in npc_pipeline.SLOTS: for slot in npc_pipeline.SLOTS: