Wednesday, December 8, 2010

MEL Scripting Tutorial: How To Write To A Text File

In this mel scripting tutorial we'll look at how to write data out to a file so you can make your own data exporting tools. By the end of it you'll have a firm understanding of and know how to take data from your Maya scene and write it out to a file. This is a follow up to the previous tutorial: How To Read A Text File. This article assumes you've read the previous tutorial and is a continuation of the concepts discussed in it. If you've not read the tutorial on how to read a text file, please browse Script Swell's Technical Artist Tutorials or click here.

Skill Level: Beginner to Intermediate

There are two things you'll need in order to proceed: some data to write out, and a text a file to write it to. To start off let's create a string array in Maya. In the script editor, copy and paste the following and run it.
// Create A String Array With Test Data
string $myStrArray[] = {"This is line one of my data from Maya.", "This is line two.", "And then line three!"} ;
Next, create a file in an easy location, e.g. "C:\mel\testFile_v01.txt". In this file, on the first line type the following: "This is the first line of my text file, version 01!". Save and close your file.

Now let's get into some code and see how this works. The idea here is that we're going to "open" our file as an object in Maya, put our data inside it, then "close" it. If you recall in the previous tutorial we use the fopen command to open the file and prepare it for use.
// Create A String Array With Test Data
string $myStrArray[] = {"This is line one of my data from Maya.", "This is line two.", "And then line three!"} ;

// Define Your File Path
string $filePath = "C:/mel/testFile_v01.txt" ;

// Open Your File
$fileId = `fopen $filePath "a"` ;
The first difference you may have noticed from the previous tutorial is that instead of "r" (for 'read') we've switched to "a" which stands for "append". What this means is that we're telling MEL that when we're working with this particular file, we'll be writing to it but we'll be appending data onto the end of whatever is there. This way using the "a" flag you can keep your current data intact and simply add more to it. Let's continue on with more code.
// Create A String Array With Test Data
string $myStrArray[] = {"This is line one of my data from Maya.", "This is line two.", "And then line three!"} ;

// Define Your File Path
string $filePath = "C:/mel/testFile_v01.txt" ;

// Open Your File
$fileId = `fopen $filePath "a"` ;

// Print Array To File
for($line in $myStrArray)
fprint $fileId ($line+"\n") ;

// Close File
fclose $fileId ;
The newest line in our code starts with a for in loop and is looping through our string array one line at a time. This is because in this case we don't just want to dump all the data to the file, we want to have control over how it's added and make sure it's exporting to an easily readable format. We loop through our array one line at a time and use the fprint command. What fprint does is essentially the same thing that the normal print command does except it prints to your text file instead of Maya's script editor. Just like print though, it will not add a new line at the end of the argument and this must be done manually, hence the "\n" that I've appended to the $line variable. If you leave this out, all of your data will print to a single line! After we're finished printing out the data we're using fclose to "close" our file object.

-----------------------------------------------------------
Tip: If you forget to use fclose and leave the file object "open" within Maya it will become locked and you won't be able to make changes to it outside of the program. If this happens you will need to close Maya to "unlock" your file.
-----------------------------------------------------------

After running this script, take a look at your text file and you'll see the file now reads the following:
This is the first line of my text file, version 01!
This is line one of my data from Maya.
This is line two.
And then line three!
Nicely done! You are now one step closer to building your data export tool. Next we'll double back slightly and look at another flag we can use in the fopen command. Look at the code below.
// Create A String Array With Test Data
string $myStrArray[] = {"This is line one of my data from Maya.", "This is line two.", "And then line three!"} ;

// Define Your File Path
string $filePath = "C:/mel/testFile_v01.txt" ;

// Open Your File
$fileId = `fopen $filePath "w"` ;

// Print Array To File
for($line in $myStrArray)
fprint $fileId ($line+"\n") ;

// Close File
fclose $fileId ;
The only change here is we've swapped the "a" flag with it's brother: "w" which stands for "write". What write will do is destroy the prior contents of the file and replace them with whatever we're printing to it. So your result will be:
This is line one of my data from Maya.
This is line two.
And then line three!
This time only the string array has been printed to the file and the original file's contents were destroyed.

Both of these flags are useful depending on the context of what you're doing. Be sure to check out these commands in the MEL documentation as well for a slightly more in-depth look at how they work.

It really is simple as that to write out data! As long as you remember to fclose your file and use the correct flags when using fopen you shouldn't have any trouble using this technique.

For more tutorials check out the Technical Artist Tutorials page. Leave a comment below if you have any thoughts or questions and thanks for reading!

Friday, November 19, 2010

A Few Fundamental MEL Commands Everyone Should Know

Here are a couple of useful MEL commands/operators that you'll end up using in a lot of the stuff you write.

continue ;
Skips one iteration of a loop.

break ;
Stops the loop and continues script where the loop ends.

Warning("You messed up, but it isn't too bad so I'll keep going.") ;
Warnings alert the user that something is awry, but it's not a deal breaker.

Error("You messed up bad, I need to quit now.") ;
The error command will stop a script in it's tracks, ending everything.

return ;
Ends a procedure/function/method, but not the entire script. Continues on where the procedure ends. Can also be used to return a value (return $myString).

CGSutra has a great article going into more depth on these and a lot more of the fundamental MEL concepts. Check it out here.

Wednesday, October 20, 2010

animatorsresource: New Animation School: AnimSchool

animatorsresource: New Animation School: AnimSchool: "...There is a new online animation school set to debut in November called AnimSchool. school will be run by the creator of 3D Appeal Blog and 3D Appeal Blog Now, David Gallagher."

Looks like David's school will have a, "3D Character" program that teaches modeling and rigging on top of the animation classes. Read JP's article over at Animators Resource for more info and an interview with David.

Sunday, October 17, 2010

MEL Scripting Tutorial: How To Read A Text File

This is a scripting tutorial that will show you how to use MEL to read a text file and use the data from it in Maya. As a scripter it's an essential skill to have. A lot of times there will be data you need in a file that's outside of your program and you'll need to get it. The file may contain animation data you need to access, skin weighting data, a list of objects needed in a scene or any other type of data you could imagine a text file having. This tutorial assumes you have a basic understanding of MEL syntax and how to run a script in Maya.

