code wiki / (root) / nx_fbx_measure.nx

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}