voxel
stable3D voxel chunk plugin for storing, querying, and manipulating cubic grids of block IDs. Supports fill, region fill, ray marching, surface detection, serialization, and neighbor queries.
use plugin voxel::{VoxelChunk, get, set, …} Functions (14)
- VoxelChunk Creates a cubic voxel chunk of size size size voxels, all initialized to air (block ID 0).
- get Read the block ID at a position
- set Write a block ID at a position
- fill Fill all voxels with one block ID
- clear Set all voxels to air (0)
- count_solid Count non-air voxels
- size Get the chunk size
- to_flat_table Export all voxels as a flat table
- neighbors_6 Get the 6 face-adjacent positions
- is_surface Check if a voxel is exposed to air
- fill_region Fill a rectangular region with a block ID
- ray_march Cast a ray and find the first solid voxel hit
- serialize Serialize the chunk to bytes
- deserialize Deserialize bytes into a VoxelChunk
Overview
voxel stores a cubic block of the world as a dense grid of block IDs — one byte
per voxel, 0 meaning air and any non-zero value meaning a solid block. A chunk
is created with VoxelChunk(size) and returned as an opaque handle that you mutate
in place through method calls; there is no separate "world" object, just the chunk
you hold. Coordinates run from 0 to size - 1 on each of the X, Y, and Z axes,
and reads or writes outside that range are handled gracefully rather than crashing.
Use it whenever you need a compact, in-memory voxel volume: building terrain or
structures with fill, set, and fill_region; querying the grid with get,
count_solid, neighbors_6, and is_surface (the basis of greedy meshing);
casting rays for picking or line-of-sight with ray_march; and persisting a chunk
to bytes with serialize / deserialize.
Common patterns
Build a solid floor, then carve a hole and count what remains:
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.fill_region(0, 0, 0, 15, 0, 15, 1)
chunk.set(8, 0, 8, 0)
print("solid voxels: {chunk.count_solid()}")
Drop a ray straight down onto the terrain and report the hit:
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.fill_region(0, 0, 0, 15, 2, 15, 1)
let hit = chunk.ray_march(8.0, 15.0, 8.0, 0.0, -1.0, 0.0, 64)
if hit != nil {
print("hit block {hit["block_id"]} at y={hit["y"]}")
}
Round-trip a chunk through its binary format:
use plugin voxel::{VoxelChunk, deserialize}
let chunk = VoxelChunk(8)
chunk.fill(1)
let restored = deserialize(chunk.serialize())
print("restored solids: {restored.count_solid()}")
Creates a cubic voxel chunk of size size size voxels, all initialized to air (block ID 0).
Creates a cubic voxel chunk of size * size * size voxels, all initialized to air (block ID 0). Coordinates range from 0 to size - 1 on each axis.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.set(0, 0, 0, 1)
chunk.set(8, 8, 8, 2)
print(chunk.count_solid())
Read the block ID at a position
Returns the block ID stored at position (x, y, z). Returns 0 (air) if the position is out of bounds.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(8)
chunk.set(3, 2, 1, 5)
print(chunk.get(3, 2, 1))
Reading outside the chunk returns air rather than erroring, so bounds checks are optional:
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(8)
print(chunk.get(999, 0, 0))
Write a block ID at a position
Sets the block ID at position (x, y, z). Out-of-bounds positions are silently ignored. Block IDs are bytes (0-255), where 0 means air.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.set(0, 0, 0, 1)
chunk.set(1, 0, 0, 2)
chunk.set(0, 1, 0, 3)
Fill all voxels with one block ID
Sets every voxel in the chunk to block_id. Useful for initializing solid or uniform chunks.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(8)
chunk.fill(1)
print(chunk.count_solid())
Set all voxels to air (0)
Sets all voxels to air (block ID 0).
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(8)
chunk.fill(1)
chunk.clear()
print(chunk.count_solid())
Count non-air voxels
Returns the number of voxels with a non-zero block ID.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(4)
chunk.fill(1)
chunk.set(0, 0, 0, 0)
print(chunk.count_solid())
Get the chunk size
Returns the side length of the cubic chunk.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(32)
print(chunk.size())
Export all voxels as a flat table
Exports all voxel data as a flat indexed table (1-based) in Z-major, Y-middle, X-minor order. Useful for serializing chunk data to Zolo-native structures.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(2)
chunk.fill(1)
let flat = chunk.to_flat_table()
print(flat[1])
Get the 6 face-adjacent positions
Returns a list of 6 tables, each with x, y, z fields for the face-adjacent neighbors of the given position. Does not check bounds — positions may be outside the chunk.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
let nbrs = chunk.neighbors_6(5, 5, 5)
print(nbrs[1]["x"])
print(nbrs[1]["y"])
Check if a voxel is exposed to air
Returns true if the voxel at (x, y, z) is solid and has at least one air neighbor (or is at the edge of the chunk). Used for mesh generation to find exposed faces.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(4)
chunk.fill(1)
print(chunk.is_surface(0, 0, 0))
print(chunk.is_surface(2, 2, 2))
Scan a chunk and collect just the exposed voxels — the set you would actually mesh:
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(4)
chunk.fill(1)
let exposed = 0
for x in 0..4 {
for y in 0..4 {
for z in 0..4 {
if chunk.is_surface(x, y, z) {
exposed = exposed + 1
}
}
}
}
print("exposed faces source: {exposed}")
Fill a rectangular region with a block ID
Fills the rectangular region between corners (x1,y1,z1) and (x2,y2,z2) (inclusive) with block_id. Corner order does not matter — the region is computed correctly regardless.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.fill_region(0, 0, 0, 7, 3, 7, 1)
print(chunk.count_solid())
Carve a hollow room by filling a solid block and then clearing its interior:
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.fill_region(0, 0, 0, 9, 9, 9, 1)
chunk.fill_region(1, 1, 1, 8, 8, 8, 0)
print("walls only: {chunk.count_solid()}")
Cast a ray and find the first solid voxel hit
Casts a ray from origin (ox, oy, oz) in direction (dx, dy, dz) and returns the first solid voxel hit as a table with x, y, z, and block_id. Returns nil if no solid voxel is hit within max_steps (default 256).
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(16)
chunk.set(8, 0, 8, 1)
let hit = chunk.ray_march(8.0, 10.0, 8.0, 0.0, -1.0, 0.0, 64)
if hit != nil {
print("Hit block {hit["block_id"]} at {hit["x"]},{hit["y"]},{hit["z"]}")
}
Serialize the chunk to bytes
Serializes the chunk into a compact binary format: a "ZVOX" magic header (4 bytes), the chunk size as a little-endian u32 (4 bytes), then the raw voxel bytes. Use deserialize to restore it.
use plugin voxel::{VoxelChunk}
let chunk = VoxelChunk(8)
chunk.fill(1)
let bytes = chunk.serialize()
print(bytes)
Deserialize bytes into a VoxelChunk
Deserializes bytes produced by serialize back into a VoxelChunk handle. Errors if the magic bytes are wrong or the data length does not match the declared size.
use plugin voxel::{VoxelChunk, deserialize}
let chunk = VoxelChunk(8)
chunk.set(3, 3, 3, 7)
let bytes = chunk.serialize()
let restored = deserialize(bytes)
print(restored.get(3, 3, 3))