Sign in to follow this  
polyfrag

Matrix to Euler (again...)

Recommended Posts

Hey, I posted a few weeks ago asking about how to take a set of axis vectors (view vector, side vector, up vector) and get the Euler angles from them (and I was answered that I should setting up an orientation/rotation matrix and extracting Euler angles from it). Well, I followed the instructions on http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm and I succeeded... it gave me the Euler angles, but the attitude and bank were mixed up. Instead of rotating my character in my game up and down when I moved the mouse up and down, it rotated it left and right (rolled it instead of pitching it). So to fix it I just tried substituting the attitude and bank when I rotated... but its not so simple, because the rotations are interconnected and when I subsitute the X-rotation with the Z-rotation it gets messed up because of the Y-rotation... Anyways... long story short, I tried a bunch of different things and couldn't get it to work, I think it might've been because the order of rotations on the site was Heading then Bank then Attitude (instead of mine, which was X, Y, Z), but I got the exact same behaviour (having it roll it instead of pitching) when I finally figured a way to do it myself (I figured out my own method) that had the right order... So... I hope the wise math/geometry gurus on this website know what I'm talking about without even needing an explanation... and if not... then is there another site that shows you how extract Euler angles from a matrix if I rotate in the order X, Y, Z? Or does someone know something else that will help me...

Share this post


Link to post
Share on other sites
Quote:
Sounds like gimbal lock problem, the only way to solve your problem is with quarternions.
@The OP: The above is incorrect - the problem is almost certainly not gimbal lock, and in any case, you don't need to use quaternions to solve it.

