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
 

15 October, 2011

Eggroll setup update

I meant to post this for a while, but never got around to finish a demo of it. Inspired by Mike Best's eggroll setup, I wanted to do a similar setup because it was a good challenge. After my previously failed attempt I switched to a special matrix(found from google) to calculate rotation of the object around an axis using maya expression. This worked very well and I finally got the rolling working. However I still had some minor problem with small offset from the ground in some positions, it might be the pivot location that might be off. But more challenging is the sliding problem, though it's not very noticeable in the demo. I would like to come back to it later. Deformation of the egg should not be difficult to add to this setup as I am already counting in the bounding box volume in nodes calculation.

Later Mike was kind enough to share the script which gave me insight into his setup. Mine is not as good as his, but I was happy to get the egg rolling :)

This challenge sparked my interest in rigid body simulation. Hopefully in the future I will be able to take time into reading more about it (and begin to understand something!) to write my own little simulator. I am looking particularly for a real egg rolling simulation where the center of mass of rigid body is shifting based on the properties of the fluid (egg yolk and egg white).

Bare essential commands that create Maya's scene

If you open .MA maya file in notepad, you will see many lines of code based on your scene size. If you go through the code, no matter how big the scene is or how many nodes are there, the whole scene is created by only 4 commands! That's it! It's very easy to find this information, but what amazes me is that the programmers were able to abstract the logic of scene creation to the following atomic tasks:
  1. Creating nodes with parenting info
  2. Adding attributes
  3. Setting changed attributes
  4. Connecting the nodes
It is very easy to generate .MA style code by following the example of MA exporter plugin. I am using some parts of it in Rigenerator to save the part of the rig as code. My next goal is to be able to analyze this code and extract metadata information for each line. This will allow me to extract scene graph information by just looking at the code. So I am currently in process of designing the logic for a code parser module which will parse the code to extract node/attribute/connection information.

14 April, 2011

Making Animatable Pivot: Part 2

In the first part, we looked at the basics of how pivot works and what is the main challenge in making pivot animatable. In this part we take a look at the implementation detail to build a system which will calculate movement around given pivot point. Here is the hierarchy I am using for this system.

netPivotShift
|_object
   |_ pivot_loc
   |_ object_rot

Instead of step by step tutorial, I will try to describe main logic of how it works. First, let's examine our goal and see what kind of result we are looking for. Here is an image that shows the initial status and what we expect after rotating the cube. 


If we rotate the cube right now in our current initial state, then what we get is shown in the image below. It's what you would expect since our pivot is parented under the cube. But this is not what we want.


So how do we go from our initial state to the final result? There are different ways to solve this problem. Here, we look at one way to do it, which may not necessarily be the easiest.
First thing we need to keep in mind is that our pivot is parented under the cube, that means our pivot is operating under cube's coordinate space (V.IMP.). Now, let's see the steps to get the final result. 

#1) We move the pivot point so that it lies on the origin. (Keep in mind, this origin is in our cube's space)  
Why do we do this? So we can apply rotation matrix which always rotates around (0,0,0)


#2) Now, we apply the rotation, which will be around the origin by default. 
See it already looks closer to the result we want! 


#3) Then we reverse the first step. We move everything back so that pivot goes to it's original location.
And we have our final result!


The matrix equation to get the above result is:
shift = pivot_loc.inverseMatrix * object_rot.matrix * pivot_loc.matrix
(this can be a bit confusing if compared to original equation in first post, because we are taking a bit different approach here)

Now prepare the network to calculate the object movement around pivot using above matrix equation. Plug object's rotation into object_rot which will be our rotation matrix. Once you have the matrix calculation network ready, plug the final .matrixSum to a decomposeMatrix node's .inputMatrix attribute. What we will get here (in .outputTranslate) is the translation value that the object should move to get the pivoting movement around pivot_loc. Just to test the result you can plug .outputTranslate attribute of decomposeMatrix node to netPivotShift node and see how it works. Give it a try, it's fun! :)

Phew! That took the whole brain out of me! Now, remains the offsetting logic. Let's keep it for the next post to give my brain processor some rest :)

09 April, 2011

WIP: Unsuccessful attempt at rolling an egg(unknown type)


I can think of two ways to implement rolling egg.
1. Use aim constraint to point in the direction of movement and have rolling axis as your up vector. The object can then roll about this up vector. I will implement this approach later.
2. Calculate rolling rotation around the rolling axis using matrix calculations. 
!These approaches do not simulate the actual physics.!
 
I am currently working on 2nd approach. Here are some notes for both the approaches:

# First one will follow a path and will need some work to blend the direction changes so the object orientation does not jump.  
# First method is more like driving a car so the object's front/rear side follows the direction of movement, egg however may or may not follow this kind of movement. 
# Second method adds the rotation on top of the current rotation, so egg starts rolling in the direction of movement without any jump in it's orientation.
# First one will rely on expression only for small amount of work, hence reliable undo operations.
# Second method depends mainly on expression to avoid cycle and use of getAttr, setAttr is required. This leads to non-user friendly undo. 


The main difficulty in 2nd method is in getting the rotation around the rolling axis. I understand the theoretical part of it, but I am having a bit difficult time implementing it. May be I am doing something wrong, but it's hard to debug it at this stage. Here is what I am doing:

