From b8afa1ed105fb29e721a934bad2ddd1bf9b6c4f9 Mon Sep 17 00:00:00 2001 From: William Herald Snyder Date: Fri, 30 Oct 2020 16:59:54 -0400 Subject: [PATCH] Models with basic materials importing correctly --- addons/io_scene_swbf_msh/__init__.py | 3 +- addons/io_scene_swbf_msh/msh_material.py | 5 +- addons/io_scene_swbf_msh/msh_scene_read.py | 130 +++++++-------------- addons/io_scene_swbf_msh/msh_to_blend.py | 124 ++++++++++++++++++++ 4 files changed, 171 insertions(+), 91 deletions(-) create mode 100644 addons/io_scene_swbf_msh/msh_to_blend.py diff --git a/addons/io_scene_swbf_msh/__init__.py b/addons/io_scene_swbf_msh/__init__.py index 6165685..e432118 100644 --- a/addons/io_scene_swbf_msh/__init__.py +++ b/addons/io_scene_swbf_msh/__init__.py @@ -60,6 +60,7 @@ from .msh_scene import create_scene from .msh_scene_save import save_scene from .msh_scene_read import read_scene from .msh_material_properties import * +from .msh_to_blend import * class ExportMSH(Operator, ExportHelper): """ Export the current scene as a SWBF .msh file. """ @@ -133,7 +134,7 @@ class ImportMSH(Operator, ImportHelper): def execute(self, context): with open(self.filepath, 'rb') as input_file: - read_scene(input_file) + extract_scene(read_scene(input_file)) return {'FINISHED'} def menu_func_import(self, context): diff --git a/addons/io_scene_swbf_msh/msh_material.py b/addons/io_scene_swbf_msh/msh_material.py index ed7867b..560e3bb 100644 --- a/addons/io_scene_swbf_msh/msh_material.py +++ b/addons/io_scene_swbf_msh/msh_material.py @@ -32,8 +32,9 @@ class MaterialFlags(Flag): @dataclass class Material: - """ Data class representing a .msh material. - Intended to be stored in a dictionary so name is missing. """ + """ Data class representing a .msh material.""" + + name: str = "" specular_color: Color = Color((1.0, 1.0, 1.0)) rendertype: Rendertype = Rendertype.NORMAL diff --git a/addons/io_scene_swbf_msh/msh_scene_read.py b/addons/io_scene_swbf_msh/msh_scene_read.py index cae2d15..9faa78f 100644 --- a/addons/io_scene_swbf_msh/msh_scene_read.py +++ b/addons/io_scene_swbf_msh/msh_scene_read.py @@ -1,4 +1,4 @@ -""" Contains functions for saving a Scene to a .msh file. """ +""" Contains functions for extracting a scene from a .msh file""" from itertools import islice from typing import Dict @@ -14,42 +14,52 @@ def read_scene(input_file) -> Scene: scene = Scene() scene.models = [] + scene.materials = {} with Reader(file=input_file, chunk_id="HEDR") as hedr: - with hedr.read_child("MSH2") as msh2: - with msh2.read_child("SINF") as sinf: - pass + while hedr.could_have_child(): - materials_list: List[str] = [] + next_header = hedr.peak_next_header() - with msh2.read_child("MATL") as matl: - materials_list = _read_matl_and_get_materials_list(matl) + if "MSH2" in next_header: - while ("MODL" in msh2.peak_next_header()): - with msh2.read_child("MODL") as modl: - scene.models.append(_read_modl(modl, materials_list)) + with hedr.read_child("MSH2") as msh2: - mats_dict = {} - for i,mat in enumerate(materials_list): - mats_dict["Material" + str(i)] = mat + materials_list = [] - scene.materials = mats_dict + while (msh2.could_have_child()): - #with hedr.read_child("ANM2") as anm2: #simple for now - # for anim in scene.anims: - # _write_anm2(anm2, anim) + next_header = msh2.peak_next_header() - #with hedr.read_child("CL1L"): - # pass + if "SINF" in next_header: + with msh2.read_child("SINF") as sinf: + pass + elif "MATL" in next_header: + with msh2.read_child("MATL") as matl: + materials_list += _read_matl_and_get_materials_list(matl) + for i,mat in enumerate(materials_list): + scene.materials[mat.name] = mat + elif "MODL" in next_header: + while ("MODL" in msh2.peak_next_header()): + with msh2.read_child("MODL") as modl: + scene.models.append(_read_modl(modl, materials_list)) + + else: + with hedr.read_child("NULL") as unknown: + pass + + else: + with hedr.read_child("NULL") as unknown: + pass return scene -def _read_matl_and_get_materials_list(matl: Reader) -> List[str]: - materials_list: List[str] = [] +def _read_matl_and_get_materials_list(matl: Reader) -> List[Material]: + materials_list: List[Material] = [] num_mats = matl.read_u32() @@ -72,7 +82,6 @@ def _read_matd(matd: Reader) -> Material: if "NAME" in next_header: with matd.read_child("NAME") as name: mat.name = name.read_string() - print(matd.indent + "Got a new material: " + mat.name) elif "DATA" in next_header: with matd.read_child("DATA") as data: @@ -109,7 +118,7 @@ def _read_matd(matd: Reader) -> Material: return mat -def _read_modl(modl: Reader, materials_list: List[str]) -> Model: +def _read_modl(modl: Reader, materials_list: List[Material]) -> Model: model = Model() @@ -119,7 +128,7 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model: if "MTYP" in next_header: with modl.read_child("MTYP") as mtyp: - model.model_type = mtyp.read_u32() + model.model_type = ModelType(mtyp.read_u32()) elif "MNDX" in next_header: with modl.read_child("MNDX") as mndx: @@ -128,7 +137,6 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model: elif "NAME" in next_header: with modl.read_child("NAME") as name: model.name = name.read_string() - print(modl.indent + "New model: " + model.name) elif "PRNT" in next_header: with modl.read_child("PRNT") as prnt: @@ -151,13 +159,7 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model: if "SEGM" in next_header_modl: with geom.read_child("SEGM") as segm: model.geometry.append(_read_segm(segm, materials_list)) - ''' - if model.model_type == ModelType.SKIN: - with modl.read_child("ENVL") as envl: - envl.write_u32(len(scene.models)) - for i in range(len(scene.models)): - envl.write_u32(i) - ''' + elif "SWCI" in next_header: prim = CollisionPrimitive() with modl.read_child("SWCI") as swci: @@ -171,6 +173,7 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model: with modl.read_child("NULL") as unknown: pass + return model def _read_tran(tran: Reader) -> ModelTransform: @@ -178,14 +181,15 @@ def _read_tran(tran: Reader) -> ModelTransform: xform = ModelTransform() tran.skip_bytes(4 * 3) #ignore scale - xform.rotation = Quaternion(tran.read_f32(4)) - xform.position = Vector(tran.read_f32(3)) + + rot = tran.read_f32(4) + xform.rotation = Quaternion((rot[3], rot[0], rot[1], rot[2])) + xform.translation = Vector(tran.read_f32(3)) return xform - -def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment: +def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: geometry_seg = GeometrySegment() @@ -195,7 +199,7 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment: if "MATI" in next_header: with segm.read_child("MATI") as mati: - geometry_seg.material_name = materials_list[mati.read_u32()] + geometry_seg.material_name = materials_list[mati.read_u32()].name elif "POSL" in next_header: with segm.read_child("POSL") as posl: @@ -211,15 +215,6 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment: for _ in range(num_positions): geometry_seg.normals.append(Vector(nrml.read_f32(3))) - elif "WGHT" in next_header: - geometry_seg.weights = [] - - with segm.read_child("WGHT") as wght: - num_boneweights = wght.read_u32() - - for _ in range(num_boneweights): - geometry_seg.weights.append((wght.read_u32(), wght.read_f32())) - elif "CLRL" in next_header: geometry_seg.colors = [] @@ -255,7 +250,7 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment: with segm.read_child("STRP") as strp: pass - if segm.read_u16 != 0: #trailing 0 bug + if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP segm.skip_bytes(-2) else: @@ -265,44 +260,3 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment: return geometry_seg - -''' - - -def _write_anm2(anm2: Writer, anim: Animation): - - with anm2.read_child("CYCL") as cycl: - - cycl.write_u32(1) - cycl.write_string(anim.name) - - for _ in range(63 - len(anim.name)): - cycl.write_u8(0) - - cycl.write_f32(10.0) #test framerate - cycl.write_u32(0) #what does play style refer to? - cycl.write_u32(0, 20) #first frame indices - - - with anm2.read_child("KFR3") as kfr3: - - kfr3.write_u32(len(anim.bone_transforms.keys())) - - for boneName in anim.bone_transforms.keys(): - kfr3.write_u32(crc(boneName)) - kfr3.write_u32(0) #what is keyframe type? - - kfr3.write_u32(21, 21) #basic testing - - for i, xform in enumerate(anim.bone_transforms[boneName]): - kfr3.write_u32(i) - kfr3.write_f32(xform.translation.x, xform.translation.y, xform.translation.z) - - for i, xform in enumerate(anim.bone_transforms[boneName]): - kfr3.write_u32(i) - kfr3.write_f32(xform.rotation.x, xform.rotation.y, xform.rotation.z, xform.rotation.w) - - -''' - - diff --git a/addons/io_scene_swbf_msh/msh_to_blend.py b/addons/io_scene_swbf_msh/msh_to_blend.py new file mode 100644 index 0000000..a5c3495 --- /dev/null +++ b/addons/io_scene_swbf_msh/msh_to_blend.py @@ -0,0 +1,124 @@ +""" Gathers the Blender objects from the current scene and returns them as a list of + Model objects. """ + +import bpy +import bmesh +import math +from enum import Enum +from typing import List, Set, Dict, Tuple +from itertools import zip_longest +from .msh_scene import Scene +from .msh_model import * +from .msh_model_utilities import * +from .msh_utilities import * +from .msh_model_gather import * + + + +def extract_models(models, materials_map): + + model_map = {} + + for model in sort_by_parent(models): + + 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) + verts = [] + faces = [] + offset = 0 + + mat_name = "" + + for i,seg in enumerate(model.geometry): + + if i == 0: + mat_name = seg.material_name + + verts += [tuple(convert_vector_space(v)) for v in seg.positions] + faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles] + + offset += len(seg.positions) + + new_mesh.from_pydata(verts, [], faces) + new_mesh.update() + new_mesh.validate() + + + edit_mesh = bmesh.new() + edit_mesh.from_mesh(new_mesh) + + uvlayer = edit_mesh.loops.layers.uv.verify() + + for edit_mesh_face in edit_mesh.faces: + mesh_face = faces[edit_mesh_face.index] + + for i,loop in enumerate(edit_mesh_face.loops): + texcoord = seg.texcoords[mesh_face[i]] + loop[uvlayer].uv = tuple([texcoord.x, texcoord.y]) + + edit_mesh.to_mesh(new_mesh) + edit_mesh.free() + + + new_obj = bpy.data.objects.new(new_mesh.name, new_mesh) + + ''' + Assign Materials - will do per segment later... + ''' + + if mat_name: + material = materials_map[mat_name] + + if new_obj.data.materials: + new_obj.data.materials[0] = material + else: + new_obj.data.materials.append(material) + + + + model_map[model.name] = new_obj + + if model.parent: + new_obj.parent = model_map[model.parent] + + new_obj.location = convert_vector_space(model.transform.translation) + new_obj.rotation_mode = "QUATERNION" + new_obj.rotation_quaternion = convert_rotation_space(model.transform.rotation) + + bpy.context.collection.objects.link(new_obj) + + + +def extract_materials(scene: Scene) -> Dict[str,bpy.types.Material]: + + extracted_materials = {} + + for material_name in scene.materials.keys(): + + new_mat = bpy.data.materials.new(name=material_name) + new_mat.use_nodes = True + bsdf = new_mat.node_tree.nodes["Principled BSDF"] + texImage = new_mat.node_tree.nodes.new('ShaderNodeTexImage') + texImage.image = bpy.data.images.load("/Users/will/Desktop/grad.png") + new_mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) + + extracted_materials[material_name] = new_mat + + return extracted_materials + + + +def extract_scene(scene: Scene): + + matmap = extract_materials(scene) + extract_models(scene.models, matmap) + + + + +