You might take a look at the Wild Magic math library (at geometrictools.com); I believe it includes code for conversion to and from matrices and Euler angles in any non-repeating order (e.g. ZYX). (Keep in mind that the WM library uses column vectors, so if your math library uses row vectors you'll need to make some adjustments to the code.)

Also, it seems that quite often people think they need to convert an orientation to Euler-angle form when they actually do not. I don't know for sure that that's the case here, but you might take another look, just to make sure the conversion is really necessary (or post here with questions if you're not sure).

Share this post


Link to post
Share on other sites
This is how I set up the matrix:


rightX(m00) upX(m01) outX(m02)
rightY(m10) upY(m11) outY(m12)
rightZ(m20) upZ(m21) outZ(m22)


Now here is the relevant code from the Wild Magic math library (from Wm4Matrix3.inl):


// Wild Magic Source Code
// David Eberly
// http://www.geometrictools.com
// Copyright (c) 1998-2008

...

template <class Real>
typename Matrix3<Real>::EulerResult Matrix3<Real>::ToEulerAnglesXYZ (
Real& rfXAngle, Real& rfYAngle, Real& rfZAngle) const
{
// +- -+ +- -+
// | r00 r01 r02 | | cy*cz -cy*sz sy |
// | r10 r11 r12 | = | cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx |
// | r20 r21 r22 | | -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy |
// +- -+ +- -+

if (m_afEntry[2] < (Real)1.0)
{
if (m_afEntry[2] > -(Real)1.0)
{
// y_angle = asin(r02)
// x_angle = atan2(-r12,r22)
// z_angle = atan2(-r01,r00)
rfYAngle = (Real)asin((double)m_afEntry[2]);
rfXAngle = Math<Real>::ATan2(-m_afEntry[5],m_afEntry[8]);
rfZAngle = Math<Real>::ATan2(-m_afEntry[1],m_afEntry[0]);
return EA_UNIQUE;
}
else
{
// y_angle = -pi/2
// z_angle - x_angle = atan2(r10,r11)
// WARNING. The solution is not unique. Choosing z_angle = 0.
rfYAngle = -Math<Real>::HALF_PI;
rfXAngle = -Math<Real>::ATan2(m_afEntry[3],m_afEntry[4]);
rfZAngle = (Real)0.0;
return EA_NOT_UNIQUE_DIF;
}
}
else
{
// y_angle = +pi/2
// z_angle + x_angle = atan2(r10,r11)
// WARNING. The solutions is not unique. Choosing z_angle = 0.
rfYAngle = Math<Real>::HALF_PI;
rfXAngle = Math<Real>::ATan2(m_afEntry[3],m_afEntry[4]);
rfZAngle = (Real)0.0;
return EA_NOT_UNIQUE_SUM;
}
}


As you can see the matrix is in the form


r00 r01 r02
r10 r11 r12
r20 r21 r22


But I don't know what those are... which are up, side, view, and which are the x, y, z components?

Quote:
Also, it seems that quite often people think they need to convert an orientation to Euler-angle form when they actually do not. I don't know for sure that that's the case here, but you might take another look, just to make sure the conversion is really necessary (or post here with questions if you're not sure).


Not really sure.. the reason I need it is because in my "Node" class I keep track of the Euler angles and a set of vectors (up, side, view). The "mouse view" function that rotates the view based on the mouse adjusts the vectors but I don't know how to adjust the Euler angles based on the mouse rotation. I need the Euler angles because the way I do my rotations when drawing in OpenGL and when rotating the vertices in my physics engine is this way:


glRotate(xrot, 1, 0, 0);
glRotate(yrot, 0, 1, 0);
glRotate(zrot, 0, 0, 1);

Share this post


Link to post
Share on other sites
A couple of quick comments. This:
glRotate(xrot, 1, 0, 0);
glRotate(yrot, 0, 1, 0);
glRotate(zrot, 0, 0, 1);
Actually applies the rotations in the order ZYX, not XYZ, so keep that in mind.

Also, I'm not sure if the 'XYZ' in 'ToEulerAnglesXYZ()' means X, then Y, then Z, or X*Y*Z (which would be Z, then Y, then X). Dave could tell you of course, or you could just try both the XYZ and ZYX versions and see which one gives you the correct results.

You'll also need to make sure the matrix elements match up (whether the code will require any changes depends on the 'majorness' and basis vector orientation of both your library and the Wild Magic library).

As for rendering in OpenGL, you don't need Euler angles for that. Just use glMultMatrix*() to upload the matrix directly.

You also mentioned physics; I'd be a little surprised if your physics API absolutely required the use of Euler angles - what API are you using?

Share this post


Link to post
Share on other sites
Quote:
You also mentioned physics; I'd be a little surprised if your physics API absolutely required the use of Euler angles - what API are you using?


I'm using the super-duper-puper-awesome-tastic physics engine... jk.. it's my own physics engine I made. It's just the basic skeleton of a physics engine. It can rotate meshes and test for triangle-to-triangle collisions between them and do elastic collision response, but that's pretty much it... the reason I use the same method for rotation in my physics engine is because the "camera class" for the player and all the objects is just the same "node" class that I use for rotations in my physics engine... basically for each vertex that I need to rotate I define a "node" and use its rotation functions...

[edit]

Okay, I should correct something I said earlier. The site http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm uses the rotation order Heading then Attitude then
Bank (instead of Heading then Bank then Attitude like I said before)... but anyways that doesn't matter.


So back to the problem, here again is how I assume the matrix looks like...

rightX(r00)	upX(r01)  outX(r02)
rightY(r10) upY(r11) outY(r12)
rightZ(r20) upZ(r21) outZ(r22)


Now here is how I tried doing the code:


float r[3];
if (viewvec.x < 1.0f)
{
if (viewvec.x > -1.0f)
{
// y_angle = asin(r02)
// x_angle = atan2(-r12,r22)
// z_angle = atan2(-r01,r00)
//rfYAngle = (Real)asin((double)m_afEntry[2]);
//rfXAngle = Math<Real>::ATan2(-m_afEntry[5],m_afEntry[8]);
//rfZAngle = Math<Real>::ATan2(-m_afEntry[1],m_afEntry[0]);
//return EA_UNIQUE;

r[1] = asin(viewvec.x); //y-angle
r[0] = atan2(-viewvec.y, viewvec.z); //x-angle
r[2] = atan2(-upvec.x, sidevec.x); //z-angle
}
else
{
// y_angle = -pi/2
// z_angle - x_angle = atan2(r10,r11)
// WARNING. The solution is not unique. Choosing z_angle = 0.
//rfYAngle = -Math<Real>::HALF_PI;
//rfXAngle = -Math<Real>::ATan2(m_afEntry[3],m_afEntry[4]);
//rfZAngle = (Real)0.0;
//return EA_NOT_UNIQUE_DIF;

r[1] = - PI / 2.0f;
r[2] = 0.0f;
r[0] = - atan2(sidevec.y, upvec.y);
}
}
else
{
// y_angle = +pi/2
// z_angle + x_angle = atan2(r10,r11)
// WARNING. The solutions is not unique. Choosing z_angle = 0.
//rfYAngle = Math<Real>::HALF_PI;
//rfXAngle = Math<Real>::ATan2(m_afEntry[3],m_afEntry[4]);
//rfZAngle = (Real)0.0;
//return EA_NOT_UNIQUE_SUM;

r[1] = PI / 2.0f;
r[2] = 0.0f;
r[0] = atan2(sidevec.y, upvec.y);
}

r[0] = RADTODEG( r[0] );
r[1] = RADTODEG( r[1] );
r[2] = RADTODEG( r[2] );

return r;


But this doesn't work... the rotations end up all wrong...

[edit]

Ooops... I didn't try using the ZYX order yet... just a sec...

[edit]

Nope... it doesn't work either...


Aaahhh... *plants face into keyboard*... this is hopeless...


What am I doing wrong...

[Edited by - polyfrag on March 3, 2008 2:27:15 AM]

Share this post


Link to post
Share on other sites
First of all, I'd recommend getting away from Euler angles entirely if you can. In most cases, they're just not worth the trouble. (A couple of exceptions to this might be a numerical user interface for setting an object's orientation, or perhaps FPS-style player control - although in the latter case you're usually only dealing with two angles, not three, which isn't quite as problematic.)

Anyway, I really have no idea where the problem in your code is, but here are a few things to check for:

1. Do you have your units (i.e. degrees vs. radians) right?

2. Do you have the matrix storage order (i.e. row- vs. column-major) right?

3. Do you have the basis vector orientation (i.e. row vs. column vectors) right?

4. Are you applying the rotation via OpenGL in the right order? (Keep in mind that the transform that appears 'last' in your code is applied first.)

5. Do you have the Euler rotation order (i.e. XYZ vs. ZYX) right? (Both in terms of how the rotations are handled in your own code, and in terms of picking the right function from the Wild Magic library.)

6. Are you sure that the angles you're getting back aren't just an alias of the angles you put it? (Remember that Euler-angle representations are not unique.)

As you can see, there are plenty of places things can go off the rails here - in fact, it's probably easier to get these sorts of conversions wrong than to get them right :-|

Assuming you've converted the code correctly, the Wild Magic code should do the trick. You could also take a look at the CML (linked in my signature), which also provides support for Euler-angle conversions using any axis order (and with any storage order or basis vector orientation as well). However, the CML Euler-angle conversion code is pretty closely tied in with other parts of the library, so it might be somewhat difficult to just pull out the relevant code for use in your project (which is why I'd suggest sticking with the WM code and trying to get it to work instead).

Share this post


Link to post
Share on other sites
Okay... another thing I should correct... the reason why in the first case I got roll instead of pitch was not only because the order of rotations was wrong but also because I mixed up the bank and attitude.

I've been trying to figure this out for too long... I'm getting everything mixed up now...

After struggling a bit more trying to get this to work, I decided I'll switch to quaternions...

Share this post


Link to post
Share on other sites
Quote:
After struggling a bit more trying to get this to work, I decided I'll switch to quaternions...
Let's say you were trying to fix a piece of equipment using a screwdriver with a red grip, and you were having some trouble. Would you expect that switching to an identical screwdriver with a blue grip would solve the problem?

Well, that's a terrible analogy, but it's early and I'm not thinking too clearly :) The point is, though, that matrices and quaternions have exactly the same behavior with respect to Euler angles, so why would you expect that switching from one to the other would solve your (Euler-angle related) problem?

I suppose you might get lucky and solve the problem inadvertently in the process of switching to quaternions, but I wouldn't count on it.

My advice: stick with matrices, and either try to solve the problem as is, or simply bypass the problem by eliminating your dependence on Euler-angle representations.

Share this post


Link to post
Share on other sites
Actually, I realized I won't need the Euler angles for rotations in my physics engine because I just put in the axis and angle in one rotation instead of doing three with Euler angles.

I'm so newbie... I didn't think of this before.

Share this post


Link to post
Share on other sites
Quote:
Original post by polyfrag
Actually, I realized I won't need the Euler angles for rotations in my physics engine because I just put in the axis and angle in one rotation instead of doing three with Euler angles.

You can also do this for glRotate*() - just beware that OpenGL uses degrees instead of radians (why, I have no idea).

Share this post


Link to post
Share on other sites
Cool... thanks for the help guys.

Quote:
just beware that OpenGL uses degrees instead of radians (why, I have no idea).


Degrees are cooler... better than radians. You can actually put in values and know how much it will rotate without having to use a calculator or pi... but that doesn't really matter...

Share this post


Link to post
Share on other sites
Quote:
Degrees are cooler... better than radians.
Hm, I'm not sure about that :) As units, radians actually 'mean something', while degrees are arbitrary. As such, many computations that work with radians naturally require conversions and intermediate values to work with degrees.

It's true that degrees are more intuitive for some, but it's a stretch to say that degrees are 'better' than radians.

Share this post


Link to post
Share on other sites
Hey I just had to do this exact algorithm for work, and this is my code if it helps anyone out.

I found the link to http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm to be confusing at first so to put his work into other terms that might be easier to understand to some others, I wrote the following.

Heading = y rotation
Bank = x rotation
Attitude = z rotation


// vRight - The matrix right vector
// vUp - The matrix up vector
// vLook - The matrix look vector

if (vRight.y > 0.998f)
{
// singularity at north pole
m_CameraRotate.y = Atan2Deg(vLook.x, vLook.z);
m_CameraRotate.z = RAD_TO_DEG(3.14f/2.0f);
m_CameraRotate.x = 0;
return;
}
if (vRight.y < -0.998f)
{
// singularity at south pole
m_CameraRotate.y = Atan2Deg(vLook.x, vLook.z);
m_CameraRotate.z = -RAD_TO_DEG(3.14f/2.0f);
m_CameraRotate.x = 0;
return;
}

m_CameraRotate.x = Atan2Deg(-vLook.y, vUp.y);
m_CameraRotate.y = Atan2Deg(-vRight.z, vRight.x);
m_CameraRotate.z = RAD_TO_DEG(asinf(vRight.y));


Where as m_CameraRotate is the Euler rotatation in degrees. I have a math function Atan2Deg() which is == atan2() but returns degrees instead of radians.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this