code wiki / _hdl_build / nx_mesh_terrain_gate.nx
nx_mesh_terrain_gate.nx source
↩ module page · 202 lines · 7941 B
1// nx_mesh_terrain_gate.nx -- proves the renderer is NOT voxel-bound: a smooth TRIANGULATED heightfield mesh
2// (not cubes) on the same sovereign f32 raster path. Same value-noise terrain as the voxel world, but rendered
3// as a continuous low-poly surface: per-grid-point f32 height, 2 triangles per cell, slope-based flat shading,
4// height biomes, perspective + z-buffer. exit 0 = rendered. Writes mesh_terrain.bmp. license_tier: ORIGINAL
5import "nx_syscalls.nx"
6import "nx_f32_hw.nx"
7
8const W: i64 = 320
9const H: i64 = 240
10const HW: i64 = 160
11const HH: i64 = 120
12const GRID: i64 = 22
13const FOCAL: i64 = 340
14const PUSH: i64 = 40
15const ZFAR: i64 = 1073741824
16
17func imin(a: i64, b: i64) -> i64 { if a < b { return a } return b }
18func imax(a: i64, b: i64) -> i64 { if a > b { return a } return b }
19func min3(a: i64, b: i64, c: i64) -> i64 { return imin(a, imin(b, c)) }
20func max3(a: i64, b: i64, c: i64) -> i64 { return imax(a, imax(b, c)) }
21func clamp255(v: i64) -> i64 { if v < 0 { return 0 } if v > 255 { return 255 } return v }
22func same_sign3(a: i64, b: i64, c: i64) -> i64 {
23 if a >= 0 { if b >= 0 { if c >= 0 { return 1 } } }
24 if a <= 0 { if b <= 0 { if c <= 0 { return 1 } } }
25 return 0
26}
27func fratio(num: i64, den: i64) -> i64 { return f32_div(f32_of(num), f32_of(den)) }
28
29// smooth bilinear value noise -> centi-height (0..~500 = 0..5 world units). Continuous => smooth surface.
30func vlat(ix: i64, iz: i64) -> i64 { var n: i64 = (ix * 131 + iz * 197 + ix * iz * 13 + 1290) % 256; if n < 0 { n = n + 256 } return n }
31func hcenti(i: i64, j: i64) -> i64 {
32 let L: i64 = 5
33 let bx: i64 = i + 200
34 let bz: i64 = j + 200
35 let ix: i64 = bx / L
36 let iz: i64 = bz / L
37 let fx: i64 = (bx % L) * 256 / L
38 let fz: i64 = (bz % L) * 256 / L
39 let h00: i64 = vlat(ix, iz)
40 let h10: i64 = vlat(ix + 1, iz)
41 let h01: i64 = vlat(ix, iz + 1)
42 let h11: i64 = vlat(ix + 1, iz + 1)
43 let a: i64 = h00 * 256 + (h10 - h00) * fx
44 let b: i64 = h01 * 256 + (h11 - h01) * fx
45 return (a * 256 + (b - a) * fz) / 65536 * 2 // 0..510
46}
47// z-buffered flat-colour triangle (depth interpolated; no texture).
48func fill_flat(fb: *i64, zb: *i64, x0: i64, y0: i64, d0: i64, x1: i64, y1: i64, d1: i64, x2: i64, y2: i64, d2: i64, col: i64) -> i64 {
49 let area: i64 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)
50 if area == 0 { return 0 }
51 let minx: i64 = imax(0, min3(x0, x1, x2))
52 let maxx: i64 = imin(W - 1, max3(x0, x1, x2))
53 let miny: i64 = imax(0, min3(y0, y1, y2))
54 let maxy: i64 = imin(H - 1, max3(y0, y1, y2))
55 var py: i64 = miny
56 while py <= maxy {
57 var px: i64 = minx
58 while px <= maxx {
59 let e0: i64 = (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1)
60 let e1: i64 = (x0 - x2) * (py - y2) - (y0 - y2) * (px - x2)
61 let e2: i64 = (x1 - x0) * (py - y0) - (y1 - y0) * (px - x0)
62 if same_sign3(e0, e1, e2) == 1 {
63 let d: i64 = (e0 * d0 + e1 * d1 + e2 * d2) / area
64 let idx: i64 = py * W + px
65 if d < zb[idx] { zb[idx] = d; fb[idx] = col }
66 }
67 px = px + 1
68 }
69 py = py + 1
70 }
71 return 0
72}
73// project world (f32 x, integer-centi height hy, f32 z) -> out [sx, sy, depth, ok].
74func projh(R: *i64, i: i64, hy: i64, j: i64, out: *i64) -> i64 {
75 let v: *i64 = sys_mmap(32) as *i64
76 let r: *i64 = sys_mmap(32) as *i64
77 v[0] = f32_sub(f32_of(i), fratio(GRID, 2))
78 v[1] = f32_sub(f32_div(f32_of(hy), f32_of(100)), f32_of(2))
79 v[2] = f32_sub(f32_of(j), fratio(GRID, 2))
80 v[3] = f32_of(0)
81 m4_vec4(R, v, r)
82 let pz: i64 = f32_add(r[2], f32_of(PUSH))
83 if (pz & 0x80000000) != 0 { out[3] = 0; return 0 }
84 let zi: i64 = f32_int(f32_mul(pz, f32_of(64)))
85 if zi <= 0 { out[3] = 0; return 0 }
86 out[0] = HW + f32_int(f32_div(f32_mul(r[0], f32_of(FOCAL)), pz))
87 out[1] = HH - f32_int(f32_div(f32_mul(r[1], f32_of(FOCAL)), pz))
88 out[2] = zi
89 out[3] = 1
90 return 0
91}
92// biome colour by average centi-height, shaded by slope (flat=bright, steep=dark).
93func tri_color(ha: i64, hb: i64, hc: i64) -> i64 {
94 let avg: i64 = (ha + hb + hc) / 3
95 let spread: i64 = max3(ha, hb, hc) - min3(ha, hb, hc)
96 let bright: i64 = clamp255(235 - spread * 2)
97 var r: i64 = 96; var g: i64 = 172; var b: i64 = 76 // grass
98 if avg < 120 { r = 64; g = 116; b = 204 } // water
99 if avg >= 360 { r = 120; g = 116; b = 110 } // rock
100 if avg >= 450 { r = 236; g = 238; b = 245 } // snow
101 return ((r * bright) / 255) + ((g * bright) / 255) * 256 + ((b * bright) / 255) * 65536
102}
103func put_u32le(b: *u8, o: i64, v: i64) -> i64 {
104 b[o] = (v & 0xff) as u8
105 b[o + 1] = ((v >> 8) & 0xff) as u8
106 b[o + 2] = ((v >> 16) & 0xff) as u8
107 b[o + 3] = ((v >> 24) & 0xff) as u8
108 return 0
109}
110func write_bmp(fb: *i64, path: *u8) -> i64 {
111 let pix: i64 = W * H * 3
112 let total: i64 = 54 + pix
113 let b: *u8 = sys_mmap(total) as *u8
114 b[0] = 66 as u8
115 b[1] = 77 as u8
116 put_u32le(b, 2, total)
117 put_u32le(b, 10, 54)
118 put_u32le(b, 14, 40)
119 put_u32le(b, 18, W)
120 put_u32le(b, 22, H)
121 b[26] = 1 as u8
122 b[28] = 24 as u8
123 put_u32le(b, 34, pix)
124 var o: i64 = 54
125 var ry: i64 = 0
126 while ry < H {
127 let syrow: i64 = H - 1 - ry
128 var x: i64 = 0
129 while x < W {
130 let c: i64 = fb[syrow * W + x]
131 b[o] = ((c >> 16) & 0xff) as u8
132 b[o + 1] = ((c >> 8) & 0xff) as u8
133 b[o + 2] = (c & 0xff) as u8
134 o = o + 3
135 x = x + 1
136 }
137 ry = ry + 1
138 }
139 let fd: i64 = sys_openat_wr(path, 0x1a4)
140 if fd < 0 { return 0 - 1 }
141 sys_write(fd, b, total)
142 sys_close(fd)
143 return 0
144}
145
146func main() -> i64 {
147 let fb: *i64 = sys_mmap(W * H * 8) as *i64
148 let zb: *i64 = sys_mmap(W * H * 8) as *i64
149 let sky: i64 = 142 + 186 * 256 + 228 * 65536
150 var i: i64 = 0
151 while i < W * H { fb[i] = sky; zb[i] = ZFAR; i = i + 1 }
152
153 let Ry: *i64 = sys_mmap(128) as *i64
154 let Rx: *i64 = sys_mmap(128) as *i64
155 let R: *i64 = sys_mmap(128) as *i64
156 m4_roty(fratio(819, 1000), fratio(574, 1000), Ry)
157 m4_rotx(fratio(819, 1000), f32_neg(fratio(574, 1000)), Rx)
158 m4_mul(Rx, Ry, R)
159
160 // project every grid vertex once; store screen x,y,depth,ok + its centi-height.
161 let np: i64 = GRID * GRID
162 let vx: *i64 = sys_mmap(np * 8) as *i64
163 let vy: *i64 = sys_mmap(np * 8) as *i64
164 let vd: *i64 = sys_mmap(np * 8) as *i64
165 let vok: *i64 = sys_mmap(np * 8) as *i64
166 let vh: *i64 = sys_mmap(np * 8) as *i64
167 var j: i64 = 0
168 while j < GRID {
169 var ii: i64 = 0
170 while ii < GRID {
171 let g: i64 = j * GRID + ii
172 let hh: i64 = hcenti(ii, j)
173 let o: *i64 = sys_mmap(32) as *i64
174 projh(R, ii, hh, j, o)
175 vx[g] = o[0]; vy[g] = o[1]; vd[g] = o[2]; vok[g] = o[3]; vh[g] = hh
176 ii = ii + 1
177 }
178 j = j + 1
179 }
180 // 2 triangles per cell.
181 var cj: i64 = 0
182 while cj < GRID - 1 {
183 var ci: i64 = 0
184 while ci < GRID - 1 {
185 let a: i64 = cj * GRID + ci
186 let b: i64 = cj * GRID + ci + 1
187 let c: i64 = (cj + 1) * GRID + ci + 1
188 let dd: i64 = (cj + 1) * GRID + ci
189 if vok[a] == 1 { if vok[b] == 1 { if vok[c] == 1 { if vok[dd] == 1 {
190 let col1: i64 = tri_color(vh[a], vh[b], vh[c])
191 fill_flat(fb, zb, vx[a], vy[a], vd[a], vx[b], vy[b], vd[b], vx[c], vy[c], vd[c], col1)
192 let col2: i64 = tri_color(vh[a], vh[c], vh[dd])
193 fill_flat(fb, zb, vx[a], vy[a], vd[a], vx[c], vy[c], vd[c], vx[dd], vy[dd], vd[dd], col2)
194 } } } }
195 ci = ci + 1
196 }
197 cj = cj + 1
198 }
199 write_bmp(fb, "web_assets/_game_build/mesh_terrain.bmp" as *u8)
200 if fb[(H / 2) * W + (W / 2)] == sky { return 1 }
201 return 0
202}