Library
A lot of functions used by Kangaroo tools are very useful. This document shows the most useful ones for whenever you want to add a python function to your asset.
Utility Nodes
The Kangaroo-Builder is using a ton of Maya Utility nodes. If you are creating your own custom scripts, you can use some of the functions that are used inside the Kangaroo-Builder. Those should help you to write your code faster and more readable.
Just make sure you import the nodes module
import kangarooTools.nodes as nodes
And then you can use all those functions. They all return an output attribute that you can either use in the next function or to connect it with the cmds.connectAttr() function. sTarget is a convenience flag that is in almost all functions. You can keep it as None, or you can add an attribute name. In that case it will automatically connect the output of that function into the that attribute name.
MultiplyDivide Node (1 dimensional)
sMultipl = nodes.createMultiplyNode(xInputA, xInputB, sTarget=None, sOperation='multiply')
cmds.connectAttr(sMultipl, 'locator1.tx')
# to write it shorter, don't assign it to a variable but set the sTarget flag instead:
nodes.createMultiplyNode(xInputA, xInputB, sTarget='locator1.tx', sOperation='multiply', sName='noname')
Create MultiplyDivide Node (3 dimensional)
While most other functions have the bVector flag, for the multiplyDivide node we have an extra function
sMultipl = nodes.createVectorMultiplyNode(xInputA, xInputB, sTarget=None, sOperation='multiply', sName='noname') cmds.connectAttr(sMultipl, 'locator1.tx')
Create PlusMinusAverage Node (1 dimensional)
nodes.createAdditionNode(xInputs, sTarget=None, sName='noname', sOperation='plus')
# xInputs is a list of strings and/or numbers. For example ['a.tx', 2, 'b.ty']
Create PlusMinusAverage Node (3 dimensional)
nodes.createVectorAdditionNode(xInputs, sTarget=None, sName='noname', sOperation='plus')
# xInputs is a list of strings and/or vectors. For example ['a.t', [2,0,0], 'b.t']
Create ReverseNode
nodes.createReverseNode(xInput, sTarget=None)
# This is the result of 1-xInput
Create Range Node
Here instead of having a specific vector node, we use the bOutRangeIsVector. If that’s on, the xOutMin, xOutMax need to be vectors or string attributes that are vectors
nodes.createRangeNode(xValue, xInMin, xInMax, xOutMin, xOutMax, sName='noname', sTarget=None, bOutRangeIsVector=False)
Create Angle Node
nodes.createAngleNode(xInputA, xInputB, sTarget=None, sName='noname')
Create Clamp Node
nodes.createClampNode(xInputA, xMin, xMax, bVector=False, sTarget=None)
Create Distance Node
nodes.createDistanceNode(xInputA, xInputB, sTarget=None, sName='noname', fNormalized=None, sDivide=None)
Create Condition Node
nodes.createConditionNode(xFirstTerm, sOperator, xSecondTerm, xOutputTrue, xOutputFalse, sName='noname', sFullName=None, bVector=False, sTarget=None, bForce=False)
# sOperator can be '==', '<', '<=', '>=' or '!='
String Expression to Nodes
There's a function that takes a spring expression and puts that into nodes.
sExpr = nodes.fromEquation('(3 + node.out) * 0.5')
Looks fancy - but we've ended up using the utility node functions above more. There's only one place in the armLeg limb, where we use it on a more complex function:
sExpr = '%s * (1.0 - (%0.8f ^ ((%s - %s) / %s))) + %s' % (sSoftAttr, math.e, sSoftAttrRev, sDistanceNormalized, sSoftAttrSafe, sSoftAttrRev)
sSoftExpr = nodes.fromEquation(sExpr, sFullName=self._createNodeName('softIk'))
Driven Keys
direct
nodes.setDrivenKey(sDriverAttr, fDriverVals, sDrivenAttrPath, fDrivenVals, sInTanType='clamped', sOutTanType='clamped')
# fDriverVals is a list of driver values, and fDrivenVals is a list of driven values
# In this example below if locator1.ty is 2, locator2.ty will be 10
nodes.setDrivenKey('locator1.ty', [0,1,2], 'locator2.ty', [5,2,10])
indirect
Sometimes when the driver values on the sDriverAttr are a result of a specific pose of a control, it’s more convenient to set the values by the control. In this function we also have sDriverAttr, however no fDriverVals. Those are created under the hood from the sCtrlAttr and fCtrlValues flags.
nodes.setDrivenKeyController(sCtrlAttr, fCtrlValues, sDriverAttr, sDrivenAttr, fDrivenVals, sInTanType='linear', sOutTanType='linear', sFullName=None)
Curves
Import the following functions like this:
import kangarooTools.curves as curves
Create pointOnCurveInfo node
sNode, sPoint = curves.createPointInfoNode(sBeltCurve, fParam=0.5)
That node needs the parameter. To quickly convert between parameter, positions and percentages chech the following chapter.
Getting Curves Values
There's a ton of functions that convert between points, params and percs with functions. They are easy to find since they all have the same naming convention.
Here some examples:
# Getting points from the joint Percentages:
fPointsOnCurve = curves.getPointsFromPercs(sCurve, [0, 0.2, 0.5, 0.8, 1.0], bReturnNumpy=True)
# Getting points from the joint Percentages:
fPointsOnCurve = curves.getParamsFromPercs(sCurve, [0, 0.2, 0.5, 0.8, 1.0], bReturnNumpy=True)
# Getting percentages from Params:
aPercs = curves.getPercsFromParams(sCtrlBpCurves[0], [0, 0.5, 1.4])
Joints to Ctrls Weighting
The getCtrlWeightings2() function takes joint params (or percentages) and ctrl params (or percentages) and gives you back a list with the length of the joints.
For each joint something like [2, 3, 0.25]. In this example it means the weighing for this joint is between ctrl 2 and ctrl 3, with the ratio of 0.25 (0.0 would mean fully at ctrl 2, and 1.0 would mean fully at ctrl 3)
xCtrlWeightings = xforms.getCtrlWeightings2(fJointParams, fCtrlParams, fIsCircleWithParamLength=None)
Create Deformers
Since the loadDeformer() handles mainly weights, in some cases it's cleaner to create the deformers in python before the loadDeformer() function.
import kangarooTools.deformers as deformers
Create Morph
For blending a mesh to another, use the morph
deformers.createMorph(sChild, sParent)
Legacy Wrap Deformer
sUnorderedWraps, sBase = deformers.createWrap(sChildren, sParent, bExclusiveBind=True)
Proximity Wrap Deformer
deformers.createProximityWrap(sChild, sDrivers, sName=None, bCreateBaseMesh=False, fFalloffScale=10.0)
Add blendShape Targets
import kangarooTools.blendShapes as blendShapes
sTargetAttrs = blendShapes.addTargets(sMesh, sTargetMeshes)