How to get enemies to turn continuously using atan2() - c++

I am trying to build a game where you control a pirate ship and other computer pirate ships try to attack you. I'm currently working on code that gets the enemies to turn to face the player, and I am running into an issue when atan2() returns a value that jumps from 0 to 2PI (I am using the fmod function to change the range from -pi to pi).
The ships will be following and turning with the player until the player crosses over from the 1st quadrant to the 4th quadrant, and then the ship will turn in the exact opposite direction to face the player. Any ideas on how to fix it and make it a continuous smooth turn? Here is my code: rotation is the current angle that the enemy ship is facing
float angleReq = atan2(distToPlayer.y, distToPlayer.x);
angleReq = fmod(angleReq + (2 * M_PI), 2 * M_PI);
if(rotation - angleReq > 0.01) {
rotation -= 0.01;
rotation = fmod(rotation, 2 * M_PI);
} else if (rotation - angleReq < -0.01) {
rotation += 0.01;
rotation = fmod(rotation, 2 * M_PI);
}

I realized how to fix it. As someone stated I was thinking about the problem wrong, I needed to tell the enemy ship to go the "shorter route" in order to get to the required angle based on its current position. Here is the solution code that solved that problem:
float angleReq = atan2(distToPlayer.y, distToPlayer.x);
angleReq = fmod(angleReq + (2 * M_PI), 2 * M_PI);
/* Moves ship counter clockwise from 0 over to 2pi radians */
if ((2 * M_PI - angleReq) + rotation < angleReq - rotation) {
rotation -= 0.01;
rotation = fmod(rotation + (2 * M_PI), 2 * M_PI);
} /* Moves ship clockwise from 2pi over to 0 radians */
else if ((2 * M_PI - rotation) + angleReq < rotation - angleReq) {
rotation += 0.01;
rotation = fmod(rotation + (2 * M_PI), 2 * M_PI);
} /* Moves ship clockwise normally */
else if (angleReq - rotation > 0.01) {
rotation += 0.01;
rotation = fmod(rotation + (2 * M_PI), 2 * M_PI);
} /* Moves ship counterclockwise normally */
else if (angleReq - rotation < -0.01) {
rotation -= 0.01;
rotation = fmod(rotation + (2 * M_PI), 2 * M_PI);
}

Related

How do I convert quaternion values to specific angles

I'm getting quaternion values, and I want to convert these values to 2 angles.
One of the angles is between the "ground plane" (please correct me if this is an incorrect name for it) and the fictional vector between the origin and the quaternion point ( the complementary angle of gamma ).
The second angle is the rotational angle, this is the rotation that the vector has made relatively to a plane perpendicular to the vector ( R in the picture ).
I looked lot up on the internet, but everything I try has a range of -90° to 90°, while I want it to go from -180° to 180°.
This is the last version of code I ended up with:
float gx = 2 * (x*z - w*y);
float gy = 2 * (w*x + y*z);
float gz = w*w - x*x - y*y + z*z;
float yaw = atan2(2*x*y - 2*w*z, 2*w*w + 2*x*x - 1); // about Z axis
float pitch = atan(gx / sqrt(gy*gy + gz*gz)); // about Y axis
float roll = atan(gy/gz); // about X axis
Serial.print(" yaw ");
Serial.print(yaw * 180/M_PI,0);
Serial.print(" pitch ");
Serial.print(pitch * 180/M_PI,2);
Serial.print(" sideways ");
// Don't mind this section as I'm applying a mathematical function I created
if(pitch > 0) Serial.println((roll * 180/M_PI) * (1/(1+pow(1.293,((pitch * 180/M_PI)-51.57)))), 2);
else if(pitch == 0) Serial.println(roll * 180/M_PI, 2);
else if(pitch < 0) Serial.println((roll * 180/M_PI) * (1/(1+pow(1.293,(((pitch) * (-180)/M_PI)-51.57)))), 2);
You should always use atan2(y,x) instead of atan(y/x). It is a common mistake. – Somos
He wrote this on a math form were I asked this too, and that was my stupid mistake -_-
My new version is:
float gx = 2 * (x*z - w*y);
float gy = 2 * (w*x + y*z);
float gz = w*w - x*x - y*y + z*z;
float yaw = atan2(2*x*y - 2*w*z, 2*w*w + 2*x*x - 1); // about Z axis
float pitch = atan2(gx, sqrt(gy*gy + gz*gz)); // about Y axis
float roll = atan2(gy, gz); // about X axis
/*Serial.print(" yaw ");
Serial.print(yaw * 180/M_PI,0);*/
Serial.print(" pitch ");
Serial.print(pitch * 180/M_PI,2);
Serial.print(" sideways ");
// Please don't pay attention to the extra function I made for the project but it doesn't have to do with the problem
if(pitch > 0) Serial.println((roll * 180/M_PI) * (1/(1+pow(1.293,((pitch * 180/M_PI)-51.57)))), 2);
else if(pitch == 0) Serial.println(roll * 180/M_PI, 2);
else if(pitch < 0) Serial.println((roll * 180/M_PI) * (1/(1+pow(1.293,(((pitch) * (-180)/M_PI)-51.57)))), 2);

Calculating position of a item relative to camera

