Skip to content

Commit

Permalink
Implement free camera movement. (#1)
Browse files Browse the repository at this point in the history
Implements the following:
* Incremental yaw, pitch, roll.
* Movement via relative directions.
* Support for user control.

Also cleans up the camera functionality.
  • Loading branch information
adityaruplaha committed Mar 19, 2020
1 parent 9f9be69 commit 446e2da
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 85 deletions.
130 changes: 86 additions & 44 deletions src/Engine/entities/camera.cpp
Expand Up @@ -4,8 +4,34 @@

Camera* Camera::current_camera{};

// Helper function prototype
bool operator == (CameraOrientation& a, CameraOrientation& b);
/// HELPER STUFF

glm::vec3 AxisData::front() const
{
return std::move(glm::normalize(glm::cross(up, right)));
}

glm::vec3 CameraOrientation::target() const
{
return std::move(position + focal_dist * axes.front());
}

// These are linked statically to prevent external use. May be exposed later.

static bool operator == (AxisData& a, AxisData& b)
{
return (a.right == b.right) &&
(a.up == b.up);
}

static bool operator == (CameraOrientation& a, CameraOrientation& b)
{
return (a.position == b.position) &&
(a.axes == b.axes) &&
(a.focal_dist == b.focal_dist);
}

/// Camera class methods

Camera::Camera(CameraOrientation& o) : orientation(std::move(o)) {}

Expand All @@ -19,55 +45,52 @@ void Camera::stop() {
flush();
}

void Camera::setDirection(float yaw, float pitch)
void Camera::ypr(float yaw, float pitch, float roll)
{
orientation.direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));
orientation.direction.y = sin(glm::radians(pitch));
orientation.direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
orientation.direction *= -1;
// Roll
auto front = orientation.axes.front();
auto roll_mat = glm::rotate(glm::mat4(1.0f), glm::radians(roll), front);
orientation.axes.right = roll_mat * orientation.axes.right;
orientation.axes.up = roll_mat * orientation.axes.up;
// Yaw
orientation.axes.right = glm::rotate(glm::mat4(1.0f), glm::radians(yaw), orientation.axes.up) * orientation.axes.right;
// Pitch
orientation.axes.up = glm::rotate(glm::mat4(1.0f), glm::radians(pitch), orientation.axes.right) * orientation.axes.up;
}

void Camera::setDirection(glm::vec3& target)
void Camera::lookAt(glm::vec3& target)
{
orientation.direction = glm::normalize(target - orientation.position);
// Get previous roll value in radians
auto prev_direction = orientation.axes.front();
auto fk_right = glm::normalize(glm::cross(prev_direction, Directions::UP));
float roll = acos(glm::dot(fk_right, orientation.axes.right));

// Get the direction right
auto direction = glm::normalize(target - orientation.position);
orientation.axes.right = glm::normalize(glm::cross(direction, Directions::UP));
orientation.axes.up = glm::normalize(glm::cross(orientation.axes.right, direction));

// Roll
auto front = orientation.axes.front();
auto roll_mat = glm::rotate(glm::mat4(1.0f), roll, front);
orientation.axes.right = roll_mat * orientation.axes.right;
orientation.axes.up = roll_mat * orientation.axes.up;
}

void Camera::setPosition(glm::vec3& pos)
void Camera::recompute()
{
orientation.position = pos;
}

void Camera::setRoll(float roll)
{
orientation.roll = roll;
}

void Camera::recompute_if_dirty()
{
if (orientation == previous_orientation)
{
return;
}
recalculateAxes();
viewMatrix = glm::lookAt(orientation.position, orientation.target(), axes.up);
previous_orientation = orientation;
viewMatrix = glm::lookAt(orientation.position, orientation.target(), orientation.axes.up);
}

void Camera::flush()
{
if (current_camera)
{
current_camera->recompute_if_dirty();
current_camera->recompute();
}
setViewMatrix(current_camera);
}

void Camera::recalculateAxes()
{
axes.right = glm::normalize(glm::cross(Directions::UP, -orientation.direction));
axes.up = glm::normalize(glm::cross(-orientation.direction, axes.right));
}

