code wiki / (root) / nx_nxa_fk.nx

nx_nxa_fk.nx source

↩ module page · 102 lines · 4089 B

1// nx_nxa_fk.nx -- SHARED integer quaternion + FK primitives for the NXA import pipeline. 2// Extracted from nx_nxa_anim (DRY rule-15: nx_nxa_skin needs the same static-FK to compute 3// TRUE bind positions instead of placeholder propagation; nobody re-derives quat math). 4// q12 fixed-point (unit = 4096); angles arrive as millidegrees; all integer. 5// license_tier: ORIGINAL 6import "nx_itrig.nx" 7const K_MAGIC_4096: i64 = 4096 8const K_MAGIC_100000000: i64 = 100000000 9const K_MAGIC_65536: i64 = 65536 10const K_MAGIC_2048: i64 = 2048 11const K_MAGIC_57296: i64 = 57296 12 13// quat q12 multiply: r = a*b (x,y,z,w at [0..3]) 14func nf_qmul(a: *i64, b: *i64, r: *i64) -> i64 { 15 r[0] = (a[3]*b[0] + a[0]*b[3] + a[1]*b[2] - a[2]*b[1]) / K_MAGIC_4096 16 r[1] = (a[3]*b[1] - a[0]*b[2] + a[1]*b[3] + a[2]*b[0]) / K_MAGIC_4096 17 r[2] = (a[3]*b[2] + a[0]*b[1] - a[1]*b[0] + a[2]*b[3]) / K_MAGIC_4096 18 r[3] = (a[3]*b[3] - a[0]*b[0] - a[1]*b[1] - a[2]*b[2]) / K_MAGIC_4096 19 return 0 20} 21// integer sqrt (Newton) for quat norms; fixed 24 iterations, break-free by design 22func nf_isqrt(n: i64) -> i64 { 23 if n <= 0 { return 1 } 24 var x: i64 = K_MAGIC_4096 25 if n > K_MAGIC_100000000 { x = K_MAGIC_65536 } 26 var i: i64 = 0 27 while i < 24 { 28 let y: i64 = (x + n/x) / 2 29 if y > 0 { x = y } 30 i = i + 1 31 } 32 if x < 1 { x = 1 } 33 return x 34} 35// renormalize q to unit 4096 -- stops q12 truncation drift across deep FK chains 36func nf_qnorm(q: *i64) -> i64 { 37 let n2: i64 = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3] 38 if n2 <= 0 { q[0]=0; q[1]=0; q[2]=0; q[3]=K_MAGIC_4096; return 0 } 39 let s: i64 = nf_isqrt(n2) 40 q[0] = q[0]*K_MAGIC_4096/s 41 q[1] = q[1]*K_MAGIC_4096/s 42 q[2] = q[2]*K_MAGIC_4096/s 43 q[3] = q[3]*K_MAGIC_4096/s 44 return 0 45} 46func nf_qconj(q: *i64, r: *i64) -> i64 { r[0]=0-q[0]; r[1]=0-q[1]; r[2]=0-q[2]; r[3]=q[3]; return 0 } 47// Euler XYZ millidegrees -> q12 quat (qz*qy*qx, FBX eOrderXYZ column form); scr = 16 words 48func nf_eul2q(mdx: i64, mdy: i64, mdz: i64, out: *i64, scr: *i64) -> i64 { 49 let hx: i64 = mdx*K_MAGIC_2048/K_MAGIC_57296 50 let hy: i64 = mdy*K_MAGIC_2048/K_MAGIC_57296 51 let hz: i64 = mdz*K_MAGIC_2048/K_MAGIC_57296 52 scr[0] = it_sin4096(hx) 53 scr[1] = 0 54 scr[2] = 0 55 scr[3] = it_cos4096(hx) 56 scr[4] = 0 57 scr[5] = it_sin4096(hy) 58 scr[6] = 0 59 scr[7] = it_cos4096(hy) 60 scr[8] = 0 61 scr[9] = 0 62 scr[10] = it_sin4096(hz) 63 scr[11] = it_cos4096(hz) 64 nf_qmul(((scr as i64)+32) as *i64, scr, ((scr as i64)+96) as *i64) 65 nf_qmul(((scr as i64)+64) as *i64, ((scr as i64)+96) as *i64, out) 66 return 0 67} 68// SHORTEST-ARC quat between two integer vectors, NO TRIG (half-angle identity): 69// q = (a x b, |a||b| + a.b) normalized. Inputs any magnitude; internally rescaled to ~4096 70// so no i64 overflow. a ~= -b (180deg) degenerates to identity -- callers use small deltas. 71func nf_qarc(ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64, out: *i64) -> i64 { 72 var la: i64 = nf_isqrt(ax*ax + ay*ay + az*az) 73 if la < 1 { la = 1 } 74 var lb: i64 = nf_isqrt(bx*bx + by*by + bz*bz) 75 if lb < 1 { lb = 1 } 76 let axs: i64 = ax*K_MAGIC_4096/la 77 let ays: i64 = ay*K_MAGIC_4096/la 78 let azs: i64 = az*K_MAGIC_4096/la 79 let bxs: i64 = bx*K_MAGIC_4096/lb 80 let bys: i64 = by*K_MAGIC_4096/lb 81 let bzs: i64 = bz*K_MAGIC_4096/lb 82 out[0] = (ays*bzs - azs*bys)/K_MAGIC_4096 83 out[1] = (azs*bxs - axs*bzs)/K_MAGIC_4096 84 out[2] = (axs*bys - ays*bxs)/K_MAGIC_4096 85 out[3] = K_MAGIC_4096 + (axs*bxs + ays*bys + azs*bzs)/K_MAGIC_4096 86 nf_qnorm(out) 87 return 0 88} 89// rotate integer vector v (any units) by unit q12 quat: out3 = q*(v,0)*conj(q); scr = 16 words 90func nf_qrotv(q: *i64, vx: i64, vy: i64, vz: i64, out3: *i64, scr: *i64) -> i64 { 91 scr[0] = vx 92 scr[1] = vy 93 scr[2] = vz 94 scr[3] = 0 95 nf_qconj(q, ((scr as i64)+32) as *i64) 96 nf_qmul(q, scr, ((scr as i64)+64) as *i64) 97 nf_qmul(((scr as i64)+64) as *i64, ((scr as i64)+32) as *i64, ((scr as i64)+96) as *i64) 98 out3[0] = scr[12] 99 out3[1] = scr[13] 100 out3[2] = scr[14] 101 return 0 102}