Euler camera, rotation around x axis in camera local system - c++

I have camera class that hold her orientation via euler angles and position. Something like that:
float m_x;
float m_y;
float m_z;
Vector4 m_pos;
And i want to move this camera free over the space.
When user move mouse up-down camera must rotate around x axis in her own coordinate system. But i want to hold only this three angles and position and nothing more.
So algorithm looks like this:
Find camera local system axis (u, v, n)
Rotate around u axis on angle alpha
Find angles around (1, 0, 0), (0, 1, 0), (0, 0, 1) that answer to rotation on angle alpha around u axis
Add them to m_x, m_y, m_z
Question is: how can i calculate rotation angles in default coordinate system (i mean in (1, 0, 0), (0, 1, 0) and (0, 0, 1)) that answer to rotation angles in local camera coordinate system?
Or may be better solution exists for this problem?

I'm answering the concise question from your comment:
how to calculate rotation in one coordinate system that answer to rotation in another coordinate system?
You can transform rotations between coordinate systems by applying a suitable transformation matrix. This in turn can be computed form the euler angles, see the Wikipedia section on conversion formulae.
Depending on your application, you might or might not have to take translations into account as well. As I understand your question, you can concentrate on the rotation part of each transformation.

Related

rotate the camera around an object in spherical coordinates glulookat

so i'm trying to rotate the camera around a cube object , using the keyboard arrows to change the y angle and the x angle , i want a result like this : this video
Well now I want to move the camera in a circular motion around the shape, so I used
the sphere parametric equation to determine the camera's x, y, and z coordinates on the parameter of the sphere using the x angle and the y angle , this is the equation : sphere parametric equation
For this moment, the equation is as following:
x = radius*cos(y_angle)*cos(x_angle)
y = radius*cos(y_angle)*sin(x_angle)
z = radius*sin(y_angle)
so the code will be :
gluLookAt(x, y, z, 0, 0, 0, 0, 1, 0); // x,y and z that we calculated above
Now it is supposed to work without problems, but the camera dose rotate in a strange way that is not as it should, I think this has something to do with the up vector in the
glulookat() function because i'm using a constant up vector like
(0,1,0)
Anyway, I can't calculate the up vector correctly, so I want help in making a circular motion around the cube like the one that appeared in the video using the glulookat , i hope i made it clear ، Because as you have noticed, I can't explain well

How to correctly represent 3D rotation in games

