code wiki / _hdl_build / nx_gltf_export_gate.nx
nx_gltf_export_gate.nx source
↩ module page · 93 lines · 5548 B
1// nx_gltf_export_gate.nx -- prove the glTF/.glb export (roadmap R7, VRM foundation): mesh a bestiary variant,
2// write a binary glTF, and validate the container: magic "glTF" + version 2 + total-length == filesize + a JSON
3// chunk (with a POSITION accessor of the right vertex count) + a BIN chunk. This is a REAL interchange format
4// (three.js/Unity/Godot/Blender import it), the step up from STL/OBJ toward riggable VRM avatars. expect_exit:0
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_sdfrender.nx"
8import "nx_meshgen.nx"
9import "nx_assetvariant.nx"
10import "nx_gltf_export.nx"
11
12func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13func 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 }
14func rd32(b: *u8, o: i64) -> i64 { return (b[o]&0xff) | ((b[o+1]&0xff)<<8) | ((b[o+2]&0xff)<<16) | ((b[o+3]&0xff)<<24) }
15func has_str(b: *u8, n: i64, s: *u8) -> i64 {
16 var sl: i64 = 0; while s[sl]!=(0 as u8){sl=sl+1}
17 var i: i64 = 0
18 while i + sl <= n { var j: i64=0; var ok: i64=1; while j<sl { if (b[i+j]&0xff)!=(s[j]&0xff){ok=0;j=sl} else {j=j+1} } if ok==1{return 1} i=i+1 }
19 return 0
20}
21
22func main() -> i64 {
23 hw("=== nx_gltf_export_gate -- mesh -> binary glTF (.glb), R7 VRM foundation ===\n" as *u8)
24 let base: i64 = sys_mmap(sdf_bytes()) as i64
25 let F: *i64 = sys_mmap((MG_N + 1) * (MG_N + 1) * (MG_N + 1) * 8) as *i64
26 let cv: *i64 = sys_mmap(MG_N * MG_N * MG_N * 8) as *i64
27 let vb: *i64 = sys_mmap(MG_MAXV * 3 * 8) as *i64
28 let fb: *i64 = sys_mmap(MG_MAXF * 3 * 8) as *i64
29 let out: *i64 = sys_mmap(16) as *i64
30 // export the full bestiary (parity with STL/OBJ); validate the last one in detail below.
31 let vt: *i64 = sys_mmap(6 * 5 * 8) as *i64
32 var q: i64 = 0
33 vt[q]=0;vt[q+1]=0;vt[q+2]=0;vt[q+3]=0;vt[q+4]=100;q=q+5 // human
34 vt[q]=0;vt[q+1]=0;vt[q+2]=1;vt[q+3]=0;vt[q+4]=80;q=q+5 // elf
35 vt[q]=2;vt[q+1]=0;vt[q+2]=1;vt[q+3]=0;vt[q+4]=100;q=q+5 // goblin
36 vt[q]=2;vt[q+1]=0;vt[q+2]=1;vt[q+3]=0;vt[q+4]=125;q=q+5 // hobgoblin
37 vt[q]=1;vt[q+1]=0;vt[q+2]=1;vt[q+3]=1;vt[q+4]=100;q=q+5 // wolf
38 vt[q]=1;vt[q+1]=1;vt[q+2]=1;vt[q+3]=1;vt[q+4]=125;q=q+5 // dire wolf
39 var nv: i64 = 0; var nf: i64 = 0
40 var v: i64 = 0
41 while v < 6 {
42 av_build(base, vt[v*5], vt[v*5+1], vt[v*5+2], vt[v*5+3], vt[v*5+4])
43 mg_build(base, F, cv, vb, fb, out)
44 nv = out[0]; nf = out[1]
45 var nm: *u8 = "knowledge/synth_human.glb" as *u8
46 if v == 1 { nm = "knowledge/synth_elf.glb" as *u8 }
47 if v == 2 { nm = "knowledge/synth_goblin.glb" as *u8 }
48 if v == 3 { nm = "knowledge/synth_hobgoblin.glb" as *u8 }
49 if v == 4 { nm = "knowledge/synth_wolf.glb" as *u8 }
50 if v == 5 { nm = "knowledge/synth_direwolf.glb" as *u8 }
51 let glen: i64 = write_glb(vb, fb, nv, nf, nm)
52 hw(" "); hw(nm); hw(" verts="); pn(nv); hw(" bytes="); pn(glen); hw("\n" as *u8)
53 v = v + 1
54 }
55 // re-mesh human for the detailed validation below (nv/nf now = direwolf; use human.glb file)
56 av_build(base, 0, 0, 0, 0, 100)
57 mg_build(base, F, cv, vb, fb, out)
58 nv = out[0]; nf = out[1]
59
60 // read it back + validate
61 let lp: *i64 = sys_mmap(16) as *i64
62 let buf: *u8 = sys_read_file("knowledge/synth_human.glb" as *u8, lp)
63 let fsz: i64 = lp[0]
64 var fails: i64 = 0
65
66 var t1: i64 = 1
67 if rd32(buf, 0) != 0x46546C67 { t1 = 0 } // "glTF"
68 if rd32(buf, 4) != 2 { t1 = 0 } // version 2
69 if rd32(buf, 8) != fsz { t1 = 0 } // total length == file size
70 if t1 == 1 { hw("T1 PASS valid GLB header (magic glTF, version 2, length="); pn(fsz); hw("==filesize)\n" as *u8) }
71 else { fails = fails + 1; hw("T1 FAIL header (magic="); pn(rd32(buf,0)); hw(" ver="); pn(rd32(buf,4)); hw(" len="); pn(rd32(buf,8)); hw(" fsz="); pn(fsz); hw(")\n" as *u8) }
72
73 let jsonlen: i64 = rd32(buf, 12)
74 var t2: i64 = 1
75 if rd32(buf, 16) != 0x4E4F534A { t2 = 0 } // "JSON"
76 let bco: i64 = 20 + jsonlen
77 if rd32(buf, bco + 4) != 0x004E4942 { t2 = 0 } // "BIN\0"
78 if t2 == 1 { hw("T2 PASS both chunks present (JSON "); pn(jsonlen); hw("B + BIN "); pn(rd32(buf, bco)); hw("B)\n" as *u8) }
79 else { fails = fails + 1; hw("T2 FAIL chunk types\n" as *u8) }
80
81 if has_str(buf, fsz, "\"POSITION\":0" as *u8) == 1 { if has_str(buf, fsz, "\"NORMAL\":1" as *u8) == 1 { hw("T3 PASS JSON has POSITION + NORMAL accessors (smooth-shaded)\n" as *u8) } else { fails=fails+1; hw("T3 FAIL no NORMAL attribute\n" as *u8) } }
82 else { fails = fails + 1; hw("T3 FAIL no POSITION attribute\n" as *u8) }
83
84 // BIN size = positions(nv*12) + normals(nv*12) + indices(nf*3*4)
85 let want_bin: i64 = nv * 24 + nf * 3 * 4
86 let got_bin: i64 = rd32(buf, bco)
87 if got_bin >= want_bin { hw("T4 PASS BIN holds "); pn(nv); hw(" float32 positions + "); pn(nf*3); hw(" indices ("); pn(got_bin); hw("B)\n" as *u8) }
88 else { fails = fails + 1; hw("T4 FAIL BIN too small ("); pn(got_bin); hw(" < "); pn(want_bin); hw(")\n" as *u8) }
89
90 if fails == 0 { hw("GLTF-EXPORT-GATE 4/4 GREEN -- mesh exports as valid binary glTF (.glb); R7 VRM foundation up\n" as *u8); sys_exit(0); return 0 }
91 hw("GLTF-EXPORT-GATE RED fails="); pn(fails); hw("\n" as *u8)
92 sys_exit(1); return 1
93}