From 5692a60907e036b8de973b90cbea3c75e8e7b5b3 Mon Sep 17 00:00:00 2001 From: William Herald Snyder Date: Thu, 29 Sep 2022 15:18:58 -0400 Subject: [PATCH] When exporting, if an object has a parent bone with the same name, add its geometry to the parent bone's model and discard the object itself. --- addons/io_scene_swbf_msh/msh_model_gather.py | 102 +++++++++++------- .../msh_skeleton_utilities.py | 8 ++ 2 files changed, 69 insertions(+), 41 deletions(-) diff --git a/addons/io_scene_swbf_msh/msh_model_gather.py b/addons/io_scene_swbf_msh/msh_model_gather.py index d9d6df7..6106bcf 100644 --- a/addons/io_scene_swbf_msh/msh_model_gather.py +++ b/addons/io_scene_swbf_msh/msh_model_gather.py @@ -24,50 +24,62 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool models_list: List[Model] = [] - armature_found = None + # Composite bones are bones which have geometry. + # If a child object has the same name, it will take said child's geometry. - for uneval_obj in select_objects(export_target): - if uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents: + # Pure bones are just bones and after all objects are explored the only + # entries remaining in this dict will be bones without geometry. + pure_bones_from_armature = {} + + objects_to_export = select_objects(export_target) + + for uneval_obj in objects_to_export: + if uneval_obj.type == "ARMATURE": + armature_found = uneval_obj.evaluated_get(depsgraph) if apply_modifiers else uneval_obj + pure_bones_from_armature = expand_armature(armature_found) + break + + for uneval_obj in objects_to_export: + if uneval_obj.type == "ARMATURE" or (uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents): continue - if apply_modifiers: - obj = uneval_obj.evaluated_get(depsgraph) - else: - obj = uneval_obj + obj = uneval_obj.evaluated_get(depsgraph) if apply_modifiers else uneval_obj check_for_bad_lod_suffix(obj) - if obj.type == "ARMATURE": - models_list += expand_armature(obj) - armature_found = obj - continue - - model = Model() - model.name = obj.name - model.model_type = get_model_type(obj, skeleton_only) - model.hidden = get_is_model_hidden(obj) - - transform = obj.matrix_local - - if obj.parent_bone: - model.parent = obj.parent_bone - - # matrix_local, when called on an armature child also parented to a bone, appears to be broken. - # At the very least, the results contradict the docs... - armature_relative_transform = obj.parent.matrix_world.inverted() @ obj.matrix_world - transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ armature_relative_transform - + # Test for a mesh object that is actually a BONE (shares name with bone_parent) + # If so, we inject geometry into the BONE while not modifying it's transform/name + if obj.parent_bone and obj.parent_bone in pure_bones_from_armature: + model = pure_bones_from_armature[obj.parent_bone] + # Since we found a composite bone, removed it from the dict of pure bones + pure_bones_from_armature.pop(obj.parent_bone) else: - if obj.parent is not None: - if obj.parent.type == "ARMATURE": - model.parent = obj.parent.parent.name if obj.parent.parent else "" - transform = obj.parent.matrix_local @ transform - else: - model.parent = obj.parent.name + model = Model() + model.name = obj.name + model.model_type = get_model_type(obj, skeleton_only) + model.hidden = get_is_model_hidden(obj) - local_translation, local_rotation, _ = transform.decompose() - model.transform.rotation = convert_rotation_space(local_rotation) - model.transform.translation = convert_vector_space(local_translation) + transform = obj.matrix_local + + if obj.parent_bone: + model.parent = obj.parent_bone + + # matrix_local, when called on an armature child also parented to a bone, appears to be broken. + # At the very least, the results contradict the docs... + armature_relative_transform = obj.parent.matrix_world.inverted() @ obj.matrix_world + transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ armature_relative_transform + + else: + if obj.parent is not None: + if obj.parent.type == "ARMATURE": + model.parent = obj.parent.parent.name if obj.parent.parent else "" + transform = obj.parent.matrix_local @ transform + else: + model.parent = obj.parent.name + + local_translation, local_rotation, _ = transform.decompose() + model.transform.rotation = convert_rotation_space(local_rotation) + model.transform.translation = convert_vector_space(local_translation) if obj.type in MESH_OBJECT_TYPES: @@ -92,9 +104,11 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool if get_is_collision_primitive(obj): model.collisionprimitive = get_collision_primitive(obj) - models_list.append(model) + # We removed all composite bones after looking through the objects, + # so the bones left are all pure and we add them all here. + models_list += pure_bones_from_armature.values() return (models_list, armature_found) @@ -371,11 +385,17 @@ def select_objects(export_target: str) -> List[bpy.types.Object]: -def expand_armature(armature: bpy.types.Object) -> List[Model]: + + + + + + +def expand_armature(armature: bpy.types.Object) -> Dict[str, Model]: proper_BONES = get_real_BONES(armature) - bones: List[Model] = [] + bones: Dict[str, Model] = {} for bone in armature.data.bones: model = Model() @@ -390,7 +410,7 @@ def expand_armature(armature: bpy.types.Object) -> List[Model]: # set model parent to armature parent if there is one else: - bone_world_matrix = armature.matrix_world @ transform + bone_world_matrix = get_bone_world_matrix(armature, bone.name) parent_obj = None for child_obj in armature.original.children: @@ -418,6 +438,6 @@ def expand_armature(armature: bpy.types.Object) -> List[Model]: model.transform.rotation = convert_rotation_space(local_rotation) model.transform.translation = convert_vector_space(local_translation) - bones.append(model) + bones[bone.name] = model return bones diff --git a/addons/io_scene_swbf_msh/msh_skeleton_utilities.py b/addons/io_scene_swbf_msh/msh_skeleton_utilities.py index f6f0883..15b63e6 100644 --- a/addons/io_scene_swbf_msh/msh_skeleton_utilities.py +++ b/addons/io_scene_swbf_msh/msh_skeleton_utilities.py @@ -12,6 +12,14 @@ from .msh_model_utilities import * from .crc import * +def get_bone_world_matrix(armature: bpy.types.Object, bone_name: str) -> Matrix: + if bone_name in armature.data.bones: + return armature.matrix_world @ armature.data.bones[bone_name].matrix_local + else: + return None + + + def has_preserved_skeleton(armature : bpy.types.Armature): return len(armature.data.swbf_msh_skel) > 0