IK / FK Matching
* Copy the script below to the clipboard and paste it into Maya/Script Editor to be able to execute.
import maya.cmds as cmds
DefaultSwitchKeyword='Switch, switch, SWITCH'
DefaultIKWord='IK'
DefaultFKWord='FK'
DefaultLimitValue = 0.0
#ik/fk matcher script
#for our 2 functions, the expectation is that uset has selected an IK/FK switch control
#how can we figure out by selecting the switch control to know the joints
#we can validate the switch control selection by checking it for our switch custom attribute
def joint_selection(switch_option):
#get the current switch option menu selection
switch = cmds.optionMenu(switch_option, q = True, value =True)
#get the parent of the switch control
parent_grp = cmds.listRelatives(switch, parent=True)
#get the children of the parent
children_grp = cmds.listRelatives(parent_grp, ad=True, fullPath=True)
found=False#bool attribute for while loop
count = 0#count attribute for while loop
joint_constraint = ''
#if children exist:
if children_grp:
#while loop to check every item in the children list
while count<len(children_grp) and found==False:
#if the type of the item is parent constraint
if cmds.nodeType(children_grp[count])=='parentConstraint':
#stop looping
found=True
#assign the item to a variable
joint_constraint=children_grp[count]
count+=1#continue loop
#if parent constraint exist:
if joint_constraint != '':
#get the short name of the joint constraint
short_name = joint_constraint.split('|')[-1]
#get the connected joint from the constraint
connection=cmds.listConnections(short_name, type = 'joint', d=False, s=True)
#remove repetitions
connection = list(set(connection))
#if connection is not empty:
if connection:
#get the wrist joint
wrist_jnt = connection[0]
#assign the parent of the wrist joint to the variable (elbow joint)
parent_jnt = cmds.listRelatives(wrist_jnt, parent = True)
#if the parent of the wrist joint exists:
if parent_jnt:
#assign it to the variable elbow_jnt
elbow_jnt = parent_jnt[0]
#assign the parent of the elbow joint to the variable parent jnt2
parent_jnt2 = cmds.listRelatives(elbow_jnt, parent = True)
#if the parent of the elbow joint exists:
if parent_jnt2:
#assign it to the variable shoulder joint
shoulder_jnt = parent_jnt2[0]
joints = [shoulder_jnt, elbow_jnt, wrist_jnt] #create a variable for joints
return joints
else:
cmds.warning('Could not find proper joint hierarchy based on the constrained joint.')
return None
else:
cmds.warning('Could not find proper joint hierarchy based on the constrained joint.')
return None
else:
cmds.warning('Could not find any joint constrained to the control.')#warning message
#confirm dialog to let the user choose a joint
joint_confirm = cmds.confirmDialog(title = 'Joint confirmation', message = 'Could not find any joint constrained to the control. If you would like to continue, please select the the wrist joint that the control affects.', button = ['Yes(with the joint selected)', 'No'])
#if the user wants to continue
if joint_confirm == 'Yes(with the joint selected)':
#get the selection
sel = cmds.ls(selection = True, type = 'joint')
#if user selected smt:
if sel:
#get the wrist joint
wrist_jnt = sel[0]
#assign the parent of the wrist joint to the variable (elbow joint)
parent_jnt = cmds.listRelatives(wrist_jnt, parent = True)
#if the parent of the wrist joint exists:
if parent_jnt:
#assign it to the variable elbow_jnt
elbow_jnt = parent_jnt[0]
#assign the parent of the elbow joint to the variable parent jnt2
parent_jnt2 = cmds.listRelatives(elbow_jnt, parent = True)
#if the parent of the elbow joint exists:
if parent_jnt2:
#assign it to the variable shoulder joint
shoulder_jnt = parent_jnt2[0]
joints = [shoulder_jnt, elbow_jnt, wrist_jnt] #create a variable for joints
return joints
else:
cmds.warning('Could not find proper joint hierarchy based on the selected joint.')
return None
else:
cmds.warning('Could not find proper joint hierarchy based on the selected joint.')
return None
else:
cmds.warning('Nothing selected.')
return None
else:
return None
else:
cmds.warning('Could not find any parent constraint. Please check whether the control has a constraint.')
return None
else:
cmds.warning('Could not find any parent or other objects under the same hierarchy.')
return None
def get_fk_controls(fk_joints):
fk_controls = []#preset fk control list
#for loop to go through every fk joint
for jnt in fk_joints:
#get the children of the joint
children = cmds.listRelatives(jnt, fullPath = True, c=True)
count = 0#count for while loop
found = False#bool for while loop
joint_constraint = ''
#if children exist:
if children:
while count<len(children) and found==False:#when checking every child of the joint
if cmds.nodeType(children[count])=='parentConstraint':#if the type of the child is parent constraint
found=True#stop while loop
joint_constraint=children[count]#assign it to the joint constraint var
count+=1#continue counting
#if joint constraint exists:
if joint_constraint != '':
#get the short name
short_name = joint_constraint.split('|')[-1]
#get the connection
connection=cmds.listConnections(short_name, d=False, s=True)
connection = list(set(connection))#remove repetitions
#for loop to go through every connection
for each in connection:
#get the shape of the connection
child = cmds.listRelatives(each, fullPath = True, shapes = True)
#if shape exists:
if child:
if cmds.nodeType(child[0]) == 'nurbsCurve':#if the type of the shape is nurbcurve
fk_controls.insert(fk_joints.index(jnt), each)#add it to the corresponding fk control list
else:
cmds.warning('Could not find any constraint under {]'.format(jnt))
else:
cmds.warning('Could not find any children under {} '.format(jnt))
if len(fk_controls)==len(fk_joints):#if find every fk control
return fk_controls
else:
return None
def get_ik_controls(ik_joints):
ik_controls = ['']#preset ik control list
#get all the ik handles
handles = cmds.ls(type = 'ikHandle')
ik_handle = ''
#for loop to go through every handle in the scene:
for each in handles:
#get the connection of the ik handle type joint
connection=cmds.listConnections(each, d=False, s=True, type = 'joint')
#check if the connection joint is one of the ik joint
for jnt in ik_joints:
if jnt == connection[0]:
ik_handle = each
#if there is an ik handle that is connected to the ik joint
if ik_handle != '':
#get the connetion of the ik handle
handle_connection=cmds.listConnections(ik_handle, d=False, s=True)
#remove repetitions
handle_connection = list(set(handle_connection))
#for loop to go through every connection
for each in handle_connection:
#if the type of the connection is a constraint
if 'Constraint' in cmds.nodeType(each):
#get the short name of the connection
short_name = each.split('|')[-1]
#get the connection of the handle connection
connection=cmds.listConnections(short_name, d=False, s=True)
connection = list(set(connection))#remove repetitions
#check every connection
for con in connection:
#get the shape of the connection
child = cmds.listRelatives(con, fullPath = True, shapes = True)
#if shape node exists:
if child:
if cmds.nodeType(child[0]) == 'nurbsCurve':#if the connection type is a nurbcurve
ik_controls.insert(1, con) #add to the ik control list as elbow control
children = cmds.listRelatives(ik_joints[2], fullPath = True, c=True)#get the children of the wrist joint
count = 0#count for while loop
found = False#bool for while loop
joint_constraint = ''
if children:#if children exist
while count<len(children) and found==False:#when checking every child of the wrist joint
if cmds.nodeType(children[count])=='parentConstraint':#if the type of the child is parent constraint
found=True#stop while loop
joint_constraint=children[count]#get the constraint
count+=1
if joint_constraint != '':#if parent constraint exist
short_name = joint_constraint.split('|')[-1]#get the short name
connection=cmds.listConnections(short_name, d=False, s=True)#get the connection of the short name
connection = list(set(connection))#remove repetitions
for each in connection:#for loop to go through every connection
child = cmds.listRelatives(each, fullPath = True, shapes = True)#get the shape of the connection
if child:#if shape node exists:
if cmds.nodeType(child[0]) == 'nurbsCurve':#if the type of the shape is nurbcurve
ik_controls.insert(2, each)#add the connection as wrist control
else:
cmds.warning('Could not find any constraint under the wrist joint.')
return None
else:
cmds.warning('Could not find any children under the wrist joint.')
return None
else:
cmds.warning('Could not find any ik handle connected to the joint of the selected switch control.')
return None
if len(ik_controls)==len(ik_joints):#if find every ik control:
return ik_controls
else:
cmds.warning('Faild to evaluate the ik controls for the ik joints.')
return None
def ik_mode(switch_option, attr, ik_val):
#get the joints based on the switch selection
joints = joint_selection(switch_option)
#if joints exist:
if joints != None:
#get the forearm length between elbow and wrist
forearm_length = cmds.getAttr('{}.translateX'.format(joints[2]))
#get the upperarm length between shoulder and elbow
upperarm_length = cmds.getAttr('{}.translateX'.format(joints[1]))
#get the arm length
arm_length = (forearm_length+upperarm_length)
#get all the joints in the scene
joint_all = cmds.ls(type = 'joint')
fk_joints = []#predefine fk joint list
ik_joints = []#predefine ik joint list
#for loop to go trhough every joint
for each in joint_all:
#for loop to go through every joint of the control
for jnt in joints:
#if the joint contains fk keyword and the control joint name:
if DefaultFKWord in each and jnt in each:
#add the joint to the fk joint list
fk_joints.insert(joints.index(jnt), each)
#else if the name of the joint contains ik keyword and the control joint name:
elif DefaultIKWord in each and jnt in each:
#add the joint to the ik joint list
ik_joints.insert(joints.index(jnt), each)
if len(ik_joints)==len(joints) and len(fk_joints)==len(joints): #if find every fk joint and ik joint
#get fk controls
fk_controls = get_fk_controls(fk_joints)
#get ik controls
ik_controls = get_ik_controls(ik_joints)
if fk_controls != None and ik_controls !=None:#if get the controls successfully
#match translation and rotation of the wrist ik to the wrist fk
cmds.matchTransform(ik_controls[2], fk_controls[2], pos=True, rot = True)
#match translation and rotation of the elbow ik to the elbow fk
cmds.matchTransform(ik_controls[1], fk_controls[1], pos = True, rot = True)
#move the elbow control back
if arm_length > 0:
cmds.move(0,0, -arm_length, ik_controls[1], relative = True)
elif arm_length <0:
cmds.move(0,0, arm_length, ik_controls[1], relative = True)
#get the ik value in the ik text field
ikValue = cmds.textField(ik_val, q=True, text = True)
#set the switch control to ik value
switchAttr= cmds.optionMenu(attr,q=True, value = True)
switch = cmds.optionMenu(switch_option, q = True, value =True)
cmds.setAttr('{0}.{1}'.format(switch, switchAttr), float(ikValue))
else:
cmds.warning('Failed to find ik joints or fk joints. Please check the naming.')
def fk_mode(switch_option, attr, fk_val):
#get the joints based on the switch selection
joints = joint_selection(switch_option)
#if joints exist:
if joints != None:
#get all the joints in the scene:
joint_all = cmds.ls(type = 'joint')
fk_joints = []#predefine fk joint list
ik_joints = []#predefine ik joint list
#for loop to go through every joint:
for each in joint_all:
#for loop to go through every joint of the control
for jnt in joints:
#if the name of the joint contains fk keyword and the control joint name:
if DefaultFKWord in each and jnt in each:
#add the joint to the fk joint list
fk_joints.insert(joints.index(jnt), each)
#else if the name of the joint contains ik keyword and the control joint name:
elif DefaultIKWord in each and jnt in each:
#add the joint to the ik joint list
ik_joints.insert(joints.index(jnt), each)
if len(ik_joints)==len(joints) and len(fk_joints)==len(joints): #if find every fk joint and ik joint
#get the fk controls based on the fk joints
fk_controls = get_fk_controls(fk_joints)
roAxis = ['X','Y','Z']#set the axis
if fk_controls !=None:#if get fk_controls successfully
#for loop to run 3 times
for jnt in range(0,3):
#for loop to go through every axis
for ro in roAxis:
#get the value of the rotation of the ik joint
value = cmds.getAttr('{0}.rotate{1}'.format(ik_joints[jnt], ro))
#set the value of the ik joint to the fk joint
cmds.setAttr('{0}.rotate{1}'.format(fk_controls[jnt], ro), value)
#get the fk value in the fk val text field
fkValue = cmds.textField(fk_val, q=True, text = True)
#set the switch attribute value to the fk value
switchAttr= cmds.optionMenu(attr,q=True, value = True)
switch= cmds.optionMenu(switch_option,q=True, value = True)
cmds.setAttr('{0}.{1}'.format(switch, switchAttr), float(fkValue))
else:
cmds.warning('Failed to find ik joints or fk joints. Please check the naming.')
def change_attribute(switch, attr,ik_val, fk_val):
#get the current attribtue selected in the attribute option menu
attribute = cmds.optionMenu(attr, q=True, value = True)
#get the switch option
selection = cmds.optionMenu(switch, q = True, value =True)
#get the limit of the selected attribute
if cmds.attributeQuery(attribute, node=selection, minExists=True, maxExists=True):
limit = cmds.attributeQuery(attribute, node=selection, range=True)
else:
limit = [DefaultLimitValue, DefaultLimitValue]
#put the minimum to ik value
cmds.textField(ik_val, text=limit[0],edit= True)
#maximum to fk value
cmds.textField(fk_val, text=limit[1],edit = True)
def change_switch(option, attr, ik_val, fk_val):
#get the current switch option menu selection
selection = cmds.optionMenu(option, q = True, value =True)
#if selection exists:
if selection:
#get the user defined attributes in the selected switch controls
switch_attr= cmds.listAttr(selection,ud=True)
#get the attribute option menu items
menuItems = cmds.optionMenu(attr, q = True, ill = True)
#if there are things exist in the attribute option menu
if menuItems != None and menuItems != []:
cmds.deleteUI(menuItems) #delete those items
#if user defined attribute exists:
if switch_attr:
# for loop to go through each attribute
for at in switch_attr:
cmds.menuItem(label=at, parent=attr)#add them into the attribute option menu
#get the limit range of the first user defined attribute
if cmds.attributeQuery(switch_attr[0], node=selection, minExists=True, maxExists=True):
limit = cmds.attributeQuery(switch_attr[0], node=selection, range=True)
else:
limit = [DefaultLimitValue, DefaultLimitValue]
#if ik_val and fk_val are not default values
if ik_val != DefaultLimitValue and fk_val != DefaultLimitValue:
#change the text field ik value to the minimum limit
cmds.textField(ik_val, text=limit[0],edit= True)
#change the text field fk value to the maximum limit
cmds.textField(fk_val, text=limit[1],edit = True)
else:
cmds.warning('Could not find any user defined attributes in this control.')
else:
cmds.warning('Could not find any switch control containing the keyword. Please re-enter the keyword')
def search_switch(txt, option, attr, ik_val, fk_val):
#get the items in the switch option menu and assign them in a var
menuItems = cmds.optionMenu(option, q = True, ill = True)
#if there are things exist in the switch option menu:
if menuItems != None and menuItems != []:
cmds.deleteUI(menuItems)#delete the items in the switch option menu
#get the text from the keyword text field
text = cmds.textField(txt, q=1, text=1)
#split the keyword text
key= text.split(',')
#define the var for the keyword list
keyword=[]
#for loop to go through the text field keyword after splitting
for each in key:
#append each item in the new keyword list var
keyword.append(each.strip())
#get all the transforms in the scene
sceneAll = cmds.ls(type = 'transform')
#set a var for the switch list
get_switch=[]
#for loop to go through every transforms in the scene
for item in sceneAll:
count=0 #set the count var for checking every keyword in the while loop
found = False #bool for stopping the while loop
#when checking every keyword and haven't found the item:
while count< len(keyword) and found==False:
if keyword[count] in item: # if the item contains the keyword
#get the shape of this item
sp = cmds.listRelatives(item, shapes=True)
#check if the type of the shape is a curve
if sp:
if cmds.nodeType(item+'|'+sp[0]) == 'nurbsCurve':
get_switch.append(item)#append the item to the switch list
#set the found bool to true, stop the while loop
found=True
count+=1#if cannot found anything with that keyword, check the next
#for loop to go through each item in the switch list
for each in get_switch:
#add each item to the switch option menu
cmds.menuItem( label=each, parent=option )
#if the attribute is not empty: to automatically update the attribute option menu and value layout
if attr != '':
#change switch
change_switch(option, attr, ik_val, fk_val)
#if get switch is not an empty list:
if get_switch:
return get_switch[0] #return the first item of the switch list
else:
return None #else return none
def create_ui():
script_ui = 'IK/FK Matcher' #The name of the UI
ui_width = 400 #The width of the UI
attr = '' #pre define the var for the attribute
ik_val=DefaultLimitValue #pre define ik val
fk_val= DefaultLimitValue# pre define fk val
#if the window has already existed, then delete the one.
if cmds.window(script_ui, exists = True):
cmds.deleteUI(script_ui)
#create the ui window
wd = cmds.window(script_ui, width = ui_width, title = 'IK/FK Switch')
#The main layout (top of the hierarchy)
main_layout = cmds.rowColumnLayout()
#define description layout
description_layout = cmds.rowColumnLayout(numberOfColumns = 1)
cmds.separator(visible = False)
cmds.separator(visible = False)
#description texts
cmds.text(label='1. Use keywords to find IK/FK Switch Control (use , to separate each keyword)', align = 'left')
cmds.text(label = '2. Select the switch control and the switch attribute in the drop-down menu', align = 'left')
cmds.text(label = '3. Adjust the attribute values.', align = 'left')
cmds.text(label = '4. Use IK Mode Matcher button or FK Mode Matcher button to match the pose', align = 'left')
#separators
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
#parent it to the main layout
cmds.setParent('..')
#search layout
searchWidth = [(1, ui_width*0.25), (2, ui_width*0.55), (3, ui_width*0.2)] #The width setting for the columns
#define the layout
search_layout = cmds.rowColumnLayout(numberOfColumns = 3, width = ui_width, columnWidth = searchWidth)
#text to describe the text field
cmds.text(label=' Switch Keywords: ', width = searchWidth[0][1])
#text field for entering the keywords. by default it takes the var DefaultSwitchKeyword
txt=cmds.textField(text=DefaultSwitchKeyword,width = searchWidth[1][1])
#button to search by using the keywords.by pressing it goes to the function search_switch
search_button= cmds.button(label='Search', width = searchWidth[2][1], command = lambda *args: search_switch(txt, switch_option, attr, ik_val, fk_val))
cmds.separator(visible = False)#separator
cmds.separator(visible = False)#separator
cmds.separator(visible = False)#separator
cmds.separator(visible = False)#separator
#parent the layout
cmds.setParent('..')
#switch layout
switchWidth=[(1, ui_width*0.25), (2, ui_width*0.75)]#switch layout width setting
switch_layout = cmds.rowColumnLayout(numberOfColumns = 2, width = ui_width , columnWidth = switchWidth)#define the layout
#describing text
cmds.text(label=' IK/FK Switch:',width = switchWidth[0][1])
#option menu for selecting the result of the search button. when changing the selection, it goes to the function change_switch
switch_option= cmds.optionMenu( width = switchWidth[1][1], changeCommand=lambda *args:change_switch(switch_option, attr, ik_val, fk_val))
#find the switch using the default value and assign it into a var
default_switch=search_switch(txt, switch_option, attr, ik_val, fk_val)
#separators
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
#descibing text for the attribute selection
cmds.text(label=' Switch Attribute: ',width = switchWidth[0][1])
#option menu for selecting the attribute.
attr=cmds.optionMenu(width = switchWidth[1][1], changeCommand = lambda *args:change_attribute(switch_option, attr,ik_val, fk_val))
#if it finds a switch control by using the default value
if default_switch != None:
#find user defined attribute in the switch control
default_attr = cmds.listAttr(default_switch,ud=True)
#add the user defined attribute into the attribute option menu
for at in default_attr:
cmds.menuItem(label=at, parent=attr )
#separators
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
#parent the layout
cmds.setParent('..')
#value layout
value_width=[(1, ui_width*0.25), (2, ui_width*0.25), (3, ui_width*0.25), (4, ui_width*0.25)]
value_layout = cmds.rowColumnLayout(numberOfColumns = 4, width = ui_width)#define the layout
#set the default ik value and fk value
#if default switch exists by using the default keywords and default attribute has values:
if default_switch != None and default_attr:
#assign the attribute limit value(min+max)to the limit if the min and the max exist
if cmds.attributeQuery(default_attr[0], node=default_switch, minExists=True, maxExists=True):
defaultLimit = cmds.attributeQuery( default_attr[0], node=default_switch, range=True )
else:#otherwise it still takes default value
defaultLimit = [DefaultLimitValue, DefaultLimitValue]
else:
#otherwise the limit takes the default value
defaultLimit = [DefaultLimitValue, DefaultLimitValue]
#describing text for ik value
cmds.text(label=' IK Value: ',width = value_width[0][1])
#text field for ik value
ik_val=cmds.textField(text=defaultLimit[0] ,width = value_width[1][1])
#describing text for fk value
cmds.text(label=' FK Value: ',width = value_width[2][1])
#text field for fk value
fk_val=cmds.textField(text=defaultLimit[1] ,width = value_width[3][1])
#describing word for ik joints keyword(for finding the joints)
cmds.text(label=' IK Joints Keyword: ',width = value_width[0][1])
#text field for the ik keyword
ik_key=cmds.textField(text=DefaultIKWord ,width = value_width[1][1])
#describing word for fk joints keyword
cmds.text(label=' FK Joints Keyword: ',width = value_width[2][1])
#text field for the fk keyword
fk_key=cmds.textField(text=DefaultFKWord ,width = value_width[3][1])
cmds.separator(visible = False) #separators
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
cmds.separator(visible = False)
#parent the layout
cmds.setParent('..')
#button layout
button_width=[(1, ui_width*0.5), (2, ui_width*0.5)] #the width setting for the layout
#define the layout
button_layout= cmds.rowColumnLayout(numberOfColumns = 2, width = ui_width, columnWidth = button_width)
#button for ik mode
ik_button=cmds.button(label='IK Mode Matcher',command = lambda *args: ik_mode(switch_option, attr, ik_val) )
#button for fk mode
fk_button=cmds.button(label='FK Mode Matcher',command = lambda *args: fk_mode(switch_option, attr, fk_val))
#parent the layout
cmds.setParent('..')
#display the window
cmds.showWindow(wd)
#call the window
create_ui()
© Jiuyang Wang │ 778-926-0288 │ einjw2999@gmail.com