Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3acf370fc | ||
|
|
04d211b90b | ||
|
|
a6ccd86f85 | ||
|
|
1ce173e9d1 | ||
|
|
90f189a269 | ||
|
|
fb3e75aba3 | ||
|
|
cd285b3a39 | ||
|
|
68f49253fe | ||
|
|
b012c6a675 | ||
|
|
2a6f29f5d0 | ||
|
|
bb54ef3eec | ||
|
|
11f53f66a3 | ||
|
|
9b65a93587 | ||
|
|
56a14747d4 | ||
|
|
8b1bf736af | ||
|
|
f121167e52 | ||
|
|
140a7ada0f | ||
|
|
5508f5510f | ||
|
|
7baa2c4f54 | ||
|
|
0cd12af994 | ||
|
|
271f10ac4d | ||
|
|
e2f52fbfd1 | ||
|
|
f5fa321a8f | ||
|
|
a2ae9e03c3 | ||
|
|
cff3937c16 | ||
|
|
1ed9e05d9b | ||
|
|
8c69f367b0 | ||
|
|
2a7921bae6 | ||
|
|
b419923637 | ||
|
|
c7fd4b59cc | ||
|
|
3fa3482608 | ||
|
|
1049d8631e | ||
|
|
deb74c53af | ||
|
|
9968bc0a4c | ||
|
|
2e1bb19e2c | ||
|
|
5450dbf88a | ||
|
|
79224c5acf | ||
|
|
f0d4b81239 | ||
|
|
48a9795248 | ||
|
|
a5e62534ce | ||
|
|
cd7697cba4 | ||
|
|
d2f773c7ad | ||
|
|
a3c3a37270 | ||
|
|
8dd4f77901 | ||
|
|
ba444152c9 | ||
|
|
d56f14c6dd | ||
|
|
b297c4543f | ||
|
|
958d3ae0ff | ||
|
|
d1898b3221 | ||
|
|
2e1e4d7f70 | ||
|
|
0b2f124ffe | ||
|
|
cdd2e02564 | ||
|
|
632ec16aaa | ||
|
|
30f240b591 | ||
|
|
58d1e79a12 | ||
|
|
44dba010a7 | ||
|
|
2a53ec9aac | ||
|
|
f0f609fe1b | ||
|
|
b386a21173 | ||
|
|
00bd197173 | ||
|
|
4f1eb1f5bf | ||
|
|
50edc47480 | ||
|
|
b8d8869e03 | ||
|
|
1b554df516 | ||
|
|
5fdda88d46 | ||
|
|
ecd50503e7 | ||
|
|
2b6e774580 | ||
|
|
dee939c2ee | ||
|
|
aec1fb7b69 | ||
|
|
50a48d2256 | ||
|
|
69f4f11633 | ||
|
|
d1fa25629e | ||
|
|
359bb9a8c7 | ||
|
|
36576a023a | ||
|
|
c1ccfd6e72 | ||
|
|
27b8f9a249 | ||
|
|
d5a2c911a6 | ||
|
|
236fbf537f | ||
|
|
588722fe7a | ||
|
|
7f2f06bf42 | ||
|
|
6db3cc0873 | ||
|
|
8938dd918c | ||
|
|
742bfd3898 | ||
|
|
87069a30e8 | ||
|
|
3f5c5ce442 | ||
|
|
28fcd96b59 | ||
|
|
e984129597 | ||
|
|
75dea2e7c2 | ||
|
|
09d07452af | ||
|
|
f5ee2b7408 | ||
|
|
5980d329e8 | ||
|
|
6052ea391b | ||
|
|
4e8665e25b | ||
|
|
b945eb6e8f | ||
|
|
03c13c716a | ||
|
|
0a9c78cb30 | ||
|
|
abe5c3f933 | ||
|
|
140ab42536 | ||
|
|
8bdfd0176e | ||
|
|
4e23cc3ad5 | ||
|
|
8d32a8cac0 | ||
|
|
2445ddab34 | ||
|
|
8cacd510bf | ||
|
|
bf383aa0fa | ||
|
|
4bbc576b7f | ||
|
|
542b1264da | ||
|
|
56f4833c8e | ||
|
|
1b31dfdbe4 | ||
|
|
5158efc467 | ||
|
|
ac0cf35cfb | ||
|
|
6655edb5c6 | ||
|
|
60f389c16b | ||
|
|
3084145031 | ||
|
|
34bca89885 | ||
|
|
b53e8a67f9 | ||
|
|
cdfd57fb65 | ||
|
|
5bab6754c0 | ||
|
|
7dd8251a0c | ||
|
|
8e113a4110 | ||
|
|
32c9c45605 | ||
|
|
e4065c2068 | ||
|
|
e7d733bc93 | ||
|
|
93da916f90 | ||
|
|
e3ec4a356a | ||
|
|
55339f23c1 | ||
|
|
a51dae2c33 | ||
|
|
6824dd76a1 | ||
|
|
fde4aa4d0a | ||
|
|
ebb36cf0cb | ||
|
|
724e8b183f | ||
|
|
e4e1566e55 | ||
|
|
0aacee6518 | ||
|
|
215b843b67 | ||
|
|
53d9dc3f78 | ||
|
|
98f2f95f07 | ||
|
|
a677a95d13 | ||
|
|
1afd97c72d | ||
|
|
6ca8bcf034 | ||
|
|
13eb4e0c87 | ||
|
|
8fac1b47cf | ||
|
|
b79e30e9df | ||
|
|
1e761e5a76 | ||
|
|
3eb2ae5e3c | ||
|
|
888897cac9 | ||
|
|
988d431da8 | ||
|
|
87e1c9e7a5 | ||
|
|
480a1ba05f | ||
|
|
43c17ce3c0 | ||
|
|
03f4d4bc49 | ||
|
|
15f9117517 | ||
|
|
78267918bf | ||
|
|
ea3860a60c | ||
|
|
b721f189c5 | ||
|
|
634b1f75a3 | ||
|
|
36583921cf | ||
|
|
3866b0ae01 | ||
|
|
8c2761427f | ||
|
|
2b07913481 | ||
|
|
768dd94bfc | ||
|
|
f516d3e796 | ||
|
|
f95a685934 | ||
|
|
05cb6a43cb | ||
|
|
343b3348c2 | ||
|
|
ad91e71eeb | ||
|
|
6e04c59589 | ||
|
|
e21ff30783 | ||
|
|
5b306b1c52 | ||
|
|
f09a5e7937 | ||
|
|
383f601bc9 | ||
|
|
c46db76d85 | ||
|
|
1a5f2863b0 | ||
|
|
a6fd77068a | ||
|
|
4ed73fcc7b | ||
|
|
c0ddce79e8 | ||
|
|
0c7c9a2b23 | ||
|
|
b94b87d096 | ||
|
|
5dec59355a | ||
|
|
fe3ba3054e | ||
|
|
88a0793bba | ||
|
|
1d4f718376 | ||
|
|
7a5b902797 | ||
|
|
2d67e14e52 | ||
|
|
2443343f2b | ||
|
|
3e41cda591 | ||
|
|
84bc4137cb | ||
|
|
5dc3ef0f4c | ||
|
|
6f3facff43 |
No files matched your search
@@ -0,0 +1,212 @@
|
|||||||
|
---
|
||||||
|
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 required 13 core bones (optional extra bones allowed), five tint slots, 24,000/24,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.
|
||||||
|
metadata:
|
||||||
|
project: Gig Line
|
||||||
|
domain: 3d-assets
|
||||||
|
version: "1.0.0"
|
||||||
|
triggers: Blender, NPC, character, gear, prop, npc_base, GLB, AO, texel, asset rebuild, fit, audit
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gig Line Blender Asset Contract
|
||||||
|
|
||||||
|
Use this skill for any change that touches:
|
||||||
|
|
||||||
|
- `assets/models/blender/**`
|
||||||
|
- `assets/models/generated/npc_base.glb`
|
||||||
|
- `assets/textures/npc/**`
|
||||||
|
- `tools/blender_assets.py`, `tools/npc_pipeline.py`, `tools/bake_ao.py`,
|
||||||
|
`tools/npc_proportions.py` or `tools/npc_baseline.py`
|
||||||
|
- Blender-authored venue/prop geometry
|
||||||
|
|
||||||
|
The project contract and measured gates override any generic Blender advice.
|
||||||
|
Blender MCP is optional interactive tooling; the headless flatpak CLI remains
|
||||||
|
authoritative and CI never needs Blender or MCP. The `blender-skills` pack is
|
||||||
|
optional, local guidance only, and gitignored. Setup and safety are in
|
||||||
|
`docs/asset-tooling.md#optional-interactive-blender-mcp`.
|
||||||
|
|
||||||
|
## Read first
|
||||||
|
|
||||||
|
From the repository root, read the relevant sources rather than relying on this
|
||||||
|
skill alone:
|
||||||
|
|
||||||
|
1. `AGENTS.md` — required workflow and verification.
|
||||||
|
2. `TODO.md` and `MEMORY.md` — current track, claimed tasks, known gotchas.
|
||||||
|
3. `docs/blender-assets.md` — library contract and command reference.
|
||||||
|
4. `docs/asset-tooling.md` — exact rebuild/bake/capture commands.
|
||||||
|
5. `docs/npc-visual-review.md` and `docs/3d-model-review.md` — accepted limits,
|
||||||
|
scorecards and known blockers.
|
||||||
|
6. `docs/known-limitations.md` — limits that must not be silently “fixed” into
|
||||||
|
contract changes.
|
||||||
|
|
||||||
|
The contract lives in code and docs, not in this skill. If they disagree, stop
|
||||||
|
and resolve the source of truth before editing assets.
|
||||||
|
|
||||||
|
## Non-negotiable contract
|
||||||
|
|
||||||
|
| Area | Rule |
|
||||||
|
| --- | --- |
|
||||||
|
| Mesh root | `npc_base` mesh node, skinned under `Rig`; preserve the limb node names. |
|
||||||
|
| 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`. |
|
||||||
|
| Orientation | Front is Blender −Y / Godot +Z; feet grounded near y = 0; metres. |
|
||||||
|
| Character budget | **24,000 triangles / 24,000 glTF vertices** per character (`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS`). |
|
||||||
|
| Frozen fallback | `npc_base` remains the low-poly fallback; it shares the 24,000/24,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. |
|
||||||
|
| 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. |
|
||||||
|
| Textures | 256² albedo/normal/ORM maps. Cloth and pants use `NPC_FABRIC_UV_SCALE = (4,4,1)` and **skip** baked per-character AO; skin/hair/gear keep baked AO. |
|
||||||
|
| CI | Offline and skill-free. Committed GLBs/AO/maps must make CI pass without Blender or MCP. |
|
||||||
|
| Licensing | No new asset source without ADR-0015: CC0, or CC-BY with attribution in `CREDITS.md`; record provenance. |
|
||||||
|
|
||||||
|
Do not change the renderer, engine baseline, runtime dependency graph or the
|
||||||
|
`npc_base` contract as part of an asset pass.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Preflight and claim
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git log --oneline -5
|
||||||
|
godot --version
|
||||||
|
flatpak run org.blender.Blender --version | head -3
|
||||||
|
```
|
||||||
|
|
||||||
|
Inspect the current asset and the relevant docs. Claim the TODO item with `[-]`
|
||||||
|
before starting. Work on one asset family per commit.
|
||||||
|
|
||||||
|
### 2. Establish a baseline
|
||||||
|
|
||||||
|
Run the checks that do not need Blender first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/blender_assets.py audit
|
||||||
|
python3 tools/blender_assets.py fit
|
||||||
|
python3 tools/blender_assets.py texels
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
For a specific incoming or existing GLB:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- \
|
||||||
|
inspect --file "$PWD/path/to/asset.glb" --category npc_gear --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Record the before measurements (tris/verts, head/shoulder/hip/leg/arm/foot,
|
||||||
|
fabric texel density, gear clearance). Do not “improve” an asset without a
|
||||||
|
measurable target.
|
||||||
|
|
||||||
|
### 3. Optional Blender-skill routing
|
||||||
|
|
||||||
|
If the local `blender-skills` pack is installed, load `blender-director`, then
|
||||||
|
only the specialists the task needs:
|
||||||
|
|
||||||
|
- character mesh/face/hands: `character-artist`, `retopology`
|
||||||
|
- rig/weights/pose: `rigging`
|
||||||
|
- UVs/texel density: `uv-workflow`
|
||||||
|
- maps/materials: `texture-workflow`, `materials`, `lookdev`
|
||||||
|
- gear/props: `prop-artist`, `hard-surface`
|
||||||
|
- budget/LOD/collision: `asset-optimization`, `lod-pipeline`, `collision-proxy`
|
||||||
|
- final review: `qa-review`
|
||||||
|
|
||||||
|
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/asset-tooling.md#optional-interactive-blender-mcp`.
|
||||||
|
|
||||||
|
### 4. Build or bake
|
||||||
|
|
||||||
|
Characters, gear and library props:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- build --only <name>
|
||||||
|
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/bake_ao.py" -- --only <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
NPC base and its material maps:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/npc_pipeline.py prepare # only when fabric tiles change
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_pipeline.py" -- build
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not overwrite a review image or a promoted texture pick. Use a new `-vN`
|
||||||
|
path and document the promotion.
|
||||||
|
|
||||||
|
### 5. Import and capture
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --import
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
|
||||||
|
# neutral baseline: 8 variants x 6 views, raw frames stay in tmp/
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd \
|
||||||
|
-- --out=res://tmp/npc-baseline
|
||||||
|
|
||||||
|
# final-context: 96 frames across both venues and day/night
|
||||||
|
godot --path . --script res://src/test/capture_npc_review.gd \
|
||||||
|
-- --out=res://tmp/npc-review
|
||||||
|
```
|
||||||
|
|
||||||
|
Compare before/after with `tools/npc_baseline.py compare`; do not replace the
|
||||||
|
committed baseline sheet until a fix actually lands. See
|
||||||
|
`references/verification.md` for the full gate matrix.
|
||||||
|
|
||||||
|
### 6. Verify and hand off
|
||||||
|
|
||||||
|
At minimum:
|
||||||
|
|
||||||
|
- `blender_assets.py audit` and `fit` exit 0.
|
||||||
|
- `npc_proportions.py` passes the stylized guide.
|
||||||
|
- `npc_pipeline.py audit` passes if the base/textures changed.
|
||||||
|
- texel density is inside the authored/fabric bands.
|
||||||
|
- No character exceeds 24,000/24,000; gear/prop caps hold.
|
||||||
|
- 24-NPC budget and Web/Compatibility path remain green.
|
||||||
|
- Contract tests, headless suite, capture gates and docs check pass.
|
||||||
|
- The diff contains only the intended assets, tools, tests and docs.
|
||||||
|
|
||||||
|
State in the handoff: files changed, measurements before/after, skills used,
|
||||||
|
gates run, and any accepted limit.
|
||||||
|
|
||||||
|
## Failure playbook
|
||||||
|
|
||||||
|
- **`Class "..." hides a global script class`** — a stray `.gd` backup is in the
|
||||||
|
project. Delete `tmp/*.gd` backups, `rm -rf .godot`, re-import; never keep
|
||||||
|
`class_name` copies in `tmp/`.
|
||||||
|
- **Blender cannot see the path** — the flatpak cwd is `/app/blender`; use
|
||||||
|
absolute paths (`"$PWD/..."`).
|
||||||
|
- **Empty or stale captures** — close modals/pause, check the capture script's
|
||||||
|
settle frames, and remove stale output directories before re-running.
|
||||||
|
- **Pose harness mismatch** — the current `capture_npc_pose.gd` is blocked per
|
||||||
|
`docs/3d-model-review.md`; do not use its frames as pose evidence until the
|
||||||
|
recipe is fixed.
|
||||||
|
- **Asset test fails after import** — re-run audit/fit first, then import; if the
|
||||||
|
manifest or AO path changed, rebuild the affected asset rather than editing
|
||||||
|
the manifest by hand.
|
||||||
|
- **Uncertain about a visual change** — produce a labelled before/after sheet and
|
||||||
|
ask for review; do not hide a regression behind a budget claim.
|
||||||
|
|
||||||
|
## Bundled references
|
||||||
|
|
||||||
|
- [`references/asset-contract.md`](references/asset-contract.md) — full contract,
|
||||||
|
inventory, file locations and provenance rules.
|
||||||
|
- `docs/asset-tooling.md#optional-interactive-blender-mcp` — optional MCP installation, connection, safety and
|
||||||
|
part-workflow instructions.
|
||||||
|
- [`references/verification.md`](references/verification.md) — command matrix,
|
||||||
|
capture gates, expected results and acceptance criteria.
|
||||||
|
|
||||||
|
## Do not
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- Rewrite committed baselines or overwrite existing images.
|
||||||
|
- Commit `.blend` sources, raw captures or generated AI inputs.
|
||||||
|
- Treat an asset audit as a substitute for visual review at portrait and crowd
|
||||||
|
distance.
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
# Asset Contract Reference
|
||||||
|
|
||||||
|
This is a working summary. The authoritative sources remain
|
||||||
|
`docs/blender-assets.md`, `docs/asset-tooling.md`, ADR-0013, the tests in
|
||||||
|
`src/test/tests/` and the constants in `tools/blender_assets.py` /
|
||||||
|
`tools/npc_pipeline.py`.
|
||||||
|
|
||||||
|
## Library inventory
|
||||||
|
|
||||||
|
- **Character variants (8):** `npc_casual`, `npc_jacket`, `npc_coat`,
|
||||||
|
`npc_vest`, `npc_dress`, `npc_hoodie`, `npc_firefighter`, `npc_guard`.
|
||||||
|
- **Frozen fallback:** `npc_base` / `assets/models/generated/npc_base.glb`.
|
||||||
|
- **Gear:** 18 attach-space pieces covering head, back, chest and hand items.
|
||||||
|
- **Library props:** 20 Blender-authored props plus 6 generated props.
|
||||||
|
- **Venues:** procedural venues in `src/game/world_builder.gd`; Blender venue
|
||||||
|
geometry follows the same budgets and collision/provenance rules.
|
||||||
|
|
||||||
|
## Skeleton and geometry
|
||||||
|
|
||||||
|
The required contract skeleton has 13 core bones:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Head, Spine, Neck,
|
||||||
|
HipL, HipR,
|
||||||
|
KneeL, KneeR,
|
||||||
|
ShoulderL, ShoulderR,
|
||||||
|
ElbowL, ElbowR,
|
||||||
|
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:
|
||||||
|
|
||||||
|
- `npc_base` mesh node exists and is skinned under `Rig`.
|
||||||
|
- Limb node names stay stable for `NpcAvatar` and tests.
|
||||||
|
- Front is Blender −Y / Godot +Z.
|
||||||
|
- Feet are grounded at approximately y = 0; no floating origin.
|
||||||
|
- Units are metres; character height is approximately 1.8 m.
|
||||||
|
- No zero-area/duplicate degenerate faces in the exported mesh.
|
||||||
|
- Every character has UVs in [0,1] unless a planned UDIM/atlas change is
|
||||||
|
explicitly accepted.
|
||||||
|
|
||||||
|
## Material slots
|
||||||
|
|
||||||
|
Exactly these five tint slots:
|
||||||
|
|
||||||
|
```text
|
||||||
|
skin, cloth, pants, accessory, hair
|
||||||
|
```
|
||||||
|
|
||||||
|
- Neutral albedo is authored in Blender; runtime tinting is authoritative.
|
||||||
|
- Slot names are contract values; do not rename or add a sixth slot casually.
|
||||||
|
- The procedural fallback (`NpcAvatar._build_body`) must keep working when the
|
||||||
|
GLB is missing.
|
||||||
|
|
||||||
|
## Budgets
|
||||||
|
|
||||||
|
| Asset | Limit |
|
||||||
|
| --- | --- |
|
||||||
|
| Character (library and base fallback) | 24,000 triangles / 24,000 glTF vertices |
|
||||||
|
| Gear piece | 1,500 triangles |
|
||||||
|
| Prop | 4,000 triangles |
|
||||||
|
| Library prop/venue total | must stay inside the venue tests and library budget |
|
||||||
|
|
||||||
|
`CHAR_MAX_TRIS`, `CHAR_MAX_VERTS`, `GEAR_MAX_TRIS`, `PROP_MAX_TRIS` and related
|
||||||
|
constants live in `tools/blender_assets.py`. The budget is per character, not an
|
||||||
|
average. Measure after every mesh change. Do not lower the cap to make a diff
|
||||||
|
pass.
|
||||||
|
|
||||||
|
## Gear and hand items
|
||||||
|
|
||||||
|
- Gear is authored in the attach-point space documented by `GEAR` /
|
||||||
|
`GEAR_ATTACH` / `ATTACH_BLENDER`.
|
||||||
|
- `blender_assets.py fit` measures gear boxes against the character's head band,
|
||||||
|
clothing radius and back clearance.
|
||||||
|
- Runtime offsets are intentional:
|
||||||
|
- `NpcAvatar.GEAR_OFFSETS = {"lanyard": Vector3(0,0,0.04),
|
||||||
|
"firefighter_helmet": Vector3(0,0.03,0)}`
|
||||||
|
- `NpcAvatar.HAND_ITEM_FORWARD = Vector3(0,0,0.04)`
|
||||||
|
- An offset is **not** an authored-asset change; do not bake it into the GLB
|
||||||
|
without a separate contract decision.
|
||||||
|
- Open-palm grip (no finger-curl rig) is the accepted stylized limit.
|
||||||
|
|
||||||
|
## Textures and AO
|
||||||
|
|
||||||
|
- NPC maps are 256² albedo + normal + packed ORM.
|
||||||
|
- `TEX_SIZE = 256` in `tools/npc_pipeline.py`.
|
||||||
|
- Fabric UV scale is `Materials.NPC_FABRIC_UV_SCALE = Vector3(4,4,1)`.
|
||||||
|
- Cloth/pants skip the per-character baked AO; skin/hair/gear keep it. This
|
||||||
|
avoids the large blotchy magnification that appeared at 256².
|
||||||
|
- Gear/props may have their own AO maps; `tools/bake_ao.py` writes
|
||||||
|
`<name>_ao.png` next to the GLB and Godot wires it into the AO channel.
|
||||||
|
- Never overwrite an existing committed image; use a new `-vN` candidate and
|
||||||
|
promote only after review.
|
||||||
|
- Run `python3 tools/asset_pipeline.py audit <path>` and the relevant
|
||||||
|
`npc_pipeline.py audit` before committing map changes.
|
||||||
|
|
||||||
|
## File map
|
||||||
|
|
||||||
|
| Path | Role |
|
||||||
|
| --- | --- |
|
||||||
|
| `tools/blender_assets.py` | Catalogue, builders, audit/fit/inspect/texels/sheet. |
|
||||||
|
| `tools/npc_pipeline.py` | `npc_base` builder, texture preparation and audit. |
|
||||||
|
| `tools/bake_ao.py` | Blender AO bake into `<name>_ao.png`. |
|
||||||
|
| `tools/npc_proportions.py` | Exported-GLB proportion audit (no Blender). |
|
||||||
|
| `tools/npc_baseline.py` | Contact sheets/comparisons (no Blender). |
|
||||||
|
| `assets/models/blender/manifest.json` | Generated asset manifest. |
|
||||||
|
| `assets/models/blender/**/*.glb` | Committed library assets. |
|
||||||
|
| `assets/models/generated/npc_base.glb` | Frozen NPC fallback. |
|
||||||
|
| `assets/textures/npc/*_{albedo,normal,orm}.png` | Committed NPC maps. |
|
||||||
|
| `src/test/capture_npc_baseline.gd` | 48-frame neutral baseline. |
|
||||||
|
| `src/test/capture_npc_review.gd` | 96-frame final-context review. |
|
||||||
|
|
||||||
|
## Provenance and source rules
|
||||||
|
|
||||||
|
- Procedural Blender assets are project-owned.
|
||||||
|
- External/CC assets need ADR-0015 clearance: CC0 preferred, CC-BY only with
|
||||||
|
attribution in `CREDITS.md`; no service assets with redistribution limits.
|
||||||
|
- Editable `.blend` files under `assets/models/blender_src/` are gitignored;
|
||||||
|
only exported `.glb`, AO maps, manifests and committed preview sheets ship.
|
||||||
|
- AI-generated source images are development inputs, not shipped assets. Keep
|
||||||
|
raw generations under gitignored paths until picked, checked and credited.
|
||||||
|
- Do not copy a downloaded/Street View/AI reference into a committed asset
|
||||||
|
without the licence and provenance review.
|
||||||
|
|
||||||
|
## Verification invariants
|
||||||
|
|
||||||
|
The asset pass is not complete unless:
|
||||||
|
|
||||||
|
1. `python3 tools/blender_assets.py audit` exits 0.
|
||||||
|
2. `python3 tools/blender_assets.py fit` exits 0 and the report is reviewed.
|
||||||
|
3. `python3 tools/npc_proportions.py report` passes the stylized guide.
|
||||||
|
4. `python3 tools/npc_pipeline.py audit` passes when the base/textures changed.
|
||||||
|
5. The 48-frame baseline and 96-frame final-context review are captured, with no
|
||||||
|
blank frames.
|
||||||
|
6. The 24-NPC budget smoke is inside its frame budget.
|
||||||
|
7. The full Godot suite and Python tooling tests pass.
|
||||||
|
8. No runtime contract, renderer or dependency changed except the rebuilt
|
||||||
|
assets and their generated metadata.
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# Verification Matrix
|
||||||
|
|
||||||
|
Run from the repository root. Stop and fix the first failure before continuing.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --version
|
||||||
|
flatpak run org.blender.Blender --version | head -3
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: Godot 4.7.2; Blender 5.2 LTS flatpak; clean or intentionally scoped
|
||||||
|
working tree. Blender's flatpak cwd is `/app/blender`; all paths passed to
|
||||||
|
Blender must be absolute.
|
||||||
|
|
||||||
|
## Asset checks
|
||||||
|
|
||||||
|
| Check | Command | Expected |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Contract audit | `python3 tools/blender_assets.py audit` | exit 0, no `BAD` assets |
|
||||||
|
| Gear fit | `python3 tools/blender_assets.py fit` | exit 0; review head band/clothing/back clearances |
|
||||||
|
| Texture/texel density | `python3 tools/blender_assets.py texels` | authored 40–400 px/m; fabric ≥250 px/m |
|
||||||
|
| NPC base | `python3 tools/npc_pipeline.py audit` | exit 0, contract bones/slots/maps |
|
||||||
|
| Proportions | `python3 tools/npc_proportions.py report` | all eight inside the stylized guide |
|
||||||
|
| Single incoming GLB | `flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- inspect --file "$PWD/path.glb" --category <cat> --json` | contract result and measurements |
|
||||||
|
| Part GLB (local) | `flatpak run org.blender.Blender --background --python "$PWD/tools/npc_parts.py" -- inspect --file "$PWD/tmp/npc-parts/<variant>/<part>.glb"` | measurements; a part GLB is not a shipped asset |
|
||||||
|
| Prop audit | `python3 tools/model_pipeline.py audit` | exit 0 when generated props changed |
|
||||||
|
| Texture audit | `python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready` | exit 0 when textures changed |
|
||||||
|
|
||||||
|
## Build/bake
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# one library asset
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- build --only npc_guard
|
||||||
|
|
||||||
|
# bake AO for the same asset
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/bake_ao.py" -- --only npc_guard
|
||||||
|
|
||||||
|
# NPC base material maps/base
|
||||||
|
python3 tools/npc_pipeline.py prepare
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_pipeline.py" -- build
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Only run `prepare` when AI fabric inputs changed. Do not run a full library
|
||||||
|
rebuild just to hide a small diff; build the affected asset and its AO map.
|
||||||
|
|
||||||
|
## Import and tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --import
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
|
python3 tools/check_docs.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: Godot suite passes; Python tooling suite passes; docs check OK. The
|
||||||
|
project uses its own headless runner, not GUT.
|
||||||
|
|
||||||
|
## Capture gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# neutral studio baseline: 48 frames (8 characters x 6 views)
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd \
|
||||||
|
-- --out=res://tmp/npc-baseline-after
|
||||||
|
|
||||||
|
# final-context: 96 frames (8 models x 3 distances x 2 venues x day/night)
|
||||||
|
godot --path . --script res://src/test/capture_npc_review.gd \
|
||||||
|
-- --out=res://tmp/npc-review-after
|
||||||
|
|
||||||
|
# optional hand close-up: 8 frames
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd \
|
||||||
|
-- --out=res://tmp/npc-hands --view=hand
|
||||||
|
```
|
||||||
|
|
||||||
|
Raw frames stay under `tmp/` and are never committed. Compare with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/npc_baseline.py compare \
|
||||||
|
--before tmp/npc-baseline --after tmp/npc-baseline-after \
|
||||||
|
--out docs/blender_previews/sheet_npc_baseline_compare-<topic>.jpg
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not replace the committed baseline sheet until the change is accepted.
|
||||||
|
|
||||||
|
## Budget/performance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --script res://src/test/smoke_npc_budget.gd
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected report: 24 avatars / 168 meshes / 131,616 triangles (current baseline)
|
||||||
|
inside the 50 ms budget. Record the actual frame time; do not copy an old number
|
||||||
|
without measuring.
|
||||||
|
|
||||||
|
## Visual review criteria
|
||||||
|
|
||||||
|
The part pipeline (`tools/npc_parts.py`) builds/extracts head/face/hand parts under
|
||||||
|
`tmp/npc-parts/<variant>/`; these are local working files and are **not shipped**. Only the
|
||||||
|
assembled character GLBs, AO/maps, the manifest and the review sheets are committed. An
|
||||||
|
assembled GLB still goes through `blender_assets.py inspect`/`audit`/`fit` before any part is
|
||||||
|
accepted. The `npc_base.blend` fallback is frozen: never save prototype detail into it.
|
||||||
|
|
||||||
|
For each changed variant, inspect at least:
|
||||||
|
|
||||||
|
- 2 m portrait: eyes, mouth, ears, neck/shoulder, hair/headgear clearance.
|
||||||
|
- 4.2 m conversation: hands, gear contact, clothing silhouette.
|
||||||
|
- 8 m crowd: readable silhouette and LOD/impostor swap.
|
||||||
|
- Day and night/LATE final-context frames.
|
||||||
|
- Front, three-quarter and profile for a shape change.
|
||||||
|
|
||||||
|
Accepted stylized limits are in `docs/npc-visual-review.md` and
|
||||||
|
`docs/known-limitations.md`. Do not turn an accepted limit into an unreviewed
|
||||||
|
contract change.
|
||||||
|
|
||||||
|
## Final diff review
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
git status --short
|
||||||
|
git diff --stat
|
||||||
|
```
|
||||||
|
|
||||||
|
Check that only intended `.glb`, AO/maps, manifest, tests and docs changed. If a
|
||||||
|
`.blend`, raw capture, generated AI input or temporary sheet is staged, stop and
|
||||||
|
remove it. Commit one coherent asset family per commit.
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
---
|
||||||
|
name: gig-line-release-qa
|
||||||
|
description: Use when preparing, validating, tagging or publishing a Gig Line release or release candidate, or when running the full screenshot, screen-design and realism QA gates. Covers version/changelog discipline, smoke and tooling matrices, clean-worktree builds, checksums, Web/HQ verification, publish rollback and post-release records. Do not use for ordinary asset edits or feature work.
|
||||||
|
compatibility: Gig Line repository checkout with Godot 4.7.2 on PATH and Python 3 + Pillow. Release export needs installed Godot export templates; publishing needs Gitea credentials and is intentionally CI/tag-driven.
|
||||||
|
metadata:
|
||||||
|
project: Gig Line
|
||||||
|
domain: release-qa
|
||||||
|
version: "1.0.0"
|
||||||
|
triggers: release, tag, version bump, changelog, build releases, publish, screenshots, screen design, realism judge, release candidate, deployment
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gig Line Release and Visual QA
|
||||||
|
|
||||||
|
Use this skill for version bumps, release candidates, tags, artifact builds,
|
||||||
|
publishing, and the full visual/QA gate. For a Blender/asset rebuild, use
|
||||||
|
`gig-line-blender-asset-contract` first.
|
||||||
|
|
||||||
|
## Read first
|
||||||
|
|
||||||
|
1. `AGENTS.md` — release procedure and verification rules.
|
||||||
|
2. `TODO.md` — current milestone, blockers and release exit criteria.
|
||||||
|
3. `docs/RELEASING.md` — build/publish commands, artifact list, macOS policy.
|
||||||
|
4. `docs/release-qa.md` — manual QA matrix and balance protocol.
|
||||||
|
5. `docs/development-status.md`, `CHANGELOG.md`, `README.md` — current claims.
|
||||||
|
6. `MEMORY.md` — release gotchas, especially the clean-worktree rule.
|
||||||
|
|
||||||
|
A release is not just a tag. It includes a version bump, changelog, clean build,
|
||||||
|
artifact/checksum verification, CI publication, and post-release records.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
- **Do not tag while the TODO still lists unresolved release blockers.** Check
|
||||||
|
external playtest, legal/AI provenance, signing and target-hardware items.
|
||||||
|
- **Never reuse a tag.** If a published version needs a fix, bump the patch and
|
||||||
|
create a new tag.
|
||||||
|
- **Build from a clean checkout or detached worktree.** A normal working tree can
|
||||||
|
pack ignored `tmp/` and `build/` content into exports, inflating a ~160 MB
|
||||||
|
package to 1.6–3.1 GB.
|
||||||
|
- A tag push triggers CI checks/build/publish. Do not upload manually unless CI is
|
||||||
|
unavailable and you have followed the same preflight.
|
||||||
|
- Do not commit generated realism runs, raw captures, API keys, or build artifacts.
|
||||||
|
- The vision judge is an optional development aid with external API cost, never a
|
||||||
|
hard CI gate.
|
||||||
|
- Keep `project.godot`, changelog, tag and release asset version equal.
|
||||||
|
|
||||||
|
## Release workflow
|
||||||
|
|
||||||
|
### 1. Scope and version
|
||||||
|
|
||||||
|
Choose the semantic increment from `AGENTS.md`:
|
||||||
|
|
||||||
|
- MAJOR: incompatible/removed behavior, save/config contract or core-pillar
|
||||||
|
replacement.
|
||||||
|
- MINOR: new user-facing feature/venue.
|
||||||
|
- PATCH: fixes and polish.
|
||||||
|
|
||||||
|
Update:
|
||||||
|
|
||||||
|
1. `project.godot` `config/version`.
|
||||||
|
2. `CHANGELOG.md` with a dated entry.
|
||||||
|
3. `README.md`, `PLAN.md`, `docs/architecture.md`,
|
||||||
|
`docs/development-status.md`, `TODO.md`/`DONE.md` and `MEMORY.md` as needed.
|
||||||
|
|
||||||
|
For a release candidate, use a prerelease version only if the project policy
|
||||||
|
allows it and never present it as the final release.
|
||||||
|
|
||||||
|
### 2. Preflight
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git log --oneline -5
|
||||||
|
git tag -l "v<version>"
|
||||||
|
rg -n 'config/version' project.godot
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: only intended release changes; tag does not exist; version matches the
|
||||||
|
changelog and planned artifacts.
|
||||||
|
|
||||||
|
### 3. Automated checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
|
python3 tools/check_docs.py
|
||||||
|
python3 tools/blender_assets.py audit
|
||||||
|
python3 tools/blender_assets.py fit
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the smoke matrix, using isolated user-data directories for
|
||||||
|
tutorial/campaign:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for s in smoke_audio smoke_crowd smoke_emergency smoke_npc_budget \
|
||||||
|
smoke_plan_verbs smoke_scenario_content smoke_session_shell \
|
||||||
|
smoke_timeline smoke_ui_flows; do
|
||||||
|
godot --headless --path . --script res://src/test/$s.gd || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-campaign godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_campaign.gd
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-tutorial godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_tutorial.gd -- --tutorial
|
||||||
|
|
||||||
|
godot --path . --script res://src/test/smoke_movement.gd
|
||||||
|
godot --path . --script res://src/test/smoke_props.gd
|
||||||
|
godot --path . --script res://src/test/smoke_focus_radial.gd
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a fresh `$XDG_DATA_HOME` for campaign/tutorial and check the exit code, not
|
||||||
|
just the log.
|
||||||
|
|
||||||
|
### 4. Visual and judge gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tools/check_screenshots.sh
|
||||||
|
python3 tools/screen_designs.py compare
|
||||||
|
python3 tools/screen_designs.py judge --model auto # optional, record model/verdicts
|
||||||
|
```
|
||||||
|
|
||||||
|
For realism/venue work:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd \
|
||||||
|
-- --out=res://docs/screens/realism/<run>/after
|
||||||
|
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> \
|
||||||
|
--judge --model <pinned-model>
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- Gallery: 21 good / 0 bad; screen judge has no unexplained `diff`.
|
||||||
|
- Realism runs stay under `docs/screens/realism/` (gitignored); only decisions and
|
||||||
|
committed sheets/docs are tracked.
|
||||||
|
- Record judge model, run count/attempts, spend if exposed, and verdicts.
|
||||||
|
- Review every `review` verdict; turn layout regressions into fixes or documented
|
||||||
|
known limitations.
|
||||||
|
|
||||||
|
### 5. Clean build
|
||||||
|
|
||||||
|
Commit the release preparation first, then build the committed tree in a clean
|
||||||
|
worktree:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree add --detach /tmp/gig-line-release HEAD
|
||||||
|
cd /tmp/gig-line-release
|
||||||
|
./tools/build_releases.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify artifact names/sizes and checksums:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -lh build/gig-line-*<version>*
|
||||||
|
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh <version>
|
||||||
|
sha256sum -c build/SHA256SUMS-<version>.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Check package contents:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unzip -l build/gig-line-web-<version>.zip | head
|
||||||
|
unzip -l build/gig-line-desktop-hq-<version>.zip | head
|
||||||
|
unzip -p build/gig-line-desktop-hq-<version>.zip override.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the runnable Linux and HQ packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
unzip -q build/gig-line-desktop-hq-<version>.zip -d /tmp/gig-line-hq
|
||||||
|
/tmp/gig-line-hq/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Check Web static serving with a local HTTP server and `curl` for `index.html`,
|
||||||
|
`index.wasm`, `index.pck` and the player docs. Windows/macOS/AppImage/installer
|
||||||
|
checks require the target matrix; if unavailable, record that explicitly.
|
||||||
|
|
||||||
|
Remove the worktree after verification:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree remove --force /tmp/gig-line-release
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Tag and publish
|
||||||
|
|
||||||
|
Only after every gate is green:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "Release <version> <topic>"
|
||||||
|
git tag -a "v<version>" -m "Gig Line <version>"
|
||||||
|
git push origin main "v<version>"
|
||||||
|
```
|
||||||
|
|
||||||
|
CI builds and publishes on the tag. Verify:
|
||||||
|
|
||||||
|
- Tag commit equals the release commit.
|
||||||
|
- CI checks/build job succeeded.
|
||||||
|
- Release page has the expected 7 assets (Linux, Windows, macOS, NSIS setup,
|
||||||
|
AppImage, Web, Desktop HQ).
|
||||||
|
- Asset names and download links point at the released version.
|
||||||
|
|
||||||
|
`tools/publish_release.sh` preflights artifact size/free space, writes SHA-256
|
||||||
|
checksums, rolls back newly uploaded assets/release on failure, and supports
|
||||||
|
opt-in retention:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RETAIN_RELEASES=7 ./tools/publish_release.sh <version>
|
||||||
|
```
|
||||||
|
|
||||||
|
The default `RETAIN_RELEASES=0` never prunes.
|
||||||
|
|
||||||
|
### 7. Post-release record
|
||||||
|
|
||||||
|
Update `README.md`, `docs/development-status.md`, `MEMORY.md`, `CHANGELOG.md`,
|
||||||
|
`TODO.md`/`DONE.md` and the release page with the built/CI result. Commit the
|
||||||
|
release record and push. Record any skipped check and the reason; never leave a
|
||||||
|
tag with unreported missing verification.
|
||||||
|
|
||||||
|
## Known gotchas
|
||||||
|
|
||||||
|
- **Dirty build inflation:** always use the clean worktree above.
|
||||||
|
- **`Class ... hides a global script class`:** remove stray `.gd` backups in
|
||||||
|
`tmp/`, delete `.godot`, re-import.
|
||||||
|
- **Screenshot staging:** `capture_ui_states.gd` needs its settle frames and
|
||||||
|
staged state; do not capture a paused frame or a stale modal.
|
||||||
|
- **Judge keys:** OpenRouter calls need `.env`/`OPENROUTER_API_KEY`; never commit
|
||||||
|
the key or generated review folders.
|
||||||
|
- **Tag builds publish automatically:** do not run a second manual publish that
|
||||||
|
duplicates assets; use the existing release if re-running.
|
||||||
|
- **macOS is unsigned unless policy changes:** record quarantine instructions and
|
||||||
|
do not claim notarization.
|
||||||
|
- **Release blockers are real:** external playtest, legal/storefront approval,
|
||||||
|
AI-provider rights and target-hardware checks cannot be replaced by green unit
|
||||||
|
tests.
|
||||||
|
|
||||||
|
## Bundled references
|
||||||
|
|
||||||
|
- [`references/release-runbook.md`](references/release-runbook.md) — version,
|
||||||
|
build, tag, CI, publish and rollback sequence.
|
||||||
|
- [`references/qa-gates.md`](references/qa-gates.md) — full command matrix,
|
||||||
|
expected results and evidence to record.
|
||||||
|
|
||||||
|
## Completion checklist
|
||||||
|
|
||||||
|
- [ ] Version/changelog/docs/tag match.
|
||||||
|
- [ ] No unresolved release blockers for a final tag.
|
||||||
|
- [ ] Godot, Python, smokes, asset and docs gates pass.
|
||||||
|
- [ ] Gallery and optional judges reviewed with evidence.
|
||||||
|
- [ ] Clean worktree build and checksums verified.
|
||||||
|
- [ ] Linux/HQ/Web checks recorded; unavailable platforms noted.
|
||||||
|
- [ ] Tag pushed; CI/publish succeeded; seven assets verified.
|
||||||
|
- [ ] Release record and durable docs committed.
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
# QA Gate Matrix
|
||||||
|
|
||||||
|
Run from the repository root. Capture the exit code and the useful report line,
|
||||||
|
not only the final “OK”.
|
||||||
|
|
||||||
|
## 1. Source and docs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git log --oneline -5
|
||||||
|
python3 tools/check_docs.py
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: intended files only; `check_docs: OK`; no whitespace errors.
|
||||||
|
|
||||||
|
## 2. Automated suites
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: the current Godot suite passes (historically 272+ tests) and the
|
||||||
|
Python tooling suite passes (currently 160 tests). Do not edit expected counts
|
||||||
|
without inspecting the suite.
|
||||||
|
|
||||||
|
## 3. Asset gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/blender_assets.py audit
|
||||||
|
python3 tools/blender_assets.py fit
|
||||||
|
python3 tools/blender_assets.py texels
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: exit 0, no `BAD` assets, all eight characters inside the stylized
|
||||||
|
proportion guide, no budget/contract regression.
|
||||||
|
|
||||||
|
Optional texture/model audits when those assets changed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready
|
||||||
|
python3 tools/model_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Smoke matrix
|
||||||
|
|
||||||
|
Headless:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for s in smoke_audio smoke_crowd smoke_emergency smoke_npc_budget \
|
||||||
|
smoke_plan_verbs smoke_scenario_content smoke_session_shell \
|
||||||
|
smoke_timeline smoke_ui_flows; do
|
||||||
|
echo "== $s"
|
||||||
|
godot --headless --path . --script res://src/test/$s.gd || exit 1
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Isolated data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf /tmp/gig-line-release-campaign /tmp/gig-line-release-tutorial
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-release-campaign godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_campaign.gd
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-release-tutorial godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_tutorial.gd -- --tutorial
|
||||||
|
```
|
||||||
|
|
||||||
|
Windowed input/UI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/smoke_movement.gd
|
||||||
|
godot --path . --script res://src/test/smoke_props.gd
|
||||||
|
godot --path . --script res://src/test/smoke_focus_radial.gd
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 14 smokes green. `smoke_npc_budget` should print the current mesh,
|
||||||
|
triangle and frame-time evidence; the 50 ms budget is a safety ceiling.
|
||||||
|
|
||||||
|
## 5. Screenshot and design gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tools/check_screenshots.sh
|
||||||
|
python3 tools/screen_designs.py compare
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 21 good / 0 bad; no unexplained compare output. The judge is optional:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/screen_designs.py judge --model auto
|
||||||
|
```
|
||||||
|
|
||||||
|
Record model, confidence/verdict summary, `diff` count and any manual review.
|
||||||
|
`docs/ui-copy.md#screen-design-judge-report` is the durable record.
|
||||||
|
|
||||||
|
## 6. Realism judge (when visual assets/lighting changed)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd \
|
||||||
|
-- --out=res://docs/screens/realism/<run>/after
|
||||||
|
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> \
|
||||||
|
--judge --model <pinned-model>
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: a complete set of required stage captures, no stale pauses/modals, and
|
||||||
|
a report under the gitignored run directory. Record:
|
||||||
|
|
||||||
|
- pinned model/fallbacks
|
||||||
|
- shots attempted/completed
|
||||||
|
- spend if the tool exposes it
|
||||||
|
- mean score and Pillow gap
|
||||||
|
- every `review` verdict and disposition
|
||||||
|
|
||||||
|
Do not commit the run. Commit only the decision/report summary and any durable
|
||||||
|
comparison sheet explicitly intended for review.
|
||||||
|
|
||||||
|
## 7. Package checks
|
||||||
|
|
||||||
|
Use the clean worktree from `release-runbook.md`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh <version>
|
||||||
|
sha256sum -c build/SHA256SUMS-<version>.txt
|
||||||
|
unzip -l build/gig-line-web-<version>.zip
|
||||||
|
unzip -l build/gig-line-desktop-hq-<version>.zip
|
||||||
|
unzip -p build/gig-line-desktop-hq-<version>.zip override.cfg
|
||||||
|
./build/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
- names match the version;
|
||||||
|
- Linux/Windows/macOS/Web/HQ present locally;
|
||||||
|
- Web contains HTML/wasm/pck and docs;
|
||||||
|
- HQ contains `override.cfg`, executables and docs;
|
||||||
|
- Linux/HQ start without a script error.
|
||||||
|
|
||||||
|
Windows installer/AppImage and macOS launch require CI or the target matrix. If
|
||||||
|
not available, mark them as unverified rather than passing them by analogy.
|
||||||
|
|
||||||
|
## 8. Release verification
|
||||||
|
|
||||||
|
After the tag push:
|
||||||
|
|
||||||
|
- CI checks job succeeded;
|
||||||
|
- CI build job succeeded;
|
||||||
|
- release page has seven assets;
|
||||||
|
- each artifact name includes the released version;
|
||||||
|
- the tag points at the release commit;
|
||||||
|
- README/changelog/status and release notes agree.
|
||||||
|
|
||||||
|
## Evidence summary template
|
||||||
|
|
||||||
|
```text
|
||||||
|
Release: vX.Y.Z (commit)
|
||||||
|
Godot: pass (N/N)
|
||||||
|
Python tooling: pass (N/N)
|
||||||
|
Smokes: 14/14
|
||||||
|
Assets: audit/fit/proportions/npc audit pass
|
||||||
|
Budget: 24 NPCs, N meshes, N triangles, N ms
|
||||||
|
Gallery: 21 good / 0 bad
|
||||||
|
Screen judge: N/N match, N review, N diff
|
||||||
|
Realism: model, attempts, mean, gap, review dispositions
|
||||||
|
Build: clean worktree, artifact sizes/checksums
|
||||||
|
Launch: Linux/HQ/Web checks; unverified platforms
|
||||||
|
CI/release: run id, seven assets
|
||||||
|
Skipped checks + reason:
|
||||||
|
Known limitations:
|
||||||
|
```
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
# Release Runbook
|
||||||
|
|
||||||
|
Authoritative source: `AGENTS.md` (Release Procedure), `docs/RELEASING.md`.
|
||||||
|
This runbook is the operational checklist; if it conflicts with those, they win.
|
||||||
|
|
||||||
|
## 0. Decide the release
|
||||||
|
|
||||||
|
Determine whether this is a major, minor, patch, or release-candidate checkpoint.
|
||||||
|
Do not assign a version to an unversioned development track until its scope is
|
||||||
|
accepted into a release.
|
||||||
|
|
||||||
|
Check the current release constraints in `TODO.md`:
|
||||||
|
|
||||||
|
- external playtest evidence
|
||||||
|
- legal/AI-provider/storefront review
|
||||||
|
- macOS signing/notarization policy
|
||||||
|
- target hardware matrix
|
||||||
|
- any manual QA gaps
|
||||||
|
|
||||||
|
If a final release would ship with unresolved blockers, prepare an RC or stop;
|
||||||
|
do not create a final tag.
|
||||||
|
|
||||||
|
## 1. Prepare the version
|
||||||
|
|
||||||
|
Files to update:
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
| --- | --- |
|
||||||
|
| `project.godot` | `config/version="X.Y.Z"` |
|
||||||
|
| `CHANGELOG.md` | dated `## [X.Y.Z] — YYYY-MM-DD` entry |
|
||||||
|
| `README.md` | latest/current version and summary |
|
||||||
|
| `PLAN.md` / `docs/architecture.md` | current version reference |
|
||||||
|
| `docs/development-status.md` | shipped capability/release result |
|
||||||
|
| `TODO.md` / `DONE.md` | close/move milestone when complete |
|
||||||
|
| `MEMORY.md` | release state and gotchas |
|
||||||
|
|
||||||
|
Then commit the preparation before building.
|
||||||
|
|
||||||
|
## 2. Run gates
|
||||||
|
|
||||||
|
Use `qa-gates.md` as the command matrix. The minimum release gate is:
|
||||||
|
|
||||||
|
- Godot suite
|
||||||
|
- Python tooling tests
|
||||||
|
- documentation gate
|
||||||
|
- asset audits/fit/proportions
|
||||||
|
- 24-NPC budget
|
||||||
|
- 14 smoke scripts
|
||||||
|
- screenshot compare
|
||||||
|
- optional screen/realism judge with recorded model/attempts
|
||||||
|
- clean build and artifact checks
|
||||||
|
|
||||||
|
Any skipped check must be written into `MEMORY.md` or `TODO.md` with a reason.
|
||||||
|
|
||||||
|
## 3. Build in a clean worktree
|
||||||
|
|
||||||
|
Why: Godot can pack ignored `tmp/` captures and `build/` outputs into the export.
|
||||||
|
A dirty build can produce 1.6–3.1 GB artifacts instead of ~160–240 MB.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree add --detach /tmp/gig-line-release HEAD
|
||||||
|
cd /tmp/gig-line-release
|
||||||
|
./tools/build_releases.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected local artifacts:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build/gig-line-linux-<version>.x86_64
|
||||||
|
build/gig-line-windows-<version>.exe
|
||||||
|
build/gig-line-macos-<version>.zip
|
||||||
|
build/gig-line-web-<version>.zip
|
||||||
|
build/gig-line-desktop-hq-<version>.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
CI additionally produces the NSIS setup and AppImage, for seven release assets.
|
||||||
|
|
||||||
|
## 4. Preflight and checksum
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh <version>
|
||||||
|
sha256sum -c build/SHA256SUMS-<version>.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
`publish_release.sh` checks non-empty artifacts and local free space before any
|
||||||
|
API call. It writes checksums under `build/` and does not upload them by default.
|
||||||
|
|
||||||
|
Inspect packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unzip -l build/gig-line-web-<version>.zip | head -20
|
||||||
|
unzip -l build/gig-line-desktop-hq-<version>.zip | head -20
|
||||||
|
unzip -p build/gig-line-desktop-hq-<version>.zip override.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
Web must contain `index.html`, `index.wasm`, `index.pck`, and player docs.
|
||||||
|
Desktop HQ must contain the Linux/Windows executables, `override.cfg`,
|
||||||
|
`README.txt`, `CREDITS.md`, `PRIVACY.md`, `accessibility.md` and
|
||||||
|
`known-limitations.md`.
|
||||||
|
|
||||||
|
## 5. Runtime launch smoke
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
unzip -q build/gig-line-desktop-hq-<version>.zip -d /tmp/gig-line-hq
|
||||||
|
/tmp/gig-line-hq/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
```
|
||||||
|
|
||||||
|
For Web, serve `build/web` and check HTTP 200 for `index.html`, `index.wasm`,
|
||||||
|
`index.pck` and docs. Do not claim browser interaction from a curl check.
|
||||||
|
|
||||||
|
Remove the temporary worktree after checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree remove --force /tmp/gig-line-release
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Tag and CI
|
||||||
|
|
||||||
|
From the real repository, on the release commit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -a "v<version>" -m "Gig Line <version>"
|
||||||
|
git push origin main "v<version>"
|
||||||
|
```
|
||||||
|
|
||||||
|
The tag triggers `.gitea/workflows/build.yml`: checks, export, installer,
|
||||||
|
AppImage, release publish. Verify the run and assets:
|
||||||
|
|
||||||
|
- check job success
|
||||||
|
- build job success
|
||||||
|
- seven assets present
|
||||||
|
- tag/version/artifact names equal
|
||||||
|
- download links respond
|
||||||
|
|
||||||
|
Do not reuse a published tag. If a fix is needed, bump patch and repeat.
|
||||||
|
|
||||||
|
## 7. Manual publish fallback
|
||||||
|
|
||||||
|
Only if CI cannot publish:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RETAIN_RELEASES=7 ./tools/publish_release.sh <version>
|
||||||
|
```
|
||||||
|
|
||||||
|
The script reuses or creates the release, uploads present artifacts, verifies the
|
||||||
|
asset count, and rolls back newly uploaded assets/new release on failure. The
|
||||||
|
default retention is `0` (no deletion). Never delete older releases implicitly.
|
||||||
|
|
||||||
|
## 8. Post-release record
|
||||||
|
|
||||||
|
Commit a release record that includes:
|
||||||
|
|
||||||
|
- tag and commit
|
||||||
|
- CI run/result
|
||||||
|
- seven assets
|
||||||
|
- checksums or size evidence
|
||||||
|
- skipped checks
|
||||||
|
- known limitations
|
||||||
|
- release URL
|
||||||
|
|
||||||
|
Update `DONE.md`/`TODO.md` and `MEMORY.md`; keep the changelog and README
|
||||||
|
consistent. Push the record commit.
|
||||||
|
|
||||||
|
## Versioning pitfalls
|
||||||
|
|
||||||
|
- A `vX.Y.Z` tag is immutable in practice; do not move/replace it.
|
||||||
|
- A release candidate is not a final release. Do not label it as such in the
|
||||||
|
README or release page if blockers remain.
|
||||||
|
- `config/version` must match the tag and artifact filenames.
|
||||||
|
- The clean-worktree build is a release requirement, not an optimization.
|
||||||
@@ -58,6 +58,20 @@ assets/models/generated/npc_base_*_normal.png
|
|||||||
|
|
||||||
# Local AppImage extraction
|
# Local AppImage extraction
|
||||||
squashfs-root/
|
squashfs-root/
|
||||||
|
|
||||||
|
# Local agent skills. The third-party Blender pack is installed per machine and
|
||||||
|
# stays ignored; the two project-authored skills are tracked so the workflow can
|
||||||
|
# be shared and reviewed.
|
||||||
|
.agents/
|
||||||
|
!.agents/
|
||||||
|
.agents/*
|
||||||
|
!.agents/.gdignore
|
||||||
|
!.agents/skills/
|
||||||
|
.agents/skills/*
|
||||||
|
!.agents/skills/gig-line-blender-asset-contract/
|
||||||
|
!.agents/skills/gig-line-blender-asset-contract/**
|
||||||
|
!.agents/skills/gig-line-release-qa/
|
||||||
|
!.agents/skills/gig-line-release-qa/**
|
||||||
# Blender asset library: per-asset Cycles renders are local review output (the
|
# Blender asset library: per-asset Cycles renders are local review output (the
|
||||||
# committed review artefacts are docs/blender_previews/sheet_*.jpg), and Godot
|
# committed review artefacts are docs/blender_previews/sheet_*.jpg), and Godot
|
||||||
# extracts the GLB-embedded textures next to the models on import.
|
# extracts the GLB-embedded textures next to the models on import.
|
||||||
@@ -74,3 +88,7 @@ assets/models/blender_src/*.blend
|
|||||||
assets/textures/ai/game_ready/v2/*
|
assets/textures/ai/game_ready/v2/*
|
||||||
!assets/textures/ai/game_ready/v2/picks/
|
!assets/textures/ai/game_ready/v2/picks/
|
||||||
docs/screens/realism/
|
docs/screens/realism/
|
||||||
|
|
||||||
|
## Local pi agent session data
|
||||||
|
pi-session-*.html
|
||||||
|
pi-session-*.jsonl
|
||||||
@@ -17,18 +17,20 @@ The project should be developed in small, reviewable steps. Agents must keep pla
|
|||||||
- `CHANGELOG.md` — The version history of user-facing changes. Keep an entry for each patch, minor, and major version.
|
- `CHANGELOG.md` — The version history of user-facing changes. Keep an entry for each patch, minor, and major version.
|
||||||
- `docs/development-status.md` — The implemented-features list and what is not yet implemented. Update it whenever a feature lands, is renamed, or is removed.
|
- `docs/development-status.md` — The implemented-features list and what is not yet implemented. Update it whenever a feature lands, is renamed, or is removed.
|
||||||
- `docs/architecture.md` — The arc42-style architecture documentation (goals, constraints, building blocks, runtime views, decisions, risks).
|
- `docs/architecture.md` — The arc42-style architecture documentation (goals, constraints, building blocks, runtime views, decisions, risks).
|
||||||
- `docs/gameplay.md` — Gameplay systems, venues, and the escalation model.
|
- `docs/gameplay.md` — Gameplay systems, tutorial, venues and the escalation model.
|
||||||
- `docs/asset-tooling.md` — Asset pipelines, model tools, and screenshot gates.
|
- `docs/venues.md` — Real-site evidence, game mapping and venue simplifications.
|
||||||
|
- `docs/asset-tooling.md` — Asset pipelines, model tools, optional Blender MCP and screenshot gates.
|
||||||
|
- `docs/blender-assets.md` — Blender library operation and asset roles.
|
||||||
- `docs/npc-model-sourcing.md` — Character-source evaluation, licence rules and the character-development plan.
|
- `docs/npc-model-sourcing.md` — Character-source evaluation, licence rules and the character-development plan.
|
||||||
- `docs/getting-started.md` — Setup, run/test commands, and the controls table.
|
- `docs/getting-started.md` — Setup, run/test commands, and the controls table.
|
||||||
- `docs/smoke-checklist.md` — The manual smoke-test checklist.
|
- `docs/release-qa.md` — The manual smoke checklist and balance-acceptance protocol.
|
||||||
- `docs/adr/` — Architecture Decision Records.
|
- `docs/adr/` — Architecture Decision Records.
|
||||||
|
|
||||||
## Priority and Versioning
|
## Priority and Versioning
|
||||||
|
|
||||||
`TODO.md` is the priority source. The active order is:
|
`TODO.md` is the priority source. The active order is:
|
||||||
|
|
||||||
1. unversioned development tracks (currently character development and local map development);
|
1. unversioned development tracks;
|
||||||
2. versioned release milestones, in version order;
|
2. versioned release milestones, in version order;
|
||||||
3. future/unversioned candidates and deferred work.
|
3. future/unversioned candidates and deferred work.
|
||||||
|
|
||||||
@@ -36,6 +38,83 @@ Do not assign a version number to an unversioned track until its scope is accept
|
|||||||
release. When a version is assigned, it must be the next semantic version that matches the
|
release. When a version is assigned, it must be the next semantic version that matches the
|
||||||
scope (major/minor/patch), not a placeholder.
|
scope (major/minor/patch), not a placeholder.
|
||||||
|
|
||||||
|
## Local Blender skills (development aid)
|
||||||
|
|
||||||
|
The MIT `blender-skills` pack is installed locally at `.agents/skills/blender-skills/`
|
||||||
|
(94 skills plus a shared `references/` folder). It is **gitignored on purpose**: it is not
|
||||||
|
game content, not part of the build, tests, CI or release packages, and every contributor
|
||||||
|
may or may not have it. The agent runtime (Pi) gates project `.agents/skills` behind project
|
||||||
|
trust, so the trust prompt must be accepted before the skills load; `/skill:blender-director`
|
||||||
|
forces one.
|
||||||
|
|
||||||
|
Use them when a task touches `tools/blender_assets.py`, `tools/npc_pipeline.py`,
|
||||||
|
`tools/bake_ao.py`, `assets/models/blender/**`, or Blender-authored venue/prop geometry.
|
||||||
|
|
||||||
|
### Project-authored skills (tracked)
|
||||||
|
|
||||||
|
Two project skills are committed under `.agents/skills/` and are guidance for
|
||||||
|
any agent, not only Pi:
|
||||||
|
|
||||||
|
- `gig-line-blender-asset-contract` — the NPC/gear/prop contract, build order,
|
||||||
|
headless Blender commands, failure playbook and verification gates.
|
||||||
|
- `gig-line-release-qa` — the test/smoke matrix, visual judges, clean-worktree
|
||||||
|
build, checksum/tag/publish sequence and release records.
|
||||||
|
|
||||||
|
They are not runtime dependencies and do not move tool implementations into
|
||||||
|
`.agents/`. The third-party `blender-skills` pack stays local/gitignored.
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
|
||||||
|
Start with `blender-director` for anything multi-discipline, then load only the
|
||||||
|
specialists the task needs:
|
||||||
|
|
||||||
|
| Work | Skills |
|
||||||
|
| --- | --- |
|
||||||
|
| Plan/route a Blender task, reference photo match | `blender-director` |
|
||||||
|
| Character mesh, face, hands, proportions | `character-artist`, `character-archetypes`, `retopology` |
|
||||||
|
| Skeleton, weights, pose/contract rigging | `rigging` |
|
||||||
|
| UV layout, texel density, UDIM | `uv-workflow` |
|
||||||
|
| Bakery, atlas, normal/AO/ORM maps | `texture-workflow` |
|
||||||
|
| PBR materials and shading | `materials` |
|
||||||
|
| Screenshot-driven material/light iteration | `lookdev` |
|
||||||
|
| Triangle/vertex budgets, naming, scene hygiene | `asset-optimization` |
|
||||||
|
| LOD tiers and impostors | `lod-pipeline` |
|
||||||
|
| Collision shapes and proxies | `collision-proxy` |
|
||||||
|
| GLB export for Godot | `godot-export`, `export-pipeline` |
|
||||||
|
| Gear, props, street furniture | `prop-artist`, `hard-surface` |
|
||||||
|
| Trees, hedges, foliage cards | `vegetation-artist` |
|
||||||
|
| Facades, venues, modular kits, dressing | `environment-artist`, `archviz`, `set-dressing`, `geometry-nodes` |
|
||||||
|
| Lighting, framing, final render/composite | `lighting`, `camera-cinematography`, `rendering`, `compositing` |
|
||||||
|
| Style direction (stylized / low-poly / realistic) | `stylized-style`, `lowpoly-style`, `realistic-style` |
|
||||||
|
| Ship/no-ship review | `qa-review` |
|
||||||
|
|
||||||
|
### Project rules that override a skill
|
||||||
|
|
||||||
|
1. **The project contract wins.** `npc_base` mesh node, five tint slots, front on Blender −Y /
|
||||||
|
Godot +Z, grounded feet, 24,000 triangles / 24,000 glTF vertices per character, 1,500 per
|
||||||
|
gear, 4,000 per prop. The shipped procedural library uses the 13 required core bones at their
|
||||||
|
rest positions; optional extra bones (fingers, face, toes) are allowed if they do not move the
|
||||||
|
core rest positions. The one documented exception is the accepted Quaternius runtime path
|
||||||
|
([ADR-0018](docs/adr/0018-quaternius-stage2-runtime-rig.md)): it keeps the 62-joint source rig
|
||||||
|
at runtime while the 0.9.9 face/silhouette gate is open, and it must still supply the five tint
|
||||||
|
slots, valid gear attach points and the same budgets. Both paths stay tested until that gate
|
||||||
|
closes. A skill 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.
|
||||||
|
2. **Blender MCP is optional interactive tooling.** The headless flatpak scripts
|
||||||
|
(`tools/blender_assets.py`, `tools/npc_pipeline.py`) remain authoritative. MCP may be used
|
||||||
|
for local part exploration; setup and safety are in [`docs/asset-tooling.md#optional-interactive-blender-mcp`](docs/asset-tooling.md#optional-interactive-blender-mcp).
|
||||||
|
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
|
||||||
|
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
|
||||||
|
generated meshes; ADR-0017 (the CC0 Quaternius NPC base) supersedes the generator-first
|
||||||
|
decision in ADR-0015, and the CC0/CC-BY rule in ADR-0015 (attribution in `CREDITS.md`) plus
|
||||||
|
the provenance rules in `docs/npc-model-sourcing.md` still apply to any new source.
|
||||||
|
5. **Evidence, not vibes.** After a skill-informed change, run `blender_assets.py inspect`,
|
||||||
|
`audit` and `fit` plus the relevant capture/smoke gates, and say in the handoff which
|
||||||
|
skills informed the change and what the measurements were.
|
||||||
|
|
||||||
## Required Workflow
|
## Required Workflow
|
||||||
|
|
||||||
### 1. Start by Inspecting the Repository
|
### 1. Start by Inspecting the Repository
|
||||||
@@ -63,8 +142,8 @@ All planned implementation work must be represented in `TODO.md`.
|
|||||||
- Order level-2 sections by current priority: unversioned development tracks first, then
|
- Order level-2 sections by current priority: unversioned development tracks first, then
|
||||||
versioned release milestones in version order, then future/deferred work. Do not assign a
|
versioned release milestones in version order, then future/deferred work. Do not assign a
|
||||||
version number to an unversioned track until it is scheduled for a release.
|
version number to an unversioned track until it is scheduled for a release.
|
||||||
- Use level-3 headings (`###`) for workstreams inside a milestone (for example, `0.8.1
|
- Use level-3 headings (`###`) for workstreams inside a milestone. Keep the hierarchy to three
|
||||||
First-shift tutorial`). Keep the hierarchy to three levels: milestone → workstream → checkbox.
|
levels: milestone → workstream → checkbox.
|
||||||
- Use checkbox items for each concrete task, normally under a level-3 workstream. Direct
|
- Use checkbox items for each concrete task, normally under a level-3 workstream. Direct
|
||||||
checkboxes under a level-2 section are acceptable for small, single-workstream sections.
|
checkboxes under a level-2 section are acceptable for small, single-workstream sections.
|
||||||
- Keep tasks small enough to complete and review independently.
|
- Keep tasks small enough to complete and review independently.
|
||||||
@@ -83,29 +162,30 @@ Recommended format:
|
|||||||
```md
|
```md
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
## Milestone 0.8.0 — First hour and content depth
|
## Milestone X.Y.Z — goal and exit criterion
|
||||||
|
|
||||||
### 0.8.1 First-shift tutorial
|
### Workstream A
|
||||||
|
|
||||||
- [x] Write the tutorial beat sheet.
|
- [x] Completed task.
|
||||||
- [-] Implement the tutorial state machine. (claimed; implementation/verification in progress)
|
- [-] Task in progress. (claimed; implementation/verification in progress)
|
||||||
- [ ] Add the tutorial smoke test.
|
- [ ] Open task.
|
||||||
|
|
||||||
### 0.8.2 Campaign shell
|
### Workstream B
|
||||||
|
|
||||||
- [ ] Define campaign data.
|
- [ ] Another task.
|
||||||
- [ ] Persist campaign progress.
|
|
||||||
|
|
||||||
**Exit:** a first-time player completes a shift without external documentation.
|
**Exit:** a verifiable statement of what "done" means for the milestone.
|
||||||
|
|
||||||
## Documentation
|
## Theme
|
||||||
|
|
||||||
- [ ] Document the test procedure in `README.md`.
|
- [ ] A small housekeeping task with no milestone.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Recommended `DONE.md` format: a `# DONE` heading, followed by each moved level-2 section
|
||||||
|
unchanged, with a completion date line under each section heading.
|
||||||
|
|
||||||
Order the actual file as described in "Priority and Versioning": unversioned development tracks
|
Order the actual file as described in "Priority and Versioning": unversioned development tracks
|
||||||
first (for example character and local-map development), then versioned milestones in version
|
first, then versioned milestones in version order, then future/deferred work.
|
||||||
order, then future/deferred work.
|
|
||||||
|
|
||||||
### 3. Move Completed Sections to `DONE.md`
|
### 3. Move Completed Sections to `DONE.md`
|
||||||
|
|
||||||
@@ -194,12 +274,12 @@ Update `README.md` whenever setup instructions, commands, project structure, or
|
|||||||
The root-level `CHANGELOG.md` records the user-facing changes for every release. It exists so players and contributors can see what changed between versions without reading the commit log.
|
The root-level `CHANGELOG.md` records the user-facing changes for every release. It exists so players and contributors can see what changed between versions without reading the commit log.
|
||||||
|
|
||||||
- Add an entry for every patch, minor, and major version change.
|
- Add an entry for every patch, minor, and major version change.
|
||||||
- Follow Semantic Versioning (`major.minor.patch`): bump major for breaking or vision-level changes, minor for new features, and patch for fixes and small polish.
|
- Follow Semantic Versioning (`major.minor.patch`): bump major only for changes that remove or break an existing player-facing feature, save format, or config contract, or that replace a system listed as a core pillar in PLAN.md; bump minor for new user-facing features; bump patch for fixes and small polish.
|
||||||
- A version increment is not complete until its `CHANGELOG.md` entry, `config/version` update and
|
- A version increment is not complete until its `CHANGELOG.md` entry, `config/version` update and
|
||||||
matching local release build have all been produced and verified.
|
matching local release build have all been produced and verified.
|
||||||
- Group each entry under `Added`, `Changed`, `Fixed`, and `Removed` headings, and date every entry.
|
- Group each entry under `Added`, `Changed`, `Fixed`, and `Removed` headings, and date every entry.
|
||||||
- Describe changes in user-facing terms, not as raw commit messages.
|
- Describe changes in user-facing terms, not as raw commit messages.
|
||||||
- Add the entry when its work ships. If the version number is not yet known, record it under `Unreleased` and move it into a numbered version when the version is tagged.
|
- Add the entry when its work ships. If the version number is not yet known, record it under `Unreleased` as a staging entry; moving it into a numbered version is a version increment and is complete only when the `config/version` update and matching local release build have also been produced and verified (see "Commit, Build and Version Discipline").
|
||||||
|
|
||||||
When a full executable level-2 milestone/theme in `TODO.md` is implemented, completed, and moved to `DONE.md`:
|
When a full executable level-2 milestone/theme in `TODO.md` is implemented, completed, and moved to `DONE.md`:
|
||||||
|
|
||||||
@@ -223,13 +303,10 @@ For each task:
|
|||||||
8. Commit each coherent, verified change as it is completed. Do not wait until a whole
|
8. Commit each coherent, verified change as it is completed. Do not wait until a whole
|
||||||
milestone is finished; keep commits small and descriptive and push them when the branch is
|
milestone is finished; keep commits small and descriptive and push them when the branch is
|
||||||
shareable.
|
shareable.
|
||||||
9. On every major, minor or patch version increment: update `config/version`, add the
|
9. If the change is a release (a major, minor, or patch version increment), follow the Release
|
||||||
`CHANGELOG.md` entry, run the local release build (`tools/build_releases.sh`), verify the
|
Procedure in "Commit, Build and Version Discipline".
|
||||||
artifacts and checksums, then commit, tag and push the version.
|
10. Push committed changes to the shared repository if applicable.
|
||||||
10. If a level-2 milestone from `TODO.md` has been fully completed, ensure its `CHANGELOG.md`
|
11. Note the new version, build artifacts or tag in the relevant documentation and handoff
|
||||||
entry and matching build are in place, then create the next semantic version or tag.
|
|
||||||
11. Push committed changes to the shared repository if applicable.
|
|
||||||
12. Note the new version, build artifacts or tag in the relevant documentation and handoff
|
|
||||||
summary.
|
summary.
|
||||||
|
|
||||||
Avoid unrelated refactors unless they are required for the task or explicitly approved.
|
Avoid unrelated refactors unless they are required for the task or explicitly approved.
|
||||||
@@ -240,12 +317,23 @@ Avoid unrelated refactors unless they are required for the task or explicitly ap
|
|||||||
milestone or release is finished. Keep the working tree clean between tasks where possible.
|
milestone or release is finished. Keep the working tree clean between tasks where possible.
|
||||||
- **Push regularly.** Push shareable commits to the shared repository; do not accumulate a long
|
- **Push regularly.** Push shareable commits to the shared repository; do not accumulate a long
|
||||||
local-only history.
|
local-only history.
|
||||||
- **Build every version.** Major, minor and patch increments each require a local release build
|
- **Build every version.** Every major, minor and patch increment follows the Release Procedure
|
||||||
and artifact/checksum verification before tagging. A changelog entry alone is not a release.
|
below; a changelog entry alone is not a release.
|
||||||
- **CI is a second gate, not a substitute.** Tag builds remain automated, but the local build is
|
- **CI is a second gate, not a substitute.** Tag builds remain automated, but the local build is
|
||||||
the required pre-tag check for every version.
|
the required pre-tag check for every version.
|
||||||
- **Unversioned tracks stay unversioned.** Character, map and other development tracks do not get
|
- **Unversioned tracks stay unversioned.** A development track does not get a placeholder
|
||||||
a placeholder version; the version is assigned when the work is scoped into a release.
|
version; the version is assigned when the work is scoped into a release.
|
||||||
|
|
||||||
|
**Release Procedure** — follow these steps in order for every major, minor, or patch version
|
||||||
|
increment:
|
||||||
|
|
||||||
|
1. Update `config/version`.
|
||||||
|
2. Write the `CHANGELOG.md` entry for the version.
|
||||||
|
3. Run the local release build (`tools/build_releases.sh`).
|
||||||
|
4. Verify the build artifacts and checksums.
|
||||||
|
5. Commit the changes.
|
||||||
|
6. Tag the version.
|
||||||
|
7. Push the commit and tag.
|
||||||
|
|
||||||
## Collaboration Rules
|
## Collaboration Rules
|
||||||
|
|
||||||
@@ -256,7 +344,7 @@ Avoid unrelated refactors unless they are required for the task or explicitly ap
|
|||||||
- Keep documentation synchronized with the implementation.
|
- Keep documentation synchronized with the implementation.
|
||||||
- Mention files changed and checks run in handoff summaries.
|
- Mention files changed and checks run in handoff summaries.
|
||||||
- Commit coherent increments as you go; do not wait until the end of a milestone.
|
- Commit coherent increments as you go; do not wait until the end of a milestone.
|
||||||
- Run a release build whenever a major, minor or patch version is incremented.
|
- Follow the Release Procedure in "Commit, Build and Version Discipline" whenever a major, minor or patch version is incremented.
|
||||||
- Leave follow-up work as checkbox items in `TODO.md` rather than hiding it in prose.
|
- Leave follow-up work as checkbox items in `TODO.md` rather than hiding it in prose.
|
||||||
- If blocked, document the blocker in `MEMORY.md` and add a corresponding task to `TODO.md`.
|
- If blocked, document the blocker in `MEMORY.md` and add a corresponding task to `TODO.md`.
|
||||||
- Avoid overwriting another contributor’s work; inspect recent changes before editing shared files.
|
- Avoid overwriting another contributor’s work; inspect recent changes before editing shared files.
|
||||||
@@ -268,6 +356,10 @@ Before handing off work, an agent must run every check below that applies to the
|
|||||||
- Run automated tests.
|
- Run automated tests.
|
||||||
- Run formatting, linting, or type checks.
|
- Run formatting, linting, or type checks.
|
||||||
- Start the game or application and perform a basic smoke test.
|
- Start the game or application and perform a basic smoke test.
|
||||||
|
- For 3D/Blender changes: `blender_assets.py inspect`, `audit` and `fit`, the relevant capture
|
||||||
|
gate, and a note of which local skills informed the change.
|
||||||
|
- If the local Blender skills are not installed, state "no local skills used" in the handoff
|
||||||
|
and list the skill-free checks that were run instead.
|
||||||
- Check that documentation and task status reflect the actual state.
|
- Check that documentation and task status reflect the actual state.
|
||||||
- Review the final diff for accidental changes or unfinished markers.
|
- Review the final diff for accidental changes or unfinished markers.
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,60 @@ All notable user-facing changes to Gig Line. The format follows
|
|||||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
||||||
Semantic Versioning.
|
Semantic Versioning.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.9.9] — 2026-11-06
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Raised the character mesh budget from 16,000 to 24,000 triangles and glTF
|
||||||
|
vertices so the Quaternius base and the role-dressed variants fit without
|
||||||
|
heavy decimation. Gear and prop budgets are unchanged.
|
||||||
|
- Replaced the whole NPC roster with the CC0 Quaternius characters. All twelve
|
||||||
|
characters (tourist, jacket, coat, vest, dress, hoodie, firefighter, guard and
|
||||||
|
the four crowd variants) now use one of three source outfits — casual tee,
|
||||||
|
hoodie or work clothes — dressed by role with the five tint slots and the gear
|
||||||
|
library. The procedural library and the MakeHuman head remain in the build as
|
||||||
|
the fallback.
|
||||||
|
- Characters stand with their arms down instead of a T/A pose, and the seated,
|
||||||
|
idle and walking poses swing from the shoulder and hip the way the animation
|
||||||
|
code intends on the new skeleton.
|
||||||
|
- Head gear (cap, hood, sunglasses, firefighter helmet) and the camera were
|
||||||
|
resized for the new head and arm. The camera now hangs from a wrist strap
|
||||||
|
instead of a neck strap that floated next to the head.
|
||||||
|
- The backpack straps were pulled in so they sit on the chest instead of
|
||||||
|
hovering in front of it.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Characters no longer render with black legs or black patches: the new base
|
||||||
|
carried a collapsed UV map that made the ambient-occlusion bake come out
|
||||||
|
black, and the roster is now unwrapped and baked correctly.
|
||||||
|
|
||||||
|
## [0.9.8] — 2026-11-04
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Characters no longer detach at the wrist: the wrist and hand now track the same
|
||||||
|
bone, the hand was raised to meet the forearm, and the sleeve cuff follows the
|
||||||
|
hand instead of the elbow.
|
||||||
|
- Seated and walking poses no longer collapse the hip/thigh or pinch the knee:
|
||||||
|
the thigh and shin are rigid to their bones with a short blend at the knee.
|
||||||
|
|
||||||
|
## [0.9.7] — 2026-11-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Character development: a more realistic face on all eight library characters
|
||||||
|
(higher head subdivision, brow ridge, both eyelids, fuller lower lip) and four
|
||||||
|
crowd-variety characters (`npc_crowd_a`–`d`) with deterministic per-role pools
|
||||||
|
so the routine crowd no longer clones a single look.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The NPC pose harness now captures seated, walking and idle deformation (the
|
||||||
|
harness froze the avatar's process so the FSM could no longer revert the pose).
|
||||||
|
|
||||||
## [0.10.0-rc.1] — 2026-11-03
|
## [0.10.0-rc.1] — 2026-11-03
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ been cleared for public distribution.
|
|||||||
|
|
||||||
## 3D characters, gear and props
|
## 3D characters, gear and props
|
||||||
|
|
||||||
|
The local MakeHuman head prototype uses the CC0 MakeHuman/MPFB base mesh
|
||||||
|
(see [`docs/makehuman-head-review.md`](docs/makehuman-head-review.md)); it is a
|
||||||
|
development prototype, not a shipped character.
|
||||||
|
|
||||||
The character, gear and prop library is generated by project-owned scripts
|
The character, gear and prop library is generated by project-owned scripts
|
||||||
(`tools/blender_assets.py`, `tools/npc_pipeline.py`, `tools/model_pipeline.py`)
|
(`tools/blender_assets.py`, `tools/npc_pipeline.py`, `tools/model_pipeline.py`)
|
||||||
using Blender. The `.blend` working sources are developer artifacts and are not
|
using Blender. The `.blend` working sources are developer artifacts and are not
|
||||||
@@ -40,11 +44,51 @@ kept outside version control.
|
|||||||
The CC-BY preview images are included only as development evidence. Any future
|
The CC-BY preview images are included only as development evidence. Any future
|
||||||
shipped derivative of CesiumMan or RiggedFigure must keep the attribution above.
|
shipped derivative of CesiumMan or RiggedFigure must keep the attribution above.
|
||||||
|
|
||||||
|
### MakeHuman head source
|
||||||
|
|
||||||
|
The standalone MakeHuman head prototype is derived from the MakeHuman / MPFB base
|
||||||
|
mesh, **CC0** (`assets/models/blender_src/makehuman_head.glb`; source
|
||||||
|
<https://static.makehumancommunity.org/makehuman.html>, MakeHuman base mesh,
|
||||||
|
CC0 1.0). It is cut, retopologised and re-weighted by project scripts; the exported
|
||||||
|
character GLBs remain project assets. The development source files were removed
|
||||||
|
from `tmp/` after the 2026-11-05 pivot to Quaternius. The head is retained as a
|
||||||
|
tested fallback, not the active NPC base. See ADR-0017 and
|
||||||
|
`docs/npc-model-sourcing.md`.
|
||||||
|
|
||||||
|
### Quaternius NPC base source
|
||||||
|
|
||||||
|
The active NPC base migration uses **Quaternius Ultimate Modular Characters** and
|
||||||
|
**Ultimate Modular Women** (CC0 1.0, <https://quaternius.com/>). The three
|
||||||
|
researched GLBs are development inputs and are not shipped directly; the baked,
|
||||||
|
retargeted character GLBs are project assets. See ADR-0017 and
|
||||||
|
`docs/npc-model-sourcing.md` for the decision, source hashes and rollout status.
|
||||||
|
|
||||||
The venue geometry is authored procedurally in `src/game/world_builder.gd` as a
|
The venue geometry is authored procedurally in `src/game/world_builder.gd` as a
|
||||||
stylized reconstruction. The Joke Shop is a fictional stand-in for the real
|
stylized reconstruction. The Joke Shop is a fictional stand-in for the real
|
||||||
Sunset Strip block; the game does not claim endorsement by, or an exact replica
|
Sunset Strip block; the game does not claim endorsement by, or an exact replica
|
||||||
of, any real venue or tenant.
|
of, any real venue or tenant.
|
||||||
|
|
||||||
|
### Development-only reference images
|
||||||
|
|
||||||
|
The venue reference boards under `docs/screens/refs/` are development documentation and are not
|
||||||
|
bundled with the game or its release packages. Files under CC BY are attributed here as well as in
|
||||||
|
their folder sidecar:
|
||||||
|
|
||||||
|
- `docs/screens/refs/hollywood/el-capitan-facade-day-wikimedia-2009-10-27.jpg` — "woo (Irvine, CA,
|
||||||
|
USA)", CC BY 2.0, via Wikimedia Commons.
|
||||||
|
- `docs/screens/refs/hollywood/el-capitan-theatre-blade-sign-day-wikimedia-2008-10.jpg` — Andreas
|
||||||
|
Praefcke, CC BY 3.0, via Wikimedia Commons.
|
||||||
|
- `docs/screens/refs/hollywood/premiere-staging-street-day-wikimedia-2006-02-12.jpg` — John
|
||||||
|
Barreiros, CC BY 2.0, via Wikimedia Commons.
|
||||||
|
- Public-domain files in the same folder: Charles W. Beam (1922) and Gary Minnaert (2006).
|
||||||
|
- `docs/screens/refs/hollywood/el-capitan-theatre-marquee-neighbour-day-wikimedia-2009-07-11.jpg` —
|
||||||
|
Alan Light, CC BY 2.0, via Wikimedia Commons.
|
||||||
|
- `docs/screens/refs/hollywood/hollywood-boulevard-night-context-ccbysa.jpg` — Downtowngal,
|
||||||
|
CC BY-SA 3.0, via Wikimedia Commons; used unmodified for colour/lighting study only, no derivative
|
||||||
|
asset is made from it.
|
||||||
|
- CC0 files in the Hollywood folder need no attribution: Luijtenphotos (2019) and Benoît Prieur
|
||||||
|
(2022).
|
||||||
|
|
||||||
## AI-assisted images
|
## AI-assisted images
|
||||||
|
|
||||||
Surface textures, skyline panoramas, UI art and concept references were produced
|
Surface textures, skyline panoramas, UI art and concept references were produced
|
||||||
|
|||||||
@@ -1109,7 +1109,6 @@ Completed 2026-11-01. The follow-ups left over after the texture/skyline adoptio
|
|||||||
|
|
||||||
Completed 2026-11-01.
|
Completed 2026-11-01.
|
||||||
|
|
||||||
|
|
||||||
### Storage restructure
|
### Storage restructure
|
||||||
|
|
||||||
- [x] Decide the canonical runtime image root (`assets/images/` for UI/scene art): recorded in
|
- [x] Decide the canonical runtime image root (`assets/images/` for UI/scene art): recorded in
|
||||||
@@ -1119,21 +1118,18 @@ Completed 2026-11-01.
|
|||||||
- [x] Re-run `godot --headless --path . --import` to regenerate `.import` sidecars after any move.
|
- [x] Re-run `godot --headless --path . --import` to regenerate `.import` sidecars after any move.
|
||||||
- [x] Confirm `.gitignore` still excludes development-only outputs (concepts, Blender sources, raw AI fabrics) under the new layout; Blender `.blend` sources are now untracked and ignored.
|
- [x] Confirm `.gitignore` still excludes development-only outputs (concepts, Blender sources, raw AI fabrics) under the new layout; Blender `.blend` sources are now untracked and ignored.
|
||||||
|
|
||||||
|
|
||||||
### Documentation path updates
|
### Documentation path updates
|
||||||
|
|
||||||
- [x] Update `README.md` paths (screen-designs section, confirmed-assets links, the `assets/images/scenes` resize example).
|
- [x] Update `README.md` paths (screen-designs section, confirmed-assets links, the `assets/images/scenes` resize example).
|
||||||
- [x] Update `docs/architecture.md`, `PLAN.md`, and ADR references: docs/screens/designs/ is now explicitly the reference-only snapshot; runtime art lives in assets/images/ (ADR-0014).
|
- [x] Update `docs/architecture.md`, `PLAN.md`, and ADR references: docs/screens/designs/ is now explicitly the reference-only snapshot; runtime art lives in assets/images/ (ADR-0014).
|
||||||
- [x] Update `tools/screen_designs.py` docstrings and mark the old paths in `DONE.md` as historical.
|
- [x] Update `tools/screen_designs.py` docstrings and mark the old paths in `DONE.md` as historical.
|
||||||
|
|
||||||
|
|
||||||
### Code path updates
|
### Code path updates
|
||||||
|
|
||||||
- [x] Point `tools/screen_designs.py generate` at the new design output path and keep `compare`/`judge` reading the right locations.
|
- [x] Point `tools/screen_designs.py generate` at the new design output path and keep `compare`/`judge` reading the right locations.
|
||||||
- [x] Update GDScript `preload()` paths if game textures move (no runtime material/model textures moved; UI art added under assets/images/).
|
- [x] Update GDScript `preload()` paths if game textures move (no runtime material/model textures moved; UI art added under assets/images/).
|
||||||
- [x] Update capture output directories if captures relocate (none did; QA captures stay in docs/screenshots/).
|
- [x] Update capture output directories if captures relocate (none did; QA captures stay in docs/screenshots/).
|
||||||
|
|
||||||
|
|
||||||
### Load proposed interfaces + scene art from disk
|
### Load proposed interfaces + scene art from disk
|
||||||
|
|
||||||
- [x] Add a central UI/scene art loader (e.g. `src/game/ui/art.gd`) that loads the mockup-derived images from the new runtime root, with a procedural fallback when a file is missing.
|
- [x] Add a central UI/scene art loader (e.g. `src/game/ui/art.gd`) that loads the mockup-derived images from the new runtime root, with a procedural fallback when a file is missing.
|
||||||
@@ -1144,19 +1140,16 @@ Completed 2026-11-01.
|
|||||||
- [x] Wire the wand popup to `wand_screening` art.
|
- [x] Wire the wand popup to `wand_screening` art.
|
||||||
- [x] Style the radial menu and finish the remaining HUD chrome against their reference images.
|
- [x] Style the radial menu and finish the remaining HUD chrome against their reference images.
|
||||||
|
|
||||||
|
|
||||||
### Verify against the mockups
|
### Verify against the mockups
|
||||||
|
|
||||||
- [x] Regenerate the staged captures and run `screen_designs.py compare` (11 good, 0 bad; verified in a clean HEAD worktree).
|
- [x] Regenerate the staged captures and run `screen_designs.py compare` (11 good, 0 bad; verified in a clean HEAD worktree).
|
||||||
- [x] Run `screen_designs.py judge` and clear every `diff` verdict (7 match, 4 review, 0 diff).
|
- [x] Run `screen_designs.py judge` and clear every `diff` verdict (7 match, 4 review, 0 diff).
|
||||||
- [x] Add the capture + compare step as a repeatable smoke so drift is caught early (`tools/check_screenshots.sh`).
|
- [x] Add the capture + compare step as a repeatable smoke so drift is caught early (`tools/check_screenshots.sh`).
|
||||||
|
|
||||||
|
|
||||||
## Fix mouse movement bug
|
## Fix mouse movement bug
|
||||||
|
|
||||||
- [x] Investigate why mouse input is not being captured or processed correctly in the game window (fixed: `_start_shift()` now calls `_refresh_freeze()`).
|
- [x] Investigate why mouse input is not being captured or processed correctly in the game window (fixed: `_start_shift()` now calls `_refresh_freeze()`).
|
||||||
|
|
||||||
|
|
||||||
## Improve in-game pop-ups
|
## Improve in-game pop-ups
|
||||||
|
|
||||||
- [x] Improve the visual appearance of in-game pop-ups (start/pause/settings/review/radio/wand/inspection art, buttons, palette, icons, text).
|
- [x] Improve the visual appearance of in-game pop-ups (start/pause/settings/review/radio/wand/inspection art, buttons, palette, icons, text).
|
||||||
@@ -1350,7 +1343,7 @@ venues, and see enough authored variety that a second shift does not feel like a
|
|||||||
- [x] Write the tutorial beat sheet: menu → movement/look → focus and target cycling → action
|
- [x] Write the tutorial beat sheet: menu → movement/look → focus and target cycling → action
|
||||||
wheel → access inspection → polite direction → formal warning/body camera → radio →
|
wheel → access inspection → polite direction → formal warning/body camera → radio →
|
||||||
shift review. Eight beats and a safe compliant visitor are specified in
|
shift review. Eight beats and a safe compliant visitor are specified in
|
||||||
[`docs/tutorial.md`](docs/tutorial.md).
|
[`docs/gameplay.md#first-shift-tutorial`](docs/gameplay.md#first-shift-tutorial).
|
||||||
- [x] Add a tutorial state machine independent of the normal shift FSM. `Tutorial` in
|
- [x] Add a tutorial state machine independent of the normal shift FSM. `Tutorial` in
|
||||||
`src/game/tutorial.gd` owns the eight beats, accepts only the current beat's action,
|
`src/game/tutorial.gd` owns the eight beats, accepts only the current beat's action,
|
||||||
supports skip/restart and has deterministic headless tests. GameManager/UI wiring starts
|
supports skip/restart and has deterministic headless tests. GameManager/UI wiring starts
|
||||||
@@ -1551,7 +1544,7 @@ and supported by a stable CI/release baseline.
|
|||||||
|
|
||||||
- [x] Existing scenario-content, tutorial and campaign smokes cover the venue/action/review paths;
|
- [x] Existing scenario-content, tutorial and campaign smokes cover the venue/action/review paths;
|
||||||
the 0.9.5 release candidate adds the external first-hour pass.
|
the 0.9.5 release candidate adds the external first-hour pass.
|
||||||
- [x] `docs/getting-started.md` and `docs/smoke-checklist.md` list tutorial, campaign/checkpoint,
|
- [x] `docs/getting-started.md` and `docs/release-qa.md#manual-smoke-checklist` list tutorial, campaign/checkpoint,
|
||||||
session, scenario, audio, crowd, emergency, movement and performance smokes.
|
session, scenario, audio, crowd, emergency, movement and performance smokes.
|
||||||
- [x] Current gallery/judge baselines are green; the 0.9.5 release candidate refreshes the final
|
- [x] Current gallery/judge baselines are green; the 0.9.5 release candidate refreshes the final
|
||||||
gallery after content lock.
|
gallery after content lock.
|
||||||
@@ -1596,7 +1589,7 @@ Goal: streamline the maps, refactor geometry to match the images in `docs/screen
|
|||||||
- [x] Remove unnecessary or hidden geometry to improve performance.
|
- [x] Remove unnecessary or hidden geometry to improve performance.
|
||||||
- [x] Ensure texture and material consistency across the map.
|
- [x] Ensure texture and material consistency across the map.
|
||||||
|
|
||||||
Completed: the Hollywood venue is rebuilt from the real 6840 Hollywood Blvd references (six-column Masonic/El Capitan facade, eight steps, neighbours, Ovation arcade, Walk of Fame, four-lane street). The map-budget test measures 58,884 triangles across 409 meshes; Day/Night menu variants start in `EARLY`/`LATE`; the realism judge gaps are 0.148 (day) and 0.278 (late). See `docs/hollywood-boulevard-map.md`.
|
Completed: the Hollywood venue is rebuilt from the real 6840 Hollywood Blvd references (six-column Masonic/El Capitan facade, eight steps, neighbours, Ovation arcade, Walk of Fame, four-lane street). The map-budget test measures 58,884 triangles across 409 meshes; Day/Night menu variants start in `EARLY`/`LATE`; the realism judge gaps are 0.148 (day) and 0.278 (late). See `docs/venues.md#hollywood-boulevard`.
|
||||||
|
|
||||||
### Map 2 — Joke Shop
|
### Map 2 — Joke Shop
|
||||||
|
|
||||||
@@ -1608,7 +1601,7 @@ Completed: the Hollywood venue is rebuilt from the real 6840 Hollywood Blvd refe
|
|||||||
- [x] Remove unnecessary or hidden geometry to improve performance.
|
- [x] Remove unnecessary or hidden geometry to improve performance.
|
||||||
- [x] Ensure texture and material consistency across the map.
|
- [x] Ensure texture and material consistency across the map.
|
||||||
|
|
||||||
Completed 2026-11-02: the Joke Shop is rebuilt from the real Comedy Store block at 8433 Sunset Blvd (27 m black hall, signature canopy, circular blade sign, red curtains, west entrance, low-rise neighbours, dry hillside and opposite frontage). The map-budget test measures 22,680 triangles across 454 meshes. The day/late captures and real-site notes are in `docs/joke-shop-map.md` and `docs/screens/concepts/joke_shop/`; the final gallery passed 13 good / 0 bad and the screen-design judge (13/13 match, 0 diff), with a 0.454 realism gap on the photoreal night reference.
|
Completed 2026-11-02: the Joke Shop is rebuilt from the real Comedy Store block at 8433 Sunset Blvd (27 m black hall, signature canopy, circular blade sign, red curtains, west entrance, low-rise neighbours, dry hillside and opposite frontage). The map-budget test measures 22,680 triangles across 454 meshes. The day/late captures and real-site notes are in `docs/venues.md#the-joke-shop` and `docs/screens/concepts/joke_shop/`; the final gallery passed 13 good / 0 bad and the screen-design judge (13/13 match, 0 diff), with a 0.454 realism gap on the photoreal night reference.
|
||||||
|
|
||||||
### Scenery Change (Day/Night) for Hollywood Boulevard
|
### Scenery Change (Day/Night) for Hollywood Boulevard
|
||||||
|
|
||||||
@@ -1727,9 +1720,9 @@ screen-design judge before any model/texture changes. No gameplay rules change.
|
|||||||
- [x] Ran `tools/check_screenshots.sh` and `screen_designs.py compare`: 21 good,
|
- [x] Ran `tools/check_screenshots.sh` and `screen_designs.py compare`: 21 good,
|
||||||
0 bad.
|
0 bad.
|
||||||
- [x] Ran the judge with automatic model selection: 21/21 match, 0 review, 0 diff.
|
- [x] Ran the judge with automatic model selection: 21/21 match, 0 review, 0 diff.
|
||||||
- [x] Recorded the judge model and result in `docs/screen-design-report.md`; the
|
- [x] Recorded the judge model and result in `docs/ui-copy.md#screen-design-judge-report`; the
|
||||||
generated captures remain local/gitignored.
|
generated captures remain local/gitignored.
|
||||||
- [x] Add a short UI copy/button checklist to `docs/smoke-checklist.md`.
|
- [x] Add a short UI copy/button checklist to `docs/release-qa.md#manual-smoke-checklist`.
|
||||||
- [x] Exit: all screens have consistent terminology and button behavior, long
|
- [x] Exit: all screens have consistent terminology and button behavior, long
|
||||||
strings/scale pass, and the Python compare/judge gate has no unexplained
|
strings/scale pass, and the Python compare/judge gate has no unexplained
|
||||||
diff.
|
diff.
|
||||||
@@ -1775,7 +1768,7 @@ and equipment fit.
|
|||||||
|
|
||||||
- [x] Audit all variants against a shared human proportion guide (head height, shoulder width,
|
- [x] Audit all variants against a shared human proportion guide (head height, shoulder width,
|
||||||
torso length, hip width, limb length, foot length) and correct the worst outliers.
|
torso length, hip width, limb length, foot length) and correct the worst outliers.
|
||||||
(`tools/npc_proportions.py` + `docs/npc-proportion-audit.md`: all eight pass; no skeleton or
|
(`tools/npc_proportions.py` + `docs/npc-visual-review.md#proportion-audit`: all eight pass; no skeleton or
|
||||||
mesh rescue was needed. Head and broad-build silhouettes are nearest their upper bounds.)
|
mesh rescue was needed. Head and broad-build silhouettes are nearest their upper bounds.)
|
||||||
- [x] Improve the neck and shoulder transition so the silhouette reads as one body rather than a
|
- [x] Improve the neck and shoulder transition so the silhouette reads as one body rather than a
|
||||||
thin post on a hanger. (Fuller neck base/top, lower shoulder root, degenerate-face cleanup;
|
thin post on a hanger. (Fuller neck base/top, lower shoulder root, degenerate-face cleanup;
|
||||||
@@ -1943,7 +1936,6 @@ Compatibility/Web parity, deterministic assets and offline CI.
|
|||||||
carry the texture results; the 0.9.5 section moved to `DONE.md` after the
|
carry the texture results; the 0.9.5 section moved to `DONE.md` after the
|
||||||
release build.)
|
release build.)
|
||||||
|
|
||||||
|
|
||||||
Completed 2026-11-02: all 0.9.5 texture and material checks are closed. The NPC
|
Completed 2026-11-02: all 0.9.5 texture and material checks are closed. The NPC
|
||||||
base and eight library characters use 256² maps, cloth/pants use the 4× fabric
|
base and eight library characters use 256² maps, cloth/pants use the 4× fabric
|
||||||
weave without baked-AO blotches, the Walk of Fame band uses the paver/terrazzo
|
weave without baked-AO blotches, the Walk of Fame band uses the paver/terrazzo
|
||||||
@@ -2022,7 +2014,6 @@ target paths and leave a clean release-candidate handoff.
|
|||||||
- [x] Exit: combined build is functionally green, visually reviewed, performance
|
- [x] Exit: combined build is functionally green, visually reviewed, performance
|
||||||
measured and ready for the 0.10.0 content-lock/playtest phase.
|
measured and ready for the 0.10.0 content-lock/playtest phase.
|
||||||
|
|
||||||
|
|
||||||
Completed 2026-11-03: the 0.9.6 integration checkpoint is closed. The current
|
Completed 2026-11-03: the 0.9.6 integration checkpoint is closed. The current
|
||||||
asset set re-imported and passed `blender_assets.py audit`/`fit` and
|
asset set re-imported and passed `blender_assets.py audit`/`fit` and
|
||||||
`npc_pipeline.py audit`; the full suite is 263/263 with 101/101 Python tests and
|
`npc_pipeline.py audit`; the full suite is 263/263 with 101/101 Python tests and
|
||||||
@@ -2033,3 +2024,171 @@ with a 0.291 Pillow gap. A clean worktree built Linux/Windows/macOS/Web/HQ at
|
|||||||
161.5/197.1/148.0/97.8/241.5 MB; the Web zip and HQ override were checked and
|
161.5/197.1/148.0/97.8/241.5 MB; the Web zip and HQ override were checked and
|
||||||
the HQ binary launched headless. Tagged `v0.9.6`; Gitea run 1296 passed checks
|
the HQ binary launched headless. Tagged `v0.9.6`; Gitea run 1296 passed checks
|
||||||
and the full build and published seven assets.
|
and the full build and published seven assets.
|
||||||
|
|
||||||
|
## Character development
|
||||||
|
|
||||||
|
Completed 2026-11-04. The procedural crowd-variety roster (four new variants, per-role pools)
|
||||||
|
shipped with the realistic face; the external-asset intake and the MakeHuman body pipeline were
|
||||||
|
promoted to the active **MakeHuman character pipeline** track in `TODO.md`.
|
||||||
|
|
||||||
|
## Character development
|
||||||
|
|
||||||
|
Goal: break the eight-character clone effect with four licence-cleared crowd-variety looks while
|
||||||
|
keeping the frozen `npc_base` bone, tint-slot and gear-attach contract. The procedural generator is
|
||||||
|
the primary path; Quaternius is a reviewed reference and MakeHuman is deferred to a separate local
|
||||||
|
development pipeline.
|
||||||
|
|
||||||
|
The roster build is deliberately sequenced **after** the 3D model and local map re-evaluation: the new variants
|
||||||
|
must inherit the reviewed face/hand/gear baseline instead of multiplying whatever it finds.
|
||||||
|
|
||||||
|
*Skill route: `character-artist`, `retopology`, `rigging`, `uv-workflow`, `materials`,
|
||||||
|
`asset-optimization`, `lowpoly-style`, `godot-export`, `qa-review`.*
|
||||||
|
|
||||||
|
### Source review and decision
|
||||||
|
|
||||||
|
- [x] Research free/open-source character sources and evaluate at least three downloads against
|
||||||
|
the existing Blender contract. (Measured Quaternius CC0, Kenney CC0, Khronos
|
||||||
|
CesiumMan/RiggedFigure CC-BY and the MakeHuman CC0 base mesh; none is a drop-in.
|
||||||
|
Results, local preview sheet and SHA-256 provenance are in
|
||||||
|
`docs/npc-model-sourcing.md`.)
|
||||||
|
- [x] Review the sourcing research with the project owner and choose Option A (procedural),
|
||||||
|
Option B (Quaternius CC0 intake) or Option C (MakeHuman/MPFB body pipeline); record the
|
||||||
|
decision in an ADR and in `MEMORY.md`. (2026-11-03: extend the procedural generator as the
|
||||||
|
primary path, keep Quaternius as a reviewed prototype/reference, defer MakeHuman to a
|
||||||
|
separate local pipeline. Recorded in ADR-0015 and `MEMORY.md`.)
|
||||||
|
- [x] Add a source licence allow-list and provenance rule: CC0 preferred, CC-BY only with
|
||||||
|
attribution in `CREDITS.md`, and no service assets whose terms restrict redistribution.
|
||||||
|
(ADR-0015; the CC-BY research previews are attributed in `CREDITS.md`.)
|
||||||
|
- [x] Decide whether the external preview sheet stays local under `tmp/` or is committed with
|
||||||
|
the required CC-BY attribution. (Committed as
|
||||||
|
`docs/blender_previews/sheet_npc_sources_research.jpg` with Cesium CC-BY attribution;
|
||||||
|
the project-owned crowd prototype sheet is also committed.)
|
||||||
|
|
||||||
|
### Intake and validation
|
||||||
|
|
||||||
|
- [x] Add a Blender `inspect` path that imports a GLB, applies node transforms, reports
|
||||||
|
tris/verts, bounds, bones, materials, UVs and skinning, and lists contract failures.
|
||||||
|
(`blender_assets.py inspect --file ...` applies parent/object transforms, converts to
|
||||||
|
Godot Y-up, ignores the importer's bone-shape helper and prints budgets, bones, slots,
|
||||||
|
UVs, skinning and animations with issues/warnings; `--json` and `--category` supported.
|
||||||
|
Seven synthetic contract tests added; all 46 library GLBs inspect clean. `audit` remains
|
||||||
|
the final gate.)
|
||||||
|
- [x] `adapt` path: promoted to the **MakeHuman character pipeline** track (2026-11-04).: scale/ground, weld seams, decimate, map the
|
||||||
|
five tint slots, and either rebuild the contract armature or record an explicit second-rig
|
||||||
|
contract. Use `retopology`/`rigging`/`uv-workflow` guidance for the manual steps, but keep
|
||||||
|
the Quaternius no-go: a contract rebuild needs a proportion warp, so this is not on the
|
||||||
|
character critical path.
|
||||||
|
- [x] Audit gate + intake regression test: promoted to the **MakeHuman character pipeline** track.
|
||||||
|
- [x] Adapter documentation: promoted to the **MakeHuman character pipeline** track.
|
||||||
|
|
||||||
|
### Quaternius prototype
|
||||||
|
|
||||||
|
- [x] Prototype one Quaternius CC0 variant: weld/decimate to budget and capture Idle/Walk
|
||||||
|
renders. (12,518 imported verts -> 3,199 after weld -> 2,423 / 4,654 tris after a 0.75
|
||||||
|
decimate; Idle/Walk quality is good. Preview in
|
||||||
|
`docs/blender_previews/sheet_npc_sources_research.jpg`.)
|
||||||
|
- [x] Measure the prototype against the `npc_casual` controls: silhouette, rest pose, body
|
||||||
|
proportions and gear fit. (No-go for a contract rebuild: T-pose 62-bone realistic rig,
|
||||||
|
~0.96 m hip, ~1.68 m arm span and ~0.153 m shoulder joint do not match the arm-down
|
||||||
|
13-bone contract. Keep as reference; a second rig/animation path needs its own ADR.)
|
||||||
|
- [x] Make a go/no-go decision on Option B. (No-go for the current pass; proceed with the procedural
|
||||||
|
generator and keep Quaternius as a visual/style reference.)
|
||||||
|
|
||||||
|
### Roster and runtime
|
||||||
|
|
||||||
|
- [x] Approve four crowd-variety looks (build, head silhouette, hair/beard, outer layer and
|
||||||
|
accessory; no clothing that labels the NPC's behaviour). (`npc_crowd_a` bun/slim/sunglasses,
|
||||||
|
`npc_crowd_b` bald/broad/beard/shoulder bag, `npc_crowd_c` coat/umbrella, `npc_crowd_d`
|
||||||
|
hood/cup; prototype sheet committed.)
|
||||||
|
- [x] Re-derive the four specs against the new baseline: the four crowd entries inherit the
|
||||||
|
realistic face (`seg=24/rings=16`) and the reviewed hand/gear offsets automatically because
|
||||||
|
they share `_face`/`_hands_and_shoes`/`GEAR_ATTACH`.
|
||||||
|
- [x] Generate the four variants: built via `blender_assets.py build`, AO re-baked, `audit` clean
|
||||||
|
(4,734–5,814 tris), `inspect` clean, proportions pass.
|
||||||
|
- [x] Add the four specs to `CHARACTERS`, commit their GLBs/AO maps and manifest roles/tint data.
|
||||||
|
(Note: this adds ~2 MB to the next release build via `export_filter=all_resources` — accepted
|
||||||
|
for the crowd-variety roster.)
|
||||||
|
- [x] Per-role pools: `character_model_for()` now picks from `CHARACTER_POOLS` by seed — the
|
||||||
|
routine crowd roles gain the crowd variants (tourist → casual/crowd_a/crowd_d,
|
||||||
|
confused_visitor → casual/crowd_b, intoxicated → casual/crowd_c) while the special roles
|
||||||
|
keep their existing alternation (guest ⅓ jacket, trespasser/threat ½ hoodie). The test
|
||||||
|
now checks seed-stability plus that every pool entry resolves to a committed GLB
|
||||||
|
(277/277).
|
||||||
|
- [x] Gear fit + inspect for the four new variants: `fit` is clean (sunglasses band=inside,
|
||||||
|
shoulder bag clear; cup/umbrella are hand gear with the forward offset), `inspect` clean,
|
||||||
|
proportions pass.
|
||||||
|
- [x] Crowd-variety reference: five licence-cleared Commons photos fetched to
|
||||||
|
`tmp/research/crowd-variety/`; the AI proposal set stays the next step for detail.
|
||||||
|
- [x] Baseline + portrait rows: the four variants are in `capture_npc_baseline.gd` and
|
||||||
|
`sheet_npc_baseline.jpg` (12 variants × 6 views); review-doc rows added.
|
||||||
|
|
||||||
|
### QA and release
|
||||||
|
|
||||||
|
- [x] Gates with the expanded library: 24-NPC budget smoke 152,464 tris / 168 meshes / 6.94 ms
|
||||||
|
(under 50 ms), portrait baseline 12 variants × 6 views, `fit` clean, Godot 277/277, Python
|
||||||
|
160/160. No release build is created for this unversioned track.
|
||||||
|
- [x] Docs: `docs/development-status.md`, `docs/3d-model-review.md` and the baseline sheet updated;
|
||||||
|
`CREDITS.md` needs no change (the crowd variants are procedural, no new source). Accepted
|
||||||
|
visual limits: the crowd accessories (umbrella canopy, cup, sunglasses, beard) read at
|
||||||
|
full-body distance but not in the head-only portrait framing.
|
||||||
|
- [x] Exit: the 12-character library passes contract audit, portrait/gear acceptance, the 24-NPC
|
||||||
|
budget and licence/provenance review; the runtime contract (13 bones, five tint slots,
|
||||||
|
`npc_base` fallback) is unchanged.
|
||||||
|
|
||||||
|
### Deferred MakeHuman local pipeline
|
||||||
|
|
||||||
|
- [x] Evaluate the MakeHuman CC0 base mesh for a later separate local development pipeline.
|
||||||
|
(19,158 verts / 36,972 tris; scaled to 1.82 m; decimate 0.35 -> 3,780 verts / 6,468 tris;
|
||||||
|
UVs present, no normals/material groups/rig. Feasible only with retopology, a custom rig and
|
||||||
|
project-owned clothing; keep out of CI/build.)
|
||||||
|
- [x] Body-shape diversity decision (2026-11-04, owner): **yes** — use MakeHuman for the next
|
||||||
|
character pass. Promoted to the **MakeHuman character pipeline** track.
|
||||||
|
|
||||||
|
## NPC pose harness (v0.9.8)
|
||||||
|
|
||||||
|
Completed 2026-11-04. The pose harness bug that made every staged capture save a rest frame
|
||||||
|
was found and fixed; it unblocked the weights/pose evidence and the seated/walking deformation
|
||||||
|
fixes. User-facing changes are recorded under 0.9.7 and 0.9.8 in `CHANGELOG.md`.
|
||||||
|
|
||||||
|
Goal: `src/test/capture_npc_pose.gd` reports a pose but saves rest frames; the root cause is
|
||||||
|
narrowed to the skinned mesh ignoring its `Rig` parent transform. Fixing it unblocks the
|
||||||
|
weights/pose column of the 3D review; the evidence now feeds the Quaternius base bake.
|
||||||
|
|
||||||
|
*Skill route: `rigging`; evidence in `docs/3d-model-review.md` §"Pose evidence is blocked".*
|
||||||
|
|
||||||
|
- [x] Root cause found (2026-11-04): the harness never disabled the avatar's `_physics_process`,
|
||||||
|
so `_advance_fsm` + `_move_toward_target` ran every tick and reset `arrived`/velocity, which
|
||||||
|
made `theater_pose()` return "idle" and reverted the pose to rest. (The earlier "moving the
|
||||||
|
`Rig` child does not render" probe was a red herring: it set `rig.position.y` inside `_shoot()`
|
||||||
|
in the same frame as `root.get_texture().get_image()`, so it never got a rendered frame.)
|
||||||
|
- [x] Harness rebuilt: `capture_npc_pose.gd` now calls `set_physics_process(false)`/
|
||||||
|
`set_process(false)` after staging the avatar; the pose survives because only the direct
|
||||||
|
`_animate()` ticks drive it. Verified: sit vs idle diff 5.63, walk vs idle diff 2.29, and the
|
||||||
|
vision judge confirms the seated frame reads as crouched.
|
||||||
|
- [x] Captured all poses for all 12 variants (idle / walk_contact / walk_pass / sit × full +
|
||||||
|
shoulder views = 96 frames) and built `sheet_pose.jpg`/`sheet_shoulder.jpg` under `tmp/`.
|
||||||
|
(No separate "wave" theater pose exists; the escalation raise-hand gesture is not a distinct
|
||||||
|
capture in this harness.)
|
||||||
|
|
||||||
|
## Milestone 0.9.7 — Character development (patch)
|
||||||
|
|
||||||
|
Completed 2026-11-04 and published as v0.9.7. The detailed character-development record already
|
||||||
|
lives under "Character development" above; this milestone checklist is kept verbatim for the
|
||||||
|
release record.
|
||||||
|
|
||||||
|
Goal: release the finished procedural character development — the realistic face on all eight
|
||||||
|
variants, the four crowd-variety characters and the per-role pools — as a **patch** on top of
|
||||||
|
0.9.6. This keeps the 3D model and map improvements out of the patch; they land together in the
|
||||||
|
0.10.0 minor.
|
||||||
|
|
||||||
|
- [x] Rig pose harness fixed and the weights/pose evidence captured (see the top track).
|
||||||
|
- [x] Character work done: 12-character library, realistic face, crowd variants, per-role pools,
|
||||||
|
24-NPC budget 6.94 ms (152,464 tris / 168 meshes).
|
||||||
|
- [x] QA + local build for 0.9.7: Godot 277/277, Python 161/161, audit 49 OK, fit/proportions
|
||||||
|
12/12, release smoke matrix green, gallery 21/0, clean worktree build (Linux 172 MB,
|
||||||
|
Windows 206 MB, macOS 159 MB, Web 111 MB, HQ 266 MB), checksums verified, Linux binary
|
||||||
|
launches headless. Tagged `v0.9.7` (CI build/publish follows).
|
||||||
|
|
||||||
|
**Exit:** met — 0.9.7 ships the 12-character library (face realism + crowd variety) with the pose
|
||||||
|
harness fixed; the runtime contract is unchanged. The Quaternius NPC base and the map rebuild are
|
||||||
|
not in this patch (they target 0.10.0).
|
||||||
@@ -16,20 +16,91 @@ The game is about **judgment, communication, access verification, and proportion
|
|||||||
|
|
||||||
## Development Priorities
|
## Development Priorities
|
||||||
|
|
||||||
The active planning order is set in [`TODO.md`](TODO.md) and in `AGENTS.md`:
|
The active task order is in [`TODO.md`](TODO.md); the gap analysis and the release roadmap are
|
||||||
|
below. The current order is:
|
||||||
|
|
||||||
1. **Character development (unversioned).** Expand the owned procedural character library with
|
1. **0.9.9–0.9.12 patches.** Finish the character base (the accepted Quaternius 62-joint runtime),
|
||||||
crowd, age, build, hair and outfit variety. Keep the frozen bone/tint/gear contract; see
|
roll it out to the 12 roles, close the scheduled venue/prop/lighting polish, and verify
|
||||||
[ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md) and
|
performance, Web and accessibility.
|
||||||
[`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md). Quaternius is a reference;
|
2. **0.10.0 minor.** Public readiness: external playtests, balance acceptance, legal/storefront
|
||||||
MakeHuman is deferred to a separate local pipeline.
|
approval, the platform matrix and the documentation/picture lock.
|
||||||
2. **Local map development (unversioned).** Move beyond stylized venue reconstruction with
|
3. **0.10.x patches → 0.11.0.** Content and systems depth: live body-camera replay, a fourth
|
||||||
licensed OpenStreetMap and 3D building data, downloaded reference imagery for photoreal
|
venue and the localization foundation.
|
||||||
facades, AI-assisted composition, and a Gaussian-splatting evaluation that stays in the
|
4. **0.12.0 → 1.0.0.** Platform/distribution, content complete, release candidates and the stable
|
||||||
development pipeline unless it proves runtime-safe.
|
release.
|
||||||
3. **Versioned release hardening.** The 0.10.0 release candidate and later semantic versions come
|
|
||||||
after the unversioned development tracks in planning order. Versions are assigned only when a
|
Versions are assigned only when a track's scope is accepted into a release (see
|
||||||
track's scope is accepted into a release.
|
[Release Roadmap](#release-roadmap)). Unversioned tracks — the 3D review evidence base,
|
||||||
|
maintenance and the Gaussian-splatting study — do not receive a placeholder version.
|
||||||
|
|
||||||
|
## Plan Evaluation (2026-11-05)
|
||||||
|
|
||||||
|
The game plan itself is sound and largely built: three venues, the escalation ladder, access
|
||||||
|
artifacts, the radio/report system, the shift review and the campaign loop all exist and are
|
||||||
|
tested. What needed fixing was the **delivery plan**, not the design.
|
||||||
|
|
||||||
|
- **Character sourcing churned.** The project moved procedural → MakeHuman → Quaternius, and the
|
||||||
|
Quaternius Stage-1 warp failed its own gate. That leaves a correct decision (ADR-0018) with a
|
||||||
|
half-finished implementation. The plan now makes the rig correction one small version, gates the
|
||||||
|
roster behind a face/readability decision, and keeps exactly one fallback.
|
||||||
|
- **One milestone carried too much.** The old 0.10.0 mixed character work, map work and every
|
||||||
|
public-release gate, so one blocked export blocked the release. Character and venue polish are
|
||||||
|
now patches; 0.10.0 is only the release gate.
|
||||||
|
- **Unversioned work had no deadline.** The 3D review backlog could grow without bound. It is now
|
||||||
|
capped: the ranked venue/prop/lighting items are scheduled into 0.9.11, the budget checks into
|
||||||
|
0.9.12, and everything else is an explicit accept or deferred. Gaussian splatting stays
|
||||||
|
unscheduled because no product need justifies its risk yet.
|
||||||
|
- **Legal, signing and storefront work is release work, not polish.** It now has named checkboxes
|
||||||
|
in 0.10.0 and 1.0.0 instead of living in prose.
|
||||||
|
- **What is deliberately not planned:** new genres or mechanics, a live-service model, or a
|
||||||
|
runtime splat renderer. The de-escalation-first, rare-emergency design stays frozen.
|
||||||
|
|
||||||
|
Feasibility: the remaining character work is small in geometry terms (6.2k triangles against a
|
||||||
|
24k cap) and larger in rig/export terms; the venue work is polish; most release gates are
|
||||||
|
evidence gathering rather than engineering. The main schedule risk is external review and
|
||||||
|
approval, not code.
|
||||||
|
|
||||||
|
## Character Creation Re-Evaluation (2026-11-05)
|
||||||
|
|
||||||
|
**Where it stands.** The shipped library is 12 procedural Blender variants (4.7k–5.8k triangles
|
||||||
|
each) with a sculpted face, five tint slots and 18 gear pieces. The owner then chose the CC0
|
||||||
|
**Quaternius Ultimate Modular Characters** as the primary source. Stage 1 (warp the Quaternius
|
||||||
|
mesh onto the 13-core contract) failed its silhouette gate — arm ×1.63, leg ×0.74, torso ×1.61,
|
||||||
|
judged distorted. [ADR-0018](docs/adr/0018-quaternius-stage2-runtime-rig.md) therefore adopts the
|
||||||
|
source **62-joint rig at runtime**: no warp, source LOD (3,199 verts / 6,206 tris), source
|
||||||
|
Idle/Walk clips, and gear pivots re-derived from the Quaternius skeleton.
|
||||||
|
|
||||||
|
**What is already built.** The canonical `quaternius_base.glb` (welded, grounded, arm-down rest),
|
||||||
|
the 62→13 joint map, the material→five-slot map, the clip inventory (24 clips; Idle and Walk in
|
||||||
|
scope) and the `NpcAvatar` joint-name mapping with rest-derived gear pivots.
|
||||||
|
|
||||||
|
**What actually blocks it.** Two technical items about the export and the pose math, not the art:
|
||||||
|
|
||||||
|
1. the arm-down GLB renders in A-pose in Godot because the bake writes wrong inverse bind
|
||||||
|
matrices; and
|
||||||
|
2. `_pose_bone()` composes rotations as `rest * Quaternion(axis, angle)`, which only cancels for
|
||||||
|
the old 180° rest, not the Quaternius ~76° rest.
|
||||||
|
|
||||||
|
**Re-evaluation findings.**
|
||||||
|
|
||||||
|
- Triangle budget is not the problem (6.2k against a 24k cap). Do not decimate the source.
|
||||||
|
- **Face fidelity is the real risk.** The 0.9.7 library has a sculpted realistic face; the
|
||||||
|
Quaternius head is low-poly. A blanket swap would regress every close-up. The plan therefore
|
||||||
|
makes the owner choose at 0.9.9 between (a) a full swap, (b) Quaternius for crowd roles with the
|
||||||
|
procedural library for named roles, or (c) extra head subdivision/hair cards on the Quaternius
|
||||||
|
head.
|
||||||
|
- **Animation** becomes clip-driven, which removes the hand-built pose composition as the main
|
||||||
|
path. Poses the clips do not cover (seated, raise-hand, head turn) must be decided explicitly
|
||||||
|
rather than patched per scene.
|
||||||
|
- **One fallback, no third source.** The procedural `npc_base` and the MakeHuman head stay tested
|
||||||
|
until the new base passes the gate. MPFB body work stays closed and the Stage-1 warp is not
|
||||||
|
reopened. A new character source needs a new ADR.
|
||||||
|
- **Sequence matters.** Fix the rig first (0.9.9), then the roster (0.9.10). Rolling a broken rig
|
||||||
|
across 12 variants would waste the whole pass.
|
||||||
|
|
||||||
|
**Reversal condition.** If the skin cannot be exported correctly without a new exporter path, or
|
||||||
|
if the owner rejects the low-poly face in every option, the plan falls back to the procedural
|
||||||
|
library and retires Quaternius with a recorded ADR. That decision is made at 0.9.9, not later.
|
||||||
|
|
||||||
## Core Gameplay Loop
|
## Core Gameplay Loop
|
||||||
|
|
||||||
@@ -144,7 +215,7 @@ Joke Shop escalation follows the common ladder: direction, explanation, repeat r
|
|||||||
|
|
||||||
### Hollywood Boulevard Late Night TV Studio
|
### Hollywood Boulevard Late Night TV Studio
|
||||||
|
|
||||||
A busy studio entrance with audience members, production staff, performers, guests, deliveries, celebrity arrivals, tourists, influencers, and the same range of street behavior as the building scenario.
|
A busy studio entrance at the same real El Capitan Entertainment Centre block as the Hollywood day shift, restaged for a late-night arrival: the same stone facade and steps with a bulb-lit canopy, a black car at the kerb and a barrier line. It has audience members, production staff, performers, guests, deliveries, celebrity arrivals, tourists, influencers, and the same range of street behavior as the building scenario.
|
||||||
|
|
||||||
Staff and guests are recognized or present a wristband, credential, ticket, guest-list confirmation, or other approved marker. The player keeps the steps clear, protects arrivals and equipment, manages filming boundaries, watches for tailgating, and calls for help only when there is immediate danger.
|
Staff and guests are recognized or present a wristband, credential, ticket, guest-list confirmation, or other approved marker. The player keeps the steps clear, protects arrivals and equipment, manages filming boundaries, watches for tailgating, and calls for help only when there is immediate danger.
|
||||||
|
|
||||||
@@ -169,15 +240,15 @@ The action wheel prioritizes de-escalation: Stop, Look, Point, No, Ask, Inspect,
|
|||||||
|
|
||||||
The visual direction is readable stylized realism:
|
The visual direction is readable stylized realism:
|
||||||
|
|
||||||
- **Character direction:** 8 skinned NPC variants, 18 gear pieces and 20 street/venue props, with four crowd-variety variants prototyped. The generator-first expansion is the active character track ([ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md)); Quaternius is a reference and MakeHuman is deferred to a local pipeline.
|
- **Character direction:** 12 skinned NPC variants, 18 gear pieces and 20 street/venue props. The active character track is the Quaternius base at runtime ([ADR-0018](docs/adr/0018-quaternius-stage2-runtime-rig.md)): the CC0 source mesh keeps its proportions on the 62-joint rig, Idle/Walk run as clips, and the procedural library stays as the tested fallback until the face/silhouette gate passes. See [Character Creation Re-Evaluation](#character-creation-re-evaluation-2026-11-05).
|
||||||
- **Realism pass:** AI-assisted per-phase skyline panoramas, v2 surface tiles with normal maps, wet asphalt at night, a warm day haze, phase-specific sun/exposure/fog, a visible flashlight beam, and denser foliage.
|
- **Realism pass:** AI-assisted per-phase skyline panoramas, v2 surface tiles with normal maps, wet asphalt at night, a warm day haze, phase-specific sun/exposure/fog, a tight flashlight spot (no beam geometry), and denser foliage.
|
||||||
- **Local map direction:** the venues are currently stylized reconstructions. The local-map track evaluates licensed OpenStreetMap/3D building data, licensed reference photography, AI-assisted composition and Gaussian splatting as a development tool; online data and splat rendering are not runtime dependencies.
|
- **Local map direction:** the venues are currently stylized reconstructions. The local-map track evaluates licensed OpenStreetMap/3D building data, licensed reference photography, AI-assisted composition and Gaussian splatting as a development tool; online data and splat rendering are not runtime dependencies.
|
||||||
- **References and QA:** runtime UI art lives in `assets/images/ui/`; reference/art-direction sets live in `docs/screens/designs/` and `docs/screens/concepts/`; captures live in `docs/screenshots/`; realism runs live in `docs/screens/realism/`. `screen_designs.py compare/judge` and `tools/check_screenshots.sh` guard drift.
|
- **References and QA:** runtime UI art lives in `assets/images/ui/`; reference/art-direction sets live in `docs/screens/designs/` and `docs/screens/concepts/`; captures live in `docs/screenshots/`; realism runs live in `docs/screens/realism/`. `screen_designs.py compare/judge` and `tools/check_screenshots.sh` guard drift.
|
||||||
- **Storage convention:** see [ADR-0014](docs/adr/0014-runtime-image-root.md). Never overwrite an existing image; new outputs get a new path or `-vN` suffix.
|
- **Storage convention:** see [ADR-0014](docs/adr/0014-runtime-image-root.md). Never overwrite an existing image; new outputs get a new path or `-vN` suffix.
|
||||||
|
|
||||||
The active planning order — unversioned character and local-map development first, then the versioned 0.10.0 release-candidate hardening and later releases — is tracked in [`TODO.md`](TODO.md); this plan stays at the product/design level.
|
The active planning order — unversioned character and local-map development first, then the versioned 0.10.0 release-candidate hardening and later releases — is tracked in [`TODO.md`](TODO.md); this plan stays at the product/design level.
|
||||||
|
|
||||||
Primary decisions: [ADR-0007](docs/adr/0007-locomotion-and-input-layers.md), [ADR-0008](docs/adr/0008-cone-focus-and-head-ring.md), [ADR-0009](docs/adr/0009-radial-action-menu.md), [ADR-0010](docs/adr/0010-procedural-npc-presentation.md), [ADR-0011](docs/adr/0011-aim-free-selection-and-precision-walk.md), [ADR-0012](docs/adr/0012-surface-materials-and-generated-props.md), [ADR-0013](docs/adr/0013-blender-authored-npc-characters.md), [ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md).
|
Primary decisions: [ADR-0007](docs/adr/0007-locomotion-and-input-layers.md), [ADR-0008](docs/adr/0008-cone-focus-and-head-ring.md), [ADR-0009](docs/adr/0009-radial-action-menu.md), [ADR-0010](docs/adr/0010-procedural-npc-presentation.md), [ADR-0011](docs/adr/0011-aim-free-selection-and-precision-walk.md), [ADR-0012](docs/adr/0012-surface-materials-and-generated-props.md), [ADR-0013](docs/adr/0013-blender-authored-npc-characters.md), [ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md), [ADR-0017](docs/adr/0017-quaternius-npc-base.md).
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
@@ -194,7 +265,107 @@ Primary decisions: [ADR-0007](docs/adr/0007-locomotion-and-input-layers.md), [AD
|
|||||||
|
|
||||||
## Versioning and Delivery
|
## Versioning and Delivery
|
||||||
|
|
||||||
`config/version` in `project.godot` is the single version source (currently `0.10.0-rc.1`). Releases are tagged `v<version>`; the CI pipeline builds and publishes the standard artifacts plus the optional Desktop HQ zip. Version bumps follow [Semantic Versioning](https://semver.org/): every major, minor and patch increment needs a changelog entry, a local `tools/build_releases.sh` build, and artifact/checksum verification before tagging. Unversioned development tracks do not receive a version until their scope enters a release. See [`docs/RELEASING.md`](docs/RELEASING.md) and [`README.md`](README.md) for commands.
|
`config/version` in `project.godot` is the single version source (currently `0.9.8`). Releases are tagged `v<version>`; the CI pipeline builds and publishes the standard artifacts plus the optional Desktop HQ zip. Version bumps follow [Semantic Versioning](https://semver.org/): every major, minor and patch increment needs a changelog entry, a local `tools/build_releases.sh` build, and artifact/checksum verification before tagging. Unversioned development tracks do not receive a version until their scope enters a release. See [`docs/RELEASING.md`](docs/RELEASING.md) and [`README.md`](README.md) for commands.
|
||||||
|
|
||||||
|
## Gap Analysis
|
||||||
|
|
||||||
|
**Published baseline:** v0.9.8 (2026-11-04) — three playable venues, tutorial/campaign/checkpoint
|
||||||
|
resume, the access/radio/escalation loop, shift review, the 12-character Blender library, 277
|
||||||
|
Godot + 162 Python tests, a 21/21 screen-design judge and Compatibility/Web/Desktop HQ builds.
|
||||||
|
|
||||||
|
**Current source:** 0.9.8. The 0.10.0-rc.1 package built on 2026-11-03 is stale and is superseded
|
||||||
|
by the patch sequence below; it is not a published release.
|
||||||
|
|
||||||
|
| Area | Remaining gap | Milestone |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Character base | Quaternius arm-down skin export, rest-relative pose composition, face/silhouette acceptance | 0.9.9 |
|
||||||
|
| Character roster | 12-role mapping, modular outfits, gear fit, AO/texels, budget evidence | 0.9.10 |
|
||||||
|
| Venue and asset polish | Ranked prop/foliage/lighting/set-dressing fixes, collider verification | 0.9.11 |
|
||||||
|
| Performance and Web | Frame/memory/load evidence after the content changes, LOD/impostor verification | 0.9.12 |
|
||||||
|
| Onboarding and balance | External first-time-player evidence, balance sweeps/tuning, content lock | 0.10.0 |
|
||||||
|
| Accessibility and localization | Acceptance pass, caption coverage, pseudo-locale/length checks | 0.9.12 / 0.10.0 |
|
||||||
|
| QA and release engineering | Full smoke matrix, clean build/checksum verification, target-matrix launches | 0.10.0 |
|
||||||
|
| Legal, signing, storefront | macOS signing/notarization, AI-provider rights, storefront/rating approval, privacy sign-off | 0.10.0 / 0.12.0 |
|
||||||
|
| Systems and content depth | Live incident replay, fourth venue, localization foundation | 0.11.0 |
|
||||||
|
| Distribution | Signed storefront builds, frozen save contract, launch languages, hardware matrix | 0.12.0 |
|
||||||
|
| Stable release | Content complete, accessibility audit, release candidates, 1.0.0 decision | 0.13.0 / 1.0.0 |
|
||||||
|
| Future | More venues/seasonal content, image-to-mesh props, other deferred ideas | Unscheduled |
|
||||||
|
|
||||||
|
The 0.9.3 UI, 0.9.4 animation, 0.9.5 texture, 0.9.6 integration, 0.9.7 character development and
|
||||||
|
0.9.8 deformation fixes are complete in [`DONE.md`](DONE.md); they are not remaining gaps. The
|
||||||
|
2026-11-01 documentation audit is also closed.
|
||||||
|
|
||||||
|
## Release Roadmap
|
||||||
|
|
||||||
|
Every version below follows the release procedure in `AGENTS.md`. Patch versions contain fixes and
|
||||||
|
polish; minor versions add user-facing features; the major version is the stable release with a
|
||||||
|
compatibility promise. Each version's executable checklist is in [`TODO.md`](TODO.md).
|
||||||
|
|
||||||
|
### Roadmap to 0.10.0
|
||||||
|
|
||||||
|
The goal of this line is to finish the character and venue work and clear every public-release
|
||||||
|
gate. The character track is versioned rather than unversioned so a blocked rig cannot hold the
|
||||||
|
whole release.
|
||||||
|
|
||||||
|
| Version | Type | Scope | Exit criterion |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 0.9.9 | patch | Quaternius pilot: correct skin export, rest-relative pose math, Idle/Walk verification, owner face/silhouette gate, one-role (or crowd-only) rollout | A Quaternius NPC renders and animates correctly and ships behind a tested procedural fallback |
|
||||||
|
| 0.9.10 | patch | 12-role roster, modular outfits, five slots, AO/texels, gear fit, 12×6 baseline | The full roster passes contract, gear, budget and owner-baseline gates |
|
||||||
|
| 0.9.11 | patch | Scheduled 3D review fixes: props/foliage, gear silhouette, venue shape language, lighting, set dressing, colliders | Every scheduled P1/P2 item is fixed or accepted with a measurement |
|
||||||
|
| 0.9.12 | patch | Performance/Web/LOD/memory verification plus accessibility and pseudo-locale checks | No regression against the pre-character baselines |
|
||||||
|
| 0.10.0 | minor | Public readiness: external playtests, balance acceptance, legal/storefront/signing decisions, platform matrix, docs and picture lock | 0.10.0 is content-locked, externally played, legally reviewed, documented and reproducibly built |
|
||||||
|
|
||||||
|
### Roadmap to 0.11.0
|
||||||
|
|
||||||
|
This line turns a stable 0.10.x into a deeper game: the incident timeline becomes a live replay, a
|
||||||
|
fourth venue enters the campaign, and the text catalogue becomes a real localization pipeline.
|
||||||
|
|
||||||
|
| Version | Type | Scope | Exit criterion |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 0.10.1 | patch | Onboarding and accessibility issues deferred from the 0.10.0 playtests | First-hour and accessibility defects closed |
|
||||||
|
| 0.10.2 | patch | Balance/fairness sweeps, review-text and difficulty-progression tuning | Every balance-acceptance cell is in target or explicitly accepted |
|
||||||
|
| 0.10.3 | patch | Save/checkpoint edge cases, gamepad/remap, pause/resume, small quality-of-life fixes | No known P1 stability defect remains |
|
||||||
|
| 0.11.0 | minor | Live body-camera replay, fourth venue (candidate: TCL Chinese Theatre forecourt), localization foundation with one secondary language | The new systems ship without regressing performance, accessibility or balance |
|
||||||
|
|
||||||
|
### Roadmap to 1.0.0
|
||||||
|
|
||||||
|
This line is about distribution, contracts and completeness: signed builds on the launch stores, a
|
||||||
|
frozen save format, finished content and release candidates.
|
||||||
|
|
||||||
|
| Version | Type | Scope | Exit criterion |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 0.11.1 | patch | Replay/venue/localization fixes from wider play | No P1 defect in the 0.11.0 feature set |
|
||||||
|
| 0.11.2 | patch | New-venue balance, campaign pacing and content readability | The 0.11.x line is stable enough to freeze its contracts |
|
||||||
|
| 0.12.0 | minor | macOS signing/notarization, storefront packages/metadata/ratings, save-format v2 freeze, hardware matrix, launch languages | Signed, storefront-ready builds exist on every launch platform |
|
||||||
|
| 0.12.1 | patch | Signing, installer, metadata and save-migration fixes from storefront dry runs | Every storefront build installs, launches and migrates cleanly |
|
||||||
|
| 0.13.0 | minor | Content complete: all planned venues/events/dialogue, audio decision, final tutorial, accessibility audit, balance freeze | All planned content is implemented and frozen |
|
||||||
|
| 0.13.1 | patch | Release candidate 1: full smoke/performance/save/accessibility matrix | A candidate passes every gate except final approval |
|
||||||
|
| 0.13.2 | patch | Release candidate 2: verify fixes, freeze release notes/credits/privacy/known limitations | The final candidate is reproducible and documented |
|
||||||
|
| 1.0.0 | major | Stable release: legal/storefront/rating approval, hardware/accessibility/localization sign-off, no P0/P1, publish on every platform | 1.0.0 is published with verified checksums and no open blocker |
|
||||||
|
|
||||||
|
## Roadmap Dependencies and Risks
|
||||||
|
|
||||||
|
- Content volume is the largest schedule risk; tutorial and campaign work must not outrun the
|
||||||
|
event/dialogue matrix.
|
||||||
|
- The vision judge is a dev-time aid with an external key and budget; CI must never make it a hard
|
||||||
|
gate.
|
||||||
|
- Godot engine changes can alter lighting and imports; do not combine an engine baseline move with
|
||||||
|
a content or feature milestone.
|
||||||
|
- Signed macOS builds, AI provenance and storefront approval are release blockers, not polish.
|
||||||
|
- The character pipeline is the current critical path. The rig/export fix must be proven on one
|
||||||
|
variant before the roster rollout; otherwise 0.9.10 multiplies a defect.
|
||||||
|
- Photo-derived map assets add a provenance risk: downloaded or user-supplied photos can carry
|
||||||
|
unclear licences, identifiable people, plates and property restrictions. No photo- or
|
||||||
|
splat-derived asset ships without a completed sidecar, consent/licence review and redaction.
|
||||||
|
- Gaussian-splat training is GPU/disk-heavy and the toolchain churns; keep it offline,
|
||||||
|
version-pinned and outside CI, and never make a runtime splat renderer a release dependency.
|
||||||
|
- The Blender skill pack is local, unversioned guidance; it can drift from the project contract or
|
||||||
|
assume Blender MCP. Re-verify every adopted technique with `inspect`/`audit`/`fit` and never make
|
||||||
|
a skill, asset pack or tool version a build or CI dependency.
|
||||||
|
- The 3D re-evaluation can expand without bound. Cap it with the scorecard ranking: P0/P1 only, one
|
||||||
|
asset family per commit, and an explicit accept for everything else.
|
||||||
|
- Keep the de-escalation-first rules and rare-emergency design unchanged while adding progression,
|
||||||
|
difficulty and feedback.
|
||||||
|
|
||||||
## Design Direction
|
## Design Direction
|
||||||
|
|
||||||
@@ -206,4 +377,5 @@ The game should feel like a tense workplace simulation with occasional crisis mo
|
|||||||
|
|
||||||
- A rare Bob Dobbs cameo (Easter-egg appearance; placement note in `MEMORY.md`).
|
- A rare Bob Dobbs cameo (Easter-egg appearance; placement note in `MEMORY.md`).
|
||||||
- Specific unusual NPC behaviors, such as slapping their own face, using hands as scissors, or interacting with props unexpectedly.
|
- Specific unusual NPC behaviors, such as slapping their own face, using hands as scissors, or interacting with props unexpectedly.
|
||||||
- Live 3D body-camera replay; additional venues or seasonal events; real voice/dialogue audio; an image-to-mesh prop pipeline.
|
- A fifth venue or seasonal events after the 0.11.0 fourth venue proves the venue pipeline.
|
||||||
|
- Real voice/dialogue audio if the 0.13.0 audio decision does not fund it; an image-to-mesh prop pipeline remains unscheduled.
|
||||||
@@ -8,29 +8,15 @@ The central design goal is simple:
|
|||||||
|
|
||||||
## Development Priorities
|
## Development Priorities
|
||||||
|
|
||||||
The active planning order lives in [`TODO.md`](TODO.md) and [`AGENTS.md`](AGENTS.md):
|
The active planning order lives in [`PLAN.md`](PLAN.md) (gap analysis and release roadmap), [`TODO.md`](TODO.md) (executable checklist) and [`AGENTS.md`](AGENTS.md):
|
||||||
|
|
||||||
1. **Character development** — crowd, age, build, hair and outfit variety on the owned procedural library, keeping the frozen NPC contract. Source evaluation and the licence decision are in [`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md).
|
1. **`0.9.9`–`0.9.12` patches** — finish the Quaternius NPC base on the accepted 62-joint runtime, roll it out to the 12 roles, close the scheduled venue/prop/lighting polish, then verify performance, Web and accessibility.
|
||||||
2. **Local map development** — licensed OpenStreetMap/3D data, reference-derived photoreal facades, AI-assisted composition and a Gaussian-splatting development evaluation.
|
2. **`0.10.0` minor** — public readiness: external playtests, balance acceptance, legal/storefront/signing decisions, the platform matrix and the documentation/picture lock.
|
||||||
3. **Versioned release hardening** — the 0.10.0 release candidate and later semantic versions follow the unversioned development tracks.
|
3. **`0.10.x` → `0.11.0`** — live incident replay, a fourth venue and the localization foundation.
|
||||||
|
4. **`0.12.0` → `1.0.0`** — signed storefront builds, a frozen save contract, content complete, release candidates and the stable release.
|
||||||
|
|
||||||
Versions are assigned only when a track's scope is accepted into a release. Every major, minor and patch version requires a changelog entry, a local release build and artifact verification before tagging.
|
Versions are assigned only when a track's scope is accepted into a release. Every major, minor and patch version requires a changelog entry, a local release build and artifact verification before tagging.
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
| | |
|
|
||||||
| --- | --- |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  | |
|
|
||||||
|
|
||||||
*Screenshots are generated by [`src/test/capture_screenshots.gd`](src/test/capture_screenshots.gd) and [`src/test/capture_prop_gallery.gd`](src/test/capture_prop_gallery.gd) from the current build — day, late-night, wand screening, escalation, shift-review timeline,
|
|
||||||
precision walk, the Joke Shop's Sunset Strip frontage, the TV Studio arrival, and the prop lineup.*
|
|
||||||
|
|
||||||
## ⬇️ Downloads
|
## ⬇️ Downloads
|
||||||
|
|
||||||
| Platform | Package |
|
| Platform | Package |
|
||||||
@@ -48,14 +34,17 @@ precision walk, the Joke Shop's Sunset Strip frontage, the TV Studio arrival, an
|
|||||||
> Downloads are public (no sign-in required) since the host's instance-wide
|
> Downloads are public (no sign-in required) since the host's instance-wide
|
||||||
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
||||||
>
|
>
|
||||||
> **Latest published: v0.9.6** — the integration checkpoint: the 0.9.5 texture
|
> **Latest published: v0.9.9** — the character-roster patch: all twelve NPCs now
|
||||||
> and material refinement (256² NPC maps, a four-times fabric weave without baked
|
> use the CC0 Quaternius base with the five tint slots, role gear and baked maps.
|
||||||
> AO blotches, repaired Walk of Fame/awning surfaces and per-phase skyline haze)
|
> Superseded: v0.9.8, the character-development patch: the
|
||||||
> is packaged with the v0.9.4 NPC visual optimization, the Hollywood/Joke Shop
|
> 12-character library, realistic face pass, four crowd-variety variants and the
|
||||||
> maps and the v0.9.3 UI/accessibility polish, with the full gallery, smoke matrix
|
> wrist/hip/knee deformation fixes. Previous 0.9.x patches added the
|
||||||
> and Web/Desktop HQ build re-verified.
|
> Hollywood/Joke Shop maps, UI/accessibility polish, NPC visual optimization,
|
||||||
|
> texture/material refinement and integration checkpoint.
|
||||||
>
|
>
|
||||||
> **Current source: v0.10.0-rc.1** — the release-candidate source; the final tag is pending external playtest and legal review.
|
> **Current source: v0.9.8** — tagged from the same commit. The 0.10.0
|
||||||
|
> development track remains gated on the Quaternius NPC rollout, local-map work
|
||||||
|
> and release approvals.
|
||||||
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
||||||
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
||||||
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
||||||
@@ -70,6 +59,61 @@ If you are here to develop or test locally instead of downloading, jump to
|
|||||||
|
|
||||||
The project is under active development. The design and architecture are documented, and a playable prototype vertical slice exists (Godot 4 + GDScript): a stylized El Capitan Entertainment Centre block, a first-person guard, simulated NPCs with intents and reactions, the six-level escalation model, access inspection and admission decisions, radio calls with credibility checks, reputation, crowd mood, and an end-of-shift review.
|
The project is under active development. The design and architecture are documented, and a playable prototype vertical slice exists (Godot 4 + GDScript): a stylized El Capitan Entertainment Centre block, a first-person guard, simulated NPCs with intents and reactions, the six-level escalation model, access inspection and admission decisions, radio calls with credibility checks, reputation, crowd mood, and an end-of-shift review.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
Every capture is rendered from the current build by [`tools/check_screenshots.sh`](tools/check_screenshots.sh)
|
||||||
|
(21 captures, compared against the runtime art). The full gallery, including every UI state, is in
|
||||||
|
[`docs/screenshots/`](docs/screenshots/README.md).
|
||||||
|
|
||||||
|
### Venues
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Hollywood Boulevard — day.** The El Capitan Entertainment Centre block: six Ionic columns, Masonic parapet, event banners, eight stone steps and the Walk of Fame terrazzo. |
|
||||||
|
|  | **Hollywood Boulevard — late.** Sconces pool on the limestone, street lamps light the boulevard and the skyline fades into night haze. |
|
||||||
|
|  | **Night patrol.** A tight flashlight spot picks out the door and the person in front of it; movement and awareness change at night. |
|
||||||
|
|  | **The Joke Shop.** A black Sunset Strip club under its signature-covered canopy, circular blade sign, red curtains and the dry hillside behind. |
|
||||||
|
|  | **TV Studio arrival.** Stone facade, bulb canopy, black car and crowd barriers for a late celebrity arrival. |
|
||||||
|
|
||||||
|
### Interaction and systems
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Focus and targeting.** The cone focus ring, name tag and target chip; `Tab`/wheel cycles nearby people without aiming. |
|
||||||
|
|  | **Access inspection.** A venue-correct wristband check with evidence shown before the admit/refuse decision. |
|
||||||
|
|  | **Radio report.** Security assistance with location, behaviour and what was actually seen. |
|
||||||
|
|  | **Wand screening.** A false alarm on keys: ask the guest to identify the item before choosing a response. |
|
||||||
|
|  | **Escalation.** Level 3 blocking with the shape meter, body camera armed and the reaction log. |
|
||||||
|
|  | **Body-camera timeline.** Every recorded event, step by step, in the shift review. |
|
||||||
|
|  | **Precision walk.** Half-speed close work on the steps, with the HUD state chip visible. |
|
||||||
|
|
||||||
|
### Shell and UI
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Venue menu.** Day and night rows for Hollywood, the Joke Shop and the TV Studio, campaign continue and review links. |
|
||||||
|
|  | **Settings.** Difficulty, volumes, HUD scale, reduce motion, captions and key/button remapping, all persisted. |
|
||||||
|
|  | **Shift review.** Grade, policy outcome, incidents, responder results and campaign Continue. |
|
||||||
|
|  | **Action wheel.** De-escalation first: stop, look, point, no, ask, inspect, warn, block, reason, recognize, radio. |
|
||||||
|
|
||||||
|
### 3D asset progress
|
||||||
|
|
||||||
|
The character roster is built from the CC0 Quaternius bases (ADR-0017/ADR-0018) and audited
|
||||||
|
before use; the procedural library and the MakeHuman head stay as the tested fallback. The
|
||||||
|
sheets below show the
|
||||||
|
current library and will be replaced after the 12 × 6 baseline review.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Character library.** Twelve role-dressed variants (4.5–6.4k triangles each) on three CC0 Quaternius bases, with five tint slots, attach-point gear and baked maps. |
|
||||||
|
|  | **Street and venue props.** Twenty library props with colliders, navigation avoidance and colour atlases. |
|
||||||
|
|  | **Neutral baseline.** 8 characters × 6 views (front, three-quarter, profile, back, full body, crowd) used for the 0.9.4 pass and the current re-evaluation. |
|
||||||
|
|  | **Texture refinement.** 128 → 256 px NPC maps and a 4× fabric weave, shown before/after on the same camera. |
|
||||||
|
|  | **Crowd variety prototypes.** Four approved looks (bun/slim, bald/broad, coat/umbrella, hood/cup) queued for the roster build. |
|
||||||
|
|
||||||
|
More sheets and the review criteria are in [`docs/blender_previews/`](docs/blender_previews/README.md)
|
||||||
|
and [`docs/3d-model-review.md`](docs/3d-model-review.md).
|
||||||
|
|
||||||
## Game Overview
|
## Game Overview
|
||||||
|
|
||||||
A typical shift lasts 10–15 minutes: watch the entrance and sidewalk, judge why
|
A typical shift lasts 10–15 minutes: watch the entrance and sidewalk, judge why
|
||||||
@@ -92,7 +136,7 @@ decisions are recorded as ADRs in [`docs/adr/`](docs/adr/).
|
|||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
- **Engine:** Godot 4.7.2 · **Language:** GDScript
|
- **Engine:** Godot 4.7.2 · **Language:** GDScript
|
||||||
- **NPCs:** Blender-authored skinned library (`assets/models/blender/`) with procedural fallbacks; lightweight FSMs on `NpcProfile`
|
- **NPCs:** CC0 Quaternius skinned library (`assets/models/blender/`, ADR-0018) with a tested procedural fallback; lightweight FSMs on `NpcProfile`
|
||||||
- **World:** procedural `WorldBuilder` + generated prop models
|
- **World:** procedural `WorldBuilder` + generated prop models
|
||||||
- **Art:** v2 skyline/surface textures in `assets/textures/ai/game_ready/v2/picks/`; UI/scene art in `assets/images/`
|
- **Art:** v2 skyline/surface textures in `assets/textures/ai/game_ready/v2/picks/`; UI/scene art in `assets/images/`
|
||||||
- **Asset tooling:** `asset_pipeline.py`, `model_pipeline.py`, `npc_pipeline.py`, `blender_assets.py`, `scene_concepts.py`, `screen_designs.py`, `check_screenshots.sh`
|
- **Asset tooling:** `asset_pipeline.py`, `model_pipeline.py`, `npc_pipeline.py`, `blender_assets.py`, `scene_concepts.py`, `screen_designs.py`, `check_screenshots.sh`
|
||||||
@@ -121,43 +165,42 @@ Full setup, every run/test/smoke command, and the controls table are in
|
|||||||
- [`TODO.md`](TODO.md) — Priority-ordered tasks: character and local-map development, release milestones, and future work.
|
- [`TODO.md`](TODO.md) — Priority-ordered tasks: character and local-map development, release milestones, and future work.
|
||||||
- [`docs/README.md`](docs/README.md) — Documentation index, current priorities and visual previews.
|
- [`docs/README.md`](docs/README.md) — Documentation index, current priorities and visual previews.
|
||||||
- [`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md) — Character-source evaluation, licence rules and the character-development plan.
|
- [`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md) — Character-source evaluation, licence rules and the character-development plan.
|
||||||
|
- [`docs/makehuman-head-review.md`](docs/makehuman-head-review.md) — Local MakeHuman head prototype, provenance and review verdict.
|
||||||
- [`AGENTS.md`](AGENTS.md) — Development, commit, build and collaboration workflow.
|
- [`AGENTS.md`](AGENTS.md) — Development, commit, build and collaboration workflow.
|
||||||
- [`DONE.md`](DONE.md) — Completed task sections.
|
- [`DONE.md`](DONE.md) — Completed task sections.
|
||||||
- [`MEMORY.md`](MEMORY.md) — Persistent session notes.
|
- [`MEMORY.md`](MEMORY.md) — Persistent session notes.
|
||||||
- [`CHANGELOG.md`](CHANGELOG.md) — User-facing changes by version.
|
- [`CHANGELOG.md`](CHANGELOG.md) — User-facing changes by version.
|
||||||
- [`docs/architecture.md`](docs/architecture.md) — arc42-style architecture documentation.
|
- [`docs/architecture.md`](docs/architecture.md) — arc42-style architecture documentation.
|
||||||
- [`docs/gameplay.md`](docs/gameplay.md) — Gameplay systems, venues, and the escalation model.
|
- [`docs/gameplay.md`](docs/gameplay.md) — Gameplay systems, venues, and the escalation model.
|
||||||
- [`docs/hollywood-boulevard-map.md`](docs/hollywood-boulevard-map.md) — Real-site references, game mapping and limitations for the Hollywood venue.
|
- [`docs/venues.md`](docs/venues.md) — Real-site references, game mapping and limitations for Hollywood, the Joke Shop and the TV Studio.
|
||||||
- [`docs/joke-shop-map.md`](docs/joke-shop-map.md) — Real-site references, game mapping and limitations for the Joke Shop / Sunset Strip venue.
|
|
||||||
- [`docs/tutorial.md`](docs/tutorial.md) — First-shift tutorial beats and rules.
|
|
||||||
- [`docs/asset-tooling.md`](docs/asset-tooling.md) — Asset pipelines, model tools, and screenshot gates.
|
- [`docs/asset-tooling.md`](docs/asset-tooling.md) — Asset pipelines, model tools, and screenshot gates.
|
||||||
|
- [`tools/README.md`](tools/README.md) — Tool inventory, categories, test commands and AI-skill evaluation.
|
||||||
|
- [`.agents/skills/gig-line-blender-asset-contract`](.agents/skills/gig-line-blender-asset-contract/SKILL.md) — tracked AI skill for Blender/NPC/gear asset work.
|
||||||
|
- [`.agents/skills/gig-line-release-qa`](.agents/skills/gig-line-release-qa/SKILL.md) — tracked AI skill for release and visual-QA work.
|
||||||
- [`docs/getting-started.md`](docs/getting-started.md) — Setup, run/test commands, and controls.
|
- [`docs/getting-started.md`](docs/getting-started.md) — Setup, run/test commands, and controls.
|
||||||
- [`docs/development-status.md`](docs/development-status.md) — Implemented features and what's next.
|
- [`docs/development-status.md`](docs/development-status.md) — Implemented features and what's next.
|
||||||
- [`docs/accessibility.md`](docs/accessibility.md) — Accessibility features, known gaps and statement.
|
- [`docs/accessibility.md`](docs/accessibility.md) — Accessibility features, known gaps and statement.
|
||||||
- [`docs/known-limitations.md`](docs/known-limitations.md) — Release-candidate blockers and accepted limitations.
|
- [`docs/known-limitations.md`](docs/known-limitations.md) — Release-candidate blockers and accepted limitations.
|
||||||
- [`docs/smoke-checklist.md`](docs/smoke-checklist.md) — Manual smoke-test checklist.
|
- [`docs/release-qa.md`](docs/release-qa.md) — Manual smoke checklist and release-candidate balance protocol.
|
||||||
- [`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/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/adr/`](docs/adr/) — Architecture Decision Records.
|
- [`docs/adr/`](docs/adr/) — Architecture Decision Records.
|
||||||
|
|
||||||
## Development Status
|
## Development Status
|
||||||
|
|
||||||
v0.10.0-rc.1 is the current release-candidate source: the 0.9.6 integration
|
v0.9.8 is the current source version: the character-development patch with the
|
||||||
checkpoint plus save-migration hardening and release tooling, on top of the 0.9.5
|
12-character library, realistic face and crowd variants, pose/deformation fixes
|
||||||
texture refinement (256² NPC maps, a 4× fabric weave with baked-AO removal, repaired
|
and save-migration/release hardening. It sits on the 0.9.6 integration
|
||||||
Walk of Fame/awning surfaces and per-phase skyline haze), on top of the v0.9.4
|
checkpoint, 0.9.5 texture refinement, 0.9.4 NPC visual optimization, 0.9.3
|
||||||
NPC visual optimization and the v0.9.3 UI/accessibility polish pass. It builds on
|
UI/accessibility polish, 0.9.2 Hollywood/Joke Shop maps and the 0.9.x engine
|
||||||
v0.9.2's Hollywood/Joke Shop map rebuilds, v0.9.1's Godot 4.7.2 baseline,
|
baseline. The active work is the `0.9.9`–`0.9.12` patch line (Quaternius NPC
|
||||||
v0.9.0's production-quality pass, v0.8's
|
base, roster, venue/asset polish, performance) followed by the `0.10.0`
|
||||||
tutorial/campaign/resume work and the v0.7.x venue/gear/judge work. The active
|
public-readiness minor. See
|
||||||
planning priorities are the unversioned character and local-map development tracks;
|
|
||||||
the 0.10.0 release candidate is the next versioned release. See
|
|
||||||
[`docs/development-status.md`](docs/development-status.md) for the implemented list
|
[`docs/development-status.md`](docs/development-status.md) for the implemented list
|
||||||
and [`TODO.md`](TODO.md) for the priority-ordered roadmap. Manual verification uses
|
and [`TODO.md`](TODO.md) for the priority-ordered roadmap. Manual verification uses
|
||||||
the [`docs/smoke-checklist.md`](docs/smoke-checklist.md).
|
[`docs/release-qa.md`](docs/release-qa.md).
|
||||||
|
|
||||||
## Credits, Privacy and Accessibility
|
## Credits, Privacy and Accessibility
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,12 @@
|
|||||||
"attach": "back",
|
"attach": "back",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.161,
|
0.15,
|
||||||
0.296,
|
0.296,
|
||||||
0.212
|
0.172
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.161,
|
-0.15,
|
||||||
-0.12,
|
-0.12,
|
||||||
-0.319
|
-0.319
|
||||||
]
|
]
|
||||||
@@ -46,29 +46,7 @@
|
|||||||
],
|
],
|
||||||
"path": "assets/models/blender/npc_gear/backpack.glb",
|
"path": "assets/models/blender/npc_gear/backpack.glb",
|
||||||
"triangles": 380,
|
"triangles": 380,
|
||||||
"vertices": 408
|
"vertices": 410
|
||||||
},
|
|
||||||
"beanie": {
|
|
||||||
"attach": "head",
|
|
||||||
"bounds_godot": {
|
|
||||||
"max": [
|
|
||||||
0.193,
|
|
||||||
0.215,
|
|
||||||
0.183
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
-0.193,
|
|
||||||
0.014,
|
|
||||||
-0.203
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"category": "npc_gear",
|
|
||||||
"materials": [
|
|
||||||
"plastic_grey"
|
|
||||||
],
|
|
||||||
"path": "assets/models/blender/npc_gear/beanie.glb",
|
|
||||||
"triangles": 460,
|
|
||||||
"vertices": 403
|
|
||||||
},
|
},
|
||||||
"bench": {
|
"bench": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
@@ -96,14 +74,14 @@
|
|||||||
"bollard": {
|
"bollard": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.105,
|
0.125,
|
||||||
0.975,
|
0.975,
|
||||||
0.105
|
0.125
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.105,
|
-0.125,
|
||||||
0,
|
0,
|
||||||
-0.105
|
-0.125
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "props",
|
"category": "props",
|
||||||
@@ -114,20 +92,20 @@
|
|||||||
"path": "assets/models/blender/props/bollard.glb",
|
"path": "assets/models/blender/props/bollard.glb",
|
||||||
"replaces": "generated/bollard.glb",
|
"replaces": "generated/bollard.glb",
|
||||||
"triangles": 138,
|
"triangles": 138,
|
||||||
"vertices": 225
|
"vertices": 221
|
||||||
},
|
},
|
||||||
"camera": {
|
"camera": {
|
||||||
"attach": "hand",
|
"attach": "hand",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.106,
|
0.065,
|
||||||
0.376,
|
0.06,
|
||||||
0.143
|
0.143
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.106,
|
-0.065,
|
||||||
-0.053,
|
-0.053,
|
||||||
-0.055
|
0.005
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
@@ -136,21 +114,21 @@
|
|||||||
"lens"
|
"lens"
|
||||||
],
|
],
|
||||||
"path": "assets/models/blender/npc_gear/camera.glb",
|
"path": "assets/models/blender/npc_gear/camera.glb",
|
||||||
"triangles": 318,
|
"triangles": 312,
|
||||||
"vertices": 406
|
"vertices": 422
|
||||||
},
|
},
|
||||||
"cap": {
|
"cap": {
|
||||||
"attach": "head",
|
"attach": "head",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.168,
|
0.108,
|
||||||
0.209,
|
0.204,
|
||||||
0.258
|
0.165
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.168,
|
-0.108,
|
||||||
0.037,
|
0.094,
|
||||||
-0.186
|
-0.119
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
@@ -262,14 +240,14 @@
|
|||||||
"fire_hydrant": {
|
"fire_hydrant": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.24,
|
0.265,
|
||||||
0.76,
|
0.815,
|
||||||
0.19
|
0.235
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.24,
|
-0.265,
|
||||||
0,
|
0,
|
||||||
-0.19
|
-0.235
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "props",
|
"category": "props",
|
||||||
@@ -279,21 +257,21 @@
|
|||||||
],
|
],
|
||||||
"path": "assets/models/blender/props/fire_hydrant.glb",
|
"path": "assets/models/blender/props/fire_hydrant.glb",
|
||||||
"replaces": "generated/fire_hydrant.glb",
|
"replaces": "generated/fire_hydrant.glb",
|
||||||
"triangles": 466,
|
"triangles": 610,
|
||||||
"vertices": 725
|
"vertices": 902
|
||||||
},
|
},
|
||||||
"firefighter_helmet": {
|
"firefighter_helmet": {
|
||||||
"attach": "head",
|
"attach": "head",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.26,
|
0.166,
|
||||||
0.245,
|
0.227,
|
||||||
0.26
|
0.166
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.26,
|
-0.166,
|
||||||
0.035,
|
0.092,
|
||||||
-0.351
|
-0.225
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
@@ -355,14 +333,14 @@
|
|||||||
"attach": "head",
|
"attach": "head",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.184,
|
0.118,
|
||||||
0.236,
|
0.221,
|
||||||
0.123
|
0.079
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.184,
|
-0.118,
|
||||||
-0.146,
|
-0.024,
|
||||||
-0.225
|
-0.144
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
@@ -439,26 +417,28 @@
|
|||||||
"category": "props",
|
"category": "props",
|
||||||
"materials": [
|
"materials": [
|
||||||
"news_blue",
|
"news_blue",
|
||||||
|
"dark_metal",
|
||||||
"glass",
|
"glass",
|
||||||
"paper",
|
"paper",
|
||||||
"steel"
|
"steel"
|
||||||
],
|
],
|
||||||
"path": "assets/models/blender/props/newspaper_box.glb",
|
"path": "assets/models/blender/props/newspaper_box.glb",
|
||||||
"replaces": "generated/newspaper_box.glb",
|
"replaces": "generated/newspaper_box.glb",
|
||||||
"triangles": 244,
|
"triangles": 300,
|
||||||
"vertices": 528
|
"vertices": 648
|
||||||
},
|
},
|
||||||
"npc_casual": {
|
"npc_casual": {
|
||||||
|
"base": "hoodie",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.321,
|
0.228,
|
||||||
1.824,
|
1.866,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.321,
|
-0.228,
|
||||||
0,
|
-0.004,
|
||||||
-0.178
|
-0.091
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -466,11 +446,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"Eye",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_casual.glb",
|
"path": "assets/models/blender/npc/npc_casual.glb",
|
||||||
@@ -487,20 +467,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4482,
|
"triangles": 6206,
|
||||||
"vertices": 4260
|
"vertices": 12988
|
||||||
},
|
},
|
||||||
"npc_coat": {
|
"npc_coat": {
|
||||||
|
"base": "hoodie",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.332,
|
0.228,
|
||||||
1.824,
|
1.866,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.332,
|
-0.228,
|
||||||
0,
|
-0.004,
|
||||||
-0.18
|
-0.091
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -508,11 +489,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"Eye",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_coat.glb",
|
"path": "assets/models/blender/npc/npc_coat.glb",
|
||||||
@@ -528,20 +509,185 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5474,
|
"triangles": 6206,
|
||||||
"vertices": 5264
|
"vertices": 12988
|
||||||
},
|
},
|
||||||
"npc_dress": {
|
"npc_crowd_a": {
|
||||||
|
"base": "casual",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.288,
|
0.18,
|
||||||
1.825,
|
1.843,
|
||||||
0.208
|
0.271
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.288,
|
-0.18,
|
||||||
0,
|
-0.009,
|
||||||
-0.208
|
-0.099
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "none",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"accessory",
|
||||||
|
"Hair_Brown",
|
||||||
|
"Brown",
|
||||||
|
"hair",
|
||||||
|
"pants"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_a.glb",
|
||||||
|
"roles": [
|
||||||
|
"tourist",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 6424,
|
||||||
|
"vertices": 13440
|
||||||
|
},
|
||||||
|
"npc_crowd_b": {
|
||||||
|
"base": "casual",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.18,
|
||||||
|
1.843,
|
||||||
|
0.271
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.18,
|
||||||
|
-0.009,
|
||||||
|
-0.099
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "none",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"accessory",
|
||||||
|
"Hair_Brown",
|
||||||
|
"Brown",
|
||||||
|
"hair",
|
||||||
|
"pants"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_b.glb",
|
||||||
|
"roles": [
|
||||||
|
"confused_visitor",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 6424,
|
||||||
|
"vertices": 13440
|
||||||
|
},
|
||||||
|
"npc_crowd_c": {
|
||||||
|
"base": "hoodie",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.228,
|
||||||
|
1.866,
|
||||||
|
0.287
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.228,
|
||||||
|
-0.004,
|
||||||
|
-0.091
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "coat",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"accessory",
|
||||||
|
"Eyebrows",
|
||||||
|
"Eye",
|
||||||
|
"hair",
|
||||||
|
"pants"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_c.glb",
|
||||||
|
"roles": [
|
||||||
|
"intoxicated",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 6206,
|
||||||
|
"vertices": 12988
|
||||||
|
},
|
||||||
|
"npc_crowd_d": {
|
||||||
|
"base": "worker",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.217,
|
||||||
|
1.826,
|
||||||
|
0.287
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.217,
|
||||||
|
-0.004,
|
||||||
|
-0.065
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "hood",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"accessory",
|
||||||
|
"hair",
|
||||||
|
"Moustache",
|
||||||
|
"Eye",
|
||||||
|
"pants"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_d.glb",
|
||||||
|
"roles": [
|
||||||
|
"tourist",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 4458,
|
||||||
|
"vertices": 9370
|
||||||
|
},
|
||||||
|
"npc_dress": {
|
||||||
|
"base": "casual",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.18,
|
||||||
|
1.843,
|
||||||
|
0.271
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.18,
|
||||||
|
-0.009,
|
||||||
|
-0.099
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -549,11 +695,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Hair_Brown",
|
||||||
|
"Brown",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_dress.glb",
|
"path": "assets/models/blender/npc/npc_dress.glb",
|
||||||
@@ -567,20 +713,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4900,
|
"triangles": 6424,
|
||||||
"vertices": 4719
|
"vertices": 13440
|
||||||
},
|
},
|
||||||
"npc_firefighter": {
|
"npc_firefighter": {
|
||||||
|
"base": "worker",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.37,
|
0.217,
|
||||||
1.819,
|
1.826,
|
||||||
0.213
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.37,
|
-0.217,
|
||||||
0,
|
-0.004,
|
||||||
-0.213
|
-0.065
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -588,13 +735,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
|
||||||
"hair",
|
"hair",
|
||||||
"hivis",
|
"Moustache",
|
||||||
"reflective",
|
"Eye",
|
||||||
"rubber"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_firefighter.glb",
|
"path": "assets/models/blender/npc/npc_firefighter.glb",
|
||||||
@@ -608,20 +753,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5344,
|
"triangles": 4458,
|
||||||
"vertices": 5494
|
"vertices": 9370
|
||||||
},
|
},
|
||||||
"npc_guard": {
|
"npc_guard": {
|
||||||
|
"base": "worker",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.361,
|
0.217,
|
||||||
1.819,
|
1.826,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.361,
|
-0.217,
|
||||||
0,
|
-0.004,
|
||||||
-0.172
|
-0.065
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -629,15 +775,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
|
||||||
"hair",
|
"hair",
|
||||||
"rubber",
|
"Moustache",
|
||||||
"leather",
|
"Eye",
|
||||||
"steel",
|
"pants"
|
||||||
"patch",
|
|
||||||
"plastic_black"
|
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_guard.glb",
|
"path": "assets/models/blender/npc/npc_guard.glb",
|
||||||
@@ -652,20 +794,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5046,
|
"triangles": 4458,
|
||||||
"vertices": 5096
|
"vertices": 9370
|
||||||
},
|
},
|
||||||
"npc_hoodie": {
|
"npc_hoodie": {
|
||||||
|
"base": "hoodie",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.326,
|
0.228,
|
||||||
1.856,
|
1.866,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.326,
|
-0.228,
|
||||||
0,
|
-0.004,
|
||||||
-0.225
|
-0.091
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -673,11 +816,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"Eye",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_hoodie.glb",
|
"path": "assets/models/blender/npc/npc_hoodie.glb",
|
||||||
@@ -693,20 +836,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5304,
|
"triangles": 6206,
|
||||||
"vertices": 4950
|
"vertices": 12988
|
||||||
},
|
},
|
||||||
"npc_jacket": {
|
"npc_jacket": {
|
||||||
|
"base": "casual",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.332,
|
0.18,
|
||||||
1.824,
|
1.843,
|
||||||
0.189
|
0.271
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.332,
|
-0.18,
|
||||||
0,
|
-0.009,
|
||||||
-0.178
|
-0.099
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -714,11 +858,11 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Hair_Brown",
|
||||||
|
"Brown",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_jacket.glb",
|
"path": "assets/models/blender/npc/npc_jacket.glb",
|
||||||
@@ -734,34 +878,35 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4872,
|
"triangles": 6424,
|
||||||
"vertices": 4661
|
"vertices": 13440
|
||||||
},
|
},
|
||||||
"npc_vest": {
|
"npc_vest": {
|
||||||
|
"base": "worker",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.321,
|
0.217,
|
||||||
1.819,
|
1.863,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.321,
|
-0.217,
|
||||||
0,
|
-0.004,
|
||||||
-0.172
|
-0.103
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
"layer": "vest",
|
"layer": "vest",
|
||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
|
"Worker_Yellow",
|
||||||
|
"Worker_Vest",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
|
||||||
"hair",
|
"hair",
|
||||||
"rubber",
|
"Moustache",
|
||||||
"hivis",
|
"Eye",
|
||||||
"reflective"
|
"pants"
|
||||||
],
|
],
|
||||||
"mesh_node": "npc_base",
|
"mesh_node": "npc_base",
|
||||||
"path": "assets/models/blender/npc/npc_vest.glb",
|
"path": "assets/models/blender/npc/npc_vest.glb",
|
||||||
@@ -775,8 +920,8 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4980,
|
"triangles": 5240,
|
||||||
"vertices": 4879
|
"vertices": 10936
|
||||||
},
|
},
|
||||||
"palm_tree": {
|
"palm_tree": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
@@ -794,7 +939,8 @@
|
|||||||
"category": "props",
|
"category": "props",
|
||||||
"materials": [
|
"materials": [
|
||||||
"palm_bark",
|
"palm_bark",
|
||||||
"palm_leaf"
|
"palm_leaf",
|
||||||
|
"palm_leaf_dark"
|
||||||
],
|
],
|
||||||
"path": "assets/models/blender/props/palm_tree.glb",
|
"path": "assets/models/blender/props/palm_tree.glb",
|
||||||
"triangles": 400,
|
"triangles": 400,
|
||||||
@@ -840,7 +986,8 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"stone_pot",
|
"stone_pot",
|
||||||
"soil",
|
"soil",
|
||||||
"foliage"
|
"foliage",
|
||||||
|
"foliage_dark"
|
||||||
],
|
],
|
||||||
"path": "assets/models/blender/props/planter_box.glb",
|
"path": "assets/models/blender/props/planter_box.glb",
|
||||||
"replaces": "generated/planter_box.glb",
|
"replaces": "generated/planter_box.glb",
|
||||||
@@ -913,7 +1060,8 @@
|
|||||||
"category": "props",
|
"category": "props",
|
||||||
"materials": [
|
"materials": [
|
||||||
"stone_pot",
|
"stone_pot",
|
||||||
"foliage"
|
"foliage",
|
||||||
|
"foliage_dark"
|
||||||
],
|
],
|
||||||
"path": "assets/models/blender/props/round_planter.glb",
|
"path": "assets/models/blender/props/round_planter.glb",
|
||||||
"triangles": 1240,
|
"triangles": 1240,
|
||||||
@@ -1097,14 +1245,14 @@
|
|||||||
"attach": "head",
|
"attach": "head",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.101,
|
0.065,
|
||||||
0.059,
|
0.108,
|
||||||
0.158
|
0.101
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.101,
|
-0.065,
|
||||||
0.011,
|
0.077,
|
||||||
0.003
|
0.002
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,33 @@
|
|||||||
|
# Quaternius build sources (CC0 1.0)
|
||||||
|
|
||||||
|
These are the **build inputs** for the shipped NPC roster, not runtime assets.
|
||||||
|
`blender_src/.gdignore` keeps Godot from importing this directory.
|
||||||
|
|
||||||
|
Source: Quaternius *Ultimate Modular Characters* and *Ultimate Modular Women*
|
||||||
|
(<https://quaternius.com/packs/ultimatemodularcharacters.html>), licence CC0 1.0,
|
||||||
|
attribution recorded in the repository `CREDITS.md`.
|
||||||
|
|
||||||
|
| File | SHA-256 | Bones | Meshes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `Casual.glb` | `bc2d84fba249ebaa0629cb9d133ad37234196e4b8004aa205a6397abed04e05c` | 62 | 4 skinned + Icosphere helper |
|
||||||
|
| `Casual_Hoodie.glb` | `f5b722199f7628b24550989af4230f65d5230727df9c326fd2b3e360effe5249` | 62 | 4 skinned + Icosphere helper |
|
||||||
|
| `Worker.glb` | `8d0f454336c2effc5c08f63a177af3a82d1a7af025ca9588b40e4c90c4703534` | 62 | 4 skinned + Icosphere helper |
|
||||||
|
|
||||||
|
Each file also carries 24 animation clips; the build exports **no** animation data, so
|
||||||
|
the shipped GLBs contain the mesh, the 62-joint skin and the material slots only.
|
||||||
|
|
||||||
|
Reproducing the shipped characters:
|
||||||
|
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- build --only npc
|
||||||
|
|
||||||
|
Upstream quirks the build compensates for (see `docs/npc-model-sourcing.md`):
|
||||||
|
|
||||||
|
1. every mesh ships a **degenerate UV map** (all loops at `0,0`) with flat-colour
|
||||||
|
materials, so the build Smart-UV-unwraps and repacks before export;
|
||||||
|
2. the meshes must be joined **without welding** — `remove_doubles` corrupts the
|
||||||
|
skin weights;
|
||||||
|
3. the rest pose is a T-pose, so the arm-down rest is baked by
|
||||||
|
`_bake_quaternius_rest()`;
|
||||||
|
4. the upstream material names differ per file, so each source carries a slot map
|
||||||
|
down to the project's five tint slots.
|
||||||
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 922 KiB |
|
After Width: | Height: | Size: 639 KiB |
|
After Width: | Height: | Size: 501 KiB |
@@ -0,0 +1,505 @@
|
|||||||
|
# 3D model review (2026-11-03)
|
||||||
|
|
||||||
|
Status: **baseline measured; scorecard open.** This is the working document for the unversioned
|
||||||
|
`3D model re-evaluation` track in [`TODO.md`](../TODO.md). It records what was measured, what the
|
||||||
|
Blender skill guidance says the targets are, the ranked candidate gaps, and the decisions that
|
||||||
|
still need the project owner. Nothing here re-opens the accepted 0.9.4 limits by itself.
|
||||||
|
|
||||||
|
Related documents: [`npc-visual-review.md`](npc-visual-review.md) (accepted 0.9.4 limits and
|
||||||
|
before/after sheets), [`npc-visual-review.md`](npc-visual-review.md#proportion-audit) (proportion guide),
|
||||||
|
[`blender-assets.md`](blender-assets.md) (asset contract), [`asset-tooling.md`](asset-tooling.md)
|
||||||
|
(commands).
|
||||||
|
|
||||||
|
## Method and skill routing
|
||||||
|
|
||||||
|
The review is screenshot- and measurement-backed, following `qa-review` (screenshot set, defect
|
||||||
|
severity, explicit verdict) rather than an opinion pass:
|
||||||
|
|
||||||
|
| Family | Skills loaded | Criteria taken from them |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Routing and pipeline | `blender-director` | Plan → blockout → detail → validate → export; MCP steps translated to headless flatpak runs |
|
||||||
|
| Characters | `character-artist`, `retopology`, `rigging` | Eye/mouth/jaw/shoulder loops, quad-dominant deformation surfaces, 3-loop joints, no triangles on primary deformers |
|
||||||
|
| UVs and textures | `uv-workflow`, `texture-workflow`, `materials` | Texel-density tiers (hero 512–1024, standard 256–512, background 128–256 px/m), consistent density, no overlapping UVs |
|
||||||
|
| Budgets and structure | `asset-optimization`, `lod-pipeline` | Triangle/vertex budgets, naming, LOD tiers |
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
Project contract and gates stay authoritative; see `AGENTS.md` §Local Blender skills. Blender MCP
|
||||||
|
is optional interactive tooling (see [`asset-tooling.md#optional-interactive-blender-mcp`](asset-tooling.md#optional-interactive-blender-mcp)); this review's evidence
|
||||||
|
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)
|
||||||
|
|
||||||
|
| Evidence | Command | Result |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Contract audit | `python3 tools/blender_assets.py audit --json` | 46/46 assets clean; library 59,012 tris |
|
||||||
|
| Imported-GLB inspection | `blender_assets.py inspect` (all 46) | All clean; transforms applied, helpers ignored |
|
||||||
|
| Gear fit | `python3 tools/blender_assets.py fit --json` | Head width 0.315 m on all eight; clothing radius 0.190–0.260 m |
|
||||||
|
| Proportions | `python3 tools/npc_proportions.py report --json` | All eight inside the stylized guide |
|
||||||
|
| Texel density | `python3 tools/blender_assets.py texels --json` | New measurement, table below |
|
||||||
|
| Neutral baseline | `capture_npc_baseline.gd` → `tmp/model-baseline` (48 frames) + `npc_baseline.py sheet` | 48 frames, none blank; sheet in `tmp/` only |
|
||||||
|
| Final context | `capture_npc_review.gd` → `tmp/model-review` (96 frames) | 96 frames, none blank; raw frames stay local |
|
||||||
|
| Performance | `smoke_npc_budget.gd` | 24 avatars, 168 meshes, 152,464 tris, 6.94 ms of 50 ms |
|
||||||
|
|
||||||
|
Raw captures and JSON stay in the gitignored `tmp/`; committed review images remain the
|
||||||
|
`docs/blender_previews/sheet_*.jpg` set from 0.9.4/0.9.5 until a fix actually lands.
|
||||||
|
|
||||||
|
## Character measurements
|
||||||
|
|
||||||
|
Contract numbers from `audit` + `inspect`; density from `texels` (256² slot maps, authored and
|
||||||
|
with the runtime 4× fabric repeat).
|
||||||
|
|
||||||
|
| Character | Tris / verts | Height / feet | Materials | Authored td (px/m) | Fabric effective (px/m) | Verdict |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| `npc_casual` | 5,038 / 4,756 | 1.824 / +0.000 | 7 | 106.8–122.2 | 429–440 | Contract pass; face prototype |
|
||||||
|
| `npc_jacket` | 5,428 / 5,071 | 1.824 / +0.000 | 7 | 100.3–113.7 | 409–413 | Contract pass; face prototype |
|
||||||
|
| `npc_coat` | 6,030 / 5,802 | 1.824 / +0.000 | 7 | 75.3–81.4 | 307–310 | Contract pass; face prototype |
|
||||||
|
| `npc_vest` | 5,536 / 5,375 | 1.819 / +0.000 | 9 | 85.5–99.3 | 362–366 | Contract pass; face prototype |
|
||||||
|
| `npc_dress` | 5,456 / 5,128 | 1.825 / +0.000 | 7 | 81.9–89.0 | 338–339 | Contract pass; face prototype |
|
||||||
|
| `npc_hoodie` | 5,860 / 5,492 | 1.856 / +0.000 | 7 | 89.5–98.6 | 359–364 | Contract pass; face prototype |
|
||||||
|
| `npc_firefighter` | 5,900 / 5,902 | 1.819 / +0.000 | 9 | 64.0–75.1 | 287–290 | Contract pass; face prototype (helmet shadow hides the lower lip) |
|
||||||
|
| `npc_guard` | 5,602 / 5,684 | 1.819 / +0.000 | 11 | 90.0–100.8 | 374–374 | Contract pass; face prototype |
|
||||||
|
| `npc_crowd_a` | 5,200 / 4,734 | 1.825 / +0.000 | 7 | 107–120 | 431–435 | 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_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; 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`
|
||||||
|
(normal-split rounding); both are recorded and neither changes a verdict.
|
||||||
|
|
||||||
|
Readings:
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- 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.
|
||||||
|
- **Authored density (64–122 px/m) sits below the skill's background tier (128–256 px/m).** The
|
||||||
|
runtime tint controls most of the look: `skin`, `hair`, `accessory` and the fixed detail slots
|
||||||
|
are neutral maps tinted at runtime, not painted skin, so density mostly controls subtle
|
||||||
|
weave/noise. Fabric is effective 287–440 px/m (standard tier) because of the 4× repeat. This is
|
||||||
|
a recorded note, not a defect, until a portrait shows texture starvation.
|
||||||
|
- Material counts 7–11 per character are fixed detail slots (eyes, rubber, hivis, reflective,
|
||||||
|
leather, metal), not extra tint slots.
|
||||||
|
|
||||||
|
## Baseline inventory (2026-11-04)
|
||||||
|
|
||||||
|
Recorded for the character baseline before the Quaternius migration. `npc_base.blend` is the
|
||||||
|
frozen fallback; the 12 characters are currently assembled procedurally by
|
||||||
|
`tools/blender_assets.py`. The active plan is now the CC0 Quaternius base bake
|
||||||
|
([ADR-0017](adr/0017-quaternius-npc-base.md)); this section and the MakeHuman notes below are the
|
||||||
|
pre-migration record. The MPFB/MakeHuman development files were removed from `tmp/` after the
|
||||||
|
2026-11-05 pivot.
|
||||||
|
|
||||||
|
### `assets/models/npc/npc_base.blend` (frozen fallback)
|
||||||
|
|
||||||
|
- Mesh `npc_base`: **652 verts / 642 tris**, five material slots in order
|
||||||
|
`pants, cloth, skin, hair, accessory`.
|
||||||
|
- Armature: **14 bones** — `Hips, Spine, Neck, Head, ShoulderL/R, ElbowL/R, HandL/R, HipL/R, KneeL/R`
|
||||||
|
(the required 13 core bones plus the `Hips` root).
|
||||||
|
- It is the compact fallback, not the prototype host; the overhaul must not absorb prototype
|
||||||
|
detail into it unless a separate contract decision approves it.
|
||||||
|
|
||||||
|
### 12 generated character GLBs
|
||||||
|
|
||||||
|
`audit` counts (glTF accessor; Blender `inspect` can differ ±1 on normal-split rounding):
|
||||||
|
|
||||||
|
| Character | Tris | Verts | | Character | Tris | Verts |
|
||||||
|
| --- | ---: | ---: | --- | --- | ---: | ---: |
|
||||||
|
| `npc_casual` | 5,034 | 4,607 | | `npc_firefighter` | 5,888 | 5,785 |
|
||||||
|
| `npc_jacket` | 5,432 | 5,225 | | `npc_guard` | 5,670 | 5,671 |
|
||||||
|
| `npc_coat` | 6,046 | 5,645 | | `npc_crowd_a` | 5,284 | 4,908 |
|
||||||
|
| `npc_vest` | 5,532 | 5,195 | | `npc_crowd_b` | 4,812 | 4,550 |
|
||||||
|
| `npc_dress` | 5,540 | 5,073 | | `npc_crowd_c` | 5,690 | 5,510 |
|
||||||
|
| `npc_hoodie` | 5,864 | 5,325 | | `npc_crowd_d` | 5,810 | 5,444 |
|
||||||
|
|
||||||
|
All well under the 16,000/16,000 cap; the largest is `npc_coat` at 38% of the tri budget, so
|
||||||
|
head/face/hand detail has ample headroom.
|
||||||
|
|
||||||
|
### Procedural builders (current geometry locations)
|
||||||
|
|
||||||
|
Coordinates are Blender metres, Z-up; face front is −Y / Godot +Z; `s` = ±1 side, `w` = build
|
||||||
|
width scale.
|
||||||
|
|
||||||
|
- **Head** — `_face` + `_shape_head_mesh`: `sphere(Head, 0.155, (0, 0, 1.635), seg=24, rings=16,
|
||||||
|
scale=(0.92, 1.0, 1.12))`. Forward-only sculpt (brow at z 1.695, cheek at z 1.612, jaw/chin at
|
||||||
|
z 1.565); recessed creases rejected because they bake as dark AO patches.
|
||||||
|
- **Eyes** — sclera `sphere(0.019, (0.052s, −0.134, 1.664), scale=(1.25, 0.62, 0.85))`;
|
||||||
|
iris `sphere(0.0105, (0.052s, −0.141, 1.664), scale=(1.0, 0.55, 1.0))`; upper lid at z +0.014,
|
||||||
|
lower lid at z −0.012; brow box at (0.052s, −0.143, 1.699).
|
||||||
|
- **Nose** — bridge `cyl(0.016→0.022, (0, −0.145, 1.652))`, tip `sphere(0.022, (0, −0.162, 1.615))`,
|
||||||
|
wings `sphere(0.012, (0.016s, −0.160, 1.606))`.
|
||||||
|
- **Mouth** — slot `sphere(0.028, (0, −0.158, 1.583), scale=(1.0, 0.28, 0.12))`, lower lip
|
||||||
|
`sphere(0.012, (0, −0.156, 1.578))`.
|
||||||
|
- **Ears** — `sphere(0.036, (0.143s, 0.008, 1.63), scale=(0.42, 1.0, 1.35))` + lobe + inner concha.
|
||||||
|
- **Hands** — `_hands_and_shoes`: palm `sphere(0.046, (0.275·s·w, −0.012, 0.762),
|
||||||
|
scale=(0.80, 0.62, 0.88))`; four fingers base (…, 0.778) → tips z 0.701–0.714; thumb
|
||||||
|
(…, 0.778 → 0.760 → 0.738). No finger bones; open palm + `HAND_ITEM_FORWARD` only.
|
||||||
|
|
||||||
|
### UV layout
|
||||||
|
|
||||||
|
- Five tint slots (`skin, hair, accessory, cloth, pants`) at 256² each; fixed detail slots
|
||||||
|
(`eye_white`, rubber, hivis, reflective, leather, metal) are the extra material slots.
|
||||||
|
- Authored texel density 64–122 px/m (below the background tier 128–256), fabric effective
|
||||||
|
287–440 px/m via the 4× repeat; runtime tint carries the look. This is a recorded note until a
|
||||||
|
portrait shows texture starvation.
|
||||||
|
|
||||||
|
### MakeHuman prototypes (historical, not shipped; research files removed from `tmp/`)
|
||||||
|
|
||||||
|
| File | Verts / tris | Bones | Slots | Note |
|
||||||
|
| --- | ---: | ---: | --- | --- |
|
||||||
|
| `makehuman_body.glb` | 19,320 / 6,444 | 0 | `skin_proto` | CC0 intake, unskinned, 1.82 m grounded |
|
||||||
|
| `makehuman_retargeted.glb` | 7,062 / 6,444 | 14 | 5 tint | arm-down + contract rig, deltoid warp remains |
|
||||||
|
| `makehuman_final.glb` | 6,651 / 6,407 | 14 | 8 (incl. `skin.001`, `eye_white`, `rubber`) | project head/hands/shoes swapped; duplicate `skin.001` |
|
||||||
|
|
||||||
|
The "Icosphere" the inspector lists alongside these GLBs is the glTF importer's
|
||||||
|
bone-display helper (a `pose_bone.custom_shape`), not part of the asset; `inspect_glb` already
|
||||||
|
excludes it from measurements. The real cleanup for `makehuman_final.glb` is the duplicate
|
||||||
|
`skin.001` material and the manual deltoid retopology.
|
||||||
|
|
||||||
|
## Overhaul acceptance criteria (measurable)
|
||||||
|
|
||||||
|
Defined 2026-11-04 for the local part pipeline. A prototype must pass every applicable item
|
||||||
|
before it is rolled out to the other variants; the bar is the stylized low-poly target, not
|
||||||
|
photorealism.
|
||||||
|
|
||||||
|
### Face (one-variant prototype first)
|
||||||
|
|
||||||
|
Capture `front` (yaw 0°), `three_quarter` (yaw 35°) and `profile` (yaw 90°) at **1.5 m** and
|
||||||
|
**1.85 m** (32° FOV), matching `capture_npc_baseline.gd` (a new 1.5 m portrait capture is added
|
||||||
|
for this). Pass conditions:
|
||||||
|
|
||||||
|
- **Eyes** — sclera + iris + both lids distinct at 1.85 m; at 1.5 m the iris is a readable dark
|
||||||
|
dot against the white sclera, not a floating oval; no socket ring or lid seam.
|
||||||
|
- **Mouth** — the mouth line and lower-lip volume read at both distances; no moustache-like
|
||||||
|
shadow from a floating dark slot.
|
||||||
|
- **Nose** — bridge, tip and ala planes read in three-quarter and profile; no nose-wing seam or
|
||||||
|
inverted normal at the base.
|
||||||
|
- **Ears** — concha depth + lobe read in profile; not a flat disc.
|
||||||
|
- **Jaw/cheek/chin** — front and profile show a jaw transition and cheek/chin planes; no faceted
|
||||||
|
plane seams, inverted normals or z-fighting.
|
||||||
|
- **Silhouette** — front/three-quarter/profile agree; face direction is unambiguous.
|
||||||
|
- **AO** — the 256² bake stays clean: no recessed crease baking into a dark patch.
|
||||||
|
|
||||||
|
### Hands (one-variant prototype first)
|
||||||
|
|
||||||
|
Capture an **open palm** and a **relaxed grip** at **0.8 m** (`--view=hand` plus a new grip
|
||||||
|
capture). Pass conditions:
|
||||||
|
|
||||||
|
- **Open palm** — four fingers and a thumb read as a hand, not a mitten; at least two of the four
|
||||||
|
finger gaps are readable.
|
||||||
|
- **Grip** — the palm plus curled fingers silhouette reads; no finger-through-palm clipping.
|
||||||
|
- **Gear contact** — phone, cup, clipboard, camera, flashlight, wand and radio contact the palm
|
||||||
|
at 0.8 m with `HAND_ITEM_FORWARD` unchanged; no floating or hand-through-item clipping.
|
||||||
|
|
||||||
|
### Contract (both, every prototype)
|
||||||
|
|
||||||
|
- 24,000 triangles / 24,000 glTF vertices per character; gear stays at 1,500.
|
||||||
|
- Optional extra bones are allowed; the required 13 core bones and their rest positions must not
|
||||||
|
move; the `npc_base` mesh node name and the five tint slots stay unchanged.
|
||||||
|
|
||||||
|
### Measurement gates
|
||||||
|
|
||||||
|
`blender_assets.py inspect`/`audit`/`fit`, `npc_proportions.py`, the 24-NPC budget, Godot + Python
|
||||||
|
tests, the 48-frame baseline and the 96-frame pose/context review all stay authoritative; the new
|
||||||
|
face/hands captures are the visual bar for this track.
|
||||||
|
|
||||||
|
### Working-set layout
|
||||||
|
|
||||||
|
Part sources and preview GLBs live under `tmp/npc-parts/<variant>/` (gitignored by the root
|
||||||
|
`tmp/` rule); only the assembled character GLBs, AO/maps, the manifest and the review sheets are
|
||||||
|
committed. No new committed `.blend` source tree is added without a docs/ADR decision — the
|
||||||
|
frozen `npc_base.blend` stays the only tracked `.blend`.
|
||||||
|
|
||||||
|
### Head prototype (in progress 2026-11-04)
|
||||||
|
|
||||||
|
`tools/npc_parts.py build --part head_proto` is the first connected-shell attempt: a seg-48
|
||||||
|
head sphere is displaced (not floating spheres) into eye sockets with an orbital rim, a nose
|
||||||
|
bridge/tip/ala, upper/lower lip volumes, and extruded ears with a concha recess; the eyeballs
|
||||||
|
stay separate. Local renders live under `tmp/npc-parts/npc_casual/head_proto_*.png`.
|
||||||
|
|
||||||
|
Iteration log (vision-judged, profile view):
|
||||||
|
|
||||||
|
1. Initial displacement too subtle — read as a "ball with props".
|
||||||
|
2. Nose + ear visible in profile but the cranium still read as a "ball with a nose".
|
||||||
|
3. Added face-plane flatten + jaw/chin taper; taper read, but "~90% sphere".
|
||||||
|
4. Stronger flatten + nose + chin; nose now reads, jawline still dissolves into the skull.
|
||||||
|
5. Fixed an ear-displacement sign bug (the ± side loop cancelled the helix); the ear now
|
||||||
|
protrudes (x ±0.155 vs the ±0.139 shell) but reads as a soft blob.
|
||||||
|
|
||||||
|
Current state (prototype built, ~3/5 vision read): the nose, mouth notch, chin bump and ear bulge
|
||||||
|
all read; the centre-profile measurement confirms the relief is objective — nose tip −0.192,
|
||||||
|
brow −0.150, chin −0.149, jaw step −0.11, forehead −0.126→−0.078. The remaining polish (a flatter
|
||||||
|
forehead, a defined ear helix/lobe, a jawline that steps to the neck) is folded into the §3.2
|
||||||
|
variant comparison; the neck is part of `_skin_body`, so the standalone head always lacks it.
|
||||||
|
|
||||||
|
### AI-driven head rework (2026-11-04)
|
||||||
|
|
||||||
|
The head was reworked against an AI-generated target concept (front + profile, saved under
|
||||||
|
`tmp/npc-parts/head_concept_*.png`) and judged with `describe_image.py` across several iterations.
|
||||||
|
The displacement-on-sphere approach oscillates between "decorated egg" (too subtle) and
|
||||||
|
"spiky wedges" (too aggressive) and settles at ~3/5 — the coarse s48 sphere cannot resolve the
|
||||||
|
nose/lip/chin relief cleanly.
|
||||||
|
|
||||||
|
The judge then compared the displacement head against the **decimated MakeHuman head**
|
||||||
|
(extracted from `makehuman_retargeted.glb`, 2,009 verts / 1,573 tris, saved as
|
||||||
|
the local `makehuman_head.glb`) and returned a decisive result: **ship the MakeHuman head** —
|
||||||
|
it keeps the nose/chin/jaw landmarks at a fraction of the cost, while the procedural head's
|
||||||
|
"clean ellipse outline instantly screams procedural sphere".
|
||||||
|
|
||||||
|
**Conclusion:** the next head asset is the MakeHuman head shape (1,573 tris) plus the project's
|
||||||
|
separate eye/mouth/brow pieces for readability, not the displacement shell. The displacement
|
||||||
|
`_head_proto` stays local as a rejected variant.
|
||||||
|
|
||||||
|
**Built (2026-11-04):** `_makehuman_head` in `blender_assets.py` imports the committed
|
||||||
|
`assets/models/blender_src/makehuman_head.glb` (CC0, attributed in `CREDITS.md`), collapses it to
|
||||||
|
one skin slot, bisects + caps the ragged neck, and adds the project sclera/iris/lids/brows/mouth
|
||||||
|
at the MakeHuman landmarks. `build_character(name, head="mh")` assembles it; the result passes
|
||||||
|
`inspect` (14 bones, skinned, no issues). The judge reads the face ~3.5/5; remaining polish is
|
||||||
|
welding the added eye/brow pieces into the head shell and a skull-conforming hair cap.
|
||||||
|
|
||||||
|
**Standalone head re-review (2026-11-04):** the separate
|
||||||
|
`assets/models/blender_src/makehuman_head.glb` prototype (12,641 tris / 7,690 split verts,
|
||||||
|
with a skin eyelid mask, recessed globes, flat iris/pupil discs and surface-anchored brows)
|
||||||
|
was **accepted by the owner** as a basic human head. The automated vision gate still returns
|
||||||
|
REJECT on the eye/lid region (globe depth, lid overlap, iris at three-quarter) as deferred
|
||||||
|
polish. The assembled `npc_casual --head mh` now adds a skull-conforming hair cap, a hidden
|
||||||
|
scalp trim and a narrowed neck/shoulder body: 14,753 tris / 12,823 seam-split verts /
|
||||||
|
14 bones / 9 materials, `inspect` clean. All 12 variants count 14,693–15,987 tris. See
|
||||||
|
[`makehuman-head-review.md`](makehuman-head-review.md).
|
||||||
|
|
||||||
|
### Head subdivision comparison (2026-11-04)
|
||||||
|
|
||||||
|
Three `head_proto` variants at UV-sphere segments 24/48/64 were rendered at 1.85 m and 1.5 m in
|
||||||
|
front/three-quarter/profile and judged side by side:
|
||||||
|
|
||||||
|
| Variant | Verts / tris | 1.85 m | 1.5 m | Verdict |
|
||||||
|
| --- | ---: | --- | --- | --- |
|
||||||
|
| s24 | 670 / 1,168 | lumpy, nose/lips melt into the chin | jagged silhouette, no mouth | reject |
|
||||||
|
| s48 | 1,558 / 2,944 | eyes/brows/nose/lips read cleanly | features resolve cleanly | **pick** |
|
||||||
|
| s64 | 2,470 / 4,768 | visually identical to s48 | indistinguishable from s48 | reject (wasted verts) |
|
||||||
|
|
||||||
|
The chosen s48 is the `head_proto` default; the rejected s24/s64 stay local under
|
||||||
|
`tmp/npc-parts/`. A before/after sign-off sheet (current `_face` vs s48, 1.85 m
|
||||||
|
front/three-quarter/profile) is
|
||||||
|
[`blender_previews/sheet_head_proto_before_after.jpg`](blender_previews/sheet_head_proto_before_after.jpg).
|
||||||
|
|
||||||
|
## Scorecard skeleton
|
||||||
|
|
||||||
|
Verdicts use `qa-review` semantics: **P0** (blocker: wrong scale, broken normals, budget fail),
|
||||||
|
**P1** (major: silhouette/readability), **P2** (minor), **accept** (recorded limit). `—` means not
|
||||||
|
yet reviewed; this document is the place to fill it.
|
||||||
|
|
||||||
|
### Characters
|
||||||
|
|
||||||
|
| Character | Silhouette | Face @1.85 m | Hands/grip | Weights/pose | Cloth/hair | LOD | Verdict |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| *(pose captured 2026-11-04: see "Pose evidence" below)* | | | | | | | |
|
||||||
|
| `npc_casual` | accept (0.9.4) | accept (face prototype) | accept (open palm) | fixed (wrist/hand + hip/knee) | accept | accept (two-quad impostor) | pending owner |
|
||||||
|
| `npc_jacket` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | pending owner |
|
||||||
|
| `npc_coat` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept (hem 0.76 stride) | accept | pending owner |
|
||||||
|
| `npc_vest` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | pending owner |
|
||||||
|
| `npc_dress` | accept; performer props dominate | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | pending owner |
|
||||||
|
| `npc_hoodie` | accept (hooded criterion) | accept (lower face only) | accept | fixed (wrist/hand + hip/knee) | accept | accept | pending owner |
|
||||||
|
| `npc_firefighter` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | pending owner |
|
||||||
|
| `npc_guard` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | pending owner |
|
||||||
|
| `npc_crowd_a` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | crowd prototype |
|
||||||
|
| `npc_crowd_b` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept (beard) | accept | crowd prototype |
|
||||||
|
| `npc_crowd_c` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept (coat hem) | accept | crowd prototype |
|
||||||
|
| `npc_crowd_d` | accept | accept (face prototype) | accept | fixed (wrist/hand + hip/knee) | accept | accept | crowd prototype |
|
||||||
|
|
||||||
|
### Gear (18)
|
||||||
|
|
||||||
|
| Gear | Attach | Tris | Verdict |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `cap` | head | 328 | — (fit measured, visual pending) |
|
||||||
|
| `hood` | head | 804 | — |
|
||||||
|
| `sunglasses` | head | 164 | accept (0.9.4 clearance) |
|
||||||
|
| `firefighter_helmet` | head | 672 | accept (0.9.4 + 3 cm runtime offset) |
|
||||||
|
| `backpack` | back | 380 | accept (0.9.4 strap refit) |
|
||||||
|
| `shoulder_bag` | back | 188 | — |
|
||||||
|
| `scba_tank` | back | 362 | — |
|
||||||
|
| `lanyard` | chest | 100 | accept (0.9.4 + 4 cm runtime offset) |
|
||||||
|
| `phone` | hand | 96 | accept (forward offset) |
|
||||||
|
| `cup` | hand | 188 | accept |
|
||||||
|
| `clipboard` | hand | 84 | accept |
|
||||||
|
| `umbrella` | hand | 134 | accept |
|
||||||
|
| `camera` | hand | 318 | accept |
|
||||||
|
| `protest_sign` | hand | 92 | accept (performer/protester read) |
|
||||||
|
| `flashlight` | hand | 168 | accept |
|
||||||
|
| `wand` | hand | 244 | accept |
|
||||||
|
| `radio` | hand | 220 | accept |
|
||||||
|
|
||||||
|
Gear total: 5,002 tris across 18 pieces (all far below the 1,500-per-piece cap).
|
||||||
|
|
||||||
|
`blender_assets.py fit` readings (2026-11-03): `shoulder_bag` is clear on both `npc_coat` and
|
||||||
|
`npc_dress` (the only back-gear clearance the fit test asserts); `sunglasses` sits inside the
|
||||||
|
head band at 0.64× head width. Three caveats worth recording, none of which changes a verdict:
|
||||||
|
`backpack` (npc_casual) and `scba_tank` (npc_firefighter) report `CLIP` because back gear is
|
||||||
|
measured against the **hip/skirt** radius, which is wider than the upper back where the bags
|
||||||
|
actually sit; `cap` and `firefighter_helmet` report `band=OUTSIDE` because they sit on the crown
|
||||||
|
above the 1.55–1.81 face/head band (expected); and the `lanyard` clip is corrected at runtime by
|
||||||
|
`GEAR_OFFSETS` (+4 cm forward). The unused `beanie` was retired on 2026-11-03 (removed from
|
||||||
|
`GEAR`, its builder, `npc_avatar.gd` and the manifest).
|
||||||
|
|
||||||
|
### Props (20 library + 6 generated fallbacks)
|
||||||
|
|
||||||
|
Library total 13,608 tris; `marquee` (2,804) and `street_tree` (2,592) are the two heaviest and are
|
||||||
|
the first candidates for a `vegetation-artist`/lookdev pass.
|
||||||
|
|
||||||
|
| Prop | Tris | Verdict |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `bollard` | 138 | accept (2026-11-03 flared base collar) |
|
||||||
|
| `bench` | 560 | — |
|
||||||
|
| `trash_bin` | 228 | — |
|
||||||
|
| `planter_box` | 1,324 | — |
|
||||||
|
| `newspaper_box` | 300 | accept (2026-11-03 recessed slot + door seam) |
|
||||||
|
| `fire_hydrant` | 610 | accept (2026-11-03 rebuild: bolted flange, bonnet cap nut, larger side + front outlets) |
|
||||||
|
| `street_lamp` | 428 | — |
|
||||||
|
| `street_tree` | 2,592 | P1 candidate (flat-shaded clumps) |
|
||||||
|
| `palm_tree` | 400 | P1 candidate (same foliage language) |
|
||||||
|
| `round_planter` | 1,240 | — |
|
||||||
|
| `a_frame_sign` | 212 | — |
|
||||||
|
| `wall_lantern` | 180 | — |
|
||||||
|
| `traffic_cone` | 210 | — |
|
||||||
|
| `crowd_barrier` | 388 | — |
|
||||||
|
| `stanchion` | 434 | — |
|
||||||
|
| `handrail` | 268 | — |
|
||||||
|
| `security_podium` | 184 | — |
|
||||||
|
| `double_door` | 380 | — |
|
||||||
|
| `marquee` | 2,804 | — (emissive check pending) |
|
||||||
|
| `sedan` | 928 | — |
|
||||||
|
|
||||||
|
Vision review 2026-11-03 (`tools/describe_image.py` on the prop gallery): `bench` and `trash_bin`
|
||||||
|
are ship-ready (minor chamfer polish only); `bollard` passes but leans on its reflective band
|
||||||
|
(add a base collar); `planter_box`/`round_planter` fail on the foliage — the `_shrub()` clumps
|
||||||
|
read as rocks, not plants (same foliage language gap, gated on the owner); `newspaper_box` is
|
||||||
|
borderline (the slot is a flat decal, wants a recessed inset + door seam); `fire_hydrant` is now
|
||||||
|
rebuilt (see row). No prop floats or sinks; shadow direction is consistent.
|
||||||
|
|
||||||
|
Generated fallbacks (`assets/models/generated/`): bench, bollard, fire_hydrant, newspaper_box,
|
||||||
|
planter_box, trash_bin. They are only used when the library prop is missing, so they are audited
|
||||||
|
for presence, not re-reviewed for art quality.
|
||||||
|
|
||||||
|
### Venues
|
||||||
|
|
||||||
|
| Venue | Measured (map-budget test) | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Hollywood Boulevard | 58,884 tris / 409 meshes | shape-language review pending |
|
||||||
|
| The Joke Shop | 26,828 tris / 580 meshes | facade review landed: 6.0 m low hall, white signature band, tied-back curtains, west hotel tower; remaining gap = rounded west corner |
|
||||||
|
| TV Studio | — | shape-language review pending |
|
||||||
|
|
||||||
|
## Pose evidence (fixed 2026-11-04)
|
||||||
|
|
||||||
|
The weights/pose column can now be filled. The root cause was the harness: it never disabled the
|
||||||
|
avatar's `_physics_process`, so `_advance_fsm` + `_move_toward_target` reset `arrived`/velocity
|
||||||
|
every tick and `theater_pose()` reverted to idle. `capture_npc_pose.gd` now calls
|
||||||
|
`set_physics_process(false)`/`set_process(false)` after staging, and the pose sticks (sit vs idle
|
||||||
|
diff 5.63, walk vs idle 2.29, vision-judged as crouched). The earlier "moving the `Rig` child does
|
||||||
|
not render" probe was a red herring: it set the position in `_shoot()` in the same frame as
|
||||||
|
`root.get_texture().get_image()`.
|
||||||
|
|
||||||
|
`src/test/capture_npc_pose.gd` stages idle / walk-contact / walk-pass / seated for all twelve
|
||||||
|
variants (full-body + shoulder views).
|
||||||
|
|
||||||
|
| Check | Result |
|
||||||
|
| --- | --- |
|
||||||
|
| Pose reaches proxies | Seated pose logs `rig_y=-0.339`, `leg0=-1.357` |
|
||||||
|
| Pose reaches skeleton bones | `HipL`/`KneeL` bone rotations differ from rest; headless test `test_pose_reaches_the_skeleton_bones` locks it (suite 273/273) |
|
||||||
|
| Idle restores rest | Same test asserts the hip returns to its rest orientation |
|
||||||
|
| Skinning bound | One `Skeleton3D`, mesh bound to it (`same=true`); hiding `npc_base` removes the body |
|
||||||
|
| Bone rotations deform the mesh | Direct writes change rendered pixels: Spine 1,719 / HipL 1,052 / KneeL 810 / ElbowL 745 / Head 671 / ShoulderL 640 sampled pixels |
|
||||||
|
| Bone translation deforms the mesh | A +0.5 m Head offset moves the head in frame |
|
||||||
|
| Standalone window probes render the pose | Three variants (avatar under `root`, under a `Node3D` stage, created mid-`_process`) render the seated crouch (diff ~2,200-2,360 samples) |
|
||||||
|
| Offscreen capture renders the pose | A `SubViewport` + `RenderingServer.force_draw()` probe renders the crouch reliably (diff 602 at +2/+6/+12 frames), including a re-created second avatar |
|
||||||
|
| The harness renders the pose | **No** in its current form: same stage, same pose, rest frames. One ordering (`_frame_view()` immediately before `force_draw()`) produced posed walk frames once, then regressed to rest/garbage on the next run |
|
||||||
|
|
||||||
|
The rig, the animation math and the offscreen render path are all sound. A 2026-11-03 re-attempt
|
||||||
|
with the recipe (SubViewport + `force_draw()` + `force_update_all_bone_transforms()` + disabled
|
||||||
|
process/physics) still produced byte-identical rest frames, and a decisive probe narrowed the
|
||||||
|
failure further: teleporting the **avatar** changes the render (diff 10), but moving the **`Rig`
|
||||||
|
child** (the GLB model's parent) does not (diff 0). The skinned mesh therefore does not follow its
|
||||||
|
`Rig` Node3D transform, so the rig lowering never reaches the rendered model — the defect is
|
||||||
|
upstream of camera/`force_draw()` ordering. Next attempt must answer why the skinned model ignores
|
||||||
|
the `Rig` node, or stage poses via the known-good `capture_npc_review.gd` fallback.
|
||||||
|
|
||||||
|
Working recipe (verified by the probes; keep it when rebuilding):
|
||||||
|
|
||||||
|
1. Create a `SubViewport` (1280x720, `own_world_3d = true`, `UPDATE_ALWAYS`) in `_initialize`.
|
||||||
|
2. Build the studio (environment, key/fill light, ground, camera) and one avatar inside it.
|
||||||
|
3. Wait 2-3 frames so the skinned mesh binds.
|
||||||
|
4. Apply the pose with `_animate()` ticks, with the avatar's process/physics disabled.
|
||||||
|
5. Call `RenderingServer.force_draw()`, then `viewport.get_texture().get_image()`.
|
||||||
|
6. Free the avatar and repeat for the next pose; a re-created avatar also rendered correctly in a probe.
|
||||||
|
|
||||||
|
## Ranked candidate gaps
|
||||||
|
|
||||||
|
Ordered by expected player-visible gain per unit of work. None is approved for fixing yet; the
|
||||||
|
owner review decides.
|
||||||
|
|
||||||
|
1. **Face plane structure at portrait distance (P1, all eight).** The neutral sheet shows flat,
|
||||||
|
faceted cheek/brow planes and slit-like eyes at 1.85 m. `character-artist`/`retopology` guidance
|
||||||
|
asks for concentric eye loops and a 3-loop mouth. This is the largest readability gap and also
|
||||||
|
the most expensive (head rebuild + re-UV + re-AO per variant). Needs a targeted single-variant
|
||||||
|
prototype before committing to eight.
|
||||||
|
|
||||||
|
The eight library characters already carry the 0.9.4 face detail in
|
||||||
|
`tools/blender_assets.py._face()`: sculpted chin plate + cheekbones (`_shape_head_mesh`),
|
||||||
|
socketed sclera + thin upper lid, nose bridge/tip/wings, a rounded mouth and ear concha
|
||||||
|
(the plain sphere head in `tools/npc_pipeline.py` is the frozen `npc_base` fallback only).
|
||||||
|
So this gap is a readability/scale question on the existing sculpt — the head is
|
||||||
|
`seg=18/rings=12` with small offsets — not missing geometry. Concrete next step, if funded:
|
||||||
|
a single-variant `npc_casual` prototype that raises head subdivision and deepens the
|
||||||
|
cheek/brow offsets, then re-bakes AO and captures the portrait before touching the other seven.
|
||||||
|
|
||||||
|
**Prototype landed 2026-11-03/04:** `npc_casual` now has `seg=24/rings=16` (smoother), a subtle
|
||||||
|
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
|
||||||
|
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 16,000). The other seven variants keep the old face
|
||||||
|
until this prototype is approved.
|
||||||
|
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
|
||||||
|
gates any claim that the rig is acceptable.
|
||||||
|
3. **Foliage language (P1, `street_tree`, `palm_tree`, `planter_box`, `round_planter`).** Flat-shaded
|
||||||
|
clumps against an otherwise readable set, already deferred once. `vegetation-artist` carded
|
||||||
|
clusters would fix it inside the existing triangle budget.
|
||||||
|
4. **Texel density below the background tier (note).** 64–122 px/m authored vs the skill's 128–256
|
||||||
|
background tier. Fabric is already standard-tier at runtime. Decide: accept because the maps are
|
||||||
|
neutral tinted detail, or raise only `skin`/`hair` to 512² for portrait distance.
|
||||||
|
5. **`npc_firefighter` density floor (P2).** 64 px/m with the largest coat area; split the coat UV
|
||||||
|
shell or raise the map size before the next character pass, whichever the density decision says.
|
||||||
|
6. **Hands at conversation distance (accept, revisit only if face work opens the budget).** Open
|
||||||
|
palm with `HAND_ITEM_FORWARD`; no finger bones exist, so a curl rig is a contract decision, not
|
||||||
|
a modelling tidy-up.
|
||||||
|
7. **Gear never reviewed as silhouette language (P2).** 18 pieces pass fit/contract but have no
|
||||||
|
prop-artist verdict (the unused `beanie` was retired 2026-11-03).
|
||||||
|
8. **Venue shape language (P2).** Hollywood and the Joke Shop have budget data but no
|
||||||
|
`environment-artist`/`archviz` review; the TV Studio has no recorded map-budget number in this
|
||||||
|
document yet.
|
||||||
|
|
||||||
|
## qa-review sign-off (2026-11-04)
|
||||||
|
|
||||||
|
All eight library characters **ship with notes**. Face realism landed on all eight (`seg=24/rings=16`,
|
||||||
|
brow ridge, both eyelids, fuller lower lip), hands/grip and cloth/hair are accepted as recorded, gear
|
||||||
|
fit is measured with its caveats, and the props are reviewed (hydrant/bollard/newspaper_box rebuilt,
|
||||||
|
foliage two-toned). The only unresolved column is **weights/pose** (blocked on the capture harness),
|
||||||
|
and the full carded-cluster foliage pass is the one remaining owner-gated P1. No P0 remains.
|
||||||
|
|
||||||
|
## Decisions needed from the owner
|
||||||
|
|
||||||
|
- Accept the measured texel density as a stylized limitation, or fund a skin/hair resolution pass.
|
||||||
|
- Approve or reject a single-variant face/eye-loop prototype (and pick which variant to prototype
|
||||||
|
on) before any eight-character rebuild.
|
||||||
|
- Decide whether the pose/weight capture is required before the crowd-variant roster.
|
||||||
|
- Confirm foliage rebuild priority versus the map track (both touch the same street trees).
|
||||||
|
|
||||||
|
## Next actions
|
||||||
|
|
||||||
|
- Review the assembled [`makehuman-head-review.md`](makehuman-head-review.md) prototype (hair cap and full-body silhouette) before rebuilding the 12 shipped character GLBs.
|
||||||
|
|
||||||
|
|
||||||
|
Tracked in [`TODO.md`](../TODO.md) `3D model re-evaluation`: the remaining `A. Baseline…` item is
|
||||||
|
this owner review; `B`/`C`/`D` carry the per-family work, and `E` adds the pose/weight,
|
||||||
|
collider-coverage and scorecard-coverage gates this document found missing.
|
||||||
@@ -4,28 +4,28 @@ This folder holds the project documentation, visual references and generated QA
|
|||||||
|
|
||||||
## Current priorities
|
## Current priorities
|
||||||
|
|
||||||
1. [Character development](../TODO.md#character-development) — crowd, age, build, hair and outfit variety; generator-first, with the frozen NPC contract.
|
1. [Quaternius NPC base migration](../TODO.md#quaternius-npc-base-migration-top-priority) — CC0 Quaternius mesh/animation source baked to the 24,000-budget runtime contract.
|
||||||
2. [Local map development](../TODO.md#local-map-development) — OpenStreetMap/3D data, licensed reference imagery, photoreal facades, AI-assisted composition and Gaussian-splatting evaluation.
|
2. [3D model and local map improvements](../TODO.md#3d-model-and-local-map-re-evaluation) — asset re-evaluation and local map reconstruction.
|
||||||
3. [0.10.0 release candidate](../TODO.md#milestone-0100--release-candidate) — versioned release hardening after the unversioned development tracks.
|
3. [0.10.0 release candidate](../TODO.md#milestone-0100--3d-model-and-map-improvements-minor) — versioned release hardening after the unversioned development tracks.
|
||||||
|
|
||||||
## Start here
|
## Start here
|
||||||
|
|
||||||
- [`../PLAN.md`](../PLAN.md) — product, design intent and development priorities
|
- [`../PLAN.md`](../PLAN.md) — product, design intent and development priorities
|
||||||
- [`../TODO.md`](../TODO.md) — priority-ordered active tasks and roadmap
|
- [`../TODO.md`](../TODO.md) — priority-ordered active tasks and roadmap
|
||||||
- [`npc-model-sourcing.md`](npc-model-sourcing.md) — character-source evaluation, licence rules and the character-development plan
|
- [`npc-model-sourcing.md`](npc-model-sourcing.md) — character-source evaluation, licence rules and the character-development plan
|
||||||
|
- [`makehuman-head-review.md`](makehuman-head-review.md) — retained MakeHuman fallback head, provenance and review verdict
|
||||||
- [`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
|
||||||
|
- [`../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
|
- [`venues.md`](venues.md) — Hollywood, Joke Shop and TV Studio real-site mapping
|
||||||
- [`tutorial.md`](tutorial.md) — first-shift tutorial beats
|
|
||||||
- [`npc-visual-review.md`](npc-visual-review.md) — NPC visual baseline, acceptance criteria and ranked gaps
|
- [`npc-visual-review.md`](npc-visual-review.md) — NPC visual baseline, acceptance criteria and ranked gaps
|
||||||
- [`npc-proportion-audit.md`](npc-proportion-audit.md) — NPC proportion ratios and stylized guide results
|
- [`3d-model-review.md`](3d-model-review.md) — 3D model re-evaluation: measurements, scorecard and ranked gaps
|
||||||
- [`development-status.md`](development-status.md) — implemented features, current gaps and active planning
|
- [`development-status.md`](development-status.md) — implemented features, current gaps and active planning
|
||||||
- [`accessibility.md`](accessibility.md) — accessibility statement and known gaps
|
- [`accessibility.md`](accessibility.md) — accessibility statement and known gaps
|
||||||
- [`known-limitations.md`](known-limitations.md) — release-candidate blockers and accepted limits
|
- [`known-limitations.md`](known-limitations.md) — release-candidate blockers and accepted limits
|
||||||
- [`smoke-checklist.md`](smoke-checklist.md) — manual verification
|
- [`release-qa.md`](release-qa.md) — manual smoke checklist and balance-acceptance protocol
|
||||||
- [`balance-acceptance.md`](balance-acceptance.md) — release-candidate balance targets and evidence protocol
|
|
||||||
- [`../CREDITS.md`](../CREDITS.md) — credits and asset provenance
|
- [`../CREDITS.md`](../CREDITS.md) — credits and asset provenance
|
||||||
- [`../PRIVACY.md`](../PRIVACY.md) — privacy note
|
- [`../PRIVACY.md`](../PRIVACY.md) — privacy note
|
||||||
- [`ui-copy.md`](ui-copy.md) — screen, copy and button inventory
|
- [`ui-copy.md`](ui-copy.md) — screen, copy and button inventory
|
||||||
@@ -37,13 +37,17 @@ This folder holds the project documentation, visual references and generated QA
|
|||||||
- [`screens/README.md`](screens/README.md) — concepts, designs, screenshots and realism runs
|
- [`screens/README.md`](screens/README.md) — concepts, designs, screenshots and realism runs
|
||||||
- [`blender_previews/README.md`](blender_previews/README.md) — character/gear/prop previews
|
- [`blender_previews/README.md`](blender_previews/README.md) — character/gear/prop previews
|
||||||
- [`screenshots/README.md`](screenshots/README.md) — committed game gallery
|
- [`screenshots/README.md`](screenshots/README.md) — committed game gallery
|
||||||
- [`screen-design-report.md`](screen-design-report.md) — latest screen-design judge result
|
- [`ui-copy.md`](ui-copy.md#screen-design-judge-report) — screen/button inventory and latest judge result
|
||||||
|
|
||||||
## Quick preview
|
## Quick preview
|
||||||
|
|
||||||
| Reference | Screenshot |
|
| Reference | Capture |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Venue design | <img src="screens/designs/venue_overview.png" width="320" alt="Venue overview design"> |
|
| Venue design | <img src="screens/designs/venue_overview.png" width="320" alt="Venue overview design"> |
|
||||||
| Current entrance | <img src="screenshots/01-venue-overview.png" width="320" alt="In-game venue overview"> |
|
| Current entrance (day) | <img src="screenshots/01-venue-overview.png" width="320" alt="In-game venue overview"> |
|
||||||
|
| Current entrance (late) | <img src="screenshots/05-late-night-avenue.png" width="320" alt="Late-night boulevard"> |
|
||||||
|
| Joke Shop | <img src="screenshots/12-joke-shop.png" width="320" alt="Joke Shop at night"> |
|
||||||
| Character sheet | <img src="blender_previews/sheet_npc.jpg" width="320" alt="NPC character sheet"> |
|
| Character sheet | <img src="blender_previews/sheet_npc.jpg" width="320" alt="NPC character sheet"> |
|
||||||
|
| Neutral NPC baseline | <img src="blender_previews/sheet_npc_baseline.jpg" width="320" alt="Neutral NPC baseline sheet"> |
|
||||||
|
| Texture refinement | <img src="blender_previews/sheet_texture_refinement_0.9.5.jpg" width="320" alt="Texture refinement comparison"> |
|
||||||
| Prop sheet | <img src="blender_previews/sheet_props.jpg" width="320" alt="Prop asset sheet"> |
|
| Prop sheet | <img src="blender_previews/sheet_props.jpg" width="320" alt="Prop asset sheet"> |
|
||||||
@@ -28,5 +28,5 @@ Known gaps:
|
|||||||
review is still a 0.10.0 release-review item.
|
review is still a 0.10.0 release-review item.
|
||||||
|
|
||||||
Test evidence and the manual checklist live in
|
Test evidence and the manual checklist live in
|
||||||
[`smoke-checklist.md`](smoke-checklist.md); settings and save behaviour are
|
[`release-qa.md`](release-qa.md#manual-smoke-checklist); settings and save behaviour are
|
||||||
documented in [`getting-started.md`](getting-started.md).
|
documented in [`getting-started.md`](getting-started.md).
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
# ADR-0015: NPC model variety sourcing
|
# ADR-0015: NPC model variety sourcing
|
||||||
|
|
||||||
- **Status:** Accepted (planning)
|
- **Status:** Superseded by [ADR-0017](0017-quaternius-npc-base.md) (2026-11-05)
|
||||||
- **Date:** 2026-11-03
|
- **Date:** 2026-11-03
|
||||||
|
|
||||||
|
## Superseded
|
||||||
|
|
||||||
|
ADR-0017 replaces the generator-first decision below: Quaternius is now the
|
||||||
|
primary NPC base. The evaluation, licence rules and consequences remain as the
|
||||||
|
record of the 2026-11-03 decision.
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
The library has eight skinned characters, and role/seed selection reuses them
|
The library has eight skinned characters, and role/seed selection reuses them
|
||||||
@@ -70,3 +76,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/asset-tooling.md#optional-interactive-blender-mcp`](../asset-tooling.md#optional-interactive-blender-mcp).
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# ADR-0016: 24,000-triangle character budget
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-11-05
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
ADR-0015 raised the character contract to 16,000 triangles / 16,000 glTF
|
||||||
|
vertices for the MakeHuman head, hands and crowd variety. The local MPFB
|
||||||
|
full-body prototype now uses that head, project hands and shoes, and a fused
|
||||||
|
neck: it measures 19,747 triangles before any clothing variation. The 16,000 cap
|
||||||
|
leaves no room for the firefighter and hoodie gear, and it forces the body and
|
||||||
|
neck into a low decimation that produces visible facets.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Raise the character contract to **24,000 triangles / 24,000 glTF vertices** per
|
||||||
|
character (`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS`, `npc_pipeline.MAX_TRIANGLES`/
|
||||||
|
`MAX_VERTICES`, and the `adapt` defaults). The 13 core bones, five tint slots,
|
||||||
|
front orientation, grounding and single `npc_base` skinned mesh are unchanged.
|
||||||
|
Gear stays at 1,500 triangles and props at 4,000.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- The MPFB body, accepted head and neck fit with headroom for the 12 clothing
|
||||||
|
variants and future jacket/hoodie detail.
|
||||||
|
- The 24-NPC and Web frame-time budgets must be re-measured before shipping any
|
||||||
|
variant built at the new cap; the extra geometry is not free.
|
||||||
|
- The procedural generator and the low-poly `npc_base` fallback remain valid;
|
||||||
|
they are not required to use the new headroom.
|
||||||
|
- MPFB stays a local development tool. Production must bake a committed source
|
||||||
|
GLB; CI and the build must not depend on MPFB.
|
||||||
|
- Historical reviews and measurements that state 16,000 remain accurate for the
|
||||||
|
date they were recorded; current contract documents state 24,000.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
- **Keep 16,000 and decimate the MPFB body further:** rejected because the neck
|
||||||
|
and shoulder already show facets and the clothing variants would exceed the
|
||||||
|
cap.
|
||||||
|
- **Separate caps for the MPFB prototype and the shipped characters:** rejected
|
||||||
|
because it creates a second contract and makes the rollout ambiguous.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# ADR-0017: Quaternius NPC base
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-11-05
|
||||||
|
- **Supersedes:** ADR-0015 (generator-first character sourcing)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The current NPC library is built by the project-owned procedural generator. It
|
||||||
|
keeps the contract and ships, but the body, neck, hands and shoes need a
|
||||||
|
bespoke part pipeline to read as human. The MakeHuman head and MPFB body
|
||||||
|
prototypes improved detail but added a second authoring path, a 19,747-triangle
|
||||||
|
prototype and unresolved neck/shoulder seams.
|
||||||
|
|
||||||
|
The CC0 **Quaternius Ultimate Modular Characters** pack already provides a
|
||||||
|
coherent low-poly human silhouette, 21 modular outfits and 24 animation clips.
|
||||||
|
The researched GLBs (`Casual`, `Casual_Hoodie`, `Worker`) measure 4 meshes, 62
|
||||||
|
joints and 24 clips each. The owner directed that Quaternius become the NPC
|
||||||
|
base. The full evaluation is in [`docs/npc-model-sourcing.md`](../npc-model-sourcing.md).
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. **Quaternius is the primary NPC mesh and animation source.** Keep the
|
||||||
|
licence and SHA-256 provenance for the three researched GLBs.
|
||||||
|
2. **Stage 1 keeps the runtime contract.** Bake a canonical Quaternius base onto
|
||||||
|
the existing 13-core contract, five tint slots, 24,000/24,000 budget,
|
||||||
|
grounded −Y front and one `npc_base` skinned mesh. Retarget Idle and Walk;
|
||||||
|
discard the combat/gun/sword clips. `NpcAvatar`, gear attach points and the
|
||||||
|
pose composition remain the runtime interface.
|
||||||
|
3. **The 62-joint rig is an authoring source, not a runtime dependency.** It is
|
||||||
|
not shipped and Quaternius is not loaded by CI, build or runtime.
|
||||||
|
4. **Stage 2 needs its own ADR.** If the proportion warp fails the silhouette,
|
||||||
|
deformation or gear gate, stop and decide whether to adopt the 62-joint rig
|
||||||
|
at runtime. Do not ship a hybrid rig or a second gear offset system.
|
||||||
|
5. **Close the MakeHuman/MPFB body work.** The accepted MakeHuman head and the
|
||||||
|
procedural `npc_base` stay as tested fallbacks until the Quaternius rollout
|
||||||
|
ships. No new MPFB development.
|
||||||
|
6. **Commit baked assets only.** The release consumes committed GLBs, maps and
|
||||||
|
the manifest; Quaternius and Blender remain development tools.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
|
||||||
|
- A human base, modular outfits and a coherent animation set are already
|
||||||
|
authored and CC0.
|
||||||
|
- The bespoke neck, hand and shoe pipelines are no longer on the critical path.
|
||||||
|
- The runtime contract and gear stay stable during the mesh migration.
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
|
||||||
|
- A 62-joint T-pose does not share the arm-down 13-core rest pose or
|
||||||
|
proportions; the proportion warp is the main risk.
|
||||||
|
- Quaternius' animation quality is only available after retargeting, unless
|
||||||
|
Stage 2 is accepted.
|
||||||
|
- The migration touches all 12 variants, their AO maps, manifest entries and
|
||||||
|
baseline captures at once.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
- **Keep the procedural generator as the primary path.** Rejected by the owner:
|
||||||
|
the human silhouette remains the weak point and the bespoke part pipeline is
|
||||||
|
larger than the Quaternius bake.
|
||||||
|
- **Adopt the 62-joint rig immediately.** Deferred to Stage 2 because it changes
|
||||||
|
`NpcAvatar`, pose composition and gear offsets at the same time as the mesh.
|
||||||
|
- **Continue the MPFB body.** Rejected: it needs a separate head/neck/hand
|
||||||
|
pipeline and still has unresolved surface seams.
|
||||||
|
- **Use a service character generator (Mixamo, Ready Player Me).** Rejected:
|
||||||
|
redistribution and runtime terms are not a CC0 basis.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# ADR-0018: Quaternius Stage 2 — adopt source proportions at runtime
|
||||||
|
|
||||||
|
- **Status:** Accepted (2026-11-05, owner chose option 2)
|
||||||
|
- **Date:** 2026-11-05
|
||||||
|
- **Supersedes:** ADR-0017 decision 2 (Stage 1 keeps the 13-core contract)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
ADR-0017 made Quaternius the NPC base and staged the migration: Stage 1 bakes a
|
||||||
|
proportion-warped mesh onto the existing 13-core contract; Stage 2 adopts the
|
||||||
|
62-joint rig at runtime only if the warp fails.
|
||||||
|
|
||||||
|
The Stage 1 proportion warp was executed and measured (`docs/npc-model-sourcing.md`
|
||||||
|
§Rest-pose and proportion-warp measurement). The arm-down rest pose bakes cleanly, but the
|
||||||
|
warp to the contract proportions does not:
|
||||||
|
|
||||||
|
| Joint | Quaternius | Contract | Delta |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Hips height | 0.869 | 0.62 | −0.249 |
|
||||||
|
| Shoulder x | 0.148 | 0.27 | +0.122 |
|
||||||
|
| Arm (shoulder→hand tip) | 0.429 | 0.70 | ×1.63 |
|
||||||
|
| Leg (hip→ankle) | 0.835 | 0.62 | ×0.74 |
|
||||||
|
| Torso (hip→head) | 0.745 | 1.20 | ×1.61 |
|
||||||
|
|
||||||
|
The vision judge reads the warped mesh as distorted: paddle/claw hands, forearms lost into the
|
||||||
|
torso, stretched shins, "long legs + short torso". The arm either stays short (a shortened arm
|
||||||
|
against the contract hand) or stretches 1.63× (a distorted deltoid + hand). Both are the gate's
|
||||||
|
rejection conditions.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Stop the Stage 1 rollout and **adopt the Quaternius 62-joint rig at runtime** (owner chose
|
||||||
|
option 2). The mesh is not proportion-warped: it keeps its source LOD (3,199 welded verts /
|
||||||
|
6,206 tris) and only the arm-down rest pose is baked.
|
||||||
|
|
||||||
|
- `NpcAvatar` maps the Quaternius joint names instead of the 13-core names.
|
||||||
|
- Gear attach points are re-derived from the Quaternius skeleton (hip ≈0.87, shoulder ≈0.148/1.39,
|
||||||
|
wrist ≈0.99, knee ≈0.52).
|
||||||
|
- The Quaternius Idle and Walk clips are used directly — they are authored for the 62-joint rig,
|
||||||
|
so no retargeting pass is needed. Combat/gun/sword clips stay unused.
|
||||||
|
|
||||||
|
Option 1 (keep the 13-core names with Quaternius rest positions) is rejected in favour of the
|
||||||
|
full source rig.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
|
||||||
|
- The Quaternius silhouette, deltoid, hand and leg proportions are preserved — no paddle hands,
|
||||||
|
no shortened arm, no torso/leg distortion.
|
||||||
|
- The mesh stays at source LOD (far under the 24,000 cap), so no decimation is needed.
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
|
||||||
|
- The runtime contract numbers (hip 0.62, shoulder 0.27, hand 0.78) change; every gear attach
|
||||||
|
point, `npc_proportions` guide, fit measurement and pose-composition constant must be
|
||||||
|
re-derived and re-tested.
|
||||||
|
- `NpcAvatar`'s bone mapping and pose composition are rewritten for the 62-joint rig, a larger
|
||||||
|
change than the mesh swap.
|
||||||
|
- The 12-variant rollout and the 12×6 baseline are still blocked until the new rest positions
|
||||||
|
and gear fit are owner-approved.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
- **Ship the warped mesh.** Rejected by the gate: the 1.63× arm / 0.74× leg warp visibly
|
||||||
|
distorts and the owner directed keeping the Quaternius as close to source as possible.
|
||||||
|
- **Keep the old contract and accept a short arm.** Rejected for the same reason: the hand ends
|
||||||
|
at ≈0.99 instead of 0.78, so handheld gear floats unless attach points move — which is option 1.
|
||||||
|
- **Abandon Quaternius and keep the procedural generator.** Rejected in ADR-0017.
|
||||||
|
|
||||||
|
## Open question
|
||||||
|
|
||||||
|
Resolved: option 2 (62-joint runtime) is the owner's choice. The first implementation step is
|
||||||
|
the `NpcAvatar` bone-map + gear-attach re-derivation, then the slot/AO bake and the 12-variant
|
||||||
|
rollout.
|
||||||
|
|
||||||
|
## Follow-up decision: full roster swap
|
||||||
|
|
||||||
|
- **Date:** 2026-11-05 (owner, at the `0.9.9` face/silhouette gate)
|
||||||
|
|
||||||
|
The owner chose **option (a) full swap** from
|
||||||
|
[`docs/screens/npc-face-gate/`](../../screens/npc-face-gate/README.md): every roster character
|
||||||
|
moves onto the Quaternius base, and the procedural generator plus the MakeHuman head retire to
|
||||||
|
the tested fallback rather than staying in the shipped roster.
|
||||||
|
|
||||||
|
Scope consequences:
|
||||||
|
|
||||||
|
- The shipped roster keeps its twelve public character names and `CHARACTER_POOLS` mapping; the
|
||||||
|
geometry behind them comes from the three CC0 full-body sources already researched
|
||||||
|
(`Casual`, `Casual_Hoodie`, `Worker`), aliased per role.
|
||||||
|
- Variety now comes from the five tint slots, the gear library and the per-role outfit rules
|
||||||
|
instead of twelve distinct meshes. Roster silhouettes are therefore *less* varied than the
|
||||||
|
procedural library until the modular part library (hair, outfits) is added — tracked as a
|
||||||
|
`0.9.10` task, not hidden.
|
||||||
|
- The procedural library, `_build_body()` and the MakeHuman head stay in the tree and tested as
|
||||||
|
the instant fallback; no MPFB or Stage-1 proportion-warp work reopens.
|
||||||
@@ -19,3 +19,6 @@ Each ADR records the context, decision, consequences and alternatives for a dura
|
|||||||
- [0013-blender-authored-npc-characters](0013-blender-authored-npc-characters.md)
|
- [0013-blender-authored-npc-characters](0013-blender-authored-npc-characters.md)
|
||||||
- [0014-runtime-image-root](0014-runtime-image-root.md)
|
- [0014-runtime-image-root](0014-runtime-image-root.md)
|
||||||
- [0015-npc-model-variety-sourcing](0015-npc-model-variety-sourcing.md)
|
- [0015-npc-model-variety-sourcing](0015-npc-model-variety-sourcing.md)
|
||||||
|
- [0016-npc-character-budget-24k](0016-npc-character-budget-24k.md)
|
||||||
|
- [0017-quaternius-npc-base](0017-quaternius-npc-base.md)
|
||||||
|
- [0018-quaternius-stage2-runtime-rig](0018-quaternius-stage2-runtime-rig.md)
|
||||||
@@ -37,7 +37,7 @@ This document follows the structure of the arc42 architecture documentation temp
|
|||||||
- **MINOR** — new venues, NPC archetypes, systems, or features that are backward compatible.
|
- **MINOR** — new venues, NPC archetypes, systems, or features that are backward compatible.
|
||||||
- **PATCH** — bug fixes and small polish that do not change behavior.
|
- **PATCH** — bug fixes and small polish that do not change behavior.
|
||||||
|
|
||||||
The single source of truth for the version is `config/version` in `project.godot` (currently `0.10.0-rc.1`). Each release is tagged in git as `v<version>` (e.g. `v0.6.0`). The `0.x` series is pre-stable, so MINOR bumps may introduce breaking changes while the prototype is still stabilizing. `tools/build_releases.sh` and `tools/publish_release.sh` read the version from `project.godot`, so bumping it there is the only required step before a release. The full bump checklist lives in [`RELEASING.md`](RELEASING.md).
|
The single source of truth for the version is `config/version` in `project.godot` (currently `0.9.8`). Each release is tagged in git as `v<version>` (e.g. `v0.6.0`). The `0.x` series is pre-stable, so MINOR bumps may introduce breaking changes while the prototype is still stabilizing. `tools/build_releases.sh` and `tools/publish_release.sh` read the version from `project.godot`, so bumping it there is the only required step before a release. The full bump checklist lives in [`RELEASING.md`](RELEASING.md).
|
||||||
|
|
||||||
## 3. Context and Scope
|
## 3. Context and Scope
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ The player interacts with NPCs, venue entrances, access artifacts, equipment, st
|
|||||||
- `WorldBuilder` assembles the procedural venue from a per-venue `VENUE_SPECS` table (facade, windows, stairs, street, nav bounds, guard spawn, feature flags and prop placements), prefers the Blender prop library in `assets/models/blender/props/`, and falls back to `assets/models/generated/` or primitives. `GameManager` rebuilds only the `World` container when the venue changes, so the guard/UI and their state survive. It also wires the phase-driven library emissives and street dressing.
|
- `WorldBuilder` assembles the procedural venue from a per-venue `VENUE_SPECS` table (facade, windows, stairs, street, nav bounds, guard spawn, feature flags and prop placements), prefers the Blender prop library in `assets/models/blender/props/`, and falls back to `assets/models/generated/` or primitives. `GameManager` rebuilds only the `World` container when the venue changes, so the guard/UI and their state survive. It also wires the phase-driven library emissives and street dressing.
|
||||||
- `src/game/ui/art.gd` loads runtime UI/scene art from `assets/images/ui/` and `assets/images/scenes/`, with null fallbacks so panels keep their procedural layouts.
|
- `src/game/ui/art.gd` loads runtime UI/scene art from `assets/images/ui/` and `assets/images/scenes/`, with null fallbacks so panels keep their procedural layouts.
|
||||||
- `tools/model_pipeline.py` builds and audits the street-prop `.glb` models and packs their colour atlases.
|
- `tools/model_pipeline.py` builds and audits the street-prop `.glb` models and packs their colour atlases.
|
||||||
- `tools/npc_pipeline.py` builds and audits the skinned NPC base model (`assets/models/generated/npc_base.glb`) and its per-slot material maps, with an optional AI fabric override (ADR-0013).
|
- `tools/npc_pipeline.py` builds and audits the skinned NPC base model (`assets/models/generated/npc_base.glb`) and its per-slot material maps, with an optional AI fabric override (ADR-0013). The active character migration bakes the CC0 Quaternius base onto the same contract (ADR-0017); the procedural base remains the fallback.
|
||||||
- `tools/blender_assets.py` builds, renders and audits the Blender asset library (`assets/models/blender/{npc,npc_gear,props}/` + `manifest.json`): NPC character variants that are drop-in for `npc_base`, attach-point gear, and street/venue props. `NpcAvatar` selects variants/gear by role/seed and `WorldBuilder` loads the prop replacements and new street dressing, with procedural fallbacks (see `docs/blender-assets.md`).
|
- `tools/blender_assets.py` builds, renders and audits the Blender asset library (`assets/models/blender/{npc,npc_gear,props}/` + `manifest.json`): NPC character variants that are drop-in for `npc_base`, attach-point gear, and street/venue props. `NpcAvatar` selects variants/gear by role/seed and `WorldBuilder` loads the prop replacements and new street dressing, with procedural fallbacks (see `docs/blender-assets.md`).
|
||||||
- `tools/scene_concepts.py` generates realism-pass references (`docs/screens/concepts/`), skyline backdrops and surface tiles (raw `assets/textures/ai/v2/raw/` → candidates `assets/textures/ai/game_ready/v2/`), promotes the validated ones to the tracked `v2/picks/` that `SurfaceMaterials`/`WorldBuilder` preload, audits them, and scores `src/test/capture_realism.gd` A/B captures (six stages, runtime texture swaps) against the concepts (`docs/screens/realism/<run>/`).
|
- `tools/scene_concepts.py` generates realism-pass references (`docs/screens/concepts/`), skyline backdrops and surface tiles (raw `assets/textures/ai/v2/raw/` → candidates `assets/textures/ai/game_ready/v2/`), promotes the validated ones to the tracked `v2/picks/` that `SurfaceMaterials`/`WorldBuilder` preload, audits them, and scores `src/test/capture_realism.gd` A/B captures (six stages, runtime texture swaps) against the concepts (`docs/screens/realism/<run>/`).
|
||||||
- `tools/asset_pipeline.py` generates and audits role-aware textures; `tools/screen_designs.py` generates screen references and judges captures; `tools/check_screenshots.sh` renders the staged gallery and runs the compare gate.
|
- `tools/asset_pipeline.py` generates and audits role-aware textures; `tools/screen_designs.py` generates screen references and judges captures; `tools/check_screenshots.sh` renders the staged gallery and runs the compare gate.
|
||||||
@@ -128,23 +128,99 @@ Contracts that keep the pipeline replaceable:
|
|||||||
- UI/scene art is loaded through `UiArt` with a null/neutral fallback; composed images live in `assets/images/`, not in the docs reference tree (ADR-0014).
|
- UI/scene art is loaded through `UiArt` with a null/neutral fallback; composed images live in `assets/images/`, not in the docs reference tree (ADR-0014).
|
||||||
- CI stays offline: only committed `.glb`/`.png`/`.tres` and Godot are needed; optional AI/Blender steps are reproducible development tools.
|
- CI stays offline: only committed `.glb`/`.png`/`.tres` and Godot are needed; optional AI/Blender steps are reproducible development tools.
|
||||||
|
|
||||||
|
### Character and gear asset contract
|
||||||
|
|
||||||
|
The runtime NPC contract is intentionally narrow. It is enforced by
|
||||||
|
`tools/blender_assets.py audit`, the GLB `inspect` path and the GDScript tests:
|
||||||
|
|
||||||
|
| Contract | Rule |
|
||||||
|
| --- | --- |
|
||||||
|
| Mesh node | `npc_base`, skinned under an armature imported as `Skeleton3D`. |
|
||||||
|
| Core bones | `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`, `Spine`, `Neck` at the frozen rest positions. Optional extra bones (fingers, face, toes) are allowed if they do not move the core rest positions or break `NpcAvatar` proxies. |
|
||||||
|
| Tint slots | `skin`, `cloth`, `pants`, `accessory`, `hair`; fixed detail materials keep their authored colours. |
|
||||||
|
| Orientation | Blender −Y / Godot +Z front, feet grounded near `y = 0`, metre scale. |
|
||||||
|
| Budget | 24,000 triangles / 24,000 glTF vertices per character; 1,500 per gear piece; 4,000 per prop. The low-poly `npc_base` remains a fallback, not a second contract. |
|
||||||
|
| Textures | 256² albedo + normal + packed ORM per slot; cloth/pants use the 4× `NPC_FABRIC_UV_SCALE` and skip per-character baked AO, while skin/hair/gear keep AO. |
|
||||||
|
| Gear attach | Authored in attach-point space: head `Rig/Head` (0, 1.62, 0), back (0, 1.10, 0), chest (0, 1.30, 0), hand (±0.27, 0.78, 0.02); grip 5 cm below the origin. `GEAR_OFFSETS` and `HAND_ITEM_FORWARD` are runtime fit corrections, not authored-geometry changes. |
|
||||||
|
| Fallback chain | Library variant → `npc_base.glb` → procedural `NpcAvatar` mesh. Committed GLBs and maps must keep CI offline and Blender-free. |
|
||||||
|
|
||||||
|
Asset types and their consumers:
|
||||||
|
|
||||||
|
- Library characters, gear and props: `assets/models/blender/**`, generated by
|
||||||
|
`tools/blender_assets.py`; loaded by `NpcAvatar`/`WorldBuilder` with
|
||||||
|
generated/procedural fallbacks.
|
||||||
|
- Frozen base: `assets/models/generated/npc_base.glb`, generated by
|
||||||
|
`tools/npc_pipeline.py`; the procedural avatar remains the last-resort path.
|
||||||
|
- Maps: `assets/textures/npc/*_{albedo,normal,orm}.png` and baked AO maps; the
|
||||||
|
mesh budgets and material wiring are checked by `audit`/`fit`/`texels` and the
|
||||||
|
headless suite.
|
||||||
|
- Editable `.blend` sources are development-only; only exported GLBs, AO/maps,
|
||||||
|
manifests and committed review sheets are runtime/repository assets.
|
||||||
|
|
||||||
|
### Venue implementation contract
|
||||||
|
|
||||||
|
The three venues are authored by `WorldBuilder.VENUE_SPECS` and rebuilt through
|
||||||
|
the `World` container when the venue changes; the guard, UI and checkpoint state
|
||||||
|
survive. Venue data is content, while the following runtime rules are fixed:
|
||||||
|
|
||||||
|
| Venue | Real reference | Game implementation | Budget |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Hollywood Boulevard | 6840 Hollywood Blvd / 1921 Masonic Temple | Six-column Masonic facade, eight steps, Walk of Fame, boulevard, Ovation arcade and neighbours | 58,884 tris / 409 meshes |
|
||||||
|
| The Joke Shop | Fictional stand-in for 8433 Sunset Blvd | 27 m black hall, signature canopy, circular blade sign, red curtain bays, west entrance, hillside and neighbours | 25,828 tris / 518 meshes |
|
||||||
|
| Late Night TV Studio | Same El Capitan block as Hollywood | Reuses the Masonic facade and steps with bulb canopy, car, barriers and studio-arrival staging | shares the Hollywood block; TV spec is a 30.0 m variant |
|
||||||
|
|
||||||
|
Venue rules:
|
||||||
|
|
||||||
|
- Coordinates are Godot metres; the guard's front is +Z. `VENUE_SPECS` owns
|
||||||
|
facade dimensions, street/nav bounds, guard spawn, feature flags and prop
|
||||||
|
placements; tests enforce the map budgets.
|
||||||
|
- Collision and navigation remain mesh-based. Props that block walking need a
|
||||||
|
`StaticBody3D` plus `NavigationObstacle3D`; the marquee, handrail, lantern and
|
||||||
|
door are documented non-blocking exceptions.
|
||||||
|
- Day/night is a phase offset on one geometry/scenario, not a second baked map.
|
||||||
|
The venue menu's Night row starts in `LATE` while the checkpoint stores the
|
||||||
|
offset.
|
||||||
|
- Real-site references are evidence for shape/materials, not shipped assets.
|
||||||
|
Licensed boards live under `docs/screens/refs/` with sidecars; local-only
|
||||||
|
research stays in `tmp/research/` and is never committed.
|
||||||
|
- The stylized geometry remains the runtime fallback if a future data-backed
|
||||||
|
block or facade kit fails budget, navigation, visual or provenance gates.
|
||||||
|
|
||||||
|
### Persistence and save contract
|
||||||
|
|
||||||
|
Persistence is local-only under Godot's `user://` directory. The architecture
|
||||||
|
owns the schema; operational locations and reset/rollback steps live in
|
||||||
|
[`getting-started.md`](getting-started.md#save-data-and-migration).
|
||||||
|
|
||||||
|
| File | Contents | Compatibility |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `settings.json` | Camera/HUD/bindings/difficulty/audio/accessibility/tutorial flags | Schema-less compatible growth; missing keys merge from `Save.DEFAULT_SETTINGS`. |
|
||||||
|
| `campaign.json` | Campaign index, completed venues, difficulty, shift number, last grade | Missing fields fall back to safe campaign defaults. |
|
||||||
|
| `shift_results.json` | Completed-shift review history (array) | Append-only; unknown fields are ignored by readers. |
|
||||||
|
| `shift_state.json` | Resumable checkpoint: version `1`, venue, guard/door/flashlight/body-camera state, coordinator snapshot, NPC rows | Versioned; unknown/missing state falls back rather than blocking boot. |
|
||||||
|
|
||||||
|
Writes go to a `.tmp` file and are renamed after the complete JSON is written.
|
||||||
|
Corrupt or partial files are ignored on load; a failed replace leaves the prior
|
||||||
|
valid file intact. The checkpoint deliberately excludes the tutorial overlay and
|
||||||
|
transient input state.
|
||||||
|
|
||||||
### Character development tooling
|
### Character development tooling
|
||||||
|
|
||||||
Characters have two authored sources and one procedural fallback. The eight-variant
|
Characters have one active migration, two committed fallbacks and one procedural last
|
||||||
Blender library (`assets/models/blender/npc/`) is the normal presentation path; the
|
resort. The Quaternius base migration (ADR-0017) bakes the CC0 Quaternius mesh and
|
||||||
older skinned `npc_base` (`assets/models/generated/npc_base.glb`) remains a frozen
|
Idle/Walk clips onto the existing 13-core contract and produces the next 12-variant
|
||||||
contract and fallback; `NpcAvatar`'s procedural rig is the offline last resort.
|
library. Until that ships, the 12-variant Blender library
|
||||||
Four crowd-variety prototypes have been built and audited locally but are not yet in
|
(`assets/models/blender/npc/`) is the normal presentation path, the older skinned
|
||||||
the shipped catalogue; generator-first expansion is the active character track
|
`npc_base` (`assets/models/generated/npc_base.glb`) is the frozen contract fallback,
|
||||||
(ADR-0015). Blender is a developer tool only — runtime, tests and CI consume committed
|
and `NpcAvatar`'s procedural rig is the offline last resort. Blender and Quaternius are
|
||||||
GLBs and maps.
|
developer tools only — runtime, tests and CI consume committed 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 24,000-triangle / 24,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 24,000 tris / 24,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-visual-review.md#proportion-audit` | 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` |
|
||||||
| `src/test/capture_npc_baseline.gd` | Fixed-camera studio baseline, 8 variants × 6 views | local `tmp/` frames | Neutral silhouette/face/gear review |
|
| `src/test/capture_npc_baseline.gd` | Fixed-camera studio baseline, 8 variants × 6 views | local `tmp/` frames | Neutral silhouette/face/gear review |
|
||||||
| `src/test/capture_npc_review.gd` | Final-context gate: 8 variants × 2 venues × day/night × 3 distances = 96 frames, crowd hidden, review-only `character_override` for the guard | local `tmp/` frames | Pre-build acceptance gate for gear fit and venue lighting |
|
| `src/test/capture_npc_review.gd` | Final-context gate: 8 variants × 2 venues × day/night × 3 distances = 96 frames, crowd hidden, review-only `character_override` for the guard | local `tmp/` frames | Pre-build acceptance gate for gear fit and venue lighting |
|
||||||
@@ -162,21 +238,21 @@ Runtime contracts that must survive any character change:
|
|||||||
- Cloth/pants use `Materials.NPC_FABRIC_UV_SCALE` (4×) and skip the baked per-character AO;
|
- Cloth/pants use `Materials.NPC_FABRIC_UV_SCALE` (4×) and skip the baked per-character AO;
|
||||||
skin/hair/gear keep AO. Do not reintroduce the blotchy cloth AO.
|
skin/hair/gear keep AO. Do not reintroduce the blotchy cloth AO.
|
||||||
- `apply_lod()` swaps to an impostor at 26 m; the 24-NPC budget smoke (6.94 ms, 168 meshes,
|
- `apply_lod()` swaps to an impostor at 26 m; the 24-NPC budget smoke (6.94 ms, 168 meshes,
|
||||||
131,616 triangles) is the performance gate. Changing either triangle cap requires
|
152,464 triangles) is the performance gate. Changing either triangle cap requires
|
||||||
re-measuring that smoke and the Web/Compatibility path.
|
re-measuring that smoke and the Web/Compatibility path.
|
||||||
- Rebuild only the assets whose geometry changed (Blender GLB export is byte-nondeterministic),
|
- Rebuild only the assets whose geometry changed (Blender GLB export is byte-nondeterministic),
|
||||||
then run `godot --headless --path . --import`; never commit a no-op full rebuild.
|
then run `godot --headless --path . --import`; never commit a no-op full rebuild.
|
||||||
|
|
||||||
Evidence and detailed contracts live in [`blender-assets.md`](blender-assets.md),
|
Evidence and detailed contracts live in [`blender-assets.md`](blender-assets.md),
|
||||||
[`npc-visual-review.md`](npc-visual-review.md) and
|
[`npc-visual-review.md`](npc-visual-review.md) and
|
||||||
[`npc-proportion-audit.md`](npc-proportion-audit.md); command-level usage is in
|
[`npc-visual-review.md#proportion-audit`](npc-visual-review.md#proportion-audit); command-level usage is in
|
||||||
[`asset-tooling.md`](asset-tooling.md).
|
[`asset-tooling.md`](asset-tooling.md).
|
||||||
|
|
||||||
### Local map development tooling (planned)
|
### Local map development tooling (planned)
|
||||||
|
|
||||||
The current venues are hand-authored procedural reconstructions in
|
The current venues are hand-authored procedural reconstructions in
|
||||||
`src/game/world_builder.gd`. The **Local map development** track in
|
`src/game/world_builder.gd`. The local map workstreams (**H–M**) in
|
||||||
[`TODO.md`](../TODO.md#local-map-development) is unversioned and planned: it will
|
[`TODO.md`](../TODO.md#3d-model-re-evaluation) are unversioned and planned: they will
|
||||||
test whether licensed data and reference photography can replace or supplement the
|
test whether licensed data and reference photography can replace or supplement the
|
||||||
stylized geometry without moving map data, AI composition or splat rendering into
|
stylized geometry without moving map data, AI composition or splat rendering into
|
||||||
the runtime.
|
the runtime.
|
||||||
@@ -312,6 +388,7 @@ Architecture decisions are recorded as ADRs:
|
|||||||
- [ADR-0008: Cone focus and focus circle](adr/0008-cone-focus-and-head-ring.md)
|
- [ADR-0008: Cone focus and focus circle](adr/0008-cone-focus-and-head-ring.md)
|
||||||
- [ADR-0009: Radial action menu](adr/0009-radial-action-menu.md)
|
- [ADR-0009: Radial action menu](adr/0009-radial-action-menu.md)
|
||||||
- [ADR-0010: Procedural NPC presentation](adr/0010-procedural-npc-presentation.md)
|
- [ADR-0010: Procedural NPC presentation](adr/0010-procedural-npc-presentation.md)
|
||||||
|
- [ADR-0017: Quaternius NPC base](adr/0017-quaternius-npc-base.md)
|
||||||
- [ADR-0011: Aim-free selection and precision walk](adr/0011-aim-free-selection-and-precision-walk.md)
|
- [ADR-0011: Aim-free selection and precision walk](adr/0011-aim-free-selection-and-precision-walk.md)
|
||||||
- [ADR-0012: Surface materials and generated props](adr/0012-surface-materials-and-generated-props.md)
|
- [ADR-0012: Surface materials and generated props](adr/0012-surface-materials-and-generated-props.md)
|
||||||
- [ADR-0013: Blender-authored NPC characters and baked materials](adr/0013-blender-authored-npc-characters.md)
|
- [ADR-0013: Blender-authored NPC characters and baked materials](adr/0013-blender-authored-npc-characters.md)
|
||||||
@@ -359,17 +436,21 @@ architecture document.
|
|||||||
| Document | Canonical role | Relationship to this architecture |
|
| Document | Canonical role | Relationship to this architecture |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [`PLAN.md`](../PLAN.md) | Product/design intent, pillars, venues and success criteria | Design source; architecture realizes it |
|
| [`PLAN.md`](../PLAN.md) | Product/design intent, pillars, venues and success criteria | Design source; architecture realizes it |
|
||||||
| [`docs/gameplay.md`](gameplay.md) | Playable loop, escalation, venue mechanics | Runtime/behavioral view |
|
| [`docs/gameplay.md`](gameplay.md) | Playable loop, escalation, tutorial, venue mechanics | Runtime/behavioral view |
|
||||||
|
| [`docs/venues.md`](venues.md) | Real-site evidence, mapping decisions and deliberate simplifications | Runtime venues' evidence companion to §5 |
|
||||||
| [`docs/architecture.md`](architecture.md) | System boundaries, data flow, constraints, deployment, cross-cutting decisions, quality risks | Single architecture source |
|
| [`docs/architecture.md`](architecture.md) | System boundaries, data flow, constraints, deployment, cross-cutting decisions, quality risks | Single architecture source |
|
||||||
| [`docs/adr/`](adr/) | Immutable decision records with context and consequences | Decision history linked from §9 |
|
| [`docs/adr/`](adr/) | Immutable decision records with context and consequences | Decision history linked from §9 |
|
||||||
| [`docs/asset-tooling.md`](asset-tooling.md) | Build/audit/capture commands and pipeline usage | Operational appendix to the pipeline section |
|
| [`docs/asset-tooling.md`](asset-tooling.md) | Build/audit/capture commands, optional Blender MCP and pipeline usage | Operational appendix to the pipeline section |
|
||||||
| [`docs/blender-assets.md`](blender-assets.md) | Blender character/gear/prop contracts and attach points | Detailed asset contract appendix |
|
| [`docs/blender-assets.md`](blender-assets.md) | Blender library art direction, roles, layout and checks | Operational asset appendix; contract lives here in §5 |
|
||||||
| [`docs/npc-visual-review.md`](npc-visual-review.md) | NPC baseline views, acceptance criteria and ranked visual gaps | Visual-QA appendix to the character tooling section |
|
| [`docs/npc-visual-review.md`](npc-visual-review.md) | NPC baseline views, acceptance criteria, visual gaps and proportion audit | Visual-QA appendix to the character tooling section |
|
||||||
| [`docs/npc-proportion-audit.md`](npc-proportion-audit.md) | Measured proportions against the stylized guide | Measurement appendix to the character tooling section |
|
| [`docs/3d-model-review.md`](3d-model-review.md) | Scorecard, measurements and ranked asset gaps | Working review companion to §5 |
|
||||||
| [`docs/npc-model-sourcing.md`](npc-model-sourcing.md) | Character-source evaluation, licence rules and the character-development plan | Supports the character tooling section and ADR-0015 |
|
| [`docs/npc-model-sourcing.md`](npc-model-sourcing.md) | Character-source evaluation and licence/prototype outcome | Supports the character tooling section and ADR-0015 |
|
||||||
| [`docs/getting-started.md`](getting-started.md) | Setup, commands, controls | Developer onboarding |
|
| [`docs/getting-started.md`](getting-started.md) | Setup, commands, controls | Developer onboarding |
|
||||||
| [`docs/RELEASING.md`](RELEASING.md) | Release checklist and CI/publishing steps | Deployment appendix to §7 |
|
| [`docs/RELEASING.md`](RELEASING.md) | Release checklist and CI/publishing steps | Deployment appendix to §7 |
|
||||||
| [`docs/smoke-checklist.md`](smoke-checklist.md) | Manual verification script | QA appendix to §10 |
|
| [`docs/release-qa.md`](release-qa.md) | Manual smoke checklist and balance protocol | QA appendix to §10 |
|
||||||
|
| [`docs/accessibility.md`](accessibility.md) | Accessibility features, statement and known gaps | Player-facing quality statement |
|
||||||
|
| [`docs/known-limitations.md`](known-limitations.md) | Release blockers and accepted limitations | Player/release-facing risk statement |
|
||||||
|
| [`docs/ui-copy.md`](ui-copy.md) | Screen/button inventory, copy audit and screen-judge report | Presentation appendix |
|
||||||
| [`docs/development-status.md`](development-status.md) | Implemented features and current gaps | Status companion to the roadmap |
|
| [`docs/development-status.md`](development-status.md) | Implemented features and current gaps | Status companion to the roadmap |
|
||||||
| [`TODO.md`](../TODO.md) | Priority-ordered active tasks, unversioned development tracks and versioned roadmap | Planning source; no architecture duplication |
|
| [`TODO.md`](../TODO.md) | Priority-ordered active tasks, unversioned development tracks and versioned roadmap | Planning source; no architecture duplication |
|
||||||
| [`CHANGELOG.md`](../CHANGELOG.md) | User-facing release history | Release/version companion |
|
| [`CHANGELOG.md`](../CHANGELOG.md) | User-facing release history | Release/version companion |
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Audit accepts image files or directories. `--role auto` infers common roles from
|
|||||||
```bash
|
```bash
|
||||||
python3 tools/asset_pipeline.py audit assets/textures
|
python3 tools/asset_pipeline.py audit assets/textures
|
||||||
python3 tools/asset_pipeline.py audit assets/images/scenes --role background --json
|
python3 tools/asset_pipeline.py audit assets/images/scenes --role background --json
|
||||||
python3 -m unittest discover -s tools -p 'test_*.py'
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
```
|
```
|
||||||
|
|
||||||
The audit classifies measurable fit issues as `good` or `bad` based on role-specific dimensions, aspect ratio, alpha requirements, and tile-edge continuity. It lists perspective/projection as a visual review item because image semantics cannot be judged reliably by these deterministic checks.
|
The audit classifies measurable fit issues as `good` or `bad` based on role-specific dimensions, aspect ratio, alpha requirements, and tile-edge continuity. It lists perspective/projection as a visual review item because image semantics cannot be judged reliably by these deterministic checks.
|
||||||
@@ -82,6 +82,8 @@ tonemapping, glow, fog, colour grading) that the phase lighting applies.
|
|||||||
|
|
||||||
NPCs are moving from the procedural capsule rig to a skinned, UV-mapped base
|
NPCs are moving from the procedural capsule rig to a skinned, UV-mapped base
|
||||||
character authored in Blender ([ADR-0013](adr/0013-blender-authored-npc-characters.md)).
|
character authored in Blender ([ADR-0013](adr/0013-blender-authored-npc-characters.md)).
|
||||||
|
The active base is the CC0 Quaternius modular character, baked to the runtime contract
|
||||||
|
([ADR-0017](adr/0017-quaternius-npc-base.md)).
|
||||||
`tools/npc_pipeline.py` builds `assets/models/generated/npc_base.glb` plus the
|
`tools/npc_pipeline.py` builds `assets/models/generated/npc_base.glb` plus the
|
||||||
per-slot material maps. `NpcAvatar` instantiates that skinned model, tints its
|
per-slot material maps. `NpcAvatar` instantiates that skinned model, tints its
|
||||||
five material slots from the seed-driven outfit, and drives the bones from the
|
five material slots from the seed-driven outfit, and drives the bones from the
|
||||||
@@ -129,10 +131,10 @@ with colliders/avoidance and drives their emissive parts from the day/night
|
|||||||
cycle. The single `npc_base.glb` and primitive props remain offline fallbacks.
|
cycle. The single `npc_base.glb` and primitive props remain offline fallbacks.
|
||||||
Contract and integration notes: [`blender-assets.md`](blender-assets.md).
|
Contract and integration notes: [`blender-assets.md`](blender-assets.md).
|
||||||
|
|
||||||
Sourcing and expansion research (no external model is shipped yet):
|
Sourcing and migration plan: [`npc-model-sourcing.md`](npc-model-sourcing.md) records the
|
||||||
[`npc-model-sourcing.md`](npc-model-sourcing.md) compares Quaternius, Kenney, Khronos and
|
2026-11-05 direction (ADR-0017). Quaternius Ultimate Modular (CC0) is the primary NPC mesh
|
||||||
MakeHuman sources against the character contract and records the 2026-11-03 direction
|
and animation source, baked to the existing 13-core contract; the procedural generator is
|
||||||
(procedural generator first, Quaternius reference, MakeHuman deferred).
|
the fallback. Kenney/Khronos/MakeHuman remain evaluated references, not active bases.
|
||||||
|
|
||||||
| Characters | Props |
|
| Characters | Props |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -140,32 +142,173 @@ MakeHuman sources against the character contract and records the 2026-11-03 dire
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- build
|
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- build
|
||||||
|
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- build --head mh
|
||||||
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- render
|
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- render
|
||||||
python3 tools/blender_assets.py sheet # docs/blender_previews/sheet_*.jpg
|
python3 tools/blender_assets.py sheet # docs/blender_previews/sheet_*.jpg
|
||||||
python3 tools/blender_assets.py audit # no Blender needed
|
python3 tools/blender_assets.py audit # no Blender needed
|
||||||
|
python3 tools/blender_assets.py fit # gear-vs-body fit report
|
||||||
|
python3 tools/blender_assets.py texels # per-slot texel density (px/m) of the characters
|
||||||
|
# `build --head mh` selects the retained MakeHuman fallback head; the default is still `face`.
|
||||||
godot --headless --path . --import
|
godot --headless --path . --import
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Intake inspection (`inspect`)
|
||||||
|
|
||||||
|
`audit` reads accessor-local bounds and ignores node transforms, so it is the right
|
||||||
|
release gate but a poor first look at a downloaded or newly authored GLB. `inspect`
|
||||||
|
imports the file into Blender, clears parents and applies object transforms, then
|
||||||
|
reports what the scene actually contains:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# one asset, a batch, or an external download; repeat --file as needed
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- inspect \
|
||||||
|
--file "$PWD/assets/models/blender/npc/npc_casual.glb" \
|
||||||
|
--file "$PWD/tmp/downloads/SomeCharacter.glb"
|
||||||
|
# machine-readable, for intake scripts (write to a file: Blender prints a banner first)
|
||||||
|
... -- inspect --file "$PWD/tmp/x.glb" --json --out "$PWD/tmp/x.json"
|
||||||
|
# force a contract family instead of inferring it from the path
|
||||||
|
... -- inspect --file "$PWD/tmp/x.glb" --category npc
|
||||||
|
```
|
||||||
|
|
||||||
|
It prints triangles (with the category budget), raw and seam-split vertex counts,
|
||||||
|
Godot-space bounds with height/feet for characters, whether transforms had to be
|
||||||
|
applied, mesh nodes and armatures, bone names, materials, UV layers, skinning and
|
||||||
|
animation count. Hard contract failures (`issues`) fail the command; advisory
|
||||||
|
findings (`warnings`) do not. The category is inferred from the path:
|
||||||
|
`blender/npc` → `npc`, `blender/npc_gear` → `npc_gear`, `blender/props` and
|
||||||
|
`generated` → `props`, anything else → `external` (measured but with no project
|
||||||
|
budget or rig contract).
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
|
||||||
|
- The glTF importer adds a bone-display mesh (`Icosphere`, used as
|
||||||
|
`pose_bone.custom_shape`). `inspect` excludes custom-shape helpers so they never
|
||||||
|
pollute bounds or the unskinned list.
|
||||||
|
- Bounds and bone rests are converted from Blender Z-up to Godot Y-up (`x, z, -y`),
|
||||||
|
matching the contract bands and the audit's coordinate space.
|
||||||
|
|
||||||
|
### Intake adaptation (`adapt`)
|
||||||
|
|
||||||
|
`adapt` is the mechanical first half of an external-mesh intake (Quaternius,
|
||||||
|
MakeHuman fallback, downloaded GLB): import, apply transforms, ground the feet,
|
||||||
|
weld seam splits, decimate to the character budget, and export a GLB. It does **not** map
|
||||||
|
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
|
||||||
|
source rig cannot be retargeted:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- adapt \
|
||||||
|
--file "$PWD/tmp/npc-research/quaternius/Casual.glb" \
|
||||||
|
--out "$PWD/tmp/npc-research/quaternius_adapted.glb" \
|
||||||
|
--target-tris 24000
|
||||||
|
# prints: adapted quaternius_adapted.glb: <source> -> 24000 tris (bones=N, meshes=1)
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `inspect` on the adapted GLB before and after, and keep the procedural
|
||||||
|
generator as the fallback until a Quaternius variant passes `audit`/`fit` and the
|
||||||
|
24-NPC budget.
|
||||||
|
- Numbers are Blender-side measurements. Triangles match `audit` on project assets; the seam-split
|
||||||
|
vertex count can differ by ±1 because the importer rounds normals. `audit` stays the single final
|
||||||
|
gate: it is stdlib-only, runs in CI, and matches what the engine imports.
|
||||||
|
- `inspect` never writes or adapts the file; scale/grounding/weld/retarget work is the
|
||||||
|
separate (not yet built) `adapt` step.
|
||||||
|
- `--json` also captures the importer's INFO log in `stats.import_log` instead of printing it.
|
||||||
|
Blender still prints its own version banner before the script runs, so intake scripts should use
|
||||||
|
`--json --out <file>` rather than parsing stdout.
|
||||||
|
|
||||||
|
### Reference-anchored concept generation
|
||||||
|
|
||||||
|
`tools/scene_concepts.py generate --reference <photo>` anchors a concept frame on a real
|
||||||
|
photograph: the photo is sent to the OpenRouter multimodal chat endpoint as a base64 data URL
|
||||||
|
(`generate_textures.generate_image(reference_path=...)`) with a prompt suffix that pins the
|
||||||
|
photographed geometry (column rhythm, band heights, signage placement, camera angle) while the
|
||||||
|
prompt restages lighting, weather and people.
|
||||||
|
|
||||||
|
Licence gate: the reference must be listed in its `docs/screens/refs/<venue>/SIDECAR.md` row
|
||||||
|
(which also supplies the licence and Commons page for provenance). A photo from anywhere else
|
||||||
|
requires `--reference-unchecked`, which records `unchecked` in the provenance. Each anchored
|
||||||
|
generation writes a `.refgen.json` sidecar next to the frame (`model`, reference file/licence/page,
|
||||||
|
geometry suffix) so a concept frame can always be traced back to its evidence photo. Where a
|
||||||
|
generated frame and its reference photo disagree, the photo wins for shape.
|
||||||
|
|
||||||
|
### Photo-anchored variant pass
|
||||||
|
|
||||||
|
`tools/scene_concepts.py anchor --reference <photo> --context "..." --treatments day,night,weathered
|
||||||
|
--out-dir docs/screens/concepts/<block>` generates several restaged conditions (day, night,
|
||||||
|
weathered) of one reference photo's framing, each with its `.refgen.json` provenance sidecar, so a
|
||||||
|
concept board stays comparable with its photo board. The reference passes the same SIDECAR licence
|
||||||
|
gate as `--reference` on `generate`.
|
||||||
|
|
||||||
|
### Local-only reference collection (`refcollect`)
|
||||||
|
|
||||||
|
`tools/refcollect.py` downloads images from DuckDuckGo image search into `tmp/research/<slug>/` for
|
||||||
|
development reference only. It applies **no licence gate** (rows are marked `local_only` /
|
||||||
|
`unchecked-local`); everything it fetches must never be committed or promoted into
|
||||||
|
[`screens/refs/`](screens/refs/README.md) without the normal sidecar licence + privacy gate. This
|
||||||
|
is the owner-authorised local-only override (TODO.md §3D model re-evaluation → A).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/refcollect.py search "Comedy Store Sunset Boulevard" --slug comedy-store --limit 20
|
||||||
|
python3 tools/refcollect.py sheet --slug comedy-store # local contact sheet (never committed)
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires `pip install duckduckgo-search` (renamed `ddgs` upstream). DuckDuckGo rate-limits by IP
|
||||||
|
(HTTP 202), so every request — the search itself and each image download — passes through a shared
|
||||||
|
token bucket (`--rate`, default 0.5 req/s, and `--burst`, default 3) and a polite retry routine:
|
||||||
|
transient statuses (408/425/429/5xx/52x) and network errors back off exponentially with full
|
||||||
|
jitter, `Retry-After` is honoured, permanent failures fail fast, and the loop gives up after six
|
||||||
|
attempts. Requests carry a full, coherent Firefox header set (`--user-agent` overrides the UA
|
||||||
|
string); the UA alone is no longer enough to look like a browser.
|
||||||
|
|
||||||
|
### Reference-board research (`refboard`)
|
||||||
|
|
||||||
|
`tools/refboard.py` searches, fetches and documents Wikimedia Commons photos of a real location for
|
||||||
|
the [`screens/refs/`](screens/refs/README.md) boards. It is stdlib-only and never commits anything
|
||||||
|
itself: downloads land in `tmp/`, and moving a file into a board folder is a deliberate manual step
|
||||||
|
that goes through the sidecar licence gate.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# search: keyword, geosearch (the reliable "photos of this exact place"), or category
|
||||||
|
python3 tools/refboard.py search --source geo "34.10118|-118.34009" --limit 60 --months 12
|
||||||
|
python3 tools/refboard.py search "El Capitan Entertainment Centre" --source search
|
||||||
|
python3 tools/refboard.py search "Hollywood Masonic Temple" --source category --json
|
||||||
|
# fetch the usable rows into tmp/ (review-only rows need --include-review)
|
||||||
|
python3 tools/refboard.py search ... --json > /tmp/manifest.json
|
||||||
|
python3 tools/refboard.py fetch --from /tmp/manifest.json --dir tmp/research/<block> --max 12
|
||||||
|
# emit SIDECAR + README table rows from the fetched manifest
|
||||||
|
python3 tools/refboard.py rows --from tmp/research/<block>/meta.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Licence gate: `Public domain`, `CC0` and `CC BY` are committable with attribution; `CC BY-SA` rows
|
||||||
|
are flagged `review` and stay research-only until the share-alike decision is written into the
|
||||||
|
sidecar (`--allow-ccbysa` marks them committable after that review); anything else is skipped.
|
||||||
|
`--months 12` flags recent captures so a "current reality" photo can be distinguished from the
|
||||||
|
older geometry evidence. Network gotchas baked in: a descriptive User-Agent, batched `titles=`
|
||||||
|
metadata queries and ~3 s download spacing to avoid Commons HTTP 429s.
|
||||||
|
|
||||||
|
### Texel density (`texels`)
|
||||||
|
|
||||||
|
`texel_density()` and the `texels` command compute `resolution * sqrt(UV area / world area)` per
|
||||||
|
material slot straight from the GLB, so the number does not depend on island packing. The NPC slot
|
||||||
|
maps are 256², and cloth/pants are reported twice: authored density and effective density with the
|
||||||
|
runtime 4× repeat from `Materials.NPC_FABRIC_UV_SCALE`. Current values and interpretation are in
|
||||||
|
[`3d-model-review.md`](3d-model-review.md).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/blender_assets.py texels # all eight characters
|
||||||
|
python3 tools/blender_assets.py texels --json # machine-readable
|
||||||
|
python3 tools/blender_assets.py texels --only npc_coat
|
||||||
|
```
|
||||||
|
|
||||||
## Local map development (planned)
|
## Local map development (planned)
|
||||||
|
|
||||||
The **Local map development** track in [`TODO.md`](../TODO.md#local-map-development)
|
The local map workstreams (H–M) in [`TODO.md`](../TODO.md#3d-model-re-evaluation) are
|
||||||
is not implemented yet; there are no map-generation commands to run. The planned
|
planned, not implemented; there are no map-generation commands yet. The runtime
|
||||||
pipeline will evaluate:
|
contract, provenance rules, budget/navigation gates and fallback are in
|
||||||
|
[`architecture.md#venue-implementation-contract`](architecture.md#venue-implementation-contract).
|
||||||
- offline OpenStreetMap extracts for footprints and licence-compatible 3D building
|
The detailed pipeline/task list lives in `TODO.md`.
|
||||||
massing (OSM Buildings, Overture, CityGML/LOD1–2);
|
|
||||||
- downloaded reference imagery from Wikimedia Commons, own/team photography or
|
|
||||||
other clearly licensed sources, with author/licence/attribution and privacy
|
|
||||||
redaction recorded per set;
|
|
||||||
- AI-assisted composition boards for palette, facade rhythm and material study,
|
|
||||||
with model/prompt/output provenance and human acceptance;
|
|
||||||
- Gaussian splatting as a development-only capture/reference/texture/proxy tool,
|
|
||||||
version-pinned offline and outside CI, until an ADR proves runtime safety.
|
|
||||||
|
|
||||||
No online map/photo/splat service is a build or play dependency. Committed map
|
|
||||||
assets will need provenance sidecars, ODbL/photo-licence review and the same
|
|
||||||
`WorldBuilder` venue-budget/navigation/collider gates as the current stylized
|
|
||||||
geometry. The detailed task plan and exit criteria live in `TODO.md`.
|
|
||||||
|
|
||||||
## Confirmed game assets
|
## Confirmed game assets
|
||||||
|
|
||||||
@@ -191,6 +334,162 @@ python3 tools/model_pipeline.py audit
|
|||||||
python3 tools/npc_pipeline.py audit
|
python3 tools/npc_pipeline.py audit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Optional interactive 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
|
||||||
|
|
||||||
|
The headless part pipeline is `tools/npc_parts.py` (see
|
||||||
|
[`blender-assets.md#local-part-pipeline`](blender-assets.md#local-part-pipeline)). It builds,
|
||||||
|
extracts, assembles, inspects and sheets the reusable head/face and hand parts and reuses the
|
||||||
|
`blender_assets.py` builders, so there is no second face/hand implementation. The MCP steps below
|
||||||
|
remain optional interactive exploration; the headless commands stay the build and audit path.
|
||||||
|
|
||||||
|
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/`; a part GLB is not a shipped asset.
|
||||||
|
- 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 |
|
||||||
|
|
||||||
## 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
|
||||||
@@ -225,6 +524,16 @@ and ranks them by advertised token/image price. The run falls through the list
|
|||||||
on provider/model errors and aborts on an invalid key. Use
|
on provider/model errors and aborts on an invalid key. Use
|
||||||
`GIG_LINE_JUDGE_MODEL` or `--model <id>` for a pinned comparison.
|
`GIG_LINE_JUDGE_MODEL` or `--model <id>` for a pinned comparison.
|
||||||
|
|
||||||
|
For an open-ended review of a single capture/reference (no structured verdict),
|
||||||
|
`tools/describe_image.py` sends the image plus a question to a vision model and
|
||||||
|
prints the answer. It is how the 2026-11-03 re-evaluation read the local-only
|
||||||
|
reference frames and the prop/venue captures:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/describe_image.py docs/screenshots/12-joke-shop.png \
|
||||||
|
--question "Do the red curtain bays read as tied-back fabric or flat slabs?"
|
||||||
|
```
|
||||||
|
|
||||||
`capture_npc_review.gd` is the pre-build NPC gate: it stages all eight
|
`capture_npc_review.gd` is the pre-build NPC gate: it stages all eight
|
||||||
library character models at portrait, conversation and crowd distance on both
|
library character models at portrait, conversation and crowd distance on both
|
||||||
promoted maps in day and night lighting, with the normal crowd hidden. It uses
|
promoted maps in day and night lighting, with the normal crowd hidden. It uses
|
||||||
@@ -244,9 +553,25 @@ contract.
|
|||||||
|
|
||||||
`tools/npc_proportions.py` checks the eight character GLBs against the shared
|
`tools/npc_proportions.py` checks the eight character GLBs against the shared
|
||||||
height/head/shoulder/hip/torso/leg/arm/foot guide; the current results are in
|
height/head/shoulder/hip/torso/leg/arm/foot guide; the current results are in
|
||||||
[`npc-proportion-audit.md`](npc-proportion-audit.md). It reads the exported GLBs
|
[`npc-visual-review.md`](npc-visual-review.md#proportion-audit). It reads the exported GLBs
|
||||||
and does not require Blender.
|
and does not require Blender.
|
||||||
|
|
||||||
|
`capture_npc_pose.gd` is the pose/deformation harness for the 3D re-evaluation: idle,
|
||||||
|
walk-contact, walk-pass and seated poses of all eight variants, full-body plus shoulder
|
||||||
|
close-ups. **It is currently blocked**: the applied pose is visible in the rig and bone values
|
||||||
|
but not in the captured frames, so do not use its sheets as pose evidence. The reproduction and
|
||||||
|
what was ruled out are in [`3d-model-review.md`](3d-model-review.md) §"Pose evidence is blocked".
|
||||||
|
|
||||||
|
The baseline sheet tool accepts a custom view list, which is also how pose sheets are tiled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# baseline set (six views, eight variants)
|
||||||
|
python3 tools/npc_baseline.py sheet --dir tmp/npc-baseline
|
||||||
|
# pose set produced by capture_npc_pose.gd
|
||||||
|
python3 tools/npc_baseline.py sheet --dir tmp/npc-pose \
|
||||||
|
--views idle,walk_contact,walk_pass,sit --out tmp/npc-pose/sheet_pose.jpg
|
||||||
|
```
|
||||||
|
|
||||||
`capture_npc_baseline.gd` is the 0.9.4 neutral baseline: eight characters ×
|
`capture_npc_baseline.gd` is the 0.9.4 neutral baseline: eight characters ×
|
||||||
front/three-quarter/profile/back/full-body/crowd = 48 frames with a fixed
|
front/three-quarter/profile/back/full-body/crowd = 48 frames with a fixed
|
||||||
camera, studio lighting and no venue/HUD/animation. The raw PNGs stay in
|
camera, studio lighting and no venue/HUD/animation. The raw PNGs stay in
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
Architecture context: the asset contracts and fallback path are summarized in
|
Architecture context: the asset contracts and fallback path are summarized in
|
||||||
[`architecture.md`](architecture.md#content-and-asset-pipeline-architecture).
|
[`architecture.md`](architecture.md#content-and-asset-pipeline-architecture).
|
||||||
|
|
||||||
Status: built, audited, and integrated (2026-11-01). `NpcAvatar` selects the
|
Status: built, audited, and integrated (2026-11-01). The active character migration
|
||||||
|
replaces the procedural body with a baked CC0 Quaternius base
|
||||||
|
([ADR-0017](adr/0017-quaternius-npc-base.md)); the current library remains the shipped
|
||||||
|
fallback until the Quaternius 12-variant baseline passes review. `NpcAvatar` selects the
|
||||||
character variants and attaches the gear; `WorldBuilder` loads the prop library
|
character variants and attaches the gear; `WorldBuilder` loads the prop library
|
||||||
with colliders and phase-driven emissives. The 0.9.4 neutral baseline and its
|
with colliders and phase-driven emissives. The 0.9.4 neutral baseline and its
|
||||||
acceptance criteria are in [`npc-visual-review.md`](npc-visual-review.md);
|
acceptance criteria are in [`npc-visual-review.md`](npc-visual-review.md);
|
||||||
@@ -21,7 +24,7 @@ captures in `docs/screenshots/`.
|
|||||||
|
|
||||||
- **People** (`targeting_npc`, `joke_shop_queue`, `studio_arrival`,
|
- **People** (`targeting_npc`, `joke_shop_queue`, `studio_arrival`,
|
||||||
`crowd_panic`, `responder_arrival`): ordinary adults in sweaters, jackets,
|
`crowd_panic`, `responder_arrival`): ordinary adults in sweaters, jackets,
|
||||||
long coats, hoodies and beanies; a security guard in a dark uniform with a cap
|
long coats and hoodies; a security guard in a dark uniform with a cap
|
||||||
and belt; a firefighter in a dark turnout coat with yellow reflective bands,
|
and belt; a firefighter in a dark turnout coat with yellow reflective bands,
|
||||||
a helmet and an SCBA tank. The current skinned `npc_base` is a set of boxes
|
a helmet and an SCBA tank. The current skinned `npc_base` is a set of boxes
|
||||||
and cylinders with a flat face, so silhouette was the main gap.
|
and cylinders with a flat face, so silhouette was the main gap.
|
||||||
@@ -35,6 +38,60 @@ 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.
|
||||||
|
|
||||||
|
## Frozen fallback boundary
|
||||||
|
|
||||||
|
`assets/models/npc/npc_base.blend` is the tracked, frozen plain-primitive
|
||||||
|
fallback. Prototype head/face/hand detail from the local part pipeline must not
|
||||||
|
be saved into it; a separate contract decision (ADR or owner sign-off) is
|
||||||
|
required before any prototype detail is absorbed. Working scenes live under
|
||||||
|
`tmp/npc-parts/` (gitignored) and are exported as local GLBs for review, never
|
||||||
|
directly over the tracked `.blend`. The same rule already appears in
|
||||||
|
[`asset-tooling.md#recommended-part-workflow`](asset-tooling.md#recommended-part-workflow).
|
||||||
|
|
||||||
|
## Local part pipeline
|
||||||
|
|
||||||
|
The overhaul uses `tools/npc_parts.py` to build, extract, assemble, inspect and sheet the
|
||||||
|
reusable head/face and hand parts before they are rolled into the 12 characters. It reuses the
|
||||||
|
`blender_assets.py` builders (no second face/hand implementation) and stays local-only/headless.
|
||||||
|
Part sources and preview GLBs live under `tmp/npc-parts/<variant>/`; they are **not shipped
|
||||||
|
assets** — only the assembled character GLBs, AO/maps, the manifest and the review sheets are
|
||||||
|
committed.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build one part fresh from the current builders (head|face|hand_l|hand_r)
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_parts.py" -- build --part face --variant npc_casual \
|
||||||
|
--out "$PWD/tmp/npc-parts/npc_casual/face.glb"
|
||||||
|
# Pull the current shipped head/hands out of the built character GLB (comparison)
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_parts.py" -- extract --part hand_l --variant npc_casual \
|
||||||
|
--out "$PWD/tmp/npc-parts/npc_casual/hand_l.glb"
|
||||||
|
# Assemble a variant (current fallback path; Quaternius migration in progress)
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_parts.py" -- assemble --variant npc_casual \
|
||||||
|
--out "$PWD/tmp/npc-parts/npc_casual/assembled.glb"
|
||||||
|
# Inspect a part or assembled GLB (Blender import view)
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_parts.py" -- inspect --file "$PWD/tmp/npc-parts/npc_casual/face.glb"
|
||||||
|
# Contact sheet / before-after comparison (outside Blender; stdlib + Pillow)
|
||||||
|
python3 tools/npc_parts.py sheet --dir tmp/npc-parts/npc_casual \
|
||||||
|
--out docs/blender_previews/sheet_parts.jpg
|
||||||
|
python3 tools/npc_parts.py compare --before DIR --after DIR --out sheet.jpg
|
||||||
|
```
|
||||||
|
|
||||||
|
`build --part head` produces the bare head shell; `build --part face` is the shell plus
|
||||||
|
nose/eyes/mouth/ears; `build --part hand_l|hand_r` are the left/right hand geometry. `extract`
|
||||||
|
pulls the current shipped geometry out of the built character GLB by vertex group (`Head` for
|
||||||
|
`head`/`face`, `HandL`/`HandR` for the hands), so it shows what is actually shipped rather than
|
||||||
|
what the builders would emit fresh. The assembled GLB must still pass
|
||||||
|
`blender_assets.py inspect`/`audit`/`fit` before any part is accepted.
|
||||||
|
|
||||||
|
## 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 [`asset-tooling.md#optional-interactive-blender-mcp`](asset-tooling.md#optional-interactive-blender-mcp) for installation, connection and safety.
|
||||||
|
|
||||||
## Running it
|
## Running it
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -47,7 +104,11 @@ flatpak run org.blender.Blender --background \
|
|||||||
python3 tools/blender_assets.py sheet # -> docs/blender_previews/sheet_<category>.jpg
|
python3 tools/blender_assets.py sheet # -> docs/blender_previews/sheet_<category>.jpg
|
||||||
python3 tools/blender_assets.py audit # stdlib only; exit 1 on any issue
|
python3 tools/blender_assets.py audit # stdlib only; exit 1 on any issue
|
||||||
python3 tools/blender_assets.py fit # head/clothing fit + bag-body clearance, per character
|
python3 tools/blender_assets.py fit # head/clothing fit + bag-body clearance, per character
|
||||||
|
python3 tools/blender_assets.py texels # 256² slot-map density (authored + 4× fabric), per character
|
||||||
python3 tools/blender_assets.py list
|
python3 tools/blender_assets.py list
|
||||||
|
# import a GLB, apply node transforms, report geometry/bones/UVs/skinning + contract failures
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- inspect --file "$PWD/tmp/downloads/x.glb"
|
||||||
godot --headless --path . --import # after any rebuild
|
godot --headless --path . --import # after any rebuild
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,35 +142,22 @@ render about 2.5 min.
|
|||||||
|
|
||||||
## Characters
|
## Characters
|
||||||
|
|
||||||
All eight use the `npc_base` contract, so each one can replace
|
All library variants share the
|
||||||
`npc_base.glb` without code changes:
|
[character and gear asset contract](architecture.md#character-and-gear-asset-contract):
|
||||||
|
`npc_base` mesh node, five tint slots, +Z visual front, 24,000/24,000 character budget and the
|
||||||
|
same 256² slot-map/AO rules. The shipped roster is on the 62-joint Quaternius skeleton
|
||||||
|
(ADR-0018); the procedural fallback built by `build --source procedural` keeps the 13 core bones.
|
||||||
|
Both are accepted by `audit`, `inspect`, the runtime bone map and the tests.
|
||||||
|
|
||||||
- mesh node `npc_base` under an armature that imports as `Skeleton3D`
|
The three Quaternius sources (`casual`, `hoodie`, `worker`) live in
|
||||||
- bones `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`
|
`assets/models/blender_src/quaternius/` as build inputs. `build` (default
|
||||||
(plus `Hips`, `Spine`, `Neck`) at the **same rest positions as `npc_base`**.
|
`--source quaternius`) does a two-pass bake per character: a cached T-pose round trip that joins
|
||||||
`test_character_joints_line_up_with_npc_base` checks this within 1 cm, so the
|
without welding, maps the upstream materials onto the five slots and unwraps the degenerate
|
||||||
animation proxies in `_build_skinned_body()` stay valid.
|
source UVs, then an arm-down rest bake exported with `export_apply=False`. The per-character slot
|
||||||
- material slots `skin`, `cloth`, `pants`, `accessory`, `hair` carry the same
|
map, triangle counts and measurements are in
|
||||||
neutral `assets/textures/npc/<slot>_{albedo,normal}.png` maps at 256², so
|
[`npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap`](npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap).
|
||||||
`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
|
|
||||||
per-character baked AO; skin, hair and fixed gear keep it.
|
|
||||||
- budget ≤ **8,000 triangles and ≤ 8,000 glTF vertices** for the eight library
|
|
||||||
variants (vertex count after seam splits; `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in
|
|
||||||
`tools/blender_assets.py`, raised from 6,000/5,000 on 2026-11-02). The frozen
|
|
||||||
`npc_base` fallback keeps its original 6,000/4,000 contract. The current range
|
|
||||||
is 4,482–5,474 triangles and 4,260–5,494 vertices, leaving room for the
|
|
||||||
remaining grip and clothing detail.
|
|
||||||
|
|
||||||
Construction: the torso and limbs are one continuous Skin-modifier mesh,
|
Role selection and look notes (tint + gear now carry the role, not the mesh):
|
||||||
subdivided twice, decimated to 26 % with X symmetry, and cut at the waist, cuffs,
|
|
||||||
sleeves and neck so the material borders are clean edge loops. The head is a
|
|
||||||
separate part carrying the face detail: brow ridge, sclera/iris/upper-lid per eye,
|
|
||||||
cheekbones, a nose with a bridge and tip, ears with lobes, and upper and lower
|
|
||||||
lips. Hands have four fingers plus a thumb; sleeves end in a cuff ring.
|
|
||||||
Hair, shoes and clothing layers are separate parts. Weights come from distance to
|
|
||||||
the bone segments, limited by body region. Parts that should stay rigid (head,
|
|
||||||
hands, shoes) get a single bone.
|
|
||||||
|
|
||||||
| Character | `layer_for()` / look | Suggested roles | Preview gear |
|
| Character | `layer_for()` / look | Suggested roles | Preview gear |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
@@ -128,6 +176,11 @@ colour texture. The current `_apply_outfit_materials()` sets
|
|||||||
`albedo_color = WHITE` on any slot it does not recognise, and these keep their
|
`albedo_color = WHITE` on any slot it does not recognise, and these keep their
|
||||||
colour through that. When integrating, tint only the five contract slots.
|
colour through that. When integrating, tint only the five contract slots.
|
||||||
|
|
||||||
|
**Head gear fit.** Head gear is authored for the shipped Quaternius head:
|
||||||
|
`HEAD_GEAR_SCALE = 0.64` (0.201 m base head against the 0.315 m procedural one) and
|
||||||
|
`HEAD_GEAR_LIFT = 0.07` (its centre sits higher). The procedural fallback therefore wears slightly
|
||||||
|
undersized caps — cosmetic and fallback-only.
|
||||||
|
|
||||||
**Runtime tint notes (implemented).** Only the five contract slots are tinted;
|
**Runtime tint notes (implemented).** Only the five contract slots are tinted;
|
||||||
fixed detail materials keep their authored colour. On `npc_vest` the vest is the
|
fixed detail materials keep their authored colour. On `npc_vest` the vest is the
|
||||||
fixed `hivis` material and `cloth` is the undershirt, so the yellow override is
|
fixed `hivis` material and `cloth` is the undershirt, so the yellow override is
|
||||||
@@ -136,22 +189,8 @@ the role's `TYPE_COLORS`.
|
|||||||
|
|
||||||
## Gear attach points
|
## Gear attach points
|
||||||
|
|
||||||
Gear is authored in attach-point space, so the origin of each GLB is its
|
Gear is authored in attach-point space; the exact contract and runtime fit offsets are in
|
||||||
attach point:
|
[`architecture.md#character-and-gear-asset-contract`](architecture.md#character-and-gear-asset-contract).
|
||||||
|
|
||||||
| Attach | Godot parent / position | Gear |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `head` | `Rig/Head` marker (0, 1.62, 0) | cap, beanie, hood, sunglasses, firefighter_helmet |
|
|
||||||
| `back` | `Rig` at (0, 1.10, 0), spine centre; items sit behind the body (Blender +Y = Godot −Z) | backpack, shoulder_bag, scba_tank |
|
|
||||||
| `chest` | `Rig` at (0, 1.30, 0), sternum | lanyard |
|
|
||||||
| `hand` | Hand proxy (±0.27, 0.78, 0.02); grip 5 cm below the origin | phone, cup, clipboard, umbrella, camera, protest_sign, flashlight, wand, radio |
|
|
||||||
|
|
||||||
The character previews in `sheet_npc.jpg` are built by importing the gear at
|
|
||||||
exactly these points, which gives a quick visual check of the convention.
|
|
||||||
`NpcAvatar.GEAR_OFFSETS` applies two final-context nudges after attachment:
|
|
||||||
the lanyard moves 4 cm forward so its badge clears the jacket, and the
|
|
||||||
firefighter helmet moves 3 cm up so its brim sits above the eyes. The
|
|
||||||
`fit` report remains an asset-space measurement of the untouched GLBs.
|
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
@@ -181,16 +220,22 @@ 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 24000/24000, 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/test_blender_assets.py`: catalogue and contract consistency with
|
- `tools/tests/test_blender_assets.py`: catalogue and contract consistency with
|
||||||
`npc_pipeline.py`, plus a clean audit and full manifest coverage when the
|
`npc_pipeline.py`, plus a clean audit, a texel-density gate (all five slots,
|
||||||
library is built.
|
40–400 px/m authored, 250 px/m fabric floor) and full manifest coverage when
|
||||||
|
the library is built.
|
||||||
- `src/test/tests/test_blender_assets.gd`: every asset imports as a scene
|
- `src/test/tests/test_blender_assets.gd`: every asset imports as a scene
|
||||||
with geometry, characters keep their bones and slots, joints match
|
with geometry, characters keep their bones and slots, joints match
|
||||||
`npc_base`, the bones accept `NpcAvatar`'s pose composition, and props are
|
`npc_base`, the bones accept `NpcAvatar`'s pose composition, and props are
|
||||||
grounded.
|
grounded.
|
||||||
|
- `tools/test_model_review.py`: every `CHARACTERS`/`GEAR`/`PROPS` entry has a
|
||||||
|
scorecard row and verdict in `docs/3d-model-review.md` (coverage gate).
|
||||||
|
- `src/test/tests/test_world.gd`: every library prop that blocks walking has a
|
||||||
|
`StaticBody3D` + `NavigationObstacle3D`, checked against the catalogue
|
||||||
|
(collider/proxy gate).
|
||||||
|
|
||||||
## Known limitations
|
## Known limitations
|
||||||
|
|
||||||
@@ -206,6 +251,8 @@ carry baked AO and the shared neutral material maps described above.
|
|||||||
stay neutral.
|
stay neutral.
|
||||||
- The 0.9.4 baseline found three P1 portrait issues. The tourist's photo prop
|
- The 0.9.4 baseline found three P1 portrait issues. The tourist's photo prop
|
||||||
now only appears in photo/film poses and the backpack straps were refit as
|
now only appears in photo/film poses and the backpack straps were refit as
|
||||||
flattened chest bands; the face is still shallow/faceted at portrait range.
|
flattened chest bands. The face carries the 0.9.4 sculpt (socketed sclera,
|
||||||
The full ranked list, measurements and before/after sheet are in
|
chin/cheekbone, nose, lips) but its `seg=18/rings=12` head still reads
|
||||||
[`npc-visual-review.md`](npc-visual-review.md).
|
somewhat faceted at portrait range; a subdivision/deeper-sculpt prototype is
|
||||||
|
gated on the owner scorecard review. The full ranked list and measurements are
|
||||||
|
in [`npc-visual-review.md`](npc-visual-review.md).
|
||||||
@@ -1,71 +1,33 @@
|
|||||||
# Blender Previews
|
# Blender previews
|
||||||
|
|
||||||
Committed contact sheets and local Blender renders for the character, gear and prop library. Individual PNGs are development previews and are gitignored; the sheets are tracked.
|
Committed contact sheets for the character, gear and prop library. Per-asset Cycles renders
|
||||||
|
(`*.png`) are local development output and gitignored; the sheets below are the review artefacts
|
||||||
|
and are built with `blender_assets.py render` + `sheet`, `npc_baseline.py sheet/compare`, or the
|
||||||
|
capture harnesses.
|
||||||
|
|
||||||
## Image previews
|
| Sheet | What it records |
|
||||||
|
|
||||||
| Preview | File |
|
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| <img src="a_frame_sign.png" width="280" alt="a_frame_sign.png"> | [`a_frame_sign.png`](a_frame_sign.png) |
|
| [](sheet_npc.jpg) <br> `sheet_npc.jpg` | Eight skinned character variants (casual, jacket, coat, hi-vis vest, dress, hoodie, firefighter, guard) with their gear attach points. |
|
||||||
| <img src="backpack.png" width="280" alt="backpack.png"> | [`backpack.png`](backpack.png) |
|
| [](sheet_npc_baseline.jpg) <br> `sheet_npc_baseline.jpg` | Neutral studio baseline: 8 characters x front, three-quarter, profile, back, full body and crowd views. |
|
||||||
| <img src="beanie.png" width="280" alt="beanie.png"> | [`beanie.png`](beanie.png) |
|
| [](sheet_makehuman_head.jpg) <br> `sheet_makehuman_head.jpg` | MakeHuman head: front, three-quarter and profile review views. The owner accepted the basic head; the automated vision gate still rejects the eye/lid region as deferred polish. See [`../makehuman-head-review.md`](../makehuman-head-review.md). |
|
||||||
| <img src="bench.png" width="280" alt="bench.png"> | [`bench.png`](bench.png) |
|
| [](sheet_npc_baseline_compare-body.jpg) <br> `sheet_npc_baseline_compare-body.jpg` | Neck/shoulder transition: fuller neck base and a lower shoulder root. |
|
||||||
| <img src="bollard.png" width="280" alt="bollard.png"> | [`bollard.png`](bollard.png) |
|
| [](sheet_npc_baseline_compare-face-detail.jpg) <br> `sheet_npc_baseline_compare-face-detail.jpg` | Face detail: nose ala wings, a single rounded mouth and a darker ear concha. |
|
||||||
| <img src="camera.png" width="280" alt="camera.png"> | [`camera.png`](camera.png) |
|
| [](sheet_npc_baseline_compare-face.jpg) <br> `sheet_npc_baseline_compare-face.jpg` | Face-depth pass: recessed sclera, upper lid and sculpted chin/cheek planes. |
|
||||||
| <img src="cap.png" width="280" alt="cap.png"> | [`cap.png`](cap.png) |
|
| [](sheet_npc_baseline_compare-p1.jpg) <br> `sheet_npc_baseline_compare-p1.jpg` | 0.9.4 P1 pass: tourist photo-prop visibility and the refit backpack straps. |
|
||||||
| <img src="clipboard.png" width="280" alt="clipboard.png"> | [`clipboard.png`](clipboard.png) |
|
| [](sheet_npc_baseline_compare-shoes.jpg) <br> `sheet_npc_baseline_compare-shoes.jpg` | Footwear: rounded toe/heel caps on shoes and responder boots. |
|
||||||
| <img src="crowd_barrier.png" width="280" alt="crowd_barrier.png"> | [`crowd_barrier.png`](crowd_barrier.png) |
|
| [](sheet_npc_baseline_compare-tex256.jpg) <br> `sheet_npc_baseline_compare-tex256.jpg` | 128 -> 256 px NPC slot maps on the same camera. |
|
||||||
| <img src="cup.png" width="280" alt="cup.png"> | [`cup.png`](cup.png) |
|
| [](sheet_npc_baseline_compare-texuv.jpg) <br> `sheet_npc_baseline_compare-texuv.jpg` | 4x fabric weave with the baked per-character AO removed. |
|
||||||
| <img src="double_door.png" width="280" alt="double_door.png"> | [`double_door.png`](double_door.png) |
|
| [](sheet_npc_baseline_compare-torso.jpg) <br> `sheet_npc_baseline_compare-torso.jpg` | Torso/hip taper without changing the skeleton or build multipliers. |
|
||||||
| <img src="fire_hydrant.png" width="280" alt="fire_hydrant.png"> | [`fire_hydrant.png`](fire_hydrant.png) |
|
| [](sheet_npc_crowd_prototypes.jpg) <br> `sheet_npc_crowd_prototypes.jpg` | Four approved crowd-variety looks queued for the 12-character roster. |
|
||||||
| <img src="firefighter_helmet.png" width="280" alt="firefighter_helmet.png"> | [`firefighter_helmet.png`](firefighter_helmet.png) |
|
| [](sheet_npc_gear.jpg) <br> `sheet_npc_gear.jpg` | Eighteen gear pieces authored in attach space: caps, hood, helmet, bags, tanks, lanyards and handheld items. |
|
||||||
| <img src="flashlight.png" width="280" alt="flashlight.png"> | [`flashlight.png`](flashlight.png) |
|
| [](sheet_npc_hands_compare.jpg) <br> `sheet_npc_hands_compare.jpg` | Hand pass: wider palms, segmented fingers and a separate thumb. |
|
||||||
| <img src="handrail.png" width="280" alt="handrail.png"> | [`handrail.png`](handrail.png) |
|
| [](sheet_npc_hands_grip_compare.jpg) <br> `sheet_npc_hands_grip_compare.jpg` | Grip pass: the forward hand-item offset that puts props in the palm. |
|
||||||
| <img src="hood.png" width="280" alt="hood.png"> | [`hood.png`](hood.png) |
|
| [](sheet_head_proto_before_after.jpg) <br> `sheet_head_proto_before_after.jpg` | Head overhaul sign-off: current `_face` vs the s48 connected-shell prototype at 1.85 m front/three-quarter/profile. |
|
||||||
| <img src="lanyard.png" width="280" alt="lanyard.png"> | [`lanyard.png`](lanyard.png) |
|
| [](sheet_npc_sources_research.jpg) <br> `sheet_npc_sources_research.jpg` | External character-source evaluation (CC0/CC-BY) for the roster research. |
|
||||||
| <img src="marquee.png" width="280" alt="marquee.png"> | [`marquee.png`](marquee.png) |
|
| [](sheet_props.jpg) <br> `sheet_props.jpg` | Twenty street and venue props with baked maps, ground contact and collider footprints. |
|
||||||
| <img src="newspaper_box.png" width="280" alt="newspaper_box.png"> | [`newspaper_box.png`](newspaper_box.png) |
|
| [](sheet_texture_refinement_0.9.5.jpg) <br> `sheet_texture_refinement_0.9.5.jpg` | 0.9.5 texture refinement before/after: pavers, awning stripes and NPC fabric. |
|
||||||
| <img src="npc_casual.png" width="280" alt="npc_casual.png"> | [`npc_casual.png`](npc_casual.png) |
|
|
||||||
| <img src="npc_coat.png" width="280" alt="npc_coat.png"> | [`npc_coat.png`](npc_coat.png) |
|
The acceptance criteria for these sheets are in [`../npc-visual-review.md`](../npc-visual-review.md),
|
||||||
| <img src="npc_dress.png" width="280" alt="npc_dress.png"> | [`npc_dress.png`](npc_dress.png) |
|
the measurements in [`../npc-visual-review.md#proportion-audit`](../npc-visual-review.md#proportion-audit) and
|
||||||
| <img src="npc_firefighter.png" width="280" alt="npc_firefighter.png"> | [`npc_firefighter.png`](npc_firefighter.png) |
|
[`../3d-model-review.md`](../3d-model-review.md), and the asset contract in
|
||||||
| <img src="npc_guard.png" width="280" alt="npc_guard.png"> | [`npc_guard.png`](npc_guard.png) |
|
[`../blender-assets.md`](../blender-assets.md).
|
||||||
| <img src="npc_hoodie.png" width="280" alt="npc_hoodie.png"> | [`npc_hoodie.png`](npc_hoodie.png) |
|
|
||||||
| <img src="npc_jacket.png" width="280" alt="npc_jacket.png"> | [`npc_jacket.png`](npc_jacket.png) |
|
|
||||||
| <img src="npc_vest.png" width="280" alt="npc_vest.png"> | [`npc_vest.png`](npc_vest.png) |
|
|
||||||
| <img src="palm_tree.png" width="280" alt="palm_tree.png"> | [`palm_tree.png`](palm_tree.png) |
|
|
||||||
| <img src="phone.png" width="280" alt="phone.png"> | [`phone.png`](phone.png) |
|
|
||||||
| <img src="planter_box.png" width="280" alt="planter_box.png"> | [`planter_box.png`](planter_box.png) |
|
|
||||||
| <img src="protest_sign.png" width="280" alt="protest_sign.png"> | [`protest_sign.png`](protest_sign.png) |
|
|
||||||
| <img src="radio.png" width="280" alt="radio.png"> | [`radio.png`](radio.png) |
|
|
||||||
| <img src="round_planter.png" width="280" alt="round_planter.png"> | [`round_planter.png`](round_planter.png) |
|
|
||||||
| <img src="scba_tank.png" width="280" alt="scba_tank.png"> | [`scba_tank.png`](scba_tank.png) |
|
|
||||||
| <img src="security_podium.png" width="280" alt="security_podium.png"> | [`security_podium.png`](security_podium.png) |
|
|
||||||
| <img src="sedan.png" width="280" alt="sedan.png"> | [`sedan.png`](sedan.png) |
|
|
||||||
| <img src="sheet_npc.jpg" width="280" alt="sheet_npc.jpg"> | [`sheet_npc.jpg`](sheet_npc.jpg) |
|
|
||||||
| <img src="sheet_npc_sources_research.jpg" width="280" alt="sheet_npc_sources_research.jpg"> | [`sheet_npc_sources_research.jpg`](sheet_npc_sources_research.jpg) |
|
|
||||||
| <img src="sheet_npc_crowd_prototypes.jpg" width="280" alt="sheet_npc_crowd_prototypes.jpg"> | [`sheet_npc_crowd_prototypes.jpg`](sheet_npc_crowd_prototypes.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline.jpg" width="280" alt="sheet_npc_baseline.jpg"> | [`sheet_npc_baseline.jpg`](sheet_npc_baseline.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-p1.jpg" width="280" alt="sheet_npc_baseline_compare-p1.jpg"> | [`sheet_npc_baseline_compare-p1.jpg`](sheet_npc_baseline_compare-p1.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-body.jpg" width="280" alt="sheet_npc_baseline_compare-body.jpg"> | [`sheet_npc_baseline_compare-body.jpg`](sheet_npc_baseline_compare-body.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-face.jpg" width="280" alt="sheet_npc_baseline_compare-face.jpg"> | [`sheet_npc_baseline_compare-face.jpg`](sheet_npc_baseline_compare-face.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-torso.jpg" width="280" alt="sheet_npc_baseline_compare-torso.jpg"> | [`sheet_npc_baseline_compare-torso.jpg`](sheet_npc_baseline_compare-torso.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-face-detail.jpg" width="280" alt="sheet_npc_baseline_compare-face-detail.jpg"> | [`sheet_npc_baseline_compare-face-detail.jpg`](sheet_npc_baseline_compare-face-detail.jpg) |
|
|
||||||
| <img src="sheet_npc_hands_compare.jpg" width="280" alt="sheet_npc_hands_compare.jpg"> | [`sheet_npc_hands_compare.jpg`](sheet_npc_hands_compare.jpg) |
|
|
||||||
| <img src="sheet_npc_hands_grip_compare.jpg" width="280" alt="sheet_npc_hands_grip_compare.jpg"> | [`sheet_npc_hands_grip_compare.jpg`](sheet_npc_hands_grip_compare.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-shoes.jpg" width="280" alt="sheet_npc_baseline_compare-shoes.jpg"> | [`sheet_npc_baseline_compare-shoes.jpg`](sheet_npc_baseline_compare-shoes.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-tex256.jpg" width="280" alt="sheet_npc_baseline_compare-tex256.jpg"> | [`sheet_npc_baseline_compare-tex256.jpg`](sheet_npc_baseline_compare-tex256.jpg) |
|
|
||||||
| <img src="sheet_npc_baseline_compare-texuv.jpg" width="280" alt="sheet_npc_baseline_compare-texuv.jpg"> | [`sheet_npc_baseline_compare-texuv.jpg`](sheet_npc_baseline_compare-texuv.jpg) |
|
|
||||||
| <img src="sheet_texture_refinement_0.9.5.jpg" width="280" alt="sheet_texture_refinement_0.9.5.jpg"> | [`sheet_texture_refinement_0.9.5.jpg`](sheet_texture_refinement_0.9.5.jpg) |
|
|
||||||
| <img src="sheet_npc_gear.jpg" width="280" alt="sheet_npc_gear.jpg"> | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) |
|
|
||||||
| <img src="sheet_props.jpg" width="280" alt="sheet_props.jpg"> | [`sheet_props.jpg`](sheet_props.jpg) |
|
|
||||||
| <img src="shoulder_bag.png" width="280" alt="shoulder_bag.png"> | [`shoulder_bag.png`](shoulder_bag.png) |
|
|
||||||
| <img src="stanchion.png" width="280" alt="stanchion.png"> | [`stanchion.png`](stanchion.png) |
|
|
||||||
| <img src="street_lamp.png" width="280" alt="street_lamp.png"> | [`street_lamp.png`](street_lamp.png) |
|
|
||||||
| <img src="street_tree.png" width="280" alt="street_tree.png"> | [`street_tree.png`](street_tree.png) |
|
|
||||||
| <img src="sunglasses.png" width="280" alt="sunglasses.png"> | [`sunglasses.png`](sunglasses.png) |
|
|
||||||
| <img src="traffic_cone.png" width="280" alt="traffic_cone.png"> | [`traffic_cone.png`](traffic_cone.png) |
|
|
||||||
| <img src="trash_bin.png" width="280" alt="trash_bin.png"> | [`trash_bin.png`](trash_bin.png) |
|
|
||||||
| <img src="umbrella.png" width="280" alt="umbrella.png"> | [`umbrella.png`](umbrella.png) |
|
|
||||||
| <img src="wall_lantern.png" width="280" alt="wall_lantern.png"> | [`wall_lantern.png`](wall_lantern.png) |
|
|
||||||
| <img src="wand.png" width="280" alt="wand.png"> | [`wand.png`](wand.png) |
|
|
||||||
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 405 KiB |
@@ -1,90 +1,52 @@
|
|||||||
# Development Status
|
# Development Status
|
||||||
|
|
||||||
Current planning priorities are the unversioned **Character development** and **Local map
|
Baseline: **v0.9.8** (2026-11-04). This page lists what the build currently
|
||||||
development** tracks in [`TODO.md`](../TODO.md); neither is part of the implemented list below
|
supports and what remains; it is not a release history. Completed work lives in
|
||||||
yet. The 0.10.0 release candidate remains the next versioned release.
|
[`DONE.md`](../DONE.md), user-facing changes in [`CHANGELOG.md`](../CHANGELOG.md),
|
||||||
|
and the active order/gaps in [`TODO.md`](../TODO.md).
|
||||||
|
|
||||||
Implemented so far:
|
## Implemented
|
||||||
|
|
||||||
- Godot 4.7.2 project scaffold, input map, procedural world builder (CI/local/release unified)
|
| Area | Current capability |
|
||||||
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
| --- | --- |
|
||||||
- Three authored scenarios (El Capitan Entertainment Centre / Hollywood Boulevard, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
| Core shift | Venue menu → 15-minute shift → events, incidents, review and campaign continuation. Pause/restart/settings/quit, checkpoint resume and campaign persistence are implemented. |
|
||||||
- Headless unit tests: **272 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
| Venues | Hollywood Boulevard (Masonic Temple / El Capitan block), The Joke Shop (Sunset Strip stand-in), and the Late Night TV Studio (same El Capitan block, arrival staging). Day/Night is a phase offset on one map, not a second bake. |
|
||||||
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
| NPCs | 12-character Blender library (8 variants + 4 crowd variants), five tint slots, 13 required core bones with optional extras, gear attach points, procedural fallback, LOD/impostor tier and a 24-NPC budget smoke. |
|
||||||
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
| Interaction | Focus cone/aim-free cycling, action wheel, gestural deterrence, reason/recognize, inspection, wand screening, blocking, body camera, assistance/emergency radio, responder arrival and text timeline. |
|
||||||
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
| Access and escalation | Six escalation levels, venue access policies, artifact validity/recognition, door lock, crowd reactions, investigation/false-report handling and shift review. |
|
||||||
- Session shell: venue start menu (keyboard + mouse), Esc pause overlay (resume / restart / settings / quit to menu), settings panel with mouse-look sensitivity and HUD font scale persisted to `user://settings.json`, and "Run another shift" returning to the venue menu
|
| UI/input | Keyboard/mouse and gamepad bindings, focus rings, default modal focus, single top-modal close path, shared button states, font scaling, long-string handling, searchable Help and 21 committed UI/game captures. |
|
||||||
- World presentation: skyline backdrop rendered with an alpha material, walkable step and landing colliders, larger marquee text, and larger focus circle / name tag
|
| Audio/accessibility | Procedural crowd bed and radio cues, master/SFX/ambience volumes, reduced motion, captions, colour-independent escalation cue, hold-to-confirm emergency and guarded destructive actions. |
|
||||||
- Routine traffic despawns after a while at its waypoint so the crowd cap no longer fills with idle standers
|
| Persistence | Separate settings, campaign, shift-history and versioned checkpoint files; default-filling migration, atomic writes and corrupt/partial-file fallback. Save locations and rollback are documented in [`getting-started.md`](getting-started.md#save-data-and-migration). |
|
||||||
- PLAN verbs: eye contact (distinct Level 1 cue), recognizing a person as a player decision recorded as access evidence, a body-camera flag that a formal warning arms (and `B` toggles), blocking that physically stations the guard between the person and the door, and a Level 3 request-reason ask answered from the NPC's `dialogue_lines`
|
| Assets | Blender character/gear/prop library with AO and manifests; 256² NPC maps with 4× fabric weave; image/texture pipelines, reference boards and three-up boards; environment/realism capture tools. |
|
||||||
- Incidents are now actually opened on focus and closed at shift end, so player actions, access evidence, escalation levels, and unresolved interactions reach the review (previously every recorded event was dropped)
|
| Build/release | Godot 4.7.2 Compatibility/Web default plus optional Forward+ Desktop HQ. Gitea checks/build/publish, seven release assets, checksums, preflight/rollback and clean-worktree release discipline. |
|
||||||
- Crowd presentation: aggressive behavior and responder arrivals make idle bystanders stop and face the incident; panic sends people away from the door (and they leave the scene). A busy 16-NPC entrance measured ~1.2 ms/frame, so no LOD/culling was needed at the current cap
|
|
||||||
- Distant skyline ring: four alpha-scissored planes at 200 m read as a hazy city on the horizon (the baked backdrop used to show as a black slab), with a larger ground plane so the buildings meet the street
|
|
||||||
- Flashlight aims straight ahead and lights the person/door being checked instead of the ground in front of the guard
|
|
||||||
- Emergency aftermath: the `evacuation` outcome locks the door and clears the entrance, an emergency call that the situation did not justify opens an investigation and applies the false-report hit, and the shift review names the responder outcome and any investigations
|
|
||||||
- Body-camera timeline: the shift review opens a text timeline of every recorded incident event (time, person, action/outcome) that you step through with the arrow keys or buttons. No live 3D replay yet
|
|
||||||
- NPC theater: seated guests sit on the steps with bent legs, influencers and tourists raise a camera prop while photographing, performers sway their sign, and filming visitors pan slowly — all once they reach their intent's waypoint
|
|
||||||
- Audio: a looping procedural crowd bed (filtered murmur) whose volume follows `CrowdSystem.mood`, plus radio open/close crackle. All generated at runtime, no audio assets, and safe with a dummy audio server
|
|
||||||
- Accessibility: the escalation cue shows a shape meter plus the numeric level (`Escalation: Blocking [###--] L3/5`) so it reads in grayscale, and the HUD font scale from settings is applied at boot by resizing fonts (not Control.scale, which broke the right-aligned layout)
|
|
||||||
- Editor-ready venues: `tools/export_venues.gd` writes `content/venues/<id>.tres` from `ScenarioFactory`, and the `.tres` files load back with events, profiles, policy, phases, door lines, and locations intact (the factory stays the source of truth)
|
|
||||||
- Localization safety: `Strings.fmt` validates the placeholder count before formatting and fails safe (warns, returns the template) instead of raising an engine error, `Strings.placeholder_count` ignores `%%`, and `Strings.validate()` lints the table for trailing `%`, unsupported conversions, and mixed positional/sequential placeholders
|
|
||||||
- Authored scenario content: Joke Shop reservation-without-ID, borrowed/mismatched ID, a guest who refuses the wand screen, and a late arrival during a performance; Studio filming-boundary repeats and a celebrity arrival that draws a small crowd; Building events that point the player at the body camera and request-reason verbs
|
|
||||||
- Visual polish pass (v0.2.0): phase-based lighting (sun/sky/ambient per shift phase), street lamps with night-mode bulbs and omni lights, glowing marquee signage, crosswalk, planters and low-poly street trees, NPC height/brightness variety, denser routine traffic — day and late-night screenshot sets
|
|
||||||
- Assets pass: authored textures in `assets/textures/` (brick facade, concrete, asphalt, awning, skyline, vignette, HUD icons, access-card art) generated by `tools/generate_textures.py`, soft sun shadows, humanoid NPC rigs with archetype props + procedural walk bob and attention head-turn, inspection artifact cards, escalation vignette, mode icons, and a body-camera overlay (REC + shift clock)
|
|
||||||
- Rendering pass (v0.3.0 work, [ADR-0012](adr/0012-surface-materials-and-generated-props.md)): one material module with roughness/metallic profiles per surface, ACES tonemapping, glow, aerial fog and colour grading driven by the shift phase, correctly scaled brick, framed facade windows that light up at night, door trim and handles, a curb and lane dashes, and the glowing marquee/lamp bulbs. NPCs gained per-role clothing palettes, caps/hoods/backpacks/sunglasses/lanyards, hands and shoes, and an arm/leg walk swing
|
|
||||||
- NPC characters (v0.5.0): articulated limb rigs, layered outfits, hand items, face/hair variety, idle behaviour (phone, clipboard, crossed arms, look-around), escalation gestures, readable reaction symbols, door-queue offsets, and three LOD tiers with a billboard impostor
|
|
||||||
- Player movement (v0.5.0): separate acceleration/braking, step-up assist, head bob and landing dip (toggleable), precision-walk FOV/chip/tick, surface-aware footsteps, gamepad support, and persistent key/button remapping in settings
|
|
||||||
- Prop collision and placement (v0.4.0): every prop fits a static collider from its mesh bounds and publishes a navigation obstacle, so the guard and guests cannot walk through benches, bins, or bollards; placements are grouped into zones with a validator, and deterministic variants add rotation, scale, and small clutter
|
|
||||||
- Prop atlas (v0.3.1): `tools/model_pipeline.py` packs each prop's palette into one power-of-two colour atlas plus a metallic/roughness atlas and emits a single material with per-vertex UVs, so a prop is one draw material rather than one per part; `audit` enforces the contract
|
|
||||||
- Generated prop models (v0.3.0 work): `tools/model_pipeline.py` builds bollards, benches, bins, planters, newspaper boxes and a hydrant as `.glb` props with per-part materials, loaded by `WorldBuilder` with a procedural fallback; the optional AI concept step (`model_pipeline.py concept`) supplies per-prop colour palettes
|
|
||||||
- Environment pass (v0.6.0): 1024² brick/concrete/asphalt with derived tangent normal maps, 256² baked AO maps for every Blender character/gear/prop, and library props whose bulbs/LEDs/sign faces follow the day/night phase
|
|
||||||
- NPC characters + library (v0.6.0): `NpcAvatar` loads one of 8 Blender-authored skinned variants (role/seed selected) with five tintable material slots, `Rig/Head` focus anchor, and attach-point gear; `npc_base.glb` and the procedural rig remain fallbacks. `tools/blender_assets.py` builds/audits the character, gear, and 20-prop library; `WorldBuilder` loads the replacements and new street dressing (lamp, trees, palms, planters, barriers, podium, doors, marquee, sedan, handrail) with colliders, navigation obstacles, and phase-driven emissives
|
|
||||||
- Web build: `build/gig-line-web-<version>.zip` (Godot's non-threaded web template, no COOP/COEP headers needed) built by `tools/build_releases.sh` and published with the desktop artifacts; serve the unzipped folder from any static host
|
|
||||||
- Aim-free selection pass (v0.3.0 work, [ADR-0011](adr/0011-aim-free-selection-and-precision-walk.md)): the focus cone ranks candidates by angle then distance, `Tab` and the mouse wheel cycle nearby people nearest-first and pin the selection, right click clears focus with a short re-acquire grace, the HUD reports how many people are in the ring, and holding `Shift` walks at half speed for close work
|
|
||||||
|
|
||||||
- Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing. The wand panel lists the guest's carried items, and multiline HUD/wand strings render real line breaks.
|
## Current verification baseline
|
||||||
- NPC neck/shoulder transition (0.9.4 work): the body graph now uses a fuller neck base, a slightly tapered neck top and a lower shoulder root, giving the head/neck/shoulder line a trapezius slope instead of a thin post. The full character library was rebuilt with a final degenerate-face cleanup; the baseline comparison is `sheet_npc_baseline_compare-body.jpg`.
|
|
||||||
- NPC face-depth pass (0.9.4 work): sclera are recessed and capped by a thin upper lid, and the separate jaw/cheek spheres are replaced by a chin plate and cheekbones sculpted into the head mesh. The full library was rebuilt; the comparison is `sheet_npc_baseline_compare-face.jpg`.
|
|
||||||
- NPC torso/hip transition (0.9.4 work): the pelvis and chest are slightly wider than the waist, giving shirts and jackets a readable taper into the hips without changing the build multipliers or the skeleton. The comparison is `sheet_npc_baseline_compare-torso.jpg`.
|
|
||||||
- Release v0.9.6 (2026-11-03): the tagged commit `4bb2896` passed Gitea Actions run 1296 (checks + full build) and published seven artifacts (Linux, Windows, macOS, NSIS setup, AppImage, Web and Desktop HQ). Release page: <https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.9.6>.
|
|
||||||
- Release candidate (0.10.0-rc.1, 2026-11-03): clean worktree `a9658ec` built Linux 161.5 MB, Windows 197.2 MB, macOS 148.0 MB, Web 97.8 MB and Desktop HQ 241.6 MB. The Web/HQ archives contain `CREDITS.md`, `PRIVACY.md`, `docs/accessibility.md` and `docs/known-limitations.md`; SHA-256 checksums were generated, the Linux and HQ binaries launched headless, and the Web index/wasm/pck/docs served with HTTP 200. The candidate is not published: external playtest, AI-provider/legal approval, macOS signing and the Windows/macOS target matrix remain open blockers.
|
|
||||||
- Save migration and reliability (0.10.0 work): committed 0.8-era settings/campaign/history fixtures and a 0.9.0 checkpoint. The loader default-fills old settings, keeps campaign progress and validates checkpoints for all three venues with an open incident, NPC rows, door, flashlight and body-camera state. Corrupt and partial files fall back to defaults; a blocked atomic replace leaves the prior valid file intact. `_load_json` uses the non-logging JSON parser; save locations and rollback are documented in `getting-started.md`. Suite is 272/272.
|
|
||||||
- Integration checkpoint (0.9.6 work): the clean worktree at `f5b8ef2` rebuilt all five release packages and the Desktop HQ zip without ignored `tmp/`/`build/` content (Linux 161.5 MB, Windows 197.1 MB, macOS 148.0 MB, Web 97.8 MB, HQ 241.5 MB). The Web zip contains `index.html`, `index.wasm` and `index.pck`; the HQ zip contains `override.cfg` and launches headless with the Forward+ override. Current verification: Godot 263/263, Python 101/101, 14 smokes green, 24-NPC budget 6.90 ms / 168 meshes / 131,616 triangles, gallery 21 good / 0 bad, screen judge 21/21 match with 0 review/diff, and a pinned realism judge (`z-ai/glm-5.3-flash`, one pass per shot) at mean overall 2.1 and Pillow gap 0.291. No new gameplay or content changes.
|
|
||||||
- Release v0.9.5 (2026-11-02): the tagged commit `4bc6390` passed Gitea Actions run 922 (checks + full build) and published seven artifacts (Linux, Windows, macOS, NSIS setup, AppImage, Web and Desktop HQ). Release page: <https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.9.5>.
|
|
||||||
- Release v0.9.4 (2026-11-02): the tagged commit `a57b86c` passed the Gitea checks and full build and published seven artifacts (Linux, Windows, macOS, NSIS setup, AppImage, Web and Desktop HQ). Release page: <https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.9.4>.
|
|
||||||
- Environment texture pass (0.9.5 work): `capture_realism.gd` now has hidden-HUD `surfaces`/`surfaces_joke` close-up stages. The Walk of Fame band uses the paver texture as dark terrazzo, the neighbour awning keeps its cream stripe, and the skyline shader blends its lower 42 m toward a per-phase haze colour. Brick, asphalt, limestone and paver UV scales/profiles passed without replacement.
|
|
||||||
- Texture/prop audit (0.9.5 work): all 21 screen-art textures are 1280×720 and referenced; access cards are 256×128 and HUD icons 64×64. No oversized or unused runtime texture was found; the legacy root textures remain only as the texture-generator/asset-test fallback. The 24-NPC smoke now reports 168 meshes, 131,616 triangles and 6.94 ms (50 ms budget); compressed payload is 24.55 MB of surface picks, 22.67 MB of screen art and 0.48 MB of NPC maps.
|
|
||||||
- NPC texture pass (0.9.5 work): the NPC base and all eight library characters now use 256² albedo/normal/ORM maps instead of 128². Cloth and pants use `NPC_FABRIC_UV_SCALE` (4× weave) and skip the baked per-character AO, removing the large blotchy patches in portraits; skin, hair and gear keep baked AO. The base and eight GLBs plus AO maps were rebuilt; the 24-NPC smoke remains green.
|
|
||||||
- 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 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 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 hand-item grip (0.9.4 work): runtime `HAND_ITEM_FORWARD` moves every hand gear 4 cm forward from the wrist proxy, so phone, cup, clipboard, camera, flashlight, wand and radio sit in the open palm instead of partly inside it. The open-hand/no-finger-rig limit is accepted for this stylized release; the comparison is `sheet_npc_hands_grip_compare.jpg`.
|
|
||||||
- NPC hand pass (0.9.4 work): wider, shallower palms; four two-segment bent fingers with rounded tips and a separate bent thumb; optional `--view=hand` capture at 0.80 m. The hand comparison is `sheet_npc_hands_compare.jpg`; the forward offset is in and the open-palm limit is accepted.
|
|
||||||
- NPC proportion audit (0.9.4 work): `tools/npc_proportions.py` measures all eight GLBs against a stylized height/head/shoulder/hip/torso/leg/arm/foot guide. All variants pass; the head and broad-build silhouettes are the nearest upper bounds. Results are in [`npc-proportion-audit.md`](npc-proportion-audit.md).
|
|
||||||
- NPC P1 portrait fixes (0.9.4 work): tourists now hide their photo-pose camera bag outside `photo`/`film` poses, removing the duplicate-backpack silhouette; the backpack gear was rebuilt with flattened, forward chest straps and a fresh AO map. The before/after sheet is [`blender_previews/sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg); the remaining face/hand work was closed under the 0.9.4 acceptance limits.
|
|
||||||
- NPC visual baseline (0.9.4 work): `src/test/capture_npc_baseline.gd` captures all eight library characters from front, three-quarter, profile, back, full-body and crowd views in a neutral studio (48 frames). The criteria, measurements and ranked gaps are in [`npc-visual-review.md`](npc-visual-review.md); the committed sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). No model changes were made in this baseline step.
|
|
||||||
- UI-state capture pass (0.9.3 work): `src/test/capture_ui_states.gd` adds committed captures 14–21 for the start menu, pause, settings, shift review, emergency radio, action wheel, crowd panic and responder arrival. Every one of the 21 catalogue entries now has a current-build capture; `tools/check_screenshots.sh` reports 21 good / 0 bad and the automatic judge reports 21/21 match, 0 diff. Crowd panic and responder arrival are staged presentation frames, not gameplay changes.
|
|
||||||
- Realism adoption (post-v0.6.0): v2 per-phase skylines and surface tiles with normal maps, retuned sun/exposure/fog, wet asphalt, denser foliage, a visible additive flashlight beam, and refreshed staging captures. `tools/check_screenshots.sh` is the repeatable capture/compare gate; judge is clean of `diff` verdicts.
|
|
||||||
- Input/reliability fixes: selecting a venue releases the start-menu input lock and recaptures the mouse; the body-camera overlay no longer covers the shift status. Mouse look now reads at `_input`, restores capture on window focus/click-back, and the HUD is mouse-transparent. Normal radio reports submit on click; emergency calls still require the hold-to-confirm path.
|
|
||||||
- Production-quality pass (v0.9.0): mixer/accessibility settings, audio volume clamping, 24-NPC budget smoke, shortened coat/dress stride, and a unified Godot 4.7.2 CI/local support baseline with documented upgrade/rollback requirements.
|
|
||||||
- Campaign and first-hour pass (v0.8.0): campaign progression, checkpoint resume, difficulty presets, a searchable Help overlay, recovery events, contextual dialogue, policy/debrief lines and tutorial/replay/skip. Scripted QA bypasses the tutorial unless `--tutorial` is passed.
|
|
||||||
- Tutorial pass (0.8.0 work): the eight-beat first shift is wired through a non-blocking prompt overlay, a safe compliant visitor, real wheel/inspection/warning/radio panels, persisted completion/skip and a Settings replay action. Scripted tests/captures bypass it unless `--tutorial` is passed; `smoke_tutorial.gd` covers the full flow.
|
|
||||||
- Vision judge (v0.7.2): discovers the cheapest current OpenRouter image+JSON model, reports the model/fallbacks used, and clears the full screen-design judge (13/13 match, 0 diff).
|
|
||||||
- Desktop HQ (v0.7.1): optional Forward+ Windows/Linux zip with volumetric fog, SDFGI and a volumetric flashlight; the default Compatibility/Web builds and headless tests gate the effect off. A/B captures are generated locally under `docs/screens/realism/` (gitignored).
|
|
||||||
- Hollywood landmark map (map optimization): the default venue is now a stylized reconstruction of 6840 Hollywood Blvd (the 1921 Masonic Temple / El Capitan Entertainment Centre) with a six-column Ionic portico, eight stone steps, Masonic parapet motto and medallions, three red event banners, central and side entrances, the El Capitan Theatre and east retail neighbours, the Ovation Hollywood arcade opposite, the Walk of Fame terrazzo and a four-lane boulevard with red kerb paint. The facade uses the shared limestone/stone material; the whole venue measures **58,884 triangles across 409 meshes** in the map-budget test. The venue menu exposes separate **Day** and **Night** Hollywood rows; the night row resolves to the same scenario with a late-phase offset, so its whole shift stays after dark. Real-site references and the geometry mapping are in [`hollywood-boulevard-map.md`](hollywood-boulevard-map.md).
|
|
||||||
- Day/night scenery variants (map optimization): the Hollywood row in the venue menu is split into Day and Night entries, with localized names/blurbs. The night entry uses `ShiftCoordinator.phase_offset_s` to start a full-length shift in `LATE` while the clock still starts at 00:00; the scheduler reads the offset time so late-night routine traffic and the late violent-threat event still fire, and the offset round-trips through checkpoints. Three `LATE` routine events keep the night street populated. Local lights were retuned from constant falloff to linear attenuation so sconces pool instead of flooding the stone.
|
|
||||||
- Joke Shop / Sunset Strip map (map optimization): the second venue is now a stylized reconstruction of The Comedy Store block at 8433 Sunset Blvd, with a 27 m black hall, signature-covered sloping canopy, circular **THE JOKE SHOP** blade sign, red velvet curtain bays, a west-end entrance, cream and white low-rise neighbours, a dry hillside with stucco houses and palms behind the roofline, and a low retail/billboard frontage across Sunset. The same geometry serves day and late lighting through the phase system. Real-site references and limits are in [`joke-shop-map.md`](joke-shop-map.md); the map measures **22,680 triangles across 454 meshes** in the budget test.
|
|
||||||
- Venue geometry (v0.7.0, Joke Shop frontage superseded by the map-optimization pass): the TV Studio builds a stone facade, bulb canopy, glossy black car and barrier line. `WorldBuilder.VENUE_SPECS` drives facade/street/nav dimensions, and `GameManager` rebuilds only the world container when the venue changes.
|
|
||||||
- Gear fit (post-v0.6.0): Blender gear is measured against the avatar head/skirt silhouette and the checks run in both the Python tooling and the headless suite. Sunglasses, the shoulder bag and the firefighter helmet were fitted to the new faces; the Influencer portrait and gallery were recaptured with a before/after report.
|
|
||||||
- End-to-end interaction pass (0.9.3 work): `smoke_ui_flows.gd` drives inspection allow/deny, normal vs emergency radio, Help filtering, wand allow/deny and review return-to-menu in the real scene; campaign and timeline smokes cover review continue and timeline round-trips.
|
|
||||||
- Destructive-action pass (0.9.3 work): reset campaign and tutorial replay share a guarded confirmation dialog (one open instance, one emission, Cancel is inert); emergency send remains hold-to-confirm with no post-submit double-fire. Covered by session/radio tests and recorded in `docs/ui-copy.md`.
|
|
||||||
- Font/layout pass (0.9.3 work): HUD font scale was exercised at 0.75× and 1.75×; the target chip measures and caps scaled names, top labels clip, and shared action buttons ellipsize long 1.5–2× labels instead of overflowing. Covered by accessibility/button tests and recorded in `docs/ui-copy.md`.
|
|
||||||
- Modal-close pass (0.9.3 work): one top-modal close path handles Esc/right-click for every overlay, Settings opened from the menu restores the menu, Help no longer opens Pause behind it, and pointer/guard freeze state is recaptured after each close. The session smoke covers these paths.
|
|
||||||
- Keyboard/gamepad focus pass (0.9.3 work): `UiFocus` rings wrap through visible/enabled controls, hidden Continue and disabled timeline actions are skipped, each modal has a default focus, and A/B are bound to `ui_accept`/`ui_cancel` for pad-only navigation. Recorded in `docs/ui-copy.md`.
|
|
||||||
- Button-state pass (0.9.3 work): a shared `UiStyles` helper now gives every action button explicit normal, hover, pressed, focus and disabled styles; the timeline disables Previous/Next at its boundaries, while hidden continue states and the keyboard-only tutorial skip remain intentional. Recorded in `docs/ui-copy.md`.
|
|
||||||
- Dialogue pass (0.9.3 work): NPC reason answers now use the authored `reason` context, with an in-character answer for every shared archetype; Hollywood and Studio gained door-staff lines; radio cancellation, tutorial Ask terminology, help, start hints and campaign reset copy were clarified. Audit recorded in `docs/ui-copy.md`.
|
|
||||||
- Pre-build NPC review (2026-11-02): all eight library characters were staged at portrait, conversation and crowd distance on Hollywood and the Joke Shop, in day and night lighting, with the normal crowd hidden (`src/test/capture_npc_review.gd`). No P0 issues were found. Two P1 gear-fit issues were fixed with runtime attach offsets: the authorized-guest lanyard badge now clears the jacket and the firefighter helmet sits above the eyes. Backpack straps, caps, hands and the intentionally low-poly faces were accepted for the stylized target; close-range flashlight wash remains an optional-lighting follow-up. The 24-NPC budget stayed at 6.94 ms, the gallery passed 13 good / 0 bad and the screen-design judge stayed 13/13, and a clean portrait scored a 0.732 vision gap against the photoreal concept.
|
|
||||||
|
|
||||||
Current gaps for the 0.x release line: deeper authored content/dialogue, animation and audio production, localization/accessibility expansion, 24-NPC/platform profiling, external playtesting, and signed/notarized release packaging. The full gap analysis and milestone plan live in [`TODO.md`](../TODO.md); the current architecture and documentation ownership map are in [`docs/architecture.md`](architecture.md).
|
- Godot headless suite: **277 passing**
|
||||||
|
- Python tooling tests: **162 passing**
|
||||||
|
- Asset audits: `blender_assets.py audit` / `fit`, `npc_pipeline.py audit` and
|
||||||
|
`npc_proportions.py` pass for the committed library.
|
||||||
|
- 24-NPC smoke: 168 meshes, 152,464 triangles, 6.94 ms (50 ms budget).
|
||||||
|
- Screenshot gate: 21 good / 0 bad; screen-design judge 21/21 match.
|
||||||
|
- Release baseline: v0.9.8 is published; the 0.10.0 release candidate remains
|
||||||
|
gated on external playtest, legal/storefront, signing and platform-matrix work.
|
||||||
|
|
||||||
|
## Known gaps
|
||||||
|
|
||||||
|
- External first-time-player sessions and balance sweeps are not completed.
|
||||||
|
- The Quaternius roster is shipped (`0.9.9`): all twelve characters are baked from the three CC0
|
||||||
|
sources (casual tee, hoodie, work clothes), 4,458–6,424 triangles each, 62-joint rig at source
|
||||||
|
proportions, arm-down rest, five tint slots, unwrapped UVs and baked 256² AO. The procedural
|
||||||
|
library and the MakeHuman head remain the tested fallback.
|
||||||
|
- Roster silhouette variety is narrower than the procedural library (three meshes for twelve
|
||||||
|
roles, differentiated by tint and gear) until the modular part library is added.
|
||||||
|
- Head gear is authored for the shipped Quaternius head; the procedural fallback therefore wears
|
||||||
|
slightly undersized caps. Cosmetic, fallback-only.
|
||||||
|
- Accessibility/localization acceptance, target-hardware profiling, macOS
|
||||||
|
signing/notarization and AI/storefront legal approval remain open.
|
||||||
|
- Live 3D replay, recorded VO/music, more venues and seasonal content are future
|
||||||
|
work.
|
||||||
|
|
||||||
|
See [`TODO.md`](../TODO.md) for the ordered task list and
|
||||||
|
[`known-limitations.md`](known-limitations.md) for the current release blockers.
|
||||||
|
Architecture and ownership are in [`architecture.md`](architecture.md).
|
||||||
@@ -16,7 +16,7 @@ A typical shift lasts 10–15 minutes. The first-person camera stays at the game
|
|||||||
|
|
||||||
The game rewards calm resolutions rather than aggressive behavior. Emergency assistance and armed responders are rare last-resort systems with meaningful consequences.
|
The game rewards calm resolutions rather than aggressive behavior. Emergency assistance and armed responders are rare last-resort systems with meaningful consequences.
|
||||||
|
|
||||||
The first shift starts with an in-game tutorial covering movement/look, focus cycling, the action wheel, access inspection, polite direction, formal warning/body camera, radio and shift review. Its beat sheet is in [`tutorial.md`](tutorial.md). After that a short campaign runs Hollywood → Joke Shop → TV Studio; progress, difficulty and a pause-safe checkpoint are persisted locally. Help (from the venue menu) explains the controls and the current venue policy.
|
The first shift starts with an in-game tutorial covering movement/look, focus cycling, the action wheel, access inspection, polite direction, formal warning/body camera, radio and shift review. See the tutorial section below. After that a short campaign runs Hollywood → Joke Shop → TV Studio; progress, difficulty and a pause-safe checkpoint are persisted locally. Help (from the venue menu) explains the controls and the current venue policy.
|
||||||
|
|
||||||
## Venues
|
## Venues
|
||||||
|
|
||||||
@@ -24,22 +24,22 @@ The first shift starts with an in-game tutorial covering movement/look, focus cy
|
|||||||
|
|
||||||
The map is a stylized reconstruction of the real building and block; the
|
The map is a stylized reconstruction of the real building and block; the
|
||||||
reference notes and geometry mapping are in
|
reference notes and geometry mapping are in
|
||||||
[`hollywood-boulevard-map.md`](hollywood-boulevard-map.md). The venue menu
|
[`venues.md`](venues.md#hollywood-boulevard). The venue menu
|
||||||
offers the same map as **Hollywood Boulevard — Day** and **Hollywood Boulevard
|
offers the same map as **Hollywood Boulevard — Day** and **Hollywood Boulevard
|
||||||
— Night**; the night variant starts directly in the late phase instead of
|
— Night**; the night variant starts directly in the late phase instead of
|
||||||
waiting for the shift clock.
|
waiting for the shift clock.
|
||||||
|
|
||||||
The original scenario places the guard at the top of the eight stone steps outside the historic Masonic Temple / El Capitan Entertainment Centre on Hollywood Boulevard. Tourists, street performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people experiencing a mental-health crisis, and rare violent threats may approach the entrance.
|
The original scenario — the starting point of the campaign — places the guard at the top of the eight stone steps outside the historic Masonic Temple / El Capitan Entertainment Centre on Hollywood Boulevard, the building that houses the *Jimmy Kimmel Live!* stage and studio. Tourists, street performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people experiencing a mental-health crisis, and rare violent threats may approach the entrance.
|
||||||
|
|
||||||
Authorized people must either be recognized by the guard or present a valid wristband. Wristbands can have different colors, patterns, dates, or event markings. They may be valid, expired, counterfeit, or missing.
|
Authorized people must either be recognized by the guard or present a valid wristband. Wristbands can have different colors, patterns, dates, or event markings. They may be valid, expired, counterfeit, or missing.
|
||||||
|
|
||||||
### The Joke Shop
|
### The Joke Shop
|
||||||
|
|
||||||
The Joke Shop is a Sunset Strip comedy club inspired by the real Comedy Store at 8433 Sunset Blvd: a black hall under a deep signature-covered canopy, a circular blade sign, red velvet curtains, a west-end entrance, neighbouring low-rise frontage and a dry hillside of houses behind. The real-site mapping is in [`joke-shop-map.md`](joke-shop-map.md). The door person checks IDs, tickets, reservations, and guest-list status, then uses a handheld security wand according to venue policy. The player must interpret wand alerts rather than treating every alert as proof of a weapon; keys, belts, phones, and other harmless objects can trigger false alarms.
|
The Joke Shop is a Sunset Strip comedy club inspired by the real Comedy Store at 8433 Sunset Blvd: a black hall under a deep signature-covered canopy, a circular blade sign, red velvet curtains, a west-end entrance, neighbouring low-rise frontage and a dry hillside of houses behind. The real-site mapping is in [`venues.md`](venues.md#the-joke-shop). The door person checks IDs, tickets, reservations, and guest-list status, then uses a handheld security wand according to venue policy. The player must interpret wand alerts rather than treating every alert as proof of a weapon; keys, belts, phones, and other harmless objects can trigger false alarms.
|
||||||
|
|
||||||
### Hollywood Boulevard Late Night TV Studio
|
### Hollywood Boulevard Late Night TV Studio
|
||||||
|
|
||||||
This scenario combines the previously defined NPC types with a busy television-studio entrance: a stone facade, a bulb-lit canopy, a glossy black car at the curb, and stanchions/barriers holding the arrival line. The player manages audience members, production staff, performers, guests, deliveries, celebrity arrivals, tourists, street performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people experiencing a mental-health crisis, and rare violent threats.
|
This scenario combines the previously defined NPC types with a busy television-studio entrance: the same El Capitan Entertainment Centre block as the Hollywood day shift, restaged as a late-night studio arrival — the stone facade and steps, a bulb-lit canopy, a glossy black car at the curb, and stanchions/barriers holding the arrival line. The player manages audience members, production staff, performers, guests, deliveries, celebrity arrivals, tourists, street performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people experiencing a mental-health crisis, and rare violent threats.
|
||||||
|
|
||||||
The studio uses wristbands, credentials, tickets, guest-list confirmation, and personal recognition to authorize entry. Crowd control, filming boundaries, following authorized guests through the door, and counterfeit credentials are key events.
|
The studio uses wristbands, credentials, tickets, guest-list confirmation, and personal recognition to authorize entry. Crowd control, filming boundaries, following authorized guests through the door, and counterfeit credentials are key events.
|
||||||
|
|
||||||
@@ -68,3 +68,51 @@ An off-duty law-enforcement officer or armed emergency responder may arrive duri
|
|||||||
- **Crowd reaction:** Aggressive behavior, visible weapons, and emergency response can create panic or attract a crowd.
|
- **Crowd reaction:** Aggressive behavior, visible weapons, and emergency response can create panic or attract a crowd.
|
||||||
- **Reputation:** Peaceful resolutions, accurate reports, low property damage, and justified calls improve performance; unnecessary confrontations and false reports reduce it.
|
- **Reputation:** Peaceful resolutions, accurate reports, low property damage, and justified calls improve performance; unnecessary confrontations and false reports reduce it.
|
||||||
- **Shift review:** The player receives feedback on access decisions, escalation choices, public complaints, injuries, and security outcomes.
|
- **Shift review:** The player receives feedback on access decisions, escalation choices, public complaints, injuries, and security outcomes.
|
||||||
|
|
||||||
|
## First-shift tutorial
|
||||||
|
|
||||||
|
Status: implemented for v0.8.0. The tutorial teaches the real controls and the real
|
||||||
|
inspection/wheel/radio panels; it is not a fake UI or a separate combat mode.
|
||||||
|
Scripted test/capture runs bypass it unless `--tutorial` is passed; a windowed
|
||||||
|
smoke covers completion, replay and skip.
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
- One short, safe, compliant visitor. No emergency, threat, crowd panic or random
|
||||||
|
event can interrupt the tutorial.
|
||||||
|
- Prompts advance on the performed action, not on a timer. The player can keep
|
||||||
|
moving/looking while a prompt is visible.
|
||||||
|
- The tutorial is skippable at any point (F1). Completion and skip are both
|
||||||
|
persisted separately; a skipped tutorial can be replayed from Settings.
|
||||||
|
- The tutorial state machine is independent of the shift FSM. It observes the
|
||||||
|
same actions as normal play and must be deterministic for tests.
|
||||||
|
|
||||||
|
### Beat sheet
|
||||||
|
|
||||||
|
| # | Beat | Prompt | Advances when | Notes |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 1 | Arrive | "Walk forward and look around. The entrance is behind you." | The guard has moved at least 1 m and looked away/returned | Start at the normal guard post, facing the street. |
|
||||||
|
| 2 | Focus | "Look at the visitor. Press Tab or the mouse wheel to cycle targets." | A target is acquired and explicitly cycled | The visitor waits at the foot of the steps; no other guest is required. |
|
||||||
|
| 3 | Wheel | "Open the action wheel with Space or left click." | The radial wheel opens on the visitor | The wheel is the real de-escalation-first wheel. |
|
||||||
|
| 4 | Inspect | "Choose Inspect and read the access evidence." | The inspection panel is opened and the policy result is visible | Use a valid wristband so the tutorial can teach both evidence and a safe admit/deny decision. |
|
||||||
|
| 5 | Direction | "Close the panel and give a polite direction." | A Level 1 polite-direction action is applied | The visitor complies; there is no escalation requirement. |
|
||||||
|
| 6 | Warning | "Give a formal warning if the visitor persists. It arms the body camera." | A Level 2 warning is applied or the body camera is switched on | Show the REC overlay and the escalation cue. |
|
||||||
|
| 7 | Radio | "Open the assistance radio and submit a short report." | The assistance report is submitted successfully | A non-emergency assistance call; no responder is required to resolve the tutorial. |
|
||||||
|
| 8 | Review | "Finish the shift and review what happened." | The shift review opens | The tutorial ends; the real review remains available for inspection. |
|
||||||
|
|
||||||
|
### Visitor script
|
||||||
|
|
||||||
|
- Display name: `Visitor`.
|
||||||
|
- Intent: `enter` (approaches the door, then waits for interaction).
|
||||||
|
- Access: valid wristband evidence; the policy result should be unambiguous.
|
||||||
|
- Behavior: compliant at every escalation level; never becomes a threat, never
|
||||||
|
joins a crowd and never triggers an emergency.
|
||||||
|
- Failure handling: if the player denies admission incorrectly, the tutorial
|
||||||
|
explains the evidence but does not fail the run; the visitor remains safe and
|
||||||
|
the review records the decision.
|
||||||
|
|
||||||
|
### Persistence and replay
|
||||||
|
|
||||||
|
Settings gain `tutorial_completed` and `tutorial_skipped`. A first launch starts
|
||||||
|
the tutorial after venue selection. Skipping sets `tutorial_skipped`; Settings/
|
||||||
|
Help offers "Replay tutorial", which clears both flags for the next shift.
|
||||||
@@ -74,31 +74,25 @@ XDG_DATA_HOME=/tmp/gigline-campaign godot --path . --fixed-fps 60 --script res:/
|
|||||||
|
|
||||||
The unit test runner is the project's own lightweight harness (no editor plugin required) so CI can run it without the editor.
|
The unit test runner is the project's own lightweight harness (no editor plugin required) so CI can run it without the editor.
|
||||||
|
|
||||||
## Save Data and Migration
|
## Save Data
|
||||||
|
|
||||||
The game keeps four small JSON files in Godot's `user://` directory, which is
|
The game writes four JSON files in Godot's `user://` directory:
|
||||||
`~/.local/share/godot/app_userdata/Gig Line/` on Linux,
|
|
||||||
`%APPDATA%\Godot\app_userdata\Gig Line\` on Windows and
|
|
||||||
`~/Library/Application Support/Godot/app_userdata/Gig Line/` on macOS:
|
|
||||||
|
|
||||||
| File | Contents |
|
| Platform | Location |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `settings.json` | Camera, HUD, binding, difficulty, audio, accessibility and tutorial flags. Missing keys are filled from the current defaults; there is no schema version because the field set grew compatibly. |
|
| Linux | `~/.local/share/godot/app_userdata/Gig Line/` |
|
||||||
| `campaign.json` | Campaign index, completed venues, difficulty, shift number and last grade. Older files with missing fields open at the safe defaults. |
|
| Windows | `%APPDATA%\Godot\app_userdata\Gig Line\` |
|
||||||
| `shift_results.json` | Completed-shift review history, appended as an array. |
|
| macOS | `~/Library/Application Support/Godot/app_userdata/Gig Line/` |
|
||||||
| `shift_state.json` | Resumable mid-shift checkpoint, version `1`. It stores the venue, guard/door/flashlight/body-camera state, coordinator snapshot and NPC rows. |
|
|
||||||
|
|
||||||
Writes go to a sibling `.tmp` file and are renamed only after the complete JSON
|
Files: `settings.json`, `campaign.json`, `shift_results.json` and the versioned
|
||||||
is written. A corrupt or partial file is ignored on load, so the player gets
|
`shift_state.json` checkpoint. The schema, migration/default-filling rules,
|
||||||
defaults rather than a crash and a valid old file is not overwritten by a failed
|
atomic-write behavior and checkpoint contents are in
|
||||||
replace. The migration fixtures used by the tests live in
|
[`architecture.md#persistence-and-save-contract`](architecture.md#persistence-and-save-contract).
|
||||||
`src/test/fixtures/saves/`.
|
|
||||||
|
|
||||||
For a clean first launch, delete the four files (or the whole `Gig Line` user
|
For a clean first launch, delete the four files (or the whole `Gig Line` user
|
||||||
directory). Settings' **Reset campaign** clears campaign progress separately.
|
directory). Settings' **Reset campaign** clears campaign progress separately.
|
||||||
To back up or move a profile, copy those files while the game is closed; to roll
|
To back up or move a profile, copy the files while the game is closed; restore
|
||||||
back, restore the previous copies. Shift history is diagnostic and is not
|
them to roll back. Shift history is diagnostic and not required to resume.
|
||||||
required to resume a checkpoint.
|
|
||||||
|
|
||||||
## Controls
|
## Controls
|
||||||
|
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
# Hollywood Boulevard map reference
|
|
||||||
|
|
||||||
The Hollywood venue is a stylized reconstruction of the real block around the
|
|
||||||
El Capitan Entertainment Centre. It is a game space, not a survey-accurate
|
|
||||||
model: the landmark silhouette, street wall and approach read from the player's
|
|
||||||
eye height, while side streets and interiors are simplified.
|
|
||||||
|
|
||||||
## Real site
|
|
||||||
|
|
||||||
- **Address:** 6840 Hollywood Boulevard, Hollywood, Los Angeles, CA 90028.
|
|
||||||
- **Coordinates:** 34.10118 N, 118.34009 W (OSM way `424024147`).
|
|
||||||
- **Building:** the 1921 Hollywood Masonic Temple, designed by John C. Austin in
|
|
||||||
the Classical Revival style; now the El Capitan Entertainment Centre and the
|
|
||||||
home of *Jimmy Kimmel Live!*.
|
|
||||||
- **Facade:** a light stone/render Neoclassical front with six oversized Ionic
|
|
||||||
columns, a recessed main entrance reached by eight stone steps, ornamental
|
|
||||||
iron door grilles, a continuous crosscut-grille transom, two street-level side
|
|
||||||
entrances, and a parapet with the Masonic motto and circular medallions.
|
|
||||||
- **Neighbours:** the El Capitan Theatre (6838) immediately west, a modern
|
|
||||||
retail block east, and the Ovation Hollywood frontage (6801) across the
|
|
||||||
four-lane boulevard.
|
|
||||||
- **Sidewalk:** the Hollywood Walk of Fame terrazzo strip with brass stars runs
|
|
||||||
along the kerb; the kerb is painted red in the reference photographs.
|
|
||||||
|
|
||||||
## Game mapping
|
|
||||||
|
|
||||||
| Real feature | In-game representation |
|
|
||||||
| --- | --- |
|
|
||||||
| 29.6 m wide, 14.5 m high facade | `VENUE_SPECS["hollywood_building"]`: `facade_width` 29.6, `facade_height` 14.5, front at `z = -4` |
|
|
||||||
| Eight stone steps | `stair_count` 8, `stair_height` 0.10, `stair_tread` 0.38, 12 m wide |
|
|
||||||
| Six Ionic columns | `IonicColonnade` at x = -12.4 … 12.4, 4.96 m bay spacing |
|
|
||||||
| Recessed main entrance + two side doors | central 4.2 m door and 2.5 m side doors with transoms |
|
|
||||||
| Red event banners | three stylized `EL / CAPITAN / LIVE` banners in the central bays |
|
|
||||||
| Masonic motto + medallions | `FriezeInscription` label and two stone discs |
|
|
||||||
| El Capitan Theatre | `NeighbourBlocks/ElCapitanTheatre` with marquee, blade sign and practical light |
|
|
||||||
| Retail block east | `NeighbourBlocks/EastRetailBlock` with glazed shopfront |
|
|
||||||
| Ovation Hollywood | `OvationHollywood` arcade with seven arched openings, sign and south sidewalk |
|
|
||||||
| Walk of Fame | `WalkOfFame` dark terrazzo band with ten stars |
|
|
||||||
| Four-lane boulevard | double-yellow centre, broken white lane lines, red kerb paint and crosswalk |
|
|
||||||
|
|
||||||
The generated AI references live in
|
|
||||||
[`docs/screens/concepts/hollywood_boulevard/`](screens/concepts/hollywood_boulevard/README.md).
|
|
||||||
They are art direction for lighting, material scale and composition; they are
|
|
||||||
not texture or geometry sources.
|
|
||||||
|
|
||||||
## Deliberate simplifications
|
|
||||||
|
|
||||||
- Show branding is replaced by the venue-neutral `EL / CAPITAN / LIVE` banner
|
|
||||||
text. The building name and address are retained.
|
|
||||||
- The opposite Ovation frontage is represented as a single arcade block rather
|
|
||||||
than its full mall plan. Interiors, the Metro entrance and side streets are
|
|
||||||
out of playable bounds.
|
|
||||||
- The AI concept frames are 1280×720 references. The in-game geometry is built
|
|
||||||
from boxes, cylinders and a small number of custom arch/star meshes so it
|
|
||||||
stays inside the Compatibility/Web budget.
|
|
||||||
- The venue menu offers Day and Night Hollywood rows. Night is the same
|
|
||||||
geometry and scenario with a late-phase offset, not a second baked map; the
|
|
||||||
phase still drives sun, sky, fog, windows, signage and practical lights.
|
|
||||||
- The landmark facade uses the shared smooth limestone/stone texture rather
|
|
||||||
than the noisier granite panel tile. The whole venue measures **58,884
|
|
||||||
triangles across 409 meshes** in `test_hollywood_geometry_stays_inside_the_map_budget`,
|
|
||||||
well inside the 150k/600 budget.
|
|
||||||
- Local lights use linear attenuation (`omni_attenuation = 1.0`). Godot treats
|
|
||||||
`0.0` as constant falloff, which previously turned the night facade into a
|
|
||||||
flat yellow wall; the sconces now pool light instead.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [Hollywood Masonic Temple — Wikipedia](https://en.wikipedia.org/wiki/Hollywood_Masonic_Temple)
|
|
||||||
- [El Capitan Entertainment Centre — OpenStreetMap way 424024147](https://www.openstreetmap.org/way/424024147)
|
|
||||||
- [El Capitan Theatre — OpenStreetMap way 424024148](https://www.openstreetmap.org/way/424024148)
|
|
||||||
- [Ovation Hollywood — OpenStreetMap way 151485232](https://www.openstreetmap.org/way/151485232)
|
|
||||||
- [Hollywood Masonic Temple facade — Wikimedia Commons](https://commons.wikimedia.org/wiki/File:El_Capitan_Theater_frontal_fachade-The_Masonic_Temple.jpg)
|
|
||||||
- [Hollywood Masonic Temple and El Capitan Theatre, 2013 — Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Hollywood_Masonic_Temple_2013-10-13_10-02-25.jpg)
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
```bash
|
|
||||||
godot --headless --path . --script res://src/test/test_runner.gd
|
|
||||||
godot --path . --script res://src/test/capture_realism.gd -- --out=/tmp/gig-line-hollywood --stages=day,evening,late
|
|
||||||
python3 tools/check_docs.py
|
|
||||||
```
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
# The Joke Shop — Sunset Strip map notes
|
|
||||||
|
|
||||||
The game's second venue is a fictional comedy club called **The Joke Shop**. Its
|
|
||||||
block, canopy, blade sign, entrance and hillside are based on the real Comedy
|
|
||||||
Store at **8433 Sunset Blvd, West Hollywood, CA 90069**.
|
|
||||||
|
|
||||||
The club name and the game's guest stories are invented. The building is a
|
|
||||||
stylized reconstruction, not a survey-accurate model: the playable frontage is
|
|
||||||
27 m, while the real parcel is much deeper and contains several rooms that the
|
|
||||||
game does not enter.
|
|
||||||
|
|
||||||
## Real site
|
|
||||||
|
|
||||||
| Item | Reference |
|
|
||||||
| --- | --- |
|
|
||||||
| Address | 8433 Sunset Blvd, West Hollywood, CA 90069, United States |
|
|
||||||
| Real venue | The Comedy Store (comedy club and theatre) |
|
|
||||||
| OpenStreetMap | Way [`428854155`](https://www.openstreetmap.org/way/428854155), centroid 34.095265, -118.373965 |
|
|
||||||
| OSM tags | `amenity=theatre`, `building=commercial`, `height=12.2`, `start_date=1940`, `wikidata=Q3520350` |
|
|
||||||
| Footprint | OSM polygon bounding box about **47.1 m × 41.2 m** across the club and rear parcels |
|
|
||||||
| Street | Sunset Boulevard, a major east–west arterial on the Sunset Strip |
|
|
||||||
| Setting | Dry Hollywood Hills directly behind the club, with houses and palms rising above the roofline |
|
|
||||||
|
|
||||||
The building began as **Ciro's**, a 1940 nightclub; it became The Comedy Store
|
|
||||||
in the early 1970s. Its street front is a low black hall under a deep,
|
|
||||||
signature-covered canopy, with the circular blade sign above the roof, red
|
|
||||||
curtains along the frontage and the main entrance set toward the west end. The
|
|
||||||
hillside houses behind it are part of the silhouette from the sidewalk.
|
|
||||||
|
|
||||||
## How the map maps to the real site
|
|
||||||
|
|
||||||
| Real element | Game implementation |
|
|
||||||
| --- | --- |
|
|
||||||
| Low black comedy hall | 27 m × 12 m footprint, 8.4 m street wall; matte black concrete surface with a low plinth and parapet |
|
|
||||||
| Deep overhanging canopy | 4.9 m sloped slab dropping about 1 m to the outer fascia; the fascia carries white signature lettering and the club name |
|
|
||||||
| Signature-covered front | Two `Label3D` signature bands on the fascia plus a smaller wall panel, so the handwriting reads at gameplay distance without dozens of decals |
|
|
||||||
| Circular blade sign | 3.9 m white disc with a black rim on a roof pole; `THE JOKE SHOP` lettering faces the street |
|
|
||||||
| Red curtains | Four three-fold velvet bays with tiebacks between the entrance and the east end; a red velvet entrance curtain |
|
|
||||||
| West-end entrance | Recessed opening at x = -8.8 m with a low step, brass pulls and the door-lock indicator |
|
|
||||||
| Warm practicals | Sconces along the wall, canopy downlights, entrance wash and three ground uplights; all off in daylight and switched by `set_night_mode()` |
|
|
||||||
| Neighbouring low-rise frontage | Cream Mediterranean block west (balconies, red awning) and white commercial block east (ribbon windows, lobby) |
|
|
||||||
| Hillside and houses | Fog-free unshaded 60° ramp behind the rear wall with stucco houses, shrubs and palms; its tint drops to a dark silhouette at night |
|
|
||||||
| Opposite side of Sunset | Low retail row, rooftop billboard, neon and palms as background context beyond the far kerb |
|
|
||||||
| Sidewalk furniture | Palms, queue barriers, stanchions, bins, planters, a-frame sign, hydrant and a parked sedan; prop placements pass the shared validator |
|
|
||||||
|
|
||||||
## Scene composition
|
|
||||||
|
|
||||||
- **Frontage:** 27 m wide, façade centre at z = -8.0, front face at z = -2.0.
|
|
||||||
- **Sidewalk:** 38 m wide × 14 m deep, meeting the kerb at z = 11.9.
|
|
||||||
- **Street:** 90 m × 24 m, with the four-lane markings reused from the
|
|
||||||
Hollywood block and a crosswalk in front of the entrance.
|
|
||||||
- **Playable area:** x = -15…15, z = -0.6…14.0. The guard starts on the west
|
|
||||||
side of the entrance at (-4.8, 0.1, 1.4), facing the approach.
|
|
||||||
- **Map budget:** the headless audit measures **22,680 triangles across 454
|
|
||||||
meshes**, well inside the 150k/700 test budget.
|
|
||||||
|
|
||||||
The same geometry serves the day and late-night looks. There is no second baked
|
|
||||||
map: the phase lighting, skyline/fog tint, lit windows and practicals change,
|
|
||||||
just as with the Hollywood day/night rows.
|
|
||||||
|
|
||||||
## Simplifications and limits
|
|
||||||
|
|
||||||
- The real club's rear rooms, basement and roof plant are not modelled; the
|
|
||||||
playable area is the street frontage only.
|
|
||||||
- The 47 m OSM footprint includes rear parcels and does not match the 27 m
|
|
||||||
street frontage; the game keeps the frontage proportions rather than the full
|
|
||||||
lot.
|
|
||||||
- Neighbouring businesses are represented by generic low-rise blocks. Their
|
|
||||||
exact names and current tenants were not verified, so the docs do not claim
|
|
||||||
them.
|
|
||||||
- The hillside is a background ramp with simplified houses, not a terrain
|
|
||||||
heightfield; it exists to restore the real silhouette above the roofline.
|
|
||||||
- Signage uses the fictional **The Joke Shop** name, not the real Comedy Store
|
|
||||||
wordmark.
|
|
||||||
- The concept frames are photoreal; the game keeps its stylized, low-poly
|
|
||||||
material treatment. The vision judge therefore still scores material and
|
|
||||||
foliage detail below the reference. The remaining texture/realism work is
|
|
||||||
tracked with the postponed 0.9.5 pass, not treated as a map-geometry defect.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- OpenStreetMap way 428854155: <https://www.openstreetmap.org/way/428854155>
|
|
||||||
- Wikimedia Commons photographs used for proportion and material study:
|
|
||||||
- <https://commons.wikimedia.org/wiki/File:ComedyStore_01.jpg>
|
|
||||||
- <https://commons.wikimedia.org/wiki/File:ComedyStore_02.jpg>
|
|
||||||
- <https://commons.wikimedia.org/wiki/File:The_Comedy_Store.jpg>
|
|
||||||
- <https://commons.wikimedia.org/wiki/File:The_Comedy_Store_West_Hollywood_(51017780551).jpg>
|
|
||||||
- AI art-direction frames: [`screens/concepts/joke_shop/`](screens/concepts/joke_shop/README.md)
|
|
||||||
- Verification: `godot --headless --path . --script res://src/test/test_runner.gd`
|
|
||||||
(`test_joke_shop_uses_the_real_sunset_strip_layout`,
|
|
||||||
`test_joke_shop_geometry_stays_inside_the_map_budget`), and
|
|
||||||
`godot --path . --script res://src/test/capture_realism.gd -- --stages=joke_shop_day,joke_shop`.
|
|
||||||
The committed gallery passed `tools/check_screenshots.sh` (13 good / 0 bad)
|
|
||||||
and the screen-design judge (13/13 match, 0 diff). The latest local realism
|
|
||||||
run reports a reference gap of 0.454 for the night entrance.
|
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# MakeHuman head prototype
|
||||||
|
|
||||||
|
Status: **retained fallback; superseded as the active NPC head by the Quaternius
|
||||||
|
base (ADR-0017).** The owner accepted the basic human head on 2026-11-04. The automated vision gate
|
||||||
|
still records REJECT on the eye/lid region as deferred polish. The asset is a
|
||||||
|
standalone head/neck extracted from the CC0 MakeHuman base. The assembled path
|
||||||
|
adds hair, a hidden scalp trim and the procedural body. The 12 shipped GLBs have
|
||||||
|
not been rebuilt with it, and the Quaternius migration replaces it as the active
|
||||||
|
base. The development source files were removed from `tmp/` on 2026-11-05.
|
||||||
|
|
||||||
|
## Asset
|
||||||
|
|
||||||
|
- GLB: [`assets/models/blender_src/makehuman_head.glb`](../assets/models/blender_src/makehuman_head.glb)
|
||||||
|
- Review sheet: [`blender_previews/sheet_makehuman_head.jpg`](blender_previews/sheet_makehuman_head.jpg)
|
||||||
|
- Source workflow (development-only, files removed after the Quaternius pivot;
|
||||||
|
retained here as the historical method) — extracts the MakeHuman head, cuts it at the neck, keeps the head/eye/mouth
|
||||||
|
components, cuts an almond aperture through a skin eyelid mask, adds recessed
|
||||||
|
eyeballs with flat iris/pupil discs, anchors brows to the face surface, renders
|
||||||
|
front/three-quarter/profile and exports the standalone GLB.
|
||||||
|
- Measurements (final standalone build, `inspect`): **12,641 triangles / 7,690
|
||||||
|
split vertices**; bounds approximately `x ±0.094 m`, `y -0.180…0.042 m`,
|
||||||
|
`z 1.530…1.803 m` in project Z-up space.
|
||||||
|
- Assembled `npc_casual --head mh`: **14,753 triangles / 12,823 seam-split
|
||||||
|
vertices / 14 bones / 9 materials**, `inspect` clean. All 12 variants count
|
||||||
|
14,693–15,987 triangles with the same head.
|
||||||
|
- Materials used: `skin`, `eye_white`, `iris`, `pupil`, `hair`. No textures are
|
||||||
|
baked into this prototype.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## What was done
|
||||||
|
|
||||||
|
1. Imported the CC0 MakeHuman base OBJ and cleared the importer axis rotation.
|
||||||
|
2. Applied the project scale/axis transform: 1.82 m body, Z-up, metres,
|
||||||
|
front −Y.
|
||||||
|
3. Cut the head at the neck (`z ≈ 1.53`) rather than keeping the cut shoulders;
|
||||||
|
capped the neck opening.
|
||||||
|
4. Built an eyelid mask by subtracting an almond aperture from a skin sphere
|
||||||
|
centred on each eye (`solver = "EXACT"`), then recessed the globes so the
|
||||||
|
cornea sits behind the lid plane.
|
||||||
|
5. Added flat iris/pupil discs on the globe axis; disabled backface culling so
|
||||||
|
the discs stay visible at three-quarter angles.
|
||||||
|
6. Anchored brow strips to the skin with object-space ray casts and added a
|
||||||
|
shallow supraorbital ridge so they do not float, then reduced the crown
|
||||||
|
height to improve the profile silhouette.
|
||||||
|
7. Fixed a material-remap bug introduced by joining the extra eye objects: the
|
||||||
|
joined head faces were pointing at the `eye_white` slot, making the whole
|
||||||
|
head render as white clay. The largest connected component is now forced
|
||||||
|
back onto `skin` after the join.
|
||||||
|
8. Rendered front/three-quarter/profile views in a neutral review studio and
|
||||||
|
exported the standalone GLB.
|
||||||
|
|
||||||
|
## Provenance
|
||||||
|
|
||||||
|
- Base mesh: MakeHuman / MPFB base, CC0, from the local research set
|
||||||
|
(development source files removed after the 2026-11-05 Quaternius pivot); see
|
||||||
|
[`CREDITS.md`](../CREDITS.md).
|
||||||
|
- The head is a **derived prototype**, not a shipped NPC. It does not enter
|
||||||
|
`assets/models/blender/npc/`, `npc_base`, the runtime `NpcAvatar` path, CI, or
|
||||||
|
a release package. It is retained as a fallback, not the active NPC base.
|
||||||
|
- Source photographs/proportions are not used as textures here; this is a
|
||||||
|
geometry-only head built from the researched model data.
|
||||||
|
|
||||||
|
## Review criteria
|
||||||
|
|
||||||
|
Accept only if:
|
||||||
|
|
||||||
|
- the head reads as human at front/three-quarter/profile and the neck is a
|
||||||
|
clean head/bust silhouette, not a shoulder cut;
|
||||||
|
- eyes, brow, nose, mouth and ears are readable without spikes, holes or
|
||||||
|
floating shells;
|
||||||
|
- the asset is inside the character budget and has no script/contract violation;
|
||||||
|
- the prototype remains clearly local and un-integrated until the part pipeline
|
||||||
|
is approved.
|
||||||
|
|
||||||
|
## Judge result
|
||||||
|
|
||||||
|
**Owner verdict: ACCEPT (basic human head, 2026-11-04).**
|
||||||
|
**Automated vision gate: REJECT** (vision judge `z-ai/glm-5.3-flash`, final
|
||||||
|
standalone build render set above). The eye/lid findings below remain deferred
|
||||||
|
polish; they are no longer a rollout blocker because the owner accepted the head.
|
||||||
|
|
||||||
|
Blocking findings:
|
||||||
|
|
||||||
|
- **P0 — globe depth is inconsistent across views.** The front/three-quarter
|
||||||
|
reads still show a bead-like iris with a pale sclera halo, while the profile
|
||||||
|
reduces the eye to a brown sliver. The judge reads this as a faked recess
|
||||||
|
rather than a socketed globe under a lid.
|
||||||
|
- **P0 — no convincing lid overlap.** The iris remains nearly circular in front
|
||||||
|
and the upper lid does not visibly clip the globe; the skin lid mask reads as
|
||||||
|
a crater around a bead.
|
||||||
|
- **P0 — iris orientation reads decal-locked** at three-quarter, so the gaze
|
||||||
|
appears to drift off the head axis.
|
||||||
|
- **P0 — the aperture/lid mask is too circular** and lacks medial/lateral
|
||||||
|
canthi, so the eye still reads as a doll eye at portrait distance.
|
||||||
|
|
||||||
|
Should-fix findings:
|
||||||
|
|
||||||
|
- the brow tubes are better anchored but the medial ends still detach in
|
||||||
|
three-quarter/profile;
|
||||||
|
- nose lacks a defined tip/wing and nostrils;
|
||||||
|
- mouth is a faint geometric seam with no lip volume;
|
||||||
|
- crown/back-of-skull is still faceted and egg-like in profile;
|
||||||
|
- the catchlight is baked geometry and is not consistent between eyes.
|
||||||
|
|
||||||
|
What passed: the skin/eye material remap is correct, the eye openings are
|
||||||
|
closed, the standalone GLB reports **12,641 triangles / 7,690 split vertices**,
|
||||||
|
and an assembled `npc_casual` with this head passes `inspect` at **14,753 /
|
||||||
|
16,000 triangles, 12,823 seam-split vertices, 14 bones, no issues**.
|
||||||
|
|
||||||
|
Known limits: bald mannequin-like presentation, stylized geometry-only
|
||||||
|
iris/pupil, no rig, no UV texture pass, and the neck cap is a review cut rather
|
||||||
|
than a body-welded seam.
|
||||||
|
|
||||||
|
## Assembled prototype (2026-11-04)
|
||||||
|
|
||||||
|
- `_makehuman_head` imports the GLB and adds a **skull-conforming hair cap**
|
||||||
|
derived from the closed head shell (scaled shell + boolean front-sphere
|
||||||
|
cut + decimate). Hair skips the old `hair_normal.png` because the skull UVs
|
||||||
|
produced large dark tangent patches.
|
||||||
|
- Head faces fully covered by the cap are deleted to recover ~950 triangles;
|
||||||
|
boundary faces stay as a safety band, so the accepted face geometry is
|
||||||
|
untouched. The hidden neck plug uses 12 segments.
|
||||||
|
- The procedural body was narrowed at the neck and shoulders (chest 0.158,
|
||||||
|
neck base 0.060, neck top 0.054, shoulder root z 1.345) and its skin decimate
|
||||||
|
dropped to 0.21 so all 12 variants fit the 16,000 cap.
|
||||||
|
- Local review renders were removed with the 2026-11-05 `tmp/` cleanup; the
|
||||||
|
committed review sheet above is the retained evidence.
|
||||||
|
|
||||||
|
## Deferred polish
|
||||||
|
|
||||||
|
- Replace the radial lid mask with real upper/lower lid masses that fold over
|
||||||
|
the globe, with pointed canthi, and pass the profile view.
|
||||||
|
- Move the iris onto a curved cornea cap so its orientation follows the globe at
|
||||||
|
three-quarter angles, and use a lighting-driven catchlight instead of baked
|
||||||
|
white geometry.
|
||||||
|
- Refine the hair cap silhouette (current short cap reads as a helmet) and
|
||||||
|
improve the full-body shoulder/arm shape before the 12-variant rollout.
|
||||||
|
- Add a clean UV/material pass and a contract armature/weights when the head
|
||||||
|
becomes part of the shipped library characters.
|
||||||
@@ -1,309 +1,324 @@
|
|||||||
# NPC model sourcing and expansion plan
|
# NPC Model Sourcing
|
||||||
|
|
||||||
Status: **direction chosen 2026-11-03.** The procedural generator is the primary
|
This page records the source evaluation and the active character plan. The
|
||||||
path, the Quaternius CC0 pack is a reviewed reference/prototype, and MakeHuman is
|
decision is [ADR-0017](adr/0017-quaternius-npc-base.md), which supersedes the
|
||||||
deferred to a separate local development pipeline. No downloaded source model is
|
generator-first sourcing decision in [ADR-0015](adr/0015-npc-model-variety-sourcing.md).
|
||||||
committed to the game or included in a release; the committed research preview
|
|
||||||
sheets carry the required attribution. Local evaluation files live under
|
|
||||||
`tmp/npc-research/` (gitignored). The decision is recorded in
|
|
||||||
[ADR-0015](adr/0015-npc-model-variety-sourcing.md).
|
|
||||||
|
|
||||||
This page records the free/open-source character sources that were reviewed, how
|
## Active direction
|
||||||
they behave when imported into the existing Blender tooling, and the options for
|
|
||||||
creating a larger NPC model library. The runtime contract and current library are
|
|
||||||
documented in [`blender-assets.md`](blender-assets.md) and
|
|
||||||
[`npc-visual-review.md`](npc-visual-review.md).
|
|
||||||
|
|
||||||
## Why this review exists
|
**Quaternius Ultimate Modular Characters (CC0) is the primary NPC mesh and
|
||||||
|
animation source.** The project-owned procedural library remains the shipped
|
||||||
|
fallback until the Quaternius migration passes the silhouette and gear gates.
|
||||||
|
|
||||||
The library has eight skinned characters, and `NpcAvatar.character_model_for()`
|
The migration keeps the runtime contract that Godot, gear and tests already
|
||||||
reuses them across role families. That is enough for a release candidate, but
|
consume:
|
||||||
crowds repeat the same faces, builds and outfits. Three venues, a 24-NPC budget
|
|
||||||
and close-up portraits make that repetition visible.
|
|
||||||
|
|
||||||
Any expansion must preserve the existing integration contract:
|
- one `npc_base` skinned mesh under the 13 required core bones (optional extras
|
||||||
|
allowed);
|
||||||
|
- five tint slots: `skin`, `cloth`, `pants`, `accessory`, `hair`;
|
||||||
|
- front on Blender −Y / Godot +Z, feet grounded, height about 1.81 m;
|
||||||
|
- 24,000 triangles / 24,000 glTF vertices per character;
|
||||||
|
- gear stays at 1,500 triangles and props at 4,000;
|
||||||
|
- committed GLBs and maps only; no Quaternius, Blender or network dependency in
|
||||||
|
CI, build or runtime.
|
||||||
|
|
||||||
- mesh node `npc_base` under a `Skeleton3D`;
|
### Stage 1 (default)
|
||||||
- bones `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R` plus
|
|
||||||
`Hips`, `Spine`, `Neck` at the contract rest positions;
|
|
||||||
- five tint slots (`skin`, `cloth`, `pants`, `accessory`, `hair`);
|
|
||||||
- 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;
|
|
||||||
- gear attach points at head, back, chest and hand.
|
|
||||||
|
|
||||||
The existing `audit` command is the gate. A source that looks good in a preview
|
1. Import the researched Quaternius GLBs and choose a canonical base.
|
||||||
but fails `audit` is not usable without an adaptation step.
|
2. Map the 62 source joints to the 13-core contract and bake a proportion warp.
|
||||||
|
3. Skin the mesh, retarget Idle and Walk, and verify seated/arm-down poses.
|
||||||
|
4. Map the five slots, unwrap and bake 256² maps.
|
||||||
|
5. Fit the existing gear and render the silhouette gate.
|
||||||
|
6. Build the 12 variants, audit, and capture the 12 × 6 baseline.
|
||||||
|
|
||||||
## What was evaluated
|
The Quaternius 62-joint rig and its 24 animation clips are an **authoring
|
||||||
|
source**. They are not loaded at runtime.
|
||||||
|
|
||||||
Blender 5.2.0 LTS (Flatpak) imported each file in background mode. Measurements
|
### Stage 2 gate
|
||||||
below come from the imported scene, because external GLBs often carry node
|
|
||||||
transforms that the project's audit (which reads accessor-local bounds) does not
|
|
||||||
apply. "Vertices" are the imported, seam-split counts that matter for the glTF
|
|
||||||
budget, not the source file's vertex count.
|
|
||||||
|
|
||||||
| Source | Licence | Files checked | Geometry / rig | Height | Verdict |
|
The 62-joint T-pose source does not share the project's arm-down rest pose or
|
||||||
| --- | --- | --- | --- | --- | --- |
|
proportions. If the Stage 1 proportion warp cannot preserve the Quaternius
|
||||||
| **Quaternius Ultimate Modular Men / Women** (mirrored GLB pack) | CC0 1.0 | `Casual_Hoodie.glb`, `Worker.glb`, `Casual.glb` | 5,240–6,424 tris; 10,534–12,968 verts; 62 bones; 24 animations; 4 skinned meshes on one armature | 1.80–1.87 m | **Best silhouette/outfit match.** Fails the contract: wrong bone names/rest positions, no tint slots, no `npc_base` node, vertex budget exceeded. |
|
silhouette and deformation, stop the rollout and write a new ADR for adopting
|
||||||
| **Kenney Blocky Characters 2.0** | CC0 1.0 | full pack (18 characters), `character-a.glb`, `character-f.glb` | 72 tris; 143 verts; static, no armature; one texture atlas per character | 2.7 m imported (needs 0.67× scale) | **Not a close-up character.** Useful as a crowd/impostor LOD or placeholder, but cannot be driven by the skeleton and has no separate face/clothing slots. |
|
the 62-joint rig at runtime. Do not ship a hybrid rig, a second gear offset
|
||||||
| **Khronos CesiumMan** | CC BY 4.0 (© Cesium) | `CesiumMan.glb` | 4,672 tris; 3,273 verts; 19-bone walk rig; 1 material/texture; 1 animation | 1.45 m | **Technical retarget test only.** One outfit and one look, wrong rig, short; works as a skinning/pose reference. Attribution is required. |
|
system or a silent `NpcAvatar` change.
|
||||||
| **Khronos RiggedFigure** | CC BY 4.0 (© Cesium) | `RiggedFigure.glb` | 256 tris; 370 verts; 19-bone walk rig; no UVs; 1 animation | 1.48 m | **Rig smoke test only.** A mannequin with no usable surface detail. |
|
|
||||||
| **MakeHuman core base mesh / MPFB** | Core assets CC0 1.0; application AGPL/GPL | `makehuman/data/3dobjs/base.obj` | 19,158 verts; 18,486 polygons; 36,972 tris; no rig in the base mesh | ~16.9 model units before scaling | **Best body-shape diversity, highest pipeline cost.** Needs scaling, decimation/retopology and a generated rig. The MakeHuman/MPFB tool itself is not bundled. |
|
|
||||||
|
|
||||||
Local review sheet (front views, not committed):
|
## Research findings
|
||||||
`tmp/npc-research/previews/contact_sheet.jpg`.
|
|
||||||
|
|
||||||
Rejected without downloading: Mixamo and Ready Player Me. Both are useful
|
The researched files are the three converted Quaternius outfits used by the
|
||||||
services, but their terms are not a simple free/open-source redistribution basis
|
Ducz Character Creator: `Casual`, `Casual_Hoodie` and `Worker`. They are kept
|
||||||
for shipped character meshes. Sketchfab, Poly Pizza and OpenGameArt can contain
|
locally in `tmp/npc-research/quaternius/` with the licence and mirror notes.
|
||||||
excellent CC0 work, but each asset needs a case-by-case licence and provenance
|
|
||||||
check; they are not a consistent library source.
|
|
||||||
|
|
||||||
### Measured contract failures
|
| File | SHA-256 | Structure |
|
||||||
|
|
||||||
Every example fails the unmodified `audit` contract:
|
|
||||||
|
|
||||||
| Check | Quaternius | Kenney | CesiumMan | RiggedFigure | MakeHuman base |
|
|
||||||
| --- | --- | --- | --- | --- | --- |
|
|
||||||
| `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 |
|
|
||||||
| Five tint slots | no | no | no | no | no |
|
|
||||||
| ≤ 8,000 tris | yes | yes | yes | yes | no |
|
|
||||||
| ≤ 8,000 verts | no (10.5–13k) | yes | yes | yes | no (19k) |
|
|
||||||
| 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 |
|
|
||||||
| Skinned / UV mapped | yes / yes | no / yes | yes / yes | yes / no | no / no |
|
|
||||||
|
|
||||||
The practical conclusion is that **there is no drop-in download**. External
|
|
||||||
sources need a deliberate intake step, and the expensive part is not decimation:
|
|
||||||
it is rebuilding or retargeting the skeleton and material slots without breaking
|
|
||||||
the animation proxies and gear placement.
|
|
||||||
|
|
||||||
The table uses Blender-imported world bounds. The stdlib `audit` reads accessor-local
|
|
||||||
bounds and ignores node transforms, so an unapplied external GLB can fail floor/height
|
|
||||||
checks even when the imported scene looks correct. The intake step must apply
|
|
||||||
transforms before it measures and exports.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
### Option A — extend the procedural generator
|
|
||||||
|
|
||||||
Add more variation to `tools/blender_assets.py` rather than importing models:
|
|
||||||
body builds, height ranges, head/face shapes, hair and facial hair, outfit
|
|
||||||
layers, and more `CHARACTERS` specs. This keeps a single owned style, needs no
|
|
||||||
third-party licence review, and stays inside the existing builder/audit flow.
|
|
||||||
|
|
||||||
- **Strengths:** lowest integration risk, deterministic, no new dependencies.
|
|
||||||
- **Limits:** the current face and clothing vocabulary is intentionally simple;
|
|
||||||
more variants may still read as the same model with a different coat.
|
|
||||||
- **Best for:** a quick 4–8 variant expansion and filling obvious role gaps.
|
|
||||||
|
|
||||||
### Option B — Quaternius CC0 intake with a contract adapter
|
|
||||||
|
|
||||||
Use Quaternius meshes as silhouette/outfit donors and adapt them to the project
|
|
||||||
contract. The visible result is a different body/clothing language from the
|
|
||||||
current library, so the adapter must also normalise head/hand style or rebuild
|
|
||||||
those parts.
|
|
||||||
|
|
||||||
- **Strengths:** best free outfit/hair variety, animations included, CC0,
|
|
||||||
already close to game proportions, front axis and floor contact.
|
|
||||||
- **Work:** weld/decimate to the vertex budget, remap materials to the five
|
|
||||||
slots, rebuild the armature at the contract rest positions, transfer weights,
|
|
||||||
repose/normalise proportions, rename the mesh node, export, audit and compare
|
|
||||||
in the acceptance harness.
|
|
||||||
- **Main risk:** retargeting a 62-bone T-pose rig to the 13-bone arm-down
|
|
||||||
contract. Hip/knee rest heights differ substantially (Quaternius upper leg
|
|
||||||
starts near 0.96 m; the contract hip starts near 0.62 m), so a naive
|
|
||||||
nearest-bone weight transfer can produce a good still image and bad walk
|
|
||||||
poses. Prototype one character before committing to a roster.
|
|
||||||
- **Best for:** a larger, visibly different library if the prototype passes the
|
|
||||||
portrait and walk gates.
|
|
||||||
|
|
||||||
### Option C — MakeHuman/MPFB body-shape pipeline
|
|
||||||
|
|
||||||
Generate a range of ages, builds and body shapes from the CC0 MakeHuman base
|
|
||||||
mesh, then replace the head/hands and dress them with project-owned clothing.
|
|
||||||
This is the strongest answer to "everyone has the same body", but requires an
|
|
||||||
offline authoring step, decimation/retopology and a custom rig/weight step. It
|
|
||||||
should be treated as a later pipeline project, not a quick content drop.
|
|
||||||
|
|
||||||
## Decision and prototype outcome
|
|
||||||
|
|
||||||
The project owner reviewed this page on 2026-11-03. The decision is:
|
|
||||||
|
|
||||||
1. **CC0 is acceptable** for shipped character assets. CC-BY is acceptable only
|
|
||||||
with attribution in `CREDITS.md`; service assets whose terms restrict
|
|
||||||
redistribution remain excluded.
|
|
||||||
2. **Extend the procedural generator** as the primary path, starting with
|
|
||||||
crowd-variety looks.
|
|
||||||
3. **Prototype Quaternius** as a secondary source. It is not approved for
|
|
||||||
shipping yet; the contract-retarget result below is a no-go for the current
|
|
||||||
skeleton.
|
|
||||||
4. **Use crowd variety** as the first visual goal, not role-specific costumes.
|
|
||||||
5. **Evaluate MakeHuman for a later, separate local development pipeline.** It
|
|
||||||
is not part of the normal build, CI or release flow.
|
|
||||||
6. **Commit the research previews with attribution.** The CC-BY entries are
|
|
||||||
listed in `CREDITS.md` and remain development-only.
|
|
||||||
|
|
||||||
### Quaternius prototype
|
|
||||||
|
|
||||||
The prototype used `Casual_Hoodie.glb`:
|
|
||||||
|
|
||||||
- **Geometry budget is not the blocker.** Imported: 12,518 vertices / 6,206
|
|
||||||
triangles. After welding seam splits: 3,199 vertices / 6,206 triangles. After
|
|
||||||
a 0.75 decimate: 2,423 vertices / 4,654 triangles, comfortably inside the
|
|
||||||
library budget.
|
|
||||||
- **Animation quality is attractive.** The imported Idle and Walk clips read
|
|
||||||
well at game distance, and the model brings 24 animations.
|
|
||||||
- **The contract rest pose is the blocker.** The source is a T-pose 62-bone rig
|
|
||||||
with realistic proportions: upper leg starts near 0.96 m, arm span is about
|
|
||||||
1.68 m, and the shoulder joint sits near x = 0.153 m. The project contract is
|
|
||||||
an arm-down 13-bone rig with a hip near 0.62 m, a shoulder near x = 0.27 m and
|
|
||||||
a hand near z = 0.78 m. Rebuilding the contract armature around this mesh would
|
|
||||||
either distort the body substantially or require a full proportion warp.
|
|
||||||
- **Decision:** do not retarget Quaternius to the contract now. Keep it as a
|
|
||||||
silhouette/style reference and, if a second rig path is ever wanted, prototype
|
|
||||||
it as its own animation path (its own skeleton, AnimationPlayer clips and
|
|
||||||
per-model gear offsets) rather than forcing it into `npc_base`. That is a
|
|
||||||
separate architecture decision, not an asset conversion.
|
|
||||||
|
|
||||||
Preview: [`blender_previews/sheet_npc_sources_research.jpg`](blender_previews/sheet_npc_sources_research.jpg).
|
|
||||||
|
|
||||||
### Generator crowd prototype
|
|
||||||
|
|
||||||
Four proposed crowd specs were built with the existing generator in a temporary
|
|
||||||
output directory. All four audit clean and stay inside the character budget:
|
|
||||||
|
|
||||||
| Prototype | Look | Tris / verts | Gear |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `npc_crowd_a` | slim, bun, sunglasses | 4,644 / 4,356 | sunglasses |
|
|
||||||
| `npc_crowd_b` | broad, bald, beard | 4,550 / 4,453 | shoulder bag |
|
|
||||||
| `npc_crowd_c` | average, short hair, long coat | 5,118 / 5,086 | umbrella |
|
|
||||||
| `npc_crowd_d` | slim, short hair, hood up | 5,258 / 4,907 | cup |
|
|
||||||
|
|
||||||
They vary build, head silhouette, hair/beard and outer layer while keeping the
|
|
||||||
existing face language. They are **not in the shipped catalogue**; production
|
|
||||||
integration belongs to the roster/runtime work under the unversioned `Character
|
|
||||||
development` section, after the current release candidate.
|
|
||||||
|
|
||||||
Preview: [`blender_previews/sheet_npc_crowd_prototypes.jpg`](blender_previews/sheet_npc_crowd_prototypes.jpg).
|
|
||||||
|
|
||||||
### MakeHuman local-pipeline evaluation
|
|
||||||
|
|
||||||
The CC0 base mesh is 19,158 vertices / 18,486 polygons (36,972 triangles after
|
|
||||||
triangulation), with 21,334 UV coordinates but no normals, material groups or
|
|
||||||
rig. Scaling it to 1.82 m and decimating to 0.35 produced 3,780 vertices / 6,468
|
|
||||||
triangles, which fits the geometry budget but is a collapse-decimation result,
|
|
||||||
not production topology.
|
|
||||||
|
|
||||||
MakeHuman is therefore **feasible only as a separate local body-shape pipeline**.
|
|
||||||
The boundary for that future work is:
|
|
||||||
|
|
||||||
- MPFB/MakeHuman is installed and run by a contributor, never by CI or the game
|
|
||||||
build.
|
|
||||||
- Generated bodies and intermediate exports stay in `tmp/` (gitignored); the
|
|
||||||
CC0 base mesh is not committed.
|
|
||||||
- The contributor scales to 1.8–1.9 m, decimates or retopologises to roughly
|
|
||||||
6–7k triangles, and checks the silhouette against `npc-visual-review.md`.
|
|
||||||
- The body gets the contract armature, weights, project-owned head/hands,
|
|
||||||
clothing and gear; it must pass `blender_assets.py audit` and `fit`.
|
|
||||||
- Only the final CC0-derived GLB and its provenance note are committed. The
|
|
||||||
application code and any non-CC0 assets stay outside the repository.
|
|
||||||
|
|
||||||
This is deferred; it is not part of the current crowd-variety work.
|
|
||||||
|
|
||||||
## Provisional NPC roster
|
|
||||||
|
|
||||||
The design rule stays: clothes, props and behaviour carry the read; a model must
|
|
||||||
not label a person as "the threat". The roster therefore adds ordinary adults
|
|
||||||
across age, build and style, not costumes that telegraph the outcome.
|
|
||||||
|
|
||||||
| Look family | Current | Proposed additions | Variation axes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| Everyday | `npc_casual`, `npc_jacket`, `npc_coat`, `npc_hoodie`, `npc_dress` | `npc_crowd_a` (bun/slim), `npc_crowd_b` (bald/broad), `npc_crowd_c` (coat/umbrella), `npc_crowd_d` (hood/cup) | build, height, hair, facial hair, outer layer, accessory |
|
|
||||||
| Workwear | `npc_vest`, `npc_guard` | later: second hi-vis build, venue-security build | build, height, headwear, badge/radio gear |
|
|
||||||
| Responder | `npc_firefighter` | later: paramedic/medical responder only if the design supports it | turnout vs medical kit, build, helmet |
|
|
||||||
| Crowd/LOD | none | optional Kenney blocky or a decimated library variant for distant crowds | silhouette and palette only |
|
|
||||||
|
|
||||||
A first target is **12 library characters** (the current eight plus the four
|
|
||||||
crowd prototypes), not twenty. That is enough to break the clone effect without
|
|
||||||
doubling the portrait QA matrix. `character_model_for()` becomes a pool per role
|
|
||||||
family with a deterministic seed choice; the current two-way alternation for
|
|
||||||
guests, trespassers and threats is the template.
|
|
||||||
|
|
||||||
## Implementation plan
|
|
||||||
|
|
||||||
The actionable tasks live in `TODO.md` under the `Character development` section. The workstreams
|
|
||||||
are:
|
|
||||||
|
|
||||||
1. **Decision and licence gate.** Complete: ADR-0015, the CC0/CC-BY rule and the
|
|
||||||
committed preview sheets.
|
|
||||||
2. **Generator crowd variants.** Complete as a prototype (four specs, clean
|
|
||||||
audit, review sheet). Production integration is pending the current release candidate.
|
|
||||||
3. **Quaternius intake.** The contract-retarget prototype is a no-go. Keep the
|
|
||||||
source as a reference; a second-rig/animation path would need its own ADR.
|
|
||||||
4. **Roster build and runtime.** Add the four approved specs to `CHARACTERS`,
|
|
||||||
build and commit their GLBs, update the manifest, and extend the role pools.
|
|
||||||
5. **QA and integration.** Run the 24-NPC budget, portrait baseline, gear fit,
|
|
||||||
headless suite and release build. Record provenance and any accepted limits.
|
|
||||||
Only then update `CHANGELOG.md` and `docs/development-status.md`.
|
|
||||||
|
|
||||||
### Adapter notes
|
|
||||||
|
|
||||||
- Apply node transforms before measuring or exporting. CesiumMan imports with a
|
|
||||||
Z-up root and an `Icosphere` helper object; exclude helpers, not just the mesh
|
|
||||||
names, when measuring.
|
|
||||||
- Weld duplicate seam vertices before decimating. Quaternius' 12k imported
|
|
||||||
vertices are mostly UV/normal splits; the underlying surface is much smaller,
|
|
||||||
but the glTF budget counts the splits after export.
|
|
||||||
- Decimate to a target below 8,000 tris/verts, then re-unwrap only if the
|
|
||||||
existing UVs cannot survive the material remap. The current library uses
|
|
||||||
smart projection at 80° to keep islands large.
|
|
||||||
- Map materials by semantic role, not by source name. A source "Purple" or
|
|
||||||
"Worker_Vest" needs an explicit mapping to `cloth`, `pants`, `skin`, `hair` or
|
|
||||||
`accessory`; do not rely on colour heuristics.
|
|
||||||
- Keep the face direction unambiguous. The front is Blender −Y; verify it with
|
|
||||||
a capture, not by guessing from the source file.
|
|
||||||
- For any future external-rig path, do not silently add a second skeleton format
|
|
||||||
to `NpcAvatar`. Record the per-model skeleton, animation source and gear
|
|
||||||
offsets as an explicit contract.
|
|
||||||
|
|
||||||
## Review outcome
|
|
||||||
|
|
||||||
The project owner answered the review questions on 2026-11-03:
|
|
||||||
|
|
||||||
| Question | Answer |
|
|
||||||
| --- | --- |
|
|
||||||
| Are third-party CC0 characters acceptable? | Yes. |
|
|
||||||
| Which direction? | Prototype Quaternius and extend the procedural generator. |
|
|
||||||
| Which looks first? | Crowd variety. |
|
|
||||||
| Should MakeHuman be pursued? | Evaluate it for a later, separate local development pipeline. |
|
|
||||||
| Commit the previews? | Yes, with CC-BY attribution. |
|
|
||||||
|
|
||||||
## Downloaded example provenance
|
|
||||||
|
|
||||||
Source models were downloaded on 2026-11-03 and kept out of version control. The two
|
|
||||||
research preview sheets are committed under `docs/blender_previews/`; the CC-BY
|
|
||||||
attribution is recorded in `CREDITS.md`.
|
|
||||||
|
|
||||||
| File | Source | SHA-256 |
|
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `quaternius/Casual_Hoodie.glb` | `raw.githubusercontent.com/LuanDucate/Ducz.CharacterCreator/HEAD/public/models/Male/Casual_Hoodie.glb` (Quaternius CC0) | `f5b722199f7628b24550989af4230f65d5230727df9c326fd2b3e360effe5249` |
|
| `Casual.glb` | `bc2d84fba249ebaa0629cb9d133ad37234196e4b8004aa205a6397abed04e05c` | 4 meshes, 62 joints, 24 clips, 7 materials |
|
||||||
| `quaternius/Worker.glb` | same mirror, `Male/Worker.glb` | `8d0f454336c2effc5c08f63a177af3a82d1a7af025ca9588b40e4c90c4703534` |
|
| `Casual_Hoodie.glb` | `f5b722199f7628b24550989af4230f65d5230727df9c326fd2b3e360effe5249` | 4 meshes, 62 joints, 24 clips, 7 materials |
|
||||||
| `quaternius/Casual.glb` | same mirror, `Female/Casual.glb` | `bc2d84fba249ebaa0629cb9d133ad37234196e4b8004aa205a6397abed04e05c` |
|
| `Worker.glb` | `8d0f454336c2effc5c08f63a177af3a82d1a7af025ca9588b40e4c90c4703534` | 4 meshes, 62 joints, 24 clips, 11 materials |
|
||||||
| `kenney_blocky_characters.zip` | `kenney.nl/assets/blocky-characters` | `5e123859aa0c1598342b600c6db197024a1d63eb9ec531398b310725f589887e` |
|
|
||||||
| `khronos/CesiumMan.glb` | `github.com/KhronosGroup/glTF-Sample-Assets` | `b7001eaeea8254bd44773bcd247e78696d94169388fbb2a1800fc69434e777d9` |
|
|
||||||
| `khronos/RiggedFigure.glb` | same repository | `d6be85417d3e256861ee733eea6916093a7af7c79c16366181fd8abcaeb38cf5` |
|
|
||||||
| `makehuman/base.obj` | `github.com/makehumancommunity/makehuman/blob/master/makehuman/data/3dobjs/base.obj` | `8e761e6624b8f54536409135d1636da63b32486a90d4897f84e121d144f6fb4c` |
|
|
||||||
|
|
||||||
Source terms:
|
Source: [Quaternius Ultimate Modular Characters](https://quaternius.com/packs/ultimatemodularcharacters.html)
|
||||||
|
and [Ultimate Modular Women](https://quaternius.com/packs/ultimatemodularwomen.html),
|
||||||
|
CC0 1.0. Download date: 2026-11-03 (via the Ducz Character Creator browser export).
|
||||||
|
The Ducz mirror is a browser builder over the same packs; it adds 21
|
||||||
|
outfits and a shared skeleton. The three full GLBs are the initial scope; the
|
||||||
|
21-part builder is a fallback for role coverage, not the default.
|
||||||
|
|
||||||
- Quaternius packs are CC0 1.0 according to the pack page and the mirror's
|
**Initial outfit scope (2026-11-05):** the three full GLBs (`Casual`,
|
||||||
credits. The mirror is a convenience download; the original pack page is
|
`Casual_Hoodie`, `Worker`) are the canonical-base and silhouette-gate scope.
|
||||||
<https://quaternius.com/packs/ultimatemodularcharacters.html>.
|
They cover only ~3 of the 12 roles (casual, hood, vest); the jacket, coat,
|
||||||
- Kenney Blocky Characters 2.0 is CC0 1.0; the pack's `License.txt` is included
|
dress, responder (firefighter) and guard looks are not in the three GLBs, so
|
||||||
in the download.
|
§4 roster coverage must pull those from the 21-part modular builder or author
|
||||||
- CesiumMan and RiggedFigure are CC BY 4.0, © 2017 Cesium. If any derivative is
|
new clothing. Do not start the modular builder until the canonical base passes
|
||||||
committed or shipped, the attribution must be added to `CREDITS.md`.
|
§3.
|
||||||
- MakeHuman core assets are CC0 per `LICENSE.ASSETS.md`; the application source
|
|
||||||
is AGPL and is not bundled.
|
The first prototype measured 12,518 imported vertices, 3,199 after welding and
|
||||||
|
2,423 vertices / 4,654 triangles after a 0.75 decimate. Idle and Walk read well.
|
||||||
|
The source is a T-pose with a 62-joint realistic rig, roughly 0.96 m hip,
|
||||||
|
1.68 m arm span and 0.153 m shoulder offset, so a contract bake is required.
|
||||||
|
|
||||||
|
The 24,000 cap leaves room to stay much closer to the source LOD than the
|
||||||
|
prototype's 4,654 triangles. Do not decimate to the old 7,000-triangle target
|
||||||
|
before the silhouette gate.
|
||||||
|
|
||||||
|
## Quaternius GLB inventory (2026-11-05)
|
||||||
|
|
||||||
|
All three GLBs share one 62-joint rig and the same 24 animation clips; only the meshes and
|
||||||
|
materials differ. Each import also adds a 42-vert/80-tri `Icosphere` bone-display helper — ignore it.
|
||||||
|
|
||||||
|
### Meshes (verts / tris)
|
||||||
|
|
||||||
|
| GLB | Body | Feet | Head | Legs | Total |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| `Casual` | 5,338 / 2,654 | 1,082 / 538 | 5,122 / 2,526 | 1,426 / 706 | 12,968 / 6,424 |
|
||||||
|
| `Casual_Hoodie` | 6,132 / 3,048 | 1,024 / 512 | 3,836 / 1,888 | 1,526 / 758 | 12,518 / 6,206 |
|
||||||
|
| `Worker` | 6,074 / 3,018 | 928 / 464 | 1,862 / 936 | 1,670 / 822 | 10,534 / 5,240 |
|
||||||
|
|
||||||
|
### Joints (62)
|
||||||
|
|
||||||
|
- Spine: `Root, Hips, Torso, Body, Abdomen, Chest, Neck, Head` (8).
|
||||||
|
- Arms: `Shoulder.L/R, UpperArm.L/R, LowerArm.L/R, Wrist.L/R` (8) plus `PT.L/R` (2 pole targets).
|
||||||
|
- Fingers: `Thumb×3, Index×4, Middle×4, Ring×4, Pinky×4` × L/R (30).
|
||||||
|
- Legs: `UpperLeg.L/R, LowerLeg.L/R, Foot.L/R` (6).
|
||||||
|
|
||||||
|
### Animation clips (24)
|
||||||
|
|
||||||
|
`Idle, Idle_Neutral, Walk, Run, Run_Back, Run_Left, Run_Right, Wave, Interact, Death,
|
||||||
|
Gun_Shoot, Idle_Gun, Idle_Gun_Pointing, Idle_Gun_Shoot, Idle_Sword, Run_Shoot, Sword_Slash,
|
||||||
|
HitRecieve, HitRecieve_2, Kick_Left, Kick_Right, Punch_Left, Punch_Right, Roll`.
|
||||||
|
|
||||||
|
The retarget scope is Idle and Walk; combat/gun/sword clips are discarded (see §2).
|
||||||
|
|
||||||
|
### Material → slot map
|
||||||
|
|
||||||
|
The five project slots (`skin`, `cloth`, `pants`, `accessory`, `hair`) are assigned by mesh
|
||||||
|
region first, then material — the same material name can mean `cloth` on the body and
|
||||||
|
`accessory` on the feet.
|
||||||
|
|
||||||
|
| Material | Where | Project slot |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Skin` | all meshes | `skin` |
|
||||||
|
| `Hair_Blond`, `Hair_Brown`, `Hair`, `Brown` (head), `Eyebrows`, `Moustache` | Head | `hair` |
|
||||||
|
| `Eye` | Head | `accessory` |
|
||||||
|
| `Worker_Yellow` | Worker body (vest stripe) + head (helmet) | `cloth` / `accessory` |
|
||||||
|
| `White`, `Grey`, `Purple`, `Black` | Body / Feet | `cloth` (body) / `accessory` (feet) |
|
||||||
|
| `Orange`, `LightBlue`, `LightBrown`, `Brown`, `Brown2` | Legs | `pants` |
|
||||||
|
| `Worker_Vest` | Worker body | `cloth` |
|
||||||
|
|
||||||
|
### Canonical base (built 2026-11-05)
|
||||||
|
|
||||||
|
`tmp/npc-research/quaternius_base.glb` is the welded, triangulated, grounded, animation-free
|
||||||
|
canonical base built from `Casual_Hoodie.glb` (the most detailed body) by
|
||||||
|
`tmp/npc-research/build_quaternius_base.py`:
|
||||||
|
|
||||||
|
- one `npc_base` mesh — 3,199 welded verts / 6,206 tris (Body + Feet + Head + Legs joined);
|
||||||
|
- 62 joints, skinned (no unskinned faces);
|
||||||
|
- height 1.81 m, feet at y 0, front on Godot +Z / Blender −Y (T-pose rest);
|
||||||
|
- no animation clips; `inspect` clean (no issues).
|
||||||
|
|
||||||
|
This is the §2 contract-bake input: the 62-joint rig is authoring-only and is mapped to the
|
||||||
|
13-core contract next, not shipped as-is.
|
||||||
|
|
||||||
|
## 62-joint → 13-core joint map (2026-11-05)
|
||||||
|
|
||||||
|
The Quaternius hierarchy (measured from `quaternius_base.glb`):
|
||||||
|
|
||||||
|
- `Root → Body → Hips → Abdomen → Torso → Chest → Neck → Head` (spine);
|
||||||
|
- `Chest → Shoulder.L/R → UpperArm.L/R → LowerArm.L/R → Wrist.L/R → fingers` (arms);
|
||||||
|
- `Body → UpperLeg.L/R → LowerLeg.L/R` and `Root → Foot.L/R` (legs — the foot is a separate
|
||||||
|
child of Root, not of LowerLeg);
|
||||||
|
- `Root → PT.L/R` (IK pole targets, no deforming weights).
|
||||||
|
|
||||||
|
The contract is 14 bones (13 core + `Hips` root): `Hips, Spine, Neck, Head, Shoulder.L/R,
|
||||||
|
Elbow.L/R, Hand.L/R, Hip.L/R, Knee.L/R`. The map:
|
||||||
|
|
||||||
|
| Contract bone | Quaternius source | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Hips` | `Hips` | `Root` + `Body` collapse into `Hips` |
|
||||||
|
| `Spine` | `Abdomen`, `Torso`, `Chest` | three collapse into one |
|
||||||
|
| `Neck` | `Neck` | |
|
||||||
|
| `Head` | `Head` | |
|
||||||
|
| `Shoulder.L/R` | `UpperArm.L/R` | the collar `Shoulder.L/R` collapses into `Spine` |
|
||||||
|
| `Elbow.L/R` | `LowerArm.L/R` | |
|
||||||
|
| `Hand.L/R` | `Wrist.L/R` | |
|
||||||
|
| `Hip.L/R` | `UpperLeg.L/R` | |
|
||||||
|
| `Knee.L/R` | `LowerLeg.L/R` | |
|
||||||
|
|
||||||
|
Optional extras (allowed by the contract; kept if they survive within 24,000 vertices):
|
||||||
|
|
||||||
|
- the 30 finger bones (`Thumb×3, Index×4, Middle×4, Ring×4, Pinky×4` × L/R) → optional finger
|
||||||
|
bones;
|
||||||
|
- `Foot.L/R` → optional toe bones.
|
||||||
|
|
||||||
|
Dropped (never shipped): `PT.L/R` (pole targets carry no deforming weights), and the
|
||||||
|
`Root`/`Body`/collar-`Shoulder` collapse bones above.
|
||||||
|
|
||||||
|
## Rest-pose and proportion-warp measurement (2026-11-05)
|
||||||
|
|
||||||
|
The rest-pose bake (T-pose → arm-down) works: rotate the `UpperArm.L/R` pose bones −90° around
|
||||||
|
their local X, apply the Armature modifier to bake the deformation, then `armature_apply` to bake
|
||||||
|
it as the new rest pose (see `tmp/npc-research/warp_full.py`). The mesh deforms cleanly to the
|
||||||
|
arm-down pose (deformed bounds x ±0.221 vs the T-pose ±0.811).
|
||||||
|
|
||||||
|
The proportion warp to the contract positions is measured and it **distorts the silhouette**:
|
||||||
|
|
||||||
|
| Joint | Quaternius | Contract | Delta |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Hips height | 0.869 | 0.62 | −0.249 (lower hip) |
|
||||||
|
| Head top | 1.614 | 1.82 | +0.206 (raise head) |
|
||||||
|
| Shoulder x | 0.148 | 0.27 | +0.122 (widen) |
|
||||||
|
| Arm (shoulder→wrist) | 0.402 | 0.60 | ×1.49 |
|
||||||
|
| Arm (shoulder→hand tip) | 0.429 | 0.70 | ×1.63 |
|
||||||
|
| Leg (hip→ankle) | 0.835 | 0.62 | ×0.74 |
|
||||||
|
| Torso (hip→head) | 0.745 | 1.20 | ×1.61 |
|
||||||
|
|
||||||
|
The vision judge reads the warped mesh as: paddle/claw hands (the 1.63× arm stretch blows the
|
||||||
|
hand out), forearms lost into the torso, stretched shins, and "long legs + short torso". The arm
|
||||||
|
must either stay short (a "shortened arm" vs the contract hand) or stretch 1.63× (a distorted
|
||||||
|
deltoid + hand) — both are the gate's rejection conditions.
|
||||||
|
|
||||||
|
**Finding:** the Stage 1 proportion warp cannot preserve the Quaternius silhouette at the
|
||||||
|
contract proportions; the gate fails and Stage 2 (a 62-joint runtime ADR) is indicated. Do not
|
||||||
|
start the 12-variant rebuild on the warped base.
|
||||||
|
|
||||||
|
[ADR-0018](adr/0018-quaternius-stage2-runtime-rig.md) records the Stage 2 decision (accepted
|
||||||
|
2026-11-05, owner chose option 2): adopt the **Quaternius 62-joint rig at runtime**, keep the
|
||||||
|
mesh at source LOD, re-derive gear attach points from the Quaternius skeleton, and use the
|
||||||
|
Quaternius Idle/Walk clips directly (no retarget).
|
||||||
|
|
||||||
|
## Pilot bake and shipped state (2026-11-05)
|
||||||
|
|
||||||
|
`npc_casual` is the first Quaternius-derived character. The reproducible pipeline lives in
|
||||||
|
`tmp/npc-research/` (local tooling, not shipped) and has two steps:
|
||||||
|
|
||||||
|
1. `build_base_minimal.py` — import `quaternius/Casual_Hoodie.glb`, join the four skinned meshes
|
||||||
|
**without welding**, rename the upstream materials to the project tint slots, Smart-UV-unwrap
|
||||||
|
and repack, export `quaternius_tpose.glb`.
|
||||||
|
2. `bake_armdown_v5.py` — pose `UpperArm.L/R` −90° local X, read the posed matrices, apply the
|
||||||
|
Armature modifier, **reset the pose to identity**, set the rest matrices, re-add the modifier,
|
||||||
|
export `quaternius_base.glb` and save `assets/models/blender_src/npc_casual.blend` for the AO
|
||||||
|
bake.
|
||||||
|
|
||||||
|
Then `tools/bake_ao.py --only npc_casual` and a copy of the GLB to
|
||||||
|
`assets/models/blender/npc/npc_casual.glb`.
|
||||||
|
|
||||||
|
| Measurement | npc_casual (Quaternius) | procedural library |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Triangles | 6,206 | 4,812–6,046 |
|
||||||
|
| glTF vertices | 12,988 | 4,908–5,785 |
|
||||||
|
| Bones | 62 | 13 |
|
||||||
|
| Head height / body height | 0.134 | 0.16–0.22 (stylized band) |
|
||||||
|
| Shoulder width | 0.201 m head width, `fit` OK | `fit` OK |
|
||||||
|
| Texel density (256² maps) | 73–74 px/m | 75–96 px/m |
|
||||||
|
| Materials | `skin`, `cloth`, `pants`, `hair`, `accessory` + fixed `Eye`, `Eyebrows` | five slots + fixed detail |
|
||||||
|
|
||||||
|
Two source quirks shaped the bake:
|
||||||
|
|
||||||
|
- the upstream UV map is degenerate (every loop at 0,0) and its materials are flat colours, so the
|
||||||
|
AO/texture bake must follow a real unwrap or it silently writes a black map;
|
||||||
|
- the upstream materials are named `Skin`/`Purple`/`LightBlue`/`White`/`Hair`, so the five-slot
|
||||||
|
contract is applied as a rename, not a re-author.
|
||||||
|
|
||||||
|
The face and silhouette gate sheet is in [`screens/npc-face-gate/`](screens/npc-face-gate/README.md).
|
||||||
|
|
||||||
|
## Shipped roster (0.9.9, owner chose the full swap)
|
||||||
|
|
||||||
|
Every public character name is baked from one of the three CC0 sources. The build is
|
||||||
|
`tools/blender_assets.py build` (default `--source quaternius`); the sources live in
|
||||||
|
`assets/models/blender_src/quaternius/` and the geometry pipeline is two passes:
|
||||||
|
|
||||||
|
1. `_quaternius_tpose_glb()` — import, drop the Icosphere helper, join **without welding**,
|
||||||
|
repoint the upstream materials onto the five tint slots, Smart-UV-unwrap + repack, export an
|
||||||
|
intermediate T-pose GLB. The round trip also normalises the mirrored upstream bone rolls,
|
||||||
|
which is what makes the next pass symmetric.
|
||||||
|
2. `_bake_quaternius_rest()` — pose `UpperArm.L/R` -90° local X, read the posed matrices, apply
|
||||||
|
the Armature modifier, reset the pose, write the rest matrices, re-add the modifier, export
|
||||||
|
with `export_apply=False`.
|
||||||
|
|
||||||
|
Then `tools/bake_ao.py --only <name>`.
|
||||||
|
|
||||||
|
| Character | Source | Tris | Material slots |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `npc_casual` | hoodie | 6,206 | skin, cloth, pants, hair, accessory + Eye, Eyebrows |
|
||||||
|
| `npc_jacket` | casual | 6,424 | five slots + eyes/brows |
|
||||||
|
| `npc_coat` | hoodie | 6,206 | five slots + eyes/brows |
|
||||||
|
| `npc_vest` | worker | 5,240 | five slots + hivis vest/hat, eyes, moustache |
|
||||||
|
| `npc_dress` | casual | 6,424 | five slots + eyes/brows |
|
||||||
|
| `npc_hoodie` | hoodie | 6,206 | five slots + eyes/brows |
|
||||||
|
| `npc_firefighter` | worker (vest + hat dropped) | 4,458 | five slots + eyes, moustache |
|
||||||
|
| `npc_guard` | worker (vest + hat dropped) | 4,458 | five slots + eyes, moustache |
|
||||||
|
| `npc_crowd_a` | casual | 6,424 | five slots + eyes/brows |
|
||||||
|
| `npc_crowd_b` | casual | 6,424 | five slots + eyes/brows |
|
||||||
|
| `npc_crowd_c` | hoodie | 6,206 | five slots + eyes/brows |
|
||||||
|
| `npc_crowd_d` | worker (vest + hat dropped) | 4,458 | five slots + eyes, moustache |
|
||||||
|
|
||||||
|
Source slot maps (`QUATERNIUS_SOURCES`): the `casual` tee is `Skin`/`White`/`Orange`/`Grey`/
|
||||||
|
`Hair_Blond`; the hoodie is `Skin`/`Purple`/`LightBlue`/`Hair`/`White`; the worker is `Skin`/
|
||||||
|
`LightBrown`/`Brown`+`Brown2`/`Grey`+`Black`/`Eyebrows`. Everything not in the map keeps its
|
||||||
|
authored colour (eyes, brows, moustache, hivis vest and hard hat), so the contract still holds if
|
||||||
|
upstream adds a material — it shows up as an extra fixed-colour slot instead of vanishing.
|
||||||
|
|
||||||
|
Head gear is authored for this head: `HEAD_GEAR_SCALE = 0.64` and `HEAD_GEAR_LIFT = 0.07` map the
|
||||||
|
0.315 m procedural head onto the 0.201–0.214 m Quaternius head. The procedural fallback therefore
|
||||||
|
wears slightly small hats.
|
||||||
|
|
||||||
|
## Contract mismatch
|
||||||
|
|
||||||
|
The expensive part is the rig and proportions, not the mesh. The source has a
|
||||||
|
T-pose and a realistic skeleton; the runtime expects an arm-down 13-core rig,
|
||||||
|
fixed gear attach points and the existing pose composition. The Stage 1 bake
|
||||||
|
must therefore:
|
||||||
|
|
||||||
|
- rotate the arms down and match the contract wrist positions;
|
||||||
|
- warp the hips, knees, shoulders and head without collapsing the deltoid,
|
||||||
|
shortening the arms or changing the silhouette;
|
||||||
|
- keep one skinned `npc_base` mesh and the five slots;
|
||||||
|
- verify gear against the new shoulder, hip and head positions.
|
||||||
|
|
||||||
|
If those constraints distort the model, Stage 2 is the correct next decision,
|
||||||
|
not a local workaround.
|
||||||
|
|
||||||
|
## Licence and provenance
|
||||||
|
|
||||||
|
- CC0 preferred. CC-BY is allowed only with attribution in
|
||||||
|
[`CREDITS.md`](../CREDITS.md).
|
||||||
|
- No service asset whose terms restrict redistribution may be shipped.
|
||||||
|
- Keep the source URL, author, licence, download date and SHA-256 in this page
|
||||||
|
and in `CREDITS.md`.
|
||||||
|
- Research files and generated previews stay in `tmp/` (gitignored). Only final
|
||||||
|
audited assets and committed review sheets enter the repository.
|
||||||
|
- Quaternius models and animations are CC0; the existing credits entry already
|
||||||
|
names Quaternius. Keep it accurate if the shipped source set changes.
|
||||||
|
|
||||||
|
## Closed tracks
|
||||||
|
|
||||||
|
The MakeHuman/MPFB body pipeline is closed for the NPC base. The accepted
|
||||||
|
MakeHuman head and the procedural `npc_base` remain tested fallbacks until the
|
||||||
|
Quaternius rollout is complete. Their research scripts and renders were removed
|
||||||
|
from `tmp/` after the pivot; the measurements and decisions remain in
|
||||||
|
[`DONE.md`](../DONE.md), [`MEMORY.md`](../MEMORY.md) and the review docs.
|
||||||
|
|
||||||
|
## Related documents
|
||||||
|
|
||||||
|
- [ADR-0017: Quaternius NPC base](adr/0017-quaternius-npc-base.md)
|
||||||
|
- [ADR-0016: 24,000-triangle character budget](adr/0016-npc-character-budget-24k.md)
|
||||||
|
- [Blender asset contract](architecture.md#character-and-gear-asset-contract)
|
||||||
|
- [`3d-model-review.md`](3d-model-review.md), [`npc-visual-review.md`](npc-visual-review.md)
|
||||||
|
- [`asset-tooling.md`](asset-tooling.md)
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
# NPC proportion audit
|
|
||||||
|
|
||||||
Status: audited 2026-11-02. All eight library variants are inside the stylized
|
|
||||||
proportion guide; no geometry correction was required in this pass. The report
|
|
||||||
is repeatable from the exported GLBs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/npc_proportions.py report
|
|
||||||
python3 tools/npc_proportions.py report --json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Method
|
|
||||||
|
|
||||||
`tools/npc_proportions.py` reads the GLB meshes and rest-pose joint hierarchy:
|
|
||||||
|
|
||||||
- height from the full mesh bounds;
|
|
||||||
- head height, width and aspect from `Head`/`skin` vertices (hair excluded so
|
|
||||||
long hairstyles do not change the head measurement);
|
|
||||||
- shoulder silhouette from vertices in the upper-chest band around the
|
|
||||||
shoulder joint;
|
|
||||||
- hip width from `HipL`/`HipR` pants vertices;
|
|
||||||
- torso length from the Spine joint to the shoulder joint;
|
|
||||||
- leg length from the Spine joint to the lowest vertex;
|
|
||||||
- arm length from the shoulder joint to the Hand joint;
|
|
||||||
- foot length from the forward extent of the KneeL/KneeR shoe vertices.
|
|
||||||
|
|
||||||
All ratios are divided by total standing height except `head_aspect`
|
|
||||||
(width / head height).
|
|
||||||
|
|
||||||
## Guide
|
|
||||||
|
|
||||||
These are stylized-but-credible bands for the game's low-poly adults, not
|
|
||||||
photoreal anatomy. The broad build is allowed a larger shoulder/hip silhouette
|
|
||||||
than the slim build.
|
|
||||||
|
|
||||||
| Ratio | Target | Notes |
|
|
||||||
| --- | ---: | --- |
|
|
||||||
| `head_height` | 0.16–0.22 | deliberately larger than a realistic 0.13–0.15 |
|
|
||||||
| `head_aspect` | 0.70–1.00 | width / height; rounded heads are acceptable |
|
|
||||||
| `shoulder_width` | 0.28–0.38 | silhouette includes sleeves and build scaling |
|
|
||||||
| `hip_width` | 0.17–0.23 | pants-only width, not skirt/coat flare |
|
|
||||||
| `torso_length` | 0.28–0.38 | Spine joint to shoulder |
|
|
||||||
| `leg_length` | 0.40–0.48 | Spine joint to ground |
|
|
||||||
| `arm_length` | 0.28–0.38 | shoulder to hand |
|
|
||||||
| `foot_length` | 0.12–0.18 | shoe forward extent |
|
|
||||||
|
|
||||||
## Result
|
|
||||||
|
|
||||||
| Character | Height | Head | Aspect | Shoulder | Hip | Torso | Leg | Arm | Foot | Result |
|
|
||||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
|
|
||||||
| `npc_casual` | 1.824 m | 0.190 | 0.907 | 0.321 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
|
||||||
| `npc_jacket` | 1.824 m | 0.190 | 0.907 | 0.333 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
|
||||||
| `npc_coat` | 1.824 m | 0.190 | 0.907 | 0.328 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
|
||||||
| `npc_vest` | 1.819 m | 0.191 | 0.907 | 0.322 | 0.209 | 0.308 | 0.451 | 0.330 | 0.144 | PASS |
|
|
||||||
| `npc_dress` | 1.825 m | 0.190 | 0.907 | 0.289 | 0.191 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
|
||||||
| `npc_hoodie` | 1.856 m | 0.187 | 0.907 | 0.328 | 0.206 | 0.302 | 0.442 | 0.323 | 0.141 | PASS |
|
|
||||||
| `npc_firefighter` | 1.819 m | 0.191 | 0.907 | 0.358 | 0.223 | 0.308 | 0.451 | 0.330 | 0.148 | PASS |
|
|
||||||
| `npc_guard` | 1.819 m | 0.191 | 0.907 | 0.369 | 0.219 | 0.308 | 0.451 | 0.330 | 0.144 | PASS |
|
|
||||||
|
|
||||||
No variant is outside a band, so the body pass does not need a global skeleton
|
|
||||||
or mesh rescale. The values closest to a boundary are:
|
|
||||||
|
|
||||||
- **head height 0.190** against the 0.22 upper bound, and **head aspect 0.907**
|
|
||||||
against 1.00 — the head is deliberately large. Any head-size change must be
|
|
||||||
coordinated with the face/headgear pass and its gear-fit measurements.
|
|
||||||
- **guard shoulder 0.369** and **firefighter hip 0.223** against the broad-build
|
|
||||||
upper bounds — these are the intended uniform/turnout silhouettes, not
|
|
||||||
outliers.
|
|
||||||
- **leg length 0.449** and **torso 0.307** sit inside the guide for all variants;
|
|
||||||
the earlier short-leg impression came from the hip joint position, not the
|
|
||||||
visible pelvis line.
|
|
||||||
|
|
||||||
## Follow-up
|
|
||||||
|
|
||||||
The proportion audit does not replace the visual criteria in
|
|
||||||
[`npc-visual-review.md`](npc-visual-review.md): neck/shoulder/hip transitions,
|
|
||||||
face depth, hand grips, clothing hems and shoes still need the appearance pass.
|
|
||||||
Re-run the audit after any body mesh or skeleton change and record the new
|
|
||||||
ratios here.
|
|
||||||
@@ -13,6 +13,14 @@ the conversation-distance hand pass
|
|||||||
[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg), and the
|
[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg), and the
|
||||||
forward-grip pass [`sheet_npc_hands_grip_compare.jpg`](blender_previews/sheet_npc_hands_grip_compare.jpg).
|
forward-grip pass [`sheet_npc_hands_grip_compare.jpg`](blender_previews/sheet_npc_hands_grip_compare.jpg).
|
||||||
|
|
||||||
|
The 2026-11-03 skill-guided re-evaluation, its measurements (texel density, budgets, imported
|
||||||
|
bounds) and the open scorecard live in [`3d-model-review.md`](3d-model-review.md). It does not
|
||||||
|
change the accepted limits below.
|
||||||
|
|
||||||
|
A single-variant face prototype (`npc_casual`) landed 2026-11-03/04: `seg=24/rings=16` head,
|
||||||
|
subtle brow ridge, fuller lower lip and both eyelids. It reads cleaner than the 0.9.4 face and is
|
||||||
|
a candidate for the remaining seven once the owner approves (see the review doc).
|
||||||
|
|
||||||
## What was captured
|
## What was captured
|
||||||
|
|
||||||
`src/test/capture_npc_baseline.gd` stages all eight library characters in a
|
`src/test/capture_npc_baseline.gd` stages all eight library characters in a
|
||||||
@@ -63,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 24,000 triangles
|
||||||
and 8,000 glTF vertices per library character. The frozen `npc_base` fallback
|
and 24,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
|
||||||
@@ -74,7 +82,7 @@ pass every applicable item before a library rebuild is accepted.
|
|||||||
the head volume has a readable jaw/cheek/chin transition; no face-plane seams,
|
the head volume has a readable jaw/cheek/chin transition; no face-plane seams,
|
||||||
inverted normals or z-fighting. The face direction must remain unambiguous.
|
inverted normals or z-fighting. The face direction must remain unambiguous.
|
||||||
4. **Hair and headgear:** hair covers the scalp without gaps or scalp-through
|
4. **Hair and headgear:** hair covers the scalp without gaps or scalp-through
|
||||||
seams; fringes, buns and beards keep their silhouette. Caps, beanies, hoods,
|
seams; fringes, buns and beards keep their silhouette. Caps, hoods,
|
||||||
sunglasses and the firefighter helmet clear the brow, eyes and ears. Headgear
|
sunglasses and the firefighter helmet clear the brow, eyes and ears. Headgear
|
||||||
may sit outside the 1.55–1.81 m face band, but its brim must not cover the
|
may sit outside the 1.55–1.81 m face band, but its brim must not cover the
|
||||||
eyes.
|
eyes.
|
||||||
@@ -105,11 +113,83 @@ measurement.
|
|||||||
|
|
||||||
## Proportion audit
|
## Proportion audit
|
||||||
|
|
||||||
The separate [`npc-proportion-audit.md`](npc-proportion-audit.md) measures
|
Status: audited 2026-11-02. All eight library variants are inside the stylized
|
||||||
height, head size, shoulder/hip width, torso, leg, arm and foot ratios for every
|
proportion guide; no geometry correction was required in this pass. The report
|
||||||
variant with `tools/npc_proportions.py`. All eight pass the stylized guide; the
|
is repeatable from the exported GLBs:
|
||||||
head and broad-build shoulders sit nearest their upper bounds. No skeleton or
|
|
||||||
mesh proportion change was needed in this pass.
|
```bash
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
python3 tools/npc_proportions.py report --json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method
|
||||||
|
|
||||||
|
`tools/npc_proportions.py` reads the GLB meshes and rest-pose joint hierarchy:
|
||||||
|
|
||||||
|
- height from the full mesh bounds;
|
||||||
|
- head height, width and aspect from `Head`/`skin` vertices (hair excluded so
|
||||||
|
long hairstyles do not change the head measurement);
|
||||||
|
- shoulder silhouette from vertices in the upper-chest band around the
|
||||||
|
shoulder joint;
|
||||||
|
- hip width from `HipL`/`HipR` pants vertices;
|
||||||
|
- torso length from the Spine joint to the shoulder joint;
|
||||||
|
- leg length from the Spine joint to the lowest vertex;
|
||||||
|
- arm length from the shoulder joint to the Hand joint;
|
||||||
|
- foot length from the forward extent of the KneeL/KneeR shoe vertices.
|
||||||
|
|
||||||
|
All ratios are divided by total standing height except `head_aspect`
|
||||||
|
(width / head height).
|
||||||
|
|
||||||
|
### Guide
|
||||||
|
|
||||||
|
These are stylized-but-credible bands for the game's low-poly adults, not
|
||||||
|
photoreal anatomy. The broad build is allowed a larger shoulder/hip silhouette
|
||||||
|
than the slim build.
|
||||||
|
|
||||||
|
| Ratio | Target | Notes |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| `head_height` | 0.16–0.22 | deliberately larger than a realistic 0.13–0.15 |
|
||||||
|
| `head_aspect` | 0.70–1.00 | width / height; rounded heads are acceptable |
|
||||||
|
| `shoulder_width` | 0.28–0.38 | silhouette includes sleeves and build scaling |
|
||||||
|
| `hip_width` | 0.17–0.23 | pants-only width, not skirt/coat flare |
|
||||||
|
| `torso_length` | 0.28–0.38 | Spine joint to shoulder |
|
||||||
|
| `leg_length` | 0.40–0.48 | Spine joint to ground |
|
||||||
|
| `arm_length` | 0.28–0.38 | shoulder to hand |
|
||||||
|
| `foot_length` | 0.12–0.18 | shoe forward extent |
|
||||||
|
|
||||||
|
### Result
|
||||||
|
|
||||||
|
| Character | Height | Head | Aspect | Shoulder | Hip | Torso | Leg | Arm | Foot | Result |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
|
||||||
|
| `npc_casual` | 1.824 m | 0.190 | 0.907 | 0.321 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
||||||
|
| `npc_jacket` | 1.824 m | 0.190 | 0.907 | 0.333 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
||||||
|
| `npc_coat` | 1.824 m | 0.190 | 0.907 | 0.328 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
||||||
|
| `npc_vest` | 1.819 m | 0.191 | 0.907 | 0.322 | 0.209 | 0.308 | 0.451 | 0.330 | 0.144 | PASS |
|
||||||
|
| `npc_dress` | 1.825 m | 0.190 | 0.907 | 0.289 | 0.191 | 0.307 | 0.449 | 0.329 | 0.144 | PASS |
|
||||||
|
| `npc_hoodie` | 1.856 m | 0.187 | 0.907 | 0.328 | 0.206 | 0.302 | 0.442 | 0.323 | 0.141 | PASS |
|
||||||
|
| `npc_firefighter` | 1.819 m | 0.191 | 0.907 | 0.358 | 0.223 | 0.308 | 0.451 | 0.330 | 0.148 | PASS |
|
||||||
|
| `npc_guard` | 1.819 m | 0.191 | 0.907 | 0.369 | 0.219 | 0.308 | 0.451 | 0.330 | 0.144 | PASS |
|
||||||
|
|
||||||
|
No variant is outside a band, so the body pass does not need a global skeleton
|
||||||
|
or mesh rescale. The values closest to a boundary are:
|
||||||
|
|
||||||
|
- **head height 0.190** against the 0.22 upper bound, and **head aspect 0.907**
|
||||||
|
against 1.00 — the head is deliberately large. Any head-size change must be
|
||||||
|
coordinated with the face/headgear pass and its gear-fit measurements.
|
||||||
|
- **guard shoulder 0.369** and **firefighter hip 0.223** against the broad-build
|
||||||
|
upper bounds — these are the intended uniform/turnout silhouettes, not
|
||||||
|
outliers.
|
||||||
|
- **leg length 0.449** and **torso 0.307** sit inside the guide for all variants;
|
||||||
|
the earlier short-leg impression came from the hip joint position, not the
|
||||||
|
visible pelvis line.
|
||||||
|
|
||||||
|
### Follow-up
|
||||||
|
|
||||||
|
The proportion audit does not replace the visual criteria in
|
||||||
|
[`npc-visual-review.md`](npc-visual-review.md): neck/shoulder/hip transitions,
|
||||||
|
face depth, hand grips, clothing hems and shoes still need the appearance pass.
|
||||||
|
Re-run the audit after any body mesh or skeleton change and record the new
|
||||||
|
ratios here.
|
||||||
|
|
||||||
## Baseline measurements
|
## Baseline measurements
|
||||||
|
|
||||||
@@ -126,8 +206,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
|
||||||
|
|||||||
@@ -1,11 +1,69 @@
|
|||||||
# Release-candidate balance acceptance
|
# Release QA and Balance
|
||||||
|
|
||||||
|
Manual verification and the 0.10.0 balance protocol. The executable command
|
||||||
|
matrix is in the release runbook/skill; this page owns the human smoke checks and
|
||||||
|
the measurable balance targets. Architecture owns the gates themselves; gameplay
|
||||||
|
owns the mechanics.
|
||||||
|
|
||||||
|
## Manual smoke checklist
|
||||||
|
|
||||||
|
1. `godot --path .` starts the game on the venue menu without script errors.
|
||||||
|
2. Choosing a venue (click or ↑/↓ + Enter) starts that venue's shift and hides the menu.
|
||||||
|
3. The guard moves and looks around; the building, steps, and sidewalk are visible.
|
||||||
|
4. The guard's camera sits at eye height and the guard stands on the landing, not inside the building.
|
||||||
|
5. Walking into the steps carries the guard up onto the landing (steps have colliders).
|
||||||
|
6. The skyline backdrop shows silhouettes against the sky, not an opaque black slab.
|
||||||
|
7. The `GIG LINE` marquee is readable from the sidewalk.
|
||||||
|
8. NPCs spawn and approach the entrance during the first minutes.
|
||||||
|
9. Looking at an NPC inside the focus cone shows a blue focus circle in front of the face, the person's name, and the HUD target line.
|
||||||
|
10. `Space` or left click opens the action wheel; `A`/`D` highlight a slice, `Space` confirms it, `Esc`/right click cancels without a log entry.
|
||||||
|
11. `E`/`C` open the inspection/wand panels from the wheel and freeze movement until they close; walking never fires the wand.
|
||||||
|
12. Pressing `Q` produces a reaction log entry and reputation feedback.
|
||||||
|
13. `E` opens access inspection showing venue-correct artifacts and policy result; `A`/`D` record a decision.
|
||||||
|
14. `6` opens the assistance form; submitting shows dispatch feedback.
|
||||||
|
15. `R` opens the emergency form; the button must be held ~1.6s or the call cancels.
|
||||||
|
16. `G` toggles the flashlight (indicator appears in the HUD); `L` toggles the door lock (indicator turns red when locked).
|
||||||
|
17. `F` toggles the debug overlay with NPC states and shift stats.
|
||||||
|
18. The wheel's `Look` slice is eye contact (Level 1) and logs as its own action.
|
||||||
|
19. `Reason` asks why the person is there and logs their `dialogue_lines` reply.
|
||||||
|
20. `Recognize` records the guard's recognition as access evidence; pressing it twice says so.
|
||||||
|
21. `Warn` turns the body camera on (REC overlay appears); `B` toggles it manually.
|
||||||
|
22. `Block` moves the guard between the person and the door.
|
||||||
|
23. The shift review reports the incident count, including interactions left unresolved.
|
||||||
|
24. Escalating on someone (or a responder arriving) makes idle bystanders stop and face the incident.
|
||||||
|
25. Repeated alarms panic the crowd and nearby people clear away from the door instead of standing around.
|
||||||
|
26. `Esc` with nothing open pauses the shift; resume / restart / settings / quit-to-menu all work, and settings changes persist across a restart.
|
||||||
|
27. NPCs route around the building using the navigation mesh (no stuck-at-wall behavior during a smoke run).
|
||||||
|
28. An unjustified emergency call (calm person, no weapon) logs an investigation and costs reputation.
|
||||||
|
29. After an evacuation outcome the door is locked and NPCs are leaving the entrance.
|
||||||
|
30. The shift review's "Body camera timeline" button steps through the recorded events (Left/Right or the buttons) and Esc returns to the review.
|
||||||
|
31. The escalation label shows a `[###--] L3/5` shape meter as well as the level name, and stays readable in grayscale.
|
||||||
|
32. Changing "HUD font scale" in Settings resizes the HUD text and persists across a restart.
|
||||||
|
33. When the shift timer reaches 0, the shift review screen shows grade, policy, incident summary, responder outcomes, investigations, and persisted shift history; campaign Continue starts the next venue.
|
||||||
|
34. On a fresh profile the tutorial starts on Hollywood, advances through its eight beats, and F1 skips it; Settings Replay tutorial restarts it.
|
||||||
|
35. Pausing saves a checkpoint: returning to the menu and choosing Continue resumes elapsed time, NPCs, incidents, door/flashlight state and campaign position.
|
||||||
|
36. Settings difficulty changes spawn pacing/compliance/report strictness while escalation rules stay the same; Help filters controls and shows the venue policy.
|
||||||
|
37. The F debug overlay lists the next eligible events; recovery events and recently played scripted events behave as authored.
|
||||||
|
38. Master/SFX/ambience volume settings clamp at 0/1 and persist; Reduce motion disables head bob; the captions toggle persists.
|
||||||
|
39. The 24-NPC budget smoke stays inside its frame budget; coats/dresses do not show obvious leg clipping in a normal walk.
|
||||||
|
40. Help remains readable with the largest HUD font scale, and CI/local verification use the same Godot 4.7.2 baseline.
|
||||||
|
41. UI copy: inspection/wand use "A = admit · D = refuse entry", review returns with "Return to menu", and application exit reads "Quit game".
|
||||||
|
42. Button states remain readable on hover/focus/pressed; settings Apply/Cancel and radio normal-send/emergency-hold behave as labelled.
|
||||||
|
43. The venue menu offers Hollywood Boulevard — Day and — Night; either starts the same map, and Night starts in the late phase with a full shift clock.
|
||||||
|
44. The Joke Shop opens on the Sunset Strip club: the black hall, signature canopy, circular **THE JOKE SHOP** sign, red curtains, west entrance, palms, dry hillside with houses, neighbouring frontage and the opposite side of Sunset all read from the door camera. Day lighting and late lighting both look correct; the guard can walk the sidewalk without leaving the nav bounds.
|
||||||
|
45. Esc and right-click close only the top panel (Help, Settings, Pause, Review, Radio, timeline, inspection/wand, action wheel); Settings opened from the venue menu returns to the menu, Help no longer opens Pause behind it, and closing a modal recaptures the mouse without leaving the guard frozen.
|
||||||
|
46. At 0.75× and 1.75× HUD font scale, long target names stay inside the target chip, the chip grows/caps correctly, and long button labels ellipsize instead of pushing panels off-screen.
|
||||||
|
47. Reset campaign and Replay tutorial both ask for confirmation; a second click reuses the open dialog, Cancel does nothing, and OK fires once. Emergency send requires the full hold, sends exactly once, and does not send again on release.
|
||||||
|
48. UI copy/button sweep: every panel uses the canonical admit/refuse, warning/blocking, assistance/emergency and body-camera terms; the start menu, pause, settings, Help, inspection, wand, radio, wheel, timeline and shift review all show readable hover/focus/pressed/disabled states; Tab/arrow focus wraps without landing on hidden or disabled actions; Esc/right-click closes only the top panel; and destructive reset/replay plus emergency send still require their confirmation or hold.
|
||||||
|
|
||||||
|
## Balance acceptance
|
||||||
|
|
||||||
These are the acceptance targets for the 0.10.0 balance pass, not measured results.
|
These are the acceptance targets for the 0.10.0 balance pass, not measured results.
|
||||||
No reaction tables, event weights or scoring rules were changed to set them. The
|
No reaction tables, event weights or scoring rules were changed to set them. The
|
||||||
[roadmap](../TODO.md) tracks the sweeps and external playtests still needed.
|
[roadmap](../TODO.md) tracks the sweeps and external playtests still needed.
|
||||||
[Gameplay](gameplay.md) owns the mechanics; this page owns their balance-test protocol.
|
[Gameplay](gameplay.md) owns the mechanics; this page owns their balance-test protocol.
|
||||||
|
|
||||||
## Test matrix and evidence
|
### Test matrix and evidence
|
||||||
|
|
||||||
Run Hollywood, Joke Shop and TV Studio on easy, normal and hard, in Early,
|
Run Hollywood, Joke Shop and TV Studio on easy, normal and hard, in Early,
|
||||||
Midday, Evening and Late: **36 cells**. Use seeds 1–100 for each cell and retain
|
Midday, Evening and Late: **36 cells**. Use seeds 1–100 for each cell and retain
|
||||||
@@ -37,7 +95,7 @@ cell. Use at least **100 eligible cases per metric per cell**; if the schedule
|
|||||||
provides fewer, add a separately labelled forced-case sample. Report zero eligible
|
provides fewer, add a separately labelled forced-case sample. Report zero eligible
|
||||||
cases as **N/A**, never as 100% success. Keep scheduled and forced results separate.
|
cases as **N/A**, never as 100% success. Keep scheduled and forced results separate.
|
||||||
|
|
||||||
## Targets
|
### Targets
|
||||||
|
|
||||||
Percentages below apply to each matrix cell, not only a pooled average. Scripted
|
Percentages below apply to each matrix cell, not only a pooled average. Scripted
|
||||||
access/report cases use complete evidence and are held to exact correctness;
|
access/report cases use complete evidence and are held to exact correctness;
|
||||||
@@ -71,7 +129,7 @@ after an accepted emergency call). Separately test depleted battery and active
|
|||||||
cooldown cases for safe containment/assistance; do not silently drop them from the
|
cooldown cases for safe containment/assistance; do not silently drop them from the
|
||||||
evidence or claim the initially-available-radio target covers them.
|
evidence or claim the initially-available-radio target covers them.
|
||||||
|
|
||||||
## Scoring and emergency dominance
|
### Scoring and emergency dominance
|
||||||
|
|
||||||
Compare the calm policy with an emergency-first policy on the same routine events
|
Compare the calm policy with an emergency-first policy on the same routine events
|
||||||
and seeds. Each comparison starts with fresh radio, crowd and reputation state.
|
and seeds. Each comparison starts with fresh radio, crowd and reputation state.
|
||||||
@@ -87,7 +145,7 @@ the incident remains answerable and the debrief explains the outcome without an
|
|||||||
unjustified-call investigation. Also compare assistance/containment routes where
|
unjustified-call investigation. Also compare assistance/containment routes where
|
||||||
appropriate; emergency need not be mandatory for every difficult interaction.
|
appropriate; emergency need not be mandatory for every difficult interaction.
|
||||||
|
|
||||||
## Implementation cautions and sign-off
|
### Implementation cautions and sign-off
|
||||||
|
|
||||||
- `NPCController.roll_reaction()` samples per-level weights. Difficulty adds
|
- `NPCController.roll_reaction()` samples per-level weights. Difficulty adds
|
||||||
+12/0/−8 to the `COMPLY` weight before normalization; `base_compliance` alone
|
+12/0/−8 to the `COMPLY` weight before normalization; `base_compliance` alone
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Screen-design judge report
|
|
||||||
|
|
||||||
Date: 2026-11-02
|
|
||||||
Model: automatic cheapest image+JSON selection (`z-ai/glm-5.3-flash` at run time)
|
|
||||||
Command: `python3 tools/screen_designs.py judge`
|
|
||||||
Result: **21/21 match, 0 review, 0 diff**
|
|
||||||
|
|
||||||
The screenshot gallery was regenerated on the current build before judging, including the eight UI-state captures 14–21 added in `src/test/capture_ui_states.gd`.
|
|
||||||
Accepted captures are in [`docs/screens/`](screens/README.md); the committed
|
|
||||||
gallery is in [`docs/screenshots/`](screenshots/README.md).
|
|
||||||
|
|
||||||
The automatic model selection and retry/spend behavior is documented in
|
|
||||||
[`docs/asset-tooling.md`](asset-tooling.md). This report intentionally records
|
|
||||||
the outcome rather than generated captures, which remain local/gitignored.
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Visual references and generated QA evidence for Gig Line.
|
Visual references and generated QA evidence for Gig Line.
|
||||||
|
|
||||||
|
- [`refs/README.md`](refs/README.md) — licensed real-location photos, provenance sidecars and the licence/privacy rules
|
||||||
- [`concepts/README.md`](concepts/README.md) — AI-assisted realism concept frames
|
- [`concepts/README.md`](concepts/README.md) — AI-assisted realism concept frames
|
||||||
- [`designs/README.md`](designs/README.md) — screen-design mockups
|
- [`designs/README.md`](designs/README.md) — screen-design mockups
|
||||||
- [`../screenshots/README.md`](../screenshots/README.md) — committed game gallery
|
- [`../screenshots/README.md`](../screenshots/README.md) — committed game gallery
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Three-up boards: photo | concept | capture
|
||||||
|
|
||||||
|
Each strip lines up the three image sources for one venue state so the evidence
|
||||||
|
chain stays visible: the licensed real photo is the **evidence**, the generated
|
||||||
|
frame is the **proposal** (geometry anchored on the photo; see the
|
||||||
|
[concepts board](../concepts/README.md) and each frame's `.refgen.json`), and
|
||||||
|
the game capture is the **proof** of the current build. Where the concept and
|
||||||
|
the photo disagree, the photo wins for shape; the capture shows the gap the
|
||||||
|
facade-realism work has to close.
|
||||||
|
|
||||||
|
Composed with `tools/three_up.py compose` (labels baked in; the photo panel's
|
||||||
|
attribution lives in the caption and the [refs sidecars](../refs/README.md)).
|
||||||
|
|
||||||
|
| Board | Caption |
|
||||||
|
| --- | --- |
|
||||||
|
|  | El Capitan facade, day. Photo: [CC BY 2.0, woo, 2009](https://commons.wikimedia.org/wiki/File:El_Capitan_Theater_frontal_fachade-The_Masonic_Temple.jpg) via `refs/hollywood`. Concept: photo-anchored generation. Capture: current build with the ashlar limestone field (`limestone_ashlar`, anchored on the same photo). |
|
||||||
|
|  | El Capitan block, night. Photo: CC BY-SA 3.0 (reviewed; redistribution only, no derived assets) via `refs/hollywood`. Concept: photo-anchored night restage. Capture: current build. |
|
||||||
|
|  | Joke Shop frontage, day. Photo: local-only research frame (`tmp/research/comedy-store`, gitignored). Concept: photo-anchored (gemini + gpt). Capture: current build with the `dark_stucco` wall tile. |
|
||||||