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}