diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c1d97a..c5b859a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,8 @@ Semantic Versioning.
the hips, so shirts and jackets sit less like straight tubes.
- Character hands now have a wider palm, bent fingers with rounded tips and a
separate thumb instead of flat bars.
+- Character noses gained ala wings and a smaller bridge/tip, the mouth is a
+ rounded slot, and the ears have a darker inner concha.
### Fixed
diff --git a/MEMORY.md b/MEMORY.md
index 6afbcbf..bd250c2 100644
--- a/MEMORY.md
+++ b/MEMORY.md
@@ -96,11 +96,16 @@ 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 face-detail pass (0.9.4, 2026-11-02):** Nose tip radius reduced to 0.022 with ala wings,
+ the two dark lip boxes became one shallow `Mouth` sphere (two blobs read as a moustache), and the
+ ears gained a dark `EarInner` concha. All eight rebuilt; current counts are 4,226–5,218 tris /
+ 4,119–5,240 verts under the 8,000/8,000 cap. Sheet:
+ `sheet_npc_baseline_compare-face-detail.jpg`.
- **NPC hand pass (0.9.4, 2026-11-02):** palm radius 0.046 at z .742, scale (.80,.62,.88);
fingers are `tube` paths from z .758 to .681–.694 with tip spheres, thumb is a bent tube. The
optional hand capture is `capture_npc_baseline.gd -- --view=hand` (8 frames, baseline stays 48).
- `npc_firefighter` is at 4,948 verts; the library cap is now 8,000 (see the character-mesh-cap
- decision above), so the remaining face/grip/clothing detail can use real headroom. Grip offsets
+ `npc_firefighter` is at 5,240 verts after the face-detail pass; the library cap is 8,000 (see
+ the character-mesh-cap decision above), so grip/clothing detail still has headroom. Grip offsets
remain open.
- **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,
diff --git a/TODO.md b/TODO.md
index 0f74bb7..8c4b1bb 100644
--- a/TODO.md
+++ b/TODO.md
@@ -110,8 +110,9 @@ and equipment fit.
sculpted into the head mesh; no separate jaw/cheek spheres or AO intersection rings.)
- [x] Improve brow/eye-socket depth and eyelid/iris readability without turning the low-poly face
into a noisy mesh. (Recessed sclera, thin upper lid; `sheet_npc_baseline_compare-face.jpg`.)
-- [ ] Refine the nose bridge/tip, lips and ear shape; keep the five tint slots and face direction
- unchanged.
+- [x] Refine the nose bridge/tip, lips and ear shape; keep the five tint slots and face direction
+ unchanged. (Ala wings, smaller tip, one rounded mouth slot, darker ear concha; all eight
+ rebuilt. `sheet_npc_baseline_compare-face-detail.jpg`.)
- [ ] Audit hair caps, fringes, side locks, buns and beard silhouettes for gaps, intersections and
obvious scalp seams; keep hair color in the tintable slot.
- [ ] Verify hats/hoods/sunglasses/helmets still clear the refined brow, ears and hair; update gear
diff --git a/assets/models/blender/manifest.json b/assets/models/blender/manifest.json
index 518154f..d2c64a7 100644
--- a/assets/models/blender/manifest.json
+++ b/assets/models/blender/manifest.json
@@ -453,7 +453,7 @@
"max": [
0.321,
1.824,
- 0.185
+ 0.181
],
"min": [
-0.321,
@@ -467,8 +467,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber"
],
@@ -487,15 +487,15 @@
"accessory",
"hair"
],
- "triangles": 4042,
- "vertices": 3894
+ "triangles": 4226,
+ "vertices": 4119
},
"npc_coat": {
"bounds_godot": {
"max": [
0.332,
1.824,
- 0.185
+ 0.181
],
"min": [
-0.332,
@@ -509,8 +509,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber"
],
@@ -528,8 +528,8 @@
"accessory",
"hair"
],
- "triangles": 5034,
- "vertices": 4814
+ "triangles": 5218,
+ "vertices": 4975
},
"npc_dress": {
"bounds_godot": {
@@ -550,8 +550,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber"
],
@@ -567,8 +567,8 @@
"accessory",
"hair"
],
- "triangles": 4460,
- "vertices": 4191
+ "triangles": 4644,
+ "vertices": 4439
},
"npc_firefighter": {
"bounds_godot": {
@@ -589,8 +589,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"hivis",
"reflective",
@@ -608,15 +608,15 @@
"accessory",
"hair"
],
- "triangles": 4904,
- "vertices": 4948
+ "triangles": 5088,
+ "vertices": 5240
},
"npc_guard": {
"bounds_godot": {
"max": [
0.361,
1.819,
- 0.185
+ 0.181
],
"min": [
-0.361,
@@ -630,8 +630,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber",
"leather",
@@ -652,15 +652,15 @@
"accessory",
"hair"
],
- "triangles": 4606,
- "vertices": 4679
+ "triangles": 4790,
+ "vertices": 4865
},
"npc_hoodie": {
"bounds_godot": {
"max": [
0.326,
1.856,
- 0.185
+ 0.181
],
"min": [
-0.326,
@@ -674,8 +674,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber"
],
@@ -693,15 +693,15 @@
"accessory",
"hair"
],
- "triangles": 4864,
- "vertices": 4506
+ "triangles": 5048,
+ "vertices": 4712
},
"npc_jacket": {
"bounds_godot": {
"max": [
0.332,
1.824,
- 0.185
+ 0.181
],
"min": [
-0.332,
@@ -715,8 +715,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber"
],
@@ -734,15 +734,15 @@
"accessory",
"hair"
],
- "triangles": 4432,
- "vertices": 4265
+ "triangles": 4616,
+ "vertices": 4447
},
"npc_vest": {
"bounds_godot": {
"max": [
0.321,
1.819,
- 0.185
+ 0.181
],
"min": [
-0.321,
@@ -756,8 +756,8 @@
"skin",
"cloth",
"pants",
- "eye_white",
"accessory",
+ "eye_white",
"hair",
"rubber",
"hivis",
@@ -775,8 +775,8 @@
"accessory",
"hair"
],
- "triangles": 4540,
- "vertices": 4503
+ "triangles": 4724,
+ "vertices": 4713
},
"palm_tree": {
"bounds_godot": {
diff --git a/assets/models/blender/npc/npc_casual.glb b/assets/models/blender/npc/npc_casual.glb
index 7f4ff48..3c43af3 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 7429ec1..facf84c 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 c3447e6..a41bb12 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 c05d791..0b8eb0a 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 6fe54bb..d720baf 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 b305b7a..bb37989 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 79f3559..bfc1d09 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 3861f59..dc3ca95 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 5bf7893..ea28796 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 4816f58..abfd458 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 a3a4627..a9a90e8 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 7a07040..352b73c 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 a0a94a1..0a4617e 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 ceed370..dec4699 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 a9c7142..858cfed 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 a20e863..453b3b1 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 0d2dc62..8f8c991 100644
--- a/docs/blender-assets.md
+++ b/docs/blender-assets.md
@@ -96,8 +96,8 @@ 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,042–5,034 triangles and 3,894–4,948 vertices, leaving room for the
- remaining face, grip and clothing detail.
+ is 4,226–5,218 triangles and 4,119–5,240 vertices, leaving room for the
+ remaining grip and clothing detail.
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 90e6568..6aa4b84 100644
--- a/docs/blender_previews/README.md
+++ b/docs/blender_previews/README.md
@@ -48,6 +48,7 @@ Committed contact sheets and local Blender renders for the character, gear and p
|
| [`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-torso.jpg`](sheet_npc_baseline_compare-torso.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_gear.jpg`](sheet_npc_gear.jpg) |
|
| [`sheet_props.jpg`](sheet_props.jpg) |
diff --git a/docs/blender_previews/sheet_npc_baseline_compare-face-detail.jpg b/docs/blender_previews/sheet_npc_baseline_compare-face-detail.jpg
new file mode 100644
index 0000000..61d9b0f
Binary files /dev/null and b/docs/blender_previews/sheet_npc_baseline_compare-face-detail.jpg differ
diff --git a/docs/development-status.md b/docs/development-status.md
index 1482e15..3912374 100644
--- a/docs/development-status.md
+++ b/docs/development-status.md
@@ -42,7 +42,8 @@ Implemented so far:
- 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 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,042–5,034 triangles and 3,894–4,948 vertices.
+- 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 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).
- 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.
diff --git a/docs/npc-visual-review.md b/docs/npc-visual-review.md
index d70745e..51797ad 100644
--- a/docs/npc-visual-review.md
+++ b/docs/npc-visual-review.md
@@ -8,6 +8,7 @@ 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)
the latest [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg),
+the face-detail pass [`sheet_npc_baseline_compare-face-detail.jpg`](blender_previews/sheet_npc_baseline_compare-face-detail.jpg),
and the conversation-distance hand pass
[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg).
@@ -115,17 +116,17 @@ From `python3 tools/blender_assets.py fit` and the manifest:
| Character | Tris | Verts | Head width | Clothing radius | Notable gear clearances |
| --- | ---: | ---: | ---: | ---: | --- |
-| `npc_casual` | 4,042 | 3,894 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 |
-| `npc_jacket` | 4,432 | 4,265 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) |
-| `npc_coat` | 5,034 | 4,814 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 |
-| `npc_vest` | 4,540 | 4,503 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 |
-| `npc_dress` | 4,460 | 4,191 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 |
-| `npc_hoodie` | 4,864 | 4,506 | 0.315 m | 0.190 m (hip) | — |
-| `npc_firefighter` | 4,904 | 4,948 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 |
-| `npc_guard` | 4,606 | 4,679 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 |
+| `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_coat` is widest at 5,034 triangles (2,966 below the 8,000 cap);
-`npc_firefighter` is closest on vertices at 4,948 (3,052 below the 8,000 cap).
+`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).
The refit backpack measures 380 triangles / 406 vertices.
## Baseline findings, ranked
@@ -139,12 +140,13 @@ The refit backpack measures 380 triangles / 406 vertices.
photo prop is now hidden outside `photo`/`film` poses
(`photo_prop_visible_for_pose()`), so it only appears when the character is
actually taking a picture; the after frame confirms one bag at rest.
-2. **Face depth. Fixed 2026-11-02.** The sclera was recessed into the socket and
- wrapped with a thin upper lid; the separate jaw/cheek spheres were replaced
- by a chin plate and cheekbones sculpted into the head mesh. That removes the
- dark intersection rings that read as a beard shadow and keeps the mouth on
- the surface. The mouth bar and low-poly brows remain stylized, but the eye
- socket and jaw/cheek transition now read as one head volume.
+2. **Face depth and detail. Fixed 2026-11-02.** The sclera was recessed into the
+ socket and wrapped with a thin upper lid; the separate jaw/cheek spheres were
+ replaced by a chin plate and cheekbones sculpted into the head mesh. The nose
+ now has ala wings and a smaller bridge/tip, the mouth is one rounded slot
+ instead of a flat bar, and the ears have a darker concha. That removes the
+ dark intersection rings and the moustache-like read at portrait distance; the
+ low-poly brows remain stylized.
3. **Backpack straps read as rigid black bars. Refit 2026-11-02.** The straps
were thin round tubes whose chest/waist points sat inside the torso, so only
fragments showed from the front. They are now flattened bands whose front path
@@ -206,6 +208,7 @@ The committed runs are
[`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).
+The face-detail pass is [`sheet_npc_baseline_compare-face-detail.jpg`](blender_previews/sheet_npc_baseline_compare-face-detail.jpg).
The hand pass is [`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg).
Keep the baseline sheet as the before artifact and add a new comparison sheet
rather than replacing it.
@@ -217,7 +220,5 @@ rather than replacing it.
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.
-4. Refine nose/lip/ear shapes only if the portrait pass still flags them after
- the hands and clothing read cleanly.
-5. Re-run the 48-frame baseline, the 96-frame final-context review, the 24-NPC
+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 ec4f469..be391cc 100644
--- a/tools/blender_assets.py
+++ b/tools/blender_assets.py
@@ -774,14 +774,19 @@ def _face(mats, spec):
# nose: bridge and ball (nostrils were cut to stay inside the vertex budget)
parts.append(cyl("NoseBridge", 0.016, 0.022, 0.06, (0, -0.145, 1.652), skin, seg=8,
rot=(math.radians(-72), 0, 0), smooth=True))
- parts.append(sphere("NoseTip", 0.024, (0, -0.163, 1.616), skin, seg=10, rings=6,
- scale=(1.0, 0.9, 0.85)))
+ parts.append(sphere("NoseTip", 0.022, (0, -0.162, 1.615), skin, seg=10, rings=6,
+ scale=(1.0, 0.85, 0.80)))
for s in (-1.0, 1.0):
- # ear with a lobe, and one crease so it is not a flat disc
+ # Ala widen the nose base into a small triangle rather than a ball.
+ parts.append(sphere("NoseWing", 0.011, (0.015 * s, -0.160, 1.606), skin,
+ seg=6, rings=4, scale=(1.0, 0.65, 0.75)))
+ # Ear with a lobe and a darker concha; the old disc had no depth cue.
parts.append(sphere("Ear", 0.036, (0.143 * s, 0.008, 1.63), skin, seg=8, rings=5,
scale=(0.42, 1.0, 1.35)))
parts.append(sphere("EarLobe", 0.018, (0.142 * s, -0.005, 1.605), skin, seg=8,
rings=5, scale=(0.45, 1.0, 0.9)))
+ parts.append(sphere("EarInner", 0.013, (0.149 * s, -0.003, 1.633), acc,
+ seg=6, rings=4, scale=(0.28, 0.65, 0.90)))
# Eye: recess the sclera into the socket, then wrap it with skin lids.
# The old white ball sat ~1 cm proud of the head; this keeps the iris
# readable without the doll-like bulge.
@@ -798,9 +803,11 @@ def _face(mats, spec):
parts.append(box("Brow", (0.055, 0.014, 0.012), (0.052 * s, -0.143, 1.699), hair,
rot=(0, 0.14 * s, 0.0)))
# lips: upper and lower instead of a single bar
- parts.append(box("LipUpper", (0.052, 0.012, 0.008), (0, -0.156, 1.588), acc,
- rot=(math.radians(6), 0, 0)))
- parts.append(box("LipLower", (0.046, 0.012, 0.009), (0, -0.154, 1.578), acc))
+ # One rounded mouth slot instead of two flat boxes. Two dark lip blobs read
+ # as a moustache at portrait distance; a single shallow oval keeps the mouth
+ # line without adding a second dark mass.
+ parts.append(sphere("Mouth", 0.028, (0, -0.158, 1.583), acc, seg=8, rings=5,
+ scale=(1.0, 0.28, 0.12)))
if spec.get("beard"):
parts.append(sphere("Beard", 0.105, (0, -0.06, 1.55), hair, seg=12, rings=8,
scale=(1.0, 0.9, 0.75), cut_below=-0.2))