nx_terrain_erode_lib.nx
buildroot/runtime/nx_terrain_erode_lib.nx
about
nx_terrain_erode_lib.nx -- TERRAIN-SCALE EROSION COMPOSITION + ITS REFEREE (PG20, procgen.plan).
WHY THIS LIB EXISTS. nx_water_erosion.nx ships all seven Mei 2007 stages and is gate-proven
(nx_water_erosion_gate, PG18) -- and the ranker measured it LIB-UNIMPORTED: nothing in the estate
composed it. PG20's contract is that composition onto a terrain heightfield, plus the thermal-talus
companion Musgrave 1989 pairs with hydraulic erosion, plus the referee that decides whether the
result reads as terrain rather than as blobs.
THREE THINGS LIVE HERE AND NOTHING ELSE:
1. te_thermal_pass -- THE ONE TALUS PASS IN THE ESTATE.
nx_pets_voxel3d.vp_erode_pass already implemented the voxel form (shed exactly one unit to every
4-neighbour overhung by more than talus, mass-conserving by construction). That is a real
lower-layer kernel and it is NOT deleted: this function GENERALISES it by one parameter and
REPRODUCES it exactly at flux_q14 == 0, which is what makes retiring the private copy onto this
owner provable rather than asserted. flux_q14 > 0 selects the Musgrave proportional law that a
continuous heightfield needs, where a fixed one-unit shed would be a voxel quantum applied to a
quantity that has no voxels.
INDEX ORDER IS NOT A CONVENTION HERE, IT IS A PROVEN PROPERTY. vp_erode_pass indexes x*gn+z and
nx_water_erosion indexes y*w+x. On a square grid the four neighbours of cell k are k-1, k+1,
k-gn, k+gn under BOTH, the boundary guards map onto each other exactly, and the deltas are
accumulated additively from the pre-pass state -- so the two orders produce byte-identical
output. The gate asserts that against a reference written in the other order rather than
leaving it as a comment nobody can check.
2. te_hypsometric_permil -- THE REFEREE. Strahler's hypsometric integral,
(mean - min) / (max - min), in permille. It is the right referee for a procedural world for a
reason that is decidable before any measurement is taken: IT IS DIMENSIONLESS. The plan named
two published bands, hypsometric curve and DRAINAGE DENSITY -- and drainage density is
kilometres of channel per square kilometre, so quoting a published band for it against a world
with no metric scale binding would be a number about a subject we do not have. That half is
declared open by name in the gate, not half-measured.
The integral is invariant under h -> a*h + b for a > 0, which is exactly what makes it
non-gameable: an implementation that merely lowers the terrain, or flattens it, or adds noise,
moves it NOWHERE. Only moving mass from highs into lows moves it, and that is erosion.
A flat field has no hypsometry, so this ABSTAINS with -1 rather than returning a number.
3. te_erode_grid -- the composed loop: seed the Mei state from a caller-owned height grid, tick the
full seven-stage pipeline, interleave the talus pass, read the bed back. Every buffer is
caller-owned and sized by te_state_bytes / te_grid_bytes, so there is no cap to guess and no
dependencies 3 imports · 1 importers
imports: nx_syscalls.nxnx_tier.nxnx_water_erosion.nx
imported by: nx_worldpipe.nx
structs
| none |
consts
| 57 | const TE_PERMIL: i64 = 1000 |
| 59 | const TE_HYPSO_UNOBSERVABLE: i64 = 0 - 1 |
functions
| 62 | func te_state_bytes(gn: i64) -> i64 { return gn * gn * NX_WATER_STRIDE * NX_SIZEOF_NX_INT } |
| 63 | func te_grid_bytes(gn: i64) -> i64 { return gn * gn * NX_SIZEOF_NX_INT } |
| 70 | func te_shed(hg: *i64, delta: *i64, src: i64, dst: i64, h: i64, talus: i64, flux_q14: i64) -> i64 called by 1: te_thermal_pass |
| 85 | func te_thermal_pass(hg: *i64, gn: i64, talus: i64, flux_q14: i64, delta: *i64) -> i64 |
| 113 | func te_hypsometric_permil(hg: *i64, n: i64) -> i64 |
| 132 | func te_seed_bed(state: *i64, hg: *i64, n: i64) -> i64 called by 1: te_erode_grid |
| 137 | func te_read_bed(state: *i64, hg: *i64, n: i64) -> i64 called by 1: te_erode_grid |
| 148 | func te_erode_grid(hg: *i64, gn: i64, ticks: i64, rain_q14: i64, talus: i64, flux_q14: i64, |