Maya MEL scripting
==================



- learning two things:
    - the language itself;
    - working with a scene through MEL (how objects are built, etc.);


- fundamental things in MEL scripting itself:
    - local and global procedures; global variables;
    - when and how your script is read into Maya; (+whatIs command)
    - userSetup.mel;
    - recommended structure for a script;
    - plug-in note: using plug-in commands in scripts;
    - where to put your scripts;
    - modifying/overriding original Maya scripts;


- fundamental things in a Maya scene (that every scripter should be aware of):
    - DAG and DG nodes; shortest unique names;
    - transforms and shapes (+components);
    - intermediate objects; multiple instancing;
    - selections and how to handle them;
    - 'exploring' the scene (+related commands: ls, listRelatives, listConnections, listAttr, ...);
    - the hierarchical structure of node types;


- coding style (some quick tips);
    - the different formats for calling MEL commands;
    - passing expressions as MEL command arguments;
    - script/global proc naming (should be the same);
    - be aware of string / string[] command results;
    - using assignments and conditions (=/==), using expressions in conditions ( if (1) ... );
    - the (<expr> ? <true> : <false>) expression;
    - global variables (avoid them if possible ;)



- coding tips/techniques;
    - using `ls` for filtering;
    - proper Maya-style selection handling (using `ls -dag -lf -sl`, ...);
    - safely querying existence and/or properties using size(`ls ...`);
    - setting attributes '.ma style' (`select <node>; setAttr ".attr" 0;`);
    - protecting execution flow with catch();
    - using dgeval() -- when and why;
    - running in UI/batch mode;


- script working style (the way your script works):
    - handling selection;
    - ...


- writing complete plug-in modules;
    - using MEL and API together for most efficiency;
    - what is a module;
    - directory structure of a module;




--------------------------------------------------------------------------------------------------



The intention of this document is to give not just an introduction of the MEL scripting language,
but rather to show how to use it in practice. It intents to address most of the common issues that
people possibly face when learning Maya/MEL scripting.

I've tried to collect all the most common problems I've faced during my MEL studies to provide
practical help to people who want to write Maya/MEL scripts. However I'm not sure this document
is far from complete -- so if there's anything you'd like to read about (and related to learning
MEL scripting of course ;) please tell me and I'll include.

I'm not going to talk about the syntax of MEL or anything else you can pick up from the regular
Maya documentations; instead, I try to focus on practical usage and related things that can come
handy (although there will be some coding tips as well).

(If you're new to MEL, I recommend to look up the docs for each command you read in the examples
to know what they do exactly. Remember any flags also. Learn. :)

I'm not a native English, but I'm trying to be as clear as I can and provide examples for better
understanding (and hoping the best ;)

I also tried to organize this document to match an usual MEL learning curve (so it begins with
the most basic things and gradually proceeds to more complex examples.)

Okay that's enough babbling :)





Two things you need to learn
============================


If you're new to MEL scripting, there are two things you'll need to learn:


1. The language itself.

    Of course, whaddidya think? :) It's especially worth to get some knowledge
    about how to write clean and understandable program code in general. Mostly
    recommended is to read C/C++ literature about clean coding style, as MEL is
    very similar (mostly plain C).

    My personal opinion is that it's one of the most important issues. Please, don't
    pick your programming style from MEL scripts you downloaded from the 'net, 'coz
    most of them are terrible!



2. How to work with a Maya scene through MEL.

    Even if you're an experienced Maya user by 'regular' means, you'll need to learn
    how to handle things through MEL (selections, creating objects/attributes, etc).

    It's basically the same like if you were working with Maya through the UI -- the
    main difference is you have to 'tell' Maya more precise what you want to do, and
    you should aim your script to be working in most imaginable circumstances. :)
    Briefly, you need to be more precise.
    
    If you didn't work with node-based software before (like Houdini, Shake, Composer,
    or Digital Fusion), get a clear understanding of what the Dependency Graph is,
    and how does it work.


Additionally, PLEASE read the Maya docs: "Essentials - 8. Setting Environment Variables"
to know about the Maya environment variables, Maya.env and stuff like that. VERY IMPORTANT
(even for windows users :) If you want to do scripting, read the basics first carefully.

You may spare yourself and other people a few questions (no, I didn't say stupid questions,
I simply just said _questions_, okay? :)



    
    
Two manuals you'll seek often
=============================



1. MEL Command descriptions

    Perhaps this needs no explanation. It's advised to carefully check the command
    descriptions as well as the various flags. It can happen that what you're planning
    to write a procedure for, can be done with a single command with the proper flags
    (especially true for ls). Most useful is the help command:
    
    help <cmd>            displays short help about the command
    help -doc <cmd>            displays the html man page of the command



2. Node and Attribute reference

    It's a list of all existing node types in Maya, with all their attributes explained.
    You can check either in alphabetical or in hierarchical order (although the latter
    is quite incomplete, at least for 4.0; but still very useful).


And again, familiarize yourself with C/C++. Once you're a pro scripter you won't even notice
when switching to API :)




Fundamental things about MEL scripting
======================================



Local and global procedures
---------------------------


A MEL script is a simple text file with '.mel' extension, and can contain MEL code in the
following forms:


    - local procedures;
    - global procedures;
    - MEL code not embedded in any procedure;


