5. World scale
The topic that was flagged for its own conversation, now largely settled: a finite world of configurable size, generated up front into a stored artifact. What that closes, what it leaves open, and the one thing people expect it to remove that it does not.
D7 — the world is finite, exactly as the reference is, and the extent is a runtime parameter rather than a hardcoded constant. The number that ships is deferred until there is real terrain to judge it against. At the boundary an invisible wall stops the player and only the skybox is drawn beyond it.
D9 — worlds are generated up front as a distinct step, producing a stored artifact on disk rather than terrain reconstituted on demand. Sharing or downloading pre-generated worlds is an idea kept in view but deferred; nothing in the plan depends on it.
This reverses the earlier "effectively infinite, streamed" position, which was the design's one knowing departure from the reference. That departure is withdrawn.
What this closes
- The divergence. Under D3 the bar is parity, and world scale was the single exception. There is now no exception.
- Prefabs stop gating anything. Whether hand-authored structures are in scope was previously the question that most affected whether the world shape was right, because coherent authored content is hard to place without global knowledge. A bounded map placed by a global pass makes it ordinary. The scope question survives; its urgency does not.
- Most of risk 7. Cross-chunk features — ore veins, connected cave systems, prefabs — were hard because a chunk had to generate identically without knowing which neighbours existed. Under D9 they all exist. See sub-decision C.
- The LOD approach reopens favourably. Pre-generating distant terrain at world-build time is available in a way it is not for an unbounded world, which is very likely how the reference affords its own distant terrain. See sub-decision F and risk 5.
What it does not close: streaming
The intuition that a finite world can simply be held in memory is worth checking against
the arithmetic, because it does not survive it. Chunk residency is set by size, not
by whether the world ends. At one voxel per metre, a 256-tall world, and four bytes per
Voxel:
| World | Voxels | Dense in RAM | Verdict |
|---|---|---|---|
| 512 m square | 67 M | ~0.27 GB | Fits comfortably |
| 1 km square | 268 M | ~1.1 GB | Fits, wastefully |
| 2 km square | 1.1 B | ~4.3 GB | No |
| 4 km square | 4.3 B | ~17 GB | No |
So anything at reference scale streams, and everything risk 2 asks for survives unchanged: async meshing, an explicit dirty set, the version counter, job cancellation, a bounded per-frame upload budget. Those were never consequences of an infinite world — they are consequences of D5 making remeshes frequent, which is untouched by any of this.
What phase 2 actually loses is unbounded addressing and generate-forever. What it keeps is the expensive part. The phase gets smaller, not cheap — and it gains world-generation-as-a-step and a world format in exchange.
Sub-decision A — World height Settled
384 to start, configurable on the same terms as the horizontal extent, so other heights can be generated and judged by feel. Budgeted from D10's stratigraphy — roughly 190 below the surface datum for the full sequence to read, and roughly 190 above it for terrain relief and build headroom:
| Band | Voxels |
|---|---|
| Topsoil, sand | 1–3 |
| Subsoil, clay | 5–10 |
| Weathered rock | 10–20 |
| Sedimentary — the ore, cave and river band | 80–100 |
| Igneous, metamorphic, deepest seams | 60–80 |
| Bedrock | 2–4 |
An earlier draft of this page argued the height could not be deferred, because strata and cave depth are functions of absolute Y and the whole generator gets tuned against them. D10 largely dissolves that: anchoring the layers relative to the surface and to the bedrock datum means changing the total height stretches the run of deep base rock and leaves everything else untouched. A working value is needed to tune against; a final one is not.
The trade to remember: height multiplies the resident working set directly, because residency is a 2D disc and the whole column is always loaded. At a 12-chunk radius, 256 tall is ~460 MB and 384 tall is ~695 MB — so height and view distance compete for the same memory in a way the horizontal extent does not.
And a gameplay counterweight to hold against the geology: players mostly dig the top 30–60 voxels. Strata at 150 down are generation cost that nobody sees, which argues for putting the variety within reach and using the deep range for rarity and the long run to bedrock — rather than spacing bands evenly for realism.
Sub-decision B — Storage, addressing, and size-independence
VoxelWorld already documents its own growth path:
Growth path (unbounded / streamed world): swap the dense vector for a hash map keyed by chunk coordinate and allocate chunks on first write. The public interface here […] is deliberately storage-agnostic so that swap doesn't ripple outward.
That prediction still holds — the world-space get/set, the dirty
protocol and ChunkVoxelSource are all coordinate-based. Three things to settle:
- Chunk dimensions stay compile-time; chunk count becomes runtime. That is the
right split.
VoxelVolume<W,H,D>keeps its 323 chunks and its compile-time indexing, and already sits behind the non-templatedVoxelVolumeBase, so only the container above it changes. - The key. The DamageMap already contains a working 21-bit-per-axis packed cell key with a bias for negative coordinates. The same scheme applied to chunk coordinates gives a ready-made hash key.
- Signedness throughout. Today
inBoundsrejects negative coordinates andgetuses/and%, both of which truncate toward zero and are therefore wrong for negative world coordinates. Floor-division and floor-modulo are needed everywhere a world coordinate becomes a chunk coordinate. Small change, very confusing bug atx = -1if skipped.
Changing the world size must not change the terrain that was already there. Otherwise comparing a 1 km world against a 4 km world compares two different worlds and teaches you nothing about size. Three requirements follow:
- Generation samples noise at absolute world position, never at a normalised or size-relative coordinate. The size is then a pure clip rectangle over an unbounded field.
- Anchor at the centre, not a corner, so growing the world adds terrain at the edges rather than translating everything. This also matches the reference — and it is where the signed-coordinate requirement above comes from.
- World size must not leak into the seed or the save format. Region files keyed by absolute chunk coordinate mean a resize keeps existing saves valid over the overlap.
Sub-decision C — Deterministic generation
Under the previous infinite-world position this was risk 7 and it was substantial: a chunk had to generate identically regardless of when it was first visited or which neighbours existed, which is free for pure noise and genuinely hard for anything spanning a boundary — ore veins, cave systems as connected structures, trees, prefabs. The standard resolution was a two-pass scheme with a bounded feature radius.
D9 collapses most of this. With the whole world generated in one pass, a feature spanning four chunks is written into four chunks that are all present. There is no consistency problem to solve, and the bounded-radius scheme becomes an available optimisation rather than a requirement — which also lifts the constraint that the world's largest possible feature had to be decided up front.
What survives is narrower, and worth stating so it is not assumed away:
- Reproducibility is still wanted, just for a different reason — the same seed and the same size should produce the same world, so a world can be described by its parameters and so generator changes are diffable.
- Generation order must not affect the result. If generation is parallelised across chunks — and it should be, since it is now a blocking up-front step — a feature pass writing into shared chunks needs a defined order or it is racy. Easier than the infinite case, but not automatic.
- The mip-chain and any pre-generated LOD are generation outputs, so they inherit the same reproducibility requirement.
Sub-decision D — The world format, and persistence
This grows in importance under D9. Previously persistence was an optional save system over a regenerable world; now the stored world is the world.
- The format carries generated data, not just a seed. Reproducing a world by re-running the generator would make every generator improvement silently alter worlds the player already has — the opposite of what a stored world means. This was first argued from distribution, but it does not depend on it.
- It must be versioned, and a version mismatch has to fail loudly rather than render subtly wrong terrain.
- It must be compressed. A 2 km world 384 tall is ~6.4 GB raw. Density fields compress unusually well — long runs of 0 and 255 — so the win is large rather than marginal. The scheme is open; see section 8, question 11.
- Player modifications are the same bytes as generated terrain. Under D5 a half-mined hillside is captured entirely by density, and construction damage is a small sparse map. So "modified" is a flag for knowing what to write back, not a separate representation.
- Region files keyed by absolute chunk coordinate — the same choice the size-independence constraint above already forces.
The engineering that follows from this — flush policy, the dirty-versus-modified distinction, the LOD version stamp, the directory layout — is settled in section 8, questions 4, 7 and 8.
Sub-decision E — Threading and frame budget
Unchanged by any of this, because it is driven by risk 2 rather than by world scale:
- Meshing on worker threads behind a job queue keyed by chunk coordinate.
- The main thread does GPU upload only, with a bounded number of chunk uploads per frame so a burst of newly-resident chunks cannot spike frame time.
- An explicit dirty set rather than a sweep, so edit-driven remeshes cost O(edits).
- Job cancellation, since a chunk can be edited again while its mesh job is in flight, or evicted before its job completes.
One addition from D9: generation is now a bulk up-front job rather than a trickle, so it wants the same worker pool but a different scheduling shape — a progress bar and a completion barrier rather than a steady background drip. And because iterating on the generator now means regenerating, generation throughput is a first-class concern.
Two things this list understates, both settled in
section 8: a worker cannot simply read shared chunks while the
main thread evicts them — jobs take a bordered snapshot instead (question 1) — and
destroying a chunk's GPU buffers currently requires a full waitIdle, so
IRenderer needs a deferred deletion queue before eviction can be continuous
(question 2).
Sub-decision F — Distant terrain Settled
Still required. A few kilometres of visible terrain needs some form of LOD whether or not there is more terrain beyond it, so nothing here is removed by the world becoming finite.
What changed is the approach, and favourably. D9 makes pre-generated distant terrain viable: with the whole world in hand at build time, a coarse representation can be produced once, stored in the world artifact, and simply loaded. That is plausibly how the reference affords its own distant terrain, and it is unavailable to an unbounded world — it replaces the runtime chunked-LOD hierarchy that would otherwise have needed Transvoxel.
D11 settles it. LOD generation is a terminal step of the generation pipeline, so the coarse field is produced up front and stored in the artifact — and because it downsamples the finished voxel field rather than re-sampling a heightmap, it keeps the caves and overhangs D4 requires.
Decided: a pre-generated static distant mesh, and Transvoxel is dropped. Near the player, streamed voxel chunks; beyond, one coarse mesh loaded from the artifact and drawn behind and below them. No runtime level selection, no transition cells, no case tables — and no licensing question, since solutions requiring those tables are ruled out. What was the nastiest risk in the plan reduces to one tuning decision: where the handoff ring sits. Tracked in risk 5.
Two things it does not settle. The coarse field must be a genuine downsample of the fine one, or the surface changes shape as the near field takes over — which is why the density mip-chain is a phase 1 concern. And the LOD step covers terrain only, so generated cities built from construction voxels are invisible at distance until the near field reaches them.
Questions remaining for this conversation
- How much of the world format lands in phase 2? Format, versioning and the modified-chunk flag are cheapest designed in alongside streaming.
- Are hand-authored structures in scope? No longer gating — the world shape does not depend on the answer any more — but it still sets the feature pass's ambition.
- What sizes are worth experimenting with? The parameter is the point; a rough range to target still sets the streaming and LOD budgets.
- Single-player only, or is multiplayer a consideration? Unchanged by this, and still worth knowing early — it changes the chunk ownership and authority model, and D9's stored, shareable worlds would make the question slightly more live rather than less.