Implement #136: Added support for exporting group properties for sequences

This commit is contained in:
Colin Basnett
2025-12-03 17:44:05 -08:00
parent 94c940b970
commit cc730b6ce3
2 changed files with 50 additions and 8 deletions

View File

@@ -18,7 +18,6 @@ from ..builder import build_psa, PsaBuildSequence, PsaBuildOptions
from psk_psa_py.psa.writer import write_psa_to_file from psk_psa_py.psa.writer import write_psa_to_file
from ...shared.helpers import populate_bone_collection_list, get_nla_strips_in_frame_range, PsxBoneCollection from ...shared.helpers import populate_bone_collection_list, get_nla_strips_in_frame_range, PsxBoneCollection
from ...shared.ui import draw_bone_filter_mode from ...shared.ui import draw_bone_filter_mode
from ...shared.types import PSX_PG_action_export, PSX_PG_scene_export
def get_sequences_propnames_from_source(sequence_source: str) -> Tuple[str, str]: def get_sequences_propnames_from_source(sequence_source: str) -> Tuple[str, str]:
@@ -301,6 +300,7 @@ class PSA_OT_export(Operator, ExportHelper):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
assert layout
pg = getattr(context.scene, 'psa_export') pg = getattr(context.scene, 'psa_export')
sequences_header, sequences_panel = layout.panel('Sequences', default_closed=False) sequences_header, sequences_panel = layout.panel('Sequences', default_closed=False)
@@ -334,7 +334,7 @@ class PSA_OT_export(Operator, ExportHelper):
sequences_panel.template_list(PSA_UL_export_sequences.bl_idname, '', pg, propname, pg, active_propname, sequences_panel.template_list(PSA_UL_export_sequences.bl_idname, '', pg, propname, pg, active_propname,
rows=max(3, min(len(getattr(pg, propname)), 10))) rows=max(3, min(len(getattr(pg, propname)), 10)))
name_header, name_panel = layout.panel('Name', default_closed=False) name_header, name_panel = sequences_panel.panel('Name', default_closed=False)
name_header.label(text='Name') name_header.label(text='Name')
if name_panel: if name_panel:
flow = name_panel.grid_flow() flow = name_panel.grid_flow()
@@ -351,8 +351,20 @@ class PSA_OT_export(Operator, ExportHelper):
if count > 1: if count > 1:
layout.label(text=f'Duplicate action: {action_name}', icon='ERROR') layout.label(text=f'Duplicate action: {action_name}', icon='ERROR')
break break
# Group
group_header, group_panel = sequences_panel.panel('Group', default_closed=True)
group_header.label(text='Group')
if group_panel is not None:
group_flow = group_panel.grid_flow()
group_flow.use_property_split = True
group_flow.use_property_decorate = False
group_flow.prop(pg, 'group_source')
if pg.group_source == 'CUSTOM':
group_flow.prop(pg, 'group_custom', placeholder='Group')
sampling_header, sampling_panel = layout.panel('Data Source', default_closed=False) # Sampling
sampling_header, sampling_panel = sequences_panel.panel('Data Source', default_closed=False)
sampling_header.label(text='Sampling') sampling_header.label(text='Sampling')
if sampling_panel: if sampling_panel:
flow = sampling_panel.grid_flow() flow = sampling_panel.grid_flow()
@@ -431,6 +443,7 @@ class PSA_OT_export(Operator, ExportHelper):
self._check_context(context) self._check_context(context)
except RuntimeError as e: except RuntimeError as e:
self.report({'ERROR_INVALID_CONTEXT'}, str(e)) self.report({'ERROR_INVALID_CONTEXT'}, str(e))
return {'CANCELLED'}
pg: PSA_PG_export = getattr(context.scene, 'psa_export') pg: PSA_PG_export = getattr(context.scene, 'psa_export')
@@ -468,6 +481,15 @@ class PSA_OT_export(Operator, ExportHelper):
export_sequences: List[PsaBuildSequence] = [] export_sequences: List[PsaBuildSequence] = []
def get_export_sequence_group(group_source: str, group_custom: str | None, action: Action | None) -> str | None:
match group_source:
case 'ACTIONS':
return action.psa_export.group if action else None
case 'CUSTOM':
return group_custom
case _:
return None
match pg.sequence_source: match pg.sequence_source:
case 'ACTIONS': case 'ACTIONS':
for action_item in filter(lambda x: x.is_selected, pg.action_list): for action_item in filter(lambda x: x.is_selected, pg.action_list):
@@ -475,7 +497,7 @@ class PSA_OT_export(Operator, ExportHelper):
continue continue
export_sequence = PsaBuildSequence(context.active_object, animation_data) export_sequence = PsaBuildSequence(context.active_object, animation_data)
export_sequence.name = action_item.name export_sequence.name = action_item.name
export_sequence.group = action_item.group export_sequence.group = get_export_sequence_group(pg.group_source, pg.group_custom, action_item.action)
export_sequence.nla_state.action = action_item.action export_sequence.nla_state.action = action_item.action
export_sequence.nla_state.frame_start = action_item.frame_start export_sequence.nla_state.frame_start = action_item.frame_start
export_sequence.nla_state.frame_end = action_item.frame_end export_sequence.nla_state.frame_end = action_item.frame_end
@@ -485,12 +507,15 @@ class PSA_OT_export(Operator, ExportHelper):
export_sequences.append(export_sequence) export_sequences.append(export_sequence)
case 'TIMELINE_MARKERS': case 'TIMELINE_MARKERS':
for marker_item in filter(lambda x: x.is_selected, pg.marker_list): for marker_item in filter(lambda x: x.is_selected, pg.marker_list):
nla_strips_actions: List[Action] = []
for nla_strip in get_nla_strips_in_frame_range(animation_data, marker_item.frame_start, marker_item.frame_end):
if nla_strip.action:
nla_strips_actions.append(nla_strip.action)
export_sequence = PsaBuildSequence(context.active_object, animation_data) export_sequence = PsaBuildSequence(context.active_object, animation_data)
export_sequence.name = marker_item.name export_sequence.name = marker_item.name
export_sequence.group = get_export_sequence_group(pg.group_source, pg.group_custom, next(iter(nla_strips_actions), None))
export_sequence.nla_state.frame_start = marker_item.frame_start export_sequence.nla_state.frame_start = marker_item.frame_start
export_sequence.nla_state.frame_end = marker_item.frame_end export_sequence.nla_state.frame_end = marker_item.frame_end
nla_strips_actions = set(
map(lambda x: x.action, get_nla_strips_in_frame_range(animation_data, marker_item.frame_start, marker_item.frame_end)))
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, nla_strips_actions) export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, nla_strips_actions)
export_sequence.compression_ratio = get_sequence_compression_ratio(pg.compression_ratio_source, pg.compression_ratio_custom, nla_strips_actions) export_sequence.compression_ratio = get_sequence_compression_ratio(pg.compression_ratio_source, pg.compression_ratio_custom, nla_strips_actions)
export_sequences.append(export_sequence) export_sequences.append(export_sequence)
@@ -498,7 +523,7 @@ class PSA_OT_export(Operator, ExportHelper):
for nla_strip_item in filter(lambda x: x.is_selected, pg.nla_strip_list): for nla_strip_item in filter(lambda x: x.is_selected, pg.nla_strip_list):
export_sequence = PsaBuildSequence(context.active_object, animation_data) export_sequence = PsaBuildSequence(context.active_object, animation_data)
export_sequence.name = nla_strip_item.name export_sequence.name = nla_strip_item.name
export_sequence.group = nla_strip_item.action.psa_export.group export_sequence.group = get_export_sequence_group(pg.group_source, pg.group_custom, nla_strip_item.action)
export_sequence.nla_state.frame_start = nla_strip_item.frame_start export_sequence.nla_state.frame_start = nla_strip_item.frame_start
export_sequence.nla_state.frame_end = nla_strip_item.frame_end export_sequence.nla_state.frame_end = nla_strip_item.frame_end
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [nla_strip_item.action]) export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [nla_strip_item.action])
@@ -510,7 +535,7 @@ class PSA_OT_export(Operator, ExportHelper):
export_sequence = PsaBuildSequence(active_action_item.armature_object, active_action_item.armature_object.animation_data) export_sequence = PsaBuildSequence(active_action_item.armature_object, active_action_item.armature_object.animation_data)
action = active_action_item.action action = active_action_item.action
export_sequence.name = action.name export_sequence.name = action.name
export_sequence.group = action.psa_export.group export_sequence.group = get_export_sequence_group(pg.group_source, pg.group_custom, action)
export_sequence.nla_state.action = action export_sequence.nla_state.action = action
export_sequence.nla_state.frame_start = int(action.frame_range[0]) export_sequence.nla_state.frame_start = int(action.frame_range[0])
export_sequence.nla_state.frame_end = int(action.frame_range[1]) export_sequence.nla_state.frame_end = int(action.frame_range[1])

