Refine the MakeHuman head (weld lids/brows, add hair cap) and add the user review page
This commit is contained in:
1 parent
0b2f124ffe
commit
2e1e4d7f70
5 files changed
+78
-9
No files matched your search
@@ -0,0 +1,53 @@
|
|||||||
|
# Head rework — review page (2026-11-04)
|
||||||
|
|
||||||
|
This page is the review checkpoint for the NPC head rework. The goal was to replace the
|
||||||
|
sphere-and-floating-primitive face with a head that has real nose / chin / jaw landmarks at
|
||||||
|
portrait distance.
|
||||||
|
|
||||||
|
## What changed
|
||||||
|
|
||||||
|
The original procedural face is a head sphere plus separate nose, mouth, ear and eye primitives.
|
||||||
|
The new head uses the **decimated MakeHuman head** (CC0, 1,573 tris) as the base shape and keeps
|
||||||
|
the project's separate eyeballs, brow and mouth pieces on top. An AI judge was used to drive the
|
||||||
|
decision: it scored the procedural rework at ~3/5 and then picked the MakeHuman head as the
|
||||||
|
clearer silhouette.
|
||||||
|
|
||||||
|
| | Source | Tris (head) | Read |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| **Before** | procedural `_face` (sphere + primitives) | ~1,900 | flat face, slit eyes |
|
||||||
|
| **After** | MakeHuman head + project eyes/brows/mouth | ~2,180 | nose/chin/jaw landmarks |
|
||||||
|
|
||||||
|
## AI-generated target concept
|
||||||
|
|
||||||
|
The rework was guided by two generated concept frames (front + profile, `bytedance-seed/seedream-4.5`).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 1.85 m comparison (top = current, bottom = new)
|
||||||
|
|
||||||
|
Left to right: front, three-quarter, profile.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## New head at 1.5 m (portrait)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Contract and checks
|
||||||
|
|
||||||
|
- `blender_assets.py inspect` on the assembled `npc_casual` with the new head: **14 bones,
|
||||||
|
skinned, no issues**.
|
||||||
|
- Source: `assets/models/blender_src/makehuman_head.glb` (CC0, attributed in `CREDITS.md`).
|
||||||
|
- Python 167/167, Godot 277/277, `check_docs` OK.
|
||||||
|
|
||||||
|
## Remaining polish (known, not blockers)
|
||||||
|
|
||||||
|
1. The hair cap still reads as a stepped block instead of hugging the skull.
|
||||||
|
2. The brows weld to the face but can read as a slightly separate bar.
|
||||||
|
3. The neck underside cap is plain; it is hidden by the body neck in the assembled character.
|
||||||
|
|
||||||
|
## Decision needed
|
||||||
|
|
||||||
|
Approve or reject the MakeHuman-head direction. If approved, the next step is rolling it into the
|
||||||
|
other 11 variants (one asset family at a time) with a before/after sheet per batch, and re-running
|
||||||
|
the 24-NPC budget + AO bake.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
+25
-9
@@ -1008,7 +1008,28 @@ def _makehuman_head(mats, spec):
|
|||||||
bmesh.ops.holes_fill(bm, edges=boundary)
|
bmesh.ops.holes_fill(bm, edges=boundary)
|
||||||
bm.to_mesh(head.data)
|
bm.to_mesh(head.data)
|
||||||
bm.free()
|
bm.free()
|
||||||
parts = [head]
|
|
||||||
|
# Lids and brows weld into the head shell so the face reads as one surface;
|
||||||
|
# the sclera/iris stay separate eyeballs.
|
||||||
|
shell_parts = [head]
|
||||||
|
for s in (-1.0, 1.0):
|
||||||
|
ex, ez = 0.052 * s, 1.664
|
||||||
|
shell_parts.append(sphere("UpperLid", 0.020, (ex, -0.135, ez + 0.014),
|
||||||
|
skin, seg=8, rings=4, scale=(1.20, 0.32, 0.24),
|
||||||
|
rot=(math.radians(-10), 0, 0)))
|
||||||
|
shell_parts.append(sphere("LowerLid", 0.017, (ex, -0.135, ez - 0.012),
|
||||||
|
skin, seg=8, rings=4, scale=(1.10, 0.22, 0.18),
|
||||||
|
rot=(math.radians(6), 0, 0)))
|
||||||
|
shell_parts.append(box("Brow", (0.055, 0.014, 0.012), (0.052 * s, -0.165, 1.705),
|
||||||
|
hair, rot=(0, 0.14 * s, 0.0)))
|
||||||
|
shell = join(shell_parts, "HeadShell")
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(shell.data)
|
||||||
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.008)
|
||||||
|
bm.to_mesh(shell.data)
|
||||||
|
bm.free()
|
||||||
|
|
||||||
|
parts = [shell]
|
||||||
for s in (-1.0, 1.0):
|
for s in (-1.0, 1.0):
|
||||||
# The MakeHuman socket is a deep cavity (floor ≈ −0.105); keep the
|
# The MakeHuman socket is a deep cavity (floor ≈ −0.105); keep the
|
||||||
# sclera recessed so it never breaks the face silhouette.
|
# sclera recessed so it never breaks the face silhouette.
|
||||||
@@ -1017,17 +1038,12 @@ def _makehuman_head(mats, spec):
|
|||||||
rings=6, scale=(1.25, 0.62, 0.85)))
|
rings=6, scale=(1.25, 0.62, 0.85)))
|
||||||
parts.append(sphere("Iris", 0.0105, (ex, ey - 0.007, ez), acc, seg=8,
|
parts.append(sphere("Iris", 0.0105, (ex, ey - 0.007, ez), acc, seg=8,
|
||||||
rings=5, scale=(1.0, 0.55, 1.0)))
|
rings=5, scale=(1.0, 0.55, 1.0)))
|
||||||
parts.append(sphere("UpperLid", 0.020, (ex, -0.135, ez + 0.014),
|
|
||||||
skin, seg=8, rings=4, scale=(1.20, 0.32, 0.24),
|
|
||||||
rot=(math.radians(-10), 0, 0)))
|
|
||||||
parts.append(sphere("LowerLid", 0.017, (ex, -0.135, ez - 0.012),
|
|
||||||
skin, seg=8, rings=4, scale=(1.10, 0.22, 0.18),
|
|
||||||
rot=(math.radians(6), 0, 0)))
|
|
||||||
parts.append(box("Brow", (0.055, 0.014, 0.012), (0.052 * s, -0.165, 1.705),
|
|
||||||
hair, rot=(0, 0.14 * s, 0.0)))
|
|
||||||
# A subtle mouth line on top of the MakeHuman lips for readability.
|
# A subtle mouth line on top of the MakeHuman lips for readability.
|
||||||
parts.append(sphere("Mouth", 0.028, (0, -0.165, 1.58), acc, seg=8, rings=5,
|
parts.append(sphere("Mouth", 0.028, (0, -0.165, 1.58), acc, seg=8, rings=5,
|
||||||
scale=(1.0, 0.28, 0.12)))
|
scale=(1.0, 0.28, 0.12)))
|
||||||
|
# A skull-conforming buzz cap sized for the smaller MakeHuman cranium.
|
||||||
|
parts.append(_hair_cap("Hair", hair, 1.70, 1.56, radius=0.115,
|
||||||
|
center=(0, 0.008, 1.66), scale=(0.95, 1.04, 1.05)))
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user