Mel

Getting Started with Cutter


main index



Introduction

The purpose of this tutorial is to help you get started with Mel scripting. The tutorial concentrates on the use of,

  • Cutter to execute scripts in Maya,
  • "for" loops,
  • "if" tests,
  • object instancing,
  • creating custom procedures, and
  • the scripting of a simple interface.

Executing a Mel Script with Cutter

Cutter can be of assistance in writing and running Mel scripts. The tutorial,
    "Cutter: Integration with Mel",
provides a full description of the facilities offered by Cutter in regard to Mel scripting. The code shown in listing 1 will be used as an example of how to use Cutter with Maya.


Step 1

After launching Maya enter the following command into the Mel text field shown in figure 1.
    commandPort -n ":2222";


Figure 1


The command can be "executed" by pressing the enter key on the numeric keypad. The command tells Maya to "listen" on port 2222 for data sent to it by an external application.

Readers who are using the Vista operating system should refer to the notes, "commandPort and Vista", in the tutorial Cutter: Integration with Mel.


Step 2

After launching Cutter, described in the tutorial Cutter: Installation, copy and paste the text from listing 1 into a new window. Open the network panel by clicking on the network icon located in the top right hand corner of the window - figure 2. Ensure the IP fields display the following text,
    localhost:2222


Figure 2


Save the document as listing1.mel - note the .mel file extension. The first Mel script that is either saved or opened causees Cutter to automatically read documentation about Maya's library of Mel commands. Command documentation is read only once per session. A progress window, figure 3, will be displayed for the few seconds it takes Cutter to read the documentation.


Figure 3


Step 3

Upon clicking the checkbox labeled Connect, refer to figure 2, you should see an error message displayed in the lower right hand corner of the Maya window - figure 4.


Figure 4

The message is caused by Cutter sending an initial message to Maya. The error is benign and serves only as a visual confirmation that Cutter and Maya are "connected" on port 2222. Unless you are using a computer with dual monitors you will find it helpful to resize the Cutter and Maya windows so that they "share" the desktop, figure 5.


Figure 5


Step 4

You are now ready to run your Mel script directly from Cutter. Hide the network panel by clicking the network icon. The keyboard short-cuts Alt + e or Control + e can be used to send the script to Maya via port 2222. In the Maya window you should see the graphics shown in figure 6.

Listing 1 - grid1d.mel


select -all;
delete;
  
for($x = 0; $x < 8; $x++) {
    sphere -r 0.25;
    move 0 (0.5 * $x) 0;
    }


Figure 6



Listing 2 demonstrates how nested loops can be used to produce a 2D array of spheres..

Listing 2 - grid2d.mel


select -all;
delete;
  
for($x = 0; $x < 8; $x++) {
    for($y = 0; $y < 8; $y++) {
        sphere -r 0.25;
        move (0.5 * $x) (0.5 * $y) 0;
        }
    }


Figure 7



Nesting a third loop (see listing 4) produces a 3D array of spheres - figure 8.



Figure 8


Holes / Voids

Listing 3 demonstrates the use of a "if" test to determine when spheres should not be inserted.


Listing 3 - holes2d.mel


select -all;
delete;
  
for($x = 0; $x < 8; $x++) {
   for($y = 0; $y < 8; $y++) {
      if($x > 2 && $x < 5 || 
         $y > 2 && $y < 5) 
           {
           // leave a "hole"
           }
       else
           {
           sphere -r 0.25;
           move (0.5 * $x) 
                (0.5 * $y) 
                 0;
           }
       }
   }


Figure 9


Changing the "AND" (&&) and the "OR" (||) of the test produces very different gaps within the 2D array of spheres. Moving the code that inserts the spheres into the "empty" block produces the "reverse" of the pattern. For example,


if($x > 2 && $x < 5 || 
   $y > 2 && $y < 5) 
    {
    sphere -r 0.25;
    move (0.5 * $x) 
         (0.5 * $y) 
          0;
    }
else
    {
    // leave a "hole"
    }


Figure 10


In listing 4 the loops have been changed so that a sequence of poly cubes are inserted into the scene in 'Z' (depth) order. The inner 'Y' loop has also been modified so as to produce the wedge effect shown in figure 11.


Listing 4 - wedge.mel


select -all;
delete;
  
for($z = 0; $z < 8; $z++) {
   for($y = 0; $y < 8 - $z; $y++) {
      for($x = 0; $x < 8; $x++) {
         if($x > 2 && $x < 5 || 
            $y > 2 && $y < 5) 
            {
            polyCube -w 0.35 
                     -h 0.35 
                     -d 0.35;
            move (0.5 * $x) 
                 (0.5 * $y) 
                 (0.5 * $z);
            }
         else
            /* leave a "hole" */ ;
         }
      }
   }


