Merge armatures button
This commit is contained in:
+9
-3
@@ -22,6 +22,12 @@ def register() -> None:
|
||||
description=t("VisemePanel.selected_mesh.desc")
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Scene, "merge_armature_source", bpy.props.EnumProperty(
|
||||
items=get_armatures,
|
||||
name=t("MergeArmatures.selected_armature.label"),
|
||||
description=t("MergeArmatures.selected_armature.label")
|
||||
)))
|
||||
|
||||
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)))
|
||||
@@ -49,8 +55,8 @@ def register() -> None:
|
||||
|
||||
register_property((bpy.types.Scene, "selected_armature", bpy.props.EnumProperty(
|
||||
items=get_armatures,
|
||||
name="Selected Armature",
|
||||
description="The currently selected armature for Avatar Toolkit operations"
|
||||
name=t("Quick_Access.selected_armature.label"),
|
||||
description=t("Quick_Access.selected_armature.desc")
|
||||
)))
|
||||
|
||||
#happy with how compressed this get_texture_node_list method is - @989onan
|
||||
@@ -88,7 +94,7 @@ def register() -> None:
|
||||
items=get_texture_node_list)))
|
||||
register_property((Material, "texture_atlas_height", EnumProperty(
|
||||
name=t("TextureAtlas.height"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.height_map").lower()),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.height").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list)))
|
||||
register_property((Material, "texture_atlas_roughness", EnumProperty(
|
||||
|
||||
@@ -338,6 +338,78 @@ class AvatarToolkit_OT_MergeBonesToParents(Operator):
|
||||
bone_child.parent = armature_data.edit_bones[bone].parent
|
||||
armature_data.edit_bones.remove(armature_data.edit_bones[bone])
|
||||
|
||||
|
||||
bpy.ops.object.mode_set(mode=prev_mode)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@register_wrap
|
||||
class AvatarToolkit_OT_MergeArmatures(Operator):
|
||||
bl_idname = "avatar_toolkit.merge_armatures"
|
||||
bl_label = t("MergeArmature.merge_armatures.label")
|
||||
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)
|
||||
|
||||
def make_active(self, obj: bpy.types.Object, context: Context):
|
||||
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)
|
||||
|
||||
def execute(cls, context: Context) -> set[str]:
|
||||
source_armature: bpy.types.Object = bpy.data.objects[context.scene.merge_armature_source]
|
||||
source_armature_data: Armature = source_armature.data
|
||||
target_armature: bpy.types.Object = common.get_selected_armature(context)
|
||||
target_armature_data: Armature = target_armature.data
|
||||
parent_dictionary: dict[str, list[str]] = {}
|
||||
|
||||
cls.make_active(obj=source_armature, context=context)
|
||||
|
||||
|
||||
#TODO: This is woefully screwed. This needs to be fixed - @989onan
|
||||
"""if cls.align_bones:
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
for bone in source_armature.pose.bones:
|
||||
if bone.name in target_armature_data.bones:
|
||||
|
||||
#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')
|
||||
|
||||
if not common.apply_pose_as_rest(armature=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'}"""
|
||||
|
||||
|
||||
|
||||
|
||||
cls.make_active(obj=source_armature, context=context)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
source_armature_data: Armature = source_armature.data
|
||||
for bone_name in [i.name for i in source_armature_data.edit_bones]:
|
||||
if bone_name in target_armature_data.bones:
|
||||
parent_dictionary[bone_name] = [i.name for i in source_armature_data.edit_bones[bone_name].children]
|
||||
source_armature_data.edit_bones.remove(source_armature_data.edit_bones[bone_name])
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
cls.make_active(obj=target_armature, context=context)
|
||||
source_armature.select_set(True)
|
||||
|
||||
bpy.ops.object.join()
|
||||
target_armature: bpy.types.Object = common.get_selected_armature(context)
|
||||
cls.make_active(obj=target_armature, context=context)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
for bone_name, bone_name_list in parent_dictionary.items():
|
||||
if bone_name in target_armature_data.edit_bones:
|
||||
for bone_child in bone_name_list:
|
||||
target_armature_data.edit_bones[bone_child].parent = target_armature_data.edit_bones[bone_name]
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -62,6 +62,8 @@
|
||||
"Optimization.selecting_meshes": "Selecting meshes...",
|
||||
"Optimization.transform_apply_failed": "Transform apply failed",
|
||||
"Optimization.vertex_excluded": "Shapekey has a moved vertex at index \"{index}\", excluding from double merging!",
|
||||
"Quick_Access.selected_armature.label": "Selected Armature",
|
||||
"Quick_Access.selected_armature.desc": "The currently \"targeted\" armature for Avatar Toolkit operations",
|
||||
"Quick_Access.export": "Export",
|
||||
"Quick_Access.export_fbx.desc": "Export the model as FBX",
|
||||
"Quick_Access.export_fbx.label": "Export FBX",
|
||||
@@ -162,6 +164,15 @@
|
||||
"Tools.merge_bones_to_parents.label": "Merge Bones to Individual Parents",
|
||||
"Tools.remove_zero_weight_bones.threshold.label": "Weight Threshold",
|
||||
"Tools.remove_zero_weight_bones.threshold.desc": "If a bone is not weighted to any part of any mesh under the armature with a threshold greater than this, it is removed",
|
||||
"MergeArmatures.select_armature": "Please select an armature",
|
||||
"MergeArmatures.title.label": "Merge Armatures:",
|
||||
"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.",
|
||||
"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.",
|
||||
"VisemePanel.create_visemes": "Create Visemes",
|
||||
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
|
||||
"VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}",
|
||||
|
||||
@@ -66,7 +66,7 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 4
|
||||
bl_order = 5
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
import bpy
|
||||
from ..core.register import register_wrap
|
||||
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from bpy.types import Panel, Context
|
||||
from ..core.common import get_selected_armature
|
||||
from ..functions.translations import t
|
||||
from ..functions.armature_modifying import AvatarToolkit_OT_MergeArmatures
|
||||
|
||||
@register_wrap
|
||||
class AvatarToolkit_PT_MergeArmaturesPanel(Panel):
|
||||
bl_label = t("MergeArmatures.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_merge_armatures"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 4
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
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")
|
||||
else:
|
||||
layout.label(text=t("MergeArmatures.select_armature"), icon='ERROR')
|
||||
+1
-1
@@ -11,7 +11,7 @@ class AvatarToolkitSettingsPanel(bpy.types.Panel):
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 6
|
||||
bl_order = 7
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 5
|
||||
bl_order = 6
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
layout = self.layout
|
||||
|
||||
Reference in New Issue
Block a user