code wiki / _hdl_build / nx_trimesh_gate.nx
nx_trimesh_gate.nx source
↩ module page · 158 lines · 9155 B
1// nx_trimesh_gate.nx -- ★prove the TRIANGLE shape kernel (operator: "we just have circles"). Now: real polygon meshes.
2// T1 FLAT FACES + SHARP EDGES: a cube renders as FLAT-shaded facets (few dominant exact colours over big regions) --
3// the thing an ellipsoid/SDF blob CANNOT do (a blob is a smooth gradient of many shades).
4// T2 Z-BUFFER: a NEAR mesh added FIRST is NOT overwritten by a FAR mesh added second -> depth (not draw order) decides.
5// T3 determinism. T4 shape gallery -> knowledge/nx_trimesh.png (cube|tetra|octa|pyramid|prism|icosa|sphere|figure).
6// license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_png.nx"
9import "nx_trimesh.nx"
10
11func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func 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 }
13func clearfb(fb: *i64, n: i64, c: i64) -> i64 { var i: i64=0; while i<n { fb[i]=c; i=i+1 } return 0 }
14func filled(fb: *i64, n: i64, bg: i64) -> i64 { var c: i64=0; var i: i64=0; while i<n { if fb[i]!=bg { c=c+1 } i=i+1 } return c }
15func bluish(fb: *i64, n: i64) -> i64 { var c: i64=0; var i: i64=0; while i<n { let p: i64=fb[i]; if (p>>16&255) > (p&255)+20 { c=c+1 } i=i+1 } return c }
16// count the most-common exact colour among non-bg pixels + how many distinct colours (flat faces => few, big counts)
17func modecount(fb: *i64, n: i64, bg: i64, outdistinct: *i64) -> i64 {
18 let CAP: i64 = 400
19 let cols: *i64 = sys_mmap(CAP*8) as *i64
20 let cnts: *i64 = sys_mmap(CAP*8) as *i64
21 var nd: i64 = 0
22 var i: i64 = 0
23 while i < n {
24 let p: i64 = fb[i]
25 if p != bg {
26 var k: i64 = 0; var found: i64 = 0
27 while k < nd { if cols[k]==p { cnts[k]=cnts[k]+1; found=1; k=nd } else { k=k+1 } }
28 if found == 0 { if nd < CAP { cols[nd]=p; cnts[nd]=1; nd=nd+1 } }
29 }
30 i = i + 1
31 }
32 var mx: i64 = 0; var k: i64 = 0
33 while k < nd { if cnts[k]>mx { mx=cnts[k] } k=k+1 }
34 outdistinct[0] = nd
35 return mx
36}
37
38// count interior FACET EDGES: adjacent non-bg pixels whose colour jumps hard (flat faceting = many; smooth gradient = few)
39func facetedges(fb: *i64, W: i64, H: i64, bg: i64) -> i64 {
40 var c: i64 = 0; var y: i64 = 0
41 while y < H {
42 var x: i64 = 0
43 while x < W-1 {
44 let p: i64 = fb[y*W+x]; let q: i64 = fb[y*W+x+1]
45 if p != bg { if q != bg {
46 var d: i64 = (p&255)-(q&255); if d<0 {d=0-d}
47 var d2: i64 = ((p>>8)&255)-((q>>8)&255); if d2<0 {d2=0-d2}
48 d = d + d2
49 if d > 20 { c = c + 1 }
50 } }
51 x = x + 1
52 }
53 y = y + 1
54 }
55 return c
56}
57
58const BG: i64 = 24 + 26*256 + 34*65536
59const CW: i64 = 220
60const CH: i64 = 240
61
62func main() -> i64 {
63 hw("=== nx_trimesh_gate -- sovereign TRIANGLE rasterizer (flat faces + z-buffer), not circles ===\n" as *u8)
64 var fails: i64 = 0
65 let npx: i64 = CW*CH
66 let fb: *i64 = sys_mmap(npx*8) as *i64
67 let zb: *i64 = sys_mmap(npx*8) as *i64
68
69 // ---- T1 FLAT FACES ----
70 tm_reset()
71 tm_cube(0, 0, 0, 150, 120 + 150*256 + 210*65536)
72 clearfb(fb, npx, BG); trimesh_zclear(zb, npx)
73 trimesh_render(fb, zb, CW, CH, 2400, 0-760, 1500, 520, 0)
74 let fil: i64 = filled(fb, npx, BG)
75 let dd: *i64 = sys_mmap(8) as *i64
76 let mode: i64 = modecount(fb, npx, BG, dd)
77 hw(" cube: tris="); pn(tm_nt()); hw(" filled="); pn(fil); hw(" top-face-colour-px="); pn(mode); hw(" distinct-colours="); pn(dd[0]); hw("\n" as *u8)
78 var t1: i64 = 0
79 if fil > 1000 { if mode*100/fil > 12 { if dd[0] < 60 { t1 = 1 } } } // one flat face = big single-colour region; few colours total
80 if t1 == 1 { hw("T1 PASS FLAT FACES: cube renders as flat facets w/ sharp edges (top face "); pn(mode*100/fil); hw("% of silhouette, "); pn(dd[0]); hw(" colours) -- an SDF blob cannot\n" as *u8) }
81 else { fails=fails+1; hw("T1 FAIL not faceted\n" as *u8) }
82
83 // ---- T2 Z-BUFFER: near cube added FIRST, far cube added SECOND, overlapping ----
84 tm_reset()
85 tm_cube(0-40, 0, 0-200, 150, 60 + 90*256 + 220*65536) // NEAR (smaller model z -> smaller view z), BLUE, added first
86 clearfb(fb, npx, BG); trimesh_zclear(zb, npx)
87 trimesh_render(fb, zb, CW, CH, 0, 0, 1500, 520, 0)
88 let blueNearOnly: i64 = bluish(fb, npx)
89 tm_cube(40, 0, 200, 160, 210 + 70*256 + 60*65536) // FAR (larger view z), RED, added SECOND, overlaps
90 clearfb(fb, npx, BG); trimesh_zclear(zb, npx)
91 trimesh_render(fb, zb, CW, CH, 0, 0, 1500, 520, 0)
92 let blueWithFar: i64 = bluish(fb, npx)
93 hw(" zbuffer: blue(near-only)="); pn(blueNearOnly); hw(" blue(near+far,far drawn 2nd)="); pn(blueWithFar); hw("\n" as *u8)
94 var t2: i64 = 0
95 if blueWithFar*100 >= blueNearOnly*94 { t2 = 1 } // z-buffer kept NEAR on top despite far drawn later
96 if t2 == 1 { hw("T2 PASS Z-BUFFER: near mesh survives a later-drawn far mesh (depth decides, not paint order)\n" as *u8) }
97 else { fails=fails+1; hw("T2 FAIL near punched through -> no working z-buffer\n" as *u8) }
98
99 // ---- T3 determinism ----
100 tm_reset(); tm_icosa(0,0,0,150, 150+200*256+120*65536)
101 let f1: *i64 = sys_mmap(npx*8) as *i64; let f2: *i64 = sys_mmap(npx*8) as *i64
102 clearfb(f1, npx, BG); trimesh_zclear(zb, npx); trimesh_render(f1, zb, CW, CH, 1700, 0-500, 1500, 520, 0)
103 clearfb(f2, npx, BG); trimesh_zclear(zb, npx); trimesh_render(f2, zb, CW, CH, 1700, 0-500, 1500, 520, 0)
104 var diff: i64 = 0; var i: i64 = 0
105 while i < npx { if f1[i]!=f2[i] { diff=diff+1 } i=i+1 }
106 var t3: i64 = 0
107 if diff == 0 { t3 = 1 }
108 if t3 == 1 { hw("T3 PASS deterministic\n" as *u8) } else { fails=fails+1; hw("T3 FAIL diff="); pn(diff); hw("\n" as *u8) }
109
110 // ---- T5 F3 GOURAUD (first ladder rung, built FROM the foundation census): smooth normals kill the faceting ----
111 tm_reset(); tm_uvsphere(0,0,0,150, 12, 18, 200+120*256+110*65536); tm_compute_normals()
112 clearfb(fb, npx, BG); trimesh_zclear(zb, npx); trimesh_render(fb, zb, CW, CH, 1500, 0-300, 1500, 520, 0)
113 let edgesFlat: i64 = facetedges(fb, CW, CH, BG)
114 clearfb(fb, npx, BG); trimesh_zclear(zb, npx); trimesh_render(fb, zb, CW, CH, 1500, 0-300, 1500, 520, 1)
115 let edgesSmooth: i64 = facetedges(fb, CW, CH, BG)
116 hw(" gouraud: sphere facet-edges flat="); pn(edgesFlat); hw(" smooth="); pn(edgesSmooth); hw("\n" as *u8)
117 var t5: i64 = 0
118 if edgesSmooth*2 < edgesFlat { if edgesFlat > 200 { t5 = 1 } } // smooth has FAR fewer internal facet edges
119 if t5 == 1 { hw("T5 PASS GOURAUD: per-vertex smooth normals remove the faceting (F3 rung: facets -> real surfaces)\n" as *u8) } else { fails=fails+1; hw("T5 FAIL flat="); pn(edgesFlat); hw(" smooth="); pn(edgesSmooth); hw("\n" as *u8) }
120
121 // ---- T4 shape gallery (4x2): cube tetra octa pyramid | prism icosa sphere figure ----
122 let COLS: i64 = 4; let ROWS: i64 = 2
123 let GW: i64 = CW*COLS; let GH: i64 = CH*ROWS
124 let gal: *i64 = sys_mmap(GW*GH*8) as *i64
125 clearfb(gal, GW*GH, BG)
126 let cell: *i64 = sys_mmap(npx*8) as *i64
127 var ci: i64 = 0
128 while ci < 8 {
129 tm_reset()
130 var camz: i64 = 1400
131 if ci == 0 { tm_cube(0,0,0,150, 120+150*256+210*65536) }
132 if ci == 1 { tm_tetra(0,0,0,175, 210+150*256+90*65536) }
133 if ci == 2 { tm_octa(0,0,0,170, 120+200*256+180*65536) }
134 if ci == 3 { tm_pyramid(0,0-20,0,160, 230+180*256+90*65536) }
135 if ci == 4 { tm_prism(0,0,0,160, 180+120*256+220*65536) }
136 if ci == 5 { tm_icosa(0,0,0,140, 150+200*256+120*65536) }
137 if ci == 6 { tm_uvsphere(0,0,0,150, 10, 14, 200+120*256+110*65536) }
138 if ci == 7 { tm_figure(0,40,0, 90+130*256+180*65536); camz = 3100 }
139 var sm: i64 = 0
140 if ci == 6 { sm = 1 } // render the sphere SMOOTH (Gouraud) -- the rest flat (polyhedra want facets)
141 if sm == 1 { tm_compute_normals() }
142 clearfb(cell, npx, BG); trimesh_zclear(zb, npx)
143 trimesh_render(cell, zb, CW, CH, 2500, 0-560, camz, 520, sm)
144 let cc: i64 = ci % COLS; let rr: i64 = (ci-cc)/COLS
145 let ox: i64 = cc*CW; let oy: i64 = rr*CH
146 var y: i64 = 0
147 while y < CH { var x: i64=0; while x<CW { gal[(oy+y)*GW+ox+x] = cell[y*CW+x]; x=x+1 } y=y+1 }
148 hw(" gallery cell "); pn(ci); hw(" tris="); pn(tm_nt()); hw("\n" as *u8)
149 ci = ci + 1
150 }
151 write_png(gal, GW, GH, "knowledge/nx_trimesh.png" as *u8)
152 hw("T4 gallery -> knowledge/nx_trimesh.png (cube|tetra|octa|pyramid|prism|icosa|sphere|figure)\n" as *u8)
153
154 if fails == 0 { hw("TRIMESH-GATE verdict=GREEN -- sovereign triangle rasterizer: flat faces, sharp edges, perspective, Z-BUFFER, primitive library + composition. We are no longer 'just circles'. Residual: textured faces + smooth (Gouraud) normals + .obj import.\n" as *u8); sys_exit(0); return 0 }
155 hw("TRIMESH-GATE RED fails="); pn(fails); hw("\n" as *u8)
156 sys_exit(1)
157 return 1
158}