Devlog
Rebuilding the tree generator from measurements
SpringFall’s trees are procedurally generated rather than placed. Trunk, branches, roots and canopy are built from a species definition and a seed, so no two are identical — and every one of them is a thing a colonist can be sent to fell, which is where most of the settlement’s wood comes from.
The output was wrong. Next to the hand-modelled reference trees, generated ones read as thin and mechanical: canopies with sky visible through them, branches leaving the trunk at uniform angles, stumps that didn’t connect to the trunk they belonged to.
Measuring the references
Tuning by eye had already been tried. Instead, a tool now parses the reference models directly — outside the engine, no editor involved — and reports trunk taper per height band, limb attach height, angle, length and radius, root spread and depth, canopy silhouette, and leaf-card count and size.
Two traps had to be cleared first, each of which produces confident garbage. The vertex-colour green channel is not the trunk/leaf switch: G=0 contains canopy. And clustering a horizontal slab has to walk mesh edges rather than XZ proximity — an 8-vertex ring on a 1 m base has its neighbours 0.78 m apart, so proximity clustering shreds it.
Five structural differences came out, roughly in order of impact:
- Branch distribution ran upside down. The generator put the fewest limbs at the base and the full count at the tip. Every reference tree is bottom-loaded, with median attach height at 0.06–0.30 of total height.
- The trunk never forked. A single stem with side branches cannot produce a broadleaf silhouette. Trunks now split into 2–3 leaders, each taking 1/√n of the parent radius.
- Branches were hard-coded to two segments — three rings, one possible bend. Measured limbs turn through four joints or more, and that meander is most of what reads as a tree at distance.
- Leaf-card count was a third of what it needed to be. About 1,300 per tree against a reference range of 2,193–9,885, with the density multiplier capped below what would close the gap. The see-through canopies were a count problem, not a texture problem.
- The base flare is trunk, not roots. In the references the trunk’s own cross-section swells and goes lobed near the ground; surface roots are 4–12% of base area at 0.19× the trunk radius. The generator built separate cones at 1.6–2.2× instead, which is why the stumps read as feet.
Two regressions the same measurements caught
Branch count had become coupled to mesh resolution — limbs attached at every stem ring, so ring spacing multiplied them. That gave 108–386 branches per tree against a reference range of 3–49, and 71,108 triangles on a single Elder Willow. Branch count is now per tree.
The rebuilt base flare then overshot to roughly 4 m fins, because swell amplitude and lobe amplitude multiply and live in separate fields.
After both: 7,790–13,986 triangles per tree at full detail, against a reference band of 6,014–25,278.

The wood was 5–47 separate tubes
The deeper problem was topology. Each branch was generated as its own tube pushed into its parent and left open at both ends. Measured across the base region: 5 to 47 disconnected components per tree and hundreds of open boundary edges. A reference tree’s wood is one closed surface that splits.
The replacement builds the whole skeleton as a single closed manifold — watertight, every edge shared by exactly two faces. It was written as a standalone reference implementation first and validated against 500 junction configurations and 400 whole trees, then ported and checked for parity on the same cases: 900/900 identical, matching both the verdict and the per-case vertex, face, component and boundary counts. In-engine it closes on 90 of 90 species and detail tiers — 0 boundary edges, 0 non-manifold edges, 1 component.
| detail tier | open tubes | welded | overhead |
|---|---|---|---|
| LOD0 (close) | 11,364 | 12,840 | +13.0% |
| LOD1 | 5,716 | 6,444 | +12.7% |
| LOD2 (distant) | 2,024 | 2,194 | +8.4% |
The overhead is smallest on the cheapest tier, which is the right way round — a forest draws mostly LOD2. Triangles rise, but vertex count drops 10–18%, because a welded ring carries no duplicated seam column. On vertex-bound foliage that may be a net win, so the triangle figure shouldn’t be quoted alone.
One thing the topology test could not catch: the first welded bake rendered as tight horizontal banding. The vertical texture coordinate had become cumulative arclength, which is defensible in isolation — it keeps texture density uniform where ring spacing varies — and wrong here, because every bark material is tuned against ring index. Correct topology and correct appearance are independent properties, and only one of them has an automated oracle.
Status
The shapes are baked and measured. Not playtested. The weld is ported, verified, and switched off on every species.
Counting the live world before turning it on: 691 trees placed, 2,081 tree-mesh instances, and every one of them a hand-modelled species. No generated tree is standing in the world yet — the 24 generated instances that exist are on the comparison bench in the picture at the top. So the weld cannot currently change a triangle of the shipped world, which makes this the cheap moment to switch it on.
Next: generated trees placed in the forest, then the same measurements taken across a full valley instead of one tree at a time.
If there’s a number in here you’d want taken further — or a system you’d rather see measured next — say so in Discord.
Follow the build
SpringFall is a colony sim built by one person, and these posts go out as the work lands. The SpringFall Discord gets builds and questions first; @SpringFallGame on X gets the short version of everything here.