code wiki / (root) / nx_worldpipe.nx

nx_worldpipe.nx source

↩ module page · 379 lines · 20700 B

1// nx_worldpipe.nx -- ★R5 STAGED PROCGEN (the banked Minecraft-pipeline curriculum, engine-convergence rung 5): 2// worldgen as an ORDERED STAGED PIPELINE over a MULTI-PARAMETER space -- not one noise call. 3// STAGE 1 PARAMS six independent noise fields at (x,z): temperature / humidity / CONTINENTALNESS / erosion / 4// weirdness / river. (Their 6th, depth, is the 3D-density param -- ours is a heightfield: named residual.) 5// STAGE 2 SPLINE height BASE = piecewise-linear SPLINE over continentalness (deep ocean -> coast -> inland -> highland), 6// the curriculum's core lesson: params map to height THROUGH SPLINES. 7// STAGE 3 RELIEF ridged mountains scaled by WEIRDNESS and flattened by EROSION (high erosion = plains). 8// STAGE 4 CARVER rivers CUT the terrain where the river field crosses its band (geometry change, toggleable). 9// STAGE 5 BIOME classified from the parameter VECTOR (temp x humidity x cont x weirdness) -- NOT from height 10// (the anti-"height-only" lesson: two same-height points can be desert vs snowfield). 11// STAGE 6 SURFACE per-biome + slope + altitude surface colours (sand/grass/snow/rock/water). 12// STAGE 7 FEATURES per-biome placements by seeded hash (trees / cacti / spruce / boulders). 13// STAGE 8 SPAWN a valid spawn point (land, gentle slope) found deterministically. 14// Pure integer, deterministic per seed. Stages 4/7 have toggles so gates can prove CAUSALITY per stage. 15// license_tier: ORIGINAL 16import "nx_syscalls.nx" 17import "nx_trimesh.nx" 18const WP_MAGIC_1400: i64 = 1400 19const WP_MAGIC_3400: i64 = 3400 20const WP_MAGIC_2600: i64 = 2600 21const WP_MAGIC_2100: i64 = 2100 22const WP_MAGIC_1700: i64 = 1700 23const WP_MAGIC_1300: i64 = 1300 24const WP_MAGIC_2400: i64 = 2400 25const WP_MAGIC_9973: i64 = 9973 26const WP_MAGIC_524287: i64 = 524287 27const WP_MAGIC_1024: i64 = 1024 28const WP_MAGIC_9600: i64 = 9600 29const WP_MAGIC_1000000000: i64 = 1000000000 30const WP_MAGIC_10100: i64 = 10100 31const WP_MAGIC_1000000: i64 = 1000000 32const WP_MAGIC_999999: i64 = 999999 33const WP_MAGIC_65536: i64 = 65536 34 35static WP_SEED: i64 36static WP_CARVE: i64 // stage-4 toggle (1 = carve rivers). set via wp_set_carve 37static WP_FEAT: i64 // stage-7 toggle (1 = place features). set via wp_set_feat 38// ★AUTHORING KNOBS (the "describe a world" layer, 2026-07-13): global biases the NL front-end sets. Defaulted in 39// wp_init so every existing gate is byte-unchanged; the authoring path sets them AFTER wp_init. 40static WP_TEMP_BIAS: i64 // added to temperature field (colder = snowier) 41static WP_CONT_BIAS: i64 // added to continentalness (lower = more ocean/islands) 42static WP_MOUNT_SCALE: i64 // relief multiplier (default 1400 = current alpine strength) 43static WP_LIGHT: i64 // stage-6.5 lighting toggle (declared ABOVE all readers -- the AT_FDCWD law) 44func wp_init(seed: i64) -> i64 { WP_SEED = seed; WP_CARVE = 1; WP_FEAT = 1; WP_TEMP_BIAS = 0; WP_CONT_BIAS = 0; WP_MOUNT_SCALE = WP_MAGIC_1400; WP_LIGHT = 1; return 0 } 45func wp_set_carve(v: i64) -> i64 { WP_CARVE = v; return 0 } 46func wp_set_feat(v: i64) -> i64 { WP_FEAT = v; return 0 } 47func wp_set_temp_bias(v: i64) -> i64 { WP_TEMP_BIAS = v; return 0 } 48func wp_set_cont_bias(v: i64) -> i64 { WP_CONT_BIAS = v; return 0 } 49func wp_set_mount_scale(v: i64) -> i64 { WP_MOUNT_SCALE = v; return 0 } 50func wp_iabs(v: i64) -> i64 { if v < 0 { return 0-v } return v } 51func wp_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v } 52 53// ---- STAGE 1: the parameter fields. k: 0 continentalness, 1 temperature, 2 humidity, 3 erosion, 4 weirdness, 5 river. 54// each = value noise on its own (seed,param) y-plane at its own frequency, centered to [-512, 511]. 55func wp_param(x: i64, z: i64, k: i64) -> i64 { 56 var f: i64 = WP_MAGIC_3400 // continentalness: the longest wavelength (continents) 57 if k == 1 { f = WP_MAGIC_2600 } // temperature 58 if k == 2 { f = WP_MAGIC_2100 } // humidity 59 if k == 3 { f = WP_MAGIC_1700 } // erosion 60 if k == 4 { f = WP_MAGIC_1300 } // weirdness 61 if k == 5 { f = WP_MAGIC_2400 } // river field 62 let yk: i64 = WP_SEED*WP_MAGIC_9973 + k*WP_MAGIC_524287 63 var bias: i64 = 0 64 if k == 0 { bias = WP_CONT_BIAS } // continentalness bias (islands/continents) 65 if k == 1 { bias = WP_TEMP_BIAS } // temperature bias (snowy/hot) 66 return tm_noise3(x, yk, z, f) - 512 + bias 67} 68 69// ---- STAGE 2: the continentalness->height SPLINE (piecewise linear over control points) ---- 70func wp_spline(c: i64) -> i64 { 71 // knots: (-512,-430) (-220,-160) (-120,-40) (-40,15) (120,90) (300,190) (512,420) 72 if c <= 0-220 { return 0-430 + (c - (0-512))*((0-160) - (0-430))/((0-220) - (0-512)) } 73 if c <= 0-120 { return 0-160 + (c - (0-220))*((0-40) - (0-160))/((0-120) - (0-220)) } 74 if c <= 0-40 { return 0-40 + (c - (0-120))*(15 - (0-40))/((0-40) - (0-120)) } 75 if c <= 120 { return 15 + (c - (0-40))*(90 - 15)/(120 - (0-40)) } 76 if c <= 300 { return 90 + (c - 120)*(190 - 90)/(300 - 120) } 77 return 190 + (c - 300)*(420 - 190)/(512 - 300) 78} 79 80// ---- STAGES 2+3+4: terrain height from the params (relief = ridged * weirdness * (1-erosion), rivers carve) ---- 81// exposed with explicit params so gates can prove each factor's effect in isolation. 82func wp_height_core(cont: i64, ero: i64, weird: i64, x: i64, z: i64) -> i64 { 83 let base: i64 = wp_spline(cont) 84 var ridge: i64 = 512 - wp_iabs(tm_noise3(x, WP_SEED*WP_MAGIC_9973 + 6*WP_MAGIC_524287, z, 900) - 512)*2 85 if ridge < 0 { ridge = 0 } 86 let wamp: i64 = weird + 512 // 0..1023: weirdness scales the mountains 87 let eroF: i64 = 512 - ero // 1..WP_MAGIC_1024: erosion FLATTENS (high ero -> small relief) 88 let mfac: i64 = wp_clamp((cont + 40)*4, 0, WP_MAGIC_1024) // mountains only inland (not in the sea) 89 let mount: i64 = ((ridge*wamp/WP_MAGIC_1024)*eroF/WP_MAGIC_1024)*mfac/WP_MAGIC_1024*WP_MOUNT_SCALE/WP_MAGIC_1024 // relief (authoring-scalable; default WP_MAGIC_1400) 90 return base + mount 91} 92func wp_height_params(cont: i64, ero: i64, weird: i64, x: i64, z: i64) -> i64 { 93 let detail: i64 = (tm_noise3(x, WP_SEED*WP_MAGIC_9973 + 7*WP_MAGIC_524287, z, 300) - 512)/8 94 return wp_height_core(cont, ero, weird, x, z) + detail 95} 96// the FLOW surface: core only -- water descends the VALLEY-scale terrain, not micro-bumps 97func wp_flow_h(x: i64, z: i64) -> i64 { 98 let cont: i64 = wp_param(x, z, 0) 99 let ero: i64 = wp_param(x, z, 3) 100 let weird: i64 = wp_param(x, z, 4) 101 return wp_height_core(cont, ero, weird, x, z) 102} 103// raw terrain (stages 2+3 only -- what the water flows OVER) 104func wp_height_raw(x: i64, z: i64) -> i64 { 105 let cont: i64 = wp_param(x, z, 0) 106 let ero: i64 = wp_param(x, z, 3) 107 let weird: i64 = wp_param(x, z, 4) 108 return wp_height_params(cont, ero, weird, x, z) 109} 110 111// ---- ★Q3 HYDROLOGY (2026-07-12, replaces the noise-band carver -- "the content was random"): rivers are 112// STEEPEST-DESCENT TRACES from high springs down to the sea. Baked once per seed into a coarse bed grid 113// (cell HYCELL over +-HYSPAN); wp_height carves terrain to the traced bed. By construction a river bed is 114// MONOTONE NON-INCREASING along its path (water cannot flow uphill) and terminates at the sea or a lake. 115const HYW: i64 = 320 116const HYCELL: i64 = 64 117const HYSPAN: i64 = 10240 118static WP_HYD: i64 // bed grid: HYW*HYW i64; 0 = no river; else bed height + HYOFF 119static WP_HYSTATS: i64 // per-spring stats: [startBed, endBed, steps, reachedSea] x 8 120static WP_HYN: i64 // springs traced 121const HYOFF: i64 = 5000000 122func wp_hyd_stamp(g: *i64, x: i64, z: i64, bed: i64, rad: i64) -> i64 { 123 var dz: i64 = 0 - rad 124 while dz <= rad { 125 var dx: i64 = 0 - rad 126 while dx <= rad { 127 let cx: i64 = (x + dx*HYCELL + HYSPAN)/HYCELL 128 let cz: i64 = (z + dz*HYCELL + HYSPAN)/HYCELL 129 if cx >= 0 { if cx < HYW { if cz >= 0 { if cz < HYW { 130 let gi: i64 = cz*HYW + cx 131 let v: i64 = bed + HYOFF 132 if g[gi] == 0 { g[gi] = v } else { if v < g[gi] { g[gi] = v } } 133 } } } } 134 dx = dx + 1 135 } 136 dz = dz + 1 137 } 138 return 0 139} 140func wp_hydro_bake() -> i64 { 141 if WP_HYD == 0 { WP_HYD = sys_mmap(HYW*HYW*8) as i64 } 142 if WP_HYSTATS == 0 { WP_HYSTATS = sys_mmap(8*4*8) as i64 } 143 let g: *i64 = WP_HYD as *i64 144 var i: i64 = 0 145 while i < HYW*HYW { g[i] = 0; i = i + 1 } 146 let st: *i64 = WP_HYSTATS as *i64 147 WP_HYN = 0 148 // springs: seeded picks on HIGH ground 149 var sz: i64 = 0-WP_MAGIC_9600 150 while sz <= WP_MAGIC_9600 { 151 var sx: i64 = 0-WP_MAGIC_9600 152 while sx <= WP_MAGIC_9600 { 153 if WP_HYN < 8 { 154 if wp_flow_h(sx, sz) > 140 { 155 if tm_hash3(sx/640, WP_SEED*17 + 3, sz/640) < 70 { 156 // trace steepest descent 157 var px: i64 = sx 158 var pz: i64 = sz 159 var bed: i64 = wp_flow_h(px, pz) - 14 160 let sb: i64 = bed 161 var steps: i64 = 0 162 var alive: i64 = 1 163 var reached: i64 = 0 164 while alive == 1 { 165 wp_hyd_stamp(g, px, pz, bed, 1) 166 var bestH: i64 = WP_MAGIC_1000000000 167 var bx: i64 = px 168 var bz: i64 = pz 169 var d: i64 = 0 170 while d < 16 { 171 var stp: i64 = 110 172 if d >= 8 { stp = 300 } // basin-escape ring 173 var nx: i64 = px 174 var nz: i64 = pz 175 let dd8: i64 = d % 8 176 if dd8 == 0 { nx = px + stp } 177 if dd8 == 1 { nx = px - stp } 178 if dd8 == 2 { nz = pz + stp } 179 if dd8 == 3 { nz = pz - stp } 180 if dd8 == 4 { nx = px + stp*7/10; nz = pz + stp*7/10 } 181 if dd8 == 5 { nx = px - stp*7/10; nz = pz + stp*7/10 } 182 if dd8 == 6 { nx = px + stp*7/10; nz = pz - stp*7/10 } 183 if dd8 == 7 { nx = px - stp*7/10; nz = pz - stp*7/10 } 184 let nh: i64 = wp_flow_h(nx, nz) 185 if nh < bestH { bestH = nh; bx = nx; bz = nz } 186 d = d + 1 187 } 188 let here: i64 = wp_flow_h(px, pz) 189 if bestH >= here { 190 // true basin even at the escape ring -> a LAKE, then stop 191 wp_hyd_stamp(g, px, pz, bed, 2) 192 alive = 0 193 } else { 194 // stamp ALONG the segment (escape jumps are 300 units; a single stamp leaves gaps) 195 wp_hyd_stamp(g, px + (bx-px)/3, pz + (bz-pz)/3, bed, 1) 196 wp_hyd_stamp(g, px + (bx-px)*2/3, pz + (bz-pz)*2/3, bed, 1) 197 px = bx 198 pz = bz 199 var nb: i64 = bestH - 14 200 if nb > bed { nb = bed } // water NEVER flows uphill 201 bed = nb 202 if bestH < 0 { reached = 1; alive = 0 } // reached the sea 203 } 204 steps = steps + 1 205 if steps > 480 { alive = 0 } 206 if px < 0-WP_MAGIC_10100 { alive = 0 } 207 if px > WP_MAGIC_10100 { alive = 0 } 208 if pz < 0-WP_MAGIC_10100 { alive = 0 } 209 if pz > WP_MAGIC_10100 { alive = 0 } 210 } 211 st[WP_HYN*4] = sb 212 st[WP_HYN*4+1] = bed 213 st[WP_HYN*4+2] = steps 214 st[WP_HYN*4+3] = reached 215 WP_HYN = WP_HYN + 1 216 } 217 } 218 } 219 sx = sx + 640 220 } 221 sz = sz + 640 222 } 223 return WP_HYN 224} 225func wp_hyd_n() -> i64 { return WP_HYN } 226func wp_hyd_stat(i: i64, k: i64) -> i64 { let st: *i64 = WP_HYSTATS as *i64; return st[i*4+k] } 227func wp_hyd_bed(x: i64, z: i64) -> i64 { 228 if WP_HYD == 0 { return 0-WP_MAGIC_1000000 } 229 let cx: i64 = (x + HYSPAN)/HYCELL 230 let cz: i64 = (z + HYSPAN)/HYCELL 231 if cx < 0 { return 0-WP_MAGIC_1000000 } 232 if cx >= HYW { return 0-WP_MAGIC_1000000 } 233 if cz < 0 { return 0-WP_MAGIC_1000000 } 234 if cz >= HYW { return 0-WP_MAGIC_1000000 } 235 let g: *i64 = WP_HYD as *i64 236 let v: i64 = g[cz*HYW + cx] 237 if v == 0 { return 0-WP_MAGIC_1000000 } 238 return v - HYOFF 239} 240func wp_height(x: i64, z: i64) -> i64 { 241 var h: i64 = wp_height_raw(x, z) 242 if WP_CARVE == 1 { 243 let bed: i64 = wp_hyd_bed(x, z) 244 if bed > 0-WP_MAGIC_999999 { if bed < h { h = bed } } // STAGE 4: the traced river carves to its bed 245 } 246 return h 247} 248 249// ---- STAGE 5: biome from the parameter VECTOR (not height). ids: 250// 0 OCEAN, 1 BEACH, 2 RIVER, 3 DESERT, 4 SNOWY, 5 PEAKS, 6 FOREST, 7 PLAINS 251func wp_biome(x: i64, z: i64) -> i64 { 252 let cont: i64 = wp_param(x, z, 0) 253 if cont <= 0-120 { return 0 } 254 if cont <= 0-40 { return 1 } 255 let hraw: i64 = wp_height_raw(x, z) 256 if WP_CARVE == 1 { 257 let bed: i64 = wp_hyd_bed(x, z) 258 if bed > 0-WP_MAGIC_999999 { if bed < hraw { return 2 } } // RIVER = the traced watercourse (Q3 hydrology) 259 } 260 // ★Q3 ALTITUDE LAPSE: temperature FALLS with height -- snow tops mountains BECAUSE they are cold, 261 // deserts exist only in hot lowlands. te = temp - lapse(h). 262 var lapse: i64 = 0 263 if hraw > 0 { lapse = hraw*4/5 } 264 let te: i64 = wp_param(x, z, 1) - lapse 265 let humid: i64 = wp_param(x, z, 2) 266 let ero: i64 = wp_param(x, z, 3) 267 let weird: i64 = wp_param(x, z, 4) 268 if te > 170 { if humid < 0-90 { return 3 } } 269 if te < 0-210 { return 4 } 270 if weird > 230 { if ero < 80 { return 5 } } 271 if humid > 70 { return 6 } 272 return 7 273} 274 275// ---- STAGE 6: surface colour from biome + slope + altitude (r + g*256 + b*65536) ---- 276func wp_surface(x: i64, z: i64, h: i64, slope: i64) -> i64 { 277 let b: i64 = wp_biome(x, z) 278 if b == 0 { if h < 0-220 { return 14 + 42*256 + 120*WP_MAGIC_65536 } return 24 + 70*256 + 165*WP_MAGIC_65536 } 279 if h < 0 { return 52 + 118*256 + 196*WP_MAGIC_65536 } // carved river water 280 var col: i64 = 96 + 150*256 + 60*WP_MAGIC_65536 // plains grass 281 if b == 1 { col = 216 + 196*256 + 140*WP_MAGIC_65536 } // beach sand 282 if b == 3 { col = 228 + 200*256 + 120*WP_MAGIC_65536 } // desert sand 283 if b == 4 { col = 232 + 236*256 + 244*WP_MAGIC_65536 } // snow 284 if b == 5 { col = 130 + 124*256 + 120*WP_MAGIC_65536 } // peaks rock 285 if b == 6 { col = 52 + 110*256 + 44*WP_MAGIC_65536 } // forest grass 286 if slope > 110 { col = 122 + 112*256 + 96*WP_MAGIC_65536 } // steep -> exposed rock 287 if h > 330 { col = 236 + 239*256 + 246*WP_MAGIC_65536 } // snowcap by altitude 288 return col 289} 290 291// ---- STAGE 6.5: LIGHTING (2026-07-26, F1158 -- the stage the residual list always named). 292// WHY (measured, not taste): every head-to-head frame score names PALETTE as the gap -- the staged 293// world graded 667 RICH with det4 112 ~ Breeders' 125 but palette 227 vs their 1904, because flat 294// per-biome fills emit a handful of distinct colours. Lighting = sun-facing diffuse off the REAL 295// heightfield + per-cell albedo variation, so colour count comes from GEOMETRY, not from noise 296// (the GX-13/14 Goodhart line: a texture overlay that moves one stat is forbidden; shading that 297// follows the terrain is structure). Toggleable like the carver so the gate proves CAUSALITY. 298const WP_L_AMB: i64 = 256 // FLAT GROUND = unity (measured: an ambient floor of 168 made the 299 // stage darken-only -- 183/183 shade-darkened but 3/201 sun-brightened; 300 // centring at 256 gives true brighten/darken symmetry about unlit) 301const WP_L_GAIN: i64 = 22 // diffuse gain per height-step unit 302const WP_L_STEP: i64 = 90 // sample distance for the slope normal 303const WP_L_MAXB: i64 = 340 // brightness ceiling (overbright allowed; channels clamp at 255) 304const WP_L_MINB: i64 = 96 // brightness floor (shadow side never crushes to black) 305const WP_L_JIT: i64 = 14 // per-cell albedo jitter amplitude (small: variation, not noise) 306const WP_L_CELL: i64 = 96 // albedo cell size (matches the feature grid feel) 307func wp_set_light(v: i64) -> i64 { WP_LIGHT = v; return 0 } 308func wp_shade(x: i64, z: i64, h: i64, slope: i64) -> i64 { 309 let base: i64 = wp_surface(x, z, h, slope) 310 if WP_LIGHT == 0 { return base } 311 // sun from the north-west, elevation baked into the gain: faces dropping toward the sun catch it 312 let hw2: i64 = wp_height(x - WP_L_STEP, z - WP_L_STEP) 313 var d: i64 = (hw2 - h) 314 if d > WP_L_STEP { d = WP_L_STEP } 315 if d < 0-WP_L_STEP { d = 0-WP_L_STEP } 316 var bright: i64 = WP_L_AMB + (d * WP_L_GAIN) / 10 317 if bright > WP_L_MAXB { bright = WP_L_MAXB } 318 if bright < WP_L_MINB { bright = WP_L_MINB } 319 // per-cell albedo variation: a deterministic +-JIT on each channel, cell-stable (not per-pixel noise) 320 let cx: i64 = x / WP_L_CELL 321 let cz: i64 = z / WP_L_CELL 322 let jr: i64 = tm_hash3(cx, 91, cz) % (2*WP_L_JIT+1) - WP_L_JIT 323 let jg: i64 = tm_hash3(cx, 92, cz) % (2*WP_L_JIT+1) - WP_L_JIT 324 let jb2: i64 = tm_hash3(cx, 93, cz) % (2*WP_L_JIT+1) - WP_L_JIT 325 var r: i64 = ((base % 256) + jr) * bright / 256 326 var g: i64 = (((base / 256) % 256) + jg) * bright / 256 327 var b: i64 = (((base / WP_MAGIC_65536) % 256) + jb2) * bright / 256 328 r = wp_clamp(r, 0, 255) 329 g = wp_clamp(g, 0, 255) 330 b = wp_clamp(b, 0, 255) 331 return r + g*256 + b*WP_MAGIC_65536 332} 333 334// ---- STAGE 7: feature at a cell (0 none, 1 tree, 2 cactus, 3 spruce, 4 boulder) ---- 335func wp_feature(cx: i64, cz: i64) -> i64 { 336 if WP_FEAT == 0 { return 0 } 337 let h: i64 = wp_height(cx, cz) 338 if h < 6 { return 0 } // no features in water/rivers/beach-line 339 let b: i64 = wp_biome(cx, cz) 340 let hsh: i64 = tm_hash3(cx, WP_SEED*31 + 7, cz) 341 if b == 6 { if hsh < 190 { return 1 } } // forest: dense trees 342 if b == 7 { if hsh < 18 { return 1 } } // plains: sparse trees 343 if b == 3 { if hsh < 55 { return 2 } } // desert: cacti 344 if b == 4 { if hsh < 75 { return 3 } } // snowy: spruce 345 if b == 5 { if hsh < 45 { return 4 } } // peaks: boulders 346 return 0 347} 348 349// ---- STAGE 8: deterministic spawn -- first gentle land cell on an outward scan. out = [x, z, h]; 1 if found ---- 350func wp_spawn(out: *i64) -> i64 { 351 var r: i64 = 0 352 while r < 40 { 353 var i: i64 = 0 354 while i < 8 { 355 var x: i64 = 0 356 var z: i64 = 0 357 if i == 0 { x = r*130 } 358 if i == 1 { x = 0-r*130 } 359 if i == 2 { z = r*130 } 360 if i == 3 { z = 0-r*130 } 361 if i == 4 { x = r*130; z = r*130 } 362 if i == 5 { x = 0-r*130; z = r*130 } 363 if i == 6 { x = r*130; z = 0-r*130 } 364 if i == 7 { x = 0-r*130; z = 0-r*130 } 365 let h: i64 = wp_height(x, z) 366 if h > 10 { 367 let s1: i64 = wp_iabs(wp_height(x+90, z) - h) 368 let s2: i64 = wp_iabs(wp_height(x, z+90) - h) 369 if s1 + s2 < 80 { 370 let b: i64 = wp_biome(x, z) 371 if b != 5 { out[0] = x; out[1] = z; out[2] = h; return 1 } 372 } 373 } 374 i = i + 1 375 } 376 r = r + 1 377 } 378 return 0 379}