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}