code wiki / (root) / nx_nxa_anim.nx

nx_nxa_anim.nx source

↩ module page · 1479 lines · 66141 B

1// nx_nxa_anim.nx -- ANIMATION IMPORTER v2: extracts the FBX's authored animation and BAKES it 2// to the NXA's normative delta-LBS convention. v1 wrote raw Lcl-space Euler quats (channel 1) 3// which are NOT drivable without the FBX local hierarchy (pre-rotations, parent chain); v2 4// evaluates full integer FK per 30Hz sample -- local = PreRotation x LclRotation(t) chained 5// down the joint parents -- and emits per-joint WORLD-SPACE deltas about the bind pose 6// (channel 2, packed 2-word keys), the exact form the LBS viewer consumes: M x = D(x-b)+b+dt. 7// SELF-VALIDATING: the static FK must reproduce the SKEL bind translations (TransformLink 8// ground truth) or the tool REFUSES (exit 7) -- pre-rotation/hierarchy math is proven against 9// data already trusted, never assumed. 10// usage: nx_nxa_anim <in.fbx> <in.nxa5> <out.nxa> 11// rc: 0 ok, 2 usage, 3 fbx unreadable, 4 no animation, 5 in.nxa bad, 7 rig-mismatch, 9 io 12// license_tier: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_zlib_wrap.nx" 15import "nx_nxa.nx" 16import "nx_itrig.nx" 17import "nx_nxa_fk.nx" 18const A_MAGIC_1500: i64 = 1500 19const A_MAGIC_2047: i64 = 2047 20const A_MAGIC_1048575: i64 = 1048575 21const A_MAGIC_4503599627370496: i64 = 4503599627370496 22const A_MAGIC_8388607: i64 = 8388607 23const A_MAGIC_8388608: i64 = 8388608 24const A_MAGIC_4090: i64 = 4090 25const A_MAGIC_2040: i64 = 2040 26const A_MAGIC_119990: i64 = 119990 27const A_MAGIC_4096: i64 = 4096 28const A_MAGIC_46186158: i64 = 46186158 29const A_MAGIC_180000: i64 = 180000 30const A_MAGIC_360000: i64 = 360000 31const A_MAGIC_7500: i64 = 7500 32const A_MAGIC_500000: i64 = 500000 33const A_MAGIC_60000: i64 = 60000 34const A_MAGIC_20000: i64 = 20000 35const A_MAGIC_4611686018427387903: i64 = 4611686018427387903 36const A_MAGIC_2000: i64 = 2000 37const A_MAGIC_200000: i64 = 200000 38const A_MAGIC_32767: i64 = 32767 39const A_MAGIC_65535: i64 = 65535 40const A_MAGIC_1024: i64 = 1024 41 42// G layout: 0=mcount 1=curvecount 2=conncount 3=anodecount 43const A_MO: i64 = 16 // model ids x4096 44const A_LO: i64 = 4112 // limb flags x4096 45const A_AN: i64 = 8208 // AnimationCurveNode ids x2048 46const A_CID: i64 = 10256 // curve ids x2048 47const A_CKT: i64 = 12304 // curve KeyTime meta (off,alen,enc,clen) x2048x4 48const A_CKV: i64 = 20496 // curve KeyValueFloat meta x2048x4 49const A_CONN: i64 = 28688 // conns (src,dst,ptag) x120000x3 50const A_PR: i64 = 388688 // per-model PreRotation millideg x3 51const A_LR: i64 = 400976 // per-model Lcl Rotation default millideg x3 52const A_LT: i64 = 413264 // per-model Lcl Translation default units x3 53const A_PF: i64 = 425552 // per-model flags: 1=pre 2=lclrot 4=lclt 54const A_NO: i64 = 430000 // per-model name byte offset (into the mapped fbx) 55const A_NL: i64 = 434096 // per-model name length 56const A_WORDS: i64 = 440000 57 58func aw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 59func an(v: i64) -> i64 { 60 let t: *u8 = sys_mmap(32) as *u8 61 var m: i64 = v; var w: i64 = 0 62 if m<0 { t[w]=45 as u8; w=w+1; m=0-m } 63 if m==0 { t[0]=48 as u8; sys_write(1,t,1); return 0 } 64 let d: *u8 = sys_mmap(32) as *u8 65 var k: i64=0 66 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 67 var j: i64=0 68 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 } 69 sys_write(1,t,w); return 0 70} 71func a_u32(b: *u8, o: i64) -> i64 { 72 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8) 73 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24) 74} 75func a_u64(b: *u8, o: i64) -> i64 { return a_u32(b, o) | (a_u32(b, o + 4) << 32) } 76// f64 -> value x scale (DRY debt: shared nx_f64.nx when the next user appears) 77func a_f64(b: *u8, o: i64, scale: i64) -> i64 { 78 let lo: i64 = a_u32(b, o) 79 let hi: i64 = a_u32(b, o + 4) 80 let sign: i64 = (hi >> 31) & 1 81 let expo: i64 = (hi >> 20) & A_MAGIC_2047 82 if expo == 0 { return 0 } 83 var mant: i64 = ((hi & A_MAGIC_1048575) << 32) | lo 84 mant = mant | A_MAGIC_4503599627370496 85 let sh: i64 = expo - 1023 86 var v: i64 = 0 87 if sh >= 52 { v = mant * scale * (1 << (sh - 52)) } 88 if sh < 52 { 89 let rs: i64 = 52 - sh 90 if rs > 62 { return 0 } 91 v = (mant * scale) >> rs 92 } 93 if sign == 1 { return 0 - v } 94 return v 95} 96func a_f32(b: *u8, o: i64, scale: i64) -> i64 { 97 let w: i64 = a_u32(b, o) 98 let sign: i64 = (w >> 31) & 1 99 let expo: i64 = (w >> 23) & 255 100 if expo == 0 { return 0 } 101 var mant: i64 = (w & A_MAGIC_8388607) | A_MAGIC_8388608 102 let sh: i64 = expo - 127 103 var v: i64 = 0 104 if sh >= 23 { v = mant * scale * (1 << (sh - 23)) } 105 if sh < 23 { v = (mant * scale) >> (23 - sh) } 106 if sign == 1 { return 0 - v } 107 return v 108} 109 110// walk capturing Models(+limb), AnimationCurveNodes, AnimationCurves(+KeyTime/KeyValueFloat 111// children), C connections WITH a property tag, and Properties70 P entries under Models 112// (PreRotation / Lcl Rotation / Lcl Translation defaults -- the FK inputs) 113func a_walk(b: *u8, flen: i64, off0: i64, big: i64, G: *i64, cctx: i64, mctx: i64) -> i64 { 114 let off: i64 = off0 115 var endo: i64 = 0 116 var nprops: i64 = 0 117 var plen: i64 = 0 118 var nlen: i64 = 0 119 var p: i64 = 0 120 if big == 1 { 121 endo = a_u64(b, off) 122 nprops = a_u64(b, off + 8) 123 plen = a_u64(b, off + 16) 124 nlen = (b[off + 24] & 0xff) as i64 125 p = off + 25 126 } 127 if big == 0 { 128 endo = a_u32(b, off) 129 nprops = a_u32(b, off + 4) 130 plen = a_u32(b, off + 8) 131 nlen = (b[off + 12] & 0xff) as i64 132 p = off + 13 133 } 134 if endo == 0 { return off } 135 if endo > flen { return flen } 136 var kind: i64 = 0 137 if nlen == 5 { if (b[p]&0xff)==77 { if (b[p+4]&0xff)==108 { kind = 1 } } } // Model 138 if nlen == 18 { if (b[p]&0xff)==65 { if (b[p+17]&0xff)==101 { kind = 2 } } } // AnimationCurveNode 139 if nlen == 14 { if (b[p]&0xff)==65 { if (b[p+13]&0xff)==101 { kind = 3 } } } // AnimationCurve 140 if nlen == 7 { if (b[p]&0xff)==75 { if (b[p+6]&0xff)==101 { kind = 4 } } } // KeyTime 141 if nlen == 13 { if (b[p]&0xff)==75 { if (b[p+12]&0xff)==116 { kind = 5 } } } // KeyValueFloat 142 if nlen == 1 { if (b[p]&0xff)==67 { kind = 6 } } // C 143 if nlen == 1 { if (b[p]&0xff)==80 { kind = 7 } } // P (Properties70) 144 p = p + nlen 145 let pstart: i64 = p 146 var id1: i64 = 0 147 var id2: i64 = 0 148 var lseen: i64 = 0 149 var s1l: i64 = 0 150 var s1o: i64 = 0 151 var s2l: i64 = 0 152 var s2o: i64 = 0 153 var sseen: i64 = 0 154 var arro: i64 = 0 155 var arrl: i64 = 0 156 var arre: i64 = 0 157 var arrc: i64 = 0 158 var dseen: i64 = 0 159 var dv1: i64 = 0 160 var dv2: i64 = 0 161 var dv3: i64 = 0 162 var pi: i64 = 0 163 var stop: i64 = 0 164 while pi < nprops { 165 if stop == 0 { 166 let t: i64 = (b[p] & 0xff) as i64 167 p = p + 1 168 var adv: i64 = 0 - 1 169 if t == 83 { 170 let l: i64 = a_u32(b, p) 171 sseen = sseen + 1 172 if sseen == 1 { s1o = p + 4; s1l = l } 173 if sseen == 2 { s2o = p + 4; s2l = l } 174 adv = 4 + l 175 } 176 if t == 82 { adv = 4 + a_u32(b, p) } 177 if t == 89 { adv = 2 } 178 if t == 67 { adv = 1 } 179 if t == 73 { adv = 4 } 180 if t == 70 { adv = 4 } 181 if t == 68 { 182 if dseen == 0 { dv1 = p } 183 if dseen == 1 { dv2 = p } 184 if dseen == 2 { dv3 = p } 185 dseen = dseen + 1 186 adv = 8 187 } 188 if t == 76 { 189 lseen = lseen + 1 190 if lseen == 1 { id1 = a_u64(b, p) } 191 if lseen == 2 { id2 = a_u64(b, p) } 192 adv = 8 193 } 194 var isarr: i64 = 0 195 if t == 100 { isarr = 1 } 196 if t == 102 { isarr = 2 } 197 if t == 108 { isarr = 3 } 198 if t == 105 { isarr = 3 } 199 if t == 98 { isarr = 3 } 200 if isarr > 0 { 201 arrl = a_u32(b, p) 202 arre = a_u32(b, p + 4) 203 arrc = a_u32(b, p + 8) 204 arro = p + 12 205 adv = 12 + arrc 206 } 207 if adv < 0 { stop = 1 } 208 if adv >= 0 { p = p + adv } 209 } 210 pi = pi + 1 211 } 212 var childc: i64 = cctx 213 var childm: i64 = mctx 214 if kind == 1 { if G[0] < A_MAGIC_4090 { 215 G[A_MO + G[0]] = id1 216 var lf: i64 = 0 217 if s2l == 8 { if (b[s2o]&0xff)==76 { if (b[s2o+7]&0xff)==101 { lf = 1 } } } 218 G[A_LO + G[0]] = lf 219 G[A_NO + G[0]] = s1o 220 G[A_NL + G[0]] = s1l 221 childm = G[0] 222 G[0] = G[0] + 1 223 } } 224 if kind == 2 { if G[3] < A_MAGIC_2040 { G[A_AN + G[3]] = id1; G[3] = G[3] + 1 } } 225 if kind == 3 { if G[1] < A_MAGIC_2040 { 226 G[A_CID + G[1]] = id1 227 childc = G[1] 228 G[1] = G[1] + 1 229 } } 230 if kind == 4 { if cctx >= 0 { 231 G[A_CKT + cctx*4] = arro 232 G[A_CKT + cctx*4 + 1] = arrl 233 G[A_CKT + cctx*4 + 2] = arre 234 G[A_CKT + cctx*4 + 3] = arrc 235 } } 236 if kind == 5 { if cctx >= 0 { 237 G[A_CKV + cctx*4] = arro 238 G[A_CKV + cctx*4 + 1] = arrl 239 G[A_CKV + cctx*4 + 2] = arre 240 G[A_CKV + cctx*4 + 3] = arrc 241 } } 242 if kind == 6 { if G[2] < A_MAGIC_119990 { 243 var ptag: i64 = 0 244 if s2l > 0 { ptag = s2l*256 + ((b[s2o + s2l - 1] & 0xff) as i64) } 245 G[A_CONN + G[2]*3] = id1 246 G[A_CONN + G[2]*3 + 1] = id2 247 G[A_CONN + G[2]*3 + 2] = ptag 248 G[2] = G[2] + 1 249 } } 250 // Properties70 P entry under a Model: name = FIRST S prop; vector value = 3 D props. 251 // Matched by (len, first, last): PreRotation(11 P..n), Lcl Rotation(12 L..n), 252 // Lcl Translation(15 L..n). Angles degrees->millideg, translation units x1000 (both x1000). 253 if kind == 7 { if mctx >= 0 { if dseen >= 3 { 254 var pk: i64 = 0 255 if s1l == 11 { if (b[s1o]&0xff)==80 { if (b[s1o+10]&0xff)==110 { pk = 1 } } } 256 if s1l == 12 { if (b[s1o]&0xff)==76 { if (b[s1o+11]&0xff)==110 { pk = 2 } } } 257 if s1l == 15 { if (b[s1o]&0xff)==76 { if (b[s1o+14]&0xff)==110 { pk = 3 } } } 258 if pk == 1 { 259 G[A_PR + mctx*3] = a_f64(b, dv1, 1000) 260 G[A_PR + mctx*3 + 1] = a_f64(b, dv2, 1000) 261 G[A_PR + mctx*3 + 2] = a_f64(b, dv3, 1000) 262 G[A_PF + mctx] = G[A_PF + mctx] | 1 263 } 264 if pk == 2 { 265 G[A_LR + mctx*3] = a_f64(b, dv1, 1000) 266 G[A_LR + mctx*3 + 1] = a_f64(b, dv2, 1000) 267 G[A_LR + mctx*3 + 2] = a_f64(b, dv3, 1000) 268 G[A_PF + mctx] = G[A_PF + mctx] | 2 269 } 270 if pk == 3 { 271 G[A_LT + mctx*3] = a_f64(b, dv1, 1000) 272 G[A_LT + mctx*3 + 1] = a_f64(b, dv2, 1000) 273 G[A_LT + mctx*3 + 2] = a_f64(b, dv3, 1000) 274 G[A_PF + mctx] = G[A_PF + mctx] | 4 275 } 276 } } } 277 p = pstart + plen 278 var sent: i64 = 13 279 if big == 1 { sent = 25 } 280 while p < endo - sent { 281 p = a_walk(b, flen, p, big, G, childc, childm) 282 if p >= flen { return flen } 283 } 284 return endo 285} 286 287func a_arr(b: *u8, off: i64, alen: i64, enc: i64, clen: i64, esz: i64) -> *u8 { 288 if enc == 0 { return ((b as i64) + off) as *u8 } 289 let zr: *NxZlibResult = nx_zlib_inflate(((b as i64) + off) as *u8, clen, alen*esz + A_MAGIC_4096) 290 if zr.error_code != 0 { return 0 as *u8 } 291 return zr.output_data 292} 293// sample curve (KeyTime l-array ticks, KeyValueFloat f-array) at t_ms -> value x1000 294func a_sample(kt: *u8, kv: *u8, nk: i64, tms: i64) -> i64 { 295 if nk < 1 { return 0 } 296 let t0: i64 = a_u64(kt, 0) / A_MAGIC_46186158 297 if tms <= t0 { return a_f32(kv, 0, 1000) } 298 let tl: i64 = a_u64(kt, (nk-1)*8) / A_MAGIC_46186158 299 if tms >= tl { return a_f32(kv, (nk-1)*4, 1000) } 300 var i: i64 = 1 301 while i < nk { 302 let ti: i64 = a_u64(kt, i*8) / A_MAGIC_46186158 303 if ti >= tms { 304 let tp: i64 = a_u64(kt, (i-1)*8) / A_MAGIC_46186158 305 let vp: i64 = a_f32(kv, (i-1)*4, 1000) 306 let vi: i64 = a_f32(kv, i*4, 1000) 307 if ti == tp { return vi } 308 return vp + (vi - vp)*(tms - tp)/(ti - tp) 309 } 310 i = i + 1 311 } 312 return a_f32(kv, (nk-1)*4, 1000) 313} 314// angular variant: SHORTEST-PATH lerp (mocap Euler channels cross the +-180deg seam; 315// a plain value lerp swings through the wrong side = frame-local limb shards, measured 316// on forearm-twist/shoulder channels of the CC walk) 317func a_sample_ang(kt: *u8, kv: *u8, nk: i64, tms: i64) -> i64 { 318 if nk < 1 { return 0 } 319 let t0: i64 = a_u64(kt, 0) / A_MAGIC_46186158 320 if tms <= t0 { return a_f32(kv, 0, 1000) } 321 let tl: i64 = a_u64(kt, (nk-1)*8) / A_MAGIC_46186158 322 if tms >= tl { return a_f32(kv, (nk-1)*4, 1000) } 323 var i: i64 = 1 324 while i < nk { 325 let ti: i64 = a_u64(kt, i*8) / A_MAGIC_46186158 326 if ti >= tms { 327 let tp: i64 = a_u64(kt, (i-1)*8) / A_MAGIC_46186158 328 let vp: i64 = a_f32(kv, (i-1)*4, 1000) 329 let vi: i64 = a_f32(kv, i*4, 1000) 330 if ti == tp { return vi } 331 var dv: i64 = vi - vp 332 while dv > A_MAGIC_180000 { dv = dv - A_MAGIC_360000 } 333 while dv < 0 - A_MAGIC_180000 { dv = dv + A_MAGIC_360000 } 334 return vp + dv*(tms - tp)/(ti - tp) 335 } 336 i = i + 1 337 } 338 return a_f32(kv, (nk-1)*4, 1000) 339} 340// quat/FK primitives live in nx_nxa_fk.nx (nf_*), shared with nx_nxa_skin (DRY rule-15) 341 342// print a Model name ("Name\x00\x01Class" form -- stop at the 0x00) for diagnostics 343func a_wname(b: *u8, off: i64, len: i64) -> i64 { 344 var n: i64 = 0 345 while n < len { 346 if (b[off+n] & 0xff) == 0 { len = n } 347 if n < len { n = n + 1 } 348 } 349 if n > 48 { n = 48 } 350 sys_write(1, ((b as i64) + off) as *u8, n) 351 return 0 352} 353 354// does a Model name contain the ASCII pattern? (foot/toe identification for heel-walk 355// flattening -- names exist in the FBX even though SKEL stays nameless by design) 356func a_namehas(b: *u8, off: i64, len: i64, pat: *u8) -> i64 { 357 var pl: i64 = 0 358 while pat[pl] != (0 as u8) { pl = pl + 1 } 359 if pl == 0 { return 0 } 360 var i: i64 = 0 361 while i + pl <= len { 362 var k: i64 = 0 363 var hit: i64 = 1 364 while k < pl { 365 if (b[off+i+k] & 0xff) != (pat[k] & 0xff) { hit = 0; k = pl } 366 k = k + 1 367 } 368 if hit == 1 { return 1 } 369 i = i + 1 370 } 371 return 0 372} 373 374func main(argc: i64, argv: *i64) -> i64 { 375 if argc < 4 { aw("usage: nx_nxa_anim <in.fbx> <in.nxa5> <out.nxa> [--heels]\n" as *u8); return 2 } 376 let lpf: *i64 = sys_mmap(16) as *i64 377 let fb: *u8 = sys_map_file(argv[1] as *u8, lpf) 378 let flen: i64 = lpf[0] 379 if flen < 64 { aw("fbx unreadable\n" as *u8); return 3 } 380 let ver: i64 = a_u32(fb, 23) 381 var big: i64 = 0 382 if ver >= A_MAGIC_7500 { big = 1 } 383 let G: *i64 = sys_mmap(A_WORDS*8) as *i64 384 var pos: i64 = 27 385 var guard: i64 = 0 386 while pos < flen - 200 { 387 if guard > A_MAGIC_500000 { break } 388 let e: i64 = a_walk(fb, flen, pos, big, G, 0 - 1, 0 - 1) 389 if e <= pos { break } 390 pos = e 391 guard = guard + 1 392 } 393 aw("models=" as *u8); an(G[0]) 394 aw(" curves=" as *u8); an(G[1]) 395 aw(" curvenodes=" as *u8); an(G[3]) 396 aw(" conns=" as *u8); an(G[2]); aw("\n" as *u8) 397 // joint indices EXACTLY as nx_nxa_skin assigns them (limb models in encounter order) 398 let mc: i64 = G[0] 399 let jmap: *i64 = sys_mmap(mc*8 + 64) as *i64 400 let jmi: *i64 = sys_mmap(A_MAGIC_4096*8 + 64) as *i64 401 var nj: i64 = 0 402 var m0: i64 = 0 403 while m0 < mc { 404 jmap[m0] = 0 - 1 405 if G[A_LO + m0] == 1 { jmap[m0] = nj; jmi[nj] = m0; nj = nj + 1 } 406 m0 = m0 + 1 407 } 408 // parent per joint via Connections (child model -> parent model), skin-identical 409 let jpar: *i64 = sys_mmap(nj*8 + 64) as *i64 410 var j0: i64 = 0 411 while j0 < nj { jpar[j0] = 0 - 1; j0 = j0 + 1 } 412 let nc: i64 = G[2] 413 var m1: i64 = 0 414 while m1 < mc { 415 if jmap[m1] >= 0 { 416 let myid: i64 = G[A_MO + m1] 417 var k: i64 = 0 418 while k < nc { 419 if G[A_CONN + k*3] == myid { 420 let dst: i64 = G[A_CONN + k*3 + 1] 421 var m2: i64 = 0 422 while m2 < mc { 423 if G[A_MO + m2] == dst { if jmap[m2] >= 0 { 424 jpar[jmap[m1]] = jmap[m2] 425 m2 = mc 426 k = nc 427 } } 428 m2 = m2 + 1 429 } 430 } 431 k = k + 1 432 } 433 } 434 m1 = m1 + 1 435 } 436 // model-parent map over ALL models: armature/figure nodes ABOVE the root joints carry 437 // the y-up->z-up conversion that TransformLink binds already include -- FK must chain 438 // through them or every world position comes out axis-swapped (measured 1.01m divergence) 439 let mpar: *i64 = sys_mmap(mc*8 + 64) as *i64 440 var mp0: i64 = 0 441 while mp0 < mc { mpar[mp0] = 0 - 1; mp0 = mp0 + 1 } 442 var mp1: i64 = 0 443 while mp1 < mc { 444 let myid2: i64 = G[A_MO + mp1] 445 var kp: i64 = 0 446 while kp < nc { 447 if G[A_CONN + kp*3] == myid2 { 448 let dstp: i64 = G[A_CONN + kp*3 + 1] 449 var mp2: i64 = 0 450 while mp2 < mc { 451 if G[A_MO + mp2] == dstp { mpar[mp1] = mp2; mp2 = mc; kp = nc } 452 mp2 = mp2 + 1 453 } 454 } 455 kp = kp + 1 456 } 457 mp1 = mp1 + 1 458 } 459 // per joint: rotation curves (ptag "Lcl Rotation" 12*256+110) and translation curves 460 // (ptag "Lcl Translation" 15*256+110), each X/Y/Z via curve->node ptag 856/857/858 461 let jrx: *i64 = sys_mmap(nj*8 + 64) as *i64 462 let jry: *i64 = sys_mmap(nj*8 + 64) as *i64 463 let jrz: *i64 = sys_mmap(nj*8 + 64) as *i64 464 let jtx: *i64 = sys_mmap(nj*8 + 64) as *i64 465 let jty: *i64 = sys_mmap(nj*8 + 64) as *i64 466 let jtz: *i64 = sys_mmap(nj*8 + 64) as *i64 467 var j1: i64 = 0 468 while j1 < nj { 469 jrx[j1] = 0 - 1 470 jry[j1] = 0 - 1 471 jrz[j1] = 0 - 1 472 jtx[j1] = 0 - 1 473 jty[j1] = 0 - 1 474 jtz[j1] = 0 - 1 475 j1 = j1 + 1 476 } 477 var rtracks: i64 = 0 478 var ttracks: i64 = 0 479 var maxms: i64 = 0 480 var m3: i64 = 0 481 while m3 < mc { 482 if jmap[m3] >= 0 { 483 let mid: i64 = G[A_MO + m3] 484 var k1: i64 = 0 485 while k1 < nc { 486 if G[A_CONN + k1*3 + 1] == mid { 487 let ptag0: i64 = G[A_CONN + k1*3 + 2] 488 var chan: i64 = 0 - 1 489 if ptag0 == 12*256 + 110 { chan = 1 } 490 if ptag0 == 15*256 + 110 { chan = 0 } 491 if chan >= 0 { 492 let nid: i64 = G[A_CONN + k1*3] 493 var isn: i64 = 0 494 var q: i64 = 0 495 while q < G[3] { if G[A_AN + q] == nid { isn = 1; q = G[3] } q = q + 1 } 496 if isn == 1 { 497 var k2: i64 = 0 498 while k2 < nc { 499 if G[A_CONN + k2*3 + 1] == nid { 500 let cid2: i64 = G[A_CONN + k2*3] 501 let pt: i64 = G[A_CONN + k2*3 + 2] 502 var ci: i64 = 0 - 1 503 var q2: i64 = 0 504 while q2 < G[1] { if G[A_CID + q2] == cid2 { ci = q2; q2 = G[1] } q2 = q2 + 1 } 505 if ci >= 0 { 506 let jj: i64 = jmap[m3] 507 if chan == 1 { 508 if pt == 3*256 + 88 { jrx[jj] = ci } 509 if pt == 3*256 + 89 { jry[jj] = ci } 510 if pt == 3*256 + 90 { jrz[jj] = ci } 511 } 512 if chan == 0 { 513 if pt == 3*256 + 88 { jtx[jj] = ci } 514 if pt == 3*256 + 89 { jty[jj] = ci } 515 if pt == 3*256 + 90 { jtz[jj] = ci } 516 } 517 let nk2: i64 = G[A_CKT + ci*4 + 1] 518 if nk2 > 0 { 519 let kta: *u8 = a_arr(fb, G[A_CKT + ci*4], nk2, G[A_CKT + ci*4 + 2], G[A_CKT + ci*4 + 3], 8) 520 if (kta as i64) != 0 { 521 let em: i64 = a_u64(kta, (nk2-1)*8) / A_MAGIC_46186158 522 if em > maxms { maxms = em } 523 } 524 } 525 } 526 } 527 k2 = k2 + 1 528 } 529 } 530 } 531 } 532 k1 = k1 + 1 533 } 534 } 535 m3 = m3 + 1 536 } 537 var jt: i64 = 0 538 while jt < nj { 539 if jrx[jt] >= 0 { rtracks = rtracks + 1 } 540 if jtx[jt] >= 0 { ttracks = ttracks + 1 } 541 jt = jt + 1 542 } 543 var pre_n: i64 = 0 544 var lr_n: i64 = 0 545 var lt_n: i64 = 0 546 var jc0: i64 = 0 547 while jc0 < nj { 548 let f: i64 = G[A_PF + jmi[jc0]] 549 if (f & 1) == 1 { pre_n = pre_n + 1 } 550 if (f & 2) == 2 { lr_n = lr_n + 1 } 551 if (f & 4) == 4 { lt_n = lt_n + 1 } 552 jc0 = jc0 + 1 553 } 554 aw("joints=" as *u8); an(nj) 555 aw(" rot_tracks=" as *u8); an(rtracks) 556 aw(" trans_tracks=" as *u8); an(ttracks) 557 aw(" prerot_joints=" as *u8); an(pre_n) 558 aw(" lclrot_joints=" as *u8); an(lr_n) 559 aw(" lclt_joints=" as *u8); an(lt_n) 560 aw(" duration_ms=" as *u8); an(maxms); aw("\n" as *u8) 561 if rtracks == 0 { aw("no animation found\n" as *u8); return 4 } 562 if maxms < 33 { maxms = 33 } 563 if maxms > A_MAGIC_60000 { maxms = A_MAGIC_60000 } 564 // in.nxa5 SKEL = ground truth rig (TransformLink binds); joint count MUST match 565 let lpn: *i64 = sys_mmap(16) as *i64 566 let nb: *u8 = sys_map_file(argv[2] as *u8, lpn) 567 let nlen2: i64 = lpn[0] 568 let nh: *i64 = nb as *i64 569 if nh[0] != nxa_magic() { aw("in.nxa bad\n" as *u8); return 5 } 570 let swo: i64 = nxa_find(nb, nlen2, nxa_tag4("SKEL" as *u8)) 571 if swo < 0 { aw("in.nxa has no SKEL\n" as *u8); return 5 } 572 let njs: i64 = nh[swo] 573 if njs != nj { aw("RIG-MISMATCH skel joints=" as *u8); an(njs); aw(" fbx joints=" as *u8); an(nj); aw("\n" as *u8); return 7 } 574 let skb: *i64 = ((nb as i64) + swo*8 + 8) as *i64 575 var pmm: i64 = 0 576 var jc1: i64 = 0 577 while jc1 < nj { 578 if skb[jc1*8] != jpar[jc1] { pmm = pmm + 1 } 579 jc1 = jc1 + 1 580 } 581 // per-joint constant local quats: pq = PreRotation, sq = static Lcl Rotation 582 let scr: *i64 = sys_mmap(256) as *i64 583 let pq: *i64 = sys_mmap(nj*32 + 64) as *i64 584 let sq: *i64 = sys_mmap(nj*32 + 64) as *i64 585 var jc2: i64 = 0 586 while jc2 < nj { 587 let mi: i64 = jmi[jc2] 588 nf_eul2q(G[A_PR + mi*3], G[A_PR + mi*3 + 1], G[A_PR + mi*3 + 2], ((pq as i64) + jc2*32) as *i64, scr) 589 nf_eul2q(G[A_LR + mi*3], G[A_LR + mi*3 + 1], G[A_LR + mi*3 + 2], ((sq as i64) + jc2*32) as *i64, scr) 590 jc2 = jc2 + 1 591 } 592 // topological order (parents first; FBX order is usually already topo but never assume) 593 let topo: *i64 = sys_mmap(nj*8 + 64) as *i64 594 let done: *i64 = sys_mmap(nj*8 + 64) as *i64 595 var tn: i64 = 0 596 var pass: i64 = 0 597 while pass < nj { 598 if tn < nj { 599 var jp0: i64 = 0 600 while jp0 < nj { 601 if done[jp0] == 0 { 602 var ok: i64 = 0 603 if jpar[jp0] < 0 { ok = 1 } 604 if jpar[jp0] >= 0 { if done[jpar[jp0]] == 1 { ok = 1 } } 605 if ok == 1 { topo[tn] = jp0; tn = tn + 1; done[jp0] = 1 } 606 } 607 jp0 = jp0 + 1 608 } 609 } 610 pass = pass + 1 611 } 612 if tn != nj { aw("RIG-MISMATCH parent cycle\n" as *u8); return 7 } 613 // STATIC FK = the reference/bind pose this bake is relative to. VALIDATE it against the 614 // SKEL bind translations before trusting any delta out of it. 615 let gqs: *i64 = sys_mmap(nj*32 + 64) as *i64 616 let gts: *i64 = sys_mmap(nj*24 + 64) as *i64 617 let lq: *i64 = sys_mmap(64) as *i64 618 let rv: *i64 = sys_mmap(64) as *i64 619 // per-root PREFIX transform = the composed non-limb ancestor chain (armature nodes), 620 // folded top-down; identity for non-root joints. Applied in BOTH static and anim FK. 621 let prefq: *i64 = sys_mmap(nj*32 + 64) as *i64 622 let preft: *i64 = sys_mmap(nj*24 + 64) as *i64 623 let anc: *i64 = sys_mmap(32*8 + 64) as *i64 624 let aqq: *i64 = sys_mmap(64) as *i64 625 let bqq: *i64 = sys_mmap(64) as *i64 626 let tq2: *i64 = sys_mmap(64) as *i64 627 var jr0: i64 = 0 628 while jr0 < nj { 629 prefq[jr0*4] = 0 630 prefq[jr0*4+1] = 0 631 prefq[jr0*4+2] = 0 632 prefq[jr0*4+3] = A_MAGIC_4096 633 if jpar[jr0] < 0 { 634 var nanc: i64 = 0 635 var cm: i64 = mpar[jmi[jr0]] 636 var hops: i64 = 0 637 while cm >= 0 { 638 if nanc < 30 { anc[nanc] = cm; nanc = nanc + 1 } 639 cm = mpar[cm] 640 hops = hops + 1 641 if hops > 30 { cm = 0 - 1 } 642 } 643 var pi2: i64 = nanc - 1 644 while pi2 >= 0 { 645 let am: i64 = anc[pi2] 646 nf_eul2q(G[A_PR + am*3], G[A_PR + am*3 + 1], G[A_PR + am*3 + 2], aqq, scr) 647 nf_eul2q(G[A_LR + am*3], G[A_LR + am*3 + 1], G[A_LR + am*3 + 2], bqq, scr) 648 nf_qmul(aqq, bqq, tq2) 649 nf_qnorm(tq2) 650 nf_qrotv(((prefq as i64) + jr0*32) as *i64, G[A_LT + am*3], G[A_LT + am*3 + 1], G[A_LT + am*3 + 2], rv, scr) 651 preft[jr0*3] = preft[jr0*3] + rv[0] 652 preft[jr0*3+1] = preft[jr0*3+1] + rv[1] 653 preft[jr0*3+2] = preft[jr0*3+2] + rv[2] 654 nf_qmul(((prefq as i64) + jr0*32) as *i64, tq2, aqq) 655 nf_qnorm(aqq) 656 prefq[jr0*4] = aqq[0] 657 prefq[jr0*4+1] = aqq[1] 658 prefq[jr0*4+2] = aqq[2] 659 prefq[jr0*4+3] = aqq[3] 660 pi2 = pi2 - 1 661 } 662 } 663 jr0 = jr0 + 1 664 } 665 var ti0: i64 = 0 666 while ti0 < nj { 667 let j: i64 = topo[ti0] 668 let mi2: i64 = jmi[j] 669 nf_qmul(((pq as i64) + j*32) as *i64, ((sq as i64) + j*32) as *i64, lq) 670 nf_qnorm(lq) 671 let pj: i64 = jpar[j] 672 if pj < 0 { 673 nf_qmul(((prefq as i64) + j*32) as *i64, lq, ((gqs as i64) + j*32) as *i64) 674 nf_qnorm(((gqs as i64) + j*32) as *i64) 675 nf_qrotv(((prefq as i64) + j*32) as *i64, G[A_LT + mi2*3], G[A_LT + mi2*3 + 1], G[A_LT + mi2*3 + 2], rv, scr) 676 gts[j*3] = preft[j*3] + rv[0] 677 gts[j*3+1] = preft[j*3+1] + rv[1] 678 gts[j*3+2] = preft[j*3+2] + rv[2] 679 } 680 if pj >= 0 { 681 nf_qmul(((gqs as i64) + pj*32) as *i64, lq, ((gqs as i64) + j*32) as *i64) 682 nf_qnorm(((gqs as i64) + j*32) as *i64) 683 nf_qrotv(((gqs as i64) + pj*32) as *i64, G[A_LT + mi2*3], G[A_LT + mi2*3 + 1], G[A_LT + mi2*3 + 2], rv, scr) 684 gts[j*3] = gts[pj*3] + rv[0] 685 gts[j*3+1] = gts[pj*3+1] + rv[1] 686 gts[j*3+2] = gts[pj*3+2] + rv[2] 687 } 688 ti0 = ti0 + 1 689 } 690 // validate ONLY joints with REAL cluster binds: nx_nxa_skin propagates a parent's bind 691 // into unclustered joints (zero-length placeholder bones), so a placeholder equals its 692 // parent's bind exactly (or is 0,0,0) -- comparing FK truth against those is meaningless 693 // (measured: J4 fk=hip@1010mm vs placeholder bind 0,0,0 -- the FK was right all along). 694 var maxerr: i64 = 0 695 var maxerrj: i64 = 0 - 1 696 var vbinds: i64 = 0 697 var jc3: i64 = 0 698 while jc3 < nj { 699 var real: i64 = 1 700 if skb[jc3*8+1] == 0 { if skb[jc3*8+2] == 0 { if skb[jc3*8+3] == 0 { real = 0 } } } 701 let pjv: i64 = skb[jc3*8] 702 if pjv >= 0 { 703 if skb[jc3*8+1] == skb[pjv*8+1] { if skb[jc3*8+2] == skb[pjv*8+2] { if skb[jc3*8+3] == skb[pjv*8+3] { real = 0 } } } 704 } 705 if real == 1 { 706 vbinds = vbinds + 1 707 var a0: i64 = 0 708 while a0 < 3 { 709 var d: i64 = gts[jc3*3+a0] - skb[jc3*8 + 1 + a0] 710 if d < 0 { d = 0 - d } 711 if d > maxerr { maxerr = d; maxerrj = jc3 } 712 a0 = a0 + 1 713 } 714 } 715 jc3 = jc3 + 1 716 } 717 var jdbg: i64 = 0 718 while jdbg < 8 { 719 aw("J" as *u8); an(jdbg) 720 aw(" fk=" as *u8); an(gts[jdbg*3]); aw("," as *u8); an(gts[jdbg*3+1]); aw("," as *u8); an(gts[jdbg*3+2]) 721 aw(" bind=" as *u8); an(skb[jdbg*8+1]); aw("," as *u8); an(skb[jdbg*8+2]); aw("," as *u8); an(skb[jdbg*8+3]) 722 aw(" par=" as *u8); an(jpar[jdbg]); aw("\n" as *u8) 723 jdbg = jdbg + 1 724 } 725 aw("parent_mismatch=" as *u8); an(pmm) 726 aw(" validated_binds=" as *u8); an(vbinds) 727 aw(" static_maxerr_units=" as *u8); an(maxerr) 728 aw(" at_joint=" as *u8); an(maxerrj); aw("\n" as *u8) 729 if vbinds < 8 { aw("TOO-FEW-REAL-BINDS to validate FK -- refusing to bake\n" as *u8); return 7 } 730 if maxerr > A_MAGIC_20000 { aw("STATIC-FK-DIVERGES from SKEL binds -- refusing to bake\n" as *u8); return 7 } 731 // pre-inflate every rig-connected curve ONCE (inflating inside the 545x104 bake loop 732 // would decompress the same arrays ~340k times); 6 channels per joint: rx ry rz tx ty tz 733 let cidx: *i64 = sys_mmap(nj*48 + 64) as *i64 734 let cptr: *i64 = sys_mmap(nj*96 + 64) as *i64 735 let cnk: *i64 = sys_mmap(nj*48 + 64) as *i64 736 var wrapn: i64 = 0 737 var jc4: i64 = 0 738 while jc4 < nj { 739 cidx[jc4*6] = jrx[jc4] 740 cidx[jc4*6+1] = jry[jc4] 741 cidx[jc4*6+2] = jrz[jc4] 742 cidx[jc4*6+3] = jtx[jc4] 743 cidx[jc4*6+4] = jty[jc4] 744 cidx[jc4*6+5] = jtz[jc4] 745 var ch: i64 = 0 746 while ch < 6 { 747 let ci: i64 = cidx[jc4*6+ch] 748 if ci >= 0 { if G[A_CKT + ci*4 + 1] > 0 { if G[A_CKT + ci*4 + 1] == G[A_CKV + ci*4 + 1] { 749 let kta: *u8 = a_arr(fb, G[A_CKT + ci*4], G[A_CKT + ci*4 + 1], G[A_CKT + ci*4 + 2], G[A_CKT + ci*4 + 3], 8) 750 let kva: *u8 = a_arr(fb, G[A_CKV + ci*4], G[A_CKV + ci*4 + 1], G[A_CKV + ci*4 + 2], G[A_CKV + ci*4 + 3], 4) 751 if (kta as i64) != 0 { if (kva as i64) != 0 { 752 cptr[jc4*12 + ch*2] = kta as i64 753 cptr[jc4*12 + ch*2 + 1] = kva as i64 754 cnk[jc4*6 + ch] = G[A_CKT + ci*4 + 1] 755 if ch < 3 { 756 var kw: i64 = 0 757 while kw < G[A_CKT + ci*4 + 1] - 1 { 758 var dvv: i64 = a_f32(kva, (kw+1)*4, 1000) - a_f32(kva, kw*4, 1000) 759 if dvv < 0 { dvv = 0 - dvv } 760 if dvv > A_MAGIC_180000 { wrapn = wrapn + 1 } 761 kw = kw + 1 762 } 763 } 764 } } 765 } } } 766 ch = ch + 1 767 } 768 jc4 = jc4 + 1 769 } 770 // FLAT-FEET (default; --heels keeps the authored tip-toe as a chosen behavior): the 771 // walk mocap is heels-authored -- feet plantarflexed the whole clip. Re-center each 772 // foot/toe joint's rotation curves around its STATIC pose (offset = channel mean minus 773 // static default, data-driven, no magic angle) so the foot oscillates about neutral = 774 // flat-ground walk; toe-off dynamics survive because only the MEAN shifts. 775 let foff: *i64 = sys_mmap(nj*24 + 64) as *i64 776 var heels: i64 = 0 777 if argc >= 5 { 778 let fav: *u8 = argv[4] as *u8 779 if fav[2] == (104 as u8) { heels = 1 } 780 } 781 var nflat: i64 = 0 782 var maxfo: i64 = 0 783 if heels == 0 { 784 var fj0: i64 = 0 785 while fj0 < nj { 786 let no: i64 = G[A_NO + jmi[fj0]] 787 let nl: i64 = G[A_NL + jmi[fj0]] 788 var isf: i64 = a_namehas(fb, no, nl, "_Foot" as *u8) 789 if isf == 0 { isf = a_namehas(fb, no, nl, "Toe" as *u8) } 790 if isf == 1 { 791 nflat = nflat + 1 792 var ch2: i64 = 0 793 while ch2 < 3 { 794 let nk3: i64 = cnk[fj0*6 + ch2] 795 if nk3 > 1 { 796 let kva2: *u8 = cptr[fj0*12 + ch2*2 + 1] as *u8 797 var sum2: i64 = 0 798 var k3: i64 = 0 799 while k3 < nk3 { sum2 = sum2 + a_f32(kva2, k3*4, 1000); k3 = k3 + 1 } 800 foff[fj0*3 + ch2] = sum2/nk3 - G[A_LR + jmi[fj0]*3 + ch2] 801 var af0: i64 = foff[fj0*3 + ch2] 802 if af0 < 0 { af0 = 0 - af0 } 803 if af0 > maxfo { maxfo = af0 } 804 } 805 ch2 = ch2 + 1 806 } 807 } 808 fj0 = fj0 + 1 809 } 810 } 811 aw("flatfeet_joints=" as *u8); an(nflat) 812 aw(" max_offset_mdeg=" as *u8); an(maxfo) 813 aw(" heels=" as *u8); an(heels); aw("\n" as *u8) 814 // per-joint curve motion range in millideg: exposes helper/duplicate joints whose 815 // exported curves are FLAT (constraint-driven in-engine, not baked by the exporter) 816 let rr: *i64 = sys_mmap(nj*8 + 64) as *i64 817 var jd0: i64 = 0 818 while jd0 < nj { 819 var rrj: i64 = 0 820 var ch9: i64 = 0 821 while ch9 < 3 { 822 let nk9: i64 = cnk[jd0*6+ch9] 823 if nk9 > 1 { 824 let kv9: *u8 = cptr[jd0*12+ch9*2+1] as *u8 825 var vmin: i64 = A_MAGIC_4611686018427387903 826 var vmax: i64 = 0 - A_MAGIC_4611686018427387903 827 var k9: i64 = 0 828 while k9 < nk9 { 829 let vv9: i64 = a_f32(kv9, k9*4, 1000) 830 if vv9 < vmin { vmin = vv9 } 831 if vv9 > vmax { vmax = vv9 } 832 k9 = k9 + 1 833 } 834 if vmax - vmin > rrj { rrj = vmax - vmin } 835 } 836 ch9 = ch9 + 1 837 } 838 rr[jd0] = rrj 839 jd0 = jd0 + 1 840 } 841 // DEAD CHAINS (whole ancestor path flat, e.g. the duplicate pelvis chain the mocap does 842 // not target -- measured: J5 w=1.3M range=0 beside thighs swinging 250deg): their flesh 843 // would freeze at bind while the living skeleton walks away. ADOPT the exact world 844 // transform of the nearest curve-moving joint by bind distance (flesh follows SPACE -- 845 // the proven region law, made per-file-exact at bake). Flat joints under a MOVING parent 846 // (twist helpers) are left alone: FK already carries their parents into them. 847 let flatj: *i64 = sys_mmap(nj*8 + 64) as *i64 848 let deadj: *i64 = sys_mmap(nj*8 + 64) as *i64 849 let donor: *i64 = sys_mmap(nj*8 + 64) as *i64 850 var jf0: i64 = 0 851 while jf0 < nj { 852 flatj[jf0] = 0 853 if rr[jf0] < A_MAGIC_2000 { flatj[jf0] = 1 } 854 jf0 = jf0 + 1 855 } 856 var td0: i64 = 0 857 while td0 < nj { 858 let j8: i64 = topo[td0] 859 deadj[j8] = 0 860 if flatj[j8] == 1 { 861 let pj8: i64 = jpar[j8] 862 if pj8 < 0 { deadj[j8] = 1 } 863 if pj8 >= 0 { if deadj[pj8] == 1 { deadj[j8] = 1 } } 864 } 865 td0 = td0 + 1 866 } 867 // ADOPTION SET v2 (measured escalation): dead chains PLUS living flat joints that CARRY 868 // real flesh (constraint-driven twist/share helpers, e.g. the scapula chain w=759k rigid 869 // under a 31deg-swinging upperarm while four sibling chains rotate -- the last shard 870 // source). Nearest-moving-bone adoption minimizes flesh seams by construction. 871 let kwo2: i64 = nxa_find(nb, nlen2, nxa_tag4("SKIN" as *u8)) 872 let wmass: *i64 = sys_mmap(nj*8 + 64) as *i64 873 if kwo2 >= 0 { 874 let skn2: *i64 = ((nb as i64) + kwo2*8 + 8) as *i64 875 let nvv: i64 = nh[kwo2] 876 var v9: i64 = 0 877 while v9 < nvv { 878 var s9: i64 = 0 879 while s9 < 4 { 880 let jw: i64 = skn2[v9*8+s9] 881 if jw >= 0 { if jw < nj { wmass[jw] = wmass[jw] + skn2[v9*8+4+s9] } } 882 s9 = s9 + 1 883 } 884 v9 = v9 + 1 885 } 886 } 887 // MEASURED ROLLBACK: adopting LIVING flat helpers by nearest-mover picked finger bones 888 // as donors for hand helpers (new spikes) and did not close the scapula seam -- living 889 // twist chains need a real twist-share model (fraction-of-parent roll), filed as debt. 890 // Adoption stays DEAD-CHAIN-ONLY, the measured clear win (duplicate pelvis, w=1.3M). 891 let adoptj: *i64 = sys_mmap(nj*8 + 64) as *i64 892 var ja0: i64 = 0 893 while ja0 < nj { 894 adoptj[ja0] = 0 895 if deadj[ja0] == 1 { adoptj[ja0] = 1 } 896 ja0 = ja0 + 1 897 } 898 var jn0: i64 = 0 899 while jn0 < nj { 900 var show: i64 = 0 901 if wmass[jn0] > A_MAGIC_200000 { show = 1 } 902 if adoptj[jn0] == 1 { show = 1 } 903 if show == 1 { 904 aw("JN " as *u8); an(jn0) 905 aw(" par=" as *u8); an(jpar[jn0]) 906 aw(" w=" as *u8); an(wmass[jn0]) 907 aw(" rr=" as *u8); an(rr[jn0]) 908 aw(" adopt=" as *u8); an(adoptj[jn0]) 909 aw(" " as *u8) 910 a_wname(fb, G[A_NO + jmi[jn0]], G[A_NL + jmi[jn0]]) 911 aw("\n" as *u8) 912 } 913 jn0 = jn0 + 1 914 } 915 var ndead: i64 = 0 916 var jf1: i64 = 0 917 while jf1 < nj { 918 donor[jf1] = 0 - 1 919 if adoptj[jf1] == 1 { 920 ndead = ndead + 1 921 var best: i64 = 0 - 1 922 var bd2: i64 = A_MAGIC_4611686018427387903 923 var jf2: i64 = 0 924 while jf2 < nj { 925 if flatj[jf2] == 0 { 926 let ddx: i64 = (gts[jf1*3] - gts[jf2*3])/16 927 let ddy: i64 = (gts[jf1*3+1] - gts[jf2*3+1])/16 928 let ddz: i64 = (gts[jf1*3+2] - gts[jf2*3+2])/16 929 let dd2: i64 = ddx*ddx + ddy*ddy + ddz*ddz 930 if dd2 < bd2 { bd2 = dd2; best = jf2 } 931 } 932 jf2 = jf2 + 1 933 } 934 donor[jf1] = best 935 } 936 jf1 = jf1 + 1 937 } 938 aw("adopted_joints=" as *u8); an(ndead); aw("\n" as *u8) 939 // ANIM bake: per 30Hz sample evaluate FK with curves, emit channel-2 PACKED world deltas: 940 // key = 2 words; w0 lanes LE [t_ms u16][dqx dqy dqz i16 q12]; w1 [dqw i16][dtx dty dtz i16 mm] 941 // D = Gq_anim x conj(Gq_static); dt = (Gt_anim - Gt_static)/100 units->mm. Every joint gets 942 // a track: ancestor rotation moves EVERY descendant's world transform. 943 let nk: i64 = maxms/33 + 1 944 let awl: i64 = 1 + nj*(3 + nk*2) 945 let ap2: *i64 = sys_mmap(awl*8 + 64) as *i64 946 ap2[0] = nj 947 let gqa: *i64 = sys_mmap(nj*32 + 64) as *i64 948 let gta: *i64 = sys_mmap(nj*24 + 64) as *i64 949 let aq: *i64 = sys_mmap(64) as *i64 950 let cq: *i64 = sys_mmap(64) as *i64 951 let dq: *i64 = sys_mmap(64) as *i64 952 // track headers first (fixed layout: track j at 1 + j*(3+nk*2)) 953 var jh: i64 = 0 954 while jh < nj { 955 let hb: i64 = 1 + jh*(3 + nk*2) 956 ap2[hb] = jh 957 ap2[hb+1] = 2 958 ap2[hb+2] = nk 959 jh = jh + 1 960 } 961 // transform snapshot at the stretch-test frame (s=106 ~ t=3.5s) 962 let gqx: *i64 = sys_mmap(nj*32 + 64) as *i64 963 let gtx: *i64 = sys_mmap(nj*24 + 64) as *i64 964 // full per-sample world-transform store for the FOOT-LOCK IK pass (nk*nj quats+pos) 965 let sgq: *i64 = sys_mmap(nk*nj*32 + 64) as *i64 966 let sgt: *i64 = sys_mmap(nk*nj*24 + 64) as *i64 967 var clamped: i64 = 0 968 var s: i64 = 0 969 while s < nk { 970 let tms: i64 = s*33 971 var ti1: i64 = 0 972 while ti1 < nj { 973 let j: i64 = topo[ti1] 974 let mi3: i64 = jmi[j] 975 // local rotation: curves replace Lcl Rotation entirely; PreRotation always applies 976 var mdx: i64 = G[A_LR + mi3*3] 977 var mdy: i64 = G[A_LR + mi3*3 + 1] 978 var mdz: i64 = G[A_LR + mi3*3 + 2] 979 if cnk[j*6] > 0 { mdx = a_sample_ang(cptr[j*12] as *u8, cptr[j*12+1] as *u8, cnk[j*6], tms) } 980 if cnk[j*6+1] > 0 { mdy = a_sample_ang(cptr[j*12+2] as *u8, cptr[j*12+3] as *u8, cnk[j*6+1], tms) } 981 if cnk[j*6+2] > 0 { mdz = a_sample_ang(cptr[j*12+4] as *u8, cptr[j*12+5] as *u8, cnk[j*6+2], tms) } 982 nf_eul2q(mdx, mdy, mdz, aq, scr) 983 nf_qmul(((pq as i64) + j*32) as *i64, aq, lq) 984 nf_qnorm(lq) 985 // local translation: curves replace Lcl Translation where present 986 var ltx: i64 = G[A_LT + mi3*3] 987 var lty: i64 = G[A_LT + mi3*3 + 1] 988 var ltz: i64 = G[A_LT + mi3*3 + 2] 989 if cnk[j*6+3] > 0 { ltx = a_sample(cptr[j*12+6] as *u8, cptr[j*12+7] as *u8, cnk[j*6+3], tms) } 990 if cnk[j*6+4] > 0 { lty = a_sample(cptr[j*12+8] as *u8, cptr[j*12+9] as *u8, cnk[j*6+4], tms) } 991 if cnk[j*6+5] > 0 { ltz = a_sample(cptr[j*12+10] as *u8, cptr[j*12+11] as *u8, cnk[j*6+5], tms) } 992 let pj2: i64 = jpar[j] 993 if pj2 < 0 { 994 nf_qmul(((prefq as i64) + j*32) as *i64, lq, ((gqa as i64) + j*32) as *i64) 995 nf_qnorm(((gqa as i64) + j*32) as *i64) 996 nf_qrotv(((prefq as i64) + j*32) as *i64, ltx, lty, ltz, rv, scr) 997 gta[j*3] = preft[j*3] + rv[0] 998 gta[j*3+1] = preft[j*3+1] + rv[1] 999 gta[j*3+2] = preft[j*3+2] + rv[2] 1000 } 1001 if pj2 >= 0 { 1002 nf_qmul(((gqa as i64) + pj2*32) as *i64, lq, ((gqa as i64) + j*32) as *i64) 1003 nf_qnorm(((gqa as i64) + j*32) as *i64) 1004 nf_qrotv(((gqa as i64) + pj2*32) as *i64, ltx, lty, ltz, rv, scr) 1005 gta[j*3] = gta[pj2*3] + rv[0] 1006 gta[j*3+1] = gta[pj2*3+1] + rv[1] 1007 gta[j*3+2] = gta[pj2*3+2] + rv[2] 1008 } 1009 // world delta about bind (static FK) -> packed key (dead joints packed below) 1010 if adoptj[j] == 0 { 1011 nf_qconj(((gqs as i64) + j*32) as *i64, cq) 1012 nf_qmul(((gqa as i64) + j*32) as *i64, cq, dq) 1013 nf_qnorm(dq) 1014 var dtx: i64 = (gta[j*3] - gts[j*3]) / 100 1015 var dty: i64 = (gta[j*3+1] - gts[j*3+1]) / 100 1016 var dtz: i64 = (gta[j*3+2] - gts[j*3+2]) / 100 1017 if dtx > A_MAGIC_32767 { dtx = A_MAGIC_32767; clamped = clamped + 1 } 1018 if dtx < 0 - A_MAGIC_32767 { dtx = 0 - A_MAGIC_32767; clamped = clamped + 1 } 1019 if dty > A_MAGIC_32767 { dty = A_MAGIC_32767; clamped = clamped + 1 } 1020 if dty < 0 - A_MAGIC_32767 { dty = 0 - A_MAGIC_32767; clamped = clamped + 1 } 1021 if dtz > A_MAGIC_32767 { dtz = A_MAGIC_32767; clamped = clamped + 1 } 1022 if dtz < 0 - A_MAGIC_32767 { dtz = 0 - A_MAGIC_32767; clamped = clamped + 1 } 1023 let kb: i64 = 1 + j*(3 + nk*2) + 3 + s*2 1024 ap2[kb] = (tms & A_MAGIC_65535) | ((dq[0] & A_MAGIC_65535) << 16) | ((dq[1] & A_MAGIC_65535) << 32) | ((dq[2] & A_MAGIC_65535) << 48) 1025 ap2[kb+1] = (dq[3] & A_MAGIC_65535) | ((dtx & A_MAGIC_65535) << 16) | ((dty & A_MAGIC_65535) << 32) | ((dtz & A_MAGIC_65535) << 48) 1026 } 1027 ti1 = ti1 + 1 1028 } 1029 // dead-chain joints: adopt the donor's EXACT world transform, re-expressed about the 1030 // dead joint's own bind: D = D_donor; dt = D(b_j - b_d) + b_d + dt_d - b_j 1031 var jd9: i64 = 0 1032 while jd9 < nj { 1033 if adoptj[jd9] == 1 { if donor[jd9] >= 0 { 1034 let dnr: i64 = donor[jd9] 1035 nf_qconj(((gqs as i64) + dnr*32) as *i64, cq) 1036 nf_qmul(((gqa as i64) + dnr*32) as *i64, cq, dq) 1037 nf_qnorm(dq) 1038 nf_qrotv(dq, gts[jd9*3] - gts[dnr*3], gts[jd9*3+1] - gts[dnr*3+1], gts[jd9*3+2] - gts[dnr*3+2], rv, scr) 1039 var dtx: i64 = (rv[0] + gta[dnr*3] - gts[jd9*3]) / 100 1040 var dty: i64 = (rv[1] + gta[dnr*3+1] - gts[jd9*3+1]) / 100 1041 var dtz: i64 = (rv[2] + gta[dnr*3+2] - gts[jd9*3+2]) / 100 1042 if dtx > A_MAGIC_32767 { dtx = A_MAGIC_32767; clamped = clamped + 1 } 1043 if dtx < 0 - A_MAGIC_32767 { dtx = 0 - A_MAGIC_32767; clamped = clamped + 1 } 1044 if dty > A_MAGIC_32767 { dty = A_MAGIC_32767; clamped = clamped + 1 } 1045 if dty < 0 - A_MAGIC_32767 { dty = 0 - A_MAGIC_32767; clamped = clamped + 1 } 1046 if dtz > A_MAGIC_32767 { dtz = A_MAGIC_32767; clamped = clamped + 1 } 1047 if dtz < 0 - A_MAGIC_32767 { dtz = 0 - A_MAGIC_32767; clamped = clamped + 1 } 1048 let kb2: i64 = 1 + jd9*(3 + nk*2) + 3 + s*2 1049 ap2[kb2] = (tms & A_MAGIC_65535) | ((dq[0] & A_MAGIC_65535) << 16) | ((dq[1] & A_MAGIC_65535) << 32) | ((dq[2] & A_MAGIC_65535) << 48) 1050 ap2[kb2+1] = (dq[3] & A_MAGIC_65535) | ((dtx & A_MAGIC_65535) << 16) | ((dty & A_MAGIC_65535) << 32) | ((dtz & A_MAGIC_65535) << 48) 1051 } } 1052 jd9 = jd9 + 1 1053 } 1054 if s == 106 { 1055 var sn0: i64 = 0 1056 while sn0 < nj*4 { gqx[sn0] = gqa[sn0]; sn0 = sn0 + 1 } 1057 var sn1: i64 = 0 1058 while sn1 < nj*3 { gtx[sn1] = gta[sn1]; sn1 = sn1 + 1 } 1059 } 1060 var sc0: i64 = 0 1061 while sc0 < nj*4 { sgq[s*nj*4 + sc0] = gqa[sc0]; sc0 = sc0 + 1 } 1062 var sc1: i64 = 0 1063 while sc1 < nj*3 { sgt[s*nj*3 + sc1] = gta[sc1]; sc1 = sc1 + 1 } 1064 s = s + 1 1065 } 1066 // ===== FOOT-LOCK IK (SOTA rung 1): plant heels during MEASURED stance phases ===== 1067 // stance = foot XY-speed ~0; ground = min foot-z over clip; 2-bone thigh-calf IK moves 1068 // the ankle to (pinned XY, ground z) blended by a smoothed stance weight; the whole 1069 // thigh subtree (twists/toes) is rigidly re-based on the corrected chain and repacked. 1070 var jFL: i64 = 0 - 1 1071 var jFR: i64 = 0 - 1 1072 var jq9: i64 = 0 1073 while jq9 < nj { 1074 if a_namehas(fb, G[A_NO + jmi[jq9]], G[A_NL + jmi[jq9]], "_L_Foot" as *u8) == 1 { jFL = jq9 } 1075 if a_namehas(fb, G[A_NO + jmi[jq9]], G[A_NL + jmi[jq9]], "_R_Foot" as *u8) == 1 { jFR = jq9 } 1076 jq9 = jq9 + 1 1077 } 1078 var planted: i64 = 0 1079 if heels == 0 { if jFL >= 0 { if jFR >= 0 { 1080 let ikw: *i64 = sys_mmap(nk*16 + 64) as *i64 1081 let pinx: *i64 = sys_mmap(nk*16 + 64) as *i64 1082 let piny: *i64 = sys_mmap(nk*16 + 64) as *i64 1083 let dq1: *i64 = sys_mmap(64) as *i64 1084 let dq2: *i64 = sys_mmap(64) as *i64 1085 let qc9: *i64 = sys_mmap(64) as *i64 1086 let qr9: *i64 = sys_mmap(64) as *i64 1087 let qo9: *i64 = sys_mmap(64) as *i64 1088 var side: i64 = 0 1089 while side < 2 { 1090 var jF: i64 = jFL 1091 if side == 1 { jF = jFR } 1092 let jC: i64 = jpar[jF] 1093 let jT: i64 = jpar[jC] 1094 var gz: i64 = A_MAGIC_4611686018427387903 1095 var s9: i64 = 0 1096 while s9 < nk { 1097 if sgt[s9*nj*3 + jF*3 + 2] < gz { gz = sgt[s9*nj*3 + jF*3 + 2] } 1098 s9 = s9 + 1 1099 } 1100 var s8: i64 = 0 1101 while s8 < nk { 1102 // stance = foot in its LOWEST height band (works for in-place AND traveling 1103 // walks; XY-speed failed on this in-place clip: planted foot slides backward) 1104 var st9: i64 = 0 1105 if sgt[s8*nj*3 + jF*3 + 2] - gz < A_MAGIC_1500 { st9 = 1 } 1106 ikw[s8*2 + side] = 0 1107 if st9 == 1 { ikw[s8*2 + side] = 1000 } 1108 s8 = s8 + 1 1109 } 1110 var bp: i64 = 0 1111 while bp < 2 { 1112 var s7: i64 = 1 1113 while s7 < nk - 1 { 1114 ikw[s7*2+side] = (ikw[(s7-1)*2+side] + ikw[s7*2+side]*2 + ikw[(s7+1)*2+side])/4 1115 s7 = s7 + 1 1116 } 1117 bp = bp + 1 1118 } 1119 var lx: i64 = 0 1120 var ly: i64 = 0 1121 var inrun: i64 = 0 1122 var s6: i64 = 0 1123 while s6 < nk { 1124 if ikw[s6*2+side] > 500 { 1125 if inrun == 0 { lx = sgt[s6*nj*3 + jF*3]; ly = sgt[s6*nj*3 + jF*3 + 1]; inrun = 1 } 1126 } 1127 if ikw[s6*2+side] <= 500 { inrun = 0; lx = sgt[s6*nj*3+jF*3]; ly = sgt[s6*nj*3+jF*3+1] } 1128 pinx[s6*2+side] = lx 1129 piny[s6*2+side] = ly 1130 s6 = s6 + 1 1131 } 1132 let L1: i64 = nf_isqrt((gts[jC*3]-gts[jT*3])*(gts[jC*3]-gts[jT*3]) + (gts[jC*3+1]-gts[jT*3+1])*(gts[jC*3+1]-gts[jT*3+1]) + (gts[jC*3+2]-gts[jT*3+2])*(gts[jC*3+2]-gts[jT*3+2])) 1133 let L2: i64 = nf_isqrt((gts[jF*3]-gts[jC*3])*(gts[jF*3]-gts[jC*3]) + (gts[jF*3+1]-gts[jC*3+1])*(gts[jF*3+1]-gts[jC*3+1]) + (gts[jF*3+2]-gts[jC*3+2])*(gts[jF*3+2]-gts[jC*3+2])) 1134 var s5: i64 = 0 1135 while s5 < nk { 1136 let w5: i64 = ikw[s5*2+side] 1137 if w5 > 20 { 1138 planted = planted + 1 1139 let bq: i64 = s5*nj*4 1140 let bt: i64 = s5*nj*3 1141 let hx: i64 = sgt[bt+jT*3] 1142 let hy: i64 = sgt[bt+jT*3+1] 1143 let hz: i64 = sgt[bt+jT*3+2] 1144 let kx: i64 = sgt[bt+jC*3] 1145 let ky: i64 = sgt[bt+jC*3+1] 1146 let kz: i64 = sgt[bt+jC*3+2] 1147 let fx: i64 = sgt[bt+jF*3] 1148 let fy: i64 = sgt[bt+jF*3+1] 1149 let fz: i64 = sgt[bt+jF*3+2] 1150 var tx: i64 = fx 1151 var ty: i64 = fy 1152 var tz: i64 = fz + (gz - fz)*w5/1000 1153 var ddx: i64 = tx - hx 1154 var ddy: i64 = ty - hy 1155 var ddz: i64 = tz - hz 1156 var dd: i64 = nf_isqrt(ddx*ddx + ddy*ddy + ddz*ddz) 1157 let dmax: i64 = L1 + L2 - 60 1158 if dd > dmax { 1159 tx = hx + ddx*dmax/dd 1160 ty = hy + ddy*dmax/dd 1161 tz = hz + ddz*dmax/dd 1162 ddx = tx - hx 1163 ddy = ty - hy 1164 ddz = tz - hz 1165 dd = dmax 1166 } 1167 if dd < 100 { dd = 100 } 1168 let aa: i64 = (L1*L1 + dd*dd - L2*L2)/(2*dd) 1169 var h2: i64 = L1*L1 - aa*aa 1170 if h2 < 0 { h2 = 0 } 1171 let hh: i64 = nf_isqrt(h2) 1172 let khx: i64 = kx - hx 1173 let khy: i64 = ky - hy 1174 let khz: i64 = kz - hz 1175 let along: i64 = (khx*ddx + khy*ddy + khz*ddz)/dd 1176 var px9: i64 = khx - ddx*along/dd 1177 var py9: i64 = khy - ddy*along/dd 1178 var pz9: i64 = khz - ddz*along/dd 1179 var pl: i64 = nf_isqrt(px9*px9 + py9*py9 + pz9*pz9) 1180 if pl < 20 { px9 = ddy; py9 = 0 - ddx; pz9 = 0; pl = nf_isqrt(px9*px9 + py9*py9 + pz9*pz9); if pl < 1 { pl = 1 } } 1181 let nkx: i64 = hx + ddx*aa/dd + px9*hh/pl 1182 let nky: i64 = hy + ddy*aa/dd + py9*hh/pl 1183 let nkz: i64 = hz + ddz*aa/dd + pz9*hh/pl 1184 nf_qarc(kx-hx, ky-hy, kz-hz, nkx-hx, nky-hy, nkz-hz, dq1) 1185 nf_qarc(fx-kx, fy-ky, fz-kz, tx-nkx, ty-nky, tz-nkz, dq2) 1186 // corrected world transforms: thigh(rot only), calf, foot (+rides dq2) 1187 var jx9: i64 = 0 1188 while jx9 < nj { 1189 // nearest corrected ancestor: F beats C beats T 1190 var anc: i64 = 0 - 1 1191 if jx9 == jT { anc = 0 - 2 } 1192 if jx9 == jC { anc = 0 - 2 } 1193 if jx9 == jF { anc = 0 - 2 } 1194 if anc == 0 - 1 { 1195 var cu9: i64 = jpar[jx9] 1196 var hops9: i64 = 0 1197 while cu9 >= 0 { 1198 if cu9 == jF { anc = jF; cu9 = 0 - 1 } 1199 if cu9 == jC { if anc < 0 { anc = jC; cu9 = 0 - 1 } } 1200 if cu9 == jT { if anc < 0 { anc = jT; cu9 = 0 - 1 } } 1201 if cu9 >= 0 { cu9 = jpar[cu9] } 1202 hops9 = hops9 + 1 1203 if hops9 > 32 { cu9 = 0 - 1 } 1204 } 1205 } 1206 if anc != 0 - 1 { 1207 // old world of this joint 1208 let qj0: i64 = bq + jx9*4 1209 // compute corrected world for jx9 1210 var nqx: i64 = 0 1211 var nqy: i64 = 0 1212 var nqz: i64 = 0 1213 var nqw: i64 = A_MAGIC_4096 1214 var ntx: i64 = 0 1215 var nty: i64 = 0 1216 var ntz: i64 = 0 1217 if jx9 == jT { 1218 nf_qmul(dq1, ((sgq as i64) + qj0*8) as *i64, qo9) 1219 nf_qnorm(qo9) 1220 nqx = qo9[0]; nqy = qo9[1]; nqz = qo9[2]; nqw = qo9[3] 1221 ntx = hx; nty = hy; ntz = hz 1222 } 1223 if jx9 == jC { 1224 nf_qmul(dq2, ((sgq as i64) + qj0*8) as *i64, qo9) 1225 nf_qnorm(qo9) 1226 nqx = qo9[0]; nqy = qo9[1]; nqz = qo9[2]; nqw = qo9[3] 1227 ntx = nkx; nty = nky; ntz = nkz 1228 } 1229 if jx9 == jF { 1230 nf_qmul(dq2, ((sgq as i64) + qj0*8) as *i64, qo9) 1231 nf_qnorm(qo9) 1232 nqx = qo9[0]; nqy = qo9[1]; nqz = qo9[2]; nqw = qo9[3] 1233 ntx = tx; nty = ty; ntz = tz 1234 } 1235 if anc >= 0 { 1236 // rebase on corrected ancestor: G' = Ganc' o (Ganc^-1 o G) 1237 var aq0: i64 = bq + anc*4 1238 var at0: i64 = bt + anc*3 1239 nf_qconj(((sgq as i64) + aq0*8) as *i64, qc9) 1240 nf_qmul(qc9, ((sgq as i64) + qj0*8) as *i64, qr9) 1241 nf_qrotv(qc9, sgt[bt+jx9*3]-sgt[at0], sgt[bt+jx9*3+1]-sgt[at0+1], sgt[bt+jx9*3+2]-sgt[at0+2], rv, scr) 1242 let rlx: i64 = rv[0] 1243 let rly: i64 = rv[1] 1244 let rlz: i64 = rv[2] 1245 // ancestor corrected values 1246 var acqx: i64 = 0 1247 var acqy: i64 = 0 1248 var acqz: i64 = 0 1249 var acqw: i64 = A_MAGIC_4096 1250 var actx: i64 = 0 1251 var acty: i64 = 0 1252 var actz: i64 = 0 1253 if anc == jT { 1254 nf_qmul(dq1, ((sgq as i64) + (bq+jT*4)*8) as *i64, qo9) 1255 nf_qnorm(qo9) 1256 acqx=qo9[0]; acqy=qo9[1]; acqz=qo9[2]; acqw=qo9[3] 1257 actx=hx; acty=hy; actz=hz 1258 } 1259 if anc == jC { 1260 nf_qmul(dq2, ((sgq as i64) + (bq+jC*4)*8) as *i64, qo9) 1261 nf_qnorm(qo9) 1262 acqx=qo9[0]; acqy=qo9[1]; acqz=qo9[2]; acqw=qo9[3] 1263 actx=nkx; acty=nky; actz=nkz 1264 } 1265 if anc == jF { 1266 nf_qmul(dq2, ((sgq as i64) + (bq+jF*4)*8) as *i64, qo9) 1267 nf_qnorm(qo9) 1268 acqx=qo9[0]; acqy=qo9[1]; acqz=qo9[2]; acqw=qo9[3] 1269 actx=tx; acty=ty; actz=tz 1270 } 1271 qc9[0]=acqx; qc9[1]=acqy; qc9[2]=acqz; qc9[3]=acqw 1272 nf_qmul(qc9, qr9, qo9) 1273 nf_qnorm(qo9) 1274 nqx=qo9[0]; nqy=qo9[1]; nqz=qo9[2]; nqw=qo9[3] 1275 nf_qrotv(qc9, rlx, rly, rlz, rv, scr) 1276 ntx = actx + rv[0] 1277 nty = acty + rv[1] 1278 ntz = actz + rv[2] 1279 } 1280 // repack this joint at this sample: D = q' x conj(static), dt=(t'-static)/100 1281 qc9[0]=nqx; qc9[1]=nqy; qc9[2]=nqz; qc9[3]=nqw 1282 nf_qconj(((gqs as i64) + jx9*32) as *i64, qr9) 1283 nf_qmul(qc9, qr9, qo9) 1284 nf_qnorm(qo9) 1285 var dtx9: i64 = (ntx - gts[jx9*3]) / 100 1286 var dty9: i64 = (nty - gts[jx9*3+1]) / 100 1287 var dtz9: i64 = (ntz - gts[jx9*3+2]) / 100 1288 if dtx9 > A_MAGIC_32767 { dtx9 = A_MAGIC_32767 } 1289 if dtx9 < 0 - A_MAGIC_32767 { dtx9 = 0 - A_MAGIC_32767 } 1290 if dty9 > A_MAGIC_32767 { dty9 = A_MAGIC_32767 } 1291 if dty9 < 0 - A_MAGIC_32767 { dty9 = 0 - A_MAGIC_32767 } 1292 if dtz9 > A_MAGIC_32767 { dtz9 = A_MAGIC_32767 } 1293 if dtz9 < 0 - A_MAGIC_32767 { dtz9 = 0 - A_MAGIC_32767 } 1294 let kb9: i64 = 1 + jx9*(3 + nk*2) + 3 + s5*2 1295 let tms9: i64 = s5*33 1296 ap2[kb9] = (tms9 & A_MAGIC_65535) | ((qo9[0] & A_MAGIC_65535) << 16) | ((qo9[1] & A_MAGIC_65535) << 32) | ((qo9[2] & A_MAGIC_65535) << 48) 1297 ap2[kb9+1] = (qo9[3] & A_MAGIC_65535) | ((dtx9 & A_MAGIC_65535) << 16) | ((dty9 & A_MAGIC_65535) << 32) | ((dtz9 & A_MAGIC_65535) << 48) 1298 } 1299 jx9 = jx9 + 1 1300 } 1301 } 1302 s5 = s5 + 1 1303 } 1304 side = side + 1 1305 } 1306 } } } 1307 aw("footik_planted_samples=" as *u8); an(planted); aw("\n" as *u8) 1308 // EDGE-STRETCH QUALITY METRIC (permanent): skin every vert with the s=106 transforms, 1309 // measure each triangle edge's skinned/bind length ratio. Tears = edges stretched >2x; 1310 // the worst offender's joint slots NAME the culprit pair (this instrument found the 1311 // eye-bound-face defect's successors instead of another day of guessing). 1312 let vwo9: i64 = nxa_find(nb, nlen2, nxa_tag4("VERT" as *u8)) 1313 let two9: i64 = nxa_find(nb, nlen2, nxa_tag4("TRIS" as *u8)) 1314 let kwo9: i64 = nxa_find(nb, nlen2, nxa_tag4("SKIN" as *u8)) 1315 if vwo9 >= 0 { if two9 >= 0 { if kwo9 >= 0 { 1316 let nv9: i64 = nh[vwo9] 1317 let nt9: i64 = nh[two9] 1318 let vx9: *i64 = ((nb as i64) + vwo9*8 + 8) as *i64 1319 let tr9: *i64 = ((nb as i64) + two9*8 + 8) as *i64 1320 let sk9: *i64 = ((nb as i64) + kwo9*8 + 8) as *i64 1321 let sp: *i64 = sys_mmap(nv9*24 + 64) as *i64 1322 let dqs: *i64 = sys_mmap(64) as *i64 1323 var v8: i64 = 0 1324 while v8 < nv9 { 1325 var px: i64 = 0 1326 var py: i64 = 0 1327 var pz: i64 = 0 1328 var s8: i64 = 0 1329 while s8 < 4 { 1330 let jj8: i64 = sk9[v8*8+s8] 1331 let ww8: i64 = sk9[v8*8+4+s8] 1332 if ww8 > 0 { if jj8 >= 0 { if jj8 < nj { 1333 nf_qconj(((gqs as i64) + jj8*32) as *i64, cq) 1334 nf_qmul(((gqx as i64) + jj8*32) as *i64, cq, dqs) 1335 nf_qnorm(dqs) 1336 nf_qrotv(dqs, vx9[v8*3] - gts[jj8*3], vx9[v8*3+1] - gts[jj8*3+1], vx9[v8*3+2] - gts[jj8*3+2], rv, scr) 1337 px = px + ww8*(rv[0] + gtx[jj8*3]) / A_MAGIC_4096 1338 py = py + ww8*(rv[1] + gtx[jj8*3+1]) / A_MAGIC_4096 1339 pz = pz + ww8*(rv[2] + gtx[jj8*3+2]) / A_MAGIC_4096 1340 } } } 1341 s8 = s8 + 1 1342 } 1343 sp[v8*3] = px 1344 sp[v8*3+1] = py 1345 sp[v8*3+2] = pz 1346 v8 = v8 + 1 1347 } 1348 var over2: i64 = 0 1349 var worst: i64 = 0 1350 let pcnt: *i64 = sys_mmap(nj*nj*8 + 64) as *i64 1351 var t8: i64 = 0 1352 while t8 < nt9 { 1353 var e8: i64 = 0 1354 while e8 < 3 { 1355 let va: i64 = tr9[t8*3 + e8] 1356 var vb: i64 = tr9[t8*3] 1357 if e8 < 2 { vb = tr9[t8*3 + e8 + 1] } 1358 let bx9: i64 = vx9[va*3] - vx9[vb*3] 1359 let by9: i64 = vx9[va*3+1] - vx9[vb*3+1] 1360 let bz9: i64 = vx9[va*3+2] - vx9[vb*3+2] 1361 let sx9: i64 = sp[va*3] - sp[vb*3] 1362 let sy9: i64 = sp[va*3+1] - sp[vb*3+1] 1363 let sz9: i64 = sp[va*3+2] - sp[vb*3+2] 1364 let bl: i64 = nf_isqrt(bx9*bx9 + by9*by9 + bz9*bz9) 1365 let sl: i64 = nf_isqrt(sx9*sx9 + sy9*sy9 + sz9*sz9) 1366 if bl > 20 { 1367 let ratio: i64 = sl*100/bl 1368 if ratio > 200 { 1369 over2 = over2 + 1 1370 // dominant joint of each endpoint -> pair bucket 1371 var da: i64 = 0 1372 var sa2: i64 = 1 1373 while sa2 < 4 { if sk9[va*8+4+sa2] > sk9[va*8+4+da] { da = sa2 } sa2 = sa2 + 1 } 1374 var db: i64 = 0 1375 var sb2: i64 = 1 1376 while sb2 < 4 { if sk9[vb*8+4+sb2] > sk9[vb*8+4+db] { db = sb2 } sb2 = sb2 + 1 } 1377 var ja0: i64 = sk9[va*8+da] 1378 var jb0: i64 = sk9[vb*8+db] 1379 if ja0 > jb0 { let tt0: i64 = ja0; ja0 = jb0; jb0 = tt0 } 1380 if ja0 >= 0 { if jb0 < nj { pcnt[ja0*nj + jb0] = pcnt[ja0*nj + jb0] + 1 } } 1381 } 1382 if ratio > worst { worst = ratio } 1383 } 1384 e8 = e8 + 1 1385 } 1386 t8 = t8 + 1 1387 } 1388 aw("stretch_over2x=" as *u8); an(over2) 1389 aw(" worst_x100=" as *u8); an(worst); aw("\n" as *u8) 1390 // top-5 tearing joint pairs by edge count 1391 var rank: i64 = 0 1392 while rank < 5 { 1393 var bc: i64 = 0 1394 var bja: i64 = 0 - 1 1395 var bjb: i64 = 0 - 1 1396 var pa0: i64 = 0 1397 while pa0 < nj { 1398 var pb0: i64 = pa0 1399 while pb0 < nj { 1400 if pcnt[pa0*nj + pb0] > bc { bc = pcnt[pa0*nj + pb0]; bja = pa0; bjb = pb0 } 1401 pb0 = pb0 + 1 1402 } 1403 pa0 = pa0 + 1 1404 } 1405 if bja >= 0 { 1406 aw(" PAIR n=" as *u8); an(bc) 1407 aw(" " as *u8) 1408 a_wname(fb, G[A_NO + jmi[bja]], G[A_NL + jmi[bja]]) 1409 aw(" <> " as *u8) 1410 a_wname(fb, G[A_NO + jmi[bjb]], G[A_NL + jmi[bjb]]) 1411 aw("\n" as *u8) 1412 pcnt[bja*nj + bjb] = 0 1413 } 1414 rank = rank + 1 1415 } 1416 } } } 1417 aw("baked tracks=" as *u8); an(nj) 1418 aw(" keys_per_track=" as *u8); an(nk) 1419 aw(" clamped=" as *u8); an(clamped) 1420 aw(" wrap_events=" as *u8); an(wrapn); aw("\n" as *u8) 1421 // CORRECTED SKEL: FK-true world positions for ALL joints replace the placeholder- 1422 // propagated binds (19 unclustered joints incl the hip chain sat at a parent's bind or 1423 // 0,0,0) -- the baked deltas pivot about gts, so the shipped SKEL must too, exactly. 1424 let swl2: i64 = 1 + nj*8 1425 let cskel: *i64 = sys_mmap(swl2*8 + 64) as *i64 1426 cskel[0] = nj 1427 var jc5: i64 = 0 1428 while jc5 < nj { 1429 cskel[1 + jc5*8] = skb[jc5*8] 1430 cskel[1 + jc5*8 + 1] = gts[jc5*3] 1431 cskel[1 + jc5*8 + 2] = gts[jc5*3+1] 1432 cskel[1 + jc5*8 + 3] = gts[jc5*3+2] 1433 cskel[1 + jc5*8 + 4] = 0 1434 cskel[1 + jc5*8 + 5] = 0 1435 cskel[1 + jc5*8 + 6] = 0 1436 cskel[1 + jc5*8 + 7] = A_MAGIC_4096 1437 jc5 = jc5 + 1 1438 } 1439 // copy the sections from in.nxa5 (SKEL swapped for the corrected one), append ANIM 1440 let ons: i64 = nh[2] 1441 let otoc: *i64 = ((nb as i64) + 32) as *i64 1442 let ns2: i64 = ons + 1 1443 let hdr: *i64 = sys_mmap(64) as *i64 1444 let toc: *i64 = sys_mmap(A_MAGIC_1024) as *i64 1445 var o: i64 = 32 + ns2*32 1446 var ti: i64 = 0 1447 while ti < ons { 1448 toc[ti*4] = otoc[ti*4] 1449 toc[ti*4+1] = o 1450 toc[ti*4+2] = otoc[ti*4+2] 1451 toc[ti*4+3] = otoc[ti*4+3] 1452 if otoc[ti*4] == nxa_tag4("SKEL" as *u8) { toc[ti*4+3] = nxa_check2(1, cskel, swl2) } 1453 o = o + otoc[ti*4+2]*8 1454 ti = ti + 1 1455 } 1456 toc[ti*4] = nxa_tag4("ANIM" as *u8) 1457 toc[ti*4+1] = o 1458 toc[ti*4+2] = awl 1459 toc[ti*4+3] = nxa_check2(1, ap2, awl) 1460 hdr[0] = nxa_magic() 1461 hdr[1] = NXA_VER 1462 hdr[2] = ns2 1463 hdr[3] = nxa_check2(1, toc, ns2*4) 1464 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4) 1465 if fd < 0 { aw("open out failed\n" as *u8); return 9 } 1466 sys_write(fd, hdr as *u8, 32) 1467 sys_write(fd, toc as *u8, ns2*32) 1468 var ci3: i64 = 0 1469 while ci3 < ons { 1470 if otoc[ci3*4] == nxa_tag4("SKEL" as *u8) { sys_write(fd, cskel as *u8, swl2*8) } 1471 if otoc[ci3*4] != nxa_tag4("SKEL" as *u8) { sys_write(fd, ((nb as i64) + otoc[ci3*4+1]) as *u8, otoc[ci3*4+2]*8) } 1472 ci3 = ci3 + 1 1473 } 1474 sys_write(fd, ap2 as *u8, awl*8) 1475 sys_close(fd) 1476 aw("NXA6 written tracks=" as *u8); an(nj) 1477 aw(" duration_ms=" as *u8); an(maxms); aw("\n" as *u8) 1478 return 0 1479}