Question about node.Orientation.Yaw.ValueDegree return value

eddy

02-08-2011 23:44:24

Hello to all!

I wanted to ask is there a way to retrieve an angle information from the usual [0- 360] system?

While trying to implement some basic one axis rotations, I got confused about it working strangely. After some testing, I found out that my return values are given according to the attached picture (rotation is done around the axis perpendicular to the screen).

Beauty

06-08-2011 12:51:40

Hi eddy,
I know this problem very well, because I got confused, too.

The problem are the returned co-domains:
getYaw -90 .. 90
getPitch -180 .. 180
getRoll -180 .. 180


Also there is a lack of documentation.
A half year ago I suggested to add a description to the Ogre Class Documentation.
Unfortunately the documentation was not improved at this points.

One reason is, that an other user told, the co-domain would be -180 to 180.
I never got such results. Now I started a new discussion in the Ogre main forum.
Be welcome to post there, too. This will affirm the interest of an improved documentation.
http://www.ogre3d.org/forums/viewtopic.php?f=1&t=66030


To get the wanted values of Yaw, you also need to check the values of Pitch and Roll.
For my own application I wrote a method to get the wanted Yaw value. I will post it later.

Alternatively:
User Kojack posted a class for Euler Angles calculations.
I didn't check it, but maybe it solves you problem, too.
http://www.ogre3d.org/tikiwiki/Euler+Angle+Class

eddy

06-08-2011 13:06:21

Thanks for your reply. I came to think this is some arcus trigonometry issue. I will check the links you posted, and I am very interested in seeing your solution.

For now I am countering this by checking the signs of the axis orientation vectors to determine in which quadrant is my object of interest pointing.

EDIT: I will check roll and pitch values and report them here.But probably not before the day after tomorrow. Now I have to finish some other coding stuff.

EDIT2.5:^scratch that. I tested ROLL (and pitch), and came to the same conclusion as beauty:

Beauty

06-08-2011 14:54:49

Here is my code.
This are 3 methods, which are used for different aims.
Also you can apply some settings by parameters.
By this it should be more flexible. Just use the parts, which you need.
Some descriptions I added. I hope you understand what I mean.
If you have problems, just tell me.

Note: The differentiation by the type "YawConvention" you can remove. I use it for my application, but it's not needed for common Mogre users.



/// <summary>
/// Calculate the Yaw angle from a given orientation (quaternion)
/// </summary>
/// <remarks>
/// The reference to SceneVariables is used better performance.
/// It prevents the re-creation of temporary variables for each call.
/// </remarks>
/// <param name="var">Reference to mScene.var, which contains helper variables</param>
/// <param name="orientation">Quaternion (contains the direction description)</param>
/// <param name="angleUnit">Define if the returned value is in Degree or Radian</param>
/// <param name="yawBasis">Define which axis is used to describe the rotation divergence</param>
/// <returns>Calculated angle in the co-domain of 0 to 360 degrees</returns>
public static Single GetYawAngle(ref PublicSceneVariables var, ref Quaternion orientation, Mogre.Math.AngleUnit angleUnit, YawConvention yawBasis)
{
var.gy_yawDirection = orientation.XAxis; // get direction vector
return GetYawAngle(ref var, ref var.gy_yawDirection, angleUnit, yawBasis);
}