Skill Level: Beginner to Intermediate

First off we need a data file to test with, so let's make one. Open up your favorite text editor (notepad will work if you don't have anything else), create a new .TXT file. and save it somewhere as "myDataFile.txt". For this lesson, I recommend saving your file somewhere with a relatively simple file path, e.g. "C:\mel\myDataFile.txt".

Copy and paste the following data into your file and save:

//objectName,posX,posY,posZ,rotX,rotY,rotZ,scaleX,scaleY,scaleZ
pCube1,0,0,0,0,0,0,1,1,1
pSphere1,-5,10,-5,-90,90,-45,1,1,1
pCylinder1,2.5,2.342,4.2823,0,0,0,1,1,1
pPlane1,0,0,0,360,180,45,1,1,1

-----------------------------------------------------------
Tip: The file doesn't necessarily have to use a .TXT file extension. Any text-based file format will work the same way. You can even make up your own file extension. I like to use .RIG files.
-----------------------------------------------------------

Now that we have a file to test with let's look at what's in it. It's important to look at what type of data you're working with so you can plan on how to bring it in and in what format you'll need it to be in once it's in Maya.

In this case there are multiple lines of text, with each line having ten values separated by commas. We refer to this type of data as comma-separated. Our example has the name of an object and it's nine transform values (translate X,Y,Z, rotate X,Y,Z, scale X,Y,Z). Note that the first line doesn't have any actual values, it just serves to tell us what each value represents. This is common in data files. You may need to adjust your script to skip the first line when it brings in the data.

Using this data we're going to find the objects in our Maya scene and set their transforms. Let's get to it!

The first part of our script is putting the path to the file we just created into a string variable.


// Define Your File Path
string $filePath = "C:/mel/myDataFile.txt" ;

-----------------------------------------------------------
Tip: File paths in Maya (and most packages) require that you use forward slashes (/) instead of backslashes (\). You will get errors if you use backslashes.
-----------------------------------------------------------

Now that we have the path saved in $filePath we're going to use a MEL command called fopen . This command tells Maya to find the file and open it and also tells Maya what we're going to do with the file. There is an optional string argument (I recommend always using it) that will tell Maya if you're going to just read the file, write to it, or append to the end of the existing file. Check out the MEL Documentation for a more in-depth description on how these work, but for now just be aware that since we're reading the data from the file, we're going to use "r" for read.

// Open Your File
$fileId = `fopen $filePath "r"` ;

With the line of code above we've stored our "open" file into the variable $fileId. Now anytime we want to refer to that file in our script we can refer to $fileId. By using the "r" string in the command we've also told Maya that it only has permission to read from the file and nothing else. This way we don't have to worry about the file being changed in any way.

-----------------------------------------------------------
Tip: In the fread command, besides "r" (read), you can also use "w" (write) or "a" (append). More on this in the next tutorial.
-----------------------------------------------------------

Now that the file is open, let's interact with it. There are two commands we're going to look at in this tutorial. The first command is fread. This is used in the following way:

// Read The Data Using fread
string $data = `fread $fileId $data` ;

The usage is a little odd since we're simultaneously defining a string variable and using it as an argument in the command, but this is the way fread works. What it will do is take all of your data in the file and return it as a single string. In this case our return string value would be:

// Result: objectName,polygonType,posX,posY,posZ,rotX,rotY,rotZ,scaleX,scaleY,scaleZ
pCube1,0,0,0,0,0,0,1,1,1
pSphere1,-5,10,-5,-90,90,-45,1,1,1
pCylinder1,2.5,2.342,4.2823,0,0,0,1,1,1
pPlane1,0,0,0,360,180,45,1,1,1//

Nice! All of the data from the text file is in Maya! Success!

The only problem is...ALL of that information is in a single string. It's not much use to you in this format since the actual data is dense and on multiple lines. If you really wanted to you could take that string and break it up into all of the data you need (a mixture of loops and heavy usage of MEL's tokenize command)...but there's an easier way to handle it. There is another command we can use called fgetline.

The fgetline command will get a single line of data from the file and put it into a string variable. This becomes useful because we can now create a string array and put each line into it separately. This will give us a string array containing our entire text file.

The only hiccup here is fgetline will simply get one line of text and then stop. It's not smart enough to continue through the whole text file, we have to tell it to do that. We do this with a WHILE loop. Check it out below ...let's combine everything we've done so far:

// Define Your File Path
string $filePath = "C:/mel/myDataFile.txt" ;

// Open Your File
$fileId = `fopen $filePath "r"` ;

// Get The First Line
string $nextLine = `fgetline $fileId` ;

// Loop Until The String Size Is Zero (No Data)
while (size($nextLine) > 0) {

// Strip Whitespace From The Beginning And End Of The Line
string $cleanLine = strip($nextLine) ;

// Print Line
print ($cleanLine+"\n") ;

// Get Next Line And Continue
$nextLine = `fgetline $fileId` ;

}

If you run this, you'll see Maya print out the five lines of our text file. This time, each of those lines was printed one at a time. Ultimate success!

Let's make a few more additions and turn this into a procedure. First I want it to return an array, so I'm going to define a string array and then put each of my lines into it when I get them from the text file. Next I want to add an argument to tell it to skip the first line. This is useful if I know the first line of my file contains data I don't want. Lastly, instead of hard coding the path into the function, I want to pass the file path into my procedure as an argument to make it more versatile.

When we're finished our procedure looks like this:

// **********************************************************
// Reads A Text File And Returns A String Array Of Each Line
global proc string[] jgTextFileToStringArray (int $skipFirstLine, string $filePath) {

// Open File
$fileId = `fopen $filePath "r"` ;

// Define String Array
string $dataArray[] ;

// Get The First Line
string $nextLine = `fgetline $fileId` ;

// Loop Until The String Size Is Zero (No Data On That Line)
while (size($nextLine) > 0) {

// Strip Whitespace From The Beginning And End Of The Line
string $cleanLine = strip($nextLine) ;

// Add To Array
$dataArray[size($dataArray)] = $cleanLine ;

// Get Next Line And Continue
$nextLine = `fgetline $fileId` ;

}

// Remove First Line
if($skipFirstLine) stringArrayRemoveAtIndex(0,$dataArray) ;

// Return Array
return $dataArray ;

}

