************************************************************************* **** * Laurent Schmalen aka BlackAxe / Kolor proudly presents * * A freeware Quaternion class for use in all non-commercial programs * ************************************************************************* **** Hi guys... You're playing with .3ds keyframing? You are sick of Euler Angles to represent rotations? Then this is the right thing for you. A Quaternion Class :=) 1.) What the heck are Quaternions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Quaternions are hyper-complex numbers. You surely know what a complex number is. If not, let's refresh the theory of them a bit. Complex numbers were invented to solve quadratic equations. They are formed of a real part and an imaginary part and are written like this: a + bi A is the real part, B is the imaginary part, i is the imaginary number, it has been chosen so that i^2 = -1 or i = sqrt(-1) which is impossible in normal maths, but here it is :) Hyper complex numbers are complex numbers with 3 imaginary units. They were invented by Sir William Hamilton. He has been trying for a long time to extend complex numbers to define a multiplication with sense on triples (3dimensional vectors). The October 16th 1843 while he was driving to the Royal British Academy, he got a brain-flash and realized that a multiplication on triples would be impossible but it would be possible on complex numbers with 3 imaginary units, so he took his knife and used it to write his theory in the Broome Bridge in Dublin, where it is still today :) He called the number q = xi + yj + zk + w a "Quaternion". W is the RealPart of q. Normally the imaginary party is used as a vector -> q = (w, v ) = w + v i + v j + v k x y z 2.) How to use this class ~~~~~~~~~~~~~~~~~~~~~~~~~ Now as you know the basics of Quaternions, let's start with how to use them. you define a Quaternion like a normal C++ class or var. Quaternion q; Or if you want it to be pre-initialised Quaternion q(1, 2.0, 3.1, 6.66); If you look in the .H file, you see that there are default arguments, you only needs to specify the real part, if the imaginary parts are not specified then they are supposed to be 0.0 so these work too: Quaternion q(1,2,3); Quaternion q(1,2); Quaternion q = 5.5; imaginary // this sets the real part to 5.5 and the // parts to 0.0 You can print a Quaternion using the normal cout streams as seen in Example.cpp cout << q << endl; // works fine ... Now you can also multiplicate Quaternions. The way to multiplicate Quaternions is rather complex and i don't want to explain it in all details, check the source-code for it ... you can just do Quaternion a(1,2,3,4); Quaternion b(4,3,2,1); Quaternion result = a*b; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!! ! Note that quaternion multiplication is not commutative, so a*b gives another ! result than b*a !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!! you can also do a *= b; this is the same than a = a*b; (of course :)) you can also use Quaternion multiplication to mul normal floats, but i wonder how would want that :) Quaternion q(14); Quaternion result = 2*q; however :) the operator ~ gives the conjugate of a quaternion just call Quaternion a; ~a; // now a is conjugated :) Quaternion b = ~a; // b is the conjugate of a the same way you use the operator - that is used to invert a quaternion Quaternion b = -a; -b; and so on :) you can normalize a Quaternion using the member function Normalize(); Quaternion q(1,2,3,4); q.Normalize(); q is now an unit Quaternion Normalize returns the Quaternion that it is actually called for, this can be handy when printing quat's Quaternion q; cout << q.Normalize() << endl; prints you the normalized quaternion, and q is normalized from now on!!!!!! the same way, the quaternion can be exponented and logarithmed ... Quaternion q(1,2,3),a; q.exp(); a.log(); this comes in handy when doing Spline Quaternion interpolation. 3.) How to use Quaternions in practice ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ok, now you know stuff like multiplying quaternions etc, but how to use them. Well, let's start with rotations ... You can convert the rotation you have in [Angle,Axis] representation easily into a Quaternion ... Let's say you want to rotate and object around the Axis (0.5, 1.0, 0.25); you simply do Quaternion Rotation; float Angle = 90.0; Rotation.FromAxis(Angle*PI/180, 0.5, 1.0, 0.25); note thata the angle must be in radians, so you need to convert first, the 3 other parameters are the (x,y,z) of the axis. Fine. Now you have a rotation Quaternion, but what to do with it ?? simply do float RotationMatrix[3][3]; Rotation.ToMatrix(RotationMatrix); now you can transform your vertices usingt this matrix ... easy huh? :) 4.) How to use this in .3ds ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First off, i want to send big thanks to MRi/DoomsDay and Kombat/Immortals for helping me out with this subject. In .3ds the rotation is described as an Angle,Axis representation. Note that the Y,Z are swapped in .3ds you just do for(int i=0; i < NrOfKeys; i++) { ReadAllStuffFromFile(); RotationKeys[i].FromAxis(Angle, Axis.x, Axis.z, Axis.y); if (i > 0) RotationKeys[i] = RotationKeys[i-1] * RotationKeys[i]; } you must multiply the current quaternion with the previous one, as the rotation is relative to the rotation in the previous Key, expect for Key 0 (frame 0) where the rotation is correct ... 5.) Interpolating Quaternions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Interpolating between Euler Angles is very ugly, you might get the "Glimbal Lock", an error that occurs very often and gives bad results. That's a reason to use Quaternions which give good results when interpolated. Let's come back to our .3ds example Let's say you have a Quaternion Rot1 at Frame 10, and a Quaternion Rot2 at frame 30. now you want to get the Rotation Quaternion at frame 17. How to do this? very easy :) float t = (17-10) / (30-10); or if you like this more float t = (ActualFrame - KeyFrame1) / (KeyFrame2 - KeyFrame1); // t must be between 0 and 1 therefor this little hack // now you do Quaternion ThisFrameRot; ThisFrameRot.Slerp(Rot1, Rot2, t); and you have your rotation at frame 17. You just need to do a ToMatrix now and you can transform your vertices ... Kinda easy, hu :)) 6.) The final bit ~~~~~~~~~~~~~~~~~ Greet/Credit me and my group (KoLoR that is) if you use this in your products. I guess you won't be that arrogant/egoistic to use this without greeting, a little greet in the infofile won't hurt, guys! Keep friendship alive! This thing should be compilable with all kind of Compiles, as it doesn't use any compiler specific functions. I had no problems to get it running under Watcom C++ 10.x and 11.0 and Borland C++ 3.1. And i haven't had the possibility to test others. Even though i have tested this class quite alot, there might still be some errors. If you find errors and you have suggestions/problems/critics, please don't be shy, and contact me. E-Mail: IRC : Laurent.Schmalen@Ci.Educ.Lu #coders on IRCNET (irc.stealth.net) #luxusbuerg on UNDERNET (us.undernet.org, de.undernet.org) see juu later blackaxe/kolor aka Laurent Schmalen