Maths classes

Minor issues with the Ogre API that can be trivial to fix
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Maths classes

Post by Kojack »

Quaternion's default constructor fills it with 1,0,0,0 (identity) but vector and matrix classes leave themselves uninitialised.

A lot of functions in the Quaternion and Matrix classes use pascal notation (functions start with upper case) instead of Ogre's standard of camel notation for functions (and pascal for classes).

Some functions have differing names to the other classes:

Dot product:
Quaternion::Dot()
Vector3::dotProduct()

Squared magnitude:
Quaternion::Norm()
Vector3::squaredLength()

Magnitude:
Vector3::length()
Quaternion has none, you need to do sqrt(Norm())

Angle/Axis conversion:
Quaternion::FromAngleAxis (const Radian& rfAngle, const Vector3& rkAxis)
Matrix3::FromAxisAngle (const Vector3& rkAxis, const Radian& fRadians)
Quaternion::ToAngleAxis (Radian& rfAngle, Vector3& rkAxis)
Matrix3::ToAxisAngle (Vector3& rkAxis, Radian& rfAngle)

Determinant:
Matrix3::Determinant()
Matrix4::determinant()

Inverse:
Matrix3::Inverse()
Matrix4::inverse()
Quaternion::Inverse()
Matrix3 also has another Inverse() function which takes a Matrix3 and returns a bool if the arg is the inverse of the current. This doesn't exist in Matrix4 or Quaternion, and should be called something like isInverse().

Quaternion and Vector3 have isNan() to check values for nans. Matrix3 and Matrix4 are missing it.
Quaternion is missing the normalisedCopy() from the vector classes.
Most methods in Matrix4 are inlined in the header, but the same functions (operator overloads, etc) are in the cpp for Matrix3.

Vector4 is lacking a lot of Vector3's methods and constants (like UNIT_X, length(), normalise(), distance(), etc). Although ogre's Vector4 class is really only intended for use with homogeneous vectors instead of generic 4d vectors, so that's probably for the best.

Matrix4::getTrans() returns the current translation, Matrix4::getScale makes a new scale matrix and returns that instead of returning the current scale. There is also a second Matrix4::getTrans() which is a static function for making a translation matrix.
Matrix4::makeTrans() overwrites the current matrix with a pure translation matrix, but there's no makeScale or makeOrientation (instead you assign a new matrix4 with a quaternion passed to the constructor).
Matrix4::setScale() directly sets the diagonal using a Vector3. This will only work on a matrix with no rotation (comments don't mention that). Calling setScale on a Matrix4 with existing rotations will do freaky stuff (at the least it will make it non orthogonal (skewed)). Really it should be doing something like setting each axis vector in the 3x3 upper corner to the normalised axis vector times the corresponding scale component.
For example if your matrix4 was
1,0,0,0
0,1,0,0
0,0,1,0
0,0,0,1
then setScale(4,5,6) would give you the correct answer of:
4,0,0,0
0,5,0,0
0,0,6,0
0,0,0,1

But if the matrix4 was rotated 90 degrees around the z axis first:
0,-1,0,0
1,0,0,0
0,0,1,0
0,0,0,1
then setScale(4,5,6) would give:
4,-1,0,0
1,5,0,0
0,0,6,0
0,0,0,1
instead of:
0,-5,0,0
4,0,0,0
0,0,6,0
0,0,0,1
Negative scales will confuse those setScale changes, but they confuse everything anyway and should be made illegal or something (I'm looking at you Sketchup!).

Everything I said about Vector3 pretty much applies to Vector2 as well (but not Vector4), they appear to be a copy/paste with the z's removed. Speaking of which, some comments might need fixing in Vector2 because of that. For example, perpendicular() says there are infinite possibilities, but for a 2d vector there's really only 2 possibilities. I haven't checked all the comments, I'm mainly looking at function names.


Also, does anybody think we should have a Matrix2 class? I don't really care either way (the only time in an ogre app I'd use 2d matrices would be in 2d game logic, and I'd do all that with my own math library anyway), but it would make sense since we already have a Vector2 class.
User avatar
betajaen
OGRE Moderator
OGRE Moderator
Posts: 3447
Joined: Mon Jul 18, 2005 4:15 pm
Location: Wales, UK
x 58

Re: Maths classes

Post by betajaen »

That is a good list. I've always thought that the Maths classes all though brilliant that they are, need a good comb through to make everything consistent.

I also think there should be a template as/from functions to convert to different types, and I've always thought that the Matrix4::getTrans(), makeTrans(), setScale() functions have been confusing as well.
User avatar
madmarx
OGRE Expert User
OGRE Expert User
Posts: 1671
Joined: Mon Jan 21, 2008 10:26 pm
x 50

Re: Maths classes

Post by madmarx »

I agree. Concerning the Matrix2, I also don't find it necessary at the moment.
Tutorials + Ogre searchable API + more for Ogre1.7 : http://sourceforge.net/projects/so3dtools/
Corresponding thread : http://www.ogre3d.org/forums/viewtopic. ... 93&start=0
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5476
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1359

Re: Maths classes

Post by dark_sylinc »

I agree it's a bit of a mess.
About the constructor not filling the variables is controversial, as it is performance sensitive.
Compilers can take them away, but not always. A big example is std::vector::resize()
It has already discussed in another thread and I'm inclined towards treating MatrixN Quaternions and Vectors as plain data types (like float or int)
JDX_John
Gnome
Posts: 397
Joined: Sat Nov 08, 2008 1:59 pm
x 2

Re: Maths classes

Post by JDX_John »

madmarx wrote:I agree. Concerning the Matrix2, I also don't find it necessary at the moment.
I think Matrix2 could be quite useful for compositors... pass a Matrix2 parameter that transforms [0,1] coordinates to some useful space.

That assumes shaders understand 2x2 matrices, do they?
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

Shaders do support float2x2. You can use it to mess around with uv coords (rotate and scale textures).

Hmm, seems this hasn't gone far. I just started my holidays, so I might see if I can whip up a patch (in between playing skyrim and X3 Albion Prelude, which will be most of my time).
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Kojack, good proposal for the math function names.

I came to the papercut section, because of your Euler Angle Class, which you published in the wiki. Now I see that you still created a math topic.

I wanted to tell you my idea to integrate the Euler Angle Class to the official Ogre souce code.
If the developers agree, the comments should be "marked up" in a way so that the Ogre documentation creator tool can grabb them.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

I actually have a new version of the euler class, much better, more tested and now compatible with ogre's angles (mine was a crazy combination of left handed and right handed angles, with some wrapping around and some unlimited. Now it's better). I haven't uploaded to the wiki yet, since I hacked one major function and haven't tested it yet.
I've got a cool test where the euler class controls the head of the sinbad model to do object tracking with angular limits and slow turning, regardless of the orientation of the original mesh. It's creepy watching him look at the camera. :)
I've also started working on a c# version for mogre.

