update plugin.

- sync PyBMap work. use different library name in different OS.
- add BMap encoding default value according to different OS (Windows and non-Windows) because non-Windows OS, we use libiconv as encoding converter.
- move all pointer properties to a single module and give corresponding visitor.
- add shared importer exporter parameters module thus bmfile import/export also can ref it.
This commit is contained in:
yyc12345 2023-11-16 22:41:03 +08:00
parent 59a1275f68
commit 1a2dd08092
8 changed files with 273 additions and 48 deletions

View File

@ -1,8 +1,11 @@
import bpy
from . import PROP_preferences, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh
from bpy_extras.wm_utils.progress_report import ProgressReport
import tempfile, os, typing
from . import PROP_preferences, PROP_ptrprop_resolver, UTIL_ioport_shared
from . import UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_icons_manager
from .PyBMap import bmap_wrapper as bmap
class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtoolsFile):
class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtoolsFile, UTIL_ioport_shared.ExportParams, UTIL_ioport_shared.VirtoolsParams):
"""Export Virtools File"""
bl_idname = "bbp.export_virtools"
bl_label = "Export Virtools File"
@ -15,13 +18,51 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
and bmap.is_bmap_available())
def execute(self, context):
UTIL_functions.message_box((self.general_get_filename(), ), 'Export Virtools File Path', 'INFO')
# check selecting first
objls: tuple[bpy.types.Object] | None = self.general_get_export_objects()
if objls is None:
UTIL_functions.message_box(
('No selected target!', ),
'Lost Parameters',
UTIL_icons_manager.BlenderPresetIcons.Error.value
)
return {'CANCELLED'}
# start exporting
with UTIL_ioport_shared.ExportEditModeBackup() as editmode_guard:
_export_virtools(
self.general_get_filename(),
self.general_get_vt_encodings(),
objls
)
self.report({'INFO'}, "Virtools File Exporting Finished.")
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.label(text = 'Export Target')
self.draw_export_params(layout)
layout.separator()
layout.label(text = 'Virtools Params')
self.draw_virtools_params(layout)
def _export_virtools(file_name_: str, encodings_: tuple[str], export_objects: tuple[bpy.types.Object]) -> None:
# create temp folder
with tempfile.TemporaryDirectory() as vt_temp_folder:
print(f'Virtools Engine Temp: {vt_temp_folder}')
# create virtools reader context
with bmap.BMFileWriter(
vt_temp_folder,
PROP_preferences.get_raw_preferences().mBallanceTextureFolder,
encodings_) as writer:
# prepare progress reporter
with ProgressReport(wm = bpy.context.window_manager) as progress:
pass
def register() -> None:
bpy.utils.register_class(BBP_OT_export_virtools)

View File

@ -1,23 +1,17 @@
import bpy
from bpy_extras.wm_utils.progress_report import ProgressReport
import tempfile, os, typing
from . import PROP_preferences
from . import PROP_preferences, UTIL_ioport_shared
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh
from . import PROP_ballance_element, PROP_virtools_group, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh
from .PyBMap import bmap_wrapper as bmap
class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtoolsFile):
class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtoolsFile, UTIL_ioport_shared.ImportParams, UTIL_ioport_shared.VirtoolsParams):
"""Import Virtools File"""
bl_idname = "bbp.import_virtools"
bl_label = "Import Virtools File"
bl_options = {'PRESET', 'UNDO'}
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 = "1252"
)
@classmethod
def poll(self, context):
return (
@ -25,15 +19,21 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool
and bmap.is_bmap_available())
def execute(self, context):
# get encoding, split it by `;` and strip blank chars.
encodings: str = self.vt_encodings
_import_virtools(
self.general_get_filename(),
tuple(map(lambda x: x.strip(), encodings.split(';')))
self.general_get_vt_encodings()
)
self.report({'INFO'}, "Virtools File Importing Finished.")
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.label(text = 'Conflict Options')
self.draw_import_params(layout)
layout.separator()
layout.label(text = 'Virtools Params')
self.draw_virtools_params(layout)
def _import_virtools(file_name_: str, encodings_: tuple[str]) -> None:
# create temp folder
with tempfile.TemporaryDirectory() as vt_temp_folder:

View File

