code wiki / _hdl_build / nx_gltf2mesh.nx

nx_gltf2mesh.nx source

↩ module page · 1815 lines · 90550 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// nx_gltf2mesh <in.glb|.vrm> <out.nxa> -- R22 RIG-CARRYING INGEST (charsim.plan, contract 14// symbol g2_skin_weights): when the OUTPUT name ends in .nxa the donor's skin is carried into 15// the estate's rig container NXANIM01 -- VERT (shared vertex table, mm) / TRIS (index triples) 16// / SKEL (one record per skins[].joints entry: parent, bind position from inverseBindMatrices, 17// identity quat) / SKIN (per vertex four joint indices + four Q12 weights summing to EXACTLY 18// 4096). That layout is the one nx_nxa_skin WRITES, nx_nxa_check ENFORCES and nx_nxa_rig_emit 19// DIVIDES BY -- read from those three, never invented here. NXMSH2 structurally cannot carry a 20// rig (84-byte triangle soup, no joint section, 17 consumers hardcode that arithmetic), which 21// is why the round trip measured 2026-08-22 lost every named part of every rigged donor. 22// An unskinned donor asked for .nxa is REFUSED by name; the .nxmesh path is byte-identical. 23// license_tier: ORIGINAL expect_exit: 0 24import "nx_syscalls.nx" 25import "nx_nxa.nx" 26import "nx_vecmath.nx" 27const G2_MAGIC_2000000000: i64 = 2000000000 28const G2_MAGIC_4096: i64 = 4096 29 30// ---- UNIT DETERMINATION (see g2_pick_scale for the whole argument) ---- 31// glTF 2.0 fixes the unit of length as the METRE, so a conformant donor needs no guessing at all. 32// The sniffing below exists only for donors authored in some other power of 1000 (mm is common). 33const G2_SCALE_STEP: i64 = 1000 34const G2_MAX_SCALE_TRIES: i64 = 4 35const G2_DEF_SLIVER_FLOOR_MM: i64 = 100 36const G2_ASCII_EQ: i64 = 61 37const G2_ASCII_NL: i64 = 10 38const G2_ASCII_ZERO: i64 = 48 39const G2_ASCII_NINE: i64 = 57 40const G2_CONFBUF: i64 = 16 41const G2_UNITS_CONF: *u8 = "knowledge/gltf2mesh_units.conf" 42const G2_UCONF_PFXLEN: i64 = 11 // len("units_conf=") 43 44const G2_CAP: i64 = 33554432 45const G2_MAXEL: i64 = 1024 46const G2_MAXTRI: i64 = 400000 47const G2_Q14: i64 = 16384 48const G2_M8388607: i64 = 8388607 49const G2_M8388608: i64 = 8388608 50const G2_GREY: i64 = 780 51const G2_CT_F32: i64 = 5126 52const G2_CT_U16: i64 = 5123 53const G2_CT_U32: i64 = 5125 54const G2_MODE_TRIS: i64 = 4 55const G2_HDR: i64 = 16 56const G2_LAYROW: i64 = 24 57const G2_TRIREC: i64 = 84 58 59// ---- R22 rig-carrying ingest (g2_skin_weights) -- every bound named for what it is ---- 60const G2_CT_U8: i64 = 5121 // glTF UNSIGNED_BYTE: JOINTS_0, normalized WEIGHTS_0 61const G2_Q12: i64 = 4096 // SKIN weight unit AND SKEL quat unit (nx_nxa_check: sum == 4096) 62const G2_VEC4_SLOTS: i64 = 4 // JOINTS_0/WEIGHTS_0 are VEC4: four influences per vertex 63const G2_U8_MAX: i64 = 255 // normalized u8 denominator (glTF: v/255) 64const G2_U16_MAX: i64 = 65535 // normalized u16 denominator (glTF: v/65535) 65const G2_F32_MANT_DENOM: i64 = 16777216 // 2^24: a float32 weight in [0,1] is exact to below 1/2^24 66const G2_F32_BYTES: i64 = 4 67const G2_U16_BYTES: i64 = 2 68const G2_U32_BYTES: i64 = 4 69const G2_VEC3_F32_BYTES: i64 = 12 70const G2_MAT4_BYTES: i64 = 64 71const G2_MAT4_COL: i64 = 4 // column-major: element (row r, col c) sits at c*4 + r 72const G2_IBM_T_COL: i64 = 3 // the translation column of a MAT4 73const G2_IBM_W_ROW: i64 = 3 // the affine row: must decode to 0 0 0 1 74const G2_Q12_IDENT_TOL: i64 = 1 // f32 1.0 / 0.0 decode EXACTLY to 4096 / 0 and the decode truncates below one unit, so any rotation or scale in a bind matrix shows as a deviation above ONE unit 75const G2_NXA_HDR_BYTES: i64 = 32 // magic, version, section count, TOC check 76const G2_NXA_TOC_BYTES: i64 = 32 // tag, byte offset, wordlen, payload check 77const G2_NXA_TOC_WORDS: i64 = 4 78const G2_NXA_SECS: i64 = 4 // VERT TRIS SKEL SKIN 79const G2_SKEL_WORDS: i64 = 8 // parent, tx, ty, tz, qx, qy, qz, qw 80const G2_SKIN_WORDS: i64 = 8 // j0 j1 j2 j3 w0 w1 w2 w3 81const G2_VERT_WORDS: i64 = 3 82const G2_TRI_WORDS: i64 = 3 83const G2_NXA_EXT_LEN: i64 = 4 // ".nxa" 84const G2_IBM_DEFAULT_IDENTITY: i64 = 1 // glTF: absent inverseBindMatrices == identity == every joint binds at the origin 85const G2_CX_WORDS: i64 = 32 // the emitter's context record 86// g2_skin_weights / g2_ibm_bind return codes (the caller names each one in its refusal) 87const G2_SW_OK: i64 = 0 88const G2_SW_SHAPE: i64 = 1 89const G2_SW_JOINT_RANGE: i64 = 2 90const G2_SW_SUM_TOL: i64 = 3 91const G2_SW_ZERO_WEIGHT: i64 = 4 92const G2_SW_NEGATIVE: i64 = 5 93const G2_IB_NONIDENT: i64 = 2 94const G2_IB_SHEAR: i64 = 3 95const G2_MAT3_DIM: i64 = 3 96// A^T A off-diagonal (SHEAR) tolerance, DERIVED from decode error: each A element truncates below 97// one Q12 unit, a product of two carries error below two units after the Q12 rescale, three summed 98// terms below six, plus one truncating division = 7. Measured on the reference VRM (seed-san, 99// 132 IBMs): every off-diagonal decodes to 0.00 q12 -- the bound is decode headroom, not a fit. 100const G2_SHEAR_TOL_Q12: i64 = 7 101// two truncating mm-decodes of nearly-equal f32 values differ by at most ONE unit 102const G2_IBM_CONFLICT_TOL_MM: i64 = 1 103 104func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 105func 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 } 106 107// ---- bounds-checked little-endian readers (D1: refuse, never wander) ---- 108static G2_LEN: i64 109static G2_ERR: i64 110func g2_u16(b: *u8, o: i64) -> i64 { 111 if o < 0 { G2_ERR = 1; return 0 } 112 if o + 2 > G2_LEN { G2_ERR = 1; return 0 } 113 return (b[o] as i64) + ((b[o+1] as i64)<<8) 114} 115func g2_u32(b: *u8, o: i64) -> i64 { 116 if o < 0 { G2_ERR = 1; return 0 } 117 if o + 4 > G2_LEN { G2_ERR = 1; return 0 } 118 return (b[o] as i64) + ((b[o+1] as i64)<<8) + ((b[o+2] as i64)<<16) + ((b[o+3] as i64)<<24) 119} 120// IEEE754 float32 bits -> integer at `scale` units per 1.0 (truncating), the nx_fbx_measure recipe 121// generalised: scale=1000 is millimetres-from-metres (g2_f32mm), scale=G2_Q12 is the SKIN/SKEL unit. 122// ONE decoder for every f32 this organ reads -- a second copy for weights would be the duplicate 123// ruler, and the two would drift. 124func g2_f32s(w: i64, scale: i64) -> i64 { 125 let sign: i64 = (w >> 31) & 1 126 let expo: i64 = (w >> 23) & 255 127 if expo == 0 { return 0 } 128 var mant: i64 = (w & G2_M8388607) | G2_M8388608 129 let sh: i64 = expo - 127 130 var v: i64 = 0 131 if sh >= 23 { if sh - 23 > 30 { G2_ERR = 1; return 0 } } 132 if sh >= 23 { v = mant * scale * (1 << (sh - 23)) } 133 if sh < 23 { if 23 - sh > 62 { return 0 } } 134 if sh < 23 { v = (mant * scale) >> (23 - sh) } 135 if sign == 1 { return 0 - v } 136 return v 137} 138// IEEE754 float32 bits -> integer millimetres (metres*1000) 139func g2_f32mm(w: i64) -> i64 { return g2_f32s(w, 1000) } 140func g2_ceil_div(a: i64, d: i64) -> i64 { return (a + d - 1) / d } 141// ---- conf read: line-anchored key=value. An unanchored match would read a number out of a 142// comment and answer confidently with the wrong bound. Returns `miss` when the key is absent, so 143// an ABSENT key stays distinguishable from a key whose value is legitimately zero. 144func g2_conf_i64(path: *u8, key: *u8, miss: i64) -> i64 { 145 let lp: *i64 = sys_mmap(G2_CONFBUF) as *i64 146 let cb: *u8 = sys_read_file(path, lp) 147 if (cb as i64) == 0 { return miss } 148 let n: i64 = lp[0] 149 var i: i64 = 0 150 var ls: i64 = 1 151 while i < n { 152 if ls == 1 { 153 var k: i64 = 0 154 var m: i64 = 1 155 while key[k] != (0 as u8) { 156 if i + k >= n { m = 0 } 157 if m == 1 { if cb[i+k] != key[k] { m = 0 } } 158 k = k + 1 159 } 160 if m == 1 { if i + k < n { if (cb[i+k] as i64) == G2_ASCII_EQ { 161 var o: i64 = i + k + 1 162 var v: i64 = 0 163 var d: i64 = 0 164 var go: i64 = 1 165 while go == 1 { 166 if o >= n { go = 0 } 167 if go == 1 { 168 let c: i64 = cb[o] as i64 169 if c < G2_ASCII_ZERO { go = 0 } 170 if c > G2_ASCII_NINE { go = 0 } 171 if go == 1 { v = v*10 + (c - G2_ASCII_ZERO); d = d + 1; o = o + 1 } 172 } 173 } 174 if d > 0 { return v } 175 } } } 176 } 177 if (cb[i] as i64) == G2_ASCII_NL { ls = 1 } else { ls = 0 } 178 i = i + 1 179 } 180 return miss 181} 182 183// ---- pick the authoring scale. Returns the divisor, or 0 if no power of 1000 lands the body 184// above the sliver floor. 185// 186// WHY THE OLD 500-2500mm WINDOW WAS WRONG, AND IT WAS NOT THE NUMBERS. That window asserted "the 187// subject is a HUMAN between 0.5 and 2.5 metres" -- ANTHROPOMETRY welded into a FORMAT CONVERTER. 188// It refused human.glb at 2.615 m (measured 2026-08-22 over the full 18-file GLB population, 189// corpus_complete=1) for being 115 mm too tall. HEIGHT IS NOT A DEFECT, and this organ also 190// converts wolves and direwolves, for which a human band is meaningless. 191// 192// The two questions were CONFLATED: (a) which power of 1000 is this donor authored in, and (b) is 193// the result a plausible body. Only (a) is the converter's business. The single MEASURED failure 194// is the SLIVER -- a metre-scale float read unscaled decodes to a ~1.6 mm body that renders as 195// ~2 px and collapses every IoU judge (2026-08-05, recorded in this file's own staging comment). 196// So the FLOOR is load-bearing and the CEILING IS DERIVED FROM IT: ceil = floor*STEP - 1. That 197// makes the per-scale bands TILE THE NUMBER LINE EXACTLY -- no overlap, so a height can never be 198// ambiguous between two scales, and no gap, so no legitimate asset falls between them. A tiled 199// partition is a decision procedure; the old pair of disjoint windows was two guesses with a hole 200// between them, and human.glb fell in the hole. 201// 202// THE IMPRECISION I CHOSE TO LIVE WITH, STATED RATHER THAN HIDDEN: one floor sets BOTH ends. At 203// floor=100mm the organ handles assets from 10 cm to 100 m authored in metres; something larger 204// than 100 m would be misread one scale down. That is a real limit, it is why the value lives in 205// a conf instead of here, and it is why the chosen scale is ANNOUNCED on every run. 206func g2_pick_scale(hgt: i64, floor_mm: i64) -> i64 { 207 var dv: i64 = 1 208 var tries: i64 = 0 209 while tries < G2_MAX_SCALE_TRIES { 210 let h: i64 = hgt / dv 211 if h >= floor_mm { if h <= floor_mm*G2_SCALE_STEP - 1 { return dv } } 212 dv = dv * G2_SCALE_STEP 213 tries = tries + 1 214 } 215 return 0 216} 217 218// integer/scale -> IEEE754 float32 bits (the nx_body_gen encoder) 219func g2_enc(v: i64, scale: i64) -> i64 { 220 if v == 0 { return 0 } 221 var neg: i64 = 0 222 var m: i64 = v 223 if m < 0 { neg = 1; m = 0-m } 224 var e: i64 = 0 225 var num: i64 = m 226 var den: i64 = scale 227 while num >= den*2 { den = den*2; e = e+1 } 228 while num < den { num = num*2; e = e-1 } 229 let frac: i64 = ((num - den)*G2_M8388608)/den 230 var bits: i64 = ((e+127) << 23) | (frac & G2_M8388607) 231 if neg == 1 { bits = bits | (1<<31) } 232 return bits 233} 234func g2_wr32(b: *u8, o: i64, v: i64) -> i64 { 235 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 236 return 0 237} 238// RETIRED ONTO THE SHARED OWNER 2026-08-24: was a private Newton floor-sqrt with a different seed; same floor 239// for every x >= 0, and vm_isqrt is gate-proven exact over 20,000 inputs. 240func g2_isqrt(x: i64) -> i64 { return vm_isqrt(x) } 241 242// ---- string-aware JSON scanning (machine-emitted glb JSON; strings honoured, depth tracked) ---- 243func g2_lit_at(b: *u8, o: i64, e: i64, lit: *u8) -> i64 { 244 var m: i64 = 0 245 while lit[m] != (0 as u8) { m = m + 1 } 246 if o + m > e { return 0 } 247 var i: i64 = 0 248 while i < m { if b[o+i] != lit[i] { return 0 } i = i + 1 } 249 return m 250} 251// 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 252func g2_topkey(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 253 var i: i64 = s 254 var depth: i64 = 0 255 var instr: i64 = 0 256 while i < e { 257 let c: i64 = b[i] as i64 258 if instr == 1 { 259 if c == 92 { i = i + 1 } 260 if c == 34 { instr = 0 } 261 } else { 262 if c == 34 { 263 if depth == 1 { 264 let m: i64 = g2_lit_at(b, i, e, lit) 265 if m > 0 { return i + m } 266 } 267 instr = 1 268 } 269 if c == 123 { depth = depth + 1 } 270 if c == 91 { depth = depth + 1 } 271 if c == 125 { depth = depth - 1 } 272 if c == 93 { depth = depth - 1 } 273 } 274 i = i + 1 275 } 276 return 0 - 1 277} 278// given off at the '[' of an array, fill starts/ends with each top element object range. -> count (or -1 refuse) 279func g2_elems(b: *u8, arr: i64, e: i64, starts: *i64, ends: *i64, maxn: i64) -> i64 { 280 if arr >= e { return 0 - 1 } 281 if b[arr] != (91 as u8) { return 0 - 1 } 282 var i: i64 = arr + 1 283 var depth: i64 = 1 284 var instr: i64 = 0 285 var cur: i64 = 0 - 1 286 var n: i64 = 0 287 while i < e { 288 let c: i64 = b[i] as i64 289 if instr == 1 { 290 if c == 92 { i = i + 1 } 291 if c == 34 { instr = 0 } 292 } else { 293 if c == 34 { instr = 1 } 294 if c == 123 { if depth == 1 { cur = i } depth = depth + 1 } 295 if c == 91 { depth = depth + 1 } 296 if c == 125 { 297 depth = depth - 1 298 if depth == 1 { 299 if cur >= 0 { 300 if n >= maxn { return 0 - 2 } 301 starts[n] = cur 302 ends[n] = i + 1 303 n = n + 1 304 cur = 0 - 1 305 } 306 } 307 } 308 if c == 93 { 309 depth = depth - 1 310 if depth == 0 { return n } 311 } 312 } 313 i = i + 1 314 } 315 return 0 - 1 316} 317// find `lit` (WITH quotes+colon) inside [s,e) at any depth, string-aware -> offset AFTER colon, or -1 318func g2_key(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 319 var i: i64 = s 320 var instr: i64 = 0 321 while i < e { 322 let c: i64 = b[i] as i64 323 if instr == 1 { 324 if c == 92 { i = i + 1 } 325 if c == 34 { instr = 0 } 326 } else { 327 if c == 34 { 328 let m: i64 = g2_lit_at(b, i, e, lit) 329 if m > 0 { return i + m } 330 instr = 1 331 } 332 } 333 i = i + 1 334 } 335 return 0 - 1 336} 337func g2_ifield(b: *u8, s: i64, e: i64, lit: *u8, def: i64) -> i64 { 338 let o: i64 = g2_key(b, s, e, lit) 339 if o < 0 { return def } 340 var i: i64 = o 341 var sk: i64 = 1 342 while sk == 1 { 343 if i >= e { sk = 0 } else { if b[i] == (32 as u8) { i = i + 1 } else { sk = 0 } } 344 } 345 var v: i64 = 0 346 var any: i64 = 0 347 var go: i64 = 1 348 while go == 1 { 349 if i >= e { go = 0 } else { 350 let c: i64 = b[i] as i64 351 if c >= 48 { if c <= 57 { v = v*10 + (c-48); any = 1; i = i + 1 } else { go = 0 } } else { go = 0 } 352 } 353 } 354 if any == 0 { return def } 355 return v 356} 357// object range starting at the '{' found at/after off -> end offset (past '}'), or -1 358func g2_objend(b: *u8, off: i64, e: i64) -> i64 { 359 var i: i64 = off 360 var found: i64 = 0 - 1 361 var sk: i64 = 1 362 while sk == 1 { 363 if i >= e { sk = 0 } else { if b[i] == (123 as u8) { found = i; sk = 0 } else { i = i + 1 } } 364 } 365 if found < 0 { return 0 - 1 } 366 i = found 367 var depth: i64 = 0 368 var instr: i64 = 0 369 while i < e { 370 let c: i64 = b[i] as i64 371 if instr == 1 { 372 if c == 92 { i = i + 1 } 373 if c == 34 { instr = 0 } 374 } else { 375 if c == 34 { instr = 1 } 376 if c == 123 { depth = depth + 1 } 377 if c == 125 { depth = depth - 1 } 378 if depth == 0 { return i + 1 } 379 } 380 i = i + 1 381 } 382 return 0 - 1 383} 384func g2_hasstr(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 385 var i: i64 = s 386 while i < e { if g2_lit_at(b, i, e, lit) > 0 { return 1 } i = i + 1 } 387 return 0 388} 389 390func g2_refuse(reason: *u8) -> i64 { 391 hw("GLTF2MESH REFUSED: " as *u8) 392 hw(reason) 393 hw("\n" as *u8) 394 return 0 395} 396 397// does the mesh object's "name" string contain `skip`? (byte substring on the name range) 398func g2_name_has(b: *u8, s: i64, e: i64, skip: *u8) -> i64 { 399 let no: i64 = g2_key(b, s, e, "\x22name\x22:" as *u8) 400 if no < 0 { return 0 } 401 var i: i64 = no 402 var sk: i64 = 1 403 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (34 as u8) { sk = 0 } else { i = i + 1 } } } 404 if i >= e { return 0 } 405 var j: i64 = i + 1 406 var ne: i64 = 0 - 1 407 sk = 1 408 while sk == 1 { if j >= e { sk = 0 } else { if b[j] == (34 as u8) { ne = j; sk = 0 } else { j = j + 1 } } } 409 if ne < 0 { return 0 } 410 return g2_hasstr(b, i + 1, ne, skip) 411} 412 413// filter arg classifier (R2 apparatus separation, debt 1785935557): each of up to two filter args is 414// "report" (print per-prim index/tris/bbox, v0-sampled) | "skipprims=i,j,k" (drop prims by REPORTED 415// global index) | any other string = skip meshes whose name contains it. 416static G2_REP: i64 417static G2_SKL: i64 418static G2_SKN: i64 419// units_conf=<path> override, as a *u8 held in an i64. 0 means "use G2_UNITS_CONF". 420// This exists so a GATE can force the refusal branch with its own /tmp conf instead of 421// editing the production one -- a gate that shares a fixture with a production beat ends up 422// reporting on the fixture rather than on the code. 423static G2_UCONF: i64 424func g2_filter(f: *u8) -> i64 { 425 if (f as i64) == 0 { return 0 } 426 if G2_SKL == 0 { G2_SKL = sys_mmap(64*8) as i64 } 427 if g2_lit_at(f, 0, 6, "report" as *u8) == 6 { G2_REP = 1; return 0 } 428 if g2_lit_at(f, 0, G2_UCONF_PFXLEN, "units_conf=" as *u8) == G2_UCONF_PFXLEN { G2_UCONF = (f as i64) + G2_UCONF_PFXLEN; return 0 } 429 if g2_lit_at(f, 0, 10, "skipprims=" as *u8) == 10 { 430 let sl: *i64 = G2_SKL as *i64 431 var sp: i64 = 10 432 var go2: i64 = 1 433 while go2 == 1 { 434 var v: i64 = 0 435 var any2: i64 = 0 436 var going: i64 = 1 437 while going == 1 { 438 let c2: i64 = f[sp] as i64 439 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); any2 = 1; sp = sp + 1 } else { going = 0 } } else { going = 0 } 440 } 441 if any2 == 1 { if G2_SKN < 64 { sl[G2_SKN] = v; G2_SKN = G2_SKN + 1 } } 442 if f[sp] == (44 as u8) { sp = sp + 1 } else { go2 = 0 } 443 } 444 return 0 445 } 446 return 1 447} 448 449// parse a JSON int array at `arr` (pointing at '[') into out[]; returns count or -1 450func g2_intarray(b: *u8, arr: i64, e: i64, out: *i64, maxn: i64) -> i64 { 451 if arr >= e { return 0 - 1 } 452 var i: i64 = arr 453 var sk: i64 = 1 454 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (91 as u8) { sk = 0 } else { i = i + 1 } } } 455 if i >= e { return 0 - 1 } 456 i = i + 1 457 var n: i64 = 0 458 var go: i64 = 1 459 while go == 1 { 460 if i >= e { go = 0 } else { 461 let c: i64 = b[i] as i64 462 if c == 93 { go = 0 } else { 463 if c >= 48 { if c <= 57 { 464 var v: i64 = 0 465 var d: i64 = 1 466 while d == 1 { 467 let c2: i64 = b[i] as i64 468 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); i = i + 1 } else { d = 0 } } else { d = 0 } 469 if i >= e { d = 0 } 470 } 471 if n >= maxn { return 0 - 2 } 472 out[n] = v 473 n = n + 1 474 } else { i = i + 1 } } else { i = i + 1 } 475 } 476 } 477 } 478 return n 479} 480// find the "name" string range of a node object; returns 1 and fills s/e via the out pair 481func g2_nodename(b: *u8, s: i64, e: i64, out: *i64) -> i64 { 482 let no: i64 = g2_key(b, s, e, "\x22name\x22:" as *u8) 483 out[0] = 0 484 out[1] = 0 485 if no < 0 { return 0 } 486 var i: i64 = no 487 var sk: i64 = 1 488 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (34 as u8) { sk = 0 } else { i = i + 1 } } } 489 if i >= e { return 0 } 490 var j: i64 = i + 1 491 var f: i64 = 0 492 while f == 0 { if j >= e { f = 1 } else { if b[j] == (34 as u8) { f = 1 } else { j = j + 1 } } } 493 if j >= e { return 0 } 494 out[0] = i + 1 495 out[1] = j 496 return 1 497} 498 499// does `path` end in ".nxa"? The NXA mode selector is the OUTPUT's own name -- explicit, readable in 500// every call site, and impossible to turn on by accident for a .nxmesh consumer. 501func g2_ends_nxa(path: *u8) -> i64 { 502 var n: i64 = 0 503 while path[n] != (0 as u8) { n = n + 1 } 504 if n < G2_NXA_EXT_LEN { return 0 } 505 let ext: *u8 = ".nxa" as *u8 506 var i: i64 = 0 507 while i < G2_NXA_EXT_LEN { if path[n - G2_NXA_EXT_LEN + i] != ext[i] { return 0 } i = i + 1 } 508 return 1 509} 510 511// ---- the emitter's context record: pointers and counts handed over in one i64 array so the 512// NXA functions stay under the estate's proven parameter count instead of carrying 20 arguments. 513// cx[0]=b cx[1]=bino cx[2]=accBV cx[3]=accBO cx[4]=accCT cx[5]=accN cx[6]=bvBO cx[7]=bvST cx[8]=nap 514// cx[9]=apPOS cx[10]=apIDX cx[11]=apJOI cx[12]=apWEI cx[13]=apMI cx[14]=mskin cx[15]=nskin cx[16]=skJ0 515// cx[17]=skJN cx[18]=skIBM cx[19]=jn cx[20]=parentOf cx[21]=nnodes cx[22]=dv cx[23]=outp cx[24]=na 516// cx[25]=nbv cx[26]=aS cx[27]=aE cx[28]=rem (skin-local joint -> UNION SKEL index, filled per 517// primitive by the emitter) cx[29]=aq (3x3 Q12 scratch for g2_ibm_one) 518 519// ---- g2_skin_weights: THE R22 CONTRACT SYMBOL. Decodes one primitive's JOINTS_0 + WEIGHTS_0 into 520// the estate's SKIN record layout, writing DIRECTLY into the SKIN payload words: for vertex v the 521// eight words at skin[1 + (vbase+v)*8] are [j0 j1 j2 j3][w0 w1 w2 w3], weights in Q12 summing to 522// EXACTLY 4096 -- the invariant nx_nxa_check REFUSES on (wsum must equal 4096) and nx_nxa_rig_emit 523// divides by. Joint indices are skin-local (JOINTS_0 indexes skins[].joints, per the glTF spec), 524// which is also the SKEL record order, so no remap is needed or performed. 525// 526// TOLERANCE IS DERIVED, NOT PICKED. Each weight is truncated to a Q12 unit (error below 1 unit, four 527// of them), plus the SOURCE quantisation: a normalized u8 weight is exact to 1/255 (error at most 528// 1/510), u16 to 1/65535, a float32 in [0,1] to below 2^-24. The per-vertex sum may therefore miss 529// 4096 by at most G2_VEC4_SLOTS + ceil(4096*G2_VEC4_SLOTS / (2*denominator)). A donor whose sum 530// misses by MORE is non-conformant (the spec says weights SHOULD sum to 1) and is REFUSED by name 531// rather than silently renormalised into something it never said. Within tolerance the residual is 532// folded into the largest weight, so the stored sum is exactly 4096. 533// 534// st: [0]=worst |sum-4096| before renormalisation [1]=tolerance used [2]=zero-weight vertices 535// [3]=first zero-weight vertex [4]=weights componentType [5]=vertices decoded 536func g2_skin_weights(cx: *i64, joi: i64, wei: i64, nvp: i64, vbase: i64, nj: i64, skin: *i64, st: *i64) -> i64 { 537 let b: *u8 = cx[0] as *u8 538 let bino: i64 = cx[1] 539 let accBV: *i64 = cx[2] as *i64 540 let accBO: *i64 = cx[3] as *i64 541 let accCT: *i64 = cx[4] as *i64 542 let bvBO: *i64 = cx[6] as *i64 543 let bvST: *i64 = cx[7] as *i64 544 let rem: *i64 = cx[28] as *i64 545 let jbv: i64 = accBV[joi] 546 let wbv: i64 = accBV[wei] 547 var jsz: i64 = 1 548 if accCT[joi] == G2_CT_U16 { jsz = 2 } 549 if accCT[joi] != G2_CT_U8 { if accCT[joi] != G2_CT_U16 { return G2_SW_SHAPE } } 550 var wsz: i64 = 1 551 var wden: i64 = G2_U8_MAX 552 if accCT[wei] == G2_CT_U16 { wsz = 2; wden = G2_U16_MAX } 553 if accCT[wei] == G2_CT_F32 { wsz = G2_F32_BYTES; wden = G2_F32_MANT_DENOM } 554 if accCT[wei] != G2_CT_U8 { if accCT[wei] != G2_CT_U16 { if accCT[wei] != G2_CT_F32 { return G2_SW_SHAPE } } } 555 var jstride: i64 = bvST[jbv] 556 if jstride == 0 { jstride = jsz*G2_VEC4_SLOTS } 557 var wstride: i64 = bvST[wbv] 558 if wstride == 0 { wstride = wsz*G2_VEC4_SLOTS } 559 let jbase: i64 = bino + bvBO[jbv] + accBO[joi] 560 let wbase: i64 = bino + bvBO[wbv] + accBO[wei] 561 let tol: i64 = G2_VEC4_SLOTS + g2_ceil_div(G2_Q12*G2_VEC4_SLOTS, 2*wden) 562 st[1] = tol 563 st[4] = accCT[wei] 564 var v: i64 = 0 565 while v < nvp { 566 let rec: i64 = 1 + (vbase + v)*G2_SKIN_WORDS 567 var sum: i64 = 0 568 var big: i64 = 0 569 var bigw: i64 = 0 - 1 570 var s: i64 = 0 571 while s < G2_VEC4_SLOTS { 572 var jv: i64 = 0 573 if jsz == 1 { jv = b[jbase + v*jstride + s] as i64 } 574 if jsz == 2 { jv = g2_u16(b, jbase + v*jstride + s*2) } 575 if jv < 0 { return G2_SW_JOINT_RANGE } 576 if jv >= nj { return G2_SW_JOINT_RANGE } 577 var wq: i64 = 0 578 if wsz == 1 { wq = (b[wbase + v*wstride + s] as i64)*G2_Q12/G2_U8_MAX } 579 if wsz == 2 { wq = g2_u16(b, wbase + v*wstride + s*2)*G2_Q12/G2_U16_MAX } 580 if wsz == G2_F32_BYTES { wq = g2_f32s(g2_u32(b, wbase + v*wstride + s*G2_F32_BYTES), G2_Q12) } 581 if G2_ERR != 0 { return G2_SW_SHAPE } 582 if wq < 0 { return G2_SW_NEGATIVE } 583 if rem[jv] != jv { st[6] = st[6] + 1 } 584 skin[rec + s] = rem[jv] 585 skin[rec + G2_VEC4_SLOTS + s] = wq 586 sum = sum + wq 587 if wq > bigw { bigw = wq; big = s } 588 s = s + 1 589 } 590 if sum == 0 { 591 if st[2] == 0 { st[3] = vbase + v } 592 st[2] = st[2] + 1 593 } else { 594 var dev: i64 = sum - G2_Q12 595 if dev < 0 { dev = 0 - dev } 596 if dev > st[0] { st[0] = dev } 597 if dev > tol { return G2_SW_SUM_TOL } 598 skin[rec + G2_VEC4_SLOTS + big] = skin[rec + G2_VEC4_SLOTS + big] + (G2_Q12 - sum) 599 } 600 v = v + 1 601 } 602 st[5] = st[5] + nvp 603 if st[2] > 0 { return G2_SW_ZERO_WEIGHT } 604 return G2_SW_OK 605} 606 607// ---- ONE bind matrix -> ONE SKEL record's pivot. glTF: skinMatrix = globalJointTransform * IBM, 608// so joint k's BIND world transform is IBM^-1; for IBM = [A | c] with A = R*diag(s) (rotation and 609// per-axis scale, NO shear) the bind position r-component is exactly -(A^T c)_r / (A^T A)_rr -- 610// which reduces BIT-FOR-BIT to the old -c_r when A is identity (numerator -4096*c_r over 4096). 611// The bind ROTATION is DROPPED to the identity quaternion the estate's SKEL declares (every 612// shipping writer emits identity; the runtime LBS reads only the bind translation) -- the same 613// convention the EXPORT leg already announces as `bind_rotation=dropped-identity-convention`. 614// MEASURED NECESSITY, 2026-08-23: the reference VRM's 132 IBMs ALL carry rotation (median |A-I| 615// 1.76 -- VRoid orients bones along their axes), so the previous identity-only refusal admitted 616// no VRoid avatar at all; rotations are counted (st[3]) and the worst deviation reported (st[0]), 617// never silently eaten. SHEAR has no exact pivot and REFUSES by name (off-diagonal A^T A above 618// the derived decode bound). The absent-IBM case is handled by the CALLER (spec: identity). 619// st: [0]=worst rotation deviation q12 (accumulated) [3]=joints whose rotation was dropped 620func g2_ibm_one(cx: *i64, ibi: i64, kloc: i64, dv: i64, out3: *i64, st: *i64) -> i64 { 621 let b: *u8 = cx[0] as *u8 622 let bino: i64 = cx[1] 623 let accBV: *i64 = cx[2] as *i64 624 let accBO: *i64 = cx[3] as *i64 625 let accCT: *i64 = cx[4] as *i64 626 let accN: *i64 = cx[5] as *i64 627 let bvBO: *i64 = cx[6] as *i64 628 let bvST: *i64 = cx[7] as *i64 629 let aq: *i64 = cx[29] as *i64 630 if accCT[ibi] != G2_CT_F32 { return G2_SW_SHAPE } 631 if accN[ibi] <= kloc { return G2_SW_SHAPE } 632 let bv: i64 = accBV[ibi] 633 var stride: i64 = bvST[bv] 634 if stride == 0 { stride = G2_MAT4_BYTES } 635 let mb: i64 = bino + bvBO[bv] + accBO[ibi] + kloc*stride 636 var r: i64 = 0 637 while r < G2_MAT3_DIM { 638 var c: i64 = 0 639 while c < G2_MAT3_DIM { 640 aq[r*G2_MAT3_DIM + c] = g2_f32s(g2_u32(b, mb + (c*G2_MAT4_COL + r)*G2_F32_BYTES), G2_Q12) 641 c = c + 1 642 } 643 r = r + 1 644 } 645 let w33: i64 = g2_f32s(g2_u32(b, mb + (G2_IBM_T_COL*G2_MAT4_COL + G2_IBM_W_ROW)*G2_F32_BYTES), G2_Q12) 646 if G2_ERR != 0 { return G2_SW_SHAPE } 647 var d33: i64 = w33 - G2_Q12 648 if d33 < 0 { d33 = 0 - d33 } 649 if d33 > G2_Q12_IDENT_TOL { return G2_SW_SHAPE } 650 var dev: i64 = 0 651 r = 0 652 while r < G2_MAT3_DIM { 653 var c2: i64 = 0 654 while c2 < G2_MAT3_DIM { 655 var want: i64 = 0 656 if r == c2 { want = G2_Q12 } 657 var d: i64 = aq[r*G2_MAT3_DIM + c2] - want 658 if d < 0 { d = 0 - d } 659 if d > dev { dev = d } 660 c2 = c2 + 1 661 } 662 r = r + 1 663 } 664 if dev > st[0] { st[0] = dev } 665 if dev > G2_Q12_IDENT_TOL { st[3] = st[3] + 1 } 666 var i2: i64 = 0 667 while i2 < G2_MAT3_DIM { 668 var j2: i64 = 0 669 while j2 < G2_MAT3_DIM { 670 if i2 != j2 { 671 var g: i64 = 0 672 var k2: i64 = 0 673 while k2 < G2_MAT3_DIM { g = g + aq[k2*G2_MAT3_DIM + i2]*aq[k2*G2_MAT3_DIM + j2]; k2 = k2 + 1 } 674 g = g / G2_Q12 675 if g < 0 { g = 0 - g } 676 if g > G2_SHEAR_TOL_Q12 { return G2_IB_SHEAR } 677 } 678 j2 = j2 + 1 679 } 680 i2 = i2 + 1 681 } 682 r = 0 683 while r < G2_MAT3_DIM { 684 var num: i64 = 0 685 var grr: i64 = 0 686 var k3: i64 = 0 687 while k3 < G2_MAT3_DIM { 688 let cm: i64 = g2_f32mm(g2_u32(b, mb + (G2_IBM_T_COL*G2_MAT4_COL + k3)*G2_F32_BYTES)) 689 num = num - aq[k3*G2_MAT3_DIM + r]*cm 690 grr = grr + aq[k3*G2_MAT3_DIM + r]*aq[k3*G2_MAT3_DIM + r] 691 k3 = k3 + 1 692 } 693 grr = grr / G2_Q12 694 if grr <= 0 { return G2_SW_SHAPE } 695 if G2_ERR != 0 { return G2_SW_SHAPE } 696 out3[r] = (num/grr)/dv 697 r = r + 1 698 } 699 return G2_SW_OK 700} 701 702func g2_toc(toc: *i64, ti: i64, tag: *u8, off: i64, wl: i64, words: *i64) -> i64 { 703 let e: i64 = ti*G2_NXA_TOC_WORDS 704 toc[e] = nxa_tag4(tag) 705 toc[e+1] = off 706 toc[e+2] = wl 707 toc[e+3] = nxa_check2(1, words, wl) 708 return off + wl*8 709} 710 711// ---- R22: emit NXANIM01 carrying the donor's rig. Layout per nx_nxa_skin (writer), nx_nxa_check 712// (fsck) and nx_nxa_rig_emit (browser LBS): 32-byte header [magic][ver][ns][toc check], ns 32-byte 713// TOC rows [tag][byte off][wordlen][payload check], then payloads VERT TRIS SKEL SKIN. Every 714// checksum is nxa_check2 from nx_nxa.nx -- the ONE definition the readers import too. The buffer is 715// sized FROM THE DATA (counts summed over the accepted primitives); there is no cap to hit here, and 716// the one cap upstream (G2_MAXEL accepted primitives) REFUSES and announces rather than truncating. 717func g2_emit_nxa(cx: *i64) -> i64 { 718 let b: *u8 = cx[0] as *u8 719 let bino: i64 = cx[1] 720 let accBV: *i64 = cx[2] as *i64 721 let accBO: *i64 = cx[3] as *i64 722 let accCT: *i64 = cx[4] as *i64 723 let accN: *i64 = cx[5] as *i64 724 let bvBO: *i64 = cx[6] as *i64 725 let bvST: *i64 = cx[7] as *i64 726 let nap: i64 = cx[8] 727 let apPOS: *i64 = cx[9] as *i64 728 let apIDX: *i64 = cx[10] as *i64 729 let apJOI: *i64 = cx[11] as *i64 730 let apWEI: *i64 = cx[12] as *i64 731 let apMI: *i64 = cx[13] as *i64 732 let mskin: *i64 = cx[14] as *i64 733 let nskin: i64 = cx[15] 734 let skJ0: *i64 = cx[16] as *i64 735 let skJN: *i64 = cx[17] as *i64 736 let skIBM: *i64 = cx[18] as *i64 737 let jn: *i64 = cx[19] as *i64 738 let parentOf: *i64 = cx[20] as *i64 739 let nnodes: i64 = cx[21] 740 let dv: i64 = cx[22] 741 let outp: *u8 = cx[23] as *u8 742 let nacc: i64 = cx[24] 743 let nbv: i64 = cx[25] 744 let aS: *i64 = cx[26] as *i64 745 let aE: *i64 = cx[27] as *i64 746 if nap <= 0 { g2_refuse("nxa: no accepted primitive" as *u8); return 5 } 747 // R22b MULTI-SKIN UNION (the VRoid case). A VRM exports one skins[] entry PER MESH (body, 748 // face, hair, clothing) over ONE shared skeleton -- measured on the reference VRM: 5 skins, 749 // joints 23/7/1/21/80, union 128 distinct nodes, IBMs agreeing on every shared node. NXANIM01 750 // carries one SKEL, so the UNION of the used skins' joints IS the skeleton and each 751 // primitive's skin-local JOINTS_0 is remapped through its skins[].joints to the union index. 752 // Skins that share a skeleton form ONE component under joint OVERLAP; the overlap graph is 753 // NOT all-pairs even on a genuine single character (hair and clothing may share only the head 754 // node), so the refusal keys on CONNECTIVITY -- never on any single pair. 755 var nv: i64 = 0 756 var nt: i64 = 0 757 let used: *i64 = sys_mmap(G2_MAXEL*8) as *i64 758 var nused: i64 = 0 759 let ufp: *i64 = sys_mmap(G2_MAXEL*8) as *i64 760 let uidx: *i64 = sys_mmap(G2_MAXEL*8) as *i64 761 let ujNode: *i64 = sys_mmap(G2_MAXEL*8) as *i64 762 let ownSkin: *i64 = sys_mmap(G2_MAXEL*8) as *i64 763 let ownLoc: *i64 = sys_mmap(G2_MAXEL*8) as *i64 764 var nuj: i64 = 0 765 var q6: i64 = 0 766 while q6 < G2_MAXEL { ufp[q6] = q6; uidx[q6] = 0 - 1; q6 = q6 + 1 } 767 var p: i64 = 0 768 while p < nap { 769 var s2: i64 = 0 - 1 770 if apMI[p] < G2_MAXEL { s2 = mskin[apMI[p]] } 771 if s2 < 0 { g2_refuse("nxa: an accepted primitive belongs to no skin -- not a rigged donor; write .nxmesh for a form oracle" as *u8); return 10 } 772 if s2 >= nskin { g2_refuse("nxa: owning skin index outside skins[]" as *u8); return 10 } 773 if apJOI[p] < 0 { g2_refuse("nxa: primitive has no JOINTS_0" as *u8); return 10 } 774 if apWEI[p] < 0 { g2_refuse("nxa: primitive has no WEIGHTS_0" as *u8); return 10 } 775 if apJOI[p] >= nacc { g2_refuse("nxa: JOINTS_0 names an accessor outside the table" as *u8); return 10 } 776 if apWEI[p] >= nacc { g2_refuse("nxa: WEIGHTS_0 names an accessor outside the table" as *u8); return 10 } 777 if g2_hasstr(b, aS[apJOI[p]], aE[apJOI[p]], "VEC4" as *u8) == 0 { g2_refuse("nxa: JOINTS_0 is not VEC4" as *u8); return 10 } 778 if g2_hasstr(b, aS[apWEI[p]], aE[apWEI[p]], "VEC4" as *u8) == 0 { g2_refuse("nxa: WEIGHTS_0 is not VEC4" as *u8); return 10 } 779 if accBV[apJOI[p]] < 0 { g2_refuse("nxa: JOINTS_0 has no bufferView" as *u8); return 10 } 780 if accBV[apWEI[p]] < 0 { g2_refuse("nxa: WEIGHTS_0 has no bufferView" as *u8); return 10 } 781 if accBV[apJOI[p]] >= nbv { g2_refuse("nxa: JOINTS_0 bufferView outside the table" as *u8); return 10 } 782 if accBV[apWEI[p]] >= nbv { g2_refuse("nxa: WEIGHTS_0 bufferView outside the table" as *u8); return 10 } 783 if accN[apJOI[p]] != accN[apPOS[p]] { g2_refuse("nxa: JOINTS_0 count differs from POSITION count -- the skin does not cover the vertex table" as *u8); return 11 } 784 if accN[apWEI[p]] != accN[apPOS[p]] { g2_refuse("nxa: WEIGHTS_0 count differs from POSITION count -- the skin does not cover the vertex table" as *u8); return 11 } 785 nv = nv + accN[apPOS[p]] 786 nt = nt + accN[apIDX[p]]/3 787 var seen: i64 = 0 788 var u2: i64 = 0 789 while u2 < nused { if used[u2] == s2 { seen = 1 } u2 = u2 + 1 } 790 if seen == 0 { 791 if skJN[s2] <= 0 { g2_refuse("nxa: a used skin declares zero joints" as *u8); return 10 } 792 var k4: i64 = 0 793 while k4 < skJN[s2] { 794 let node: i64 = jn[skJ0[s2] + k4] 795 if node < 0 { g2_refuse("nxa: skins[].joints names a negative node" as *u8); return 10 } 796 if node >= G2_MAXEL { g2_refuse("nxa: skins[].joints names a node outside the parsed table" as *u8); return 10 } 797 if uidx[node] < 0 { 798 uidx[node] = nuj 799 ujNode[nuj] = node 800 ownSkin[nuj] = s2 801 ownLoc[nuj] = k4 802 nuj = nuj + 1 803 } else { 804 var ra: i64 = s2 805 while ufp[ra] != ra { ra = ufp[ra] } 806 var rb: i64 = ownSkin[uidx[node]] 807 while ufp[rb] != rb { rb = ufp[rb] } 808 if ra != rb { ufp[ra] = rb } 809 } 810 k4 = k4 + 1 811 } 812 used[nused] = s2 813 nused = nused + 1 814 } 815 p = p + 1 816 } 817 var comps: i64 = 0 818 var u3: i64 = 0 819 while u3 < nused { 820 var rr: i64 = used[u3] 821 while ufp[rr] != rr { rr = ufp[rr] } 822 if rr == used[u3] { comps = comps + 1 } 823 u3 = u3 + 1 824 } 825 if comps > 1 { 826 g2_refuse("nxa: the used skins do not share a skeleton (joint-overlap components > 1) -- not one rigged character; split the donor" as *u8) 827 hw(" skins_used=" as *u8); pn(nused); hw(" components=" as *u8); pn(comps); hw("\n" as *u8) 828 return 10 829 } 830 let nj: i64 = nuj 831 // sizes FROM THE DATA 832 let vwl: i64 = 1 + nv*G2_VERT_WORDS 833 let twl: i64 = 1 + nt*G2_TRI_WORDS 834 let swl: i64 = 1 + nj*G2_SKEL_WORDS 835 let kwl: i64 = 1 + nv*G2_SKIN_WORDS 836 let head: i64 = G2_NXA_HDR_BYTES + G2_NXA_SECS*G2_NXA_TOC_BYTES 837 let total: i64 = head + (vwl + twl + swl + kwl)*8 838 let ob: *i64 = sys_mmap(total + 64) as *i64 839 let vert: *i64 = ((ob as i64) + head) as *i64 840 let tris: *i64 = ((vert as i64) + vwl*8) as *i64 841 let skel: *i64 = ((tris as i64) + twl*8) as *i64 842 let skin: *i64 = ((skel as i64) + swl*8) as *i64 843 vert[0] = nv 844 tris[0] = nt 845 skel[0] = nj 846 skin[0] = nv 847 let st: *i64 = sys_mmap(8*8) as *i64 848 let rem: *i64 = sys_mmap(G2_MAXEL*8) as *i64 849 cx[28] = rem as i64 850 let aqbuf: *i64 = sys_mmap(G2_MAT3_DIM*G2_MAT3_DIM*8) as *i64 851 cx[29] = aqbuf as i64 852 var vbase: i64 = 0 853 var tbase: i64 = 0 854 p = 0 855 while p < nap { 856 let posi: i64 = apPOS[p] 857 let idxi: i64 = apIDX[p] 858 let pb: i64 = accBV[posi] 859 var pstride: i64 = bvST[pb] 860 if pstride == 0 { pstride = G2_VEC3_F32_BYTES } 861 let pbase: i64 = bino + bvBO[pb] + accBO[posi] 862 let pcount: i64 = accN[posi] 863 var v: i64 = 0 864 while v < pcount { 865 let vw: i64 = 1 + (vbase + v)*G2_VERT_WORDS 866 vert[vw] = g2_f32mm(g2_u32(b, pbase + v*pstride))/dv 867 vert[vw+1] = g2_f32mm(g2_u32(b, pbase + v*pstride + G2_F32_BYTES))/dv 868 vert[vw+2] = g2_f32mm(g2_u32(b, pbase + v*pstride + 2*G2_F32_BYTES))/dv 869 v = v + 1 870 } 871 let ib2: i64 = accBV[idxi] 872 let ibase: i64 = bino + bvBO[ib2] + accBO[idxi] 873 var isz: i64 = G2_U16_BYTES 874 if accCT[idxi] == G2_CT_U32 { isz = G2_U32_BYTES } 875 let nix: i64 = accN[idxi] 876 var q: i64 = 0 877 while q < nix { 878 var ix: i64 = 0 879 if isz == G2_U16_BYTES { ix = g2_u16(b, ibase + q*G2_U16_BYTES) } 880 if isz == G2_U32_BYTES { ix = g2_u32(b, ibase + q*G2_U32_BYTES) } 881 if ix >= pcount { g2_refuse("nxa: index outside its POSITION accessor" as *u8); return 7 } 882 tris[1 + tbase*G2_TRI_WORDS + q] = vbase + ix 883 q = q + 1 884 } 885 var s5: i64 = 0 - 1 886 if apMI[p] < G2_MAXEL { s5 = mskin[apMI[p]] } 887 var k6: i64 = 0 888 while k6 < skJN[s5] { rem[k6] = uidx[jn[skJ0[s5] + k6]]; k6 = k6 + 1 } 889 let wrc: i64 = g2_skin_weights(cx, apJOI[p], apWEI[p], pcount, vbase, skJN[s5], skin, st) 890 if wrc == G2_SW_SHAPE { g2_refuse("nxa: JOINTS_0/WEIGHTS_0 shape unsupported (need VEC4 u8/u16 joints, u8/u16/f32 weights, data in bounds)" as *u8); return 11 } 891 if wrc == G2_SW_JOINT_RANGE { g2_refuse("nxa: JOINTS_0 names a joint outside skins[].joints" as *u8); return 11 } 892 if wrc == G2_SW_NEGATIVE { g2_refuse("nxa: a NEGATIVE weight -- non-conformant donor" as *u8); return 11 } 893 if wrc == G2_SW_SUM_TOL { 894 g2_refuse("nxa: a vertex's weights miss 4096 by more than the DERIVED tolerance -- non-conformant donor, refusing to renormalise what it did not say" as *u8) 895 hw(" worst_dev_q12=" as *u8); pn(st[0]); hw(" tolerance_q12=" as *u8); pn(st[1]); hw("\n" as *u8) 896 return 11 897 } 898 if G2_ERR != 0 { g2_refuse("nxa: out-of-bounds read while copying vertices or indices" as *u8); return 7 } 899 vbase = vbase + pcount 900 tbase = tbase + nix/3 901 p = p + 1 902 } 903 if st[2] > 0 { 904 g2_refuse("nxa: vertices with ZERO total weight -- unbound vertices cannot be represented (SKIN demands a 4096 sum)" as *u8) 905 hw(" zero_weight_vertices=" as *u8); pn(st[2]); hw(" first=" as *u8); pn(st[3]); hw("\n" as *u8) 906 return 11 907 } 908 // SKEL: parent from the node hierarchy (UNION index, -1 for a root), bind pivot from the 909 // owning skin's IBM via g2_ibm_one; a node carried by SEVERAL skins must bind at the SAME 910 // position across them (two truncating decodes of one bind differ by at most one mm unit) or 911 // the donor is refused by name -- the skins would disagree on the skeleton. 912 let ist: *i64 = sys_mmap(8*8) as *i64 913 let p3: *i64 = sys_mmap(G2_MAT3_DIM*8) as *i64 914 let p4: *i64 = sys_mmap(G2_MAT3_DIM*8) as *i64 915 var conflicts: i64 = 0 916 var conflict_worst: i64 = 0 917 var absent_ident: i64 = 0 918 var k: i64 = 0 919 while k < nj { 920 let node: i64 = ujNode[k] 921 var par: i64 = 0 - 1 922 if node < nnodes { 923 let pnode: i64 = parentOf[node] 924 if pnode >= 0 { if pnode < G2_MAXEL { if uidx[pnode] >= 0 { par = uidx[pnode] } } } 925 } 926 let jw: i64 = 1 + k*G2_SKEL_WORDS 927 skel[jw] = par 928 let sOwn: i64 = ownSkin[k] 929 if skIBM[sOwn] < 0 { 930 p3[0] = 0; p3[1] = 0; p3[2] = 0 931 absent_ident = absent_ident + 1 932 } else { 933 let irc: i64 = g2_ibm_one(cx, skIBM[sOwn], ownLoc[k], dv, p3, ist) 934 if irc == G2_SW_SHAPE { g2_refuse("nxa: inverseBindMatrices is not a float MAT4 accessor readable at this joint" as *u8); return 11 } 935 if irc == G2_IB_SHEAR { 936 g2_refuse("nxa: bind matrix carries SHEAR (A^T A off-diagonal above the derived decode bound) -- no exact pivot exists; refusing rather than guessing" as *u8) 937 return 12 938 } 939 } 940 var u4: i64 = 0 941 while u4 < nused { 942 let s4: i64 = used[u4] 943 if s4 != sOwn { if skIBM[s4] >= 0 { 944 var fnd: i64 = 0 945 var k5: i64 = 0 946 while k5 < skJN[s4] { 947 if fnd == 0 { if jn[skJ0[s4] + k5] == node { 948 fnd = 1 949 let irc2: i64 = g2_ibm_one(cx, skIBM[s4], k5, dv, p4, ist) 950 if irc2 == G2_SW_OK { 951 var ax: i64 = 0 952 while ax < G2_MAT3_DIM { 953 var dd: i64 = p4[ax] - p3[ax] 954 if dd < 0 { dd = 0 - dd } 955 if dd > conflict_worst { conflict_worst = dd } 956 if dd > G2_IBM_CONFLICT_TOL_MM { conflicts = conflicts + 1 } 957 ax = ax + 1 958 } 959 } 960 } } 961 k5 = k5 + 1 962 } 963 } } 964 u4 = u4 + 1 965 } 966 skel[jw+1] = p3[0] 967 skel[jw+2] = p3[1] 968 skel[jw+3] = p3[2] 969 skel[jw+4] = 0; skel[jw+5] = 0; skel[jw+6] = 0; skel[jw+7] = G2_Q12 970 k = k + 1 971 } 972 if conflicts > 0 { 973 g2_refuse("nxa: a joint shared by two skins binds at DIFFERENT positions (beyond one decode unit) -- the skins disagree on the skeleton; split the donor" as *u8) 974 hw(" ibm_conflicts=" as *u8); pn(conflicts); hw(" worst_mm=" as *u8); pn(conflict_worst); hw("\n" as *u8) 975 return 12 976 } 977 ist[2] = 0 978 if absent_ident > 0 { ist[2] = G2_IBM_DEFAULT_IDENTITY } 979 // header + TOC, checksums from the ONE definition 980 ob[0] = nxa_magic() 981 ob[1] = NXA_VER 982 ob[2] = G2_NXA_SECS 983 let toc: *i64 = ((ob as i64) + G2_NXA_HDR_BYTES) as *i64 984 var off: i64 = head 985 off = g2_toc(toc, 0, "VERT" as *u8, off, vwl, vert) 986 off = g2_toc(toc, 1, "TRIS" as *u8, off, twl, tris) 987 off = g2_toc(toc, 2, "SKEL" as *u8, off, swl, skel) 988 off = g2_toc(toc, 3, "SKIN" as *u8, off, kwl, skin) 989 ob[3] = nxa_check2(1, toc, G2_NXA_SECS*G2_NXA_TOC_WORDS) 990 if off != total { g2_refuse("nxa: internal -- payload offsets do not close on the buffer size" as *u8); return 13 } 991 let ofd: i64 = sys_openat_wr(outp, MODE_0644) 992 if ofd < 0 { g2_refuse("output path unwritable" as *u8); return 6 } 993 let wr: i64 = sys_write(ofd, ob as *u8, total) 994 sys_close(ofd) 995 if wr != total { g2_refuse("nxa: short write" as *u8); hw(" wrote=" as *u8); pn(wr); hw(" of=" as *u8); pn(total); hw("\n" as *u8); return 6 } 996 hw("{\x22organ\x22:\x22nx_gltf2mesh\x22,\x22mode\x22:\x22nxa\x22,\x22contract\x22:\x22g2_skin_weights\x22,\x22prims_accepted\x22:" as *u8); pn(nap) 997 hw(",\x22verts\x22:" as *u8); pn(nv) 998 hw(",\x22tris\x22:" as *u8); pn(nt) 999 hw(",\x22joints\x22:" as *u8); pn(nj) 1000 hw(",\x22skins_used\x22:" as *u8); pn(nused) 1001 hw(",\x22skin_joints_union\x22:" as *u8); pn(nuj) 1002 hw(",\x22joints_remapped\x22:" as *u8); pn(st[6]) 1003 hw(",\x22ibm_conflicts\x22:" as *u8); pn(conflicts) 1004 hw(",\x22ibm_conflict_worst_mm\x22:" as *u8); pn(conflict_worst) 1005 hw(",\x22bind_rot_dropped_joints\x22:" as *u8); pn(ist[3]) 1006 hw(",\x22weights_componentType\x22:" as *u8); pn(st[4]) 1007 hw(",\x22weight_sum_worst_dev_q12\x22:" as *u8); pn(st[0]) 1008 hw(",\x22weight_tolerance_q12\x22:" as *u8); pn(st[1]) 1009 hw(",\x22ibm_absent_identity\x22:" as *u8); pn(ist[2]) 1010 hw(",\x22bind_rot_worst_dev_q12\x22:" as *u8); pn(ist[0]) 1011 hw(",\x22scale_div\x22:" as *u8); pn(dv) 1012 hw(",\x22bytes\x22:" as *u8); pn(total) 1013 hw(",\x22note\x22:\x22NXANIM01 VERT/TRIS/SKEL/SKIN; degenerate index triples KEPT (an index list is data); bind pivot exact for rotation plus per-axis scale, bind ROTATION dropped to the identity quat the estate declares (counted above, matching the export leg); multi-skin donors union their shared skeleton; axes as authored, not remapped\x22}" as *u8) 1014 hw("\n" as *u8) 1015 return 0 1016} 1017 1018// ---- the conversion ---- 1019func g2_convert(inp: *u8, outp: *u8, skip: *u8) -> i64 { 1020 G2_ERR = 0 1021 let fd: i64 = sys_openat_rd(inp) 1022 if fd < 0 { g2_refuse("input unreadable" as *u8); return 3 } 1023 let b: *u8 = sys_mmap(G2_CAP + 64) 1024 var n: i64 = 0 1025 var go: i64 = 1 1026 while go == 1 { 1027 let r: i64 = sys_read(fd, ((b as i64) + n) as *u8, G2_CAP - n) 1028 if r <= 0 { go = 0 } else { n = n + r } 1029 if n >= G2_CAP { go = 0 } 1030 } 1031 sys_close(fd) 1032 if n >= G2_CAP { g2_refuse("input exceeds 32MiB cap" as *u8); return 3 } 1033 if n < 28 { g2_refuse("too small for a glb" as *u8); return 3 } 1034 G2_LEN = n 1035 if b[0] != (103 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 1036 if b[1] != (108 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 1037 if b[2] != (84 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 1038 if b[3] != (70 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 } 1039 let total: i64 = g2_u32(b, 8) 1040 if total != n { g2_refuse("length field disagrees with file size" as *u8); return 3 } 1041 let jlen: i64 = g2_u32(b, 12) 1042 if 20 + jlen > n { g2_refuse("JSON chunk overruns file" as *u8); return 3 } 1043 let js: i64 = 20 1044 let je: i64 = 20 + jlen 1045 var bino: i64 = 0 - 1 1046 var binn: i64 = 0 1047 if je + 8 <= n { 1048 binn = g2_u32(b, je) 1049 if b[je+4] != (66 as u8) { g2_refuse("second chunk is not BIN" as *u8); return 3 } 1050 bino = je + 8 1051 if bino + binn > n { 1052 g2_refuse("BIN chunk overruns file" as *u8) 1053 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) 1054 return 3 1055 } 1056 } 1057 if bino < 0 { g2_refuse("no BIN chunk (external buffers unsupported)" as *u8); return 3 } 1058 if G2_ERR != 0 { g2_refuse("header read out of bounds" as *u8); return 3 } 1059 1060 // top-level tables 1061 let accA: i64 = g2_topkey(b, js, je, "\x22accessors\x22:" as *u8) 1062 let bvA: i64 = g2_topkey(b, js, je, "\x22bufferViews\x22:" as *u8) 1063 let meA: i64 = g2_topkey(b, js, je, "\x22meshes\x22:" as *u8) 1064 if accA < 0 { g2_refuse("no accessors array" as *u8); return 4 } 1065 if bvA < 0 { g2_refuse("no bufferViews array" as *u8); return 4 } 1066 if meA < 0 { g2_refuse("no meshes array" as *u8); return 4 } 1067 let aS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1068 let aE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1069 let na: i64 = g2_elems(b, accA, je, aS, aE, G2_MAXEL) 1070 if na <= 0 { g2_refuse("accessors array unparseable or empty" as *u8); return 4 } 1071 let vS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1072 let vE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1073 let nv: i64 = g2_elems(b, bvA, je, vS, vE, G2_MAXEL) 1074 if nv <= 0 { g2_refuse("bufferViews array unparseable or empty" as *u8); return 4 } 1075 let mS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1076 let mE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1077 let nm: i64 = g2_elems(b, meA, je, mS, mE, G2_MAXEL) 1078 if nm <= 0 { g2_refuse("meshes array unparseable or empty" as *u8); return 4 } 1079 1080 // accessor + bufferView flat tables 1081 let accBV: *i64 = sys_mmap(na*8) as *i64 1082 let accBO: *i64 = sys_mmap(na*8) as *i64 1083 let accCT: *i64 = sys_mmap(na*8) as *i64 1084 let accN: *i64 = sys_mmap(na*8) as *i64 1085 let accV3: *i64 = sys_mmap(na*8) as *i64 1086 var i: i64 = 0 1087 while i < na { 1088 accBV[i] = g2_ifield(b, aS[i], aE[i], "\x22bufferView\x22:" as *u8, 0 - 1) 1089 accBO[i] = g2_ifield(b, aS[i], aE[i], "\x22byteOffset\x22:" as *u8, 0) 1090 accCT[i] = g2_ifield(b, aS[i], aE[i], "\x22componentType\x22:" as *u8, 0) 1091 accN[i] = g2_ifield(b, aS[i], aE[i], "\x22count\x22:" as *u8, 0) 1092 accV3[i] = g2_hasstr(b, aS[i], aE[i], "VEC3" as *u8) 1093 i = i + 1 1094 } 1095 let bvBO: *i64 = sys_mmap(nv*8) as *i64 1096 let bvLN: *i64 = sys_mmap(nv*8) as *i64 1097 let bvST: *i64 = sys_mmap(nv*8) as *i64 1098 i = 0 1099 while i < nv { 1100 bvBO[i] = g2_ifield(b, vS[i], vE[i], "\x22byteOffset\x22:" as *u8, 0) 1101 bvLN[i] = g2_ifield(b, vS[i], vE[i], "\x22byteLength\x22:" as *u8, 0) 1102 bvST[i] = g2_ifield(b, vS[i], vE[i], "\x22byteStride\x22:" as *u8, 0) 1103 i = i + 1 1104 } 1105 1106 // ---- L2 NAMED PARTS (semantic ladder 1785954089): the donor ALREADY CARRIES its own segmentation 1107 // in the skin. JOINTS_0 indexes skins[].joints[], which indexes nodes[], which carry NAMES. So a 1108 // rigged donor segments itself for free -- no clustering heuristic, no invented vocabulary, the 1109 // parts are named by whoever built the asset. 1110 let jn: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1111 var njoint: i64 = 0 1112 let nn0: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1113 let nn1: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1114 var nnodes: i64 = 0 1115 // ★A MULTI-MESH DONOR HAS ONE SKIN PER MESH: taking skins[0] labelled all 34k triangles with the 1116 // HAIR rig (measured 2026-08-05). The skin that owns a primitive is the one on the NODE that 1117 // instantiates its mesh, so resolve mesh -> node -> skin, and label by NODE INDEX (globally unique) 1118 // rather than by a skin-local joint index (which means a different bone in every skin). 1119 let skJ0: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1120 let skJN: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1121 let skIBM: *i64 = sys_mmap(G2_MAXEL*8) as *i64 // R22: each skin's inverseBindMatrices accessor (-1 = absent) 1122 var nskin: i64 = 0 1123 let skA: i64 = g2_topkey(b, js, je, "\x22skins\x22:" as *u8) 1124 if skA >= 0 { 1125 let sS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1126 let sE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1127 let ns: i64 = g2_elems(b, skA, je, sS, sE, G2_MAXEL) 1128 var si: i64 = 0 1129 while si < ns { 1130 let jA: i64 = g2_key(b, sS[si], sE[si], "\x22joints\x22:" as *u8) 1131 skJ0[si] = njoint 1132 skJN[si] = 0 1133 skIBM[si] = g2_ifield(b, sS[si], sE[si], "\x22inverseBindMatrices\x22:" as *u8, 0 - 1) 1134 if jA >= 0 { 1135 let r: i64 = g2_intarray(b, jA, sE[si], ((jn as i64) + njoint*8) as *i64, G2_MAXEL - njoint) 1136 if r > 0 { skJN[si] = r; njoint = njoint + r } 1137 } 1138 si = si + 1 1139 } 1140 nskin = ns 1141 } 1142 let mskin: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1143 var q4: i64 = 0 1144 while q4 < G2_MAXEL { mskin[q4] = 0 - 1; q4 = q4 + 1 } 1145 // R22: parent-of map from nodes[].children -- the hierarchy the SKEL parent column is read from 1146 let parentOf: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1147 var q5: i64 = 0 1148 while q5 < G2_MAXEL { parentOf[q5] = 0 - 1; q5 = q5 + 1 } 1149 let chbuf: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1150 let ndA: i64 = g2_topkey(b, js, je, "\x22nodes\x22:" as *u8) 1151 if ndA >= 0 { 1152 let dS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1153 let dE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1154 let nd: i64 = g2_elems(b, ndA, je, dS, dE, G2_MAXEL) 1155 let pr: *i64 = sys_mmap(16) as *i64 1156 var q3: i64 = 0 1157 while q3 < nd { 1158 g2_nodename(b, dS[q3], dE[q3], pr) 1159 nn0[q3] = pr[0] 1160 nn1[q3] = pr[1] 1161 let chA: i64 = g2_key(b, dS[q3], dE[q3], "\x22children\x22:" as *u8) 1162 if chA >= 0 { 1163 let nch: i64 = g2_intarray(b, chA, dE[q3], chbuf, G2_MAXEL) 1164 var ci: i64 = 0 1165 while ci < nch { if chbuf[ci] >= 0 { if chbuf[ci] < G2_MAXEL { parentOf[chbuf[ci]] = q3 } } ci = ci + 1 } 1166 } 1167 let mh: i64 = g2_ifield(b, dS[q3], dE[q3], "\x22mesh\x22:" as *u8, 0 - 1) 1168 let sk2: i64 = g2_ifield(b, dS[q3], dE[q3], "\x22skin\x22:" as *u8, 0 - 1) 1169 if mh >= 0 { if mh < G2_MAXEL { if sk2 >= 0 { mskin[mh] = sk2 } } } 1170 q3 = q3 + 1 1171 } 1172 nnodes = nd 1173 } 1174 let trij: *i64 = sys_mmap(G2_MAXTRI*8 + 64) as *i64 1175 1176 // output buffer + mm staging (positions are held as integer mm and unit-normalized BEFORE encoding: 1177 // the NXMSH2 ecosystem convention is f32 MILLIMETRES -- raw meter-scale VRM floats decode to a 1178 // 1.6mm sliver that renders as ~2px and collapses every IoU judge; measured 2026-08-05) 1179 let hdr: i64 = G2_HDR + G2_LAYROW 1180 let ob: *u8 = sys_mmap(hdr + G2_MAXTRI*G2_TRIREC + G2_MAXTRI*4 + 64) 1181 let stg: *i64 = sys_mmap(G2_MAXTRI*12*8 + 64) as *i64 1182 var nt: i64 = 0 1183 var prims: i64 = 0 1184 var accepted: i64 = 0 1185 var skipped: i64 = 0 1186 var degen: i64 = 0 1187 var trunc: i64 = 0 1188 var vseen: i64 = 0 1189 var miny: i64 = G2_MAGIC_2000000000 1190 var maxy: i64 = 0 - G2_MAGIC_2000000000 1191 // R22: the accepted-primitive list, consumed by the NXA emitter AFTER the acceptance predicate 1192 // has run exactly once. Recording is unconditional and output-neutral on the NXMSH2 path. 1193 let nxa_mode: i64 = g2_ends_nxa(outp) 1194 let apPOS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1195 let apIDX: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1196 let apJOI: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1197 let apWEI: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1198 let apMI: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1199 var ap_over: i64 = 0 1200 1201 let pS: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1202 let pE: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1203 var mi: i64 = 0 1204 var meshes_skipped: i64 = 0 1205 while mi < nm { 1206 var mesh_ok: i64 = 1 1207 if (skip as i64) != 0 { if g2_name_has(b, mS[mi], mE[mi], skip) == 1 { mesh_ok = 0; meshes_skipped = meshes_skipped + 1 } } 1208 var prA: i64 = 0 - 1 1209 if mesh_ok == 1 { prA = g2_key(b, mS[mi], mE[mi], "\x22primitives\x22:" as *u8) } 1210 if prA >= 0 { 1211 let np: i64 = g2_elems(b, prA, mE[mi], pS, pE, G2_MAXEL) 1212 var pi: i64 = 0 1213 while pi < np { 1214 prims = prims + 1 1215 var ok: i64 = 1 1216 let mode: i64 = g2_ifield(b, pS[pi], pE[pi], "\x22mode\x22:" as *u8, G2_MODE_TRIS) 1217 if mode != G2_MODE_TRIS { ok = 0 } 1218 let atA: i64 = g2_key(b, pS[pi], pE[pi], "\x22attributes\x22:" as *u8) 1219 var posi: i64 = 0 - 1 1220 if atA < 0 { ok = 0 } 1221 if ok == 1 { 1222 let atE2: i64 = g2_objend(b, atA, pE[pi]) 1223 if atE2 < 0 { ok = 0 } else { posi = g2_ifield(b, atA, atE2, "\x22POSITION\x22:" as *u8, 0 - 1) } 1224 } 1225 var joi: i64 = 0 - 1 1226 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) } } } 1227 var wei: i64 = 0 - 1 1228 if ok == 1 { if atA >= 0 { let atE4: i64 = g2_objend(b, atA, pE[pi]); if atE4 > 0 { wei = g2_ifield(b, atA, atE4, "\x22WEIGHTS_0\x22:" as *u8, 0 - 1) } } } 1229 let idxi: i64 = g2_ifield(b, pS[pi], pE[pi], "\x22indices\x22:" as *u8, 0 - 1) 1230 if posi < 0 { ok = 0 } 1231 if idxi < 0 { ok = 0 } 1232 if posi >= na { ok = 0 } 1233 if idxi >= na { ok = 0 } 1234 if ok == 1 { if accCT[posi] != G2_CT_F32 { ok = 0 } } 1235 if ok == 1 { if accV3[posi] != 1 { ok = 0 } } 1236 if ok == 1 { if accCT[idxi] != G2_CT_U16 { if accCT[idxi] != G2_CT_U32 { ok = 0 } } } 1237 if ok == 1 { if accN[idxi] % 3 != 0 { ok = 0 } } 1238 if ok == 1 { if accBV[posi] < 0 { ok = 0 } } 1239 if ok == 1 { if accBV[idxi] < 0 { ok = 0 } } 1240 if ok == 1 { if accBV[posi] >= nv { ok = 0 } } 1241 if ok == 1 { if accBV[idxi] >= nv { ok = 0 } } 1242 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 } } } 1243 if ok == 0 { skipped = skipped + 1 } else { 1244 let pb: i64 = accBV[posi] 1245 let ib2: i64 = accBV[idxi] 1246 var pstride: i64 = bvST[pb] 1247 if pstride == 0 { pstride = 12 } 1248 let pbase: i64 = bino + bvBO[pb] + accBO[posi] 1249 let ibase: i64 = bino + bvBO[ib2] + accBO[idxi] 1250 var isz: i64 = 2 1251 if accCT[idxi] == G2_CT_U32 { isz = 4 } 1252 let pcount: i64 = accN[posi] 1253 vseen = vseen + pcount 1254 let ntri: i64 = accN[idxi] / 3 1255 var pmnx: i64 = G2_MAGIC_2000000000 1256 var pmxx: i64 = 0 - G2_MAGIC_2000000000 1257 var pmny: i64 = G2_MAGIC_2000000000 1258 var pmxy: i64 = 0 - G2_MAGIC_2000000000 1259 var pmnz: i64 = G2_MAGIC_2000000000 1260 var pmxz: i64 = 0 - G2_MAGIC_2000000000 1261 var t: i64 = 0 1262 while t < ntri { 1263 if nt >= G2_MAXTRI { trunc = 1; t = t + ntri } else { 1264 var i0: i64 = 0 1265 var i1: i64 = 0 1266 var i2: i64 = 0 1267 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) } 1268 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) } 1269 var okv: i64 = 1 1270 if i0 >= pcount { okv = 0 } 1271 if i1 >= pcount { okv = 0 } 1272 if i2 >= pcount { okv = 0 } 1273 if G2_ERR != 0 { okv = 0 } 1274 if okv == 1 { 1275 let w00: i64 = g2_u32(b, pbase + i0*pstride) 1276 let w01: i64 = g2_u32(b, pbase + i0*pstride + 4) 1277 let w02: i64 = g2_u32(b, pbase + i0*pstride + 8) 1278 let w10: i64 = g2_u32(b, pbase + i1*pstride) 1279 let w11: i64 = g2_u32(b, pbase + i1*pstride + 4) 1280 let w12: i64 = g2_u32(b, pbase + i1*pstride + 8) 1281 let w20: i64 = g2_u32(b, pbase + i2*pstride) 1282 let w21: i64 = g2_u32(b, pbase + i2*pstride + 4) 1283 let w22: i64 = g2_u32(b, pbase + i2*pstride + 8) 1284 if G2_ERR != 0 { okv = 0 } else { 1285 let x0: i64 = g2_f32mm(w00) 1286 let y0: i64 = g2_f32mm(w01) 1287 let z0: i64 = g2_f32mm(w02) 1288 let x1: i64 = g2_f32mm(w10) 1289 let y1: i64 = g2_f32mm(w11) 1290 let z1: i64 = g2_f32mm(w12) 1291 let x2: i64 = g2_f32mm(w20) 1292 let y2: i64 = g2_f32mm(w21) 1293 let z2: i64 = g2_f32mm(w22) 1294 if y0 < miny { miny = y0 } 1295 if y0 > maxy { maxy = y0 } 1296 if y1 < miny { miny = y1 } 1297 if y1 > maxy { maxy = y1 } 1298 if y2 < miny { miny = y2 } 1299 if y2 > maxy { maxy = y2 } 1300 if x0 < pmnx { pmnx = x0 } 1301 if x0 > pmxx { pmxx = x0 } 1302 if y0 < pmny { pmny = y0 } 1303 if y0 > pmxy { pmxy = y0 } 1304 if z0 < pmnz { pmnz = z0 } 1305 if z0 > pmxz { pmxz = z0 } 1306 let e1x: i64 = x1-x0 1307 let e1y: i64 = y1-y0 1308 let e1z: i64 = z1-z0 1309 let e2x: i64 = x2-x0 1310 let e2y: i64 = y2-y0 1311 let e2z: i64 = z2-z0 1312 var cx: i64 = e1y*e2z - e1z*e2y 1313 var cy: i64 = e1z*e2x - e1x*e2z 1314 var cz: i64 = e1x*e2y - e1y*e2x 1315 let mag: i64 = g2_isqrt(cx*cx + cy*cy + cz*cz) 1316 if mag == 0 { degen = degen + 1 } else { 1317 let so: i64 = nt*12 1318 stg[so]=x0; stg[so+1]=y0; stg[so+2]=z0 1319 stg[so+3]=x1; stg[so+4]=y1; stg[so+5]=z1 1320 stg[so+6]=x2; stg[so+7]=y2; stg[so+8]=z2 1321 // part label = the joint bound to vertex 0 (rigid-dominant proxy; 1322 // full weight-max over 3 verts is the named refinement) 1323 var lab: i64 = 0 - 1 1324 if joi >= 0 { if joi < na { if accBV[joi] >= 0 { if accBV[joi] < nv { 1325 var jstride: i64 = bvST[accBV[joi]] 1326 var jsz: i64 = 1 1327 if accCT[joi] == G2_CT_U16 { jsz = 2 } 1328 if jstride == 0 { jstride = jsz*4 } 1329 let jbase: i64 = bino + bvBO[accBV[joi]] + accBO[joi] 1330 if jsz == 1 { lab = b[jbase + i0*jstride] as i64 } 1331 if jsz == 2 { lab = g2_u16(b, jbase + i0*jstride) } 1332 // skin-local joint -> NODE index, via the skin that owns THIS mesh 1333 var sk3: i64 = 0 - 1 1334 if mi < G2_MAXEL { sk3 = mskin[mi] } 1335 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 } 1336 } } } } 1337 trij[nt] = lab 1338 stg[so+9] = cx*G2_Q14/mag 1339 stg[so+10] = cy*G2_Q14/mag 1340 stg[so+11] = cz*G2_Q14/mag 1341 nt = nt + 1 1342 } 1343 } 1344 } 1345 t = t + 1 1346 } 1347 } 1348 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) } 1349 if accepted < G2_MAXEL { apPOS[accepted] = posi; apIDX[accepted] = idxi; apJOI[accepted] = joi; apWEI[accepted] = wei; apMI[accepted] = mi } else { ap_over = 1 } 1350 accepted = accepted + 1 1351 } 1352 pi = pi + 1 1353 } 1354 } 1355 mi = mi + 1 1356 } 1357 1358 if nt == 0 { g2_refuse("zero triangles accepted -- a form oracle with no surface is vacuous" as *u8); return 5 } 1359 if trunc == 1 { g2_refuse("tri cap hit -- a truncated oracle is a lying ruler (raise G2_MAXTRI deliberately)" as *u8); return 7 } 1360 if G2_ERR != 0 { g2_refuse("out-of-bounds read during extraction -- file declares data it does not carry" as *u8); return 7 } 1361 // unit normalization: determine the AUTHORING SCALE (see g2_pick_scale), then emit. The bound 1362 // is DATA, not code, and its provenance is printed -- the estate's own build governor prints 1363 // envelope_src= for exactly this reason: a bound whose source is unstated gets trusted as 1364 // measured when it may be a built-in fallback. 1365 let hgt: i64 = maxy - miny 1366 var cpath: *u8 = G2_UNITS_CONF 1367 if G2_UCONF != 0 { cpath = G2_UCONF as *u8 } 1368 var floor_mm: i64 = g2_conf_i64(cpath, "sliver_floor_mm" as *u8, 0 - 1) 1369 var bsrc: *u8 = "conf" as *u8 1370 if floor_mm <= 0 { floor_mm = G2_DEF_SLIVER_FLOOR_MM; bsrc = "builtin-default" as *u8 } 1371 let dv: i64 = g2_pick_scale(hgt, floor_mm) 1372 hw(" units: height_raw=" as *u8); pn(hgt) 1373 hw(" sliver_floor_mm=" as *u8); pn(floor_mm) 1374 hw(" ceil_mm=" as *u8); pn(floor_mm*G2_SCALE_STEP - 1) 1375 hw(" bounds_src=" as *u8); hw(bsrc) 1376 hw(" bounds_file=" as *u8); hw(cpath) 1377 hw(" scale_div=" as *u8); pn(dv); hw("\n" as *u8) 1378 if dv == 0 { g2_refuse("no power-of-1000 scale lands the body above the sliver floor -- refusing to guess units" as *u8); hw(" height_raw=" as *u8); pn(hgt); hw(" sliver_floor_mm=" as *u8); pn(floor_mm); hw("\n" as *u8); return 8 } 1379 // ---- R22: an .nxa output leaves the NXMSH2 path here, AFTER the shared acceptance predicate and 1380 // unit determination, and carries the rig into NXANIM01. Nothing below this branch runs for it. 1381 if nxa_mode == 1 { 1382 if ap_over == 1 { g2_refuse("nxa: accepted-primitive list exceeded G2_MAXEL -- a prefix would be a truncated rig; raise it deliberately" as *u8); return 7 } 1383 let cx: *i64 = sys_mmap(G2_CX_WORDS*8) as *i64 1384 cx[0] = b as i64; cx[1] = bino; cx[2] = accBV as i64; cx[3] = accBO as i64; cx[4] = accCT as i64 1385 cx[5] = accN as i64; cx[6] = bvBO as i64; cx[7] = bvST as i64; cx[8] = accepted 1386 cx[9] = apPOS as i64; cx[10] = apIDX as i64; cx[11] = apJOI as i64; cx[12] = apWEI as i64; cx[13] = apMI as i64 1387 cx[14] = mskin as i64; cx[15] = nskin; cx[16] = skJ0 as i64; cx[17] = skJN as i64; cx[18] = skIBM as i64 1388 cx[19] = jn as i64; cx[20] = parentOf as i64; cx[21] = nnodes; cx[22] = dv; cx[23] = outp as i64 1389 cx[24] = na; cx[25] = nv; cx[26] = aS as i64; cx[27] = aE as i64 1390 let rcn: i64 = g2_emit_nxa(cx) 1391 return rcn 1392 } 1393 // ---- layer plan: one layer per USED joint, contiguous by construction (NXMSH2 requires ranges). 1394 // A donor with no skin emits the single "skin" layer exactly as before (additive, never a regression). 1395 var nlay: i64 = 1 1396 let lj: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1397 let lcnt: *i64 = sys_mmap(G2_MAXEL*8) as *i64 1398 if njoint > 0 { 1399 var u: i64 = 0 1400 while u < G2_MAXEL { lcnt[u] = 0; u = u + 1 } 1401 var t3: i64 = 0 1402 while t3 < nt { let L: i64 = trij[t3]; if L >= 0 { if L < G2_MAXEL { lcnt[L] = lcnt[L] + 1 } } t3 = t3 + 1 } 1403 nlay = 0 1404 u = 0 1405 while u < G2_MAXEL { if lcnt[u] > 0 { lj[nlay] = u; nlay = nlay + 1 } u = u + 1 } 1406 if nlay == 0 { nlay = 1 } 1407 } 1408 let hdr2: i64 = G2_HDR + nlay*G2_LAYROW 1409 let gc: i64 = g2_enc(G2_GREY, 1000) 1410 var oc: i64 = 0 1411 var Lx: i64 = 0 1412 while Lx < nlay { 1413 var wt: i64 = 0 1414 while wt < nt { 1415 var take: i64 = 1 1416 if nlay > 1 { take = 0; if trij[wt] == lj[Lx] { take = 1 } } 1417 if take == 1 { 1418 let so2: i64 = wt*12 1419 let o: i64 = hdr2 + oc*G2_TRIREC 1420 var pc: i64 = 0 1421 while pc < 9 { g2_wr32(ob, o + pc*4, g2_enc(stg[so2+pc]/dv, 1)); pc = pc + 1 } 1422 let nb: i64 = g2_enc(stg[so2+9], G2_Q14) 1423 let nb2: i64 = g2_enc(stg[so2+10], G2_Q14) 1424 let nb3: i64 = g2_enc(stg[so2+11], G2_Q14) 1425 var q: i64 = 0 1426 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 } 1427 g2_wr32(ob, o+72, gc); g2_wr32(ob, o+76, gc); g2_wr32(ob, o+80, gc) 1428 oc = oc + 1 1429 } 1430 wt = wt + 1 1431 } 1432 Lx = Lx + 1 1433 } 1434 miny = miny/dv 1435 maxy = maxy/dv 1436 // header + single layer "skin" + per-tri layer ids 1437 ob[0]=78 as u8; ob[1]=88 as u8; ob[2]=77 as u8; ob[3]=83 as u8 1438 ob[4]=72 as u8; ob[5]=50 as u8; ob[6]=0 as u8; ob[7]=0 as u8 1439 g2_wr32(ob, 8, nlay); g2_wr32(ob, 12, nt) 1440 // layer table: 16-byte name from the donor's own node name, then offset+count 1441 var run: i64 = 0 1442 var Ly: i64 = 0 1443 while Ly < nlay { 1444 let lb: i64 = G2_HDR + Ly*G2_LAYROW 1445 var q2: i64 = 0 1446 while q2 < 16 { ob[lb+q2] = 0 as u8; q2 = q2 + 1 } 1447 var cnt2: i64 = nt 1448 if nlay > 1 { 1449 let jidx: i64 = lj[Ly] 1450 cnt2 = lcnt[jidx] 1451 let nd2: i64 = jidx 1452 if nd2 >= 0 { if nd2 < nnodes { if nn1[nd2] > nn0[nd2] { 1453 var w2: i64 = 0 1454 while w2 < 15 { if nn0[nd2]+w2 < nn1[nd2] { ob[lb+w2] = b[nn0[nd2]+w2] } w2 = w2 + 1 } 1455 } } } 1456 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 } 1457 } else { ob[lb]=115 as u8; ob[lb+1]=107 as u8; ob[lb+2]=105 as u8; ob[lb+3]=110 as u8 } 1458 g2_wr32(ob, lb+16, run); g2_wr32(ob, lb+20, cnt2) 1459 run = run + cnt2 1460 Ly = Ly + 1 1461 } 1462 var z: i64 = 0 1463 var Lz: i64 = 0 1464 var zc: i64 = 0 1465 while Lz < nlay { 1466 var cz: i64 = nt 1467 if nlay > 1 { cz = lcnt[lj[Lz]] } 1468 var k2: i64 = 0 1469 while k2 < cz { g2_wr32(ob, hdr2 + nt*G2_TRIREC + zc*4, Lz); zc = zc + 1; k2 = k2 + 1 } 1470 Lz = Lz + 1 1471 } 1472 let bytes: i64 = hdr2 + nt*G2_TRIREC + nt*4 1473 let ofd: i64 = sys_openat_wr(outp, MODE_0644) 1474 if ofd < 0 { g2_refuse("output path unwritable" as *u8); return 6 } 1475 sys_write(ofd, ob, bytes) 1476 sys_close(ofd) 1477 1478 hw("{\x22organ\x22:\x22nx_gltf2mesh\x22,\x22prims\x22:" as *u8); pn(prims) 1479 hw(",\x22prims_accepted\x22:" as *u8); pn(accepted) 1480 hw(",\x22prims_skipped\x22:" as *u8); pn(skipped) 1481 hw(",\x22verts_seen\x22:" as *u8); pn(vseen) 1482 hw(",\x22tris\x22:" as *u8); pn(nt) 1483 hw(",\x22degenerate_dropped\x22:" as *u8); pn(degen) 1484 hw(",\x22meshes_skipped_by_name\x22:" as *u8); pn(meshes_skipped) 1485 hw(",\x22skin_joints\x22:" as *u8); pn(njoint) 1486 hw(",\x22named_parts_emitted\x22:" as *u8); pn(nlay) 1487 hw(",\x22miny_mm\x22:" as *u8); pn(miny) 1488 hw(",\x22maxy_mm\x22:" as *u8); pn(maxy) 1489 hw(",\x22scale_div\x22:" as *u8); pn(dv) 1490 hw(",\x22bytes\x22:" as *u8); pn(bytes) 1491 hw(",\x22note\x22:\x22bind-pose model space; node transforms not applied (skinned donors); morph-target POSITIONs excluded by attributes-scope\x22}" as *u8) 1492 hw("\n" as *u8) 1493 return 0 1494} 1495 1496// ---- selftest: build a real 1-tri glb on disk, convert it, then three malformed inputs must REFUSE ---- 1497func st_wr(path: *u8, buf: *u8, n: i64) -> i64 { 1498 let fd: i64 = sys_openat_wr(path, 420) 1499 if fd < 0 { return 0 - 1 } 1500 sys_write(fd, buf, n) 1501 sys_close(fd) 1502 return 0 1503} 1504func st_mkglb(buf: *u8, breakmode: i64) -> i64 { 1505 // JSON (compact, machine-shape) 1506 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 1507 var jl: i64 = 0 1508 while j[jl] != (0 as u8) { jl = jl + 1 } 1509 var jp: i64 = jl 1510 while jp % 4 != 0 { jp = jp + 1 } 1511 // bin length appears ONLY as a literal below: locals in this frame mis-read (nx_cc bug 1785936860) 1512 let total: i64 = 12 + 8 + jp + 8 + 44 1513 var o: i64 = 0 1514 buf[0]=103 as u8; buf[1]=108 as u8; buf[2]=84 as u8; buf[3]=70 as u8 1515 if breakmode == 2 { buf[0]=88 as u8 } 1516 g2_wr32(buf, 4, 2) 1517 g2_wr32(buf, 8, total) 1518 g2_wr32(buf, 12, jp) 1519 buf[16]=74 as u8; buf[17]=83 as u8; buf[18]=79 as u8; buf[19]=78 as u8 1520 var i: i64 = 0 1521 while i < jl { buf[20+i] = j[i]; i = i + 1 } 1522 while i < jp { buf[20+i] = 32 as u8; i = i + 1 } 1523 o = 20 + jp 1524 g2_wr32(buf, o, 44) 1525 buf[o+4]=66 as u8; buf[o+5]=73 as u8; buf[o+6]=78 as u8; buf[o+7]=0 as u8 1526 // verts (0,0,0) (1,0,0) (0,1,0) as f32 -- offsets all o+N literals, NO further locals (nx_cc bug 1785936860) 1527 g2_wr32(buf, o+8, 0); g2_wr32(buf, o+12, 0); g2_wr32(buf, o+16, 0) 1528 g2_wr32(buf, o+20, g2_enc(1000, 1000)); g2_wr32(buf, o+24, 0); g2_wr32(buf, o+28, 0) 1529 g2_wr32(buf, o+32, 0); g2_wr32(buf, o+36, g2_enc(1000, 1000)); g2_wr32(buf, o+40, 0) 1530 buf[o+44]=0 as u8; buf[o+45]=0 as u8; buf[o+46]=1 as u8; buf[o+47]=0 as u8 1531 buf[o+48]=2 as u8; buf[o+49]=0 as u8; buf[o+50]=0 as u8; buf[o+51]=0 as u8 1532 if breakmode == 1 { return total - 9 } 1533 return total 1534} 1535func g2_selftest() -> i64 { 1536 var fails: i64 = 0 1537 let buf: *u8 = sys_mmap(G2_MAGIC_4096) 1538 let n0: i64 = st_mkglb(buf, 0) 1539 st_wr("/tmp/g2_ok.glb" as *u8, buf, n0) 1540 let r0: i64 = g2_convert("/tmp/g2_ok.glb" as *u8, "/tmp/g2_ok.nxmesh" as *u8, 0 as *u8) 1541 if r0 == 0 { 1542 let vb: *u8 = sys_mmap(G2_MAGIC_4096) 1543 let vfd: i64 = sys_openat_rd("/tmp/g2_ok.nxmesh" as *u8) 1544 let vn: i64 = sys_read(vfd, vb, G2_MAGIC_4096) 1545 sys_close(vfd) 1546 var t0: i64 = 0 1547 if vn > 40 { if vb[0] == (78 as u8) { if vb[5] == (50 as u8) { t0 = 1 } } } 1548 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) } 1549 } else { fails = fails + 1; hw("T0 FAIL convert refused a valid glb\n" as *u8) } 1550 let n1: i64 = st_mkglb(buf, 1) 1551 st_wr("/tmp/g2_trunc.glb" as *u8, buf, n1) 1552 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) } 1553 let n2: i64 = st_mkglb(buf, 2) 1554 st_wr("/tmp/g2_badmagic.glb" as *u8, buf, n2) 1555 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) } 1556 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) } 1557 if fails == 0 { hw("GLTF2MESH-SELFTEST GREEN 4/4\n" as *u8); return 0 } 1558 hw("GLTF2MESH-SELFTEST RED fails=" as *u8); pn(fails); hw("\n" as *u8) 1559 return 1 1560} 1561 1562 1563// Additive preservation-only command. Legacy mesh/NXA conversion remains separate. 1564import "nx_gltf_load.nx" 1565import "nx_bundle_ingest.nx" 1566const GPN_CTX_WORDS:i64=18 1567// GLB stores total byte length in uint32; reserve normalization header/alignment headroom. 1568const GPN_GLB_LENGTH_MAX:i64=4294967295 1569const GPN_NORMALIZATION_HEADROOM:i64=64 1570func gpn_reason(code:i64)->*u8 { 1571 if code==-1{return "invalid-or-overlapping-buffers"} 1572 if code==-10{return "invalid-json"} 1573 if code==-11{return "asset-version-field-refused"} 1574 if code==-12{return "gltf-version-unsupported"} 1575 if code==-13{return "buffers-field-refused"} 1576 if code==-14{return "multiple-buffers-unsupported"} 1577 if code==-15{return "buffer-byte-length-refused"} 1578 if code==-16{return "output-capacity-refused"} 1579 if code==-17{return "buffer-uri-field-refused"} 1580 if code==-18{return "buffer-uri-form-unsupported"} 1581 if code==-19{return "base64-or-decoded-length-refused"} 1582 if code==-20{return "allocation-failed"} 1583 return "normalization-refused" 1584} 1585 1586// External image references are preserved, never fetched. Receipt points to exact source literals. 1587func gpn_deps(cx:*i64,w:*JsonWriter)->i64 { 1588 let src:*u8=cx[14] as *u8;let n:i64=cx[6] 1589 if (src as i64)<=0{return 0} 1590 let states:*u8=sys_mmap(n);let t:*NxJsonTok=nx_json_new(src,n);let sp:*i64=sys_mmap(32) as *i64 1591 var result:i64=-1 1592 if (states as i64)>0 && (t as i64)>0 && (sp as i64)>0 { 1593 glp_reset(t,src,n,0) 1594 let have:i64=glp_member(t,"images" as *u8,sp,states,n) 1595 if have==0{result=0} 1596 if have==1 { 1597 glp_reset(t,src,n,sp[0]) 1598 if t.kind==NX_JSON_LBRACKET { 1599 nx_json_next(t);result=0;var index:i64=0;var unresolved:i64=0 1600 while t.kind!=NX_JSON_RBRACKET { 1601 let hasuri:i64=glp_member(t,"uri" as *u8,sp,states,n) 1602 if hasuri<0{result=-1;break} 1603 let next:i64=t.tok_off 1604 if hasuri==1 { 1605 glp_reset(t,src,n,sp[0]) 1606 if t.kind!=NX_JSON_STRING{result=-1;break} 1607 var embedded:i64=0 1608 if t.tok_len>=7 { 1609 let p:*u8=(src as i64+t.tok_off+1) as *u8 1610 if p[0]==100 as u8 && p[1]==97 as u8 && p[2]==116 as u8 && p[3]==97 as u8 && p[4]==58 as u8{embedded=1} 1611 } 1612 if embedded==0 { 1613 unresolved=unresolved+1;result=result+t.tok_len*6+BIX_JSON_ROW_ROOM 1614 if (w as i64)>0 { 1615 var rc:i64=json_begin_object(w) 1616 rc=rc | bix_num(w,"image_index",index) 1617 rc=rc | bix_key(w,"state","external-uri-unresolved-not-fetched") 1618 rc=rc | json_emit_key(w,"uri_json_literal",16) 1619 rc=rc | json_emit_string(w,(src as i64+t.tok_off) as *u8,t.tok_len) 1620 rc=rc | json_end_object(w) 1621 if rc<0{result=-1;break} 1622 } 1623 } 1624 } 1625 glp_reset(t,src,n,next);index=index+1 1626 if t.kind==NX_JSON_RBRACKET{break} 1627 if t.kind!=NX_JSON_COMMA{result=-1;break};nx_json_next(t) 1628 } 1629 cx[16]=unresolved;cx[17]=index 1630 } 1631 } 1632 } 1633 if (sp as i64)>0{sys_munmap(sp as *u8,32)} 1634 if (t as i64)>0{sys_munmap(t as *u8,NX_JSONTOK_BYTES)} 1635 if (states as i64)>0{sys_munmap(states,n)} 1636 return result 1637} 1638 1639func gpn_receipt(cx:*i64)->i64 { 1640 if sys_clock_gettime_real((cx as i64+12*8) as *i64)!=0{return -1} 1641 let cap:i64=BIX_JSON_BASE_ROOM+cx[15]+6*(bic_len(cx[0] as *u8)+bic_len(cx[1] as *u8)) 1642 let buf:*u8=sys_mmap(cap) 1643 if (buf as i64)<=0{return -1} 1644 let w:*JsonWriter=sys_mmap(40) as *JsonWriter 1645 if (w as i64)<=0{sys_munmap(buf,cap);return -1} 1646 let prior:*u8=sys_mmap(JE_MAX_DEPTH+16) 1647 if (prior as i64)<=0{sys_munmap(w as *u8,40);sys_munmap(buf,cap);return -1} 1648 w.buf=buf;w.pos=0;w.cap=cap;w.depth=0;w.prior=prior 1649 var rc:i64=json_begin_object(w) 1650 rc=rc | bix_key(w,"schema","nishi.gltf.normalization.v1") 1651 rc=rc | bix_key(w,"state",cx[9] as *u8) 1652 rc=rc | bix_key(w,"source",cx[0] as *u8) 1653 rc=rc | bix_key(w,"source_expected_sha256",cx[3] as *u8) 1654 rc=rc | bix_key(w,"source_sha256",cx[4] as *u8) 1655 rc=rc | bix_num(w,"source_bytes",cx[6]) 1656 rc=rc | bix_key(w,"output",cx[1] as *u8) 1657 rc=rc | bix_key(w,"output_sha256",cx[5] as *u8) 1658 rc=rc | bix_num(w,"output_bytes",cx[7]) 1659 rc=rc | bix_num(w,"code",cx[8]) 1660 rc=rc | bix_num(w,"exit_code",cx[11]) 1661 rc=rc | bix_num(w,"max_input_bytes",cx[10]) 1662 rc=rc | bix_key(w,"preservation","exact authored JSON except buffers[0].uri; exact decoded BIN; images and animation retained") 1663 rc=rc | bix_key(w,"scope","single embedded glTF2 buffer; canonical Base64; no full glTF schema validation or rendering acceptance") 1664 rc=rc | bix_num(w,"observed_unix",cx[12]) 1665 var depstate:*u8="not-inspected" 1666 if cx[14]!=0{depstate="source-inspected"} 1667 rc=rc | bix_key(w,"dependency_inventory_state",depstate) 1668 var outkind:*u8="candidate-only" 1669 if fsx_seq(cx[9] as *u8,"normalized-verified")==1{outkind="verified-written-or-identical"} 1670 rc=rc | bix_key(w,"output_identity_kind",outkind) 1671 rc=rc | bix_num(w,"images",cx[17]) 1672 rc=rc | bix_num(w,"external_image_uris_unresolved",cx[16]) 1673 rc=rc | json_emit_key(w,"unresolved_dependencies",23) 1674 rc=rc | json_begin_array(w) 1675 if gpn_deps(cx,w)<0{rc=-1} 1676 rc=rc | json_end_array(w) 1677 rc=rc | json_end_object(w) 1678 if rc>=0 {rc=bix_receipt(cx[2] as *u8,w)} 1679 sys_munmap(prior,JE_MAX_DEPTH+16);sys_munmap(w as *u8,40);sys_munmap(buf,cap) 1680 return rc 1681} 1682func gpn_convert(cx:*i64,src:*u8,out:*u8,sha:*u8,digest:*u8)->i64 { 1683 let n:i64=cx[6] 1684 if cgo_read(cx[0] as *u8,src,n+1)!=n{cx[9]="source-read-or-size-changed" as i64;return 3} 1685 sha256_digest(src,n,digest);pv_hex(digest,sha);cx[4]=sha as i64 1686 if fsx_seq(sha,cx[3] as *u8)!=1{cx[9]="source-identity-mismatch" as i64;return 3} 1687 cx[14]=src as i64 1688 let depcap:i64=gpn_deps(cx,0 as *JsonWriter) 1689 if depcap<0{cx[14]=0;cx[9]="image-dependency-inventory-refused" as i64;return 3} 1690 cx[15]=depcap 1691 let built:i64=gltf_json_to_glb(src,n,out,n+GPN_NORMALIZATION_HEADROOM) 1692 if built<0{cx[8]=built;cx[9]=gpn_reason(built) as i64;return 3} 1693 cx[7]=built 1694 let outsha:*u8=(sha as i64+PV_SHA_HEX+1) as *u8 1695 sha256_digest(out,built,digest);pv_hex(digest,outsha);cx[5]=outsha as i64 1696 cx[9]="validated-output-pending" as i64 1697 if gpn_receipt(cx)!=0{cx[9]="intent-receipt-failed" as i64;return 4} 1698 let stored:i64=bix_store(cx[1] as *u8,out,built) 1699 if stored!=0{cx[8]=stored;cx[9]=bix_store_state(stored) as i64;return 4} 1700 cx[9]="normalized-verified" as i64;return 0 1701} 1702func gpn_main(argc:i64,argv:*i64)->i64 { 1703 if argc!=7{hw("{\"schema\":\"nishi.gltf.normalization.v1\",\"state\":\"usage-refused\",\"usage\":\"normalize-json input.gltf output.glb receipt.jsonl max_input_bytes expected_input_sha256\"}\n" as *u8);return 2} 1704 let maximum:i64=bix_uint(argv[5] as *u8) 1705 if maximum<=0{hw("{\"state\":\"invalid-input-limit\"}\n" as *u8);return 2} 1706 if maximum>GPN_GLB_LENGTH_MAX-GPN_NORMALIZATION_HEADROOM{hw("{\"state\":\"invalid-input-limit\"}\n" as *u8);return 2} 1707 if pv_is_sha_hex(argv[6] as *u8)!=1{hw("{\"state\":\"invalid-expected-sha256\"}\n" as *u8);return 2} 1708 let input:*u8=argv[2] as *u8;let output:*u8=argv[3] as *u8;let receipt:*u8=argv[4] as *u8 1709 if fsx_denied(input)==1 || fsx_write_denied(output)==1 || fsx_write_denied(receipt)==1 {hw("{\"state\":\"path-policy-refused\"}\n" as *u8);return 5} 1710 if bix_alias(input,output)!=0 || bix_alias(input,receipt)!=0 || bix_alias(output,receipt)!=0 {hw("{\"state\":\"path-alias-or-metadata-refused\"}\n" as *u8);return 5} 1711 let cx:*i64=sys_mmap(GPN_CTX_WORDS*8) as *i64 1712 if (cx as i64)<=0{return 4} 1713 cx[0]=input as i64;cx[1]=output as i64;cx[2]=receipt as i64;cx[3]=argv[6] 1714 cx[4]="" as i64;cx[5]="" as i64;cx[9]="source-open-failed" as i64;cx[10]=maximum 1715 var result:i64=3;let fd:i64=sys_openat_rd(input) 1716 if fd>=0 { 1717 let n:i64=sys_lseek(fd,0,2);sys_close(fd);cx[6]=n 1718 cx[9]="source-empty-or-over-limit" as i64 1719 if n>0 && n<=maximum { 1720 let src:*u8=sys_mmap(n+1);let out:*u8=sys_mmap(n+GPN_NORMALIZATION_HEADROOM) 1721 let sha:*u8=sys_mmap((PV_SHA_HEX+1)*2);let digest:*u8=sys_mmap(PV_SHA_BYTES) 1722 cx[9]="allocation-failed" as i64;result=4 1723 if (src as i64)>0 && (out as i64)>0 && (sha as i64)>0 && (digest as i64)>0 { 1724 result=gpn_convert(cx,src,out,sha,digest) 1725 } 1726 // Receipt consumes hash buffers before their release, including refusal evidence. 1727 cx[11]=result;if gpn_receipt(cx)!=0{result=4} 1728 if (digest as i64)>0{sys_munmap(digest,PV_SHA_BYTES)} 1729 if (sha as i64)>0{sys_munmap(sha,(PV_SHA_HEX+1)*2)} 1730 if (out as i64)>0{sys_munmap(out,n+GPN_NORMALIZATION_HEADROOM)} 1731 if (src as i64)>0{sys_munmap(src,n+1)} 1732 sys_munmap(cx as *u8,GPN_CTX_WORDS*8);return result 1733 } 1734 } 1735 cx[11]=result;if gpn_receipt(cx)!=0{result=4};sys_munmap(cx as *u8,GPN_CTX_WORDS*8);return result 1736} 1737 1738 1739func gli_report(src:*u8,n:i64,stats:*i64,input:*u8,receipt:*u8,sha:*u8)->i64 { 1740 let cap:i64=stats[1]+BIX_JSON_BASE_ROOM+GLI_STATS_WORDS*BIX_JSON_ROW_ROOM+6*(bic_len(input)+bic_len(receipt)) 1741 let buf:*u8=sys_mmap(cap);let w:*JsonWriter=sys_mmap(40) as *JsonWriter;let prior:*u8=sys_mmap(JE_MAX_DEPTH+16) 1742 if (buf as i64)<=0 || (w as i64)<=0 || (prior as i64)<=0{return 4} 1743 w.buf=buf;w.cap=cap;w.pos=0;w.depth=0;w.prior=prior 1744 var rc:i64=json_begin_object(w) 1745 rc=rc|bix_key(w,"schema","nishi.gltf.inventory.v1") 1746 rc=rc|bix_key(w,"state","complete-structural-inventory") 1747 rc=rc|bix_key(w,"source",input);rc=rc|bix_key(w,"source_sha256",sha);rc=rc|bix_num(w,"source_bytes",n) 1748 rc=rc|bix_key(w,"scope","GLB container and JSON structural inventory; no accessor-value validation, no rig/material/animation conversion, no rendering or physical-unit qualification") 1749 rc=rc|bix_key(w,"preservation","source unchanged; authored_json is exact complete source JSON including materials, channels, morph targets, transforms and extensions") 1750 rc=rc|bix_num(w,"json_bytes",stats[1]);rc=rc|bix_num(w,"bin_bytes",stats[2]);rc=rc|bix_num(w,"chunks",stats[3]) 1751 rc=rc|json_emit_key(w,"counts",6);rc=rc|json_begin_object(w) 1752 var i:i64=4;while i<GLI_STATS_WORDS{rc=rc|bix_num(w,gli_name(i),stats[i]);i=i+1} 1753 rc=rc|json_end_object(w) 1754 rc=rc|json_emit_key(w,"authored_json",13);rc=rc|je_sep(w) 1755 rc=rc|je_emit_literal(w,(src as i64+stats[0]) as *u8,stats[1]);rc=rc|json_end_object(w) 1756 if rc>=0 {rc=bix_store(receipt,buf,w.pos)} 1757 if rc==0 { 1758 bic_w(1,"{\"schema\":\"nishi.gltf.inventory.result.v1\",\"state\":\"stored-verified\",\"report_bytes\":");nxi_out(w.pos) 1759 bic_w(1,",\"source_bytes\":");nxi_out(n);bic_w(1,"}\n") 1760 } 1761 sys_munmap(prior,JE_MAX_DEPTH+16);sys_munmap(w as *u8,40);sys_munmap(buf,cap) 1762 if rc!=0{return 4};return 0 1763} 1764func gli_main(argc:i64,argv:*i64)->i64 { 1765 if argc!=6 {bic_w(1,"{\"state\":\"usage-refused\",\"usage\":\"inspect-glb input.glb receipt.json max_input_bytes expected_sha256\"}\n");return 2} 1766 let input:*u8=argv[2] as *u8;let receipt:*u8=argv[3] as *u8 1767 let maximum:i64=bix_uint(argv[4] as *u8) 1768 if maximum<=0 || maximum>GPN_GLB_LENGTH_MAX || pv_is_sha_hex(argv[5] as *u8)!=1{return 2} 1769 if fsx_denied(input)==1 || fsx_write_denied(receipt)==1 || bix_alias(input,receipt)!=0{return 5} 1770 let fd:i64=sys_openat_rd(input);if fd<0{return 3} 1771 let n:i64=sys_lseek(fd,0,2);sys_close(fd) 1772 if n<=0 || n>maximum {bic_w(1,"{\"state\":\"input-size-refused\"}\n");return 3} 1773 let src:*u8=sys_mmap(n+1);let stats:*i64=sys_mmap(GLI_STATS_WORDS*8) as *i64 1774 let digest:*u8=sys_mmap(PV_SHA_BYTES);let sha:*u8=sys_mmap(PV_SHA_HEX+1) 1775 var result:i64=4 1776 if (src as i64)>0 && (stats as i64)>0 && (digest as i64)>0 && (sha as i64)>0 { 1777 if cgo_read(input,src,n+1)==n { 1778 if sha256_digest_checked_native(src,n,digest)==0 { 1779 pv_hex(digest,sha) 1780 if fsx_seq(sha,argv[5] as *u8)==1 { 1781 let inspected:i64=gltf_inventory_glb(src,n,stats) 1782 if inspected==0 {result=gli_report(src,n,stats,input,receipt,sha)} else { 1783 bic_w(1,"{\"state\":\"inventory-refused\",\"code\":");nxi_out(inspected);bic_w(1,"}\n");result=3 1784 } 1785 } else {bic_w(1,"{\"state\":\"source-hash-mismatch\"}\n");result=3} 1786 } 1787 } 1788 } 1789 if (sha as i64)>0{sys_munmap(sha,PV_SHA_HEX+1)} 1790 if (digest as i64)>0{sys_munmap(digest,PV_SHA_BYTES)} 1791 if (stats as i64)>0{sys_munmap(stats as *u8,GLI_STATS_WORDS*8)} 1792 if (src as i64)>0{sys_munmap(src,n+1)} 1793 return result 1794} 1795 1796func main(argc: i64, argv: *i64) -> i64 { 1797 if argc>=2 {if fsx_seq(argv[1] as *u8,"inspect-glb")==1{return gli_main(argc,argv)}} 1798 if argc>=2 {if fsx_seq(argv[1] as *u8,"normalize-json")==1 {return gpn_main(argc,argv)}} 1799 if argc >= 2 { 1800 let a1: *u8 = argv[1] as *u8 1801 if g2_lit_at(a1, 0, 8, "selftest" as *u8) == 8 { let rc: i64 = g2_selftest(); sys_exit(rc); return rc } 1802 } 1803 if argc < 3 { 1804 hw("usage: nx_gltf2mesh <in.glb|.vrm> <out.nxmesh> [skip-mesh-name-substr] | selftest\n" as *u8) 1805 hw(" nx_gltf2mesh <in.glb|.vrm> <out.nxa> -- rigged donor -> NXANIM01 VERT/TRIS/SKEL/SKIN (R22, g2_skin_weights)\n" as *u8) 1806 sys_exit(2) 1807 return 2 1808 } 1809 var skip: *u8 = 0 as *u8 1810 if argc >= 4 { if g2_filter(argv[3] as *u8) == 1 { skip = argv[3] as *u8 } } 1811 if argc >= 5 { if g2_filter(argv[4] as *u8) == 1 { skip = argv[4] as *u8 } } 1812 let rc2: i64 = g2_convert(argv[1] as *u8, argv[2] as *u8, skip) 1813 sys_exit(rc2) 1814 return rc2 1815}