I'm trying to create a first-person shooter camera for my DirectX game. But I'm struggling in trying to get my view matrix right. At the moment I have only got a triangle to display on the screen, but when I rotate the camera and move forward it seems like the triangle is rotating around the camera instead. Here is the code I have done for the Camera Class
Camera.h
#pragma once
#include"Window.h"
#include"Matrix.h"
#include "Vector3.h"
class Camera
{
public:
Camera();
Camera(float pitch, float yaw, Vector3 position);
~Camera();
void Update();
D3DXMATRIX BuildViewMatrix();
Vector3 GetPosition() const { return position; }
void SetPosition(Vector3 val) { position = val; }
protected:
float yaw;
float pitch;
Vector3 position;
};
Camera.cpp
#include"Camera.h"
Camera::Camera()
{
yaw = 0.0f;
pitch = 0.0f;
}
Camera::Camera(float pitch, float yaw, Vector3 position)
{
this->pitch = pitch;
this->yaw = yaw;
this->position = position;
}
Camera::~Camera()
{
}
void Camera::Update()
{
pitch = min(pitch, 90.0f);
pitch = max(pitch, -90.0f);
if(yaw < 0)
yaw += 360.0f;
if( yaw > 360.0f)
yaw -= 360.0f;
if(Window::GetKeyboard()->KeyDown(KEYBOARD_W))
{
D3DXMATRIX translateMat;
D3DXMatrixTranslation(&translateMat, position.x, position.y, position.z);
D3DXMATRIX rotationMat;
D3DXMatrixRotationY(&rotationMat, D3DXToRadian(yaw));
D3DXMATRIX forwardMat;
D3DXMatrixTranslation(&forwardMat, 0.0f, 0.0f, 10.0f);
D3DXMATRIX transformMat = translateMat * rotationMat * forwardMat;
position.x = transformMat._41;
position.y = transformMat._42;
position.z = transformMat._43;
std::cout << "X: " << transformMat._41 << std::endl;
std::cout << "Y: " << transformMat._42 << std::endl;
std::cout << "Z: " << transformMat._43 << std::endl;
}
else if(Window::GetKeyboard()->KeyDown(KEYBOARD_S))
{
D3DXMATRIX translateMat;
D3DXMatrixTranslation(&translateMat, position.x, position.y, position.z);
D3DXMATRIX rotationMat;
D3DXMatrixRotationY(&rotationMat, D3DXToRadian(yaw));
D3DXMATRIX forwardMat;
D3DXMatrixTranslation(&forwardMat, 0.0f, 0.0f, -10.0f);
D3DXMATRIX transformMat = translateMat * rotationMat * forwardMat;
position.x = transformMat._41;
position.y = transformMat._42;
position.z = transformMat._43;
std::cout << "X: " << transformMat._41 << std::endl;
std::cout << "Y: " << transformMat._42 << std::endl;
std::cout << "Z: " << transformMat._43 << std::endl;
}
if(Window::GetKeyboard()->KeyDown(KEYBOARD_Q))
{
yaw = yaw - 1.0f;
}
if(Window::GetKeyboard()->KeyDown(KEYBOARD_E))
{
yaw = yaw + 1.0f;
}
}
D3DXMATRIX Camera::BuildViewMatrix()
{
D3DXMATRIX translationMat;
D3DXMatrixTranslation(&translationMat, (position.x * -1.0f), (position.y * -1.0f), (position.z * -1.0f));
D3DXMATRIX yawMat;
D3DXMatrixRotationY(&yawMat, D3DXToRadian((yaw * -1.0f)));
D3DXMATRIX pitchMat;
D3DXMatrixRotationX(&pitchMat, D3DXToRadian((pitch * -1.0f)));
D3DXMATRIX viewMat = pitchMat * yawMat * translationMat;
return viewMat;
}
I'm using device->SetTransform(D3DTS_VIEW, &camera->BuildViewMatrix()); to send the view matrix to directX. What exactly am I doing wrong? Please help
UPDATE:
I have change the code and tried to use the D3DXMatrixLookAtLH function (doesn't use the Camera at all), but this time, it doesn't do anything - the triangle stays in the same position no matter how I change the LookAt parameter on the function. Here is the code for it:
void Renderer::Render()
{
device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(40, 40, 40), 1.0f, 0);
device->BeginScene(); //Must be used as it tells DirectX we're starting to draw stuff.
D3DXMATRIX worldMat;
D3DXMatrixTranslation(&worldMat, 0.0f, 0.0f, 0.0f);
device->SetTransform(D3DTS_WORLD, &worldMat);
D3DXMATRIX viewMatrix;
D3DXMatrixLookAtLH( &viewMatrix,
&D3DXVECTOR3(0.0f, -100.0f, 1000.0f), //position
&D3DXVECTOR3(0.0f, 1.0f, 0.0f), //Look at
&D3DXVECTOR3(0.0f, 1.0f, 0.0f));
device->SetTransform(D3DTS_VIEW, &viewMatrix);
//device->SetTransform(D3DTS_VIEW, &camera->BuildViewMatrix());
D3DXMATRIX projMatrix;
D3DXMatrixPerspectiveFovLH( &projMatrix,
D3DXToRadian(45),
(float)width/(float)height,
1.0f,
10000.0f);
device->SetTransform(D3DTS_PROJECTION, &projMatrix);
device->SetFVF(VERTEXFORMAT);
device->SetStreamSource(0, vertexBuffer, 0, sizeof(VERTEX));
device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
device->EndScene(); //Thank you for waiting, I have finished drawing stuff on the screen, please handle the rest Mr DirectX.
device->Present(NULL, NULL, NULL, NULL);
}
Found out the problem, the matrices I've calculated isn't correct as well as the view matrix. the fixed it by adding the following codes:
Camera.h
#pragma once
#pragma comment(lib, "d3dx9.lib")
#include<d3dx9math.h>
#include"Window.h"
class Camera
{
public:
Camera();
Camera(float pitch, float yaw, D3DXVECTOR3 position);
~Camera();
void Update();
D3DXMATRIX BuildViewMatrix();
inline D3DXVECTOR3 GetPosition() const { return position; }
inline void SetPosition(D3DXVECTOR3 position){ this->position = position;}
protected:
float yaw;
float pitch;
float roll;
D3DXVECTOR3 position;
private:
D3DXVECTOR3 xAxis;
D3DXVECTOR3 yAxis;
D3DXVECTOR3 zAxis;
};
Camera.cpp
#include"Camera.h"
Camera::Camera()
{
yaw = 0.0f;
pitch = 0.0f;
roll = 0.0f;
}
//yaw = head twist
//pitch = head tilt
Camera::Camera(float pitch, float yaw, D3DXVECTOR3 position)
{
this->pitch = pitch;
this->yaw = yaw;
roll = 0.0f;
this->position = position;
xAxis = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
yAxis = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
zAxis = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
}
Camera::~Camera()
{
}
void Camera::Update()
{
if(Window::GetKeyboard()->KeyDown(KEYBOARD_Q)){
yaw -= 1.0f;
}
else if(Window::GetKeyboard()->KeyDown(KEYBOARD_E)){
yaw += 1.0f;
}
if(Window::GetKeyboard()->KeyDown(KEYBOARD_Z)){
pitch += 1.0f;
}
else if(Window::GetKeyboard()->KeyDown(KEYBOARD_X)){
pitch -= 1.0f;
}
pitch = min(pitch, 90.0f);
pitch = max(pitch, -90.0f);
if(yaw < 0.0f)
yaw += 360.0f;
if( yaw > 360.0f)
yaw -= 360.0f;
D3DXMATRIX yawMat;
D3DXMatrixRotationAxis(&yawMat, &yAxis, D3DXToRadian(yaw));
D3DXVec3TransformCoord(&zAxis, &zAxis, &yawMat);
D3DXVec3TransformCoord(&xAxis, &xAxis, &yawMat);
D3DXMATRIX pitchMat;
D3DXMatrixRotationAxis(&pitchMat, &xAxis, D3DXToRadian(pitch));
D3DXVec3TransformCoord(&zAxis, &zAxis, &pitchMat);
D3DXVec3TransformCoord(&yAxis, &yAxis, &pitchMat);
D3DXMATRIX rollMat;
D3DXMatrixRotationAxis(&rollMat, &zAxis, D3DXToRadian(0.0f));
D3DXVec3TransformCoord(&xAxis, &xAxis, &rollMat);
D3DXVec3TransformCoord(&yAxis, &yAxis, &rollMat);
if(Window::GetKeyboard()->KeyDown(KEYBOARD_W))
{
position = position + (zAxis * -1) * 10.0f;
}
else if(Window::GetKeyboard()->KeyDown(KEYBOARD_S))
{
position = position + zAxis * 10.0f;
}
if(Window::GetKeyboard()->KeyDown(KEYBOARD_A))
{
position = position + xAxis * 10.0f;
}
else if(Window::GetKeyboard()->KeyDown(KEYBOARD_D))
{
position = position - xAxis * 10.0f;
}
yaw = 0.0f;
pitch = 0.0f;
roll = 0.0f;
}
D3DXMATRIX Camera::BuildViewMatrix()
{
D3DXMATRIX viewMatrix;
D3DXMatrixIdentity(&viewMatrix);
viewMatrix._11 = xAxis.x;
viewMatrix._21 = xAxis.y;
viewMatrix._31 = xAxis.z;
viewMatrix._12 = yAxis.x;
viewMatrix._22 = yAxis.y;
viewMatrix._32 = yAxis.z;
viewMatrix._13 = zAxis.x;
viewMatrix._23 = zAxis.y;
viewMatrix._33 = zAxis.z;
viewMatrix._41 = D3DXVec3Dot(&position, &xAxis);
viewMatrix._42 = D3DXVec3Dot(&position, &yAxis);
viewMatrix._43 = D3DXVec3Dot(&position, &zAxis);
return viewMatrix;
}
Related
I'm currently working on my own small game engine (I am learning OpenGL). I made camera mechanism that can rotate vectors with Euler angles, and now I'm working on rotations with quaternions. Now I'm stuck because my quaternions rotations behave very strangely (flipping objects, not rotating camera as it should). Please, help me find out what is wrong with my algorithm. Could you suggest some fixes to my camera code? Below is my source code, and here is some marks to it: target I want to look at is at coordinates (0.0f, 0.0f, 0.0f). I want my position from which I am look at the target to be glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f)
My camera class:
class Camera {
private:
bool eulerMode = false;
float m_mouseSensitivity;
float m_velocity;
glm::vec3 m_rightAxis{};
glm::vec3 m_upAxis;
glm::vec3 m_position;
glm::vec3 m_target{};
glm::vec3 m_r{};
glm::vec3 m_direction{};
static glm::vec3 rotateVector(float angle, glm::vec3 rotationAxis, glm::vec3 vectorToRotate);
static glm::quat quaternion(float angle, glm::vec3 vec);
public:
static Camera *s_context;
float m_yaw;
float m_pitch;
float m_mouseLastX;
float m_mouseLastY;
bool m_firstMouse;
glm::vec3 m_frontAxis;
Camera(float speed,
int width,
int height,
glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f),
glm::vec3 target = glm::vec3(0.0f, 0.0f, 0.0f)
);
glm::mat4 getLookAtMatrix();
static void quaternionRotate(GLFWwindow *window, double x, double y);
};
Camera::Camera(
float speed,
int width,
int height,
glm::vec3 position,
glm::vec3 up,
glm::vec3 target
)
: m_pitch(0.0f), m_yaw(-90.0f), m_mouseLastX((float) width / 2),
m_mouseLastY((float) height / 2), m_mouseSensitivity(0.1f), m_upAxis(up), m_position(position),
m_frontAxis(glm::vec3(0.0f, 0.0f, -1.0f)), m_firstMouse(true) {
m_velocity = speed;
m_direction = glm::normalize(position - target);
m_rightAxis = glm::normalize(glm::cross(up, m_direction));
m_upAxis = glm::cross(m_direction, m_rightAxis);
s_context = this;
glfwSetWindowUserPointer(g_Window->getOpenGLWindow(), this);
if (eulerMode) {
glfwSetCursorPosCallback(g_Window->getOpenGLWindow(), eulerRotate);
} else {
glfwSetCursorPosCallback(g_Window->getOpenGLWindow(), quaternionRotate);
}
}
glm::mat4 Camera::getLookAtMatrix() {
glm::mat4 view = glm::lookAt(m_position, m_r, m_upAxis);
return view;
}
// for the sake of brevity, I skipped some class methods that are unnecessary for quaternions rotations
void Camera::quaternionRotate(GLFWwindow *window, double x, double y) {
if (s_context->m_firstMouse) {
s_context->m_mouseLastX = (float) x;
s_context->m_mouseLastY = (float) y;
s_context->m_firstMouse = false;
}
auto xoffset = (float) (x - s_context->m_mouseLastX);
auto yoffset = (float) (s_context->m_mouseLastY - y);
s_context->m_mouseLastX = (float) x;
s_context->m_mouseLastY = (float) y;
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
s_context->m_yaw += xoffset;
s_context->m_pitch += yoffset;
glm::vec3 yAxis = glm::vec3(0, 1, 0);
// Rotate the view vector by the horizontal angle around the vertical axis
glm::vec3 view = s_context->m_direction;
view = glm::normalize(rotateVector(s_context->m_yaw, yAxis, view));
// Rotate the view vector by the vertical angle around the horizontal axis
glm::vec3 xAxis = glm::normalize(glm::cross(yAxis, view));
view = glm::normalize(rotateVector(s_context->m_pitch, xAxis, view));
s_context->m_r = view;
s_context->m_upAxis = glm::normalize(glm::cross(s_context->m_r, xAxis));
}
glm::vec3 Camera::rotateVector(float angle, const glm::vec3 rotationAxis, const glm::vec3 vectorToRotate) {
glm::quat rotationQ = quaternion(angle, rotationAxis);
glm::quat conjugateQ = glm::conjugate(rotationQ);
glm::quat result = rotationQ * vectorToRotate * conjugateQ;
return {result.x, result.y, result.z};
}
glm::quat Camera::quaternion(float angle, const glm::vec3 vec) {
float HalfAngleInRadians = glm::radians(angle / 2);
float SineHalfAngle = sinf(HalfAngleInRadians);
float CosHalfAngle = cosf(HalfAngleInRadians);
float xC = vec.x * SineHalfAngle;
float yC = vec.y * SineHalfAngle;
float zC = vec.z * SineHalfAngle;
float wC = CosHalfAngle;
return {wC, xC, yC, zC};
}
I am trying to implement "planet" world. So in any point on it's surface with my old camera class I got result like this :
Camera class update code is:
void cCamera::ProcessMouseMovement(GLdouble dt, glm::vec2 curOffset)
{
curOffset.x *= static_cast<GLfloat>(dt * m_MouseSensitivity);
curOffset.y *= static_cast<GLfloat>(dt * m_MouseSensitivity);
m_fPitch += curOffset.x;
m_fYaw += curOffset.y;
if (m_fYaw > 89.0f)
m_fYaw = 89.0f;
if (m_fYaw < -89.0f)
m_fYaw = -89.0f;
UpdateCameraVectors();
}
void cCamera::UpdateCameraVectors(void)
{
float fYawRad = glm::radians(m_fYaw);
float fPitchRad = glm::radians(m_fPitch);
float fYawCos = cos(fYawRad);
glm::vec3 front { cos(fPitchRad) * fYawCos,
sin(fYawRad),
sin(fPitchRad) * fYawCos };
*m_pCameraFront = glm::normalize(front);
m_Right = glm::normalize(glm::cross(*m_pCameraFront, m_WorldUp));
m_Up = glm::normalize(glm::cross(m_Right, front));
*m_pViewMatrix = glm::lookAt(glm::vec3(0.f), glm::vec3(0.f) + *m_pCameraFront, m_Up);
}
As (0,0,0) is planet center, I suppose I should udate m_WorldUp vector this way:
m_WorldUp = glm::normalize(*m_pPos);
And indeed, the results are good: camera turned the right way, but it's rotation is broken. Yaw and pitch are still dependent on old world up. I think, I should update front vector with new m_WorldUp, but don't really know how.
Solution was found here: https://gamedev.stackexchange.com/questions/73588/how-do-i-fix-my-planet-facing-camera
In my case code is
void cCamera::UpdateCameraVectors(void)
{
m_WorldUp = glm::normalize(*m_pPos);
glm::quat world_axes_rotation = glm::angleAxis(glm::radians(m_fPitch), glm::vec3(0.0f, -1.0f, 0.0f));
world_axes_rotation = glm::normalize(world_axes_rotation);
world_axes_rotation = glm::rotate(world_axes_rotation, glm::radians(m_fYaw), glm::vec3(1.0f, 0.0f, 0.0f));
m_Pole = glm::normalize(m_Pole - glm::dot(m_WorldUp, m_Pole) * m_WorldUp);
glm::mat4 local_transform;
local_transform[0] = glm::vec4(m_Pole, 0.0f);
local_transform[1] = glm::vec4(m_WorldUp, 0.0f);
local_transform[2] = glm::vec4(glm::cross(m_Pole, m_WorldUp), 0.0f);
local_transform[3] = glm::vec4(0.f, 0.f, 0.f, 1.0f);
world_axes_rotation = glm::normalize(world_axes_rotation);
*m_pViewMatrix = local_transform * glm::mat4_cast(world_axes_rotation);
*m_pCameraFront = -1.0f * glm::vec3((*m_pViewMatrix)[2]);
m_Up = glm::vec3((*m_pViewMatrix)[1]);
m_Right = glm::vec3((*m_pViewMatrix)[0]);
*m_pViewMatrix = glm::inverse(*m_pViewMatrix);
}
I'm trying to move camera around the player.
Right now I'm using camera class like this:
Camera::Camera(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch)
{
this->position = position;
this->m_WorldUp = up;
this->up = up;
this->m_Yaw = yaw;
this->m_Pitch = pitch;
this->UpdateCameraVectors();
}
glm::mat4 Camera::getViewMatrix()
{
return glm::lookAt(position, position + m_Front, up);
}
void Camera::ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime)
{
float velocity = moveSpeed * deltaTime;
switch (direction) {
case FORWARD: position += m_Front * velocity; break;
case BACKWARD: position -= m_Front * velocity; break;
case LEFT: position -= m_Right * velocity; break;
case RIGHT: position += m_Right * velocity; break;
case UPWARDS: position += m_WorldUp * velocity; break;
case DOWNWARDS: position -= m_WorldUp * velocity; break;
}
}
void Camera::ProcessMouseMovement(GLfloat xOffset, GLfloat yOffset, GLboolean constrainPitch)
{
xOffset *= sensitivity;
yOffset *= sensitivity;
m_Yaw += xOffset;
m_Pitch += yOffset;
if (constrainPitch) {
if (m_Pitch > 89.0f) {
m_Pitch = 89.0f;
} else if (m_Pitch < -89.0f) {
m_Pitch = -89.0f;
}
}
UpdateCameraVectors();
}
void Camera::UpdateCameraVectors()
{
glm::vec3 front;
front.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
front.y = -sin(glm::radians(m_Pitch));
front.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
m_Front = glm::normalize(front);
m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp));
up = glm::normalize(glm::cross(m_Right, m_Front));
}
It allows me to free look and move around the world.
Player's update method at the moment:
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), 16.0f / 9.0f, 0.1f, 1000.0f);
glm::mat4 view = glm::mat4(1.0f);
view = camera->getViewMatrix();
glm::mat4 model = glm::mat4(1.0f); {
glm::mat4 translate = glm::translate(model, position);
glm::mat4 rotate = glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 scale = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));
model = translate * rotate * scale;
}
glm::mat4 mvp = projection * view * model;
GLint u_mvp = shader.GetUniformLocation("u_mvp");
glUniformMatrix4fv(u_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
I know that I have to change something with view matrix, but I have not got enough knowledge.
How can I upgrade my camera class that it can look, rotate, around a player, like in a circle, an MMO RPG style?
The camera class itself should not be receiving keyboard updates at all - that should be done in the player class. Every time the player moves, update the camera class with its new position. See comments in below code for more details.
Camera::Camera(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch, GLfloat dist)
{
m_WorldUp = up;
// this->up = up; <- delete this variable; lookAt computes it for us
m_Pos = position; // this is the *player* position
m_Yaw = yaw;
m_Pitch = pitch;
m_Dist = dist; // distance from the player
UpdateViewMatrix(true);
}
// private method
void Camera::UpdateViewMatrix(bool computeDir = false)
{
// compute the new direction
if (computeDir)
{
glm::vec3 radial;
radial.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
radial.y = sin(glm::radians(m_Pitch)); // there was a sign error here
radial.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
m_Dir = -radial;
}
glm::vec3 pos = m_Pos - m_Dist * m_Dir; // *camera* position
// additional view matrix member variable
// you were using lookAt in the wrong way
m_View = glm::lookAt(m_Pos, pos, m_WorldUp);
}
// public method - call this everytime the player moves
void Camera::UpdateTargetPosition(glm::vec3 const & pos)
{
m_Pos = pos;
UpdateViewMatrix();
}
void Camera::UpdateAngles(GLfloat yaw, GLfloat pitch, GLboolean constrainPitch)
{
if (constrainPitch) {
if (pitch > 89.0f) {
pitch = 89.0f;
} else if (pitch < -89.0f) {
pitch = -89.0f;
}
}
// if yaw is outside the conventional range (-180.0, 180.0], shift it
if (yaw < -180.0f || yaw > 180.0f) {
yaw -= floor((yaw + 180.0f) / 360.0f) * 360.0f;
}
m_Yaw = yaw;
m_Pitch = pitch;
UpdateViewMatrix(true);
}
void Camera::ProcessMouseMovement(GLfloat xOffset, GLfloat yOffset, GLboolean constrainPitch)
{
UpdateAngles(m_Yaw + xOffset * sensitivity,
m_Pitch + yOffset * sensitivity,
constrainPitch);
}
for mouse follow you need:
camera = inverse(player * camera_view_and_offset)
where player is your player direct matrix, camera_view_and_offset is the view offset and turn around matrix relative to your player coordinate system and camera is your camera inverse matrix you should use as part of modelview ....
for more info see:
Understanding 4x4 homogenous transform matrices
For fun, I've made a 3d camera in opengl. It works well, except for the fact that I cannot figure out how to limit rotation about the x-axis. If you scroll up too much, the up and down controls will invert. I've tried limiting the camFront.y variable to an arbitrary value, but the camera will still flip over the x-axis.
Here is my code:
#ifndef CAMERA_H
#define CAMERA_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#define WORLD_UP glm::vec3(0.0f, 1.0f, 0.0f)
#include <iostream>
enum CamDirection {
CAM_FORWARD,
CAM_BACKWARD,
CAM_LEFT,
CAM_RIGHT
};
class Camera {
public:
void cameraUpdate();
glm::mat4 getViewMatrix();
Camera();
Camera(glm::vec3 startPosition);
void move(CamDirection dir, GLfloat deltaTime);
void look(double xOffset, double yOffset);
void update();
private:
glm::vec3 camPos;
glm::vec3 camFront;
glm::vec3 camUp;
glm::vec3 camRight;
const GLfloat camSpeed = 5.05f;
};
glm::mat4 Camera::getViewMatrix() {
return glm::lookAt(camPos, camPos + camFront, camUp);
}
Camera::Camera():
camPos (glm::vec3(0.0f, 0.0f, 0.0f)),
camFront(glm::vec3(0.0f, 0.0f, -1.0f)),
camUp (WORLD_UP)
{}
Camera::Camera(glm::vec3 startPos):
camPos (startPos),
camFront (glm::vec3(0.0f, 0.0f, -1.0f)),
camUp (WORLD_UP)
{}
void Camera::move(CamDirection dir, GLfloat deltaTime) {
const GLfloat v = camSpeed * deltaTime;
if (dir == CAM_FORWARD)
camPos += v * camFront;
else if (dir == CAM_BACKWARD)
camPos -= v * camFront;
else if (dir == CAM_RIGHT)
camPos += v * camRight;
else
camPos -= v * camRight;
}
void Camera::look(double xOffset, double yOffset) {
glm::quat startQuat = {0, camFront.x, camFront.y, camFront.z};
glm::quat rotation = glm::angleAxis((GLfloat)xOffset, glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat view = startQuat * rotation;
rotation = glm::angleAxis((GLfloat)yOffset, glm::vec3(-1.0f, 0.0f, 0.0f));
view = view * rotation;
camFront = glm::vec3(view.x, view.y, view.z);
std::cerr << camFront.x << ' ' << camFront.y << ' ' << camFront.z << std::endl;
}
void Camera::update() {
// Also re-calculate the Right and Up vector
camRight = glm::normalize(glm::cross(camFront, WORLD_UP)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
camUp = glm::normalize(glm::cross(camRight, camFront));
}
#endif // CAMERA_H
How can I fix this?
You need to limit view.y and view.z values before assigning them to camFront, to be max 89 degrees and minimum -89 degrees. At 90 and -90 degrees it starts to invert. So a very simple approach could be,
if(view.y > 89)
{
view.y = 89;
}
if(view.y < -89)
{
view.y = -89;
}
if(view.z > 89)
{
view.z = 89;
}
if(view.z < -89)
{
view.z = -89;
}
I have a camera class, which is initialized like so:
CameraFP::CameraFP() {
this->aspect_ratio = 800.0f / 600.0f;
this->fov = 45.0f;
this->near_plane = 0.1f;
this->far_plane = 1000.0f;
this->position = glm::vec3(0, 0, 0);
this->target = position + glm::vec3(0, 0, -1);
this->up = glm::vec3(0, 1, 0);
this->m_rotation = glm::mat4(1.0);
m_view = glm::lookAt(position, target, up);
m_projection = glm::perspective(fov, aspect_ratio, near_plane, far_plane);
}
And here are other functions of import:
void CameraFP::update(sf::Window *app) {
process_keyboard(app);
process_mouse(app);
calculate_view();
}
void CameraFP::process_keyboard(sf::Window *app) {
const sf::Input *input = &app->GetInput();
up = m_rotation * glm::vec3(0, 1, 0);
glm::vec3 forward = glm::vec3(0, 0, -1);
glm::vec3 forward_rotated = m_rotation * forward;
glm::vec3 right = glm::vec3(1, 0, 0);
glm::vec3 right_rotated = m_rotation * right;
if (input->IsKeyDown(sf::Key::W)) {
position += forward_rotated;
}
if (input->IsKeyDown(sf::Key::S)) {
position -= forward_rotated;
}
if (input->IsKeyDown(sf::Key::A)) {
position -= right_rotated;
}
if (input->IsKeyDown(sf::Key::D)) {
position += right_rotated;
}
}
void CameraFP::process_mouse(sf::Window *app) {
// TODO: Make the below constants, and take framerate into account
GLfloat SPEED_X = 0.000001f;
GLfloat SPEED_Y = 0.000001f;
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 ||
mouse_y_delta != 0) {
if (mouse_x_delta != 0) {
y_rot += mouse_x_delta * SPEED_X;
m_rotation = glm::rotate(m_rotation, y_rot, glm::vec3(0, 1, 0));
}
if (mouse_y_delta != 0) {
x_rot += mouse_y_delta * SPEED_Y;
m_rotation = glm::rotate(m_rotation, 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);
}
void CameraFP::calculate_view() {
glm::vec3 forward = glm::vec3(0, 0, -1);
glm::vec3 forward_rotated = m_rotation * forward;
target = position += glm::normalize(forward_rotated);
m_view = glm::lookAt(position, target, up);
}
My problem is that when I compile the project, the compiler outputs an error saying:
\CameraFP.cpp|59|error: no match for 'operator*' in '((CameraFP*)this)->CameraFP::m_rotation * glm::detail::tvec3<float>(((const int&)((const int*)(&0))), ((const int&)((const int*)(&1))), ((const int&)((const int*)(&0))))'|
From what I understand vec = mat4 * vec should yield a rotated vector? Since I haven't been able to test this code, I don't know if the function work correctly.
Edit
Updated code according to the comments and awnsers. My problem is now that I get a BSOD, somewhere in the render function...
void CameraFP::process_keyboard(sf::Window *app) {
const sf::Input *input = &app->GetInput();
up = m_rotation * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f);
glm::vec4 forward = glm::vec4(0.0f, 0.0f, -1.0f, 0.0f);
glm::vec4 forward_rotated = m_rotation * forward;
glm::vec4 right = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f);
glm::vec4 right_rotated = m_rotation * right;
if (input->IsKeyDown(sf::Key::W)) {
position += forward_rotated;
}
if (input->IsKeyDown(sf::Key::S)) {
position -= forward_rotated;
}
if (input->IsKeyDown(sf::Key::A)) {
position -= right_rotated;
}
if (input->IsKeyDown(sf::Key::D)) {
position += right_rotated;
}
}
void CameraFP::process_mouse(sf::Window *app) {
// TODO: Make the below constants, and take framerate into account
GLfloat SPEED_X = 0.000001f;
GLfloat SPEED_Y = 0.000001f;
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 ||
mouse_y_delta != 0) {
if (mouse_x_delta != 0) {
y_rot += mouse_x_delta * SPEED_X;
m_rotation = glm::rotate(m_rotation, y_rot, glm::vec3(0.0f, 1.0f, 0.0f));
}
if (mouse_y_delta != 0) {
x_rot += mouse_y_delta * SPEED_Y;
m_rotation = glm::rotate(m_rotation, x_rot, glm::vec3(1.0f, 0.0f, 0.0f));;
}
}
this->old_mouse_x = mouse_x;
this->old_mouse_y = mouse_y;
app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);
}
void CameraFP::calculate_view() {
glm::vec4 forward = glm::vec4(0.0f, 0.0f, -1.0f, 0.0f);
glm::vec4 forward_rotated = m_rotation * forward;
target = position += forward_rotated;
m_view = glm::lookAt(v4tov3(position), v4tov3(target), v4tov3(up));
}
glm::vec3 v4tov3(glm::vec4 v1) {
return glm::vec3(v1.x, v1.y, v1.z);
}
Edit 2
Problem now is with the camera rotation with the mouse, it just doesn't work, for some reason changes on the x axis oft times effect change on the y and vice versa. In addition, if I move the mouse right or left on the x axis (y rotation) the camera rotates left...
void CameraFP::process_mouse(sf::Clock *clock, sf::Window *app) {
// TODO: Make the below constants, and take framerate into account
GLfloat SPEED_X = 0.25f;
GLfloat SPEED_Y = 0.25f;
GLfloat screen_x = app->GetWidth();
GLfloat screen_y = app->GetHeight();
GLfloat mouse_x = float(screen_x / 2 - app->GetInput().GetMouseX());
GLfloat mouse_y = float(screen_y / 2 - app->GetInput().GetMouseY());
GLfloat mouse_x_delta = old_mouse_x - mouse_x;
GLfloat mouse_y_delta = old_mouse_y - mouse_y;
GLfloat current_time = clock->GetElapsedTime();
GLfloat delta_time = current_time - last_time;
this->last_time = current_time;
if (mouse_x_delta != 0 ||
mouse_y_delta != 0) {
if (mouse_x_delta != 0) {
y_rot += glm::radians(delta_time * SPEED_X * mouse_x);
m_rotation = glm::rotate(m_rotation, y_rot, glm::vec3(0.0f, 1.0f, 0.0f));
std::cout << "Y Rotation: " << y_rot << "\n";
}
if (mouse_y_delta != 0) {
x_rot += glm::radians(delta_time * SPEED_Y * mouse_y);
m_rotation = glm::rotate(m_rotation, x_rot, glm::vec3(1.0f, 0.0f, 0.0f));
std::cout << "X rotation: " << x_rot << "\n";
}
}
app->SetCursorPosition(screen_x / 2, screen_y / 2);
this->old_mouse_x = float(screen_x / 2 - app->GetInput().GetMouseX());
this->old_mouse_y = float(screen_y / 2 - app->GetInput().GetMouseY());
}
Replace all glm::vec3(0, 1, 0); by glm::vec3(0.0f, 1.0f, 0.0f);
As for the vec-mac multiplication, AquilaRapax is right in that you can only multiply a mat4 with a vec4. But since you're multiplying directions, the 4rth coordinate should be 0.0f, not 1.0f. This will have the effect to ignore the translations (1.0 will teke them into account, which you don't want)
See http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ for details on matrices.
However, it's often a good idea to keep vec3 instead of vec4's, mostly for clarity purposes (i.e., glm::vec3 mPosition instead of glm::vec4 mPosition). It is thus handy to have 2 functions like these (untested) :
glm::vec3 TransformDirection(glm::vec3 pDirection, glm::mat4 pMatrix){
return pMatrix * glm::vec4(pDirection, 0.0f);
}
glm::vec3 TransformPosition(glm::vec3 pDirection, glm::mat4 pMatrix){
return pMatrix * glm::vec4(pDirection, 1.0f);
}
At the end of process::mouse you save the coordinates in old_mouse_x and old_mouse_y but then you move the cursor to the middle of the screen. If you do this old_mouse_x and old_mouse_y becomes invalid. What you need to do is set these variables after repositioning the cursor:
app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);
this->old_mouse_x = app->GetWidth() / 2;
this->old_mouse_y = app->GetHeight() / 2;