Saturday, March 20, 2010

MEL Scripting Tutorial: Using The Tokenize Command

This MEL scripting tutorial will give you an in-depth look into how to use the tokenize command. Tokenize is used for taking a string and breaking it up into a number of parts so they may be used on their own. It's one of the most commonly used commands in the language and a strong knowledge of how to use it will help anyone write better procedures. This tutorial assumes a basic knowledge of running a MEL script in Maya.

Skill Level:
Beginner

The tokenize MEL command is incredibly useful and very easy to use. You'll find once you know how it works and what it does, you'll end up using it in almost every script you write in one way or another. What tokenize does is essentially break a string up into different parts based on a splitter character, and return them into an empty string array. Why would you need to break up a string into different parts? There are many reasons you may need to do this. An example being let's say you're string is an Object.Attr and you need to parse out just the attribute, or perhaps just the object. You can use tokenize to split them up into their own strings. Maybe you have a path to a file and you want to tokenize it by slashes. Or, you have a comma-separated line of text and you want to split each field into an array. There are countless ways to use it and I think you'll find once you know how to use it, you'll use it constantly. Let's look at some examples:

"myObject.translateX" would tokenize into "myObject" and "translateX". Here's how we would do that:

// Here is our string, in the variable $myString
string $myString = "myObject.translateX" ;

// First we create an empty string array, to play our "parts" in.
string $buffer[] ;

// Now we'll tokenize our string and put the tokens into $buffer
tokenize $myString "." $buffer ;

// Notice the arguments: string, splitter, array.

// Now if we print out $buffer we'll get:
print $buffer ;
// Result:
// myObject
// translateX

Note: Because we used the "." as the splitter, it is now gone from any return strings.

Besides just splitting a string up into different parts, tokenize will also return the number of parts your string was split up into. This can be useful for many reasons as well. Let's say you have a string that looks like this: "myObject,myAttribute,myValue,myMultiplier,myComment". This could be a comma-separated text file you're reading from, or data from a database table. So now for whatever reason let's say we want to know how many comma-separated fields are in this line of text. We can use tokenize for that as well.

// This is the original string
string $myLine = "myObject,myAttribute,myValue,myMultiplier,myComment" ;

// Tokenize it, but save the return of tokenize into an INT variable
string $buffer[] ;
int $numTokens = `tokenize $myLine "," $buffer` ;

print $numTokens ;
// Result: 5

print $buffer ;
// Result:
// myObject
// myAttribute
// myValue
// myMultiplier
// myComment

Now we have both our tokens saved into $buffer and the number of tokens saved into $numTokens. Now that you've got your 5 fields into your $buffer array, you can now define them properly.

// Define Vars
string $object = $buffer[0] ;
string $attribute = $buffer[1] ;
string $value = $buffer[2] ;
string $multiplier = $buffer[3] ;
string $comment = $buffer[4] ;

Note: Remember MEL arrays are "zero-based" so the first index of $buffer is going to be zero.

You may notice that I've defined $value and $multiplier as strings in the above example. Since they are coming from a comma-separated string and then tokenized into a string array they are technically strings. However, since they are most likely floats I can define them as such when I take them out of $buffer. You will get a Warning in Maya when you do this, but it won't stop your script. Same example but with redefined vars below:

// Define Vars, But Assign To Float
string $object = $buffer[0] ;
string $attribute = $buffer[1] ;
float $value = $buffer[2] ;
float $multiplier = $buffer[3] ;
string $comment = $buffer[4] ;

Now we've got five values that we took from our comma-separated text line, all in their own variables with no commas. Very useful! Now, what if we need to save this data out again? How do you form a line again with all these variables? The practical way would be to do this:

// Redefine CSV String
string $myNewLine = ($object+","+$attribute+","+$value+","+$multiplier+","+$comment) ;

This will put all your variables into a new, single string variable. But, if you've done this sort of thing you may have noticed that this will produce an error in MEL, because we're attempting to concatenate two different types of variables a string. In MEL, we can't combine floats and strings together into a string. (Thank you Python!) So, the alternative is to just use our original string array from your tokenize, along with the MEL command: stringArrayToString.

// Redefine CSV String
string $newLine = stringArrayToString($buffer, ",") ;

