
Trees were the job I’d been putting off. Procedural vegetation is where a lot of generated worlds give themselves away, and a voxel world adds its own problem: at the one-metre resolution the ground uses, leaves come out jagged.
This week I got them working. Luminids now has full-scale procedural trees, grown on their own voxel layer at four times the resolution of the ground. A grove of seven loads in about two seconds.
Trees at their real height
Each species has a real height in metres, from silver birches up to towering poplars, so a tree stands at the right size next to the player.
The catch is the detail layer. It runs at quarter scale, so the generator multiplies every branch vector, influence radius and leaf cluster radius by one over that scale. A 12 metre oak is grown as a 48 metre giant, keeps its proportions, and then the scene graph shrinks it back to reality.
Branch radii also scale up with the tree, using a soft 1.2 multiplier, and leaf clusters get a 1.8 boost for readability.
Two terrains at once
Luminids runs two terrain layers in parallel: the main terrain at one-metre voxels, which keeps it fast, and the tree overlay at quarter-metre resolution. The trunks meet the heavy terrain, and the crowns get the detail grid.
To make that work, I added an empty generator preset that pre-fills every overlay chunk with air, so a tree buffer always has somewhere to land. After pasting a tree in, we issue a save command, which forces the mesher to rebuild only the affected chunks, so the rest of the world stays light.


One atlas for every leaf
The trees use a block-based mesher, so every branch and leaf references our texture atlas instead of a stack of separate materials. That keeps draw calls low, keeps the art consistent, and packs bark, lichen, moss and leaf variants into a single lookup.
The snag is that the block mesher doesn’t pass UV2 channels. So we built a 256 by 1 lookup map, where each block model index fetches the correct layer in the texture array. The generator stores branch radius and leaf density per voxel, the mesher reads the lookup, and the engine turns it into shaded foliage in a single pass.
The first version was too slow
My first implementation tried to be clever. It checked every voxel before writing, merged overlapping spheres, and paid dearly for it. The fix was to stop being clever: write first, and keep the sampling intervals simple.
The tree buffers now average 50 to 200 thousand voxels, fill rates sit around thirty percent, and a grove of seven trees loads in roughly two seconds.
How space colonisation works
For anyone who wants the detail, the trees grow using the classic space colonisation algorithm.
It starts by scattering attractor points through the space where the crown should be. Those are the destinations. A trunk is seeded at the base, and each branch tip looks for the closest attractor within its influence radius and grows a segment towards it. As branches get close to attractors they claim them, and a claimed attractor stops pulling new growth. Branches that never reach one get pruned.
What’s left fills the crown naturally, with thick trunks near the base and fine twigs at the edges. Every species uses the same algorithm, but random seeds keep each tree’s shape unique, and a batch script updates attractor influence for every species in one click.

What’s next
The generator supports even bigger scales: six times for old-growth forests, eight for legendary biomes. Getting there only needs bigger buffers and adjusted attractor influence. With the technical side sorted, the fun part starts.
Nick





