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

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;
}
}
// ...
}
}

Related

OpenGL, pitch forces yaw to be 0

So i have been learning opengl and 3d game programming, i started trying to make a camera class and implement movement and viewing with mouse.
Moving works flawlessly but looking around with mouse isn't working as it should.
Looking up without any problems only works when yaw is 0°, then i can look straight up. But when, yaw < 0 or yaw > 0, looking up wont be straight up but more like this ) or ( .
i also noticed that when yaw isn't 0 looking forces yaw to 0 while either looking down or up.
i dont understand why this is happening, why does pitch only work correctly when yaw is 0 and when it is not why does it forced yaw to be 0.
im following a tutorial nothing really seems wrong
processing mouse movement and updating
void Camera::processMouseMovement(GLfloat xOffset, GLfloat yOffset)
{
GLboolean constrainPitch = true;
xOffset *= this->mouseSensitivity;
yOffset *= this->mouseSensitivity;
this->yaw += xOffset;
this->pitch += yOffset;
if(constrainPitch)
{
if(this->pitch > 90.0f)
{
this->pitch = 90.0f;
}
if(this->pitch < -90.0f)
{
this->pitch = -90.0f;
}
}
this->updateCameraVectors();
}
here's is my update function
void Camera::updateCameraVectors()
{
Vector3 front;
Matrix4 mat4;
front.x = cos((DEG2RAD * this->yaw) * cos(DEG2RAD * this->pitch));
front.y = sin(DEG2RAD * this->pitch);
front.z = sin((DEG2RAD * this->yaw) * cos(DEG2RAD * this->pitch));
this->front = mat4.normalize(front);
this->right = mat4.normalize(mat4.cross(this->front, this->worldUp));
this->up = mat4.normalize(mat4.cross(this->right, this->front));
std::cout << "yaw: " << yaw << std::endl;
std::cout << "pitch: " << pitch << std::endl;
}
in main.cpp
Camera camera(Vector3(0.0f, 0.0f, 0.0f));
GLfloat lastX = width / 2.0f;
GLfloat lastY = height / 2.0f;
GLboolean key[1024];
GLboolean firstMouse = true;
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
input with glfw, aslo in main.cpp
glfwSetKeyCallback(window, keyCallBack);
glfwSetCursorPosCallback(window, mouseCallBack);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
in main loop
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glfwPollEvents();
doMovement();
proj = proj.perspective(camera.getZoom(), float(700 / 600), 1.0f, 1000.0f);
glUniformMatrix4fv(uniProj, 1, GL_FALSE, proj.get());
view = camera.getViewMatrix();
mouse callback function
void mouseCallBack(GLFWwindow *window, GLdouble xPos, GLdouble yPos)
{
if(firstMouse)
{
lastX = xPos;
lastY = yPos;
firstMouse = false;
}
GLfloat xOffset = lastX - xPos;
GLfloat yOffset = yPos - lastY;
lastX = xPos;
lastY = yPos;
std::cout << "mouse position x: " << xPos << std::endl;
std::cout << "mouse position y: " << yPos << std::endl;
camera.processMouseMovement(xOffset, yOffset);
}
thank you :)
Assuming front is your average-everyday forward vector, I would calculate it like so:
front.x = cos(DEG2RAD * this->yaw) * sin(DEG2RAD * this->pitch);
front.y = sin(DEG2RAD * this->yaw);
front.z = cos(DEG2RAD * this->yaw) * cos(DEG2RAD * this->pitch);
I'm not keen enough on my trigonometry to understand what your code is doing.
UPDATE: I stared at your code for a while... It looks like the outer set of parenthesis in your code is a mistake. The only other difference between my code and that is my use of sin when calculating forward.x, which would just swap an axis.

How to handle rotations based on a quat camera?