That will reassemble our string array into a single line again, and use the comma as the splitter. You can use anything you want as a splitter as long as it's a valid character.

Play around with tokenize a bit and see what you can come up with. It's one of the MEL commands I use the most and getting comfortable with how it works and how to use it will improve your scripting a lot!

Looking for more tutorials? Check out Script Swell's Technical Artist Tutorials page.

Friday, March 19, 2010

Python: Learn Python via Google

Google has put up a free video collection of Python classes. Another great resource on the web for learning how to code in the coolest language ever. :)

From Google Code:
"Welcome to Google's Python Class -- this is a free class for people with a little bit of programming experience who want to learn Python. The class includes written materials, lecture videos, and lots of code exercises to practice Python coding. These materials are used within Google to introduce Python to people who have just a little programming experience. The first exercises work on basic Python concepts like strings and lists, building up to the later exercises which are full programs dealing with text files, processes, and http connections. The class is geared for people who have a little bit of programming experience in some language, enough to know what a "variable" or "if statement" is. Beyond that, you do not need to be an expert programmer to use this material."
Very cool if you're looking to learn some Python and have been waiting for an opportunity.

http://code.google.com/edu/languages/google-python-class/

Wednesday, March 17, 2010

Happy St. Patricks Day From ScriptSwell!

Here's some St. Patrick's Day code for you. :)


global proc jgGreenBeerForTheWin () {
if(`date -sd` == "03/17") {
print "Happy St. Patrick's Day!" ;
textCurves -ch 0 -f "Times New Roman|h-13|w400|c0" -t "Happy St. Patrick's Day From ScriptSwell.net!";
}
}
jgGreenBeerForTheWin ;


"May your pockets be heavy and your heart be light,
May good luck pursue you each morning and night."

Monday, March 15, 2010

MEL: setAttr On All Translates And Rotates Using T and R

This is a really simple MEL trick, but one that's pretty rad if you didn't know about it before. It can save you time and precious lines of code.

You can set all three translates or rotates each with a single line of code.

Instead of this:

// Set Translates
setAttr myObject.translateX 10 ;
setAttr myObject.translateY 10 ;
setAttr myObject.translateZ 10 ;

// Set Rotates
setAttr myObject.rotateX 180 ;
setAttr myObject.rotateY 180 ;
setAttr myObject.rotateZ 180 ;

You can combine it all into just two lines, using .t and .r

// Set Translates
setAttr myObject.t 10 10 10 ;

// Set Rotates
setAttr myObject.r 180 180 180 ;

If you're really looking for some fun, try doing it all in one line using the .worldMatrix attribute. :)

Wednesday, March 10, 2010

Game Developers Conference 2010, San Francisco, CA

I'm off to San Francisco for GDC! This years Game Developers Conference should be an awesome time. Lots of great people, great stuff to see, beer to drink and hotel lobby's to pass out in. Hope to see everyone there and if you're in town give me a shout! I'm not going to post my phone number but anyone internet savvy can find it easily if they need it. If you're not so savvy, comment here or toss me an email.

Our company Image Metrics is hosting a shindig tomorrow night at the W Hotel across from the Moscone Center. More information here: http://gdconf.com/events/partiesnetworking.html

And of course, for more information on GDC check out: http://www.gdconf.com

Huzzah! See you there!

Friday, March 5, 2010

Efficient Scripting In MEL - Part II

TL;DR - Make your own MEL procedures that are better versions of existing commands if it will speed up your workflow. Some of the default MEL commands are lacking.

This next article is a bit more about scripting and programming in general and less about MEL specifically. It's about using procedures to take care of basic functions in order to speed up tasks you perform regularly in your scripts.

Let say for example you want to print out some data. In MEL and most languages I've programmed with, when you print or echo something it will not create a new line the next time you print. So if you print the phrase "Hello World" twice in a row, it will look like this: "HelloWorldHelloWorld".

So, a quick and easy way to do this is to add a newline. To do this in MEL, you use the characters "\n". So your print statement looks like this:

print "Hello World\n" ;
print "Hello World\n" ;
// Result:
// Hello World
// Hello World
//
// Without \n you'll get:
// Result: HelloWorldHelloWorld

