figures.clown — the figure itself: his proportions, his paint, his meshes.
There is exactly ONE figure design in this film, because the whole horror of
it is that there is exactly one. The performer on the stage and every member
of the audience are built from the parts in here, and the greasepaint they
are all wearing is one generated texture set that every one of them samples.
Two consumers, one design:
* THE PERFORMER is an entity hierarchy — pelvis, torso, neck, head, two
arms, two legs, two eyes that move independently of each other — so a
component can walk him on, bow him, and blink him out of sync with
himself for the 18-degree macro of shot 6.
* THE AUDIENCE is three merged meshes (torso-with-arms, head-with-face,
one gloved hand) drawn hardware-instanced 1,412 times each, posed
entirely by `crowd_pose.computeShader`. Merged, because one draw call per
part is what makes 1,412 of him affordable at all.
The paint is generated, not painted by hand: a cracked greasepaint base with
a matching normal and roughness map, so at 45 cm the cracks catch the
footlights and at 30 m the same texture reads as a pale smudge.
hash2(x: number, y: number) → number
| arg | type | description |
|---|
| x | number | |
| y | number | |
vnoise(x: number, y: number) → number
| arg | type | description |
|---|
| x | number | |
| y | number | |
fbm(x: number, y: number, oct: number) → number
| arg | type | description |
|---|
| x | number | |
| y | number | |
| oct | number | |
crackField(x: number, y: number) → number
A ridged field whose crests are thin filaments: the crazing in dried
greasepaint over a face that has been made up and made up again.
| arg | type | description |
|---|
| x | number | |
| y | number | |
smoothstep(e0: number, e1: number, x: number) → number
Reversed edges (e0 > e1) are a ramp that falls, not a hard step: several of
the paint's own gradients run downward and a clamped denominator turns every
one of them into a visible seam across the face.
| arg | type | description |
|---|
| e0 | number | |
| e1 | number | |
| x | number | |
toV(y: number) → number
Height on the head (metres, origin at the cheekbone line) -> texture v.
| arg | type | description |
|---|
| y | number | |
toY(v: number) → number
...and back, which is what the generator walks the image with.
| arg | type | description |
|---|
| v | number | |
faceDist(u: number, v: number, cx: number, cy: number, rx: number, ry: number) → number
Distance from (u,v) to a feature centred at (x, y) METRES on the face, scaled
by that feature's half-width and half-height in metres. 1.0 is its edge.
| arg | type | description |
|---|
| u | number | |
| v | number | |
| cx | number | |
| cy | number | |
| rx | number | |
| ry | number | |
paintTextures( ) → void
Build the greasepaint base-colour, normal and roughness/metallic maps in one
pass. Returns three TextureHandles.
The whole face is drawn here — none of it is a hand-painted asset. It is a
base of caked white greasepaint gone sallow, and green-grey at the edges
where the theatre's own bloom has got into it; crazed all over with the fine
cracks paint gets when it has been put on over yesterday's paint; two black
lozenges thumbed over the sockets; brows drawn far too high; and a smile
painted a long way past the mouth with its corners carried up into the
cheeks. That smile is the only saturated red in the film until Act IV, which
is why it is worth generating rather than approximating.
paintAt(base: buffer, u: number, v: number) → void
What the generator put at one point on the face, in 0..255 RGB, straight out
of the buffer it just filled. The paint is the whole slice, and a face that
comes out wrong on screen has to be told apart from a face that was drawn
wrong in the first place — this answers the second half without a GPU in the
way.
| arg | type | description |
|---|
| base | buffer | |
| u | number | |
| v | number | |
limb(dst: ?, a: ?, b: ?, r0: number, r1: number, segs: number?, opts: ?) → void
A limb between two points, as a tapered tube. `a` and `b` are in the
destination geometry's own space.
| arg | type | description |
|---|
| dst | ? | |
| a | ? | |
| b | ? | |
| r0 | number | |
| r1 | number | |
| segs | number? | |
| opts | ? | |
torsoGeom(lod: string?) → void
The torso: a suit two sizes too big, gathered at the waist, with the collar
opening the ruff sits in. Origin at the hip.
| arg | type | description |
|---|
| lod | string? | |
headGeom(lod: string?) → void
The head, lathed off `Clown.HEAD_PROFILE` and mapped into the part of the
texture above the swatch band. `lod` is how many copies of it are about to be
drawn: the performer's own head is worth 44 segments at 45 cm on an
18-degree lens, and 1,412 of them at thirty metres are not.
| arg | type | description |
|---|
| lod | string? | |
noseGeom(lod: string?) → void
The nose. Its own mesh so it can carry the one saturated red in Acts I-III.
| arg | type | description |
|---|
| lod | string? | |
hairGeom(side: number, lod: string?) → void
A tuft of hair over the ear: six locks that go OUT from the skull and then
fall, the way an Auguste's bald crown and side-hair does. Out and down, not
up — up reads as horns, which is a different film.
| arg | type | description |
|---|
| side | number | |
| lod | string? | |
handGeom( ) → void
A gloved hand: a long baggy cuff swallowing the wrist, and a mitt. Origin at
the mitt's centre, which is the point the compute shader places.
shoeGeom( ) → void
A shoe: far too long, turned up at the toe.
ruffGeom(lod: string?) → void
A ruff at the throat, pleated. Two-sided, because it is seen from under as
often as from over.
| arg | type | description |
|---|
| lod | string? | |
crowdTorsoGeom(lod: string?) → void
One mesh per moving piece, because one draw call per piece is what makes
1,412 of him affordable. The arms are baked into the torso in a hands-at-the
chest attitude, ending inside the cuff the hand mesh carries, so the two
meet however far the clap has thrown them apart.
| arg | type | description |
|---|
| lod | string? | |
limbDownGeom(r0: number, r1: number, len: number, segs: number, opts: ?) → void
Built pointing DOWN from the joint, which is the rest pose a leg and an arm
hang in, so a joint entity's pitch about X swings it forward and back and
nothing has to compose a rest rotation with an animated one.
| arg | type | description |
|---|
| r0 | number | |
| r1 | number | |
| len | number | |
| segs | number | |
| opts | ? | |
crowdHeadGeom(lod: string?) → void
| arg | type | description |
|---|
| lod | string? | |