code wiki / (root) / nx_worldgen.nx

nx_worldgen.nx source

↩ module page · 574 lines · 32155 B

1// nx_worldgen.nx -- ★SOVEREIGN INFINIGEN, RUNG 1: procedural NATURE (operator 2026-07-08, sharing princeton-vl/ 2// infinigen: "build a WORLD full of living people, animals, environments, weather"). Infinigen = seed->procedural 3// photoreal 3D, NO ML, NO external assets -- EXACTLY our doctrine. This is the nature/"hello world" module: a 4// seed grows a whole LANDSCAPE -- fBm terrain (grass/rock/snow by height+slope) + water (lakes/sea) + sky gradient 5// + sun + procedural clouds + atmospheric fog. All INTEGER, sovereign, our own heightfield ray-marcher. Different 6// seed = different world; a weather param retints sun/sky/fog (clear / sunset / overcast). license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_itrig.nx" 9import "nx_vecmath.nx" 10const WG_MAGIC_374761393: i64 = 374761393 11const WG_MAGIC_668265263: i64 = 668265263 12const WG_MAGIC_65536: i64 = 65536 13const WG_MAGIC_4000: i64 = 4000 14const WG_MAGIC_1024: i64 = 1024 15const WG_MAGIC_32768: i64 = 32768 16const WG_MAGIC_2900: i64 = 2900 17const WG_MAGIC_1048576: i64 = 1048576 18const WG_MAGIC_3000: i64 = 3000 19const WG_MAGIC_40000: i64 = 40000 20const WG_MAGIC_1050: i64 = 1050 21const WG_MAGIC_4096: i64 = 4096 22const WG_MAGIC_9999: i64 = 9999 23const WG_MAGIC_60000: i64 = 60000 24const WG_MAGIC_2000000000: i64 = 2000000000 25const WG_MAGIC_1800: i64 = 1800 26const WG_MAGIC_1500: i64 = 1500 27const WG_MAGIC_1350: i64 = 1350 28const WG_MAGIC_1100: i64 = 1100 29 30const WG_W: i64 = 1280 // 2x render -> the page downscales it = free anti-aliasing 31const WG_H: i64 = 768 32const WG_FOCAL: i64 = 720 33const WG_HORIZON: i64 = 92 // shifts the horizon down (look slightly over the land) 34const WATER: i64 = 0 // water level (world units) 35const HSCALE: i64 = 4600 36const SNOW: i64 = 2620 37 38// RETIRED ONTO THE SHARED OWNER 2026-08-24 (was a private Newton floor-sqrt; vm_isqrt is gate-proven exact). 39func wg_isqrt(v: i64) -> i64 { return vm_isqrt(v) } 40func wg_abs(v: i64) -> i64 { if v<0 {return 0-v} return v } 41func wg_min(a: i64, b: i64) -> i64 { if a<b {return a} return b } 42func wg_max(a: i64, b: i64) -> i64 { if a>b {return a} return b } 43func wg_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v } 44 45func wg_hash(ix: i64, iz: i64, seed: i64) -> i64 { 46 var h: i64 = (ix + seed*7)*WG_MAGIC_374761393 + (iz - seed*3)*WG_MAGIC_668265263 + ix*iz*97 + seed*13 47 if h < 0 { h = 0 - h } 48 return h % WG_MAGIC_65536 49} 50func wg_smooth(t: i64) -> i64 { return t*t*(768 - 2*t)/WG_MAGIC_65536 } // smoothstep, t in 0..256 -> 0..256 51func wg_vnoise(x256: i64, z256: i64, seed: i64) -> i64 { 52 let ix: i64 = x256 >> 8; let iz: i64 = z256 >> 8 53 let fx: i64 = x256 & 255; let fz: i64 = z256 & 255 54 let sx: i64 = wg_smooth(fx); let sz: i64 = wg_smooth(fz) 55 let v00: i64 = wg_hash(ix,iz,seed); let v10: i64 = wg_hash(ix+1,iz,seed) 56 let v01: i64 = wg_hash(ix,iz+1,seed); let v11: i64 = wg_hash(ix+1,iz+1,seed) 57 let a: i64 = v00 + (v10-v00)*sx/256 58 let b: i64 = v01 + (v11-v01)*sx/256 59 return a + (b-a)*sz/256 60} 61// fractal Brownian motion -> 0..1024 62func wg_fbm(wx: i64, wz: i64, seed: i64) -> i64 { 63 let cx: i64 = wx*256/WG_MAGIC_4000; let cz: i64 = wz*256/WG_MAGIC_4000 64 var sum: i64 = 0; var amp: i64 = 512; var norm: i64 = 0; var o: i64 = 0 65 while o < 5 { 66 sum = sum + wg_vnoise(cx << o, cz << o, seed + o*131) * amp / WG_MAGIC_65536 67 norm = norm + amp; amp = amp/2; o = o + 1 68 } 69 return sum*WG_MAGIC_1024/norm 70} 71func wg_terrain_h(wx: i64, wz: i64, seed: i64) -> i64 { return (wg_fbm(wx,wz,seed) - 402) * HSCALE / WG_MAGIC_1024 } 72 73// ---- PG22 (2026-09-06): THE VOLUMETRIC WEATHER FIELD -- a cloud is a 3D density, never a sheet --------------------- 74// wg_cloud_volume(wx, wy, wz, t, cover_q10, wind_dx_q10, wind_dz_q10, seed) -> density Q10 in [0, 1024]. The volume is 75// DRIVEN BY THE WEATHER FIELD THROUGH ITS OUTPUTS: cover_q10 comes from wg_cover_from_weather over the field's precip, 76// visibility and fog params (the gate composes nx_weather_pattern and proves CLEAR < OVERCAST < RAIN), and the field's 77// wind vector advects the volume in time -- no wind, no motion, which the gate holds as a neg-control. Shape is 3D 78// value-noise fBm over WG_CLOUD_CELL-unit cells, gated by a vertical envelope between WG_CLOUD_BASE and WG_CLOUD_TOP 79// and by the coverage threshold thr = 1024 - cover: density = clamp((fbm - thr) * WG_CLOUD_GAIN) * envelope. The 80// 2D sheet the CPU tier keeps drawing is wg_cloud_sheet, the incumbent sky expression BY NAME (the renderer below 81// calls it), so the fallback and the volume are one coverage law. Integer, seeded, bit-exact. This file's closure is 82// deliberately unchanged (no import): the weather field is an INPUT, so the volume compiles on every tree the field 83// does not, and a world without a weather field can still pass a constant cover. 84const WG_CLOUD_BASE: i64 = 5200 // world units; terrain tops out near HSCALE, the deck floats above it 85const WG_CLOUD_TOP: i64 = 8400 86const WG_CLOUD_RAMP_LO: i64 = 400 // density rises over this many units above the base 87const WG_CLOUD_RAMP_HI: i64 = 1200 // and falls over this many units below the top (anvils thin out) 88const WG_CLOUD_CELL: i64 = 640 // one noise cell in world units at octave 0 89const WG_CLOUD_OCTAVES: i64 = 4 90const WG_CLOUD_GAIN: i64 = 6 // slope of the coverage threshold 91const WG_CLOUD_SEED_SALT: i64 = 7 // the sheet's seed, shared so volume and sheet agree on which sky 92const WG_CLOUD_ADVECT: i64 = 12 // world units per time unit at wind Q10 = 1024 93const WG_CLOUD_ORIGIN: i64 = 1048576 // positive offset so >> and & see positive coordinates 94const WG_COVER_LOSTVIS_W: i64 = 4 // coverage weights over the Q14 field params: lost visibility dominates 95const WG_COVER_PRECIP_W: i64 = 1 96const WG_COVER_FOG_W: i64 = 1 97const WG_SHEET_THR_GAIN: i64 = 5 // the incumbent sheet threshold: 1024 - cloud*5 98func wg_hash3(ix: i64, iy: i64, iz: i64, seed: i64) -> i64 { 99 var h: i64 = (ix + seed*7)*WG_MAGIC_374761393 + (iy + seed*11)*WG_MAGIC_668265263 + (iz - seed*3)*WG_MAGIC_374761393 + ix*iz*97 + iy*iz*89 + ix*iy*83 + seed*13 100 if h < 0 { h = 0 - h } 101 return h % WG_MAGIC_65536 102} 103func wg_vnoise3(x256: i64, y256: i64, z256: i64, seed: i64) -> i64 { 104 let ix: i64 = x256 >> 8; let iy: i64 = y256 >> 8; let iz: i64 = z256 >> 8 105 let sx: i64 = wg_smooth(x256 & 255); let sy: i64 = wg_smooth(y256 & 255); let sz: i64 = wg_smooth(z256 & 255) 106 let v000: i64 = wg_hash3(ix,iy,iz,seed); let v100: i64 = wg_hash3(ix+1,iy,iz,seed) 107 let v010: i64 = wg_hash3(ix,iy+1,iz,seed); let v110: i64 = wg_hash3(ix+1,iy+1,iz,seed) 108 let v001: i64 = wg_hash3(ix,iy,iz+1,seed); let v101: i64 = wg_hash3(ix+1,iy,iz+1,seed) 109 let v011: i64 = wg_hash3(ix,iy+1,iz+1,seed); let v111: i64 = wg_hash3(ix+1,iy+1,iz+1,seed) 110 let a0: i64 = v000 + (v100-v000)*sx/256 111 let a1: i64 = v010 + (v110-v010)*sx/256 112 let b0: i64 = v001 + (v101-v001)*sx/256 113 let b1: i64 = v011 + (v111-v011)*sx/256 114 let a: i64 = a0 + (a1-a0)*sy/256 115 let b: i64 = b0 + (b1-b0)*sy/256 116 return a + (b-a)*sz/256 117} 118// 3D fractal Brownian motion -> 0..1024, the shape of wg_fbm lifted one dimension 119func wg_fbm3(x256: i64, y256: i64, z256: i64, seed: i64) -> i64 { 120 var sum: i64 = 0; var amp: i64 = 512; var norm: i64 = 0; var o: i64 = 0 121 while o < WG_CLOUD_OCTAVES { 122 sum = sum + wg_vnoise3(x256 << o, y256 << o, z256 << o, seed + o*131) * amp / WG_MAGIC_65536 123 norm = norm + amp; amp = amp/2; o = o + 1 124 } 125 return sum*WG_MAGIC_1024/norm 126} 127// the weather field's params (Q14, q = the field's own Q) -> cloud cover Q10: lost visibility, precipitation and fog 128func wg_cover_from_weather(precip_q14: i64, visibility_q14: i64, fog_q14: i64, q: i64) -> i64 { 129 if q <= 0 { return 0 } 130 var lost: i64 = q - visibility_q14 131 if lost < 0 { lost = 0 } 132 let c14: i64 = lost*WG_COVER_LOSTVIS_W + precip_q14*WG_COVER_PRECIP_W + fog_q14*WG_COVER_FOG_W 133 return wg_clamp(c14*WG_MAGIC_1024/q, 0, WG_MAGIC_1024) 134} 135// the vertical envelope Q10: 0 below the base and above the top, ramping in over RAMP_LO and out over RAMP_HI 136func wg_cloud_envelope(wy: i64) -> i64 { 137 if wy <= WG_CLOUD_BASE { return 0 } 138 if wy >= WG_CLOUD_TOP { return 0 } 139 let up: i64 = wg_min((wy - WG_CLOUD_BASE)*WG_MAGIC_1024/WG_CLOUD_RAMP_LO, WG_MAGIC_1024) 140 let dn: i64 = wg_min((WG_CLOUD_TOP - wy)*WG_MAGIC_1024/WG_CLOUD_RAMP_HI, WG_MAGIC_1024) 141 return wg_min(up, dn) 142} 143func wg_cloud_volume(wx: i64, wy: i64, wz: i64, t: i64, cover_q10: i64, wind_dx_q10: i64, wind_dz_q10: i64, seed: i64) -> i64 { 144 if cover_q10 <= 0 { return 0 } 145 let env: i64 = wg_cloud_envelope(wy) 146 if env <= 0 { return 0 } 147 let ax: i64 = wx - wind_dx_q10*t*WG_CLOUD_ADVECT/WG_MAGIC_1024 148 let az: i64 = wz - wind_dz_q10*t*WG_CLOUD_ADVECT/WG_MAGIC_1024 149 let x256: i64 = (ax + WG_CLOUD_ORIGIN)*256/WG_CLOUD_CELL 150 let y256: i64 = (wy + WG_CLOUD_ORIGIN)*256/WG_CLOUD_CELL 151 let z256: i64 = (az + WG_CLOUD_ORIGIN)*256/WG_CLOUD_CELL 152 let shape: i64 = wg_fbm3(x256, y256, z256, seed + WG_CLOUD_SEED_SALT) 153 let thr: i64 = WG_MAGIC_1024 - cover_q10 154 let d: i64 = wg_clamp((shape - thr)*WG_CLOUD_GAIN, 0, WG_MAGIC_1024) 155 return d*env/WG_MAGIC_1024 156} 157// the CPU tier's sheet: the incumbent sky cloud, positive where the sheet is cloud (fbm minus the coverage threshold) 158func wg_cloud_sheet(px: i64, pz: i64, cloud: i64) -> i64 { 159 let c: i64 = wg_fbm(px+WG_MAGIC_40000, pz+WG_MAGIC_40000, WG_CLOUD_SEED_SALT) 160 return c - (WG_MAGIC_1024 - cloud*WG_SHEET_THR_GAIN) 161} 162 163// weather -> params array [sundx,sundy,sundz(fx1024 unit), sunr,sung,sunb, skyzr,skyzg,skyzb, skyhr,skyhg,skyhb, fog, cloud] 164func wg_weather(mode: i64, W: *i64) -> i64 { 165 // default CLEAR day: sun upper-right 166 W[0]=520; W[1]=620; W[2]=560; W[3]=255; W[4]=248; W[5]=228 167 W[6]=64; W[7]=120; W[8]=210; W[9]=176; W[10]=205; W[11]=232; W[12]=44; W[13]=42 168 if mode == 1 { // SUNSET: low warm sun, warm sky 169 W[0]=780; W[1]=180; W[2]=560; W[3]=255; W[4]=170; W[5]=96 170 W[6]=40; W[7]=70; W[8]=150; W[9]=250; W[10]=150; W[11]=96; W[12]=60; W[13]=54 171 } 172 if mode == 2 { // OVERCAST: high diffuse, grey sky, heavy fog+cloud 173 W[0]=300; W[1]=760; W[2]=400; W[3]=210; W[4]=212; W[5]=214 174 W[6]=150; W[7]=158; W[8]=168; W[9]=196; W[10]=200; W[11]=205; W[12]=90; W[13]=110 175 } 176 let l: i64 = wg_isqrt(W[0]*W[0]+W[1]*W[1]+W[2]*W[2]) 177 if l > 0 { W[0]=W[0]*WG_MAGIC_1024/l; W[1]=W[1]*WG_MAGIC_1024/l; W[2]=W[2]*WG_MAGIC_1024/l } 178 return 0 179} 180 181// sun penumbra at a surface point (the soft-shadow march, shared by the pixel loop AND the GI cache): march 182// toward the sun tracking min(clearance/dist); 0 = fully shadowed .. 1024 = fully lit. Wp = weather (sun dir). 183func wg_sun_pen(seed: i64, hx: i64, hy: i64, hz: i64, Wp: *i64) -> i64 { 184 var pen: i64 = WG_MAGIC_1024 185 var sh2: i64 = 300 186 var ss: i64 = 0 187 while ss < 30 { 188 let qx: i64 = hx + Wp[0]*sh2/WG_MAGIC_1024 189 let qy: i64 = hy + Wp[1]*sh2/WG_MAGIC_1024 190 let qz: i64 = hz + Wp[2]*sh2/WG_MAGIC_1024 191 let clr: i64 = qy - wg_terrain_h(qx, qz, seed) + 10 192 if clr < 0 { pen = 0; ss = 999 } 193 else { 194 let p2: i64 = clr*WG_MAGIC_32768/sh2 195 if p2 < pen { pen = p2 } 196 sh2 = sh2 + 160 + sh2/9 197 if qy > WG_MAGIC_2900 { ss = 999 } 198 } 199 if ss != 999 { ss = ss + 1 } 200 } 201 if pen > WG_MAGIC_1024 { pen = WG_MAGIC_1024 } 202 return pen 203} 204 205// ★R3 GI: a SINGLE-BOUNCE IRRADIANCE CACHE (the classic technique, integer): a coarse world grid stores each 206// cell's SUN-LIT outgoing radiance (albedo x direct light); pixels gather from nearby cells with cosine + falloff 207// weighting -> coloured bounce light (sunlit grass bleeds green into shade). HONEST LABEL: one bounce, no 208// bounce-visibility test, coarse cells -- an approximation, not path tracing. wg_set_gi(0) disables (byte- 209// identical off path). 210const GI_N: i64 = 96 // cells per axis 211const GI_CS: i64 = 500 // world units per cell 212const GI_X0: i64 = 0 - 24000 // grid origin (covers the visible near field) 213const GI_Z0: i64 = 0 214static WG_GION: i64 215static WG_GIC: i64 // cache ptr: GI_N*GI_N cells x 2 i64 {packed rgb radiance, height} 216func wg_set_gi(on: i64) -> i64 { WG_GION = on; return 0 } 217func wg_gi_build(seed: i64, biome: i64, Wp: *i64) -> i64 { 218 if WG_GIC == 0 { WG_GIC = sys_mmap(GI_N*GI_N*2*8) as i64 } 219 let C: *i64 = WG_GIC as *i64 220 var j: i64 = 0 221 while j < GI_N { 222 var i: i64 = 0 223 while i < GI_N { 224 let wx: i64 = GI_X0 + i*GI_CS + GI_CS/2 225 let wz: i64 = GI_Z0 + j*GI_CS + GI_CS/2 226 let hh: i64 = wg_terrain_h(wx, wz, seed) 227 // cell normal (coarse) 228 let e: i64 = 120 229 let hl: i64 = wg_terrain_h(wx-e, wz, seed); let hr2: i64 = wg_terrain_h(wx+e, wz, seed) 230 let hd: i64 = wg_terrain_h(wx, wz-e, seed); let hu: i64 = wg_terrain_h(wx, wz+e, seed) 231 var nx: i64 = (hl-hr2)*WG_MAGIC_1024/(2*e); var ny: i64 = WG_MAGIC_1024; var nz: i64 = (hd-hu)*WG_MAGIC_1024/(2*e) 232 let nl: i64 = wg_isqrt(nx*nx+ny*ny+nz*nz) 233 if nl > 0 { nx=nx*WG_MAGIC_1024/nl; ny=ny*WG_MAGIC_1024/nl; nz=nz*WG_MAGIC_1024/nl } 234 // cell albedo (low-frequency biome palette: grass/rock/sand/snow by height+slope, no speckle) 235 var cr: i64 = 70; var cg: i64 = 104; var cb: i64 = 50 236 if biome == 1 { cr=196; cg=160; cb=110 } 237 if biome == 2 { cr=214; cg=224; cb=236 } 238 if biome == 3 { cr=150; cg=104; cb=44 } 239 var snowline: i64 = SNOW 240 if biome == 2 { snowline = 300 } 241 if ny < 640 { cr=118; cg=104; cb=94 } 242 if biome != 1 { if hh > snowline { if ny > 600 { cr=234; cg=240; cb=248 } } } 243 if hh < WATER { cr=24; cg=74; cb=116 } 244 // direct sun radiance at the cell (Lambert x penumbra), plus a small ambient floor 245 var nd: i64 = (nx*Wp[0]+ny*Wp[1]+nz*Wp[2])/WG_MAGIC_1024 246 if nd < 0 { nd = 0 } 247 var pen: i64 = WG_MAGIC_1024 248 if hh >= WATER { pen = wg_sun_pen(seed, wx, hh+14, wz, Wp) } 249 let lit: i64 = 20 + nd*95*pen/WG_MAGIC_1048576 250 var rr: i64 = cr*lit/100 251 var rg: i64 = cg*lit/100 252 var rb: i64 = cb*lit/100 253 if rr > 255 { rr = 255 } 254 if rg > 255 { rg = 255 } 255 if rb > 255 { rb = 255 } 256 C[(j*GI_N+i)*2] = rr + rg*256 + rb*WG_MAGIC_65536 257 C[(j*GI_N+i)*2+1] = hh 258 i = i + 1 259 } 260 j = j + 1 261 } 262 return 0 263} 264// gather bounce irradiance at a surface point with unit normal (nx,ny,nz fx1024). out[0..2] = rgb irradiance. 265func wg_gi_gather(hx: i64, hy: i64, hz: i64, nx: i64, ny: i64, nz: i64, out: *i64) -> i64 { 266 out[0]=0; out[1]=0; out[2]=0 267 if WG_GION == 0 { return 0 } 268 if WG_GIC == 0 { return 0 } 269 let C: *i64 = WG_GIC as *i64 270 let ci: i64 = (hx - GI_X0) / GI_CS 271 let cj: i64 = (hz - GI_Z0) / GI_CS 272 if ci < 2 { return 0 } 273 if cj < 2 { return 0 } 274 if ci > GI_N-3 { return 0 } 275 if cj > GI_N-3 { return 0 } 276 var sr: i64 = 0 277 var sg: i64 = 0 278 var sb: i64 = 0 279 var wsum: i64 = 0 280 var oj: i64 = 0 - 2 281 while oj <= 2 { 282 var oi: i64 = 0 - 2 283 while oi <= 2 { 284 var ring: i64 = oi 285 if ring < 0 { ring = 0 - ring } 286 var aj: i64 = oj 287 if aj < 0 { aj = 0 - aj } 288 if aj > ring { ring = aj } 289 if ring >= 1 { // exclude the self cell; use rings 1-2 290 let ii: i64 = ci + oi 291 let jj: i64 = cj + oj 292 let crad: i64 = C[(jj*GI_N+ii)*2] 293 let chh: i64 = C[(jj*GI_N+ii)*2+1] 294 let dxg: i64 = (GI_X0 + ii*GI_CS + GI_CS/2) - hx 295 let dyg: i64 = chh - hy 296 let dzg: i64 = (GI_Z0 + jj*GI_CS + GI_CS/2) - hz 297 let dl: i64 = wg_isqrt(dxg*dxg + dyg*dyg + dzg*dzg) 298 if dl > 60 { 299 var cosr: i64 = (nx*dxg + ny*dyg + nz*dzg)/dl // fx1024 cos at the receiver 300 if cosr > 0 { 301 let fall: i64 = GI_CS*WG_MAGIC_1024/(GI_CS + dl) // gentle distance falloff (fx1024) 302 let w: i64 = cosr*fall/WG_MAGIC_1024 303 sr = sr + (crad & 255)*w 304 sg = sg + ((crad >> 8) & 255)*w 305 sb = sb + ((crad >> 16) & 255)*w 306 wsum = wsum + WG_MAGIC_1024 307 } 308 } 309 } 310 oi = oi + 1 311 } 312 oj = oj + 1 313 } 314 if wsum == 0 { return 0 } 315 out[0] = sr/wsum 316 out[1] = sg/wsum 317 out[2] = sb/wsum 318 return 0 319} 320 321func wg_sky(dx: i64, dy: i64, dz: i64, Wp: *i64, oc: *i64) -> i64 { 322 var up: i64 = dy; if up<0 {up=0} 323 let zr: i64 = Wp[6]; let zg: i64 = Wp[7]; let zb: i64 = Wp[8] 324 let hr: i64 = Wp[9]; let hg: i64 = Wp[10]; let hb: i64 = Wp[11] 325 var r: i64 = hr + (zr-hr)*up/WG_MAGIC_1024; var g: i64 = hg + (zg-hg)*up/WG_MAGIC_1024; var b: i64 = hb + (zb-hb)*up/WG_MAGIC_1024 326 // sun glow + disk 327 let sd: i64 = (dx*Wp[0]+dy*Wp[1]+dz*Wp[2])/WG_MAGIC_1024 328 if sd > 800 { 329 let gl: i64 = (sd-800)*100/224 330 r = r + (Wp[3]-r)*gl/100; g = g + (Wp[4]-g)*gl/100; b = b + (Wp[5]-b)*gl/100 331 } 332 if sd > 1012 { r=Wp[3]; g=Wp[4]; b=Wp[5] } 333 // procedural clouds (fBm on the ray projected to a high plane) 334 if dy > 40 { 335 let px: i64 = dx*WG_MAGIC_3000/dy; let pz: i64 = dz*WG_MAGIC_3000/dy 336 let cs: i64 = wg_cloud_sheet(px, pz, Wp[13]) // PG22: the sheet by name, one coverage law with the volume 337 if cs > 0 { 338 let cf: i64 = wg_min(cs*100/200, 100) * up/WG_MAGIC_1024 339 r = r + (238-r)*cf/100; g = g + (240-g)*cf/100; b = b + (242-b)*cf/100 340 } 341 } 342 oc[0]=wg_clamp(r,0,255); oc[1]=wg_clamp(g,0,255); oc[2]=wg_clamp(b,0,255) 343 return 0 344} 345 346// ★R1 DIVERSITY: BIOMES (census critic: "1 fBm biome family"). biome: 0 TEMPERATE (the original) · 1 DESERT 347// (sand/red-rock, no snow) · 2 ARCTIC (snow-dominant, ice water) · 3 AUTUMN (warm foliage). Same terrain 348// machinery; a biome = a palette + snowline row. 349// ★R2 GT ANNOTATIONS: the renderer emits CV ground truth alongside the image (their GroundTruthAnnotations 350// use-case): seg (0 sky / 1 terrain / 2 water), nrm (surface normal packed rgb: 128 + n*127/1024 per axis; 351// 0 for sky), depth (hit distance; BIG for sky). Occlusion boundaries derive from depth discontinuities. 352// ★R4 CAMERA RIG: cam = [dcamx, dcamy, dcamz, yaw] offsets/orientation over the auto terrain-following base 353// (cam 0/absent -> the original fixed +z view, byte-identical). A trajectory is a sequence of cam vectors -> 354// animation; reprojecting a pixel's world point through a second cam -> exact optical-flow GT (see wg_project). 355// render a world. Writes fb (WG_W*WG_H) + depth + seg + nrm. 356func wg_cam_base_y(seed: i64) -> i64 { return wg_max(WATER, wg_terrain_h(0,0,seed)) + WG_MAGIC_1050 } 357func worldgen_render_full(seed: i64, mode: i64, biome: i64, cam: *i64, fb: *i64, depth: *i64, seg: *i64, nrm: *i64) -> i64 { 358 let Wp: *i64 = sys_mmap(32*8) as *i64 359 wg_weather(mode, Wp) 360 if WG_GION == 1 { wg_gi_build(seed, biome, Wp) } // R3: refresh the bounce cache for this frame 361 let gio: *i64 = sys_mmap(32) as *i64 362 let oc: *i64 = sys_mmap(32) as *i64 363 let camx: i64 = cam[0] 364 let camy: i64 = wg_cam_base_y(seed) + cam[1] 365 let camz0: i64 = cam[2] 366 let yaw: i64 = cam[3] 367 let sy4: i64 = it_sin4096(yaw) 368 let cy4: i64 = it_cos4096(yaw) 369 var py: i64 = 0 370 while py < WG_H { 371 var px: i64 = 0 372 while px < WG_W { 373 let sxo: i64 = px - WG_W/2 374 let syo: i64 = (WG_H/2 - py) - WG_HORIZON 375 var dx: i64 = WG_FOCAL*sy4/WG_MAGIC_4096 + sxo*cy4/WG_MAGIC_4096 376 var dy: i64 = syo 377 var dz: i64 = WG_FOCAL*cy4/WG_MAGIC_4096 - sxo*sy4/WG_MAGIC_4096 378 let dl: i64 = wg_isqrt(dx*dx+dy*dy+dz*dz) 379 dx = dx*WG_MAGIC_1024/dl; dy = dy*WG_MAGIC_1024/dl; dz = dz*WG_MAGIC_1024/dl 380 // march the heightfield 381 var t: i64 = 200 382 var hit: i64 = 0 383 var hx: i64 = 0; var hy: i64 = 0; var hz: i64 = 0; var hh: i64 = 0 384 var step: i64 = 0 385 while step < 220 { 386 let wx: i64 = camx + dx*t/WG_MAGIC_1024 387 let wy: i64 = camy + dy*t/WG_MAGIC_1024 388 let wz: i64 = camz0 + dz*t/WG_MAGIC_1024 389 let th: i64 = wg_terrain_h(wx, wz, seed) 390 if wy < th { hit=1; hx=wx; hy=wy; hz=wz; hh=th; step=WG_MAGIC_9999 } 391 else { t = t + 55 + t/48; if t > WG_MAGIC_60000 { step=WG_MAGIC_9999 } } 392 if step != WG_MAGIC_9999 { step = step + 1 } 393 } 394 var r: i64 = 0; var g: i64 = 0; var b: i64 = 0 395 // water intercept (ray crosses y=WATER going down) 396 var twater: i64 = 0 397 if dy < 0 { twater = (WATER - camy)*WG_MAGIC_1024/dy } 398 let water_in_front: i64 = (twater > 0) & ((hit == 0) | (twater < t)) 399 // decide surface 400 var surf: i64 = 0 // 0 sky, 1 terrain, 2 water 401 if hit == 1 { if hh >= WATER { surf = 1 } } 402 if (surf == 0) & (water_in_front == 1) { surf = 2 } 403 if (hit == 1) & (hh < WATER) & (water_in_front == 1) { surf = 2 } 404 405 var dpt: i64 = WG_MAGIC_2000000000 406 if surf == 1 { dpt = t } 407 if surf == 2 { dpt = twater } 408 depth[py*WG_W+px] = dpt 409 seg[py*WG_W+px] = surf // GT: 0 sky / 1 terrain / 2 water 410 var nrmv: i64 = 0 // GT normal (sky = 0) 411 if surf == 2 { nrmv = 128 + 255*256 + 128*WG_MAGIC_65536 } // water: straight up 412 nrm[py*WG_W+px] = nrmv 413 414 if surf == 1 { 415 // terrain normal 416 let e: i64 = 24 417 let hl: i64 = wg_terrain_h(hx-e, hz, seed); let hr2: i64 = wg_terrain_h(hx+e, hz, seed) 418 let hd: i64 = wg_terrain_h(hx, hz-e, seed); let hu: i64 = wg_terrain_h(hx, hz+e, seed) 419 var nx: i64 = (hl-hr2)*WG_MAGIC_1024/(2*e); var ny: i64 = WG_MAGIC_1024; var nz: i64 = (hd-hu)*WG_MAGIC_1024/(2*e) 420 // micro-relief bump from fine noise -> surface texture caught by the lighting (not a smooth blob) 421 let bb0: i64 = wg_vnoise((hx+6)*256/55, hz*256/55, seed+31) - wg_vnoise((hx-6)*256/55, hz*256/55, seed+31) 422 let bb1: i64 = wg_vnoise(hx*256/55, (hz+6)*256/55, seed+31) - wg_vnoise(hx*256/55, (hz-6)*256/55, seed+31) 423 nx = nx - bb0*5/100; nz = nz - bb1*5/100 424 let nl: i64 = wg_isqrt(nx*nx+ny*ny+nz*nz) 425 // GT normal (unit, packed rgb) -- written before shading consumes nx/ny/nz 426 if nl > 0 { 427 let ux2: i64 = nx*WG_MAGIC_1024/nl 428 let uy2: i64 = ny*WG_MAGIC_1024/nl 429 let uz2: i64 = nz*WG_MAGIC_1024/nl 430 nrm[py*WG_W+px] = (128 + ux2*127/WG_MAGIC_1024) + (128 + uy2*127/WG_MAGIC_1024)*256 + (128 + uz2*127/WG_MAGIC_1024)*WG_MAGIC_65536 431 } 432 if nl>0 { nx=nx*WG_MAGIC_1024/nl; ny=ny*WG_MAGIC_1024/nl; nz=nz*WG_MAGIC_1024/nl } 433 var nd: i64 = (nx*Wp[0]+ny*Wp[1]+nz*Wp[2])/WG_MAGIC_1024 // fx1024 Lambert (0..WG_MAGIC_1024) 434 if nd<0 {nd=0} 435 // ★P3 SOFT SHADOWS (penumbra): the shared sun-march helper (also feeds the GI cache) -- 436 // edges soften with occluder distance; only DIRECT light is occluded (ambient stays). 437 let pen: i64 = wg_sun_pen(seed, hx, hy, hz, Wp) 438 // ★P3 r2 SKY-VISIBILITY ambient (cheap GI): surrounding ridges block skylight -> valleys and 439 // hollows receive less ambient. Horizon slope sampled 4 directions x 3 distances; sv 0..1024. 440 var slmax: i64 = 0 441 var sd2: i64 = 0 442 while sd2 < 4 { 443 var ddx: i64 = 0 444 var ddz: i64 = 0 445 if sd2 == 0 { ddx = 1 } 446 if sd2 == 1 { ddx = 0-1 } 447 if sd2 == 2 { ddz = 1 } 448 if sd2 == 3 { ddz = 0-1 } 449 var dq: i64 = 200 450 while dq <= WG_MAGIC_1800 { 451 let th2: i64 = wg_terrain_h(hx + ddx*dq, hz + ddz*dq, seed) 452 let sl: i64 = (th2 - hy) * WG_MAGIC_1024 / dq 453 if sl > slmax { slmax = sl } 454 dq = dq * 3 455 } 456 sd2 = sd2 + 1 457 } 458 if slmax > WG_MAGIC_1024 { slmax = WG_MAGIC_1024 } 459 var sv: i64 = WG_MAGIC_1024 - slmax*6/10 460 if sv < 420 { sv = 420 } 461 let lit: i64 = 42*sv/WG_MAGIC_1024 + nd*82*pen/WG_MAGIC_1048576 // sky-vis ambient + penumbra-scaled direct 462 // base colour by BIOME, with per-location vegetation variation 463 let nv2: i64 = wg_vnoise(hx*256/WG_MAGIC_1500, hz*256/WG_MAGIC_1500, seed+99) 464 var cr: i64 = 60 + nv2*44/WG_MAGIC_65536; var cg: i64 = 94 + nv2*50/WG_MAGIC_65536; var cb: i64 = 42 + nv2*22/WG_MAGIC_65536 465 var rockr: i64 = 112; var rockg: i64 = 101; var rockb: i64 = 92 466 var snowline: i64 = SNOW 467 var snowon: i64 = 1 468 if biome == 1 { // DESERT: sand base, red rock, no snow 469 cr = 188 + nv2*36/WG_MAGIC_65536; cg = 154 + nv2*30/WG_MAGIC_65536; cb = 104 + nv2*18/WG_MAGIC_65536 470 rockr = 152; rockg = 96; rockb = 66 471 snowon = 0 472 } 473 if biome == 2 { // ARCTIC: snow base, blue-grey rock 474 cr = 212 + nv2*24/WG_MAGIC_65536; cg = 222 + nv2*20/WG_MAGIC_65536; cb = 234 + nv2*14/WG_MAGIC_65536 475 rockr = 96; rockg = 106; rockb = 120 476 snowline = 300 477 } 478 if biome == 3 { // AUTUMN: warm mixed foliage 479 cr = 128 + nv2*70/WG_MAGIC_65536; cg = 84 + nv2*44/WG_MAGIC_65536; cb = 30 + nv2*16/WG_MAGIC_65536 480 rockr = 118; rockg = 100; rockb = 86 481 } 482 // alpine: higher -> browner (vegetation thins out); skip where base is already snow/sand 483 if biome == 0 { if hh > WG_MAGIC_1350 { let m: i64 = wg_min((hh-WG_MAGIC_1350)*100/WG_MAGIC_1100, 100); cr=cr+(124-cr)*m/100; cg=cg+(110-cg)*m/100; cb=cb+(94-cb)*m/100 } } 484 if biome == 3 { if hh > WG_MAGIC_1350 { let m: i64 = wg_min((hh-WG_MAGIC_1350)*100/WG_MAGIC_1100, 100); cr=cr+(124-cr)*m/100; cg=cg+(104-cg)*m/100; cb=cb+(88-cb)*m/100 } } 485 if ny < 640 { cr=rockr; cg=rockg; cb=rockb } // steep -> exposed rock 486 if hh < WATER+130 { cr=198; cg=186; cb=152 } // shoreline sand 487 if snowon == 1 { if (hh > snowline) & (ny > 600) { cr=234; cg=240; cb=248 } } // snow: high AND gentle 488 // fine surface speckle (grass blades / dirt / rock grain) 489 let tex2: i64 = wg_vnoise(hx*256/24, hz*256/24, seed+53) 490 let tv: i64 = (tex2-WG_MAGIC_32768)*26/WG_MAGIC_65536 491 cr = cr + tv; cg = cg + tv*11/10; cb = cb + tv*6/10 492 r = cr*lit/100; g = cg*lit/100; b = cb*lit/100 493 // ★R3 GI: gather single-bounce irradiance from the cache; couple through the receiver albedo 494 // (green light on green grass reinforces; green light on snow shows as a green tint -- physical) 495 wg_gi_gather(hx, hy, hz, nx, ny, nz, gio) 496 r = r + cr*gio[0]*3/512 497 g = g + cg*gio[1]*3/512 498 b = b + cb*gio[2]*3/512 499 b = b + (WG_MAGIC_1024-pen)*9/WG_MAGIC_1024 // shade is sky-lit -> subtle cool tint 500 // atmospheric fog toward horizon-sky by distance 501 let fog: i64 = wg_min(t*Wp[12]/WG_MAGIC_60000, 100) 502 r = r + (Wp[9]-r)*fog/100; g = g + (Wp[10]-g)*fog/100; b = b + (Wp[11]-b)*fog/100 503 } 504 if surf == 2 { 505 // water: deep blue blended with sky reflection + sun glint (arctic = icy pale) 506 wg_sky(dx, 0-dy, dz, Wp, oc) // reflected sky (mirror y) 507 var dwr: i64 = 24; var dwg: i64 = 74; var dwb: i64 = 116 508 if biome == 2 { dwr = 118; dwg = 160; dwb = 190 } 509 if biome == 1 { dwr = 30; dwg = 96; dwb = 104 } 510 r = (dwr + oc[0])/2; g = (dwg + oc[1])/2; b = (dwb + oc[2])/2 511 let sd: i64 = (dx*Wp[0]-dy*Wp[1]+dz*Wp[2])/WG_MAGIC_1024 512 if sd > 990 { r=wg_min(r+120,255); g=wg_min(g+110,255); b=wg_min(b+90,255) } 513 let fog: i64 = wg_min(twater*Wp[12]/WG_MAGIC_60000, 100) 514 r = r + (Wp[9]-r)*fog/100; g = g + (Wp[10]-g)*fog/100; b = b + (Wp[11]-b)*fog/100 515 } 516 if surf == 0 { wg_sky(dx, dy, dz, Wp, oc); r=oc[0]; g=oc[1]; b=oc[2] } 517 fb[py*WG_W+px] = wg_clamp(r,0,255) + wg_clamp(g,0,255)*256 + wg_clamp(b,0,255)*WG_MAGIC_65536 518 px = px + 1 519 } 520 py = py + 1 521 } 522 return 0 523} 524// ★R4 camera helpers (the inverse pair the flow GT reuses, so gate + renderer share exact math): 525// wg_ray = the normalized fx1024 ray direction for a pixel (depends only on pixel + yaw); 526// wg_project = screen coords of a world point through cam=[dx,dy,dz,yaw] (returns -1 in out[2] if behind). 527func wg_ray(px: i64, py: i64, yaw: i64, out: *i64) -> i64 { 528 let sy4: i64 = it_sin4096(yaw) 529 let cy4: i64 = it_cos4096(yaw) 530 let sxo: i64 = px - WG_W/2 531 let syo: i64 = (WG_H/2 - py) - WG_HORIZON 532 var dx: i64 = WG_FOCAL*sy4/WG_MAGIC_4096 + sxo*cy4/WG_MAGIC_4096 533 var dy: i64 = syo 534 var dz: i64 = WG_FOCAL*cy4/WG_MAGIC_4096 - sxo*sy4/WG_MAGIC_4096 535 let dl: i64 = wg_isqrt(dx*dx+dy*dy+dz*dz) 536 if dl > 0 { dx = dx*WG_MAGIC_1024/dl; dy = dy*WG_MAGIC_1024/dl; dz = dz*WG_MAGIC_1024/dl } 537 out[0]=dx; out[1]=dy; out[2]=dz 538 return 0 539} 540func wg_project(seed: i64, cam: *i64, Px: i64, Py: i64, Pz: i64, out: *i64) -> i64 { 541 let camx: i64 = cam[0] 542 let camy: i64 = wg_cam_base_y(seed) + cam[1] 543 let camz0: i64 = cam[2] 544 let yaw: i64 = cam[3] 545 let sy4: i64 = it_sin4096(yaw) 546 let cy4: i64 = it_cos4096(yaw) 547 let rx: i64 = Px - camx 548 let ry: i64 = Py - camy 549 let rz: i64 = Pz - camz0 550 let zc: i64 = (rx*sy4 + rz*cy4)/WG_MAGIC_4096 // forward distance 551 out[2] = 0 - 1 552 if zc <= 16 { return 0 } // behind the camera 553 let xc: i64 = (rx*cy4 - rz*sy4)/WG_MAGIC_4096 // right offset 554 out[0] = WG_W/2 + WG_FOCAL*xc/zc 555 out[1] = WG_H/2 - WG_HORIZON - WG_FOCAL*ry/zc 556 out[2] = zc 557 return 0 558} 559 560// back-compat wrappers: fixed camera / no annotations / temperate biome / no depth buffer 561func worldgen_render_bd(seed: i64, mode: i64, biome: i64, fb: *i64, depth: *i64) -> i64 { 562 let s: *i64 = sys_mmap(WG_W*WG_H*8) as *i64 563 let n: *i64 = sys_mmap(WG_W*WG_H*8) as *i64 564 let cam: *i64 = sys_mmap(4*8) as *i64 // [0,0,0,0] = the original fixed +z camera (identical) 565 return worldgen_render_full(seed, mode, biome, cam, fb, depth, s, n) 566} 567func worldgen_render_d(seed: i64, mode: i64, fb: *i64, depth: *i64) -> i64 { return worldgen_render_bd(seed, mode, 0, fb, depth) } 568func worldgen_render(seed: i64, mode: i64, fb: *i64) -> i64 { 569 let d: *i64 = sys_mmap(WG_W*WG_H*8) as *i64 570 worldgen_render_bd(seed, mode, 0, fb, d) 571 return 0 572} 573func wg_w() -> i64 { return WG_W } 574func wg_h() -> i64 { return WG_H }