Introduction to MEL

In this lesson we are going to look at Maya's Embedded Langauge or MEL and how we can use it as productivity tool. Unlike most other scripting languages MEL is very easy to understand and implement.

If you can do it manually in Maya, you can create a MEL script to do it automatically.

To introduce MEL we are going to create a script that automates the creation of an FK handle for an existing joint.

Revision of FK Setup

We will create an FK handle for an existing joint, such as that need for an FK arm.

So that we can apply this later to a MEL script we will carefully note all the steps and conditions involved in the creation of each FK handle.

Step 1: Create a controller (Typically a NURBS circle) and point snap it to the joint in question and freeze its transformations.

Step 2: Orient the controller such that its local orientation axes line up with that of the joint we wish to control

Step 3: Orient constrain the joint to the controller

Step 4: Group the controller to itself and move the pivot of the group to the joint's parent joint (if it has one)

Step 5: Point and Orient constrain the group to the joint's parent

To start with lets have a look at how Maya performs these actions using MEL. If we open the script editor whilst working we can record which MEL commands are required to perform each of the above tasks.

If we were to perform all these actions on the arm hierarchy shown above we would see the following script.

circle -c 0 0 0 -nr 0 1 0 -sw 360 -r 1 -d 3 -ut 0 -tol 0.01 -s 8 -ch 1; objectMoveCommand;
rename "group3" "Shoulder_GRP";

move -rpr 2.656576 7.581287 -1.171452 ;
makeIdentity -apply true -t 1 -r 1 -s 1;

Step 1
rotate -r -os 0 0 -90 ;
select -r Shoulder_Joint ;
select -tgl nurbsCircle2 ;
orientConstraint -offset 0 0 0 -weight 1;
select -r nurbsCircle2_orientConstraint1 ;
delete;
Step 2
select -r nurbsCircle2 ;
select -tgl Shoulder_Joint ;
orientConstraint -offset 0 0 0 -weight 1;
Step 3
select -r nurbsCircle2 ;
group; xform -os -piv 0 0 0;
move -rpr 1.438862 7.581287 -0.527456 ;
Step 4

select -r Clavicle_Joint ;
select -add Shoulder_GRP ;
pointConstraint -offset 0 0 0 -weight 1;
orientConstraint -mo -weight 1;

Step 5

From this we can see straight away that the process for repeating exactly the steps for creating an FK controller are not that complex, but what is important to notice is that there are several points in the code that refer to specific objects such as the nurbsCircle2, group3, or Shoulder_Joint.

Logical Exceptions

Before we can try to decipher the code above effectively we need to be able to devise a logical flow of events and possible scenarios that we may encounter in the creation of FK controllers.

The first case would be if there is no joint selected (either nothing or the object selected is not a joint).

The second case would be if the joint has no parent. If this is the case then we cannot create an FK controller and we will have to advise the user accordingly.
Both exceptions will result in the prevention of the script executing.

First lets check to see if something at all is selected.

The MEL command ls -sl (list command with the selected only flag) will return an array containing a list of all the nodes currently selected. We start by creating a string array to accept the list and then ask Maya what nodes are currently selected.

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

Next we need to perform a check to make sure that only one object is selected.

