nx_gltf_mesh.nx source
↩ module page · 132 lines · 8201 B
1// nx_gltf_mesh.nx -- ★F6 ASSET IO (graphics foundation ladder): export the current nx_trimesh mesh as a standard binary
2// glTF (.glb) -- POSITION (float32) + NORMAL (normalized int16) + COLOR_0 (normalized uint8 VEC4) + indices (uint32),
3// one PBR material, doubleSided. This makes our sovereign geometry a REAL, universally-readable 3D asset (Blender, web
4// viewers, engines). 100% integer: includes an integer IEEE-754 float32 encoder. license_tier: ORIGINAL
5import "nx_syscalls.nx"
6import "nx_trimesh.nx"
7const K_MAGIC_8388607: i64 = 8388607
8const K_MAGIC_2000000000: i64 = 2000000000
9const K_MAGIC_32767: i64 = 32767
10const K_MAGIC_65535: i64 = 65535
11const K_MAGIC_8192: i64 = 8192
12
13// integer -> IEEE-754 float32 bit pattern (exact for |v| < 2^24, which covers our coordinates)
14func gl_f32bits(v: i64) -> i64 {
15 if v == 0 { return 0 }
16 var sign: i64 = 0; var a: i64 = v
17 if v < 0 { sign = 1; a = 0-v }
18 var e: i64 = 0; var t: i64 = a
19 while t > 1 { t = t>>1; e = e+1 } // e = floor(log2 a)
20 var mant: i64 = 0
21 if e <= 23 { mant = (a - (1<<e)) << (23-e) } else { mant = (a - (1<<e)) >> (e-23) }
22 let exp: i64 = e + 127
23 return (sign<<31) + (exp<<23) + (mant & K_MAGIC_8388607)
24}
25func gl_u8(out: *u8, at: i64, v: i64) -> i64 { out[at] = (v&255) as u8; return at+1 }
26func gl_u16(out: *u8, at: i64, v: i64) -> i64 { out[at]=(v&255) as u8; out[at+1]=((v>>8)&255) as u8; return at+2 }
27func gl_u32(out: *u8, at: i64, v: i64) -> i64 { out[at]=(v&255) as u8; out[at+1]=((v>>8)&255) as u8; out[at+2]=((v>>16)&255) as u8; out[at+3]=((v>>24)&255) as u8; return at+4 }
28func gl_f32(out: *u8, at: i64, v: i64) -> i64 { return gl_u32(out, at, gl_f32bits(v)) }
29func gl_str(out: *u8, at: i64, s: *u8) -> i64 { var i: i64=0; var a: i64=at; while s[i]!=(0 as u8){ out[a]=s[i]; a=a+1; i=i+1 } return a }
30func gl_align4(x: i64) -> i64 { return (x+3)/4*4 }
31// json helpers
32func jcat(o: *u8, at: i64, s: *u8) -> i64 { var i: i64=0; var a: i64=at; while s[i]!=(0 as u8){o[a]=s[i]; a=a+1; i=i+1} return a }
33func jnum(o: *u8, at: i64, v: i64) -> i64 {
34 let b: *u8 = sys_mmap(32) as *u8
35 var x: i64 = v; var ng: i64 = 0
36 if x < 0 { ng = 1; x = 0-x }
37 var i: i64 = 31
38 if x == 0 { b[i]=48 as u8; i=i-1 }
39 while x > 0 { b[i]=(48+x%10) as u8; x=x/10; i=i-1 }
40 if ng == 1 { b[i]=45 as u8; i=i-1 }
41 var a: i64 = at; var j: i64 = i+1
42 while j < 32 { o[a]=b[j]; a=a+1; j=j+1 }
43 return a
44}
45
46// export current trimesh -> GLB bytes in `out` (*u8, must be >= ~NV*22 + NT*12 + 4096). Returns total length.
47func gltf_export(out: *u8) -> i64 {
48 let NV: i64 = tm_nv(); let NT: i64 = tm_nt(); let NI: i64 = NT*3
49 // ---- BIN layout (each bufferView aligned for its component type) ----
50 let posOff: i64 = 0; let posLen: i64 = NV*12
51 let nrmOff: i64 = posLen; let nrmLen: i64 = NV*6
52 let colOff: i64 = gl_align4(nrmOff+nrmLen); let colLen: i64 = NV*4
53 let idxOff: i64 = colOff+colLen; let idxLen: i64 = NI*4
54 let binLen: i64 = idxOff+idxLen
55 let bin: *u8 = sys_mmap(binLen + 64)
56 let vp: *i64 = sys_mmap(24) as *i64
57 let tt: *i64 = sys_mmap(24) as *i64
58 var minx: i64 = K_MAGIC_2000000000; var miny: i64 = K_MAGIC_2000000000; var minz: i64 = K_MAGIC_2000000000
59 var maxx: i64 = 0-K_MAGIC_2000000000; var maxy: i64 = 0-K_MAGIC_2000000000; var maxz: i64 = 0-K_MAGIC_2000000000
60 // positions (float32) + bbox
61 var a: i64 = 0; var at: i64 = posOff
62 while a < NV {
63 tm_vpos(a, vp)
64 at = gl_f32(bin, at, vp[0]); at = gl_f32(bin, at, vp[1]); at = gl_f32(bin, at, vp[2])
65 if vp[0]<minx {minx=vp[0]} if vp[0]>maxx {maxx=vp[0]}
66 if vp[1]<miny {miny=vp[1]} if vp[1]>maxy {maxy=vp[1]}
67 if vp[2]<minz {minz=vp[2]} if vp[2]>maxz {maxz=vp[2]}
68 a = a + 1
69 }
70 // normals (int16 normalized): n is fx4096 (|n|=4096 -> 1.0). short = n*8 (4096*8=32768 -> clamp 32767)
71 a = 0; at = nrmOff
72 while a < NV {
73 tm_vnorm(a, vp)
74 var c: i64 = 0
75 while c < 3 {
76 var s: i64 = vp[c]*8
77 if s > K_MAGIC_32767 { s = K_MAGIC_32767 } if s < 0-K_MAGIC_32767 { s = 0-K_MAGIC_32767 }
78 at = gl_u16(bin, at, s & K_MAGIC_65535)
79 c = c + 1
80 }
81 a = a + 1
82 }
83 // colours (uint8 normalized VEC4 r,g,b,255)
84 a = 0; at = colOff
85 while a < NV {
86 let col: i64 = tm_getvcol(a)
87 at = gl_u8(bin, at, col&255); at = gl_u8(bin, at, (col>>8)&255); at = gl_u8(bin, at, (col>>16)&255); at = gl_u8(bin, at, 255)
88 a = a + 1
89 }
90 // indices (uint32)
91 var tix: i64 = 0; at = idxOff
92 while tix < NT {
93 tm_tget(tix, tt)
94 at = gl_u32(bin, at, tt[0]); at = gl_u32(bin, at, tt[1]); at = gl_u32(bin, at, tt[2])
95 tix = tix + 1
96 }
97 // ---- JSON ----
98 let json: *u8 = sys_mmap(K_MAGIC_8192)
99 var j: i64 = 0
100 j = jcat(json, j, "{\"asset\":{\"version\":\"2.0\",\"generator\":\"nishi nx_gltf_mesh\"},\"scene\":0,\"scenes\":[{\"nodes\":[0]}],\"nodes\":[{\"mesh\":0}]," as *u8)
101 j = jcat(json, j, "\"meshes\":[{\"primitives\":[{\"attributes\":{\"POSITION\":0,\"NORMAL\":1,\"COLOR_0\":2},\"indices\":3,\"material\":0}]}]," as *u8)
102 j = jcat(json, j, "\"materials\":[{\"pbrMetallicRoughness\":{\"metallicFactor\":0,\"roughnessFactor\":1},\"doubleSided\":true}]," as *u8)
103 j = jcat(json, j, "\"accessors\":[{\"bufferView\":0,\"componentType\":5126,\"count\":" as *u8); j = jnum(json, j, NV)
104 j = jcat(json, j, ",\"type\":\"VEC3\",\"min\":[" as *u8); j = jnum(json,j,minx); j=jcat(json,j,","as *u8); j=jnum(json,j,miny); j=jcat(json,j,","as *u8); j=jnum(json,j,minz)
105 j = jcat(json, j, "],\"max\":[" as *u8); j=jnum(json,j,maxx); j=jcat(json,j,","as *u8); j=jnum(json,j,maxy); j=jcat(json,j,","as *u8); j=jnum(json,j,maxz); j=jcat(json,j,"]}," as *u8)
106 j = jcat(json, j, "{\"bufferView\":1,\"componentType\":5122,\"normalized\":true,\"count\":" as *u8); j = jnum(json, j, NV); j = jcat(json, j, ",\"type\":\"VEC3\"}," as *u8)
107 j = jcat(json, j, "{\"bufferView\":2,\"componentType\":5121,\"normalized\":true,\"count\":" as *u8); j = jnum(json, j, NV); j = jcat(json, j, ",\"type\":\"VEC4\"}," as *u8)
108 j = jcat(json, j, "{\"bufferView\":3,\"componentType\":5125,\"count\":" as *u8); j = jnum(json, j, NI); j = jcat(json, j, ",\"type\":\"SCALAR\"}]," as *u8)
109 j = jcat(json, j, "\"bufferViews\":[{\"buffer\":0,\"byteOffset\":0,\"byteLength\":" as *u8); j = jnum(json, j, posLen); j = jcat(json, j, ",\"target\":34962}," as *u8)
110 j = jcat(json, j, "{\"buffer\":0,\"byteOffset\":" as *u8); j=jnum(json,j,nrmOff); j=jcat(json,j,",\"byteLength\":" as *u8); j=jnum(json,j,nrmLen); j=jcat(json,j,",\"target\":34962}," as *u8)
111 j = jcat(json, j, "{\"buffer\":0,\"byteOffset\":" as *u8); j=jnum(json,j,colOff); j=jcat(json,j,",\"byteLength\":" as *u8); j=jnum(json,j,colLen); j=jcat(json,j,",\"target\":34962}," as *u8)
112 j = jcat(json, j, "{\"buffer\":0,\"byteOffset\":" as *u8); j=jnum(json,j,idxOff); j=jcat(json,j,",\"byteLength\":" as *u8); j=jnum(json,j,idxLen); j=jcat(json,j,",\"target\":34963}]," as *u8)
113 j = jcat(json, j, "\"buffers\":[{\"byteLength\":" as *u8); j = jnum(json, j, binLen); j = jcat(json, j, "}]}" as *u8)
114 let jsonLen: i64 = j
115 let jsonPad: i64 = gl_align4(jsonLen)
116 // ---- assemble GLB ----
117 var o: i64 = 0
118 o = gl_str(out, o, "glTF" as *u8) // magic
119 o = gl_u32(out, o, 2) // version
120 let total: i64 = 12 + 8 + jsonPad + 8 + binLen
121 o = gl_u32(out, o, total) // total length
122 o = gl_u32(out, o, jsonPad) // JSON chunk length
123 o = gl_str(out, o, "JSON" as *u8) // JSON chunk type
124 var q: i64 = 0
125 while q < jsonLen { out[o] = json[q]; o = o + 1; q = q + 1 }
126 while q < jsonPad { out[o] = 32 as u8; o = o + 1; q = q + 1 } // pad JSON with spaces
127 o = gl_u32(out, o, binLen) // BIN chunk length
128 o = gl_u8(out, o, 66); o = gl_u8(out, o, 73); o = gl_u8(out, o, 78); o = gl_u8(out, o, 0) // "BIN\0"
129 q = 0
130 while q < binLen { out[o] = bin[q]; o = o + 1; q = q + 1 }
131 return o
132}