code wiki / (root) / nx_gltf_mesh.nx

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}