code wiki / (root) / nx_nxa_rig_motion_20260909.nx

nx_nxa_rig_motion_20260909.nx source

↩ module page · 67 lines · 3283 B

1// Caller-owned native rig composition extracted from nx_nxa_retarget, 2026-09-09. 2// Inputs are admitted parent-first identity-bind rigs in the SAME target raw basis. 3// No allocator, shared writable arena, entity count, or joint ceiling. 4// q/d source inputs are immutable; outputs W(nt*4), P(nt*3), tdt(nt*3) must be disjoint. 5// scratch requires NRM_SCR_W words. Validate owned spans at the external context boundary. 6import "nx_nxa_fk.nx" 7const NRM_Q12: i64 = 4096 8const NRM_SCR_W: i64 = 32 9func nrm_q(p: *i64, i: i64) -> *i64 { return ((p as i64) + i*32) as *i64 } 10func nrm_compose(ns: i64, nt: i64, sD: *i64, sdt: *i64, map: *i64, tpar: *i64, tbind: *i64, bdst: *i64, bound: i64, sse: i64, tse: i64, W: *i64, P: *i64, tdt: *i64, scratch: *i64) -> i64 { 11 if ns < 1 { return 0 - 1 } 12 if nt < 1 { return 0 - 1 } 13 if sse < 1 { return 0 - 1 } 14 if tse < 1 { return 0 - 1 } 15 var v: i64 = 0 16 while v < nt { 17 if map[v] < 0 { return 0 - 2 } 18 if map[v] >= ns { return 0 - 2 } 19 if tpar[v] >= v { return 0 - 3 } 20 if tpar[v] < (0 - 1) { return 0 - 3 } 21 v = v + 1 22 } 23 let cq: *i64 = scratch 24 let aq: *i64 = ((scratch as i64) + 32) as *i64 25 let o3: *i64 = ((scratch as i64) + 64) as *i64 26 let scr: *i64 = ((scratch as i64) + 128) as *i64 27 var j: i64 = 0 28 while j < nt { 29 let s: i64 = map[j] 30 let pj: i64 = tpar[j] 31 if pj < 0 { 32 // root: no parent to conjugate against, so A = D. Root translation is the ONE quantity 33 // that must cross skeletons by size, so it is scaled by the ratio of the two rigs' own 34 // measured stature extents -- derived from the assets, never a picked factor. 35 W[j*4] = sD[s*4]; W[j*4+1] = sD[s*4+1]; W[j*4+2] = sD[s*4+2]; W[j*4+3] = sD[s*4+3] 36 P[j*3] = tbind[j*3] + sdt[s*3] * tse / sse 37 P[j*3+1] = tbind[j*3+1] + sdt[s*3+1] * tse / sse 38 P[j*3+2] = tbind[j*3+2] + sdt[s*3+2] * tse / sse 39 } 40 if pj >= 0 { 41 let sp: i64 = map[pj] 42 // A joint the source cannot resolve within its OWN joint spacing gets IDENTITY local 43 // articulation, so it rides its parent rigidly instead of being handed a rotation 44 // borrowed from whatever joint happened to be nearest. Fabricated articulation on an 45 // unresolvable joint is exactly the awful-motion class: it looks like animation and is 46 // not derived from anything. 47 aq[0] = 0; aq[1] = 0; aq[2] = 0; aq[3] = NRM_Q12 48 if bdst[j] <= bound { 49 nf_qconj(nrm_q(sD, sp), cq) 50 nf_qmul(cq, nrm_q(sD, s), aq) 51 } 52 nf_qmul(nrm_q(W, pj), aq, nrm_q(W, j)) 53 nf_qrotv(nrm_q(W, pj), tbind[j*3] - tbind[pj*3], tbind[j*3+1] - tbind[pj*3+1], tbind[j*3+2] - tbind[pj*3+2], o3, scr) 54 P[j*3] = P[pj*3] + o3[0] 55 P[j*3+1] = P[pj*3+1] + o3[1] 56 P[j*3+2] = P[pj*3+2] + o3[2] 57 } 58 // q12 multiplication truncates; normalize before this joint becomes a parent. 59 nf_qnorm(nrm_q(W, j)) 60 tdt[j*3] = P[j*3] - tbind[j*3] 61 tdt[j*3+1] = P[j*3+1] - tbind[j*3+1] 62 tdt[j*3+2] = P[j*3+2] - tbind[j*3+2] 63 j = j + 1 64 } 65 66 return 0 67}