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.