Automated Stretchy IK Limbs

Before we go about creating our script lets quickly go over the logistics of how to create stretchy IK limbs.

1) create a simple arm
2) create an RP IK handle
3.
a)create a controller
b)group it to itself
c) point snap the group to the IK handle
d) parent the IK handle to the controller (not the group)
e) hide the IK handle

4.
a) create a measurement tool with the first locator at the first joint and the last at the last joint
b) point constrain the first locator to the first joint
c) point constrain the second locator the the third joint

5. In the Hypergraph bring together the following nodes (MMB click and drag)
- the distanceDimention shape node
- the 1st and 2nd joints

and create both a multiply/divide and a conditional node

6. stretch the arm until it is fully extended and can no longer be stretched.

7. Return to the hypergraph and connect the distanceDimention distance value to the firstTerm value of Condition1. Open the attribute editor for the condition node



The copy this value from the first term box to the Second Term box and set the operation to Greater Than

9. Now connect the distance value to the inputX of the multiplyDivide node. Open the attribute editor for the multiplyDivide node.

Set the operation to Divide, and copy the value you seen in Input1 to Input2.

10. Now connect the outputX of the multiplyDivide to the colourIfTrueR of the condition.


Once you have these values recorded and the appropriate connections are made return the controller to its zero position then remove the point constraint on the locator2 of the distance measure and parent it to the controller. YOU MUST DO THIS BEFORE THE NEXT STEP.

 


11. Finally we connect the outColorR to both the joint1 and joint2 scaleX.

And there we go, we have a very basic stretch limb setup.

 

 

So now on to scripting...

Lets assume that the arm already exists with the IK handle and controller already setup (like this file)

The first step is to create a measurement tool between the first and last joints.

in MEL this is

distanceDimension -sp X1 Y1 Z1 -ep X2 Y2 Z2;

First, assuming we have both the beginning and end joints selected (we can invent later some logic to ensure this) we will identify the selection.

string $jointSelection[];
$jointSelection = `ls -sl`;

So we will have to be able to extract the coordinates of the beginning and end joints.

float $jointTrans1[], $jointTrans2[];
$jointTrans1 = eval("pointPosition -w "+$jointSelection[0]+".selectHandle");
$jointTrans2 = eval("pointPosition -w "+$jointSelection[1]+".selectHandle");

so now we can create our distance dimention with

distanceDimension -sp $jointTrans1[0] $jointTrans1[1] $jointTrans1[2] -ep $jointTrans2[0] $jointTrans2[1] $jointTrans2[2];

Now we find out the name of the distance measure and then change it to a name we can recognise

string $distanceMeasures[];
$distanceMeasures = `ls -sl`;
rename $distanceMeasures[1] "stretchLimit";

Next we identify what each of the controlling locators is called and then point constrain them to the joints.

string $mLocators[];
$mLocators = ` listConnections stretchLimitShape`;
pointConstraint -n pc0 $jointSelection[0] $mLocators[0];
pointConstraint -n pc1 $jointSelection[1] $mLocators[1];


Next we create the required utility nodes

shadingNode -n limitCondition -asUtility condition;
setAttr limitCondition.operation 2; //Greater Than

shadingNode -n stretchScale -asUtility multiplyDivide;
setAttr stretchScale.operation 2; //Divide Operation

Move the controller to a stretched pose

setAttr "Wrist_Controller.translateX" 10;

Then start to set up our connections

float $unitStretch = `getAttr stretchLimitShape.distance`;

connectAttr -f stretchLimitShape.distance limitCondition.firstTerm;
setAttr "limitCondition.secondTerm" $unitStretch;

connectAttr -f stretchLimitShape.distance stretchScale.input1X;
setAttr "stretchScale.input2X" $unitStretch;

connectAttr -f stretchScale.outputX limitCondition.colorIfTrueR;

Now we return our controller to its zero position and delete the point constraint.

setAttr "Wrist_Controller.translateX" 0;
delete pc0;
parent $mLocators[0] Wrist_Controller;

And the jewel in the crown is to connect up the out condition (the relative scale) to the scaleX or each of the top two joints.

eval("connectAttr -f limitCondition.outColorR "+$jointSelection[0]+".scaleX");
$elbowJoint = `listRelatives - c $jointSelection[1]`;
eval("connectAttr -f limitCondition.outColorR "+$elbowJoint[0]+".scaleX");

Finally we probably want to hide the measurement nodes

setAttr "stretchLimit.visibility" 0;
eval("setAttr "+$mLocators[0]+".visibility 0");
eval("setAttr "+$mLocators[1]+".visibility 0");

And thats about it

Sure there is a lot of error and exception control that is still needed, but its a good start.

 

--------------------------------------------------------------------------------------------------------------------
Here is the whole code without any clever flow control, but it does the trick
--------------------------------------------------------------------------------------------------------------------

string $jointSelection[];
$jointSelection = `ls -sl`;
float $jointTrans1[], $jointTrans2[];
$jointTrans1 = eval("pointPosition -w "+$jointSelection[0]+".selectHandle");
$jointTrans2 = eval("pointPosition -w "+$jointSelection[1]+".selectHandle");
distanceDimension -sp $jointTrans1[0] $jointTrans1[1] $jointTrans1[2] -ep $jointTrans2[0] $jointTrans2[1] $jointTrans2[2];
string $distanceMeasures[];
$distanceMeasures = `ls -sl`;
rename $distanceMeasures[1] "stretchLimit";
string $mLocators[];
$mLocators = ` listConnections stretchLimitShape`;
pointConstraint -n pc0 $jointSelection[0] $mLocators[0];
pointConstraint -n pc1 $jointSelection[1] $mLocators[1];
shadingNode -n limitCondition -asUtility condition;
setAttr limitCondition.operation 2; //Greater Than
shadingNode -n stretchScale -asUtility multiplyDivide;
setAttr stretchScale.operation 2; //Divide Operation
setAttr "Wrist_Controller.translateX" 10;
float $unitStretch = `getAttr stretchLimitShape.distance`;
connectAttr -f stretchLimitShape.distance limitCondition.firstTerm;
setAttr "limitCondition.secondTerm" $unitStretch;
connectAttr -f stretchLimitShape.distance stretchScale.input1X;
setAttr "stretchScale.input2X" $unitStretch;
connectAttr -f stretchScale.outputX limitCondition.colorIfTrueR;
setAttr "Wrist_Controller.translateX" 0;
delete pc0;
parent $mLocators[0] Wrist_Controller;
eval("connectAttr -f limitCondition.outColorR "+$jointSelection[1]+".scaleX");
$elbowJoint = `listRelatives - c $jointSelection[1]`;
eval("connectAttr -f limitCondition.outColorR "+$elbowJoint[0]+".scaleX");
setAttr "stretchLimit.visibility" 0;
eval("setAttr "+$mLocators[0]+".visibility 0");
eval("setAttr "+$mLocators[1]+".visibility 0");