if(size($nodes)==1)
.........{
.........Statements if TRUE
.........{
else
.........{
.........//Statements if FALSE
.........print("Please select ONE joint);
.........{

So how do we check so see if a joint is selected.

The MEL command objectType can be used to determine the type of object that has been selected, so the following command can be used to check if the current selection is actually a joint

if(size($nodes)==1)
.........{
.........if(objectType -isType "joint" $nodes[0])
..................{
..................Statements if TRUE
..................{
.........else
..................{
..................//Statements if FALSE
..................print("The selection is not a joint");
..................{
.........}
else
.........{
.........print("Please select ONE joint);
.........{

The next thing to check before we continue is to check to see if the selected joint is a child (ie it has at least one parent joint). We can do this using the listRelatives command (with the -p flag which returns only the direct parent of the selected object);

string $nodeParent[];
$nodeParent = `listRelatives -p $nodes[0]`;
if(size($nodeParent)==1)
.........{
.........Statements if TRUE
.........{
else
.........{
.........//Statements if FALSE
.........print("The joint selected must have a parent joint);
.........{

So now our whole exception statement looks like this

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

if(size($nodes)==1)
.........{
.........$currentJoint = $nodes[0];
.........if(objectType -isType "joint" $nodes[0])
..................{
..................string $nodeParent[];
..................$nodeParent = `listRelatives -p $nodes[0]`;
..................if(size($nodeParent)==1)
..........................{
..........................Statements if TRUE
..........................{
..................else
..........................{
..........................print("The joint selected must have a parent joint);
..........................{
..................{
.........else
..................{
..................print("The selection is not a joint");
..................{
.........}
else
.........{
.........print("Please select ONE joint);
.........{

Now that we are sure that we are dealing with a joint and one with the correct conditions, we can start to follow through the flow of things.

Quite a few of the steps require knowledge of the joint's name. Remember how I told you a long time ago that naming your joints correctly is a necessary evil for later scripting... well here is the reason why.

We want to find out what the "root" of the joint name is, eg. for Clavicle_joint, "Clavicle" is the root. So how can we extract this from our current joint name ($nodes[0]). To do this we can use the tokenize MEL command. Tokenize will look through a string for a delimiter and split the string up accordingly. Eg. tokenizing "Clavicle_joint" with a token of "_" gives "Clavicle" and "joint". So we can find out the root name with the following statements.

string $buffer[];
tokenize $currentJoint[0] "_" $buffer;
$root = $buffer[0];

$jointControl = $root+"_CON";

Now we can use this root anywhere to categorize our controller with the joint it is created from.

Implementing Step 1

With all this done we can tackle STEP 1. Step 1 consists of four commands
1) Create a circle
2) Name it
3) Move it to the joint in question
4) Freeze the controller's transformations

To create a circle, we simple use the MEL command "circle" but we want to be able to create it at the same XYZ coordinates as the joint. We can query the current joint with the getAttr command to find out its current translation vector.
As the joint is part of a hierarchy we will have to unparent it first to reveal its true translation. After the controller is in place we can reparent the joint.

Unparent;
float $jointTranslation[];
$jointTranslation = eval("getAttr "+$currentJoint+".translate");
eval("circle -nr 1 0 0 -c "+$jointTranslation[0]+" "+$jointTranslation[1]+" "+$jointTranslation[2] +" -n "+$jointControl);
CenterPivot;
parent $nodes[0] $nodeParent[0];

makeIdentity -apply true -t 1 -r 1 -s 1;

Now lets compare what we initally got from Maya and how we have used it to create a more generalised form of code.

From Maya Our customized code
circle -c 0 0 0 -nr 0 1 0 -sw 360 -r 1 -d 3 -ut 0 -tol 0.01 -s 8 -ch 1; objectMoveCommand;
rename "group3" "Shoulder_GRP";
move -rpr 2.656576 7.581287 -1.171452 ;
makeIdentity -apply true -t 1 -r 1 -s 1;
Unparent;
float $jointTranslation[];
$jointTranslation = eval("getAttr "+$nodes[0]+".translate");
eval("circle -nr 1 0 0 -c "+$jointTranslation[0]+" "+$jointTranslation[1]+" "+$jointTranslation[2] +" -n "+$root+"_GRP");
CenterPivot;
parent $nodes[0] $nodeParent;

makeIdentity -apply true -t 1 -r 1 -s 1;

Implementing Step 2

Step 2 consists of two steps
1) orienting the controller to the joint
2) deleting the orient constraint

With all our organization this becomes quite simply

orientConstraint -offset 0 0 0 -weight 1-n ConstrainTmp $currentJoint $jointControl;
delete ConstrainTmp;

From Maya Our customized code
rotate -r -os 0 0 -90 ;
select -r Shoulder_Joint ;
select -tgl nurbsCircle2 ;
orientConstraint -offset 0 0 0 -weight 1;
select -r nurbsCircle2_orientConstraint1 ;
delete;
orientConstraint -offset 0 0 0 -weight 1 -n ConstrainTmp $currentJoint $jointControl;
delete ConstrainTmp;

Implementing Step 3

Step 2 consists of one step
1) to orient constrain the joint to the joint controller

With all our organization this becomes quite simply

orientConstraint -mo -weight 1-n $orientName $jointControl $currentJoint;

From Maya Our customized code
select -r nurbsCircle2 ;
select -tgl Shoulder_Joint ;
orientConstraint -offset 0 0 0 -weight 1;
$orientName = $root+"_orientConstraint";
orientConstraint -mo -weight 1-n $orientName $jointControl $currentJoint;

Implementing Step 4

Step 4 consists of one step
1) Group the controller to itself
2) Rename the group based on the root
3) Move the group's pivot to the joint's parent

Before we do this we need to find out what the translate coordinates of the joint's parent are. This raises two possibilities
1) The joint's parent has its own parent
2) The joint's parent has no parent.
If the joint has a parent we will need to temporarily unparent it. Otherwise we just steal the coordinates.

..................string $aboveParents[];
..................float $controlTranslation[];
..................$aboveParents = `listRelatives -p $nodeParent`;
..................if(size($nodeParent)==1)
...........................{
...........................//Then it has a parent

...........................Unparent;..............
...........................$controlTranslation = eval("getAttr "+$nodeParent[0]+".translate");
...........................parent $nodeParent[0] $aboveParents[0];

...........................}
..................else
...........................{
...........................$controlTranslation = eval("getAttr "+$nodeParent[0]+".translate");
...........................{
...........................$parentGroup = $root+"Group";
...........................group -n $parentGroup $jointControl; xform -os -piv $controlTranslation[0] $controlTranslation[1] $controlTranslation[2];

From Maya Our customized code
select -r nurbsCircle2 ;
group; xform -os -piv 0 0 0;
move -rpr 1.438862 7.581287 -0.527456 ;
string $aboveParents[];
float $controlTranslation[];
$aboveParents = `listRelatives -p $nodeParent`;
if(size($nodeParent)==1)
{
//Then it has a parent

Unparent;..............
$controlTranslation = eval("getAttr "+$nodeParent[0]+".translate");
parent $nodeParent[0] $aboveParents[0];

}
else
{
$controlTranslation = eval("getAttr "+$nodeParent[0]+".translate");
{
$parentGroup = $root+"Group";
group -n $parentGroup $jointControl; xform -os -piv $controlTranslation[0] $controlTranslation[1] $controlTranslation[2];

Implementing Step 5

Finally all that is needed is to point and orient constrain the group to the joint's parent

$pointParentName = $root+"_parent_pointConstraint";
$orientParentName = $root+"_parent_orientConstraint";

orientConstraint -mo -weight 1-n $orientParentName nodeParent[0] $parentGroup;
pointConstraint -mo -weight 1-n $pointParentName nodeParent[0] $parentGroup;

From Maya

Our customized code

select -r Clavicle_Joint ;
select -add Shoulder_GRP ;
pointConstraint -offset 0 0 0 -weight 1;
orientConstraint -mo -weight 1;

$pointParentName = $root+"_parent_pointConstraint";
$orientParentName = $root+"_parent_orientConstraint";

orientConstraint -mo -weight 1-n $orientParentName nodeParent[0] $parentGroup;
pointConstraint -mo -weight 1-n $pointParentName nodeParent[0] $parentGroup;

Here is the final MEL script