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]]:
|
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']
|
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]:
|
def get_selected_armature(context: Context) -> Optional[Object]:
|
||||||
if context.scene.selected_armature:
|
if context.scene.selected_armature:
|
||||||
armature = bpy.data.objects.get(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 True
|
||||||
return False
|
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]:
|
def get_all_meshes(context: Context) -> List[Object]:
|
||||||
armature = get_selected_armature(context)
|
armature = get_selected_armature(context)
|
||||||
if armature and is_valid_armature(armature):
|
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.types import Scene, Object, Material, Context
|
||||||
from bpy.props import BoolProperty, EnumProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
|
from bpy.props import BoolProperty, EnumProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
|
||||||
from ..core.addon_preferences import get_preference
|
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:
|
def register() -> None:
|
||||||
default_language = get_preference("language", 0)
|
default_language = get_preference("language", 0)
|
||||||
@@ -23,10 +23,21 @@ def register() -> None:
|
|||||||
)))
|
)))
|
||||||
|
|
||||||
register_property((bpy.types.Scene, "merge_armature_source", bpy.props.EnumProperty(
|
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"),
|
name=t("MergeArmatures.selected_armature.label"),
|
||||||
description=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_language_changed", bpy.props.BoolProperty(default=False)))
|
||||||
|
|
||||||
|
|||||||
+48
-104
@@ -4,6 +4,8 @@ from bpy.types import Context, Mesh, Panel, Operator, Armature, EditBone
|
|||||||
from ..functions.translations import t
|
from ..functions.translations import t
|
||||||
from ..core.common import get_selected_armature, get_all_meshes
|
from ..core.common import get_selected_armature, get_all_meshes
|
||||||
from ..core import common
|
from ..core import common
|
||||||
|
from ..core.dictionaries import bone_names
|
||||||
|
from mathutils import Matrix
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
class AvatarToolkit_OT_StartPoseMode(Operator):
|
class AvatarToolkit_OT_StartPoseMode(Operator):
|
||||||
@@ -95,98 +97,10 @@ class AvatarToolkit_OT_ApplyPoseAsRest(Operator):
|
|||||||
return get_selected_armature(context) != None and context.mode == "POSE"
|
return get_selected_armature(context) != None and context.mode == "POSE"
|
||||||
|
|
||||||
def execute(self, context: Context):
|
def execute(self, context: Context):
|
||||||
for obj in get_all_meshes(context):
|
|
||||||
mesh_data: Mesh = obj.data
|
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'}
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@register_wrap
|
@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_description = t("MergeArmature.merge_armatures.desc").format(selected_armature_label=t("MergeArmatures.selected_armature.label"))
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
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
|
@classmethod
|
||||||
def poll(cls, context: Context) -> bool:
|
def poll(cls, context: Context) -> bool:
|
||||||
return (common.get_selected_armature(context) is not None) and (context.scene.merge_armature_source is not None)
|
return (common.get_selected_armature(context) is not None) and (context.scene.merge_armature_source is not None)
|
||||||
@@ -372,22 +284,54 @@ class AvatarToolkit_OT_MergeArmatures(Operator):
|
|||||||
cls.make_active(obj=source_armature, context=context)
|
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
|
if context.scene.merge_armature_apply_transforms:
|
||||||
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')
|
target_armature.select_set(True)
|
||||||
|
for obj in target_armature.children:
|
||||||
if not common.apply_pose_as_rest(armature=source_armature,meshes=[i for i in source_armature.children if i.type == 'MESH'], context=context):
|
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')
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 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"))
|
cls.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
|
||||||
return {'FINISHED'}"""
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cls.make_active(obj=source_armature, context=context)
|
cls.make_active(obj=source_armature, context=context)
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
source_armature_data: Armature = source_armature.data
|
source_armature_data: Armature = source_armature.data
|
||||||
|
|||||||
@@ -169,10 +169,14 @@
|
|||||||
"MergeArmatures.label": "Merge Armatures",
|
"MergeArmatures.label": "Merge Armatures",
|
||||||
"MergeArmatures.selected_armature.label": "Armature to Merge From",
|
"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.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.label": "Merge Armatures Together",
|
||||||
"MergeArmature.merge_armatures.desc": "Merge {selected_armature_label} to the targeted armature for Avatar Toolkit.",
|
"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.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.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.create_visemes": "Create Visemes",
|
||||||
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
|
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
|
||||||
"VisemePanel.creating_viseme_detail": "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:
|
if armature:
|
||||||
layout.label(text=t("MergeArmatures.title.label"), icon='ARMATURE_DATA')
|
layout.label(text=t("MergeArmatures.title.label"), icon='ARMATURE_DATA')
|
||||||
|
|
||||||
layout.separator(factor=0.5)
|
layout.separator(factor=0.5)
|
||||||
layout.prop(context.scene,property="merge_armature_source",icon="ARMATURE_DATA")
|
row = layout.row(align=True)
|
||||||
layout.operator(operator=AvatarToolkit_OT_MergeArmatures.bl_idname,icon="ARMATURE_DATA")
|
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:
|
else:
|
||||||
layout.label(text=t("MergeArmatures.select_armature"), icon='ERROR')
|
layout.label(text=t("MergeArmatures.select_armature"), icon='ERROR')
|
||||||
Reference in New Issue
Block a user