Implemented #22: Allow users to change the exported PSK material order

There is now a material menu that the user can re-order in the PSK export dialog.
This commit is contained in:
Colin Basnett
2022-07-01 19:47:01 -07:00
parent 494c5b116b
commit f58d4c5539
2 changed files with 113 additions and 29 deletions

View File

@@ -1,4 +1,5 @@
from collections import OrderedDict from collections import OrderedDict
from typing import Dict, List
from .data import * from .data import *
from ..helpers import * from ..helpers import *
@@ -15,8 +16,9 @@ class PskInputObjects(object):
class PskBuildOptions(object): class PskBuildOptions(object):
def __init__(self): def __init__(self):
self.bone_filter_mode = 'ALL' self.bone_filter_mode = 'ALL'
self.bone_group_indices = [] self.bone_group_indices: List[int] = []
self.use_raw_mesh_data = True self.use_raw_mesh_data = True
self.material_names: List[str] = []
def get_psk_input_objects(context) -> PskInputObjects: def get_psk_input_objects(context) -> PskInputObjects:
@@ -62,7 +64,6 @@ def build_psk(context, options: PskBuildOptions) -> Psk:
psk = Psk() psk = Psk()
bones = [] bones = []
materials = OrderedDict()
if armature_object is None: if armature_object is None:
# If the mesh has no armature object, simply assign it a dummy bone at the root to satisfy the requirement # If the mesh has no armature object, simply assign it a dummy bone at the root to satisfy the requirement
@@ -93,7 +94,7 @@ def build_psk(context, options: PskBuildOptions) -> Psk:
psk_bone.parent_index = parent_index psk_bone.parent_index = parent_index
psk.bones[parent_index].children_count += 1 psk.bones[parent_index].children_count += 1
except ValueError: except ValueError:
psk_bone.parent_index = 0 psk_bone.parent_index = -1
if bone.parent is not None: if bone.parent is not None:
rotation = bone.matrix.to_quaternion().conjugated() rotation = bone.matrix.to_quaternion().conjugated()
@@ -102,40 +103,35 @@ def build_psk(context, options: PskBuildOptions) -> Psk:
parent_tail = quat_parent @ bone.parent.tail parent_tail = quat_parent @ bone.parent.tail
location = (parent_tail - parent_head) + bone.head location = (parent_tail - parent_head) + bone.head
else: else:
location = armature_object.matrix_local @ bone.head local_matrix = armature_object.matrix_local
rot_matrix = bone.matrix @ armature_object.matrix_local.to_3x3() location = local_matrix @ bone.head
rot_matrix = bone.matrix @ local_matrix.to_3x3()
rotation = rot_matrix.to_quaternion() rotation = rot_matrix.to_quaternion()
psk_bone.location.x = location.x psk_bone.location.x = location.x
psk_bone.location.y = location.y psk_bone.location.y = location.y
psk_bone.location.z = location.z psk_bone.location.z = location.z
psk_bone.rotation.w = rotation.w
psk_bone.rotation.x = rotation.x psk_bone.rotation.x = rotation.x
psk_bone.rotation.y = rotation.y psk_bone.rotation.y = rotation.y
psk_bone.rotation.z = rotation.z psk_bone.rotation.z = rotation.z
psk_bone.rotation.w = rotation.w
psk.bones.append(psk_bone) psk.bones.append(psk_bone)
# MATERIALS
material_names = options.material_names
for material_name in material_names:
psk_material = Psk.Material()
psk_material.name = bytes(material_name, encoding='windows-1252')
psk_material.texture_index = len(psk.materials)
psk.materials.append(psk_material)
for input_mesh_object in input_objects.mesh_objects: for input_mesh_object in input_objects.mesh_objects:
# MATERIALS # MATERIALS
material_indices = [] material_indices = [material_names.index(material.name) for material in input_mesh_object.data.materials]
for i, material in enumerate(input_mesh_object.data.materials):
if material is None:
raise RuntimeError('Material cannot be empty (index ' + str(i) + ')')
if material.name in materials:
# Material already evaluated, just get its index.
material_index = list(materials.keys()).index(material.name)
else:
# New material.
psk_material = Psk.Material()
psk_material.name = bytes(material.name, encoding='windows-1252')
psk_material.texture_index = len(psk.materials)
psk.materials.append(psk_material)
materials[material.name] = material
material_index = psk_material.texture_index
material_indices.append(material_index)
if options.use_raw_mesh_data: if options.use_raw_mesh_data:
mesh_object = input_mesh_object mesh_object = input_mesh_object

