Maya MEL Programming

 

MEL (Maya Embedded Language) is the interpreted Maya Autodesk 3D software scripting language. While MEL as been there since a long time, it is still being used by cinema studio’s technical directors to extend existing Maya UI, menus and tools. Given Maya compatible python package that were created on the way (like Pymel, cmds, openMaya) since Maya’s creation, the place and use cases of MEL has been greatly reduced while it might still be usefull for some specific tasks or fast prototyping.

Ways to run MEL/Python code in Maya

  • Command Line
  • Command Shell
  • Script Editor
  • Shelf Tool
  • Script File
  • Callback
  • Expressions

Default Mel scripts folder

// scripts put here will be automatically loaded by Maya at launch time
~/maya/x.x/scripts

Adding custom scripts folders

// Custom extra scripts folders could be added by updating bash env variable
set MAYA_SCRIPTS_PATH=$MAYA_SCRIPTS_PATH;<new_script_path>;

// An alternative way is to update Maya.env file with
MAYA_SCRIPTS_PATH = <new_script_path>;

// Retrieve the current Maya.env filepath location
string $envFile = `about -environnementFile`;

Maya Init scripts

// userSetup.mel is being executed every time maya is started
// this can't be used to set the scene up

Debugging with Trace

int $a = 65;
trace -where ("value : " + $a);
// returns 
// file: <filepath> line n : value : 65 

CRUD operations

// 3 Mel command mode: create (default), query, edit
// <nodeType> <mode> <flags/values> -name <nodeName>
sphere -radius 4 -name mySphere;  // Create (default)
sphere -query -radius mysphere; // Query 
sphere -edit -radius 2 mySphere; // Edit
rename mySphere mySphere2; // Rename
delete mySphere; // Delete

Reloading (updated) Mel file

source myMelFile.mel // reimport current myMelFile definition state

Cancel last action

undo;

Retrieve available flags for command:

help(sphere)

Mel Variables

int $myvar = 2;
sphere -edit -radius $myvar MySphere;

Mel Data Types

// Int & Float
int $myInt = 2;
float $myFloat = 2.0;

// String
string $myString = "Hello World\n";
int numChars = size($myString);

// Vector
vector $myVector = << 10.0, 2.0, 3.0 >>; 
// update x axis value as follow
vector $myVector = << 15.0, $myVector.y, $myVector.z >>; 
move -absolute ($myVector.x) ($myVector.y) ($myVector.z);
print ($myVector.x);

// Array
int $myIntArray[] = {2, 5, 8}; // more efficient to provide array size at creation time
string $myStrArray[3] = {"hello", "world", '!'}; 
vector $myVecArray[2] = {<< 10.0, 2.0, 3.0 >>, << 10.0, 5.0, 3.0 >>};
float $myFloatArray[3] = {2.5, 5.0, 8.1};
print ( $myFloatArray[0], size($myFloatArray) );

// dynamic array resizing
int $vals = [];
$vals[0] = 2;
$vals[4] = 4;
print ( $vals ) // returns 2 0 0 4

// appending to array
$myStrArray[ size($myStrArray) ] = "\nYea";

// deleting array content
clear( $myStrArray );

// Matrices
matrix $myMatrix[2][4] = << 3,8,9,7; 5,4,3,2 >>;
print ( $myMatrix[0][0] );
float $topTransformMtx[] = `xform -query -matrix topTransform;` // retrieve topTransform node's matrix as a float values list

// Boolean
bool_true = on; // => true
bool_true = true; // => true
bool_true = yes; // => true

bool_false = off; // => false
bool_false = false; // => false
bool_false = no; // => false

Assignment chaining & in-place operation

int $a, $b;

$b = $a = 50;

$a/=1;
$a+=1;
$a-=1;
$a*=1;

$a++;
$a--;

Getter & Setter

float $myNodeScale = `getAttr myNode.scale`; // store scale info in $myNodeScale var

vector  $tmp = << 1.0, 0.0, 0.0>>;
setAttr myNode.scale ($tmp.x) ($tmp.y) ($tmp.z);

Custom Attributes

/* Create */
addAttr -longName "mySlider" -attibutType long -min 0 -max 10 -defaultValue 0 myNode; //create a slider

if (!attributeExists ("id", myNode)) {
    addAttr -longName "id" -attibutType long myNode;
}

