Everything equidistant from everything else
The first generated room put 174 pieces of ground cover inside a 29 m circle, every one drawn from a uniform distribution over the disc. I put the headset on and it read as confetti. Nothing was wrong with any individual…
The first generated room put 174 pieces of ground cover inside a 29 m circle, every one drawn from a uniform distribution over the disc. I put the headset on and it read as confetti. Nothing was wrong with any individual object; the arrangement was wrong, and it was wrong in a way I had explicitly asked for.
Because uniform is not what outdoors looks like. Mushrooms grow in patches and thin out at the edges. Firewood is stacked somewhere by someone. Trees of one kind grow near other trees of the same kind. A draw that treats every square metre as equally likely produces the one arrangement that never occurs in nature: everything equidistant from everything else.
Patches
The fix is a layer I've started calling minor features, sitting under the major feature a room is built around. Before any deck draws, the generator seeds a handful of patch centres — where the mushrooms are, where the flowers are — and a draw from a matching family lands around a patch instead of over the arena.
The detail that took a second attempt is the shape of the patch. My instinct was to pick a point uniformly inside a circle of some radius. That's wrong, and wrong in an interesting way: the area of a ring at distance r grows with r, so a uniform draw inside a disc is denser at the edge than in the middle. You get a ring, not a patch, with a hard rim where it stops. What you want is a Rayleigh-distributed radius — sigma * sqrt(-2 ln u), which is what a 2D Gaussian looks like in polar form. It has no edge at all. About 39% of members land within one sigma, 86% within two, and the stragglers beyond that are the taper.
Measured over the same seed, median nearest-neighbour distance:
- mushroom 1.42 m → 0.87 m
- flower 2.34 m → 0.74 m
The flowers needed a second pass. At my first spread they measured 2.34 → 2.10, which is nothing, because spread is relative to how many members a patch actually gets and flowers are the smallest family in the pool. The patch counts looked perfectly healthy the whole time. Only the spacing said the pass had done nothing.
Firewood, twice
Firewood is evidence of people, so it belongs to a building rather than to the arena — anchored in a band around a structure, and laid out according to whether that structure is a ruin or somewhere still kept. A ruin's wood is scattered over a wide area; a tended place has it stacked. That part I still like.
What was wrong was the volume. A quarter of every room was firewood — 47 pieces — and that number came from nowhere anyone decided: the pool has 10 firewood candidates out of 40, so the room's composition was a fact about the asset pack rather than a choice. Capping the family's share at 12% took it to 24.
Then I made the reverse mistake. To get "singular pieces spread out to add a bit of character" I dropped the chance of joining a pile, and produced a pile of two logs over a 1.6 m spread — which is not a pile, it's loose wood with extra steps. Two dials that look interchangeable and are not: the cap decides how much firewood exists, the join decides whether what exists is grouped. Only one of them can make a pile.
The same decision, made three times
Here's the part I actually want to remember. When I went to group the treeline by species, I found this comment in the generator:
Round-robin the species so neighbours differ rather than clustering by type.
I had written the identical anti-clustering decision into three separate generators, independently, each time to solve "this looks repetitive" — and each time it produced the noise I then had to undo. Variety at every individual point reads as noise. Variety between groups reads as a place.
Grouping the trees turned up something worse. Species were being read off the asset's parent folder, and the pack nests some kinds one level deeper: Fir_Trees/TwoSided/Fir_Tree_crk_m_01_LOD. So the folder rule returned TwoSided and OneSided — not tree kinds at all — and between them they swallowed 237 of 539 prefabs. Every fir was one species and every pine was another.
That had been wrong for as long as the code existed and had never mattered, because dealing one of each in turn gives you a mixed ring whatever the buckets are called. A wrong classification and a right one look identical right up until you ask to group by it.
There's a related constraint I only found by measuring: a stand can only be a species stocked at every rank. The ranks are height bands, and the pack's apples, firs and palms top out below the back rank — so those stands existed in front and dissolved behind. Allowing any species put 19 of 66 trees on a fallback. That's not an edge case, that's most of the back two ranks.
And the fix that left the residue
Separately, two set pieces were sitting above the ground, because a tool took its height from a ground tile's bounding box top — the highest point anywhere in 625 m² rather than the height under the piece.
Except the correction I'd applied the day before was itself part of the problem. The sink depth was measured against the template's pivot, and the op it was handed seats the lowest rendered geometry. Those differ by however far a prefab's mesh hangs below its root: 5 cm on the shrine, 9.6 cm on the ruin. So yesterday's fix is what left the residue, and the shrine looked correct afterwards.
Chasing that turned up its sibling. Ten cracked trees float their broken-off section, for exactly the same reason — the root flare dips 0.3 m below the pivot, so the snap seats the flare and rides the whole prefab up. And the two of those I'd "fixed" by hand were set to exactly y = 0.000, a typed number wearing a measurement's clothes. The terrain there is at −0.16. They were still 8–11 cm high.
A run of exact zeros in a scene where everything else sits at some measured height is a tell. I'll take that one forward.