I think integrating it was mentioned before and rejected. It's pretty small and header only, so pasting from the wiki is fine for most people.


I implemented most of the changes in this thread to ogre, but I haven't submitted a patch because it breaks so many files, plus probably a heap of wiki pages. I still think it should be done, but not with ogre 1.8 already at rc1. It would be better to start fresh with 1.9. I'll submit a patch once then (once I work out how to do patches in mercurial). This could coincide with the frequently requested plan to separate the ogre maths code into a library, so people can use it in servers or other apps without all the other ogre stuff.

Good point on the comments format, I should get into the habit of that.
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Kojack wrote:I actually have a new version of the euler class
Nice to know. Please keep it in mind to publish it.
I will wait for your notice by suscription of this topic. :wink:
Kojack wrote:I've also started working on a c# version for mogre.
Oh, interesting. Do you use Mogre, too?
If not, what's your motivation to do the extra work?
Kojack wrote:I think integrating it was mentioned before and rejected.
I thought, it would be a useful extension for the Ogre.Math class. And it doesn't blow up the Ogre code, because it has only "a few" lines of code.
But this is only my personal opinion.
I implemented most of the changes [...] it breaks so many files, plus probably a heap of wiki pages.
One way could be:
Keep the old API functions and add an obsolete note to the description.
Parallel to that add the new functions.
On the other hand the redundancy would blow up the API a little bit and perhaps confuse people which only looks to API members in Visual Studio without reading the description.
You are right in the point that there should not be API changes after release of RC1.

For the wiki pages I don't care too much.
We can search for all related pages by google "site:ogre3d.org tiki <functionName>".
Then we can update the code snippets and add a note for usage with previous Ogre version.
Also there is information about in the Changelog. When somebody search for an "unknown method", he will find the changelog.
It's some extra work for developers, but they have good chances to manage it.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

I use mogre around the middle of every year. I have a rapid app dev class which teaches c#, then we use mogre to make game level editors and stuff.
I thought it would be handy, and a bit of c# practice.
(Plus when searching for euler on the wiki, your scratchpad shows up, saying you had plans to port the euler class. So I knew there was an audience for a c# version of the new one) :)

You should have seen the trouble I had getting ogre recorded into animated gifs (of euler stuff). :) Virtual Dub can take image sequences and save them as a gif, but only firefox can display them, every other image program says the gif is corrupt.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

Ok, I got off my ass and updated the wiki.

