* 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
This commit is contained in:
Colin Basnett
2022-01-16 18:57:46 -08:00
parent c34ebce128
commit 10e02a84f1
3 changed files with 38 additions and 18 deletions

View File

@@ -43,17 +43,19 @@ from bpy.props import PointerProperty
classes = [ classes = [
psk_exporter.PskExportOperator, psk_exporter.PskExportOperator,
psk_importer.PskImportOperator, psk_importer.PskImportOperator,
psa_exporter.PsaExportOperator,
psa_importer.PsaImportOperator, psa_importer.PsaImportOperator,
psa_importer.PsaImportFileSelectOperator, psa_importer.PsaImportFileSelectOperator,
psa_importer.PSA_UL_ActionList, psa_importer.PSA_UL_ActionList,
psa_importer.PSA_UL_ImportActionList, psa_importer.PSA_UL_ImportActionList,
psa_exporter.PsaExportActionListItem,
psa_importer.PsaImportActionListItem, psa_importer.PsaImportActionListItem,
psa_importer.PsaImportSelectAll, psa_importer.PsaImportSelectAll,
psa_importer.PsaImportDeselectAll, psa_importer.PsaImportDeselectAll,
psa_importer.PSA_PT_ImportPanel, psa_importer.PSA_PT_ImportPanel,
psa_importer.PsaImportPropertyGroup, psa_importer.PsaImportPropertyGroup,
psa_exporter.PsaExportOperator,
psa_exporter.PsaExportSelectAll,
psa_exporter.PsaExportDeselectAll,
psa_exporter.PsaExportActionListItem,
psa_exporter.PsaExportPropertyGroup, psa_exporter.PsaExportPropertyGroup,
] ]

View File

@@ -71,6 +71,9 @@ class PsaExportOperator(Operator, ExportHelper):
box.label(text='Actions', icon='ACTION') box.label(text='Actions', icon='ACTION')
row = box.row() row = box.row()
row.template_list('PSA_UL_ActionList', 'asd', scene.psa_export, 'action_list', scene.psa_export, 'action_list_index', rows=10) 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): def is_action_for_armature(self, action):
if len(action.fcurves) == 0: if len(action.fcurves) == 0:
@@ -120,3 +123,23 @@ class PsaExportOperator(Operator, ExportHelper):
exporter = PsaExporter(psa) exporter = PsaExporter(psa)
exporter.export(self.filepath) exporter.export(self.filepath)
return {'FINISHED'} 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'}

View File

@@ -61,28 +61,26 @@ class PsaImporter(object):
# Create intermediate bone data for import operations. # Create intermediate bone data for import operations.
import_bones = [] import_bones = []
import_bones_dict = dict() import_bones_dict = dict()
for psa_bone_index, psa_bone in enumerate(psa.bones): for psa_bone_index, psa_bone in enumerate(psa.bones):
bone_name = psa_bone.name.decode('windows-1252') 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 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. # PSA bone does not map to armature bone, skip it and leave an empty bone in its place.
import_bones.append(None) import_bones.append(None)
continue continue
import_bone = ImportBone(psa_bone) 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_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: for import_bone in filter(lambda x: x is not None, import_bones):
if armature_bone.parent.name in psa_bone_names: 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] import_bone.parent = import_bones_dict[armature_bone.parent.name]
else: # Calculate the original location & rotation of each bone (in world-space maybe?)
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.
if armature_bone.get('orig_quat') is not None: 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_quat = Quaternion(armature_bone['orig_quat'])
import_bone.orig_loc = Vector(armature_bone['orig_loc']) import_bone.orig_loc = Vector(armature_bone['orig_loc'])
import_bone.post_quat = Quaternion(armature_bone['post_quat']) 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_loc = armature_bone.matrix_local.translation.copy()
import_bone.orig_quat = armature_bone.matrix_local.to_quaternion() import_bone.orig_quat = armature_bone.matrix_local.to_quaternion()
import_bone.post_quat = import_bone.orig_quat.conjugated() 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. # Create and populate the data for new sequences.
for sequence in sequences: for sequence in sequences:
action = bpy.data.actions.new(name=sequence.name.decode()) 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(): for psa_bone_index, armature_bone_index in psa_to_armature_bone_indices.items():
import_bone = import_bones[psa_bone_index] import_bone = import_bones[psa_bone_index]
pose_bone = armature_object.pose.bones[armature_bone_index] pose_bone = import_bone.pose_bone
# rotation # rotation
rotation_data_path = pose_bone.path_from_id('rotation_quaternion') rotation_data_path = pose_bone.path_from_id('rotation_quaternion')