code wiki / (root) / nx_fbx_probe.nx

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}