New euler class is up at http://www.ogre3d.org/tikiwiki/tiki-ind ... ef_id=1114
It's now fully compatible with Ogre::Matrix3::FromEulerAnglesYXZ and Ogre::Matrix3::ToEulerAnglesYXZ (note: only the YXZ versions, because my class is based purely on Yaw Pitch Roll ordering, which is most convenient for upright character controllers and cameras).

(My previous class was made to be easy, so I used clockwise for positive yaw to match compass headings. But the rest of ogre uses the standard anticlockwise right handed rotation for yaw. Now mine matches ogre).
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Kojack wrote: I use mogre around the middle of every year. I have a rapid app dev class which teaches c#, then we use mogre to make game level editors and stuff.
This sounds great!!
I would like to be your student. :D

Ok, I got off my ass and updated the wiki.
Very very nice !! :D
Thank you so much.

I wanted to publish an announcement in the Mogre forum.
But then I recognized that your new class would have problems with the unfixed Mogre bug. (As we talked about some months ago.)
Your old class should work even with the bug, because it doesn't touches the buggy properties (get: Quaternion.Yaw/.Pitch/.Roll).

Details related to the bugs are here:
http://www.ogre3d.org/addonforums/viewt ... 017#p98017
...... Start to read the post at section "Also important".

My preparated announcement is here: (if you are interested to read)
http://www.ogre3d.org/addonforums/viewt ... 048#p98048
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

I tried to get my one to match the quaternion's yaw/pitch/roll methods (generate a quaternion from the euler, then read the quat back into a second euler and see how they compare). It didn't go well.
But what works much better than reading the angles from a quaternion directly is to convert the quaternion to a 3x3 matrix, then call ToEulerAnglesYXZ on it. That returns them in the way I was expecting.

The quaternion code is using a different angle combination than what I wanted. That may also cause some of the confusion people have had with it.
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Mogre returns values related to other co-domains and in special cases even NaN values.
Source of the problems is that currently Mogre uses System.Math instead of Ogre.Math.

I quote yourself:
Kojack wrote:Does Mogre use Ogre's math code for quaternions? I think it used it's own vector math instead of wrapping ogre's for performance.
If so, it might not have the same protection.
getYaw does an asin operation. This MUST be given a value between -1 to 1. Outside of that range will cause a nan. Even slight floating point errors like 1.000001 will fail. Ogre provides Math::ASin, which protects asin from being called with bad values. The Ogre getYaw function calls Math::ASin, but maybe the Mogre getYaw is doing asin directly.
That's the only operation in there that could cause a nan, assuming that the quaternion didn't contain a nan to start with.

There are problems with the getYaw and related functions though. I was testing them a few days ago (while making a new version of my Euler class) and I found that the returned values are wrong in many cases. Not just the usual thing where several different combination of eulers result in the same quaternion, this was giving results that were mirrored along the x or y axes. Converting the quaternion to a matrix3, then calling one of the toeuler methods gave the correct results.
The related discussion was in topic Quaternion::getYaw() returns NaN (not defined)

Kojack wrote:Mogre using System::Math::Asin is definitely the cause of the nans.
Asin can only accept numbers in the range -1 to 1. Anything outside that range is mathematically impossible and meaningless. Due to floating point error, the result of -2*(x*z-w*y) could end up being slightly off, such as 1.000001 or -1.000001. That is enough to make Asin (and Acos) fail. Ogre has protection against this, it checks the value before calling asin. Mogre should do this too if it isn't going to call the ogre version.
The discussion was in topic co-domains of Quaternion::getYaw(), getPitch() and getRoll()

I hope the problems will be fixed soon.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

I didn't mean the nan stuff, I remember that. The problems with the quaternion code that I had were different, due to euler ordering. There's 6 valid orders, I used one and ogre wanted another, causing the object to sometimes freak out and flip around the wrong axis (not instant flip, it was smooth). The matrix 3 class gives you the option of using any of the 6 orderings manually, while the quaternion class is hard coded to just one ordering. It's not really a problem because a major point of the euler class was to not read values back from a quaternion.
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Kojack wrote:I didn't mean the nan stuff, I remember that. [...] causing the object to sometimes freak out and flip around the wrong axis
Do you mean the common behaviour or the special Mogre behaviour?

For the second case:
They are different, because of the bug, which causes other co-domains for the returned Euler values.
With the Mogre but, the returned Yaw value has a co-domain of -90..90.
If the Yaw rotation is out of this range, the scene node is upside down.

Kojack wrote:The matrix 3 class gives you the option of using any of the 6 orderings manually
Yes, but only when the user understands the related math. :lol:

Kojack wrote:It's not really a problem because a major point of the euler class was to not read values back from a quaternion.
In the past I tried to write code, which converts a quaternion state to euler angles. It worked for the Yaw angle, but then I gave up, because I stent too much time to understand the needed math basics.