I'm having a problem with getting rotations based on mouse movement.
I use a camera class similar to this:
http://hamelot.io/visualization/moderngl-camera/
Here is the code:
#include "Camera.h"
#include <gl/glew.h>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Mouse.hpp>
Camera::Camera() : _viewportX(0), _viewportY(0), _windowX(1920), _windowY(1080), _lastX(0), _lastY(0), _aspect(1), _nearClip(.001f), _farClip(1000.0f), _cameraHeading(0), _cameraPitch(0), _grab(false)
{
_cameraType = CameraType::FREE;
_cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
_fov = 45.0f;
_cameraPosDelta = glm::vec3(0);
_cameraScale = 0.01f;
_maxPitch = 5;
_maxHeading = 5;
_moveCamera = false;
}
Camera::~Camera()
{
}
void Camera::reset()
{
_cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
}
void Camera::update()
{
_oldDirection = _cameraDirection;
_cameraDirection = glm::normalize(_cameraLookAt - _cameraPos);
// We need to set the matrix state, this is important because lighting won't work otherwise
glViewport(_viewportX, _viewportY, _windowX, _windowY);
if (_cameraType == CameraType::ORTHO)
{
_projection = glm::ortho(-1.5f * float(_aspect), 1.5f * float(_aspect), -1.5f, 1.5f, -10.0f, 10.0f);
}
else
{
_projection = glm::perspective(_fov, _aspect, _nearClip, _farClip);
// Axis for pitch rotation
glm::vec3 axis = glm::cross(_cameraDirection, _cameraUp);
// Compute quaternion for pitch based on the camera pitch angle
glm::quat pitchQuat = glm::angleAxis(_cameraPitch, axis);
// Determine heading quaternion from the camera up vector and the heading angle
glm::quat headingQuat = glm::angleAxis(_cameraHeading, _cameraUp);
// Add the two quats
glm::quat tempQuat = glm::cross(pitchQuat, headingQuat);
tempQuat = glm::normalize(tempQuat);
// Update the direction from the quaternion
_cameraDirection = glm::rotate(tempQuat, _cameraDirection);
// add the camera delta
_cameraPos += _cameraPosDelta;
// set the lookat matrix to be infront of the camera
_cameraLookAt = _cameraPos + _cameraDirection * 1.0f;
// Damping for smooth camera
_cameraHeading *= 0.5f;
_cameraPitch *= 0.5f;
_cameraPosDelta *= 0.8f;
}
// compute the mvp
_view = glm::lookAt(_cameraPos, _cameraLookAt, _cameraUp);
}
void Camera::moveKeyboard()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
processMovement(UP);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
processMovement(DOWN);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
processMovement(LEFT);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
processMovement(RIGHT);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
processMovement(FORWARD);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
processMovement(BACK);
}
void Camera::changePitch(float degrees)
{
//Check bounds with the max pitch rate so that we aren't moving too fast
if (degrees < -_maxPitch)
{
degrees = -_maxPitch;
}
else if (degrees > _maxPitch)
{
degrees = _maxPitch;
}
_cameraPitch += degrees;
// Check bounds for cameraPitch
if (_cameraPitch > 360.0f)
{
_cameraPitch -= 360.0f;
}
else if (_cameraPitch < -360.0f)
{
_cameraPitch += 360.0f;
}
}
void Camera::changeHeading(float degrees)
{
//Check bounds with the max Heading rate so that we aren't moving too fast
if (degrees < -_maxHeading)
{
degrees = -_maxHeading;
}
else if (degrees > _maxHeading)
{
degrees = _maxHeading;
}
_cameraHeading += degrees;
// This controls how the heading is changed if the camera is pointed straight up or down
// The heading delta direction changes
if (_cameraPitch > 90 && _cameraPitch < 270 || (_cameraPitch < -90 && _cameraPitch > -270))
{
_cameraHeading -= degrees;
}
else
{
_cameraHeading += degrees;
}
// Check bounds for cameraHeading
if (_cameraHeading > 360.0f)
{
_cameraHeading -= 360.0f;
}
else if (_cameraHeading < -360.0f)
{
_cameraHeading += 360.0f;
}
}
void Camera::processMouseMovement(sf::RenderWindow& window)
{
auto mousePos = sf::Mouse::getPosition(window);
if (_lastX == 0 && _lastY == 0)
{
_lastX = _windowX / 2;
_lastY = _windowY / 2;
}
if (mousePos != sf::Vector2i(_lastX, _lastY))
{
GLfloat xOffset = (_windowX / 2) - mousePos.x;
GLfloat yOffset = (_windowY / 2) - mousePos.y;
xOffset *= _cameraScale;
yOffset *= _cameraScale;
if (_moveCamera)
{
changeHeading(.08f * xOffset);
changePitch(.08f * yOffset);
}
}
sf::Mouse::setPosition(sf::Vector2i(_windowX / 2, _windowY / 2), window);
}
void Camera::setMode(CameraType type)
{
_cameraType = type;
_cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
}
void Camera::setPosition(glm::vec3 pos)
{
_cameraPos = pos;
}
void Camera::setLookAt(glm::vec3 pos)
{
_cameraLookAt = pos;
}
void Camera::setFOV(double fov)
{
_fov = fov;
}
void Camera::setViewport(int locX, int locY, int width, int height)
{
_viewportX = locX;
_viewportY = locY;
_windowX = width;
_windowY = height;
_aspect = static_cast<double>(_windowX) / static_cast<double>(_windowY);
}
void Camera::setClipping(double nearClipDistance, double farClipDistance)
{
_nearClip = nearClipDistance;
_farClip = farClipDistance;
}
void Camera::processMouseButtons()
{
if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))
{
_moveCamera = true;
}
else
{
_moveCamera = false;
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Right))
{
_grab = true;
}
else
{
_grab = false;
}
}
CameraType Camera::getMode()
{
return _cameraType;
}
void Camera::getViewPort(int& locX, int& locY, int& width, int& height)
{
locX = _viewportX;
locY = _viewportY;
width = _windowX;
height = _windowY;
}
void Camera::getMatrices(glm::mat4& view, glm::mat4& projection)
{
projection = _projection;
view = _view;
}
void Camera::processMovement(CameraDirection direction)
{
if (_cameraType == FREE)
{
switch (direction)
{
case UP:
_cameraPosDelta -= _cameraUp * _cameraScale;
break;
case DOWN:
_cameraPosDelta += _cameraUp * _cameraScale;
break;
case LEFT:
_cameraPosDelta -= glm::cross(_cameraDirection, _cameraUp) * _cameraScale;
break;
case RIGHT:
_cameraPosDelta += glm::cross(_cameraDirection, _cameraUp) * _cameraScale;
break;
case FORWARD:
_cameraPosDelta += _cameraDirection * _cameraScale;
break;
case BACK:
_cameraPosDelta -= _cameraDirection * _cameraScale;
break;
case DEFAULT:
break;
}
}
}
I'm trying to make a model rotate based on the camera direction (or lookAt). I've done the basics like:
float xRot = glm::dot(_oldDirection.x, _cameraDirection.x);
xRot = acos(xRot);
This gives me the angle of the two vectors, and i plug that into my model rotation:
model[1][1] *= cos(xRot * (PI / 180));
model[2][1] *= -sin(xRot * (PI / 180));
model[1][2] *= sin(xRot * (PI / 180));
model[2][2] *= cos(xRot * (PI / 180));
The problems i'm facing are:
The model rotates about 7x the amount of a my movements. If i move the mouse 180 degrees, the model will rotate 7 times that amount.
The model locks up if i rotate too much in one direction, to fix it i have to rotate the other direction.
A unrelated problem with the camera class : I'm getting a form of gimbal lock. If i move the mouse very fast downward ( i mean i have to thrust it as hard as i can downward) The screen will turn grey and the screen/camera will lock up. The same happens when i thrust the mouse upwards, I can't I think the sides don't gimbal lock.
If you could link me to any resources or help me out, that would be great, thanks!
You seem to be calculating a quaternion from heading/pitch/roll angles, which is the wrong order. To use a quaternion for smooth camera movement, the mouse/keyboard/whatever controls continually update a quaternion. When you draw, convert this quaternion into a matrix and use that for the camera orientation. (Quat -> rotation matrix should be part of your 3D maths library.)
Search "Ken Shoemake quaternion" for explanations and sample code.
Hope this helps.
I think what you might find useful is an alternative to glm::lookAt:
void Camera::lookAt(Quaternion rot, Vertex Pos)
{
view = Matrix<4>();
Vertex v1 = (rot*Vertex(0.0f,0.0f,1.0f)).normalize(); // forward
Vertex v2 = (rot*Vertex(0.0f,1.0f,0.0f)).normalize().cross(v1); // up
Vertex v3 = v1.cross(v2);
view[0][0] = v2.x;
view[0][1] = v3.x;
view[0][2] = v1.x;
view[1][0] = v2.y;
view[1][1] = v3.y;
view[1][2] = v1.y;
view[2][0] = v2.z;
view[2][1] = v3.z;
view[2][2] = v1.z;
view[3][0] = -v2.dot(Pos);
view[3][1] = -v3.dot(Pos);
view[3][2] = -v1.dot(Pos);
//Comment this out if you aren't using the left hand coordinate system
view = view.transpose();
}
Massive thanks to joe stevens for mentioning this method: http://joestevens.net/post/20063172257/alternate-view-matrix-construction
I rewrote this for my own purposes, but check out the original by any means.
You can then just feed your camera the Quaternion and its Center position and it will churn out a nice view matrix.
This means that you can simply add your yaw,pitch,roll to the quaternion each time that you rotate, or even better use the quaternion as your source of rotations.
I've been looking for a good and simple way to implement an exclusively quaternion based camera. Surprisingly I couldn't find any good implementation in glm so here it goes.
principles.
- accumulate the frame pitch yaw and roll separately.
- calculate the yaw(x) in world space and the pitch(x) and roll(z) in local(default) space.
void Render::Node::setRotationByXYZ(float inX, float inY, float inZ) {
x += inX/100;
y += inY/100;
z += inZ/100;
glm::quat qX = normalize(glm::quat(cos(x/2), sin(x/2), 0, 0));
glm::quat qY = normalize(glm::quat(cos(y/2), 0, sin(y/2), 0));
glm::quat qZ = normalize(glm::quat(cos(z/2), 0, 0, sin(z/2)));
glm::quat qXWorld = glm::inverse(glm::quat(1.0,0.0,0.0,0.0))*qX;
glm::quat q = qXWorld*qZ*qY;
this->rotation = q;
}
So normally you would convert to matrix and transform by identity matrix, but using the quat identity value works just the same.
If you don't need roll, just omit qZ. This works for me but your multiplication order may differ of course.
This is by far the simplest approach I could come up with, I hope it helps someone else.

