nx_wasmvoxel.nx source
↩ module page · 222 lines · 8713 B
1// nx_wasmvoxel.nx -- a VOXEL TERRAIN in the WASM/BROWSER memory model: an 8x8 heightmap (two procedural hills,
2// height-banded grass/rock/snow) of f32-projected voxel columns, painter-sorted far->near, integer-filled to a
3// fixed-offset framebuffer. Same sovereign chain as the cube (nx_compile_wat -> nx_wat_compiler -> .wasm). JS owns
4// trig + blits the fb to <canvas>; wasm owns the world + the hardware f32 math. license_tier: ORIGINAL
5import "nx_f32_hw.nx"
6const O_MAGIC_65536: i64 = 65536
7const O_MAGIC_16777216: i64 = 16777216
8
9const W: i64 = 128
10const H: i64 = 128
11const HWc: i64 = 64
12const HHc: i64 = 64
13const SCALE: i64 = 9
14const N: i64 = 8
15const O_FB: i64 = 0
16const O_RY: i64 = 131072
17const O_RX: i64 = 131200
18const O_R: i64 = 131328
19const O_VTMP: i64 = 131456
20const O_OTMP: i64 = 131488
21const O_SX: i64 = 131520
22const O_SY: i64 = 131584
23const O_CIDX: i64 = 131648
24const O_CZ: i64 = 132160
25const O_CX: i64 = 132672
26const O_CZW: i64 = 133184
27const O_CH: i64 = 133696
28const O_FACES: i64 = 134208
29
30func imin(a: i64, b: i64) -> i64 { if a < b { return a } return b }
31func imax(a: i64, b: i64) -> i64 { if a > b { return a } return b }
32func min3(a: i64, b: i64, c: i64) -> i64 { return imin(a, imin(b, c)) }
33func max3(a: i64, b: i64, c: i64) -> i64 { return imax(a, imax(b, c)) }
34func same_sign3(a: i64, b: i64, c: i64) -> i64 {
35 if a >= 0 { if b >= 0 { if c >= 0 { return 1 } } }
36 if a <= 0 { if b <= 0 { if c <= 0 { return 1 } } }
37 return 0
38}
39func fbp() -> *i64 { return (O_FB as i64) as *i64 }
40func fratio(num: i64, den: i64) -> i64 { return f32_div(f32_of(num), f32_of(den)) }
41func fill_tri(x0: i64, y0: i64, x1: i64, y1: i64, x2: i64, y2: i64, col: i64) -> i64 {
42 let fb: *i64 = fbp()
43 let area: i64 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)
44 if area == 0 { return 0 }
45 let minx: i64 = imax(0, min3(x0, x1, x2))
46 let maxx: i64 = imin(W - 1, max3(x0, x1, x2))
47 let miny: i64 = imax(0, min3(y0, y1, y2))
48 let maxy: i64 = imin(H - 1, max3(y0, y1, y2))
49 var py: i64 = miny
50 while py <= maxy {
51 var px: i64 = minx
52 while px <= maxx {
53 let e0: i64 = (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1)
54 let e1: i64 = (x0 - x2) * (py - y2) - (y0 - y2) * (px - x2)
55 let e2: i64 = (x1 - x0) * (py - y0) - (y1 - y0) * (px - x0)
56 if same_sign3(e0, e1, e2) == 1 { fb[py * W + px] = col }
57 px = px + 1
58 }
59 py = py + 1
60 }
61 return 0
62}
63func rot_nz(R: *i64, lx: i64, ly: i64, lz: i64) -> i64 {
64 let v: *i64 = (O_VTMP as i64) as *i64
65 let o: *i64 = (O_OTMP as i64) as *i64
66 v[0] = lx; v[1] = ly; v[2] = lz; v[3] = f32_of(0)
67 m4_vec4(R, v, o)
68 return o[2]
69}
70func shade(nz: i64) -> i64 {
71 var z: i64 = nz
72 if (z & 0x80000000) != 0 { z = f32_of(0) }
73 return 50 + f32_int(f32_mul(z, f32_of(190)))
74}
75func packc(r: i64, g: i64, b: i64, br: i64) -> i64 {
76 let rr: i64 = (r * br) / 255
77 let gg: i64 = (g * br) / 255
78 let bb: i64 = (b * br) / 255
79 return rr + gg * 256 + bb * O_MAGIC_65536 + 255 * O_MAGIC_16777216
80}
81// procedural terrain: two mounds, integer math, height 1..5
82func hcol(gx: i64, gz: i64) -> i64 {
83 let d1: i64 = (gx - 3) * (gx - 3) + (gz - 3) * (gz - 3)
84 var h: i64 = 5 - d1 / 3
85 let d2: i64 = (gx - 6) * (gx - 6) + (gz - 5) * (gz - 5)
86 let h2: i64 = 4 - d2 / 3
87 if h2 > h { h = h2 }
88 if h < 1 { h = 1 }
89 return h
90}
91// rotated normal-z for a face index (top/+X/-X/+Z/-Z)
92func face_nz(R: *i64, fidx: i64) -> i64 {
93 if fidx == 0 { return rot_nz(R, f32_of(0), f32_of(1), f32_of(0)) }
94 if fidx == 1 { return rot_nz(R, f32_of(1), f32_of(0), f32_of(0)) }
95 if fidx == 2 { return rot_nz(R, f32_neg(f32_of(1)), f32_of(0), f32_of(0)) }
96 if fidx == 3 { return rot_nz(R, f32_of(0), f32_of(0), f32_of(1)) }
97 return rot_nz(R, f32_of(0), f32_of(0), f32_neg(f32_of(1)))
98}
99// base rgb (packed r|g<<8|b<<16) by height band; top=grass/rock/snow, side=dirt/rock/snow-shadow
100func basergb(hh: i64, top: i64) -> i64 {
101 if top == 1 {
102 if hh >= 4 { return 210 + 220 * 256 + 235 * O_MAGIC_65536 }
103 if hh == 3 { return 118 + 120 * 256 + 130 * O_MAGIC_65536 }
104 return 74 + 150 * 256 + 70 * O_MAGIC_65536
105 }
106 if hh >= 4 { return 140 + 150 * 256 + 170 * O_MAGIC_65536 }
107 if hh == 3 { return 80 + 82 * 256 + 92 * O_MAGIC_65536 }
108 return 104 + 80 * 256 + 52 * O_MAGIC_65536
109}
110// render the world. ycos/ysin = spin, xcos/xsin = tilt (cos/sin * 1000; JS owns trig).
111func render(ycos: i64, ysin: i64, xcos: i64, xsin: i64) -> i64 {
112 let fb: *i64 = fbp()
113 let bg: i64 = 120 + 150 * 256 + 200 * O_MAGIC_65536 + 255 * O_MAGIC_16777216
114 var i: i64 = 0
115 while i < W * H { fb[i] = bg; i = i + 1 }
116 let Ry: *i64 = (O_RY as i64) as *i64
117 let Rx: *i64 = (O_RX as i64) as *i64
118 let R: *i64 = (O_R as i64) as *i64
119 m4_roty(fratio(ycos, 1000), fratio(ysin, 1000), Ry)
120 m4_rotx(fratio(xcos, 1000), fratio(xsin, 1000), Rx)
121 m4_mul(Rx, Ry, R)
122 let FA: *i64 = (O_FACES as i64) as *i64
123 FA[0] = 2; FA[1] = 3; FA[2] = 7; FA[3] = 6
124 FA[4] = 1; FA[5] = 5; FA[6] = 7; FA[7] = 3
125 FA[8] = 0; FA[9] = 2; FA[10] = 6; FA[11] = 4
126 FA[12] = 4; FA[13] = 5; FA[14] = 7; FA[15] = 6
127 FA[16] = 0; FA[17] = 1; FA[18] = 3; FA[19] = 2
128 let fhalf: i64 = f32_div(f32_of(1), f32_of(2))
129 let fnhalf: i64 = f32_neg(fhalf)
130 let fcy: i64 = f32_div(f32_of(5), f32_of(2))
131 let fc35: i64 = f32_div(f32_of(7), f32_of(2))
132 let fsc: i64 = f32_of(SCALE)
133 let CIDX: *i64 = (O_CIDX as i64) as *i64
134 let CZ: *i64 = (O_CZ as i64) as *i64
135 let CX: *i64 = (O_CX as i64) as *i64
136 let CZW: *i64 = (O_CZW as i64) as *i64
137 let CH: *i64 = (O_CH as i64) as *i64
138 let v: *i64 = (O_VTMP as i64) as *i64
139 let o: *i64 = (O_OTMP as i64) as *i64
140 // pass 1: column world coords, height, center depth
141 var gz: i64 = 0
142 while gz < N {
143 var gx: i64 = 0
144 while gx < N {
145 let ci: i64 = gz * N + gx
146 let wx: i64 = f32_sub(f32_of(gx), fc35)
147 let wz: i64 = f32_sub(f32_of(gz), fc35)
148 let hh: i64 = hcol(gx, gz)
149 CX[ci] = wx; CZW[ci] = wz; CH[ci] = hh; CIDX[ci] = ci
150 v[0] = wx; v[1] = f32_neg(fcy); v[2] = wz; v[3] = f32_of(0)
151 m4_vec4(R, v, o)
152 CZ[ci] = f32_int(f32_mul(o[2], f32_of(1000)))
153 gx = gx + 1
154 }
155 gz = gz + 1
156 }
157 let ncol: i64 = N * N
158 // pass 2: selection-sort column indices by depth ascending (far first)
159 var p: i64 = 0
160 while p < ncol {
161 var mn: i64 = p
162 var q: i64 = p + 1
163 while q < ncol {
164 if CZ[CIDX[q]] < CZ[CIDX[mn]] { mn = q }
165 q = q + 1
166 }
167 let t: i64 = CIDX[p]; CIDX[p] = CIDX[mn]; CIDX[mn] = t
168 p = p + 1
169 }
170 let SX: *i64 = (O_SX as i64) as *i64
171 let SY: *i64 = (O_SY as i64) as *i64
172 // pass 3: draw far->near, each column's 8 corners projected then 5 faces (sides, then top last)
173 var dp: i64 = 0
174 while dp < ncol {
175 let ci: i64 = CIDX[dp]
176 let wx: i64 = CX[ci]
177 let wz: i64 = CZW[ci]
178 let hh: i64 = CH[ci]
179 var k: i64 = 0
180 while k < 8 {
181 let bx: i64 = k & 1
182 let by: i64 = (k >> 1) & 1
183 let bz: i64 = (k >> 2) & 1
184 var lx: i64 = f32_add(wx, fnhalf)
185 if bx == 1 { lx = f32_add(wx, fhalf) }
186 var ya: i64 = f32_neg(fcy)
187 if by == 1 { ya = f32_sub(f32_of(hh), fcy) }
188 var lz: i64 = f32_add(wz, fnhalf)
189 if bz == 1 { lz = f32_add(wz, fhalf) }
190 v[0] = lx; v[1] = ya; v[2] = lz; v[3] = f32_of(0)
191 m4_vec4(R, v, o)
192 SX[k] = HWc + f32_int(f32_mul(o[0], fsc))
193 SY[k] = HHc - f32_int(f32_mul(o[1], fsc))
194 k = k + 1
195 }
196 var fo: i64 = 0
197 while fo < 5 {
198 var fidx: i64 = fo + 1
199 if fo == 4 { fidx = 0 }
200 let nz: i64 = face_nz(R, fidx)
201 let br: i64 = shade(nz)
202 var top: i64 = 0
203 if fidx == 0 { top = 1 }
204 let rgb: i64 = basergb(hh, top)
205 let col: i64 = packc(rgb & 255, (rgb >> 8) & 255, (rgb >> 16) & 255, br)
206 let fb4: i64 = fidx * 4
207 let i0: i64 = FA[fb4]
208 let i1: i64 = FA[fb4 + 1]
209 let i2: i64 = FA[fb4 + 2]
210 let i3: i64 = FA[fb4 + 3]
211 fill_tri(SX[i0], SY[i0], SX[i1], SY[i1], SX[i2], SY[i2], col)
212 fill_tri(SX[i0], SY[i0], SX[i2], SY[i2], SX[i3], SY[i3], col)
213 fo = fo + 1
214 }
215 dp = dp + 1
216 }
217 return 0
218}
219func fb_off() -> i64 { return O_FB }
220func ww() -> i64 { return W }
221func hh() -> i64 { return H }
222func main() -> i64 { return 0 }