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
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};
}
Using a glm::lookAt function to create the camera view matrix is good but does not help in storing the camera Euler angles. although the calculations seem correct, the view seems to bring the wrong pitch value.
in the following code, if the Y values of the current camera position and destination is the same then, there is no problem. however, the view seems to tilt further down or up if the Y values of the camera position and destination are not equal.
The question is: why does the camera not correctly points an object if the Y values of the camera and object positions are not equal.
float wrapAngle(float angle)
{
int break_after = 100;
constexpr float full_rotation = 2.0 * glm::pi<float>();
while (angle < 0.0f || angle >= full_rotation)
{
if (angle < 0.0f) angle = angle + full_rotation;
if (angle >= full_rotation) angle = angle - full_rotation;
if (--break_after == 0) break;
}
if (break_after == 0) angle = 0.0f;
return angle;
}
void getLookAtAngle(const glm::vec3& position, const glm::vec3& destination, glm::vec3 &angle)
{
//! Find vector of sight toward destination.
glm::vec3 sight = destination - position;
//! Find X, Y rotation against the axis (global).
double yAngle = wrapAngle(std::atan2(sight.x, sight.z));
glm::mat4 yModel(1);
yModel = glm::rotate(yModel, static_cast<float>(-yAngle), glm::vec3(0.0f, 1, 0));
sight = yModel * glm::vec4(sight, 1.0f);
double xAngle = wrapAngle(-std::atan2(sight.y, sight.z));
//! assign xAngle, yAngle to the parameter 'angle'
angle.x = glm::degrees(static_cast<float>(xAngle));
angle.y = glm::degrees(static_cast<float>(yAngle));
angle.z = 0.0f;
}
void updateCamera(const glm::vec3& position, const glm::vec3& destination)
{
glm::vec3 m_rotation(1);
getLookAtAngle(position, destination, m_rotation)
m_model = glm::mat4(1);
m_model = glm::rotate(m_model, glm::radians(m_rotation[0]), glm::vec3(1.0f, 0, 0));
m_model = glm::rotate(m_model, glm::radians(m_rotation[1]), glm::vec3(0.0f, 1, 0));
m_model = glm::rotate(m_model, glm::radians(m_rotation[2]), glm::vec3(0.0f, 0, 1));
m_front = glm::normalize(glm::vec3(m_model * glm::vec4(0, 0, 1.0f, 1.0f)));
m_right = glm::normalize(glm::cross(m_front, glm::vec3(0, 1.0f, 0)));
m_up = glm::normalize(glm::cross(m_right, m_front));
m_view = glm::lookAt(position, position+ m_front, m_up);
}
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.
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;
}
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.