View File

@@ -133,6 +133,11 @@ sampling_mode_items = (
('SUBFRAME', 'Subframe', 'Sampling is performed by evaluating the bone poses at the subframe time.\n\nNot recommended unless you are also animating with subframes enabled.', 'SUBFRAME', 1), ('SUBFRAME', 'Subframe', 'Sampling is performed by evaluating the bone poses at the subframe time.\n\nNot recommended unless you are also animating with subframes enabled.', 'SUBFRAME', 1),
) )
group_source_items = (
('ACTIONS', 'Actions', '', 0),
('CUSTOM', 'Custom', '', 1),
)
def sequence_source_update_cb(self: 'PSA_PG_export', context: Context) -> None: def sequence_source_update_cb(self: 'PSA_PG_export', context: Context) -> None:
armature_objects = [] armature_objects = []
@@ -232,6 +237,18 @@ class PSA_PG_export(PropertyGroup, TransformMixin, ExportSpaceMixin, PsxBoneExpo
items=sampling_mode_items, items=sampling_mode_items,
default='INTERPOLATED' default='INTERPOLATED'
) )
group_source: EnumProperty(
name='Group Source',
options=set(),
description='The source of the exported sequence\'s group property',
items=group_source_items,
default='ACTIONS'
)
group_custom: StringProperty(
name='Group',
options=set(),
description='The group to apply to all exported sequences. Only applicable when Group Source is Custom.'
)
def filter_sequences(pg: PSA_PG_export, sequences) -> List[int]: def filter_sequences(pg: PSA_PG_export, sequences) -> List[int]: