fix issues

- disable bmx import/export temporaryly
- optimize the ui display of importing/exporting. (use box to organize props)
- place button horizontally, not vertically in virtools material.
- disallow apply in legacy align if no axis selected.
- add applied step counter in legacy alignment.
- add feedback for reseting bme material/component. (add a message box to show success)
This commit is contained in:
yyc12345 2024-01-01 13:07:10 +08:00
parent 318d661ac1
commit 680f367a42
12 changed files with 172 additions and 64 deletions

View File

@ -1,7 +1,7 @@
import bpy import bpy
from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_icons_manager, UTIL_ioport_shared
class BBP_OT_export_bmfile(bpy.types.Operator, UTIL_file_browser.ExportBmxFile): class BBP_OT_export_bmfile(bpy.types.Operator, UTIL_file_browser.ExportBmxFile, UTIL_ioport_shared.ExportParams):
"""Save a Ballance Map File (BM File Spec 1.4)""" """Save a Ballance Map File (BM File Spec 1.4)"""
bl_idname = "bbp.export_bmfile" bl_idname = "bbp.export_bmfile"
bl_label = "Export BM (Ballance Map) File" bl_label = "Export BM (Ballance Map) File"
@ -12,12 +12,18 @@ class BBP_OT_export_bmfile(bpy.types.Operator, UTIL_file_browser.ExportBmxFile):
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder() return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
def execute(self, context): def execute(self, context):
UTIL_functions.message_box((self.general_get_filename(), ), 'Export BM File Path', 'INFO') UTIL_functions.message_box(
('This function not supported yet.', ),
'No Implement',
UTIL_icons_manager.BlenderPresetIcons.Error.value
)
self.report({'INFO'}, "BM File Exporting Finished.") self.report({'INFO'}, "BM File Exporting Finished.")
return {'FINISHED'} return {'FINISHED'}
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text = 'Export Target')
self.draw_export_params(layout.box())
def register() -> None: def register() -> None:
bpy.utils.register_class(BBP_OT_export_bmfile) bpy.utils.register_class(BBP_OT_export_bmfile)

View File

@ -68,15 +68,21 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text = 'Export Target') layout.label(text = 'Export Target')
self.draw_export_params(layout) self.draw_export_params(layout.box())
layout.separator() layout.separator()
layout.label(text = 'Virtools Params') layout.label(text = 'Virtools Params')
self.draw_virtools_params(layout) box = layout.box()
layout.label(text = 'Global Texture Save Option') self.draw_virtools_params(box)
layout.prop(self, 'texture_save_opt', text = '')
layout.prop(self, 'use_compress') box.separator()
box.label(text = 'Global Texture Save Option')
box.prop(self, 'texture_save_opt', text = '')
box.separator()
box.prop(self, 'use_compress')
if self.use_compress: if self.use_compress:
layout.prop(self, 'compress_level') box.prop(self, 'compress_level')
_TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject] _TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
_TMeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh] _TMeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]

View File

@ -1,7 +1,7 @@
import bpy import bpy
from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_icons_manager, UTIL_ioport_shared
class BBP_OT_import_bmfile(bpy.types.Operator, UTIL_file_browser.ImportBmxFile): class BBP_OT_import_bmfile(bpy.types.Operator, UTIL_file_browser.ImportBmxFile, UTIL_ioport_shared.ImportParams):
"""Load a Ballance Map File (BM File Spec 1.4)""" """Load a Ballance Map File (BM File Spec 1.4)"""
bl_idname = "bbp.import_bmfile" bl_idname = "bbp.import_bmfile"
bl_label = "Import BM (Ballance Map) File" bl_label = "Import BM (Ballance Map) File"
@ -12,12 +12,18 @@ class BBP_OT_import_bmfile(bpy.types.Operator, UTIL_file_browser.ImportBmxFile):
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder() return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
def execute(self, context): def execute(self, context):
UTIL_functions.message_box((self.general_get_filename(), ), 'Import BM File Path', 'INFO') UTIL_functions.message_box(
('This function not supported yet.', ),
'No Implement',
UTIL_icons_manager.BlenderPresetIcons.Error.value
)
self.report({'INFO'}, "BM File Importing Finished.") self.report({'INFO'}, "BM File Importing Finished.")
return {'FINISHED'} return {'FINISHED'}
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text = 'Conflict Options')
self.draw_import_params(layout.box())
def register() -> None: def register() -> None:
bpy.utils.register_class(BBP_OT_import_bmfile) bpy.utils.register_class(BBP_OT_import_bmfile)

