+23
-1
@@ -6,10 +6,11 @@ import time
|
||||
import webbrowser
|
||||
import typing
|
||||
|
||||
from ..core.register import register_wrap
|
||||
from typing import List, Optional, Tuple
|
||||
from bpy.types import Object, ShapeKey, Mesh, Context, Material, PropertyGroup
|
||||
from functools import lru_cache
|
||||
from bpy.props import PointerProperty
|
||||
from bpy.props import PointerProperty, IntProperty, StringProperty
|
||||
from bpy.utils import register_class
|
||||
|
||||
|
||||
@@ -240,3 +241,24 @@ def get_shapekeys(mesh: Object, prefix: str = '') -> List[tuple]:
|
||||
if not has_shapekeys(mesh):
|
||||
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)]
|
||||
|
||||
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)
|
||||
|
||||
@@ -26,6 +26,9 @@ def register() -> None:
|
||||
|
||||
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(
|
||||
name=t("VisemePanel.mouth_a.label"),
|
||||
description=t("VisemePanel.mouth_a.desc")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import bpy
|
||||
import re
|
||||
from typing import List, Tuple, Optional, Set
|
||||
from bpy.types import Material, Operator, Context, Object
|
||||
from ..core.common import clean_material_names, get_selected_armature, is_valid_armature, get_all_meshes
|
||||
from typing import List, Tuple, Optional, Set, Dict
|
||||
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.register import register_wrap
|
||||
from ..functions.translations import t
|
||||
|
||||
@@ -21,31 +21,30 @@ def copy_tex_nodes(mat1: Material, mat2: Material) -> None:
|
||||
node2.mapping = node1.mapping
|
||||
node2.projection = node1.projection
|
||||
|
||||
def consolidate_textures(mat1: Material, mat2: Material) -> None:
|
||||
if mat1.node_tree and mat2.node_tree:
|
||||
for node1 in mat1.node_tree.nodes:
|
||||
if node1.type == 'TEX_IMAGE':
|
||||
if node1.node_tree:
|
||||
consolidate_textures(node1.node_tree, mat2.node_tree)
|
||||
|
||||
for node2 in mat2.node_tree.nodes:
|
||||
if (node2.type == 'TEX_IMAGE' and
|
||||
node1.image == node2.image):
|
||||
consolidate_nodes(node1, node2)
|
||||
node2.image = node1.image
|
||||
copy_tex_nodes(mat1, mat2)
|
||||
def consolidate_textures(node_tree1: NodeTree, node_tree2: NodeTree) -> None:
|
||||
for node1 in node_tree1.nodes:
|
||||
if node1.type == 'TEX_IMAGE':
|
||||
for node2 in node_tree2.nodes:
|
||||
if (node2.type == 'TEX_IMAGE' and
|
||||
node1.image == node2.image):
|
||||
consolidate_nodes(node1, node2)
|
||||
node2.image = node1.image
|
||||
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:
|
||||
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:
|
||||
if not color_match(mat1.diffuse_color, mat2.diffuse_color, tolerance):
|
||||
return False
|
||||
|
||||
if mat1.roughness != mat2.roughness:
|
||||
if abs(mat1.roughness - mat2.roughness) > tolerance:
|
||||
return False
|
||||
|
||||
consolidate_textures(mat1, mat2)
|
||||
if mat1.node_tree and mat2.node_tree:
|
||||
consolidate_textures(mat1.node_tree, mat2.node_tree)
|
||||
|
||||
return True
|
||||
|
||||
@@ -53,9 +52,6 @@ def get_base_name(name: str) -> str:
|
||||
mat_match = re.match(r"^(.*)\.\d{3}$", 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
|
||||
class CombineMaterials(Operator):
|
||||
bl_idname = "avatar_toolkit.combine_materials"
|
||||
@@ -82,11 +78,23 @@ class CombineMaterials(Operator):
|
||||
self.report({'WARNING'}, t("Optimization.no_meshes_found"))
|
||||
return {'CANCELLED'}
|
||||
|
||||
init_progress(context, 5) # 5 steps in total
|
||||
|
||||
update_progress(self, context, t("Optimization.consolidating_materials"))
|
||||
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()
|
||||
|
||||
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'}
|
||||
|
||||
def consolidate_materials(self, meshes: List[Object]) -> None:
|
||||
@@ -102,31 +110,29 @@ class CombineMaterials(Operator):
|
||||
base_mat: Material = mat_mapping[base_name]
|
||||
try:
|
||||
if materials_match(base_mat, mat):
|
||||
consolidate_textures(base_mat, mat)
|
||||
consolidate_textures(base_mat.node_tree, mat.node_tree)
|
||||
num_combined += 1
|
||||
slot.material = base_mat
|
||||
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
|
||||
else:
|
||||
mat_mapping[base_name] = mat
|
||||
|
||||
self.report({'INFO'}, t("Optimization.materials_combined").format(num_combined=num_combined))
|
||||
|
||||
def remove_unused_materials(self) -> None:
|
||||
for mat in bpy.data.materials:
|
||||
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)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.material_slot_remove_unused()
|
||||
obj.select_set(False)
|
||||
|
||||
def clean_material_slots(self, meshes: List[Object]) -> None:
|
||||
for obj in meshes:
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.material_slot_remove_unused()
|
||||
obj.select_set(False)
|
||||
|
||||
def clean_material_names(self) -> None:
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == 'MESH':
|
||||
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)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from bpy.types import Operator
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from ..core.register import register_wrap
|
||||
from ..core.importer import imports, import_types
|
||||
from ..core.common import remove_default_objects
|
||||
from ..functions.translations import t
|
||||
import pathlib
|
||||
import os
|
||||
@@ -23,7 +24,7 @@ class ImportAnyModel(Operator, ImportHelper):
|
||||
#since I wrote this myself, a bit more efficent than cats. mostly - @989onan
|
||||
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.
|
||||
|
||||
common.remove_default_objects()
|
||||
#check if we are importing multiple files
|
||||
is_multi = False
|
||||
try:
|
||||
|
||||
+60
-15
@@ -2,7 +2,7 @@ import bpy
|
||||
from typing import List, Optional, Set
|
||||
from bpy.types import Operator, Context, Object
|
||||
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
|
||||
|
||||
@register_wrap
|
||||
@@ -18,34 +18,58 @@ class JoinAllMeshes(Operator):
|
||||
return armature is not None and is_valid_armature(armature)
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
self.join_all_meshes(context)
|
||||
return {'FINISHED'}
|
||||
try:
|
||||
self.join_all_meshes(context)
|
||||
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:
|
||||
if not select_current_armature(context):
|
||||
self.report({'WARNING'}, t("Optimization.no_armature_selected"))
|
||||
return
|
||||
raise ValueError(t("Optimization.no_armature_selected"))
|
||||
|
||||
armature = get_selected_armature(context)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
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:
|
||||
mesh.select_set(True)
|
||||
|
||||
if bpy.context.selected_objects:
|
||||
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0]
|
||||
bpy.ops.object.join()
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
update_progress(self, context, t("Optimization.joining_meshes"))
|
||||
try:
|
||||
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)
|
||||
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)
|
||||
|
||||
update_progress(self, context, t("Optimization.finalizing"))
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
self.report({'INFO'}, t("Optimization.meshes_joined"))
|
||||
else:
|
||||
self.report({'WARNING'}, t("Optimization.no_mesh_selected"))
|
||||
raise ValueError(t("Optimization.no_mesh_selected"))
|
||||
|
||||
context.view_layer.objects.active = armature
|
||||
finish_progress(context)
|
||||
|
||||
@register_wrap
|
||||
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
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
self.join_selected_meshes(context)
|
||||
return {'FINISHED'}
|
||||
try:
|
||||
self.join_selected_meshes(context)
|
||||
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:
|
||||
selected_objects: List[Object] = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
|
||||
|
||||
if len(selected_objects) < 2:
|
||||
self.report({'WARNING'}, t("Optimization.select_at_least_two_meshes"))
|
||||
return
|
||||
raise ValueError(t("Optimization.select_at_least_two_meshes"))
|
||||
|
||||
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.select_all(action='DESELECT')
|
||||
|
||||
update_progress(self, context, t("Optimization.selecting_meshes"))
|
||||
for obj in selected_objects:
|
||||
obj.select_set(True)
|
||||
|
||||
if bpy.context.selected_objects:
|
||||
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0]
|
||||
bpy.ops.object.join()
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
update_progress(self, context, t("Optimization.joining_meshes"))
|
||||
try:
|
||||
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)
|
||||
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)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
self.report({'INFO'}, t("Optimization.selected_meshes_joined"))
|
||||
else:
|
||||
self.report({'WARNING'}, t("Optimization.no_mesh_selected"))
|
||||
raise ValueError(t("Optimization.no_mesh_selected"))
|
||||
|
||||
finish_progress(context)
|
||||
|
||||
@@ -30,7 +30,7 @@ def load_translations() -> bool:
|
||||
languages.append(lang)
|
||||
|
||||
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"
|
||||
language = bpy.context.preferences.view.language
|
||||
@@ -40,27 +40,27 @@ def load_translations() -> bool:
|
||||
except IndexError:
|
||||
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")
|
||||
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
|
||||
# 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
|
||||
# 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
|
||||
# print(f"Loaded default translations: {dictionary}") # Debug print
|
||||
else:
|
||||
print("Default translation file 'en_US.json' not found.")
|
||||
|
||||
@@ -72,7 +72,7 @@ def t(phrase: str, default: str = None) -> str:
|
||||
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
|
||||
# print(f"Translating '{phrase}' to '{output}'") # Debug print
|
||||
return output
|
||||
|
||||
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')
|
||||
|
||||
# Initial load of translations
|
||||
print("Performing initial load of translations") # Debug print
|
||||
# print("Performing initial load of translations") # Debug print
|
||||
load_translations()
|
||||
|
||||
+20
-23
@@ -3,7 +3,7 @@ from ..core import common
|
||||
from ..core.register import register_wrap
|
||||
from ..functions.translations import t
|
||||
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
|
||||
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)
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> set:
|
||||
print("Starting viseme creation...")
|
||||
mesh = bpy.data.objects.get(context.scene.selected_mesh)
|
||||
if not mesh or not common.has_shapekeys(mesh):
|
||||
self.report({'ERROR'}, t('AutoVisemeButton.error.noShapekeys'))
|
||||
try:
|
||||
self.create_visemes(context)
|
||||
return {'FINISHED'}
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, str(e))
|
||||
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)
|
||||
|
||||
shape_a = context.scene.mouth_a
|
||||
shape_o = context.scene.mouth_o
|
||||
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":
|
||||
self.report({'ERROR'}, t('AutoVisemeButton.error.selectShapekeys'))
|
||||
return {'CANCELLED'}
|
||||
raise ValueError(t('AutoVisemeButton.error.selectShapekeys'))
|
||||
|
||||
# Create visemes
|
||||
update_progress(self, context, t("VisemePanel.creating_visemes"))
|
||||
visemes: List[Tuple[str, List[Tuple[str, float]]]] = [
|
||||
('vrc.v_aa', [(shape_a, 0.9998)]),
|
||||
('vrc.v_ch', [(shape_ch, 0.9996)]),
|
||||
@@ -57,38 +63,29 @@ class AutoVisemeButton(bpy.types.Operator):
|
||||
]
|
||||
|
||||
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)
|
||||
|
||||
print("Sorting shape keys...")
|
||||
update_progress(self, context, t("VisemePanel.sorting_shapekeys"))
|
||||
common.sort_shape_keys(mesh)
|
||||
|
||||
self.report({'INFO'}, t('AutoVisemeButton.success'))
|
||||
return {'FINISHED'}
|
||||
update_progress(self, context, t("VisemePanel.viseme_creation_completed"))
|
||||
finish_progress(context)
|
||||
|
||||
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
|
||||
|
||||
# Remove existing viseme if it exists
|
||||
if viseme_name in shape_keys:
|
||||
print(f" Removing existing viseme: {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.value = 0.0
|
||||
|
||||
# Mix shapes
|
||||
for shape_name, value in shape_mix:
|
||||
if shape_name in shape_keys:
|
||||
source_shape = shape_keys[shape_name]
|
||||
print(f" Mixing shape: {shape_name} with value: {value * intensity}")
|
||||
for i, vert in enumerate(new_key.data):
|
||||
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:
|
||||
vrc_prefixes = ['vrc.v_', 'vrc.blink_', 'vrc.lowerlid_']
|
||||
shape_keys = mesh.data.shape_keys.key_blocks
|
||||
|
||||
+125
-106
@@ -1,120 +1,139 @@
|
||||
{
|
||||
"authors": ["Avatar Toolkit Team"],
|
||||
"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.error.noShapekeys": "No shape keys found",
|
||||
"AutoVisemeButton.error.selectShapekeys": "Please Select shape keys",
|
||||
"AutoVisemeButton.label": "Create Visemes",
|
||||
"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.language.label": "Language",
|
||||
"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.label": "Translation Update",
|
||||
"Settings.translation_restart_popup.message1": "Some translations may not apply",
|
||||
"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...",
|
||||
"Importer.mmd_anim_importer.label": "MMD Animation",
|
||||
"Importer.mmd_anim_importer.desc": "Import a MMD Animation (.vmd)",
|
||||
"Importing.importer_search_term": "https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web",
|
||||
"Importer.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_vrchat.label": "Export to VRChat",
|
||||
"Importer.export_vrchat.desc": "Export to VRChat, may also work for ChilloutVR. Is similar to Cats export."
|
||||
"TextureAtlas.atlas_completed": "Texture atlas creation completed",
|
||||
"TextureAtlas.atlas_error": "An error occurred during texture atlas creation",
|
||||
"TextureAtlas.atlas_materials": "Atlas Materials",
|
||||
"TextureAtlas.atlas_materials_desc": "Atlas materials to optimize the model",
|
||||
"TextureAtlas.label": "Texture Atlasing",
|
||||
"TextureAtlas.loaded_list": "Loaded Texture Atlas Material List",
|
||||
"TextureAtlas.material_list_label": "Texture Atlas Material List Material",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,139 @@
|
||||
{
|
||||
"authors": ["Avatar Toolkit チーム"],
|
||||
"authors": ["Avatar Toolkit Team"],
|
||||
"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.en_US": "English",
|
||||
"Language.ja_JP": "日本語",
|
||||
"AvatarToolkit.label": "アバターツールキット",
|
||||
"AvatarToolkit.welcome": "アバターツールキットへようこそ、これは",
|
||||
"AvatarToolkit.description": "Blenderでアバターを作成・編集するためのツールです。",
|
||||
"AvatarToolkit.alpha_warning": "これは早期アルファ版なので、バグや問題が予想されます。",
|
||||
"Quick_Access.label": "クイックアクセス",
|
||||
"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.applying_transforms": "トランスフォームを適用中...",
|
||||
"Optimization.cleaning_material_names": "マテリアル名をクリーニング中...",
|
||||
"Optimization.cleaning_material_slots": "マテリアルスロットをクリーニング中...",
|
||||
"Optimization.clearing_unused_data": "未使用データをクリア中...",
|
||||
"Optimization.combine_materials.desc": "ドローコールを減らしパフォーマンスを向上させるために類似したマテリアルを結合する",
|
||||
"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.desc": "すべてのメッシュを1つに結合",
|
||||
"Optimization.join_error": "メッシュ結合中にエラーが発生しました",
|
||||
"Optimization.join_operation_failed": "結合操作に失敗しました",
|
||||
"Optimization.join_selected_meshes.desc": "選択したメッシュのみを1つのオブジェクトにマージする",
|
||||
"Optimization.join_selected_meshes.label": "選択したメッシュを結合",
|
||||
"Optimization.join_selected_meshes.desc": "現在選択されているすべてのメッシュを1つに結合",
|
||||
"Optimization.remove_doubles_safely.label": "安全に重複頂点を削除",
|
||||
"Optimization.remove_doubles_safely.desc": "口などの部分が融合しないように注意しながら、すべてのメッシュの重複頂点を削除",
|
||||
"Optimization.no_armature_selected": "アーマチュアが選択されていません",
|
||||
"Optimization.no_meshes_found": "選択されたアーマチュアにメッシュが見つかりません",
|
||||
"Optimization.joinmeshes.label": "メッシュを結合:",
|
||||
"Optimization.joining_meshes": "メッシュを結合中...",
|
||||
"Optimization.label": "最適化",
|
||||
"Optimization.material_attribute_mismatch": "マテリアル{material_name}の属性が一致しません。スキップします",
|
||||
"Optimization.materials_combined": "{num_combined}個のマテリアルを結合しました",
|
||||
"Optimization.meshes_joined": "メッシュが正常に結合されました",
|
||||
"Optimization.no_armature_selected": "アーマチュアが選択されていません",
|
||||
"Optimization.no_mesh_selected": "メッシュオブジェクトが選択されていません",
|
||||
"Optimization.select_at_least_two_meshes": "少なくとも2つのメッシュオブジェクトを選択してください",
|
||||
"Optimization.selected_meshes_joined": "選択されたメッシュが正常に結合されました",
|
||||
"Optimization.vertex_excluded": "シェイプキーのインデックス\"{index}\"の頂点が移動しているため、重複マージから除外しました!",
|
||||
"Optimization.processing_shapekey": "メッシュ\"{mesh_name}\"のシェイプキー\"{shapekeyname}\"を処理中",
|
||||
"Optimization.processing_mesh_no_shapekeys": "シェイプキーのないメッシュ\"{mesh_name}\"を処理中",
|
||||
"Optimization.no_meshes_found": "選択されたアーマチュアに対応するメッシュが見つかりません",
|
||||
"Optimization.options.label": "最適化:",
|
||||
"Optimization.preparing_meshes": "メッシュを準備中...",
|
||||
"Optimization.processing_mesh_no_shapekeys": "シェイプキーのないメッシュ「{mesh_name}」を処理中",
|
||||
"Optimization.processing_shapekey": "メッシュ「{mesh_name}」のシェイプキー「{shapekeyname}」を処理中",
|
||||
"Optimization.remove_doubles_completed": "重複頂点の削除が完了しました",
|
||||
"Optimization.remove_doubles_safely.desc": "口の形状などの重要な特徴を保持しながら重複頂点を削除する",
|
||||
"Optimization.remove_doubles_safely.label": "安全に重複頂点を削除",
|
||||
"Optimization.select_armature": "アーマチュアを選択してください",
|
||||
"Tools.select_armature": "アーマチュアを選択してください",
|
||||
"Tools.label": "ツール",
|
||||
"Tools.tools_title.label": "ツール",
|
||||
"Tools.convert_to_resonite.label": "Resoniteに変換",
|
||||
"Tools.convert_to_resonite.desc": "モデルのボーン名をResoniteと互換性のある名前に変換します",
|
||||
"Tools.create_digitigrade_legs.label": "デジタイグレード脚を作成",
|
||||
"Tools.create_digitigrade_legs.desc": "選択したボーンチェーンからデジタイグレード脚を作成",
|
||||
"Tools.digitigrade_legs.error.bone_format": "ボーンフォーマットが正しくありません!4つの連続したボーンのチェーンを選択してください!",
|
||||
"Tools.digitigrade_legs.success": "デジタイグレード脚が正常に作成されました",
|
||||
"Tools.no_armature_selected": "アーマチュアが選択されていません",
|
||||
"Tools.bones_translated_with_fails": "{translate_bone_fails}個のボーンをヒューマノイド名に変換できませんでした。それらの名前に\"<noik>\"を追加しています。",
|
||||
"Tools.bones_translated_success": "すべてのボーンをヒューマノイド名に正常に変換しました",
|
||||
"Tools.import_any_model.desc": "FBX、SMD、DMX、GLTF、PMD、PMXなど、サポートされているすべてのモデルをインポートします。",
|
||||
"TextureAtlas.label": "テクスチャアトラス",
|
||||
"TextureAtlas.material_list_label": "テクスチャアトラスマテリアルリストマテリアル",
|
||||
"TextureAtlas.reload_list": "テクスチャアトラスマテリアルリストを再読み込み",
|
||||
"TextureAtlas.loaded_list": "テクスチャアトラスマテリアルリストを読み込みました",
|
||||
"TextureAtlas.atlas_materials": "アトラスマテリアル",
|
||||
"TextureAtlas.atlas_materials_desc": "モデルを最適化するためにマテリアルをアトラス化",
|
||||
"Optimization.select_at_least_two_meshes": "少なくとも2つのメッシュオブジェクトを選択してください",
|
||||
"Optimization.selected_meshes_joined": "選択したメッシュが正常に結合されました",
|
||||
"Optimization.selecting_meshes": "メッシュを選択中...",
|
||||
"Optimization.transform_apply_failed": "トランスフォームの適用に失敗しました",
|
||||
"Optimization.vertex_excluded": "シェイプキーのインデックス「{index}」に移動した頂点があります。重複マージから除外します!",
|
||||
"Quick_Access.export": "エクスポート",
|
||||
"Quick_Access.export_fbx.desc": "モデルをFBXとしてエクスポート",
|
||||
"Quick_Access.export_fbx.label": "FBXをエクスポート",
|
||||
"Quick_Access.export_menu.desc": "サポートされているフォーマットにエクスポート",
|
||||
"Quick_Access.export_menu.label": "エクスポートメニュー",
|
||||
"Quick_Access.import": "インポート",
|
||||
"Quick_Access.import_export.label": "インポート/エクスポート:",
|
||||
"Quick_Access.import_menu.desc": "モデルをインポート",
|
||||
"Quick_Access.import_menu.label": "インポートメニュー",
|
||||
"Quick_Access.import_pmd": "PMDをインポート",
|
||||
"Quick_Access.import_pmd.desc": "MMD PMDモデルをインポート",
|
||||
"Quick_Access.import_pmx": "PMXをインポート",
|
||||
"Quick_Access.import_pmx.desc": "MMD PMXモデルをインポート",
|
||||
"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_error": "テクスチャアトラスの作成中にエラーが発生しました",
|
||||
"VisemePanel.label": "ビセーム",
|
||||
"VisemePanel.select_mesh": "メッシュを選択",
|
||||
"VisemePanel.mouth_a.label": "口 A",
|
||||
"VisemePanel.mouth_a.desc": "'A'の口の形のシェイプキー",
|
||||
"VisemePanel.mouth_o.label": "口 O",
|
||||
"VisemePanel.mouth_o.desc": "'O'の口の形のシェイプキー",
|
||||
"VisemePanel.mouth_ch.label": "口 CH",
|
||||
"VisemePanel.mouth_ch.desc": "'CH'の口の形のシェイプキー",
|
||||
"VisemePanel.shape_intensity": "シェイプの強度",
|
||||
"VisemePanel.shape_intensity_desc": "ビセームシェイプキーの強度",
|
||||
"TextureAtlas.atlas_materials": "マテリアルをアトラス化",
|
||||
"TextureAtlas.atlas_materials_desc": "モデルを最適化するためにマテリアルをアトラス化する",
|
||||
"TextureAtlas.label": "テクスチャアトラス化",
|
||||
"TextureAtlas.loaded_list": "テクスチャアトラスマテリアルリストを読み込みました",
|
||||
"TextureAtlas.material_list_label": "テクスチャアトラスマテリアルリストのマテリアル",
|
||||
"TextureAtlas.reload_list": "テクスチャアトラスマテリアルリストを再読み込み",
|
||||
"Tools.bones_translated_success": "すべての骨をヒューマノイド名に正常に変換しました",
|
||||
"Tools.bones_translated_with_fails": "{translate_bone_fails}個の骨をヒューマノイド名に変換できませんでした。それらの名前に「<noik>」を追加します。",
|
||||
"Tools.convert_to_resonite.desc": "モデルの骨の名前をResoniteと互換性のある名前に変換します",
|
||||
"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.creating_viseme": "ビセームを作成中:{viseme_name}",
|
||||
"VisemePanel.creating_viseme_detail": "ビセームを作成中:{viseme_name}",
|
||||
"VisemePanel.creating_visemes": "ビセームを作成中...",
|
||||
"VisemePanel.error.noArmature": "アーマチュアが選択されていません",
|
||||
"VisemePanel.error.noMesh": "メッシュが選択されていません",
|
||||
"VisemePanel.error.noShapekeys": "選択されたメッシュにシェイプキーがありません",
|
||||
"VisemePanel.error.selectMesh": "ビセームを作成するメッシュを選択してください",
|
||||
"VisemePanel.error.noArmature": "アーマチュアが選択されていません",
|
||||
"VisemePanel.info.selectMesh": "ビセームを作成するメッシュを選択してください",
|
||||
"VisemePanel.start_viseme_creation": "ビセーム作成を開始しています...",
|
||||
"VisemePanel.selected_shapes": "選択されたシェイプ: A={shape_a}, O={shape_o}, CH={shape_ch}",
|
||||
"VisemePanel.creating_viseme": "ビセームを作成中: {viseme_name}",
|
||||
"VisemePanel.label": "ビセーム",
|
||||
"VisemePanel.mixing_shape": "シェイプをミックス中:{shape_name}、値:{value}",
|
||||
"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.viseme_creation_completed": "ビセーム作成が完了しました。",
|
||||
"VisemePanel.creating_viseme_detail": "ビセームを作成中: {viseme_name}",
|
||||
"VisemePanel.removing_existing_viseme": "既存のビセームを削除中: {viseme_name}",
|
||||
"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のエクスポートに似ています。"
|
||||
"VisemePanel.start_viseme_creation": "ビセーム作成を開始...",
|
||||
"VisemePanel.viseme_created_successfully": "ビセーム{viseme_name}が正常に作成されました",
|
||||
"VisemePanel.viseme_creation_completed": "ビセーム作成が完了しました。"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-11
@@ -2,7 +2,7 @@ from bpy.types import UIList, Panel, UILayout, Object, Context,Material, Operato
|
||||
import bpy
|
||||
from ..core.register import register_wrap
|
||||
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.translations import t
|
||||
|
||||
@@ -70,14 +70,19 @@ class TextureAtlasPanel(Panel):
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
row = layout.row()
|
||||
boxoutter = row.box()
|
||||
direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
|
||||
row = boxoutter.row()
|
||||
row.operator(ExpandSection_Materials.bl_idname, text=(t("TextureAtlas.reload_list") if not context.scene.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")), icon=direction_icon)
|
||||
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||
row = boxoutter.row()
|
||||
row.template_list(MaterialTextureAtlasProperties.bl_idname, 'material_list', context.scene, 'materials',
|
||||
context.scene, 'texture_atlas_material_index', rows=12, type='DEFAULT')
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
if armature:
|
||||
row = layout.row()
|
||||
row.operator(Atlas_Materials.bl_idname, text=t("TextureAtlas.atlas_materials"))
|
||||
boxoutter = row.box()
|
||||
direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
|
||||
row = boxoutter.row()
|
||||
row.operator(ExpandSection_Materials.bl_idname, text=(t("TextureAtlas.reload_list") if not context.scene.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")), icon=direction_icon)
|
||||
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||
row = boxoutter.row()
|
||||
row.template_list(MaterialTextureAtlasProperties.bl_idname, 'material_list', context.scene, 'materials',
|
||||
context.scene, 'texture_atlas_material_index', rows=12, type='DEFAULT')
|
||||
row = layout.row()
|
||||
row.operator(Atlas_Materials.bl_idname, text=t("TextureAtlas.atlas_materials"))
|
||||
else:
|
||||
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
||||
|
||||
+15
-10
@@ -19,19 +19,24 @@ class AvatarToolkitOptimizationPanel(bpy.types.Panel):
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("Optimization.options.label"))
|
||||
layout.label(text=t("Optimization.options.label"), icon='SETTINGS')
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.2
|
||||
row.operator("avatar_toolkit.combine_materials", text=t("Optimization.combine_materials.label"))
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
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.join_all_meshes", text=t("Optimization.join_all_meshes.label"))
|
||||
row.operator("avatar_toolkit.join_selected_meshes", text=t("Optimization.join_selected_meshes.label"))
|
||||
row.operator("avatar_toolkit.remove_doubles_safely", text=t("Optimization.remove_doubles_safely.label"))
|
||||
else:
|
||||
layout.label(text=t("Optimization.select_armature"))
|
||||
row.operator("avatar_toolkit.remove_doubles_safely", text=t("Optimization.remove_doubles_safely.label"), icon='SNAP_VERTEX')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
layout.label(text=t("Optimization.joinmeshes.label"), icon='SETTINGS')
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.2
|
||||
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"), icon='STICKY_UVS_LOC')
|
||||
|
||||
else:
|
||||
layout.label(text=t("Optimization.select_armature"), icon='ERROR')
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -15,5 +15,5 @@ class AvatarToolkitPanel(bpy.types.Panel):
|
||||
layout.label(text=t("AvatarToolkit.welcome"))
|
||||
layout.label(text=t("AvatarToolkit.description"))
|
||||
layout.label(text=t("AvatarToolkit.alpha_warning"))
|
||||
#print("Avatar Toolkit Panel is being drawn")
|
||||
|
||||
|
||||
|
||||
+10
-8
@@ -9,6 +9,7 @@ from ..core.import_pmd import import_pmd
|
||||
from ..functions.import_anything import ImportAnyModel
|
||||
from ..core.common import get_selected_armature, set_selected_armature
|
||||
|
||||
@register_wrap
|
||||
@register_wrap
|
||||
class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
|
||||
bl_label = t("Quick_Access.label")
|
||||
@@ -21,9 +22,11 @@ class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
|
||||
|
||||
def draw(self, context: Context):
|
||||
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.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.scale_y = 1.5
|
||||
row.operator(ImportAnyModel.bl_idname, text=t("Quick_Access.import"))
|
||||
row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export"))
|
||||
row.operator(ImportAnyModel.bl_idname, text=t("Quick_Access.import"), icon='IMPORT')
|
||||
row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export"), icon='EXPORT')
|
||||
|
||||
@register_wrap
|
||||
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):
|
||||
layout = self.layout
|
||||
layout.label(text=t("Quick_Access.select_export.label"))
|
||||
layout.operator("avatar_toolkit.export_resonite", text=t("Quick_Access.select_export_resonite.label"))
|
||||
layout.operator("avatar_toolkit.export_fbx", text=t("Quick_Access.export_fbx.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"), icon='SCENE_DATA')
|
||||
layout.operator("avatar_toolkit.export_fbx", text=t("Quick_Access.export_fbx.label"), icon='OBJECT_DATA')
|
||||
|
||||
@register_wrap
|
||||
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):
|
||||
bpy.ops.export_scene.fbx('INVOKE_DEFAULT')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
+6
-4
@@ -15,7 +15,9 @@ class AvatarToolkitSettingsPanel(bpy.types.Panel):
|
||||
|
||||
def draw(self, context):
|
||||
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
|
||||
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):
|
||||
if context.scene.avatar_toolkit_language_changed:
|
||||
# Reload the addon after the popup is closed
|
||||
bpy.ops.script.reload()
|
||||
context.scene.avatar_toolkit_language_changed = False
|
||||
return {'FINISHED'}
|
||||
@@ -36,5 +37,6 @@ class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.label(text=t("Settings.translation_restart_popup.message1"))
|
||||
layout.label(text=t("Settings.translation_restart_popup.message2"))
|
||||
layout.label(text=t("Settings.translation_restart_popup.message1"), icon='INFO')
|
||||
layout.label(text=t("Settings.translation_restart_popup.message2"), icon='FILE_REFRESH')
|
||||
|
||||
|
||||
+4
-4
@@ -21,13 +21,13 @@ class AvatarToolkitToolsPanel(bpy.types.Panel):
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
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)
|
||||
|
||||
row = layout.row(align=True)
|
||||
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.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:
|
||||
layout.label(text=t("Tools.select_armature"))
|
||||
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
||||
|
||||
+10
-7
@@ -18,18 +18,21 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
|
||||
|
||||
armature = get_selected_armature(context)
|
||||
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)
|
||||
if mesh and mesh.type == 'MESH':
|
||||
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_o", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_o.label'))
|
||||
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_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'), icon='SHAPEKEY_DATA')
|
||||
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:
|
||||
layout.label(text=t('VisemePanel.error.noShapekeys'), icon='ERROR')
|
||||
else:
|
||||
@@ -38,4 +41,4 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
|
||||
layout.label(text=t('VisemePanel.error.noArmature'), icon='ERROR')
|
||||
|
||||
layout.separator()
|
||||
layout.label(text=t('VisemePanel.info.selectMesh'))
|
||||
layout.label(text=t('VisemePanel.info.selectMesh'), icon='HELP')
|
||||
|
||||
Reference in New Issue
Block a user