Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to constrain 3D rotations (Euler)

Tags:

c++

animation

3d

What's the correct/best way of constraining a 3D rotation (using Euler angles and/or quaternions)?

It seems like there's something wrong with my way of doing it. I'm applying the rotations to bones in a skeletal hierarchy for animation, and the bones sometimes visibly "jump" into the wrong orientation, and the individual Euler components are wrapping around to the opposite end of their ranges.

I'm using Euler angles to represent the current orientation, converting to quaternions to do rotations, and clamping each Euler angle axis independently. Here's C++ pseudo-code showing basically what I'm doing:

Euler min = ...;
Euler max = ...;

Quat rotation = ...;
Euler eCurrent = ...;

// do rotation
Quat qCurrent = eCurrent.toQuat();
qCurrent = qCurrent * rotation;
eCurrent = qCurrent.toEuler();

// constrain
for (unsigned int i = 0; i < 3; i++)
    eCurrent[i] = clamp(eCurrent[i], min[i], max[i]);
like image 500
KTC Avatar asked Nov 14 '22 05:11

KTC


1 Answers

One problem with Euler angles is that there are multiple ways to represent the same rotation, so you can easily create a sequence of rotations that are smooth, but the angles representing that rotation may jump around. If the angles jump in and out of the constrained range, then you will see effects like you are describing.

Imagine that only the X rotation was involved, and you had constrained the X rotation to be between 0 and 180 degrees. Also imagine that your function that converts the quaternion to Euler angles gave angles from -180 to 180 degrees.

You then have this sequence of rotations:

True rotation    After conversion    After constraint
179              179                 179
180              180                 180
181             -179                 0

You can see that even though the rotation is changing smoothly, the result will suddenly jump from one side to the other because the conversion function forces the result to be represented in a certain range.

When you are converting the quaternion to Euler angles, find the angles that are closest to the previous result. For example:

eCurrent = closestAngles(qCurrent.toEuler(),eCurrent);
eConstrained = clampAngles(eCurrent,min,max);

remember the eCurrent values for next time, and apply the eConstrained rotations to your skeleton.

like image 200
Vaughn Cato Avatar answered Dec 02 '22 17:12

Vaughn Cato