nx_pets_voxel3d.nx source
↩ module page · 1683 lines · 89099 B
1// nx_pets_voxel3d.nx -- RUNG 2 of the Pixelmon-exceed ladder: TRUE 3D voxel terrain by composing
2// the team's CERTIFIED 3D math + z-buffered rasterizer. Replaces the rung-1 2.5D raycaster columns
3// with real perspective-projected, depth-occluded, directionally-lit cubes (Minecraft-class).
4//
5// Composes (parts-composition doctrine -- I author ONLY the mesh/scene glue, not the math/raster):
6// nx_camera_q14 -- certified Q14 camera: perspective + yaw/pitch + mat4 mul + mat4*vec4.
7// nx_raster_triangle -- certified z-buffered Pineda rasterizer; u32 RGBA framebuffer that is
8// EXPLICITLY browser-ready (JS reads Uint32Array -> putImageData). The
9// alpha channel is an ambient/light multiplier -> per-face directional light.
10// (nx_voxel_mesh is still ABSENT per MINECRAFTCLONE_BITS_UP_GAPS Gap 8, so the cube-face meshing
11// here is the composition glue = tutor tooling, per the game-spec authorship law.)
12//
13// Pipeline (the real hardware-up render path): world cube corner (Q14) -> MVP = P*V (certified
14// mat4) -> clip (certified mat4*vec4) -> perspective divide -> integer screen x,y + depth w ->
15// certified z-buffered triangle raster -> u32 framebuffer -> 24bpp BMP (last-mile image; the same
16// framebuffer feeds the browser canvas in the delivery rung).
17//
18// BAKED gate (camera-independent correctness, no false green): z-occlusion (near tri hides far) +
19// projection (in-front projects centered, behind-camera is rejected, off-axis projects off-centre) +
20// scene-non-empty. Browser delivery of this exact framebuffer = the NAMED next rung.
21// license_tier: ORIGINAL
22import "nx_camera_q14.nx"
23import "nx_raster_triangle.nx"
24import "nx_perlin.nx"
25import "nx_syscalls.nx"
26const NX_MAGIC_65536: i64 = 65536
27const NX_MAGIC_16777216: i64 = 16777216
28const NX_MAGIC_4096: i64 = 4096
29const NX_MAGIC_1024: i64 = 1024
30const NX_MAGIC_2048: i64 = 2048
31const NX_MAGIC_2147483647: i64 = 2147483647
32const NX_MAGIC_1103515: i64 = 1103515
33const NX_MAGIC_12345: i64 = 12345
34const NX_MAGIC_5000: i64 = 5000
35const NX_MAGIC_100000: i64 = 100000
36const NX_MAGIC_10000: i64 = 10000
37const NX_MAGIC_8000: i64 = 8000
38const NX_MAGIC_320000: i64 = 320000
39const NX_MAGIC_2835: i64 = 2835
40const NX_MAGIC_1337: i64 = 1337
41const NX_MAGIC_7777: i64 = 7777
42const NX_MAGIC_909090: i64 = 909090
43const NX_MAGIC_424242: i64 = 424242
44const NX_MAGIC_515151: i64 = 515151
45const NX_MAGIC_31337: i64 = 31337
46const NX_MAGIC_4000: i64 = 4000
47const NX_MAGIC_110000: i64 = 110000
48const NX_MAGIC_16384: i64 = 16384
49
50const NX_W: nx_int = 320
51const NX_H: nx_int = 240
52const NX_W2: nx_int = 160
53const NX_H2: nx_int = 120
54const NX_VQ: nx_int = 16384
55
56func vp_pack(r: nx_int, g: nx_int, b: nx_int, a: nx_int) -> nx_int {
57 return r + g * 256 + b * NX_MAGIC_65536 + a * NX_MAGIC_16777216
58}
59
60// project a world corner (integer voxel coords) through the MVP; write [sx, sy, depth, ok] into scr
61func vp_project(mvp: *i64, wx: nx_int, wy: nx_int, wz: nx_int, scr: *i64) -> nx_int {
62 let v: *i64 = (sys_mmap(32)) as *i64
63 let clip: *i64 = (sys_mmap(32)) as *i64
64 v[0] = wx * NX_VQ
65 v[1] = wy * NX_VQ
66 v[2] = wz * NX_VQ
67 v[3] = NX_VQ
68 nx_camera_mat4_vec4(mvp, v, clip)
69 let w: nx_int = clip[3]
70 if w < NX_MAGIC_4096 { scr[3] = 0; return 0 } // at/behind near plane -> reject (no div)
71 scr[0] = NX_W2 + (clip[0] * NX_W2) / w
72 scr[1] = NX_H2 - (clip[1] * NX_H2) / w
73 scr[2] = w // depth (Q14, smaller = closer)
74 scr[3] = 1
75 return 1
76}
77
78func vp_tri(fb: *i64, zb: *i64, a: *i64, b: *i64, c: *i64, col: nx_int) -> nx_int {
79 if a[3] == 0 { return 0 }
80 if b[3] == 0 { return 0 }
81 if c[3] == 0 { return 0 }
82 let tri: *i64 = (sys_mmap(96)) as *i64
83 tri[0] = a[0]; tri[1] = a[1]; tri[2] = a[2]; tri[3] = col
84 tri[4] = b[0]; tri[5] = b[1]; tri[6] = b[2]; tri[7] = col
85 tri[8] = c[0]; tri[9] = c[1]; tri[10] = c[2]; tri[11] = col
86 nx_raster_triangle(fb, zb, NX_W, NX_H, tri)
87 return 0
88}
89
90// p holds 8 projected corners (4 i64 each). Draw quad (corner indices a,b,c,d) as 2 triangles.
91func vp_quad(fb: *i64, zb: *i64, p: *i64, a: nx_int, b: nx_int, c: nx_int, d: nx_int, col: nx_int) -> nx_int {
92 let pa: *i64 = ((p as i64) + a * 32) as *i64
93 let pb: *i64 = ((p as i64) + b * 32) as *i64
94 let pc: *i64 = ((p as i64) + c * 32) as *i64
95 let pd: *i64 = ((p as i64) + d * 32) as *i64
96 vp_tri(fb, zb, pa, pb, pc, col)
97 vp_tri(fb, zb, pa, pc, pd, col)
98 return 0
99}
100
101// procedural terrain height at (x,z): seeded Perlin noise (composes nx_perlin), mapped to 1..6
102func vp_h(ps: *PerlinState, x: nx_int, z: nx_int) -> nx_int {
103 let n: nx_int = nx_perlin_2d(ps, x * 340, z * 340) // Q10 gradient noise, ~[-724,724]
104 var h: nx_int = 1 + (n + NX_MAGIC_1024) * 5 / NX_MAGIC_2048
105 if h < 1 { h = 1 }
106 if h > 6 { h = 6 }
107 return h
108}
109// biome at (x,z): a SECOND Perlin field (warmth/rockiness) + height -> biome id.
110// 0 grass-plains 1 desert 2 stone/rock 3 snow-cap 4 water
111func vp_biome(bps: *PerlinState, h: nx_int, x: nx_int, z: nx_int) -> nx_int {
112 if h <= 1 { return 4 } // water fills the low basins
113 if h >= 5 { return 3 } // snow on the peaks
114 let bn: nx_int = nx_perlin_2d(bps, x * 300, z * 300)
115 if bn > 250 { return 1 } // warm/dry -> desert
116 if bn < 0 - 250 { return 2 } // rocky -> stone
117 return 0 // temperate -> grass
118}
119
120// ---- ambient occlusion (composes nx_raster_triangle's alpha-as-light: per-corner alpha ->
121// barycentric-interpolated brightness = smooth contact shadows) ----
122func hget(hgrid: *i64, gn: nx_int, x: nx_int, z: nx_int) -> nx_int {
123 if x < 0 { return 0 }
124 if z < 0 { return 0 }
125 if x >= gn { return 0 }
126 if z >= gn { return 0 }
127 return hgrid[x * gn + z]
128}
129// AO darkening: more occluding neighbours -> lower alpha (the raster dims RGB by alpha/255)
130func vp_ao(occ: nx_int) -> nx_int {
131 if occ <= 0 { return 255 }
132 if occ == 1 { return 200 }
133 if occ == 2 { return 160 }
134 return 130
135}
136// count, of the 3 neighbour columns touching a top-face corner, how many are TALLER (occlude it)
137func vp_occ(hgrid: *i64, gn: nx_int, h: nx_int,
138 ax: nx_int, az: nx_int, bx: nx_int, bz: nx_int, cx: nx_int, cz: nx_int) -> nx_int {
139 var n: nx_int = 0
140 if hget(hgrid, gn, ax, az) > h { n = n + 1 }
141 if hget(hgrid, gn, bx, bz) > h { n = n + 1 }
142 if hget(hgrid, gn, cx, cz) > h { n = n + 1 }
143 return n
144}
145// ---- AABB-vs-voxel collision (the player-physics floor): the resting top under an AABB
146// footprint = the TALLEST column it covers, so a body can't sink through terrain ----
147func vp_aabb_floor(hgrid: *i64, gn: nx_int, x0: nx_int, z0: nx_int, x1: nx_int, z1: nx_int) -> nx_int {
148 var top: nx_int = 0
149 var z: nx_int = z0
150 while z <= z1 {
151 var x: nx_int = x0
152 while x <= x1 {
153 let h: nx_int = hget(hgrid, gn, x, z)
154 if h > top { top = h }
155 x = x + 1
156 }
157 z = z + 1
158 }
159 return top
160}
161func vp_collide_y(hgrid: *i64, gn: nx_int, x0: nx_int, z0: nx_int, x1: nx_int, z1: nx_int, y: nx_int) -> nx_int {
162 let f: nx_int = vp_aabb_floor(hgrid, gn, x0, z0, x1, z1)
163 if y < f { return f } // resolved up onto the surface
164 return y
165}
166// ---- infinite chunk streaming: any 8x8 chunk generated on demand from its (cx,cz); chunks are
167// windows into ONE global Perlin field, so neighbours are seamless by construction ----
168func vp_gen_chunk(ps: *PerlinState, cx: nx_int, cz: nx_int, out: *i64) -> nx_int {
169 var lz: nx_int = 0
170 while lz < 8 {
171 var lx: nx_int = 0
172 while lx < 8 { out[lx * 8 + lz] = vp_h(ps, cx * 8 + lx, cz * 8 + lz); lx = lx + 1 }
173 lz = lz + 1
174 }
175 return 0
176}
177// ---- interactive world edit: place adds a block on top, break removes one (bounded 1..6) ----
178func vp_place(hgrid: *i64, gn: nx_int, x: nx_int, z: nx_int) -> nx_int {
179 let h: nx_int = hget(hgrid, gn, x, z)
180 if h >= 1 { if h < 6 { hgrid[x * gn + z] = h + 1 } }
181 return 0
182}
183func vp_break(hgrid: *i64, gn: nx_int, x: nx_int, z: nx_int) -> nx_int {
184 let h: nx_int = hget(hgrid, gn, x, z)
185 if h > 1 { hgrid[x * gn + z] = h - 1 }
186 return 0
187}
188// ---- greedy meshing: count maximal same-(height,biome) runs in a row -> fewer, larger quads.
189// gated on the triangle-reduction property (uniform run of N -> 1 quad, not N) ----
190func vp_greedy_row(hrow: *i64, brow: *i64, n: nx_int) -> nx_int {
191 if n <= 0 { return 0 }
192 var spans: nx_int = 1
193 var i: nx_int = 1
194 while i < n {
195 if hrow[i] != hrow[i - 1] { spans = spans + 1 }
196 else { if brow[i] != brow[i - 1] { spans = spans + 1 } }
197 i = i + 1
198 }
199 return spans
200}
201// ---- structure placement: anchor where the structure Perlin field is a strict local max on land.
202// strict-local-max => naturally spaced (no two adjacent cells can both be strict maxima) ----
203func vp_struct_at(sps: *PerlinState, hgrid: *i64, gn: nx_int, x: nx_int, z: nx_int) -> nx_int {
204 if hget(hgrid, gn, x, z) < 2 { return 0 } // not in water
205 let c: nx_int = nx_perlin_2d(sps, x * 220, z * 220)
206 if c <= nx_perlin_2d(sps, (x - 1) * 220, z * 220) { return 0 }
207 if c <= nx_perlin_2d(sps, (x + 1) * 220, z * 220) { return 0 }
208 if c <= nx_perlin_2d(sps, x * 220, (z - 1) * 220) { return 0 }
209 if c <= nx_perlin_2d(sps, x * 220, (z + 1) * 220) { return 0 }
210 return 1
211}
212// thermal/hydraulic erosion: each cell sheds 1 unit to every 4-neighbour it overhangs by > talus.
213// mass-conserving (every -1 has a matching +1); deltas computed from the pre-pass state then applied.
214func vp_erode_pass(hg: *i64, gn: nx_int, talus: nx_int, delta: *i64) -> nx_int {
215 var i: nx_int = 0
216 while i < gn * gn { delta[i] = 0; i = i + 1 }
217 var z: nx_int = 0
218 while z < gn {
219 var x: nx_int = 0
220 while x < gn {
221 let h: nx_int = hg[x * gn + z]
222 if x > 0 { if h - hg[(x-1)*gn+z] > talus { delta[x*gn+z] = delta[x*gn+z] - 1; delta[(x-1)*gn+z] = delta[(x-1)*gn+z] + 1 } }
223 if x < gn - 1 { if h - hg[(x+1)*gn+z] > talus { delta[x*gn+z] = delta[x*gn+z] - 1; delta[(x+1)*gn+z] = delta[(x+1)*gn+z] + 1 } }
224 if z > 0 { if h - hg[x*gn+(z-1)] > talus { delta[x*gn+z] = delta[x*gn+z] - 1; delta[x*gn+(z-1)] = delta[x*gn+(z-1)] + 1 } }
225 if z < gn - 1 { if h - hg[x*gn+(z+1)] > talus { delta[x*gn+z] = delta[x*gn+z] - 1; delta[x*gn+(z+1)] = delta[x*gn+(z+1)] + 1 } }
226 x = x + 1
227 }
228 z = z + 1
229 }
230 var j: nx_int = 0
231 while j < gn * gn { hg[j] = hg[j] + delta[j]; j = j + 1 }
232 return 0
233}
234// ore presence at (x,z,depth y): a LOW-frequency Perlin field sheared by depth -> rare clustered veins
235func vp_ore(ops: *PerlinState, x: nx_int, z: nx_int, y: nx_int) -> nx_int {
236 if nx_perlin_2d(ops, x * 120 + y * 90, z * 120) > 350 { return 1 }
237 return 0
238}
239// 3D-noise cave carving (Minecraft 3D-noise caves): a voxel (x,z,y) is carved to air where a
240// 3D-sheared Perlin field falls in a narrow band around zero -> winding connected tunnels. BOTH
241// samples are y-coupled (ore-proven x*..+y*90 scale), so the field genuinely moves with depth ->
242// caves are 3D, NOT an extruded 2D column mask.
243func vp_cave(cps: *PerlinState, x: nx_int, z: nx_int, y: nx_int) -> nx_int {
244 let a: nx_int = nx_perlin_2d(cps, x * 130 + y * 90, z * 130)
245 let b: nx_int = nx_perlin_2d(cps, z * 130 + y * 90, x * 130)
246 let f: nx_int = a + b
247 if f > 0 - 120 { if f < 120 { return 1 } }
248 return 0
249}
250// ---- wave-function-collapse procedural tiling (constraint-solved generation). 4 tiles
251// (0 grass 1 water 2 sand 3 stone); each cell carries a 4-bit mask of still-allowed tiles.
252// Collapse the lowest-entropy cell to a seeded choice, then propagate adjacency constraints
253// (arc-consistency) to stability; repeat until fully collapsed. Output obeys every adjacency. ----
254func vp_bit(t: nx_int) -> nx_int {
255 if t == 0 { return 1 }
256 if t == 1 { return 2 }
257 if t == 2 { return 4 }
258 return 8
259}
260func vp_popcount4(m: nx_int) -> nx_int {
261 var c: nx_int = 0
262 if m % 2 == 1 { c = c + 1 }
263 if (m / 2) % 2 == 1 { c = c + 1 }
264 if (m / 4) % 2 == 1 { c = c + 1 }
265 if (m / 8) % 2 == 1 { c = c + 1 }
266 return c
267}
268// adjacency rule (symmetric): sand(2) connects to everything; same tile always ok; grass(0) & stone(3)
269// may touch each other; water(1) may NOT touch grass or stone. So water/grass and water/stone are illegal.
270func vp_wfc_allow(a: nx_int, b: nx_int) -> nx_int {
271 if a == 2 { return 1 }
272 if b == 2 { return 1 }
273 if a == b { return 1 }
274 if a == 0 { if b == 3 { return 1 } return 0 }
275 if a == 3 { if b == 0 { return 1 } return 0 }
276 return 0
277}
278// arc-consistency prune: drop from dst any candidate tile b that NO remaining tile a of src supports.
279func vp_wfc_prune(mask: *i64, src: nx_int, dst: nx_int) -> nx_int {
280 var changed: nx_int = 0
281 var newmask: nx_int = 0
282 var b: nx_int = 0
283 while b < 4 {
284 if (mask[dst] / vp_bit(b)) % 2 == 1 {
285 var ok: nx_int = 0
286 var a: nx_int = 0
287 while a < 4 {
288 if (mask[src] / vp_bit(a)) % 2 == 1 { if vp_wfc_allow(a, b) == 1 { ok = 1 } }
289 a = a + 1
290 }
291 if ok == 1 { newmask = newmask + vp_bit(b) }
292 else { changed = 1 }
293 }
294 b = b + 1
295 }
296 mask[dst] = newmask
297 return changed
298}
299// solve: fills cells[0..gn*gn) with collapsed tile ids. mask is a caller-allocated scratch buffer.
300// returns 1 on full collapse with no contradiction, 0 otherwise. all loops are bounded (no-hang).
301func vp_wfc_solve(seed: nx_int, cells: *i64, mask: *i64, gn: nx_int) -> nx_int {
302 let total: nx_int = gn * gn
303 var i: nx_int = 0
304 while i < total { mask[i] = 15; i = i + 1 } // all 4 tiles allowed
305 var rng: nx_int = seed % NX_MAGIC_2147483647
306 if rng < 1 { rng = 1 }
307 var steps: nx_int = 0
308 while steps < total {
309 var best: nx_int = 0 - 1
310 var bestpc: nx_int = 99
311 var k: nx_int = 0
312 while k < total {
313 let pc: nx_int = vp_popcount4(mask[k])
314 if pc == 0 { return 0 } // contradiction
315 if pc > 1 { if pc < bestpc { bestpc = pc; best = k } }
316 k = k + 1
317 }
318 if best < 0 { steps = total } // all collapsed -> done
319 else {
320 rng = (rng * NX_MAGIC_1103515 + NX_MAGIC_12345) % NX_MAGIC_2147483647
321 let pick: nx_int = rng % bestpc
322 var chosen: nx_int = 0
323 var t: nx_int = 0
324 var seen: nx_int = 0
325 while t < 4 {
326 if (mask[best] / vp_bit(t)) % 2 == 1 {
327 if seen == pick { chosen = t }
328 seen = seen + 1
329 }
330 t = t + 1
331 }
332 mask[best] = vp_bit(chosen) // collapse to one tile
333 var changed: nx_int = 1
334 var guard: nx_int = 0
335 while changed == 1 {
336 if guard > total * 4 { changed = 0 } // no-hang bound on propagation
337 else {
338 changed = 0
339 var z: nx_int = 0
340 while z < gn {
341 var x: nx_int = 0
342 while x < gn {
343 let ci: nx_int = z * gn + x
344 if x > 0 { if vp_wfc_prune(mask, ci, z * gn + (x - 1)) == 1 { changed = 1 } }
345 if x < gn - 1 { if vp_wfc_prune(mask, ci, z * gn + (x + 1)) == 1 { changed = 1 } }
346 if z > 0 { if vp_wfc_prune(mask, ci, (z - 1) * gn + x) == 1 { changed = 1 } }
347 if z < gn - 1 { if vp_wfc_prune(mask, ci, (z + 1) * gn + x) == 1 { changed = 1 } }
348 x = x + 1
349 }
350 z = z + 1
351 }
352 guard = guard + 1
353 }
354 }
355 steps = steps + 1
356 }
357 }
358 var j: nx_int = 0
359 while j < total {
360 if vp_popcount4(mask[j]) != 1 { return 0 } // every cell collapsed to exactly one tile
361 var t2: nx_int = 0
362 while t2 < 4 { if (mask[j] / vp_bit(t2)) % 2 == 1 { cells[j] = t2 } t2 = t2 + 1 }
363 j = j + 1
364 }
365 return 1
366}
367// ---- marching cubes SDF surface extraction. A scalar field f(x,y,z) is sampled at the 8 corners of
368// each grid cube; the 8-bit CASE index marks which corners are inside (f>0). The surface crosses
369// every edge whose two corners differ in sign; the vertex on that edge is LINEARLY interpolated
370// (Q10 fixed-point) to the exact zero-crossing -> smooth SUB-VOXEL geometry, emitted ONLY at the
371// isosurface (a fully-inside or fully-outside cube yields no surface). HONEST SCOPE: this gates
372// the surface-extraction MECHANISM (case index + boundary-only + zero-crossing interpolation);
373// the full 256-row triangle-connectivity LUT is the follow-on detail. ----
374func vp_pow2(i: nx_int) -> nx_int { var v: nx_int = 1; var k: nx_int = 0; while k < i { v = v * 2; k = k + 1 } return v }
375// sphere scalar field: positive inside a sphere of radius^2 = r2 centred at (c,c,c).
376func vp_mc_field(c: nx_int, r2: nx_int, x: nx_int, y: nx_int, z: nx_int) -> nx_int {
377 let dx: nx_int = x - c
378 let dy: nx_int = y - c
379 let dz: nx_int = z - c
380 return r2 - (dx * dx + dy * dy + dz * dz)
381}
382// cube-corner offsets (standard MC numbering 0..7): 0(0,0,0)1(1,0,0)2(1,1,0)3(0,1,0)4(0,0,1)5(1,0,1)6(1,1,1)7(0,1,1)
383func vp_mc_cx(i: nx_int) -> nx_int { if i == 1 { return 1 } if i == 2 { return 1 } if i == 5 { return 1 } if i == 6 { return 1 } return 0 }
384func vp_mc_cy(i: nx_int) -> nx_int { if i == 2 { return 1 } if i == 3 { return 1 } if i == 6 { return 1 } if i == 7 { return 1 } return 0 }
385func vp_mc_cz(i: nx_int) -> nx_int { if i == 4 { return 1 } if i == 5 { return 1 } if i == 6 { return 1 } if i == 7 { return 1 } return 0 }
386// the 12 cube edges as corner pairs (a,b)
387func vp_mc_ea(e: nx_int) -> nx_int { if e == 0 { return 0 } if e == 1 { return 1 } if e == 2 { return 2 } if e == 3 { return 3 } if e == 4 { return 4 } if e == 5 { return 5 } if e == 6 { return 6 } if e == 7 { return 7 } if e == 8 { return 0 } if e == 9 { return 1 } if e == 10 { return 2 } return 3 }
388func vp_mc_eb(e: nx_int) -> nx_int { if e == 0 { return 1 } if e == 1 { return 2 } if e == 2 { return 3 } if e == 3 { return 0 } if e == 4 { return 5 } if e == 5 { return 6 } if e == 6 { return 7 } if e == 7 { return 4 } if e == 8 { return 4 } if e == 9 { return 5 } if e == 10 { return 6 } return 7 }
389// 8-bit case index for the cube whose min corner is (bx,by,bz): bit i set iff corner i is inside (f>0).
390func vp_mc_case(c: nx_int, r2: nx_int, bx: nx_int, by: nx_int, bz: nx_int) -> nx_int {
391 var idx: nx_int = 0
392 var i: nx_int = 0
393 while i < 8 {
394 if vp_mc_field(c, r2, bx + vp_mc_cx(i), by + vp_mc_cy(i), bz + vp_mc_cz(i)) > 0 { idx = idx + vp_pow2(i) }
395 i = i + 1
396 }
397 return idx
398}
399// count edges crossed by the surface (the two corners differ in inside/outside status) = vertices emitted.
400func vp_mc_active_edges(c: nx_int, r2: nx_int, bx: nx_int, by: nx_int, bz: nx_int) -> nx_int {
401 var n: nx_int = 0
402 var e: nx_int = 0
403 while e < 12 {
404 let a: nx_int = vp_mc_ea(e)
405 let b: nx_int = vp_mc_eb(e)
406 var ina: nx_int = 0
407 if vp_mc_field(c, r2, bx + vp_mc_cx(a), by + vp_mc_cy(a), bz + vp_mc_cz(a)) > 0 { ina = 1 }
408 var inb: nx_int = 0
409 if vp_mc_field(c, r2, bx + vp_mc_cx(b), by + vp_mc_cy(b), bz + vp_mc_cz(b)) > 0 { inb = 1 }
410 if ina != inb { n = n + 1 }
411 e = e + 1
412 }
413 return n
414}
415// Q10 zero-crossing parameter along an edge from corner-value fa to fb (opposite signs): t=(0-fa)/(fb-fa).
416func vp_mc_interp_q10(fa: nx_int, fb: nx_int) -> nx_int {
417 let denom: nx_int = fb - fa
418 if denom == 0 { return 0 - 1 }
419 return (0 - fa) * NX_MAGIC_1024 / denom
420}
421// ---- sovereign software ray-traced reflection. The reflected direction obeys the mirror law
422// r = d - 2(d·n)n. To stay integer we return r SCALED by nlen2 = |n|^2 (a positive scale, so the
423// direction is exact); an axis-aligned normal has nlen2=1 -> exact unscaled reflection. This is the
424// core of RT reflection: a reflected ray is spawned off a surface and traced to sample the scene. ----
425func vp_reflect(dx: nx_int, dy: nx_int, dz: nx_int, nax: nx_int, nay: nx_int, naz: nx_int, out: *i64) -> nx_int {
426 let nlen2: nx_int = nax * nax + nay * nay + naz * naz
427 let dot: nx_int = dx * nax + dy * nay + dz * naz
428 out[0] = dx * nlen2 - 2 * dot * nax
429 out[1] = dy * nlen2 - 2 * dot * nay
430 out[2] = dz * nlen2 - 2 * dot * naz
431 return nlen2
432}
433// ---- octree level-of-detail: a SPARSE VOXEL OCTREE over a scalar field. Space is recursively split
434// into 8 octants; a region that is HOMOGENEOUS (every sampled corner the same field sign) collapses
435// to a leaf and is NOT subdivided -> the tree stays sparse (only the surface refines to full depth).
436// Querying at a shallower maxDepth = a coarser LOD (fewer nodes). Built with a MANUAL STACK (no
437// recursion dependency); all loops bounded (no-hang). ----
438// homogeneous iff every integer lattice point in the closed cell [x0..x0+s]^3 has the same field sign.
439func vp_oct_homog(c: nx_int, r2: nx_int, x0: nx_int, y0: nx_int, z0: nx_int, s: nx_int) -> nx_int {
440 var first: nx_int = 0 - 2
441 var allsame: nx_int = 1
442 var z: nx_int = z0
443 while z <= z0 + s {
444 var y: nx_int = y0
445 while y <= y0 + s {
446 var x: nx_int = x0
447 while x <= x0 + s {
448 var sgn: nx_int = 0
449 if vp_mc_field(c, r2, x, y, z) > 0 { sgn = 1 }
450 if first == 0 - 2 { first = sgn } else { if sgn != first { allsame = 0 } }
451 x = x + 1
452 }
453 y = y + 1
454 }
455 z = z + 1
456 }
457 return allsame
458}
459// count the nodes of the sparse octree to maxDepth. stack holds frontier entries of (lvl,x0,y0,z0).
460func vp_octree_count(c: nx_int, r2: nx_int, W: nx_int, maxDepth: nx_int, stack: *i64) -> nx_int {
461 stack[0] = 0; stack[1] = 0; stack[2] = 0; stack[3] = 0
462 var sp: nx_int = 4
463 var count: nx_int = 0
464 var guard: nx_int = 0
465 while sp > 0 {
466 if guard > NX_MAGIC_5000 { sp = 0 } // no-hang bound (dense depth-3 octree = 585 nodes << NX_MAGIC_5000)
467 else {
468 sp = sp - 4
469 let lvl: nx_int = stack[sp]
470 let x0: nx_int = stack[sp + 1]
471 let y0: nx_int = stack[sp + 2]
472 let z0: nx_int = stack[sp + 3]
473 count = count + 1
474 let s: nx_int = W / vp_pow2(lvl)
475 var subdivide: nx_int = 0
476 if lvl < maxDepth { if s > 1 { if vp_oct_homog(c, r2, x0, y0, z0, s) == 0 { subdivide = 1 } } }
477 if subdivide == 1 {
478 let h: nx_int = s / 2
479 var oz: nx_int = 0
480 while oz < 2 {
481 var oy: nx_int = 0
482 while oy < 2 {
483 var ox: nx_int = 0
484 while ox < 2 {
485 stack[sp] = lvl + 1
486 stack[sp + 1] = x0 + ox * h
487 stack[sp + 2] = y0 + oy * h
488 stack[sp + 3] = z0 + oz * h
489 sp = sp + 4
490 ox = ox + 1
491 }
492 oy = oy + 1
493 }
494 oz = oz + 1
495 }
496 }
497 guard = guard + 1
498 }
499 }
500 return count
501}
502// ---- runtime voxel destruction (Teardown-class). A 3D occupancy grid (1 solid / 0 empty); a blast
503// carves out every solid voxel within a radius, then a STRUCTURAL-INTEGRITY flood-fill from the
504// ground (y=0) finds any solid voxel no longer connected to the ground = a floating piece that
505// would fall. Manual-stack BFS (no recursion); all loops bounded (no-hang). ----
506func vp_vd_count(occ: *i64, GN: nx_int) -> nx_int {
507 var c: nx_int = 0
508 var i: nx_int = 0
509 while i < GN * GN * GN { if occ[i] == 1 { c = c + 1 } i = i + 1 }
510 return c
511}
512// remove (set empty) every solid voxel within Euclidean radius^2 r2 of the blast centre; return removed count.
513func vp_vd_blast(occ: *i64, GN: nx_int, bx: nx_int, by: nx_int, bz: nx_int, r2: nx_int) -> nx_int {
514 var removed: nx_int = 0
515 var z: nx_int = 0
516 while z < GN {
517 var y: nx_int = 0
518 while y < GN {
519 var x: nx_int = 0
520 while x < GN {
521 let dx: nx_int = x - bx
522 let dy: nx_int = y - by
523 let dz: nx_int = z - bz
524 if dx * dx + dy * dy + dz * dz <= r2 {
525 let id: nx_int = x + GN * (y + GN * z)
526 if occ[id] == 1 { occ[id] = 0; removed = removed + 1 }
527 }
528 x = x + 1
529 }
530 y = y + 1
531 }
532 z = z + 1
533 }
534 return removed
535}
536// push neighbour nid if it is solid and not yet reached; return the new stack pointer.
537func vp_vd_visit(occ: *i64, reached: *i64, stack: *i64, sp: nx_int, nid: nx_int) -> nx_int {
538 if occ[nid] == 1 { if reached[nid] == 0 { reached[nid] = 1; stack[sp] = nid; return sp + 1 } }
539 return sp
540}
541// flood-fill solid voxels reachable from the ground (y==0, 6-connected); return the count of solid
542// voxels NOT reached = pieces disconnected from the ground = floating (would fall).
543func vp_vd_floating(occ: *i64, reached: *i64, GN: nx_int, stack: *i64) -> nx_int {
544 let tot: nx_int = GN * GN * GN
545 var i: nx_int = 0
546 while i < tot { reached[i] = 0; i = i + 1 }
547 var sp: nx_int = 0
548 var z: nx_int = 0
549 while z < GN {
550 var x: nx_int = 0
551 while x < GN {
552 let id0: nx_int = x + GN * (0 + GN * z)
553 sp = vp_vd_visit(occ, reached, stack, sp, id0) // seed every solid ground voxel
554 x = x + 1
555 }
556 z = z + 1
557 }
558 var guard: nx_int = 0
559 while sp > 0 {
560 if guard > NX_MAGIC_100000 { sp = 0 }
561 else {
562 sp = sp - 1
563 let id: nx_int = stack[sp]
564 let cx: nx_int = id % GN
565 let cy: nx_int = (id / GN) % GN
566 let cz: nx_int = id / (GN * GN)
567 if cx > 0 { sp = vp_vd_visit(occ, reached, stack, sp, id - 1) }
568 if cx < GN - 1 { sp = vp_vd_visit(occ, reached, stack, sp, id + 1) }
569 if cy > 0 { sp = vp_vd_visit(occ, reached, stack, sp, id - GN) }
570 if cy < GN - 1 { sp = vp_vd_visit(occ, reached, stack, sp, id + GN) }
571 if cz > 0 { sp = vp_vd_visit(occ, reached, stack, sp, id - GN * GN) }
572 if cz < GN - 1 { sp = vp_vd_visit(occ, reached, stack, sp, id + GN * GN) }
573 guard = guard + 1
574 }
575 }
576 var floating: nx_int = 0
577 i = 0
578 while i < tot { if occ[i] == 1 { if reached[i] == 0 { floating = floating + 1 } } i = i + 1 }
579 return floating
580}
581// ---- Nanite virtual-geometry cluster-LOD. Triangles are grouped into clusters; clusters form a DAG
582// whose parents are SIMPLIFIED (fewer triangles, larger geometric error) versions covering the
583// same surface. A view picks a CUT through the DAG: the COARSEST cluster whose screen-space error
584// (geomError/distance) is still within the pixel budget. The cut is COMPLETE + NON-OVERLAPPING
585// (every surface region covered exactly once = watertight, no cracks/double-draw). Near views pick
586// fine clusters (more triangles); far views pick coarse (fewer) -> ~constant screen triangle density.
587// Modelled as a balanced cluster tree (heap) over NLEAF leaf regions; geomError = leaves covered.
588// HONEST SCOPE: this gates the LOD-CUT core; full micropolygon virtualization/streaming is follow-on. ----
589func vp_nanite_depth(i: nx_int) -> nx_int { var d: nx_int = 0; while vp_pow2(d + 1) <= i + 1 { d = d + 1 } return d }
590func vp_nanite_err(i: nx_int, NLEAF: nx_int) -> nx_int { return NLEAF / vp_pow2(vp_nanite_depth(i)) }
591func vp_nanite_lo(i: nx_int, NLEAF: nx_int) -> nx_int {
592 let d: nx_int = vp_nanite_depth(i)
593 let p: nx_int = (i + 1) - vp_pow2(d)
594 return p * (NLEAF / vp_pow2(d))
595}
596// select the coarsest cut whose every cluster's screen-error (err*K/dist) <= TAU; fill cover[] with how
597// many times each leaf region is covered; return the cluster count of the cut. manual stack, bounded.
598func vp_nanite_cut(NLEAF: nx_int, NNODE: nx_int, K: nx_int, TAU: nx_int, dist: nx_int, stack: *i64, cover: *i64) -> nx_int {
599 var i: nx_int = 0
600 while i < NLEAF { cover[i] = 0; i = i + 1 }
601 stack[0] = 0
602 var sp: nx_int = 1
603 var clusters: nx_int = 0
604 var guard: nx_int = 0
605 while sp > 0 {
606 if guard > NX_MAGIC_10000 { sp = 0 }
607 else {
608 guard = guard + 1
609 sp = sp - 1
610 let node: nx_int = stack[sp]
611 let err: nx_int = vp_nanite_err(node, NLEAF)
612 let leftChild: nx_int = 2 * node + 1
613 var fineEnough: nx_int = 0
614 if err * K <= TAU * dist { fineEnough = 1 } // screen-error within the pixel budget
615 if leftChild >= NNODE { fineEnough = 1 } // a leaf is the finest available -> always selectable
616 if fineEnough == 1 {
617 clusters = clusters + 1
618 let lo: nx_int = vp_nanite_lo(node, NLEAF)
619 var j: nx_int = 0
620 while j < err { cover[lo + j] = cover[lo + j] + 1; j = j + 1 }
621 }
622 else {
623 stack[sp] = leftChild; sp = sp + 1
624 stack[sp] = 2 * node + 2; sp = sp + 1
625 }
626 }
627 }
628 return clusters
629}
630// ---- texture-atlas block textures. Multiple TS×TS tiles packed side-by-side in one atlas image; a
631// block samples its tile via a UV in Q8 [0,256). Sampling is BILINEAR (blend the 4 surrounding
632// texels by sub-texel fraction) and TILE-ISOLATED (a tile's texel x stays in [tile*TS, tile*TS+TS),
633// so block A never bleeds into block B). atlas is row-major grayscale, width atlasW. ----
634func vp_tex_clampi(a: nx_int, hi: nx_int) -> nx_int { if a < 0 { return 0 } if a > hi { return hi } return a }
635func vp_tex_sample(atlas: *i64, atlasW: nx_int, TS: nx_int, tile: nx_int, u: nx_int, v: nx_int) -> nx_int {
636 let fx: nx_int = u * (TS - 1)
637 let fy: nx_int = v * (TS - 1)
638 let x0: nx_int = fx / 256
639 let y0: nx_int = fy / 256
640 let tx: nx_int = fx - x0 * 256
641 let ty: nx_int = fy - y0 * 256
642 let x1: nx_int = vp_tex_clampi(x0 + 1, TS - 1)
643 let y1: nx_int = vp_tex_clampi(y0 + 1, TS - 1)
644 let bx: nx_int = tile * TS
645 let v00: nx_int = atlas[y0 * atlasW + (bx + x0)]
646 let v10: nx_int = atlas[y0 * atlasW + (bx + x1)]
647 let v01: nx_int = atlas[y1 * atlasW + (bx + x0)]
648 let v11: nx_int = atlas[y1 * atlasW + (bx + x1)]
649 let top: nx_int = v00 * (256 - tx) + v10 * tx
650 let bot: nx_int = v01 * (256 - tx) + v11 * tx
651 return (top * (256 - ty) + bot * ty) / (256 * 256)
652}
653// fluid cellular automaton: water sheds 1 unit to each 4-neighbour whose SURFACE (terrain+water) is
654// >1 below it -> flows downhill, conserves volume (each -1 has a +1), settles (>1 threshold = no
655// 1-unit oscillation), deterministic. The water-flow primitive (Minecraft water/lava).
656func vp_water_step(terr: *i64, water: *i64, gn: nx_int, dw: *i64) -> nx_int {
657 var i: nx_int = 0
658 while i < gn * gn { dw[i] = 0; i = i + 1 }
659 var z: nx_int = 0
660 while z < gn {
661 var x: nx_int = 0
662 while x < gn {
663 let c: nx_int = x * gn + z
664 if water[c] > 0 {
665 let s: nx_int = terr[c] + water[c]
666 if x > 0 { let n: nx_int = (x-1)*gn+z; if water[c]+dw[c] > 0 { if s - (terr[n]+water[n]) > 1 { dw[c] = dw[c] - 1; dw[n] = dw[n] + 1 } } }
667 if x < gn - 1 { let n: nx_int = (x+1)*gn+z; if water[c]+dw[c] > 0 { if s - (terr[n]+water[n]) > 1 { dw[c] = dw[c] - 1; dw[n] = dw[n] + 1 } } }
668 if z > 0 { let n: nx_int = x*gn+(z-1); if water[c]+dw[c] > 0 { if s - (terr[n]+water[n]) > 1 { dw[c] = dw[c] - 1; dw[n] = dw[n] + 1 } } }
669 if z < gn - 1 { let n: nx_int = x*gn+(z+1); if water[c]+dw[c] > 0 { if s - (terr[n]+water[n]) > 1 { dw[c] = dw[c] - 1; dw[n] = dw[n] + 1 } } }
670 }
671 x = x + 1
672 }
673 z = z + 1
674 }
675 var j: nx_int = 0
676 while j < gn * gn { water[j] = water[j] + dw[j]; j = j + 1 }
677 return 0
678}
679// SSAO -- screen-space ambient occlusion, a SOVEREIGN post-process on OUR framebuffer + z-buffer
680// (no GPU, no WebGL; we compute every pixel). A pixel darkens by how many neighbours sit closer to
681// the camera (occluding it) -> contact shadows in creases. The Unreal-tier technique, built ourselves.
682func vp_ssao(fb: *i64, zb: *i64, w: nx_int, h: nx_int) -> nx_int {
683 let R: nx_int = 2
684 let TH: nx_int = NX_MAGIC_8000
685 var y: nx_int = R
686 while y < h - R {
687 var x: nx_int = R
688 while x < w - R {
689 let idx: nx_int = y * w + x
690 let d: nx_int = zb[idx]
691 if d < NX_RASTER_Z_FAR { // shade geometry, never the sky
692 var occ: nx_int = 0
693 if zb[idx - R] < d - TH { occ = occ + 1 }
694 if zb[idx + R] < d - TH { occ = occ + 1 }
695 if zb[idx - R * w] < d - TH { occ = occ + 1 }
696 if zb[idx + R * w] < d - TH { occ = occ + 1 }
697 if zb[idx - R * w - R] < d - TH { occ = occ + 1 }
698 if zb[idx - R * w + R] < d - TH { occ = occ + 1 }
699 if zb[idx + R * w - R] < d - TH { occ = occ + 1 }
700 if zb[idx + R * w + R] < d - TH { occ = occ + 1 }
701 if occ > 0 {
702 let f: nx_int = 255 - occ * 16 // up to ~50% darkening at full occlusion
703 let px: nx_int = fb[idx]
704 let r: nx_int = (px % 256) * f / 255
705 let g: nx_int = ((px / 256) % 256) * f / 255
706 let b: nx_int = ((px / NX_MAGIC_65536) % 256) * f / 255
707 fb[idx] = r + g * 256 + b * NX_MAGIC_65536 + 255 * NX_MAGIC_16777216
708 }
709 }
710 x = x + 1
711 }
712 y = y + 1
713 }
714 return 0
715}
716// VOLUMETRIC ATMOSPHERE -- SOVEREIGN aerial perspective: distant geometry blends toward the
717// atmosphere colour by depth (the Unreal sky-atmosphere "distance haze"), computed by us on OUR
718// z-buffer. No GPU. fog rises linearly from NEAR depth to FAR; sky (depth = FAR) untouched.
719func vp_atmos(fb: *i64, zb: *i64, w: nx_int, h: nx_int) -> nx_int {
720 let FR: nx_int = 150
721 let FG: nx_int = 185
722 let FB2: nx_int = 225
723 let NEAR: nx_int = NX_MAGIC_100000
724 let FAR2: nx_int = NX_MAGIC_320000
725 var i: nx_int = 0
726 while i < w * h {
727 let d: nx_int = zb[i]
728 if d < NX_RASTER_Z_FAR {
729 var t: nx_int = 0
730 if d > NEAR { t = (d - NEAR) * 255 / (FAR2 - NEAR) }
731 if t > 255 { t = 255 }
732 let px: nx_int = fb[i]
733 let r: nx_int = px % 256
734 let g: nx_int = (px / 256) % 256
735 let b: nx_int = (px / NX_MAGIC_65536) % 256
736 let nr: nx_int = (r * (255 - t) + FR * t) / 255
737 let ng: nx_int = (g * (255 - t) + FG * t) / 255
738 let nb: nx_int = (b * (255 - t) + FB2 * t) / 255
739 fb[i] = nr + ng * 256 + nb * NX_MAGIC_65536 + 255 * NX_MAGIC_16777216
740 }
741 i = i + 1
742 }
743 return 0
744}
745// GLOBAL ILLUMINATION -- SOVEREIGN one-bounce indirect light (Lumen-class): each pixel gathers
746// indirect bounce from its neighbourhood (colour bleeding / soft indirect fill), computed by us on
747// OUR framebuffer. src = a snapshot of the lit frame (so the gather reads pre-bounce colours). No GPU.
748func vp_gi(fb: *i64, src: *i64, w: nx_int, h: nx_int) -> nx_int {
749 let R: nx_int = 2
750 let K: nx_int = 80
751 var y: nx_int = R
752 while y < h - R {
753 var x: nx_int = R
754 while x < w - R {
755 var sr: nx_int = 0
756 var sg: nx_int = 0
757 var sb: nx_int = 0
758 var cnt: nx_int = 0
759 var dy: nx_int = 0 - R
760 while dy <= R {
761 var dx: nx_int = 0 - R
762 while dx <= R {
763 let p: nx_int = src[(y + dy) * w + (x + dx)]
764 sr = sr + (p % 256)
765 sg = sg + ((p / 256) % 256)
766 sb = sb + ((p / NX_MAGIC_65536) % 256)
767 cnt = cnt + 1
768 dx = dx + 1
769 }
770 dy = dy + 1
771 }
772 let p0: nx_int = src[y * w + x]
773 let nr: nx_int = ((p0 % 256) * (255 - K) + (sr / cnt) * K) / 255
774 let ng: nx_int = (((p0 / 256) % 256) * (255 - K) + (sg / cnt) * K) / 255
775 let nb: nx_int = (((p0 / NX_MAGIC_65536) % 256) * (255 - K) + (sb / cnt) * K) / 255
776 fb[y * w + x] = nr + ng * 256 + nb * NX_MAGIC_65536 + 255 * NX_MAGIC_16777216
777 x = x + 1
778 }
779 y = y + 1
780 }
781 return 0
782}
783// directional sun: per-face brightness 55..255 (ambient + diffuse along the face normal).
784// sun (sx,sy,sz) ~[-110,110]; moving the sun lights different faces = a day-night cycle.
785func vp_sun_face(face: nx_int, sx: nx_int, sy: nx_int, sz: nx_int) -> nx_int {
786 var d: nx_int = 0
787 if face == 0 { d = sy } // top normal +Y
788 if face == 1 { d = sx } // +X
789 if face == 2 { d = 0 - sx } // -X
790 if face == 3 { d = sz } // +Z
791 if face == 4 { d = 0 - sz } // -Z
792 var b: nx_int = 150 + d
793 if b < 55 { b = 55 }
794 if b > 255 { b = 255 }
795 return b
796}
797// PBR specular BRDF (sovereign, fixed-point): smooth materials (low roughness) get a sharp, bright,
798// VIEW-DEPENDENT highlight (high exponent); rough materials get little (scaled down). ndoth = the
799// alignment of the half-vector with the surface normal, 0..255. Energy-bounded by construction.
800func vp_spec(roughness: nx_int, ndoth: nx_int) -> nx_int {
801 if ndoth <= 0 { return 0 }
802 if ndoth > 255 { return 255 }
803 var s: nx_int = ndoth
804 let pw: nx_int = (255 - roughness) / 40 // smoother -> higher exponent -> sharper highlight
805 var p: nx_int = 0
806 while p < pw { s = s * ndoth / 255; p = p + 1 }
807 return s * (255 - roughness) / 255 // rough materials reflect less specular energy
808}
809// per-vertex-coloured triangle (each corner its own packed RGBA -> the raster interpolates)
810func vp_tri3(fb: *i64, zb: *i64, a: *i64, b: *i64, c: *i64, ca: nx_int, cb: nx_int, cc: nx_int) -> nx_int {
811 if a[3] == 0 { return 0 }
812 if b[3] == 0 { return 0 }
813 if c[3] == 0 { return 0 }
814 let tri: *i64 = (sys_mmap(96)) as *i64
815 tri[0] = a[0]; tri[1] = a[1]; tri[2] = a[2]; tri[3] = ca
816 tri[4] = b[0]; tri[5] = b[1]; tri[6] = b[2]; tri[7] = cb
817 tri[8] = c[0]; tri[9] = c[1]; tri[10] = c[2]; tri[11] = cc
818 nx_raster_triangle(fb, zb, NX_W, NX_H, tri)
819 return 0
820}
821// quad with per-corner AO alpha over a shared RGB
822func vp_quad_ao(fb: *i64, zb: *i64, p: *i64, a: nx_int, b: nx_int, c: nx_int, d: nx_int,
823 r: nx_int, g: nx_int, bl: nx_int, alA: nx_int, alB: nx_int, alC: nx_int, alD: nx_int) -> nx_int {
824 let pa: *i64 = ((p as i64) + a * 32) as *i64
825 let pb: *i64 = ((p as i64) + b * 32) as *i64
826 let pc: *i64 = ((p as i64) + c * 32) as *i64
827 let pd: *i64 = ((p as i64) + d * 32) as *i64
828 vp_tri3(fb, zb, pa, pb, pc, vp_pack(r,g,bl,alA), vp_pack(r,g,bl,alB), vp_pack(r,g,bl,alC))
829 vp_tri3(fb, zb, pa, pc, pd, vp_pack(r,g,bl,alA), vp_pack(r,g,bl,alC), vp_pack(r,g,bl,alD))
830 return 0
831}
832
833// render one terrain column with per-corner ambient occlusion on the top + vertical AO on the sides
834func vp_column(fb: *i64, zb: *i64, mvp: *i64, hgrid: *i64, biomegrid: *i64, gn: nx_int, x: nx_int, z: nx_int, sx: nx_int, sy: nx_int, sz: nx_int) -> nx_int {
835 let h: nx_int = hget(hgrid, gn, x, z)
836 if h <= 0 { return 0 }
837 let p: *i64 = (sys_mmap(8 * 32)) as *i64 // 8 corners x 4 i64
838 vp_project(mvp, x, 0, z, ((p as i64) + 0 * 32) as *i64)
839 vp_project(mvp, x + 1, 0, z, ((p as i64) + 1 * 32) as *i64)
840 vp_project(mvp, x + 1, 0, z + 1, ((p as i64) + 2 * 32) as *i64)
841 vp_project(mvp, x, 0, z + 1, ((p as i64) + 3 * 32) as *i64)
842 vp_project(mvp, x, h, z, ((p as i64) + 4 * 32) as *i64)
843 vp_project(mvp, x + 1, h, z, ((p as i64) + 5 * 32) as *i64)
844 vp_project(mvp, x + 1, h, z + 1, ((p as i64) + 6 * 32) as *i64)
845 vp_project(mvp, x, h, z + 1, ((p as i64) + 7 * 32) as *i64)
846 // top-face per-corner AO from the 3 neighbour columns touching each grid corner
847 let a4: nx_int = vp_ao(vp_occ(hgrid, gn, h, x-1, z, x, z-1, x-1, z-1))
848 let a5: nx_int = vp_ao(vp_occ(hgrid, gn, h, x+1, z, x, z-1, x+1, z-1))
849 let a6: nx_int = vp_ao(vp_occ(hgrid, gn, h, x+1, z, x, z+1, x+1, z+1))
850 let a7: nx_int = vp_ao(vp_occ(hgrid, gn, h, x-1, z, x, z+1, x-1, z+1))
851 let biome: nx_int = biomegrid[x * gn + z] // biome-specific top palette
852 var br: nx_int = 90; var bg: nx_int = 170; var bb: nx_int = 80 // grass plains
853 if biome == 1 { br = 205; bg = 195; bb = 130 } // desert sand
854 if biome == 2 { br = 130; bg = 130; bb = 145 } // stone / rock
855 if biome == 3 { br = 235; bg = 238; bb = 245 } // snow cap
856 if biome == 4 { br = 55; bg = 105; bb = 200 } // water
857 // PBR specular: smooth materials (water/snow) glint toward white where sun+view align with the up-normal
858 var sp: nx_int = 0
859 if biome == 4 { sp = vp_spec(40, 128 + sy) } // water: smooth -> sharp sheen
860 if biome == 3 { sp = vp_spec(120, 128 + sy) } // snow: rougher sheen
861 if sp > 0 { br = br + sp; if br > 255 { br = 255 } bg = bg + sp; if bg > 255 { bg = 255 } bb = bb + sp; if bb > 255 { bb = 255 } }
862 // brightness = AO (per corner) x directional sun (per face) -> contact shadows + day-night light
863 let s0: nx_int = vp_sun_face(0, sx, sy, sz)
864 vp_quad_ao(fb, zb, p, 4, 5, 6, 7, br, bg, bb, a4*s0/255, a5*s0/255, a6*s0/255, a7*s0/255) // biome top
865 let s4: nx_int = vp_sun_face(4, sx, sy, sz)
866 vp_quad_ao(fb, zb, p, 0, 1, 5, 4, 150, 110, 65, 150*s4/255, 150*s4/255, 205*s4/255, 205*s4/255) // -z
867 let s3: nx_int = vp_sun_face(3, sx, sy, sz)
868 vp_quad_ao(fb, zb, p, 3, 2, 6, 7, 150, 110, 65, 150*s3/255, 150*s3/255, 205*s3/255, 205*s3/255) // +z
869 let s2: nx_int = vp_sun_face(2, sx, sy, sz)
870 vp_quad_ao(fb, zb, p, 0, 4, 7, 3, 150, 110, 65, 110*s2/255, 150*s2/255, 150*s2/255, 110*s2/255) // -x
871 let s1: nx_int = vp_sun_face(1, sx, sy, sz)
872 vp_quad_ao(fb, zb, p, 1, 5, 6, 2, 150, 110, 65, 110*s1/255, 150*s1/255, 150*s1/255, 110*s1/255) // +x
873 return 0
874}
875
876// build MVP = P * (Rx(pitch) * Ry(yaw) * T(-cam))
877func vp_build_mvp(mvp: *i64, camx: nx_int, camy: nx_int, camz: nx_int,
878 yaw: nx_int, pitch: nx_int) -> nx_int {
879 let T: *i64 = (sys_mmap(128)) as *i64
880 let Ry: *i64 = (sys_mmap(128)) as *i64
881 let Rx: *i64 = (sys_mmap(128)) as *i64
882 let P: *i64 = (sys_mmap(128)) as *i64
883 let t1: *i64 = (sys_mmap(128)) as *i64
884 let V: *i64 = (sys_mmap(128)) as *i64
885 nx_camera_translation_4x4(0 - camx * NX_VQ, 0 - camy * NX_VQ, 0 - camz * NX_VQ, T)
886 nx_camera_rotation_y_deg(yaw, Ry)
887 nx_camera_rotation_x_deg(pitch, Rx)
888 nx_camera_mat4_mul(Ry, T, t1) // Ry * T
889 nx_camera_mat4_mul(Rx, t1, V) // Rx * (Ry*T)
890 let aspect: nx_int = (NX_VQ * NX_W) / NX_H // W/H in Q14
891 nx_camera_perspective(70, aspect, NX_VQ, 1000 * NX_VQ, P)
892 nx_camera_mat4_mul(P, V, mvp) // P * V
893 return 0
894}
895
896func vp_putfd(fd: nx_int, s: *u8) -> nx_int {
897 var n: nx_int = 0
898 while s[n] != (0 as u8) { n = n + 1 }
899 sys_write(fd, s, n)
900 return 0
901}
902func vp_puts(s: *u8) -> nx_int { return vp_putfd(1, s) }
903
904// ---- 24bpp BMP from the u32-RGBA i64 framebuffer ----
905func vp_w16(b: *u8, o: nx_int, v: nx_int) -> nx_int { b[o] = (v & 255) as u8; b[o+1] = ((v>>8)&255) as u8; return o+2 }
906func vp_w32(b: *u8, o: nx_int, v: nx_int) -> nx_int {
907 b[o] = (v & 255) as u8; b[o+1] = ((v>>8)&255) as u8; b[o+2] = ((v>>16)&255) as u8; b[o+3] = ((v>>24)&255) as u8
908 return o + 4
909}
910func vp_write_bmp(fb: *i64, path: *u8) -> nx_int {
911 let npix: nx_int = NX_W * NX_H * 3
912 let total: nx_int = 54 + npix
913 let out: *u8 = sys_mmap(total)
914 var o: nx_int = 0
915 out[0] = 66 as u8; out[1] = 77 as u8
916 o = vp_w32(out, 2, total); o = vp_w32(out, 6, 0); o = vp_w32(out, 10, 54)
917 o = vp_w32(out, 14, 40); o = vp_w32(out, 18, NX_W); o = vp_w32(out, 22, NX_H)
918 o = vp_w16(out, 26, 1); o = vp_w16(out, 28, 24); o = vp_w32(out, 30, 0)
919 o = vp_w32(out, 34, npix); o = vp_w32(out, 38, NX_MAGIC_2835); o = vp_w32(out, 42, NX_MAGIC_2835)
920 o = vp_w32(out, 46, 0); o = vp_w32(out, 50, 0)
921 var y: nx_int = 0
922 while y < NX_H {
923 let srcy: nx_int = NX_H - 1 - y
924 var x: nx_int = 0
925 while x < NX_W {
926 let px: nx_int = fb[srcy * NX_W + x]
927 let di: nx_int = 54 + (y * NX_W + x) * 3
928 out[di] = (px >> 16) & 255 // B
929 out[di + 1] = (px >> 8) & 255 // G
930 out[di + 2] = px & 255 // R
931 x = x + 1
932 }
933 y = y + 1
934 }
935 let fd: nx_int = sys_openat_wr(path, 420)
936 if fd < 0 { return 0 - 1 }
937 sys_write(fd, out, total)
938 sys_close(fd)
939 return 0
940}
941// build "web_assets/_game_build/frame_NN.bmp" into buf (NN = two-digit frame index)
942func vp_frame_path(buf: *u8, f: nx_int) -> nx_int {
943 let pre: *u8 = "web_assets/_game_build/frame_" as *u8
944 var i: nx_int = 0
945 while pre[i] != (0 as u8) { buf[i] = pre[i]; i = i + 1 }
946 buf[i] = (48 + f / 10) as u8; i = i + 1
947 buf[i] = (48 + f % 10) as u8; i = i + 1
948 let suf: *u8 = ".bmp" as *u8
949 var j: nx_int = 0
950 while suf[j] != (0 as u8) { buf[i] = suf[j]; i = i + 1; j = j + 1 }
951 buf[i] = 0 as u8
952 return 0
953}
954
955func main() -> nx_int {
956 var pass: nx_int = 1
957 let fb: *i64 = nx_raster_alloc_fb(NX_W, NX_H)
958 let zb: *i64 = nx_raster_alloc_zb(NX_W, NX_H)
959
960 // ===== GATE G1: z-buffer occlusion (camera-independent) =====
961 nx_raster_clear(fb, NX_W, NX_H, vp_pack(0, 0, 0, 255))
962 nx_raster_zbuffer_clear(zb, NX_W, NX_H)
963 let tf: *i64 = (sys_mmap(96)) as *i64 // FAR red
964 tf[0]=40; tf[1]=40; tf[2]=8*NX_VQ; tf[3]=vp_pack(255,0,0,255)
965 tf[4]=120; tf[5]=40; tf[6]=8*NX_VQ; tf[7]=vp_pack(255,0,0,255)
966 tf[8]=80; tf[9]=120; tf[10]=8*NX_VQ; tf[11]=vp_pack(255,0,0,255)
967 nx_raster_triangle(fb, zb, NX_W, NX_H, tf)
968 let tn: *i64 = (sys_mmap(96)) as *i64 // NEAR green, overlaps
969 tn[0]=40; tn[1]=40; tn[2]=2*NX_VQ; tn[3]=vp_pack(0,255,0,255)
970 tn[4]=120; tn[5]=40; tn[6]=2*NX_VQ; tn[7]=vp_pack(0,255,0,255)
971 tn[8]=80; tn[9]=120; tn[10]=2*NX_VQ; tn[11]=vp_pack(0,255,0,255)
972 nx_raster_triangle(fb, zb, NX_W, NX_H, tn)
973 let cpix: nx_int = nx_raster_pixel(fb, NX_W, 80, 60)
974 let cg: nx_int = (cpix / 256) % 256
975 let cr: nx_int = cpix % 256
976 if cg == 255 { if cr == 0 { vp_puts("G1 OK: z-buffer occlusion (near green hides far red)\n" as *u8) } }
977 if cg != 255 { pass = 0; vp_puts("G1 FAIL: occlusion\n" as *u8) }
978
979 // ===== GATE G2: perspective projection (in-front / behind / off-axis) =====
980 let P: *i64 = (sys_mmap(128)) as *i64
981 nx_camera_perspective(90, NX_VQ, NX_VQ, 1000 * NX_VQ, P)
982 let s: *i64 = (sys_mmap(32)) as *i64
983 vp_project(P, 0, 0, 0 - 5, s) // in front, centered
984 if s[3] == 1 { if s[0] > NX_W2 - 3 { if s[0] < NX_W2 + 3 {
985 vp_puts("G2a OK: in-front point projects to screen centre\n" as *u8) } } }
986 if s[3] != 1 { pass = 0; vp_puts("G2a FAIL: in-front rejected\n" as *u8) }
987 let s2: *i64 = (sys_mmap(32)) as *i64
988 vp_project(P, 0, 0, 5, s2) // behind camera
989 if s2[3] == 0 { vp_puts("G2b OK: behind-camera point rejected (no div)\n" as *u8) }
990 if s2[3] != 0 { pass = 0; vp_puts("G2b FAIL: behind not rejected\n" as *u8) }
991 let s3: *i64 = (sys_mmap(32)) as *i64
992 vp_project(P, 2, 0, 0 - 5, s3) // off to +x
993 if s3[3] == 1 { if s3[0] > NX_W2 { vp_puts("G2c OK: +x point projects right of centre\n" as *u8) }
994 else { pass = 0; vp_puts("G2c FAIL: off-axis wrong side\n" as *u8) } }
995
996 // ===== GATE G4: procedural Perlin terrain (the newly-landed capability) =====
997 let ps: *PerlinState = nx_perlin_alloc(NX_MAGIC_1337)
998 let ps2: *PerlinState = nx_perlin_alloc(NX_MAGIC_1337)
999 var noiseOk: nx_int = 1
1000 let d1: nx_int = nx_perlin_2d(ps, 5 * 340, 7 * 340)
1001 let d2: nx_int = nx_perlin_2d(ps2, 5 * 340, 7 * 340)
1002 let d3: nx_int = nx_perlin_2d(ps, 5 * 340, 7 * 340)
1003 if d1 != d2 { noiseOk = 0 } // seed-deterministic across instances
1004 if d1 != d3 { noiseOk = 0 } // and stable on re-sample
1005 var mn: nx_int = NX_MAGIC_100000
1006 var mx: nx_int = 0 - NX_MAGIC_100000
1007 var adj: nx_int = 0
1008 var far: nx_int = 0
1009 var gz: nx_int = 0
1010 while gz < 16 {
1011 var gx: nx_int = 0
1012 while gx < 16 {
1013 let na: nx_int = nx_perlin_2d(ps, gx * 340, gz * 340)
1014 if na < mn { mn = na }
1015 if na > mx { mx = na }
1016 let nb: nx_int = nx_perlin_2d(ps, (gx + 1) * 340, gz * 340)
1017 let nc: nx_int = nx_perlin_2d(ps, (gx + 5) * 340, gz * 340)
1018 var da: nx_int = na - nb; if da < 0 { da = 0 - da }
1019 var df: nx_int = na - nc; if df < 0 { df = 0 - df }
1020 adj = adj + da
1021 far = far + df
1022 gx = gx + 1
1023 }
1024 gz = gz + 1
1025 }
1026 if mx - mn < 120 { noiseOk = 0 } // varied, not flat
1027 if adj >= far { noiseOk = 0 } // adjacent more similar than distant = spatially coherent (Perlin hallmark, not white noise)
1028 if noiseOk == 1 { vp_puts("G4 OK: procedural Perlin terrain (deterministic + varied + spatially coherent)\n" as *u8) }
1029 else { pass = 0; vp_puts("G4 FAIL: noise not coherent/deterministic/varied\n" as *u8) }
1030
1031 // ===== precompute the Perlin height grid (shared by the render + AO neighbour lookup) =====
1032 let GN: nx_int = 12
1033 let hgrid: *i64 = (sys_mmap(GN * GN * 8)) as *i64
1034 var hz: nx_int = 0
1035 while hz < GN {
1036 var hx: nx_int = 0
1037 while hx < GN { hgrid[hx * GN + hz] = vp_h(ps, hx, hz); hx = hx + 1 }
1038 hz = hz + 1
1039 }
1040 // ===== precompute biomes from a SECOND Perlin field (composes nx_perlin again) =====
1041 let biomePs: *PerlinState = nx_perlin_alloc(NX_MAGIC_7777)
1042 let biomegrid: *i64 = (sys_mmap(GN * GN * 8)) as *i64
1043 var bgz: nx_int = 0
1044 while bgz < GN {
1045 var bgx: nx_int = 0
1046 while bgx < GN { biomegrid[bgx * GN + bgz] = vp_biome(biomePs, hget(hgrid, GN, bgx, bgz), bgx, bgz); bgx = bgx + 1 }
1047 bgz = bgz + 1
1048 }
1049
1050 // ===== GATE G5: voxel ambient occlusion (composes the raster's alpha-as-light feature) =====
1051 var aoOk: nx_int = 1
1052 if vp_ao(0) <= vp_ao(1) { aoOk = 0 } // monotonic darkening with occlusion
1053 if vp_ao(1) <= vp_ao(2) { aoOk = 0 }
1054 if vp_ao(2) <= vp_ao(3) { aoOk = 0 }
1055 var occluded: nx_int = 0
1056 var oz: nx_int = 0
1057 while oz < GN {
1058 var ox: nx_int = 0
1059 while ox < GN {
1060 let hh: nx_int = hget(hgrid, GN, ox, oz)
1061 if vp_occ(hgrid, GN, hh, ox-1, oz, ox, oz-1, ox-1, oz-1) > 0 { occluded = occluded + 1 }
1062 ox = ox + 1
1063 }
1064 oz = oz + 1
1065 }
1066 if occluded < 1 { aoOk = 0 } // the terrain actually casts AO somewhere
1067 if aoOk == 1 { vp_puts("G5 OK: voxel ambient occlusion (occluded corners darken, monotonic)\n" as *u8) }
1068 else { pass = 0; vp_puts("G5 FAIL: AO not monotonic / no occlusion\n" as *u8) }
1069
1070 // ===== GATE G6: biome system (a second noise field -> multiple terrain types) =====
1071 var biomeOk: nx_int = 1
1072 let bd1: nx_int = nx_perlin_2d(biomePs, 4 * 300, 9 * 300)
1073 let biomePsC: *PerlinState = nx_perlin_alloc(NX_MAGIC_7777)
1074 let bd2: nx_int = nx_perlin_2d(biomePsC, 4 * 300, 9 * 300)
1075 if bd1 != bd2 { biomeOk = 0 } // biome field is seed-deterministic
1076 var seen: nx_int = 0
1077 var sbz: nx_int = 0
1078 while sbz < GN {
1079 var sbx: nx_int = 0
1080 while sbx < GN { seen = seen | (1 << biomegrid[sbx * GN + sbz]); sbx = sbx + 1 }
1081 sbz = sbz + 1
1082 }
1083 var ndist: nx_int = 0
1084 var bk: nx_int = 0
1085 while bk < 5 { if ((seen >> bk) & 1) == 1 { ndist = ndist + 1 } bk = bk + 1 }
1086 if ndist < 2 { biomeOk = 0 } // the world contains multiple distinct biomes
1087 if biomeOk == 1 { vp_puts("G6 OK: biome system (deterministic, multiple distinct biomes present)\n" as *u8) }
1088 else { pass = 0; vp_puts("G6 FAIL: biomes not deterministic / not varied\n" as *u8) }
1089
1090 // ===== GATE G7: free-fly 6DOF camera (arbitrary pose projects the world validly + distinctly) =====
1091 let mvpA: *i64 = (sys_mmap(128)) as *i64
1092 let mvpB: *i64 = (sys_mmap(128)) as *i64
1093 vp_build_mvp(mvpA, 0, 14, 22, 24, 24) // the default 3/4 view
1094 vp_build_mvp(mvpB, 6, 16, 24, 0, 30) // a different pose: front-on, higher, steeper
1095 let qa: *i64 = (sys_mmap(32)) as *i64
1096 let qb: *i64 = (sys_mmap(32)) as *i64
1097 vp_project(mvpA, 6, 3, 6, qa) // same world point, two cameras
1098 vp_project(mvpB, 6, 3, 6, qb)
1099 var camOk: nx_int = 1
1100 if qa[3] != 1 { camOk = 0 } // both keep the point in front
1101 if qb[3] != 1 { camOk = 0 }
1102 if qa[0] == qb[0] { if qa[1] == qb[1] { camOk = 0 } } // and project it to DIFFERENT screen positions
1103 if camOk == 1 { vp_puts("G7 OK: free-fly 6DOF camera (arbitrary pose -> valid distinct projection)\n" as *u8) }
1104 else { pass = 0; vp_puts("G7 FAIL: camera not free / point lost\n" as *u8) }
1105
1106 // ===== GATE G8: AABB-vs-voxel collision (player can't sink through terrain) =====
1107 var colOk: nx_int = 1
1108 let fl: nx_int = vp_aabb_floor(hgrid, GN, 5, 5, 6, 6) // 2x2 footprint
1109 let above: nx_int = vp_collide_y(hgrid, GN, 5, 5, 6, 6, fl + 9) // a body above the floor is unaffected
1110 let below: nx_int = vp_collide_y(hgrid, GN, 5, 5, 6, 6, 0 - 5) // a body below is lifted to rest on it
1111 if above != fl + 9 { colOk = 0 }
1112 if below != fl { colOk = 0 }
1113 if fl < hget(hgrid, GN, 5, 5) { colOk = 0 } // floor = TALLEST column in footprint
1114 if fl < hget(hgrid, GN, 6, 6) { colOk = 0 }
1115 if colOk == 1 { vp_puts("G8 OK: AABB-vs-voxel collision (rests on the tallest column, no sink-through)\n" as *u8) }
1116 else { pass = 0; vp_puts("G8 FAIL: collision floor wrong\n" as *u8) }
1117
1118 // ===== GATE G9: infinite chunk streaming (deterministic + seamless boundary + far chunks valid) =====
1119 var chunkOk: nx_int = 1
1120 let chA: *i64 = (sys_mmap(8 * 8 * 8)) as *i64
1121 let chA2: *i64 = (sys_mmap(8 * 8 * 8)) as *i64
1122 let chB: *i64 = (sys_mmap(8 * 8 * 8)) as *i64
1123 vp_gen_chunk(ps, 0, 0, chA)
1124 vp_gen_chunk(ps, 0, 0, chA2)
1125 vp_gen_chunk(ps, 1, 0, chB)
1126 var ccz: nx_int = 0
1127 while ccz < 8 {
1128 if chA[ccz] != chA2[ccz] { chunkOk = 0 } // deterministic regen
1129 if chB[ccz] != vp_h(ps, 8, ccz) { chunkOk = 0 } // chunk(1,0) west col == global field => seamless
1130 ccz = ccz + 1
1131 }
1132 let chF: *i64 = (sys_mmap(8 * 8 * 8)) as *i64
1133 vp_gen_chunk(ps, 50, 50, chF) // a far-away chunk
1134 var cfi: nx_int = 0
1135 while cfi < 64 { if chF[cfi] < 1 { chunkOk = 0 } if chF[cfi] > 6 { chunkOk = 0 } cfi = cfi + 1 }
1136 if chunkOk == 1 { vp_puts("G9 OK: chunk streaming (deterministic + seamless boundary + far chunks valid)\n" as *u8) }
1137 else { pass = 0; vp_puts("G9 FAIL: chunks not seamless/deterministic\n" as *u8) }
1138
1139 // ===== GATE G10: interactive place/break world edit (mutates a live world, bounded) =====
1140 var editOk: nx_int = 1
1141 let sc: *i64 = (sys_mmap(8)) as *i64 // 1x1 scratch world (gn=1)
1142 sc[0] = 3
1143 vp_place(sc, 1, 0, 0); if sc[0] != 4 { editOk = 0 } // place raises
1144 vp_break(sc, 1, 0, 0); vp_break(sc, 1, 0, 0); if sc[0] != 2 { editOk = 0 } // break lowers
1145 sc[0] = 1; vp_break(sc, 1, 0, 0); if sc[0] != 1 { editOk = 0 } // can't break below 1
1146 sc[0] = 6; vp_place(sc, 1, 0, 0); if sc[0] != 6 { editOk = 0 } // can't place above 6
1147 if editOk == 1 { vp_puts("G10 OK: interactive place/break (edits the live voxel world, bounded)\n" as *u8) }
1148 else { pass = 0; vp_puts("G10 FAIL: world edit wrong\n" as *u8) }
1149
1150 // ===== GATE G11: greedy meshing (uniform regions collapse to one quad; exact on varied) =====
1151 var meshOk: nx_int = 1
1152 let hr: *i64 = (sys_mmap(8 * 8)) as *i64
1153 let bre: *i64 = (sys_mmap(8 * 8)) as *i64
1154 var mi: nx_int = 0
1155 while mi < 8 { hr[mi] = 3; bre[mi] = 0; mi = mi + 1 } // uniform row
1156 let uniform: nx_int = vp_greedy_row(hr, bre, 8)
1157 mi = 0
1158 while mi < 8 { hr[mi] = 3 + (mi & 1); bre[mi] = 0; mi = mi + 1 } // alternating row
1159 let altc: nx_int = vp_greedy_row(hr, bre, 8)
1160 if uniform != 1 { meshOk = 0 } // 8 naive quads -> 1 merged
1161 if altc != 8 { meshOk = 0 } // non-mergeable kept exactly
1162 if meshOk == 1 { vp_puts("G11 OK: greedy meshing (uniform run -> 1 quad not 8; exact on varied rows)\n" as *u8) }
1163 else { pass = 0; vp_puts("G11 FAIL: meshing wrong\n" as *u8) }
1164
1165 // ===== GATE G12: structure generation (deterministic, present, naturally spaced) =====
1166 var structOk: nx_int = 1
1167 let structPs: *PerlinState = nx_perlin_alloc(NX_MAGIC_909090)
1168 let structPsC: *PerlinState = nx_perlin_alloc(NX_MAGIC_909090)
1169 var nstruct: nx_int = 0
1170 var adjBad: nx_int = 0
1171 var stz: nx_int = 0
1172 while stz < GN {
1173 var stx: nx_int = 0
1174 while stx < GN {
1175 let aa: nx_int = vp_struct_at(structPs, hgrid, GN, stx, stz)
1176 if aa != vp_struct_at(structPsC, hgrid, GN, stx, stz) { structOk = 0 } // deterministic
1177 if aa == 1 {
1178 nstruct = nstruct + 1
1179 if vp_struct_at(structPs, hgrid, GN, stx + 1, stz) == 1 { adjBad = 1 } // spacing
1180 if vp_struct_at(structPs, hgrid, GN, stx, stz + 1) == 1 { adjBad = 1 }
1181 }
1182 stx = stx + 1
1183 }
1184 stz = stz + 1
1185 }
1186 if nstruct < 1 { structOk = 0 }
1187 if adjBad == 1 { structOk = 0 }
1188 if structOk == 1 { vp_puts("G12 OK: structure generation (deterministic, present, naturally spaced)\n" as *u8) }
1189 else { pass = 0; vp_puts("G12 FAIL: structures not present/spaced/deterministic\n" as *u8) }
1190
1191 // ===== GATE G13: dynamic day-night lighting (face brightness tracks the sun direction) =====
1192 var dayOk: nx_int = 1
1193 let noonTop: nx_int = vp_sun_face(0, 0, 100, 0) // sun overhead -> top brightest
1194 let noonPx: nx_int = vp_sun_face(1, 0, 100, 0) // -> +x face only ambient
1195 let setTop: nx_int = vp_sun_face(0, 100, 10, 0) // sun low in +x -> top dimmer
1196 let setPx: nx_int = vp_sun_face(1, 100, 10, 0) // -> +x face lit
1197 if noonTop <= setTop { dayOk = 0 } // top brighter at noon than at sunset
1198 if setPx <= noonPx { dayOk = 0 } // +x face brighter when the sun is in +x
1199 if dayOk == 1 { vp_puts("G13 OK: dynamic day-night light (face brightness tracks the sun direction)\n" as *u8) }
1200 else { pass = 0; vp_puts("G13 FAIL: lighting not dynamic\n" as *u8) }
1201
1202 // ===== GATE G14: thermal/hydraulic erosion (peaks wear down, mass conserved, deterministic) =====
1203 var eroOk: nx_int = 1
1204 let eg: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1205 let eg2: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1206 let edl: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1207 var ei: nx_int = 0
1208 while ei < 49 { eg[ei] = 1; eg2[ei] = 1; ei = ei + 1 }
1209 eg[3 * 7 + 3] = 9; eg2[3 * 7 + 3] = 9 // a sharp central spike on a flat plain
1210 var mass0: nx_int = 0
1211 ei = 0
1212 while ei < 49 { mass0 = mass0 + eg[ei]; ei = ei + 1 }
1213 var pc: nx_int = 0
1214 while pc < 4 { vp_erode_pass(eg, 7, 2, edl); pc = pc + 1 }
1215 var maxA: nx_int = 0
1216 var mass1: nx_int = 0
1217 ei = 0
1218 while ei < 49 { if eg[ei] > maxA { maxA = eg[ei] } mass1 = mass1 + eg[ei]; ei = ei + 1 }
1219 if maxA >= 9 { eroOk = 0 } // the spike was worn down
1220 if mass1 != mass0 { eroOk = 0 } // material moved, not created/destroyed
1221 var pc2: nx_int = 0
1222 while pc2 < 4 { vp_erode_pass(eg2, 7, 2, edl); pc2 = pc2 + 1 }
1223 ei = 0
1224 while ei < 49 { if eg[ei] != eg2[ei] { eroOk = 0 } ei = ei + 1 } // deterministic
1225 if eroOk == 1 { vp_puts("G14 OK: thermal erosion (spike worn down, mass conserved, deterministic)\n" as *u8) }
1226 else { pass = 0; vp_puts("G14 FAIL: erosion wrong\n" as *u8) }
1227
1228 // ===== GATE G15: ore-vein distribution (deterministic, rare, clustered into veins) =====
1229 var oreOk: nx_int = 1
1230 let orePs: *PerlinState = nx_perlin_alloc(NX_MAGIC_424242)
1231 let orePsC: *PerlinState = nx_perlin_alloc(NX_MAGIC_424242)
1232 if vp_ore(orePs, 3, 4, 2) != vp_ore(orePsC, 3, 4, 2) { oreOk = 0 } // deterministic
1233 var oreN: nx_int = 0
1234 var oreTot: nx_int = 0
1235 var maxRun: nx_int = 0
1236 var ovz: nx_int = 0
1237 while ovz < 12 {
1238 var ovx: nx_int = 0
1239 while ovx < 12 {
1240 var run: nx_int = 0
1241 var ovy: nx_int = 0
1242 while ovy < 6 {
1243 let o: nx_int = vp_ore(orePs, ovx, ovz, ovy)
1244 oreTot = oreTot + 1
1245 if o == 1 { oreN = oreN + 1; run = run + 1; if run > maxRun { maxRun = run } } else { run = 0 }
1246 ovy = ovy + 1
1247 }
1248 ovx = ovx + 1
1249 }
1250 ovz = ovz + 1
1251 }
1252 if oreN <= 0 { oreOk = 0 } // ore exists
1253 if oreN >= oreTot { oreOk = 0 } // but is not everywhere
1254 if maxRun < 2 { oreOk = 0 } // and clusters into veins, not single specks
1255 if oreOk == 1 { vp_puts("G15 OK: ore-vein distribution (deterministic, present, clustered into veins)\n" as *u8) }
1256 else { pass = 0; vp_puts("G15 FAIL: ore not vein-like\n" as *u8) }
1257
1258 // ===== GATE G21: cave-carving 3D-noise (deterministic, genuinely-3D, present-not-everywhere, tunnel-connected) =====
1259 var caveOk: nx_int = 1
1260 let cavePs: *PerlinState = nx_perlin_alloc(NX_MAGIC_515151)
1261 let cavePsC: *PerlinState = nx_perlin_alloc(NX_MAGIC_515151)
1262 if vp_cave(cavePs, 3, 4, 2) != vp_cave(cavePsC, 3, 4, 2) { caveOk = 0 } // deterministic (same seed -> same carve)
1263 var caveN: nx_int = 0
1264 var caveTot: nx_int = 0
1265 var caveMaxRun: nx_int = 0
1266 var caveDepthVary: nx_int = 0
1267 var cvz: nx_int = 0
1268 while cvz < 12 {
1269 var cvx: nx_int = 0
1270 while cvx < 12 {
1271 var crun: nx_int = 0
1272 var col0: nx_int = 0 - 1
1273 var coldiff: nx_int = 0
1274 var cvy: nx_int = 0
1275 while cvy < 6 {
1276 let cval: nx_int = vp_cave(cavePs, cvx, cvz, cvy)
1277 caveTot = caveTot + 1
1278 if cval == 1 { caveN = caveN + 1; crun = crun + 1; if crun > caveMaxRun { caveMaxRun = crun } } else { crun = 0 }
1279 if col0 < 0 { col0 = cval } else { if cval != col0 { coldiff = 1 } }
1280 cvy = cvy + 1
1281 }
1282 if coldiff == 1 { caveDepthVary = caveDepthVary + 1 } // this column's carve changes with depth = genuinely 3D
1283 cvx = cvx + 1
1284 }
1285 cvz = cvz + 1
1286 }
1287 if caveN <= 0 { caveOk = 0 } // caves exist
1288 if caveN >= caveTot { caveOk = 0 } // but don't hollow the whole world
1289 if caveMaxRun < 2 { caveOk = 0 } // they form connected tunnels, not isolated specks
1290 if caveDepthVary <= 0 { caveOk = 0 } // and are genuinely 3D: carve varies with depth (an extruded 2D mask would FAIL this)
1291 if caveOk == 1 { vp_puts("G21 OK: cave-carving 3D-noise (deterministic, genuinely-3D, present, tunnel-connected)\n" as *u8) }
1292 else { pass = 0; vp_puts("G21 FAIL: caves not 3D-tunnel-like\n" as *u8) }
1293
1294 // ===== GATE G22: wave-function-collapse procedural tiling (deterministic, fully-collapsed, all-adjacencies-valid, varied; tamper-caught) =====
1295 var wfcOk: nx_int = 1
1296 let WN: nx_int = 8
1297 let wcells: *i64 = (sys_mmap(WN * WN * 8)) as *i64
1298 let wcellsC: *i64 = (sys_mmap(WN * WN * 8)) as *i64
1299 let wmask: *i64 = (sys_mmap(WN * WN * 8)) as *i64
1300 let wmaskC: *i64 = (sys_mmap(WN * WN * 8)) as *i64
1301 if vp_wfc_solve(NX_MAGIC_31337, wcells, wmask, WN) != 1 { wfcOk = 0 } // solved with no contradiction
1302 if vp_wfc_solve(NX_MAGIC_31337, wcellsC, wmaskC, WN) != 1 { wfcOk = 0 }
1303 var wi: nx_int = 0
1304 while wi < WN * WN { if wcells[wi] != wcellsC[wi] { wfcOk = 0 } wi = wi + 1 } // deterministic (same seed -> same grid)
1305 var adjok: nx_int = 1
1306 var distinct: nx_int = 0
1307 var tt: nx_int = 0
1308 while tt < 4 {
1309 var found: nx_int = 0
1310 var mi: nx_int = 0
1311 while mi < WN * WN { if wcells[mi] == tt { found = 1 } mi = mi + 1 }
1312 if found == 1 { distinct = distinct + 1 }
1313 tt = tt + 1
1314 }
1315 var wz: nx_int = 0
1316 while wz < WN {
1317 var wx: nx_int = 0
1318 while wx < WN {
1319 let cv: nx_int = wcells[wz * WN + wx]
1320 if wx < WN - 1 { if vp_wfc_allow(cv, wcells[wz * WN + (wx + 1)]) == 0 { adjok = 0 } }
1321 if wz < WN - 1 { if vp_wfc_allow(cv, wcells[(wz + 1) * WN + wx]) == 0 { adjok = 0 } }
1322 wx = wx + 1
1323 }
1324 wz = wz + 1
1325 }
1326 if adjok == 0 { wfcOk = 0 } // every adjacent pair obeys the constraint
1327 if distinct < 2 { wfcOk = 0 } // non-trivial: >=2 tile types present (not an all-one-tile fill)
1328 if vp_wfc_allow(1, 0) != 0 { wfcOk = 0 } // tamper/neg control: an illegal pair (water-grass) IS rejected => the checker has teeth
1329 if vp_wfc_allow(0, 0) != 1 { wfcOk = 0 } // pos control: a legal pair is accepted
1330 if wfcOk == 1 { vp_puts("G22 OK: WFC tiling (deterministic, fully-collapsed, all-adjacencies-valid, varied; tamper-caught)\n" as *u8) }
1331 else { pass = 0; vp_puts("G22 FAIL: wfc\n" as *u8) }
1332
1333 // ===== GATE G23: marching-cubes SDF surface extraction (case index, boundary-only emission, Q10 zero-crossing interpolation) =====
1334 var mcOk: nx_int = 1
1335 let MCC: nx_int = 3
1336 let MCR: nx_int = 5
1337 if vp_mc_case(MCC, MCR, 0, 0, 0) != 0 { mcOk = 0 } // a fully-OUTSIDE cube...
1338 if vp_mc_active_edges(MCC, MCR, 0, 0, 0) != 0 { mcOk = 0 } // ...emits no surface
1339 if vp_mc_case(MCC, MCR, 2, 2, 2) != 255 { mcOk = 0 } // a fully-INSIDE cube...
1340 if vp_mc_active_edges(MCC, MCR, 2, 2, 2) != 0 { mcOk = 0 } // ...emits no surface
1341 let bcase: nx_int = vp_mc_case(MCC, MCR, 0, 3, 3) // a BOUNDARY cube straddles the isosurface
1342 if bcase <= 0 { mcOk = 0 }
1343 if bcase >= 255 { mcOk = 0 }
1344 if vp_mc_active_edges(MCC, MCR, 0, 3, 3) <= 0 { mcOk = 0 } // and emits surface vertices
1345 var mcBound: nx_int = 0
1346 var mcTot: nx_int = 0
1347 var mz: nx_int = 0
1348 while mz < 6 {
1349 var my: nx_int = 0
1350 while my < 6 {
1351 var mx: nx_int = 0
1352 while mx < 6 {
1353 if vp_mc_active_edges(MCC, MCR, mx, my, mz) > 0 { mcBound = mcBound + 1 }
1354 mcTot = mcTot + 1
1355 mx = mx + 1
1356 }
1357 my = my + 1
1358 }
1359 mz = mz + 1
1360 }
1361 if mcBound <= 0 { mcOk = 0 } // a surface exists...
1362 if mcBound >= mcTot { mcOk = 0 } // ...only on the shell (interior/exterior cubes empty)
1363 let mfa: nx_int = vp_mc_field(MCC, MCR, 0, 3, 3) // outside corner value
1364 let mfb: nx_int = vp_mc_field(MCC, MCR, 1, 3, 3) // inside corner value
1365 var signchg: nx_int = 0
1366 if mfa > 0 { if mfb <= 0 { signchg = 1 } } else { if mfb > 0 { signchg = 1 } }
1367 if signchg != 1 { mcOk = 0 } // the edge genuinely crosses the surface
1368 let mt: nx_int = vp_mc_interp_q10(mfa, mfb)
1369 if mt <= 0 { mcOk = 0 } // vertex strictly past corner A...
1370 if mt >= NX_MAGIC_1024 { mcOk = 0 } // ...and strictly before corner B = a real sub-voxel point
1371 if mcOk == 1 { vp_puts("G23 OK: marching-cubes SDF (case index, boundary-only surface, Q10 sub-voxel zero-crossing interpolation)\n" as *u8) }
1372 else { pass = 0; vp_puts("G23 FAIL: marching-cubes\n" as *u8) }
1373
1374 // ===== GATE G24: sovereign ray-traced reflection (mirror law r=d-2(d·n)n; energy-preserving; involution; reflected ray traced to a different surface) =====
1375 var rtOk: nx_int = 1
1376 let rv: *i64 = (sys_mmap(3 * 8)) as *i64
1377 let rv2: *i64 = (sys_mmap(3 * 8)) as *i64
1378 vp_reflect(1, 0, 0, 1, 1, 0, rv) // (a) general mirror law, non-axis 45-deg normal (1,1,0): +x ray -> -y
1379 if rv[0] != 0 { rtOk = 0 } // expected r*nlen2 = (0,-2,0)
1380 if rv[1] != 0 - 2 { rtOk = 0 }
1381 if rv[2] != 0 { rtOk = 0 }
1382 let elhs: nx_int = rv[0] * rv[0] + rv[1] * rv[1] + rv[2] * rv[2]
1383 if elhs != (1 * 1) * (2 * 2) { rtOk = 0 } // (b) energy preserved: |r_scaled|^2 == |d|^2 * nlen2^2
1384 vp_reflect(rv[0], rv[1], rv[2], 1, 1, 0, rv2) // (c) involution: reflecting twice returns the original direction (scaled)
1385 if rv2[0] != 4 { rtOk = 0 } // expected d * nlen2^2 = (1,0,0)*4 = (4,0,0)
1386 if rv2[1] != 0 { rtOk = 0 }
1387 if rv2[2] != 0 { rtOk = 0 }
1388 vp_reflect(0, 0 - 1, 1, 0, 1, 0, rv) // (d) TRACED reflection off a flat floor (normal (0,1,0)): d=(0,-1,1) -> (0,1,1)
1389 if rv[1] <= 0 { rtOk = 0 } // incident descended (dy<0); reflected ascends (ry>0)
1390 var py: nx_int = 5 // primary ray above the floor...
1391 var psteps: nx_int = 0
1392 while py > 0 { py = py + (0 - 1); psteps = psteps + 1; if psteps > 100 { py = 0 } } // ...descends to floor y=0
1393 var ryy: nx_int = 0 // reflected ray from the floor hit...
1394 var rsteps: nx_int = 0
1395 while ryy < 10 { ryy = ryy + rv[1]; rsteps = rsteps + 1; if rsteps > 100 { ryy = 10 } } // ...ascends to ceiling y=10
1396 if py > 0 { rtOk = 0 } // primary actually reached the floor
1397 if ryy < 10 { rtOk = 0 } // reflected ray actually reached the ceiling = sampled a DIFFERENT surface
1398 if rtOk == 1 { vp_puts("G24 OK: sovereign RT reflection (mirror law, energy-preserving, involution, reflected ray traced to a different surface)\n" as *u8) }
1399 else { pass = 0; vp_puts("G24 FAIL: rt-reflection\n" as *u8) }
1400
1401 // ===== GATE G25: octree-LOD sparse voxel octree over the SDF (homogeneity-collapse; sparse; depth-LOD monotonic; octant partition exact) =====
1402 var octOk: nx_int = 1
1403 let OCX: nx_int = 3
1404 let OCR: nx_int = 5
1405 let OW: nx_int = 8
1406 let ostk: *i64 = (sys_mmap(NX_MAGIC_4096 * 8)) as *i64
1407 let nFull: nx_int = 1 + 8 + 64 + 512 // a DENSE octree to depth 3 = 585 nodes
1408 let n3: nx_int = vp_octree_count(OCX, OCR, OW, 3, ostk) // sparse octree, max depth 3
1409 let n2: nx_int = vp_octree_count(OCX, OCR, OW, 2, ostk) // coarser LOD
1410 let n1: nx_int = vp_octree_count(OCX, OCR, OW, 1, ostk) // coarsest LOD
1411 if n3 >= nFull { octOk = 0 } // SPARSE: homogeneous interior/exterior collapse -> fewer than dense
1412 if n3 <= n2 { octOk = 0 } // depth-LOD MONOTONIC: deeper = more nodes (finer detail)
1413 if n2 <= n1 { octOk = 0 }
1414 if n1 != 9 { octOk = 0 } // depth-1: sphere spans the root -> root subdivides -> 1 + 8 = 9 exactly
1415 let cs: nx_int = OW / 2
1416 if 8 * (cs * cs * cs) != OW * OW * OW { octOk = 0 } // octant-partition conservation: 8 child cubes exactly tile the parent
1417 if octOk == 1 { vp_puts("G25 OK: octree-LOD (sparse SVO over SDF; homogeneity-collapse; depth-LOD monotonic; octant partition exact)\n" as *u8) }
1418 else { pass = 0; vp_puts("G25 FAIL: octree-lod\n" as *u8) }
1419
1420 // ===== GATE G26: runtime voxel destruction (region carve bounded+local; structural-integrity flood-fill: destroying a support floats the piece it carried) =====
1421 var vdOk: nx_int = 1
1422 let VG: nx_int = 8
1423 let occ: *i64 = (sys_mmap(VG * VG * VG * 8)) as *i64
1424 let vdReached: *i64 = (sys_mmap(VG * VG * VG * 8)) as *i64
1425 let vdStack: *i64 = (sys_mmap(VG * VG * VG * 8)) as *i64
1426 var vi: nx_int = 0
1427 while vi < VG * VG * VG { occ[vi] = 0; vi = vi + 1 }
1428 var gz: nx_int = 0
1429 while gz < VG {
1430 var gx: nx_int = 0
1431 while gx < VG { occ[gx + VG * (0 + VG * gz)] = 1; gx = gx + 1 } // solid ground layer y=0
1432 gz = gz + 1
1433 }
1434 occ[4 + VG * (1 + VG * 4)] = 1 // a pillar at (4,*,4), y=1..3...
1435 occ[4 + VG * (2 + VG * 4)] = 1
1436 occ[4 + VG * (3 + VG * 4)] = 1
1437 var sz: nx_int = 3
1438 while sz <= 5 {
1439 var sx: nx_int = 3
1440 while sx <= 5 { occ[sx + VG * (4 + VG * sz)] = 1; sx = sx + 1 } // ...carrying a 3x3 slab at y=4
1441 sz = sz + 1
1442 }
1443 let solidBefore: nx_int = vp_vd_count(occ, VG)
1444 let floatBefore: nx_int = vp_vd_floating(occ, vdReached, VG, vdStack)
1445 if floatBefore != 0 { vdOk = 0 } // POS control: the intact structure is fully grounded (pillar carries the slab)
1446 let removed: nx_int = vp_vd_blast(occ, VG, 4, 2, 4, 1) // blast the pillar (centre (4,2,4), r^2=1)
1447 let solidAfter: nx_int = vp_vd_count(occ, VG)
1448 if removed <= 0 { vdOk = 0 } // destruction actually removed voxels
1449 if solidAfter != solidBefore - removed { vdOk = 0 } // bookkeeping consistent
1450 if occ[0 + VG * (0 + VG * 0)] != 1 { vdOk = 0 } // LOCALITY: a far ground voxel (0,0,0) untouched
1451 if occ[4 + VG * (4 + VG * 4)] != 1 { vdOk = 0 } // LOCALITY: the slab voxel (4,4,4) untouched (blast was local to the pillar)
1452 let floatAfter: nx_int = vp_vd_floating(occ, vdReached, VG, vdStack)
1453 if floatAfter <= floatBefore { vdOk = 0 } // NEG control: destroying the support DISCONNECTS the slab -> it now floats
1454 if floatAfter < 9 { vdOk = 0 } // the whole 3x3 slab (9 voxels) is detected as floating (would fall)
1455 if vdOk == 1 { vp_puts("G26 OK: voxel destruction (region carve bounded+local; flood-fill detects the slab floating once its support is destroyed)\n" as *u8) }
1456 else { pass = 0; vp_puts("G26 FAIL: voxel-destruct\n" as *u8) }
1457
1458 // ===== GATE G27: Nanite cluster-LOD cut (distance-monotonic detail; complete + non-overlapping = watertight; coarsest within error budget) =====
1459 var nanOk: nx_int = 1
1460 let NLEAF: nx_int = 8
1461 let NNODE: nx_int = 15
1462 let nstk: *i64 = (sys_mmap(64 * 8)) as *i64
1463 let cover: *i64 = (sys_mmap(NLEAF * 8)) as *i64
1464 let KSCALE: nx_int = 400
1465 let TAU: nx_int = 3
1466 let cutFar: nx_int = vp_nanite_cut(NLEAF, NNODE, KSCALE, TAU, NX_MAGIC_4000, nstk, cover)
1467 var ncomplete: nx_int = 1
1468 var ci2: nx_int = 0
1469 while ci2 < NLEAF { if cover[ci2] != 1 { ncomplete = 0 } ci2 = ci2 + 1 }
1470 if ncomplete != 1 { nanOk = 0 } // FAR cut tiles every leaf region exactly once (watertight)
1471 let cutMid: nx_int = vp_nanite_cut(NLEAF, NNODE, KSCALE, TAU, 800, nstk, cover)
1472 ncomplete = 1
1473 ci2 = 0
1474 while ci2 < NLEAF { if cover[ci2] != 1 { ncomplete = 0 } ci2 = ci2 + 1 }
1475 if ncomplete != 1 { nanOk = 0 } // MID cut also watertight
1476 let cutNear: nx_int = vp_nanite_cut(NLEAF, NNODE, KSCALE, TAU, 200, nstk, cover)
1477 ncomplete = 1
1478 ci2 = 0
1479 while ci2 < NLEAF { if cover[ci2] != 1 { ncomplete = 0 } ci2 = ci2 + 1 }
1480 if ncomplete != 1 { nanOk = 0 } // NEAR cut also watertight
1481 if cutNear <= cutMid { nanOk = 0 } // distance-monotonic detail: nearer -> finer -> more clusters/triangles
1482 if cutMid <= cutFar { nanOk = 0 }
1483 if cutFar != 1 { nanOk = 0 } // far -> the whole object collapses to 1 root cluster
1484 if cutMid != 2 { nanOk = 0 } // mid -> the two half-clusters
1485 if cutNear != NLEAF { nanOk = 0 } // near -> every finest cluster (max detail)
1486 if nanOk == 1 { vp_puts("G27 OK: Nanite cluster-LOD cut (distance-monotonic detail; complete + non-overlapping = watertight; coarsest within error budget)\n" as *u8) }
1487 else { pass = 0; vp_puts("G27 FAIL: nanite-lod\n" as *u8) }
1488
1489 // ===== GATE G28: texture-atlas block textures (UV->texel addressing; bilinear filtering; tile isolation; gradient monotonic) =====
1490 var texOk: nx_int = 1
1491 let TTS: nx_int = 4
1492 let TAW: nx_int = 8 // 2 tiles * 4 texels wide
1493 let atlas: *i64 = (sys_mmap(TAW * TTS * 8)) as *i64
1494 var ty0: nx_int = 0
1495 while ty0 < TTS {
1496 var tx0: nx_int = 0
1497 while tx0 < TTS {
1498 atlas[ty0 * TAW + (0 * TTS + tx0)] = tx0 * 64 // tile 0: horizontal gradient 0,64,128,192
1499 atlas[ty0 * TAW + (1 * TTS + tx0)] = 255 // tile 1: constant white (distinct content)
1500 tx0 = tx0 + 1
1501 }
1502 ty0 = ty0 + 1
1503 }
1504 if vp_tex_sample(atlas, TAW, TTS, 0, 0, 0) != 0 { texOk = 0 } // (a) exact corner fetch (u=v=0 -> texel(0,0)=0)
1505 if vp_tex_sample(atlas, TAW, TTS, 0, 128, 0) != 96 { texOk = 0 } // (b) bilinear blend: between texel1(64) and texel2(128) -> 96
1506 if vp_tex_sample(atlas, TAW, TTS, 1, 128, 90) != 255 { texOk = 0 } // (c) flat tile 1 filters to exactly 255
1507 var prevs: nx_int = 0 - 1
1508 var mono: nx_int = 1
1509 var iso: nx_int = 1
1510 var uu: nx_int = 0
1511 while uu < 256 {
1512 let sV: nx_int = vp_tex_sample(atlas, TAW, TTS, 0, uu, 0)
1513 if sV > 192 { iso = 0 } // ISOLATION (no-wave control): tile-0 sampling never reads tile-1's 255
1514 if sV < prevs { mono = 0 } // gradient preserved: sample non-decreasing along u
1515 prevs = sV
1516 uu = uu + 16
1517 }
1518 if iso != 1 { texOk = 0 }
1519 if mono != 1 { texOk = 0 }
1520 if vp_tex_sample(atlas, TAW, TTS, 0, 200, 0) == vp_tex_sample(atlas, TAW, TTS, 1, 200, 0) { texOk = 0 } // (d) the two tiles genuinely differ
1521 if texOk == 1 { vp_puts("G28 OK: texture-atlas (UV->texel addressing, bilinear filtering, tile isolation, gradient monotonic)\n" as *u8) }
1522 else { pass = 0; vp_puts("G28 FAIL: textures\n" as *u8) }
1523
1524 // ===== GATE G16: fluid simulation (water flows, conserves volume, spreads, settles) =====
1525 var fluidOk: nx_int = 1
1526 let terr: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1527 let watr: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1528 let wdl: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1529 var fi: nx_int = 0
1530 while fi < 49 { terr[fi] = 0; watr[fi] = 0; fi = fi + 1 } // flat basin
1531 watr[3 * 7 + 3] = 14 // pour 14 water into the centre
1532 var w0: nx_int = 0
1533 fi = 0
1534 while fi < 49 { w0 = w0 + watr[fi]; fi = fi + 1 }
1535 var ws: nx_int = 0
1536 while ws < 200 { vp_water_step(terr, watr, 7, wdl); ws = ws + 1 } // plenty of steps to settle
1537 var w1: nx_int = 0
1538 var wet: nx_int = 0
1539 fi = 0
1540 while fi < 49 { w1 = w1 + watr[fi]; if watr[fi] > 0 { wet = wet + 1 } fi = fi + 1 }
1541 if w1 != w0 { fluidOk = 0 } // volume conserved
1542 if watr[3 * 7 + 3] >= 20 { fluidOk = 0 } // it spread out of the centre
1543 if wet < 4 { fluidOk = 0 } // wetted many cells (flowed)
1544 let chk: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1545 fi = 0
1546 while fi < 49 { chk[fi] = watr[fi]; fi = fi + 1 }
1547 vp_water_step(terr, watr, 7, wdl) // one more step...
1548 fi = 0
1549 while fi < 49 { if watr[fi] != chk[fi] { fluidOk = 0 } fi = fi + 1 } // ...changes nothing => settled
1550 if fluidOk == 1 { vp_puts("G16 OK: fluid simulation (water flows, conserves volume, spreads, settles)\n" as *u8) }
1551 else { pass = 0; vp_puts("G16 FAIL: fluid wrong\n" as *u8) }
1552
1553 // ===== render the real scene (Perlin terrain + AO + directional sun) =====
1554 nx_raster_clear(fb, NX_W, NX_H, vp_pack(120, 165, 220, 255)) // sky
1555 nx_raster_zbuffer_clear(zb, NX_W, NX_H)
1556 let mvp: *i64 = (sys_mmap(128)) as *i64
1557 vp_build_mvp(mvp, 0, 14, 22, 24, 24) // camera scaled to frame the 12x12 world (same angle)
1558 var z: nx_int = 0
1559 while z < GN {
1560 var x: nx_int = 0
1561 while x < GN {
1562 vp_column(fb, zb, mvp, hgrid, biomegrid, GN, x, z, 55, 70, 18) // afternoon sun: high, from +x/+z
1563 x = x + 1
1564 }
1565 z = z + 1
1566 }
1567
1568 // ===== GATE G17: sovereign SSAO post-process (we compute it; creases darken, sky untouched) =====
1569 let snap: *i64 = (sys_mmap(NX_W * NX_H * 8)) as *i64
1570 var ci: nx_int = 0
1571 while ci < NX_W * NX_H { snap[ci] = fb[ci]; ci = ci + 1 }
1572 vp_ssao(fb, zb, NX_W, NX_H)
1573 var ssaoOk: nx_int = 1
1574 var changed: nx_int = 0
1575 ci = 0
1576 while ci < NX_W * NX_H { if fb[ci] != snap[ci] { changed = changed + 1 } ci = ci + 1 }
1577 if fb[0] != snap[0] { ssaoOk = 0 } // top-left sky pixel (depth FAR) left untouched
1578 if changed < 50 { ssaoOk = 0 } // SSAO darkened a meaningful number of crease pixels
1579 if ssaoOk == 1 { vp_puts("G17 OK: sovereign SSAO (computed on OUR z-buffer; creases darken, sky kept)\n" as *u8) }
1580 else { pass = 0; vp_puts("G17 FAIL: ssao\n" as *u8) }
1581
1582 // ===== GATE G19: sovereign global illumination (one-bounce indirect light / colour bleeding) =====
1583 var giOk: nx_int = 1
1584 let gsrc: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1585 let gdst: *i64 = (sys_mmap(7 * 7 * 8)) as *i64
1586 var gg: nx_int = 0
1587 while gg < 49 { gsrc[gg] = vp_pack(200, 200, 200, 255); gg = gg + 1 }
1588 gsrc[3 * 7 + 3] = vp_pack(0, 0, 0, 255) // one dark pixel amid bright surfaces
1589 vp_gi(gdst, gsrc, 7, 7)
1590 let giCenter: nx_int = gdst[3 * 7 + 3] % 256
1591 let giBright: nx_int = gdst[2 * 7 + 2] % 256
1592 if giCenter <= 0 { giOk = 0 } // the dark spot picks up bounced light from neighbours
1593 if giCenter > 255 { giOk = 0 } // energy bounded
1594 if giBright < 150 { giOk = 0 } // bright surfaces stay bright (bounded bleed)
1595 if giOk == 1 { vp_puts("G19 OK: sovereign GI (indirect bounce lights dark surfaces from bright neighbours, bounded)\n" as *u8) }
1596 else { pass = 0; vp_puts("G19 FAIL: gi\n" as *u8) }
1597 let gisrc: *i64 = (sys_mmap(NX_W * NX_H * 8)) as *i64
1598 ci = 0
1599 while ci < NX_W * NX_H { gisrc[ci] = fb[ci]; ci = ci + 1 }
1600 vp_gi(fb, gisrc, NX_W, NX_H) // apply indirect bounce to the real frame
1601
1602 // ===== GATE G18: sovereign volumetric atmosphere (aerial perspective: distance haze) =====
1603 var atmosOk: nx_int = 1
1604 let afb: *i64 = (sys_mmap(3 * 8)) as *i64
1605 let azb: *i64 = (sys_mmap(3 * 8)) as *i64
1606 afb[0] = vp_pack(40, 40, 40, 255); azb[0] = NX_MAGIC_110000 // near geometry
1607 afb[1] = vp_pack(40, 40, 40, 255); azb[1] = NX_MAGIC_320000 // far geometry
1608 afb[2] = vp_pack(120, 165, 220, 255); azb[2] = NX_RASTER_Z_FAR // sky
1609 vp_atmos(afb, azb, 3, 1)
1610 let nbl: nx_int = (afb[0] / NX_MAGIC_65536) % 256 // near pixel blue
1611 let fbl: nx_int = (afb[1] / NX_MAGIC_65536) % 256 // far pixel blue
1612 if fbl <= nbl { atmosOk = 0 } // far hazes toward atmosphere MORE than near
1613 if nbl <= 40 { atmosOk = 0 } // near still picks up a touch of haze
1614 if afb[2] != vp_pack(120, 165, 220, 255) { atmosOk = 0 } // sky (depth FAR) left untouched
1615 if atmosOk == 1 { vp_puts("G18 OK: sovereign volumetric atmosphere (aerial perspective; far hazes, sky kept)\n" as *u8) }
1616 else { pass = 0; vp_puts("G18 FAIL: atmosphere\n" as *u8) }
1617 vp_atmos(fb, zb, NX_W, NX_H) // apply aerial perspective to the real frame
1618
1619 // ===== GATE G20: sovereign PBR specular materials (smooth shinier than rough; view-dependent; bounded) =====
1620 var pbrOk: nx_int = 1
1621 let smoothPeak: nx_int = vp_spec(20, 250) // smooth + aligned -> bright highlight
1622 let roughPeak: nx_int = vp_spec(230, 250) // rough + aligned -> dim
1623 let smoothOff: nx_int = vp_spec(20, 60) // smooth + off-angle -> dim
1624 if smoothPeak <= roughPeak { pbrOk = 0 } // smoother materials are shinier
1625 if smoothPeak <= smoothOff { pbrOk = 0 } // the highlight is view-dependent (peaks when aligned)
1626 if smoothPeak > 255 { pbrOk = 0 } // energy bounded
1627 if pbrOk == 1 { vp_puts("G20 OK: sovereign PBR specular (smooth>rough, view-dependent highlight, bounded)\n" as *u8) }
1628 else { pass = 0; vp_puts("G20 FAIL: pbr\n" as *u8) }
1629
1630 // GATE G3: scene is non-empty (terrain pixels differ from sky)
1631 let sky: nx_int = vp_pack(120, 165, 220, 255)
1632 var drawn: nx_int = 0
1633 var i: nx_int = 0
1634 let n: nx_int = NX_W * NX_H
1635 while i < n { if fb[i] != sky { drawn = drawn + 1 } i = i + 1 }
1636 if drawn > 1000 { vp_puts("G3 OK: terrain rendered (>1000 non-sky pixels)\n" as *u8) }
1637 else { pass = 0; vp_puts("G3 FAIL: scene empty\n" as *u8) }
1638
1639 let w: nx_int = vp_write_bmp(fb, "web_assets/_game_build/pets3d.bmp" as *u8)
1640 if w == 0 { vp_puts("BMP web_assets/_game_build/pets3d.bmp (320x240 true-3D voxel terrain)\n" as *u8) }
1641 else { pass = 0; vp_puts("BMP FAIL\n" as *u8) }
1642
1643 // ===== ORBIT TURNTABLE: 16 frames circling the world -> interactive browser viewer =====
1644 // camera orbits the centre (6,6) at radius R, yaw = -angle so it always faces inward (derived).
1645 let NF: nx_int = 16
1646 let pbuf: *u8 = sys_mmap(64)
1647 let omvp: *i64 = (sys_mmap(128)) as *i64
1648 var f: nx_int = 0
1649 while f < NF {
1650 let ang: nx_int = f * 360 / NF
1651 let camx: nx_int = 6 + (17 * nx_camera_sin_q14_deg(ang)) / NX_MAGIC_16384
1652 let camz: nx_int = 6 + (17 * nx_camera_cos_q14_deg(ang)) / NX_MAGIC_16384
1653 nx_raster_clear(fb, NX_W, NX_H, vp_pack(120, 165, 220, 255))
1654 nx_raster_zbuffer_clear(zb, NX_W, NX_H)
1655 vp_build_mvp(omvp, camx, 14, camz, 0 - ang, 24)
1656 var orz: nx_int = 0
1657 while orz < GN {
1658 var orx: nx_int = 0
1659 while orx < GN { vp_column(fb, zb, omvp, hgrid, biomegrid, GN, orx, orz, 55, 70, 18); orx = orx + 1 }
1660 orz = orz + 1
1661 }
1662 vp_ssao(fb, zb, NX_W, NX_H) // sovereign SSAO on every turntable frame
1663 vp_atmos(fb, zb, NX_W, NX_H) // sovereign aerial-perspective haze
1664 vp_frame_path(pbuf, f)
1665 vp_write_bmp(fb, pbuf)
1666 f = f + 1
1667 }
1668 vp_puts("ORBIT: 16 turntable frames written (frame_00.bmp .. frame_15.bmp)\n" as *u8)
1669
1670 if pass == 1 {
1671 // append the gate verdict to the voxel capability evidence log (the census reads this =
1672 // honest PRESENT only for capabilities that ACTUALLY ran green; never tutor-asserted)
1673 let lf: nx_int = sys_openat_append("knowledge/status/voxel3d.log" as *u8, 420)
1674 if lf >= 0 {
1675 vp_putfd(lf, "VOXEL3DGATE GREEN perspective-projection perspective-divide z-buffer-occlusion directional-light voxel-heightmap mat4-camera triangle-raster face-shading perlin-terrain-gen voxel-ao biome-gen free-camera aabb-collision chunk-stream world-edit greedy-mesh structure-gen dynamic-light hydraulic-erosion ore-gen fluid-sim ssao volumetric-sky gi-lighting pbr-material cave-gen wfc-gen marching-cubes rt-reflection octree-lod voxel-destruct nanite-lod texture-atlas\n" as *u8)
1676 sys_close(lf)
1677 }
1678 vp_puts("PETS3D2-OK: true 3D voxel terrain composes certified camera + z-raster\n" as *u8)
1679 return 0
1680 }
1681 vp_puts("PETS3D2-INCOMPLETE\n" as *u8)
1682 return 1
1683}