非常讨厌在Adv Picker里面切换ikfk!为了避免折寿,我花了一些时间来创建这个脚本,选择对应的ikfk控制器点击即可切换

 

 

from maya import cmds
from maya.api import OpenMaya as om2
#import logging
'''
Create in ADV 6.040, select the corresponding IKFK controller, and simply run the script to switch
by kangddan
'''
#logger = logging.getLogger(__name__)  

def setAttrs(nodes, attrs, value):

    for n in nodes:
        for ids, i in enumerate(attrs):
            try:
                if isinstance(value[ids], (list, tuple)):
                    cmds.setAttr('{}.{}'.format(n, i), *value[ids])
                else:
                    cmds.setAttr('{}.{}'.format(n, i), value[ids])
            except:
                pass
    
class Path:
    @classmethod
    def path(cls, fullPathName):
        return fullPathName.split("|")[-1]
        
    @classmethod
    def name(cls, fullPathName):
        return cls.path(fullPathName).split(':')[-1]
        
    @classmethod    
    def namespace(cls, fullPathName):
        return cls.path(fullPathName).rpartition(':')[0]
        
class MOpenMaya:
    
    @staticmethod
    def toMObject(nodeName):
        sl = om2.MSelectionList(); sl.add(nodeName)
        return sl.getDependNode(0)
    
    @classmethod
    def getDagNode(cls, mobj) :
        if not mobj.hasFn(om2.MFn.kDagNode): return
        return om2.MDagPath.getAPathTo(mobj)
    
    @classmethod
    def getGlobatMatrix(cls, dagNode):
        if not isinstance(dagNode, om2.MDagPath): return
        return dagNode.inclusiveMatrix()
    
    
    @classmethod
    def getGlobalPos(cls, dagNode):
        if not dagNode: return    
        tm = om2.MTransformationMatrix(cls.getGlobatMatrix(dagNode))
        return  tm.translation(om2.MSpace.kTransform)
        
    @classmethod
    def ls(cls):
        sl = om2.MGlobal.getActiveSelectionList()
        return [cls.getDagNode(sl.getDependNode(o)) for o in range(sl.length())]