View File

@ -29,10 +29,10 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text = 'Conflict Options') layout.label(text = 'Conflict Options')
self.draw_import_params(layout) self.draw_import_params(layout.box())
layout.separator() layout.separator()
layout.label(text = 'Virtools Params') layout.label(text = 'Virtools Params')
self.draw_virtools_params(layout) self.draw_virtools_params(layout.box())
def _import_virtools(file_name_: str, encodings_: tuple[str]) -> None: def _import_virtools(file_name_: str, encodings_: tuple[str]) -> None:
# create temp folder # create temp folder

View File

@ -82,8 +82,21 @@ class BBP_OT_legacy_align(bpy.types.Operator):
if self.apply_flag == True: return if self.apply_flag == True: return
self.apply_flag = True self.apply_flag = True
# check whether add new entry
# if no selected axis, this alignment is invalid
entry: BBP_PG_legacy_align_history = self.align_history[-1]
if entry.align_x == True or entry.align_y == True or entry.align_z == True:
# valid one
# add a new entry in history # add a new entry in history
self.align_history.add() self.align_history.add()
else:
# invalid one
# reset all data to default
entry.align_x = False
entry.align_y = False
entry.align_z = False
entry.current_align_mode = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter)
entry.target_align_mode = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter)
# reset hinder # reset hinder
self.recursive_hinder = False self.recursive_hinder = False
@ -142,7 +155,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
col = layout.column() col = layout.column()
# show axis # show axis
col.label(text="Align Axis") col.label(text="Align Axis (Multi-selection)")
row = col.row() row = col.row()
row.prop(entry, "align_x", toggle = 1) row.prop(entry, "align_x", toggle = 1)
row.prop(entry, "align_y", toggle = 1) row.prop(entry, "align_y", toggle = 1)
@ -152,12 +165,17 @@ class BBP_OT_legacy_align(bpy.types.Operator):
col.separator() col.separator()
col.label(text = 'Current Object (Active Object)') col.label(text = 'Current Object (Active Object)')
col.prop(entry, "current_align_mode", expand = True) col.prop(entry, "current_align_mode", expand = True)
col.label(text = 'Target Objects (Other Objects)') col.label(text = 'Target Objects (Selected Objects)')
col.prop(entry, "target_align_mode", expand = True) col.prop(entry, "target_align_mode", expand = True)
# show apply button # show apply button
col.separator() col.separator()
col.prop(self, 'apply_flag', text = 'Apply', icon = 'CHECKMARK', toggle = 1) conditional_disable_area = col.column()
# only allow Apply when there is a selected axis
conditional_disable_area.enabled = entry.align_x == True or entry.align_y == True or entry.align_z == True
# show apply and counter
conditional_disable_area.prop(self, 'apply_flag', text = 'Apply', icon = 'CHECKMARK', toggle = 1)
conditional_disable_area.label(text = f'Total {len(self.align_history) - 1} applied alignments')
#region Core Functions #region Core Functions

View File

@ -54,7 +54,8 @@ class BBP_OT_select_object_by_virtools_group(bpy.types.Operator, PROP_virtools_g
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text='Selection Mode') layout.label(text='Selection Mode')
layout.prop(self, 'selection_mode', expand = True) sublayout = layout.column() # make selection expand vertically, not horizontal.
sublayout.prop(self, 'selection_mode', expand = True)
layout.separator() layout.separator()
layout.label(text='Group Parameters') layout.label(text='Group Parameters')

View File

