Procedural LAYOUT + authored TILES: the AI decides where things go, Kenney's
artwork decides how it looks. Beats both random prop scatter and purely
procedural geometry, and it is how low-poly games are actually made.
ADJACENCY HOLDS BY CONSTRUCTION, not by rules. Rather than pairwise rules
between named roles (fragile and quadratic), layout marks occupied cells, each
cell reads its target mask off its occupied NEIGHBOURS, and a tile is chosen
matching that mask at some rotation. Both sides of every shared edge derive
from the same grid, so only rotation arithmetic can be wrong — and that is
what the tests pin. Junction type is never specified by a caller: two crossing
paths yield a crossroad, one teeing in yields a T, purely from neighbour count.
The interface deliberately keys on a 4-bit N/E/S/W `mask`, not on `role`, so
these algorithms don't depend on the asset index's filename taxonomy — if a
kit classifies `road-split` oddly, setting the mask keeps everything working.
Incomplete kits fall back to a superset tile: a crossroad standing in for a
missing tee leaves a stub opening onto nothing, which reads as unfinished road
rather than a hole in the world.
Generators: road_network (polylines), road_from_spline (the authored-tile
counterpart to the existing ribbon mesh — a kart track wants the ribbon, a city
street wants tiles), town (street grid, buildings on lots that front and face
a street, props at junctions), dungeon (BSP rooms + corridors, connectivity
guaranteed by the spanning tree and PROVED by flood fill over 12 seeds), plus
place_tile as the escape hatch.
track from closed spline 13 us 120 tiles
road network (13 paths) 32 us 397
town 24x24 71 us 547
town 60x60 822 us 2710
dungeon 48x48 137 us 1180
dungeon 96x96 932 us 3616
Town road histogram: 1248 straight, 121 cross, 44 tee, 4 corner, 0 dead ends —
correct for a closed grid. Zero mismatched edges on both large levels.
Two bugs caught by its own tests: indexing one kit with another kit's
placement indices (now impossible — layers merge by kit id, invariant
documented), and a superset-fallback that allocated a Vec per cell and tripled
generation time. The allocation-free count-then-pick rewrite is faster than
before the fallback existed: dungeon 96x96 went 1952 us -> 932 us.
Seed-deterministic via GenRng, never the world rng, so a town replicates as
(kit, seed, params). Not done: walls/doors around dungeon rooms (floor-only
today), multi-cell buildings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seed-deterministic, baked at spawn, emitted straight into the 24-byte packed
vertex format. Two devices given the same seed produce byte-identical
geometry — which is what lets a forest replicate as (preset, seed, position)
tuples instead of mesh data.
- L-system plants: expansion + 3D turtle + skeleton, 8 species (oak, pine,
palm, bush, fern, cactus, dead, grass)
- Surface nets (not marching cubes — fewer, better-shaped triangles at
low-poly) for rocks, boulders, mushrooms, clouds, blobs
- Spline tracks with width, banking, curbs and rails, returning centreline
frames that carry lap distance — so spawn points and checkpoints derive
from the track instead of a second hand-written list
- Poisson-disk scatter with flatness/height rules
- Texture generator with CPU mip chains (backends never generate them)
- LRU cache keyed by FNV-1a over the full recipe with floats hashed by exact
bits; -0.0 and 0.0 normalise together (identical geometry), 4.0 and
4.000001 stay distinct. Meshes hand out as Rc so eviction cannot pull
geometry out from under a frame mid-draw
- DrawGameFoliage: growth and wind as an OPT-IN shader variant, a sibling of
DrawGameSkinned rather than a flag inside the shared shaders — wind costs
~20 vertex ALU and the cube shader draws most of the world. Growth and flex
weights share one packed nibble pair, so both animations cost zero extra
vertex bytes
A realistic forest — 150x150 m, 582 trees, 3 species, 6 seeds — generates in
1.70 ms with 470 KB resident: 6 generations and 576 cache hits.
Three bugs the unit tests had passed, found by writing an ASCII silhouette
probe because captures were out of scope: every species came out ~4x its
requested height (the test compared two sizes RELATIVELY, so a uniform
overshoot sailed through — now the finished skeleton is measured and rescaled,
and the test asserts absolute height for all 8 species at three sizes); palm
emitted no foliage at all because its L-system contained no leaf symbol; and
cactus sprawled sideways like a shrub. Pine also dropped from 4 iterations to
3 — 15032 -> 2504 triangles, 728 -> 68 us — because 15k triangles for one
background tree is indefensible.
Honest caveat: that is a silhouette judgement, not a rendered one. Shading,
leaf-card orientation and the wind/growth animation are visually unverified.
Script verbs are not wired yet — the crate is a library only.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>