CDLOD terrain quadtree — guide
How to use the CDLOD selector (facett_map3d_flight::cdlod) to draw continuous-LOD terrain draped on a DEM.
1. Build a selector over your terrain extent
use facett_map3d_flight::{Cdlod, CdlodConfig, GeoRegion, ProceduralTerrain, FloatingOrigin, WorldPos};
let region = GeoRegion::new(18.0, 59.25, 18.1, 59.35); // the terrain extent (lon/lat)
let cdlod = Cdlod::new(region, CdlodConfig::default());
CdlodConfig::default() is a flight-sim ladder: max_depth = 5 (6 LOD levels), leaf_range_m = 1500, range_ratio = 2.0, morph_start_ratio = 0.66, grid = 33 (a 33×33 patch per node). Tune leaf_range_m for how aggressively the near field subdivides, and grid for per-patch resolution.
2. Select the nodes to draw each frame
let camera = WorldPos::from_lonlat(18.05, 59.30, 300.0); // f64 world position
let nodes = cdlod.select(camera);
Each SelectedNode carries its region, depth, LOD level, dist_m, and morph (0 = full detail, 1 = fully morphed onto the parent grid). The regions exactly tile your extent and adjacent nodes never differ by more than one level, so there are no cracks once you apply the morph. Selection is deterministic — the same camera always returns the same Vec (a scripted flythrough is reproducible, FC-7).
3. Drape the nodes on the terrain
The one-call path builds a TerrainMesh per node by sampling your Elevation source (any Dem / ProceduralTerrain / TerrainField) over the node's region, reusing build_terrain_mesh:
let elev = ProceduralTerrain::default(); // or a loaded SRTM .hgt Dem
let origin = FloatingOrigin::snapped(camera, 256.0);
for (node, mesh) in cdlod.build_meshes(camera, &elev, origin, 1.0) {
// `mesh` is a grid×grid TerrainMesh in the renderer's f32 frame;
// pass `node.morph` to the terrain vertex shader for the crack-free edge.
let _ = (node.morph, mesh.triangle_count());
}
A near node is a small patch → finer topography; a far node is a large patch at the same grid → coarser and cheaper. cdlod.triangle_count(nodes.len()) is the total budget; it stays far below a uniform finest-resolution grid.
4. Apply the morph (crack-free edges)
In the terrain vertex shader, morph each vertex's height toward the parent grid by the node's morph factor — the CPU reference is morph_height:
use facett_map3d_flight::morph_height;
let y = morph_height(h /* this grid */, h_parent /* coarser grid */, node.morph);
The GPU path (cdlod_morph.wgsl · cs_morph, behind the wgpu feature) runs the exact same blend; CdlodMorphGpu is proven equal to morph_height texel-for-texel.
5. Testing (works without a GPU)
cargo test -p facett-map3d-flight cdlod runs the full selection-math proof headless. Add --features wgpu,testmatrix to also run the GPU morph-parity proof — it self-skips with a recorded sentinel when no compute adapter is present, so it is green on a GPU-less box. The testmatrix rows emitted: cdlod_subdivision, cdlod_morph_factor, cdlod_drape, and (GPU) cdlod_morph_gpu_parity.