@ -1,7 +1,7 @@
import bpy import bpy
import os, typing, enum, array import os, typing, enum, array
from . import PROP_virtools_mesh from . import PROP_virtools_mesh
from . import UTIL_functions, UTIL_file_io, UTIL_blender_mesh, UTIL_virtools_types from . import UTIL_functions, UTIL_file_io, UTIL_blender_mesh, UTIL_virtools_types, UTIL_icons_manager
#region Raw Elements Operations #region Raw Elements Operations
@ -349,6 +349,12 @@ class BBP_OT_reset_ballance_elements(bpy.types.Operator):
def execute(self, context): def execute(self, context):
reset_ballance_elements(context.scene) reset_ballance_elements(context.scene)
# show a window to let user know, not silence
UTIL_functions.message_box(
('Reset OK.', ),
"Reset Result",
UTIL_icons_manager.BlenderPresetIcons.Info.value
)
return {'FINISHED'} return {'FINISHED'}
class BBP_PT_ballance_elements(bpy.types.Panel): class BBP_PT_ballance_elements(bpy.types.Panel):

View File

@ -1,7 +1,7 @@
import bpy import bpy
import typing, enum, copy import typing, enum, copy
from . import PROP_virtools_material, PROP_virtools_texture from . import PROP_virtools_material, PROP_virtools_texture
from . import UTIL_ballance_texture, UTIL_functions from . import UTIL_ballance_texture, UTIL_functions, UTIL_icons_manager
#region BME Material Presets #region BME Material Presets
@ -239,6 +239,12 @@ class BBP_OT_reset_bme_materials(bpy.types.Operator):
def execute(self, context): def execute(self, context):
reset_bme_materials(context.scene) reset_bme_materials(context.scene)
# show a window to let user know, not silence
UTIL_functions.message_box(
('Reset OK.', ),
"Reset Result",
UTIL_icons_manager.BlenderPresetIcons.Info.value
)
return {'FINISHED'} return {'FINISHED'}
class BBP_PT_bme_materials(bpy.types.Panel): class BBP_PT_bme_materials(bpy.types.Panel):

View File

@ -656,8 +656,9 @@ class BBP_PT_virtools_material(bpy.types.Panel):
props: BBP_PG_virtools_material = get_virtools_material(context.material) props: BBP_PG_virtools_material = get_virtools_material(context.material)
# draw operator # draw operator
layout.operator(BBP_OT_preset_virtools_material.bl_idname, icon="PRESET") row = layout.row()
layout.operator(BBP_OT_apply_virtools_material.bl_idname, icon="NODETREE") row.operator(BBP_OT_preset_virtools_material.bl_idname, text = 'Preset', icon = "PRESET")
row.operator(BBP_OT_apply_virtools_material.bl_idname, text = 'Apply', icon = "NODETREE")
# draw data # draw data
layout.label(text="Color Parameters") layout.label(text="Color Parameters")

View File

