Add AMFOWSH
Add apply modifier for object with shapekeys tool
This commit is contained in:
@@ -535,6 +535,18 @@ def add_armature_modifier(mesh: Object, armature: Object) -> None:
|
||||
modifier: Modifier = mesh.modifiers.new('Armature', 'ARMATURE')
|
||||
modifier.object = armature
|
||||
|
||||
def get_modifiers(self: Optional[Any] = None, context: Optional[Context] = None) -> List[Tuple[str, str, str]]:
|
||||
returned: List[Tuple[str, str, str]] = []
|
||||
if context.active_object == None:
|
||||
return returned
|
||||
if context.active_object.type != "MESH":
|
||||
return returned
|
||||
for mod in context.active_object.modifiers:
|
||||
returned.append((mod.name,mod.name,""))
|
||||
|
||||
return returned
|
||||
|
||||
|
||||
def get_shapekeys(context: Context,
|
||||
names: List[str],
|
||||
is_mouth: bool,
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import traceback
|
||||
import bpy
|
||||
import re
|
||||
from typing import Any, Set, Dict, List, Optional, Tuple
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Context,
|
||||
Object,
|
||||
Material,
|
||||
NodeTree,
|
||||
ShaderNodeTexImage
|
||||
)
|
||||
import mathutils
|
||||
import bmesh
|
||||
from ...core.logging_setup import logger
|
||||
from ...core.translations import t
|
||||
from ...core.common import (
|
||||
get_active_armature,
|
||||
get_all_meshes,
|
||||
ProgressTracker,
|
||||
calculate_bone_orientation,
|
||||
add_armature_modifier,
|
||||
get_modifiers,
|
||||
has_shapekeys
|
||||
)
|
||||
from ...core.armature_validation import validate_armature
|
||||
|
||||
class AvatarToolkit_OT_ApplyModifierForShapkeyObj(bpy.types.Operator):
|
||||
"""Operator for forcing the application of a modifier. A shortened way of saying \"Apply modifier for object with shapekeys\""""
|
||||
bl_idname: str = 'avatar_toolkit.merge_armatures'
|
||||
bl_label: str = t('Tools.apply_modifier_on_shapekey_obj')
|
||||
bl_description: str = t('Tools.apply_modifier_on_shapekey_obj_desc')
|
||||
bl_options: Set[str] = {'REGISTER', 'UNDO'}
|
||||
|
||||
modifier: bpy.props.EnumProperty(items=get_modifiers,name="Modifier To Apply")
|
||||
|
||||
|
||||
def draw(self, context: Context) -> None:
|
||||
"""Draw the operator's UI"""
|
||||
layout = self.layout
|
||||
layout.prop(self, "modifier")
|
||||
|
||||
def invoke(self, context: Context, event: bpy.types.Event) -> set[str]:
|
||||
"""Initialize the operator"""
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: Context) -> bool:
|
||||
if context.active_object != None:
|
||||
return context.active_object.type == "MESH"
|
||||
return False
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
|
||||
obj: bpy.types.Object = context.active_object
|
||||
mesh: bpy.types.Mesh = obj.data
|
||||
|
||||
shapes: list[bpy.types.Object] = []
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
if has_shapekeys(obj):
|
||||
#reset shapekeys
|
||||
for idx,key in enumerate(mesh.shape_keys.key_blocks):
|
||||
obj.active_shape_key_index = idx
|
||||
obj.active_shape_key.value = 0
|
||||
|
||||
for idx,key in enumerate(mesh.shape_keys.key_blocks):
|
||||
# duplicate object for shapekey
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.duplicate()
|
||||
|
||||
# name new object after shapekey
|
||||
new_obj = context.view_layer.objects.active
|
||||
new_obj.select_set(True)
|
||||
new_obj.active_shape_key_index = idx
|
||||
new_obj.name = new_obj.active_shape_key.name
|
||||
|
||||
#add to cleanup list
|
||||
shapes.append(new_obj)
|
||||
|
||||
#make basis the same shape as shapekey
|
||||
for idx,point in enumerate(new_obj.active_shape_key.points):
|
||||
new_obj.data.vertices[idx].co.xyz = point.co.xyz
|
||||
|
||||
#remove all shaoekeys on new object and then apply modifier
|
||||
bpy.ops.object.shape_key_remove(all=True,apply_mix=False)
|
||||
try:
|
||||
bpy.ops.object.modifier_apply(modifier=self.modifier)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, f"Shapekey modifier apply for shapekey \"{new_obj.name}\" failed!!")
|
||||
print(f"Shapekey modifier apply for shapekey \"{new_obj.name}\" failed!!")
|
||||
print(traceback.format_exc(e))
|
||||
#clean up after critical failure
|
||||
for shape in shapes:
|
||||
bpy.data.objects.remove(shape)#faster than ops delete
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
|
||||
|
||||
try:
|
||||
#remove shapekeys on original object
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
bpy.ops.object.shape_key_remove(all=True,apply_mix=False)
|
||||
bpy.ops.object.modifier_apply(modifier=self.modifier)
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
#delete first shapekey object aka basis
|
||||
bpy.data.objects.remove(shapes.pop(0))
|
||||
|
||||
#join all objects with applied modifiers back together as shapes
|
||||
for shape in shapes:
|
||||
shape.select_set(True)
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
bpy.ops.object.join_shapes()
|
||||
except Exception as e:
|
||||
|
||||
self.report({'ERROR'}, f"Shapekey joining failed!!")
|
||||
print(f"Shapekey joining failed!!")
|
||||
print(traceback.format_exc(e))
|
||||
#clean up after critical failure
|
||||
for shape in shapes:
|
||||
bpy.data.objects.remove(shape)#faster than ops delete
|
||||
|
||||
#final clean up
|
||||
for shape in shapes:
|
||||
bpy.data.objects.remove(shape)#faster than ops delete
|
||||
|
||||
else:
|
||||
#mesh has no shapekeys, just apply normally.
|
||||
bpy.ops.object.modifier_apply(modifier=self.modifier)
|
||||
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
@@ -219,6 +219,8 @@
|
||||
"Tools.clean_weights_threshold_desc": "Minimum weight value to consider a bone as weighted",
|
||||
"Tools.find_shortest_seam_path": "Find Shortest Seam Path",
|
||||
"Tools.find_shortest_seam_path_desc": "Find shortest path of seams between two selected vertices connected to seams.",
|
||||
"Tools.apply_modifier_on_shapekey_obj":"Apply Modifier on Shapekey Object",
|
||||
"Tools.apply_modifier_on_shapekey_obj_desc":"Applies a modifier on an object regardless of it having shapekeys.",
|
||||
"Tools.merge_title": "Merge Tools",
|
||||
"Tools.merge_to_active": "Merge to Active",
|
||||
"Tools.merge_to_active_desc": "Merge selected bones to active bone",
|
||||
|
||||
@@ -19,6 +19,7 @@ from ..functions.tools.standardize_armature import AvatarToolkit_OT_StandardizeA
|
||||
from ..functions.tools.merge_tools import AvatarToolkit_OT_MergeToActive, AvatarToolkit_OT_MergeToParent, AvatarToolkit_OT_ConnectBones
|
||||
from ..functions.tools.rigify_converter import AvatarToolkit_OT_ConvertRigifyToUnity
|
||||
from ..functions.tools.general_mesh_tools import AvatarToolkit_OT_SelectShortestSeamPath
|
||||
from ..functions.custom_tools.force_apply_modifier import AvatarToolkit_OT_ApplyModifierForShapkeyObj
|
||||
|
||||
class AvatarToolKit_PT_ToolsPanel(Panel):
|
||||
"""Panel containing various tools for avatar customization and optimization"""
|
||||
@@ -66,6 +67,7 @@ class AvatarToolKit_PT_ToolsPanel(Panel):
|
||||
col.label(text=t("Tools.mesh_title"), icon='MESH_DATA')
|
||||
col.separator(factor=0.5)
|
||||
col.operator(AvatarToolkit_OT_SelectShortestSeamPath.bl_idname,text=t("Tools.find_shortest_seam_path"),icon="MESH_DATA")
|
||||
col.operator(AvatarToolkit_OT_ApplyModifierForShapkeyObj.bl_idname,text=t("Tools.apply_modifier_on_shapekey_obj"),icon="SHAPEKEY_DATA")
|
||||
|
||||
|
||||
# Standardization Tools
|
||||
|
||||
Reference in New Issue
Block a user