Plugin Registration Changes

- Re-wrote how the plugin registers itself.
- No longer need @register_wrapper classes get auto detected and added.
- The new Auto loader is much better then the old way, no longer need "if "bpy" not in locals():" this was an old way of doing things and wasn't really efficient.

 using auto_load.py provides several advantages:

- It automatically discovers and loads all modules in the addon.
- It handles dependencies between classes correctly through topological sorting.
- It manages registration order automatically.
- It properly handles unregistration in the correct order.

This approach is much less error prone and I not had any issues so far. However it still needs testing fully.

I have also start to re-organise files into folders as well, this is going to be needed so we don't have a long list of files as Avatar Toolkit is getting larger then i originally planned.
This commit is contained in:
Yusarina
2024-12-02 01:52:11 +00:00
parent ac6e98c27e
commit fe8f5f69d5
40 changed files with 581 additions and 580 deletions
+9 -51
View File
@@ -1,55 +1,13 @@
if "bpy" not in locals(): modules = None
import bpy ordered_classes = None
from . import ui
from . import core
from . import functions
from .core import register
from .core.register import __bl_ordered_classes
from .core import properties
from .core import addon_preferences
from .core.updater import check_for_update_on_start
else:
import importlib
importlib.reload(ui)
importlib.reload(core)
importlib.reload(functions)
importlib.reload(properties)
importlib.reload(addon_preferences)
def register(): def register():
print("Registering Avatar Toolkit") from .core import auto_load
# Register the addon properties print("Starting registration")
properties.register() auto_load.init()
auto_load.register()
# Load the translations print("Registration complete")
functions.translations.load_translations()
# Order the classes before registration
core.register.order_classes()
# Register the UI classes
for cls in core.register.__bl_ordered_classes:
print("registering " + str(cls))
bpy.utils.register_class(cls)
#finally register properties that may use some classes.
core.register.register_properties()
bpy.app.handlers.load_post.append(check_for_update_on_start)
from .functions.mesh_tools import AvatarToolkit_OT_ApplyShapeKey
bpy.types.MESH_MT_shape_key_context_menu.append((lambda self, context: self.layout.separator()))
bpy.types.MESH_MT_shape_key_context_menu.append((lambda self, context: self.layout.operator(AvatarToolkit_OT_ApplyShapeKey.bl_idname, icon="KEY_HLT")))
def unregister(): def unregister():
print("Unregistering Avatar Toolkit") from .core import auto_load
# Unregister the UI classes auto_load.unregister()
if check_for_update_on_start in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(check_for_update_on_start)
# Iterate over the classes to unregister in reverse order and unregister them
for cls in reversed(list(__bl_ordered_classes)):
bpy.utils.unregister_class(cls)
print("unregistering " + str(cls))
core.register.unregister_properties()
properties.unregister()
-20
View File
@@ -1,20 +0,0 @@
# core/__init__.py
from .register import register_wrap
#to reload all things in this directory and import them properly - @989onan
if "bpy" not in locals():
import bpy
import glob
import os
from os.path import dirname, basename, isfile, join
modules = glob.glob(join(dirname(__file__), "*.py"))
for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]:
exec("from . import "+module_name)
print("importing " +module_name)
else:
import importlib
modules = glob.glob(join(dirname(__file__), "*.py"))
for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]:
exec("importlib.reload("+module_name+")")
print("reloading " +module_name)
+172
View File
@@ -0,0 +1,172 @@
import os
import bpy
import sys
import typing
import inspect
import pkgutil
import tomllib
import importlib
from pathlib import Path
__all__ = (
"init",
"register",
"unregister",
)
modules = None
ordered_classes = None
def init():
global modules
global ordered_classes
print("Auto-load init starting")
modules = get_all_submodules(Path(__file__).parent.parent)
ordered_classes = get_ordered_classes_to_register(modules)
print(f"Found modules: {modules}")
print(f"Found classes: {ordered_classes}")
def register():
print("Registering classes")
for cls in ordered_classes:
print(f"Registering: {cls}")
try:
bpy.utils.register_class(cls)
except ValueError:
continue
for module in modules:
if module.__name__ == __name__:
continue
if hasattr(module, "register"):
module.register()
def unregister():
for cls in reversed(ordered_classes):
bpy.utils.unregister_class(cls)
for module in modules:
if module.__name__ == __name__:
continue
if hasattr(module, "unregister"):
module.unregister()
def get_manifest_id():
manifest_path = Path(__file__).parent.parent / "blender_manifest.toml"
with open(manifest_path, "rb") as f:
manifest = tomllib.load(f)
return manifest["id"]
def get_all_submodules(directory):
modules = []
addon_id = get_manifest_id()
for root, dirs, files in os.walk(directory):
if "__pycache__" in root:
continue
path = Path(root)
if path == directory:
package_name = f"bl_ext.user_default.{addon_id}"
else:
relative_path = path.relative_to(directory).as_posix().replace('/', '.')
package_name = f"bl_ext.user_default.{addon_id}.{relative_path}"
for name in sorted(iter_module_names(path)):
modules.append(importlib.import_module(f".{name}", package_name))
return modules
def iter_submodules(path, package_name):
for name in sorted(iter_module_names(path)):
yield importlib.import_module("." + name, package_name)
def iter_module_names(path):
print(f"Scanning path: {path}") # Debug path
modules_list = list(pkgutil.iter_modules([str(path)]))
print(f"Found these modules: {modules_list}") # Debug modules
for _, module_name, is_pkg in modules_list:
if not is_pkg:
print(f"Found module: {module_name}")
yield module_name
def get_ordered_classes_to_register(modules):
return toposort(get_register_deps_dict(modules))
def get_register_deps_dict(modules):
deps_dict = {}
classes_to_register = set(iter_classes_to_register(modules))
for cls in classes_to_register:
deps_dict[cls] = set(iter_own_register_deps(cls, classes_to_register))
return deps_dict
def iter_own_register_deps(cls, classes_to_register):
yield from (dep for dep in iter_register_deps(cls) if dep in classes_to_register)
def iter_register_deps(cls):
for value in typing.get_type_hints(cls, {}, {}).values():
dependency = get_dependency_from_annotation(value)
if dependency is not None:
yield dependency
def get_dependency_from_annotation(value):
if isinstance(value, tuple) and len(value) == 2:
if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
return value[1]["type"]
return None
def iter_classes_to_register(modules):
base_types = get_register_base_types()
for cls in get_classes_in_modules(modules):
if any(base in base_types for base in cls.__bases__):
if not getattr(cls, "_is_registered", False):
yield cls
def get_classes_in_modules(modules):
classes = set()
for module in modules:
for cls in iter_classes_in_module(module):
classes.add(cls)
return classes
def iter_classes_in_module(module):
for value in module.__dict__.values():
if inspect.isclass(value):
yield value
def get_register_base_types():
return set(getattr(bpy.types, name) for name in [
"Panel", "Operator", "PropertyGroup",
"AddonPreferences", "Header", "Menu",
"Node", "NodeSocket", "NodeTree",
"UIList", "RenderEngine"
])
def toposort(deps_dict):
sorted_list = []
sorted_values = set()
# First pass: Register panels without parents
panels_to_sort = [(value, deps) for value, deps in deps_dict.items()
if hasattr(value, 'bl_parent_id')]
base_panels = [(value, deps) for value, deps in deps_dict.items()
if not hasattr(value, 'bl_parent_id')]
# Add base panels first
for value, deps in base_panels:
if len(deps) == 0:
sorted_list.append(value)
sorted_values.add(value)
# Then add child panels
while len(deps_dict) > len(sorted_values):
unsorted = []
for value, deps in deps_dict.items():
if value not in sorted_values:
if len(deps - sorted_values) == 0:
sorted_list.append(value)
sorted_values.add(value)
else:
unsorted.append(value)
return sorted_list
-1
View File
@@ -6,7 +6,6 @@ import time
import webbrowser import webbrowser
import typing import typing
from ..core.register import register_wrap
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from bpy.types import Object, ShapeKey, Mesh, Context, Material, PropertyGroup from bpy.types import Object, ShapeKey, Mesh, Context, Material, PropertyGroup
from functools import lru_cache from functools import lru_cache
@@ -1,14 +1,10 @@
import bpy import bpy
from typing import List, Optional from typing import List, Optional
from .common import get_armature from ...core.common import get_armature
from bpy.types import Object, ShapeKey, Mesh, Context, Operator from bpy.types import Object, ShapeKey, Mesh, Context, Operator
from functools import lru_cache from functools import lru_cache
from ..core.register import register_wrap from ...core.translations import t
from ..functions.translations import t
@register_wrap
class AvatarToolKit_OT_ExportResonite(Operator): class AvatarToolKit_OT_ExportResonite(Operator):
bl_idname = 'avatar_toolkit.export_resonite' bl_idname = 'avatar_toolkit.export_resonite'
bl_label = t("Importer.export_resonite.label") bl_label = t("Importer.export_resonite.label")
-3
View File
@@ -1,3 +0,0 @@
{
"language": 0
}
+148 -137
View File
@@ -1,169 +1,180 @@
import bpy import bpy
from ..functions.translations import t, get_languages_list, update_language from .translations import t, get_languages_list, update_language
from ..core.register import register_property from bpy.types import PropertyGroup, Material, Scene, Object, Context
from bpy.types import Scene, Object, Material, Context from bpy.props import (StringProperty, BoolProperty, EnumProperty,
from bpy.props import BoolProperty, EnumProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty IntProperty, FloatProperty, CollectionProperty,
from ..core.addon_preferences import get_preference PointerProperty)
from ..core.common import SceneMatClass, MaterialListBool, get_armatures, get_mesh_items, get_armatures_that_are_not_selected from .addon_preferences import get_preference
from .common import SceneMatClass, MaterialListBool, get_armatures, get_mesh_items, get_armatures_that_are_not_selected
from .updater import get_version_list from .updater import get_version_list
def register() -> None: class AvatarToolkitSceneProperties(PropertyGroup):
default_language = get_preference("language", 0) language: EnumProperty(
register_property((bpy.types.Scene, "avatar_toolkit_language", bpy.props.EnumProperty( name="Language",
name=t("Settings.language.label", "Language"), description="Select the language for the addon",
description=t("Settings.language.desc", "Select the language for the addon"),
items=get_languages_list, items=get_languages_list,
default=default_language,
update=update_language update=update_language
))) )
register_property((bpy.types.Scene, "selected_mesh", bpy.props.EnumProperty( selected_mesh: EnumProperty(
items=get_mesh_items, items=get_mesh_items,
name=t("VisemePanel.selected_mesh.label"), name="Selected Mesh",
description=t("VisemePanel.selected_mesh.desc") description="Select mesh to modify"
))) )
register_property((bpy.types.Object, "material_group_expanded", bpy.props.BoolProperty( material_search_filter: StringProperty(
name="Expand Material Group",
description="Show/hide materials for this mesh",
default=False
)))
register_property((bpy.types.Material, "material_expanded", bpy.props.BoolProperty(
name="Expand Material",
description="Show/hide material properties",
default=False
)))
register_property((bpy.types.Scene, "material_search_filter", bpy.props.StringProperty(
name="Search Materials", name="Search Materials",
description="Filter materials by name", description="Filter materials by name",
default="" default=""
))) )
register_property((bpy.types.Material, "include_in_atlas", bpy.props.BoolProperty( merge_armature_apply_transforms: BoolProperty(
name=t("TextureAtlas.include_in_atlas"),
description=t("TextureAtlas.include_in_atlas_desc"),
default=True
)))
register_property((bpy.types.Scene, "merge_armature_apply_transforms", bpy.props.BoolProperty(
default=False, default=False,
name=t("MergeArmature.merge_armatures.apply_transforms.label"), name="Apply Transforms",
description=t("MergeArmature.merge_armatures.apply_transforms.desc") description="Apply transforms when merging armatures"
))) )
register_property((bpy.types.Scene, "merge_armature_align_bones", bpy.props.BoolProperty(
merge_armature_align_bones: BoolProperty(
default=False, default=False,
name=t("MergeArmature.merge_armatures.align_bones.label"), name="Align Bones",
description=t("MergeArmature.merge_armatures.align_bones.desc") description="Align bones when merging armatures"
))) )
register_property((bpy.types.Scene, "avatar_toolkit_language_changed", bpy.props.BoolProperty(default=False))) progress_steps: IntProperty(default=0)
progress_current: IntProperty(default=0)
language_changed: BoolProperty(default=False)
register_property((bpy.types.Scene, "avatar_toolkit_progress_steps", bpy.props.IntProperty(default=0))) mouth_a: StringProperty(
register_property((bpy.types.Scene, "avatar_toolkit_progress_current", bpy.props.IntProperty(default=0))) name="Mouth A",
description="Shape key for A sound"
)
register_property((bpy.types.Scene, "avatar_toolkit_mouth_a", bpy.props.StringProperty( mouth_o: StringProperty(
name=t("VisemePanel.mouth_a.label"), name="Mouth O",
description=t("VisemePanel.mouth_a.desc") description="Shape key for O sound"
))) )
register_property((bpy.types.Scene, "avatar_toolkit_mouth_o", bpy.props.StringProperty(
name=t("VisemePanel.mouth_o.label"), mouth_ch: StringProperty(
description=t("VisemePanel.mouth_o.desc") name="Mouth CH",
))) description="Shape key for CH sound"
register_property((bpy.types.Scene, "avatar_toolkit_mouth_ch", bpy.props.StringProperty( )
name=t("VisemePanel.mouth_ch.label"),
description=t("VisemePanel.mouth_ch.desc") shape_intensity: FloatProperty(
))) name="Shape Intensity",
register_property((bpy.types.Scene, "avatar_toolkit_shape_intensity", bpy.props.FloatProperty( description="Intensity of shape key modifications",
name=t("VisemePanel.shape_intensity"),
description=t("VisemePanel.shape_intensity_desc"),
default=1.0, default=1.0,
min=0.0, min=0.0,
max=2.0 max=2.0
))) )
register_property((bpy.types.Scene, "merge_twist_bones", bpy.props.BoolProperty(
name=t("Tools.merge_twist_bones.label"), merge_twist_bones: BoolProperty(
description=t("Tools.merge_twist_bones.desc"), name="Merge Twist Bones",
description="Merge twist bones during processing",
default=True default=True
))) )
register_property((bpy.types.Scene, "selected_armature", bpy.props.EnumProperty( selected_armature: EnumProperty(
items=get_armatures, items=get_armatures,
name=t("Quick_Access.selected_armature.label"), name="Selected Armature",
description=t("Quick_Access.selected_armature.desc"), description="Select the armature to work with"
default=0 )
)))
register_property((bpy.types.Scene, "merge_armature_source", bpy.props.EnumProperty( merge_armature_source: EnumProperty(
items=get_armatures_that_are_not_selected, items=get_armatures_that_are_not_selected,
name=t("MergeArmatures.selected_armature.label"), name="Source Armature",
description=t("MergeArmatures.selected_armature.label"), description="Select the source armature for merging"
default=0 )
)))
register_property((bpy.types.Scene, "avatar_toolkit_updater_version_list", bpy.props.EnumProperty( texture_atlas_material_index: IntProperty(
name=t('Scene.avatar_toolkit_updater_version_list.name'),
description=t('Scene.avatar_toolkit_updater_version_list.description'),
items=get_version_list
)))
#happy with how compressed this get_texture_node_list method is - @989onan
def get_texture_node_list(self: Material, context: Context) -> list[set[3]]:
if self.use_nodes:
Object.Enum = [((i.image.name if i.image else i.name+"_image"),(i.image.name if i.image else "node with no image..."),(i.image.name if i.image else i.name),index+1) for index,i in enumerate(self.node_tree.nodes) if i.bl_idname == "ShaderNodeTexImage"]
if not len(Object.Enum):
Object.Enum = [(t("TextureAtlas.error.label"), t("TextureAtlas.no_images_error.desc") , t("TextureAtlas.error.label"), 0)]
else:
Object.Enum = [(t("TextureAtlas.error.label"), t("TextureAtlas.no_nodes_error.desc"), t("TextureAtlas.error.label"), 0)]
Object.Enum.append((t("TextureAtlas.none.label"), t("TextureAtlas.none.label"), t("TextureAtlas.none.label"), 0))
return Object.Enum
register_property((Material, "texture_atlas_albedo", EnumProperty(
name=t("TextureAtlas.albedo"),
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.albedo").lower()),
default=0,
items=get_texture_node_list)))
register_property((Material, "texture_atlas_normal", EnumProperty(
name=t("TextureAtlas.normal"),
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.normal").lower()),
default=0,
items=get_texture_node_list)))
register_property((Material, "texture_atlas_emission", EnumProperty(
name=t("TextureAtlas.emission"),
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.emission").lower()),
default=0,
items=get_texture_node_list)))
register_property((Material, "texture_atlas_ambient_occlusion", EnumProperty(
name=t("TextureAtlas.ambient_occlusion"),
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.ambient_occlusion").lower()),
default=0,
items=get_texture_node_list)))
register_property((Material, "texture_atlas_height", EnumProperty(
name=t("TextureAtlas.height"),
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.height").lower()),
default=0,
items=get_texture_node_list)))
register_property((Material, "texture_atlas_roughness", EnumProperty(
name=t("TextureAtlas.roughness"),
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.roughness").lower()),
default=0,
items=get_texture_node_list)))
register_property((Scene, "texture_atlas_material_index", IntProperty(
default=-1, default=-1,
get=(lambda self : -1), get=lambda self: -1,
set=(lambda self,context : None)))) set=lambda self, context: None
)
register_property((Scene, "materials", CollectionProperty(type=SceneMatClass))) materials: CollectionProperty(type=SceneMatClass)
register_property((Scene, "texture_atlas_Has_Mat_List_Shown", BoolProperty( texture_atlas_Has_Mat_List_Shown: BoolProperty(
default=False, default=False,
get=MaterialListBool.get_bool, get=MaterialListBool.get_bool,
set=MaterialListBool.set_bool))) set=MaterialListBool.set_bool
)
def unregister() -> None: class AvatarToolkitMaterialProperties(PropertyGroup):
#if you register properties with register_property then you shouldn't need this function. material_expanded: BoolProperty(
pass name="Expand Material",
description="Show/hide material properties",
default=False
)
include_in_atlas: BoolProperty(
name="Include in Atlas",
description="Include this material in texture atlas",
default=True
)
def get_texture_node_list(self, context):
if self.use_nodes:
nodes = [(i.image.name if i.image else i.name+"_image",
i.image.name if i.image else "node with no image...",
i.image.name if i.image else i.name, index+1)
for index, i in enumerate(self.node_tree.nodes)
if i.bl_idname == "ShaderNodeTexImage"]
if not nodes:
nodes = [("Error", "No images found", "Error", 0)]
else:
nodes = [("Error", "No node tree found", "Error", 0)]
nodes.append(("None", "None", "None", 0))
return nodes
texture_atlas_albedo: EnumProperty(
name="Albedo",
description="Albedo texture for atlas",
items=get_texture_node_list
)
texture_atlas_normal: EnumProperty(
name="Normal",
description="Normal map for atlas",
items=get_texture_node_list
)
texture_atlas_emission: EnumProperty(
name="Emission",
description="Emission texture for atlas",
items=get_texture_node_list
)
texture_atlas_ambient_occlusion: EnumProperty(
name="Ambient Occlusion",
description="AO texture for atlas",
items=get_texture_node_list
)
texture_atlas_height: EnumProperty(
name="Height",
description="Height map for atlas",
items=get_texture_node_list
)
texture_atlas_roughness: EnumProperty(
name="Roughness",
description="Roughness map for atlas",
items=get_texture_node_list
)
class AvatarToolkitObjectProperties(PropertyGroup):
material_group_expanded: BoolProperty(
name="Expand Material Group",
description="Show/hide materials for this mesh",
default=False
)
def register():
bpy.types.Scene.avatar_toolkit = PointerProperty(type=AvatarToolkitSceneProperties)
bpy.types.Material.avatar_toolkit = PointerProperty(type=AvatarToolkitMaterialProperties)
bpy.types.Object.avatar_toolkit = PointerProperty(type=AvatarToolkitObjectProperties)
def unregister():
del bpy.types.Scene.avatar_toolkit
del bpy.types.Material.avatar_toolkit
del bpy.types.Object.avatar_toolkit
-105
View File
@@ -1,105 +0,0 @@
import bpy
import typing
from typing import List, Type
# List to store the classes to register
__bl_classes = []
# List to store the ordered classes for registration
__bl_ordered_classes = []
# List to store props to register
__bl_props = []
def register_wrap(cls):
# Check if the class has a 'bl_rna' attribute (indicating it's a Blender class)
if hasattr(cls, 'bl_rna'):
# Add the class to the list of classes to register
__bl_classes.append(cls)
return cls
# Register all properties
def register_property(prop):
__bl_props.append(prop)
def register_properties():
for prop in __bl_props:
if isinstance(prop[2], bpy.props._PropertyDeferred):
setattr(prop[0], prop[1], prop[2])
else:
prop()
def clear_registration():
__bl_classes.clear()
__bl_ordered_classes.clear()
__bl_props.clear()
def unregister_properties():
for prop in reversed(__bl_props):
try:
delattr(prop[0], prop[1])
except AttributeError:
continue
clear_registration()
#- @989onan had to add this from Cats. This is extremely important else you will be screamed at by register order issues!
# Find order to register to solve dependencies
#################################################
def toposort(deps_dict):
sorted_list = []
sorted_values = set()
while len(deps_dict) > 0:
unsorted = []
for value, deps in deps_dict.items():
if len(deps) == 0:
sorted_list.append(value)
sorted_values.add(value)
else:
unsorted.append(value)
deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
#sort_order(sorted_list) #to sort by 'bl_order' so we can choose how things may appear in the ui
return sorted_list
def order_classes():
deps_dict = {}
classes_to_register = set(iter_classes_to_register())
for class_obj in classes_to_register:
deps_dict[class_obj] = set(iter_own_register_deps(class_obj, classes_to_register))
__bl_ordered_classes.clear()
# Then put everything else sorted into the list
for class_obj in toposort(deps_dict):
__bl_ordered_classes.append(class_obj)
print(__bl_ordered_classes)
__bl_classes.clear()
def iter_classes_to_register():
for class_obj in __bl_classes:
yield class_obj
def iter_own_register_deps(class_obj, own_classes):
yield from (dep for dep in iter_register_deps(class_obj) if dep in own_classes)
def iter_register_deps(class_obj):
for value in typing.get_type_hints(class_obj, {}, {}, True).values():
dependency = get_dependency_from_annotation(value)
if dependency is not None:
yield dependency
if hasattr(class_obj, "bl_parent_id"):
if class_obj.bl_parent_id != "":
for dependency in __bl_classes:
if dependency.bl_idname == class_obj.bl_parent_id:
yield dependency
def get_dependency_from_annotation(value):
if isinstance(value, tuple) and len(value) == 2:
if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
return value[1]["type"]
return None
+97
View File
@@ -0,0 +1,97 @@
import os
import json
import bpy
from bpy.app.translations import locale
from typing import Dict, List, Tuple
from .addon_preferences import save_preference, get_preference
# Use __file__ to get the current file's directory
current_dir = os.path.dirname(os.path.abspath(__file__))
main_dir = os.path.dirname(current_dir)
resources_dir = os.path.join(main_dir, "resources")
translations_dir = os.path.join(resources_dir, "translations")
dictionary: Dict[str, str] = dict()
languages: List[str] = []
verbose: bool = True
def load_translations() -> bool:
global dictionary, languages
old_dictionary = dictionary.copy()
dictionary = dict()
languages = ["auto"]
# Populate languages list
for i in os.listdir(translations_dir):
lang = i.split(".")[0]
if lang != "auto":
languages.append(lang)
language_index = get_preference("language", 0)
# print(f"Loading translations for language index: {language_index}") # Debug print
if language_index == 0: # "auto"
language = bpy.context.preferences.view.language
else:
try:
language = languages[language_index]
except IndexError:
language = bpy.context.preferences.view.language
# print(f"Selected language: {language}") # Debug print
translation_file: str = os.path.join(translations_dir, language + ".json")
if os.path.exists(translation_file):
with open(translation_file, 'r', encoding='utf-8') as file:
dictionary = json.load(file)["messages"]
# print(f"Loaded translations: {dictionary}") # Debug print
else:
custom_language: str = language.split("_")[0]
custom_translation_file: str = os.path.join(translations_dir, custom_language + ".json")
if os.path.exists(custom_translation_file):
with open(custom_translation_file, 'r', encoding='utf-8') as file:
dictionary = json.load(file)["messages"]
# print(f"Loaded custom translations: {dictionary}") # Debug print
else:
print(f"Translation file not found for language: {language}")
default_file: str = os.path.join(translations_dir, "en_US.json")
if os.path.exists(default_file):
with open(default_file, 'r', encoding='utf-8') as file:
dictionary = json.load(file)["messages"]
# print(f"Loaded default translations: {dictionary}") # Debug print
else:
print("Default translation file 'en_US.json' not found.")
return dictionary != old_dictionary
def t(phrase: str, default: str = None, **kwargs) -> str:
output: str = dictionary.get(phrase)
if output is None:
if verbose:
print(f'Warning: Unknown phrase: {phrase}')
return default if default is not None else phrase
# print(f"Translating '{phrase}' to '{output}'") # Debug print
return output.format(**kwargs) if kwargs else output
def get_language_display_name(lang: str) -> str:
if lang == "auto":
return t("Language.auto", "Automatic")
return t(f"Language.{lang}", lang)
def get_languages_list(self, context) -> List[Tuple[str, str, str]]:
return [(str(i), get_language_display_name(lang), f"Use {lang} language") for i, lang in enumerate(languages)]
def update_language(self, context):
print(f"Updating language to: {self.avatar_toolkit_language}") # Debug print
save_preference("language", int(self.avatar_toolkit_language))
load_translations()
# Set a flag to indicate that a language change has occurred
context.scene.avatar_toolkit_language_changed = True
# Show popup after language change
bpy.ops.avatar_toolkit.translation_restart_popup('INVOKE_DEFAULT')
# Initial load of translations
# print("Performing initial load of translations") # Debug print
load_translations()
+7 -8
View File
@@ -10,10 +10,9 @@ import time
from urllib import request, error from urllib import request, error
from threading import Thread from threading import Thread
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
from ..functions.translations import t from .translations import t
from .addon_preferences import get_preference, get_current_version, save_preference from .addon_preferences import get_preference, get_current_version, save_preference
from .register import register_wrap from ..ui.main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..ui.panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from typing import Dict, List, Tuple, Optional, Set, Any from typing import Dict, List, Tuple, Optional, Set, Any
GITHUB_REPO = "teamneoneko/Avatar-Toolkit" GITHUB_REPO = "teamneoneko/Avatar-Toolkit"
@@ -27,7 +26,7 @@ version_list: Optional[Dict[str, List[str]]] = None
main_dir: str = os.path.dirname(os.path.dirname(__file__)) main_dir: str = os.path.dirname(os.path.dirname(__file__))
downloads_dir: str = os.path.join(main_dir, "downloads") downloads_dir: str = os.path.join(main_dir, "downloads")
@register_wrap
class AvatarToolkit_OT_CheckForUpdate(bpy.types.Operator): class AvatarToolkit_OT_CheckForUpdate(bpy.types.Operator):
bl_idname = 'avatar_toolkit.check_for_update' bl_idname = 'avatar_toolkit.check_for_update'
bl_label = t('CheckForUpdateButton.label') bl_label = t('CheckForUpdateButton.label')
@@ -38,7 +37,7 @@ class AvatarToolkit_OT_CheckForUpdate(bpy.types.Operator):
check_for_update_background() check_for_update_background()
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_UpdateToLatest(bpy.types.Operator): class AvatarToolkit_OT_UpdateToLatest(bpy.types.Operator):
bl_idname = 'avatar_toolkit.update_latest' bl_idname = 'avatar_toolkit.update_latest'
bl_label = t('UpdateToLatestButton.label') bl_label = t('UpdateToLatestButton.label')
@@ -49,7 +48,7 @@ class AvatarToolkit_OT_UpdateToLatest(bpy.types.Operator):
update_now(latest=True) update_now(latest=True)
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_UpdateNotificationPopup(bpy.types.Operator): class AvatarToolkit_OT_UpdateNotificationPopup(bpy.types.Operator):
bl_idname = "avatar_toolkit.update_notification_popup" bl_idname = "avatar_toolkit.update_notification_popup"
bl_label = t('UpdateNotificationPopup.label') bl_label = t('UpdateNotificationPopup.label')
@@ -69,7 +68,7 @@ class AvatarToolkit_OT_UpdateNotificationPopup(bpy.types.Operator):
col = layout.column(align=True) col = layout.column(align=True)
col.label(text=t('UpdateNotificationPopup.newUpdate', default="New update available: {version}").format(version=latest_version_str)) col.label(text=t('UpdateNotificationPopup.newUpdate', default="New update available: {version}").format(version=latest_version_str))
@register_wrap
class AvatarToolkit_PT_UpdaterPanel(bpy.types.Panel): class AvatarToolkit_PT_UpdaterPanel(bpy.types.Panel):
bl_label = t("Updater.label") bl_label = t("Updater.label")
bl_idname = "OBJECT_PT_avatar_toolkit_updater" bl_idname = "OBJECT_PT_avatar_toolkit_updater"
@@ -83,7 +82,7 @@ class AvatarToolkit_PT_UpdaterPanel(bpy.types.Panel):
layout = self.layout layout = self.layout
draw_updater_panel(context, layout) draw_updater_panel(context, layout)
@register_wrap
class AvatarToolkit_OT_RestartBlenderPopup(bpy.types.Operator): class AvatarToolkit_OT_RestartBlenderPopup(bpy.types.Operator):
bl_idname = "avatar_toolkit.restart_blender_popup" bl_idname = "avatar_toolkit.restart_blender_popup"
bl_label = t('RestartBlenderPopup.label', default="Restart Blender") bl_label = t('RestartBlenderPopup.label', default="Restart Blender")
-18
View File
@@ -1,18 +0,0 @@
from ..core.register import register_wrap
#to reload all things in this directory and import them properly - @989onan
if "bpy" not in locals():
import bpy
import glob
import os
from os.path import dirname, basename, isfile, join
modules = glob.glob(join(dirname(__file__), "*.py"))
for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]:
exec("from . import "+module_name)
print("importing " +module_name)
else:
import importlib
modules = glob.glob(join(dirname(__file__), "*.py"))
for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]:
exec("importlib.reload("+module_name+")")
print("reloading " +module_name)
+6 -7
View File
@@ -1,11 +1,10 @@
import bpy import bpy
import math import math
from bpy.types import Context, Operator from bpy.types import Context, Operator
from ..core.register import register_wrap
from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes
from ..functions.translations import t from ..core.translations import t
@register_wrap
class AvatarToolKit_OT_ApplyTransforms(Operator): class AvatarToolKit_OT_ApplyTransforms(Operator):
bl_idname = "avatar_toolkit.apply_transforms" bl_idname = "avatar_toolkit.apply_transforms"
bl_label = t("Tools.apply_transforms.label") bl_label = t("Tools.apply_transforms.label")
@@ -37,7 +36,7 @@ class AvatarToolKit_OT_ApplyTransforms(Operator):
self.report({'INFO'}, t("Tools.apply_transforms.success")) self.report({'INFO'}, t("Tools.apply_transforms.success"))
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_ConnectBones(Operator): class AvatarToolKit_OT_ConnectBones(Operator):
bl_idname = "avatar_toolkit.connect_bones" bl_idname = "avatar_toolkit.connect_bones"
bl_label = t("Tools.connect_bones.label") bl_label = t("Tools.connect_bones.label")
@@ -90,7 +89,7 @@ class AvatarToolKit_OT_ConnectBones(Operator):
layout = self.layout layout = self.layout
layout.prop(self, "min_distance") layout.prop(self, "min_distance")
@register_wrap
class AvatarToolKit_OT_DeleteBoneConstraints(Operator): class AvatarToolKit_OT_DeleteBoneConstraints(Operator):
bl_idname = "avatar_toolkit.delete_bone_constraints" bl_idname = "avatar_toolkit.delete_bone_constraints"
bl_label = t("Tools.delete_bone_constraints.label") bl_label = t("Tools.delete_bone_constraints.label")
@@ -120,7 +119,7 @@ class AvatarToolKit_OT_DeleteBoneConstraints(Operator):
self.report({'INFO'}, t("Tools.delete_bone_constraints.success").format(constraints_removed=constraints_removed)) self.report({'INFO'}, t("Tools.delete_bone_constraints.success").format(constraints_removed=constraints_removed))
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_SeparateByMaterials(Operator): class AvatarToolKit_OT_SeparateByMaterials(Operator):
bl_idname = "avatar_toolkit.separate_by_materials" bl_idname = "avatar_toolkit.separate_by_materials"
bl_label = t("Tools.separate_by_materials.label") bl_label = t("Tools.separate_by_materials.label")
@@ -140,7 +139,7 @@ class AvatarToolKit_OT_SeparateByMaterials(Operator):
self.report({'INFO'}, t("Tools.separate_by_materials.success")) self.report({'INFO'}, t("Tools.separate_by_materials.success"))
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_SeparateByLooseParts(Operator): class AvatarToolKit_OT_SeparateByLooseParts(Operator):
bl_idname = "avatar_toolkit.separate_by_loose_parts" bl_idname = "avatar_toolkit.separate_by_loose_parts"
bl_label = t("Tools.separate_by_loose_parts.label") bl_label = t("Tools.separate_by_loose_parts.label")
+9 -10
View File
@@ -1,13 +1,12 @@
import bpy import bpy
from ..core.register import register_wrap
from bpy.types import Context, Mesh, Panel, Operator, Armature, EditBone from bpy.types import Context, Mesh, Panel, Operator, Armature, EditBone
from ..functions.translations import t from ..core.translations import t
from ..core.common import get_selected_armature, get_all_meshes from ..core.common import get_selected_armature, get_all_meshes
from ..core import common from ..core import common
from ..core.dictionaries import bone_names from ..core.dictionaries import bone_names
from mathutils import Matrix from mathutils import Matrix
@register_wrap
class AvatarToolkit_OT_StartPoseMode(Operator): class AvatarToolkit_OT_StartPoseMode(Operator):
bl_idname = 'avatar_toolkit.start_pose_mode' bl_idname = 'avatar_toolkit.start_pose_mode'
bl_label = t("Quick_Access.start_pose_mode.label") bl_label = t("Quick_Access.start_pose_mode.label")
@@ -33,7 +32,7 @@ class AvatarToolkit_OT_StartPoseMode(Operator):
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_StopPoseMode(Operator): class AvatarToolkit_OT_StopPoseMode(Operator):
bl_idname = 'avatar_toolkit.stop_pose_mode' bl_idname = 'avatar_toolkit.stop_pose_mode'
bl_label = t("Quick_Access.stop_pose_mode.label") bl_label = t("Quick_Access.stop_pose_mode.label")
@@ -55,7 +54,7 @@ class AvatarToolkit_OT_StopPoseMode(Operator):
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_ApplyPoseAsShapekey(Operator): class AvatarToolkit_OT_ApplyPoseAsShapekey(Operator):
bl_idname = 'avatar_toolkit.apply_pose_as_shapekey' bl_idname = 'avatar_toolkit.apply_pose_as_shapekey'
bl_label = t("Quick_Access.apply_pose_as_shapekey.label") bl_label = t("Quick_Access.apply_pose_as_shapekey.label")
@@ -98,7 +97,7 @@ class AvatarToolkit_OT_ApplyPoseAsShapekey(Operator):
self.report({'INFO'}, t('Tools.apply_pose_as_rest.success')) self.report({'INFO'}, t('Tools.apply_pose_as_rest.success'))
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_ApplyPoseAsRest(Operator): class AvatarToolkit_OT_ApplyPoseAsRest(Operator):
bl_idname = 'avatar_toolkit.apply_pose_as_rest' bl_idname = 'avatar_toolkit.apply_pose_as_rest'
bl_label = t("Quick_Access.apply_pose_as_rest.label") bl_label = t("Quick_Access.apply_pose_as_rest.label")
@@ -117,7 +116,7 @@ class AvatarToolkit_OT_ApplyPoseAsRest(Operator):
return {'CANCELLED'} return {'CANCELLED'}
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_RemoveZeroWeightBones(Operator): class AvatarToolkit_OT_RemoveZeroWeightBones(Operator):
bl_idname = "avatar_toolkit.remove_zero_weight_bones" bl_idname = "avatar_toolkit.remove_zero_weight_bones"
bl_label = t("Tools.remove_zero_weight_bones.label") bl_label = t("Tools.remove_zero_weight_bones.label")
@@ -218,7 +217,7 @@ class AvatarToolkit_OT_RemoveZeroWeightBones(Operator):
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_MergeBonesToActive(Operator): class AvatarToolkit_OT_MergeBonesToActive(Operator):
bl_idname = "avatar_toolkit.merge_bones_to_active" bl_idname = "avatar_toolkit.merge_bones_to_active"
bl_label = t("Tools.merge_bones_to_active.label") bl_label = t("Tools.merge_bones_to_active.label")
@@ -267,7 +266,7 @@ class AvatarToolkit_OT_MergeBonesToActive(Operator):
bpy.ops.object.mode_set(mode=prev_mode) bpy.ops.object.mode_set(mode=prev_mode)
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_MergeBonesToParents(Operator): class AvatarToolkit_OT_MergeBonesToParents(Operator):
bl_idname = "avatar_toolkit.merge_bones_to_parents" bl_idname = "avatar_toolkit.merge_bones_to_parents"
bl_label = t("Tools.merge_bones_to_parents.label") bl_label = t("Tools.merge_bones_to_parents.label")
@@ -342,7 +341,7 @@ class AvatarToolkit_OT_MergeBonesToParents(Operator):
bpy.ops.object.mode_set(mode=prev_mode) bpy.ops.object.mode_set(mode=prev_mode)
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_MergeArmatures(Operator): class AvatarToolkit_OT_MergeArmatures(Operator):
bl_idname = "avatar_toolkit.merge_armatures" bl_idname = "avatar_toolkit.merge_armatures"
bl_label = t("MergeArmature.merge_armatures.label") bl_label = t("MergeArmature.merge_armatures.label")
+2 -3
View File
@@ -5,10 +5,9 @@ import bpy
import os import os
from typing import List, Tuple, Optional from typing import List, Tuple, Optional
from bpy.types import Material, Operator, Context, Object, Image, Mesh, MeshUVLoopLayer, Float2AttributeValue, ShaderNodeTexImage, ShaderNodeBsdfPrincipled, ShaderNodeNormalMap from bpy.types import Material, Operator, Context, Object, Image, Mesh, MeshUVLoopLayer, Float2AttributeValue, ShaderNodeTexImage, ShaderNodeBsdfPrincipled, ShaderNodeNormalMap
from ..core.register import register_wrap
from ..core.common import SceneMatClass, MaterialListBool from ..core.common import SceneMatClass, MaterialListBool
from ..core.packer.rectangle_packer import MaterialImageList, BinPacker from ..core.packer.rectangle_packer import MaterialImageList, BinPacker
from ..functions.translations import t from ..core.translations import t
class MaterialImageList: class MaterialImageList:
def __init__(self): def __init__(self):
@@ -134,7 +133,7 @@ def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
@register_wrap
class AvatarToolKit_OT_AtlasMaterials(Operator): class AvatarToolKit_OT_AtlasMaterials(Operator):
bl_idname = "avatar_toolkit.atlas_materials" bl_idname = "avatar_toolkit.atlas_materials"
+2 -3
View File
@@ -3,8 +3,7 @@ import re
from typing import List, Tuple, Optional, Set, Dict from typing import List, Tuple, Optional, Set, Dict
from bpy.types import Material, Operator, Context, Object, NodeTree from bpy.types import Material, Operator, Context, Object, NodeTree
from ..core.common import clean_material_names, get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress from ..core.common import clean_material_names, get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress
from ..core.register import register_wrap from ..core.translations import t
from ..functions.translations import t
def textures_match(tex1: bpy.types.ImageTexture, tex2: bpy.types.ImageTexture) -> bool: def textures_match(tex1: bpy.types.ImageTexture, tex2: bpy.types.ImageTexture) -> bool:
return tex1.image == tex2.image and tex1.extension == tex2.extension return tex1.image == tex2.image and tex1.extension == tex2.extension
@@ -52,7 +51,7 @@ def get_base_name(name: str) -> str:
mat_match = re.match(r"^(.*)\.\d{3}$", name) mat_match = re.match(r"^(.*)\.\d{3}$", name)
return mat_match.group(1) if mat_match else name return mat_match.group(1) if mat_match else name
@register_wrap
class AvatarToolKit_OT_CombineMaterials(Operator): class AvatarToolKit_OT_CombineMaterials(Operator):
bl_idname = "avatar_toolkit.combine_materials" bl_idname = "avatar_toolkit.combine_materials"
bl_label = t("Optimization.combine_materials.label") bl_label = t("Optimization.combine_materials.label")
+2 -3
View File
@@ -1,11 +1,10 @@
import bpy import bpy
from ..core import common from ..core import common
from ..core import register_wrap from ..core.translations import t
from .translations import t
import re import re
@register_wrap
class AvatarToolKit_OT_CreateDigitigradeLegs(bpy.types.Operator): class AvatarToolKit_OT_CreateDigitigradeLegs(bpy.types.Operator):
bl_idname = "avatar_toolkit.create_digitigrade_legs" bl_idname = "avatar_toolkit.create_digitigrade_legs"
bl_label = t('Tools.create_digitigrade_legs.label') bl_label = t('Tools.create_digitigrade_legs.label')
+5 -6
View File
@@ -1,16 +1,15 @@
import bpy import bpy
from bpy.types import Operator from bpy.types import Operator
from bpy_extras.io_utils import ImportHelper from bpy_extras.io_utils import ImportHelper
from ..core.register import register_wrap from ..core.importers.importer import imports, import_types
from ..core.importer import imports, import_types
from ..core.common import remove_default_objects from ..core.common import remove_default_objects
from ..functions.translations import t from ..core.translations import t
import pathlib import pathlib
import os import os
VRM_IMPORTER_URL = "https://github.com/saturday06/VRM_Addon_for_Blender" VRM_IMPORTER_URL = "https://github.com/saturday06/VRM_Addon_for_Blender"
@register_wrap
class AvatarToolKit_OT_ImportAnyModel(Operator, ImportHelper): class AvatarToolKit_OT_ImportAnyModel(Operator, ImportHelper):
bl_idname = 'avatar_toolkit.import_any_model' bl_idname = 'avatar_toolkit.import_any_model'
bl_label = t('Tools.import_any_model.label') bl_label = t('Tools.import_any_model.label')
@@ -67,7 +66,7 @@ class AvatarToolKit_OT_ImportAnyModel(Operator, ImportHelper):
self.report({'INFO'}, t('Quick_Access.import_success')) self.report({'INFO'}, t('Quick_Access.import_success'))
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class VRMImporterPopup(Operator): class VRMImporterPopup(Operator):
bl_idname = "wm.vrm_importer_popup" bl_idname = "wm.vrm_importer_popup"
bl_label = "VRM Importer Not Installed" bl_label = "VRM Importer Not Installed"
@@ -87,7 +86,7 @@ class VRMImporterPopup(Operator):
#TODO: This needs to be done with our own MMD importer. #TODO: This needs to be done with our own MMD importer.
""" """
#stolen from cats. Oh wait I made this code riiiiiiight - @989onan #stolen from cats. Oh wait I made this code riiiiiiight - @989onan
@register_wrap
class ImportMMDAnimation(bpy.types.Operator, ImportHelper): class ImportMMDAnimation(bpy.types.Operator, ImportHelper):
bl_idname = 'avatar_toolkit.import_mmd_animation' bl_idname = 'avatar_toolkit.import_mmd_animation'
bl_label = t('Importer.mmd_anim_importer.label') bl_label = t('Importer.mmd_anim_importer.label')
+5 -6
View File
@@ -3,10 +3,9 @@ import bpy
from typing import List, Optional, Set from typing import List, Optional, Set
from bpy.types import Operator, Context, Object from bpy.types import Operator, Context, Object
from ..core.common import fix_uv_coordinates, get_selected_armature, get_all_meshes, is_valid_armature, apply_shapekey_to_basis, has_shapekeys, select_current_armature, init_progress, update_progress, finish_progress from ..core.common import fix_uv_coordinates, get_selected_armature, get_all_meshes, is_valid_armature, apply_shapekey_to_basis, has_shapekeys, select_current_armature, init_progress, update_progress, finish_progress
from ..functions.translations import t from ..core.translations import t
from ..core.register import register_wrap
@register_wrap
class AvatarToolkit_OT_RemoveUnusedShapekeys(bpy.types.Operator): class AvatarToolkit_OT_RemoveUnusedShapekeys(bpy.types.Operator):
tolerance: bpy.props.FloatProperty(name=t("Tools.remove_unused_shapekeys.tolerance.label"), default=0.001, description=t("Tools.remove_unused_shapekeys.tolerance.desc")) tolerance: bpy.props.FloatProperty(name=t("Tools.remove_unused_shapekeys.tolerance.label"), default=0.001, description=t("Tools.remove_unused_shapekeys.tolerance.desc"))
bl_idname = "avatar_toolkit.remove_unused_shapekeys" bl_idname = "avatar_toolkit.remove_unused_shapekeys"
@@ -56,7 +55,7 @@ class AvatarToolkit_OT_RemoveUnusedShapekeys(bpy.types.Operator):
continue continue
ob.shape_key_remove(ob.data.shape_keys.key_blocks[kb_name]) ob.shape_key_remove(ob.data.shape_keys.key_blocks[kb_name])
@register_wrap
class AvatarToolkit_OT_ApplyShapeKey(bpy.types.Operator): class AvatarToolkit_OT_ApplyShapeKey(bpy.types.Operator):
bl_idname = "avatar_toolkit.apply_shape_key" bl_idname = "avatar_toolkit.apply_shape_key"
bl_label = t("Tools.apply_shape_key.label") bl_label = t("Tools.apply_shape_key.label")
@@ -79,7 +78,7 @@ class AvatarToolkit_OT_ApplyShapeKey(bpy.types.Operator):
self.report({'ERROR'}, t("Tools.apply_shape_key.error")) self.report({'ERROR'}, t("Tools.apply_shape_key.error"))
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_JoinAllMeshes(Operator): class AvatarToolKit_OT_JoinAllMeshes(Operator):
bl_idname = "avatar_toolkit.join_all_meshes" bl_idname = "avatar_toolkit.join_all_meshes"
bl_label = t("Optimization.join_all_meshes.label") bl_label = t("Optimization.join_all_meshes.label")
@@ -150,7 +149,7 @@ class AvatarToolKit_OT_JoinAllMeshes(Operator):
finish_progress(context) finish_progress(context)
@register_wrap
class AvatarToolKit_OT_JoinSelectedMeshes(Operator): class AvatarToolKit_OT_JoinSelectedMeshes(Operator):
bl_idname = "avatar_toolkit.join_selected_meshes" bl_idname = "avatar_toolkit.join_selected_meshes"
bl_label = t("Optimization.join_selected_meshes.label") bl_label = t("Optimization.join_selected_meshes.label")
+5 -6
View File
@@ -2,13 +2,12 @@ import bpy
import numpy as np import numpy as np
import re import re
from bpy.types import Operator, Context, Material, ShaderNodeTexImage, ShaderNodeGroup, Object from bpy.types import Operator, Context, Material, ShaderNodeTexImage, ShaderNodeGroup, Object
from ..core.register import register_wrap from ..core.translations import t
from ..functions.translations import t
from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress
from ..functions.additional_tools import AvatarToolKit_OT_ConnectBones, AvatarToolKit_OT_DeleteBoneConstraints from ..functions.additional_tools import AvatarToolKit_OT_ConnectBones, AvatarToolKit_OT_DeleteBoneConstraints
from ..functions.armature_modifying import AvatarToolkit_OT_RemoveZeroWeightBones, AvatarToolkit_OT_MergeBonesToParents from ..functions.armature_modifying import AvatarToolkit_OT_RemoveZeroWeightBones, AvatarToolkit_OT_MergeBonesToParents
@register_wrap
class AvatarToolKit_OT_CleanupMesh(Operator): class AvatarToolKit_OT_CleanupMesh(Operator):
bl_idname = "avatar_toolkit.cleanup_mesh" bl_idname = "avatar_toolkit.cleanup_mesh"
bl_label = t("MMDOptions.cleanup_mesh.label") bl_label = t("MMDOptions.cleanup_mesh.label")
@@ -61,7 +60,7 @@ class AvatarToolKit_OT_CleanupMesh(Operator):
if key.name != 'Basis' and all(abs(key.data[i].co[j] - obj.data.shape_keys.reference_key.data[i].co[j]) < 0.0001 for i in range(len(key.data)) for j in range(3)): if key.name != 'Basis' and all(abs(key.data[i].co[j] - obj.data.shape_keys.reference_key.data[i].co[j]) < 0.0001 for i in range(len(key.data)) for j in range(3)):
obj.shape_key_remove(key) obj.shape_key_remove(key)
@register_wrap
class AvatarToolKit_OT_OptimizeWeights(Operator): class AvatarToolKit_OT_OptimizeWeights(Operator):
bl_idname = "avatar_toolkit.optimize_weights" bl_idname = "avatar_toolkit.optimize_weights"
bl_label = t("MMDOptions.optimize_weights.label") bl_label = t("MMDOptions.optimize_weights.label")
@@ -108,7 +107,7 @@ class AvatarToolKit_OT_OptimizeWeights(Operator):
for g in sorted_groups[self.max_weights:]: for g in sorted_groups[self.max_weights:]:
obj.vertex_groups[g.group].remove([v.index]) obj.vertex_groups[g.group].remove([v.index])
@register_wrap
class AvatarToolKit_OT_OptimizeArmature(Operator): class AvatarToolKit_OT_OptimizeArmature(Operator):
bl_idname = "avatar_toolkit.optimize_armature" bl_idname = "avatar_toolkit.optimize_armature"
bl_label = t("MMDOptions.optimize_armature.label") bl_label = t("MMDOptions.optimize_armature.label")
@@ -339,7 +338,7 @@ def fix_vrm_shader(material: Material):
material.node_tree.links = [link for link in material.node_tree.links material.node_tree.links = [link for link in material.node_tree.links
if not (link.from_node == node or link.to_node == node)] if not (link.from_node == node or link.to_node == node)]
@register_wrap
class AvatarToolKit_OT_ConvertMaterials(Operator): class AvatarToolKit_OT_ConvertMaterials(Operator):
bl_idname = "avatar_toolkit.convert_materials" bl_idname = "avatar_toolkit.convert_materials"
bl_label = t("MMDOptions.convert_materials.label") bl_label = t("MMDOptions.convert_materials.label")
+3 -4
View File
@@ -1,9 +1,8 @@
import bpy import bpy
from typing import List, TypedDict, Any from typing import List, TypedDict, Any
from bpy.types import Operator, Context, Object from bpy.types import Operator, Context, Object
from ..core.register import register_wrap
from ..core.common import get_selected_armature, is_valid_armature, select_current_armature, get_all_meshes from ..core.common import get_selected_armature, is_valid_armature, select_current_armature, get_all_meshes
from ..functions.translations import t from ..core.translations import t
class meshEntry(TypedDict): class meshEntry(TypedDict):
mesh: Object mesh: Object
@@ -11,7 +10,7 @@ class meshEntry(TypedDict):
vertices: int vertices: int
cur_vertex_pass: int cur_vertex_pass: int
@register_wrap
class AvatarToolKit_OT_RemoveDoublesSafelyAdvanced(Operator): class AvatarToolKit_OT_RemoveDoublesSafelyAdvanced(Operator):
bl_idname = "avatar_toolkit.remove_doubles_safely_advanced" bl_idname = "avatar_toolkit.remove_doubles_safely_advanced"
bl_label = t("Optimization.remove_doubles_safely_advanced.label") bl_label = t("Optimization.remove_doubles_safely_advanced.label")
@@ -39,7 +38,7 @@ class AvatarToolKit_OT_RemoveDoublesSafelyAdvanced(Operator):
return {'RUNNING_MODAL'} return {'RUNNING_MODAL'}
@register_wrap
class AvatarToolKit_OT_RemoveDoublesSafely(Operator): class AvatarToolKit_OT_RemoveDoublesSafely(Operator):
bl_idname = "avatar_toolkit.remove_doubles_safely" bl_idname = "avatar_toolkit.remove_doubles_safely"
bl_label = t("Optimization.remove_doubles_safely.label") bl_label = t("Optimization.remove_doubles_safely.label")
+2 -3
View File
@@ -1,13 +1,12 @@
import bpy import bpy
from ..core.register import register_wrap
from typing import List, Optional from typing import List, Optional
import re import re
from bpy.types import Operator, Context, Object from bpy.types import Operator, Context, Object
from ..core.dictionaries import bone_names from ..core.dictionaries import bone_names
from ..core.common import get_selected_armature, simplify_bonename, is_valid_armature from ..core.common import get_selected_armature, simplify_bonename, is_valid_armature
from ..functions.translations import t from ..core.translations import t
@register_wrap
class AvatarToolKit_OT_ConvertToResonite(Operator): class AvatarToolKit_OT_ConvertToResonite(Operator):
bl_idname = 'avatar_toolkit.convert_to_resonite' bl_idname = 'avatar_toolkit.convert_to_resonite'
bl_label = t('Tools.convert_to_resonite.label') bl_label = t('Tools.convert_to_resonite.label')
+1 -10
View File
@@ -1,18 +1,9 @@
# This code is heavily based on the Rigify-Move-DEF by NyankoNyan (https://github.com/NyankoNyan/Rigify-Move-DEF), which is licensed under the MIT License. We just heavily improve the code and add some new features. # This code is heavily based on the Rigify-Move-DEF by NyankoNyan (https://github.com/NyankoNyan/Rigify-Move-DEF), which is licensed under the MIT License. We just heavily improve the code and add some new features.
import bpy import bpy
from ..core.register import register_wrap
from ..core.common import get_selected_armature, is_valid_armature from ..core.common import get_selected_armature, is_valid_armature
from ..functions.translations import t from ..core.translations import t
from bpy.types import Operator, Context from bpy.types import Operator, Context
import bpy
from ..core.register import register_wrap
from ..core.common import get_selected_armature, is_valid_armature
from ..functions.translations import t
from bpy.types import Operator, Context
@register_wrap
class AvatarToolKit_OT_ConvertRigifyToUnity(Operator): class AvatarToolKit_OT_ConvertRigifyToUnity(Operator):
bl_idname = "avatar_toolkit.convert_rigify_to_unity" bl_idname = "avatar_toolkit.convert_rigify_to_unity"
bl_label = t("Tools.convert_rigify_to_unity.label") bl_label = t("Tools.convert_rigify_to_unity.label")
+2 -3
View File
@@ -4,15 +4,14 @@ from bpy.types import Operator, Object, Context, Mesh, MeshUVLoopLayer
import bmesh import bmesh
import numpy as np import numpy as np
import math import math
from ..functions.translations import t from ..core.translations import t
from ..core.register import register_wrap
class GenerateLoopTreeResult(TypedDict): class GenerateLoopTreeResult(TypedDict):
tree: dict[str, set[str]] tree: dict[str, set[str]]
selected_loops: dict[str,list[int]] selected_loops: dict[str,list[int]]
selected_verts: dict[str,int] selected_verts: dict[str,int]
@register_wrap
class AvatarToolkit_OT_AlignUVEdgesToTarget(Operator): class AvatarToolkit_OT_AlignUVEdgesToTarget(Operator):
bl_idname = "avatar_toolkit.align_uv_edges_to_target" bl_idname = "avatar_toolkit.align_uv_edges_to_target"
bl_label = t("avatar_toolkit.align_uv_edges_to_target.label") bl_label = t("avatar_toolkit.align_uv_edges_to_target.label")
+2 -3
View File
@@ -1,11 +1,10 @@
import bpy import bpy
from ..core import common from ..core import common
from ..core.register import register_wrap from ..core.translations import t
from ..functions.translations import t
from typing import List, Tuple from typing import List, Tuple
from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress
@register_wrap
class AvatarToolKit_OT_AutoVisemeButton(bpy.types.Operator): class AvatarToolKit_OT_AutoVisemeButton(bpy.types.Operator):
bl_idname = 'avatar_toolkit.create_visemes' bl_idname = 'avatar_toolkit.create_visemes'
bl_label = t('AutoVisemeButton.label') bl_label = t('AutoVisemeButton.label')
-15
View File
@@ -1,15 +0,0 @@
if "bpy" not in locals():
import bpy
import glob
import os
from os.path import dirname, basename, isfile, join
modules = glob.glob(join(dirname(__file__), "*.py"))
for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]:
exec("from . import "+module_name)
print("importing " +module_name)
else:
import importlib
modules = glob.glob(join(dirname(__file__), "*.py"))
for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]:
print("reloading " +module_name)
exec("importlib.reload("+module_name+")")
+38 -53
View File
@@ -1,57 +1,51 @@
from bpy.types import UIList, Panel, UILayout, Object, Context, Material, Operator from bpy.types import UIList, Panel, UILayout, Object, Context, Material, Operator
import bpy import bpy
from math import sqrt from math import sqrt
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..core.common import SceneMatClass, MaterialListBool, get_selected_armature from ..core.common import SceneMatClass, MaterialListBool, get_selected_armature
from ..functions.atlas_materials import AvatarToolKit_OT_AtlasMaterials from ..functions.atlas_materials import AvatarToolKit_OT_AtlasMaterials
from ..functions.translations import t from ..core.translations import t
@register_wrap
class AvatarToolKit_OT_SelectAllMaterials(Operator): class AvatarToolKit_OT_SelectAllMaterials(Operator):
bl_idname = 'avatar_toolkit.select_all_materials' bl_idname = 'avatar_toolkit.select_all_materials'
bl_label = "Select All" bl_label = "Select All"
bl_description = "Select all materials for atlas" bl_description = "Select all materials for atlas"
def execute(self, context): def execute(self, context):
for item in context.scene.materials: for item in context.scene.avatar_toolkit.materials:
item.mat.include_in_atlas = True item.mat.avatar_toolkit.include_in_atlas = True
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_SelectNoneMaterials(Operator): class AvatarToolKit_OT_SelectNoneMaterials(Operator):
bl_idname = 'avatar_toolkit.select_none_materials' bl_idname = 'avatar_toolkit.select_none_materials'
bl_label = "Select None" bl_label = "Select None"
bl_description = "Deselect all materials" bl_description = "Deselect all materials"
def execute(self, context): def execute(self, context):
for item in context.scene.materials: for item in context.scene.avatar_toolkit.materials:
item.mat.include_in_atlas = False item.mat.avatar_toolkit.include_in_atlas = False
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_ExpandAllMaterials(Operator): class AvatarToolKit_OT_ExpandAllMaterials(Operator):
bl_idname = 'avatar_toolkit.expand_all_materials' bl_idname = 'avatar_toolkit.expand_all_materials'
bl_label = "Expand All" bl_label = "Expand All"
bl_description = "Expand all material settings" bl_description = "Expand all material settings"
def execute(self, context): def execute(self, context):
for item in context.scene.materials: for item in context.scene.avatar_toolkit.materials:
item.mat.material_expanded = True item.mat.avatar_toolkit.material_expanded = True
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_CollapseAllMaterials(Operator): class AvatarToolKit_OT_CollapseAllMaterials(Operator):
bl_idname = 'avatar_toolkit.collapse_all_materials' bl_idname = 'avatar_toolkit.collapse_all_materials'
bl_label = "Collapse All" bl_label = "Collapse All"
bl_description = "Collapse all material settings" bl_description = "Collapse all material settings"
def execute(self, context): def execute(self, context):
for item in context.scene.materials: for item in context.scene.avatar_toolkit.materials:
item.mat.material_expanded = False item.mat.avatar_toolkit.material_expanded = False
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_OT_ExpandSectionMaterials(Operator): class AvatarToolKit_OT_ExpandSectionMaterials(Operator):
bl_idname = 'avatar_toolkit.expand_section_materials' bl_idname = 'avatar_toolkit.expand_section_materials'
bl_label = "" bl_label = ""
@@ -62,23 +56,22 @@ class AvatarToolKit_OT_ExpandSectionMaterials(Operator):
return True return True
def execute(self, context: Context) -> set: def execute(self, context: Context) -> set:
if not context.scene.texture_atlas_Has_Mat_List_Shown: if not context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown:
context.scene.materials.clear() context.scene.avatar_toolkit.materials.clear()
newlist: list[Material] = [] newlist: list[Material] = []
for obj in bpy.context.scene.objects: for obj in context.scene.objects:
if len(obj.material_slots)>0: if len(obj.material_slots)>0:
for mat_slot in obj.material_slots: for mat_slot in obj.material_slots:
if mat_slot.material: if mat_slot.material:
if mat_slot.material not in newlist: if mat_slot.material not in newlist:
newlist.append(mat_slot.material) newlist.append(mat_slot.material)
newitem: SceneMatClass = context.scene.materials.add() newitem: SceneMatClass = context.scene.avatar_toolkit.materials.add()
newitem.mat = mat_slot.material newitem.mat = mat_slot.material
MaterialListBool.old_list[context.scene.name] = newlist MaterialListBool.old_list[context.scene.name] = newlist
else: else:
context.scene.texture_atlas_Has_Mat_List_Shown = False context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown = False
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList): class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList):
bl_label = t("TextureAtlas.material_list_label") bl_label = t("TextureAtlas.material_list_label")
bl_idname = "Material_UL_avatar_toolkit_texture_atlas_mat_list_mat" bl_idname = "Material_UL_avatar_toolkit_texture_atlas_mat_list_mat"
@@ -93,38 +86,35 @@ class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList):
row.operator("avatar_toolkit.select_none_materials", text="", icon='CHECKBOX_DEHLT') row.operator("avatar_toolkit.select_none_materials", text="", icon='CHECKBOX_DEHLT')
row.operator("avatar_toolkit.expand_all_materials", text="", icon='DISCLOSURE_TRI_DOWN') row.operator("avatar_toolkit.expand_all_materials", text="", icon='DISCLOSURE_TRI_DOWN')
row.operator("avatar_toolkit.collapse_all_materials", text="", icon='DISCLOSURE_TRI_RIGHT') row.operator("avatar_toolkit.collapse_all_materials", text="", icon='DISCLOSURE_TRI_RIGHT')
row.prop(context.scene, "material_search_filter", text="", icon='VIEWZOOM') row.prop(context.scene.avatar_toolkit, "material_search_filter", text="", icon='VIEWZOOM')
box = layout.box() box = layout.box()
row = box.row() row = box.row()
row.label(text=f"Estimated Atlas Size: {self.calculate_atlas_size(context)}px") row.label(text=f"Estimated Atlas Size: {self.calculate_atlas_size(context)}px")
def draw_item(self, context: Context, layout: UILayout, data: Object, item: SceneMatClass, icon, active_data, active_propname, index): def draw_item(self, context: Context, layout: UILayout, data: Object, item: SceneMatClass, icon, active_data, active_propname, index):
if context.scene.texture_atlas_Has_Mat_List_Shown: if context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown:
if context.scene.material_search_filter and context.scene.material_search_filter.lower() not in item.mat.name.lower(): if context.scene.avatar_toolkit.material_search_filter and context.scene.avatar_toolkit.material_search_filter.lower() not in item.mat.name.lower():
return return
row = layout.row() row = layout.row()
# Add a clear checkbox for material selection row.prop(item.mat.avatar_toolkit, "include_in_atlas", text="", icon='CHECKBOX_HLT' if item.mat.avatar_toolkit.include_in_atlas else 'CHECKBOX_DEHLT')
row.prop(item.mat, "include_in_atlas", text="", icon='CHECKBOX_HLT' if item.mat.include_in_atlas else 'CHECKBOX_DEHLT')
# Material name and expansion toggle row.prop(item.mat.avatar_toolkit, "material_expanded",
row.prop(item.mat, "material_expanded",
text=item.mat.name, text=item.mat.name,
icon='DOWNARROW_HLT' if item.mat.material_expanded else 'RIGHTARROW', icon='DOWNARROW_HLT' if item.mat.avatar_toolkit.material_expanded else 'RIGHTARROW',
emboss=False) emboss=False)
# Show texture settings if expanded if item.mat.avatar_toolkit.material_expanded and item.mat.avatar_toolkit.include_in_atlas:
if item.mat.material_expanded and item.mat.include_in_atlas:
box = layout.box() box = layout.box()
col = box.column(align=True) col = box.column(align=True)
self.draw_texture_row(col, item.mat, "texture_atlas_albedo", "IMAGE_RGB") self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_albedo", "IMAGE_RGB")
self.draw_texture_row(col, item.mat, "texture_atlas_normal", "NORMALS_FACE") self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_normal", "NORMALS_FACE")
self.draw_texture_row(col, item.mat, "texture_atlas_emission", "LIGHT") self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_emission", "LIGHT")
self.draw_texture_row(col, item.mat, "texture_atlas_ambient_occlusion", "SHADING_SOLID") self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_ambient_occlusion", "SHADING_SOLID")
self.draw_texture_row(col, item.mat, "texture_atlas_height", "IMAGE_ZDEPTH") self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_height", "IMAGE_ZDEPTH")
self.draw_texture_row(col, item.mat, "texture_atlas_roughness", "MATERIAL") self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_roughness", "MATERIAL")
col.separator(factor=0.5) col.separator(factor=0.5)
@@ -136,21 +126,15 @@ class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList):
else: else:
row.label(text="", icon='X') row.label(text="", icon='X')
def is_material_ready(self, material):
return bool(material.texture_atlas_albedo or
material.texture_atlas_normal or
material.texture_atlas_emission)
def calculate_atlas_size(self, context): def calculate_atlas_size(self, context):
total_size = 0 total_size = 0
for mat in context.scene.materials: for mat in context.scene.avatar_toolkit.materials:
if mat.mat.include_in_atlas: if mat.mat.avatar_toolkit.include_in_atlas:
if mat.mat.texture_atlas_albedo: if mat.mat.avatar_toolkit.texture_atlas_albedo:
img = bpy.data.images[mat.mat.texture_atlas_albedo] img = bpy.data.images[mat.mat.avatar_toolkit.texture_atlas_albedo]
total_size += img.size[0] * img.size[1] total_size += img.size[0] * img.size[1]
return f"{int(sqrt(total_size))}x{int(sqrt(total_size))}" return f"{int(sqrt(total_size))}x{int(sqrt(total_size))}"
@register_wrap
class AvatarToolKit_PT_TextureAtlasPanel(Panel): class AvatarToolKit_PT_TextureAtlasPanel(Panel):
bl_label = t("TextureAtlas.label") bl_label = t("TextureAtlas.label")
bl_idname = "OBJECT_PT_avatar_toolkit_texture_atlas" bl_idname = "OBJECT_PT_avatar_toolkit_texture_atlas"
@@ -170,18 +154,18 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
box = layout.box() box = layout.box()
row = box.row() row = box.row()
direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT' direction_icon = 'RIGHTARROW' if not context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
row.operator(AvatarToolKit_OT_ExpandSectionMaterials.bl_idname, row.operator(AvatarToolKit_OT_ExpandSectionMaterials.bl_idname,
text=(t("TextureAtlas.reload_list") if not context.scene.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")), text=(t("TextureAtlas.reload_list") if not context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")),
icon=direction_icon) icon=direction_icon)
if context.scene.texture_atlas_Has_Mat_List_Shown: if context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown:
row = box.row() row = box.row()
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname, row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname,
'material_list', 'material_list',
context.scene, context.scene.avatar_toolkit,
'materials', 'materials',
context.scene, context.scene.avatar_toolkit,
'texture_atlas_material_index', 'texture_atlas_material_index',
rows=12, rows=12,
type='DEFAULT') type='DEFAULT')
@@ -195,3 +179,4 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
icon='NODE_TEXTURE') icon='NODE_TEXTURE')
else: else:
layout.label(text=t("Tools.select_armature"), icon='ERROR') layout.label(text=t("Tools.select_armature"), icon='ERROR')
+2 -4
View File
@@ -1,10 +1,8 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME from ..core.translations import t
from ..functions.translations import t
from ..core.common import open_web_after_delay_multi_threaded from ..core.common import open_web_after_delay_multi_threaded
@register_wrap
class AvatarToolkit_PT_CreditsSupport(bpy.types.Panel): class AvatarToolkit_PT_CreditsSupport(bpy.types.Panel):
bl_label = t("CreditsSupport.label") bl_label = t("CreditsSupport.label")
bl_idname = "OBJECT_PT_avatar_toolkit_credits_support" bl_idname = "OBJECT_PT_avatar_toolkit_credits_support"
+3 -6
View File
@@ -1,6 +1,7 @@
import bpy import bpy
from ..core.register import register_wrap from ..core.translations import t
from ..functions.translations import t
CATEGORY_NAME = "Avatar Toolkit"
def draw_title(self: bpy.types.Panel): def draw_title(self: bpy.types.Panel):
layout = self.layout layout = self.layout
@@ -8,9 +9,6 @@ def draw_title(self: bpy.types.Panel):
layout.label(text=t("AvatarToolkit.desc2")) layout.label(text=t("AvatarToolkit.desc2"))
layout.label(text=t("AvatarToolkit.desc3")) layout.label(text=t("AvatarToolkit.desc3"))
CATEGORY_NAME = "Avatar Toolkit"
@register_wrap
class AvatarToolKit_PT_AvatarToolkitPanel(bpy.types.Panel): class AvatarToolKit_PT_AvatarToolkitPanel(bpy.types.Panel):
bl_label = t("AvatarToolkit.label") bl_label = t("AvatarToolkit.label")
bl_idname = "OBJECT_PT_avatar_toolkit" bl_idname = "OBJECT_PT_avatar_toolkit"
@@ -21,4 +19,3 @@ class AvatarToolKit_PT_AvatarToolkitPanel(bpy.types.Panel):
def draw(self: bpy.types.Panel, context: bpy.types.Context): def draw(self: bpy.types.Panel, context: bpy.types.Context):
draw_title(self) draw_title(self)
+6 -9
View File
@@ -1,13 +1,10 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from bpy.types import Panel, Context from bpy.types import Panel, Context
from ..core.common import get_selected_armature from ..core.common import get_selected_armature
from ..functions.translations import t from ..core.translations import t
from ..functions.armature_modifying import AvatarToolkit_OT_MergeArmatures from ..functions.armature_modifying import AvatarToolkit_OT_MergeArmatures
@register_wrap
class AvatarToolkit_PT_MergeArmaturesPanel(Panel): class AvatarToolkit_PT_MergeArmaturesPanel(Panel):
bl_label = t("MergeArmatures.label") bl_label = t("MergeArmatures.label")
bl_idname = "OBJECT_PT_avatar_toolkit_merge_armatures" bl_idname = "OBJECT_PT_avatar_toolkit_merge_armatures"
@@ -29,14 +26,14 @@ class AvatarToolkit_PT_MergeArmaturesPanel(Panel):
box = layout.box() box = layout.box()
col = box.column(align=True) col = box.column(align=True)
col.prop(context.scene, property="selected_armature", text=t("MergeArmatures.target_armature.label"), icon="ARMATURE_DATA") col.prop(context.scene.avatar_toolkit, "selected_armature", text=t("MergeArmatures.target_armature.label"), icon="ARMATURE_DATA")
col.prop(context.scene, property="merge_armature_source", icon="OUTLINER_OB_ARMATURE") col.prop(context.scene.avatar_toolkit, "merge_armature_source", icon="OUTLINER_OB_ARMATURE")
layout.separator(factor=0.5) layout.separator(factor=0.5)
col = layout.column(align=True) col = layout.column(align=True)
col.prop(context.scene, property="merge_armature_align_bones", icon="BONE_DATA") col.prop(context.scene.avatar_toolkit, "merge_armature_align_bones", icon="BONE_DATA")
col.prop(context.scene, property="merge_armature_apply_transforms", icon="OBJECT_ORIGIN") col.prop(context.scene.avatar_toolkit, "merge_armature_apply_transforms", icon="OBJECT_ORIGIN")
layout.separator(factor=1.0) layout.separator(factor=1.0)
+2 -5
View File
@@ -1,13 +1,11 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME from ..core.translations import t
from ..functions.translations import t
from ..functions.mmd_functions import * from ..functions.mmd_functions import *
from ..functions.mesh_tools import AvatarToolKit_OT_JoinAllMeshes from ..functions.mesh_tools import AvatarToolKit_OT_JoinAllMeshes
from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials
from ..functions.additional_tools import AvatarToolKit_OT_ApplyTransforms from ..functions.additional_tools import AvatarToolKit_OT_ApplyTransforms
@register_wrap
class AvatarToolkit_PT_MMDOptionsPanel(bpy.types.Panel): class AvatarToolkit_PT_MMDOptionsPanel(bpy.types.Panel):
bl_label = t("MMDOptions.label") bl_label = t("MMDOptions.label")
bl_idname = "OBJECT_PT_avatar_toolkit_mmd_options" bl_idname = "OBJECT_PT_avatar_toolkit_mmd_options"
@@ -48,4 +46,3 @@ class AvatarToolkit_PT_MMDOptionsPanel(bpy.types.Panel):
row = layout.row() row = layout.row()
row.scale_y = 1.2 row.scale_y = 1.2
row.operator(AvatarToolKit_OT_ConvertMaterials.bl_idname, icon='SHADING_TEXTURE') row.operator(AvatarToolKit_OT_ConvertMaterials.bl_idname, icon='SHADING_TEXTURE')
+3 -7
View File
@@ -1,13 +1,11 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME from ..core.translations import t
from ..functions.translations import t
from ..functions.remove_doubles_safely import AvatarToolKit_OT_RemoveDoublesSafely, AvatarToolKit_OT_RemoveDoublesSafelyAdvanced from ..functions.remove_doubles_safely import AvatarToolKit_OT_RemoveDoublesSafely, AvatarToolKit_OT_RemoveDoublesSafelyAdvanced
from ..core.common import get_selected_armature from ..core.common import get_selected_armature
from ..functions.mesh_tools import AvatarToolKit_OT_JoinAllMeshes, AvatarToolKit_OT_JoinSelectedMeshes from ..functions.mesh_tools import AvatarToolKit_OT_JoinAllMeshes, AvatarToolKit_OT_JoinSelectedMeshes
from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials
@register_wrap
class AvatarToolkit_PT_OptimizationPanel(bpy.types.Panel): class AvatarToolkit_PT_OptimizationPanel(bpy.types.Panel):
bl_label = t("Optimization.label") bl_label = t("Optimization.label")
bl_idname = "OBJECT_PT_avatar_toolkit_optimization" bl_idname = "OBJECT_PT_avatar_toolkit_optimization"
@@ -17,7 +15,7 @@ class AvatarToolkit_PT_OptimizationPanel(bpy.types.Panel):
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 2 bl_order = 2
def draw(self: bpy.types.Panel, context: bpy.types.Context): def draw(self, context: bpy.types.Context):
layout = self.layout layout = self.layout
armature = get_selected_armature(context) armature = get_selected_armature(context)
@@ -46,5 +44,3 @@ class AvatarToolkit_PT_OptimizationPanel(bpy.types.Panel):
else: else:
layout.label(text=t("Optimization.select_armature"), icon='ERROR') layout.label(text=t("Optimization.select_armature"), icon='ERROR')
+9 -14
View File
@@ -1,17 +1,15 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME from ..core.exporters.export_resonite import AvatarToolKit_OT_ExportResonite
from ..core.export_resonite import AvatarToolKit_OT_ExportResonite
from bpy.types import Context, Mesh, Panel, Operator from bpy.types import Context, Mesh, Panel, Operator
from ..functions.translations import t from ..core.translations import t
from ..core.common import get_selected_armature
from ..core.import_pmx import import_pmx
from ..core.import_pmd import import_pmd
from ..functions.import_anything import AvatarToolKit_OT_ImportAnyModel from ..functions.import_anything import AvatarToolKit_OT_ImportAnyModel
from ..functions.armature_modifying import AvatarToolkit_OT_StartPoseMode, AvatarToolkit_OT_StopPoseMode, AvatarToolkit_OT_ApplyPoseAsRest, AvatarToolkit_OT_ApplyPoseAsShapekey from ..functions.armature_modifying import (AvatarToolkit_OT_StartPoseMode,
from ..core.common import get_selected_armature, set_selected_armature, get_all_meshes AvatarToolkit_OT_StopPoseMode,
AvatarToolkit_OT_ApplyPoseAsRest,
AvatarToolkit_OT_ApplyPoseAsShapekey)
@register_wrap
class AvatarToolkitQuickAccessPanel(Panel): class AvatarToolkitQuickAccessPanel(Panel):
bl_label = t("Quick_Access.label") bl_label = t("Quick_Access.label")
bl_idname = "OBJECT_PT_avatar_toolkit_quick_access" bl_idname = "OBJECT_PT_avatar_toolkit_quick_access"
@@ -28,7 +26,7 @@ class AvatarToolkitQuickAccessPanel(Panel):
layout.separator(factor=1.0) layout.separator(factor=1.0)
layout.label(text=t("Quick_Access.select_armature"), icon='ARMATURE_DATA') layout.label(text=t("Quick_Access.select_armature"), icon='ARMATURE_DATA')
layout.prop(context.scene, "selected_armature", text="") layout.prop(context.scene.avatar_toolkit, "selected_armature", text="")
layout.separator(factor=1.0) layout.separator(factor=1.0)
@@ -58,8 +56,6 @@ class AvatarToolkitQuickAccessPanel(Panel):
row.scale_y = 1.2 row.scale_y = 1.2
row.operator(AvatarToolkit_OT_StartPoseMode.bl_idname, text=t("Quick_Access.start_pose_mode.label"), icon='POSE_HLT') row.operator(AvatarToolkit_OT_StartPoseMode.bl_idname, text=t("Quick_Access.start_pose_mode.label"), icon='POSE_HLT')
@register_wrap
class AVATAR_TOOLKIT_OT_ExportMenu(bpy.types.Operator): class AVATAR_TOOLKIT_OT_ExportMenu(bpy.types.Operator):
bl_idname = "avatar_toolkit.export_menu" bl_idname = "avatar_toolkit.export_menu"
bl_label = t("Quick_Access.export_menu.label") bl_label = t("Quick_Access.export_menu.label")
@@ -82,7 +78,6 @@ class AVATAR_TOOLKIT_OT_ExportMenu(bpy.types.Operator):
layout.operator(AvatarToolKit_OT_ExportResonite.bl_idname, text=t("Quick_Access.select_export_resonite.label"), icon='SCENE_DATA') layout.operator(AvatarToolKit_OT_ExportResonite.bl_idname, text=t("Quick_Access.select_export_resonite.label"), icon='SCENE_DATA')
layout.operator(AVATAR_TOOLKIT_OT_ExportFbx.bl_idname, text=t("Quick_Access.export_fbx.label"), icon='OBJECT_DATA') layout.operator(AVATAR_TOOLKIT_OT_ExportFbx.bl_idname, text=t("Quick_Access.export_fbx.label"), icon='OBJECT_DATA')
@register_wrap
class AVATAR_TOOLKIT_OT_ExportFbx(bpy.types.Operator): class AVATAR_TOOLKIT_OT_ExportFbx(bpy.types.Operator):
bl_idname = 'avatar_toolkit.export_fbx' bl_idname = 'avatar_toolkit.export_fbx'
bl_label = t("Quick_Access.export_fbx.label") bl_label = t("Quick_Access.export_fbx.label")
+5 -9
View File
@@ -1,9 +1,7 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME from ..core.translations import t
from ..functions.translations import t
@register_wrap
class AvatarToolkitSettingsPanel(bpy.types.Panel): class AvatarToolkitSettingsPanel(bpy.types.Panel):
bl_label = t("Settings.label") bl_label = t("Settings.label")
bl_idname = "OBJECT_PT_avatar_toolkit_settings" bl_idname = "OBJECT_PT_avatar_toolkit_settings"
@@ -17,9 +15,8 @@ class AvatarToolkitSettingsPanel(bpy.types.Panel):
layout = self.layout layout = self.layout
layout.label(text=t("Settings.language.label")) layout.label(text=t("Settings.language.label"))
layout.prop(context.scene, "avatar_toolkit_language", text="", icon='WORLD') layout.prop(context.scene.avatar_toolkit, "language", text="", icon='WORLD')
@register_wrap
class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator): class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
bl_idname = "avatar_toolkit.translation_restart_popup" bl_idname = "avatar_toolkit.translation_restart_popup"
bl_label = t("Settings.translation_restart_popup.label") bl_label = t("Settings.translation_restart_popup.label")
@@ -27,9 +24,9 @@ class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
bl_options = {'INTERNAL'} bl_options = {'INTERNAL'}
def execute(self, context): def execute(self, context):
if context.scene.avatar_toolkit_language_changed: if context.scene.avatar_toolkit.language_changed:
bpy.ops.script.reload() bpy.ops.script.reload()
context.scene.avatar_toolkit_language_changed = False context.scene.avatar_toolkit.language_changed = False
return {'FINISHED'} return {'FINISHED'}
def invoke(self, context, event): def invoke(self, context, event):
@@ -39,4 +36,3 @@ class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
layout = self.layout layout = self.layout
layout.label(text=t("Settings.translation_restart_popup.message1"), icon='INFO') layout.label(text=t("Settings.translation_restart_popup.message1"), icon='INFO')
layout.label(text=t("Settings.translation_restart_popup.message2"), icon='FILE_REFRESH') layout.label(text=t("Settings.translation_restart_popup.message2"), icon='FILE_REFRESH')
+12 -12
View File
@@ -1,17 +1,20 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from bpy.types import Context from bpy.types import Context
from ..functions.digitigrade_legs import AvatarToolKit_OT_CreateDigitigradeLegs from ..functions.digitigrade_legs import AvatarToolKit_OT_CreateDigitigradeLegs
from ..functions.resonite_functions import AvatarToolKit_OT_ConvertToResonite from ..functions.resonite_functions import AvatarToolKit_OT_ConvertToResonite
from ..functions.translations import t from ..core.translations import t
from ..core.common import get_selected_armature from ..core.common import get_selected_armature
from ..functions.mesh_tools import AvatarToolkit_OT_RemoveUnusedShapekeys from ..functions.mesh_tools import AvatarToolkit_OT_RemoveUnusedShapekeys
from ..functions.additional_tools import AvatarToolKit_OT_ApplyTransforms, AvatarToolKit_OT_ConnectBones, AvatarToolKit_OT_DeleteBoneConstraints, AvatarToolKit_OT_SeparateByMaterials, AvatarToolKit_OT_SeparateByLooseParts from ..functions.additional_tools import (AvatarToolKit_OT_ApplyTransforms,
from ..functions.armature_modifying import AvatarToolkit_OT_RemoveZeroWeightBones, AvatarToolkit_OT_MergeBonesToActive, AvatarToolkit_OT_MergeBonesToParents AvatarToolKit_OT_ConnectBones,
from ..functions.rigify_functions import AvatarToolKit_OT_ConvertRigifyToUnity AvatarToolKit_OT_DeleteBoneConstraints,
AvatarToolKit_OT_SeparateByMaterials,
AvatarToolKit_OT_SeparateByLooseParts)
from ..functions.armature_modifying import (AvatarToolkit_OT_RemoveZeroWeightBones,
AvatarToolkit_OT_MergeBonesToActive,
AvatarToolkit_OT_MergeBonesToParents)
@register_wrap
class AvatarToolkit_PT_ToolsPanel(bpy.types.Panel): class AvatarToolkit_PT_ToolsPanel(bpy.types.Panel):
bl_label = t("Tools.label") bl_label = t("Tools.label")
bl_idname = "OBJECT_PT_avatar_toolkit_tools" bl_idname = "OBJECT_PT_avatar_toolkit_tools"
@@ -33,10 +36,6 @@ class AvatarToolkit_PT_ToolsPanel(bpy.types.Panel):
row.scale_y = 1.5 row.scale_y = 1.5
row.operator(AvatarToolKit_OT_ConvertToResonite.bl_idname, text=t("Tools.convert_to_resonite.label"), icon='SCENE_DATA') row.operator(AvatarToolKit_OT_ConvertToResonite.bl_idname, text=t("Tools.convert_to_resonite.label"), icon='SCENE_DATA')
row = layout.row(align=True)
row.scale_y = 1.5
row.operator(AvatarToolKit_OT_ConvertRigifyToUnity.bl_idname, text=t("Tools.convert_rigify_to_unity.label"), icon='ARMATURE_DATA')
layout.separator(factor=1.0) layout.separator(factor=1.0)
layout.label(text=t("Tools.separate_by.label"), icon='MESH_DATA') layout.label(text=t("Tools.separate_by.label"), icon='MESH_DATA')
@@ -62,7 +61,7 @@ class AvatarToolkit_PT_ToolsPanel(bpy.types.Panel):
row.operator(AvatarToolKit_OT_DeleteBoneConstraints.bl_idname, text=t("Tools.delete_bone_constraints.label"), icon='CONSTRAINT_BONE') row.operator(AvatarToolKit_OT_DeleteBoneConstraints.bl_idname, text=t("Tools.delete_bone_constraints.label"), icon='CONSTRAINT_BONE')
row = layout.row() row = layout.row()
row.prop(context.scene, "merge_twist_bones") row.prop(context.scene.avatar_toolkit, "merge_twist_bones")
layout.separator(factor=1.0) layout.separator(factor=1.0)
@@ -74,3 +73,4 @@ class AvatarToolkit_PT_ToolsPanel(bpy.types.Panel):
layout.separator(factor=1.0) layout.separator(factor=1.0)
else: else:
layout.label(text=t("Tools.select_armature"), icon='ERROR') layout.label(text=t("Tools.select_armature"), icon='ERROR')
+2 -5
View File
@@ -1,9 +1,7 @@
import bpy import bpy
from ..core.register import register_wrap from ..core.translations import t
from ..functions.translations import t from .main_panel import draw_title
from .panel import draw_title
@register_wrap
class UVTools_PT_MainPanel(bpy.types.Panel): class UVTools_PT_MainPanel(bpy.types.Panel):
bl_label = t("AvatarToolkit.label") bl_label = t("AvatarToolkit.label")
bl_idname = "OBJECT_PT_avatar_toolkit_uv" bl_idname = "OBJECT_PT_avatar_toolkit_uv"
@@ -13,5 +11,4 @@ class UVTools_PT_MainPanel(bpy.types.Panel):
def draw(self: bpy.types.Panel, context: bpy.types.Context): def draw(self: bpy.types.Panel, context: bpy.types.Context):
layout = self.layout layout = self.layout
draw_title(self) draw_title(self)
+2 -6
View File
@@ -1,12 +1,9 @@
import bpy import bpy
from ..core.register import register_wrap from ..core.translations import t
from ..functions.translations import t
from ..functions.uv_tools import AvatarToolkit_OT_AlignUVEdgesToTarget from ..functions.uv_tools import AvatarToolkit_OT_AlignUVEdgesToTarget
from .panel import draw_title from .main_panel import draw_title
from .uv_panel import UVTools_PT_MainPanel from .uv_panel import UVTools_PT_MainPanel
@register_wrap
class UVTools_PT_Tools(bpy.types.Panel): class UVTools_PT_Tools(bpy.types.Panel):
bl_label = t("Tools.label") bl_label = t("Tools.label")
bl_idname = "OBJECT_PT_avatar_toolkit_uv_tools" bl_idname = "OBJECT_PT_avatar_toolkit_uv_tools"
@@ -18,6 +15,5 @@ class UVTools_PT_Tools(bpy.types.Panel):
def draw(self, context: bpy.types.Context): def draw(self, context: bpy.types.Context):
layout = self.layout layout = self.layout
row = layout.row(align=True) row = layout.row(align=True)
row.operator(AvatarToolkit_OT_AlignUVEdgesToTarget.bl_idname, text=t("avatar_toolkit.align_uv_edges_to_target.label"), icon='GP_MULTIFRAME_EDITING') row.operator(AvatarToolkit_OT_AlignUVEdgesToTarget.bl_idname, text=t("avatar_toolkit.align_uv_edges_to_target.label"), icon='GP_MULTIFRAME_EDITING')
+8 -11
View File
@@ -1,11 +1,9 @@
import bpy import bpy
from ..core.register import register_wrap from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..functions.viseme import AvatarToolKit_OT_AutoVisemeButton from ..functions.viseme import AvatarToolKit_OT_AutoVisemeButton
from ..functions.translations import t from ..core.translations import t
from ..core.common import get_selected_armature from ..core.common import get_selected_armature
@register_wrap
class AvatarToolkitVisemePanel(bpy.types.Panel): class AvatarToolkitVisemePanel(bpy.types.Panel):
bl_label = t("VisemePanel.label") bl_label = t("VisemePanel.label")
bl_idname = "OBJECT_PT_avatar_toolkit_viseme" bl_idname = "OBJECT_PT_avatar_toolkit_viseme"
@@ -24,20 +22,20 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
layout.separator(factor=0.5) layout.separator(factor=0.5)
layout.prop(context.scene, "selected_mesh", text=t("VisemePanel.select_mesh"), icon='OUTLINER_OB_MESH') layout.prop(context.scene.avatar_toolkit, "selected_mesh", text=t("VisemePanel.select_mesh"), icon='OUTLINER_OB_MESH')
mesh = bpy.data.objects.get(context.scene.selected_mesh) mesh = bpy.data.objects.get(context.scene.avatar_toolkit.selected_mesh)
if mesh and mesh.type == 'MESH': if mesh and mesh.type == 'MESH':
if mesh.data.shape_keys: if mesh.data.shape_keys:
box = layout.box() box = layout.box()
col = box.column(align=True) col = box.column(align=True)
col.prop_search(context.scene, "avatar_toolkit_mouth_a", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_a.label'), icon='SHAPEKEY_DATA') col.prop_search(context.scene.avatar_toolkit, "mouth_a", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_a.label'), icon='SHAPEKEY_DATA')
col.prop_search(context.scene, "avatar_toolkit_mouth_o", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_o.label'), icon='SHAPEKEY_DATA') col.prop_search(context.scene.avatar_toolkit, "mouth_o", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_o.label'), icon='SHAPEKEY_DATA')
col.prop_search(context.scene, "avatar_toolkit_mouth_ch", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_ch.label'), icon='SHAPEKEY_DATA') col.prop_search(context.scene.avatar_toolkit, "mouth_ch", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_ch.label'), icon='SHAPEKEY_DATA')
layout.separator(factor=0.5) layout.separator(factor=0.5)
layout.prop(context.scene, 'avatar_toolkit_shape_intensity', text=t('VisemePanel.shape_intensity'), icon='FORCE_LENNARDJONES') layout.prop(context.scene.avatar_toolkit, 'shape_intensity', text=t('VisemePanel.shape_intensity'), icon='FORCE_LENNARDJONES')
layout.separator(factor=1.0) layout.separator(factor=1.0)
@@ -53,4 +51,3 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
layout.separator(factor=1.0) layout.separator(factor=1.0)
layout.label(text=t('VisemePanel.info.selectMesh'), icon='HELP') layout.label(text=t('VisemePanel.info.selectMesh'), icon='HELP')