@ -1,6 +1,6 @@
import bpy import bpy
import enum import enum
from . import UTIL_virtools_types from . import UTIL_virtools_types, UTIL_functions
from . import PROP_ptrprop_resolver from . import PROP_ptrprop_resolver
## Intent ## Intent
@ -9,65 +9,90 @@ from . import PROP_ptrprop_resolver
# and call general getter to get user selected data. # and call general getter to get user selected data.
# Also provide draw function thus caller do not need draw the params themselves. # Also provide draw function thus caller do not need draw the params themselves.
class ConflictStrategy(enum.IntEnum):
Rename = enum.auto()
Current = enum.auto()
Replace = enum.auto()
_g_ConflictStrategyDesc: dict[ConflictStrategy, tuple[str, str]] = {
ConflictStrategy.Rename: ('Rename', 'Rename the new one'),
ConflictStrategy.Current: ('Use Current', 'Use current one'),
ConflictStrategy.Replace: ('Replace', 'Replace the old one with new one'),
}
_g_EnumHelper_ConflictStrategy: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
ConflictStrategy,
lambda x: str(x.value),
lambda x: ConflictStrategy(int(x)),
lambda x: _g_ConflictStrategyDesc[x][0],
lambda x: _g_ConflictStrategyDesc[x][1],
lambda _: ''
)
class ImportParams(): class ImportParams():
texture_conflict_strategy: bpy.props.EnumProperty( texture_conflict_strategy: bpy.props.EnumProperty(
name = "Texture name conflict", name = "Texture Name Conflict",
items = ( items = _g_EnumHelper_ConflictStrategy.generate_items(),
('NEW', "New Instance", "Create a new instance"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process texture name conflict", description = "Define how to process texture name conflict",
default = 'CURRENT', default = _g_EnumHelper_ConflictStrategy.to_selection(ConflictStrategy.Current),
) )
material_conflict_strategy: bpy.props.EnumProperty( material_conflict_strategy: bpy.props.EnumProperty(
name = "Material name conflict", name = "Material Name Conflict",
items = ( items = _g_EnumHelper_ConflictStrategy.generate_items(),
('RENAME', "Rename", "Rename the new one"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process material name conflict", description = "Define how to process material name conflict",
default = 'RENAME', default = _g_EnumHelper_ConflictStrategy.to_selection(ConflictStrategy.Rename),
) )
mesh_conflict_strategy: bpy.props.EnumProperty( mesh_conflict_strategy: bpy.props.EnumProperty(
name = "Mesh name conflict", name = "Mesh Name Conflict",
items = ( items = _g_EnumHelper_ConflictStrategy.generate_items(),
('RENAME', "Rename", "Rename the new one"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process mesh name conflict", description = "Define how to process mesh name conflict",
default = 'RENAME', default = _g_EnumHelper_ConflictStrategy.to_selection(ConflictStrategy.Rename),
) )
object_conflict_strategy: bpy.props.EnumProperty( object_conflict_strategy: bpy.props.EnumProperty(
name = "Object name conflict", name = "Object Name Conflict",
items = ( items = _g_EnumHelper_ConflictStrategy.generate_items(),
('RENAME', "Rename", "Rename the new one"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process object name conflict", description = "Define how to process object name conflict",
default = 'RENAME', default = _g_EnumHelper_ConflictStrategy.to_selection(ConflictStrategy.Rename),
) )
def draw_import_params(self, layout: bpy.types.UILayout) -> None: def draw_import_params(self, layout: bpy.types.UILayout) -> None:
layout.prop(self, 'object_conflict_strategy') layout.label(text = 'Object Name Conflict')
layout.prop(self, 'mesh_conflict_strategy') layout.prop(self, 'object_conflict_strategy', text = '')
layout.prop(self, 'material_conflict_strategy') layout.label(text = 'Mesh Name Conflict')
layout.prop(self, 'texture_conflict_strategy') layout.prop(self, 'mesh_conflict_strategy', text = '')
layout.label(text = 'Material Name Conflict')
layout.prop(self, 'material_conflict_strategy', text = '')
layout.label(text = 'Texture Name Conflict')
layout.prop(self, 'texture_conflict_strategy', text = '')
def general_get_texture_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.texture_conflict_strategy)
def general_get_material_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.material_conflict_strategy)
def general_get_mesh_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.mesh_conflict_strategy)
def general_get_object_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.object_conflict_strategy)
class ExportParams(): class ExportParams():
export_mode: bpy.props.EnumProperty( export_mode: bpy.props.EnumProperty(
name = "Export Mode", name = "Export Mode",
items = ( items = (
('COLLECTION', "Collection", "Export a collection"), ('COLLECTION', "Collection", "Export a collection", 'OUTLINER_COLLECTION', 0),
('OBJECT', "Object", "Export an object"), ('OBJECT', "Object", "Export an object", 'OBJECT_DATA', 1),
), ),
) )
def draw_export_params(self, layout: bpy.types.UILayout) -> None: def draw_export_params(self, layout: bpy.types.UILayout) -> None:
# make prop expand horizontaly, not vertical.
sublayout = layout.row()
# draw switch # draw switch
layout.prop(self, "export_mode", expand = True) sublayout.prop(self, "export_mode", expand = True)
# draw picker # draw picker
if self.export_mode == 'COLLECTION': if self.export_mode == 'COLLECTION':
PROP_ptrprop_resolver.draw_export_collection(layout) PROP_ptrprop_resolver.draw_export_collection(layout)
@ -96,7 +121,8 @@ class VirtoolsParams():
) )
def draw_virtools_params(self, layout: bpy.types.UILayout) -> None: def draw_virtools_params(self, layout: bpy.types.UILayout) -> None:
layout.prop(self, 'vt_encodings') layout.label(text = 'Encodings')
layout.prop(self, 'vt_encodings', text = '')
def general_get_vt_encodings(self) -> tuple[str]: def general_get_vt_encodings(self) -> tuple[str]:
# get encoding, split it by `;` and strip blank chars. # get encoding, split it by `;` and strip blank chars.
@ -108,6 +134,13 @@ class ExportEditModeBackup():
The class which save Edit Mode when exporting and restore it after exporting. The class which save Edit Mode when exporting and restore it after exporting.
Because edit mode is not allowed when exporting. Because edit mode is not allowed when exporting.
Support `with` statement. Support `with` statement.
```
with ExportEditModeBackup():
# do some exporting work
blabla()
# restore automatically when exiting "with"
```
""" """
mInEditMode: bool mInEditMode: bool
@ -127,4 +160,28 @@ class ExportEditModeBackup():
bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle()
self.mInEditMode = False self.mInEditMode = False
class ConflictResolver():
"""
This class frequently used when importing objects.
This class accept 4 conflict strategies for object, mesh, material and texture,
and provide 4 general creation functions to handle these strategies.
Each general creation functions will return an instance and a bool indicating whether this instance need be initialized.
This class also provide 3 static common creation functions without considering conflict.
They just a redirect calling to `bpy.data.xxx.new()`.
No static texture (Image) creation function because texture is not created from `bpy.data.images`.
"""
@staticmethod
def create_object(name: str, data: bpy.types.Mesh) -> bpy.types.Object:
return bpy.data.objects.new(name, data)
@staticmethod
def create_mesh(name: str) -> bpy.types.Mesh:
return bpy.data.meshes.new(name)
@staticmethod
def create_material(name: str) -> bpy.types.Material:
return bpy.data.materials.new(name)

View File

@ -373,7 +373,8 @@ class VirtoolsGroupConvention():
case BallanceObjectType.COMPONENT: case BallanceObjectType.COMPONENT:
# group into component type # group into component type
gp.add_group(info.mComponentType) # use typing.cast() to force linter accept it because None is impossible
gp.add_group(typing.cast(str, info.mComponentType))
# group to sector # group to sector
if info.mSector == 9: if info.mSector == 9:

View File

@ -103,12 +103,12 @@ MenuDrawer_t = typing.Callable[[typing.Any, typing.Any], None]
def menu_drawer_import(self, context) -> None: def menu_drawer_import(self, context) -> None:
layout: bpy.types.UILayout = self.layout layout: bpy.types.UILayout = self.layout
layout.operator(OP_IMPORT_bmfile.BBP_OT_import_bmfile.bl_idname, text = "Ballance Map (.bmx)") #layout.operator(OP_IMPORT_bmfile.BBP_OT_import_bmfile.bl_idname, text = "Ballance Map (.bmx)")
layout.operator(OP_IMPORT_virtools.BBP_OT_import_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)") layout.operator(OP_IMPORT_virtools.BBP_OT_import_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)")
def menu_drawer_export(self, context) -> None: def menu_drawer_export(self, context) -> None:
layout: bpy.types.UILayout = self.layout layout: bpy.types.UILayout = self.layout
layout.operator(OP_EXPORT_bmfile.BBP_OT_export_bmfile.bl_idname, text = "Ballance Map (.bmx)") #layout.operator(OP_EXPORT_bmfile.BBP_OT_export_bmfile.bl_idname, text = "Ballance Map (.bmx)")
layout.operator(OP_EXPORT_virtools.BBP_OT_export_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)") layout.operator(OP_EXPORT_virtools.BBP_OT_export_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)")
def menu_drawer_view3d(self, context) -> None: def menu_drawer_view3d(self, context) -> None: