code wiki / (root) / nx_fbx_import.nx

nx_fbx_import.nx source

↩ module page · 156 lines · 8251 B

1// nx_fbx_import.nx -- binary FBX GEOMETRY extraction. Format knowledge lives in nx_fbx_core; this 2// file only knows what it wants (Vertices + PolygonVertexIndex -> fx1024 triangles). 3// 4// ★CONSOLIDATED (seq1788). The node walk was written twice -- here and in nx_fbx_rig.nx (the rig 5// survey: joints, skin clusters, weights, animation curves, emits nothing by design). The purposes 6// differ; the FORMAT READING did not. Both now share nx_fbx_core, so a node-format bug has one home. 7// The consolidation also FIXED A REAL CAPABILITY GAP for free: this reader used to REFUSE v7500+ 8// outright while nx_fbx_rig already handled u64 offsets. Sharing the header decoder means geometry 9// extraction inherits that instead of re-deriving it. 10// 11// ★POLYGON INDICES ARE XOR-TERMINATED: a NEGATIVE index marks the last vertex of a polygon and its 12// true value is -v-1. Missing that reads every polygon one vertex short and silently yields a 13// shredded mesh -- plausible counts over garbage, the failure shape this lane keeps meeting. 14// 15// usage as a lib: fbx_read(path, vbuf, fbuf, vcap, fcap, counts) -> 0 ok, negative on error 16// counts[0]=nv counts[1]=ntri counts[2]=nodes counts[3]=nVertices 17// counts[4]=nPolyIdx counts[5]=nGeometry counts[6]=max_arraylen counts[7]=decode_fails 18// license_tier: ORIGINAL 19import "nx_fbx_core.nx" 20 21// st: 0=vbuf 1=fbuf 2=vcap 3=fcap 4=nv 5=ntri 6=nodes 7=nVerts 8=nPoly 9=nGeom 10=maxlen 11=fails 12=big 22func fbx_walk(buf: *u8, n: i64, pos0: i64, end: i64, st: *i64) -> i64 { 23 var pos: i64 = pos0 24 let hdr: *i64 = sys_mmap(FBXC_OUTN * 8) as *i64 25 let outp: *i64 = sys_mmap(64) as *i64 26 var guard: i64 = 0 27 while guard == 0 { 28 if fbxc_hdr(buf, n, pos, st[12], hdr) != 0 { guard = 1 } else { 29 let eoff: i64 = hdr[FBXC_END] 30 if eoff > end { guard = 1 } else { 31 if eoff <= pos { guard = 1 } else { 32 let nlen: i64 = hdr[FBXC_NAMELEN] 33 let nameo: i64 = hdr[FBXC_NAMEOFF] 34 let props: i64 = hdr[FBXC_PROPSOFF] 35 st[6] = st[6] + 1 36 if fbxc_name_is(buf, nameo, nlen, "Geometry" as *u8) == 1 { st[9] = st[9] + 1 } 37 38 if fbxc_name_is(buf, nameo, nlen, "Vertices" as *u8) == 1 { 39 st[7] = st[7] + 1 40 if hdr[FBXC_NPROPS] >= 1 { 41 if buf[props] == (100 as u8) { 42 let al: i64 = fbxc_u32(buf, props + 1) 43 if al > st[10] { st[10] = al } 44 let arc: i64 = fbxc_array(buf, props + 1, 8, outp) 45 if arc != 0 { st[11] = st[11] + 1 } else { 46 let dp: *u8 = outp[0] as *u8 47 let cnt: i64 = outp[2] 48 let vb: *i64 = st[0] as *i64 49 // MULTI-GEOMETRY (2026-08-23): APPEND at the running total and record 50 // this geometry's base in st[13]; PolygonVertexIndex offsets its 51 // indices by that base, so many Geometry nodes concatenate into ONE 52 // coherent vertex/index space. st[4] is now the TOTAL vertex count, 53 // not the largest single array -- a Fab character ships body/armor/ 54 // hair as separate Geometry nodes and the old max-only read kept one. 55 let vbase: i64 = st[4] 56 st[13] = vbase 57 var i: i64 = 0 58 var nv: i64 = 0 59 while i + 2 < cnt { 60 if vbase + nv < st[2] { 61 vb[(vbase+nv)*3] = fbxc_f64_fx(fbxc_i64le(dp, i*8)) 62 vb[(vbase+nv)*3+1] = fbxc_f64_fx(fbxc_i64le(dp, (i+1)*8)) 63 vb[(vbase+nv)*3+2] = fbxc_f64_fx(fbxc_i64le(dp, (i+2)*8)) 64 nv = nv + 1 65 } 66 i = i + 3 67 } 68 st[4] = vbase + nv 69 } 70 } 71 } 72 } 73 if fbxc_name_is(buf, nameo, nlen, "PolygonVertexIndex" as *u8) == 1 { 74 st[8] = st[8] + 1 75 if hdr[FBXC_NPROPS] >= 1 { 76 if buf[props] == (105 as u8) { 77 let arc2: i64 = fbxc_array(buf, props + 1, 4, outp) 78 if arc2 != 0 { st[11] = st[11] + 1 } else { 79 let dp2: *u8 = outp[0] as *u8 80 let cnt2: i64 = outp[2] 81 let fb: *i64 = st[1] as *i64 82 let poly: *i64 = sys_mmap(512 * 8) as *i64 83 var k: i64 = 0 84 var nt: i64 = st[5] 85 var np: i64 = 0 86 while k < cnt2 { 87 var iv: i64 = fbxc_i32(dp2, k * 4) 88 var last: i64 = 0 89 if iv < 0 { iv = 0 - iv - 1; last = 1 } 90 if np < 512 { poly[np] = iv + st[13]; np = np + 1 } 91 if last == 1 { 92 var j: i64 = 1 93 while j + 1 < np { 94 if nt < st[3] { 95 fb[nt*3] = poly[0] 96 fb[nt*3+1] = poly[j] 97 fb[nt*3+2] = poly[j+1] 98 nt = nt + 1 99 } 100 j = j + 1 101 } 102 np = 0 103 } 104 k = k + 1 105 } 106 sys_munmap(poly as *u8, 512 * 8) 107 st[5] = nt 108 } 109 } 110 } 111 } 112 let childs: i64 = props + hdr[FBXC_PLEN] 113 if childs + hdr[FBXC_HDRSZ] <= eoff { fbx_walk(buf, n, childs, eoff, st) } 114 pos = eoff 115 } 116 } 117 } 118 } 119 sys_munmap(hdr as *u8, FBXC_OUTN * 8) 120 sys_munmap(outp as *u8, 64) 121 return 0 122} 123 124// Read geometry from a binary FBX. 0 ok; -1 unreadable, -2 not an FBX, -4 no vertices found. 125// ⚠v7500+ is now SUPPORTED via the shared header decoder (it used to be refused outright). 126func fbx_read(path: *u8, vbuf: *i64, fbuf: *i64, vcap: i64, fcap: i64, counts: *i64) -> i64 { 127 var z: i64 = 0 128 while z < 8 { counts[z] = 0; z = z + 1 } 129 let bl: *i64 = sys_mmap(16) as *i64 130 bl[0] = 0 131 let buf: *u8 = sys_read_file(path, bl) 132 let n: i64 = bl[0] 133 if (buf as i64) == 0 { return 0 - 1 } 134 let ver: i64 = fbxc_version(buf, n) 135 if ver < 0 { return 0 - 2 } 136 let st: *i64 = sys_mmap(128) as *i64 137 var q: i64 = 0 138 while q < 16 { st[q] = 0; q = q + 1 } 139 st[0] = vbuf as i64 140 st[1] = fbuf as i64 141 st[2] = vcap 142 st[3] = fcap 143 st[12] = fbxc_big(ver) 144 fbx_walk(buf, n, 27, n, st) 145 counts[0] = st[4] 146 counts[1] = st[5] 147 counts[2] = st[6] 148 counts[3] = st[7] 149 counts[4] = st[8] 150 counts[5] = st[9] 151 counts[6] = st[10] 152 counts[7] = st[11] 153 sys_munmap(st as *u8, 128) 154 if counts[0] == 0 { return 0 - 4 } 155 return 0 156}