I have an object "character" which can turn around itself left and right and move forward in its face direction, and a camera always looks at the object and can rotate around it.
The problem is : I want to move the camera with the object movement in its direction regardless the camera direction as in Prince of Persia, if the character face against the camera direction and the character move forward this means if the camera is not moving the character become more near to the camera so if the camera is moveable the camera should move back to make the distance between them the same, if the character looks to the right then the camera should moves to its right and so on for any direction, the camera have position, front, right and up vectors and yaw and pitch angles, the character has position and yaw and pitch angles.
the code which makes the character move is:
void Mesh::Move(Move_Directions direction, GLfloat deltaTime) {
GLfloat velocity = 50 * this->movementSpeed * deltaTime;
if (direction == FORWARD) {
glm::vec3 t = glm::vec3(sin(glm::radians(yaw)), 0.0, cos(glm::radians(yaw))) * velocity;
matrix = glm::translate(matrix, t);
for (GLuint i = 0; i < this->m_Entries.size(); i++) {
this->m_Entries[i].setModelMatrix(matrix);
}
//scene->getDefCamera()->Translate(t);
}
if (direction == BACKWARD) {
glm::vec3 t = glm::vec3(sin(glm::radians(yaw)), 0.0, cos(glm::radians(yaw))) * velocity;
matrix = glm::translate(matrix, -t);
for (GLuint i = 0; i < this->m_Entries.size(); i++) {
this->m_Entries[i].setModelMatrix(matrix);
}
//scene->getDefCamera()->Translate(t);
}
if (direction == RIGHT) {
matrix = glm::rotate(matrix, (GLfloat) -M_PI*deltaTime, glm::vec3(0.0, 1.0, 0.0));
for (GLuint i = 0; i < this->m_Entries.size(); i++) {
this->m_Entries[i].setModelMatrix(matrix);
}
}
if (direction == LEFT) {
matrix = glm::rotate(matrix, (GLfloat) M_PI*deltaTime, glm::vec3(0.0, 1.0, 0.0));
for (GLuint i = 0; i < this->m_Entries.size(); i++) {
this->m_Entries[i].setModelMatrix(matrix);
}
}
}
and the code of camera is:
void Camera::updateVectors() {
GLfloat radius = glm::length(center - position);
position.x = radius * cos(glm::radians(this->yaw)) * cos(glm::radians(this->pitch));
position.y = radius * sin(glm::radians(this->pitch));
position.z = radius * sin(glm::radians(this->yaw)) * cos(glm::radians(this->pitch));
this->front = glm::normalize(center - position);
this->right = glm::normalize(glm::cross(this->front, this->worldUp));
this->up = glm::normalize(glm::cross(this->right, this->front));
}
You want to add a behaviour to your camera base maybe on the distance to the object. Maybe add a treshold, if camera.pos - object.pos < d, move the camera else the camera should stay at its position.
For your image you can compute the vector your object is facing.
When you take your "z,q,s,d" input you should move the character and the camera with the same amount
Related
I'm studying opengl and following the tutorials on the learnopengl website (https://learnopengl.com/)
Could someone please help me convert this camera from the learnopengl tutorial, from free camera to third person ?
I've tried to put the values of the object (character), but I can't make the camera rotate around the player. Only the object walks in front of the camera, if I turn to the side, the object turns along, look like FPS camera and the object (character) being the weapon.
The code to walk (keyboard):
void processKeyboard(Camera_Movement direction, float deltaTime)
{
frontY = Front.y;//para tirar o freeCamera
if (cameraStyle == FPS_CAMERA) {
Front.y = 0;
}
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
Front.y = frontY;
}
Mouse event:
void processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
{
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// Make sure that when pitch is out of bounds, screen doesn't get flipped
if (constrainPitch)
{
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
// Update Front, Right and Up Vectors using the updated Euler angles
updateCameraVectors();
}
To update values:
void updateCameraVectors()
{
// Calculate the new Front vector
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));
Front = glm::normalize(front);
// Also re-calculate the Right and Up vector
Right = glm::normalize(glm::cross(Front, WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
Up = glm::normalize(glm::cross(Right, Front));
}
And to use:
glm::vec3 playerPosition = glm::vec3(Position.x, terrainY, Position.z) + glm::vec3(1, -0.06f, 1)
has anyone been through this and who could help me?
Thank you
Here is the code I use to create a third person camera:
float pitch = -Pitch;
// I use a 90.0f offset
// but you can play around with that value to suit your needs
float yaw = Yaw - 90.0f;
// constrain pitch so you don't look from below ground level
if (pitch < 0.0) {
pitch = 0.0;
}
// algorithm from ThinMatrix video on third person cameras
float distance = 20.0f;
float x = distance * cos(glm::radians(pitch)) * sin(radians(-yaw));
float y = distance * sin(glm::radians(pitch));
float z = distance * cos(glm::radians(pitch)) * cos(glm::radians(yaw));
glm::vec3 tpCamPos = playerPosition + vec3(-x, y, -z);
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 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
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.
So I'm trying to figure out how to mannually create a camera class that creates a local frame for camera transformations. I've created a player object based on OpenGL SuperBible's GLFrame class.
I got keyboard keys mapped to the MoveUp, MoveRight and MoveForward functions and the horizontal and vertical mouse movements are mapped to the xRot variable and rotateLocalY function. This is done to create a FPS style camera.
The problem however is in the RotateLocalY. Translation works fine and so does the vertical mouse movement but the horizontal movement scales all my objects down or up in a weird way. Besides the scaling, the rotation also seems to restrict itself to 180 degrees and rotates around the world origin (0.0) instead of my player's local position.
I figured that the scaling had something to do with normalizing vectors but the GLframe class (which I used for reference) never normalized any vectors and that class works just fine. Normalizing most of my vectors only solved the scaling and all the other problems were still there so I'm figuring one piece of code is causing all these problems?
I can't seem to figure out where the problem lies, I'll post all the appropriate code here and a screenshot to show the scaling.
Player object
Player::Player()
{
location[0] = 0.0f; location[1] = 0.0f; location[2] = 0.0f;
up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
forward[0] = 0.0f; forward[1] = 0.0f; forward[2] = -1.0f;
}
// Does all the camera transformation. Should be called before scene rendering!
void Player::ApplyTransform()
{
M3DMatrix44f cameraMatrix;
this->getTransformationMatrix(cameraMatrix);
glRotatef(xAngle, 1.0f, 0.0f, 0.0f);
glMultMatrixf(cameraMatrix);
}
void Player::MoveForward(GLfloat delta)
{
location[0] += forward[0] * delta;
location[1] += forward[1] * delta;
location[2] += forward[2] * delta;
}
void Player::MoveUp(GLfloat delta)
{
location[0] += up[0] * delta;
location[1] += up[1] * delta;
location[2] += up[2] * delta;
}
void Player::MoveRight(GLfloat delta)
{
// Get X axis vector first via cross product
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, forward);
location[0] += xAxis[0] * delta;
location[1] += xAxis[1] * delta;
location[2] += xAxis[2] * delta;
}
void Player::RotateLocalY(GLfloat angle)
{
// Calculate a rotation matrix first
M3DMatrix44f rotationMatrix;
// Rotate around the up vector
m3dRotationMatrix44(rotationMatrix, angle, up[0], up[1], up[2]); // Use up vector to get correct rotations even with multiple rotations used.
// Get new forward vector out of the rotation matrix
M3DVector3f newForward;
newForward[0] = rotationMatrix[0] * forward[0] + rotationMatrix[4] * forward[1] + rotationMatrix[8] * forward[2];
newForward[1] = rotationMatrix[1] * forward[1] + rotationMatrix[5] * forward[1] + rotationMatrix[9] * forward[2];
newForward[2] = rotationMatrix[2] * forward[2] + rotationMatrix[6] * forward[1] + rotationMatrix[10] * forward[2];
m3dCopyVector3(forward, newForward);
}
void Player::getTransformationMatrix(M3DMatrix44f matrix)
{
// Get Z axis (Z axis is reversed with camera transformations)
M3DVector3f zAxis;
zAxis[0] = -forward[0];
zAxis[1] = -forward[1];
zAxis[2] = -forward[2];
// Get X axis
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, zAxis);
// Fill in X column in transformation matrix
m3dSetMatrixColumn44(matrix, xAxis, 0); // first column
matrix[3] = 0.0f; // Set 4th value to 0
// Fill in the Y column
m3dSetMatrixColumn44(matrix, up, 1); // 2nd column
matrix[7] = 0.0f;
// Fill in the Z column
m3dSetMatrixColumn44(matrix, zAxis, 2); // 3rd column
matrix[11] = 0.0f;
// Do the translation
M3DVector3f negativeLocation; // Required for camera transform (right handed OpenGL system. Looking down negative Z axis)
negativeLocation[0] = -location[0];
negativeLocation[1] = -location[1];
negativeLocation[2] = -location[2];
m3dSetMatrixColumn44(matrix, negativeLocation, 3); // 4th column
matrix[15] = 1.0f;
}
Player object header
class Player
{
public:
//////////////////////////////////////
// Variables
M3DVector3f location;
M3DVector3f up;
M3DVector3f forward;
GLfloat xAngle; // Used for FPS divided X angle rotation (can't combine yaw and pitch since we'll also get a Roll which we don't want for FPS)
/////////////////////////////////////
// Functions
Player();
void ApplyTransform();
void MoveForward(GLfloat delta);
void MoveUp(GLfloat delta);
void MoveRight(GLfloat delta);
void RotateLocalY(GLfloat angle); // Only need rotation on local axis for FPS camera style. Then a translation on world X axis. (done in apply transform)
private:
void getTransformationMatrix(M3DMatrix44f matrix);
};
Applying transformations
// Clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Apply camera transforms
player.ApplyTransform();
// Set up lights
...
// Use shaders
...
// Render the scene
RenderScene();
// Do post rendering operations
glutSwapBuffers();
and mouse
float mouseSensitivity = 500.0f;
float horizontal = (width / 2) - mouseX;
float vertical = (height / 2) - mouseY;
horizontal /= mouseSensitivity;
vertical /= (mouseSensitivity / 25);
player.xAngle += -vertical;
player.RotateLocalY(horizontal);
glutWarpPointer((width / 2), (height / 2));
Honestly I think you are taking a way to complicated approach to your problem. There are many ways to create a camera. My favorite is using a R3-Vector and a Quaternion, but you could also work with a R3-Vector and two floats (pitch and yaw).
The setup with two angles is simple:
glLoadIdentity();
glTranslatef(-pos[0], -pos[1], -pos[2]);
glRotatef(-yaw, 0.0f, 0.0f, 1.0f);
glRotatef(-pitch, 0.0f, 1.0f, 0.0f);
The tricky part now is moving the camera. You must do something along the lines of:
flaot ds = speed * dt;
position += tranform_y(pich, tranform_z(yaw, Vector3(ds, 0, 0)));
How to do the transforms, I would have to look that up, but you could to it by using a rotation matrix
Rotation is trivial, just add or subtract from the pitch and yaw values.
I like using a quaternion for the orientation because it is general and thus you have a camera (any entity that is) that independent of any movement scheme. In this case you have a camera that looks like so:
class Camera
{
public:
// lots of stuff omitted
void setup();
void move_local(Vector3f value);
void rotate(float dy, float dz);
private:
mx::Vector3f position;
mx::Quaternionf orientation;
};
Then the setup code uses shamelessly gluLookAt; you could make a transformation matrix out of it, but I never got it to work right.
void Camera::setup()
{
// projection related stuff
mx::Vector3f eye = position;
mx::Vector3f forward = mx::transform(orientation, mx::Vector3f(1, 0, 0));
mx::Vector3f center = eye + forward;
mx::Vector3f up = mx::transform(orientation, mx::Vector3f(0, 0, 1));
gluLookAt(eye(0), eye(1), eye(2), center(0), center(1), center(2), up(0), up(1), up(2));
}
Moving the camera in local frame is also simple:
void Camera::move_local(Vector3f value)
{
position += mx::transform(orientation, value);
}
The rotation is also straight forward.
void Camera::rotate(float dy, float dz)
{
mx::Quaternionf o = orientation;
o = mx::axis_angle_to_quaternion(horizontal, mx::Vector3f(0, 0, 1)) * o;
o = o * mx::axis_angle_to_quaternion(vertical, mx::Vector3f(0, 1, 0));
orientation = o;
}
(Shameless plug):
If you are asking what math library I use, it is mathex. I wrote it...