Files
streaming_world/assets/blender/scripts/mixamo/mixamo_rig.py
2025-01-28 11:30:59 +03:00

2836 lines
100 KiB
Python

import bpy, sys, linecache, ast
import math
from math import *
from mathutils import *
from bpy.types import Panel, UIList
from .utils import *
from .define import *
# OPERATOR CLASSES
##################
class MR_OT_update(bpy.types.Operator):
"""Update old control rig to Blender 3.0"""
bl_idname = "mr.update"
bl_label = "update"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
return "mr_control_rig" in context.active_object.data.keys()
def execute(self, context):
try:
_update(self)
finally:
pass
return {'FINISHED'}
class MR_OT_exportGLTF(bpy.types.Operator):
"""Export to GLTF format"""
bl_idname = "mr.export_gltf"
bl_label = "export_gltf"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
return True
def execute(self, context):
try:
bpy.ops.export_scene.gltf()
finally:
pass
return {'FINISHED'}
class MR_OT_apply_shape(bpy.types.Operator):
"""Apply the selected shape"""
bl_idname = "mr.apply_shape"
bl_label = "apply_shape"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.mode == 'EDIT_MESH':
if 'cs_user' in context.active_object.name:
return True
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
_apply_shape()
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
class MR_OT_edit_custom_shape(bpy.types.Operator):
"""Edit the selected bone shape"""
bl_idname = "mr.edit_custom_shape"
bl_label = "edit_custom_shape"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.mode == 'POSE':
if bpy.context.active_pose_bone:
return True
def execute(self, context):
try:
cs = bpy.context.active_pose_bone.custom_shape
if cs:
_edit_custom_shape()
else:
self.report({"ERROR"}, "No custom shapes set for this bone.")
finally:
pass
return {'FINISHED'}
class MR_OT_make_rig(bpy.types.Operator):
"""Generate a control rig from the selected Mixamo skeleton"""
bl_idname = "mr.make_rig"
bl_label = "Create control rig from selected armature"
bl_options = {'UNDO'}
bake_anim: bpy.props.BoolProperty(name="Bake Anim", description="Bake animation to the control bones", default=True)
ik_arms: bpy.props.BoolProperty(name="IK Hands", description="Use IK for arm bones, otherwise use FK (can be toggled later using the rig properties)", default=True)
ik_legs: bpy.props.BoolProperty(name="IK Legs", description="Use IK for leg bones, otherwise use FK (can be toggled later using the rig properties)", default=True)
animated_armature = None
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
if not "mr_control_rig" in context.active_object.data.keys():
return True
return False
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=450)
def draw(self, context):
layout = self.layout
layout.prop(self, 'bake_anim', text="Apply Animation")
layout.prop(self, 'ik_arms', text="IK Arms")
layout.prop(self, 'ik_legs', text="IK Legs")
def execute(self, context):
debug = False
# ~ layer_select = []
try:
# only select the armature
arm = get_object(context.active_object.name)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(arm.name)
# enable all armature layers
layer_select = enable_all_armature_layers()
# animation import: initial steps
if self.bake_anim:
if not "mr_control_rig" in arm.data.keys():# only if the control rig is not already built
# duplicate current skeleton
duplicate_object()
copy_name = arm.name+"_TEMPANIM"
self.animated_armature = get_object(bpy.context.active_object.name)
self.animated_armature.name = copy_name
self.animated_armature["mix_to_del"] = True
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(arm.name)
# set to rest pose, clear animation
_zero_out()
# build control rig
_make_rig(self)
if blender_version._float < 291:
# Child Of constraints inverse matrix must be set manually in Blender versions < 2.91
print("Set inverse ChildOf")
_reset_inverse_constraints()
# animation import: retarget
if self.bake_anim and self.animated_armature:
_import_anim(self.animated_armature, arm)
# set KeyingSet
ks = context.scene.keying_sets_all
try:
ks.active = ks["Location & Rotation"]
except:# doesn't exist in older Blender versions
pass
finally:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(arm.name)
if debug == False:
restore_armature_layers(layer_select)
remove_retarget_cns(bpy.context.active_object)
remove_temp_objects()
clean_scene()
self.report({"INFO"}, "Control Rig Done!")
return {'FINISHED'}
class MR_OT_zero_out(bpy.types.Operator):
"""Delete all keys and set every bones to (0,0,0) rotation"""
bl_idname = "mr.zero_out"
bl_label = "zero_out"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
return context.active_object.type == "ARMATURE"
return False
def execute(self, context):
scn = bpy.context.scene
try:
_zero_out()
finally:
print("")
return {'FINISHED'}
class MR_OT_bake_anim(bpy.types.Operator):
"""Merge all animation layers (see NLA editor) into a single layer"""
bl_idname = "mr.bake_anim"
bl_label = "bake_anim"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
return context.active_object.type == "ARMATURE"
return False
def execute(self, context):
scn = bpy.context.scene
try:
_bake_anim(self)
finally:
pass
return {'FINISHED'}
class MR_OT_import_anim(bpy.types.Operator):
"""Import an animation file (FBX) of the same character to the control rig"""
bl_idname = "mr.import_anim_to_rig"
bl_label = "import_anim_to_rig"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
if "mr_control_rig" in context.active_object.data.keys():
return True
return False
def execute(self, context):
scn = bpy.context.scene
debug = False
error = False
layer_select = []
if scn.mix_source_armature == None:
self.report({'ERROR'}, "Source armature must be set")
return {'FINISHED'}
try:
layer_select = enable_all_armature_layers()
#tar_arm = scn.mix_target_armature
tar_arm = get_object(bpy.context.active_object.name)
#src_arm = [i for i in bpy.context.selected_objects if i != tar_arm][0]
src_arm = scn.mix_source_armature
print("Source", src_arm.name)
print("Target", tar_arm.name)
_import_anim(src_arm, tar_arm, import_only=True)
#except:
# error = True
# print("Error")
finally:
if debug == False:
restore_armature_layers(layer_select)
remove_retarget_cns(bpy.context.active_object)
if scn.mix_source_armature:
try:
remove_retarget_cns(mix_source_armature)
except:
pass
remove_temp_objects()
self.report({"INFO"}, "Animation imported")
return {'FINISHED'}
# OPERATOR FUNCTIONS
#####################
def _apply_shape():
bpy.ops.object.mode_set(mode='OBJECT')
obj = bpy.context.active_object
obj_name = obj.name
shape = bpy.data.objects.get(obj_name)
delete_obj = False
cs_grp = get_object('cs_grp')
if cs_grp:
shape.parent = bpy.data.objects['cs_grp']
mr_armature_name = None
mr_armature = None
if len(shape.keys()) > 0:
for key in shape.keys():
if 'delete' in shape.keys():
delete_obj = True
if 'mr_armature' in key:
mr_armature_name = shape['mr_armature']
mr_armature = bpy.data.objects.get(mr_armature_name)
if delete_obj:
bpy.ops.object.delete(use_global=False)
else:
# assign to collection
if mr_armature:
if len(mr_armature.users_collection) > 0:
for collec in mr_armature.users_collection:
if len(collec.name.split('_')) == 1:
continue
if collec.name.split('_')[1] == "rig" or collec.name.split('_')[1] == "grp":
cs_collec = bpy.data.collections.get(collec.name.split('_')[0] + '_cs')
if cs_collec:
# remove from root collection
if bpy.context.scene.collection.objects.get(shape.name):
bpy.context.scene.collection.objects.unlink(shape)
# remove from other collections
for other_collec in shape.users_collection:
other_collec.objects.unlink(shape)
# assign to cs collection
cs_collec.objects.link(shape)
print("assigned to collec", cs_collec.name)
else:
print("cs collec not found")
else:
print("rig collec not found")
else:
print("Armature has no collection")
else:
print("Armature not set")
# hide shape
try:
hide_object(shape)
except: # weird error 'StructRNA of type Object has been removed'
print("Error, could not hide shape")
pass
if mr_armature:
set_active_object(mr_armature.name)
bpy.ops.object.mode_set(mode='POSE')
def _edit_custom_shape():
bone = bpy.context.active_pose_bone
rig_name = bpy.context.active_object.name
rig = get_object(rig_name)
cs = bone.custom_shape
cs_mesh = cs.data
bpy.ops.object.posemode_toggle()
# make sure the active collection is not hidden, otherwise we can't access the newly created object data
active_collec = bpy.context.layer_collection
if not active_collec.is_visible:
for col in rig.users_collection:
layer_col = search_layer_collection(bpy.context.view_layer.layer_collection, col.name)
if layer_col.hide_viewport == False and col.hide_viewport == False:
bpy.context.view_layer.active_layer_collection = layer_col
break
# create new mesh data
bpy.ops.mesh.primitive_plane_add(size=1, enter_editmode=False, location=(-0, 0, 0.0), rotation=(0.0, 0.0, 0.0))
mesh_obj = bpy.context.active_object
mesh_obj.name = 'cs_user_' + bone.name
if cs.name == "cs_user_" + bone.name:# make a mesh instance if it's a already edited
mesh_obj.data = cs_mesh
mesh_obj['delete'] = 1.0
else: # else create new object data
mesh_obj.data = cs_mesh.copy()
mesh_obj.data.name = mesh_obj.name
bone.custom_shape = mesh_obj
# store the current armature name in a custom prop
mesh_obj['mr_armature'] = rig_name
if bone.custom_shape_transform:
bone_transf = bone.custom_shape_transform
mesh_obj.matrix_world = rig.matrix_world @ bone_transf.matrix
else:
mesh_obj.matrix_world = rig.matrix_world @ bone.matrix
mesh_obj.scale *= get_custom_shape_scale(bone)
mesh_obj.scale *= bone.length
bpy.ops.object.mode_set(mode='EDIT')
def clean_scene():
# hide cs_grp
cs_grp = get_object("cs_grp")
if cs_grp:
for c in cs_grp.children:
hide_object(c)
hide_object(cs_grp)
# Display layer 0 and 1 only
# ~ layers = bpy.context.active_object.data.layers
# ~ layers[0] = True
# ~ for i in range(0, 32):
# ~ layers[i] = i in [0]
for c in bpy.context.active_object.data.collections:
if c.name in ('CTRL'):
c.is_visible = True
else:
c.is_visible = False
def init_armature_transforms(rig):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(rig.name)
bpy.ops.object.mode_set(mode='OBJECT')
# first unparent children meshes (init scale messed up children scale in Blender 2.8)
child_par_dict = {}
for child in bpy.data.objects[rig.name].children:
bone_parent = None
if child.parent_type == "BONE":
bone_parent = child.parent_bone
child_par_dict[child.name] = bone_parent
child_mat = child.matrix_world.copy()
child.parent = None
bpy.context.evaluated_depsgraph_get().update()
child.matrix_world = child_mat
# apply armature transforms
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
bpy.context.evaluated_depsgraph_get().update()
# restore armature children
for child_name in child_par_dict:
child = bpy.data.objects.get(child_name)
child_mat = child.matrix_world.copy()
child.parent = bpy.data.objects[rig.name]
if child_par_dict[child_name] != None:# bone parent
child.parent_type = "BONE"
child.parent_bone = child_par_dict[child_name]
bpy.context.evaluated_depsgraph_get().update()
child.matrix_world = child_mat
def _reset_inverse_constraints():
bpy.ops.object.mode_set(mode='POSE')
rig_name = bpy.context.active_object.name
rig = get_object(rig_name)
for pb in rig.pose.bones:
if len(pb.constraints):
for cns in pb.constraints:
if cns.type == 'CHILD_OF':
set_constraint_inverse_matrix(cns)
bpy.ops.object.mode_set(mode='OBJECT')
def _update(self):
if blender_version._float >= 300:
convert_drivers_cs_to_xyz(bpy.context.active_object)
def _make_rig(self):
print("\nBuilding control rig...")
scn = bpy.context.scene
rig_name = bpy.context.active_object.name
rig = get_object(rig_name)
coll_mix_name = "DEF"
coll_ctrl_name = "CTRL"
coll_intern_name = "MCH"
# ~ _ = rig.data.collections.new(coll_0_name)
# ~ for eb in rig.data.edit_bones:
# ~ set_bone_collection(rig, eb, coll_0_name)
# ~ layer_mix_idx = 1
# ~ layer_ctrl_idx = 0
# ~ layer_intern_idx = 8
use_name_prefix = True
c_master_name = c_prefix+master_rig_names["master"]
# Init transforms
init_armature_transforms(rig)
def add_master():
print(" Add Master")
# -- Edit --
bpy.ops.object.mode_set(mode='EDIT')
# Create bones
c_master = create_edit_bone(c_master_name)
c_master.head = [0, 0, 0]
c_master.tail = [0, 0, 0.05 * rig.dimensions[2]]
c_master.roll = 0.01
set_bone_collection(rig, c_master, coll_ctrl_name)
# ~ set_bone_layer(c_master, layer_ctrl_idx)
c_master["mixamo_ctrl"] = 1# tag as controller bone
# -- Pose --
bpy.ops.object.mode_set(mode='POSE')
c_master_pb = get_pose_bone(c_master_name)
# set custom shapes
set_bone_custom_shape(c_master_pb, "cs_master")
# set rotation mode
c_master_pb.rotation_mode = "XYZ"
# set color group
set_bone_color_group(rig, c_master_pb, "master")
def add_spine():
print(" Add Spine")
# -- Edit --
bpy.ops.object.mode_set(mode='EDIT')
# Create bones
hips_name = get_mix_name(spine_names["pelvis"], use_name_prefix)
spine_name = get_mix_name(spine_names["spine1"], use_name_prefix)
spine1_name = get_mix_name(spine_names["spine2"], use_name_prefix)
spine2_name = get_mix_name(spine_names["spine3"], use_name_prefix)
hips = get_edit_bone(hips_name)
spine = get_edit_bone(spine_name)
spine1 = get_edit_bone(spine1_name)
spine2 = get_edit_bone(spine2_name)
if not hips or not spine or not spine1 or not spine2:
print(" Spine bones are missing, skip spine")
return
for b in [hips, spine, spine1, spine2]:
set_bone_collection(rig, b, coll_mix_name)
# ~ set_bone_layer(b, layer_mix_idx)
# Hips Ctrl
c_hips_name = c_prefix+spine_rig_names["pelvis"]
c_hips = create_edit_bone(c_hips_name)
copy_bone_transforms(hips, c_hips)
c_hips.parent = get_edit_bone(c_prefix+master_rig_names["master"])
set_bone_collection(rig, c_hips, coll_ctrl_name)
# ~ set_bone_layer(c_hips, layer_ctrl_idx)
c_hips["mixamo_ctrl"] = 1# tag as controller bone
# Free Hips Ctrl
c_hips_free_name = c_prefix+spine_rig_names["hips_free"]
c_hips_free = create_edit_bone(c_hips_free_name)
c_hips_free.head = hips.tail.copy()
c_hips_free.tail = hips.head.copy()
align_bone_x_axis(c_hips_free, hips.x_axis)
c_hips_free["mixamo_ctrl"] = 1# tag as controller bone
c_hips_free.parent = c_hips
set_bone_collection(rig, c_hips_free, coll_ctrl_name)
# ~ set_bone_layer(c_hips_free, layer_ctrl_idx)
# Free Hips helper
hips_free_h_name = spine_rig_names["hips_free_helper"]
hips_free_helper = create_edit_bone(hips_free_h_name)
copy_bone_transforms(hips, hips_free_helper)
hips_free_helper.parent = c_hips_free
set_bone_collection(rig, hips_free_helper, coll_intern_name)
# ~ set_bone_layer(hips_free_helper, layer_intern_idx)
# Spine Ctrl
c_spine_name = c_prefix+spine_rig_names["spine1"]
c_spine = create_edit_bone(c_spine_name)
copy_bone_transforms(spine, c_spine)
c_spine.parent = c_hips
# ~ set_bone_layer(c_spine, layer_ctrl_idx)
set_bone_collection(rig, c_spine, coll_ctrl_name)
c_spine["mixamo_ctrl"] = 1# tag as controller bone
# Spine1 Ctrl
c_spine1_name = c_prefix+spine_rig_names["spine2"]
c_spine1 = create_edit_bone(c_spine1_name)
copy_bone_transforms(spine1, c_spine1)
c_spine1.parent = c_spine
set_bone_collection(rig, c_spine1, coll_ctrl_name)
# ~ set_bone_layer(c_spine1, layer_ctrl_idx)
c_spine1["mixamo_ctrl"] = 1# tag as controller bone
# Spine2 Ctrl
c_spine2_name = c_prefix+spine_rig_names["spine3"]
c_spine2 = create_edit_bone(c_spine2_name)
copy_bone_transforms(spine2, c_spine2)
c_spine2.parent = c_spine1
set_bone_collection(rig, c_spine2, coll_ctrl_name)
# ~ set_bone_layer(c_spine2, layer_ctrl_idx)
c_spine2["mixamo_ctrl"] = 1# tag as controller bone
# -- Pose --
bpy.ops.object.mode_set(mode='POSE')
c_hips_pb = get_pose_bone(c_hips_name)
hips_helper_pb = get_pose_bone(hips_free_h_name)
c_hips_free_pb = get_pose_bone(c_hips_free_name)
c_spine_pb = get_pose_bone(c_spine_name)
c_spine1_pb = get_pose_bone(c_spine1_name)
c_spine2_pb = get_pose_bone(c_spine2_name)
# set custom shapes
set_bone_custom_shape(c_hips_pb, "cs_square_2")
set_bone_custom_shape(c_hips_free_pb, "cs_hips")
set_bone_custom_shape(c_spine_pb, "cs_circle")
set_bone_custom_shape(c_spine1_pb, "cs_circle")
set_bone_custom_shape(c_spine2_pb, "cs_circle")
# set rotation mode
c_hips_pb.rotation_mode = "XYZ"
c_hips_free_pb.rotation_mode = "XYZ"
c_spine_pb.rotation_mode = "XYZ"
c_spine1_pb.rotation_mode = "XYZ"
c_spine2_pb.rotation_mode = "XYZ"
# set color group
set_bone_color_group(rig, c_hips_pb, "root_master")
set_bone_color_group(rig, c_hips_free_pb, "body_mid")
set_bone_color_group(rig, c_spine_pb, "body_mid")
set_bone_color_group(rig, c_spine1_pb, "body_mid")
set_bone_color_group(rig, c_spine2_pb, "body_mid")
# constraints
# Hips
mixamo_spine_pb = get_pose_bone(hips_name)
cns = mixamo_spine_pb.constraints.get("Copy Transforms")
if cns == None:
cns = mixamo_spine_pb.constraints.new("COPY_TRANSFORMS")
cns.name = "Copy Transforms"
cns.target = rig
cns.subtarget = hips_free_h_name
# Spine
spine_bone_matches = {"1": c_spine_name, "2":c_spine1_name, "3":c_spine2_name}
for str_idx in spine_bone_matches:
c_name = spine_bone_matches[str_idx]
mixamo_bname = get_mix_name(spine_names["spine"+str_idx], use_name_prefix)
mixamo_spine_pb = get_pose_bone(mixamo_bname)
cns = mixamo_spine_pb.constraints.get("Copy Transforms")
if cns == None:
cns = mixamo_spine_pb.constraints.new("COPY_TRANSFORMS")
cns.name = "Copy Transforms"
cns.target = rig
cns.subtarget = c_name
def add_head():
print(" Add Head")
# -- Edit --
bpy.ops.object.mode_set(mode='EDIT')
# Create bones
neck_name = get_mix_name(head_names["neck"], use_name_prefix)
head_name = get_mix_name(head_names["head"], use_name_prefix)
head_end_name = get_mix_name(head_names["head_end"], use_name_prefix)
neck = get_edit_bone(neck_name)
head = get_edit_bone(head_name)
head_end = get_edit_bone(head_end_name)
if not neck or not head:
print(" Head or neck bones are missing, skip head")
return
for b in [neck, head, head_end]:
set_bone_collection(rig, b, coll_mix_name)
# ~ set_bone_layer(b, layer_mix_idx)
# Neck Ctrl
c_neck_name = c_prefix+head_rig_names["neck"]
c_neck = create_edit_bone(c_neck_name)
copy_bone_transforms(neck, c_neck)
c_neck.parent = get_edit_bone(c_prefix+spine_rig_names["spine3"])
# ~ set_bone_layer(c_neck, layer_ctrl_idx)
set_bone_collection(rig, c_neck, coll_ctrl_name)
c_neck["mixamo_ctrl"] = 1# tag as controller bone
# Head Ctrl
c_head_name = c_prefix+head_rig_names["head"]
c_head = create_edit_bone(c_head_name)
copy_bone_transforms(head, c_head)
c_head.parent = c_neck
# ~ set_bone_layer(c_head, layer_ctrl_idx)
set_bone_collection(rig, c_head, coll_ctrl_name)
c_head["mixamo_ctrl"] = 1# tag as controller bone
# -- Pose --
bpy.ops.object.mode_set(mode='POSE')
c_neck_pb = get_pose_bone(c_neck_name)
c_head_pb = get_pose_bone(c_head_name)
# set custom shapes
set_bone_custom_shape(c_neck_pb, "cs_neck")
set_bone_custom_shape(c_head_pb, "cs_head")
# set rotation mode
c_neck_pb.rotation_mode = "XYZ"
c_head_pb.rotation_mode = "XYZ"
# set color group
set_bone_color_group(rig, c_neck_pb, "neck")
set_bone_color_group(rig, c_head_pb, "head")
# constraints
# Neck
neck_pb = get_pose_bone(neck_name)
head_pb = get_pose_bone(head_name)
add_copy_transf(neck_pb, rig, c_neck_name)
add_copy_transf(head_pb, rig, c_head_name)
def add_leg(side):
print(" Add Leg", side)
_side = "_"+side
thigh_name = get_mix_name(side+leg_names["thigh"], use_name_prefix)
calf_name = get_mix_name(side+leg_names["calf"], use_name_prefix)
foot_name = get_mix_name(side+leg_names["foot"], use_name_prefix)
toe_name = get_mix_name(side+leg_names["toes"], use_name_prefix)
toe_end_name = get_mix_name(side+leg_names["toes_end"], use_name_prefix)
# -- Edit --
bpy.ops.object.mode_set(mode='EDIT')
thigh = get_edit_bone(thigh_name)
calf = get_edit_bone(calf_name)
foot = get_edit_bone(foot_name)
toe = get_edit_bone(toe_name)
toe_end = get_edit_bone(toe_end_name)
hips = get_edit_bone(get_mix_name(spine_names["pelvis"], use_name_prefix))
c_hips_free_name = c_prefix+spine_rig_names["hips_free"]
c_hips_free = get_edit_bone(c_hips_free_name)
if not thigh or not calf or not foot or not toe:
print(" Leg bones are missing, skip leg: "+side)
return
# Set Mixamo bones in layer
for b in [thigh, calf, foot, toe, toe_end]:
set_bone_collection(rig, b, coll_mix_name)
# ~ set_bone_layer(b, layer_mix_idx)
# Create bones
# correct straight leg angle, need minimum 0.1 degrees for IK constraints to work
def get_leg_angle():
#return degrees(thigh.y_axis.angle(calf.y_axis))
vec1 = calf.head - thigh.head
vec2 = foot.head - calf.head
return degrees(vec1.angle(vec2))
leg_angle = get_leg_angle()
if leg_angle < 0.1:
print(" ! Straight leg bones, angle = "+str(leg_angle))
max_iter = 10000
i = 0
while leg_angle < 0.1 and i < max_iter:
dir = ((thigh.z_axis + calf.z_axis)*0.5).normalized()
calf.head += dir * (calf.tail-calf.head).magnitude * 0.0001
leg_angle = get_leg_angle()
i += 1
print(" corrected leg angle: "+str(leg_angle))
# Thigh IK
thigh_ik_name = leg_rig_names["thigh_ik"]+_side
thigh_ik = create_edit_bone(thigh_ik_name)
copy_bone_transforms(thigh, thigh_ik)
# auto-align knee position with global Y axis to ensure IK pole vector is physically correct
leg_axis = calf.tail - thigh.head
leg_midpoint = (thigh.head + calf.tail) * 0.5
#cur_vec = calf.head - leg_midpoint
#cur_vec[2] = 0.0
#global_y_vec = Vector((0, -1, 0))
dir = calf.head - leg_midpoint
cur_vec = project_vector_onto_plane(dir, leg_axis)
global_y_vec = project_vector_onto_plane(Vector((0, -1, 0)), leg_axis)
signed_cur_angle = signed_angle(cur_vec, global_y_vec, leg_axis)
print(" IK base angle:", degrees(signed_cur_angle))
# rotate
rotated_point = rotate_point(calf.head.copy(), -signed_cur_angle, leg_midpoint, leg_axis)
# (check)
dir = rotated_point - leg_midpoint
cur_vec = project_vector_onto_plane(dir, leg_axis)
signed_cur_angle = signed_angle(cur_vec, global_y_vec, leg_axis)
print(" IK corrected angle:", degrees(signed_cur_angle))
thigh_ik.tail = rotated_point
thigh_ik.parent = c_hips_free
set_bone_collection(rig, thigh_ik, coll_intern_name)
# ~ set_bone_layer(thigh_ik, layer_intern_idx)
# Thigh FK Ctrl
c_thigh_fk_name = c_prefix+leg_rig_names["thigh_fk"]+_side
c_thigh_fk = create_edit_bone(c_thigh_fk_name)
copy_bone_transforms(thigh_ik, c_thigh_fk)
c_thigh_fk.parent = c_hips_free
# ~ set_bone_layer(c_thigh_fk, layer_ctrl_idx)
set_bone_collection(rig, c_thigh_fk, coll_ctrl_name)
c_thigh_fk["mixamo_ctrl"] = 1# tag as controller bone
# Calf IK
calf_ik_name = leg_rig_names["calf_ik"]+_side
# check if bone exist to avoid undesired transformation when running the function multiple time
calf_ik_exist = get_edit_bone(calf_ik_name)
calf_ik = create_edit_bone(calf_ik_name)
if calf_ik_exist == None:
copy_bone_transforms(calf, calf_ik)
calf_ik.head = thigh_ik.tail.copy()
calf_ik.tail = foot.head.copy()
calf_ik.parent = thigh_ik
calf_ik.use_connect = True
# ~ set_bone_layer(calf_ik, layer_intern_idx)
set_bone_collection(rig, calf_ik, coll_intern_name)
# align thigh and calf IK roll
# align calf_ik local Z
align_bone_z_axis(calf_ik, (calf_ik.head-leg_midpoint))
# align thigh_ik on calf_ik
align_bone_z_axis(thigh_ik, calf_ik.z_axis)
# copy thigh_ik to c_thigh_fk
copy_bone_transforms(thigh_ik, c_thigh_fk)
# Calf FK Ctrl
c_calf_fk_name = c_prefix+leg_rig_names["calf_fk"]+_side
c_calf_fk = create_edit_bone(c_calf_fk_name)
copy_bone_transforms(calf_ik, c_calf_fk)
c_calf_fk.parent = c_thigh_fk
set_bone_collection(rig, c_calf_fk, coll_ctrl_name)
# ~ set_bone_layer(c_calf_fk, layer_ctrl_idx)
c_calf_fk["mixamo_ctrl"] = 1# tag as controller bone
# Foot FK Ctrl
c_foot_fk_name = c_prefix+leg_rig_names["foot_fk"]+_side
c_foot_fk = create_edit_bone(c_foot_fk_name)
copy_bone_transforms(foot, c_foot_fk)
c_foot_fk.tail[2] = foot.head[2]
align_bone_z_axis(c_foot_fk, Vector((0,0,1)))
c_foot_fk.parent = c_calf_fk
# ~ set_bone_layer(c_foot_fk, layer_ctrl_idx)
set_bone_collection(rig, c_foot_fk, coll_ctrl_name)
c_foot_fk["mixamo_ctrl"] = 1# tag as controller bone
# Foot FK
foot_fk_name = leg_rig_names["foot_fk"]+_side
foot_fk = create_edit_bone(foot_fk_name)
copy_bone_transforms(foot, foot_fk)
foot_fk.parent = c_foot_fk
set_bone_collection(rig, foot_fk, coll_intern_name)
# ~ set_bone_layer(foot_fk, layer_intern_idx)
# Foot IK Ctrl
c_foot_ik_name = c_prefix+leg_rig_names["foot_ik"]+_side
c_foot_ik = create_edit_bone(c_foot_ik_name)
copy_bone_transforms(foot, c_foot_ik)
c_foot_ik.tail[2] = foot.head[2]
align_bone_z_axis(c_foot_ik, Vector((0,0,1)))
set_bone_collection(rig, c_foot_ik, coll_ctrl_name)
# ~ set_bone_layer(c_foot_ik, layer_ctrl_idx)
c_foot_ik["mixamo_ctrl"] = 1# tag as controller bone
# Foot IK
foot_ik_name = leg_rig_names["foot_ik"]+_side
foot_ik = create_edit_bone(foot_ik_name)
copy_bone_transforms(foot, foot_ik)
foot_ik.parent = c_foot_ik
set_bone_collection(rig, foot_ik, coll_intern_name)
# ~ set_bone_layer(foot_ik, layer_intern_idx)
# Foot Snap
foot_snap_name = leg_rig_names["foot_snap"]+_side
foot_snap = create_edit_bone(foot_snap_name)
copy_bone_transforms(c_foot_ik, foot_snap)
foot_snap.parent = foot_ik
set_bone_collection(rig, foot_snap, coll_intern_name)
# ~ set_bone_layer(foot_snap, layer_intern_idx)
# Foot IK target
foot_ik_target_name = leg_rig_names["foot_ik_target"]+_side
foot_ik_target = create_edit_bone(foot_ik_target_name)
foot_ik_target.head = foot_ik.head.copy()
foot_vec = (foot.tail - foot.head)
foot_ik_target.tail = foot_ik_target.head - (foot_vec*0.25)
align_bone_z_axis(foot_ik_target, Vector((0,0,1)))
# parent set below (c_foot_01)
# ~ set_bone_layer(foot_ik_target, layer_intern_idx)
set_bone_collection(rig, foot_ik_target, coll_intern_name)
# Foot Heel Out
heel_out_name = leg_rig_names["heel_out"]+_side
heel_out = create_edit_bone(heel_out_name)
heel_out.head, heel_out.tail = Vector((0,0,0)), Vector((0,0,1))
heel_out.parent = c_foot_ik
# ~ set_bone_layer(heel_out, layer_intern_idx)
set_bone_collection(rig, heel_out, coll_intern_name)
# Foot Heel In
heel_in_name = leg_rig_names["heel_in"]+_side
heel_in = create_edit_bone(heel_in_name)
heel_in.head, heel_in.tail = Vector((0,0,0)), Vector((0,0,1))
heel_in.parent = heel_out
# ~ set_bone_layer(heel_in, layer_intern_idx)
set_bone_collection(rig, heel_in, coll_intern_name)
# Foot Heel Mid
heel_mid_name = leg_rig_names["heel_mid"]+_side
heel_mid = create_edit_bone(heel_mid_name)
heel_mid.head, heel_mid.tail = Vector((0,0,0)), Vector((0,0,1))
heel_mid.parent = heel_in
# ~ set_bone_layer(heel_mid, layer_intern_idx)
set_bone_collection(rig, heel_mid, coll_intern_name)
heel_mid.head[0], heel_mid.head[1], heel_mid.head[2] = foot.head[0], foot.head[1], foot.tail[2]
heel_mid.tail = foot.tail.copy()
heel_mid.tail[2] = heel_mid.head[2]
heel_mid.tail = heel_mid.head + (heel_mid.tail-heel_mid.head)*0.5
align_bone_x_axis(heel_mid, foot.x_axis)
copy_bone_transforms(heel_mid, heel_in)
# use the foot x axis to determine "inside" vector, make sure it's pointing in the right direction for right and left side
fac = 1
if side == "Right":
fac = -1
heel_in.head += foot.x_axis.normalized() * foot.length*0.3 * fac
heel_in.tail += foot.x_axis.normalized() * foot.length*0.3 * fac
copy_bone_transforms(heel_mid, heel_out)
heel_out.head += foot.x_axis.normalized() * foot.length*0.3 * -fac
heel_out.tail += foot.x_axis.normalized() * foot.length*0.3 * -fac
# Toe End
toes_end_name = leg_rig_names["toes_end"]+_side
toes_end = create_edit_bone(toes_end_name)
copy_bone_transforms(toe, toes_end)
toe_vec = (toes_end.tail-toes_end.head)
toes_end.tail += toe_vec
toes_end.head += toe_vec
toes_end.parent = heel_mid
# ~ set_bone_layer(toes_end, layer_intern_idx)
set_bone_collection(rig, toes_end, coll_intern_name)
# Toe End 01
toes_end_01_name = leg_rig_names["toes_end_01"]+_side
toes_end_01 = create_edit_bone(toes_end_01_name)
copy_bone_transforms(toes_end, toes_end_01)
vec = toes_end_01.tail -toes_end_01.head
toes_end_01.tail = toes_end_01.head + (vec*0.5)
toes_end_01.parent = toes_end
# ~ set_bone_layer(toes_end_01, layer_intern_idx)
set_bone_collection(rig, toes_end_01, coll_intern_name)
# Foot 01 Ctrl
c_foot_01_name = c_prefix+leg_rig_names["foot_01"]+_side
c_foot_01 = create_edit_bone(c_foot_01_name)
copy_bone_transforms(foot, c_foot_01)
c_foot_01_vec = c_foot_01.tail - c_foot_01.head
c_foot_01.tail += c_foot_01_vec
c_foot_01.head += c_foot_01_vec
c_foot_01.parent = toes_end
# ~ set_bone_layer(c_foot_01, layer_ctrl_idx)
set_bone_collection(rig, c_foot_01, coll_ctrl_name)
c_foot_01["mixamo_ctrl"] = 1# tag as controller bone
# Foot_ik_target parent
foot_ik_target.parent = c_foot_01
# Foot 01 Pole
foot_01_pole_name = leg_rig_names["foot_01_pole"]+_side
foot_01_pole = create_edit_bone(foot_01_pole_name)
foot_01_pole.head = c_foot_01.head + (c_foot_01.z_axis * 0.05 * c_foot_01.length * 40)
foot_01_pole.tail = foot_01_pole.head + (c_foot_01.z_axis * 0.05 * c_foot_01.length * 40)
foot_01_pole.roll = radians(180)
foot_01_pole.parent = c_foot_01
# ~ set_bone_layer(foot_01_pole, layer_intern_idx)
set_bone_collection(rig, foot_01_pole, coll_intern_name)
# Toe IK Ctrl
c_toe_ik_name = c_prefix+leg_rig_names["toes_ik"]+_side
c_toe_ik = create_edit_bone(c_toe_ik_name)
copy_bone_transforms(toe, c_toe_ik)
c_toe_ik.parent = toes_end
# ~ set_bone_layer(c_toe_ik, layer_ctrl_idx)
set_bone_collection(rig, c_toe_ik, coll_ctrl_name)
c_toe_ik["mixamo_ctrl"] = 1# tag as controller bone
# Toe Track
toe_track_name = leg_rig_names["toes_track"]+_side
toe_track = create_edit_bone(toe_track_name)
copy_bone_transforms(toe, toe_track)
toe_track.parent = foot_ik
# ~ set_bone_layer(toe_track, layer_intern_idx)
set_bone_collection(rig, toe_track, coll_intern_name)
# Toe_01 IK
toe_01_ik_name = leg_rig_names["toes_01_ik"]+_side
toe_01_ik = create_edit_bone(toe_01_ik_name)
copy_bone_transforms(toe, toe_01_ik)
toe_01_ik.tail = toe_01_ik.head + (toe_01_ik.tail-toe_01_ik.head)*0.5
toe_01_ik.parent = toe_track
# ~ set_bone_layer(toe_01_ik, layer_intern_idx)
set_bone_collection(rig, toe_01_ik, coll_intern_name)
# Toe_02
toe_02_name = leg_rig_names["toes_02"]+_side
toe_02 = create_edit_bone(toe_02_name)
copy_bone_transforms(toe, toe_02)
toe_02.head = toe_02.head + (toe_02.tail-toe_02.head)*0.5
toe_02.parent = toe_01_ik
# ~ set_bone_layer(toe_02, layer_intern_idx)
set_bone_collection(rig, toe_02, coll_intern_name)
# Toe FK Ctrl
c_toe_fk_name = c_prefix+leg_rig_names["toes_fk"]+_side
c_toe_fk = create_edit_bone(c_toe_fk_name)
copy_bone_transforms(toe, c_toe_fk)
c_toe_fk.parent = foot_fk
# ~ set_bone_layer(c_toe_fk, layer_ctrl_idx)
set_bone_collection(rig, c_toe_fk, coll_ctrl_name)
c_toe_fk["mixamo_ctrl"] = 1# tag as controller bone
# Foot Roll Cursor Ctrl
c_foot_roll_cursor_name = c_prefix+leg_rig_names["foot_roll_cursor"]+_side
c_foot_roll_cursor = create_edit_bone(c_foot_roll_cursor_name)
copy_bone_transforms(c_foot_ik, c_foot_roll_cursor)
vec = c_foot_roll_cursor.tail - c_foot_roll_cursor.head
dist = 1.2
c_foot_roll_cursor.head -= vec*dist
c_foot_roll_cursor.tail -= vec*dist
c_foot_roll_cursor.parent = c_foot_ik
# ~ set_bone_layer(c_foot_roll_cursor, layer_ctrl_idx)
set_bone_collection(rig, c_foot_roll_cursor, coll_ctrl_name)
c_foot_roll_cursor["mixamo_ctrl"] = 1# tag as controller bone
# Pole IK Ctrl
c_pole_ik_name = c_prefix+leg_rig_names["pole_ik"]+_side
c_pole_ik = create_edit_bone(c_pole_ik_name)
# ~ set_bone_layer(c_pole_ik, layer_ctrl_idx)
set_bone_collection(rig, c_pole_ik, coll_ctrl_name)
c_pole_ik["mixamo_ctrl"] = 1# tag as controller bone
plane_normal = (thigh_ik.head - calf_ik.tail)
prepole_dir = calf_ik.head - leg_midpoint
pole_pos = calf_ik.head + prepole_dir.normalized()
pole_pos = project_point_onto_plane(pole_pos, calf_ik.head, plane_normal)
pole_pos = calf_ik.head + ((pole_pos - calf_ik.head).normalized() * (calf_ik.head - thigh.head).magnitude * 1.7)
c_pole_ik.head = pole_pos
c_pole_ik.tail = [c_pole_ik.head[0], c_pole_ik.head[1], c_pole_ik.head[2] + (0.165 * thigh_ik.length * 2)]
ik_pole_angle = get_pole_angle(thigh_ik, calf_ik, c_pole_ik.head)
# -- Pose --
bpy.ops.object.mode_set(mode='POSE')
# Add constraints to control/mechanic bones
# Calf IK
calf_ik_pb = get_pose_bone(calf_ik_name)
cns_name = "IK"
ik_cns = calf_ik_pb.constraints.get(cns_name)
if ik_cns == None:
ik_cns = calf_ik_pb.constraints.new("IK")
ik_cns.name = cns_name
ik_cns.target = rig
ik_cns.subtarget = foot_ik_target_name
ik_cns.pole_target = rig
ik_cns.pole_subtarget = c_pole_ik_name
ik_cns.pole_angle = ik_pole_angle
ik_cns.chain_count = 2
ik_cns.use_tail = True
ik_cns.use_stretch = False
calf_ik_pb.lock_ik_y = True
calf_ik_pb.lock_ik_z = True
# Foot IK
foot_ik_pb = get_pose_bone(foot_ik_name)
cns_name = "Copy Location"
copy_loc_cns = foot_ik_pb.constraints.get(cns_name)
if copy_loc_cns == None:
copy_loc_cns = foot_ik_pb.constraints.new("COPY_LOCATION")
copy_loc_cns.name = cns_name
copy_loc_cns.target = rig
copy_loc_cns.subtarget = calf_ik_name
copy_loc_cns.head_tail = 1.0
cns_name = "TrackTo"
cns = foot_ik_pb.constraints.get(cns_name)
if cns == None:
cns = foot_ik_pb.constraints.new("TRACK_TO")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_foot_01_name
cns.head_tail = 0.0
cns.track_axis = "TRACK_Y"
cns.up_axis = "UP_Z"
cns.use_target_z = True
cns_name = "Locked Track"
cns = foot_ik_pb.constraints.get(cns_name)
if cns == None:
cns = foot_ik_pb.constraints.new("LOCKED_TRACK")
cns.name = cns_name
cns.target = rig
cns.subtarget = foot_01_pole_name
cns.head_tail = 0.0
cns.track_axis = "TRACK_Z"
cns.lock_axis = "LOCK_Y"
cns_name = "Copy Scale"
cns = foot_ik_pb.constraints.get(cns_name)
if cns == None:
cns = foot_ik_pb.constraints.new("COPY_SCALE")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_foot_ik_name
# Foot Ctrl IK
c_foot_ik_pb = get_pose_bone(c_foot_ik_name)
cns_name = "Child Of"
cns = c_foot_ik_pb.constraints.get(cns_name)
if cns == None:
cns = c_foot_ik_pb.constraints.new("CHILD_OF")
cns.name = cns_name
cns.target = rig
cns.subtarget = "Ctrl_Master"
# Pole IK
c_pole_ik_pb = get_pose_bone(c_pole_ik_name)
cns_name = "Child Of"
child_cns = c_pole_ik_pb.constraints.get(cns_name)
if child_cns == None:
child_cns = c_pole_ik_pb.constraints.new("CHILD_OF")
child_cns.name = cns_name
child_cns.target = rig
child_cns.subtarget = c_foot_ik_name
cns_power = 8
# Toe End
toes_end_pb = get_pose_bone(toes_end_name)
len = toes_end_pb.length * cns_power
cns_name = "Transformation"
cns = toes_end_pb.constraints.get(cns_name)
if cns == None:
cns = toes_end_pb.constraints.new("TRANSFORM")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_foot_roll_cursor_name
cns.use_motion_extrapolate = True
cns.target_space = cns.owner_space ="LOCAL"
cns.map_from = "LOCATION"
cns.from_min_z = 0.5 * len
cns.from_max_z = -0.5 * len
cns.map_to = "ROTATION"
cns.map_to_x_from = "Z"
cns.map_to_z_from = "X"
cns.to_min_x_rot = -2.61
cns.to_max_x_rot = 2.61
cns.mix_mode_rot = "ADD"
cns_name = "Limit Rotation"
cns = toes_end_pb.constraints.get(cns_name)
if cns == None:
cns = toes_end_pb.constraints.new("LIMIT_ROTATION")
cns.name = cns_name
cns.owner_space ="LOCAL"
cns.use_limit_x = True
cns.min_x = -2*pi
cns.max_x = 0.0
# Toe 01 ik
toe_01_ik_pb = get_pose_bone(toe_01_ik_name)
cns_name = "Copy Transforms"
cns = toe_01_ik_pb.constraints.get(cns_name)
if cns == None:
cns = toe_01_ik_pb.constraints.new("COPY_TRANSFORMS")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_toe_ik_name
cns.mix_mode = "REPLACE"
cns.target_space = cns.owner_space = "WORLD"
# Toe 02
toe_02_pb = get_pose_bone(toe_02_name)
cns_name = "Copy CopyRotation"
cns = toe_02_pb.constraints.get(cns_name)
if cns == None:
cns = toe_02_pb.constraints.new("COPY_ROTATION")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_toe_ik_name
cns.mix_mode = "REPLACE"
cns.target_space = cns.owner_space = "WORLD"
# Toe Track
toe_track_pb = get_pose_bone(toe_track_name)
cns_name = "TrackTo"
cns = toe_track_pb.constraints.get(cns_name)
if cns == None:
cns = toe_track_pb.constraints.new("TRACK_TO")
cns.name = cns_name
cns.target = rig
cns.subtarget = toes_end_01_name
cns.head_tail = 0.0
cns.track_axis = 'TRACK_Y'
cns.up_axis = "UP_Z"
cns.use_target_z = True
# Heel Mid
heel_mid_pb = get_pose_bone(heel_mid_name)
len = heel_mid_pb.length * cns_power
cns_name = "Transformation"
cns = heel_mid_pb.constraints.get(cns_name)
if cns == None:
cns = heel_mid_pb.constraints.new("TRANSFORM")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_foot_roll_cursor_name
cns.owner_space = cns.target_space = "LOCAL"
cns.map_from = "LOCATION"
cns.from_min_z = -0.25 * len
cns.from_max_z = 0.25 * len
cns.map_to = "ROTATION"
cns.map_to_x_from = "Z"
cns.map_to_y_from = "X"
cns.map_to_z_from = "Y"
cns.to_min_x_rot = radians(100)
cns.to_max_x_rot = -radians(100)
cns.mix_mode_rot = 'ADD'
cns_name = "Limit Rotation"
cns = heel_mid_pb.constraints.get(cns_name)
if cns == None:
cns = heel_mid_pb.constraints.new("LIMIT_ROTATION")
cns.name = cns_name
cns.use_limit_x = True
cns.min_x = radians(0)
cns.max_x = radians(360)
cns.owner_space = "LOCAL"
# Heel In
heel_in_pb = get_pose_bone(heel_in_name)
len = heel_in_pb.length * cns_power
cns_name = "Transformation"
cns = heel_in_pb.constraints.get(cns_name)
if cns == None:
cns = heel_in_pb.constraints.new("TRANSFORM")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_foot_roll_cursor_name
cns.owner_space = cns.target_space = "LOCAL"
cns.map_from = "LOCATION"
cns.from_min_x = -0.25 * len
cns.from_max_x = 0.25 * len
cns.map_to = "ROTATION"
cns.map_to_x_from = "Z"
cns.map_to_y_from = "X"
cns.map_to_z_from = "Y"
cns.to_min_y_rot = -radians(100)
cns.to_max_y_rot = radians(100)
cns.mix_mode_rot = 'ADD'
cns_name = "Limit Rotation"
cns = heel_in_pb.constraints.get(cns_name)
if cns == None:
cns = heel_in_pb.constraints.new("LIMIT_ROTATION")
cns.name = cns_name
cns.use_limit_y = True
if side == "Left":
cns.min_y = 0.0
cns.max_y = radians(90)
elif side == "Right":
cns.min_y = radians(-90)
cns.max_y = radians(0.0)
cns.owner_space = "LOCAL"
# Heel Out
heel_out_pb = get_pose_bone(heel_out_name)
len = heel_out_pb.length * cns_power
cns_name = "Transformation"
cns = heel_out_pb.constraints.get(cns_name)
if cns == None:
cns = heel_out_pb.constraints.new("TRANSFORM")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_foot_roll_cursor_name
cns.owner_space = cns.target_space = "LOCAL"
cns.map_from = "LOCATION"
cns.from_min_x = -0.25 * len
cns.from_max_x = 0.25 * len
cns.map_to = "ROTATION"
cns.map_to_x_from = "Z"
cns.map_to_y_from = "X"
cns.map_to_z_from = "Y"
cns.to_min_y_rot = -radians(100)
cns.to_max_y_rot = radians(100)
cns.mix_mode_rot = 'ADD'
cns_name = "Limit Rotation"
cns = heel_out_pb.constraints.get(cns_name)
if cns == None:
cns = heel_out_pb.constraints.new("LIMIT_ROTATION")
cns.name = cns_name
cns.use_limit_y = True
if side == "Left":
cns.min_y = radians(-90)
cns.max_y = radians(0.0)
elif side == "Right":
cns.min_y = radians(0.0)
cns.max_y = radians(90)
cns.owner_space = "LOCAL"
# Add constraints to Mixamo bones
foot_pb = get_pose_bone(foot_name)
thigh_pb = get_pose_bone(thigh_name)
# IK-FK switch property
if not "ik_fk_switch" in c_foot_ik_pb.keys():
create_custom_prop(node=c_foot_ik_pb, prop_name="ik_fk_switch", prop_val=0.0, prop_min=0.0, prop_max=1.0, prop_description="IK-FK switch value")
c_foot_ik_pb["ik_fk_switch"] = 0.0 if self.ik_legs else 1.0
# Thigh
cns_name = "IK_follow"
cns_ik = thigh_pb.constraints.get(cns_name)
if cns_ik == None:
cns_ik = thigh_pb.constraints.new("COPY_TRANSFORMS")
cns_ik.name = cns_name
cns_ik.target = rig
cns_ik.subtarget = thigh_ik_name
cns_ik.influence = 1.0
cns_name = "FK_follow"
cns_fk = thigh_pb.constraints.get(cns_name)
if cns_fk == None:
cns_fk = thigh_pb.constraints.new("COPY_TRANSFORMS")
cns_fk.name = cns_name
cns_fk.target = rig
cns_fk.subtarget = c_thigh_fk_name
cns_fk.influence = 0.0
add_driver_to_prop(rig, 'pose.bones["'+thigh_name+'"].constraints["'+cns_name+'"].influence', 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
# Calf
calf_pb = get_pose_bone(calf_name)
cns_name = "IK_follow"
cns_ik = calf_pb.constraints.get(cns_name)
if cns_ik == None:
cns_ik = calf_pb.constraints.new("COPY_TRANSFORMS")
cns_ik.name = cns_name
cns_ik.target = rig
cns_ik.subtarget = calf_ik_name
cns_ik.influence = 1.0
cns_name = "FK_follow"
cns_fk = calf_pb.constraints.get(cns_name)
if cns_fk == None:
cns_fk = calf_pb.constraints.new("COPY_TRANSFORMS")
cns_fk.name = cns_name
cns_fk.target = rig
cns_fk.subtarget = c_calf_fk_name
cns_fk.influence = 0.0
add_driver_to_prop(rig, 'pose.bones["'+calf_name+'"].constraints["'+cns_name+'"].influence', 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
# Foot
cns_name = "IK_follow"
cns_ik = foot_pb.constraints.get(cns_name)
if cns_ik == None:
cns_ik = foot_pb.constraints.new("COPY_TRANSFORMS")
cns_ik.name = cns_name
cns_ik.target = rig
cns_ik.subtarget = foot_ik_name
cns_ik.influence = 1.0
cns_name = "FK_follow"
cns_fk = foot_pb.constraints.get(cns_name)
if cns_fk == None:
cns_fk = foot_pb.constraints.new("COPY_TRANSFORMS")
cns_fk.name = cns_name
cns_fk.target = rig
cns_fk.subtarget = foot_fk_name
cns_fk.influence = 0.0
add_driver_to_prop(rig, 'pose.bones["'+foot_name+'"].constraints["'+cns_name+'"].influence', 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
# Toe
toe_pb = get_pose_bone(toe_name)
cns_name = "IK_Rot_follow"
cns_ik_rot = toe_pb.constraints.get(cns_name)
if cns_ik_rot == None:
cns_ik_rot = toe_pb.constraints.new("COPY_ROTATION")
cns_ik_rot.name = cns_name
cns_ik_rot.target = rig
cns_ik_rot.subtarget = c_toe_ik_name
cns_ik_rot.influence = 1.0
cns_name = "IK_Scale_follow"
cns_ik_scale = toe_pb.constraints.get(cns_name)
if cns_ik_scale == None:
cns_ik_scale = toe_pb.constraints.new("COPY_SCALE")
cns_ik_scale.name = cns_name
cns_ik_scale.target = rig
cns_ik_scale.subtarget = c_toe_ik_name
cns_ik_scale.influence = 1.0
cns_name_fk_rot = "FK_Rot_follow"
cns_fk_rot = toe_pb.constraints.get(cns_name_fk_rot)
if cns_fk_rot == None:
cns_fk_rot = toe_pb.constraints.new("COPY_ROTATION")
cns_fk_rot.name = cns_name_fk_rot
cns_fk_rot.target = rig
cns_fk_rot.subtarget = c_toe_fk_name
cns_fk_rot.influence = 1.0
cns_name_fk_scale = "FK_Scale_follow"
cns_fk_scale = toe_pb.constraints.get(cns_name_fk_scale)
if cns_fk_scale == None:
cns_fk_scale = toe_pb.constraints.new("COPY_SCALE")
cns_fk_scale.name = cns_name_fk_scale
cns_fk_scale.target = rig
cns_fk_scale.subtarget = c_toe_fk_name
cns_fk_scale.influence = 1.0
add_driver_to_prop(rig, 'pose.bones["'+toe_name+'"].constraints["'+cns_name_fk_rot+'"].influence', 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
add_driver_to_prop(rig, 'pose.bones["'+toe_name+'"].constraints["'+cns_name_fk_scale+'"].influence', 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
c_foot_01_pb = get_pose_bone(c_foot_01_name)
c_foot_roll_cursor_pb = get_pose_bone(c_foot_roll_cursor_name)
c_thigh_fk_pb = get_pose_bone(c_thigh_fk_name)
c_calf_fk_pb = get_pose_bone(c_calf_fk_name)
c_foot_fk_pb = get_pose_bone(c_foot_fk_name)
c_toe_ik_pb = get_pose_bone(c_toe_ik_name)
c_toe_fk_pb = get_pose_bone(c_toe_fk_name)
# Set transforms locks
lock_pbone_transform(c_foot_roll_cursor_pb, "location", [1])
lock_pbone_transform(c_foot_roll_cursor_pb, "rotation", [0,1,2])
lock_pbone_transform(c_foot_roll_cursor_pb, "scale", [0,1,2])
lock_pbone_transform(c_foot_01_pb, "location", [0,1,2])
lock_pbone_transform(c_foot_01_pb, "rotation", [1,2])
lock_pbone_transform(c_foot_01_pb, "scale", [0,1,2])
lock_pbone_transform(c_foot_fk_pb, "location", [0,1,2])
lock_pbone_transform(c_pole_ik_pb, "rotation", [0,1,2])
lock_pbone_transform(c_pole_ik_pb, "scale", [0,1,2])
lock_pbone_transform(c_thigh_fk_pb, "location", [0,1,2])
lock_pbone_transform(c_calf_fk_pb, "location", [0,1,2])
c_pbones_list = [c_foot_ik_pb, c_pole_ik_pb, c_foot_01_pb, c_foot_roll_cursor_pb, c_thigh_fk_pb, c_calf_fk_pb, c_foot_fk_pb, c_toe_fk_pb, c_toe_ik_pb]
# Set custom shapes
set_bone_custom_shape(c_thigh_fk_pb, "cs_thigh_fk")
set_bone_custom_shape(c_calf_fk_pb, "cs_calf_fk")
set_bone_custom_shape(c_foot_ik_pb, "cs_foot")
set_bone_custom_shape(c_foot_fk_pb, "cs_foot")
set_bone_custom_shape(c_pole_ik_pb, "cs_sphere_012")
set_bone_custom_shape(c_foot_roll_cursor_pb, "cs_foot_roll")
set_bone_custom_shape(c_foot_01_pb, "cs_foot_01")
set_bone_custom_shape(c_toe_fk_pb, "cs_toe")
set_bone_custom_shape(c_toe_ik_pb, "cs_toe")
# set custom shape drivers
ik_controls_names = [c_foot_ik_name, c_foot_01_name, c_toe_ik_name, c_foot_roll_cursor_name, c_pole_ik_name]
arr_ids = [-1]
if blender_version._float >= 300:
arr_ids = [0, 1, 2]
for n in ik_controls_names:
dr_dp = 'pose.bones["'+n+'"].'+get_custom_shape_scale_prop_name()
tar_dp = 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]'
for arr_id in arr_ids:
add_driver_to_prop(rig, dr_dp, tar_dp, array_idx=arr_id, exp="1-var")
fk_controls_names = [c_foot_fk_name, c_thigh_fk_name, c_calf_fk_name, c_toe_fk_name]
for n in fk_controls_names:
dr_dp = 'pose.bones["'+n+'"].'+get_custom_shape_scale_prop_name()
tar_dp = 'pose.bones["'+c_foot_ik_name+'"]["ik_fk_switch"]'
for arr_id in arr_ids:
add_driver_to_prop(rig, dr_dp, tar_dp, array_idx=arr_id, exp="var")
for pb in c_pbones_list:
# set rotation euler
pb.rotation_mode = "XYZ"
# set color group
set_bone_color_group(rig, pb, "body"+_side.lower())
def add_arm(side):
print(" Add Arm", side)
_side = "_"+side
shoulder_name = get_mix_name(side+arm_names["shoulder"], use_name_prefix)
arm_name = get_mix_name(side+arm_names["arm"], use_name_prefix)
forearm_name = get_mix_name(side+arm_names["forearm"], use_name_prefix)
hand_name = get_mix_name(side+arm_names["hand"], use_name_prefix)
# -- Edit --
bpy.ops.object.mode_set(mode='EDIT')
shoulder = get_edit_bone(shoulder_name)
arm = get_edit_bone(arm_name)
forearm = get_edit_bone(forearm_name)
hand = get_edit_bone(hand_name)
if not shoulder or not arm or not forearm or not hand:
print(" Arm bones are missing, skip arm: "+side)
return
# Create bones
# Fingers
fingers_names = []
c_fingers_names = []
fingers = []
finger_leaves = []
for fname in fingers_type:
for i in range(1, 4):
finger_name = get_mix_name(side+"Hand"+fname+str(i), use_name_prefix)
finger = get_edit_bone(finger_name)
if finger == None:
continue
fingers_names.append(finger_name)
fingers.append(finger)
c_finger_name = c_prefix+fname+str(i)+_side
c_fingers_names.append(c_finger_name)
c_finger = create_edit_bone(c_finger_name)
copy_bone_transforms(finger, c_finger)
# ~ set_bone_layer(c_finger, 0)
set_bone_collection(rig, c_finger, coll_ctrl_name)
c_finger["mixamo_ctrl"] = 1# tag as controller bone
if i == 1:
c_finger.parent = hand
else:
prev_finger_name = c_prefix+fname+str(i-1)+_side
prev_finger = get_edit_bone(prev_finger_name)
c_finger.parent = prev_finger
# fingers "leaves"/tip bones
for fname in fingers_type:
finger_name = get_mix_name(side+"Hand"+fname+"4", use_name_prefix)
finger_leaf = get_edit_bone(finger_name)
finger_leaves.append(finger_leaf)
# Set Mixamo bones in layer
for b in [shoulder, arm, forearm, hand] + fingers + finger_leaves:
# ~ set_bone_layer(b, layer_mix_idx)
set_bone_collection(rig, b, coll_mix_name)
# Shoulder Ctrl
c_shoulder_name = c_prefix+arm_rig_names["shoulder"]+_side
c_shoulder = create_edit_bone(c_shoulder_name)
copy_bone_transforms(shoulder, c_shoulder)
c_shoulder.parent = get_edit_bone(c_prefix+spine_rig_names["spine3"])
# ~ set_bone_layer(c_shoulder, layer_ctrl_idx)
set_bone_collection(rig, c_shoulder, coll_ctrl_name)
c_shoulder["mixamo_ctrl"] = 1# tag as controller bone
# Arm IK
arm_ik_name = arm_rig_names["arm_ik"]+_side
arm_ik = create_edit_bone(arm_ik_name)
copy_bone_transforms(arm, arm_ik)
# correct straight arms angle, need minimum 0.1 degrees for IK constraints to work
angle_min = 0.1
def get_arm_angle():
#return degrees(arm.y_axis.angle(forearm.y_axis))
vec1 = forearm.head - arm.head
vec2 = hand.head - forearm.head
return degrees(vec1.angle(vec2))
arm_angle = get_arm_angle()
if arm_angle < angle_min:
print(" ! Straight arm bones, angle = "+str(arm_angle))
max_iter = 10000
i = 0
while arm_angle < angle_min and i < max_iter:
dir = ((arm.x_axis + forearm.x_axis)*0.5).normalized()
if side == "Right":
dir *= -1
forearm.head += dir * (forearm.tail-forearm.head).magnitude * 0.0001
arm_angle = get_arm_angle()
i += 1
print(" corrected arm angle: "+str(arm_angle))
# auto-align knee position with global Y axis to ensure IK pole vector is physically correct
arm_axis = forearm.tail - arm.head
arm_midpoint = (arm.head + forearm.tail) * 0.5
#cur_vec = forearm.head - arm_midpoint
#cur_vec[0] = 0.0
#global_y_vec = Vector((0, 1, 0))
dir = forearm.head - arm_midpoint
cur_vec = project_vector_onto_plane(dir, arm_axis)
global_y_vec = project_vector_onto_plane(Vector((0, 1, 0)), arm_axis)
signed_cur_angle = signed_angle(cur_vec, global_y_vec, arm_axis)
print(" IK correc angle:", degrees(signed_cur_angle))
# rotate
rotated_point = rotate_point(forearm.head.copy(), -signed_cur_angle, arm_midpoint, arm_axis)
"""
rot_mat = Matrix.Rotation(-signed_cur_angle, 4, arm_axis.normalized())
# rotate in world origin space
offset_vec = -arm_midpoint
offset_elbow = forearm.head + offset_vec
# rotate
rotated_point = rot_mat @ offset_elbow
# bring back to original space
rotated_point = rotated_point -offset_vec
"""
# (check)
dir = rotated_point - arm_midpoint
cur_vec = project_vector_onto_plane(dir, arm_axis)
signed_cur_angle = signed_angle(cur_vec, global_y_vec, arm_axis)
print(" IK corrected angle:", degrees(signed_cur_angle))
arm_ik.tail = rotated_point
arm_ik.parent = c_shoulder
# ~ set_bone_layer(arm_ik, layer_intern_idx)
set_bone_collection(rig, arm_ik, coll_intern_name)
# Arm FK Ctrl
c_arm_fk_name = c_prefix+arm_rig_names["arm_fk"]+_side
c_arm_fk = create_edit_bone(c_arm_fk_name)
c_arm_fk.parent = get_edit_bone(c_prefix+spine_rig_names["spine3"])
copy_bone_transforms(arm_ik, c_arm_fk)
# ~ set_bone_layer(c_arm_fk, layer_ctrl_idx)
set_bone_collection(rig, c_arm_fk, coll_ctrl_name)
c_arm_fk["mixamo_ctrl"] = 1# tag as controller bone
# ForeArm IK
forearm_ik_name = arm_rig_names["forearm_ik"]+_side
forearm_ik = create_edit_bone(forearm_ik_name)
copy_bone_transforms(forearm, forearm_ik)
forearm_ik.head = arm_ik.tail.copy()
forearm_ik.tail = hand.head.copy()
forearm_ik.parent = arm_ik
# ~ set_bone_layer(forearm_ik, layer_intern_idx)
set_bone_collection(rig, forearm_ik, coll_intern_name)
# align arm and forearm IK roll
# align forearm_ik local Z
align_bone_x_axis(forearm_ik, (forearm_ik.head-arm_midpoint))
# align arm_ik on forearm_ik
align_bone_x_axis(arm_ik, forearm_ik.x_axis)
# copy arm_ik to c_arm_fk
copy_bone_transforms(arm_ik, c_arm_fk)
if side == "Right":
forearm_ik.roll += radians(180)
arm_ik.roll += radians(180)
c_arm_fk.roll += radians(180)
# Forearm FK Ctrl
c_forearm_fk_name = c_prefix+arm_rig_names["forearm_fk"]+_side
c_forearm_fk = create_edit_bone(c_forearm_fk_name)
copy_bone_transforms(forearm_ik, c_forearm_fk)
c_forearm_fk.parent = c_arm_fk
# ~ set_bone_layer(c_forearm_fk, layer_ctrl_idx)
set_bone_collection(rig, c_forearm_fk, coll_ctrl_name)
c_forearm_fk["mixamo_ctrl"] = 1# tag as controller bone
# Pole IK Ctrl
c_pole_ik_name = c_prefix+arm_rig_names["pole_ik"]+_side
c_pole_ik = create_edit_bone(c_pole_ik_name)
# ~ set_bone_layer(c_pole_ik, layer_ctrl_idx)
set_bone_collection(rig, c_pole_ik, coll_ctrl_name)
c_pole_ik["mixamo_ctrl"] = 1# tag as controller bone
arm_midpoint = (arm_ik.head + forearm_ik.tail) * 0.5
plane_normal = (arm_ik.head - forearm_ik.tail)
prepole_dir = forearm_ik.head - arm_midpoint
pole_pos = forearm_ik.head + prepole_dir.normalized()
pole_pos = project_point_onto_plane(pole_pos, forearm_ik.head, plane_normal)
pole_pos = forearm_ik.head + ((pole_pos - forearm_ik.head).normalized() * (forearm_ik.head - arm.head).magnitude * 1.0)
c_pole_ik.head = pole_pos
c_pole_ik.tail = [c_pole_ik.head[0], c_pole_ik.head[1], c_pole_ik.head[2] + (0.165 * arm_ik.length * 4)]
ik_pole_angle = get_pole_angle(arm_ik, forearm_ik, c_pole_ik.head)
# Hand IK Ctrl
c_hand_ik_name = c_prefix + arm_rig_names["hand_ik"]+_side
c_hand_ik = create_edit_bone(c_hand_ik_name)
# ~ set_bone_layer(c_hand_ik, layer_ctrl_idx)
set_bone_collection(rig, c_hand_ik, coll_ctrl_name)
copy_bone_transforms(hand, c_hand_ik)
c_hand_ik["mixamo_ctrl"] = 1# tag as controller bone
# Hand FK Ctrl
c_hand_fk_name = c_prefix+arm_rig_names["hand_fk"]+_side
c_hand_fk = create_edit_bone(c_hand_fk_name)
copy_bone_transforms(hand, c_hand_fk)
c_hand_fk.parent = c_forearm_fk
# ~ set_bone_layer(c_hand_fk, layer_ctrl_idx)
set_bone_collection(rig, c_hand_fk, coll_ctrl_name)
c_hand_fk["mixamo_ctrl"] = 1# tag as controller bone
# ---- Pose ----
bpy.ops.object.mode_set(mode='POSE')
# Add constraints to control/mechanic bones
c_shoulder_pb = get_pose_bone(c_shoulder_name)
shoulder_pb = get_pose_bone(shoulder_name)
c_arm_fk_pb = get_pose_bone(c_arm_fk_name)
forearm_ik_pb = get_pose_bone(forearm_ik_name)
c_pole_ik_pb = get_pose_bone(c_pole_ik_name)
c_hand_ik_pb = get_pose_bone(c_hand_ik_name)
# Arm FK Ctrl
cns_name = "Copy Location"
cns = c_arm_fk_pb.constraints.get(cns_name)
if cns == None:
cns = c_arm_fk_pb.constraints.new("COPY_LOCATION")
cns.name = cns_name
cns.head_tail = 1.0
cns.target = rig
cns.subtarget = c_shoulder_name
# Forearm IK
cns_name = "IK"
ik_cns = forearm_ik_pb.constraints.get(cns_name)
if ik_cns == None:
ik_cns = forearm_ik_pb.constraints.new("IK")
ik_cns.name = cns_name
ik_cns.target = rig
ik_cns.subtarget = c_hand_ik_name
ik_cns.pole_target = rig
ik_cns.pole_subtarget = c_pole_ik_name
ik_cns.pole_angle = 0.0
if side == "Right":
ik_cns.pole_angle = radians(180)
ik_cns.chain_count = 2
ik_cns.use_tail = True
ik_cns.use_stretch = False
forearm_ik_pb.lock_ik_y = True
forearm_ik_pb.lock_ik_x = True
# Pole IK Ctrl
cns_name = "Child Of"
cns = c_pole_ik_pb.constraints.get(cns_name)
if cns == None:
cns = c_pole_ik_pb.constraints.new("CHILD_OF")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_prefix+spine_rig_names["pelvis"]
# Hand IK Ctrl
cns_name = "Child Of"
cns = c_hand_ik_pb.constraints.get(cns_name)
if cns == None:
cns = c_hand_ik_pb.constraints.new("CHILD_OF")
cns.name = cns_name
cns.target = rig
cns.subtarget = c_master_name
# Add constraints to Mixamo bones
hand_pb = get_pose_bone(hand_name)
# Fingers
for i, fname in enumerate(c_fingers_names):
c_finger_pb = get_pose_bone(fname)
finger_pb = get_pose_bone(fingers_names[i])
add_copy_transf(finger_pb, rig, c_finger_pb.name)
# Shoulder
add_copy_transf(shoulder_pb, rig, c_shoulder_pb.name)
# IK-FK switch property
if not "ik_fk_switch" in c_hand_ik_pb.keys():
create_custom_prop(node=c_hand_ik_pb, prop_name="ik_fk_switch", prop_val=0.0, prop_min=0.0, prop_max=1.0, prop_description="IK-FK switch value")
c_hand_ik_pb["ik_fk_switch"] = 0.0 if self.ik_arms else 1.0
# Arm
arm_pb = get_pose_bone(arm_name)
cns_ik_name = "IK_follow"
cns_ik = arm_pb.constraints.get(cns_ik_name)
if cns_ik == None:
cns_ik = arm_pb.constraints.new("COPY_TRANSFORMS")
cns_ik.name = cns_ik_name
cns_ik.target = rig
cns_ik.subtarget = arm_ik_name
cns_ik.influence = 1.0
cns_fk_name = "FK_Follow"
cns_fk = arm_pb.constraints.get(cns_fk_name)
if cns_fk == None:
cns_fk = arm_pb.constraints.new("COPY_TRANSFORMS")
cns_fk.name = cns_fk_name
cns_fk.target = rig
cns_fk.subtarget = c_arm_fk_name
cns_fk.influence = 0.0
add_driver_to_prop(rig, 'pose.bones["'+arm_name+'"].constraints["'+cns_fk_name+'"].influence', 'pose.bones["'+c_hand_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
# ForeArm
forearm_pb = get_pose_bone(forearm_name)
cns_ik_name = "IK_follow"
cns_ik = forearm_pb.constraints.get(cns_ik_name)
if cns_ik == None:
cns_ik = forearm_pb.constraints.new("COPY_TRANSFORMS")
cns_ik.name = cns_ik_name
cns_ik.target = rig
cns_ik.subtarget = forearm_ik_name
cns_ik.influence = 1.0
cns_fk_name = "FK_Follow"
cns_fk = forearm_pb.constraints.get(cns_fk_name)
if cns_fk == None:
cns_fk = forearm_pb.constraints.new("COPY_TRANSFORMS")
cns_fk.name = cns_fk_name
cns_fk.target = rig
cns_fk.subtarget = c_forearm_fk_name
cns_fk.influence = 0.0
add_driver_to_prop(rig, 'pose.bones["'+forearm_name+'"].constraints["'+cns_fk_name+'"].influence', 'pose.bones["'+c_hand_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
c_arm_fk_pb = get_pose_bone(c_arm_fk_name)
c_forearm_fk_pb = get_pose_bone(c_forearm_fk_name)
lock_pbone_transform(c_forearm_fk_pb, "location", [0,1,2])
# Hand
cns_ik_name = "IK_follow"
cns_ik = hand_pb.constraints.get(cns_ik_name)
if cns_ik == None:
cns_ik = hand_pb.constraints.new("COPY_ROTATION")
cns_ik.name = cns_ik_name
cns_ik.target = rig
cns_ik.subtarget = c_hand_ik_name
cns_ik.influence = 1.0
cns_fk_name = "FK_Follow"
cns_fk = hand_pb.constraints.get(cns_fk_name)
if cns_fk == None:
cns_fk = hand_pb.constraints.new("COPY_ROTATION")
cns_fk.name = cns_fk_name
cns_fk.target = rig
cns_fk.subtarget = c_hand_fk_name
cns_fk.influence = 0.0
add_driver_to_prop(rig, 'pose.bones["'+hand_name+'"].constraints["'+cns_fk_name+'"].influence', 'pose.bones["'+c_hand_ik_name+'"]["ik_fk_switch"]', array_idx=-1, exp="var")
c_hand_fk_pb = get_pose_bone(c_hand_fk_name)
lock_pbone_transform(c_hand_fk_pb, "location", [0,1,2])
# Set custom shapes
c_hand_ik_pb = get_pose_bone(c_hand_ik_name)
set_bone_custom_shape(c_shoulder_pb, "cs_shoulder_"+side.lower())
set_bone_custom_shape(c_arm_fk_pb, "cs_arm_fk")
set_bone_custom_shape(c_forearm_fk_pb, "cs_forearm_fk")
set_bone_custom_shape(c_pole_ik_pb, "cs_sphere_012")
set_bone_custom_shape(c_hand_fk_pb, "cs_hand")
set_bone_custom_shape(c_hand_ik_pb, "cs_hand")
c_fingers_pb = []
for fname in c_fingers_names:
finger_pb = get_pose_bone(fname)
c_fingers_pb.append(finger_pb)
set_bone_custom_shape(finger_pb, "cs_circle_025")
c_pbones_list = [c_shoulder_pb, c_arm_fk_pb, c_forearm_fk_pb, c_pole_ik_pb, c_hand_fk_pb, c_hand_ik_pb] + c_fingers_pb
# set custom shape drivers
ik_controls_names = [c_pole_ik_name, c_hand_ik_name]
arr_ids = [-1]
if blender_version._float >= 300:
arr_ids = [0, 1, 2]
for n in ik_controls_names:
dr_dp = 'pose.bones["'+n+'"].'+get_custom_shape_scale_prop_name()
tar_dp = 'pose.bones["'+c_hand_ik_name+'"]["ik_fk_switch"]'
for arr_id in arr_ids:
add_driver_to_prop(rig, dr_dp, tar_dp, array_idx=arr_id, exp="1-var")
fk_controls_names = [c_arm_fk_name, c_forearm_fk_name, c_hand_fk_name]
for n in fk_controls_names:
dr_dp = 'pose.bones["'+n+'"].'+get_custom_shape_scale_prop_name()
tar_dp = 'pose.bones["'+c_hand_ik_name+'"]["ik_fk_switch"]'
for arr_id in arr_ids:
add_driver_to_prop(rig, dr_dp, tar_dp, array_idx=arr_id, exp="var")
for pb in c_pbones_list:
# set rotation euler
pb.rotation_mode = "XYZ"
# set color group
set_bone_color_group(rig, pb, "body"+_side.lower())
add_master()
add_spine()
add_head()
add_arm("Left")
add_arm("Right")
add_leg("Left")
add_leg("Right")
# tag the armature with a custom prop to specify the control rig is built
rig.data["mr_control_rig"] = True
def _zero_out():
print("\nZeroing out...")
scn = bpy.context.scene
arm = bpy.data.objects.get(bpy.context.active_object.name)
print(" Clear anim")
# Clear animation data
action = None
if arm.animation_data:
if arm.animation_data.action:
action = arm.animation_data.action
if action:
while len(action.fcurves):
action.fcurves.remove(action.fcurves[0])
print(" Clear pose")
# Reset pose
bpy.ops.object.mode_set(mode='POSE')
for b in arm.pose.bones:
b.location = [0,0,0]
b.rotation_euler = [0,0,0]
b.rotation_quaternion = [1,0,0,0]
b.scale = [1,1,1]
print("Zeroed out.")
def _bake_anim(self):
scn = bpy.context.scene
# get min-max frame range
rig = bpy.context.active_object
if rig.animation_data is None:
print("No animation data, exit bake")
return
if rig.animation_data.nla_tracks is None:
print("No NLA tracks found, exit bake")
return
tracks = rig.animation_data.nla_tracks
fs = None
fe = None
# from NLA tracks
for track in tracks:
for strip in track.strips:
if fs is None:
fs = strip.frame_start
if fe is None:
fe = strip.frame_end
if strip.frame_start < fs:
fs = strip.frame_start
if strip.frame_end > fe:
fe = strip.frame_end
if fs is None or fe is None:
print("No NLA tracks found, exit")
return
# get active action frame range
act = rig.animation_data.action
if act is not None:
if act.frame_range[0] < fs:
fs = act.frame_range[0]
if act.frame_range[1] > fe:
fe = act.frame_range[1]
# select only controllers bones
bpy.ops.object.mode_set(mode='POSE')
bpy.ops.pose.select_all(action='DESELECT')
found_ctrl = False
for pbone in rig.pose.bones:
if "mixamo_ctrl" in pbone.bone.keys():
rig.data.bones.active = pbone.bone
pbone.bone.select = True
found_ctrl = True
if not found_ctrl:# backward compatibility, use layer 0 instead
print("Ctrl bones not tagged, search in layer 0 instead...")
c0 = rig.data.collections.get("CTRL")
if c0 is not None:
for b in c0.bones:
pb = rig.pose.bones.get(b.name)
if pb is not None:
rig.data.bones.active = pb.bone
pb.bone.select = True
# ~ for pbone in rig.pose.bones:
# ~ if pbone.bone.layers[0]:
# ~ rig.data.bones.active = pbone.bone
# ~ pbone.bone.select = True
fs, fe = int(fs), int(fe)
scn.frame_set(fs)
bpy.context.view_layer.update()
# bake NLA strips
print("Baking, frame start:", fs, ",frame end", fe)
bpy.ops.nla.bake(frame_start=fs, frame_end=fe, step=1, only_selected=True, visual_keying=False,
clear_constraints=False, clear_parents=False, use_current_action=False,
clean_curves=False, bake_types={'POSE'})
# remove tracks
while len(tracks):
rig.animation_data.nla_tracks.remove(tracks[0])
def redefine_source_rest_pose(src_arm, tar_arm):
print(" Redefining source rest pose...")
scn = bpy.context.scene
src_arm_loc = src_arm.location.copy()
src_arm.location = [0,0,0]
fr_range = src_arm.animation_data.action.frame_range
fr_start = int(fr_range[0])
fr_end = int(fr_range[1])
# duplicate source armature
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(src_arm.name)
bpy.ops.object.mode_set(mode='OBJECT')
duplicate_object()
src_arm_dupli = get_object(bpy.context.active_object.name)
src_arm_dupli["mix_to_del"] = True
"""
# Store bone matrices
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(src_arm.name)
bpy.ops.object.mode_set(mode='POSE')
bones_data = []
for f in range(fr_start, fr_end+1):
print("Frame", f)
scn.frame_set(f)
bpy.context.view_layer.update()
bones_matrices = {}
for pbone in src_arm.pose.bones:
bones_matrices[pbone.name] = pbone.matrix.copy()
#bones_matrices[pbone.name] = src_arm.convert_space(pose_bone=pbone, matrix=pbone.matrix, from_space="POSE", to_space="LOCAL")
bones_data.append((f, bones_matrices))
"""
# Store target bones rest transforms
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(tar_arm.name)
bpy.ops.object.mode_set(mode='EDIT')
rest_bones = {}
for ebone in tar_arm.data.edit_bones:
rest_bones[ebone.name] = ebone.head.copy(), ebone.tail.copy(), vec_roll_to_mat3(ebone.y_axis, ebone.roll)
# Apply source bones rest transforms
print(" Set rest pose...")
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(src_arm.name)
bpy.ops.object.mode_set(mode='EDIT')
for bname in rest_bones:
ebone = get_edit_bone(bname)
if ebone == None:
#print("Warning, bone not found on source armature:", bname)
continue
head, tail, mat3 = rest_bones[bname]
ebone.head, ebone.tail, ebone.roll = src_arm.matrix_world.inverted() @ head, src_arm.matrix_world.inverted() @ tail, mat3_to_vec_roll(src_arm.matrix_world.inverted().to_3x3() @ mat3)
# Add constraints
bpy.ops.object.mode_set(mode='POSE')
for pb in src_arm.pose.bones:
cns = pb.constraints.new("COPY_TRANSFORMS")
cns.name = "temp"
cns.target = src_arm_dupli
cns.subtarget = pb.name
# Restore animation
print("Restore animation...")
bake_anim(frame_start=fr_start, frame_end=fr_end, only_selected=False, bake_bones=True, bake_object=False)
# Restore location
src_arm.location = src_arm_loc
# Delete temp data
# constraints
for pb in src_arm.pose.bones:
if len(pb.constraints):
cns = pb.constraints.get("temp")
if cns:
pb.constraints.remove(cns)
# src_arm_dupli
delete_object(src_arm_dupli)
print(" Source armature rest pose redefined.")
def _import_anim(src_arm, tar_arm, import_only=False):
print("\nImporting animation...")
scn = bpy.context.scene
if src_arm.animation_data == None:
print(" No action found on the source armature")
return
if src_arm.animation_data.action == None:
print(" No action found on the source armature")
return
if len(src_arm.animation_data.action.fcurves) == 0:
print(" No keyframes to import")
return
use_name_prefix = True
# Redefine source armature rest pose if importing only animation, since
# Mixamo Fbx may have different rest pose when the Fbx file contains only animation data
if bpy.context.view_layer.objects.active == None:
set_active_object(tar_arm.name)
if import_only:
redefine_source_rest_pose(src_arm, tar_arm)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(tar_arm.name)
bpy.ops.object.mode_set(mode='POSE')
hand_left_name = get_mix_name("LeftHand", use_name_prefix)
hand_right_name = get_mix_name("RightHand", use_name_prefix)
foot_left_name = get_mix_name("LeftFoot", use_name_prefix)
foot_right_name = get_mix_name("RightFoot", use_name_prefix)
hand_left_pb = get_pose_bone(hand_left_name)
c_hand_ik_left_pb = get_pose_bone(c_prefix + arm_rig_names["hand_ik"]+"_Left")
hand_right_pb = get_pose_bone(hand_right_name)
c_hand_ik_right_pb = get_pose_bone(c_prefix + arm_rig_names["hand_ik"]+"_Right")
foot_left_pb = get_pose_bone(foot_left_name)
c_foot_ik_left_pb = get_pose_bone(c_prefix + leg_rig_names["foot_ik"]+"_Left")
foot_right_pb = get_pose_bone(foot_right_name)
c_foot_ik_right_pb = get_pose_bone(c_prefix + leg_rig_names["foot_ik"]+"_Right")
arm_left_kinematic = "IK" if c_hand_ik_left_pb["ik_fk_switch"] < 0.5 else "FK"
arm_right_kinematic = "IK" if c_hand_ik_right_pb["ik_fk_switch"] < 0.5 else "FK"
leg_left_kinematic = "IK" if c_foot_ik_left_pb["ik_fk_switch"] < 0.5 else "FK"
leg_right_kinematic = "IK" if c_foot_ik_right_pb["ik_fk_switch"] < 0.5 else "FK"
# Set bones mapping for retargetting
bones_map = {}
bones_map[get_mix_name("Hips", use_name_prefix)] = c_prefix+"Hips"
bones_map[get_mix_name("Spine", use_name_prefix)] = c_prefix+"Spine"
bones_map[get_mix_name("Spine1", use_name_prefix)] = c_prefix+"Spine1"
bones_map[get_mix_name("Spine2", use_name_prefix)] = c_prefix+"Spine2"
bones_map[get_mix_name("Neck", use_name_prefix)] = c_prefix+"Neck"
bones_map[get_mix_name("Head", use_name_prefix)] = c_prefix+"Head"
bones_map[get_mix_name("LeftShoulder", use_name_prefix)] = c_prefix+"Shoulder_Left"
bones_map[get_mix_name("RightShoulder", use_name_prefix)] = c_prefix+"Shoulder_Right"
# Arm
if arm_left_kinematic == "FK":
bones_map[get_mix_name("LeftArm", use_name_prefix)] = c_prefix+"Arm_FK_Left"
bones_map[get_mix_name("LeftForeArm", use_name_prefix)] = c_prefix+"ForeArm_FK_Left"
bones_map[get_mix_name("LeftHand", use_name_prefix)] = c_prefix+"Hand_FK_Left"
elif arm_left_kinematic == "IK":
bones_map[c_prefix+"Hand_IK_Left"] = c_prefix+"Hand_IK_Left"
if arm_right_kinematic == "FK":
bones_map[get_mix_name("RightArm", use_name_prefix)] = c_prefix+"Arm_FK_Right"
bones_map[get_mix_name("RightForeArm", use_name_prefix)] = c_prefix+"ForeArm_FK_Right"
bones_map[get_mix_name("RightHand", use_name_prefix)] = c_prefix+"Hand_FK_Right"
elif arm_right_kinematic == "IK":
bones_map[c_prefix+"Hand_IK_Right"] = c_prefix+"Hand_IK_Right"
# Fingers
bones_map[get_mix_name("LeftHandThumb1", use_name_prefix)] = c_prefix+"Thumb1_Left"
bones_map[get_mix_name("LeftHandThumb2", use_name_prefix)] = c_prefix+"Thumb2_Left"
bones_map[get_mix_name("LeftHandThumb3", use_name_prefix)] = c_prefix+"Thumb3_Left"
bones_map[get_mix_name("LeftHandIndex1", use_name_prefix)] = c_prefix+"Index1_Left"
bones_map[get_mix_name("LeftHandIndex2", use_name_prefix)] = c_prefix+"Index2_Left"
bones_map[get_mix_name("LeftHandIndex3", use_name_prefix)] = c_prefix+"Index3_Left"
bones_map[get_mix_name("LeftHandMiddle1", use_name_prefix)] = c_prefix+"Middle1_Left"
bones_map[get_mix_name("LeftHandMiddle2", use_name_prefix)] = c_prefix+"Middle2_Left"
bones_map[get_mix_name("LeftHandMiddle3", use_name_prefix)] = c_prefix+"Middle3_Left"
bones_map[get_mix_name("LeftHandRing1", use_name_prefix)] = c_prefix+"Ring1_Left"
bones_map[get_mix_name("LeftHandRing2", use_name_prefix)] = c_prefix+"Ring2_Left"
bones_map[get_mix_name("LeftHandRing3", use_name_prefix)] = c_prefix+"Ring3_Left"
bones_map[get_mix_name("LeftHandPinky1", use_name_prefix)] = c_prefix+"Pinky1_Left"
bones_map[get_mix_name("LeftHandPinky2", use_name_prefix)] = c_prefix+"Pinky2_Left"
bones_map[get_mix_name("LeftHandPinky3", use_name_prefix)] = c_prefix+"Pinky3_Left"
bones_map[get_mix_name("RightHandThumb1", use_name_prefix)] = c_prefix+"Thumb1_Right"
bones_map[get_mix_name("RightHandThumb2", use_name_prefix)] = c_prefix+"Thumb2_Right"
bones_map[get_mix_name("RightHandThumb3", use_name_prefix)] = c_prefix+"Thumb3_Right"
bones_map[get_mix_name("RightHandIndex1", use_name_prefix)] = c_prefix+"Index1_Right"
bones_map[get_mix_name("RightHandIndex2", use_name_prefix)] = c_prefix+"Index2_Right"
bones_map[get_mix_name("RightHandIndex3", use_name_prefix)] = c_prefix+"Index3_Right"
bones_map[get_mix_name("RightHandMiddle1", use_name_prefix)] = c_prefix+"Middle1_Right"
bones_map[get_mix_name("RightHandMiddle2", use_name_prefix)] = c_prefix+"Middle2_Right"
bones_map[get_mix_name("RightHandMiddle3", use_name_prefix)] = c_prefix+"Middle3_Right"
bones_map[get_mix_name("RightHandRing1", use_name_prefix)] = c_prefix+"Ring1_Right"
bones_map[get_mix_name("RightHandRing2", use_name_prefix)] = c_prefix+"Ring2_Right"
bones_map[get_mix_name("RightHandRing3", use_name_prefix)] = c_prefix+"Ring3_Right"
bones_map[get_mix_name("RightHandPinky1", use_name_prefix)] = c_prefix+"Pinky1_Right"
bones_map[get_mix_name("RightHandPinky2", use_name_prefix)] = c_prefix+"Pinky2_Right"
bones_map[get_mix_name("RightHandPinky3", use_name_prefix)] = c_prefix+"Pinky3_Right"
bones_map["Root"] = "Ctrl_Master"
# bones_map["Root"] = "Root"
if leg_left_kinematic == "FK":
bones_map[get_mix_name("LeftUpLeg", use_name_prefix)] = c_prefix+"UpLeg_FK_Left"
bones_map[get_mix_name("LeftLeg", use_name_prefix)] = c_prefix+"Leg_FK_Left"
bones_map[c_prefix+"Foot_FK_Left"] = c_prefix+"Foot_FK_Left"
bones_map[get_mix_name("LeftToeBase", use_name_prefix)] = c_prefix+"Toe_FK_Left"
elif leg_left_kinematic == "IK":
bones_map[c_prefix+"Foot_IK_Left"] = c_prefix+"Foot_IK_Left"
bones_map[get_mix_name("LeftToeBase", use_name_prefix)] = c_prefix+"Toe_IK_Left"
if leg_right_kinematic == "FK":
bones_map[get_mix_name("RightUpLeg", use_name_prefix)] = c_prefix+"UpLeg_FK_Right"
bones_map[get_mix_name("RightLeg", use_name_prefix)] = c_prefix+"Leg_FK_Right"
bones_map[c_prefix+"Foot_FK_Right"] = c_prefix+"Foot_FK_Right"
bones_map[get_mix_name("RightToeBase", use_name_prefix)] = c_prefix+"Toe_FK_Right"
elif leg_right_kinematic == "IK":
bones_map[c_prefix+"Foot_IK_Right"] = c_prefix+"Foot_IK_Right"
bones_map[get_mix_name("RightToeBase", use_name_prefix)] = c_prefix+"Toe_IK_Right"
action = None
if src_arm.animation_data == None:
print(" No action found on the source armature")
if src_arm.animation_data.action == None:
print(" No action found on the source armature")
# Work on a source armature duplicate
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(src_arm.name)
duplicate_object()
src_arm_copy_name = src_arm.name+"_COPY"
bpy.context.active_object.name = src_arm_copy_name
src_arm = get_object(src_arm_copy_name)
src_arm["mix_to_del"] = True
# Get anim data
action = src_arm.animation_data.action
fr_start = int(action.frame_range[0])
fr_end = int(action.frame_range[1])
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(tar_arm.name)
# Store bones data from target armature
bpy.ops.object.mode_set(mode='EDIT')
ctrl_matrices = {}
ik_bones_data = {}
kinematics = {"HandLeft":["Hand", arm_left_kinematic,"Left"], "HandRight":["Hand", arm_right_kinematic, "Right"], "FootLeft":["Foot", leg_left_kinematic, "Left"], "FootRight":["Foot", leg_right_kinematic, "Right"]}
for b in kinematics:
type, kin_mode, side = kinematics[b]
ctrl_name = c_prefix+type+'_'+kin_mode+'_'+side
ctrl_ebone = get_edit_bone(ctrl_name)
mix_bone_name = get_mix_name(side+type, use_name_prefix)
ctrl_matrices[ctrl_name] = ctrl_ebone.matrix.copy(), mix_bone_name
# store corrected ik bones
if kin_mode == "IK":
ik_bones = {}
ik_chain = []
if type == "Foot":
ik_chain = ["UpLeg_IK_"+side, "Leg_IK_"+side]
elif type == "Hand":
ik_chain = ["Arm_IK_"+side, "ForeArm_IK_"+side]
ik1 = get_edit_bone(ik_chain[0])
ik2 = get_edit_bone(ik_chain[1])
ik_bones["ik1"] = ik1.name, ik1.head.copy(), ik1.tail.copy(), ik1.roll
ik_bones["ik2"] = ik2.name, ik2.head.copy(), ik2.tail.copy(), ik2.roll
ik_bones_data[b] = type, side, ik_bones
# Init source armature rotation and scale
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(src_arm.name)
scale_fac = src_arm.scale[0]
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
for fc in action.fcurves:
dp = fc.data_path
if dp.startswith('pose.bones') and dp.endswith(".location"):
for k in fc.keyframe_points:
k.co[1] *= scale_fac
bpy.ops.object.mode_set(mode='EDIT')
# Add helper source bones
# add feet bones helpers
for name in ctrl_matrices:
foot_ebone = create_edit_bone(name)
foot_ebone.head, foot_ebone.tail = [0,0,0], [0,0,0.1]
foot_ebone.matrix = ctrl_matrices[name][0]
foot_ebone.parent = get_edit_bone(ctrl_matrices[name][1])
# add IK bones helpers
for b in ik_bones_data:
type, side, ik_bones = ik_bones_data[b]
for bone_type in ik_bones:
bname, bhead, btail, broll = ik_bones[bone_type]
ebone = create_edit_bone(bname)
ebone.head, ebone.tail, ebone.roll = bhead, btail, broll
# set parents
for b in ik_bones_data:
type, side, ik_bones = ik_bones_data[b]
ik2_name = ik_bones["ik2"][0]
ik2 = get_edit_bone(ik2_name)
# set constraints
bpy.ops.object.mode_set(mode='POSE')
bake_ik_data = {"src_arm":src_arm}
for b in ik_bones_data:
type, side, ik_bones = ik_bones_data[b]
b1_name = ik_bones["ik1"][0]
b2_name = ik_bones["ik2"][0]
b1_pb = get_pose_bone(b1_name)
b2_pb = get_pose_bone(b2_name)
chain = []
if type == "Foot":
chain = [get_mix_name(side+"UpLeg", use_name_prefix), get_mix_name(side+"Leg", use_name_prefix)]
bake_ik_data["Leg"+side] = chain
elif type == "Hand":
chain = [get_mix_name(side+"Arm", use_name_prefix), get_mix_name(side+"ForeArm", use_name_prefix)]
bake_ik_data["Arm"+side] = chain
cns = b1_pb.constraints.new("COPY_TRANSFORMS")
cns.name = "Copy Transforms"
cns.target = src_arm
cns.subtarget = chain[0]
cns = b2_pb.constraints.new("COPY_TRANSFORMS")
cns.name = "Copy Transforms"
cns.target = src_arm
cns.subtarget = chain[1]
# Retarget
retarget_method = 2
# Method 1: Direct matrix retargetting (slower)
if retarget_method == 1:
for fr in range(fr_start, fr_end+1):
print(" frame", fr)
scn.frame_set(fr)
bpy.context.view_layer.update()
for src_name in bones_map:
tar_name = bones_map[src_name]
src_bone = src_arm.pose.bones.get(src_name)
tar_bone = tar_arm.pose.bones.get(tar_name)
if "Foot" in src_name:
tar_mix_bone = tar_arm.pose.bones.get(src_name)
#print(" tar_mix_bone", tar_mix_bone.name)
offset_mat = tar_bone.matrix @ tar_mix_bone.matrix.inverted()
tar_bone.matrix = offset_mat @ src_bone.matrix.copy()
else:
tar_bone.matrix = src_bone.matrix.copy()
if not "Hips" in src_name:
tar_bone.location = [0,0,0]
bpy.context.view_layer.update()# Not ideal, slow performances
# Method 2: Constrained retargetting (faster)
elif retarget_method == 2:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(tar_arm.name)
bpy.ops.object.mode_set(mode='POSE')
bpy.ops.pose.select_all(action='DESELECT')
# add constraints
for src_name in bones_map:
tar_name = bones_map[src_name]
src_bone = src_arm.pose.bones.get(src_name)
tar_bone = tar_arm.pose.bones.get(tar_name)
if src_bone == None:
#print("SKIP BONE", src_name)
continue
if tar_bone == None:
#print("SKIP BONE", tar_name)
continue
if "Root" in src_name:
print("CONSTRAINT", src_bone, tar_bone)
cns_name = "Copy Rotation_retarget"
cns = tar_bone.constraints.new('COPY_ROTATION')
cns.name = cns_name
cns.target = src_arm
cns.subtarget = src_name
if "Hips" in src_name:
cns_name = "Copy Location_retarget"
cns = tar_bone.constraints.new('COPY_LOCATION')
cns.name = cns_name
cns.target = src_arm
cns.subtarget = src_name
cns.owner_space = cns.target_space = "LOCAL"
# Foot IK, Hand IK
if (leg_left_kinematic == "IK" and "Foot_IK_Left" in src_name) or (leg_right_kinematic == "IK" and "Foot_IK_Right" in src_name) or (arm_left_kinematic == "IK" and "Hand_IK_Left" in src_name) or (arm_right_kinematic == "IK" and "Hand_IK_Right" in src_name):
#print(" set IK remap constraints", src_name)
cns_name = "Copy Location_retarget"
cns = tar_bone.constraints.new('COPY_LOCATION')
cns.name = cns_name
cns.target = src_arm
cns.subtarget = src_name
cns.target_space = cns.owner_space = "POSE"
# select IK poles
_side = "_Left" if "Left" in src_name else "_Right"
ik_pole_name = ""
if "Hand" in src_name:
ik_pole_name = c_prefix+arm_rig_names["pole_ik"]+_side
elif "Foot" in src_name:
ik_pole_name = c_prefix+leg_rig_names["pole_ik"]+_side
ik_pole_ctrl = get_pose_bone(ik_pole_name)
tar_arm.data.bones.active = ik_pole_ctrl.bone
ik_pole_ctrl.bone.select = True
# select
tar_arm.data.bones.active = tar_bone.bone
tar_bone.bone.select = True
bpy.context.view_layer.update()
# bake
bake_anim(frame_start=fr_start, frame_end=fr_end, only_selected=True, bake_bones=True, bake_object=False, ik_data=bake_ik_data)
bpy.ops.object.mode_set(mode='OBJECT')
set_active_object(src_arm.name)
set_active_object(tar_arm.name)
print("Animation imported.")
def remove_retarget_cns(armature):
#print("Removing constraints...")
for pb in armature.pose.bones:
if len(pb.constraints):
for cns in pb.constraints:
if cns.name.endswith("_retarget") or cns.name == "temp":
pb.constraints.remove(cns)
def remove_temp_objects():
for obj in bpy.data.objects:
if "mix_to_del" in obj.keys():
delete_object(obj)
def update_mixamo_tab():
try:
bpy.utils.unregister_class(MR_PT_MenuMain)
bpy.utils.unregister_class(MR_PT_MenuRig)
bpy.utils.unregister_class(MR_PT_MenuAnim)
bpy.utils.unregister_class(MR_PT_MenuExport)
bpy.utils.unregister_class(MR_PT_MenuUpdate)
except:
pass
MixamoRigPanel.bl_category = bpy.context.preferences.addons[__package__].preferences.mixamo_tab_name
bpy.utils.register_class(MR_PT_MenuMain)
bpy.utils.register_class(MR_PT_MenuRig)
bpy.utils.register_class(MR_PT_MenuAnim)
bpy.utils.register_class(MR_PT_MenuExport)
bpy.utils.register_class(MR_PT_MenuUpdate)
########### UI PANELS ###################
class MixamoRigPanel:
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Mixamo"
class MR_PT_MenuMain(Panel, MixamoRigPanel):
bl_label = "Mixamo Control Rig"
def draw(self, context):
scn = context.scene
layt = self.layout
layt.use_property_split = True
layt.use_property_decorate = False
#col = layt.column(align=True)
#col.scale_y = 1.3
#col.prop_search(scn, "mix_source_armature", scn, "objects", text="Skeleton")
arm_name = "None"
if context.active_object != None:
if context.active_object.type == "ARMATURE":
arm_name = context.active_object.name
layt.label(text="Character: "+arm_name)
class MR_PT_MenuRig(Panel, MixamoRigPanel):
bl_label = "Control Rig"
bl_parent_id = "MR_PT_MenuMain"
def draw(self, context):
layt = self.layout
layt.use_property_split = True
layt.use_property_decorate = False
obj = context.active_object
scn = context.scene
"""
has_rigged = False
if obj:
if obj.type == "ARMATURE":
if len(obj.data.keys()):
if "mr_data" in obj.data.keys():
has_rigged = True
"""
col = layt.column(align=True)
col.scale_y = 1.3
col.operator(MR_OT_make_rig.bl_idname, text="Create Control Rig")
col.operator(MR_OT_zero_out.bl_idname, text="Zero Out Rig")
col = layt.column(align=True)
col.separator()
if bpy.context.mode != 'EDIT_MESH':
col.operator(MR_OT_edit_custom_shape.bl_idname, text="Edit Control Shape")
else:
col.operator(MR_OT_apply_shape.bl_idname, text="Apply Control Shape")
class MR_PT_MenuAnim(Panel, MixamoRigPanel):
bl_label = "Animation"
bl_parent_id = "MR_PT_MenuMain"
def draw(self, context):
layt = self.layout
layt.use_property_split = True
layt.use_property_decorate = False# No animation.
scn = context.scene
layt.use_property_split = True
layt.use_property_decorate = False
col = layt.column(align=True)
col.scale_y = 1
#col.prop_search(scn, "mix_target_armature", scn, "objects", text="Control Rig")
col.label(text="Source Skeleton:")
col.prop_search(scn, "mix_source_armature", scn, "objects", text="")
col.separator()
col = layt.column(align=True)
col.scale_y = 1.3
col.operator(MR_OT_import_anim.bl_idname, text="Apply Animation to Control Rig")
col = layt.column(align=True)
col.scale_y = 1.3
col.operator(MR_OT_bake_anim.bl_idname, text="Bake Animation")
class MR_PT_MenuUpdate(Panel, MixamoRigPanel):
bl_label = "Update"
bl_parent_id = "MR_PT_MenuMain"
def draw(self, context):
layt = self.layout
layt.operator(MR_OT_update.bl_idname, text="Update Control Rig")
class MR_PT_MenuExport(Panel, MixamoRigPanel):
bl_label = "Export"
bl_parent_id = "MR_PT_MenuMain"
def draw(self, context):
layt = self.layout
layt.operator('export_scene.gltf', text="GLTF Export...")#MR_OT_exportGLTF.bl_idname
########### REGISTER ##################
classes = (
MR_PT_MenuMain,
MR_PT_MenuRig,
MR_PT_MenuAnim,
MR_PT_MenuExport,
MR_PT_MenuUpdate,
MR_OT_make_rig,
MR_OT_zero_out,
MR_OT_bake_anim,
MR_OT_import_anim,
MR_OT_edit_custom_shape,
MR_OT_apply_shape,
MR_OT_exportGLTF,
MR_OT_update
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
update_mixamo_tab()
bpy.types.Scene.mix_source_armature = bpy.props.PointerProperty(type=bpy.types.Object)
bpy.types.Scene.mix_target_armature = bpy.props.PointerProperty(type=bpy.types.Object)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.mix_source_armature
del bpy.types.Scene.mix_target_armature
if __name__ == "__main__":
register()