code wiki / _hdl_build / nx_gltf2mesh.nx

nx_gltf2mesh.nx source

↩ module page · 855 lines · 39933 B

1// nx_gltf2mesh.nx -- GLB/VRM -> NXMSH2 mesh-oracle extractor (debt 1785902685, the D2 POSITION slice). 2// The whole bench suite (nx_bodybench / nx_twinbench / nx_curvebench / nx_facemark) judges .nxmesh 3// oracles, but NO organ could turn a donor .glb/.vrm into one -- donor FORM was invisible to every 4// instrument we own (operator 2026-08-05: the T2 being is still a clay blob because the ruler only 5// reads skeletons). This organ reads ONLY what a form oracle needs: every TRIANGLES primitive's 6// POSITION + indices (u16 AND u32, tight or strided bufferViews), multi-mesh multi-primitive concat. 7// FAIL-CLOSED at every offset (D1): a file this cannot parse REFUSES with a named reason and a 8// nonzero exit -- never a segfault, never a silent empty mesh (zero accepted tris = REFUSAL). 9// Morph-target POSITIONs are excluded by scoping the attribute scan to the primitive's attributes{}. 10// Skinned bind-pose positions are model-space per the glTF spec (node transforms ignored for skinned 11// meshes); VRM avatars are skinned throughout -- reported in the status line, never silent. 12// nx_gltf2mesh <in.glb|.vrm> <out.nxmesh> | nx_gltf2mesh selftest 13// license_tier: ORIGINAL expect_exit: 0 14import "nx_syscalls.nx" 15const G2_MAGIC_2000000000: i64 = 2000000000 16const G2_MAGIC_2500: i64 = 2500 17const G2_MAGIC_500000: i64 = 500000 18const G2_MAGIC_2500000: i64 = 2500000 19const G2_MAGIC_4096: i64 = 4096 20 21const G2_CAP: i64 = 33554432 22const G2_MAXEL: i64 = 1024 23const G2_MAXTRI: i64 = 400000 24const G2_Q14: i64 = 16384 25const G2_M8388607: i64 = 8388607 26const G2_M8388608: i64 = 8388608 27const G2_GREY: i64 = 780 28const G2_CT_F32: i64 = 5126 29const G2_CT_U16: i64 = 5123 30const G2_CT_U32: i64 = 5125 31const G2_MODE_TRIS: i64 = 4 32const G2_HDR: i64 = 16 33const G2_LAYROW: i64 = 24 34const G2_TRIREC: i64 = 84 35 36func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 37func 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 } 38 39// ---- bounds-checked little-endian readers (D1: refuse, never wander) ---- 40static G2_LEN: i64 41static G2_ERR: i64 42func g2_u16(b: *u8, o: i64) -> i64 { 43 if o < 0 { G2_ERR = 1; return 0 } 44 if o + 2 > G2_LEN { G2_ERR = 1; return 0 } 45 return (b[o] as i64) + ((b[o+1] as i64)<<8) 46} 47func g2_u32(b: *u8, o: i64) -> i64 { 48 if o < 0 { G2_ERR = 1; return 0 } 49 if o + 4 > G2_LEN { G2_ERR = 1; return 0 } 50 return (b[o] as i64) + ((b[o+1] as i64)<<8) + ((b[o+2] as i64)<<16) + ((b[o+3] as i64)<<24) 51} 52// IEEE754 float32 bits -> integer millimetres (metres*1000), the nx_fbx_measure recipe 53func g2_f32mm(w: i64) -> i64 { 54 let sign: i64 = (w >> 31) & 1 55 let expo: i64 = (w >> 23) & 255 56 if expo == 0 { return 0 } 57 var mant: i64 = (w & G2_M8388607) | G2_M8388608 58 let sh: i64 = expo - 127 59 var v: i64 = 0 60 if sh >= 23 { if sh - 23 > 30 { G2_ERR = 1; return 0 } } 61 if sh >= 23 { v = mant * 1000 * (1 << (sh - 23)) } 62 if sh < 23 { if 23 - sh > 62 { return 0 } } 63 if sh < 23 { v = (mant * 1000) >> (23 - sh) } 64 if sign == 1 { return 0 - v } 65 return v 66} 67// integer/scale -> IEEE754 float32 bits (the nx_body_gen encoder) 68func g2_enc(v: i64, scale: i64) -> i64 { 69 if v == 0 { return 0 } 70 var neg: i64 = 0 71 var m: i64 = v 72 if m < 0 { neg = 1; m = 0-m } 73 var e: i64 = 0 74 var num: i64 = m 75 var den: i64 = scale 76 while num >= den*2 { den = den*2; e = e+1 } 77 while num < den { num = num*2; e = e-1 } 78 let frac: i64 = ((num - den)*G2_M8388608)/den 79 var bits: i64 = ((e+127) << 23) | (frac & G2_M8388607) 80 if neg == 1 { bits = bits | (1<<31) } 81 return bits 82} 83func g2_wr32(b: *u8, o: i64, v: i64) -> i64 { 84 b[o]=(v&255) as u8; b[o+1]=((v>>8)&255) as u8; b[o+2]=((v>>16)&255) as u8; b[o+3]=((v>>24)&255) as u8 85 return 0 86} 87func g2_isqrt(x: i64) -> i64 { 88 if x <= 0 { return 0 } 89 var r: i64 = x 90 var q: i64 = (x/2) + 1 91 while q < r { r = q; q = (r + x/r)/2 } 92 return r 93} 94 95// ---- string-aware JSON scanning (machine-emitted glb JSON; strings honoured, depth tracked) ---- 96func g2_lit_at(b: *u8, o: i64, e: i64, lit: *u8) -> i64 { 97 var m: i64 = 0 98 while lit[m] != (0 as u8) { m = m + 1 } 99 if o + m > e { return 0 } 100 var i: i64 = 0 101 while i < m { if b[o+i] != lit[i] { return 0 } i = i + 1 } 102 return m 103} 104// find a top-level (depth 1) key `lit` (pass WITH quotes+colon e.g. "\x22meshes\x22:") in [s,e) -> offset AFTER the colon, or -1 105func g2_topkey(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 106 var i: i64 = s 107 var depth: i64 = 0 108 var instr: i64 = 0 109 while i < e { 110 let c: i64 = b[i] as i64 111 if instr == 1 { 112 if c == 92 { i = i + 1 } 113 if c == 34 { instr = 0 } 114 } else { 115 if c == 34 { 116 if depth == 1 { 117 let m: i64 = g2_lit_at(b, i, e, lit) 118 if m > 0 { return i + m } 119 } 120 instr = 1 121 } 122 if c == 123 { depth = depth + 1 } 123 if c == 91 { depth = depth + 1 } 124 if c == 125 { depth = depth - 1 } 125 if c == 93 { depth = depth - 1 } 126 } 127 i = i + 1 128 } 129 return 0 - 1 130} 131// given off at the '[' of an array, fill starts/ends with each top element object range. -> count (or -1 refuse) 132func g2_elems(b: *u8, arr: i64, e: i64, starts: *i64, ends: *i64, maxn: i64) -> i64 { 133 if arr >= e { return 0 - 1 } 134 if b[arr] != (91 as u8) { return 0 - 1 } 135 var i: i64 = arr + 1 136 var depth: i64 = 1 137 var instr: i64 = 0 138 var cur: i64 = 0 - 1 139 var n: i64 = 0 140 while i < e { 141 let c: i64 = b[i] as i64 142 if instr == 1 { 143 if c == 92 { i = i + 1 } 144 if c == 34 { instr = 0 } 145 } else { 146 if c == 34 { instr = 1 } 147 if c == 123 { if depth == 1 { cur = i } depth = depth + 1 } 148 if c == 91 { depth = depth + 1 } 149 if c == 125 { 150 depth = depth - 1 151 if depth == 1 { 152 if cur >= 0 { 153 if n >= maxn { return 0 - 2 } 154 starts[n] = cur 155 ends[n] = i + 1 156 n = n + 1 157 cur = 0 - 1 158 } 159 } 160 } 161 if c == 93 { 162 depth = depth - 1 163 if depth == 0 { return n } 164 } 165 } 166 i = i + 1 167 } 168 return 0 - 1 169} 170// find `lit` (WITH quotes+colon) inside [s,e) at any depth, string-aware -> offset AFTER colon, or -1 171func g2_key(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 172 var i: i64 = s 173 var instr: i64 = 0 174 while i < e { 175 let c: i64 = b[i] as i64 176 if instr == 1 { 177 if c == 92 { i = i + 1 } 178 if c == 34 { instr = 0 } 179 } else { 180 if c == 34 { 181 let m: i64 = g2_lit_at(b, i, e, lit) 182 if m > 0 { return i + m } 183 instr = 1 184 } 185 } 186 i = i + 1 187 } 188 return 0 - 1 189} 190func g2_ifield(b: *u8, s: i64, e: i64, lit: *u8, def: i64) -> i64 { 191 let o: i64 = g2_key(b, s, e, lit) 192 if o < 0 { return def } 193 var i: i64 = o 194 var sk: i64 = 1 195 while sk == 1 { 196 if i >= e { sk = 0 } else { if b[i] == (32 as u8) { i = i + 1 } else { sk = 0 } } 197 } 198 var v: i64 = 0 199 var any: i64 = 0 200 var go: i64 = 1 201 while go == 1 { 202 if i >= e { go = 0 } else { 203 let c: i64 = b[i] as i64 204 if c >= 48 { if c <= 57 { v = v*10 + (c-48); any = 1; i = i + 1 } else { go = 0 } } else { go = 0 } 205 } 206 } 207 if any == 0 { return def } 208 return v 209} 210// object range starting at the '{' found at/after off -> end offset (past '}'), or -1 211func g2_objend(b: *u8, off: i64, e: i64) -> i64 { 212 var i: i64 = off 213 var found: i64 = 0 - 1 214 var sk: i64 = 1 215 while sk == 1 { 216 if i >= e { sk = 0 } else { if b[i] == (123 as u8) { found = i; sk = 0 } else { i = i + 1 } } 217 } 218 if found < 0 { return 0 - 1 } 219 i = found 220 var depth: i64 = 0 221 var instr: i64 = 0 222 while i < e { 223 let c: i64 = b[i] as i64 224 if instr == 1 { 225 if c == 92 { i = i + 1 } 226 if c == 34 { instr = 0 } 227 } else { 228 if c == 34 { instr = 1 } 229 if c == 123 { depth = depth + 1 } 230 if c == 125 { depth = depth - 1 } 231 if depth == 0 { return i + 1 } 232 } 233 i = i + 1 234 } 235 return 0 - 1 236} 237func g2_hasstr(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 238 var i: i64 = s 239 while i < e { if g2_lit_at(b, i, e, lit) > 0 { return 1 } i = i + 1 } 240 return 0 241} 242 243func g2_refuse(reason: *u8) -> i64 { 244 hw("GLTF2MESH REFUSED: " as *u8) 245 hw(reason) 246 hw("\n" as *u8) 247 return 0 248} 249 250// does the mesh object's "name" string contain `skip`? (byte substring on the name range) 251func g2_name_has(b: *u8, s: i64, e: i64, skip: *u8) -> i64 { 252 let no: i64 = g2_key(b, s, e, "\x22name\x22:" as *u8) 253 if no < 0 { return 0 } 254 var i: i64 = no 255 var sk: i64 = 1 256 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (34 as u8) { sk = 0 } else { i = i + 1 } } } 257 if i >= e { return 0 } 258 var j: i64 = i + 1 259 var ne: i64 = 0 - 1 260 sk = 1 261 while sk == 1 { if j >= e { sk = 0 } else { if b[j] == (34 as u8) { ne = j; sk = 0 } else { j = j + 1 } } } 262 if ne < 0 { return 0 } 263 return g2_hasstr(b, i + 1, ne, skip) 264} 265 266// filter arg classifier (R2 apparatus separation, debt 1785935557): each of up to two filter args is 267// "report" (print per-prim index/tris/bbox, v0-sampled) | "skipprims=i,j,k" (drop prims by REPORTED 268// global index) | any other string = skip meshes whose name contains it. 269static G2_REP: i64 270static G2_SKL: i64 271static G2_SKN: i64 272func g2_filter(f: *u8) -> i64 { 273 if (f as i64) == 0 { return 0 } 274 if G2_SKL == 0 { G2_SKL = sys_mmap(64*8) as i64 } 275 if g2_lit_at(f, 0, 6, "report" as *u8) == 6 { G2_REP = 1; return 0 } 276 if g2_lit_at(f, 0, 10, "skipprims=" as *u8) == 10 { 277 let sl: *i64 = G2_SKL as *i64 278 var sp: i64 = 10 279 var go2: i64 = 1 280 while go2 == 1 { 281 var v: i64 = 0 282 var any2: i64 = 0 283 var going: i64 = 1 284 while going == 1 { 285 let c2: i64 = f[sp] as i64 286 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); any2 = 1; sp = sp + 1 } else { going = 0 } } else { going = 0 } 287 } 288 if any2 == 1 { if G2_SKN < 64 { sl[G2_SKN] = v; G2_SKN = G2_SKN + 1 } } 289 if f[sp] == (44 as u8) { sp = sp + 1 } else { go2 = 0 } 290 } 291 return 0 292 } 293 return 1 294} 295 296// parse a JSON int array at `arr` (pointing at '[') into out[]; returns count or -1 297func g2_intarray(b: *u8, arr: i64, e: i64, out: *i64, maxn: i64) -> i64 { 298 if arr >= e { return 0 - 1 } 299 var i: i64 = arr 300 var sk: i64 = 1 301 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (91 as u8) { sk = 0 } else { i = i + 1 } } } 302 if i >= e { return 0 - 1 } 303 i = i + 1 304 var n: i64 = 0 305 var go: i64 = 1 306 while go == 1 { 307 if i >= e { go = 0 } else { 308 let c: i64 = b[i] as i64 309 if c == 93 { go = 0 } else { 310 if c >= 48 { if c <= 57 { 311 var v: i64 = 0 312 var d: i64 = 1 313 while d == 1 { 314 let c2: i64 = b[i] as i64 315 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); i = i + 1 } else { d = 0 } } else { d = 0 } 316 if i >= e { d = 0 } 317 } 318 if n >= maxn { return 0 - 2 } 319 out[n] = v 320 n = n + 1 321 } else { i = i + 1 } } else { i = i + 1 } 322 } 323 } 324 } 325 return n 326} 327// find the "name" string range of a node object; returns 1 and fills s/e via the out pair 328func g2_nodename(b: *u8, s: i64, e: i64, out: *i64) -> i64 { 329 let no: i64 = g2_key(b, s, e, "\x22name\x22:" as *u8) 330 out[0] = 0 331 out[1] = 0 332 if no < 0 { return 0 } 333 var i: i64 = no 334 var sk: i64 = 1 335 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (34 as u8) { sk = 0 } else { i = i + 1 } } } 336 if i >= e { return 0 } 337 var j: i64 = i + 1 338 var f: i64 = 0 339 while f == 0 { if j >= e { f = 1 } else { if b[j] == (34 as u8) { f = 1 } else { j = j + 1 } } } 340 if j >= e { return 0 } 341 out[0] = i + 1 342 out[1] = j 343 return 1 344} 345 346// ---- the conversion ---- 347func g2_convert(inp: *u8, outp: *u8, skip: *u8) -> i64 { 348 G2_ERR = 0 349 let fd: i64 = sys_openat_rd(inp) 350 if fd < 0 { g2_refuse("input unreadable" as *u8); return 3 } 351 let b: *u8 = sys_mmap(G2_CAP + 64) 352 var n: i64 = 0 353 var go: i64 = 1 354 while go == 1 { 355 let r: i64 = sys_read(fd, ((b as i64) + n) as *u8, G2_CAP - n) 356 if r <= 0 { go = 0 } else { n = n + r } 357 if n >= G2_CAP { go = 0 } 358 } 359 sys_close(fd) 360 if n >= G2_CAP { g2_refuse("input exceeds 32MiB cap" as *u8); return 3 } 361 if n < 28 { g2_refuse("too small for a glb" as *u8); return 3 } 362 G2_LEN = n 363 if b[0] != (103 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 364 if b[1] != (108 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 365 if b[2] != (84 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 366 if b[3] != (70 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 367 let total: i64 = g2_u32(b, 8) 368 if total != n { g2_refuse("length field disagrees with file size" as *u8); return 3 } 369 let jlen: i64 = g2_u32(b, 12) 370 if 20 + jlen > n { g2_refuse("JSON chunk overruns file" as *u8); return 3 } 371 let js: i64 = 20 372 let je: i64 = 20 + jlen 373 var bino: i64 = 0 - 1 374 var binn: i64 = 0 375 if je + 8 <= n { 376 binn = g2_u32(b, je) 377 if b[je+4] != (66 as u8) { g2_refuse("second chunk is not BIN" as *u8); return 3 } 378 bino = je + 8 379 if bino + binn > n { 380 g2_refuse("BIN chunk overruns file" as *u8) 381 hw(" bino=" as *u8); pn(bino); hw(" binn=" as *u8); pn(binn); hw(" n=" as *u8); pn(n); hw(" jlen=" as *u8); pn(jlen); hw("\n" as *u8) 382 return 3 383 } 384 } 385 if bino < 0 { g2_refuse("no BIN chunk (external buffers unsupported)" as *u8); return 3 } 386 if G2_ERR != 0 { g2_refuse("header read out of bounds" as *u8); return 3 } 387 388 // top-level tables 389 let accA: i64 = g2_topkey(b, js, je, "\x22accessors\x22:" as *u8) 390 let bvA: i64 = g2_topkey(b, js, je, "\x22bufferViews\x22:" as *u8) 391 let meA: i64 = g2_topkey(b, js, je, "\x22meshes\x22:" as *u8) 392 if accA < 0 { g2_refuse("no accessors array" as *u8); return 4 } 393 if bvA < 0 { g2_refuse("no bufferViews array" as *u8); return 4 } 394 if meA < 0 { g2_refuse("no meshes array" as *u8); return 4 } 395 let aS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 396 let aE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 397 let na: i64 = g2_elems(b, accA, je, aS, aE, G2_MAXEL) 398 if na <= 0 { g2_refuse("accessors array unparseable or empty" as *u8); return 4 } 399 let vS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 400 let vE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 401 let nv: i64 = g2_elems(b, bvA, je, vS, vE, G2_MAXEL) 402 if nv <= 0 { g2_refuse("bufferViews array unparseable or empty" as *u8); return 4 } 403 let mS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 404 let mE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 405 let nm: i64 = g2_elems(b, meA, je, mS, mE, G2_MAXEL) 406 if nm <= 0 { g2_refuse("meshes array unparseable or empty" as *u8); return 4 } 407 408 // accessor + bufferView flat tables 409 let accBV: *i64 = sys_mmap(na*8) as *i64 410 let accBO: *i64 = sys_mmap(na*8) as *i64 411 let accCT: *i64 = sys_mmap(na*8) as *i64 412 let accN: *i64 = sys_mmap(na*8) as *i64 413 let accV3: *i64 = sys_mmap(na*8) as *i64 414 var i: i64 = 0 415 while i < na { 416 accBV[i] = g2_ifield(b, aS[i], aE[i], "\x22bufferView\x22:" as *u8, 0 - 1) 417 accBO[i] = g2_ifield(b, aS[i], aE[i], "\x22byteOffset\x22:" as *u8, 0) 418 accCT[i] = g2_ifield(b, aS[i], aE[i], "\x22componentType\x22:" as *u8, 0) 419 accN[i] = g2_ifield(b, aS[i], aE[i], "\x22count\x22:" as *u8, 0) 420 accV3[i] = g2_hasstr(b, aS[i], aE[i], "VEC3" as *u8) 421 i = i + 1 422 } 423 let bvBO: *i64 = sys_mmap(nv*8) as *i64 424 let bvLN: *i64 = sys_mmap(nv*8) as *i64 425 let bvST: *i64 = sys_mmap(nv*8) as *i64 426 i = 0 427 while i < nv { 428 bvBO[i] = g2_ifield(b, vS[i], vE[i], "\x22byteOffset\x22:" as *u8, 0) 429 bvLN[i] = g2_ifield(b, vS[i], vE[i], "\x22byteLength\x22:" as *u8, 0) 430 bvST[i] = g2_ifield(b, vS[i], vE[i], "\x22byteStride\x22:" as *u8, 0) 431 i = i + 1 432 } 433 434 // ---- L2 NAMED PARTS (semantic ladder 1785954089): the donor ALREADY CARRIES its own segmentation 435 // in the skin. JOINTS_0 indexes skins[].joints[], which indexes nodes[], which carry NAMES. So a 436 // rigged donor segments itself for free -- no clustering heuristic, no invented vocabulary, the 437 // parts are named by whoever built the asset. 438 let jn: *i64 = sys_mmap(G2_MAXEL*8) as *i64 439 var njoint: i64 = 0 440 let nn0: *i64 = sys_mmap(G2_MAXEL*8) as *i64 441 let nn1: *i64 = sys_mmap(G2_MAXEL*8) as *i64 442 var nnodes: i64 = 0 443 // ★A MULTI-MESH DONOR HAS ONE SKIN PER MESH: taking skins[0] labelled all 34k triangles with the 444 // HAIR rig (measured 2026-08-05). The skin that owns a primitive is the one on the NODE that 445 // instantiates its mesh, so resolve mesh -> node -> skin, and label by NODE INDEX (globally unique) 446 // rather than by a skin-local joint index (which means a different bone in every skin). 447 let skJ0: *i64 = sys_mmap(G2_MAXEL*8) as *i64 448 let skJN: *i64 = sys_mmap(G2_MAXEL*8) as *i64 449 var nskin: i64 = 0 450 let skA: i64 = g2_topkey(b, js, je, "\x22skins\x22:" as *u8) 451 if skA >= 0 { 452 let sS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 453 let sE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 454 let ns: i64 = g2_elems(b, skA, je, sS, sE, G2_MAXEL) 455 var si: i64 = 0 456 while si < ns { 457 let jA: i64 = g2_key(b, sS[si], sE[si], "\x22joints\x22:" as *u8) 458 skJ0[si] = njoint 459 skJN[si] = 0 460 if jA >= 0 { 461 let r: i64 = g2_intarray(b, jA, sE[si], ((jn as i64) + njoint*8) as *i64, G2_MAXEL - njoint) 462 if r > 0 { skJN[si] = r; njoint = njoint + r } 463 } 464 si = si + 1 465 } 466 nskin = ns 467 } 468 let mskin: *i64 = sys_mmap(G2_MAXEL*8) as *i64 469 var q4: i64 = 0 470 while q4 < G2_MAXEL { mskin[q4] = 0 - 1; q4 = q4 + 1 } 471 let ndA: i64 = g2_topkey(b, js, je, "\x22nodes\x22:" as *u8) 472 if ndA >= 0 { 473 let dS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 474 let dE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 475 let nd: i64 = g2_elems(b, ndA, je, dS, dE, G2_MAXEL) 476 let pr: *i64 = sys_mmap(16) as *i64 477 var q3: i64 = 0 478 while q3 < nd { 479 g2_nodename(b, dS[q3], dE[q3], pr) 480 nn0[q3] = pr[0] 481 nn1[q3] = pr[1] 482 let mh: i64 = g2_ifield(b, dS[q3], dE[q3], "\x22mesh\x22:" as *u8, 0 - 1) 483 let sk2: i64 = g2_ifield(b, dS[q3], dE[q3], "\x22skin\x22:" as *u8, 0 - 1) 484 if mh >= 0 { if mh < G2_MAXEL { if sk2 >= 0 { mskin[mh] = sk2 } } } 485 q3 = q3 + 1 486 } 487 nnodes = nd 488 } 489 let trij: *i64 = sys_mmap(G2_MAXTRI*8 + 64) as *i64 490 491 // output buffer + mm staging (positions are held as integer mm and unit-normalized BEFORE encoding: 492 // the NXMSH2 ecosystem convention is f32 MILLIMETRES -- raw meter-scale VRM floats decode to a 493 // 1.6mm sliver that renders as ~2px and collapses every IoU judge; measured 2026-08-05) 494 let hdr: i64 = G2_HDR + G2_LAYROW 495 let ob: *u8 = sys_mmap(hdr + G2_MAXTRI*G2_TRIREC + G2_MAXTRI*4 + 64) 496 let stg: *i64 = sys_mmap(G2_MAXTRI*12*8 + 64) as *i64 497 var nt: i64 = 0 498 var prims: i64 = 0 499 var accepted: i64 = 0 500 var skipped: i64 = 0 501 var degen: i64 = 0 502 var trunc: i64 = 0 503 var vseen: i64 = 0 504 var miny: i64 = G2_MAGIC_2000000000 505 var maxy: i64 = 0 - G2_MAGIC_2000000000 506 507 let pS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 508 let pE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 509 var mi: i64 = 0 510 var meshes_skipped: i64 = 0 511 while mi < nm { 512 var mesh_ok: i64 = 1 513 if (skip as i64) != 0 { if g2_name_has(b, mS[mi], mE[mi], skip) == 1 { mesh_ok = 0; meshes_skipped = meshes_skipped + 1 } } 514 var prA: i64 = 0 - 1 515 if mesh_ok == 1 { prA = g2_key(b, mS[mi], mE[mi], "\x22primitives\x22:" as *u8) } 516 if prA >= 0 { 517 let np: i64 = g2_elems(b, prA, mE[mi], pS, pE, G2_MAXEL) 518 var pi: i64 = 0 519 while pi < np { 520 prims = prims + 1 521 var ok: i64 = 1 522 let mode: i64 = g2_ifield(b, pS[pi], pE[pi], "\x22mode\x22:" as *u8, G2_MODE_TRIS) 523 if mode != G2_MODE_TRIS { ok = 0 } 524 let atA: i64 = g2_key(b, pS[pi], pE[pi], "\x22attributes\x22:" as *u8) 525 var posi: i64 = 0 - 1 526 if atA < 0 { ok = 0 } 527 if ok == 1 { 528 let atE2: i64 = g2_objend(b, atA, pE[pi]) 529 if atE2 < 0 { ok = 0 } else { posi = g2_ifield(b, atA, atE2, "\x22POSITION\x22:" as *u8, 0 - 1) } 530 } 531 var joi: i64 = 0 - 1 532 if ok == 1 { if atA >= 0 { let atE3: i64 = g2_objend(b, atA, pE[pi]); if atE3 > 0 { joi = g2_ifield(b, atA, atE3, "\x22JOINTS_0\x22:" as *u8, 0 - 1) } } } 533 let idxi: i64 = g2_ifield(b, pS[pi], pE[pi], "\x22indices\x22:" as *u8, 0 - 1) 534 if posi < 0 { ok = 0 } 535 if idxi < 0 { ok = 0 } 536 if posi >= na { ok = 0 } 537 if idxi >= na { ok = 0 } 538 if ok == 1 { if accCT[posi] != G2_CT_F32 { ok = 0 } } 539 if ok == 1 { if accV3[posi] != 1 { ok = 0 } } 540 if ok == 1 { if accCT[idxi] != G2_CT_U16 { if accCT[idxi] != G2_CT_U32 { ok = 0 } } } 541 if ok == 1 { if accN[idxi] % 3 != 0 { ok = 0 } } 542 if ok == 1 { if accBV[posi] < 0 { ok = 0 } } 543 if ok == 1 { if accBV[idxi] < 0 { ok = 0 } } 544 if ok == 1 { if accBV[posi] >= nv { ok = 0 } } 545 if ok == 1 { if accBV[idxi] >= nv { ok = 0 } } 546 if ok == 1 { if G2_SKN > 0 { let sl2: *i64 = G2_SKL as *i64; var si2: i64 = 0; while si2 < G2_SKN { if sl2[si2] == prims - 1 { ok = 0 } si2 = si2 + 1 } } } 547 if ok == 0 { skipped = skipped + 1 } else { 548 let pb: i64 = accBV[posi] 549 let ib2: i64 = accBV[idxi] 550 var pstride: i64 = bvST[pb] 551 if pstride == 0 { pstride = 12 } 552 let pbase: i64 = bino + bvBO[pb] + accBO[posi] 553 let ibase: i64 = bino + bvBO[ib2] + accBO[idxi] 554 var isz: i64 = 2 555 if accCT[idxi] == G2_CT_U32 { isz = 4 } 556 let pcount: i64 = accN[posi] 557 vseen = vseen + pcount 558 let ntri: i64 = accN[idxi] / 3 559 var pmnx: i64 = G2_MAGIC_2000000000 560 var pmxx: i64 = 0 - G2_MAGIC_2000000000 561 var pmny: i64 = G2_MAGIC_2000000000 562 var pmxy: i64 = 0 - G2_MAGIC_2000000000 563 var pmnz: i64 = G2_MAGIC_2000000000 564 var pmxz: i64 = 0 - G2_MAGIC_2000000000 565 var t: i64 = 0 566 while t < ntri { 567 if nt >= G2_MAXTRI { trunc = 1; t = t + ntri } else { 568 var i0: i64 = 0 569 var i1: i64 = 0 570 var i2: i64 = 0 571 if isz == 2 { i0 = g2_u16(b, ibase + (t*3)*2); i1 = g2_u16(b, ibase + (t*3+1)*2); i2 = g2_u16(b, ibase + (t*3+2)*2) } 572 if isz == 4 { i0 = g2_u32(b, ibase + (t*3)*4); i1 = g2_u32(b, ibase + (t*3+1)*4); i2 = g2_u32(b, ibase + (t*3+2)*4) } 573 var okv: i64 = 1 574 if i0 >= pcount { okv = 0 } 575 if i1 >= pcount { okv = 0 } 576 if i2 >= pcount { okv = 0 } 577 if G2_ERR != 0 { okv = 0 } 578 if okv == 1 { 579 let w00: i64 = g2_u32(b, pbase + i0*pstride) 580 let w01: i64 = g2_u32(b, pbase + i0*pstride + 4) 581 let w02: i64 = g2_u32(b, pbase + i0*pstride + 8) 582 let w10: i64 = g2_u32(b, pbase + i1*pstride) 583 let w11: i64 = g2_u32(b, pbase + i1*pstride + 4) 584 let w12: i64 = g2_u32(b, pbase + i1*pstride + 8) 585 let w20: i64 = g2_u32(b, pbase + i2*pstride) 586 let w21: i64 = g2_u32(b, pbase + i2*pstride + 4) 587 let w22: i64 = g2_u32(b, pbase + i2*pstride + 8) 588 if G2_ERR != 0 { okv = 0 } else { 589 let x0: i64 = g2_f32mm(w00) 590 let y0: i64 = g2_f32mm(w01) 591 let z0: i64 = g2_f32mm(w02) 592 let x1: i64 = g2_f32mm(w10) 593 let y1: i64 = g2_f32mm(w11) 594 let z1: i64 = g2_f32mm(w12) 595 let x2: i64 = g2_f32mm(w20) 596 let y2: i64 = g2_f32mm(w21) 597 let z2: i64 = g2_f32mm(w22) 598 if y0 < miny { miny = y0 } 599 if y0 > maxy { maxy = y0 } 600 if y1 < miny { miny = y1 } 601 if y1 > maxy { maxy = y1 } 602 if y2 < miny { miny = y2 } 603 if y2 > maxy { maxy = y2 } 604 if x0 < pmnx { pmnx = x0 } 605 if x0 > pmxx { pmxx = x0 } 606 if y0 < pmny { pmny = y0 } 607 if y0 > pmxy { pmxy = y0 } 608 if z0 < pmnz { pmnz = z0 } 609 if z0 > pmxz { pmxz = z0 } 610 let e1x: i64 = x1-x0 611 let e1y: i64 = y1-y0 612 let e1z: i64 = z1-z0 613 let e2x: i64 = x2-x0 614 let e2y: i64 = y2-y0 615 let e2z: i64 = z2-z0 616 var cx: i64 = e1y*e2z - e1z*e2y 617 var cy: i64 = e1z*e2x - e1x*e2z 618 var cz: i64 = e1x*e2y - e1y*e2x 619 let mag: i64 = g2_isqrt(cx*cx + cy*cy + cz*cz) 620 if mag == 0 { degen = degen + 1 } else { 621 let so: i64 = nt*12 622 stg[so]=x0; stg[so+1]=y0; stg[so+2]=z0 623 stg[so+3]=x1; stg[so+4]=y1; stg[so+5]=z1 624 stg[so+6]=x2; stg[so+7]=y2; stg[so+8]=z2 625 // part label = the joint bound to vertex 0 (rigid-dominant proxy; 626 // full weight-max over 3 verts is the named refinement) 627 var lab: i64 = 0 - 1 628 if joi >= 0 { if joi < na { if accBV[joi] >= 0 { if accBV[joi] < nv { 629 var jstride: i64 = bvST[accBV[joi]] 630 var jsz: i64 = 1 631 if accCT[joi] == G2_CT_U16 { jsz = 2 } 632 if jstride == 0 { jstride = jsz*4 } 633 let jbase: i64 = bino + bvBO[accBV[joi]] + accBO[joi] 634 if jsz == 1 { lab = b[jbase + i0*jstride] as i64 } 635 if jsz == 2 { lab = g2_u16(b, jbase + i0*jstride) } 636 // skin-local joint -> NODE index, via the skin that owns THIS mesh 637 var sk3: i64 = 0 - 1 638 if mi < G2_MAXEL { sk3 = mskin[mi] } 639 if sk3 >= 0 { if sk3 < nskin { if lab >= 0 { if lab < skJN[sk3] { lab = jn[skJ0[sk3] + lab] } else { lab = 0 - 1 } } } } else { lab = 0 - 1 } 640 } } } } 641 trij[nt] = lab 642 stg[so+9] = cx*G2_Q14/mag 643 stg[so+10] = cy*G2_Q14/mag 644 stg[so+11] = cz*G2_Q14/mag 645 nt = nt + 1 646 } 647 } 648 } 649 t = t + 1 650 } 651 } 652 if G2_REP == 1 { hw("PRIM " as *u8); pn(prims - 1); hw(" mesh " as *u8); pn(mi); hw(" tris " as *u8); pn(ntri); hw(" x " as *u8); pn(pmnx); hw(".." as *u8); pn(pmxx); hw(" y " as *u8); pn(pmny); hw(".." as *u8); pn(pmxy); hw(" z " as *u8); pn(pmnz); hw(".." as *u8); pn(pmxz); hw("\n" as *u8) } 653 accepted = accepted + 1 654 } 655 pi = pi + 1 656 } 657 } 658 mi = mi + 1 659 } 660 661 if nt == 0 { g2_refuse("zero triangles accepted -- a form oracle with no surface is vacuous" as *u8); return 5 } 662 if trunc == 1 { g2_refuse("tri cap hit -- a truncated oracle is a lying ruler (raise G2_MAXTRI deliberately)" as *u8); return 7 } 663 if G2_ERR != 0 { g2_refuse("out-of-bounds read during extraction -- file declares data it does not carry" as *u8); return 7 } 664 // unit normalization: land the body in plausible human millimetres or REFUSE (never emit a sliver oracle) 665 let hgt: i64 = maxy - miny 666 var dv: i64 = 0 667 if hgt >= 500 { if hgt <= G2_MAGIC_2500 { dv = 1 } } 668 if hgt >= G2_MAGIC_500000 { if hgt <= G2_MAGIC_2500000 { dv = 1000 } } 669 if dv == 0 { g2_refuse("bbox height lands outside 500-2500mm at every power-of-1000 scale -- refusing to guess units" as *u8); hw(" height_raw=" as *u8); pn(hgt); hw("\n" as *u8); return 8 } 670 // ---- layer plan: one layer per USED joint, contiguous by construction (NXMSH2 requires ranges). 671 // A donor with no skin emits the single "skin" layer exactly as before (additive, never a regression). 672 var nlay: i64 = 1 673 let lj: *i64 = sys_mmap(G2_MAXEL*8) as *i64 674 let lcnt: *i64 = sys_mmap(G2_MAXEL*8) as *i64 675 if njoint > 0 { 676 var u: i64 = 0 677 while u < G2_MAXEL { lcnt[u] = 0; u = u + 1 } 678 var t3: i64 = 0 679 while t3 < nt { let L: i64 = trij[t3]; if L >= 0 { if L < G2_MAXEL { lcnt[L] = lcnt[L] + 1 } } t3 = t3 + 1 } 680 nlay = 0 681 u = 0 682 while u < G2_MAXEL { if lcnt[u] > 0 { lj[nlay] = u; nlay = nlay + 1 } u = u + 1 } 683 if nlay == 0 { nlay = 1 } 684 } 685 let hdr2: i64 = G2_HDR + nlay*G2_LAYROW 686 let gc: i64 = g2_enc(G2_GREY, 1000) 687 var oc: i64 = 0 688 var Lx: i64 = 0 689 while Lx < nlay { 690 var wt: i64 = 0 691 while wt < nt { 692 var take: i64 = 1 693 if nlay > 1 { take = 0; if trij[wt] == lj[Lx] { take = 1 } } 694 if take == 1 { 695 let so2: i64 = wt*12 696 let o: i64 = hdr2 + oc*G2_TRIREC 697 var pc: i64 = 0 698 while pc < 9 { g2_wr32(ob, o + pc*4, g2_enc(stg[so2+pc]/dv, 1)); pc = pc + 1 } 699 let nb: i64 = g2_enc(stg[so2+9], G2_Q14) 700 let nb2: i64 = g2_enc(stg[so2+10], G2_Q14) 701 let nb3: i64 = g2_enc(stg[so2+11], G2_Q14) 702 var q: i64 = 0 703 while q < 3 { g2_wr32(ob, o+36+q*12, nb); g2_wr32(ob, o+40+q*12, nb2); g2_wr32(ob, o+44+q*12, nb3); q = q + 1 } 704 g2_wr32(ob, o+72, gc); g2_wr32(ob, o+76, gc); g2_wr32(ob, o+80, gc) 705 oc = oc + 1 706 } 707 wt = wt + 1 708 } 709 Lx = Lx + 1 710 } 711 miny = miny/dv 712 maxy = maxy/dv 713 // header + single layer "skin" + per-tri layer ids 714 ob[0]=78 as u8; ob[1]=88 as u8; ob[2]=77 as u8; ob[3]=83 as u8 715 ob[4]=72 as u8; ob[5]=50 as u8; ob[6]=0 as u8; ob[7]=0 as u8 716 g2_wr32(ob, 8, nlay); g2_wr32(ob, 12, nt) 717 // layer table: 16-byte name from the donor's own node name, then offset+count 718 var run: i64 = 0 719 var Ly: i64 = 0 720 while Ly < nlay { 721 let lb: i64 = G2_HDR + Ly*G2_LAYROW 722 var q2: i64 = 0 723 while q2 < 16 { ob[lb+q2] = 0 as u8; q2 = q2 + 1 } 724 var cnt2: i64 = nt 725 if nlay > 1 { 726 let jidx: i64 = lj[Ly] 727 cnt2 = lcnt[jidx] 728 let nd2: i64 = jidx 729 if nd2 >= 0 { if nd2 < nnodes { if nn1[nd2] > nn0[nd2] { 730 var w2: i64 = 0 731 while w2 < 15 { if nn0[nd2]+w2 < nn1[nd2] { ob[lb+w2] = b[nn0[nd2]+w2] } w2 = w2 + 1 } 732 } } } 733 if ob[lb] == (0 as u8) { ob[lb]=106 as u8; ob[lb+1]=111 as u8; ob[lb+2]=105 as u8; ob[lb+3]=110 as u8; ob[lb+4]=116 as u8 } 734 } else { ob[lb]=115 as u8; ob[lb+1]=107 as u8; ob[lb+2]=105 as u8; ob[lb+3]=110 as u8 } 735 g2_wr32(ob, lb+16, run); g2_wr32(ob, lb+20, cnt2) 736 run = run + cnt2 737 Ly = Ly + 1 738 } 739 var z: i64 = 0 740 var Lz: i64 = 0 741 var zc: i64 = 0 742 while Lz < nlay { 743 var cz: i64 = nt 744 if nlay > 1 { cz = lcnt[lj[Lz]] } 745 var k2: i64 = 0 746 while k2 < cz { g2_wr32(ob, hdr2 + nt*G2_TRIREC + zc*4, Lz); zc = zc + 1; k2 = k2 + 1 } 747 Lz = Lz + 1 748 } 749 let bytes: i64 = hdr2 + nt*G2_TRIREC + nt*4 750 let ofd: i64 = sys_openat_wr(outp, 420) 751 if ofd < 0 { g2_refuse("output path unwritable" as *u8); return 6 } 752 sys_write(ofd, ob, bytes) 753 sys_close(ofd) 754 755 hw("{\x22organ\x22:\x22nx_gltf2mesh\x22,\x22prims\x22:" as *u8); pn(prims) 756 hw(",\x22prims_accepted\x22:" as *u8); pn(accepted) 757 hw(",\x22prims_skipped\x22:" as *u8); pn(skipped) 758 hw(",\x22verts_seen\x22:" as *u8); pn(vseen) 759 hw(",\x22tris\x22:" as *u8); pn(nt) 760 hw(",\x22degenerate_dropped\x22:" as *u8); pn(degen) 761 hw(",\x22meshes_skipped_by_name\x22:" as *u8); pn(meshes_skipped) 762 hw(",\x22skin_joints\x22:" as *u8); pn(njoint) 763 hw(",\x22named_parts_emitted\x22:" as *u8); pn(nlay) 764 hw(",\x22miny_mm\x22:" as *u8); pn(miny) 765 hw(",\x22maxy_mm\x22:" as *u8); pn(maxy) 766 hw(",\x22scale_div\x22:" as *u8); pn(dv) 767 hw(",\x22bytes\x22:" as *u8); pn(bytes) 768 hw(",\x22note\x22:\x22bind-pose model space; node transforms not applied (skinned donors); morph-target POSITIONs excluded by attributes-scope\x22}" as *u8) 769 hw("\n" as *u8) 770 return 0 771} 772 773// ---- selftest: build a real 1-tri glb on disk, convert it, then three malformed inputs must REFUSE ---- 774func st_wr(path: *u8, buf: *u8, n: i64) -> i64 { 775 let fd: i64 = sys_openat_wr(path, 420) 776 if fd < 0 { return 0 - 1 } 777 sys_write(fd, buf, n) 778 sys_close(fd) 779 return 0 780} 781func st_mkglb(buf: *u8, breakmode: i64) -> i64 { 782 // JSON (compact, machine-shape) 783 let j: *u8 = "{\x22asset\x22:{\x22version\x22:\x222.0\x22},\x22meshes\x22:[{\x22primitives\x22:[{\x22attributes\x22:{\x22POSITION\x22:0},\x22indices\x22:1}]}],\x22accessors\x22:[{\x22bufferView\x22:0,\x22componentType\x22:5126,\x22count\x22:3,\x22type\x22:\x22VEC3\x22},{\x22bufferView\x22:1,\x22componentType\x22:5123,\x22count\x22:3,\x22type\x22:\x22SCALAR\x22}],\x22bufferViews\x22:[{\x22buffer\x22:0,\x22byteOffset\x22:0,\x22byteLength\x22:36},{\x22buffer\x22:0,\x22byteOffset\x22:36,\x22byteLength\x22:6}],\x22buffers\x22:[{\x22byteLength\x22:44}]}" as *u8 784 var jl: i64 = 0 785 while j[jl] != (0 as u8) { jl = jl + 1 } 786 var jp: i64 = jl 787 while jp % 4 != 0 { jp = jp + 1 } 788 // bin length appears ONLY as a literal below: locals in this frame mis-read (nx_cc bug 1785936860) 789 let total: i64 = 12 + 8 + jp + 8 + 44 790 var o: i64 = 0 791 buf[0]=103 as u8; buf[1]=108 as u8; buf[2]=84 as u8; buf[3]=70 as u8 792 if breakmode == 2 { buf[0]=88 as u8 } 793 g2_wr32(buf, 4, 2) 794 g2_wr32(buf, 8, total) 795 g2_wr32(buf, 12, jp) 796 buf[16]=74 as u8; buf[17]=83 as u8; buf[18]=79 as u8; buf[19]=78 as u8 797 var i: i64 = 0 798 while i < jl { buf[20+i] = j[i]; i = i + 1 } 799 while i < jp { buf[20+i] = 32 as u8; i = i + 1 } 800 o = 20 + jp 801 g2_wr32(buf, o, 44) 802 buf[o+4]=66 as u8; buf[o+5]=73 as u8; buf[o+6]=78 as u8; buf[o+7]=0 as u8 803 // verts (0,0,0) (1,0,0) (0,1,0) as f32 -- offsets all o+N literals, NO further locals (nx_cc bug 1785936860) 804 g2_wr32(buf, o+8, 0); g2_wr32(buf, o+12, 0); g2_wr32(buf, o+16, 0) 805 g2_wr32(buf, o+20, g2_enc(1000, 1000)); g2_wr32(buf, o+24, 0); g2_wr32(buf, o+28, 0) 806 g2_wr32(buf, o+32, 0); g2_wr32(buf, o+36, g2_enc(1000, 1000)); g2_wr32(buf, o+40, 0) 807 buf[o+44]=0 as u8; buf[o+45]=0 as u8; buf[o+46]=1 as u8; buf[o+47]=0 as u8 808 buf[o+48]=2 as u8; buf[o+49]=0 as u8; buf[o+50]=0 as u8; buf[o+51]=0 as u8 809 if breakmode == 1 { return total - 9 } 810 return total 811} 812func g2_selftest() -> i64 { 813 var fails: i64 = 0 814 let buf: *u8 = sys_mmap(G2_MAGIC_4096) 815 let n0: i64 = st_mkglb(buf, 0) 816 st_wr("/tmp/g2_ok.glb" as *u8, buf, n0) 817 let r0: i64 = g2_convert("/tmp/g2_ok.glb" as *u8, "/tmp/g2_ok.nxmesh" as *u8, 0 as *u8) 818 if r0 == 0 { 819 let vb: *u8 = sys_mmap(G2_MAGIC_4096) 820 let vfd: i64 = sys_openat_rd("/tmp/g2_ok.nxmesh" as *u8) 821 let vn: i64 = sys_read(vfd, vb, G2_MAGIC_4096) 822 sys_close(vfd) 823 var t0: i64 = 0 824 if vn > 40 { if vb[0] == (78 as u8) { if vb[5] == (50 as u8) { t0 = 1 } } } 825 if t0 == 1 { hw("T0 PASS convert emits valid NXMSH2 (1 tri)\n" as *u8) } else { fails = fails + 1; hw("T0 FAIL output not NXMSH2\n" as *u8) } 826 } else { fails = fails + 1; hw("T0 FAIL convert refused a valid glb\n" as *u8) } 827 let n1: i64 = st_mkglb(buf, 1) 828 st_wr("/tmp/g2_trunc.glb" as *u8, buf, n1) 829 if g2_convert("/tmp/g2_trunc.glb" as *u8, "/tmp/g2_x1.nxmesh" as *u8, 0 as *u8) != 0 { hw("T1 PASS truncated glb refused\n" as *u8) } else { fails = fails + 1; hw("T1 FAIL truncated glb accepted\n" as *u8) } 830 let n2: i64 = st_mkglb(buf, 2) 831 st_wr("/tmp/g2_badmagic.glb" as *u8, buf, n2) 832 if g2_convert("/tmp/g2_badmagic.glb" as *u8, "/tmp/g2_x2.nxmesh" as *u8, 0 as *u8) != 0 { hw("T2 PASS bad magic refused\n" as *u8) } else { fails = fails + 1; hw("T2 FAIL bad magic accepted\n" as *u8) } 833 if g2_convert("/tmp/g2_absent_zz.glb" as *u8, "/tmp/g2_x3.nxmesh" as *u8, 0 as *u8) != 0 { hw("T3 PASS absent input refused\n" as *u8) } else { fails = fails + 1; hw("T3 FAIL absent input accepted\n" as *u8) } 834 if fails == 0 { hw("GLTF2MESH-SELFTEST GREEN 4/4\n" as *u8); return 0 } 835 hw("GLTF2MESH-SELFTEST RED fails=" as *u8); pn(fails); hw("\n" as *u8) 836 return 1 837} 838 839func main(argc: i64, argv: *i64) -> i64 { 840 if argc >= 2 { 841 let a1: *u8 = argv[1] as *u8 842 if g2_lit_at(a1, 0, 8, "selftest" as *u8) == 8 { let rc: i64 = g2_selftest(); sys_exit(rc); return rc } 843 } 844 if argc < 3 { 845 hw("usage: nx_gltf2mesh <in.glb|.vrm> <out.nxmesh> [skip-mesh-name-substr] | selftest\n" as *u8) 846 sys_exit(2) 847 return 2 848 } 849 var skip: *u8 = 0 as *u8 850 if argc >= 4 { if g2_filter(argv[3] as *u8) == 1 { skip = argv[3] as *u8 } } 851 if argc >= 5 { if g2_filter(argv[4] as *u8) == 1 { skip = argv[4] as *u8 } } 852 let rc2: i64 = g2_convert(argv[1] as *u8, argv[2] as *u8, skip) 853 sys_exit(rc2) 854 return rc2 855}