msh_reader refined, more chunks implemented

This commit is contained in:
William Herald Snyder 2020-11-01 11:48:07 -05:00
parent b8afa1ed10
commit 706c32431d
5 changed files with 71 additions and 66 deletions

View File

@ -12,6 +12,7 @@ class ModelType(Enum):
CLOTH = 2 CLOTH = 2
BONE = 3 BONE = 3
STATIC = 4 STATIC = 4
SHADOWVOLUME = 6
class CollisionPrimitiveShape(Enum): class CollisionPrimitiveShape(Enum):
SPHERE = 0 SPHERE = 0

View File

@ -3,18 +3,17 @@ import io
import struct import struct
class Reader: class Reader:
def __init__(self, file, chunk_id: str, parent=None, indent=0): def __init__(self, file, parent=None, indent=0):
self.file = file self.file = file
self.size: int = 0 self.size: int = 0
self.size_pos = None self.size_pos = None
self.parent = parent self.parent = parent
self.header = chunk_id self.indent = " " * indent #for print debugging
self.indent = " " * indent
def __enter__(self): def __enter__(self):
self.size_pos = self.file.tell() self.size_pos = self.file.tell()
self.file.seek(4,1) #skip header, will add check later self.header = self.read_bytes(4).decode("utf-8")
self.size = self.read_u32() self.size = self.read_u32()
padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0 padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0
@ -84,8 +83,8 @@ class Reader:
def read_child(self, child_id: str): def read_child(self):
child = Reader(self.file, chunk_id=child_id, parent=self, indent=int(len(self.indent) / 2) + 1) child = Reader(self.file, parent=self, indent=int(len(self.indent) / 2) + 1)
return child return child
@ -103,16 +102,4 @@ class Reader:
return self.end_pos - self.file.tell() >= 8 return self.end_pos - self.file.tell() >= 8
MAX_SIZE: int = 2147483647 - 8 MAX_SIZE: int = 2147483647 - 8
'''
with open("/Users/will/Desktop/spacedoortest/spa1_prop_impdoor.msh", "rb") as tst_stream:
with Reader(tst_stream, "HEDR") as hedr:
print(hedr.peak_next_header())
'''

View File

@ -44,6 +44,8 @@ class Scene:
materials: Dict[str, Material] = field(default_factory=dict) materials: Dict[str, Material] = field(default_factory=dict)
models: List[Model] = field(default_factory=list) models: List[Model] = field(default_factory=list)
skeleton: List[int] = field(default_factory=list)
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str) -> Scene: def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str) -> Scene:
""" Create a msh Scene from the active Blender scene. """ """ Create a msh Scene from the active Blender scene. """

View File

