Add simple camera and input handling classes

This commit is contained in:
Evert Prants 2018-04-14 15:31:42 +03:00
parent 4c0a3d37d7
commit 25f1002ec1
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
9 changed files with 500 additions and 27 deletions

View File

@ -3,3 +3,11 @@ An idea for a 3D game engine that renders voxel planets, puts them into orbits a
## The Goal
The goal of this application is to make a scriptable voxel planets engine to use in the making of games.
## Compile
Currently only tested on Linux.
```
mkdir build && cd build
cmake ..
make
```

View File

@ -52,10 +52,69 @@ void Application::Initialize()
// Initialize GLEW
glewInit();
// Create camera
m_camera = new Camera();
// Run the engine
Run();
}
void Application::HandleEvents()
{
/* Update the input manager */
Input::getInstance().Flush();
/* Check for events */
SDL_Event e;
while ( SDL_PollEvent(&e) )
{
switch(e.type)
{
case SDL_QUIT:
Exit();
break;
case SDL_KEYDOWN:
Input::getInstance().pressKey(e.key.keysym.sym);
break;
case SDL_KEYUP:
Input::getInstance().releaseKey(e.key.keysym.sym);
break;
case SDL_MOUSEMOTION:
Input::getInstance().setMouseCoords(e.motion.x, e.motion.y);
break;
case SDL_MOUSEBUTTONDOWN:
Input::getInstance().pressKey(e.button.button);
break;
case SDL_MOUSEBUTTONUP:
Input::getInstance().releaseKey(e.button.button);
break;
case SDL_MOUSEWHEEL:
Input::getInstance().setMouseWheel(e.wheel.y, e.wheel.x);
break;
}
}
glm::vec2 mousepos = Input::getInstance().getMouseCoords();
if(Input::getInstance().isKeyDown(SDLK_w))
m_camera->ProcessKeyboard(Camera_Movement::FORWARD, 0.01f);
if(Input::getInstance().isKeyDown(SDLK_s))
m_camera->ProcessKeyboard(Camera_Movement::BACKWARD, 0.01f);
if(Input::getInstance().isKeyDown(SDLK_d))
m_camera->ProcessKeyboard(Camera_Movement::RIGHT, 0.01f);
if(Input::getInstance().isKeyDown(SDLK_a))
m_camera->ProcessKeyboard(Camera_Movement::LEFT, 0.01f);
m_camera->ProcessMouseScroll((float) Input::getInstance().getMouseWheelVertical() / 10.0f);
if(Input::getInstance().isKeyPressed(SDL_BUTTON_LEFT))
std::cout << "mX: " << mousepos.x << " mY: " << mousepos.y << std::endl;
}
void Application::Run()
{
m_run = true;
@ -66,14 +125,8 @@ void Application::Run()
{
last_ = now_;
now_ = SDL_GetPerformanceCounter();
while(SDL_PollEvent(&m_event) != 0)
{
// Close button is pressed
if(m_event.type == SDL_QUIT)
{
Exit();
}
}
HandleEvents();
deltaTime_ = ((now_ - last_) / (double)SDL_GetPerformanceFrequency());
@ -105,7 +158,7 @@ void Application::Run()
void Application::Update(GLfloat dtime)
{
log_info(std::to_string(dtime));
}
void Application::Render()

View File

@ -2,6 +2,8 @@
#define __APPLICATION_H__
#include "util/Common.h"
#include "util/Singleton.h"
#include "Camera.h"
#include "Input.h"
class Application : public Singleton<Application>
{
@ -14,6 +16,7 @@ class Application : public Singleton<Application>
friend class Singleton<Application>;
private:
Camera* m_camera;
SDL_Window* m_window;
SDL_Event m_event;
SDL_GLContext m_glContext;
@ -24,6 +27,7 @@ class Application : public Singleton<Application>
bool m_run;
void HandleEvents();
void Run();
void Render();
void Update(GLfloat dtime);

114
src/Camera.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "Camera.h"
// TODO: use Roll
Camera::Camera(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch, GLfloat roll) :
m_front(glm::vec3(0.0f, 0.0f, -1.0f)),
m_movementSpeed(SPEED),
m_mouseSensitivity(SENSITIVTY),
m_zoom(ZOOM)
{
m_position = position;
m_worldUp = up;
m_yaw = yaw;
m_pitch = pitch;
m_roll = roll;
updateCameraVectors();
}
Camera::Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch, GLfloat roll) :
m_front(glm::vec3(0.0f, 0.0f, -1.0f)),
m_movementSpeed(SPEED),
m_mouseSensitivity(SENSITIVTY),
m_zoom(ZOOM)
{
m_position = glm::vec3(posX, posY, posZ);
m_worldUp = glm::vec3(upX, upY, upZ);
m_yaw = yaw;
m_pitch = pitch;
m_roll = roll;
updateCameraVectors();
}
Camera::~Camera()
{
}
glm::mat4 Camera::getViewMatrix()
{
return glm::lookAt(m_position, m_position + m_front, m_up);
}
void Camera::ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime)
{
GLfloat velocity = m_movementSpeed * deltaTime;
if (direction == FORWARD)
m_position += m_front * velocity;
if (direction == BACKWARD)
m_position -= m_front * velocity;
if (direction == LEFT)
m_position -= m_right * velocity;
if (direction == RIGHT)
m_position += m_right * velocity;
}
void Camera::ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch = true)
{
xoffset *= m_mouseSensitivity;
yoffset *= m_mouseSensitivity;
m_yaw += xoffset;
m_pitch += yoffset;
// Make sure that when pitch is out of bounds, screen doesn't get flipped
if (constrainPitch)
{
if (m_pitch > 89.0f)
{
m_pitch = 89.0f;
}
if (m_pitch < -89.0f)
{
m_pitch = -89.0f;
}
}
// Update Front, Right and Up Vectors using the updated Eular angles
updateCameraVectors();
}
void Camera::ProcessMouseScroll(GLfloat yoffset)
{
if (m_zoom >= 44.0f && m_zoom <= 45.0f)
{
m_zoom -= yoffset;
}
if (m_zoom <= 44.0f)
{
m_zoom = 44.0f;
}
if (m_zoom >= 45.0f)
{
m_zoom = 45.0f;
}
}
void Camera::updateCameraVectors()
{
// Calculate the new Front vector
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);
// Also re-calculate the Right and Up vector
// Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
m_right = glm::normalize(glm::cross(m_front, m_worldUp));
m_up = glm::normalize(glm::cross(m_right, m_front));
}

