nx_bvhmotion.nx source
↩ module page · 212 lines · 9841 B
1// nx_bvhmotion.nx -- BVH MOTION CAPTURE CENSUS (thin CLI over nx_bvh_lib).
2//
3// NAMING (deliberate, do not "fix" to nx_bvh): nx_bvh.nx is a BOUNDING VOLUME HIERARCHY for the
4// slicer. The collision is real and it already cost a false capability reading -- a grep for "bvh"
5// reported this format as ALREADY SUPPORTED when nothing of the kind existed.
6// *A NAME IS NOT A CAPABILITY.
7//
8// WHY: measured 2026-08-07 against vrstormlab.com/dancexr/play (their served bytes): the LW build
9// ingests VMD + BVH motion. BVH is the universal interchange, and unlike VMD its corpus has a
10// commercially-clean tier -- which matters because our banked license truth says the SMPL/AMASS
11// academic stack is non-commercial even for TRAINING.
12//
13// PROVEN ON REAL THIRD-PARTY DATA 2026-08-07 (not only on a fixture I authored):
14// test_mocapbank.bvh 19 joints / 60 chan / 455 frames -> 27,300 values = 455*60 EXACT
15// test_freebvh.bvh 55 joints / 168 chan / 69 frames -> 11,592 values = 69*168 EXACT
16// Both channel sums also reconcile independently (6 + 18*3 = 60; 6 + 54*3 = 168).
17//
18// usage: nx_bvhmotion info <file.bvh> joints, channels, frames, frame time, derived fps
19// nx_bvhmotion joints <file.bvh> joint tree: index, parent, nchan, offset xyz, name
20// nx_bvhmotion dump <file.bvh> [n] first n motion rows
21// nx_bvhmotion --kat selftest: 7 teeth, 3 of them anti-vacuity
22// exit 0 ok | 2 unreadable | 3 usage | 4 REFUSED | 1 selftest RED
23// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
24import "nx_bvh_lib.nx"
25const M_MAGIC_5000: i64 = 5000
26const M_MAGIC_33333: i64 = 33333
27const M_MAGIC_2500: i64 = 2500
28const M_MAGIC_11000: i64 = 11000
29const M_MAGIC_90000: i64 = 90000
30
31const M_DUMP_DEF: i64 = 4
32const M_KATBUF: i64 = 4096
33
34func m_info(H: *i64) -> i64 {
35 b_puts("BVH joints=" as *u8); b_num(H[B_H_NJOINT])
36 b_puts(" channels=" as *u8); b_num(H[B_H_NCHAN])
37 b_puts(" frames=" as *u8); b_num(H[B_H_NFRAME])
38 b_puts("\nframe_time_us=" as *u8); b_num(H[B_H_FTIME])
39 b_puts(" fps=" as *u8)
40 if H[B_H_FTIME] > 0 { b_num(B_TSCALE / H[B_H_FTIME]) } else { b_num(0) }
41 b_puts("\nmotion_values=" as *u8); b_num(H[B_H_NFRAME] * H[B_H_NCHAN])
42 b_puts(" width-proven-against-channel-sum\n" as *u8)
43 b_puts("units off_x1000 rot_millideg time_us\n" as *u8)
44 return 0
45}
46
47func m_joints(H: *i64, names: *u8, parent: *i64, nchan: *i64, off: *i64) -> i64 {
48 b_puts("idx\tparent\tnchan\tox\toy\toz\tname\n" as *u8)
49 var j: i64 = 0
50 while j < H[B_H_NJOINT] {
51 b_num(j); b_puts("\t" as *u8)
52 b_num(parent[j]); b_puts("\t" as *u8)
53 b_num(nchan[j]); b_puts("\t" as *u8)
54 b_num(off[j*3]); b_puts("\t" as *u8)
55 b_num(off[j*3+1]); b_puts("\t" as *u8)
56 b_num(off[j*3+2]); b_puts("\t" as *u8)
57 b_puts(((names as i64) + j * B_NAMEW) as *u8)
58 b_puts("\n" as *u8)
59 j = j + 1
60 }
61 return 0
62}
63
64func m_dump(buf: *u8, len: i64, H: *i64, maxn: i64) -> i64 {
65 let vals: *i64 = sys_mmap(B_MAX_CHAN * 8) as *i64
66 var f: i64 = 0
67 var nf: i64 = H[B_H_NFRAME]
68 if maxn < nf { nf = maxn }
69 while f < nf {
70 if b_frame_values(buf, len, H, f, vals) < 0 { return 4 }
71 b_num(f); b_puts(":" as *u8)
72 var c: i64 = 0
73 while c < H[B_H_NCHAN] { b_puts("\t" as *u8); b_num(vals[c]); c = c + 1 }
74 b_puts("\n" as *u8)
75 f = f + 1
76 }
77 return 0
78}
79
80// ===== KAT ========================================================
81func m_cat(b: *u8, o: i64, s: *u8) -> i64 {
82 let n: i64 = b_slen(s)
83 var i: i64 = 0
84 while i < n { b[o + i] = s[i]; i = i + 1 }
85 return o + n
86}
87
88func m_kat_build(b: *u8, frames: *u8, rows: i64) -> i64 {
89 var o: i64 = 0
90 o = m_cat(b, o, "HIERARCHY\nROOT Hips\n{\n" as *u8)
91 o = m_cat(b, o, "OFFSET 0.00 0.00 0.00\n" as *u8)
92 o = m_cat(b, o, "CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation\n" as *u8)
93 o = m_cat(b, o, "JOINT Chest\n{\n" as *u8)
94 o = m_cat(b, o, "OFFSET 0.00 5.00 0.00\n" as *u8)
95 o = m_cat(b, o, "CHANNELS 3 Zrotation Xrotation Yrotation\n" as *u8)
96 o = m_cat(b, o, "End Site\n{\nOFFSET 0.00 5.00 0.00\n}\n}\n}\n" as *u8)
97 o = m_cat(b, o, "MOTION\nFrames: " as *u8)
98 o = m_cat(b, o, frames)
99 o = m_cat(b, o, "\nFrame Time: 0.0333333\n" as *u8)
100 if rows > 0 { o = m_cat(b, o, "0.0 10.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n" as *u8) }
101 if rows > 1 { o = m_cat(b, o, "1.0 11.0 -2.5 0.0 0.0 90.0 0.0 45.0 0.0\n" as *u8) }
102 b[o] = 0 as u8
103 return o
104}
105
106func m_kat() -> i64 {
107 let b: *u8 = sys_mmap(M_KATBUF)
108 let H: *i64 = sys_mmap(B_H_WORDS * 8) as *i64
109 let names: *u8 = sys_mmap(B_MAX_JOINTS * B_NAMEW)
110 let parent: *i64 = sys_mmap(B_MAX_JOINTS * 8) as *i64
111 let nchan: *i64 = sys_mmap(B_MAX_JOINTS * 8) as *i64
112 let chan0: *i64 = sys_mmap(B_MAX_JOINTS * 8) as *i64
113 let off: *i64 = sys_mmap(B_MAX_JOINTS * 24) as *i64
114 let chtype: *i64 = sys_mmap(B_MAX_CHAN * 8) as *i64
115 var red: i64 = 0
116
117 let n: i64 = m_kat_build(b, "2" as *u8, 2)
118 let r: i64 = b_parse(b, n, H, names, parent, nchan, chan0, off, chtype)
119
120 if r != 0 { b_puts("T1 RED parse-refused-valid code=" as *u8); b_num(r); b_puts("\n" as *u8); red = red + 1 }
121 else {
122 if H[B_H_NJOINT] != 2 { b_puts("T1 RED joints\n" as *u8); red = red + 1 }
123 if H[B_H_NCHAN] != 9 { b_puts("T1 RED chans\n" as *u8); red = red + 1 }
124 if H[B_H_NFRAME] != 2 { b_puts("T1 RED frames\n" as *u8); red = red + 1 }
125 }
126 if red == 0 { b_puts("T1 GREEN joints=2 chans=9 frames=2\n" as *u8) }
127
128 if off[1*3+1] != M_MAGIC_5000 { b_puts("T2 RED chest_oy=" as *u8); b_num(off[1*3+1]); b_puts("\n" as *u8); red = red + 1 }
129 else { b_puts("T2 GREEN endsite-not-a-joint chest_oy=5000\n" as *u8) }
130
131 if H[B_H_FTIME] != M_MAGIC_33333 { b_puts("T3 RED ftime=" as *u8); b_num(H[B_H_FTIME]); b_puts("\n" as *u8); red = red + 1 }
132 else { b_puts("T3 GREEN ftime_us=33333 fps=" as *u8); b_num(B_TSCALE / H[B_H_FTIME]); b_puts("\n" as *u8) }
133
134 let tk: *u8 = sys_mmap(B_TOKW)
135 tk[0] = 45 as u8; tk[1] = 50 as u8; tk[2] = 46 as u8; tk[3] = 53 as u8; tk[4] = 0 as u8
136 let neg: i64 = b_parse_fx(tk, 4, B_SCALE)
137 if neg != 0 - M_MAGIC_2500 { b_puts("T4 RED parse(-2.5)=" as *u8); b_num(neg); b_puts("\n" as *u8); red = red + 1 }
138 else { b_puts("T4 GREEN parse(-2.5)=-2500\n" as *u8) }
139
140 let b2: *u8 = sys_mmap(M_KATBUF)
141 let H2: *i64 = sys_mmap(B_H_WORDS * 8) as *i64
142 let n2: i64 = m_kat_build(b2, "3" as *u8, 2)
143 if b_parse(b2, n2, H2, names, parent, nchan, chan0, off, chtype) != B_R_WIDTH {
144 b_puts("T5 RED short-motion-accepted\n" as *u8); red = red + 1
145 } else { b_puts("T5 GREEN declared-3-rows-2 refused\n" as *u8) }
146
147 let b3: *u8 = sys_mmap(M_KATBUF)
148 let H3: *i64 = sys_mmap(B_H_WORDS * 8) as *i64
149 let n3: i64 = m_kat_build(b3, "2" as *u8, 0)
150 if b_parse(b3, n3, H3, names, parent, nchan, chan0, off, chtype) != B_R_WIDTH {
151 b_puts("T6 RED empty-motion-accepted\n" as *u8); red = red + 1
152 } else { b_puts("T6 GREEN empty-motion refused\n" as *u8) }
153
154 let H4: *i64 = sys_mmap(B_H_WORDS * 8) as *i64
155 let b4: *u8 = sys_mmap(M_KATBUF)
156 let n4: i64 = m_kat_build(b4, "2" as *u8, 2)
157 if b_parse(b4, n4, H4, names, parent, nchan, chan0, off, chtype) != 0 {
158 b_puts("T7 RED fixture-not-idempotent\n" as *u8); red = red + 1
159 } else {
160 let vals: *i64 = sys_mmap(B_MAX_CHAN * 8) as *i64
161 if b_frame_values(b4, n4, H4, 1, vals) != 0 {
162 b_puts("T7 RED frame1-unreadable\n" as *u8); red = red + 1
163 } else {
164 if vals[1] != M_MAGIC_11000 { b_puts("T7 RED v1=" as *u8); b_num(vals[1]); b_puts("\n" as *u8); red = red + 1 }
165 if vals[2] != 0 - M_MAGIC_2500 { b_puts("T7 RED v2=" as *u8); b_num(vals[2]); b_puts("\n" as *u8); red = red + 1 }
166 if vals[5] != M_MAGIC_90000 { b_puts("T7 RED v5=" as *u8); b_num(vals[5]); b_puts("\n" as *u8); red = red + 1 }
167 b_puts("T7 frame1 v1=11000 v2=-2500 v5=90000 idempotent\n" as *u8)
168 }
169 }
170
171 if red > 0 { b_puts("nx_bvhmotion KAT RED teeth_failed=" as *u8); b_num(red); b_puts("\n" as *u8); return 1 }
172 b_puts("nx_bvhmotion KAT GREEN 7/7\n" as *u8)
173 return 0
174}
175
176func main(argc: i64, argv: *i64) -> i64 {
177 if argc < 2 { b_puts("usage: nx_bvhmotion info|joints|dump <file.bvh> [n] | --kat\n" as *u8); return 3 }
178 let verb: *u8 = argv[1] as *u8
179 if b_slen(verb) == 5 { if verb[0] == (45 as u8) { return m_kat() } }
180 if argc < 3 { b_puts("usage: nx_bvhmotion info|joints|dump <file.bvh> [n] | --kat\n" as *u8); return 3 }
181
182 let path: *u8 = argv[2] as *u8
183 let lenp: *i64 = sys_mmap(16) as *i64
184 let buf: *u8 = sys_read_file(path, lenp)
185 if (buf as i64) == 0 { b_puts("nx_bvhmotion unreadable\n" as *u8); return 2 }
186 let len: i64 = lenp[0]
187
188 let H: *i64 = sys_mmap(B_H_WORDS * 8) as *i64
189 let names: *u8 = sys_mmap(B_MAX_JOINTS * B_NAMEW)
190 let parent: *i64 = sys_mmap(B_MAX_JOINTS * 8) as *i64
191 let nchan: *i64 = sys_mmap(B_MAX_JOINTS * 8) as *i64
192 let chan0: *i64 = sys_mmap(B_MAX_JOINTS * 8) as *i64
193 let off: *i64 = sys_mmap(B_MAX_JOINTS * 24) as *i64
194 let chtype: *i64 = sys_mmap(B_MAX_CHAN * 8) as *i64
195
196 let r: i64 = b_parse(buf, len, H, names, parent, nchan, chan0, off, chtype)
197 if r < 0 { return b_refuse(r) }
198
199 if verb[0] == (105 as u8) { return m_info(H) }
200 if verb[0] == (106 as u8) { return m_joints(H, names, parent, nchan, off) }
201 if verb[0] == (100 as u8) {
202 var nn: i64 = M_DUMP_DEF
203 if argc > 3 {
204 let s: *u8 = argv[3] as *u8
205 let v: i64 = b_parse_fx(s, b_slen(s), 1)
206 if v > 0 { nn = v }
207 }
208 return m_dump(buf, len, H, nn)
209 }
210 b_puts("nx_bvhmotion unknown verb\n" as *u8)
211 return 3
212}