Figure 11



Object Instancing

In terms of memory useage, the Mel code shown above can be improved by the use of object instancing. For example, listings 5 shows how multiple instances of a single "master" sphere can be used to create the same vertical column of spheres as listing 1.


Listing 5


// Create a "master" object
string $master[] = `sphere -r 0.25`;
  
string $inst[];
for($x = 0; $x < 8; $x++) {
    $inst = `instance $master`;
    xform -t 0 (0.5 * $x) 0 $inst;
    }
  
// Switch off the visibility of the master.
// Note: visibility is an attribute of the
// transform node.
setAttr ($master[0] + ".visibility") 0; 
  
// Change the size of the master will effect
// all the instances.
// Note: radius is an attribute of the "input"
setAttr ($master[1] + ".radius") .2; 

Note that using the setAttr command to change the size of the master sphere will also effect the instances. To avoid rendering artifacts, caused by the position of the surface of the master sphere coinciding with that of the first instance, it is adviseable to switch off the visibility of the master object.


Converting to a Custom Proc

Listing 6 shows the previous code "packaged" as a custom Mel procedure.


Listing 6


global proc string[] stackOfSpheres(int $num, int $spacing, float $rad)
{
// Create a "master" object
string $master[] = `sphere -r $rad`;
  
string $inst[];
for($x = 0; $x < $num; $x++) {
    $inst = `instance $master`;
    xform -t 0 ($spacing * $x) 0 $inst;
    }
setAttr ($master[0] + ".visibility") 0; 
return $master;
}
  
string $obj[] = stackOfSpheres(6, 1, 0.25);
//setAttr ($obj[1] + ".radius") .72; 

A more useful version of the proc is shown in the next listing. Note the proc can accept any shape as an input. $master has been declared as a global variable so that it can be referenced by an external Mel script.


Listing 7


global proc stackOfShapes(string $master[], 
                             int $num, 
                             int $spacing)
{
string $inst[];
for($x = 0; $x < $num; $x++) {
    $inst = `instance $master`;
    xform -t 0 ($spacing * $x) 0 $inst;
    }
setAttr ($master[0] + ".visibility") 0; 
}
//---------------------------------------
  
global string $cones[];
  
$cones = `cone -r 0.5`;
stackOfShapes($cones, 6, 1);
  
setAttr ($cones[1] + ".radius") 1.2; 


Figure 12




Adding a User Interface

This section of the tutorial shows how to make a simple user interface (figure 13) that enables a user to create a random grid of cones - all of which can be resized as a result of a slider modifying the master object from which the cones were instanced.



Figure 13


Listings 8 and 9 show two scripts that work in tandem. The first script, randomConesUI.mel, isolates the user-interface code that is responsible for creating a window containing a button and a slider.


Listing 8 (randomConesUI.mel)


// Reference the stackOfShapes.mel script so that its procs
// can be use called from this script.
source "PATH_TO/randomCones.mel";
  
// The master object must be global so the procs in
// the sourced file can access it.
global string $master[];
  
proc randomConesUI()
{
// Inform this proc it will be accessing an external
// variable.
global string $master[];
  
// Create the master object immediately before opening
// the user interface. Hide the master object from the user.
$master = `cone -r 0.5 -axis 0 1 0`;
setAttr ($master[0] + ".visibility") 0; 
  
  
string $winName = `window -title "randomConesUI"`;
columnLayout;
button     -label "make grid"
        -command "randomCones 30";
  
attrFieldSliderGrp -label "size"
                    -min 0.005
                    -max 10.0
                    -at ($master[1] + ".radius"); 
showWindow $winName;
  
// Resize the window
window -edit -widthHeight 450 100 $winName;
}

The second script, randomCones.mel, isolates the code that instances the master cone created by the other script. The procs randomConesUI and randomCones could have been included in a single script. However, it is good practice to keep UI code in a separate file. The scripts also demonstrate the use of a global variable named $master.


Listing 9 (randomConesUI.mel)


global proc randomCones(int $num)
{
// Inform this proc it will be accessing 
// an external variable.
global string $master[];
  
string $inst[];
  
for($n = 0; $n < $num; $n++) {
    $inst = `instance $master`;
    xform -t (rand(-5, 5)) 
             (rand( 0,10)) 
             (rand(-5, 5)) $inst;
    setAttr ($inst[0] + ".visibility") 1;
    }
}



© 2002- Malcolm Kesson. All rights reserved.