Monday, July 30, 2012

Vector Angle setup

For the past two days or so I've been thinking about vector math, trying to pull off a node setup like this one. I've got the concept in my head, but I'm still working on the details of the implementation, as far as the actual math goes. I've been studying the Maya API as well, and while I'm understanding how everything fits together (the general structure of API plugins), I just don't have enough experience with the API classes to know which MObjects and functions to use to get what's in my head into the code and onto the screen. So I figured I'd mock up a simple node network to see what I could do, albeit with a limited understanding of the math involved. I'd still like to get an API locator node implementation figured out, but I figured I'd share what I've come up with so far.

Ranged Vector Angle Setup from kyleMR on Vimeo.


Basically, I'm getting an aim vector from an object (in this case, the cone) by using a locator. I subtract the position of the cone from the position of the locator  to get the aim vector (which is a normalized vector, since the locator is one unit from the cone) and then I do the same for the target locator, which gives me a target vector. I take the dot product of those vectors and pipe them into a setRange node, which allows me to remap the value between 0 and 1, as well as change the high and low boundaries of the calculation. Pretty simple. Basically, this sort of setup can work as a pose space deformer, although I'm just driving the scale of a sphere for the sake of visualization. The process only takes a few minutes to set up by hand, but I scripted it out just for the hell of it. If anyone is interested in the code just let me know.

This problem has been churning in my mind for a few days now, and I'll definitely be taking the solution further.

Saturday, July 28, 2012

Check Symmetry Utility

A little while ago I worked out this little melscript utility to check for non-symmetrical vertices in your models, and today I decided to turn it into a python script and rework some of the logic. Basically, you run the script and it returns the number of non-symmetrical vertices in the mesh you have selected. It also selects those vertices for you, so you can see them in the viewport. If you want, you can also specify a certain mesh, mirror axis (the plane across which the symmetry will be determined, default is the YZ plane), tolerance, and turn off the selection of the verts at the end. That way you could use this as a utility to another script that deals with symmetrical meshes. Grab it here: checkNonSymmetricalVerts.

Friday, July 27, 2012

Stick a fork in it

... because I finally finished my modular autorig. Developed by following along with 3DBuzz's monstrous 50+ hour 'Developing Modular Rigging Systems with Python' DVD, this autorig is by far the most elaborate and involved scripting project I've ever taken on. This all started because I wanted to build my own autorig, but didn't really know exactly where to start, so I picked up the learning DVD to get a better idea of the process. I definitely learned a ton doing this project, so I wanted to share some of those insights here. Below is a quick demo of the system's functionality.


The process:

The autorig itself spans over 30 python files, written from scratch, and it took a little over 2 months working off and on to finish. The process was pretty simple at first; watch the videos and write the code as they write it, testing frequently. However, it quickly became apparent that the process wouldn't just be a walk in the park, copying code the whole way. Discrepancies in the way UI code is handled by the pre-QT interface of Maya 210 (which was the version the system was developed in) and Maya 2012 meant that subtle changes had to be made in order for all of the elements of the user interface to be displayed correctly. Also, fixing any bugs that I discovered while testing (most due to my own typos, I'll admit) was totally up to me.

I coded the entire system in Notepad++, rather than relying on the benefits of code completion and error checking in an IDE such as Eclipse. The result is that I've grown extremely adept at catching my own syntax errors, and quickly fixing those that do manage to slip through the cracks. Even some of the trickier and more insidious bugs didn't last much longer than 30 minutes or so of solid debugging.

The entire system is scripted in the maya.cmds implementation of python rather than pyMel. This has the effect of making the code much more verbose and quite a bit harder to read... but overall it wasn't that bad.

Insights:

Since I took this on as a learning experience, I wanted to really break down what I learned, the good, the bad, and the ugly, from this project.
The main thing that I learned was that, in a project of this magnitude, construction is king. I was reading Code Complete by Steve McConnell while working on the system, and it was a great combination. It really helped drive home the importance of well designed, well documented, well constructed coding. I'll get more into that in a second.

