25 January, 2012

Notes: Building a multi-way (group) constraint

I have been highly inspired by the work that Andrea Maiolo and Tim Naylor did on multi-way constraint system, particularly what they presented at Siggraph 2006 on "Bi-Directional Constraining". I think I first saw their Siggraph work when I was at school in 2008 and that's what got me into understanding Maya's DG and matrix mathematics for 3D transformations. Since then I have tried to solve this kind of constraint system from time to time and I always got stuck as I didn't have enough knowledge at that time. Between 2008 and 2011 I have learned many things by challenging myself to new problems and also from inspirational work of talented technical artists. So I have picked up this project again and I think I have made some progress compared to my previous failed attempts. However, the progress it not hurdle free. Here are some notes on the issues I have encountered.

The challenges:
The first challenge we encounter when building a two-way constraint is the cyclic dependency. However, that's not really the main and only issue. It is possible to create connections and do calculations in such a way that we can avoid this cyclic dependency. The real challenge is that we can't really have true Bi-directional constraint (I say true for lack of a better word). When I started, my idea of a bi-directional constraint was that both nodes involved are the masters and both nodes affect each other at the same time without any switching. Based on this definition the main question is how to interpolate from one state to the other when both objects are moving each other. This leads us to another question, what should be the sequence of operations when calculating for the goal state? This is an important question and more so because the final state is the sum of transformations of both nodes. 

Sequence dependency:
One of the most important properties of rotational transformations is that the operations are non-commutative. We already know this based on what we know about rotation orders. Let's take an example. Let's say we have two transform nodes A and B in their initial state set apart by some distance with zero rotations on both. Let's assume that both A and B affect each other (bi-directionality). Now we apply 90 degree rotation in z-axis on both nodes. Now based on the sequence of operations we get different locations in the end result as illustrated below.

Main problem here is that we are trying to treat both nodes A and B as masters at the same time. If we consider only one node as the master at a given point of time, we can avoid the problem of sequence dependency since only one node affects the others at a given time. ExoSwitch constraint uses a concept of driver nodes and driven nodes. Using this concept, we assign one driver for the constraint system at a given point of time to drive all the other nodes. So ExoSwitch constraint does not have a problem of finding the right sequence at a single point of time.

I can't think of any simple way to record a sequence in which a user is manipulating the nodes involved in a multi-way constraint. However, I think it should be possible to implement a system where all the nodes are treated as masters (or drivers) at the same time. One idea would be to have some kind of iteration based solver that calculates the interpolation to reach the goal state when all the nodes are driving one another. Maya's FBIK comes to my mind, but it seems that it takes a bit different approach. This approach is to solve the system when user moves one of the nodes(effectors) and update all the nodes with their final coordinates. When you animate these coordinates on effectors, each node is interpolated independently. Even though it works for FBIK, this behavior is not quite desirable for a multi-way constraint system.

Still a long way to go for finishing a working multi-way constraint. I always get more hopeful when I solve a problem on the way. But I think I should look forward to the next problems on my path and be ready to challenge my small brain for some exercise :)


  1. check out this thread...

    there is a link to an autodesk master class that is pretty much about this topic.

    1. I have seen that thread, I think it's great work. Thanks for mentioning that master class. I saw it some time back, but don't remember the details. I will check it again. I am taking a different approach and creating a constraint node based on concept of having a master node in a group, like exo-swith constraint. But if I am successful in a couple of things I am trying then, I think I will be able to build some nice features. Let's see how it goes :)
      Thanks for stopping by. You have posted some very useful stuff on your rigging blog.

    2. I started the thread on TAO. So we are working on a similar problem I guess! :)
      I am looking forward seeing some progress of yours!

    3. It seems like it :) I think your solution is looking great. I have done some theory testing and seems it would work as long as I can implement using APIs.

  2. I've just started thinking about this too. With the generalized approach you basically push the source object into the targets transform either by just setting it the targets transform or setting its transform as the targets transform * the inverse of the sources parents (if it has one) - and then just add the offset and any addition offset back on.

    With multi-directional constraints you have to think i think in terms of 'weakly' known of each target to one another and only when there directly known about pull them into the chain of transforms. I will think about this some more..

    A < ? > B

    1. It would be nice to see what you come up with. I have been thinking about it for some time now and it seems that the transform concatenation would not work as it depends highly on the sequence. Initially I thought of finding a way to get to the final transforms by some kind of complex calculations, but it turns out the problem becomes much simpler by thinking about only one master at a time. I am coding the plug-in right now and I find what's more challenging is to keep the DG evaluation proper while updating all the offsets.

      Do post some notes on your blog :)

  3. you guys do know of this right? http://exotools.net/ExoSwitchConstraint/Features/Features.html

    1. Yes brad. That's the primary inspiration for what I started. I tried it when it was in beta. I think it's an awesome plugin. But there are few things that I wish could be done differently. And if I am successful in my method it should address it. I am planning to post notes from different methods.

  4. The other thing is going to be dependency tracking when keying the switch and cleaning the offset between multiple constrained objects.

    If you have for example A constrained to B at frame 10, then B to A at 20 then A back again to B at 30 - if you've moved any of the constraint keys or the space of the system your'll get a pop.

    Now with cleaning this i.e fixing the pop how do you approach it? Fixing A's space at frame 10 will break the offset of B at 20 and so on...

    So what may be the best approach is basically a while loop - you constantly store the offsets of the dependencies and keep fixing them as they go out of sync. This could be a persistant thing but might be better a button that constantly runs through the constraints until they have no more offset differences.

    1. Exactly. To fix the pop I am using callbacks in API to listen for change in animation for individual animation nodes that are linked to inputs. I just published some update about this issue in the new post. So handling animation and fixing pop when animation changes is next on my list. I should start on it once I am back.