How to constrain 3D rotations (Euler) - c++

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]);

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.

The problem here is that the constraints you are applying have no relation to the rotation be applied. From a conceptual point of view this is what you are trying to achieve:
assume a bone is in an unconstrained state.
apply rotation
has bone exceeded constraints? If yes, rotate it to where it is not constrained any more
Your code that clamps the Euler rotations is the part where you rotating the bone back. However this code ignores the original bone rotation so you will see odd behavior, such as the snapping you are seeing.
A simple way to work with this is to do this instead:
assume a bone is in an unconstrained state
apply rotation
test if bone exceeded constraints
if yes, we need to find where the constraint stops movement.
halve the rotation, apply it in reverse
is the bone exceeding constraints? If yes go to 1
If no, halve the rotation, apply it in the forward direction. Goto 2
keep doing that until you are within some tolerance of your constraining angles
Now this will work, but because your rotation quarternions is being applied on all angles, the rotation will stop when any one of those constraints are net, even if there is freedom some where else.
If instead you apply rotations independently of each other, then you will be able to reliable use your clamping or the above technique to honor constraints, and also rotate as closely to your target as you can.
-
-

Related

OpenGl rotations and translations

I am building a camera class to look arround a scene. At the moment I have 3 cubes just spread arround to have a good impression of what is going on. I have set my scroll button on a mouse to give me translation along z-axis and when I move my mouse left or right I detect this movement and rotate arround y-axis. This is just to see what happens and play arround a bit. So I succeeded in making the camera rotate by rotating the cubes arround the origin but after I rotate by some angle, lets say 90 degrees, and try to translate along z axis to my surprise I find out that my cubes are now going from left to right and not towards me or away from me. So what is going on here? It seems that z axis is rotated also. I guess the same goes for x axis. So it seems that nothing actually moved in regard to the origin, but the whole coordinate system with all the objects was just rotated. Can anyone help me here, what is going on? How coordinate system works in opengl?
You are most likely confusing local and global rotations. Usual cheap remedy is to change(reverse) order of some of your transformation. However doing this blindly is trial&error and can be frustrating. Its better to understand the math first...
Old API OpeGL uses MVP matrix which is:
MVP = Model * View * Projection
Where Model and View are already multiplied together. What you have is most likely the same. Now the problem is that Model is direct matrix, but View is Inverse.
So if you have some transform matrix representing your camera in oder to use it to transform back you need to use its inverse...
MVP = Model * Inverse(Camera) * Projection
Then you can use the same order of transformations for both Model and Camera and also use their geometric properties like basis vectors etc ... then stuff like camera local movements or camera follow are easy. Beware some tutorials use glTranspose instead of real matrix Inverse. That is correct only if the Matrix contains only unit (or equal sized) orthogonal basis vectors without any offset so no scale,skew,offset or projections just rotation and equal scale along all axises !!!
That means when you rotate Model and View in the same way the result is opposite. So in old code there is usual to have something like this:
// view part of matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotate3f(view_c,0,0,1); // ugly euler angles
glRotate3f(view_b,0,1,0); // ugly euler angles
glRotate3f(view_a,1,0,0); // ugly euler angles
glTranslatef(view_pos); // set camera position
// model part of matrix
for (i=0;i<objs;i++)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(obj_pos[i]); // set camera position
glRotate3f(obj_a[i],1,0,0); // ugly euler angles
glRotate3f(obj_b[i],0,1,0); // ugly euler angles
glRotate3f(obj_c[i],0,0,1); // ugly euler angles
//here render obj[i]
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
note the order of transforms is opposite (I just wrote it here in editor so its not tested and can be opposite to native GL notation ... I do not use Euler angles) ... The order must match your convention... To know more about these (including examples) not using useless Euler angles see:
Understanding 4x4 homogenous transform matrices
Here is 4D version of what your 3D camera class should look like (just shrink the matrices to 4x4 and have just 3 rotations instead of 6):
reper4D
pay attention to difference between local lrot_?? and global grot_?? functions. Also note rotations are defined by plane not axis vector as axis vector is just human abstraction that does not really work except 2D and 3D ... planes work from 2D to ND
PS. its a good idea to have the distortions (scale,skew) separated from model and keep transform matrices representing coordinate systems orthonormal. It will ease up a lot of things latter on once you got to do advanced math on them. Resulting in:
MVP = Model * Model_distortion * Inverse(Camera) * Projection

Quaternion reaching gimbal lock

In attempt to avoid angle lock when performing rotations I've tried to switch over to Quaternions. Somehow, I'm still managing to reach gimbal lock.
I'm not sure if its due to the math I've implemented, or a design error, so please point out if I should change my approach for my object coordinates.
Each of my objects hold an X,Y,Z value, and a pitch,yaw,roll value.
When I change a rotation value, the object recalculates its vertices based on the above information. This logic is as follows:
// vertex array
vertices[x] -= /*Offset by origin point*/;
// Quat.'s representing rotation around xyz axes
Quaternion q1 = Quaternion(glm::vec3(1,0,0),pitch);
Quaternion q2 = Quaternion(glm::vec3(0,1,0),yaw);
Quaternion q3 = Quaternion(glm::vec3(0,0,1),roll);
// total rotation
Quaternion TotalRot = ( (q3 * q2) * (q1) );
// conversion of original coordinates to quaternion
Quaternion Point1 = Quaternion(0, vertices[x].x(), vertices[x].y(), vertices[x].z());
// resulting rotated point
Quaternion Point2 = Quaternion( (TotalRot * Point1) * TotalRot.inverse() );
// placing new point back into vertices array
vertices[x] = QVector3D(round(Point2.v.x),round(Point2.v.y),round(Point2.v.z));
vertices[x]+= /*Undo origin point offset*/;
"vertices[]" is the objects vertex array. The origin point offset commented out above is just so that the object is rotated around the proper origin point, so it is shifted relative to 0,0,0 since rotations occur around that point (right?).
I have a pictorial representation of my problem, where I first yaw by 90, pitch by 45, then roll by -90, but the roll axis became parallel to the pitch axis:
Edit:
I tried multiplying those 3 axis quaternions together, then multiplied by a 4x4 matrix, followed by multiplying that by my vertex point, but I still gimbal lock/reach singularity!
Quaternion q1 = (1,0,0,pitch);
Quaternion q2 = (0,1,0,yaw);
Quaternion q3 = (0,0,1,roll);
Quaternion qtot = (q1*q2)*q3;
Quaternion p1(0, vertices[x].x(), vertices[x].y(), vertices[x].z());
QMatrix4x4 m;
m.rotate(qtot);
QVector4D v = m*p1;
vertices[x] = QVector3D(v.x(),v.y(),v.z());
Your issue is that even when you're using quaternions, you're still storing three pitch, yaw, and roll values, rather than a quaternion, to represent an object's orientation.
Here's how you should use quaternions for rotation here:
Instead of storing X, Y, Z, pitch, yaw, roll for each object, instead store X, Y, Z, orientation
in each object, where orientation is a quaternion starting at the initial value (0, 0, 0, 1), meaning no rotation. Storing pitch, yaw, and roll for each object is vulnerable to singularities (gimbal lock) because as small changes are added, one of the intermediate rotations (say, a pitch) could result in the object being parallel to a rotation axis (say, the yaw axis), so that the next rotation about that axis could fail.
Then, as an object is rotated, determine the pitch, yaw, and roll for that object that occurred during that frame (assuming that your input device provides the rotation in that form), convert it to a quaternion, then pre-multiply that quaternion into the object's orientation quaternion. This approach is less vulnerable to singularities because the changes in the rotation are expected to be very small each frame.
Don't modify the object's X, Y, and Z (your verticies array) directly after changing the orientation. Instead, when an object's orientation changes, create a new rotation matrix to serve as part of the object's world transformation matrix (along with scaling and translation; for best results, calculate the world transform as translation * rotation * scaling).
Every few frames, you should normalize the orientation quaternion to avoid undesirable changes in the orientation, which can happen due to rounding error.
If you take a Euler angles representation and turn it into quaternions just for doing the vector rotations, then you are still just using Euler angles. The gimbal lock problem will remain as long as you have Euler angles involved anywhere. You need to switch completely to quaternions and never go through a Euler angles representation anywhere at all if you want to completely eliminate that problem.
The basic way to do it is that you can use Euler angles at the far ends of your calculations (as the original input or as the ultimate output) if that is more convenient for you (e.g., Euler angles are often a more "human-readable" representation). But, use quaternions for everything else (and you can convert occasionally to rotation matrices, because they are more efficient for rotating vectors), and never do intermediary conversions to any of the "bad" rotation representations (Euler angles, axis-angles, etc.). The "gimbal-lock-free" benefits of quaternions and rotation matrices only apply if you stick with those two representations throughout all your calculations.
So, on one side, you have the singular representations (the formal term for "gimbal lock" is singularity):
Euler angles (of any kind, of which there are 12, by the way); and
axis-angle.
And on the other side, you have the singularity-free representations:
quaternions (more efficient memory-wise and for composition operations); and
rotation matrices (more efficient for applying rotations to vectors).
Whenever you operate (like putting several rotations together, or moving a rotating object) with a singular representation, you will have to worry about that singularity in every calculation you do. When you do those same operations with a singularity-free representation, then you don't have to worry about that (but you do have to worry about the constraints, which is the unit-norm constraint for quaternions and the proper orthogonality for rotation matrices). And of course, any time you convert to or from a singular representation, you have to worry about the singularity. This is why you should only do the conversions at the far ends of your calculations (when entering or leaving), and stick with the non-singular representations all through your calculations otherwise.
The only good purpose for Euler angles is for human readability, period.

