Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a196a35342 | ||
|
|
efa062833f | ||
|
|
6381dbc5cb | ||
|
|
7546092c01 | ||
|
|
16dc1dfea4 | ||
|
|
2ba40f3578 | ||
|
|
045ed2a58f | ||
|
|
d3a87acf2d | ||
|
|
b18236f9b4 | ||
|
|
3b9e3b61d8 | ||
|
|
f6bb2b1de3 | ||
|
|
7a46963e3f | ||
|
|
a0dbf75806 | ||
|
|
c70a523f0f | ||
|
|
fcbeee0db2 | ||
|
|
d5e180e50c | ||
|
|
a36068aae7 | ||
|
|
6b58350464 | ||
|
|
219588bf8a | ||
|
|
cba00facf5 | ||
|
|
9fd48243ad | ||
|
|
30d145079a | ||
|
|
98f618c501 | ||
|
|
a290fe83c8 | ||
|
|
529d15ee7a | ||
|
|
3ab7577634 | ||
|
|
c8875a3dfa | ||
|
|
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 | ||
|
|
51f537720f | ||
|
|
08e9044593 | ||
|
|
ad14410337 | ||
|
|
876fb59882 | ||
|
|
979c6ad789 | ||
|
|
1921bfae91 | ||
|
|
78485259b7 | ||
|
|
fef3af44c0 | ||
|
|
bfe3c5a701 | ||
|
|
46f1e41874 | ||
|
|
f48d87c84f | ||
|
|
a9658ecd81 | ||
|
|
b8507df346 | ||
|
|
60bf9a8ef6 | ||
|
|
b51bdc8efe | ||
|
|
a7b2852253 | ||
|
|
4bb2896819 | ||
|
|
f5b8ef2e70 |
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/assets/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/assets/blender-assets.md` — library contract and command reference.
|
||||||
|
4. `docs/assets/asset-tooling.md` — exact rebuild/bake/capture commands.
|
||||||
|
5. `docs/assets/npc-model-sourcing.md` and `docs/assets/3d-model-review.md` — accepted limits,
|
||||||
|
scorecards and known blockers.
|
||||||
|
6. `docs/design/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/assets/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/assets/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/assets/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/assets/blender-assets.md`, `docs/assets/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/assets/3d-model-review.md` and
|
||||||
|
`docs/design/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/process/RELEASING.md` — build/publish commands, artifact list, macOS policy.
|
||||||
|
4. `docs/process/release-qa.md` — manual QA matrix and balance protocol.
|
||||||
|
5. `docs/project/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/project/architecture.md`,
|
||||||
|
`docs/project/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/project/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/design/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/process/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/project/architecture.md` | current version reference |
|
||||||
|
| `docs/project/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
|
||||||
@@ -15,14 +15,106 @@ The project should be developed in small, reviewable steps. Agents must keep pla
|
|||||||
- `README.md` — Root-level documentation for the project. It should explain what the game is, how to work on it, how to run it, and where to find project information.
|
- `README.md` — Root-level documentation for the project. It should explain what the game is, how to work on it, how to run it, and where to find project information.
|
||||||
- `AGENTS.md` — This collaboration and agent workflow guide.
|
- `AGENTS.md` — This collaboration and agent workflow guide.
|
||||||
- `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/project/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/project/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/design/gameplay.md` — Gameplay systems, tutorial, venues and the escalation model.
|
||||||
- `docs/asset-tooling.md` — Asset pipelines, model tools, and screenshot gates.
|
- `docs/design/venues.md` — Real-site evidence, game mapping and venue simplifications.
|
||||||
- `docs/getting-started.md` — Setup, run/test commands, and the controls table.
|
- `docs/assets/asset-tooling.md` — Asset pipelines, model tools, optional Blender MCP and screenshot gates.
|
||||||
- `docs/smoke-checklist.md` — The manual smoke-test checklist.
|
- `docs/assets/blender-assets.md` — Blender library operation and asset roles.
|
||||||
|
- `docs/assets/npc-model-sourcing.md` — Character-source evaluation, licence rules and the character-development plan.
|
||||||
|
- `docs/process/getting-started.md` — Setup, run/test commands, and the controls table.
|
||||||
|
- `docs/process/release-qa.md` — The manual smoke checklist and balance-acceptance protocol.
|
||||||
- `docs/adr/` — Architecture Decision Records.
|
- `docs/adr/` — Architecture Decision Records.
|
||||||
|
|
||||||
|
## Priority and Versioning
|
||||||
|
|
||||||
|
`TODO.md` is the priority source. The active order is:
|
||||||
|
|
||||||
|
1. unversioned development tracks;
|
||||||
|
2. versioned release milestones, in version order;
|
||||||
|
3. future/unversioned candidates and deferred work.
|
||||||
|
|
||||||
|
Do not assign a version number to an unversioned track until its scope is accepted into a
|
||||||
|
release. When a version is assigned, it must be the next semantic version that matches the
|
||||||
|
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/assets/asset-tooling.md#optional-interactive-blender-mcp`](docs/assets/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/assets/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
|
||||||
@@ -47,8 +139,11 @@ All planned implementation work must be represented in `TODO.md`.
|
|||||||
- Use level-2 headings (`##`) for milestones or stable themes. A milestone should state its goal
|
- Use level-2 headings (`##`) for milestones or stable themes. A milestone should state its goal
|
||||||
and an explicit exit criterion; a theme groups small related work that does not need a
|
and an explicit exit criterion; a theme groups small related work that does not need a
|
||||||
milestone.
|
milestone.
|
||||||
- Use level-3 headings (`###`) for workstreams inside a milestone (for example, `0.8.1
|
- Order level-2 sections by current priority: unversioned development tracks first, then
|
||||||
First-shift tutorial`). Keep the hierarchy to three levels: milestone → workstream → checkbox.
|
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.
|
||||||
|
- Use level-3 headings (`###`) for workstreams inside a milestone. Keep the hierarchy to three
|
||||||
|
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.
|
||||||
@@ -67,26 +162,31 @@ 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
|
||||||
|
first, then versioned milestones in version order, then future/deferred work.
|
||||||
|
|
||||||
### 3. Move Completed Sections to `DONE.md`
|
### 3. Move Completed Sections to `DONE.md`
|
||||||
|
|
||||||
`TODO.md` uses nested sections. Apply these completion rules:
|
`TODO.md` uses nested sections. Apply these completion rules:
|
||||||
@@ -96,7 +196,7 @@ Recommended format:
|
|||||||
level-3 section to `DONE.md`.
|
level-3 section to `DONE.md`.
|
||||||
2. **Level-2 milestone/theme:** it is complete only when its direct checkboxes and **all** of its
|
2. **Level-2 milestone/theme:** it is complete only when its direct checkboxes and **all** of its
|
||||||
level-3 workstreams are complete. Verify the whole milestone, record its capabilities in
|
level-3 workstreams are complete. Verify the whole milestone, record its capabilities in
|
||||||
`docs/development-status.md` when it represents shipped functionality, and write the matching
|
`docs/project/development-status.md` when it represents shipped functionality, and write the matching
|
||||||
`CHANGELOG.md` entry.
|
`CHANGELOG.md` entry.
|
||||||
3. Move the **entire level-2 section**, including all level-3 headings and checklists, to
|
3. Move the **entire level-2 section**, including all level-3 headings and checklists, to
|
||||||
`DONE.md`. Remove it from `TODO.md` and add a completion date/note.
|
`DONE.md`. Remove it from `TODO.md` and add a completion date/note.
|
||||||
@@ -107,7 +207,7 @@ Recommended format:
|
|||||||
complete, or split the unfinished work into a new smaller level-2 milestone first.
|
complete, or split the unfinished work into a new smaller level-2 milestone first.
|
||||||
|
|
||||||
When a completed milestone represents shipped functionality, also record the new capability in
|
When a completed milestone represents shipped functionality, also record the new capability in
|
||||||
`docs/development-status.md` so the implemented-features list stays accurate, and write a matching
|
`docs/project/development-status.md` so the implemented-features list stays accurate, and write a matching
|
||||||
entry in `CHANGELOG.md` (see "Maintain `CHANGELOG.md`" below).
|
entry in `CHANGELOG.md` (see "Maintain `CHANGELOG.md`" below).
|
||||||
|
|
||||||
### 4. Maintain `MEMORY.md`
|
### 4. Maintain `MEMORY.md`
|
||||||
@@ -117,7 +217,7 @@ Use `MEMORY.md` for information that should persist between sessions but does no
|
|||||||
Record items such as:
|
Record items such as:
|
||||||
|
|
||||||
- Important design and architecture decisions
|
- Important design and architecture decisions
|
||||||
- Notable implementation state and gotchas (the canonical implemented-features list lives in `docs/development-status.md`, not here)
|
- Notable implementation state and gotchas (the canonical implemented-features list lives in `docs/project/development-status.md`, not here)
|
||||||
- Known bugs and workarounds
|
- Known bugs and workarounds
|
||||||
- Commands needed to run, test, or build the project
|
- Commands needed to run, test, or build the project
|
||||||
- Decisions that future agents should not revisit unnecessarily
|
- Decisions that future agents should not revisit unnecessarily
|
||||||
@@ -167,17 +267,19 @@ The root-level `README.md` is the project’s public-facing technical and projec
|
|||||||
- Links to `PLAN.md`, `TODO.md`, `DONE.md`, `MEMORY.md`, and this file where appropriate
|
- Links to `PLAN.md`, `TODO.md`, `DONE.md`, `MEMORY.md`, and this file where appropriate
|
||||||
- Known limitations and contribution guidance
|
- Known limitations and contribution guidance
|
||||||
|
|
||||||
Update `README.md` whenever setup instructions, commands, project structure, or user-facing behavior changes. The README is intentionally concise and links to the `docs/` files; keep `docs/development-status.md` (and the other docs it links to) synchronized with the implementation rather than pasting detail back into the README.
|
Update `README.md` whenever setup instructions, commands, project structure, or user-facing behavior changes. The README is intentionally concise and links to the `docs/` files; keep `docs/project/development-status.md` (and the other docs it links to) synchronized with the implementation rather than pasting detail back into the README.
|
||||||
|
|
||||||
### 7. Maintain `CHANGELOG.md`
|
### 7. Maintain `CHANGELOG.md`
|
||||||
|
|
||||||
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
|
||||||
|
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`:
|
||||||
|
|
||||||
@@ -195,16 +297,44 @@ For each task:
|
|||||||
2. Inspect the relevant files and dependencies.
|
2. Inspect the relevant files and dependencies.
|
||||||
3. Make the smallest coherent change that completes the task.
|
3. Make the smallest coherent change that completes the task.
|
||||||
4. Run the relevant tests, checks, or manual verification.
|
4. Run the relevant tests, checks, or manual verification.
|
||||||
5. Update documentation if the change creates lasting context: add features to `docs/development-status.md`, adjust the README links or other `docs/` files as needed, and record decisions/gotchas in `MEMORY.md`.
|
5. Update documentation if the change creates lasting context: add features to `docs/project/development-status.md`, adjust the README links or other `docs/` files as needed, and record decisions/gotchas in `MEMORY.md`.
|
||||||
6. Mark the task complete.
|
6. Mark the task complete.
|
||||||
7. Keep completed level-3 workstreams in place until the parent milestone is complete; then move the entire level-2 milestone to `DONE.md` after recording its `CHANGELOG.md` entry.
|
7. Keep completed level-3 workstreams in place until the parent milestone is complete; then move the entire level-2 milestone to `DONE.md` after recording its `CHANGELOG.md` entry.
|
||||||
8. Commit the changes to the version control system with a descriptive message.
|
8. Commit each coherent, verified change as it is completed. Do not wait until a whole
|
||||||
9. If a level-2 milestone from `TODO.md` has been fully completed, ensure its `CHANGELOG.md` entry is in place, then create a new version or tag in the version control system to mark the milestone.
|
milestone is finished; keep commits small and descriptive and push them when the branch is
|
||||||
10. Push the committed changes to the shared repository if applicable.
|
shareable.
|
||||||
11. Note the new version or tag created in the version control system in any relevant documentation or handoff summary.
|
9. If the change is a release (a major, minor, or patch version increment), follow the Release
|
||||||
|
Procedure in "Commit, Build and Version Discipline".
|
||||||
|
10. Push committed changes to the shared repository if applicable.
|
||||||
|
11. Note the new version, build artifacts or tag in the relevant documentation and handoff
|
||||||
|
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.
|
||||||
|
|
||||||
|
### 9. Commit, Build and Version Discipline
|
||||||
|
|
||||||
|
- **Commit often.** Make a commit after each coherent change or verified task, not only when a
|
||||||
|
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
|
||||||
|
local-only history.
|
||||||
|
- **Build every version.** Every major, minor and patch increment follows the Release Procedure
|
||||||
|
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
|
||||||
|
the required pre-tag check for every version.
|
||||||
|
- **Unversioned tracks stay unversioned.** A development track does not get a placeholder
|
||||||
|
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
|
||||||
|
|
||||||
- Prefer clear, reversible changes over broad rewrites.
|
- Prefer clear, reversible changes over broad rewrites.
|
||||||
@@ -213,6 +343,8 @@ Avoid unrelated refactors unless they are required for the task or explicitly ap
|
|||||||
- Do not claim a task is complete without verification.
|
- Do not claim a task is complete without verification.
|
||||||
- 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.
|
||||||
|
- 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.
|
||||||
@@ -224,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,123 @@ 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]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The red event banners are lit from the top down at night instead of reading as flat red cards.
|
||||||
|
- Hollywood and the TV Studio got the same queue treatment, and the TV Studio's props no longer sit
|
||||||
|
in the entrance steps.
|
||||||
|
- The Joke Shop has a readable queue: a velvet rope line leading to the check-in podium, a thicker
|
||||||
|
bike-rack barricade on the curb side, and a planter no longer clipping the palm foliage.
|
||||||
|
- Street lamps now throw a warm pool on the pavement instead of dimly washing the whole walkway.
|
||||||
|
- The flashlight actually lights what it points at. It was tuned to a beam so weak it changed the
|
||||||
|
frame by less than one 255th of a level; it now throws a visible pool on the door and the pavement
|
||||||
|
ahead, in both venues.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Street greenery now reads as leaves instead of green rocks: the planter shrubs and tree crowns
|
||||||
|
are built from leaf cards, which also cut their triangle count by about a third.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Removed every hat and hood from the NPC roster: the firefighter helmet is the only head gear
|
||||||
|
left, so no character hides its face behind a cap. The street performer no longer wears the
|
||||||
|
cone hat and carries the baton and board in the hands instead.
|
||||||
|
- Hands, head gear and carried items were re-fitted to the new characters: hand items sit in the
|
||||||
|
palm instead of floating at the wrist, the lanyard moved up to the sternum, back gear moved up to
|
||||||
|
shoulder-blade height, and the clipboard is carried by its top edge.
|
||||||
|
- NPCs now wear eleven different outfits instead of three: the modular outfit pack brought a formal
|
||||||
|
suit for the influencer, a punk jacket and mohawk, beach wear, farmer overalls and straw hat, an
|
||||||
|
adventurer jacket, a second casual jersey, and royal armour with a gold crown for the street
|
||||||
|
performer.
|
||||||
|
- NPCs no longer all share one hairstyle: the roster now uses short, buzz and long hair silhouettes
|
||||||
|
instead of a single shell in twelve colours.
|
||||||
|
- Eyes now have whites and pupils instead of flat dark rectangles.
|
||||||
|
- Every NPC now has a visible mouth; the Quaternius heads shipped without one, so the faces read as
|
||||||
|
brow-and-eye masks.
|
||||||
|
- Fixed the modular Worker characters: the guard, delivery, firefighter and crowd roles had holes
|
||||||
|
in the torso and an open crown, a continuous unibrow and no visible eyes. They now have a closed
|
||||||
|
torso, hair, two eyebrows and two eyes.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Prepared the release-candidate packages for Linux, Windows, macOS, Web and the
|
||||||
|
optional Desktop HQ zip. The Web/HQ archives now include the credits, privacy,
|
||||||
|
accessibility and known-limitations pages, and release publishing writes
|
||||||
|
SHA-256 checksums and rolls back partial uploads.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Corrupt or partial save files now fall back to safe defaults without logging a
|
||||||
|
parse error, and a failed atomic replace leaves the previous valid settings
|
||||||
|
intact. Added migration coverage for 0.8-era profiles and 0.9.0 checkpoints
|
||||||
|
across all three venues.
|
||||||
|
|
||||||
|
## [0.9.6] — 2026-11-02
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The UI, model and texture passes are now integrated into a verified checkpoint
|
||||||
|
build. The gallery, NPC portraits, realism captures, asset audits, smoke
|
||||||
|
matrix, 24-NPC budget and Web/Desktop HQ packages were regenerated and
|
||||||
|
re-checked; no gameplay or content changes were introduced.
|
||||||
|
|
||||||
## [0.9.5] — 2026-11-02
|
## [0.9.5] — 2026-11-02
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@@ -253,12 +370,6 @@ Semantic Versioning.
|
|||||||
|
|
||||||
## [0.7.0] — 2026-11-01
|
## [0.7.0] — 2026-11-01
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Agent workflow now requires maintaining this changelog for every patch, minor, and major version.
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Distinct venue geometry: the Joke Shop is now a narrow brick alley with a
|
- Distinct venue geometry: the Joke Shop is now a narrow brick alley with a
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Credits and Provenance
|
||||||
|
|
||||||
|
Gig Line is a local, single-player security-shift game built with Godot 4 and
|
||||||
|
GDScript. This file records the asset provenance needed for the release review;
|
||||||
|
it is not a legal opinion or a claim that every provider licence has already
|
||||||
|
been cleared for public distribution.
|
||||||
|
|
||||||
|
## Game, code and design
|
||||||
|
|
||||||
|
- Game design, systems, scenarios, dialogue and UI code: the Gig Line project.
|
||||||
|
- Engine: [Godot Engine 4.7.2](https://godotengine.org/), MIT licence.
|
||||||
|
- The game bundles no third-party fonts; it uses Godot's default theme font.
|
||||||
|
- No third-party music, sound effects or voice recordings are bundled. Crowd
|
||||||
|
ambience and radio cues are generated procedurally at runtime.
|
||||||
|
|
||||||
|
## 3D characters, gear and props
|
||||||
|
|
||||||
|
The local MakeHuman head prototype uses the CC0 MakeHuman/MPFB base mesh
|
||||||
|
(see [`docs/assets/makehuman-head-review.md`](docs/assets/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
|
||||||
|
(`tools/blender_assets.py`, `tools/npc_pipeline.py`, `tools/model_pipeline.py`)
|
||||||
|
using Blender. The `.blend` working sources are developer artifacts and are not
|
||||||
|
shipped. The exported `.glb` files and baked AO maps are project assets; they do
|
||||||
|
not embed third-party models or purchased asset packs.
|
||||||
|
|
||||||
|
### Development-only research previews
|
||||||
|
|
||||||
|
Two review sheets under `docs/blender_previews/` compare source candidates for
|
||||||
|
the future NPC variety work. They are development documentation and are not
|
||||||
|
bundled with the game or its release packages. The underlying source models are
|
||||||
|
kept outside version control.
|
||||||
|
|
||||||
|
- [`sheet_npc_sources_research.jpg`](docs/blender_previews/sheet_npc_sources_research.jpg)
|
||||||
|
includes renders of **Quaternius Ultimate Modular Characters** (CC0 1.0,
|
||||||
|
<https://quaternius.com/>), **Kenney Blocky Characters 2.0** (CC0 1.0,
|
||||||
|
<https://kenney.nl/assets/blocky-characters>), and **CesiumMan** and
|
||||||
|
**RiggedFigure** (CC BY 4.0, © 2017 Cesium,
|
||||||
|
<https://github.com/KhronosGroup/glTF-Sample-Assets>).
|
||||||
|
- [`sheet_npc_crowd_prototypes.jpg`](docs/blender_previews/sheet_npc_crowd_prototypes.jpg)
|
||||||
|
contains renders of project-owned generator prototypes.
|
||||||
|
|
||||||
|
The CC-BY preview images are included only as development evidence. Any future
|
||||||
|
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/assets/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/assets/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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
Surface textures, skyline panoramas, UI art and concept references were produced
|
||||||
|
with image-generation calls from the project's tooling. The default model slugs
|
||||||
|
recorded in the source are:
|
||||||
|
|
||||||
|
- `bytedance-seed/seedream-4.5` — `tools/generate_textures.py`
|
||||||
|
- `openai/gpt-5-image-mini` — `tools/asset_pipeline.py`,
|
||||||
|
`tools/model_pipeline.py`, `tools/scene_concepts.py` and
|
||||||
|
`tools/screen_designs.py`
|
||||||
|
- Judging/review calls use the model selected by `tools/openrouter_judge.py` and
|
||||||
|
are development-only; they do not ship in the game.
|
||||||
|
|
||||||
|
The prompts and generation workflow live in those tools. Per-file generation
|
||||||
|
metadata is not stored with every image, so the exact provider, model version and
|
||||||
|
generation date of each committed bitmap cannot be reconstructed from the
|
||||||
|
repository alone. Before a public 0.10.0 release, the provider terms and
|
||||||
|
commercial-use/redistribution rights for those generated outputs must be
|
||||||
|
reviewed and recorded. **This is a release blocker, not an assumed clearance.**
|
||||||
|
|
||||||
|
Reference photographs linked from the venue documents are hosted on Wikimedia
|
||||||
|
Commons and were used for proportion/material study only. They are not included
|
||||||
|
in the game or its release packages. If any future derivative asset copies a
|
||||||
|
reference more directly, its attribution and licence must be added here.
|
||||||
|
|
||||||
|
## Audio
|
||||||
|
|
||||||
|
All runtime audio is procedural (crowd bed, radio crackle and interface cues).
|
||||||
|
There are no external audio files, so there is no separate music/SFX licence to
|
||||||
|
clear. See `docs/design/accessibility.md` for caption and volume behaviour.
|
||||||
|
|
||||||
|
## Third-party names
|
||||||
|
|
||||||
|
The game is a work of fiction. Real place names and public references are used
|
||||||
|
for orientation and satire/stand-in geography; no real person, venue or brand
|
||||||
|
endorses the game. Review the storefront and trademark language before release.
|
||||||
@@ -137,7 +137,7 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every
|
|||||||
|
|
||||||
- [x] Commit `export_presets.cfg` with Linux, Windows, and macOS desktop presets.
|
- [x] Commit `export_presets.cfg` with Linux, Windows, and macOS desktop presets.
|
||||||
- [x] Add `tools/build_releases.sh` for headless release exports.
|
- [x] Add `tools/build_releases.sh` for headless release exports.
|
||||||
- [x] Add `docs/RELEASING.md` with template installation, build, and Gitea release steps.
|
- [x] Add `docs/process/RELEASING.md` with template installation, build, and Gitea release steps.
|
||||||
- [x] Add a prominent **Downloads** section at the top of `README.md` linking to the release page.
|
- [x] Add a prominent **Downloads** section at the top of `README.md` linking to the release page.
|
||||||
- [x] Add `tools/publish_release.sh` (Gitea API release + asset upload, requires `GITEA_TOKEN`).
|
- [x] Add `tools/publish_release.sh` (Gitea API release + asset upload, requires `GITEA_TOKEN`).
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every
|
|||||||
- [x] Tag builds automatically publish the Gitea release via `tools/publish_release.sh` using
|
- [x] Tag builds automatically publish the Gitea release via `tools/publish_release.sh` using
|
||||||
the `GITEA_TOKEN` secret.
|
the `GITEA_TOKEN` secret.
|
||||||
- [x] Remove the old `.github/workflows/ci.yml` to avoid double runs on hosts scanning both paths.
|
- [x] Remove the old `.github/workflows/ci.yml` to avoid double runs on hosts scanning both paths.
|
||||||
- [x] Document the CI flow in `docs/RELEASING.md`.
|
- [x] Document the CI flow in `docs/process/RELEASING.md`.
|
||||||
|
|
||||||
## Gameplay — Venue selection
|
## Gameplay — Venue selection
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every
|
|||||||
service; ~2 min of retry timeouts per run; Godot import is fast without it).
|
service; ~2 min of retry timeouts per run; Godot import is fast without it).
|
||||||
- [x] Add `GODOT_SILENCE_ROOT_WARNING=1` to both jobs.
|
- [x] Add `GODOT_SILENCE_ROOT_WARNING=1` to both jobs.
|
||||||
- [x] Confirmed in run 1090: zero `CacheService` lines, 54/54 tests pass, job succeeds.
|
- [x] Confirmed in run 1090: zero `CacheService` lines, 54/54 tests pass, job succeeds.
|
||||||
- [x] Document the cache situation and re-enable path in `docs/RELEASING.md`.
|
- [x] Document the cache situation and re-enable path in `docs/process/RELEASING.md`.
|
||||||
- [x] Ignore runner logs in `.gitignore` (`.gitea/logs/`).
|
- [x] Ignore runner logs in `.gitignore` (`.gitea/logs/`).
|
||||||
|
|
||||||
## Gameplay — Venue door-staff dialogue
|
## Gameplay — Venue door-staff dialogue
|
||||||
@@ -660,7 +660,7 @@ produces a 63 MB static bundle and it serves locally with HTTP 200s for html/was
|
|||||||
- [x] Publish the web zip with the desktop artifacts (`tools/publish_release.sh`).
|
- [x] Publish the web zip with the desktop artifacts (`tools/publish_release.sh`).
|
||||||
- [x] Cover the preset, the non-threaded choice, and the release wiring in
|
- [x] Cover the preset, the non-threaded choice, and the release wiring in
|
||||||
`src/test/tests/test_web_export.gd`.
|
`src/test/tests/test_web_export.gd`.
|
||||||
- [x] Document the artifact in `docs/RELEASING.md`, the README gallery/status, and `PLAN.md`.
|
- [x] Document the artifact in `docs/process/RELEASING.md`, the README gallery/status, and `PLAN.md`.
|
||||||
|
|
||||||
## Prop Texture Atlas
|
## Prop Texture Atlas
|
||||||
|
|
||||||
@@ -909,7 +909,7 @@ Goal: replace the outdated flat skyline backdrop (`assets/textures/skyline.png`,
|
|||||||
with dot windows) and the flat-looking surface tiles with AI-generated, more realistic
|
with dot windows) and the flat-looking surface tiles with AI-generated, more realistic
|
||||||
backgrounds and textures. Add a larger set of example 3D scene images as art direction, and
|
backgrounds and textures. Add a larger set of example 3D scene images as art direction, and
|
||||||
measure the in-game result against that target with the screenshot tools. Plan:
|
measure the in-game result against that target with the screenshot tools. Plan:
|
||||||
[`PLAN.md` → Visual Quality Direction → Realism pass](PLAN.md#realism-pass).
|
[`PLAN.md` → Visual Quality Direction → Realism pass](PLAN.md#presentation-and-controls).
|
||||||
|
|
||||||
**Rule for every step: never overwrite an existing image.** New outputs get new paths. When a
|
**Rule for every step: never overwrite an existing image.** New outputs get new paths. When a
|
||||||
path is taken, the tools pick the next free `-vN` suffix. Raw AI output stays on disk under
|
path is taken, the tools pick the next free `-vN` suffix. Raw AI output stays on disk under
|
||||||
@@ -1047,7 +1047,7 @@ reports to `docs/screens/realism/<run>/`.
|
|||||||
`docs/screens/realism/2026-11-01-adopt/` (3 judge repeats per shot) gives a like-for-like
|
`docs/screens/realism/2026-11-01-adopt/` (3 judge repeats per shot) gives a like-for-like
|
||||||
improvement on every sub-score: overall 0.8→1.0, lighting 0.8→1.0, materials 0.8→1.0,
|
improvement on every sub-score: overall 0.8→1.0, lighting 0.8→1.0, materials 0.8→1.0,
|
||||||
texture 0.8→1.0, backdrop 0.6→1.6, and the Pillow metric gap 0.444→0.348.
|
texture 0.8→1.0, backdrop 0.6→1.6, and the Pillow metric gap 0.444→0.348.
|
||||||
- [x] Update `README.md` (tool usage + gallery), `MEMORY.md` and `docs/architecture.md`.
|
- [x] Update `README.md` (tool usage + gallery), `MEMORY.md` and `docs/project/architecture.md`.
|
||||||
|
|
||||||
## Realism Follow-ups — Lighting, Flashlight, NPC Detail
|
## Realism Follow-ups — Lighting, Flashlight, NPC Detail
|
||||||
|
|
||||||
@@ -1087,7 +1087,7 @@ Completed 2026-11-01. The follow-ups left over after the texture/skyline adoptio
|
|||||||
bridge and tip, ears with lobes, and split upper/lower lips. Hands have four fingers plus a
|
bridge and tip, ears with lobes, and split upper/lower lips. Hands have four fingers plus a
|
||||||
thumb, and every sleeve ends in a cuff ring.
|
thumb, and every sleeve ends in a cuff ring.
|
||||||
- [x] Raised `CHAR_MAX_VERTS` 4000 → **5000** (documented in the code and
|
- [x] Raised `CHAR_MAX_VERTS` 4000 → **5000** (documented in the code and
|
||||||
`docs/blender-assets.md`) after measuring 16 avatars at ~6.9 ms of a 50 ms frame on a single
|
`docs/assets/blender-assets.md`) after measuring 16 avatars at ~6.9 ms of a 50 ms frame on a single
|
||||||
skinned mesh, so `npc_coat` fits at 4807 vertices; the character range is now 3,974–5,026
|
skinned mesh, so `npc_coat` fits at 4807 vertices; the character range is now 3,974–5,026
|
||||||
triangles and 3,744–4,807 vertices. Body decimation 0.36 → 0.26.
|
triangles and 3,744–4,807 vertices. Body decimation 0.36 → 0.26.
|
||||||
- [x] Rebuilt all 8 variants and re-baked their AO maps (`assets/models/blender/npc/*_ao.png`),
|
- [x] Rebuilt all 8 variants and re-baked their AO maps (`assets/models/blender/npc/*_ao.png`),
|
||||||
@@ -1109,31 +1109,27 @@ 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
|
||||||
`MEMORY.md` and [ADR-0014](docs/adr/0014-runtime-image-root.md).
|
`MEMORY.md` and [ADR-0014](docs/adr/0014-runtime-image-root.md).
|
||||||
- [x] Move the reference mockups from `docs/screens/designs/` into the runtime root (keep `docs/screens/designs/` as a docs-only copy or replace it with the new path).
|
- [x] Move the reference mockups from `docs/screens/designs/` into the runtime root (keep `docs/screens/designs/` as a docs-only copy or replace it with the new path).
|
||||||
- [x] Document one convention for game vs reference vs development images across `assets/textures/`, `assets/images/`, `assets/models/generated/`, `docs/screenshots/`, and `docs/screens/designs/` (in `docs/architecture.md`, ADR-0014).
|
- [x] Document one convention for game vs reference vs development images across `assets/textures/`, `assets/images/`, `assets/models/generated/`, `docs/screenshots/`, and `docs/screens/designs/` (in `docs/project/architecture.md`, ADR-0014).
|
||||||
- [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/project/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).
|
||||||
@@ -1195,7 +1188,7 @@ architecture's version, runtime-art loader, asset/tooling blocks, Gitea CI/build
|
|||||||
Web deployment and release artifact list were brought up to date. `MEMORY.md` was cleaned of
|
Web deployment and release artifact list were brought up to date. `MEMORY.md` was cleaned of
|
||||||
obsolete uncommitted/active-pass claims and now points at the current follow-ups.
|
obsolete uncommitted/active-pass claims and now points at the current follow-ups.
|
||||||
|
|
||||||
- [x] Re-check `PLAN.md`/`MEMORY.md` against the current build, update `README.md` and `docs/architecture.md`, and compact the historical PLAN sections.
|
- [x] Re-check `PLAN.md`/`MEMORY.md` against the current build, update `README.md` and `docs/project/architecture.md`, and compact the historical PLAN sections.
|
||||||
|
|
||||||
## NPC gear fit (v0.6.1)
|
## NPC gear fit (v0.6.1)
|
||||||
|
|
||||||
@@ -1204,7 +1197,7 @@ Completed 2026-11-01. Eyes, bag and helmet now fit the measured body silhouette;
|
|||||||
The 2026-11-01 detail pass fixed the bodies but not the props worn on them. In the portrait
|
The 2026-11-01 detail pass fixed the bodies but not the props worn on them. In the portrait
|
||||||
capture (`--stages=day,portrait`, see the report in `docs/screens/realism/2026-11-01-npc/`) the
|
capture (`--stages=day,portrait`, see the report in `docs/screens/realism/2026-11-01-npc/`) the
|
||||||
sunglasses span the whole upper face and the shoulder bag cuts through `npc_coat`'s skirt. Blender sources for gear
|
sunglasses span the whole upper face and the shoulder bag cuts through `npc_coat`'s skirt. Blender sources for gear
|
||||||
live in `tools/blender_assets.py` (`_build_gear()`), the contract in `docs/blender-assets.md`
|
live in `tools/blender_assets.py` (`_build_gear()`), the contract in `docs/assets/blender-assets.md`
|
||||||
(gear budget 1500 tris, `GEAR_ATTACH` points in `NpcAvatar`).
|
(gear budget 1500 tris, `GEAR_ATTACH` points in `NpcAvatar`).
|
||||||
|
|
||||||
- [x] Measure the fit in Blender, not by eye: `python3 tools/blender_assets.py fit` prints each
|
- [x] Measure the fit in Blender, not by eye: `python3 tools/blender_assets.py fit` prints each
|
||||||
@@ -1277,7 +1270,7 @@ have no stage in the committed `docs/screenshots/` gallery, only in `capture_rea
|
|||||||
`src/test/capture_screenshots.gd` (`12-joke-shop.png`, `13-studio-arrival.png`) reuse the
|
`src/test/capture_screenshots.gd` (`12-joke-shop.png`, `13-studio-arrival.png`) reuse the
|
||||||
existing `joke_shop_queue` / `studio_arrival` designs. `check_screenshots.sh` is **13 good /
|
existing `joke_shop_queue` / `studio_arrival` designs. `check_screenshots.sh` is **13 good /
|
||||||
0 bad**; the new screenshots are 18.2 / 32.6 MAE against their references.
|
0 bad**; the new screenshots are 18.2 / 32.6 MAE against their references.
|
||||||
- [-] Update `docs/architecture.md` (venue geometry), `README.md` (gallery) and the screenshot
|
- [-] Update `docs/project/architecture.md` (venue geometry), `README.md` (gallery) and the screenshot
|
||||||
table when the stages land.
|
table when the stages land.
|
||||||
|
|
||||||
## Forward+ desktop preset and a volumetric flashlight (v0.7.1)
|
## Forward+ desktop preset and a volumetric flashlight (v0.7.1)
|
||||||
@@ -1303,7 +1296,7 @@ path.
|
|||||||
dirs) and record the diff in `docs/screens/realism/2026-11-01-forward-plus/`: entrance
|
dirs) and record the diff in `docs/screens/realism/2026-11-01-forward-plus/`: entrance
|
||||||
metric gap 1.568 → 1.187, two-shot mean 0.907 → 0.723. Judge scores are blocked by the
|
metric gap 1.568 → 1.187, two-shot mean 0.907 → 0.723. Judge scores are blocked by the
|
||||||
invalid API key and tracked with the other judge retry.
|
invalid API key and tracked with the other judge retry.
|
||||||
- [x] Add the preset to `tools/build_releases.sh` and `docs/RELEASING.md` as an optional
|
- [x] Add the preset to `tools/build_releases.sh` and `docs/process/RELEASING.md` as an optional
|
||||||
"desktop HQ" artifact, and note the runtime requirement (Vulkan driver) in `README.md`.
|
"desktop HQ" artifact, and note the runtime requirement (Vulkan driver) in `README.md`.
|
||||||
`gig-line-desktop-hq-<version>.zip` packages the Linux/Windows binaries, override.cfg and
|
`gig-line-desktop-hq-<version>.zip` packages the Linux/Windows binaries, override.cfg and
|
||||||
README; `publish_release.sh` uploads it when present.
|
README; `publish_release.sh` uploads it when present.
|
||||||
@@ -1334,7 +1327,7 @@ back when a provider rejects a request.
|
|||||||
- [x] Re-run the full screen-design judge and the Forward+ A/B judge with the valid
|
- [x] Re-run the full screen-design judge and the Forward+ A/B judge with the valid
|
||||||
`OPENROUTER_API_KEY`: screen designs are **13/13 match, 0 diff**; the Forward+ report
|
`OPENROUTER_API_KEY`: screen designs are **13/13 match, 0 diff**; the Forward+ report
|
||||||
records the selected model and ~$0.006 spend.
|
records the selected model and ~$0.006 spend.
|
||||||
- [x] Update `docs/asset-tooling.md`, `MEMORY.md`, `README.md` and `CHANGELOG.md`; the
|
- [x] Update `docs/assets/asset-tooling.md`, `MEMORY.md`, `README.md` and `CHANGELOG.md`; the
|
||||||
public catalogue picks the cheapest image+JSON model (`z-ai/glm-5.3-flash` at run time).
|
public catalogue picks the cheapest image+JSON model (`z-ai/glm-5.3-flash` at run time).
|
||||||
|
|
||||||
## Milestone 0.8.0 — First hour and content depth
|
## Milestone 0.8.0 — First hour and content depth
|
||||||
@@ -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/design/gameplay.md#first-shift-tutorial`](docs/design/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/process/getting-started.md` and `docs/process/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.
|
||||||
@@ -1567,7 +1560,7 @@ and supported by a stable CI/release baseline.
|
|||||||
smoke requirement. This explicit matrix is documented.
|
smoke requirement. This explicit matrix is documented.
|
||||||
- [x] No project-wide upgrade in 0.9; the existing version-adaptive light-falloff shim remains
|
- [x] No project-wide upgrade in 0.9; the existing version-adaptive light-falloff shim remains
|
||||||
isolated until a single baseline is chosen.
|
isolated until a single baseline is chosen.
|
||||||
- [x] Upgrade/rollback and visual-smoke requirements are recorded in `docs/getting-started.md`
|
- [x] Upgrade/rollback and visual-smoke requirements are recorded in `docs/process/getting-started.md`
|
||||||
and `MEMORY.md`.
|
and `MEMORY.md`.
|
||||||
- [ ] Exit: one documented runtime version policy, consistent CI/local exports, and no untested
|
- [ ] Exit: one documented runtime version policy, consistent CI/local exports, and no untested
|
||||||
engine-specific rendering hacks.
|
engine-specific rendering hacks.
|
||||||
@@ -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/design/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/design/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
|
||||||
|
|
||||||
@@ -1653,7 +1646,7 @@ redesign.
|
|||||||
|
|
||||||
- [x] Do not run the release build, bump a release version or tag until the map exit criteria and
|
- [x] Do not run the release build, bump a release version or tag until the map exit criteria and
|
||||||
this NPC revisit are both signed off.
|
this NPC revisit are both signed off.
|
||||||
- [x] Record the sign-off in `docs/development-status.md` and `MEMORY.md`, then hand off to the
|
- [x] Record the sign-off in `docs/project/development-status.md` and `MEMORY.md`, then hand off to the
|
||||||
postponed integration/build milestone.
|
postponed integration/build milestone.
|
||||||
|
|
||||||
**Exit:** the final maps and lighting still present the NPCs clearly; no untriaged NPC regression
|
**Exit:** the final maps and lighting still present the NPCs clearly; no untriaged NPC regression
|
||||||
@@ -1670,20 +1663,20 @@ screen-design judge before any model/texture changes. No gameplay rules change.
|
|||||||
|
|
||||||
- [x] Inventory every screen/panel: start menu, venue menu, tutorial, HUD, pause,
|
- [x] Inventory every screen/panel: start menu, venue menu, tutorial, HUD, pause,
|
||||||
settings, Help, inspection, wand, radio/emergency, wheel, shift review,
|
settings, Help, inspection, wand, radio/emergency, wheel, shift review,
|
||||||
timeline and campaign/continue screens. Recorded in `docs/ui-copy.md`.
|
timeline and campaign/continue screens. Recorded in `docs/design/ui-copy.md`.
|
||||||
- [x] Record the canonical terms for admit/refuse, warning/blocking,
|
- [x] Record the canonical terms for admit/refuse, warning/blocking,
|
||||||
assistance/emergency, body camera, shift review and campaign; the current
|
assistance/emergency, body camera, shift review and campaign; the current
|
||||||
inspection/wand/radio/review labels use them (see `docs/ui-copy.md`).
|
inspection/wand/radio/review labels use them (see `docs/design/ui-copy.md`).
|
||||||
- [x] Audit all button labels for verb-first wording, capitalization, ordering,
|
- [x] Audit all button labels for verb-first wording, capitalization, ordering,
|
||||||
destructive-action confirmation and consistent confirm/cancel placement;
|
destructive-action confirmation and consistent confirm/cancel placement;
|
||||||
recorded in `docs/ui-copy.md` and fixed Quit/Apply wording.
|
recorded in `docs/design/ui-copy.md` and fixed Quit/Apply wording.
|
||||||
- [x] List every hardcoded player-facing string and route it through
|
- [x] List every hardcoded player-facing string and route it through
|
||||||
`content/strings.en.json`; remaining UI literals are numeric/debug-only or
|
`content/strings.en.json`; remaining UI literals are numeric/debug-only or
|
||||||
dynamic data. Placeholder validation covers the new keys.
|
dynamic data. Placeholder validation covers the new keys.
|
||||||
- [x] Review NPC/door-staff/radio/tutorial/help/review/campaign dialogue for
|
- [x] Review NPC/door-staff/radio/tutorial/help/review/campaign dialogue for
|
||||||
repetition, ambiguity, tone and localization length. (Reason contexts,
|
repetition, ambiguity, tone and localization length. (Reason contexts,
|
||||||
door-staff lines, radio/tutorial/help copy and length findings recorded in
|
door-staff lines, radio/tutorial/help copy and length findings recorded in
|
||||||
`docs/ui-copy.md`; longest dialogue line is 56 characters.)
|
`docs/design/ui-copy.md`; longest dialogue line is 56 characters.)
|
||||||
|
|
||||||
### Button and panel behavior
|
### Button and panel behavior
|
||||||
|
|
||||||
@@ -1691,15 +1684,15 @@ screen-design judge before any model/texture changes. No gameplay rules change.
|
|||||||
tutorial skip, venue continue, settings apply/cancel/reset, judge/review,
|
tutorial skip, venue continue, settings apply/cancel/reset, judge/review,
|
||||||
radio send/emergency hold and Help close. (Shared `UiStyles` states applied;
|
radio send/emergency hold and Help close. (Shared `UiStyles` states applied;
|
||||||
timeline boundary disabling and the keyboard-only F1 skip are documented in
|
timeline boundary disabling and the keyboard-only F1 skip are documented in
|
||||||
`docs/ui-copy.md`.)
|
`docs/design/ui-copy.md`.)
|
||||||
- [x] Verify keyboard-only and gamepad-only navigation: focus order, wrapping,
|
- [x] Verify keyboard-only and gamepad-only navigation: focus order, wrapping,
|
||||||
default focus, confirm/cancel behavior and no focus traps. (Focus rings,
|
default focus, confirm/cancel behavior and no focus traps. (Focus rings,
|
||||||
gamepad A/B UI bindings, default focus and per-modal cancel behavior are
|
gamepad A/B UI bindings, default focus and per-modal cancel behavior are
|
||||||
recorded in `docs/ui-copy.md`.)
|
recorded in `docs/design/ui-copy.md`.)
|
||||||
- [x] Verify every modal closes with Esc/right-click where documented, recaptures
|
- [x] Verify every modal closes with Esc/right-click where documented, recaptures
|
||||||
the mouse, and does not leave the guard frozen or the menu visible. (Single
|
the mouse, and does not leave the guard frozen or the menu visible. (Single
|
||||||
top-modal close path, menu/settings return handling and pointer recapture are
|
top-modal close path, menu/settings return handling and pointer recapture are
|
||||||
recorded in `docs/ui-copy.md`; session smoke covers menu Settings, Help
|
recorded in `docs/design/ui-copy.md`; session smoke covers menu Settings, Help
|
||||||
right-click and pause Settings.)
|
right-click and pause Settings.)
|
||||||
- [x] Test buttons at minimum/maximum HUD font scale and with 1.5–2× longer
|
- [x] Test buttons at minimum/maximum HUD font scale and with 1.5–2× longer
|
||||||
strings; fix clipping, overflow and overlapping layouts. (Target chip now
|
strings; fix clipping, overflow and overlapping layouts. (Target chip now
|
||||||
@@ -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/design/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/process/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.
|
||||||
@@ -1749,10 +1742,10 @@ fallback behavior.
|
|||||||
produces 48 frames; `sheet_npc_baseline.jpg` is committed.)
|
produces 48 frames; `sheet_npc_baseline.jpg` is committed.)
|
||||||
- [x] Define acceptance criteria for proportions, face readability, hair, hands, shoes, clothing
|
- [x] Define acceptance criteria for proportions, face readability, hair, hands, shoes, clothing
|
||||||
silhouette, gear fit and animation at the gameplay camera height (1.7 m). (Eight criteria
|
silhouette, gear fit and animation at the gameplay camera height (1.7 m). (Eight criteria
|
||||||
in `docs/npc-visual-review.md`; the current 8,000-triangle/8,000-vertex library contract is explicit; the `npc_base` fallback remains 6,000/4,000.)
|
in the review evidence (since retired with the 0.9.9 roster pages); the current 8,000-triangle/8,000-vertex library contract is explicit; the `npc_base` fallback remains 6,000/4,000.)
|
||||||
- [x] Record the camera/lighting/background used for comparison so model changes can be judged
|
- [x] Record the camera/lighting/background used for comparison so model changes can be judged
|
||||||
consistently; keep the existing portrait capture path deterministic. (Neutral studio, fixed
|
consistently; keep the existing portrait capture path deterministic. (Neutral studio, fixed
|
||||||
orbit cameras, no HUD/crowd/animation; commands recorded in `docs/npc-visual-review.md`.)
|
orbit cameras, no HUD/crowd/animation; commands recorded in the retired review evidence.)
|
||||||
- [x] Identify the highest-impact gaps from the current portraits (e.g. face depth, hair mass,
|
- [x] Identify the highest-impact gaps from the current portraits (e.g. face depth, hair mass,
|
||||||
neck/shoulder transition, hand/foot shape, coat/dress hem) and rank them by player visibility.
|
neck/shoulder transition, hand/foot shape, coat/dress hem) and rank them by player visibility.
|
||||||
(P1: tourist duplicate bag, face depth, backpack straps; P2: hands/grips, clothing tiling,
|
(P1: tourist duplicate bag, face depth, backpack straps; P2: hands/grips, clothing tiling,
|
||||||
@@ -1762,7 +1755,7 @@ fallback behavior.
|
|||||||
the side-by-side after sheet without overwriting it.)
|
the side-by-side after sheet without overwriting it.)
|
||||||
|
|
||||||
Completed 2026-11-02: 48-frame neutral studio baseline, eight criteria, current fit and
|
Completed 2026-11-02: 48-frame neutral studio baseline, eight criteria, current fit and
|
||||||
triangle/vertex measurements, and ranked P1/P2 gaps are in `docs/npc-visual-review.md`. The
|
triangle/vertex measurements, and ranked P1/P2 gaps are in `docs/assets/3d-model-review.md`. The
|
||||||
baseline itself changed no character models; the first P1 gear fixes are recorded under Gear
|
baseline itself changed no character models; the first P1 gear fixes are recorded under Gear
|
||||||
and equipment fit.
|
and equipment fit.
|
||||||
|
|
||||||
@@ -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/assets/3d-model-review.md#character-measurements`: 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;
|
||||||
@@ -1864,7 +1857,7 @@ and equipment fit.
|
|||||||
`blender_assets.py audit`, `fit`, character imports and the full Godot test suite.
|
`blender_assets.py audit`, `fit`, character imports and the full Godot test suite.
|
||||||
- [x] Recapture the portrait, character gallery and crowd-distance shots; compare old/new and run
|
- [x] Recapture the portrait, character gallery and crowd-distance shots; compare old/new and run
|
||||||
the Python screen-design judge.
|
the Python screen-design judge.
|
||||||
- [x] Update `docs/blender-assets.md`, architecture/status, `MEMORY.md` and the changelog with the
|
- [x] Update `docs/assets/blender-assets.md`, architecture/status, `MEMORY.md` and the changelog with the
|
||||||
new budgets, limitations and rebuild commands.
|
new budgets, limitations and rebuild commands.
|
||||||
- [x] Exit: all eight variants pass the portrait/readability criteria, gear and hems no longer
|
- [x] Exit: all eight variants pass the portrait/readability criteria, gear and hems no longer
|
||||||
clip in normal play, the 24-NPC budget remains green, and the visual judge has no unexplained
|
clip in normal play, the 24-NPC budget remains green, and the visual judge has no unexplained
|
||||||
@@ -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
|
||||||
@@ -1957,3 +1949,348 @@ screen-design judge is 21/21 match. The optional realism judge ran with
|
|||||||
local; the committed comparison sheet is
|
local; the committed comparison sheet is
|
||||||
`docs/blender_previews/sheet_texture_refinement_0.9.5.jpg`. Tagged `v0.9.5`;
|
`docs/blender_previews/sheet_texture_refinement_0.9.5.jpg`. Tagged `v0.9.5`;
|
||||||
Gitea run 922 passed checks and the full build and published seven assets.
|
Gitea run 922 passed checks and the full build and published seven assets.
|
||||||
|
|
||||||
|
## Patch 0.9.6 — Integration, performance and judge gate
|
||||||
|
|
||||||
|
Goal: integrate the UI/model/texture patches, prove the combined build on the
|
||||||
|
target paths and leave a clean release-candidate handoff.
|
||||||
|
|
||||||
|
### Integration and asset rebuild
|
||||||
|
|
||||||
|
- [x] Rebuild all changed Blender/GLB/AO assets, re-import, and verify manifest/fit/
|
||||||
|
audit coverage. (The v0.9.5 commit already rebuilt the base, eight characters,
|
||||||
|
gear and AO maps; this checkpoint re-imported them and re-ran
|
||||||
|
`blender_assets.py audit`/`fit` and `npc_pipeline.py audit`, all exit 0.)
|
||||||
|
- [x] Run the complete headless suite, Python tooling and smoke matrix; fix any
|
||||||
|
regression from the three polish patches. (Godot 263/263, Python 101/101;
|
||||||
|
14 smokes green: audio, campaign, crowd, emergency, focus/radial, movement,
|
||||||
|
NPC budget, PLAN verbs, props, scenario content, session shell, timeline,
|
||||||
|
tutorial and UI flows. Campaign/tutorial used isolated `XDG_DATA_HOME`.)
|
||||||
|
- [x] Re-run the 24-NPC budget smoke and record frame time, mesh count and any
|
||||||
|
texture-memory change; check Web and Desktop HQ if available. (24 avatars,
|
||||||
|
168 meshes, 131,616 triangles, 6.90 ms of 50 ms headless; NPC maps remain
|
||||||
|
0.48 MB. A clean detached worktree at `f5b8ef2` built Linux/Windows/macOS/
|
||||||
|
Web/HQ (`161.5/197.1/148.0/97.8/241.5 MB`); the Web zip has index.html,
|
||||||
|
wasm and pck, the HQ zip has `override.cfg`, and the HQ binary launches
|
||||||
|
headless with the Forward+ override.)
|
||||||
|
- [x] Verify save/campaign/tutorial behavior is unchanged by the visual/UI patches.
|
||||||
|
(Persistence/session tests remain in the 263-test suite; campaign and tutorial
|
||||||
|
smokes passed with isolated `XDG_DATA_HOME`.)
|
||||||
|
|
||||||
|
### Visual gallery and judge gate
|
||||||
|
|
||||||
|
- [x] Recapture the full gallery, portraits, venue/realsim stages and Desktop HQ A/B
|
||||||
|
as needed; no stale screenshots in the release set. (Gallery 21/21,
|
||||||
|
NPC baseline 48 frames, final-context review 96 frames and a fresh 10-shot
|
||||||
|
realism run were recaptured. No Forward+ code changed since 0.7.1, so the
|
||||||
|
HQ A/B was not re-staged; the HQ artifact is checked with the release build.)
|
||||||
|
- [x] Run `tools/check_screenshots.sh`, `screen_designs.py compare` and
|
||||||
|
`screen_designs.py judge` on the final captures. (21 good / 0 bad; compare
|
||||||
|
21 good / 0 bad; judge 21/21 match, 0 review, 0 diff.)
|
||||||
|
- [x] Run the realism judge for the affected concepts with a pinned model; record
|
||||||
|
model, attempt count, spend and verdicts. (`z-ai/glm-5.3-flash`, 1 pass per
|
||||||
|
shot, 10 shots, no fallback/retry: mean overall 2.1, lighting 2.4, materials
|
||||||
|
2.1, texture 1.7, backdrop 2.3, characters 1.8, Pillow gap 0.291. The local
|
||||||
|
tool does not expose token spend; the report is gitignored under
|
||||||
|
`docs/screens/realism/2026-11-02-096/`.)
|
||||||
|
- [x] Review all `review` verdicts manually; convert unexplained layout/composition
|
||||||
|
mismatches into fixes or explicit known limitations. (Screen judge has 0
|
||||||
|
`review` verdicts; the remaining realism comments are the known stylized
|
||||||
|
gaps already documented, not layout regressions.)
|
||||||
|
- [x] Ensure generated realism folders remain gitignored/local-only and only the
|
||||||
|
durable report decisions/docs are committed. (`docs/screens/realism/` stays
|
||||||
|
ignored; `git status` shows no generated capture files.)
|
||||||
|
|
||||||
|
### Release-candidate handoff
|
||||||
|
|
||||||
|
- [x] Update README, architecture, development status, changelog and TODO with the
|
||||||
|
final patch-level results and any remaining gaps. (README/PLAN/architecture
|
||||||
|
now point at 0.9.6; the changelog and development status record the
|
||||||
|
integration build, performance numbers, judge results and the clean-worktree
|
||||||
|
export rule; `docs/process/RELEASING.md` documents the ignored-folder trap.)
|
||||||
|
- [x] Tag/version or otherwise freeze `0.9.6` as the integration checkpoint before
|
||||||
|
the `0.10.0` release candidate. (Tag `v0.9.6` on `4bb2896`; Gitea run 1296
|
||||||
|
passed checks and the full build and published seven assets.)
|
||||||
|
- [x] Exit: combined build is functionally green, visually reviewed, performance
|
||||||
|
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
|
||||||
|
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
|
||||||
|
14 smokes green; the 24-NPC budget is 6.90 ms with 168 meshes and 131,616
|
||||||
|
triangles; the gallery and screen-design gate are 21 good / 0 bad and 21/21
|
||||||
|
match; the pinned realism judge (`z-ai/glm-5.3-flash`) scored mean overall 2.1
|
||||||
|
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
|
||||||
|
the HQ binary launched headless. Tagged `v0.9.6`; Gitea run 1296 passed checks
|
||||||
|
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/assets/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/project/development-status.md`, `docs/assets/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/assets/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).
|
||||||
|
|
||||||
|
## Milestone 0.9.9 — Quaternius NPC base swap (patch)
|
||||||
|
|
||||||
|
Goal: make one Quaternius-derived NPC render and animate correctly in Godot and ship it as the
|
||||||
|
pilot base, with the procedural library still selectable as the fallback. This is the first
|
||||||
|
versioned character milestone; it contains the rig correction that has been blocking the
|
||||||
|
ADR-0018 rollout.
|
||||||
|
|
||||||
|
**Why this is a patch:** it changes character presentation and animation quality but adds no new
|
||||||
|
player-facing system. The 0.10.0 minor is reserved for the release gates (see below), so a
|
||||||
|
blocked rig can no longer block the whole release.
|
||||||
|
|
||||||
|
**Completed 2026-11-06.** Shipped as `v0.9.9` (tagged, local release build with checksums)
|
||||||
|
and accepted by the owner after the follow-up fixes. Durable evidence: `docs/assets/3d-model-review.md`,
|
||||||
|
`docs/assets/blender-assets.md`, `docs/adr/0018-quaternius-stage2-runtime-rig.md`.
|
||||||
|
|
||||||
|
### Rig and export correction
|
||||||
|
|
||||||
|
- [x] Reproduce and record the failure: the `armature_apply` + `export_apply=True` bake wrote
|
||||||
|
wrong inverse bind matrices (Godot rendered A-pose while the rest was arm-down). Recorded
|
||||||
|
in `MEMORY.md`; `bake_armdown_clean.py` was the failing recipe.
|
||||||
|
- [x] Fix the arm-down bake so the exported skin matches the rest. `tmp/npc-research/bake_armdown_v2.py`
|
||||||
|
(pose-rotate → apply modifier → reset pose → edit-mode rotate arms → re-add modifier →
|
||||||
|
export `export_apply=False`) now yields a correct arm-down base: Blender deformed x ±0.221
|
||||||
|
and the rest bone tail points down. Still local (`tmp/`), not shipped.
|
||||||
|
- [x] Re-derive the pose composition for a non-180° rest. Superseded by the rig-agnostic
|
||||||
|
`_swing_bone()` below: the rest-relative `set_bone_pose_rotation` form was never exact,
|
||||||
|
because `get_bone_rest()` is parent-relative and already carries the importer's axis
|
||||||
|
conversion. Kept in `MEMORY.md` as a rejected approach.
|
||||||
|
- [x] Fix the skinning/rest bake end to end (commit `cd285b3`):
|
||||||
|
(1) the canonical base is **join-only** (`tmp/npc-research/build_base_minimal.py`: no
|
||||||
|
`remove_doubles` weld, no scale/ground transform, keep the Armature modifiers) — the weld
|
||||||
|
and the transform corrupted the Quaternius weights;
|
||||||
|
(2) the arm-down bake (`tmp/npc-research/bake_armdown_v5.py` — pose, read pose matrices,
|
||||||
|
apply modifier, **reset pose to identity**, set rest matrices, re-add modifier) bakes the
|
||||||
|
rest correctly (DEFORMED x −0.228/0.228, Godot bind pose arm-down);
|
||||||
|
(3) `NpcAvatar._swing_bone()` now writes the **global** bone pose from
|
||||||
|
`get_bone_global_rest()`, so one formula drives both the 62-joint Quaternius rig and the
|
||||||
|
13-core fallback (measured: posed bone axis matches the proxy pivot at dot 1.000 on the
|
||||||
|
Quaternius, and the procedural rigs are unchanged);
|
||||||
|
(4) `RIG_BONES` + `NpcAvatar.bone_name()` hold the only side mapping (the Quaternius labels
|
||||||
|
`L`/`R` opposite the project), and the hand proxy is `global_wrist − global_elbow`.
|
||||||
|
Verified in Godot: idle/sit/walk captures on npc_casual plus the unchanged procedural rigs.
|
||||||
|
- [x] Add regression tests: `test_shipped_rig_resolves_the_animation_joints` covers the joint map,
|
||||||
|
the baked arm-down rest, the down-pointing arm axis and the proxy side mapping; the Blender
|
||||||
|
audit/`inspect` and `npc_proportions` now understand both rigs. Godot 278/278, Python
|
||||||
|
167/167.
|
||||||
|
- [x] Give the Quaternius base the five tint slots: `build_base_minimal.py` renames the upstream
|
||||||
|
materials to `skin`/`cloth`/`pants`/`hair`/`accessory` and keeps `Eye`/`Eyebrows` as
|
||||||
|
fixed-colour detail. The source UV map is degenerate (every loop at 0,0), so the build now
|
||||||
|
Smart-UV-unwraps and repacks the layout — texel density went from an unmeasurable 0 to
|
||||||
|
73–74 px/m on the 256² maps and the 256² AO bake works again.
|
||||||
|
- [x] Idle and Walk verified on the 62-joint rig through the pose code, not the clips: the 24
|
||||||
|
upstream clips stay authoring reference only, because `_animate()` + `_sync_skeleton()`
|
||||||
|
already drive sitting, hand-raising and head turns, and playing clips would replace that
|
||||||
|
state machine rather than supplement it. Recorded in ADR-0018 and `MEMORY.md`.
|
||||||
|
- [x] Poses the clips do not cover are driven by the same proxy pivots (`_legs`, `_knees`,
|
||||||
|
`_arms`, `_elbows`, `_head`), which is why no clip retarget was needed.
|
||||||
|
- [x] Fallback stays tested: `_build_body()` still builds the 13-core rig, and
|
||||||
|
`test_forced_fallback_still_builds_the_procedural_rig` now asserts the hip swings and a hand
|
||||||
|
pivot exists on that path. No MPFB or Stage-1 proportion work reopened.
|
||||||
|
- [x] Backpack straps re-fitted for the Quaternius torso (`fb3e75a`) and the back attach raised to
|
||||||
|
shoulder-blade height; the camera kept its wrist strap.
|
||||||
|
|
||||||
|
### Silhouette and face gate (owner)
|
||||||
|
|
||||||
|
- [x] Re-run the silhouette gate at gameplay distance and in portrait against the current
|
||||||
|
`npc_casual`, front / three-quarter / profile. ADR-0018 removed the proportion warp, so
|
||||||
|
source proportions are accepted by definition (head 13.4 % of height, measured by
|
||||||
|
`npc_proportions`).
|
||||||
|
- [x] Owner visual signoff obtained (2026-11-06) on the roster, the faces, the head gear and the
|
||||||
|
hand/back gear. The review page and its sheets were retired after acceptance; the measurements
|
||||||
|
live on in `docs/assets/3d-model-review.md` and `docs/assets/blender-assets.md`.
|
||||||
|
- [x] Face-fidelity comparison was made and the full swap chosen; the comparison sheets were
|
||||||
|
retired with the review pages, and the decision is recorded in the follow-up section of
|
||||||
|
[`docs/adr/0018-quaternius-stage2-runtime-rig.md`](docs/adr/0018-quaternius-stage2-runtime-rig.md).
|
||||||
|
- [x] **Owner decision:** option (a) full swap (2026-11-05). Applied: all twelve characters are
|
||||||
|
baked from the three Quaternius sources inside 24,000 tris / 24,000 glTF vertices
|
||||||
|
(`a6ccd86`), and the gate was re-run.
|
||||||
|
- [x] Record the decision and its consequence in `MEMORY.md`,
|
||||||
|
`docs/assets/npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap` and the
|
||||||
|
follow-up decision section of
|
||||||
|
[`docs/adr/0018-quaternius-stage2-runtime-rig.md`](docs/adr/0018-quaternius-stage2-runtime-rig.md).
|
||||||
|
- [x] Owner visual signoff on the shipped roster (2026-11-06): accepted.
|
||||||
|
|
||||||
|
### Pilot rollout
|
||||||
|
|
||||||
|
- [x] Map the five tint slots onto the Quaternius materials and unwrap the degenerate source
|
||||||
|
UV map; bake the 256² AO and check texel density (73–74 px/m).
|
||||||
|
- [x] Re-fit every gear attach point against the Quaternius skeleton and test each piece on the
|
||||||
|
character (the 0.9.10 *Prop and gear fit pass* below carries the measurements).
|
||||||
|
- [x] Wired the approved base into all twelve roles, with the procedural library as the instant
|
||||||
|
fallback; no runtime download or optional dependency.
|
||||||
|
- [x] Ran `inspect` (clean), `audit` (12/12), `fit` (OK), `npc_proportions` (measured), the 24-NPC
|
||||||
|
budget smoke (175,264 tris, 0.87 ms) and the capture gates before/after the fixes.
|
||||||
|
- [x] Released `0.9.9`: `config/version` 0.9.9, `CHANGELOG.md` entry, local build with checksums,
|
||||||
|
commit, tag `v0.9.9` and push. Owner accepted the roster on 2026-11-06.
|
||||||
|
|
||||||
|
**Exit:** one committed Quaternius GLB renders arm-down with correct skinning, plays Idle and
|
||||||
|
Walk on the 62-joint rig, poses correctly under the new composition, passes the owner face and
|
||||||
|
silhouette gate, fits the 18 gear pieces, stays inside budget, and ships as `0.9.9` with the
|
||||||
|
procedural library still available as a fallback.
|
||||||
@@ -14,6 +14,94 @@ The game is about **judgment, communication, access verification, and proportion
|
|||||||
- **Emergency help is a last resort.** Responders handle crises the guard cannot safely manage; calling them has costs.
|
- **Emergency help is a last resort.** Responders handle crises the guard cannot safely manage; calling them has costs.
|
||||||
- **Readability beats raw fidelity.** The GL Compatibility renderer, clear silhouettes, the focus ring, and the HUD stay legible.
|
- **Readability beats raw fidelity.** The GL Compatibility renderer, clear silhouettes, the focus ring, and the HUD stay legible.
|
||||||
|
|
||||||
|
## Development Priorities
|
||||||
|
|
||||||
|
The active task order is in [`TODO.md`](TODO.md); the gap analysis and the release roadmap are
|
||||||
|
below. The current order is:
|
||||||
|
|
||||||
|
1. **0.9.9–0.9.12 patches.** Finish the character base (the accepted Quaternius 62-joint runtime),
|
||||||
|
roll it out to the 12 roles, close the scheduled venue/prop/lighting polish, and verify
|
||||||
|
performance, Web and accessibility.
|
||||||
|
2. **0.10.0 minor.** Public readiness: external playtests, balance acceptance, legal/storefront
|
||||||
|
approval, the platform matrix and the documentation/picture lock.
|
||||||
|
3. **0.10.x patches → 0.11.0.** Content and systems depth: live body-camera replay, a fourth
|
||||||
|
venue and the localization foundation.
|
||||||
|
4. **0.12.0 → 1.0.0.** Platform/distribution, content complete, release candidates and the stable
|
||||||
|
release.
|
||||||
|
|
||||||
|
Versions are assigned only when a track's scope is accepted into a release (see
|
||||||
|
[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
|
||||||
|
|
||||||
1. Watch the entrance, steps, queue, and sidewalk.
|
1. Watch the entrance, steps, queue, and sidewalk.
|
||||||
@@ -127,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.
|
||||||
|
|
||||||
@@ -152,14 +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:
|
||||||
|
|
||||||
- **Blender library:** 8 skinned NPC variants, 18 gear pieces, and 20 street/venue props, with baked albedo/normal/AO/ORM maps, a manifest, and procedural fallbacks. `Rig/Head` stays the focus anchor.
|
- **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.
|
||||||
- **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 path from the current vertical slice to the 0.10.0 release candidate — content depth, onboarding, accessibility, platform QA and release readiness — 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).
|
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
|
||||||
|
|
||||||
@@ -176,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.9.5`). 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/). 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.9`). 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/process/RELEASING.md`](docs/process/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
|
||||||
|
|
||||||
@@ -188,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.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Privacy Note
|
||||||
|
|
||||||
|
Gig Line runs locally. The game itself does not include telemetry, analytics,
|
||||||
|
accounts, advertising, cloud saves or network requests. It does not access the
|
||||||
|
camera, microphone, contacts or location, and the body-camera feature is a
|
||||||
|
simulated overlay, not a real recording device.
|
||||||
|
|
||||||
|
Player data is written only to Godot's local `user://` directory:
|
||||||
|
|
||||||
|
- `settings.json`
|
||||||
|
- `campaign.json`
|
||||||
|
- `shift_results.json`
|
||||||
|
- `shift_state.json`
|
||||||
|
|
||||||
|
Those files stay on the player's machine unless the player copies them. Deleting
|
||||||
|
the files (or the `Gig Line` user directory) resets the profile. See
|
||||||
|
[`docs/process/getting-started.md`](docs/process/getting-started.md#save-data) for
|
||||||
|
locations, migration and rollback instructions.
|
||||||
|
|
||||||
|
The repository's development tools can call OpenRouter when a developer supplies
|
||||||
|
`OPENROUTER_API_KEY`; those calls generate or judge development assets and are
|
||||||
|
not part of the shipped game. The key and generated review artifacts remain
|
||||||
|
local/gitignored.
|
||||||
|
|
||||||
|
Questions about a storefront release should be checked against the final package
|
||||||
|
and the provider terms recorded in [`CREDITS.md`](CREDITS.md); external legal and
|
||||||
|
storefront approval is still pending for 0.10.0.
|
||||||
@@ -6,20 +6,16 @@ The central design goal is simple:
|
|||||||
|
|
||||||
> The player wins by choosing the lowest level of escalation that keeps everyone safe.
|
> The player wins by choosing the lowest level of escalation that keeps everyone safe.
|
||||||
|
|
||||||
## Screenshots
|
## Development Priorities
|
||||||
|
|
||||||
| | |
|
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):
|
||||||
| --- | --- |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  | |
|
|
||||||
|
|
||||||
*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,
|
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.
|
||||||
precision walk, the Joke Shop's Sunset Strip frontage, the TV Studio arrival, and the prop lineup.*
|
2. **`0.10.0` minor** — public readiness: external playtests, balance acceptance, legal/storefront/signing decisions, the platform matrix and the documentation/picture lock.
|
||||||
|
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.
|
||||||
|
|
||||||
## ⬇️ Downloads
|
## ⬇️ Downloads
|
||||||
|
|
||||||
@@ -38,27 +34,86 @@ 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.5** — the texture and material refinement pass:
|
> **Latest published: v0.9.9** — the character-roster patch: all twelve NPCs now
|
||||||
> 256² NPC maps, a four-times fabric weave without baked AO blotches, repair of
|
> use the CC0 Quaternius base with the five tint slots, role gear and baked maps.
|
||||||
> the Walk of Fame terrazzo band and awning stripe, and per-phase skyline haze,
|
> Superseded: v0.9.8, the character-development patch: the
|
||||||
> on top of the v0.9.4 NPC visual optimization, the Hollywood/Joke Shop maps and
|
> 12-character library, realistic face pass, four crowd-variety variants and the
|
||||||
> the v0.9.3 UI/accessibility polish.
|
> wrist/hip/knee deformation fixes. Previous 0.9.x patches added the
|
||||||
|
> Hollywood/Joke Shop maps, UI/accessibility polish, NPC visual optimization,
|
||||||
|
> texture/material refinement and integration checkpoint.
|
||||||
>
|
>
|
||||||
> **Current source: v0.9.5** — tagged and built from the same commit.
|
> **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/process/RELEASING.md`](docs/process/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
|
||||||
> (NSIS + appimagetool) and uploads with the portable binaries.
|
> (NSIS + appimagetool) and uploads with the portable binaries.
|
||||||
>
|
>
|
||||||
> macOS builds are unsigned: right-click → Open, or clear the quarantine with
|
> macOS builds are unsigned: right-click → Open, or clear the quarantine with
|
||||||
> `xattr -dr com.apple.quarantine gig-line-macos.zip`. Build instructions and
|
> `xattr -dr com.apple.quarantine gig-line-macos.zip`. Build instructions and
|
||||||
> the release pipeline live in [`docs/RELEASING.md`](docs/RELEASING.md).
|
> the release pipeline live in [`docs/process/RELEASING.md`](docs/process/RELEASING.md).
|
||||||
|
|
||||||
If you are here to develop or test locally instead of downloading, jump to
|
If you are here to develop or test locally instead of downloading, jump to
|
||||||
[Setup and Run](#setup-and-run) below.
|
[Setup and Run](#setup-and-run) below.
|
||||||
|
|
||||||
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/assets/3d-model-review.md`](docs/assets/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
|
||||||
@@ -67,7 +122,7 @@ tickets or credentials, and escalate only when necessary. The game rewards calm
|
|||||||
resolution over force. The venue menu splits Hollywood Boulevard into Day and
|
resolution over force. The venue menu splits Hollywood Boulevard into Day and
|
||||||
Night rows; the full mechanics — the three venues, the six-level
|
Night rows; the full mechanics — the three venues, the six-level
|
||||||
escalation model, and the core systems — are in
|
escalation model, and the core systems — are in
|
||||||
[`docs/gameplay.md`](docs/gameplay.md); the design rationale lives in
|
[`docs/design/gameplay.md`](docs/design/gameplay.md); the design rationale lives in
|
||||||
[`PLAN.md`](PLAN.md).
|
[`PLAN.md`](PLAN.md).
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
@@ -75,13 +130,13 @@ escalation model, and the core systems — are in
|
|||||||
A venue-driven simulation: shared systems (movement, perception, interaction,
|
A venue-driven simulation: shared systems (movement, perception, interaction,
|
||||||
access verification, escalation, radio, crowd, reputation, evidence, shift
|
access verification, escalation, radio, crowd, reputation, evidence, shift
|
||||||
review) driven by per-venue scenario data, on Godot 4 + GDScript. The full
|
review) driven by per-venue scenario data, on Godot 4 + GDScript. The full
|
||||||
arc42-style document is [`docs/architecture.md`](docs/architecture.md);
|
arc42-style document is [`docs/project/architecture.md`](docs/project/architecture.md);
|
||||||
decisions are recorded as ADRs in [`docs/adr/`](docs/adr/).
|
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`
|
||||||
@@ -89,7 +144,7 @@ decisions are recorded as ADRs in [`docs/adr/`](docs/adr/).
|
|||||||
- **Targets:** Windows, Linux, macOS, Web
|
- **Targets:** Windows, Linux, macOS, Web
|
||||||
|
|
||||||
The asset pipelines, model tools, and screenshot gates are documented in
|
The asset pipelines, model tools, and screenshot gates are documented in
|
||||||
[`docs/asset-tooling.md`](docs/asset-tooling.md).
|
[`docs/assets/asset-tooling.md`](docs/assets/asset-tooling.md).
|
||||||
|
|
||||||
## Setup and Run
|
## Setup and Run
|
||||||
|
|
||||||
@@ -102,42 +157,60 @@ godot --headless --path . --script res://src/test/test_runner.gd # run all tes
|
|||||||
```
|
```
|
||||||
|
|
||||||
Full setup, every run/test/smoke command, and the controls table are in
|
Full setup, every run/test/smoke command, and the controls table are in
|
||||||
[`docs/getting-started.md`](docs/getting-started.md).
|
[`docs/process/getting-started.md`](docs/process/getting-started.md).
|
||||||
|
|
||||||
## Project Documentation
|
## Project Documentation
|
||||||
|
|
||||||
- [`PLAN.md`](PLAN.md) — Game design plan and scenario details.
|
- [`PLAN.md`](PLAN.md) — Game design plan, development priorities and scenario details.
|
||||||
- [`AGENTS.md`](AGENTS.md) — Development and collaboration workflow.
|
- [`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/assets/npc-model-sourcing.md`](docs/assets/npc-model-sourcing.md) — Character-source evaluation, licence rules and the character-development plan.
|
||||||
|
- [`docs/assets/makehuman-head-review.md`](docs/assets/makehuman-head-review.md) — Local MakeHuman head prototype, provenance and review verdict.
|
||||||
|
- [`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/README.md`](docs/README.md) — Documentation index and visual previews.
|
- [`docs/project/architecture.md`](docs/project/architecture.md) — arc42-style architecture documentation.
|
||||||
- [`docs/architecture.md`](docs/architecture.md) — arc42-style architecture documentation.
|
- [`docs/design/gameplay.md`](docs/design/gameplay.md) — Gameplay systems, venues, and the escalation model.
|
||||||
- [`docs/gameplay.md`](docs/gameplay.md) — Gameplay systems, venues, and the escalation model.
|
- [`docs/design/venues.md`](docs/design/venues.md) — Real-site references, game mapping and limitations for Hollywood, the Joke Shop and the TV Studio.
|
||||||
- [`docs/hollywood-boulevard-map.md`](docs/hollywood-boulevard-map.md) — Real-site references, game mapping and limitations for the Hollywood venue.
|
- [`docs/assets/asset-tooling.md`](docs/assets/asset-tooling.md) — Asset pipelines, model tools, and screenshot gates.
|
||||||
- [`docs/joke-shop-map.md`](docs/joke-shop-map.md) — Real-site references, game mapping and limitations for the Joke Shop / Sunset Strip venue.
|
- [`tools/README.md`](tools/README.md) — Tool inventory, categories, test commands and AI-skill evaluation.
|
||||||
- [`docs/tutorial.md`](docs/tutorial.md) — First-shift tutorial beats and rules.
|
- [`.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.
|
||||||
- [`docs/asset-tooling.md`](docs/asset-tooling.md) — Asset pipelines, model tools, and screenshot gates.
|
- [`.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/process/getting-started.md`](docs/process/getting-started.md) — Setup, run/test commands, and controls.
|
||||||
- [`docs/development-status.md`](docs/development-status.md) — Implemented features and what's next.
|
- [`docs/project/development-status.md`](docs/project/development-status.md) — Implemented features and what's next.
|
||||||
- [`docs/smoke-checklist.md`](docs/smoke-checklist.md) — Manual smoke-test checklist.
|
- [`docs/design/accessibility.md`](docs/design/accessibility.md) — Accessibility features, known gaps and statement.
|
||||||
- [`docs/blender-assets.md`](docs/blender-assets.md) — Blender asset library: characters, gear, props, attach points.
|
- [`docs/design/known-limitations.md`](docs/design/known-limitations.md) — Release-candidate blockers and accepted limitations.
|
||||||
- [`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/process/release-qa.md`](docs/process/release-qa.md) — Manual smoke checklist and release-candidate balance protocol.
|
||||||
- [`docs/npc-proportion-audit.md`](docs/npc-proportion-audit.md) — NPC proportion measurements against the shared stylized guide.
|
- [`CREDITS.md`](CREDITS.md) — Credits, AI/reference provenance and licensing notes.
|
||||||
- [`docs/adr/`](docs/adr/) — Architecture Decision Records.
|
- [`PRIVACY.md`](PRIVACY.md) — Local-only data, save files and development API use.
|
||||||
- [`TODO.md`](TODO.md) — Active tasks plus the 0.x gap analysis and milestone roadmap.
|
- [`docs/assets/blender-assets.md`](docs/assets/blender-assets.md) — Blender asset library: characters, gear, props, attach points.
|
||||||
|
- [`docs/assets/npc-model-sourcing.md`](docs/assets/npc-model-sourcing.md) — character sources, the shipped roster table and the build steps; 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/adr/`](docs/adr) — Architecture Decision Records.
|
||||||
|
|
||||||
## Development Status
|
## Development Status
|
||||||
|
|
||||||
v0.9.5 is the current source version: the completed texture and material
|
v0.9.8 is the current source version: the character-development patch with the
|
||||||
refinement (256² NPC maps, a 4× fabric weave with baked-AO removal, repaired
|
12-character library, realistic face and crowd variants, pose/deformation fixes
|
||||||
Walk of Fame/awning surfaces and per-phase skyline haze), on top of the v0.9.4
|
and save-migration/release hardening. It sits on the 0.9.6 integration
|
||||||
NPC visual optimization and the v0.9.3 UI/accessibility polish pass. It builds on
|
checkpoint, 0.9.5 texture refinement, 0.9.4 NPC visual optimization, 0.9.3
|
||||||
v0.9.2's Hollywood/Joke Shop map rebuilds, v0.9.1's Godot 4.7.2 baseline,
|
UI/accessibility polish, 0.9.2 Hollywood/Joke Shop maps and the 0.9.x engine
|
||||||
v0.9.0's production-quality pass, v0.8's
|
baseline. The active work is the `0.9.9`–`0.9.12` patch line (Quaternius NPC
|
||||||
tutorial/campaign/resume work and the v0.7.x venue/gear/judge work. See [`docs/development-status.md`](docs/development-status.md)
|
base, roster, venue/asset polish, performance) followed by the `0.10.0`
|
||||||
for the implemented list and [`TODO.md`](TODO.md) for the 0.x roadmap. Manual
|
public-readiness minor. See
|
||||||
verification uses the [`docs/smoke-checklist.md`](docs/smoke-checklist.md).
|
[`docs/project/development-status.md`](docs/project/development-status.md) for the implemented list
|
||||||
|
and [`TODO.md`](TODO.md) for the priority-ordered roadmap. Manual verification uses
|
||||||
|
[`docs/process/release-qa.md`](docs/process/release-qa.md).
|
||||||
|
|
||||||
|
## Credits, Privacy and Accessibility
|
||||||
|
|
||||||
|
Gig Line is a local-only single-player game: there is no runtime telemetry or
|
||||||
|
network service, and saves stay in Godot's local `user://` folder. The
|
||||||
|
body-camera overlay is simulated. Asset provenance, AI-assisted image usage and
|
||||||
|
credits are recorded in [`CREDITS.md`](CREDITS.md); the local-data policy is in
|
||||||
|
[`PRIVACY.md`](PRIVACY.md); accessibility features and gaps are in
|
||||||
|
[`docs/design/accessibility.md`](docs/design/accessibility.md); and release-candidate blockers
|
||||||
|
are listed in [`docs/design/known-limitations.md`](docs/design/known-limitations.md).
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
|
|
||||||
|
|||||||
@@ -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,42 +114,20 @@
|
|||||||
"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": {
|
|
||||||
"attach": "head",
|
|
||||||
"bounds_godot": {
|
|
||||||
"max": [
|
|
||||||
0.168,
|
|
||||||
0.209,
|
|
||||||
0.258
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
-0.168,
|
|
||||||
0.037,
|
|
||||||
-0.186
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"category": "npc_gear",
|
|
||||||
"materials": [
|
|
||||||
"plastic_black"
|
|
||||||
],
|
|
||||||
"path": "assets/models/blender/npc_gear/cap.glb",
|
|
||||||
"triangles": 328,
|
|
||||||
"vertices": 285
|
|
||||||
},
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"attach": "hand",
|
"attach": "hand",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.115,
|
0.11,
|
||||||
0.05,
|
-0.07,
|
||||||
0.053
|
0.053
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.115,
|
-0.11,
|
||||||
-0.265,
|
-0.375,
|
||||||
0.036
|
0.036
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -188,12 +144,12 @@
|
|||||||
"crowd_barrier": {
|
"crowd_barrier": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
1.116,
|
1.12,
|
||||||
1.066,
|
1.07,
|
||||||
0.307
|
0.307
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-1.122,
|
-1.128,
|
||||||
-0.014,
|
-0.014,
|
||||||
-0.307
|
-0.307
|
||||||
]
|
]
|
||||||
@@ -262,14 +218,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 +235,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.166,
|
||||||
0.26,
|
0.26,
|
||||||
0.245,
|
0.166
|
||||||
0.26
|
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.26,
|
-0.166,
|
||||||
0.035,
|
0.125,
|
||||||
-0.351
|
-0.225
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
@@ -351,28 +307,6 @@
|
|||||||
"triangles": 268,
|
"triangles": 268,
|
||||||
"vertices": 400
|
"vertices": 400
|
||||||
},
|
},
|
||||||
"hood": {
|
|
||||||
"attach": "head",
|
|
||||||
"bounds_godot": {
|
|
||||||
"max": [
|
|
||||||
0.184,
|
|
||||||
0.236,
|
|
||||||
0.123
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
-0.184,
|
|
||||||
-0.146,
|
|
||||||
-0.225
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"category": "npc_gear",
|
|
||||||
"materials": [
|
|
||||||
"plastic_grey"
|
|
||||||
],
|
|
||||||
"path": "assets/models/blender/npc_gear/hood.glb",
|
|
||||||
"triangles": 804,
|
|
||||||
"vertices": 649
|
|
||||||
},
|
|
||||||
"lanyard": {
|
"lanyard": {
|
||||||
"attach": "chest",
|
"attach": "chest",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
@@ -439,26 +373,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.262,
|
||||||
1.824,
|
1.866,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.321,
|
-0.262,
|
||||||
0,
|
-0.004,
|
||||||
-0.178
|
-0.091
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -466,11 +402,13 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"sclera",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"pants",
|
||||||
|
"mouth",
|
||||||
|
"pupil"
|
||||||
],
|
],
|
||||||
"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,32 +425,36 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4482,
|
"triangles": 6242,
|
||||||
"vertices": 4260
|
"vertices": 13100
|
||||||
},
|
},
|
||||||
"npc_coat": {
|
"npc_coat": {
|
||||||
|
"base": "suit",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.332,
|
0.255,
|
||||||
1.824,
|
1.809,
|
||||||
0.189
|
0.279
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.332,
|
-0.255,
|
||||||
0,
|
-0.001,
|
||||||
-0.18
|
-0.083
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
"layer": "coat",
|
"layer": "coat",
|
||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
|
"Tie",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
"White",
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"sclera",
|
||||||
|
"pants",
|
||||||
|
"pupil"
|
||||||
],
|
],
|
||||||
"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,32 +470,220 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5474,
|
"triangles": 6582,
|
||||||
"vertices": 5264
|
"vertices": 13607
|
||||||
},
|
},
|
||||||
"npc_dress": {
|
"npc_crowd_a": {
|
||||||
|
"base": "beach",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.288,
|
0.252,
|
||||||
1.825,
|
1.805,
|
||||||
0.208
|
0.277
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.288,
|
-0.252,
|
||||||
0,
|
-0.005,
|
||||||
-0.208
|
-0.088
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "none",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"accessory",
|
||||||
|
"Eyebrows",
|
||||||
|
"sclera",
|
||||||
|
"hair",
|
||||||
|
"Earrings",
|
||||||
|
"pants",
|
||||||
|
"pupil"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_a.glb",
|
||||||
|
"roles": [
|
||||||
|
"tourist",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 6602,
|
||||||
|
"vertices": 13557
|
||||||
|
},
|
||||||
|
"npc_crowd_b": {
|
||||||
|
"base": "farmer",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.256,
|
||||||
|
1.81,
|
||||||
|
0.28
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.256,
|
||||||
|
-0.0,
|
||||||
|
-0.11
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "none",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"LightBlue",
|
||||||
|
"cloth",
|
||||||
|
"Beige",
|
||||||
|
"accessory",
|
||||||
|
"Eyebrows",
|
||||||
|
"Beige.001",
|
||||||
|
"Red",
|
||||||
|
"sclera",
|
||||||
|
"pants",
|
||||||
|
"pupil",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"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": 5512,
|
||||||
|
"vertices": 11291
|
||||||
|
},
|
||||||
|
"npc_crowd_c": {
|
||||||
|
"base": "adventurer",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.255,
|
||||||
|
1.809,
|
||||||
|
0.279
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.255,
|
||||||
|
-0.001,
|
||||||
|
-0.256
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "coat",
|
||||||
|
"materials": [
|
||||||
|
"Brown",
|
||||||
|
"LightGreen",
|
||||||
|
"Gold",
|
||||||
|
"Green",
|
||||||
|
"skin",
|
||||||
|
"Tie",
|
||||||
|
"cloth",
|
||||||
|
"White",
|
||||||
|
"accessory",
|
||||||
|
"Eyebrows",
|
||||||
|
"hair",
|
||||||
|
"sclera",
|
||||||
|
"pants",
|
||||||
|
"pupil"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_c.glb",
|
||||||
|
"roles": [
|
||||||
|
"intoxicated",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 9526,
|
||||||
|
"vertices": 19501
|
||||||
|
},
|
||||||
|
"npc_crowd_d": {
|
||||||
|
"base": "worker",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.262,
|
||||||
|
1.866,
|
||||||
|
0.287
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.262,
|
||||||
|
-0.004,
|
||||||
|
-0.087
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": "npc",
|
||||||
|
"layer": "hood",
|
||||||
|
"materials": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"accessory",
|
||||||
|
"Eyebrows",
|
||||||
|
"Moustache",
|
||||||
|
"pants",
|
||||||
|
"Brows",
|
||||||
|
"sclera",
|
||||||
|
"hair",
|
||||||
|
"mouth",
|
||||||
|
"pupil"
|
||||||
|
],
|
||||||
|
"mesh_node": "npc_base",
|
||||||
|
"path": "assets/models/blender/npc/npc_crowd_d.glb",
|
||||||
|
"roles": [
|
||||||
|
"tourist",
|
||||||
|
"guest"
|
||||||
|
],
|
||||||
|
"tint_slots": [
|
||||||
|
"skin",
|
||||||
|
"cloth",
|
||||||
|
"pants",
|
||||||
|
"accessory",
|
||||||
|
"hair"
|
||||||
|
],
|
||||||
|
"triangles": 6290,
|
||||||
|
"vertices": 13188
|
||||||
|
},
|
||||||
|
"npc_dress": {
|
||||||
|
"base": "king",
|
||||||
|
"bounds_godot": {
|
||||||
|
"max": [
|
||||||
|
0.249,
|
||||||
|
1.809,
|
||||||
|
0.269
|
||||||
|
],
|
||||||
|
"min": [
|
||||||
|
-0.249,
|
||||||
|
-0.001,
|
||||||
|
-0.083
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
"layer": "dress",
|
"layer": "dress",
|
||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
|
"Blue",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
"Beige",
|
||||||
|
"Metal_Dark",
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"Gold",
|
||||||
|
"sclera",
|
||||||
|
"pants",
|
||||||
|
"Metal_Dark_legs",
|
||||||
|
"pupil"
|
||||||
],
|
],
|
||||||
"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 +697,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4900,
|
"triangles": 11126,
|
||||||
"vertices": 4719
|
"vertices": 22516
|
||||||
},
|
},
|
||||||
"npc_firefighter": {
|
"npc_firefighter": {
|
||||||
|
"base": "worker",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.37,
|
0.262,
|
||||||
1.819,
|
1.866,
|
||||||
0.213
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.37,
|
-0.262,
|
||||||
0,
|
-0.004,
|
||||||
-0.213
|
-0.087
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -588,13 +719,15 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"Moustache",
|
||||||
|
"pants",
|
||||||
|
"Brows",
|
||||||
|
"sclera",
|
||||||
"hair",
|
"hair",
|
||||||
"hivis",
|
"mouth",
|
||||||
"reflective",
|
"pupil"
|
||||||
"rubber"
|
|
||||||
],
|
],
|
||||||
"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 +741,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5344,
|
"triangles": 6290,
|
||||||
"vertices": 5494
|
"vertices": 13188
|
||||||
},
|
},
|
||||||
"npc_guard": {
|
"npc_guard": {
|
||||||
|
"base": "worker",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.361,
|
0.262,
|
||||||
1.819,
|
1.866,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.361,
|
-0.262,
|
||||||
0,
|
-0.004,
|
||||||
-0.172
|
-0.087
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -629,15 +763,15 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"Moustache",
|
||||||
|
"pants",
|
||||||
|
"Brows",
|
||||||
|
"sclera",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber",
|
"mouth",
|
||||||
"leather",
|
"pupil"
|
||||||
"steel",
|
|
||||||
"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,32 +786,38 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5046,
|
"triangles": 6290,
|
||||||
"vertices": 5096
|
"vertices": 13188
|
||||||
},
|
},
|
||||||
"npc_hoodie": {
|
"npc_hoodie": {
|
||||||
|
"base": "punk",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.326,
|
0.241,
|
||||||
1.856,
|
1.806,
|
||||||
0.189
|
0.263
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.326,
|
-0.241,
|
||||||
0,
|
-0.004,
|
||||||
-0.225
|
-0.147
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
"layer": "hood",
|
"layer": "hood",
|
||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
|
"White",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"sclera",
|
||||||
|
"Eyebrows",
|
||||||
|
"Red_Dark",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"Earrings",
|
||||||
|
"Skin_legs",
|
||||||
|
"pants",
|
||||||
|
"pupil"
|
||||||
],
|
],
|
||||||
"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 +833,21 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 5304,
|
"triangles": 5524,
|
||||||
"vertices": 4950
|
"vertices": 11257
|
||||||
},
|
},
|
||||||
"npc_jacket": {
|
"npc_jacket": {
|
||||||
|
"base": "casual_2",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.332,
|
0.255,
|
||||||
1.824,
|
1.809,
|
||||||
0.189
|
0.279
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.332,
|
-0.255,
|
||||||
0,
|
-0.001,
|
||||||
-0.178
|
-0.164
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
@@ -714,11 +855,13 @@
|
|||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Skin_Darker",
|
||||||
|
"Eyebrows",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber"
|
"sclera",
|
||||||
|
"pants",
|
||||||
|
"pupil"
|
||||||
],
|
],
|
||||||
"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 +877,38 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4872,
|
"triangles": 5800,
|
||||||
"vertices": 4661
|
"vertices": 11866
|
||||||
},
|
},
|
||||||
"npc_vest": {
|
"npc_vest": {
|
||||||
|
"base": "worker",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.321,
|
0.262,
|
||||||
1.819,
|
1.789,
|
||||||
0.189
|
0.287
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.321,
|
-0.262,
|
||||||
0,
|
-0.004,
|
||||||
-0.172
|
-0.083
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc",
|
"category": "npc",
|
||||||
"layer": "vest",
|
"layer": "vest",
|
||||||
"materials": [
|
"materials": [
|
||||||
"skin",
|
"skin",
|
||||||
|
"Worker_Yellow",
|
||||||
"cloth",
|
"cloth",
|
||||||
"pants",
|
|
||||||
"accessory",
|
"accessory",
|
||||||
"eye_white",
|
"Eyebrows",
|
||||||
|
"Moustache",
|
||||||
|
"pants",
|
||||||
|
"Brows",
|
||||||
|
"sclera",
|
||||||
"hair",
|
"hair",
|
||||||
"rubber",
|
"mouth",
|
||||||
"hivis",
|
"pupil"
|
||||||
"reflective"
|
|
||||||
],
|
],
|
||||||
"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 +922,8 @@
|
|||||||
"accessory",
|
"accessory",
|
||||||
"hair"
|
"hair"
|
||||||
],
|
],
|
||||||
"triangles": 4980,
|
"triangles": 6290,
|
||||||
"vertices": 4879
|
"vertices": 13186
|
||||||
},
|
},
|
||||||
"palm_tree": {
|
"palm_tree": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
@@ -794,7 +941,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,
|
||||||
@@ -827,7 +975,7 @@
|
|||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.5,
|
0.5,
|
||||||
1.08,
|
1.118,
|
||||||
0.5
|
0.5
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
@@ -840,12 +988,13 @@
|
|||||||
"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",
|
||||||
"triangles": 1324,
|
"triangles": 818,
|
||||||
"vertices": 2780
|
"vertices": 1701
|
||||||
},
|
},
|
||||||
"protest_sign": {
|
"protest_sign": {
|
||||||
"attach": "hand",
|
"attach": "hand",
|
||||||
@@ -900,24 +1049,25 @@
|
|||||||
"round_planter": {
|
"round_planter": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.432,
|
0.399,
|
||||||
1.12,
|
1.175,
|
||||||
0.36
|
0.36
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.36,
|
-0.36,
|
||||||
0,
|
0,
|
||||||
-0.363
|
-0.376
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"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": 808,
|
||||||
"vertices": 2418
|
"vertices": 1522
|
||||||
},
|
},
|
||||||
"scba_tank": {
|
"scba_tank": {
|
||||||
"attach": "back",
|
"attach": "back",
|
||||||
@@ -1071,14 +1221,14 @@
|
|||||||
"street_tree": {
|
"street_tree": {
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
1.179,
|
1.15,
|
||||||
3.62,
|
3.634,
|
||||||
1.102
|
1.053
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.941,
|
-1.041,
|
||||||
0,
|
0,
|
||||||
-1.082
|
-1.08
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "props",
|
"category": "props",
|
||||||
@@ -1090,21 +1240,21 @@
|
|||||||
],
|
],
|
||||||
"path": "assets/models/blender/props/street_tree.glb",
|
"path": "assets/models/blender/props/street_tree.glb",
|
||||||
"replaces": "WorldBuilder cone trees",
|
"replaces": "WorldBuilder cone trees",
|
||||||
"triangles": 2592,
|
"triangles": 1506,
|
||||||
"vertices": 5620
|
"vertices": 3330
|
||||||
},
|
},
|
||||||
"sunglasses": {
|
"sunglasses": {
|
||||||
"attach": "head",
|
"attach": "head",
|
||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.101,
|
0.065,
|
||||||
0.059,
|
0.141,
|
||||||
0.158
|
0.101
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.101,
|
-0.065,
|
||||||
0.011,
|
0.11,
|
||||||
0.003
|
0.002
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"category": "npc_gear",
|
"category": "npc_gear",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 72 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 |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 52 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/assets/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.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Quaternius modular parts
|
||||||
|
|
||||||
|
Build inputs for outfit, hair and accessory variety. Committed like the three whole-body
|
||||||
|
sources, so `build` works from a fresh checkout without the upstream pack.
|
||||||
|
|
||||||
|
## Where they come from
|
||||||
|
|
||||||
|
The upstream *Ultimate Modular Characters* pack is distributed as a Google Drive folder
|
||||||
|
(<https://drive.google.com/drive/folders/1USAAquX2JJWuA2m6zol0KUkFe3UkZ8zX>, CC0 1.0). It cannot be
|
||||||
|
fetched reproducibly by a script, so the owner downloads it **once** into the gitignored
|
||||||
|
`tmp/quaternius-intake/`. Everything after that is scripted:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 tools/quaternius_parts.py inventory --dir tmp/quaternius-intake
|
||||||
|
python3 tools/quaternius_parts.py extract \
|
||||||
|
--file tmp/quaternius-intake/<file>.glb --mesh <mesh> --part <part>
|
||||||
|
python3 tools/quaternius_parts.py verify --dir assets/models/blender_src/quaternius/parts
|
||||||
|
```
|
||||||
|
|
||||||
|
`inventory` and `verify` run outside Blender; `extract` needs Blender, because it rewrites the skin.
|
||||||
|
The pack itself is never committed — only the parts that are actually used, each with its SHA-256
|
||||||
|
recorded here and in `CREDITS.md`.
|
||||||
|
|
||||||
|
## Part contract
|
||||||
|
|
||||||
|
A part is **one skinned mesh on the 62-joint rig**:
|
||||||
|
|
||||||
|
- one mesh object, parented to the rig's armature, carrying `JOINTS_0`/`WEIGHTS_0`;
|
||||||
|
- the standard joint names (`Hips`, `Chest`, `Head`, `UpperArm.L`, …). The rig check in
|
||||||
|
`quaternius_parts.py verify` fails a part that is missing any of them, because
|
||||||
|
`NpcAvatar` maps those bones;
|
||||||
|
- materials named after a contract slot (`skin`, `cloth`, `pants`, `hair`, `accessory`) so the
|
||||||
|
runtime tint reaches them, or a fixed-colour detail name that is deliberately not tinted;
|
||||||
|
- triangles counted against the character budget: base body plus every joined part must stay
|
||||||
|
inside 24,000 triangles and 24,000 glTF vertices.
|
||||||
|
|
||||||
|
The builder drops the part's own armature when it joins the mesh onto a base body, because the base
|
||||||
|
already owns the skeleton and two armatures would duplicate every bone. Weights survive the join
|
||||||
|
because Blender merges vertex groups by name.
|
||||||
|
|
||||||
|
## Assigning parts to a role
|
||||||
|
|
||||||
|
```python
|
||||||
|
"npc_vest": {"layer": "vest", "base": "worker", "parts": ["hivis_vest"], ...}
|
||||||
|
```
|
||||||
|
|
||||||
|
`build` joins those parts after the body join and before the material slot map, so the part's
|
||||||
|
materials go through the same slot mapping as the base. The T-pose cache is keyed on the part list.
|
||||||
|
|
||||||
|
## Kept parts
|
||||||
|
|
||||||
|
| Part | Source file | SHA-256 | Tris |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| _(none yet — the pack has not been intaken)_ | | | |
|
||||||
|
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 |
@@ -1,36 +1,53 @@
|
|||||||
# Gig Line Documentation
|
# Gig Line Documentation
|
||||||
|
|
||||||
This folder holds the project documentation, visual references and generated QA evidence.
|
Documentation is grouped by category. Product intent, the gap analysis and the release roadmap
|
||||||
|
live in [`../PLAN.md`](../PLAN.md); the executable task list is [`../TODO.md`](../TODO.md); the
|
||||||
|
workflow rules are in [`../AGENTS.md`](../AGENTS.md).
|
||||||
|
|
||||||
|
## Categories
|
||||||
|
|
||||||
|
| Folder | Contents |
|
||||||
|
| --- | --- |
|
||||||
|
| [`project/`](project/README.md) | Architecture (arc42) and the implemented-features status |
|
||||||
|
| [`design/`](design/README.md) | Gameplay, venues, UI copy, accessibility and known limitations |
|
||||||
|
| [`assets/`](assets/README.md) | Blender library, asset tooling, NPC sourcing and the 3D model review |
|
||||||
|
| [`process/`](process/README.md) | Setup/controls, release QA and the release runbook |
|
||||||
|
| [`adr/`](adr/README.md) | Architecture Decision Records |
|
||||||
|
| [`screens/`](screens/README.md) | Licensed references, concepts and screen designs |
|
||||||
|
| [`blender_previews/`](blender_previews/README.md) | Committed character/gear/prop preview sheets |
|
||||||
|
| [`screenshots/`](screenshots/README.md) | The committed in-game gallery |
|
||||||
|
|
||||||
## Start here
|
## Start here
|
||||||
|
|
||||||
- [`../PLAN.md`](../PLAN.md) — product and game-design plan
|
- [`../PLAN.md`](../PLAN.md) — product, design pillars, gap analysis and the roadmap to 1.0.0
|
||||||
- [`gameplay.md`](gameplay.md) — controls, venues and escalation
|
- [`../TODO.md`](../TODO.md) — milestone-ordered active tasks
|
||||||
- [`joke-shop-map.md`](joke-shop-map.md) — Sunset Strip real-site mapping
|
- [`project/architecture.md`](project/architecture.md) — system architecture and ownership
|
||||||
- [`tutorial.md`](tutorial.md) — first-shift tutorial beats
|
- [`project/development-status.md`](project/development-status.md) — what the build supports now
|
||||||
- [`architecture.md`](architecture.md) — arc42 system architecture
|
- [`design/gameplay.md`](design/gameplay.md) — the playable loop and escalation model
|
||||||
- [`getting-started.md`](getting-started.md) — setup, run/test commands and controls
|
- [`design/venues.md`](design/venues.md) — venue mapping and real-site evidence
|
||||||
- [`asset-tooling.md`](asset-tooling.md) — image/model pipelines and screenshot gates
|
- [`assets/npc-model-sourcing.md`](assets/npc-model-sourcing.md) — the active NPC base and licence
|
||||||
- [`npc-visual-review.md`](npc-visual-review.md) — NPC visual baseline, acceptance criteria and ranked gaps
|
rules
|
||||||
- [`npc-proportion-audit.md`](npc-proportion-audit.md) — NPC proportion ratios and stylized guide results
|
- [`assets/3d-model-review.md`](assets/3d-model-review.md) — asset measurements and scorecard
|
||||||
- [`development-status.md`](development-status.md) — implemented features and current gaps
|
- [`process/getting-started.md`](process/getting-started.md) — setup, run/test and controls
|
||||||
- [`smoke-checklist.md`](smoke-checklist.md) — manual verification
|
- [`../tools/README.md`](../tools/README.md) — tool inventory and test runner
|
||||||
- [`ui-copy.md`](ui-copy.md) — screen, copy and button inventory
|
- [`../CREDITS.md`](../CREDITS.md) — credits, AI/reference provenance and licensing
|
||||||
- [`RELEASING.md`](RELEASING.md) — export, CI and publishing workflow
|
- [`../PRIVACY.md`](../PRIVACY.md) — local-only data and save files
|
||||||
- [`adr/README.md`](adr/README.md) — architecture decisions
|
|
||||||
|
|
||||||
## Visual references
|
## Current work
|
||||||
|
|
||||||
- [`screens/README.md`](screens/README.md) — concepts, designs, screenshots and realism runs
|
v0.9.9 shipped the Quaternius NPC roster. The active patch line is `0.9.10` (roster follow-ups:
|
||||||
- [`blender_previews/README.md`](blender_previews/README.md) — character/gear/prop previews
|
outfit families, the Swat base orientation, per-slot atlases) through `0.9.12`, followed by the
|
||||||
- [`screenshots/README.md`](screenshots/README.md) — committed game gallery
|
`0.10.0` public-readiness minor. See [`../TODO.md`](../TODO.md) for the live checklist.
|
||||||
- [`screen-design-report.md`](screen-design-report.md) — latest screen-design 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"> |
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# ADR-0015: NPC model variety sourcing
|
||||||
|
|
||||||
|
- **Status:** Superseded by [ADR-0017](0017-quaternius-npc-base.md) (2026-11-05)
|
||||||
|
- **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
|
||||||
|
|
||||||
|
The library has eight skinned characters, and role/seed selection reuses them
|
||||||
|
across the three venues. Crowds therefore repeat the same faces, builds and
|
||||||
|
outfits. The existing contract is strict: `npc_base` mesh node, 13 named bones at
|
||||||
|
fixed rest positions, five tint slots, front on Blender −Y and an 8,000
|
||||||
|
triangle/vertex budget.
|
||||||
|
|
||||||
|
A review compared free/open-source sources against that contract. Quaternius
|
||||||
|
Ultimate Modular (CC0), Kenney Blocky Characters (CC0), Khronos CesiumMan and
|
||||||
|
RiggedFigure (CC BY 4.0) and the MakeHuman base mesh (CC0) were imported and
|
||||||
|
measured. None was a drop-in; the full results are in
|
||||||
|
[`docs/assets/npc-model-sourcing.md`](../assets/npc-model-sourcing.md).
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. **Ship only CC0, or CC BY with recorded attribution.** No service asset whose
|
||||||
|
terms restrict redistribution may be used.
|
||||||
|
2. **Extend the project-owned procedural generator as the primary path** for
|
||||||
|
shipped NPC variety. The first target is four crowd-variety looks, bringing
|
||||||
|
the library to twelve characters.
|
||||||
|
3. **Keep Quaternius as a reviewed reference/prototype, not a shipped base.** A
|
||||||
|
contract retarget is a no-go because the source is a T-pose 62-bone realistic
|
||||||
|
rig with substantially different proportions from the arm-down 13-bone
|
||||||
|
contract. A future external-rig path would need its own ADR covering its
|
||||||
|
skeleton, animation source and per-model gear offsets.
|
||||||
|
4. **MakeHuman is deferred to a separate local development pipeline.** Its CC0
|
||||||
|
base mesh can be decimated into budget, but it needs retopology, a custom rig
|
||||||
|
and project-owned clothing before it is usable. It must not become a CI or
|
||||||
|
build dependency.
|
||||||
|
5. **Commit research previews with attribution.** CC-BY preview images are
|
||||||
|
development-only and are not bundled with the game or its release packages.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
|
||||||
|
- The shipped library keeps one art direction, one skeleton and one audit gate.
|
||||||
|
- Crowd variety lands without a new runtime dependency or a second animation
|
||||||
|
system.
|
||||||
|
- The licence position is simple: project-owned assets plus explicit CC-BY
|
||||||
|
attribution where a preview or future asset requires it.
|
||||||
|
- The Quaternius evaluation removes a tempting but high-risk retarget from the
|
||||||
|
critical path.
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
|
||||||
|
- Procedural variation is still limited by the existing face and clothing
|
||||||
|
vocabulary; four variants do not provide full age/body diversity.
|
||||||
|
- Quaternius' animation set remains unused unless a second rig path is approved.
|
||||||
|
- MakeHuman body-shape diversity is postponed.
|
||||||
|
- External previews add a small documentation footprint that must stay out of
|
||||||
|
release packages.
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
- **Retarget Quaternius onto the `npc_base` contract.** Rejected for now:
|
||||||
|
welding brings the geometry inside budget, but the rest pose and body
|
||||||
|
proportions differ enough that a contract rebuild would either distort the
|
||||||
|
model or require a full proportion warp.
|
||||||
|
- **Use Quaternius' own rig and animations directly.** Deferred: attractive
|
||||||
|
animation quality, but it adds a second skeleton/animation path and per-model
|
||||||
|
gear offsets to `NpcAvatar`.
|
||||||
|
- **Use MakeHuman as the primary body source.** Rejected for this milestone: high
|
||||||
|
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
|
||||||
|
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/assets/asset-tooling.md#optional-interactive-blender-mcp`](../assets/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/assets/npc-model-sourcing.md`](../assets/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,98 @@
|
|||||||
|
# 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/assets/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 24 upstream clips ship as authoring reference only and are **not played at runtime**.
|
||||||
|
`NpcAvatar._animate()` drives the proxy pivots and `_sync_skeleton()` writes the bone poses, which
|
||||||
|
is what the FSM already needed for sitting, raising a hand and turning the head. Playing the clips
|
||||||
|
would replace that state machine rather than supplement it; revisiting this needs its own ADR.
|
||||||
|
|
||||||
|
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
|
||||||
|
the face/silhouette gate (its sheets were retired with the review pages): 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.
|
||||||
@@ -18,3 +18,7 @@ Each ADR records the context, decision, consequences and alternatives for a dura
|
|||||||
- [0012-surface-materials-and-generated-props](0012-surface-materials-and-generated-props.md)
|
- [0012-surface-materials-and-generated-props](0012-surface-materials-and-generated-props.md)
|
||||||
- [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)
|
||||||
|
- [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)
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
# Asset Pipeline and Tooling
|
|
||||||
|
|
||||||
Everything the game renders is produced or gated by the scripts below. Architecture-level contracts,
|
|
||||||
data flow and failure fallbacks are summarized in
|
|
||||||
[`architecture.md`](architecture.md#content-and-asset-pipeline-architecture); this page is the
|
|
||||||
operational reference. Development-only outputs (AI concept sheets, Blender sources, raw AI fabrics)
|
|
||||||
are gitignored; the audited game assets are committed.
|
|
||||||
|
|
||||||
## Image Asset Tools
|
|
||||||
|
|
||||||
Install the Pillow dependency used by the existing texture generator and the asset tools:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -m pip install -r requirements-tools.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Generate a role-sized image (generation requires `OPENROUTER_API_KEY`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py generate --prompt "weathered red brick" --role tile --output assets/textures/brick_extra.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Generated images are resized to the selected role profile. Non-square requests use
|
|
||||||
the validated `openai/gpt-5-image-mini` source ratio and crop to the final role dimensions.
|
|
||||||
Transparent glyphs can be recolored while preserving their alpha mask with
|
|
||||||
`--foreground '#ffffff'`.
|
|
||||||
|
|
||||||
Crop uses the image center by default; `--x` and `--y` select an explicit crop origin. Resize defaults to aspect-preserving `contain`; use `--fit cover` to crop or `--fit stretch` to distort to the exact dimensions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py crop assets/textures/source.png assets/textures/crop.png --width 256 --height 128
|
|
||||||
python3 tools/asset_pipeline.py resize assets/textures/source.png assets/images/scenes/scene.png --width 1920 --height 1080 --fit cover
|
|
||||||
python3 tools/asset_pipeline.py seamless assets/textures/rough_tile.png assets/textures/tile.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Audit accepts image files or directories. `--role auto` infers common roles from paths and filenames; explicit roles include `tile`, `texture`, `icon`, `card`, `banner`, `backdrop`, `background`, `overlay`, `app_icon`, and `screenshot`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py audit assets/textures
|
|
||||||
python3 tools/asset_pipeline.py audit assets/images/scenes --role background --json
|
|
||||||
python3 -m unittest discover -s 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.
|
|
||||||
|
|
||||||
## Prop models
|
|
||||||
|
|
||||||
Street furniture (bollards, benches, bins, planters, newspaper boxes, a hydrant)
|
|
||||||
is real 3D geometry generated by `tools/model_pipeline.py` and written to
|
|
||||||
`assets/models/generated/*.glb`. Godot imports each file as a `PackedScene`, and
|
|
||||||
`WorldBuilder` instantiates it with a procedural fallback when the model or its
|
|
||||||
import is missing, so the world still builds on a fresh checkout.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Print the catalogue (parts, triangle count, whether a model/concept exists)
|
|
||||||
python3 tools/model_pipeline.py list
|
|
||||||
|
|
||||||
# Rebuild the models (deterministic, offline) and validate them
|
|
||||||
python3 tools/model_pipeline.py build
|
|
||||||
python3 tools/model_pipeline.py audit
|
|
||||||
|
|
||||||
# Optional: an image model supplies the colour palette per prop
|
|
||||||
# (requires OPENROUTER_API_KEY; the repo .env is read automatically)
|
|
||||||
python3 tools/model_pipeline.py concept
|
|
||||||
python3 tools/model_pipeline.py concept --only bench --force
|
|
||||||
```
|
|
||||||
|
|
||||||
Each prop is a small list of boxes and cylinders, so `build` needs no network and
|
|
||||||
the output is byte-identical between runs. `concept` asks an image model for a
|
|
||||||
studio-style reference; `dominant_colors` and `assign_roles` then map that image's
|
|
||||||
colours onto material roles (darkest → metal, warm saturated → wood, light grey →
|
|
||||||
stone, green → foliage). glTF stores colours in linear space, so the tool converts
|
|
||||||
the sRGB palettes before writing them. Re-run `build` and `godot --headless
|
|
||||||
--path . --import` after changing a prop. See
|
|
||||||
[ADR-0012](adr/0012-surface-materials-and-generated-props.md).
|
|
||||||
|
|
||||||
Surface materials share one definition in `src/game/materials.gd`: named
|
|
||||||
roughness/metallic profiles per surface, plus the environment tune-up (ACES
|
|
||||||
tonemapping, glow, fog, colour grading) that the phase lighting applies.
|
|
||||||
|
|
||||||
## NPC character model
|
|
||||||
|
|
||||||
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)).
|
|
||||||
`tools/npc_pipeline.py` builds `assets/models/generated/npc_base.glb` plus the
|
|
||||||
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
|
|
||||||
existing animation code; the procedural rig remains the offline fallback (see
|
|
||||||
[ADR-0013](adr/0013-blender-authored-npc-characters.md) and
|
|
||||||
[`blender-assets.md`](blender-assets.md)).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Print the frozen contract (slots, bone names, triangle budget)
|
|
||||||
python3 tools/npc_pipeline.py list
|
|
||||||
|
|
||||||
# Rebuild the skinned base mesh + neutral maps in Blender and validate it
|
|
||||||
flatpak run org.blender.Blender --background --python "$PWD/tools/npc_pipeline.py" -- build
|
|
||||||
python3 tools/npc_pipeline.py audit
|
|
||||||
|
|
||||||
# Layer the AI clothing albedos (shirt/trousers) over the procedural weave, and
|
|
||||||
# derive per-fabric outfit colours for the avatar palette
|
|
||||||
python3 tools/npc_pipeline.py prepare
|
|
||||||
python3 tools/npc_pipeline.py palette
|
|
||||||
```
|
|
||||||
|
|
||||||
`prepare` resizes the AI fabric tiles (kept locally in `assets/textures/npc/ai/`,
|
|
||||||
which is gitignored) onto the 256² `cloth`/`pants` albedos; `build` bakes them into
|
|
||||||
the `.glb` and regenerates the procedural normal + packed ORM maps at the same
|
|
||||||
256² size. Runtime fabric materials repeat the weave 4× (`NPC_FABRIC_UV_SCALE`)
|
|
||||||
and skip the per-character baked AO, which otherwise magnified into large
|
|
||||||
blotches; skin, hair and fixed gear keep AO. `palette` writes
|
|
||||||
`assets/textures/npc/palette.json` using `model_pipeline.dominant_colors`.
|
|
||||||
|
|
||||||
## Blender asset library (loaded by the game)
|
|
||||||
|
|
||||||
`tools/blender_assets.py` models a larger library procedurally in Blender for the
|
|
||||||
v0.6.0 integration: **8 skinned NPC characters** (casual, jacket, coat, hi-vis vest,
|
|
||||||
dress, hoodie, firefighter, guard), each a drop-in for `npc_base.glb` (same bones,
|
|
||||||
joint positions and tint slots); **18 gear pieces** authored at head/hand/back/chest
|
|
||||||
attach points (caps, hood, helmet, backpack, SCBA tank, lanyard, phone, cup,
|
|
||||||
clipboard, umbrella, camera, sign, flashlight, wand, radio, …); and **20 props**
|
|
||||||
(octagonal bollard and bin, slatted bench, hydrant, news box, planters, street lamp,
|
|
||||||
street and palm trees, A-frame sign, wall lantern, cone, crowd barrier, stanchion,
|
|
||||||
handrail, podium, double door, marquee, sedan).
|
|
||||||
|
|
||||||
`NpcAvatar` selects a character by role/seed, tints the five contract slots, and
|
|
||||||
attaches gear at the documented points; `WorldBuilder` loads the library props
|
|
||||||
with colliders/avoidance and drives their emissive parts from the day/night
|
|
||||||
cycle. The single `npc_base.glb` and primitive props remain offline fallbacks.
|
|
||||||
Contract and integration notes: [`blender-assets.md`](blender-assets.md).
|
|
||||||
|
|
||||||
| Characters | Props |
|
|
||||||
| --- | --- |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
```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" -- render
|
|
||||||
python3 tools/blender_assets.py sheet # docs/blender_previews/sheet_*.jpg
|
|
||||||
python3 tools/blender_assets.py audit # no Blender needed
|
|
||||||
godot --headless --path . --import
|
|
||||||
```
|
|
||||||
|
|
||||||
## Confirmed game assets
|
|
||||||
|
|
||||||
Everything below is audited and shipped in the game; development-only outputs
|
|
||||||
(AI concept sheets, Blender sources, raw AI fabrics) are gitignored.
|
|
||||||
|
|
||||||
| | |
|
|
||||||
| --- | --- |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
- **Surface textures:** the v2 picks above (brick, sidewalk pavers, dry and wet asphalt, awning canvas) plus their normal maps, loaded by `src/game/materials.gd` and `WorldBuilder`; the three skyline panoramas are switched per shift phase. The first-generation tiles and `assets/textures/skyline.png` are no longer loaded.
|
|
||||||
- **UI art:** `assets/textures/ai/game_ready/icons/` (flashlight, door lock, wand, REC) and `assets/textures/ai/game_ready/cards/` (ID, wristband, ticket, credential, guest list), loaded by `src/game/ui/hud.gd`.
|
|
||||||
- **Generated models:** `assets/models/generated/*.glb` — bollard, bench, trash bin, planter, newspaper box, hydrant, and the NPC base — plus their colour atlases.
|
|
||||||
- **NPC material maps:** `assets/textures/npc/*_{albedo,normal,orm}.png` and `palette.json`.
|
|
||||||
|
|
||||||
Audit them all:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready
|
|
||||||
python3 tools/model_pipeline.py audit
|
|
||||||
python3 tools/npc_pipeline.py audit
|
|
||||||
```
|
|
||||||
|
|
||||||
## Realism pass: scene concepts, skylines and textures
|
|
||||||
|
|
||||||
The skyline, surface tiles and phase lighting were rebuilt against AI concept frames. The
|
|
||||||
concepts live in [`docs/screens/concepts/`](../docs/screens/concepts/README.md); the validated
|
|
||||||
picks the game loads are in `assets/textures/ai/game_ready/v2/picks/` (day/dusk/night
|
|
||||||
panoramas, brick, sidewalk pavers, dry and wet asphalt, awning canvas, each with a normal
|
|
||||||
map).
|
|
||||||
|
|
||||||
`tools/scene_concepts.py` generates and processes new candidates, promotes the validated ones
|
|
||||||
to stable names, audits them, and scores captures against the concepts. It never overwrites an
|
|
||||||
image: new files get a `-vN` suffix, and `process` skips existing candidates unless `--new`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/scene_concepts.py generate --group scene # or backdrop / texture, --variants N
|
|
||||||
python3 tools/scene_concepts.py process && python3 tools/scene_concepts.py audit
|
|
||||||
python3 tools/scene_concepts.py promote # validated picks -> v2/picks/
|
|
||||||
godot --headless --path . --import
|
|
||||||
|
|
||||||
# A/B a candidate without touching game code: the same cameras, textures swapped at runtime.
|
|
||||||
godot --path . --script res://src/test/capture_realism.gd -- --out=res://docs/screens/realism/<run>/after
|
|
||||||
godot --path . --script res://src/test/capture_realism.gd -- --out=res://docs/screens/realism/<run>/try \
|
|
||||||
--swap=res://assets/textures/ai/game_ready/v2/picks/brick_facade.png=res://assets/textures/ai/game_ready/v2/brick_facade-v2.png
|
|
||||||
|
|
||||||
# Score before/after against the concepts (Pillow detail metrics + vision judge).
|
|
||||||
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> --judge \
|
|
||||||
--model google/gemini-2.5-flash --repeats 3
|
|
||||||
```
|
|
||||||
|
|
||||||
Judges default to `--model auto`: `tools/openrouter_judge.py` reads the live
|
|
||||||
OpenRouter catalogue, keeps image-input models that support `response_format`,
|
|
||||||
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
|
|
||||||
`GIG_LINE_JUDGE_MODEL` or `--model <id>` for a pinned comparison.
|
|
||||||
|
|
||||||
`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
|
|
||||||
promoted maps in day and night lighting, with the normal crowd hidden. It uses
|
|
||||||
the review-only `NpcAvatar.character_override` to include the guard model, which
|
|
||||||
has no spawned NPC role:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
godot --path . --script res://src/test/capture_npc_review.gd -- \
|
|
||||||
--out=res://tmp/npc-review
|
|
||||||
```
|
|
||||||
|
|
||||||
Contact sheets can be assembled from the files (the output is local QA and is
|
|
||||||
not committed). The runtime applies small attach-space offsets to the lanyard
|
|
||||||
and firefighter helmet so the badge clears the jacket and the brim does not sit
|
|
||||||
over the eyes; these are `NpcAvatar.GEAR_OFFSETS`, not changes to the gear
|
|
||||||
contract.
|
|
||||||
|
|
||||||
`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
|
|
||||||
[`npc-proportion-audit.md`](npc-proportion-audit.md). It reads the exported GLBs
|
|
||||||
and does not require Blender.
|
|
||||||
|
|
||||||
`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
|
|
||||||
camera, studio lighting and no venue/HUD/animation. The raw PNGs stay in
|
|
||||||
`tmp/`; `tools/npc_baseline.py sheet` builds the committed contact sheet, and
|
|
||||||
`tools/npc_baseline.py compare --before DIR --after DIR` builds the review
|
|
||||||
comparison after a model change. See
|
|
||||||
[`npc-visual-review.md`](npc-visual-review.md).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
godot --path . --script res://src/test/capture_npc_baseline.gd -- \
|
|
||||||
--out=res://tmp/npc-baseline
|
|
||||||
python3 tools/npc_baseline.py sheet --dir tmp/npc-baseline
|
|
||||||
```
|
|
||||||
|
|
||||||
`capture_realism.gd` runs nine stages in this order: day, Hollywood surface close-ups,
|
|
||||||
portrait, evening, late, Joke Shop day, Joke Shop surface close-ups, Joke Shop night,
|
|
||||||
studio; `--stages=day,late` limits it while tuning; `--flashlight` turns on the guard
|
|
||||||
lamp for night stages (used by the Desktop HQ A/B). The `portrait` shot freezes one
|
|
||||||
NPC on open sidewalk and frames the face, which is the only capture that can judge
|
|
||||||
avatar detail (`--portrait-type=N` picks a role). For the 0.9.5 texture audit, run
|
|
||||||
the surface stages directly; the HUD is hidden for them:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
godot --path . --script res://src/test/capture_realism.gd \
|
|
||||||
-- --out=res://tmp/surfaces --stages=surfaces,surfaces_joke
|
|
||||||
```
|
|
||||||
|
|
||||||
The generated realism runs under
|
|
||||||
`docs/screens/realism/` are **local, gitignored QA artifacts**; commit only the
|
|
||||||
resulting decisions/docs, not the run folders. The 0.9.5 texture/UI audit checked actual screen sizes: all screen art is
|
|
||||||
1280×720, access cards are 256×128 and HUD icons are 64×64; no runtime texture
|
|
||||||
was oversized or unused. The 24-NPC smoke now reports 168 meshes / 131,616
|
|
||||||
triangles / 6.94 ms. Compressed payloads are 24.55 MB of surface picks, 22.67 MB
|
|
||||||
of screen art and 0.48 MB of NPC maps.
|
|
||||||
|
|
||||||
The 0.9.5 realism judge ran once per shot with `z-ai/glm-5.3-flash`
|
|
||||||
(`python3 tools/scene_concepts.py realism --run docs/screens/realism/2026-11-02-tex
|
|
||||||
--judge`). Its mean `after` scores were overall 1.8, lighting 2.3, materials 1.8,
|
|
||||||
texture detail 1.2, backdrop 2.0 and characters 1.2; the Pillow metric gap was
|
|
||||||
0.306. The judge still ranks the painted concept targets above the stylized build,
|
|
||||||
particularly lit night facades and dense backdrop detail. That is expected: the
|
|
||||||
pass validated close-range surfaces and promoted the texture repairs, not a
|
|
||||||
photoreal renderer. The 0.9.5 before/after close-up sheet is
|
|
||||||
[`blender_previews/sheet_texture_refinement_0.9.5.jpg`](blender_previews/sheet_texture_refinement_0.9.5.jpg);
|
|
||||||
the texture-adoption run itself was the earlier 0.8-style candidate comparison,
|
|
||||||
and its report remains local/gitignored.
|
|
||||||
|
|
||||||
The Forward+ A/B compared the Compatibility entrance with the optional Desktop HQ
|
|
||||||
preset. The gear-fit run checked the sunglasses, bag and helmet against measured
|
|
||||||
head/skirt bounds with `python3 tools/blender_assets.py fit`; it also fixed the
|
|
||||||
portrait yaw and hid the focus overlay so the close-up shows the avatar.
|
|
||||||
|
|
||||||
Regenerate a run locally with the capture and `realism --run ...` commands above;
|
|
||||||
the reports and sheets are intentionally not part of the release repository.
|
|
||||||
|
|
||||||
## Screen design references
|
|
||||||
|
|
||||||
Screen mockups and screenshot comparison use `tools/screen_designs.py`. It turns the
|
|
||||||
screen descriptions in `docs/` (README captions, ADRs 0007–0010, `PLAN.md`, and
|
|
||||||
`architecture.md`) into runtime reference art under `assets/images/ui/` (with a
|
|
||||||
reference-only snapshot in `docs/screens/designs/`), then uses those images as
|
|
||||||
the expected state to check the game's captures.
|
|
||||||
|
|
||||||
Each screen is tagged `scene` (a realistic first-person 3D world view) or `ui`
|
|
||||||
(an in-game panel overlaid on a softened scene), and the generator appends a
|
|
||||||
shared art-direction suffix so the frames read as one coherent game. Requests use
|
|
||||||
a 3:2 landscape source and crop to the 16:9 capture dimensions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Export the built-in scenarios to editor-authorable .tres resources
|
|
||||||
godot --headless --path . --script res://tools/export_venues.gd
|
|
||||||
|
|
||||||
# List the catalogue (name, kind, whether a reference design exists)
|
|
||||||
python3 tools/screen_designs.py list
|
|
||||||
|
|
||||||
# Deterministic wireframes (offline, no API key); existing designs are kept
|
|
||||||
# unless --force is passed, so an offline run never replaces an AI mockup
|
|
||||||
python3 tools/screen_designs.py generate --draft
|
|
||||||
python3 tools/screen_designs.py generate --draft --only wand --force
|
|
||||||
|
|
||||||
# Realistic 3D scenes and in-game screen designs via OpenRouter
|
|
||||||
# (requires OPENROUTER_API_KEY)
|
|
||||||
python3 tools/screen_designs.py generate
|
|
||||||
|
|
||||||
# UI-state gallery (menu, pause, settings, review, radio, wheel, crowd, responder)
|
|
||||||
godot --path . --script res://src/test/capture_ui_states.gd -- --out=res://docs/screenshots
|
|
||||||
|
|
||||||
# Prop gallery (studio line-up) for close-range prop checks
|
|
||||||
godot --path . --script res://src/test/capture_prop_gallery.gd
|
|
||||||
|
|
||||||
# Compare docs/screenshots/*.png against the designs
|
|
||||||
python3 tools/screen_designs.py compare
|
|
||||||
python3 tools/screen_designs.py compare --json
|
|
||||||
|
|
||||||
# Ask a vision model whether each capture realises its design's screen and layout
|
|
||||||
# (requires OPENROUTER_API_KEY; the repo .env is read automatically)
|
|
||||||
python3 tools/screen_designs.py judge
|
|
||||||
python3 tools/screen_designs.py judge --model openai/gpt-4o-mini --json
|
|
||||||
python3 tools/screen_designs.py judge --only 03- # one capture
|
|
||||||
python3 tools/screen_designs.py judge --dry-run # build requests, no network
|
|
||||||
```
|
|
||||||
|
|
||||||
The catalogue covers 21 screens: the thirteen scene captures plus the start menu,
|
|
||||||
pause overlay, settings, shift review, radial menu, emergency radio, crowd panic,
|
|
||||||
and responder arrival. All 21 have an AI reference in `assets/images/ui/`
|
|
||||||
(snapshot in `docs/screens/designs/`) and a committed capture, so every entry is
|
|
||||||
compared or judged against the running game.
|
|
||||||
|
|
||||||
`compare` flags gross drift: wrong aspect ratio, undersized frame, near-blank
|
|
||||||
frame, and large layout/palette/detail distance. It is a regression gate, not a
|
|
||||||
semantic judge — pixel and perceptual metrics cannot decide whether a frame's
|
|
||||||
composition is *right*.
|
|
||||||
|
|
||||||
`judge` covers that gap: it sends each capture together with its concept mockup to
|
|
||||||
a vision model and asks whether the capture realises the same screen and layout
|
|
||||||
intent. Art style, fidelity, fonts, and placeholder text are explicitly ignored.
|
|
||||||
Verdicts are classed as `match`, `review` (a mismatch below `--fail-confidence`,
|
|
||||||
default 0.6 — expected when a concept mockup is more stylised than the game), or
|
|
||||||
`diff` (a confident mismatch, which exits non-zero). Regenerate the designs after changing screen layout, then re-run
|
|
||||||
`compare` after regenerating the game screenshots with
|
|
||||||
`src/test/capture_screenshots.gd` and `src/test/capture_ui_states.gd`.
|
|
||||||
|
|
||||||
For a one-command drift check, run `tools/check_screenshots.sh`. It renders the
|
|
||||||
scene captures, UI-state captures and prop gallery into `docs/screenshots/` (using
|
|
||||||
`xvfb-run` when no display is available) and then runs `screen_designs.py compare`.
|
|
||||||
@@ -0,0 +1,596 @@
|
|||||||
|
# 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-model-sourcing.md`](npc-model-sourcing.md) (sources, shipped roster and
|
||||||
|
build steps), [`blender-assets.md`](blender-assets.md) (asset contract),
|
||||||
|
[`asset-tooling.md`](asset-tooling.md) (commands). The 0.9.4 procedural baseline this page was
|
||||||
|
measured against no longer ships; its before/after sheets stay in `blender_previews/`.
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
## Foliage language P1 — resolved 2026-11-06
|
||||||
|
|
||||||
|
The ranked P1 was that shrubs read as rocks: `_shrub()` stacked spheres, so the silhouette was
|
||||||
|
smooth and the light response was a single facet per clump.
|
||||||
|
|
||||||
|
`_leaf_card()` replaces the stack: one low-poly inner mass for opacity, then 18–53 two-triangle
|
||||||
|
quads scattered over the canopy shell by a jittered Fibonacci distribution, each rotated onto the
|
||||||
|
canopy normal and tilted off it. No alpha texture, so the flat-colour library and the Compatibility
|
||||||
|
renderer are untouched.
|
||||||
|
|
||||||
|
| Prop | Before | After |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `planter_box` | 1,324 tris | 818 |
|
||||||
|
| `round_planter` | 1,240 tris | 808 |
|
||||||
|
| `street_tree` | 2,592 tris | 1,506 |
|
||||||
|
| Props total (20) | 13,808 | 11,784 |
|
||||||
|
|
||||||
|
## Lighting pass — flashlight measured 2026-11-06
|
||||||
|
|
||||||
|
`capture_realism.gd --stages=late --flashlight` renders the same frame with the lamp off and on, so
|
||||||
|
the beam's contribution is a number instead of an impression. Mean luminance, 0–255:
|
||||||
|
|
||||||
|
| Region | Lamp off | Old settings | Shipped now |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Aimed centre (facade) | 13.5 | 13.6 | **29.7** |
|
||||||
|
| Near pavement | 3.7 | 3.7 | **11.7** |
|
||||||
|
|
||||||
|
The old flashlight (energy 4.2, 20 m range, 14° cone, aimed dead ahead) moved the frame by 0.1/255 —
|
||||||
|
it was effectively off. The shipped values are 10 energy, 45 m, 24°, tilted 5° down: the beam lands
|
||||||
|
on the door or person being checked *and* puts a pool on the pavement in front, without flooding the
|
||||||
|
facade. Verified in both venues (Hollywood facade and the Joke Shop door).
|
||||||
|
|
||||||
|
### Street-lamp pools, measured
|
||||||
|
|
||||||
|
A new `lamp_late` shot frames a street lamp so the pool under it can be sampled. The lamp is
|
||||||
|
`StreetLamp_*` (named so `set_night_mode` can tune it apart from the sconce and marquee glows).
|
||||||
|
Attenuation stays at 1.0 — the venue test requires physical falloff — so the pool is made by range
|
||||||
|
and energy:
|
||||||
|
|
||||||
|
| Setting | Pool | Gap | Pool/gap |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| old: 16 m, energy 2.2 | 14.1 | 2.7 | 5.2 |
|
||||||
|
| 16 m, energy 4.0 | 21.1 | 6.5 | 3.3 (washed) |
|
||||||
|
| 8.5 m, energy 6.5 | 11.5 | 1.3 | 8.9 |
|
||||||
|
| **10 m, energy 8.0 (shipped)** | **16.0** | **1.3** | **12.3** |
|
||||||
|
|
||||||
|
Mean luminance 0–255. The shipped values brighten the pool past the old setting while darkening the
|
||||||
|
gap between poles, which is what "pools, not a wash" means numerically.
|
||||||
|
|
||||||
|
### Event banners read as lit cloth (2026-11-06)
|
||||||
|
|
||||||
|
The banners were one flat emissive panel, so they measured the same luminance from the mounting bar
|
||||||
|
to the hem (114/121/122/125 down the panel) and read as a painted red card rather than lit fabric.
|
||||||
|
They are now four stacked strips with a night gradient (energies 2.6 / 2.2 / 1.8 / 1.4), carried per
|
||||||
|
material through `night_energy` metadata so `set_night_mode` keeps the ratio instead of flattening it.
|
||||||
|
|
||||||
|
| | Before | After |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Vertical profile down a banner | 114 → 125 (flat) | 135 → 84 |
|
||||||
|
| Clipping (p99 / pixels ≥ 250) | 192 / 0.2 % | 192 / 0.2 % |
|
||||||
|
| Daylight read | uniform | uniform (strips flush, no seam) |
|
||||||
|
|
||||||
|
No clipping in either state, and the Compatibility renderer draws it with the same flat emissive
|
||||||
|
material as before — the change is geometry plus per-material energy, not a new shader.
|
||||||
|
|
||||||
|
## Set dressing — Joke Shop queue (2026-11-06)
|
||||||
|
|
||||||
|
The queue props were placed individually (`stanchion` at ±3, `crowd_barrier` at x 2.2, podium at 6.8),
|
||||||
|
so no line read and the stanchions' 0.9 m velvet stubs pointed in unrelated directions. They are now
|
||||||
|
one queue: four stanchions 0.9 m apart west of the guest approach lane, the check-in podium at the
|
||||||
|
head, and the barricade on the curb side. The line has to sit outside
|
||||||
|
`PROP_EXCLUSIONS["guest approach lane"]` (x ±1.4), which is what the placement validator enforces —
|
||||||
|
the first attempt put the podium at x 0.4 and the test caught it.
|
||||||
|
|
||||||
|
Readability at 8 m drove the thickness pass: bike-rack frame 22 → 28 mm, bars 10 → 12 mm, velvet rope
|
||||||
|
18 → 22 mm. The west planter also moved 1.6 m clear of the palm canopy, which was clipping its lip.
|
||||||
|
|
||||||
|
Measured at night afterwards: the entrance walkway sits at 20.7/255 against 3.0 on the side pavement,
|
||||||
|
so the door pool exists; the queue area itself is still unlit and stays queued as a follow-up.
|
||||||
|
|
||||||
|
### Hollywood and the TV Studio
|
||||||
|
|
||||||
|
Hollywood had one stanchion and one barricade, which read as stray street furniture; it now has the
|
||||||
|
same three-post rope line into the check-in podium, with a barricade on the curb.
|
||||||
|
|
||||||
|
The TV Studio had **never been validated**: `placement_problems()` was only applied to the Joke Shop
|
||||||
|
in its test, and running it on the studio reported six violations — three props at z 4.2–4.4 sat
|
||||||
|
inside the steps/door exclusion and outside the sidewalk bounds. They are re-placed as a rope line,
|
||||||
|
podium and two curb barricades, and `test_every_venue_placement_clears_the_sidewalk_and_the_approach`
|
||||||
|
now validates all three venues so the gap cannot reopen.
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Asset Documentation
|
||||||
|
|
||||||
|
Asset contracts, pipelines, sourcing and reviews. These pages own the measured evidence; the
|
||||||
|
executable gates are `tools/blender_assets.py inspect|audit|fit`, `tools/npc_proportions.py` and
|
||||||
|
the capture scripts.
|
||||||
|
|
||||||
|
- [`blender-assets.md`](blender-assets.md) — Blender library operation, the character/gear/prop
|
||||||
|
contract, attach points and rebuild discipline.
|
||||||
|
- [`asset-tooling.md`](asset-tooling.md) — image/model pipelines, the optional Blender MCP setup
|
||||||
|
and the screenshot/vision gates.
|
||||||
|
- [`npc-model-sourcing.md`](npc-model-sourcing.md) — character-source evaluation, licence rules,
|
||||||
|
the Quaternius intake and the active NPC plan.
|
||||||
|
- [`3d-model-review.md`](3d-model-review.md) — measurements, per-asset scorecard, ranked gaps and
|
||||||
|
accepted limits.
|
||||||
|
- [`makehuman-head-review.md`](makehuman-head-review.md) — the retained MakeHuman fallback head,
|
||||||
|
its provenance and the review verdict.
|
||||||
|
|
||||||
|
Up: [`../README.md`](../README.md) · Product plan: [`../../PLAN.md`](../../PLAN.md) · Tasks:
|
||||||
|
[`../../TODO.md`](../../TODO.md).
|
||||||
@@ -0,0 +1,730 @@
|
|||||||
|
# Asset Pipeline and Tooling
|
||||||
|
|
||||||
|
Everything the game renders is produced or gated by the scripts below. Architecture-level contracts,
|
||||||
|
data flow and failure fallbacks are summarized in
|
||||||
|
[`architecture.md`](../project/architecture.md#content-and-asset-pipeline-architecture); this page is the
|
||||||
|
operational reference. Development-only outputs (AI concept sheets, Blender sources, raw AI fabrics)
|
||||||
|
are gitignored; the audited game assets are committed.
|
||||||
|
|
||||||
|
## Image Asset Tools
|
||||||
|
|
||||||
|
Install the Pillow dependency used by the existing texture generator and the asset tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m pip install -r requirements-tools.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a role-sized image (generation requires `OPENROUTER_API_KEY`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/asset_pipeline.py generate --prompt "weathered red brick" --role tile --output assets/textures/brick_extra.png
|
||||||
|
```
|
||||||
|
|
||||||
|
Generated images are resized to the selected role profile. Non-square requests use
|
||||||
|
the validated `openai/gpt-5-image-mini` source ratio and crop to the final role dimensions.
|
||||||
|
Transparent glyphs can be recolored while preserving their alpha mask with
|
||||||
|
`--foreground '#ffffff'`.
|
||||||
|
|
||||||
|
Crop uses the image center by default; `--x` and `--y` select an explicit crop origin. Resize defaults to aspect-preserving `contain`; use `--fit cover` to crop or `--fit stretch` to distort to the exact dimensions.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/asset_pipeline.py crop assets/textures/source.png assets/textures/crop.png --width 256 --height 128
|
||||||
|
python3 tools/asset_pipeline.py resize assets/textures/source.png assets/images/scenes/scene.png --width 1920 --height 1080 --fit cover
|
||||||
|
python3 tools/asset_pipeline.py seamless assets/textures/rough_tile.png assets/textures/tile.png
|
||||||
|
```
|
||||||
|
|
||||||
|
Audit accepts image files or directories. `--role auto` infers common roles from paths and filenames; explicit roles include `tile`, `texture`, `icon`, `card`, `banner`, `backdrop`, `background`, `overlay`, `app_icon`, and `screenshot`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/asset_pipeline.py audit assets/textures
|
||||||
|
python3 tools/asset_pipeline.py audit assets/images/scenes --role background --json
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Prop models
|
||||||
|
|
||||||
|
Street furniture (bollards, benches, bins, planters, newspaper boxes, a hydrant)
|
||||||
|
is real 3D geometry generated by `tools/model_pipeline.py` and written to
|
||||||
|
`assets/models/generated/*.glb`. Godot imports each file as a `PackedScene`, and
|
||||||
|
`WorldBuilder` instantiates it with a procedural fallback when the model or its
|
||||||
|
import is missing, so the world still builds on a fresh checkout.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Print the catalogue (parts, triangle count, whether a model/concept exists)
|
||||||
|
python3 tools/model_pipeline.py list
|
||||||
|
|
||||||
|
# Rebuild the models (deterministic, offline) and validate them
|
||||||
|
python3 tools/model_pipeline.py build
|
||||||
|
python3 tools/model_pipeline.py audit
|
||||||
|
|
||||||
|
# Optional: an image model supplies the colour palette per prop
|
||||||
|
# (requires OPENROUTER_API_KEY; the repo .env is read automatically)
|
||||||
|
python3 tools/model_pipeline.py concept
|
||||||
|
python3 tools/model_pipeline.py concept --only bench --force
|
||||||
|
```
|
||||||
|
|
||||||
|
Each prop is a small list of boxes and cylinders, so `build` needs no network and
|
||||||
|
the output is byte-identical between runs. `concept` asks an image model for a
|
||||||
|
studio-style reference; `dominant_colors` and `assign_roles` then map that image's
|
||||||
|
colours onto material roles (darkest → metal, warm saturated → wood, light grey →
|
||||||
|
stone, green → foliage). glTF stores colours in linear space, so the tool converts
|
||||||
|
the sRGB palettes before writing them. Re-run `build` and `godot --headless
|
||||||
|
--path . --import` after changing a prop. See
|
||||||
|
[ADR-0012](../adr/0012-surface-materials-and-generated-props.md).
|
||||||
|
|
||||||
|
Surface materials share one definition in `src/game/materials.gd`: named
|
||||||
|
roughness/metallic profiles per surface, plus the environment tune-up (ACES
|
||||||
|
tonemapping, glow, fog, colour grading) that the phase lighting applies.
|
||||||
|
|
||||||
|
## NPC character model
|
||||||
|
|
||||||
|
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)).
|
||||||
|
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
|
||||||
|
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
|
||||||
|
existing animation code; the procedural rig remains the offline fallback (see
|
||||||
|
[ADR-0013](../adr/0013-blender-authored-npc-characters.md) and
|
||||||
|
[`blender-assets.md`](blender-assets.md)).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Print the frozen contract (slots, bone names, triangle budget)
|
||||||
|
python3 tools/npc_pipeline.py list
|
||||||
|
|
||||||
|
# Rebuild the skinned base mesh + neutral maps in Blender and validate it
|
||||||
|
flatpak run org.blender.Blender --background --python "$PWD/tools/npc_pipeline.py" -- build
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
|
||||||
|
# Layer the AI clothing albedos (shirt/trousers) over the procedural weave, and
|
||||||
|
# derive per-fabric outfit colours for the avatar palette
|
||||||
|
python3 tools/npc_pipeline.py prepare
|
||||||
|
python3 tools/npc_pipeline.py palette
|
||||||
|
```
|
||||||
|
|
||||||
|
`prepare` resizes the AI fabric tiles (kept locally in `assets/textures/npc/ai/`,
|
||||||
|
which is gitignored) onto the 256² `cloth`/`pants` albedos; `build` bakes them into
|
||||||
|
the `.glb` and regenerates the procedural normal + packed ORM maps at the same
|
||||||
|
256² size. Runtime fabric materials repeat the weave 4× (`NPC_FABRIC_UV_SCALE`)
|
||||||
|
and skip the per-character baked AO, which otherwise magnified into large
|
||||||
|
blotches; skin, hair and fixed gear keep AO. `palette` writes
|
||||||
|
`assets/textures/npc/palette.json` using `model_pipeline.dominant_colors`.
|
||||||
|
|
||||||
|
## Blender asset library (loaded by the game)
|
||||||
|
|
||||||
|
`tools/blender_assets.py` models a larger library procedurally in Blender for the
|
||||||
|
v0.6.0 integration: **8 skinned NPC characters** (casual, jacket, coat, hi-vis vest,
|
||||||
|
dress, hoodie, firefighter, guard), each a drop-in for `npc_base.glb` (same bones,
|
||||||
|
joint positions and tint slots); **15 gear pieces** authored at head/hand/back/chest
|
||||||
|
attach points (caps, hood, helmet, backpack, SCBA tank, lanyard, phone, cup,
|
||||||
|
clipboard, umbrella, camera, sign, flashlight, wand, radio, …); and **20 props**
|
||||||
|
(octagonal bollard and bin, slatted bench, hydrant, news box, planters, street lamp,
|
||||||
|
street and palm trees, A-frame sign, wall lantern, cone, crowd barrier, stanchion,
|
||||||
|
handrail, podium, double door, marquee, sedan).
|
||||||
|
|
||||||
|
`NpcAvatar` selects a character by role/seed, tints the five contract slots, and
|
||||||
|
attaches gear at the documented points; `WorldBuilder` loads the library props
|
||||||
|
with colliders/avoidance and drives their emissive parts from the day/night
|
||||||
|
cycle. The single `npc_base.glb` and primitive props remain offline fallbacks.
|
||||||
|
Contract and integration notes: [`blender-assets.md`](blender-assets.md).
|
||||||
|
|
||||||
|
Sourcing and migration plan: [`npc-model-sourcing.md`](npc-model-sourcing.md) records the
|
||||||
|
2026-11-05 direction (ADR-0017). Quaternius Ultimate Modular (CC0) is the primary NPC mesh
|
||||||
|
and animation source, baked to the existing 13-core contract; the procedural generator is
|
||||||
|
the fallback. Kenney/Khronos/MakeHuman remain evaluated references, not active bases.
|
||||||
|
|
||||||
|
| Characters | Props |
|
||||||
|
| --- | --- |
|
||||||
|
|  |  |
|
||||||
|
|
||||||
|
```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 --head mh
|
||||||
|
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 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Quaternius modular parts (outfit variety)
|
||||||
|
|
||||||
|
`tools/quaternius_parts.py` is the intake for the upstream modular pack, which ships as a Google
|
||||||
|
Drive folder and is therefore downloaded once by hand into `tmp/quaternius-intake/` (gitignored):
|
||||||
|
|
||||||
|
| Step | Command | Runs in |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| list what the download contains | `python3 tools/quaternius_parts.py inventory --dir tmp/quaternius-intake` | plain Python (parses the GLB container) |
|
||||||
|
| cut one mesh out as a part | `... extract --file <glb> --mesh <name> --part <name>` | Blender (rewrites the skin) |
|
||||||
|
| hash and validate the kept parts | `... verify --dir assets/models/blender_src/quaternius/parts` | plain Python |
|
||||||
|
|
||||||
|
A part is one skinned mesh on the 62-joint rig; the contract and the join behaviour are in
|
||||||
|
`assets/models/blender_src/quaternius/parts/README.md`. `build` joins a role's
|
||||||
|
`CHARACTERS[...]["parts"]` onto its base body before the slot map, keeping the part's weights and
|
||||||
|
dropping its redundant armature. The pack is never committed — only the parts that are used, with
|
||||||
|
their SHA-256 in `CREDITS.md`.
|
||||||
|
|
||||||
|
## Local map development (planned)
|
||||||
|
|
||||||
|
The local map workstreams (H–M) in [`TODO.md`](../../TODO.md#3d-model-and-local-map-re-evaluation) are
|
||||||
|
planned, not implemented; there are no map-generation commands yet. The runtime
|
||||||
|
contract, provenance rules, budget/navigation gates and fallback are in
|
||||||
|
[`architecture.md#venue-implementation-contract`](../project/architecture.md#venue-implementation-contract).
|
||||||
|
The detailed pipeline/task list lives in `TODO.md`.
|
||||||
|
|
||||||
|
## Confirmed game assets
|
||||||
|
|
||||||
|
Everything below is audited and shipped in the game; development-only outputs
|
||||||
|
(AI concept sheets, Blender sources, raw AI fabrics) are gitignored.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  |  |
|
||||||
|
|  |  |
|
||||||
|
|  |  |
|
||||||
|
|
||||||
|
- **Surface textures:** the v2 picks above (brick, sidewalk pavers, dry and wet asphalt, awning canvas) plus their normal maps, loaded by `src/game/materials.gd` and `WorldBuilder`; the three skyline panoramas are switched per shift phase. The first-generation tiles and `assets/textures/skyline.png` are no longer loaded.
|
||||||
|
- **UI art:** `assets/textures/ai/game_ready/icons/` (flashlight, door lock, wand, REC) and `assets/textures/ai/game_ready/cards/` (ID, wristband, ticket, credential, guest list), loaded by `src/game/ui/hud.gd`.
|
||||||
|
- **Generated models:** `assets/models/generated/*.glb` — bollard, bench, trash bin, planter, newspaper box, hydrant, and the NPC base — plus their colour atlases.
|
||||||
|
- **NPC material maps:** `assets/textures/npc/*_{albedo,normal,orm}.png` and `palette.json`.
|
||||||
|
|
||||||
|
Audit them all:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready
|
||||||
|
python3 tools/model_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
|
||||||
|
|
||||||
|
The skyline, surface tiles and phase lighting were rebuilt against AI concept frames. The
|
||||||
|
concepts live in [`docs/screens/concepts/`](../screens/concepts/README.md); the validated
|
||||||
|
picks the game loads are in `assets/textures/ai/game_ready/v2/picks/` (day/dusk/night
|
||||||
|
panoramas, brick, sidewalk pavers, dry and wet asphalt, awning canvas, each with a normal
|
||||||
|
map).
|
||||||
|
|
||||||
|
`tools/scene_concepts.py` generates and processes new candidates, promotes the validated ones
|
||||||
|
to stable names, audits them, and scores captures against the concepts. It never overwrites an
|
||||||
|
image: new files get a `-vN` suffix, and `process` skips existing candidates unless `--new`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/scene_concepts.py generate --group scene # or backdrop / texture, --variants N
|
||||||
|
python3 tools/scene_concepts.py process && python3 tools/scene_concepts.py audit
|
||||||
|
python3 tools/scene_concepts.py promote # validated picks -> v2/picks/
|
||||||
|
godot --headless --path . --import
|
||||||
|
|
||||||
|
# A/B a candidate without touching game code: the same cameras, textures swapped at runtime.
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd -- --out=res://docs/screens/realism/<run>/after
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd -- --out=res://docs/screens/realism/<run>/try \
|
||||||
|
--swap=res://assets/textures/ai/game_ready/v2/picks/brick_facade.png=res://assets/textures/ai/game_ready/v2/brick_facade-v2.png
|
||||||
|
|
||||||
|
# Score before/after against the concepts (Pillow detail metrics + vision judge).
|
||||||
|
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> --judge \
|
||||||
|
--model google/gemini-2.5-flash --repeats 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Judges default to `--model auto`: `tools/openrouter_judge.py` reads the live
|
||||||
|
OpenRouter catalogue, keeps image-input models that support `response_format`,
|
||||||
|
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
|
||||||
|
`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
|
||||||
|
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
|
||||||
|
the review-only `NpcAvatar.character_override` to include the guard model, which
|
||||||
|
has no spawned NPC role:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_npc_review.gd -- \
|
||||||
|
--out=res://tmp/npc-review
|
||||||
|
```
|
||||||
|
|
||||||
|
Contact sheets can be assembled from the files (the output is local QA and is
|
||||||
|
not committed). The runtime applies small attach-space offsets to the lanyard
|
||||||
|
and firefighter helmet so the badge clears the jacket and the brim does not sit
|
||||||
|
over the eyes; these are `NpcAvatar.GEAR_OFFSETS`, not changes to the gear
|
||||||
|
contract.
|
||||||
|
|
||||||
|
`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
|
||||||
|
[`3d-model-review.md`](3d-model-review.md#character-measurements). It reads the exported GLBs
|
||||||
|
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 ×
|
||||||
|
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
|
||||||
|
`tmp/`; `tools/npc_baseline.py sheet` builds the committed contact sheet, and
|
||||||
|
`tools/npc_baseline.py compare --before DIR --after DIR` builds the review
|
||||||
|
comparison after a model change. See
|
||||||
|
[`3d-model-review.md`](3d-model-review.md).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd -- \
|
||||||
|
--out=res://tmp/npc-baseline
|
||||||
|
python3 tools/npc_baseline.py sheet --dir tmp/npc-baseline
|
||||||
|
```
|
||||||
|
|
||||||
|
`capture_realism.gd` runs nine stages in this order: day, Hollywood surface close-ups,
|
||||||
|
portrait, evening, late, Joke Shop day, Joke Shop surface close-ups, Joke Shop night,
|
||||||
|
studio; `--stages=day,late` limits it while tuning; `--flashlight` turns on the guard
|
||||||
|
lamp for night stages (used by the Desktop HQ A/B). The `portrait` shot freezes one
|
||||||
|
NPC on open sidewalk and frames the face, which is the only capture that can judge
|
||||||
|
avatar detail (`--portrait-type=N` picks a role). For the 0.9.5 texture audit, run
|
||||||
|
the surface stages directly; the HUD is hidden for them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd \
|
||||||
|
-- --out=res://tmp/surfaces --stages=surfaces,surfaces_joke
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated realism runs under
|
||||||
|
`docs/screens/realism/` are **local, gitignored QA artifacts**; commit only the
|
||||||
|
resulting decisions/docs, not the run folders. The 0.9.5 texture/UI audit checked actual screen sizes: all screen art is
|
||||||
|
1280×720, access cards are 256×128 and HUD icons are 64×64; no runtime texture
|
||||||
|
was oversized or unused. The 24-NPC smoke now reports 168 meshes / 131,616
|
||||||
|
triangles / 6.94 ms. Compressed payloads are 24.55 MB of surface picks, 22.67 MB
|
||||||
|
of screen art and 0.48 MB of NPC maps.
|
||||||
|
|
||||||
|
The 0.9.5 realism judge ran once per shot with `z-ai/glm-5.3-flash`
|
||||||
|
(`python3 tools/scene_concepts.py realism --run docs/screens/realism/2026-11-02-tex
|
||||||
|
--judge`). Its mean `after` scores were overall 1.8, lighting 2.3, materials 1.8,
|
||||||
|
texture detail 1.2, backdrop 2.0 and characters 1.2; the Pillow metric gap was
|
||||||
|
0.306. The judge still ranks the painted concept targets above the stylized build,
|
||||||
|
particularly lit night facades and dense backdrop detail. That is expected: the
|
||||||
|
pass validated close-range surfaces and promoted the texture repairs, not a
|
||||||
|
photoreal renderer. The 0.9.5 before/after close-up sheet is
|
||||||
|
[`blender_previews/sheet_texture_refinement_0.9.5.jpg`](../blender_previews/sheet_texture_refinement_0.9.5.jpg);
|
||||||
|
the texture-adoption run itself was the earlier 0.8-style candidate comparison,
|
||||||
|
and its report remains local/gitignored.
|
||||||
|
|
||||||
|
For the 0.9.6 integration checkpoint the same judge was pinned to
|
||||||
|
`z-ai/glm-5.3-flash` and run once per shot on the 10-shot set: mean overall 2.1,
|
||||||
|
lighting 2.4, materials 2.1, texture 1.7, backdrop 2.3, characters 1.8, Pillow
|
||||||
|
gap 0.291. No `review` verdicts were produced by the screen judge, so there was
|
||||||
|
no manual layout verdict to convert into a fix; the remaining realism comments
|
||||||
|
are the accepted stylized gaps.
|
||||||
|
|
||||||
|
Build release artifacts from a clean checkout or detached worktree. A normal
|
||||||
|
working tree may contain ignored `tmp/` captures and old `build/` artifacts, and
|
||||||
|
Godot can pack enough of those into exports to inflate a 0.9.6 package from
|
||||||
|
~160 MB to 1.6–3.1 GB. The 0.9.6 clean worktree produced 161.5 MB Linux,
|
||||||
|
197.1 MB Windows, 148.0 MB macOS, 97.8 MB Web and 241.5 MB Desktop HQ.
|
||||||
|
|
||||||
|
The Forward+ A/B compared the Compatibility entrance with the optional Desktop HQ
|
||||||
|
preset. The gear-fit run checked the sunglasses, bag and helmet against measured
|
||||||
|
head/skirt bounds with `python3 tools/blender_assets.py fit`; it also fixed the
|
||||||
|
portrait yaw and hid the focus overlay so the close-up shows the avatar.
|
||||||
|
|
||||||
|
Regenerate a run locally with the capture and `realism --run ...` commands above;
|
||||||
|
the reports and sheets are intentionally not part of the release repository.
|
||||||
|
|
||||||
|
## Screen design references
|
||||||
|
|
||||||
|
Screen mockups and screenshot comparison use `tools/screen_designs.py`. It turns the
|
||||||
|
screen descriptions in `docs/` (README captions, ADRs 0007–0010, `PLAN.md`, and
|
||||||
|
`architecture.md`) into runtime reference art under `assets/images/ui/` (with a
|
||||||
|
reference-only snapshot in `docs/screens/designs/`), then uses those images as
|
||||||
|
the expected state to check the game's captures.
|
||||||
|
|
||||||
|
Each screen is tagged `scene` (a realistic first-person 3D world view) or `ui`
|
||||||
|
(an in-game panel overlaid on a softened scene), and the generator appends a
|
||||||
|
shared art-direction suffix so the frames read as one coherent game. Requests use
|
||||||
|
a 3:2 landscape source and crop to the 16:9 capture dimensions.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Export the built-in scenarios to editor-authorable .tres resources
|
||||||
|
godot --headless --path . --script res://tools/export_venues.gd
|
||||||
|
|
||||||
|
# List the catalogue (name, kind, whether a reference design exists)
|
||||||
|
python3 tools/screen_designs.py list
|
||||||
|
|
||||||
|
# Deterministic wireframes (offline, no API key); existing designs are kept
|
||||||
|
# unless --force is passed, so an offline run never replaces an AI mockup
|
||||||
|
python3 tools/screen_designs.py generate --draft
|
||||||
|
python3 tools/screen_designs.py generate --draft --only wand --force
|
||||||
|
|
||||||
|
# Realistic 3D scenes and in-game screen designs via OpenRouter
|
||||||
|
# (requires OPENROUTER_API_KEY)
|
||||||
|
python3 tools/screen_designs.py generate
|
||||||
|
|
||||||
|
# UI-state gallery (menu, pause, settings, review, radio, wheel, crowd, responder)
|
||||||
|
godot --path . --script res://src/test/capture_ui_states.gd -- --out=res://docs/screenshots
|
||||||
|
|
||||||
|
# Prop gallery (studio line-up) for close-range prop checks
|
||||||
|
godot --path . --script res://src/test/capture_prop_gallery.gd
|
||||||
|
|
||||||
|
# Compare docs/screenshots/*.png against the designs
|
||||||
|
python3 tools/screen_designs.py compare
|
||||||
|
python3 tools/screen_designs.py compare --json
|
||||||
|
|
||||||
|
# Ask a vision model whether each capture realises its design's screen and layout
|
||||||
|
# (requires OPENROUTER_API_KEY; the repo .env is read automatically)
|
||||||
|
python3 tools/screen_designs.py judge
|
||||||
|
python3 tools/screen_designs.py judge --model openai/gpt-4o-mini --json
|
||||||
|
python3 tools/screen_designs.py judge --only 03- # one capture
|
||||||
|
python3 tools/screen_designs.py judge --dry-run # build requests, no network
|
||||||
|
```
|
||||||
|
|
||||||
|
The catalogue covers 21 screens: the thirteen scene captures plus the start menu,
|
||||||
|
pause overlay, settings, shift review, radial menu, emergency radio, crowd panic,
|
||||||
|
and responder arrival. All 21 have an AI reference in `assets/images/ui/`
|
||||||
|
(snapshot in `docs/screens/designs/`) and a committed capture, so every entry is
|
||||||
|
compared or judged against the running game.
|
||||||
|
|
||||||
|
`compare` flags gross drift: wrong aspect ratio, undersized frame, near-blank
|
||||||
|
frame, and large layout/palette/detail distance. It is a regression gate, not a
|
||||||
|
semantic judge — pixel and perceptual metrics cannot decide whether a frame's
|
||||||
|
composition is *right*.
|
||||||
|
|
||||||
|
`judge` covers that gap: it sends each capture together with its concept mockup to
|
||||||
|
a vision model and asks whether the capture realises the same screen and layout
|
||||||
|
intent. Art style, fidelity, fonts, and placeholder text are explicitly ignored.
|
||||||
|
Verdicts are classed as `match`, `review` (a mismatch below `--fail-confidence`,
|
||||||
|
default 0.6 — expected when a concept mockup is more stylised than the game), or
|
||||||
|
`diff` (a confident mismatch, which exits non-zero). Regenerate the designs after changing screen layout, then re-run
|
||||||
|
`compare` after regenerating the game screenshots with
|
||||||
|
`src/test/capture_screenshots.gd` and `src/test/capture_ui_states.gd`.
|
||||||
|
|
||||||
|
For a one-command drift check, run `tools/check_screenshots.sh`. It renders the
|
||||||
|
scene captures, UI-state captures and prop gallery into `docs/screenshots/` (using
|
||||||
|
`xvfb-run` when no display is available) and then runs `screen_designs.py compare`.
|
||||||