code wiki / _hdl_build / nx_gltf2mesh.nx
nx_gltf2mesh.nx source
↩ module page · 855 lines · 39933 B
1// nx_gltf2mesh.nx -- GLB/VRM -> NXMSH2 mesh-oracle extractor (debt 1785902685, the D2 POSITION slice).
2// The whole bench suite (nx_bodybench / nx_twinbench / nx_curvebench / nx_facemark) judges .nxmesh
3// oracles, but NO organ could turn a donor .glb/.vrm into one -- donor FORM was invisible to every
4// instrument we own (operator 2026-08-05: the T2 being is still a clay blob because the ruler only
5// reads skeletons). This organ reads ONLY what a form oracle needs: every TRIANGLES primitive's
6// POSITION + indices (u16 AND u32, tight or strided bufferViews), multi-mesh multi-primitive concat.
7// FAIL-CLOSED at every offset (D1): a file this cannot parse REFUSES with a named reason and a
8// nonzero exit -- never a segfault, never a silent empty mesh (zero accepted tris = REFUSAL).
9// Morph-target POSITIONs are excluded by scoping the attribute scan to the primitive's attributes{}.
10// Skinned bind-pose positions are model-space per the glTF spec (node transforms ignored for skinned
11// meshes); VRM avatars are skinned throughout -- reported in the status line, never silent.
12// nx_gltf2mesh <in.glb|.vrm> <out.nxmesh> | nx_gltf2mesh selftest
13// license_tier: ORIGINAL expect_exit: 0
14import "nx_syscalls.nx"
15const G2_MAGIC_2000000000: i64 = 2000000000
16const G2_MAGIC_2500: i64 = 2500
17const G2_MAGIC_500000: i64 = 500000
18const G2_MAGIC_2500000: i64 = 2500000
19const G2_MAGIC_4096: i64 = 4096
20
21const G2_CAP: i64 = 33554432
22const G2_MAXEL: i64 = 1024
23const G2_MAXTRI: i64 = 400000
24const G2_Q14: i64 = 16384
25const G2_M8388607: i64 = 8388607
26const G2_M8388608: i64 = 8388608
27const G2_GREY: i64 = 780
28const G2_CT_F32: i64 = 5126
29const G2_CT_U16: i64 = 5123
30const G2_CT_U32: i64 = 5125
31const G2_MODE_TRIS: i64 = 4
32const G2_HDR: i64 = 16
33const G2_LAYROW: i64 = 24
34const G2_TRIREC: i64 = 84
35
36func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
37func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
38
39// ---- bounds-checked little-endian readers (D1: refuse, never wander) ----
40static G2_LEN: i64
41static G2_ERR: i64
42func g2_u16(b: *u8, o: i64) -> i64 {
43 if o < 0 { G2_ERR = 1; return 0 }
44 if o + 2 > G2_LEN { G2_ERR = 1; return 0 }
45 return (b[o] as i64) + ((b[o+1] as i64)<<8)
46}
47func g2_u32(b: *u8, o: i64) -> i64 {
48 if o < 0 { G2_ERR = 1; return 0 }
49 if o + 4 > G2_LEN { G2_ERR = 1; return 0 }
50 return (b[o] as i64) + ((b[o+1] as i64)<<8) + ((b[o+2] as i64)<<16) + ((b[o+3] as i64)<<24)
51}
52// IEEE754 float32 bits -> integer millimetres (metres*1000), the nx_fbx_measure recipe
53func g2_f32mm(w: i64) -> i64 {
54 let sign: i64 = (w >> 31) & 1
55 let expo: i64 = (w >> 23) & 255
56 if expo == 0 { return 0 }
57 var mant: i64 = (w & G2_M8388607) | G2_M8388608
58 let sh: i64 = expo - 127
59 var v: i64 = 0
60 if sh >= 23 { if sh - 23 > 30 { G2_ERR = 1; return 0 } }
61 if sh >= 23 { v = mant * 1000 * (1 << (sh - 23)) }
62 if sh < 23 { if 23 - sh > 62 { return 0 } }
63 if sh < 23 { v = (mant * 1000) >> (23 - sh) }
64 if sign == 1 { return 0 - v }
65 return v
66}
67// integer/scale -> IEEE754 float32 bits (the nx_body_gen encoder)
68func g2_enc(v: i64, scale: i64) -> i64 {
69 if v == 0 { return 0 }
70 var neg: i64 = 0
71 var m: i64 = v
72 if m < 0 { neg = 1; m = 0-m }
73 var e: i64 = 0
74 var num: i64 = m
75 var den: i64 = scale
76 while num >= den*2 { den = den*2; e = e+1 }
77 while num < den { num = num*2; e = e-1 }
78 let frac: i64 = ((num - den)*G2_M8388608)/den
79 var bits: i64 = ((e+127) << 23) | (frac & G2_M8388607)
80 if neg == 1 { bits = bits | (1<<31) }
81 return bits
82}
83func g2_wr32(b: *u8, o: i64, v: i64) -> i64 {
84 b[o]=(v&255) as u8; b[o+1]=((v>>8)&255) as u8; b[o+2]=((v>>16)&255) as u8; b[o+3]=((v>>24)&255) as u8
85 return 0
86}
87func g2_isqrt(x: i64) -> i64 {
88 if x <= 0 { return 0 }
89 var r: i64 = x
90 var q: i64 = (x/2) + 1
91 while q < r { r = q; q = (r + x/r)/2 }
92 return r
93}
94
95// ---- string-aware JSON scanning (machine-emitted glb JSON; strings honoured, depth tracked) ----
96func g2_lit_at(b: *u8, o: i64, e: i64, lit: *u8) -> i64 {
97 var m: i64 = 0
98 while lit[m] != (0 as u8) { m = m + 1 }
99 if o + m > e { return 0 }
100 var i: i64 = 0
101 while i < m { if b[o+i] != lit[i] { return 0 } i = i + 1 }
102 return m
103}
104// find a top-level (depth 1) key `lit` (pass WITH quotes+colon e.g. "\x22meshes\x22:") in [s,e) -> offset AFTER the colon, or -1
105func g2_topkey(b: *u8, s: i64, e: i64, lit: *u8) -> i64 {
106 var i: i64 = s
107 var depth: i64 = 0
108 var instr: i64 = 0
109 while i < e {
110 let c: i64 = b[i] as i64
111 if instr == 1 {
112 if c == 92 { i = i + 1 }
113 if c == 34 { instr = 0 }
114 } else {
115 if c == 34 {
116 if depth == 1 {
117 let m: i64 = g2_lit_at(b, i, e, lit)
118 if m > 0 { return i + m }
119 }
120 instr = 1
121 }
122 if c == 123 { depth = depth + 1 }
123 if c == 91 { depth = depth + 1 }
124 if c == 125 { depth = depth - 1 }
125 if c == 93 { depth = depth - 1 }
126 }
127 i = i + 1
128 }
129 return 0 - 1
130}
131// given off at the '[' of an array, fill starts/ends with each top element object range. -> count (or -1 refuse)
132func g2_elems(b: *u8, arr: i64, e: i64, starts: *i64, ends: *i64, maxn: i64) -> i64 {
133 if arr >= e { return 0 - 1 }
134 if b[arr] != (91 as u8) { return 0 - 1 }
135 var i: i64 = arr + 1
136 var depth: i64 = 1
137 var instr: i64 = 0
138 var cur: i64 = 0 - 1
139 var n: i64 = 0
140 while i < e {
141 let c: i64 = b[i] as i64
142 if instr == 1 {
143 if c == 92 { i = i + 1 }
144 if c == 34 { instr = 0 }
145 } else {
146 if c == 34 { instr = 1 }
147 if c == 123 { if depth == 1 { cur = i } depth = depth + 1 }
148 if c == 91 { depth = depth + 1 }
149 if c == 125 {
150 depth = depth - 1
151 if depth == 1 {
152 if cur >= 0 {
153 if n >= maxn { return 0 - 2 }
154 starts[n] = cur
155 ends[n] = i + 1
156 n = n + 1
157 cur = 0 - 1
158 }
159 }
160 }
161 if c == 93 {
162 depth = depth - 1
163 if depth == 0 { return n }
164 }
165 }
166 i = i + 1
167 }
168 return 0 - 1
169}
170// find `lit` (WITH quotes+colon) inside [s,e) at any depth, string-aware -> offset AFTER colon, or -1
171func g2_key(b: *u8, s: i64, e: i64, lit: *u8) -> i64 {
172 var i: i64 = s
173 var instr: i64 = 0
174 while i < e {
175 let c: i64 = b[i] as i64
176 if instr == 1 {
177 if c == 92 { i = i + 1 }
178 if c == 34 { instr = 0 }
179 } else {
180 if c == 34 {
181 let m: i64 = g2_lit_at(b, i, e, lit)
182 if m > 0 { return i + m }
183 instr = 1
184 }
185 }
186 i = i + 1
187 }
188 return 0 - 1
189}
190func g2_ifield(b: *u8, s: i64, e: i64, lit: *u8, def: i64) -> i64 {
191 let o: i64 = g2_key(b, s, e, lit)
192 if o < 0 { return def }
193 var i: i64 = o
194 var sk: i64 = 1
195 while sk == 1 {
196 if i >= e { sk = 0 } else { if b[i] == (32 as u8) { i = i + 1 } else { sk = 0 } }
197 }
198 var v: i64 = 0
199 var any: i64 = 0
200 var go: i64 = 1
201 while go == 1 {
202 if i >= e { go = 0 } else {
203 let c: i64 = b[i] as i64
204 if c >= 48 { if c <= 57 { v = v*10 + (c-48); any = 1; i = i + 1 } else { go = 0 } } else { go = 0 }
205 }
206 }
207 if any == 0 { return def }
208 return v
209}
210// object range starting at the '{' found at/after off -> end offset (past '}'), or -1
211func g2_objend(b: *u8, off: i64, e: i64) -> i64 {
212 var i: i64 = off
213 var found: i64 = 0 - 1
214 var sk: i64 = 1
215 while sk == 1 {
216 if i >= e { sk = 0 } else { if b[i] == (123 as u8) { found = i; sk = 0 } else { i = i + 1 } }
217 }
218 if found < 0 { return 0 - 1 }
219 i = found
220 var depth: i64 = 0
221 var instr: i64 = 0
222 while i < e {
223 let c: i64 = b[i] as i64
224 if instr == 1 {
225 if c == 92 { i = i + 1 }
226 if c == 34 { instr = 0 }
227 } else {
228 if c == 34 { instr = 1 }
229 if c == 123 { depth = depth + 1 }
230 if c == 125 { depth = depth - 1 }
231 if depth == 0 { return i + 1 }
232 }
233 i = i + 1
234 }
235 return 0 - 1
236}
237func g2_hasstr(b: *u8, s: i64, e: i64, lit: *u8) -> i64 {
238 var i: i64 = s
239 while i < e { if g2_lit_at(b, i, e, lit) > 0 { return 1 } i = i + 1 }
240 return 0
241}
242
243func g2_refuse(reason: *u8) -> i64 {
244 hw("GLTF2MESH REFUSED: " as *u8)
245 hw(reason)
246 hw("\n" as *u8)
247 return 0
248}
249
250// does the mesh object's "name" string contain `skip`? (byte substring on the name range)
251func g2_name_has(b: *u8, s: i64, e: i64, skip: *u8) -> i64 {
252 let no: i64 = g2_key(b, s, e, "\x22name\x22:" as *u8)
253 if no < 0 { return 0 }
254 var i: i64 = no
255 var sk: i64 = 1
256 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (34 as u8) { sk = 0 } else { i = i + 1 } } }
257 if i >= e { return 0 }
258 var j: i64 = i + 1
259 var ne: i64 = 0 - 1
260 sk = 1
261 while sk == 1 { if j >= e { sk = 0 } else { if b[j] == (34 as u8) { ne = j; sk = 0 } else { j = j + 1 } } }
262 if ne < 0 { return 0 }
263 return g2_hasstr(b, i + 1, ne, skip)
264}
265
266// filter arg classifier (R2 apparatus separation, debt 1785935557): each of up to two filter args is
267// "report" (print per-prim index/tris/bbox, v0-sampled) | "skipprims=i,j,k" (drop prims by REPORTED
268// global index) | any other string = skip meshes whose name contains it.
269static G2_REP: i64
270static G2_SKL: i64
271static G2_SKN: i64
272func g2_filter(f: *u8) -> i64 {
273 if (f as i64) == 0 { return 0 }
274 if G2_SKL == 0 { G2_SKL = sys_mmap(64*8) as i64 }
275 if g2_lit_at(f, 0, 6, "report" as *u8) == 6 { G2_REP = 1; return 0 }
276 if g2_lit_at(f, 0, 10, "skipprims=" as *u8) == 10 {
277 let sl: *i64 = G2_SKL as *i64
278 var sp: i64 = 10
279 var go2: i64 = 1
280 while go2 == 1 {
281 var v: i64 = 0
282 var any2: i64 = 0
283 var going: i64 = 1
284 while going == 1 {
285 let c2: i64 = f[sp] as i64
286 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); any2 = 1; sp = sp + 1 } else { going = 0 } } else { going = 0 }
287 }
288 if any2 == 1 { if G2_SKN < 64 { sl[G2_SKN] = v; G2_SKN = G2_SKN + 1 } }
289 if f[sp] == (44 as u8) { sp = sp + 1 } else { go2 = 0 }
290 }
291 return 0
292 }
293 return 1
294}
295
296// parse a JSON int array at `arr` (pointing at '[') into out[]; returns count or -1
297func g2_intarray(b: *u8, arr: i64, e: i64, out: *i64, maxn: i64) -> i64 {
298 if arr >= e { return 0 - 1 }
299 var i: i64 = arr
300 var sk: i64 = 1
301 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (91 as u8) { sk = 0 } else { i = i + 1 } } }
302 if i >= e { return 0 - 1 }
303 i = i + 1
304 var n: i64 = 0
305 var go: i64 = 1
306 while go == 1 {
307 if i >= e { go = 0 } else {
308 let c: i64 = b[i] as i64
309 if c == 93 { go = 0 } else {
310 if c >= 48 { if c <= 57 {
311 var v: i64 = 0
312 var d: i64 = 1
313 while d == 1 {
314 let c2: i64 = b[i] as i64
315 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); i = i + 1 } else { d = 0 } } else { d = 0 }
316 if i >= e { d = 0 }
317 }
318 if n >= maxn { return 0 - 2 }
319 out[n] = v
320 n = n + 1
321 } else { i = i + 1 } } else { i = i + 1 }
322 }
323 }
324 }
325 return n
326}
327// find the "name" string range of a node object; returns 1 and fills s/e via the out pair
328func g2_nodename(b: *u8, s: i64, e: i64, out: *i64) -> i64 {
329 let no: i64 = g2_key(b, s, e, "\x22name\x22:" as *u8)
330 out[0] = 0
331 out[1] = 0
332 if no < 0 { return 0 }
333 var i: i64 = no
334 var sk: i64 = 1
335 while sk == 1 { if i >= e { sk = 0 } else { if b[i] == (34 as u8) { sk = 0 } else { i = i + 1 } } }
336 if i >= e { return 0 }
337 var j: i64 = i + 1
338 var f: i64 = 0
339 while f == 0 { if j >= e { f = 1 } else { if b[j] == (34 as u8) { f = 1 } else { j = j + 1 } } }
340 if j >= e { return 0 }
341 out[0] = i + 1
342 out[1] = j
343 return 1
344}
345
346// ---- the conversion ----
347func g2_convert(inp: *u8, outp: *u8, skip: *u8) -> i64 {
348 G2_ERR = 0
349 let fd: i64 = sys_openat_rd(inp)
350 if fd < 0 { g2_refuse("input unreadable" as *u8); return 3 }
351 let b: *u8 = sys_mmap(G2_CAP + 64)
352 var n: i64 = 0
353 var go: i64 = 1
354 while go == 1 {
355 let r: i64 = sys_read(fd, ((b as i64) + n) as *u8, G2_CAP - n)
356 if r <= 0 { go = 0 } else { n = n + r }
357 if n >= G2_CAP { go = 0 }
358 }
359 sys_close(fd)
360 if n >= G2_CAP { g2_refuse("input exceeds 32MiB cap" as *u8); return 3 }
361 if n < 28 { g2_refuse("too small for a glb" as *u8); return 3 }
362 G2_LEN = n
363 if b[0] != (103 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 }
364 if b[1] != (108 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 }
365 if b[2] != (84 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 }
366 if b[3] != (70 as u8) { g2_refuse("bad magic (not glTF)" as *u8); return 3 }
367 let total: i64 = g2_u32(b, 8)
368 if total != n { g2_refuse("length field disagrees with file size" as *u8); return 3 }
369 let jlen: i64 = g2_u32(b, 12)
370 if 20 + jlen > n { g2_refuse("JSON chunk overruns file" as *u8); return 3 }
371 let js: i64 = 20
372 let je: i64 = 20 + jlen
373 var bino: i64 = 0 - 1
374 var binn: i64 = 0
375 if je + 8 <= n {
376 binn = g2_u32(b, je)
377 if b[je+4] != (66 as u8) { g2_refuse("second chunk is not BIN" as *u8); return 3 }
378 bino = je + 8
379 if bino + binn > n {
380 g2_refuse("BIN chunk overruns file" as *u8)
381 hw(" bino=" as *u8); pn(bino); hw(" binn=" as *u8); pn(binn); hw(" n=" as *u8); pn(n); hw(" jlen=" as *u8); pn(jlen); hw("\n" as *u8)
382 return 3
383 }
384 }
385 if bino < 0 { g2_refuse("no BIN chunk (external buffers unsupported)" as *u8); return 3 }
386 if G2_ERR != 0 { g2_refuse("header read out of bounds" as *u8); return 3 }
387
388 // top-level tables
389 let accA: i64 = g2_topkey(b, js, je, "\x22accessors\x22:" as *u8)
390 let bvA: i64 = g2_topkey(b, js, je, "\x22bufferViews\x22:" as *u8)
391 let meA: i64 = g2_topkey(b, js, je, "\x22meshes\x22:" as *u8)
392 if accA < 0 { g2_refuse("no accessors array" as *u8); return 4 }
393 if bvA < 0 { g2_refuse("no bufferViews array" as *u8); return 4 }
394 if meA < 0 { g2_refuse("no meshes array" as *u8); return 4 }
395 let aS: *i64 = sys_mmap(G2_MAXEL*8) as *i64
396 let aE: *i64 = sys_mmap(G2_MAXEL*8) as *i64
397 let na: i64 = g2_elems(b, accA, je, aS, aE, G2_MAXEL)
398 if na <= 0 { g2_refuse("accessors array unparseable or empty" as *u8); return 4 }
399 let vS: *i64 = sys_mmap(G2_MAXEL*8) as *i64
400 let vE: *i64 = sys_mmap(G2_MAXEL*8) as *i64
401 let nv: i64 = g2_elems(b, bvA, je, vS, vE, G2_MAXEL)
402 if nv <= 0 { g2_refuse("bufferViews array unparseable or empty" as *u8); return 4 }
403 let mS: *i64 = sys_mmap(G2_MAXEL*8) as *i64
404 let mE: *i64 = sys_mmap(G2_MAXEL*8) as *i64
405 let nm: i64 = g2_elems(b, meA, je, mS, mE, G2_MAXEL)
406 if nm <= 0 { g2_refuse("meshes array unparseable or empty" as *u8); return 4 }
407
408 // accessor + bufferView flat tables
409 let accBV: *i64 = sys_mmap(na*8) as *i64
410 let accBO: *i64 = sys_mmap(na*8) as *i64
411 let accCT: *i64 = sys_mmap(na*8) as *i64
412 let accN: *i64 = sys_mmap(na*8) as *i64
413 let accV3: *i64 = sys_mmap(na*8) as *i64
414 var i: i64 = 0
415 while i < na {
416 accBV[i] = g2_ifield(b, aS[i], aE[i], "\x22bufferView\x22:" as *u8, 0 - 1)
417 accBO[i] = g2_ifield(b, aS[i], aE[i], "\x22byteOffset\x22:" as *u8, 0)
418 accCT[i] = g2_ifield(b, aS[i], aE[i], "\x22componentType\x22:" as *u8, 0)
419 accN[i] = g2_ifield(b, aS[i], aE[i], "\x22count\x22:" as *u8, 0)
420 accV3[i] = g2_hasstr(b, aS[i], aE[i], "VEC3" as *u8)
421 i = i + 1
422 }
423 let bvBO: *i64 = sys_mmap(nv*8) as *i64
424 let bvLN: *i64 = sys_mmap(nv*8) as *i64
425 let bvST: *i64 = sys_mmap(nv*8) as *i64
426 i = 0
427 while i < nv {
428 bvBO[i] = g2_ifield(b, vS[i], vE[i], "\x22byteOffset\x22:" as *u8, 0)
429 bvLN[i] = g2_ifield(b, vS[i], vE[i], "\x22byteLength\x22:" as *u8, 0)
430 bvST[i] = g2_ifield(b, vS[i], vE[i], "\x22byteStride\x22:" as *u8, 0)
431 i = i + 1
432 }
433
434 // ---- L2 NAMED PARTS (semantic ladder 1785954089): the donor ALREADY CARRIES its own segmentation
435 // in the skin. JOINTS_0 indexes skins[].joints[], which indexes nodes[], which carry NAMES. So a
436 // rigged donor segments itself for free -- no clustering heuristic, no invented vocabulary, the
437 // parts are named by whoever built the asset.
438 let jn: *i64 = sys_mmap(G2_MAXEL*8) as *i64
439 var njoint: i64 = 0
440 let nn0: *i64 = sys_mmap(G2_MAXEL*8) as *i64
441 let nn1: *i64 = sys_mmap(G2_MAXEL*8) as *i64
442 var nnodes: i64 = 0
443 // ★A MULTI-MESH DONOR HAS ONE SKIN PER MESH: taking skins[0] labelled all 34k triangles with the
444 // HAIR rig (measured 2026-08-05). The skin that owns a primitive is the one on the NODE that
445 // instantiates its mesh, so resolve mesh -> node -> skin, and label by NODE INDEX (globally unique)
446 // rather than by a skin-local joint index (which means a different bone in every skin).
447 let skJ0: *i64 = sys_mmap(G2_MAXEL*8) as *i64
448 let skJN: *i64 = sys_mmap(G2_MAXEL*8) as *i64
449 var nskin: i64 = 0
450 let skA: i64 = g2_topkey(b, js, je, "\x22skins\x22:" as *u8)
451 if skA >= 0 {
452 let sS: *i64 = sys_mmap(G2_MAXEL*8) as *i64
453 let sE: *i64 = sys_mmap(G2_MAXEL*8) as *i64
454 let ns: i64 = g2_elems(b, skA, je, sS, sE, G2_MAXEL)
455 var si: i64 = 0
456 while si < ns {
457 let jA: i64 = g2_key(b, sS[si], sE[si], "\x22joints\x22:" as *u8)
458 skJ0[si] = njoint
459 skJN[si] = 0
460 if jA >= 0 {
461 let r: i64 = g2_intarray(b, jA, sE[si], ((jn as i64) + njoint*8) as *i64, G2_MAXEL - njoint)
462 if r > 0 { skJN[si] = r; njoint = njoint + r }
463 }
464 si = si + 1
465 }
466 nskin = ns
467 }
468 let mskin: *i64 = sys_mmap(G2_MAXEL*8) as *i64
469 var q4: i64 = 0
470 while q4 < G2_MAXEL { mskin[q4] = 0 - 1; q4 = q4 + 1 }
471 let ndA: i64 = g2_topkey(b, js, je, "\x22nodes\x22:" as *u8)
472 if ndA >= 0 {
473 let dS: *i64 = sys_mmap(G2_MAXEL*8) as *i64
474 let dE: *i64 = sys_mmap(G2_MAXEL*8) as *i64
475 let nd: i64 = g2_elems(b, ndA, je, dS, dE, G2_MAXEL)
476 let pr: *i64 = sys_mmap(16) as *i64
477 var q3: i64 = 0
478 while q3 < nd {
479 g2_nodename(b, dS[q3], dE[q3], pr)
480 nn0[q3] = pr[0]
481 nn1[q3] = pr[1]
482 let mh: i64 = g2_ifield(b, dS[q3], dE[q3], "\x22mesh\x22:" as *u8, 0 - 1)
483 let sk2: i64 = g2_ifield(b, dS[q3], dE[q3], "\x22skin\x22:" as *u8, 0 - 1)
484 if mh >= 0 { if mh < G2_MAXEL { if sk2 >= 0 { mskin[mh] = sk2 } } }
485 q3 = q3 + 1
486 }
487 nnodes = nd
488 }
489 let trij: *i64 = sys_mmap(G2_MAXTRI*8 + 64) as *i64
490
491 // output buffer + mm staging (positions are held as integer mm and unit-normalized BEFORE encoding:
492 // the NXMSH2 ecosystem convention is f32 MILLIMETRES -- raw meter-scale VRM floats decode to a
493 // 1.6mm sliver that renders as ~2px and collapses every IoU judge; measured 2026-08-05)
494 let hdr: i64 = G2_HDR + G2_LAYROW
495 let ob: *u8 = sys_mmap(hdr + G2_MAXTRI*G2_TRIREC + G2_MAXTRI*4 + 64)
496 let stg: *i64 = sys_mmap(G2_MAXTRI*12*8 + 64) as *i64
497 var nt: i64 = 0
498 var prims: i64 = 0
499 var accepted: i64 = 0
500 var skipped: i64 = 0
501 var degen: i64 = 0
502 var trunc: i64 = 0
503 var vseen: i64 = 0
504 var miny: i64 = G2_MAGIC_2000000000
505 var maxy: i64 = 0 - G2_MAGIC_2000000000
506
507 let pS: *i64 = sys_mmap(G2_MAXEL*8) as *i64
508 let pE: *i64 = sys_mmap(G2_MAXEL*8) as *i64
509 var mi: i64 = 0
510 var meshes_skipped: i64 = 0
511 while mi < nm {
512 var mesh_ok: i64 = 1
513 if (skip as i64) != 0 { if g2_name_has(b, mS[mi], mE[mi], skip) == 1 { mesh_ok = 0; meshes_skipped = meshes_skipped + 1 } }
514 var prA: i64 = 0 - 1
515 if mesh_ok == 1 { prA = g2_key(b, mS[mi], mE[mi], "\x22primitives\x22:" as *u8) }
516 if prA >= 0 {
517 let np: i64 = g2_elems(b, prA, mE[mi], pS, pE, G2_MAXEL)
518 var pi: i64 = 0
519 while pi < np {
520 prims = prims + 1
521 var ok: i64 = 1
522 let mode: i64 = g2_ifield(b, pS[pi], pE[pi], "\x22mode\x22:" as *u8, G2_MODE_TRIS)
523 if mode != G2_MODE_TRIS { ok = 0 }
524 let atA: i64 = g2_key(b, pS[pi], pE[pi], "\x22attributes\x22:" as *u8)
525 var posi: i64 = 0 - 1
526 if atA < 0 { ok = 0 }
527 if ok == 1 {
528 let atE2: i64 = g2_objend(b, atA, pE[pi])
529 if atE2 < 0 { ok = 0 } else { posi = g2_ifield(b, atA, atE2, "\x22POSITION\x22:" as *u8, 0 - 1) }
530 }
531 var joi: i64 = 0 - 1
532 if ok == 1 { if atA >= 0 { let atE3: i64 = g2_objend(b, atA, pE[pi]); if atE3 > 0 { joi = g2_ifield(b, atA, atE3, "\x22JOINTS_0\x22:" as *u8, 0 - 1) } } }
533 let idxi: i64 = g2_ifield(b, pS[pi], pE[pi], "\x22indices\x22:" as *u8, 0 - 1)
534 if posi < 0 { ok = 0 }
535 if idxi < 0 { ok = 0 }
536 if posi >= na { ok = 0 }
537 if idxi >= na { ok = 0 }
538 if ok == 1 { if accCT[posi] != G2_CT_F32 { ok = 0 } }
539 if ok == 1 { if accV3[posi] != 1 { ok = 0 } }
540 if ok == 1 { if accCT[idxi] != G2_CT_U16 { if accCT[idxi] != G2_CT_U32 { ok = 0 } } }
541 if ok == 1 { if accN[idxi] % 3 != 0 { ok = 0 } }
542 if ok == 1 { if accBV[posi] < 0 { ok = 0 } }
543 if ok == 1 { if accBV[idxi] < 0 { ok = 0 } }
544 if ok == 1 { if accBV[posi] >= nv { ok = 0 } }
545 if ok == 1 { if accBV[idxi] >= nv { ok = 0 } }
546 if ok == 1 { if G2_SKN > 0 { let sl2: *i64 = G2_SKL as *i64; var si2: i64 = 0; while si2 < G2_SKN { if sl2[si2] == prims - 1 { ok = 0 } si2 = si2 + 1 } } }
547 if ok == 0 { skipped = skipped + 1 } else {
548 let pb: i64 = accBV[posi]
549 let ib2: i64 = accBV[idxi]
550 var pstride: i64 = bvST[pb]
551 if pstride == 0 { pstride = 12 }
552 let pbase: i64 = bino + bvBO[pb] + accBO[posi]
553 let ibase: i64 = bino + bvBO[ib2] + accBO[idxi]
554 var isz: i64 = 2
555 if accCT[idxi] == G2_CT_U32 { isz = 4 }
556 let pcount: i64 = accN[posi]
557 vseen = vseen + pcount
558 let ntri: i64 = accN[idxi] / 3
559 var pmnx: i64 = G2_MAGIC_2000000000
560 var pmxx: i64 = 0 - G2_MAGIC_2000000000
561 var pmny: i64 = G2_MAGIC_2000000000
562 var pmxy: i64 = 0 - G2_MAGIC_2000000000
563 var pmnz: i64 = G2_MAGIC_2000000000
564 var pmxz: i64 = 0 - G2_MAGIC_2000000000
565 var t: i64 = 0
566 while t < ntri {
567 if nt >= G2_MAXTRI { trunc = 1; t = t + ntri } else {
568 var i0: i64 = 0
569 var i1: i64 = 0
570 var i2: i64 = 0
571 if isz == 2 { i0 = g2_u16(b, ibase + (t*3)*2); i1 = g2_u16(b, ibase + (t*3+1)*2); i2 = g2_u16(b, ibase + (t*3+2)*2) }
572 if isz == 4 { i0 = g2_u32(b, ibase + (t*3)*4); i1 = g2_u32(b, ibase + (t*3+1)*4); i2 = g2_u32(b, ibase + (t*3+2)*4) }
573 var okv: i64 = 1
574 if i0 >= pcount { okv = 0 }
575 if i1 >= pcount { okv = 0 }
576 if i2 >= pcount { okv = 0 }
577 if G2_ERR != 0 { okv = 0 }
578 if okv == 1 {
579 let w00: i64 = g2_u32(b, pbase + i0*pstride)
580 let w01: i64 = g2_u32(b, pbase + i0*pstride + 4)
581 let w02: i64 = g2_u32(b, pbase + i0*pstride + 8)
582 let w10: i64 = g2_u32(b, pbase + i1*pstride)
583 let w11: i64 = g2_u32(b, pbase + i1*pstride + 4)
584 let w12: i64 = g2_u32(b, pbase + i1*pstride + 8)
585 let w20: i64 = g2_u32(b, pbase + i2*pstride)
586 let w21: i64 = g2_u32(b, pbase + i2*pstride + 4)
587 let w22: i64 = g2_u32(b, pbase + i2*pstride + 8)
588 if G2_ERR != 0 { okv = 0 } else {
589 let x0: i64 = g2_f32mm(w00)
590 let y0: i64 = g2_f32mm(w01)
591 let z0: i64 = g2_f32mm(w02)
592 let x1: i64 = g2_f32mm(w10)
593 let y1: i64 = g2_f32mm(w11)
594 let z1: i64 = g2_f32mm(w12)
595 let x2: i64 = g2_f32mm(w20)
596 let y2: i64 = g2_f32mm(w21)
597 let z2: i64 = g2_f32mm(w22)
598 if y0 < miny { miny = y0 }
599 if y0 > maxy { maxy = y0 }
600 if y1 < miny { miny = y1 }
601 if y1 > maxy { maxy = y1 }
602 if y2 < miny { miny = y2 }
603 if y2 > maxy { maxy = y2 }
604 if x0 < pmnx { pmnx = x0 }
605 if x0 > pmxx { pmxx = x0 }
606 if y0 < pmny { pmny = y0 }
607 if y0 > pmxy { pmxy = y0 }
608 if z0 < pmnz { pmnz = z0 }
609 if z0 > pmxz { pmxz = z0 }
610 let e1x: i64 = x1-x0
611 let e1y: i64 = y1-y0
612 let e1z: i64 = z1-z0
613 let e2x: i64 = x2-x0
614 let e2y: i64 = y2-y0
615 let e2z: i64 = z2-z0
616 var cx: i64 = e1y*e2z - e1z*e2y
617 var cy: i64 = e1z*e2x - e1x*e2z
618 var cz: i64 = e1x*e2y - e1y*e2x
619 let mag: i64 = g2_isqrt(cx*cx + cy*cy + cz*cz)
620 if mag == 0 { degen = degen + 1 } else {
621 let so: i64 = nt*12
622 stg[so]=x0; stg[so+1]=y0; stg[so+2]=z0
623 stg[so+3]=x1; stg[so+4]=y1; stg[so+5]=z1
624 stg[so+6]=x2; stg[so+7]=y2; stg[so+8]=z2
625 // part label = the joint bound to vertex 0 (rigid-dominant proxy;
626 // full weight-max over 3 verts is the named refinement)
627 var lab: i64 = 0 - 1
628 if joi >= 0 { if joi < na { if accBV[joi] >= 0 { if accBV[joi] < nv {
629 var jstride: i64 = bvST[accBV[joi]]
630 var jsz: i64 = 1
631 if accCT[joi] == G2_CT_U16 { jsz = 2 }
632 if jstride == 0 { jstride = jsz*4 }
633 let jbase: i64 = bino + bvBO[accBV[joi]] + accBO[joi]
634 if jsz == 1 { lab = b[jbase + i0*jstride] as i64 }
635 if jsz == 2 { lab = g2_u16(b, jbase + i0*jstride) }
636 // skin-local joint -> NODE index, via the skin that owns THIS mesh
637 var sk3: i64 = 0 - 1
638 if mi < G2_MAXEL { sk3 = mskin[mi] }
639 if sk3 >= 0 { if sk3 < nskin { if lab >= 0 { if lab < skJN[sk3] { lab = jn[skJ0[sk3] + lab] } else { lab = 0 - 1 } } } } else { lab = 0 - 1 }
640 } } } }
641 trij[nt] = lab
642 stg[so+9] = cx*G2_Q14/mag
643 stg[so+10] = cy*G2_Q14/mag
644 stg[so+11] = cz*G2_Q14/mag
645 nt = nt + 1
646 }
647 }
648 }
649 t = t + 1
650 }
651 }
652 if G2_REP == 1 { hw("PRIM " as *u8); pn(prims - 1); hw(" mesh " as *u8); pn(mi); hw(" tris " as *u8); pn(ntri); hw(" x " as *u8); pn(pmnx); hw(".." as *u8); pn(pmxx); hw(" y " as *u8); pn(pmny); hw(".." as *u8); pn(pmxy); hw(" z " as *u8); pn(pmnz); hw(".." as *u8); pn(pmxz); hw("\n" as *u8) }
653 accepted = accepted + 1
654 }
655 pi = pi + 1
656 }
657 }
658 mi = mi + 1
659 }
660
661 if nt == 0 { g2_refuse("zero triangles accepted -- a form oracle with no surface is vacuous" as *u8); return 5 }
662 if trunc == 1 { g2_refuse("tri cap hit -- a truncated oracle is a lying ruler (raise G2_MAXTRI deliberately)" as *u8); return 7 }
663 if G2_ERR != 0 { g2_refuse("out-of-bounds read during extraction -- file declares data it does not carry" as *u8); return 7 }
664 // unit normalization: land the body in plausible human millimetres or REFUSE (never emit a sliver oracle)
665 let hgt: i64 = maxy - miny
666 var dv: i64 = 0
667 if hgt >= 500 { if hgt <= G2_MAGIC_2500 { dv = 1 } }
668 if hgt >= G2_MAGIC_500000 { if hgt <= G2_MAGIC_2500000 { dv = 1000 } }
669 if dv == 0 { g2_refuse("bbox height lands outside 500-2500mm at every power-of-1000 scale -- refusing to guess units" as *u8); hw(" height_raw=" as *u8); pn(hgt); hw("\n" as *u8); return 8 }
670 // ---- layer plan: one layer per USED joint, contiguous by construction (NXMSH2 requires ranges).
671 // A donor with no skin emits the single "skin" layer exactly as before (additive, never a regression).
672 var nlay: i64 = 1
673 let lj: *i64 = sys_mmap(G2_MAXEL*8) as *i64
674 let lcnt: *i64 = sys_mmap(G2_MAXEL*8) as *i64
675 if njoint > 0 {
676 var u: i64 = 0
677 while u < G2_MAXEL { lcnt[u] = 0; u = u + 1 }
678 var t3: i64 = 0
679 while t3 < nt { let L: i64 = trij[t3]; if L >= 0 { if L < G2_MAXEL { lcnt[L] = lcnt[L] + 1 } } t3 = t3 + 1 }
680 nlay = 0
681 u = 0
682 while u < G2_MAXEL { if lcnt[u] > 0 { lj[nlay] = u; nlay = nlay + 1 } u = u + 1 }
683 if nlay == 0 { nlay = 1 }
684 }
685 let hdr2: i64 = G2_HDR + nlay*G2_LAYROW
686 let gc: i64 = g2_enc(G2_GREY, 1000)
687 var oc: i64 = 0
688 var Lx: i64 = 0
689 while Lx < nlay {
690 var wt: i64 = 0
691 while wt < nt {
692 var take: i64 = 1
693 if nlay > 1 { take = 0; if trij[wt] == lj[Lx] { take = 1 } }
694 if take == 1 {
695 let so2: i64 = wt*12
696 let o: i64 = hdr2 + oc*G2_TRIREC
697 var pc: i64 = 0
698 while pc < 9 { g2_wr32(ob, o + pc*4, g2_enc(stg[so2+pc]/dv, 1)); pc = pc + 1 }
699 let nb: i64 = g2_enc(stg[so2+9], G2_Q14)
700 let nb2: i64 = g2_enc(stg[so2+10], G2_Q14)
701 let nb3: i64 = g2_enc(stg[so2+11], G2_Q14)
702 var q: i64 = 0
703 while q < 3 { g2_wr32(ob, o+36+q*12, nb); g2_wr32(ob, o+40+q*12, nb2); g2_wr32(ob, o+44+q*12, nb3); q = q + 1 }
704 g2_wr32(ob, o+72, gc); g2_wr32(ob, o+76, gc); g2_wr32(ob, o+80, gc)
705 oc = oc + 1
706 }
707 wt = wt + 1
708 }
709 Lx = Lx + 1
710 }
711 miny = miny/dv
712 maxy = maxy/dv
713 // header + single layer "skin" + per-tri layer ids
714 ob[0]=78 as u8; ob[1]=88 as u8; ob[2]=77 as u8; ob[3]=83 as u8
715 ob[4]=72 as u8; ob[5]=50 as u8; ob[6]=0 as u8; ob[7]=0 as u8
716 g2_wr32(ob, 8, nlay); g2_wr32(ob, 12, nt)
717 // layer table: 16-byte name from the donor's own node name, then offset+count
718 var run: i64 = 0
719 var Ly: i64 = 0
720 while Ly < nlay {
721 let lb: i64 = G2_HDR + Ly*G2_LAYROW
722 var q2: i64 = 0
723 while q2 < 16 { ob[lb+q2] = 0 as u8; q2 = q2 + 1 }
724 var cnt2: i64 = nt
725 if nlay > 1 {
726 let jidx: i64 = lj[Ly]
727 cnt2 = lcnt[jidx]
728 let nd2: i64 = jidx
729 if nd2 >= 0 { if nd2 < nnodes { if nn1[nd2] > nn0[nd2] {
730 var w2: i64 = 0
731 while w2 < 15 { if nn0[nd2]+w2 < nn1[nd2] { ob[lb+w2] = b[nn0[nd2]+w2] } w2 = w2 + 1 }
732 } } }
733 if ob[lb] == (0 as u8) { ob[lb]=106 as u8; ob[lb+1]=111 as u8; ob[lb+2]=105 as u8; ob[lb+3]=110 as u8; ob[lb+4]=116 as u8 }
734 } else { ob[lb]=115 as u8; ob[lb+1]=107 as u8; ob[lb+2]=105 as u8; ob[lb+3]=110 as u8 }
735 g2_wr32(ob, lb+16, run); g2_wr32(ob, lb+20, cnt2)
736 run = run + cnt2
737 Ly = Ly + 1
738 }
739 var z: i64 = 0
740 var Lz: i64 = 0
741 var zc: i64 = 0
742 while Lz < nlay {
743 var cz: i64 = nt
744 if nlay > 1 { cz = lcnt[lj[Lz]] }
745 var k2: i64 = 0
746 while k2 < cz { g2_wr32(ob, hdr2 + nt*G2_TRIREC + zc*4, Lz); zc = zc + 1; k2 = k2 + 1 }
747 Lz = Lz + 1
748 }
749 let bytes: i64 = hdr2 + nt*G2_TRIREC + nt*4
750 let ofd: i64 = sys_openat_wr(outp, 420)
751 if ofd < 0 { g2_refuse("output path unwritable" as *u8); return 6 }
752 sys_write(ofd, ob, bytes)
753 sys_close(ofd)
754
755 hw("{\x22organ\x22:\x22nx_gltf2mesh\x22,\x22prims\x22:" as *u8); pn(prims)
756 hw(",\x22prims_accepted\x22:" as *u8); pn(accepted)
757 hw(",\x22prims_skipped\x22:" as *u8); pn(skipped)
758 hw(",\x22verts_seen\x22:" as *u8); pn(vseen)
759 hw(",\x22tris\x22:" as *u8); pn(nt)
760 hw(",\x22degenerate_dropped\x22:" as *u8); pn(degen)
761 hw(",\x22meshes_skipped_by_name\x22:" as *u8); pn(meshes_skipped)
762 hw(",\x22skin_joints\x22:" as *u8); pn(njoint)
763 hw(",\x22named_parts_emitted\x22:" as *u8); pn(nlay)
764 hw(",\x22miny_mm\x22:" as *u8); pn(miny)
765 hw(",\x22maxy_mm\x22:" as *u8); pn(maxy)
766 hw(",\x22scale_div\x22:" as *u8); pn(dv)
767 hw(",\x22bytes\x22:" as *u8); pn(bytes)
768 hw(",\x22note\x22:\x22bind-pose model space; node transforms not applied (skinned donors); morph-target POSITIONs excluded by attributes-scope\x22}" as *u8)
769 hw("\n" as *u8)
770 return 0
771}
772
773// ---- selftest: build a real 1-tri glb on disk, convert it, then three malformed inputs must REFUSE ----
774func st_wr(path: *u8, buf: *u8, n: i64) -> i64 {
775 let fd: i64 = sys_openat_wr(path, 420)
776 if fd < 0 { return 0 - 1 }
777 sys_write(fd, buf, n)
778 sys_close(fd)
779 return 0
780}
781func st_mkglb(buf: *u8, breakmode: i64) -> i64 {
782 // JSON (compact, machine-shape)
783 let j: *u8 = "{\x22asset\x22:{\x22version\x22:\x222.0\x22},\x22meshes\x22:[{\x22primitives\x22:[{\x22attributes\x22:{\x22POSITION\x22:0},\x22indices\x22:1}]}],\x22accessors\x22:[{\x22bufferView\x22:0,\x22componentType\x22:5126,\x22count\x22:3,\x22type\x22:\x22VEC3\x22},{\x22bufferView\x22:1,\x22componentType\x22:5123,\x22count\x22:3,\x22type\x22:\x22SCALAR\x22}],\x22bufferViews\x22:[{\x22buffer\x22:0,\x22byteOffset\x22:0,\x22byteLength\x22:36},{\x22buffer\x22:0,\x22byteOffset\x22:36,\x22byteLength\x22:6}],\x22buffers\x22:[{\x22byteLength\x22:44}]}" as *u8
784 var jl: i64 = 0
785 while j[jl] != (0 as u8) { jl = jl + 1 }
786 var jp: i64 = jl
787 while jp % 4 != 0 { jp = jp + 1 }
788 // bin length appears ONLY as a literal below: locals in this frame mis-read (nx_cc bug 1785936860)
789 let total: i64 = 12 + 8 + jp + 8 + 44
790 var o: i64 = 0
791 buf[0]=103 as u8; buf[1]=108 as u8; buf[2]=84 as u8; buf[3]=70 as u8
792 if breakmode == 2 { buf[0]=88 as u8 }
793 g2_wr32(buf, 4, 2)
794 g2_wr32(buf, 8, total)
795 g2_wr32(buf, 12, jp)
796 buf[16]=74 as u8; buf[17]=83 as u8; buf[18]=79 as u8; buf[19]=78 as u8
797 var i: i64 = 0
798 while i < jl { buf[20+i] = j[i]; i = i + 1 }
799 while i < jp { buf[20+i] = 32 as u8; i = i + 1 }
800 o = 20 + jp
801 g2_wr32(buf, o, 44)
802 buf[o+4]=66 as u8; buf[o+5]=73 as u8; buf[o+6]=78 as u8; buf[o+7]=0 as u8
803 // verts (0,0,0) (1,0,0) (0,1,0) as f32 -- offsets all o+N literals, NO further locals (nx_cc bug 1785936860)
804 g2_wr32(buf, o+8, 0); g2_wr32(buf, o+12, 0); g2_wr32(buf, o+16, 0)
805 g2_wr32(buf, o+20, g2_enc(1000, 1000)); g2_wr32(buf, o+24, 0); g2_wr32(buf, o+28, 0)
806 g2_wr32(buf, o+32, 0); g2_wr32(buf, o+36, g2_enc(1000, 1000)); g2_wr32(buf, o+40, 0)
807 buf[o+44]=0 as u8; buf[o+45]=0 as u8; buf[o+46]=1 as u8; buf[o+47]=0 as u8
808 buf[o+48]=2 as u8; buf[o+49]=0 as u8; buf[o+50]=0 as u8; buf[o+51]=0 as u8
809 if breakmode == 1 { return total - 9 }
810 return total
811}
812func g2_selftest() -> i64 {
813 var fails: i64 = 0
814 let buf: *u8 = sys_mmap(G2_MAGIC_4096)
815 let n0: i64 = st_mkglb(buf, 0)
816 st_wr("/tmp/g2_ok.glb" as *u8, buf, n0)
817 let r0: i64 = g2_convert("/tmp/g2_ok.glb" as *u8, "/tmp/g2_ok.nxmesh" as *u8, 0 as *u8)
818 if r0 == 0 {
819 let vb: *u8 = sys_mmap(G2_MAGIC_4096)
820 let vfd: i64 = sys_openat_rd("/tmp/g2_ok.nxmesh" as *u8)
821 let vn: i64 = sys_read(vfd, vb, G2_MAGIC_4096)
822 sys_close(vfd)
823 var t0: i64 = 0
824 if vn > 40 { if vb[0] == (78 as u8) { if vb[5] == (50 as u8) { t0 = 1 } } }
825 if t0 == 1 { hw("T0 PASS convert emits valid NXMSH2 (1 tri)\n" as *u8) } else { fails = fails + 1; hw("T0 FAIL output not NXMSH2\n" as *u8) }
826 } else { fails = fails + 1; hw("T0 FAIL convert refused a valid glb\n" as *u8) }
827 let n1: i64 = st_mkglb(buf, 1)
828 st_wr("/tmp/g2_trunc.glb" as *u8, buf, n1)
829 if g2_convert("/tmp/g2_trunc.glb" as *u8, "/tmp/g2_x1.nxmesh" as *u8, 0 as *u8) != 0 { hw("T1 PASS truncated glb refused\n" as *u8) } else { fails = fails + 1; hw("T1 FAIL truncated glb accepted\n" as *u8) }
830 let n2: i64 = st_mkglb(buf, 2)
831 st_wr("/tmp/g2_badmagic.glb" as *u8, buf, n2)
832 if g2_convert("/tmp/g2_badmagic.glb" as *u8, "/tmp/g2_x2.nxmesh" as *u8, 0 as *u8) != 0 { hw("T2 PASS bad magic refused\n" as *u8) } else { fails = fails + 1; hw("T2 FAIL bad magic accepted\n" as *u8) }
833 if g2_convert("/tmp/g2_absent_zz.glb" as *u8, "/tmp/g2_x3.nxmesh" as *u8, 0 as *u8) != 0 { hw("T3 PASS absent input refused\n" as *u8) } else { fails = fails + 1; hw("T3 FAIL absent input accepted\n" as *u8) }
834 if fails == 0 { hw("GLTF2MESH-SELFTEST GREEN 4/4\n" as *u8); return 0 }
835 hw("GLTF2MESH-SELFTEST RED fails=" as *u8); pn(fails); hw("\n" as *u8)
836 return 1
837}
838
839func main(argc: i64, argv: *i64) -> i64 {
840 if argc >= 2 {
841 let a1: *u8 = argv[1] as *u8
842 if g2_lit_at(a1, 0, 8, "selftest" as *u8) == 8 { let rc: i64 = g2_selftest(); sys_exit(rc); return rc }
843 }
844 if argc < 3 {
845 hw("usage: nx_gltf2mesh <in.glb|.vrm> <out.nxmesh> [skip-mesh-name-substr] | selftest\n" as *u8)
846 sys_exit(2)
847 return 2
848 }
849 var skip: *u8 = 0 as *u8
850 if argc >= 4 { if g2_filter(argv[3] as *u8) == 1 { skip = argv[3] as *u8 } }
851 if argc >= 5 { if g2_filter(argv[4] as *u8) == 1 { skip = argv[4] as *u8 } }
852 let rc2: i64 = g2_convert(argv[1] as *u8, argv[2] as *u8, skip)
853 sys_exit(rc2)
854 return rc2
855}