Skip to content

Voxel data format #4

@dougbinks

Description

@dougbinks

This issue is to discuss the format for voxel data.

High level voxel data description

I propose that in order to handle sparsity we allow a single voxel object to be divided up into volumes (for example Minecraft map chunks). If we use glTF these would likely be specified in the JSON part as extensions to the spec.

  1. Voxel Object a set of Voxel Volumes, much like a Mesh is a set of primitives.
    1. Attributes (or do we move this to the voxel volume below as a way to support non-interleaved data?)
    2. Volume AABB min: 3 integers (or doubles?)
    3. Volume AABB max: 3 integers (or doubles?)
  2. Voxel Volume a set of Voxels occupying a 3D box volume.
    1. Offset coordinates: 3 integers
    2. Volume dimensions: 3 unsigned integers. We may want to limit this size.
    3. Material Type (indexed palette or single material)
    4. Material (integer index to palette or material depending on above)
    5. Buffer View (buffer, offset and length)
    6. Encoding (this could be moved into the voxel object)

If we use JSON the integers above will have precision equal to that of a double maximum, but implementations of gltf readers may differ.

The names Voxel Object and Voxel Volume are tbd.

Voxel Volume Buffer Encoding

  1. Run length encoded or other simple format?
  2. Bit-depth of count for RLE and for integer values may need to be specified.
  3. If we RLE encode do we do so as rows, Morton ordered, or NxN blocks?
  4. Do we add bits to encode data presence (i.e. data has certain attributes, is it a single voxel or has a count, see below)
  5. Mesh Optimizer may be useful as a pass over the final data (see references below)

Data Presence

Essentially imagine you have two voxel attributes, MATERIAL_INDEX and DENSITY plus we have the RLE_LENGTH. The first bits of the encoding will thus be 3 bits to encode whether these exist in the following data. So 111 would mean we have all, and 110 would mean we have a single voxel with both MATERIAL_INDEX and DENSITY but no RLE_LENGTH, thus making random voxels more efficient than if we needed to encode all. This is similar to the description for the voxel Disk format in Roblox, except there Arseny uses the term occupancy rather than density.

So for the following voxel attributes (bit depth, usage, and type etc. described in Attibutes property as above):
MATERIAL_INDEX: 8bit uint (we can allow lower bit depths as some volumes will have fewer materials etc.)
DENSITY: 8bit uint normalized
with also an RLE_LENGTH : 8bits.

Presence (3bits) if Presence 0 (8bits) f Presence 1 (8bits) if Presence 2 (8bits)
012 MATERIAL_INDEX DENSITY RLE_LENGTH-2

Note that since we never see 0 voxels, and 1 voxel is encoded with the RLE_LENGTH a value of 0 in the RLE_LENGTH field can be used to represent 2 voxels.

Example bit pattern for a single voxel with no density (where M is material bits):

100 MMMMMMMM

Example bit pattern for 2 voxels with both material index and density (where M is material bits, D density):

111 MMMMMMMM DDDDDDDD00000000

Example bit pattern for 258 empty voxels:

001 11111111

References:
https://zeux.io/2017/03/27/voxel-terrain-storage/
https://aras-p.info/blog/2023/02/02/Float-Compression-4-Mesh-Optimizer/
https://github.com/zeux/meshoptimizer?tab=readme-ov-file#mesh-compression

Your thoughts are appreciated below!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions