# Packs a .gltf with external buffers and images into one self-contained .glb, which is the only # model format Singe loads. Usage: python3 util/packGlb.py Model.gltf Model.glb import json, os, struct, sys src, out = sys.argv[1], sys.argv[2] base = os.path.dirname(src) g = json.load(open(src)) binary = bytearray() views = g.setdefault('bufferViews', []) def add(data): while len(binary) % 4: binary.append(0) off = len(binary); binary.extend(data) return off # Buffers: concatenate, remapping views. offsets = [] for b in g.get('buffers', []): data = open(os.path.join(base, b['uri']), 'rb').read() offsets.append(add(data)) for v in views: v['byteOffset'] = v.get('byteOffset', 0) + offsets[v.get('buffer', 0)] v['buffer'] = 0 # Images: each becomes a buffer view with a mime type. for img in g.get('images', []): uri = img.pop('uri') data = open(os.path.join(base, uri), 'rb').read() ext = uri.lower().rsplit('.', 1)[-1] views.append({'buffer': 0, 'byteOffset': add(data), 'byteLength': len(data)}) img['bufferView'] = len(views) - 1 img['mimeType'] = 'image/png' if ext == 'png' else 'image/jpeg' while len(binary) % 4: binary.append(0) g['buffers'] = [{'byteLength': len(binary)}] js = json.dumps(g, separators=(',', ':')).encode() while len(js) % 4: js += b' ' with open(out, 'wb') as f: f.write(struct.pack('<4sII', b'glTF', 2, 12 + 8 + len(js) + 8 + len(binary))) f.write(struct.pack('