When you need to add a newline character onto every single thing you print it can get time-consuming. Especially when you need to concatenate it with a variable which in turn you then need to add parenthesis around your entire statement, it's very annoying. We know every millisecond counts right? So let's save ourselves some time and build a custom print statement.

// **********************************************************
// Faster way to print
global proc jgPrint (string $print) {
print ($print+"\n") ;
}

// Now I can simply call:
jgPrint "Hello World!" ;

// And it adds the newline for me.

Slightly unrelated, but another benefit to this method is that you can print a concatenated int or float with a string argument using this method which you can't with a normal print statement.

So there we've created a small procedure that will save us lots of time down the road. Essentially we're creating tools to help perform tasks faster than we could without them, and filling in gaps with tools that the default language doesn't provide or where the default commands are lacking in one way or another.

There are an infinite number of ways you can use these. I'm not going to provide all the code for you since it's a good learning experience to write these on your own...but here are a couple examples of some procs I use on a daily basis.
  • Append A Single String Argument To A String Array
  • Read A Text File And Return It As A String Array
  • Create A Set Driven Key
  • Make An Attribute On An Object
  • Edit An Attribute's Values
  • Pass An Array Into A String Argument
These are all things that already have built-in Maya commands but are either unintuitive to use or are just plain annoying and slow. There are no rules that say you need to do it the "right" way or the way Maya wants you to. If you can get the job done faster by making your own commands or procedures than go for it!

Thursday, March 4, 2010

Efficient Scripting In MEL - Part I

TL;DR - The less lines in your code the better, and use argument passing tricks whenever possible. Keep it clean!

After talking to a coworker today about how it'd be cool to try and rewrite a script he made in only ten lines of code...I thought it'd be good to to write a short post about how to increase efficiency in your MEL scripting. By efficiency, I mean saving yourself time both in the creation and the editing of your code by condensing your scripts into shorter blocks and not using more lines than you need to. I've found that it helps me a lot in the long term when my scripts are much shorter and easier to edit. On top of that, it's just way more awesome to write a script in ten lines that does the exact same thing as one that does it in fifty.

We'll start with some simple examples:

Let's start with a FOR Loop. Normally you'd setup a FOR loop like this:

for($i = 0; $i < 10; $i++) {
print $i ;
}

Or like this..


for($item in $myArray){
print $item ;
}

But why not condense it? Let's put that three lines of code into one.


for($item in $myArray) print $item

It doesn't seem like much of a gain, but in actuality you've cut the size by 66%.

Now let's add an IF statement to this line..


for($item in $myArray) if($item == "myItem") print $item

Still one line of code but you've got another check in there.

Another example, check if an object exists, error out if not.

int $exists = `objExists $myObject` ;
if(!$exists) {
error "Object doesn't exist." ;
}

// Four lines? Bleh. Let's bring it down a few..

if(!`objExists $myObject`) error "Object doesn't exist." ;

Some more error check examples:

if(!`filetest -f $pathToFile`) error "File does not exist." ;
//
if(!`endsWith $mayaFile ".mb"` && !`endsWith $mayaFile ".ma"`) error "File is not a Maya file." ;
//

This helps a lot when you've got a procedure that has a lot of error checking. Don't allow your checks to steal all of your screen real estate. Condense them into one line and celebrate your awesomeness with a frosty pint.

What about when you need to get some data from a textField or any other control? Assuming it returns a string, and not a string array. You condense the following:

string $myText = `textField -query -text myTextField` ;
print $myText ;

// Pffffff.

print `textField -query -text myTextField` ;

// Much better!

So this is a short, basic look into how to do this sort of stuff. I find that although making your code cleaner and nicer is probably in the top ten nerdiest things possible, you'll appreciate it someday when you look step back and look at your script and actually smile at how cool it looks. It is after all an art form. :)

Wednesday, March 3, 2010

PYMEL: "from pymel import *" Not Working?

If you're trying to get Pymel installed on your machine and it seems to be working but nothing happens when you import the modules into Maya, read on.

The Pymel documentation currently tells you to type:

from pymel import *

But the latest release (1.0.0.rc1) will not work with this method on a new installation. We've tested it on about a dozen machines and so far all of them require a slight change, which is to type the following:


