diff --git a/bbp_ng/OP_EXPORT_bmfile.py b/bbp_ng/OP_EXPORT_bmfile.py index 3d0413d..450d8c3 100644 --- a/bbp_ng/OP_EXPORT_bmfile.py +++ b/bbp_ng/OP_EXPORT_bmfile.py @@ -1,5 +1,5 @@ import bpy -from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_icons_manager, UTIL_ioport_shared +from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ioport_shared 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)""" @@ -12,18 +12,14 @@ class BBP_OT_export_bmfile(bpy.types.Operator, UTIL_file_browser.ExportBmxFile, return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder() def execute(self, context): - 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({'ERROR'}, 'This function not supported yet.') + # self.report({'INFO'}, "BM File Exporting Finished.") return {'FINISHED'} def draw(self, context): layout = self.layout layout.label(text = 'Export Target') - self.draw_export_params(layout.box()) + self.draw_export_params(context, layout.box()) def register() -> None: bpy.utils.register_class(BBP_OT_export_bmfile) diff --git a/bbp_ng/OP_EXPORT_virtools.py b/bbp_ng/OP_EXPORT_virtools.py index 0087236..eb34031 100644 --- a/bbp_ng/OP_EXPORT_virtools.py +++ b/bbp_ng/OP_EXPORT_virtools.py @@ -2,7 +2,7 @@ import bpy, mathutils from bpy_extras.wm_utils.progress_report import ProgressReport import tempfile, os, typing from . import PROP_preferences, UTIL_ioport_shared -from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_icons_manager, UTIL_naming_convension +from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_naming_convension from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light from .PyBMap import bmap_wrapper as bmap @@ -20,30 +20,28 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool def execute(self, context): # check selecting first - objls: tuple[bpy.types.Object] | None = self.general_get_export_objects() + objls: tuple[bpy.types.Object] | None = self.general_get_export_objects(context) if objls is None: - UTIL_functions.message_box( - ('No selected target!', ), - 'Lost Parameters', - UTIL_icons_manager.BlenderPresetIcons.Error.value - ) + self.report({'ERROR'}, 'No selected target!') return {'CANCELLED'} - # check texture save option to prevent real stupid user. + # check texture save option to avoid real stupid user. texture_save_opt = self.general_get_texture_save_opt() if texture_save_opt == UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_USEGLOBAL: - UTIL_functions.message_box( - ('You can not specify "Use Global" as global texture save option!', ), - 'Wrong Parameters', - UTIL_icons_manager.BlenderPresetIcons.Error.value - ) + self.report({'ERROR'}, 'You can not specify "Use Global" as global texture save option!') + return {'CANCELLED'} + + # check whether encoding list is empty to avoid real stupid user. + encodings = self.general_get_vt_encodings(context) + if len(encodings) == 0: + self.report({'ERROR'}, 'You must specify at least one encoding for file saving (e.g. cp1252, gb2312)!') return {'CANCELLED'} # start exporting with UTIL_ioport_shared.ExportEditModeBackup() as editmode_guard: _export_virtools( self.general_get_filename(), - self.general_get_vt_encodings(), + encodings, texture_save_opt, self.general_get_use_compress(), self.general_get_compress_level(), @@ -57,8 +55,8 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool def draw(self, context): layout = self.layout - self.draw_export_params(layout) - self.draw_virtools_params(layout, False) + self.draw_export_params(context, layout) + self.draw_virtools_params(context, layout, False) self.draw_ballance_params(layout, False) _TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject] diff --git a/bbp_ng/OP_IMPORT_bmfile.py b/bbp_ng/OP_IMPORT_bmfile.py index 8c579ae..7d867f8 100644 --- a/bbp_ng/OP_IMPORT_bmfile.py +++ b/bbp_ng/OP_IMPORT_bmfile.py @@ -1,5 +1,5 @@ import bpy -from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_icons_manager, UTIL_ioport_shared +from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ioport_shared 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)""" @@ -12,12 +12,8 @@ class BBP_OT_import_bmfile(bpy.types.Operator, UTIL_file_browser.ImportBmxFile, return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder() def execute(self, context): - 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({'ERROR'}, 'This function not supported yet.') + # self.report({'INFO'}, "BM File Importing Finished.") return {'FINISHED'} def draw(self, context): diff --git a/bbp_ng/OP_IMPORT_virtools.py b/bbp_ng/OP_IMPORT_virtools.py index bb3b204..ac411d4 100644 --- a/bbp_ng/OP_IMPORT_virtools.py +++ b/bbp_ng/OP_IMPORT_virtools.py @@ -19,9 +19,15 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool and bmap.is_bmap_available()) def execute(self, context): + # check whether encoding list is empty to avoid real stupid user. + encodings = self.general_get_vt_encodings(context) + if len(encodings) == 0: + self.report({'ERROR'}, 'You must specify at least one encoding for file loading (e.g. cp1252, gb2312)!') + return {'CANCELLED'} + _import_virtools( self.general_get_filename(), - self.general_get_vt_encodings(), + encodings, self.general_get_conflict_resolver() ) self.report({'INFO'}, "Virtools File Importing Finished.") @@ -30,7 +36,7 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool def draw(self, context): layout = self.layout self.draw_import_params(layout) - self.draw_virtools_params(layout, True) + self.draw_virtools_params(context, layout, True) self.draw_ballance_params(layout, True) def _import_virtools(file_name_: str, encodings_: tuple[str], resolver: UTIL_ioport_shared.ConflictResolver) -> None: diff --git a/bbp_ng/OP_UV_rail_uv.py b/bbp_ng/OP_UV_rail_uv.py index 794db0e..d7aa475 100644 --- a/bbp_ng/OP_UV_rail_uv.py +++ b/bbp_ng/OP_UV_rail_uv.py @@ -19,13 +19,10 @@ class BBP_OT_rail_uv(bpy.types.Operator): def execute(self, context): # check material - mtl: bpy.types.Material = PROP_ptrprop_resolver.PtrPropResolver.get_rail_uv_material() + ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene) + mtl: bpy.types.Material = ptrprops.get_rail_uv_material() if mtl is None: - UTIL_functions.message_box( - ("No specific material", ), - "Lost Parameter", - UTIL_icons_manager.BlenderPresetIcons.Error.value - ) + self.report({'ERROR'}, "Specified material is empty.") return {'CANCELLED'} # apply rail uv @@ -34,7 +31,8 @@ class BBP_OT_rail_uv(bpy.types.Operator): def draw(self, context): layout: bpy.types.UILayout = self.layout - PROP_ptrprop_resolver.PtrPropResolver.draw_rail_uv_material(layout) + ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene) + ptrprops.draw_rail_uv_material(layout) #region Real Worker Functions diff --git a/bbp_ng/PROP_ptrprop_resolver.py b/bbp_ng/PROP_ptrprop_resolver.py index f99fa37..d160322 100644 --- a/bbp_ng/PROP_ptrprop_resolver.py +++ b/bbp_ng/PROP_ptrprop_resolver.py @@ -1,6 +1,6 @@ import bpy from bpy.types import Context, Event -from . import UTIL_functions +from . import UTIL_functions, UTIL_virtools_types ## Intent # Operator is not allowed to register Pointer Properties. @@ -45,15 +45,15 @@ class BBP_PG_ptrprop_resolver(bpy.types.PropertyGroup): #endregion -def get_ptrprop_resolver() -> BBP_PG_ptrprop_resolver: - return bpy.context.scene.bbp_ptrprop_resolver +def get_ptrprop_resolver(scene: bpy.types.Scene) -> BBP_PG_ptrprop_resolver: + return scene.bbp_ptrprop_resolver -def get_ioport_encodings() -> UTIL_functions.CollectionVisitor[BBP_PG_bmap_encoding]: - return UTIL_functions.CollectionVisitor(get_ptrprop_resolver().ioport_encodings) -def get_active_ioport_encoding() -> int: - return get_ptrprop_resolver().active_ioport_encodings -def set_active_ioport_encoding(val: int) -> None: - get_ptrprop_resolver().active_ioport_encodings = val +def get_ioport_encodings(scene: bpy.types.Scene) -> UTIL_functions.CollectionVisitor[BBP_PG_bmap_encoding]: + return UTIL_functions.CollectionVisitor(get_ptrprop_resolver(scene).ioport_encodings) +def get_active_ioport_encoding(scene: bpy.types.Scene) -> int: + return get_ptrprop_resolver(scene).active_ioport_encodings +def set_active_ioport_encoding(scene: bpy.types.Scene, val: int) -> None: + get_ptrprop_resolver(scene).active_ioport_encodings = val #region Blender Operator Defines for Encodings @@ -68,7 +68,7 @@ class BBP_OT_add_ioport_encodings(bpy.types.Operator): return True def execute(self, context): - encodings = get_ioport_encodings() + encodings = get_ioport_encodings(context.scene) encodings.add() return {'FINISHED'} @@ -80,13 +80,19 @@ class BBP_OT_rm_ioport_encodings(bpy.types.Operator): @classmethod def poll(cls, context: bpy.types.Context) -> bool: - encodings = get_ioport_encodings() - index = get_active_ioport_encoding() + encodings = get_ioport_encodings(context.scene) + index = get_active_ioport_encoding(context.scene) return index >= 0 and index < len(encodings) def execute(self, context): - encodings = get_ioport_encodings() - encodings.remove(get_active_ioport_encoding()) + # delete selected item + encodings = get_ioport_encodings(context.scene) + index = get_active_ioport_encoding(context.scene) + encodings.remove(index) + # try to correct selected item + if index >= len(encodings): index = len(encodings) - 1 + if index < 0: index = 0 + set_active_ioport_encoding(context.scene, index) return {'FINISHED'} class BBP_OT_up_ioport_encodings(bpy.types.Operator): @@ -97,15 +103,15 @@ class BBP_OT_up_ioport_encodings(bpy.types.Operator): @classmethod def poll(cls, context: bpy.types.Context) -> bool: - encodings = get_ioport_encodings() - index = get_active_ioport_encoding() + encodings = get_ioport_encodings(context.scene) + index = get_active_ioport_encoding(context.scene) return index >= 1 and index < len(encodings) def execute(self, context): - encodings = get_ioport_encodings() - index = get_active_ioport_encoding() + encodings = get_ioport_encodings(context.scene) + index = get_active_ioport_encoding(context.scene) encodings.move(index, index - 1) - set_active_ioport_encoding(index - 1) + set_active_ioport_encoding(context.scene, index - 1) return {'FINISHED'} class BBP_OT_down_ioport_encodings(bpy.types.Operator): @@ -116,15 +122,15 @@ class BBP_OT_down_ioport_encodings(bpy.types.Operator): @classmethod def poll(cls, context: bpy.types.Context) -> bool: - encodings = get_ioport_encodings() - index = get_active_ioport_encoding() + encodings = get_ioport_encodings(context.scene) + index = get_active_ioport_encoding(context.scene) return index >= 0 and index < len(encodings) - 1 def execute(self, context): - encodings = get_ioport_encodings() - index = get_active_ioport_encoding() + encodings = get_ioport_encodings(context.scene) + index = get_active_ioport_encoding(context.scene) encodings.move(index, index + 1) - set_active_ioport_encoding(index + 1) + set_active_ioport_encoding(context.scene, index + 1) return {'FINISHED'} class BBP_OT_clear_ioport_encodings(bpy.types.Operator): @@ -142,55 +148,46 @@ class BBP_OT_clear_ioport_encodings(bpy.types.Operator): return wm.invoke_confirm(self, event) def execute(self, context): - encodings = get_ioport_encodings() + encodings = get_ioport_encodings(context.scene) encodings.clear() - set_active_ioport_encoding(0) + set_active_ioport_encoding(context.scene, 0) return {'FINISHED'} #endregion -class PtrPropResolver(): +class PropsVisitor(): """ - All outside code should use this class static methods to fetch property data or draw property. - All function inside in this module should not be called directly. + When outside code want to fetch or draw properties defined in ptrprop_resolver, + they should create the instance of this class with given associated scene instance first. + Then use this class provided member function to draw or fetch these properties. + The function located in this module should not be called directly! """ - @staticmethod - def get_rail_uv_material() -> bpy.types.Material: - return get_ptrprop_resolver().rail_uv_material - @staticmethod - def draw_rail_uv_material(layout: bpy.types.UILayout) -> None: - layout.prop(get_ptrprop_resolver(), 'rail_uv_material') + __mAssocScene: bpy.types.Scene - @staticmethod - def get_export_collection() -> bpy.types.Collection: - return get_ptrprop_resolver().export_collection - @staticmethod - def draw_export_collection(layout: bpy.types.UILayout) -> None: - layout.prop(get_ptrprop_resolver(), 'export_collection') + def __init__(self, assoc_scene: bpy.types.Scene): + self.__mAssocScene = assoc_scene - @staticmethod - def get_export_object() -> bpy.types.Object: - return get_ptrprop_resolver().export_object - @staticmethod - def draw_export_object(layout: bpy.types.UILayout) -> None: - layout.prop(get_ptrprop_resolver(), 'export_object') + def get_rail_uv_material(self) -> bpy.types.Material: + return get_ptrprop_resolver(self.__mAssocScene).rail_uv_material + def draw_rail_uv_material(self, layout: bpy.types.UILayout) -> None: + layout.prop(get_ptrprop_resolver(self.__mAssocScene), 'rail_uv_material') - @staticmethod - def get_ioport_encodings() -> tuple[str, ...]: - encodings = get_ioport_encodings() + def get_export_collection(self) -> bpy.types.Collection: + return get_ptrprop_resolver(self.__mAssocScene).export_collection + def draw_export_collection(self, layout: bpy.types.UILayout) -> None: + layout.prop(get_ptrprop_resolver(self.__mAssocScene), 'export_collection') + + def get_export_object(self) -> bpy.types.Object: + return get_ptrprop_resolver(self.__mAssocScene).export_object + def draw_export_object(self, layout: bpy.types.UILayout) -> None: + layout.prop(get_ptrprop_resolver(self.__mAssocScene), 'export_object') + + def get_ioport_encodings(self) -> tuple[str, ...]: + encodings = get_ioport_encodings(self.__mAssocScene) return tuple(i.encoding for i in encodings) - @staticmethod - def set_ioport_encodings(user_encodings: tuple[str, ...]) -> None: - encodings = get_ioport_encodings() - # clear and apply user encoding one by one - encodings.clear() - for user_encoding in user_encodings: - item = encodings.add() - item.encoding = user_encoding - @staticmethod - def draw_ioport_encodings(layout: bpy.types.UILayout) -> None: - target = get_ptrprop_resolver() + def draw_ioport_encodings(self, layout: bpy.types.UILayout) -> None: + target = get_ptrprop_resolver(self.__mAssocScene) row = layout.row() # draw main list @@ -212,11 +209,24 @@ class PtrPropResolver(): col.separator() col.operator(BBP_OT_clear_ioport_encodings.bl_idname, icon='TRASH', text='') +@bpy.app.handlers.persistent +def _ioport_encodings_initializer(file_path: str): + # if we can fetch property, and it is empty after loading file + # we fill it with default value + encodings = get_ioport_encodings(bpy.context.scene) + if len(encodings) == 0: + for default_enc in UTIL_virtools_types.g_PyBMapDefaultEncodings: + item = encodings.add() + item.encoding = default_enc + def register() -> None: bpy.utils.register_class(BBP_PG_bmap_encoding) bpy.utils.register_class(BBP_UL_bmap_encoding) bpy.utils.register_class(BBP_PG_ptrprop_resolver) + # register ioport encodings default value + bpy.app.handlers.load_post.append(_ioport_encodings_initializer) + bpy.utils.register_class(BBP_OT_add_ioport_encodings) bpy.utils.register_class(BBP_OT_rm_ioport_encodings) bpy.utils.register_class(BBP_OT_up_ioport_encodings) @@ -234,6 +244,9 @@ def unregister() -> None: bpy.utils.unregister_class(BBP_OT_rm_ioport_encodings) bpy.utils.unregister_class(BBP_OT_add_ioport_encodings) + # unregister ioport encodings default value + bpy.app.handlers.load_post.remove(_ioport_encodings_initializer) + bpy.utils.unregister_class(BBP_PG_ptrprop_resolver) bpy.utils.unregister_class(BBP_UL_bmap_encoding) bpy.utils.unregister_class(BBP_PG_bmap_encoding) diff --git a/bbp_ng/UTIL_ioport_shared.py b/bbp_ng/UTIL_ioport_shared.py index 418a47d..979ce47 100644 --- a/bbp_ng/UTIL_ioport_shared.py +++ b/bbp_ng/UTIL_ioport_shared.py @@ -242,7 +242,7 @@ class ExportParams(): ), ) # type: ignore - def draw_export_params(self, layout: bpy.types.UILayout) -> None: + def draw_export_params(self, context: bpy.types.Context, layout: bpy.types.UILayout) -> None: header: bpy.types.UILayout body: bpy.types.UILayout header, body = layout.panel("BBP_PT_ioport_shared_export_params", default_closed=False) @@ -255,22 +255,24 @@ class ExportParams(): horizon_body.prop(self, "export_mode", expand = True) # draw picker + ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene) if self.export_mode == 'COLLECTION': - PROP_ptrprop_resolver.PtrPropResolver.draw_export_collection(body) + ptrprops.draw_export_collection(body) elif self.export_mode == 'OBJECT': - PROP_ptrprop_resolver.PtrPropResolver.draw_export_object(body) + ptrprops.draw_export_object(body) - def general_get_export_objects(self) -> tuple[bpy.types.Object] | None: + def general_get_export_objects(self, context: bpy.types.Context) -> tuple[bpy.types.Object] | None: """ Return resolved exported objects or None if no selection. """ + ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene) if self.export_mode == 'COLLECTION': - col: bpy.types.Collection = PROP_ptrprop_resolver.PtrPropResolver.get_export_collection() + col: bpy.types.Collection = ptrprops.get_export_collection() if col is None: return None else: return tuple(col.all_objects) else: - obj: bpy.types.Object = PROP_ptrprop_resolver.PtrPropResolver.get_export_object() + obj: bpy.types.Object = ptrprops.get_export_object() if obj is None: return None else: return (obj, ) @@ -278,12 +280,6 @@ class ExportParams(): _g_EnumHelper_CK_TEXTURE_SAVEOPTIONS: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS) class VirtoolsParams(): - vt_encodings: bpy.props.StringProperty( - name = "Encodings", - description = "The encoding list used by Virtools engine to resolve object name. Use `;` to split multiple encodings", - default = UTIL_virtools_types.g_PyBMapDefaultEncoding - ) # type: ignore - texture_save_opt: bpy.props.EnumProperty( name = "Global Texture Save Options", description = "Decide how texture saved if texture is specified as Use Global as its Save Options.", @@ -304,7 +300,7 @@ class VirtoolsParams(): default = 5, ) # type: ignore - def draw_virtools_params(self, layout: bpy.types.UILayout, is_importer: bool) -> None: + def draw_virtools_params(self, context: bpy.types.Context, layout: bpy.types.UILayout, is_importer: bool) -> None: header: bpy.types.UILayout body: bpy.types.UILayout header, body = layout.panel("BBP_PT_ioport_shared_virtools_params", default_closed=False) @@ -313,8 +309,8 @@ class VirtoolsParams(): # draw encodings body.label(text = 'Encodings') - body.prop(self, 'vt_encodings', text = '') - PROP_ptrprop_resolver.PtrPropResolver.draw_ioport_encodings(body) + ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene) + ptrprops.draw_ioport_encodings(body) # following field are only valid in exporter if not is_importer: @@ -329,10 +325,10 @@ class VirtoolsParams(): body.prop(self, 'compress_level') - def general_get_vt_encodings(self) -> tuple[str]: - # get encoding, split it by `;` and strip blank chars. - encodings: str = self.vt_encodings - return tuple(map(lambda x: x.strip(), encodings.split(';'))) + def general_get_vt_encodings(self, context: bpy.types.Context) -> tuple[str, ...]: + # get from ptrprop resolver then filter empty item + ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene) + return tuple(filter(lambda encoding: len(encoding) != 0, ptrprops.get_ioport_encodings())) def general_get_texture_save_opt(self) -> UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS: return _g_EnumHelper_CK_TEXTURE_SAVEOPTIONS.get_selection(self.texture_save_opt) diff --git a/bbp_ng/UTIL_virtools_types.py b/bbp_ng/UTIL_virtools_types.py index c73a157..3c810dd 100644 --- a/bbp_ng/UTIL_virtools_types.py +++ b/bbp_ng/UTIL_virtools_types.py @@ -270,6 +270,9 @@ def virtools_name_regulator(name: str | None) -> str: # Since LibCmo 0.2, the encoding name of LibCmo become universal encoding which is platfoorm independent. # So no need set it according to different platform. # Use universal encoding name (like Python). -g_PyBMapDefaultEncoding: str = 'cp1252;gb2312' +g_PyBMapDefaultEncodings: tuple[str, ...] = ( + 'cp1252', + 'gb2312' +) #endregion