Check out the finished version here:
jgTextFileToStringArray.mel

The next tutorial will extend on this and go over how to write to and append to a text file.

Leave a comment below if you found this helpful or have any questions.

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

Tuesday, September 28, 2010

Understanding Recursion

Always remember...

"In order to understand recursion, you must first understand recursion."

:-)

Thursday, September 23, 2010

ScriptSwell Milestone - Over 1000 Visitors This Month!

1,135 unique visitors this month! The one thousand mark is a milestone I've been waiting for and to my surprise it went way up and over in the last few days. Apparently people really like knowing how to remove namespaces. :-)

I just wanted to say thanks and give a quick shout out to everyone who has been visiting Script Swell so far this year. I enjoy writing this blog a lot but it's always more motivating to know people are reading it. Thanks for stopping by and I hope Script Swell continues to be a useful resource for you.

continue ;

Wednesday, September 22, 2010

MEL: Delete Targets From A Blendshape Node

Cleaning up a blendshape node in Maya can be a nightmare. It makes it really difficult to remove targets from the node and even when you do it leaves ghost indexes that can only be removed if you know what they are. The script below allows you to highlight the targets you want to keep in the Channel Box and remove all of the others. Clean and simple. It does this by building a new blendshape node with only your highlighted targets and reconnecting everything exactly the way it was originally. This includes ordering the input stack in the same order it found them.

Code Can Be Found Here: jgRebuildBlendshapeNode.mel

A smart man (or woman!) might wonder why Maya has such a difficult time simply deleting some blendshapes. A discussion for another day I suppose.

This script utilizes the jgReturnMeshDeformers procedure from yesterday's post. Read about it here.

Tuesday, September 21, 2010

MEL: List All Deformers On A Mesh

Yesterday while working on another script, I found myself in need of a list of all of the deformers on a piece of geometry. It seemed like a simple enough thing to get. After all, it was listed right there in the history tab. Fast forward an hour later and the only method I had come up with to find the data was not going to work for what I needed. So, I reached out to a couple of friends and posted on Tech-Artists.org and got a really great answer. It's still a bit convoluted and I'm bitter that Maya won't just give it to you easily, but it works and it's a relatively small amount of code.

I had been tinkering with using the listHistory mel command but the problem with this is that it returns ALL of the history on the mesh object and not just the deformers. My issue was then, how do I distinguish the deformers from the rest of the data? Without a list of every possible deformer Maya offers to compare it to, there's no way I could find them all.

Solution? `nodeType -inherited`

This command along with the -inherited flag will "
Return a string array containing the names of each of the base node types inherited by the given object."

Someone pointed out on the forum that all deformers inherit from the "geometryFilter" base node. With this information we can check through each node in the meshes' history and see if it inherits from geometryFilter. With this, we can return a nice, clean list of all the deformers on a mesh object.


// **********************************************************
// Returns All Deformers On A Mesh
global proc string[] jgReturnMeshDeformers (string $mesh) {

// List History
string $history[] = `listHistory $mesh` ;

// Loop And Check If It's A Deformer
string $deformers[] ;
for($node in $history) {
string $types[] = `nodeType -inherited $node`;
if(stringArrayContains("geometryFilter",$types)) {
stringArrayInsertAtIndex(size($deformers),$deformers,$node) ;
}
}

return $deformers ;

}

Download Here: jgReturnMeshDeformers.mel

Another little gem that comes with this method is that it returns them in the current stack order. Bonus!

So far this method is working out very well for my needs. If you find any issues with it or would like to discuss, please leave a comment below.

Tomorrow's post will be about the script I was originally needing this for, which is a procedure to rebuild a blendShape node with only your specified targets. Maya doesn't allow you delete targets from a blendShape node without a hassle, and when it does delete them it leaves empty target indexes and a big mess under the hood. Swing by tomorrow for more about this. Cheers!

Thursday, September 16, 2010

MEL Script: Incremental Save

This mel script will automatically increment your saved scene for you. Simple and sweet, just source the script to either increment your already numbered scene or to add a serial number at the end of a filename without one. Also adds your new file to your Recent Files list in the File menu. I recommend saving it and then calling it with a shelf button for easy access.

Download Here: MEL Script - jgIncrementalSave.mel


// **********************************************************
// Automatically Increments A Saved Maya File
global proc jgIncrementalSave () {

// Scene File Name
string $sceneFile = `file -q -sn` ;

// Get File Extension
string $extension = `fileExtension $sceneFile` ;

// Remove Extension
string $fileNameNoExt ;
if($extension == "mb" || $extension == ".ma") {

// Tokenize Filename
string $buffer[] ;
tokenize $sceneFile "." $buffer ;

// Recompile Scenename Without Extension
for($i = 0; $i < (size($buffer)-1); $i++) { $fileNameNoExt = $fileNameNoExt + $buffer[$i] ; } } else { // Error Check error "Open file isn't .mb or .ma, please save before incrementing." ; } // Extract Name And Current Version Using Regex string $nameWithNoVersion = `match ".*[^0-9]" $fileNameNoExt` ; string $currentVersion = `match "[0-9]+$" $fileNameNoExt` ; // Check For Integer, Add One If None if($currentVersion == "") $currentVersion = "00" ; // Increment Version int $int = $currentVersion ; $int++ ; // Pad Digits If Needed int $beforeCount = size($currentVersion) ; string $incVersion = $int ; int $afterCount = size($incVersion) ; if($beforeCount > $afterCount) {
while($beforeCount > $afterCount) {
$incVersion = ("0"+$incVersion) ;
$afterCount = size($incVersion) ;
}
}

// Make New Filename
string $newFileName = ($nameWithNoVersion+$incVersion+"."+$extension) ;

// Save It
file -rename $newFileName ;
file -f -save ;

// Add To Recent File List
if($extension == "mb") catch(`addRecentFile $newFileName "mayaBinary"`) ;
if($extension == "ma") catch(`addRecentFile $newFileName "mayaAscii"`) ;

// Finish
print ("Saved as "+$newFileName+"\n") ;

} jgIncrementalSave ;