My alternative plan was to "cache" the euler values. Your Euler class will do the job well. Additionally it has extra functionality.


A converter to read values back from quaternions would be still useful, too.
Example 1: Get "world related" (derived) Euler angles of a SceneNode, which is clamped to an other SceneNode.
Example 2: Get Euler angles of SceneNodes, which were moved by a physics library.

Well, this is just a side note.
Perhaps there still exists relating solutions in other game/3D developer forums.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Re: Maths classes

Post by Kojack »

I'm just talking about general ogre behaviour.

Here's a video showing the issue:
[youtube]-boq7NPi1m8[/youtube]

The right sinbad uses a euler object with keyboard control (he also has head tracking on, but that's not important here). The euler object is used to set his orientation quaternion.
The middle sinbad reads the right sinbad's orientation (as a quaternion), calls getYaw, getPitch and getRoll on it, puts them into a second euler object and uses it as the orientation.
The left sinbad reads the right sinbad's orientation as well, but it converts the orientation quaternion into a matrix, then uses the matrix ToEulerAnglesYXZ to get the yaw, pitch and roll and puts them in a third euler object and then use that for orientation.

As you can see, the left and right ones are identical. The problem is that the rotation order for getYaw, getPitch and getRoll doesn't match my euler code. But matrices let you choose, so I just picked the correct one.
Different combinations can be chosen based on coordinate system (does yaw go around the y or z axis?) and gimbal lock. When the second angle is 90 or -90 degrees, the first and third angles gimbal lock. So in mine if you look up (pitch), the yaw and roll gimbal lock. If looking up is common, a different ordering would move the gimbal lock to a less important angle.

I'm pretty sure this is also why you were getting yaw in the +/-90 range. If you don't want to flip upside down (or sideways, or whatever), the first angle in the euler combination is usually used with a 360 degree range, while the second only has a 180 degree range (so a camera could look around 360 degrees, but usually you don't pitch it until it's upside down to look behind yourself). Ogre's quaternions don't use yaw/pitch/roll order, so yaw isn't the first angle so it isn't considered the primary one you rotate to face a direction.

To bypass the range issue, instead of this:

Code: Select all

Quaternion q = node->getOrientation();
Radian yaw = q.getYaw();
Radian pitch = q.getPitch();
Radian roll = q.getRoll();
you can do this:

Code: Select all

Quaternion q = node->getOrientation();
Radian yaw;
Radian pitch;
Radian roll;
Matrix3 m;
q.ToRotationMatrix(m);
m.ToEulerAnglesYXZ(yaw,pitch,roll);
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Thanks for your comprehensive explanation and creating the video, which is a good demonstration.
I want to put a copy into the wiki. So others can find it at a much more common place. (this topic is very hidden)
Kojack wrote:you can do this:

Code: Select all

Quaternion q = node->getOrientation();
Radian yaw;
Radian pitch;
Radian roll;
Matrix3 m;
q.ToRotationMatrix(m);
m.ToEulerAnglesYXZ(yaw,pitch,roll);
Very good to know.
In the past I couldn't figure out the sense and usage of functions like ToEulerAnglesYXZ(...).

One year ago I created the topic Add description of class members to Ogre class reference.
There I listed several undocumented class members.
For some of them are proposed descriptions in the topic.
It would need just copy and paste for a core code administrator.
Unfortunately in the current Ogre documentation (online) I don't see any change. :cry:

Without documentation it's hard to figure out how to do something.

Kojack wrote:I'm pretty sure this is also why you were getting yaw in the +/-90 range.
I just quote yourself as posted here in March 2011:
Kojack wrote:getYaw() is -180 to 180. I just tested it and it returned values below -90 and above 90 fine.
In comparison to that Mogre returns:

Code: Select all

getYaw -90 .. 90
getPitch -180 .. 180
getRoll -180 .. 180
Well, I don't want to hit you with quotes.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39

Re: Maths classes

Post by Beauty »

Kojack wrote: you can do this:

Code: Select all

Quaternion q = node->getOrientation();
Radian yaw;
Radian pitch;
Radian roll;
Matrix3 m;
q.ToRotationMatrix(m);
m.ToEulerAnglesYXZ(yaw,pitch,roll);
By your useful code snippet I created a second constructor for the C# version of your class:

Code: Select all

/// <summary>
/// Constructor which calculates the Euler Angles from a quaternion.
/// </summary>
public Euler(Quaternion oriantation)
{
    Matrix3 rotMat;
    rotMat = oriantation.ToRotationMatrix();
    rotMat.ToEulerAnglesYXZ(out mYaw, out mPitch, out mRoll);
    mChanged = true;
    mCachedQuaternion = Quaternion.IDENTITY;
}
Also I embedded some description text from your posts.
Just to let you know about the further benefit of your work. :D
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.