/// <summary>
/// Calculate the Yaw angle of a given SceneNode
/// </summary>
/// <remarks>
/// The reference to SceneVariables is used better performance.
/// It prevents the re-creation of temporary variables for each call.
/// </remarks>
/// <param name="var">Reference to mScene.var, which contains helper variables</param>
/// <param name="sceneNode">SceneNode, which yaw value should be returned</param>
/// <param name="angleUnit">Define if the returned value is in Degree or Radian</param>
/// <param name="localOrWorld">Define if yaw value of local space or global space is needed</param>
/// <param name="yawBasis">Define which axis is used to describe the rotation divergence</param>
/// <returns>Calculated angle in the co-domain of 0 to 360 degrees</returns>
public static Single GetYawAngle(ref PublicSceneVariables var, ref SceneNode sceneNode, Mogre.Math.AngleUnit angleUnit, SceneNode.TransformSpace localOrWorld, YawConvention yawBasis)
{
if (sceneNode == null)
return 0;

switch (localOrWorld)
{
case Node.TransformSpace.TS_LOCAL:
var.gy_yawDirection = sceneNode.Orientation.XAxis;
break;

case Node.TransformSpace.TS_WORLD:
var.gy_yawDirection = sceneNode._getDerivedOrientation().XAxis;
break;

default:
// TS_PARENT
throw new NotSupportedException();
}
return GetYawAngle(ref var, ref var.gy_yawDirection, angleUnit, yawBasis);
}





/// <summary>
/// Calculate the Yaw angle from a given direction vector
/// </summary>
/// <remarks>
/// The reference to SceneVariables is used better performance.
/// It prevents the re-creation of temporary variables for each call.
/// </remarks>
/// <param name="var">Reference to mScene.var, which contains helper variables</param>
/// <param name="direction">Direction vector from which the Yaw angle should be extracted</param>
/// <param name="angleUnit">Define if the returned value is in Degree or Radian</param>
/// <param name="yawBasis">Define which axis is used to describe the rotation divergence</param>
/// <returns>Calculated angle in the co-domain of 0 to 360 degrees</returns>
public static Single GetYawAngle(ref PublicSceneVariables var, ref Vector3 direction, Mogre.Math.AngleUnit angleUnit, YawConvention yawBasis)
{
var.gy_yawDirection = direction;
var.gy_yawDirection.y = 0;
var.gy_yawDirection.Normalise();

if (yawBasis == YawConvention.NED)
{
var.gy_yawAngle = Mogre.Math.ACos(var.gy_yawDirection.DotProduct(Vector3.UNIT_X));
var.gy_overrunCheckAngle = Mogre.Math.ACos(var.gy_yawDirection.DotProduct(Vector3.UNIT_Z));
}
else // YawConvention.Ogre
{
var.gy_yawAngle = Mogre.Math.ACos(var.gy_yawDirection.DotProduct(Vector3.UNIT_Z));
var.gy_overrunCheckAngle = Mogre.Math.ACos(var.gy_yawDirection.DotProduct(Vector3.UNIT_X));
}

// adaption if more than 180 degree (because Ogre returns only a co-domain of -90..90 degrees)
if (var.gy_overrunCheckAngle.ValueRadians > var.gy_HalfPiInRad)
var.gy_yawAngle = var.gy_TwoPiInRad - var.gy_yawAngle;

if (angleUnit == Mogre.Math.AngleUnit.AU_DEGREE)
return var.gy_yawAngle.ValueDegrees;
else
return var.gy_yawAngle.ValueRadians;
} // GetYawAngle()



/// <summary>
/// Define used Yaw convention. Differences are the comparison axis for divergence calculation and the rotation direction.
/// </summary>
public enum YawConvention
{
/// <summary>Convention of NED coordinate system:
/// Rotation divergence to the X-axis; NEGATIVE rotation around the Ogre Y-axis
/// (Comply to the positive rotation around the NED Z-axis)</summary>
NED,

/// <summary>Convention of Ogre coordinate system:
/// Rotation divergence to the Z-axis; Positive rotation around the Ogre Y-axis</summary>
Ogre
}

//-------------------------

public class SceneVariables
{
// temp helper variables for Yaw calculations
public Vector3 gy_yawDirection;
public Radian gy_yawAngle;
public Radian gy_overrunCheckAngle;
public Radian gy_TwoPiInRad = new Radian(Mogre.Math.TWO_PI);
public Radian gy_HalfPiInRad = new Radian(Mogre.Math.HALF_PI);
}

