code wiki / (root) / nx_nxa_retarget.nx

nx_nxa_retarget.nx source

↩ module page · 1091 lines · 51459 B

1// nx_nxa_retarget.nx -- DONOR RETARGET: drive a rigged NXA that carries NO authored clips from a 2// SOURCE rig's POSE, preserving the TARGET's OWN bone lengths BY CONSTRUCTION. 3// 4// WHY (measured 2026-08-23): all five donors report poses_in_library=0 -- they carry rigs but no 5// authored motion, so the visiting cast on /world/beach stand as statues while every other piece 6// of the character stack ships. This organ is the missing leg. 7// 8// THE NAIVE RETARGET IS PROVABLY WRONG, AND THAT PROOF IS WHY THIS FILE EXISTS. In the NXA 9// normative form M x = D(x-b) + b + dt, applying joint j's transform to its own bind position 10// gives M_j b_j = b_j + dt_j. So a posed joint sits at b_j + dt_j and POSED BONE LENGTH IS A PURE 11// FUNCTION OF dt. Copying source dt -- or scaling it by a height ratio -- therefore cannot preserve 12// the target's bone lengths unless the two skeletons are proportionally identical, and ours are 13// emphatically not (104 joints at 1.715 m against 370 joints on a differently-proportioned donor). 14// A dt-transfer retarget stretches and shears limbs on every donor: the exact defect the operator 15// named as awful motion. It is not shipped here. 16// 17// THE ALGORITHM (four lines, and limb length is preserved BY CONSTRUCTION, not by tolerance): 18// A_j = conj(D_parent) . D_j source: world delta -> local articulation 19// W_j = W_parent . A_j target: parent-first composition 20// P_j = P_parent + W_parent (x) (b'_j - b'_p) the TARGET's OWN bone vector, ROTATED ONLY 21// dt'_j = P_j - b'_j 22// The target bone vector is only ever rotated -- never scaled, never replaced -- so no pose this 23// organ can emit is able to change a target bone's length. The limb-length tooth in the gate is a 24// REGRESSION GUARD on that property, not the thing that establishes it. 25// 26// The general form needs a similarity conjugation by each joint's bind orientation. It collapses 27// to the four lines above because NXA bind orientation is IDENTITY BY CONSTRUCTION -- nx_nxa_skin 28// emits SKEL quats as identity and states the reason in its own header (delta-LBS convention: 29// animation is applied RELATIVE to bind). VERIFY THAT BEFORE TRUSTING IT: the `bindquats` verb 30// measures it per asset and is the reason this organ has three verbs instead of one. 31// 32// JOINT MAPPING IS BY TOPOLOGY AND NORMALIZED BIND POSITION, NEVER BY NAME. SKEL carries no names 33// by design, and the donors come from different authoring tools -- a name map would be the 34// classifier-keys-on-a-string defect. The normalized space is the same one nx_nxa_joints uses: 35// every axis divided by the rig's OWN stature extent, so a taller or differently-scaled rig lands 36// in the same space. The stature axis is DERIVED as the largest bind extent, never assumed z-up: 37// the FBX/VRM donors arrive Y-up, which is the axis-convention defect a sibling lane measured in 38// the renderer on the same day. 39// 40// usage: nx_nxa_retarget bindquats <file.nxa> 41// nx_nxa_retarget map <src.nxa> <dst.nxa> 42// nx_nxa_retarget retarget <src.nxa> <pose_id> <dst.nxa> [out.nxa] [weight_permil] [emit=pose] 43// emit=pose (anywhere after <dst.nxa>): publish the POSE LIBRARY entry (W quat + dt per target joint) instead of a 44// baked VERT, so the output stays a RIG the player can replay, hold and sequence, and nx_nxa_play becomes an 45// independent witness of the motion this organ measured (2026-09-05, /compare/modding MD3; measured: every baked 46// donor read poses_in_library=0 joints_posed=0 under nx_nxa_play while the house rig read 104/104). The writer is 47// nx_nxa_posewrite_lib -- the estate's ONE POSE-section writer, shared with nx_nxa_pose freeze. 48// license_tier: ORIGINAL expect_exit: 0 49import "nx_syscalls.nx" 50import "nx_nxa.nx" 51import "nx_nxa_fk.nx" 52import "nx_skeleton.nx" 53import "nx_nxa_posewrite_lib.nx" 54 55const NR_Q12: i64 = 4096 // quaternion fixed-point unit (NXA v1 spec) 56const NR_WJ: i64 = 8 // words per SKEL joint (spec: parent,tx,ty,tz,qx,qy,qz,qw) 57const NR_WPE: i64 = 8 // words per POSE entry (spec: joint,qx,qy,qz,qw,dtx,dty,dtz) 58const NR_PERMIL: i64 = 1000 59const NR_INF: i64 = 4 // SKIN influences per vertex, fixed by the format 60const NR_WQ: i64 = 4096 // SKIN weight scale q12, sum = 4096 (spec) 61const NR_FX: i64 = 256 // nx_skeleton weight scale fx256, sum = 256 62// POSE dt lanes are already MODEL units (0.01 mm), not mm: the x100 is applied inside 63// nx_nxa_pose at freeze. The spec paragraph claiming mm is stale -- settled from the record 64// (eaten row 1785380029) by the player lane, and nx_nxa_play ships the same NP_DT_MODEL=1. 65const NR_DT_MODEL: i64 = 1 66const NR_WFULL: i64 = 1000 67const NR_MODE: i64 = 0x1a4 // 0644, matching every sibling NXA writer 68const NR_EXIT_USAGE: i64 = 2 69const NR_EXIT_REFUSE: i64 = 3 70const NR_EXIT_NOSEC: i64 = 5 71const NR_EXIT_NONIDENT: i64 = 1 // bindquats: a measurement outcome, not an error 72 73func nrw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 74func nrerr(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 } 75func nrn(v: i64) -> i64 { 76 let b: *u8 = sys_mmap(32) 77 var x: i64 = v 78 if x < 0 { b[0] = 45 as u8; sys_write(1, b, 1); x = 0 - x } 79 if x == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 } 80 var d: i64 = 0 81 var y: i64 = x 82 while y > 0 { d = d + 1; y = y / 10 } 83 var i: i64 = d 84 while i > 0 { i = i - 1; b[i] = ((x % 10) + 48) as u8; x = x / 10 } 85 sys_write(1, b, d) 86 return 0 87} 88func nr_atoi(s: *u8) -> i64 { 89 var v: i64 = 0 90 var i: i64 = 0 91 while s[i] != (0 as u8) { 92 let c: i64 = s[i] as i64 93 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 94 i = i + 1 95 } 96 return v 97} 98func nr_streq(a: *u8, b: *u8) -> i64 { 99 var i: i64 = 0 100 while a[i] != (0 as u8) { 101 if a[i] != b[i] { return 0 } 102 i = i + 1 103 } 104 if b[i] != (0 as u8) { return 0 } 105 return 1 106} 107func nr_q(p: *i64, i: i64) -> *i64 { return ((p as i64) + i * 32) as *i64 } 108 109// Normalize a bind set into a scale-invariant space and RETURN THE STATURE EXTENT. 110// Every axis is divided by the rig's own largest extent, so proportion is preserved and a 111// differently-scaled rig lands in the same space. Returns -1 if the rig is degenerate. 112func nr_norm(bind: *i64, n: i64, out: *i64) -> i64 { 113 if n < 1 { return 0 - 1 } 114 let mn: *i64 = sys_mmap(64) 115 let mx: *i64 = sys_mmap(64) 116 var k: i64 = 0 117 while k < 3 { mn[k] = bind[k]; mx[k] = bind[k]; k = k + 1 } 118 var j: i64 = 1 119 while j < n { 120 k = 0 121 while k < 3 { 122 let v: i64 = bind[j*3+k] 123 if v < mn[k] { mn[k] = v } 124 if v > mx[k] { mx[k] = v } 125 k = k + 1 126 } 127 j = j + 1 128 } 129 var se: i64 = mx[0] - mn[0] 130 k = 1 131 while k < 3 { 132 let e: i64 = mx[k] - mn[k] 133 if e > se { se = e } 134 k = k + 1 135 } 136 if se <= 0 { return 0 - 1 } 137 // Anchor on the ROOT -- joint 0, since parent-first ordering is enforced before this is called, 138 // so joint 0 IS the root -- rather than on the per-axis bounding box. A BOUNDING BOX IS 139 // POSE-DEPENDENT: a T-posed rig and an A-posed rig have different lateral extents, so 140 // box-anchored coordinates shift EVERY joint when only the arms moved. The root does not move 141 // with the pose, so anchoring there compares anatomy instead of comparing posture. 142 j = 0 143 while j < n { 144 k = 0 145 while k < 3 { out[j*3+k] = (bind[j*3+k] - bind[k]) * NR_PERMIL / se; k = k + 1 } 146 j = j + 1 147 } 148 return se 149} 150func nr_d2(a: *i64, ai: i64, b: *i64, bi: i64) -> i64 { 151 let dx: i64 = a[ai*3] - b[bi*3] 152 let dy: i64 = a[ai*3+1] - b[bi*3+1] 153 let dz: i64 = a[ai*3+2] - b[bi*3+2] 154 return dx*dx + dy*dy + dz*dz 155} 156// The admission bound is DERIVED from the target's own geometry: the mean nearest-neighbour 157// spacing between its joints. If the best source match for a joint is farther away than the 158// target's own typical joint spacing, the source rig cannot resolve that joint and the mapping 159// is unreliable -- so it is refused rather than silently producing a plausible-looking pose. 160func nr_mean_nn(nrm: *i64, n: i64) -> i64 { 161 if n < 2 { return 0 - 1 } 162 var tot: i64 = 0 163 var j: i64 = 0 164 while j < n { 165 var best: i64 = 0 - 1 166 var i: i64 = 0 167 while i < n { 168 if i != j { 169 let d: i64 = nr_d2(nrm, j, nrm, i) 170 if best < 0 { best = d } 171 if d < best { best = d } 172 } 173 i = i + 1 174 } 175 tot = tot + nf_isqrt(best) 176 j = j + 1 177 } 178 return tot / n 179} 180func nr_load(path: *u8, lp: *i64) -> *u8 { 181 // sys_read_file sizes from the file itself and cannot short-read: the banked law against a 182 // hand-picked cap on a file read, which silently truncates its own subject. 183 return sys_read_file(path, lp) 184} 185 186// Derive the rig's OWN anatomical frame rather than assuming one. THE DONORS ARRIVE Y-UP WHILE OUR 187// GENERATED RIGS ARE Z-UP -- the same axis-convention defect a sibling lane measured in the renderer 188// (a donor rendering lying flat) and another measured in the soft-tissue solver (refusing donors for 189// having no left-side front vertices in the bust band) on this same day. Assuming largest-extent-is- 190// stature is ALSO wrong here: a T-posed rig's arm span can exceed its height, and these donors are 191// T/A-posed. So the LATERAL axis is identified by BILATERAL SYMMETRY -- a humanoid rig mirrors 192// left-to-right about that axis and about no other -- and of the two remaining axes the taller one is 193// stature. The match tolerance is the rig's OWN mean joint spacing, never a picked fraction. 194func nr_sym(bind: *i64, n: i64, k: i64, tol: i64) -> i64 { 195 var mn: i64 = bind[k] 196 var mx: i64 = bind[k] 197 var j: i64 = 1 198 while j < n { 199 let v: i64 = bind[j*3+k] 200 if v < mn { mn = v } 201 if v > mx { mx = v } 202 j = j + 1 203 } 204 let mid2: i64 = mn + mx 205 var hit: i64 = 0 206 j = 0 207 while j < n { 208 let want: i64 = mid2 - bind[j*3+k] 209 var found: i64 = 0 210 var i: i64 = 0 211 while i < n { 212 var d: i64 = bind[i*3+k] - want 213 if d < 0 { d = 0 - d } 214 if d <= tol { 215 var ok: i64 = 1 216 var o: i64 = 0 217 while o < 3 { 218 if o != k { 219 var d2: i64 = bind[i*3+o] - bind[j*3+o] 220 if d2 < 0 { d2 = 0 - d2 } 221 if d2 > tol { ok = 0 } 222 } 223 o = o + 1 224 } 225 if ok == 1 { found = 1 } 226 } 227 i = i + 1 228 } 229 hit = hit + found 230 j = j + 1 231 } 232 return hit * NR_PERMIL / n 233} 234func nr_frame(bind: *i64, n: i64, ax: *i64) -> i64 { 235 let tol: i64 = nr_mean_nn(bind, n) 236 let ext: *i64 = sys_mmap(64) 237 var k: i64 = 0 238 while k < 3 { 239 var mn: i64 = bind[k] 240 var mx: i64 = bind[k] 241 var j: i64 = 1 242 while j < n { 243 let v: i64 = bind[j*3+k] 244 if v < mn { mn = v } 245 if v > mx { mx = v } 246 j = j + 1 247 } 248 ext[k] = mx - mn 249 k = k + 1 250 } 251 var lat: i64 = 0 252 var bests: i64 = 0 - 1 253 k = 0 254 while k < 3 { 255 let s: i64 = nr_sym(bind, n, k, tol) 256 if s > bests { bests = s; lat = k } 257 k = k + 1 258 } 259 var a1: i64 = 0 - 1 260 var a2: i64 = 0 - 1 261 k = 0 262 while k < 3 { 263 if k != lat { 264 if a1 < 0 { a1 = k } 265 if a1 != k { a2 = k } 266 } 267 k = k + 1 268 } 269 var up: i64 = a1 270 var dep: i64 = a2 271 if ext[a2] > ext[a1] { up = a2; dep = a1 } 272 ax[0] = up; ax[1] = lat; ax[2] = dep 273 return bests 274} 275func nr_permute(bind: *i64, n: i64, ax: *i64, sg: *i64, out: *i64) -> i64 { 276 var j: i64 = 0 277 while j < n { 278 var k: i64 = 0 279 while k < 3 { out[j*3+k] = bind[j*3+ax[k]] * sg[k]; k = k + 1 } 280 j = j + 1 281 } 282 return 0 283} 284// The signed permutation carrying SOURCE raw coordinates into TARGET raw coordinates. A rotation's 285// axis is an AXIAL vector: under a frame change of determinant -1 it picks up that sign, otherwise 286// a mirrored frame would silently reverse every rotation direction. 287func nr_reframe_v(v: *i64, out: *i64, sax: *i64, ssg: *i64, tax: *i64, tsg: *i64) -> i64 { 288 var c: i64 = 0 289 while c < 3 { out[tax[c]] = v[sax[c]] * ssg[c] * tsg[c]; c = c + 1 } 290 return 0 291} 292func nr_frame_det(sax: *i64, ssg: *i64, tax: *i64, tsg: *i64) -> i64 { 293 let m: *i64 = sys_mmap(128) 294 var i: i64 = 0 295 while i < 9 { m[i] = 0; i = i + 1 } 296 var c: i64 = 0 297 while c < 3 { m[tax[c]*3 + sax[c]] = ssg[c] * tsg[c]; c = c + 1 } 298 return m[0]*(m[4]*m[8]-m[5]*m[7]) - m[1]*(m[3]*m[8]-m[5]*m[6]) + m[2]*(m[3]*m[7]-m[4]*m[6]) 299} 300 301// Permutation parity of the derived axis triple. Once UP and ANTERIOR are both pinned, handedness 302// is no longer free: the lateral sign is whatever makes the frame's determinant +1, so it is DERIVED 303// rather than searched. That is the whole reason the ambiguity below can be closed. 304func nr_parity(ax: *i64) -> i64 { 305 let p: i64 = (ax[1]-ax[0]) * (ax[2]-ax[0]) * (ax[2]-ax[1]) 306 if p < 0 { return 0 - 1 } 307 return 1 308} 309// ANTERIOR DIRECTION FROM THE MESH -- the cue that handedness actually needs. 310// MEASURED, NOT ASSUMED: a bilaterally symmetric point set is near-ISOMETRIC under mirroring, so a 311// symmetric cue (joint positions, mapping distance) cannot carry handedness at all -- every rig here 312// scores sym_permil=1000 and the argmin between mirrored options picked arbitrarily, which posed two 313// of four donors MIRRORED while every limb-length tooth passed (a mirror preserves every length it 314// checks). The asymmetry a humanoid actually has is FRONT/BACK, and it lives in the MESH, not in the 315// skeleton: at the FOOT band the toes extend anteriorly far past the heel, and at the HEAD band the 316// face extends anteriorly past the back of the skull. Both bands are measured INDEPENDENTLY and must 317// AGREE; when they disagree the cue is ambiguous and this returns 0 so the caller abstains BY NAME. 318// An arbitrary pick that is right half the time is worse than a refusal: it produces confident wrong 319// output. The band height is the rig's OWN mean joint spacing, never a picked fraction of stature. 320// The band height at an extremity is that extremity's OWN BONE LENGTH -- the local anatomical 321// scale, taken where the measurement is actually made. A GLOBAL mean joint spacing was the defect: 322// it averages dense finger clusters together with long limb bones, so on a ~110-joint rig the band 323// grew tall enough that BOTH bands covered the ENTIRE MESH and the two-witness test became vacuous 324// (measured: paladin foot=head=2184, dark_knight foot=head=-140 -- byte-identical, i.e. the same 325// vertex set answering twice). 326func nr_extremity_band(bind: *i64, par: *i64, n: i64, upax: i64, want_max: i64) -> i64 { 327 if n < 2 { return 0 } 328 var je: i64 = 0 329 var ve: i64 = bind[upax] 330 var j: i64 = 1 331 while j < n { 332 let v: i64 = bind[j*3+upax] 333 if want_max == 1 { if v > ve { ve = v; je = j } } 334 if want_max == 0 { if v < ve { ve = v; je = j } } 335 j = j + 1 336 } 337 let pj: i64 = par[je] 338 if pj >= 0 { 339 let ax2: i64 = bind[pj*3] - bind[je*3] 340 let ay2: i64 = bind[pj*3+1] - bind[je*3+1] 341 let az2: i64 = bind[pj*3+2] - bind[je*3+2] 342 let l: i64 = nf_isqrt(ax2*ax2 + ay2*ay2 + az2*az2) 343 if l > 0 { return l } 344 } 345 // the extremity is the root (or a zero-length bone): fall back to its nearest neighbour joint 346 var best: i64 = 0 - 1 347 var i: i64 = 0 348 while i < n { 349 if i != je { 350 let dx: i64 = bind[i*3] - bind[je*3] 351 let dy: i64 = bind[i*3+1] - bind[je*3+1] 352 let dz: i64 = bind[i*3+2] - bind[je*3+2] 353 let d: i64 = dx*dx + dy*dy + dz*dz 354 if best < 0 { best = d } 355 if d < best { best = d } 356 } 357 i = i + 1 358 } 359 if best < 0 { return 0 } 360 return nf_isqrt(best) 361} 362// THE BAND IS DERIVED FROM THE MESH ITSELF: the SMALLEST vertical window at an extremity that holds 363// enough vertices to be a witness. No picked fraction of stature -- the population requirement sizes 364// it and the mesh's own density decides how tall that is, so a dense rig gets a tight band and a 365// sparse one gets a wider band automatically. 366// 367// THIS REPLACES BONE LENGTH, AND THAT SUBSTITUTION IS ALSO A DIAGNOSTIC. Bone length is a property of 368// the SKELETON, so a single mis-placed joint makes the extremity bone enormous and the band nonsense: 369// dark_knight and paladin measured 48,293 and 74,484-unit extremity bones and were refused for 370// overlap. If a mesh-derived band resolves them, the bone was the defect; if it does not, the 371// geometry is, and that is the same root cause as the fragmented render on the same asset. 372func nr_band_for_pop(vert: *i64, nv: i64, upax: i64, want_max: i64, minpop: i64, ulo: i64, uhi: i64) -> i64 { 373 var lo: i64 = 0 374 var hi: i64 = uhi - ulo 375 if hi < 1 { return 0 } 376 while lo < hi { 377 let mid: i64 = (lo + hi) / 2 378 var c: i64 = 0 379 var i: i64 = 0 380 while i < nv { 381 let u: i64 = vert[i*3+upax] 382 if want_max == 0 { if u <= ulo + mid { c = c + 1 } } 383 if want_max == 1 { if u >= uhi - mid { c = c + 1 } } 384 i = i + 1 385 } 386 if c >= minpop { hi = mid } 387 if c < minpop { lo = mid + 1 } 388 } 389 return lo 390} 391func nr_anterior(vert: *i64, nv: i64, ax: *i64, blo: i64, bhi: i64, minpop: i64, ev: *i64) -> i64 { 392 ev[4] = 0 393 if nv < 1 { return 0 } 394 if blo < 1 { return 0 } 395 if bhi < 1 { return 0 } 396 var umin: i64 = vert[ax[0]] 397 var umax: i64 = umin 398 var dmin: i64 = vert[ax[2]] 399 var dmax: i64 = dmin 400 var i: i64 = 1 401 while i < nv { 402 let u: i64 = vert[i*3+ax[0]] 403 let d: i64 = vert[i*3+ax[2]] 404 if u < umin { umin = u } 405 if u > umax { umax = u } 406 if d < dmin { dmin = d } 407 if d > dmax { dmax = d } 408 i = i + 1 409 } 410 // TWO WITNESSES THAT ARE SECRETLY THE SAME WITNESS AGREE ONE HUNDRED PERCENT OF THE TIME. 411 // The bands must be DISJOINT before their agreement means anything -- this is the shared- 412 // failure-mode law arriving inside one organ. Overlap is reported (ev[4]) so the caller can 413 // abstain BY NAME on THIS cause rather than on a generic ambiguity. 414 // Bands sized from the MESH (see nr_band_for_pop). The bone-derived values are still reported in 415 // ev[5]/ev[6] beside them, because the COMPARISON is the diagnostic: a bone band far larger than 416 // the mesh band means that extremity's joint is mis-placed, not that the rig is unusual. 417 let mblo: i64 = nr_band_for_pop(vert, nv, ax[0], 0, minpop, umin, umax) 418 let mbhi: i64 = nr_band_for_pop(vert, nv, ax[0], 1, minpop, umin, umax) 419 ev[5] = blo 420 ev[6] = bhi 421 ev[7] = mblo 422 ev[8] = mbhi 423 if mblo < 1 { ev[4] = 2; return 0 } 424 if mbhi < 1 { ev[4] = 2; return 0 } 425 if umin + mblo >= umax - mbhi { ev[4] = 1; return 0 } 426 let mid: i64 = (dmin + dmax) / 2 427 var lo_s: i64 = 0 428 var lo_n: i64 = 0 429 var hi_s: i64 = 0 430 var hi_n: i64 = 0 431 i = 0 432 while i < nv { 433 let u: i64 = vert[i*3+ax[0]] 434 let d: i64 = vert[i*3+ax[2]] - mid 435 if u <= umin + mblo { lo_s = lo_s + d; lo_n = lo_n + 1 } 436 if u >= umax - mbhi { hi_s = hi_s + d; hi_n = hi_n + 1 } 437 i = i + 1 438 } 439 // DISJOINTNESS IS NECESSARY BUT NOT SUFFICIENT: a band can be disjoint and still be too thin to 440 // be a witness. Measured on toon3d8: its toe-tip bone is 1 unit long, so the foot band held 441 // exactly 2 VERTICES and a whole handedness verdict rested on them. A band must therefore also 442 // be POPULATED, and the floor is derived from the asset itself -- a mesh region holding fewer 443 // points than the rig has JOINTS is a few stray vertices, not a shape. 444 if lo_n < minpop { ev[4] = 2; return 0 } 445 if hi_n < minpop { ev[4] = 2; return 0 } 446 if lo_n < 1 { return 0 } 447 if hi_n < 1 { return 0 } 448 let lo: i64 = lo_s / lo_n 449 let hi: i64 = hi_s / hi_n 450 ev[0] = lo; ev[1] = hi; ev[2] = lo_n; ev[3] = hi_n 451 if lo > 0 { if hi >= 0 { return 1 } } 452 if lo < 0 { if hi <= 0 { return 0 - 1 } } 453 // The bands CONTRADICT. They are not equally trustworthy, and the reason is anatomical rather 454 // than convenient: TOES project anteriorly on every standing humanoid, while the HEAD band is 455 // contaminated by HAIR AND HEADWEAR, which hang POSTERIORLY. Measured on the real corpus: 456 // toon3d8's long back-hair drove its head band to -2925 against a foot band of +11638, and 457 // ref9d's head band read only +1475 against a foot band of -9493 -- in both cases the head is 458 // the weaker and the dirtier signal. So a contradicting head vote overrides the foot ONLY when 459 // it is STRICTLY STRONGER; when it is, the shape is not a standing humanoid and we abstain 460 // rather than guess. dark_witch needs none of this: +62699 foot and +88153 head both agree. 461 var alo: i64 = lo 462 if alo < 0 { alo = 0 - alo } 463 var ahi: i64 = hi 464 if ahi < 0 { ahi = 0 - ahi } 465 if ahi > alo { return 0 } 466 if lo > 0 { return 1 } 467 if lo < 0 { return 0 - 1 } 468 return 0 469} 470 471// ---- verb: bindquats -- MEASURE the identity-bind convention this organ's math depends on ------ 472func nr_bindquats(path: *u8) -> i64 { 473 let lp: *i64 = sys_mmap(64) 474 let b: *u8 = nr_load(path, lp) 475 if b as i64 == 0 { nrerr("NXA-RETARGET-REFUSE cannot read input\n" as *u8); return NR_EXIT_REFUSE } 476 let flen: i64 = lp[0] 477 let w: *i64 = b as *i64 478 let swo: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8)) 479 if swo < 0 { nrerr("NXA-RETARGET-REFUSE no valid SKEL section\n" as *u8); return NR_EXIT_NOSEC } 480 let nj: i64 = w[swo] 481 var nonid: i64 = 0 482 var j: i64 = 0 483 while j < nj { 484 let so: i64 = swo + 1 + j * NR_WJ 485 var bad: i64 = 0 486 if w[so+4] != 0 { bad = 1 } 487 if w[so+5] != 0 { bad = 1 } 488 if w[so+6] != 0 { bad = 1 } 489 if w[so+7] != NR_Q12 { bad = 1 } 490 if bad == 1 { nonid = nonid + 1 } 491 j = j + 1 492 } 493 nrw(" joints=" as *u8); nrn(nj) 494 nrw(" nonidentity_bind_quats=" as *u8); nrn(nonid) 495 nrw(" identity_bind_convention=" as *u8) 496 if nonid == 0 { nrw("HOLDS\n" as *u8) } 497 if nonid != 0 { nrw("VIOLATED\n" as *u8) } 498 if nonid != 0 { return NR_EXIT_NONIDENT } 499 return 0 500} 501 502func main(argc: i64, argv: *i64) -> i64 { 503 if argc < 3 { 504 nrerr("usage: nx_nxa_retarget bindquats <file.nxa>\n" as *u8) 505 nrerr(" nx_nxa_retarget map <src.nxa> <dst.nxa>\n" as *u8) 506 nrerr(" nx_nxa_retarget retarget <src.nxa> <pose_id> <dst.nxa> [out.nxa] [weight_permil] [emit=pose]\n" as *u8) 507 return NR_EXIT_USAGE 508 } 509 let verb: *u8 = argv[1] as *u8 510 if nr_streq(verb, "bindquats" as *u8) == 1 { return nr_bindquats(argv[2] as *u8) } 511 512 let ismap: i64 = nr_streq(verb, "map" as *u8) 513 let isret: i64 = nr_streq(verb, "retarget" as *u8) 514 if ismap == 0 { if isret == 0 { 515 nrerr("NXA-RETARGET-REFUSE unknown verb\n" as *u8) 516 return NR_EXIT_USAGE 517 } } 518 // <out.nxa> is OPTIONAL: with it the posed asset is published, without it the run MEASURES ONLY. 519 // A gate must be able to exercise this organ without writing artifacts as a side effect, and the 520 // shared subprocess primitive (gk_run_capture) carries four arguments -- so the measuring form 521 // has to fit in four. Publishing is the extra, not the baseline. 522 if isret == 1 { if argc < 5 { 523 nrerr("usage: nx_nxa_retarget retarget <src.nxa> <pose_id> <dst.nxa> [out.nxa] [weight_permil] [emit=pose]\n" as *u8) 524 return NR_EXIT_USAGE 525 } } 526 527 let spath: *u8 = argv[2] as *u8 528 var pose_id: i64 = 0 529 var dpath: *u8 = argv[3] as *u8 530 var outp: *u8 = 0 as *u8 531 var wpm: i64 = NR_WFULL 532 var emitpose: i64 = 0 533 if isret == 1 { 534 pose_id = nr_atoi(argv[3] as *u8) 535 dpath = argv[4] as *u8 536 // trailing args are SCANNED, not slotted: `emit=pose` may sit anywhere after <dst.nxa>, so the positional 537 // [out.nxa] [weight_permil] contract every existing caller holds is untouched. 538 var ai: i64 = 5 539 var slot: i64 = 0 540 while ai < argc { 541 let a: *u8 = argv[ai] as *u8 542 if nr_streq(a, "emit=pose" as *u8) == 1 { emitpose = 1 } 543 if nr_streq(a, "emit=pose" as *u8) == 0 { 544 if slot == 0 { outp = a } 545 if slot == 1 { wpm = nr_atoi(a) } 546 slot = slot + 1 547 } 548 ai = ai + 1 549 } 550 if wpm < 0 { wpm = 0 } 551 if wpm > NR_WFULL { wpm = NR_WFULL } 552 } 553 554 // ---- load source ---------------------------------------------------------------------- 555 let slp: *i64 = sys_mmap(64) 556 let sb: *u8 = nr_load(spath, slp) 557 if sb as i64 == 0 { nrerr("NXA-RETARGET-REFUSE cannot read source\n" as *u8); return NR_EXIT_REFUSE } 558 let sflen: i64 = slp[0] 559 let sw: *i64 = sb as *i64 560 let sswo: i64 = nxa_find(sb, sflen, nxa_tag4("SKEL" as *u8)) 561 if sswo < 0 { nrerr("NXA-RETARGET-REFUSE source has no valid SKEL\n" as *u8); return NR_EXIT_NOSEC } 562 let ns: i64 = sw[sswo] 563 if ns < 1 { nrerr("NXA-RETARGET-REFUSE source SKEL is empty\n" as *u8); return NR_EXIT_REFUSE } 564 565 // ---- load target ---------------------------------------------------------------------- 566 let dlp: *i64 = sys_mmap(64) 567 let db: *u8 = nr_load(dpath, dlp) 568 if db as i64 == 0 { nrerr("NXA-RETARGET-REFUSE cannot read target\n" as *u8); return NR_EXIT_REFUSE } 569 let dflen: i64 = dlp[0] 570 let dw: *i64 = db as *i64 571 let dswo: i64 = nxa_find(db, dflen, nxa_tag4("SKEL" as *u8)) 572 if dswo < 0 { nrerr("NXA-RETARGET-REFUSE target has no valid SKEL (asset is not rigged)\n" as *u8); return NR_EXIT_NOSEC } 573 let nt: i64 = dw[dswo] 574 if nt < 1 { nrerr("NXA-RETARGET-REFUSE target SKEL is empty\n" as *u8); return NR_EXIT_REFUSE } 575 576 // ---- unpack binds + parents ----------------------------------------------------------- 577 let sbind: *i64 = sys_mmap(ns * 3 * 8) 578 let spar: *i64 = sys_mmap(ns * 8) 579 var j: i64 = 0 580 while j < ns { 581 let so: i64 = sswo + 1 + j * NR_WJ 582 spar[j] = sw[so] 583 sbind[j*3] = sw[so+1]; sbind[j*3+1] = sw[so+2]; sbind[j*3+2] = sw[so+3] 584 j = j + 1 585 } 586 let tbind: *i64 = sys_mmap(nt * 3 * 8) 587 let tpar: *i64 = sys_mmap(nt * 8) 588 j = 0 589 var order_bad: i64 = 0 590 while j < nt { 591 let so: i64 = dswo + 1 + j * NR_WJ 592 tpar[j] = dw[so] 593 if tpar[j] >= j { order_bad = order_bad + 1 } 594 tbind[j*3] = dw[so+1]; tbind[j*3+1] = dw[so+2]; tbind[j*3+2] = dw[so+3] 595 j = j + 1 596 } 597 // The four-line composition is parent-first and cannot be evaluated out of order. A SKEL whose 598 // parents are not emitted before their children is REFUSED BY NAME rather than composed against 599 // an unwritten parent, which would read as a plausible but wrong pose. 600 if order_bad != 0 { 601 nrerr("NXA-RETARGET-REFUSE target SKEL is not parent-first ordered\n" as *u8) 602 return NR_EXIT_REFUSE 603 } 604 605 // ---- canonical anatomical frames, DERIVED per rig --------------------------------------- 606 let sax: *i64 = sys_mmap(64) 607 let tax: *i64 = sys_mmap(64) 608 let ssym: i64 = nr_frame(sbind, ns, sax) 609 let tsym: i64 = nr_frame(tbind, nt, tax) 610 let ssg: *i64 = sys_mmap(64) 611 let tsg: *i64 = sys_mmap(64) 612 ssg[0] = 1; ssg[1] = 1; ssg[2] = 1 613 let scan: *i64 = sys_mmap(ns * 3 * 8) 614 nr_permute(sbind, ns, sax, ssg, scan) 615 let snrm: *i64 = sys_mmap(ns * 3 * 8) 616 let sse: i64 = nr_norm(scan, ns, snrm) 617 if sse < 0 { nrerr("NXA-RETARGET-REFUSE source rig has zero extent\n" as *u8); return NR_EXIT_REFUSE } 618 619 // Lateral and depth SIGN are searched rather than guessed: all four combinations are mapped and 620 // the lowest total mapping distance is taken. 621 // *** MEASURED LIMITATION, AND A CORRECTION TO THIS ORGAN'S OWN FIRST CLAIM. *** 622 // v1 of this comment said the search RESOLVES HANDEDNESS FROM THE DATA. IT DOES NOT, and the 623 // live run proved it: every rig here measures sym_permil=1000, i.e. BILATERALLY SYMMETRIC, and a 624 // symmetric point set is very nearly ISOMETRIC UNDER MIRRORING -- so both handedness options 625 // score almost the same total distance and the argmin picks between near-ties essentially 626 // arbitrarily. The evidence is in the numbers: frame_det came out +1 for toon3d8 and 627 // dark_knight but -1 for dark_witch and paladin, on four ordinary humanoids that should all 628 // agree. A mirrored frame swaps left and right, which is a wrong pose no limb-length tooth can 629 // catch -- exactly the awful-motion class. 630 // THE FIX IS NAMED, NOT GUESSED: handedness has to come from an ASYMMETRIC cue, because a 631 // symmetric one cannot carry the information. Front/back is that cue (a face, a nose, a chest 632 // are forward and do not mirror), and it lives in the MESH, not in the joint positions this 633 // search sees. Until that lands, the lateral sign on any given donor is UNPROVEN and the pose 634 // it produces may be mirrored; frame_det is printed on every run so a reader can see which. 635 let tcan: *i64 = sys_mmap(nt * 3 * 8) 636 let bnrm: *i64 = sys_mmap(nt * 3 * 8) 637 let map: *i64 = sys_mmap(nt * 8) 638 // HANDEDNESS IS DERIVED, NOT SEARCHED. The mesh carries the front/back asymmetry the skeleton 639 // cannot; once UP and ANTERIOR are pinned the lateral sign is forced to whatever makes the 640 // frame determinant +1, so both rigs land in the SAME handed frame by construction. 641 let svwo: i64 = nxa_find(sb, sflen, nxa_tag4("VERT" as *u8)) 642 let tvwo: i64 = nxa_find(db, dflen, nxa_tag4("VERT" as *u8)) 643 if svwo < 0 { nrerr("NXA-RETARGET-REFUSE source has no valid VERT to derive anterior from\n" as *u8); return NR_EXIT_NOSEC } 644 if tvwo < 0 { nrerr("NXA-RETARGET-REFUSE target has no valid VERT to derive anterior from\n" as *u8); return NR_EXIT_NOSEC } 645 let svp: *i64 = ((sw as i64) + (svwo+1)*8) as *i64 646 let tvp: *i64 = ((dw as i64) + (tvwo+1)*8) as *i64 647 // 16 words: the evidence block now carries ev[0..8] -- foot, head, both counts, the abstain 648 // cause, and BOTH band sources. A 64-byte (8-word) allocation was one word short and the arena 649 // canary caught it as ARENA-OVERRUN on the very first run. Sized with headroom and NAMED, so the 650 // next field added does not silently walk off the end. 651 let sev: *i64 = sys_mmap(128) 652 let tev: *i64 = sys_mmap(128) 653 let sblo: i64 = nr_extremity_band(sbind, spar, ns, sax[0], 0) 654 let sbhi: i64 = nr_extremity_band(sbind, spar, ns, sax[0], 1) 655 let tblo: i64 = nr_extremity_band(tbind, tpar, nt, tax[0], 0) 656 let tbhi: i64 = nr_extremity_band(tbind, tpar, nt, tax[0], 1) 657 let sant: i64 = nr_anterior(svp, sw[svwo], sax, sblo, sbhi, ns, sev) 658 let tant: i64 = nr_anterior(tvp, dw[tvwo], tax, tblo, tbhi, nt, tev) 659 nrw(" src_anterior=" as *u8); nrn(sant) 660 nrw(" foot=" as *u8); nrn(sev[0]); nrw(" head=" as *u8); nrn(sev[1]) 661 nrw(" bands=" as *u8); nrn(sblo); nrw("/" as *u8); nrn(sbhi) 662 nrw(" nverts=" as *u8); nrn(sev[2]); nrw("/" as *u8); nrn(sev[3]) 663 nrw(" overlap=" as *u8); nrn(sev[4]); nrw("\n" as *u8) 664 nrw(" tgt_anterior=" as *u8); nrn(tant) 665 nrw(" foot=" as *u8); nrn(tev[0]); nrw(" head=" as *u8); nrn(tev[1]) 666 nrw(" meshband=" as *u8); nrn(tev[7]); nrw("/" as *u8); nrn(tev[8]) 667 nrw(" boneband=" as *u8); nrn(tev[5]); nrw("/" as *u8); nrn(tev[6]) 668 nrw(" nverts=" as *u8); nrn(tev[2]); nrw("/" as *u8); nrn(tev[3]) 669 nrw(" overlap=" as *u8); nrn(tev[4]); nrw("\n" as *u8) 670 if sant == 0 { 671 if sev[4] == 1 { nrerr("NXA-RETARGET-REFUSE source foot and head bands OVERLAP -- they are the same witness twice, so their agreement proves nothing\n" as *u8) } 672 if sev[4] == 2 { nrerr("NXA-RETARGET-REFUSE source anterior band holds fewer vertices than the rig has joints -- too thin to be a witness\n" as *u8) } 673 if sev[4] == 0 { nrerr("NXA-RETARGET-REFUSE source anterior is AMBIGUOUS (a stronger head vote contradicts the foot) -- abstaining rather than picking a handedness\n" as *u8) } 674 return NR_EXIT_REFUSE 675 } 676 if tant == 0 { 677 if tev[4] == 1 { nrerr("NXA-RETARGET-REFUSE target foot and head bands OVERLAP -- they are the same witness twice, so their agreement proves nothing\n" as *u8) } 678 if tev[4] == 2 { nrerr("NXA-RETARGET-REFUSE target anterior band holds fewer vertices than the rig has joints -- too thin to be a witness\n" as *u8) } 679 if tev[4] == 0 { nrerr("NXA-RETARGET-REFUSE target anterior is AMBIGUOUS (a stronger head vote contradicts the foot) -- abstaining rather than picking a handedness\n" as *u8) } 680 return NR_EXIT_REFUSE 681 } 682 ssg[2] = sant; ssg[1] = nr_parity(sax) * sant 683 tsg[0] = 1; tsg[2] = tant; tsg[1] = nr_parity(tax) * tant 684 let bslat: i64 = tsg[1] 685 let bsdep: i64 = tsg[2] 686 nr_permute(sbind, ns, sax, ssg, scan) 687 if nr_norm(scan, ns, snrm) < 0 { nrerr("NXA-RETARGET-REFUSE source rig has zero extent\n" as *u8); return NR_EXIT_REFUSE } 688 nr_permute(tbind, nt, tax, tsg, tcan) 689 let tse: i64 = nr_norm(tcan, nt, bnrm) 690 if tse < 0 { nrerr("NXA-RETARGET-REFUSE target rig has zero extent\n" as *u8); return NR_EXIT_REFUSE } 691 var maxd: i64 = 0 692 var totd: i64 = 0 693 j = 0 694 while j < nt { 695 var best: i64 = 0 - 1 696 var bi: i64 = 0 697 var i: i64 = 0 698 while i < ns { 699 let d: i64 = nr_d2(bnrm, j, snrm, i) 700 if best < 0 { best = d; bi = i } 701 if d < best { best = d; bi = i } 702 i = i + 1 703 } 704 map[j] = bi 705 let dd: i64 = nf_isqrt(best) 706 if dd > maxd { maxd = dd } 707 totd = totd + dd 708 j = j + 1 709 } 710 let bdst: *i64 = sys_mmap(nt * 8) 711 j = 0 712 while j < nt { bdst[j] = nf_isqrt(nr_d2(bnrm, j, snrm, map[j])); j = j + 1 } 713 // THE ADMISSION BOUND IS THE SOURCE'S OWN JOINT SPACING, NOT THE TARGET'S. The question a 714 // mapping has to answer is whether the SOURCE can resolve a given joint, so the scale that 715 // decides it belongs to the source. Deriving it from the target was measured wrong on the first 716 // live run: a 370-joint donor packs its joints 3 permil apart, so every match from a 104-joint 717 // source read as LOOSE and the whole retarget refused -- a bound that indicted the source rig 718 // for the target's density. A target joint the source genuinely cannot resolve (a finger, a 719 // facial bone) is not an error either: it INHERITS its parent's articulation and rides along, 720 // which is what a rigid sub-chain should do, instead of fabricating motion for it. 721 let bound: i64 = nr_mean_nn(snrm, ns) 722 nrw(" src_frame up=" as *u8); nrn(sax[0]); nrw(" lat=" as *u8); nrn(sax[1]); nrw(" dep=" as *u8); nrn(sax[2]) 723 nrw(" sym_permil=" as *u8); nrn(ssym); nrw("\n" as *u8) 724 nrw(" tgt_frame up=" as *u8); nrn(tax[0]); nrw(" lat=" as *u8); nrn(tax[1]); nrw(" dep=" as *u8); nrn(tax[2]) 725 nrw(" sym_permil=" as *u8); nrn(tsym) 726 nrw(" lat_sign=" as *u8); nrn(bslat); nrw(" dep_sign=" as *u8); nrn(bsdep); nrw("\n" as *u8) 727 nrw(" src_joints=" as *u8); nrn(ns) 728 nrw(" tgt_joints=" as *u8); nrn(nt) 729 nrw(" map_max_permil=" as *u8); nrn(maxd) 730 nrw(" map_mean_permil=" as *u8); nrn(totd / nt) 731 nrw(" admit_bound_permil=" as *u8); nrn(bound) 732 nrw("\n" as *u8) 733 var nmapped: i64 = 0 734 var ninherit: i64 = 0 735 j = 0 736 while j < nt { 737 if bdst[j] <= bound { nmapped = nmapped + 1 } 738 if bdst[j] > bound { ninherit = ninherit + 1 } 739 j = j + 1 740 } 741 nrw(" mapped=" as *u8); nrn(nmapped) 742 nrw(" inherited=" as *u8); nrn(ninherit) 743 nrw(" of=" as *u8); nrn(nt) 744 nrw("\n" as *u8) 745 // A partition that does not sum is a leak, so it is asserted rather than assumed. 746 if nmapped + ninherit != nt { 747 nrerr("NXA-RETARGET-REFUSE mapped/inherited partition does not sum to the joint count\n" as *u8) 748 return NR_EXIT_REFUSE 749 } 750 // Refused only when the source can resolve NOTHING: then there is no articulation to transfer 751 // and any output would be the bind pose wearing a retarget's name. 752 if nmapped == 0 { 753 nrerr("NXA-RETARGET-REFUSE source rig resolves no target joint within its own joint spacing\n" as *u8) 754 return NR_EXIT_REFUSE 755 } 756 if ismap == 1 { 757 nrw(" verdict=ADMITTED\n" as *u8) 758 return 0 759 } 760 761 // ---- source pose ---------------------------------------------------------------------- 762 let sD: *i64 = sys_mmap(ns * 4 * 8) 763 let sdt: *i64 = sys_mmap(ns * 3 * 8) 764 j = 0 765 while j < ns { 766 sD[j*4] = 0; sD[j*4+1] = 0; sD[j*4+2] = 0; sD[j*4+3] = NR_Q12 767 sdt[j*3] = 0; sdt[j*3+1] = 0; sdt[j*3+2] = 0 768 j = j + 1 769 } 770 var applied: i64 = 0 771 let pwo: i64 = nxa_find(sb, sflen, nxa_tag4("POSE" as *u8)) 772 if pwo < 0 { 773 nrerr("NXA-RETARGET-REFUSE source carries no POSE library to retarget from\n" as *u8) 774 return NR_EXIT_NOSEC 775 } 776 let np: i64 = sw[pwo] 777 var cur: i64 = pwo + 1 778 var p: i64 = 0 779 while p < np { 780 let pid: i64 = sw[cur] 781 let ne: i64 = sw[cur+1] 782 if pid == pose_id { 783 var e: i64 = 0 784 while e < ne { 785 let eo: i64 = cur + 2 + e * NR_WPE 786 let jj: i64 = sw[eo] 787 if jj >= 0 { if jj < ns { 788 sD[jj*4] = sw[eo+1]; sD[jj*4+1] = sw[eo+2] 789 sD[jj*4+2] = sw[eo+3]; sD[jj*4+3] = sw[eo+4] 790 sdt[jj*3] = sw[eo+5]; sdt[jj*3+1] = sw[eo+6]; sdt[jj*3+2] = sw[eo+7] 791 applied = applied + 1 792 } } 793 e = e + 1 794 } 795 } 796 cur = cur + 2 + ne * NR_WPE 797 p = p + 1 798 } 799 if applied == 0 { 800 nrerr("NXA-RETARGET-REFUSE source POSE library has no entries for that pose_id\n" as *u8) 801 return NR_EXIT_REFUSE 802 } 803 804 // Reframe the source pose into the TARGET's raw axis convention BEFORE composing. Without this 805 // a rotation about the source's stature axis would be applied about the target's depth axis -- 806 // the mapping would be geometrically correct and the motion still wrong. 807 let det: i64 = nr_frame_det(sax, ssg, tax, tsg) 808 let tmpv: *i64 = sys_mmap(64) 809 let tmpo: *i64 = sys_mmap(64) 810 j = 0 811 while j < ns { 812 tmpv[0] = sD[j*4]; tmpv[1] = sD[j*4+1]; tmpv[2] = sD[j*4+2] 813 nr_reframe_v(tmpv, tmpo, sax, ssg, tax, tsg) 814 sD[j*4] = tmpo[0] * det; sD[j*4+1] = tmpo[1] * det; sD[j*4+2] = tmpo[2] * det 815 tmpv[0] = sdt[j*3]; tmpv[1] = sdt[j*3+1]; tmpv[2] = sdt[j*3+2] 816 nr_reframe_v(tmpv, tmpo, sax, ssg, tax, tsg) 817 sdt[j*3] = tmpo[0]; sdt[j*3+1] = tmpo[1]; sdt[j*3+2] = tmpo[2] 818 j = j + 1 819 } 820 nrw(" frame_det=" as *u8); nrn(det); nrw("\n" as *u8) 821 822 // ---- the four lines ------------------------------------------------------------------- 823 let W: *i64 = sys_mmap(nt * 4 * 8) 824 let P: *i64 = sys_mmap(nt * 3 * 8) 825 let tdt: *i64 = sys_mmap(nt * 3 * 8) 826 let cq: *i64 = sys_mmap(64) 827 let aq: *i64 = sys_mmap(64) 828 let o3: *i64 = sys_mmap(64) 829 let scr: *i64 = sys_mmap(256) 830 j = 0 831 while j < nt { 832 let s: i64 = map[j] 833 let pj: i64 = tpar[j] 834 if pj < 0 { 835 // root: no parent to conjugate against, so A = D. Root translation is the ONE quantity 836 // that must cross skeletons by size, so it is scaled by the ratio of the two rigs' own 837 // measured stature extents -- derived from the assets, never a picked factor. 838 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] 839 P[j*3] = tbind[j*3] + sdt[s*3] * tse / sse 840 P[j*3+1] = tbind[j*3+1] + sdt[s*3+1] * tse / sse 841 P[j*3+2] = tbind[j*3+2] + sdt[s*3+2] * tse / sse 842 } 843 if pj >= 0 { 844 let sp: i64 = map[pj] 845 // A joint the source cannot resolve within its OWN joint spacing gets IDENTITY local 846 // articulation, so it rides its parent rigidly instead of being handed a rotation 847 // borrowed from whatever joint happened to be nearest. Fabricated articulation on an 848 // unresolvable joint is exactly the awful-motion class: it looks like animation and is 849 // not derived from anything. 850 aq[0] = 0; aq[1] = 0; aq[2] = 0; aq[3] = NR_Q12 851 if bdst[j] <= bound { 852 nf_qconj(nr_q(sD, sp), cq) 853 nf_qmul(cq, nr_q(sD, s), aq) 854 } 855 nf_qmul(nr_q(W, pj), aq, nr_q(W, j)) 856 nf_qrotv(nr_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) 857 P[j*3] = P[pj*3] + o3[0] 858 P[j*3+1] = P[pj*3+1] + o3[1] 859 P[j*3+2] = P[pj*3+2] + o3[2] 860 } 861 tdt[j*3] = P[j*3] - tbind[j*3] 862 tdt[j*3+1] = P[j*3+1] - tbind[j*3+1] 863 tdt[j*3+2] = P[j*3+2] - tbind[j*3+2] 864 j = j + 1 865 } 866 867 // ---- hold at wpm/1000 so a SEQUENCE can be emitted ------------------------------------- 868 // nlerp from identity with the spec's shortest-path sign flip: a delta whose w is negative sits 869 // on the far side of the quaternion double cover, and blending toward it without the flip takes 870 // the long way round, which renders as a limb rotating the wrong way. 871 if wpm < NR_WFULL { 872 j = 0 873 while j < nt { 874 var qx: i64 = W[j*4] 875 var qy: i64 = W[j*4+1] 876 var qz: i64 = W[j*4+2] 877 var qw: i64 = W[j*4+3] 878 if qw < 0 { qx = 0 - qx; qy = 0 - qy; qz = 0 - qz; qw = 0 - qw } 879 W[j*4] = qx * wpm / NR_WFULL 880 W[j*4+1] = qy * wpm / NR_WFULL 881 W[j*4+2] = qz * wpm / NR_WFULL 882 W[j*4+3] = (NR_Q12 * (NR_WFULL - wpm) + qw * wpm) / NR_WFULL 883 nf_qnorm(nr_q(W, j)) 884 tdt[j*3] = tdt[j*3] * wpm / NR_WFULL 885 tdt[j*3+1] = tdt[j*3+1] * wpm / NR_WFULL 886 tdt[j*3+2] = tdt[j*3+2] * wpm / NR_WFULL 887 j = j + 1 888 } 889 } 890 891 // ---- limb-length residual: the property the algorithm preserves BY CONSTRUCTION --------- 892 // Reported, not merely asserted, so a regression is visible as a number rather than a verdict. 893 var maxlen: i64 = 0 894 j = 0 895 while j < nt { 896 let pj: i64 = tpar[j] 897 if pj >= 0 { 898 let ax: i64 = tbind[j*3] - tbind[pj*3] 899 let ay: i64 = tbind[j*3+1] - tbind[pj*3+1] 900 let az: i64 = tbind[j*3+2] - tbind[pj*3+2] 901 let bx: i64 = P[j*3] - P[pj*3] 902 let by: i64 = P[j*3+1] - P[pj*3+1] 903 let bz: i64 = P[j*3+2] - P[pj*3+2] 904 let la: i64 = nf_isqrt(ax*ax + ay*ay + az*az) 905 let lb: i64 = nf_isqrt(bx*bx + by*by + bz*bz) 906 var dl: i64 = lb - la 907 if dl < 0 { dl = 0 - dl } 908 if dl > maxlen { maxlen = dl } 909 } 910 j = j + 1 911 } 912 nrw(" pose_entries_applied=" as *u8); nrn(applied) 913 nrw(" weight_permil=" as *u8); nrn(wpm) 914 nrw(" limb_len_residual_max=" as *u8); nrn(maxlen) 915 nrw("\n" as *u8) 916 917 // ---- skin the target through the SHARED evaluator --------------------------------------- 918 let vwo: i64 = nxa_find(db, dflen, nxa_tag4("VERT" as *u8)) 919 let kwo: i64 = nxa_find(db, dflen, nxa_tag4("SKIN" as *u8)) 920 if vwo < 0 { nrerr("NXA-RETARGET-REFUSE target has no valid VERT\n" as *u8); return NR_EXIT_NOSEC } 921 if kwo < 0 { nrerr("NXA-RETARGET-REFUSE target has no valid SKIN\n" as *u8); return NR_EXIT_NOSEC } 922 let nv: i64 = dw[vwo] 923 let nsk: i64 = dw[kwo] 924 if nsk != nv { 925 nrerr("NXA-RETARGET-REFUSE target SKIN vertex count does not match VERT count\n" as *u8) 926 return NR_EXIT_REFUSE 927 } 928 let abytes: i64 = sk_bytes_for(nt, nv, NR_INF) 929 let base: i64 = sys_mmap(abytes) as i64 930 if base == 0 { nrerr("NXA-RETARGET-REFUSE arena allocation failed\n" as *u8); return NR_EXIT_REFUSE } 931 if sk_init_cap(base, nt, nv, NR_INF) < 0 { 932 nrerr("NXA-RETARGET-REFUSE evaluator rejected the declared capacity\n" as *u8) 933 return NR_EXIT_REFUSE 934 } 935 let mm: *i64 = sys_mmap(128) 936 let tv: *i64 = sys_mmap(64) 937 j = 0 938 while j < nt { 939 let bx: i64 = tbind[j*3] 940 let by: i64 = tbind[j*3+1] 941 let bz: i64 = tbind[j*3+2] 942 let bi: i64 = sk_add_bone(base, tpar[j], bx, by, bz) 943 if bi < 0 { nrerr("NXA-RETARGET-REFUSE bone capacity exceeded\n" as *u8); return NR_EXIT_REFUSE } 944 // D -> 3x3 fx256 by rotating the fx256 basis: column k = D e_k (the sibling player's form). 945 var k: i64 = 0 946 while k < 3 { 947 var ex: i64 = 0 948 var ey: i64 = 0 949 var ez: i64 = 0 950 if k == 0 { ex = NR_FX } 951 if k == 1 { ey = NR_FX } 952 if k == 2 { ez = NR_FX } 953 nf_qrotv(nr_q(W, j), ex, ey, ez, o3, scr) 954 mm[k] = o3[0]; mm[3+k] = o3[1]; mm[6+k] = o3[2] 955 k = k + 1 956 } 957 let bp: *i64 = sk_bone(base, bi) 958 k = 0 959 while k < 9 { bp[6+k] = mm[k]; k = k + 1 } 960 sk_matvec(mm, bx, by, bz, tv) 961 bp[15] = bx - tv[0] + tdt[j*3] * NR_DT_MODEL 962 bp[16] = by - tv[1] + tdt[j*3+1] * NR_DT_MODEL 963 bp[17] = bz - tv[2] + tdt[j*3+2] * NR_DT_MODEL 964 bp[18] = 1 965 j = j + 1 966 } 967 let bs: *i64 = sys_mmap(64) 968 let ws: *i64 = sys_mmap(64) 969 var i2: i64 = 0 970 while i2 < nv { 971 let ko: i64 = kwo + 1 + i2 * NR_WJ 972 var tot: i64 = 0 973 var bigk: i64 = 0 974 var bigw: i64 = 0 - 1 975 var k2: i64 = 0 976 while k2 < NR_INF { 977 let jw: i64 = dw[ko + 4 + k2] 978 let cw: i64 = jw * NR_FX / NR_WQ 979 bs[k2] = dw[ko + k2] 980 ws[k2] = cw 981 tot = tot + cw 982 if jw > bigw { bigw = jw; bigk = k2 } 983 k2 = k2 + 1 984 } 985 // residual folded into the largest influence so the sum is EXACTLY fx256: an under-sum 986 // shrinks the vertex toward the origin, which is silent volume loss. 987 if tot > 0 { ws[bigk] = ws[bigk] + (NR_FX - tot) } 988 let vo: i64 = vwo + 1 + i2 * 3 989 if sk_add_vert_n(base, bs, ws, NR_INF, dw[vo], dw[vo+1], dw[vo+2]) < 0 { 990 nrerr("NXA-RETARGET-REFUSE vertex capacity exceeded\n" as *u8) 991 return NR_EXIT_REFUSE 992 } 993 i2 = i2 + 1 994 } 995 sk_skin(base) 996 997 // ---- measure BEFORE publishing ---------------------------------------------------------- 998 // A WRITER MUST NOT MUTATE A READER'S INPUT. The publish below overwrites the VERT payload in 999 // place -- the very buffer this loop compares the skinned output against -- so the measurement 1000 // is taken first, and the publish writes into its OWN copy. The sibling player shipped a v1 1001 // that reversed this and printed max_disp=0 for six correctly-posed assets: a silent, 1002 // self-inflicted false negative that reads exactly like broken skinning. 1003 var maxd2: i64 = 0 1004 var mind2: i64 = 0 - 1 1005 i2 = 0 1006 while i2 < nv { 1007 let vo: i64 = vwo + 1 + i2 * 3 1008 let op: *i64 = sk_out(base, i2) 1009 let ddx: i64 = op[0] - dw[vo] 1010 let ddy: i64 = op[1] - dw[vo+1] 1011 let ddz: i64 = op[2] - dw[vo+2] 1012 let dd: i64 = nf_isqrt(ddx*ddx + ddy*ddy + ddz*ddz) 1013 if dd > maxd2 { maxd2 = dd } 1014 if mind2 < 0 { mind2 = dd } 1015 if dd < mind2 { mind2 = dd } 1016 i2 = i2 + 1 1017 } 1018 nrw(" verts=" as *u8); nrn(nv) 1019 nrw(" max_disp=" as *u8); nrn(maxd2) 1020 nrw(" min_disp=" as *u8); nrn(mind2) 1021 nrw(" spread=" as *u8); nrn(maxd2 - mind2) 1022 nrw("\n" as *u8) 1023 1024 if outp as i64 != 0 { if emitpose == 1 { 1025 // emit=pose: publish the POSE LIBRARY, not a baked mesh. W and tdt per target joint ARE the spec's POSE entry 1026 // (world-delta quat about bind + dt in model units -- the same pair the skinning above consumed), so a player 1027 // replays this file with the exact LBS the ANIM path proves and nx_nxa_play becomes an INDEPENDENT witness of 1028 // the motion measured above. VERT stays at bind, so the asset is still a rig and not a statue; a second run with 1029 // another pose id ADDS to the library, the same id REPLACES. MEASURED 2026-09-05: the baked form read 1030 // poses_in_library=0 joints_posed=0 on every donor while the house rig read 104/104. 1031 let jl: *i64 = sys_mmap(nt * 8) 1032 let pst: *i64 = sys_mmap(64) 1033 var jj2: i64 = 0 1034 while jj2 < nt { jl[jj2] = jj2; jj2 = jj2 + 1 } 1035 let pwb: i64 = npw_write(db, dflen, pose_id, jl, W, tdt, nt, outp, pst) 1036 if pwb < 0 { 1037 nrerr("NXA-RETARGET-REFUSE POSE publish failed (nx_nxa_posewrite_lib code below)\n" as *u8) 1038 nrw(" posewrite_code=" as *u8); nrn(pwb); nrw("\n" as *u8) 1039 return NR_EXIT_REFUSE 1040 } 1041 nrw(" published=" as *u8); nrn(pwb) 1042 nrw(" of=" as *u8); nrn(pwb) 1043 nrw(" mode=pose pose_id=" as *u8); nrn(pose_id) 1044 nrw(" entries=" as *u8); nrn(pst[NPW_ST_N]) 1045 nrw(" kept_poses=" as *u8); nrn(pst[NPW_ST_KEPT]) 1046 nrw(" sections=" as *u8); nrn(pst[NPW_ST_SECTIONS]) 1047 nrw("\n" as *u8) 1048 return 0 1049 } } 1050 if outp as i64 != 0 { 1051 // Publish posed VERT IN PLACE into our OWN copy: the payload keeps its word length, so only 1052 // the VERT section checksum and the TOC checksum are recomputed. No TOC surgery, no section 1053 // insertion -- the same publish shape the player already ships and proves. 1054 let olp: *i64 = sys_mmap(64) 1055 let ob: *u8 = nr_load(dpath, olp) 1056 if ob as i64 == 0 { nrerr("NXA-RETARGET-REFUSE cannot re-read target for publish\n" as *u8); return NR_EXIT_REFUSE } 1057 let oflen: i64 = olp[0] 1058 let ow: *i64 = ob as *i64 1059 let ovwo: i64 = nxa_find(ob, oflen, nxa_tag4("VERT" as *u8)) 1060 if ovwo < 0 { nrerr("NXA-RETARGET-REFUSE publish copy lost VERT\n" as *u8); return NR_EXIT_REFUSE } 1061 i2 = 0 1062 while i2 < nv { 1063 let vo: i64 = ovwo + 1 + i2 * 3 1064 let op: *i64 = sk_out(base, i2) 1065 ow[vo] = op[0]; ow[vo+1] = op[1]; ow[vo+2] = op[2] 1066 i2 = i2 + 1 1067 } 1068 let ns2: i64 = ow[2] 1069 let tb2: *i64 = ((ob as i64) + 32) as *i64 1070 var s2: i64 = 0 1071 while s2 < ns2 { 1072 if tb2[s2*4] == nxa_tag4("VERT" as *u8) { 1073 let off: i64 = tb2[s2*4+1] 1074 let wl: i64 = tb2[s2*4+2] 1075 let pw: *i64 = ((ob as i64) + off) as *i64 1076 tb2[s2*4+3] = nxa_check2(1, pw, wl) 1077 } 1078 s2 = s2 + 1 1079 } 1080 ow[3] = nxa_check2(1, tb2, ns2*4) 1081 let fd: i64 = sys_openat_wr(outp, NR_MODE) 1082 if fd < 0 { nrerr("NXA-RETARGET-REFUSE cannot open output\n" as *u8); return NR_EXIT_REFUSE } 1083 let wrote: i64 = sys_write(fd, ob, oflen) 1084 sys_close(fd) 1085 nrw(" published=" as *u8); nrn(wrote) 1086 nrw(" of=" as *u8); nrn(oflen) 1087 nrw("\n" as *u8) 1088 if wrote != oflen { nrerr("NXA-RETARGET-REFUSE short write on publish\n" as *u8); return NR_EXIT_REFUSE } 1089 } 1090 return 0 1091}