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 typing import Dict, List
from .data import *
from ..helpers import *
@@ -15,8 +16,9 @@ class PskInputObjects(object):
class PskBuildOptions(object):
def __init__(self):
self.bone_filter_mode = 'ALL'
self.bone_group_indices = []
self.bone_group_indices: List[int] = []
self.use_raw_mesh_data = True
self.material_names: List[str] = []
def get_psk_input_objects(context) -> PskInputObjects:
@@ -62,7 +64,6 @@ def build_psk(context, options: PskBuildOptions) -> Psk:
psk = Psk()
bones = []
materials = OrderedDict()
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
@@ -93,7 +94,7 @@ def build_psk(context, options: PskBuildOptions) -> Psk:
psk_bone.parent_index = parent_index
psk.bones[parent_index].children_count += 1
except ValueError:
psk_bone.parent_index = 0
psk_bone.parent_index = -1
if bone.parent is not None:
rotation = bone.matrix.to_quaternion().conjugated()
@@ -102,40 +103,35 @@ def build_psk(context, options: PskBuildOptions) -> Psk:
parent_tail = quat_parent @ bone.parent.tail
location = (parent_tail - parent_head) + bone.head
else:
location = armature_object.matrix_local @ bone.head
rot_matrix = bone.matrix @ armature_object.matrix_local.to_3x3()
local_matrix = armature_object.matrix_local
location = local_matrix @ bone.head
rot_matrix = bone.matrix @ local_matrix.to_3x3()
rotation = rot_matrix.to_quaternion()
psk_bone.location.x = location.x
psk_bone.location.y = location.y
psk_bone.location.z = location.z
psk_bone.rotation.w = rotation.w
psk_bone.rotation.x = rotation.x
psk_bone.rotation.y = rotation.y
psk_bone.rotation.z = rotation.z
psk_bone.rotation.w = rotation.w
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:
# MATERIALS
material_indices = []
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)
material_indices = [material_names.index(material.name) for material in input_mesh_object.data.materials]
if options.use_raw_mesh_data:
mesh_object = input_mesh_object

View File

@@ -1,7 +1,7 @@
from typing import Type
from bpy.props import BoolProperty, StringProperty, CollectionProperty, IntProperty, EnumProperty
from bpy.types import Operator, PropertyGroup
from bpy.props import BoolProperty, StringProperty, CollectionProperty, IntProperty, EnumProperty, PointerProperty
from bpy.types import Operator, PropertyGroup, UIList, Material
from bpy_extras.io_utils import ExportHelper
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
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):
bl_idname = 'export.psk'
bl_label = 'Export'
@@ -92,6 +161,7 @@ class PskExportOperator(Operator, ExportHelper):
# Populate bone groups 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)
@@ -114,10 +184,9 @@ class PskExportOperator(Operator, ExportHelper):
layout.prop(pg, 'use_raw_mesh_data')
# BONES
box = layout.box()
box.label(text='Bones', icon='BONE_DATA')
layout.label(text='Bones', icon='BONE_DATA')
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:
identifier = item.identifier
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)
if pg.bone_filter_mode == 'BONE_GROUPS':
row = box.row()
row = layout.row()
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)
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):
pg = context.scene.psk_export
options = PskBuildOptions()
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.use_raw_mesh_data = pg.use_raw_mesh_data
options.material_names = [m.material_name for m in pg.material_list]
try:
psk = build_psk(context, options)
export_psk(psk, self.filepath)
@@ -158,9 +240,15 @@ class PskExportPropertyGroup(PropertyGroup):
bone_group_list: CollectionProperty(type=BoneGroupListItem)
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')
material_list: CollectionProperty(type=MaterialListItem)
material_list_index: IntProperty(default=0)
classes = (
MaterialListItem,
PSK_UL_MaterialList,
PskMaterialListItemMoveUp,
PskMaterialListItemMoveDown,
PskExportOperator,
PskExportPropertyGroup
PskExportPropertyGroup,
)