Fixed a load of PEP8 warnings
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import datetime
|
||||
from collections import Counter
|
||||
import re
|
||||
from collections import Counter
|
||||
from typing import List, Iterable
|
||||
|
||||
import bpy.types
|
||||
from bpy.types import NlaStrip, Object
|
||||
from .types import BoneGroupListItem
|
||||
|
||||
|
||||
class Timer:
|
||||
@@ -47,7 +47,7 @@ def get_nla_strips_in_timeframe(animation_data, frame_min, frame_max) -> List[Nl
|
||||
return strips
|
||||
|
||||
|
||||
def populate_bone_group_list(armature_object: Object, bone_group_list: Iterable[BoneGroupListItem]) -> None:
|
||||
def populate_bone_group_list(armature_object: Object, bone_group_list: bpy.types.Collection) -> None:
|
||||
"""
|
||||
Updates the bone group collection.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Dict, Iterable
|
||||
from typing import Dict
|
||||
|
||||
from bpy.types import Action
|
||||
|
||||
@@ -217,7 +217,8 @@ def build_psa(context, options: PsaBuildOptions) -> Psa:
|
||||
|
||||
# Add prefixes and suffices to the names of the export sequences and strip whitespace.
|
||||
for export_sequence in export_sequences:
|
||||
export_sequence.name = f'{options.sequence_name_prefix}{export_sequence.name}{options.sequence_name_suffix}'.strip()
|
||||
export_sequence.name = f'{options.sequence_name_prefix}{export_sequence.name}{options.sequence_name_suffix}'
|
||||
export_sequence.name = export_sequence.name.strip()
|
||||
|
||||
# Now build the PSA sequences.
|
||||
# We actually alter the timeline frame and simply record the resultant pose bone matrices.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import fnmatch
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from typing import Type
|
||||
|
||||
import bpy
|
||||
@@ -16,7 +14,6 @@ from ..helpers import *
|
||||
from ..types import BoneGroupListItem
|
||||
|
||||
|
||||
def export_psa(psa: Psa, path: str):
|
||||
def write_section(fp, name: bytes, data_type: Type[Structure] = None, data: list = None):
|
||||
section = Section()
|
||||
section.name = name
|
||||
@@ -27,6 +24,9 @@ def export_psa(psa: Psa, path: str):
|
||||
if data is not None:
|
||||
for datum in data:
|
||||
fp.write(datum)
|
||||
|
||||
|
||||
def export_psa(psa: Psa, path: str):
|
||||
with open(path, 'wb') as fp:
|
||||
write_section(fp, b'ANIMHEAD')
|
||||
write_section(fp, b'BONENAMES', Psa.Bone, psa.bones)
|
||||
@@ -61,16 +61,19 @@ def psa_export_property_group_animation_data_override_poll(_context, obj):
|
||||
return obj.animation_data is not None
|
||||
|
||||
|
||||
empty_set = set()
|
||||
|
||||
|
||||
class PsaExportPropertyGroup(PropertyGroup):
|
||||
root_motion: BoolProperty(
|
||||
name='Root Motion',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
default=False,
|
||||
description='The root bone will be transformed as it appears in the scene',
|
||||
)
|
||||
should_override_animation_data: BoolProperty(
|
||||
name='Override Animation Data',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
default=False,
|
||||
description='Use the animation data from a different object instead of the selected object'
|
||||
)
|
||||
@@ -80,7 +83,7 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
)
|
||||
sequence_source: EnumProperty(
|
||||
name='Source',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='',
|
||||
items=(
|
||||
('ACTIONS', 'Actions', 'Sequences will be exported using actions', 'ACTION', 0),
|
||||
@@ -90,7 +93,7 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
)
|
||||
fps_source: EnumProperty(
|
||||
name='FPS Source',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='',
|
||||
items=(
|
||||
('SCENE', 'Scene', '', 'SCENE_DATA', 0),
|
||||
@@ -100,7 +103,7 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
('CUSTOM', 'Custom', '', 2)
|
||||
)
|
||||
)
|
||||
fps_custom: FloatProperty(default=30.0, min=sys.float_info.epsilon, soft_min=1.0, options=set(), step=100,
|
||||
fps_custom: FloatProperty(default=30.0, min=sys.float_info.epsilon, soft_min=1.0, options=empty_set, step=100,
|
||||
soft_max=60.0)
|
||||
action_list: CollectionProperty(type=PsaExportActionListItem)
|
||||
action_list_index: IntProperty(default=0)
|
||||
@@ -108,7 +111,7 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
marker_list_index: IntProperty(default=0)
|
||||
bone_filter_mode: EnumProperty(
|
||||
name='Bone Filter',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='',
|
||||
items=(
|
||||
('ALL', 'All', 'All bones will be exported.'),
|
||||
@@ -121,7 +124,7 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
should_use_original_sequence_names: BoolProperty(
|
||||
default=False,
|
||||
name='Original Names',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
update=should_use_original_sequence_names_updated,
|
||||
description='If the action was imported from the PSA Import panel, the original name of the sequence will be '
|
||||
'used instead of the Blender action name',
|
||||
@@ -129,12 +132,12 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
should_trim_timeline_marker_sequences: BoolProperty(
|
||||
default=True,
|
||||
name='Trim Sequences',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Frames without NLA track information at the boundaries of timeline markers will be excluded from '
|
||||
'the exported sequences '
|
||||
)
|
||||
sequence_name_prefix: StringProperty(name='Prefix', options=set())
|
||||
sequence_name_suffix: StringProperty(name='Suffix', options=set())
|
||||
sequence_name_prefix: StringProperty(name='Prefix', options=empty_set)
|
||||
sequence_name_suffix: StringProperty(name='Suffix', options=empty_set)
|
||||
sequence_filter_name: StringProperty(
|
||||
default='',
|
||||
name='Filter by Name',
|
||||
@@ -143,14 +146,14 @@ class PsaExportPropertyGroup(PropertyGroup):
|
||||
sequence_use_filter_invert: BoolProperty(
|
||||
default=False,
|
||||
name='Invert',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Invert filtering (show hidden items, and vice versa)')
|
||||
sequence_filter_asset: BoolProperty(
|
||||
default=False,
|
||||
name='Show assets',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Show actions that belong to an asset library')
|
||||
sequence_use_filter_sort_reverse: BoolProperty(default=True, options=set())
|
||||
sequence_use_filter_sort_reverse: BoolProperty(default=True, options=empty_set)
|
||||
|
||||
|
||||
def is_bone_filter_mode_item_available(context, identifier):
|
||||
@@ -182,13 +185,13 @@ class PsaExportOperator(Operator, ExportHelper):
|
||||
try:
|
||||
cls._check_context(context)
|
||||
except RuntimeError as e:
|
||||
cls.poll_message_set((str(e)))
|
||||
cls.poll_message_set(str(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
|
||||
# FPS
|
||||
layout.prop(pg, 'fps_source', text='FPS')
|
||||
@@ -291,7 +294,7 @@ class PsaExportOperator(Operator, ExportHelper):
|
||||
except RuntimeError as e:
|
||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
self.armature = context.view_layer.objects.active
|
||||
|
||||
# Populate actions list.
|
||||
@@ -326,7 +329,7 @@ class PsaExportOperator(Operator, ExportHelper):
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
|
||||
actions = [x.action for x in pg.action_list if x.is_selected]
|
||||
marker_names = [x.name for x in pg.marker_list if x.is_selected]
|
||||
@@ -349,6 +352,7 @@ class PsaExportOperator(Operator, ExportHelper):
|
||||
|
||||
try:
|
||||
psa = build_psa(context, options)
|
||||
self.report({'INFO'}, f'PSA export successful')
|
||||
except RuntimeError as e:
|
||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||
return {'CANCELLED'}
|
||||
@@ -358,7 +362,7 @@ class PsaExportOperator(Operator, ExportHelper):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def filter_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_collection) -> List[int]:
|
||||
def filter_sequences(pg: PsaExportPropertyGroup, sequences) -> List[int]:
|
||||
bitflag_filter_item = 1 << 30
|
||||
flt_flags = [bitflag_filter_item] * len(sequences)
|
||||
|
||||
@@ -381,7 +385,7 @@ def filter_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_c
|
||||
return flt_flags
|
||||
|
||||
|
||||
def get_visible_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_collection) -> List[PsaExportActionListItem]:
|
||||
def get_visible_sequences(pg: PsaExportPropertyGroup, sequences) -> List[PsaExportActionListItem]:
|
||||
visible_sequences = []
|
||||
for i, flag in enumerate(filter_sequences(pg, sequences)):
|
||||
if bool(flag & (1 << 30)):
|
||||
@@ -402,7 +406,7 @@ class PSA_UL_ExportSequenceList(UIList):
|
||||
layout.label(text='', icon='ASSET_MANAGER')
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
row = layout.row()
|
||||
subrow = row.row(align=True)
|
||||
subrow.prop(pg, 'sequence_filter_name', text="")
|
||||
@@ -414,7 +418,7 @@ class PSA_UL_ExportSequenceList(UIList):
|
||||
subrow.prop(pg, 'sequence_filter_asset', icon_only=True, icon='ASSET_MANAGER')
|
||||
|
||||
def filter_items(self, context, data, prop):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
actions = getattr(data, prop)
|
||||
flt_flags = filter_sequences(pg, actions)
|
||||
flt_neworder = bpy.types.UI_UL_list.sort_items_by_name(actions, 'name')
|
||||
@@ -438,14 +442,14 @@ class PsaExportActionsSelectAll(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
item_list = cls.get_item_list(context)
|
||||
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):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
sequences = self.get_item_list(context)
|
||||
for sequence in get_visible_sequences(pg, sequences):
|
||||
sequence.is_selected = True
|
||||
@@ -474,7 +478,7 @@ class PsaExportActionsDeselectAll(Operator):
|
||||
return len(item_list) > 0 and has_selected_items
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
item_list = self.get_item_list(context)
|
||||
for sequence in get_visible_sequences(pg, item_list):
|
||||
sequence.is_selected = False
|
||||
@@ -489,13 +493,13 @@ class PsaExportBoneGroupsSelectAll(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
item_list = pg.bone_group_list
|
||||
has_unselected_items = any(map(lambda action: not action.is_selected, item_list))
|
||||
return len(item_list) > 0 and has_unselected_items
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
for item in pg.bone_group_list:
|
||||
item.is_selected = True
|
||||
return {'FINISHED'}
|
||||
@@ -509,13 +513,13 @@ class PsaExportBoneGroupsDeselectAll(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
item_list = pg.bone_group_list
|
||||
has_selected_actions = any(map(lambda action: action.is_selected, item_list))
|
||||
return len(item_list) > 0 and has_selected_actions
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_export
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
for action in pg.bone_group_list:
|
||||
action.is_selected = False
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -26,10 +26,6 @@ class PsaImportOptions(object):
|
||||
self.action_name_prefix = ''
|
||||
|
||||
|
||||
def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions):
|
||||
sequences = map(lambda x: psa_reader.sequences[x], options.sequence_names)
|
||||
armature_data = armature_object.data
|
||||
|
||||
class ImportBone(object):
|
||||
def __init__(self, psa_bone: Psa.Bone):
|
||||
self.psa_bone: Psa.Bone = psa_bone
|
||||
@@ -41,6 +37,7 @@ def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions
|
||||
self.post_quat: Quaternion = Quaternion()
|
||||
self.fcurves = []
|
||||
|
||||
|
||||
def calculate_fcurve_data(import_bone: ImportBone, key_data: []):
|
||||
# Convert world-space transforms to local-space transforms.
|
||||
key_rotation = Quaternion(key_data[0:4])
|
||||
@@ -58,6 +55,11 @@ def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions
|
||||
loc.rotate(import_bone.post_quat.conjugated())
|
||||
return quat.w, quat.x, quat.y, quat.z, loc.x, loc.y, loc.z
|
||||
|
||||
|
||||
def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions):
|
||||
sequences = map(lambda x: psa_reader.sequences[x], options.sequence_names)
|
||||
armature_data = armature_object.data
|
||||
|
||||
# Create an index mapping from bones in the PSA to bones in the target armature.
|
||||
psa_to_armature_bone_indices = {}
|
||||
armature_bone_names = [x.name for x in armature_data.bones]
|
||||
@@ -176,7 +178,8 @@ def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions
|
||||
fcurve_frame_data = sequence_data_matrix[:, bone_index, fcurve_index]
|
||||
last_written_datum = 0
|
||||
for frame_index, datum in enumerate(fcurve_frame_data):
|
||||
# If the f-curve data is not different enough to the last written frame, un-mark this data for writing.
|
||||
# If the f-curve data is not different enough to the last written frame,
|
||||
# un-mark this data for writing.
|
||||
if frame_index > 0 and abs(datum - last_written_datum) < threshold:
|
||||
keyframe_write_matrix[frame_index, bone_index, fcurve_index] = 0
|
||||
else:
|
||||
@@ -217,9 +220,12 @@ def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions
|
||||
nla_track.strips.new(name=action.name, start=0, action=action)
|
||||
|
||||
|
||||
empty_set = set()
|
||||
|
||||
|
||||
class PsaImportActionListItem(PropertyGroup):
|
||||
action_name: StringProperty(options=set())
|
||||
is_selected: BoolProperty(default=False, options=set())
|
||||
action_name: StringProperty(options=empty_set)
|
||||
is_selected: BoolProperty(default=False, options=empty_set)
|
||||
|
||||
|
||||
def load_psa_file(context):
|
||||
@@ -246,7 +252,7 @@ def on_psa_file_path_updated(property, context):
|
||||
|
||||
|
||||
class PsaBonePropertyGroup(PropertyGroup):
|
||||
bone_name: StringProperty(options=set())
|
||||
bone_name: StringProperty(options=empty_set)
|
||||
|
||||
|
||||
class PsaDataPropertyGroup(PropertyGroup):
|
||||
@@ -255,37 +261,37 @@ class PsaDataPropertyGroup(PropertyGroup):
|
||||
|
||||
|
||||
class PsaImportPropertyGroup(PropertyGroup):
|
||||
psa_file_path: StringProperty(default='', options=set(), update=on_psa_file_path_updated, name='PSA File Path')
|
||||
psa_file_path: StringProperty(default='', options=empty_set, update=on_psa_file_path_updated, name='PSA File Path')
|
||||
psa_error: StringProperty(default='')
|
||||
psa: PointerProperty(type=PsaDataPropertyGroup)
|
||||
sequence_list: CollectionProperty(type=PsaImportActionListItem)
|
||||
sequence_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())
|
||||
options=empty_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())
|
||||
options=empty_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())
|
||||
should_overwrite: BoolProperty(default=False, name='Reuse Existing Actions', options=set(),
|
||||
options=empty_set)
|
||||
should_use_action_name_prefix: BoolProperty(default=False, name='Prefix Action Name', options=empty_set)
|
||||
action_name_prefix: StringProperty(default='', name='Prefix', options=empty_set)
|
||||
should_overwrite: BoolProperty(default=False, name='Reuse Existing Actions', options=empty_set,
|
||||
description='If an action with a matching name already exists, the existing action will have it\'s data overwritten instead of a new action being created')
|
||||
should_write_keyframes: BoolProperty(default=True, name='Keyframes', options=set())
|
||||
should_write_metadata: BoolProperty(default=True, name='Metadata', options=set(),
|
||||
should_write_keyframes: BoolProperty(default=True, name='Keyframes', options=empty_set)
|
||||
should_write_metadata: BoolProperty(default=True, name='Metadata', options=empty_set,
|
||||
description='Additional data will be written to the custom properties of the Action (e.g., frame rate)')
|
||||
sequence_filter_name: StringProperty(default='', options={'TEXTEDIT_UPDATE'})
|
||||
sequence_filter_is_selected: BoolProperty(default=False, options=set(), name='Only Show Selected',
|
||||
sequence_filter_is_selected: BoolProperty(default=False, options=empty_set, name='Only Show Selected',
|
||||
description='Only show selected sequences')
|
||||
sequence_use_filter_invert: BoolProperty(default=False, options=set())
|
||||
sequence_use_filter_invert: BoolProperty(default=False, options=empty_set)
|
||||
sequence_use_filter_regex: BoolProperty(default=False, name='Regular Expression',
|
||||
description='Filter using regular expressions', options=set())
|
||||
description='Filter using regular expressions', options=empty_set)
|
||||
select_text: PointerProperty(type=bpy.types.Text)
|
||||
|
||||
|
||||
def filter_sequences(pg: PsaImportPropertyGroup, sequences: bpy.types.bpy_prop_collection) -> List[int]:
|
||||
def filter_sequences(pg: PsaImportPropertyGroup, sequences) -> List[int]:
|
||||
bitflag_filter_item = 1 << 30
|
||||
flt_flags = [bitflag_filter_item] * len(sequences)
|
||||
|
||||
@@ -319,8 +325,7 @@ def filter_sequences(pg: PsaImportPropertyGroup, sequences: bpy.types.bpy_prop_c
|
||||
return flt_flags
|
||||
|
||||
|
||||
def get_visible_sequences(pg: PsaImportPropertyGroup, sequences: bpy.types.bpy_prop_collection) -> List[
|
||||
PsaImportActionListItem]:
|
||||
def get_visible_sequences(pg: PsaImportPropertyGroup, sequences) -> List[PsaImportActionListItem]:
|
||||
bitflag_filter_item = 1 << 30
|
||||
visible_sequences = []
|
||||
for i, flag in enumerate(filter_sequences(pg, sequences)):
|
||||
@@ -330,26 +335,25 @@ def get_visible_sequences(pg: PsaImportPropertyGroup, sequences: bpy.types.bpy_p
|
||||
|
||||
|
||||
class PSA_UL_SequenceList(UIList):
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_property, index, flt_flag):
|
||||
row = layout.row(align=True)
|
||||
split = row.split(align=True, factor=0.75)
|
||||
column = split.row(align=True)
|
||||
column.alignment = 'LEFT'
|
||||
column.prop(item, 'is_selected', icon_only=True)
|
||||
column.label(text=item.action_name)
|
||||
column.label(text=getattr(item, 'action_name'))
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
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.prop(pg, 'sequence_use_filter_regex', text="", icon='SORTBYEXT')
|
||||
subrow.prop(pg, 'sequence_filter_is_selected', text="", icon='CHECKBOX_HLT')
|
||||
sub_row = row.row(align=True)
|
||||
sub_row.prop(pg, 'sequence_filter_name', text="")
|
||||
sub_row.prop(pg, 'sequence_use_filter_invert', text="", icon='ARROW_LEFTRIGHT')
|
||||
sub_row.prop(pg, 'sequence_use_filter_regex', text="", icon='SORTBYEXT')
|
||||
sub_row.prop(pg, 'sequence_filter_is_selected', text="", icon='CHECKBOX_HLT')
|
||||
|
||||
def filter_items(self, context, data, property):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
sequences = getattr(data, property)
|
||||
flt_flags = filter_sequences(pg, sequences)
|
||||
flt_neworder = bpy.types.UI_UL_list.sort_items_by_name(sequences, 'action_name')
|
||||
@@ -372,7 +376,7 @@ class PsaImportSequencesFromText(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
return len(pg.sequence_list) > 0
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -380,12 +384,12 @@ class PsaImportSequencesFromText(Operator):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
layout.label(icon='INFO', text='Each sequence name should be on a new line.')
|
||||
layout.prop(pg, 'select_text', text='')
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
if pg.select_text is None:
|
||||
self.report({'ERROR_INVALID_CONTEXT'}, 'No text block selected')
|
||||
return {'CANCELLED'}
|
||||
@@ -408,13 +412,13 @@ class PsaImportSequencesSelectAll(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
visible_sequences = get_visible_sequences(pg, pg.sequence_list)
|
||||
has_unselected_actions = any(map(lambda action: not action.is_selected, visible_sequences))
|
||||
return len(visible_sequences) > 0 and has_unselected_actions
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
visible_sequences = get_visible_sequences(pg, pg.sequence_list)
|
||||
for sequence in visible_sequences:
|
||||
sequence.is_selected = True
|
||||
@@ -429,13 +433,13 @@ class PsaImportSequencesDeselectAll(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
visible_sequences = get_visible_sequences(pg, pg.sequence_list)
|
||||
has_selected_sequences = any(map(lambda sequence: sequence.is_selected, visible_sequences))
|
||||
return len(visible_sequences) > 0 and has_selected_sequences
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
visible_sequences = get_visible_sequences(pg, pg.sequence_list)
|
||||
for sequence in visible_sequences:
|
||||
sequence.is_selected = False
|
||||
@@ -451,7 +455,7 @@ class PSA_PT_ImportPanel_Advanced(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
|
||||
col = layout.column(heading="Options")
|
||||
col.use_property_split = True
|
||||
@@ -476,11 +480,11 @@ class PSA_PT_ImportPanel(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object.type == 'ARMATURE'
|
||||
return context.view_layer.objects.active.type == 'ARMATURE'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator(PsaImportSelectFile.bl_idname, text='', icon='FILEBROWSER')
|
||||
@@ -552,7 +556,7 @@ class PsaImportSelectFile(Operator):
|
||||
filter_glob: bpy.props.StringProperty(default="*.psa", options={'HIDDEN'})
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.psa_import.psa_file_path = self.filepath
|
||||
getattr(context.scene, 'psa_import').psa_file_path = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -568,14 +572,14 @@ class PsaImportOperator(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
active_object = context.view_layer.objects.active
|
||||
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
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
psa_reader = PsaReader(pg.psa_file_path)
|
||||
sequence_names = [x.action_name for x in pg.sequence_list if x.is_selected]
|
||||
|
||||
@@ -613,7 +617,7 @@ class PsaImportFileSelectOperator(Operator, ImportHelper):
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psa_import
|
||||
pg = getattr(context.scene, 'psa_import')
|
||||
pg.psa_file_path = self.filepath
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ from .data import *
|
||||
class PsaReader(object):
|
||||
"""
|
||||
This class reads the sequences and bone information immediately upon instantiation and holds onto a file handle.
|
||||
The keyframe data is not read into memory upon instantiation due to it's potentially very large size.
|
||||
To read the key data for a particular sequence, call `read_sequence_keys`.
|
||||
The keyframe data is not read into memory upon instantiation due to its potentially very large size.
|
||||
To read the key data for a particular sequence, call :read_sequence_keys.
|
||||
"""
|
||||
|
||||
def __init__(self, path):
|
||||
@@ -38,7 +38,8 @@ class PsaReader(object):
|
||||
return matrix
|
||||
|
||||
def read_sequence_keys(self, sequence_name: str) -> List[Psa.Key]:
|
||||
""" Reads and returns the key data for a sequence.
|
||||
"""
|
||||
Reads and returns the key data for a sequence.
|
||||
|
||||
:param sequence_name: The name of the sequence.
|
||||
:return: A list of Psa.Keys.
|
||||
@@ -60,7 +61,7 @@ class PsaReader(object):
|
||||
return keys
|
||||
|
||||
@staticmethod
|
||||
def _read_types(fp, data_class: ctypes.Structure, section: Section, data):
|
||||
def _read_types(fp, data_class, section: Section, data):
|
||||
buffer_length = section.data_size * section.data_count
|
||||
buffer = fp.read(buffer_length)
|
||||
offset = 0
|
||||
@@ -86,7 +87,7 @@ class PsaReader(object):
|
||||
# Skip keys on this pass. We will keep this file open and read from it as needed.
|
||||
self.keys_data_offset = fp.tell()
|
||||
fp.seek(section.data_size * section.data_count, 1)
|
||||
elif section.name in [b'SCALEKEYS']:
|
||||
elif section.name == b'SCALEKEYS':
|
||||
fp.seek(section.data_size * section.data_count, 1)
|
||||
else:
|
||||
raise RuntimeError(f'Unrecognized section "{section.name}"')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Type
|
||||
|
||||
from bpy.props import BoolProperty, StringProperty, CollectionProperty, IntProperty, EnumProperty, PointerProperty
|
||||
from bpy.types import Operator, PropertyGroup, UIList, Material
|
||||
from bpy.props import BoolProperty, StringProperty, CollectionProperty, IntProperty, EnumProperty
|
||||
from bpy.types import Operator, PropertyGroup, UIList
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
|
||||
from .builder import build_psk, PskBuildOptions, get_psk_input_objects
|
||||
@@ -70,7 +70,7 @@ def is_bone_filter_mode_item_available(context, identifier):
|
||||
class PSK_UL_MaterialList(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
row = layout.row()
|
||||
row.label(text=str(item.material_name), icon='MATERIAL')
|
||||
row.label(text=str(getattr(item, 'material_name')), icon='MATERIAL')
|
||||
|
||||
|
||||
class MaterialListItem(PropertyGroup):
|
||||
@@ -108,11 +108,11 @@ class PskMaterialListItemMoveUp(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psk_export
|
||||
pg = getattr(context.scene, 'psk_export')
|
||||
return pg.material_list_index > 0
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psk_export
|
||||
pg = getattr(context.scene, 'psk_export')
|
||||
pg.material_list.move(pg.material_list_index, pg.material_list_index - 1)
|
||||
pg.material_list_index -= 1
|
||||
return {"FINISHED"}
|
||||
@@ -126,11 +126,11 @@ class PskMaterialListItemMoveDown(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pg = context.scene.psk_export
|
||||
pg = getattr(context.scene, 'psk_export')
|
||||
return pg.material_list_index < len(pg.material_list) - 1
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psk_export
|
||||
pg = getattr(context.scene, 'psk_export')
|
||||
pg.material_list.move(pg.material_list_index, pg.material_list_index + 1)
|
||||
pg.material_list_index += 1
|
||||
return {"FINISHED"}
|
||||
@@ -157,7 +157,7 @@ class PskExportOperator(Operator, ExportHelper):
|
||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||
return {'CANCELLED'}
|
||||
|
||||
pg = context.scene.psk_export
|
||||
pg = getattr(context.scene, 'psk_export')
|
||||
|
||||
# Populate bone groups list.
|
||||
populate_bone_group_list(input_objects.armature_object, pg.bone_group_list)
|
||||
@@ -178,8 +178,7 @@ class PskExportOperator(Operator, ExportHelper):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
pg = scene.psk_export
|
||||
pg = getattr(context.scene, 'psk_export')
|
||||
|
||||
layout.prop(pg, 'use_raw_mesh_data')
|
||||
|
||||
@@ -220,6 +219,7 @@ class PskExportOperator(Operator, ExportHelper):
|
||||
try:
|
||||
psk = build_psk(context, options)
|
||||
export_psk(psk, self.filepath)
|
||||
self.report({'INFO'}, f'PSK export successful')
|
||||
except RuntimeError as e:
|
||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||
return {'CANCELLED'}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import os
|
||||
import sys
|
||||
from math import inf
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
|
||||
import bmesh
|
||||
import bpy
|
||||
import numpy as np
|
||||
from bpy.props import BoolProperty, EnumProperty, FloatProperty, StringProperty
|
||||
from bpy.types import Operator, PropertyGroup
|
||||
from bpy.types import Operator, PropertyGroup, VertexGroup
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from mathutils import Quaternion, Vector, Matrix
|
||||
|
||||
@@ -28,6 +28,24 @@ class PskImportOptions(object):
|
||||
self.bone_length = 1.0
|
||||
|
||||
|
||||
class ImportBone(object):
|
||||
"""
|
||||
Intermediate bone type for the purpose of construction.
|
||||
"""
|
||||
def __init__(self, index: int, psk_bone: Psk.Bone):
|
||||
self.index: int = index
|
||||
self.psk_bone: Psk.Bone = psk_bone
|
||||
self.parent: Optional[ImportBone] = None
|
||||
self.local_rotation: Quaternion = Quaternion()
|
||||
self.local_translation: Vector = Vector()
|
||||
self.world_rotation_matrix: Matrix = Matrix()
|
||||
self.world_matrix: Matrix = Matrix()
|
||||
self.vertex_group = None
|
||||
self.orig_quat: Quaternion = Quaternion()
|
||||
self.orig_loc: Vector = Vector()
|
||||
self.post_quat: Quaternion = Quaternion()
|
||||
|
||||
|
||||
def import_psk(psk: Psk, context, options: PskImportOptions):
|
||||
armature_object = None
|
||||
|
||||
@@ -49,21 +67,6 @@ def import_psk(psk: Psk, context, options: PskImportOptions):
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
# Intermediate bone type for the purpose of construction.
|
||||
class ImportBone(object):
|
||||
def __init__(self, index: int, psk_bone: Psk.Bone):
|
||||
self.index: int = index
|
||||
self.psk_bone: Psk.Bone = psk_bone
|
||||
self.parent: Optional[ImportBone] = None
|
||||
self.local_rotation: Quaternion = Quaternion()
|
||||
self.local_translation: Vector = Vector()
|
||||
self.world_rotation_matrix: Matrix = Matrix()
|
||||
self.world_matrix: Matrix = Matrix()
|
||||
self.vertex_group = None
|
||||
self.orig_quat: Quaternion = Quaternion()
|
||||
self.orig_loc: Vector = Vector()
|
||||
self.post_quat: Quaternion = Quaternion()
|
||||
|
||||
import_bones = []
|
||||
|
||||
for bone_index, psk_bone in enumerate(psk.bones):
|
||||
@@ -213,7 +216,7 @@ def import_psk(psk: Psk, context, options: PskImportOptions):
|
||||
|
||||
# Get a list of all bones that have weights associated with them.
|
||||
vertex_group_bone_indices = set(map(lambda weight: weight.bone_index, psk.weights))
|
||||
vertex_groups = [None] * len(psk.bones)
|
||||
vertex_groups: List[Optional[VertexGroup]] = [None] * len(psk.bones)
|
||||
for bone_index, psk_bone in map(lambda x: (x, psk.bones[x]), vertex_group_bone_indices):
|
||||
vertex_groups[bone_index] = mesh_object.vertex_groups.new(name=psk_bone.name.decode('windows-1252'))
|
||||
|
||||
@@ -234,16 +237,19 @@ def import_psk(psk: Psk, context, options: PskImportOptions):
|
||||
pass
|
||||
|
||||
|
||||
empty_set = set()
|
||||
|
||||
|
||||
class PskImportPropertyGroup(PropertyGroup):
|
||||
should_import_vertex_colors: BoolProperty(
|
||||
default=True,
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
name='Vertex Colors',
|
||||
description='Import vertex colors from PSKX files, if available'
|
||||
)
|
||||
vertex_color_space: EnumProperty(
|
||||
name='Vertex Color Space',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='The source vertex color space',
|
||||
default='SRGBA',
|
||||
items=(
|
||||
@@ -254,25 +260,25 @@ class PskImportPropertyGroup(PropertyGroup):
|
||||
should_import_vertex_normals: BoolProperty(
|
||||
default=True,
|
||||
name='Vertex Normals',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Import vertex normals from PSKX files, if available'
|
||||
)
|
||||
should_import_extra_uvs: BoolProperty(
|
||||
default=True,
|
||||
name='Extra UVs',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Import extra UV maps from PSKX files, if available'
|
||||
)
|
||||
should_import_mesh: BoolProperty(
|
||||
default=True,
|
||||
name='Import Mesh',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Import mesh'
|
||||
)
|
||||
should_import_skeleton: BoolProperty(
|
||||
default=True,
|
||||
name='Import Skeleton',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Import skeleton'
|
||||
)
|
||||
bone_length: FloatProperty(
|
||||
@@ -281,7 +287,7 @@ class PskImportPropertyGroup(PropertyGroup):
|
||||
step=100,
|
||||
soft_min=1.0,
|
||||
name='Bone Length',
|
||||
options=set(),
|
||||
options=empty_set,
|
||||
description='Length of the bones'
|
||||
)
|
||||
|
||||
@@ -300,7 +306,7 @@ class PskImportOperator(Operator, ImportHelper):
|
||||
default='')
|
||||
|
||||
def execute(self, context):
|
||||
pg = context.scene.psk_import
|
||||
pg = getattr(context.scene, 'psk_import')
|
||||
|
||||
psk = read_psk(self.filepath)
|
||||
|
||||
@@ -319,7 +325,7 @@ class PskImportOperator(Operator, ImportHelper):
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
pg = context.scene.psk_import
|
||||
pg = getattr(context.scene, 'psk_import')
|
||||
layout = self.layout
|
||||
layout.prop(pg, 'should_import_mesh')
|
||||
row = layout.column()
|
||||
|
||||
@@ -3,7 +3,7 @@ import ctypes
|
||||
from .data import *
|
||||
|
||||
|
||||
def _read_types(fp, data_class: ctypes.Structure, section: Section, data):
|
||||
def _read_types(fp, data_class, section: Section, data):
|
||||
buffer_length = section.data_size * section.data_count
|
||||
buffer = fp.read(buffer_length)
|
||||
offset = 0
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from bpy.props import StringProperty, IntProperty, BoolProperty
|
||||
from bpy.types import PropertyGroup, UIList
|
||||
from bpy.types import PropertyGroup, UIList, UILayout, Context, AnyType
|
||||
|
||||
|
||||
class PSX_UL_BoneGroupList(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
|
||||
def draw_item(self, context: Context, layout: UILayout, data: AnyType, item: AnyType, icon: int,
|
||||
active_data: AnyType, active_property: str, index: int = 0, flt_flag: int = 0):
|
||||
row = layout.row()
|
||||
row.prop(item, 'is_selected', text=item.name)
|
||||
row.label(text=str(item.count), icon='BONE_DATA')
|
||||
row.prop(item, 'is_selected', text=getattr(item, 'name'))
|
||||
row.label(text=str(getattr(item, 'count')), icon='BONE_DATA')
|
||||
|
||||
|
||||
class BoneGroupListItem(PropertyGroup):
|
||||
|
||||
Reference in New Issue
Block a user