How to translate, rotate and scale at a particular point? - c++

I want to translate, rotate and scale, all of these transformations from a specified point(origin). This is what I use right now(I tried many combinations but still cannot figure it out! Looks like it's back to learning Linear Algebra for me).
I am using GLM.
Take a look at the code which I am using:-
GLfloat rx = sprite->origin.x / width;
GLfloat ry = sprite->origin.y / height;
translate(model, vec3( -sprite->origin.x, -sprite->origin.y, 0.0f ));
translate(model, vec3( rx * scale[0], ry * scale[1], 0.0f ));
rotate(model, radians(rotation), vec3( 0.0f, 0.0f, 1.0f ));
translate(model, vec3( -(rx * scale[0]), -(ry * scale[1]), 0.0f ));
scale(model, vec3( width * scale[0], height * scale[1], 1.0f ));
translate(model, vec3( position[0], position[1], 0.0f ));
Origin is the starting point of the object, then translate it to the center for rotation and then translate back.
Scale it and translate the object to the specified position.
I mean, what is wrong with this code?

I found the answer,
Do it in this order:-
Translate the object to the position, e.g, vec3(position, 0.0f).
And then rotate the object.
Translate the point back to the origin, e.g, vec3(-originx * scale, -originy * scale, 0.0f).
Finally, scale the object.
Explanation:
Take the position of the object as 0, 0 in 2D coordinates(take it as the variable position mentioned above in the example of the first point).
You can see that the object will translate(move) to the position.
Note: Don't forget to multiply it with your scale or it can yield wrong results if your scale is more than 1.
Now is the time you rotate the object.
Scaling works from the top-left corner so you can't do it from the center.
Next, just scale the object.
That's all.

Related

Direct2D Draw Ellipse - Randomly Shifts at 0/360 degrees

I'm having some trouble with drawing a circle with direct2D. I have a potentiometer device that I rotate in a circle, and as I do the value of Ch1_Data_ThreadShared (which is a float variable) changes. 0 degrees and 360 degrees correspond to a value of .5 and 4.5 in the Ch1_Data_ThreadShared variable. I convert those values to radians and then since I was having issues I constrained it so the value would always be between 0 and 2pi. Then I create my x and y coordinates so the ellipse object moves in a circle on the screen.
// Create x and y coordinates to rotate in a circle as Ch1_Data_ThreadShared value changes
float Ch1_Data_ThreadSharedConvertedToRadians = (Ch0_Data_ThreadShared-.5) * .5 * pi;
if (Ch1_Data_ThreadSharedConvertedToRadians > (2 * pi))
Ch1_Data_ThreadSharedConvertedToRadians = (2 * pi);
if (Ch1_Data_ThreadSharedConvertedToRadians < 0)
Ch1_Data_ThreadSharedConvertedToRadians = 0;
float Ch1_Data_ThreadSharedConvertedToRadiansXValue = 200 * (cos(Ch1_Data_ThreadSharedConvertedToRadians));
float Ch1_Data_ThreadSharedConvertedToRadiansYValue = 200 * (sin(Ch1_Data_ThreadSharedConvertedToRadians));
And then this is where I later in the code draw the circle. When I run it the ellipse object moves in a perfect smooth circle on the screen as expected, except for when it shifts from 0 radians to 2pi radians (or the other way around). At that point, the ellipse object shifts randomly to another part of the screen for a brief moment and then continues along it's expected path.
Is this a weird quirk with drawing in direct2D? Or is there something in my math that's missing?
// Draw Ellipse
graphics->DrawEllipse(400.0f + Ch1_Data_ThreadShared*100, 400.0f, // position
50.0f, 50.0f, // radius
1.0f, 0.2f, 0.2f, 0.2f, // fill color
1.0f, 0.9f, 0.9f, 0.9f, // line color
0.0f, // line width
45.0f, // object rotation (degrees)
1.0f, 1.0f); // object scaling in the x and y directions
graphics->SetTransformToIdentity(); // if apply transformation then need to reset this otherwise it will carry to next object

How to rotate muliple objects around the same pivot/point in 3D?

Here is the rotation code when initialising the model matrix:
_model = translate(_position) *
( rotate(_rotation.data[0], 1.0f, 0.0f, 0.0f) *
rotate(_rotation.data[1], 0.0f, 1.0f, 0.0f) *
rotate(_rotation.data[2], 0.0f, 0.0f, 1.0f)) *
scale(_scale);
Basically, I have got a 3D level and I want to rotate the level and all the objects in it around the same pivot point.
How could I do this?
This is typically done by the concatenation (i.e. multiplication) of three matrices:
T: Translate the desired pivot to the origin (0, 0, 0).
R: Apply the rotation.
Tinv: Translate back.
Because of the way OpenGL matrices are structured, the right order is Tinv * R * T. Premultiply your view matrix by that.

glm lookAt FPS camera flips vertical mouse look when Z < 0

I am trying to implement a FPS camera using C++, OpenGL and GLM.
What I did until now:
I have a cameraPosition vector for the camera position, and also
cameraForward (pointing to where the camera looks at), cameraRight and cameraUp, which are calculated like this:
inline void controlCamera(GLFWwindow* currentWindow, const float& mouseSpeed, const float& deltaTime)
{
double mousePositionX, mousePositionY;
glfwGetCursorPos(currentWindow, &mousePositionX, &mousePositionY);
int windowWidth, windowHeight;
glfwGetWindowSize(currentWindow, &windowWidth, &windowHeight);
m_cameraYaw += (windowWidth / 2 - mousePositionX) * mouseSpeed;
m_cameraPitch += (windowHeight / 2 - mousePositionY) * mouseSpeed;
lockCamera();
glfwSetCursorPos(currentWindow, windowWidth / 2, windowHeight / 2);
// Rotate the forward vector horizontally. (the first argument is the default forward vector)
m_cameraForward = rotate(vec3(0.0f, 0.0f, -1.0f), m_cameraYaw, vec3(0.0f, 1.0f, 0.0f));
// Rotate the forward vector vertically.
m_cameraForward = rotate(m_cameraForward, -m_cameraPitch, vec3(1.0f, 0.0f, 0.0f));
// Calculate the right vector. First argument is the default right vector.
m_cameraRight = rotate(vec3(1.0, 0.0, 0.0), m_cameraYaw, vec3(0.0f, 1.0f, 0.0f));
// Calculate the up vector.
m_cameraUp = cross(m_cameraRight, m_cameraForward);
}
Then I "look at" like this:
lookAt(m_cameraPosition, m_cameraPosition + m_cameraForward, m_cameraUp)
The problem: I seem to be missing something, because my FPS camera works as it is supposed to be until I move forward and get behind Z(0.0) (z becomes negative).. then my vertical mouse look flips and when I try to look up my application looks down...
The same question was asked here: glm::lookAt vertical camera flips when z <= 0 , but I didn't understand what the issue is and how to solve it.
EDIT: The problem is definitely in the forward, up and right vectors. When I calculate them like this:
m_cameraForward = vec3(
cos(m_cameraPitch) * sin(m_cameraYaw),
sin(m_cameraPitch),
cos(m_cameraPitch) * cos(m_cameraYaw)
);
m_cameraRight = vec3(
sin(m_cameraYaw - 3.14f/2.0f),
0,
cos(m_cameraYaw - 3.14f/2.0f)
);
m_cameraUp = glm::cross(m_cameraRight, m_cameraForward);
Then the problem goes away, but then m_cameraPitch and m_cameraYaw don't match... I mean if m_cameraYaw is 250 and I make a 180 flip m_cameraYaw is 265... I can't restrict leaning backwards for example like that? Any ideas?

OpenGL perspective projection to map the screen pixels

I am trying to set up perspective projection in OpenGL such that it maps exactly to the screen pixels, like we can do in orthographic projections. I can do this for specific screen sizes, for example 640x960, but cannot do it for any screen size. Below is the code I have used to set it up for 640x960 size
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
Matrix4f perspective( Matrix4f::perspective( 60.0f, ( size.width / size.height ), 0.5f, 1500.f ) );
Matrix4f lookAt( Matrix4f::lookAt( size.width / 2.0f, size.height / 2.0f, size.height / 1.1566f, size.width / 2.0f, size.height / 2.0f, 0.0f, 0.0f, 1.0f, 0.0f ) );
Matrix4f m( ( perspective * lookAt ).translate( -( size.width / 2.0f ), -( size.height / 2.0f ), -( size.height/ 1.1566f ) ) );
glLoadMatrixf( m.get() );
This works for 640x960 resolution, but does not work for any other like 1024x768. In other resolutions it does not exactly map to the screen pixels.
I want to set up perspective projection because I want to provide an ability to rotate the sprites along x and y axes too.
I am trying to set up perspective projection in OpenGL such that it maps exactly to the screen pixels
By its very nature this kind of mapping will hold for only one certain depth. For all others it simply can't map view X,Y to viewport X,Y
I want to set up perspective projection because I want to provide an ability to rotate the sprites along x and y axes too.
Nothing stops you from doing that with an ortho projection.
Seems like the problem was with the multiplication order. The above code now works fine with any resolution.

How to recalculate axis-aligned bounding box after translate/rotate

When I first load my object I calculate the initial AABB with the maximum and minimum (x,y,z) points. But this is in object space and the object moves around the world and more importantly, rotates.
How do I recalculate the new AABB every time the object is translated/rotated? This happens basically in every frame. Is it going to be a very intensive operation to recalculate the new AABB every frame? If so, what would be the alternative?
I know AABBs will make my collision detection less accurate, but it's easier to implement the collision detection code than OBBs and I want to take this one step at a time.
Here's my current code after some insight from the answers below:
typedef struct sAxisAlignedBoundingBox {
Vector3D bounds[8];
Vector3D max, min;
} AxisAlignedBoundingBox;
void drawAxisAlignedBoundingBox(AxisAlignedBoundingBox box) {
glPushAttrib(GL_LIGHTING_BIT | GL_POLYGON_BIT);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_LINE_LOOP);
glVertex3f(box.bounds[0].x, box.bounds[0].y, box.bounds[0].z);
glVertex3f(box.bounds[1].x, box.bounds[1].y, box.bounds[1].z);
glVertex3f(box.bounds[2].x, box.bounds[2].y, box.bounds[2].z);
glVertex3f(box.bounds[3].x, box.bounds[3].y, box.bounds[3].z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(box.bounds[4].x, box.bounds[4].y, box.bounds[4].z);
glVertex3f(box.bounds[5].x, box.bounds[5].y, box.bounds[5].z);
glVertex3f(box.bounds[6].x, box.bounds[6].y, box.bounds[6].z);
glVertex3f(box.bounds[7].x, box.bounds[7].y, box.bounds[7].z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(box.bounds[0].x, box.bounds[0].y, box.bounds[0].z);
glVertex3f(box.bounds[5].x, box.bounds[5].y, box.bounds[5].z);
glVertex3f(box.bounds[6].x, box.bounds[6].y, box.bounds[6].z);
glVertex3f(box.bounds[1].x, box.bounds[1].y, box.bounds[1].z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(box.bounds[4].x, box.bounds[4].y, box.bounds[4].z);
glVertex3f(box.bounds[7].x, box.bounds[7].y, box.bounds[7].z);
glVertex3f(box.bounds[2].x, box.bounds[2].y, box.bounds[2].z);
glVertex3f(box.bounds[3].x, box.bounds[3].y, box.bounds[3].z);
glEnd();
glPopAttrib();
}
void calculateAxisAlignedBoundingBox(GLMmodel *model, float matrix[16]) {
AxisAlignedBoundingBox box;
float dimensions[3];
// This will give me the absolute dimensions of the object
glmDimensions(model, dimensions);
// This calculates the max and min points in object space
box.max.x = dimensions[0] / 2.0f, box.min.x = -1.0f * box.max.x;
box.max.y = dimensions[1] / 2.0f, box.min.y = -1.0f * box.max.y;
box.max.z = dimensions[2] / 2.0f, box.min.z = -1.0f * box.max.z;
// These calculations are probably the culprit but I don't know what I'm doing wrong
box.max.x = matrix[0] * box.max.x + matrix[4] * box.max.y + matrix[8] * box.max.z + matrix[12];
box.max.y = matrix[1] * box.max.x + matrix[5] * box.max.y + matrix[9] * box.max.z + matrix[13];
box.max.z = matrix[2] * box.max.x + matrix[6] * box.max.y + matrix[10] * box.max.z + matrix[14];
box.min.x = matrix[0] * box.min.x + matrix[4] * box.min.y + matrix[8] * box.min.z + matrix[12];
box.min.y = matrix[1] * box.min.x + matrix[5] * box.min.y + matrix[9] * box.min.z + matrix[13];
box.min.z = matrix[2] * box.min.x + matrix[6] * box.min.y + matrix[10] * box.min.z + matrix[14];
/* NOTE: If I remove the above calculations and do something like this:
box.max = box.max + objPlayer.position;
box.min = box.min + objPlayer.position;
The bounding box will move correctly when I move the player, the same does not
happen with the calculations above. It makes sense and it's very simple to move
the box like this. The only problem is when I rotate the player, the box should
be adapted and increased/decreased in size to properly fit the object as a AABB.
*/
box.bounds[0] = Vector3D(box.max.x, box.max.y, box.min.z);
box.bounds[1] = Vector3D(box.min.x, box.max.y, box.min.z);
box.bounds[2] = Vector3D(box.min.x, box.min.y, box.min.z);
box.bounds[3] = Vector3D(box.max.x, box.min.y, box.min.z);
box.bounds[4] = Vector3D(box.max.x, box.min.y, box.max.z);
box.bounds[5] = Vector3D(box.max.x, box.max.y, box.max.z);
box.bounds[6] = Vector3D(box.min.x, box.max.y, box.max.z);
box.bounds[7] = Vector3D(box.min.x, box.min.y, box.max.z);
// This draw call is for testing porpuses only
drawAxisAlignedBoundingBox(box);
}
void drawObjectPlayer(void) {
static float mvMatrix[16];
if(SceneCamera.GetActiveCameraMode() == CAMERA_MODE_THIRD_PERSON) {
objPlayer.position = SceneCamera.GetPlayerPosition();
objPlayer.rotation = SceneCamera.GetRotationAngles();
objPlayer.position.y += -PLAYER_EYE_HEIGHT + 0.875f;
/* Only one of the two code blocks below should be active at the same time
Neither of them is working as expected. The bounding box doesn't is all
messed up with either code. */
// Attempt #1
glPushMatrix();
glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
glCallList(gameDisplayLists.player);
glGetFloatv(GL_MODELVIEW_MATRIX, mvMatrix);
glPopMatrix();
// Attempt #2
glPushMatrix();
glLoadIdentity();
glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, mvMatrix);
glPopMatrix();
calculateAxisAlignedBoundingBox(objPlayer.model, mvMatrix);
}
}
But it doesn't work as it should... What I'm doing wrong?
Simply recompute the AABB of the transformed AABB. This means transforming 8 vertices (8 vertex - matrix multiplications) and 8 vertex-vertex comparisons.
So at initialisation, you compute your AABB in model space: for each x,y,z of each vertex of the model, you check against xmin, xmax, ymin, ymax, etc.
For each frame, you generate a new transformation matrix. In OpenGL this is done with glLoadIdentity followed by glTransform/Rotate/Scale (if using the old API). This is the Model Matrix, as lmmilewski said.
You compute this transformation matrix a second time (outside OpenGL, for instance using glm). You also can get OpenGL's resulting matrix using glGet.
You multiply each of your AABB's eight vertices by this matrix. Use glm for matrix-vector multiplication. You'll get your transformed AABB (in world space). It it most probably rotated (not axis-aligned anymore).
Now your algorithm probably only work with axis-aligned stuff, hence your question. So now you approximate the new bounding box of the transformed model by takinf the bounding box of the transformed bounding box:
For each x,y,z of each vertex of the new AABB, you check against xmin, xmax, ymin, ymax, etc. This gives you an world-space AABB that you can use in your clipping algorithm.
This is not optimal (AABB-wise). You'll get lots of empty space, but performance-wise, it's much much better that recomputing the AABB of the whole mesh.
As for the transformation matrix, in drawObjectPlayer:
gLLoadIdentity();
glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, mvMatrix);
// Now you've got your OWN Model Matrix (don't trust the GL_MODELVIEW_MATRIX flag : this is a workaround, and I know what I'm doing ^^ )
gLLoadIdentity(); // Reset the matrix so that you won't make the transformations twice
gluLookAt( whatever you wrote here earlier )
glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
// Now OpenGL is happy, he's got his MODELVIEW matrix correct ( gluLookAt is the VIEW part; Translate/Rotate is the MODEL part
glCallList(gameDisplayLists.player); // Transformed correcty
I can't explain it further than that... as said in the comments, you had to do it twice. You wouldn't have these problems and ugly workarounds in OpenGL 3, btw, because you'd be fully responsible of your own matrices. Equivalent in OpenGL 2:
glm::mat4 ViewMatrix = glm::LookAt(...);
glm::mat4 ModelMatrix = glm::rotate() * glm::translate(...);
// Use ModelMatrix for whatever you want
glm::mat4 ModelViewMatrix = ViewMatrix * ModelMatrix;
glLoadMatrix4fv( &ModelViewMatrix[0][0] ); // In OpenGL 3 you would use an uniform instead
Much cleaner, right?
Yep, you can transform the eight corner vertices and do min/max on the results, but there is a faster way, as described by Jim Arvo from his chapter in Graphics Gems (1990).
Performance-wise, Arvo's method is roughly equivalent to two transforms instead of eight and basically goes as follows (this transforms box A into box B)
// Split the transform into a translation vector (T) and a 3x3 rotation (M).
B = zero-volume AABB at T
for each element (i,j) of M:
a = M[i][j] * A.min[j]
b = M[i][j] * A.max[j]
B.min[i] += a < b ? a : b
B.max[i] += a < b ? b : a
return B
One variation of Arvo's method uses center / extent representation rather than mix / max, which is described by Christer Ericson in Real-Time Collision Detection (photo).
Complete C code for Graphics Gems article can be found here.
To do that you have to loop over every vertex, calculate its position in the world (multiply by modelview) and find the minimum / maximum vertex coordinates within every object (just like when you compute it for the first time).
You can scale your AABB a bit, so that you don't have to recalculate it - it is enough to enlarge it by factor sqrt(2) - your rotated object then always fits in AABB.
There is also a question in which direction you rotate(?). If always in one then you can enlarge AABB only in that direction.
Optionally, you can use bounding spheres instead of AABBs. Then you don't care about rotation and scaling is not a problem.
To quote a previous response on AABB # Stack Overflow:
"Sadly yes, if your character rotates you need to recalculate your AABB . . .
Skurmedel
The respondent's suggestion, and mine, is to implement oriented bounding boxes once you have AABB working, and also to note you can make aabb's of portions of a mesh to fudge collision detection with greater accuracy than one enormous box for each object.
Why not use your GPU? Today I implimented a solution of this problem by rendening a couple of frames.
Temporary place your camera over the object, above it, pointing
down at the object.
Render only your object, with out lights or
anything.
Use orthographic projection too.
Then read the frame buffer. Rows and columns of black pixels means the model isn't there. Hit a white pixel - you hit one of the model AABB borders.
I know this isn't a solution for all the cases, but with some prior knowledge, this is very efficient.
For rendering off screen see here.