Calculating position of a item relative to camera - c++

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!

Related

How to get enemies to turn continuously using atan2()

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);
}

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.

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.

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.

Drawing Sphere in OpenGL without using gluSphere()?

Are there any tutorials out there that explain how I can draw a sphere in OpenGL without having to use gluSphere()?
Many of the 3D tutorials for OpenGL are just on cubes. I have searched but most of the solutions to drawing a sphere are to use gluSphere(). There is also a site that has the code to drawing a sphere at this site but it doesn't explain the math behind drawing the sphere. I have also other versions of how to draw the sphere in polygon instead of quads in that link. But again, I don't understand how the spheres are drawn with the code. I want to be able to visualize so that I could modify the sphere if I need to.
One way you can do it is to start with a platonic solid with triangular sides - an octahedron, for example. Then, take each triangle and recursively break it up into smaller triangles, like so:
Once you have a sufficient amount of points, you normalize their vectors so that they are all a constant distance from the center of the solid. This causes the sides to bulge out into a shape that resembles a sphere, with increasing smoothness as you increase the number of points.
Normalization here means moving a point so that its angle in relation to another point is the same, but the distance between them is different.
Here's a two dimensional example.
A and B are 6 units apart. But suppose we want to find a point on line AB that's 12 units away from A.
We can say that C is the normalized form of B with respect to A, with distance 12. We can obtain C with code like this:
#returns a point collinear to A and B, a given distance away from A.
function normalize(a, b, length):
#get the distance between a and b along the x and y axes
dx = b.x - a.x
dy = b.y - a.y
#right now, sqrt(dx^2 + dy^2) = distance(a,b).
#we want to modify them so that sqrt(dx^2 + dy^2) = the given length.
dx = dx * length / distance(a,b)
dy = dy * length / distance(a,b)
point c = new point
c.x = a.x + dx
c.y = a.y + dy
return c
If we do this normalization process on a lot of points, all with respect to the same point A and with the same distance R, then the normalized points will all lie on the arc of a circle with center A and radius R.
Here, the black points begin on a line and "bulge out" into an arc.
This process can be extended into three dimensions, in which case you get a sphere rather than a circle. Just add a dz component to the normalize function.
If you look at the sphere at Epcot, you can sort of see this technique at work. it's a dodecahedron with bulged-out faces to make it look rounder.
I'll further explain a popular way of generating a sphere using latitude and longitude (another
way, icospheres, was already explained in the most popular answer at the time of this writing.)
A sphere can be expressed by the following parametric equation:
F(u, v) = [ cos(u)*sin(v)*r, cos(v)*r, sin(u)*sin(v)*r ]
Where:
r is the radius;
u is the longitude, ranging from 0 to 2π; and
v is the latitude, ranging from 0 to π.
Generating the sphere then involves evaluating the parametric function at fixed intervals.
For example, to generate 16 lines of longitude, there will be 17 grid lines along the u axis, with a step of
π/8 (2π/16) (the 17th line wraps around).
The following pseudocode generates a triangle mesh by evaluating a parametric function
at regular intervals (this works for any parametric surface function, not just spheres).
In the pseudocode below, UResolution is the number of grid points along the U axis
(here, lines of longitude), and VResolution is the number of grid points along the V axis
(here, lines of latitude)
var startU=0
var startV=0
var endU=PI*2
var endV=PI
var stepU=(endU-startU)/UResolution // step size between U-points on the grid
var stepV=(endV-startV)/VResolution // step size between V-points on the grid
for(var i=0;i<UResolution;i++){ // U-points
for(var j=0;j<VResolution;j++){ // V-points
var u=i*stepU+startU
var v=j*stepV+startV
var un=(i+1==UResolution) ? endU : (i+1)*stepU+startU
var vn=(j+1==VResolution) ? endV : (j+1)*stepV+startV
// Find the four points of the grid
// square by evaluating the parametric
// surface function
var p0=F(u, v)
var p1=F(u, vn)
var p2=F(un, v)
var p3=F(un, vn)
// NOTE: For spheres, the normal is just the normalized
// version of each vertex point; this generally won't be the case for
// other parametric surfaces.
// Output the first triangle of this grid square
triangle(p0, p2, p1)
// Output the other triangle of this grid square
triangle(p3, p1, p2)
}
}
The code in the sample is quickly explained. You should look into the function void drawSphere(double r, int lats, int longs):
void drawSphere(double r, int lats, int longs) {
int i, j;
for(i = 0; i <= lats; i++) {
double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats);
double z0 = sin(lat0);
double zr0 = cos(lat0);
double lat1 = M_PI * (-0.5 + (double) i / lats);
double z1 = sin(lat1);
double zr1 = cos(lat1);
glBegin(GL_QUAD_STRIP);
for(j = 0; j <= longs; j++) {
double lng = 2 * M_PI * (double) (j - 1) / longs;
double x = cos(lng);
double y = sin(lng);
glNormal3f(x * zr0, y * zr0, z0);
glVertex3f(r * x * zr0, r * y * zr0, r * z0);
glNormal3f(x * zr1, y * zr1, z1);
glVertex3f(r * x * zr1, r * y * zr1, r * z1);
}
glEnd();
}
}
The parameters lat defines how many horizontal lines you want to have in your sphere and lon how many vertical lines. r is the radius of your sphere.
Now there is a double iteration over lat/lon and the vertex coordinates are calculated, using simple trigonometry.
The calculated vertices are now sent to your GPU using glVertex...() as a GL_QUAD_STRIP, which means you are sending each two vertices that form a quad with the previously two sent.
All you have to understand now is how the trigonometry functions work, but I guess you can figure it out easily.
If you wanted to be sly like a fox you could half-inch the code from GLU. Check out the MesaGL source code (http://cgit.freedesktop.org/mesa/mesa/).
See the OpenGL red book: http://www.glprogramming.com/red/chapter02.html#name8
It solves the problem by polygon subdivision.
My example how to use 'triangle strip' to draw a "polar" sphere, it consists in drawing points in pairs:
const float PI = 3.141592f;
GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles
GLfloat radius = 60.0f;
int gradation = 20;
for (alpha = 0.0; alpha < GL_PI; alpha += PI/gradation)
{
glBegin(GL_TRIANGLE_STRIP);
for (beta = 0.0; beta < 2.01*GL_PI; beta += PI/gradation)
{
x = radius*cos(beta)*sin(alpha);
y = radius*sin(beta)*sin(alpha);
z = radius*cos(alpha);
glVertex3f(x, y, z);
x = radius*cos(beta)*sin(alpha + PI/gradation);
y = radius*sin(beta)*sin(alpha + PI/gradation);
z = radius*cos(alpha + PI/gradation);
glVertex3f(x, y, z);
}
glEnd();
}
First point entered (glVertex3f) is as follows the parametric equation and the second one is shifted by a single step of alpha angle (from next parallel).
Although the accepted answer solves the question, there's a little misconception at the end. Dodecahedrons are (or could be) regular polyhedron where all faces have the same area. That seems to be the case of the Epcot (which, by the way, is not a dodecahedron at all). Since the solution proposed by #Kevin does not provide this characteristic I thought I could add an approach that does.
A good way to generate an N-faced polyhedron where all vertices lay in the same sphere and all its faces have similar area/surface is starting with an icosahedron and the iteratively sub-dividing and normalizing its triangular faces (as suggested in the accepted answer). Dodecahedrons, for instance, are actually truncated icosahedrons.
Regular icosahedrons have 20 faces (12 vertices) and can easily be constructed from 3 golden rectangles; it's just a matter of having this as a starting point instead of an octahedron. You may find an example here.
I know this is a bit off-topic but I believe it may help if someone gets here looking for this specific case.
Python adaptation of #Constantinius answer:
lats = 10
longs = 10
r = 10
for i in range(lats):
lat0 = pi * (-0.5 + i / lats)
z0 = sin(lat0)
zr0 = cos(lat0)
lat1 = pi * (-0.5 + (i+1) / lats)
z1 = sin(lat1)
zr1 = cos(lat1)
glBegin(GL_QUAD_STRIP)
for j in range(longs+1):
lng = 2 * pi * (j+1) / longs
x = cos(lng)
y = sin(lng)
glNormal(x * zr0, y * zr0, z0)
glVertex(r * x * zr0, r * y * zr0, r * z0)
glNormal(x * zr1, y * zr1, z1)
glVertex(r * x * zr1, r * y * zr1, r * z1)
glEnd()
void draw_sphere(float r)
{
float pi = 3.141592;
float di = 0.02;
float dj = 0.04;
float db = di * 2 * pi;
float da = dj * pi;
for (float i = 0; i < 1.0; i += di) //horizonal
for (float j = 0; j < 1.0; j += dj) //vertical
{
float b = i * 2 * pi; //0 to 2pi
float a = (j - 0.5) * pi; //-pi/2 to pi/2
//normal
glNormal3f(
cos(a + da / 2) * cos(b + db / 2),
cos(a + da / 2) * sin(b + db / 2),
sin(a + da / 2));
glBegin(GL_QUADS);
//P1
glTexCoord2f(i, j);
glVertex3f(
r * cos(a) * cos(b),
r * cos(a) * sin(b),
r * sin(a));
//P2
glTexCoord2f(i + di, j);//P2
glVertex3f(
r * cos(a) * cos(b + db),
r * cos(a) * sin(b + db),
r * sin(a));
//P3
glTexCoord2f(i + di, j + dj);
glVertex3f(
r * cos(a + da) * cos(b + db),
r * cos(a + da) * sin(b + db),
r * sin(a + da));
//P4
glTexCoord2f(i, j + dj);
glVertex3f(
r * cos(a + da) * cos(b),
r * cos(a + da) * sin(b),
r * sin(a + da));
glEnd();
}
}
One way is to make a quad that faces the camera and write a vertex and fragment shader that renders something that looks like a sphere. You could use equations for a circle/sphere that you can find on the internet.
One nice thing is that the silhouette of a sphere looks the same from any angle. However, if the sphere is not in the center of a perspective view, then it would appear perhaps more like an ellipse. You could work out the equations for this and put them in the fragment shading. Then the light shading needs to changed as the player moves, if you do indeed have a player moving in 3D space around the sphere.
Can anyone comment on if they have tried this or if it would be too expensive to be practical?