diff --git a/CHANGELOG.md b/CHANGELOG.md index 848343f..a6a9c6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ Semantic Versioning. rounded slot, and the ears have a darker inner concha. - Hand-held items now sit forward in the open palm instead of being partly buried inside the hand. +- Shoes and responder boots now have rounded toes and heels instead of reading + as flat blocks. ### Fixed diff --git a/MEMORY.md b/MEMORY.md index 1272df4..3994e03 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -96,6 +96,10 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o 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. +- **NPC footwear pass (0.9.4, 2026-11-02):** added `ShoeToe`/`ShoeHeel` spheres to the casual shoe + and `BootToe`/`BootHeel` to responder boots; both stay above the sole/boot contact plane. Current + counts are 4,482–5,474 tris / 4,260–5,494 verts under 8,000/8,000. Sheet: + `sheet_npc_baseline_compare-shoes.jpg`. - **NPC hand-item forward offset (0.9.4, 2026-11-02):** `NpcAvatar.HAND_ITEM_FORWARD = Vector3(0,0,0.04)` is added in `_attach_gear()` for every `GEAR_ATTACH` hand item after the head/chest offsets. Hand gear is authored around the wrist proxy; without this, phone, diff --git a/TODO.md b/TODO.md index efeb192..d3860fe 100644 --- a/TODO.md +++ b/TODO.md @@ -131,8 +131,9 @@ and equipment fit. sheet `sheet_npc_hands_grip_compare.jpg` and regression assertions.) - [ ] Add finger curl/thumb contact for the hand items and judge the umbrella/sign grips against the new palm/finger geometry. -- [ ] Audit shoes/boots for silhouette, grounding and contact with the floor; preserve the - responder and firefighter footwear language. +- [x] Audit shoes/boots for silhouette, grounding and contact with the floor; preserve the + responder and firefighter footwear language. (Rounded toe/heel caps; boots keep the black + rubber language; all eight rebuilt, grounding and proportions still pass.) - [ ] Improve clothing layer transitions at collars, cuffs, plackets, waistbands, pockets, badges and reflective bands; remove obvious z-fighting or floating detail. - [ ] Add believable fabric folds/wrinkles at elbows, knees, waist and hems where the extra diff --git a/assets/models/blender/manifest.json b/assets/models/blender/manifest.json index d2c64a7..641856a 100644 --- a/assets/models/blender/manifest.json +++ b/assets/models/blender/manifest.json @@ -453,7 +453,7 @@ "max": [ 0.321, 1.824, - 0.181 + 0.189 ], "min": [ -0.321, @@ -487,15 +487,15 @@ "accessory", "hair" ], - "triangles": 4226, - "vertices": 4119 + "triangles": 4482, + "vertices": 4260 }, "npc_coat": { "bounds_godot": { "max": [ 0.332, 1.824, - 0.181 + 0.189 ], "min": [ -0.332, @@ -528,8 +528,8 @@ "accessory", "hair" ], - "triangles": 5218, - "vertices": 4975 + "triangles": 5474, + "vertices": 5264 }, "npc_dress": { "bounds_godot": { @@ -567,8 +567,8 @@ "accessory", "hair" ], - "triangles": 4644, - "vertices": 4439 + "triangles": 4900, + "vertices": 4719 }, "npc_firefighter": { "bounds_godot": { @@ -608,15 +608,15 @@ "accessory", "hair" ], - "triangles": 5088, - "vertices": 5240 + "triangles": 5344, + "vertices": 5494 }, "npc_guard": { "bounds_godot": { "max": [ 0.361, 1.819, - 0.181 + 0.189 ], "min": [ -0.361, @@ -652,15 +652,15 @@ "accessory", "hair" ], - "triangles": 4790, - "vertices": 4865 + "triangles": 5046, + "vertices": 5096 }, "npc_hoodie": { "bounds_godot": { "max": [ 0.326, 1.856, - 0.181 + 0.189 ], "min": [ -0.326, @@ -693,15 +693,15 @@ "accessory", "hair" ], - "triangles": 5048, - "vertices": 4712 + "triangles": 5304, + "vertices": 4950 }, "npc_jacket": { "bounds_godot": { "max": [ 0.332, 1.824, - 0.181 + 0.189 ], "min": [ -0.332, @@ -734,15 +734,15 @@ "accessory", "hair" ], - "triangles": 4616, - "vertices": 4447 + "triangles": 4872, + "vertices": 4661 }, "npc_vest": { "bounds_godot": { "max": [ 0.321, 1.819, - 0.181 + 0.189 ], "min": [ -0.321, @@ -775,8 +775,8 @@ "accessory", "hair" ], - "triangles": 4724, - "vertices": 4713 + "triangles": 4980, + "vertices": 4879 }, "palm_tree": { "bounds_godot": { diff --git a/assets/models/blender/npc/npc_casual.glb b/assets/models/blender/npc/npc_casual.glb index 3c43af3..712de73 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 facf84c..86302ed 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 a41bb12..a5d6378 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 0b8eb0a..09d3147 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 d720baf..2dcfa5c 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 bb37989..a947abc 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 bfc1d09..ce47f67 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 dc3ca95..846460d 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 ea28796..ef11298 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 abfd458..fb81e73 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 a9a90e8..53a8685 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 352b73c..1e88e4e 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 0a4617e..494c68c 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 dec4699..f491002 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 858cfed..c83f4a0 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 453b3b1..7d98956 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 8f8c991..0607ac2 100644 --- a/docs/blender-assets.md +++ b/docs/blender-assets.md @@ -96,7 +96,7 @@ All eight use the `npc_base` contract, so each one can replace variants (vertex count after seam splits; `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in `tools/blender_assets.py`, raised from 6,000/5,000 on 2026-11-02). The frozen `npc_base` fallback keeps its original 6,000/4,000 contract. The current range - is 4,226–5,218 triangles and 4,119–5,240 vertices, leaving room for the + is 4,482–5,474 triangles and 4,260–5,494 vertices, leaving room for the remaining grip and clothing detail. Construction: the torso and limbs are one continuous Skin-modifier mesh, diff --git a/docs/blender_previews/README.md b/docs/blender_previews/README.md index 4daf974..98d3423 100644 --- a/docs/blender_previews/README.md +++ b/docs/blender_previews/README.md @@ -51,6 +51,7 @@ Committed contact sheets and local Blender renders for the character, gear and p | sheet_npc_baseline_compare-face-detail.jpg | [`sheet_npc_baseline_compare-face-detail.jpg`](sheet_npc_baseline_compare-face-detail.jpg) | | sheet_npc_hands_compare.jpg | [`sheet_npc_hands_compare.jpg`](sheet_npc_hands_compare.jpg) | | sheet_npc_hands_grip_compare.jpg | [`sheet_npc_hands_grip_compare.jpg`](sheet_npc_hands_grip_compare.jpg) | +| sheet_npc_baseline_compare-shoes.jpg | [`sheet_npc_baseline_compare-shoes.jpg`](sheet_npc_baseline_compare-shoes.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-shoes.jpg b/docs/blender_previews/sheet_npc_baseline_compare-shoes.jpg new file mode 100644 index 0000000..73bc1b5 Binary files /dev/null and b/docs/blender_previews/sheet_npc_baseline_compare-shoes.jpg differ diff --git a/docs/development-status.md b/docs/development-status.md index 189226d..f250967 100644 --- a/docs/development-status.md +++ b/docs/development-status.md @@ -45,6 +45,7 @@ Implemented so far: - NPC mesh budget (0.9.4 work): the eight library variants now have an 8,000-triangle / 8,000-glTF-vertex cap (up from 6,000/5,000); the `npc_base` fallback remains 6,000/4,000. Current characters measure 4,226–5,218 triangles and 4,119–5,240 vertices. - NPC face-detail pass (0.9.4 work): ala wings and a smaller nose bridge/tip, one rounded mouth slot, and a darker ear concha; all eight rebuilt. The comparison is `sheet_npc_baseline_compare-face-detail.jpg`. - NPC headgear clearance (0.9.4 work): the 96-frame final-context review after the face-detail pass confirms the cap, firefighter helmet, hood and sunglasses clear the refined brow and eyes; no gear rebuild was needed. +- NPC footwear pass (0.9.4 work): casual shoes gained rounded toe and heel caps; responder/firefighter boots gained matching toe/heel volumes while retaining the black rubber boot language. All eight rebuilt; comparison `sheet_npc_baseline_compare-shoes.jpg`. - NPC hand-item grip (0.9.4 work): runtime `HAND_ITEM_FORWARD` moves every hand gear 4 cm forward from the wrist proxy, so phone, cup, clipboard, camera, flashlight, wand and radio sit in the open palm instead of partly inside it. Finger curl/thumb contact remains open; the comparison is `sheet_npc_hands_grip_compare.jpg`. - NPC hand pass (0.9.4 work): wider, shallower palms; four two-segment bent fingers with rounded tips and a separate bent thumb; optional `--view=hand` capture at 0.80 m. The hand comparison is `sheet_npc_hands_compare.jpg`; item grip offsets remain open. - 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). diff --git a/docs/npc-visual-review.md b/docs/npc-visual-review.md index d7a3a2b..685fbba 100644 --- a/docs/npc-visual-review.md +++ b/docs/npc-visual-review.md @@ -117,17 +117,17 @@ From `python3 tools/blender_assets.py fit` and the manifest: | Character | Tris | Verts | Head width | Clothing radius | Notable gear clearances | | --- | ---: | ---: | ---: | ---: | --- | -| `npc_casual` | 4,226 | 4,119 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | -| `npc_jacket` | 4,616 | 4,447 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | -| `npc_coat` | 5,218 | 4,975 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | -| `npc_vest` | 4,724 | 4,713 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | -| `npc_dress` | 4,644 | 4,439 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | -| `npc_hoodie` | 5,048 | 4,712 | 0.315 m | 0.190 m (hip) | — | -| `npc_firefighter` | 5,088 | 5,240 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | -| `npc_guard` | 4,790 | 4,865 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | +| `npc_casual` | 4,482 | 4,260 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | +| `npc_jacket` | 4,872 | 4,661 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | +| `npc_coat` | 5,474 | 5,264 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | +| `npc_vest` | 4,980 | 4,879 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | +| `npc_dress` | 4,900 | 4,719 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | +| `npc_hoodie` | 5,304 | 4,950 | 0.315 m | 0.190 m (hip) | — | +| `npc_firefighter` | 5,344 | 5,494 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | +| `npc_guard` | 5,046 | 5,096 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | -`npc_coat` is widest at 5,218 triangles (2,782 below the 8,000 cap); -`npc_firefighter` is closest on vertices at 5,240 (2,760 below the 8,000 cap). +`npc_coat` is widest at 5,474 triangles (2,526 below the 8,000 cap); +`npc_firefighter` is closest on vertices at 5,494 (2,506 below the 8,000 cap). The refit backpack measures 380 triangles / 406 vertices. ## Baseline findings, ranked @@ -169,9 +169,10 @@ The refit backpack measures 380 triangles / 406 vertices. fabric pattern repeats strongly on the casual, coat and dress variants, and collar/cuff/waist edges are abrupt. This is more visible than the shoe/hem details in the current portraits. -6. **Shoes are simple blocks.** They are grounded, but the toe/heel silhouette - is minimal; the responder and firefighter footwear should remain distinct - after any refinement. +6. **Shoes: fixed 2026-11-02.** Casual shoes gained rounded toe and heel caps; + responder/firefighter boots gained matching toe/heel volumes while keeping the + taller black rubber boot language. Both stay above the sole contact plane, and + the full-body comparison is `sheet_npc_baseline_compare-shoes.jpg`. 7. **Headgear scale and clearance. Checked 2026-11-02.** The cap and helmet width ratios are 1.067 and 1.651; those are expected for headgear. Final-context portraits confirm the cap, firefighter helmet, hood and sunglasses clear the @@ -225,6 +226,6 @@ rather than replacing it. grips; the forward offset is already in place. 2. Add the remaining body-shape variation between variants and re-measure the LOD/impostor silhouettes. -3. Audit hems and footwear in walk/sit/block poses. +3. Audit hems in walk/sit/block poses; footwear silhouette is now in the baseline. 4. Re-run the 48-frame baseline, the 96-frame final-context review, the 24-NPC budget and `blender_assets.py audit/fit`; publish the next comparison sheet. diff --git a/tools/blender_assets.py b/tools/blender_assets.py index be391cc..9bd543f 100644 --- a/tools/blender_assets.py +++ b/tools/blender_assets.py @@ -845,6 +845,12 @@ def _hands_and_shoes(mats, spec): seg=6, rings=4)) parts.append(box("Shoe", (0.10, 0.25, 0.075), (0.12 * s, -0.045, 0.04), shoe, bevel=0.028, segs=1)) + # Round the toe and heel so the low-poly shoe reads as footwear rather + # than a box; both stay above the rubber sole contact plane. + parts.append(sphere("ShoeToe", 0.045, (0.12 * s, -0.155, 0.045), shoe, + seg=8, rings=5, scale=(1.05, 0.80, 0.55))) + parts.append(sphere("ShoeHeel", 0.034, (0.12 * s, 0.070, 0.045), shoe, + seg=8, rings=5, scale=(1.0, 0.55, 0.55))) parts.append(box("Sole", (0.106, 0.262, 0.02), (0.12 * s, -0.045, 0.01), material("rubber", textured_solid=True))) return parts @@ -916,6 +922,12 @@ def _clothing(spec, mats): rot=(0, 0, 0), seg=14, minor_seg=4)) parts.append(box("Boot", (0.12, 0.27, 0.22), (0.12 * s, -0.04, 0.11), mats["rubber"], bevel=0.03, segs=2)) + parts.append(sphere("BootToe", 0.055, (0.12 * s, -0.155, 0.075), + mats["rubber"], seg=8, rings=5, + scale=(1.0, 0.80, 0.55))) + parts.append(sphere("BootHeel", 0.045, (0.12 * s, 0.080, 0.055), + mats["rubber"], seg=8, rings=5, + scale=(1.0, 0.55, 0.50))) if layer == "guard": belt = torus("Belt", 0.168, 0.022, (0, 0, 0.86), mats["leather"], seg=24, minor_seg=4, scale=(1.0, 0.72, 1.4))