Composing quaternions - opengl

I'm currently working on a 3D scene which has to be rotated by dragging the mouse. To do this, I'm utilizing a ArcBall implementation.
The used framework, immediately transformed the resulting quaternion into a rotation matrix. Any subsequent rotations would also be converted into a matrix. These matrices in turn are multiplied.
I wanted to optimize this by simply multiplying the rotation quaternions. Now the scene seems to be spinning uncontrollably while the original version worked okay(ish).
My code for multiplying quaternions:
public static Quat4f mul(Quat4f q1, Quat4f q2) {
float x = q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x;
float y = -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y;
float z = q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z;
float w = -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w;
return new Quat4f(x,y,z,w);
}
The multiplication is done as (the result is normalized for good measure):
currentRotation = Quat4f.mul(newRotation, currentRotation);
An finally, the rotation is applied as:
glMultMatrixf(currentRotation.toMatrix());
Does anyone know what I'm doing wrong?

glMultMatrixf (...) multiplies the current matrix by the one you pass it. Now here is where things get kind of funny, you have a Quaternion that also seems to be cumulative (named currentRotation). You are accumulating the rotation in two different places.
Chances are, what you actually want is glLoadMatrixf (...). Rather than multiplying the current matrix by your rotation matrix, that will replace the matrix. You could achieve the same result by calling glLoadIdentity (...) immediately before glMultMatrixf (...); the bottom line is you need to discard any old rotation in your matrix before applying this.
It is not clear whether the behavior you are after is such that GL's matrix is reset every time it is updated or if the quaternion is reset, but you really need to reset one of the two or you will spin endlessly.

Related

Reflection of a Quaternion

There is a camera represented by a quaternion [w, x, y, z] associated with a position [x, y, z].
The camera is looking at a mirror (plane) defined as a normal and a point residing on the mirror. This mirror may also be represented as the plane equation AX + BY + CZ + D = 0.
Using the information above, how would the reflected/virtual camera be found?
The quaternion roll should be preserved, ie if you are looking directly into a mirror, the quaternion looking back at you should have the same roll and not be upside down. Ray vs plane can be used to find the virtual camera position, the reflected vector origin and 3D direction, but I'm not sure that helps in finding the quaternion. Finding the virtual camera position was easy enough as shown below (I prefer this over the ray vs. plane tracing method due to parallel or looking away case):
//plane equation ax + by + cz + d = 0
float plane_equation[4];//a, b, c, d
plane_equation[0] = mirror_normal[0];
plane_equation[1] = mirror_normal[1];
plane_equation[2] = mirror_normal[2];
float mults[3];
mults[0] = plane_equation[0] * mirror_position[0];
mults[1] = plane_equation[1] * mirror_position[1];
mults[2] = plane_equation[2] * mirror_position[2];
plane_equation[3] = -1 * (mults[0] + mults[1] + mults[2]);
//find the virtual camera position for reflection using the plane equation
//a(p0 + aT) + b(p1 + bT) + c(p2 + cT) + d = 0
//ap0 + bp1 + cp2 + T(a^2 + b^2 + c^2) + d = 0
//U + TV + d = 0
//T = (-d - U) / V
float t_parts[2];
//U
t_parts[0] = plane_equation[0] * cam_pos[0] + plane_equation[1] * cam_pos[1] + plane_equation[2] * cam_pos[2];
//V
t_parts[1] = plane_equation[0] * plane_equation[0] + plane_equation[1] * plane_equation[1] + plane_equation[2] * plane_equation[2];
float t = (-plane_equation[3] - t_parts[0]) / t_parts[1];
//t gets us to the intersection point. 2t gets us to the virtual point
virtual_cam_pos[0] = 2 * t * plane_equation[0] + cam_pos[0];
virtual_cam_pos[1] = 2 * t * plane_equation[1] + cam_pos[1];
virtual_cam_pos[2] = 2 * t * plane_equation[2] + cam_pos[2];
One solution, it could probably be simplified but this works:
Do ray vs plane. There are 3 cases that need to be taken into account. First is looking towards the mirror so that there is an intersection. Second is looking parallel to the mirror. Third is looking away from the mirror (the mirror may still be visible by the camera, do flipped ray vs plane)
Find the reflected vector
Use the distance from the camera position to the mirror intersection point, intersection point, and reflected vector to find the virtual camera position
Use the vector from the virtual camera to the mirror intersection point to create a quaternion
Apply a local roll to the quaternion. An exact equation to find the appropriate amount of roll for all possible cases is a little unclear to me, but this gets most of the way there