As always if you find any bugs, have any questions about the code, or would like to make suggestions please leave a comment below.

Monday, September 13, 2010

MEL Script: Remove All Namespaces

There are already a few posts on ScriptSwell dealing with namespaces in general, but here is a quick and dirty way to just remove all of them from your scene, no questions asked.


// Get All Nodes In Scene
string $allNodes[] = `ls` ;

// Loop Through Them
for($node in $allNodes) {

// Tokenize And Rename
string $buffer[] ;
tokenize $node ":" $buffer ;
string $newName = $buffer[size($buffer)-1] ;
catch(`rename $node $newName`) ;

}
This uses the tokenize command. For more info, check out this mel tutorial on how to use the tokenize command. Essentially it's looping through every node in your scene, checking if there are colons in the name of it, and renaming the object to what it would be with no namespaces.

The reason I call this dirty is because it has potential to cause naming conflicts in your scene file. In a lot of cases namespaces are there for a reason, so you should make sure it's safe to remove them before doing so. Maya does not support having more than one object with the same name, so if you run this script it may end up changing the names of some of your nodes. Usually this is in the form of adding a number to the end of it. (i.e. hand_CTRL becomes hand_CTRL1). Be sure to save your scene before running this (as you should with any MEL script) and do some testing before you continue forward to make sure everything is working like it should.

Also:
  • Every Maya scene file has a list of namespaces that have at one time or another been used in that scene. This script will not remove any from that list.
  • If something is referenced in your scene, this will not be able to remove the namespace properly.
  • You'll see a slew of error messages pass by, this is Maya trying to rename nodes that aren't allowed to be renamed. You can ignore these.

For more reading about namespaces in Maya, check out the following:

Saturday, August 21, 2010

Repost: Some lesser-known truths about programming

I saw this on Reddit this morning and thought some of you may like to read it. Find the link to the full article on http://dotmac.rationalmind.net below.
My experience as a programmer has taught me a few things about writing software. Here are some things that people might find surprising about writing code:
  • A programmer spends about 10-20% of his time writing code, and most programmers write about 10-12 lines of code per day that goes into the final product, regardless of their skill level. Good programmers spend much of the other 90% thinking, researching, and experimenting to find the best design. Bad programmers spend much of that 90% debugging code by randomly making changes and seeing if they work.
    “A great lathe operator commands several times the wage of an average lathe operator, but a great writer of software code is worth 10,000 times the price of an average software writer.” –Bill Gates
  • A good programmer is ten times more productive than an average programmer. A great programmer is 20-100 times more productive than the average. This is not an exaggeration – studies since the 1960′s have consistently shown this. A bad programmer is not just unproductive – he will not only not get any work done, but create a lot of work and headaches for others to fix.
Read the full article here: http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-programming/

Tuesday, July 27, 2010

Maya: Transfer Mesh UVs After Rigging

This is slightly off-topic than the usual scripting posts, but I found this really useful and wanted to share.

It's a technique on how to transfer UVs from one mesh to another while maintaining deformer history on your object. It worked for what I was doing and I imagine I'll be using it again in the future.

http://animbiz.blogspot.com/2010/02/transferring-uvs-after-rigging.html

I'm currently working on a bullet proof transfer script, I'll let the world know when it works. :)

Wednesday, July 21, 2010

Maxscript: Check If Auto Key Is Turned On

Need to query if auto key is turned on or off using maxscript? Use the animButtonState command. It returns true or false, so using this info you can check it's current state and then setup a toggle for it, or use it make sure Max is in the correct mode before running a script.

Simple, yet extremely useful!

Wednesday, July 14, 2010

MEL To Maxscript To MEL

For whatever reason, I am a huge fan of using MEL to export a Maxscript file and vice versa. There is a strange feeling of satisfaction, something along the lines of getting away with a crime or not letting the man bring you down. Grab your code by the balls. That is all. :)

Tuesday, July 13, 2010

MEL/PyMEL: Get Top Most Parent Of An Object

Here is a function I use often that will give you the top-most parent of any object. I use it when I want to perform a function on an entire heirarchy but don't want to worry about needing to select the top most object. I can just grab any of them, hit a button and it grabs the top (using the function here) and then selects or grabs the entire hierarchy. I'm kind of OCD about saving every possible second.

Mel Version:
// **********************************************************
// Return Top Parent Of Any Node
global proc string jgReturnTopParent (string $obj) {

string $a[] = {} ;
string $b ;
$a = `listRelatives -p $obj` ;
while(size($a) > 0) {
$b = $a[0] ;
$a = `listRelatives -p $a[0]` ;
}
return $b ;

}

PyMel Version:
##################################################
## Return Top Parent Of Any Node
def jgReturnTopParent(obj):

# Imports
import pymel.core as pm

# Get First Parent
varA = pm.listRelatives(obj,p=True)

# Loop All Parents
while(len(varA) > 0):

varB = varA[0]
varA = pm.listRelatives(varA[0],p=True)

return varB

Update: Thanks to Scott Englert for pointing out in the comments that MEL and PyMel both have built in functions for this already.

MEL command that comes with Maya:

rootOf($obj);

PyMEL method of dag nodes:
obj.root()

Cheers Scott!

Monday, July 12, 2010

Get Maya Version With MEL

Today I had to figure out how to get the current Maya version with MEL. I assumed it would be in some sort of environment variable but it turns out it had it's own command. I spent a good fifteen minutes trying to track this down so I hope this blog post can save somebody out there at least some of that time.

The command is getApplicationVersionAsFloat.
float $mayaVersion = `getApplicationVersionAsFloat` ;

Friday, July 9, 2010

Using White Space To Your Advantage

