301 lines
12 KiB
Python
301 lines
12 KiB
Python
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', 'root':'RootSystem'}
|
|
LEG_BASE_JNTS = {'base1':'Hip', 'base2':'Knee', 'base3':'Ankle', 'root':'RootSystem'}
|
|
|
|
|
|
# --------------------------------------------------------------
|
|
|
|
@staticmethod
|
|
def getLRDict(_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
|
|
|
|
@staticmethod
|
|
def getLongName(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 = [IkFk.ARM_IK_CTRLS, IkFk.ARM_FK_CTRLS, IkFk.ARM_FKTOIK_LOCS, IkFk.ARM_IKTOFK_LOCS, IkFk.ARM_IKFK,
|
|
IkFk.LEG_IK_CTRLS, IkFk.LEG_FK_CTRLS, IkFk.LEG_FKTOIK_LOCS, IkFk.LEG_IKTOFK_LOCS, IkFk.leg_IKFK,
|
|
IkFk.ARM_BASE_JNTS, IkFk.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
|
|
|
|
@staticmethod
|
|
def setPolePose(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)
|
|
|
|
@staticmethod
|
|
def setIkStretch(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
|
|
|
|
@staticmethod
|
|
def _updateDict(_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, root = armBase['base1'], armBase['base2'], armBase['base3'], armBase['root']
|
|
|
|
# -------------------------------------------
|
|
scaleValue = cmds.getAttr('{}.sx'.format(root))
|
|
# -------------------------------------------
|
|
|
|
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 / scaleValue)
|
|
cmds.setAttr('{}.{}'.format(ikCtrl, 'Lenght2'), midV/ scaleValue)
|
|
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, root = legBase['base1'], legBase['base2'], legBase['base3'], legBase['root']
|
|
|
|
# -------------------------------------------
|
|
scaleValue = cmds.getAttr('{}.sx'.format(root))
|
|
# -------------------------------------------
|
|
|
|
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 / scaleValue)
|
|
cmds.setAttr('{}.{}'.format(ikCtrl, 'Lenght2'), midV / scaleValue)
|
|
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(False)
|
|
|
|
'''
|
|
|
|
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
|
|
|
|
''' |