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}