A really helpful hint when scripting is to know how to leverage white-space in your code. What this means is that certain languages will allow a lot of flexibility in the way your code is laid out that can make it much easier to read and edit. Take this example:

string $fileArray[] = {"C:/_Maya/temp/file_0001.ma","C:/_Maya/temp/file_0002.ma","C:/_Maya/temp/file_0003.ma","C:/_Maya/temp/file_0004.ma","C:/_Maya/temp/file_0005.ma","C:/_Maya/temp/file_0006.ma","C:/_Maya/temp/file_0007.ma","C:/_Maya/temp/file_0008.ma","C:/_Maya/temp/file_0009.ma","C:/_Maya/temp/file_0010.ma","C:/_Maya/temp/file_0011.ma","C:/_Maya/temp/file_0012.ma","C:/_Maya/temp/file_0013.ma","C:/_Maya/temp/file_0014.ma","C:/_Maya/temp/file_0015.ma","C:/_Maya/temp/file_0016.ma","C:/_Maya/temp/file_0017.ma","C:/_Maya/temp/file_0018.ma","C:/_Maya/temp/file_0019.ma","C:/_Maya/temp/file_0020.ma"} ;

The code above shows a string array containing twenty Maya Ascii files. It's straight forward, but a little difficult to read due to it all being condensed. If you need to make edits to one of the file names or add some into the list somewhere specific it can be a bit annoying finding the right spot. Let's use white space to help us with this.

string $fileArray[] = {
"C:/_Maya/temp/file_0001.ma",
"C:/_Maya/temp/file_0002.ma",
"C:/_Maya/temp/file_0003.ma",
"C:/_Maya/temp/file_0004.ma",
"C:/_Maya/temp/file_0005.ma",
"C:/_Maya/temp/file_0006.ma",
"C:/_Maya/temp/file_0007.ma",
"C:/_Maya/temp/file_0008.ma",
"C:/_Maya/temp/file_0009.ma",
"C:/_Maya/temp/file_0010.ma",
"C:/_Maya/temp/file_0011.ma",
"C:/_Maya/temp/file_0012.ma",
"C:/_Maya/temp/file_0013.ma",
"C:/_Maya/temp/file_0014.ma",
"C:/_Maya/temp/file_0015.ma",
"C:/_Maya/temp/file_0016.ma",
"C:/_Maya/temp/file_0017.ma",
"C:/_Maya/temp/file_0018.ma",
"C:/_Maya/temp/file_0019.ma",
"C:/_Maya/temp/file_0020.ma"
} ;

Cool huh? MEL and most languages will allow you to do this and many other things with white space in your code to really space it out evenly and neatly so it's easy for you and anyone else interacting with your scripts to use and read. Practice ways of using this trick to make your code more awesome!

Wednesday, July 7, 2010

Maya Scripting: "Should I Learn MEL Or Python?"

The question I get asked most often by people just getting into scripting in Maya is, "Should I learn MEL first or go right to Python?". It's a legitimate question and at this stage in the lives of both languages, it has merit. The reality is you need to learn both...but which one first? And why learn MEL when Python is there? Maya's embedded language has been around for ages and is notoriously weak and difficult to use in certain areas. As Python slithers it's way deeper and deeper into our pipelines, many believe MEL will be phased out completely in coming years. Seeings how it will inevitably become more and more prevalent in 3D applications and is not Maya-specific, isn't it only logical to not bother with Maya's aging language and go right to Python? All signs seem to point that way, but I don't agree with this philosophy and I'll explain why.

Python and Mel - Both Badass, (though one is insane)

My answer is almost always the same when people ask me this question: First, learn enough MEL for it to be useful and help you work, then start to learn Python along side it.

If you are new to programming and scripting, you are unlikely to learn enough Python quickly enough for it to be of any use to you. Python is definitely more powerful, gives you more control, and is really more fun to use in the end...BUT...it's more difficult. There are more rules, more things involved with getting it working properly and generally just more things you need to know in order to use and learn it. You'll need to learn the basics of installing libraries, having paths set up correctly, using environment variables, linking modules, and all sorts of steps just to get it doing what you want. Of course you can just go in there and start giving it Maya commands, but at that point you're not taking advantage of it's abilities and may as well use MEL. That brings me to my point.

With MEL, you can start doing things almost instantly even with little to no knowledge of how it works. Compared to some languages it's relatively easy to pick up and get going with and it exists in Maya already with great documentation. To get started, you can watch your Script Editor to see what certain commands are doing and how they work. Run a tool or do something in Maya and watch what kind of code it spits out in the Script Editor. Copy and paste that code and start changing things around to make your own little scripts and buttons. Learning the basics of variables, functions and syntax are all easy using the tutorials in the Maya documentation. All in all, within a couple of hours you can be writing small snippets of code that will actually be speeding up your work-flow. It's not to say you can't do the same with Python if you're a fast learner, but generally speaking you'll be up and running with MEL a lot quicker.

Once you're cruising with MEL and you're putting it good use...that is when I'd recommend diving into some Python and beginning to learn how it works. At the end of the day you can (and will) use both in various situations, but I'd never suggest to anyone to NOT learn how to script in MEL. Whether it be needing to clean up a co-workers code, make changes to a script, write a Maya GUI, or write a script in an older version of Maya that doesn't have Python...there are plenty of reasons you should not skip MEL.

All that being said, PyMEL combines the best of both and is my drug of choice at the moment. Learn them all so that you can make your own call on what's best. :)

Thursday, July 1, 2010

To Script, or Not To Script

It seems to happen all of the time. One of those situations arise where you have a task to accomplish that you know will be simple as pie to script. There's a big smile on your face as you finish writing it, nod approvingly at it's beauty and let it run it's course. Relaxing back into your seat, you bask in the glory of having once again thwarted destinies vain attempts to make you work.

But, this was one of those not-so-rare occasions where the task at hand would have taken you only a few minutes longer, or in some cases less time to accomplish than writing the code to make it work. You were torn for a moment, "Should I just do it? I'll never have to do this ever again and scripting it will take just as long as doing it manually". To script, or not to script?

