parentConstraint with maintainOffset flips object 180 degrees... and workaround

Just a heads up, in case anybody else has been frustrated by this:

Sometimes when doing a parentConstraint with maintain offset ON, the constrained object will be flipped 180 degrees.

I’ve found, though, that (at least in my test cases) if you apply the parentConstraint to the current selection, instead of explicitly supplying arguments in a script, it works correctly.

Here’s some sample mel code:

// 1) Doing this will end up flipping the pyramid upside down

file -new -f;

createNode transform -n “L_leg_ikCtrl”;
setAttr “.r” -type “double3” 180 0 0 ;
setAttr “.ro” 3;

polyCone -sx 4 -name “L_leg_ikCtrl_cone”;
parent “L_leg_ikCtrl_cone” “L_leg_ikCtrl”;

createNode transform -n “transform2”;
setAttr “.r” -type “double3” -77.853031303165153 12.12112240316919 -87.412239779165134 ;
setAttr “.s” -type “double3” 0.99999987813066582 0.99999970476198963 0.99999976145619462 ;
setAttr “.sh” -type “double3” -3.5036528336529942e-08 -3.2133419985118477e-09 1.1636280827992151e-09 ;
createNode joint -n “L_foot01_skinJnt1” -p “transform2”;
setAttr “.r” -type “double3” -1.3716142063811415e-14 -1.2762596861458585e-14 -5.2677936911739486e-15 ;
setAttr “.ro” 2;
setAttr “.s” -type “double3” 0.9999997615814209 0.9999997615814209 0.99999970197677612 ;
setAttr “.jo” -type “double3” 84.368437919533207 -10.468423747497605 -73.90108294829956 ;
setAttr “.ssc” no;
setAttr “.is” -type “double3” 1 0.99999994039535522 1 ;
setAttr “.bps” -type “matrix” 0 -4.6905289999999997 8.8317010000000007 0 10 0 0 0
0 8.8317010000000007 4.6905289999999997 0 13.9152 9.1014099999999996 -11.1616 1;

parentConstraint -mo L_foot01_skinJnt1 L_leg_ikCtrl;

// 2) Doing this will work correctly, even though the only thing that’s different is that this version is
// using the current selection, as opposed to explicitly supplying the arguments.

file -new -f;

createNode transform -n “L_leg_ikCtrl”;
setAttr “.r” -type “double3” 180 0 0 ;
setAttr “.ro” 3;

polyCone -sx 4 -name “L_leg_ikCtrl_cone”;
parent “L_leg_ikCtrl_cone” “L_leg_ikCtrl”;

createNode transform -n “transform2”;
setAttr “.r” -type “double3” -77.853031303165153 12.12112240316919 -87.412239779165134 ;
setAttr “.s” -type “double3” 0.99999987813066582 0.99999970476198963 0.99999976145619462 ;
setAttr “.sh” -type “double3” -3.5036528336529942e-08 -3.2133419985118477e-09 1.1636280827992151e-09 ;
createNode joint -n “L_foot01_skinJnt1” -p “transform2”;
setAttr “.r” -type “double3” -1.3716142063811415e-14 -1.2762596861458585e-14 -5.2677936911739486e-15 ;
setAttr “.ro” 2;
setAttr “.s” -type “double3” 0.9999997615814209 0.9999997615814209 0.99999970197677612 ;
setAttr “.jo” -type “double3” 84.368437919533207 -10.468423747497605 -73.90108294829956 ;
setAttr “.ssc” no;
setAttr “.is” -type “double3” 1 0.99999994039535522 1 ;
setAttr “.bps” -type “matrix” 0 -4.6905289999999997 8.8317010000000007 0 10 0 0 0
0 8.8317010000000007 4.6905289999999997 0 13.9152 9.1014099999999996 -11.1616 1;

select -r L_foot01_skinJnt1 ;
select -tgl L_leg_ikCtrl_cone ;
parentConstraint -mo -weight 1;

I’ve had this issue on facial rig setups I’ve made, it was super annoying, but I didn’t make the constraint with scripting and still had the issue and it was pretty inconsistent.

Just thought I should point out that the second case isn’t exactly the same as the first, since you’re constraining a different object. Not sure that’s got any bearing on the difference in results, or if it’s just a local typo.

parentConstraint -mo L_foot01_skinJnt1 L_leg_ikCtrl;

select -r L_foot01_skinJnt1 ;
select -tgl L_leg_ikCtrl_cone ;
parentConstraint -mo -weight 1;

chargrin

You’re right, of course… and here I was coming back to report that my magic fix wasn’t so magically after all. I guess that explains it.

Anybody else been able to come up with any workarounds?

The work around is make sure that your two objects match transforms, the 180 flip happens when the target and source object are in two very diffrent parent spaces.

