flora
The geometry the forest floor of *First Light: Goura* is made of.
game.floor.flora
The geometry the forest floor of First Light: Goura is made of.
local Flora = require("game.floor.flora")
Every generator returns a Geo — flat positions / normals / uvs /
colors / indices arrays in the shape renderer.mesh.create takes — in LOCAL
space. Flora.append(dst, src, xform) copies one into another, transformed,
which is how a frond becomes a fern and a fern becomes a bed.
The two channels that carry what the silhouette does not
- Vertex colour RGB multiplies the albedo.
fl_surfaceandfl_frondboth read it, so a trunk can darken toward its base and a log bleach along its top with no texture and no second material. - Vertex ALPHA is sway freedom on foliage — 1 at a free tip, 0 where the leaf is rooted — and ambient occlusion everywhere else, so a fern's crown sits in its own shadow.
UVs
u runs ACROSS a blade (0 at the spine, 1 at an edge) and v along it, which
is what fl_frond reads to darken a midrib and pale an edge. On a swept tube,
u goes round and v along the axis.
What is here
| Call | Makes |
|---|---|
new / count / tris / append | the geometry container and the merge |
vert / tri / quad / basis | the primitives the generators are written with |
mixColor / shade / rand | colour and the deterministic hash everything scatters with |
pathFrom(steps, at) | a swept path's nodes, tangents worked out from the curve |
tube(nodes, opts) | trunks, logs, buttress roots, stems — with ridges (lobes), twist, caps, and an arbitrary radiusAt for a buttress flare |
blade(opts) | one leaf: folded, drooping, tapering, along local +Z |
frond(opts) | a pinnate frond — a drooping stem with paired leaflets |
fernBed(opts) | a whole fern: fronds radiating from one crown |
tuft(opts) | blades radiating from a point around local +Y — a grass clump, a sapling's crown, one mass of canopy |
litter(opts) | fallen leaves lying flat on the floor at every angle |
blob(opts) | a lumpy mass — a moss mound, a bank |
ground(opts) | a grid displaced by a height field, painted by a colour field, with normals taken from the field itself |
Determinism
Flora.rand(n) is the only randomness. Every generator takes a seed and
threads it, so the same lane is laid out identically on every peer, after every
hot reload, and on both sides of an edit/play flip.
Instancing
Nothing here merges a whole lane into one mesh. A handful of prototypes are
built once and each is worn by many entities, which is what lets the world-space
noise in fl_surface / fl_frond do the differentiating: two copies of one
trunk mesh standing in different places shade differently because the noise is
read in world space, not in UV space.
Scoped to this part · feeds back into the world's score.