Skip to content

World System

MichaelFisher1997 edited this page Jan 5, 2026 · 1 revision

World System

The world system implements Minecraft-style voxel terrain with streaming chunk loading, greedy mesh optimization, and multi-level LOD for extreme render distances.


Overview

Component Responsibility
World Chunk orchestration, loading/unloading, rendering
Chunk 16x256x16 block storage with lighting
ChunkMesh GPU mesh from block data
GlobalVertexAllocator 4GB GPU megabuffer
LODManager Distant Horizons-style LOD system

Chunk Data Structure

File: src/world/chunk.zig

Constants

Constant Value Description
CHUNK_SIZE_X 16 Width in blocks
CHUNK_SIZE_Y 256 Height in blocks
CHUNK_SIZE_Z 16 Depth in blocks
CHUNK_VOLUME 65,536 Total blocks per chunk
MAX_LIGHT 15 Maximum light level

Chunk Fields

Field Type Description
chunk_x, chunk_z i32 Chunk coordinates
blocks [65536]BlockType Block data
light [65536]PackedLight Per-block lighting
biomes [256]BiomeId Biome per column
heightmap [256]i16 Surface Y per column
state State Pipeline state
pin_count atomic.Value(u32) Async protection

PackedLight

packed struct {
    block_light: u4 = 0,  // Lower 4 bits
    sky_light: u4 = 0,    // Upper 4 bits
}

Data Layout

Index formula: x + z * 16 + y * 256 (Y-major for cache-efficient layer iteration)

Chunk State Machine

missing → generating → generated → meshing → mesh_ready → uploading → renderable

Coordinate Conversion

Function Description
worldToChunk(world_x, world_z) Convert world to chunk coords
worldToLocal(world_x, world_z) Convert world to local (0-15)
getIndex(x, y, z) Local coords to flat index

World Manager

File: src/world/world.zig

World Fields

Field Description
chunks HashMap of loaded chunks
chunks_mutex Thread-safe access
generator Terrain generator
render_distance Visible chunk radius
gen_queue / mesh_queue Priority job queues
gen_pool (4) / mesh_pool (3) Worker threads
upload_queue Pending GPU uploads
vertex_allocator 4GB megabuffer
lod_manager Optional LOD system

Key Methods

Method Description
init(allocator, distance, seed, rhi) Standard init
initWithLOD(allocator, distance, seed, rhi, lod_config) Init with LOD
update(player_pos, dt) Queue chunks, process uploads
render(view_proj, camera_pos) Render with frustum culling
renderShadowPass(light_matrix, camera_pos) Shadow map rendering
getBlock(x, y, z) Read block at world coords
setBlock(x, y, z, block) Set block, update neighbors

Chunk Loading

fn update(self, player_pos, dt) {
    const player_cx = worldToChunk(player_pos.x);
    const player_cz = worldToChunk(player_pos.z);
    
    // Queue chunks within render distance
    for (-render_distance..render_distance) |dx| {
        for (-render_distance..render_distance) |dz| {
            const cx = player_cx + dx;
            const cz = player_cz + dz;
            if (!self.hasChunk(cx, cz)) {
                self.queueGeneration(cx, cz);
            }
        }
    }
    
    // Process uploads (max 32/frame)
    while (upload_queue.pop()) |chunk| {
        chunk.mesh.upload(vertex_allocator);
    }
    
    // Unload distant chunks
    self.unloadDistant(player_cx, player_cz);
}

Render Stats

pub const RenderStats = struct {
    chunks_rendered: u32,
    chunks_culled: u32,
    vertices_rendered: u64,
};

Mesh Generation

File: src/world/chunk_mesh.zig

Greedy Meshing Algorithm

  1. For each axis (Y, X, Z), iterate through boundary planes
  2. Compare adjacent blocks across the boundary
  3. Create mask of faces needing rendering
  4. Merge adjacent faces with matching properties:
    • Same block type
    • Same light level
    • Same biome color
  5. Generate quads with UVs, normals, and lighting

