luvcraft/birthday/gnomes.lisp
--------------------------------------------------------------------- The dance, as knobs
Like the gnome's proportions, each number below stands in the shader source by name and folds to a literal, so tuning a step rebuilds the troupe's pipeline rather than writing a uniform. The amplitudes are small on purpose: a four-year-old should giggle, and a gnome bouncing his own height stops being a gnome and starts being a hazard.
Hops per second, before each gnome's phase spreads the troupe out.
The frame clock wraps at 3600 seconds; at 1.4 that is a whole number of hops, so the hour turns over without a stumble.
How much the hop squashes and stretches him: tall near the top of the hop, flat at the landing, cross-section compensating either way so he keeps his volume.
The side-to-side lean, in radians, about his own forward axis. It runs at half the hop tempo, so he leans the other way on every second landing.
Pirouettes per second: how fast the whole figure yaws. Slow on purpose; the turn is for showing the hat from every side, not for spinning.
--------------------------------------------------------------------- Dance variants of the gnome's field
The originals answer for one upright fellow and will not bend, so the
two functions below wrap gnome-distance and its tetrahedron normal in
the one non-isometric part of the warp. WIDE and TALL are the squash's
axis factors; LIMBER, the smaller of them, scales the answer back to an
honest lower bound so the march never oversteps the compressed axis.
gnome-distance through the squash-and-stretch.
The sample point is divided back into the unsquashed body; the answer is a distance in the warped space only after LIMBER shrinks it.
The tetrahedron normal over the squashed field.
Differentiating the warped field directly keeps the normal consistent with
the surface the march actually found: the constant LIMBER factor can only
shrink the gradient, never turn it, and normalize forgets it.
gnome-albedo with the robe and hat dressed for a party.
The same five parts answer for the same materials -- shape and colour still cannot drift apart -- but the hat wears HUE and the robe its near-complement, toned back toward the garden palette so the troupe is festive rather than fluorescent. Beard, skin and boots keep their own colours: it is a party, not a costume change.
The running nearest-so-far, folded by hand as in gnome-albedo.
--------------------------------------------------------------------- The pipeline stages
The vertex stage is the gnome's billboard with one more lane riding through: (scale, phase, hue, spare) per instance, beside the bounding sphere the proxy is sized by.
The beat: the frame clock at the troupe's tempo, this gnome's phase ahead, so the troupe ripples instead of drilling.
Squash and stretch, volume held roughly constant: stretched near the top of the hop, squashed at the landing.
The pirouette. The stock gnome always faces the camera; this one faces the camera only on average, yawing slowly through the whole circle with his phase deciding where he started.
The figure frame, yawed in the ground plane. Both basis vectors are horizontal and unit, so the turned pair stays orthonormal.
Per-instance stature: the knob times this gnome's own scale,
entering once, exactly as in the stock fragment. He stands with
his feet at figure y zero; the instance centre is the middle of
his bounding sphere, 0.85 above them (*GNOME-BODY-CENTRE*).
Bounding-sphere entry and exit, analytically as ever. The CPU sized the sphere to hold the whole dance -- hop, lean, stretch -- so the march can trust it.
The hop and the sway are rigid motions, so their inverses fold into the camera and the ray once -- un-lift, then un-lean about the feet -- and the march runs in the body's own swaying space at no cost per step.
The hat's domain warp plus the squash make this field a little less metric than the stock gnome's; the step keeps a wider margin of honesty.
The unsquashed body point, where the parts and the face live.
The normal, back out through the warp: differentiated in swaying space, rotated forward through the lean, then through the yawed basis into the world.
The lighting is the stock gnome's, verbatim: he is the same fellow, just busier.
Premultiplied, like the scene target: misses leave no rectangular trace of the conservative billboard.
--------------------------------------------------------------------- The troupe overlay: one pipeline, one instance buffer, one draw.
The bounding radius, in figure units, of one dancing gnome mid-move.
gnome-figure-radius answers for the fellow standing still; the dance adds
the hop's lift, the sway's sweep of everything up to the hat's tip, and the
stretch of the squash, each taken at its extreme. Conservative on purpose:
the proxy edge does not blur what it cuts, it slices it flat.
Two vec4 lanes per gnome: (centre.xyz, radius) and (scale, phase, hue, 0).
The plist's :X :Y :Z place his feet in world cells; the record's centre is
the middle of his bounding sphere, *GNOME-BODY-CENTRE* figure units above
them at his own stature. Phase and hue default to golden-ratio spreads so
an unadorned troupe still arrives as individuals rather than as a drill
team in uniform.
Every dancing gnome in one instanced draw. The instances were written once when the troupe arrived; the dance itself lives in the shader.
Call in the troupe: draw gnomes dancing in session as one overlay.
gnomes is a list of plists (:x :y :z :scale :phase :hue): feet position in
world cells, stature multiplier, dance phase in turns, and hue in turns
around the party colour wheel; scale, phase and hue may be omitted. Any
troupe already dancing in session is relieved first. Returns the overlay,
or NIL when gnomes is empty.
Send the troupe home: release session's dancing-gnome overlay, if any.
(session gnomes)Call in the troupe: draw GNOMES dancing in SESSION as one overlay. GNOMES is a list of plists (:x :y :z :scale :phase :hue): feet position in world cells, stature multiplier, dance phase in turns, and hue in turns around the party colour wheel; scale, phase and hue may be omitted. Any troupe already dancing in…
The whole fellow: five parts welded with fillets of different sizes. The robe-to-head weld is generous because a gnome has no neck to speak of; the hat sits on the brim with almost no fillet at all, because a hat is worn rather than grown.
(session)Hops per second, before each gnome's phase spreads the troupe out. The frame clock wraps at 3600 seconds; at 1.4 that is a whole number of hops, so the hour turns over without a stumble.
How high the hop lifts his feet, in figure units.
How much the hop squashes and stretches him: tall near the top of the hop, flat at the landing, cross-section compensating either way so he keeps his volume.
The side-to-side lean, in radians, about his own forward axis. It runs at half the hop tempo, so he leans the other way on every second landing.
Pirouettes per second: how fast the whole figure yaws. Slow on purpose; the turn is for showing the hat from every side, not for spinning.
Construct a meaningful literal.
Compare compatible quantities and produce dimensionless values.
((name symbol) (type (eql 'arithmetic-operator)))(name (&key label (group :grading) documentation
(class ''scalar-knob)
quantity (type 'single-float)
unit-label minimum maximum step)
place)Define NAME as a knob of CLASS over the setf-able PLACE. PLACE may refer to SESSION, so a knob can live on the session -- its sky clock, its player -- as well as in a special. QUANTITY is a quantity plist as DEFINE-QUANTITY-CONSTANT takes, (:quantity ... :unit ...); it and TYPE make the knob's declaration, which…
(name parameters &body body)Define a reusable typed shader expression with ordinary source syntax. The body is parsed at each call site, so argument types and quantity meanings flow through the same operator protocol as handwritten shader expressions. LET* is lexical inside the function. The definition macro records source; its body does not…
GNOME-DISTANCE through the squash-and-stretch. The sample point is divided back into the unsquashed body; the answer is a distance in the warped space only after LIMBER shrinks it.
Multiplication and scalar scaling.
Division of two represented quantities.
Select and reorder vector components by a designator such as :XYZ or :RGB.
The tetrahedron normal over the squashed field. Differentiating the warped field directly keeps the normal consistent with the surface the march actually found: the constant LIMBER factor can only shrink the gradient, never turn it, and NORMALIZE forgets it.
Normalize a dimensionless vector.
Addition over compatible quantities.
A festive colour on the hue wheel, HUE in turns. Three phases of one cosine: as cheap as a rainbow gets, and it never leaves [0,1].
Subtraction or unary negation.
GNOME-ALBEDO with the robe and hat dressed for a party. The same five parts answer for the same materials -- shape and colour still cannot drift apart -- but the hat wears HUE and the robe its near-complement, toned back toward the garden palette so the troupe is festive rather than fluorescent. Beard, skin and…
The nearest part decides the colour. A muted, slightly earthy palette on purpose: a scarlet hat over a slate robe reads as someone who has been standing in a garden for a long time, where saturated primaries would read as a toy.
The robe, flaring to a hem that rests on the ground, and two mitten hands. The hem stops just above his boots. An earlier version ran the cone's lower cap down past the feet, and the terrain cut it off in a dead straight line: he looked less like he was standing on the world than pasted onto it.
Head and nose. Most of the head is under the hat or behind the beard; the nose is the part of him you actually see, so it is a frank sphere.
A broad beard with a blunt point: the mass that gives him his gravity. Its top edge is the lower bound of his face, so it sits just under the nose: enough beard to be venerable, not so much that there is nobody behind it.
The tall cone and the brim it sits on. The brim rides high, near the crown rather than at eye level. A brim low enough to be practical would put his whole face in shadow, and a gnome whose eyes you cannot find is not a friendly little fellow but a bollard.
Interpolate compatible quantities by a scalar amount.
The minimum of compatible quantities.
Produce dimensionless progress across compatible edges.
Eyes, brows and a little colour in the cheeks, painted on the skin only. The features are placed by *direction* from the centre of the head rather than by distance to a point in space. Placing them by point failed: the welded surface stands a fillet's width outside the head sphere the markers were measured against,…
(generic-function name specialized-lambda-list options &body body)Define a shader-producing method with ordinary DEFMETHOD identity. Calling the method reparses its small source form so changes to source-level abstractions participate in live rebuilding. Method replacement remains the role/stage identity watched by the MOP; abstraction revisions are tracked separately by live…
(role stage)Return the current durable shader specification for ROLE and STAGE.
Expose a quantity's raw representation.
The inner product of two vectors.
The maximum of compatible quantities.
The componentwise absolute value of a raw value.
The componentwise square root of a raw value.
Test whether one compatible scalar is less than another.
Test whether one compatible scalar is greater than another.
Constrain a quantity between compatible bounds.
Raise a dimensionless value to a dimensionless power.
()The bounding radius, in figure units, of one dancing gnome mid-move. GNOME-FIGURE-RADIUS answers for the fellow standing still; the dance adds the hop's lift, the sway's sweep of everything up to the hat's tip, and the stretch of the squash, each taken at its extreme. Conservative on purpose: the proxy edge does not…
()The radius, in figure units, of a sphere about *GNOME-BODY-CENTRE* that holds every part of the gnome the knobs are currently asking for. The billboard is a conservative proxy and nothing outside it is drawn, so this has to be recomputed rather than assumed: a hat raised past a fixed radius does not overflow, it…
Convert one scalar float or unsigned value to a 32-bit float.
How high the hat's tip stands above his feet.
(gnomes)Two vec4 lanes per gnome: (centre.xyz, radius) and (scale, phase, hue, 0). The plist's :X :Y :Z place his feet in world cells; the record's centre is the middle of his bounding sphere, *GNOME-BODY-CENTRE* figure units above them at his own stature. Phase and hue default to golden-ratio spreads so an unadorned troupe…
A small SDF-rendered garden gnome carrying a WORLD-AGENT.
The non-negative remainder of integer division.
The height above his feet of the centre of the gnome's bounding sphere. The shader's marcher agrees with this number; moving one moves both.
Every dancing gnome in one instanced draw. The instances were written once when the troupe arrived; the dance itself lives in the shader.
(focus session)Return a non-negative targeting score when FOCUS can be entered by TAB.
(overlay)The live shader pipelines OVERLAY owns, so the session can count them among its own; none by default.
Logical conjunction of tests and raw truth values.
(overlay session pass surface-texture)Encode OVERLAY into SESSION's open scene PASS for SURFACE-TEXTURE.
An explicitly owned AVFrame.
(pass-encoder pipeline)(pass-encoder slot buffer &key (offset 0))(pass-encoder index bind-group)(pass-encoder vertex-count
&optional (instance-count 1) (first-vertex 0) (first-instance 0))(overlay)Release resources owned by an object attached to luvcraft.
(artifact)(handle)Logically invalidate HANDLE immediately. Native teardown may be deferred until submitted work which captured HANDLE has completed.
(device descriptor)Asks the DEVICE for a handle to newly created instance of some object fulfilling the DESCRIPTOR.
(&key role (stage :fragment) vertex-role label device layout vertex-module
vertex-buffers target-format target-blend primitive depth-stencil)Create and install a live mathematical render pipeline. VERTEX-MODULE is borrowed when VERTEX-ROLE is NIL and STAGE is not :VERTEX. Every module compiled from ROLE or VERTEX-ROLE is owned by the returned artifact and retired on replacement or explicit release.
The linear HDR attachment format shared by every scene-stage pipeline.
(session overlay)Attach OVERLAY at SESSION's next native frame boundary and return it. ADD consumes a newly offered OVERLAY on both success and terminal rejection. Once SESSION begins stopping, the rejected overlay is released exactly once before APPLICATION-ATTACHMENT-CLOSED is signalled.
(session overlay &key (release-p t))Detach OVERLAY at a frame boundary and optionally release it there. The no-release path is intentionally preserved for FUSE-LUVCRAFT-OVERLAY: that path may run inside a frame whose command stream still borrows OVERLAY.
Dancing gnomes: the agent's SDF body, multiplied and set to music.
Contract: (
add-dancing-gnomessession gnomes) registers one scene overlay drawing every gnome as an instanced SDF billboard, reusing the gnome part functions from luvcraft/agent/shaders.lisp (gnome-distanceand friends live in #:luvcraft.shaders; define any dance variants here rather than editing that file). GNOMES is a list of plists (:x :y :z :scale :phase :hue); each gnome dances -- bouncing, swaying, slowly turning -- driven by the frame clock offset by its phase, and its hue shifts the robe and hat so the troupe reads as individuals. (remove-dancing-gnomessession) releases the overlay.The CPU's whole contribution is one instance buffer written once: feet, radius, scale, phase and hue per gnome. Everything that moves, moves in the fragment shader, on the bounded frame clock the sky's clouds already drift by. The hop and the sway are rigid motions folded into the ray before the march starts; only the squash-and-stretch touches the field itself, and it pays for that with a shorter, humbler step.