Spatial and games

HoloVoxel: a level-of-detail-native voxel engine for .NET

Level of detail is not a mesh trick. It is how a read works.

  • Release 1.5.2
  • Engine only, no graphics dependency
  • net8.0 and net10.0
  • Built on Phasor
dotnet add package EvaluatedApplications.HoloVoxel

Commercial licence for commercial use. Evaluation and non-commercial use are permitted.Licence terms

Detail follows the viewer

A strip of 9 terrain chunks, each stored once as a single hologram, is decoded at three budgets. Near the viewer a chunk is read with 8,192 components and is 98.4 percent correct. In the middle band 1,568 components give 67.1 percent, and far away 392 give 34.0. The far read takes 0.71 ms and 6,272 bytes of hologram against 17.0 ms and 131,072 bytes for the near one. Move the viewer and the bands move with it.

Accuracy against component budgetOne chunk stored as a single hologram is decoded at 10 component budgets from 24 to 8,192. Accuracy rises from 19.5 to 98.4 percent. The tables below list every budget with its stored size and decode time.025507510024489819639234.07841,56867.13,1366,2728,19298.4components read from the hologrampercent of cells correct
Data behind the diagram
The three levels of detail the scene uses, for one 16 by 16 chunk
LevelCell sizeComponents readCells decodedCorrect (percent)Hologram size (bytes)Decode time (ms)
Near, level 118,19225698.4131,07217.04
Middle, level 221,5686467.125,0883.19
Far, level 343921634.06,2720.71
Accuracy, stored size and decode time of one chunk against the component budget
Components readCorrect (percent)Hologram size (bytes)Decode time (ms)
2419.53840.09
4819.77680.15
9823.41,6000.26
19628.73,1360.41
39234.06,2720.71
78449.212,5441.58
1,56867.125,0883.19
3,13683.350,1766.25
6,27296.4100,35213.09
8,19298.4131,07217.04
What Lod.Components returns for a chunk at each distance, and the band the scene puts it in
Chunks from the viewerWorld unitsComponents suggestedBand
008,192Near, level 1
1168,192Near, level 1
2326,272Near, level 1
3482,787Middle, level 2
4641,568Middle, level 2
5801,003Middle, level 2
696696Middle, level 2
7112512Far, level 3
8128392Far, level 3
Decoded by running the published EvaluatedApplications.HoloVoxel package (release 1.5.2). The terrain, the colours and the heights are the package's output for one fixed world. The three bands are this scene's choice, and each band's component count comes from Lod.Components. Accuracy is the share of one chunk's cells decoded to the right block, measured at full resolution for each budget.

What HoloVoxel is

Most voxel engines store a world as arrays of blocks and fake distant detail with mesh simplification, mipmaps or detail popping. HoloVoxel stores a chunk of the world as one holographic superposition instead. Every voxel is bound as position and content and summed into a single vector, using the Phasor codec.

Reading a voxel back is a correlation against that vector, and correlating with fewer components gives a cheaper, fuzzier answer. Near the viewer, with the full budget, the correlation collapses to one crisp block. Far away, with a handful of components, the interference of every candidate block blends into a soft, continuous colour. The result is crisp near and soft far, with no discrete tiers of detail and no popping between them.

The engine also exposes a rendering signal. Decoded confidence, how unambiguous a read is, maps naturally to visual solidity, so a renderer can make distant, low-confidence terrain read as hazy while confident terrain stays solid. The number is computed here. Drawing it is up to your renderer.

  • Level of detail is free

    There is no simplified mesh to generate, store or stitch. The same hologram answers a near read and a far read, at whatever cost you choose to pay for that read.

  • Memory follows perceived detail

    A chunk stores only as many components as its level of detail will ever read back, and the first k components of a truncated hologram are bit-identical to reading the same chunk in full. Storage tracks the budget you plan to read at, not the size of the world.

  • The blur is the data

    Distant terrain looks soft because the engine has less information at that budget, not because a texture was downsampled. The same signal that saves compute drives the look.

  • Plug into anything

    No rendering or graphics dependency, and it is verified headless. You bring the renderer, the block palette and the world source, or use the built-in procedural generator.

Quick start

Decode a near chunk at the full budget and a far chunk cheaply, from the same kind of hologram.
C#
using HoloVoxel.Core;

var world = new HoloWorld(seed: 2024);

// A near chunk at the full budget: crisp, one cell per surface column of a 16 by 16 chunk.
var near = world.GetChunk(0, 0).DecodeField(step: 1, components: world.Comps);
Console.WriteLine($"near: {near.Count} cells from {world.Comps} components, each {near[0].Size} wide");

// A distant chunk, decoded cheaply and fuzzily: coarse cells and a small budget.
var far = world.GetChunk(40, 0).DecodeField(step: 8, components: 40);
Console.WriteLine($"far: {far.Count} cells from 40 components, each {far[0].Size} wide");
Output
near: 256 cells from 8192 components, each 1 wide
far: 4 cells from 40 components, each 8 wide

This is the complete program, compiled and run against the published package.

Lod.Components turns a distance into a budget. Confidence says where a flat distance curve is not enough, and the adaptive budget spends more only there.

C#
using HoloVoxel.Core;

var world = new HoloWorld(seed: 2024);
var chunk = world.GetChunk(0, 0);

