Okay, I'm still somewhat new to these beasts, but after having read several articles on the topic - well... no, I gotta be honest, I was STILL confused. But then I found a small paper on how to actually use them in a game-context and it all seemed easy. Too easy, I suppose. ;)
Anyways,
this [PDF] is where I got the paper. I post this here, because there might be something wrong with it, who knows.
This is my source-code (in C++):
Quaternion.cpp
#include "quaternion.h"
void CQuaternion::Set ( float a, float b, float c, float d )
// sets a quaternion to given values
{
A = a;
B = b;
C = c;
D = d;
}
CQuaternion CQuaternion::operator * ( const CQuaternion &quaternion ) const
// performs multiplication of two quaternions
{
float a, b, c, d;
// calculate
a = ( A * quaternion.D ) + ( D * quaternion.A ) + ( B * quaternion.C ) - ( C * quaternion.B );
b = ( B * quaternion.D ) + ( D * quaternion.B ) + ( C * quaternion.A ) - ( A * quaternion.C );
c = ( C * quaternion.D ) + ( D * quaternion.C ) + ( A * quaternion.B ) - ( B * quaternion.A );
d = ( D * quaternion.D ) - ( A * quaternion.A ) - ( B * quaternion.B ) - ( C * quaternion.C );
return CQuaternion ( a, b, c, d );
}
CQuaternion & CQuaternion::operator *= ( const CQuaternion &quaternion )
// performs multiplication of two quaternions
{
// calculate
A = ( A * quaternion.D ) + ( D * quaternion.A ) + ( B * quaternion.C ) - ( C * quaternion.B );
B = ( B * quaternion.D ) + ( D * quaternion.B ) + ( C * quaternion.A ) - ( A * quaternion.C );
C = ( C * quaternion.D ) + ( D * quaternion.C ) + ( A * quaternion.B ) - ( B * quaternion.A );
D = ( D * quaternion.D ) - ( A * quaternion.A ) - ( B * quaternion.B ) - ( C * quaternion.C );
return *this;
}
CQuaternion Conjugate ( CQuaternion &quaternion )
// conjugates a quaternion
{
CQuaternion ConjugatedQuaternion;
// negate first three elements
ConjugatedQuaternion.Set ( -quaternion.A, -quaternion.B, -quaternion.C, quaternion.D );
return ConjugatedQuaternion;
}
CVertex RotateVertexAroundAxis ( CVertex vertex, CVector3 axis, float angle )
// rotates a vertex a given angle around axis
{
float short_term;
CQuaternion VertexQuaternion, RotationQuaternion, ResultingQuaternion;
// get quaternion from vertex
VertexQuaternion.Set ( vertex.GetX ( ), vertex.GetY ( ), vertex.GetZ ( ), 1.0f );
// normalize length for safety purposes
//Normalize ( axis );
// use short-cut to avoid redundant calculations
short_term = ( float ) ::sin ( angle / 2.0f );
// get quaternion from axis
RotationQuaternion.Set ( axis.X * short_term, axis.Y * short_term, axis.Z * short_term, ( float ) ::cos ( angle / 2.0f ) );
// calculate result
//ResultingQuaternion = RotationQuaternion * VertexQuaternion * Conjugate ( RotationQuaternion );
ResultingQuaternion = Conjugate ( RotationQuaternion ) * VertexQuaternion * RotationQuaternion;
// return rotated vertex
return CVertex ( ResultingQuaternion.A, ResultingQuaternion.B, ResultingQuaternion.C );
}
main.cpp
CVertex Point;
CVector3 Axis;
Point.Set ( 3.0f, 7.0f, -2.0f );
Axis.Set ( 0.156f, -0.312f, 0.937f );
CVertex Result = RotateVertexAroundAxis ( Point, Axis, 30.0f );
printf ( "result: %f %f %f", Result.GetX ( ), Result.GetY ( ), Result.GetZ ( ) );
I played around with the lines that are commented out, because I suspected errors there (the normalization of the axis and the order of multiplications), but I tried all combinations and - while it did change the result - it never fixed my problem...
Okay, as you can see, this is what I'm doing (or, rather, trying to do): I set up a point/vertex (like in the example in the paper, at 3/7/-2), then I set up an axis (again, like in the example, with normalized values by the way) and then I call my function "RotateVertexAroundAxis ( [...])" - problem is: I get wrong values. Take a look:
result: 2.999555 6.998961 -1.999703
Look at the paper to see what I should be getting.
I already checked my conjugate-function and if the paper is correct (and I understood it correctly), it does exactly what I want it to do (invert first three elements, leave fourth untouched).
So, please, take a look at my code, tell me where it does something or doesn't that should or should not be done according to the paper and then let me know. I suspect this might be easy for experts to solve, or maybe it's a dumb typo somewhere, but I can't find it.
While you're at it, general comments and critique on the quaternion-implementation are always appreciated as well.
Thanks ahead of time.