OpenGL Camera rotation weirdness - c++

I've been working on a small project with FreeGlut and Glew. Now I'm coding a camera system, but there are some things that are simply weird:
In fullscreen mode if the mouse moves in lower area of the screen, camera movements are faster than if camera moves in upper areas.
The camera makes weird movement, always in same direction, a small 8 figure move move.
code:
void MouseOps(int x, int y)
{
// Changes in mousepositions. Always same direction and
// in lower right corner of monitor faster, for some reason.
deltaX = x - MousePreviousX;
deltaY = y - MousePreviousY;
// Also I didn't bother to put * 360 in next equations,
// because it would make the camera jump for crazy.
// resx and resy are screen resolutions.
// Endresult should be that camera can
// rotate once when mouse moves over screen
yaw = yaw + (((deltaX / resx)) * deginrad);
pitch = pitch + (((deltaY / resy)) * deginrad);
//Check clippings (eg. camera wont end upside down etc.)
if(yaw >= (2 * pi) || yaw <= (-2 * pi) )
yaw = 0;
if(pitch >= (pi / 2))
pitch = pi / 2;
if(pitch <= (pi / -2))
pitch = pi / -2;
//Calculate x, y, and z coordinates of unit sphere to look at (r = 1)
cam_normX = cos(yaw) * sin(pitch);
cam_normY = sin(yaw) * sin(pitch);
cam_normZ = cos(yaw);
// Current x and y to previous
int MousePreviousX = x;
int MousePreviousY = y;
}
I tried to use this
http://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
system to calculate the point to look at. Then I passed all "cam_norm" variables to
gluLookAt(cam_posX, cam_posY, cam_posZ,
cam_posX+cam_normX, cam_posY+cam_normY, cam_posZ + cam_normZ,
cam_upX, cam_upY, cam_upZ);

I don't know why this works but it fixed all problems:
bool isCursorWarping = false;
void MouseOps(int x, int y)
{
if(isCursorWarping == false){
// Changes in mousepositions. Always same direction and in lower right corner of monitor faster, for some reason.
deltaX = x - MousePreviousX;
deltaY = y - MousePreviousY;
yaw = yaw + ((((deltaX / resx)) * deginrad) * 360);
pitch = pitch + ((((deltaY / resy)) * deginrad) * 360);
//Check clippings (eg. camera wont end upside down etc.)
if(x >= resx - 1 || y >= resy - 1 || x == 0 || y == 0)
{
warpCursor();
MousePreviousX = resx / 2;
MousePreviousY = resy / 2;
}else{
MousePreviousX = x;
MousePreviousY = y;
}
if(yaw >= (2 * pi) || yaw <= (-2 * pi) )
yaw = 0;
if(pitch >= (pi / 2))
pitch = pi / 2;
if(pitch <= (pi / -2))
pitch = pi / -2;
//Calculate x, y, and z coordinates of unit sphere to look at (r = 1)
cam_normX = cos(pitch) * cos(yaw);
cam_normY = sin(pitch) * sin(yaw);
cam_normZ = cos(pitch) * sin(yaw);
}
// Current x and y to previous and cleanup
isCursorWarping = false;
}
void warpCursor()
{
isCursorWarping = true;
glutWarpPointer(resx / 2, resy / 2);
}
Then I pass the "cam_norm" values to:
gluLookAt(0.0f, 1.0f, 2.0f, 0.0f + cam_normX, 1.0f + cam_normY, 2.0f+ cam_normZ, 0.0f, 0.1f, 0.0f);

Related

How can I adjust my mouse coord to isometric tile coordinate to compensate for diamond shaped tiles?

