Page 1 of 1

Information on Quaternions

Posted: Tue Jan 25, 2005 7:11 am
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.

Posted: Tue Jan 25, 2005 7:21 am
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).

Posted: Tue Jan 25, 2005 7:23 am
by IoN_PuLse
Perhaps this wiki might yield some of the information you're looking for?

Posted: Tue Jan 25, 2005 8:26 am
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?

Posted: Tue Jan 25, 2005 11:53 am
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...

Posted: Tue Jan 25, 2005 12:23 pm
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)

Posted: Tue Jan 25, 2005 3:28 pm
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?

Posted: Tue Jan 25, 2005 3:45 pm
by Robomaniac
can't u just use

node->lookAt(destination)?

Posted: Tue Jan 25, 2005 5:47 pm
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.

Posted: Tue Jan 25, 2005 6:45 pm
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);
}

Posted: Tue Jan 25, 2005 11:28 pm
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.

Posted: Wed Jan 26, 2005 12:41 am
by sinbad
There is no assumed facing. If you use the SceneNode methods they all take a 'default facing' as a parameter.

Posted: Wed Jan 26, 2005 1:37 am
by Clay
Fair enough, but I'm still not sure why the UNIT_Z vector is used.

Posted: Wed Jan 26, 2005 2:11 am
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.

Posted: Fri Jan 28, 2005 11:23 am
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:

Posted: Fri Jan 28, 2005 11:30 am
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:

Posted: Fri Jan 28, 2005 12:07 pm
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.

Posted: Fri Jan 28, 2005 12:44 pm
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.

Posted: Fri Jan 28, 2005 6:24 pm
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!