Joint flip from FK rotation

I have a joint (RO XYZ, X primary), constrained (no offset) by a FK controller (oriented the same as the joint, RO also XYZ).
I rotate the controller 45 degrees in Z. The joint matches.
I then rotate the controller 89 degrees in Y. The joint matches.
I then rotate the controller to 90 Y. The joint’s Y matches, but the Z rotation zeroed and moved to the X rotation.

So FK controller is 0,90, 45. The joint is 45, 90, 0.

What’s happening? How do I have the joint match the FK controller?

I can answer the “What’s happening”: The overall orientation of an object can be represented by multiple different sets of rotation values.

A constraint isn’t just a direct connection of values. It asks “Where is the constraining object in the space of the constrained object’s parent?” The math for that is fast if the computer uses matrices, and matrices don’t keep track of the normal xyz rotations. So we have take the matrix that answers that question and turn it back into xyz rotations.

But since there are multiple ways to represent that orientation, we are forced to pick one of them. There are internal rules for which one to pick, but those rules apparently don’t take the original rotation values into account. So its possible to get the same overall orientation, but different xyz values.


Now, as for the “How do I have the joint match the FK controller?” … I don’t know :slight_smile: It’s been too long since I’ve done any real rigging. Somebody else will have to give you an answer for that.

And (as always for questions like this), it’s much more productive if you tell us what you’re trying to do, rather than just asking the technical question.

2 Likes

Matrix nodes and offset parent matrix setups are going to be less susceptible to joint flipping, but can be a bit confusing on first glance, If you are used to parent constraints. Jared Love has a good You Tube channel.

Also Rigging Dojo has a great explanation of the WTF of Maya joint orients.

1 Like

Thank you. What I did as a workaround was direct connect the rotations. Annoying, but so far it’s working.

Even the FK control would experience gimballing when rotated to that extreme, but… have you applied joint orients? If you have non-zero joint rotations that haven’t been applied to the joint orient channels then that might explain why the control and joint aren’t gimballing at the same orientation.

Thank you. The joints are all zeroed.

To provide some greater context:
This is happening for an upper arm joint. There are separate ik and fk joint chains driving the bind skeleton through parent constraints.
This issue is occurring between the fk controller and the fk upper arm joint.
I changed the parent constraint to a point constraint and did direct connections between the rotations of the controller and the fk joint, and that seems to solve the problem, however the problem now is between the fk arm joint and the bind skeleton’s arm joint. It’s actually the same problem as before, so I applied the same solution.
The solution this time is more complicated because of the ik and fk joints sharing control so I had to use a condition node driven by the ikfk blending attribute to directly connect the rotations.

I suppose I’m left wondering why isn’t a parent constraint absolute?
Is this a flaw, or a result of some feature(s) of the constraint I’m not aware of?

Have you confirmed that all 3 joint chains have identical rotations, joint orients, and rotation orders?

Interesting question.
The ik and fk chains are duplicates of the bind chains, but I placed the ik and fk chains in groups for better organization, and the groups match the transforms of the upper arm so the joint orients don’t match. The duplicates have zero joint orients because of the group.
As an experiment, I took the fk chains and the fk controller out of the group, so the fk joint orients match the bind joints’ orients and it still happens.

I think it’s because of what I said in my previous post.
The constraint goes through matrix-math-land and you end up in exactly the same situation as before: Choosing between equivalent matrix → Euler rotation conversions.

Constraints will always go through matrix-land. And the matrix → Euler conversion will always have that issue somewhere. The only thing you can do is build your setups to avoid that issue.

Have you tried other rotation orders? Gimbal lock happens when you rotate the middle axis to 90. Since you’re not rotating in X, you should try putting that one in the middle. Not sure if ZXY or YXZ is correct for you.

1 Like

Yes, changing the rotation order could avoid the issue. The primary is X so I thought that has to remain last, no?

Yeah, you’re right. Like I said, I haven’t done real rigging in a long time, so I came up with the math solution to the one example you gave.

But then there’s something else going on here. And that something else is going to depend on exactly how you set up your rig.

I don’t think you’ve told us what your problem actually is. Yes those values are different, but why does that matter? Are you getting pops? Nonlinear blends? Are you trying to use those values to drive something else?

1 Like

To reiterate what others are saying, there are so many different ways that you could assemble a rig that we’re kind of taking a shot in the dark to guess what might be going on without more detailed information about the rig. That limits how helpful any of our answers can be.

The only mathematically stable way to represent a transform is with a matrix, but they are not easily parsed by humans, so euler rotations make much more sense for the end user. Parent constraints use matrices to do the math under the hood, and then have to decompose those results into translations + euler rotations to drive the node you are using. This rarely results in any issues as long as you’re only constraining to one target at a time. If constraining to multiple targets, you’ll have to take care to make sure the parent spaces of the targets are similar, or you’ll get interpolation issues. I use no offset parent constraints all the time and I have never seen the behavior you are describing, so clearly there is something else happening with your setup. You might want to double-check your constraint targets and their weights.

Typically the setup I use has an FK chain, IK chain, and bind joint chain. The FK and IK chains are constrained directly to controls, and then their translates and rotates are blended together via direct connections through a pairBlend (or similar) node in order to get the final bind translates and rotates. Directly blending the rotate channels between the joint chains instead of constraining the bind joints is more stable than using orient constraints with multiple targets.

But there are a few things that have to be set up correctly for that to work.

  1. Joint orients all have to be applied / zeroed
  2. The world transforms AND local transforms of the joints have to be the same between all the chains.
    a. This means that if you group the FK or IK chains, the group’s transform has to match the transform of the bind chain’s parent transform.
  3. After constraining to controls, your FK and IK chains should have identical transforms when at rest
  4. Any nodes that have rotations directly connected (every joint chain in this example) need to have the same rotate order. It doesn’t matter what it is, as long as they are the same. Otherwise the blend result won’t be interpreting the rotations the same. This isn’t necessary for constrained nodes.

Apart from those things I’m not sure it’s going to be possible to help without seeing your rig hierarchy, constraint settings, etc.

1 Like

Yes, flipping Z rotations to X rotations causes a noticeable pop.

For now, the simplest fix is changing the rotation order to YXZ.

I appreciate the extensive and detailed reply. I definitely have been doing 2a incorrectly so thank you for that information. That makes total sense and I feel silly for not realizing that before. I may try a test with changing the grouping so that the transforms match.