code wiki / (root) / nx_game_world.nx

nx_game_world.nx source

↩ module page · 170 lines · 7445 B

1// nx_game_world.nx -- the sovereign GAME WORLD layer: procedural terrain + entity geometry, both emitted 2// PLAYER-RELATIVE straight into the nx_swgpu vertex/triangle arrays. Extracted from nx_gx4_walk_gate the 3// moment a second gate needed the same world (migrate-on-touch, so there is never a second copy to drift -- 4// the dual-source hazard this ecosystem has paid for before). Behaviour-preserving by construction: the walk 5// gate's determinism checksum must stay bit-identical across the extraction, which is how the refactor is 6// PROVEN clean rather than assumed. license_tier: ORIGINAL 7import "nx_swgpu.nx" 8const GW_MAGIC_15000: i64 = 15000 9const GW_MAGIC_4096: i64 = 4096 10const GW_MAGIC_2500: i64 = 2500 11const GW_MAGIC_4200: i64 = 4200 12const GW_MAGIC_1200: i64 = 1200 13 14const GW_N: i64 = 28 // terrain grid cells per side -> (N+1)^2 verts 15const GW_CELL: i64 = 4096 // 1.0 world unit per cell (fx4096) 16 17// DESIGNED world (not tuned noise): a grass plain with ONE mountain at grid (14,22) -- rock ring, snow cap -- 18// plus gentle rolling. Composition is deliberate so a spawning player is looking AT something. 19func gw_h(i: i64, j: i64) -> i64 { 20 let di: i64 = i - 14 21 let dj: i64 = j - 22 22 let d2: i64 = di*di + dj*dj 23 var mountain: i64 = GW_MAGIC_15000 - 260*d2 24 if mountain < 0 { mountain = 0 } 25 let rolling: i64 = it_sin4096(i*920) * it_sin4096(j*760) / GW_MAGIC_4096 * 1000 / GW_MAGIC_4096 26 return mountain + rolling - GW_MAGIC_2500 27} 28 29// altitude band of a quad by its highest corner: 0 grass, 1 rock, 2 snow 30func gw_band(i: i64, j: i64) -> i64 { 31 var m: i64 = gw_h(i,j) 32 if gw_h(i+1,j) > m { m = gw_h(i+1,j) } 33 if gw_h(i,j+1) > m { m = gw_h(i,j+1) } 34 if gw_h(i+1,j+1) > m { m = gw_h(i+1,j+1) } 35 if m > GW_MAGIC_4200 { return 2 } 36 if m > (0-GW_MAGIC_1200) { return 1 } 37 return 0 38} 39 40// terrain height under a world position, clamped to the grid 41func gw_ground(wx: i64, wz: i64) -> i64 { 42 var gi: i64 = wx / GW_CELL 43 if gi < 0 { gi = 0 } 44 if gi > GW_N { gi = GW_N } 45 var gj: i64 = wz / GW_CELL 46 if gj < 0 { gj = 0 } 47 if gj > GW_N { gj = GW_N } 48 return gw_h(gi, gj) 49} 50 51// Emit every grid vert PLAYER-RELATIVE with normals from the height gradient, then ONLY the triangles whose 52// quad belongs to `band` (both windings = double-sided). One call per material band; the caller projects and 53// renders each pass over a shared z-buffer. 54func gw_build_terrain(base: i64, band: i64, ppx: i64, ppz: i64, eyey: i64) -> i64 { 55 let px: *i64 = (base + O_PX) as *i64 56 let py: *i64 = (base + O_PY) as *i64 57 let pz: *i64 = (base + O_PZ) as *i64 58 let nx: *i64 = (base + O_NX) as *i64 59 let ny: *i64 = (base + O_NY) as *i64 60 let nz: *i64 = (base + O_NZ) as *i64 61 let ta: *i64 = (base + O_TA) as *i64 62 let tn: *i64 = (base + O_TN) as *i64 63 let vc: *i64 = (base + O_VC) as *i64 64 sg_reset(base) 65 let side: i64 = GW_N + 1 66 var j: i64 = 0 67 while j < side { 68 var i: i64 = 0 69 while i < side { 70 let vi: i64 = j*side + i 71 px[vi] = i*GW_CELL - ppx 72 py[vi] = gw_h(i,j) - eyey 73 pz[vi] = j*GW_CELL - ppz 74 var il: i64 = i-1; if il < 0 { il = 0 } 75 var ir: i64 = i+1; if ir > GW_N { ir = GW_N } 76 var jl: i64 = j-1; if jl < 0 { jl = 0 } 77 var jr: i64 = j+1; if jr > GW_N { jr = GW_N } 78 var gx: i64 = 0 - (gw_h(ir,j) - gw_h(il,j)) 79 var gy: i64 = 2*GW_CELL 80 var gz: i64 = 0 - (gw_h(i,jr) - gw_h(i,jl)) 81 let gl: i64 = sg_isqrt(gx*gx + gy*gy + gz*gz) 82 if gl > 0 { gx = gx*GW_MAGIC_4096/gl; gy = gy*GW_MAGIC_4096/gl; gz = gz*GW_MAGIC_4096/gl } 83 nx[vi] = gx; ny[vi] = gy; nz[vi] = gz 84 i = i + 1 85 } 86 j = j + 1 87 } 88 vc[0] = side*side 89 var t: i64 = 0 90 j = 0 91 while j < GW_N { 92 var i2: i64 = 0 93 while i2 < GW_N { 94 if gw_band(i2,j) == band { 95 let v00: i64 = j*side + i2 96 let v10: i64 = j*side + i2 + 1 97 let v01: i64 = (j+1)*side + i2 98 let v11: i64 = (j+1)*side + i2 + 1 99 ta[t*3]=v00; ta[t*3+1]=v10; ta[t*3+2]=v11; t=t+1 100 ta[t*3]=v00; ta[t*3+1]=v11; ta[t*3+2]=v10; t=t+1 101 ta[t*3]=v00; ta[t*3+1]=v11; ta[t*3+2]=v01; t=t+1 102 ta[t*3]=v00; ta[t*3+1]=v01; ta[t*3+2]=v11; t=t+1 103 } 104 i2 = i2 + 1 105 } 106 j = j + 1 107 } 108 tn[0] = t 109 return t 110} 111 112// ---- ENTITIES: an octahedron per LIVE entity, player-relative, floating `lift` above the ground under it. 113// Collected/dead entities are simply not emitted -- so the render is a pure function of game state, which is 114// what makes "it vanished when I picked it up" a rendering FACT rather than a separate animation to maintain. 115func gw_build_entities(base: i64, ex: *i64, ez: *i64, alive: *i64, n: i64, ppx: i64, ppz: i64, eyey: i64, r: i64, lift: i64) -> i64 { 116 let px: *i64 = (base + O_PX) as *i64 117 let py: *i64 = (base + O_PY) as *i64 118 let pz: *i64 = (base + O_PZ) as *i64 119 let nxp: *i64 = (base + O_NX) as *i64 120 let nyp: *i64 = (base + O_NY) as *i64 121 let nzp: *i64 = (base + O_NZ) as *i64 122 let ta: *i64 = (base + O_TA) as *i64 123 let tn: *i64 = (base + O_TN) as *i64 124 let vc: *i64 = (base + O_VC) as *i64 125 sg_reset(base) 126 var m: i64 = 0 127 var t: i64 = 0 128 var k: i64 = 0 129 while k < n { 130 if alive[k] == 1 { 131 let cx: i64 = ex[k] - ppx 132 let cy: i64 = gw_ground(ex[k], ez[k]) + lift - eyey 133 let cz: i64 = ez[k] - ppz 134 let b: i64 = m * 6 135 px[b+0]=cx+r; py[b+0]=cy; pz[b+0]=cz; nxp[b+0]=GW_MAGIC_4096; nyp[b+0]=0; nzp[b+0]=0 136 px[b+1]=cx-r; py[b+1]=cy; pz[b+1]=cz; nxp[b+1]=0-GW_MAGIC_4096;nyp[b+1]=0; nzp[b+1]=0 137 px[b+2]=cx; py[b+2]=cy+r; pz[b+2]=cz; nxp[b+2]=0; nyp[b+2]=GW_MAGIC_4096; nzp[b+2]=0 138 px[b+3]=cx; py[b+3]=cy-r; pz[b+3]=cz; nxp[b+3]=0; nyp[b+3]=0-GW_MAGIC_4096;nzp[b+3]=0 139 px[b+4]=cx; py[b+4]=cy; pz[b+4]=cz+r; nxp[b+4]=0; nyp[b+4]=0; nzp[b+4]=GW_MAGIC_4096 140 px[b+5]=cx; py[b+5]=cy; pz[b+5]=cz-r; nxp[b+5]=0; nyp[b+5]=0; nzp[b+5]=0-GW_MAGIC_4096 141 // 8 octahedron faces, each emitted in BOTH windings (double-sided; winding conventions are not 142 // what an entity test should be sensitive to) 143 let fa: *i64 = sys_mmap(8*8) as *i64 144 let fb: *i64 = sys_mmap(8*8) as *i64 145 let fc: *i64 = sys_mmap(8*8) as *i64 146 fa[0]=2;fb[0]=0;fc[0]=4; fa[1]=2;fb[1]=4;fc[1]=1 147 fa[2]=2;fb[2]=1;fc[2]=5; fa[3]=2;fb[3]=5;fc[3]=0 148 fa[4]=3;fb[4]=4;fc[4]=0; fa[5]=3;fb[5]=1;fc[5]=4 149 fa[6]=3;fb[6]=5;fc[6]=1; fa[7]=3;fb[7]=0;fc[7]=5 150 var f: i64 = 0 151 while f < 8 { 152 ta[t*3]=b+fa[f]; ta[t*3+1]=b+fb[f]; ta[t*3+2]=b+fc[f]; t=t+1 153 ta[t*3]=b+fa[f]; ta[t*3+1]=b+fc[f]; ta[t*3+2]=b+fb[f]; t=t+1 154 f = f + 1 155 } 156 m = m + 1 157 } 158 k = k + 1 159 } 160 vc[0] = m * 6 161 tn[0] = t 162 return m 163} 164 165// squared world-plane distance -- the collision primitive (no sqrt, so it stays exact integer) 166func gw_dist2(ax: i64, az: i64, bx: i64, bz: i64) -> i64 { 167 let dx: i64 = ax - bx 168 let dz: i64 = az - bz 169 return dx*dx + dz*dz 170}