MagicaVoxelの.voxファイルを読む
https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt を参考にしてMagicaVoxelの.vox形式のファイルをスクリプトで読んだ。 少ないバイト数でreadを呼ぶのはシステムコールを多用していて遅そうだったり、PACKというチャンクの実装をスキップしていたり、改善の余地はまだまだあるがバイナリファイルにも手出し出来そうだと分かった。
from io import BufferedReader from typing import Tuple # reference: https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt VOX_HEADER = b"VOX " VOX_VERSION_NUMBER = 150 def main(): file = "./blue.vox" with open(file, "rb") as f: header = f.read(4) assert header == VOX_HEADER version: int = int.from_bytes(f.read(4), byteorder="little") assert version == VOX_VERSION_NUMBER while True: chunk_id = f.read(4) if len(chunk_id) == 0: exit() if chunk_id == b"MAIN": read_chunk_header(f) elif chunk_id == b"SIZE": bytes_of_chunk_content, _ = read_chunk_header(f) size_bytes = f.read(bytes_of_chunk_content) assert len(size_bytes) == 12 size_x = size_bytes[:4] size_y = size_bytes[4:8] size_z = size_bytes[8:12] print(size_x, size_y, size_z) elif chunk_id == b"XYZI": bytes_of_chunk_content, _ = read_chunk_header(f) num_voxels = int.from_bytes(f.read(4), byteorder="little") assert bytes_of_chunk_content == num_voxels * 4 + 4 for _ in range(num_voxels): x, y, z, i = f.read(4) print(x, y, z, i) elif chunk_id == b"RGBA": read_chunk_header(f) for _ in range(256): r, g, b, a = f.read(4) print(r, g, b, a) elif chunk_id == b"PACK": raise NotImplementedError() else: print(f"unknown Chunk {chunk_id}") bytes_of_chunk_content, bytes_of_children_chunks = read_chunk_header(f) f.read(bytes_of_chunk_content) f.read(bytes_of_children_chunks) def read_chunk_header(f: BufferedReader) -> Tuple[int, int]: bytes_of_chunk_content = int.from_bytes(f.read(4), byteorder="little") bytes_of_children_chunks = int.from_bytes(f.read(4), byteorder="little") return (bytes_of_chunk_content, bytes_of_children_chunks) if __name__ == "__main__": main()