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_helpers)
|
||||
importlib.reload(shared_types)
|
||||
importlib.reload(shared_dfs)
|
||||
importlib.reload(shared_ui)
|
||||
|
||||
importlib.reload(psk_data)
|
||||
importlib.reload(psk_reader)
|
||||
@@ -33,6 +35,7 @@ if 'bpy' in locals():
|
||||
importlib.reload(psa_import_ui)
|
||||
else:
|
||||
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, \
|
||||
importer as psk_importer, properties as psk_properties
|
||||
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 ..writer import write_psa
|
||||
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):
|
||||
@@ -120,14 +121,6 @@ def get_animation_data_object(context: Context) -> 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:
|
||||
# Timeline markers need to be sorted so that we can determine the sequence start and end positions.
|
||||
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)
|
||||
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.use_property_split = True
|
||||
@@ -295,7 +288,8 @@ class PSA_OT_export(Operator, ExportHelper):
|
||||
bones_header.label(text='Bones', icon='BONE_DATA')
|
||||
if bones_panel:
|
||||
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':
|
||||
row = bones_panel.row(align=True)
|
||||
|
||||
@@ -7,6 +7,7 @@ from bpy.props import BoolProperty, PointerProperty, EnumProperty, FloatProperty
|
||||
StringProperty
|
||||
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
|
||||
|
||||
|
||||
@@ -155,11 +156,7 @@ class PSA_PG_export(PropertyGroup):
|
||||
name='Bone Filter',
|
||||
options=empty_set,
|
||||
description='',
|
||||
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.'),
|
||||
)
|
||||
items=bone_filter_mode_items,
|
||||
)
|
||||
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
||||
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
|
||||
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty
|
||||
from bpy.types import Operator, Context, Object
|
||||
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, CollectionProperty, IntProperty
|
||||
from bpy.types import Operator, Context, Object, Collection, SpaceProperties
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
|
||||
from .properties import object_eval_state_items, export_space_items
|
||||
from ..builder import build_psk, PskBuildOptions, get_psk_input_objects_for_context, \
|
||||
get_psk_input_objects_for_collection
|
||||
from ..writer import write_psk
|
||||
from ...shared.data import bone_filter_mode_items
|
||||
from ...shared.helpers import populate_bone_collection_list
|
||||
|
||||
|
||||
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
|
||||
from ...shared.types import PSX_PG_bone_collection_list_item
|
||||
from ...shared.ui import draw_bone_filter_mode
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
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):
|
||||
bl_idname = 'psk_export.material_list_item_move_up'
|
||||
bl_label = 'Move Up'
|
||||
@@ -78,6 +115,9 @@ class PSK_OT_material_list_move_down(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
empty_set = set()
|
||||
|
||||
|
||||
class PSK_OT_export_collection(Operator, ExportHelper):
|
||||
bl_idname = 'export.psk_collection'
|
||||
bl_label = 'Export'
|
||||
@@ -121,6 +161,14 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
||||
items=export_space_items,
|
||||
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):
|
||||
@@ -133,12 +181,13 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
||||
return {'CANCELLED'}
|
||||
|
||||
options = PskBuildOptions()
|
||||
options.bone_filter_mode = 'ALL'
|
||||
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.should_enforce_bone_name_restrictions = self.should_enforce_bone_name_restrictions
|
||||
options.scale = self.scale
|
||||
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:
|
||||
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.label(text='Bones', icon='BONE_DATA')
|
||||
if bones_panel:
|
||||
flow = bones_panel.grid_flow(row_major=True)
|
||||
flow.use_property_split = True
|
||||
flow.use_property_decorate = False
|
||||
flow.prop(self, 'should_enforce_bone_name_restrictions')
|
||||
draw_bone_filter_mode(bones_panel, self)
|
||||
|
||||
row = bones_panel.row(align=True)
|
||||
|
||||
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):
|
||||
@@ -215,7 +271,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
||||
populate_bone_collection_list(input_objects.armature_object, pg.bone_collection_list)
|
||||
|
||||
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:
|
||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||
return {'CANCELLED'}
|
||||
@@ -242,14 +298,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
||||
bones_header, bones_panel = layout.panel('Bones', default_closed=False)
|
||||
bones_header.label(text='Bones', icon='BONE_DATA')
|
||||
if bones_panel:
|
||||
bone_filter_mode_items = pg.bl_rna.properties['bone_filter_mode'].enum_items_static
|
||||
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)
|
||||
|
||||
draw_bone_filter_mode(bones_panel, pg)
|
||||
if pg.bone_filter_mode == 'BONE_COLLECTIONS':
|
||||
row = bones_panel.row()
|
||||
rows = max(3, min(len(pg.bone_collection_list), 10))
|
||||
@@ -303,4 +352,5 @@ classes = (
|
||||
PSK_OT_material_list_move_down,
|
||||
PSK_OT_export,
|
||||
PSK_OT_export_collection,
|
||||
PSK_OT_populate_bone_collection_list,
|
||||
)
|
||||
|
||||
@@ -26,11 +26,7 @@ class PSK_PG_export(PropertyGroup):
|
||||
name='Bone Filter',
|
||||
options=empty_set,
|
||||
description='',
|
||||
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')
|
||||
)
|
||||
items=bone_filter_mode_items
|
||||
)
|
||||
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
||||
bone_collection_list_index: IntProperty(default=0)
|
||||
|
||||
@@ -93,3 +93,9 @@ class Section(Structure):
|
||||
def __init__(self, *args, **kw):
|
||||
super().__init__(*args, **kw)
|
||||
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