Builtin Library
README.md
voxbin
Binary encode/decode for voxel records. The on-disk format for shapes,
world chunks, templates, and captures. Mirrors the in-memory record
shape used by Voxel.Grid so a round-trip through encode / decode
is lossless.
Container layout (in order on disk): fixed header (12 bytes — magic
VBIN, version, kind byte, flags) → geometry header (16 bytes — size,
blockSize) → palette section (count + entries) → grid section (dense or
chunked, with optional RLE compression and u8/u16 index width) → optional
custom-data trailer → CRC32 trailer. All multi-byte fields are little-
endian. See the source for the per-byte breakdown.
Strict policies:
- CRC32 trailer is always present. Decoder verifies it before parsing — corrupt files reject loudly.
- Custom-data trailer is dense-only. Chunked records with
hasCustomData = trueraise (issue #1269 tracks the follow-up). - Encoder picks RLE vs raw bytes per grid section by emitting whichever is shorter; RLE flag is set only when strictly smaller.
- Palette width auto-selects u8 (≤255 entries) vs u16 (≥256 entries).
Exports
M.encode(record: VoxbinRecord) -> string— encode an in-memory record to bytes. Raises on shape mismatch.M.decode(bytes: string) -> VoxbinRecord— decode bytes back to a record. Verifies CRC32 first.M.write(path: string, record: VoxbinRecord) -> boolean— encode +vfs.writein one step.M.read(path: string) -> VoxbinRecord—vfs.read+ decode in one step.
Types:
Vec3 = { x: number, y: number, z: number }PaletteEntry = { name?, color?, atlasName?, faceTiles?, material? }ChunkKey = { number }(i32 triple{ x, y, z })ChunkEntry = { chunkKey: ChunkKey, grid: { number } }VoxbinRecord = { kind, chunked?, hasCustomData?, size, blockSize, palette?, grid?, chunks?, chunkSize?, customData? }
Usage
local Voxbin = require("@builtin::systems.voxelEngine.voxelEngine.voxbin")
-- Round-trip in memory:
local bytes = Voxbin.encode(record)
local record = Voxbin.decode(bytes)
-- VFS wrappers:
Voxbin.write("/source/voxel/shapes/my.vbin", record)
local record = Voxbin.read("/source/voxel/shapes/my.vbin")
Notes
- Format version is currently
1. Unknown versions raise on decode — there is no implicit migration. - The encoder never hardcodes a path; callers supply it.
M.write/M.readare thin VFS wrappers. - Custom-data payload bytes are produced by a minimal Luau-native table
serializer because the engine runtime exposes neither
msgpacknorjsonat the time of writing. Issue #1270 tracks the migration to msgpack once it lands. Supported payload types are nil / boolean / number / string / nested tables with string or 1-based integer keys. - RLE markers are sentinel-prefixed (
0xFFu8 /0xFFFFu16) and literal payload bytes that collide with the sentinel are escaped as run-of-1 markers. Decoder always treats a sentinel as a marker start. - The CRC32 polynomial is the standard IEEE 802.3 / PKZIP variant
(
0xEDB88320reflected) — any external CRC32 tool computes the same value over the same bytes.