To continue on this thought...

How would you use this over time? Glad you asked.

In the action loop of an entity you would want to update the quaternions vector (so the axis you rotate around changes) or the quaternions rotation value (so objects are rotated more or less around the quaternion axis). I'm going to include a test entities action code and explain the pertinent parts.

Code:

action quaternion_test()
{
my.qe1phi = 0.0;my.qe1theta=0.0;
my.qe2phi = 0.0;my.qe2theta=0.0;
my.qe3phi = 0.0;my.qe3theta=0.0;
my.qeAlt = 100;
my.flag1 = off; my.flag2 = off;
while(1)
{
// quaternion testing

qvec1[0] = cos(my.qe1phi) * cos(my.qe1theta); // spherical rotation calculations
qvec1[1] = sin(my.qe1phi) * cos(my.qe1theta); // these are used to create the 3 axis/angles vectors
qvec1[2] = sin(my.qe1theta);

qvec2[0] = cos(my.qe2phi) * cos(my.qe2theta);
qvec2[1] = sin(my.qe2phi) * cos(my.qe2theta);
qvec2[2] = sin(my.qe2theta);

qvec3[0] = cos(my.qe3phi) * cos(my.qe3theta);
qvec3[1] = sin(my.qe3phi) * cos(my.qe3theta);
qvec3[2] = sin(my.qe3theta);

if(my.flag1 == on)
{
qvar1 += 1 * time;
qvar1 %= 360;
//qvar1 = 1;
}
if(my.flag2 == on)
{
qvar2 += 1 * time;
qvar2 %= 360;
}

ql_AAvecToQ(qvec1,qvar1,qtest1); // create a quaternion (qtest1) from the qvec1,qvar1 (axis/angle) pair
ql_AAvecToQ(qvec2,qvar2,qtest2); // create a quaternion (qtest2) from the qvec2,qvar2 (axis/angle) pair

ql_mul(qtest1,qtest2,qtest3); // creates a new quaternion (qtest3) from qtest1 * qtest2
ql_qnormalize(qtest3); // re-normalize the result (qtest3)

ql_QMV(qtest3,qmat1,qvec3); // create the new position from the quaternion (qtest3)

my.x = quatpos.x + (qvec3.x * my.qeAlt); // tranlates and scales the resulting vector (qvec3) so that the
my.y = quatpos.y + (qvec3.y * my.qeAlt); // 3-tuple (x,y,z) values are where we need them.
my.z = quatpos.z + (qvec3.z * my.qeAlt); // this concludes the proof of concept GS usage of quaternions
// --- this places this object (my) at the new coords [x,y,z]
vec_set(temp,qvec2.x); //
vec_scale(temp,my.qeAlt); //
vec_add(temp, quatpos.x); //
vec_set(quat_mkr.x,temp); //

vec_set(temp,vector(qvec1[0],qvec1[1],qvec1[2]));
vec_add(temp,quatpos.x);
vec_sub(temp,quat_axis1.x);
vec_to_angle(quat_axis1.pan,temp);

vec_set(temp,vector(qvec2[0],qvec2[1],qvec2[2]));
vec_add(temp,quatpos.x);
vec_sub(temp,quat_axis2.x);
vec_to_angle(quat_axis2.pan,temp);

vec_set(temp,vector(qvec3[0],qvec3[1],qvec3[2]));
vec_add(temp,quatpos.x);
vec_sub(temp,quat_axis3.x);
vec_to_angle(quat_axis3.pan,temp);

// this segment makes the camera follow and look-at a target, not really useful on a static object, but great for moving objects
/*
vec_set(temp,qvec3.x);
vec_scale(temp,300);
vec_add(temp,quatpos.x);
vec_set(camera.x,temp);
vec_set(temp,quatpos.x);
vec_sub(temp,camera.x);
vec_to_angle(camera.pan,temp);
*/
if(toqli.visible==on)
{
str_for_num(toqli.string[0],qvar1);
str_for_num(toqli.string[1],qvar2);
str_for_num(toqli.string[3],qtest3[0]); // the final quaternion
str_for_num(toqli.string[4],qtest3[1]);
str_for_num(toqli.string[5],qtest3[2]);
str_for_num(toqli.string[6],qtest3[3]);
str_for_num(toql.string[0],qmat1[0]); // show matrix contents
str_for_num(toql.string[1],qmat1[1]);
str_for_num(toql.string[2],qmat1[2]);
str_for_num(toql.string[4],qmat1[3]);
str_for_num(toql.string[5],qmat1[4]);
str_for_num(toql.string[6],qmat1[5]);
str_for_num(toql.string[8],qmat1[6]);
str_for_num(toql.string[9],qmat1[7]);
str_for_num(toql.string[10],qmat1[8]);
str_for_num(toqlo.string[0],qvec1[0]); // the vectors
str_for_num(toqlo.string[1],qvec1[1]);
str_for_num(toqlo.string[2],qvec1[2]);
str_for_num(toqlo.string[4],qvec2[0]);
str_for_num(toqlo.string[5],qvec2[1]);
str_for_num(toqlo.string[6],qvec2[2]);
str_for_num(toqlo.string[8],qvec3[0]);
str_for_num(toqlo.string[9],qvec3[1]);
str_for_num(toqlo.string[10],qvec3[2]);
}
wait(1);
}
// end quaternion testing
}



What you're seeing me do here is to create 3 vectors at the top of the loop, then I increment a couple of global counters. These counters represent the quaternion rotation values for 2 separate quaternions. You see I recreate the quaternions every iteration of the loop. This is necessary because I am changing the quaternion rotation values and external to this action I am altering the 3 vectors pan/tilt (phi/theta) angles so I can move the quaternions around as well.

Next is an example of what makes quaternions really cool!!! Quaternion multiplication with another Quaternion. This may not sound like much, but this allows for things like Linear Interpolation, which is the steady rotation of a quaternion. It has other uses, but again, there are tons of resources on the subject available on the net.

You'll next see that I am moving the object this action is assigned to. I have several helper objects(entities) loaded elsewhere that I place in the world as well. I said earlier this was my example code and I was using it to demonstrate to a friend what a quaternion is and how it is applied. I move my 3 helper objects then display in a few text objects a bunch of output.

This demonstrates updating a quaternion, multiplying quaternions, moving objects and displaying information.

One thing to note is the call to ql_qnormalize(). Quaternions are 4-element vectors and MUST always be normalized (length of the vector is 1). A non-normalized quaternion is no longer considered a quaternion but rather something undefined. The function ql_AAvecToQ does 2 things, it normalizes the input vector and then normalizes the quaternion before exiting. I have since streamlined my code to include all the relevant code down into a function so no other function calls are necessary, but for the sake of clarity I decided to post this pre-optimized version so you could see what was happening.

The big thing about the Conitec rotations is that they are based on Z-X-Z rotations and not true 3-axis Z-Y-X rotation. If you have a plane at the origin (0,0,0) and it's facing along the positive +X axis (pan = 0), you can't roll the plane then change it's pan so that the nose follows the angle of the roll. The nose always rotates around the vertical +Z axis. These quaternions functions do not overcome that limitation. It would take an external DLL that handles model rotation and probably the graphics pipeline as well to overcome the limitation. I've seen several posts asking about 6-degrees of freedom rotation. The GStudio engine doesn't do it, or at least the limited trial 6.2 version doesn't. For real 6D freedom it would have to allow arbitrary axis rotation in the engine code, which is where quaternions shine.

There are numerous ways to use quaternions, my example was only to demonstrate both GS as a potential design platform and to give a friend a visual aid in understanding some math. My demo action is not the best way to use them, it's not the worst way to use them, it's just "a" way to use them.

As for getting and testing the latest incarnation of GS, I've thought about it, but after butting up against a lot of limited functionality I can only imagine similar limits would be more so. I've been trying to get a small dev house to purchase the full, high-end package so I can give the whole smash a whirl but they're not entirely impressed with GS, not enough to fork out the $900. It's got some promise, but I can't see living well with the wife were I to spend that kind of cash on a potential useable platform. Maybe next year. In the meantime as I run across posts asking for help with some of the more esoteric aspects of game programming, I'll try to lend a hand with code and explanation.

I do make some assumptions in my writing. I assume you know how to code C-Script and that calling functions, creating actions, etc, is old-hat to you. Those subjects are covered in the forum as well. If you do have a specific question on how to use a quaternion, speak up. Right now there is a C-Lite quaternion thread and I'm letting that gentleman explain his snippets.

I hope these 2 posts answer some questions, give some examples and useable code to the GS community.

Quantum Mechanic
Better living at the subatomic level.

Last edited by quantum69; 03/28/07 23:54.

------------------------------------
Quantum Mechanic
Better living at the subatomic level