code wiki / (root) / nx_gltf_anim.nx

nx_gltf_anim.nx source

↩ module page · 195 lines · 16628 B

1// nx_gltf_anim.nx -- ★SKINNED + ANIMATED glTF export: the being as a rigged, WALKING .glb (plays in any glTF viewer). 2// Reuses our FK rig: each mesh vertex is rigidly bound to its nearest part's deepest bone (joints supplied by the driver). 3// 9 joints (root + L/R shoulder,elbow,hip,knee) in a 2-level hierarchy; inverse-bind = translate(-pivot); a walk-cycle 4// animation = per-bone quaternion rotation keyframes about X (sagittal). Includes an integer float32 + fixed-point encoder. 5// license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_trimesh.nx" 8import "nx_itrig.nx" 9import "nx_gltf_mesh.nx" 10const GA_MAGIC_3217: i64 = 3217 11const GA_MAGIC_25736: i64 = 25736 12const GA_MAGIC_4096: i64 = 4096 13const GA_MAGIC_1400: i64 = 1400 14const GA_MAGIC_1700: i64 = 1700 15const GA_MAGIC_2400: i64 = 2400 16const GA_MAGIC_8192: i64 = 8192 17const GA_MAGIC_2000000000: i64 = 2000000000 18const GA_MAGIC_32767: i64 = 32767 19const GA_MAGIC_65535: i64 = 65535 20const GA_MAGIC_65536: i64 = 65536 21 22// encode a fixed-point value n/2^fb as float32 bits (n integer). Divides by 2^fb = subtract fb from the exponent field. 23func ga_f32fx(n: i64, fb: i64) -> i64 { if n == 0 { return 0 } return gl_f32bits(n) - (fb<<23) } 24 25const GA_K: i64 = 9 // walk keyframes (dt = 1/8 s -> exact float times, 1.0 s cycle) 26const GA_NJ: i64 = 11 // joints: 0 root, 1 Lsh,2 Lel,3 Rsh,4 Rel,5 Lhip,6 Lkn,7 Rhip,8 Rkn, 9 Lank,10 Rank (ankle = the calf landmark, unanimated; debt 1785900993) 27 28// absolute bind pivots per joint (joint 0 = root at origin). 29// ★ROOT FIX 2026-08-04 (caught by the R3 closing measurement, debt 1785900993): this table was a BAKED 30// COPY of the atlas's base pivots, so a parametrically-reshaped body exported with the ORIGINAL skeleton -- 31// mesh moved, rig did not, and the retarget convergence test correctly refused to converge. The atlas is 32// the SSOT of its own rig: read ba_piv(). The literals below remain ONLY as the fallback for a caller that 33// exports before any atlas_build (pivots unallocated), and they are the atlas's own defaults. 34func ga_piv(j: i64, axis: i64) -> i64 { 35 if j > 0 { if ba_piv_ready() == 1 { return ba_piv(j, axis) } } 36 let px: *i64 = sys_mmap(GA_NJ*8) as *i64; let py: *i64 = sys_mmap(GA_NJ*8) as *i64; let pz: *i64 = sys_mmap(GA_NJ*8) as *i64 37 px[0]=0; py[0]=0; pz[0]=0 38 px[1]=0-185; py[1]=520; pz[1]=15 39 px[2]=0-205; py[2]=300; pz[2]=20 40 px[3]=185; py[3]=520; pz[3]=15 41 px[4]=205; py[4]=300; pz[4]=20 42 px[5]=0-88; py[5]=0-40; pz[5]=10 43 px[6]=0-84; py[6]=0-430; pz[6]=20 44 px[7]=88; py[7]=0-40; pz[7]=10 45 px[8]=84; py[8]=0-430; pz[8]=20 46 px[9]=0-84; py[9]=0-830; pz[9]=20 47 px[10]=84; py[10]=0-830; pz[10]=20 48 if axis == 0 { return px[j] } 49 if axis == 1 { return py[j] } 50 return pz[j] 51} 52// walk-cycle angle (it4096) for bone j (1..8) at keyframe k (0..K-1) 53func ga_angle(j: i64, k: i64) -> i64 { 54 let phase: i64 = k * GA_MAGIC_3217 // k * 2PI4096/(K-1) = k*GA_MAGIC_25736/8 55 let s: i64 = it_sin4096(phase) // [-GA_MAGIC_4096,GA_MAGIC_4096] 56 let c: i64 = it_cos4096(phase) 57 if j == 1 { return GA_MAGIC_1400*s/GA_MAGIC_4096 } // L shoulder (+arm swing) 58 if j == 3 { return 0-GA_MAGIC_1400*s/GA_MAGIC_4096 } // R shoulder (opposite) 59 if j == 2 { return 700 } // L elbow (slight constant bend) 60 if j == 4 { return 700 } // R elbow 61 if j == 5 { return 0-GA_MAGIC_1700*s/GA_MAGIC_4096 } // L hip (opposite the L arm) 62 if j == 7 { return GA_MAGIC_1700*s/GA_MAGIC_4096 } // R hip 63 if j == 6 { return GA_MAGIC_2400*(GA_MAGIC_4096-c)/GA_MAGIC_8192 } // L knee (bends 0..GA_MAGIC_2400 over the cycle) 64 if j == 8 { return GA_MAGIC_2400*(GA_MAGIC_4096+c)/GA_MAGIC_8192 } // R knee (opposite phase) 65 return 0 66} 67 68// export the current trimesh as a skinned+animated .glb. jarr[i] = joint index (0..8) that vertex i is bound to. -> length. 69func gltf_anim_export(out: *u8, jarr: *i64) -> i64 { 70 let NV: i64 = tm_nv(); let NT: i64 = tm_nt(); let NI: i64 = NT*3 71 // ---- BIN layout ---- 72 let posOff: i64 = 0; let posLen: i64 = NV*12 73 let nrmOff: i64 = posLen; let nrmLen: i64 = NV*6 74 let colOff: i64 = gl_align4(nrmOff+nrmLen);let colLen: i64 = NV*4 75 let joOff: i64 = colOff+colLen; let joLen: i64 = NV*4 76 let weOff: i64 = joOff+joLen; let weLen: i64 = NV*4 77 let idxOff: i64 = weOff+weLen; let idxLen: i64 = NI*4 78 let ibmOff: i64 = idxOff+idxLen; let ibmLen: i64 = GA_NJ*64 79 let inOff: i64 = ibmOff+ibmLen; let inLen: i64 = GA_K*4 80 let outOff: i64 = inOff+inLen; let outLen1: i64 = GA_K*16 // per animated bone (8 bones) 81 let binLen: i64 = gl_align4(outOff + outLen1*8) 82 let bin: *u8 = sys_mmap(binLen + 64) 83 let vp: *i64 = sys_mmap(24) as *i64 84 let tt: *i64 = sys_mmap(24) as *i64 85 var minx: i64=GA_MAGIC_2000000000; var miny: i64=GA_MAGIC_2000000000; var minz: i64=GA_MAGIC_2000000000 86 var maxx: i64=0-GA_MAGIC_2000000000; var maxy: i64=0-GA_MAGIC_2000000000; var maxz: i64=0-GA_MAGIC_2000000000 87 var a: i64 = 0; var at: i64 = posOff 88 while a < NV { tm_vpos(a,vp); at=gl_f32(bin,at,vp[0]); at=gl_f32(bin,at,vp[1]); at=gl_f32(bin,at,vp[2]); if vp[0]<minx{minx=vp[0]} if vp[0]>maxx{maxx=vp[0]} if vp[1]<miny{miny=vp[1]} if vp[1]>maxy{maxy=vp[1]} if vp[2]<minz{minz=vp[2]} if vp[2]>maxz{maxz=vp[2]} a=a+1 } 89 a=0; at=nrmOff 90 while a < NV { tm_vnorm(a,vp); var c: i64=0; while c<3 { var s: i64=vp[c]*8; if s>GA_MAGIC_32767{s=GA_MAGIC_32767} if s<0-GA_MAGIC_32767{s=0-GA_MAGIC_32767} at=gl_u16(bin,at,s&GA_MAGIC_65535); c=c+1 } a=a+1 } 91 a=0; at=colOff 92 while a < NV { let col: i64=tm_getvcol(a); 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); a=a+1 } 93 a=0; at=joOff 94 while a < NV { at=gl_u8(bin,at,jarr[a]); at=gl_u8(bin,at,0); at=gl_u8(bin,at,0); at=gl_u8(bin,at,0); a=a+1 } 95 a=0; at=weOff 96 while a < NV { at=gl_u8(bin,at,255); at=gl_u8(bin,at,0); at=gl_u8(bin,at,0); at=gl_u8(bin,at,0); a=a+1 } // normalized ubyte weight [1,0,0,0] 97 var tix: i64=0; at=idxOff 98 while tix < NT { tm_tget(tix,tt); at=gl_u32(bin,at,tt[0]); at=gl_u32(bin,at,tt[1]); at=gl_u32(bin,at,tt[2]); tix=tix+1 } 99 // inverse-bind matrices (column-major translate(-pivot)) 100 at=ibmOff; var j: i64=0 101 while j < GA_NJ { 102 at=gl_f32(bin,at,1); at=gl_f32(bin,at,0); at=gl_f32(bin,at,0); at=gl_f32(bin,at,0) 103 at=gl_f32(bin,at,0); at=gl_f32(bin,at,1); at=gl_f32(bin,at,0); at=gl_f32(bin,at,0) 104 at=gl_f32(bin,at,0); at=gl_f32(bin,at,0); at=gl_f32(bin,at,1); at=gl_f32(bin,at,0) 105 at=gl_f32(bin,at,0-ga_piv(j,0)); at=gl_f32(bin,at,0-ga_piv(j,1)); at=gl_f32(bin,at,0-ga_piv(j,2)); at=gl_f32(bin,at,1) 106 j=j+1 107 } 108 // animation input (times: k/8 seconds, exact) 109 at=inOff; var k: i64=0 110 while k < GA_K { at=gl_u32(bin,at,ga_f32fx(k,3)); k=k+1 } 111 // animation outputs: 8 bones (joints 1..8), K quaternions each (rotation about X) 112 var bn: i64=1 113 while bn <= 8 { 114 k=0 115 while k < GA_K { 116 let ang: i64 = ga_angle(bn,k) 117 let half: i64 = ang/2 118 at=gl_u32(bin,at,ga_f32fx(it_sin4096(half),12)) // qx = sin(half) 119 at=gl_u32(bin,at,0) // qy 120 at=gl_u32(bin,at,0) // qz 121 at=gl_u32(bin,at,ga_f32fx(it_cos4096(half),12)) // qw = cos(half) 122 k=k+1 123 } 124 bn=bn+1 125 } 126 // ---- JSON ---- 127 let json: *u8 = sys_mmap(GA_MAGIC_65536) 128 var jj: i64 = 0 129 jj=jcat(json,jj,"{\"asset\":{\"version\":\"2.0\",\"generator\":\"nishi nx_gltf_anim\"},\"scene\":0,\"scenes\":[{\"nodes\":[0,1]}]," as *u8) 130 // nodes: 0 mesh(skin), 1 root, 2 Lsh,3 Lel,4 Rsh,5 Rel,6 Lhip,7 Lkn,8 Rhip,9 Rkn,10 Lank,11 Rank 131 jj=jcat(json,jj,"\"nodes\":[{\"mesh\":0,\"skin\":0},{\"name\":\"root\",\"children\":[2,4,6,8]}," as *u8) 132 // helper to write a joint node with translation (local = pivot - parentPivot) + optional child 133 // Lsh (parent root): T=pivot1 ; child Lel(3) 134 jj=jcat(json,jj,"{\"name\":\"Lsh\",\"children\":[3],\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(1,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(1,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(1,2)); jj=jcat(json,jj,"]},"as *u8) 135 jj=jcat(json,jj,"{\"name\":\"Lel\",\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(2,0)-ga_piv(1,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(2,1)-ga_piv(1,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(2,2)-ga_piv(1,2)); jj=jcat(json,jj,"]},"as *u8) 136 jj=jcat(json,jj,"{\"name\":\"Rsh\",\"children\":[5],\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(3,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(3,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(3,2)); jj=jcat(json,jj,"]},"as *u8) 137 jj=jcat(json,jj,"{\"name\":\"Rel\",\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(4,0)-ga_piv(3,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(4,1)-ga_piv(3,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(4,2)-ga_piv(3,2)); jj=jcat(json,jj,"]},"as *u8) 138 jj=jcat(json,jj,"{\"name\":\"Lhip\",\"children\":[7],\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(5,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(5,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(5,2)); jj=jcat(json,jj,"]},"as *u8) 139 jj=jcat(json,jj,"{\"name\":\"Lkn\",\"children\":[10],\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(6,0)-ga_piv(5,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(6,1)-ga_piv(5,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(6,2)-ga_piv(5,2)); jj=jcat(json,jj,"]},"as *u8) 140 jj=jcat(json,jj,"{\"name\":\"Rhip\",\"children\":[9],\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(7,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(7,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(7,2)); jj=jcat(json,jj,"]},"as *u8) 141 jj=jcat(json,jj,"{\"name\":\"Rkn\",\"children\":[11],\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(8,0)-ga_piv(7,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(8,1)-ga_piv(7,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(8,2)-ga_piv(7,2)); jj=jcat(json,jj,"]},"as *u8) 142 jj=jcat(json,jj,"{\"name\":\"Lank\",\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(9,0)-ga_piv(6,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(9,1)-ga_piv(6,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(9,2)-ga_piv(6,2)); jj=jcat(json,jj,"]},"as *u8) 143 jj=jcat(json,jj,"{\"name\":\"Rank\",\"translation\":["as *u8); jj=jnum(json,jj,ga_piv(10,0)-ga_piv(8,0)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(10,1)-ga_piv(8,1)); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,ga_piv(10,2)-ga_piv(8,2)); jj=jcat(json,jj,"]}],"as *u8) 144 jj=jcat(json,jj,"\"skins\":[{\"inverseBindMatrices\":6,\"joints\":[1,2,3,4,5,6,7,8,9,10,11]}]," as *u8) 145 jj=jcat(json,jj,"\"meshes\":[{\"primitives\":[{\"attributes\":{\"POSITION\":0,\"NORMAL\":1,\"COLOR_0\":2,\"JOINTS_0\":3,\"WEIGHTS_0\":4},\"indices\":5,\"material\":0}]}]," as *u8) 146 jj=jcat(json,jj,"\"materials\":[{\"pbrMetallicRoughness\":{\"metallicFactor\":0,\"roughnessFactor\":1},\"doubleSided\":true}]," as *u8) 147 // accessors 0..15 148 jj=jcat(json,jj,"\"accessors\":[{\"bufferView\":0,\"componentType\":5126,\"count\":"as *u8); jj=jnum(json,jj,NV); jj=jcat(json,jj,",\"type\":\"VEC3\",\"min\":["as *u8); jj=jnum(json,jj,minx); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,miny); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,minz); jj=jcat(json,jj,"],\"max\":["as *u8); jj=jnum(json,jj,maxx); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,maxy); jj=jcat(json,jj,","as *u8); jj=jnum(json,jj,maxz); jj=jcat(json,jj,"]},"as *u8) 149 jj=jcat(json,jj,"{\"bufferView\":1,\"componentType\":5122,\"normalized\":true,\"count\":"as *u8); jj=jnum(json,jj,NV); jj=jcat(json,jj,",\"type\":\"VEC3\"},"as *u8) 150 jj=jcat(json,jj,"{\"bufferView\":2,\"componentType\":5121,\"normalized\":true,\"count\":"as *u8); jj=jnum(json,jj,NV); jj=jcat(json,jj,",\"type\":\"VEC4\"},"as *u8) 151 jj=jcat(json,jj,"{\"bufferView\":3,\"componentType\":5121,\"count\":"as *u8); jj=jnum(json,jj,NV); jj=jcat(json,jj,",\"type\":\"VEC4\"},"as *u8) 152 jj=jcat(json,jj,"{\"bufferView\":4,\"componentType\":5121,\"normalized\":true,\"count\":"as *u8); jj=jnum(json,jj,NV); jj=jcat(json,jj,",\"type\":\"VEC4\"},"as *u8) 153 jj=jcat(json,jj,"{\"bufferView\":5,\"componentType\":5125,\"count\":"as *u8); jj=jnum(json,jj,NI); jj=jcat(json,jj,",\"type\":\"SCALAR\"},"as *u8) 154 jj=jcat(json,jj,"{\"bufferView\":6,\"componentType\":5126,\"count\":"as *u8); jj=jnum(json,jj,GA_NJ); jj=jcat(json,jj,",\"type\":\"MAT4\"},"as *u8) 155 jj=jcat(json,jj,"{\"bufferView\":7,\"componentType\":5126,\"count\":"as *u8); jj=jnum(json,jj,GA_K); jj=jcat(json,jj,",\"type\":\"SCALAR\",\"min\":[0],\"max\":["as *u8); jj=jnum(json,jj,GA_K-1); jj=jcat(json,jj,"]},"as *u8) 156 var acn: i64 = 8 157 while acn < 16 { if acn>8 { } jj=jcat(json,jj,"{\"bufferView\":"as *u8); jj=jnum(json,jj,acn); jj=jcat(json,jj,",\"componentType\":5126,\"count\":"as *u8); jj=jnum(json,jj,GA_K); jj=jcat(json,jj,",\"type\":\"VEC4\"}"as *u8); if acn<15 { jj=jcat(json,jj,","as *u8) } acn=acn+1 } 158 jj=jcat(json,jj,"]," as *u8) 159 // bufferViews 0..15 160 jj=jcat(json,jj,"\"bufferViews\":[" as *u8) 161 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":0,\"byteLength\":"as *u8); jj=jnum(json,jj,posLen); jj=jcat(json,jj,"},"as *u8) 162 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,nrmOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,nrmLen); jj=jcat(json,jj,"},"as *u8) 163 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,colOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,colLen); jj=jcat(json,jj,"},"as *u8) 164 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,joOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,joLen); jj=jcat(json,jj,"},"as *u8) 165 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,weOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,weLen); jj=jcat(json,jj,"},"as *u8) 166 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,idxOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,idxLen); jj=jcat(json,jj,"},"as *u8) 167 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,ibmOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,ibmLen); jj=jcat(json,jj,"},"as *u8) 168 jj=jcat(json,jj,"{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,inOff); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,inLen); jj=jcat(json,jj,"}"as *u8) 169 var bvn: i64 = 0 170 while bvn < 8 { jj=jcat(json,jj,",{\"buffer\":0,\"byteOffset\":"as *u8); jj=jnum(json,jj,outOff+bvn*outLen1); jj=jcat(json,jj,",\"byteLength\":"as *u8); jj=jnum(json,jj,outLen1); jj=jcat(json,jj,"}"as *u8); bvn=bvn+1 } 171 jj=jcat(json,jj,"]," as *u8) 172 // animation: 8 samplers + 8 channels (bone j -> node j+1, path rotation) 173 jj=jcat(json,jj,"\"animations\":[{\"name\":\"walk\",\"samplers\":[" as *u8) 174 var sn: i64 = 0 175 while sn < 8 { if sn>0 { jj=jcat(json,jj,","as *u8) } jj=jcat(json,jj,"{\"input\":7,\"interpolation\":\"LINEAR\",\"output\":"as *u8); jj=jnum(json,jj,8+sn); jj=jcat(json,jj,"}"as *u8); sn=sn+1 } 176 jj=jcat(json,jj,"],\"channels\":[" as *u8) 177 sn = 0 178 while sn < 8 { if sn>0 { jj=jcat(json,jj,","as *u8) } jj=jcat(json,jj,"{\"sampler\":"as *u8); jj=jnum(json,jj,sn); jj=jcat(json,jj,",\"target\":{\"node\":"as *u8); jj=jnum(json,jj,sn+2); jj=jcat(json,jj,",\"path\":\"rotation\"}}"as *u8); sn=sn+1 } 179 jj=jcat(json,jj,"]}]," as *u8) 180 jj=jcat(json,jj,"\"buffers\":[{\"byteLength\":" as *u8); jj=jnum(json,jj,binLen); jj=jcat(json,jj,"}]}" as *u8) 181 let jsonLen: i64 = jj 182 let jsonPad: i64 = gl_align4(jsonLen) 183 // ---- GLB ---- 184 var o: i64 = 0 185 o=gl_str(out,o,"glTF"as *u8); o=gl_u32(out,o,2) 186 let total: i64 = 12+8+jsonPad+8+binLen 187 o=gl_u32(out,o,total); o=gl_u32(out,o,jsonPad); o=gl_str(out,o,"JSON"as *u8) 188 var q: i64=0 189 while q<jsonLen { out[o]=json[q]; o=o+1; q=q+1 } 190 while q<jsonPad { out[o]=32 as u8; o=o+1; q=q+1 } 191 o=gl_u32(out,o,binLen); o=gl_u8(out,o,66); o=gl_u8(out,o,73); o=gl_u8(out,o,78); o=gl_u8(out,o,0) 192 q=0 193 while q<binLen { out[o]=bin[q]; o=o+1; q=q+1 } 194 return o 195}