Despite a flat tire in the middle of nowhere and very little sleep, I had a wonderful time in Florida last week! Many thanks to our hosts at Ringling College of Art and Design. We had a great turn out for both the presentation and Q&A sessions, and the student work was really impressive. Best of luck to the Seniors with their thesis games. Hopefully we will return in the Spring for some more presentations and portfolio reviews.
Whenever asked, I always advise aspiring tech artists to blog about problems they've solved. I thought it might be finally time that I follow my own advice. What follows is a walk through of the script I wrote as a test while in my last semester at Ringling. This is what got me into Riot as an intern! I'm going to make this walk-through very high-level, trying to focus on steps I'd approach when writing any script. Let's do this.
First off, here is a video that I made back when I first finished the tool demoing the functionality (might need to re-size window to view). Check out that sweet Maya 2009 GUI! I will describe the problem and my thought process below, so don't worry if you don't want to spend fifteen minutes watching through the whole thing. Secondly, here is the script.
The goal of the script was to mimic some XSI weighting functionality that the customer's version of Maya (read: Maya 8) did not have. The problem as described was, "be able to select a set of verts and joints and intuitively weight those points to those joints, either evenly or by distance." This has obvious uses, such as when one has many noodle-y appendages near one another and does not want to hand-paint away all undesired spill-over weighting from nearby joint chains.
So! What do we need to do to make this happen? Whenever I approach a script, I do the following things:
- Design a work flow/ user experience
- Write up pseudo-script logic of what the script will do
- Identify any problem spots I'm not sure how to do
- Draw some mock-up UI
In the case of this script, the user experience I imagined was:
- Select the verts that need to be re-weighted
- Select the joints they should be weighted to
- Set whatever settings we need (evenly or by distance? max influences? Anything else?)
- Push button, receive
Seems easy enough. Okay, so aside from the UI, what does the script actually do when I run it? Time for some pseudo-code:
|joints = list of joints selected|
verts = list of verts selected
for vert in verts:
bonedist = dictionary of distances to each bone
for joint in joints:
bonedist.append(distance from vert to joint, paired to bone name)
count = user setting for max influences
while count > 0:
if even bind:
lowbone = bone with (count)th lowest distance
vertweighting(vert, lowbone) = 1/max influences
count - 1
if distance bind:
distSum = sum of all items in bonedist
lowbone = bone with (count)th lowest distance
vertweighting(vert, lowbone) = bonedist(lowbone)/distSum
count - 1
Next, we need to identify the parts of this script that are difficult. Depending on your experience with Maya or python, you may be hesitant about:
- How to organize this data elegantly
- How to use the dictionary data structure for the bone/ distance pairs
- How Maya stores vertex weighting
- What command sets the weighting of a specific vertex to a specific bone
Luckily, a bit of internet searching can answer any of these questions! With a bit of digging, you will learn that a dictionary is an unsorted list of key/value pairs that can be searched by key or value. You will discover that Maya polygon objects store a weighting matrix of vertices and joint influences. You will discover that you need the setVertWeights command for this task. Isn't the internet awesome?
Here is the final UI (again in awesome Maya 2009 style) that I arrived at. As you can see, I provided check boxes and radio buttons for all the options we planned for, as well as several that merited addition after some further research.
Once we have all these things in place, it's time to actually start coding. I'm not going to go into this step as this post is getting a bit long. Suffice it to say that no mater how well we plan, it usually becomes apparent that things are more complicated than we anticipated. In my case, I ended up dividing functions and re-combining them as I struggled to understand python class/ object structure. I learned a lot through this project about the structure of a tool, a lot about object oriented programming, and a lot about vertex weighting in Maya.
When writing any tool, it's important to not lose sight of your goal. Think of the person that you're designing for, and try to imagine what it will feel like to use the tool you've created. Is it intuitive? Does it do all the things it should do? Is it fast? Could you make this tool simpler? For anyone who might want to improve upon your work, did you comment your code sufficiently that another programmer could follow your thought process?
I heard a great piece of advice from a co-worker: when you code, you have three customers. The first is the end-user, who will use your work. The second is other coders, who may have to edit and maintain it. The third is the hardware, which will have to process it as efficiently as possible. Making just one or two of these customers happy is not enough. Weigh principle against pragmatism, and invest your efforts where they'll be most beneficial to the project.
That concludes this post mortem of the jpWeightsTool. I hope you got as much out of reading it as I did out of writing it! Cheers, and happy coding.