nx_fbx_measure.nx source
↩ module page · 520 lines · 21411 B
1// nx_fbx_measure.nx -- SOVEREIGN body-mesh measurement organ (replaces the node instrument).
2// Reads a binary FBX (7200/7500: node records, zlib'd double/float vertex arrays) OR an ASCII
3// OBJ, finds the largest vertex array (the body), and emits a height-sliced silhouette profile
4// + landmark table (hip/waist/shoulder/head positions and widths as permyriad-of-height).
5// MEASUREMENT ONLY -- no geometry is retained or copied; safe under every CC license variant.
6// The same landmark code measures our EMITTED meshes, closing the 1:1-by-measurement loop.
7// usage: nx_fbx_measure <file.fbx|file.obj>
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_zlib_wrap.nx"
11import "nx_nxa.nx"
12const FM_MAGIC_2047: i64 = 2047
13const FM_MAGIC_1048575: i64 = 1048575
14const FM_MAGIC_4503599627370496: i64 = 4503599627370496
15const FM_MAGIC_9007199254740992: i64 = 9007199254740992
16const FM_MAGIC_8388607: i64 = 8388607
17const FM_MAGIC_8388608: i64 = 8388608
18const FM_MAGIC_2000000: i64 = 2000000
19const FM_MAGIC_4611686018427387903: i64 = 4611686018427387903
20const FM_MAGIC_10000: i64 = 10000
21const FM_MAGIC_7500: i64 = 7500
22const FM_MAGIC_500000: i64 = 500000
23const FM_MAGIC_4096: i64 = 4096
24const FM_MAGIC_2147483647: i64 = 2147483647
25const FM_MAGIC_4294967296: i64 = 4294967296
26
27const FM_SLICES: i64 = 40
28const FM_MAXV: i64 = 4000000 // vertex cap (12M i64 mm coords = 96MB)
29
30func fmw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
31func fmn(v: i64) -> i64 {
32 let t: *u8 = sys_mmap(32) as *u8
33 var m: i64 = v; var w: i64 = 0
34 if m<0 { t[w]=45 as u8; w=w+1; m=0-m }
35 if m==0 { t[w]=48 as u8; sys_write(1,t,w+1); return 0 }
36 let d: *u8 = sys_mmap(32) as *u8
37 var k: i64=0
38 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
39 var j: i64=0
40 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 }
41 sys_write(1,t,w); return 0
42}
43func fm_u32(b: *u8, o: i64) -> i64 {
44 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8)
45 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24)
46}
47func fm_u64(b: *u8, o: i64) -> i64 {
48 return fm_u32(b, o) | (fm_u32(b, o + 4) << 32)
49}
50// IEEE754 double at byte offset -> integer MILLIMETERS (x1000), pure i64
51func fm_f64_mm(b: *u8, o: i64) -> i64 {
52 let lo: i64 = fm_u32(b, o)
53 let hi: i64 = fm_u32(b, o + 4)
54 let sign: i64 = (hi >> 31) & 1
55 let expo: i64 = (hi >> 20) & FM_MAGIC_2047
56 if expo == 0 { return 0 }
57 var mant: i64 = ((hi & FM_MAGIC_1048575) << 32) | lo // 52-bit mantissa
58 mant = mant | FM_MAGIC_4503599627370496 // implicit leading 1 (2^52)
59 let sh: i64 = expo - 1023
60 // value_mm = mant * 2^(sh-52) * 1000
61 var v: i64 = 0
62 if sh >= 52 { v = mant * 1000 * (1 << (sh - 52)) }
63 if sh < 52 {
64 let rs: i64 = 52 - sh
65 if rs > 62 { return 0 }
66 v = (mant * 1000) >> rs
67 if rs > 9 { if mant > FM_MAGIC_9007199254740992/2 { v = (mant >> 10) * 1000 >> (rs - 10) } }
68 }
69 if sign == 1 { return 0 - v }
70 return v
71}
72// IEEE754 float32 -> integer millimeters
73func fm_f32_mm(b: *u8, o: i64) -> i64 {
74 let w: i64 = fm_u32(b, o)
75 let sign: i64 = (w >> 31) & 1
76 let expo: i64 = (w >> 23) & 255
77 if expo == 0 { return 0 }
78 var mant: i64 = (w & FM_MAGIC_8388607) | FM_MAGIC_8388608 // implicit 1 (2^23)
79 let sh: i64 = expo - 127
80 var v: i64 = 0
81 if sh >= 23 { v = mant * 1000 * (1 << (sh - 23)) }
82 if sh < 23 { v = (mant * 1000) >> (23 - sh) }
83 if sign == 1 { return 0 - v }
84 return v
85}
86
87// ---- FBX two-pass walk: find the LARGEST Vertices array, remember where ----
88// best[0]=data-offset best[1]=alen best[2]=enc best[3]=clen best[4]=type(0=d,1=f)
89func fm_walk(b: *u8, flen: i64, off0: i64, big: i64, best: *i64) -> i64 {
90 var off: i64 = off0
91 var guard: i64 = 0
92 while guard < FM_MAGIC_2000000 {
93 var endo: i64 = 0
94 var nprops: i64 = 0
95 var plen: i64 = 0
96 var nlen: i64 = 0
97 var p: i64 = 0
98 if big == 1 {
99 endo = fm_u64(b, off)
100 nprops = fm_u64(b, off + 8)
101 plen = fm_u64(b, off + 16)
102 nlen = (b[off + 24] & 0xff) as i64
103 p = off + 25
104 }
105 if big == 0 {
106 endo = fm_u32(b, off)
107 nprops = fm_u32(b, off + 4)
108 plen = fm_u32(b, off + 8)
109 nlen = (b[off + 12] & 0xff) as i64
110 p = off + 13
111 }
112 if endo == 0 { return off }
113 if endo > flen { return flen }
114 // is this node named "Vertices" (isv=1) or "PolygonVertexIndex" (isv=2)?
115 var isv: i64 = 0
116 if nlen == 8 {
117 if (b[p] & 0xff) == 86 { if (b[p+1] & 0xff) == 101 { if (b[p+2] & 0xff) == 114 {
118 if (b[p+3] & 0xff) == 116 { if (b[p+7] & 0xff) == 115 { isv = 1 } } } } }
119 }
120 if nlen == 18 {
121 if (b[p] & 0xff) == 80 { if (b[p+7] & 0xff) == 86 { if (b[p+13] & 0xff) == 73 {
122 isv = 2 } } }
123 }
124 p = p + nlen
125 let pstart: i64 = p
126 var pi: i64 = 0
127 var stop: i64 = 0
128 while pi < nprops {
129 if stop == 0 {
130 let t: i64 = (b[p] & 0xff) as i64
131 p = p + 1
132 if t == 83 { let l: i64 = fm_u32(b, p); p = p + 4 + l } // S
133 if t == 82 { let l2: i64 = fm_u32(b, p); p = p + 4 + l2 } // R
134 if t == 89 { p = p + 2 } // Y
135 if t == 67 { p = p + 1 } // C
136 if t == 73 { p = p + 4 } // I
137 if t == 70 { p = p + 4 } // F
138 if t == 68 { p = p + 8 } // D
139 if t == 76 { p = p + 8 } // L
140 var isarr: i64 = 0
141 if t == 100 { isarr = 1 } // d
142 if t == 102 { isarr = 2 } // f
143 if t == 108 { isarr = 3 }
144 if t == 105 { isarr = 3 }
145 if t == 98 { isarr = 3 }
146 if isarr > 0 {
147 let alen: i64 = fm_u32(b, p)
148 let enc: i64 = fm_u32(b, p + 4)
149 let clen: i64 = fm_u32(b, p + 8)
150 // PAIRED capture: a PVI belongs to the NEAREST PRECEDING Vertices (they are
151 // siblings of one Geometry node). Taking largest-Vertices and largest-PVI
152 // INDEPENDENTLY once paired arrays across nodes in a multi-mesh file --
153 // half the model rendered as a foreign shell. best[9..13] = last Vertices
154 // seen; commit the PAIR when its PVI arrives and it beats the champion.
155 if isv == 1 { if isarr < 3 {
156 best[9] = p + 12
157 best[10] = alen
158 best[11] = enc
159 best[12] = clen
160 best[13] = isarr - 1
161 } }
162 if isv == 2 { if t == 105 { if best[10] > best[1] {
163 best[0] = best[9]
164 best[1] = best[10]
165 best[2] = best[11]
166 best[3] = best[12]
167 best[4] = best[13]
168 best[5] = p + 12
169 best[6] = alen
170 best[7] = enc
171 best[8] = clen
172 } } }
173 p = p + 12 + clen
174 }
175 if t != 83 { if t != 82 { if t != 89 { if t != 67 { if t != 73 { if t != 70 {
176 if t != 68 { if t != 76 { if isarr == 0 { stop = 1 } } } } } } } } }
177 }
178 pi = pi + 1
179 }
180 p = pstart + plen
181 var sent: i64 = 13
182 if big == 1 { sent = 25 }
183 while p < endo - sent {
184 p = fm_walk(b, flen, p, big, best)
185 if p >= flen { return flen }
186 }
187 return endo
188 }
189 return flen
190}
191
192// measure verts[0..n*3) (mm ints, layout xyz) -> print profile + landmarks
193func fm_measure(vx: *i64, n: i64) -> i64 {
194 let mins: *i64 = sys_mmap(64) as *i64
195 let maxs: *i64 = sys_mmap(64) as *i64
196 var a: i64 = 0
197 while a < 3 { mins[a] = FM_MAGIC_4611686018427387903; maxs[a] = 0 - FM_MAGIC_4611686018427387903; a = a + 1 }
198 var i: i64 = 0
199 while i < n {
200 var a2: i64 = 0
201 while a2 < 3 {
202 let v: i64 = vx[i*3 + a2]
203 if v < mins[a2] { mins[a2] = v }
204 if v > maxs[a2] { maxs[a2] = v }
205 a2 = a2 + 1
206 }
207 i = i + 1
208 }
209 let ex: *i64 = sys_mmap(64) as *i64
210 var a3: i64 = 0
211 while a3 < 3 { ex[a3] = maxs[a3] - mins[a3]; a3 = a3 + 1 }
212 var up: i64 = 0
213 if ex[1] > ex[up] { up = 1 }
214 if ex[2] > ex[up] { up = 2 }
215 var lat: i64 = 0
216 if up == 0 { lat = 1 }
217 if ex[3 - up - lat] > ex[lat] { lat = 3 - up - lat }
218 let oth: i64 = 3 - up - lat
219 let H: i64 = ex[up]
220 fmw("height_mm=" as *u8); fmn(H)
221 fmw(" up=" as *u8); fmn(up); fmw(" lat=" as *u8); fmn(lat)
222 fmw(" verts=" as *u8); fmn(n); fmw("\n" as *u8)
223 if H <= 0 { return 1 }
224 let wid: *i64 = sys_mmap(FM_SLICES*8) as *i64
225 let dep: *i64 = sys_mmap(FM_SLICES*8) as *i64
226 var s0: i64 = 0
227 while s0 < FM_SLICES { wid[s0] = 0; dep[s0] = 0; s0 = s0 + 1 }
228 let cl: i64 = mins[lat] + ex[lat]/2
229 let co: i64 = mins[oth] + ex[oth]/2
230 var i2: i64 = 0
231 while i2 < n {
232 var sl: i64 = (vx[i2*3 + up] - mins[up]) * FM_SLICES / H
233 if sl < 0 { sl = 0 }
234 if sl >= FM_SLICES { sl = FM_SLICES - 1 }
235 var w: i64 = vx[i2*3 + lat] - cl
236 if w < 0 { w = 0 - w }
237 var d2: i64 = vx[i2*3 + oth] - co
238 if d2 < 0 { d2 = 0 - d2 }
239 if w > wid[sl] { wid[sl] = w }
240 if d2 > dep[sl] { dep[sl] = d2 }
241 i2 = i2 + 1
242 }
243 fmw("profile (slice, half-w permyriad of H, half-d permyriad):\n" as *u8)
244 var s1: i64 = 0
245 while s1 < FM_SLICES {
246 fmw(" " as *u8); fmn(s1)
247 fmw(" " as *u8); fmn(wid[s1]*FM_MAGIC_10000/H)
248 fmw(" " as *u8); fmn(dep[s1]*FM_MAGIC_10000/H)
249 fmw("\n" as *u8)
250 s1 = s1 + 1
251 }
252 // landmarks (permyriad widths, slice positions)
253 var hipS: i64 = FM_SLICES*42/100
254 var hipW: i64 = 0
255 var s2: i64 = FM_SLICES*42/100
256 while s2 <= FM_SLICES*62/100 { if wid[s2] > hipW { hipW = wid[s2]; hipS = s2 } s2 = s2 + 1 }
257 var waiS: i64 = hipS
258 var waiW: i64 = FM_MAGIC_4611686018427387903
259 var s3: i64 = hipS
260 while s3 <= FM_SLICES*72/100 { if wid[s3] < waiW { waiW = wid[s3]; waiS = s3 } s3 = s3 + 1 }
261 var heaS: i64 = FM_SLICES*90/100
262 var heaW: i64 = 0
263 var s4: i64 = FM_SLICES*90/100
264 while s4 < FM_SLICES { if wid[s4] > heaW { heaW = wid[s4]; heaS = s4 } s4 = s4 + 1 }
265 fmw("LANDMARKS hipS=" as *u8); fmn(hipS)
266 fmw(" hipW=" as *u8); fmn(hipW*FM_MAGIC_10000/H)
267 fmw(" waiS=" as *u8); fmn(waiS)
268 fmw(" waiW=" as *u8); fmn(waiW*FM_MAGIC_10000/H)
269 fmw(" headS=" as *u8); fmn(heaS)
270 fmw(" headW=" as *u8); fmn(heaW*FM_MAGIC_10000/H)
271 fmw(" waist_hip_pct=" as *u8); fmn(waiW*100/hipW)
272 fmw("\n" as *u8)
273 return 0
274}
275
276// ---- ASCII OBJ path: lines "v x y z" (decimal, mm precision) ----
277func fm_obj(b: *u8, flen: i64, vx: *i64) -> i64 {
278 var n: i64 = 0
279 var p: i64 = 0
280 while p < flen {
281 if (b[p] & 0xff) == 118 { if p + 1 < flen { if (b[p+1] & 0xff) == 32 {
282 var q: i64 = p + 2
283 var c: i64 = 0
284 while c < 3 {
285 while q < flen { if (b[q] & 0xff) == 32 { q = q + 1 } else { break } }
286 var sgn: i64 = 1
287 if (b[q] & 0xff) == 45 { sgn = 0 - 1; q = q + 1 }
288 var ip: i64 = 0
289 while q < flen {
290 let ch: i64 = (b[q] & 0xff) as i64
291 if ch >= 48 { if ch <= 57 { ip = ip*10 + ch - 48; q = q + 1; continue } }
292 break
293 }
294 var fr: i64 = 0
295 var fd: i64 = 0
296 if q < flen { if (b[q] & 0xff) == 46 {
297 q = q + 1
298 while q < flen {
299 let ch2: i64 = (b[q] & 0xff) as i64
300 if ch2 >= 48 { if ch2 <= 57 {
301 if fd < 3 { fr = fr*10 + ch2 - 48; fd = fd + 1 }
302 q = q + 1
303 continue
304 } }
305 break
306 }
307 } }
308 while fd < 3 { fr = fr*10; fd = fd + 1 }
309 if n < FM_MAXV { vx[n*3 + c] = sgn*(ip*1000 + fr) }
310 c = c + 1
311 }
312 if n < FM_MAXV { n = n + 1 }
313 } } }
314 while p < flen { if (b[p] & 0xff) != 10 { p = p + 1 } else { break } }
315 p = p + 1
316 }
317 return n
318}
319
320func main(argc: i64, argv: *i64) -> i64 {
321 if argc < 2 { fmw("usage: nx_fbx_measure <file.fbx|obj>\n" as *u8); return 2 }
322 let lp: *i64 = sys_mmap(16) as *i64
323 let b: *u8 = sys_map_file(argv[1] as *u8, lp)
324 let flen: i64 = lp[0]
325 if flen < 64 { fmw("unreadable\n" as *u8); return 3 }
326 // OBJ? (starts with '#' or 'v ' or contains no FBX magic)
327 var isfbx: i64 = 0
328 if (b[0] & 0xff) == 75 { if (b[1] & 0xff) == 97 { if (b[2] & 0xff) == 121 { isfbx = 1 } } }
329 if isfbx == 0 {
330 let vx0: *i64 = sys_mmap(FM_MAXV*24) as *i64
331 let n0: i64 = fm_obj(b, flen, vx0)
332 fmw("OBJ verts=" as *u8); fmn(n0); fmw("\n" as *u8)
333 if n0 < 8 { return 4 }
334 return fm_measure(vx0, n0)
335 }
336 let ver: i64 = fm_u32(b, 23)
337 var big: i64 = 0
338 if ver >= FM_MAGIC_7500 { big = 1 }
339 fmw("FBX ver=" as *u8); fmn(ver); fmw("\n" as *u8)
340 let best: *i64 = sys_mmap(128) as *i64
341 var pos: i64 = 27
342 var guard: i64 = 0
343 while pos < flen - 200 {
344 if guard > FM_MAGIC_500000 { break }
345 let e: i64 = fm_walk(b, flen, pos, big, best)
346 if e <= pos { break }
347 pos = e
348 guard = guard + 1
349 }
350 fmw("largest Vertices alen=" as *u8); fmn(best[1])
351 fmw(" enc=" as *u8); fmn(best[2]); fmw(" type=" as *u8); fmn(best[4]); fmw("\n" as *u8)
352 if best[1] < 24 { fmw("no vertex array\n" as *u8); return 5 }
353 var raw: *u8 = ((b as i64) + best[0]) as *u8
354 var rawn: i64 = best[3]
355 if best[2] == 1 {
356 let zr: *NxZlibResult = nx_zlib_inflate(raw, best[3], best[1]*8 + FM_MAGIC_4096)
357 if zr.error_code != 0 { fmw("inflate err=" as *u8); fmn(zr.error_code); fmw("\n" as *u8); return 6 }
358 raw = zr.output_data
359 rawn = zr.output_size
360 }
361 let n: i64 = best[1] / 3
362 if n > FM_MAXV { fmw("too many verts\n" as *u8); return 7 }
363 let vx: *i64 = sys_mmap(n*24 + 64) as *i64
364 var i: i64 = 0
365 if best[4] == 0 {
366 while i < n*3 { vx[i] = fm_f64_mm(raw, i*8); i = i + 1 }
367 }
368 if best[4] == 1 {
369 while i < n*3 { vx[i] = fm_f32_mm(raw, i*4); i = i + 1 }
370 }
371 // CONVERT MODE (1:1 FULL DETAIL): argv[2]=conv argv[3]=out.nxmesh -- every vertex + every
372 // polygon (fan-triangulated; FBX marks a polygon's last index as ~x) into the sovereign
373 // mesh format: [NXMH][nverts][ntris][verts mm x3][tris x3]
374 if argc >= 4 {
375 var iraw: *u8 = ((b as i64) + best[5]) as *u8
376 var irawn: i64 = best[8]
377 if best[7] == 1 {
378 let zr2: *NxZlibResult = nx_zlib_inflate(iraw, best[8], best[6]*4 + FM_MAGIC_4096)
379 if zr2.error_code != 0 { fmw("idx inflate err\n" as *u8); return 8 }
380 iraw = zr2.output_data
381 irawn = zr2.output_size
382 }
383 let ni: i64 = best[6]
384 let tris: *i64 = sys_mmap(ni*3*8 + 64) as *i64
385 var nt: i64 = 0
386 var fstart: i64 = 0
387 var k: i64 = 0
388 while k < ni {
389 var idx: i64 = fm_u32(iraw, k*4)
390 if idx > FM_MAGIC_2147483647 { idx = idx - FM_MAGIC_4294967296 } // sign-extend i32
391 var fin: i64 = 0
392 if idx < 0 { idx = (0 - idx) - 1; fin = 1 }
393 if fin == 1 {
394 // fan-triangulate polygon fstart..k
395 var t3: i64 = fstart + 1
396 while t3 < k {
397 var i0: i64 = fm_u32(iraw, fstart*4)
398 if i0 > FM_MAGIC_2147483647 { i0 = i0 - FM_MAGIC_4294967296 }
399 if i0 < 0 { i0 = (0 - i0) - 1 }
400 var i1: i64 = fm_u32(iraw, t3*4)
401 if i1 > FM_MAGIC_2147483647 { i1 = i1 - FM_MAGIC_4294967296 }
402 if i1 < 0 { i1 = (0 - i1) - 1 }
403 var i2b: i64 = fm_u32(iraw, (t3+1)*4)
404 if i2b > FM_MAGIC_2147483647 { i2b = i2b - FM_MAGIC_4294967296 }
405 if i2b < 0 { i2b = (0 - i2b) - 1 }
406 if i0 < n { if i1 < n { if i2b < n {
407 tris[nt*3] = i0
408 tris[nt*3+1] = i1
409 tris[nt*3+2] = i2b
410 nt = nt + 1
411 } } }
412 t3 = t3 + 1
413 }
414 fstart = k + 1
415 }
416 k = k + 1
417 }
418 fmw("tris=" as *u8); fmn(nt); fmw("\n" as *u8)
419 // CLUS (virtualized-geometry lesson): partition TRIS into <=128-tri clusters with mm
420 // bounds so renderers can cull per-pixel -- geometry cost follows SCREEN RESOLUTION.
421 // Contiguous ranges (FBX polygon order is spatially coherent); 10 words per cluster:
422 // [tri_start][tri_count][bmin xyz][bmax xyz][parent=-1][max Chebyshev edge mm]
423 let ncl: i64 = (nt + 127) / 128
424 let cl: *i64 = sys_mmap((1 + ncl*10)*8 + 64) as *i64
425 cl[0] = ncl
426 var ci: i64 = 0
427 while ci < ncl {
428 let ts: i64 = ci*128
429 var tc: i64 = 128
430 if ts + tc > nt { tc = nt - ts }
431 let cb: i64 = 1 + ci*10
432 cl[cb] = ts
433 cl[cb+1] = tc
434 var bx: i64 = FM_MAGIC_4611686018427387903
435 var by2: i64 = bx
436 var bz: i64 = bx
437 var Bx: i64 = 0 - bx
438 var By: i64 = 0 - bx
439 var Bz: i64 = 0 - bx
440 var me: i64 = 0
441 var q2: i64 = 0
442 while q2 < tc {
443 var e3: i64 = 0
444 while e3 < 3 {
445 let vi: i64 = tris[(ts+q2)*3 + e3]
446 let px2: i64 = vx[vi*3]
447 let py2: i64 = vx[vi*3+1]
448 let pz2: i64 = vx[vi*3+2]
449 if px2 < bx { bx = px2 } if px2 > Bx { Bx = px2 }
450 if py2 < by2 { by2 = py2 } if py2 > By { By = py2 }
451 if pz2 < bz { bz = pz2 } if pz2 > Bz { Bz = pz2 }
452 let vj: i64 = tris[(ts+q2)*3 + ((e3+1)%3)]
453 var dx2: i64 = vx[vi*3] - vx[vj*3]
454 if dx2 < 0 { dx2 = 0 - dx2 }
455 var dy2: i64 = vx[vi*3+1] - vx[vj*3+1]
456 if dy2 < 0 { dy2 = 0 - dy2 }
457 var dz2: i64 = vx[vi*3+2] - vx[vj*3+2]
458 if dz2 < 0 { dz2 = 0 - dz2 }
459 if dx2 > me { me = dx2 }
460 if dy2 > me { me = dy2 }
461 if dz2 > me { me = dz2 }
462 e3 = e3 + 1
463 }
464 q2 = q2 + 1
465 }
466 cl[cb+2] = bx
467 cl[cb+3] = by2
468 cl[cb+4] = bz
469 cl[cb+5] = Bx
470 cl[cb+6] = By
471 cl[cb+7] = Bz
472 cl[cb+8] = 0 - 1
473 cl[cb+9] = me
474 ci = ci + 1
475 }
476 // emit NXA v1 (knowledge/nxa_format_spec.md): header + TOC + [count][data] payloads,
477 // per-section rolling checks + a TOC check so readers can refuse corruption BEFORE use
478 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4)
479 if fd < 0 { fmw("open out failed\n" as *u8); return 9 }
480 let hdr: *i64 = sys_mmap(64) as *i64
481 let toc: *i64 = sys_mmap(256) as *i64
482 let cw: *i64 = sys_mmap(64) as *i64
483 cw[0] = n
484 cw[1] = nt
485 let vwl: i64 = 1 + n*3
486 let twl: i64 = 1 + nt*3
487 let cwl: i64 = 1 + ncl*10
488 let voff: i64 = 32 + 3*32
489 let toff: i64 = voff + vwl*8
490 let coff: i64 = toff + twl*8
491 toc[0] = nxa_tag4("VERT" as *u8)
492 toc[1] = voff
493 toc[2] = vwl
494 toc[3] = nxa_check2(nxa_check2(1, cw, 1), vx, n*3)
495 toc[4] = nxa_tag4("TRIS" as *u8)
496 toc[5] = toff
497 toc[6] = twl
498 toc[7] = nxa_check2(nxa_check2(1, ((cw as i64)+8) as *i64, 1), tris, nt*3)
499 toc[8] = nxa_tag4("CLUS" as *u8)
500 toc[9] = coff
501 toc[10] = cwl
502 toc[11] = nxa_check2(1, cl, cwl)
503 hdr[0] = nxa_magic()
504 hdr[1] = NXA_VER
505 hdr[2] = 3
506 hdr[3] = nxa_check2(1, toc, 12)
507 sys_write(fd, hdr as *u8, 32)
508 sys_write(fd, toc as *u8, 96)
509 sys_write(fd, cw as *u8, 8)
510 sys_write(fd, vx as *u8, n*24)
511 sys_write(fd, ((cw as i64)+8) as *u8, 8)
512 sys_write(fd, tris as *u8, nt*24)
513 sys_write(fd, cl as *u8, cwl*8)
514 sys_close(fd)
515 fmw("NXA written verts=" as *u8); fmn(n)
516 fmw(" tris=" as *u8); fmn(nt)
517 fmw(" clusters=" as *u8); fmn(ncl); fmw("\n" as *u8)
518 }
519 return fm_measure(vx, n)
520}