@ -16,7 +16,7 @@ def read_scene(input_file) -> Scene:
scene.models = [] scene.models = []
scene.materials = {} scene.materials = {}
with Reader(file=input_file, chunk_id="HEDR") as hedr: with Reader(file=input_file) as hedr:
while hedr.could_have_child(): while hedr.could_have_child():
@ -24,7 +24,7 @@ def read_scene(input_file) -> Scene:
if "MSH2" in next_header: if "MSH2" in next_header:
with hedr.read_child("MSH2") as msh2: with hedr.read_child() as msh2:
materials_list = [] materials_list = []
@ -33,26 +33,37 @@ def read_scene(input_file) -> Scene:
next_header = msh2.peak_next_header() next_header = msh2.peak_next_header()
if "SINF" in next_header: if "SINF" in next_header:
with msh2.read_child("SINF") as sinf: with msh2.read_child() as sinf:
pass pass
elif "MATL" in next_header: elif "MATL" in next_header:
with msh2.read_child("MATL") as matl: with msh2.read_child() as matl:
materials_list += _read_matl_and_get_materials_list(matl) materials_list += _read_matl_and_get_materials_list(matl)
for i,mat in enumerate(materials_list): for i,mat in enumerate(materials_list):
scene.materials[mat.name] = mat scene.materials[mat.name] = mat
elif "MODL" in next_header: elif "MODL" in next_header:
while ("MODL" in msh2.peak_next_header()): while ("MODL" in msh2.peak_next_header()):
with msh2.read_child("MODL") as modl: with msh2.read_child() as modl:
scene.models.append(_read_modl(modl, materials_list)) scene.models.append(_read_modl(modl, materials_list))
else: else:
with hedr.read_child("NULL") as unknown: with hedr.read_child() as unknown:
pass pass
elif "SKL2" in next_header:
with hedr.read_child() as skl2:
num_bones = skl2.read_u32()
scene.skeleton = [skl2.read_u32(5)[0] for i in range(num_bones)]
print("Skeleton models: ")
for crc_hash in scene.skeleton:
for model in scene.models:
if crc_hash == crc(model.name):
print("\t" + model.name + " with type: " + str(model.model_type))
else: else:
with hedr.read_child("NULL") as unknown: with hedr.read_child() as unknown:
pass pass
return scene return scene
@ -64,7 +75,7 @@ def _read_matl_and_get_materials_list(matl: Reader) -> List[Material]:
num_mats = matl.read_u32() num_mats = matl.read_u32()
for _ in range(num_mats): for _ in range(num_mats):
with matl.read_child("MATD") as matd: with matl.read_child() as matd:
materials_list.append(_read_matd(matd)) materials_list.append(_read_matd(matd))
return materials_list return materials_list
@ -80,36 +91,36 @@ def _read_matd(matd: Reader) -> Material:
next_header = matd.peak_next_header() next_header = matd.peak_next_header()
if "NAME" in next_header: if "NAME" in next_header:
with matd.read_child("NAME") as name: with matd.read_child() as name:
mat.name = name.read_string() mat.name = name.read_string()
elif "DATA" in next_header: elif "DATA" in next_header:
with matd.read_child("DATA") as data: with matd.read_child() as data:
data.read_f32(4) # Diffuse Color (Seams to get ignored by modelmunge) data.read_f32(4) # Diffuse Color (Seams to get ignored by modelmunge)
mat.specular_color = data.read_f32(4) mat.specular_color = data.read_f32(4)
data.read_f32(4) # Ambient Color (Seams to get ignored by modelmunge and Zero(?)) data.read_f32(4) # Ambient Color (Seams to get ignored by modelmunge and Zero(?))
data.read_f32() # Specular Exponent/Decay (Gets ignored by RedEngine in SWBFII for all known materials) data.read_f32() # Specular Exponent/Decay (Gets ignored by RedEngine in SWBFII for all known materials)
elif "ATRB" in next_header: elif "ATRB" in next_header:
with matd.read_child("ATRB") as atrb: with matd.read_child() as atrb:
mat.flags = atrb.read_u8() mat.flags = atrb.read_u8()
mat.rendertype = atrb.read_u8() mat.rendertype = atrb.read_u8()
mat.data = atrb.read_u8(2) mat.data = atrb.read_u8(2)
elif "TX0D" in next_header: elif "TX0D" in next_header:
with matd.read_child("TX0D") as tx0d: with matd.read_child() as tx0d:
mat.texture0 = tx0d.read_string() mat.texture0 = tx0d.read_string()
elif "TX1D" in next_header: elif "TX1D" in next_header:
with matd.read_child("TX1D") as tx1d: with matd.read_child() as tx1d:
mat.texture1 = tx1d.read_string() mat.texture1 = tx1d.read_string()
elif "TX2D" in next_header: elif "TX2D" in next_header:
with matd.read_child("TX2D") as tx2d: with matd.read_child() as tx2d:
mat.texture2 = tx2d.read_string() mat.texture2 = tx2d.read_string()
elif "TX3D" in next_header: elif "TX3D" in next_header:
with matd.read_child("TX3D") as tx3d: with matd.read_child() as tx3d:
mat.texture3 = tx3d.read_string() mat.texture3 = tx3d.read_string()
else: else:
@ -127,50 +138,50 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
next_header = modl.peak_next_header() next_header = modl.peak_next_header()
if "MTYP" in next_header: if "MTYP" in next_header:
with modl.read_child("MTYP") as mtyp: with modl.read_child() as mtyp:
model.model_type = ModelType(mtyp.read_u32()) model.model_type = ModelType(mtyp.read_u32())
elif "MNDX" in next_header: elif "MNDX" in next_header:
with modl.read_child("MNDX") as mndx: with modl.read_child() as mndx:
pass pass
elif "NAME" in next_header: elif "NAME" in next_header:
with modl.read_child("NAME") as name: with modl.read_child() as name:
model.name = name.read_string() model.name = name.read_string()
elif "PRNT" in next_header: elif "PRNT" in next_header:
with modl.read_child("PRNT") as prnt: with modl.read_child() as prnt:
model.parent = prnt.read_string() model.parent = prnt.read_string()
elif "FLGS" in next_header: elif "FLGS" in next_header:
with modl.read_child("FLGS") as flgs: with modl.read_child() as flgs:
model.hidden = flgs.read_u32() model.hidden = flgs.read_u32()
elif "TRAN" in next_header: elif "TRAN" in next_header:
with modl.read_child("TRAN") as tran: with modl.read_child() as tran:
model.transform = _read_tran(tran) model.transform = _read_tran(tran)
elif "GEOM" in next_header: elif "GEOM" in next_header:
model.geometry = [] model.geometry = []
with modl.read_child("GEOM") as geom: with modl.read_child() as geom:
next_header_modl = geom.peak_next_header() next_header_modl = geom.peak_next_header()
if "SEGM" in next_header_modl: if "SEGM" in next_header_modl:
with geom.read_child("SEGM") as segm: with geom.read_child() as segm:
model.geometry.append(_read_segm(segm, materials_list)) model.geometry.append(_read_segm(segm, materials_list))
elif "SWCI" in next_header: elif "SWCI" in next_header:
prim = CollisionPrimitive() prim = CollisionPrimitive()
with modl.read_child("SWCI") as swci: with modl.read_child() as swci:
prim.shape.value = swci.read_u32() prim.shape = CollisionPrimitiveShape(swci.read_u32())
prim.radius = swci.read_f32() prim.radius = swci.read_f32()
prim.height = swci.read_f32() prim.height = swci.read_f32()
prim.length = swci.read_f32() prim.length = swci.read_f32()
model.collisionprimitive = prim model.collisionprimitive = prim
else: else:
with modl.read_child("NULL") as unknown: with modl.read_child() as unknown:
pass pass
return model return model
@ -198,18 +209,18 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
next_header = segm.peak_next_header() next_header = segm.peak_next_header()
if "MATI" in next_header: if "MATI" in next_header:
with segm.read_child("MATI") as mati: with segm.read_child() as mati:
geometry_seg.material_name = materials_list[mati.read_u32()].name geometry_seg.material_name = materials_list[mati.read_u32()].name
elif "POSL" in next_header: elif "POSL" in next_header:
with segm.read_child("POSL") as posl: with segm.read_child() as posl:
num_positions = posl.read_u32() num_positions = posl.read_u32()
for _ in range(num_positions): for _ in range(num_positions):
geometry_seg.positions.append(Vector(posl.read_f32(3))) geometry_seg.positions.append(Vector(posl.read_f32(3)))
elif "NRML" in next_header: elif "NRML" in next_header:
with segm.read_child("NRML") as nrml: with segm.read_child() as nrml:
num_normals = nrml.read_u32() num_normals = nrml.read_u32()
for _ in range(num_positions): for _ in range(num_positions):
@ -218,21 +229,21 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
elif "CLRL" in next_header: elif "CLRL" in next_header:
geometry_seg.colors = [] geometry_seg.colors = []
with segm.read_child("CLRL") as clrl: with segm.read_child() as clrl:
num_colors = clrl.read_u32() num_colors = clrl.read_u32()
for _ in range(num_colors): for _ in range(num_colors):
geometry_seg.colors += unpack_color(clrl.read_u32()) geometry_seg.colors += unpack_color(clrl.read_u32())
elif "UV0L" in next_header: elif "UV0L" in next_header:
with segm.read_child("UV0L") as uv0l: with segm.read_child() as uv0l:
num_texcoords = uv0l.read_u32() num_texcoords = uv0l.read_u32()
for _ in range(num_texcoords): for _ in range(num_texcoords):
geometry_seg.texcoords.append(Vector(uv0l.read_f32(2))) geometry_seg.texcoords.append(Vector(uv0l.read_f32(2)))
elif "NDXL" in next_header: elif "NDXL" in next_header:
with segm.read_child("NDXL") as ndxl: with segm.read_child() as ndxl:
num_polygons = ndxl.read_u32() num_polygons = ndxl.read_u32()
for _ in range(num_polygons): for _ in range(num_polygons):
@ -240,23 +251,24 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
geometry_seg.polygons.append(polygon) geometry_seg.polygons.append(polygon)
elif "NDXT" in next_header: elif "NDXT" in next_header:
with segm.read_child("NDXT") as ndxt: with segm.read_child() as ndxt:
num_tris = ndxt.read_u32() num_tris = ndxt.read_u32()
for _ in range(num_tris): for _ in range(num_tris):
geometry_seg.triangles.append(ndxt.read_u16(3)) geometry_seg.triangles.append(ndxt.read_u16(3))
elif "STRP" in next_header: elif "STRP" in next_header:
with segm.read_child("STRP") as strp: with segm.read_child() as strp:
pass pass
if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP
segm.skip_bytes(-2) segm.skip_bytes(-2)
else: else:
with segm.read_child("NULL") as unknown: with segm.read_child() as unknown:
pass pass
return geometry_seg return geometry_seg

