Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-map3d-flight/.nornir/cdlod-terrain-design.md

CDLOD terrain quadtree — design

The chosen design for FLIGHT-SIM roadmap §6 (continuous-distance terrain LOD): a pure-CPU CDLOD (Filip Strugar's Continuous Distance-dependent Level Of Detail) quadtree that selects which terrain patches to draw at which resolution given the camera, draped on the already-landed real terrain (terrain.rs) by reusing build_terrain_mesh — never forking it. The GPU vertex height-morph is proven equal to the CPU oracle behind the wgpu feature; the landed 21-pass facett-map3d pipeline is untouched (L2 additive, no regression).

Why CDLOD (and not a fixed grid / geomipmapping)

A single flat grid over a DEM tile is wrong at both ends: too coarse near the camera (blocky ridgelines) and ruinously heavy far away (millions of sub-pixel triangles). CDLOD keeps the drawn triangle count roughly constant by subdividing a quadtree only where the camera is close, and it fixes the two failure modes a naive LOD hits:

(The rejected alternatives — uniform grid, geomipmapping with skirts, ROAM — and why they lose to CDLOD live in cdlod-terrain-reasoning.md, per the docs doctrine: this design doc names only the chosen path.)

The pieces (src/cdlod.rs)

Distance metric (the range seam)

Ranges are ground distances in the f64 Web-Mercator WorldPos frame the floating origin + streamer already key on: ground_dist_to_region projects a node's lon/lat corners (Mercator is monotone, so they map to the node's world AABB) and returns the nearest-point distance (0 when the camera is over the node). One metric frame across floating origin (§1), tile streaming (§5), and CDLOD (§6) — L5 reuse, no twin.

The GPU seam (src/cdlod_morph.wgsl, behind wgpu)

cs_morph is a @workgroup_size(64) compute twin of morph_height: it blends an array of (h, h_parent) pairs under one morph factor, and CdlodMorphGpu::morph reads the result back so the vertex shader's crack-closing blend is proven texel-for-texel equal to the CPU oracle (max_abs_error_vs_oracle < 1e-3). This mirrors the scattering-LUT pass exactly. The full render-pass wiring into facett-map3d's pipeline (uploading the node instances + a terrain vertex shader that morphs in-shader) is the next roadmap step, just as the scattering LUT deferred its sky.wgsl sampling — so nothing in the landed pipeline changes here.

Testability without a GPU (session constraint)

Every correctness property is a RED-when-broken, GPU-less assertion in src/cdlod.rs: subdivision-follows-distance, watertight tiling, the ≤ 1-level neighbour invariant, the morph-factor ramp endpoints + monotonicity, the morph-height blend, the bounded triangle budget, deterministic selection, and the drape. The GPU parity test self-skips with a recorded sentinel when no adapter is present, so a laptop tester with no GPU still runs the whole selection-math proof green.