Add head subdivision variants and pick s48 after 1.5m/1.85m comparison
This commit is contained in:
1 parent
b386a21173
commit
f0f609fe1b
3 files changed
+28
-7
No files matched your search
@@ -90,9 +90,13 @@ the rest of §C remains the broad review checklist.
|
|||||||
centre-profile evidence: nose tip −0.192, brow −0.150, chin −0.149, jaw step −0.11, forehead
|
centre-profile evidence: nose tip −0.192, brow −0.150, chin −0.149, jaw step −0.11, forehead
|
||||||
−0.126→−0.078. Vision-judged ~3/5; remaining polish (forehead/ear/jawline read) is folded
|
−0.126→−0.078. Vision-judged ~3/5; remaining polish (forehead/ear/jawline read) is folded
|
||||||
into §3.2 comparison.
|
into §3.2 comparison.
|
||||||
- [ ] Compare two or three subdivision/loop variants at 1.85 m front/three-quarter/profile and a
|
- [x] Compare two or three subdivision/loop variants at 1.85 m front/three-quarter/profile and a
|
||||||
1.5 m portrait. Pick the variant that reads best before changing all 12 characters; keep the
|
1.5 m portrait. Pick the variant that reads best before changing all 12 characters; keep the
|
||||||
rejected variants local.
|
rejected variants local.
|
||||||
|
Compared `head_proto` at seg 24 (670 verts), 48 (1,558 verts) and 64 (2,470 verts) at 1.85 m
|
||||||
|
and 1.5 m, front/three-quarter/profile. Vision-judged: **s48 wins** — s24 is too faceted
|
||||||
|
(nose/lips melt, jagged silhouette), s64 is visually identical to s48 at both distances
|
||||||
|
(extra ~900 verts wasted). Rejected variants stay in `tmp/npc-parts/`.
|
||||||
- [ ] Verify head weights stay on the Head bone, headgear clearance still passes `fit`, and the
|
- [ ] Verify head weights stay on the Head bone, headgear clearance still passes `fit`, and the
|
||||||
face does not bake recessed creases into dark AO patches.
|
face does not bake recessed creases into dark AO patches.
|
||||||
- [ ] Get owner sign-off on the one-variant prototype, then roll the approved head/face into the
|
- [ ] Get owner sign-off on the one-variant prototype, then roll the approved head/face into the
|
||||||
|
|||||||
@@ -232,6 +232,20 @@ brow −0.150, chin −0.149, jaw step −0.11, forehead −0.126→−0.078. Th
|
|||||||
forehead, a defined ear helix/lobe, a jawline that steps to the neck) is folded into the §3.2
|
forehead, a defined ear helix/lobe, a jawline that steps to the neck) is folded into the §3.2
|
||||||
variant comparison; the neck is part of `_skin_body`, so the standalone head always lacks it.
|
variant comparison; the neck is part of `_skin_body`, so the standalone head always lacks it.
|
||||||
|
|
||||||
|
### Head subdivision comparison (2026-11-04)
|
||||||
|
|
||||||
|
Three `head_proto` variants at UV-sphere segments 24/48/64 were rendered at 1.85 m and 1.5 m in
|
||||||
|
front/three-quarter/profile and judged side by side:
|
||||||
|
|
||||||
|
| Variant | Verts / tris | 1.85 m | 1.5 m | Verdict |
|
||||||
|
| --- | ---: | --- | --- | --- |
|
||||||
|
| s24 | 670 / 1,168 | lumpy, nose/lips melt into the chin | jagged silhouette, no mouth | reject |
|
||||||
|
| s48 | 1,558 / 2,944 | eyes/brows/nose/lips read cleanly | features resolve cleanly | **pick** |
|
||||||
|
| s64 | 2,470 / 4,768 | visually identical to s48 | indistinguishable from s48 | reject (wasted verts) |
|
||||||
|
|
||||||
|
The chosen s48 is the `head_proto` default; the rejected s24/s64 stay local under
|
||||||
|
`tmp/npc-parts/`.
|
||||||
|
|
||||||
## Scorecard skeleton
|
## Scorecard skeleton
|
||||||
|
|
||||||
Verdicts use `qa-review` semantics: **P0** (blocker: wrong scale, broken normals, budget fail),
|
Verdicts use `qa-review` semantics: **P0** (blocker: wrong scale, broken normals, budget fail),
|
||||||
|
|||||||
+9
-6
@@ -93,18 +93,19 @@ def _export_obj(obj, path):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def _head_proto(mats, spec):
|
def _head_proto(mats, spec, subdiv=48):
|
||||||
"""Connected head shell with sculpted sockets, nose, lips, ears and brow/cheek/jaw.
|
"""Connected head shell with sculpted sockets, nose, lips, ears and brow/cheek/jaw.
|
||||||
|
|
||||||
This is the §3 prototype: the skin features are displaced out of one head
|
This is the §3 prototype: the skin features are displaced out of one head
|
||||||
mesh instead of floating spheres, so the face is a single connected surface
|
mesh instead of floating spheres, so the face is a single connected surface
|
||||||
with loop-based eye sockets and protruding nose/lip/ear volumes. The eyeballs
|
with loop-based eye sockets and protruding nose/lip/ear volumes. The eyeballs
|
||||||
(sclera/iris/lids) stay separate — they are not skin. Blender Z-up, face
|
(sclera/iris/lids) stay separate — they are not skin. Blender Z-up, face
|
||||||
front −Y.
|
front −Y. `subdiv` is the UV-sphere u-segments (24/48/64 variants).
|
||||||
"""
|
"""
|
||||||
skin, acc, hair = mats["skin"], mats["accessory"], mats["hair"]
|
skin, acc, hair = mats["skin"], mats["accessory"], mats["hair"]
|
||||||
white = ba.material("eye_white", textured_solid=True)
|
white = ba.material("eye_white", textured_solid=True)
|
||||||
head = ba.sphere("HeadShell", 0.155, (0, 0, 1.635), skin, seg=48, rings=26,
|
rings = max(14, subdiv // 2 + 2)
|
||||||
|
head = ba.sphere("HeadShell", 0.155, (0, 0, 1.635), skin, seg=subdiv, rings=rings,
|
||||||
scale=(0.90, 0.96, 1.08))
|
scale=(0.90, 0.96, 1.08))
|
||||||
ba._shape_head_mesh(head) # brow ridge + cheekbone + jaw/chin planes
|
ba._shape_head_mesh(head) # brow ridge + cheekbone + jaw/chin planes
|
||||||
bm = ba.bmesh.new()
|
bm = ba.bmesh.new()
|
||||||
@@ -204,7 +205,7 @@ def _head_proto(mats, spec):
|
|||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
|
||||||
def build_part(name, variant, out):
|
def build_part(name, variant, out, subdiv=48):
|
||||||
"""Build one part fresh from the current builders (Blender-only)."""
|
"""Build one part fresh from the current builders (Blender-only)."""
|
||||||
mats, spec = _mats_and_spec(variant)
|
mats, spec = _mats_and_spec(variant)
|
||||||
if name == "head":
|
if name == "head":
|
||||||
@@ -214,7 +215,7 @@ def build_part(name, variant, out):
|
|||||||
elif name == "face":
|
elif name == "face":
|
||||||
obj = ba.join(ba._face(mats, spec), "face")
|
obj = ba.join(ba._face(mats, spec), "face")
|
||||||
elif name == "head_proto":
|
elif name == "head_proto":
|
||||||
obj = ba.join(_head_proto(mats, spec), "head_proto")
|
obj = ba.join(_head_proto(mats, spec, subdiv=subdiv), "head_proto")
|
||||||
else:
|
else:
|
||||||
side = -1.0 if name == "hand_l" else 1.0
|
side = -1.0 if name == "hand_l" else 1.0
|
||||||
hs = ba._hands_and_shoes(mats, spec)
|
hs = ba._hands_and_shoes(mats, spec)
|
||||||
@@ -378,6 +379,8 @@ def main(argv=None):
|
|||||||
s.add_argument("--part", required=True, choices=PART_NAMES)
|
s.add_argument("--part", required=True, choices=PART_NAMES)
|
||||||
s.add_argument("--variant", default=PROTOTYPE)
|
s.add_argument("--variant", default=PROTOTYPE)
|
||||||
s.add_argument("--out", required=True)
|
s.add_argument("--out", required=True)
|
||||||
|
s.add_argument("--subdiv", type=int, default=48,
|
||||||
|
help="head_proto UV-sphere u-segments (24/48/64 variants)")
|
||||||
|
|
||||||
asm = sub.add_parser("assemble")
|
asm = sub.add_parser("assemble")
|
||||||
asm.add_argument("--variant", default=PROTOTYPE)
|
asm.add_argument("--variant", default=PROTOTYPE)
|
||||||
@@ -411,7 +414,7 @@ def main(argv=None):
|
|||||||
return code
|
return code
|
||||||
ba.reset_scene()
|
ba.reset_scene()
|
||||||
if args.cmd == "build":
|
if args.cmd == "build":
|
||||||
build_part(args.part, args.variant, args.out)
|
build_part(args.part, args.variant, args.out, subdiv=args.subdiv)
|
||||||
print(f"built part {args.part} -> {args.out}")
|
print(f"built part {args.part} -> {args.out}")
|
||||||
elif args.cmd == "extract":
|
elif args.cmd == "extract":
|
||||||
extract_part(args.part, args.variant, args.out)
|
extract_part(args.part, args.variant, args.out)
|
||||||
|
|||||||
Reference in new issue
Block a user