Fixes and Improvements

- Improved typing in some areas.
- Improved code readability in some areas.
- Delete bone constraints would error out if the user is in edit mode, we now start in Object mode first.
- Fixed Eye tracking Ajust string not being in the translation files.
- There is now a selection box to select the mesh in the current active armature for viseme creation instead of the user having to select it in the 3D scene.
- Viseme preview mode won't allow you to start it if your in a other mode, you need to be in Object mode now.
- Combine Materials won't allow you to start it if your in a other mode, you need to be in Object mode now.
- Added Japanese and Korean UI Languages.
This commit is contained in:
Yusarina
2024-12-18 02:44:26 +00:00
parent c5d07892c2
commit 8665292c7b
15 changed files with 1338 additions and 778 deletions
+48 -42
View File
@@ -1,6 +1,6 @@
import bpy
from typing import Set
from bpy.types import Panel, Context, UILayout, Operator
from typing import Set, List, Tuple, Any
from bpy.types import Panel, Context, UILayout, Operator, Event, WindowManager
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..core.translations import t
from ..core.common import (
@@ -11,10 +11,11 @@ from ..core.common import (
)
class AvatarToolkit_OT_SearchMergeArmatureInto(Operator):
bl_idname = "avatar_toolkit.search_merge_armature_into"
bl_label = ""
bl_description = t('MergeArmature.into_search_desc')
bl_property = "search_merge_armature_into_enum"
"""Search operator for selecting target armature to merge into"""
bl_idname: str = "avatar_toolkit.search_merge_armature_into"
bl_label: str = ""
bl_description: str = t('MergeArmature.into_search_desc')
bl_property: str = "search_merge_armature_into_enum"
search_merge_armature_into_enum: bpy.props.EnumProperty(
name=t('MergeArmature.into'),
@@ -22,19 +23,20 @@ class AvatarToolkit_OT_SearchMergeArmatureInto(Operator):
items=get_armature_list
)
def execute(self, context):
def execute(self, context: Context) -> Set[str]:
context.scene.avatar_toolkit.merge_armature_into = self.search_merge_armature_into_enum
return {'FINISHED'}
def invoke(self, context, event):
def invoke(self, context: Context, event: Event) -> Set[str]:
context.window_manager.invoke_search_popup(self)
return {'FINISHED'}
class AvatarToolkit_OT_SearchMergeArmature(Operator):
bl_idname = "avatar_toolkit.search_merge_armature"
bl_label = ""
bl_description = t('MergeArmature.from_search_desc')
bl_property = "search_merge_armature_enum"
"""Search operator for selecting source armature to merge from"""
bl_idname: str = "avatar_toolkit.search_merge_armature"
bl_label: str = ""
bl_description: str = t('MergeArmature.from_search_desc')
bl_property: str = "search_merge_armature_enum"
search_merge_armature_enum: bpy.props.EnumProperty(
name=t('MergeArmature.from'),
@@ -42,44 +44,46 @@ class AvatarToolkit_OT_SearchMergeArmature(Operator):
items=get_armature_list
)
def execute(self, context):
def execute(self, context: Context) -> Set[str]:
context.scene.avatar_toolkit.merge_armature = self.search_merge_armature_enum
return {'FINISHED'}
def invoke(self, context, event):
def invoke(self, context: Context, event: Event) -> Set[str]:
context.window_manager.invoke_search_popup(self)
return {'FINISHED'}
class AvatarToolkit_OT_SearchAttachMesh(Operator):
bl_idname = "avatar_toolkit.search_attach_mesh"
bl_label = ""
bl_description = t('AttachMesh.search_desc')
bl_property = "search_attach_mesh_enum"
"""Search operator for selecting mesh to attach to armature"""
bl_idname: str = "avatar_toolkit.search_attach_mesh"
bl_label: str = ""
bl_description: str = t('AttachMesh.search_desc')
bl_property: str = "search_attach_mesh_enum"
search_attach_mesh_enum: bpy.props.EnumProperty(
name=t('AttachMesh.select'),
description=t('AttachMesh.select_desc'),
items=lambda self, context: [
(obj.name, obj.name, "")
(obj.name, obj.name, "")
for obj in bpy.data.objects
if obj.type == 'MESH'
and not any(mod.type == 'ARMATURE' for mod in obj.modifiers)
]
)
def execute(self, context):
def execute(self, context: Context) -> Set[str]:
context.scene.avatar_toolkit.attach_mesh = self.search_attach_mesh_enum
return {'FINISHED'}
def invoke(self, context, event):
def invoke(self, context: Context, event: Event) -> Set[str]:
context.window_manager.invoke_search_popup(self)
return {'FINISHED'}
class AvatarToolkit_OT_SearchAttachBone(Operator):
bl_idname = "avatar_toolkit.search_attach_bone"
bl_label = ""
bl_description = t('AttachBone.search_desc')
bl_property = "search_attach_bone_enum"
"""Search operator for selecting bone to attach mesh to"""
bl_idname: str = "avatar_toolkit.search_attach_bone"
bl_label: str = ""
bl_description: str = t('AttachBone.search_desc')
bl_property: str = "search_attach_bone_enum"
search_attach_bone_enum: bpy.props.EnumProperty(
name=t('AttachBone.select'),
@@ -90,26 +94,27 @@ class AvatarToolkit_OT_SearchAttachBone(Operator):
] if get_active_armature(context) else []
)
def execute(self, context):
def execute(self, context: Context) -> Set[str]:
context.scene.avatar_toolkit.attach_bone = self.search_attach_bone_enum
return {'FINISHED'}
def invoke(self, context, event):
def invoke(self, context: Context, event: Event) -> Set[str]:
context.window_manager.invoke_search_popup(self)
return {'FINISHED'}
class AvatarToolKit_PT_CustomPanel(Panel):
"""Panel containing tools for custom avatar creation and merging"""
bl_label = t('CustomPanel.label')
bl_idname = "VIEW3D_PT_avatar_toolkit_custom"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 4
bl_options = {'DEFAULT_CLOSED'}
bl_label: str = t('CustomPanel.label')
bl_idname: str = "VIEW3D_PT_avatar_toolkit_custom"
bl_space_type: str = 'VIEW_3D'
bl_region_type: str = 'UI'
bl_category: str = CATEGORY_NAME
bl_parent_id: str = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order: int = 4
bl_options: Set[str] = {'DEFAULT_CLOSED'}
def draw(self, context: Context) -> None:
"""Draw the custom avatar panel UI"""
layout: UILayout = self.layout
toolkit = context.scene.avatar_toolkit
@@ -129,6 +134,7 @@ class AvatarToolKit_PT_CustomPanel(Panel):
self.draw_mesh_tools(layout, context)
def draw_armature_tools(self, layout: UILayout, context: Context) -> None:
"""Draw the armature merging tools section"""
toolkit = context.scene.avatar_toolkit
# Merge Settings Box
@@ -148,13 +154,13 @@ class AvatarToolKit_PT_CustomPanel(Panel):
col.separator(factor=0.5)
# Group related options together
transform_col = col.column(align=True)
transform_col: UILayout = col.column(align=True)
transform_col.prop(toolkit, "merge_all_bones")
transform_col.prop(toolkit, "apply_transforms")
col.separator(factor=0.5)
cleanup_col = col.column(align=True)
cleanup_col: UILayout = col.column(align=True)
cleanup_col.prop(toolkit, "join_meshes")
cleanup_col.prop(toolkit, "remove_zero_weights")
cleanup_col.prop(toolkit, "cleanup_shape_keys")
@@ -178,12 +184,13 @@ class AvatarToolKit_PT_CustomPanel(Panel):
# Merge button with emphasis
merge_box: UILayout = layout.box()
col = merge_box.column(align=True)
row = col.row(align=True)
col: UILayout = merge_box.column(align=True)
row: UILayout = col.row(align=True)
row.scale_y = 1.5
row.operator("avatar_toolkit.merge_armatures", icon='ARMATURE_DATA')
def draw_mesh_tools(self, layout: UILayout, context: Context) -> None:
"""Draw the mesh attachment tools section"""
toolkit = context.scene.avatar_toolkit
# Mesh Tools Box
@@ -220,8 +227,7 @@ class AvatarToolKit_PT_CustomPanel(Panel):
# Attach button with emphasis
attach_box: UILayout = layout.box()
col = attach_box.column(align=True)
row = col.row(align=True)
col: UILayout = attach_box.column(align=True)
row: UILayout = col.row(align=True)
row.scale_y = 1.5
row.operator("avatar_toolkit.attach_mesh", icon='ARMATURE_DATA')
+43 -40
View File
@@ -1,6 +1,6 @@
import bpy
from typing import Set
from bpy.types import Panel, Context, UILayout, Operator
from bpy.types import Panel, Context, UILayout, Operator, Event, WindowManager
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..core.translations import t
from ..core.common import get_active_armature, get_all_meshes
@@ -20,32 +20,32 @@ from ..functions.eye_tracking import (
class AvatarToolKit_PT_EyeTrackingPanel(Panel):
"""Panel containing eye tracking setup and testing tools"""
bl_label = t("EyeTracking.label")
bl_idname = "VIEW3D_PT_avatar_toolkit_eye_tracking"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 6
bl_options = {'DEFAULT_CLOSED'}
bl_label: str = t("EyeTracking.label")
bl_idname: str = "VIEW3D_PT_avatar_toolkit_eye_tracking"
bl_space_type: str = 'VIEW_3D'
bl_region_type: str = 'UI'
bl_category: str = CATEGORY_NAME
bl_parent_id: str = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order: int = 6
bl_options: Set[str] = {'DEFAULT_CLOSED'}
def draw(self, context: Context) -> None:
"""Draw the eye tracking panel interface"""
layout = self.layout
layout: UILayout = self.layout
toolkit = context.scene.avatar_toolkit
# SDK Version Selection Box
sdk_box = layout.box()
col = sdk_box.column(align=True)
sdk_box: UILayout = layout.box()
col: UILayout = sdk_box.column(align=True)
col.label(text=t("EyeTracking.sdk_version"), icon='PRESET')
col.separator(factor=0.5)
row = col.row(align=True)
row: UILayout = col.row(align=True)
row.prop(toolkit, "eye_tracking_type", expand=True)
if toolkit.eye_tracking_type == 'SDK2':
# Mode Selection Box
mode_box = layout.box()
col = mode_box.column(align=True)
mode_box: UILayout = layout.box()
col: UILayout = mode_box.column(align=True)
col.label(text=t("EyeTracking.setup"), icon='TOOL_SETTINGS')
col.separator(factor=0.5)
col.prop(toolkit, "eye_mode", expand=True)
@@ -59,11 +59,12 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
self.draw_av3_setup(context, layout)
def draw_av3_setup(self, context: Context, layout: UILayout) -> None:
"""Draw the AV3 eye tracking setup interface"""
toolkit = context.scene.avatar_toolkit
# Bone Setup Box
bone_box = layout.box()
col = bone_box.column(align=True)
bone_box: UILayout = layout.box()
col: UILayout = bone_box.column(align=True)
col.label(text=t("EyeTracking.bone_setup"), icon='BONE_DATA')
col.separator(factor=0.5)
@@ -76,16 +77,17 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
col.label(text=t("EyeTracking.no_armature"), icon='ERROR')
# Create Button
row = layout.row(align=True)
row: UILayout = layout.row(align=True)
row.scale_y = 1.5
row.operator(CreateEyesAV3Button.bl_idname, icon='PLAY')
def draw_creation_mode(self, context: Context, layout: UILayout) -> None:
"""Draw the eye tracking creation mode interface"""
toolkit = context.scene.avatar_toolkit
# Bone Setup Box
bone_box = layout.box()
col = bone_box.column(align=True)
bone_box: UILayout = layout.box()
col: UILayout = bone_box.column(align=True)
col.label(text=t("EyeTracking.bone_setup"), icon='BONE_DATA')
col.separator(factor=0.5)
@@ -98,15 +100,15 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
col.label(text=t("EyeTracking.no_armature"), icon='ERROR')
# Mesh Setup Box
mesh_box = layout.box()
col = mesh_box.column(align=True)
mesh_box: UILayout = layout.box()
col: UILayout = mesh_box.column(align=True)
col.label(text=t("EyeTracking.mesh_setup"), icon='MESH_DATA')
col.separator(factor=0.5)
col.prop_search(toolkit, "mesh_name_eye", bpy.data, "objects", text="")
# Shape Key Setup Box
shape_box = layout.box()
col = shape_box.column(align=True)
shape_box: UILayout = layout.box()
col: UILayout = shape_box.column(align=True)
col.label(text=t("EyeTracking.shapekey_setup"), icon='SHAPEKEY_DATA')
col.separator(factor=0.5)
@@ -120,8 +122,8 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
col.label(text=t("EyeTracking.no_shapekeys"), icon='ERROR')
# Options Box
options_box = layout.box()
col = options_box.column(align=True)
options_box: UILayout = layout.box()
col: UILayout = options_box.column(align=True)
col.label(text=t("EyeTracking.options"), icon='SETTINGS')
col.separator(factor=0.5)
col.prop(toolkit, "disable_eye_blinking")
@@ -130,26 +132,27 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
col.prop(toolkit, "eye_distance")
# Create Button
row = layout.row(align=True)
row: UILayout = layout.row(align=True)
row.scale_y = 1.5
row.operator(CreateEyesSDK2Button.bl_idname, icon='PLAY')
def draw_testing_mode(self, context: Context, layout: UILayout) -> None:
"""Draw the eye tracking testing mode interface"""
toolkit = context.scene.avatar_toolkit
if context.mode != 'POSE':
# Testing Start Box
test_box = layout.box()
col = test_box.column(align=True)
test_box: UILayout = layout.box()
col: UILayout = test_box.column(align=True)
col.label(text=t("EyeTracking.testing"), icon='PLAY')
col.separator(factor=0.5)
row = col.row(align=True)
row: UILayout = col.row(align=True)
row.scale_y = 1.5
row.operator(StartTestingButton.bl_idname, icon='PLAY')
else:
# Eye Rotation Box
rotation_box = layout.box()
col = rotation_box.column(align=True)
rotation_box: UILayout = layout.box()
col: UILayout = rotation_box.column(align=True)
col.label(text=t("EyeTracking.rotation_controls"), icon='DRIVER_ROTATIONAL_DIFFERENCE')
col.separator(factor=0.5)
col.prop(toolkit, "eye_rotation_x", text=t("EyeTracking.rotation.x"))
@@ -157,31 +160,31 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
col.operator(ResetRotationButton.bl_idname, icon='LOOP_BACK')
# Eye Adjustment Box
adjust_box = layout.box()
col = adjust_box.column(align=True)
adjust_box: UILayout = layout.box()
col: UILayout = adjust_box.column(align=True)
col.label(text=t("EyeTracking.adjustments"), icon='MODIFIER')
col.separator(factor=0.5)
col.prop(toolkit, "eye_distance")
col.operator(AdjustEyesButton.bl_idname, icon='CON_TRACKTO')
# Blinking Test Box
blink_box = layout.box()
col = blink_box.column(align=True)
blink_box: UILayout = layout.box()
col: UILayout = blink_box.column(align=True)
col.label(text=t("EyeTracking.blink_testing"), icon='HIDE_OFF')
col.separator(factor=0.5)
row = col.row(align=True)
row: UILayout = col.row(align=True)
row.prop(toolkit, "eye_blink_shape")
row.operator(TestBlinking.bl_idname, icon='RESTRICT_VIEW_OFF')
row = col.row(align=True)
row: UILayout = col.row(align=True)
row.prop(toolkit, "eye_lowerlid_shape")
row.operator(TestLowerlid.bl_idname, icon='RESTRICT_VIEW_OFF')
col.operator(ResetBlinkTest.bl_idname, icon='LOOP_BACK')
# Stop Testing Button
row = layout.row(align=True)
row: UILayout = layout.row(align=True)
row.scale_y = 1.5
row.operator(StopTestingButton.bl_idname, icon='PAUSE')
# Reset Button
row = layout.row(align=True)
row: UILayout = layout.row(align=True)
row.operator(ResetEyeTrackingButton.bl_idname, icon='FILE_REFRESH')
+27 -12
View File
@@ -1,6 +1,8 @@
from bpy.types import Panel, Context, UILayout
import bpy
from bpy.types import Panel, Context, UILayout, Object, ShapeKey
from ..core.translations import t
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..core.common import get_active_armature
class AvatarToolKit_PT_VisemesPanel(Panel):
"""Panel containing viseme creation and preview tools"""
@@ -11,26 +13,39 @@ class AvatarToolKit_PT_VisemesPanel(Panel):
bl_category: str = CATEGORY_NAME
bl_parent_id: str = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order: int = 5
bl_options = {'DEFAULT_CLOSED'}
bl_options: set[str] = {'DEFAULT_CLOSED'}
def draw(self, context: Context) -> None:
"""Draw the visemes panel interface"""
"""Draw the visemes panel interface with shape key selection and preview controls"""
layout: UILayout = self.layout
props = context.scene.avatar_toolkit
# Check for valid mesh with shape keys
if not context.active_object or context.active_object.type != 'MESH' or not context.active_object.data.shape_keys:
# Mesh Selection Box
mesh_box: UILayout = layout.box()
col: UILayout = mesh_box.column(align=True)
col.label(text=t("Visemes.mesh_select"), icon='OUTLINER_OB_MESH')
col.separator(factor=0.5)
armature = get_active_armature(context)
if armature:
col.prop_search(props, "viseme_mesh", bpy.data, "objects", text="")
else:
col.label(text=t("Visemes.no_armature"), icon='ERROR')
# Get selected mesh
mesh_obj = bpy.data.objects.get(props.viseme_mesh)
if not mesh_obj or not mesh_obj.data.shape_keys:
layout.label(text=t("Visemes.no_shapekeys"))
return
# Shape Key Selection Box
# Shape Key Selection Box
shape_box: UILayout = layout.box()
col: UILayout = shape_box.column(align=True)
col.label(text=t("Visemes.shape_selection"), icon='SHAPEKEY_DATA')
col.separator(factor=0.5)
# Shape key selection with valid data
shape_keys = context.active_object.data.shape_keys
shape_keys: ShapeKey = mesh_obj.data.shape_keys
col.prop_search(props, "mouth_a", shape_keys, "key_blocks", text=t("Visemes.mouth_a"))
col.prop_search(props, "mouth_o", shape_keys, "key_blocks", text=t("Visemes.mouth_o"))
col.prop_search(props, "mouth_ch", shape_keys, "key_blocks", text=t("Visemes.mouth_ch"))
@@ -41,7 +56,7 @@ class AvatarToolKit_PT_VisemesPanel(Panel):
# Preview Box
preview_box: UILayout = layout.box()
col = preview_box.column(align=True)
col: UILayout = preview_box.column(align=True)
col.label(text=t("Visemes.preview_label"), icon='HIDE_OFF')
col.separator(factor=0.5)
@@ -49,12 +64,12 @@ class AvatarToolKit_PT_VisemesPanel(Panel):
col.prop(props, "viseme_preview_selection", text="")
col.separator()
preview_text = t("Visemes.stop_preview") if props.viseme_preview_mode else t("Visemes.start_preview")
preview_text: str = t("Visemes.stop_preview") if props.viseme_preview_mode else t("Visemes.start_preview")
col.operator("avatar_toolkit.preview_visemes", text=preview_text, icon='HIDE_OFF')
# Create Box
create_box: UILayout = layout.box()
col = create_box.column(align=True)
col: UILayout = create_box.column(align=True)
col.label(text=t("Visemes.create_label"), icon='ADD')
col.separator(factor=0.5)
col.operator("avatar_toolkit.create_visemes", icon='ADD')