code wiki / _hdl_build / nx_f32_voxel_gate.nx
nx_f32_voxel_gate.nx source
↩ module page · 260 lines · 10087 B
1// nx_f32_voxel_gate.nx -- R4b/R4c: a PERSPECTIVE, TEXTURED, Z-BUFFERED procedural voxel LANDSCAPE on the
2// sovereign hardware-f32 software path (NishiLang -> nx_cc_sovereign(f32) -> nxasm -> native ELF; NO gcc/GPU/
3// wasm/VM). Value-noise terrain, neighbour face-culling, height biomes (water/grass/stone/snow), directional
4// shade, f32 mat4 camera + f32 perspective divide, per-pixel z-buffer, 24-bit BMP out. exit 0 = rendered.
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_f32_hw.nx"
8
9const W: i64 = 320
10const H: i64 = 240
11const HW: i64 = 160
12const HH: i64 = 120
13const N: i64 = 14
14const TN: i64 = 4
15const FOCAL: i64 = 300
16const PUSH: i64 = 24
17const ZFAR: i64 = 1073741824
18
19func imin(a: i64, b: i64) -> i64 { if a < b { return a } return b }
20func imax(a: i64, b: i64) -> i64 { if a > b { return a } return b }
21func min3(a: i64, b: i64, c: i64) -> i64 { return imin(a, imin(b, c)) }
22func max3(a: i64, b: i64, c: i64) -> i64 { return imax(a, imax(b, c)) }
23func iabs(a: i64) -> i64 { if a < 0 { return 0 - a } return a }
24func clamp255(v: i64) -> i64 { if v < 0 { return 0 } if v > 255 { return 255 } return v }
25func same_sign3(a: i64, b: i64, c: i64) -> i64 {
26 if a >= 0 { if b >= 0 { if c >= 0 { return 1 } } }
27 if a <= 0 { if b <= 0 { if c <= 0 { return 1 } } }
28 return 0
29}
30func fratio(num: i64, den: i64) -> i64 { return f32_div(f32_of(num), f32_of(den)) }
31
32// ---- procedural terrain: bilinear value noise, smooth rolling hills 1..6 ----
33func lat(ix: i64, iz: i64) -> i64 { var n: i64 = (ix * 71 + iz * 191 + ix * iz * 7) % 6; if n < 0 { n = n + 6 } return n }
34func terrain_h(gx: i64, gz: i64) -> i64 {
35 let L: i64 = 4
36 let bx: i64 = gx + 200
37 let bz: i64 = gz + 200
38 let ix: i64 = bx / L
39 let iz: i64 = bz / L
40 let fx: i64 = (bx % L) * 256 / L
41 let fz: i64 = (bz % L) * 256 / L
42 let h00: i64 = lat(ix, iz)
43 let h10: i64 = lat(ix + 1, iz)
44 let h01: i64 = lat(ix, iz + 1)
45 let h11: i64 = lat(ix + 1, iz + 1)
46 let a: i64 = h00 * 256 + (h10 - h00) * fx
47 let b: i64 = h01 * 256 + (h11 - h01) * fx
48 var h: i64 = (a * 256 + (b - a) * fz) / 65536 + 1
49 if h < 1 { h = 1 }
50 if h > 6 { h = 6 }
51 return h
52}
53// neighbour height, but out-of-grid = 0 so the world's outer walls always draw.
54func hN(gx: i64, gz: i64) -> i64 {
55 if gx < 0 { return 0 }
56 if gz < 0 { return 0 }
57 if gx >= N { return 0 }
58 if gz >= N { return 0 }
59 return terrain_h(gx, gz)
60}
61// top-face biome colour by height, packed R+G*256+B*65536.
62func top_color(h: i64) -> i64 {
63 if h <= 1 { return 64 + 116 * 256 + 204 * 65536 } // water
64 if h >= 6 { return 236 + 238 * 256 + 245 * 65536 } // snow
65 if h == 5 { return 122 + 118 * 256 + 112 * 65536 } // rock
66 return 96 + 172 * 256 + 76 * 65536 // grass
67}
68func side_color(h: i64) -> i64 {
69 if h <= 1 { return 58 + 104 * 256 + 188 * 65536 } // water
70 if h >= 5 { return 110 + 106 * 256 + 100 * 65536 } // stone
71 return 134 + 98 * 256 + 60 * 65536 // dirt
72}
73
74func texel_col(baseR: i64, baseG: i64, baseB: i64, u: i64, v: i64, bright: i64) -> i64 {
75 let n: i64 = ((u * 7 + v * 13 + u * v * 5) % 7) - 3
76 let r: i64 = clamp255(baseR + n * 8)
77 let g: i64 = clamp255(baseG + n * 8)
78 let b: i64 = clamp255(baseB + n * 8)
79 return ((r * bright) / 255) + ((g * bright) / 255) * 256 + ((b * bright) / 255) * 65536
80}
81
82func fill_ztex(fb: *i64, zb: *i64, t: *i64, br: i64, bg: i64, bb: i64, bright: i64) -> i64 {
83 let x0: i64 = t[0]; let y0: i64 = t[1]; let d0: i64 = t[2]; let u0: i64 = t[3]; let v0: i64 = t[4]
84 let x1: i64 = t[5]; let y1: i64 = t[6]; let d1: i64 = t[7]; let u1: i64 = t[8]; let v1: i64 = t[9]
85 let x2: i64 = t[10]; let y2: i64 = t[11]; let d2: i64 = t[12]; let u2: i64 = t[13]; let v2: i64 = t[14]
86 let area: i64 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)
87 if area == 0 { return 0 }
88 let minx: i64 = imax(0, min3(x0, x1, x2))
89 let maxx: i64 = imin(W - 1, max3(x0, x1, x2))
90 let miny: i64 = imax(0, min3(y0, y1, y2))
91 let maxy: i64 = imin(H - 1, max3(y0, y1, y2))
92 var py: i64 = miny
93 while py <= maxy {
94 var px: i64 = minx
95 while px <= maxx {
96 let e0: i64 = (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1)
97 let e1: i64 = (x0 - x2) * (py - y2) - (y0 - y2) * (px - x2)
98 let e2: i64 = (x1 - x0) * (py - y0) - (y1 - y0) * (px - x0)
99 if same_sign3(e0, e1, e2) == 1 {
100 let d: i64 = (e0 * d0 + e1 * d1 + e2 * d2) / area
101 let idx: i64 = py * W + px
102 if d < zb[idx] {
103 zb[idx] = d
104 let u: i64 = (e0 * u0 + e1 * u1 + e2 * u2) / area
105 let v: i64 = (e0 * v0 + e1 * v1 + e2 * v2) / area
106 fb[idx] = texel_col(br, bg, bb, u, v, bright)
107 }
108 }
109 px = px + 1
110 }
111 py = py + 1
112 }
113 return 0
114}
115
116func project(R: *i64, iwx: i64, iwy: i64, iwz: i64, out: *i64) -> i64 {
117 let v: *i64 = sys_mmap(32) as *i64
118 let r: *i64 = sys_mmap(32) as *i64
119 v[0] = f32_sub(f32_of(iwx), fratio(N, 2))
120 v[1] = f32_sub(f32_of(iwy), f32_of(2))
121 v[2] = f32_sub(f32_of(iwz), fratio(N, 2))
122 v[3] = f32_of(0)
123 m4_vec4(R, v, r)
124 let pz: i64 = f32_add(r[2], f32_of(PUSH))
125 if (pz & 0x80000000) != 0 { out[3] = 0; return 0 }
126 let zi: i64 = f32_int(f32_mul(pz, f32_of(64)))
127 if zi <= 0 { out[3] = 0; return 0 }
128 out[0] = HW + f32_int(f32_div(f32_mul(r[0], f32_of(FOCAL)), pz))
129 out[1] = HH - f32_int(f32_div(f32_mul(r[1], f32_of(FOCAL)), pz))
130 out[2] = zi
131 out[3] = 1
132 return 0
133}
134// directional light from above-front (world space): brightness from the LOCAL normal . light (integer).
135func shade_local(lnx: i64, lny: i64, lnz: i64) -> i64 {
136 var d: i64 = lnx * 1 + lny * 3 + lnz * 2
137 if d < 0 { d = 0 }
138 return clamp255(64 + d * 50)
139}
140
141func draw_face(fb: *i64, zb: *i64, sx: *i64, sy: *i64, sd: *i64, sok: *i64,
142 a: i64, b: i64, c: i64, dd: i64, lnx: i64, lny: i64, lnz: i64, col: i64) -> i64 {
143 if sok[a] == 0 { return 0 }
144 if sok[b] == 0 { return 0 }
145 if sok[c] == 0 { return 0 }
146 if sok[dd] == 0 { return 0 }
147 let bright: i64 = shade_local(lnx, lny, lnz)
148 let br: i64 = col & 0xff
149 let bg: i64 = (col >> 8) & 0xff
150 let bb: i64 = (col >> 16) & 0xff
151 let t: *i64 = sys_mmap(15 * 8) as *i64
152 t[0] = sx[a]; t[1] = sy[a]; t[2] = sd[a]; t[3] = 0; t[4] = 0
153 t[5] = sx[b]; t[6] = sy[b]; t[7] = sd[b]; t[8] = TN; t[9] = 0
154 t[10] = sx[c]; t[11] = sy[c]; t[12] = sd[c]; t[13] = TN; t[14] = TN
155 fill_ztex(fb, zb, t, br, bg, bb, bright)
156 t[0] = sx[a]; t[1] = sy[a]; t[2] = sd[a]; t[3] = 0; t[4] = 0
157 t[5] = sx[c]; t[6] = sy[c]; t[7] = sd[c]; t[8] = TN; t[9] = TN
158 t[10] = sx[dd]; t[11] = sy[dd]; t[12] = sd[dd]; t[13] = 0; t[14] = TN
159 fill_ztex(fb, zb, t, br, bg, bb, bright)
160 return 0
161}
162// draw a terrain column with neighbour face-culling + biome colours.
163func draw_col(fb: *i64, zb: *i64, R: *i64, gx: i64, gz: i64) -> i64 {
164 let h: i64 = terrain_h(gx, gz)
165 let sx: *i64 = sys_mmap(8 * 8) as *i64
166 let sy: *i64 = sys_mmap(8 * 8) as *i64
167 let sd: *i64 = sys_mmap(8 * 8) as *i64
168 let sok: *i64 = sys_mmap(8 * 8) as *i64
169 var k: i64 = 0
170 while k < 8 {
171 let dx: i64 = k & 1
172 let dy: i64 = (k >> 1) & 1
173 let dz: i64 = (k >> 2) & 1
174 let o: *i64 = sys_mmap(32) as *i64
175 project(R, gx + dx, dy * h, gz + dz, o)
176 sx[k] = o[0]; sy[k] = o[1]; sd[k] = o[2]; sok[k] = o[3]
177 k = k + 1
178 }
179 let tc: i64 = top_color(h)
180 let sc: i64 = side_color(h)
181 draw_face(fb, zb, sx, sy, sd, sok, 2, 3, 7, 6, 0, 1, 0, tc) // top
182 if hN(gx + 1, gz) < h { draw_face(fb, zb, sx, sy, sd, sok, 1, 5, 7, 3, 1, 0, 0, sc) }
183 if hN(gx - 1, gz) < h { draw_face(fb, zb, sx, sy, sd, sok, 0, 4, 6, 2, 0 - 1, 0, 0, sc) }
184 if hN(gx, gz + 1) < h { draw_face(fb, zb, sx, sy, sd, sok, 4, 5, 7, 6, 0, 0, 1, sc) }
185 if hN(gx, gz - 1) < h { draw_face(fb, zb, sx, sy, sd, sok, 0, 1, 3, 2, 0, 0, 0 - 1, sc) }
186 return 0
187}
188
189func put_u32le(b: *u8, o: i64, v: i64) -> i64 {
190 b[o] = (v & 0xff) as u8
191 b[o + 1] = ((v >> 8) & 0xff) as u8
192 b[o + 2] = ((v >> 16) & 0xff) as u8
193 b[o + 3] = ((v >> 24) & 0xff) as u8
194 return 0
195}
196func write_bmp(fb: *i64, path: *u8) -> i64 {
197 let pix: i64 = W * H * 3
198 let total: i64 = 54 + pix
199 let b: *u8 = sys_mmap(total) as *u8
200 b[0] = 66 as u8
201 b[1] = 77 as u8
202 put_u32le(b, 2, total)
203 put_u32le(b, 10, 54)
204 put_u32le(b, 14, 40)
205 put_u32le(b, 18, W)
206 put_u32le(b, 22, H)
207 b[26] = 1 as u8
208 b[28] = 24 as u8
209 put_u32le(b, 34, pix)
210 var o: i64 = 54
211 var ry: i64 = 0
212 while ry < H {
213 let syrow: i64 = H - 1 - ry
214 var x: i64 = 0
215 while x < W {
216 let c: i64 = fb[syrow * W + x]
217 b[o] = ((c >> 16) & 0xff) as u8
218 b[o + 1] = ((c >> 8) & 0xff) as u8
219 b[o + 2] = (c & 0xff) as u8
220 o = o + 3
221 x = x + 1
222 }
223 ry = ry + 1
224 }
225 let fd: i64 = sys_openat_wr(path, 0x1a4)
226 if fd < 0 { return 0 - 1 }
227 sys_write(fd, b, total)
228 sys_close(fd)
229 return 0
230}
231
232func main() -> i64 {
233 let fb: *i64 = sys_mmap(W * H * 8) as *i64
234 let zb: *i64 = sys_mmap(W * H * 8) as *i64
235 let sky: i64 = 142 + 186 * 256 + 228 * 65536
236 var i: i64 = 0
237 while i < W * H {
238 fb[i] = sky
239 zb[i] = ZFAR
240 i = i + 1
241 }
242 let Ry: *i64 = sys_mmap(128) as *i64
243 let Rx: *i64 = sys_mmap(128) as *i64
244 let R: *i64 = sys_mmap(128) as *i64
245 m4_roty(fratio(819, 1000), fratio(574, 1000), Ry)
246 m4_rotx(fratio(819, 1000), f32_neg(fratio(574, 1000)), Rx) // ~35deg pitch down -> look over the landscape
247 m4_mul(Rx, Ry, R)
248
249 var gz: i64 = 0
250 while gz < N {
251 var gx: i64 = 0
252 while gx < N {
253 draw_col(fb, zb, R, gx, gz)
254 gx = gx + 1
255 }
256 gz = gz + 1
257 }
258 write_bmp(fb, "web_assets/_game_build/f32voxel.bmp" as *u8)
259 return 0
260}