In most 3D platform games, only rotation around the Y axis is needed since the player is always positioned upright.
However, for a 3D space game where the player needs to be rotated on all axises, what is the best way to represent the rotation?
I first tried using Euler angles:
glRotatef(anglex, 1.0f, 0.0f, 0.0f);
glRotatef(angley, 0.0f, 1.0f, 0.0f);
glRotatef(anglez, 0.0f, 0.0f, 1.0f);
The problem I had with this approach is that after each rotation, the axises change. For example, when anglex and angley are 0, anglez rotates the ship around its wings, however if anglex or angley are non zero, this is no longer true. I want anglez to always rotate around the wings, irrelevant of anglex and angley.
I read that quaternions can be used to exhibit this desired behavior however was unable to achieve it in practice.
I assume my issue is due to the fact that I am basically still using Euler angles, but am converting the rotation to its quaternion representation before usage.
struct quaternion q = eulerToQuaternion(anglex, angley, anglez);
struct matrix m = quaternionToMatrix(q);
glMultMatrix(&m);
However, if storing each X, Y, and Z angle directly is incorrect, how do I say "Rotate the ship around the wings (or any consistent axis) by 1 degree" when my rotation is stored as a quaternion?
Additionally, I want to be able to translate the model at the angle that it is rotated by. Say I have just a quaternion with q.x, q.y, q.z, and q.w, how can I move it?
Quaternions are very good way to represent rotations, because they are efficient, but I prefer to represent the full state "position and orientation" by 4x4 matrices.
So, imagine you have a 4x4 matrix for every object in the scene. Initially, when the object is unrotated and untraslated, this matrix is the identity matrix, this is what I will call "original state". Suppose, for instance, the nose of your ship points towards -z in its original state, so a rotation matrix that spin the ship along the z axis is:
Matrix4 around_z(radian angle) {
c = cos(angle);
s = sin(angle);
return Matrix4(c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
now, if your ship is anywhere in space and rotated to any direction, and lets call this state t, if you want to spin the ship around z axis for an angle amount as if it was on its "original state", it would be:
t = t * around_z(angle);
And when drawing with OpenGL, t is what you multiply for every vertex of that ship. This assumes you are using column vectors (as OpenGL does), and be aware that matrices in OpenGL are stored columns first.
Basically, your problem seems to be with the order you are applying your rotations. See, quaternions and matrices multiplication are non-commutative. So, if instead, you write:
t = around_z(angle) * t;
You will have the around_z rotation applied not to the "original state" z, but to global coordinate z, with the ship already affected by the initial transformation (roatated and translated). This is the same thing when you call the glRotate and glTranslate functions. The order they are called matters.
Being a little more specific for your problem: you have the absolute translation trans, and the rotation around its center rot. You would update each object in your scene with something like:
void update(quaternion delta_rot, vector delta_trans) {
rot = rot * delta_rot;
trans = trans + rot.apply(delta_trans);
}
Where delta_rot and delta_trans are both expressed in coordinates relative to the original state, so, if you want to propel your ship forward 0.5 units, your delta_trans would be (0, 0, -0.5). To draw, it would be something like:
void draw() {
// Apply the absolute translation first
glLoadIdentity();
glTranslatevf(&trans);
// Apply the absolute rotation last
struct matrix m = quaternionToMatrix(q);
glMultMatrix(&m);
// This sequence is equivalent to:
// final_vertex_position = translation_matrix * rotation_matrix * vertex;
// ... draw stuff
}
The order of the calls I choose by reading the manual for glTranslate and glMultMatrix, to guarantee the order the transformations are applied.
About rot.apply()
As explained at Wikipedia article Quaternions and spatial rotation, to apply a rotation described by quaternion q on a vector p, it would be rp = q * p * q^(-1), where rp is the newly rotated vector. If you have a working quaternion library implemented on your game, you should either already have this operation implemented, or should implement it now, because this is the core of using quaternions as rotations.
For instance, if you have a quaternion that describes a rotation of 90° around (0,0,1), if you apply it to (1,0,0), you will have the vector (0,1,0), i.e. you have the original vector rotated by the quaternion. This is equivalent to converting your quaternion to matrix, and doing a matrix to colum-vector multiplication (by matrix multiplication rules, it yields another column-vector, the rotated vector).

How to properly rotate a quaternion along all axis?

I want to code a first person camera with its rotation stored in a quaternion. Unfortunately there is something wrong with the rotation.
The following function is responsible to rotate the camera. The parameters Mouse and Speed pass the mouse movement and rotation speed. Then the function fetches the rotation quaternion, rotates it and stores the result. By the way, I'm using Bullet Physics that is where the types and functions come from.
void Rotate(vec2 Mouse, float Speed)
{
btTransform transform = camera->getWorldTransform();
btQuaternion rotation = transform.getRotation();
Mouse = Mouse * Speed; // apply mouse sensitivity
btQuaternion change(Mouse.y, Mouse.x, 0); // create quaternion from angles
rotation = change * rotation; // rotate camera by that
transform.setRotation(rotation);
camera->setWorldTransform(transform);
}
To illustrate the resulting camera rotation when the mouse moves, I show you a hand drawing. On the left side the wrong rotation the camera actually performs is shown. On the right side the desired correct case is shown. The arrows indicate how the camera is rotate when moving the mouse up (in orange) and down (in blue).
As you can see, as long as the yaw is zero, the rotation is correct. But the more yaw it has, the smaller the circles in which the camera rotates become. In contrast, the circles should always run along the whole sphere like a longitude.
I am not very familiar with quaternions, so here I ask how to correctly rotate them.
I found out how to properly rotate a quaternion on my own. The key was to find vectors for the axis I want to rotate around. Those are used to create quaternions from axis and angle, when angle is the amount to rotate around the actual axis.
The following code shows what I ended up with. It also allows to roll the camera, which might be useful some time.
void Rotate(btVector3 Amount, float Sensitivity)
{
// fetch current rotation
btTransform transform = camera->getWorldTransform();
btQuaternion rotation = transform.getRotation();
// apply mouse sensitivity
Amount *= Sensitivity;
// create orientation vectors
btVector3 up(0, 1, 0);
btVector3 lookat = quatRotate(rotation, btVector3(0, 0, 1));
btVector3 forward = btVector3(lookat.getX(), 0, lookat.getZ()).normalize();
btVector3 side = btCross(up, forward);
// rotate camera with quaternions created from axis and angle
rotation = btQuaternion(up, Amount.getY()) * rotation;
rotation = btQuaternion(side, Amount.getX()) * rotation;
rotation = btQuaternion(forward, Amount.getZ()) * rotation;
// set new rotation
transform.setRotation(rotation);
camera->setWorldTransform(transform);
}
Since I rarely found information about quaternion rotation, I'll spend some time further explaining the code above.
Fetching and setting the rotation is specific to the physics engine and isn't related to this question so I won't elaborate on this. The next part, multiplying the amount by a mouse sensitivity should be really clear. Let's continue with the direction vectors.
The up vector depends on your own implementation. Most conveniently, the positive Y axis points up, therefore we end up with 0, 1, 0.
The lookat vector represents the direction the camera looks at. We simply rotate a unit vector pointing forward by the camera rotation quaternion. Again, the forward pointing vector depends on your conventions. If the Y axis is up, the positive Z axis might point forward, which is 0, 0, 1.
Do not mix that up with the next vector. It's named forward which references to the camera rotation. Therefore we just need to project the lookat vector to the ground. In this case, we simply take the lookat vector and ignore the up pointing component. For neatness we normalize that vector.
The side vector points leftwards from the camera orientation. Therefore it lies perpendicular to both the up and the forward vector and we can use the cross product to compute it.
Given those vectors, we can correctly rotate the camera quaternion around them. Which you start with, Z, Y or Z, depends on the Euler angle sequence which is, again, a convention varying from application to application. Since I want to rotations to be applied in Y X Z order, I do the following.
First, rotate the camera around the up axis by the amount for the Y rotation. This is yaw.
Then rotate around the side axis, which points leftwards, by the X amount. It's pitch.
And lastly, rotate around the forward vector by the Z amount to apply roll.
To apply those rotations, we need to multiply the quaternions create by axis and angle with the current camera rotation. Lastly we apply the resulted quaternion to the body in the physics simulation.
Matrices and pitch/yaw/roll both having their limitations, I do not use them anymore but use instead quaternions. I rotate the view vector and recalculate first the camera vectors, then the view matrix in regard to the rotated view vector.
void Camera::rotateViewVector(glm::quat quat) {
glm::quat rotatedViewQuat;
quat = glm::normalize(quat);
m_viewVector = glm::normalize(m_viewVector);
glm::quat viewQuat(0.0f,
m_viewVector.x,
m_viewVector.y,
m_viewVector.z);
viewQuat = glm::normalize(viewQuat);
rotatedViewQuat = (quat * viewQuat) * glm::conjugate(quat);
rotatedViewQuat = glm::normalize(rotatedViewQuat);
m_viewVector = glm::normalize(glm::vec3(rotatedViewQuat.x, rotatedViewQuat.y, rotatedViewQuat.z));
m_rightVector = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_viewVector));
m_upVector = glm::normalize(glm::cross(m_viewVector, m_rightVector));
}

