Model optimization
This commit is contained in:
1 parent
be6a8cca9e
commit
d24748df1e
5 files changed
+244
-10
No files matched your search
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert a text-based static mesh file (.txt) to binary format (.txt.bin).
|
||||
|
||||
Usage:
|
||||
python convert_model_to_binary.py <input.txt> [<output.bin>]
|
||||
|
||||
If the output path is not given it is derived by appending ".bin" to the input path,
|
||||
e.g. resources/w/firebox.txt -> resources/w/firebox.txt.bin
|
||||
|
||||
Binary format (BSMF v1) -- all values little-endian:
|
||||
|
||||
HEADER
|
||||
4 bytes magic "BSMF"
|
||||
uint32 version (1)
|
||||
uint32 numVertices
|
||||
uint32 numTriangles
|
||||
|
||||
VERTICES (numVertices entries):
|
||||
3 x float position x, y, z -- engine coordinate space
|
||||
3 x float normal x, y, z -- engine coordinate space
|
||||
2 x float UV u, v
|
||||
|
||||
TRIANGLES (numTriangles entries):
|
||||
3 x uint32 vertex indices i0, i1, i2
|
||||
|
||||
The same Blender->engine axis swap applied by LoadFromTextFile02 is baked in here,
|
||||
so the C++ binary loader can read coordinates directly without any post-processing:
|
||||
engine_x = blender_y
|
||||
engine_y = blender_z
|
||||
engine_z = blender_x
|
||||
"""
|
||||
|
||||
import struct
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
def _parse_floats(text):
|
||||
return [float(x) for x in re.findall(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?', text)]
|
||||
|
||||
|
||||
def _parse_ints(text):
|
||||
return [int(x) for x in re.findall(r'[-]?\d+', text)]
|
||||
|
||||
|
||||
def _swap_axes(x, y, z):
|
||||
"""Blender Y-up -> engine coordinate system (mirrors LoadFromTextFile02)."""
|
||||
return y, z, x
|
||||
|
||||
|
||||
def convert(input_path, output_path):
|
||||
with open(input_path, 'r', encoding='utf-8') as f:
|
||||
lines = [line.rstrip('\n') for line in f]
|
||||
|
||||
idx = 0
|
||||
|
||||
def next_line():
|
||||
nonlocal idx
|
||||
while idx < len(lines):
|
||||
line = lines[idx]
|
||||
idx += 1
|
||||
return line
|
||||
raise EOFError("Unexpected end of file while parsing: " + input_path)
|
||||
|
||||
# --- Vertices ---
|
||||
while True:
|
||||
line = next_line()
|
||||
if '===Vertices' in line:
|
||||
break
|
||||
|
||||
m = re.search(r'\d+', line)
|
||||
if not m:
|
||||
raise ValueError("Could not parse vertex count from: " + line)
|
||||
num_vertices = int(m.group())
|
||||
|
||||
positions = []
|
||||
normals = []
|
||||
uvs = []
|
||||
|
||||
for i in range(num_vertices):
|
||||
line = next_line()
|
||||
# V N: Pos(x, y, z) Norm(nx, ny, nz) UV(u, v)
|
||||
nums = _parse_floats(line)
|
||||
# nums[0] = vertex index (float-parsed), then 3 pos, 3 norm, 2 uv
|
||||
if len(nums) < 9:
|
||||
raise ValueError(f"Malformed vertex line {i}: {line}")
|
||||
px, py, pz = _swap_axes(nums[1], nums[2], nums[3])
|
||||
nx, ny, nz = _swap_axes(nums[4], nums[5], nums[6])
|
||||
positions.append((px, py, pz))
|
||||
normals.append((nx, ny, nz))
|
||||
uvs.append((nums[7], nums[8]))
|
||||
|
||||
# --- Triangles ---
|
||||
while True:
|
||||
line = next_line()
|
||||
if '===Triangles' in line:
|
||||
break
|
||||
|
||||
m = re.search(r'\d+', line)
|
||||
if not m:
|
||||
raise ValueError("Could not parse triangle count from: " + line)
|
||||
num_triangles = int(m.group())
|
||||
|
||||
triangles = []
|
||||
for i in range(num_triangles):
|
||||
line = next_line()
|
||||
ints = _parse_ints(line)
|
||||
if len(ints) != 3:
|
||||
raise ValueError(f"Malformed triangle line {i}: {line}")
|
||||
triangles.append(tuple(ints))
|
||||
|
||||
# --- Write binary ---
|
||||
with open(output_path, 'wb') as out:
|
||||
out.write(b'BSMF')
|
||||
out.write(struct.pack('<I', 1))
|
||||
out.write(struct.pack('<I', num_vertices))
|
||||
out.write(struct.pack('<I', num_triangles))
|
||||
|
||||
for i in range(num_vertices):
|
||||
out.write(struct.pack('<3f', *positions[i]))
|
||||
out.write(struct.pack('<3f', *normals[i]))
|
||||
out.write(struct.pack('<2f', *uvs[i]))
|
||||
|
||||
for tri in triangles:
|
||||
out.write(struct.pack('<3I', *tri))
|
||||
|
||||
in_size = os.path.getsize(input_path)
|
||||
out_size = os.path.getsize(output_path)
|
||||
print(f"Converted: {input_path} ({in_size:,} bytes text) -> {output_path} ({out_size:,} bytes binary)")
|
||||
print(f" Vertices: {num_vertices}, Triangles: {num_triangles}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2 or len(sys.argv) > 3:
|
||||
print(f"Usage: {sys.argv[0]} <input.txt> [<output.bin>]")
|
||||
sys.exit(1)
|
||||
|
||||
input_path = sys.argv[1]
|
||||
output_path = sys.argv[2] if len(sys.argv) == 3 else input_path + '.bin'
|
||||
convert(input_path, output_path)
|
||||
Reference in new issue
Block a user