From 463e3e45c615d6280e36a54f6a1e4a88d66cc8d3 Mon Sep 17 00:00:00 2001 From: georgeto Date: Sun, 24 Mar 2024 18:29:46 +0100 Subject: [PATCH] Make bones included in xmot export configurable --- g3blend/operators/io_export_xmot.py | 21 +++++++++++++++++- g3blend/ui/io_export_xmot.py | 34 ++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/g3blend/operators/io_export_xmot.py b/g3blend/operators/io_export_xmot.py index 23386ae..0bb3c97 100644 --- a/g3blend/operators/io_export_xmot.py +++ b/g3blend/operators/io_export_xmot.py @@ -16,9 +16,16 @@ logger = logging.getLogger(__name__) +# These are the only slots commonly used animations. +COMMONLY_USED_SLOTS = { + 'Slot_LeftHand_Weapon', # relevant for everything except faces + 'Slot_RightHand_Weapon', # relevant for everything except faces + 'Slot_Beard', # relevant for faces +} + def save_xmot(context: bpy.types.Context, filepath: str, arm_obj: bpy.types.Object, global_scale: float, - global_matrix: Matrix, ignore_transform: bool, use_selection: bool): + global_matrix: Matrix, ignore_transform: bool, bone_filter: str): xmot = Xmot() xmot.resource_size = 0 xmot.resource_priority = 0.0 @@ -33,6 +40,10 @@ def save_xmot(context: bpy.types.Context, filepath: str, arm_obj: bpy.types.Obje if arm_obj is None: raise ValueError('No armature was selected.') + use_selection = bone_filter in ['SELECTED', 'SELECTED_WITH_SLOTS'] + with_keysframes = bone_filter == 'WITH_KEYFRAMES' + filter_slots = not with_keysframes and bone_filter not in ['ALL_WITH_SLOTS', 'SELECTED_WITH_SLOTS'] + # 2. Figure out keyframe/animation data for each bone action = arm_obj.animation_data.action @@ -92,9 +103,17 @@ def save_xmot(context: bpy.types.Context, filepath: str, arm_obj: bpy.types.Obje # 3. Collect all bones in armature selected_pose_bones = context.selected_pose_bones for pose_bone in reversed(arm_obj.pose.bones): + # Filter by selection. if use_selection and pose_bone not in selected_pose_bones: continue + # Filter by keyframes. + if with_keysframes and not any(True for bone_name, _ in frames_per_bone.keys() if bone_name == pose_bone.name): + continue + + if filter_slots and pose_bone.name.startswith('Slot_') and pose_bone.name not in COMMONLY_USED_SLOTS: + continue + bone = pose_bone.bone rest_matrix = bone.matrix_local diff --git a/g3blend/ui/io_export_xmot.py b/g3blend/ui/io_export_xmot.py index 2706b29..f95d7d9 100644 --- a/g3blend/ui/io_export_xmot.py +++ b/g3blend/ui/io_export_xmot.py @@ -38,10 +38,32 @@ def _get_target_armature(self): get=_get_target_armature, ) - use_selection: BoolProperty( - name='Selection Only', - description='Export selected objects only', - default=False, + # Overlay / Normal / Interactive + # All Bones + # Bones with Keyframes + # Selected Bones + # TODO: + # All Bones (including unused slots) + # Selected Bones (including unused slots) + bone_filter: EnumProperty( + name='Bones', + description='Bones that shall be included in the exported animation', + items=[ + ('ALL', 'All', 'Include all bones (slots not commonly used in animations are filtered out)'), + ('WITH_KEYFRAMES', 'With Keyframes', + 'Include only bones that have keyframe data assigned. ' + 'Especially useful when re-exporting a modified animation, because for example overlay animations' + '(recognizable by their _O_ infix) only apply to a part of the skeleton. ' + 'In such an xmot, only a subset of bones is contained and on import keyframe data is only created for these bones.' + "When re-exporting later with 'With Keyframes' ensures that only this subset of bones is included."), + ('SELECTED', 'Selected', + 'Include only selected bones (slots not commonly used in animations are filtered out)'), + ('ALL_WITH_SLOTS', 'All (including uncommon slots)', + 'Include all bones (including slots not commonly used in animations)'), + ('SELECTED_WITH_SLOTS', 'Selected (including uncommon slots)', + 'Include only selected bones (including slots not commonly used in animations)'), + ], + default='WITH_KEYFRAMES', ) ignore_transform: BoolProperty( @@ -58,7 +80,7 @@ def execute(self, context): global_scale, global_matrix = self._global_transform(context) target_armature = get_object_for_armature_item(context, self.target_armature) save_xmot(context, self.filepath, target_armature, global_scale, global_matrix, self.ignore_transform, - self.use_selection) + self.bone_filter) self.target_armature_index = 0 except Exception as e: self.report({'ERROR'}, f'Error while exporting {self.filepath}: {e}') @@ -77,7 +99,7 @@ def _draw(self, context: bpy.types.Context, layout: bpy.types.UILayout, operator target_armature = get_object_for_armature_item(context, operator.target_armature) sub = layout.row() sub.enabled = target_armature is not None - sub.prop(operator, "use_selection") + sub.prop(operator, "bone_filter") class G3BLEND_PT_export_xmot_transform(AbstractFileTransformPanel):