nx_fbx_probe.nx source
↩ module page · 462 lines · 18662 B
1// nx_fbx_probe.nx -- read a binary FBX and report what actually came out.
2// Probe BEFORE wiring: a reader that returns rc=0 over an empty buffer has already happened twice
3// in this lane (jpeg_ascii decode, the smooth<2 vertex-colour discard), so the counts and the
4// coordinate BOUNDS are printed rather than assumed. Bounds are the real tell -- a mis-parsed f64
5// yields counts that look plausible and coordinates that are absurd.
6// usage: nx_fbx_probe <file.fbx>
7// license_tier: ORIGINAL expect_exit: 0
8import "nx_fbx_import.nx"
9import "nx_fbx_bind.nx"
10
11func fp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
12func fp_putn(v: i64) -> i64 {
13 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
14 var m: i64 = v
15 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
16 let d: *u8 = sys_mmap(24); var k: i64 = 0
17 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
18 let o: *u8 = sys_mmap(24); var w: i64 = 0
19 while w < k { o[w] = d[k-1-w]; w = w + 1 }
20 sys_write(1, o, k)
21 sys_munmap(d, 24); sys_munmap(o, 24)
22 return 0
23}
24
25
26func fp_scene_property_size(b: *u8,p: i64,end: i64) -> i64 {
27 if p < 0 || p >= end { return 0 - 1 }
28 let t: i64 = b[p] as i64
29 var header: i64 = 1
30 if t == 83 || t == 82 { header = 5 }
31 if t == 102 || t == 100 || t == 108 || t == 105 || t == 98 { header = 13 }
32 if header > end - p { return 0 - 1 }
33 let size: i64 = fbxc_prop_size(b,p)
34 if size <= 0 || size > end - p { return 0 - 1 }
35 return size
36}
37
38func fp_scene_string_is(b: *u8,p: i64,end: i64,s: *u8) -> i64 {
39 let size: i64 = fp_scene_property_size(b,p,end)
40 if size < 5 { return 0 }
41 if b[p] != (83 as u8) { return 0 }
42 return fbxc_name_is(b,p+5,size-5,s)
43}
44
45func fp_scene_properties(b: *u8,p0: i64,end: i64,count: i64) -> i64 {
46 var p: i64 = p0
47 var i: i64 = 0
48 while i < count {
49 let size: i64 = fp_scene_property_size(b,p,end)
50 if size < 0 { return 0 - 1 }
51 let t: i64 = b[p] as i64
52 fp_puts(" | " as *u8)
53 if t == 83 {
54 var k: i64 = p + 5
55 while k < p + size {
56 let c: i64 = b[k] as i64
57 if c >= 32 && c <= 126 { sys_write(1,((b as i64)+k) as *u8,1) }
58 else { fp_puts("." as *u8) }
59 k = k + 1
60 }
61 } else {
62 if t == 76 { fp_putn(fbxc_i64le(b,p+1)) }
63 else { if t == 68 { fp_puts("fx1024=" as *u8); fp_putn(fbxc_f64_fx(fbxc_i64le(b,p+1))) }
64 else { if t == 73 { fp_putn(fbxc_i32(b,p+1)) } else { fp_puts("type=" as *u8); fp_putn(t) } } }
65 }
66 p = p + size
67 i = i + 1
68 }
69 fp_puts("\n" as *u8)
70 if p != end { return 0 - 1 }
71 return 0
72}
73
74func fp_scene_walk(b: *u8,n: i64,start: i64,end: i64,big: i64,mesh: i64,depth: i64,st: *i64) -> i64 {
75 if depth > st[1] { return 0 - 2 }
76 let hdr: *i64 = sys_mmap(FBXC_OUTN*8) as *i64
77 var pos: i64 = start
78 var rc: i64 = 0
79 var stop: i64 = 0
80 while pos < end && stop == 0 {
81 if fbxc_hdr(b,n,pos,big,hdr) != 0 { stop = 1 }
82 else {
83 let eo: i64 = hdr[FBXC_END]
84 let p: i64 = hdr[FBXC_PROPSOFF]
85 let plen: i64 = hdr[FBXC_PLEN]
86 if eo <= pos || eo > end || p < pos || p > eo || plen < 0 || plen > eo-p { rc = 0 - 1; stop = 1 }
87 else {
88 let pe: i64 = p + plen
89 let no: i64 = hdr[FBXC_NAMEOFF]
90 let nl: i64 = hdr[FBXC_NAMELEN]
91 var childmesh: i64 = mesh
92 var emit: i64 = 0
93 if fbxc_name_is(b,no,nl,"Model" as *u8) == 1 {
94 childmesh = 0
95 let a: i64 = fp_scene_property_size(b,p,pe)
96 if a > 0 {
97 let c: i64 = fp_scene_property_size(b,p+a,pe)
98 if c > 0 { childmesh = fp_scene_string_is(b,p+a+c,pe,"Mesh" as *u8) }
99 }
100 emit = childmesh
101 }
102 if fbxc_name_is(b,no,nl,"Geometry" as *u8) == 1 { emit = 1 }
103 if mesh == 1 && fbxc_name_is(b,no,nl,"P" as *u8) == 1 { emit = 1 }
104 if emit == 1 {
105 if st[0] >= st[1] { rc = 0 - 2; stop = 1 }
106 else {
107 sys_write(1,((b as i64)+no) as *u8,nl)
108 rc = fp_scene_properties(b,p,pe,hdr[FBXC_NPROPS])
109 st[0] = st[0] + 1
110 if rc != 0 { stop = 1 }
111 }
112 }
113 if stop == 0 {
114 if pe < eo { rc = fp_scene_walk(b,n,pe,eo,big,childmesh,depth+1,st) }
115 if rc != 0 { stop = 1 }
116 }
117 pos = eo
118 }
119 }
120 }
121 sys_munmap(hdr as *u8,FBXC_OUTN*8)
122 return rc
123}
124
125func fp_scene(path: *u8,budgetstr: *u8) -> i64 {
126 var budget: i64 = 0
127 var i: i64 = 0
128 while budgetstr[i] != (0 as u8) {
129 let c: i64 = budgetstr[i] as i64
130 if c < 48 || c > 57 { return 2 }
131 if budget > 214748364 { return 2 }
132 budget = budget*10+c-48
133 i = i + 1
134 }
135 if budget <= 0 { return 2 }
136 let lp: *i64 = sys_mmap(16) as *i64
137 let b: *u8 = sys_read_file(path,lp)
138 if (b as i64) == 0 { return 3 }
139 let n: i64 = lp[0]
140 let ver: i64 = fbxc_version(b,n)
141 if ver < 0 { return 3 }
142 let st: *i64 = sys_mmap(16) as *i64
143 st[0] = 0; st[1] = budget
144 let rc: i64 = fp_scene_walk(b,n,27,n,fbxc_big(ver),0,0,st)
145 fp_puts("SCENE-PROBE rows=" as *u8); fp_putn(st[0])
146 fp_puts(" row_depth_budget=" as *u8); fp_putn(budget)
147 fp_puts(" rc=" as *u8); fp_putn(rc); fp_puts("\n" as *u8)
148 sys_munmap(st as *u8,16); sys_munmap(b,n); sys_munmap(lp as *u8,16)
149 if rc != 0 { return 4 }
150 return 0
151}
152
153
154
155func fp_bind_composed(s: *FbxBindScene) -> i64 {
156 let refs: *i64 = sys_mmap(s.count*FBB_MATRIX_WORDS*FBB_WORD) as *i64
157 let seen: *i64 = sys_mmap(s.count*FBB_WORD) as *i64
158 let spread: *i64 = sys_mmap(s.count*FBB_WORD) as *i64
159 let tmp: *i64 = sys_mmap(FBB_MATRIX_WORDS*FBB_WORD) as *i64
160 var rc: i64 = 0
161 var i: i64 = 0
162 while i < s.count && rc == 0 {
163 let c: *FbxBindObject = fbb_object(s,i)
164 if c.kind == FBB_CLUSTER {
165 let si: i64 = fbb_parent(s,c.id,FBB_SKIN)
166 if si < 0 { rc = si }
167 else {
168 let sk: *FbxBindObject = fbb_object(s,si)
169 let gi: i64 = fbb_parent(s,sk.id,FBB_GEOMETRY)
170 if gi < 0 { rc = gi }
171 else {
172 rc = fbb_compose(c.link_matrix as *i64,c.matrix as *i64,tmp)
173 if rc == 0 {
174 var j: i64 = 0
175 while j < FBB_MATRIX_WORDS {
176 let idx: i64 = gi*FBB_MATRIX_WORDS+j
177 if seen[gi] == 0 { refs[idx] = tmp[j] }
178 else {
179 // Both operands must permit a signed difference before measurement.
180 let a: i64 = refs[idx]
181 let b: i64 = tmp[j]
182 if b < 0 && a > FBB_I64_MAX+b { rc = FBB_RANGE }
183 else {
184 if b > 0 && a < (0-FBB_I64_MAX)+b { rc = FBB_RANGE }
185 else {
186 var delta: i64 = a-b
187 if delta < 0 { delta = 0-delta }
188 if delta > spread[gi] { spread[gi] = delta }
189 }
190 }
191 }
192 j = j+1
193 }
194 seen[gi] = seen[gi]+1
195 }
196 }
197 }
198 }
199 i = i+1
200 }
201 i = 0
202 while i < s.count && rc == 0 {
203 let g: *FbxBindObject = fbb_object(s,i)
204 if g.kind == FBB_GEOMETRY {
205 fp_puts("BIND-COMPOSE geometry_id=" as *u8); fp_putn(g.id)
206 fp_puts(" vertices=" as *u8); fp_putn(g.count)
207 fp_puts(" clusters=" as *u8); fp_putn(seen[i])
208 fp_puts(" max_spread_q20=" as *u8); fp_putn(spread[i])
209 fp_puts(" first_matrix_q20=" as *u8)
210 var j: i64 = 0
211 while j < FBB_MATRIX_WORDS {
212 if j != 0 { fp_puts("," as *u8) }
213 fp_putn(refs[i*FBB_MATRIX_WORDS+j]); j = j+1
214 }
215 fp_puts("\n" as *u8)
216 let mi: i64 = fbb_parent(s,g.id,FBB_MODEL)
217 if mi < 0 { rc=mi }
218 else {
219 let model: *FbxBindObject = fbb_object(s,mi)
220 fp_puts("BIND-POSE model_id=" as *u8); fp_putn(model.id)
221 if model.matrix == 0 { fp_puts(" missing=1\n" as *u8) }
222 else {
223 fp_puts(" matrix_q20=" as *u8)
224 let pm: *i64 = model.matrix as *i64
225 var k: i64 = 0
226 while k < FBB_MATRIX_WORDS {
227 if k != 0 { fp_puts("," as *u8) }
228 fp_putn(pm[k]); k=k+1
229 }
230 fp_puts("\n" as *u8)
231 }
232 }
233 }
234 i = i+1
235 }
236 fp_puts("BIND-COMPOSE rc=" as *u8); fp_putn(rc)
237 fp_puts(" diagnostic_only=1 vertices_changed=0\n" as *u8)
238 sys_munmap(refs as *u8,s.count*FBB_MATRIX_WORDS*FBB_WORD)
239 sys_munmap(seen as *u8,s.count*FBB_WORD)
240 sys_munmap(spread as *u8,s.count*FBB_WORD)
241 sys_munmap(tmp as *u8,FBB_MATRIX_WORDS*FBB_WORD)
242 return rc
243}
244
245
246func fp_bind_math_check() -> i64 {
247 let a: *i64 = sys_mmap(128) as *i64
248 let b: *i64 = sys_mmap(128) as *i64
249 let out: *i64 = sys_mmap(128) as *i64
250 let expected: *i64 = sys_mmap(128) as *i64
251 // A rotates 90 degrees about Z and translates (5,7,11).
252 // B scales (2,3,4) and translates (13,17,19).
253 // A*B therefore translates (-12,20,30); B*A would differ.
254 a[1]=FBB_Q; a[4]=0-FBB_Q; a[10]=FBB_Q; a[15]=FBB_Q
255 a[12]=5*FBB_Q; a[13]=7*FBB_Q; a[14]=11*FBB_Q
256 b[0]=2*FBB_Q; b[5]=3*FBB_Q; b[10]=4*FBB_Q; b[15]=FBB_Q
257 b[12]=13*FBB_Q; b[13]=17*FBB_Q; b[14]=19*FBB_Q
258 expected[1]=2*FBB_Q; expected[4]=0-3*FBB_Q
259 expected[10]=4*FBB_Q; expected[15]=FBB_Q
260 expected[12]=0-12*FBB_Q; expected[13]=20*FBB_Q; expected[14]=30*FBB_Q
261 var passed: i64 = 0
262 var bad: i64 = 0
263 let rc: i64 = fbb_compose(a,b,out)
264 if rc != 0 { bad=1 }
265 var i: i64 = 0
266 while i < 16 { if out[i] != expected[i] { bad=1 } i=i+1 }
267 if bad == 0 { passed=passed+1 }
268 a[0]=FBB_I64_MAX
269 let overflow: i64 = fbb_compose(a,b,out)
270 bad=0
271 if overflow != FBB_RANGE { bad=1 }
272 i=0
273 while i < 16 { if out[i] != expected[i] { bad=1 } i=i+1 }
274 if bad == 0 { passed=passed+1 }
275 fp_puts("BIND-MATH checks=" as *u8); fp_putn(passed); fp_puts("/2\n" as *u8)
276 sys_munmap(a as *u8,128); sys_munmap(b as *u8,128)
277 sys_munmap(out as *u8,128); sys_munmap(expected as *u8,128)
278 if passed != 2 { return 7 }
279 return 0
280}
281
282
283func fp_bind_vertices(path: *u8,s: *FbxBindScene) -> i64 {
284 var nv: i64 = 0
285 var i: i64 = 0
286 while i < s.count {
287 let g: *FbxBindObject = fbb_object(s,i)
288 if g.kind == FBB_GEOMETRY { nv=nv+g.count }
289 i=i+1
290 }
291 if nv <= 0 || nv > FBB_I64_MAX/(3*FBB_WORD) { return FBB_RANGE }
292 let raw: *i64 = sys_mmap(nv*3*FBB_WORD) as *i64
293 let converted: *i64 = sys_mmap(nv*3*FBB_WORD) as *i64
294 let unused: *i64 = sys_mmap(FBB_WORD) as *i64
295 let counts: *i64 = sys_mmap(128) as *i64
296 // Geometry-only inspection: zero index capacity asks the existing reader to emit no indices.
297 var rc: i64 = fbx_read(path,raw,unused,nv,0,counts)
298 if counts[0] != nv || counts[7] != 0 { rc=FBB_BAD }
299 if rc == 0 { rc=fbb_apply_pose(s,raw,nv,converted) }
300 fp_puts("BIND-VERTICES rc=" as *u8); fp_putn(rc)
301 fp_puts(" vertices=" as *u8); fp_putn(nv); fp_puts("\n" as *u8)
302 if rc == 0 {
303 var base: i64 = 0
304 i=0
305 while i < s.count {
306 let g: *FbxBindObject = fbb_object(s,i)
307 if g.kind == FBB_GEOMETRY {
308 var changed: i64 = 0
309 var j: i64 = 0
310 while j < g.count {
311 let off: i64 = (base+j)*3
312 if raw[off] != converted[off] || raw[off+1] != converted[off+1] || raw[off+2] != converted[off+2] { changed=changed+1 }
313 j=j+1
314 }
315 fp_puts("BIND-VERTICES geometry_id=" as *u8); fp_putn(g.id)
316 fp_puts(" changed=" as *u8); fp_putn(changed)
317 fp_puts(" axis_bounds_fx1024=" as *u8)
318 var axis: i64 = 0
319 while axis < 3 {
320 var lo: i64 = FBB_I64_MAX
321 var hi: i64 = 0-FBB_I64_MAX
322 j=0
323 while j < g.count {
324 let v: i64 = converted[(base+j)*3+axis]
325 if v < lo { lo=v }
326 if v > hi { hi=v }
327 j=j+1
328 }
329 if axis != 0 { fp_puts("," as *u8) }
330 fp_putn(lo); fp_puts(":" as *u8); fp_putn(hi)
331 axis=axis+1
332 }
333 fp_puts("\n" as *u8)
334 base=base+g.count
335 }
336 i=i+1
337 }
338 // Count mismatch must preserve the destination byte-for-byte.
339 let refused: i64 = fbb_apply_pose(s,raw,nv-1,converted)
340 if refused != FBB_BAD { rc=FBB_BAD }
341 let repeat: *i64 = sys_mmap(nv*3*FBB_WORD) as *i64
342 let again: i64 = fbb_apply_pose(s,raw,nv,repeat)
343 if again != 0 { rc=again }
344 i=0
345 while i < nv*3 { if converted[i] != repeat[i] { rc=FBB_CONFLICT } i=i+1 }
346 sys_munmap(repeat as *u8,nv*3*FBB_WORD)
347 fp_puts("BIND-VERTICES repeat_and_refusal_rc=" as *u8); fp_putn(rc)
348 fp_puts(" disk_assets_changed=0\n" as *u8)
349 }
350 sys_munmap(raw as *u8,nv*3*FBB_WORD); sys_munmap(converted as *u8,nv*3*FBB_WORD)
351 sys_munmap(unused as *u8,FBB_WORD); sys_munmap(counts as *u8,128)
352 return rc
353}
354
355func fp_bind(path: *u8) -> i64 {
356 if fp_bind_math_check() != 0 { return 7 }
357 let lp: *i64 = sys_mmap(16) as *i64
358 let b: *u8 = sys_read_file(path,lp)
359 if (b as i64) == 0 { sys_munmap(lp as *u8,16); return 3 }
360 let s: *FbxBindScene = sys_mmap(__size_of(FbxBindScene)) as *FbxBindScene
361 let rc: i64 = fbb_load(b,lp[0],s)
362 fp_puts("BIND-PROBE load_rc=" as *u8); fp_putn(rc)
363 fp_puts(" objects=" as *u8); fp_putn(s.count)
364 fp_puts(" links=" as *u8); fp_putn(s.link_count); fp_puts("\n" as *u8)
365 if rc != 0 { return 4 }
366 var clusters: i64 = 0
367 var geometry: i64 = 0
368 var i: i64 = 0
369 while i < s.count {
370 let o: *FbxBindObject = fbb_object(s,i)
371 if o.kind == FBB_CLUSTER { clusters = clusters+1 }
372 if o.kind == FBB_GEOMETRY { geometry = geometry+1 }
373 i = i+1
374 }
375 let resolved: i64 = fbb_resolve(s)
376 fp_puts("BIND-PROBE clusters=" as *u8); fp_putn(clusters)
377 fp_puts(" geometries=" as *u8); fp_putn(geometry)
378 fp_puts(" raw_transform_agreement_rc=" as *u8); fp_putn(resolved)
379 fp_puts(" diagnostic_only=1 vertices_changed=0\n" as *u8)
380 let poses: i64 = fbb_pose_models(b,lp[0],s)
381 fp_puts("BIND-POSE load_rc=" as *u8); fp_putn(poses); fp_puts("\n" as *u8)
382 if poses != 0 { return 8 }
383 let composed: i64 = fp_bind_composed(s)
384 let vertices: i64 = fp_bind_vertices(path,s)
385 sys_munmap(b,lp[0]); sys_munmap(lp as *u8,16)
386 // Standalone diagnostic process reclaims scene allocations at exit.
387 if vertices != 0 { return 9 }
388 if composed != 0 { return 6 }
389 if resolved != 0 { return 5 }
390 return 0
391}
392
393func main(argc: i64, argv: *i64) -> i64 {
394 if argc == 3 {
395 let mode: *u8 = argv[2] as *u8
396 var size: i64 = 0
397 while mode[size] != (0 as u8) { size = size+1 }
398 if fbxc_name_is(mode,0,size,"bind" as *u8) != 1 { return 2 }
399 return fp_bind(argv[1] as *u8)
400 }
401 if argc == 4 {
402 let mode: *u8 = argv[2] as *u8
403 var modebytes: i64 = 0
404 while mode[modebytes] != (0 as u8) { modebytes = modebytes + 1 }
405 if fbxc_name_is(mode,0,modebytes,"scene" as *u8) != 1 { return 2 }
406 return fp_scene(argv[1] as *u8,argv[3] as *u8)
407 }
408 if argc < 2 { fp_puts("REFUSED reason=need_fbx_path\n" as *u8); return 2 }
409 let vcap: i64 = 400000
410 let fcap: i64 = 800000
411 let vb: *i64 = sys_mmap(vcap * 3 * 8) as *i64
412 let fb: *i64 = sys_mmap(fcap * 3 * 8) as *i64
413 let ct: *i64 = sys_mmap(128) as *i64
414 let rc: i64 = fbx_read(argv[1] as *u8, vb, fb, vcap, fcap, ct)
415 fp_puts("rc=" as *u8); fp_putn(rc)
416 fp_puts(" verts=" as *u8); fp_putn(ct[0])
417 fp_puts(" tris=" as *u8); fp_putn(ct[1])
418 fp_puts(" nodes=" as *u8); fp_putn(ct[2])
419 fp_puts(" n_Vertices=" as *u8); fp_putn(ct[3])
420 fp_puts(" n_PolyIdx=" as *u8); fp_putn(ct[4])
421 fp_puts(" n_Geometry=" as *u8); fp_putn(ct[5])
422 fp_puts(" max_arraylen=" as *u8); fp_putn(ct[6])
423 fp_puts(" decode_fails=" as *u8); fp_putn(ct[7])
424 if rc != 0 { fp_puts(" FAILED\n" as *u8); return 3 }
425 if ct[0] <= 0 { fp_puts(" NO_VERTS\n" as *u8); return 4 }
426
427 // coordinate bounds in fx1024 -- the honest check that the f64 decode is real
428 var xlo: i64 = 999999999
429 var xhi: i64 = 0 - 999999999
430 var ylo: i64 = 999999999
431 var yhi: i64 = 0 - 999999999
432 var zlo: i64 = 999999999
433 var zhi: i64 = 0 - 999999999
434 var i: i64 = 0
435 while i < ct[0] {
436 let x: i64 = vb[i*3]
437 let y: i64 = vb[i*3+1]
438 let z: i64 = vb[i*3+2]
439 if x < xlo { xlo = x }
440 if x > xhi { xhi = x }
441 if y < ylo { ylo = y }
442 if y > yhi { yhi = y }
443 if z < zlo { zlo = z }
444 if z > zhi { zhi = z }
445 i = i + 1
446 }
447 fp_puts(" x=[" as *u8); fp_putn(xlo); fp_puts("," as *u8); fp_putn(xhi)
448 fp_puts("] y=[" as *u8); fp_putn(ylo); fp_puts("," as *u8); fp_putn(yhi)
449 fp_puts("] z=[" as *u8); fp_putn(zlo); fp_puts("," as *u8); fp_putn(zhi)
450 fp_puts("] height_fx=" as *u8); fp_putn(yhi - ylo)
451 // max triangle index must stay inside the vertex array or the mesh is shredded
452 var maxi: i64 = 0
453 var t: i64 = 0
454 while t < ct[1] * 3 {
455 if fb[t] > maxi { maxi = fb[t] }
456 t = t + 1
457 }
458 fp_puts(" max_index=" as *u8); fp_putn(maxi)
459 if maxi >= ct[0] { fp_puts(" INDEX_OUT_OF_RANGE" as *u8) } else { fp_puts(" indices_in_range" as *u8) }
460 fp_puts("\n" as *u8)
461 return 0
462}