Information on Quaternions

Problems building or running the engine, queries about how to use features etc.
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Information on Quaternions

Post by Clay »

I could ask about 100 questions related to Quaternions, but lets cut to the heart of the matter.

Does anyone have a good reference for Quaternions in their application to Ogre/3D graphics programming they could link me? Mathworld and Wikipedia have very specific information pertaining to the underlying math (which I am going to read after I finish this post), but is there a good "getting started" guide that would be useful?

Thanks.
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Post by Clay »

A quick clarification. I'll learn how they work mathematically on my own. I primarily am interested in specifics of how to use them in ogre (such as ensuring characters walk the correct way by multiplying the orientation of chained scene nodes with the translate vector).
User avatar
IoN_PuLse
Goblin
Posts: 220
Joined: Mon May 31, 2004 5:54 am
Location: Canada

Post by IoN_PuLse »

Perhaps this wiki might yield some of the information you're looking for?
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Post by Clay »

Already read it. =)

I guess I'll just post a specific question. I have the same problem this guy was having http://www.ogre3d.org/phpBB2/viewtopic. ... quaternion. I have a character sitting in space, and I need him to face the direction I'm moving him.

I have a unit vector in the direction that I want him to face mDirection, and his SceneNode mNode. What I think I need to do is call: mNode->yaw( angle );

However, I don't know how to calculate that angle. Is this the right way to aproach this? From the post I linked above, Sinbad seems to suggest I should be using Quaternions instead of trying to yaw the SceneNode, but I am having trouble finding documentation on how to use Slerp. http://www.ogre3d.org/docs/api/html/cla ... aterniond0 does not have any explination.

I also tried this:

Code: Select all

Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_Z;
Ogre::Quaternion quat = src.getRotationTo( mDirection );
but even if this correctly gets the Quatrernion I want, I have no idea what to do with the quat variable to rotate the scene node.

Any ideas?
User avatar
DWORD
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 1365
Joined: Tue Sep 07, 2004 12:43 pm
Location: Aalborg, Denmark

Post by DWORD »

You are nearly there. :)

Code: Select all

Quaternion Ogre::Quaternion::Slerp(Real fT, const Quaternion &rkP, const Quaternion &rkQ, bool shortestPath = false);
rkP and rkQ are source and destination orientations, and fT is a value from 0 to 1 describing the transition (0 = source, 1 = destination, or any value inbetween). shortestPath specifies whether to use the shortest path. :roll:
I also tried this:

Code: Select all

Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_Z; 
Ogre::Quaternion quat = src.getRotationTo( mDirection ); 
but even if this correctly gets the Quatrernion I want, I have no idea what to do with the quat variable to rotate the scene node.
Now you just use the original mNode->getOrientation() as your source (rkP) quaternion, and quat as your destination (rkQ) quaternion. By linearly interpolating fT from 0 to 1 over time in Ogre::Quaternion::Slerp(), you get an intermediate quaternion, which you can use for your scene node's orientation. Hth.

Edit: Ouch, that's not correct... src.getRotationTo() returns 'the shortest arc quaternion to rotate this vector to the destination vector'. So you'll have to use mNode->getOrientation()*quat as your destination quaternion. I hope I got it right this time...
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 8

Post by haffax »

You can apply the Quaternion with SceneNode::rotate().

If nNode has not the the root node as its parent, make sure you use the correct transform space. When mDirection is a direction in world space, then you first need to apply mNodes world orientation to it, before getting the rotation.

Code: Select all

Vector3 transDir = mNode->getWorldOrientation() * mDirection;
(From the top of my head. So there might be errors in it)
team-pantheon programmer
creators of Rastullahs Lockenpracht
Van
Hobgoblin
Posts: 512
Joined: Fri Nov 19, 2004 3:56 am

Post by Van »

Clay,
I hope you have better luck than me in getting your problem to work.

I took this example and did the following:

Code: Select all

		// Code to get SceneNode "NodeArrowTargetVector" to point at SceneNode "CurrentTarget"
		Ogre::Vector3 srcNodeVector = NodeArrowTargetVector->getWorldOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z;
		Ogre::Quaternion destNodeQuant = srcNodeVector.getRotationTo( MySpaceShip->CurrentTarget->Node->getWorldPosition() );
		Ogre::Quaternion srcNewOrientationQuat = Ogre::Quaternion::Slerp(
			1,
			NodeArrowTargetVector->getWorldOrientation(),
			MySpaceShip->CurrentTarget->Node->getWorldOrientation() * destNodeQuant,
			true);

		// Change NodeArrowTargetVector Orientation to now point at CurrentTarget SceneNode
		NodeArrowTargetVector->setOrientation(srcNewOrientationQuat);
The Goal
Point NodeArrowTargetVector SceneNode at CurrentTarget SceneNode.

Result
In one frame, the NodeArrowTargetVector does point at the target. In the next frame is points off into space. On the next frame, it poinsts at the target, and so on, and so on...

I can not see what I am doing wrong. The math keeps coming up with two different solutions (one per frame) causing the arrow to jump back and forth between the two solutions (ie. each frame).

Can someone see what is wrong with this math?
User avatar
Robomaniac
Hobgoblin
Posts: 508
Joined: Tue Feb 03, 2004 6:39 am

Post by Robomaniac »

can't u just use

node->lookAt(destination)?
phear hingo

My Webpage
Van
Hobgoblin
Posts: 512
Joined: Fri Nov 19, 2004 3:56 am

Post by Van »

Robomaniac wrote:can't u just use

node->lookAt(destination)?
Unfortunately, No. If your node(s) rotates in any axis beyond 180 degrees the node will tumble. I can't figure out why it tumbles but it does.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 67

Post by sinbad »

Actually it's exactly 180 degrees that has the problem, and the reason is that there are an infinite number of rotations that can perform the lookAt in that case, therefore the 'up' vector could go almost anywhere. Just try doing it with a model plane or something - point it exactly 180 degrees away from the target and then tell me how you decide how to rotate it to point the other way. Sure, you might intuitively use a yaw but Ogre can't know that - it could yaw, it could pitch, it could do any one of an infinite number of rotations, each one resulting in the plane pointing the other way, but with an infinite number of roll positions. You have to special case this situation, we can't do it for you.

For example:

Code: Select all

Real d = 1.0f + olddirection.dotProduct(newdirection);
if (fabs(d) < 0.00001)
{
    // Diametrically opposed vectors
    // How you resolve this depends on your application
    //  Options include a 180 degree yaw, a 180 degree rotate around the
    //  axis of momentum, etc
}
else
{
    node->lookAt(newdirection);
}
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Post by Clay »

Thanks! This worked perfectly. For those of you that find this thread later:

The following code does the job:

Code: Select all

Ogre::Vector3 src = mNode->getWorldOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z;
Ogre::Quaternion quat = src.getRotationTo( mDirection );
mNode->rotate( Ogre::Quaternion::Slerp(1.0f, mNode->getWorldOrientation(), quat, true ) );

// The last line could also be:
//mNode->rotate( Ogre::Quaternion::nlerp(1.0f, mNode->getWorldOrientation(), quat, true ) );
//mNode->setOrientation( Ogre::Quaternion::Slerp(1.0f, mNode->getWorldOrientation(), quat, true ) );
//mNode->setOrientation( Ogre::Quaternion::nlerp(1.0f, mNode->getWorldOrientation(), quat, true ) );
I have only two final questions:

1) In the above code, I understand the difference between nlerp and Slerp. What I don't understand is the difference between rotate and setOrientation. When I use either of them, they take me to the correct direction, should I be specifically using one of them in the above code?

2) The constant UNIT_Z is seems arbitrary. I understand you need to multiply by SOME vector to get the correct direction, but why UNIT_Z? Is this the default facing of an object in Ogre?

~~~

When I get these things figured out, I'm going to update the wiki with this information.

Edit: The above will fail if this is supposed to be a 180 degree rotation correct? If so I can update the code to handle that special case (and use a dot product to determine when the lines are parallel).
Edit2: Actually that was backwards, I needed NEGATIVE_UNIT_Z, not UNIT_Z.
Last edited by Clay on Wed Jan 26, 2005 1:51 am, edited 1 time in total.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 67

Post by sinbad »

There is no assumed facing. If you use the SceneNode methods they all take a 'default facing' as a parameter.
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Post by Clay »

Fair enough, but I'm still not sure why the UNIT_Z vector is used.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 8

Post by haffax »