from pymel.core import *

There may be a simple work-around for this, but if you follow the installation instructions from scratch on a machine with no previous installs, you will need to do this to get the pymel modules to import into Maya.

What is Pymel? From the site:

Python in Maya Done Right

PyMEL makes python scripting with Maya work the way it should. Maya's command module is a direct translation of mel commands into python commands. The result is a very awkward and unpythonic syntax which does not take advantage of python's strengths -- particulary, a flexible, object-oriented design. PyMEL builds on the cmds module by organizing many of its commands into a class hierarchy, and by customizing them to operate in a more succinct and intuitive way.

Check out the Pymel website for more information.

http://code.google.com/p/pymel/

Tuesday, March 2, 2010

MEL: Run A Command Line Program Through Maya

Ever needed to run an executable/command line app from Maya? You can use the "system" command to do this.


system("echo \"Hello World!\"") ;
// Result: "Hello World!"

So, if you need to use your maya data to run through an executable for any reason, you can use the system command the same as you would with any Maya procedure.

Random Example:

string $numVerts = "2023" ; // Number of vertices on a mesh
string $mesh = "myMesh" ; // Mesh Name
system("C:/myAppDirectory/mySpecialVertApp "+$numVerts+" "+$mesh) ;

This allows you to gather data in a Maya scene and send it directly to your executable without needing to save it out first. It's a bit slower than running it normally, but barely noticeable.

Scripting Topics

MEL (41) Maya (39) Scripting (32) Scripts (21) programming (14) Free Mel Scripts (8) MaxScript (7) Coding (6) Rigging (5) tutorial (5) 3ds Max (4) Python (4) Tricks (4) faceware (4) image metrics (4) Learn (3) Namespace (3) Namespacing (3) animation (3) facial (3) webinar (3) Code (2) GDC (2) Game Developers Conference (2) Multiple Namespaces (2) Print Selected Objects (2) Recursive (2) Removing Namespace (2) Return (2) Set Driven Keys (2) TOkenize (2) Tips (2) Toggle Background Color with MEL (2) animation tools (2) animators resource (2) deformers (2) learning (2) maya tools (2) mesh (2) modeling (2) nodes (2) procedure (2) script swell (2) transforms (2) Animschool (1) Attribute (1) Background Color (1) Beer (1) Blur (1) Character Setup (1) Check if an object exists (1) Class (1) Command Line (1) Constraints (1) Create SDK (1) Create a directory with mel (1) Data (1) Export (1) FilterString (1) Fix (1) Floating Slider Time (1) Functions (1) Get Maya Version MEL (1) Get Parent (1) Google (1) Holiday (1) How To Write To A Text File (1) Import (1) Incremental Save (1) Index (1) Joint Chain (1) Make Set Driven Keys (1) Maya Version (1) Modules (1) Objects (1) Orient Constraint (1) PYMEL (1) Parent (1) Parent Constraint (1) Point Constraint (1) Position (1) Print (1) Print Current Selection (1) Print Random Quotes (1) Print Selection (1) Print Vertices (1) Progress Bar (1) Progress Window (1) PyQT (1) Removing Spaces From Names (1) Scene File Name (1) Select Connections (1) Select Outgoing Nodes (1) Split Bones (1) Split Joints (1) St. Patrick's Day (1) String Array (1) System (1) Transfer UVs (1) Viewport (1) White Space (1) Windows Username (1) Zero Out Attributes (1) animButtonState (1) arrays (1) articles (1) auto key (1) better (1) blendshapes (1) break (1) confirm dialog (1) continue (1) convention (1) e3 (1) efficiency (1) error (1) eval (1) executable (1) fclose (1) fopen (1) fprint (1) games (1) improving (1) infinite loop (1) joints (1) listHistory (1) listRelatives (1) logic (1) loops (1) milestone (1) nodeType (1) objExists (1) recursion (1) rotates (1) rotations (1) schools (1) sculpting (1) setAttr (1) shout outs (1) source (1) source a script with a variable (1) speed (1) tech-artists.org (1) translates (1) video (1) warning (1) world matrix (1) worldMatrix (1)
 
Script Swell - Blogged