openGL (GLFW) - camera movement and view direction vector

I'm making a game in openGL using c++ and GLFW libraries.
I made my camera movement function working, here is the code
void Player::mouse_callback(double xpos, double ypos) {
static float lastX = 0.0;
static float lastY = 0.0;
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
GLfloat sensitivity = 0.1;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw -= xoffset;
pitch -= yoffset;
if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
this->viewDirection = glm::normalize(front);
cout << "ViewDirection: " << viewDirection.x << " " << viewDirection.y << " " << viewDirection.z << endl;
}
(there is also some glrotate in another class)
glRotatef(this->pov->pitch, 1, 0, 0);
glRotatef(this->pov->yaw, 0, -1, 0);
Now I think that viewDirection is a vector that indicates the direction I'm going.
I tried now to place a marker next to me that stays always in front of me while I am moving the camera, but its positioning is not correct. This is the part of the code from the draw function of the marker class
this->setPosition(this->scene->getPov()->getPosition() + this->scene->getPov()->getViewDirection() * 4.0f );
(the 4x is to place it at some distance from me)
Do you have a clue of how can I make the mark always to stay in front of me?

OpenGL Camera rotation weirdness

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

Creating a First Person camera in Open GL 2.0 and glm

I am farly new to Open GL, and c++ and have come across the issue of creating a first person camera. I don't understand matrix math so that makes it all the more harder for me. So far, for calculating the rotation of the camera I have gone for this:
void CameraFP::calculate_view() {
m_view = glm::rotate(m_view, this->get_rotation_x(), glm::vec3(1, 0, 0));
m_view = glm::rotate(m_view, this->get_rotation_y(), glm::vec3(0, 1, 0));
}
That function is called every update call.
For handling the rotation of the camera via the mous, I've done the following:
void CameraFP::process_mouse(sf::Window *app) {
GLfloat mouse_x = app->GetInput().GetMouseX();
GLfloat mouse_y = app->GetInput().GetMouseY();
GLfloat mouse_x_delta = std::max(mouse_x, old_mouse_x) - std::min(mouse_x, old_mouse_x);
GLfloat mouse_y_delta = std::max(mouse_y, old_mouse_y) - std::min(mouse_y, old_mouse_y);
y_rot += mouse_x_delta / (float)app->GetWidth();
x_rot += mouse_y_delta / (float)app->GetHeight();
this->old_mouse_x = mouse_x;
this->old_mouse_y = mouse_y;
app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);
}
and for handling the movement I've done the following:
void CameraFP::process_keyboard(sf::Window *app) {
const sf::Input *input = &app->GetInput();
if (input->IsKeyDown(sf::Key::W)) {
position.z += 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::S)) {
position.z -= 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::A)) {
position.x -= 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::D)) {
position.x += 1.0f / 100.0f;
}
}
My issues lie in the fact that my camera does not move in the direction you face, and that it never stops rotating :/. Also, if you could point me to a guide or something on Matrix math that would be awesome :)
Edit 2:'
I just started a new process mouse function, and it a movement along the x axis of the screen as a y rotation for the camera correctly. However, it doesn't matter if I move the mouse left or right, both movements rotate me right. Same with the x axis in 3d space, but this happens downwards. Any ideas on what is causing this?
void CameraFP::process_mouse(sf::Window *app) {
GLfloat mouse_x = app->GetInput().GetMouseX();
GLfloat mouse_y = app->GetInput().GetMouseY();
GLfloat mouse_x_delta = old_mouse_x - mouse_x;
GLfloat mouse_y_delta = old_mouse_y - mouse_y;
if (mouse_x_delta > 0) {
y_rot += mouse_x_delta / (float)app->GetWidth() * 0.1f;
} else if (mouse_x_delta < 0) {
y_rot -= mouse_x_delta / (float)app->GetWidth() * 0.1f;
}
if (mouse_y_delta > 0) {
x_rot += mouse_y_delta / (float)app->GetWidth() * 0.1f;
} else if (mouse_y_delta < 0) {
x_rot -= mouse_y_delta / (float)app->GetWidth() * 0.1f;
}
if (mouse_x != old_mouse_x) {
m_view = glm::rotate(m_view, y_rot, glm::vec3(0, 1, 0));
}
if (mouse_y != old_mouse_y) {
m_view = glm::rotate(m_view, x_rot, glm::vec3(1, 0, 0));
}
this->old_mouse_x = mouse_x;
this->old_mouse_y = mouse_y;
app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);
}
I don't have time to look for my camera code at the moment, but what I can tell you right now is, that your movement calculation is simply wrong. You aren't taking the camera rotation into account.
You have to do something like this:
if (input->IsKeyDown(sf::Key::W)) {
position.z += sin(camera_rot_y);
}
else if (input->IsKeyDown(sf::Key::S)) {
position.z -= sin(camera_rot_y);
}
if (input->IsKeyDown(sf::Key::A)) {
position.x -= cos(camera_rot_y);
}
else if (input->IsKeyDown(sf::Key::D)) {
position.x += cos(camera_rot_y);
}
Also you can use "else if"s, because you cannot move forward and backward at the same time.^^
Also, why are you using std::min() and max() in the following code?:
GLfloat mouse_x_delta = std::max(mouse_x, old_mouse_x) - std::min(mouse_x, old_mouse_x);
GLfloat mouse_y_delta = std::max(mouse_y, old_mouse_y) - std::min(mouse_y, old_mouse_y);
If you want to rotate in the opposite direction you need to get a negative delta, which is impossible with your code. Get rid of the min() and max() functions.
BTW: My camera code is not the most efficient (uses rotate instead of direct calculations), but it works.
You didn't show usage of position in CameraFP and m_view but I think problem si here:
void CameraFP::process_keyboard(sf::Window *app) {
const sf::Input *input = &app->GetInput();
if (input->IsKeyDown(sf::Key::W)) {
position.z += 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::S)) {
position.z -= 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::A)) {
position.x -= 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::D)) {
position.x += 1.0f / 100.0f;
}
}
You must multiply matrix, with this translation, and m_view. In matrix multiplication is important order of matrices. First must be m_view because it is main matrix, and you want move forward from m_view rotation. But there is glm namespace and there is method glm::translate which them multiply in right order and you just create glm::vec3 as parameter of that method (translation with x, y, z).
(If you use relative position ... only difference between frames)
So you must change it like this:
void CameraFP::process_keyboard(sf::Window *app) {
const sf::Input *input = &app->GetInput();
glm::vec3 pos;
if (input->IsKeyDown(sf::Key::W)) {
pos.z += 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::S)) {
pos.z -= 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::A)) {
pos.x -= 1.0f / 100.0f;
}
if (input->IsKeyDown(sf::Key::D)) {
pos.x += 1.0f / 100.0f;
}
//Make translation
m_view = glm::translate(m_view, pos);
//Values at position 12, 13, 14 are translation x, y, z
//Save absolute position of camera
position = glm::vec3(m_view[12], m_view[13], m_view[14]);
}
PS: Sorry for my poor english.