Head - SethKitchen/ThreeMojo GitHub Wiki

Head

add_head attaches the neck and the head: the seven cervical vertebrae, the skull, the mandible, the teeth and the hyoid, and their joint tissues. It also attaches the muscles of the neck, the jaw and the face, and the head's vessels, nerves, lymph nodes, skin, hair and eyes.

For engineering use and representation limits, see Humanoid fidelity.

A six-foot male neck and head turn twice: bones, joint tissues and muscles on the left, and skin, hair and eyes on the right

The neck and the head share the pelvis frame. The origin is the midpoint of the two hip joint centers. Plus y is proximal, plus x is body-right and plus z is anterior. The package is extensions/humanoid/skeleton/head/. The neck stands on the first thoracic vertebra of the Torso.

This is not a three.js port. See Extensions.

A quality level sets how many triangles the body holds. See Mesh quality.

Call it

from extensions.humanoid.athleticism import TONED
from extensions.humanoid.sex import MALE
from extensions.humanoid.spec import HumanoidSpec
from extensions.humanoid.skeleton.head.assembly import add_head
from extensions.humanoid.skeleton.head.contents import ALL, BONES
from extensions.humanoid.skeleton.head.frame import head_dimensions
from units.si import FOOT, Length

var person = HumanoidSpec(Length(6.0, FOOT), MALE, TONED)
var dims = head_dimensions(person.stature, person.sex)
var atlas = dims.centers[0]

add_head attaches the neck and the head under a parent node. Hang them from the same node as the torso. Pass contents to pick the layers. The layers are the arm's, and EYES: see Arm. add_body attaches the head as well, with the torso's layers, and its one skin covers the head; see Torso.

Pass the spec's genome to head_dimensions to shape the head: head_dimensions(person.stature, person.sex, person.genome). See Genome.

A bare integer is a compile error, for a bone, a part or a layer.

Pose and size

The head sits on the atlas with the Frankfort plane level, so the eyes look straight ahead. The neck climbs from the first thoracic vertebra in a gentle lordosis.

On the six-foot male template, the top of the skull is 84.4 cm above the hip joint centers. The eyes are at 72.5 cm and the chin is at 60.5 cm. Stature scales every length. A female template is narrower and shallower, as the torso's is. The values are template parameters. They are not a cited anthropometric table.

A head keeps its size better than a body does. Across adults a head grows about as the square root of stature, so a short person's head is larger for their height. head_scale(stature, sex) gives the factor over what stature alone makes, and the head's frame grows the head by it about the base of the jaw. A woman's head gets a further 5%, and her neck is 10% slimmer than her frame alone makes it. The neck keeps its own size, so a shorter person's crown stands a little above the stature: about 2.5 cm at 1.63 m.

On a 1.75 m man the head measures about 56 cm round and the neck about 43 cm. On a 1.63 m woman they measure about 53 cm and 36 cm.

Bones

Part Bones
Cervical vertebrae C1, the atlas, to C7
Skull SKULL: the vault, the base and the face
Jaw MANDIBLE and TEETH, both rows
Throat HYOID

The atlas is a ring with two lateral masses. The axis carries the dens up through the atlas's front arch. C3 to C6 have short forked spines. The spine of C7 is long.

Vertebral bodies use flat endplates at the authored lower and upper y coordinates. Their full body height is the listed height; no rounded sweep cap extends into the disc space. Discs use horizontal elliptical sections between those endplates, including the C7–T1 junction. These template planes enforce a dimensional contract, not measured endplate anatomy. The dens, arches, processes and their smooth joins remain separate features. This change does not certify that all tissues are disjoint or suitable for contact or finite-element analysis.

An edited C2 through C7 body must be tall enough to contain the authored articular pillar diameter. head_bone_field refuses a shorter body. Equality is permitted: the pillar becomes one rounded station, with coincident segment endpoints. The canonical dimensions keep their existing pillar radius.

The vault is a thin dome of bone about seven millimeters thick. The face is one solid with the orbits and the nasal opening cut out. The mandible is a U of bone with a ramus and a condyle on each side. An alveolar arch on the crest of its body holds the lower teeth, as the maxilla's arch holds the upper teeth. The condyles sit in front of the ear canals. The occipital condyles sit on the atlas.

