code wiki / _hdl_build / nx_cornell_dyn_gate.nx
nx_cornell_dyn_gate.nx source
↩ module page · 121 lines · 6126 B
1// nx_cornell_dyn_gate.nx -- ★R3 DYNAMIC GI (the Lumen direction, taken the fusion way): the radiosity REACTS to scene
2// changes. Form factors are CACHED (the Surface-Cache move); a moved LIGHT invalidates only its own pairs (~20x less
3// work, integer-EXACTLY equal to a from-scratch rebuild -- a proof only a bit-exact stack can make); moved GEOMETRY
4// recomputes occlusion and the SHADOW RELOCATES. All effects measured on patch radiosities, evidence rendered.
5// T1 light moves -> bleeding shifts (incremental). T2 incremental == full rebuild EXACTLY. T3 block moves -> shadow
6// relocates. T4 evidence (baseline | light moved | block moved). license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_png.nx"
9import "nx_trimesh.nx"
10import "nx_cornell.nx"
11
12func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
14func clearfb(fb: *i64, n: i64, c: i64) -> i64 { var i: i64=0; while i<n { fb[i]=c; i=i+1 } return 0 }
15
16const BG: i64 = 10 + 10*256 + 14*65536
17const W: i64 = 360
18const H: i64 = 380
19
20func col_rg(i: i64) -> i64 {
21 let b3: *i64 = sys_mmap(24) as *i64
22 var s: i64 = 0
23 var j: i64 = 0
24 while j < 12 { cor_b(200 + i*12 + j, b3); s = s + (b3[0] - b3[1]); j = j + 1 }
25 return s/12
26}
27func floor_near(x: i64, z: i64) -> i64 {
28 let c3: *i64 = sys_mmap(24) as *i64
29 var best: i64 = 0-1
30 var bd: i64 = 9000000000
31 var i: i64 = 0
32 while i < 100 {
33 cor_center(i, c3)
34 let dx: i64 = c3[0]-x
35 let dz: i64 = c3[2]-z
36 let d: i64 = dx*dx + dz*dz
37 if d < bd { bd = d; best = i }
38 i = i + 1
39 }
40 return best
41}
42func lum(i: i64) -> i64 { let b3: *i64 = sys_mmap(24) as *i64; cor_b(i, b3); return (b3[0]+b3[1]+b3[2])/3 }
43func rendpanel(gal: *i64, GW: i64, slot: i64) -> i64 {
44 let npx: i64 = W*H
45 let fb: *i64 = sys_mmap(npx*8) as *i64
46 let zb: *i64 = sys_mmap(npx*8) as *i64
47 cornell_tomesh(1900)
48 clearfb(fb, npx, BG)
49 trimesh_zclear(zb, npx)
50 trimesh_render(fb, zb, W, H, 0, 0, 800, 500, 3)
51 var y: i64 = 0
52 while y < H { var x: i64=0; while x<W { gal[y*GW+slot*W+x]=fb[y*W+x]; x=x+1 } y=y+1 }
53 return 0
54}
55
56func main() -> i64 {
57 hw("=== nx_cornell_dyn_gate -- R3 DYNAMIC GI: cached form factors + incremental invalidation, exact ===\n" as *u8)
58 var fails: i64 = 0
59 tm_set_spec(0)
60 tm_set_tex(0)
61 tm_set_image(0 as *u8, 0, 0)
62 let np: i64 = cornell_build()
63 let fullWork: i64 = cornell_formfactors()
64 cornell_solve(4)
65 let GW: i64 = W*3
66 let gal: *i64 = sys_mmap(GW*H*8) as *i64
67 clearfb(gal, GW*H, BG)
68 rendpanel(gal, GW, 0)
69 let base_red: i64 = col_rg(0)
70 let base_grn: i64 = 0 - col_rg(11)
71 hw(" baseline: patches="); pn(np); hw(" F-pairs="); pn(fullWork); hw(" red-side bleed=+"); pn(base_red); hw(" green-side=+"); pn(base_grn); hw("\n" as *u8)
72
73 // ---- T1 DYNAMIC LIGHT: slide the lamp toward the RED wall; invalidate only its pairs ----
74 cornell_move_light(0-140, 0)
75 let partWork: i64 = cornell_ff_partial(544, 560)
76 cornell_solve(4)
77 rendpanel(gal, GW, 1)
78 let l_red: i64 = col_rg(0)
79 let l_grn: i64 = 0 - col_rg(11)
80 hw(" light moved toward red: red-side=+"); pn(l_red); hw(" green-side=+"); pn(l_grn); hw(" incremental work="); pn(partWork); hw(" pairs (full="); pn(fullWork); hw(", x10 ratio="); pn(fullWork*10/partWork); hw(")\n" as *u8)
81 var t1: i64 = 0
82 if l_red > base_red { if l_grn < base_grn { if partWork*10 < fullWork { t1 = 1 } } }
83 if t1 == 1 { hw("T1 PASS DYNAMIC LIGHT: the GI field REACTS (red bleed up, green down) via incremental invalidation (>10x less work than rebuild)\n" as *u8) }
84 else { fails=fails+1; hw("T1 FAIL\n" as *u8) }
85
86 // ---- T2 EXACTNESS: incremental result == full rebuild, bit for bit ----
87 let keep_red: i64 = l_red
88 let keep_grn: i64 = l_grn
89 let keep_f15: i64 = lum(floor_near(0-230, 330))
90 cornell_formfactors()
91 cornell_solve(4)
92 var t2: i64 = 0
93 if col_rg(0) == keep_red { if (0-col_rg(11)) == keep_grn { if lum(floor_near(0-230,330)) == keep_f15 { t2 = 1 } } }
94 if t2 == 1 { hw("T2 PASS EXACT: incremental invalidation == from-scratch rebuild BIT-FOR-BIT (integer determinism -- a proof float engines cannot make)\n" as *u8) }
95 else { fails=fails+1; hw("T2 FAIL\n" as *u8) }
96
97 // ---- T3 DYNAMIC GEOMETRY: slide the tall block +300 -> the shadow must RELOCATE ----
98 let oldSh: i64 = floor_near(0-230, 330)
99 let newSh: i64 = floor_near(350, 330)
100 let preOld: i64 = lum(oldSh)
101 let preNew: i64 = lum(newSh)
102 cornell_move_tall(300)
103 cornell_formfactors() // occlusion changed globally -> full recompute (honest)
104 cornell_solve(4)
105 rendpanel(gal, GW, 2)
106 let postOld: i64 = lum(oldSh)
107 let postNew: i64 = lum(newSh)
108 hw(" block moved +300: old-shadow lum "); pn(preOld); hw("->"); pn(postOld); hw(" new-shadow lum "); pn(preNew); hw("->"); pn(postNew); hw("\n" as *u8)
109 var t3: i64 = 0
110 if postOld*10 > preOld*14 { if postNew*10 < preNew*7 { t3 = 1 } }
111 if t3 == 1 { hw("T3 PASS DYNAMIC GEOMETRY: the shadow RELOCATES with the moved occluder (old spot brightens >=1.4x, new spot darkens <=0.7x)\n" as *u8) }
112 else { fails=fails+1; hw("T3 FAIL\n" as *u8) }
113
114 write_png(gal, GW, H, "knowledge/nx_cornell_dyn.png" as *u8)
115 hw("T4 evidence -> knowledge/nx_cornell_dyn.png (baseline | light moved | block moved)\n" as *u8)
116
117 if fails == 0 { hw("CORNELL-DYN-GATE GREEN -- R3 dynamic GI: cached form factors + incremental invalidation (Surface-Cache-kin), light AND geometry changes re-light the scene correctly, incremental == rebuild EXACTLY. Residual vs Lumen: per-frame scale, specular reflections, screen traces.\n" as *u8); sys_exit(0); return 0 }
118 hw("CORNELL-DYN-GATE RED fails="); pn(fails); hw("\n" as *u8)
119 sys_exit(1)
120 return 1
121}