/* Read */
listAttr myNode; // list all attributes
listAttr -userDefined myNode; // list all custom attributes
listAttr -keyable myNode; // list all keyable attributes

/* Update */
setAttr -keyable true myNode.mySlider;

/* Delete */
deleteAttr myNode.mySlider; //delete attr

If statement

int $a = 10;

if ( $a < 10) {
	print "a is lower than 10";
}
else if ($a == 10) {
	print "a is equal to 10";
}
else {
	print "a is higher than 10";
}

Switch statement

int $a = 10;

switch ($a) {
    case 1:
        print "a == 1";
        break;
    case 2:
        print "a == 2";
        break;
    default:
        print "a is not in equal to either 1 or 2";
        break;
}

Conditional operator

int $a = 10;
$equal_ten = ($a == 10) ? true : false; 

For loop

int $elems[3] = { 10, 5, 2 };
int $index;

for ($index=0; $index < size($elems); $index++ ) {
    print ( $elems[$index] );
}

For-in loop

int $elems[3] = { 10, 5, 2 };
int $elem;

for ($elem in $elems ) {
    print ( $elem );
}

Warnings & Errors

warning "Incorrect parm, it will be ignored!";
error "Error, illegit value";

Check if a procedure exists

if (!`exists myProcedure`)
    error "Error, Couldn't find myProcedure. Please install it"
else 
    // returns the filepath where the procedure comes from
    `whatIs myProcedure`;  // whatIs could also be used to retrieve variable's type

Type variables

int $a = 10;
whatIs "$a"; // returns int variable

Functions aka:procedure

proc int test (int $input){
	if ($input == 1) {
		return 1;
	}
	return 0;
}

Try / Except

int $err = catch( eval("source foobar") ) // return 1 if an error was raised

Time and keyframes


float $currentFrame = currentime -query; //retrieve current frame
currentTime 2; // set current frame to be the #2 
currentTime 2sec; // compute and place the current frame to be on 2sec 

listAnimatable myNode; // retrieve all keyable attributes
keyframe -query -name myNode; // retrieve myNode keyframed attributes
keyframe -query -name myNode.translateY; // retrieve myNode.translateY keyframes
keyframe -query -time 2 -eval myNode.translateY; // retrieve myNode.translateY keyframes at frame 2 without evaluating DAG
getAttr -time 2 myNode.translateX; // retrieve x translate value at frame 2 (getAttr with -time could induce DAG eval)
play; // play the frame range

setKeyframe -time 2 -value 2 myNode.translateY; // set a keyframe at frame 2
setKeyframe -time 4 -value 4 myNode.translateY; // set a keyframe at frame 2
setKeyframe -insert -time 3 myNode.translateY; // insert a keyframe at frame 3 without changing the curve shape
keyframe -edit -time 4 -valueChange 10 myNode.translateY; // update keyframe value

Fetch & select nodes

ls  // list all nodes
ls -type surfaceShape // list all surface shape nodes
ls -selection // list currently selected nodes
ls -clear // cancel selection

listRelatives parentNode; // list all child nodes, extra flags could be used to filter out result
listRelatives -parent childNode; // retrieve parent nodes
listRelatives -shapes node; // retrieve shapeNode

select -r mySphere; // select a node


Parent & Group

group -name topTransform mySphere; // create a new transform node and parent the node to it. Transformation applied to topTransform will also affect mySphere
parent mySphere2 topTransform; // make mySphere2 a child of topTransform and applied transforms
inherit Transform -off MySphere2; // Disable parent tranforms inheritance for MySphere2

Transform obj

/* Transform Objects: */

// -relative or -absolute flags could be used
move -relative 1 0 0 topTransform; // Translate topTransform and its childrens
scale -absolute 10 2.3 3 MySphere2;// Scale MyShere2
rotate -relative 90deg 0 0 topTransform; // Rotate topTransform and its childrens

xform -relative -translation 0 5 -6 -scale 0.5 0 0 -rotation 90deg 0 0 topTransform; // Do all operation at once

// transform every selected node per 1 on y axis
$nodes = `ls -selection`;
for ($node in $nodes) {
    setAttr ($node + ".translateY") (1.0 + `getAttr $node.translateY`)
}


/* Move an object along a curve: */
pathAnimation -curve testCurve myNode;

Joint skeleton

