fix merge armatures
now the different options work. and it has an apply transforms
This commit is contained in:
+103
@@ -115,6 +115,9 @@ def get_armature(context: Context, armature_name: Optional[str] = None) -> Optio
|
||||
def get_armatures(self, context: Context) -> List[Tuple[str, str, str]]:
|
||||
return [(obj.name, obj.name, "") for obj in bpy.data.objects if obj.type == 'ARMATURE']
|
||||
|
||||
def get_armatures_that_are_not_selected(self, context: Context) -> List[Tuple[str, str, str]]:
|
||||
return [(obj.name, obj.name, "") for obj in bpy.data.objects if ((obj.type == 'ARMATURE') and (obj.name != context.scene.selected_armature))]
|
||||
|
||||
def get_selected_armature(context: Context) -> Optional[Object]:
|
||||
if context.scene.selected_armature:
|
||||
armature = bpy.data.objects.get(context.scene.selected_armature)
|
||||
@@ -141,6 +144,106 @@ def select_current_armature(context: Context) -> bool:
|
||||
return True
|
||||
return False
|
||||
|
||||
def apply_pose_as_rest(context: Context, armature_obj: bpy.types.Object, meshes: list[bpy.types.Object]) -> bool:
|
||||
for obj in meshes:
|
||||
mesh_data: Mesh = obj.data
|
||||
|
||||
if mesh_data.shape_keys:
|
||||
shape_key_obj_list: list[bpy.types.Object] = []
|
||||
modifier_armature_name: str = ""
|
||||
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "ARMATURE":
|
||||
arm_modifier: bpy.types.ArmatureModifier = modifier
|
||||
if not (arm_modifier.object == armature_obj):
|
||||
continue
|
||||
modifier_armature_name = arm_modifier.object.name
|
||||
|
||||
if modifier_armature_name == "":
|
||||
continue
|
||||
for idx,shape in enumerate(mesh_data.shape_keys.key_blocks):
|
||||
if idx == 0:
|
||||
continue
|
||||
context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
#create duplicate of object
|
||||
bpy.ops.object.duplicate()
|
||||
|
||||
shape_obj = context.view_layer.objects.active
|
||||
|
||||
#make current shapekey a separate object
|
||||
shape_obj.active_shape_key_index = idx
|
||||
shape_obj.name = shape.name
|
||||
|
||||
bpy.ops.object.shape_key_move(type="TOP")
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
bpy.ops.object.shape_key_remove(all=True)
|
||||
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
||||
|
||||
#for modifier_name in [i.name for i in shape_obj.modifiers]:
|
||||
# bpy.ops.object.modifier_remove(modifier=modifier_name)
|
||||
|
||||
shape_key_obj_list.append(shape_obj) #add to a list of shape key objects
|
||||
context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
context.view_layer.objects.active.select_set(True)
|
||||
bpy.ops.object.shape_key_remove(all=True)
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
for shapekey_obj in shape_key_obj_list:
|
||||
shapekey_obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
context.view_layer.objects.active.select_set(True)
|
||||
|
||||
try:
|
||||
bpy.ops.object.join_shapes()
|
||||
except:
|
||||
|
||||
#delete shapekey objects to not leave ourselves in a bad exit state - @989onan
|
||||
context.view_layer.objects.active = shape_key_obj_list[0]
|
||||
obj.select_set(False)
|
||||
bpy.ops.object.delete(confirm=False)
|
||||
return False
|
||||
context.view_layer.objects.active = shape_key_obj_list[0]
|
||||
obj.select_set(False)
|
||||
bpy.ops.object.delete(confirm=False)
|
||||
else:
|
||||
modifier_armature_name: str = ""
|
||||
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "ARMATURE":
|
||||
arm_modifier: bpy.types.ArmatureModifier = modifier
|
||||
if not (arm_modifier.object == armature_obj):
|
||||
continue
|
||||
modifier_armature_name = arm_modifier.object.name
|
||||
|
||||
if modifier_armature_name == "":
|
||||
continue
|
||||
context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
context.view_layer.objects.active.select_set(True)
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
||||
|
||||
context.view_layer.objects.active = armature_obj
|
||||
armature_obj.select_set(True)
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="POSE")
|
||||
|
||||
bpy.ops.pose.armature_apply(selected=False)
|
||||
return True
|
||||
|
||||
def get_all_meshes(context: Context) -> List[Object]:
|
||||
armature = get_selected_armature(context)
|
||||
if armature and is_valid_armature(armature):
|
||||
|
||||
+13
-2
@@ -4,7 +4,7 @@ from ..core.register import register_property
|
||||
from bpy.types import Scene, Object, Material, Context
|
||||
from bpy.props import BoolProperty, EnumProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
|
||||
from ..core.addon_preferences import get_preference
|
||||
from ..core.common import SceneMatClass, MaterialListBool, get_armatures, get_mesh_items
|
||||
from ..core.common import SceneMatClass, MaterialListBool, get_armatures, get_mesh_items, get_armatures_that_are_not_selected
|
||||
|
||||
def register() -> None:
|
||||
default_language = get_preference("language", 0)
|
||||
@@ -23,11 +23,22 @@ def register() -> None:
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Scene, "merge_armature_source", bpy.props.EnumProperty(
|
||||
items=get_armatures,
|
||||
items=get_armatures_that_are_not_selected,
|
||||
name=t("MergeArmatures.selected_armature.label"),
|
||||
description=t("MergeArmatures.selected_armature.label")
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Scene, "merge_armature_apply_transforms", bpy.props.BoolProperty(
|
||||
default=False,
|
||||
name=t("MergeArmature.merge_armatures.apply_transforms.label"),
|
||||
description=t("MergeArmature.merge_armatures.apply_transforms.desc")
|
||||
)))
|
||||
register_property((bpy.types.Scene, "merge_armature_align_bones", bpy.props.BoolProperty(
|
||||
default=False,
|
||||
name=t("MergeArmature.merge_armatures.align_bones.label"),
|
||||
description=t("MergeArmature.merge_armatures.align_bones.desc")
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Scene, "avatar_toolkit_language_changed", bpy.props.BoolProperty(default=False)))
|
||||
|
||||
register_property((bpy.types.Scene, "avatar_toolkit_progress_steps", bpy.props.IntProperty(default=0)))
|
||||
|
||||
+45
-101
@@ -4,6 +4,8 @@ from bpy.types import Context, Mesh, Panel, Operator, Armature, EditBone
|
||||
from ..functions.translations import t
|
||||
from ..core.common import get_selected_armature, get_all_meshes
|
||||
from ..core import common
|
||||
from ..core.dictionaries import bone_names
|
||||
from mathutils import Matrix
|
||||
|
||||
@register_wrap
|
||||
class AvatarToolkit_OT_StartPoseMode(Operator):
|
||||
@@ -95,98 +97,10 @@ class AvatarToolkit_OT_ApplyPoseAsRest(Operator):
|
||||
return get_selected_armature(context) != None and context.mode == "POSE"
|
||||
|
||||
def execute(self, context: Context):
|
||||
for obj in get_all_meshes(context):
|
||||
mesh_data: Mesh = obj.data
|
||||
|
||||
|
||||
|
||||
if mesh_data.shape_keys:
|
||||
shape_key_obj_list: list[bpy.types.Object] = []
|
||||
modifier_armature_name: str = ""
|
||||
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "ARMATURE":
|
||||
arm_modifier: bpy.types.ArmatureModifier = modifier
|
||||
modifier_armature_name = arm_modifier.object.name
|
||||
for idx,shape in enumerate(mesh_data.shape_keys.key_blocks):
|
||||
if idx == 0:
|
||||
continue
|
||||
context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
#create duplicate of object
|
||||
bpy.ops.object.duplicate()
|
||||
|
||||
shape_obj = context.view_layer.objects.active
|
||||
|
||||
#make current shapekey a separate object
|
||||
shape_obj.active_shape_key_index = idx
|
||||
shape_obj.name = shape.name
|
||||
|
||||
bpy.ops.object.shape_key_move(type="TOP")
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
bpy.ops.object.shape_key_remove(all=True)
|
||||
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
||||
|
||||
#for modifier_name in [i.name for i in shape_obj.modifiers]:
|
||||
# bpy.ops.object.modifier_remove(modifier=modifier_name)
|
||||
|
||||
shape_key_obj_list.append(shape_obj) #add to a list of shape key objects
|
||||
context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
context.view_layer.objects.active.select_set(True)
|
||||
bpy.ops.object.shape_key_remove(all=True)
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
for shapekey_obj in shape_key_obj_list:
|
||||
shapekey_obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
context.view_layer.objects.active.select_set(True)
|
||||
|
||||
try:
|
||||
bpy.ops.object.join_shapes()
|
||||
except:
|
||||
self.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
|
||||
#delete shapekey objects to not leave ourselves in a bad exit state - @989onan
|
||||
context.view_layer.objects.active = shape_key_obj_list[0]
|
||||
obj.select_set(False)
|
||||
bpy.ops.object.delete(confirm=False)
|
||||
return {'CANCELLED'}
|
||||
context.view_layer.objects.active = shape_key_obj_list[0]
|
||||
obj.select_set(False)
|
||||
bpy.ops.object.delete(confirm=False)
|
||||
else:
|
||||
modifier_armature_name: str = ""
|
||||
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "ARMATURE":
|
||||
arm_modifier: bpy.types.ArmatureModifier = modifier
|
||||
modifier_armature_name = arm_modifier.object.name
|
||||
context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
context.view_layer.objects.active.select_set(True)
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
||||
|
||||
armature_obj: bpy.types.Object = get_selected_armature(context)
|
||||
|
||||
context.view_layer.objects.active = armature_obj
|
||||
armature_obj.select_set(True)
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="POSE")
|
||||
|
||||
bpy.ops.pose.armature_apply(selected=False)
|
||||
|
||||
if common.apply_pose_as_rest(armature_obj=get_selected_armature(context),meshes=get_all_meshes(context), context=context):
|
||||
self.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
|
||||
return {'FINISHED'}
|
||||
return {'FINISHED'}
|
||||
|
||||
@register_wrap
|
||||
@@ -349,8 +263,6 @@ class AvatarToolkit_OT_MergeArmatures(Operator):
|
||||
bl_description = t("MergeArmature.merge_armatures.desc").format(selected_armature_label=t("MergeArmatures.selected_armature.label"))
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
"""align_bones: bpy.props.BoolProperty(default=False,name=t("MergeArmature.merge_armatures.align_bones.label"),description=t("MergeArmature.merge_armatures.align_bones.desc"))"""
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: Context) -> bool:
|
||||
return (common.get_selected_armature(context) is not None) and (context.scene.merge_armature_source is not None)
|
||||
@@ -372,18 +284,50 @@ class AvatarToolkit_OT_MergeArmatures(Operator):
|
||||
cls.make_active(obj=source_armature, context=context)
|
||||
|
||||
|
||||
#TODO: This is woefully screwed. This needs to be fixed - @989onan
|
||||
"""if cls.align_bones:
|
||||
|
||||
if context.scene.merge_armature_apply_transforms:
|
||||
target_armature.select_set(True)
|
||||
for obj in target_armature.children:
|
||||
obj.select_set(True)
|
||||
for obj in source_armature.children:
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply()
|
||||
|
||||
|
||||
if context.scene.merge_armature_align_bones:
|
||||
if not context.scene.merge_armature_apply_transforms:
|
||||
source_armature.matrix_world = target_armature.matrix_world
|
||||
|
||||
def children_bone_recursive(parent_bone) -> list[bpy.types.PoseBone]:
|
||||
child_bones = []
|
||||
child_bones.append(parent_bone)
|
||||
for child in parent_bone.children:
|
||||
child_bones.extend(children_bone_recursive(child))
|
||||
return child_bones
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
for bone in source_armature.pose.bones:
|
||||
if bone.name in target_armature_data.bones:
|
||||
source_armature_bone_names = [j.name for j in children_bone_recursive(
|
||||
source_armature.pose.bones[
|
||||
next(bone.name for bone in source_armature.pose.bones if common.simplify_bonename(bone.name) in bone_names['hips']) #Find bone that matches dictionary for hips before continuing.
|
||||
]
|
||||
)] #bones are default in order of parent child.
|
||||
|
||||
#sorry for this one liner - @989onan
|
||||
bone.matrix = source_armature.convert_space(matrix=target_armature.convert_space(matrix=target_armature_data.bones[bone.name].matrix_local, pose_bone=None,from_space='LOCAL',to_space='WORLD'), pose_bone=None, from_space='WORLD', to_space='LOCAL')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
context.view_layer.objects.active = target_armature
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
for source_bone_name in source_armature_bone_names:
|
||||
|
||||
if not common.apply_pose_as_rest(armature=source_armature,meshes=[i for i in source_armature.children if i.type == 'MESH'], context=context):
|
||||
if source_bone_name in target_armature_data.edit_bones:
|
||||
obj = source_armature
|
||||
editbone = target_armature_data.edit_bones[source_bone_name]
|
||||
bone = obj.pose.bones[source_bone_name]
|
||||
bone.matrix = editbone.matrix
|
||||
else:
|
||||
continue
|
||||
if not common.apply_pose_as_rest(armature_obj=source_armature,meshes=[i for i in source_armature.children if i.type == 'MESH'], context=context):
|
||||
cls.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
|
||||
return {'FINISHED'}"""
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -169,10 +169,14 @@
|
||||
"MergeArmatures.label": "Merge Armatures",
|
||||
"MergeArmatures.selected_armature.label": "Armature to Merge From",
|
||||
"MergeArmatures.selected_armature.desc": "The armature that should be merged into the targeted armature for Avatar Toolkit.",
|
||||
"MergeArmatures.target_armature.label": "Armature to Merge To",
|
||||
"MergeArmatures.target_armature.desc": "The armature that should be the target for merging armatures.",
|
||||
"MergeArmature.merge_armatures.label": "Merge Armatures Together",
|
||||
"MergeArmature.merge_armatures.desc": "Merge {selected_armature_label} to the targeted armature for Avatar Toolkit.",
|
||||
"MergeArmature.merge_armatures.align_bones.label": "Align Bones",
|
||||
"MergeArmature.merge_armatures.align_bones.desc": "Align bones from source armature to target armature,\nstretching bones to match before merging.",
|
||||
"MergeArmature.merge_armatures.apply_transforms.label": "Apply Transforms",
|
||||
"MergeArmature.merge_armatures.apply_transforms.desc": "Apply transforms on armature and it's meshes before merging.",
|
||||
"VisemePanel.create_visemes": "Create Visemes",
|
||||
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
|
||||
"VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}",
|
||||
|
||||
+12
-2
@@ -23,8 +23,18 @@ class AvatarToolkit_PT_MergeArmaturesPanel(Panel):
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("MergeArmatures.title.label"), icon='ARMATURE_DATA')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
layout.prop(context.scene,property="merge_armature_source",icon="ARMATURE_DATA")
|
||||
layout.operator(operator=AvatarToolkit_OT_MergeArmatures.bl_idname,icon="ARMATURE_DATA")
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene, property="selected_armature",text=t("MergeArmatures.target_armature.label"),icon="STYLUS_PRESSURE")
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene, property="merge_armature_source",icon="SORT_DESC")
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene, property="merge_armature_align_bones")
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene, property="merge_armature_apply_transforms")
|
||||
row = layout.row(align=True)
|
||||
row.operator(operator=AvatarToolkit_OT_MergeArmatures.bl_idname,icon="ARMATURE_DATA")
|
||||
|
||||
else:
|
||||
layout.label(text=t("MergeArmatures.select_armature"), icon='ERROR')
|
||||
Reference in New Issue
Block a user