Files
Blender-Mixamo/mixamo_rig_functions.py
T

1117 lines
37 KiB
Python

import bpy, os
from mathutils import *
from math import *
from bpy.app.handlers import persistent
from operator import itemgetter
from .utils import *
from .define import *
fk_leg = [c_prefix+leg_rig_names["thigh_fk"], c_prefix+leg_rig_names["calf_fk"], c_prefix+leg_rig_names["foot_fk"], c_prefix+leg_rig_names["toes_fk"]]
ik_leg = [leg_rig_names["thigh_ik"], leg_rig_names["calf_ik"], c_prefix+leg_rig_names["foot_ik"], c_prefix+leg_rig_names["pole_ik"], c_prefix+leg_rig_names["toes_ik"], c_prefix+leg_rig_names["foot_01"], c_prefix+leg_rig_names["foot_roll_cursor"], leg_rig_names["foot_snap"]]
fk_arm = [c_prefix+arm_rig_names["arm_fk"], c_prefix+arm_rig_names["forearm_fk"], c_prefix+arm_rig_names["hand_fk"]]
ik_arm = [arm_rig_names["arm_ik"], arm_rig_names["forearm_ik"], c_prefix+arm_rig_names["hand_ik"], c_prefix+arm_rig_names["pole_ik"]]
################## OPERATOR CLASSES ###################
class MR_OT_arm_bake_fk_to_ik(bpy.types.Operator):
"""Snaps and bake an FK to an IK arm over a specified frame range"""
bl_idname = "pose.mr_bake_arm_fk_to_ik"
bl_label = "Snap an FK to IK arm over a specified frame range"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
frame_start : bpy.props.IntProperty(name="Frame start", default=0)
frame_end : bpy.props.IntProperty(name="Frame end", default=10)
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def draw(self, context):
layout = self.layout
layout.prop(self, 'frame_start', text='Frame Start')
layout.prop(self, 'frame_end', text='Frame End')
def invoke(self, context, event):
action = context.active_object.animation_data.action
self.frame_start, self.frame_end = action.frame_range[0], action.frame_range[1]
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
# save current autokey state
auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto
# set auto key to True
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
try:
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
bake_fk_to_ik_arm(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
# restore autokey state
bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state
return {'FINISHED'}
class MR_OT_arm_fk_to_ik(bpy.types.Operator):
"""Snaps an FK arm to an IK arm"""
bl_idname = "pose.mr_arm_fk_to_ik_"
bl_label = "Snap FK arm to IK"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
fk_to_ik_arm(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
class MR_OT_arm_bake_ik_to_fk(bpy.types.Operator):
"""Snaps and bake an IK to an FK arm over a specified frame range"""
bl_idname = "pose.mr_bake_arm_ik_to_fk"
bl_label = "Snap an IK to FK arm over a specified frame range"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
frame_start : bpy.props.IntProperty(name="Frame start", default=0)
frame_end : bpy.props.IntProperty(name="Frame end", default=10)
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def draw(self, context):
layout = self.layout
layout.prop(self, 'frame_start', text='Frame Start')
layout.prop(self, 'frame_end', text='Frame End')
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
# save current autokey state
auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto
# set auto key to True
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
try:
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
bake_ik_to_fk_arm(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
# restore autokey state
bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state
return {'FINISHED'}
class MR_OT_arm_ik_to_fk(bpy.types.Operator):
"""Snaps an IK arm to an FK arm"""
bl_idname = "pose.mr_arm_ik_to_fk_"
bl_label = "Snap IK arm to FK"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
ik_to_fk_arm(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
class MR_OT_switch_snap_anim(bpy.types.Operator):
"""Switch and snap IK-FK over multiple frames"""
bl_idname = "pose.mr_switch_snap_anim"
bl_label = "Switch and Snap IK FK anim"
bl_options = {'UNDO'}
rig = None
side : bpy.props.StringProperty(name="bone side", default="")
_side = ""
prefix: bpy.props.StringProperty(name="", default="")
type : bpy.props.StringProperty(name="type", default="")
frame_start : bpy.props.IntProperty(name="Frame start", default=0)
frame_end : bpy.props.IntProperty(name="Frame end", default=10)
has_action = False
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def draw(self, context):
layout = self.layout
if self.has_action:
layout.prop(self, 'frame_start', text='Frame Start')
layout.prop(self, 'frame_end', text='Frame End')
else:
layout.label(text="This rig is not animated!")
def invoke(self, context, event):
try:
action = context.active_object.animation_data.action
if action:
self.has_action = True
except:
pass
if self.has_action:
self.frame_start, self.frame_end = action.frame_range[0], action.frame_range[1]
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
def execute(self, context):
if self.has_action == False:
return {'FINISHED'}
try:
scn = context.scene
# save current autokey state
auto_key_state = scn.tool_settings.use_keyframe_insert_auto
# set auto key to True
scn.tool_settings.use_keyframe_insert_auto = True
# save current frame
cur_frame = scn.frame_current
self.rig = context.active_object
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
self._side = '_'+self.side
self.prefix = get_mixamo_prefix()
if is_selected(fk_leg, bname) or is_selected(ik_leg, bname):
self.type = "LEG"
elif is_selected(fk_arm, bname) or is_selected(ik_arm, bname):
self.type = "ARM"
if self.type == "ARM":
c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+self._side)#self.prefix+self.side+'Hand')
if c_hand_ik['ik_fk_switch'] < 0.5:
bake_fk_to_ik_arm(self)
else:
bake_ik_to_fk_arm(self)
elif self.type == "LEG":
c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+self._side)#get_pose_bone(self.prefix+self.side+'Foot')
if c_foot_ik['ik_fk_switch'] < 0.5:
bake_fk_to_ik_leg(self)
else:
print("Bake IK to FK leg")
bake_ik_to_fk_leg(self)
finally:
# restore autokey state
scn.tool_settings.use_keyframe_insert_auto = auto_key_state
# restore frame
scn.frame_set(cur_frame)
return {'FINISHED'}
class MR_OT_switch_snap(bpy.types.Operator):
"""Switch and snap IK-FK for the current frame"""
bl_idname = "pose.mr_switch_snap"
bl_label = "Switch and Snap IK FK"
bl_options = {'UNDO'}
rig = None
side : bpy.props.StringProperty(name="bone side", default="")
_side = ""
prefix: bpy.props.StringProperty(name="", default="")
type : bpy.props.StringProperty(name="type", default="")
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
self.rig = context.active_object
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
self._side = '_'+self.side
self.prefix = get_mixamo_prefix()
if is_selected(fk_leg, bname) or is_selected(ik_leg, bname):
self.type = "LEG"
elif is_selected(fk_arm, bname) or is_selected(ik_arm, bname):
self.type = "ARM"
if self.type == "ARM":
#base_hand = get_pose_bone(self.prefix+self.side+'Hand')
c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+self._side)#self.prefix+self.side+'Hand')
if c_hand_ik['ik_fk_switch'] < 0.5:
fk_to_ik_arm(self)
else:
ik_to_fk_arm(self)
elif self.type == "LEG":
#base_foot = get_pose_bone(self.prefix+self.side+'Foot')
c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+self._side)#get_pose_bone(self.prefix+self.side+'Foot')
if c_foot_ik['ik_fk_switch'] < 0.5:
fk_to_ik_leg(self)
else:
ik_to_fk_leg(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
class MR_OT_leg_bake_fk_to_ik(bpy.types.Operator):
"""Snaps and bake an FK leg to an IK leg over a specified frame range"""
bl_idname = "pose.mr_bake_leg_fk_to_ik"
bl_label = "Snap an FK to IK leg over a specified frame range"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
_side = ""
prefix = ""
frame_start : bpy.props.IntProperty(name="Frame start", default=0)
frame_end : bpy.props.IntProperty(name="Frame end", default=10)
rig = None
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def draw(self, context):
layout = self.layout
layout.prop(self, 'frame_start', text='Frame Start')
layout.prop(self, 'frame_end', text='Frame End')
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
# save current autokey state
auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto
# set auto key to True
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
try:
self.rig = context.active_object
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
self._side = '_'+self.side
self.prefix = get_mixamo_prefix()
bake_fk_to_ik_leg(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
# restore autokey state
bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state
return {'FINISHED'}
class MR_OT_leg_fk_to_ik(bpy.types.Operator):
"""Snaps an FK leg to an IK leg"""
bl_idname = "pose.mr_leg_fk_to_ik_"
bl_label = "Snap FK leg to IK"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
rig = None
_side = ""
prefix = ""
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
self.rig = context.active_object
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
self._side = '_'+self.side
self.prefix = get_mixamo_prefix()
fk_to_ik_leg(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
class MR_OT_leg_bake_ik_to_fk(bpy.types.Operator):
"""Snaps and bake an IK leg to an FK leg over a specified frame range"""
bl_idname = "pose.mr_bake_leg_ik_to_fk"
bl_label = "Snap an IK to FK leg over a specified frame range"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
frame_start : bpy.props.IntProperty(name="Frame start", default=0)
frame_end : bpy.props.IntProperty(name="Frame end", default=10)
rig = None
_side = ""
prefix = ""
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def draw(self, context):
layout = self.layout
layout.prop(self, 'frame_start', text='Frame Start')
layout.prop(self, 'frame_end', text='Frame End')
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
# save current autokey state
auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto
# set auto key to True
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
try:
self.rig = context.active_object
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
self._side = '_'+self.side
self.prefix = get_mixamo_prefix()
bake_ik_to_fk_leg(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
# restore autokey state
bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state
return {'FINISHED'}
class MR_OT_leg_ik_to_fk(bpy.types.Operator):
"""Snaps an IK leg to an FK leg"""
bl_idname = "pose.mr_leg_ik_to_fk_"
bl_label = "Snap IK leg to FK"
bl_options = {'UNDO'}
side : bpy.props.StringProperty(name="bone side")
rig = None
_side = ""
prefix = ""
@classmethod
def poll(cls, context):
return (context.active_object != None and context.mode == 'POSE')
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
self.rig = context.active_object
bname = get_selected_pbone_name()
self.side = get_bone_side(bname)
self._side = '_'+self.side
self.prefix = get_mixamo_prefix()
ik_to_fk_leg(self)
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
################## FUNCTIONS ##################
def set_pose_rotation(pose_bone, mat):
q = mat.to_quaternion()
if pose_bone.rotation_mode == 'QUATERNION':
pose_bone.rotation_quaternion = q
elif pose_bone.rotation_mode == 'AXIS_ANGLE':
pose_bone.rotation_axis_angle[0] = q.angle
pose_bone.rotation_axis_angle[1] = q.axis[0]
pose_bone.rotation_axis_angle[2] = q.axis[1]
pose_bone.rotation_axis_angle[3] = q.axis[2]
else:
pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode)
def snap_pos(pose_bone, target_bone):
# Snap a bone to another bone. Supports child of constraints and parent.
# if the pose_bone has direct parent
if pose_bone.parent:
# apply double time because of dependecy lag
pose_bone.matrix = target_bone.matrix
update_transform()
# second apply
pose_bone.matrix = target_bone.matrix
else:
# is there a child of constraint attached?
child_of_cns = None
if len(pose_bone.constraints) > 0:
all_child_of_cns = [i for i in pose_bone.constraints if i.type == "CHILD_OF" and i.influence == 1.0 and i.mute == False and i.target]
if len(all_child_of_cns) > 0:
child_of_cns = all_child_of_cns[0]# in case of multiple child of constraints enabled, use only the first for now
if child_of_cns != None:
if child_of_cns.subtarget != "" and get_pose_bone(child_of_cns.subtarget):
# apply double time because of dependecy lag
pose_bone.matrix = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ target_bone.matrix
update_transform()
pose_bone.matrix = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ target_bone.matrix
else:
pose_bone.matrix = target_bone.matrix
else:
pose_bone.matrix = target_bone.matrix
def snap_pos_matrix(pose_bone, target_bone_matrix):
# Snap a bone to another bone. Supports child of constraints and parent.
# if the pose_bone has direct parent
if pose_bone.parent:
pose_bone.matrix = target_bone_matrix.copy()
update_transform()
else:
# is there a child of constraint attached?
child_of_cns = None
if len(pose_bone.constraints) > 0:
all_child_of_cns = [i for i in pose_bone.constraints if i.type == "CHILD_OF" and i.influence == 1.0 and i.mute == False and i.target]
if len(all_child_of_cns) > 0:
child_of_cns = all_child_of_cns[0]# in case of multiple child of constraints enabled, use only the first for now
if child_of_cns != None:
if child_of_cns.subtarget != "" and get_pose_bone(child_of_cns.subtarget):
pose_bone.matrix = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ target_bone_matrix
update_transform()
else:
pose_bone.matrix = target_bone_matrix.copy()
else:
pose_bone.matrix = target_bone_matrix.copy()
def snap_rot(pose_bone, target_bone):
method = 1
if method == 1:
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
set_pose_rotation(pose_bone, mat)
#bpy.ops.object.mode_set(mode='OBJECT')
#bpy.ops.object.mode_set(mode='POSE')
bpy.context.view_layer.update()
elif method == 2:
loc, scale = pose_bone.location.copy(), pose_bone.scale.copy()
pose_bone.matrix = target_bone.matrix
pose_bone.location, pose_bone.scale = loc, scale
bpy.context.view_layer.update()
def bake_fk_to_ik_arm(self):
for f in range(self.frame_start, self.frame_end +1):
bpy.context.scene.frame_set(f)
print("baking frame", f)
fk_to_ik_arm(self)
def fk_to_ik_arm(self):
rig = self.rig
side = self.side
_side = self._side
prefix = self.prefix
arm_fk = rig.pose.bones[fk_arm[0] + _side]
forearm_fk = rig.pose.bones[fk_arm[1] + _side]
hand_fk = rig.pose.bones[fk_arm[2] + _side]
arm_ik = rig.pose.bones[ik_arm[0] + _side]
forearm_ik = rig.pose.bones[ik_arm[1] + _side]
hand_ik = rig.pose.bones[ik_arm[2] + _side]
pole = rig.pose.bones[ik_arm[3] + _side]
#Snap rot
snap_rot(arm_fk, arm_ik)
snap_rot(forearm_fk, forearm_ik)
snap_rot(hand_fk, hand_ik)
#Snap scale
hand_fk.scale =hand_ik.scale
#rot debug
forearm_fk.rotation_euler[0]=0
forearm_fk.rotation_euler[1]=0
#switch
#base_hand = get_pose_bone(prefix+side+'Hand')
c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+_side)
c_hand_ik['ik_fk_switch'] = 1.0
#udpate view
bpy.context.view_layer.update()
#insert key if autokey enable
if bpy.context.scene.tool_settings.use_keyframe_insert_auto:
#fk chain
c_hand_ik.keyframe_insert(data_path='["ik_fk_switch"]')
hand_fk.keyframe_insert(data_path="scale")
hand_fk.keyframe_insert(data_path="rotation_euler")
arm_fk.keyframe_insert(data_path="rotation_euler")
forearm_fk.keyframe_insert(data_path="rotation_euler")
#ik chain
hand_ik.keyframe_insert(data_path="location")
hand_ik.keyframe_insert(data_path="rotation_euler")
hand_ik.keyframe_insert(data_path="scale")
pole.keyframe_insert(data_path="location")
# change FK to IK hand selection, if selected
if hand_ik.bone.select:
hand_fk.bone.select = True
hand_ik.bone.select = False
def bake_ik_to_fk_arm(self):
for f in range(self.frame_start, self.frame_end +1):
bpy.context.scene.frame_set(f)
print("baking frame", f)
ik_to_fk_arm(self)
def ik_to_fk_arm(self):
rig = self.rig
side = self.side
_side = self._side
prefix = self.prefix
arm_fk = rig.pose.bones[fk_arm[0] + _side]
forearm_fk = rig.pose.bones[fk_arm[1] + _side]
hand_fk = rig.pose.bones[fk_arm[2] + _side]
arm_ik = rig.pose.bones[ik_arm[0] + _side]
forearm_ik = rig.pose.bones[ik_arm[1] + _side]
hand_ik = rig.pose.bones[ik_arm[2] + _side]
pole_ik = rig.pose.bones[ik_arm[3] + _side]
# Snap
# constraint support
constraint = None
bparent_name = ""
parent_type = ""
valid_constraint = True
# Snap Hand
if len(hand_ik.constraints) > 0:
for c in hand_ik.constraints:
if not c.mute and c.influence > 0.5 and c.type == 'CHILD_OF':
if c.target:
#if bone
if c.target.type == 'ARMATURE':
bparent_name = c.subtarget
parent_type = "bone"
constraint = c
#if object
else:
bparent_name = c.target.name
parent_type = "object"
constraint = c
if constraint != None:
if parent_type == "bone":
if bparent_name == "":
valid_constraint = False
if constraint and valid_constraint:
if parent_type == "bone":
bone_parent = get_pose_bone(bparent_name)
hand_ik.matrix = bone_parent.matrix_channel.inverted() @ hand_fk.matrix
if parent_type == "object":
bone_parent = bpy.data.objects[bparent_name]
obj_par = bpy.data.objects[bparent_name]
hand_ik.matrix = constraint.inverse_matrix.inverted() @ obj_par.matrix_world.inverted() @ hand_fk.matrix
else:
hand_ik.matrix = hand_fk.matrix
# Snap Pole
_axis = forearm_fk.x_axis if side == "Left" else -forearm_fk.x_axis
pole_pos = get_ik_pole_pos(arm_fk, forearm_fk, method=2, axis=_axis)
pole_mat = Matrix.Translation(pole_pos)
snap_pos_matrix(pole_ik, pole_mat)
# Switch
c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+_side)
#base_hand = get_pose_bone(prefix+side+'Hand')
c_hand_ik['ik_fk_switch'] = 0.0
# update
update_transform()
#insert key if autokey enable
if bpy.context.scene.tool_settings.use_keyframe_insert_auto:
#ik chain
c_hand_ik.keyframe_insert(data_path='["ik_fk_switch"]')
hand_ik.keyframe_insert(data_path="location")
hand_ik.keyframe_insert(data_path="rotation_euler")
hand_ik.keyframe_insert(data_path="scale")
pole_ik.keyframe_insert(data_path="location")
#fk chain
hand_fk.keyframe_insert(data_path="location")
hand_fk.keyframe_insert(data_path="rotation_euler")
hand_fk.keyframe_insert(data_path="scale")
arm_fk.keyframe_insert(data_path="rotation_euler")
forearm_fk.keyframe_insert(data_path="rotation_euler")
# change FK to IK hand selection, if selected
if hand_fk.bone.select:
hand_fk.bone.select = False
hand_ik.bone.select = True
def bake_fk_to_ik_leg(self):
for f in range(self.frame_start, self.frame_end +1):
bpy.context.scene.frame_set(f)
print("baking frame", f)
fk_to_ik_leg(self)
def fk_to_ik_leg(self):
rig = self.rig
side = self.side
_side = self._side
prefix = self.prefix
thigh_fk = rig.pose.bones[fk_leg[0] + _side]
leg_fk = rig.pose.bones[fk_leg[1] + _side]
foot_fk = rig.pose.bones[fk_leg[2] + _side]
toes_fk = rig.pose.bones[fk_leg[3] + _side]
thigh_ik = rig.pose.bones[ik_leg[0] + _side]
leg_ik = rig.pose.bones[ik_leg[1] + _side]
foot_ik = rig.pose.bones[ik_leg[2] + _side]
pole_ik = rig.pose.bones[ik_leg[3] + _side]
toes_ik = rig.pose.bones[ik_leg[4] + _side]
foot_01_ik = rig.pose.bones[ik_leg[5] + _side]
foot_roll_ik = rig.pose.bones[ik_leg[6] + _side]
foot_snap_ik = rig.pose.bones[ik_leg[7] + _side]
# Thigh snap
snap_rot(thigh_fk, thigh_ik)
#thigh_fk.matrix = thigh_ik.matrix.copy()
# Leg snap
snap_rot(leg_fk, leg_ik)
# Foot snap
snap_rot(foot_fk, foot_snap_ik)
foot_fk.scale =foot_ik.scale
# Toes snap
snap_rot(toes_fk, toes_ik)
toes_fk.scale = toes_ik.scale
# rotation fix
leg_fk.rotation_euler[1] = 0.0
leg_fk.rotation_euler[2] = 0.0
# switch prop value
c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+_side)
#base_foot = get_pose_bone(prefix+side+'Foot')
c_foot_ik['ik_fk_switch'] = 1.0
# udpate hack
bpy.context.view_layer.update()
#if bpy.context.scene.frame_current == 2:
# print(br)
#insert key if autokey enable
if bpy.context.scene.tool_settings.use_keyframe_insert_auto:
#fk chain
c_foot_ik.keyframe_insert(data_path='["ik_fk_switch"]')
thigh_fk.keyframe_insert(data_path="rotation_euler")
leg_fk.keyframe_insert(data_path="rotation_euler")
foot_fk.keyframe_insert(data_path="rotation_euler")
foot_fk.keyframe_insert(data_path="scale")
toes_fk.keyframe_insert(data_path="rotation_euler")
toes_fk.keyframe_insert(data_path="scale")
#ik chain
foot_ik.keyframe_insert(data_path="location")
foot_ik.keyframe_insert(data_path="rotation_euler")
foot_ik.keyframe_insert(data_path="scale")
foot_01_ik.keyframe_insert(data_path="rotation_euler")
foot_roll_ik.keyframe_insert(data_path="location")
toes_ik.keyframe_insert(data_path="rotation_euler")
toes_ik.keyframe_insert(data_path="scale")
pole_ik.keyframe_insert(data_path="location")
# change IK to FK foot selection, if selected
if foot_ik.bone.select:
foot_fk.bone.select = True
foot_ik.bone.select = False
def bake_ik_to_fk_leg(self):
for f in range(self.frame_start, self.frame_end +1):
bpy.context.scene.frame_set(f)
print("baking frame", f)
ik_to_fk_leg(self)
def ik_to_fk_leg(self):
rig = self.rig
side = self.side
_side = self._side
prefix = self.prefix
thigh_fk = rig.pose.bones[fk_leg[0] + _side]
leg_fk = rig.pose.bones[fk_leg[1] + _side]
foot_fk = rig.pose.bones[fk_leg[2] + _side]
toes_fk = rig.pose.bones[fk_leg[3] + _side]
thigh_ik = rig.pose.bones[ik_leg[0] + _side]
calf_ik = rig.pose.bones[ik_leg[1] + _side]
foot_ik = rig.pose.bones[ik_leg[2] + _side]
pole_ik = rig.pose.bones[ik_leg[3] + _side]
toes_ik = rig.pose.bones[ik_leg[4] + _side]
foot_01_ik = rig.pose.bones[ik_leg[5] + _side]
foot_roll_ik = rig.pose.bones[ik_leg[6] + _side]
# reset IK foot_01 and foot_roll
foot_01_ik.rotation_euler = [0,0,0]
foot_roll_ik.location[0] = 0.0
foot_roll_ik.location[2] = 0.0
# Snap toes
toes_ik.rotation_euler = toes_fk.rotation_euler.copy()
toes_ik.scale = toes_fk.scale.copy()
# Child Of constraint or parent cases
constraint = None
bparent_name = ""
parent_type = ""
valid_constraint = True
if len(foot_ik.constraints) > 0:
for c in foot_ik.constraints:
if not c.mute and c.influence > 0.5 and c.type == 'CHILD_OF':
if c.target:
#if bone
if c.target.type == 'ARMATURE':
bparent_name = c.subtarget
parent_type = "bone"
constraint = c
#if object
else:
bparent_name = c.target.name
parent_type = "object"
constraint = c
if constraint != None:
if parent_type == "bone":
if bparent_name == "":
valid_constraint = False
# Snap Foot
if constraint and valid_constraint:
if parent_type == "bone":
bone_parent = rig.pose.bones[bparent_name]
foot_ik.matrix = bone_parent.matrix_channel.inverted() @ foot_fk.matrix
if parent_type == "object":
ob = bpy.data.objects[bparent_name]
foot_ik.matrix = constraint.inverse_matrix.inverted() @ ob.matrix_world.inverted() @ foot_fk.matrix
else:
foot_ik.matrix = foot_fk.matrix
# update
bpy.context.view_layer.update()
# Snap Pole
pole_pos = get_ik_pole_pos(thigh_fk, leg_fk, method=2, axis=leg_fk.z_axis)
pole_mat = Matrix.Translation(pole_pos)
snap_pos_matrix(pole_ik, pole_mat)
update_transform()
# switch
c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+_side)
#base_foot = get_pose_bone(prefix+side+'Foot')
c_foot_ik['ik_fk_switch'] = 0.0
update_transform()
#insert key if autokey enable
if bpy.context.scene.tool_settings.use_keyframe_insert_auto:
#ik chain
c_foot_ik.keyframe_insert(data_path='["ik_fk_switch"]')
foot_01_ik.keyframe_insert(data_path="rotation_euler")
foot_roll_ik.keyframe_insert(data_path="location")
foot_ik.keyframe_insert(data_path="location")
foot_ik.keyframe_insert(data_path="rotation_euler")
foot_ik.keyframe_insert(data_path="scale")
toes_ik.keyframe_insert(data_path="rotation_euler")
toes_ik.keyframe_insert(data_path="scale")
pole_ik.keyframe_insert(data_path="location")
#fk chain
thigh_fk.keyframe_insert(data_path="rotation_euler")
leg_fk.keyframe_insert(data_path="rotation_euler")
foot_fk.keyframe_insert(data_path="rotation_euler")
foot_fk.keyframe_insert(data_path="scale")
toes_fk.keyframe_insert(data_path="rotation_euler")
toes_fk.keyframe_insert(data_path="scale")
# change IK to FK foot selection, if selected
if foot_fk.bone.select:
foot_fk.bone.select = False
foot_ik.bone.select = True
def get_active_child_of_cns(bone):
constraint = None
bparent_name = ""
parent_type = ""
valid_constraint = True
if len(bone.constraints) > 0:
for c in bone.constraints:
if not c.mute and c.influence > 0.5 and c.type == 'CHILD_OF':
if c.target:
if c.target.type == 'ARMATURE':# bone
bparent_name = c.subtarget
parent_type = "bone"
constraint = c
else:# object
bparent_name = c.target.name
parent_type = "object"
constraint = c
if constraint:
if parent_type == "bone":
if bparent_name == "":
valid_constraint = False
return constraint, bparent_name, parent_type, valid_constraint
def is_selected(names, selected_bone_name, startswith=False):
side = ""
if get_bone_side(selected_bone_name) != None:
side = get_bone_side(selected_bone_name)
_side = "_"+side
if startswith == False:
if type(names) == list:
for name in names:
if not "." in name[-2:]:
if name + _side == selected_bone_name:
return True
else:
if name[-2:] == ".x":
if name[:-2] + _side == selected_bone_name:
return True
elif names == selected_bone_name:
return True
else:#startswith
if type(names) == list:
for name in names:
if selected_bone_name.startswith(name):
return True
else:
return selected_bone_name.startswith(names)
return False
def is_selected_prop(pbone, prop_name):
if pbone.bone.keys():
if prop_name in pbone.bone.keys():
return True
################## User Interface ##################
class MR_PT_rig_ui(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Tool"
bl_label = "Mixamo Rig Settings"
bl_idname = "MR_PT_rig_ui"
@classmethod
def poll(self, context):
if context.mode != 'POSE':
return False
return True
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
rig = context.active_object
if rig == None:
return
if rig.type != "ARMATURE":
return
# check if a Mixamo ctrl rig is selected
if len(rig.data.keys()):
if not 'mr_control_rig' in rig.data.keys():
return
else:
return
pose_bones = rig.pose.bones
try:
active_bone = context.selected_pose_bones[0]#context.active_pose_bone
selected_bone_name = active_bone.name
except:
return
side = get_bone_side(selected_bone_name)
prefix = get_mixamo_prefix()
# Leg
if (is_selected(fk_leg, selected_bone_name) or is_selected(ik_leg, selected_bone_name)):
# IK-FK Switch
col = layout.column(align=True)
#foot_base = get_pose_bone(prefix+side.title()+'Foot')
c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+'_'+side.title())
col.prop(c_foot_ik, '["ik_fk_switch"]', text="IK-FK Switch", slider=True)
col.operator(MR_OT_switch_snap.bl_idname, text="Snap Frame IK/FK")
col.operator(MR_OT_switch_snap_anim.bl_idname, text="Snap Anim IK-FK")
# Arm
if is_selected(fk_arm, selected_bone_name) or is_selected(ik_arm, selected_bone_name):
# IK-FK Switch
col = layout.column(align=True)
#hand_base = get_pose_bone(prefix+side.title()+'Hand')
c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+'_'+side.title())
col.prop(c_hand_ik, '["ik_fk_switch"]', text="IK-FK Switch", slider=True)
col.operator(MR_OT_switch_snap.bl_idname, text="Snap Frame IK-FK")
col.operator(MR_OT_switch_snap_anim.bl_idname, text="Snap Anim IK-FK")
################## REGISTER ##################
classes = (
MR_OT_arm_bake_fk_to_ik,
MR_OT_arm_fk_to_ik,
MR_OT_arm_bake_ik_to_fk,
MR_OT_arm_ik_to_fk,
MR_OT_switch_snap,
MR_OT_leg_fk_to_ik,
MR_OT_leg_bake_fk_to_ik,
MR_OT_leg_ik_to_fk,
MR_OT_leg_bake_ik_to_fk,
MR_PT_rig_ui,
MR_OT_switch_snap_anim)
def update_mixamo_tab():
try:
bpy.utils.unregister_class(MR_PT_rig_ui)
except:
pass
MR_PT_rig_ui.bl_category = bpy.context.preferences.addons[__package__].preferences.mixamo_tab_name
bpy.utils.register_class(MR_PT_rig_ui)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
update_mixamo_tab()
bpy.types.Scene.mix_show_ik_fk_advanced = bpy.props.BoolProperty(name="Show IK-FK operators", description="Show IK-FK manual operators", default=False)
def unregister():
from bpy.utils import unregister_class
for cls in classes:
unregister_class(cls)
del bpy.types.Scene.mix_show_ik_fk_advanced