The tools I am using are: gcc, opengl es 2 and glm.
I am trying to align a weapon model with the first-person camera. The main issue is that I can not seem to figure out how to "anchor the weapon to the camera" by utilizing the camera space and offsetting and rotating the object so it fits like in an FPS-game.
With the help of GDN, I created this camera class:
[spoiler]
#pragma once
#include <GLES2/gl2.h>
#include "deps/glm/glm.hpp"
#include "deps/glm/gtc/matrix_transform.hpp"
#define DEFAULT_YAW -90.f
#define DEFAULT_PITCH 0.f
#define DEFAULT_SPEED 15.f
#define DEFAULT_SCROLL_SPEED 0.04f
#define DEFAULT_SENSE .24f
#define DEFAULT_FOV_Y 45.f
#define DEFAULT_RATIO 16.f/9.f
#define DEFAULT_NEAR .1f
#define DEFAULT_FAR 1024.f
#define MIN_FOV 44.3f
#define MAX_FOV DEFAULT_FOV_Y
#define MIN_PITCH -89.f
#define MAX_PITCH 89.f
class Camera
{
public:
Camera(glm::vec3 pos = default_pos, glm::vec3 up = default_up) :
m_Position(pos),
m_WorldUp(up)
{
UpdateVectors(); // Otherwise glm::LookAt will complain.
}
protected:
glm::vec3 m_Position; // Set by ctor.
glm::vec3 m_WorldUp; // Set by ctor.
glm::vec3 m_Target; // Set by UpdateVectors().
glm::vec3 m_Right; // Set by UpdateVectors().
glm::vec3 m_Up; // Set by UpdateVectors().
GLfloat m_Ratio = DEFAULT_RATIO;
GLfloat m_FOV = DEFAULT_FOV_Y;
GLfloat m_FOVmin = MIN_FOV;
GLfloat m_FOVmax = MAX_FOV;
GLfloat m_Yaw = DEFAULT_YAW;
GLfloat m_Pitch = DEFAULT_PITCH;
GLfloat m_PitchMin = MIN_PITCH;
GLfloat m_PitchMax = MAX_PITCH;
GLfloat m_MovementSpeed = DEFAULT_SPEED;
GLfloat m_ScrollSpeed = DEFAULT_SCROLL_SPEED;
GLfloat m_MouseSenseX = DEFAULT_SENSE;
GLfloat m_MouseSenseY = DEFAULT_SENSE;
public:
glm::mat4 View() const { return glm::lookAt(m_Position, m_Target + m_Position, m_Up); }
glm::mat4 Projection() const { return glm::perspective(m_FOV, m_Ratio, DEFAULT_NEAR, DEFAULT_FAR); }
glm::mat4 ViewProjection() const { return Projection() * View(); }
glm::vec3 const& Position() const { return m_Position; }
void Position(glm::vec3 pos) { m_Position = pos; }
glm::vec3 Target() const { return m_Target; }
void Target(GLfloat x, GLfloat y, GLfloat z) { m_Target = glm::vec3(x, y, z); }
glm::vec3 Right() const { return m_Right; }
glm::vec3 WorldUp() const { return m_WorldUp; }
glm::vec3 Up() const { return m_Up; }
GLfloat const& GetRatio() const { return m_Ratio; }
GLfloat const& GetFOV() const { return m_FOV; }
GLfloat const& GetYaw() const { return m_Yaw; }
GLfloat const& GetPitch() const { return m_Pitch; }
void SetYaw(GLfloat yaw) { m_Yaw = yaw; }
void SetPitch(GLfloat pitch) { m_Pitch = pitch; }
void MoveForward(GLfloat time_delta) { m_Position += m_Target*m_MovementSpeed*time_delta; }
void MoveBack(GLfloat time_delta) { m_Position -= m_Target*m_MovementSpeed*time_delta; }
void MoveLeft(GLfloat time_delta){ m_Position -= m_Right*m_MovementSpeed*time_delta; }
void MoveRight(GLfloat time_delta) { m_Position += m_Right*m_MovementSpeed*time_delta; }
void MoveUp(GLfloat time_delta) { m_Position += m_WorldUp*m_MovementSpeed*time_delta; }
void MoveDown(GLfloat time_delta) { m_Position -= m_WorldUp*m_MovementSpeed*time_delta; }
void Yaw(GLfloat offset) { m_Yaw += offset*m_MouseSenseX; }
void Pitch(GLfloat offset)
{
m_Pitch += offset*m_MouseSenseY;
if (m_Pitch > m_PitchMax)
{
m_Pitch = m_PitchMax;
}
else if (m_Pitch < m_PitchMin)
{
m_Pitch = m_PitchMin;
}
}
void Zoom(GLfloat y)
{
if (m_FOV >= m_FOVmin && m_FOV <= m_FOVmax)
{
m_FOV -= y*m_ScrollSpeed;
}
if (m_FOV > m_FOVmax)
{
m_FOV = m_FOVmax;
}
if (m_FOV < m_FOVmin)
{
m_FOV = m_FOVmin;
}
}
void UpdateVectors()
{
glm::vec3 front;
front.x = glm::cos(glm::radians(m_Yaw)) * glm::cos(glm::radians(m_Pitch));
front.y = glm::sin(glm::radians(m_Pitch));
front.z = glm::sin(glm::radians(m_Yaw)) * glm::cos(glm::radians(m_Pitch));
m_Target = glm::normalize(front);
m_Right = glm::normalize(glm::cross(m_Target, m_WorldUp));
m_Up = glm::normalize(glm::cross(m_Right, m_Target));
}
};
[/spoiler]
All my models inherit from the Transformable class, which I use to create the model matrix, dubbed Transform(), take a look:
[spoiler]
#pragma once
#include <GLES2/gl2.h>
#include "deps/glm/gtc/matrix_transform.hpp"
class Transformable
{
public:
Transformable() {}
Transformable(glm::vec3 const& pos, glm::vec3 const& size)
{
Translate(pos.x, pos.y + size.y/2.f, pos.z); // Place on the y-coordinate.
Scale(size); // Set initial size.
}
protected:
glm::mat4 m_TransformTranslation;
glm::mat4 m_TransformScaling;
glm::mat4 m_TransformRotationX;
glm::mat4 m_TransformRotationY;
glm::mat4 m_TransformRotationZ;
glm::vec3 m_Orientation;
public:
void Position(glm::vec3 const& pos) { m_TransformTranslation = glm::translate(glm::mat4(1.f), pos); }
glm::vec4 const& Position() const { return m_TransformTranslation[3]; }
glm::mat4 Rotation() const { return m_TransformRotationX * m_TransformRotationY * m_TransformRotationZ; }
GLfloat X() const { return Position().x; }
GLfloat Y() const { return Position().y; }
GLfloat Z() const { return Position().z; }
GLfloat Width() const { return m_TransformScaling[0].x; }
GLfloat Height() const { return m_TransformScaling[1].y; }
GLfloat Length() const { return m_TransformScaling[2].z; }
void OrientX(GLfloat deg) { m_Orientation.x = deg; m_TransformRotationX = glm::rotate(glm::mat4(1), m_Orientation.x, glm::vec3(1, 0, 0)); }
void OrientY(GLfloat deg) { m_Orientation.y = -deg; m_TransformRotationY = glm::rotate(glm::mat4(1), m_Orientation.y, glm::vec3(0, 1, 0)); }
void OrientZ(GLfloat deg) { m_Orientation.z = deg; m_TransformRotationZ = glm::rotate(glm::mat4(1), m_Orientation.z, glm::vec3(0, 0, 1)); }
void Translate(glm::vec3 const& vec) { m_TransformTranslation = glm::translate(m_TransformTranslation, vec); }
void Scale(glm::vec3 const& vec) { m_TransformScaling = glm::scale(glm::mat4(1), vec); }
void Translate(GLfloat x, GLfloat y, GLfloat z) { Translate(glm::vec3(x, y, z)); }
void Scale(GLfloat x, GLfloat y, GLfloat z) { Scale(glm::vec3(x, y, z)); }
glm::mat4 Transform() const { return m_TransformTranslation * Rotation() * m_TransformScaling; }
};
[/spoiler]
I do all the offsetting, yawing and pitching in the player class, which holds the model for the weapon.
[spoiler]
#pragma once
#include "camera.h"
#include "model.h"
class Player
{
public:
Player(Model *weapon) :
m_Camera(new Camera),
m_Weapon(weapon)
{}
~Player()
{
delete m_Camera;
}
private:
Camera *m_Camera;
Model *m_Weapon;
glm::vec3 m_Position;
glm::vec3 m_Rotation;
GLfloat m_MovementSpeed = 100.f;
GLfloat m_MouseSenseX = 1;
GLfloat m_MouseSenseY = 1;
public:
Camera* PlayerCamera() const { return m_Camera; }
Model* GetWeaponModel() const { return m_Weapon; }
void Update()
{
m_Camera->Position(m_Position);
m_Camera->Update();
glm::vec3 weapon_offset_y = glm::vec3(0, 10, 0);
m_Weapon->Position(m_Position + weapon_offset_y);
m_Weapon->OrientY(m_Camera->GetYaw() + 30);
m_Weapon->OrientZ(m_Camera->GetPitch() + 30);
m_Weapon->OrientX(20);
}
void Position(glm::vec3 pos) { m_Position = pos; Update(); }
void SetYaw(GLfloat deg) { m_Camera->SetYaw(deg); Update(); }
void MoveForward(GLfloat delta) { m_Position += m_Camera->Target() * m_MovementSpeed * delta; Update(); }
void MoveBack(GLfloat delta) { m_Position -= m_Camera->Target() * m_MovementSpeed * delta; Update(); }
void MoveLeft(GLfloat delta) { m_Position -= m_Camera->Right() * m_MovementSpeed * delta; Update(); }
void MoveRight(GLfloat delta) { m_Position += m_Camera->Right() * m_MovementSpeed * delta; Update(); }
void Yaw(GLfloat delta) { m_Camera->Yaw(m_MouseSenseX * delta); Update(); }
void Pitch(GLfloat delta) { m_Camera->Pitch(m_MouseSenseX * delta); Update(); }
};
[/spoiler]
As you can see, I am struggling with the model matrix, which is currently an RTS-matrix and the camera provides a projection*view matrix with ViewProjection(). I multiply both and pass it as a uniform to the shader.
Although It seems that I am doing the offsets and rotations wrong, because when I move the camera, the weapon model moves in arcs and shifts weird.
Any suggestions and advice is much appreciated!