Joint tissues and cartilages

Part Role
CERVICAL_DISCS A disc under each body from C2 down, the last on T1.
NUCHAL_LIGAMENT A thin sheet in the midline, from the occiput down the spines to C7.
ATLANTO_OCCIPITAL_JOINTS The capsules between the occipital condyles and the atlas.
TEMPOROMANDIBULAR_JOINT The disc of one jaw joint. It is paired.
LARYNX The thyroid cartilage's two plates and the cricoid's ring.
TRACHEA The windpipe, down to the root of the neck.

Muscles

Group Parts
Neck Sternocleidomastoid, upper trapezius, splenius capitis, semispinalis capitis, levator scapulae, scalenes, longus colli.
Throat The infrahyoid straps and the suprahyoid floor of the mouth.
Jaw Masseter and temporalis.
Face Frontalis, orbicularis oculi, zygomaticus major and orbicularis oris.

The upper trapezius meets the torso's trapezius at the base of the neck. The levator scapulae reaches the scapula's superior angle. The suprahyoid floor and the orbicularis oris cross the midline. The rest are paired.

Vessels, nerves and lymph

The common carotid artery rises beside the trachea and forks at the level of the hyoid. The external carotid gives the facial artery and ends as the superficial temporal artery. The vertebral artery climbs through the transverse processes and loops over the atlas. The internal jugular vein runs down beside the carotid. The external jugular vein runs down across the sternocleidomastoid.

The cervical spinal cord runs up the vertebral canal. The vagus nerve runs down in the carotid sheath. The phrenic nerve runs down the front of the anterior scalene. The cervical plexus fans out from behind the sternocleidomastoid. The facial nerve fans forward through the parotid.

The deep cervical nodes lie in a chain along the internal jugular vein. The submandibular, parotid, occipital and supraclavicular nodes are small groups.

Skin and hair

The face is a scan. The skin of the head is the mean head of the ICT Face Model Light, a morphable model learned from scans of real faces. A sculpt of even solids makes a face that looks like a doll's. The scan brings the lids, the lips, the nostrils, the folds and the ears of a real face.

ScannedHead fits the scan to the anatomy, in extensions/humanoid/skeleton/head/skin/scan.mojo:

  1. The face shape genes weigh the scan's identity modes, so each face is a face of its own. The scan's eyes then land on the template's eyes. Each vertex then goes through the head's frame, as an authored point does. So the genome's face and head genes move the scan as they move the skull under it. See Genome.
  2. The ear genes warp the ears. Each ear grows about its root, its back edge stands out, and its lobe hangs lower.
  3. The scan is pulled over the modeled solids like a sleeve. The solids are the vault, an ellipsoid a scalp's thickness outside the skull, and the neck, swept round its muscles. Each vertex inside them must move out along its normal. The moves are spread over the mesh until they are smooth, so the mesh stretches and does not fold.
  4. The mouth and the eyes are open in the scan. The palate, the teeth and the orbits fill those spaces, so a fan of triangles closes each opening. The scan is kept as a MeshField, the signed distance to a mesh through a tree of boxes.

HeadSkinField is the smooth union of the scan and the modeled solids. This fit does not test every anatomical part for containment.

The skin's mesh is the scan's own mesh, not a mesh extracted from the field. Each vertex that the field's surface does not pass through is walked onto it. The mouth's and the eyes' sockets are drawn inside. Below the seam on the neck, 56.5 cm on the template, the skin is meshed by narrow-band surface nets, in extensions/humanoid/skeleton/surface_nets.mojo. The two meshes lie on one surface and overlap by a few millimeters. add_body joins the body's skin to the scan at the same seam.

tools/ict_face_model.mojo converts the model's OBJ files into assets/face/ict_face.bin. FaceModel reads it. The file keeps the mean head, its mesh, sixty identity modes and fifty-seven expressions, with the left and the right side apart. FaceModel reads only what you ask for, so a head that needs no expression loads in a few milliseconds.

Function Returns
FaceModel(path, identities, expressions) The model, or the part of it you ask for.
FaceModel.shape(identity, expression) Every vertex for one weight per identity mode and one per expression.
FaceModel.part(points, part) One part as a mesh: FACE_AND_HEAD, TEETH, LEFT_EYEBALL and the others.
MeshField(points, triangles) The signed distance to a mesh.
ScannedHead(h, model, hull) The scan fitted to one person, over the solids in hull.