class IkFk:
    ARM_IK_CTRLS       = {'ik1':'IKArm', 'ik2':'PoleArm'}                                   # 0
    ARM_FK_CTRLS       = {'fk1':'FKShoulder', 'fk2':'FKElbow', 'fk3':'FKWrist'}             # 1  
    ARM_FKTOIK_LOCS    = {'loc1':'IKX2Shoulder', 'loc2':'IKX2Elbow', 'loc3':'IKX2Wrist'}    # 2 
    ARM_IKTOFK_LOCS    = {'loc1':'AlignIKToWrist'}                                          # 3            
    ARM_IKFK           = {'switch':'FKIKArm'}                                                
    
    LEG_IK_CTRLS       = {'ik1':'IKLeg', 'ik2':'PoleLeg', 'ik3':'RollHeel', 'ik4':'RollToesEnd', 'ik5':'RollToes', 'ik6':'IKToes'}
    LEG_FK_CTRLS       = {'fk1':'FKHip', 'fk2':'FKKnee', 'fk3':'FKAnkle', 'fk4':'FKToes'}
    LEG_FKTOIK_LOCS    = {'loc1':'IKX2Hip', 'loc2':'IKX2Knee', 'loc3':'IKX2Ankle', 'loc4':'IKX2Toes', 'loc5':'IKX2ToesEnd'}
    LEG_IKTOFK_LOCS    = {'loc1':'AlignIKToAnkle', 'loc2':'AlignIKToToes'}
    leg_IKFK           = {'switch':'FKIKLeg'}
    
    # --------------------------------------------------------------
    ARM_BASE_JNTS      = {'base1':'Shoulder', 'base2':'Elbow', 'base3':'Wrist'} 
    LEG_BASE_JNTS      = {'base1':'Hip', 'base2':'Knee', 'base3':'Ankle'}

    
    # --------------------------------------------------------------
        
    
    def getLRDict(self, _dict, namespace=''):
        
        ldictList    = [{k:'{}{}_L'.format(namespace, v) for k, v in d.items()} for d in _dict[:-2]]
        rdictList    = [{k:'{}{}_R'.format(namespace, v) for k, v in d.items()} for d in _dict[:-2]]
        basedictList = [{k:'{}{}'.format(namespace, v) for k, v in d.items()} for d in _dict[-2:]]

        return ldictList, rdictList, basedictList
        
    def getLongName(self, sName, _dict):
        for k, v in _dict.items():
            longNames = cmds.ls(v, long=True)
            for ln in longNames:
                if sName == ln[0:len(sName)]:
                    _dict[k] = ln
                # if ln[0].startswith(sName):
                #     _dict[k] = ln
                    break
                    
    # -------------------------------------------------------------- 
             
    def newDict(self, fullPathName):
        dictList = [self.ARM_IK_CTRLS, self.ARM_FK_CTRLS, self.ARM_FKTOIK_LOCS, self.ARM_IKTOFK_LOCS, self.ARM_IKFK,
                    self.LEG_IK_CTRLS, self.LEG_FK_CTRLS, self.LEG_FKTOIK_LOCS, self.LEG_IKTOFK_LOCS, self.leg_IKFK,
                    self.ARM_BASE_JNTS, self.LEG_BASE_JNTS]
                    
        namespace = Path.namespace(fullPathName)
        if namespace:
            self.isRef = True
            return self.getLRDict(dictList, namespace+':')
        else:
            self.isRef = False
            sName = '|' + cmds.ls('*{}'.format(fullPathName), long=True)[0].split('|')[1]
            ldictList, rdictList, basedictList = self.getLRDict(dictList)
            
            for dL, dR in zip(ldictList, rdictList):    
                self.getLongName(sName, dL)
                self.getLongName(sName, dR)
                
            for bS in basedictList: self.getLongName(sName, bS) # 

            return ldictList, rdictList, basedictList

    def setPolePose(self, jntList, pole):
        jntPos = [MOpenMaya.getGlobalPos(MOpenMaya.getDagNode(MOpenMaya.toMObject(j))) for j in jntList]
        off = ((jntPos[1] - jntPos[0]).length() + (jntPos[2] - jntPos[1]).length())
        
        midPos = ((jntPos[1] - jntPos[0]).normal() + ((jntPos[2] - jntPos[1]).normal() * -1)) * 0.5
        polePos = (midPos.normal() * off) + jntPos[1]
        cmds.xform(pole, t=polePos, ws=True)
        
    def setIkStretch(self, jntList, locList):
        jntPos = [MOpenMaya.getGlobalPos(MOpenMaya.getDagNode(MOpenMaya.toMObject(j))) for j in jntList]
        locPos = [MOpenMaya.getGlobalPos(MOpenMaya.getDagNode(MOpenMaya.toMObject(l))) for l in locList]
        
        upper = (jntPos[1] - jntPos[0]).length()
        mid = (jntPos[2] - jntPos[1]).length()
        
        locUpper = (locPos[1] - locPos[0]).length()
        locMid = (locPos[2] - locPos[1]).length()
        
        upperError = abs(upper / locUpper)
        midErrorr = abs(mid / locMid)
        return upperError, midErrorr
        
    def getNewDict(self, fullPathName):
        ldictList, rdictList, basedictList = self.newDict(fullPathName)
        ALD = ldictList[0:5]; LLD = ldictList[5:]
        ARD = rdictList[0:5]; LRD = rdictList[5:]
        
        AB  = basedictList[0]; LB =  basedictList[1] # base joint ref

        return ALD, ARD, LLD, LRD, AB, LB
    
    def _updateDict(self, _dict):
        return [value for d in [_dict[0], _dict[1], _dict[-1]] for value in d.values()]
            
    def __init__(self, autoK=True):
        self.curTime , self.prevTime  = cmds.currentTime(q=True), cmds.currentTime(q=True)-1
        self.autoK = autoK
        self.isRef = False
        self.ikfkSwitch()
    
    
    def autoKey(self, nodes=[], num=None):
        if not self.autoK:
            return
        for i in nodes:
            try:
                if cmds.keyframe(i, q=True, kc=True):
                    cmds.setKeyframe(i, i=True, t=num)
                else:
                    cmds.setKeyframe(i, i=False, t=num)
            except:
                continue
                        
    def ikfkSwitch(self):
        
        sl = MOpenMaya.ls()
        for i in sl:
            if i is None:
                om2.MGlobal.displayWarning('Please select a DAG node')
                continue
                        
            ALD, ARD, LLD, LRD, AB, LB = self.getNewDict(i.fullPathName()) 
            path = Path.path(i.fullPathName()) if self.isRef else i.fullPathName()    
            
            if path in self._updateDict(ALD):   # L arm
                self.armIKFk(ALD, AB)
            
            elif path in self._updateDict(ARD): # R arm
                self.armIKFk(ARD, AB)
                
            elif path in self._updateDict(LLD): # L leg
                self.legIkFk(LLD, LB)
                
            elif path in self._updateDict(LRD): # R leg
                self.legIkFk(LRD, LB)           
           
                
    def armIKFk(self, _dict, armBase):
        switch                      = _dict[-1]['switch']
        ikCtrl, poleCtrl            = _dict[0]['ik1'], _dict[0]['ik2']
        fkCtrl1, fkCtrl2, fkCtrl3   = _dict[1]['fk1'], _dict[1]['fk2'], _dict[1]['fk3']
        fti1, fti2, fti3            = _dict[2]['loc1'], _dict[2]['loc2'], _dict[2]['loc3']
        itf                         = _dict[3]['loc1']
        baseJ1, baseJ2, baseJ3      = armBase['base1'], armBase['base2'], armBase['base3']
        # -------------------------------------------
        
        value = cmds.getAttr(switch + '.FKIKBlend')
        
        self.autoKey([switch, ikCtrl, poleCtrl, fkCtrl1, fkCtrl2, fkCtrl3], self.prevTime)   
            
        # fk to ik
        if value == 0.0:
            upperV, midV = self.setIkStretch([fkCtrl1, fkCtrl2, fkCtrl3], [baseJ1, baseJ2, baseJ3])
            cmds.setAttr('{}.{}'.format(ikCtrl, 'Lenght1'), upperV)
            cmds.setAttr('{}.{}'.format(ikCtrl, 'Lenght2'), midV)
            cmds.setAttr('{}.{}'.format(ikCtrl, 'stretchy'), 0)
            cmds.setAttr('{}.{}'.format(poleCtrl, 'lock'), 0)
            
            cmds.matchTransform(ikCtrl, itf)
            self.setPolePose([fkCtrl1, fkCtrl2, fkCtrl3], poleCtrl)
            cmds.setAttr(switch + '.FKIKBlend', 10.0)
        
        # ik to fk    
        elif value == 10.0:
            for fk, loc in zip([fkCtrl1, fkCtrl2, fkCtrl3], [fti1, fti2, fti3]):
                cmds.matchTransform(fk, loc)
                
            cmds.setAttr(switch + '.FKIKBlend', 0.0)
            
        self.autoKey([switch, ikCtrl, poleCtrl, fkCtrl1, fkCtrl2, fkCtrl3], self.curTime) 
        
    def legIkFk(self, _dict, legBase):
        switch                                   = _dict[-1]['switch']
        ikCtrl, poleCtrl, ik3, ik4, ik5, ik6     = _dict[0]['ik1'], _dict[0]['ik2'], _dict[0]['ik3'], _dict[0]['ik4'], _dict[0]['ik5'], _dict[0]['ik6']
        fkCtrl1, fkCtrl2, fkCtrl3, fkCtrl4       = _dict[1]['fk1'], _dict[1]['fk2'], _dict[1]['fk3'], _dict[1]['fk4']
        fti1, fti2, fti3, fti4, fti5             = _dict[2]['loc1'], _dict[2]['loc2'], _dict[2]['loc3'], _dict[2]['loc4'], _dict[2]['loc5']
        itf1, itf2                               = _dict[3]['loc1'], _dict[3]['loc2']
        baseJ1, baseJ2, baseJ3                   = legBase['base1'], legBase['base2'], legBase['base3']
        # -------------------------------------------
        
        value = cmds.getAttr(switch + '.FKIKBlend')
        
        self.autoKey([switch, ikCtrl, poleCtrl, ik3, ik4, ik5, ik6, fkCtrl1, fkCtrl2, fkCtrl3, fkCtrl4], self.prevTime)   
        
        # fk to ik
        if value == 0.0:
            upperV, midV = self.setIkStretch([fkCtrl1, fkCtrl2, fkCtrl3], [baseJ1, baseJ2, baseJ3])
            cmds.setAttr('{}.{}'.format(ikCtrl, 'Lenght1'), upperV)
            cmds.setAttr('{}.{}'.format(ikCtrl, 'Lenght2'), midV)
            cmds.setAttr('{}.{}'.format(ikCtrl, 'stretchy'), 0)
            cmds.setAttr('{}.{}'.format(poleCtrl, 'lock'), 0)
                     
            setAttrs([ikCtrl], ['swivel', 'roll', 'rock', 'antiPop'], [0, 0, 0, 0])
            setAttrs([ik3, ik4, ik5], ['t', 'r', 's'], [[0, 0, 0], [0, 0, 0], [1, 1, 1]])  
            
            cmds.matchTransform(ikCtrl, itf1) 
            cmds.matchTransform(ik6, itf2)  #
            self.setPolePose([fkCtrl1, fkCtrl2, fkCtrl3], poleCtrl)
            cmds.setAttr(switch + '.FKIKBlend', 10.0)
        
        # ik to fk    
        elif value == 10.0:
            for fk, loc in zip([fkCtrl1, fkCtrl2, fkCtrl3, fkCtrl4], [fti1, fti2, fti3, fti4]):
                cmds.matchTransform(fk, loc)
                
            cmds.setAttr(switch + '.FKIKBlend', 0.0)
        
        self.autoKey([switch, ikCtrl, poleCtrl, ik3, ik4, ik5, ik6, fkCtrl1, fkCtrl2, fkCtrl3, fkCtrl4], self.curTime) 
        
    
if __name__ == '__main__':        
    IkFk(True)
    
'''

Revision History 
Revision 1: 2023-08-11 : First publish
Revision 2: 2023-08-14 : Fix a series of errors in the IK controller
Revision 3: 2023-08-18 : Code Redundancy Optimization
Revision 4: 2023-08-19 : Adding auto keyframe 
Revision 5: 2023-08-22 : Update IK Pole Position Algorithm
Revision 6: 2023-09-15 : Optimize the transition from FK to IK
Revision 7: 2024-03-24 : Refactoring!!!

'''