I'm making a 3D-game using C++ and Irrlicht. It is a FPS-style game, so player should be able to carry weapons. But I've been struggling with calculating the position relative to the camera. If camera wouldn't rotate, calculation would be easy:
// node is camera's child
vector3df modifier = vector3df(2, 0, 2);
node->setPosition(node->getPosition() + modifier);
But however, camera isn't a static but a rotating object, so things are a bit more complicated. Here's an image which hopefully crearifies what I'm trying to say:
This should work in all dimensions, X, Y and Z. I think there's only two trigonometric functions for this kind of purpose, sine and cosine, which are for calculating X and Y coordinates. Am I at the wrong path or can they be applied to this? Or is there a solution in Irrlicht itself? There's the code which I've tried to use (found it from SO):
vector3df obj = vector3df(2, 0, 2);
vector3df n = vector3df(0, 0, 0);
n.X = obj.X * cos(60 * DEGTORAD) - obj.Z * sin(60 * DEGTORAD);
n.Z = obj.Z * cos(60 * DEGTORAD) + obj.X * sin(60 * DEGTORAD);
node->setPosition(node->getPosition() + n);
But the weapon just flies forward.
I would be glad for any kind of help or guidance.
P.S. Hopefully this question is clearer than the previous one
The problem with your code is that the rotation is performed around the origin, not around the camera.
What you want to do is to rotate the object (weapon) around the center of the camera by the angle that the camera rotates:
In order to do that you need to perform the following steps:
1 - Translate all the points so that the center of the camera is at the origin,
2 - Apply the rotation matrix (angle is alpha):
[cos (alpha) -sin (alpha)]
[sin (alpha) cos (alpha)]
3 - Undo the step 1 on the rotated point.
Sample algorithm:
Position of the weapon: (xObject, yObject)
Position of the camera: (xCamera, yCamera)
Turning angle: alpha
//step 1:
xObject -= xCamera;
yObject -= yCamera;
// step 2
xRot = xObject * cos(alpha) - yObject * sin(alpha);
yRot = xObject * sin(alpha) + yObject * cos(alpha);
// step 3:
xObject = xRot + xCamera;
yObject = yRot + yCamera;
This algorithm is on XY plane but can be modified for XZ plane. Assuming that in your code obj represent the position of the weapon. Your code can be something like:
...
// Step 1
obj.X-=cam.X;
obj.Z-=cam.Z;
//Step 2
n.X = obj.X * cos(60 * DEGTORAD) - obj.Z * sin(60 * DEGTORAD);
n.Z = obj.Z * cos(60 * DEGTORAD) + obj.X * sin(60 * DEGTORAD);
// Step 3
obj.X = n.X + cam.X;
obj.Z = n.Z + cam.Z;
...
Hope that helps!

What's wrong with my quaternion 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.

C++ AI Rotation Issue

For a game I'm making, I want 3 ships which will all race around the map following a collection of points. It works perfectly fine, except for one point in the map, where the ships decide to rotate almost 360 degrees counter clockwise even though only 10 degrees clockwise should be enough.
The code for calculating the rotation:
vec2 distance = *desiredPosition - position;
float rot = atan2(distance.y, distance.x);
rot = rot * 180.f / PI + 90.f;
if (rot < angle)
{
angle -= dAngle;
boat->RotateImage(-dAngle);
}
if (rot > angle)
{
angle += dAngle;
boat->RotateImage(dAngle);
}
velocity += vec2(acceleration * cos((angle - 90) * PI / 180.0), acceleration * sin((angle - 90) * PI / 180.0));
How do I ensure it won't rotate in the wrong direction there?
Thanks to Richard Byron (accepted answer below), the problem is fixed. Taking the dot product is better than using degrees.
The final code:
vec2 distance = desiredPosition - position;
normal = vec2(sin((angle - 90) * PI / 180.0), cos((angle - 90) * PI / 180.0) * -1);
float dir = normal.x * distance.x + normal.y * distance.y;
//turn
if (dir > 0)
{
angle -= dAngle;
boat->RotateImage(-dAngle);
}
if (dir < 0)
{
angle += dAngle;
boat->RotateImage(dAngle);
}
velocity += vec2(acceleration * cos((angle - 90) * PI / 180.0), acceleration * sin((angle - 90) * PI / 180.0));
The angle the boat turns should be less than 180 degrees either CW or CCW. If it turns more than 180 degrees in one direction it would have been better to turn the other way.
A more general solution would be calculate the distance vector with respect to the boat's frame of reference.
There are a couple of problems with your updated code. Firstly, it should be rot2 = 360 - rot1; (rot1 + 360 is exactly the same angle as rot1).
The second issue is that you are not taking into account that 1 and 359 degrees are almost the same angle. So if abs(rot1 - angle) > 180, then you really want to use 360 - abs(rot1 - angle) in that case. Your later comparisons with rot and angle are a problem for the same reason, and you need to handle angle incrementing above 360 and decrementing below 0.
I could write out code for this, but there's actually a much simpler and faster way to do this. If you take the dot product of the vector (desiredPosition - position) and a vector at right angles to the ships current heading, then you can turn based on the sign of that result. If it's not clear how to do this, let me know and I can expand on it in the comments.

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.