Dimensions
side = 1 << levels
The source length must equal side³. Partial volumes
and non-power-of-two sides are rejected. The maximum single
channel is 512³ values.
Voxel octree storage for .NET
Tedd.Voxtree encodes one cubic voxel channel as a versioned, contiguous byte sequence. Build from z-fastest linear input or Morton Encoding (also known as the Z-order curve or Lebesgue curve), then query owned or caller-managed storage without expanding the tree first. Group independent channels in immutable chunks, then place them in bounded sparse regions or signed 64-bit streamed worlds.
dotnet add package Tedd.Voxtree
Octree<UInt32>
LEVELS532³ voxels
INPUT LAYOUTMortonZ-order curve
STORAGETreeVersioned bytes
channel 0
tree.Get(13, 7, 21)
42
Sparse voxel treeUniform octants collapse; incompressible data stays dense
SIMD-enabled pathsRuntime-vectorized scans and equality operations on .NET 10/11
Zero-copy / allocation-freeBorrowed lookup and caller-buffer build paths
Concurrent read modelImmutable publication and shared-lock world reads
Memory layout and format invariants
The encoder accepts a complete cubic channel. It does not infer dimensions, strides, component packing, or buffer ownership.
Dimensions
side = 1 << levels
The source length must equal side³. Partial volumes
and non-power-of-two sides are rejected. The maximum single
channel is 512³ values.
Linear indexing
(x × side²) + (y × side) + z
z changes fastest, followed by y, then
x. This order is the
DenseVoxelLayout.Linear input contract.
Morton indexing
DenseVoxelLayout.MortonMorton encoding maps three-dimensional coordinates to a one-dimensional index by interleaving coordinate bits. Conversion writes to caller-provided spans.
Encoded representation
Uniform regions collapse to value leaves. If a tree would not be smaller, the encoder selects a dense payload. The format contains offsets and no process-local pointers.
Type and ownership model
Dense buffers hold uncompressed values. An octree encodes one channel. A chunk publishes related channels. A world maps chunk addresses to memory and persistent storage.
Edit
Use z-fastest linear storage or Morton encoding.
DenseVoxelBlockSpan overlays caller-owned,
channel-major memory.
Encode
Octree<T> owns its encoded byte array.
OctreeSpan<T> is a stack-bound view over borrowed,
immutable bytes.
Compose
Publish an immutable set of independently encoded channels with common dimensions. Rebuild one channel while retaining the others.
Stream
Map signed global coordinates to power-of-two chunks by arithmetic shift. Load absent residents from configured storage on demand.
Why choose it for engine code
Use Tedd.Voxtree when voxel data is read more often than it is rewritten and chunk lifetime must be explicit. These guarantees concern representation, ownership, and allocation; throughput remains workload- and hardware-dependent.
Sparse voxel encoding
Uniform octants become value leaves in one pointer-free byte sequence. When tree encoding would not be smaller, the encoder selects a dense payload instead.
SIMD-enabled paths
The .NET 10 and .NET 11 builds use runtime-vectorized uniform-region detection and equality search/count operations. The .NET Standard 2.1 target uses portable scalar fallbacks with the same format and API.
Zero-copy and allocation control
Build into Span<byte> and query borrowed bytes through
OctreeSpan<T>. Valid caller-buffer builds and spatial
queries allocate no managed heap memory; owned builds allocate one
final array.
Multi-threading contract
Octree<T> publishes new immutable arrays lock-free.
OctreeWorld<T> permits concurrent readers, serializes
writers, and supplies batch scopes. Mutable editors remain
exclusive to one worker.
Multiple channels
Keep material, collision, lighting, fluid, or application-defined fields in separate encodings. A channel update can reuse every unchanged channel and preserve their uniform regions.
Bulk mutation
A hot chunk retains mutable, channel-major Morton-encoded values across repeated edits. Readers retain the previous immutable snapshot until commit; stale publication is rejected.
Deferred point edits
DeferredOctreeChunk<T> retains pooled point edits
and materializes only affected channels on overflow. A synchronized store
exposes deduplicated chunks for bounded worker repackaging. Sparse
mode is configurable and enabled by default.
Sparse worlds
OctreeWorld<T> stores sparse outer branches and
resident chunks without a complete chunk-reference grid. It also
distinguishes unloaded space from known air.
Streaming and persistence
Load chunks on demand, track dirty snapshots, save atomically, and evict older residents against a soft serialized-byte target. Compression and LOD namespaces are explicit storage options.
Encoded spatial queries
Execute point lookup, filtered existence and counts, bounded hit enumeration, region decode, and nearest-match search directly against encoded regions. Reusable neighborhood caches refresh only entering slabs during local movement.
Synchronous world persistence
With storage configured, Get may synchronously load a
nonresident chunk. UnloadChunk writes a dirty chunk
before release. SaveAllChunks writes dirty residents
only.
EstimatedResidentBytes counts serialized packet bytes,
not CLR object or process memory.
var storage = new ChunkStorageOptions("world/chunks")
{
Compression = ChunkCompression.Brotli,
CompressionLevel = CompressionLevel.Fastest
};
var world = new WorldEntity(
chunkSize: 32,
channelCount: 4,
storage,
maxResidentBytes: 512L * 1024 * 1024);
world.SetChunk(-1, 0, 2, chunk);
world.SaveAllChunks();
world.UnloadChunk(-1, 0, 2);
uint material = world.Get(
channel: 0, x: -1, y: 7, z: 64);
Ownership and lifetime
ref struct, it cannot
cross await or yield, or be stored on the
managed heap.
Operational constraints
Tedd.Voxtree does not generate or select LOD data, simulate entities, or define the meaning of unloaded space. The application owns those policies.
WorldEntity, HotOctreeChunk, and standalone
DeferredOctreeChunk owners are not thread-safe.
DeferredChunkStore synchronizes its operations, while
OctreeWorld uses a
ReaderWriterLockSlim and provides explicit read and write
batches. The library does not create worker threads; the application
controls scheduling and partitions mutable editors by worker.
One encoded channel is limited to levels 0 through 9: a maximum side
length of 512 and 134,217,728 dense values. World depth
and chunk addressing are separate from this per-channel limit.
Tedd.Voxtree 2.x
Select the dense layout, value width, ownership model, and world synchronization policy before integrating the query path.