Creating a matched node as the parent of my target object is how I get around this if it is the same bug.

Hmm, good to know bclark. Unfortunately, in my particular situation, I’m not really free to alter the parenting of either object. I ended up ‘calculating’ the offset myself - if anybody’s curious, the basic idea was something like:

  1. duplicate the transform we wish to constrain - we will use this as a reference for the “original” orientation

  2. Do a normal parent constraint w/ offset, check if orientation changed a “significant” amount (do this instead of always calculating offset ourselves, because if maya calculates it correctly, it’s faster)

  3. if it’s not right, zero out the offsets for the parent constraint, then create a another reference transform as a child of our constrained object

  4. constrain our new child transform to the original orientation - then save it’s rotation values, and delete both reference transforms

  5. set the offset for the parent constraint to our saved rotation values

Since I’m still using maya constraints to get the offset, instead of doing the appropriate math myself, there may be situations in which this still fails… but it seems to be working for me thus far.

I had an issue with an orient constraint for the fore arm joint and the wrist control. the x rotation was as I expected but when the wrist moved other ways it would flip the forearm by 180. So I used a locator and connected the x rotation to the wrist controller then used the locator for the orient constraint of the forearm. This ensured I didn’t get any y or z rotation data from the wrist controller

I am also having the same issue and it seems like calculating my own offset will be the solution. My current rigging solution is very modular so hierarchy modification isn’t an option. When I perform the same operation manually I don’t get the flip. I find this very curious. I also notice that the offset values are different between the manual and scripted version, however this could be caused by an order of operations thing. I will provide more information when I decide on a solution.

I just ran into this issue writing a custom constraint tool (sorry if I’m resurrecting an old thread). I would agree. Setting your own offset is the answer. I was able to get the correct rotation offsets by using some temp groups. Since constraint offsets are relative to the driver of the constraint, I used two temporary transforms in a parent-child hierarchy to match the transforms of the driver and the passenger and extract the rotation values to use in the offset.
[ol]
[li]Create two empty groups. [/li][li]Set the rotate order of your ‘parent’ group to match the ‘driver’ object’s rotate order.[/li][li]Set the rotate order of your ‘child’ group to match the ‘passenger’ object’s rotate order.[/li][li]Parent the ‘child’ group to the ‘parent’ group.[/li][li]Place the parent group at the same position of the driver object. Use the cmds.delete(cmds.parentConstraint(driver, parent_group)) matching technique. Other matching techniques may not work. (matrix matching will not get the results we want)[/li][li]Place the child group at the same position of the passenger object. cmds.delete(cmds.parentConstraint(passenger, child_group))[/li][li]The child group’s transforms are now relative to your driver object. Store the rotation values from the child group, these are your rotation offset values for your constraint.[/li][li]Create your parent constraint with ‘maintainOffset=True’. Edit the rotation offsets to match the values retrieved in the previous step.[/li][li]Don’t forget to delete your temp groups.[/li][/ol]
I hope this helps someone else out there struggling with this issue.


Marcos

I have this problem sometimes when I want the controls on the right side to be mirrored to the left. The i need the controls local space group to be “scaleX = -1” or similar. This flips the joints when I orient, or parent constraint the mirrored control to the joint.

I solve this problem by just adding an extra node, which will be controlled by the driver object and controlling the driver object. So for example: Driver object (in my example control) -> Parent Constraint -> Extra node (example locator) -> parent constraint -> target object (in my example joint).

I had this problem and I was able to solve it simply by changing the interpolation type on the constraint from Average to Shortest, I’m sure that will help solve the 180 degree rotation some of you are having.

Edit: check out this post I made on here a few months ago, it includes a YouTube video which demonstrates the problem I was having which might be similar to the problem some of you are having.

-Harry

A year later and I have more info…

Since we’ve been able to update parent constraint offsets via the update button in the attribute editor (I don’t know which version of Maya introduced this), another solution lay here in wait.

If you create a parent constraint and you get a flip due to different parent spaces (the dreaded neg scale space), just update the offsets and the flip corrects itself.
Try this:

from maya import cmds

sel = cmds.ls(selection=True) # select a driver and passenger first of course
cmds.parentConstraint(sel[0], sel[1], maintainOffset=True)
cmds.parentConstraint(sel[0], sel[1], edit=True, maintainOffset=True) # the equivalent of the offset 'Update' button

I would’ve thought that you would have to capture the result of the parent constraint command to get the parent constraint node and then use it in the parent constraint edit command, but it turns out that this is how it’s done.

No need for intermediate nodes or self calculations of offsets. Maya can do it if nudged a little. I hope this helps anyone who has run into this issue. Now I need to rewrite some code…


Marcos