nx_nxa_skin.nx source
↩ module page · 950 lines · 37119 B
1// nx_nxa_skin.nx -- RIG IMPORTER: fills the NXA's SKEL + SKIN sections from a binary FBX.
2// Walks the FBX id-graph (Model/LimbNode joints, Deformer Clusters with Indexes/Weights/
3// TransformLink bind matrices, the Connections table) and emits a 5-section NXA:
4// VERT/TRIS/CLUS copied from the mesh import, SKEL = joints [parent][t mm][quat q12=identity]
5// (delta-LBS convention: animation is applied RELATIVE to bind, so identity bind quats are
6// exact), SKIN = per-vertex top-4 joints + q12 weights normalized to sum 4096.
7// usage: nx_nxa_skin <in.fbx> <in.nxa> <out.nxa>
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_zlib_wrap.nx"
11import "nx_nxa.nx"
12import "nx_nxa_fk.nx"
13const G_MAGIC_20000: i64 = 20000
14const G_MAGIC_2047: i64 = 2047
15const G_MAGIC_1048575: i64 = 1048575
16const G_MAGIC_4503599627370496: i64 = 4503599627370496
17const G_MAGIC_4090: i64 = 4090
18const G_MAGIC_99990: i64 = 99990
19const G_MAGIC_4096: i64 = 4096
20const G_MAGIC_7500: i64 = 7500
21const G_MAGIC_500000: i64 = 500000
22const G_MAGIC_4611686018427387903: i64 = 4611686018427387903
23
24// G layout (word offsets): 0=mcount 1=ccount 2=conncount 3=bestVertAlen 4=bestGeomId 5=skincount
25const G_MO: i64 = 16 // model ids x4096
26const G_LO: i64 = 4112 // limb flags x4096
27const G_CID: i64 = 8208 // cluster ids x512
28const G_CIX: i64 = 8720 // cluster Indexes meta (off,alen,enc,clen) x512x4
29const G_CW: i64 = 10768 // cluster Weights meta x512x4
30const G_CT: i64 = 12816 // cluster TransformLink meta x512x4
31const G_CONN: i64 = 14864 // connection pairs (src,dst) x100000x2
32const G_SID: i64 = 214864 // Skin deformer ids x64
33const G_PR: i64 = 216000 // per-model PreRotation millideg x3
34const G_LR: i64 = 228288 // per-model Lcl Rotation default millideg x3
35const G_LT: i64 = 240576 // per-model Lcl Translation default units x3
36const G_PF: i64 = 252864 // per-model flags: 1=pre 2=lclrot 4=lclt
37const G_WORDS: i64 = 258000
38
39func skw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
40func skn(v: i64) -> i64 {
41 let t: *u8 = sys_mmap(32) as *u8
42 var m: i64 = v; var w: i64 = 0
43 if m<0 { t[w]=45 as u8; w=w+1; m=0-m }
44 if m==0 { t[w]=48 as u8; sys_write(1,t,w+1); return 0 }
45 let d: *u8 = sys_mmap(32) as *u8
46 var k: i64=0
47 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
48 var j: i64=0
49 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 }
50 sys_write(1,t,w); return 0
51}
52func sk_u32(b: *u8, o: i64) -> i64 {
53 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8)
54 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24)
55}
56func sk_u64(b: *u8, o: i64) -> i64 { return sk_u32(b, o) | (sk_u32(b, o + 4) << 32) }
57// IEEE754 double -> value x scale (scale=1000 for mm, 4096 for q12 weights). DRY debt vs
58// nx_fbx_measure's copy -- extract to a shared nx_f64.nx when the third user appears.
59func sk_f64(b: *u8, o: i64, scale: i64) -> i64 {
60 let lo: i64 = sk_u32(b, o)
61 let hi: i64 = sk_u32(b, o + 4)
62 let sign: i64 = (hi >> 31) & 1
63 let expo: i64 = (hi >> 20) & G_MAGIC_2047
64 if expo == 0 { return 0 }
65 var mant: i64 = ((hi & G_MAGIC_1048575) << 32) | lo
66 mant = mant | G_MAGIC_4503599627370496
67 let sh: i64 = expo - 1023
68 var v: i64 = 0
69 if sh >= 52 { v = mant * scale * (1 << (sh - 52)) }
70 if sh < 52 {
71 let rs: i64 = 52 - sh
72 if rs > 62 { return 0 }
73 v = (mant * scale) >> rs
74 }
75 if sign == 1 { return 0 - v }
76 return v
77}
78
79// recursive walk; ctx = current Cluster index (child arrays attach to it) or -1;
80// gctx = current Geometry node id (Vertices children identify OUR mesh's geometry) or 0
81func sk_walk(b: *u8, flen: i64, off0: i64, big: i64, G: *i64, ctx: i64, gctx: i64, mctx: i64) -> i64 {
82 let off: i64 = off0
83 var endo: i64 = 0
84 var nprops: i64 = 0
85 var plen: i64 = 0
86 var nlen: i64 = 0
87 var p: i64 = 0
88 if big == 1 {
89 endo = sk_u64(b, off)
90 nprops = sk_u64(b, off + 8)
91 plen = sk_u64(b, off + 16)
92 nlen = (b[off + 24] & 0xff) as i64
93 p = off + 25
94 }
95 if big == 0 {
96 endo = sk_u32(b, off)
97 nprops = sk_u32(b, off + 4)
98 plen = sk_u32(b, off + 8)
99 nlen = (b[off + 12] & 0xff) as i64
100 p = off + 13
101 }
102 if endo == 0 { return off }
103 if endo > flen { return flen }
104 var kind: i64 = 0
105 if nlen == 5 { if (b[p]&0xff)==77 { if (b[p+4]&0xff)==108 { kind = 1 } } } // Model
106 if nlen == 8 {
107 if (b[p]&0xff)==68 { if (b[p+7]&0xff)==114 { kind = 2 } } // Deformer
108 if (b[p]&0xff)==71 { if (b[p+7]&0xff)==121 { kind = 7 } } // Geometry
109 if (b[p]&0xff)==86 { if (b[p+7]&0xff)==115 { kind = 8 } } // Vertices
110 }
111 if nlen == 7 {
112 if (b[p]&0xff)==73 { if (b[p+6]&0xff)==115 { kind = 3 } } // Indexes
113 if (b[p]&0xff)==87 { if (b[p+6]&0xff)==115 { kind = 4 } } // Weights
114 }
115 if nlen == 13 { if (b[p]&0xff)==84 { if (b[p+12]&0xff)==107 { kind = 5 } } } // TransformLink
116 if nlen == 1 { if (b[p]&0xff)==67 { kind = 6 } } // C
117 if nlen == 1 { if (b[p]&0xff)==80 { kind = 9 } } // P (Properties70)
118 p = p + nlen
119 let pstart: i64 = p
120 var id1: i64 = 0
121 var id2: i64 = 0
122 var lseen: i64 = 0
123 var s1l: i64 = 0
124 var s1o: i64 = 0
125 var s2l: i64 = 0
126 var s2o: i64 = 0
127 var sseen: i64 = 0
128 var dseen: i64 = 0
129 var dv1: i64 = 0
130 var dv2: i64 = 0
131 var dv3: i64 = 0
132 var arro: i64 = 0
133 var arrl: i64 = 0
134 var arre: i64 = 0
135 var arrc: i64 = 0
136 var pi: i64 = 0
137 var stop: i64 = 0
138 while pi < nprops {
139 if stop == 0 {
140 let t: i64 = (b[p] & 0xff) as i64
141 p = p + 1
142 var adv: i64 = 0 - 1
143 if t == 83 {
144 let l: i64 = sk_u32(b, p)
145 sseen = sseen + 1
146 if sseen == 1 { s1o = p + 4; s1l = l }
147 if sseen == 2 { s2o = p + 4; s2l = l }
148 adv = 4 + l
149 }
150 if t == 82 { adv = 4 + sk_u32(b, p) }
151 if t == 89 { adv = 2 }
152 if t == 67 { adv = 1 }
153 if t == 73 { adv = 4 }
154 if t == 70 { adv = 4 }
155 if t == 68 {
156 if dseen == 0 { dv1 = p }
157 if dseen == 1 { dv2 = p }
158 if dseen == 2 { dv3 = p }
159 dseen = dseen + 1
160 adv = 8
161 }
162 if t == 76 {
163 lseen = lseen + 1
164 if lseen == 1 { id1 = sk_u64(b, p) }
165 if lseen == 2 { id2 = sk_u64(b, p) }
166 adv = 8
167 }
168 var isarr: i64 = 0
169 if t == 100 { isarr = 1 }
170 if t == 102 { isarr = 1 }
171 if t == 108 { isarr = 1 }
172 if t == 105 { isarr = 1 }
173 if t == 98 { isarr = 1 }
174 if isarr == 1 {
175 arrl = sk_u32(b, p)
176 arre = sk_u32(b, p + 4)
177 arrc = sk_u32(b, p + 8)
178 arro = p + 12
179 adv = 12 + arrc
180 }
181 if adv < 0 { stop = 1 }
182 if adv >= 0 { p = p + adv }
183 }
184 pi = pi + 1
185 }
186 var childctx: i64 = ctx
187 var childg: i64 = gctx
188 var childm: i64 = mctx
189 if kind == 1 { if G[0] < G_MAGIC_4090 {
190 G[G_MO + G[0]] = id1
191 var lf: i64 = 0
192 if s2l == 8 { if (b[s2o]&0xff)==76 { if (b[s2o+7]&0xff)==101 { lf = 1 } } }
193 G[G_LO + G[0]] = lf
194 childm = G[0]
195 G[0] = G[0] + 1
196 } }
197 if kind == 2 { if s2l == 7 { if G[1] < 510 {
198 G[G_CID + G[1]] = id1
199 childctx = G[1]
200 G[1] = G[1] + 1
201 } } }
202 if kind == 2 { if s2l == 4 { if G[5] < 60 {
203 G[G_SID + G[5]] = id1
204 G[5] = G[5] + 1
205 } } }
206 if kind == 7 { childg = id1 }
207 if kind == 8 { if arrl > G[3] { G[3] = arrl; G[4] = gctx } }
208 if kind == 3 { if ctx >= 0 {
209 G[G_CIX + ctx*4] = arro
210 G[G_CIX + ctx*4 + 1] = arrl
211 G[G_CIX + ctx*4 + 2] = arre
212 G[G_CIX + ctx*4 + 3] = arrc
213 } }
214 if kind == 4 { if ctx >= 0 {
215 G[G_CW + ctx*4] = arro
216 G[G_CW + ctx*4 + 1] = arrl
217 G[G_CW + ctx*4 + 2] = arre
218 G[G_CW + ctx*4 + 3] = arrc
219 } }
220 if kind == 5 { if ctx >= 0 {
221 G[G_CT + ctx*4] = arro
222 G[G_CT + ctx*4 + 1] = arrl
223 G[G_CT + ctx*4 + 2] = arre
224 G[G_CT + ctx*4 + 3] = arrc
225 } }
226 if kind == 6 { if G[2] < G_MAGIC_99990 {
227 G[G_CONN + G[2]*2] = id1
228 G[G_CONN + G[2]*2 + 1] = id2
229 G[2] = G[2] + 1
230 } }
231 // Properties70 P entry under a Model: name = FIRST S prop; vector value = 3 D props.
232 // Matched by (len, first, last): PreRotation(11 P..n), Lcl Rotation(12 L..n),
233 // Lcl Translation(15 L..n). Angles degrees->millideg, translation units x1000.
234 if kind == 9 { if mctx >= 0 { if dseen >= 3 {
235 var pk: i64 = 0
236 if s1l == 11 { if (b[s1o]&0xff)==80 { if (b[s1o+10]&0xff)==110 { pk = 1 } } }
237 if s1l == 12 { if (b[s1o]&0xff)==76 { if (b[s1o+11]&0xff)==110 { pk = 2 } } }
238 if s1l == 15 { if (b[s1o]&0xff)==76 { if (b[s1o+14]&0xff)==110 { pk = 3 } } }
239 if pk == 1 {
240 G[G_PR + mctx*3] = sk_f64(b, dv1, 1000)
241 G[G_PR + mctx*3 + 1] = sk_f64(b, dv2, 1000)
242 G[G_PR + mctx*3 + 2] = sk_f64(b, dv3, 1000)
243 G[G_PF + mctx] = G[G_PF + mctx] | 1
244 }
245 if pk == 2 {
246 G[G_LR + mctx*3] = sk_f64(b, dv1, 1000)
247 G[G_LR + mctx*3 + 1] = sk_f64(b, dv2, 1000)
248 G[G_LR + mctx*3 + 2] = sk_f64(b, dv3, 1000)
249 G[G_PF + mctx] = G[G_PF + mctx] | 2
250 }
251 if pk == 3 {
252 G[G_LT + mctx*3] = sk_f64(b, dv1, 1000)
253 G[G_LT + mctx*3 + 1] = sk_f64(b, dv2, 1000)
254 G[G_LT + mctx*3 + 2] = sk_f64(b, dv3, 1000)
255 G[G_PF + mctx] = G[G_PF + mctx] | 4
256 }
257 } } }
258 p = pstart + plen
259 var sent: i64 = 13
260 if big == 1 { sent = 25 }
261 while p < endo - sent {
262 p = sk_walk(b, flen, p, big, G, childctx, childg, childm)
263 if p >= flen { return flen }
264 }
265 return endo
266}
267
268// inflate-or-raw helper for FBX arrays
269func sk_arr(b: *u8, off: i64, alen: i64, enc: i64, clen: i64, esz: i64) -> *u8 {
270 if enc == 0 { return ((b as i64) + off) as *u8 }
271 let zr: *NxZlibResult = nx_zlib_inflate(((b as i64) + off) as *u8, clen, alen*esz + G_MAGIC_4096)
272 if zr.error_code != 0 { return 0 as *u8 }
273 return zr.output_data
274}
275
276func main(argc: i64, argv: *i64) -> i64 {
277 if argc < 4 { skw("usage: nx_nxa_skin <in.fbx> <in.nxa> <out.nxa>\n" as *u8); return 2 }
278 let lpf: *i64 = sys_mmap(16) as *i64
279 let fb: *u8 = sys_map_file(argv[1] as *u8, lpf)
280 let flen: i64 = lpf[0]
281 if flen < 64 { skw("fbx unreadable\n" as *u8); return 3 }
282 let ver: i64 = sk_u32(fb, 23)
283 var big: i64 = 0
284 if ver >= G_MAGIC_7500 { big = 1 }
285 let G: *i64 = sys_mmap(G_WORDS*8) as *i64
286 var pos: i64 = 27
287 var guard: i64 = 0
288 while pos < flen - 200 {
289 if guard > G_MAGIC_500000 { break }
290 let e: i64 = sk_walk(fb, flen, pos, big, G, 0 - 1, 0, 0 - 1)
291 if e <= pos { break }
292 pos = e
293 guard = guard + 1
294 }
295 let mc: i64 = G[0]
296 let cc: i64 = G[1]
297 let nc: i64 = G[2]
298 let gid: i64 = G[4]
299 skw("models=" as *u8); skn(mc)
300 skw(" clusters=" as *u8); skn(cc)
301 skw(" conns=" as *u8); skn(nc)
302 skw(" skins=" as *u8); skn(G[5]); skw("\n" as *u8)
303 // OUR mesh only: valid Skins connect to the geometry that carries the LARGEST Vertices
304 // (the body we imported); clusters of other meshes index foreign control points and would
305 // stamp weights onto wrong vertices -- filter them out entirely.
306 let sval: *i64 = sys_mmap(64*8 + 64) as *i64
307 var sv0: i64 = 0
308 while sv0 < G[5] {
309 sval[sv0] = 0
310 var k9: i64 = 0
311 while k9 < nc {
312 if G[G_CONN + k9*2] == G[G_SID + sv0] { if G[G_CONN + k9*2 + 1] == gid {
313 sval[sv0] = 1
314 k9 = nc
315 } }
316 k9 = k9 + 1
317 }
318 sv0 = sv0 + 1
319 }
320 // cluster validity: cluster -> (valid Skin)
321 let cval: *i64 = sys_mmap(cc*8 + 64) as *i64
322 var cv0: i64 = 0
323 while cv0 < cc {
324 cval[cv0] = 0
325 var k8: i64 = 0
326 while k8 < nc {
327 if G[G_CONN + k8*2] == G[G_CID + cv0] {
328 let d8: i64 = G[G_CONN + k8*2 + 1]
329 var s8: i64 = 0
330 while s8 < G[5] {
331 if G[G_SID + s8] == d8 { if sval[s8] == 1 {
332 cval[cv0] = 1
333 s8 = 64
334 k8 = nc
335 } }
336 s8 = s8 + 1
337 }
338 }
339 k8 = k8 + 1
340 }
341 cv0 = cv0 + 1
342 }
343 // joints = limb models in encounter order
344 let jmap: *i64 = sys_mmap(mc*8 + 64) as *i64
345 var nj: i64 = 0
346 var m0: i64 = 0
347 while m0 < mc {
348 jmap[m0] = 0 - 1
349 if G[G_LO + m0] == 1 { jmap[m0] = nj; nj = nj + 1 }
350 m0 = m0 + 1
351 }
352 // parent per joint via Connections (child -> parent)
353 let jpar: *i64 = sys_mmap(nj*8 + 64) as *i64
354 var j0: i64 = 0
355 while j0 < nj { jpar[j0] = 0 - 1; j0 = j0 + 1 }
356 var m1: i64 = 0
357 while m1 < mc {
358 if jmap[m1] >= 0 {
359 let myid: i64 = G[G_MO + m1]
360 var k: i64 = 0
361 while k < nc {
362 if G[G_CONN + k*2] == myid {
363 let dst: i64 = G[G_CONN + k*2 + 1]
364 var m2: i64 = 0
365 while m2 < mc {
366 if G[G_MO + m2] == dst { if jmap[m2] >= 0 {
367 jpar[jmap[m1]] = jmap[m2]
368 m2 = mc
369 k = nc
370 } }
371 m2 = m2 + 1
372 }
373 }
374 k = k + 1
375 }
376 }
377 m1 = m1 + 1
378 }
379 // cluster -> joint (Model(limb) --conn--> Cluster) + bind translation from TransformLink
380 let cjoint: *i64 = sys_mmap(cc*8 + 64) as *i64
381 let jbind: *i64 = sys_mmap(nj*24 + 64) as *i64
382 let jreal: *i64 = sys_mmap(nj*8 + 64) as *i64
383 var c0: i64 = 0
384 while c0 < cc { cjoint[c0] = 0 - 1; c0 = c0 + 1 }
385 var c1: i64 = 0
386 while c1 < cc {
387 let cid: i64 = G[G_CID + c1]
388 var k2: i64 = 0
389 while k2 < nc {
390 if G[G_CONN + k2*2 + 1] == cid {
391 let src: i64 = G[G_CONN + k2*2]
392 var m3: i64 = 0
393 while m3 < mc {
394 if G[G_MO + m3] == src { if jmap[m3] >= 0 {
395 cjoint[c1] = jmap[m3]
396 m3 = mc
397 k2 = nc
398 } }
399 m3 = m3 + 1
400 }
401 }
402 k2 = k2 + 1
403 }
404 // bind translation: TransformLink col-major elems 12,13,14 (joint->world at bind)
405 if cjoint[c1] >= 0 { if G[G_CT + c1*4 + 1] == 16 {
406 let tl: *u8 = sk_arr(fb, G[G_CT + c1*4], 16, G[G_CT + c1*4 + 2], G[G_CT + c1*4 + 3], 8)
407 if (tl as i64) != 0 {
408 let jj: i64 = cjoint[c1]
409 jbind[jj*3] = sk_f64(tl, 12*8, 1000)
410 jbind[jj*3+1] = sk_f64(tl, 13*8, 1000)
411 jbind[jj*3+2] = sk_f64(tl, 14*8, 1000)
412 jreal[jj] = 1
413 }
414 } }
415 c1 = c1 + 1
416 }
417 // v2: STATIC FK -> TRUE bind positions for ALL joints, replacing the old placeholder
418 // propagation (which parked unclustered joints on an ancestor; auto-skin against those
419 // degenerate segments mis-bound flesh -- shoulder shards under real per-joint mocap).
420 // Local = PreRotation x LclRotation chained down parents; non-limb ancestor Models
421 // compose into a per-root prefix. VALIDATED against the real cluster binds before use.
422 let mpar: *i64 = sys_mmap(mc*8 + 64) as *i64
423 var mp0: i64 = 0
424 while mp0 < mc { mpar[mp0] = 0 - 1; mp0 = mp0 + 1 }
425 var mp1: i64 = 0
426 while mp1 < mc {
427 let myid2: i64 = G[G_MO + mp1]
428 var kp: i64 = 0
429 while kp < nc {
430 if G[G_CONN + kp*2] == myid2 {
431 let dstp: i64 = G[G_CONN + kp*2 + 1]
432 var mp2: i64 = 0
433 while mp2 < mc {
434 if G[G_MO + mp2] == dstp { mpar[mp1] = mp2; mp2 = mc; kp = nc }
435 mp2 = mp2 + 1
436 }
437 }
438 kp = kp + 1
439 }
440 mp1 = mp1 + 1
441 }
442 let jmi: *i64 = sys_mmap(G_MAGIC_4096*8 + 64) as *i64
443 var mj0: i64 = 0
444 while mj0 < mc {
445 if jmap[mj0] >= 0 { jmi[jmap[mj0]] = mj0 }
446 mj0 = mj0 + 1
447 }
448 let scr: *i64 = sys_mmap(256) as *i64
449 let pq: *i64 = sys_mmap(nj*32 + 64) as *i64
450 let sq: *i64 = sys_mmap(nj*32 + 64) as *i64
451 var jq0: i64 = 0
452 while jq0 < nj {
453 let mi: i64 = jmi[jq0]
454 nf_eul2q(G[G_PR + mi*3], G[G_PR + mi*3 + 1], G[G_PR + mi*3 + 2], ((pq as i64) + jq0*32) as *i64, scr)
455 nf_eul2q(G[G_LR + mi*3], G[G_LR + mi*3 + 1], G[G_LR + mi*3 + 2], ((sq as i64) + jq0*32) as *i64, scr)
456 jq0 = jq0 + 1
457 }
458 let topo: *i64 = sys_mmap(nj*8 + 64) as *i64
459 let done: *i64 = sys_mmap(nj*8 + 64) as *i64
460 var tn: i64 = 0
461 var pass2: i64 = 0
462 while pass2 < nj {
463 if tn < nj {
464 var jp0: i64 = 0
465 while jp0 < nj {
466 if done[jp0] == 0 {
467 var ok: i64 = 0
468 if jpar[jp0] < 0 { ok = 1 }
469 if jpar[jp0] >= 0 { if done[jpar[jp0]] == 1 { ok = 1 } }
470 if ok == 1 { topo[tn] = jp0; tn = tn + 1; done[jp0] = 1 }
471 }
472 jp0 = jp0 + 1
473 }
474 }
475 pass2 = pass2 + 1
476 }
477 if tn != nj { skw("PARENT-CYCLE -- refusing\n" as *u8); return 7 }
478 let gqs: *i64 = sys_mmap(nj*32 + 64) as *i64
479 let gts: *i64 = sys_mmap(nj*24 + 64) as *i64
480 let lq: *i64 = sys_mmap(64) as *i64
481 let rv: *i64 = sys_mmap(64) as *i64
482 let prefq: *i64 = sys_mmap(nj*32 + 64) as *i64
483 let preft: *i64 = sys_mmap(nj*24 + 64) as *i64
484 let anc: *i64 = sys_mmap(32*8 + 64) as *i64
485 let aqq: *i64 = sys_mmap(64) as *i64
486 let bqq: *i64 = sys_mmap(64) as *i64
487 let tq2: *i64 = sys_mmap(64) as *i64
488 var jr0: i64 = 0
489 while jr0 < nj {
490 prefq[jr0*4] = 0
491 prefq[jr0*4+1] = 0
492 prefq[jr0*4+2] = 0
493 prefq[jr0*4+3] = G_MAGIC_4096
494 if jpar[jr0] < 0 {
495 var nanc: i64 = 0
496 var cm: i64 = mpar[jmi[jr0]]
497 var hops: i64 = 0
498 while cm >= 0 {
499 if nanc < 30 { anc[nanc] = cm; nanc = nanc + 1 }
500 cm = mpar[cm]
501 hops = hops + 1
502 if hops > 30 { cm = 0 - 1 }
503 }
504 var pi2: i64 = nanc - 1
505 while pi2 >= 0 {
506 let am: i64 = anc[pi2]
507 nf_eul2q(G[G_PR + am*3], G[G_PR + am*3 + 1], G[G_PR + am*3 + 2], aqq, scr)
508 nf_eul2q(G[G_LR + am*3], G[G_LR + am*3 + 1], G[G_LR + am*3 + 2], bqq, scr)
509 nf_qmul(aqq, bqq, tq2)
510 nf_qnorm(tq2)
511 nf_qrotv(((prefq as i64) + jr0*32) as *i64, G[G_LT + am*3], G[G_LT + am*3 + 1], G[G_LT + am*3 + 2], rv, scr)
512 preft[jr0*3] = preft[jr0*3] + rv[0]
513 preft[jr0*3+1] = preft[jr0*3+1] + rv[1]
514 preft[jr0*3+2] = preft[jr0*3+2] + rv[2]
515 nf_qmul(((prefq as i64) + jr0*32) as *i64, tq2, aqq)
516 nf_qnorm(aqq)
517 prefq[jr0*4] = aqq[0]
518 prefq[jr0*4+1] = aqq[1]
519 prefq[jr0*4+2] = aqq[2]
520 prefq[jr0*4+3] = aqq[3]
521 pi2 = pi2 - 1
522 }
523 }
524 jr0 = jr0 + 1
525 }
526 var ti9: i64 = 0
527 while ti9 < nj {
528 let j9: i64 = topo[ti9]
529 let mi9: i64 = jmi[j9]
530 nf_qmul(((pq as i64) + j9*32) as *i64, ((sq as i64) + j9*32) as *i64, lq)
531 nf_qnorm(lq)
532 let pj9: i64 = jpar[j9]
533 if pj9 < 0 {
534 nf_qmul(((prefq as i64) + j9*32) as *i64, lq, ((gqs as i64) + j9*32) as *i64)
535 nf_qnorm(((gqs as i64) + j9*32) as *i64)
536 nf_qrotv(((prefq as i64) + j9*32) as *i64, G[G_LT + mi9*3], G[G_LT + mi9*3 + 1], G[G_LT + mi9*3 + 2], rv, scr)
537 gts[j9*3] = preft[j9*3] + rv[0]
538 gts[j9*3+1] = preft[j9*3+1] + rv[1]
539 gts[j9*3+2] = preft[j9*3+2] + rv[2]
540 }
541 if pj9 >= 0 {
542 nf_qmul(((gqs as i64) + pj9*32) as *i64, lq, ((gqs as i64) + j9*32) as *i64)
543 nf_qnorm(((gqs as i64) + j9*32) as *i64)
544 nf_qrotv(((gqs as i64) + pj9*32) as *i64, G[G_LT + mi9*3], G[G_LT + mi9*3 + 1], G[G_LT + mi9*3 + 2], rv, scr)
545 gts[j9*3] = gts[pj9*3] + rv[0]
546 gts[j9*3+1] = gts[pj9*3+1] + rv[1]
547 gts[j9*3+2] = gts[pj9*3+2] + rv[2]
548 }
549 ti9 = ti9 + 1
550 }
551 var fkmax: i64 = 0
552 var fkvb: i64 = 0
553 var jv0: i64 = 0
554 while jv0 < nj {
555 if jreal[jv0] == 1 {
556 fkvb = fkvb + 1
557 var a9: i64 = 0
558 while a9 < 3 {
559 var d9: i64 = gts[jv0*3+a9] - jbind[jv0*3+a9]
560 if d9 < 0 { d9 = 0 - d9 }
561 if d9 > fkmax { fkmax = d9 }
562 a9 = a9 + 1
563 }
564 }
565 jv0 = jv0 + 1
566 }
567 skw("fk_validated=" as *u8); skn(fkvb)
568 skw(" fk_maxerr_units=" as *u8); skn(fkmax); skw("\n" as *u8)
569 if fkvb < 8 { skw("TOO-FEW-REAL-BINDS -- refusing FK bind replacement\n" as *u8); return 7 }
570 if fkmax > G_MAGIC_20000 { skw("FK-DIVERGES from cluster binds -- refusing\n" as *u8); return 7 }
571 var jb9: i64 = 0
572 while jb9 < nj {
573 jbind[jb9*3] = gts[jb9*3]
574 jbind[jb9*3+1] = gts[jb9*3+1]
575 jbind[jb9*3+2] = gts[jb9*3+2]
576 jb9 = jb9 + 1
577 }
578 // SKIN accumulation: top-4 (joint, weight q12) per vertex of the main mesh
579 let lpn: *i64 = sys_mmap(16) as *i64
580 let nb: *u8 = sys_map_file(argv[2] as *u8, lpn)
581 let nlen2: i64 = lpn[0]
582 let vwo: i64 = nxa_find(nb, nlen2, nxa_tag4("VERT" as *u8))
583 if vwo < 0 { skw("in.nxa bad\n" as *u8); return 5 }
584 let two: i64 = nxa_find(nb, nlen2, nxa_tag4("TRIS" as *u8))
585 if two < 0 { skw("in.nxa bad\n" as *u8); return 5 }
586 let cwo: i64 = nxa_find(nb, nlen2, nxa_tag4("CLUS" as *u8))
587 let nh: *i64 = nb as *i64
588 let nv: i64 = nh[vwo]
589 let nt: i64 = nh[two]
590 var nclw: i64 = 0
591 if cwo >= 0 { nclw = 1 + nh[cwo]*10 }
592 let j4: *i64 = sys_mmap(nv*32 + 64) as *i64
593 let w4: *i64 = sys_mmap(nv*32 + 64) as *i64
594 let japp: *i64 = sys_mmap(nj*8 + 64) as *i64
595 var cvn: i64 = 0
596 var cvi: i64 = 0
597 var dbg: i64 = 0
598 while dbg < cc {
599 if cval[dbg] == 1 { cvn = cvn + 1; cvi = cvi + G[G_CIX + dbg*4 + 1] }
600 dbg = dbg + 1
601 }
602 skw("valid_clusters=" as *u8); skn(cvn)
603 skw(" their_index_elems=" as *u8); skn(cvi)
604 skw(" gid_vert_alen=" as *u8); skn(G[3]); skw("\n" as *u8)
605 var c2: i64 = 0
606 var applied: i64 = 0
607 while c2 < cc {
608 if cval[c2] == 1 { if cjoint[c2] >= 0 { if G[G_CIX + c2*4 + 1] > 0 { if G[G_CIX + c2*4 + 1] == G[G_CW + c2*4 + 1] {
609 let ni: i64 = G[G_CIX + c2*4 + 1]
610 let ia: *u8 = sk_arr(fb, G[G_CIX + c2*4], ni, G[G_CIX + c2*4 + 2], G[G_CIX + c2*4 + 3], 4)
611 let wa: *u8 = sk_arr(fb, G[G_CW + c2*4], ni, G[G_CW + c2*4 + 2], G[G_CW + c2*4 + 3], 8)
612 if (ia as i64) != 0 { if (wa as i64) != 0 {
613 let jj2: i64 = cjoint[c2]
614 var k3: i64 = 0
615 while k3 < ni {
616 let vi: i64 = sk_u32(ia, k3*4)
617 if vi < nv {
618 let wq: i64 = sk_f64(wa, k3*8, G_MAGIC_4096)
619 if wq > 0 {
620 // replace the smallest of the vertex's 4 slots if we beat it
621 var mslot: i64 = 0
622 var s: i64 = 1
623 while s < 4 { if w4[vi*4+s] < w4[vi*4+mslot] { mslot = s } s = s + 1 }
624 if wq > w4[vi*4+mslot] {
625 w4[vi*4+mslot] = wq
626 j4[vi*4+mslot] = jj2
627 japp[jj2] = japp[jj2] + 1
628 applied = applied + 1
629 }
630 }
631 }
632 k3 = k3 + 1
633 }
634 } }
635 } } } }
636 c2 = c2 + 1
637 }
638 // normalize each vertex to exact sum 4096; unskinned verts -> AUTO-SKIN to the nearest
639 // two clustered joints by inverse distance (authored weights only cover skin sub-regions
640 // in CC exports, and unrigged sculpts have none at all -- auto-fill makes every mesh posable)
641 let vx5: *i64 = ((nb as i64) + vwo*8 + 8) as *i64
642 // auto-skin candidates = ONLY bones that received AUTHORED cluster weight on OUR mesh.
643 // v2 regression caught by the name instrument: FK-true binds gave EYE/TONGUE bones real
644 // positions and nearest-segment auto-skin bound ~1600 FACE verts to an eyeball rotating
645 // 260deg in the mocap -- the "scapula shards" were the face. Organ bones whose clusters
646 // live on foreign meshes (eyes/tongue/teeth) drop out of japp by construction.
647 let jok: *i64 = sys_mmap(nj*8 + 64) as *i64
648 var jo0: i64 = 0
649 while jo0 < nj {
650 jok[jo0] = 0
651 if japp[jo0] > 0 { jok[jo0] = 1 }
652 jo0 = jo0 + 1
653 }
654 var skinned: i64 = 0
655 var autos: i64 = 0
656 var v0: i64 = 0
657 while v0 < nv {
658 let s0: i64 = w4[v0*4] + w4[v0*4+1] + w4[v0*4+2] + w4[v0*4+3]
659 if s0 == 0 {
660 var b1: i64 = 0 - 1
661 var b2j: i64 = 0 - 1
662 var d1: i64 = G_MAGIC_4611686018427387903
663 var d2: i64 = d1
664 var jn: i64 = 0
665 while jn < nj {
666 if jok[jn] == 1 {
667 // distance to the BONE SEGMENT (parent->joint), not the joint point --
668 // point-distance bound mid-limb verts to the wrong bone = walk clipping
669 var ax: i64 = jbind[jn*3]
670 var ay: i64 = jbind[jn*3+1]
671 var az: i64 = jbind[jn*3+2]
672 let pj: i64 = jpar[jn]
673 var bxs: i64 = ax
674 var bys: i64 = ay
675 var bzs: i64 = az
676 if pj >= 0 { if jok[pj] == 1 {
677 bxs = jbind[pj*3]
678 bys = jbind[pj*3+1]
679 bzs = jbind[pj*3+2]
680 } }
681 let abx: i64 = (bxs - ax)/16
682 let aby: i64 = (bys - ay)/16
683 let abz: i64 = (bzs - az)/16
684 let vax: i64 = (vx5[v0*3] - ax)/16
685 let vay: i64 = (vx5[v0*3+1] - ay)/16
686 let vaz: i64 = (vx5[v0*3+2] - az)/16
687 let denom: i64 = abx*abx + aby*aby + abz*abz
688 var tq: i64 = 0
689 if denom > 0 { tq = (vax*abx + vay*aby + vaz*abz)*G_MAGIC_4096/denom }
690 if tq < 0 { tq = 0 }
691 if tq > G_MAGIC_4096 { tq = G_MAGIC_4096 }
692 let dx: i64 = vax - abx*tq/G_MAGIC_4096
693 let dy: i64 = vay - aby*tq/G_MAGIC_4096
694 let dz: i64 = vaz - abz*tq/G_MAGIC_4096
695 let dd: i64 = dx*dx + dy*dy + dz*dz
696 if dd < d1 { d2 = d1; b2j = b1; d1 = dd; b1 = jn }
697 if dd >= d1 { if dd < d2 { d2 = dd; b2j = jn } }
698 }
699 jn = jn + 1
700 }
701 if b1 >= 0 {
702 if b2j >= 0 {
703 // inverse-distance split over the two nearest bones (d in squared space)
704 let wsum: i64 = d1 + d2
705 var wa: i64 = G_MAGIC_4096*d2/wsum
706 if wa > G_MAGIC_4096 { wa = G_MAGIC_4096 }
707 j4[v0*4] = b1
708 w4[v0*4] = wa
709 j4[v0*4+1] = b2j
710 w4[v0*4+1] = G_MAGIC_4096 - wa
711 }
712 if b2j < 0 {
713 j4[v0*4] = b1
714 w4[v0*4] = G_MAGIC_4096
715 }
716 autos = autos + 1
717 }
718 if b1 < 0 {
719 j4[v0*4] = 0
720 w4[v0*4] = G_MAGIC_4096
721 }
722 }
723 if s0 > 0 {
724 skinned = skinned + 1
725 var acc: i64 = 0
726 var s1: i64 = 0
727 while s1 < 3 {
728 let nw: i64 = w4[v0*4+s1]*G_MAGIC_4096/s0
729 w4[v0*4+s1] = nw
730 acc = acc + nw
731 s1 = s1 + 1
732 }
733 w4[v0*4+3] = G_MAGIC_4096 - acc
734 }
735 v0 = v0 + 1
736 }
737 // WEIGHT SMOOTHING (2 Laplacian passes over the mesh graph): the stretch instrument
738 // measured hard 100pct-to-100pct weight steps tearing under real per-joint motion --
739 // armpit Spine02<>UpperarmTwist01 128 torn edges, hip crease Pelvis<>ThighTwist01 118,
740 // shoulder top <>Clavicle 112. 60/40 own/neighbor blend, top-4 retruncate, sum=4096.
741 let tr5: *i64 = ((nb as i64) + two*8 + 8) as *i64
742 let deg: *i64 = sys_mmap(nv*8 + 64) as *i64
743 var te0: i64 = 0
744 while te0 < nt {
745 deg[tr5[te0*3]] = deg[tr5[te0*3]] + 2
746 deg[tr5[te0*3+1]] = deg[tr5[te0*3+1]] + 2
747 deg[tr5[te0*3+2]] = deg[tr5[te0*3+2]] + 2
748 te0 = te0 + 1
749 }
750 let adjo: *i64 = sys_mmap(nv*8 + 72) as *i64
751 var ao0: i64 = 0
752 var acc0: i64 = 0
753 while ao0 < nv { adjo[ao0] = acc0; acc0 = acc0 + deg[ao0]; ao0 = ao0 + 1 }
754 adjo[nv] = acc0
755 let adjl: *i64 = sys_mmap(acc0*8 + 64) as *i64
756 let cur: *i64 = sys_mmap(nv*8 + 64) as *i64
757 var te1: i64 = 0
758 while te1 < nt {
759 var ec0: i64 = 0
760 while ec0 < 3 {
761 let a5: i64 = tr5[te1*3 + ec0]
762 var b5: i64 = tr5[te1*3]
763 var c5: i64 = tr5[te1*3 + 1]
764 if ec0 == 0 { b5 = tr5[te1*3+1]; c5 = tr5[te1*3+2] }
765 if ec0 == 1 { b5 = tr5[te1*3]; c5 = tr5[te1*3+2] }
766 if ec0 == 2 { b5 = tr5[te1*3]; c5 = tr5[te1*3+1] }
767 adjl[adjo[a5] + cur[a5]] = b5
768 cur[a5] = cur[a5] + 1
769 adjl[adjo[a5] + cur[a5]] = c5
770 cur[a5] = cur[a5] + 1
771 ec0 = ec0 + 1
772 }
773 te1 = te1 + 1
774 }
775 let j4b: *i64 = sys_mmap(nv*32 + 64) as *i64
776 let w4b: *i64 = sys_mmap(nv*32 + 64) as *i64
777 let mj: *i64 = sys_mmap(64*8 + 64) as *i64
778 let mw: *i64 = sys_mmap(64*8 + 64) as *i64
779 var it0: i64 = 0
780 while it0 < 3 {
781 var vs0: i64 = 0
782 while vs0 < nv {
783 let dv5: i64 = deg[vs0]
784 if dv5 == 0 {
785 var sc0: i64 = 0
786 while sc0 < 4 { j4b[vs0*4+sc0] = j4[vs0*4+sc0]; w4b[vs0*4+sc0] = w4[vs0*4+sc0]; sc0 = sc0 + 1 }
787 }
788 if dv5 > 0 {
789 var mn: i64 = 0
790 var so0: i64 = 0
791 while so0 < 4 {
792 if w4[vs0*4+so0] > 0 {
793 let jj5: i64 = j4[vs0*4+so0]
794 let ww5: i64 = w4[vs0*4+so0]*6*dv5
795 var f0: i64 = 0 - 1
796 var m0s: i64 = 0
797 while m0s < mn { if mj[m0s] == jj5 { f0 = m0s; m0s = mn } m0s = m0s + 1 }
798 if f0 >= 0 { mw[f0] = mw[f0] + ww5 }
799 if f0 < 0 { if mn < 60 { mj[mn] = jj5; mw[mn] = ww5; mn = mn + 1 } }
800 }
801 so0 = so0 + 1
802 }
803 var nb0: i64 = 0
804 while nb0 < dv5 {
805 let vn5: i64 = adjl[adjo[vs0] + nb0]
806 var sn0: i64 = 0
807 while sn0 < 4 {
808 if w4[vn5*4+sn0] > 0 {
809 let jj6: i64 = j4[vn5*4+sn0]
810 let ww6: i64 = w4[vn5*4+sn0]*4
811 var f1: i64 = 0 - 1
812 var m1s: i64 = 0
813 while m1s < mn { if mj[m1s] == jj6 { f1 = m1s; m1s = mn } m1s = m1s + 1 }
814 if f1 >= 0 { mw[f1] = mw[f1] + ww6 }
815 if f1 < 0 { if mn < 60 { mj[mn] = jj6; mw[mn] = ww6; mn = mn + 1 } }
816 }
817 sn0 = sn0 + 1
818 }
819 nb0 = nb0 + 1
820 }
821 // top-4 of the merged map -> renormalize to exact 4096
822 var sl0: i64 = 0
823 while sl0 < 4 {
824 var bi5: i64 = 0 - 1
825 var bw5: i64 = 0
826 var m2s: i64 = 0
827 while m2s < mn {
828 if mw[m2s] > bw5 { bw5 = mw[m2s]; bi5 = m2s }
829 m2s = m2s + 1
830 }
831 if bi5 >= 0 {
832 j4b[vs0*4+sl0] = mj[bi5]
833 w4b[vs0*4+sl0] = mw[bi5]
834 mw[bi5] = 0
835 }
836 if bi5 < 0 { j4b[vs0*4+sl0] = 0; w4b[vs0*4+sl0] = 0 }
837 sl0 = sl0 + 1
838 }
839 let ts5: i64 = w4b[vs0*4] + w4b[vs0*4+1] + w4b[vs0*4+2] + w4b[vs0*4+3]
840 if ts5 > 0 {
841 var an5: i64 = 0
842 var sr0: i64 = 0
843 while sr0 < 3 {
844 let nw5: i64 = w4b[vs0*4+sr0]*G_MAGIC_4096/ts5
845 w4b[vs0*4+sr0] = nw5
846 an5 = an5 + nw5
847 sr0 = sr0 + 1
848 }
849 w4b[vs0*4+3] = G_MAGIC_4096 - an5
850 }
851 if ts5 == 0 { j4b[vs0*4] = 0; w4b[vs0*4] = G_MAGIC_4096; w4b[vs0*4+1] = 0; w4b[vs0*4+2] = 0; w4b[vs0*4+3] = 0 }
852 }
853 vs0 = vs0 + 1
854 }
855 var cp0: i64 = 0
856 while cp0 < nv*4 { j4[cp0] = j4b[cp0]; w4[cp0] = w4b[cp0]; cp0 = cp0 + 1 }
857 it0 = it0 + 1
858 }
859 skw("weights_smoothed=2passes\n" as *u8)
860 skw("joints=" as *u8); skn(nj)
861 skw(" skinned=" as *u8); skn(skinned)
862 skw(" autoskinned=" as *u8); skn(autos)
863 skw("/" as *u8); skn(nv)
864 skw(" applied=" as *u8); skn(applied); skw("\n" as *u8)
865 // build SKEL + SKIN payloads
866 let skel: *i64 = sys_mmap((1 + nj*8)*8 + 64) as *i64
867 skel[0] = nj
868 var j1: i64 = 0
869 while j1 < nj {
870 skel[1 + j1*8] = jpar[j1]
871 skel[1 + j1*8 + 1] = jbind[j1*3]
872 skel[1 + j1*8 + 2] = jbind[j1*3 + 1]
873 skel[1 + j1*8 + 3] = jbind[j1*3 + 2]
874 skel[1 + j1*8 + 4] = 0
875 skel[1 + j1*8 + 5] = 0
876 skel[1 + j1*8 + 6] = 0
877 skel[1 + j1*8 + 7] = G_MAGIC_4096
878 j1 = j1 + 1
879 }
880 let skin: *i64 = sys_mmap((1 + nv*8)*8 + 64) as *i64
881 skin[0] = nv
882 var v1: i64 = 0
883 while v1 < nv {
884 var s2: i64 = 0
885 while s2 < 4 {
886 skin[1 + v1*8 + s2] = j4[v1*4 + s2]
887 skin[1 + v1*8 + 4 + s2] = w4[v1*4 + s2]
888 s2 = s2 + 1
889 }
890 v1 = v1 + 1
891 }
892 // emit the 5-section NXA
893 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4)
894 if fd < 0 { skw("open out failed\n" as *u8); return 9 }
895 let vwl: i64 = 1 + nv*3
896 let twl: i64 = 1 + nt*3
897 let swl: i64 = 1 + nj*8
898 let kwl: i64 = 1 + nv*8
899 var ns: i64 = 4
900 if nclw > 0 { ns = 5 }
901 let hdr: *i64 = sys_mmap(64) as *i64
902 let toc: *i64 = sys_mmap(512) as *i64
903 var o: i64 = 32 + ns*32
904 var ti: i64 = 0
905 toc[ti*4] = nxa_tag4("VERT" as *u8)
906 toc[ti*4+1] = o
907 toc[ti*4+2] = vwl
908 toc[ti*4+3] = nxa_check2(1, ((nb as i64) + vwo*8) as *i64, vwl)
909 o = o + vwl*8
910 ti = ti + 1
911 toc[ti*4] = nxa_tag4("TRIS" as *u8)
912 toc[ti*4+1] = o
913 toc[ti*4+2] = twl
914 toc[ti*4+3] = nxa_check2(1, ((nb as i64) + two*8) as *i64, twl)
915 o = o + twl*8
916 ti = ti + 1
917 if nclw > 0 {
918 toc[ti*4] = nxa_tag4("CLUS" as *u8)
919 toc[ti*4+1] = o
920 toc[ti*4+2] = nclw
921 toc[ti*4+3] = nxa_check2(1, ((nb as i64) + cwo*8) as *i64, nclw)
922 o = o + nclw*8
923 ti = ti + 1
924 }
925 toc[ti*4] = nxa_tag4("SKEL" as *u8)
926 toc[ti*4+1] = o
927 toc[ti*4+2] = swl
928 toc[ti*4+3] = nxa_check2(1, skel, swl)
929 o = o + swl*8
930 ti = ti + 1
931 toc[ti*4] = nxa_tag4("SKIN" as *u8)
932 toc[ti*4+1] = o
933 toc[ti*4+2] = kwl
934 toc[ti*4+3] = nxa_check2(1, skin, kwl)
935 hdr[0] = nxa_magic()
936 hdr[1] = NXA_VER
937 hdr[2] = ns
938 hdr[3] = nxa_check2(1, toc, ns*4)
939 sys_write(fd, hdr as *u8, 32)
940 sys_write(fd, toc as *u8, ns*32)
941 sys_write(fd, ((nb as i64) + vwo*8) as *u8, vwl*8)
942 sys_write(fd, ((nb as i64) + two*8) as *u8, twl*8)
943 if nclw > 0 { sys_write(fd, ((nb as i64) + cwo*8) as *u8, nclw*8) }
944 sys_write(fd, skel as *u8, swl*8)
945 sys_write(fd, skin as *u8, kwl*8)
946 sys_close(fd)
947 skw("NXA5 written joints=" as *u8); skn(nj)
948 skw(" skin_verts=" as *u8); skn(nv); skw("\n" as *u8)
949 return 0
950}