When a script is read in (sourced), all MEL code not embedded in a procedure gets executed.
Also, all the procedures are read by Maya, and after the sourcing is done, all the global
procedures defined in the script can be called.

You don't have to use procedures at all when scripting, but for any more serious stuff
(except quickly executing some commands) it's recommended to use them.



The difference between local and global procs:

    - local procedures can only be called from the same script they were defined in
      (they're 'local' to the script);

    - global procedures can be called from anywhere.


Note that you need to define a procedure before calling it (so if proc B calls proc A in
a script, you'll need to place proc A to an earlier place in the script than proc B).

Read the following section below on how to name your scripts and global procedures.





When and how your script is read into Maya
------------------------------------------


There are more ways to get your script sourced (read into Maya). First of all, you can
type the MEL command


    source <script name>;


(You may need to put the script name into quotes if it contains funky characters; but
you don't have to append the .mel extension, as it will be done automatically).

This command will search all Maya script folders for the script, and if one is found,
it'll be read in. All MEL commands in the script not enclosed in any procedure will
be executed in order, and all global procedures in the script will be available from
now on.



You don't need to explicitly source a script however, as there's an automated mechanism.
If you enter the name of a global procedure, the following things will happen:

    - First, Maya checks if there's such a procedure already defined. If so,
      calls it.

    - If no such proc defined, Maya will look in all the script folders for
      a script with the same name as the given procedure.

    - If such a script is found, Maya checks if it contains the global proc
      with the given name.

    - If it's so, Maya sources the script and then calls the procedure.
      (Otherwise the script won't be sourced.)


Briefly, if you give the same name to a script and a global procedure in it, you
don't have to source the script -- just enter the proc name, and Maya will do the
rest.

Note however that in such cases Maya will source the script only ONCE (not every
time you call the procedure), so if you change anything in the script after the
first call, you'll need to source it again 'manually'.


(A quick note: you can use the 'whatIs' command to check procedures/scripts. For
example, 'whatIs myProcedure' will give you info about the global proc myProcedure,
or 'whatIs myScript.mel' tells the path where Maya would read the script from.)




userSetup.mel
-------------


[Although I don't want to repeat anything existing stuff in the Maya docs, this is
one of the exceptions, as pretty many people don't even have a clue what the heck
userSetup.mel is (and keep on writing mails 'i don't find mine, where it should
be!?' :) ]


userSetup.mel is a 'dedicated' MEL script -- it gets sourced by Maya on every
startup. It can be used to perform your own startup stuff -- usually script sourcing.

It doesn't have to exist (and it doesn't exist by default after a Maya install),
but if Maya finds a script with such name on startup, it sources it.

[Begging note: PLEASE USE userSetup.mel ONLY FOR SOURCING OTHER SCRIPTS (or other
short forms of initializations)! But don't define any local/global procs, or don't
rename any script to 'userSetup.mel' just 'coz you want it to load on startup.
Otherwise your TD will be waiting for you at a dark corner at night... :]





Recommended structure of a script
---------------------------------


In general, when you write a script, it should have the following structure:

    
    - script header (some comment lines containing the name and other infos
      about the script);

    - local procedure definition(s);

    - global procedure definition(s) [possibly only one];


The script header should contain the script name, version number, creation/last
modification date, a short description/usage notes, possibly a quick version
history. This will help everybody (even you!) to keep track of what's the hell
was going on with that script. :)

Then the local procedure definitions follow. They're 'local' to the script (no
one 'outside' sees them), so feel free to name them as you like, and use as
many dirty coding tricks as you want (as long as they work :)


At the end the global procedure(s).

It's recommended to have only one global procedure in a script, and it should be
the same name as the script file itself (see previous section), so the users
don't need to explicitly source the script -- they just have to type in the main
procedure name.

Also, choose you script/global proc name carefully; the name should be unique (as
if there's an existing global procedure with the same name, it'll be overridden).
The best idea is to choose a 'prefix' that you use for all the scripts and global
procedures (it can be your monogram or whatever).

So, instead of naming the script 'createBigDragon', use something 'prefixed', like
'xyCreateBigDragon' (but not the xy prefix as I'm already using it!!! You'll be
punished if you try to steal it! :) That's usually enough.

(If you followed general structured programming style, any global procedures are
quite short, and mostly contain calls to the local ones which do all the grunt
work.)




Note about using plug-in commands in scripts
--------------------------------------------


Please note if use MEL command(s) in a script that are provided by a custom plug-in
(not internal Maya MEL commands), you need to load the plug-in first, then source
the script after -- in this specific order.

(If you load the plug-in after the script is sourced, Maya won't recognize the MEL
commands created by the plug-in and the script will stop. If this happens, source
the script again.)


Also note, that...
------------------

On Maya startup, the order of script sourcing (including userSetup.mel) and the
order of plug-ins' loading is _undefined_. In other words, it can happen that
plug-ins get loaded _after_ userSetup.mel is sourced.

So it can happen you have a plug-in flagged as 'auto-load', and you have a script
that needs a command from that plug-in, which you source from userSetup.mel --
and you still get error messages on Maya startup. Usually, it means that the
script is sourced _before_ the plug-in load, which is no good.

To overcome that, you need to make sure things will go in the proper order (plugin
load first, script sourcing after). (An unfortunate thing is that as far as I know
there's no 'official' way to do this.) Here come some ideas:


1. Load the required plug-in(s) 'by hand'. Put something like that into the first
line(s) of your script (without placing into any procedure):

    loadPlugin -q myPlugin.so;    // or .mll on nt



2. Let the script be sourced after all startup tasks are done, and Maya became idle:

    evalDeferred -lp "source myScript";

(But I'm not sure how well this one works when Maya is started in batch mode.)





Where to put your scripts
-------------------------


...



A note about using modified versions of original Maya scripts
-------------------------------------------------------------


It can happen you get a modified/fixed version of an original Maya script (especially
if you check my home page ;) These usually reside in the C:/aw/MayaX.X/scripts/
folder (linux users should now imagine a pathname for themselves.)

You may think that you need to overwrite the script(s) there, if you have a modified
version, but that's really not the recommended way. Since the 'core Maya' script path
is always checked last for a script, it's enough if you copy any modified Maya scripts
into your scripts/ folder.

Again, briefly: don't overwrite any installed Maya script. Always use _your_ scripts/
folder, and it will work.

A good workflow if you want to change something in Maya is similar: copy the script
you want to mess with from the Maya installation to your scripts/ directory, and
mess around with that copy. If you delete the modified version, Maya will 'fall back'
to it's original script provided with the installation.





Fundamental things about Maya/scene handling
============================================


Besides knowing the language, you also need to know about how to manage a scene
through MEL, or get along with Maya in general. The following sections try to touch
the most common things needed.





DAG and DG nodes
----------------


Maya scenes are built from nodes -- they're the building blocks of construction histories,
shading networks, animation curves and clips, constraints, etc. From our current point of
view, we can distinguish two different main node types:

- DAG nodes: represent the scene hierarchy; parent-child relationships (Directed Acyclic Graph);
- DG nodes:  represent data flow within the scene, like histories, etc. (Dependency Graph);

All these nodes share a common namespace, ie. every node should have a unique name (no
two nodes can exist with the same name).

There's a very important issue about node (especially DAG) naming, which is frequently
ignored by home-scripters (and unfortunately quite often by commercial software programmers
as well). So we need to discuss about...





Shortest UNIQUE names of DAG objects
------------------------------------


The naming is a bit different for DG and DAG nodes. DG (=history) nodes' name are simply the
node name, but this is not the case for DAG (hierarchy) nodes.

To uniquely identify a (DAG) node, you need to know it's unique name. In Maya, two DAG nodes
can have the same _shortest_ name, as long as they don't have the same parent. It can be so
because in Maya, the 'real' names of DAG nodes are not only the name of the node, but rather
a hierarchy 'path' specification.

The easiest way is to see an example. Say we have the following hierarchy:


    Hierarchy:        Full names:        Shortest unique names:


    + root1            |root1            root1
    !
    +--+ group1        |root1|group1        group1
    !  !
    !  +-- pCube1        |root1|group1|pCube1    group1|pCube1
    !
    !--+ group2        |root1|group2        group2
       !
       +-- pCube1        |root1|group2|pCube1    group2|pCube1



In the above example we have two object with the same _shortest_ name (pCube1). But in Maya,
DAG objects are identified by their _shortest unique_ names. So if you try to select one cube
with the following command:

    select -r "pCube1";

It won't work ("more than one objects with that name"). Instead, you should say one of the
followings:

    select -r "group1|pCube1";

    select -r "|root1|group1|pCube1";


(This naming style is very similar to relative and absolute filesystem path specifications).

You should always be take care that your scripts' functions always refer objects to their
_shortest unique_ or absolute names. Otherwise they'll break like hell any time. Usually MEL
commands return shortest unique object names so you won't have a problem with this, but there
are a few exceptions, where you should explicitly tell the command to do so (like the
listRelatives command, where you should always use the -path flag.)

A good advice is to always check the man page of the list-command you want to use, if there's
a flag for returning proper node names, and if there's one, always use it.





Transforms and Shapes; multiple instancing
------------------------------------------


This is one of the earliest obstacles in MEL scripting. What you see as one NURBS, polygons
or whatever object on the screen, is really not one, but rather consists of two components.

In Maya, all objects you see in the viewport consists of two DAG nodes: a transform and a
shape. The transform tells where the object is in 3d space (translate, rotate, etc.), and
the shape contains the actual geometry data.

(You can see the shape nodes in the Outliner by turning on Show Shapes.)

One transform nodes can have more than one shape nodes under it. What's even more interesting
is that Maya allows to share a single shape node between multiple transform nodes (this is
called multiple instancing). In other words you can have multiple copies of the same object,
but the actual geometry data is stored only once (thus saving memory); and if you change the
shape by modeling or whatever, all instances will reflect the changes.

(You can visualize multiple instancing in the Hypergraph, by enabling the display of Shape
nodes.)

The DAG naming scheme described in the previous section also applies to shape nodes, as they're
DAG nodes as well, part of the scene hierarchy. Generally you should think of shapes as just
another level in the scene DAG hierarchy (except you can't re-parent them as easily as transforms).

Briefly, you should always use shortest unique names of shape nodes. Even two transform nodes
under a common parent can have shape nodes with the same shortest name, so watch out!


What can be confusing for MEL beginners is the users usually select transform nodes while
working in Maya (when you pick an object in a viewport for example, Maya selects its transform
node). Unfortunately most of the time we're interested in the shape node.

So, let's say, in your first script you want to determine the type of the first selected object,
and type something like that:


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

    if (size($sl)>0) print("Type of first selected object: "+ nodeType( $sl[0] ) +"...\n" );
    else print("Nothing selected!\n");
}



The above code always return "transform", no matter what you select. Dammit! :) Why don't you try
this instead:


{
    string    $sl[]=`ls -sl`;                    // get list of selected objects

    if (size($sl)>0)                    // check if there's a selection
    {
        if (size(`ls -s $sl[0]`)==0)            // check if the object is a shape node
            $sl=`listRelatives -pa -ni -s $sl[0]`;    // if not, get shape node(s)

        if (size($sl)>0) print("Selected object type: "+ nodeType($sl[0]) +"...\n" );
        else print("A transform with no shape (empty group) is selected\n");
    }
    else print("Nothing selected!\n");
}


The above code works both if a transform or a shape node is selected (it's also aware of intermediate
objects, see below); you probably won't need more serious checkings for this than the code above.

By the way, there's a separate section below on how to handle selection lists-n'stuff.





Intermediate Objects
--------------------


As mentioned in the above section, a transform node can have multiple shapes under it. This is
definitely always the case objects with deformers applied.

When you apply a deformer to an object, Maya builds the object's history in a way that the 'original'
geometry shape (before deformation) will be preserved -- instead of a single shape, there will be
two shapes under the object's transform: a 'final' shape with the deformation (what you see in the
viewport), and an 'intermediate' shape containing the geometry before any deformations applied.
(The default shortcut CONTROL+I is for cycling between intermediate shapes on an object).

This is very useful for the user, as (s)he can always go back 'before' the deformation and apply
changes to the original geometry. Even the history can be deleted from such an intermediate object
(it will delete only the previous history, but not the deformations -- can be useful if you skinned
a character but forgot to delete the modeling history :)

Note that usually there's a single intermediate shape for all deformers on an object (not one for
each deformer). But you can freely add 'own' intermediate shapes anywhere by manipulating the graph
by hand (or by script! :)

However this feature can hide a trap for the non-aware script writer. Let's again say we want to
list the shape node of a transform by the command:


    string $shapes[]=`listRelatives -pa -s "my_transform1"`;


If our object has any deformers applied, it will return multiple names (as it will also return
the intermediate shapes' name). But again, we usually need the 'final' shape. The following
command will do the trick:


    string $shapes[]=`listRelatives -pa -s -ni "my_transform1"`;


The -ni flag means -noIntermediate (see the command man page for more details). Most listing
commands (like ls) has this very same flag, so be aware and use it.

(Just a quick side note: I don't recommend to make any assumptions about the order of the list
returned by `listRelatives`. It's NOT always the first/last returned object is the final one
y'know :)

Btw, the intermediate state of a shape node is stored in the ".intermediateObject" attribute on
the shape. But it's always safer and faster to check with a listing command with the -ni flag
(especially if you have large object lists).



(It can happen that you have an amount of unused intermediate shapes in the scene you don't
know about, especially after deleting deformers from objects. In such cases, Maya deletes only the
deformer node, but not the intermediate shape. You can get rid of them by using Optimize Scene
with the Delete Unused Deformers option turned on. Then check the script editor to see how much
nasty stuff was removed :)





Selections (or how to handle a selection list Maya-style)
---------------------------------------------------------


First a few words about the DAG node selection in Maya. While working with scene objects, usually
the objects' transform nodes get selected (for example, by clicking on an object in the viewport).
Although shapes can also be selected explicitly, users select transforms most of the time (but
not always, so be aware!).

Additionally, Maya has a strict selection scheme for DAG hierarchies: if a transform is selected,
all children are treated as selected aswell: they also get highlighted in the viewport and any
operation will be applied to them as well (but they won't be included in the selection list!).

For example, if you select an object and assign a shading group to it, the same shading group
will be assigned for the children as well. In this case, if you want to make the assignment for
the selected object only, you need to explicitly select the shape node in the outliner.

(Now we got a pretty good practical reason why to check if a node is a transform or a shape,
don't we?:)

As all the children are parented to the transform node and not the shape, if the shape is
selected, no operation will be applied to any children object.


Generally, in such rare cases when writing scripts that need to work with selections, it's a good
idea to have the script handle the selections in the Maya fashion described above, as this is
what the users got used to. Briefly:


  - multiple object selections should always be supported;
  - for selected transform(s), the operation should be applied to all chilren as well;
  - for selected shape(s), the operation should be applied to the shape only;


This can be quite messy if you try to collect the actual selection list 'manually'. Fortunately
the ls command has some quite useful flags that can help us out. Some examples:



    ls -dag -lf -ni -s -sl     (or: ls -dagObjects -leaf -noIntermediate -shapes -selection)

This command returns the shape of the selected transform, as well as all the shapes in the hierarchy
below; if a shape is selected, it returns only the shape name. It's exactly what we need!


    ls -dag -ap -tr -sl        (or: ls -dagObjects -allPaths -transforms -selection)

This command returns the selected transform(s) as well as all the child transforms under them. Could
be handy as well.


Let's see a simple example. The following code applies a 'reverse poly normal' operation on the
current selection (handled in Maya fashion off coursor! :)


    {
        string    $sl[]=`ls -dag -lf -ni -type mesh -sl`, $s;

        for($s in $sl)
            polyNormal -ch 1 -nm 0 $s;
    }


Quite short but still can handle the selection list in Maya fashion. Note that the ls command is told
to list only shape nodes of type "mesh". You can find more related examples in the coding tips section.





Exploring and modifying the scene: most frequent commands
---------------------------------------------------------


Exploration:


    ls
    --
    
    Used to get lists of specified scene elements or the current selection. It has an
    extensive set of flags so it's quite flexible. It can be used for other things as well.
    For example, to check if $object is a transform or not:
    
    if ( size(`ls -tr $object`)!=0 ) print($object+" is a transform.\n");


    listRelatives
    -------------

    Can be used to query DAG chilren/shape/parent transform nodes. Always use the -pa or -f
    flag!


    listHistory
    -----------

    To list construction history/future nodes of an object.


    listAttr
    --------

    To list attributes of an object.


    listConnections
    ---------------

    To list up/downstream (incoming/outgoing) connections of an object or an attribute.
    Some examples:

    listConnections -s 1 -d 0 group1;            list all incoming connections to group1
    listConnections -s 0 -d 1 group1.translateX;        list all outgoing connections from group1.tx

    (See the man page for more details.)


    objExists
    ---------

    To check if a node or attribute exists. Some examples:

    if ( objExists("group1") ) ... ;            check if node exists
    if ( objExists("group1.my_attribute") ) ... ;        check if attribute exists on node

    The first example is equivalent to

    if ( size(`ls "group1"`) ) ... ;            check if node exists


    nodeType
    --------
    
    To get the type of the specified node. For example:
    
    nodeType("persp")                    returns "transform"
    nodeType("perspShape")                    returns "camera"
    nodeType("initialShadingGroup")                returns "shadingEngine"


    getAttr
    -------

    To get the current value of a node attribute. Examples:

    int    $v    = getAttr("group1.visibility");
    float    $t[]  = getAttr("group1.translate");        $t[0..2]: translate X/Y/Z

    int    $v1   =`getAttr -t 3 "group1.visibility"`;    (returns attr value at frame 3)

    string    $type =`getAttr -typ "group1.translate"`;    (returns attr type)
    int    $size =`getAttr -s "mynode.my_array_attr"`;    (returns size of array attribute)




Modification:


    createNode
    ----------
    
    To create DAG/DG nodes. For example, the following code creates an empty
    polygonal object (a transform and a shape):
    
    string    $tr=`group -em -w -n "poly1"`;
    string    $sh=`createNode mesh -p $tr -n "polyShape1"`;


    group
    -----

    To group object(s) under a common parent transform, or to create an empty
    transform node (group -em).


    addAttr
    -------
    
    To create a new attribute on a node.


    setAttr
    -------

    To set the value of a node attribute (for non-numeric attributes you may need
    to explicitly specify the attribute type). Can also be used to set the locked/
    keyable states of attributes. Examples:

    setAttr "group1.tx" 0;
    setAttr -type "string" "file1.fileTexureName" "image.iff";
    
    setAttr ".visibility" 0;                clears the selected object's visibility

    setAttr -k 0 "group1.translateZ";


    connectAttr/disconnectAttr
    --------------------------
    
    To create/delete connections between node attributes.


    xform
    -----
    
    Very useful command to perform/query various transformations on objects (transform
    nodes). Has flags to work in local and world space as well.


    dgeval
    ------

    Can be used to force re-evaluation (refresh) of a node or attribute. This is a bit
    more advanced command, but can be used to 'clean the situation up' in some cases
    (especially when dealing with DG connections).

    For example, to transfer the actual geometry data from one polymesh shape to another:

    connectAttr -f "meshShape1.outMesh" "meshShape2.inMesh";
    dgeval "meshShape2.outMesh";
    disconnectAttr "meshShape1.outMesh" "meshShape2.inMesh";

    Here the dgeval command is necessary to force an evaluation of the destination mesh
    shape. There's a topic about dgeval in the coding tips section.

    
    eval, evalEcho, evalDeferred
    ----------------------------
    
    All these take a string argument, and execute it as a MEL command (can be used for
    constructing commands on-the-fly).
    
    evalEcho will also print the executed command in the Script Editor history (allowing
    the user to put it on a shelf, hint-hint :)
    
    evalDeferred executes the command the next time Maya gets into 'idle' state, ie.
    not executing anything (probably at the end of your script execution).


Other:

    size
    ----
    
    To return the size of a string (no. of characters) or any array (no. of elements
    in the array). The latter can be useful for other purposes, for example:
    
    size(`ls -sl`)            no. of selected items
    size(`ls my_object1`)        returns non-zero if 'my_object1' exists

    size(`ls -tr my_xform1`)    returns non-zero if 'my_xform1' is a transform.
                    (See the "hierarchical structure of node types"
                    section for an explanation.)

    match
    -----
    
    Very useful substring matching function. (People seems not using it very much
    although, don't know why.)


    tokenize
    --------
    
    Creates an array of string from a simple string based on a separator character
    (like words of a sentence based on the space character.)






The hierarchical structure of node types
----------------------------------------


[This can be familiar for ones who know about object-oriented programming.]

In Maya, there are different node types, and all the scenes are built from these (there's
a complete listing of all node types in the Maya Docs: Node and Attribute descriptions).
These node types are actually form a hierarchy (which you can examine by choosing
Hierarcical display of nodes in the docs).

The parent-child hierarchical relationships of nodes you can see in the docs has a similar
idea behind it like in object-oriented programming.

If a parent nodetype has child nodetype(s), it means that all the children are derived
from the parent type. All the children have all the same attributes as the parent -- but
have additional attributes as well, specific to each child.

This is very logical (and practical as we'll see) as this is an easy way to deal with
node types of similar functionality.

A good example is the transform node. If you check the docs, the transform node has
many children (like place3dTexture, ikHandle, joint, lodGroup) -- these are basically
transform nodes, but all with additional attributes and functionality.

In the node type hierarchy, there are regular types, and _abstract_ types (like 'entity'
for example). Abstract nodes can't be created, they just exist to provide a parent class
for a bunch of child nodes.

The practical usefulness of this is that for all commands you may specify a node type,
you can use any type (even abstract ones) and enjoy the effects of the hierarchical
structure:


    ls -type surfaceShape        lists all nodes of type surfaceShape (including
                    nurbsSurface, mesh, subdiv nodes)


    ls -type field            lists all field nodes (gravity, turbulence, etc.)



As you can see, specifying a parent nodetype will include all child node types as well.
(Actually, all the above examples use abstract types.)

There are many cases where you can take advantage of the hierarchical node type structure,
and end up shorter and cleaner code.




Let's see a practical example (actually, a very common one), we want to decide if our
node ($node) is a transform or not.

We could write the following:


    if ( nodeType($node)=="transform" ) print($node+" is a transform\n");


[The above expression returns true if the _exact_ node type of $node is "transform",
and false if not. Please note that you also need to first check if $node exist at all,
otherwise your script execution may break.]


This will definitely work for transform nodes. But if you check the hierarchical node
list in the Maya docs, you'll see that there are a plenty of child nodes exist below
transform (such as fields, joint, etc. which have the same attributes as the regular
transform node, along with additional ones.)

So the above line will work only for transforms, but not for those others which are
fundamentally transforms as well.

Below some better examples:


    if ( size(`ls -type transform $node`)>0 ) ... ;
    if ( size(`ls -tr $node`)>0 )  ... ;

    or even shorter:

    if (size(`ls -type transform $node`)) ... ;
    if (size(`ls -tr $node`))  ... ;


[The above expressions return true if $node exists and is a transform, and return false
if $node is not a transform or it doesn't exist.]


These will always give the results we'd expect. An additional advantage of these latter
methods are that you don't have to check first if $node actually exist -- when using
nodeType() you need to do that beforehand, or your script can stop with an error if you
give a non-existing node name to nodeType().






Some quick tips on coding style
===============================



Different calling formats of MEL commands:
-----------------------------------------


There are different formats for calling MEL commands: using the 'reverse aphostrophe'
character (` -- backquote) or using 'C-style' function call format (or you can mix both).


An example of C-style calling:

        string    $na=getAttr("persp.translateX");


An example of `` calling:

        string    $na=`getAttr "persp.translateX"`;


A mix of both:

        string    $na=`getAttr("persp.translateX")`;


It's simply a matter of personal preference or taste what you like to use, but it's
recommended to maintain a consistent style throughout your scripts (just for aesthetical
reasons :)

[I myself prefer using C-style calling if possible, and using backquoting only for commands
where flags need to be specified. I prefer it as the resulting script will be more similar
to C code. ;]


The following commands are also equivalent:


        string    $sel[]=`ls -sl -tr`;

        string    $sel[]=ls("-sl","-tr");


Use whatever you like ;)






Passing expressions as MEL command arguments:
--------------------------------------------


This is an important one. If you pass expressions as arguments for a MEL command, you always
need to put them into parentheses, otherwise a syntax error will occur (at least as my
experience goes). Some examples:


    Baddie                    Goodie
    ------                    ------
    
    -                    `ls $my_object`

    `ls $my_prefix+"_"+$my_object`        `ls ($my_prefix+"_"+$my_object)`

    `getAttr $my_xform+".translateX"`    `getAttr ($my_xform+".translateX")`

    -                    `ls $obj[0] $obj[1]`

    `ls $pre[0]+$obj[0] $pre[1]+$obj[1]`    `ls ($pre[0]+$obj[0]) ($pre[1]+$obj[1])`



    

Script and global proc naming:
-----------------------------


You should design your script in a way that preferably your script should have one
global proc, and it should have the same name as the script file name (not counting the ".mel"
extension). This way you don't have to explicitly source your script before using.

(See "Recommended structure of a script" section for more details.)




Be aware of string / string[] command results:
---------------------------------------------


Some commands return a single string result, while others return string arrays (string[]).
You should always be aware of this (and check the command docs beforehand to know), as it
can break your script easily.


    string    $na=`ls -sl`;            This will break (ls returns a string array).

    string    $na[]=`ls -sl`;            This is correct.


But also note that in MEL syntax, once a variable is declared as an array, you don't need
(==shouldn't) use the [] brackets anymore. So:

    {
        string    $na[];
        $na=`ls -sl`;            This is correct.
    }

    
    {
        string    $na[];
        $na[]=`ls -sl`;            This is not.
    }




Assignments and conditionals differences (just like in C)
---------------------------------------------------------


[Everyone with just a little C experience can bravely skip this. ;]

These things work exactly like in C (and you have to watch out exactly for the same things
as well :)

An assignment is like:


    $a=3        where you simply assign a value to a variable.
            when using this in an expression, it's like if you simply use the
            assignment value (3).

    $a==3        a conditional check (if $a is 3 or not). results in 1 if true,
            0 (zero) otherwise.


Now what you have to always watch out (just like in C) is the difference between these two.
Beginners may seem the following code differently than it really is:


    {
        int $a=3;
        if ($a=5) print("hey man, 'a' equals five!");    // but no it doesn't!
    }


If you're surprised why the above code 'believes' that $a equals five (it really doesn't),
check the conditional expression more carefully. It's not a conditional, but an assignment!

What it really does is, it's first simply performs the $a=5 assignment (puts 5 in $a), then
check the assignment value (5) against zero. In Maya (and C) non-zero values mean TRUE, so
it executes the print() command.

Some examples:

        if (1) ... ;            always true
        if (1976) ... ;

        if (0) ... ;            always false

        if ($a)                true if $a is non-zero, false otherwise
        if (!$a)            reverse of the above


When doing conditionals, they return 1 for 'true' and 0 for 'false'.


        $a=( 3==3 );            $a will be 1
        $a=( 3==5 );            $a will be 0



This is what you should watch out for. But there also a bright side: by taking advantage of
this kind of expression evaluation, we can write much shorter code. Another example:


    {
        string    $sel[];

        if ( size($sel=`ls -sl`)>0 )
        {
            print("The current selection is:\n");
            print($sel);
        }
    }


In the above code, the 'if' line first puts the current selection list into the $sel[] array,
then checks if it has more than zero elements, and if so it prints them. Another common example
could be:

    {
        if ( ($fh=fopen("my_file","r") ) != 0 )
        {
            // read the file here

            fclose($fh);
        }
    }


I won't explain this, instead think about it yourself. :) A shorter equivalent of the 'if' line
is:

        if ( $fh=fopen("my_file","r") )
        ...


(Consult some C manuals and code examples if it's still not clear or need to familiarize yourself
with this.)





The ?: expression
-----------------


Just to quickly mention another useful expression borrowed from C. I won't get into much detail
(by now you should already got yourself a C book! :), instead some examples:


    int    $b=( $a!=0 ? 3 : 5 );            the value of $b will be
                            - 3 if $a is not zero,
                            - 5 if $a is zero

    string    $s=( $a<6 ? "smaller" : "larger" );    the $s string will be "smaller" if
                            $a<6, or "larger" otherwise.

The general form of this expression is:

    ( <conditional> ? <true-expr> : <false-expr> )


The above returns the value of <true-expr> if <conditional> is true, and <false-expr>
otherwise.




Global variables
----------------


Avoid them if possible. :)

But really, pass all your variable-stuff through procedure arguments instead.

They should be avoided at all costs if you plan to use them for passing data between procedures
of a single script.




Other coding tips'n'techniques
==============================



Using `ls` for filtering and safe query
---------------------------------------


- $sl=`ls -tr $sl`;
- size(`ls -tr ...`)




Proper Maya-style selection handling
------------------------------------


See the "Selections (or how to handle a selection list Maya-style)" section above on
more about this very important issue.




Setting attributes '.ma' style
------------------------------


A quick way to change more attributes on the same node is the same way Maya does it
in ascii scene (.ma) files:

    {
        select -r my_node1;        // note the -r (replace selection)
        setAttr .my_attr1 0;
        setAttr .my_attr2 1;
        // ...
    }


If you specify the attribute name the above form for setAttr (no node name, just "dot
attribute"), it will set the specified attribute on the selected node. (But don't forget
to select the node first!)



Protecting execution flow with catch()
--------------------------------------


Sometimes you need to issue a command you know beforehand it may fail, but you still
need to do it (and you don't want your script to be stopped by an error message). This
is what the catch() command for (note the back-quotes):

    catch(`delete my_node`)


This will execute the command, and returns 1 if an error occured, 0 otherwise (if I
remember correctly ;^) Regardless, script execution won't stop.


[A practical (but a bit non-standard) example. There's a bug in Maya 4.0 in the
connectAttr -f command. If you try to connect to an attribute which has a connection
already, you given a warning message. But (although it's just a warning) it stops
script execution. Luckily we have catch(). So instead of using

    connectAttr -f node1.attr1 node2.already_connected_attr2;

    I always use

    catch(`connectAttr -f node1.attr1 node2.already_connected_attr2`);

]



Running in UI/batch mode
------------------------


Maya can be run in interactive (UI) and in batch mode. The UI mode is the usual way
people use it when doing 'regular' work (modeling, animating, or anything interactive).

Batch mode is more useful for TDs and technical people. In this mode, no windows will
be opened, but all Maya functionality (except UI) is available -- scenes can be loaded
or saved, scripts can be run, etc. Ideal for batch-processing scenes with custom
scripts, and the like.


You can call Maya in batch mode by entering

    maya -batch [-prompt]        on unix/linux, or
    mayabatch [-prompt]        on nt


(If you specify the -prompt flag, Maya will open a command prompt in the current console,
and you can enter commands, etc. Check the manuals (or type maya -help) for more on this.)

Anyway, your script may needs to know if Maya is in batch or UI mode. This can be queryed
by the following command:

    `about -b`            this will return 1 for batch and 0 for UI mode.





Using dgeval()
--------------


[This is a somewhat 'advanced' topic. Altough I try to be clear as possible, I assume the
reader also has a clear understanding of the working details of the Dependency Graph.]

The dgeval command can be used to directly force the evaluation of a specified node
attribute. Usually there's no need for it (Maya always evaluates what's needed -- or let's
say usually, khm :) but there can be situations where it comes handy.

An example: you have two mesh shapes, and you want to copy the exact mesh data from one
to the other. An obvious solution is to connect one mesh shape's 'outMesh' attribute to
the other's 'inMesh', then disconnect it:

    
    connectAttr meshShape1.outMesh meshShape2.inMesh;
    disconnectAttr meshShape1.outMesh meshShape2.inMesh;

    
If you try this through the Script Editor, after issuing the first command you'll see that
the second mesh shape became identical to the first. Then you disconnect the attributes
(we like to get rid of loose ends, aren't we?:)

However, if you try it in a script this will not work at all (or even in the Script Editor,
if you issue the two commands in one execution.) The reason is that although Maya performs
both the connect and the disconnect operations, the data simply won't 'get through' -- to
be so, the destination mesh shape would need an evaluation (which usually happens when Maya
refreshes the viewport).

So this is one case we need to force an evaluation ourselves. Insert the following line
between the above first and the second lines:


    dgeval meshShape2.outMesh;

    
This will evaluate the destination mesh's outMesh attribute after the connection is made,
giving us the result we expected (to put it another way, it's if like meshShape1 would be
in the history of meshShape2).









Writing complete plug-in modules
================================


Once you reached so far you're a scripting pro and don't have anything else to know about
MEL scripting, you may start thinking about trying writing API/C++ plug-ins. I recommend
you to begin at once with no hesitation -- Maya has a rich API which is so easy to use
that it takes not much more than a MEL script (ok, it's a bit more complex, but still,
nothing psychedelic, or what :)



Using MEL and API together for most efficiency
----------------------------------------------


Once you gave API a few tries, you will see that

    - it's much more capable,
    - much faster,
    - a lot of things are much more hassle than in MEL, goddamit!!! :)


The last one is the price of more flexibility. API code can be much larger than the
equivalent MEL code (IMHO it's also more difficult to debug). If it comes to usual scene
management (node/connection creations, etc.) MEL can be much comfortable -- just try to
write the equivalent of a simple connectAttr in API and you'll know what I'm talking about ;)

As far as my experience goes, the most effective way of developing for Maya (optimal in both
development time and execution speed) is using a MEL/API hybrid code, where the application
'core' is in API, and there's a MEL 'shell' around it (and the MEL code is calling the API
routines through custom MEL commands defined by the API part).




What is a module
----------------


A module is a collection of MEL script(s) and plug-in shared object file(s) placed under a
pre-defined directory structure. To put it in another way, a module is like an 'extension'
to Maya which provides it's functionality through it's script(s) and/or plug-in(s).

A module can consist of script(s), plug-in(s) or both, but none of them is mandatory (it
can be scripts and/or plug-ins only). Also, a module is just a convention -- it's the same
if all scripts were copied to the scripts/ folder and all plug-ins were in the plug-ins/
folder. The advantage of modules is that you can keep all related things in one place.

[For example, you can create a module out of scripts only -- it's one good way to have a
common place for scripts written by the same person. For instance, I myself have a Billauch
and an Ewert module (just to have a little free embedded advertisement for some guys :) ]


A module consists of two parts: a module definition (a single line text file) and a disk
directory containing the module folders.

The directory structure of a module looks the following way:


    <module root dir>
    |
    +- icons/        (shelf icons)
    |
    +- plug-ins/        (place all .so/.mll files here)
    |
    +- scripts/        (place all .mel scripts here)
    |
    +- ...            (optional other folders, like docs, bin, etc.)


All the above folders should have the exact specified name; but all of them are optional
(ie. doesn't need to exist).


The module definition file is a text file containing a single line in the following format:


+ <module_name> <module_version> <module_root_path>


For example:


+ xyMayaTools 1.0 T:/!share/maya/4.0/modules/xyMayaTools


The 'plus' is mandatory and should be the first character. All arguments should be separated
by space(s).


Maya looks for the module definition files (the text files) in the directory specified by
the MAYA_MODULE_PATH environment variable.