55
src/Camera.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef __CAMERA_H__
#define __CAMERA_H__
#include "util/Common.h"
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// Default camera values
const GLfloat YAW = -90.0f;
const GLfloat PITCH = 0.0f;
const GLfloat ROLL = 0.0f;
const GLfloat SPEED = 3.0f;
const GLfloat SENSITIVTY = 0.25f;
const GLfloat ZOOM = 45.0f;
class Camera
{
public:
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), GLfloat yaw = YAW, GLfloat pitch = PITCH, GLfloat roll = ROLL);
Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch, GLfloat roll);
~Camera();
glm::mat4 getViewMatrix();
glm::vec3 getPosition() const { return m_position; }
void ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime);
void ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch);
void ProcessMouseScroll(GLfloat yoffset);
GLfloat getFOV() const { return m_zoom; }
private:
glm::vec3 m_position;
glm::vec3 m_front;
glm::vec3 m_up;
glm::vec3 m_right;
glm::vec3 m_worldUp;
// Eular Angles
GLfloat m_yaw;
GLfloat m_pitch;
GLfloat m_roll;
// Camera options
GLfloat m_movementSpeed;
GLfloat m_mouseSensitivity;
GLfloat m_zoom;
void updateCameraVectors();
};
#endif // __CAMERA_H__

64
src/Input.cpp Normal file
View File

@ -0,0 +1,64 @@
#include "Input.h"
// Remember previous positions
void Input::Flush()
{
for(auto& it : m_keyMap)
{
m_oldKeyMap[it.first] = it.second;
}
// Keep old mouse coordinates
m_oldMouseCoords = m_mouseCoords;
// Reset the mouse wheel scroll, as we will ever only need it once.
m_mwheelY = 0;
m_mwheelX = 0;
}
void Input::pressKey(unsigned int keyID)
{
m_keyMap[keyID] = true;
}
void Input::releaseKey(unsigned int keyID)
{
m_keyMap[keyID] = false;
}
void Input::setMouseCoords(float x, float y)
{
m_mouseCoords.x = x;
m_mouseCoords.y = y;
}
bool Input::isKeyDown(unsigned int keyID)
{
auto it = m_keyMap.find(keyID);
if(it != m_keyMap.end())
return it->second;
else
return false;
}
bool Input::wasKeyDown(unsigned int keyID)
{
auto it = m_oldKeyMap.find(keyID);
if(it != m_oldKeyMap.end())
return it->second;
else
return false;
}
bool Input::isKeyPressed(unsigned int keyID)
{
if(isKeyDown(keyID) == true && wasKeyDown(keyID) == false)
return true;
return false;
}
void Input::setMouseWheel(int vert, int horiz)
{
m_mwheelY = vert;
m_mwheelX = horiz;
}