@ -1,27 +1,8 @@
import bpy, bmesh, mathutils
import typing
from . import PROP_ptrprop_resolver
from . import UTIL_virtools_types, UTIL_icons_manager, UTIL_functions
#region Material Pointer Property Resolver
class BBP_PG_patch_rail_uv(bpy.types.PropertyGroup):
rail_mtl: bpy.props.PointerProperty(
name = "Material",
description = "The material used for rail",
type = bpy.types.Material,
)
def get_rail_uv_patch() -> BBP_PG_patch_rail_uv:
return bpy.context.scene.bbp_patch_rail_uv
def get_raw_rail_uv_patch() -> bpy.types.Material:
return get_rail_uv_patch().rail_mtl
def draw_rail_uv_patch(layout: bpy.types.UILayout) -> None:
layout.prop(get_rail_uv_patch(), 'rail_mtl')
#endregion
class BBP_OT_rail_uv(bpy.types.Operator):
"""Create UV for Rail as Ballance Showen (TT_ReflectionMapping)"""
bl_idname = "bbp.rail_uv"
@ -38,7 +19,7 @@ class BBP_OT_rail_uv(bpy.types.Operator):
def execute(self, context):
# check material
mtl: bpy.types.Material = get_raw_rail_uv_patch()
mtl: bpy.types.Material = PROP_ptrprop_resolver.get_rail_uv_material()
if mtl is None:
UTIL_functions.message_box(
("No specific material", ),
@ -53,7 +34,7 @@ class BBP_OT_rail_uv(bpy.types.Operator):
def draw(self, context):
layout: bpy.types.UILayout = self.layout
draw_rail_uv_patch(layout)
PROP_ptrprop_resolver.draw_rail_uv_material(layout)
#region Real Worker Functions
@ -200,14 +181,8 @@ def _create_rail_uv(meshes: typing.Iterable[bpy.types.Mesh], mtl: bpy.types.Mate
#endregion
def register() -> None:
# register patch first
bpy.utils.register_class(BBP_PG_patch_rail_uv)
bpy.types.Scene.bbp_patch_rail_uv = bpy.props.PointerProperty(type = BBP_PG_patch_rail_uv)
bpy.utils.register_class(BBP_OT_rail_uv)
def unregister() -> None:
del bpy.types.Scene.bbp_patch_rail_uv
bpy.utils.unregister_class(BBP_OT_rail_uv)

View File

@ -0,0 +1,51 @@
import bpy
## Intent
# Operator is not allowed to register Pointer Properties.
# The solution is register pointer properties in Scene and reference it when drawing operator window.
# This module contains all pointer properties used by other operators.
class BBP_PG_ptrprop_resolver(bpy.types.PropertyGroup):
rail_uv_material: bpy.props.PointerProperty(
name = "Material",
description = "The material used for rail",
type = bpy.types.Material,
)
export_collection: bpy.props.PointerProperty(
type = bpy.types.Collection,
name = "Collection",
description = "The collection exported. Nested collections allowed."
)
export_object: bpy.props.PointerProperty(
type = bpy.types.Object,
name = "Object",
description = "The object exported"
)
def get_ptrprop_resolver() -> BBP_PG_ptrprop_resolver:
return bpy.context.scene.bbp_ptrprop_resolver
def get_rail_uv_material() -> bpy.types.Material:
return get_ptrprop_resolver().rail_uv_material
def draw_rail_uv_material(layout: bpy.types.UILayout) -> None:
layout.prop(get_ptrprop_resolver(), 'rail_uv_material')
def get_export_collection() -> bpy.types.Collection:
return get_ptrprop_resolver().export_collection
def draw_export_collection(layout: bpy.types.UILayout) -> None:
layout.prop(get_ptrprop_resolver(), 'export_collection')
def get_export_object() -> bpy.types.Object:
return get_ptrprop_resolver().export_object
def draw_export_object(layout: bpy.types.UILayout) -> None:
layout.prop(get_ptrprop_resolver(), 'export_object')
def register():
bpy.utils.register_class(BBP_PG_ptrprop_resolver)
bpy.types.Scene.bbp_ptrprop_resolver = bpy.props.PointerProperty(type = BBP_PG_ptrprop_resolver)
def unregister():
del bpy.types.Scene.bbp_ptrprop_resolver
bpy.utils.unregister_class(BBP_PG_ptrprop_resolver)

View File

@ -1,4 +1,4 @@
import ctypes, os
import ctypes, os, sys
#region Type Defines
@ -67,10 +67,21 @@ bm_VxMatrix_p = ctypes.POINTER(bm_VxMatrix)
#region BMap Loader
_g_BMapLibName: str
if sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
_g_BMapLibName = "BMap.dll"
elif sys.platform.startswith('linux') or sys.platform.startswith('freebsd'):
_g_BMapLibName = "BMap.so"
elif sys.platform.startswith('darwin'):
_g_BMapLibName = "BMap.dylib"
else:
_g_BMapLibName = "BMap.bin"
_g_BMapModule: ctypes.CDLL = None
try:
_g_BMapModule = ctypes.cdll.LoadLibrary(
os.path.join(os.path.dirname(__file__), "BMap.dll")
os.path.join(os.path.dirname(__file__), _g_BMapLibName)
)
except:
_g_BMapModule = None

View File

@ -1,5 +1,5 @@
import bpy
import math, typing, enum
import math, typing, enum, sys
class BBPException(Exception):
"""
@ -84,5 +84,18 @@ def generate_vt_enums_for_bl_enumprop(enum_data: type[InheritingIntEnum_t], anno
(str(member.value), get_display_name(member.value, member.name), get_description(member.value, ""), "", member.value) for member in enum_data
)
#endregion
#region Default Encoding of BMap
# Use semicolon split each encodings. Support Western European and Simplified Chinese in default.
g_PyBMapDefaultEncoding: str
if sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
# See: https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
g_PyBMapDefaultEncoding = "1252;936"
else:
# See: https://www.gnu.org/software/libiconv/
g_PyBMapDefaultEncoding = "CP1252;CP936"
#endregion

View File

@ -0,0 +1,130 @@
import bpy
import enum
from . import UTIL_functions
from . import PROP_ptrprop_resolver
## Intent
# Some importer or exporter may share same properties.
# So we create some shared class and user just need inherit them
# and call general getter to get user selected data.
# Also provide draw function thus caller do not need draw the params themselves.
class ImportParams():
texture_conflict_strategy: bpy.props.EnumProperty(
name = "Texture name conflict",
items = (
('NEW', "New Instance", "Create a new instance"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process texture name conflict",
default = 'CURRENT',
)
material_conflict_strategy: bpy.props.EnumProperty(
name = "Material name conflict",
items = (
('RENAME', "Rename", "Rename the new one"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process material name conflict",
default = 'RENAME',
)
mesh_conflict_strategy: bpy.props.EnumProperty(
name = "Mesh name conflict",
items = (
('RENAME', "Rename", "Rename the new one"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process mesh name conflict",
default = 'RENAME',
)
object_conflict_strategy: bpy.props.EnumProperty(
name = "Object name conflict",
items = (
('RENAME', "Rename", "Rename the new one"),
('CURRENT', "Use Current", "Use current one"),
),
description = "Define how to process object name conflict",
default = 'RENAME',
)
def draw_import_params(self, layout: bpy.types.UILayout) -> None:
layout.prop(self, 'object_conflict_strategy')
layout.prop(self, 'mesh_conflict_strategy')
layout.prop(self, 'material_conflict_strategy')
layout.prop(self, 'texture_conflict_strategy')
class ExportParams():
export_mode: bpy.props.EnumProperty(
name = "Export Mode",
items = (
('COLLECTION', "Collection", "Export a collection"),
('OBJECT', "Object", "Export an object"),
),
)
def draw_export_params(self, layout: bpy.types.UILayout) -> None:
# draw switch
layout.prop(self, "export_mode", expand = True)
# draw picker
if self.export_mode == 'COLLECTION':
PROP_ptrprop_resolver.draw_export_collection(layout)
elif self.export_mode == 'OBJECT':
PROP_ptrprop_resolver.draw_export_object(layout)
def general_get_export_objects(self) -> tuple[bpy.types.Object] | None:
"""
Return resolved exported objects or None if no selection.
"""
if self.export_mode == 'COLLECTION':
col: bpy.types.Collection = PROP_ptrprop_resolver.get_export_collection()
if col is None: return None
else:
return tuple(col.all_objects)
else:
obj: bpy.types.Object = PROP_ptrprop_resolver.get_export_object()
if obj is None: return None
else: return (obj, )
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_functions.g_PyBMapDefaultEncoding
)
def draw_virtools_params(self, layout: bpy.types.UILayout) -> None:
layout.prop(self, 'vt_encodings')
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(';')))
class ExportEditModeBackup():
"""
The class which save Edit Mode when exporting and restore it after exporting.
Because edit mode is not allowed when exporting.
Support `with` statement.
"""
mInEditMode: bool
def __init__(self):
if bpy.context.object and bpy.context.object.mode == "EDIT":
# set and toggle it. otherwise exporting will failed.
self.mInEditMode = True
bpy.ops.object.editmode_toggle()
else:
self.mInEditMode = False
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.mInEditMode:
bpy.ops.object.editmode_toggle()
self.mInEditMode = False

View File

@ -23,7 +23,7 @@ if "bpy" in locals():
#endregion
from . import PROP_preferences, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_ballance_element, PROP_virtools_group
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_ballance_element, PROP_virtools_group
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
from . import OP_UV_flatten_uv, OP_UV_rail_uv
@ -85,6 +85,8 @@ g_BldMenus: tuple[MenuEntry, ...] = (
def register() -> None:
# register module
PROP_preferences.register()
PROP_ptrprop_resolver.register()
PROP_virtools_material.register()
PROP_virtools_texture.register()
PROP_virtools_mesh.register()
@ -133,6 +135,8 @@ def unregister() -> None:
PROP_virtools_mesh.unregister()
PROP_virtools_texture.unregister()
PROP_virtools_material.unregister()
PROP_ptrprop_resolver.unregister()
PROP_preferences.unregister()
if __name__ == "__main__":