nx_mesh_view.nx source
↩ module page · 274 lines · 11343 B
1// nx_mesh_view.nx -- SOVEREIGN full-detail mesh renderer: loads a .nxmesh (every vertex, every
2// triangle -- the 1:1 capture of the reference model) and software-rasterizes it to PNG with a
3// z-buffer, barycentric depth, and two-light lambert. This is the proof the sovereign pipeline
4// CARRIES reference-class detail: 28k triangles, not 30 primitives.
5// usage: nx_mesh_view <in.nxmesh> <out.png> [yaw_deg]
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_itrig.nx"
9import "nx_png_write.nx"
10import "nx_nxa.nx"
11const MV_MAGIC_1314016334: i64 = 1314016334
12const MV_MAGIC_4611686018427387903: i64 = 4611686018427387903
13const MV_MAGIC_4096: i64 = 4096
14
15const MV_W: i64 = 640
16const MV_H: i64 = 1024
17
18func mvw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
19func mvn(v: i64) -> i64 {
20 let t: *u8 = sys_mmap(32) as *u8
21 var m: i64 = v; var w: i64 = 0
22 if m<0 { t[w]=45 as u8; w=w+1; m=0-m }
23 if m==0 { t[w]=48 as u8; sys_write(1,t,w+1); return 0 }
24 let d: *u8 = sys_mmap(32) as *u8
25 var k: i64=0
26 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
27 var j: i64=0
28 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 }
29 sys_write(1,t,w); return 0
30}
31func mv_isqrt(v: i64) -> i64 {
32 if v <= 0 { return 0 }
33 var x: i64 = v
34 var y: i64 = (x + 1) / 2
35 var g: i64 = 0
36 while g < 40 { if y < x { x = y; y = (x + v/x)/2 } g = g + 1 }
37 return x
38}
39func mv_min3(a: i64, b: i64, c: i64) -> i64 { var m: i64 = a; if b < m { m = b } if c < m { m = c } return m }
40func mv_max3(a: i64, b: i64, c: i64) -> i64 { var m: i64 = a; if b > m { m = b } if c > m { m = c } return m }
41
42func main(argc: i64, argv: *i64) -> i64 {
43 if argc < 3 { mvw("usage: nx_mesh_view <in.nxmesh> <out.png> [yaw_deg]\n" as *u8); return 2 }
44 var yawdeg: i64 = 20
45 if argc >= 4 {
46 let a3: *u8 = argv[3] as *u8
47 yawdeg = 0
48 var q: i64 = 0
49 while a3[q] != (0 as u8) { yawdeg = yawdeg*10 + ((a3[q] & 0xff) as i64) - 48; q = q + 1 }
50 }
51 let lp: *i64 = sys_mmap(16) as *i64
52 let b: *u8 = sys_map_file(argv[1] as *u8, lp)
53 if lp[0] < 24 { mvw("unreadable\n" as *u8); return 3 }
54 let hdr: *i64 = b as *i64
55 var nv: i64 = 0
56 var nt: i64 = 0
57 var vx: *i64 = 0 as *i64
58 var tr: *i64 = 0 as *i64
59 if hdr[0] == MV_MAGIC_1314016334 {
60 // legacy .nxmesh v0 (additive-only: old artifacts keep rendering)
61 nv = hdr[1]
62 nt = hdr[2]
63 vx = ((b as i64) + 24) as *i64
64 tr = ((b as i64) + 24 + nv*24) as *i64
65 }
66 if hdr[0] == nxa_magic() {
67 let vwo: i64 = nxa_find(b, lp[0], nxa_tag4("VERT" as *u8))
68 if vwo == 0 - 2 { mvw("NXA-FUTURE-VERSION: refusing\n" as *u8); return 6 }
69 if vwo < 0 { mvw("NXA-CORRUPT: refusing\n" as *u8); return 5 }
70 let two: i64 = nxa_find(b, lp[0], nxa_tag4("TRIS" as *u8))
71 if two < 0 { mvw("NXA-CORRUPT: refusing\n" as *u8); return 5 }
72 nv = hdr[vwo]
73 nt = hdr[two]
74 vx = ((b as i64) + vwo*8 + 8) as *i64
75 tr = ((b as i64) + two*8 + 8) as *i64
76 }
77 if nv < 3 { mvw("bad magic\n" as *u8); return 4 }
78 mvw("verts=" as *u8); mvn(nv); mvw(" tris=" as *u8); mvn(nt); mvw("\n" as *u8)
79 // bounds (fbx z-up: x=lat, y=depth, z=up)
80 var mnx: i64 = MV_MAGIC_4611686018427387903
81 var mxx: i64 = 0 - mnx
82 var mny: i64 = mnx
83 var mxy: i64 = 0 - mnx
84 var mnz: i64 = mnx
85 var mxz: i64 = 0 - mnx
86 var i: i64 = 0
87 while i < nv {
88 let X: i64 = vx[i*3]
89 let Y: i64 = vx[i*3+1]
90 let Z: i64 = vx[i*3+2]
91 if X < mnx { mnx = X } if X > mxx { mxx = X }
92 if Y < mny { mny = Y } if Y > mxy { mxy = Y }
93 if Z < mnz { mnz = Z } if Z > mxz { mxz = Z }
94 i = i + 1
95 }
96 // UP-AXIS AUTO-DETECT (parity with the browser viewer and fm_measure): a standing body's
97 // largest extent IS its up axis; lat = larger of the rest. FBX files arrive both z-up and
98 // y-up -- assuming z-up framed the y-up 2M-tri model as a lying close-up.
99 let exv: *i64 = sys_mmap(64) as *i64
100 exv[0] = mxx - mnx
101 exv[1] = mxy - mny
102 exv[2] = mxz - mnz
103 var iu: i64 = 0
104 if exv[1] > exv[iu] { iu = 1 }
105 if exv[2] > exv[iu] { iu = 2 }
106 var il: i64 = 0
107 if iu == 0 { il = 1 }
108 let io0: i64 = 3 - iu - il
109 if exv[io0] > exv[il] { il = io0 }
110 let io: i64 = 3 - iu - il
111 let mnv: *i64 = sys_mmap(64) as *i64
112 mnv[0] = mnx
113 mnv[1] = mny
114 mnv[2] = mnz
115 let mxv: *i64 = sys_mmap(64) as *i64
116 mxv[0] = mxx
117 mxv[1] = mxy
118 mxv[2] = mxz
119 let H: i64 = exv[iu]
120 let cx: i64 = (mnv[il] + mxv[il])/2
121 let cy: i64 = (mnv[io] + mxv[io])/2
122 // yaw the model around the up axis so the view angle is a parameter
123 let ya: i64 = yawdeg * IT_PI / 180
124 let sy: i64 = it_sin4096(ya)
125 let cyw: i64 = it_cos4096(ya)
126 // scale: fit height to 94% of canvas
127 let scale_n: i64 = MV_H * 94 / 100
128 let fb: *i64 = sys_mmap(MV_W*MV_H*8) as *i64
129 let zb: *i64 = sys_mmap(MV_W*MV_H*8) as *i64
130 var p0: i64 = 0
131 while p0 < MV_W*MV_H {
132 let g: i64 = 26 + p0/(MV_W*MV_H/14)
133 fb[p0] = g | ((g+4) << 8) | ((g+10) << 16)
134 zb[p0] = 0 - MV_MAGIC_4611686018427387903
135 p0 = p0 + 1
136 }
137 // screen coords per vertex (mm -> px), rotated: xr = (x-cx)*cos + (y-cy)*sin; depth = -(x-cx)*sin + (y-cy)*cos
138 let sxp: *i64 = sys_mmap(nv*8) as *i64
139 let syp: *i64 = sys_mmap(nv*8) as *i64
140 let szp: *i64 = sys_mmap(nv*8) as *i64
141 var i2: i64 = 0
142 while i2 < nv {
143 let rx: i64 = ((vx[i2*3+il] - cx)*cyw + (vx[i2*3+io] - cy)*sy)/MV_MAGIC_4096
144 let rd: i64 = (0 - (vx[i2*3+il] - cx)*sy + (vx[i2*3+io] - cy)*cyw)/MV_MAGIC_4096
145 sxp[i2] = MV_W/2 + rx*scale_n/H
146 syp[i2] = MV_H - (MV_H - scale_n)/2 - (vx[i2*3+iu] - mnv[iu])*scale_n/H
147 szp[i2] = rd
148 i2 = i2 + 1
149 }
150 // rasterize with barycentric z; two lights (key upper-left-front, fill right)
151 var t: i64 = 0
152 var drawn: i64 = 0
153 while t < nt {
154 let a: i64 = tr[t*3]
155 let bb: i64 = tr[t*3+1]
156 let c: i64 = tr[t*3+2]
157 let x0: i64 = sxp[a]
158 let y0: i64 = syp[a]
159 let x1: i64 = sxp[bb]
160 let y1: i64 = syp[bb]
161 let x2: i64 = sxp[c]
162 let y2: i64 = syp[c]
163 let area: i64 = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0)
164 if area != 0 {
165 // world-space normal for lighting (mm coords, rotated frame)
166 let e1x: i64 = (vx[bb*3]-vx[a*3])
167 let e1y: i64 = (vx[bb*3+1]-vx[a*3+1])
168 let e1z: i64 = (vx[bb*3+2]-vx[a*3+2])
169 let e2x: i64 = (vx[c*3]-vx[a*3])
170 let e2y: i64 = (vx[c*3+1]-vx[a*3+1])
171 let e2z: i64 = (vx[c*3+2]-vx[a*3+2])
172 var nxv: i64 = e1y*e2z - e1z*e2y
173 var nyv: i64 = e1z*e2x - e1x*e2z
174 var nzv: i64 = e1x*e2y - e1y*e2x
175 let nl: i64 = mv_isqrt(nxv*nxv/256 + nyv*nyv/256 + nzv*nzv/256)*16
176 if nl > 0 {
177 nxv = nxv*1000/nl
178 nyv = nyv*1000/nl
179 nzv = nzv*1000/nl
180 // key light dir ~ (-.4,-.75,.53) front-upper-left (y = toward viewer at yaw 0)
181 var lam: i64 = (0-400)*nxv + (0-750)*nyv + 530*nzv
182 if lam < 0 { lam = 0 - lam } // double-sided (raw scan meshes flip)
183 lam = lam/1000
184 var lit: i64 = 300 + lam*720/1000
185 if lit > 1000 { lit = 1000 }
186 let rr: i64 = 236*lit/1000
187 let gg: i64 = 196*lit/1000
188 let bl: i64 = 172*lit/1000
189 let col: i64 = rr | (gg << 8) | (bl << 16)
190 var miny: i64 = mv_min3(y0, y1, y2)
191 var maxy: i64 = mv_max3(y0, y1, y2)
192 var minx: i64 = mv_min3(x0, x1, x2)
193 var maxx: i64 = mv_max3(x0, x1, x2)
194 if miny < 0 { miny = 0 }
195 if minx < 0 { minx = 0 }
196 if maxy > MV_H-1 { maxy = MV_H-1 }
197 if maxx > MV_W-1 { maxx = MV_W-1 }
198 var py: i64 = miny
199 while py <= maxy {
200 var px: i64 = minx
201 while px <= maxx {
202 let w0: i64 = (x1-px)*(y2-py) - (x2-px)*(y1-py)
203 let w1: i64 = (x2-px)*(y0-py) - (x0-px)*(y2-py)
204 let w2: i64 = (x0-px)*(y1-py) - (x1-px)*(y0-py)
205 var inside: i64 = 0
206 if w0 >= 0 { if w1 >= 0 { if w2 >= 0 { inside = 1 } } }
207 if w0 <= 0 { if w1 <= 0 { if w2 <= 0 { inside = 1 } } }
208 if inside == 1 {
209 let z: i64 = (w0*szp[a] + w1*szp[bb] + w2*szp[c]) / area
210 let o: i64 = py*MV_W + px
211 if z > zb[o] {
212 zb[o] = z
213 fb[o] = col
214 }
215 }
216 px = px + 1
217 }
218 py = py + 1
219 }
220 drawn = drawn + 1
221 }
222 }
223 t = t + 1
224 }
225 mvw("drawn=" as *u8); mvn(drawn); mvw("\n" as *u8)
226 // SKELETON OVERLAY: if SKEL is present, draw bones (parent->joint) as bright segments
227 // through the SAME projection -- the proof that binds share the mesh's space: a coherent
228 // armature INSIDE the figure, or the import is wrong.
229 let swo: i64 = nxa_find(b, lp[0], nxa_tag4("SKEL" as *u8))
230 if swo >= 0 {
231 let njj: i64 = hdr[swo]
232 let sk: *i64 = ((b as i64) + swo*8 + 8) as *i64
233 var jb: i64 = 0
234 var bones: i64 = 0
235 while jb < njj {
236 let par: i64 = sk[jb*8]
237 if par >= 0 {
238 // project both ends with the same axis permutation + yaw
239 var seg: i64 = 0
240 while seg <= 100 {
241 let bx3: i64 = (sk[par*8+1]*(100-seg) + sk[jb*8+1]*seg)/100
242 let by3: i64 = (sk[par*8+2]*(100-seg) + sk[jb*8+2]*seg)/100
243 let bz3: i64 = (sk[par*8+3]*(100-seg) + sk[jb*8+3]*seg)/100
244 let co: *i64 = sys_mmap(32) as *i64
245 co[0] = bx3
246 co[1] = by3
247 co[2] = bz3
248 let rx2: i64 = ((co[il] - cx)*cyw + (co[io] - cy)*sy)/MV_MAGIC_4096
249 let px3: i64 = MV_W/2 + rx2*scale_n/H
250 let py3: i64 = MV_H - (MV_H - scale_n)/2 - (co[iu] - mnv[iu])*scale_n/H
251 if px3 >= 1 { if px3 < MV_W-1 { if py3 >= 1 { if py3 < MV_H-1 {
252 fb[py3*MV_W + px3] = 60 | (255 << 8) | (120 << 16)
253 fb[py3*MV_W + px3 + 1] = 60 | (255 << 8) | (120 << 16)
254 } } } }
255 seg = seg + 1
256 }
257 bones = bones + 1
258 }
259 jb = jb + 1
260 }
261 mvw("bones=" as *u8); mvn(bones); mvw("\n" as *u8)
262 }
263 let rgb: *u8 = sys_mmap(MV_W*MV_H*3) as *u8
264 var p2: i64 = 0
265 while p2 < MV_W*MV_H {
266 rgb[p2*3] = (fb[p2] & 255) as u8
267 rgb[p2*3+1] = ((fb[p2] >> 8) & 255) as u8
268 rgb[p2*3+2] = ((fb[p2] >> 16) & 255) as u8
269 p2 = p2 + 1
270 }
271 nx_png_write_rgb(argv[2] as *u8, rgb, MV_W, MV_H)
272 mvw("PNG written\n" as *u8)
273 return 0
274}