diff --git a/MEMORY.md b/MEMORY.md index 58bea3c..e7458f1 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -13,6 +13,9 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o (46 `.blend` files removed from the index on 2026-11-01); the `.gdignore` marker stays tracked. - `screen_designs.py judge` default `google/gemini-2.5-flash-lite` worked on 2026-11-01 (7 match / 4 review / 0 diff); `--model` remains available. +- `capture_realism.gd` parses `--portrait-type=N`; it was documented but ignored before the gear + pass. Portrait captures need the requested NPC type to have spawned, otherwise the stage can + finish with an empty frame; use the Blender `npc_coat` preview for a deterministic gear check. - Gear fit is measured with `python3 tools/blender_assets.py fit` (stdlib, reads the GLBs): head width uses skin vertices weighted to Head in y 1.55–1.81; clothing radius is the cloth skirt in y 0.30–0.52 with a pants hip fallback. The command reports per-character gear boxes, diff --git a/TODO.md b/TODO.md index 51a6118..4cdb859 100644 --- a/TODO.md +++ b/TODO.md @@ -16,11 +16,11 @@ live in `tools/blender_assets.py` (`_build_gear()`), the contract in `docs/blend bounding box with head-band status or skirt clearance. Current measurements: head width 0.315 m; coat skirt 0.225 m, dress 0.260 m; sunglasses 0.282 m (0.90x head); shoulder-bag x-clearance -0.025 m on the coat (radial -0.031 m on the dress). -- [ ] Narrow `sunglasses` from 0.28 m to ~0.20 m. The width is driven by the arms/hinges - (`Hinge` at `x = ±0.11` with a 0.06 box, `Arm` at `±0.138`), not by the 0.03 m lenses, so - pull those in first and shrink the lenses to ~0.024 m radius; keep the lens plane at - `y = -0.152` (in front of the sclera) and `z = 0.035` (eye line) so it does not ride up - over the new brow ridge. +- [x] Narrow `sunglasses` from 0.28 m to ~0.20 m. The hinges moved to ±0.075 (0.035 box), the + arms to ±0.098, and the lenses are 0.024 m radius at the same `y=-0.152`, `z=0.035` plane. + Rebuilt GLB/AO: measured width **0.202 m** (0.64x head), still inside the head band. + Also fixed `capture_realism.gd` so `--portrait-type=N` is actually parsed (it was documented + but ignored). - [ ] Fix `shoulder_bag` clipping through `npc_coat`/`npc_dress`: the bag body spans `|x|` 0.17–0.25 at the hip while the skirt flares to 0.225–0.26, so its inner half is inside the cloth. Move it outboard (or up onto the belt line) until the inner face clears the flare, diff --git a/assets/models/blender/manifest.json b/assets/models/blender/manifest.json index b438c08..e975096 100644 --- a/assets/models/blender/manifest.json +++ b/assets/models/blender/manifest.json @@ -1097,13 +1097,13 @@ "attach": "head", "bounds_godot": { "max": [ - 0.141, - 0.065, + 0.101, + 0.059, 0.158 ], "min": [ - -0.141, - 0.005, + -0.101, + 0.011, 0.003 ] }, diff --git a/assets/models/blender/npc_gear/sunglasses.glb b/assets/models/blender/npc_gear/sunglasses.glb index d95efb7..77c92c8 100644 Binary files a/assets/models/blender/npc_gear/sunglasses.glb and b/assets/models/blender/npc_gear/sunglasses.glb differ diff --git a/assets/models/blender/npc_gear/sunglasses_ao.png b/assets/models/blender/npc_gear/sunglasses_ao.png index a2c1813..73d10a2 100644 Binary files a/assets/models/blender/npc_gear/sunglasses_ao.png and b/assets/models/blender/npc_gear/sunglasses_ao.png differ diff --git a/src/test/capture_realism.gd b/src/test/capture_realism.gd index e16620f..d8c628d 100644 --- a/src/test/capture_realism.gd +++ b/src/test/capture_realism.gd @@ -112,6 +112,8 @@ func _parse_args() -> void: elif text.begins_with("--stages="): for name in text.substr(9).split(","): _only_stages[name.strip_edges()] = true + elif text.begins_with("--portrait-type="): + _portrait_type = int(text.substr(16)) func _pair(text: String, into: Dictionary) -> void: var cut := text.find("=", 6) # skip the "res://" scheme diff --git a/tools/blender_assets.py b/tools/blender_assets.py index 2f92e32..9f168eb 100644 --- a/tools/blender_assets.py +++ b/tools/blender_assets.py @@ -970,12 +970,15 @@ def build_gear(name): elif name == "hood": p.append(_hood("Hood", M("plastic_grey"), (0, 0, 0.015))) elif name == "sunglasses": + # Keep the front frame narrow enough to sit inside the 0.315 m head + # silhouette. The arms/hinges set the overall width, so pull those in + # before shrinking the lenses; the lens plane stays in front of the eyes. for s in (-1.0, 1.0): - p.append(cyl("Lens", 0.03, 0.03, 0.008, (0.05 * s, -0.152, 0.035), M("lens"), + p.append(cyl("Lens", 0.024, 0.024, 0.008, (0.04 * s, -0.152, 0.035), M("lens"), seg=14, rot=(math.radians(90), 0, 0), scale=(1.25, 1.0, 1.0))) - p.append(box("Hinge", (0.06, 0.008, 0.01), (0.11 * s, -0.150, 0.045), + p.append(box("Hinge", (0.035, 0.008, 0.01), (0.075 * s, -0.150, 0.045), M("plastic_black"))) - p.append(box("Arm", (0.006, 0.15, 0.008), (0.138 * s, -0.078, 0.042), + p.append(box("Arm", (0.006, 0.15, 0.008), (0.098 * s, -0.078, 0.042), M("plastic_black"))) p.append(box("Bridge", (0.04, 0.006, 0.008), (0, -0.155, 0.045), M("plastic_black"))) elif name == "firefighter_helmet":