7. Open questions

The specific decisions the chosen direction has surfaced. Each has a recommendation attached — they are questions because the recommendation could reasonably go the other way, not because there is nothing to say.

Answered Q1 — What density range does a tool strike consume?

Settled as recommended belowkToolPower × (127 / toughness) per strike, clearing the cell at the isovalue. The "interesting consequence" flagged below was later argued against and then withdrawn, because D10 puts the soft band at the surface where the material is topsoil. Full reasoning in section 10, question 1.

Density runs 0–255 with the isovalue at 128. Working through what Marching Cubes actually does with that: a cell at 255 next to air produces a surface at the grid plane between them; as the cell's density falls, the crossing moves toward the cell centre; at exactly 128 the cell's contribution collapses to a point. Below 128 the corner flips and the cell reads as air.

So the usable shrink range is 255 → 128 — the upper half of the byte — and it does produce a complete shrink-to-nothing, not a half-shrink followed by a pop.

Recommendation: a strike removes (255 - 128) / toughness(material) density, and the strike that reaches 128 clears the cell. Stone at toughness 8 removes ~16 per swing; leaves at toughness 1 goes in one. The existing materials::kToughness table carries over unchanged and keeps meaning "strikes to destroy".

The interesting consequence to confirm you want: the generator writes a soft bandkBand = 40 density units per voxel of depth — so surface cells are already partially dense before anything touches them. A cell that generated at 150 is already 80% mined. Under this scheme a thin crust of terrain breaks in one or two hits while deep rock takes the full count. That reads as correct behaviour and comes for free, but it does mean the toughness table describes strikes-from-full rather than strikes-always.

Answered Q2 — World height

384 to start, configurable so other heights can be generated and judged by feel (D7). Budgeted from D10's stratigraphy: ~190 below the surface datum for the full sequence to read, ~190 above for relief and build headroom.

An earlier draft of this question argued the height could not be deferred, because strata and cave depth are functions of absolute Y and the 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 the rest 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 budget 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 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.

Q3 — Are hand-authored structures ever in scope?

No longer gating. This was previously the question that most affected whether an infinite world was the right call, since coherent authored content — cities, roads, points of interest — needs global knowledge to place. D7 and D9 now give exactly that: a bounded map generated in one pass with everything present. The enabler is in place regardless of the answer.

And largely answered in passing by D11: roads, railways, cities and points of interest are named as anticipated pipeline stages. So authored content is designed for even though it is not scheduled — the generator will not need restructuring to accept it.

Recommendation: what remains is scope and ambition, not architecture — how large the authored pieces get, and whether they are stamped prefabs or generated. Answer whenever convenient.

Answered Q4 — When a construction block is placed into terrain, what happens to the terrain?

The question dissolves rather than being answered: it cannot happen. A construction block may not be placed into a terrain cell that still has density. A terrain cell whose density falls below the isovalue is destroyed — cleared to a genuinely empty voxel — and only an empty cell accepts a new voxel of either class.

So there is never a partially-dense cell being overwritten, and therefore nothing to discard or stash. The recommendation below is superseded; the cell lifecycle and one sub-case it raises are in section 10, question 11.

The cell can only have one class. Placing a block into a partially-dense terrain cell either discards that density or stashes it somewhere.

Recommendation: discard it. The cell becomes Construction at full density; breaking the block later leaves air, not the terrain that was there before. This matches the reference and avoids carrying shadow state on every cell. The alternative — restoring the original terrain when a block is removed — is a real design choice, but it costs a per-cell field that is almost always unused, which is exactly the trade the DamageMap was written to avoid.

Answered Q5 — How is construction crack damage delivered to the shader?

The per-chunk lookup, as recommended below, allocated lazily per damaged chunk. Four crack stages, shared across all materials rather than authored per material. And the work moved to phase 2, so its IRenderer addition rides along with the deferred deletion queue in one interface pass (section 10, questions 4, 5, 6 and 10).