// Distance in, component budget out: continuous, with no discrete tiers.
foreach (var distance in new[] { 0, 64, 128, 256, 512 })
    Console.WriteLine($"distance {distance,3}: {Lod.Components(distance, world.Comps)} components");

// Spend more only where a read is ambiguous. Confidence is the margin between the winning block and the runner-up.
var confidence = chunk.FieldConfidence(components: 48);
float average = Lod.AverageConfidence(confidence, c => c.Confidence);
int baseBudget = Lod.Components(distance: 256, maxComps: world.Comps);
Console.WriteLine($"average confidence {average:F2}: {baseBudget} components stay {Lod.AdaptiveComponents(baseBudget, average, maxComps: world.Comps)}");

// Below the confidence floor the budget is boosted toward the maximum, and never lowered.
Console.WriteLine($"average confidence 0.20: {baseBudget} components become {Lod.AdaptiveComponents(baseBudget, 0.2f, maxComps: world.Comps)}");
Output
distance   0: 8192 components
distance  64: 1568 components
distance 128: 392 components
distance 256: 98 components
distance 512: 24 components
average confidence 0.95: 98 components stay 98
average confidence 0.20: 98 components become 3566

Entities are stored the same way as terrain, with the same level-of-detail and confidence machinery, independent of the terrain grid.

C#
using HoloVoxel.Core;

var world = new HoloWorld(seed: 2024);

// A creature is stored the same way as a chunk: one small hologram, decoded with the same level-of-detail rules.
var (voxels, palette) = Creatures.Wraith();
var wraith = new HoloModel(world, voxels, palette);

var crisp = wraith.Decode(step: 1, components: world.Comps);
var fuzzy = wraith.Decode(step: 1, components: 24);
double blur = crisp.Zip(fuzzy, (a, b) => Math.Abs(a.R - b.R) + Math.Abs(a.G - b.G) + Math.Abs(a.B - b.B)).Average() / 3;
Console.WriteLine($"wraith: {crisp.Count} cells, bounding box ({wraith.MinX},{wraith.MinY},{wraith.MinZ}) to ({wraith.MaxX},{wraith.MaxY},{wraith.MaxZ})");
Console.WriteLine($"mean colour change from 24 components against the full budget: {blur:F3}");
Console.WriteLine($"confidence values: {wraith.FieldConfidence(components: world.Comps).Count}");
Output
wraith: 102 cells, bounding box (0,0,1) to (6,10,2)
mean colour change from 24 components against the full budget: 0.140
confidence values: 102

Capabilities

  • Holographic chunk storage

    HoloWorld and Chunk encode a chunk once and decode it at any component budget, as a colour field or as a triangulated surface mesh with analytic per-vertex normals.

  • Continuous, meshless level of detail

    The same stored hologram serves a crisp near view and a soft far view. Lod.Components maps a distance to a budget as a starting point.

  • Field confidence for adaptive detail

    FieldConfidence reports how unambiguous each decoded point is, the softmax margin between the winning material and its runner-up. Lod.AverageConfidence and Lod.AdaptiveComponents turn that into a budget that spends more only where the world is genuinely ambiguous.

  • Spatial coarsening at distance

    Far reads average over a footprint of the underlying world data, for terrain height and material, instead of sampling one noisy point. That keeps far terrain reading as smooth, coherent shapes. It is a separate knob from the component budget, and the two compound.

  • Truncated holograms

    Storage cost tracks the level of detail you plan to read at, not the size of the world.

  • Fused SIMD bind and correlate

    Generation and decode are allocation-free on the hot path.

  • Entities too

    HoloModel stores a creature or a prop the same holographic way, with the same level of detail and confidence, independent of the terrain grid.

  • A built-in world generator

    WorldGen offers biomes, height fields and an optional maze layout. Supply your own IWorldSource to skip it entirely.

  • Ringworld warp

    Ring is a pure flat-to-cylinder coordinate transform for cylindrical spaces, on top of the same flat chunk storage.

Limits

What HoloVoxel does not do, and where to be careful
  • This is the engine, not the renderer. It hands you decoded colour fields, meshes and confidence values. Shaders, GPU decode and a game-engine binding belong to your front end. A reference shading recipe (lighting from the mesh normals, ambient occlusion from the height field, confidence-driven haze) is a reference outside the package.
  • Chunks are 16 columns on a side and that size is fixed. A full build stores four buried layers per column.
  • Chunks only ever get finer. A chunk built coarse can be refined but never coarsened, and decoding at a step finer than the build is invalid.
  • Call Prewarm once, on one thread, before any concurrent chunk work. After that, parallel generation and decode are lock-free.
  • Accuracy checks and the argmax decode need a full build. Confidence is empty for a chunk whose first build was coarse and for a wall chunk.
  • A read asking for fewer than eight components still reads eight.
  • Entity voxels must stay inside 16 by 16 columns and 12 layers, or they alias another position.

Every public type is documented with a worked example in the package readme on NuGet. Open the NuGet page.

of cells decoded correctly with all 8,192 components
98.4%
less hologram read at 24 components than at the full budget
341 times
less decode time at the smallest budget than at the full one
189 times

Related

Licence

Proprietary. Every package ships as a compiled library. Evaluation and non-commercial development or testing are permitted; commercial, production or revenue-generating use needs a commercial licence from the owner. Read the licence terms.