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:
Yusarina
2024-07-25 23:06:20 +01:00
parent fef46cd567
commit 771c4926a6
7 changed files with 94 additions and 33 deletions
+14 -1
View File
@@ -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)
+20 -1
View File
@@ -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)
+7 -7
View File
@@ -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
View File
@@ -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