# Euler angles Yaw/Pitch/Roll - differences to Ogre

Beauty

24-01-2012 13:17:17

Related to the properties:
Quaternion.Yaw
Quaternion.Pitch
Quaternion.Roll

SceneNode.Orientation is a Quaternion datatype. So this topic is also related to:
SceneNode.Orientation.Yaw
SceneNode.Orientation.Pitch
SceneNode.Orientation.Roll

Many months ago I recognized, that the Yaw/Pitch/Roll properties of Mogre Quaternions return strage values.

If you have the problem that Pitch returns NaN values (undefined), look to the topic Euler angles - current bugs + improved Euler Angle Class.
In the Mogre source code the bug is fixed now, but there will be "unfixed" binary files in the world for a longer time.
Just for information.

Different results for Mogre and Ogre

This topic I created related to the co-domain of the returned values of Yaw, Pitch and Roll.

Co-domain: Currently Mogre returns Euler values in the range
```Yaw -90 .. 90 Pitch -180 .. 180 Roll -180 .. 180```

The Ogre related methods getYaw(), getPitch() and getRoll() return values in a different co-domain.
```Yaw -180 .. 180 Pitch ?? .. ?? Roll ?? .. ??```

In most cases, the user would like to have a Yaw co-domain of -180 .. +180.
Additionally we want to have the same behaviour for Mogre and Ogre.

Related to this behaviour I had a discussion in the topic co-domains of Quaternion::getYaw(), getPitch() and getRoll().
There I got an answer for the reason:

Why Mogre returns different Euler Angle values:
Ogre's getYaw code is:
```if (reprojectAxis) { // yaw = atan2(localz.x, localz.z) // pick parts of zAxis() implementation that we need Real fTx = 2.0f*x; Real fTy = 2.0f*y; Real fTz = 2.0f*z; Real fTwy = fTy*w; Real fTxx = fTx*x; Real fTxz = fTz*x; Real fTyy = fTy*y; // Vector3(fTxz+fTwy, fTyz-fTwx, 1.0-(fTxx+fTyy)); return Radian(Math::ATan2(fTxz+fTwy, 1.0f-(fTxx+fTyy))); } else { // internal version return Radian(Math::ASin(-2*(x*z - w*y))); }```
The reprojectAxis bool defaults to true.
This means that by default ogre is doing a different calculation than mogre, which is probably why I'm getting the full range while you are getting a smaller value. I'd probably get the 60,180,180 thing if I used the non default formula.

Now our task is to update our Mogre source code (in the correct way).

Ogre code from file OgreQuaternion.cpp:
``` Radian Quaternion::getRoll(bool reprojectAxis) const { if (reprojectAxis) { // roll = atan2(localx.y, localx.x) // pick parts of xAxis() implementation that we need // Real fTx = 2.0*x; Real fTy = 2.0f*y; Real fTz = 2.0f*z; Real fTwz = fTz*w; Real fTxy = fTy*x; Real fTyy = fTy*y; Real fTzz = fTz*z; // Vector3(1.0-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy); return Radian(Math::ATan2(fTxy+fTwz, 1.0f-(fTyy+fTzz))); } else { return Radian(Math::ATan2(2*(x*y + w*z), w*w + x*x - y*y - z*z)); } } //----------------------------------------------------------------------- Radian Quaternion::getPitch(bool reprojectAxis) const { if (reprojectAxis) { // pitch = atan2(localy.z, localy.y) // pick parts of yAxis() implementation that we need Real fTx = 2.0f*x; Real fTy = 2.0f*y; Real fTz = 2.0f*z; Real fTwx = fTx*w; Real fTxx = fTx*x; Real fTyz = fTz*y; Real fTzz = fTz*z; // Vector3(fTxy-fTwz, 1.0-(fTxx+fTzz), fTyz+fTwx); return Radian(Math::ATan2(fTyz+fTwx, 1.0f-(fTxx+fTzz))); } else { // internal version return Radian(Math::ATan2(2*(y*z + w*x), w*w - x*x - y*y + z*z)); } } //----------------------------------------------------------------------- Radian Quaternion::getYaw(bool reprojectAxis) const { if (reprojectAxis) { // yaw = atan2(localz.x, localz.z) // pick parts of zAxis() implementation that we need Real fTx = 2.0f*x; Real fTy = 2.0f*y; Real fTz = 2.0f*z; Real fTwy = fTy*w; Real fTxx = fTx*x; Real fTxz = fTz*x; Real fTyy = fTy*y; // Vector3(fTxz+fTwy, fTyz-fTwx, 1.0-(fTxx+fTyy)); return Radian(Math::ATan2(fTxz+fTwy, 1.0f-(fTxx+fTyy))); } else { // internal version return Radian(Math::ASin(-2*(x*z - w*y))); } } ```

Mogre code from file MogreQuaternion.cpp:
``` Radian Quaternion::Roll::get() { return Radian(Math::ATan2(2*(x*y + w*z), w*w + x*x - y*y - z*z)); } //----------------------------------------------------------------------- Radian Quaternion::Pitch::get() { return Radian(Math::ATan2(2*(y*z + w*x), w*w - x*x - y*y + z*z)); } //----------------------------------------------------------------------- Radian Quaternion::Yaw::get() { return Radian(Mogre::Math::ASin(-2*(x*z - w*y))); }```

How we could do

I'm not shure how to handle the parameter.
I propose to
1. Keep the properties Yaw/Pitch/Roll[/*:m]
. . . . . private or public?
. . . . . I think private as long as we don't know when we need false for reprojectAxis.
. . . . . To keep it private would avoid confusions for users.[/*:m]
3. Call that methods from the Properties[/*:m][/list:u]

For "internal usage" the parameter reprojectAxis is false.
I don't know the background and which "internal" methods call getYaw() / getPitch() / getRoll().

Perhaps there are also other parts of the math related classes, which should be updated.

Pure .NET classes

Just for information:
About 20 classes are not wrapped, because of a better performance. Instead they will be compiled for .NET (as C++ code).
The downside is, that we need to update them manually, although I think that changes are very rare.
All "custom" classes are listed in the wiki (here).
In the source code you find them here:
```Mogre\Main\include\Custom\ // header files Mogre\Main\src\Custom\ // source files ```