- Find the direction of movement
- Find rolling axis using cross product
- Find a matrix difference between so that z-axis of egg rotation matrix matches rolling axis (Diff)
- Do this operation to rotate egg around rolling axis by R
  F = Egg * Diff * R * Diff(-1)
- In above equation we first take the current egg rotation and then multiply it by "Diff" matrix to align z-axis with rolling axis. Then apply the rotation amount around z-axis and then just apply inverse of Diff matrix.

And here is the result.
You can see that in the beginning it rolls fine, but when direction changes rotation does not follow the rolling axis.


03 April, 2011

Making Animatable Pivot: Part 1

First concept here to understand is the rotation operation and how it is affected by pivot point. Here is a post I wrote on my blog last year to understand how pivot works.


First we will look at how rotation around pivot point is calculated in simple steps.
1. Translate the object from its current location to the pivot point location
2. Rotate the object
3. Translate the object back to its place from pivot point location

What the last step does is that it moves the object in the rotated transform axis. Hence it moves the object to the final location which gives the effect of rotation around given pivot point. Mathematically here is what we will do using matrices to get the final result:
F = T * R * T'
Where,
T = translation matrix to move from current position to the pivot position
R = rotation matrix
T' = inverse matrix of T

This was just the first step, in calculating the rotation around arbitrary point. However, the main challenge is to make pivot point animatabale.

If you have rotated the object first and then try to change the pivot location, the object will move as well. What happens here is that the object already had some rotation applied to it based on previous pivot position. Now, we change the pivot location which will cause the final matrix to be recalculated based on existing rotation and new pivot point. This results in object shifting to a new location. This seems quite obvious, but in many cases it is unwanted. Maya deals with this problem by storing an offset value in ".translateRotatePivot" attribute when you are moving the pivot point. This stops object from shifting.
But the problem with Maya's approach is that all the rotation calculations are always based on single pivot location. So even if object does not move while moving pivot point, it does affect all the previously keyframed transform values.

So to solve this problem we calculate the offset at each change of pivot location and then store this offset which can be animated on a pivot control. I will write about this in detail in my next post.

20 March, 2011

Removing multiple items from textScrollList

I have started working more with Maya UI and I can see why people get frustrated sometimes with handling UI interaction using mel. For example, there is no function to get an item at a given index.

Deletion of multiple items from a list or an array is always tricky because, while deleting multiple items the indices are updated automatically. One solution is to start deleting at the highest index so that the indices to be deleted later are always unaffected.

Here is the function I wrote for the extended textScrollList class. Python's built-in function to sort a list comes in handy here.

def removeAt(self, inds):
    """
    Remove item(s) at given indices from textScrollList
    @inds: takes a single int or a list of int
    """
    if not isinstance(inds,list):
        inds = [inds]
       
    # indices should be sorted first into descending order 
    #   so that the indices to be deleted next are unaffected
    inds.sort(reverse=True)
    for i in inds:
        self.edit(removeIndexedItem=i)

14 March, 2011

Rigenerator Update

I have switched back to rigging related work. I have resumed my work on Rigenerator by analyzing my existing code. It can be very helpful to take a break from the work and revisit it later. I found many ideas to improve and more importantly simplify the logic for Rigenerator. Earlier it was not very bad, but I always felt that the code and logic I had written was a bit twisted and difficult to understand. I have put so many comments in the code, but still I was not satisfactory with the logical structure. So I have gone through the code many times as I kept refining it and simplifying the logic.

The first thing I did was to divide the code into proper modules (which I had to do anyway). While doing so I also kept the UI in mind. This will make plugging the UI process easier. There are some changes in logic for exploring the graph. It will allow more possibilities in conditional gathering of graph nodes. Finally I have moved Rigenerator out of my rigging framework project and it is a tool by itself now.

The major changes are in the graph rule structure. It is much more flexible and easy to follow now. The new rule structure allows the following,
# A value can be a node type or a node name
# Neighbor node(s) can be specified for rule condition
# Also, connection attributes can be specified for the rule
# Any value in the above conditions can use simple regular expression (RegEx.)
# Simple RegEx. can be extended in the future to allow more complicated RegEx. syntax
# Rule conditions can be grouped to force AND operation (all conditions should match)

Since I have made a lot of changes, this also requires for more testing. But I am looking forward to finishing the new algorithm and start testing the results!

12 February, 2011

New Year New Adventure

It's been very very long since I posted any updates. I have been working on many projects simultaneously so it's been difficult to find time. I have done facial rigging for a friend of mine and I am also working on my Rigenerator project. For Rigenerator, I have started working on converting code for special nodes into the code that uses node's own command, e.g. cluster. Later I have a plan to add support for Python. But there is a lot to be done before that.

However, I have taken a break from my rigging work and I have started diving into two other development projects, a web app and an IPhone game.
I was already building my website using CodeIgniter framework for php and during that time my wife suggested some great ideas for building a web application. Since then I have become passionate about this idea. So I have started building some parts of the app as a prototype. I am using PHP (with CodeIgniter as framework), jQuery and MySQL to build this web app.  It is going to be a long term project so I will be switching between other projects and this one.
I am also exploring some game ideas for iphone while learning Objective C along the way. There are many aspects to consider when designing a game. A good game should keep users interested from beginning to the end and also a good game users will want to play again and again. I am considering coscos2d-iphone game engine for the game and later plan to add box2d physics engine. This will also help me with my technical animation reel.

Here is another blog I started to take notes about the new projects and programming in general.
http://maulik13tech.blogspot.com