From afc063b2abc7f481b5d3e393100a179c12bd8017 Mon Sep 17 00:00:00 2001 From: asrael Date: Mon, 2 Mar 2026 03:17:42 -0600 Subject: [PATCH] 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 --- .../2026-03-02-bsp-chunk-streaming-design.md | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/plans/2026-03-02-bsp-chunk-streaming-design.md diff --git a/docs/plans/2026-03-02-bsp-chunk-streaming-design.md b/docs/plans/2026-03-02-bsp-chunk-streaming-design.md new file mode 100644 index 0000000..eefb10b --- /dev/null +++ b/docs/plans/2026-03-02-bsp-chunk-streaming-design.md @@ -0,0 +1,80 @@ +# 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