My drawing function looks like this:
for(std::size_t y = 0; y < mapSizeY; ++y)
{
for(std::size_t x = mapSizeX-1; x != static_cast<std::size_t>(-1); --x)
{
auto tile = GetTile(x,y);
int64_t xPos = (x * TILE_WIDTH / 2) + (y * TILE_WIDTH / 2);
int64_t yPos = (y * TILE_HEIGHT / 2) - (x * TILE_HEIGHT / 2);
xPos += camera.GetXOffset();
yPos += camera.GetYOffset();
yPos -= tile->z * TILE_HEIGHT/2; // everything is at height 1 for now
auto zoom = camera.GetZoomFactor();
xPos *= zoom;
yPos *= zoom;
if(xPos < 0-TILE_WIDTH*zoom || yPos < 0-TILE_HEIGHT*zoom)
continue;
if(xPos > GetScreenDimensions().x || yPos > GetScreenDimensions().y)
continue;
// x is up right
// y is down right
Blit(m_grass,{xPos,yPos,TILE_WIDTH*zoom,TILE_HEIGHT*zoom+TILE_HEIGHT/2*zoom});
}
}
When a user clicks the mouse, I'm trying to get the x,y coord of the tile clicked on. To translate the mouse coords to the map coords I'm doing the following:
// x and y are the mouse coords
auto zoom = camera.GetZoomFactor();
auto xOffset = camera.GetXOffset();
auto yOffset = camera.GetYOffset();
int z = 1; // every tile is at height 1 for now
int32_t xTile = -(TILE_HEIGHT * (2 * xOffset * zoom + TILE_WIDTH * z * zoom - 2 * x) + 2 * TILE_WIDTH * (y - yOffset * zoom)) / (2 * TILE_HEIGHT * TILE_WIDTH * zoom);
int32_t yTile = (-2 * TILE_HEIGHT * xOffset * zoom + TILE_HEIGHT * TILE_WIDTH * z * zoom + 2 * TILE_HEIGHT * x - 2 * yOffset * TILE_WIDTH * zoom + 2 * TILE_WIDTH * y) / (2 * TILE_HEIGHT * TILE_WIDTH * zoom);
I came up with the above by solving my draw positions for x,y
The results I get appear to be on the correct tile. But the issue is that if I click past the "halfway" point of a tile, in either x or y coordinate, it attributes it to the next tile (because they're squares with transparent corners to make the "diamond" shape).
How can I adjust the above to compensate?

How to make 3d camera infinitely rotate around with SDL2 mouse motion?

I am trying to implement 3D camera mouse rotation. Right now what I can do is only rotate if my mouse is in the bound of the window. What it means is if my window is 1280 width I cannot rotate more if mouse coordinates rich 0 or 1279. I've read that relative mouse should help. It does not work for me though.
before the main loop
SDL_ShowCursor(SDL_DISABLE);
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_WarpMouseInWindow(window, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
The function that rotates xyz
glm::vec3 MouseMotion(float x, float y)
{
float lastX = SCREEN_WIDTH / 2, lastY = SCREEN_HEIGHT / 2;
float pitch = 0.0f, yaw = -90.0f;
float offsetX = x - lastX;
float offsetY = lastY - y;
lastX = x;
lastY = y;
float sensitivity = 0.3f;
yaw += offsetX * sensitivity;
pitch += offsetY * sensitivity;
if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
glm::vec3 front;
front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
front.y = std::sin(glm::radians(pitch));
front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
//std::cout << "Mouse: " << offsetX << ", " << offsetY << std::endl;
//std::cout << "YAW AND PITCH: " << yaw << ", " << pitch << std::endl;
return glm::normalize(front);
}
and the event loop
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
exit(0);
}
if (event.type == SDL_MOUSEMOTION)
{
float mouseX = (float)event.motion.x;
float mouseY = (float)event.motion.y;
cameraFront = MouseMotion(mouseX, mouseY);
}
}
it is working, but only if my mouse is in the bound.
Changing to event.motion.xrel and event.motion.yrel do not fix this. It makes it more bugged because my camera shakes, i.e. going a little bit right and get back to center or left and back to center.
How to make my camera move fully, infinitely move around?
Following on from the comments, here is a list of problems:
You need to call SDL_WarpMouseInWindow every time after an SDL_MOUSEMOTION event to reset the mouse, or it will quickly hit the "bound" you were talking about.
pitch and yaw need to be higher in scope than the game loop, or they will be reset every time and you won't be able to achieve "infinite rotation".
sensitivity is far too high. A typical mouse movement of ~500 units translates to a ~180 degree turning.
Here is an attempt at fixing the above issues, with some code refactoring:
glm::vec3 polarVector(float p, float y)
{
// this form is already normalized
return glm::vec3(std::cos(y) * std::cos(p),
std::sin(p),
std::sin(y) * std::cos(p));
}
// clamp pitch to [-89, 89]
float clampPitch(float p)
{
return p > 89.0f ? 89.0f : (p < -89.0f ? -89.0f : p);
}
// clamp yaw to [-180, 180] to reduce floating point inaccuracy
float clampYaw(float y)
{
float temp = (y + 180.0f) / 360.0f;
return y - ((int)temp - (temp < 0.0f ? 1 : 0)) * 360.0f;
}
int main(int argc, char*args[])
{
// ...
// experiment with this
const float sensitivity = 0.001f;
#define CTR_X (SCREEN_WIDTH / 2)
#define CTR_Y (SCREEN_HEIGHT / 2)
#define RESET_MOUSE SDL_WarpMouseInWindow(window, CTR_X, CTR_Y)
// call once at the start
RESET_MOUSE;
// keep outside the loop
float pitch = 0.0f, yaw = 0.0f;
while (!quit)
{
// ...
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
exit(0);
}
if (event.type == SDL_MOUSEMOTION)
{
float deltaX = (float)event.motion.x - CTR_X;
float deltaY = (float)event.motion.y - CTR_Y;
yaw = clampYaw(yaw + sensitivity * deltaX);
pitch = clampPitch(pitch - sensitivity * deltaY);
// assumes radians input
cameraFront = polarVector(glm::radians(pitch), glm::radians(yaw));
// reset *every time*
RESET_MOUSE;
}
}
// ...
}
}

