From a5955bf09bcbc2270c2b1ccb7c44379ef3bf2167 Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Mon, 4 Apr 2022 00:32:12 -0700 Subject: [PATCH] Lots of interface tweaks --- io_scene_psk_psa/data.py | 8 ++ io_scene_psk_psa/helpers.py | 19 ++++ io_scene_psk_psa/psa/builder.py | 96 ++++++++++++-------- io_scene_psk_psa/psa/exporter.py | 130 +++++++++++++++++---------- io_scene_psk_psa/psa/importer.py | 147 ++++++------------------------- io_scene_psk_psa/psk/builder.py | 6 +- 6 files changed, 200 insertions(+), 206 deletions(-) diff --git a/io_scene_psk_psa/data.py b/io_scene_psk_psa/data.py index 1639a01..0d3db93 100644 --- a/io_scene_psk_psa/data.py +++ b/io_scene_psk_psa/data.py @@ -55,6 +55,10 @@ class Vector3(Structure): def __repr__(self): return repr(tuple(self)) + @classmethod + def zero(cls): + return Vector3(0, 0, 0) + class Quaternion(Structure): _fields_ = [ @@ -73,6 +77,10 @@ class Quaternion(Structure): def __repr__(self): return repr(tuple(self)) + @classmethod + def identity(cls): + return Quaternion(0, 0, 0, 1) + class Section(Structure): _fields_ = [ diff --git a/io_scene_psk_psa/helpers.py b/io_scene_psk_psa/helpers.py index ab8bfa1..df60781 100644 --- a/io_scene_psk_psa/helpers.py +++ b/io_scene_psk_psa/helpers.py @@ -1,6 +1,25 @@ from bpy.types import NlaStrip from typing import List, Tuple, Optional from collections import Counter +import datetime + + +class Timer: + def __enter__(self): + self.start = datetime.datetime.now() + self.interval = None + return self + + def __exit__(self, *args): + self.end = datetime.datetime.now() + self.interval = self.end - self.start + + @property + def duration(self): + if self.interval is not None: + return self.interval + else: + return datetime.datetime.now() - self.start def rgb_to_srgb(c): diff --git a/io_scene_psk_psa/psa/builder.py b/io_scene_psk_psa/psa/builder.py index d093206..983bdf8 100644 --- a/io_scene_psk_psa/psa/builder.py +++ b/io_scene_psk_psa/psa/builder.py @@ -16,17 +16,25 @@ class PsaBuilderOptions(object): self.sequence_name_suffix = '' +class PsaBuilderPerformance: + def __init__(self): + self.frame_set_duration = datetime.timedelta() + self.key_build_duration = datetime.timedelta() + self.key_add_duration = datetime.timedelta() + + class PsaBuilder(object): def __init__(self): pass def build(self, context, options: PsaBuilderOptions) -> Psa: - object = context.view_layer.objects.active + performance = PsaBuilderPerformance() + active_object = context.view_layer.objects.active - if object.type != 'ARMATURE': + if active_object.type != 'ARMATURE': raise RuntimeError('Selected object must be an Armature') - armature = object + armature = active_object if armature.animation_data is None: raise RuntimeError('No animation data for armature') @@ -99,13 +107,17 @@ class PsaBuilder(object): psa.bones.append(psa_bone) # Populate the export sequence list. - class ExportSequence: + class NlaState: def __init__(self): - self.name = '' self.frame_min = 0 self.frame_max = 0 self.action = None + class ExportSequence: + def __init__(self): + self.name = '' + self.nla_state = NlaState() + export_sequences = [] if options.sequence_source == 'ACTIONS': @@ -113,9 +125,11 @@ class PsaBuilder(object): if len(action.fcurves) == 0: continue export_sequence = ExportSequence() - export_sequence.action = action + export_sequence.nla_state.action = action export_sequence.name = get_psa_sequence_name(action, options.should_use_original_sequence_names) - export_sequence.frame_min, export_sequence.frame_max = [int(x) for x in action.frame_range] + frame_min, frame_max = [int(x) for x in action.frame_range] + export_sequence.nla_state.frame_min = frame_min + export_sequence.nla_state.frame_max = frame_max export_sequences.append(export_sequence) pass elif options.sequence_source == 'TIMELINE_MARKERS': @@ -123,10 +137,10 @@ class PsaBuilder(object): for name, (frame_min, frame_max) in sequence_frame_ranges.items(): export_sequence = ExportSequence() - export_sequence.action = None export_sequence.name = name - export_sequence.frame_min = frame_min - export_sequence.frame_max = frame_max + export_sequence.nla_state.action = None + export_sequence.nla_state.frame_min = frame_min + export_sequence.nla_state.frame_max = frame_max export_sequences.append(export_sequence) else: raise ValueError(f'Unhandled sequence source: {options.sequence_source}') @@ -140,13 +154,13 @@ class PsaBuilder(object): frame_start_index = 0 for export_sequence in export_sequences: - armature.animation_data.action = export_sequence.action + armature.animation_data.action = export_sequence.nla_state.action context.view_layer.update() psa_sequence = Psa.Sequence() - frame_min = export_sequence.frame_min - frame_max = export_sequence.frame_max + frame_min = export_sequence.nla_state.frame_min + frame_max = export_sequence.nla_state.frame_max frame_count = frame_max - frame_min + 1 psa_sequence.name = bytes(export_sequence.name, encoding='windows-1252') @@ -157,34 +171,40 @@ class PsaBuilder(object): frame_count = frame_max - frame_min + 1 for frame in range(frame_count): - context.scene.frame_set(frame_min + frame) + with Timer() as t: + context.scene.frame_set(frame_min + frame) + performance.frame_set_duration += t.duration for pose_bone in pose_bones: - key = Psa.Key() - pose_bone_matrix = pose_bone.matrix + with Timer() as t: + key = Psa.Key() + pose_bone_matrix = pose_bone.matrix - if pose_bone.parent is not None: - pose_bone_parent_matrix = pose_bone.parent.matrix - pose_bone_matrix = pose_bone_parent_matrix.inverted() @ pose_bone_matrix + if pose_bone.parent is not None: + pose_bone_parent_matrix = pose_bone.parent.matrix + pose_bone_matrix = pose_bone_parent_matrix.inverted() @ pose_bone_matrix - location = pose_bone_matrix.to_translation() - rotation = pose_bone_matrix.to_quaternion().normalized() + location = pose_bone_matrix.to_translation() + rotation = pose_bone_matrix.to_quaternion().normalized() - if pose_bone.parent is not None: - rotation.x = -rotation.x - rotation.y = -rotation.y - rotation.z = -rotation.z + if pose_bone.parent is not None: + rotation.x = -rotation.x + rotation.y = -rotation.y + rotation.z = -rotation.z - key.location.x = location.x - key.location.y = location.y - key.location.z = location.z - key.rotation.x = rotation.x - key.rotation.y = rotation.y - key.rotation.z = rotation.z - key.rotation.w = rotation.w - key.time = 1.0 / psa_sequence.fps + key.location.x = location.x + key.location.y = location.y + key.location.z = location.z + key.rotation.x = rotation.x + key.rotation.y = rotation.y + key.rotation.z = rotation.z + key.rotation.w = rotation.w + key.time = 1.0 / psa_sequence.fps + performance.key_build_duration += t.duration - psa.keys.append(key) + with Timer() as t: + psa.keys.append(key) + performance.key_add_duration += t.duration psa_sequence.bone_count = len(pose_bones) psa_sequence.track_time = frame_count @@ -193,6 +213,10 @@ class PsaBuilder(object): psa.sequences[export_sequence.name] = psa_sequence + print(f'frame set duration: {performance.frame_set_duration}') + print(f'key build duration: {performance.key_build_duration}') + print(f'key add duration: {performance.key_add_duration}') + return psa def get_timeline_marker_sequence_frame_ranges(self, object, context, options: PsaBuilderOptions) -> Dict: @@ -214,8 +238,8 @@ class PsaBuilder(object): frame_max = sorted_timeline_markers[next_marker_index].frame if options.should_trim_timeline_marker_sequences: nla_strips = get_nla_strips_in_timeframe(object, marker.frame, frame_max) - frame_max = min(frame_max, max(map(lambda x: x.frame_end, nla_strips))) - frame_min = max(frame_min, min(map(lambda x: x.frame_start, nla_strips))) + frame_max = min(frame_max, max(map(lambda nla_strip: nla_strip.frame_end, nla_strips))) + frame_min = max(frame_min, min(map(lambda nla_strip: nla_strip.frame_start, nla_strips))) else: # There is no next marker. # Find the final frame of all the NLA strips and use that as the last frame of this sequence. diff --git a/io_scene_psk_psa/psa/exporter.py b/io_scene_psk_psa/psa/exporter.py index 18123f7..b53ed9d 100644 --- a/io_scene_psk_psa/psa/exporter.py +++ b/io_scene_psk_psa/psa/exporter.py @@ -38,23 +38,15 @@ class PsaExporter(object): class PsaExportActionListItem(PropertyGroup): action: PointerProperty(type=Action) - action_name: StringProperty() + name: StringProperty() is_selected: BoolProperty(default=False) - @property - def name(self): - return self.action.name - class PsaExportTimelineMarkerListItem(PropertyGroup): marker_index: IntProperty() - marker_name: StringProperty() + name: StringProperty() is_selected: BoolProperty(default=True) - @property - def name(self): - return self.marker_name - def update_action_names(context): pg = context.scene.psa_export @@ -110,6 +102,10 @@ class PsaExportPropertyGroup(PropertyGroup): ) sequence_name_prefix: StringProperty(name='Prefix', options=set()) sequence_name_suffix: StringProperty(name='Suffix', options=set()) + sequence_filter_name: StringProperty(default='', options={'TEXTEDIT_UPDATE'}) + sequence_use_filter_invert: BoolProperty(default=False, options=set()) + sequence_filter_asset: BoolProperty(default=False, name='Show assets', description='Show actions that belong to an asset library', options=set()) + sequence_use_filter_sort_reverse: BoolProperty(default=True, options=set()) def is_bone_filter_mode_item_available(context, identifier): @@ -152,6 +148,7 @@ class PsaExportOperator(Operator, ExportHelper): # ACTIONS if pg.sequence_source == 'ACTIONS': rows = max(3, min(len(pg.action_list), 10)) + layout.template_list('PSA_UL_ExportActionList', '', pg, 'action_list', pg, 'action_list_index', rows=rows) col = layout.column() @@ -174,7 +171,7 @@ class PsaExportOperator(Operator, ExportHelper): # Determine if there is going to be a naming conflict and display an error, if so. selected_items = [x for x in pg.action_list if x.is_selected] - action_names = [x.action_name for x in selected_items] + action_names = [x.name for x in selected_items] action_name_counts = Counter(action_names) for action_name, count in action_name_counts.items(): if count > 1: @@ -195,6 +192,9 @@ class PsaExportOperator(Operator, ExportHelper): rows = max(3, min(len(pg.bone_group_list), 10)) layout.template_list('PSX_UL_BoneGroupList', '', pg, 'bone_group_list', pg, 'bone_group_list_index', rows=rows) + def should_action_be_selected_by_default(self, action): + return action is not None and action.asset_data is None + def is_action_for_armature(self, action): if len(action.fcurves) == 0: return False @@ -228,8 +228,8 @@ class PsaExportOperator(Operator, ExportHelper): continue item = pg.action_list.add() item.action = action - item.action_name = action.name - item.is_selected = True + item.name = action.name + item.is_selected = self.should_action_be_selected_by_default(action) update_action_names(context) @@ -237,7 +237,7 @@ class PsaExportOperator(Operator, ExportHelper): pg.marker_list.clear() for marker in context.scene.timeline_markers: item = pg.marker_list.add() - item.marker_name = marker.name + item.name = marker.name if len(pg.action_list) == 0 and len(pg.marker_names) == 0: # If there are no actions at all, we have nothing to export, so just cancel the operation. @@ -255,7 +255,7 @@ class PsaExportOperator(Operator, ExportHelper): pg = context.scene.psa_export actions = [x.action for x in pg.action_list if x.is_selected] - marker_names = [x.marker_name for x in pg.marker_list if x.is_selected] + marker_names = [x.name for x in pg.marker_list if x.is_selected] options = PsaBuilderOptions() options.sequence_source = pg.sequence_source @@ -283,39 +283,75 @@ class PsaExportOperator(Operator, ExportHelper): class PSA_UL_ExportTimelineMarkerList(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - layout.prop(item, 'is_selected', icon_only=True, text=item.marker_name) + layout.prop(item, 'is_selected', icon_only=True, text=item.name) def filter_items(self, context, data, property): - actions = getattr(data, property) - flt_flags = [] - flt_neworder = [] - if self.filter_name: - flt_flags = bpy.types.UI_UL_list.filter_items_by_name( - self.filter_name, - self.bitflag_filter_item, - actions, - 'marker_name', - reverse=self.use_filter_invert - ) + pg = context.scene.psa_export + sequences = getattr(data, property) + flt_flags = filter_sequences(pg, sequences) + flt_neworder = bpy.types.UI_UL_list.sort_items_by_name(sequences, 'name') return flt_flags, flt_neworder +def filter_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_collection) -> List[int]: + bitflag_filter_item = 1 << 30 + flt_flags = [bitflag_filter_item] * len(sequences) + + if pg.sequence_filter_name is not None: + # Filter name is non-empty. + import fnmatch + for i, sequence in enumerate(sequences): + if not fnmatch.fnmatch(sequence.name, f'*{pg.sequence_filter_name}*'): + flt_flags[i] &= ~bitflag_filter_item + + if not pg.sequence_filter_asset: + for i, sequence in enumerate(sequences): + if hasattr(sequence, 'action') and sequence.action.asset_data is not None: + flt_flags[i] &= ~bitflag_filter_item + + if pg.sequence_use_filter_invert: + # Invert filter flags for all items. + for i, sequence in enumerate(sequences): + flt_flags[i] ^= ~bitflag_filter_item + + return flt_flags + + +def get_visible_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_collection) -> List[PsaExportActionListItem]: + visible_sequences = [] + for i, flag in enumerate(filter_sequences(pg, sequences)): + if bool(flag & (1 << 30)): + visible_sequences.append(sequences[i]) + return visible_sequences + + class PSA_UL_ExportActionList(UIList): + + def __init__(self): + super(PSA_UL_ExportActionList, self).__init__() + # Show the filtering options by default. + self.use_filter_show = True + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - layout.prop(item, 'is_selected', icon_only=True, text=item.action_name) + layout.prop(item, 'is_selected', icon_only=True, text=item.name) + if item.action.asset_data is not None: + layout.label(text='', icon='ASSET_MANAGER') + + def draw_filter(self, context, layout): + pg = context.scene.psa_export + row = layout.row() + subrow = row.row(align=True) + subrow.prop(pg, 'sequence_filter_name', text="") + subrow.prop(pg, 'sequence_use_filter_invert', text="", icon='ARROW_LEFTRIGHT') + subrow = row.row(align=True) + subrow.prop(pg, 'sequence_filter_asset', icon_only=True, icon='ASSET_MANAGER') + # subrow.prop(pg, 'sequence_use_filter_sort_reverse', text='', icon='SORT_ASC') def filter_items(self, context, data, property): + pg = context.scene.psa_export actions = getattr(data, property) - flt_flags = [] - flt_neworder = [] - if self.filter_name: - flt_flags = bpy.types.UI_UL_list.filter_items_by_name( - self.filter_name, - self.bitflag_filter_item, - actions, - 'action_name', - reverse=self.use_filter_invert - ) + flt_flags = filter_sequences(pg, actions) + flt_neworder = bpy.types.UI_UL_list.sort_items_by_name(actions, 'name') return flt_flags, flt_neworder @@ -336,14 +372,17 @@ class PsaExportActionsSelectAll(Operator): @classmethod def poll(cls, context): + pg = context.scene.psa_export item_list = cls.get_item_list(context) - has_unselected_items = any(map(lambda item: not item.is_selected, item_list)) - return len(item_list) > 0 and has_unselected_items + visible_sequences = get_visible_sequences(pg, item_list) + has_unselected_sequences = any(map(lambda item: not item.is_selected, visible_sequences)) + return has_unselected_sequences def execute(self, context): - item_list = self.get_item_list(context) - for item in item_list: - item.is_selected = True + pg = context.scene.psa_export + sequences = self.get_item_list(context) + for sequence in get_visible_sequences(pg, sequences): + sequence.is_selected = True return {'FINISHED'} @@ -369,9 +408,10 @@ class PsaExportActionsDeselectAll(Operator): return len(item_list) > 0 and has_selected_items def execute(self, context): + pg = context.scene.psa_export item_list = self.get_item_list(context) - for item in item_list: - item.is_selected = False + for sequence in get_visible_sequences(pg, item_list): + sequence.is_selected = False return {'FINISHED'} diff --git a/io_scene_psk_psa/psa/importer.py b/io_scene_psk_psa/psa/importer.py index e6b82de..a9af2cf 100644 --- a/io_scene_psk_psa/psa/importer.py +++ b/io_scene_psk_psa/psa/importer.py @@ -208,7 +208,6 @@ def load_psa_file(context): pg = context.scene.psa_import pg.sequence_list.clear() pg.psa.bones.clear() - pg.action_list.clear() pg.psa_error = '' try: # Read the file and populate the action list. @@ -243,13 +242,13 @@ class PsaImportPropertyGroup(PropertyGroup): psa: PointerProperty(type=PsaDataPropertyGroup) sequence_list: CollectionProperty(type=PsaImportActionListItem) sequence_list_index: IntProperty(name='', default=0) - action_list: CollectionProperty(type=PsaImportActionListItem) - action_list_index: IntProperty(name='', default=0) should_clean_keys: BoolProperty(default=True, name='Clean Keyframes', description='Exclude unnecessary keyframes from being written to the actions.', options=set()) should_use_fake_user: BoolProperty(default=True, name='Fake User', description='Assign each imported action a fake user so that the data block is saved even it has no users.', options=set()) should_stash: BoolProperty(default=False, name='Stash', description='Stash each imported action as a strip on a new non-contributing NLA track', options=set()) should_use_action_name_prefix: BoolProperty(default=False, name='Prefix Action Name', options=set()) action_name_prefix: StringProperty(default='', name='Prefix', options=set()) + sequence_filter_name: StringProperty(default='', options={'TEXTEDIT_UPDATE'}) + sequence_use_filter_invert: BoolProperty(default=False, options=set()) class PSA_UL_SequenceList(UIList): @@ -263,25 +262,26 @@ class PSA_UL_SequenceList(UIList): column.label(text=item.action_name) def draw_filter(self, context, layout): + pg = context.scene.psa_import row = layout.row() subrow = row.row(align=True) - subrow.prop(self, 'filter_name', text="") - subrow.prop(self, 'use_filter_invert', text="", icon='ARROW_LEFTRIGHT') - subrow = row.row(align=True) - subrow.prop(self, 'use_filter_sort_reverse', text='', icon='SORT_ASC') + # TODO: current used for both, not good! + subrow.prop(pg, 'sequence_filter_name', text="") + subrow.prop(pg, 'sequence_use_filter_invert', text="", icon='ARROW_LEFTRIGHT') def filter_items(self, context, data, property): - actions = getattr(data, property) + pg = context.scene.psa_import + sequences = getattr(data, property) flt_flags = [] - if self.filter_name: + if pg.sequence_filter_name: flt_flags = bpy.types.UI_UL_list.filter_items_by_name( - self.filter_name, + pg.sequence_filter_name, self.bitflag_filter_item, - actions, + sequences, 'action_name', - reverse=self.use_filter_invert + reverse=pg.sequence_use_filter_invert ) - flt_neworder = bpy.types.UI_UL_list.sort_items_by_name(actions, 'action_name') + flt_neworder = bpy.types.UI_UL_list.sort_items_by_name(sequences, 'action_name') return flt_flags, flt_neworder @@ -313,26 +313,6 @@ class PsaImportSequencesSelectAll(Operator): return {'FINISHED'} -class PsaImportActionsSelectAll(Operator): - bl_idname = 'psa_import.actions_select_all' - bl_label = 'All' - bl_description = 'Select all actions' - bl_options = {'INTERNAL'} - - @classmethod - def poll(cls, context): - pg = context.scene.psa_import - action_list = pg.action_list - has_unselected_actions = any(map(lambda action: not action.is_selected, action_list)) - return len(action_list) > 0 and has_unselected_actions - - def execute(self, context): - pg = context.scene.psa_import - for action in pg.action_list: - action.is_selected = True - return {'FINISHED'} - - class PsaImportSequencesDeselectAll(Operator): bl_idname = 'psa_import.sequences_deselect_all' bl_label = 'None' @@ -353,26 +333,6 @@ class PsaImportSequencesDeselectAll(Operator): return {'FINISHED'} -class PsaImportActionsDeselectAll(Operator): - bl_idname = 'psa_import.actions_deselect_all' - bl_label = 'None' - bl_description = 'Deselect all actions' - bl_options = {'INTERNAL'} - - @classmethod - def poll(cls, context): - pg = context.scene.psa_import - action_list = pg.action_list - has_selected_actions = any(map(lambda action: action.is_selected, action_list)) - return len(action_list) > 0 and has_selected_actions - - def execute(self, context): - pg = context.scene.psa_import - for action in pg.action_list: - action.is_selected = False - return {'FINISHED'} - - class PSA_PT_ImportPanel_Advanced(Panel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -443,31 +403,21 @@ class PSA_PT_ImportPanel(Panel): box.label(text=f'Sequences', icon='ARMATURE_DATA') # select - rows = max(3, min(len(pg.sequence_list) + len(pg.action_list), 10)) + rows = max(3, max(len(pg.sequence_list), 10)) row = box.row() col = row.column() row2 = col.row(align=True) row2.label(text='Select') - row2.operator(PsaImportSequencesSelectAll.bl_idname, text='All') - row2.operator(PsaImportSequencesDeselectAll.bl_idname, text='None') + row2.operator(PsaImportSequencesSelectAll.bl_idname, text='All', icon='CHECKBOX_HLT') + row2.operator(PsaImportSequencesDeselectAll.bl_idname, text='None', icon='CHECKBOX_DEHLT') col = col.row() col.template_list('PSA_UL_ImportSequenceList', '', pg, 'sequence_list', pg, 'sequence_list_index', rows=rows) - col = row.column(align=True) - col.operator(PsaImportPushToActions.bl_idname, icon='TRIA_RIGHT', text='') - col.operator(PsaImportPopFromActions.bl_idname, icon='TRIA_LEFT', text='') - - col = row.column() - row2 = col.row(align=True) - row2.label(text='Select') - row2.operator(PsaImportActionsSelectAll.bl_idname, text='All') - row2.operator(PsaImportActionsDeselectAll.bl_idname, text='None') - col.template_list('PSA_UL_ImportActionList', '', pg, 'action_list', pg, 'action_list_index', rows=rows) - col.separator() - col.operator(PsaImportOperator.bl_idname, text=f'Import') + row = box.row() + row.operator(PsaImportOperator.bl_idname, text=f'Import') class PsaImportFileReload(Operator): @@ -508,69 +458,26 @@ class PsaImportOperator(Operator): def poll(cls, context): pg = context.scene.psa_import active_object = context.view_layer.objects.active - action_list = pg.action_list - return len(action_list) and active_object is not None and active_object.type == 'ARMATURE' + if active_object is None or active_object.type != 'ARMATURE': + return False + return any(map(lambda x: x.is_selected, pg.sequence_list)) def execute(self, context): pg = context.scene.psa_import psa_reader = PsaReader(pg.psa_file_path) - sequence_names = [x.action_name for x in pg.action_list] + sequence_names = [x.action_name for x in pg.sequence_list if x.is_selected] + options = PsaImportOptions() options.sequence_names = sequence_names options.should_clean_keys = pg.should_clean_keys options.should_use_fake_user = pg.should_use_fake_user options.should_stash = pg.should_stash options.action_name_prefix = pg.action_name_prefix + PsaImporter().import_psa(psa_reader, context.view_layer.objects.active, options) + self.report({'INFO'}, f'Imported {len(sequence_names)} action(s)') - return {'FINISHED'} - -class PsaImportPushToActions(Operator): - bl_idname = 'psa_import.push_to_actions' - bl_label = 'Push to Actions' - bl_options = {'INTERNAL'} - - @classmethod - def poll(cls, context): - pg = context.scene.psa_import - has_sequences_selected = any(map(lambda x: x.is_selected, pg.sequence_list)) - return has_sequences_selected - - def execute(self, context): - pg = context.scene.psa_import - indices_to_remove = [] - for sequence_index, item in enumerate(pg.sequence_list): - if item.is_selected: - indices_to_remove.append(sequence_index) - action = pg.action_list.add() - action.action_name = item.action_name - for index in reversed(indices_to_remove): - pg.sequence_list.remove(index) - return {'FINISHED'} - - -class PsaImportPopFromActions(Operator): - bl_idname = 'psa_import.pop_from_actions' - bl_label = 'Pop From Actions' - bl_options = {'INTERNAL'} - - @classmethod - def poll(cls, context): - pg = context.scene.psa_import - has_actions_selected = any(map(lambda x: x.is_selected, pg.action_list)) - return has_actions_selected - - def execute(self, context): - pg = context.scene.psa_import - indices_to_remove = [] - for action_index, item in enumerate(pg.action_list): - if item.is_selected: - indices_to_remove.append(action_index) - sequence = pg.sequence_list.add() - sequence.action_name = item.action_name - for index in reversed(indices_to_remove): - pg.action_list.remove(index) return {'FINISHED'} @@ -606,8 +513,6 @@ classes = ( PSA_UL_ImportActionList, PsaImportSequencesSelectAll, PsaImportSequencesDeselectAll, - PsaImportActionsSelectAll, - PsaImportActionsDeselectAll, PsaImportFileReload, PSA_PT_ImportPanel, PSA_PT_ImportPanel_Advanced, @@ -615,6 +520,4 @@ classes = ( PsaImportOperator, PsaImportFileSelectOperator, PsaImportSelectFile, - PsaImportPushToActions, - PsaImportPopFromActions, ) diff --git a/io_scene_psk_psa/psk/builder.py b/io_scene_psk_psa/psk/builder.py index de222b1..f48e139 100644 --- a/io_scene_psk_psa/psk/builder.py +++ b/io_scene_psk_psa/psk/builder.py @@ -70,12 +70,12 @@ class PskBuilder(object): # 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.name = bytes('static', encoding='windows-1252') + psk_bone.name = bytes('root', encoding='windows-1252') psk_bone.flags = 0 psk_bone.children_count = 0 psk_bone.parent_index = 0 - psk_bone.location = Vector3(0, 0, 0) - psk_bone.rotation = Quaternion(0, 0, 0, 1) + psk_bone.location = Vector3.zero() + psk_bone.rotation = Quaternion.identity() psk.bones.append(psk_bone) else: bone_names = get_export_bone_names(armature_object, options.bone_filter_mode, options.bone_group_indices)