diff --git a/bbp_ng/PROP_virtools_group.py b/bbp_ng/PROP_virtools_group.py index 3d7dbfb..1df0edb 100644 --- a/bbp_ng/PROP_virtools_group.py +++ b/bbp_ng/PROP_virtools_group.py @@ -1,6 +1,6 @@ import bpy import typing, enum -from . import UTIL_functions +from . import UTIL_functions, UTIL_icons_manager #region Virtools Groups Define & Help Class @@ -198,6 +198,35 @@ class VirtoolsGroupsPreset(enum.Enum): _g_VtGrpPresetValues: tuple[str] = tuple(map(lambda x: x.value, VirtoolsGroupsPreset)) +## Some of group names are not matched with icon name +# So we create a convertion map to convert them. +_g_GroupIconNameConvMap: dict[str, str] = { + "PS_Levelstart": "PS_FourFlames", + "PE_Levelende": "PE_Balloon", + "PC_Checkpoints": "PC_TwoFlames", + "PR_Resetpoints": "PR_Resetpoint", + + "Sound_HitID_01": "SoundID_01", + "Sound_RollID_01": "SoundID_01", + "Sound_HitID_02": "SoundID_02", + "Sound_RollID_02": "SoundID_02", + "Sound_HitID_03": "SoundID_03", + "Sound_RollID_03": "SoundID_03" +} +def _get_group_icon_by_name(gp_name: str) -> int: + # try converting group name + # if not found, return self + gp_name = _g_GroupIconNameConvMap.get(gp_name, gp_name) + + # get from extra group icon first + value: int | None = UTIL_icons_manager.get_group_icon(gp_name) + if value is not None: return value + + # if failed, get from element. if still failed, return empty icon + value = UTIL_icons_manager.get_element_icon(gp_name) + if value is not None: return value + else: return UTIL_icons_manager.get_empty_icon() + class SharedGroupNameInputProperties(): group_name_source: bpy.props.EnumProperty( name = "Group Name Source", @@ -212,7 +241,7 @@ class SharedGroupNameInputProperties(): description="Pick vanilla Ballance group name.", items=tuple( # token, display name, descriptions, icon, index - (str(idx), grp, "", "", idx) for idx, grp in enumerate(_g_VtGrpPresetValues) + (str(idx), grp, "", _get_group_icon_by_name(grp), idx) for idx, grp in enumerate(_g_VtGrpPresetValues) ), ) diff --git a/bbp_ng/UTIL_icons_manager.py b/bbp_ng/UTIL_icons_manager.py index 3d366e1..a067234 100644 --- a/bbp_ng/UTIL_icons_manager.py +++ b/bbp_ng/UTIL_icons_manager.py @@ -1,7 +1,113 @@ -import bpy -import os, enum +import bpy, bpy.utils.previews +import os, enum, typing class BlenderPresetIcons(enum.Enum): Info = 'INFO' Warning = 'ERROR' Error = 'CANCEL' + +#region Custom Icons Helper + +_g_SupportedImageExts: set[str] = ( + '.png', +) + +_g_IconsManager: bpy.utils.previews.ImagePreviewCollection | None = None + +_g_EmptyIcon: int = 0 +_g_IconPrefix: str = "BlcBldPlg_" + +_g_FloorIconsMap: dict[str, int] = {} +_g_FloorIconPrefix: str = _g_IconPrefix + 'Floor_' +_g_ElementIconsMap: dict[str, int] = {} +_g_ElementIconPrefix: str = _g_IconPrefix + 'Element_' +_g_GroupIconsMap: dict[str, int] = {} +_g_GroupIconPrefix: str = _g_IconPrefix + 'Group_' + +def _iterate_folder_images(folder: str) -> typing.Iterator[tuple[str, str]]: + for name in os.listdir(folder): + # check whether it is file + filepath: str = os.path.join(folder, name) + if os.path.isfile(filepath): + # check file exts + (root, ext) = os.path.splitext(name) + if ext.lower() in _g_SupportedImageExts: + yield (filepath, root) + +def _load_image_folder( + folder: str, + loader: bpy.utils.previews.ImagePreviewCollection, + container: dict[str, int], + name_prefix: str) -> None: + # iterate folder + for (filepath, filename_no_ext) in _iterate_folder_images(folder): + # generate name for unique + icon_name: str = name_prefix + filename_no_ext + # load it + loader.load(icon_name, filepath, 'IMAGE') + # add into list. use plain name (not the unique name) + container[filename_no_ext] = loader[icon_name].icon_id + +#endregion + +#region Custom Icons Visitors + +def get_empty_icon() -> int: + return _g_EmptyIcon + +def get_floor_icon(name: str) -> int | None: + return _g_FloorIconsMap.get(name, None) + +def get_element_icon(name: str) -> int | None: + return _g_ElementIconsMap.get(name, None) + +def get_group_icon(name: str) -> int | None: + return _g_GroupIconsMap.get(name, None) + +#endregion + +def register(): + global _g_IconsManager + global _g_EmptyIcon + global _g_FloorIconsMap, _g_ElementIconsMap, _g_GroupIconsMap + + # create preview collection and get icon folder + icons_folder: str = os.path.join(os.path.dirname(__file__), "icons") + _g_IconsManager = bpy.utils.previews.new() + + # load empty icon as default fallback + empty_icon_name: str = _g_IconPrefix + 'EmptyIcon' + _g_IconsManager.load(empty_icon_name, os.path.join(icons_folder, "Empty.png"), 'IMAGE') + _g_EmptyIcon = _g_IconsManager[empty_icon_name].icon_id + + # load floor, element, group icon + _load_image_folder( + os.path.join(icons_folder, 'floor'), + _g_IconsManager, + _g_FloorIconsMap, + _g_FloorIconPrefix + ) + _load_image_folder( + os.path.join(icons_folder, 'element'), + _g_IconsManager, + _g_ElementIconsMap, + _g_ElementIconPrefix + ) + _load_image_folder( + os.path.join(icons_folder, 'group'), + _g_IconsManager, + _g_GroupIconsMap, + _g_GroupIconPrefix + ) + +def unregister(): + global _g_IconsManager + global _g_EmptyIcon + global _g_FloorIconsMap, _g_ElementIconsMap, _g_GroupIconsMap + + bpy.utils.previews.remove(_g_IconsManager) + _g_IconsManager = None + + _g_FloorIconsMap.clear() + _g_ElementIconsMap.clear() + _g_GroupIconsMap.clear() diff --git a/bbp_ng/__init__.py b/bbp_ng/__init__.py index 7f1adf6..7ce29c5 100644 --- a/bbp_ng/__init__.py +++ b/bbp_ng/__init__.py @@ -23,6 +23,12 @@ if "bpy" in locals(): #endregion +# we must load icons manager first +# and register it +from . import UTIL_icons_manager +UTIL_icons_manager.register() + +# then load other modules 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 diff --git a/bbp_ng/raw_icons/group/SoundID_03.png b/bbp_ng/raw_icons/group/SoundID_03.png index bd2f4bf..46c2721 100644 Binary files a/bbp_ng/raw_icons/group/SoundID_03.png and b/bbp_ng/raw_icons/group/SoundID_03.png differ