Merge pull request #38 from Yusarina/improvements

Improvements
This commit is contained in:
Onan Chew
2024-07-25 20:35:46 -04:00
committed by GitHub
16 changed files with 463 additions and 335 deletions
+23 -1
View File
@@ -6,10 +6,11 @@ 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
from bpy.props import PointerProperty from bpy.props import PointerProperty, IntProperty, StringProperty
from bpy.utils import register_class from bpy.utils import register_class
@@ -240,3 +241,24 @@ def get_shapekeys(mesh: Object, prefix: str = '') -> List[tuple]:
if not has_shapekeys(mesh): if not has_shapekeys(mesh):
return [] return []
return [(key.name, key.name, key.name) for key in mesh.data.shape_keys.key_blocks if key.name != 'Basis' and key.name.startswith(prefix)] return [(key.name, key.name, key.name) for key in mesh.data.shape_keys.key_blocks if key.name != 'Basis' and key.name.startswith(prefix)]
def remove_default_objects():
for obj in bpy.data.objects:
if obj.name in ["Camera", "Light", "Cube"]:
bpy.data.objects.remove(obj, do_unlink=True)
def init_progress(context, steps):
context.window_manager.progress_begin(0, 100)
context.scene.avatar_toolkit_progress_steps = steps
context.scene.avatar_toolkit_progress_current = 0
def update_progress(self, context, message):
context.scene.avatar_toolkit_progress_current += 1
progress = (context.scene.avatar_toolkit_progress_current / context.scene.avatar_toolkit_progress_steps) * 100
context.window_manager.progress_update(progress)
context.area.header_text_set(message)
self.report({'INFO'}, message)
def finish_progress(context):
context.window_manager.progress_end()
context.area.header_text_set(None)
+3
View File
@@ -26,6 +26,9 @@ def register() -> None:
bpy.types.Scene.avatar_toolkit_language_changed = bpy.props.BoolProperty(default=False) bpy.types.Scene.avatar_toolkit_language_changed = bpy.props.BoolProperty(default=False)
bpy.types.Scene.avatar_toolkit_progress_steps = bpy.props.IntProperty(default=0)
bpy.types.Scene.avatar_toolkit_progress_current = bpy.props.IntProperty(default=0)
bpy.types.Scene.mouth_a = bpy.props.StringProperty( bpy.types.Scene.mouth_a = bpy.props.StringProperty(
name=t("VisemePanel.mouth_a.label"), name=t("VisemePanel.mouth_a.label"),
description=t("VisemePanel.mouth_a.desc") description=t("VisemePanel.mouth_a.desc")
+35 -29
View File
@@ -1,8 +1,8 @@
import bpy import bpy
import re import re
from typing import List, Tuple, Optional, Set from typing import List, Tuple, Optional, Set, Dict
from bpy.types import Material, Operator, Context, Object 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 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.register import register_wrap
from ..functions.translations import t from ..functions.translations import t
@@ -21,31 +21,30 @@ def copy_tex_nodes(mat1: Material, mat2: Material) -> None:
node2.mapping = node1.mapping node2.mapping = node1.mapping
node2.projection = node1.projection node2.projection = node1.projection
def consolidate_textures(mat1: Material, mat2: Material) -> None: def consolidate_textures(node_tree1: NodeTree, node_tree2: NodeTree) -> None:
if mat1.node_tree and mat2.node_tree: for node1 in node_tree1.nodes:
for node1 in mat1.node_tree.nodes:
if node1.type == 'TEX_IMAGE': if node1.type == 'TEX_IMAGE':
if node1.node_tree: for node2 in node_tree2.nodes:
consolidate_textures(node1.node_tree, mat2.node_tree)
for node2 in mat2.node_tree.nodes:
if (node2.type == 'TEX_IMAGE' and if (node2.type == 'TEX_IMAGE' and
node1.image == node2.image): node1.image == node2.image):
consolidate_nodes(node1, node2) consolidate_nodes(node1, node2)
node2.image = node1.image node2.image = node1.image
copy_tex_nodes(mat1, mat2) elif node1.type == 'GROUP':
if node1.node_tree and node2.node_tree:
consolidate_textures(node1.node_tree, node2.node_tree)
def color_match(col1: Tuple[float, float, float, float], col2: Tuple[float, float, float, float], tolerance: float = 0.01) -> bool: def color_match(col1: Tuple[float, float, float, float], col2: Tuple[float, float, float, float], tolerance: float = 0.01) -> bool:
return abs(col1[0] - col2[0]) < tolerance return all(abs(c1 - c2) < tolerance for c1, c2 in zip(col1, col2))
def materials_match(mat1: Material, mat2: Material, tolerance: float = 0.01) -> bool: def materials_match(mat1: Material, mat2: Material, tolerance: float = 0.01) -> bool:
if not color_match(mat1.diffuse_color, mat2.diffuse_color, tolerance): if not color_match(mat1.diffuse_color, mat2.diffuse_color, tolerance):
return False return False
if mat1.roughness != mat2.roughness: if abs(mat1.roughness - mat2.roughness) > tolerance:
return False return False
consolidate_textures(mat1, mat2) if mat1.node_tree and mat2.node_tree:
consolidate_textures(mat1.node_tree, mat2.node_tree)
return True return True
@@ -53,9 +52,6 @@ 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
def report_consolidated(self: Operator, num_combined: int) -> None:
self.report({'INFO'}, f"Combined {num_combined} materials")
@register_wrap @register_wrap
class CombineMaterials(Operator): class CombineMaterials(Operator):
bl_idname = "avatar_toolkit.combine_materials" bl_idname = "avatar_toolkit.combine_materials"
@@ -82,11 +78,23 @@ class CombineMaterials(Operator):
self.report({'WARNING'}, t("Optimization.no_meshes_found")) self.report({'WARNING'}, t("Optimization.no_meshes_found"))
return {'CANCELLED'} return {'CANCELLED'}
init_progress(context, 5) # 5 steps in total
update_progress(self, context, t("Optimization.consolidating_materials"))
self.consolidate_materials(meshes) self.consolidate_materials(meshes)
self.remove_unused_materials()
self.cleanmatslots() update_progress(self, context, t("Optimization.cleaning_material_slots"))
self.clean_material_slots(meshes)
update_progress(self, context, t("Optimization.cleaning_material_names"))
self.clean_material_names() self.clean_material_names()
update_progress(self, context, t("Optimization.clearing_unused_data"))
self.clear_unused_data_blocks()
update_progress(self, context, t("Optimization.finalizing"))
finish_progress(context)
return {'FINISHED'} return {'FINISHED'}
def consolidate_materials(self, meshes: List[Object]) -> None: def consolidate_materials(self, meshes: List[Object]) -> None:
@@ -102,25 +110,19 @@ class CombineMaterials(Operator):
base_mat: Material = mat_mapping[base_name] base_mat: Material = mat_mapping[base_name]
try: try:
if materials_match(base_mat, mat): if materials_match(base_mat, mat):
consolidate_textures(base_mat, mat) consolidate_textures(base_mat.node_tree, mat.node_tree)
num_combined += 1 num_combined += 1
slot.material = base_mat slot.material = base_mat
except AttributeError: except AttributeError:
# Skip this material if there's an attribute mismatch self.report({'WARNING'}, t("Optimization.material_attribute_mismatch").format(material_name=mat.name))
continue continue
else: else:
mat_mapping[base_name] = mat mat_mapping[base_name] = mat
self.report({'INFO'}, t("Optimization.materials_combined").format(num_combined=num_combined)) self.report({'INFO'}, t("Optimization.materials_combined").format(num_combined=num_combined))
def remove_unused_materials(self) -> None: def clean_material_slots(self, meshes: List[Object]) -> None:
for mat in bpy.data.materials: for obj in meshes:
if not any(obj for obj in bpy.data.objects if obj.material_slots and mat.name in obj.material_slots):
bpy.data.materials.remove(mat, do_unlink=True)
def cleanmatslots(self) -> None:
for obj in bpy.data.objects:
if obj.type == 'MESH':
obj.select_set(True) obj.select_set(True)
bpy.context.view_layer.objects.active = obj bpy.context.view_layer.objects.active = obj
bpy.ops.object.material_slot_remove_unused() bpy.ops.object.material_slot_remove_unused()
@@ -130,3 +132,7 @@ class CombineMaterials(Operator):
for obj in bpy.data.objects: for obj in bpy.data.objects:
if obj.type == 'MESH': if obj.type == 'MESH':
clean_material_names(obj) clean_material_names(obj)
def clear_unused_data_blocks(self) -> None:
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
+2 -1
View File
@@ -3,6 +3,7 @@ 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.register import register_wrap
from ..core.importer import imports, import_types from ..core.importer import imports, import_types
from ..core.common import remove_default_objects
from ..functions.translations import t from ..functions.translations import t
import pathlib import pathlib
import os import os
@@ -23,7 +24,7 @@ class ImportAnyModel(Operator, ImportHelper):
#since I wrote this myself, a bit more efficent than cats. mostly - @989onan #since I wrote this myself, a bit more efficent than cats. mostly - @989onan
def execute(self, context: bpy.types.Context): def execute(self, context: bpy.types.Context):
file_grouping_dict: dict[str, list[dict[str,str]]] = dict()#group our files so our importers can import them together. in the case of OBJ+MTL and others that need grouped files, this is extremely important. file_grouping_dict: dict[str, list[dict[str,str]]] = dict()#group our files so our importers can import them together. in the case of OBJ+MTL and others that need grouped files, this is extremely important.
common.remove_default_objects()
#check if we are importing multiple files #check if we are importing multiple files
is_multi = False is_multi = False
try: try:
+52 -7
View File
@@ -2,7 +2,7 @@ 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.register import register_wrap from ..core.register import register_wrap
from ..core.common import fix_uv_coordinates, get_selected_armature, is_valid_armature, select_current_armature, get_all_meshes from ..core.common import fix_uv_coordinates, get_selected_armature, is_valid_armature, select_current_armature, get_all_meshes, init_progress, update_progress, finish_progress
from ..functions.translations import t from ..functions.translations import t
@register_wrap @register_wrap
@@ -18,34 +18,58 @@ class JoinAllMeshes(Operator):
return armature is not None and is_valid_armature(armature) return armature is not None and is_valid_armature(armature)
def execute(self, context: Context) -> Set[str]: def execute(self, context: Context) -> Set[str]:
try:
self.join_all_meshes(context) self.join_all_meshes(context)
return {'FINISHED'} return {'FINISHED'}
except Exception as e:
self.report({'ERROR'}, f"{t('Optimization.join_error')}: {str(e)}")
return {'CANCELLED'}
def join_all_meshes(self, context: Context) -> None: def join_all_meshes(self, context: Context) -> None:
if not select_current_armature(context): if not select_current_armature(context):
self.report({'WARNING'}, t("Optimization.no_armature_selected")) raise ValueError(t("Optimization.no_armature_selected"))
return
armature = get_selected_armature(context) armature = get_selected_armature(context)
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
meshes: List[Object] = get_all_meshes(context) meshes: List[Object] = get_all_meshes(context)
if not meshes:
raise ValueError(t("Optimization.no_meshes_found"))
init_progress(context, 5) # 5 steps in total
update_progress(self, context, t("Optimization.selecting_meshes"))
for mesh in meshes: for mesh in meshes:
mesh.select_set(True) mesh.select_set(True)
if bpy.context.selected_objects: if bpy.context.selected_objects:
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0] bpy.context.view_layer.objects.active = bpy.context.selected_objects[0]
update_progress(self, context, t("Optimization.joining_meshes"))
try:
bpy.ops.object.join() bpy.ops.object.join()
except RuntimeError as e:
raise RuntimeError(f"{t('Optimization.join_operation_failed')}: {str(e)}")
update_progress(self, context, t("Optimization.applying_transforms"))
try:
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
except RuntimeError as e:
raise RuntimeError(f"{t('Optimization.transform_apply_failed')}: {str(e)}")
update_progress(self, context, t("Optimization.fixing_uv_coordinates"))
fix_uv_coordinates(context) fix_uv_coordinates(context)
update_progress(self, context, t("Optimization.finalizing"))
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
self.report({'INFO'}, t("Optimization.meshes_joined")) self.report({'INFO'}, t("Optimization.meshes_joined"))
else: else:
self.report({'WARNING'}, t("Optimization.no_mesh_selected")) raise ValueError(t("Optimization.no_mesh_selected"))
context.view_layer.objects.active = armature context.view_layer.objects.active = armature
finish_progress(context)
@register_wrap @register_wrap
class JoinSelectedMeshes(Operator): class JoinSelectedMeshes(Operator):
@@ -59,29 +83,50 @@ class JoinSelectedMeshes(Operator):
return context.mode == 'OBJECT' and len([obj for obj in context.selected_objects if obj.type == 'MESH']) > 1 return context.mode == 'OBJECT' and len([obj for obj in context.selected_objects if obj.type == 'MESH']) > 1
def execute(self, context: Context) -> Set[str]: def execute(self, context: Context) -> Set[str]:
try:
self.join_selected_meshes(context) self.join_selected_meshes(context)
return {'FINISHED'} return {'FINISHED'}
except Exception as e:
self.report({'ERROR'}, f"{t('Optimization.join_error')}: {str(e)}")
return {'CANCELLED'}
def join_selected_meshes(self, context: Context) -> None: def join_selected_meshes(self, context: Context) -> None:
selected_objects: List[Object] = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH'] selected_objects: List[Object] = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
if len(selected_objects) < 2: if len(selected_objects) < 2:
self.report({'WARNING'}, t("Optimization.select_at_least_two_meshes")) raise ValueError(t("Optimization.select_at_least_two_meshes"))
return
init_progress(context, 5) # 5 steps in total
update_progress(self, context, t("Optimization.preparing_meshes"))
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
update_progress(self, context, t("Optimization.selecting_meshes"))
for obj in selected_objects: for obj in selected_objects:
obj.select_set(True) obj.select_set(True)
if bpy.context.selected_objects: if bpy.context.selected_objects:
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0] bpy.context.view_layer.objects.active = bpy.context.selected_objects[0]
update_progress(self, context, t("Optimization.joining_meshes"))
try:
bpy.ops.object.join() bpy.ops.object.join()
except RuntimeError as e:
raise RuntimeError(f"{t('Optimization.join_operation_failed')}: {str(e)}")
update_progress(self, context, t("Optimization.applying_transforms"))
try:
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
except RuntimeError as e:
raise RuntimeError(f"{t('Optimization.transform_apply_failed')}: {str(e)}")
update_progress(self, context, t("Optimization.fixing_uv_coordinates"))
fix_uv_coordinates(context) fix_uv_coordinates(context)
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
self.report({'INFO'}, t("Optimization.selected_meshes_joined")) self.report({'INFO'}, t("Optimization.selected_meshes_joined"))
else: else:
self.report({'WARNING'}, t("Optimization.no_mesh_selected")) raise ValueError(t("Optimization.no_mesh_selected"))
finish_progress(context)
+7 -7
View File
@@ -30,7 +30,7 @@ def load_translations() -> bool:
languages.append(lang) languages.append(lang)
language_index = get_preference("language", 0) language_index = get_preference("language", 0)
print(f"Loading translations for language index: {language_index}") # Debug print # print(f"Loading translations for language index: {language_index}") # Debug print
if language_index == 0: # "auto" if language_index == 0: # "auto"
language = bpy.context.preferences.view.language language = bpy.context.preferences.view.language
@@ -40,27 +40,27 @@ def load_translations() -> bool:
except IndexError: except IndexError:
language = bpy.context.preferences.view.language language = bpy.context.preferences.view.language
print(f"Selected language: {language}") # Debug print # print(f"Selected language: {language}") # Debug print
translation_file: str = os.path.join(translations_dir, language + ".json") translation_file: str = os.path.join(translations_dir, language + ".json")
if os.path.exists(translation_file): if os.path.exists(translation_file):
with open(translation_file, 'r', encoding='utf-8') as file: with open(translation_file, 'r', encoding='utf-8') as file:
dictionary = json.load(file)["messages"] dictionary = json.load(file)["messages"]
print(f"Loaded translations: {dictionary}") # Debug print # print(f"Loaded translations: {dictionary}") # Debug print
else: else:
custom_language: str = language.split("_")[0] custom_language: str = language.split("_")[0]
custom_translation_file: str = os.path.join(translations_dir, custom_language + ".json") custom_translation_file: str = os.path.join(translations_dir, custom_language + ".json")
if os.path.exists(custom_translation_file): if os.path.exists(custom_translation_file):
with open(custom_translation_file, 'r', encoding='utf-8') as file: with open(custom_translation_file, 'r', encoding='utf-8') as file:
dictionary = json.load(file)["messages"] dictionary = json.load(file)["messages"]
print(f"Loaded custom translations: {dictionary}") # Debug print # print(f"Loaded custom translations: {dictionary}") # Debug print
else: else:
print(f"Translation file not found for language: {language}") print(f"Translation file not found for language: {language}")
default_file: str = os.path.join(translations_dir, "en_US.json") default_file: str = os.path.join(translations_dir, "en_US.json")
if os.path.exists(default_file): if os.path.exists(default_file):
with open(default_file, 'r', encoding='utf-8') as file: with open(default_file, 'r', encoding='utf-8') as file:
dictionary = json.load(file)["messages"] dictionary = json.load(file)["messages"]
print(f"Loaded default translations: {dictionary}") # Debug print # print(f"Loaded default translations: {dictionary}") # Debug print
else: else:
print("Default translation file 'en_US.json' not found.") print("Default translation file 'en_US.json' not found.")
@@ -72,7 +72,7 @@ def t(phrase: str, default: str = None) -> str:
if verbose: if verbose:
print(f'Warning: Unknown phrase: {phrase}') print(f'Warning: Unknown phrase: {phrase}')
return default if default is not None else phrase return default if default is not None else phrase
print(f"Translating '{phrase}' to '{output}'") # Debug print # print(f"Translating '{phrase}' to '{output}'") # Debug print
return output return output
def get_language_display_name(lang: str) -> str: def get_language_display_name(lang: str) -> str:
@@ -93,5 +93,5 @@ def update_language(self, context):
bpy.ops.avatar_toolkit.translation_restart_popup('INVOKE_DEFAULT') bpy.ops.avatar_toolkit.translation_restart_popup('INVOKE_DEFAULT')
# Initial load of translations # Initial load of translations
print("Performing initial load of translations") # Debug print # print("Performing initial load of translations") # Debug print
load_translations() load_translations()
+20 -23
View File
@@ -3,7 +3,7 @@ from ..core import common
from ..core.register import register_wrap from ..core.register import register_wrap
from ..functions.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 from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress
@register_wrap @register_wrap
class AutoVisemeButton(bpy.types.Operator): class AutoVisemeButton(bpy.types.Operator):
@@ -18,26 +18,32 @@ class AutoVisemeButton(bpy.types.Operator):
return armature is not None and is_valid_armature(armature) and get_all_meshes(context) return armature is not None and is_valid_armature(armature) and get_all_meshes(context)
def execute(self, context: bpy.types.Context) -> set: def execute(self, context: bpy.types.Context) -> set:
print("Starting viseme creation...") try:
mesh = bpy.data.objects.get(context.scene.selected_mesh) self.create_visemes(context)
if not mesh or not common.has_shapekeys(mesh): return {'FINISHED'}
self.report({'ERROR'}, t('AutoVisemeButton.error.noShapekeys')) except Exception as e:
self.report({'ERROR'}, str(e))
return {'CANCELLED'} return {'CANCELLED'}
# Remove existing VRC shape keys def create_visemes(self, context: bpy.types.Context) -> None:
init_progress(context, 5) # 5 main steps
update_progress(self, context, t("VisemePanel.start_viseme_creation"))
mesh = bpy.data.objects.get(context.scene.selected_mesh)
if not mesh or not common.has_shapekeys(mesh):
raise ValueError(t('AutoVisemeButton.error.noShapekeys'))
update_progress(self, context, t("VisemePanel.removing_existing_visemes"))
self.remove_existing_vrc_shapekeys(mesh) self.remove_existing_vrc_shapekeys(mesh)
shape_a = context.scene.mouth_a shape_a = context.scene.mouth_a
shape_o = context.scene.mouth_o shape_o = context.scene.mouth_o
shape_ch = context.scene.mouth_ch shape_ch = context.scene.mouth_ch
print(f"Selected shapes: A={shape_a}, O={shape_o}, CH={shape_ch}")
if shape_a == "Basis" or shape_o == "Basis" or shape_ch == "Basis": if shape_a == "Basis" or shape_o == "Basis" or shape_ch == "Basis":
self.report({'ERROR'}, t('AutoVisemeButton.error.selectShapekeys')) raise ValueError(t('AutoVisemeButton.error.selectShapekeys'))
return {'CANCELLED'}
# Create visemes update_progress(self, context, t("VisemePanel.creating_visemes"))
visemes: List[Tuple[str, List[Tuple[str, float]]]] = [ visemes: List[Tuple[str, List[Tuple[str, float]]]] = [
('vrc.v_aa', [(shape_a, 0.9998)]), ('vrc.v_aa', [(shape_a, 0.9998)]),
('vrc.v_ch', [(shape_ch, 0.9996)]), ('vrc.v_ch', [(shape_ch, 0.9996)]),
@@ -57,38 +63,29 @@ class AutoVisemeButton(bpy.types.Operator):
] ]
for viseme_name, shape_mix in visemes: for viseme_name, shape_mix in visemes:
print(f"Creating viseme: {viseme_name}")
self.create_viseme(mesh, viseme_name, shape_mix, context.scene.shape_intensity) self.create_viseme(mesh, viseme_name, shape_mix, context.scene.shape_intensity)
print("Sorting shape keys...") update_progress(self, context, t("VisemePanel.sorting_shapekeys"))
common.sort_shape_keys(mesh) common.sort_shape_keys(mesh)
self.report({'INFO'}, t('AutoVisemeButton.success')) update_progress(self, context, t("VisemePanel.viseme_creation_completed"))
return {'FINISHED'} finish_progress(context)
def create_viseme(self, mesh: bpy.types.Object, viseme_name: str, shape_mix: List[Tuple[str, float]], intensity: float) -> None: def create_viseme(self, mesh: bpy.types.Object, viseme_name: str, shape_mix: List[Tuple[str, float]], intensity: float) -> None:
print(f" Creating viseme: {viseme_name}")
shape_keys = mesh.data.shape_keys.key_blocks shape_keys = mesh.data.shape_keys.key_blocks
# Remove existing viseme if it exists
if viseme_name in shape_keys: if viseme_name in shape_keys:
print(f" Removing existing viseme: {viseme_name}")
mesh.shape_key_remove(shape_keys[viseme_name]) mesh.shape_key_remove(shape_keys[viseme_name])
# Create new viseme
new_key = mesh.shape_key_add(name=viseme_name, from_mix=False) new_key = mesh.shape_key_add(name=viseme_name, from_mix=False)
new_key.value = 0.0 new_key.value = 0.0
# Mix shapes
for shape_name, value in shape_mix: for shape_name, value in shape_mix:
if shape_name in shape_keys: if shape_name in shape_keys:
source_shape = shape_keys[shape_name] source_shape = shape_keys[shape_name]
print(f" Mixing shape: {shape_name} with value: {value * intensity}")
for i, vert in enumerate(new_key.data): for i, vert in enumerate(new_key.data):
vert.co += (source_shape.data[i].co - shape_keys['Basis'].data[i].co) * value * intensity vert.co += (source_shape.data[i].co - shape_keys['Basis'].data[i].co) * value * intensity
print(f" Viseme {viseme_name} created successfully.")
def remove_existing_vrc_shapekeys(self, mesh: bpy.types.Object) -> None: def remove_existing_vrc_shapekeys(self, mesh: bpy.types.Object) -> None:
vrc_prefixes = ['vrc.v_', 'vrc.blink_', 'vrc.lowerlid_'] vrc_prefixes = ['vrc.v_', 'vrc.blink_', 'vrc.lowerlid_']
shape_keys = mesh.data.shape_keys.key_blocks shape_keys = mesh.data.shape_keys.key_blocks
+125 -106
View File
@@ -1,120 +1,139 @@
{ {
"authors": ["Avatar Toolkit Team"], "authors": ["Avatar Toolkit Team"],
"messages": { "messages": {
"Language.auto": "Automatic",
"Language.en_US": "English",
"Language.ja_JP": "日本語",
"AvatarToolkit.label": "Avatar Toolkit",
"AvatarToolkit.welcome": "Welcome to Avatar Toolkit, a tool for",
"AvatarToolkit.description": "creating and editing avatars in blender,",
"AvatarToolkit.alpha_warning": "This is an early alpha version, so expect bugs and issues.",
"Quick_Access.label": "Quick Access",
"Quick_Access.import_export.label": "Import/Export",
"Quick_Access.options": "Quick Access Options",
"Quick_Access.import_menu.label": "Import Menu",
"Quick_Access.import": "Import",
"Quick_Access.export": "Export",
"Quick_Access.import_menu.desc": "Import a Model",
"Quick_Access.import_pmx": "Import PMX",
"Quick_Access.import_pmx.desc": "Import MMD PMX Model",
"Quick_Access.import_pmd": "Import PMD",
"Quick_Access.import_pmd.desc": "Import MMD PMD Model",
"Quick_Access.export_menu.label": "Export Menu",
"Quick_Access.export_menu.desc": "Export to a supported format",
"Quick_Access.select_export.label": "Select Export Method",
"Quick_Access.select_export_resonite.label": "Resonite",
"Quick_Access.export_fbx.label": "Export FBX",
"Quick_Access.export_fbx.desc": "Export the model as FBX",
"Quick_Access.import_success": "Model imported successfully",
"Export.resonite.label": "Export to Resonite",
"Export.resonite.desc": "Export a GLB with all animations and materials. For animation data see:",
"Optimization.label": "Optimization",
"Optimization.options.label": "Optimization Options",
"Optimization.combine_materials.label": "Combine Materials",
"Optimization.combine_materials.desc": "Combine similar materials to optimize the model",
"Optimization.join_all_meshes.label": "Join All Meshes",
"Optimization.join_all_meshes.desc": "Join all meshes into one",
"Optimization.join_selected_meshes.label": "Join Selected Meshes",
"Optimization.join_selected_meshes.desc": "Join all currently Selected Meshes into one",
"Optimization.remove_doubles_safely.label": "Remove Doubles Safely",
"Optimization.remove_doubles_safely.desc": "Remove doubles on all meshes, making sure not to fuse things like mouths together",
"Optimization.no_armature_selected": "No armature selected",
"Optimization.no_meshes_found": "No meshes found for the selected armature",
"Optimization.materials_combined": "Combined {num_combined} materials",
"Optimization.meshes_joined": "Meshes joined successfully",
"Optimization.no_mesh_selected": "No mesh objects selected",
"Optimization.select_at_least_two_meshes": "Please select at least two mesh objects",
"Optimization.selected_meshes_joined": "Selected meshes joined successfully",
"Optimization.vertex_excluded": "Shapekey has a moved vertex at index \"{index}\", excluding from double merging!",
"Optimization.processing_shapekey": "Processing shapekey \"{shapekeyname}\" on mesh \"{mesh_name}\"",
"Optimization.processing_mesh_no_shapekeys": "Processing mesh with no shapekeys named \"{mesh_name}\"",
"Optimization.remove_doubles_completed": "Remove doubles operation completed",
"Optimization.select_armature": "Please select an armature",
"Tools.select_armature": "Please select an armature",
"Tools.label": "Tools",
"Tools.tools_title.label": "Tools",
"Tools.convert_to_resonite.label": "Convert to Resonite",
"Tools.convert_to_resonite.desc": "Converts bone names on a model to names compatible with Resonite",
"Tools.create_digitigrade_legs.label": "Create Digitigrade Legs",
"Tools.create_digitigrade_legs.desc": "Create digitigrade legs from a selected bone chain",
"Tools.digitigrade_legs.error.bone_format": "Bone format incorrect! Please select a chain of 4 continuous bones!",
"Tools.digitigrade_legs.success": "Digitigrade legs created successfully",
"Tools.no_armature_selected": "No armature selected",
"Tools.bones_translated_with_fails": "Failed to translate {translate_bone_fails} bones to humanoid names. Adding \"<noik>\" to their names.",
"Tools.bones_translated_success": "Successfully translated all bones to humanoid names",
"Tools.import_any_model.desc": "Import any supported model, FBX, SMD, DMX, GLTF, PMD, PMX and more.",
"TextureAtlas.label": "Texture Atlasing",
"TextureAtlas.material_list_label": "Texture Atlas Material List Material",
"TextureAtlas.reload_list": "Reload Texture Atlas Material List",
"TextureAtlas.loaded_list": "Loaded Texture Atlas Material List",
"TextureAtlas.atlas_materials": "Atlas Materials",
"TextureAtlas.atlas_materials_desc": "Atlas materials to optimize the model",
"TextureAtlas.atlas_completed": "Texture atlas creation completed",
"TextureAtlas.atlas_error": "An error occurred during texture atlas creation",
"VisemePanel.label": "Visemes",
"VisemePanel.select_mesh": "Select Mesh",
"VisemePanel.mouth_a.label": "Mouth A",
"VisemePanel.mouth_a.desc": "The shapekey for the 'A' mouth shape",
"VisemePanel.mouth_o.label": "Mouth O",
"VisemePanel.mouth_o.desc": "The shapekey for the 'O' mouth shape",
"VisemePanel.mouth_ch.label": "Mouth CH",
"VisemePanel.mouth_ch.desc": "The shapekey for the 'CH' mouth shape",
"VisemePanel.shape_intensity": "Shape Intensity",
"VisemePanel.shape_intensity_desc": "The intensity of the viseme shapekeys",
"VisemePanel.create_visemes": "Create Visemes",
"VisemePanel.error.noMesh": "No mesh selected",
"VisemePanel.error.noShapekeys": "Selected mesh has no shape keys",
"VisemePanel.error.selectMesh": "Select a mesh to create visemes",
"VisemePanel.error.noArmature": "No armature selected",
"VisemePanel.info.selectMesh": "Select a mesh to create visemes",
"VisemePanel.start_viseme_creation": "Starting viseme creation...",
"VisemePanel.selected_shapes": "Selected shapes: A={shape_a}, O={shape_o}, CH={shape_ch}",
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
"VisemePanel.sorting_shapekeys": "Sorting shape keys...",
"VisemePanel.viseme_creation_completed": "Viseme creation completed.",
"VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}",
"VisemePanel.removing_existing_viseme": "Removing existing viseme: {viseme_name}",
"VisemePanel.mixing_shape": "Mixing shape: {shape_name} with value: {value}",
"VisemePanel.viseme_created_successfully": "Viseme {viseme_name} created successfully",
"AutoVisemeButton.label": "Create Visemes",
"AutoVisemeButton.desc": "Create visemes automatically, based on shape keys", "AutoVisemeButton.desc": "Create visemes automatically, based on shape keys",
"AutoVisemeButton.error.noShapekeys": "No shape keys found", "AutoVisemeButton.error.noShapekeys": "No shape keys found",
"AutoVisemeButton.error.selectShapekeys": "Please Select shape keys", "AutoVisemeButton.error.selectShapekeys": "Please Select shape keys",
"AutoVisemeButton.label": "Create Visemes",
"AutoVisemeButton.success": "Visemes created successfully", "AutoVisemeButton.success": "Visemes created successfully",
"AvatarToolkit.alpha_warning": "This is an early alpha version, so expect bugs and issues.",
"AvatarToolkit.description": "creating and editing avatars in blender,",
"AvatarToolkit.label": "Avatar Toolkit",
"AvatarToolkit.welcome": "Welcome to Avatar Toolkit, a tool for",
"Export.resonite.desc": "Export a GLB with all animations and materials. For animation data see:",
"Export.resonite.label": "Export to Resonite",
"Importer.export_resonite.desc": "Export to Resonite as a GLTF. Make sure your model is to scale in blender, and import as meters in Resonite.",
"Importer.export_resonite.label": "Export to Resonite",
"Importer.export_vrchat.desc": "Export to VRChat, may also work for ChilloutVR. Is similar to Cats export.",
"Importer.export_vrchat.label": "Export to VRChat",
"Importer.mmd_anim_importer.desc": "Import a MMD Animation (.vmd)",
"Importer.mmd_anim_importer.label": "MMD Animation",
"Importing.importer_search_term": "https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web",
"Importing.need_importer": "You do not have the required importer for the {extension} type! Opening web browser for importer search term...",
"Language.auto": "Automatic",
"Language.en_US": "English",
"Language.ja_JP": "日本語",
"Optimization.applying_transforms": "Applying transforms...",
"Optimization.cleaning_material_names": "Cleaning material names...",
"Optimization.cleaning_material_slots": "Cleaning material slots...",
"Optimization.clearing_unused_data": "Clearing unused data...",
"Optimization.combine_materials.desc": "Combine similar materials to reduce draw calls and improve performance",
"Optimization.combine_materials.label": "Combine Materials",
"Optimization.consolidating_materials": "Consolidating materials...",
"Optimization.finalizing": "Finalizing...",
"Optimization.fixing_uv_coordinates": "Fixing UV coordinates...",
"Optimization.join_all_meshes.desc": "Merge all meshes into a single object to reduce draw calls",
"Optimization.join_all_meshes.label": "Join All Meshes",
"Optimization.join_error": "Error during mesh joining",
"Optimization.join_operation_failed": "Join operation failed",
"Optimization.join_selected_meshes.desc": "Merge only the selected meshes into a single object",
"Optimization.join_selected_meshes.label": "Join Selected Meshes",
"Optimization.joinmeshes.label": "Join Meshes:",
"Optimization.joining_meshes": "Joining meshes...",
"Optimization.label": "Optimization",
"Optimization.material_attribute_mismatch": "Attribute mismatch in material {material_name}, skipping",
"Optimization.materials_combined": "Combined {num_combined} materials",
"Optimization.meshes_joined": "Meshes joined successfully",
"Optimization.no_armature_selected": "No armature selected",
"Optimization.no_mesh_selected": "No mesh objects selected",
"Optimization.no_meshes_found": "No meshes found for the selected armature",
"Optimization.options.label": "Optimization:",
"Optimization.preparing_meshes": "Preparing meshes...",
"Optimization.processing_mesh_no_shapekeys": "Processing mesh with no shapekeys named \"{mesh_name}\"",
"Optimization.processing_shapekey": "Processing shapekey \"{shapekeyname}\" on mesh \"{mesh_name}\"",
"Optimization.remove_doubles_completed": "Remove doubles operation completed",
"Optimization.remove_doubles_safely.desc": "Remove duplicate vertices while preserving important features like mouth shapes",
"Optimization.remove_doubles_safely.label": "Remove Doubles Safely",
"Optimization.select_armature": "Please select an armature",
"Optimization.select_at_least_two_meshes": "Please select at least two mesh objects",
"Optimization.selected_meshes_joined": "Selected meshes joined successfully",
"Optimization.selecting_meshes": "Selecting meshes...",
"Optimization.transform_apply_failed": "Transform apply failed",
"Optimization.vertex_excluded": "Shapekey has a moved vertex at index \"{index}\", excluding from double merging!",
"Quick_Access.export": "Export",
"Quick_Access.export_fbx.desc": "Export the model as FBX",
"Quick_Access.export_fbx.label": "Export FBX",
"Quick_Access.export_menu.desc": "Export to a supported format",
"Quick_Access.export_menu.label": "Export Menu",
"Quick_Access.import": "Import",
"Quick_Access.import_export.label": "Import/Export:",
"Quick_Access.import_menu.desc": "Import a Model",
"Quick_Access.import_menu.label": "Import Menu",
"Quick_Access.import_pmd": "Import PMD",
"Quick_Access.import_pmd.desc": "Import MMD PMD Model",
"Quick_Access.import_pmx": "Import PMX",
"Quick_Access.import_pmx.desc": "Import MMD PMX Model",
"Quick_Access.import_success": "Model imported successfully",
"Quick_Access.label": "Quick Access",
"Quick_Access.options": "Quick Access:",
"Quick_Access.select_armature": "Select Armature:",
"Quick_Access.select_export.label": "Select Export Method",
"Quick_Access.select_export_resonite.label": "Resonite",
"Settings.label": "Settings", "Settings.label": "Settings",
"Settings.language.label": "Language",
"Settings.language.desc": "Select the language for the addon's UI", "Settings.language.desc": "Select the language for the addon's UI",
"Settings.translation_restart_popup.label": "Translation Update", "Settings.language.label": "Language:",
"Settings.translation_restart_popup.description": "Information about translation updates", "Settings.translation_restart_popup.description": "Information about translation updates",
"Settings.translation_restart_popup.label": "Translation Update",
"Settings.translation_restart_popup.message1": "Some translations may not apply", "Settings.translation_restart_popup.message1": "Some translations may not apply",
"Settings.translation_restart_popup.message2": "until you restart Blender.", "Settings.translation_restart_popup.message2": "until you restart Blender.",
"Importing.need_importer": "You do not have the required importer for the {extension} type! Opening web browser for importer search term...", "TextureAtlas.atlas_completed": "Texture atlas creation completed",
"Importer.mmd_anim_importer.label": "MMD Animation", "TextureAtlas.atlas_error": "An error occurred during texture atlas creation",
"Importer.mmd_anim_importer.desc": "Import a MMD Animation (.vmd)", "TextureAtlas.atlas_materials": "Atlas Materials",
"Importing.importer_search_term": "https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web", "TextureAtlas.atlas_materials_desc": "Atlas materials to optimize the model",
"Importer.export_resonite.label": "Export to Resonite", "TextureAtlas.label": "Texture Atlasing",
"Importer.export_resonite.desc": "Export to Resonite as a GLTF. Make sure your model is to scale in blender, and import as meters in Resonite.", "TextureAtlas.loaded_list": "Loaded Texture Atlas Material List",
"Importer.export_vrchat.label": "Export to VRChat", "TextureAtlas.material_list_label": "Texture Atlas Material List Material",
"Importer.export_vrchat.desc": "Export to VRChat, may also work for ChilloutVR. Is similar to Cats export." "TextureAtlas.reload_list": "Reload Texture Atlas Material List",
"Tools.bones_translated_success": "Successfully translated all bones to humanoid names",
"Tools.bones_translated_with_fails": "Failed to translate {translate_bone_fails} bones to humanoid names. Adding \"<noik>\" to their names.",
"Tools.convert_to_resonite.desc": "Converts bone names on a model to names compatible with Resonite",
"Tools.convert_to_resonite.label": "Convert to Resonite",
"Tools.create_digitigrade_legs.desc": "Create digitigrade legs from a selected bone chain",
"Tools.create_digitigrade_legs.label": "Create Digitigrade Legs",
"Tools.digitigrade_legs.error.bone_format": "Bone format incorrect! Please select a chain of 4 continuous bones!",
"Tools.digitigrade_legs.success": "Digitigrade legs created successfully",
"Tools.import_any_model.desc": "Import any supported model, FBX, SMD, DMX, GLTF, PMD, PMX and more.",
"Tools.import_any_model.label": "Import Model",
"Tools.label": "Tools",
"Tools.no_armature_selected": "No armature selected",
"Tools.select_armature": "Please select an armature",
"Tools.tools_title.label": "Tools:",
"VisemePanel.create_visemes": "Create Visemes",
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
"VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}",
"VisemePanel.creating_visemes": "Creating visemes...",
"VisemePanel.error.noArmature": "No armature selected",
"VisemePanel.error.noMesh": "No mesh selected",
"VisemePanel.error.noShapekeys": "Selected mesh has no shape keys",
"VisemePanel.error.selectMesh": "Select a mesh to create visemes",
"VisemePanel.info.selectMesh": "Select a mesh to create visemes",
"VisemePanel.label": "Visemes",
"VisemePanel.mixing_shape": "Mixing shape: {shape_name} with value: {value}",
"VisemePanel.mouth_a.desc": "The shapekey for the 'A' mouth shape",
"VisemePanel.mouth_a.label": "Mouth A",
"VisemePanel.mouth_ch.desc": "The shapekey for the 'CH' mouth shape",
"VisemePanel.mouth_ch.label": "Mouth CH",
"VisemePanel.mouth_o.desc": "The shapekey for the 'O' mouth shape",
"VisemePanel.mouth_o.label": "Mouth O",
"VisemePanel.removing_existing_viseme": "Removing existing viseme: {viseme_name}",
"VisemePanel.removing_existing_visemes": "Removing existing visemes...",
"VisemePanel.select_mesh": "Select Mesh",
"VisemePanel.selected_shapes": "Selected shapes: A={shape_a}, O={shape_o}, CH={shape_ch}",
"VisemePanel.shape_intensity": "Shape Intensity",
"VisemePanel.shape_intensity_desc": "The intensity of the viseme shapekeys",
"VisemePanel.sorting_shapekeys": "Sorting shape keys...",
"VisemePanel.start_viseme_creation": "Starting viseme creation...",
"VisemePanel.viseme_created_successfully": "Viseme {viseme_name} created successfully",
"VisemePanel.viseme_creation_completed": "Viseme creation completed."
} }
} }
+116 -98
View File
@@ -1,121 +1,139 @@
{ {
"authors": ["Avatar Toolkit チーム"], "authors": ["Avatar Toolkit Team"],
"messages": { "messages": {
"AutoVisemeButton.desc": "シェイプキーに基づいて自動的にビセームを作成する",
"AutoVisemeButton.error.noShapekeys": "シェイプキーが見つかりません",
"AutoVisemeButton.error.selectShapekeys": "シェイプキーを選択してください",
"AutoVisemeButton.label": "ビセームを作成",
"AutoVisemeButton.success": "ビセームが正常に作成されました",
"AvatarToolkit.alpha_warning": "これは早期アルファ版であり、バグや問題が発生する可能性があります。",
"AvatarToolkit.description": "Blenderでアバターを作成および編集するための",
"AvatarToolkit.label": "Avatar Toolkit",
"AvatarToolkit.welcome": "Avatar Toolkitへようこそ、",
"Export.resonite.desc": "すべてのアニメーションとマテリアルを含むGLBをエクスポートします。アニメーションデータについては以下を参照してください:",
"Export.resonite.label": "Resoniteにエクスポート",
"Importer.export_resonite.desc": "ResoniteにGLTFとしてエクスポートします。モデルがBlenderで正しいスケールであることを確認し、Resoniteでメートル単位でインポートしてください。",
"Importer.export_resonite.label": "Resoniteにエクスポート",
"Importer.export_vrchat.desc": "VRChatにエクスポートします。ChilloutVRでも動作する可能性があります。Catsのエクスポートに似ています。",
"Importer.export_vrchat.label": "VRChatにエクスポート",
"Importer.mmd_anim_importer.desc": "MMDアニメーション(.vmd)をインポート",
"Importer.mmd_anim_importer.label": "MMDアニメーション",
"Importing.importer_search_term": "https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web",
"Importing.need_importer": "{extension}タイプに必要なインポーターがありません!インポーター検索用のウェブブラウザを開きます...",
"Language.auto": "自動", "Language.auto": "自動",
"Language.en_US": "English", "Language.en_US": "English",
"Language.ja_JP": "日本語", "Language.ja_JP": "日本語",
"AvatarToolkit.label": "アバターツールキット", "Optimization.applying_transforms": "トランスフォームを適用中...",
"AvatarToolkit.welcome": "アバターツールキットへようこそ、これは", "Optimization.cleaning_material_names": "マテリアル名をクリーニング中...",
"AvatarToolkit.description": "Blenderでアバターを作成・編集するためのツールです。", "Optimization.cleaning_material_slots": "マテリアルスロットをクリーニング中...",
"AvatarToolkit.alpha_warning": "これは早期アルファ版なので、バグや問題が予想されます。", "Optimization.clearing_unused_data": "未使用データをクリア中...",
"Quick_Access.label": "クイックアクセス", "Optimization.combine_materials.desc": "ドローコールを減らしパフォーマンスを向上させるために類似したマテリアルを結合する",
"Quick_Access.import_export.label": "インポート/エクスポート",
"Quick_Access.options": "クイックアクセスオプション",
"Quick_Access.import_menu.label": "インポートメニュー",
"Quick_Access.import": "インポート",
"Quick_Access.export": "エクスポート",
"Quick_Access.import_menu.desc": "モデルをインポート",
"Quick_Access.import_pmx": "PMXをインポート",
"Quick_Access.import_pmx.desc": "MMD PMXモデルをインポート",
"Quick_Access.import_pmd": "PMDをインポート",
"Quick_Access.import_pmd.desc": "MMD PMDモデルをインポート",
"Quick_Access.export_menu.label": "エクスポートメニュー",
"Quick_Access.export_menu.desc": "サポートされているフォーマットにエクスポート",
"Quick_Access.select_export.label": "エクスポート方法を選択",
"Quick_Access.select_export_resonite.label": "Resonite",
"Quick_Access.export_fbx.label": "FBXをエクスポート",
"Quick_Access.export_fbx.desc": "モデルをFBXとしてエクスポート",
"Quick_Access.import_success": "モデルが正常にインポートされました",
"Export.resonite.label": "Resoniteにエクスポート",
"Export.resonite.desc": "すべてのアニメーションとマテリアルを含むGLBをエクスポートします。アニメーションデータについては以下を参照:",
"Optimization.label": "最適化",
"Optimization.options.label": "最適化オプション",
"Optimization.combine_materials.label": "マテリアルを結合", "Optimization.combine_materials.label": "マテリアルを結合",
"Optimization.combine_materials.desc": "類似したマテリアルを結合してモデルを最適化", "Optimization.consolidating_materials": "マテリアルを統合中...",
"Optimization.finalizing": "最終処理中...",
"Optimization.fixing_uv_coordinates": "UV座標を修正中...",
"Optimization.join_all_meshes.desc": "ドローコールを減らすためにすべてのメッシュを1つのオブジェクトにマージする",
"Optimization.join_all_meshes.label": "すべてのメッシュを結合", "Optimization.join_all_meshes.label": "すべてのメッシュを結合",
"Optimization.join_all_meshes.desc": "すべてのメッシュを1つに結合", "Optimization.join_error": "メッシュ結合中にエラーが発生しました",
"Optimization.join_operation_failed": "結合操作に失敗しました",
"Optimization.join_selected_meshes.desc": "選択したメッシュのみを1つのオブジェクトにマージする",
"Optimization.join_selected_meshes.label": "選択したメッシュを結合", "Optimization.join_selected_meshes.label": "選択したメッシュを結合",
"Optimization.join_selected_meshes.desc": "現在選択されているすべてのメッシュを1つに結合", "Optimization.joinmeshes.label": "メッシュを結合",
"Optimization.remove_doubles_safely.label": "安全に重複頂点を削除", "Optimization.joining_meshes": "メッシュを結合中...",
"Optimization.remove_doubles_safely.desc": "口などの部分が融合しないように注意しながら、すべてのメッシュの重複頂点を削除", "Optimization.label": "最適化",
"Optimization.no_armature_selected": "アーマチュアが選択されていません", "Optimization.material_attribute_mismatch": "マテリアル{material_name}の属性が一致しません。スキップします",
"Optimization.no_meshes_found": "選択されたアーマチュアにメッシュが見つかりません",
"Optimization.materials_combined": "{num_combined}個のマテリアルを結合しました", "Optimization.materials_combined": "{num_combined}個のマテリアルを結合しました",
"Optimization.meshes_joined": "メッシュが正常に結合されました", "Optimization.meshes_joined": "メッシュが正常に結合されました",
"Optimization.no_armature_selected": "アーマチュアが選択されていません",
"Optimization.no_mesh_selected": "メッシュオブジェクトが選択されていません", "Optimization.no_mesh_selected": "メッシュオブジェクトが選択されていません",
"Optimization.select_at_least_two_meshes": "少なくとも2つのメッシュオブジェクトを選択してください", "Optimization.no_meshes_found": "選択されたアーマチュアに対応するメッシュが見つかりません",
"Optimization.selected_meshes_joined": "選択されたメッシュが正常に結合されました", "Optimization.options.label": "最適化:",
"Optimization.vertex_excluded": "シェイプキーのインデックス\"{index}\"の頂点が移動しているため、重複マージから除外しました!", "Optimization.preparing_meshes": "メッシュを準備中...",
"Optimization.processing_shapekey": "メッシュ\"{mesh_name}\"のシェイプキー\"{shapekeyname}\"を処理中", "Optimization.processing_mesh_no_shapekeys": "シェイプキーのないメッシュ{mesh_name}を処理中",
"Optimization.processing_mesh_no_shapekeys": "シェイプキーのないメッシュ\"{mesh_name}\"を処理中", "Optimization.processing_shapekey": "メッシュ{mesh_name}」のシェイプキー「{shapekeyname}」を処理中",
"Optimization.remove_doubles_completed": "重複頂点の削除が完了しました", "Optimization.remove_doubles_completed": "重複頂点の削除が完了しました",
"Optimization.remove_doubles_safely.desc": "口の形状などの重要な特徴を保持しながら重複頂点を削除する",
"Optimization.remove_doubles_safely.label": "安全に重複頂点を削除",
"Optimization.select_armature": "アーマチュアを選択してください", "Optimization.select_armature": "アーマチュアを選択してください",
"Tools.select_armature": "アーマチュアを選択してください", "Optimization.select_at_least_two_meshes": "少なくとも2つのメッシュオブジェクトを選択してください",
"Tools.label": "ツール", "Optimization.selected_meshes_joined": "選択したメッシュが正常に結合されました",
"Tools.tools_title.label": "ツール", "Optimization.selecting_meshes": "メッシュを選択中...",
"Tools.convert_to_resonite.label": "Resoniteに変換", "Optimization.transform_apply_failed": "トランスフォームの適用に失敗しました",
"Tools.convert_to_resonite.desc": "モデルのボーン名をResoniteと互換性のある名前に変換します", "Optimization.vertex_excluded": "シェイプキーのインデックス「{index}」に移動した頂点があります。重複マージから除外します",
"Tools.create_digitigrade_legs.label": "デジタイグレード脚を作成", "Quick_Access.export": "エクスポート",
"Tools.create_digitigrade_legs.desc": "選択したボーンチェーンからデジタイグレード脚を作成", "Quick_Access.export_fbx.desc": "モデルをFBXとしてエクスポート",
"Tools.digitigrade_legs.error.bone_format": "ボーンフォーマットが正しくありません!4つの連続したボーンのチェーンを選択してください!", "Quick_Access.export_fbx.label": "FBXをエクスポート",
"Tools.digitigrade_legs.success": "デジタイグレード脚が正常に作成されました", "Quick_Access.export_menu.desc": "サポートされているフォーマットにエクスポート",
"Tools.no_armature_selected": "アーマチュアが選択されていません", "Quick_Access.export_menu.label": "エクスポートメニュー",
"Tools.bones_translated_with_fails": "{translate_bone_fails}個のボーンをヒューマノイド名に変換できませんでした。それらの名前に\"<noik>\"を追加しています。", "Quick_Access.import": "インポート",
"Tools.bones_translated_success": "すべてのボーンをヒューマノイド名に正常に変換しました", "Quick_Access.import_export.label": "インポート/エクスポート:",
"Tools.import_any_model.desc": "FBX、SMD、DMX、GLTF、PMD、PMXなど、サポートされているすべてのモデルをインポートします。", "Quick_Access.import_menu.desc": "モデルをインポート",
"TextureAtlas.label": "テクスチャアトラス", "Quick_Access.import_menu.label": "インポートメニュー",
"TextureAtlas.material_list_label": "テクスチャアトラスマテリアルリストマテリアル", "Quick_Access.import_pmd": "PMDをインポート",
"TextureAtlas.reload_list": "テクスチャアトラスマテリアルリストを再読み込み", "Quick_Access.import_pmd.desc": "MMD PMDモデルをインポート",
"TextureAtlas.loaded_list": "テクスチャアトラスマテリアルリストを読み込みました", "Quick_Access.import_pmx": "PMXをインポート",
"TextureAtlas.atlas_materials": "アトラスマテリアル", "Quick_Access.import_pmx.desc": "MMD PMXモデルをインポート",
"TextureAtlas.atlas_materials_desc": "モデルを最適化するためにマテリアルをアトラス化", "Quick_Access.import_success": "モデルが正常にインポートされました",
"Quick_Access.label": "クイックアクセス",
"Quick_Access.options": "クイックアクセス:",
"Quick_Access.select_armature": "アーマチュアを選択:",
"Quick_Access.select_export.label": "エクスポート方法を選択",
"Quick_Access.select_export_resonite.label": "Resonite",
"Settings.label": "設定",
"Settings.language.desc": "アドオンのUI言語を選択",
"Settings.language.label": "言語:",
"Settings.translation_restart_popup.description": "翻訳の更新に関する情報",
"Settings.translation_restart_popup.label": "翻訳の更新",
"Settings.translation_restart_popup.message1": "一部の翻訳は適用されない場合があります",
"Settings.translation_restart_popup.message2": "Blenderを再起動するまで。",
"TextureAtlas.atlas_completed": "テクスチャアトラスの作成が完了しました", "TextureAtlas.atlas_completed": "テクスチャアトラスの作成が完了しました",
"TextureAtlas.atlas_error": "テクスチャアトラスの作成中にエラーが発生しました", "TextureAtlas.atlas_error": "テクスチャアトラスの作成中にエラーが発生しました",
"VisemePanel.label": "ビセーム", "TextureAtlas.atlas_materials": "マテリアルをアトラス化",
"VisemePanel.select_mesh": "メッシュを選択", "TextureAtlas.atlas_materials_desc": "モデルを最適化するためにマテリアルをアトラス化する",
"VisemePanel.mouth_a.label": "口 A", "TextureAtlas.label": "テクスチャアトラス化",
"VisemePanel.mouth_a.desc": "'A'の口の形のシェイプキー", "TextureAtlas.loaded_list": "テクスチャアトラスマテリアルリストを読み込みました",
"VisemePanel.mouth_o.label": "口 O", "TextureAtlas.material_list_label": "テクスチャアトラスマテリアルリストのマテリアル",
"VisemePanel.mouth_o.desc": "'O'の口の形のシェイプキー", "TextureAtlas.reload_list": "テクスチャアトラスマテリアルリストを再読み込み",
"VisemePanel.mouth_ch.label": "口 CH", "Tools.bones_translated_success": "すべての骨をヒューマノイド名に正常に変換しました",
"VisemePanel.mouth_ch.desc": "'CH'の口の形のシェイプキー", "Tools.bones_translated_with_fails": "{translate_bone_fails}個の骨をヒューマノイド名に変換できませんでした。それらの名前に「<noik>」を追加します。",
"VisemePanel.shape_intensity": "シェイプの強度", "Tools.convert_to_resonite.desc": "モデルの骨の名前をResoniteと互換性のある名前に変換します",
"VisemePanel.shape_intensity_desc": "ビセームシェイプキーの強度", "Tools.convert_to_resonite.label": "Resoniteに変換",
"Tools.create_digitigrade_legs.desc": "選択した骨のチェーンからデジティグレード脚を作成する",
"Tools.create_digitigrade_legs.label": "デジティグレード脚を作成",
"Tools.digitigrade_legs.error.bone_format": "骨のフォーマットが正しくありません!4つの連続した骨のチェーンを選択してください!",
"Tools.digitigrade_legs.success": "デジティグレード脚が正常に作成されました",
"Tools.import_any_model.desc": "サポートされているモデル(FBX、SMD、DMX、GLTF、PMD、PMXなど)をインポートします。",
"Tools.import_any_model.label": "モデルをインポート",
"Tools.label": "ツール",
"Tools.no_armature_selected": "アーマチュアが選択されていません",
"Tools.select_armature": "アーマチュアを選択してください",
"Tools.tools_title.label": "ツール:",
"VisemePanel.create_visemes": "ビセームを作成", "VisemePanel.create_visemes": "ビセームを作成",
"VisemePanel.creating_viseme": "ビセームを作成中:{viseme_name}",
"VisemePanel.creating_viseme_detail": "ビセームを作成中:{viseme_name}",
"VisemePanel.creating_visemes": "ビセームを作成中...",
"VisemePanel.error.noArmature": "アーマチュアが選択されていません",
"VisemePanel.error.noMesh": "メッシュが選択されていません", "VisemePanel.error.noMesh": "メッシュが選択されていません",
"VisemePanel.error.noShapekeys": "選択されたメッシュにシェイプキーがありません", "VisemePanel.error.noShapekeys": "選択されたメッシュにシェイプキーがありません",
"VisemePanel.error.selectMesh": "ビセームを作成するメッシュを選択してください", "VisemePanel.error.selectMesh": "ビセームを作成するメッシュを選択してください",
"VisemePanel.error.noArmature": "アーマチュアが選択されていません",
"VisemePanel.info.selectMesh": "ビセームを作成するメッシュを選択してください", "VisemePanel.info.selectMesh": "ビセームを作成するメッシュを選択してください",
"VisemePanel.start_viseme_creation": "ビセーム作成を開始しています...", "VisemePanel.label": "ビセーム",
"VisemePanel.selected_shapes": "選択されたシェイプ: A={shape_a}, O={shape_o}, CH={shape_ch}", "VisemePanel.mixing_shape": "シェイプをミックス中:{shape_name}、値:{value}",
"VisemePanel.creating_viseme": "ビセームを作成中: {viseme_name}", "VisemePanel.mouth_a.desc": "'A'の口の形状のシェイプキー",
"VisemePanel.mouth_a.label": "口 A",
"VisemePanel.mouth_ch.desc": "'CH'の口の形状のシェイプキー",
"VisemePanel.mouth_ch.label": "口 CH",
"VisemePanel.mouth_o.desc": "'O'の口の形状のシェイプキー",
"VisemePanel.mouth_o.label": "口 O",
"VisemePanel.removing_existing_viseme": "既存のビセームを削除中:{viseme_name}",
"VisemePanel.removing_existing_visemes": "既存のビセームを削除中...",
"VisemePanel.select_mesh": "メッシュを選択",
"VisemePanel.selected_shapes": "選択されたシェイプ:A={shape_a}、O={shape_o}、CH={shape_ch}",
"VisemePanel.shape_intensity": "シェイプの強度",
"VisemePanel.shape_intensity_desc": "ビセームシェイプキーの強度",
"VisemePanel.sorting_shapekeys": "シェイプキーをソート中...", "VisemePanel.sorting_shapekeys": "シェイプキーをソート中...",
"VisemePanel.viseme_creation_completed": "ビセーム作成が完了しました。", "VisemePanel.start_viseme_creation": "ビセーム作成を開始...",
"VisemePanel.creating_viseme_detail": "ビセームを作成中: {viseme_name}", "VisemePanel.viseme_created_successfully": "ビセーム{viseme_name}が正常に作成されました",
"VisemePanel.removing_existing_viseme": "既存のビセームを削除中: {viseme_name}", "VisemePanel.viseme_creation_completed": "ビセーム作成が完了しました。"
"VisemePanel.mixing_shape": "シェイプをミックス中: {shape_name} 値: {value}",
"VisemePanel.viseme_created_successfully": "ビセーム {viseme_name} が正常に作成されました",
"AutoVisemeButton.label": "ビセームを作成",
"AutoVisemeButton.desc": "シェイプキーに基づいて自動的にビセームを作成",
"AutoVisemeButton.error.noShapekeys": "シェイプキーが見つかりません",
"AutoVisemeButton.error.selectShapekeys": "シェイプキーを選択してください",
"AutoVisemeButton.success": "ビセームが正常に作成されました",
"Settings.label": "設定",
"Settings.language.label": "言語",
"Settings.language.desc": "アドオンのUIの言語を選択",
"Settings.translation_restart_popup.label": "翻訳の更新",
"Settings.translation_restart_popup.description": "翻訳の更新に関する情報",
"Settings.translation_restart_popup.message1": "一部の翻訳は適用されない場合があります",
"Settings.translation_restart_popup.message2": "Blenderを再起動するまで。",
"Importing.need_importer": "{extension}タイプに必要なインポーターがありません!インポーター検索用のウェブブラウザを開いています...",
"Importer.mmd_anim_importer.label": "MMDアニメーション",
"Importer.mmd_anim_importer.desc": "MMDアニメーション(.vmd)をインポート",
"Importing.importer_search_term": "https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web",
"Importer.export_resonite.label": "Resoniteにエクスポート",
"Importer.export_resonite.desc": "GLTFとしてResoniteにエクスポートします。Blenderでモデルが正しいスケールであることを確認し、Resoniteでメートル単位でインポートしてください。",
"Importer.export_vrchat.label": "VRChatにエクスポート",
"Importer.export_vrchat.desc": "VRChatにエクスポートします。ChilloutVRでも動作する可能性があります。Catsのエクスポートに似ています。"
} }
} }
+6 -1
View File
@@ -2,7 +2,7 @@ from bpy.types import UIList, Panel, UILayout, Object, Context,Material, Operato
import bpy import bpy
from ..core.register import register_wrap from ..core.register import register_wrap
from .panel import AvatarToolkitPanel from .panel import AvatarToolkitPanel
from ..core.common import SceneMatClass, material_list_bool from ..core.common import SceneMatClass, material_list_bool, get_selected_armature
from ..functions.atlas_materials import Atlas_Materials from ..functions.atlas_materials import Atlas_Materials
from ..functions.translations import t from ..functions.translations import t
@@ -70,6 +70,9 @@ class TextureAtlasPanel(Panel):
def draw(self, context: Context): def draw(self, context: Context):
layout = self.layout layout = self.layout
armature = get_selected_armature(context)
if armature:
row = layout.row() row = layout.row()
boxoutter = row.box() boxoutter = row.box()
direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT' direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
@@ -81,3 +84,5 @@ class TextureAtlasPanel(Panel):
context.scene, 'texture_atlas_material_index', rows=12, type='DEFAULT') context.scene, 'texture_atlas_material_index', rows=12, type='DEFAULT')
row = layout.row() row = layout.row()
row.operator(Atlas_Materials.bl_idname, text=t("TextureAtlas.atlas_materials")) row.operator(Atlas_Materials.bl_idname, text=t("TextureAtlas.atlas_materials"))
else:
layout.label(text=t("Tools.select_armature"), icon='ERROR')
+12 -7
View File
@@ -19,19 +19,24 @@ class AvatarToolkitOptimizationPanel(bpy.types.Panel):
armature = get_selected_armature(context) armature = get_selected_armature(context)
if armature: if armature:
layout.label(text=t("Optimization.options.label")) layout.label(text=t("Optimization.options.label"), icon='SETTINGS')
row = layout.row() row = layout.row()
row.scale_y = 1.2 row.scale_y = 1.2
row.operator("avatar_toolkit.combine_materials", text=t("Optimization.combine_materials.label")) row.operator("avatar_toolkit.combine_materials", text=t("Optimization.combine_materials.label"), icon='MATERIAL')
row = layout.row(align=True)
row.scale_y = 1.2
row.operator("avatar_toolkit.remove_doubles_safely", text=t("Optimization.remove_doubles_safely.label"), icon='SNAP_VERTEX')
layout.separator(factor=0.5) layout.separator(factor=0.5)
layout.label(text=t("Optimization.joinmeshes.label"), icon='SETTINGS')
row = layout.row(align=True) row = layout.row(align=True)
row.scale_y = 1.2 row.scale_y = 1.2
row.operator("avatar_toolkit.join_all_meshes", text=t("Optimization.join_all_meshes.label")) row.operator("avatar_toolkit.join_all_meshes", text=t("Optimization.join_all_meshes.label"), icon='OUTLINER_OB_MESH')
row.operator("avatar_toolkit.join_selected_meshes", text=t("Optimization.join_selected_meshes.label")) row.operator("avatar_toolkit.join_selected_meshes", text=t("Optimization.join_selected_meshes.label"), icon='STICKY_UVS_LOC')
row.operator("avatar_toolkit.remove_doubles_safely", text=t("Optimization.remove_doubles_safely.label"))
else: else:
layout.label(text=t("Optimization.select_armature")) layout.label(text=t("Optimization.select_armature"), icon='ERROR')
+1 -1
View File
@@ -15,5 +15,5 @@ class AvatarToolkitPanel(bpy.types.Panel):
layout.label(text=t("AvatarToolkit.welcome")) layout.label(text=t("AvatarToolkit.welcome"))
layout.label(text=t("AvatarToolkit.description")) layout.label(text=t("AvatarToolkit.description"))
layout.label(text=t("AvatarToolkit.alpha_warning")) layout.label(text=t("AvatarToolkit.alpha_warning"))
#print("Avatar Toolkit Panel is being drawn")
+10 -8
View File
@@ -9,6 +9,7 @@ from ..core.import_pmd import import_pmd
from ..functions.import_anything import ImportAnyModel from ..functions.import_anything import ImportAnyModel
from ..core.common import get_selected_armature, set_selected_armature from ..core.common import get_selected_armature, set_selected_armature
@register_wrap
@register_wrap @register_wrap
class AvatarToolkitQuickAccessPanel(bpy.types.Panel): class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
bl_label = t("Quick_Access.label") bl_label = t("Quick_Access.label")
@@ -21,9 +22,11 @@ class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
def draw(self, context: Context): def draw(self, context: Context):
layout = self.layout layout = self.layout
layout.label(text=t("Quick_Access.options")) layout.label(text=t("Quick_Access.options"), icon='TOOL_SETTINGS')
layout.prop(context.scene, "selected_armature", text="Select Armature") layout.label(text=t("Quick_Access.select_armature"), icon='ARMATURE_DATA')
layout.prop(context.scene, "selected_armature", text="")
row = layout.row() row = layout.row()
row.label(text=t("Quick_Access.import_export.label"), icon='IMPORT') row.label(text=t("Quick_Access.import_export.label"), icon='IMPORT')
@@ -32,8 +35,8 @@ class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
row = layout.row(align=True) row = layout.row(align=True)
row.scale_y = 1.5 row.scale_y = 1.5
row.operator(ImportAnyModel.bl_idname, text=t("Quick_Access.import")) row.operator(ImportAnyModel.bl_idname, text=t("Quick_Access.import"), icon='IMPORT')
row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export")) row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export"), icon='EXPORT')
@register_wrap @register_wrap
class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator): class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
@@ -54,9 +57,9 @@ class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
def draw(self, context: Context): def draw(self, context: Context):
layout = self.layout layout = self.layout
layout.label(text=t("Quick_Access.select_export.label")) layout.label(text=t("Quick_Access.select_export.label"), icon='EXPORT')
layout.operator("avatar_toolkit.export_resonite", text=t("Quick_Access.select_export_resonite.label")) layout.operator("avatar_toolkit.export_resonite", text=t("Quick_Access.select_export_resonite.label"), icon='SCENE_DATA')
layout.operator("avatar_toolkit.export_fbx", text=t("Quick_Access.export_fbx.label")) layout.operator("avatar_toolkit.export_fbx", text=t("Quick_Access.export_fbx.label"), icon='OBJECT_DATA')
@register_wrap @register_wrap
class AVATAR_TOOLKIT_OT_export_fbx(bpy.types.Operator): class AVATAR_TOOLKIT_OT_export_fbx(bpy.types.Operator):
@@ -68,4 +71,3 @@ class AVATAR_TOOLKIT_OT_export_fbx(bpy.types.Operator):
def execute(self, context): def execute(self, context):
bpy.ops.export_scene.fbx('INVOKE_DEFAULT') bpy.ops.export_scene.fbx('INVOKE_DEFAULT')
return {'FINISHED'} return {'FINISHED'}
+6 -4
View File
@@ -15,7 +15,9 @@ class AvatarToolkitSettingsPanel(bpy.types.Panel):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.prop(context.scene, "avatar_toolkit_language", text=t("Settings.language.label"))
layout.label(text=t("Settings.language.label"))
layout.prop(context.scene, "avatar_toolkit_language", text="", icon='WORLD')
@register_wrap @register_wrap
class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator): class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
@@ -26,7 +28,6 @@ class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
def execute(self, context): def execute(self, context):
if context.scene.avatar_toolkit_language_changed: if context.scene.avatar_toolkit_language_changed:
# Reload the addon after the popup is closed
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'}
@@ -36,5 +37,6 @@ class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text=t("Settings.translation_restart_popup.message1")) layout.label(text=t("Settings.translation_restart_popup.message1"), icon='INFO')
layout.label(text=t("Settings.translation_restart_popup.message2")) layout.label(text=t("Settings.translation_restart_popup.message2"), icon='FILE_REFRESH')
+4 -4
View File
@@ -21,13 +21,13 @@ class AvatarToolkitToolsPanel(bpy.types.Panel):
armature = get_selected_armature(context) armature = get_selected_armature(context)
if armature: if armature:
layout.label(text=t("Tools.tools_title.label")) layout.label(text=t("Tools.tools_title.label"), icon='TOOL_SETTINGS')
layout.separator(factor=0.5) layout.separator(factor=0.5)
row = layout.row(align=True) row = layout.row(align=True)
row.scale_y = 1.5 row.scale_y = 1.5
row.operator("avatar_toolkit.convert_to_resonite", text=t("Tools.convert_to_resonite.label")) row.operator("avatar_toolkit.convert_to_resonite", text=t("Tools.convert_to_resonite.label"), icon='SCENE_DATA')
row = layout.row(align=True) row = layout.row(align=True)
row.operator(CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label")) row.operator(CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA')
else: else:
layout.label(text=t("Tools.select_armature")) layout.label(text=t("Tools.select_armature"), icon='ERROR')
+10 -7
View File
@@ -18,18 +18,21 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
armature = get_selected_armature(context) armature = get_selected_armature(context)
if armature: if armature:
layout.prop(context.scene, "selected_mesh", text=t("VisemePanel.select_mesh")) layout.prop(context.scene, "selected_mesh", text=t("VisemePanel.select_mesh"), icon='OUTLINER_OB_MESH')
row = layout.row()
mesh = bpy.data.objects.get(context.scene.selected_mesh) mesh = bpy.data.objects.get(context.scene.selected_mesh)
if mesh and mesh.type == 'MESH': if mesh and mesh.type == 'MESH':
if mesh.data.shape_keys: if mesh.data.shape_keys:
layout.prop_search(context.scene, "mouth_a", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_a.label')) layout.prop_search(context.scene, "mouth_a", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_a.label'), icon='SHAPEKEY_DATA')
layout.prop_search(context.scene, "mouth_o", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_o.label')) layout.prop_search(context.scene, "mouth_o", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_o.label'), icon='SHAPEKEY_DATA')
layout.prop_search(context.scene, "mouth_ch", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_ch.label')) layout.prop_search(context.scene, "mouth_ch", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_ch.label'), icon='SHAPEKEY_DATA')
layout.prop(context.scene, 'shape_intensity', text=t('VisemePanel.shape_intensity')) layout.prop(context.scene, 'shape_intensity', text=t('VisemePanel.shape_intensity'), icon='FORCE_LENNARDJONES')
layout.operator("avatar_toolkit.create_visemes", text=t('VisemePanel.create_visemes'), icon='TRIA_RIGHT') row = layout.row()
row.scale_y = 1.2
row.operator("avatar_toolkit.create_visemes", text=t('VisemePanel.create_visemes'), icon='TRIA_RIGHT')
else: else:
layout.label(text=t('VisemePanel.error.noShapekeys'), icon='ERROR') layout.label(text=t('VisemePanel.error.noShapekeys'), icon='ERROR')
else: else:
@@ -38,4 +41,4 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
layout.label(text=t('VisemePanel.error.noArmature'), icon='ERROR') layout.label(text=t('VisemePanel.error.noArmature'), icon='ERROR')
layout.separator() layout.separator()
layout.label(text=t('VisemePanel.info.selectMesh')) layout.label(text=t('VisemePanel.info.selectMesh'), icon='HELP')