More polishing (more operator descriptions, better error reporting etc.)
This commit is contained in:
@@ -9,6 +9,7 @@ def populate_bone_group_list(armature_object, bone_group_list):
|
|||||||
item.index = -1
|
item.index = -1
|
||||||
item.is_selected = True
|
item.is_selected = True
|
||||||
|
|
||||||
|
if armature_object and armature_object.pose:
|
||||||
for bone_group_index, bone_group in enumerate(armature_object.pose.bone_groups):
|
for bone_group_index, bone_group in enumerate(armature_object.pose.bone_groups):
|
||||||
item = bone_group_list.add()
|
item = bone_group_list.add()
|
||||||
item.name = bone_group.name
|
item.name = bone_group.name
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def is_bone_filter_mode_item_available(context, identifier):
|
|||||||
class PsaExportOperator(Operator, ExportHelper):
|
class PsaExportOperator(Operator, ExportHelper):
|
||||||
bl_idname = 'export.psa'
|
bl_idname = 'export.psa'
|
||||||
bl_label = 'Export'
|
bl_label = 'Export'
|
||||||
__doc__ = 'PSA Exporter (.psa)'
|
__doc__ = 'Export actions to PSA'
|
||||||
filename_ext = '.psa'
|
filename_ext = '.psa'
|
||||||
filter_glob: StringProperty(default='*.psa', options={'HIDDEN'})
|
filter_glob: StringProperty(default='*.psa', options={'HIDDEN'})
|
||||||
filepath: StringProperty(
|
filepath: StringProperty(
|
||||||
@@ -174,7 +174,11 @@ class PsaExportOperator(Operator, ExportHelper):
|
|||||||
options.bone_filter_mode = property_group.bone_filter_mode
|
options.bone_filter_mode = property_group.bone_filter_mode
|
||||||
options.bone_group_indices = [x.index for x in property_group.bone_group_list if x.is_selected]
|
options.bone_group_indices = [x.index for x in property_group.bone_group_list if x.is_selected]
|
||||||
builder = PsaBuilder()
|
builder = PsaBuilder()
|
||||||
|
try:
|
||||||
psa = builder.build(context, options)
|
psa = builder.build(context, options)
|
||||||
|
except RuntimeError as e:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
exporter = PsaExporter(psa)
|
exporter = PsaExporter(psa)
|
||||||
exporter.export(self.filepath)
|
exporter.export(self.filepath)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
@@ -204,6 +208,7 @@ class PSA_UL_ExportActionList(UIList):
|
|||||||
class PsaExportSelectAll(bpy.types.Operator):
|
class PsaExportSelectAll(bpy.types.Operator):
|
||||||
bl_idname = 'psa_export.actions_select_all'
|
bl_idname = 'psa_export.actions_select_all'
|
||||||
bl_label = 'Select All'
|
bl_label = 'Select All'
|
||||||
|
bl_description = 'Select all actions'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -222,6 +227,7 @@ class PsaExportSelectAll(bpy.types.Operator):
|
|||||||
class PsaExportDeselectAll(bpy.types.Operator):
|
class PsaExportDeselectAll(bpy.types.Operator):
|
||||||
bl_idname = 'psa_export.actions_deselect_all'
|
bl_idname = 'psa_export.actions_deselect_all'
|
||||||
bl_label = 'Deselect All'
|
bl_label = 'Deselect All'
|
||||||
|
bl_description = 'Deselect all actions'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ def on_armature_object_updated(property, context):
|
|||||||
|
|
||||||
|
|
||||||
class PsaImportPropertyGroup(bpy.types.PropertyGroup):
|
class PsaImportPropertyGroup(bpy.types.PropertyGroup):
|
||||||
psa_file_path: StringProperty(default='', update=on_psa_file_path_updated)
|
psa_file_path: StringProperty(default='', update=on_psa_file_path_updated, name='PSA File Path')
|
||||||
psa_bones: CollectionProperty(type=PsaImportPsaBoneItem)
|
psa_bones: CollectionProperty(type=PsaImportPsaBoneItem)
|
||||||
# armature_object: PointerProperty(name='Object', type=bpy.types.Object, update=on_armature_object_updated)
|
# armature_object: PointerProperty(name='Object', type=bpy.types.Object, update=on_armature_object_updated)
|
||||||
action_list: CollectionProperty(type=PsaImportActionListItem)
|
action_list: CollectionProperty(type=PsaImportActionListItem)
|
||||||
@@ -260,6 +260,7 @@ class PSA_UL_ImportActionList(UIList):
|
|||||||
class PsaImportSelectAll(bpy.types.Operator):
|
class PsaImportSelectAll(bpy.types.Operator):
|
||||||
bl_idname = 'psa_import.actions_select_all'
|
bl_idname = 'psa_import.actions_select_all'
|
||||||
bl_label = 'All'
|
bl_label = 'All'
|
||||||
|
bl_description = 'Select all actions'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -278,6 +279,7 @@ class PsaImportSelectAll(bpy.types.Operator):
|
|||||||
class PsaImportDeselectAll(bpy.types.Operator):
|
class PsaImportDeselectAll(bpy.types.Operator):
|
||||||
bl_idname = 'psa_import.actions_deselect_all'
|
bl_idname = 'psa_import.actions_deselect_all'
|
||||||
bl_label = 'None'
|
bl_label = 'None'
|
||||||
|
bl_description = 'Deselect all actions'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -335,11 +337,12 @@ class PSA_PT_ImportPanel(Panel):
|
|||||||
|
|
||||||
|
|
||||||
class PsaImportSelectFile(Operator):
|
class PsaImportSelectFile(Operator):
|
||||||
bl_idname = "psa_import.select_file"
|
bl_idname = 'psa_import.select_file'
|
||||||
bl_label = "Select"
|
bl_label = 'Select'
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
bl_description = 'Select a PSA file from which to import animations'
|
||||||
filter_glob: bpy.props.StringProperty(default="*.psa", options={"HIDDEN"})
|
filepath: bpy.props.StringProperty(subtype='FILE_PATH')
|
||||||
|
filter_glob: bpy.props.StringProperty(default="*.psa", options={'HIDDEN'})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.psa_import.psa_file_path = self.filepath
|
context.scene.psa_import.psa_file_path = self.filepath
|
||||||
@@ -353,6 +356,7 @@ class PsaImportSelectFile(Operator):
|
|||||||
class PsaImportOperator(Operator):
|
class PsaImportOperator(Operator):
|
||||||
bl_idname = 'psa_import.import'
|
bl_idname = 'psa_import.import'
|
||||||
bl_label = 'Import'
|
bl_label = 'Import'
|
||||||
|
bl_description = 'Import the selected animations into the scene as actions'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ class PskBuilder(object):
|
|||||||
materials = OrderedDict()
|
materials = OrderedDict()
|
||||||
|
|
||||||
if armature_object is None:
|
if armature_object is None:
|
||||||
# Static mesh (no armature)
|
# If the mesh has no armature object, simply assign it a dummy bone at the root to satisfy the requirement
|
||||||
|
# that a PSK file must have at least one bone.
|
||||||
psk_bone = Psk.Bone()
|
psk_bone = Psk.Bone()
|
||||||
psk_bone.name = bytes('static', encoding='utf-8')
|
psk_bone.name = bytes('static', encoding='utf-8')
|
||||||
psk_bone.flags = 0
|
psk_bone.flags = 0
|
||||||
@@ -79,13 +80,16 @@ class PskBuilder(object):
|
|||||||
else:
|
else:
|
||||||
bones = list(armature_object.data.bones)
|
bones = list(armature_object.data.bones)
|
||||||
|
|
||||||
# If bone groups are specified, get only the bones that are in the specified bone groups and their ancestors.
|
# If we are filtering by bone groups, get only the bones that are in the specified bone groups and their
|
||||||
if len(options.bone_group_indices) > 0:
|
# ancestors.
|
||||||
|
if options.bone_filter_mode == 'BONE_GROUPS':
|
||||||
bone_indices = get_export_bone_indices_for_bone_groups(armature_object, options.bone_group_indices)
|
bone_indices = get_export_bone_indices_for_bone_groups(armature_object, options.bone_group_indices)
|
||||||
bones = [bones[bone_index] for bone_index in bone_indices]
|
bones = [bones[bone_index] for bone_index in bone_indices]
|
||||||
|
|
||||||
# Ensure that the exported hierarchy has a single root bone.
|
# Ensure that the exported hierarchy has a single root bone.
|
||||||
root_bones = [x for x in bones if x.parent is None]
|
root_bones = [x for x in bones if x.parent is None]
|
||||||
|
print('root bones')
|
||||||
|
print(root_bones)
|
||||||
if len(root_bones) > 1:
|
if len(root_bones) > 1:
|
||||||
root_bone_names = [x.name for x in bones]
|
root_bone_names = [x.name for x in bones]
|
||||||
raise RuntimeError('Exported bone hierarchy must have a single root bone.'
|
raise RuntimeError('Exported bone hierarchy must have a single root bone.'
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ def is_bone_filter_mode_item_available(context, identifier):
|
|||||||
input_objects = PskBuilder.get_input_objects(context)
|
input_objects = PskBuilder.get_input_objects(context)
|
||||||
armature_object = input_objects.armature_object
|
armature_object = input_objects.armature_object
|
||||||
if identifier == 'BONE_GROUPS':
|
if identifier == 'BONE_GROUPS':
|
||||||
if not armature_object.pose or not armature_object.pose.bone_groups:
|
if not armature_object or not armature_object.pose or not armature_object.pose.bone_groups:
|
||||||
return False
|
return False
|
||||||
# else if... you can set up other conditions if you add more options
|
# else if... you can set up other conditions if you add more options
|
||||||
return True
|
return True
|
||||||
@@ -73,7 +73,7 @@ def is_bone_filter_mode_item_available(context, identifier):
|
|||||||
class PskExportOperator(Operator, ExportHelper):
|
class PskExportOperator(Operator, ExportHelper):
|
||||||
bl_idname = 'export.psk'
|
bl_idname = 'export.psk'
|
||||||
bl_label = 'Export'
|
bl_label = 'Export'
|
||||||
__doc__ = 'PSK Exporter (.psk)'
|
__doc__ = 'Export mesh and armature to PSK'
|
||||||
filename_ext = '.psk'
|
filename_ext = '.psk'
|
||||||
filter_glob: StringProperty(default='*.psk', options={'HIDDEN'})
|
filter_glob: StringProperty(default='*.psk', options={'HIDDEN'})
|
||||||
|
|
||||||
@@ -125,7 +125,11 @@ class PskExportOperator(Operator, ExportHelper):
|
|||||||
builder = PskBuilder()
|
builder = PskBuilder()
|
||||||
options = PskBuilderOptions()
|
options = PskBuilderOptions()
|
||||||
options.bone_group_indices = [x.index for x in property_group.bone_group_list if x.is_selected]
|
options.bone_group_indices = [x.index for x in property_group.bone_group_list if x.is_selected]
|
||||||
|
try:
|
||||||
psk = builder.build(context, options)
|
psk = builder.build(context, options)
|
||||||
|
except RuntimeError as e:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
exporter = PskExporter(psk)
|
exporter = PskExporter(psk)
|
||||||
exporter.export(self.filepath)
|
exporter.export(self.filepath)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class PskImporter(object):
|
|||||||
class PskImportOperator(Operator, ImportHelper):
|
class PskImportOperator(Operator, ImportHelper):
|
||||||
bl_idname = 'import.psk'
|
bl_idname = 'import.psk'
|
||||||
bl_label = 'Export'
|
bl_label = 'Export'
|
||||||
__doc__ = 'PSK Importer (.psk)'
|
__doc__ = 'Load a PSK file'
|
||||||
filename_ext = '.psk'
|
filename_ext = '.psk'
|
||||||
filter_glob: StringProperty(default='*.psk', options={'HIDDEN'})
|
filter_glob: StringProperty(default='*.psk', options={'HIDDEN'})
|
||||||
filepath: StringProperty(
|
filepath: StringProperty(
|
||||||
|
|||||||
Reference in New Issue
Block a user