C++ move an object at an angle based on a quaternion rotation matrix [duplicate]

This question already has answers here:
Quaternion rotation without Euler angles
(3 answers)
Closed 4 years ago.
So I'm writing a program where objects move around spacesim-style, in order to learn how to move things smoothly through 3D space. After messing around with Euler angles a bit, it seems they aren't really appropriate for free-form 3D movement in arbitrary directions, so I decided to move on to what seems to be best for the job - quaternions. I intend for the object to rotate around its local X-Y-Z axes at all times, never around the global X-Y-Z axes.
I've tried to implement a system of rotation using quaternions, but something isn't working. When rotating the object along a single axis, if no previous rotations were undertaken, the thing rotates fine along a given axis. However, when applying one rotation after another has been performed, the second rotation is not always along the local axis it's supposed to be rotating along - for instance, after a rotation of about 90° around the Z axis, a rotation around the Y axis still takes place around the global Y axis, rather than the new local Y axis which is aligned with the global X axis.
Huh. So let's go through this step by step. The mistake must be in here somewhere.
STEP 1 - Capture Input
I figured it would be best to use Euler angles (or a Pitch-Yaw-Roll scheme) for capturing player input. At the moment, arrow keys control Pitch and Yaw, whereas Q and E control Roll. I capture player input thus (I am using SFML 1.6):
///SPEEDS
float ForwardSpeed = 0.05;
float TurnSpeed = 0.5;
//Rotation
sf::Vector3<float> Rotation;
Rotation.x = 0;
Rotation.y = 0;
Rotation.z = 0;
//PITCH
if (m_pApp->GetInput().IsKeyDown(sf::Key::Up) == true)
{
Rotation.x -= TurnSpeed;
}
if (m_pApp->GetInput().IsKeyDown(sf::Key::Down) == true)
{
Rotation.x += TurnSpeed;
}
//YAW
if (m_pApp->GetInput().IsKeyDown(sf::Key::Left) == true)
{
Rotation.y -= TurnSpeed;
}
if (m_pApp->GetInput().IsKeyDown(sf::Key::Right) == true)
{
Rotation.y += TurnSpeed;
}
//ROLL
if (m_pApp->GetInput().IsKeyDown(sf::Key::Q) == true)
{
Rotation.z -= TurnSpeed;
}
if (m_pApp->GetInput().IsKeyDown(sf::Key::E) == true)
{
Rotation.z += TurnSpeed;
}
//Translation
sf::Vector3<float> Translation;
Translation.x = 0;
Translation.y = 0;
Translation.z = 0;
//Move the entity
if (Rotation.x != 0 ||
Rotation.y != 0 ||
Rotation.z != 0)
{
m_Entity->ApplyForce(Translation, Rotation);
}
m_Entity is the thing I'm trying to rotate. It also contains the quaternion and rotation matrices representing the object's rotation.
STEP 2 - Update quaternion
I'm not 100% sure this is the way it's supposed to be done, but this is what I tried doing in Entity::ApplyForce():
//Rotation
m_Rotation.x += Rotation.x;
m_Rotation.y += Rotation.y;
m_Rotation.z += Rotation.z;
//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(m_Rotation.x, m_Rotation.y, m_Rotation.z);// * m_qRotation;
m_qRotation.RotationMatrix(m_RotationMatrix);
As you can see, I'm not sure whether it's best to just build a new quaternion from updated Euler angles, or whether I'm supposed to multiply the quaternion representing the change with the quaternion representing the overall current rotation, which is the impression I got when reading this guide. If the latter, my code would look like this:
//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(Rotation.x, Rotation.y, Rotation.z) * m_qRotation;
m_Rotation is the object's current rotation stored in PYR format; Rotation is the change demanded by player input. Either way, though, the problem might be in my implementation of my Quaternion class. Here is the whole thing:
Quaternion::Quaternion(float Pitch, float Yaw, float Roll)
{
float Pi = 4 * atan(1);
//Set the values, which came in degrees, to radians for C++ trig functions
float rYaw = Yaw * Pi / 180;
float rPitch = Pitch * Pi / 180;
float rRoll = Roll * Pi / 180;
//Components
float C1 = cos(rYaw / 2);
float C2 = cos(rPitch / 2);
float C3 = cos(rRoll / 2);
float S1 = sin(rYaw / 2);
float S2 = sin(rPitch / 2);
float S3 = sin(rRoll / 2);
//Create the final values
a = ((C1 * C2 * C3) - (S1 * S2 * S3));
x = (S1 * S2 * C3) + (C1 * C2 * S3);
y = (S1 * C2 * C3) + (C1 * S2 * S3);
z = (C1 * S2 * C3) - (S1 * C2 * S3);
}
//Overload the multiplier operator
Quaternion Quaternion::operator* (Quaternion OtherQuat)
{
float A = (OtherQuat.a * a) - (OtherQuat.x * x) - (OtherQuat.y * y) - (OtherQuat.z * z);
float X = (OtherQuat.a * x) + (OtherQuat.x * a) + (OtherQuat.y * z) - (OtherQuat.z * y);
float Y = (OtherQuat.a * y) - (OtherQuat.x * z) - (OtherQuat.y * a) - (OtherQuat.z * x);
float Z = (OtherQuat.a * z) - (OtherQuat.x * y) - (OtherQuat.y * x) - (OtherQuat.z * a);
Quaternion NewQuat = Quaternion(0, 0, 0);
NewQuat.a = A;
NewQuat.x = X;
NewQuat.y = Y;
NewQuat.z = Z;
return NewQuat;
}
//Calculates a rotation matrix and fills Matrix with it
void Quaternion::RotationMatrix(GLfloat* Matrix)
{
//Column 1
Matrix[0] = (a*a) + (x*x) - (y*y) - (z*z);
Matrix[1] = (2*x*y) + (2*a*z);
Matrix[2] = (2*x*z) - (2*a*y);
Matrix[3] = 0;
//Column 2
Matrix[4] = (2*x*y) - (2*a*z);
Matrix[5] = (a*a) - (x*x) + (y*y) - (z*z);
Matrix[6] = (2*y*z) + (2*a*x);
Matrix[7] = 0;
//Column 3
Matrix[8] = (2*x*z) + (2*a*y);
Matrix[9] = (2*y*z) - (2*a*x);
Matrix[10] = (a*a) - (x*x) - (y*y) + (z*z);
Matrix[11] = 0;
//Column 4
Matrix[12] = 0;
Matrix[13] = 0;
Matrix[14] = 0;
Matrix[15] = 1;
}
There's probably something in there to make somebody wiser than me cringe, but I can't see it. For converting from Euler angles to a quaternion, I used the "first method" according to this source, which also seems to suggest that the equation automatically creates a unit quaternion ("clearly normalized"). For multiplying quaternions, I again drew on this C++ guide.
STEP 3 - Deriving a rotation matrix from the quaternion
Once that is done, as per R. Martinho Fernandes' answer to this question, I try to build a rotation matrix from the quaternion and use that to update my object's rotation, using the above Quaternion::RotationMatrix() code in the following line:
m_qRotation.RotationMatrix(m_RotationMatrix);
I should note that m_RotationMatrix is GLfloat m_RotationMatrix[16], as per the required parameters of glMultMatrix, which I believe I am supposed to use later on when displaying the object. It is initialized as:
m_RotationMatrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
Which I believe is the "neutral" OpenGL rotation matrix (every 4 values together represent a column, correct? Again, I get this from the glMultMatrix page).
STEP 4 - Display!
Finally, we get to the function run each cycle for the object that is supposed to display it.
glPushMatrix();
glTranslatef(m_Position.x, m_Position.y, m_Position.z);
glMultMatrixf(m_RotationMatrix);
//glRotatef(m_Rotation.y, 0.0, 1.0, 0.0);
//glRotatef(m_Rotation.z, 0.0, 0.0, 1.0);
//glRotatef(m_Rotation.x, 1.0, 0.0, 0.0);
//glRotatef(m_qRotation.a, m_qRotation.x, m_qRotation.y, m_qRotation.z);
//[...] various code displaying the object's VBO
glPopMatrix();
I have left my previous failed attempts there, commented out.
Conclusion - Sad panda
That is the conclusion of the life cycle of player input, from cradle to OpenGL-managed grave.
I've obviously not understood something, since the behavior I get isn't the behavior I want or expect. But I'm not particularly experienced with matrix math or quaternions, so I don't have the insight required to see the error in my ways.
Can somebody help me out here?
All you have done is effectively implement Euler angles with quaternions. That's not helping.
The problem with Euler angles is that, when you compute the matrices, each angle is relative to the rotation of the matrix that came before it. What you want is to take an object's current orientation, and apply a rotation along some axis, producing a new orientation.
You can't do that with Euler angles. You can with matrices, and you can with quaternions (as they're just the rotation part of a matrix). But you can't do it by pretending they are Euler angles.
This is done by not storing angles at all. Instead, you just have a quaternion which represents the current orientation of the object. When you decide to apply a rotation to it (of some angle by some axis), you construct a quaternion that represents that rotation by an angle around that axis. Then you right-multiply that quaternion with the current orientation quaternion, producing a new current orientation.
When you render the object, you use the current orientation as... the orientation.
Quaternions represent orientations around 3D compound axes.
But they can also represent 'delta-rotations'.
To 'rotate an orientation', we need an orientation (a quat), and a rotation (also a quat), and we multiply them together, resulting in (you guessed it) a quat.
You noticed they are not commutative, that means the order we multiply them in absolutely matters, just like for matrices.
The order tends to depend on the implementation of your math library, but really, there's only two possible ways to do it, so it shouldn't take you too long to figure out which one is the right one - if things are 'orbiting' instead of 'rotating', then you have them the wrong way around.
For your example of yaw and pitch, I would build my 'delta-rotation' quaternion from yaw, pitch and roll angles, with roll set to zero, and then apply that to my 'orientation' quaternion, rather than doing the rotations one axis at a time.

