diff --git a/addons/io_scene_swbf_msh/msh_model.py b/addons/io_scene_swbf_msh/msh_model.py index c23c5c5..bb052eb 100644 --- a/addons/io_scene_swbf_msh/msh_model.py +++ b/addons/io_scene_swbf_msh/msh_model.py @@ -2,7 +2,7 @@ saved to a .msh file. """ from dataclasses import dataclass, field -from typing import List +from typing import List, Tuple from enum import Enum from mathutils import Vector, Quaternion @@ -44,6 +44,8 @@ class GeometrySegment: triangles: List[List[int]] = field(default_factory=list) triangle_strips: List[List[int]] = None + weights: List[List[Tuple[int, float]]] = None + @dataclass class CollisionPrimitive: """ Class representing a 'SWCI' section in a .msh file. """ diff --git a/addons/io_scene_swbf_msh/msh_model_utilities.py b/addons/io_scene_swbf_msh/msh_model_utilities.py index 1547697..98ae83e 100644 --- a/addons/io_scene_swbf_msh/msh_model_utilities.py +++ b/addons/io_scene_swbf_msh/msh_model_utilities.py @@ -5,6 +5,16 @@ from .msh_model import * from .msh_utilities import * from mathutils import Vector, Matrix + +def convert_vector_space_(vec: Vector) -> Vector: + return Vector((-vec.x, vec.z, vec.y)) + +def convert_rotation_space_(quat: Quaternion) -> Quaternion: + return Quaternion((-quat.w, quat.x, -quat.z, -quat.y)) + +def model_transform_to_matrix(transform: ModelTransform): + return Matrix.Translation(convert_vector_space_(transform.translation)) @ convert_rotation_space_(transform.rotation).to_matrix().to_4x4() + def scale_segments(scale: Vector, segments: List[GeometrySegment]): """ Scales are positions in the GeometrySegment list. """ diff --git a/addons/io_scene_swbf_msh/msh_reader.py b/addons/io_scene_swbf_msh/msh_reader.py index 3203955..f81a077 100644 --- a/addons/io_scene_swbf_msh/msh_reader.py +++ b/addons/io_scene_swbf_msh/msh_reader.py @@ -19,7 +19,7 @@ class Reader: padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0 self.end_pos = self.size_pos + padding_length + self.size + 8 - print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos)) + #print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos)) return self @@ -28,7 +28,7 @@ class Reader: if self.size > self.MAX_SIZE: raise OverflowError(f".msh file overflowed max size. size = {self.size} MAX_SIZE = {self.MAX_SIZE}") - print(self.indent + "End " + self.header) + #print(self.indent + "End " + self.header) self.file.seek(self.end_pos) diff --git a/addons/io_scene_swbf_msh/msh_scene_read.py b/addons/io_scene_swbf_msh/msh_scene_read.py index b774d5a..b4d6462 100644 --- a/addons/io_scene_swbf_msh/msh_scene_read.py +++ b/addons/io_scene_swbf_msh/msh_scene_read.py @@ -57,11 +57,12 @@ def read_scene(input_file) -> Scene: 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: ") + #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)) + pass + #print("\t" + model.name + " with type: " + str(model.model_type)) elif "ANM2" in next_header: with hedr.read_child() as anm2: @@ -70,11 +71,7 @@ def read_scene(input_file) -> Scene: else: with hedr.read_child() as null: pass - - print("Models indexed by ENVLs: ") - for envl_index in set(envls): - print("\t" + scene.models[envl_index].name) - + return scene @@ -285,9 +282,24 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: segm.skip_bytes(-2) elif "WGHT" in next_header: - with segm.read_child() as null: + with segm.read_child() as wght: pass + ''' + geometry_seg.weights = [] + num_weights = wght.read_u32() + + for _ in range(num_weights): + weight_set = [] + for _ in range(4): + index = wght.read_u32() + value = wght.read_f32() + + if value > 0.000001: + weight_set.append((index,value)) + + geometry_seg.weights.append(weight_set) + ''' else: with segm.read_child() as unknown: pass diff --git a/addons/io_scene_swbf_msh/msh_to_blend.py b/addons/io_scene_swbf_msh/msh_to_blend.py index c2715ce..a768cc5 100644 --- a/addons/io_scene_swbf_msh/msh_to_blend.py +++ b/addons/io_scene_swbf_msh/msh_to_blend.py @@ -18,6 +18,102 @@ import os + +def refined_skeleton_to_armature(refined_skeleton : List[Model], model_map): + + armature = bpy.data.armatures.new("skeleton") + armature_obj = bpy.data.objects.new("skeleton", armature) + + bpy.context.view_layer.active_layer_collection.collection.objects.link(armature_obj) + armature_obj.select_set(True) + + bpy.context.view_layer.objects.active = armature_obj + bpy.ops.object.mode_set(mode='EDIT') + + for bone in refined_skeleton: + + edit_bone = armature.edit_bones.new(bone.name) + + if bone.parent: + edit_bone.parent = armature.edit_bones[bone.parent] + + edit_bone.head = model_map[bone.name].matrix_world.translation + + bone_children = [b for b in get_model_children(bone, refined_skeleton)] + + if len(bone_children) > 0: + edit_bone.tail = Vector((0.0,0.0,0.0)) + for bone_child in bone_children: + edit_bone.tail += model_map[bone_child.name].matrix_world.translation + edit_bone.tail = edit_bone.tail / len(bone_children) + else: + edit_bone.tail = model_map[bone.name].matrix_world @ Vector((-0.2,0.0,0.0)) + + + bpy.ops.object.mode_set(mode='OBJECT') + armature_obj.select_set(True) + bpy.context.view_layer.update() + + + + + + +def extract_refined_skeleton(scene: Scene): + + model_dict = {} + children_dict = {} + skeleton_models = [] + + for model in scene.models: + model_dict[model.name] = model + children_dict[model.name] = [] + + for model in scene.models: + if model.parent: + children_dict[model.parent].append(model) + + if crc(model.name) in scene.skeleton: + skeleton_models.append(model) + + refined_skeleton_models = [] + + for bone in skeleton_models: + + if bone.parent: + + curr_ancestor = model_dict[bone.parent] + stacked_transform = model_transform_to_matrix(bone.transform) + + while True: + + if crc(curr_ancestor.name) in scene.skeleton or "dummyroot" in curr_ancestor.name.lower(): + new_model = Model() + new_model.name = bone.name + new_model.parent = curr_ancestor.name if "dummyroot" not in curr_ancestor.name.lower() else "" + + loc, rot, _ = stacked_transform.decompose() + + new_model.transform.rotation = rot + new_model.transform.translation = loc + + refined_skeleton_models.append(new_model) + break + + else: + curr_ancestor = model_dict[curr_ancestor.parent] + stacked_transform = model_transform_to_matrix(curr_ancestor.transform) @ stacked_transform + + return sort_by_parent(refined_skeleton_models) + + + + + + + + + def extract_models(scene: Scene, materials_map): model_map = {} @@ -25,7 +121,13 @@ def extract_models(scene: Scene, materials_map): for model in sort_by_parent(scene.models): new_obj = None - if model.name.startswith("p_") or "collision" in model.name: + if "bone_l_toe" in model.name: + loc = get_model_world_matrix(model, scene.models).translation + print("World bone_l_toe: " + str(loc)) + + + + if model.name.startswith("p_") or "collision" in model.name or model.name.startswith("c_") or model.name.startswith("sv_"): continue if model.model_type == ModelType.STATIC or model.model_type == ModelType.SKIN: @@ -37,37 +139,47 @@ def extract_models(scene: Scene, materials_map): mat_name = "" + full_texcoords = [] + 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] + + if seg.texcoords is not None: + full_texcoords += seg.texcoords + else: + full_texcoords += [(0.0,0.0) for _ in range(len(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) + + if len(full_texcoords) > 0: - uvlayer = edit_mesh.loops.layers.uv.verify() + edit_mesh = bmesh.new() + edit_mesh.from_mesh(new_mesh) - for edit_mesh_face in edit_mesh.faces: - mesh_face = faces[edit_mesh_face.index] + uvlayer = edit_mesh.loops.layers.uv.verify() - for i,loop in enumerate(edit_mesh_face.loops): - texcoord = seg.texcoords[mesh_face[i]] - loop[uvlayer].uv = tuple([texcoord.x, texcoord.y]) + for edit_mesh_face in edit_mesh.faces: + mesh_face = faces[edit_mesh_face.index] - edit_mesh.to_mesh(new_mesh) - edit_mesh.free() - ''' + for i,loop in enumerate(edit_mesh_face.loops): + + texcoord = full_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) @@ -102,6 +214,8 @@ def extract_models(scene: Scene, materials_map): bpy.context.collection.objects.link(new_obj) + return model_map + def extract_materials(folder_path: str, scene: Scene) -> Dict[str,bpy.types.Material]: @@ -135,7 +249,13 @@ def extract_scene(filepath: str, scene: Scene): folder = os.path.join(os.path.dirname(filepath),"") matmap = extract_materials(folder,scene) - extract_models(scene, matmap) + + model_map = extract_models(scene, matmap) + + + skel = extract_refined_skeleton(scene) + refined_skeleton_to_armature(skel, model_map) +