Progress System
- Added Progress system so the user knows something is being done when there use certain functions. Currently only Join Meshes, Viseme creation and combine materials use it. - Disbabled some translation debguing stuff to remove spam from the console.
This commit is contained in:
@@ -2,7 +2,7 @@ import bpy
|
||||
import re
|
||||
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
|
||||
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
|
||||
|
||||
@@ -78,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)
|
||||
|
||||
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:
|
||||
@@ -123,3 +135,4 @@ class CombineMaterials(Operator):
|
||||
|
||||
def clear_unused_data_blocks(self) -> None:
|
||||
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -37,22 +37,31 @@ class JoinAllMeshes(Operator):
|
||||
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]
|
||||
|
||||
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"))
|
||||
@@ -60,6 +69,7 @@ class JoinAllMeshes(Operator):
|
||||
raise ValueError(t("Optimization.no_mesh_selected"))
|
||||
|
||||
context.view_layer.objects.active = armature
|
||||
finish_progress(context)
|
||||
|
||||
@register_wrap
|
||||
class JoinSelectedMeshes(Operator):
|
||||
@@ -86,24 +96,32 @@ class JoinSelectedMeshes(Operator):
|
||||
if len(selected_objects) < 2:
|
||||
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]
|
||||
|
||||
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')
|
||||
@@ -111,3 +129,4 @@ class JoinSelectedMeshes(Operator):
|
||||
else:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user