joint; // create joint1 under the currently selected obj (might wanna do a ls -clear before) 
joint -position 0 1 0;  // create joint2 under joint1 at given position
insert joint1; //create a new joint under joint1 instead of joint 2 as the joint command would have

joint -edit -relative -position 0 5 0 joint2; // edit joint2 position to be 0 5 0 from its parent joint3
joint -query -degreeOfFreedom joint3; // return axis that could be edited
joint -edit -degreeOfFreedom "y" joint1; // limit joint1 movement to y axis only

joint -query -limitY joint1; // retrieve current min/max possible values for edition (-360; 360);
joint -edit -limitY 0deg 180deg joint1; // limit rotation value on Y axis from 0-90 degrees
joint -edit -angleY 50deg joint3; // rotate joint3 relatively to its parent
rotate 0 90deg 0 joint2; // rotate joint2 with an absolute rotation

removeJoint joint2; //delete joint



/* rescale skeleton: */

proc rescaleSkeleton ( string $inputNode, float $outputScale) {
    
    string $jointNodes = `listRelatives -fullPath -children -type joint -allDescendents $inputNode`;

    for ($jointNode in $jointNodes) {
        // retrieve current joint position, edit it and update it
        float $currentJointPosition = `joint -query -relative -position $jointNode`;

        $currentJointPosition[0] *= $outputScale;
        $currentJointPosition[1] *= $outputScale;
        $currentJointPosition[2] *= $outputScale;

        joint -edit -position $currentJointPosition[0] $currentJointPosition[1] $currentJointPosition[2]  $jointNode;
    }
}
--------------------------------

/* Retrieve joints position: */

proc printJointsTransform(string $inputNode, string $spaceFlag) {
    /*
    $inputNode is the skeleton root node
    $spaceFlag is either '-worldSpace' or '-objectSpace'
    */

    $jointNodes = `listRelatives -fullPath -children -type joint -allDescendents $inputNode`;
    
    for ($jointNode in $jointNodes) {
        // retrieve current joint transform and print it
        float $trans [] = `xform $spaceFlag -query -translation $jointNode;`
        float $rot [] = `xform $spaceFlag -query -rotation $jointNode;`
        float $scale [] = `xform $spaceFlag -query -scale $jointNode;`

        print ("\n Joint: " + $jointNode);
        print ('\n Translation: ' + $trans[0] + ' ' + $trans[1] + ' ' +  $trans[2]);
        print ('\n Rotation: ' + $rot[0] + ' ' + $rot[1] + ' ' +  $rot[2]);
        print ('\n Scale: ' + $scale[0] + ' ' + $scale[1] + ' ' +  $scale[2]);
    
    }

}

UI


// Simple window connecting attr of a node to some parm
// source: https://www.asc.ohio-state.edu/price.566/courses/694/cntrlConnections.html


{
    string $node [] = `sphere`;
    window -title "1st way node parm binding" testWindow;
        panelLayout -configuration "horizontal2";
        columnLayout;
            attrFieldGrp -attribute $node[0] + ".translate" -label $node[0] + "_translate";
            attrFieldGrp -attribute $node[0] + ".rotate" -label $node[0] + "_rotate";
            attrFieldGrp -attribute $node[0] + ".scale" -label $node[0] + "_scale";
            setParrent ..;
  
}
///////////////////////////////////////////////////

{
    string $nodes [] = `ls -selection`;
    string $node = $nodes[0];
    //deleteUI testWindow2;
    
    window -title "2st way individual node parm binding" testWindow2;
        columnLayout;
            floatFieldGrp -label "Translate" -numberOfFields 3 translation_field;
            
            // connect fields to attr, all 3 parms needs to be plugged to work
            connectControl -index 1 translation_field ($node + ".tx");
            connectControl -index 2 translation_field ($node + ".ty");
            connectControl -index 3 translation_field ($node + ".tz");
    
    showWindow testWindow2;
    
}

Miscellaneous


/* Type object: */

$obj = `ls -selection`;
objectType $obj; // returns node type
--------------------------------

/* Check if object exists: */

objectExists nurbs1; // returns 1 if nurbs1 exists, 0 otherwise
--------------------------------

/* Find operating System and Versioning: */

float $mayaVer = float(`about -version`);
string $opSys = `about -operatingSystem`;
--------------------------------

Sources

    - Complete Maya Programming, David A. D. Gould (2003)
    - https://www.asc.ohio-state.edu/price.566/courses/694/