Well, many projects (like our) use -Z as the default facing direction. All models are created this way. In the standard right handed coordinate system, that Ogre uses, -Z points "into the monitor". So this choice comes naturally and for methods that take a facing (like lookAt()) NEGATIVE_UNIT_Z is the default parameter.
(I guess you actually mean NEGATIVE_UNIT_Z not UNIT_Z)

The difference between rotate() and setOrientation() is, that rotate() applies the rotation relative to the current orientation in the specified transform space, whereas setOrientation() sets the given rotation relative to the parent node's orientation. This does not depend on the node's former orientation.
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
y721
Gremlin
Posts: 153
Joined: Mon Nov 08, 2004 3:15 am
Location: Taiwan

Post by y721 »

Hi:

I have 2 questions:

Frist is regarding Quaternion * vector3.
Ogre::Vector3 src = mNode->getWorldOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z;
Does v' = q * v mean that v is rotated by q? If it's true, would the node's up vector be q * UNIT_Y?

and what about v' = q * v * q', (q' is conjugate of q), I found the fomula in several resources.

The other is regarding interpolation.
Ogre::Quaternion quat = src.getRotationTo( mDirection );
mNode->rotate( Ogre::Quaternion::Slerp(1.0f, mNode->getWorldOrientation(), quat, true ) );
Since the parameter is 1.0f, function Slerp () = quat.
Would it be replaced with

mNode->rotate (quat);

Thank you.
:roll:
User avatar
DWORD
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 1365
Joined: Tue Sep 07, 2004 12:43 pm
Location: Aalborg, Denmark

Post by DWORD »

y721 wrote:
Ogre::Vector3 src = mNode->getWorldOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z;
Does v' = q * v mean that v is rotated by q? If it's true, would the node's up vector be q * UNIT_Y?
Yes, that's correct. I can't answer your question on v' = q * v * q', though.
y721 wrote:The other is regarding interpolation.
Ogre::Quaternion quat = src.getRotationTo( mDirection );
mNode->rotate( Ogre::Quaternion::Slerp(1.0f, mNode->getWorldOrientation(), quat, true ) );
Since the parameter is 1.0f, function Slerp () = quat.
Would it be replaced with

mNode->rotate (quat);
I also thought about this, and I'm pretty sure it's the same, so Slerp() should really be substitued because it isn't really needed. I didn't have the chance to try this code myself, though, so I wouldn't post any unqualified guesses. :roll:
User avatar
DWORD
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 1365
Joined: Tue Sep 07, 2004 12:43 pm
Location: Aalborg, Denmark

Post by DWORD »

The point in using the Slerp() function would be to interpolate the rotation over time, so that we could get a smooth turn. This would require two quaternions, which we almost already have: a source quaternion which is the original orientation, and a destination quaternion which is the wanted orientation. Warning: Untested code.

Code: Select all

// Get the node's original orientation
Quaternion srcOrientation = node->getOrientation();

// Calculate the node's original direction (in world space)
Vector3 srcDirection = node->getWorldOrientation() * Vector3::NEGATIVE_UNIT_Z;

// Assume that dstDirection is the wanted direction in world space,
// and calculate the necessary rotation.
Quaternion rotation = srcDirection.getRotationTo(dstDirection);

// Calculate wanted destination orientation
Quaternion dstOrientation = srcOrientation * rotation;
Note that it's still necessary to special-case a near 180° rotation. Now we can interpolate over time to get a smooth rotation of the node. E.g. in a frame listener:

Code: Select all

node->setOrientation(Quaternion::Slerp(t, srcOrientation, dstOrientation));
Now we only need to change t over time from 0 to 1.

Edit: Clarifications.
Last edited by DWORD on Sat Jan 29, 2005 4:54 pm, edited 2 times in total.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 8

Post by haffax »

y721 wrote: and what about v' = q * v * q', (q' is conjugate of q), I found the fomula in several resources.
That's what Quaternion::operator*(const Vector3&) does effectivly. Only that this formula is already remodeled. Just write this equation component wise and you see you can convert it to Ogre's implementation.
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Post by Clay »

Since the parameter is 1.0f, function Slerp () = quat.
Would it be replaced with

mNode->rotate (quat);
Wow I made that so much harder than it needed to be. This works perfectly. I'll update the wiki with this change now.

Thanks!