Building a Voxel Engine with Signed Distance Fields
Most voxel engines store the world as a grid of blocks. Minecraft does it. Teardown does it. It works. But it imposes a hard constraint: every surface is axis-aligned. You get cubes, or you get nothing.
I wanted smooth terrain. Caves that curve. Asteroids with craters. So I built Plenum, a voxel engine that stores distances instead of blocks.
Plenum's core data structure is heavily inspired by voxelis, a Rust library implementing sparse voxel DAGs with hash-based deduplication. The design of voxelis's node pool and content-addressed storage informed much of Plenum's architecture. If you're interested in the low-level DAG implementation, voxelis is an excellent starting point.
What's a Signed Distance Field?
An SDF is a function that, for any point in space, returns the distance to the nearest surface. Negative values mean you're inside the object. Positive means outside. Zero is the surface itself.
Move your mouse over the visualization below. The bright ring is where SDF = 0, the surface.
Blue is inside (SDF < 0), warm tones are outside (SDF > 0).
The floating numbers show the actual distance value sampled at that point.
A sphere's SDF is trivially simple: distance(point, center) - radius.
But SDFs compose beautifully. You can take the min of two SDFs to union them,
the max to intersect, and max(a, -b) to subtract one from another.
This is called Constructive Solid Geometry (CSG), and it's how Plenum handles terrain editing.
Sparse Storage with a DAG
Storing an SDF value at every voxel position is wasteful. Most of the volume is either deep inside the terrain or far outside it. Only the thin shell near the surface matters.
Plenum uses a Sparse Voxel DAG (Directed Acyclic Graph): an octree that only subdivides where the surface actually exists. Uniform regions (all inside or all outside) are stored as a single node regardless of their spatial extent. Identical subtrees are further deduplicated via content hashing, so two separate empty regions share the same pointer.
The visualization below shows this in 2D (a quadtree, the 2D analog of an octree). Notice how only the surface region subdivides to full depth. Large interior and exterior blocks stay as single nodes.
As you increase the max depth, notice how the total node count grows slowly compared to what a full grid would require. At depth 7, a dense grid would have 16,384 cells, but the sparse tree typically uses a few hundred. In 3D, this difference is even more dramatic: a depth-8 octree covers 2563 = 16 million voxels, but Plenum stores roughly 2% of that.
Hybrid Terrain
Plenum's terrain is hybrid: a procedural SDF function generates the base shape (noise-driven asteroids, spheres, etc.), and the DAG stores only the edits players make. Unmodified regions cost zero memory because they're evaluated on the fly from the procedural function.
HybridSdf checks the DAG first. If a voxel has been edited, use the stored value.
Otherwise, fall through to the procedural function. This is a single branch per sample.CSG Editing
Click on the visualization below to carve or add material. The sphere brush applies CSG operations in real-time, exactly how Plenum handles player edits.
Each click places a sphere brush. In subtract mode, the operation is max(current, -brush): it carves away material where the brush overlaps.
In add mode, it's min(current, brush), a union that fills in new material.
Plenum writes the resulting SDF values back into the DAG, and only the affected chunks get re-meshed. Because the DAG deduplicates identical subtrees, adding material to one spot doesn't balloon memory for unrelated regions.
From SDF to Triangles
An SDF is just numbers. To render it, we need triangles. Plenum uses Surface Nets, an algorithm that places a vertex inside each cell that straddles the surface (has both positive and negative corners), then connects neighboring vertices into quads.
The result is smooth, organic geometry with no staircase artifacts and no sharp voxel edges. And because the meshing reads from the SDF buffer rather than a block grid, it naturally handles arbitrary shapes from the CSG operations.
The meshing pipeline is heavily SIMD-optimized. I wrote a separate post about that: Marchless Cubes: SIMD-Accelerated Voxel Meshing.
Architecture
Plenum is designed as a library, not a monolith. The core crate provides:
- dag: Sparse Voxel DAG with hash deduplication, reference counting, and implicit LOD
- sdf: SDF traits (
ProceduralSdf,HybridSdf,SdfSampler) - mesh: Surface Nets and Dual Contouring with LOD boundary stitching
- lod: Level-of-detail constraint propagation
A separate bevy_plenum crate provides Bevy integration. The asteroid you can mine
on the homepage of this site is powered by Plenum running in the browser via WebAssembly.
What I Learned
Building Plenum taught me that the best data structure isn't always the most complex one; it's the one that matches your access patterns. The DAG is conceptually simple (it's just a hash-consed octree), but it unlocks massive memory savings because voxel worlds are inherently sparse.
SDFs are a joy to work with. They compose, they blend, they don't care about topology. The tradeoff is meshing cost, but with SIMD and smart early-exit strategies, that cost is very manageable, even in the browser.