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>
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):
- Sample noise at
(cx * scale, cz * scale)with the world seed - Map noise value to biome:
- Dungeon (noise < -0.2): Indoor rooms with corridors, walls, ceiling. Existing
generate_roomslogic. - Courtyard (noise -0.2 to 0.2): Central open area with wing rooms. Existing
generate_courtyardlogic. - Exterior (noise > 0.2): Open terrain with height variation, no ceiling, cliff walls at steep changes.
- Dungeon (noise < -0.2): Indoor rooms with corridors, walls, ceiling. Existing
- Per-chunk seed:
hash(world_seed, cx, cz)for deterministic RNG. - 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_ENTERsent 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 coordinatesCHUNK_ENTER: repacked payload ascx: i32, cz: i32(8 bytes)- Fragment assembly keyed by
(cx, cz)instead ofid
Removals
generate_connected,LayoutMode::Connected— replaced by biome systemremap_bsp_materials— each biome sets its own materialscarve_corridor_h_offset/carve_corridor_v_offset— no more combined gridsmain.rssingle-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_bsppipeline — gainsorigin_x/origin_zparameter for world-space offsetgrid_to_bspheights parameter for exterior terrain elevation- BSP PVS, cell portals — per-chunk visibility still works
- Chunk cache (512 entries), fragment assembly, UDP transport