Skip to content

Instantly share code, notes, and snippets.

@Robadob
Last active September 14, 2016 14:05
Show Gist options
  • Save Robadob/1ba4d4a5fb587d6e874642559d4cbb01 to your computer and use it in GitHub Desktop.
Save Robadob/1ba4d4a5fb587d6e874642559d4cbb01 to your computer and use it in GitHub Desktop.
No Clip Style camera controller
#include "Camera.h"
#include <glm/gtx/rotate_vector.hpp>
/*
Initialises the camera located at (1,1,1) directed at (0,0,0)
*/
Camera::Camera()
: Camera(glm::vec3(1, 1, 1))
{}
/*
Initialises the camera located at eye, directed at (0,0,0)
@param eye The coordinates the camera is located
*/
Camera::Camera(glm::vec3 eye)
: Camera(eye, glm::vec3(0, 0, 0))
{}
//Initialiser list written to remove any references to member variables
//Because member variables are initialised via initaliser lists in the order they are declared in the class declaration (rather than the order of the initialiser list)
/*
Initialises the camera located at eye directed at target
@param eye The coordinates the camera is located
@param target The coordinates the camera is directed towards
*/
Camera::Camera(glm::vec3 eye, glm::vec3 target)
: pureUp(0.0f, 1.0f, 0.0f)
, eye(eye)
, look(normalize(target - eye))
, right(normalize(cross(glm::vec3(0, 1, 0), target - eye)))
, up(normalize(cross(cross(glm::vec3(0, 1, 0), target - eye), target - eye)))
, stabilise(true)
{
//this->eye = eye; //Eye is the location passed by user
//this->look = target - eye; //Look is the direction from eye to target
//this->right = cross(glm::vec3(0, 1, 0), look); //Right is perpendicular to look and (0,1,0)[Default up]
//this->up = cross(right, look); //Up is perpendicular to right and look
//Make sure Up is not upside down
if (up.y < 0)
{
up = -up;
right = -right;
}
this->updateViews();
}
/*
Default destructor
*/
Camera::~Camera(){
}
/*
Rotate look and right, yaw radians about up
Rotate look and up, pitch radians about right
@param yaw The number of radians to rotate the camera's direction about the up vector
@param pitch The number of radians to rotate the camera's direction about the right vector
*/
void Camera::turn(float yaw, float pitch){
//Rotate everything yaw rads about up vector
this->look = normalize(rotate(this->look, -yaw, this->up));
this->right = normalize(rotate(this->right, -yaw, this->up));
//Rotate everything pitch rads about right vector
glm::vec3 look = normalize(rotate(this->look, -pitch, this->right));
glm::vec3 up = normalize(rotate(this->up, -pitch, this->right));
glm::vec3 right = this->right;
if (stabilise)
{
//Right is perpendicular to look and (0,1,0)[Default up]
right = cross(look, this->pureUp);
//Stabilised up is perpendicular to right and look
up = cross(right, look);
//Don't let look get too close to pure up, else we will spin
if (abs(dot(look, this->pureUp)) > 0.98)
return;
}
//Commit changes
this->look = look;
this->right = right;
this->up = up;
this->updateViews();
}
/*
Move eye specified distance along look
@param distance The number of units to move the camera
*/
void Camera::move(float distance){
eye += look*distance;
this->updateViews();
}
/*
Rotate right and up, roll radians about look
@param roll The number of radians to rotate the camera's direction about the look vector
*/
void Camera::roll(float roll){
pureUp = normalize(rotate(pureUp, roll, look));
right = normalize(rotate(right, roll, look));
up = normalize(rotate(up, roll, look));
this->updateViews();
}
/*
Move eye specified distance along right
@param distance The number of units to move the camera
*/
void Camera::strafe(float distance){
eye += right*distance;
this->updateViews();
}
/*
Move eye specified distance along up
@param distance The number of units to move the camera
*/
void Camera::ascend(float distance){
eye += up*distance;
this->updateViews();
}
/*
Updates the view and skyboxView matrices
Called whenever any internal camera variables are updated
*/
void Camera::updateViews(){
viewMat = glm::lookAt(eye, eye + look, up);
skyboxViewMat = glm::lookAt(glm::vec3(0), look, up);
}
/*
Returns the projection matrix
For use with shader uniforms or glLoadMatrixf() after calling glMatrixMode(GL_MODELVIEW)
@return the modelview matrix as calculated by glm::lookAt(glm::vec3, glm::vec3, glm::vec3)
*/
glm::mat4 Camera::view() const{
return viewMat;
}
/*
Calls gluLookAt()
For people using fixed function pipeline
@see view()
*/
void Camera::gluLookAt(){
GL_CALL(::gluLookAt(
eye.x, eye.y, eye.z,
eye.x + look.x, eye.y + look.y, eye.z + look.z,
up.x, up.y, up.z
));
}
/*
Returns the projection matrix from the perspective required for rendering a skybox (direction only)
For use with shader uniforms or glLoadMatrixf() after calling glMatrixMode(GL_MODELVIEW)
@return the modelview matrix as calculated by glm::lookAt(glm::vec3, glm::vec3, glm::vec3)
*/
glm::mat4 Camera::skyboxView() const{
return skyboxViewMat;
}
/*
Calls gluLookAt() from the perspective required for rendering a skybox (direction only)
For people using fixed function pipeline, although manually setting the matrix with glLoadMatrixf() also works.
@see skyboxView()
*/
void Camera::skyboxGluLookAt() const
{
GL_CALL(::gluLookAt(
0, 0, 0,
look.x, look.y, look.z,
up.x, up.y, up.z
));
}
/*
Returns the cameras location
@return The location of the camera in world space
*/
glm::vec3 Camera::getEye() const{
return eye;
}
/*
Returns the cameras normalized direction vector
@return The normalized direction of the camera
*/
glm::vec3 Camera::getLook() const{
return look;
}
/*
Returns the cameras normalized up vector
@return The normalized direction the camera treats as upwards
*/
glm::vec3 Camera::getUp() const{
return up;
}
/*
Returns the value of pureUp
This value is used by the stabilisation to prevent the camera rolling unintentionally
@return The normalized direction the camera treats as the true up
*/
glm::vec3 Camera::getPureUp() const{
return pureUp;
}
/*
Returns the cameras normalized right vector
@return The normalized direction the camera treats as rightwards
*/
glm::vec3 Camera::getRight() const{
return right;
}
/*
Sets whether the camera should be stabilised
When the camera is stabilised, the up vector will not rotate about the cameras direction
When the camera is not stabilsed, moving the mouse in a circular motion may cause the camera to roll
@param stabilise Whether the camera should be stabilised
*/
void Camera::setStabilise(bool stabilise){
this->stabilise = stabilise;
}
/*
Returns a constant pointer to the cameras modelview matrix
This pointer can be used to continuously track the modelview matrix
@return A pointer to the modelview matrix
*/
const glm::mat4 *Camera::getViewMatPtr() const{
return &viewMat;
}
/*
Returns a constant pointer to the cameras skybox modelview matrix
This pointer can be used to continuously track the skybox modelview matrix
@return A pointer to the modelview matrix
*/
const glm::mat4 *Camera::getSkyboxViewMatPtr() const{
return &skyboxViewMat;
}
#ifndef __Camera_h__
#define __Camera_h__
#include "GLcheck.h"
#include <glm/glm.hpp>
class Camera
{
public:
Camera();
Camera(glm::vec3 eye);
Camera(glm::vec3 eye, glm::vec3 target);
~Camera();
void turn(float thetaInc, float phiInc);
void move(float distance);
void strafe(float distance);
void ascend(float distance);
void roll(float distance);
void setStabilise(bool stabilise);
glm::mat4 view() const;
void gluLookAt();
glm::mat4 skyboxView() const;
void skyboxGluLookAt() const;
glm::vec3 getEye() const;
glm::vec3 getLook() const;
glm::vec3 getUp() const;
glm::vec3 getPureUp() const;
glm::vec3 getRight() const;
const glm::mat4 *Camera::getViewMatPtr() const;
const glm::mat4 *Camera::getSkyboxViewMatPtr() const;
private:
void updateViews();
//ModelView matrix
glm::mat4 viewMat;
//Model view matrix without camera position taken into consideration
glm::mat4 skyboxViewMat;
//Up vector used for stabilisation, only rotated when roll is called
glm::vec3 pureUp;
//Eyelocation
glm::vec3 eye;
//3 perpendicular vectors which represent the cameras direction and orientation
glm::vec3 look;
glm::vec3 right;
glm::vec3 up;
bool stabilise;
};
#endif //ifndef __Camera_h__
#ifndef __GLcheck_h__
#define __GLcheck_h__
#include <GL/glew.h>
#include <stdio.h>
#include <stdlib.h>
//Define EXIT_ON_ERROR to cause the program to exit when a GL error occurs
#ifdef _DEBUG //VS standard debug flag
inline static void HandleGLError(const char *file, int line) {
GLuint error = glGetError();
if (error != GL_NO_ERROR)
{
printf("%s(%i) GL Error Occurred;\n%s\n", file, line, gluErrorString(error));
#if EXIT_ON_ERROR
getchar();
exit(1);
#endif
}
}
#define GL_CALL( err ) err ;HandleGLError(__FILE__, __LINE__)
#define GL_CHECK() (HandleGLError(__FILE__, __LINE__))
#else //ifdef _DEBUG
//Remove the checks when running release mode.
#define GL_CALL( err ) err
#define GL_CHECK()
#endif //ifdef _DEBUG
inline static void InitGlew() {
//https://www.opengl.org/wiki/OpenGL_Loading_Library#GLEW_.28OpenGL_Extension_Wrangler.29
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (GLEW_OK != err)
{
fprintf(stderr, "Error: %s\n", (char *)glewGetErrorString(err));
getchar();
exit(1);
}
}
#define GLEW_INIT() (InitGlew())
#endif //ifndef __GLcheck_h__
/**
* Example usage based on the SDL implementation from sdl_exp
* Full example available from https://github.com/Robadob/sdl_exp
**/
#define FOVY 60.0f
#define NEAR_CLIP 0.001f
#define FAR_CLIP 500.0f
#define DELTA_THETA_PHI 0.01f
#define MOUSE_SPEED 0.001f
#define SHIFT_MULTIPLIER 5.0f
#define MOUSE_SPEED_FPS 0.05f
#define DELTA_MOVE 0.1f
#define DELTA_STRAFE 0.1f
#define DELTA_ASCEND 0.1f
#define DELTA_ROLL 0.01f
#define ONE_SECOND_MS 1000
/*
Renders a single frame
@note Also handle user inputs
*/
void Visualisation::renderLoop()
{
// Handle continuous key presses (movement)
const Uint8 *state = SDL_GetKeyboardState(NULL);
float turboMultiplier = state[SDL_SCANCODE_LSHIFT] ? SHIFT_MULTIPLIER : 1.0f;
if (state[SDL_SCANCODE_W]) {
this->camera.move(DELTA_MOVE*turboMultiplier);
}
if (state[SDL_SCANCODE_A]) {
this->camera.strafe(-DELTA_STRAFE*turboMultiplier);
}
if (state[SDL_SCANCODE_S]) {
this->camera.move(-DELTA_MOVE*turboMultiplier);
}
if (state[SDL_SCANCODE_D]) {
this->camera.strafe(DELTA_STRAFE*turboMultiplier);
}
if (state[SDL_SCANCODE_Q]) {
this->camera.roll(-DELTA_ROLL);
}
if (state[SDL_SCANCODE_E]) {
this->camera.roll(DELTA_ROLL);
}
if (state[SDL_SCANCODE_SPACE]) {
this->camera.ascend(DELTA_ASCEND*turboMultiplier);
}
if (state[SDL_SCANCODE_LCTRL]) {
this->camera.ascend(-DELTA_ASCEND*turboMultiplier);
}
//Static fn var for tracking the time to send to scene->update()
static unsigned int updateTime = 0;
SDL_Event e;
// handle each event on the queue
while (SDL_PollEvent(&e) != 0){
switch (e.type){
case SDL_MOUSEMOTION:
this->handleMouseMove(e.motion.xrel, e.motion.yrel);
break;
}
}
/*
Moves the camera according to the motion of the mouse (whilst the mouse is attatched to the window via toggleMouseMode())
@param x The horizontal distance moved
@param y The vertical distance moved
@note This is called within the render loop
*/
void Visualisation::handleMouseMove(int x, int y){
if (SDL_GetRelativeMouseMode()){
this->camera.turn(x * MOUSE_SPEED, y * MOUSE_SPEED);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment