Example project
Gododin voxel stream
examples/voxel_stream/README.md
A seeded voxel landscape streamed around a flying camera. Pure Odin generates
and greedy-meshes 32³ chunks on one cancellable worker. The authored
Voxel_World node owns scheduling, editable voxel storage, staging and server
RIDs. Godot owns the scene, material, sky, sun, shadows, fog, camera and UI.
The useful language boundary is the whole chunk: generation and meshing stay in Odin; a handful of calls and whole-buffer copies publish a finished surface. This example does not contain a GDScript performance baseline, so its timings do not establish a speedup over GDScript or another native language.

Try it
Click the landscape to capture the mouse. WASD flies, Space/Shift climb/sink, Ctrl boosts speed, and Esc or right click releases. Left click while captured carves a sphere at the first occupied voxel within twelve blocks. A miss is harmless. Enter a whole-number seed and press Generate to rebuild; the radius slider takes effect on Generate. Edits are retained only while their chunks remain loaded; leaving and revisiting regenerates those chunks from the seed.
Read the code
Start with the scene, camera and UI, the native world class, the background worker, and the chunk mesher. A completed chunk is the unit of work that crosses from Odin to Godot.
Run
Project setup covers the tools and SDK connection.
The machine-local compiler, SDK and engine paths in test.sh must agree with
.gododin/local.toml and .gododin/authority.toml. Use the exact target in the
attached SDK/example lock; the active-target record
owns its identity. Historical dev5 measurements below retain their actual engine.
./test.sh # pure tests, doctor, generate, check, accepted debug build
./test.sh eval # pure subsystem measurements and hard replay assertions
./test.sh run # windowed flight, edit, replay, saturation and teardown
./test.sh probe # settled visual capture and actual surface-byte checks
./test.sh play # interactive use of the same accepted publicationSet GODODIN_PROFILE=release for both the build and subsequent run to measure
optimized native code. Debug/release acceptance caches are separate and the
runner checks the publication profile. Eval and probe launches disable VSync
and the engine FPS cap; ordinary play keeps the project's presentation settings.
Engine modes refuse stale source/build combinations and refuse to launch while
another Godot process is running. Keep the window frontmost. The runtime is
prepared under .gododin/run/runtime; logs and accepted source hashes are in
.gododin/test/<profile>/, and each launch retains its own isolated HOME. The run and probe modes
save a screenshot there.
Engine exit status, error diagnostics and ownership receipts are checked.
Keep --rendering-driver opengl3 for this recorded path. Upstream dev5 had
GH-123394, windowed
Forward+/Metal heap corruption in texture freeing. The runner pins Compatibility
for all windowed modes, including interactive play. The project also defaults to Compatibility. Do not override it to Forward+
to reproduce these receipts. The dev6 migration
records the engine fix, but it does not requalify this example on Metal.
Changing the runner's renderer requires a separate inspected run.
Generation, meshing and ownership
The subsystem imports neither Godot, Gododin nor Raylib. Explicit voxel arrays, a four-plane neighbor apron, a per-axis greedy mask and retained byte buffers make its working set visible. Generation uses seeded gradient noise, a terraced heightmap and cave carving. Repeatability is measured for the pinned compiler and machine; floating-point noise is not a promise of identical output on every architecture or compiler setting.
Only an opaque voxel adjoining air emits a face. Different opaque material IDs inside solid terrain do not emit hidden faces. A chunk never emits its neighbor's geometry; below-layer bedrock only occludes. All six face directions use Godot's clockwise outward winding. The fixed uint16 layout admits at most 65,532 vertices; pathologically fragmented chunks are refused before an out-of-bounds write. This is an explicit example limit, not support for arbitrary voxel data.
Mesh_Workspace owns the producer buffers. mesh_chunk_into borrows them and
allocates nothing; the convenience mesh_chunk API allocates owned output and
is not the streaming path. Four worker result slots each retain a workspace and
voxel storage. A full result queue applies backpressure instead of dropping
required completions. A consumer leases a slot until worker_release_result.
The pending queue is bounded at 128 jobs; the native scheduler retries admission
while stationary, so a radius-six world can settle to all 169 chunks.
Cancellation invalidates obsolete queued/published work and wakes a blocked
producer. Shutdown joins the worker and checks its allocation accounting.
The native owner retains chunk storage and three Packed_Byte_Staging owners.
A staging owner is reused when its byte extent matches and replaced when it
changes. These resize allocations and the engine's packed-array copies, mesh
storage and rendering allocations are outside the zero warm Odin producer
allocation claim. There is no per-voxel allocation in generation/meshing and
no per-element foreign call in the chunk upload. The input-event dig samples a
short voxel ray through scalar queries; that is not the streaming bulk path.
The scene creates each mesh RID and transfers ownership immediately through
stage_front. Native commit binds a replacement instance before retiring the
previous visible mesh. Normal retirement, reconfiguration, error rollback and
shutdown free adopted mesh and instance RIDs symmetrically. A material Resource
is retained as long as its RID is referenced by a surface.
LOD
Chebyshev distance from the anchor chunk selects three rings: distance ≤2 uses stride 1, distance ≤4 uses stride 2, and the remaining configured radius uses stride 4. Radius 4 contains the first two rings; radius 6 also exercises the third. LOD 0→1 transitions can be roughly 64 blocks from the camera, depending on its position within the anchor chunk. Fog is not a correctness mechanism.
Each coarse chunk samples its interior at its stride and keeps an exact one-voxel shell on its four horizontal sides. The same unit-cell greedy sweep meshes the resulting occupancy. Neighbors therefore agree on their shared wall at every pair of LODs, and the shell closes transitions to the coarse interior. Coarse interiors still change shape at ring transitions; there is no geomorph. This rule reduces geometry but does not retain the old proportional reduction in mask-sweep CPU cost. The offline eval measures each LOD independently.
A desired-ring change arms a real 0.5-second dwell deadline; movement within the
same desired ring does not restart it. begin_frame performs bounded scheduling
bookkeeping, including periodic hysteresis checks. Meshing itself runs only for
new chunks, matured ring changes or edits, through the same worker and upload
budget (two chunks per frame). “Nothing LOD-related runs per frame” would be
incorrect: scheduling and apron preparation run on the main thread. Missing
neighbor aprons can require a bounded synchronous generation; the measured
driver cost includes that work. Greedy meshing runs on the worker.
Material, sky, shadows and fog
project/chunk_material.gdshader is a lit spatial shader using RGBA8 vertex
palette colors, a muted material tint, roughness 1 and restrained world-space
value noise. The noise
is continuous across chunk borders and sampled per fragment; its cost follows
screen coverage, not the number of LOD quads. Godot supplies sunlight, ambient
sky illumination, shadows and fog. The material uses normal backface culling.
main.tscn authors a procedural sky and one warm DirectionalLight3D at about
32° elevation, with shadows enabled. The 250-block shadow distance is a rendering
budget, not a promise to cover every corner of the maximum stream radius.
A chunk is one server instance and one surface; shadow and other passes can draw
that surface more than once. No extra post-processing stack is required.
Environment fog combines distance density 0.0045 with a height of 10 and height
density 0.04. Positive height density fills valleys exponentially below the
height plane. This uses dev5's real property names, not a fabricated
fog_height_enabled/min/max trio. GODODIN_VOXEL_NOFOG=1 and
GODODIN_VOXEL_NOSHADOW=1 are visual comparison switches. The optional
GODODIN_VOXEL_STD_MATERIAL=1 selects a retained StandardMaterial3D reference.
Culling and scope
Each surface has a world-space AABB inside its chunk footprint; Godot uses it for instance frustum culling. Native verification checks the stored AABB against a fresh mesh and rejects out-of-footprint bounds with error -7. The mesh digest includes its AABB. Representative engine readback also compares the uploaded AABB and all three byte arrays.
There are no baked occluders. A static bake is a poor fit for mutable streamed chunks; a dynamic occlusion solution could be added with its own costs and verification. Empty chunks emit nothing, and the worker skips their meshing.
There is one 32-block vertical layer, no physics bodies, no saved edits, no texture assets or texture streaming, and no GPU mesher. Dev5's opt-in streamed texture import would concern texels if large textures were added; today's vertex palette and procedural detail do not exercise that feature. GPU meshing would be a different Godot rendering workload, not an automatic improvement.
What the eval proves
eval/main.odin retains the original 64-coordinate workload and default seed.
It measures generation and warm meshing at strides 1/2/4, counts actual visible
unit faces for the greedy ratio, repeats every coordinate and LOD, and compares
worker results to the synchronous reference. Submission errors, missing or
duplicate results, timeouts, digest mismatches, warm allocation and nonzero
worker teardown accounting fail the run. Timings are machine-local information;
digest equality and the correctness/allocation assertions are hard gates.
project/stream_eval.gd preserves the original flight: four warm seconds then
eight measured seconds at 10 blocks/s, seed 20260911, radius 4. frame_avg_usec
and frame_p95_usec now measure wall-clock intervals between frames, including
renderer/wait/presentation pacing. driver_avg_usec separately measures the
synchronous upload callback. Phase durations, settlement deadlines and upload
throughput use a monotonic wall clock. Camera motion and the original periodic
carve follow Godot frame delta, which can be capped under heavy load; the named
flight speeds are simulation speeds. None is an isolated GPU timestamp.
Outside that measured interval, the driver stops and requires full coverage, verifies every resident chunk, makes a multi-voxel border carve and verifies it, then retires the edited outer chunk and verifies the surviving neighbor before it reconfigures twice at radius 6 and compares seed replay/storage/RID counts, then flies at 80 blocks/s and again requires 169 settled chunks. Settlement is bounded by a 30-second timeout. Surface readback at all three LODs compares actual engine bytes to the native upload; six real mesher face directions are independently encoded by ArrayMesh and compared to Odin's octahedral normal bytes. These expensive checks do not run in interactive mode or the measured flight window. The final shutdown asserts zero native RID/storage/staging owners, followed by the Host's zero-instance and zero-foreign-value receipt.
The old receipt (repository: examples/voxel_stream/eval/voxel-stream-evidence.json) and
original review remain historical evidence.
The fix batch records the corrections,
actual follow-up commands, commits, receipts and evidence limits. Old timings
are not presented as measurements of the corrected algorithm.
The consume decision
The pinned server exposes RenderingServer.mesh_add_surface(RID, Dictionary).
Its two-argument binding is in servers/rendering/rendering_server.cpp:2371;
_dict_to_surf at 1934 decodes the primitive, format, vertex and attribute bytes,
counts, AABB, indices and material. The example's bound native kits do not expose
the Variant construction needed to assemble that Dictionary. GDScript therefore
makes the single surface-add call per nonempty upload. An empty chunk has no
surface-add. This remains a documented deviation from an entirely native consume.
The mesher writes the engine layout directly: sequential positions (3×f32), then
octahedral normals (2×u16), separate RGBA8 attributes and uint16 indices. Dev5
core/math/vector3.cpp:90 owns the octahedral algorithm;
servers/rendering/rendering_server.cpp:621–627 packs its u16 values; RD mesh
storage at servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp:1507
describes the position/normal sections. These facts were checked against the
exact dev5 archive, not inferred from successful buffer-size validation.
The scene pulls staged byte views through the admitted []u8 return form,
which adds whole-buffer Host copies. The code does not claim zero-copy upload.