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}