diff --git a/core/common.py b/core/common.py index e8a89b4..0ea4ae0 100644 --- a/core/common.py +++ b/core/common.py @@ -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) diff --git a/core/properties.py b/core/properties.py index 2a00b5d..8eda6a6 100644 --- a/core/properties.py +++ b/core/properties.py @@ -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") diff --git a/functions/combine_materials.py b/functions/combine_materials.py index 43d4472..e58056d 100644 --- a/functions/combine_materials.py +++ b/functions/combine_materials.py @@ -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) + diff --git a/functions/import_anything.py b/functions/import_anything.py index 58ed75e..96d0351 100644 --- a/functions/import_anything.py +++ b/functions/import_anything.py @@ -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: diff --git a/functions/join_meshes.py b/functions/join_meshes.py index 3310ae9..d3f74b9 100644 --- a/functions/join_meshes.py +++ b/functions/join_meshes.py @@ -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) diff --git a/functions/translations.py b/functions/translations.py index 9e2a742..e5867b6 100644 --- a/functions/translations.py +++ b/functions/translations.py @@ -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() diff --git a/functions/viseme.py b/functions/viseme.py index 871ce73..8e32146 100644 --- a/functions/viseme.py +++ b/functions/viseme.py @@ -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 diff --git a/resources/translations/en_US.json b/resources/translations/en_US.json index 8d6f720..368396b 100644 --- a/resources/translations/en_US.json +++ b/resources/translations/en_US.json @@ -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 \"\" 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 \"\" 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." } } diff --git a/resources/translations/ja_JP.json b/resources/translations/ja_JP.json index fcbf679..ef93193 100644 --- a/resources/translations/ja_JP.json +++ b/resources/translations/ja_JP.json @@ -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}個のボーンをヒューマノイド名に変換できませんでした。それらの名前に\"\"を追加しています。", - "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}個の骨をヒューマノイド名に変換できませんでした。それらの名前に「」を追加します。", + "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": "ビセーム作成が完了しました。" } } - diff --git a/ui/atlas_materials.py b/ui/atlas_materials.py index 63805a2..7d0f7c3 100644 --- a/ui/atlas_materials.py +++ b/ui/atlas_materials.py @@ -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') diff --git a/ui/optimization.py b/ui/optimization.py index 3701ea5..3a618bd 100644 --- a/ui/optimization.py +++ b/ui/optimization.py @@ -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') + diff --git a/ui/panel.py b/ui/panel.py index b8afd9e..629fee3 100644 --- a/ui/panel.py +++ b/ui/panel.py @@ -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") + diff --git a/ui/quick_access.py b/ui/quick_access.py index cf767a0..e79d7a1 100644 --- a/ui/quick_access.py +++ b/ui/quick_access.py @@ -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'} - diff --git a/ui/settings.py b/ui/settings.py index 6425922..48ae72b 100644 --- a/ui/settings.py +++ b/ui/settings.py @@ -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') + diff --git a/ui/tools.py b/ui/tools.py index d139d7d..78dfbe1 100644 --- a/ui/tools.py +++ b/ui/tools.py @@ -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') diff --git a/ui/viseme.py b/ui/viseme.py index 239154b..7dbf49d 100644 --- a/ui/viseme.py +++ b/ui/viseme.py @@ -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')