code wiki / (root) / nx_rigbench_lib_t21.nx

nx_rigbench_lib_t21.nx source

↩ module page · 535 lines · 25225 B

1// nx_rigbench_lib.nx -- LIB: THE REAL-CHARACTER REFEREE FOR THE AUTO-RIG (dcc DC16 rung M2): score the sovereign auto-rig 2// against an ARTIST rig on the same mesh, in the artist's own units, with the artist's own pose. Three legs, each a 3// number no story can replace: 4// A. SKELETON -- the bare mesh (VERT+TRIS only) goes through nx_curveskel; every artist joint's distance to the 5// nearest joint of ours is measured, and an artist joint counts as COVERED when one of ours lies within the 6// artist rig's OWN mean bone length (derived from that rig, never picked). The reverse names our SPURIOUS joints 7// (farther than a bone length from every artist joint). Our limb count is reported beside the artist's leaf count. 8// B. WEIGHTS -- the artist SKELETON is held fixed and nx_boneheat re-weights the mesh (bh_run REPLACES the SKIN 9// section in place and keeps every other section); per-vertex L1 between our four influences and the artist's, 10// as a permil of the NXA weight scale, plus the fraction of vertices whose strongest joint agrees. 11// C. DEFORMATION, the referee-grade number -- the artist's own POSE is played by the SERVED nx_nxa_play twice, 12// once with the artist's weights and once with ours, and md_measure (the estate's one mesh ruler) judges the 13// two posed skins against each other: max and p95 surface deviation. When the rig carries no POSE this leg 14// ABSTAINS (exit 3) rather than acquits; A and B still print. 15// UNITS: NXA 0.01 mm; md_measure reports 0.1 mm for a mesh fed in millimetres (measured 2026-09-06); the report 16// says which. Usage: nx_rigbench <rigged.nxa> <workdir> [pose_id] [cells] Exits: 0 ok | 2 usage | 3 abstain | 4 refuse 17// license_tier: ORIGINAL expect_exit: 0 18import "nx_rigbench_motion_t21.nx" 19import "nx_syscalls.nx" 20import "nx_nxa.nx" 21import "nx_curveskel_lib.nx" 22import "nx_tool_run.nx" 23import "nx_nxmesh_lib.nx" 24import "nx_mmdev_lib.nx" 25 26const RB_PLAY: *u8 = "/volume1/homes/elderwesto/nishihost/nx_nxa_play.elf" 27const RB_EXIT_OK: i64 = 0 28const RB_EXIT_USAGE: i64 = 2 29const RB_EXIT_ABSTAIN: i64 = 3 30const RB_EXIT_REFUSE: i64 = 4 31const RB_CELLS_DEFAULT: i64 = 96 32const RB_POSE_DEFAULT: i64 = 1 33const RB_W_PARENT: i64 = 0 34const RB_W_X: i64 = 1 35const RB_W_Y: i64 = 2 36const RB_W_Z: i64 = 3 37const RB_SKIN_WORDS: i64 = 8 38const RB_SKIN_INF: i64 = 4 39const RB_Q12: i64 = 4096 40const RB_PERMIL: i64 = 1000 41const RB_PATH_CAP: i64 = 4096 42const RB_ARGV_SLOTS: i64 = 8 43const RB_WORD_BYTES: i64 = 8 44const RB_CAPTURE_CAP: i64 = 65536 45const RB_NO_TIMEOUT: i64 = 0 46const RB_SLASH: i64 = 47 47const RB_WFULL: *u8 = "1000" 48const RB_NXA_PER_TENTH: i64 = 10 // NXA 0.01 mm -> NXMESH input tenths of a mm 49const RB_MAGIC: *u8 = "NXMSH2" 50const RB_MAGIC_LEN: i64 = 6 51const RB_MAGIC_FIELD: i64 = 8 52const RB_UMM_PER_MM: i64 = 100 53const RB_P95_NUM: i64 = 95 54const RB_P95_DEN: i64 = 100 55 56func rbw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 57func rbn(v: i64) -> i64 { 58 let b: *u8 = sys_mmap(32) 59 var x: i64 = v 60 if x < 0 { b[0] = 45 as u8; sys_write(1, b, 1); x = 0 - x } 61 if x == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 } 62 var d: i64 = 0 63 var y: i64 = x 64 while y > 0 { d = d + 1; y = y / 10 } 65 var i: i64 = d 66 while i > 0 { i = i - 1; b[i] = ((x % 10) + 48) as u8; x = x / 10 } 67 sys_write(1, b, d) 68 return 0 69} 70func rb_kv(k: *u8, v: i64) -> i64 { rbw(k); rbn(v); rbw("\n" as *u8); return 0 } 71func rb_atoi(s: *u8) -> i64 { 72 var v: i64 = 0 73 var i: i64 = 0 74 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } 75 return v 76} 77func rb_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 78// write v as a NUL-terminated decimal into out; returns the digit count 79func rb_itoa(out: *u8, v: i64) -> i64 { 80 if v == 0 { out[0] = 48 as u8; out[1] = 0 as u8; return 1 } 81 var d: i64 = 0; var y: i64 = v 82 while y > 0 { d = d + 1; y = y / 10 } 83 var i: i64 = d; var x: i64 = v 84 while i > 0 { i = i - 1; out[i] = ((x % 10) + 48) as u8; x = x / 10 } 85 out[d] = 0 as u8; return d 86} 87// the non-negative integer immediately after `key` in buf[0..n), or -1 if the key or a digit is absent. 88// Used to read `joints_posed=` from a captured NXA-PLAY line so a static pose cannot silently vacate the leg. 89func rb_after(buf: *u8, n: i64, key: *u8) -> i64 { 90 var kl: i64 = 0 91 while key[kl] != (0 as u8) { kl = kl + 1 } 92 var i: i64 = 0 93 while i + kl <= n { 94 var j: i64 = 0 95 var ok: i64 = 1 96 while j < kl { if buf[i + j] != key[j] { ok = 0; j = kl } else { j = j + 1 } } 97 if ok == 1 { 98 var p: i64 = i + kl 99 var val: i64 = 0 100 var any: i64 = 0 101 while p < n { let c: i64 = buf[p] as i64; if c < 48 { break } if c > 57 { break } val = val * 10 + (c - 48); any = 1; p = p + 1 } 102 if any == 1 { return val } 103 return 0 - 1 104 } 105 i = i + 1 106 } 107 return 0 - 1 108} 109// <workdir>/<name> into a fresh buffer 110func rb_join(dir: *u8, name: *u8) -> *u8 { 111 let out: *u8 = sys_mmap(RB_PATH_CAP) 112 var i: i64 = 0 113 while dir[i] != (0 as u8) { if i < RB_PATH_CAP - 2 { out[i] = dir[i] } i = i + 1 } 114 out[i] = RB_SLASH as u8; i = i + 1 115 var j: i64 = 0 116 while name[j] != (0 as u8) { if i < RB_PATH_CAP - 1 { out[i] = name[j] } i = i + 1; j = j + 1 } 117 out[i] = 0 as u8 118 return out 119} 120func rb_dirname(path: *u8, out: *u8) -> i64 { 121 var last: i64 = 0 - 1 122 var i: i64 = 0 123 while path[i] != (0 as u8) { if path[i] == (RB_SLASH as u8) { last = i } i = i + 1 } 124 if last <= 0 { out[0] = RB_SLASH as u8; out[1] = 0 as u8; return 1 } 125 var k: i64 = 0 126 while k < last { if k < RB_PATH_CAP - 1 { out[k] = path[k] } k = k + 1 } 127 out[last] = 0 as u8 128 return last 129} 130// write VERT+TRIS only: the bare mesh the skeleton leg must see, with no artist rig to lean on 131func rb_write_bare(b: *u8, flen: i64, outp: *u8) -> i64 { 132 let w: *i64 = b as *i64 133 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8)) 134 let two: i64 = nxa_find(b, flen, nxa_tag4("TRIS" as *u8)) 135 if vwo < 0 { return 0 - 1 } 136 if two < 0 { return 0 - 1 } 137 let tags: *i64 = sys_mmap(64) as *i64 138 let ptrs: *i64 = sys_mmap(64) as *i64 139 let wls: *i64 = sys_mmap(64) as *i64 140 tags[0] = nxa_tag4("VERT" as *u8); ptrs[0] = ((w as i64) + vwo * RB_WORD_BYTES); wls[0] = 1 + w[vwo] * 3 141 tags[1] = nxa_tag4("TRIS" as *u8); ptrs[1] = ((w as i64) + two * RB_WORD_BYTES); wls[1] = 1 + w[two] * 3 142 return bh_nxa_write(outp, 2, tags, ptrs, wls) 143} 144func rb_jw(w: *i64, swo: i64, j: i64, word: i64) -> i64 { return w[swo + 1 + j * CS_JOINT_WORDS + word] } 145func rb_dist(w1: *i64, s1: i64, j1: i64, w2: *i64, s2: i64, j2: i64) -> i64 { 146 let dx: i64 = rb_jw(w1, s1, j1, RB_W_X) - rb_jw(w2, s2, j2, RB_W_X) 147 let dy: i64 = rb_jw(w1, s1, j1, RB_W_Y) - rb_jw(w2, s2, j2, RB_W_Y) 148 let dz: i64 = rb_jw(w1, s1, j1, RB_W_Z) - rb_jw(w2, s2, j2, RB_W_Z) 149 return vm_isqrt(dx * dx + dy * dy + dz * dz) 150} 151// the artist rig's own mean bone length (joint to parent), in 0.01 mm: the DERIVED coverage bar 152func rb_mean_bone(w: *i64, swo: i64, nj: i64) -> i64 { 153 var sum: i64 = 0 154 var n: i64 = 0 155 var j: i64 = 0 156 while j < nj { 157 let p: i64 = rb_jw(w, swo, j, RB_W_PARENT) 158 if p >= 0 { if p < nj { sum = sum + rb_dist(w, swo, j, w, swo, p); n = n + 1 } } 159 j = j + 1 160 } 161 if n < 1 { return 0 } 162 return sum / n 163} 164func rb_leaves(w: *i64, swo: i64, nj: i64) -> i64 { 165 let isp: *i64 = sys_mmap(nj * 8 + 64) as *i64 166 var j: i64 = 0 167 while j < nj { isp[j] = 0; j = j + 1 } 168 j = 0 169 while j < nj { let p: i64 = rb_jw(w, swo, j, RB_W_PARENT); if p >= 0 { if p < nj { isp[p] = 1 } } j = j + 1 } 170 var n: i64 = 0 171 j = 0 172 while j < nj { if isp[j] == 0 { n = n + 1 } j = j + 1 } 173 return n 174} 175// fork the served player: <in> <pose> <out> 1000, in its own directory 176func rb_play(inp: *u8, pose: *u8, outp: *u8, cap: *u8, olen: *i64) -> i64 { 177 let av: *i64 = sys_mmap(RB_WORD_BYTES * RB_ARGV_SLOTS) as *i64 178 av[0] = RB_PLAY as i64; av[1] = inp as i64; av[2] = pose as i64; av[3] = outp as i64; av[4] = RB_WFULL as i64; av[5] = 0 179 let cwd: *u8 = sys_mmap(RB_PATH_CAP) 180 rb_dirname(RB_PLAY, cwd) 181 return tr_run_capture_cwd(RB_PLAY, av, cap, RB_CAPTURE_CAP, olen, RB_NO_TIMEOUT, cwd) 182} 183// an NXA's VERT/TRIS as an in-memory NXMESH for md_measure (tenths of a mm in; float mm stored). TWIN of the 184// conversion in nx_autorig_pose_gate -- byte-identical on purpose so nx_oo_extract can lift both into one lib. 185func rb_nxmesh(b: *u8, flen: i64) -> *u8 { 186 let w: *i64 = b as *i64 187 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8)) 188 let two: i64 = nxa_find(b, flen, nxa_tag4("TRIS" as *u8)) 189 if vwo < 0 { return 0 as *u8 } 190 if two < 0 { return 0 as *u8 } 191 let nt: i64 = w[two] 192 let m: *u8 = sys_mmap(nm_file_bytes(0, nt) + 64) 193 var i: i64 = 0 194 while i < RB_MAGIC_LEN { m[i] = RB_MAGIC[i]; i = i + 1 } 195 while i < RB_MAGIC_FIELD { m[i] = 0 as u8; i = i + 1 } 196 nm_put_u32(m, NM_OFF_NLAYERS, 0) 197 nm_put_u32(m, NM_OFF_NTRIS, nt) 198 let tbase: i64 = nm_tri_base(m) 199 let xyz9: *i64 = sys_mmap(9 * 8) as *i64 200 var t: i64 = 0 201 while t < nt { 202 var v: i64 = 0 203 while v < 3 { 204 let vi: i64 = w[two + 1 + t * 3 + v] 205 let o: i64 = vwo + 1 + vi * 3 206 xyz9[v * 3] = w[o] / RB_NXA_PER_TENTH 207 xyz9[v * 3 + 1] = w[o + 1] / RB_NXA_PER_TENTH 208 xyz9[v * 3 + 2] = w[o + 2] / RB_NXA_PER_TENTH 209 v = v + 1 210 } 211 nm_put_tri(m, tbase, t, xyz9) 212 t = t + 1 213 } 214 return m 215} 216// per-vertex L1 between two SKIN rows (four influences each), dense over joints; in q12 units, max 2 x 4096 217func rb_skin_l1(wa: *i64, ka: i64, wo: *i64, ko: i64, v: i64, nj: i64, dense: *i64) -> i64 { 218 var j: i64 = 0 219 while j < nj { dense[j] = 0; j = j + 1 } 220 let ra: i64 = ka + 1 + v * RB_SKIN_WORDS 221 let ro: i64 = ko + 1 + v * RB_SKIN_WORDS 222 var k: i64 = 0 223 while k < RB_SKIN_INF { 224 let ja: i64 = wa[ra + k]; let wq: i64 = wa[ra + RB_SKIN_INF + k] 225 if ja >= 0 { if ja < nj { dense[ja] = dense[ja] + wq } } 226 k = k + 1 227 } 228 k = 0 229 while k < RB_SKIN_INF { 230 let jo: i64 = wo[ro + k]; let wq: i64 = wo[ro + RB_SKIN_INF + k] 231 if jo >= 0 { if jo < nj { dense[jo] = dense[jo] - wq } } 232 k = k + 1 233 } 234 var l1: i64 = 0 235 j = 0 236 while j < nj { l1 = l1 + rb_abs(dense[j]); j = j + 1 } 237 return l1 238} 239func rb_top1(w: *i64, ko: i64, v: i64) -> i64 { 240 let r: i64 = ko + 1 + v * RB_SKIN_WORDS 241 var best: i64 = w[r] 242 var bw: i64 = w[r + RB_SKIN_INF] 243 var k: i64 = 1 244 while k < RB_SKIN_INF { if w[r + RB_SKIN_INF + k] > bw { bw = w[r + RB_SKIN_INF + k]; best = w[r + k] } k = k + 1 } 245 return best 246} 247 248// rep slots (shared vocabulary with the gate) 249const RB_R_ART_JOINTS: i64 = 0 250const RB_R_ART_LEAVES: i64 = 1 251const RB_R_OUR_JOINTS: i64 = 2 252const RB_R_OUR_LIMBS: i64 = 3 253const RB_R_BONE_MEAN_UMM: i64 = 4 254const RB_R_COVERED_PERMIL: i64 = 5 255const RB_R_SPURIOUS_PERMIL: i64 = 6 256const RB_R_JOINT_ERR_MEAN_UMM: i64 = 7 257const RB_R_JOINT_ERR_MAX_UMM: i64 = 8 258const RB_R_VERTS: i64 = 9 259const RB_R_W_L1_MEAN_PERMIL: i64 = 10 260const RB_R_TOP1_MATCH_PERMIL: i64 = 11 261const RB_R_POSES: i64 = 12 262const RB_R_PLAY_A_RC: i64 = 13 263const RB_R_PLAY_O_RC: i64 = 14 264const RB_R_MD_MAX_0P1MM: i64 = 15 265const RB_R_MD_P95_0P1MM: i64 = 16 266const RB_R_MD_REFUSED: i64 = 17 267const RB_R_PLAY_A_MAXDISP_UMM: i64 = 18 268const RB_R_PLAY_O_MAXDISP_UMM: i64 = 19 269const RB_R_DEFORM_POSE: i64 = 22 // the pose rb_run auto-selected for the deformation leg, -1 when it abstained 270const RB_R_DEFORM_JOINTS: i64 = 23 // joints that pose moved on the ARTIST play (the non-vacuity witness the gate reads) 271const RB_R_MD_SCALE_DIV: i64 = 24 // the integer divisor rb_md_normalize applied before md_measure; 1 = no-op 272const RB_XYZ9_BYTES: i64 = 4096 // one page holds the nine coordinates nm_put_tri writes 273const RB_BBOX_BYTES: i64 = 4096 // one page holds a three-axis bound 274const RB_COORD_MAX: i64 = 1 << 62 // above any coordinate the ruler can hold: the bound's starting value 275const RB_R_ART_TOP1_DISTINCT: i64 = 20 // how many distinct joints the ARTIST skin uses as a strongest influence 276const RB_R_OUR_TOP1_DISTINCT: i64 = 21 // ...and how many OURS uses: the instrument for the helper-joint hypothesis 277const RB_R_WORDS: i64 = 25 278 279func rb_run(rigged: *u8, workdir: *u8, pose_id: i64, cells: i64, rep: *i64) -> i64 { 280 var k: i64 = 0 281 while k < RB_R_WORDS { rep[k] = 0; k = k + 1 } 282 let lp: *i64 = sys_mmap(16) as *i64 283 let b: *u8 = sys_read_file(rigged, lp) 284 if (b as i64) == 0 { rbw("RIGBENCH-REFUSE cannot read input\n" as *u8); return RB_EXIT_REFUSE } 285 let flen: i64 = lp[0] 286 let w: *i64 = b as *i64 287 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8)) 288 let swo: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8)) 289 let kwo: i64 = nxa_find(b, flen, nxa_tag4("SKIN" as *u8)) 290 let pwo: i64 = nxa_find(b, flen, nxa_tag4("POSE" as *u8)) 291 if vwo < 0 { rbw("RIGBENCH-REFUSE no VERT\n" as *u8); return RB_EXIT_REFUSE } 292 if swo < 0 { rbw("RIGBENCH-REFUSE no artist SKEL: nothing to score against\n" as *u8); return RB_EXIT_REFUSE } 293 if kwo < 0 { rbw("RIGBENCH-REFUSE no artist SKIN: nothing to score against\n" as *u8); return RB_EXIT_REFUSE } 294 let nv: i64 = w[vwo] 295 let nja: i64 = w[swo] 296 rep[RB_R_ART_JOINTS] = nja 297 rep[RB_R_VERTS] = nv 298 rep[RB_R_ART_LEAVES] = rb_leaves(w, swo, nja) 299 if pwo >= 0 { rep[RB_R_POSES] = w[pwo] } 300 // ---- A. skeleton from the bare mesh ---- 301 let p_bare: *u8 = rb_join(workdir, "bare.nxa" as *u8) 302 let p_ours: *u8 = rb_join(workdir, "ourskel.nxa" as *u8) 303 if rb_write_bare(b, flen, p_bare) <= 0 { rbw("RIGBENCH-REFUSE cannot write the bare mesh\n" as *u8); return RB_EXIT_REFUSE } 304 let crep: *i64 = sys_mmap(CR_WORDS * 8) as *i64 305 let rcS: i64 = cs_run_cells(p_bare, p_ours, crep, cells) 306 if rcS != CS_EXIT_OK { rbw("RIGBENCH-REFUSE skeleton stage refused the bare mesh\n" as *u8); return RB_EXIT_REFUSE } 307 rep[RB_R_OUR_LIMBS] = crep[CR_LIMBS] 308 let lo: *i64 = sys_mmap(16) as *i64 309 let bo: *u8 = sys_read_file(p_ours, lo) 310 if (bo as i64) == 0 { rbw("RIGBENCH-REFUSE cannot read our skeleton back\n" as *u8); return RB_EXIT_REFUSE } 311 let wo: *i64 = bo as *i64 312 let swo2: i64 = nxa_find(bo, lo[0], nxa_tag4("SKEL" as *u8)) 313 if swo2 < 0 { rbw("RIGBENCH-REFUSE our output carries no SKEL\n" as *u8); return RB_EXIT_REFUSE } 314 let njo: i64 = wo[swo2] 315 rep[RB_R_OUR_JOINTS] = njo 316 let bone: i64 = rb_mean_bone(w, swo, nja) 317 rep[RB_R_BONE_MEAN_UMM] = bone 318 var covered: i64 = 0 319 var errsum: i64 = 0 320 var errmax: i64 = 0 321 var a: i64 = 0 322 while a < nja { 323 var best: i64 = 0 - 1 324 var o: i64 = 0 325 while o < njo { let d: i64 = rb_dist(w, swo, a, wo, swo2, o); if best < 0 { best = d } else { if d < best { best = d } } o = o + 1 } 326 if best < 0 { best = 0 } 327 if best <= bone { covered = covered + 1 } 328 errsum = errsum + best 329 if best > errmax { errmax = best } 330 a = a + 1 331 } 332 if nja > 0 { rep[RB_R_COVERED_PERMIL] = covered * RB_PERMIL / nja; rep[RB_R_JOINT_ERR_MEAN_UMM] = errsum / nja } 333 rep[RB_R_JOINT_ERR_MAX_UMM] = errmax 334 var spurious: i64 = 0 335 var o2: i64 = 0 336 while o2 < njo { 337 var best: i64 = 0 - 1 338 a = 0 339 while a < nja { let d: i64 = rb_dist(wo, swo2, o2, w, swo, a); if best < 0 { best = d } else { if d < best { best = d } } a = a + 1 } 340 if best > bone { spurious = spurious + 1 } 341 o2 = o2 + 1 342 } 343 if njo > 0 { rep[RB_R_SPURIOUS_PERMIL] = spurious * RB_PERMIL / njo } 344 // ---- B. weights on the artist skeleton ---- 345 let p_skin: *u8 = rb_join(workdir, "ourskin.nxa" as *u8) 346 let brep: *i64 = sys_mmap(BR_WORDS * 8) as *i64 347 let rcB: i64 = bh_run(rigged, p_skin, BH_MODE_GEODESIC, cells, brep) 348 if rcB != BH_EXIT_OK { rbw("RIGBENCH-REFUSE weight stage refused the artist rig\n" as *u8); return RB_EXIT_REFUSE } 349 let lk: *i64 = sys_mmap(16) as *i64 350 let bk: *u8 = sys_read_file(p_skin, lk) 351 if (bk as i64) == 0 { rbw("RIGBENCH-REFUSE cannot read our skin back\n" as *u8); return RB_EXIT_REFUSE } 352 let wk: *i64 = bk as *i64 353 let kwo2: i64 = nxa_find(bk, lk[0], nxa_tag4("SKIN" as *u8)) 354 if kwo2 < 0 { rbw("RIGBENCH-REFUSE our output carries no SKIN\n" as *u8); return RB_EXIT_REFUSE } 355 let dense: *i64 = sys_mmap(nja * 8 + 64) as *i64 356 let seen_a: *i64 = sys_mmap(nja * 8 + 64) as *i64 357 let seen_o: *i64 = sys_mmap(nja * 8 + 64) as *i64 358 var sj: i64 = 0 359 while sj < nja { seen_a[sj] = 0; seen_o[sj] = 0; sj = sj + 1 } 360 var l1sum: i64 = 0 361 var top1: i64 = 0 362 var v: i64 = 0 363 while v < nv { 364 l1sum = l1sum + rb_skin_l1(w, kwo, wk, kwo2, v, nja, dense) 365 let ta: i64 = rb_top1(w, kwo, v) 366 let to: i64 = rb_top1(wk, kwo2, v) 367 if ta == to { top1 = top1 + 1 } 368 if ta >= 0 { if ta < nja { seen_a[ta] = 1 } } 369 if to >= 0 { if to < nja { seen_o[to] = 1 } } 370 v = v + 1 371 } 372 // the helper-joint instrument: a rig whose artist skin drives 30 joints while we drive 90 disagrees on strongest joint 373 // by construction (twist, IK and control joints carry no artist weight but sit inside the volume and get ours) 374 var da: i64 = 0 375 var dob: i64 = 0 376 sj = 0 377 while sj < nja { da = da + seen_a[sj]; dob = dob + seen_o[sj]; sj = sj + 1 } 378 rep[RB_R_ART_TOP1_DISTINCT] = da 379 rep[RB_R_OUR_TOP1_DISTINCT] = dob 380 if nv > 0 { rep[RB_R_W_L1_MEAN_PERMIL] = (l1sum / nv) * RB_PERMIL / RB_Q12; rep[RB_R_TOP1_MATCH_PERMIL] = top1 * RB_PERMIL / nv } 381 // ---- C. the artist pose, artist weights vs ours, judged by md_measure ---- 382 if pwo < 0 { rbw("RIGBENCH-ABSTAIN no POSE library: the deformation leg cannot be measured\n" as *u8); return RB_EXIT_ABSTAIN } 383 let p_posedA: *u8 = rb_join(workdir, "posed_artist.nxa" as *u8) 384 let p_posedO: *u8 = rb_join(workdir, "posed_ours.nxa" as *u8) 385 let capA: *u8 = sys_mmap(RB_CAPTURE_CAP) 386 let lenA: *i64 = sys_mmap(16) as *i64 387 let capO: *u8 = sys_mmap(RB_CAPTURE_CAP) 388 let lenO: *i64 = sys_mmap(16) as *i64 389 let pose_s: *u8 = sys_mmap(32) 390 // AUTO-SELECT A POSE THAT MOVES JOINTS. A pose_id that replays bind (identity, or an id absent from the 391 // library) moves zero joints, and md then reads a false ~0 mm -- the leg is SILENTLY VACUOUS (measured 392 // 2026-09-06: pose_id=1 on ref9d, whose only pose is id 0, read md 0.2 mm). Try the requested id first, 393 // then scan 0..poses-1, and take the first pose whose ARTIST play moves joints. If NONE moves, ABSTAIN md=-1. 394 var npose: i64 = rep[RB_R_POSES] 395 if npose < 1 { npose = 1 } 396 var chosen: i64 = 0 - 1 397 var moved: i64 = 0 398 var idx: i64 = 0 - 1 399 while idx < npose { 400 var pid: i64 = pose_id 401 if idx >= 0 { pid = idx } 402 var skip: i64 = 0 403 if idx >= 0 { if idx == pose_id { skip = 1 } } 404 if pid < 0 { skip = 1 } 405 if skip == 0 { 406 rb_itoa(pose_s, pid) 407 rep[RB_R_PLAY_A_RC] = rb_play(rigged, pose_s, p_posedA, capA, lenA) 408 let jm: i64 = rb_after(capA, lenA[0], "joints_posed=" as *u8) 409 if jm > 0 { chosen = pid; moved = jm; idx = npose } 410 } 411 idx = idx + 1 412 } 413 rb_kv(" deform_pose=" as *u8, chosen) 414 rb_kv(" deform_pose_joints_moved=" as *u8, moved) 415 rep[RB_R_DEFORM_POSE] = chosen 416 rep[RB_R_DEFORM_JOINTS] = moved 417 if chosen < 0 { 418 rep[RB_R_PLAY_O_RC] = 0 - 1 419 rep[RB_R_MD_REFUSED] = 0 - 1 420 rep[RB_R_MD_MAX_0P1MM] = 0 - 1 421 rep[RB_R_MD_P95_0P1MM] = 0 - 1 422 rbw("RIGBENCH deformation leg ABSTAINS: every pose in the library is static (moves zero joints)\n" as *u8) 423 return RB_EXIT_OK 424 } 425 rb_itoa(pose_s, chosen) 426 rep[RB_R_PLAY_O_RC] = rb_play(p_skin, pose_s, p_posedO, capO, lenO) 427 rbw(" " as *u8); sys_write(1, capA, lenA[0]) 428 rbw(" " as *u8); sys_write(1, capO, lenO[0]) 429 if rep[RB_R_PLAY_A_RC] != 0 { rbw("RIGBENCH-REFUSE the served player refused the artist rig\n" as *u8); return RB_EXIT_REFUSE } 430 if rep[RB_R_PLAY_O_RC] != 0 { rbw("RIGBENCH-REFUSE the served player refused our rig\n" as *u8); return RB_EXIT_REFUSE } 431 let la: *i64 = sys_mmap(16) as *i64 432 let ba: *u8 = sys_read_file(p_posedA, la) 433 let lo2: *i64 = sys_mmap(16) as *i64 434 let bo2: *u8 = sys_read_file(p_posedO, lo2) 435 if (ba as i64) == 0 { rbw("RIGBENCH-REFUSE cannot read the artist-posed skin\n" as *u8); return RB_EXIT_REFUSE } 436 if (bo2 as i64) == 0 { rbw("RIGBENCH-REFUSE cannot read our posed skin\n" as *u8); return RB_EXIT_REFUSE } 437 let mA: *u8 = rb_nxmesh(ba, la[0]) 438 let mO: *u8 = rb_nxmesh(bo2, lo2[0]) 439 let res: *i64 = md_res() 440 let kdiv: i64 = rb_md_normalize(mA, mO) 441 rep[RB_R_MD_SCALE_DIV] = kdiv 442 rb_kv(" md_scale_div=" as *u8, kdiv) 443 rep[RB_R_MD_REFUSED] = md_measure(mO, mA, res) 444 rep[RB_R_MD_MAX_0P1MM] = res[MD_R_MAX] * kdiv 445 rep[RB_R_MD_P95_0P1MM] = res[MD_R_P95] * kdiv 446 return RB_EXIT_OK 447} 448// NORMALIZE FOR MEASUREMENT (dcc.plan 1788710510, 2026-09-06). md_grid_build REFUSES a mesh whose bounding-box 449// extent exceeds MD_EXTENT_MAX (2.5 m in tenths of a mm) because its barycentric products would overflow i64; 450// a donor authored past that (dark_witch) is not a defect of the ruler, so the referee scales BOTH posed skins 451// by ONE integer divisor into the bound, measures, and multiplies the deviation back. The divisor is DERIVED 452// from the extent, never picked; 1 is a byte-identical no-op, which is what keeps ref9d's p95 reproducing 453// exactly. Precision cost stated: every coordinate is integer-divided, so the returned deviation carries up 454// to kdiv tenths of a mm of quantisation, and md_scale_div= is printed beside every number it scaled. 455func rb_md_extent(m: *u8, lo: *i64, hi: *i64) -> i64 { 456 let tb: i64 = nm_tri_base(m) 457 let nt: i64 = nm_ntris(m) 458 var t: i64 = 0 459 while t < nt { 460 var v: i64 = 0 461 while v < 3 { 462 var a: i64 = 0 463 while a < 3 { 464 let c: i64 = nm_coord(m, tb, t, v, a) 465 if c < lo[a] { lo[a] = c } 466 if c > hi[a] { hi[a] = c } 467 a = a + 1 468 } 469 v = v + 1 470 } 471 t = t + 1 472 } 473 return nt 474} 475func rb_md_scale(m: *u8, kdiv: i64) -> i64 { 476 let tb: i64 = nm_tri_base(m) 477 let nt: i64 = nm_ntris(m) 478 let xyz9: *i64 = sys_mmap(RB_XYZ9_BYTES) as *i64 479 var t: i64 = 0 480 while t < nt { 481 var v: i64 = 0 482 while v < 3 { 483 var a: i64 = 0 484 while a < 3 { xyz9[v * 3 + a] = nm_coord(m, tb, t, v, a) / kdiv; a = a + 1 } 485 v = v + 1 486 } 487 nm_put_tri(m, tb, t, xyz9) 488 t = t + 1 489 } 490 sys_munmap(xyz9 as *u8, RB_XYZ9_BYTES) 491 return nt 492} 493func rb_md_normalize(mA: *u8, mO: *u8) -> i64 { 494 let lo: *i64 = sys_mmap(RB_BBOX_BYTES) as *i64 495 let hi: *i64 = sys_mmap(RB_BBOX_BYTES) as *i64 496 var a: i64 = 0 497 while a < 3 { lo[a] = RB_COORD_MAX; hi[a] = 0 - RB_COORD_MAX; a = a + 1 } 498 rb_md_extent(mA, lo, hi) 499 rb_md_extent(mO, lo, hi) 500 var ext: i64 = 0 501 a = 0 502 while a < 3 { let r: i64 = hi[a] - lo[a]; if r > ext { ext = r } a = a + 1 } 503 sys_munmap(lo as *u8, RB_BBOX_BYTES) 504 sys_munmap(hi as *u8, RB_BBOX_BYTES) 505 if ext <= MD_EXTENT_MAX { return 1 } 506 let kdiv: i64 = (ext + MD_EXTENT_MAX - 1) / MD_EXTENT_MAX 507 rb_md_scale(mA, kdiv) 508 rb_md_scale(mO, kdiv) 509 return kdiv 510} 511func rb_report(rep: *i64) -> i64 { 512 rb_kv("RIGBENCH artist_joints=" as *u8, rep[RB_R_ART_JOINTS]) 513 rb_kv(" artist_leaves=" as *u8, rep[RB_R_ART_LEAVES]) 514 rb_kv(" our_joints=" as *u8, rep[RB_R_OUR_JOINTS]) 515 rb_kv(" our_limbs=" as *u8, rep[RB_R_OUR_LIMBS]) 516 rb_kv(" artist_mean_bone_umm=" as *u8, rep[RB_R_BONE_MEAN_UMM]) 517 rb_kv(" artist_joints_covered_permil=" as *u8, rep[RB_R_COVERED_PERMIL]) 518 rb_kv(" our_joints_spurious_permil=" as *u8, rep[RB_R_SPURIOUS_PERMIL]) 519 rb_kv(" joint_err_mean_umm=" as *u8, rep[RB_R_JOINT_ERR_MEAN_UMM]) 520 rb_kv(" joint_err_max_umm=" as *u8, rep[RB_R_JOINT_ERR_MAX_UMM]) 521 rb_kv(" verts=" as *u8, rep[RB_R_VERTS]) 522 rb_kv(" weight_l1_mean_permil=" as *u8, rep[RB_R_W_L1_MEAN_PERMIL]) 523 rb_kv(" top1_joint_match_permil=" as *u8, rep[RB_R_TOP1_MATCH_PERMIL]) 524 rb_kv(" artist_top1_distinct_joints=" as *u8, rep[RB_R_ART_TOP1_DISTINCT]) 525 rb_kv(" our_top1_distinct_joints=" as *u8, rep[RB_R_OUR_TOP1_DISTINCT]) 526 rb_kv(" poses_in_library=" as *u8, rep[RB_R_POSES]) 527 rb_kv(" play_artist_rc=" as *u8, rep[RB_R_PLAY_A_RC]) 528 rb_kv(" play_ours_rc=" as *u8, rep[RB_R_PLAY_O_RC]) 529 rb_kv(" md_refused=" as *u8, rep[RB_R_MD_REFUSED]) 530 rb_kv(" md_ours_vs_artist_max_0p1mm=" as *u8, rep[RB_R_MD_MAX_0P1MM]) 531 rb_kv(" md_ours_vs_artist_p95_0p1mm=" as *u8, rep[RB_R_MD_P95_0P1MM]) 532 rb_kv(" md_scale_div=" as *u8, rep[RB_R_MD_SCALE_DIV]) 533 return 0 534} 535// main() lives in nx_rigbench.nx (the program). This file is the LIB, so nx_rigbench_gate composes rb_run in-process.