glbフォーマット概説

JSON記述部と、画像や頂点配列を記録するバイナリ部の2つの部分からなります。

gltf形式では、URLやパスで参照する方法で外部のバイナリデータにアクセスします。 glb形式ではJSON部とバイナリ部をひとつのファイルにまとめていて、バイト列のオフセットでバイナリデータにアクセスします。 プログラムから扱うには外部ファイルへのアクセスが無いglb形式の方が簡単1です。

glb形式

ヘッダ部 + チャンク部繰り返しという構造になっています。 実質的には、 ヘッダ部 + JSON CHUNk + BINARY CHUNKとなります。

ヘッダ部

長さ

内容

4

ascii

"glTF"

4

gltfバージョン

int32

2

4

file size

int32

チャンク部

長さ

内容

4

chunk size

int32

4

chunk type

ascii

"JSON" or "BIN\x00"

chunk size

chunk body

バイト列

python3によるパース例

import struct
import json

class Reader:
    def __init__(self, data: bytes)->None:
        self.data = data
        self.pos = 0

    def read_str(self, size):
        result = self.data[self.pos: self.pos + size]
        self.pos += size
        return result.strip()

    def read(self, size):
        result = self.data[self.pos: self.pos + size]
        self.pos += size
        return result

    def read_uint(self):
        result = struct.unpack('I', self.data[self.pos:self.pos + 4])[0]
        self.pos += 4
        return result


def parse_glb(data: bytes):
    reader = Reader(data)
    magic = reader.read_str(4)
    if  magic != b'glTF':
        raise Exception(f'magic not found: #{magic}')

    version = reader.read_uint()
    if version != 2:
        raise Exception(f'version:#{version} is not 2')

    size = reader.read_uint()
    size -= 12

    json_str = None
    body = None
    while size > 0:
        #print(size)

        chunk_size = reader.read_uint()
        size -= 4

        chunk_type = reader.read_str(4)
        size -= 4

        chunk_data = reader.read(chunk_size)
        size -= chunk_size

        if chunk_type == b'BIN\x00':
            body = chunk_data
        elif chunk_type == b'JSON':
            json_str = chunk_data
        else:
            raise Exception(f'unknown chunk_type: {chunk_type}')

    return json.loads(json_str), body


with open('AliciaSolid.vrm', 'rb') as f:
    parsed, body = parse_glb(f.read())

1

VRMではglbを採用しています。