If you're reading this blog, you're most likely in the demographic that would answer that question easily. Script it! Even though this is a specific case and the actual code will most likely never get used again verbatim, you still gain a lot from scripting tasks like this. Here is why:

Within every task given us is a problem to solve. Some are easier than others, some are difficult, and some may not seem like problems at all. The fact is though that as simple or as direct as a task may seem there is always some level of problem solving involved with it and as it is with any other skill, practice and repetition always make you better. If you approach every task like a challenge and use it to level up in some way, it makes even the simplest script worth writing!

What can you do to make a simple, easy-to-write script more challenging? Some ideas:
  • Write it in another language or application
  • Use commands or functions you've never used before
  • Make it more robust, so that is becomes useful in other situations
  • Write it in as few lines of code as possible (don't count comments or white space lines)
All of these things will definitely make you better and are usually fun. As long as your skills are improving, you're getting more benefit from scripting a simple one-off task then if you were to just do it manually. Your gaining skills, experience, challenging yourself, and inevitably getting better so that you'll be more apt to tackle a difficult assignment when it comes knocking.

In our line of work, the ones who stop learning are the ones that are left behind. Always seek out new ways to improve your skill-set and keep getting better!

Friday, June 25, 2010

Lightbot 2.0 - Free Flash Game Teaches Programming Logic

I stumbled upon this great little gem on Reddit the other day: it's a free flash game that teaches you how to solve puzzles using programming style logic. You use functions, recursion and other programming tools and tricks to control a little man trying to solve his way through puzzles. It's brilliantly designed and has already helped me get a colleague of mine starting to think like a scripter.


"Light-bot is back, more puzzling than ever! Use programmer-style logic to tell the bot how to light up all the blue tiles! Functions, conditionals, recursion, expert levels- many different features for new and old players."

It's called Lightbot 2.0 and can be found here: http://www.kongregate.com/games/Coolio_Niato/lighbot-2-0

Friday, June 11, 2010

MEL Script: Print Toolbox

One of the best things about scripting is that it gives you the ability to design your own tools that compliment and speed up your workflow. When I'm using Maya I am in constant need of information from my scene. Whether I'm debugging a broken file, preparing scenes for delivery to a client, or setting up a rig, I always need little morsels of data from the scene to do this. To help me with this I wrote this simple little toolbox that let's me get a lot of the data in just a click.

It's a small floating gui with a half-dozen buttons that let you print out some very useful information.

Features:
  • Print Transform Values: Prints out basic transform values in the script editor for everything selected in an easy to read format.
  • Print SDK Count: Prints the number of set driven keys in your scene.
  • Print Joint Count: Prints the number of joints in your scene.
  • Print Selection Count: Tells you how many objects are selected.
  • Print All Nodes Count: Gives you the total number of nodes in your scene.
  • Print Selected To Array: This will print out your selected objects into an array that you can copy from the script editor.
There are of course an enormous number of random things that you may want to print out at any given time but these are the ones that I need to use on a day to day basis. Perhaps in an updated version I'll add the ability to customize which buttons appear and allow you to print out a load of other things. For now, enjoy!

Get the Print Toolbox here: jgPrintToolbox.mel

As always feedback and comments are welcome. If you're just getting into MEL scripting take a look at the script itself to see how it's working.

Thursday, June 3, 2010

Shout Out: Animators Resource

Animators Resource is a blog dedicated to all things animation. It features links to helpful sites, videos, behind-the-scenes info on upcoming animated films, links to useful animation tools and a lot more awesome content. It's updated regularly by a group of dedicated animators and is a great resource for both the student and the professional.

http://animatorsresource.blogspot.com/

Wednesday, May 26, 2010

Confirmations And You

I got into an interesting debate with a colleague last year about the use of confirmations in tool writing. He was passionately of the mind that the user should be solely responsible for clicking a button, and if they didn't mean to click it then they should face the consequences. Now, most people will agree that there are few tools in our pipelines that will cause issues if a button is pressed when the user isn't ready, it's usually no big deal. There are some though. One example would be sending a shot to be rendered.

Let's look at an example where the user has a button on a shelf that will send their currently open scene to be rendered, on the farm or elsewhere. As soon as he or she clicks the "Render" button, the shot is saved, archived, render is set up, and it's sent to the farm to be magiked into a video. If the shot is ready to be rendered then this is certainly a great solution. One simple click and they're off to play golf. However, what if it's going on the dark side of an 18 hour shift and the shelf buttons have become blurry enough that the user clicks the button by mistake? Let's look at why this is an annoyance in production.

First off, there is now an asset being created that is of no real use. The user will to spend time alerting the powers that be that they need not use this asset for anything and it should be deleted when it's finished being comped. Next, the powers that be must track down this asset and delete it so it doesn't get confused with a proper deliverable of any sort. On top of all this, the process of rendering and comping this shot will inevitably take up resources that would be better served doing anything but an irrelevant task. In the big picture it's not a huge problem by anyone's standards, but it could have all been avoided by requiring one extra click.

I won't argue that there is a time and place for a confirmation box, but when the potential for screwing up is wasting people's time and resources then I think it's right to use one. A tired animator or rigger can save themselves and others time and money by being given a chance to say "No!".

Today I was coding something and a similar situation occured where I was making a decision to include a confirmation or not. This is how I was reminded of the passionate fellow who argued never to use them. I laughed for a minute, then added my confirm dialog, with gusto.

Thoughts? To confirm, or not to confirm?

Monday, May 24, 2010

MEL Script: Makes A Quick Set Driven Key

This is a tool I use from time to time that enables me to quickly bridge a set driven key between two objects/attributes that I can then edit. It's much faster for me to grab a node and edit a curve into the values I want than it is to use Maya's default Set Driven Key creation GUI or even the default MEL commands. This lets me quickly create an SDK and edit without all the fuss. It's not something I'd use when creating Set Driven Keys on a massive scale, like in the case of an entire rig...but instead for when I just need a couple of them here and there.

The 'GET' button will grab the selected attribute from the channel box, or you can type in your 'object.attribute' manually. Hit the 'Make SDK' button to create. Sourcing the script will launch the GUI.

Get it for free here: jgMakeSimpleSDK.mel

Friday, May 7, 2010

MEL: jgViewportBackgroundColor - Change/Save/Load Viewport Background Colors

This is a script I use from time to time as a quick and easy way to change your Maya viewport's background color. You can use the slider to adjust value or click the color image to bring up a palette and choose a new shade.

If you want to get back to your favorite color quickly, there's a save and load feature as well.

Simply source in Maya to use.

Download the Mel Script Here: jgViewportBackgroundColor.mel

Thursday, May 6, 2010

MEL: Zero Out Selected Controls

Here's a really simple script I threw together for an animator that zeros out all visible and keyable attributes on objects. It ignores visibility and scale, and any non-scalar (numeric) attributes.

Great for getting a character back into his or her neutral position.

Download Here: jgZeroOutAttrs.mel

Wednesday, May 5, 2010

MEL Script: Select Connections

This is a small tool I wrote to give you easy access to connections and connected nodes coming in or out of a specific attribute. I'm constantly working with thousands of set driven key nodes and I need fast and simple access to specific ones so this script/gui gives you just that.

To use, first select the node containing the attribute, then hit 'Load Attributes'. This will populate the list with all the visible/keyable attributes on your object. You can then select as many as you like from the list and press 'Select Connections'. There are also checkboxes you can turn on or off to only select incoming or outgoing connections.

It's available for download here: jgSelectConnections.mel

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.

Saturday, February 20, 2010

MEL: Remove Namespaces From A String or Object Name

Dealing with namespaces is a pain in the ass. Period.

Veteran TD's and Maya guys will tell you how easy it is and that there are so many ways to deal with them and this and that. They are correct. However, they are still a giant, giant pain in the ass.

Thankfully there's a simple trick to remove namespaces from using the tokenize MEL command.

What tokenize does is split a string into multiple parts using a character(s) as the splitting point. For example, if an object is named this_Object if you were to tokenize it and split it using the character "_" than you would end up with two tokens: "this" and "Object".

We know that if an object has a namespace, or in some cases multiple namespaces that the object name will always be prefixed with the namespace and a colon. (:)

So, let's tokenize it out using a colon.

string $objectName = "Character:r_arm_CTRL" ; // Our Object Name
string $buffer[] ; // Create Empty Array To Hold Tokens
tokenize $objectName ":" $buffer ; // Split $objectName by ":"

// Now...
string $namespace = $buffer[0] ; // "Character"
string $object = $buffer[1] ; // "r_arm_CTRL"

With this example we've done a basic namespace removal. However, this is prone to errors for multiple reasons. What if you have multiple namespaces? By hardcoding which index you're getting from $buffer you will end up potentially grabbing the wrong data. A way around this is to use a programming trick to always grab the very last item in an array. As with namespaces, we always know the last item will be the object itself so we'll end up with the correct string.

string $objectName = "Character:r_arm_CTRL" ;
string $buffer[] ;
tokenize $objectName ":" $buffer ;

string $objectWithoutNamespace = $buffer[size($buffer)-1] ;


By getting the size of the array, and subtracting one, you'll end up with the last index of a zero-based array. If it were Maxscript, arrays are one-based (they start with one, not zero) so you wouldn't need to subtract one from the size.

(If you're curious, the equivalent of tokenize in Maxscript is filterString)

So there you are, a quick, easy, non-dirty way of getting rid of namespaces in an object's name.

Friday, February 19, 2010

MEL: Source A Script With A Variable

Maya's MEL script command "source" will not let you source a path through a variable. Example, the following will NOT work.


string $myScriptPath = "C:/myScript.mel" ;
source $myScriptPath ;

It will produce a syntax error. So, how do we do it then? One method is by using the "eval" command and putting the source command into the string itself and slashing out the quotes.


string $command = "source \"C:/script.mel\" ;" ;
eval $command ;

"eval" will run the string as a command, thus sourcing it and your script/variable.

Another option here is to create your own proc, let's call it betterSource() ;


global proc betterSource (string $script) {

string $command = "source \""+$script+"\"";
eval $command ;

}

// Now you can run:

string $path = "C:/script.mel" ;
betterSource $path ;

Either way works and one is not necessarily better than the other.

Thursday, February 11, 2010

MEL: Snap / Move An Object To The Position and Orientation of Another Object

There's a super easy trick for snapping an object to the position and orientation of another object in your scene.

All you need to do is constrain your object with maintain offset turned off, and then delete the constraint. So, a quick and easy way to do this with point and orient constraints.


delete `pointConstraint $obj $targetObj` ;
delete `orientConstraint $obj $targetObj` ;

This deletes the return (the name of the constraint) after running the constraint itself.

Similarly you could just use a parent constraint.

delete `parentConstraint $obj $targetObj` ;

Credit to John Riggs for showing me this trick.

Tuesday, February 9, 2010

MEL: Split A Joint / Bone Chain To A Specified Number Of Joints

Here's a procedure you can use in Maya to split a joint chain into multiple joints.

This proc requires jgArrToStr which you may recognize from yesterday's post. :)

Source them both first and then run it by typing:

jgSplitJointChain joint1 joint2 4 ;

(joint1 being the parent of the two.)
(4 being an example of how many joints you want to add.)

One note, currently it assumes y-up as your secondary axis orient. Maya doesn't let you query SAO so there's no way I know of to dynamically check what to use. Change it in the proc if it's not y-up in you're setup.


// **********************************************************
// Adds A Specified Number Of Joints Into A Chain
global proc jgSplitJointChain (string $parent, string $child, int $numJnts) {

// Adjustment For Math Purposes
$numJnts++ ;

// Checks
select -cl ;
if(!`joint -ex $parent`) error ($parent+" doesn't exist in scene or is not a joint.") ; // Does Parent Exist
if(!`joint -ex $child`) error ($child+" doesn't exist in scene or is not a joint.") ; // Does Child Exist
if(jgArrToStr(`listRelatives -p $child`, 0) != $parent) error ($parent+" is not the parent of "+$child+".") ; // Check Parent/Child Relationship

// Joint Positions
float $pPos[] = `joint -q -p $parent` ;
float $cPos[] = `joint -q -p $child` ;

// Parent Orientation
string $ro = `joint -q -roo $parent` ;
string $sao = "yup" ;

// Unparent Child
parent -w $child ;

// Vector
float $jointVector[] ;
$jointVector[0] = ($cPos[0]-$pPos[0])/$numJnts;
$jointVector[1] = ($cPos[1]-$pPos[1])/$numJnts;
$jointVector[2] = ($cPos[2]-$pPos[2])/$numJnts;

// Deselect All
select -cl ;

// Loop And Setup Joints
string $lastJntCreated = $parent ;
string $newJnts[] ;
for($i = 1; $i < $numJnts; $i++) {

// Create Joint
string $jnt = `joint -p ($pPos[0]+($i*$jointVector[0])) ($pPos[1]+($i*$jointVector[1])) ($pPos[2]+($i*$jointVector[2]))` ;

if($i == 1) parent $jnt $parent ;

// Set Last Created For Reference (Updates Until Last Iteration)
$lastJntCreated = $jnt ;

}

// Reparent End Joint
parent $child $lastJntCreated ;

// Orient Joints
joint -e -oj $ro -secondaryAxisOrient yup -ch -zso $parent ;

}

If you have any trouble using this or have questions/comments, send me a note!

Monday, February 8, 2010

MEL: Trick To Pass An Array Into A String Argument

I wrote a quick proc this morning that will let me pass an array (specified index) into a string argument since Maya won't let you do it any way by default in MEL.

Example...

// Looking For This Object's Shape Node
string $object = "myObject" ;
string $shapeArr[] = `listRelatives -shape $object` ;
string $shapeNode = $shapeArr[0] ;

// The above returns an array, which is annoying. To me at least...



I'm aware you can use $shapeArr[0] as your shape node without redeclaring it into the second variable, but I've always felt it was sloppy to do it that way.

Here's a simple MEL procedure that makes it easier.

// **********************************************************
// Returns An Index From A String Array
global proc string jgArrToStr (string $array[], int $index) {

// Get Index From Array
string $obj = $array[$index] ;

// Return
return $obj ;

}

// Now you can do...

string $object = "myObject" ;
string $shape = jgArrToStr(`listRelatives -shape $object`, 0) ;

This will only work if you know the index you're looking for in the array, but a lot of times MEL returns string arrays when it doesn't need to and you know what it will be.

Wednesday, February 3, 2010

MEL: Working With Multiple Level Namespaces

I learned a great trick today concerning Maya scenes with objects that have multiple levels of namespaces.

Namespaces in general are a bit difficult to work with as they tend to break scripts if you don't constantly check for them, so this trick is a simple and very useful way to check for them and prevent errors.

Let's say you have an object you want to select called, "r_hand_CTRL". You want to select it, but you need to test for namespaces because it might actually be named "Char:r_hand_CTRL" in your Maya scene. Or for more fun let's say it's named, "Post:Char:r_hand_CTRL".

Selecting r_hand_CTRL isn't going to work as it doesn't exist if it's prefixed with namespaces, so we have to search for what the namespaces are. Maya's namespace MEL commands are annoying and difficult to use. So here is a trick.

// Define Control
string $ctrl = "r_hand_CTRL" ;

// This int is used below to prevent an infinite loop
int $i = 0 ;

// Loop until it finds all the namespaces
// Stop at ten if it still can't find it
// Hopefully you don't have more than ten recursive namespaces
// May the Gods help you if you do.

// Check forever until the object exists, or $i = 10
while(!`objExists $ctrl` || $i == 10) {

// Append *: until it can find the object
$ctrl = ("*:"+$ctrl) ;

// Increment $i so we don't do this infinitely
$i++ ;

}

// Note: You could also use a FOR loop. But I didn't here.

// Now that we know $ctrl exists, we can do whatever we want with it
select -r $ctrl ;

I've already used this trick like forty times, it's awesome. Enjoy!

Edit: Since learning this, people have mentioned that you can just use the following command to find your object:

ls -recursive 1 "myObject" ;

Quite handy, and much more useful than what we worked on above! I wish I had the entire MEL docs memorized.... :)

Friday, January 22, 2010

MEL: Progress Window

Ever wanted to build a progress window into a script you're running that is taking a long time? MEL has one built in already, and it works quite well!



int $amount = 0;

progressWindow
-title "Doing Nothing"
-progress $amount
-status "Sleeping: 0%"
-isInterruptable true;

while (true) {

// Check if the dialog has been cancelled
if ( `progressWindow -query -isCancelled` ) break;

// Check if end condition has been reached
if ( `progressWindow -query -progress` >= 100 ) break;

$amount += 5;

progressWindow -edit
-progress $amount
-status ("Sleeping: "+$amount+"%");

pause -seconds 1;
}

progressWindow -endProgress;

You can find it in the MEL help file. It's fun.

Thursday, January 21, 2010

MEL: Select Outgoing Nodes From A Specific Attribute

Sometimes you'll have a node on a rig or in your scene that has thousands of outgoing connections on lots of different attributes. You may only want to edit a specific attribute's outgoing connections but you can't find them in the Hypergraph because there are just too many to search through. Here is a small trick to speed that up.

Let's assume your node is called "myBusyNode". The attribute you're looking to check is called, "thisAttr".

// Selects all nodes that are outgoing connections of myBusyNode.thisAttr
select -r `listConnections -s false -d true myBusyNode.thisAttr` ;

Alternately you can select the incoming connections by swapping source and destination.

// Selects all nodes that are incoming connections of myBusyNode.thisAttr
select -r `listConnections -s true -d false myBusyNode.thisAttr` ;

And, of course you could set both to true and select both incoming and outgoing nodes.

The return value of listConnections will give you all of the outgoing connected nodes from that attribute. You can then focus on your selection in the Hypergraph and you'll be able to see only the network of your selected nodes, not every single connection coming out of myBusyNode. I use this trick about four hundred and eighty seven times a day.

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