Since I had planned to take what I learned and apply it to a new autorigger of my own design, I noted a few things that I would change in my system. The first was the reliance on naming convention and namespaces to store and recall information about where a particular rig part came from and what it could do. This led to namespaces nested three layers deep, and an outliner that quickly became a solid wall of text. It worked, but storing information inside of metaNodes connected directly to rig parts would have been a more elegant, more powerful, and a cleaner solution. Second, once you 'lock' the rig and turn the blueprint modules into actual joints, that's it. the process is pretty much irreversible. It would have been nice to be able to see the joint locations, and then be able to go back and make changes. The other big design choice that I would avoid was the use of locked containers. Containers in Maya may be powerful, and theoretically awesome from a rigger's point of view (animators can't rename or delete important nodes, everything is bundled together), but in practice they're pretty annoying. Having to constantly unlock nested containers in order to debug things is slow and painful. In addition, attributes published to containers behave strangely in the channel box, disallowing middle-mouse scrubbing to change attribute values, and turning the clean list of attributes into an organizational nightmare.

Seen here: the arm controls? I'm not even sure...

The main thing I took away from this project though, was the importance of construction. I want to be clear: I mean no disrespect or insult to the guys who developed this system. It was a rapidly developed prototype, meant as a proof of concept and a learning tool -- not a production ready rigging pipeline. What the guys at 3DBuzz managed to pull off in this DVD was nothing short of awesome. That being said, the code itself is pretty rough.
There is almost no documentation whatsoever to the code. This was addressed directly in the videos a few times, as the addition of comments and documentation would have made the videos even longer, but even so, it makes the system a nightmare to read through. I considered adding documentation myself, but I would have never been able to keep up with the videos, and I wouldn't have truly understood the code well enough as I was writing it to comment it well anyways. Since the system was developed bit by bit, there were pieces of the code that weren't actually implemented until many hours later. They were only added when they were because the architect of the system knew that those functions would be needed eventually.
I would have liked to see more wrappers to Maya functions. There are arguments against this sort of thing (see the "Wrapping Commands in Maya" post on Sune Kempf's rigging page), but after you've written this for the umpteenth time:

cmds.xform(obj, worldSpace=True, absolute=True, translation=cmds.xform(target, q=True, worldSpace=True, translation=True))

You'll find yourself wishing you had declared a utility function:

utils.matchWorldSpacePosition(obj, target)

Stuff like this would have also made the routines smaller, since they tend to run on (and on and on).

The biggest issue I had with the code though, and the one thing that would really make this system solid, was the problem of information hiding, or more specifically, the lack thereof. Throughout the system, routines and functions made assumptions about the data that was being returned to them. This may have worked, since the assumptions were essentially hard-coded by the system's architect himself, but it made the code nigh impossible to read and follow in places. It also cause random bugs to pop up all over the place.
For instance, in one function, there was a check to see if a spine joint chain had 5 or more joints, and if it did, it could have a certain control rig installed on it. The code to check that condition was:

blueprintJoints = utils.findJointChain(blueprintJointsGrp)
if len(blueprintJoints) >= 6:
    # do stuff

This seems like a typo. It seemed like a typo to me as I wrote it, and it took me a second to remember that the 'findJointChain' function returned the parent group of the joints in index one of the returned array. So instead of checking to see if there were 5 joints in the returned array, you had to add one, in order to account for that group. You'd never guess that was the case from the title of the routine, but this is one of many examples of this sort of thing. A better implementation of this check would be to write a routine that really did only return the joints (the existing routine could continue to return the group as well), and use a named constant instead of the magic number 6:

MIN_JOINTS_IN_SPINE_RIG = 5
blueprintJoints = utils.findOnlyJointChain(blueprintJointsGrp)
if len(blueprintJoints) >= MIN_JOINTS_IN_SPINE_RIG:
    # do stuff

This makes the code much more readable, almost self-documenting.

ALL the learning:

This post is getting crazy long, so I'll try and wrap it up. What did I learn from this project? First, I got an INCREDIBLE amount of experience, writing the code, testing it, and debugging it. Second, I got to see how the whole system fits together. How multiple files and classes interrelate and work together to create an incredible amount of functionality. I learned some cool python tricks and techniques as well, such as using __import__ instead of the usual import when you need to bring in a module that is being stored as a variable, and you don't explicitly know which module it is. I also got to use a bit of vector math, which got me thinking of solutions to problems that stumped me a week ago (matching pole vector locations with a no-flip leg setup? I'll probably write a post about it). I also saw how the complexity of a system grows exponentially, and how the design is so import when it comes to the scope of a project, and determining which features get implemented or not.
Finally, I'd just like to say again, what an undertaking the creators of this system have pulled off. Not just building the autorig, but deconstructing it and rebuilding it in a tutorial format, constructing the system piece by piece, adding functionality and keeping it running and testable at every stage. Kudos to those guys at 3DBuzz. Would I recommend the DVD to others? If you've got the basics of python and scripting for maya down, and you're not intimidated by a challenge -- definitely. You'll learn a ton, I know I did.

Tuesday, July 3, 2012

mr_motionTrail is available now!

The mr_motionTrail utility that I demo at the end of my current rigging reel is now available for download at CreativeCrash! I had some people expressing interest in using it for their own animation work, so I decided to put it online for download. Go get it, it's free!