diff --git a/docs/gltf/format.md b/docs/gltf/format.md new file mode 100644 index 000000000..07b765e8e --- /dev/null +++ b/docs/gltf/format.md @@ -0,0 +1,99 @@ +# glbフォーマット概説 + +JSON記述部と、画像や頂点配列を記録するバイナリ部の2つの部分からなります。 + +gltf形式では、URLやパスで参照する方法で外部のバイナリデータにアクセスします。 +glb形式ではJSON部とバイナリ部をひとつのファイルにまとめていて、バイト列のオフセットでバイナリデータにアクセスします。 +プログラムから扱うには外部ファイルへのアクセスが無いglb形式の方が簡単[^VRM_glb]です。 + +[^VRM_glb]: VRMではglbを採用しています。 + +## 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によるパース例 + +```python +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()) +``` diff --git a/docs/index.md b/docs/index.md index de619c407..984808da3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -29,6 +29,7 @@ build gltf/0_82_glb_import gltf/how_to_impl_extension gltf/0_36_update +gltf/format ``` ## VRM