39
src/Input.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __INPUT_H__
#define __INPUT_H__
#include "util/Singleton.h"
#include "util/Math3D.h"
#include <unordered_map>
class Input : public Singleton<Input>
{
public:
void Flush();
void pressKey(unsigned int keyID);
void releaseKey(unsigned int keyID);
void setMouseCoords(float x, float y);
void setMouseWheel(int vert, int horiz);
bool isKeyDown(unsigned int keyID);
bool isKeyPressed(unsigned int keyID);
glm::vec2 getMouseCoords() const { return m_mouseCoords; }
glm::vec2 getOldMouseCoords() const { return m_oldMouseCoords; }
int getMouseWheelVertical() const { return m_mwheelY; }
int getMouseWheelHorizontal() const { return m_mwheelX; }
friend class Singleton<Input>;
private:
bool wasKeyDown(unsigned int keyID);
std::unordered_map<unsigned int, bool> m_keyMap;
std::unordered_map<unsigned int, bool> m_oldKeyMap;
glm::vec2 m_mouseCoords;
glm::vec2 m_oldMouseCoords;
int m_mwheelY;
int m_mwheelX;
};
#endif // __INPUT_H__

View File

@ -1,4 +1,13 @@
#include "Shader.h"
#include "util/Log.h"
struct Shader::Attribute {
GLint size;
GLboolean normalized;
GLsizei stride;
GLuint offset;
GLenum type;
};
/** Compile a shader from file */
void Shader::compileShader(const string& filePath, GLuint& id)
@ -39,14 +48,14 @@ void Shader::compileShader(const string& filePath, GLuint& id)
glDeleteShader(id);
// Exit with failure.
// Exit with failure.
std::printf("%s\n", &(errorLog[0]));
fatalError("Shader " + filePath + " failed to compile");
fatalError("Shader @" + filePath + " failed to compile.");
}
}
/** Create new shader from vertex and fragment files */
Shader& Shader::createShader(const string& vertexShaderFilePath, const string& fragmentShaderFilePath)
Shader& Shader::createShader(const string& vertexShaderFilePath, const string& fragmentShaderFilePath)
{
Shader* shader = new Shader();
@ -69,6 +78,37 @@ Shader& Shader::createShader(const string& vertexShaderFilePath, const string& f
return *shader;
}
/** Create new shader from vertex, fragment and geometry files */
Shader& Shader::createShader(const string& vertexShaderFilePath, const string& fragmentShaderFilePath, const string& geometryShaderFilePath)
{
Shader* shader = new Shader();
shader->m_programID = glCreateProgram();
shader->m_vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
if(shader->m_vertexShaderID == 0)
{
fatalError("Vertex shader failed to be created!");
}
shader->m_fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
if(shader->m_fragmentShaderID == 0)
{
fatalError("Fragment shader failed to be created!");
}
shader->m_geometryShaderID = glCreateShader(GL_GEOMETRY_SHADER);
if(shader->m_geometryShaderID == 0)
{
fatalError("Geometry shader failed to be created!");
}
Shader::compileShader(vertexShaderFilePath, shader->m_vertexShaderID);
Shader::compileShader(fragmentShaderFilePath, shader->m_fragmentShaderID);
Shader::compileShader(geometryShaderFilePath, shader->m_geometryShaderID);
return *shader;
}
/** Get uniform location */
GLuint Shader::getUniformLocation(const string& uniformName)
{
@ -76,9 +116,11 @@ GLuint Shader::getUniformLocation(const string& uniformName)
if (it == m_uniforms.end())
{
// Get uniform location
GLint r = glGetUniformLocation(m_programID, uniformName.c_str());
if ( r == GL_INVALID_OPERATION || r < 0 )
GLint r = glGetUniformLocation(m_programID, uniformName.c_str());
if (r == GL_INVALID_OPERATION || r < 0)
{
log_warn("Uniform " + uniformName + " doesn't exist in program.");
}
// Add it to the cache
m_uniforms[uniformName] = r;
@ -91,6 +133,35 @@ GLuint Shader::getUniformLocation(const string& uniformName)
}
}
GLuint Shader::getAttribLocation(const string& attrbuteName)
{
GLint attrib = glGetAttribLocation(m_programID, attrbuteName.c_str());
if (attrib == GL_INVALID_OPERATION || attrib < 0)
{
log_warn("Attribute " + attrbuteName + " doesn't exist in program.");
}
return attrib;
}
void Shader::setAttribute(const std::string& name,
GLint size,
GLboolean normalized,
GLsizei stride,
GLuint offset,
GLenum type)
{
GLuint loc = getAttribLocation(name);
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, size, type, normalized, stride * sizeof(GLfloat),
(void*)(offset * sizeof(GLfloat)));
m_attributes[name].size = size;
m_attributes[name].normalized = normalized;
m_attributes[name].stride = stride;
m_attributes[name].offset = offset;
m_attributes[name].type = type;
}
/** Link the shaders together */
void Shader::linkShaders()
{
@ -120,8 +191,8 @@ void Shader::linkShaders()
glDeleteShader(m_vertexShaderID);
glDeleteShader(m_fragmentShaderID);
fatalError("Shader linking failed!");
std::printf("%s\n", &(infoLog[0]));
fatalError("Shader linking failed!");
}
// Always detach shaders after a successful link.
@ -131,12 +202,6 @@ void Shader::linkShaders()
glDeleteShader(m_fragmentShaderID);
}
/** Bind attribute location */
void Shader::addAttribute(const string& attribName)
{
glBindAttribLocation(m_programID, m_attributes++, attribName.c_str());
}
/** Bind the shader for usage */
void Shader::start()
{
@ -198,3 +263,60 @@ void Shader::setUniform(const string& name, int val)
{
glUniform1i(getUniformLocation(name), val);
}
void Shader::use()
{
if (!m_valid)
{
m_valid = true;
glLinkProgram(m_programID);
start();
GLint result;
glGetProgramiv(m_programID, GL_LINK_STATUS, &result);
if (result != GL_TRUE)
{
log_error("Shader relink failed!");
GLsizei maxLength = 0;
glGetProgramiv(m_programID, GL_INFO_LOG_LENGTH, &maxLength);
char* log = new char[maxLength];
glGetProgramInfoLog(m_programID, maxLength, &maxLength, log);
std::printf("%s\n", log);
}
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
for (auto it(m_attributes.begin()); it != m_attributes.end(); ++it)
{
GLuint location = getAttribLocation(it->first);
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
it->second.size,
it->second.type,
it->second.normalized,
it->second.stride * sizeof(GLfloat),
(void*)(it->second.offset * sizeof(GLfloat))
);
}
} else {
start();
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
}
}
void Shader::setBuffers(GLint vao, GLint vbo, GLint ebo)
{
this->vao = vao;
this->vbo = vbo;
this->ebo = ebo;
}

