From 10e02a84f15147276cac2badbafff90d81a920f8 Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Sun, 16 Jan 2022 18:57:46 -0800 Subject: [PATCH] * Fixed a bug where animations could fail to import if the order of the PSA bones did not match the DFS-order of the target armature. * Added Select All + Deselect All to PSA export operator --- io_export_psk_psa/__init__.py | 6 ++++-- io_export_psk_psa/psa/exporter.py | 23 +++++++++++++++++++++++ io_export_psk_psa/psa/importer.py | 27 +++++++++++---------------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/io_export_psk_psa/__init__.py b/io_export_psk_psa/__init__.py index 0921161..e15dcd0 100644 --- a/io_export_psk_psa/__init__.py +++ b/io_export_psk_psa/__init__.py @@ -43,17 +43,19 @@ from bpy.props import PointerProperty classes = [ psk_exporter.PskExportOperator, psk_importer.PskImportOperator, - psa_exporter.PsaExportOperator, psa_importer.PsaImportOperator, psa_importer.PsaImportFileSelectOperator, psa_importer.PSA_UL_ActionList, psa_importer.PSA_UL_ImportActionList, - psa_exporter.PsaExportActionListItem, psa_importer.PsaImportActionListItem, psa_importer.PsaImportSelectAll, psa_importer.PsaImportDeselectAll, psa_importer.PSA_PT_ImportPanel, psa_importer.PsaImportPropertyGroup, + psa_exporter.PsaExportOperator, + psa_exporter.PsaExportSelectAll, + psa_exporter.PsaExportDeselectAll, + psa_exporter.PsaExportActionListItem, psa_exporter.PsaExportPropertyGroup, ] diff --git a/io_export_psk_psa/psa/exporter.py b/io_export_psk_psa/psa/exporter.py index 57b312b..ffd662e 100644 --- a/io_export_psk_psa/psa/exporter.py +++ b/io_export_psk_psa/psa/exporter.py @@ -71,6 +71,9 @@ class PsaExportOperator(Operator, ExportHelper): box.label(text='Actions', icon='ACTION') row = box.row() row.template_list('PSA_UL_ActionList', 'asd', scene.psa_export, 'action_list', scene.psa_export, 'action_list_index', rows=10) + row = box.row() + row.operator('psa_export.actions_select_all', text='Select All') + row.operator('psa_export.actions_deselect_all', text='Deselect All') def is_action_for_armature(self, action): if len(action.fcurves) == 0: @@ -120,3 +123,23 @@ class PsaExportOperator(Operator, ExportHelper): exporter = PsaExporter(psa) exporter.export(self.filepath) return {'FINISHED'} + + +class PsaExportSelectAll(bpy.types.Operator): + bl_idname = 'psa_export.actions_select_all' + bl_label = 'Select All' + + def execute(self, context): + for action in context.scene.psa_export.action_list: + action.is_selected = True + return {'FINISHED'} + + +class PsaExportDeselectAll(bpy.types.Operator): + bl_idname = 'psa_export.actions_deselect_all' + bl_label = 'Deselect All' + + def execute(self, context): + for action in context.scene.psa_export.action_list: + action.is_selected = False + return {'FINISHED'} diff --git a/io_export_psk_psa/psa/importer.py b/io_export_psk_psa/psa/importer.py index dfe1538..e779b27 100644 --- a/io_export_psk_psa/psa/importer.py +++ b/io_export_psk_psa/psa/importer.py @@ -61,28 +61,26 @@ class PsaImporter(object): # Create intermediate bone data for import operations. import_bones = [] import_bones_dict = dict() + for psa_bone_index, psa_bone in enumerate(psa.bones): bone_name = psa_bone.name.decode('windows-1252') - print(bone_name) if psa_bone_index not in psa_to_armature_bone_indices: # TODO: replace with bone_name in armature_data.bones # PSA bone does not map to armature bone, skip it and leave an empty bone in its place. import_bones.append(None) continue - import_bone = ImportBone(psa_bone) - armature_bone = armature_data.bones[bone_name] + import_bone.armature_bone = armature_data.bones[bone_name] import_bone.pose_bone = armature_object.pose.bones[bone_name] + import_bones_dict[bone_name] = import_bone + import_bones.append(import_bone) - if armature_bone.parent is not None: - if armature_bone.parent.name in psa_bone_names: - import_bone.parent = import_bones_dict[armature_bone.parent.name] - else: - import_bone.parent = None - - # Calculate the original location & rotation of each bone (in world space maybe?) - # TODO: check if the armature bones have custom data written to them and use that instead. + for import_bone in filter(lambda x: x is not None, import_bones): + armature_bone = import_bone.armature_bone + if armature_bone.parent is not None and armature_bone.parent.name in psa_bone_names: + import_bone.parent = import_bones_dict[armature_bone.parent.name] + # Calculate the original location & rotation of each bone (in world-space maybe?) if armature_bone.get('orig_quat') is not None: - # TODO: ideally we don't rely on bone auxiliary data like this, the non-aux data path is incorrect (animations are flipped 180) + # TODO: ideally we don't rely on bone auxiliary data like this, the non-aux data path is incorrect (animations are flipped 180 around Z) import_bone.orig_quat = Quaternion(armature_bone['orig_quat']) import_bone.orig_loc = Vector(armature_bone['orig_loc']) import_bone.post_quat = Quaternion(armature_bone['post_quat']) @@ -97,16 +95,13 @@ class PsaImporter(object): import_bone.orig_loc = armature_bone.matrix_local.translation.copy() import_bone.orig_quat = armature_bone.matrix_local.to_quaternion() import_bone.post_quat = import_bone.orig_quat.conjugated() - import_bones_dict[bone_name] = import_bone - import_bones.append(import_bone) # Create and populate the data for new sequences. for sequence in sequences: action = bpy.data.actions.new(name=sequence.name.decode()) - # TODO: problem might be here (yea, we are confused about the ordering of these things!) for psa_bone_index, armature_bone_index in psa_to_armature_bone_indices.items(): import_bone = import_bones[psa_bone_index] - pose_bone = armature_object.pose.bones[armature_bone_index] + pose_bone = import_bone.pose_bone # rotation rotation_data_path = pose_bone.path_from_id('rotation_quaternion')