SceneVariables var = new SceneVariables();

update: added souce code from next post


I don't know if it works for all cases.
Perhaps there are problems when Pitch and/or Roll is greater than +/-90 degree. (I didn't check it.) Such cases I don't have in my application.

You can check several cases by a simple test method.
Adapt this method and compare input/output values.
Then you know. :wink:


Single yaw = 0;
Single pitch = 0;
Single roll = 0;

Single ymin = 0;
Single ymax = 0;
Single pmin = 0;
Single pmax = 0;
Single rmin = 0;
Single rmax = 0;

Single y, p, r;

Quaternion quat = new Quaternion(new Degree(yaw), Vector3.UNIT_Y);
SceneNode node = new SceneNode(Smgr);
Console.WriteLine("Start calculation ...\n");
Console.Write("Intermediate steps: ");

for (yaw = -200; yaw <= 400; yaw++)
{
for (pitch = -200; pitch <= 200; pitch++)
{
for (roll = -200; roll <= 200; roll++)
{
node.Orientation = new Quaternion(new Degree(yaw), Vector3.UNIT_Y);
node.Pitch(new Degree(pitch), Node.TransformSpace.TS_LOCAL);
node.Roll(new Degree(roll), Node.TransformSpace.TS_LOCAL);
quat = node.Orientation;

y = quat.Yaw.ValueDegrees;
p = quat.Pitch.ValueDegrees;
r = quat.Roll.ValueDegrees;

if (y < ymin)
ymin = y;
if (y > ymax)
ymax = y;
if (p < pmin)
pmin = p;
if (p > pmax)
pmax = p;
if (r < rmin)
rmin = r;
if (r > rmax)
rmax = r;

}

}
Console.Write(".");
if ((yaw % 10) == 0)
Console.Write(yaw); // show current state of calculation

}
Console.WriteLine("\n\nCalculation ready.\n");

Console.Write("co-domains:\n");
Console.Write(String.Format("Yaw {0} .. {1} \n", ymin, ymax));
Console.Write(String.Format("Pitch {0} .. {1} \n", pmin, pmax));
Console.Write(String.Format("Roll {0} .. {1} \n", rmin, rmax));

Beauty

07-08-2011 09:55:39

Addition:

My methods contains the parameter "ref PublicSceneVariables var".
The reason: For performance reasons I don't want to re-create temporary variables for each call. So I keep them in the class "PublicSceneVariables".
If you like, you can make them local or become a field of the same class. So there would not be a need to have an extra reference.

Here is the definition of my temporary variables:
public class SceneVariables
{
// temp helper variables for Yaw calculations
public Vector3 gy_yawDirection;
public Radian gy_yawAngle;
public Radian gy_overrunCheckAngle;
public Radian gy_TwoPiInRad = new Radian(Mogre.Math.TWO_PI);
public Radian gy_HalfPiInRad = new Radian(Mogre.Math.HALF_PI);
}

SceneVariables var = new SceneVariables();

Beauty

08-08-2011 09:10:04

By help of user Kojack we found out that Mogre causes problems with getYaw.

I suppose for performance reasons Mogre doesn't use a wrapped Ogre method.
Instead it uses a common equation. It's mathematical correct, but bad for usage with Ogre/Mogre.

Details are here:
http://www.ogre3d.org/forums/viewtopic. ... 56#p435756 (bad co-domain)
http://www.ogre3d.org/forums/viewtopic. ... 95#p426695 (NaN problem)

So we have 2 options for updating the Mogre wrapper:

1)
Just wrap the Ogre function of getYaw.

2)
Update the Mogre Yaw property and mathematical functions and use a port of the related Ogre source code.

In general we should compare all mathematical functions of Mogre.
If they are not wrapped (for performance reasons) we should use a port of the Ogre source code.

I prefer option 2, because of performance reasons. (getYaw is a method which could be called very often)
Which options are prefered by the other Mogre users?