ChunkMesh Fields

Field Description
solid_allocation GPU buffer for opaque geometry
fluid_allocation GPU buffer for transparent geometry
pending_solid/fluid CPU data awaiting upload
mutex Thread safety

Neighbor Chunks

Cross-chunk face culling requires neighbor access:

pub const NeighborChunks = struct {
    north: ?*const Chunk = null,  // -Z
    south: ?*const Chunk = null,  // +Z
    east: ?*const Chunk = null,   // +X
    west: ?*const Chunk = null,   // -X
};

Render Passes

Pass Blocks
Solid Opaque blocks (stone, dirt, etc.) and leaves
Fluid Transparent blocks (water)

Fluid pass renders after solid for correct blending.


GPU Memory Management

File: src/world/chunk_allocator.zig

GlobalVertexAllocator

Single large GPU buffer (default 4GB) for all chunk vertices.

Field Description
buffer Single GPU buffer handle
capacity Total buffer size in bytes
free_blocks Sorted free-list
mutex Thread-safe allocation

Allocation Strategy

  • Best-fit: Smallest block that fits request
  • Coalescing: Merge adjacent free blocks on free
  • No buffer switching: Same buffer for all chunks

VertexAllocation

pub const VertexAllocation = struct {
    offset: usize,         // Byte offset in megabuffer
    count: u32,            // Vertex count
    handle: BufferHandle,  // Same for all allocations
};

LOD System

File: src/world/lod_manager.zig

Distant Horizons-style multi-level LOD for extreme render distances (100+ chunks).

LOD Levels

Level Scale Grid Size Description
LOD0 1 (2x2) Full Standard voxel chunks
LOD1 2 (4x4) 32x32 2-block resolution
LOD2 4 (8x8) 32x32 4-block resolution
LOD3 8 (16x16) 32x32 Heightmap only

LOD Config

Parameter Default Description
lod0_radius 16 Matches render_distance
lod1_radius 40 Extended distance
lod2_radius 80 Extended distance
lod3_radius 160 Horizon distance

LOD Visibility

  • LOD regions hide when all underlying chunks are renderable
  • Rendered with -3.0 Y offset to prevent terrain poking
  • Frustum culled per region

LODSimplifiedData

struct {
    width: u32,           // Grid size (32)
    heightmap: []i16,     // Height per cell
    biomes: []BiomeId,    // Biome per cell
    top_blocks: []BlockType, // Surface block
    colors: []u32,        // Packed RGB
}

Threading Model

Worker Pools

Pool Threads Purpose
gen_pool 4 Terrain generation
mesh_pool 3 Greedy meshing
lod_gen_pool 3 LOD generation/meshing

Synchronization

  • RwLock on chunk map (shared reads, exclusive writes)
  • Mutex on job queues and mesh data
  • Atomic pin_count prevents unload during async work

Job Priority

Jobs sorted by squared distance to player. Closer chunks processed first.

Upload Throttling

Maximum 32 GPU uploads per frame to prevent stalls.


Data Flow

Player Position
      ↓
World.update() → Queue chunks needing generation
      ↓
gen_pool (4 workers) → TerrainGenerator.generate()
      ↓
mesh_pool (3 workers) → ChunkMesh.buildWithNeighbors()
      ↓
upload_queue → GlobalVertexAllocator.allocate() [main thread]
      ↓
World.render() → Frustum cull, draw solid, draw fluid

LODManager (parallel):
      ↓
lod_gen_pool (3 workers) → Generate heightmap, build LODMesh
      ↓
LODManager.render() → Draw LOD3, LOD2, LOD1 (far to near)

Coordinate Systems

Coordinate Type Range Conversion
World i32 Infinite Base unit
Chunk i32 Infinite @divFloor(world, 16)
Local u32 0-15 (X/Z), 0-255 (Y) @mod(world, 16)
Region (LODn) i32 Infinite @divFloor(chunk, chunksPerSide)

See Also


Source: src/world/ | Last updated: January 2026

Clone this wiki locally