20 December, 2011

Rigenerator: A Short Introduction

I am updating Rigenerator slowly, but steadily with exciting updates coming soon. Before I post any updates I thought it would be a good idea to post some introduction about this tool.

At a basic level Rigenerator is a tool to regenerate rigs in Maya. It allows you to gather information about selected rig by doing advanced traversal through Maya's scene graph. After gathering information and generating metadata the tool allows you to see what (dag & dg) nodes are part of your rig and then generate MA style code to reproduce the same rig in a single click. However, this is just the basic framework. We can build on top of this to add some really helpful and advanced features. The main features of Rigenerator are listed below.
  • Extract the part of the rig 
  • Rename and regenerate the rig using MA style code
  • Mirror the extracted rig
  • Abstract the rig code into a generalized module
At the core of the framework is a module for traversing maya scene graph with rule based conditions to extract a part of the rig. So you can select just the root nodes of your rig, for example left arm rig, and the rig analyzer will gather all the nodes involved in that rig. So if you have stretchy IK, the rig analyzer will find all the utility calculation nodes involved in the rig as well. There are lots of things to consider when analyzing a rig by traversing a scene graph. One major issue to tackle is to solve cyclic dependency if the nodes are connected in a circular way. Right now Rigenerator's graph analyzer is able to avoid it for the most part and solve it in the post analysis phase for simpler dependencies.

The next module is closer to MA exporter plugin, but with more functionalities. There are few things about maya Ascii code that are not helpful if you want to execute it directly in Maya. For example, -ci (cache internally) flag in addAttr will generate warning. And the major problem comes with all keyframe nodes as mentioned here. Apparently, MA file uses attribute ".ktv" to set key value pairs which cannot be used outside saved MA file because that attribute is configured to work only when reading MA file by Maya. So there are some challenges that can only be solved iteratively as they are discovered. To solve such problems and to support the fourth feature on the list, I have started writing a module that will parse MA commands and based on rules a set of MA commands will be replaced by regular mel commands. 

Mirroring a rig is another project. First I had to learn the logic behind mirroring transforms and orientation. Instead of using mel commands I decided to write functions to mirror transform values giving me the flexibility of playing with any numbers and not just maya nodes. For example, transformGeometry node stores frozen transform values in a matrix attribute ".txf". To decompose or mirror its value I need to feed this attribute to matrix class and use matrix methods. Mirroring logic is actually quite simple once you figure out the logic and math behind it. So I took some time to understand Transform matrix and vector math to be able to code them in python classes and then use them to mirror orientation. I will keep the details for another post. However, these are still very simple things compared to generalization of mirroring any rig. There are many challenges involved in generalized mirroring, some of which includes mirroring constraints based on how other objects are mirrored, using connection sequence to predict the mirroring of custom attributes etc. I believe that it's going to be very difficult to create a perfect solution that always mirrors the rig automatically as intended by the artist/rigger. However we should be able to provide a toolset and workflow to the users so they are able to specify inputs/settings and get the mirrored rig as they want.

Once I finish the brain tearing task of mirroring logic, I want to take Rigenerator further to convert MA style code into proper mel commands. For example if you create a constraint in the rig, MA code actually creates constraint by manually creating the node, setting all the attributes and connecting the constraint node to its input and output nodes. My goal is to convert all these MA commands into just a couple of lines of code by utilizing maya's built-in commands, e.g. pointConstraint, aimConstraint etc. Now that's actually very difficult as I have come to realize after thinking about the logic for a while (say around a year). The most difficult task is to come up with rules for each type of node and specifying logic for each node. The second part makes it very difficult to generalize the main logic. However, by iterative process if I am able to create a database of such rules and some toolset to make the rule/logic generation easier then it should be possible to accomplish this. What this means is that you actually don't have to write the code for your rig from scratch! You get a ready function with inputs to create your rig and you just need to optimize the code! I would love something like that and that's why I am very excited to build this functionality.

In summary, what I want to accomplish using Rigenerator is to allow riggers to focus more on inventing and experimenting in the rig creation process and not worry about reproducing it. Hopefully when the tool becomes stable enough I would like to release it for free and later make it open source when the code is a bit more cleaner (easily readable to others :) ). Stay tuned!

10 December, 2011

Some python goodies

I love coding in python and every time I work with it I feel enthusiastic about this programming language. While coding it is also exciting to learn new features and figure out what you can do with it. Following are some useful snippets that I am using in my rigging framework. 

1. Assign values from a list that is shorter or longer than the original list
I find this simple function handy since this allows me to assign values from a list that is shorter or longer than the target list. It acts like partial assignment, leaving elements intact in my original list if source list is shorter.
def assignValues(list1, list2):
    mapFunc = lambda a,b: a if b is None else b
    origLen = len(list1)
    list1[:] = map(mapFunc, list1, list2)[:origLen] 

tgtList = [1,2,3,4]
srcList = [0,0]
assignValues(tgtList, srcList)

2. Overriding [] (indexing) in python for 2d indexing We know that we can use __getitem__ to assign an array like behavior (or make object iterable). But how about making object behave like 2 or 3 dimensional array? You can actually pass tuple as a key to __getitem__  and then use unpacking feature of python. I found the following code online on activestate recipe website.
def __getitem__(self, (row, column)):
    return self._data[row][column]

3. Some great recipes on python website Some great code recipes and a very useful module.  http://docs.python.org/library/itertools.html#recipes