tint_head_skin writes the face's zones of color into the mesh's color attribute. The lips are red. The cheeks, the nose's tip and the ears are a little redder than the forehead. The lids and the skin under the eyes are darker, and the lashes darken the lids' margins. A man's jaw and upper lip carry the gray-blue of a beard under the skin.

tint_head_skin also writes a thinness attribute. skin_scatter reads it, so light behind an ear, a nostril or a lip shows through it, deep red, as three.js's MeshSSSNodeMaterial draws it.

Eyes

EYES draws the two eyeballs. Each is a sphere about 24 mm across on the six-foot template, with the cornea proud of it at the front. The lids are a shell round the front of each eyeball, open in an almond-shaped slit. iris_albedo maps the pupil, the iris with its fibers and its limbal ring, and the white sclera. eye_physical gives the wet cornea a strong clear coat, so the eye catches a glint.

An eyeball goes where its lids go. The face's genes move the lids and the eye's center by different amounts. So eye_center takes the center from the ring where the lids rest on the eyeball. No point of the ring lies behind the eyeball, so the eye never stands out in front of its lids.

Function Returns
eye_center(dimensions, side) The center of one eyeball, in meters.
eye_radius(dimensions) Its radius, scaled by stature and EYE_SIZE.
eyeball_mesh(dimensions, side, detail) Its mesh, with texture coordinates from the front pole.

add_head draws the eyes when contents has EYES. ALL has it. add_body draws the eyes and the scalp's hair with the skin.

Expressions and speech

A head can smile, frown and talk. Rig it once, then set its weights each frame.

from extensions.humanoid.skeleton.head.contents import EYES, MOUTH, SKIN
from extensions.humanoid.skeleton.head.expression import (
    SMILE, FaceWeights, face_rig_shapes,
)
from extensions.humanoid.skeleton.head.speech import Speech

var first = len(scene.meshes)
_ = add_head(..., contents=SKIN.plus(EYES).plus(MOUTH),
             face_shapes=face_rig_shapes())
var speech = Speech("Hello there!")
var face = FaceWeights()
face.add_expression(SMILE, 0.6)
speech.speak(face, time)
for m in range(first, len(scene.meshes)):
    if len(scene.meshes[m].morph_target_dictionary) > 0:
        face.apply(scene.meshes[m])

face_shapes names the face model's blend shapes to rig, as jawOpen or mouthSmile_L. The face model is ICT FaceKit, and its shapes are Apple's ARKit set. add_head gives the skin one morph target per shape. Each target moves the scanned face as the model's shape does, placed on the person as the face at rest is. The targets carry normals, so a smile shades as it creases.

The targets fade to nothing at the neck's seam, so the face never parts from the neck. Building the head costs the same. A frame only sets weights.

MOUTH draws the model's own teeth, gums and tongue, rigged with the same shapes. The jaw opening drops the lower teeth and the tongue with the chin. teeth_physical and gum_physical are their looks.

Expression The face
NEUTRAL At rest.
SMILE The corners of the mouth up, the cheeks raised, the eyes narrowed a little.
FROWN The brows drawn down, the corners of the mouth down, the lips pressed.
SADNESS The inner brows raised, the corners of the mouth down, the chin pushed up.
SURPRISE The brows up, the eyes wide, the jaw dropped.
ANGER The brows down, the eyes narrowed, the lips pressed hard.
DISGUST The nose wrinkled and the upper lip raised.
FEAR The brows up and together, the eyes wide, the lips stretched.

Each expression is a recipe of shapes from the Facial Action Coding System. A smile is action units 6 and 12, for example. expression_recipe returns it.

A Viseme is the shape the mouth takes for a group of sounds that look alike. The fifteen are the Oculus Lipsync set, from SILENT and PP to OU. The model has no tongue shapes, so the sounds the tongue makes show as the jaw and the lips do. viseme_recipe returns each recipe.

FaceWeights adds expressions, visemes and blinks. apply holds each weight to zero through one and sets it on a mesh. So a face can smile while it talks.

