nx_fbx_bind_scratch_candidate_t181.nx source
↩ module page · 511 lines · 21919 B
1// Bind-pose geometry conversion for skinned binary FBX assets.
2// Diagnostic prototype: raw Transform is bone-relative. TransformLink * Transform
3// reconstructs mesh-global bind space; agreement must be qualified before conversion.
4import "nx_fbx_core.nx"
5
6const FBB_WORD: i64 = 8
7const FBB_MATRIX_WORDS: i64 = 16
8const FBB_Q: i64 = 1048576
9const FBB_VERTEX_Q: i64 = 1024
10const FBB_I64_MAX: i64 = 9223372036854775807
11const FBB_BAD: i64 = 0 - 10
12const FBB_MISSING: i64 = 0 - 11
13const FBB_CONFLICT: i64 = 0 - 12
14const FBB_RANGE: i64 = 0 - 13
15const FBB_GEOMETRIC: i64 = 0 - 14
16const FBB_MEMORY: i64 = 0 - 15
17const FBB_GEOMETRY: i64 = 1
18const FBB_SKIN: i64 = 2
19const FBB_CLUSTER: i64 = 3
20const FBB_MODEL: i64 = 4
21
22struct FbxBindObject {
23 id: i64,
24 kind: i64,
25 count: i64,
26 matrix: i64,
27 geometric: i64,
28 link_matrix: i64,
29}
30struct FbxBindScene {
31 objects: *FbxBindObject,
32 count: i64,
33 links: *i64,
34 link_count: i64,
35}
36
37func fbb_property_size(b: *u8,p: i64,end: i64) -> i64 {
38 if p < 0 || p >= end { return FBB_BAD }
39 let t: i64 = b[p] as i64
40 var h: i64 = 1
41 if t == 83 || t == 82 { h = 5 }
42 if t == 102 || t == 100 || t == 108 || t == 105 || t == 98 { h = 13 }
43 if h > end-p { return FBB_BAD }
44 let size: i64 = fbxc_prop_size(b,p)
45 if size <= 0 || size > end-p { return FBB_BAD }
46 return size
47}
48func fbb_string(b: *u8,p: i64,end: i64,s: *u8) -> i64 {
49 let size: i64 = fbb_property_size(b,p,end)
50 if size < 5 { return 0 }
51 if b[p] != (83 as u8) { return 0 }
52 return fbxc_name_is(b,p+5,size-5,s)
53}
54func fbb_node(b: *u8,n: i64,p: i64,end: i64,big: i64,h: *i64) -> i64 {
55 var hs: i64 = 13
56 if big == 1 { hs = 25 }
57 if p < 0 || p > end || end > n || hs > end-p { return FBB_BAD }
58 // A null record terminates this sibling list.
59 if fbxc_u32(b,p) == 0 {
60 var z: i64 = 0
61 while z < hs { if b[p+z] != (0 as u8) { return FBB_BAD } z = z+1 }
62 return 0
63 }
64 if fbxc_hdr(b,n,p,big,h) != 0 { return FBB_BAD }
65 let eo: i64 = h[FBXC_END]
66 let po: i64 = h[FBXC_PROPSOFF]
67 let pl: i64 = h[FBXC_PLEN]
68 if eo <= p || eo > end || po < p || po > eo || pl < 0 || pl > eo-po { return FBB_BAD }
69 return 1
70}
71func fbb_find_child(b: *u8,n: i64,start: i64,end: i64,big: i64,name: *u8,out: *i64) -> i64 {
72 let h: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
73 var p: i64 = start
74 var rc: i64 = 0
75 var stop: i64 = 0
76 while p < end && stop == 0 {
77 rc = fbb_node(b,n,p,end,big,h)
78 if rc <= 0 { stop = 1 }
79 else {
80 if fbxc_name_is(b,h[FBXC_NAMEOFF],h[FBXC_NAMELEN],name) == 1 {
81 var j: i64 = 0
82 while j < FBXC_OUTN { out[j] = h[j]; j = j+1 }
83 stop = 1
84 } else { p = h[FBXC_END]; rc = 0 }
85 }
86 }
87 sys_munmap(h as *u8,FBXC_OUTN*FBB_WORD)
88 return rc
89}
90func fbb_sibling_count(b: *u8,n: i64,start: i64,end: i64,big: i64) -> i64 {
91 let h: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
92 var p: i64 = start
93 var count: i64 = 0
94 var stop: i64 = 0
95 while p < end && stop == 0 {
96 let rc: i64 = fbb_node(b,n,p,end,big,h)
97 if rc < 0 { count = rc; stop = 1 }
98 else { if rc == 0 { stop = 1 } else { count = count+1; p = h[FBXC_END] } }
99 }
100 sys_munmap(h as *u8,FBXC_OUTN*FBB_WORD)
101 return count
102}
103func fbb_matrix(b: *u8,p: i64,end: i64,out: *i64) -> i64 {
104 let size: i64 = fbb_property_size(b,p,end)
105 if size < 13 { return FBB_BAD }
106 if b[p] != (100 as u8) || fbxc_u32(b,p+1) != FBB_MATRIX_WORDS { return FBB_BAD }
107 let meta: *i64 = sys_mmap(4*FBB_WORD) as *i64
108 if fbxc_array(b,p+1,FBB_WORD,meta) != 0 { sys_munmap(meta as *u8,4*FBB_WORD); return FBB_BAD }
109 var rc: i64 = 0
110 if meta[1] != FBB_MATRIX_WORDS*FBB_WORD { rc = FBB_BAD }
111 var j: i64 = 0
112 while j < FBB_MATRIX_WORDS && rc == 0 {
113 let bits: i64 = fbxc_i64le(meta[0] as *u8,j*FBB_WORD)
114 let exponent: i64 = (bits >> 52) & 2047
115 // Q20 must fit a signed i64; reject nonfinite and unrepresentable input.
116 if exponent >= 1065 { rc = FBB_RANGE }
117 else { out[j] = fbxc_f64_p20(bits) }
118 j = j+1
119 }
120 if meta[3] == 1 { sys_munmap(meta[0] as *u8,meta[1]) }
121 sys_munmap(meta as *u8,4*FBB_WORD)
122 if rc != 0 { return rc }
123 if out[3] != 0 || out[7] != 0 || out[11] != 0 || out[15] != FBB_Q { return FBB_BAD }
124 return 0
125}
126func fbb_parent(s: *FbxBindScene,id: i64,kind: i64) -> i64 {
127 var found: i64 = FBB_MISSING
128 var i: i64 = 0
129 while i < s.link_count {
130 if s.links[i*2] == id {
131 let pid: i64 = s.links[i*2+1]
132 var j: i64 = 0
133 while j < s.count {
134 let o: *FbxBindObject = ((s.objects as i64)+j*__size_of(FbxBindObject)) as *FbxBindObject
135 if o.id == pid && o.kind == kind {
136 if found >= 0 && found != j { return FBB_CONFLICT }
137 found = j
138 }
139 j = j+1
140 }
141 }
142 i = i+1
143 }
144 return found
145}
146func fbb_object(s: *FbxBindScene,i: i64) -> *FbxBindObject {
147 return ((s.objects as i64)+i*__size_of(FbxBindObject)) as *FbxBindObject
148}
149func fbb_geometric_check(b: *u8,n: i64,start: i64,end: i64,big: i64,h: *i64) -> i64 {
150 let hit: i64 = fbb_find_child(b,n,start,end,big,"Properties70" as *u8,h)
151 if hit <= 0 { return hit }
152 var p: i64 = h[FBXC_PROPSOFF]+h[FBXC_PLEN]
153 let pe: i64 = h[FBXC_END]
154 while p < pe {
155 let rc: i64 = fbb_node(b,n,p,pe,big,h)
156 if rc <= 0 { return rc }
157 let po: i64 = h[FBXC_PROPSOFF]
158 let ep: i64 = po+h[FBXC_PLEN]
159 if fbb_string(b,po,ep,"GeometricTranslation" as *u8) == 1 { return FBB_GEOMETRIC }
160 if fbb_string(b,po,ep,"GeometricRotation" as *u8) == 1 { return FBB_GEOMETRIC }
161 if fbb_string(b,po,ep,"GeometricScaling" as *u8) == 1 { return FBB_GEOMETRIC }
162 p = h[FBXC_END]
163 }
164 return 0
165}
166func fbb_load(b: *u8,n: i64,s: *FbxBindScene) -> i64 {
167 let version: i64 = fbxc_version(b,n)
168 if version < 0 { return FBB_BAD }
169 let big: i64 = fbxc_big(version)
170 let h: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
171 let c: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
172 if fbb_find_child(b,n,FBXC_TOPOFF,n,big,"Objects" as *u8,h) != 1 { return FBB_BAD }
173 let start: i64 = h[FBXC_PROPSOFF]+h[FBXC_PLEN]
174 let end: i64 = h[FBXC_END]
175 let count: i64 = fbb_sibling_count(b,n,start,end,big)
176 if count <= 0 { return FBB_BAD }
177 s.objects = sys_mmap(count*__size_of(FbxBindObject)) as *FbxBindObject
178 s.count = count
179 var p: i64 = start
180 var i: i64 = 0
181 while i < count {
182 if fbb_node(b,n,p,end,big,h) != 1 { return FBB_BAD }
183 let o: *FbxBindObject = fbb_object(s,i)
184 let po: i64 = h[FBXC_PROPSOFF]
185 let ep: i64 = po+h[FBXC_PLEN]
186 if fbb_property_size(b,po,ep) < 9 || b[po] != (76 as u8) { return FBB_BAD }
187 o.id = fbxc_i64le(b,po+1)
188 let named: i64 = fbb_property_size(b,po+9,ep)
189 if named < 0 { return FBB_BAD }
190 let typep: i64 = po+9+named
191 let child: i64 = ep
192 let objend: i64 = h[FBXC_END]
193 if fbxc_name_is(b,h[FBXC_NAMEOFF],h[FBXC_NAMELEN],"Geometry" as *u8) == 1 {
194 if fbb_string(b,typep,ep,"Mesh" as *u8) == 1 {
195 o.kind = FBB_GEOMETRY
196 if fbb_find_child(b,n,child,objend,big,"Vertices" as *u8,c) != 1 { return FBB_BAD }
197 let vp: i64 = c[FBXC_PROPSOFF]
198 if fbb_property_size(b,vp,vp+c[FBXC_PLEN]) < 13 || b[vp] != (100 as u8) { return FBB_BAD }
199 let scalars: i64 = fbxc_u32(b,vp+1)
200 if scalars%3 != 0 { return FBB_BAD }
201 o.count = scalars/3
202 }
203 }
204 if fbxc_name_is(b,h[FBXC_NAMEOFF],h[FBXC_NAMELEN],"Deformer" as *u8) == 1 {
205 if fbb_string(b,typep,ep,"Skin" as *u8) == 1 { o.kind = FBB_SKIN }
206 if fbb_string(b,typep,ep,"Cluster" as *u8) == 1 {
207 o.kind = FBB_CLUSTER
208 if fbb_find_child(b,n,child,objend,big,"Transform" as *u8,c) != 1 { return FBB_MISSING }
209 o.matrix = sys_mmap(FBB_MATRIX_WORDS*FBB_WORD) as i64
210 let rc: i64 = fbb_matrix(b,c[FBXC_PROPSOFF],c[FBXC_PROPSOFF]+c[FBXC_PLEN],o.matrix as *i64)
211 if rc != 0 { return rc }
212 if fbb_find_child(b,n,child,objend,big,"TransformLink" as *u8,c) != 1 { return FBB_MISSING }
213 o.link_matrix = sys_mmap(FBB_MATRIX_WORDS*FBB_WORD) as i64
214 let lr: i64 = fbb_matrix(b,c[FBXC_PROPSOFF],c[FBXC_PROPSOFF]+c[FBXC_PLEN],o.link_matrix as *i64)
215 if lr != 0 { return lr }
216 }
217 }
218 if fbxc_name_is(b,h[FBXC_NAMEOFF],h[FBXC_NAMELEN],"Model" as *u8) == 1 {
219 o.kind = FBB_MODEL
220 o.geometric = fbb_geometric_check(b,n,child,objend,big,c)
221 if o.geometric < 0 && o.geometric != FBB_GEOMETRIC { return o.geometric }
222 }
223 p = objend
224 i = i+1
225 }
226 if fbb_find_child(b,n,FBXC_TOPOFF,n,big,"Connections" as *u8,h) != 1 { return FBB_BAD }
227 p = h[FBXC_PROPSOFF]+h[FBXC_PLEN]
228 let lend: i64 = h[FBXC_END]
229 let lc: i64 = fbb_sibling_count(b,n,p,lend,big)
230 if lc < 0 { return lc }
231 s.links = sys_mmap(lc*2*FBB_WORD) as *i64
232 s.link_count = 0
233 i = 0
234 while i < lc {
235 if fbb_node(b,n,p,lend,big,h) != 1 { return FBB_BAD }
236 let po: i64 = h[FBXC_PROPSOFF]
237 let ep: i64 = po+h[FBXC_PLEN]
238 if fbb_string(b,po,ep,"OO" as *u8) == 1 {
239 let a: i64 = po+fbb_property_size(b,po,ep)
240 if fbb_property_size(b,a,ep) != 9 || fbb_property_size(b,a+9,ep) != 9 { return FBB_BAD }
241 if b[a] != (76 as u8) || b[a+9] != (76 as u8) { return FBB_BAD }
242 s.links[s.link_count*2] = fbxc_i64le(b,a+1)
243 s.links[s.link_count*2+1] = fbxc_i64le(b,a+10)
244 s.link_count = s.link_count+1
245 }
246 p = h[FBXC_END]
247 i = i+1
248 }
249 sys_munmap(h as *u8,FBXC_OUTN*FBB_WORD)
250 sys_munmap(c as *u8,FBXC_OUTN*FBB_WORD)
251 return 0
252}
253func fbb_resolve(s: *FbxBindScene) -> i64 {
254 var i: i64 = 0
255 while i < s.count {
256 let c: *FbxBindObject = fbb_object(s,i)
257 if c.kind == FBB_CLUSTER {
258 let si: i64 = fbb_parent(s,c.id,FBB_SKIN)
259 if si < 0 { return si }
260 let sk: *FbxBindObject = fbb_object(s,si)
261 let gi: i64 = fbb_parent(s,sk.id,FBB_GEOMETRY)
262 if gi < 0 { return gi }
263 let g: *FbxBindObject = fbb_object(s,gi)
264 if g.matrix == 0 { g.matrix = c.matrix }
265 else {
266 let a: *i64 = g.matrix as *i64
267 let d: *i64 = c.matrix as *i64
268 var j: i64 = 0
269 while j < FBB_MATRIX_WORDS { if a[j] != d[j] { return FBB_CONFLICT } j = j+1 }
270 }
271 }
272 i = i+1
273 }
274 i = 0
275 while i < s.count {
276 let g: *FbxBindObject = fbb_object(s,i)
277 if g.kind == FBB_GEOMETRY {
278 if g.matrix == 0 { return FBB_MISSING }
279 let mi: i64 = fbb_parent(s,g.id,FBB_MODEL)
280 if mi < 0 { return mi }
281 let model: *FbxBindObject = fbb_object(s,mi)
282 if model.geometric != 0 { return model.geometric }
283 }
284 i = i+1
285 }
286 return 0
287}
288
289// Column-major affine composition. Overflow checks precede every multiply/add.
290// out remains untouched on failure; Q20 truncation is measured by the diagnostic.
291// Caller-owned scratch avoids one allocation per joint while preserving atomic output.
292func fbb_compose_into(a:*i64,b:*i64,out:*i64,tmp:*i64,tmp_words:i64)->i64 {
293 if (a as i64)<=0||(b as i64)<=0||(out as i64)<=0||(tmp as i64)<=0||tmp_words<FBB_MATRIX_WORDS{return FBB_BAD}
294 let bytes:i64=FBB_MATRIX_WORDS*FBB_WORD
295 let ta:i64=tmp as i64;let aa:i64=a as i64;let ba:i64=b as i64;let oa:i64=out as i64
296 if (ta>=aa&&ta-aa<bytes)||(aa>ta&&aa-ta<bytes){return FBB_CONFLICT}
297 if (ta>=ba&&ta-ba<bytes)||(ba>ta&&ba-ta<bytes){return FBB_CONFLICT}
298 if (ta>=oa&&ta-oa<bytes)||(oa>ta&&oa-ta<bytes){return FBB_CONFLICT}
299 var rc: i64 = 0
300 var col: i64 = 0
301 while col < 4 && rc == 0 {
302 var row: i64 = 0
303 while row < 4 && rc == 0 {
304 var sum: i64 = 0
305 var k: i64 = 0
306 while k < 4 && rc == 0 {
307 let x: i64 = a[k*4+row]
308 let y: i64 = b[col*4+k]
309 if x < (0-FBB_I64_MAX) || y < (0-FBB_I64_MAX) { rc = FBB_RANGE }
310 else {
311 var ax: i64 = x
312 var ay: i64 = y
313 if ax < 0 { ax = 0-ax }
314 if ay < 0 { ay = 0-ay }
315 if ay != 0 { if ax > FBB_I64_MAX/ay { rc = FBB_RANGE } }
316 if rc == 0 {
317 let term: i64 = (x*y)/FBB_Q
318 if term > 0 && sum > FBB_I64_MAX-term { rc = FBB_RANGE }
319 else {
320 if term < 0 && sum < (0-FBB_I64_MAX)-term { rc = FBB_RANGE }
321 else { sum = sum+term }
322 }
323 }
324 }
325 k = k+1
326 }
327 tmp[col*4+row] = sum
328 row = row+1
329 }
330 col = col+1
331 }
332 if rc == 0 {
333 var j: i64 = 0
334 while j < FBB_MATRIX_WORDS { out[j] = tmp[j]; j = j+1 }
335 }
336 return rc
337}
338
339func fbb_compose(a:*i64,b:*i64,out:*i64)->i64 {
340 let tmp:*i64=sys_mmap_try(FBB_MATRIX_WORDS*FBB_WORD) as *i64
341 if (tmp as i64)<=0{return FBB_MEMORY}
342 let rc:i64=fbb_compose_into(a,b,out,tmp,FBB_MATRIX_WORDS)
343 sys_munmap_direct(tmp as *u8,FBB_MATRIX_WORDS*FBB_WORD)
344 return rc
345}
346
347func fbb_pose_models(b: *u8,n: i64,s: *FbxBindScene) -> i64 {
348 let big: i64 = fbxc_big(fbxc_version(b,n))
349 let h: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
350 let c: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
351 let d: *i64 = sys_mmap(FBXC_OUTN*FBB_WORD) as *i64
352 let matrix: *i64 = sys_mmap(FBB_MATRIX_WORDS*FBB_WORD) as *i64
353 var rc: i64 = 0
354 if fbb_find_child(b,n,FBXC_TOPOFF,n,big,"Objects" as *u8,h) != 1 { rc = FBB_BAD }
355 var p: i64 = h[FBXC_PROPSOFF]+h[FBXC_PLEN]
356 let end: i64 = h[FBXC_END]
357 while p < end && rc == 0 {
358 let node: i64 = fbb_node(b,n,p,end,big,h)
359 if node <= 0 { if node < 0 { rc = node } p = end }
360 else {
361 let next: i64 = h[FBXC_END]
362 if fbxc_name_is(b,h[FBXC_NAMEOFF],h[FBXC_NAMELEN],"Pose" as *u8) == 1 {
363 let child: i64 = h[FBXC_PROPSOFF]+h[FBXC_PLEN]
364 let hit: i64 = fbb_find_child(b,n,child,next,big,"Type" as *u8,c)
365 if hit < 0 { rc = hit }
366 if hit == 1 {
367 if fbb_string(b,c[FBXC_PROPSOFF],c[FBXC_PROPSOFF]+c[FBXC_PLEN],"BindPose" as *u8) == 1 {
368 var q: i64 = child
369 while q < next && rc == 0 {
370 let sub: i64 = fbb_node(b,n,q,next,big,c)
371 if sub <= 0 { if sub < 0 { rc = sub } q = next }
372 else {
373 let qe: i64 = c[FBXC_END]
374 if fbxc_name_is(b,c[FBXC_NAMEOFF],c[FBXC_NAMELEN],"PoseNode" as *u8) == 1 {
375 let qp: i64 = c[FBXC_PROPSOFF]+c[FBXC_PLEN]
376 if fbb_find_child(b,n,qp,qe,big,"Node" as *u8,d) != 1 { rc = FBB_BAD }
377 else {
378 let np: i64 = d[FBXC_PROPSOFF]
379 if fbb_property_size(b,np,np+d[FBXC_PLEN]) != 9 { rc = FBB_BAD }
380 else {
381 if b[np] != (76 as u8) { rc = FBB_BAD }
382 else {
383 let id: i64 = fbxc_i64le(b,np+1)
384 var i: i64 = 0
385 while i < s.count && rc == 0 {
386 let o: *FbxBindObject = fbb_object(s,i)
387 if o.id == id && o.kind == FBB_MODEL {
388 if fbb_find_child(b,n,qp,qe,big,"Matrix" as *u8,d) != 1 { rc = FBB_MISSING }
389 else {
390 rc = fbb_matrix(b,d[FBXC_PROPSOFF],d[FBXC_PROPSOFF]+d[FBXC_PLEN],matrix)
391 if rc == 0 {
392 if o.matrix == 0 {
393 o.matrix = sys_mmap(FBB_MATRIX_WORDS*FBB_WORD) as i64
394 let dest: *i64 = o.matrix as *i64
395 var j: i64 = 0
396 while j < FBB_MATRIX_WORDS { dest[j] = matrix[j]; j = j+1 }
397 } else {
398 let saved: *i64 = o.matrix as *i64
399 var j: i64 = 0
400 while j < FBB_MATRIX_WORDS {
401 if saved[j] != matrix[j] { rc = FBB_CONFLICT }
402 j = j+1
403 }
404 }
405 }
406 }
407 }
408 i = i+1
409 }
410 }
411 }
412 }
413 }
414 q = qe
415 }
416 }
417 }
418 }
419 }
420 p = next
421 }
422 }
423 sys_munmap(h as *u8,FBXC_OUTN*FBB_WORD)
424 sys_munmap(c as *u8,FBXC_OUTN*FBB_WORD)
425 sys_munmap(d as *u8,FBXC_OUTN*FBB_WORD)
426 sys_munmap(matrix as *u8,FBB_MATRIX_WORDS*FBB_WORD)
427 return rc
428}
429
430// Applies explicit BindPose model matrices to the ordered geometry stream.
431// Q20 matrix * fx1024 position returns fx1024; originals/output remain intact on error.
432func fbb_point(m: *i64,v: *i64,out: *i64) -> i64 {
433 var row: i64 = 0
434 while row < 3 {
435 // Translation is converted from Q20 to fx1024 before accumulation.
436 var sum: i64 = m[12+row]/(FBB_Q/FBB_VERTEX_Q)
437 var k: i64 = 0
438 while k < 3 {
439 let a: i64 = m[k*4+row]
440 let b: i64 = v[k]
441 if a < (0-FBB_I64_MAX) || b < (0-FBB_I64_MAX) { return FBB_RANGE }
442 var aa: i64 = a
443 var bb: i64 = b
444 if aa < 0 { aa=0-aa }
445 if bb < 0 { bb=0-bb }
446 if bb != 0 { if aa > FBB_I64_MAX/bb { return FBB_RANGE } }
447 let term: i64 = (a*b)/FBB_Q
448 if term > 0 { if sum > FBB_I64_MAX-term { return FBB_RANGE } }
449 if term < 0 { if sum < (0-FBB_I64_MAX)-term { return FBB_RANGE } }
450 sum=sum+term
451 k=k+1
452 }
453 out[row]=sum
454 row=row+1
455 }
456 return 0
457}
458func fbb_apply_pose(s: *FbxBindScene,input: *i64,nv: i64,out: *i64) -> i64 {
459 if nv <= 0 || nv > FBB_I64_MAX/(3*FBB_WORD) { return FBB_RANGE }
460 var total: i64 = 0
461 var i: i64 = 0
462 while i < s.count {
463 let g: *FbxBindObject = fbb_object(s,i)
464 if g.kind == FBB_GEOMETRY {
465 if g.count < 0 || g.count > nv-total { return FBB_BAD }
466 total=total+g.count
467 let mi: i64 = fbb_parent(s,g.id,FBB_MODEL)
468 if mi < 0 { return mi }
469 let model: *FbxBindObject = fbb_object(s,mi)
470 if model.matrix == 0 { return FBB_MISSING }
471 if model.geometric != 0 { return model.geometric }
472 }
473 i=i+1
474 }
475 if total != nv { return FBB_BAD }
476 let scratch: *i64 = sys_mmap(nv*3*FBB_WORD) as *i64
477 var base: i64 = 0
478 var rc: i64 = 0
479 i=0
480 while i < s.count && rc == 0 {
481 let g: *FbxBindObject = fbb_object(s,i)
482 if g.kind == FBB_GEOMETRY {
483 let mi: i64 = fbb_parent(s,g.id,FBB_MODEL)
484 let model: *FbxBindObject = fbb_object(s,mi)
485 var j: i64 = 0
486 while j < g.count && rc == 0 {
487 let off: i64 = (base+j)*3*FBB_WORD
488 rc=fbb_point(model.matrix as *i64,((input as i64)+off) as *i64,((scratch as i64)+off) as *i64)
489 j=j+1
490 }
491 base=base+g.count
492 }
493 i=i+1
494 }
495 if rc == 0 {
496 var j: i64 = 0
497 while j < nv*3 { out[j]=scratch[j]; j=j+1 }
498 }
499 sys_munmap(scratch as *u8,nv*3*FBB_WORD)
500 return rc
501}
502
503// Private qualification closure; this exact helper belongs in nx_syscalls before adoption.
504// Matching release for sys_mmap_try and other whole kernel mappings.
505// Never pass an arena allocation from sys_mmap: its small pointers may be interior.
506// Preserve the requested mapping length; the kernel applies its page rounding.
507const NXA_MAP_INVALID:i64=0-22 // Linux EINVAL, a protocol value rather than a resource budget.
508func sys_munmap_direct(addr:*u8,len:i64)->i64{
509 if (addr as i64)<=0||len<=0{return NXA_MAP_INVALID}
510 return __syscall(11,addr as i64,len,0,0,0,0)
511}