04 January, 2012

Freedom of a constrained node

When you constrain a transform node using Maya's constraints, it stops inheriting transformations from its top hierarchy. However, if you just connect (for example) translation attributes directly or using simple arithmetic nodes, the connected node continues to inherit transformations from its parent. The difference is because of how a constraint calculates the final output. Let's do a simple experiment.
  1. Create a nurbsCircle and a locator.
  2. Group the locator (group1->locator1).
  3. Now point constrain locator1 by nurbsCircle1 (keep offset option off).

The locator1 should be stuck to nurbsCircle1. Notice that locator1 does not move if you move its parent (group1). Now zero them out, so everything is on the origin. Then move group1 2 units in y-direction. Check the position values of locator1, it changed to (0,-2,0)! So the constraint recalculates position of locator1 to keep it locked to nurbsCircle1. To do this, constraint node uses transformations of nurbsCircle1 and group1(parent of locator1). And the position of locator1 is calculated by converting world-space coordinates of nurbsCircle1 to local coordinates under group1 (parent of the constrained node). This calculation uses 'worldInverseMatrix' of group1 which is the same as 'parentInverseMatrix' of locator1. To get more idea about this have a look at this nice article by Hamish Mckenzie.

Now let's change this behavior. Disconnect this connection, locator1.parentInverseMatrix[0] -> locator1_pointConstraint1.constraintParentInverseMatrix and move group1, you should see that locator1 is moving with its parent now! Both group1 and nurbsCircle1 should affect position of locator1. What happened here is that by disconnecting parentInverseMatrix we stopped constraint node from compensating for movement of group1(parent of constrained node). Or the space in which final coordinates for locator1 needs to be calculated is fixed and not affected by locator1's parent.

That explains the reason behind why after constraining a node it stops inheriting transform values from its top hierarchy.

To make things interesting, let's try the following:
  1. Zero out all the transforms.
  2. Group "group1" (we get  group2->group1->locator1).
  3. connect "group2.worldInverseMatrix[0]" to "locator1_pointConstraint1.constraintParentInverseMatrix;"

What we did here is made point constraint consider group2 as the parent of locator1 instead of group1. So when constraint calculates position for locator1, it will compensate for group2, but not group1. This means that if you move group2, locator1 will not move but if you move group1 then locator1 will move!

Here is a diagram I put together that might be useful in understanding what I wrote. I have just repeated the same thing actually, but in different ways as I understood this.


  1. That's awesome! Works great for setting up local-space to world-space driver interactions.

    I had been using a very convoluted setup of nested hierarchies with direct connections; but this way simpler and seems clean enough.

    I can't believe I hadn't thought of digging into the constraint connections like this before. Nice solution, and thanks for clear explanation!

    1. I am glad you found it useful :) Would be very interesting to see how you are using it.

  2. I have an problem connecting "group2.worldInverseMatrix[0]" to "locator1_pointConstraint1.constraintParentInverseMatrix;"

    Maya does not allow me to do that, can you make an video or upload snapshot so i can get how to connect it.

    and ya this is really great tuto, but i really wish to connect matrix node

    1. What kind of error do you get? I don't see any problem connecting them.

    2. Here is the error which my script editor shows me

      connectAttr -f group2.worldInverseMatrix locator1_pointConstraint1.parentInverseMatrix;
      // Error: The attribute 'group2.worldInverseMatrix[0]' cannot be connected to 'locator1_pointConstraint1.parentInverseMatrix[0]'. //

    3. Your target attribute should be constraintParentInverseMatrix. That should work.

    4. Oh great it works thanks,
      that was my mistake i didn't show constraintParentInverseMatrix

  3. This is cool. I'm currently looking into sticky controls for layered deformations.
    Using this concept would it be possible to have locator1 consider two matrices?
    Grouping nurbsCircle1 (group3), and having locator1 consider group2 and group3, you would be able to stick group3 onto a geometry. Then only when nurbsCircle1 gets translated/rotated, locator1 would move.

    1. Hi Toke,
      I am not sure about the structure of your transforms, so hard for me to tell if it will work. And do you mean locator1 should be affected by two transforms?

    2. heres a video of what I'm trying to achieve in the end: http://vimeo.com/31513345

      here is a maya scene with the setup so far (hope you have maya 2012): https://www.dropbox.com/s/h88v9tpj1l6z6i5/freedomConstraint_example_01.ma

      In the maya scene, locator1 considers nurbsCircle1's parent (group2) so you can transform group2 around freely without the locator1 moving.
      Then when you start transforming nurbsCircle1, locator1 moves as well. But the problem is that locator1 and nurbsCircle1 are in different parent spaces so the transform doesn't relate especially when you have rotated nurbsCircle1's parent.
      So my suggestion would be to be able to orientConstraint locator1's parent to nurbsCircle1's parent, so the parent spaces are the same. And then have locator1 ignore or compensate for its parent space as well as nurbsCircle1's parent space.

      Does this make any more sense?

    3. How about this?
      1. Make a null transform
      2. Point constraint null with group2
      3. Connect null1.worldInverseMatrix[0]->constraintNode.constraintParentInverseMatrix

      Your solution would work as well I believe. But you might want to plan how your on mesh controls are controlling the deformation controls. For example if your joint based facial rig is a blendshape to the main face rig, you can directly connect your controls to the parent of joints without any constraint because you would want only local space changes to reflect on on-face controls.

    4. Sorry forgot to add explanation for steps I mentioned before.

      Basically what that method will do is it will negate only positional transform and not rotational transform. Hence group2 will orient locator but it will not move it.

    5. You're right about the local space changes, but that only works with a single layered approach. When you start layering multiple systems or adding in more blendShapes, the transformation of the on-face controls doesn't relate to the geometry.

      Here is a example of a simple scene: https://www.dropbox.com/s/0yn2yzflxtp3yrq/freedomConstraint_example_02.ma

      The first 50 frames is with a single layered approach, which works really well.
      From 50-80 I add another blendshape to the system, and then the on-face controls don't match the geometry.

      I'm guessing this is because the parent space of offset joint is not that same as the parent space of the controller?

    6. Local space change works with layered approach, but it depends on how you are layering everything. All the local rigs should combine into a final rig that is blendshape to the main rig. The joints on the facial rig don't move unless the local space of controls change. The local spaces would match since on face controls are constraint such that they align on the face corresponding to their joints on the facial rig. It's a little bit involved setup so you have to think through it. I believe Jason Osipa's third edition of Stop Staring has a chapter for on-face controls in case you are looking for a reference.

  4. Thanks for pointing me towards Jasons book. I read it a long time ago, but missed out on that last crucial section.

    After some experiments, I got something working with this approach. The only problem was that since its a constraint, Maya wouldn't let me do it on the same mesh as there is a circular dependency.
    So I had to make a copy of the mesh/skin cluster, which is a bit more expensive compared to Jason's connecting attributes approach.