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}