code wiki / _hdl_build / nx_explodelab_material_gate.nx
nx_explodelab_material_gate.nx source
↩ module page · 224 lines · 7506 B
1// nx_explodelab_material_gate.nx -- teeth on R-V6 PER-PART MATERIALS + the DEPTH CUE.
2// A material system fails quietly in two ways: the table is filled in but every entry is effectively the
3// same (nothing differentiates, yet "materials" ships), or the per-vertex material id never reaches the
4// rasteriser so the table is dead data. Both look GREEN to any "does it render" check. So: prove the table
5// is differentiated, prove the id survives into the vertex data, prove metal and dielectric highlights
6// behave DIFFERENTLY, prove depth actually attenuates, and prove the frame changes when materials are
7// flattened to one. expect_exit: 0 license_tier: ORIGINAL
8import "nx_wasm_explodelab.nx"
9
10func ag_w(s: *u8) -> i64 {
11 var n: i64 = 0
12 while s[n] != (0 as u8) { n = n + 1 }
13 sys_write(1, s, n)
14 return 0
15}
16func ag_n(v: i64) -> i64 {
17 let buf: *u8 = sys_mmap(64)
18 var a: i64 = v
19 var neg: i64 = 0
20 if a < 0 {
21 neg = 1
22 a = 0 - a
23 }
24 var i: i64 = 31
25 if a == 0 {
26 buf[i] = 48 as u8
27 i = i - 1
28 }
29 while a > 0 {
30 buf[i] = (48 + (a % 10)) as u8
31 a = a / 10
32 i = i - 1
33 }
34 if neg == 1 {
35 buf[i] = 45 as u8
36 i = i - 1
37 }
38 sys_write(1, ((buf as i64) + i + 1) as *u8, 31 - i)
39 return 0
40}
41func ag_tooth(name: *u8, pass: i64, fails: *i64) -> i64 {
42 ag_w(" " as *u8)
43 ag_w(name)
44 ag_w(" -> " as *u8)
45 if pass == 1 {
46 ag_w("PASS\n" as *u8)
47 return 0
48 }
49 ag_w("FAIL\n" as *u8)
50 fails[0] = fails[0] + 1
51 return 0
52}
53func ag_chk(base: i64) -> i64 {
54 let fb: *i64 = (base + 0) as *i64
55 var x: i64 = 0
56 var i: i64 = 0
57 let n: i64 = ww() * hh()
58 while i < n {
59 x = x + fb[i] * (1 + (i % 7))
60 i = i + 1
61 }
62 return x
63}
64// how many of the 5 material fields differ between two kinds
65func ag_diff(k1: i64, k2: i64) -> i64 {
66 var d: i64 = 0
67 var f: i64 = 0
68 while f < 5 {
69 if exl_mat(k1, f) != exl_mat(k2, f) { d = d + 1 }
70 f = f + 1
71 }
72 return d
73}
74
75func main() -> i64 {
76 ag_w("=== nx_explodelab_material_gate -- per-part materials + depth cue ===\n" as *u8)
77 let fails: *i64 = sys_mmap(8) as *i64
78 fails[0] = 0
79 let base: i64 = sys_mmap(mem_bytes()) as i64
80 start_at(base)
81 let st: *i64 = st_ptr(base)
82 st[6] = 1
83 st[10] = 1
84 st[2] = 0
85 let e: *i64 = e_ptr(base)
86 let nrm: *i64 = (base + O_NRM) as *i64
87 let vst: *i64 = (base + O_VST) as *i64
88 let vct: *i64 = (base + O_VCT) as *i64
89 let nv: i64 = e[1]
90
91 // ---- T1 the table is genuinely DIFFERENTIATED (steel vs cast vs aluminium)
92 let d_sc: i64 = ag_diff(1, 4)
93 let d_sa: i64 = ag_diff(1, 5)
94 let d_ca: i64 = ag_diff(4, 5)
95 ag_w(" field diffs: steel/cast=" as *u8)
96 ag_n(d_sc)
97 ag_w(" steel/alu=" as *u8)
98 ag_n(d_sa)
99 ag_w(" cast/alu=" as *u8)
100 ag_n(d_ca)
101 ag_w(" of 5\n" as *u8)
102 var t1: i64 = 0
103 if d_sc >= 3 { if d_sa >= 3 { if d_ca >= 3 { t1 = 1 } } }
104 ag_tooth("T1 materials are genuinely distinct (>=3 of 5 fields differ between each pair)" as *u8, t1, fails)
105
106 // ---- T2 the per-vertex material id reaches the vertex data, and the smooth bit SURVIVED the packing
107 var wrongkind: i64 = 0
108 var anysmooth: i64 = 0
109 var p: i64 = 0
110 let np: i64 = exl_n()
111 while p < np {
112 let k: i64 = exl_kind(p)
113 var v: i64 = vst[p]
114 let vend: i64 = vst[p] + vct[p]
115 while v < vend {
116 if nrm[v * 4 + 3] / 2 != k { wrongkind = wrongkind + 1 }
117 if (nrm[v * 4 + 3] % 2) == 1 { anysmooth = anysmooth + 1 }
118 v = v + 1
119 }
120 p = p + 1
121 }
122 var t2: i64 = 0
123 if wrongkind == 0 { if anysmooth > 0 { t2 = 1 } }
124 ag_w(" (verts with wrong material id=" as *u8)
125 ag_n(wrongkind)
126 ag_w(" still-smooth=" as *u8)
127 ag_n(anysmooth)
128 ag_w(" of " as *u8)
129 ag_n(nv)
130 ag_w(")\n" as *u8)
131 ag_tooth("T2 material id packs into the vertex WITHOUT destroying the smooth-shading bit" as *u8, t2, fails)
132
133 // ---- T3 METAL vs DIELECTRIC: a metal tints its highlight by its own albedo, a dielectric adds white.
134 // With a saturated RED albedo the metal highlight must lift red far more than blue; the dielectric
135 // must lift them equally.
136 let red: i64 = 4278190080 + 200
137 let met: i64 = exl_shade2(red, 128, 200, 1, 256)
138 let die: i64 = exl_shade2(red, 128, 200, 0, 256)
139 let mr: i64 = met % 256
140 let mb: i64 = (met / 65536) % 256
141 let dr: i64 = die % 256
142 let db: i64 = (die / 65536) % 256
143 ag_w(" (metal r=" as *u8)
144 ag_n(mr)
145 ag_w(" b=" as *u8)
146 ag_n(mb)
147 ag_w(" dielectric r=" as *u8)
148 ag_n(dr)
149 ag_w(" b=" as *u8)
150 ag_n(db)
151 ag_w(")\n" as *u8)
152 var t3: i64 = 0
153 if mr - mb > 40 { if db - dr == 0 - (dr - db) { if db >= 100 { t3 = 1 } } }
154 ag_tooth("T3 metal tints its specular by albedo; dielectric stays white" as *u8, t3, fails)
155
156 // ---- T4 specular tightness really tightens: for a partial cosine, broad > medium > tight
157 let s0: i64 = exl_specp(200, 0)
158 let s1: i64 = exl_specp(200, 1)
159 let s2v: i64 = exl_specp(200, 2)
160 var t4: i64 = 0
161 if s0 > s1 { if s1 > s2v { t4 = 1 } }
162 ag_w(" (cos=200 -> broad " as *u8)
163 ag_n(s0)
164 ag_w(" med " as *u8)
165 ag_n(s1)
166 ag_w(" tight " as *u8)
167 ag_n(s2v)
168 ag_w(")\n" as *u8)
169 ag_tooth("T4 specular tightness is monotonic (broad > medium > tight)" as *u8, t4, fails)
170
171 // ---- T5 DEPTH CUE attenuates: same vertex, same material, deeper z -> darker
172 let scr: *i64 = (base + O_SCR) as *i64
173 let ig: i64 = render_at(base)
174 // ⚠set BOTH depths explicitly. Using the vertex's real (large) projected depth as the "near" case made
175 // both ends clamp to the same fog floor -- the tooth then compared two identical values and called the
176 // feature dead. Same one-variable discipline as everywhere else in this arc.
177 let zsave: i64 = scr[2]
178 scr[2] = 0
179 let near: i64 = exl_corner_col((base + O_WV) as *i64, nrm, scr, 0, 0, 0, NRM_Q, 0, 1000)
180 scr[2] = 1000
181 let far: i64 = exl_corner_col((base + O_WV) as *i64, nrm, scr, 0, 0, 0, NRM_Q, 0, 1000)
182 scr[2] = zsave
183 var t5: i64 = 0
184 if (far % 256) < (near % 256) { t5 = 1 }
185 ag_w(" (near r=" as *u8)
186 ag_n(near % 256)
187 ag_w(" far r=" as *u8)
188 ag_n(far % 256)
189 ag_w(")\n" as *u8)
190 ag_tooth("T5 DEPTH CUE: the same surface renders darker further away" as *u8, t5, fails)
191
192 // ---- T6 NON-VACUITY: flatten every vertex to ONE material and the frame must change
193 let real: i64 = ag_chk(base)
194 var i2: i64 = 0
195 while i2 < nv {
196 nrm[i2 * 4 + 3] = (nrm[i2 * 4 + 3] % 2) + 2
197 i2 = i2 + 1
198 }
199 let ig2: i64 = render_at(base)
200 let flat: i64 = ag_chk(base)
201 var t6: i64 = 0
202 if flat != real { t6 = 1 }
203 ag_tooth("T6 NON-VACUITY: flattening all parts to one material CHANGES the frame" as *u8, t6, fails)
204
205 // ---- T7 determinism after a clean rebuild
206 start_at(base)
207 st[6] = 1
208 st[10] = 1
209 st[2] = 0
210 let ig3: i64 = render_at(base)
211 var t7: i64 = 0
212 if ag_chk(base) == real { t7 = 1 }
213 ag_tooth("T7 deterministic: rebuild reproduces the material frame exactly" as *u8, t7, fails)
214
215 ag_w("\nfails=" as *u8)
216 ag_n(fails[0])
217 ag_w("\n" as *u8)
218 if fails[0] == 0 {
219 ag_w("GREEN -- materials 7/7 (steel/cast/aluminium distinct, metal tint, depth cue live)\n" as *u8)
220 return 0
221 }
222 ag_w("RED\n" as *u8)
223 return 1
224}