diff --git a/inferno/src/inferno/render/camera.cpp b/inferno/src/inferno/render/camera.cpp new file mode 100644 index 0000000..9e102e9 --- /dev/null +++ b/inferno/src/inferno/render/camera.cpp @@ -0,0 +1,211 @@ +#include // std::make_shared + +#include // glm::perspective, glm::ortho +#include // glm::radians +#include // glm::lookAt, glm::perspective + +#include "inferno/application.h" +#include "inferno/component/transform.h" +#include "inferno/input.h" +#include "inferno/inputcodes.h" +#include "inferno/render/camera.h" +#include "inferno/window.h" + +namespace Inferno { + + Camera::Camera() + : Camera({0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}) + { + } + + Camera::Camera(glm::vec3 translate, glm::vec3 rotate) + { + glm::vec3 scale = {1.0f, 1.0f, 1.0f}; + m_transform = std::make_shared(translate, rotate, scale); + } + +// ---------------------------------------- + + OrthographicCamera::OrthographicCamera() + : Camera({0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}) + { + initialize(); + } + + OrthographicCamera::OrthographicCamera(glm::vec3 translate, glm::vec3 rotate) + : Camera(translate, rotate) + { + initialize(); + } + + void OrthographicCamera::initialize() + { + // Set the roatation axis + m_rotate = glm::vec3(0.0f, 0.0f, 1.0f); + + dbg(Log::Info) << "OrthographicCamera initialized"; + } + + void OrthographicCamera::update(float deltaTime) + { + // Update camera rotation + + float cameraRotateSpeed = ROTATE_SPEED * deltaTime; + + glm::vec3 rotate = transform()->rotate(); + if (Input::isKeyPressed(KeyCode("GLFW_KEY_Q"))) { + rotate.z += cameraRotateSpeed; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_E"))) { + rotate.z -= cameraRotateSpeed; + } + + if (rotate.z > 180.0f) + rotate.z -= 360.0f; + else if (rotate.z <= -180.0f) + rotate.z += 360.0f; + + transform()->setRotate(rotate); + + // Update camera translation + + float cameraTranslateSpeed = TRANSLATE_SPEED * deltaTime; + + glm::vec3 translate = transform()->translate(); + // WASD movement + if (Input::isKeyPressed(KeyCode("GLFW_KEY_W"))) { + translate.x += -sin(glm::radians(rotate.z)) * cameraTranslateSpeed; + translate.y += cos(glm::radians(rotate.z)) * cameraTranslateSpeed; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_S"))) { + translate.x -= -sin(glm::radians(rotate.z)) * cameraTranslateSpeed; + translate.y -= cos(glm::radians(rotate.z)) * cameraTranslateSpeed; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_A"))) { + translate.x -= cos(glm::radians(rotate.z)) * cameraTranslateSpeed; + translate.y -= sin(glm::radians(rotate.z)) * cameraTranslateSpeed; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_D"))) { + translate.x += cos(glm::radians(rotate.z)) * cameraTranslateSpeed; + translate.y += sin(glm::radians(rotate.z)) * cameraTranslateSpeed; + } + + transform()->setTranslate(translate); + + // Update camera matrix + + // Local space -> World space: model matrix + // Is done in Object::update() + + // World space -> View space: view matrix + transform()->setTransform(glm::translate(glm::mat4(1.0f), translate) * glm::rotate(glm::mat4(1.0f), glm::radians(rotate.z), m_rotate)); + transform()->setTransform(glm::inverse(transform()->transform())); + + // View space -> Clip space: projection matrix + float aspectRatio = Application::get().getWindow().getAspect(); + setProjection(glm::ortho(-aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f)); + + // Clip space -> Screen space: viewport transform + // Is done in the fragment shader using the settings of glViewport + } + +// ---------------------------------------- + + PerspectiveCamera::PerspectiveCamera() + : Camera({0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, -1.0f}) + { + initialize(); + } + + PerspectiveCamera::PerspectiveCamera(glm::vec3 translate, glm::vec3 rotate) + : Camera(translate, rotate) + { + initialize(); + } + + void PerspectiveCamera::initialize() + { + m_fov = 90.0f; + m_pitch = 0.0f; + m_yaw = -90.0f; + + // Up vector in world space + m_up = glm::vec3(0.0f, 1.0f, 0.0f); + + dbg(Log::Info) << "PerspectiveCamera initialized"; + } + + void PerspectiveCamera::update(float deltaTime) + { + // Get mouse movement offset compared to last frame + float xOffset = Input::getXOffset() * SENSITIVITY; + float yOffset = Input::getYOffset() * SENSITIVITY; + m_yaw += xOffset; + m_pitch += yOffset; + + // Prevent gimbal lock + if (m_pitch > 89.0f) m_pitch = 89.0f; + if (m_pitch < -89.0f) m_pitch = -89.0f; + + // Update camera rotation, by calculating direction vector via yaw and pitch + + transform()->setRotate( + cos(glm::radians(m_pitch)) * cos(glm::radians(m_yaw)), + sin(glm::radians(m_pitch)), + cos(glm::radians(m_pitch)) * sin(glm::radians(m_yaw))); + transform()->setRotate(glm::normalize(transform()->rotate())); + // The direction vector is based on + // Camera direction (z): normalize(position - target) + // Right axis (x): normalize(cross(up, direction)) + // Up axis (y): cross(direction, right) + + // Source: https://learnopengl.com/img/getting-started/camera_axes.png + + // Cross = combination of two vectors in 3D space, + // where result is always perpendicular to both of the vectors + + // Update camera translation + + float cameraSpeed = TRANSLATE_SPEED * deltaTime; + // WASD movement + if (Input::isKeyPressed(KeyCode("GLFW_KEY_W"))) { + transform()->setTranslate(transform()->translate() + cameraSpeed * transform()->rotate()); + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_S"))) { + transform()->setTranslate(transform()->translate() - cameraSpeed * transform()->rotate()); + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_A"))) { + transform()->setTranslate(transform()->translate() - + glm::normalize(glm::cross(transform()->rotate(), m_up)) * cameraSpeed); + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_D"))) { + transform()->setTranslate(transform()->translate() + + glm::normalize(glm::cross(transform()->rotate(), m_up)) * cameraSpeed); + } + // Up / down movement + if (Input::isKeyPressed(KeyCode("GLFW_KEY_SPACE"))) { + transform()->setTranslate(transform()->translate().x, transform()->translate().y + cameraSpeed, transform()->translate().z); + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_LEFT_SHIFT"))) { + transform()->setTranslate(transform()->translate().x, transform()->translate().y - cameraSpeed, transform()->translate().z); + } + + // Update camera matrix + + // Local space -> World space: model matrix + // Is done in Object::update() + + // World space -> View space: view matrix + transform()->setTransform(glm::lookAt(transform()->translate(), transform()->translate() + transform()->rotate(), m_up)); + + // View space -> Clip space: projection matrix + float aspect = Application::get().getWindow().getAspect(); + setProjection(glm::perspective(glm::radians(m_fov), aspect, NEAR_PLANE, FAR_PLANE)); + + // Clip space -> Screen space: viewport transform + // Is done in the fragment shader using the settings of glViewport + + // Souce: https://learnopengl.com/img/getting-started/coordinate_systems.png + } + +} diff --git a/inferno/src/inferno/render/camera.h b/inferno/src/inferno/render/camera.h new file mode 100644 index 0000000..c14cd32 --- /dev/null +++ b/inferno/src/inferno/render/camera.h @@ -0,0 +1,87 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "inferno/log.h" // @@@@@@@@@@ +#define TRANSLATE_SPEED 2.5f +#define ROTATE_SPEED 90.0f +#define SENSITIVITY 0.25f +#define NEAR_PLANE 0.1f +#define FAR_PLANE 100.0f + +#include // std::shared_ptr + +#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 +#include "glm/ext/vector_float3.hpp" // glm:vec3 + +namespace Inferno { + + class Transform; + + class Camera { + public: + Camera(); + Camera(glm::vec3 translate, glm::vec3 rotate); + virtual ~Camera() { dbg(Log::Danger) << "KILLED CAMERA"; } + + virtual void initialize() = 0; + virtual void update(float deltaTime) = 0; + // virtual void render() = 0; + // virtual void destroy() = 0; + + void setProjection(glm::mat4 projection) { m_projection = projection; } + + const glm::mat4& projection() const { return m_projection; } + const std::shared_ptr& transform() const { return m_transform; } + + private: + glm::mat4 m_projection; + std::shared_ptr m_transform; + }; + +// ---------------------------------------- + + class OrthographicCamera final : public Camera { + public: + OrthographicCamera(); + OrthographicCamera(glm::vec3 translate, glm::vec3 rotate); + virtual ~OrthographicCamera() { dbg(Log::Danger) << "KILLED ORTHOGRAPHICCAMERA"; } + + virtual void initialize() override; + virtual void update(float deltaTime) override; + // virtual void render() override; + // virtual void destroy() override; + + private: + glm::vec3 m_rotate; + }; + +// ---------------------------------------- + + class PerspectiveCamera final : public Camera { + public: + PerspectiveCamera(); + PerspectiveCamera(glm::vec3 translate, glm::vec3 rotate); + virtual ~PerspectiveCamera() { dbg(Log::Danger) << "KILLED PERSPECTIVECAMERA"; } + + virtual void initialize() override; + virtual void update(float deltaTime) override; + // virtual void render() override; + // virtual void destroy() override; + + private: + float m_fov; + float m_pitch; + float m_yaw; + glm::vec3 m_up; + }; + +// ---------------------------------------- + +} + +#endif // CAMERA_H + +// @Todo: +// - Add sensitivity, fov to settings:camera +// - Add zoom to ortho camera +// - Change ortho view matrix to use glm::lookAt()