Apply multiple transformations to view - c++

I am working on a OpenGL ES application using C++. I run the code on iOS with a few wrapper Obj-c classes that enable me to use gestures (like pan, pinch, rotation).
I use orthographic projection. The code only draws a simple quad for now, and I want to be able to apply any transformations to it using user gesture. That means move it, zoom in and out, rotate (some other derived gestures, like zoom using double-tap etc.).
I thought it will be fine if I simply store origin, scale and angle floats and construct Matrix using each of these, then multiply. That works for simple translation and scale, however, I can't rotate around any point. Now I can't figure out what do I need to be able to rotate around some point, translate, rotate some more around different point.
Here is example how I initialize my matrix
Mat4 Mat4::translation(float tx, float ty)
{
float a[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1 };
return Mat4(a);
}
Mat4 Mat4::rotation(float angle)
{
float a[16] = { cosf(angle), sinf(angle), 0, 0, -sinf(angle), cosf(angle), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
return Mat4(a);
}
How can I apply any number of transformations to 2D view in OpenGL(ES), using matrices? These transformations are caused by gestures (pan, pinch, rotate). For pinch and rotation, I know amount and center point in screen coordinates.

I believe your problem boils down to finding out with respect to which reference frame you define your transformations.
In practice, you can specify any trasformation (i.e. translation and rotation in 3D) as a 4x4 matrix, like you've done in your example. If they are defined with respect to the current pose of the object, then you can apply a new one by multiplying the new matrix on the right. If they are specified with respect to a fixed reference frame (e.g. your screen origin which does not move) then you pre-multiply them.
Say you apply two rotations, defined by T_rot_1 and T_rot_2
If transformations are defined wrt reference frame attached to the object:
T_final_wrt_fixed = T_rot_1_wrt_obj * T_rot_2_wrt_obj
If transformations are defined wrt reference to fixed frame:
T_final_wrt_fixed = T_rot_2_wrt_fixed * T_rot_1_wrt_fixed
For more info, check out these slides.

Rotation around arbitrary point is done by translation of the point to origin, rotation around origin and then translating back.

Related

Perspective view matrix for Y-down coordinate system

I have this little toy graphics library in rust I tinker with to learn OpenGL. I initially intended it to do only low-res 2D stuff on it, so I set up the coordinate system for pixel-space orthographic rendering, a system with (0, 0) at the top-left corner of the screen that allows sprites to be "snapped" to pixels for an 8/16-bit style.
Now I'm thinking it would be fun to add a perspective camera to render the sprites in perspective as a sort of paper-doll theater thing, sort of inverse 2.5D, world is 3D but characters are 2D.
So I use the excellent tutorial here:
https://learnopengl.com/Getting-started/Camera
Great stuff, but the algorithm calls for deriving from the up-vector [0, 1, 0], so my world renders upside down (since my "up" is [0, -1, 0]). If I do set the "up" as [0, -1, 0] I instead get the X-axis extending from right to left, as if negating "up" rotates 180° around the Z-axis, which again isn't what I want.
Is it at all possible to do a perspective camera view matrix with a typical orthographic world Y-down coordinate system, and how would that be done practically using e.g. glm?
The trick with the reflection matrix works; The orientation and position of all objects on screen works out and camera movement produces the expected results.
This page is what tipped me off on the fact that the only difference between left and right-hand view matrixes is the direction of the X-axis, and that mirroring/reflections changes the handedness:
https://www.realtimerendering.com/blog/left-handed-vs-right-handed-viewing/
That should make sense, if you compare your hands they only differ in what side the thumb is on, not in what direction the fingers are pointing.
The solution:
Create a standard right-handed view matrix, but define the up-vector as [0, -1, 0]
Create a reflection matrix that flips the view around the Y-axis:
[-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0
0, 0, 0, 1]
multiply the reflection and view matrix:
reflection_matrix * view_matrix;
This works in OpenGL, but allegedly DirectX works with row-major matrices, so you have to take that into account when multiplying.
This is the code I use to produce the view matrix:
let eye: glm::Vec3 = glm::vec3(160.0, 90.0, 300.0);
let target: glm::Vec3 = glm::vec3(160.0, 90.0, 0.0);
let cam_forward = glm::normalize(&(&eye - &target));
let up = glm::Vec3::new(0.0, -1.0, 0.0);
let cam_right = glm::normalize(&glm::cross(&up, &cam_forward));
let cam_up = glm::normalize(&glm::cross(&cam_forward, &cam_right));
let mut mx_view = glm::look_at(&eye, &target, &cam_up);
let mut mirr: glm::Mat4 = glm::Mat4::identity();
mirr[0] = -1.0;
mx_view = mirr * mx_view;
The camera looks down the Z-axis of my 320 by 180 pixel world.
This is of-course in rust, but, based on the excellent nalgebra-glm crate which pretty closely replicates what glm does for C++, so I think anyone using C++ should also be able to follow.
I do not recommend changing the camera matrix.
Instead, try to use glClipControl(GL_UPPRER_LEFT,GL_ZERO_TO_ONE); in this situation, the front face will be CW.
Or simply multiply the projection matrix P[1][1] by -1.

Rotating object in OpenGL around specific point

I'm modelling Newtons Cradle and I am having trouble with wires/strings rotation as they currently rotate around their centre instead of where they are attached to the frame. There seem to be very little info online apart from " Translate to rotation point, rotate, translate back" which does not seem to work.
std::stack<glm::mat4>model;
model.push(glm::mat4(1.0f));
model.top() = glm::translate(model.top(), glm::vec3(x, y, z));
model.top() = glm::rotate(model.top(), vx, glm::vec3(1, 0, 0)); //rotating in clockwise direction around x-axis
model.top() = glm::rotate(model.top(), vy, glm::vec3(0, 1, 0)); //rotating in clockwise direction around y-axis
model.top() = glm::rotate(model.top(), vz, glm::vec3(0, 0, 1)); //rotating in clockwise direction around z-axis
model.top() = glm::scale(model.top(), glm::vec3(scale/40, scale*5, scale/40));//scale equally in all axis
model.top() = glm::translate(model.top(), glm::vec3(-x, -y, -z));
glUniformMatrix4fv(modelID, 1, GL_FALSE, &model.top()[0][0]);
glm::mat3 normalmatrix = glm::transpose(glm::inverse(glm::mat3(View * model.top())));
glUniformMatrix3fv(normalmatrixID, 1, GL_FALSE, &normalmatrix[0][0])
This is the bit that translates and rotates or its current iteration, stack really does nothing at the moment.
If my problem is unclear imagine analogue clock arm, it will rotate around the clock centre at its end and not by arms itself centre. I know there is no native implementation to rotate object around specific point in OpenGl
As a side note, there is no longer any "native" implementation of any matrix stuff in OpenGL (outside of GLSL).
Your problem here is that you're only setting the top() matrix to a value instead of accumulating the transformations by multiplying them together.
You will probably also later notice a problem where it doesn't move back into the same spot (after making it multiply the matrices together). This is because you're scaling the object down a lot before translating back to the original spot. The order of your transformations matter.
A tutorial like this one that provides an in-depth explanation of all of this stuff will likely be very useful to you:
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/

How do I rotate a 3D object about an axis without changing it's location? (LWJGL)

I'm currently trying to get a composite 3D object that consists of two 3D pyramids to rotate about a given axis by an angle in radians. However, one of the pyramids of the composite object is not staying flush to the other.
In the image below, I am attempting to rotate the left object PI radians about the X axis. However, one of the pyramids doesn't stay in the correct position.
http://i.stack.imgur.com/MekFZ.png
Below is the code I am using to to the scale, rotate, and translate.
float[] model = { xSize, 0, 0, 0,
0, ySize, 0, 0,
0, 0, zSize, 0,
xLoc, yLoc, zLoc, 1 };
FloatBuffer modelBuf = BufferUtils.createFloatBuffer(model.length);
matrix = new Matrix4f();
matrix = matrix.scale(xSize, ySize, zSize);
matrix = matrix.rotateXYZ(dxRot, dyRot, dzRot);
matrix = matrix.setTranslation(xLoc, yLoc, zLoc);
modelBuf = matrix.get(modelBuf);
glUniformMatrix4fv( unif_model, false, modelBuf );
Does anybody know why this is could be happening?
It seems as if you want to group objects. You should use a transform tree for that. I.e. introduce a transformation matrix for the entire group and separate transformation matrices for each object within the group (relative to the group's coordinate system).
In your concrete example, the objects' matrices would consist only of a constant translation and rotation part. They do not change. When rendering the objects, you should set the current transformation matrix as:
transform = groupTransform * objectTransform
If you want to move, rotate, scale an entire group, apply these transformations only to groupTransform.

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).

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.