Expand description
Per-pixel 3D-DDA + brickmap CPU renderer (Substage DDA).
This is the clean-room replacement for the voxlap-derived
column-coherent opticast pipeline (opticast + grouscan +
scan_loops). Every pixel casts one independent ray, so none of
the column/row-coherence stitching artifacts of the 2.5D voxlap
renderer can occur (silhouette notch, floor hairlines, axis-aligned
mip beams, cross-chunk virtual-column complexity). See
PORTING-DDA.md for the full stage plan.
Stage status — DDA.6 (per-grid distance mip) + DDA.7 (tile
parallelism). Each pixel casts one ray over the grid’s full voxel
box (GridView::voxel_bounds, spanning every chunk in XY and
Z) via a 3D-DDA (Amanatides–Woo). A uniform render mip (chosen per
grid by LOD distance, clamped by effective_mip to a level every
chunk has built) coarsens the cell size to 2^mip mip-0 voxels and
samples mip-mip data — the ray stays in mip-0 units so depth and
fog are exact. [BrickMaps] (one occupancy map per populated chunk,
at the render mip) are built once per frame and shared immutably; a
[Sampler] resolves each cell to its chunk
(GridView::chunk_at_xyz) and brick-gates the
GridView::surface_color_mip slab walk, caching the current chunk
so air costs an O(1) bit test. render_dda_parallel splits the
frame into disjoint rayon bands — bit-identical to sequential since
pixels are independent. Hits are shaded by baked brightness
([shade]) + DdaEnv::side_shades face tint, fogged toward
DdaEnv::fog_color ([apply_fog]); misses sample the
DdaEnv::sky panorama ([sample_sky]) or keep the solid pre-fill.
Buffer conventions match the rest of the engine so this backend is
colour is packed 0x80RRGGBB; depth is perpendicular distance from
the camera with smaller = closer (so the scene compositor’s
min-z merge works directly on the z-buffer this writes).
Structs§
- Brick
Cache - Persistent, cross-frame brick occupancy cache (Substage DDA.7
perf). Keyed by
(chunk x, y, z, mip)with the chunk’s editversion; an entry is reused until its chunk’s version changes, so a static / streamed-once world pays zero brick-build cost after the first frame (the per-frame rebuild was the dominant DDA cost). - Composite
Occluder - XS.2 — a
WorldOccluderthat ORs two others (e.g. the grid occluder + the sprite occluder), so a single shadow query covers both.trueif either blocks the ray. - CpuLights
- CPU.1 — the per-frame dynamic-light environment for one grid (grid-local).
Mirror of the GPU
shade_litinputs.enabled == false(the default) keeps the baked-byte path. CPU.2 adds hard voxel shadows (sun + flagged point lights) via a per-(voxel,face) shadow march;shadow_strength == 0(theDefault) leaves the lighting diffuse-only. - CpuPoint
Light - CPU.1 — one point light in a grid’s local frame for the CPU renderer.
- DdaEnv
- Per-frame environment for DDA shading (Substage DDA.5): a textured sky panorama, distance fog, and per-face side shading.
- Raster
Sink PixelSinkover a borrowed(framebuffer, zbuffer)pair.- World
Shadow Ctx - XS.1 — per-grid context for a cross-scene shadow query: the scene-wide
WorldOccluderplus the current grid’s local→world transform, so a grid-local shadow ray (the frameshade_dynamicworks in) can be lifted to world space before the scene-wide test.cols[i]is the world-space image of grid-local axisi(the grid rotation’s columns);originis the grid’s world origin.
Traits§
- Pixel
Sink - Per-pixel output target for the DDA renderer.
- World
Occluder - XS.1 — a world-space occlusion oracle over the whole scene (all grids,
and sprites in XS.2). Implemented in
roxlap-scene(it needs the grid / sprite stores); the CPU DDA reaches it throughDdaEnv::world_shadowso shadow rays cross grid + object boundaries instead of stopping at the current grid.occluded_world(origin, dir, max_t)is in world voxel units:trueiff any solid voxel anywhere blocks the segment.
Functions§
- effective_
mip - Clamp a requested render mip to one every populated chunk actually
has built — so the uniform-mip traversal never under-samples a chunk
that lacks the requested level (which would punch holes).
0short- circuits (always available). - pixel_
ray - World-space ray for screen pixel
(px, py)under opticast’s pinhole: origin is the camera position, direction is(px - hx)·right + (py - hy)·down + hz·forward. - render_
dda - Render one grid into
sinkwith per-pixel 3D-DDA. - render_
dda_ parallel - Tile-parallel
render_ddawriting straight into(fb, zb). - render_
sky_ fill - Fill the panorama
Skyinto every background pixel — one whose z-buffer entry is still+∞(no grid/terrain hit). The per-grid DDA only samples the sky inside each grid’s screen rect (and only its sky-owning grid); pixels outside any grid — most of a sprite/effect-only view, or the margins around a small world grid — would otherwise keep the caller’s flat clear colour. This paints the real panorama there while leaving terrain (finite z) and composited translucent pixels untouched. The z-buffer is not modified.cam/settingsare the same per-frame projection the renderer used.