Recess NPC eyes and sculpt the jaw and cheeks
This commit is contained in:
1 parent
15ab8c686d
commit
03c355e7c1
26 files changed
+117
-70
No files matched your search
+48
-18
@@ -728,23 +728,48 @@ def _hood(name, mat, head_center):
|
||||
return _finish(_link(name, bm), mat, smooth=True, sharp_angle=60)
|
||||
|
||||
|
||||
def _shape_head_mesh(head):
|
||||
"""Sculpt a chin plate and cheekbones into the head sphere itself.
|
||||
|
||||
Separate jaw/cheek spheres leave visible intersection rings in the baked AO,
|
||||
which read as a beard shadow. Moving the head's own front vertices keeps the
|
||||
surface continuous while still giving the key light something to catch.
|
||||
"""
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(head.data)
|
||||
for v in bm.verts:
|
||||
x, y, z = v.co
|
||||
if y >= 0.0:
|
||||
continue
|
||||
ax = abs(x)
|
||||
# Chin/jaw plate: forward and slightly wider below the mouth.
|
||||
jaw = (max(0.0, 1.0 - abs(z - 1.565) / 0.055)
|
||||
* max(0.0, 1.0 - ax / 0.075))
|
||||
v.co.y -= 0.012 * jaw
|
||||
v.co.x *= 1.0 + 0.06 * jaw
|
||||
# Cheekbone: a shallow, wide lift under each eye.
|
||||
cheek = (max(0.0, 1.0 - abs(z - 1.610) / 0.040)
|
||||
* max(0.0, 1.0 - abs(ax - 0.072) / 0.050))
|
||||
v.co.y -= 0.006 * cheek
|
||||
bm.normal_update()
|
||||
bm.to_mesh(head.data)
|
||||
bm.free()
|
||||
|
||||
|
||||
def _face(mats, spec):
|
||||
"""Head with readable eyes, brow ridge, nose, lips and ears.
|
||||
|
||||
At conversation distance the eye region carries the read, so each eye gets a
|
||||
white sclera, a dark iris and an upper lid, and the face gets cheekbones and
|
||||
a nose bridge instead of a single cone (realism pass detail work).
|
||||
white sclera, a dark iris and an upper lid, and the face gets sculpted
|
||||
cheekbones and a nose bridge instead of a single cone (realism pass detail
|
||||
work).
|
||||
"""
|
||||
skin, acc, hair = mats["skin"], mats["accessory"], mats["hair"]
|
||||
white = material("eye_white", textured_solid=True)
|
||||
parts = [sphere("Head", 0.155, (0, 0, 1.635), skin, seg=18, rings=12,
|
||||
scale=(0.92, 1.0, 1.12))]
|
||||
# jaw / chin and cheekbones soften the egg into a face
|
||||
parts.append(sphere("Jaw", 0.10, (0, -0.04, 1.556), skin, seg=12, rings=8,
|
||||
scale=(1.0, 1.0, 0.8)))
|
||||
for s in (-1.0, 1.0):
|
||||
parts.append(sphere("Cheek", 0.034, (0.076 * s, -0.104, 1.606), skin, seg=8,
|
||||
rings=5, scale=(1.0, 0.7, 0.8)))
|
||||
head = sphere("Head", 0.155, (0, 0, 1.635), skin, seg=18, rings=12,
|
||||
scale=(0.92, 1.0, 1.12))
|
||||
_shape_head_mesh(head)
|
||||
parts = [head]
|
||||
# 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))
|
||||
@@ -756,20 +781,25 @@ def _face(mats, spec):
|
||||
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)))
|
||||
# eye: sclera, iris, upper lid and brow
|
||||
ex, ey, ez = 0.052 * s, -0.138, 1.664
|
||||
# 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.
|
||||
ex, ey, ez = 0.052 * s, -0.134, 1.664
|
||||
parts.append(sphere("EyeWhite", 0.019, (ex, ey, ez), white, seg=8, rings=5,
|
||||
scale=(1.25, 0.7, 0.85)))
|
||||
parts.append(sphere("Iris", 0.0105, (ex, ey - 0.013, ez), acc, seg=8, rings=5,
|
||||
scale=(1.25, 0.62, 0.85)))
|
||||
parts.append(sphere("Iris", 0.0105, (ex, ey - 0.007, ez), acc, seg=8, rings=5,
|
||||
scale=(1.0, 0.55, 1.0)))
|
||||
parts.append(box("Lid", (0.038, 0.010, 0.008), (ex, ey + 0.004, ez + 0.014),
|
||||
skin, rot=(math.radians(-10), 0, 0)))
|
||||
# One thin upper lid; no lower lid, which made the eyes read as
|
||||
# half-closed slits at conversation distance.
|
||||
parts.append(sphere("UpperLid", 0.020, (ex, ey - 0.006, ez + 0.014), skin,
|
||||
seg=8, rings=4, scale=(1.20, 0.32, 0.24),
|
||||
rot=(math.radians(-10), 0, 0)))
|
||||
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.145, 1.588), acc,
|
||||
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.143, 1.578), acc))
|
||||
parts.append(box("LipLower", (0.046, 0.012, 0.009), (0, -0.154, 1.578), acc))
|
||||
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))
|
||||
|
||||
Reference in new issue
Block a user