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}