How to check if vectors are facing same direction

I am working on a question where I am working within a right-handed coordinate system where the y-axis is straight up. I am supplied a structure that represents a 3-dimensional vector that looks like this:
struct vec{float x; float y; float z; };
I need to write a function that takes in a unit vector representing north and a unit vector represent a player's forward vector, and return if they are facing more north than south. Unfortunatly I have no idea where to go from here, I believe I have to do something like:
PlayerDirection = sqrt((PlayerVector.x *= PlayerVector.x)
+ (PlayerVector.y *= PlayerVector.y)
+ (PlayerVector.z *= PlayerVector.z));
But I do not know where to go from here. Any help/explanation would help, thanks.
apply a dot product to both vectors. The dot product will be positive if the angle between both vectors is smaller than 90 degrees, and negative otherwise.
The formula you include (and which contains an error - the product is "*", not "*=") gives you the entity of the movement - the length of the vector. Which you already know, since it is a unit vector and therefore has length 1.
You need instead to perform the dot product between the two vectors. You get 1 if the two unit vectors are completely aligned (parallel), -1 if they're antiparallel, and zero if they're normal to each other.
"More north than south" means that the scalar product is positive, so:
return if they are facing more north than south
Alignment = ((PlayerVector.x * NorthVector.x)
+ (PlayerVector.y * NorthVector.y)
+ (PlayerVector.z * NorthVector.z));
return (Alignment > 0);
Questions
What if I wanted to tell if it was facing east/west?
The dot product tells you how much two vectors are aligned. It is the same formula shown by Kevin Glasson, without the unit vectors norms since they are 1, and dividing by one doesn't change anything.
So, you can't use it to tell where a vector is facing except in terms of another vector. And that's why you're given the north vector; with the player vector alone you can't run a dot product. To tell whether the player is facing east, you need the east vector (or the west vector, and then take the opposite sign).
So if the number comes back as say 35 it means it is facing more north than south but why?
Why it is so: you can find it explained on the Wikipedia page, the dot product is equal to the product of the two lengths by the cosine of their angle. Since the lengths are both 1, it is just the cosine. The cosine varies between 1 and -1 (so you can't ever get 35). When the cosine is 1, it means that the angle is zero and the vectors are aligned; when it is -1, they're opposite. A cosine of zero means the vectors are normal to each other, i.e. form an angle of 90°, and in this case it means that the player is facing either East, West, or Up, or Down - but it doesn't tell you which.
I think what would work is taking the 'dot product' of the vectors. Using the following form:
Where you would re-arrange for theta. This would give you the angle between the two vectors.
In my mind at least if the angle was 0 then you are pointing exactly north and if the angle were greater than 90 then you were facing more south.
I don't know exactly how you intend to use this, but this should work for being able to tell North from South in 3D space.
Well you can use the dot-product to get the angle between 2 vectors. The formula is the following:
cos(phi) = (a * b) / (|a|*|b|)
Which converts to this:
phi = acos((ax*bx + ay*by + az * bz) / (sqrt(ax^2 + ay^2 + az^2)+sqrt(bx^2 + by^2 + bz^2)))
Now the dot product is symmetric, meaning: (1,1,1)(2,2,2) gives you the same result as (2,2,2)(1,1,1). Hence you have to add a further step. By adding a third vector, whose angle to the first given vector you do know, you can verify the true angle between the two vectors. Optaining this reference vector can be done by turning the first vector arround an axis, now to ensure it is a valid reference it has to be in the same plane as the vectors a and b.
This means that your axis to turn your first vector arround has to be orthogonal to vector 1 and 2, the vectorproduct of 2 vectors gives a vector orthogonal to both, so we will use the so obtained vector as axis.
axis = a x b
This is equal to:
axis = (aybz-azby, azbx-axbz, axby-aybx)
To turn a vector by a given amount arround an axis the following has to be done:
double R[3][3] = {};
Vector axis = Axis.getUnitVector();
double deg = degrees / 180 * M_PI;
R[0][0] = axis.X * axis.X * (1 - cos(deg)) + cos(deg); R[0][1] = axis.X * axis.Y * (1 - cos(deg)) - axis.Z * sin(deg); R[0][2] = axis.X * axis.Z * (1 - cos(deg)) + axis.Y * sin(deg);
R[1][0] = axis.Y * axis.X * (1 - cos(deg)) + axis.Z * sin(deg); R[1][1] = axis.Y * axis.Y * (1 - cos(deg)) + cos(deg); R[1][2] = axis.Y * axis.Z * (1 - cos(deg)) - axis.X * sin(deg);
R[2][0] = axis.Z * axis.X * (1 - cos(deg)) - axis.Y * sin(deg); R[2][1] = axis.Z * axis.Y * (1 - cos(deg)) + axis.X * sin(deg); R[2][2] = axis.Z * axis.Z * (1 - cos(deg)) + cos(deg);
double x = this->X * R[0][0] + this->Y * R[0][1] + this->Z * R[0][2];
double y = this->X * R[1][0] + this->Y * R[1][1] + this->Z * R[1][2];
double z = this->X * R[2][0] + this->Y * R[2][1] + this->Z * R[2][2];
The unitvector is defined as following:
e_a = (ax / sqrt(ax^2+ay^2+az^2),ay / sqrt(ax^2+ay^2+az^2),az / sqrt(ax^2+ay^2+az^2))
Using this new axis we can rotate our first vector a 90°. By calculating the angle between our reference and our second vector we can now evaluate the real angle between the first and second vector. If the angle to the reference is bigger than 90° the 2nd vector is in the 3rd or 4th sector or a cartesian coordinate system, meaning to get the real angle we'll have to substract our aquired angle between the first and the 2nd vector from 360°. If the angle to the reference it is smaller than 90° the calculated angle is the real angle.
Now there is another issue, what/where is north? If we know north we could just calculate the angle between north and the two vectors and the one with the smaller angle would be more north. This means there is no reason to evaluate a reference vector or build and apply the rotation matrix.
In case of a fixed north you could also project your vectors on a plane containing north, and simplify the calculations required.
Provide more information and I will edit this.
edit:/ since you provide the north and a player vector, just calculate the angle between them.

Vector3 Matrix Multiplication

Lets say we have:
Vector3 vec;//has some random values
Matrix4x4 mat;//has some random values as well
Now I want to multiply them to get a Vector3 out of it.
Vector3 mult = vec * mat;
Would this function fulfill that:
void Matrix4x4::Transform( Vector3 &oPoint )
{
float fX = oPoint.GetX();
float fY = oPoint.GetY();
float fZ = oPoint.GetZ();
oPoint.SetX( fX * this->matrix[0][0] + fY * this->matrix[0][1] + fZ * this->matrix[0][2] + this->matrix[0][3]);
oPoint.SetY( fX * this->matrix[1][0] + fY * this->matrix[1][1] + fZ * this->matrix[1][2] + this->matrix[1][3]);
oPoint.SetZ( fX * this->matrix[2][0] + fY * this->matrix[2][1] + fZ * this->matrix[2][2] + this->matrix[2][3]);
}
If not, can you lead me in the right direction.
Your code seems ok. I assume that, as you are using OpenGL, your matrix is in column-major order, and that when you want to multiply vector of size 3 on 4x4 matrix, your fourth vector coord is 1.
But, as you are obviously doing computer graphics, you should use vectors of size 4 because of homogenous coordinates. Also I don't really understand, why you want to multiply vector on matrix, and not vice versa.

Using Quaternions for OpenGL Rotations [duplicate]

This question already has answers here:
Quaternion rotation without Euler angles
(3 answers)
Closed 4 years ago.
So I'm writing a program where objects move around spacesim-style, in order to learn how to move things smoothly through 3D space. After messing around with Euler angles a bit, it seems they aren't really appropriate for free-form 3D movement in arbitrary directions, so I decided to move on to what seems to be best for the job - quaternions. I intend for the object to rotate around its local X-Y-Z axes at all times, never around the global X-Y-Z axes.
I've tried to implement a system of rotation using quaternions, but something isn't working. When rotating the object along a single axis, if no previous rotations were undertaken, the thing rotates fine along a given axis. However, when applying one rotation after another has been performed, the second rotation is not always along the local axis it's supposed to be rotating along - for instance, after a rotation of about 90° around the Z axis, a rotation around the Y axis still takes place around the global Y axis, rather than the new local Y axis which is aligned with the global X axis.
Huh. So let's go through this step by step. The mistake must be in here somewhere.
STEP 1 - Capture Input
I figured it would be best to use Euler angles (or a Pitch-Yaw-Roll scheme) for capturing player input. At the moment, arrow keys control Pitch and Yaw, whereas Q and E control Roll. I capture player input thus (I am using SFML 1.6):
///SPEEDS
float ForwardSpeed = 0.05;
float TurnSpeed = 0.5;
//Rotation
sf::Vector3<float> Rotation;
Rotation.x = 0;
Rotation.y = 0;
Rotation.z = 0;
//PITCH
if (m_pApp->GetInput().IsKeyDown(sf::Key::Up) == true)
{
Rotation.x -= TurnSpeed;
}
if (m_pApp->GetInput().IsKeyDown(sf::Key::Down) == true)
{
Rotation.x += TurnSpeed;
}
//YAW
if (m_pApp->GetInput().IsKeyDown(sf::Key::Left) == true)
{
Rotation.y -= TurnSpeed;
}
if (m_pApp->GetInput().IsKeyDown(sf::Key::Right) == true)
{
Rotation.y += TurnSpeed;
}
//ROLL
if (m_pApp->GetInput().IsKeyDown(sf::Key::Q) == true)
{
Rotation.z -= TurnSpeed;
}
if (m_pApp->GetInput().IsKeyDown(sf::Key::E) == true)
{
Rotation.z += TurnSpeed;
}
//Translation
sf::Vector3<float> Translation;
Translation.x = 0;
Translation.y = 0;
Translation.z = 0;
//Move the entity
if (Rotation.x != 0 ||
Rotation.y != 0 ||
Rotation.z != 0)
{
m_Entity->ApplyForce(Translation, Rotation);
}
m_Entity is the thing I'm trying to rotate. It also contains the quaternion and rotation matrices representing the object's rotation.
STEP 2 - Update quaternion
I'm not 100% sure this is the way it's supposed to be done, but this is what I tried doing in Entity::ApplyForce():
//Rotation
m_Rotation.x += Rotation.x;
m_Rotation.y += Rotation.y;
m_Rotation.z += Rotation.z;
//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(m_Rotation.x, m_Rotation.y, m_Rotation.z);// * m_qRotation;
m_qRotation.RotationMatrix(m_RotationMatrix);
As you can see, I'm not sure whether it's best to just build a new quaternion from updated Euler angles, or whether I'm supposed to multiply the quaternion representing the change with the quaternion representing the overall current rotation, which is the impression I got when reading this guide. If the latter, my code would look like this:
//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(Rotation.x, Rotation.y, Rotation.z) * m_qRotation;
m_Rotation is the object's current rotation stored in PYR format; Rotation is the change demanded by player input. Either way, though, the problem might be in my implementation of my Quaternion class. Here is the whole thing:
Quaternion::Quaternion(float Pitch, float Yaw, float Roll)
{
float Pi = 4 * atan(1);
//Set the values, which came in degrees, to radians for C++ trig functions
float rYaw = Yaw * Pi / 180;
float rPitch = Pitch * Pi / 180;
float rRoll = Roll * Pi / 180;
//Components
float C1 = cos(rYaw / 2);
float C2 = cos(rPitch / 2);
float C3 = cos(rRoll / 2);
float S1 = sin(rYaw / 2);
float S2 = sin(rPitch / 2);
float S3 = sin(rRoll / 2);
//Create the final values
a = ((C1 * C2 * C3) - (S1 * S2 * S3));
x = (S1 * S2 * C3) + (C1 * C2 * S3);
y = (S1 * C2 * C3) + (C1 * S2 * S3);
z = (C1 * S2 * C3) - (S1 * C2 * S3);
}
//Overload the multiplier operator
Quaternion Quaternion::operator* (Quaternion OtherQuat)
{
float A = (OtherQuat.a * a) - (OtherQuat.x * x) - (OtherQuat.y * y) - (OtherQuat.z * z);
float X = (OtherQuat.a * x) + (OtherQuat.x * a) + (OtherQuat.y * z) - (OtherQuat.z * y);
float Y = (OtherQuat.a * y) - (OtherQuat.x * z) - (OtherQuat.y * a) - (OtherQuat.z * x);
float Z = (OtherQuat.a * z) - (OtherQuat.x * y) - (OtherQuat.y * x) - (OtherQuat.z * a);
Quaternion NewQuat = Quaternion(0, 0, 0);
NewQuat.a = A;
NewQuat.x = X;
NewQuat.y = Y;
NewQuat.z = Z;
return NewQuat;
}
//Calculates a rotation matrix and fills Matrix with it
void Quaternion::RotationMatrix(GLfloat* Matrix)
{
//Column 1
Matrix[0] = (a*a) + (x*x) - (y*y) - (z*z);
Matrix[1] = (2*x*y) + (2*a*z);
Matrix[2] = (2*x*z) - (2*a*y);
Matrix[3] = 0;
//Column 2
Matrix[4] = (2*x*y) - (2*a*z);
Matrix[5] = (a*a) - (x*x) + (y*y) - (z*z);
Matrix[6] = (2*y*z) + (2*a*x);
Matrix[7] = 0;
//Column 3
Matrix[8] = (2*x*z) + (2*a*y);
Matrix[9] = (2*y*z) - (2*a*x);
Matrix[10] = (a*a) - (x*x) - (y*y) + (z*z);
Matrix[11] = 0;
//Column 4
Matrix[12] = 0;
Matrix[13] = 0;
Matrix[14] = 0;
Matrix[15] = 1;
}
There's probably something in there to make somebody wiser than me cringe, but I can't see it. For converting from Euler angles to a quaternion, I used the "first method" according to this source, which also seems to suggest that the equation automatically creates a unit quaternion ("clearly normalized"). For multiplying quaternions, I again drew on this C++ guide.
STEP 3 - Deriving a rotation matrix from the quaternion
Once that is done, as per R. Martinho Fernandes' answer to this question, I try to build a rotation matrix from the quaternion and use that to update my object's rotation, using the above Quaternion::RotationMatrix() code in the following line:
m_qRotation.RotationMatrix(m_RotationMatrix);
I should note that m_RotationMatrix is GLfloat m_RotationMatrix[16], as per the required parameters of glMultMatrix, which I believe I am supposed to use later on when displaying the object. It is initialized as:
m_RotationMatrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
Which I believe is the "neutral" OpenGL rotation matrix (every 4 values together represent a column, correct? Again, I get this from the glMultMatrix page).
STEP 4 - Display!
Finally, we get to the function run each cycle for the object that is supposed to display it.
glPushMatrix();
glTranslatef(m_Position.x, m_Position.y, m_Position.z);
glMultMatrixf(m_RotationMatrix);
//glRotatef(m_Rotation.y, 0.0, 1.0, 0.0);
//glRotatef(m_Rotation.z, 0.0, 0.0, 1.0);
//glRotatef(m_Rotation.x, 1.0, 0.0, 0.0);
//glRotatef(m_qRotation.a, m_qRotation.x, m_qRotation.y, m_qRotation.z);
//[...] various code displaying the object's VBO
glPopMatrix();
I have left my previous failed attempts there, commented out.
Conclusion - Sad panda
That is the conclusion of the life cycle of player input, from cradle to OpenGL-managed grave.
I've obviously not understood something, since the behavior I get isn't the behavior I want or expect. But I'm not particularly experienced with matrix math or quaternions, so I don't have the insight required to see the error in my ways.
Can somebody help me out here?
All you have done is effectively implement Euler angles with quaternions. That's not helping.
The problem with Euler angles is that, when you compute the matrices, each angle is relative to the rotation of the matrix that came before it. What you want is to take an object's current orientation, and apply a rotation along some axis, producing a new orientation.
You can't do that with Euler angles. You can with matrices, and you can with quaternions (as they're just the rotation part of a matrix). But you can't do it by pretending they are Euler angles.
This is done by not storing angles at all. Instead, you just have a quaternion which represents the current orientation of the object. When you decide to apply a rotation to it (of some angle by some axis), you construct a quaternion that represents that rotation by an angle around that axis. Then you right-multiply that quaternion with the current orientation quaternion, producing a new current orientation.
When you render the object, you use the current orientation as... the orientation.
Quaternions represent orientations around 3D compound axes.
But they can also represent 'delta-rotations'.
To 'rotate an orientation', we need an orientation (a quat), and a rotation (also a quat), and we multiply them together, resulting in (you guessed it) a quat.
You noticed they are not commutative, that means the order we multiply them in absolutely matters, just like for matrices.
The order tends to depend on the implementation of your math library, but really, there's only two possible ways to do it, so it shouldn't take you too long to figure out which one is the right one - if things are 'orbiting' instead of 'rotating', then you have them the wrong way around.
For your example of yaw and pitch, I would build my 'delta-rotation' quaternion from yaw, pitch and roll angles, with roll set to zero, and then apply that to my 'orientation' quaternion, rather than doing the rotations one axis at a time.