Ragnavoxel docs

Content as text

Materials, voxel types, microvoxel models and prop models in TOML; parametric parts, ranges sampled per instance, branches, scatters and ASCII voxel art; the asset library; and model grids for bodies, hair and gear.

Everything a Ragnavoxel world is made of can be read and edited as text. Materials, voxel types and models are TOML files a project lists in its project file, loaded by voxel::AssetLibrary (voxel/AssetLibrary.hpp). Units in model files are voxels unless stated; colours are linear RGB unless written as sRGB hex.

Materials

[[material]]
name = "granite_dartmoor"          # unique id
albedo = [0.37, 0.30, 0.26]        # linear reflectance
roughness = 0.62                   # 0 mirror .. 1 matte
subsurface = 0.0                   # translucency (leaves, petals)
emissive = [0, 0, 0]
seam_color = [1.0, 0.62, 0.22]     # the emissive seam round merged tiles
seam_strength = 0.55
flags = ["opaque", "collidable"]   # opaque water foliage emissive collidable dressed glass
face = { pattern = "granite", amplitude = 70, frequency = 150 }   # default face parameters
# Optional: up-facing faces grow blades in the grass pass.
grass = { height = [0.28, 0.78], density = 680, width = 0.034, stiffness = 1.0,
          base = [0.03, 0.075, 0.018], tip = [0.15, 0.27, 0.055], flowers = 0.025, flower = [0.85, 0.8, 0.55] }

Material files load in the order listed, and later files may use the materials of earlier ones. Air is material 0 and is registered for you. See Voxels and units for the flags and the face parameters.

Voxel types

A named voxel type is a material with face parameters for each side, and tint-jittered variants:

[voxels.bark]
material = "bark_oak"
variants = 4          # tint-jittered copies
jitter = 10           # +/- tint jitter per channel
faces.side = { pattern = "bark_oak", amplitude = 110, frequency = 150 }
faces.top  = { pattern = "cells", amplitude = 30 }
# keys: all, side, top, bottom, px, nx, pz, nz (applied in that order)

Voxel types can be global (standalone terrain files, loadVoxelTypes) or local to a model file.

Microvoxel models

One model a file: trees, shrubs, herbs, fungi, rocks and fauna, built from parametric parts and stamped into the world.

[model]
name = "english_oak"
kind = "tree"                 # tree | shrub | herb | fungus | rock | fauna
description = "Quercus robur ..."

[variation]
scale = [0.85, 1.2]           # uniform scale per instance
yaw = "free"                  # free | quarter | none

[[part]]
id = "trunk"
type = "capsule"              # capsule | ellipsoid | box | layers | branches | scatter
from = [0, -38, 0]            # any number may be a [lo, hi] range, sampled per instance
to = [[-12, 12], [340, 520], [-12, 12]]
radius = [[22, 32], [14, 20]] # start, end
segments = 3                  # polyline segments
bend = [0, 14]                # lateral jitter of interior points
shell = "bark"                # voxel names (model-local or global)
core = "heartwood"
core_inset = 3
alt = "lenticel"              # optional alternate shell voxel
alt_ratio = 0.1
grain = "micro"               # micro: per voxel | clump: per cell of bricks
priority = "wood"             # foliage | wood | rock | embed | overlay

Ranges are first-class. Any number may be [lo, hi], sampled for each instance from its seed, so one file grows a whole wood of different oaks.

Part types

  • capsule: a tapered cylinder along a polyline (from, to, radius, segments, bend).
  • ellipsoid: center, radii, fill or shell, an optional top voxel for upward faces, noise (surface wobble), holes (see-through gaps, 0 to 1), dome = true keeps the upper half, and grain = "clump" with cell = 2 fills quarter-block leaf clumps.
  • box: min, max, fill, optional top.
  • branches: grown from a parent (a capsule or another branches part): count, along (0 to 1 along the parent), pitch (degrees above horizontal), length, relative_radius = [start, end] (fractions of the parent’s radius), droop, and a tip template placed at each end.
  • scatter: count, scatter_radius, and a child template placed at each point.
  • layers: ASCII voxel art. legend maps single characters to voxel names (. or anything unmapped is air); layers go bottom to top, each a list of rows along +z, each row a string along +x; origin is the art’s minimum corner relative to the anchor. Layers turn by quarter turns and are never scaled.

A part with template = true is placed only through tip or child, its coordinates relative to that anchor (plus offset).

Using models

rv::voxel::AssetLibrary assets(materials, &palette);
assets.loadMaterials("assets/materials/geology.toml");  // before the palette is created
assets.loadVoxelTypes("assets/terrain/rocks.toml");
assets.loadModels("assets/models");                    // a folder, searched recursively

rv::voxel::PrimitiveSet prims;
if (const auto* oak = assets.model("english_oak")) assets.instantiate(*oak, base, seed, prims);
scene.stamp(prims);

instantiate expands a model into world-space primitives at a base position, drawing yaw and variation from the seed; another overload grows it to a given shape. parseModel(text, file) parses a model without registering it (editors use it for live previews of unsaved edits) and throws with the file and the reason on any error; putModel and removeModel change the library at run time.

Prop models

model/PropModel.hpp. Furniture, tools, fittings and any hard-surface piece, built from a handful of shapes and rasterised into a fine voxel grid (typically 1/128 m to 1/32 m), then greedy-meshed into actor vertices, so props keep the world’s voxel look at any size. A file holds any number of [[model]] tables:

[[model]]
id = "stool"
cell = 0.0156                 # voxel size in metres
[[model.part]]
shape = "cylinder"            # box ellipsoid cylinder capsule cone torus
a = [0.0, 0.42, 0.0]          # ends (cylinder, capsule); box/ellipsoid/torus use `at`
b = [0.0, 0.46, 0.0]          # cone uses `base` and `tip`
r = 0.17                      # cylinder, cone radius; capsule `ra`/`rb`; torus `R` ring, `r` tube
colour = "#a2723e"            # sRGB hex; `colour2` the pattern's second colour
pattern = "rings"
period = 0.06                 # metres per repeat of the pattern
rough = 0.8                   # roughness; also emissive, metal, unlit, translucent, foliage
carve = false                 # true removes the shape from what came before

Box parts also take half (half extents) and yaw, pitch, roll; ellipsoids radii; tori axis. The patterns are wood grain (along x, y or z), rings, staves, stripes, checks, knotwork, weave, mail, mottle, a drawn map and masonry. Parts stamp in file order, and a carving part removes what came before it. Games add their own keys beside these (lights, seats, work spots) and read them themselves; the parser ignores what it does not know, and the editor still edits them.

Model grids: bodies, hair and gear

model/VoxelGrid.hpp. Actors (bodies, hair, clothes, armour, held gear) are built from signed distance functions rasterised into a voxel grid of any cell size, then greedy-meshed into flat-shaded faces. Each cell carries the joint it rides on, so one grid holds a whole articulated body, and skinned meshing keeps such bodies closed under any pose. LostVale grows its heroes at 1/64 m, their heads at 1/128 m and each eye at 1/1024 m in grids of their own this way.

Content checks

The editor’s --validate loads every model with the engine’s own loader, and --selftest checks that every content file round-trips byte for byte through the editor’s TOML handling (see The editor).

Written from the engine's headers and source as of commit 02e81b2 (2026-11-01).