Speech(text, rate) reads English text by its spelling. Pairs of letters such as "th", "ch" and "oo" come first. A silent final "e" and an "h" on its own make no shape. A vowel is held 0.13 s, a consonant 0.075 s, a space 0.06 s and a phrase's end 0.32 s, all divided by rate. speak(face, time) adds the visemes at time. Each viseme ramps in and out over 0.035 s either side of its time, so the mouth passes through both shapes between two sounds.

Spelling is not sound in English. A word spelled far from how it sounds moves the mouth as it is spelled.

For caller-aligned phonemes or visemes on a game character, use Audio-aligned game faces. That path uses the supplied audio clock. The spelling-based Speech behavior above is unchanged.

Hair

The scalp's hair is strands over a shell. add_groom grows the strands. The shell under them is the mass of hair in shade.

The shell lies over the skin itself: about seven millimeters deep at the sides and a centimeter on the crown. It follows every head a genome makes. Its hairline is level across the middle of the forehead, set back at each temple, and turns down past the temples to the sideburns. A male's temples are set back more than a female's. The shell is cut away round each ear and off above the nape.

Strands

add_groom(scene, assets, parent, spec, guides, followers) grows a groom, the way grooming tools grow one:

  • Guide strands grow from roots on the shell. Each lies along it, combed away from the crown's whorl and pulled down as the hair grows longer. A guide at the front can fall off the hairline onto the forehead as a fringe. It stops at a ragged line above the brows.
  • Follow strands fill in round each guide, as AMD's TressFX makes them. Each keeps an offset from its guide that widens toward the tip.
  • Clumping pulls each follower back toward its guide at the tip, so the hair gathers into locks. Frizz moves each tip a little.

Each strand uses a feathered LineSegments2 ribbon, unlit, in colors worked out at its points. Its sample coverage is hashed, with opacity 0.65 by default. A kept sample writes depth. A rejected sample shows what is behind it. The same rule shapes its cast shadow. Ordinary wide lines keep their solid three.js behavior.

See Strand hair rendering for the coverage rule, motion fields, reference scenes, tests and hardware qualification limits.

The colors are strand-space shading, as Frostbite's hair works it out. Kajiya and Kay's diffuse and Marschner's specular read the strand's direction, not a normal. Marschner's R highlight reflects white off the fiber; TRT passes through it and comes back in the pigment's color. Light is also lost with depth into the hair, as the Beer-Lambert law has it. Call HairStrands.shade when the head turns or the camera moves: the highlights move with them. The shading is ported from Frostbitten Hair WebGPU, and the follow strands from AMD TressFX.

Light that diffuses through a fiber crosses it twice, so the pigment tints the diffuse twice. Pale hair then stays golden and does not wash out to white.

Wind

The hair moves. HairSimulation(groom) makes every point of every strand a particle. step(collider, wind) moves them a thirtieth of a second. Pass the groom to write to move its strands and refresh its normals and scalp-depth proxy. Then call HairStrands.shade to rebuild the moving density field, shade the strands and update their shared geometry storage. Each step works as position-based dynamics does:

  1. Each point but the root carries on as it moved, less friction, under gravity and the wind.
  2. Each segment keeps the length it was groomed at.
  3. Each point is pulled back to where it was groomed: fully near the root and less toward the tip, so the style holds.
  4. Each run of three points keeps its bend.
  5. A point inside the body is pushed out of it.

Pass a HairBody as the collider. The skins' own fields are too slow to read for every point, so it bakes them into grids of distances:

  • The head's skin, into a fine grid. Above the chin only the head counts, so the hair on the scalp lies smooth.
  • The body's skin, from the middle of the back up, into a coarser grid. Below the neck the body counts, so long hair falls on the shoulders and the back.

HairCollider bakes any other field. HairWind blows one way, in gusts. Each strand's strength flutters, out of phase with its neighbors'. The simulation is ported from Frostbitten Hair WebGPU, with its defaults. Its grids of density and velocity are left out.

Hairstyles

A HairStyle says how the hair is cut and laid. Pass the same style to add_groom as style and to add_head or add_body as hair_style.