Terrain Collision issues

I'm trying to implement terrain collision for my height map terrain, and I'm following this. The tutorial is for java but I'm using C++, though the principles are the same so it shouldn't be a problem.
To start off we need a function to get the height of the terrain based on the camera's position. WorldX and WorldZ is the camera's position (x, z) and heights is an 2D-array containing all the heights of the vertices.
float HeightMap::getHeightOfTerrain(float worldX, float worldZ, float heights[][256])
{
//Image is (256 x 256)
float gridLength = 256;
float terrainLength = 256;
float terrainX = worldX;
float terrainZ = worldZ;
float gridSquareLength = terrainLength / ((float)gridLength - 1);
int gridX = (int)std::floor(terrainX / gridSquareLength);
int gridZ = (int)std::floor(terrainZ / gridSquareLength);
//Check if position is on the terrain
if (gridX >= gridLength - 1 || gridZ >= gridLength - 1 || gridX < 0 || gridZ < 0)
{
return 0;
}
//Find out where the player is on the grid square
float xCoord = std::fmod(terrainX, gridSquareLength) / gridSquareLength;
float zCoord = std::fmod(terrainZ, gridSquareLength) / gridSquareLength;
float answer = 0.0;
//Top triangle of a square else the bottom
if (xCoord <= (1 - zCoord))
{
answer = barryCentric(glm::vec3(0, heights[gridX][gridZ], 0),
glm::vec3(1, heights[gridX + 1][gridZ], 0), glm::vec3(0, heights[gridX][gridZ + 1], 1),
glm::vec2(xCoord, zCoord));
}
else
{
answer = barryCentric(glm::vec3(1, heights[gridX + 1][gridZ], 0),
glm::vec3(1, heights[gridX + 1][gridZ + 1], 1), glm::vec3(0, heights[gridX][gridZ + 1], 1),
glm::vec2(xCoord, zCoord));
}
return answer;
}
To find the height of the triangle the camera is currently standing on we use the baryCentric interpolation function.
float HeightMap::barryCentric(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, glm::vec2 pos)
{
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
Then we just have to use the height we have calculated to check for
collision during the game
float terrainHeight = heightMap.getHeightOfTerrain(camera.Position.x, camera.Position.z, heights);
if (camera.Position.y < terrainHeight)
{
camera.Position.y = terrainHeight;
};
Now according to the tutorial this should work perfectly fine, but the height is rather off and at some places it doesn't even work. I figured it might have something to do with the translation and scaling part of the terrain
glm::mat4 model;
model = glm::translate(model, glm::vec3(0.0f, -0.3f, -15.0f));
model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));
and that I should multiply the values of the heights array by 0.1, as the scaling does that part for the terrain on the GPU side, but that didn't do the trick.
Note
In the tutorial the first lines in the getHeightOfTerrain function says
float terrainX = worldX - x;
float terrainZ = worldZ - z;
where x and z is the world position of the terrain. This is done to get the player position relative to the terrain's position. I tried with the values from the translation part, but it doensn't work either. I changed these lines because it doesn't seem necessary.
float terrainX = worldX - x;
float terrainZ = worldZ - z;
Those lines are, in fact, very necessary, unless your terrain is always at the origin.
Your code resource (tutorial) assumes that you haven't scaled or rotated the terrain in any way. The x and z variables are the XZ position of the terrain which take care of cases where the terrain is translated.
Ideally, you should transform the world position vector from world space to object space (using the inverse of the model matrix you use for the terrain), something like
vec3 localPosition = inverse(model) * vec4(worldPosition, 1)
And then use localPosition.x and localPosition.z instead of terrainX and terrainZ.

