df_geom — the geometry bench the deep-water animals are built from.
The engine ships eight primitives (cube, sphere, cylinder, cone, torus,
capsule, plane, octahedron) and none of them is an eel. So the lower-water
animals are LOFTED: a profile of rings is swept along an axis or along a
path, welded into a closed surface, given smooth normals, and uploaded once
as a runtime GPU mesh that every sighting of that species reuses.
Three builders cover every body in this slice:
Geom.lathe(rows, radial) a profile revolved around +Y — bells, mantles, jaws
Geom.tube(stations, radial) a profile swept along a path — eels, arms, tentacles
Geom.blade(spine, radial) a flattened lathe — fins, webs, ctene plates
Meshes are memoised by key, so thirteen sightings of a comb jelly upload
ninety-six ctene plates ONCE and then only spawn transforms.
Slice owner: fauna-lower.
smoothNormals(positions: { number }, indices: { number }) → void
Normals
Area-weighted face normals accumulated per vertex. Positions are a flat
[x,y,z,...] array (1-based in Lua); indices are 0-based triangle corners,
which is the shape renderer.mesh.create takes.
| arg | type | description |
|---|
| positions | { number } | |
| indices | { number } | |
band(indices: { number }, lower: number, upper: number, radial: number) → void
Stitch the quad band between two consecutive rings of `radial` vertices.
The corner order is the one whose face normal points AWAY from the axis:
(a, c, d) then (a, d, b), with a/b on the lower ring and c/d on the upper.
| arg | type | description |
|---|
| indices | { number } | |
| lower | number | |
| upper | number | |
| radial | number | |
cap(positions: { number }, uvs: { number }, indices: { number }, ringStart: number, radial: number, y: number, sign: number) → void
A flat cap over one ring, wound so it faces along `sign` * +Y.
| arg | type | description |
|---|
| positions | { number } | |
| uvs | { number } | |
| indices | { number } | |
| ringStart | number | |
| radial | number | |
| y | number | |
| sign | number | |
lathe(rows: { any }, radial: number, opts: any?) → any
lathe — a profile revolved around +Y
`rows` runs bottom to top. Each row is { y = , r = } for a circular
section, or { y = , rx = , rz = } for an elliptical one. A row with a
zero radius closes the surface to a point, so a body that tapers to
nothing needs no cap.
Geom.lathe({ { y = -0.5, r = 0 }, { y = 0, r = 0.4 }, { y = 0.5, r = 0 } }, 16)
| arg | type | description |
|---|
| rows | { any } | |
| radial | number | |
| opts | any? | |
blade(spine: { any }, thickness: number, radial: number) → any
A flattened lathe: the same profile, squashed in Z. `spine` rows are
{ y = , w = } (half-width in X); `thickness` is the half-depth in Z at
the widest point. This is every fin, web, ear and ctene plate here.
| arg | type | description |
|---|
| spine | { any } | |
| thickness | number | |
| radial | number | |
tube(stations: { any }, radial: number, opts: any?) → any
tube — a profile swept along a path
`stations` are { x, y, z, r } (or rx/rz for a flattened section) in order
along the body. The frame is parallel-transported station to station, so a
path that curves back on itself does not spin its cross-section.
| arg | type | description |
|---|
| stations | { any } | |
| radial | number | |
| opts | any? | |
tangentAt(i: number) → void
| arg | type | description |
|---|
| i | number | |
fan(ringStart: number, st: any, sign: number) → void
Fan an end shut onto its own centre. A body that opens into a mouth
leaves the head end open and puts the gape geometry inside it.
| arg | type | description |
|---|
| ringStart | number | |
| st | any | |
| sign | number | |
profile(y0: number, y1: number, steps: number, f: (number) → void
Profile sugar
A run of rows sampled from a shape function: `f(t)` returns the radius at
t in 0..1, and the rows span y0..y1. Most bodies here are one of these.
| arg | type | description |
|---|
| y0 | number | |
| y1 | number | |
| steps | number | |
| f | (number | |
spindle(length: number, radius: number, steps: number, bias: number) → void
A spindle: fat in the middle, pointed at both ends, `bias` moves the
widest point toward the head (0) or the tail (1).
| arg | type | description |
|---|
| length | number | |
| radius | number | |
| steps | number | |
| bias | number | |
handle(key: string, fn: () → void
Upload once, reuse for every sighting. `fn` is only ever called on a miss.
| arg | type | description |
|---|
| key | string | |
| fn | ( | |
part(parent: any, name: string, key: string, fn: () → void
Spawn one piece of an animal: an entity carrying a memoised mesh and a
material. `opts` takes { shadows = bool } and nothing else.
| arg | type | description |
|---|
| parent | any | |
| name | string | |
| key | string | |
| fn | ( | |
chain(parent: any, prefix: string, n: number, len: number, r0: number, r1: number, material: string, radial: number?, meshKey: string?) → void
A chain of PARENTED links running along -X from `parent`, tapering from
`r0` at the head to `r1` at the tip. Because each link hangs off the one in
front of it, a few degrees of rotation per link compounds into a smooth
curve — which is how an eel undulates and how a squid's filament trails.
Returns the links head-first.
`meshKey` names the mesh family. Ten arms built with the same meshKey and
ten different entity prefixes share ONE set of link meshes; omit it and
each chain uploads its own.
| arg | type | description |
|---|
| parent | any | |
| prefix | string | |
| n | number | |
| len | number | |
| r0 | number | |
| r1 | number | |
| material | string | |
| radial | number? | |
| meshKey | string? | |
rig(root: any) → any
| arg | type | description |
|---|
| root | any | |
forgetAll( ) → void
Drop both caches. The GPU meshes are left alone — another sighting may
still be drawing one — so this is a rebuild-on-next-use, not a free.
travel(i: number, n: number, t: number, freq: number, lag: number) → number
Motion helpers
A travelling wave down a chain: segment `i` of `n`, at time `t`.
| arg | type | description |
|---|
| i | number | |
| n | number | |
| t | number | |
| freq | number | |
| lag | number | |
axisAngle(ax: number, ay: number, az: number, a: number) → void
Quaternion for a rotation of `a` radians about a unit axis.
| arg | type | description |
|---|
| ax | number | |
| ay | number | |
| az | number | |
| a | number | |
deg(r: number) → number
Euler (in radians, YXZ as the engine reads localEulerAngles in degrees)
is easier to read for a fin flap, so this is just a degree conversion.
| arg | type | description |
|---|
| r | number | |