# Quaternion To Euler issue, fix possible ?

This topic is 1087 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi,

Here the code I use based on this link :

http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/

void CQuaternion::ToEulerAngles( float* X, float* Y, float* Z ) const
{
const float SingularityTest = ( x * y ) + ( z * w );
if( SingularityTest > 0.499f )
{
*Y = 2.0f * CMath::ATan2( x, w );
*Z = CMath::HALF_PI;
*X = 0.0f;
}
else if( SingularityTest < -0.499f )
{
*Y = -2.0f * CMath::ATan2( x, w );
*Z = -CMath::HALF_PI;
*X = 0.0f;
}
else
{
const float zz = z * z;
*Y = CMath::ATan2( 2.0f * ( ( y * w ) - ( x * z ) ), 1.0f - 2.0f * ( ( y * y ) - zz ) );
*Z = CMath::ASin( 2.0f * SingularityTest );
*X = CMath::ATan2( 2.0f * ( ( x * w ) - ( y * z ) ), 1.0f - 2.0f * ( ( x * x ) - zz ) );
}
}

Quaternion To Euler is called when gizmo is used to shows the new rotation on the rotation property in the editor.

It's also called when you attach one actor to another since the rotation is transformed to local space.

The problem is it can fastly gives bad result.

There is a fix to have correct result ?

Thanks

##### Share on other sites
When you changed this
1.0f - 2.0f * y * y - 2.0f * zz

to this
1.0f - 2.0f * ( ( y * y ) - zz )

You didn't factor out the -2.0f correctly to give you what should be this

1.0f - 2.0f * ( ( y * y ) + zz )

Same applies to the assignment of *X

##### Share on other sites

Nice catch ! That works better but it's not very accurate, after a quat to euler if the euler is modified the rotation move slightly.

In some case I have really bad euler resulting from this cacule.

Edited by Alundra

##### Share on other sites

i prefer to convert quat to mat using DX, then convert mat to eulers using inhouse code, which i think is posted in my gamedev info journal here...

nope, it was in a reply to a post about converting mats to eulers. do a search, you'll find the code in the thread.

Edited by Norman Barrows

##### Share on other sites

Here the matrix version which ends to the same issue :

void CQuaternion::ToEulerAngles( float* X, float* Y, float* Z ) const
{
// Convert to matrix.
const CMatrix4 Matrix = ToMatrix();

// Extract eulers.
if( Matrix.m44[ 1 ][ 0 ] > 0.998f )
{
*Y = CMath::ATan2( Matrix.m44[ 0 ][ 2 ], Matrix.m44[ 2 ][ 2 ] );
*Z = CMath::HALF_PI;
*X = 0.0f;
}
else if( Matrix.m44[ 1 ][ 0 ] < -0.998f )
{
*Y = CMath::ATan2( Matrix.m44[ 0 ][ 2 ], Matrix.m44[ 2 ][ 2 ] );
*Z = -CMath::HALF_PI;
*X = 0.0f;
}
else
{
*Y = CMath::ATan2( -Matrix.m44[ 2 ][ 0 ], Matrix.m44[ 0 ][ 0 ] );
*Z = CMath::ASin( Matrix.m44[ 1 ][ 0 ] );
*X = CMath::ATan2( -Matrix.m44[ 1 ][ 2 ], Matrix.m44[ 1 ][ 1 ] );
}
}

The only way to keep correct euler angles is to track them and compute the quaternion from them apparently.

Edited by Alundra

##### Share on other sites
Comparing with my code i use PI where you use half pi (for matrx to euler):

sVec3 ToEulerZYX (const int order = 0x012)
{
int a0 = (order>>8)&3;
int a1 = (order>>4)&3;
int a2 = (order>>0)&3;

sVec3 euler;
// Assuming the angles are in radians.
if ((*this)[a0][a2] > (1.0-FP_EPSILON))
{ // singularity at north pole
euler[a0] = -atan2((*this)[a2][a1], (*this)[a1][a1]);
euler[a1] = -3.1415926535897932384626433832795f/2.0f;
euler[a2] = 0;
return euler;
}
if ((*this)[a0][a2] < -(1.0-FP_EPSILON))
{ // singularity at south pole
euler[a0] = -atan2((*this)[a2][a1], (*this)[a1][a1]);
euler[a1] = 3.1415926535897932384626433832795f/2.0f;
euler[a2] = 0;
return euler;
}
euler[a0] =	-atan2(-(*this)[a1][a2], (*this)[a2][a2]);
euler[a1] =	-asin ( (*this)[a0][a2]);
euler[a2] =	-atan2(-(*this)[a0][a1], (*this)[a0][a0]);
return euler;
}

I took the original code from the same site, and added custom order.
0x012 = xyz, 0x120 = yzx...

Edit: Ooops, PI is so long i've missed the /2 at the end
So maybe the bug is about the signs Edited by JoeJ

##### Share on other sites
That gives a result not far but very not accurate, if you render using the quaternion and the quaternion from the eulers extracted, it has lot of angles of difference.
One case where the result is just not correct at all : -160 degrees on X, -75 degrees on Y and 70 degrees on Z.
If you extract euler from this case you have : -133.219162 on X, -31.7808132 on Y and -62.0091019 on Z.
When you render using a quaternion from these euler you have a completely different result.
Like I said before, I think the only way to keep correct euler angles is to track them and compute the quaternion from them apparently.
But this is the only case for the editor because in a game you only works using quaternion, you don't care of the angle values.
Maybe it's a good way to have editor-only functions which do the angle operations and make the quaternion from them.
Edited by Alundra

##### Share on other sites
I'd like to see if i can reproduce the issue and if so, write my own code.

I do not trust the above myself, but so far i've had no issues (using it for "humans need eulers to edit animation curves" purpose only).

##### Share on other sites
QuaternionToEuler : x = -160 degrees, y = -75 degrees, z = 70 degrees
Quaternion(x,y,z,w) : -0.700637, 0.361543, -0.412074, 0.456716
Euler(x,y,z) : -133.219162, -31.780813, -62.009102
Edited by Alundra

##### Share on other sites
Tried it but i could not reproduce exactly.
I get similar angles when transposing matrix and using YZX order, but with different signs.
I come back to the original matrix using those angles, so the ToEuler function posted above still seems correct.

I assume you have a column major vs. major column matrices issue.
I use OpenGL order and Euclidean Space site too most probably.
Maybe all you need to do is swapping matrix indices.

		sQuat quat (-0.700637, 0.361543, -0.412074, 0.456716);
quat.Normalize();
sMat3 matrix;
matrix.FromQuat (quat);

matrix = matrix.Transposed(); // column / major issue ?

sVec3 verify	= radians * 180.0f/PI; // {mX=133.219284 mY=31.7808933 mZ=62.0091209 ...}

sMat3 verifyMatrix = matrixX * matrixZ * matrixY; // == matrix



Edit: Another Ooops - i made a mistake testing all rotation orders.
Using XZY order i get rid of the matrix transpose, but still different angle signs.

		sQuat quat (-0.700637, 0.361543, -0.412074, 0.456716);
quat.Normalize();
sMat3 matrix;
matrix.FromQuat (quat);
//matrix = matrix.Transposed();

sVec3 verify = radians * 180.0f/PI; // {mX=133.219284 mY=31.7808933 mZ=62.0091209 ...}

sMat3 verifyMatrix = matrixX * matrixZ * matrixY; // == matrix

Edited by JoeJ

1. 1
2. 2
Rutin
16
3. 3
4. 4
5. 5

• 26
• 11
• 9
• 9
• 11
• ### Forum Statistics

• Total Topics
633710
• Total Posts
3013485
×