Generated maps in Shards of Stone used to be terrain, trees, mines and creep camps, with bare grass between them. Now a field has a split-rail fence round it, a ridge has a fallen colonnade, a kobold camp has tents and a cook fire, and a ratmen street has its own barricades. The registry held 1,361 props when I wrote this in early October, and all but 44 of them are built by a function when the map loads. None of those is a model the game downloads.
Adam's brief was short: maps should feel alive. He also set the yardstick: a mature RTS editor ships over a thousand placeable pieces of scenery for its map makers. This game had 44, every one of them a corruption growth, a piece of dungeon furniture or neutral clutter, and none was outdoor scenery.
Why the props are built in code
Every building in the game came out of the image-to-3D pipeline in turning 2D sprites into 3D buildings, so the obvious route was to generate the scenery the same way. There were four reasons not to.
A prop built by a function downloads nothing. Generated models are the heaviest thing a match waits for, which is why so much work went into cutting the download that happens before a match starts. A recipe is a few hundred lines of code that ships inside the game.
A recipe takes a seed, so one rock recipe is every rock. It takes a biome, so the same fence is pale in the desert and mossy in the swamp. It also takes a detail level, which means the far version of a prop is written on purpose with the silhouette kept, instead of whatever a decimator leaves behind.
Scenery is also drawn in very large numbers. Each combination of prop, variant and detail level is one instanced batch, so two hundred identical fence posts cost one draw call, the same trick described in drawing hundreds of units without losing the frame rate.
The last reason is the one that ruled generation out for the faction walls too. A fence is a run, not an object. Each segment has to meet the next at exactly the same height and thickness, and turn a corner, and end in a post. A generator returns one self-contained object per request.
Two of the building blocks are borrowed, and credited in the repository's third-party notices. The rock generator (a sphere, pushed about with noise and then cut by planes) is adapted from Starfall by e01.ai, and the chamfered box and swept tube come from Ashlands by Peter Blenessy. Both are under the MIT licence.
The first barrels looked like toys
Geometry was the easy half. The first props were shaded with flat colours per vertex, a tint here and a darker band there, and Adam rejected them on sight. His bar was the hand-painted look of classic fantasy game scenery, where every plank has a worn highlight on its edge and every iron band has rivets.
What gives that look is an old technique called a trim sheet. It is one painted texture made of horizontal strips, and each strip tiles left to right: a nailed plank, a riveted iron band, twisted rope, a hemmed cloth border, a carved stone moulding. A model does not unwrap its own texture. Each face is laid along one strip, lengthways for its length and across for its width, so every face gets the whole painted band with its worn edge and its dark groove.

