Expand description
One-shot heightmap → quantized-mesh (.terrain) encoding.
This module ties together the three crates that otherwise have to be wired up by hand:
martinigenerates an adaptive RTIN mesh from the elevation grid.- The mesh’s
(u, v, height)are quantised to the 0..=32767 range. quantized_meshencodes the header, vertices, edge indices and optional extensions into the quantized-mesh-1.0 byte stream.
The fiddly bits it handles for you:
- Re-sampling each mesh vertex’s height (martini discards heights once
the error pyramid is built, so the transform has to recover the grid
coordinate from
(u, v)and read the DEM again). - Computing the encoded height range from the mesh vertices (what is actually stored), not the full grid.
- Streaming the mesh vertices through
QuantizedMeshHeader::from_bounds_with_vertices_iterfor a tight horizon-occlusion point. - Vertex normals via the
NormalModeof your choice. - Folding the ellipsoid
curvature_bulgeinto the error pyramid so nearly-flat tiles keep enough triangles to track the globe at low zoom (the bulge feeds error estimation only, never the emitted heights).
§Grid orientation
elevations (and the get_height closure’s y) are row-major,
north → south: row 0 is the northern edge, row grid_size - 1 the
southern edge. This matches crate::normals::BufferedElevations, so a
buffered grid can be reused directly for NormalMode::BufferedGradient.
§Seamless tiling — the caller supplies the halo
These functions encode one tile in isolation; they never fetch neighbouring tiles. For gap-free, seam-free output the caller must widen the input to overlap the neighbours — fetch the halo cells along with the tile and stitch them in before calling:
- Geometry seam. martini needs a
2^n + 1grid, so anN-post DEM tile needs one extra post on its east and south edges. That+1post is the neighbour tile’s first post for the shared edge — read it from the neighbour, don’t edge-replicate, or adjacent tiles won’t agree on the boundary and the globe cracks along tile seams. - Normal seam.
NormalMode::BufferedGradientneeds abuffer-cell halo of neighbour samples on every side (seeBufferedElevations). Edge vertices read their±1neighbours out of that halo, so the same physical edge gets identical normals from either tile and lighting stays continuous.
Gathering that neighbour data (over HTTP, from disk, from a cache, …) is deliberately left to the caller — hence this module takes an already-assembled grid rather than fetching tiles itself, which also keeps it free of any async/runtime assumptions.
§Example
use terrain_codec::quantized_mesh::TileBounds;
use terrain_codec::terrain::{encode_terrain, TerrainOptions};
let grid_size = 65; // 2^6 + 1
let elevations = vec![0.0f32; (grid_size * grid_size) as usize];
let bounds = TileBounds::new(139.0, 35.0, 139.01, 35.01);
let terrain: Vec<u8> = encode_terrain(
&elevations,
grid_size,
&bounds,
&TerrainOptions {
max_error: 1.0,
..Default::default()
},
);
assert!(terrain.starts_with(&[0x1f, 0x8b])); // gzip magic (default level 6)Structs§
- Terrain
Options - Options controlling
encode_terrainand the other encode functions in this module.
Enums§
- Normal
Mode - How (and whether) per-vertex normals are computed for the oct-encoded vertex-normals extension.
Functions§
- curvature_
bulge - Radial deviation (metres) of the WGS84 ellipsoid surface above the flat
bilinear interpolation of the tile’s four corners, evaluated at grid cell
(x, y). The encode functions in this module always add this to the height field that drives martini’s error pyramid (never to the stored heights), so nearly-flat tiles still tessellate enough to track the globe’s curvature instead of collapsing to a flat quad that cuts under the ellipsoid at low zoom. Exposed so callers can reason about / reproduce the subdivision. - encode_
terrain - Encode a flat row-major (north → south)
f32elevation grid to a quantized-mesh.terrainbyte vector. - encode_
terrain_ from_ fn - Encode a heightmap to a quantized-mesh
.terrainbyte vector, sampling elevations through a closure. - encode_
terrain_ from_ fn_ to - Like
encode_terrain_from_fn, but streams the encoded bytes to a writer instead of allocating aVec. - encode_
terrain_ to - Like
encode_terrain, but streams the encoded bytes to a writer.