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: Modifier = mesh.modifiers.new('Armature', 'ARMATURE')
|
||||||
modifier.object = 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,
|
def get_shapekeys(context: Context,
|
||||||
names: List[str],
|
names: List[str],
|
||||||
is_mouth: bool,
|
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.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": "Find Shortest Seam Path",
|
||||||
"Tools.find_shortest_seam_path_desc": "Find shortest path of seams between two selected vertices connected to seams.",
|
"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_title": "Merge Tools",
|
||||||
"Tools.merge_to_active": "Merge to Active",
|
"Tools.merge_to_active": "Merge to Active",
|
||||||
"Tools.merge_to_active_desc": "Merge selected bones to active bone",
|
"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.merge_tools import AvatarToolkit_OT_MergeToActive, AvatarToolkit_OT_MergeToParent, AvatarToolkit_OT_ConnectBones
|
||||||
from ..functions.tools.rigify_converter import AvatarToolkit_OT_ConvertRigifyToUnity
|
from ..functions.tools.rigify_converter import AvatarToolkit_OT_ConvertRigifyToUnity
|
||||||
from ..functions.tools.general_mesh_tools import AvatarToolkit_OT_SelectShortestSeamPath
|
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):
|
class AvatarToolKit_PT_ToolsPanel(Panel):
|
||||||
"""Panel containing various tools for avatar customization and optimization"""
|
"""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.label(text=t("Tools.mesh_title"), icon='MESH_DATA')
|
||||||
col.separator(factor=0.5)
|
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_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
|
# Standardization Tools
|
||||||
|
|||||||
Reference in New Issue
Block a user