View File

@@ -1,7 +1,7 @@
from typing import Type from typing import Type
from bpy.props import BoolProperty, StringProperty, CollectionProperty, IntProperty, EnumProperty from bpy.props import BoolProperty, StringProperty, CollectionProperty, IntProperty, EnumProperty, PointerProperty
from bpy.types import Operator, PropertyGroup from bpy.types import Operator, PropertyGroup, UIList, Material
from bpy_extras.io_utils import ExportHelper from bpy_extras.io_utils import ExportHelper
from .builder import build_psk, PskBuildOptions, get_psk_input_objects from .builder import build_psk, PskBuildOptions, get_psk_input_objects
@@ -67,6 +67,75 @@ def is_bone_filter_mode_item_available(context, identifier):
return True return True
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')
class MaterialListItem(PropertyGroup):
material_name: StringProperty()
index: IntProperty()
@property
def name(self):
return self.material_name
def populate_material_list(mesh_objects, material_list):
material_list.clear()
material_names = []
for mesh_object in mesh_objects:
for i, material in enumerate(mesh_object.data.materials):
# TODO: put this in the poll arg?
if material is None:
raise RuntimeError('Material cannot be empty (index ' + str(i) + ')')
if material.name not in material_names:
material_names.append(material.name)
for index, material_name in enumerate(material_names):
m = material_list.add()
m.material_name = material_name
m.index = index
class PskMaterialListItemMoveUp(Operator):
bl_idname = 'psk_export.material_list_item_move_up'
bl_label = 'Move Up'
bl_options = {'INTERNAL'}
bl_description = 'Move the selected material up one slot'
@classmethod
def poll(cls, context):
pg = context.scene.psk_export
return pg.material_list_index > 0
def execute(self, context):
pg = context.scene.psk_export
pg.material_list.move(pg.material_list_index, pg.material_list_index - 1)
pg.material_list_index -= 1
return {"FINISHED"}
class PskMaterialListItemMoveDown(Operator):
bl_idname = 'psk_export.material_list_item_move_down'
bl_label = 'Move Down'
bl_options = {'INTERNAL'}
bl_description = 'Move the selected material down one slot'
@classmethod
def poll(cls, context):
pg = context.scene.psk_export
return pg.material_list_index < len(pg.material_list) - 1
def execute(self, context):
pg = context.scene.psk_export
pg.material_list.move(pg.material_list_index, pg.material_list_index + 1)
pg.material_list_index += 1
return {"FINISHED"}
class PskExportOperator(Operator, ExportHelper): class PskExportOperator(Operator, ExportHelper):
bl_idname = 'export.psk' bl_idname = 'export.psk'
bl_label = 'Export' bl_label = 'Export'
@@ -92,6 +161,7 @@ class PskExportOperator(Operator, ExportHelper):
# Populate bone groups list. # Populate bone groups list.
populate_bone_group_list(input_objects.armature_object, pg.bone_group_list) populate_bone_group_list(input_objects.armature_object, pg.bone_group_list)
populate_material_list(input_objects.mesh_objects, pg.material_list)
context.window_manager.fileselect_add(self) context.window_manager.fileselect_add(self)
@@ -114,10 +184,9 @@ class PskExportOperator(Operator, ExportHelper):
layout.prop(pg, 'use_raw_mesh_data') layout.prop(pg, 'use_raw_mesh_data')
# BONES # BONES
box = layout.box() layout.label(text='Bones', icon='BONE_DATA')
box.label(text='Bones', icon='BONE_DATA')
bone_filter_mode_items = pg.bl_rna.properties['bone_filter_mode'].enum_items_static bone_filter_mode_items = pg.bl_rna.properties['bone_filter_mode'].enum_items_static
row = box.row(align=True) row = layout.row(align=True)
for item in bone_filter_mode_items: for item in bone_filter_mode_items:
identifier = item.identifier identifier = item.identifier
item_layout = row.row(align=True) item_layout = row.row(align=True)
@@ -125,16 +194,29 @@ class PskExportOperator(Operator, ExportHelper):
item_layout.enabled = is_bone_filter_mode_item_available(context, identifier) item_layout.enabled = is_bone_filter_mode_item_available(context, identifier)
if pg.bone_filter_mode == 'BONE_GROUPS': if pg.bone_filter_mode == 'BONE_GROUPS':
row = box.row() row = layout.row()
rows = max(3, min(len(pg.bone_group_list), 10)) rows = max(3, min(len(pg.bone_group_list), 10))
row.template_list('PSX_UL_BoneGroupList', '', pg, 'bone_group_list', pg, 'bone_group_list_index', rows=rows) row.template_list('PSX_UL_BoneGroupList', '', pg, 'bone_group_list', pg, 'bone_group_list_index', rows=rows)
layout.separator()
# MATERIALS
layout.label(text='Materials', icon='MATERIAL')
row = layout.row()
rows = max(3, min(len(pg.bone_group_list), 10))
row.template_list('PSK_UL_MaterialList', '', pg, 'material_list', pg, 'material_list_index', rows=rows)
col = row.column(align=True)
col.operator(PskMaterialListItemMoveUp.bl_idname, text='', icon='TRIA_UP')
col.operator(PskMaterialListItemMoveDown.bl_idname, text='', icon='TRIA_DOWN')
def execute(self, context): def execute(self, context):
pg = context.scene.psk_export pg = context.scene.psk_export
options = PskBuildOptions() options = PskBuildOptions()
options.bone_filter_mode = pg.bone_filter_mode options.bone_filter_mode = pg.bone_filter_mode
options.bone_group_indices = [x.index for x in pg.bone_group_list if x.is_selected] options.bone_group_indices = [x.index for x in pg.bone_group_list if x.is_selected]
options.use_raw_mesh_data = pg.use_raw_mesh_data options.use_raw_mesh_data = pg.use_raw_mesh_data
options.material_names = [m.material_name for m in pg.material_list]
try: try:
psk = build_psk(context, options) psk = build_psk(context, options)
export_psk(psk, self.filepath) export_psk(psk, self.filepath)
@@ -158,9 +240,15 @@ class PskExportPropertyGroup(PropertyGroup):
bone_group_list: CollectionProperty(type=BoneGroupListItem) bone_group_list: CollectionProperty(type=BoneGroupListItem)
bone_group_list_index: IntProperty(default=0) bone_group_list_index: IntProperty(default=0)
use_raw_mesh_data: BoolProperty(default=False, name='Raw Mesh Data', description='No modifiers will be evaluated as part of the exported mesh') use_raw_mesh_data: BoolProperty(default=False, name='Raw Mesh Data', description='No modifiers will be evaluated as part of the exported mesh')
material_list: CollectionProperty(type=MaterialListItem)
material_list_index: IntProperty(default=0)
classes = ( classes = (
MaterialListItem,
PSK_UL_MaterialList,
PskMaterialListItemMoveUp,
PskMaterialListItemMoveDown,
PskExportOperator, PskExportOperator,
PskExportPropertyGroup PskExportPropertyGroup,
) )