Creating constraints for aTransformation Matrix

In a 3d space I have a 3d object which I am rotating using a transformation matrix. The transformation matrix is 4x4 but I am just using the rotation part of the matrix. I want to add constraints to the rotation for example the object can only rotate in the z direction for 20 degrees. I know the following but when I add manual constraints such as angle cant be larger than 20 i get scaling and skewing in my object.
To summarize my question how can I add constraints to a transformation matrix?
The short answer, you should add constraints to your Euler Angle representation.
If you keep rotation only in matrix form, than convert it to Euler Angle representation, apply constraints and convert Euler Angle to matrix form.
NOTE: Your Rx Ry Rz representation is called "Euler Angels"
http://en.wikipedia.org/wiki/Euler_angles. There is a many ways to combine rotations about orthogonal axes. code for all conversions can be taken from
http://tog.acm.org/resources/GraphicsGems/gemsiv/euler_angle/

C++: Rotate vector around normal of plane

I'm trying to rotate a point on a plane around the normal of the plane with a certain angle (so it stays on the plane).
For example:
Point = (0,0,1) (on the plane)
Normal = (0,1,0)
Angle = 33 degrees
But can't seem to figure out how to do it
EDIT:
The axis of rotation always passes through the origin (0,0,0)
If you're looking for axis-angle rotations in 3-space, Rodrigues's Rotation Formula is very useful. The Wikipedia page is pretty good: here
Probably not optimal, but: find the span vectors of the plane (call them U and V), express the point P in terms of U and V and apply 2D rotation. PS: a normal does not fully define a plane; you need at least a point in the plane in addition.
To compute the rotation matrix you want, you will need a bit of linear algebra. There is an article on Wikipedia which explains what you need to do.

How to know that object has been rotated only in 15 degree chunks in 3D?

I have an 3D object in 3D space oriented in some orientation. How can I know it has been rotated from its initial rotation q (in euler angles: 0, 0, 0) only in arbitrary number of 15 degree rotations in any axis. E.g, rotated in X-axis 4*15 degrees, and in Y-axis 7*15 degrees and in Z-axis 13*15 degrees. How can I detect this?
I have tried to take euler angles from the object's rotation matrix but it seems to detect the situation correctly only if I rotate only in one axis. If I start to mix X,Y and Z axis 15 degree rotations, the euler angles gets messed up.
I am 100% sure that the object has been rotated only in 15 degree chunks.
Rotation matrices can only be decomposed into quaternions, I believe, and cannot be decomposed into per-axis rotations.
I am not sure what math library you are using, but, if provided, you could use quaternions, which will quite straightforward the functionality you want. They can be translated to rotation matrices afterwards.
Rotations are not commutative, therefore describing them with Euler angles is problematic if you don't know the right order. If you know the order, you can set up an equation system with a multiplication of three rotation matrices (parameterized by the rotation angle) on one side and the final rotation matrix on the other side. Gives you three equations and three unknowns. But watch out for singularities in Euler angle representation!
If you can, the dead simplest way is to store both the components (float3 m_Translation, m_Rotation, m_Scale) and a matrix that is updated whenever they change (float4x4 m_Matrix). To get the data out, simply grab the vector for that part and return it. The memory use is small (12-16 floats) and computational overhead is minimal (recalculate the matrix when a part is updated).
However, if that can't be done, Google suggests you can decompose rotation matrices with a bit of work like so. It does take a chunk of math.
As noted in that page, the D3DX libraries also have a function to decompose matrices: D3DXMatrixDecompose.