Creating new Maya Tools using MEL

Another much less used feature of Maya is the creation of a custom tool. We already use tools like the Birail, or IK handle, or joint tool, all of which are tools which use the scriptCtx command to create a new tool based on the selection tool.

In this tutorial we will first create a simple tool that implements our Stretchy IK arm script that we developed earlier.

To create a tool we first have to define the tool using the scriptCtx command, and then invoke the tool with the setToolTo command. The minimum number of flags needed for a tool is the number of selections sets required

scriptCtx
-totalSelectionSets 1
StretchyIK;
setToolTo StretchyIK;

Next we can give it a title

scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
StretchyIK;
setToolTo StretchyIK;


Pasting this into the script editor will come up with the following error;

  Error: Object's name is not unique: StretchyIK

So as with our User Interface example we will have to do a test to see if the tool exists first

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
StretchyIK;
setToolTo StretchyIK;

The next thing we can do is specify which icon we wish to be displayed in the active tool slot on the tool bar

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
StretchyIK;
setToolTo StretchyIK;

We can also specify which type of cursor we wish the tool to use

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
StretchyIK;
setToolTo StretchyIK;

Next we can give some instructions as to how the tool is used

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"
StretchyIK;
setToolTo StretchyIK;

Note now, that if we select an object the tool instantly completes its function. This is because we have not specified how the tool is to handle multiple selections. First we need to specify how many objects we want to be selectable, and we want to be able to add the second selection into the selection set, that is, to toggle the selection set

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"

-setSelectionCount 2
-setAutoToggleSelection true

StretchyIK;
setToolTo StretchyIK;

Now that we can select both objects we can give some instructions after the first selection is made

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"
-setSelectionPrompt "Shoulder selected, please now select the wrist"
-setAutoToggleSelection true
-setSelectionCount 2
StretchyIK;
setToolTo StretchyIK;

and some nice words of wisdom to terminate the tool action

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"
-setSelectionPrompt "Shoulder selected, please now select the wrist"
-setDoneSelectionPrompt "Wrist selected"
-setAutoToggleSelection true
-setSelectionCount 2
StretchyIK;
setToolTo StretchyIK;

We can now limit the tool so that it only works on joints

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"
-setSelectionPrompt "Shoulder selected, please now select the wrist"
-setDoneSelectionPrompt "Wrist selected"
-setAutoToggleSelection true
-setSelectionCount 2
-joint true
StretchyIK;
setToolTo StretchyIK;

Now that our tool interface is setup, we can actually make it DO something

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"
-setSelectionPrompt "Shoulder selected, please now select the wrist"
-setDoneSelectionPrompt "Wrist selected"
-setAutoToggleSelection true
-setSelectionCount 2
-joint true
-finalCommandScript ( "performStretchyIK $Selection1 $Selection2")
StretchyIK;
setToolTo StretchyIK;

proc performStretchyIK($Selection1, $Selection2)
{
print("yay "+$Selection1+" "+$Selection2);
}
setToolTo StretchyIK;

Now that we have our tool working nicely we just have to insert our stretchy IK code into the performStretchyIK code.

Here is the final code

===========================================================================

if( `scriptCtx -exists StretchyIK` ) deleteUI StretchyIK;
scriptCtx
-totalSelectionSets 1
-title "Stretchy IK"
-i1 "blendSurface.xpm"
-toolCursorType "create"
-setNoSelectionPrompt "Select the shoulder and wrist joint for the IK arm"
-setSelectionPrompt "Shoulder selected, please now select the wrist"
-setDoneSelectionPrompt "Wrist selected"
-setAutoToggleSelection true
-setSelectionCount 2
-joint true
-finalCommandScript ( "performStretchyIK;")
StretchyIK;
setToolTo StretchyIK;

proc performStretchyIK()
{
string $jointSelection[];
float $jointTrans1[], $jointTrans2[];
$jointSelection = `ls -sl`;
string $topJoint = $jointSelection[0];
string $botJoint = $jointSelection[1];
string $allDescendent[] = eval("listRelatives -ad "+$jointSelection[1]);
for($i in $allDescendent)
{
if($i==$jointSelection[0])
{
$topJoint = $jointSelection[1];
$botJoint = $jointSelection[0];
}
}
$jointTrans1 = eval("pointPosition -w "+$topJoint+".selectHandle");
$jointTrans2 = eval("pointPosition -w "+$botJoint+".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 $topJoint $mLocators[0];
pointConstraint -n pc1 $botJoint $mLocators[1];
createNode -n wrist_controllerShape implicitSphere;
group -n wrist_grp wrist_controller;
move $jointTrans2[0] $jointTrans2[1] $jointTrans2[2] wrist_grp;
ikHandle -n wrist_IK -sj $topJoint -ee $botJoint;
parent wrist_IK wrist_controller;
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 pc1;
parent $mLocators[1] wrist_controller;
eval("connectAttr -f limitCondition.outColorR "+$topJoint+".scaleX");
$elbowJoint = `listRelatives - c $topJoint`;
eval("connectAttr -f limitCondition.outColorR "+$elbowJoint[0]+".scaleX");
setAttr "stretchLimit.visibility" 0;
eval("setAttr "+$mLocators[0]+".visibility 0");
eval("setAttr "+$mLocators[1]+".visibility 0");
}

===========================================================================