code wiki / _hdl_build / nx_render3d.nx
nx_render3d.nx source
↩ module page · 213 lines · 9482 B
1// nx_render3d.nx -- THE 3D RENDER ORGAN: sovereign first-byte-up 3D as an INVOCABLE SERVICE (CLI -> tools-api
2// MCP -> HTTP tools/call), composing the SAME Q14 core family (nx_render_core via nx_meshrender) that the live
3// browser wasm page runs -- one core, three surfaces: NishiOS native, Nishi tools/MCP, 3rd-party browser (wasm,
4// JS shim = last mile only). Renders a per-face-Lambert lit cube (mirrors the VM-parity-gated mv_render_cube)
5// or a z-buffer proof scene, at ANY size/angle, to PNG. Pure integer, no gcc/GL; runs natively on the NAS.
6// usage: nx_render3d <mode:cube|ztri> <angle_deg> <size_px> <out.png> [url]
7// stdout: one JSON line {tool,mode,angle,size,drawn,coverage,out,url} (the tools/call return).
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_meshrender.nx"
11import "nx_png_write.nx"
12const R3_MAGIC_16384000: i64 = 16384000
13const R3_MAGIC_4278190335: i64 = 4278190335
14const R3_MAGIC_4294901760: i64 = 4294901760
15const R3_MAGIC_12000: i64 = 12000
16const R3_MAGIC_2000: i64 = 2000
17const R3_MAGIC_65536: i64 = 65536
18const R3_MAGIC_2048: i64 = 2048
19
20const R3_FOVH: i64 = 30 // half-FOV degrees (matches the live wasm page)
21const R3_DIST: i64 = 65536 // camera distance 4.0 Q14
22const R3_HALF: i64 = 9830 // cube half-extent 0.6 Q14
23const R3_BASE: i64 = 4292004040 // cube base material (200,200,210) -- shading reveals form
24const R3_BG: i64 = 4280295456 // background (32,33,32)-ish, a255
25const R3_AMB: i64 = 2048 // ambient floor Q14 (~12.5%)
26const R3_LX: i64 = 9459 // unit light from upper-right-front (0.577,0.577,0.577) Q14
27const R3_MAXSZ: i64 = 1024 // size clamp: 1024x1024 fb = 8MB i64 -- bounded for the tools-api
28
29func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
30func slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
31func seq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]==(0 as u8) { return 1 } return 0 }
32func satoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; var sg: i64=1; if s[0]==(45 as u8){sg=0-1;i=1} while s[i]>=(48 as u8) { if s[i]>(57 as u8) { return v*sg } v=v*10+((s[i] as i64)-48); i=i+1 } return v*sg }
33func jput(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[o+i]=s[i];i=i+1} return o+i }
34func jputn(dst: *u8, o: i64, v: i64) -> i64 {
35 var m: i64=v
36 var oo: i64=o
37 if m<0 { dst[oo]=45 as u8; oo=oo+1; m=0-m }
38 let t: *u8=sys_mmap(24)
39 var k: i64=0
40 if m==0 { t[0]=48 as u8; k=1 }
41 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
42 var j: i64=0
43 while j<k { dst[oo+j]=t[k-1-j]; j=j+1 }
44 return oo+k
45}
46
47// stage the 12-tri cube: verts (8x3) + tris (12x7: i0,i1,i2,nx,ny,nz,rgba)
48func r3_cube_scene(v: *i64, t: *i64) -> i64 {
49 let H: i64 = R3_HALF
50 let Q: i64 = RC_Q
51 v[0]=0-H; v[1]=0-H; v[2]=0-H
52 v[3]=H; v[4]=0-H; v[5]=0-H
53 v[6]=H; v[7]=H; v[8]=0-H
54 v[9]=0-H; v[10]=H; v[11]=0-H
55 v[12]=0-H; v[13]=0-H; v[14]=H
56 v[15]=H; v[16]=0-H; v[17]=H
57 v[18]=H; v[19]=H; v[20]=H
58 v[21]=0-H; v[22]=H; v[23]=H
59 var k: i64 = 0
60 while k < 84 { t[k]=0; k=k+1 }
61 // faces: -Z +Z -X +X -Y +Y (2 tris each), outward normals, base color
62 t[0]=0;t[1]=1;t[2]=2; t[3]=0;t[4]=0;t[5]=0-Q; t[6]=R3_BASE
63 t[7]=0;t[8]=2;t[9]=3; t[10]=0;t[11]=0;t[12]=0-Q; t[13]=R3_BASE
64 t[14]=4;t[15]=6;t[16]=5; t[17]=0;t[18]=0;t[19]=Q; t[20]=R3_BASE
65 t[21]=4;t[22]=7;t[23]=6; t[24]=0;t[25]=0;t[26]=Q; t[27]=R3_BASE
66 t[28]=0;t[29]=3;t[30]=7; t[31]=0-Q;t[32]=0;t[33]=0; t[34]=R3_BASE
67 t[35]=0;t[36]=7;t[37]=4; t[38]=0-Q;t[39]=0;t[40]=0; t[41]=R3_BASE
68 t[42]=1;t[43]=5;t[44]=6; t[45]=Q;t[46]=0;t[47]=0; t[48]=R3_BASE
69 t[49]=1;t[50]=6;t[51]=2; t[52]=Q;t[53]=0;t[54]=0; t[55]=R3_BASE
70 t[56]=0;t[57]=4;t[58]=5; t[59]=0;t[60]=0-Q;t[61]=0; t[62]=R3_BASE
71 t[63]=0;t[64]=5;t[65]=1; t[66]=0;t[67]=0-Q;t[68]=0; t[69]=R3_BASE
72 t[70]=3;t[71]=2;t[72]=6; t[73]=0;t[74]=Q;t[75]=0; t[76]=R3_BASE
73 t[77]=3;t[78]=6;t[79]=7; t[80]=0;t[81]=Q;t[82]=0; t[83]=R3_BASE
74 return 0
75}
76
77// lit cube at angle into fb/zb (w x h). Mirrors the VM-parity-gated mv_render_cube; parameterized buffers/size.
78func r3_render_cube(fb: *i64, zb: *i64, w: i64, h: i64, angle: i64) -> i64 {
79 rc_clear(fb, w, h, R3_BG)
80 rc_zclear(zb, w, h)
81 let v: *i64 = sys_mmap(8*3*8) as *i64
82 let tris: *i64 = sys_mmap(12*7*8) as *i64
83 r3_cube_scene(v, tris)
84 let proj: *i64 = sys_mmap(128) as *i64
85 let roty: *i64 = sys_mmap(128) as *i64
86 let mvm: *i64 = sys_mmap(128) as *i64
87 let trans: *i64 = sys_mmap(128) as *i64
88 let mvp: *i64 = sys_mmap(128) as *i64
89 let vbuf: *i64 = sys_mmap(32) as *i64
90 let clipbuf: *i64 = sys_mmap(32) as *i64
91 let scr: *i64 = sys_mmap(8*4*8) as *i64
92 let tri: *i64 = sys_mmap(96) as *i64
93 let nrm: *i64 = sys_mmap(32) as *i64
94 rc_perspective_cs(rc_cos_q14(R3_FOVH), rc_sin_q14(R3_FOVH), w*RC_Q/h, RC_Q, R3_MAGIC_16384000, proj)
95 let cc: i64 = rc_cos_q14(angle)
96 let ss: i64 = rc_sin_q14(angle)
97 var z: i64 = 0
98 while z < 16 { roty[z]=0; z=z+1 }
99 roty[0]=cc; roty[2]=ss; roty[5]=RC_Q; roty[8]=0-ss; roty[10]=cc; roty[15]=RC_Q
100 rc_translation_4x4(0, 0, 0-R3_DIST, trans)
101 rc_mat4_mul(trans, roty, mvm)
102 rc_mat4_mul(proj, mvm, mvp)
103 var vi: i64 = 0
104 while vi < 8 { mr_project(mvp, v[vi*3+0], v[vi*3+1], v[vi*3+2], w, h, vbuf, clipbuf, scr, vi*4); vi=vi+1 }
105 var drawn: i64 = 0
106 var ti: i64 = 0
107 while ti < 12 {
108 let a: i64 = tris[ti*7+0]
109 let b: i64 = tris[ti*7+1]
110 let c: i64 = tris[ti*7+2]
111 var vis: i64 = 0
112 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } }
113 if vis==1 {
114 mr_rotn(cc, ss, tris[ti*7+3], tris[ti*7+4], tris[ti*7+5], nrm)
115 if nrm[2] > 0 {
116 let br: i64 = mr_lambert(nrm[0], nrm[1], nrm[2], R3_LX, R3_LX, R3_LX)
117 let col: i64 = mr_shade(tris[ti*7+6], br, R3_AMB)
118 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2]
119 tri[3]=scr[b*4+0]; tri[4]=scr[b*4+1]; tri[5]=scr[b*4+2]
120 tri[6]=scr[c*4+0]; tri[7]=scr[c*4+1]; tri[8]=scr[c*4+2]
121 tri[9]=col
122 rc_triangle_flat(fb, zb, w, h, tri)
123 drawn = drawn + 1
124 }
125 }
126 ti = ti + 1
127 }
128 return drawn
129}
130
131// z-buffer proof: near RED tri over far BLUE tri, overlapping centre -- screen-space, scaled by size
132func r3_render_ztri(fb: *i64, zb: *i64, w: i64, h: i64) -> i64 {
133 rc_clear(fb, w, h, R3_BG)
134 rc_zclear(zb, w, h)
135 let RED: i64 = R3_MAGIC_4278190335
136 let BLU: i64 = R3_MAGIC_4294901760
137 let tri: *i64 = sys_mmap(96) as *i64
138 // far BLUE first (z=12000), apex-up
139 tri[0]=w/12; tri[1]=h-h/12; tri[2]=R3_MAGIC_12000; tri[3]=BLU
140 tri[4]=w-w/12; tri[5]=h-h/12; tri[6]=R3_MAGIC_12000; tri[7]=BLU
141 tri[8]=w/2; tri[9]=h/8; tri[10]=R3_MAGIC_12000; tri[11]=BLU
142 rc_triangle(fb, zb, w, h, tri)
143 // near RED second (z=2000), apex-down -- must WIN the overlap
144 tri[0]=w/12; tri[1]=h/12; tri[2]=R3_MAGIC_2000; tri[3]=RED
145 tri[4]=w-w/12; tri[5]=h/12; tri[6]=R3_MAGIC_2000; tri[7]=RED
146 tri[8]=w/2; tri[9]=h-h/8; tri[10]=R3_MAGIC_2000; tri[11]=RED
147 rc_triangle(fb, zb, w, h, tri)
148 return 2
149}
150
151func main(argc: i64, argv: *i64) -> i64 {
152 if argc < 5 {
153 hw("{\"tool\":\"nx_render3d\",\"error\":\"usage: nx_render3d <cube|ztri> <angle> <size> <out.png> [url]\"}\n" as *u8)
154 sys_exit(2)
155 return 2
156 }
157 let mode: *u8 = argv[1] as *u8
158 let angle: i64 = satoi(argv[2] as *u8)
159 var size: i64 = satoi(argv[3] as *u8)
160 let outp: *u8 = argv[4] as *u8
161 if size < 16 { size = 16 }
162 if size > R3_MAXSZ { size = R3_MAXSZ }
163 let npix: i64 = size * size
164 let fb: *i64 = sys_mmap(npix * 8) as *i64
165 let zb: *i64 = sys_mmap(npix * 8) as *i64
166
167 var drawn: i64 = 0
168 var ok: i64 = 0
169 if seq(mode, "cube" as *u8) == 1 { drawn = r3_render_cube(fb, zb, size, size, angle); ok = 1 }
170 if seq(mode, "ztri" as *u8) == 1 { drawn = r3_render_ztri(fb, zb, size, size); ok = 1 }
171 if ok == 0 {
172 hw("{\"tool\":\"nx_render3d\",\"error\":\"unknown mode (cube|ztri)\"}\n" as *u8)
173 sys_exit(3)
174 return 3
175 }
176
177 // i64 packed (r + g<<8 + b<<16) -> RGB bytes -> PNG; count coverage (non-bg)
178 let rgb: *u8 = sys_mmap(npix * 3)
179 var cov: i64 = 0
180 var i: i64 = 0
181 while i < npix {
182 let px: i64 = fb[i]
183 if px != R3_BG { cov = cov + 1 }
184 rgb[i*3] = (px % 256) as u8
185 rgb[i*3+1] = ((px / 256) % 256) as u8
186 rgb[i*3+2] = ((px / R3_MAGIC_65536) % 256) as u8
187 i = i + 1
188 }
189 nx_png_write_rgb(outp, rgb, size, size)
190
191 let out: *u8 = sys_mmap(R3_MAGIC_2048)
192 var o: i64 = 0
193 o = jput(out, o, "{\"tool\":\"nx_render3d\",\"mode\":\"" as *u8)
194 o = jput(out, o, mode)
195 o = jput(out, o, "\",\"angle\":" as *u8)
196 o = jputn(out, o, angle)
197 o = jput(out, o, ",\"size\":" as *u8)
198 o = jputn(out, o, size)
199 o = jput(out, o, ",\"drawn\":" as *u8)
200 o = jputn(out, o, drawn)
201 o = jput(out, o, ",\"coverage\":" as *u8)
202 o = jputn(out, o, cov)
203 o = jput(out, o, ",\"out\":\"" as *u8)
204 o = jput(out, o, outp)
205 if argc >= 6 {
206 o = jput(out, o, "\",\"url\":\"" as *u8)
207 o = jput(out, o, argv[5] as *u8)
208 }
209 o = jput(out, o, "\"}\n" as *u8)
210 sys_write(1, out, o)
211 sys_exit(0)
212 return 0
213}