View File

@ -4,19 +4,22 @@
#include <map>
#include "util/Common.h"
#include "util/Log.h"
class Shader
{
public:
static Shader& createShader(const string& vertexShaderFilePath, const string& fragmentShaderFilePath);
static Shader& createShader(const string& vertexShaderFilePath, const string& fragmentShaderFilePath, const string& geometryShaderFilePath);
void linkShaders();
void addAttribute(const string& attribName);
GLuint getUniformLocation(const string& uniformName);
void setAttribute(const std::string& name, GLint size, GLboolean normalized, GLsizei stride, GLuint offset, GLenum type = GL_FLOAT);
GLuint getUniformLocation(const string& uniformName);
inline GLuint operator[](const std::string& name) { return getUniformLocation(name); }
GLuint getAttribLocation(const string& attrbuteName);
// Set uniforms
void setUniform(const string& name, float x, float y, float z);
void setUniform(const string& name, const vec3& v);
void setUniform(const string& name, const dvec3& v);
@ -28,16 +31,27 @@ public:
void setUniform(const string& name, float val);
void setUniform(const string& name, int val);
void setBuffers(GLint vao, GLint vbo, GLint ebo);
// Bind Attributes and start the shader program
void use();
// Start the shader program without binding attributes
void start();
void stop();
private:
int m_attributes;
struct Attribute;
bool m_valid;
GLuint m_programID;
GLuint m_vertexShaderID;
GLuint m_fragmentShaderID;
GLuint m_geometryShaderID;
map<string, GLint> m_uniforms;
map<string, GLuint> m_uniforms;
map<string, Attribute> m_attributes;
GLint vao, vbo, ebo;
static void compileShader(const string& filePath, GLuint& id);
};