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}