Builtin Library
README.md
voxelEngine
Public API for the voxel system. Single entry point for creating and
editing voxel worlds and shapes. Tools and custom code call into here;
nobody touches the VoxelWorld / VoxelShape components directly.
Two top-level concepts:
- World — singleton entity carrying a chunked
VoxelWorldgrid. Created lazily on the firstcreateWorldcall, found by name later viaworld()/hasWorld(), destroyed withdestroyWorld(). - Shapes — per-entity dense or chunked
VoxelShapegrids. Spawned freely; each carries its own voxbin path on disk underConfig.get("paths.shapes")/<entityId>.voxbin.
Templates are stored under Config.get("paths.templates")/<name>.voxbin
and instantiated via fromTemplate. Captures (snapshots of a world
region) land at Config.get("paths.captures")/<name>.voxbin.
Exports
Module-level:
M.world() -> Handle?— handle to the singleton VoxelWorld, or nil.M.createWorld(opts: CreateWorldOpts?) -> Handle— create or reuse the singleton.M.destroyWorld()— despawn the singleton world if it exists.M.hasWorld() -> boolean— true when the singleton exists.M.shape(name_or_id) -> Handle?— handle to an existing shape entity.M.createShape(opts: CreateShapeOpts) -> Handle— spawn a new shape entity.M.destroyShape(name_or_id)— despawn the shape entity if it exists.M.fromTemplate(name: string, opts: FromTemplateOpts?) -> Handle— instantiate frompaths.templates/<name>.voxbin. Supports live-link, detached, and memory-only modes.M.saveAsTemplate(shape: Handle, name: string, opts: SaveAsTemplateOpts?) -> string— write a shape's grid as a named template.M.listTemplates() -> { string }— enumerate template names (no extension).M.deleteTemplate(name: string)— remove a template file.M.copyTemplate(srcName: string, dstName: string) -> string— duplicate a template file.M.captureArea(opts: CaptureAreaOpts) -> any— flatten a region of world + shapes into a dense voxbin record (and optionally save it).M.scatter(templateName: string, opts: ScatterOpts) -> { Handle }— bulk-spawn instances across a region.M.stamp(templateName: string, opts: StampOpts) -> Handle— single placement, either as a new entity (mode = "instance", default) or rasterised into the world grid (mode = "merge").M.composite— re-export of the composite-prop module.M._resolvePivot(value) -> { number, number, number }?— internal pivot helper, exposed for composite reuse.
Handle methods (returned by createWorld / createShape / fromTemplate / world / shape / scatter / stamp):
h:setBlock(x, y, z, name)/h:getBlock(x, y, z)h:fillBox(min, max, name)/h:fillSphere(center, radius, name)h:fillCylinder(p1, p2, radius, name)/h:fillLine(p1, p2, name)h:clear()— clear every cell.h:setPalette(palette)/h:addPaletteEntry(name, color)h:bounds() -> Bounds?— inclusive bounds of every filled cell.h:flush()— flush pending dirty-chunk remeshes.h:save()— write the grid to the component's dataPath.h:dirtyChunks() -> { any }h:isLive() -> boolean/h:templatePath() -> string?h:bake() -> Handle— break the live-link, copy into a private dataPath. Shape-only.h:saveAsTemplate(name, opts) -> stringh:exists() -> boolean/h:destroy()h:setPosition(x, y, z) -> Handle/h:getPosition() -> Vec3Map?h:setRotation(r) -> Handle/h:getRotation() -> Vec3Map?(Euler radians, ZYX)h:setParent(parentEntityId) -> Handleh:copyRegion(min, max) -> any/h:pasteRegion(offset, regionData, opts)h:mirror(axis)/h:translate(dx, dy, dz)h:getNeighbors(x, y, z) -> anyh:queryRegion(min, max, opts) -> { any }/h:countByName(min, max) -> { [string]: number }h:raycast(origin, dir, maxDist) -> any/h:sphereQuery(center, radius) -> { any }/h:lineQuery(p1, p2) -> { any }h:clone(opts) -> Handle— shape-only. Detached duplicate.h:snapToWorld(opts) -> Handle— quantise transform or shift voxel data to the world grid.
Types:
Vec3Map = { x: number?, y: number?, z: number? }Vec3Array = { number }Vec3Like = Vec3Map | Vec3ArrayColorMap = { r: number?, g: number?, b: number? }ColorArray = { number }ColorLike = ColorMap | ColorArrayPaletteEntry = { name: string, color: ColorLike, material: any?, atlas: any?, faces: any? }Palette = { PaletteEntry } | { [string]: ColorLike }Bounds = { min: Vec3Map, max: Vec3Map }Handle = { entityId: any, componentName: string, [string]: any }CreateWorldOpts/CreateShapeOpts/FromTemplateOpts/SaveAsTemplateOptsCaptureAreaOpts/CaptureAreaBoxSelector/CaptureAreaSphereSelectorCloneOpts/SnapToWorldOptsRegionSpec/ScatterOpts/StampOptsQueryRegionOpts/PasteRegionOpts
Usage
local Voxel = require("@builtin::systems.voxelEngine")
-- Create the singleton world, write a couple of cells.
local world = Voxel.createWorld({
size = { x = 64, y = 32, z = 64 },
palette = { stone = { r = 0.5, g = 0.5, b = 0.5 } },
})
world:setBlock(0, 0, 0, "stone")
world:fillBox({ 1, 0, 0 }, { 3, 0, 3 }, "stone")
-- Spawn a shape from a template, live-linked to the template file.
local tree = Voxel.fromTemplate("tree", {
position = { x = 10, y = 0, z = 5 },
})
-- Scatter 20 copies inside a region.
local hs = Voxel.scatter("tree", {
region = { box = { min = { 0, 0, 0 }, max = { 50, 0, 50 } } },
count = 20,
snap = true,
})
-- Capture a region of world + shapes into a dense voxbin record.
local rec = Voxel.captureArea({
box = { min = { 0, 0, 0 }, max = { 15, 15, 15 } },
})
Notes
- Handles are thin wrappers over an
entityId+ a component name. Each method re-resolves the underlying component on every call, so the handle stays usable across hot-reload cycles. - All
h:setBlock/h:fillBox/ … calls forward to the component via colon-call (c:method(...)) — dot-call would dropselfand the strict typed-function checker would read args from the wrong slots. fromTemplatedefaults tolive = true+persist = true— live-linked instance that auto-bakes private edits to disk. Passlive = falsefor a detached copy with its own dataPath, orpersist = falsefor a memory-only instance.Voxel.scatter/Voxel.stamp(mode = "merge") obey the play-mode write gate enforced byassertModeAllowsWrite. In play mode, callers must toggleConfig.set("allowPlayWrites", true)(or set the[voxel].allowPlayWritesWORLD_SETTINGS override) to unlock voxbin writes.Voxel.stamp(mode = "merge")only supports yaw rotations of 0 / 90 / 180 / 270 degrees — voxel grids require axis-aligned stamps. Non-axis-aligned yaws raise.- The
compositesub-module is reachable asVoxel.composite. It builds kitbashed.voxcompprops that re-resolve their children at instantiation time;fromTemplatetransparently dispatches to it when the named template resolves to a.voxcompmanifest.