OpenGL simultaneous translate and rotate around local axis

I am working on an application that has similar functionality to MotionBuilder in its viewport interactions. It has three buttons:
Button 1 rotates the viewport around X and Y depending on X/Y mouse drags.
Button 2 translates the viewport around X and Y depending on X/Y mouse drags.
Button 3 "zooms" the viewport by translating along Z.
The code is simple:
glTranslatef(posX,posY,posZ);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
Now, the problem is that if I translate first, the translation will be correct but the rotation then follows the world axis. I've also tried rotating first:
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glTranslatef(posX,posY,posZ);
^ the rotation works, but the translation works according to world axis.
My question is, how can I do both so I achieve the translation from code snippet one and the rotation from code snippet 2.
EDIT
I drew this rather crude image to illustrate what I mean by world and local rotations/translations. I need the camera to rotate and translate around its local axis.
http://i45.tinypic.com/2lnu3rs.jpg
Ok, the image makes things a bit clearer.
If you were just talking about an object, then your first code snippet would be fine, but for the camera it's quite different.
Since there's technically no object as a 'camera' in opengl, what you're doing when building a camera is just moving everything by the inverse of how you're moving the camera. I.e. you don't move the camera up by +1 on the Y axis, you just move the world by -1 on the y axis, which achieves the same visual effect of having a camera.
Imagine you have a camera at position (Cx, Cy, Cz), and it has x/y rotation angles (CRx, CRy). If this were just a regular object, and not the camera, you would transform this by:
glTranslate(Cx, Cy, Cz);
glRotate(CRx, 1, 0, 0);
glRotate(CRy, 0, 1, 0);
But because this is the camera, we need to do the inverse of this operation instead (we just want to move the world by (-Cx, -Cy, and -Cz) to emulate the moving of a 'camera'. To invert the matrix, you just have to do the opposite of each individual transform, and do them in reverse order.
glRotate(-CRy, 0, 1, 0);
glRotate(-CRx, 1, 0, 0);
glTranslate(-Cx, -Cy, -Cz);
I think this will give you the kind of camera you're mentioning in your image.
I suggest that you bite the apple and implement a camera class that stores the current state of the camera (position, view direction, up vector, right vector) and manipulate that state according to your control scheme. Then you can set up the projection matrix using gluLookAt(). Then, the order of operations becomes unimportant. Here is an example:
Let camPos be the current position of the camera, camView its view direction, camUp the up vector and camRight the right vector.
To translate the camera by moveDelta, simply add moveDelta to camPos. Rotation is a bit more difficult, but if you understand quaternions you'll be able to understand it quickly.
First you need to create a quaternion for each of your two rotations. I assume that your horizontal rotation is always about the positive Z axis (which points at the "ceiling" if you will). Let hQuat be the quaternion representing the horizontal rotation. I further assume that you want to rotate the camera about its right axis for your vertical rotation (creating a pitch effect). For this, you must apply the horizontal rotation to the camera's current angle. The result is the rotation axis for your vertical rotation hQuat. The total rotation quaternion is then rQuat = hQuat * vQuat. Then you apply rQuat to the camera's view direction, up, and right vectors.
Quat hRot(rotX, 0, 0, 1); // creates a quaternion that rotates by angle rotX about the positive Z axis
Vec3f vAxis = hRot * camRight; // applies hRot to the camera's right vector
Quat vRot(rotY, vAxis); // creates a quaternion that rotates by angle rotY about the rotated camera's right vector
Quat rQuat = hRot * vRot; // creates the total rotation
camUp = rQuat * camUp;
camRight = rQuat * camRight;
camView = rQuat * camView;
Hope this helps you solve your problem.
glRotate always works around the origin. If you do:
glPushMatrix();
glTranslated(x,y,z);
glRotated(theta,1,0,0);
glTranslated(-x,-y,-z);
drawObject();
glPopMatrix();
Then the 'object' is rotate around (x,y,z) instead of the origin, because you moved (x,y,z) to the origin, did the rotation, and then pushed (x,y,z) back where it started.
However, I don't think that's going to be enough to get the effect you're describing. If you always want transformations to be done with respect to the current frame of reference, then you need to keep track of the transformation matrix yourself. This why people use Quaternion based cameras.

OpenGL rotation?

I'm writing an simple application based on openGL and qt. It creates primitive (triangle for example) which rotation user can change by using one of three spinboxes
(coordinates: x, y, z). The problem is local coordinate system. It changes and works properly for axes Y and Z but not for X. Local axis X always coincides with global axis X. So when i rotate my figure by 90' around Y then axes X and Z are parallel because local Z changed (as I expected) and local X didn't change. Here is fragment of my paintGL() function:
glRotatef(rot[0], 1.0, 0, 0);
glRotatef(rot[1], 0, 1.0, 0);
glRotatef(rot[2], 0, 0, 1.0);
You should use rotation matrix which lets you specify rotation axis and angles of rotation:
http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
Generally it is not a good idea to perform rotations with euler angles: the next problem you will come around is the 180 deg. flip in certain angle constellations. The problem is broadly known as gimbal lock with euler angles.