View File

@ -12,21 +12,18 @@ from .msh_model import *
from .msh_model_utilities import * from .msh_model_utilities import *
from .msh_utilities import * from .msh_utilities import *
from .msh_model_gather import * from .msh_model_gather import *
from .crc import *
def extract_models(models, materials_map): def extract_models(scene: Scene, materials_map):
model_map = {} model_map = {}
for model in sort_by_parent(models): for model in sort_by_parent(scene.models):
if model.model_type != ModelType.STATIC: if model.model_type == ModelType.STATIC:
new_obj = bpy.data.objects.new(model.name, None)
new_obj.empty_display_size = 1
new_obj.empty_display_type = 'PLAIN_AXES'
else:
new_mesh = bpy.data.meshes.new(model.name) new_mesh = bpy.data.meshes.new(model.name)
verts = [] verts = []
faces = [] faces = []
@ -67,10 +64,10 @@ def extract_models(models, materials_map):
new_obj = bpy.data.objects.new(new_mesh.name, new_mesh) new_obj = bpy.data.objects.new(new_mesh.name, new_mesh)
''' '''
Assign Materials - will do per segment later... Assign Materials - will do per segment later...
''' '''
if mat_name: if mat_name:
material = materials_map[mat_name] material = materials_map[mat_name]
@ -78,6 +75,12 @@ def extract_models(models, materials_map):
new_obj.data.materials[0] = material new_obj.data.materials[0] = material
else: else:
new_obj.data.materials.append(material) new_obj.data.materials.append(material)
else:
new_obj = bpy.data.objects.new(model.name, None)
new_obj.empty_display_size = 1
new_obj.empty_display_type = 'PLAIN_AXES'
@ -104,7 +107,7 @@ def extract_materials(scene: Scene) -> Dict[str,bpy.types.Material]:
new_mat.use_nodes = True new_mat.use_nodes = True
bsdf = new_mat.node_tree.nodes["Principled BSDF"] bsdf = new_mat.node_tree.nodes["Principled BSDF"]
texImage = new_mat.node_tree.nodes.new('ShaderNodeTexImage') texImage = new_mat.node_tree.nodes.new('ShaderNodeTexImage')
texImage.image = bpy.data.images.load("/Users/will/Desktop/grad.png") texImage.image = bpy.data.images.load("/Users/will/Desktop/grad.jpg")
new_mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) new_mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color'])
extracted_materials[material_name] = new_mat extracted_materials[material_name] = new_mat
@ -114,9 +117,9 @@ def extract_materials(scene: Scene) -> Dict[str,bpy.types.Material]:
def extract_scene(scene: Scene): def extract_scene(scene: Scene):
return None
matmap = extract_materials(scene) #matmap = extract_materials(scene)
extract_models(scene.models, matmap) #extract_models(scene, matmap)