code wiki / (root) / nx_nxa_anim.nx

nx_nxa_anim.nx source

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