pxl8/docs/plans/2026-03-02-bsp-chunk-streaming-design.md
asrael afc063b2ab docs: BSP chunk streaming design
Grid of 16x16-cell BSP chunks with noise-based biomes, 3x3 streaming
radius, world-space coordinates, and exterior terrain support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 03:17:42 -06:00

4.1 KiB

BSP Chunk Streaming Design

Minecraft-style infinite procedural world using BSP geometry. The world is a grid of BSP chunks that generate and stream as the player explores. Both interior (dungeons, courtyards) and exterior (hills, cliffs) terrain use BSP representation.

Chunk Addressing

ChunkId::Bsp(u32) becomes ChunkId::Bsp { cx: i32, cz: i32 }. Each chunk covers a 16x16 cell region (1024x1024 world units). World-space origin of chunk (cx, cz) is (cx * 1024.0, cz * 1024.0).

The ChunkMessage transport header already has cx/cy/cz fields (currently unused) — we start populating them. The id field becomes hash(cx, cz) for fragment assembly deduplication.

Procgen — Biome Selection

The server holds a world_seed: u64. For each chunk at (cx, cz):

  1. Sample noise at (cx * scale, cz * scale) with the world seed
  2. Map noise value to biome:
    • Dungeon (noise < -0.2): Indoor rooms with corridors, walls, ceiling. Existing generate_rooms logic.
    • Courtyard (noise -0.2 to 0.2): Central open area with wing rooms. Existing generate_courtyard logic.
    • Exterior (noise > 0.2): Open terrain with height variation, no ceiling, cliff walls at steep changes.
  3. Per-chunk seed: hash(world_seed, cx, cz) for deterministic RNG.
  4. All vertex positions emitted in world space: offset by (cx * 1024.0, cz * 1024.0).

Procgen — Exterior Terrain

New generate_exterior function:

  • Each cell gets a floor height from fine-grained noise at world-space position
  • All cells open (no ceiling)
  • Walls emitted between adjacent cells with height difference > 32 units (cliff faces)
  • No walls at chunk edges — floor extends to boundary, noise is continuous across chunks
  • Single directional light (sunlight) instead of per-room point lights
  • Materials 7/8/9 (ground/cliff/grass)

Server Streaming

Track player's chunk coordinate: chunk_cx = floor(player.x / 1024), chunk_cz = floor(player.z / 1024).

Each tick, compute 3x3 ring around player vs ClientChunkState.known:

  • New chunks in range: Generate lazily, queue for streaming
  • Chunks leaving range: No action — client LRU handles eviction
  • Active chunk: The chunk the player stands in. CHUNK_ENTER sent when it changes.

Streaming priority: player's chunk bursts at 64 fragments/tick, neighbors at 8 fragments/tick.

Client — Multi-Chunk Rendering & Collision

World struct: Add loaded_chunks[9] array for the 3x3 ring. active_chunk remains the player's chunk (collision).

Sync: Iterate chunk cache for the 3x3 ring around player's chunk coordinate. Populate loaded_chunks.

Rendering: Iterate loaded_chunks, call pxl8_bsp_render for each. Vertices are in world space — no transform needed. Frustum-cull entire chunks before BSP rendering (chunk AABB: 1024x128x1024).

Collision: pxl8_sim_trace checks active_chunk BSP. Swap active chunk when player crosses boundary. Cross-chunk collision is a future refinement.

BSP render state: Store pxl8_bsp_render_state* inside each pxl8_world_chunk, created on arrival, destroyed on eviction.

Wire Protocol

No new message types. Existing messages gain spatial meaning:

  • ChunkMessage.cx/cz: populated with grid coordinates
  • CHUNK_ENTER: repacked payload as cx: i32, cz: i32 (8 bytes)
  • Fragment assembly keyed by (cx, cz) instead of id

Removals

  • generate_connected, LayoutMode::Connected — replaced by biome system
  • remap_bsp_materials — each biome sets its own materials
  • carve_corridor_h_offset / carve_corridor_v_offset — no more combined grids
  • main.rs single-BSP generation on spawn — replaced by streaming around player position

What Stays

  • generate_rooms / generate_courtyard — used as chunk-level biome generators
  • Door system (pxl8_sim_door, door_angle, entities.fnl) — chunk-local feature
  • grid_to_bsp pipeline — gains origin_x/origin_z parameter for world-space offset
  • grid_to_bsp heights parameter for exterior terrain elevation
  • BSP PVS, cell portals — per-chunk visibility still works
  • Chunk cache (512 entries), fragment assembly, UDP transport