Code: Select all
Ogre::Matrix3 m;
Ogre::Radian y,p,r;
m.FromEulerAnglesZYX(Ogre::Degree(0), Ogre::Degree(-90), Ogre::Degree(0));
m.ToEulerAnglesZYX(y,p,r);
EXPECT_NEAR(y.valueDegrees(), 0, 0.00001);
EXPECT_NEAR(p.valueDegrees(), -90, 0.00001);
EXPECT_NEAR(r.valueDegrees(), 0, 0.00001);
y == 180
p == -90
r == 0
Now, I know about the gimbal lock thing with yaw-pitch-roll values, but no matter how you look at this, the result is at a 180 degree rotation from the expected result. I'm also aware of the not-so-intuitive notion that the 'y' parameter in this case is actually the Z rotation (roll), since the order of the parameters match the order of the rotations.
Note that the rotation matrix obtained with 'FromEulerAnglesZYX' is correct, so the problem is definitely in 'ToEulerAnglesZYX'. I don't know if similar issues exist with different rotation orders as I have not done any tests with them.
Using Ogre 1.8.1, but there are no changes to that part of the code since that version, so a newer version would certainly not fix the issue.
The issue happens with p == -90, no matter the values of y and r. It does not happen with p == +90.
This looks as a bug to me, unless I'm missing something. Any ideas?
For reference, here's Matrix3::ToEulerAnglesZYX with some annotations:
Code: Select all
bool Matrix3::ToEulerAnglesZYX (Radian& rfYAngle, Radian& rfPAngle,
Radian& rfRAngle) const
{
// rot = cy*cz cz*sx*sy-cx*sz cx*cz*sy+sx*sz
// cy*sz cx*cz+sx*sy*sz -cz*sx+cx*sy*sz
// -sy cy*sx cx*cy
rfPAngle = Math::ASin(-m[2][0]); ///<<< This gives -1.57..., or -PI/2
if ( rfPAngle < Radian(Math::HALF_PI) )
{
if ( rfPAngle > Radian(-Math::HALF_PI) )
{
rfYAngle = Math::ATan2(m[1][0],m[0][0]);
rfRAngle = Math::ATan2(m[2][1],m[2][2]);
return true;
}
else
{
// WARNING. Not a unique solution. ///< We enter this case
Radian fRmY = Math::ATan2(-m[0][1],m[0][2]); ///< This gives -PI; it would need to be 0 somehow for the result to make sense
rfRAngle = Radian(0.0); // any angle works
rfYAngle = rfRAngle - fRmY;
return false;
}
}
else
{
// WARNING. Not a unique solution.
Radian fRpY = Math::ATan2(-m[0][1],m[0][2]);
rfRAngle = Radian(0.0); // any angle works
rfYAngle = fRpY - rfRAngle;
return false;
}
}
I think that:
Code: Select all
Radian fRmY = Math::ATan2(-m[0][1],m[0][2]);
Code: Select all
Radian fRmY = Math::ATan2(-m[1][2],m[1][1]);
I'm having a hard time finding sources that show how to properly deal with the gimbal lock. I got that from some old LabView code I have, I'm not sure if it's right...