autoFK demo
File Download of the script below
# INSTRUCTIONS
# - load the function definitions first [SECTION 1]
# - select joints (not just the parent joint!!!)
# - run the =script test= section at the end of this script [SECTION 2]
# [SECTION 1]
import maya.cmds as mc
import maya.api.OpenMaya as om2
def nAutoMultMatrix(
inputList:om2.MSelectionList,
resultAttr:om2.MSelectionList,
reverseEval=False,
noOutput=False
) -> om2.MSelectionList:
inIter = om2.MItSelectionList(inputList)
while not inIter.isDone():
try: inIter.getPlug()
except:
raise TypeError ("nAutoMultMatrix - inputList item in MSList not an attribute: " + inIter.getStrings()[0])
inIter.next()
del inIter
# do the thing
sequence = inputList.getSelectionStrings()
if not reverseEval: # inputList is not in maya's multMatrix order
sequence = list(sequence)
sequence.reverse()
multMat = mc.createNode("multMatrix")
multMatInput = f"{multMat}.matrixIn"
intDex = 0
for attr in sequence:
# IMPORTANT FOR ATTRIBUTE-ARRAYS[n]: INDEX MUST BE SPECIFIED, connectAttr() IS NOT SMART
# TODO: look into nextAvailable flag in connectAttr() and why it's refusing to work
try:
#print(f"DEBUG: {attr} >--> {multMatInput}[{intDex}]")
mc.connectAttr(attr, f"{multMatInput}[{intDex}]")
intDex += 1
except:
raise TypeError( "nAutoMultMatrix - cmds.connectAttr to multMatrix operation failed:", attr)
if noOutput:
# no output. return node immediately
return om2.MSelectionList().add(multMat) # >> MSelectionList
# test for output
errors = []
attrList = []
multOutAttr = om2.MSelectionList()
multOutAttr.add(f"{multMat}.matrixSum")
multOutAttr = om2.MFnAttribute(multOutAttr.getPlug(0).attribute())
try:
# test output MSL to ensure all of them are plugs of matrix type
outIter = om2.MItSelectionList(resultAttr)
while not outIter.isDone():
hasError = False
try:
# ============= see pyExperiments/openMayaNotes_MFnData_Vals.md
plugAttr = om2.MFnAttribute(outIter.getPlug().attribute())
if multOutAttr.acceptsAttribute(plugAttr):
attrList.append( outIter.getStrings()[0] )
# test passed, next item
outIter.next()
continue
else:
hasError = True # not matrix attribute type that .matrixSum expects
except:
hasError= True # om2 MPlug MDataHandle operation chain failed
if hasError:
errors.append( outIter.getStrings()[0] )
outIter.next()
del outIter
except: # MSL operation failed
raise TypeError ("nAutoMultMatrix - om2.MSelectionList operation failed:", str(resultAttr))
# check for any erros caught
if len(errors) > 0: # raise error if there are attributes that aren't matrices
raise TypeError ("nAutoMultMatrix - following attributes for output incompatible, stopping:", str(errors))
# output list all good! connect them all!
multMatOutput = f"{multMat}.matrixSum" # yes, this is what it's called
for n in attrList:
mc.connectAttr(multMatOutput, n)
# finally
return om2.MSelectionList().add(multMat) # >> MSelectionList
def rAutoFK(
inJoint:om2.MSelectionList,
) -> om2.MSelectionList:
gRootName = "rigRoot"
opGroupShort = "autoFK"
opGroupDagPath = f"{gRootName}|r:{opGroupShort}"
jointListIter = om2.MItSelectionList(inJoint)
errorList = []
while not jointListIter.isDone():
try:
# joint test
if jointListIter.getDependNode().apiType() != om2.MFn.kJoint:
# node not a joint
errorList.append(jointListIter.getStrings()[0])
except:
# failed getDependNode or apiType
errorList.append(jointListIter.getStrings()[0])
# tests passed, continue
jointListIter.next()
# if errors occured, raise error
if len(errorList) > 0:
raise TypeError ("rAutoFK - following input object(s) not a joint:", str(errorList))
del errorList
# all clear
# ============= do the thing
jointListIter.reset()
returnList = []
while not jointListIter.isDone():
returnMSL = om2.MSelectionList()
jointName = om2.MFnDagNode(jointListIter.getDagPath()).partialPathName()
# == new c:control shape/curve ->
cControl = mc.circle(name = f":c:{jointName}_FK", nr=[1,0,0], ch = False)
cControl = cControl[0] #TODO: investigate list return for mc.circle
# == parent c:control to rigging operation opGroup
mc.parent( cControl , opGroupDagPath )
returnMSL.add(cControl)
# om2.MSL: A [rigRoot.worldInverse, joint.worldMatrix, control.inverseMatrix]
# om2.MSL: B [t:group.offsetParentMatrix]
# == invoke nAutoMultMatrix with input MSL A result MSL B ->
mxmInput = om2.MSelectionList()
mxmInput.add(f"{gRootName}.worldInverseMatrix")
mxmInput.add(f"{jointName}.worldMatrix")
mxmInput.add(f"{cControl}.inverseMatrix")
mxmOutput = om2.MSelectionList()
mxmOutput.add(f"{cControl}.offsetParentMatrix")
nMultiply = nAutoMultMatrix(mxmInput,mxmOutput) # >> om2.MSL of multMatrixNode
nMultName = (nMultiply.getSelectionStrings()[0])
mc.rename(nMultName,f":n:{opGroupShort}_{jointName}_mxm")
returnMSL.merge(nMultiply)
# connect c:control.rotate to joint.rotate
# connect c:control.scale to joint.scale
mc.connectAttr(f"{cControl}.rotate", f"{jointName}.rotate")
mc.connectAttr(f"{cControl}.scale", f"{jointName}.scale" )
#============= cleanup
# lock and hide:
# - c:control.translate
# - gc_group .translate .rotate .scale
# TODO - nSetAttr (just make be following a lot more parametric)
quickList = [
f"{cControl}.tx", f"{cControl}.ty", f"{cControl}.tz"
] # because channel box only displays separate axes and maya isn't that smart
for attr in quickList:
mc.setAttr(attr, lock = True, keyable = False, channelBox = False)
#============= end cleanup
returnList.append(returnMSL)
jointListIter.next() # next (next)
# ============= done the thing
return returnList
# end rAutoFK()
# [SECTION 2]
# maya script test, open a new python tab and run separately
# ==========================================================
# test for :rigRoot and r:autoFK, and namespace n:
mc.namespace( set=':' )
for nsp in [":r",":n",":c"]:
if not mc.namespace(exists=nsp):
mc.namespace(add=nsp)
sceneSetupSel = om2.MSelectionList()
try:
sceneSetupSel.add("rigRoot")
except:
mc.createNode("transform", n="rigRoot",ss=True)
try:
sceneSetupSel.add("rigRoot|r:autoFK")
except:
mc.createNode("transform", n="r:autoFK",p="rigRoot",ss=True)
sceneSetupSel.clear()
del sceneSetupSel
# run, select the
getSelect = om2.MGlobal.getActiveSelectionList()
rAutoFK(getSelect)
# ==========================================================