diff --git a/addons/io_scene_swbf_msh/msh_anim_gather.py b/addons/io_scene_swbf_msh/msh_anim_gather.py index e525042..631839f 100644 --- a/addons/io_scene_swbf_msh/msh_anim_gather.py +++ b/addons/io_scene_swbf_msh/msh_anim_gather.py @@ -40,14 +40,14 @@ def gather_animdata(armature: bpy.types.Armature) -> List[Animation]: xform.translation = Vector((vt.x * -1.0, vt.y, vt.z)) xform.rotation = convert_rotation_space(bone.rotation_quaternion) - - ''' + + ''' xform.translation = bone.location xform.rotation = bone.rotation_quaternion anim_data.bone_transforms[bone.name].append(xform) - ''' - + ''' + return [anim_data] diff --git a/addons/io_scene_swbf_msh/msh_model.py b/addons/io_scene_swbf_msh/msh_model.py index 6e0e13d..31f04a0 100644 --- a/addons/io_scene_swbf_msh/msh_model.py +++ b/addons/io_scene_swbf_msh/msh_model.py @@ -34,11 +34,11 @@ class GeometrySegment: material_name: str = "" positions: List[Vector] = field(default_factory=list) - weights: List[Tuple[int, float]] = None normals: List[Vector] = field(default_factory=list) colors: List[List[float]] = None texcoords: List[Vector] = field(default_factory=list) - # TODO: Skin support. + + weights: List[Tuple[int, float]] = None polygons: List[List[int]] = field(default_factory=list) triangles: List[List[int]] = field(default_factory=list) @@ -67,11 +67,12 @@ class Model: geometry: List[GeometrySegment] = None collisionprimitive: CollisionPrimitive = None + vgroups_to_modelnames_map : Dict[int, str] = None + @dataclass class Animation: """ Class representing 'CYCL' + 'KFR3' sections in a .msh file """ name: str = "open" - anim_type: str = "HardSkinned" bone_transforms: Dict[str, List[ModelTransform]] = field(default_factory=dict) diff --git a/addons/io_scene_swbf_msh/msh_model_gather.py b/addons/io_scene_swbf_msh/msh_model_gather.py index b2656c3..7b6e7fd 100644 --- a/addons/io_scene_swbf_msh/msh_model_gather.py +++ b/addons/io_scene_swbf_msh/msh_model_gather.py @@ -4,7 +4,7 @@ import bpy import math from enum import Enum -from typing import List, Set, Dict, Tuple +from typing import List, Set, Dict, Tuple, Set from itertools import zip_longest from .msh_model import * from .msh_model_utilities import * @@ -22,11 +22,7 @@ def gather_models(apply_modifiers: bool, export_target: str) -> List[Model]: parents = create_parents_set() models_list: List[Model] = [] - model_indices: Dict[str, int] = {} - - for i, uneval_obj in enumerate(select_objects(export_target)): - if not (uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents): - model_indices[uneval_obj.name] = i + skeleton: bpy.types.Armature = None for uneval_obj in select_objects(export_target): if uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents: @@ -50,19 +46,23 @@ def gather_models(apply_modifiers: bool, export_target: str) -> List[Model]: if obj.parent is not None: if obj.parent.type == "ARMATURE": - model.parent = obj.parent.parent.name - else: - model.parent = obj.parent.name + skeleton = obj.parent + + parent_bone_name = obj.parent_bone + if parent_bone_name == "": + model.parent = obj.parent.parent + else: + model.parent = parent_bone_name + + if model.model_type == ModelType.SKIN: + model.vgroups_to_modelnames_map = {} + for i, vgroup in enumerate(obj.vertex_groups): + vgroups_to_modelnames_map[i] = vgroup.name if obj.type in MESH_OBJECT_TYPES: - mesh = obj.to_mesh() - - vgroups_to_indices = {} - for i, vgroup in enumerate(obj.vertex_groups): - vgroups_to_indices[i] = model_indices[vgroup.name] - - model.geometry = create_mesh_geometry(mesh, vgroups_to_indices) + mesh = obj.to_mesh() + model.geometry = create_mesh_geometry(mesh, model.model_type == ModelType.SKIN) obj.to_mesh_clear() _, _, world_scale = obj.matrix_world.decompose() @@ -80,6 +80,30 @@ def gather_models(apply_modifiers: bool, export_target: str) -> List[Model]: models_list.append(model) + + for bone in skeleton.data.bones: + + model = Model() + model.name = bone.name + model.model_type = ModelType.NULL + model.hidden = False + + local_translation, local_rotation, _ = bone.matrix_local.decompose() + model.transform.rotation = convert_rotation_space(local_rotation) + model.transform.translation = convert_vector_space(local_translation) + + parent_name = bone.parent + if parent_name is not None: + model.parent = parent_name + else: + if skeleton.parent is not None: + model.parent = skeleton.parent.name + else: + model.parent = None + + models_list.append(model) + + return models_list def create_parents_set() -> Set[str]: @@ -94,7 +118,7 @@ def create_parents_set() -> Set[str]: return parents -def create_mesh_geometry(mesh: bpy.types.Mesh, vgrps_to_indices : Dict[int, int]) -> List[GeometrySegment]: +def create_mesh_geometry(mesh: bpy.types.Mesh, is_skinned : bool) -> List[GeometrySegment]: """ Creates a list of GeometrySegment objects from a Blender mesh. Does NOT create triangle strips in the GeometrySegment however. """ @@ -115,10 +139,6 @@ def create_mesh_geometry(mesh: bpy.types.Mesh, vgrps_to_indices : Dict[int, int] for segment in segments: segment.colors = [] - if vgrps_to_indices: - for segment in segments: - segment.weights = [] - for segment, material in zip(segments, mesh.materials): segment.material_name = material.name @@ -172,13 +192,15 @@ def create_mesh_geometry(mesh: bpy.types.Mesh, vgrps_to_indices : Dict[int, int] segment.positions.append(convert_vector_space(mesh.vertices[vertex_index].co)) segment.normals.append(convert_vector_space(vertex_normal)) - if vgrps_to_indices is not None: - for i,grp_el in mesh.vertices[vertex_index].groups: - segment.weights.append(tuple(vgrps_to_indices[grp_el.group], grp_el.weight)) - if i > 3: - break - while (i < 3): - segment.weights.append(tuple(0,0.0)) + if is_skinned: + for i,grp_el in mesh.vertices[vertex_index].groups: + segment.weights.append(tuple(grp_el.group, grp_el.weight)) + print("Adding weight to group {grp_el.group} of value {grp_el.weight}") + if i > 3: #will have to look into aramture/skin settings for limiting envolopes to 4 weights... + break + while i < 3: + segment.weights.append(tuple(0,0.0)) + i+=1 if mesh.uv_layers.active is None: segment.texcoords.append(Vector((0.0, 0.0))) diff --git a/addons/io_scene_swbf_msh/msh_scene.py b/addons/io_scene_swbf_msh/msh_scene.py index 05debe8..7fa5930 100644 --- a/addons/io_scene_swbf_msh/msh_scene.py +++ b/addons/io_scene_swbf_msh/msh_scene.py @@ -46,6 +46,7 @@ class Scene: models: List[Model] = field(default_factory=list) anims: List[Animation] = field(default_factory=list) + def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str) -> Scene: """ Create a msh Scene from the active Blender scene. """ @@ -69,9 +70,23 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t if has_multiple_root_models(scene.models): scene.models = reparent_model_roots(scene.models) + + #now that we've collected all models, we should remap WGHT indices... + names_to_indices = {} + for i,model in enumerate(scene.models): + names_to_indices[model.name] = i; + + for model in scene.models: + if model.model_type == ModelType.SKIN: + for segment in model.geometry: + for i in range(len(segment.weights)): + vgroup_index = segment.weights[i][0] + segment.weights[i][0] = names_to_indices[model.vgroups_to_modelnames_map[vgroup_index]] + + scene.materials = remove_unused_materials(scene.materials, scene.models) - scene.anims = gather_animdata(bpy.context.scene.objects["Armature"]) + #scene.anims = gather_animdata(bpy.context.scene.objects["Armature"]) return scene diff --git a/addons/io_scene_swbf_msh/msh_scene_save.py b/addons/io_scene_swbf_msh/msh_scene_save.py index dfdec59..2fd0945 100644 --- a/addons/io_scene_swbf_msh/msh_scene_save.py +++ b/addons/io_scene_swbf_msh/msh_scene_save.py @@ -26,11 +26,11 @@ def save_scene(output_file, scene: Scene): for index, model in enumerate(scene.models): with msh2.create_child("MODL") as modl: - _write_modl(modl, model, index, material_index) + _write_modl(modl, model, index, material_index, scene) - with hedr.create_child("ANM2") as anm2: #simple for now - for anim in scene.anims: - _write_anm2(anm2, anim) + #with hedr.create_child("ANM2") as anm2: #simple for now + # for anim in scene.anims: + # _write_anm2(anm2, anim) with hedr.create_child("CL1L"): pass @@ -103,7 +103,7 @@ def _write_matd(matd: Writer, material_name: str, material: Material): with matd.create_child("TX3D") as tx3d: tx3d.write_string(material.texture3) -def _write_modl(modl: Writer, model: Model, index: int, material_index: Dict[str, int]): +def _write_modl(modl: Writer, model: Model, index: int, material_index: Dict[str, int], scene: Scene): with modl.create_child("MTYP") as mtyp: mtyp.write_u32(model.model_type.value) @@ -129,9 +129,12 @@ def _write_modl(modl: Writer, model: Model, index: int, material_index: Dict[str for segment in model.geometry: with geom.create_child("SEGM") as segm: _write_segm(segm, segment, material_index) - if model.type = ModelType.SKIN: - with modl.create_child("ENVL") as envl: - + + if model.model_type == ModelType.SKIN: + with modl.create_child("ENVL") as envl: + envl.write_u32(len(scene.models)) + for i in range(len(scene.models)): + envl.write_u32(i) if model.collisionprimitive is not None: with modl.create_child("SWCI") as swci: @@ -162,6 +165,13 @@ def _write_segm(segm: Writer, segment: GeometrySegment, material_index: Dict[str for normal in segment.normals: nrml.write_f32(normal.x, normal.y, normal.z) + if segment.weights is not None: + with segm.create_child("WGHT") as wght: + wght.write_u32(len(segment.weights) / 4) + for weight in segment.weights: + wght.write_u32(weight[0]) + wght.write_f32(weight[1]) + if segment.colors is not None: with segm.create_child("CLRL") as clrl: clrl.write_u32(len(segment.colors)) @@ -207,7 +217,7 @@ def _write_anm2(anm2: Writer, anim: Animation): cycl.write_u32(1) cycl.write_string(anim.name) - for _ in range(64 - (len(anim.name) + 1)): + for _ in range(63 - len(anim.name)): cycl.write_u8(0) cycl.write_f32(10.0) #test framerate