mirror of
https://github.com/DarklightGames/io_scene_psk_psa.git
synced 2025-02-22 20:09:56 +01:00
Unified handling of translating bpy PSK options to the options passed into the build function
Also fixed a bug where the normal export operator was exporting duplicate objects
This commit is contained in:
parent
ff5ded004a
commit
c1d5a2229d
@ -1,24 +1,23 @@
|
|||||||
from typing import List, Optional, cast
|
from typing import List, Optional, cast, Iterable
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, CollectionProperty, IntProperty
|
from bpy.props import StringProperty
|
||||||
from bpy.types import Operator, Context, Object, Collection, SpaceProperties
|
from bpy.types import Operator, Context, Object, Collection, SpaceProperties, Depsgraph, Material
|
||||||
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 add_psk_export_properties
|
||||||
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
|
from ...shared.ui import draw_bone_filter_mode
|
||||||
|
|
||||||
|
|
||||||
def get_materials_for_mesh_objects(mesh_objects: List[Object]):
|
def get_materials_for_mesh_objects(depsgraph: Depsgraph, mesh_objects: Iterable[Object]):
|
||||||
materials = []
|
materials = []
|
||||||
for mesh_object in mesh_objects:
|
for mesh_object in mesh_objects:
|
||||||
for i, material_slot in enumerate(mesh_object.material_slots):
|
evaluated_mesh_object = mesh_object.evaluated_get(depsgraph)
|
||||||
|
for i, material_slot in enumerate(evaluated_mesh_object.material_slots):
|
||||||
material = material_slot.material
|
material = material_slot.material
|
||||||
if material is None:
|
if material is None:
|
||||||
raise RuntimeError('Material slot cannot be empty (index ' + str(i) + ')')
|
raise RuntimeError('Material slot cannot be empty (index ' + str(i) + ')')
|
||||||
@ -27,12 +26,12 @@ def get_materials_for_mesh_objects(mesh_objects: List[Object]):
|
|||||||
return materials
|
return materials
|
||||||
|
|
||||||
|
|
||||||
def populate_material_list(mesh_objects, material_list):
|
def populate_material_name_list(depsgraph: Depsgraph, mesh_objects, material_list):
|
||||||
materials = get_materials_for_mesh_objects(mesh_objects)
|
materials = get_materials_for_mesh_objects(depsgraph, mesh_objects)
|
||||||
material_list.clear()
|
material_list.clear()
|
||||||
for index, material in enumerate(materials):
|
for index, material in enumerate(materials):
|
||||||
m = material_list.add()
|
m = material_list.add()
|
||||||
m.material = material
|
m.material_name = material.name
|
||||||
m.index = index
|
m.index = index
|
||||||
|
|
||||||
|
|
||||||
@ -79,6 +78,27 @@ class PSK_OT_populate_bone_collection_list(Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class PSK_OT_populate_material_name_list(Operator):
|
||||||
|
bl_idname = 'psk_export.populate_material_name_list'
|
||||||
|
bl_label = 'Populate Material Name List'
|
||||||
|
bl_description = 'Populate the material name list from the objects that will be used in this 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'}
|
||||||
|
depsgraph = context.evaluated_depsgraph_get()
|
||||||
|
input_objects = get_psk_input_objects_for_collection(context.collection)
|
||||||
|
try:
|
||||||
|
populate_material_name_list(depsgraph, [x.obj for x in input_objects.mesh_objects], export_operator.material_name_list)
|
||||||
|
except RuntimeError as e:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
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'
|
||||||
@ -88,12 +108,12 @@ class PSK_OT_material_list_move_up(Operator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
pg = getattr(context.scene, 'psk_export')
|
pg = getattr(context.scene, 'psk_export')
|
||||||
return pg.material_list_index > 0
|
return pg.material_name_list_index > 0
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
pg = getattr(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_name_list.move(pg.material_name_list_index, pg.material_name_list_index - 1)
|
||||||
pg.material_list_index -= 1
|
pg.material_name_list_index -= 1
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
@ -106,47 +126,97 @@ class PSK_OT_material_list_move_down(Operator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
pg = getattr(context.scene, 'psk_export')
|
pg = getattr(context.scene, 'psk_export')
|
||||||
return pg.material_list_index < len(pg.material_list) - 1
|
return pg.material_name_list_index < len(pg.material_name_list) - 1
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
pg = getattr(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_name_list.move(pg.material_name_list_index, pg.material_name_list_index + 1)
|
||||||
pg.material_list_index += 1
|
pg.material_name_list_index += 1
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class PSK_OT_material_list_name_move_up(Operator):
|
||||||
|
bl_idname = 'psk_export.material_name_list_item_move_up'
|
||||||
|
bl_label = 'Move Up'
|
||||||
|
bl_options = {'INTERNAL'}
|
||||||
|
bl_description = 'Move the selected material name up one slot'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
export_operator = get_collection_export_operator_from_context(context)
|
||||||
|
if export_operator is None:
|
||||||
|
return False
|
||||||
|
return export_operator.material_name_list_index > 0
|
||||||
|
|
||||||
|
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'}
|
||||||
|
export_operator.material_name_list.move(export_operator.material_name_list_index, export_operator.material_name_list_index - 1)
|
||||||
|
export_operator.material_name_list_index -= 1
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class PSK_OT_material_list_name_move_down(Operator):
|
||||||
|
bl_idname = 'psk_export.material_name_list_item_move_down'
|
||||||
|
bl_label = 'Move Down'
|
||||||
|
bl_options = {'INTERNAL'}
|
||||||
|
bl_description = 'Move the selected material name down one slot'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
export_operator = get_collection_export_operator_from_context(context)
|
||||||
|
if export_operator is None:
|
||||||
|
return False
|
||||||
|
return export_operator.material_name_list_index < len(export_operator.material_name_list) - 1
|
||||||
|
|
||||||
|
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'}
|
||||||
|
export_operator.material_name_list.move(export_operator.material_name_list_index, export_operator.material_name_list_index + 1)
|
||||||
|
export_operator.material_name_list_index += 1
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
empty_set = set()
|
empty_set = set()
|
||||||
|
|
||||||
axis_identifiers = ('X', 'Y', 'Z', '-X', '-Y', '-Z')
|
|
||||||
|
|
||||||
forward_items = (
|
def get_sorted_materials_by_names(materials: Iterable[Material], material_names: List[str]) -> List[Material]:
|
||||||
('X', 'X Forward', ''),
|
"""
|
||||||
('Y', 'Y Forward', ''),
|
Sorts the materials by the order of the material names list. Any materials not in the list will be appended to the
|
||||||
('Z', 'Z Forward', ''),
|
end of the list in the order they are found.
|
||||||
('-X', '-X Forward', ''),
|
@param materials: A list of materials to sort
|
||||||
('-Y', '-Y Forward', ''),
|
@param material_names: A list of material names to sort by
|
||||||
('-Z', '-Z Forward', ''),
|
@return: A sorted list of materials
|
||||||
)
|
"""
|
||||||
|
materials_in_collection = [m for m in materials if m.name in material_names]
|
||||||
up_items = (
|
materials_not_in_collection = [m for m in materials if m.name not in material_names]
|
||||||
('X', 'X Up', ''),
|
materials_in_collection = sorted(materials_in_collection, key=lambda x: material_names.index(x.name))
|
||||||
('Y', 'Y Up', ''),
|
return materials_in_collection + materials_not_in_collection
|
||||||
('Z', 'Z Up', ''),
|
|
||||||
('-X', '-X Up', ''),
|
|
||||||
('-Y', '-Y Up', ''),
|
|
||||||
('-Z', '-Z Up', ''),
|
|
||||||
)
|
|
||||||
|
|
||||||
def forward_axis_update(self, context):
|
|
||||||
if self.forward_axis == self.up_axis:
|
|
||||||
# Automatically set the up axis to the next available axis
|
|
||||||
self.up_axis = next((axis for axis in axis_identifiers if axis != self.forward_axis), 'Z')
|
|
||||||
|
|
||||||
|
|
||||||
def up_axis_update(self, context):
|
def get_psk_build_options_from_property_group(mesh_objects: Iterable[Object], pg: 'PSK_PG_export', depsgraph: Optional[Depsgraph] = None) -> PskBuildOptions:
|
||||||
if self.up_axis == self.forward_axis:
|
if depsgraph is None:
|
||||||
# Automatically set the forward axis to the next available axis
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
self.forward_axis = next((axis for axis in axis_identifiers if axis != self.up_axis), 'X')
|
|
||||||
|
options = PskBuildOptions()
|
||||||
|
options.object_eval_state = pg.object_eval_state
|
||||||
|
options.export_space = pg.export_space
|
||||||
|
options.bone_filter_mode = pg.bone_filter_mode
|
||||||
|
options.bone_collection_indices = [x.index for x in pg.bone_collection_list if x.is_selected]
|
||||||
|
options.scale = pg.scale
|
||||||
|
options.forward_axis = pg.forward_axis
|
||||||
|
options.up_axis = pg.up_axis
|
||||||
|
|
||||||
|
# TODO: perhaps move this into the build function and replace the materials list with a material names list.
|
||||||
|
materials = get_materials_for_mesh_objects(depsgraph, mesh_objects)
|
||||||
|
options.materials = get_sorted_materials_by_names(materials, [m.material_name for m in pg.material_name_list])
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
class PSK_OT_export_collection(Operator, ExportHelper):
|
class PSK_OT_export_collection(Operator, ExportHelper):
|
||||||
bl_idname = 'export.psk_collection'
|
bl_idname = 'export.psk_collection'
|
||||||
@ -162,50 +232,6 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
|||||||
subtype='FILE_PATH')
|
subtype='FILE_PATH')
|
||||||
collection: StringProperty(options={'HIDDEN'})
|
collection: StringProperty(options={'HIDDEN'})
|
||||||
|
|
||||||
object_eval_state: EnumProperty(
|
|
||||||
items=object_eval_state_items,
|
|
||||||
name='Object Evaluation State',
|
|
||||||
default='EVALUATED'
|
|
||||||
)
|
|
||||||
should_exclude_hidden_meshes: BoolProperty(
|
|
||||||
default=False,
|
|
||||||
name='Visible Only',
|
|
||||||
description='Export only visible meshes'
|
|
||||||
)
|
|
||||||
scale: FloatProperty(
|
|
||||||
name='Scale',
|
|
||||||
default=1.0,
|
|
||||||
description='Scale factor to apply to the exported mesh and armature',
|
|
||||||
min=0.0001,
|
|
||||||
soft_max=100.0
|
|
||||||
)
|
|
||||||
export_space: EnumProperty(
|
|
||||||
name='Export Space',
|
|
||||||
description='Space to export the mesh in',
|
|
||||||
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)
|
|
||||||
forward_axis: EnumProperty(
|
|
||||||
name='Forward',
|
|
||||||
items=forward_items,
|
|
||||||
default='X',
|
|
||||||
update=forward_axis_update
|
|
||||||
)
|
|
||||||
up_axis: EnumProperty(
|
|
||||||
name='Up',
|
|
||||||
items=up_items,
|
|
||||||
default='Z',
|
|
||||||
update=up_axis_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
collection = bpy.data.collections.get(self.collection)
|
collection = bpy.data.collections.get(self.collection)
|
||||||
|
|
||||||
@ -215,15 +241,7 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
|||||||
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
self.report({'ERROR_INVALID_CONTEXT'}, str(e))
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
options = PskBuildOptions()
|
options = get_psk_build_options_from_property_group([x.obj for x in input_objects.mesh_objects], self)
|
||||||
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.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]
|
|
||||||
options.forward_axis = self.forward_axis
|
|
||||||
options.up_axis = self.up_axis
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = build_psk(context, input_objects, options)
|
result = build_psk(context, input_objects, options)
|
||||||
@ -261,12 +279,25 @@ 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:
|
||||||
bones_panel.operator(PSK_OT_populate_bone_collection_list.bl_idname, icon='FILE_REFRESH')
|
|
||||||
draw_bone_filter_mode(bones_panel, self)
|
draw_bone_filter_mode(bones_panel, self)
|
||||||
if self.bone_filter_mode == 'BONE_COLLECTIONS':
|
if self.bone_filter_mode == 'BONE_COLLECTIONS':
|
||||||
|
bones_panel.operator(PSK_OT_populate_bone_collection_list.bl_idname, icon='FILE_REFRESH')
|
||||||
rows = max(3, min(len(self.bone_collection_list), 10))
|
rows = max(3, min(len(self.bone_collection_list), 10))
|
||||||
bones_panel.template_list('PSX_UL_bone_collection_list', '', self, 'bone_collection_list', self, 'bone_collection_list_index', rows=rows)
|
bones_panel.template_list('PSX_UL_bone_collection_list', '', self, 'bone_collection_list', self, 'bone_collection_list_index', rows=rows)
|
||||||
|
|
||||||
|
# MATERIALS
|
||||||
|
materials_header, materials_panel = layout.panel('Materials', default_closed=False)
|
||||||
|
materials_header.label(text='Materials', icon='MATERIAL')
|
||||||
|
|
||||||
|
if materials_panel:
|
||||||
|
materials_panel.operator(PSK_OT_populate_material_name_list.bl_idname, icon='FILE_REFRESH')
|
||||||
|
rows = max(3, min(len(self.material_name_list), 10))
|
||||||
|
row = materials_panel.row()
|
||||||
|
row.template_list('PSK_UL_material_names', '', self, 'material_name_list', self, 'material_name_list_index', rows=rows)
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator(PSK_OT_material_list_name_move_up.bl_idname, text='', icon='TRIA_UP')
|
||||||
|
col.operator(PSK_OT_material_list_name_move_down.bl_idname, text='', icon='TRIA_DOWN')
|
||||||
|
|
||||||
# TRANSFORM
|
# TRANSFORM
|
||||||
transform_header, transform_panel = layout.panel('Transform', default_closed=False)
|
transform_header, transform_panel = layout.panel('Transform', default_closed=False)
|
||||||
transform_header.label(text='Transform')
|
transform_header.label(text='Transform')
|
||||||
@ -280,6 +311,11 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
|||||||
flow.prop(self, 'up_axis')
|
flow.prop(self, 'up_axis')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add_psk_export_properties(PSK_OT_export_collection)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PSK_OT_export(Operator, ExportHelper):
|
class PSK_OT_export(Operator, ExportHelper):
|
||||||
bl_idname = 'export.psk'
|
bl_idname = 'export.psk'
|
||||||
bl_label = 'Export'
|
bl_label = 'Export'
|
||||||
@ -287,7 +323,6 @@ class PSK_OT_export(Operator, ExportHelper):
|
|||||||
bl_description = 'Export mesh and armature to PSK'
|
bl_description = 'Export mesh and armature to PSK'
|
||||||
filename_ext = '.psk'
|
filename_ext = '.psk'
|
||||||
filter_glob: StringProperty(default='*.psk', options={'HIDDEN'})
|
filter_glob: StringProperty(default='*.psk', options={'HIDDEN'})
|
||||||
|
|
||||||
filepath: StringProperty(
|
filepath: StringProperty(
|
||||||
name='File Path',
|
name='File Path',
|
||||||
description='File path used for exporting the PSK file',
|
description='File path used for exporting the PSK file',
|
||||||
@ -309,8 +344,10 @@ 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)
|
||||||
|
|
||||||
|
depsgraph = context.evaluated_depsgraph_get()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
populate_material_list([x.obj for x in input_objects.mesh_objects], pg.material_list)
|
populate_material_name_list(depsgraph, [x.obj for x in input_objects.mesh_objects], pg.material_name_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'}
|
||||||
@ -349,7 +386,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
|||||||
if materials_panel:
|
if materials_panel:
|
||||||
row = materials_panel.row()
|
row = materials_panel.row()
|
||||||
rows = max(3, min(len(pg.bone_collection_list), 10))
|
rows = max(3, min(len(pg.bone_collection_list), 10))
|
||||||
row.template_list('PSK_UL_materials', '', pg, 'material_list', pg, 'material_list_index', rows=rows)
|
row.template_list('PSK_UL_material_names', '', pg, 'material_name_list', pg, 'material_name_list_index', rows=rows)
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator(PSK_OT_material_list_move_up.bl_idname, text='', icon='TRIA_UP')
|
col.operator(PSK_OT_material_list_move_up.bl_idname, text='', icon='TRIA_UP')
|
||||||
col.operator(PSK_OT_material_list_move_down.bl_idname, text='', icon='TRIA_DOWN')
|
col.operator(PSK_OT_material_list_move_down.bl_idname, text='', icon='TRIA_DOWN')
|
||||||
@ -358,15 +395,8 @@ class PSK_OT_export(Operator, ExportHelper):
|
|||||||
pg = getattr(context.scene, 'psk_export')
|
pg = getattr(context.scene, 'psk_export')
|
||||||
|
|
||||||
input_objects = get_psk_input_objects_for_context(context)
|
input_objects = get_psk_input_objects_for_context(context)
|
||||||
|
options = get_psk_build_options_from_property_group([x.obj for x in input_objects.mesh_objects], pg)
|
||||||
|
|
||||||
options = PskBuildOptions()
|
|
||||||
options.bone_filter_mode = pg.bone_filter_mode
|
|
||||||
options.bone_collection_indices = [x.index for x in pg.bone_collection_list if x.is_selected]
|
|
||||||
options.object_eval_state = pg.object_eval_state
|
|
||||||
options.materials = [m.material for m in pg.material_list]
|
|
||||||
options.scale = pg.scale
|
|
||||||
options.export_space = pg.export_space
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = build_psk(context, input_objects, options)
|
result = build_psk(context, input_objects, options)
|
||||||
for warning in result.warnings:
|
for warning in result.warnings:
|
||||||
@ -379,7 +409,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
|||||||
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'}
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
@ -389,4 +419,7 @@ classes = (
|
|||||||
PSK_OT_export,
|
PSK_OT_export,
|
||||||
PSK_OT_export_collection,
|
PSK_OT_export_collection,
|
||||||
PSK_OT_populate_bone_collection_list,
|
PSK_OT_populate_bone_collection_list,
|
||||||
|
PSK_OT_populate_material_name_list,
|
||||||
|
PSK_OT_material_list_name_move_up,
|
||||||
|
PSK_OT_material_list_name_move_down,
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from bpy.props import EnumProperty, CollectionProperty, IntProperty, PointerProperty, FloatProperty
|
from bpy.props import EnumProperty, CollectionProperty, IntProperty, PointerProperty, FloatProperty, StringProperty, \
|
||||||
|
BoolProperty
|
||||||
from bpy.types import PropertyGroup, Material
|
from bpy.types import PropertyGroup, Material
|
||||||
|
|
||||||
from ...shared.data import bone_filter_mode_items
|
from ...shared.data import bone_filter_mode_items
|
||||||
@ -6,7 +7,6 @@ from ...shared.types import PSX_PG_bone_collection_list_item
|
|||||||
|
|
||||||
empty_set = set()
|
empty_set = set()
|
||||||
|
|
||||||
|
|
||||||
object_eval_state_items = (
|
object_eval_state_items = (
|
||||||
('EVALUATED', 'Evaluated', 'Use data from fully evaluated object'),
|
('EVALUATED', 'Evaluated', 'Use data from fully evaluated object'),
|
||||||
('ORIGINAL', 'Original', 'Use data from original object with no modifiers applied'),
|
('ORIGINAL', 'Original', 'Use data from original object with no modifiers applied'),
|
||||||
@ -17,44 +17,110 @@ export_space_items = [
|
|||||||
('ARMATURE', 'Armature', 'Export in armature space'),
|
('ARMATURE', 'Armature', 'Export in armature space'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
axis_identifiers = ('X', 'Y', 'Z', '-X', '-Y', '-Z')
|
||||||
|
forward_items = (
|
||||||
|
('X', 'X Forward', ''),
|
||||||
|
('Y', 'Y Forward', ''),
|
||||||
|
('Z', 'Z Forward', ''),
|
||||||
|
('-X', '-X Forward', ''),
|
||||||
|
('-Y', '-Y Forward', ''),
|
||||||
|
('-Z', '-Z Forward', ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
up_items = (
|
||||||
|
('X', 'X Up', ''),
|
||||||
|
('Y', 'Y Up', ''),
|
||||||
|
('Z', 'Z Up', ''),
|
||||||
|
('-X', '-X Up', ''),
|
||||||
|
('-Y', '-Y Up', ''),
|
||||||
|
('-Z', '-Z Up', ''),
|
||||||
|
)
|
||||||
|
|
||||||
class PSK_PG_material_list_item(PropertyGroup):
|
class PSK_PG_material_list_item(PropertyGroup):
|
||||||
material: PointerProperty(type=Material)
|
material: PointerProperty(type=Material)
|
||||||
index: IntProperty()
|
index: IntProperty()
|
||||||
|
|
||||||
|
class PSK_PG_material_name_list_item(PropertyGroup):
|
||||||
|
material_name: StringProperty()
|
||||||
|
index: IntProperty()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def forward_axis_update(self, _context):
|
||||||
|
if self.forward_axis == self.up_axis:
|
||||||
|
# Automatically set the up axis to the next available axis
|
||||||
|
self.up_axis = next((axis for axis in axis_identifiers if axis != self.forward_axis), 'Z')
|
||||||
|
|
||||||
|
|
||||||
|
def up_axis_update(self, _context):
|
||||||
|
if self.up_axis == self.forward_axis:
|
||||||
|
# Automatically set the forward axis to the next available axis
|
||||||
|
self.forward_axis = next((axis for axis in axis_identifiers if axis != self.up_axis), 'X')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# In order to share the same properties between the PSA and PSK export properties, we need to define the properties in a
|
||||||
|
# separate function and then apply them to the classes. This is because the collection exporter cannot have
|
||||||
|
# PointerProperties, so we must effectively duplicate the storage of the properties.
|
||||||
|
def add_psk_export_properties(cls):
|
||||||
|
cls.__annotations__['object_eval_state'] = EnumProperty(
|
||||||
|
items=object_eval_state_items,
|
||||||
|
name='Object Evaluation State',
|
||||||
|
default='EVALUATED'
|
||||||
|
)
|
||||||
|
cls.__annotations__['should_exclude_hidden_meshes'] = BoolProperty(
|
||||||
|
default=False,
|
||||||
|
name='Visible Only',
|
||||||
|
description='Export only visible meshes'
|
||||||
|
)
|
||||||
|
cls.__annotations__['scale'] = FloatProperty(
|
||||||
|
name='Scale',
|
||||||
|
default=1.0,
|
||||||
|
description='Scale factor to apply to the exported mesh and armature',
|
||||||
|
min=0.0001,
|
||||||
|
soft_max=100.0
|
||||||
|
)
|
||||||
|
cls.__annotations__['export_space'] = EnumProperty(
|
||||||
|
name='Export Space',
|
||||||
|
description='Space to export the mesh in',
|
||||||
|
items=export_space_items,
|
||||||
|
default='WORLD'
|
||||||
|
)
|
||||||
|
cls.__annotations__['bone_filter_mode'] = EnumProperty(
|
||||||
|
name='Bone Filter',
|
||||||
|
options=empty_set,
|
||||||
|
description='',
|
||||||
|
items=bone_filter_mode_items,
|
||||||
|
)
|
||||||
|
cls.__annotations__['bone_collection_list'] = CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
||||||
|
cls.__annotations__['bone_collection_list_index'] = IntProperty(default=0)
|
||||||
|
cls.__annotations__['forward_axis'] = EnumProperty(
|
||||||
|
name='Forward',
|
||||||
|
items=forward_items,
|
||||||
|
default='X',
|
||||||
|
update=forward_axis_update
|
||||||
|
)
|
||||||
|
cls.__annotations__['up_axis'] = EnumProperty(
|
||||||
|
name='Up',
|
||||||
|
items=up_items,
|
||||||
|
default='Z',
|
||||||
|
update=up_axis_update
|
||||||
|
)
|
||||||
|
cls.__annotations__['material_name_list'] = CollectionProperty(type=PSK_PG_material_name_list_item)
|
||||||
|
cls.__annotations__['material_name_list_index'] = IntProperty(default=0)
|
||||||
|
|
||||||
|
|
||||||
class PSK_PG_export(PropertyGroup):
|
class PSK_PG_export(PropertyGroup):
|
||||||
bone_filter_mode: EnumProperty(
|
pass
|
||||||
name='Bone Filter',
|
|
||||||
options=empty_set,
|
|
||||||
description='',
|
add_psk_export_properties(PSK_PG_export)
|
||||||
items=bone_filter_mode_items
|
|
||||||
)
|
|
||||||
bone_collection_list: CollectionProperty(type=PSX_PG_bone_collection_list_item)
|
|
||||||
bone_collection_list_index: IntProperty(default=0)
|
|
||||||
object_eval_state: EnumProperty(
|
|
||||||
items=object_eval_state_items,
|
|
||||||
name='Object Evaluation State',
|
|
||||||
default='EVALUATED'
|
|
||||||
)
|
|
||||||
material_list: CollectionProperty(type=PSK_PG_material_list_item)
|
|
||||||
material_list_index: IntProperty(default=0)
|
|
||||||
scale: FloatProperty(
|
|
||||||
name='Scale',
|
|
||||||
default=1.0,
|
|
||||||
description='Scale factor to apply to the exported mesh',
|
|
||||||
min=0.0001,
|
|
||||||
soft_max=100.0
|
|
||||||
)
|
|
||||||
export_space: EnumProperty(
|
|
||||||
name='Export Space',
|
|
||||||
options=empty_set,
|
|
||||||
description='Space to export the mesh in',
|
|
||||||
items=export_space_items,
|
|
||||||
default='WORLD'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
PSK_PG_material_list_item,
|
PSK_PG_material_list_item,
|
||||||
|
PSK_PG_material_name_list_item,
|
||||||
PSK_PG_export,
|
PSK_PG_export,
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
import bpy
|
||||||
from bpy.types import UIList
|
from bpy.types import UIList
|
||||||
|
|
||||||
|
|
||||||
class PSK_UL_materials(UIList):
|
class PSK_UL_material_names(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_propname, index):
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(item.material, 'name', text='', emboss=False, icon_value=layout.icon(item.material))
|
material = bpy.data.materials.get(item.material_name, None)
|
||||||
|
row.prop(item, 'material_name', text='', emboss=False, icon_value=layout.icon(material) if material else 0)
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
PSK_UL_materials,
|
PSK_UL_material_names,
|
||||||
)
|
)
|
||||||
|
@ -133,11 +133,12 @@ def dfs_view_layer_objects(view_layer: ViewLayer) -> Iterable[DfsObject]:
|
|||||||
@param view_layer: The view layer to inspect.
|
@param view_layer: The view layer to inspect.
|
||||||
@return: An iterable of tuples containing the object, the instance objects, and the world matrix.
|
@return: An iterable of tuples containing the object, the instance objects, and the world matrix.
|
||||||
'''
|
'''
|
||||||
|
visited = set()
|
||||||
def layer_collection_objects_recursive(layer_collection: LayerCollection):
|
def layer_collection_objects_recursive(layer_collection: LayerCollection):
|
||||||
for child in layer_collection.children:
|
for child in layer_collection.children:
|
||||||
yield from layer_collection_objects_recursive(child)
|
yield from layer_collection_objects_recursive(child)
|
||||||
# Iterate only the top-level objects in this collection first.
|
# Iterate only the top-level objects in this collection first.
|
||||||
yield from _dfs_collection_objects_recursive(layer_collection.collection)
|
yield from _dfs_collection_objects_recursive(layer_collection.collection, visited=visited)
|
||||||
|
|
||||||
yield from layer_collection_objects_recursive(view_layer.layer_collection)
|
yield from layer_collection_objects_recursive(view_layer.layer_collection)
|
||||||
|
|
||||||
|
@ -4,12 +4,8 @@ from .data import bone_filter_mode_items
|
|||||||
|
|
||||||
|
|
||||||
def is_bone_filter_mode_item_available(pg, identifier):
|
def is_bone_filter_mode_item_available(pg, identifier):
|
||||||
match identifier:
|
if identifier == 'BONE_COLLECTIONS' and len(pg.bone_collection_list) == 0:
|
||||||
case 'BONE_COLLECTIONS':
|
return False
|
||||||
if len(pg.bone_collection_list) == 0:
|
|
||||||
return False
|
|
||||||
case _:
|
|
||||||
pass
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user