diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fced3b..8c5f52a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Semantic Versioning. reading as a thin post on a hanger. - Character eyes sit in the socket behind a thin upper lid, and the chin and cheekbones are sculpted into the head instead of reading as separate spheres. +- Character torsos taper from the chest to the waist and flare slightly into + the hips, so shirts and jackets sit less like straight tubes. - Tourists no longer appear to carry two backpacks: the photo-pose camera bag is hidden except while they are actually taking a picture. - Backpack shoulder straps are now flattened bands that sit outside the chest diff --git a/MEMORY.md b/MEMORY.md index db329fe..b2e8477 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -86,6 +86,10 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o (`_shape_head_mesh`) around a chin plate and cheekbones. This avoids dark AO intersection rings. Moved lips forward to stay on the reshaped surface. Latest review sheet: `sheet_npc_baseline_compare-face.jpg`; max character is now 4,754 tris / 4,574 verts. +- **NPC torso/hip transition (0.9.4, 2026-11-02):** pelvis radius 0.165→0.168, waist + 0.150→0.146, chest 0.170→0.168, upper chest 0.172→0.170. This is a subtle shirt taper, not a + skeleton change; all proportions pass and `sheet_npc_baseline_compare-torso.jpg` records it. + Max character is 4,754 tris / 4,584 verts. - **UI flow smoke (2026-11-02):** `smoke_ui_flows.gd` boots the real scene and exercises inspection allow/deny, normal vs emergency radio, Help filtering, wand allow/deny and review return-to-menu. Keep this smoke in the 0.9.6 diff --git a/TODO.md b/TODO.md index c17ee37..835d763 100644 --- a/TODO.md +++ b/TODO.md @@ -89,8 +89,9 @@ and equipment fit. - [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; all eight rebuilt and the body comparison sheet is in `docs/blender_previews/`.) -- [ ] Improve the torso and hip transition; preserve the existing broad/slim/average build - variations. +- [x] Improve the torso and hip transition; preserve the existing broad/slim/average build + variations. (Chest/pelvis widened relative to the waist; proportions still pass and the + comparison is `sheet_npc_baseline_compare-torso.jpg`.) - [ ] Add subtle body-shape variation between variants beyond uniform scaling (shoulder slope, torso taper, hip width, limb thickness) while staying inside the triangle/vertex budget. - [ ] Check the procedural fallback and `npc_base` silhouette against the library so the fallback diff --git a/assets/models/blender/manifest.json b/assets/models/blender/manifest.json index 5fc897c..f9a60a5 100644 --- a/assets/models/blender/manifest.json +++ b/assets/models/blender/manifest.json @@ -487,8 +487,8 @@ "accessory", "hair" ], - "triangles": 3760, - "vertices": 3556 + "triangles": 3762, + "vertices": 3474 }, "npc_coat": { "bounds_godot": { @@ -529,7 +529,7 @@ "hair" ], "triangles": 4754, - "vertices": 4574 + "vertices": 4584 }, "npc_dress": { "bounds_godot": { @@ -567,7 +567,7 @@ "accessory", "hair" ], - "triangles": 4176, + "triangles": 4180, "vertices": 3863 }, "npc_firefighter": { @@ -609,7 +609,7 @@ "hair" ], "triangles": 4624, - "vertices": 4487 + "vertices": 4583 }, "npc_guard": { "bounds_godot": { @@ -652,8 +652,8 @@ "accessory", "hair" ], - "triangles": 4334, - "vertices": 4456 + "triangles": 4326, + "vertices": 4443 }, "npc_hoodie": { "bounds_godot": { @@ -693,8 +693,8 @@ "accessory", "hair" ], - "triangles": 4592, - "vertices": 4187 + "triangles": 4584, + "vertices": 4294 }, "npc_jacket": { "bounds_godot": { @@ -734,8 +734,8 @@ "accessory", "hair" ], - "triangles": 4160, - "vertices": 3930 + "triangles": 4152, + "vertices": 3900 }, "npc_vest": { "bounds_godot": { @@ -775,8 +775,8 @@ "accessory", "hair" ], - "triangles": 4272, - "vertices": 4186 + "triangles": 4260, + "vertices": 4183 }, "palm_tree": { "bounds_godot": { diff --git a/assets/models/blender/npc/npc_casual.glb b/assets/models/blender/npc/npc_casual.glb index 637e2c7..ed87176 100644 Binary files a/assets/models/blender/npc/npc_casual.glb and b/assets/models/blender/npc/npc_casual.glb differ diff --git a/assets/models/blender/npc/npc_casual_ao.png b/assets/models/blender/npc/npc_casual_ao.png index 092be57..1bd500e 100644 Binary files a/assets/models/blender/npc/npc_casual_ao.png and b/assets/models/blender/npc/npc_casual_ao.png differ diff --git a/assets/models/blender/npc/npc_coat.glb b/assets/models/blender/npc/npc_coat.glb index 068e129..fd922da 100644 Binary files a/assets/models/blender/npc/npc_coat.glb and b/assets/models/blender/npc/npc_coat.glb differ diff --git a/assets/models/blender/npc/npc_coat_ao.png b/assets/models/blender/npc/npc_coat_ao.png index 876e346..cfc3528 100644 Binary files a/assets/models/blender/npc/npc_coat_ao.png and b/assets/models/blender/npc/npc_coat_ao.png differ diff --git a/assets/models/blender/npc/npc_dress.glb b/assets/models/blender/npc/npc_dress.glb index 54c1d60..d517058 100644 Binary files a/assets/models/blender/npc/npc_dress.glb and b/assets/models/blender/npc/npc_dress.glb differ diff --git a/assets/models/blender/npc/npc_dress_ao.png b/assets/models/blender/npc/npc_dress_ao.png index 5e3cb52..c666ed0 100644 Binary files a/assets/models/blender/npc/npc_dress_ao.png and b/assets/models/blender/npc/npc_dress_ao.png differ diff --git a/assets/models/blender/npc/npc_firefighter.glb b/assets/models/blender/npc/npc_firefighter.glb index cfac41f..a29192f 100644 Binary files a/assets/models/blender/npc/npc_firefighter.glb and b/assets/models/blender/npc/npc_firefighter.glb differ diff --git a/assets/models/blender/npc/npc_firefighter_ao.png b/assets/models/blender/npc/npc_firefighter_ao.png index 49f5b28..ce44bcd 100644 Binary files a/assets/models/blender/npc/npc_firefighter_ao.png and b/assets/models/blender/npc/npc_firefighter_ao.png differ diff --git a/assets/models/blender/npc/npc_guard.glb b/assets/models/blender/npc/npc_guard.glb index 762c67d..5f9afbc 100644 Binary files a/assets/models/blender/npc/npc_guard.glb and b/assets/models/blender/npc/npc_guard.glb differ diff --git a/assets/models/blender/npc/npc_guard_ao.png b/assets/models/blender/npc/npc_guard_ao.png index 5cac8f1..20b2e46 100644 Binary files a/assets/models/blender/npc/npc_guard_ao.png and b/assets/models/blender/npc/npc_guard_ao.png differ diff --git a/assets/models/blender/npc/npc_hoodie.glb b/assets/models/blender/npc/npc_hoodie.glb index 5e0b34d..184b216 100644 Binary files a/assets/models/blender/npc/npc_hoodie.glb and b/assets/models/blender/npc/npc_hoodie.glb differ diff --git a/assets/models/blender/npc/npc_hoodie_ao.png b/assets/models/blender/npc/npc_hoodie_ao.png index e35fcc8..d35870b 100644 Binary files a/assets/models/blender/npc/npc_hoodie_ao.png and b/assets/models/blender/npc/npc_hoodie_ao.png differ diff --git a/assets/models/blender/npc/npc_jacket.glb b/assets/models/blender/npc/npc_jacket.glb index dcc6e78..889a093 100644 Binary files a/assets/models/blender/npc/npc_jacket.glb and b/assets/models/blender/npc/npc_jacket.glb differ diff --git a/assets/models/blender/npc/npc_jacket_ao.png b/assets/models/blender/npc/npc_jacket_ao.png index f3aa10c..8e444b2 100644 Binary files a/assets/models/blender/npc/npc_jacket_ao.png and b/assets/models/blender/npc/npc_jacket_ao.png differ diff --git a/assets/models/blender/npc/npc_vest.glb b/assets/models/blender/npc/npc_vest.glb index 3c9db1f..1e8204a 100644 Binary files a/assets/models/blender/npc/npc_vest.glb and b/assets/models/blender/npc/npc_vest.glb differ diff --git a/assets/models/blender/npc/npc_vest_ao.png b/assets/models/blender/npc/npc_vest_ao.png index bf69ba4..dec6029 100644 Binary files a/assets/models/blender/npc/npc_vest_ao.png and b/assets/models/blender/npc/npc_vest_ao.png differ diff --git a/docs/blender-assets.md b/docs/blender-assets.md index 44b85c5..853b9e1 100644 --- a/docs/blender-assets.md +++ b/docs/blender-assets.md @@ -94,8 +94,8 @@ All eight use the `npc_base` contract, so each one can replace `Materials.npc_slot()` tints them the same way it tints `npc_base`. - budget ≤ 6000 triangles and ≤ 5000 vertices (glTF vertex count, after seam splits; `CHAR_MAX_VERTS` in `tools/blender_assets.py`, raised from 4000 for - the 2026-11-01 face/hand pass). The current range is 3,760–4,754 triangles - and 3,556–4,574 vertices. + the 2026-11-01 face/hand pass). The current range is 3,762–4,754 triangles + and 3,474–4,584 vertices. Construction: the torso and limbs are one continuous Skin-modifier mesh, subdivided twice, decimated to 26 % with X symmetry, and cut at the waist, cuffs, diff --git a/docs/blender_previews/README.md b/docs/blender_previews/README.md index d0b40ff..7f2ded9 100644 --- a/docs/blender_previews/README.md +++ b/docs/blender_previews/README.md @@ -47,6 +47,7 @@ Committed contact sheets and local Blender renders for the character, gear and p | sheet_npc_baseline_compare-p1.jpg | [`sheet_npc_baseline_compare-p1.jpg`](sheet_npc_baseline_compare-p1.jpg) | | sheet_npc_baseline_compare-body.jpg | [`sheet_npc_baseline_compare-body.jpg`](sheet_npc_baseline_compare-body.jpg) | | sheet_npc_baseline_compare-face.jpg | [`sheet_npc_baseline_compare-face.jpg`](sheet_npc_baseline_compare-face.jpg) | +| sheet_npc_baseline_compare-torso.jpg | [`sheet_npc_baseline_compare-torso.jpg`](sheet_npc_baseline_compare-torso.jpg) | | sheet_npc_gear.jpg | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) | | sheet_props.jpg | [`sheet_props.jpg`](sheet_props.jpg) | | shoulder_bag.png | [`shoulder_bag.png`](shoulder_bag.png) | diff --git a/docs/blender_previews/sheet_npc_baseline_compare-torso.jpg b/docs/blender_previews/sheet_npc_baseline_compare-torso.jpg new file mode 100644 index 0000000..c55d8d0 Binary files /dev/null and b/docs/blender_previews/sheet_npc_baseline_compare-torso.jpg differ diff --git a/docs/development-status.md b/docs/development-status.md index 6aaec55..7c5b9fd 100644 --- a/docs/development-status.md +++ b/docs/development-status.md @@ -41,6 +41,7 @@ Implemented so far: - Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing. The wand panel lists the guest's carried items, and multiline HUD/wand strings render real line breaks. - NPC neck/shoulder transition (0.9.4 work): the body graph now uses a fuller neck base, a slightly tapered neck top and a lower shoulder root, giving the head/neck/shoulder line a trapezius slope instead of a thin post. The full character library was rebuilt with a final degenerate-face cleanup; the baseline comparison is `sheet_npc_baseline_compare-body.jpg`. - NPC face-depth pass (0.9.4 work): sclera are recessed and capped by a thin upper lid, and the separate jaw/cheek spheres are replaced by a chin plate and cheekbones sculpted into the head mesh. The full library was rebuilt; the comparison is `sheet_npc_baseline_compare-face.jpg`. +- NPC torso/hip transition (0.9.4 work): the pelvis and chest are slightly wider than the waist, giving shirts and jackets a readable taper into the hips without changing the build multipliers or the skeleton. The comparison is `sheet_npc_baseline_compare-torso.jpg`. - NPC proportion audit (0.9.4 work): `tools/npc_proportions.py` measures all eight GLBs against a stylized height/head/shoulder/hip/torso/leg/arm/foot guide. All variants pass; the head and broad-build silhouettes are the nearest upper bounds. Results are in [`npc-proportion-audit.md`](npc-proportion-audit.md). - NPC P1 portrait fixes (0.9.4 work): tourists now hide their photo-pose camera bag outside `photo`/`film` poses, removing the duplicate-backpack silhouette; the backpack gear was rebuilt with flattened, forward chest straps and a fresh AO map. The before/after sheet is [`blender_previews/sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg); the face and remaining clothing/hand work stay open. - NPC visual baseline (0.9.4 work): `src/test/capture_npc_baseline.gd` captures all eight library characters from front, three-quarter, profile, back, full-body and crowd views in a neutral studio (48 frames). The criteria, measurements and ranked gaps are in [`npc-visual-review.md`](npc-visual-review.md); the committed sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). No model changes were made in this baseline step. diff --git a/docs/npc-proportion-audit.md b/docs/npc-proportion-audit.md index a054528..e6030dc 100644 --- a/docs/npc-proportion-audit.md +++ b/docs/npc-proportion-audit.md @@ -50,11 +50,11 @@ than the slim build. | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | | `npc_casual` | 1.824 m | 0.190 | 0.907 | 0.321 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | | `npc_jacket` | 1.824 m | 0.190 | 0.907 | 0.333 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_coat` | 1.824 m | 0.190 | 0.907 | 0.334 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_vest` | 1.819 m | 0.191 | 0.907 | 0.322 | 0.210 | 0.308 | 0.451 | 0.330 | 0.144 | PASS | +| `npc_coat` | 1.824 m | 0.190 | 0.907 | 0.328 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | +| `npc_vest` | 1.819 m | 0.191 | 0.907 | 0.322 | 0.209 | 0.308 | 0.451 | 0.330 | 0.144 | PASS | | `npc_dress` | 1.825 m | 0.190 | 0.907 | 0.289 | 0.191 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_hoodie` | 1.856 m | 0.187 | 0.907 | 0.328 | 0.205 | 0.302 | 0.442 | 0.323 | 0.141 | PASS | -| `npc_firefighter` | 1.819 m | 0.191 | 0.907 | 0.358 | 0.225 | 0.308 | 0.451 | 0.330 | 0.148 | PASS | +| `npc_hoodie` | 1.856 m | 0.187 | 0.907 | 0.328 | 0.206 | 0.302 | 0.442 | 0.323 | 0.141 | PASS | +| `npc_firefighter` | 1.819 m | 0.191 | 0.907 | 0.358 | 0.223 | 0.308 | 0.451 | 0.330 | 0.148 | PASS | | `npc_guard` | 1.819 m | 0.191 | 0.907 | 0.369 | 0.219 | 0.308 | 0.451 | 0.330 | 0.144 | PASS | No variant is outside a band, so the body pass does not need a global skeleton @@ -63,7 +63,7 @@ or mesh rescale. The values closest to a boundary are: - **head height 0.190** against the 0.22 upper bound, and **head aspect 0.907** against 1.00 — the head is deliberately large. Any head-size change must be coordinated with the face/headgear pass and its gear-fit measurements. -- **guard shoulder 0.369** and **firefighter hip 0.225** against the broad-build +- **guard shoulder 0.369** and **firefighter hip 0.223** against the broad-build upper bounds — these are the intended uniform/turnout silhouettes, not outliers. - **leg length 0.449** and **torso 0.307** sit inside the guide for all variants; diff --git a/docs/npc-visual-review.md b/docs/npc-visual-review.md index 66e32f0..304fe23 100644 --- a/docs/npc-visual-review.md +++ b/docs/npc-visual-review.md @@ -1,12 +1,12 @@ # NPC visual baseline and acceptance criteria Status: baseline captured 2026-11-02. The first P1 fixes (tourist photo-prop -visibility, backpack straps), the neck/shoulder transition and the face-depth -pass are applied. Remaining body/clothing work is open; the baseline remains the -reference for 0.9.4. Comparison sheets: +visibility, backpack straps), the neck/shoulder and torso/hip transitions and +the face-depth pass are applied. Remaining hand/clothing work is open; the +baseline remains the reference for 0.9.4. Comparison sheets: [`sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg), [`sheet_npc_baseline_compare-body.jpg`](blender_previews/sheet_npc_baseline_compare-body.jpg) -and the latest [`sheet_npc_baseline_compare-face.jpg`](blender_previews/sheet_npc_baseline_compare-face.jpg). +and the latest [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg). ## What was captured @@ -100,17 +100,17 @@ From `python3 tools/blender_assets.py fit` and the manifest: | Character | Tris | Verts | Head width | Clothing radius | Notable gear clearances | | --- | ---: | ---: | ---: | ---: | --- | -| `npc_casual` | 3,760 | 3,556 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | -| `npc_jacket` | 4,160 | 3,930 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | -| `npc_coat` | 4,754 | 4,574 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | -| `npc_vest` | 4,272 | 4,186 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | -| `npc_dress` | 4,176 | 3,863 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | -| `npc_hoodie` | 4,592 | 4,187 | 0.315 m | 0.190 m (hip) | — | -| `npc_firefighter` | 4,624 | 4,487 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | -| `npc_guard` | 4,334 | 4,456 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | +| `npc_casual` | 3,762 | 3,474 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | +| `npc_jacket` | 4,152 | 3,900 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | +| `npc_coat` | 4,754 | 4,584 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | +| `npc_vest` | 4,260 | 4,183 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | +| `npc_dress` | 4,180 | 3,863 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | +| `npc_hoodie` | 4,584 | 4,294 | 0.315 m | 0.190 m (hip) | — | +| `npc_firefighter` | 4,624 | 4,583 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | +| `npc_guard` | 4,326 | 4,443 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | -The widest character is `npc_coat` at 4,754 triangles and 4,574 vertices, so the -current headroom is 1,246 triangles and 426 vertices per character. Any hand or +The widest character is `npc_coat` at 4,754 triangles and 4,584 vertices, so the +current headroom is 1,246 triangles and 416 vertices per character. Any hand or clothing refinement must come out of that budget or replace existing detail. The refit backpack measures 380 triangles / 406 vertices. @@ -188,12 +188,14 @@ The committed runs are [`sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg), [`sheet_npc_baseline_compare-body.jpg`](blender_previews/sheet_npc_baseline_compare-body.jpg) and [`sheet_npc_baseline_compare-face.jpg`](blender_previews/sheet_npc_baseline_compare-face.jpg). +The transition pass is [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg). Keep the baseline sheet as the before artifact and add a new comparison sheet rather than replacing it. ## Next implementation order -1. Improve the torso/hip transition and the remaining body-shape variation. +1. Add the remaining body-shape variation between variants and re-measure the + LOD/impostor silhouettes. 2. Add a conversation-distance hand/grip capture and fix hand geometry before judging hand items. 3. Audit hems and footwear in walk/sit/block poses. diff --git a/tools/blender_assets.py b/tools/blender_assets.py index b152c32..aa9c74f 100644 --- a/tools/blender_assets.py +++ b/tools/blender_assets.py @@ -531,10 +531,10 @@ def _skin_body(spec): "guard": 0.05}.get(layer, 0.0) leg = 1.0 + {"responder": 0.1, "dress": -0.1}.get(layer, 0.0) nodes = [ # (pos, radius_x, radius_y) - ((0, 0, 0.80), 0.165 * w, 0.115 * d), # 0 pelvis - ((0, 0, 0.96), 0.150 * w * bulk, 0.105 * d * bulk), # 1 waist - ((0, 0.0, 1.18), 0.170 * w * bulk, 0.115 * d * bulk), # 2 chest - ((0, 0.0, 1.355), 0.172 * w * bulk, 0.108 * d * bulk), # 3 upper chest + ((0, 0, 0.80), 0.168 * w, 0.116 * d), # 0 pelvis + ((0, 0, 0.96), 0.146 * w * bulk, 0.103 * d * bulk), # 1 waist + ((0, 0.0, 1.18), 0.168 * w * bulk, 0.114 * d * bulk), # 2 chest + ((0, 0.0, 1.355), 0.170 * w * bulk, 0.108 * d * bulk), # 3 upper chest # A slightly fuller neck base and a lower shoulder root give the # head/neck/shoulder line a trapezius slope instead of a thin post on # a hanger. The head and collar still sit above this band.