Added the ability to filter bone collections from PSK collection exporter
This commit is contained in:
@@ -6,6 +6,8 @@ if 'bpy' in locals():
|
|||||||
importlib.reload(shared_data)
|
importlib.reload(shared_data)
|
||||||
importlib.reload(shared_helpers)
|
importlib.reload(shared_helpers)
|
||||||
importlib.reload(shared_types)
|
importlib.reload(shared_types)
|
||||||
|
importlib.reload(shared_dfs)
|
||||||
|
importlib.reload(shared_ui)
|
||||||
|
|
||||||
importlib.reload(psk_data)
|
importlib.reload(psk_data)
|
||||||
importlib.reload(psk_reader)
|
importlib.reload(psk_reader)
|
||||||
@@ -33,6 +35,7 @@ if 'bpy' in locals():
|
|||||||
importlib.reload(psa_import_ui)
|
importlib.reload(psa_import_ui)
|
||||||
else:
|
else:
|
||||||
from .shared import data as shared_data, types as shared_types, helpers as shared_helpers
|
from .shared import data as shared_data, types as shared_types, helpers as shared_helpers
|
||||||
|
from .shared import dfs as shared_dfs, ui as shared_ui
|
||||||
from .psk import data as psk_data, builder as psk_builder, writer as psk_writer, \
|
from .psk import data as psk_data, builder as psk_builder, writer as psk_writer, \
|
||||||
importer as psk_importer, properties as psk_properties
|
importer as psk_importer, properties as psk_properties
|
||||||
from .psk import reader as psk_reader, ui as psk_ui
|
from .psk import reader as psk_reader, ui as psk_ui
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from .properties import PSA_PG_export, PSA_PG_export_action_list_item, filter_se
|
|||||||
from ..builder import build_psa, PsaBuildSequence, PsaBuildOptions
|
from ..builder import build_psa, PsaBuildSequence, PsaBuildOptions
|
||||||
from ..writer import write_psa
|
from ..writer import write_psa
|
||||||
from ...shared.helpers import populate_bone_collection_list, get_nla_strips_in_frame_range
|
from ...shared.helpers import populate_bone_collection_list, get_nla_strips_in_frame_range
|
||||||
|
from ...shared.ui import draw_bone_filter_mode
|
||||||
|
|
||||||
|
|
||||||
def is_action_for_armature(armature: Armature, action: Action):
|
def is_action_for_armature(armature: Armature, action: Action):
|
||||||
@@ -120,14 +121,6 @@ def get_animation_data_object(context: Context) -> Object:
|
|||||||
return animation_data_object
|
return animation_data_object
|
||||||
|
|
||||||
|
|
||||||
def is_bone_filter_mode_item_available(context, identifier):
|
|
||||||
if identifier == 'BONE_COLLECTIONS':
|
|
||||||
armature = context.active_object.data
|
|
||||||
if len(armature.collections) == 0:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_timeline_marker_sequence_frame_ranges(animation_data: AnimData, context: Context, marker_names: List[str]) -> Dict:
|
def get_timeline_marker_sequence_frame_ranges(animation_data: AnimData, context: Context, marker_names: List[str]) -> Dict:
|
||||||
# Timeline markers need to be sorted so that we can determine the sequence start and end positions.
|
# Timeline markers need to be sorted so that we can determine the sequence start and end positions.
|
||||||
sequence_frame_ranges = dict()
|
sequence_frame_ranges = dict()
|
||||||
@@ -268,7 +261,7 @@ class PSA_OT_export(Operator, ExportHelper):
|
|||||||
|
|
||||||
propname, active_propname = get_sequences_propnames_from_source(pg.sequence_source)
|
propname, active_propname = get_sequences_propnames_from_source(pg.sequence_source)
|
||||||
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)))
|
||||||
|
|
||||||
flow = sequences_panel.grid_flow()
|
flow = sequences_panel.grid_flow()
|
||||||
flow.use_property_split = True
|
flow.use_property_split = True
|
||||||
@@ -295,7 +288,8 @@ class PSA_OT_export(Operator, ExportHelper):
|
|||||||
bones_header.label(text='Bones', icon='BONE_DATA')
|
bones_header.label(text='Bones', icon='BONE_DATA')
|
||||||
if bones_panel:
|
if bones_panel:
|
||||||
row = bones_panel.row(align=True)
|
row = bones_panel.row(align=True)
|
||||||
row.prop(pg, 'bone_filter_mode', text='Bones')
|
|
||||||
|
draw_bone_filter_mode(row, pg)
|
||||||
|
|
||||||
if pg.bone_filter_mode == 'BONE_COLLECTIONS':
|
if pg.bone_filter_mode == 'BONE_COLLECTIONS':
|
||||||
row = bones_panel.row(align=True)
|
row = bones_panel.row(align=True)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from bpy.props import BoolProperty, PointerProperty, EnumProperty, FloatProperty
|
|||||||
StringProperty
|
StringProperty
|
||||||
from bpy.types import PropertyGroup, Object, Action, AnimData, Context
|
from bpy.types import PropertyGroup, Object, Action, AnimData, Context
|
||||||
|
|
||||||
|
from ...shared.data import bone_filter_mode_items
|
||||||
from ...shared.types import PSX_PG_bone_collection_list_item
|
from ...shared.types import PSX_PG_bone_collection_list_item
|
||||||
|
|
||||||
|
|
||||||
@@ -155,11 +156,7 @@ class PSA_PG_export(PropertyGroup):
|
|||||||
name='Bone Filter',
|
name='Bone Filter',
|
||||||
options=empty_set,
|
options=empty_set,
|
||||||
description='',
|
description='',
|
||||||
items=(
|
items=bone_filter_mode_items,
|
||||||
('ALL', 'All', 'All bones will be exported.'),
|
|
||||||
('BONE_COLLECTIONS', 'Bone Collections', 'Only bones belonging to the selected bone collections and their '
|
|
||||||
'ancestors will be exported.'),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
||||||
bone_collection_list_index: IntProperty(default=0, name='', description='')
|
bone_collection_list_index: IntProperty(default=0, name='', description='')
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
from typing import List
|
from typing import List, Optional, cast
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty
|
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, CollectionProperty, IntProperty
|
||||||
from bpy.types import Operator, Context, Object
|
from bpy.types import Operator, Context, Object, Collection, SpaceProperties
|
||||||
from bpy_extras.io_utils import ExportHelper
|
from bpy_extras.io_utils import ExportHelper
|
||||||
|
|
||||||
from .properties import object_eval_state_items, export_space_items
|
from .properties import object_eval_state_items, export_space_items
|
||||||
from ..builder import build_psk, PskBuildOptions, get_psk_input_objects_for_context, \
|
from ..builder import build_psk, PskBuildOptions, get_psk_input_objects_for_context, \
|
||||||
get_psk_input_objects_for_collection
|
get_psk_input_objects_for_collection
|
||||||
from ..writer import write_psk
|
from ..writer import write_psk
|
||||||
|
from ...shared.data import bone_filter_mode_items
|
||||||
from ...shared.helpers import populate_bone_collection_list
|
from ...shared.helpers import populate_bone_collection_list
|
||||||
|
from ...shared.types import PSX_PG_bone_collection_list_item
|
||||||
|
from ...shared.ui import draw_bone_filter_mode
|
||||||
def is_bone_filter_mode_item_available(context, identifier):
|
|
||||||
input_objects = get_psk_input_objects_for_context(context)
|
|
||||||
armature_object = input_objects.armature_object
|
|
||||||
if identifier == 'BONE_COLLECTIONS':
|
|
||||||
if armature_object is None or armature_object.data is None or len(armature_object.data.collections) == 0:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_materials_for_mesh_objects(mesh_objects: List[Object]):
|
def get_materials_for_mesh_objects(mesh_objects: List[Object]):
|
||||||
@@ -42,6 +36,49 @@ def populate_material_list(mesh_objects, material_list):
|
|||||||
m.index = index
|
m.index = index
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_collection_from_context(context: Context) -> Optional[Collection]:
|
||||||
|
if context.space_data.type != 'PROPERTIES':
|
||||||
|
return None
|
||||||
|
|
||||||
|
space_data = cast(SpaceProperties, context.space_data)
|
||||||
|
|
||||||
|
if space_data.use_pin_id:
|
||||||
|
return cast(Collection, space_data.pin_id)
|
||||||
|
else:
|
||||||
|
return context.collection
|
||||||
|
|
||||||
|
|
||||||
|
def get_collection_export_operator_from_context(context: Context) -> Optional[object]:
|
||||||
|
collection = get_collection_from_context(context)
|
||||||
|
if collection is None:
|
||||||
|
return None
|
||||||
|
if 0 > collection.active_exporter_index >= len(collection.exporters):
|
||||||
|
return None
|
||||||
|
exporter = collection.exporters[collection.active_exporter_index]
|
||||||
|
# TODO: make sure this is actually an ASE exporter.
|
||||||
|
return exporter.export_properties
|
||||||
|
|
||||||
|
|
||||||
|
class PSK_OT_populate_bone_collection_list(Operator):
|
||||||
|
bl_idname = 'psk_export.populate_bone_collection_list'
|
||||||
|
bl_label = 'Populate Bone Collection List'
|
||||||
|
bl_description = 'Populate the bone collection list from the armature that will be used in this collection export'
|
||||||
|
bl_options = {'INTERNAL'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
export_operator = get_collection_export_operator_from_context(context)
|
||||||
|
if export_operator is None:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, 'No valid export operator found in context')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
input_objects = get_psk_input_objects_for_collection(context.collection)
|
||||||
|
if input_objects.armature_object is None:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, 'No armature found in collection')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
populate_bone_collection_list(input_objects.armature_object, export_operator.bone_collection_list)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class PSK_OT_material_list_move_up(Operator):
|
class PSK_OT_material_list_move_up(Operator):
|
||||||
bl_idname = 'psk_export.material_list_item_move_up'
|
bl_idname = 'psk_export.material_list_item_move_up'
|
||||||
bl_label = 'Move Up'
|
bl_label = 'Move Up'
|
||||||
@@ -78,6 +115,9 @@ class PSK_OT_material_list_move_down(Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
empty_set = set()
|
||||||
|
|
||||||
|
|
||||||
class PSK_OT_export_collection(Operator, ExportHelper):
|
class PSK_OT_export_collection(Operator, ExportHelper):
|
||||||
bl_idname = 'export.psk_collection'
|
bl_idname = 'export.psk_collection'
|
||||||
bl_label = 'Export'
|
bl_label = 'Export'
|
||||||
@@ -121,6 +161,14 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
|||||||
items=export_space_items,
|
items=export_space_items,
|
||||||
default='WORLD'
|
default='WORLD'
|
||||||
)
|
)
|
||||||
|
bone_filter_mode: EnumProperty(
|
||||||
|
name='Bone Filter',
|
||||||
|
options=empty_set,
|
||||||
|
description='',
|
||||||
|
items=bone_filter_mode_items,
|
||||||
|
)
|
||||||
|
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
||||||
|
bone_collection_list_index: IntProperty(default=0)
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -133,12 +181,13 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
|||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
options = PskBuildOptions()
|
options = PskBuildOptions()
|
||||||
options.bone_filter_mode = 'ALL'
|
|
||||||
options.object_eval_state = self.object_eval_state
|
options.object_eval_state = self.object_eval_state
|
||||||
options.materials = get_materials_for_mesh_objects([x.obj for x in input_objects.mesh_objects])
|
options.materials = get_materials_for_mesh_objects([x.obj for x in input_objects.mesh_objects])
|
||||||
options.should_enforce_bone_name_restrictions = self.should_enforce_bone_name_restrictions
|
options.should_enforce_bone_name_restrictions = self.should_enforce_bone_name_restrictions
|
||||||
options.scale = self.scale
|
options.scale = self.scale
|
||||||
options.export_space = self.export_space
|
options.export_space = self.export_space
|
||||||
|
options.bone_filter_mode = self.bone_filter_mode
|
||||||
|
options.bone_collection_indices = [x.index for x in self.bone_collection_list if x.is_selected]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = build_psk(context, input_objects, options)
|
result = build_psk(context, input_objects, options)
|
||||||
@@ -179,10 +228,17 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
|||||||
bones_header, bones_panel = layout.panel('Bones', default_closed=False)
|
bones_header, bones_panel = layout.panel('Bones', default_closed=False)
|
||||||
bones_header.label(text='Bones', icon='BONE_DATA')
|
bones_header.label(text='Bones', icon='BONE_DATA')
|
||||||
if bones_panel:
|
if bones_panel:
|
||||||
flow = bones_panel.grid_flow(row_major=True)
|
draw_bone_filter_mode(bones_panel, self)
|
||||||
flow.use_property_split = True
|
|
||||||
flow.use_property_decorate = False
|
row = bones_panel.row(align=True)
|
||||||
flow.prop(self, 'should_enforce_bone_name_restrictions')
|
|
||||||
|
if self.bone_filter_mode == 'BONE_COLLECTIONS':
|
||||||
|
rows = max(3, min(len(self.bone_collection_list), 10))
|
||||||
|
row.template_list('PSX_UL_bone_collection_list', '', self, 'bone_collection_list', self, 'bone_collection_list_index', rows=rows)
|
||||||
|
col = row.column()
|
||||||
|
col.operator(PSK_OT_populate_bone_collection_list.bl_idname, text='', icon='FILE_REFRESH')
|
||||||
|
|
||||||
|
bones_panel.prop(self, 'should_enforce_bone_name_restrictions')
|
||||||
|
|
||||||
|
|
||||||
class PSK_OT_export(Operator, ExportHelper):
|
class PSK_OT_export(Operator, ExportHelper):
|
||||||
@@ -215,7 +271,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
|||||||
populate_bone_collection_list(input_objects.armature_object, pg.bone_collection_list)
|
populate_bone_collection_list(input_objects.armature_object, pg.bone_collection_list)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
populate_material_list([x[0] for x in input_objects.mesh_objects], pg.material_list)
|
populate_material_list([x.obj for x in input_objects.mesh_objects], pg.material_list)
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
@@ -242,14 +298,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
|||||||
bones_header, bones_panel = layout.panel('Bones', default_closed=False)
|
bones_header, bones_panel = layout.panel('Bones', default_closed=False)
|
||||||
bones_header.label(text='Bones', icon='BONE_DATA')
|
bones_header.label(text='Bones', icon='BONE_DATA')
|
||||||
if bones_panel:
|
if bones_panel:
|
||||||
bone_filter_mode_items = pg.bl_rna.properties['bone_filter_mode'].enum_items_static
|
draw_bone_filter_mode(bones_panel, pg)
|
||||||
row = bones_panel.row(align=True)
|
|
||||||
for item in bone_filter_mode_items:
|
|
||||||
identifier = item.identifier
|
|
||||||
item_layout = row.row(align=True)
|
|
||||||
item_layout.prop_enum(pg, 'bone_filter_mode', item.identifier)
|
|
||||||
item_layout.enabled = is_bone_filter_mode_item_available(context, identifier)
|
|
||||||
|
|
||||||
if pg.bone_filter_mode == 'BONE_COLLECTIONS':
|
if pg.bone_filter_mode == 'BONE_COLLECTIONS':
|
||||||
row = bones_panel.row()
|
row = bones_panel.row()
|
||||||
rows = max(3, min(len(pg.bone_collection_list), 10))
|
rows = max(3, min(len(pg.bone_collection_list), 10))
|
||||||
@@ -303,4 +352,5 @@ classes = (
|
|||||||
PSK_OT_material_list_move_down,
|
PSK_OT_material_list_move_down,
|
||||||
PSK_OT_export,
|
PSK_OT_export,
|
||||||
PSK_OT_export_collection,
|
PSK_OT_export_collection,
|
||||||
|
PSK_OT_populate_bone_collection_list,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,11 +26,7 @@ class PSK_PG_export(PropertyGroup):
|
|||||||
name='Bone Filter',
|
name='Bone Filter',
|
||||||
options=empty_set,
|
options=empty_set,
|
||||||
description='',
|
description='',
|
||||||
items=(
|
items=bone_filter_mode_items
|
||||||
('ALL', 'All', 'All bones will be exported'),
|
|
||||||
('BONE_COLLECTIONS', 'Bone Collections',
|
|
||||||
'Only bones belonging to the selected bone collections and their ancestors will be exported')
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
||||||
bone_collection_list_index: IntProperty(default=0)
|
bone_collection_list_index: IntProperty(default=0)
|
||||||
|
|||||||
@@ -93,3 +93,9 @@ class Section(Structure):
|
|||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
super().__init__(*args, **kw)
|
super().__init__(*args, **kw)
|
||||||
self.type_flags = 1999801
|
self.type_flags = 1999801
|
||||||
|
|
||||||
|
|
||||||
|
bone_filter_mode_items = (
|
||||||
|
('ALL', 'All', 'All bones will be exported'),
|
||||||
|
('BONE_COLLECTIONS', 'Bone Collections', 'Only bones belonging to the selected bone collections and their ancestors will be exported')
|
||||||
|
)
|
||||||
|
|||||||
22
io_scene_psk_psa/shared/ui.py
Normal file
22
io_scene_psk_psa/shared/ui.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from bpy.types import UILayout
|
||||||
|
|
||||||
|
from .data import bone_filter_mode_items
|
||||||
|
|
||||||
|
|
||||||
|
def is_bone_filter_mode_item_available(pg, identifier):
|
||||||
|
match identifier:
|
||||||
|
case 'BONE_COLLECTIONS':
|
||||||
|
if len(pg.bone_collection_list) == 0:
|
||||||
|
return False
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def draw_bone_filter_mode(layout: UILayout, pg):
|
||||||
|
row = layout.row(align=True)
|
||||||
|
for item_identifier, _, _ in bone_filter_mode_items:
|
||||||
|
identifier = item_identifier
|
||||||
|
item_layout = row.row(align=True)
|
||||||
|
item_layout.prop_enum(pg, 'bone_filter_mode', item_identifier)
|
||||||
|
item_layout.enabled = is_bone_filter_mode_item_available(pg, identifier)
|
||||||
Reference in New Issue
Block a user