245 lines
6.1 KiB
Python
245 lines
6.1 KiB
Python
import sys
|
|
from dataclasses import dataclass, field
|
|
from pprint import pprint
|
|
|
|
@dataclass
|
|
class MD5MeshJoint:
|
|
bone_name: str = None
|
|
parent_index: int = None
|
|
x_pos: float = None
|
|
y_pos: float = None
|
|
z_pos: float = None
|
|
x_orient: float = None
|
|
y_orient: float = None
|
|
y_orient: float = None
|
|
|
|
@dataclass
|
|
class MD5MeshVert:
|
|
vert_index: int = None
|
|
tex_u: float = None
|
|
tex_v: float = None
|
|
weight_index: int = None
|
|
weight_elem: int = None
|
|
|
|
@dataclass
|
|
class MD5MeshTri:
|
|
tri_index: int = None
|
|
vert_index1: int = None
|
|
vert_index2: int = None
|
|
vert_index3: int = None
|
|
|
|
@dataclass
|
|
class MD5MeshWeight:
|
|
weight_index: int = None
|
|
joint_index: int = None
|
|
weight_value: float = None
|
|
x_pos: float = None
|
|
y_pos: float = None
|
|
z_pos: float = None
|
|
|
|
@dataclass
|
|
class MD5MeshMesh:
|
|
mesh_name: str = None
|
|
shader: str = None
|
|
num_verts: int = None
|
|
verts: list[MD5MeshVert] = field(default_factory=lambda: list())
|
|
num_tris: int = None
|
|
tris: list[MD5MeshTri] = field(default_factory=lambda: list())
|
|
num_weights: int = None
|
|
weights: list[MD5MeshWeight] = field(default_factory=lambda: list())
|
|
|
|
@dataclass
|
|
class MD5Mesh:
|
|
num_joints: int = None
|
|
num_meshes: int = None
|
|
joints: list[MD5MeshJoint] = field(default_factory=lambda: list())
|
|
meshes: list[MD5MeshMesh] = field(default_factory=lambda: list())
|
|
|
|
def parse_header(l, ix, md5mesh):
|
|
assert l[ix+0] == "MD5Version 10"
|
|
assert l[ix+1].startswith("commandline")
|
|
return ix + 2
|
|
|
|
def parse_parameters(l, ix, md5mesh):
|
|
assert l[ix+0].startswith("numJoints ")
|
|
assert l[ix+1].startswith("numMeshes ")
|
|
|
|
md5mesh.num_joints = int(l[ix+0].removeprefix("numJoints "), 10)
|
|
md5mesh.num_meshes = int(l[ix+1].removeprefix("numMeshes "), 10)
|
|
|
|
return ix + 2
|
|
|
|
def parse_joint(l, ix, md5mesh):
|
|
s = l[ix]
|
|
lc = s.split("//", maxsplit=1)
|
|
if len(lc) == 2:
|
|
line, comment = lc
|
|
elif len(lc) == 1:
|
|
line = lc
|
|
comment = None
|
|
else:
|
|
assert False, len(lc)
|
|
|
|
tokens = line.split()
|
|
joint = MD5MeshJoint()
|
|
|
|
# bone name
|
|
bone_name = tokens[0]
|
|
assert bone_name.startswith('"') and bone_name.endswith('"')
|
|
joint.bone_name = bone_name[1:-1]
|
|
|
|
# parent index
|
|
joint.parent_index = int(tokens[1], 10)
|
|
|
|
assert tokens[2] == "("
|
|
|
|
joint.x_pos = float(tokens[3])
|
|
joint.y_pos = float(tokens[4])
|
|
joint.z_pos = float(tokens[5])
|
|
|
|
assert tokens[6] == ")"
|
|
|
|
assert tokens[7] == "("
|
|
|
|
joint.x_orient = float(tokens[8])
|
|
joint.y_orient = float(tokens[9])
|
|
joint.z_orient = float(tokens[10])
|
|
|
|
assert tokens[11] == ")"
|
|
|
|
md5mesh.joints.append(joint)
|
|
|
|
return ix + 1
|
|
|
|
def parse_joints(l, ix, md5mesh):
|
|
assert md5mesh.joints == []
|
|
|
|
while l[ix] != "}":
|
|
ix = parse_joint(l, ix, md5mesh)
|
|
|
|
return ix
|
|
|
|
def parse_mesh_vert(line, mesh):
|
|
vert = MD5MeshVert()
|
|
tokens = line.split()
|
|
assert tokens[0] == "vert"
|
|
vert.vert_index = int(tokens[1], 10)
|
|
assert tokens[2] == "("
|
|
vert.tex_u = float(tokens[3])
|
|
vert.tex_v = float(tokens[4])
|
|
assert tokens[5] == ")"
|
|
vert.weight_index = int(tokens[6], 10)
|
|
vert.weight_elem = int(tokens[7], 10)
|
|
|
|
assert vert.vert_index == len(mesh.verts)
|
|
mesh.verts.append(vert)
|
|
|
|
def parse_mesh_tri(line, mesh):
|
|
tri = MD5MeshTri()
|
|
tokens = line.split()
|
|
assert tokens[0] == "tri"
|
|
tri.tri_index = int(tokens[1], 10)
|
|
tri.vert_index1 = int(tokens[2], 10)
|
|
tri.vert_index2 = int(tokens[3], 10)
|
|
tri.vert_index3 = int(tokens[4], 10)
|
|
|
|
assert tri.tri_index == len(mesh.tris)
|
|
mesh.tris.append(tri)
|
|
|
|
def parse_mesh_weight(line, mesh):
|
|
weight = MD5MeshWeight()
|
|
tokens = line.split()
|
|
assert tokens[0] == "weight"
|
|
weight.weight_index = int(tokens[1], 10)
|
|
weight.joint_index = int(tokens[2], 10)
|
|
weight.weight_value = float(tokens[3])
|
|
assert tokens[4] == "("
|
|
weight.x_pos = float(tokens[5])
|
|
weight.y_pos = float(tokens[6])
|
|
weight.z_pos = float(tokens[7])
|
|
assert tokens[8] == ")"
|
|
|
|
assert weight.weight_index == len(mesh.weights)
|
|
mesh.weights.append(weight)
|
|
|
|
def parse_mesh(l, ix, md5mesh):
|
|
mesh = MD5MeshMesh()
|
|
|
|
while l[ix] != "}":
|
|
line = l[ix]
|
|
if line.startswith("shader"):
|
|
assert mesh.shader is None
|
|
_, shader = line.split()
|
|
assert shader.startswith('"') and shader.endswith('"')
|
|
mesh.shader = shader[1:-1]
|
|
|
|
elif line.startswith("numverts"):
|
|
assert mesh.num_verts is None
|
|
mesh.num_verts = int(line.removeprefix("numverts "), 10)
|
|
|
|
elif line.startswith("numtris"):
|
|
assert mesh.num_tris is None
|
|
mesh.num_tris = int(line.removeprefix("numtris "), 10)
|
|
|
|
elif line.startswith("numweights"):
|
|
assert mesh.num_weights is None
|
|
mesh.num_weights = int(line.removeprefix("numweights "), 10)
|
|
|
|
elif line.startswith("vert"):
|
|
parse_mesh_vert(line, mesh)
|
|
|
|
elif line.startswith("tri"):
|
|
parse_mesh_tri(line, mesh)
|
|
|
|
elif line.startswith("weight"):
|
|
parse_mesh_weight(line, mesh)
|
|
|
|
else:
|
|
assert False, line
|
|
|
|
ix += 1
|
|
|
|
assert mesh.num_verts == len(mesh.verts)
|
|
assert mesh.num_tris == len(mesh.tris)
|
|
assert mesh.num_weights == len(mesh.weights)
|
|
|
|
md5mesh.meshes.append(mesh)
|
|
|
|
return ix
|
|
|
|
def parse_ordered_list(l, ix, md5mesh):
|
|
assert l[ix].endswith("{"), l[ix]
|
|
string, _ = l[ix].split()
|
|
ix += 1
|
|
|
|
if string == "joints":
|
|
ix = parse_joints(l, ix, md5mesh)
|
|
elif string == "mesh":
|
|
print("parse-mesh1", ix)
|
|
ix = parse_mesh(l, ix, md5mesh)
|
|
print("parse-mesh2", ix)
|
|
else:
|
|
assert False, string
|
|
|
|
assert l[ix] == "}", l[ix]
|
|
ix += 1
|
|
return ix
|
|
|
|
def parse_file(l):
|
|
ix = 0
|
|
md5mesh = MD5Mesh()
|
|
ix = parse_header(l, ix, md5mesh)
|
|
ix = parse_parameters(l, ix, md5mesh)
|
|
while ix < len(l):
|
|
ix = parse_ordered_list(l, ix, md5mesh)
|
|
|
|
pprint(md5mesh)
|
|
|
|
if __name__ == "__main__":
|
|
with open(sys.argv[1], 'r') as f:
|
|
buf = f.read()
|
|
|
|
l = [i.strip() for i in buf.split('\n') if i.strip()]
|
|
|
|
parse_file(l)
|