code wiki / (root) / nx_nxa_skin_authored_t37.nx

nx_nxa_skin_authored_t37.nx source

↩ module page · 1083 lines · 45856 B

1// nx_nxa_skin.nx -- RIG IMPORTER: fills the NXA's SKEL + SKIN sections from a binary FBX. 2// Walks the FBX id-graph (Model/LimbNode joints, Deformer Clusters with Indexes/Weights/ 3// TransformLink bind matrices, the Connections table) and emits a 5-section NXA: 4// VERT/TRIS/CLUS copied from the mesh import, SKEL = joints [parent][t mm][quat q12=identity] 5// (delta-LBS convention: animation is applied RELATIVE to bind, so identity bind quats are 6// exact), SKIN = per-vertex top-4 joints + q12 weights normalized to sum 4096. 7// usage: nx_nxa_skin <in.fbx> <in.nxa> <out.nxa> 8// license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_zlib_wrap.nx" 11import "nx_nxa.nx" 12import "nx_nxa_fk.nx" 13import "nx_fbx_core.nx" 14// G_USF: FBX GlobalSettings UnitScaleFactor in MICRO units (1000000 = factor 1.0), parsed once 15// in main via fbxc_unit_scale_micro and applied at every LENGTH site (Lcl Translation, the 16// TransformLink bind translations) -- rotations and weights are scale-free. Slot sits in the 17// spare G words past G_CLOV, same convention as the other G_ offsets. 18const G_USF: i64 = 357091 19const G_MAGIC_20000: i64 = 20000 20const G_MAGIC_2047: i64 = 2047 21const G_MAGIC_1048575: i64 = 1048575 22const G_MAGIC_4503599627370496: i64 = 4503599627370496 23const G_MAGIC_4090: i64 = 4090 24const G_MAGIC_99990: i64 = 99990 25const G_MAGIC_4096: i64 = 4096 26const G_MAGIC_7500: i64 = 7500 27const G_MAGIC_500000: i64 = 500000 28const G_MAGIC_4611686018427387903: i64 = 4611686018427387903 29 30// G layout (word offsets): 0=mcount 1=ccount 2=conncount 3=bestVertAlen 4=bestGeomId 5=skincount 31const G_MO: i64 = 16 // model ids x4096 32const G_LO: i64 = 4112 // limb flags x4096 33// CLUSTER TABLES x2048 (2026-08-23): the x512 era saturated SILENTLY on every multi-geometry 34// Fab donor -- all four measured donors reported exactly clusters=510 (the cap, not the count), 35// and every cluster past the cap dropped its authored weights without a word. 16 skins x ~110 36// joints can approach 2000 clusters; 2048 covers that and the count is now honest again. 37// CLUSTER TABLES x8192 (2026-08-23, second raise same day): x2048 ALSO saturated -- three 38// donors again read exactly clusters=2046, the cap wearing the shape of a measurement. A Fab 39// donor's cluster count is skins x joints (30 x 108 = 3240; toon 8 x 370 = 2960), so 8192 40// covers the class with headroom -- AND the walker now COUNTS overflow at G_CLOV and the 41// receipt prints it, so a future saturation announces itself instead of impersonating a count. 42const G_CID: i64 = 8208 // cluster ids x8192 43const G_CIX: i64 = 16400 // cluster Indexes meta (off,alen,enc,clen) x8192x4 44const G_CW: i64 = 49168 // cluster Weights meta x8192x4 45const G_CT: i64 = 81936 // cluster TransformLink meta x8192x4 46const G_CONN: i64 = 114704 // connection pairs (src,dst) x100000x2 47const G_SID: i64 = 314704 // Skin deformer ids x64 48const G_PR: i64 = 316000 // per-model PreRotation millideg x3 49const G_LR: i64 = 328288 // per-model Lcl Rotation default millideg x3 50const G_LT: i64 = 340576 // per-model Lcl Translation default units x3 51const G_PF: i64 = 352864 // per-model flags: 1=pre 2=lclrot 4=lclt 52const G_WORDS: i64 = 358000 53// MULTI-GEOMETRY (2026-08-23): table of every Vertices-array encounter in document order -- 54// (geometry id, array len) pairs -- mirroring nx_fbx_import's merge order, so per-geometry 55// vertex bases in the merged in.nxa are prefix sums over this table. Count at G_GT, pairs after. 56// Cap 64 geometries; overflow COUNTED at G_GTOV and printed, never silent (a dropped geometry 57// would silently unskin its part). Table lives inside the G arena tail: 356960+129 < 358000. 58const G_GT: i64 = 356960 59const G_GTCAP: i64 = 64 60const G_GTOV: i64 = 357089 61// cluster-table admission cap: table is x8192, two slots of slack kept exactly as the x512 era 62// kept (512-2=510), so the walker can never write the final pair out of bounds mid-node. 63const G_CLUSTER_CAP: i64 = 8190 64// cluster overflow counter: clusters seen past the cap. Printed on the receipt -- a saturated 65// table must say so, never impersonate a count (this line exists because it happened twice today). 66const G_CLOV: i64 = 357090 67 68func skw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 69func skn(v: i64) -> i64 { 70 let t: *u8 = sys_mmap(32) as *u8 71 var m: i64 = v; var w: i64 = 0 72 if m<0 { t[w]=45 as u8; w=w+1; m=0-m } 73 if m==0 { t[w]=48 as u8; sys_write(1,t,w+1); return 0 } 74 let d: *u8 = sys_mmap(32) as *u8 75 var k: i64=0 76 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 77 var j: i64=0 78 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 } 79 sys_write(1,t,w); return 0 80} 81func sk_u32(b: *u8, o: i64) -> i64 { 82 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8) 83 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24) 84} 85func sk_u64(b: *u8, o: i64) -> i64 { return sk_u32(b, o) | (sk_u32(b, o + 4) << 32) } 86// IEEE754 double -> value x scale (scale=1000 for mm, 4096 for q12 weights). DRY debt vs 87// nx_fbx_measure's copy -- extract to a shared nx_f64.nx when the third user appears. 88func sk_f64(b: *u8,o: i64,scale: i64) -> i64 { 89 // Shift the IEEE mantissa before decimal scaling; mantissa*4096 wraps at weight 1.0. 90 let bits:i64=fbxc_i64le(b,o) 91 let exponent:i64=(bits>>52)&2047 92 if scale<0||scale>G_MAGIC_20000||exponent>=1043{return 0} 93 return fbxc_f64_p20(bits)*scale/FBXC_P20 94} 95 96// recursive walk; ctx = current Cluster index (child arrays attach to it) or -1; 97// gctx = current Geometry node id (Vertices children identify OUR mesh's geometry) or 0 98func sk_walk(b: *u8, flen: i64, off0: i64, big: i64, G: *i64, ctx: i64, gctx: i64, mctx: i64) -> i64 { 99 let off: i64 = off0 100 var endo: i64 = 0 101 var nprops: i64 = 0 102 var plen: i64 = 0 103 var nlen: i64 = 0 104 var p: i64 = 0 105 if big == 1 { 106 endo = sk_u64(b, off) 107 nprops = sk_u64(b, off + 8) 108 plen = sk_u64(b, off + 16) 109 nlen = (b[off + 24] & 0xff) as i64 110 p = off + 25 111 } 112 if big == 0 { 113 endo = sk_u32(b, off) 114 nprops = sk_u32(b, off + 4) 115 plen = sk_u32(b, off + 8) 116 nlen = (b[off + 12] & 0xff) as i64 117 p = off + 13 118 } 119 if endo == 0 { return off } 120 if endo > flen { return flen } 121 var kind: i64 = 0 122 if nlen == 5 { if (b[p]&0xff)==77 { if (b[p+4]&0xff)==108 { kind = 1 } } } // Model 123 if nlen == 8 { 124 if (b[p]&0xff)==68 { if (b[p+7]&0xff)==114 { kind = 2 } } // Deformer 125 if (b[p]&0xff)==71 { if (b[p+7]&0xff)==121 { kind = 7 } } // Geometry 126 if (b[p]&0xff)==86 { if (b[p+7]&0xff)==115 { kind = 8 } } // Vertices 127 } 128 if nlen == 7 { 129 if (b[p]&0xff)==73 { if (b[p+6]&0xff)==115 { kind = 3 } } // Indexes 130 if (b[p]&0xff)==87 { if (b[p+6]&0xff)==115 { kind = 4 } } // Weights 131 } 132 if nlen == 13 { if (b[p]&0xff)==84 { if (b[p+12]&0xff)==107 { kind = 5 } } } // TransformLink 133 if nlen == 1 { if (b[p]&0xff)==67 { kind = 6 } } // C 134 if nlen == 1 { if (b[p]&0xff)==80 { kind = 9 } } // P (Properties70) 135 p = p + nlen 136 let pstart: i64 = p 137 var id1: i64 = 0 138 var id2: i64 = 0 139 var lseen: i64 = 0 140 var s1l: i64 = 0 141 var s1o: i64 = 0 142 var s2l: i64 = 0 143 var s2o: i64 = 0 144 var sseen: i64 = 0 145 var dseen: i64 = 0 146 var dv1: i64 = 0 147 var dv2: i64 = 0 148 var dv3: i64 = 0 149 var arro: i64 = 0 150 var arrl: i64 = 0 151 var arre: i64 = 0 152 var arrc: i64 = 0 153 var pi: i64 = 0 154 var stop: i64 = 0 155 while pi < nprops { 156 if stop == 0 { 157 let t: i64 = (b[p] & 0xff) as i64 158 p = p + 1 159 var adv: i64 = 0 - 1 160 if t == 83 { 161 let l: i64 = sk_u32(b, p) 162 sseen = sseen + 1 163 if sseen == 1 { s1o = p + 4; s1l = l } 164 if sseen == 2 { s2o = p + 4; s2l = l } 165 adv = 4 + l 166 } 167 if t == 82 { adv = 4 + sk_u32(b, p) } 168 if t == 89 { adv = 2 } 169 if t == 67 { adv = 1 } 170 if t == 73 { adv = 4 } 171 if t == 70 { adv = 4 } 172 if t == 68 { 173 if dseen == 0 { dv1 = p } 174 if dseen == 1 { dv2 = p } 175 if dseen == 2 { dv3 = p } 176 dseen = dseen + 1 177 adv = 8 178 } 179 if t == 76 { 180 lseen = lseen + 1 181 if lseen == 1 { id1 = sk_u64(b, p) } 182 if lseen == 2 { id2 = sk_u64(b, p) } 183 adv = 8 184 } 185 var isarr: i64 = 0 186 if t == 100 { isarr = 1 } 187 if t == 102 { isarr = 1 } 188 if t == 108 { isarr = 1 } 189 if t == 105 { isarr = 1 } 190 if t == 98 { isarr = 1 } 191 if isarr == 1 { 192 arrl = sk_u32(b, p) 193 arre = sk_u32(b, p + 4) 194 arrc = sk_u32(b, p + 8) 195 arro = p + 12 196 adv = 12 + arrc 197 } 198 if adv < 0 { stop = 1 } 199 if adv >= 0 { p = p + adv } 200 } 201 pi = pi + 1 202 } 203 var childctx: i64 = ctx 204 var childg: i64 = gctx 205 var childm: i64 = mctx 206 if kind == 1 { if G[0] < G_MAGIC_4090 { 207 G[G_MO + G[0]] = id1 208 var lf: i64 = 0 209 if s2l == 8 { if (b[s2o]&0xff)==76 { if (b[s2o+7]&0xff)==101 { lf = 1 } } } 210 G[G_LO + G[0]] = lf 211 childm = G[0] 212 G[0] = G[0] + 1 213 } } 214 if kind == 2 { if s2l == 7 { 215 var ctf: i64 = 0 216 if G[1] < G_CLUSTER_CAP { ctf = 1 } 217 if ctf == 1 { 218 G[G_CID + G[1]] = id1 219 childctx = G[1] 220 G[1] = G[1] + 1 221 } 222 if ctf == 0 { G[G_CLOV] = G[G_CLOV] + 1 } 223 } } 224 if kind == 2 { if s2l == 4 { if G[5] < 60 { 225 G[G_SID + G[5]] = id1 226 G[5] = G[5] + 1 227 } } } 228 if kind == 7 { childg = id1 } 229 if kind == 8 { 230 if arrl > G[3] { G[3] = arrl; G[4] = gctx } 231 var gtf: i64 = 0 232 if G[G_GT] < G_GTCAP { gtf = 1 } 233 if gtf == 1 { 234 G[G_GT + 1 + G[G_GT]*2] = gctx 235 G[G_GT + 2 + G[G_GT]*2] = arrl 236 G[G_GT] = G[G_GT] + 1 237 } 238 if gtf == 0 { G[G_GTOV] = G[G_GTOV] + 1 } 239 } 240 if kind == 3 { if ctx >= 0 { 241 G[G_CIX + ctx*4] = arro 242 G[G_CIX + ctx*4 + 1] = arrl 243 G[G_CIX + ctx*4 + 2] = arre 244 G[G_CIX + ctx*4 + 3] = arrc 245 } } 246 if kind == 4 { if ctx >= 0 { 247 G[G_CW + ctx*4] = arro 248 G[G_CW + ctx*4 + 1] = arrl 249 G[G_CW + ctx*4 + 2] = arre 250 G[G_CW + ctx*4 + 3] = arrc 251 } } 252 if kind == 5 { if ctx >= 0 { 253 G[G_CT + ctx*4] = arro 254 G[G_CT + ctx*4 + 1] = arrl 255 G[G_CT + ctx*4 + 2] = arre 256 G[G_CT + ctx*4 + 3] = arrc 257 } } 258 if kind == 6 { if G[2] < G_MAGIC_99990 { 259 G[G_CONN + G[2]*2] = id1 260 G[G_CONN + G[2]*2 + 1] = id2 261 G[2] = G[2] + 1 262 } } 263 // Properties70 P entry under a Model: name = FIRST S prop; vector value = 3 D props. 264 // Matched by (len, first, last): PreRotation(11 P..n), Lcl Rotation(12 L..n), 265 // Lcl Translation(15 L..n). Angles degrees->millideg, translation units x1000. 266 if kind == 9 { if mctx >= 0 { if dseen >= 3 { 267 var pk: i64 = 0 268 if s1l == 11 { if (b[s1o]&0xff)==80 { if (b[s1o+10]&0xff)==110 { pk = 1 } } } 269 if s1l == 12 { if (b[s1o]&0xff)==76 { if (b[s1o+11]&0xff)==110 { pk = 2 } } } 270 if s1l == 15 { if (b[s1o]&0xff)==76 { if (b[s1o+14]&0xff)==110 { pk = 3 } } } 271 if pk == 1 { 272 G[G_PR + mctx*3] = sk_f64(b, dv1, 1000) 273 G[G_PR + mctx*3 + 1] = sk_f64(b, dv2, 1000) 274 G[G_PR + mctx*3 + 2] = sk_f64(b, dv3, 1000) 275 G[G_PF + mctx] = G[G_PF + mctx] | 1 276 } 277 if pk == 2 { 278 G[G_LR + mctx*3] = sk_f64(b, dv1, 1000) 279 G[G_LR + mctx*3 + 1] = sk_f64(b, dv2, 1000) 280 G[G_LR + mctx*3 + 2] = sk_f64(b, dv3, 1000) 281 G[G_PF + mctx] = G[G_PF + mctx] | 2 282 } 283 if pk == 3 { 284 // LENGTH site: Lcl Translation carries the unit factor (identity at 1.0). 285 G[G_LT + mctx*3] = sk_f64(b, dv1, 1000) * G[G_USF] / FBXC_MICRO 286 G[G_LT + mctx*3 + 1] = sk_f64(b, dv2, 1000) * G[G_USF] / FBXC_MICRO 287 G[G_LT + mctx*3 + 2] = sk_f64(b, dv3, 1000) * G[G_USF] / FBXC_MICRO 288 G[G_PF + mctx] = G[G_PF + mctx] | 4 289 } 290 } } } 291 p = pstart + plen 292 var sent: i64 = 13 293 if big == 1 { sent = 25 } 294 while p < endo - sent { 295 p = sk_walk(b, flen, p, big, G, childctx, childg, childm) 296 if p >= flen { return flen } 297 } 298 return endo 299} 300 301// inflate-or-raw helper for FBX arrays 302func sk_arr(b: *u8, off: i64, alen: i64, enc: i64, clen: i64, esz: i64) -> *u8 { 303 if enc == 0 { return ((b as i64) + off) as *u8 } 304 let zr: *NxZlibResult = nx_zlib_inflate(((b as i64) + off) as *u8, clen, alen*esz + G_MAGIC_4096) 305 if zr.error_code != 0 { return 0 as *u8 } 306 return zr.output_data 307} 308 309static sk_full_payload:*i64 310static sk_full_words:i64 311func sk_weight_q48(b:*u8,o:i64)->i64{ 312 let bits:i64=fbxc_i64le(b,o);let exponent:i64=(bits>>52)&2047 313 if bits<0||exponent>1036{return 0-1};if exponent==0{return 0} 314 let mantissa:i64=(bits&4503599627370495)|4503599627370496 315 let shift:i64=1027-exponent;if shift>62{return 0};if shift<0{return mantissa<<(0-shift)};return mantissa>>shift 316} 317func sk_authored_main(argc: i64, argv: *i64) -> i64 { 318 sk_full_payload=0 as *i64;sk_full_words=0 319 var authored:i64=0 320 if argc>4{if fbxc_name_is(argv[4] as *u8,0,8,"authored" as *u8)==1{authored=1}else{return 2}} 321 if argc < 4 { skw("usage: nx_nxa_skin <in.fbx> <in.nxa> <out.nxa>\n" as *u8); return 2 } 322 let lpf: *i64 = sys_mmap(16) as *i64 323 let fb: *u8 = sys_map_file(argv[1] as *u8, lpf) 324 let flen: i64 = lpf[0] 325 if flen < 64 { skw("fbx unreadable\n" as *u8); return 3 } 326 let ver: i64 = sk_u32(fb, 23) 327 var big: i64 = 0 328 if ver >= G_MAGIC_7500 { big = 1 } 329 let G: *i64 = sys_mmap(G_WORDS*8) as *i64 330 // unit scale: parse once, announce, stash for every length site (see G_USF decl). -2 refuses 331 // (forged/nonpositive factor); -1 (absent) assumes the FBX template default 1.0, announced. 332 var usf9: i64 = fbxc_unit_scale_micro(fb, flen) 333 if usf9 == 0 - 2 { skw("NXA-SKIN REFUSED: UnitScaleFactor present but not a positive double\n" as *u8); return 3 } 334 if usf9 == 0 - 1 { 335 skw("unit_scale=ABSENT assumed=1000000 micro (FBX template default)\n" as *u8) 336 usf9 = FBXC_MICRO 337 } else { 338 skw("unit_scale_micro=" as *u8); skn(usf9); skw(" source=GlobalSettings\n" as *u8) 339 } 340 G[G_USF] = usf9 341 var pos: i64 = 27 342 var guard: i64 = 0 343 while pos < flen - 200 { 344 if guard > G_MAGIC_500000 { break } 345 let e: i64 = sk_walk(fb, flen, pos, big, G, 0 - 1, 0, 0 - 1) 346 if e <= pos { break } 347 pos = e 348 guard = guard + 1 349 } 350 let mc: i64 = G[0] 351 let cc: i64 = G[1] 352 let nc: i64 = G[2] 353 let gid: i64 = G[4] 354 skw("models=" as *u8); skn(mc) 355 skw(" clusters=" as *u8); skn(cc) 356 skw(" conns=" as *u8); skn(nc) 357 skw(" skins=" as *u8); skn(G[5]) 358 skw(" cluster_overflow=" as *u8); skn(G[G_CLOV]); skw("\n" as *u8) 359 // MULTI-GEOMETRY (2026-08-23): a Skin is valid iff it connects to ANY geometry in the walk 360 // table, and we RECORD WHICH -- each cluster's Indexes are local to its own geometry's 361 // vertex space, so the stamping loop offsets by that geometry's base in the merged mesh. 362 // (The old rule -- only the LARGEST geometry's skin counts -- was the single-geometry era: 363 // on a 16-part Fab donor the largest part need not be the skinned body, and it filtered 364 // every cluster out: measured valid_clusters=0 skinned=0 on dark_witch.) 365 let sval: *i64 = sys_mmap(64*8 + 64) as *i64 366 let sgeo: *i64 = sys_mmap(64*8 + 64) as *i64 367 var sv0: i64 = 0 368 while sv0 < G[5] { 369 sval[sv0] = 0 370 sgeo[sv0] = 0 - 1 371 var k9: i64 = 0 372 while k9 < nc { 373 if G[G_CONN + k9*2] == G[G_SID + sv0] { 374 let dg9: i64 = G[G_CONN + k9*2 + 1] 375 var g9: i64 = 0 376 while g9 < G[G_GT] { 377 if G[G_GT + 1 + g9*2] == dg9 { 378 sval[sv0] = 1 379 sgeo[sv0] = g9 380 g9 = G[G_GT] 381 k9 = nc 382 } 383 g9 = g9 + 1 384 } 385 } 386 k9 = k9 + 1 387 } 388 sv0 = sv0 + 1 389 } 390 // cluster validity: cluster -> (valid Skin), RECORDING the skin's geometry for base offsets 391 let cval: *i64 = sys_mmap(cc*8 + 64) as *i64 392 let cgeo: *i64 = sys_mmap(cc*8 + 64) as *i64 393 var cv0: i64 = 0 394 while cv0 < cc { 395 cval[cv0] = 0 396 cgeo[cv0] = 0 - 1 397 var k8: i64 = 0 398 while k8 < nc { 399 if G[G_CONN + k8*2] == G[G_CID + cv0] { 400 let d8: i64 = G[G_CONN + k8*2 + 1] 401 var s8: i64 = 0 402 while s8 < G[5] { 403 if G[G_SID + s8] == d8 { if sval[s8] == 1 { 404 cval[cv0] = 1 405 cgeo[cv0] = sgeo[s8] 406 s8 = 64 407 k8 = nc 408 } } 409 s8 = s8 + 1 410 } 411 } 412 k8 = k8 + 1 413 } 414 cv0 = cv0 + 1 415 } 416 // joints = limb models in encounter order 417 let jmap: *i64 = sys_mmap(mc*8 + 64) as *i64 418 var nj: i64 = 0 419 var m0: i64 = 0 420 while m0 < mc { 421 jmap[m0] = 0 - 1 422 if G[G_LO + m0] == 1 { jmap[m0] = nj; nj = nj + 1 } 423 m0 = m0 + 1 424 } 425 // parent per joint via Connections (child -> parent) 426 let jpar: *i64 = sys_mmap(nj*8 + 64) as *i64 427 var j0: i64 = 0 428 while j0 < nj { jpar[j0] = 0 - 1; j0 = j0 + 1 } 429 var m1: i64 = 0 430 while m1 < mc { 431 if jmap[m1] >= 0 { 432 let myid: i64 = G[G_MO + m1] 433 var k: i64 = 0 434 while k < nc { 435 if G[G_CONN + k*2] == myid { 436 let dst: i64 = G[G_CONN + k*2 + 1] 437 var m2: i64 = 0 438 while m2 < mc { 439 if G[G_MO + m2] == dst { if jmap[m2] >= 0 { 440 jpar[jmap[m1]] = jmap[m2] 441 m2 = mc 442 k = nc 443 } } 444 m2 = m2 + 1 445 } 446 } 447 k = k + 1 448 } 449 } 450 m1 = m1 + 1 451 } 452 // cluster -> joint (Model(limb) --conn--> Cluster) + bind translation from TransformLink 453 let cjoint: *i64 = sys_mmap(cc*8 + 64) as *i64 454 let jbind: *i64 = sys_mmap(nj*24 + 64) as *i64 455 let jreal: *i64 = sys_mmap(nj*8 + 64) as *i64 456 var c0: i64 = 0 457 while c0 < cc { cjoint[c0] = 0 - 1; c0 = c0 + 1 } 458 var c1: i64 = 0 459 while c1 < cc { 460 let cid: i64 = G[G_CID + c1] 461 var k2: i64 = 0 462 while k2 < nc { 463 if G[G_CONN + k2*2 + 1] == cid { 464 let src: i64 = G[G_CONN + k2*2] 465 var m3: i64 = 0 466 while m3 < mc { 467 if G[G_MO + m3] == src { if jmap[m3] >= 0 { 468 cjoint[c1] = jmap[m3] 469 m3 = mc 470 k2 = nc 471 } } 472 m3 = m3 + 1 473 } 474 } 475 k2 = k2 + 1 476 } 477 // bind translation: TransformLink col-major elems 12,13,14 (joint->world at bind) 478 if cjoint[c1] >= 0 { if G[G_CT + c1*4 + 1] == 16 { 479 let tl: *u8 = sk_arr(fb, G[G_CT + c1*4], 16, G[G_CT + c1*4 + 2], G[G_CT + c1*4 + 3], 8) 480 if (tl as i64) != 0 { 481 let jj: i64 = cjoint[c1] 482 // LENGTH site: TransformLink bind translation carries the unit factor (identity 483 // at 1.0) -- must scale WITH the mesh or the rig detaches from the skin. 484 jbind[jj*3] = sk_f64(tl, 12*8, 1000) * G[G_USF] / FBXC_MICRO 485 jbind[jj*3+1] = sk_f64(tl, 13*8, 1000) * G[G_USF] / FBXC_MICRO 486 jbind[jj*3+2] = sk_f64(tl, 14*8, 1000) * G[G_USF] / FBXC_MICRO 487 jreal[jj] = 1 488 } 489 } } 490 c1 = c1 + 1 491 } 492 // v2: STATIC FK -> TRUE bind positions for ALL joints, replacing the old placeholder 493 // propagation (which parked unclustered joints on an ancestor; auto-skin against those 494 // degenerate segments mis-bound flesh -- shoulder shards under real per-joint mocap). 495 // Local = PreRotation x LclRotation chained down parents; non-limb ancestor Models 496 // compose into a per-root prefix. VALIDATED against the real cluster binds before use. 497 let mpar: *i64 = sys_mmap(mc*8 + 64) as *i64 498 var mp0: i64 = 0 499 while mp0 < mc { mpar[mp0] = 0 - 1; mp0 = mp0 + 1 } 500 var mp1: i64 = 0 501 while mp1 < mc { 502 let myid2: i64 = G[G_MO + mp1] 503 var kp: i64 = 0 504 while kp < nc { 505 if G[G_CONN + kp*2] == myid2 { 506 let dstp: i64 = G[G_CONN + kp*2 + 1] 507 var mp2: i64 = 0 508 while mp2 < mc { 509 if G[G_MO + mp2] == dstp { mpar[mp1] = mp2; mp2 = mc; kp = nc } 510 mp2 = mp2 + 1 511 } 512 } 513 kp = kp + 1 514 } 515 mp1 = mp1 + 1 516 } 517 let jmi: *i64 = sys_mmap(G_MAGIC_4096*8 + 64) as *i64 518 var mj0: i64 = 0 519 while mj0 < mc { 520 if jmap[mj0] >= 0 { jmi[jmap[mj0]] = mj0 } 521 mj0 = mj0 + 1 522 } 523 let scr: *i64 = sys_mmap(256) as *i64 524 let pq: *i64 = sys_mmap(nj*32 + 64) as *i64 525 let sq: *i64 = sys_mmap(nj*32 + 64) as *i64 526 var jq0: i64 = 0 527 while jq0 < nj { 528 let mi: i64 = jmi[jq0] 529 nf_eul2q(G[G_PR + mi*3], G[G_PR + mi*3 + 1], G[G_PR + mi*3 + 2], ((pq as i64) + jq0*32) as *i64, scr) 530 nf_eul2q(G[G_LR + mi*3], G[G_LR + mi*3 + 1], G[G_LR + mi*3 + 2], ((sq as i64) + jq0*32) as *i64, scr) 531 jq0 = jq0 + 1 532 } 533 let topo: *i64 = sys_mmap(nj*8 + 64) as *i64 534 let done: *i64 = sys_mmap(nj*8 + 64) as *i64 535 var tn: i64 = 0 536 var pass2: i64 = 0 537 while pass2 < nj { 538 if tn < nj { 539 var jp0: i64 = 0 540 while jp0 < nj { 541 if done[jp0] == 0 { 542 var ok: i64 = 0 543 if jpar[jp0] < 0 { ok = 1 } 544 if jpar[jp0] >= 0 { if done[jpar[jp0]] == 1 { ok = 1 } } 545 if ok == 1 { topo[tn] = jp0; tn = tn + 1; done[jp0] = 1 } 546 } 547 jp0 = jp0 + 1 548 } 549 } 550 pass2 = pass2 + 1 551 } 552 if tn != nj { skw("PARENT-CYCLE -- refusing\n" as *u8); return 7 } 553 let gqs: *i64 = sys_mmap(nj*32 + 64) as *i64 554 let gts: *i64 = sys_mmap(nj*24 + 64) as *i64 555 let lq: *i64 = sys_mmap(64) as *i64 556 let rv: *i64 = sys_mmap(64) as *i64 557 let prefq: *i64 = sys_mmap(nj*32 + 64) as *i64 558 let preft: *i64 = sys_mmap(nj*24 + 64) as *i64 559 let anc: *i64 = sys_mmap(32*8 + 64) as *i64 560 let aqq: *i64 = sys_mmap(64) as *i64 561 let bqq: *i64 = sys_mmap(64) as *i64 562 let tq2: *i64 = sys_mmap(64) as *i64 563 var jr0: i64 = 0 564 while jr0 < nj { 565 prefq[jr0*4] = 0 566 prefq[jr0*4+1] = 0 567 prefq[jr0*4+2] = 0 568 prefq[jr0*4+3] = G_MAGIC_4096 569 if jpar[jr0] < 0 { 570 var nanc: i64 = 0 571 var cm: i64 = mpar[jmi[jr0]] 572 var hops: i64 = 0 573 while cm >= 0 { 574 if nanc < 30 { anc[nanc] = cm; nanc = nanc + 1 } 575 cm = mpar[cm] 576 hops = hops + 1 577 if hops > 30 { cm = 0 - 1 } 578 } 579 var pi2: i64 = nanc - 1 580 while pi2 >= 0 { 581 let am: i64 = anc[pi2] 582 nf_eul2q(G[G_PR + am*3], G[G_PR + am*3 + 1], G[G_PR + am*3 + 2], aqq, scr) 583 nf_eul2q(G[G_LR + am*3], G[G_LR + am*3 + 1], G[G_LR + am*3 + 2], bqq, scr) 584 nf_qmul(aqq, bqq, tq2) 585 nf_qnorm(tq2) 586 nf_qrotv(((prefq as i64) + jr0*32) as *i64, G[G_LT + am*3], G[G_LT + am*3 + 1], G[G_LT + am*3 + 2], rv, scr) 587 preft[jr0*3] = preft[jr0*3] + rv[0] 588 preft[jr0*3+1] = preft[jr0*3+1] + rv[1] 589 preft[jr0*3+2] = preft[jr0*3+2] + rv[2] 590 nf_qmul(((prefq as i64) + jr0*32) as *i64, tq2, aqq) 591 nf_qnorm(aqq) 592 prefq[jr0*4] = aqq[0] 593 prefq[jr0*4+1] = aqq[1] 594 prefq[jr0*4+2] = aqq[2] 595 prefq[jr0*4+3] = aqq[3] 596 pi2 = pi2 - 1 597 } 598 } 599 jr0 = jr0 + 1 600 } 601 var ti9: i64 = 0 602 while ti9 < nj { 603 let j9: i64 = topo[ti9] 604 let mi9: i64 = jmi[j9] 605 nf_qmul(((pq as i64) + j9*32) as *i64, ((sq as i64) + j9*32) as *i64, lq) 606 nf_qnorm(lq) 607 let pj9: i64 = jpar[j9] 608 if pj9 < 0 { 609 nf_qmul(((prefq as i64) + j9*32) as *i64, lq, ((gqs as i64) + j9*32) as *i64) 610 nf_qnorm(((gqs as i64) + j9*32) as *i64) 611 nf_qrotv(((prefq as i64) + j9*32) as *i64, G[G_LT + mi9*3], G[G_LT + mi9*3 + 1], G[G_LT + mi9*3 + 2], rv, scr) 612 gts[j9*3] = preft[j9*3] + rv[0] 613 gts[j9*3+1] = preft[j9*3+1] + rv[1] 614 gts[j9*3+2] = preft[j9*3+2] + rv[2] 615 } 616 if pj9 >= 0 { 617 nf_qmul(((gqs as i64) + pj9*32) as *i64, lq, ((gqs as i64) + j9*32) as *i64) 618 nf_qnorm(((gqs as i64) + j9*32) as *i64) 619 nf_qrotv(((gqs as i64) + pj9*32) as *i64, G[G_LT + mi9*3], G[G_LT + mi9*3 + 1], G[G_LT + mi9*3 + 2], rv, scr) 620 gts[j9*3] = gts[pj9*3] + rv[0] 621 gts[j9*3+1] = gts[pj9*3+1] + rv[1] 622 gts[j9*3+2] = gts[pj9*3+2] + rv[2] 623 } 624 ti9 = ti9 + 1 625 } 626 var fkmax: i64 = 0 627 var fkvb: i64 = 0 628 var jv0: i64 = 0 629 while jv0 < nj { 630 if jreal[jv0] == 1 { 631 fkvb = fkvb + 1 632 var a9: i64 = 0 633 while a9 < 3 { 634 var d9: i64 = gts[jv0*3+a9] - jbind[jv0*3+a9] 635 if d9 < 0 { d9 = 0 - d9 } 636 if d9 > fkmax { fkmax = d9 } 637 a9 = a9 + 1 638 } 639 } 640 jv0 = jv0 + 1 641 } 642 skw("fk_validated=" as *u8); skn(fkvb) 643 skw(" fk_maxerr_units=" as *u8); skn(fkmax); skw("\n" as *u8) 644 if fkvb < 8 { skw("TOO-FEW-REAL-BINDS -- refusing FK bind replacement\n" as *u8); return 7 } 645 // FK-vs-cluster divergence DOWNGRADED from refusal to announced-keep (2026-08-23): TransformLink 646 // cluster binds ARE the rig's own declared bind pose; the FK recomposition is a cross-check whose 647 // pre/post-rotation model does not fit every exporter (measured: a Fab donor diverged by 63% of 648 // its own height -- structural, not tolerance noise). When they disagree the DECLARED binds win 649 // and the divergence stays announced above; when they agree, FK replacement proceeds as before. 650 if fkmax > G_MAGIC_20000 { skw("FK-DIVERGES from cluster binds -- KEEPING declared cluster binds; fk replacement skipped\n" as *u8) } 651 if fkmax <= G_MAGIC_20000 { 652 var jb9: i64 = 0 653 while jb9 < nj { 654 jbind[jb9*3] = gts[jb9*3] 655 jbind[jb9*3+1] = gts[jb9*3+1] 656 jbind[jb9*3+2] = gts[jb9*3+2] 657 jb9 = jb9 + 1 658 } 659 } 660 // SKIN accumulation: top-4 (joint, weight q12) per vertex of the main mesh 661 let lpn: *i64 = sys_mmap(16) as *i64 662 let nb: *u8 = sys_map_file(argv[2] as *u8, lpn) 663 let nlen2: i64 = lpn[0] 664 let vwo: i64 = nxa_find(nb, nlen2, nxa_tag4("VERT" as *u8)) 665 if vwo < 0 { skw("in.nxa bad\n" as *u8); return 5 } 666 let two: i64 = nxa_find(nb, nlen2, nxa_tag4("TRIS" as *u8)) 667 if two < 0 { skw("in.nxa bad\n" as *u8); return 5 } 668 let cwo: i64 = nxa_find(nb, nlen2, nxa_tag4("CLUS" as *u8)) 669 let nh: *i64 = nb as *i64 670 let nv: i64 = nh[vwo] 671 let nt: i64 = nh[two] 672 var nclw: i64 = 0 673 if cwo >= 0 { nclw = 1 + nh[cwo]*10 } 674 let j4: *i64 = sys_mmap(nv*32 + 64) as *i64 675 let w4: *i64 = sys_mmap(nv*32 + 64) as *i64 676 let japp: *i64 = sys_mmap(nj*8 + 64) as *i64 677 var cvn: i64 = 0 678 var cvi: i64 = 0 679 var dbg: i64 = 0 680 while dbg < cc { 681 if cval[dbg] == 1 { cvn = cvn + 1; cvi = cvi + G[G_CIX + dbg*4 + 1] } 682 dbg = dbg + 1 683 } 684 skw("valid_clusters=" as *u8); skn(cvn) 685 skw(" their_index_elems=" as *u8); skn(cvi) 686 skw(" gid_vert_alen=" as *u8); skn(G[3]); skw("\n" as *u8) 687 // MULTI-GEOMETRY (2026-08-23): each cluster's Indexes are LOCAL to its own geometry's 688 // vertex space; offset by that geometry's base (prefix sum over the walk table, same 689 // document order nx_fbx_import merges in). DESYNC GUARD: if the table's vertex total 690 // disagrees with the in.nxa's, the pairing is unproven and weights would land on wrong 691 // vertices -- refuse authored-weight application BY NAME (joints still emit, autoskin 692 // covers) rather than stamp a plausible-looking wrong skin. 693 var tblnv: i64 = 0 694 var gtb0: i64 = 0 695 while gtb0 < G[G_GT] { tblnv = tblnv + G[G_GT + 2 + gtb0*2]/3; gtb0 = gtb0 + 1 } 696 var geomsync: i64 = 1 697 if tblnv != nv { geomsync = 0 } 698 skw("geom_table=" as *u8); skn(G[G_GT]) 699 skw(" geom_table_overflow=" as *u8); skn(G[G_GTOV]) 700 skw(" tbl_nv=" as *u8); skn(tblnv) 701 skw(" mesh_nv=" as *u8); skn(nv) 702 skw(" sync=" as *u8); skn(geomsync); skw("\n" as *u8) 703 if geomsync == 0 { skw("GEOM-TABLE-DESYNC -- refusing authored-weight application (autoskin only)\n" as *u8) } 704 let full_records:*i64=sys_mmap((cvi+1)*24) as *i64 705 let full_counts:*i64=sys_mmap(nv*8) as *i64 706 var full_count:i64=0 707 var c2: i64 = 0 708 var applied: i64 = 0 709 while c2 < cc { 710 if geomsync == 1 { if cval[c2] == 1 { if cjoint[c2] >= 0 { if G[G_CIX + c2*4 + 1] > 0 { if G[G_CIX + c2*4 + 1] == G[G_CW + c2*4 + 1] { 711 let ni: i64 = G[G_CIX + c2*4 + 1] 712 let cg: i64 = cgeo[c2] 713 var vb2: i64 = 0 714 var gpre: i64 = 0 715 while gpre < cg { vb2 = vb2 + G[G_GT + 2 + gpre*2]/3; gpre = gpre + 1 } 716 var gnv: i64 = 0 717 if cg >= 0 { gnv = G[G_GT + 2 + cg*2]/3 } 718 let ia: *u8 = sk_arr(fb, G[G_CIX + c2*4], ni, G[G_CIX + c2*4 + 2], G[G_CIX + c2*4 + 3], 4) 719 let wa: *u8 = sk_arr(fb, G[G_CW + c2*4], ni, G[G_CW + c2*4 + 2], G[G_CW + c2*4 + 3], 8) 720 if (ia as i64) != 0 { if (wa as i64) != 0 { 721 let jj2: i64 = cjoint[c2] 722 var k3: i64 = 0 723 while k3 < ni { 724 let vi0: i64 = sk_u32(ia, k3*4) 725 let vi: i64 = vb2 + vi0 726 if vi0 < gnv { if vi < nv { 727 let full_weight:i64=sk_weight_q48(wa,k3*8) 728 if authored==1{ 729 if full_weight<0{return 7} 730 if full_weight>0{ 731 if full_count>=cvi{return 7} 732 full_records[full_count*3]=vi;full_records[full_count*3+1]=jj2;full_records[full_count*3+2]=full_weight 733 full_counts[vi]=full_counts[vi]+1;full_count=full_count+1 734 } 735 } 736 let wq: i64 = sk_f64(wa, k3*8, G_MAGIC_4096) 737 if wq > 0 { 738 // replace the smallest of the vertex's 4 slots if we beat it 739 var mslot: i64 = 0 740 var s: i64 = 1 741 while s < 4 { if w4[vi*4+s] < w4[vi*4+mslot] { mslot = s } s = s + 1 } 742 if wq > w4[vi*4+mslot] { 743 w4[vi*4+mslot] = wq 744 j4[vi*4+mslot] = jj2 745 japp[jj2] = japp[jj2] + 1 746 applied = applied + 1 747 } 748 } 749 } } 750 k3 = k3 + 1 751 } 752 } } 753 } } } } } 754 c2 = c2 + 1 755 } 756 // normalize each vertex to exact sum 4096; unskinned verts -> AUTO-SKIN to the nearest 757 // two clustered joints by inverse distance (authored weights only cover skin sub-regions 758 // in CC exports, and unrigged sculpts have none at all -- auto-fill makes every mesh posable) 759 let vx5: *i64 = ((nb as i64) + vwo*8 + 8) as *i64 760 // auto-skin candidates = ONLY bones that received AUTHORED cluster weight on OUR mesh. 761 // v2 regression caught by the name instrument: FK-true binds gave EYE/TONGUE bones real 762 // positions and nearest-segment auto-skin bound ~1600 FACE verts to an eyeball rotating 763 // 260deg in the mocap -- the "scapula shards" were the face. Organ bones whose clusters 764 // live on foreign meshes (eyes/tongue/teeth) drop out of japp by construction. 765 let jok: *i64 = sys_mmap(nj*8 + 64) as *i64 766 var jo0: i64 = 0 767 while jo0 < nj { 768 jok[jo0] = 0 769 if japp[jo0] > 0 { jok[jo0] = 1 } 770 jo0 = jo0 + 1 771 } 772 var skinned: i64 = 0 773 var autos: i64 = 0 774 var v0: i64 = 0 775 while v0 < nv { 776 let s0: i64 = w4[v0*4] + w4[v0*4+1] + w4[v0*4+2] + w4[v0*4+3] 777 if s0 == 0 { 778 if authored==1{skw("AUTHORED-UNWEIGHTED vertex=" as *u8);skn(v0);skw("\n" as *u8);return 7} 779 var b1: i64 = 0 - 1 780 var b2j: i64 = 0 - 1 781 var d1: i64 = G_MAGIC_4611686018427387903 782 var d2: i64 = d1 783 var jn: i64 = 0 784 while jn < nj { 785 if jok[jn] == 1 { 786 // distance to the BONE SEGMENT (parent->joint), not the joint point -- 787 // point-distance bound mid-limb verts to the wrong bone = walk clipping 788 var ax: i64 = jbind[jn*3] 789 var ay: i64 = jbind[jn*3+1] 790 var az: i64 = jbind[jn*3+2] 791 let pj: i64 = jpar[jn] 792 var bxs: i64 = ax 793 var bys: i64 = ay 794 var bzs: i64 = az 795 if pj >= 0 { if jok[pj] == 1 { 796 bxs = jbind[pj*3] 797 bys = jbind[pj*3+1] 798 bzs = jbind[pj*3+2] 799 } } 800 let abx: i64 = (bxs - ax)/16 801 let aby: i64 = (bys - ay)/16 802 let abz: i64 = (bzs - az)/16 803 let vax: i64 = (vx5[v0*3] - ax)/16 804 let vay: i64 = (vx5[v0*3+1] - ay)/16 805 let vaz: i64 = (vx5[v0*3+2] - az)/16 806 let denom: i64 = abx*abx + aby*aby + abz*abz 807 var tq: i64 = 0 808 if denom > 0 { tq = (vax*abx + vay*aby + vaz*abz)*G_MAGIC_4096/denom } 809 if tq < 0 { tq = 0 } 810 if tq > G_MAGIC_4096 { tq = G_MAGIC_4096 } 811 let dx: i64 = vax - abx*tq/G_MAGIC_4096 812 let dy: i64 = vay - aby*tq/G_MAGIC_4096 813 let dz: i64 = vaz - abz*tq/G_MAGIC_4096 814 let dd: i64 = dx*dx + dy*dy + dz*dz 815 if dd < d1 { d2 = d1; b2j = b1; d1 = dd; b1 = jn } 816 if dd >= d1 { if dd < d2 { d2 = dd; b2j = jn } } 817 } 818 jn = jn + 1 819 } 820 if b1 >= 0 { 821 if b2j >= 0 { 822 // inverse-distance split over the two nearest bones (d in squared space) 823 let wsum: i64 = d1 + d2 824 var wa: i64 = G_MAGIC_4096*d2/wsum 825 if wa > G_MAGIC_4096 { wa = G_MAGIC_4096 } 826 j4[v0*4] = b1 827 w4[v0*4] = wa 828 j4[v0*4+1] = b2j 829 w4[v0*4+1] = G_MAGIC_4096 - wa 830 } 831 if b2j < 0 { 832 j4[v0*4] = b1 833 w4[v0*4] = G_MAGIC_4096 834 } 835 autos = autos + 1 836 } 837 if b1 < 0 { 838 j4[v0*4] = 0 839 w4[v0*4] = G_MAGIC_4096 840 } 841 } 842 if s0 > 0 { 843 skinned = skinned + 1 844 var acc: i64 = 0 845 var s1: i64 = 0 846 while s1 < 3 { 847 let nw: i64 = w4[v0*4+s1]*G_MAGIC_4096/s0 848 w4[v0*4+s1] = nw 849 acc = acc + nw 850 s1 = s1 + 1 851 } 852 w4[v0*4+3] = G_MAGIC_4096 - acc 853 } 854 v0 = v0 + 1 855 } 856 // WEIGHT SMOOTHING (2 Laplacian passes over the mesh graph): the stretch instrument 857 // measured hard 100pct-to-100pct weight steps tearing under real per-joint motion -- 858 // armpit Spine02<>UpperarmTwist01 128 torn edges, hip crease Pelvis<>ThighTwist01 118, 859 // shoulder top <>Clavicle 112. 60/40 own/neighbor blend, top-4 retruncate, sum=4096. 860 let tr5: *i64 = ((nb as i64) + two*8 + 8) as *i64 861 let deg: *i64 = sys_mmap(nv*8 + 64) as *i64 862 var te0: i64 = 0 863 while te0 < nt { 864 deg[tr5[te0*3]] = deg[tr5[te0*3]] + 2 865 deg[tr5[te0*3+1]] = deg[tr5[te0*3+1]] + 2 866 deg[tr5[te0*3+2]] = deg[tr5[te0*3+2]] + 2 867 te0 = te0 + 1 868 } 869 let adjo: *i64 = sys_mmap(nv*8 + 72) as *i64 870 var ao0: i64 = 0 871 var acc0: i64 = 0 872 while ao0 < nv { adjo[ao0] = acc0; acc0 = acc0 + deg[ao0]; ao0 = ao0 + 1 } 873 adjo[nv] = acc0 874 let adjl: *i64 = sys_mmap(acc0*8 + 64) as *i64 875 let cur: *i64 = sys_mmap(nv*8 + 64) as *i64 876 var te1: i64 = 0 877 while te1 < nt { 878 var ec0: i64 = 0 879 while ec0 < 3 { 880 let a5: i64 = tr5[te1*3 + ec0] 881 var b5: i64 = tr5[te1*3] 882 var c5: i64 = tr5[te1*3 + 1] 883 if ec0 == 0 { b5 = tr5[te1*3+1]; c5 = tr5[te1*3+2] } 884 if ec0 == 1 { b5 = tr5[te1*3]; c5 = tr5[te1*3+2] } 885 if ec0 == 2 { b5 = tr5[te1*3]; c5 = tr5[te1*3+1] } 886 adjl[adjo[a5] + cur[a5]] = b5 887 cur[a5] = cur[a5] + 1 888 adjl[adjo[a5] + cur[a5]] = c5 889 cur[a5] = cur[a5] + 1 890 ec0 = ec0 + 1 891 } 892 te1 = te1 + 1 893 } 894 let j4b: *i64 = sys_mmap(nv*32 + 64) as *i64 895 let w4b: *i64 = sys_mmap(nv*32 + 64) as *i64 896 let mj: *i64 = sys_mmap(64*8 + 64) as *i64 897 let mw: *i64 = sys_mmap(64*8 + 64) as *i64 898 var it0: i64 = 0 899 while it0 < 3 && authored == 0 { 900 var vs0: i64 = 0 901 while vs0 < nv { 902 let dv5: i64 = deg[vs0] 903 if dv5 == 0 { 904 var sc0: i64 = 0 905 while sc0 < 4 { j4b[vs0*4+sc0] = j4[vs0*4+sc0]; w4b[vs0*4+sc0] = w4[vs0*4+sc0]; sc0 = sc0 + 1 } 906 } 907 if dv5 > 0 { 908 var mn: i64 = 0 909 var so0: i64 = 0 910 while so0 < 4 { 911 if w4[vs0*4+so0] > 0 { 912 let jj5: i64 = j4[vs0*4+so0] 913 let ww5: i64 = w4[vs0*4+so0]*6*dv5 914 var f0: i64 = 0 - 1 915 var m0s: i64 = 0 916 while m0s < mn { if mj[m0s] == jj5 { f0 = m0s; m0s = mn } m0s = m0s + 1 } 917 if f0 >= 0 { mw[f0] = mw[f0] + ww5 } 918 if f0 < 0 { if mn < 60 { mj[mn] = jj5; mw[mn] = ww5; mn = mn + 1 } } 919 } 920 so0 = so0 + 1 921 } 922 var nb0: i64 = 0 923 while nb0 < dv5 { 924 let vn5: i64 = adjl[adjo[vs0] + nb0] 925 var sn0: i64 = 0 926 while sn0 < 4 { 927 if w4[vn5*4+sn0] > 0 { 928 let jj6: i64 = j4[vn5*4+sn0] 929 let ww6: i64 = w4[vn5*4+sn0]*4 930 var f1: i64 = 0 - 1 931 var m1s: i64 = 0 932 while m1s < mn { if mj[m1s] == jj6 { f1 = m1s; m1s = mn } m1s = m1s + 1 } 933 if f1 >= 0 { mw[f1] = mw[f1] + ww6 } 934 if f1 < 0 { if mn < 60 { mj[mn] = jj6; mw[mn] = ww6; mn = mn + 1 } } 935 } 936 sn0 = sn0 + 1 937 } 938 nb0 = nb0 + 1 939 } 940 // top-4 of the merged map -> renormalize to exact 4096 941 var sl0: i64 = 0 942 while sl0 < 4 { 943 var bi5: i64 = 0 - 1 944 var bw5: i64 = 0 945 var m2s: i64 = 0 946 while m2s < mn { 947 if mw[m2s] > bw5 { bw5 = mw[m2s]; bi5 = m2s } 948 m2s = m2s + 1 949 } 950 if bi5 >= 0 { 951 j4b[vs0*4+sl0] = mj[bi5] 952 w4b[vs0*4+sl0] = mw[bi5] 953 mw[bi5] = 0 954 } 955 if bi5 < 0 { j4b[vs0*4+sl0] = 0; w4b[vs0*4+sl0] = 0 } 956 sl0 = sl0 + 1 957 } 958 let ts5: i64 = w4b[vs0*4] + w4b[vs0*4+1] + w4b[vs0*4+2] + w4b[vs0*4+3] 959 if ts5 > 0 { 960 var an5: i64 = 0 961 var sr0: i64 = 0 962 while sr0 < 3 { 963 let nw5: i64 = w4b[vs0*4+sr0]*G_MAGIC_4096/ts5 964 w4b[vs0*4+sr0] = nw5 965 an5 = an5 + nw5 966 sr0 = sr0 + 1 967 } 968 w4b[vs0*4+3] = G_MAGIC_4096 - an5 969 } 970 if ts5 == 0 { j4b[vs0*4] = 0; w4b[vs0*4] = G_MAGIC_4096; w4b[vs0*4+1] = 0; w4b[vs0*4+2] = 0; w4b[vs0*4+3] = 0 } 971 } 972 vs0 = vs0 + 1 973 } 974 var cp0: i64 = 0 975 while cp0 < nv*4 { j4[cp0] = j4b[cp0]; w4[cp0] = w4b[cp0]; cp0 = cp0 + 1 } 976 it0 = it0 + 1 977 } 978 if authored==1{skw("weights_mode=AUTHORED top4_q12_only smoothing=0 autoskin=REFUSE\n" as *u8)}else{skw("weights_smoothed=3passes\n" as *u8)} 979 skw("joints=" as *u8); skn(nj) 980 skw(" skinned=" as *u8); skn(skinned) 981 skw(" autoskinned=" as *u8); skn(autos) 982 skw("/" as *u8); skn(nv) 983 skw(" applied=" as *u8); skn(applied); skw("\n" as *u8) 984 // SKV1 is additive: retain every positive source influence with Q48 quantization declared. 985 if authored==1{ 986 sk_full_words=4+nv+1+full_count*2 987 sk_full_payload=sys_mmap(sk_full_words*8) as *i64 988 sk_full_payload[0]=1;sk_full_payload[1]=nv;sk_full_payload[2]=full_count;sk_full_payload[3]=281474976710656 989 let cursor:*i64=sys_mmap(nv*8) as *i64 990 var v:i64=0;var offset:i64=0 991 while v<nv{if full_counts[v]<=0{return 7};sk_full_payload[4+v]=offset;cursor[v]=offset;offset=offset+full_counts[v];v=v+1} 992 sk_full_payload[4+nv]=offset 993 var k:i64=0;let base:i64=4+nv+1 994 while k<full_count{v=full_records[k*3];let at:i64=base+cursor[v]*2;sk_full_payload[at]=full_records[k*3+1];sk_full_payload[at+1]=full_records[k*3+2];cursor[v]=cursor[v]+1;k=k+1} 995 sys_munmap(cursor as *u8,nv*8) 996 } 997 sys_munmap(full_records as *u8,(cvi+1)*24);sys_munmap(full_counts as *u8,nv*8) 998 // build SKEL + SKIN payloads 999 let skel: *i64 = sys_mmap((1 + nj*8)*8 + 64) as *i64 1000 skel[0] = nj 1001 var j1: i64 = 0 1002 while j1 < nj { 1003 skel[1 + j1*8] = jpar[j1] 1004 skel[1 + j1*8 + 1] = jbind[j1*3] 1005 skel[1 + j1*8 + 2] = jbind[j1*3 + 1] 1006 skel[1 + j1*8 + 3] = jbind[j1*3 + 2] 1007 skel[1 + j1*8 + 4] = 0 1008 skel[1 + j1*8 + 5] = 0 1009 skel[1 + j1*8 + 6] = 0 1010 skel[1 + j1*8 + 7] = G_MAGIC_4096 1011 j1 = j1 + 1 1012 } 1013 let skin: *i64 = sys_mmap((1 + nv*8)*8 + 64) as *i64 1014 skin[0] = nv 1015 var v1: i64 = 0 1016 while v1 < nv { 1017 var s2: i64 = 0 1018 while s2 < 4 { 1019 skin[1 + v1*8 + s2] = j4[v1*4 + s2] 1020 skin[1 + v1*8 + 4 + s2] = w4[v1*4 + s2] 1021 s2 = s2 + 1 1022 } 1023 v1 = v1 + 1 1024 } 1025 // emit the 5-section NXA 1026 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4) 1027 if fd < 0 { skw("open out failed\n" as *u8); return 9 } 1028 let vwl: i64 = 1 + nv*3 1029 let twl: i64 = 1 + nt*3 1030 let swl: i64 = 1 + nj*8 1031 let kwl: i64 = 1 + nv*8 1032 var ns: i64 = 4 1033 if nclw > 0 { ns = 5 } 1034 let hdr: *i64 = sys_mmap(64) as *i64 1035 let toc: *i64 = sys_mmap(512) as *i64 1036 var o: i64 = 32 + ns*32 1037 var ti: i64 = 0 1038 toc[ti*4] = nxa_tag4("VERT" as *u8) 1039 toc[ti*4+1] = o 1040 toc[ti*4+2] = vwl 1041 toc[ti*4+3] = nxa_check2(1, ((nb as i64) + vwo*8) as *i64, vwl) 1042 o = o + vwl*8 1043 ti = ti + 1 1044 toc[ti*4] = nxa_tag4("TRIS" as *u8) 1045 toc[ti*4+1] = o 1046 toc[ti*4+2] = twl 1047 toc[ti*4+3] = nxa_check2(1, ((nb as i64) + two*8) as *i64, twl) 1048 o = o + twl*8 1049 ti = ti + 1 1050 if nclw > 0 { 1051 toc[ti*4] = nxa_tag4("CLUS" as *u8) 1052 toc[ti*4+1] = o 1053 toc[ti*4+2] = nclw 1054 toc[ti*4+3] = nxa_check2(1, ((nb as i64) + cwo*8) as *i64, nclw) 1055 o = o + nclw*8 1056 ti = ti + 1 1057 } 1058 toc[ti*4] = nxa_tag4("SKEL" as *u8) 1059 toc[ti*4+1] = o 1060 toc[ti*4+2] = swl 1061 toc[ti*4+3] = nxa_check2(1, skel, swl) 1062 o = o + swl*8 1063 ti = ti + 1 1064 toc[ti*4] = nxa_tag4("SKIN" as *u8) 1065 toc[ti*4+1] = o 1066 toc[ti*4+2] = kwl 1067 toc[ti*4+3] = nxa_check2(1, skin, kwl) 1068 hdr[0] = nxa_magic() 1069 hdr[1] = NXA_VER 1070 hdr[2] = ns 1071 hdr[3] = nxa_check2(1, toc, ns*4) 1072 sys_write(fd, hdr as *u8, 32) 1073 sys_write(fd, toc as *u8, ns*32) 1074 sys_write(fd, ((nb as i64) + vwo*8) as *u8, vwl*8) 1075 sys_write(fd, ((nb as i64) + two*8) as *u8, twl*8) 1076 if nclw > 0 { sys_write(fd, ((nb as i64) + cwo*8) as *u8, nclw*8) } 1077 sys_write(fd, skel as *u8, swl*8) 1078 sys_write(fd, skin as *u8, kwl*8) 1079 sys_close(fd) 1080 skw("NXA5 written joints=" as *u8); skn(nj) 1081 skw(" skin_verts=" as *u8); skn(nv); skw("\n" as *u8) 1082 return 0 1083}