code wiki / (root) / nx_bvh_lib.nx

nx_bvh_lib.nx source

↩ module page · 371 lines · 13626 B

1// nx_bvh_lib.nx -- SHARED BVH (BioVision Hierarchy) PARSING SUBSTRATE. 2// 3// Extracted 2026-08-07 when the second consumer appeared (nx_bvhfk, forward kinematics) rather than 4// copy-pasting the tokenizer into it -- Rule 15, and Rule 9: this library PARSES, it does not 5// interpret. Kinematics, census and rendering are separate organs on top of it. 6// 7// NAMING: nx_bvh.nx is a BOUNDING VOLUME HIERARCHY for the slicer. This file and its consumers are 8// the MOTION-CAPTURE bvh. The collision already cost one false capability reading. 9// 10// *THE FILE CARRIES ITS OWN ANSWER KEY: a BVH declares a joint tree whose CHANNELS sum to an exact 11// per-frame width, then prints rows of that width. b_motion PROVES that identity before returning, 12// so a hierarchy parse that is wrong by a single channel REFUSES instead of silently mis-slicing 13// every frame. Verified against two independent real files (19 joints/60 chan/455 frames = 27,300 14// values; 55 joints/168 chan/69 frames = 11,592 values), both exact. 15// 16// UNITS ARE DECLARED, NEVER IMPLIED: 17// OFFSET + positional channels = file units x B_SCALE (1000). BVH does not state its unit in the 18// format; the convention is centimetres, so we do NOT claim mm. 19// rotational channels = degrees x B_SCALE (1000) = millidegrees. 20// frame time = MICROSECONDS (x B_TSCALE); fps is DERIVED, never assumed 30. 21// 22// license_tier: ORIGINAL No hw writes (Rule 26). 23import "nx_syscalls.nx" 24 25const B_SCALE: i64 = 1000 26const B_TSCALE: i64 = 1000000 27const B_MAX_JOINTS: i64 = 256 28const B_MAX_CHAN: i64 = 1024 29const B_NAMEW: i64 = 32 30const B_TOKW: i64 = 64 31const B_TEN: i64 = 10 32 33const B_CH_XPOS: i64 = 0 34const B_CH_YPOS: i64 = 1 35const B_CH_ZPOS: i64 = 2 36const B_CH_XROT: i64 = 3 37const B_CH_YROT: i64 = 4 38const B_CH_ZROT: i64 = 5 39const B_CH_BAD: i64 = 0 - 1 40 41const B_H_NJOINT: i64 = 0 42const B_H_NCHAN: i64 = 1 43const B_H_NFRAME: i64 = 2 44const B_H_FTIME: i64 = 3 45const B_H_MOTOFF: i64 = 4 46const B_H_WORDS: i64 = 8 47 48const B_R_NOHIER: i64 = 0 - 1 49const B_R_NOMOTION:i64 = 0 - 2 50const B_R_CAP: i64 = 0 - 3 51const B_R_WIDTH: i64 = 0 - 4 52const B_R_FRAMES: i64 = 0 - 5 53const B_R_CHAN: i64 = 0 - 6 54const B_R_FRAMEIDX:i64 = 0 - 7 55 56func b_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i } 57func b_puts(s: *u8) -> i64 { sys_write(1, s, b_slen(s)); return 0 } 58 59func b_num(v: i64) -> i64 { 60 let t: *u8 = sys_mmap(32) 61 var m: i64 = v 62 var w: i64 = 0 63 if m < 0 { t[0] = 45 as u8; sys_write(1, t, 1); m = 0 - m } 64 if m == 0 { t[0] = 48 as u8; sys_write(1, t, 1); return 0 } 65 let d: *u8 = sys_mmap(32) 66 var k: i64 = 0 67 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 68 var j: i64 = 0 69 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 } 70 sys_write(1, t, w) 71 return 0 72} 73 74func b_is_space(c: i64) -> i64 { 75 if c == 32 { return 1 } 76 if c == 9 { return 1 } 77 if c == 10 { return 1 } 78 if c == 13 { return 1 } 79 return 0 80} 81 82// Next whitespace-delimited token; braces are their own tokens even when unspaced. 83// *TERMINATES WITH A FLAG, NEVER THE CURSOR. The earlier draft ended these loops by writing 84// p = len + len and subtracting it back, which resets p to 0 and spins forever -- the banked 85// cursor law, and it hung the organ on its first run. 86func b_token(buf: *u8, len: i64, pos: *i64, tok: *u8) -> i64 { 87 var p: i64 = pos[0] 88 var skip: i64 = 0 89 while skip == 0 { 90 if p >= len { skip = 1 } 91 else { 92 if b_is_space((buf[p] & 0xff) as i64) == 1 { p = p + 1 } else { skip = 1 } 93 } 94 } 95 if p >= len { pos[0] = p; return 0 } 96 var n: i64 = 0 97 let c0: i64 = (buf[p] & 0xff) as i64 98 if c0 == 123 { tok[0] = 123 as u8; tok[1] = 0 as u8; pos[0] = p + 1; return 1 } 99 if c0 == 125 { tok[0] = 125 as u8; tok[1] = 0 as u8; pos[0] = p + 1; return 1 } 100 var stop: i64 = 0 101 while stop == 0 { 102 if p >= len { stop = 1 } 103 else { 104 let c: i64 = (buf[p] & 0xff) as i64 105 if b_is_space(c) == 1 { stop = 1 } 106 else { 107 if c == 123 { stop = 1 } 108 else { 109 if c == 125 { stop = 1 } 110 else { 111 if n < B_TOKW - 1 { tok[n] = buf[p]; n = n + 1 } 112 p = p + 1 113 } 114 } 115 } 116 } 117 } 118 tok[n] = 0 as u8 119 pos[0] = p 120 return n 121} 122 123func b_tok_is(tok: *u8, n: i64, s: *u8) -> i64 { 124 if b_slen(s) != n { return 0 } 125 var i: i64 = 0 126 while i < n { if tok[i] != s[i] { return 0 } i = i + 1 } 127 return 1 128} 129 130// decimal -> fixed point x scale. Sign, fraction, and scientific exponent. 131func b_parse_fx(tok: *u8, n: i64, scale: i64) -> i64 { 132 var i: i64 = 0 133 var neg: i64 = 0 134 if n > 0 { 135 if tok[0] == (45 as u8) { neg = 1; i = 1 } 136 else { if tok[0] == (43 as u8) { i = 1 } } 137 } 138 var ip: i64 = 0 139 var s1: i64 = 0 140 while s1 == 0 { 141 if i >= n { s1 = 1 } 142 else { 143 let c: i64 = (tok[i] & 0xff) as i64 144 if c >= 48 { if c <= 57 { ip = ip * B_TEN + (c - 48); i = i + 1 } else { s1 = 1 } } 145 else { s1 = 1 } 146 } 147 } 148 var v: i64 = ip * scale 149 if i < n { 150 if tok[i] == (46 as u8) { 151 i = i + 1 152 var div: i64 = 1 153 var fp: i64 = 0 154 var s2: i64 = 0 155 while s2 == 0 { 156 if i >= n { s2 = 1 } 157 else { 158 let c2: i64 = (tok[i] & 0xff) as i64 159 if c2 >= 48 { 160 if c2 <= 57 { 161 if div < scale { fp = fp * B_TEN + (c2 - 48); div = div * B_TEN } 162 i = i + 1 163 } else { s2 = 1 } 164 } else { s2 = 1 } 165 } 166 } 167 if div > 1 { v = v + fp * scale / div } 168 } 169 } 170 if i < n { 171 var ec: i64 = (tok[i] & 0xff) as i64 172 if ec == 101 { ec = 69 } 173 if ec == 69 { 174 i = i + 1 175 var eneg: i64 = 0 176 if i < n { 177 if tok[i] == (45 as u8) { eneg = 1; i = i + 1 } 178 else { if tok[i] == (43 as u8) { i = i + 1 } } 179 } 180 var ex: i64 = 0 181 var s3: i64 = 0 182 while s3 == 0 { 183 if i >= n { s3 = 1 } 184 else { 185 let c3: i64 = (tok[i] & 0xff) as i64 186 if c3 >= 48 { if c3 <= 57 { ex = ex * B_TEN + (c3 - 48); i = i + 1 } else { s3 = 1 } } 187 else { s3 = 1 } 188 } 189 } 190 var k: i64 = 0 191 while k < ex { 192 if eneg == 1 { v = v / B_TEN } else { v = v * B_TEN } 193 k = k + 1 194 } 195 } 196 } 197 if neg == 1 { return 0 - v } 198 return v 199} 200 201func b_chan_code(tok: *u8, n: i64) -> i64 { 202 if b_tok_is(tok, n, "Xposition" as *u8) == 1 { return B_CH_XPOS } 203 if b_tok_is(tok, n, "Yposition" as *u8) == 1 { return B_CH_YPOS } 204 if b_tok_is(tok, n, "Zposition" as *u8) == 1 { return B_CH_ZPOS } 205 if b_tok_is(tok, n, "Xrotation" as *u8) == 1 { return B_CH_XROT } 206 if b_tok_is(tok, n, "Yrotation" as *u8) == 1 { return B_CH_YROT } 207 if b_tok_is(tok, n, "Zrotation" as *u8) == 1 { return B_CH_ZROT } 208 return B_CH_BAD 209} 210 211func b_hierarchy(buf: *u8, len: i64, pos: *i64, H: *i64, 212 names: *u8, parent: *i64, nchan: *i64, chan0: *i64, 213 off: *i64, chtype: *i64) -> i64 { 214 let tok: *u8 = sys_mmap(B_TOKW) 215 var nj: i64 = 0 216 var nc: i64 = 0 217 var cur: i64 = 0 - 1 218 var in_end: i64 = 0 219 var saw_hier: i64 = 0 220 221 var n: i64 = b_token(buf, len, pos, tok) 222 while n > 0 { 223 if b_tok_is(tok, n, "HIERARCHY" as *u8) == 1 { saw_hier = 1 } 224 else { if b_tok_is(tok, n, "MOTION" as *u8) == 1 { 225 if saw_hier == 0 { return B_R_NOHIER } 226 H[B_H_NJOINT] = nj 227 H[B_H_NCHAN] = nc 228 return 0 229 } 230 else { if b_tok_is(tok, n, "ROOT" as *u8) == 1 { 231 if nj >= B_MAX_JOINTS { return B_R_CAP } 232 n = b_token(buf, len, pos, tok) 233 var k: i64 = 0 234 while k < n { if k < B_NAMEW - 1 { names[nj * B_NAMEW + k] = tok[k] } k = k + 1 } 235 names[nj * B_NAMEW + B_NAMEW - 1] = 0 as u8 236 if n < B_NAMEW { names[nj * B_NAMEW + n] = 0 as u8 } 237 parent[nj] = 0 - 1 238 nchan[nj] = 0 239 cur = nj 240 nj = nj + 1 241 in_end = 0 242 } 243 else { if b_tok_is(tok, n, "JOINT" as *u8) == 1 { 244 if nj >= B_MAX_JOINTS { return B_R_CAP } 245 n = b_token(buf, len, pos, tok) 246 var k2: i64 = 0 247 while k2 < n { if k2 < B_NAMEW - 1 { names[nj * B_NAMEW + k2] = tok[k2] } k2 = k2 + 1 } 248 names[nj * B_NAMEW + B_NAMEW - 1] = 0 as u8 249 if n < B_NAMEW { names[nj * B_NAMEW + n] = 0 as u8 } 250 parent[nj] = cur 251 nchan[nj] = 0 252 cur = nj 253 nj = nj + 1 254 in_end = 0 255 } 256 else { if b_tok_is(tok, n, "End" as *u8) == 1 { 257 n = b_token(buf, len, pos, tok) 258 in_end = 1 259 } 260 else { if b_tok_is(tok, n, "OFFSET" as *u8) == 1 { 261 let t1: i64 = b_token(buf, len, pos, tok); let ox: i64 = b_parse_fx(tok, t1, B_SCALE) 262 let t2: i64 = b_token(buf, len, pos, tok); let oy: i64 = b_parse_fx(tok, t2, B_SCALE) 263 let t3: i64 = b_token(buf, len, pos, tok); let oz: i64 = b_parse_fx(tok, t3, B_SCALE) 264 if in_end == 0 { 265 if cur >= 0 { off[cur * 3] = ox; off[cur * 3 + 1] = oy; off[cur * 3 + 2] = oz } 266 } 267 } 268 else { if b_tok_is(tok, n, "CHANNELS" as *u8) == 1 { 269 let tn: i64 = b_token(buf, len, pos, tok) 270 let cn: i64 = b_parse_fx(tok, tn, 1) 271 if cn < 0 { return B_R_CHAN } 272 if nc + cn > B_MAX_CHAN { return B_R_CAP } 273 if cur < 0 { return B_R_CHAN } 274 chan0[cur] = nc 275 nchan[cur] = cn 276 var c: i64 = 0 277 while c < cn { 278 let tt: i64 = b_token(buf, len, pos, tok) 279 let code: i64 = b_chan_code(tok, tt) 280 if code == B_CH_BAD { return B_R_CHAN } 281 chtype[nc] = code 282 nc = nc + 1 283 c = c + 1 284 } 285 } 286 else { if b_tok_is(tok, n, "}" as *u8) == 1 { 287 if in_end == 1 { in_end = 0 } 288 else { if cur >= 0 { cur = parent[cur] } } 289 } } } } } } } } 290 n = b_token(buf, len, pos, tok) 291 } 292 return B_R_NOMOTION 293} 294 295func b_motion(buf: *u8, len: i64, pos: *i64, H: *i64) -> i64 { 296 let tok: *u8 = sys_mmap(B_TOKW) 297 var n: i64 = b_token(buf, len, pos, tok) 298 if b_tok_is(tok, n, "Frames:" as *u8) == 0 { 299 if b_tok_is(tok, n, "Frames" as *u8) == 0 { return B_R_NOMOTION } 300 n = b_token(buf, len, pos, tok) 301 if b_tok_is(tok, n, ":" as *u8) == 0 { return B_R_NOMOTION } 302 } 303 n = b_token(buf, len, pos, tok) 304 let nf: i64 = b_parse_fx(tok, n, 1) 305 if nf < 0 { return B_R_FRAMES } 306 H[B_H_NFRAME] = nf 307 308 n = b_token(buf, len, pos, tok) 309 if b_tok_is(tok, n, "Frame" as *u8) == 1 { 310 n = b_token(buf, len, pos, tok) 311 if b_tok_is(tok, n, "Time:" as *u8) == 0 { 312 if b_tok_is(tok, n, "Time" as *u8) == 1 { n = b_token(buf, len, pos, tok) } 313 } 314 n = b_token(buf, len, pos, tok) 315 } 316 H[B_H_FTIME] = b_parse_fx(tok, n, B_TSCALE) 317 H[B_H_MOTOFF] = pos[0] 318 319 var count: i64 = 0 320 let pp: *i64 = sys_mmap(16) as *i64 321 pp[0] = pos[0] 322 var t: i64 = b_token(buf, len, pp, tok) 323 while t > 0 { count = count + 1; t = b_token(buf, len, pp, tok) } 324 if count != H[B_H_NFRAME] * H[B_H_NCHAN] { return B_R_WIDTH } 325 return 0 326} 327 328func b_refuse(code: i64) -> i64 { 329 b_puts("BVH REFUSED code=" as *u8); b_num(0 - code) 330 if code == B_R_NOHIER { b_puts(" no-HIERARCHY" as *u8) } 331 if code == B_R_NOMOTION { b_puts(" no-MOTION" as *u8) } 332 if code == B_R_CAP { b_puts(" joint-or-channel-cap" as *u8) } 333 if code == B_R_WIDTH { b_puts(" motion-width-mismatch" as *u8) } 334 if code == B_R_FRAMES { b_puts(" bad-frame-count" as *u8) } 335 if code == B_R_CHAN { b_puts(" bad-channel-spec" as *u8) } 336 if code == B_R_FRAMEIDX { b_puts(" frame-out-of-range" as *u8) } 337 b_puts("\n" as *u8) 338 return 4 339} 340 341func b_parse(buf: *u8, len: i64, H: *i64, names: *u8, parent: *i64, 342 nchan: *i64, chan0: *i64, off: *i64, chtype: *i64) -> i64 { 343 let pos: *i64 = sys_mmap(16) as *i64 344 pos[0] = 0 345 let r: i64 = b_hierarchy(buf, len, pos, H, names, parent, nchan, chan0, off, chtype) 346 if r < 0 { return r } 347 return b_motion(buf, len, pos, H) 348} 349 350// Read one frame's channel values (x B_SCALE) into out[0..nchan). Fail-closed on a short block. 351func b_frame_values(buf: *u8, len: i64, H: *i64, frame: i64, out: *i64) -> i64 { 352 if frame < 0 { return B_R_FRAMEIDX } 353 if frame >= H[B_H_NFRAME] { return B_R_FRAMEIDX } 354 let tok: *u8 = sys_mmap(B_TOKW) 355 let pos: *i64 = sys_mmap(16) as *i64 356 pos[0] = H[B_H_MOTOFF] 357 let skip: i64 = frame * H[B_H_NCHAN] 358 var i: i64 = 0 359 while i < skip { 360 if b_token(buf, len, pos, tok) == 0 { return B_R_WIDTH } 361 i = i + 1 362 } 363 var c: i64 = 0 364 while c < H[B_H_NCHAN] { 365 let t: i64 = b_token(buf, len, pos, tok) 366 if t == 0 { return B_R_WIDTH } 367 out[c] = b_parse_fx(tok, t, B_SCALE) 368 c = c + 1 369 } 370 return 0 371}