Simple (hopefully) quaternion rotation help...

Started by
6 comments, last by Dookie 16 years, 9 months ago
Hello! I'm trying to figure out how to get an object to spin around a point along the Z axis, while the axis itself is slowly spinning. Try to imagine a spinning bicycle wheel on a bike that was thrown off a cliff (the wheel is spinning along its axle, while the bike is spinning all over the place as it falls to the ground). I want an object that spins around a point like that bicycle wheel, and I want to axis of rotation to spin around similar to the bike falling off the cliff. This code almost works...
	float pPoint[3], rRadius;

	// Location of point...
	pPoint[0] = 320.0f;
	pPoint[1] = 200.0f;
	pPoint[2] = 500.0f;

	// Rotation radius...
	rRadius = 96.0f;

	// Increment rotation values for quad...
	quad->pRotVal[0] += frameTime * 12.0f;
	quad->pRotVal[1] += frameTime * 0.8f;
	quad->pRotVal[2] += frameTime * 0.2f;

	// Apply rotation values to location of quad!
	quad->newLoc[0] = pPoint[0] + rRadius * (cos(quad->pRotVal[0]) * sin(quad->pRotVal[1]));
	quad->newLoc[1] = pPoint[1] + rRadius * (sin(quad->pRotVal[0]) * cos(quad->pRotVal[2]));
	quad->newLoc[2] = pPoint[2] + rRadius * (cos(quad->pRotVal[1]) * sin(quad->pRotVal[2]));
... but the rotating object sometimes stops and the radius sometimes collapses (gimbal lock). I want the radius and spin speed along the Z axis to stay constant. How would I implement quaternion rotation (or a more appropriate rotation scheme) to get this effect to work? This is turning into a real head-scratcher for me! Thanks in advance for the help, I really appreciate it! [smile]
"The crows seemed to be calling his name, thought Caw"
Advertisement
As I find myself saying quite often, the use of quaternions is completely incidental here, so you might as well forget about them for the time being.

What you're talking about is basically just a hierarchy of transforms (in your bicycle example, the bicycle frame would be the root of the tree, and the wheels would be child nodes of the root). How to set up and apply these transforms depends on what graphics API and/or math library you're using.
Thanks for the info jyk, but I dunno if we're talking about the same effect (I really stink at explaining what I'm trying to do). Graphics API and math libraries aren't an issue here, since I'm working with basic math functions (sin(), cos(), sqrt(), etc), and simple float and double variables and arrays. I'm not using API-specific matricies or anything like that.

If I perform the Z rotation (wheel), then the rotation along the X axis, then the rotation along the Y axis, (three rotation stages) there's times when the X/Y rotation stages either cancel the Z rotation (wheel) or collapse the Z radius (distance between the wheel's tire and the axle). You'd have to test my code to see what I'm talking about.

What I'm trying to accomplish is getting an object to orbit another object, but I want the axis that the object is orbiting around to wobble... Maybe a crashing bicycle is a bad visual aid... Imagine a dot on a gyroscope wheel (the dot is my game object), and the axes of the gyroscope are rotating and spinning while the dot spins with the gyroscope wheel. I'd like to do it using quaternions, just so I can figure out how they work.

Hope somebody can help me with this one.
"The crows seemed to be calling his name, thought Caw"
Quote:Original post by Dookie
Thanks for the info jyk, but I dunno if we're talking about the same effect...
I'm fairly certain that we are indeed talking about the same effect :)

As I think you're discovering, doing this sort of thing manually by manipulating vector elements using trig functions and whatnot is quite awkward and prone to error. This is one of the reasons why we typically use matrices for these sorts of operations.

I will also say that quaternions really aren't the answer here. In this context at least, quaternions are usually used to represent pure rotations, whereas (or so I gather) you're talking about a hierarchy of affine transforms (an affine transform is a linear transform - probably a pure rotation in this case - combined with a translation).

The easiest way to do what you're wanting would probably be to write or otherwise get a hold of a matrix math library, and then construct, combine, and manipulate the transforms in matrix form. It may be that you'll still have the same problems (e.g. Euler-angle issues and numerical drift), but your code will be much easier to test and debug once you get all the operations in matrix form.

Also, I assume you're rendering something, right? If so, what API are you using for the rendering?
Thanks again for the reply, jyk!

I'm rendering in Direct3D, but it's just simple 2D quads in a 2D game. I'd like to get one quad to orbit another one, but I'd like the orbit to look something like it's spinning around in 3D space. I can get a nice circular orbit using this formula:

Xnew = Radius * (Xold*cos(theta) - Yold*sin(theta));
Ynew = Radius * (Xold*sin(theta) + Yold*cos(theta));

But as soon as I try to rotate around more than just the 'Z' axis, my sin/cos math starts to cancel each other out... The orbiting quad sometimes falls in on the object it's orbiting (x/y axes cancel each other out) or the orbiting object slows to a stop and then starts orbiting again (gimbal lock).

I'll look at some matrix stuff I have in a couple of books and see what I can come up with. But I'm all ears if you want to point me to any handy references! [smile]
"The crows seemed to be calling his name, thought Caw"
Hey, just out of curiosity, what's wrong with this code:

struct Quaternion{	double x, y, z, w;};...void VecToQuat(float *vAxis, float fAngle, Quaternion* qOut){	double sin_a, cos_a;	VecNormalize3D(vAxis);	sin_a = sin( fAngle / 2.0f );	cos_a = cos( fAngle / 2.0f );	qOut->x = vAxis[0] * sin_a;	qOut->y = vAxis[1] * sin_a;	qOut->z = vAxis[2] * sin_a;	qOut->w = cos_a;}void QuatConjugate(Quaternion* qIn, Quaternion* qOut){	qOut->x = -qIn->x;	qOut->y = -qIn->y;	qOut->z = -qIn->z;}// v' = qr * v * iqrvoid QuatRotate(float *vAxis, float fAngle, float *vOut){	Quaternion	qRotate, qOut, qConj;	float fTemp;	VecToQuat(vAxis, fAngle, &qRotate);	QuatConjugate(&qRotate, &qConj);	vOut[0] = (float)(qRotate.x * vOut[0] * qConj.x);	vOut[1] = (float)(qRotate.y * vOut[1] * qConj.y);	vOut[2] = (float)(qRotate.z * vOut[2] * qConj.z);}


Let's say quadLoc[3] is the x/y/z location of a game object. rotAngle is a value in radians. rotAxis[3] is a normalized vector. If I execute QuatRotate(rotAxis, rotAngle, quadLoc), I don't get what I expect (a rotation based on vOut's current value). What is QuatRotate returning? A vector, or a new location? Or is something wrong in this code? Or am I using this code incorrectly?

Hope you can help me out on this, I'd really like to understand how it works! [smile]
"The crows seemed to be calling his name, thought Caw"
Quote:Original post by Dookie
Hey, just out of curiosity, what's wrong with this code:
// v' = qr * v * iqrvoid QuatRotate(float *vAxis, float fAngle, float *vOut){	Quaternion	qRotate, qOut, qConj;	float fTemp;	VecToQuat(vAxis, fAngle, &qRotate);	QuatConjugate(&qRotate, &qConj);	vOut[0] = (float)(qRotate.x * vOut[0] * qConj.x);	vOut[1] = (float)(qRotate.y * vOut[1] * qConj.y);	vOut[2] = (float)(qRotate.z * vOut[2] * qConj.z);}

That's not how you multiply quaternions.

Anyway, unless you are using a software renderer that uses quaternions exclusively or unless you intend to transform all the vertexes in software on the CPU before rendering, you will have to set up matrixes for rendering. So you might as well use matrixes rather than quaternions since you have to use them anyway.

I recommend that you convert your code to use matrices. In the short term it might be more work, but in the long term it will save you time and frustration. To be honest, I can't tell why your code is acting strangely because I am used to looking at operations on matrixes and not the complex trigonometric manipulations that the matrixes would simplify.


[Edited by - JohnBolton on July 23, 2007 9:02:18 PM]
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Thanks for the tip, JohnBolton. And sorry about my bull-headedness about the quaternions, jyk.

I set up a few rotation matrices and found that if I first transform the rotation along the Z axis, then the gimbal lock that occurs on the X or Y axis isn't as noticeable. I'm doing my rotations in three stages - I first rotate on the Z, then pass the results to the Y rotation matrix, then pass those results to the X rotation matrix. It ain't perfect but it looks good enough for my game, so I'll probably just go with that.

Here's my code, if you want to see it. It could probably be optimized, but I left it as is for my own clarity's sake...

void CompoundRotate(float *vRotRads, float *vRotVec, float *vOut){	float xo, x1, x2, x3;	float yo, y1, y2, y3;	float zo, z1, z2, z3;	xo = vRotVec[0];	yo = vRotVec[1];	zo = vRotVec[2];	// Z	x1 = xo*(float)cos(vRotRads[2]) - yo*(float)sin(vRotRads[2]);	y1 = xo*(float)sin(vRotRads[2]) + yo*(float)cos(vRotRads[2]);	z1 = zo;	// Y	x2 = x1*(float)cos(vRotRads[1]) + z1*(float)sin(vRotRads[1]);	y2 = y1;	z2 = z1*(float)cos(vRotRads[1]) - x1*(float)sin(vRotRads[1]);	// X	x3 = x2;	y3 = y2*(float)cos(vRotRads[0]) - z2*(float)sin(vRotRads[0]);	z3 = y2*(float)sin(vRotRads[0]) + z2*(float)cos(vRotRads[0]);	// Final result!	vOut[0] = x3;	vOut[1] = y3;	vOut[2] = z3;}

Notice the three-stage approach... The array 'vRotRads[3]' is three radian floats, 'vRotVec[3]' is the axis of rotation, and 'vOut[3]' is the rotation result. LOTS of sin() and cos() math going on here! Is this the way to do it, or is there a way to do the same thing with only one matrix?
"The crows seemed to be calling his name, thought Caw"

This topic is closed to new replies.

Advertisement