Style The hair
GROWN The groom above, combed down to the length HAIR_LENGTH asks for. The default.
LAYERED Sintel's hair: a layered cut to the jaw, with a fringe. From Sintel Lite by BenDansie, (c) the Blender Foundation, CC-BY 3.0.
MOHAWK A crest from the brow to the nape, and shaved sides. From AMD TressFX's Ratboy, MIT license.
LONG Parted down the middle, and falling 34 to 50 cm, past the shoulders.
PONYTAIL Combed back to a tie at the back of the head. A tail of 20 to 32 cm springs back from the tie and falls.
BUN Combed up to a tie at the top of the back of the head, and coiled round a ball on it.
HIGH_PONYTAIL Combed up to a tie high on the crown. A tail of 26 to 38 cm springs from it.
PIGTAILS Parted down the middle and tied behind each ear. Each tail is 16 to 26 cm.
SPACE_BUNS Parted down the middle and coiled into a small bun on each side of the crown.
BRAID Combed back to a tie at the nape, and woven into one braid of three strands, 30 to 36 cm long.
HALF_UP The hair above the temples is tied in a short tail at the back. The rest falls long.
BOB Parted down the middle and cut level at the jaw.
PIXIE Cropped short, 3 to 6.5 cm, and combed forward and down.

An artist groomed LAYERED and MOHAWK. tools/hair_style.mojo converts their TressFX files into assets/hair/.

The groom designs the other ten on its own guides. Each guide grows from a root on the scalp's hair:

  • Loose hair walks over the hair away from the part and down, to the ears. Then it hangs to its length and sways a little. A bob's hangs only to its cut.
  • A pixie's walks forward and down, as far as it is long.
  • Tied hair follows the arc from its root to its tie, round the cranium's middle. A style with a tie on each side takes the nearer one. Hair from the brow goes up over the crown, and hair from the temple goes back over the ear. The arc arches up a little in its middle, so the hair clears the ear.
  • Past the tie, a tail springs back and falls. A bun climbs over its ball and coils round it. A braid falls in three lanes, each a third of a turn behind the next, which cross over one another.

Each strand is then lifted out of the head's skin and the body's grid. A lifted point carries the rest of its strand with it, so long hair drapes over the shoulders and does not break. Tied hair is pulled taut over the scalp: it curls only past the tie, and its shell does not stand fuller for the curl. Every designed style keeps a thin shell, so the strands are what shows. A tied style's roots reach down to the nape, which shows when the hair is pulled up.

examples/hairstyles.mojo draws one woman in every style and writes out/hairstyles.png. Each head is seen from behind and to one side.

A style keeps no head of its own. Each root is a point of a unit cranium, and each strand is kept as offsets from its root in the cranium's frame there. So a style fits every head a genome makes. HairStyleFile.strand puts a strand on a person's cranium. The groom then walks its root onto the skin and lifts any point of it that would pass under the skin. The follow strands, the clumping and the shading are the grown hair's.

A mohawk's shell covers only a strip along the midline, so the sides are bare.

HAIR_CURL curls the hair of every style. Zero and below is straight. A third is wavy, two thirds curly, and one tightly coiled.

Each guide is sampled at eight points a turn and wound round its own line. It swings across the hair and out off it, never in under it. The swing grows in over the first half turn, so the root stays where it grew. The tighter the curl, the shorter each turn and the fuller the hair stands off the head. See Genome.

HAIR_LENGTH below zero crops it close. Above zero it grows a fall that hangs over the ears and the nape toward the jaw, open over the face. hair_albedo maps its strands. Its mass is the volume of a dome over the cranium times HAIR_PACKING, one tenth, for the air between the shafts.

Each eyebrow is an arc on the skin over its orbit, thick at its head and thin at its tail. BROW_THICKNESS makes it fuller or finer. add_head paints the brows into the skin's colors, hair by hair, in the hair's color. A solid strip stands off the curve of the brow ridge, so the brows are not a mesh. head_hair still meshes one, and its mass is its volume.

Examples

examples/expressions.mojo draws one face in each of the eight expressions and writes out/expressions.png. examples/talking.mojo writes out/talking.png, an animation: a woman says four phrases, and her face greets, smiles, frowns and is surprised.

examples/head.mojo draws a six-foot male neck and head twice and writes out/head.png. The left copy shows the bones, the joint tissues and the muscles. The right copy shows the skin, the hair and the eyes. The program also prints the mass of several head parts.

.venv/bin/mojo run -I . examples/head.mojo out/head.png