OpenGL - Frustum not culling polygons beyond far plane

I have implemented frustum culling and am checking the bounding box for its intersection with the frustum planes. I added the ability to pause frustum updates which lets me see if the frustum culling has been working correctly. When I turn around after I have paused it, nothing renders behind me and to the left and right side, they taper off as well just as you would expect. Beyond the clip distance (far plane), they still render and I am not sure whether it is a problem with my frustum updating or bounding box checking code or I am using the wrong matrix or what. As I put the distance in the projection matrix at 3000.0f, it still says that bounding boxes well past that are still in the frustum, which isn't the case.
Here is where I create my modelview matrix:
projectionMatrix = glm::perspective(newFOV, 4.0f / 3.0f, 0.1f, 3000.0f);
viewMatrix = glm::mat4(1.0);
viewMatrix = glm::scale(viewMatrix, glm::vec3(1.0, 1.0, -1.0));
viewMatrix = glm::rotate(viewMatrix, anglePitch, glm::vec3(1.0, 0.0, 0.0));
viewMatrix = glm::rotate(viewMatrix, angleYaw, glm::vec3(0.0, 1.0, 0.0));
viewMatrix = glm::translate(viewMatrix, glm::vec3(-x, -y, -z));
modelViewProjectiomMatrix = projectionMatrix * viewMatrix;
The reason I scale it by -1 in the Z direction is because the levels were designed to be rendered with DirectX so I reverse the Z direction.
Here is where I update my frustum:
void CFrustum::calculateFrustum()
{
glm::mat4 mat = camera.getModelViewProjectionMatrix();
// Calculate the LEFT side
m_Frustum[LEFT][A] = (mat[0][3]) + (mat[0][0]);
m_Frustum[LEFT][B] = (mat[1][3]) + (mat[1][0]);
m_Frustum[LEFT][C] = (mat[2][3]) + (mat[2][0]);
m_Frustum[LEFT][D] = (mat[3][3]) + (mat[3][0]);
// Calculate the RIGHT side
m_Frustum[RIGHT][A] = (mat[0][3]) - (mat[0][0]);
m_Frustum[RIGHT][B] = (mat[1][3]) - (mat[1][0]);
m_Frustum[RIGHT][C] = (mat[2][3]) - (mat[2][0]);
m_Frustum[RIGHT][D] = (mat[3][3]) - (mat[3][0]);
// Calculate the TOP side
m_Frustum[TOP][A] = (mat[0][3]) - (mat[0][1]);
m_Frustum[TOP][B] = (mat[1][3]) - (mat[1][1]);
m_Frustum[TOP][C] = (mat[2][3]) - (mat[2][1]);
m_Frustum[TOP][D] = (mat[3][3]) - (mat[3][1]);
// Calculate the BOTTOM side
m_Frustum[BOTTOM][A] = (mat[0][3]) + (mat[0][1]);
m_Frustum[BOTTOM][B] = (mat[1][3]) + (mat[1][1]);
m_Frustum[BOTTOM][C] = (mat[2][3]) + (mat[2][1]);
m_Frustum[BOTTOM][D] = (mat[3][3]) + (mat[3][1]);
// Calculate the FRONT side
m_Frustum[FRONT][A] = (mat[0][3]) + (mat[0][2]);
m_Frustum[FRONT][B] = (mat[1][3]) + (mat[1][2]);
m_Frustum[FRONT][C] = (mat[2][3]) + (mat[2][2]);
m_Frustum[FRONT][D] = (mat[3][3]) + (mat[3][2]);
// Calculate the BACK side
m_Frustum[BACK][A] = (mat[0][3]) - (mat[0][2]);
m_Frustum[BACK][B] = (mat[1][3]) - (mat[1][2]);
m_Frustum[BACK][C] = (mat[2][3]) - (mat[2][2]);
m_Frustum[BACK][D] = (mat[3][3]) - (mat[3][2]);
// Normalize all the sides
NormalizePlane(m_Frustum, LEFT);
NormalizePlane(m_Frustum, RIGHT);
NormalizePlane(m_Frustum, TOP);
NormalizePlane(m_Frustum, BOTTOM);
NormalizePlane(m_Frustum, FRONT);
NormalizePlane(m_Frustum, BACK);
}
And finally, where I check the bounding box:
bool CFrustum::BoxInFrustum( float x, float y, float z, float x2, float y2, float z2)
{
// Go through all of the corners of the box and check then again each plane
// in the frustum. If all of them are behind one of the planes, then it most
// like is not in the frustum.
for(int i = 0; i < 6; i++ )
{
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
// If we get here, it isn't in the frustum
return false;
}
// Return a true for the box being inside of the frustum
return true;
}
I've noticed a few things, particularly with how you set up the projection matrix. For starters, gluProject doesn't return a value, unless you're using some kind of wrapper or weird api. gluLookAt is used more often.
Next, assuming the scale, rotate, and translate functions are intended to change the modelview matrix, you need to reverse their order. OpenGL doesn't actually move objects around; instead it effectively moves the origin around, and renders each object using the new definition of <0,0,0>. Thus you 'move' to where you want it to render, then you rotate the axes as needed, then you stretch out the grid.
As for the clipping problem, you may want to give glClipPlane() a good look over. If everything else mostly works, but there seems to be some rounding error, try changing the near clipping plane in your perspective(,,,) function from 0.1 to 1.0 (smaller values tend to mess with the z-buffer).
I see a lot of unfamiliar syntax, so I think you're using some kind of wrapper; but here are some (Qt) code fragments from my own GL project that I use. Might help, dunno:
//This gets called during resize, as well as once during initialization
void GLWidget::resizeGL(int width, int height) {
int side = qMin(width, height);
padX = (width-side)/2.0;
padY = (height-side)/2.0;
glViewport(padX, padY, side, side);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, 1.0, 1.0, 2400.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//This fragment gets called at the top of every paint event:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLightfv(GL_LIGHT0, GL_POSITION, FV0001);
camMain.stepVars();
gluLookAt(camMain.Pos[0],camMain.Pos[1],camMain.Pos[2],
camMain.Aim[0],camMain.Aim[1],camMain.Aim[2],
0.0,1.0,0.0);
glPolygonMode(GL_FRONT_AND_BACK, drawMode);
//And this fragment represents a typical draw event
void GLWidget::drawFleet(tFleet* tIn) {
if (tIn->firstShip != 0){
glPushMatrix();
glTranslatef(tIn->Pos[0], tIn->Pos[1], tIn->Pos[2]);
glRotatef(tIn->Yaw, 0.0, 1.0, 0.0);
glRotatef(tIn->Pitch,0,0,1);
drawShip(tIn->firstShip);
glPopMatrix();
}
}
I'm working on the assumption that you're newish to GL, so my apologies if I come off as a little pedantic.
I had the same problem.
Given Vinny Rose's answer, I checked the function that creates a normalized plane, and found an error.
This is the corrected version, with the incorrect calculation commented out:
plane plane_normalized(float A, float B, float C, float D) {
// Wrong, this is not a 4D vector
// float nf = 1.0f / sqrtf(A * A + B * B + C * C + D * D);
// Correct
float nf = 1.0f / sqrtf(A * A + B * B + C * C);
return (plane) {{
nf * A,
nf * B,
nf * C,
nf * D
}};
}
My guess is that your NormalizePlane function does something similar.
The point of normalizing is to have a plane in Hessian normal form so that we can do easy half-space tests. If you normalize the plane as you would a four-dimensional vector, the normal direction [A, B, C] is still correct but the offset D is not.
I think you'd get correct results when testing points against the top, bottom, left and right planes because they pass through the origin, and the near plane might be close enough to not notice. (Bounding sphere tests would fail.)
The frustum cull worked as expected for me when I restored the correct normalization.
Here's what I think is happening: The far plane is getting defined correctly but in my testing the D value of that plane is coming out much too small. So objects are getting accepted as being on the correct side of the far plane because the math is forcing the far plane to actually be much farther away than you want.
Try a different approach: (http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-extracting-the-planes/)
float tang = tanf(fov * PI / 360.0f);
float nh = near * tang; // near height
float nw = nh * aspect; // near width
float fh = far * tang; // far height
float fw = fh * aspect; // far width
glm::vec3 p,nc,fc,X,Y,Z,Xnw,Ynh;
//camera position
p = glm::vec3(viewMatrix[3][0],viewMatrix[3][1],viewMatrix[3][2]);
// the left vector
glm::vec3 X = glm::vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
// the up vector
glm::vec3 Y = glm::vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
// the look vector
glm::vec3 Z = glm::vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]);
nc = p - Z * near; // center of the near plane
fc = p - Z * far; // center of the far plane
// the distance to get to the left or right edge of the near plane from nc
Xnw = X * nw;
// the distance to get to top or bottom of the near plane from nc
Ynh = Y * nh;
// the distance to get to the left or right edge of the far plane from fc
Xfw = X * fw;
// the distance to get to top or bottom of the far plane from fc
Yfh = Y * fh;
ntl = nc + Ynh - Xnw; // "near top left"
ntr = nc + Ynh + Xnw; // "near top right" and so on
nbl = nc - Ynh - Xnw;
nbr = nc - Ynh + Xnw;
ftl = fc + Yfh - Xfw;
ftr = fc + Yfh + Xfw;
fbl = fc - Yfh - Xfw;
fbr = fc - Yfh + Xfw;
m_Frustum[TOP] = planeWithPoints(ntr,ntl,ftl);
m_Frustum[BOTTOM] = planeWithPoints(nbl,nbr,fbr);
m_Frustum[LEFT] = planeWithPoints(ntl,nbl,fbl);
m_Frustum[RIGHT] = planeWithPoints(nbr,ntr,fbr);
m_Frustum[FRONT] = planeWithPoints(ntl,ntr,nbr);
m_Frustum[BACK] = planeWithPoints(ftr,ftl,fbl);
// Normalize all the sides
NormalizePlane(m_Frustum, LEFT);
NormalizePlane(m_Frustum, RIGHT);
NormalizePlane(m_Frustum, TOP);
NormalizePlane(m_Frustum, BOTTOM);
NormalizePlane(m_Frustum, FRONT);
NormalizePlane(m_Frustum, BACK);
Then planeWithPoints would be something like:
planeWithPoints(glm::vec3 a, glm::vec3 b, glm::vec3 c){
double A = a.y * (b.z - c.z) + b.y * (c.z - a.z) + c.y * (a.z - b.z);
double B = a.z * (b.x - c.x) + b.z * (c.x - a.x) + c.z * (a.x - b.x);
double C = a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y);
double D = -(a.x * (b.y * c.z - c.y * b.z) + b.x * (c.y * a.z - a.y * c.z) + c.x * (a.y * b.z - b.y * a.z));
return glm::vec4(A,B,C,D);
}
I didn't test any of the above. But the original reference is there if you need it.
Previous Answer:
OpenGL and GLSL matrices are stored and accessed in column-major order when the matrix is represented by a 2D array. This is also true with GLM as they follow the GLSL standards.
You need to change your frustum creation to the following.
// Calculate the LEFT side (column1 + column4)
m_Frustum[LEFT][A] = (mat[3][0]) + (mat[0][0]);
m_Frustum[LEFT][B] = (mat[3][1]) + (mat[0][1]);
m_Frustum[LEFT][C] = (mat[3][2]) + (mat[0][2]);
m_Frustum[LEFT][D] = (mat[3][3]) + (mat[0][3]);
// Calculate the RIGHT side (-column1 + column4)
m_Frustum[RIGHT][A] = (mat[3][0]) - (mat[0][0]);
m_Frustum[RIGHT][B] = (mat[3][1]) - (mat[0][1]);
m_Frustum[RIGHT][C] = (mat[3][2]) - (mat[0][2]);
m_Frustum[RIGHT][D] = (mat[3][3]) - (mat[0][3]);
// Calculate the TOP side (-column2 + column4)
m_Frustum[TOP][A] = (mat[3][0]) - (mat[1][0]);
m_Frustum[TOP][B] = (mat[3][1]) - (mat[1][1]);
m_Frustum[TOP][C] = (mat[3][2]) - (mat[1][2]);
m_Frustum[TOP][D] = (mat[3][3]) - (mat[1][3]);
// Calculate the BOTTOM side (column2 + column4)
m_Frustum[BOTTOM][A] = (mat[3][0]) + (mat[1][0]);
m_Frustum[BOTTOM][B] = (mat[3][1]) + (mat[1][1]);
m_Frustum[BOTTOM][C] = (mat[3][2]) + (mat[1][2]);
m_Frustum[BOTTOM][D] = (mat[3][3]) + (mat[1][3]);
// Calculate the FRONT side (column3 + column4)
m_Frustum[FRONT][A] = (mat[3][0]) + (mat[2][0]);
m_Frustum[FRONT][B] = (mat[3][1]) + (mat[2][1]);
m_Frustum[FRONT][C] = (mat[3][2]) + (mat[2][2]);
m_Frustum[FRONT][D] = (mat[3][3]) + (mat[2][3]);
// Calculate the BACK side (-column3 + column4)
m_Frustum[BACK][A] = (mat[3][0]) - (mat[2][0]);
m_Frustum[BACK][B] = (mat[3][1]) - (mat[2][1]);
m_Frustum[BACK][C] = (mat[3][2]) - (mat[2][2]);
m_Frustum[BACK][D] = (mat[3][3]) - (mat[2][3]);