D6 only pays off if damaging a block costs no remeshing. That rules out baking the damage stage into vertex data and points at a per-chunk damage lookup — a small buffer or texture indexed by cell coordinate — sampled in the fragment shader to blend a crack overlay from a stage texture array.

Recommendation: the per-chunk lookup. Two things worth flagging: it is a new renderer capabilityIRenderer currently has no concept of a per-draw data buffer that is updated frequently, so the interface gains something before Vulkan does. And the number of crack stages needs picking; the reference uses a handful, and each is a texture-array layer.

Answered Q6 — How many materials may blend at one marched vertex?

Four, as recommended below and now accepted.

Cubes never have this problem: a face belongs to exactly one voxel, so it takes that voxel's material and samples one texture. A marched vertex sits on the boundary between cells, and those cells can be different materials — a single triangle can span dirt, sandstone and granite. There is no answer to "what material is this triangle".

So each vertex carries N material layer indices plus N weights summing to 1, and the fragment shader samples all N and blends them. That is splatting, and N is the question:

  • N = 1 — every vertex snaps to one dominant material. Hard banding at every contact, and the seam follows triangle edges, so it reads as faceted and wrong.
  • N = 2 — handles a simple two-material contact (soil over rock) smoothly.
  • N = 4 — handles a point where four differently-materialed cells meet, which is what a corner is.

Recommendation: four — and D10 turns this from a rule of thumb into a requirement. Many thin bands stacked vertically means a dug wall cuts across them constantly, so band-on-band contacts are the normal view rather than an edge case; and where a band pinches out, three bands meet at a point. That is exactly where N = 2 falls apart, and exactly where the geology is most interesting to look at.

The cost is small: four uint8 indices plus four unorm8 weights is 8 bytes per vertex. The ceiling is not the voxel's colorIndex byte (256 materials) — it is texture-array memory and the work of authoring art for every material.

Answered Q7 — One tool with two behaviours, or two tools?

One tool dispatching on class, explicitly for now. The deferred damage model (section 10) has several tool types with per-material modifiers — shovels for loose soil, pickaxes for rock, sledgehammers for brick — so this is a staging decision rather than a final one.

D5 and D6 describe a pickaxe-style tool for terrain and a hammer-style tool for construction. Whether that is one tool dispatching on VoxelClass or two separate items with a right-tool-for-the-job penalty is a gameplay decision that reaches back into the edit ops and the HUD.

Recommendation: start with one tool dispatching on class — it is the smaller change and keeps the current single-button interaction intact. The reference's behaviour (both tools work, the wrong one is slow) is a later refinement that needs an inventory to be meaningful.

Answered Q8 — Does the health HUD survive as-is?

Kept for construction, numeric readout dropped for terrain — the shrinking surface is the better progress bar. The highlight tint survives for both, but for terrain it derives from density rather than the DamageMap, which terrain no longer has an entry in (section 10, question 9).

The HUD currently draws the targeted voxel's health/toughness above the crosshair, fading green to red. Under D5, terrain "health" is derived from density rather than stored separately, and the shrinking geometry already communicates progress visually — arguably better than a number does.

Recommendation: keep it for construction, where the crack stages are coarse and a readout adds real information; consider dropping it for terrain, where the surface itself is the progress bar. Worth deciding deliberately rather than leaving the current behaviour to carry over by default.

Questions already answered

Recorded so they do not get reopened by accident:

QuestionAnswer
Look or feel?Both, to reference parity, before any divergence
Caves and overhangs?Required — forces genuinely 3D generation
Whole-cell dig or sub-cell carve?Neither — per-cell edit, continuous effect, via density
World scale?Finite, exactly as the reference — and the size is a runtime parameter, deferred. Reversed from "effectively infinite", which removes the design's only knowing divergence
Generated up front, or lazily?Up front, as a distinct step, producing a stored artifact on disk. Sharing or downloading worlds is an idea kept in view but deferred
What is at the world edge?An invisible wall; only the skybox beyond it. The simple option deliberately
What is in the world vertically?Layered geology (D10) — soils and sands over distinct rock types, mineral seams within specific host rocks, caves and rivers, down to bedrock. Strata may sit at different depths or be absent; bedrock never is
How is bedrock made unbreakable?A hardcoded special case — undamageable, never deviates from full density — rather than a material with very high toughness
Do liquids and gases flow?Deferred by decision to its own discussion. The fork is a material that merely looks like oil versus a fluid simulation subsystem
How is the generator structured?Ordered steps, each holding one or more generators that run concurrently within it (D11). Generators sharing a step must write disjoint fields or combine commutatively — but disjointness is only permission to share, not a reason to: a step reads only earlier steps, so passes that want each other's output get their own. Bedrock clamp and LOD generation are the terminal steps and not reorderable
How is distant terrain handled?A pre-generated static coarse mesh, built by the terminal LOD step and loaded at runtime, drawn behind and below the streamed near field
How is distant construction handled?Its own simplified LOD models, generated separately by the same terminal step — not meshed into the terrain LOD. Regenerating them for player-built structures is deferred to its own discussion
What must every generator write?Smoothly varying density, never binary — a contract on the generator interface, enforced by a max-Δdensity test, because the cube renderer cannot show a violation and MC would not surface it until phase 3
Where do a marched vertex's material weights come from?The owning cell's eight corners, top four by contribution, with a mandatory stable tiebreak (weight desc, then material index asc) and renormalisation after truncation. Section 9
Does terrain get ambient occlusion?Not in phase 3. SSAO later, covering both representations — rather than a per-vertex term costing a vertex field, an equivalence-test term and a border width at once
How do the two representations resolve in depth?A small depth bias so construction wins. Explicitly not a geometric fix — that is what Epic #24 burned five commits on
What does the mesher read past the world edge?Air, so the surface closes into a clean terrain wall
How big is a voxel?1.0 m. Was implicit under every distance and memory figure; now stated (section 8, parameter 1)
Is the full-resolution radius a separate setting?No longer. The static distant mesh removed the coarse-but-resident band, so it is the residency radius — one knob, not two
What bounds resident memory?An LRU cap, default 1.5 GB, with the residency radius clamping down to fit it. Without that clamp the LRU evicts what residency immediately reloads — permanent thrash
What determines damage dealt?Material toughness alone, for now. Player attributes and tool types with per-material modifiers are designed for but deferred — so the per-strike damage stays a single function rather than an inlined expression
Does construction damage persist?Yes — the sparse DamageMap serialises. The dense per-chunk GPU buffer is a derived cache, rebuilt on load and never saved
How is the damage-clearing invariant held?By routing every occupant change through one function that clears damage, rather than by remembering to pass an optional pointer at each call site
When can a cell accept a new voxel?Only when empty. A terrain cell falling below the isovalue is destroyed — cleared to density 0 — and destruction is an explicit event, not a silent overwrite. So nothing is ever placed over live terrain density
How is the world artifact compressed?Fields split into planes first (a run in the interleaved struct breaks whenever any one field changes), uniform chunks elided to a header record, then per-chunk RLE. A codec byte per chunk keeps the upgrade to zstd from being a format break
Transvoxel?Dropped. Solutions requiring Lengyel's transition tables and their licensing are ruled out by decision. MC's own 256-case table and the asymptotic decider are unaffected
How many materials blend at a marched vertex?Four — four layer indices plus four weights, 8 bytes per vertex
Structural integrity?In scope, both representations, deferred
Which isosurface method?Marching Cubes specifically, for parity
How do terrain and construction meet?They interpenetrate — no shared boundary, no sealing, no snapping
Collision under a marched surface?Staged, not chosen: grid collision ships with MC, then a triangle narrowphase over broadphase candidates. Constraint: factor MC as a standalone marchCell
Disambiguate the MC table?Yes, from the start — zero holes, absolutely. Asymptotic decider on ambiguous faces (necessary and sufficient for watertightness); full MC33 interior subcases optional later