The sheet here is 16 rows of 128 pixels in one 2048 pixel texture. Gemini painted the bands, and a build step finds each one, makes it wrap, and fits a whole number of repeats across the width. Adam compared a handful of pilot props with their flat versions, approved the approach for all of them, and the sheet was rolled out to every built prop, every rock and every camp.
Two details made one sheet enough for the whole game.
The colour is relative. The build evens every strip out to the same average brightness, so the texture carries only light, shade and wear. The hue comes from the vertex colour, taken from the biome's palette. One plank strip is therefore every plank in every biome, and a new biome needs a palette, not new art.
Small hardware is real geometry. Nail heads, rivets, hoops, straps and rope lashings are tiny closed solids sunk slightly into their host, added by shared helpers and trimmed first when a prop is over its triangle budget. A painted rivet is flat at the camera angle an RTS uses, and a modelled one catches the light.
Because every trim piece on a prop uses the one texture, those pieces merge into a single draw. The trim sheet and the five tree atlases shipped together as 864 KB of AVIF, down from 27 MB of PNG.
The column that was drawn inside out
The thing that went wrong came from the geometry I had called the easy half. Adam opened the first batch and sent a screenshot of a ruined column whose cap and plinth were floating strips of edge with nothing between them.
The cause was one shared helper. The function that builds a slab had wound nine of its ten faces to point inwards, so the renderer, which skips faces pointing away from the camera, drew the inside of the far wall and nothing else. Every prop that used a slab was wrong, and so was every simplified block that the middle detail level is built from.
Adam's next message was that other props had missing faces as well, and he was right. I wrote an audit and ran it over all 61 recipes that existed then. It flagged 18: iron fences whose spear tips were pyramids with no base, logs with open ends, a gate with flipped faces, two obelisks with coincident faces at their lowest detail. Every one of them had passed its kit's tests, which had no check for a closed solid.
The audit became a permanent gate over every recipe, at every variant and every detail level. It first covered 1,719 mesh builds and now runs over several thousand. A prop that is not a closed, outward-facing solid with nothing floating fails before it is ever drawn.
A check for your own generated meshes
This is the core of that audit, with no dependencies. It works on any triangle list, whatever built it.
// Is this triangle mesh a closed solid whose faces point outwards?
// `p` is a flat x,y,z array with three vertices per triangle (non-indexed).
function auditSolid(p: ArrayLike<number>, eps = 1e-4) {
const key = (i: number) =>
`${Math.round(p[i] / eps)},${Math.round(p[i + 1] / eps)},${Math.round(p[i + 2] / eps)}`;
const edges = new Map<string, number>();
let volume = 0;
for (let t = 0; t < p.length; t += 9) {
const k = [key(t), key(t + 3), key(t + 6)];
for (let e = 0; e < 3; e++) {
const a = k[e], b = k[(e + 1) % 3];
// An edge walked a->b must be walked b->a by the triangle next door.
edges.set(`${a}|${b}`, (edges.get(`${a}|${b}`) ?? 0) + 1);
edges.set(`${b}|${a}`, (edges.get(`${b}|${a}`) ?? 0) - 1);
}
// Signed volume of the tetrahedron this triangle makes with the origin.
volume += (p[t] * (p[t + 4] * p[t + 8] - p[t + 5] * p[t + 7])
- p[t + 1] * (p[t + 3] * p[t + 8] - p[t + 5] * p[t + 6])
+ p[t + 2] * (p[t + 3] * p[t + 7] - p[t + 4] * p[t + 6])) / 6;
}
let unmatched = 0;
for (const n of edges.values()) if (n !== 0) unmatched++;
return { closed: unmatched === 0, outward: volume > 0, badEdges: unmatched / 2 };
}Rounding the positions welds vertices that sit in the same place, which matters because generated meshes rarely share them. An edge left over is either a hole or a neighbour wound the other way. The volume is positive when triangles run anticlockwise seen from outside, which is the three.js default for a front face. Run it once per separate piece, because a prop made of several solids can hide one inverted piece inside a positive total. Feed it a mesh you have broken on purpose first, so you have seen it fail.
Dressing a map the same way for every player

Generated maps place their own props, and since late September that is on by default. In a multiplayer match every player's machine builds the map separately, so the scenery has to come out identical on all of them and must not disturb anything the match depends on.
Placement therefore never touches the game's random number stream. Each choice comes from a hash of the map seed and the tile position, the same idea that picks the banners along a wall. The props are laid out with the same mirror symmetry as the mines and the camps, and a keep-out mask holds them away from start positions, mines, oil, expansions, bridges and ramps.
The gate is a test that generates each map with decoration on and off, and requires two things to match: the fairness fingerprint, and the number of random draws the generator made. The second catches a prop that quietly consumed a random number and shifted everything placed after it. When the default was switched on, a standard two-player map carried between 101 and 149 props.
What it costs to draw
Windmill sails turn, lanterns flicker and reeds sway, all in the shader. I benchmarked 2,000 animated prop instances against the same 2,000 standing still, and the difference stayed within 0.15 ms a frame.
The trees went the same way. Every biome's trees are now recipes traced from the tree model they replaced, and each tree picks its own detail from its size on screen. Counted across the camera poses the game uses, that draws between 4.6 and 33 times fewer triangles than the models did, which continues the work in keeping a thousand trees on screen.
Start a skirmish from the play page and the map will have dressed itself. To place props by hand, with rotation, scale and variant, open The Forge, where the whole registry is in the palette.