OpenGL camera is laggy

I was setting up a camera following this tutorial. My problem is that when I move it isn't fluid, it kinda jumps. I'm calculating the MVP whenever the mouse moves using this code:
void motion(int x, int y) {
static bool wrap = false;
if(!wrap) {
int ww = glutGet(GLUT_WINDOW_WIDTH);
int wh = glutGet(GLUT_WINDOW_HEIGHT);
int dx = x - ww / 2;
int dy = y - wh / 2;
const float mousespeed = 0.001;
angles.x += dx * mousespeed;
angles.y += dy * mousespeed;
if(angles.x < -M_PI)
angles.x += M_PI * 2;
else if(angles.x > M_PI)
angles.x -= M_PI * 2;
if(angles.y < -M_PI / 2)
angles.y = -M_PI / 2;
if(angles.y > M_PI / 2)
angles.y = M_PI / 2;
lookat.x = sinf(angles.x) * cosf(angles.y);
lookat.y = sinf(angles.y);
lookat.z = cosf(angles.x) * cosf(angles.y);
view = glm::lookAt(position, position + lookat, glm::vec3(0, 1, 0));
// move mouse pointer back to the center of the window
wrap = true;
glutWarpPointer(ww / 2, wh / 2);
} else {
wrap = false;
}
}
And then I'm updating the attribute on my 'OnIdele()' function:
void onIdle() {
glUseProgram(program);
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
glm::mat4 Model = glm::mat4(1.0f);
glm::mat4 MVP = Projection * view * Model;
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(MVP));
glutPostRedisplay();
}
My question is, is this the right way to implement this? Is there any way to avoid the laggyness?
Also if you don't mind me asking, how exactly does this code work? I know it limits where you can look but I can't seem to make sense of it:
if(angles.x < -M_PI)
angles.x += M_PI * 2;
else if(angles.x > M_PI)
angles.x -= M_PI * 2;
if(angles.y < -M_PI / 2)
angles.y = -M_PI / 2;
if(angles.y > M_PI / 2)
angles.y = M_PI / 2;
See if increasing mousespeed makes a difference. After you find the distance the mouse has moved, stored in dx and dy, you scale the distance by mousespeed before adding it to the camera's angles. The lower the value of mousespeed the less your mouse movement will affect the angles of your camera, and vice versa.
Also the code you asked about is limiting your camera angles to between 0 and PI * 2, or 0 and 360 degrees.