void Camera::setViewMatrix(Camera* c)
{
glm::mat4 mat(1.0f);
Expand All @@ -78,17 +101,36 @@ void Camera::setViewMatrix(Camera* c)
Program::current_program->setMat4(Program::current_program->metadata.view_mat_name, mat);
}

// HELPER STUFF
/// Camera movement methods

glm::vec3 CameraOrientation::target()
{
return std::move(position + focal_dist * direction);
void Camera::rmoveFD(float amt) {
orientation.position += amt * orientation.axes.front();
}

bool operator == (CameraOrientation& a, CameraOrientation& b)
{
return (a.position == b.position) &&
(a.direction == b.direction) &&
(a.roll == b.roll) &&
(a.focal_dist == b.focal_dist);
void Camera::rmoveBK(float amt) {
orientation.position += amt * orientation.axes.front();
}

void Camera::rmoveRT(float amt) {
orientation.position += amt * orientation.axes.right;
}

void Camera::rmoveLT(float amt) {
orientation.position += amt * orientation.axes.right;
}

void Camera::rmoveUP(float amt) {
orientation.position += amt * orientation.axes.up;
}

void Camera::rmoveDN(float amt) {
orientation.position += amt * orientation.axes.up;
}

void Camera::rturnLT(float amt) {
ypr(amt, 0, 0);
}

void Camera::rturnRT(float amt) {
ypr(amt, 0, 0);
}
59 changes: 39 additions & 20 deletions src/Engine/entities/camera.h
Expand Up @@ -3,22 +3,22 @@
#include "../window.h"
#include <GLCore/base/gl_math_defs.h>

// Roll is not implemented.
struct AxisData
{
glm::vec3 up;
glm::vec3 right;

glm::vec3 front() const;
};

struct CameraOrientation
{
glm::vec3 position;
glm::vec3 direction;
float roll;
AxisData axes;

float focal_dist;

glm::vec3 target();
};

struct AxisData
{
glm::vec3 up;
glm::vec3 right;
glm::vec3 target() const;
};

class Camera {
Expand All @@ -28,27 +28,46 @@ class Camera {
void stop();

CameraOrientation orientation;
void setDirection(float yaw, float pitch);
void setDirection(glm::vec3& target);
void setPosition(glm::vec3& pos);
void setRoll(float roll);

// Yaw, pitch by a certain amount in degrees. Incremental.
// +ve yaw: towards left of axes.front()
// OR anticlockwise along axes.up (right hand thumb rule)
// +ve pitch: upwards ( axes.up --> see Camera::axes)
// OR anticlockwise along axes.right (right hand thumb rule)
// +ve roll: Anticlockwise along orientation.direction (right hand thumb rule)
void ypr(float yaw, float pitch, float roll);
// Set a target to look at.
void lookAt(glm::vec3& target);

// Mostly to be used by flush() but can be used manually.
void recompute_if_dirty();
void recompute();

static void flush();
static Camera* current_camera;

private:
AxisData axes;
void recalculateAxes();
// Move methods
void rmoveFD(float amt = FDBK);
void rmoveBK(float amt = -FDBK);
void rmoveRT(float amt = LTRT);
void rmoveLT(float amt = -LTRT);
void rmoveUP(float amt = UPDN);
void rmoveDN(float amt = -UPDN);
void rturnRT(float amt = YAWM);
void rturnLT(float amt = -YAWM);

private:
glm::mat4 viewMatrix;

static void setViewMatrix(Camera* c);

// Contains the previous orientation
CameraOrientation previous_orientation;
/// Default movement weights

constexpr static float UPDN = 0.05f;
constexpr static float LTRT = 0.075f;
constexpr static float FDBK = 0.075f;

// This should always be -ve for directions to make sense.
constexpr static float YAWM = -1.0f;
};

namespace Directions {
Expand Down
16 changes: 12 additions & 4 deletions src/Engine/input_handler.h
Expand Up @@ -7,16 +7,24 @@
class InputHandler {

public:
InputHandler(Window* window);
void attach(Window* window);
void attach(Window* wndow);
virtual void listen() = 0;

protected:
Window* attached_window;
};

class KeyboardInputHandler : InputHandler {

public:
KeyboardInputHandler(Window* window);
void connect(int key, std::function<void ()> func);
void connect(std::vector<int> keys, std::function<void ()> func);
void disconnect(int key);
void disconnect(std::vector<int> keys);
void listen();
void listen() override;

private:
Window* attached_window;
std::vector<std::pair<int, std::function<void()>>> binds;
std::vector<std::pair<std::vector<int>, std::function<void()>>> binds_m;
};
20 changes: 11 additions & 9 deletions src/Engine/input_handler.cpp → src/Engine/kbd_input_handler.cpp
@@ -1,29 +1,31 @@
#include <algorithm>
#include "input_handler.h"

InputHandler::InputHandler(Window* window)
void InputHandler::attach(Window* window)
{
attach(window);
attached_window = window;
}

void InputHandler::attach(Window* window)
/// Specific stuff

KeyboardInputHandler::KeyboardInputHandler(Window* window)
{
attached_window = window;
this->attach(window);
}

void InputHandler::connect(int key, std::function<void()> func)
void KeyboardInputHandler::connect(int key, std::function<void()> func)
{
auto pair = std::make_pair(key, func);
binds.push_back(pair);
}

void InputHandler::connect(std::vector<int> keys, std::function<void()> func)
void KeyboardInputHandler::connect(std::vector<int> keys, std::function<void()> func)
{
auto pair = std::make_pair(keys, func);
binds_m.push_back(pair);
}

void InputHandler::disconnect(int key)
void KeyboardInputHandler::disconnect(int key)
{
for (int i = 0; i < binds.size(); i++)
{
Expand All @@ -34,7 +36,7 @@ void InputHandler::disconnect(int key)
}
}

void InputHandler::disconnect(std::vector<int> keys)
void KeyboardInputHandler::disconnect(std::vector<int> keys)
{
for (int i = 0; i < binds_m.size(); i++)
{
Expand All @@ -45,7 +47,7 @@ void InputHandler::disconnect(std::vector<int> keys)
}
}

void InputHandler::listen()
void KeyboardInputHandler::listen()
{
auto* ptr = attached_window->getWindowPointer();
for (auto b : binds)
Expand Down
26 changes: 18 additions & 8 deletions src/Engine/main.cpp
Expand Up @@ -22,7 +22,7 @@ int main()

auto *window = new Window(600, 600, "OpenGL Engine " + version, {45.0f, 0.1f, 100.0f});
Initializer::GLAD_Init();
auto *handler = new InputHandler(window);
auto *handler = new KeyboardInputHandler(window);

handler->connect(GLFW_KEY_ESCAPE, [&window]() { window->close(); });

Expand Down Expand Up @@ -54,26 +54,36 @@ int main()
prog->setInt("texture0", *container);
prog->setInt("texture1", *smile);

CameraOrientation o{glm::vec3(0, 0, 3), Directions::INTO_SCREEN, 0.0f, 1.0f};
CameraOrientation o{glm::vec3(0, 0, 2), {Directions::UP, Directions::RIGHT}, 1.0f};

auto *cam = new Camera(o);
cam->start();

auto mix_ratio = PingPongMap(0, 1, 0.5);
handler->connect(GLFW_KEY_W, [&cam]() { cam->rmoveFD(); });
handler->connect(GLFW_KEY_S, [&cam]() { cam->rmoveBK(); });
handler->connect(GLFW_KEY_Q, [&cam]() { cam->rmoveLT(); });
handler->connect(GLFW_KEY_E, [&cam]() { cam->rmoveRT(); });

handler->connect(GLFW_KEY_A, [&cam]() { cam->rturnLT(); });
handler->connect(GLFW_KEY_D, [&cam]() { cam->rturnRT(); });

auto camera_y = SinMap(0.3, 8, 0.4);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

prog->setFloat("ratio", 0.7f);

//cam->ypr(0, 0, 10.0f);

while (!window->should_close())
{
Window::clear();
handler->listen();

prog->setFloat("ratio", mix_ratio);

instance->orientation.position.z = mix_ratio;
instance->orientation.rotation.x -= 0.7f;
instance->orientation.rotation.y += 0.57f;
instance->render();

//cam->orientation.position.y = camera_y;
//cam->lookAt(instance->orientation.position);
//cam->ypr(0,0, 10);
Camera::flush();
window->buffer_swap();
}
Expand Down

0 comments on commit 446e2da

Please sign in to comment.