Fixed a load of PEP8 warnings

This commit is contained in:
Colin Basnett
2022-08-06 23:52:18 -07:00
parent 96001651c6
commit cd490af431
9 changed files with 181 additions and 163 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -1,7 +1,5 @@
import fnmatch
import re
import sys
from collections import Counter
from typing import Type
import bpy
@@ -16,17 +14,19 @@ from ..helpers import *
from ..types import BoneGroupListItem
def write_section(fp, name: bytes, data_type: Type[Structure] = None, data: list = None):
section = Section()
section.name = name
if data_type is not None and data is not None:
section.data_size = sizeof(data_type)
section.data_count = len(data)
fp.write(section)
if data is not None:
for datum in data:
fp.write(datum)
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
if data_type is not None and data is not None:
section.data_size = sizeof(data_type)
section.data_count = len(data)
fp.write(section)
if data is not None:
for datum in data:
fp.write(datum)
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'}

View File

@@ -26,38 +26,40 @@ class PsaImportOptions(object):
self.action_name_prefix = ''
class ImportBone(object):
def __init__(self, psa_bone: Psa.Bone):
self.psa_bone: Psa.Bone = psa_bone
self.parent: Optional[ImportBone] = None
self.armature_bone = None
self.pose_bone = None
self.orig_loc: Vector = Vector()
self.orig_quat: Quaternion = Quaternion()
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])
key_location = Vector(key_data[4:])
q = import_bone.post_quat.copy()
q.rotate(import_bone.orig_quat)
quat = q
q = import_bone.post_quat.copy()
if import_bone.parent is None:
q.rotate(key_rotation.conjugated())
else:
q.rotate(key_rotation)
quat.rotate(q.conjugated())
loc = key_location - import_bone.orig_loc
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
class ImportBone(object):
def __init__(self, psa_bone: Psa.Bone):
self.psa_bone: Psa.Bone = psa_bone
self.parent: Optional[ImportBone] = None
self.armature_bone = None
self.pose_bone = None
self.orig_loc: Vector = Vector()
self.orig_quat: Quaternion = Quaternion()
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])
key_location = Vector(key_data[4:])
q = import_bone.post_quat.copy()
q.rotate(import_bone.orig_quat)
quat = q
q = import_bone.post_quat.copy()
if import_bone.parent is None:
q.rotate(key_rotation.conjugated())
else:
q.rotate(key_rotation)
quat.rotate(q.conjugated())
loc = key_location - import_bone.orig_loc
loc.rotate(import_bone.post_quat.conjugated())
return quat.w, quat.x, quat.y, quat.z, loc.x, loc.y, loc.z
# 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'}

View File

@@ -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}"')

View File

@@ -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'}

View File

@@ -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()

View File

@@ -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

View File

@@ -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):