diff --git a/inferno/src/inferno/systems/camera.cpp b/inferno/src/inferno/systems/camera.cpp new file mode 100644 index 0000000..d9d303f --- /dev/null +++ b/inferno/src/inferno/systems/camera.cpp @@ -0,0 +1,125 @@ +#include // glm::perspective, glm::ortho +#include // glm::radians, glm::lookAt + +#include "inferno/application.h" +#include "inferno/assertions.h" +#include "inferno/input.h" +#include "inferno/inputcodes.h" +#include "inferno/log.h" +#include "inferno/scene/entity.h" +#include "inferno/systems/camera.h" +#include "inferno/window.h" + +namespace Inferno { + + CameraSystem* CameraSystem::s_instance = nullptr; + + void CameraSystem::initialize() + { + ASSERT(!s_instance, "CameraSystem already exists!"); + s_instance = this; + + dbg(Log::Info) << "CameraSystem initialized"; + } + + void CameraSystem::update() + { + auto orthoView = m_registry->view(); + + for(auto&& [entity, transform, orthographic] : orthoView.each()) { + updateOrthographic(transform, orthographic); + } + + auto perspectiveView = m_registry->view(); + + for(auto&& [entity, transform, perspective] : perspectiveView.each()) { + updatePerspective(transform, perspective); + } + } + + void CameraSystem::destroy() + { + delete s_instance; + s_instance = nullptr; + } + + void CameraSystem::updateOrthographic(TransformComponent& transform, OrthographicCameraComponment& orthographic) + { + + } + + void CameraSystem::updatePerspective(TransformComponent& transform, PerspectiveCameraComponent& perspective) + { + // Get mouse movement offset compared to last frame + float xOffset = Input::getXOffset() * MOUSE_SENSITIVITY; + float yOffset = Input::getYOffset() * MOUSE_SENSITIVITY; + perspective.yaw += xOffset; + perspective.pitch += yOffset; + + // Prevent gimbal lock + if (perspective.pitch > 89.0f) perspective.pitch = 89.0f; + if (perspective.pitch < -89.0f) perspective.pitch = -89.0f; + + // Update camera rotation, by calculating direction vector via yaw and pitch + + transform.rotate = { + cos(glm::radians(perspective.pitch)) * cos(glm::radians(perspective.yaw)), + sin(glm::radians(perspective.pitch)), + cos(glm::radians(perspective.pitch)) * sin(glm::radians(perspective.yaw)) + }; + transform.rotate = 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 * (1.0f / 60.0f); + // WASD movement + if (Input::isKeyPressed(KeyCode("GLFW_KEY_W"))) { + transform.translate = { transform.translate + cameraSpeed * transform.rotate }; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_S"))) { + transform.translate = { transform.translate - cameraSpeed * transform.rotate }; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_A"))) { + transform.translate = { transform.translate - + glm::normalize(glm::cross(transform.rotate, perspective.up)) * cameraSpeed }; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_D"))) { + transform.translate = { transform.translate + + glm::normalize(glm::cross(transform.rotate, perspective.up)) * cameraSpeed }; + } + // Up / down movement + if (Input::isKeyPressed(KeyCode("GLFW_KEY_SPACE"))) { + transform.translate = { transform.translate.x, transform.translate.y + cameraSpeed, transform.translate.z }; + } + if (Input::isKeyPressed(KeyCode("GLFW_KEY_LEFT_SHIFT"))) { + transform.translate = { 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.transform = { glm::lookAt(transform.translate, transform.translate + transform.rotate, perspective.up) }; + + // View space -> Clip space: projection matrix + float aspect = Application::the().getWindow().getAspect(); + perspective.projection = { glm::perspective(glm::radians(perspective.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/systems/camera.h b/inferno/src/inferno/systems/camera.h new file mode 100644 index 0000000..6303c32 --- /dev/null +++ b/inferno/src/inferno/systems/camera.h @@ -0,0 +1,42 @@ +#ifndef CAMERA_SYSTEM_H +#define CAMERA_SYSTEM_H + +#define TRANSLATE_SPEED 2.5f +#define ROTATE_SPEED 90.0f +#define ZOOM_SENSITIVITY 2.5f +#define MOUSE_SENSITIVITY 0.25f +#define NEAR_PLANE 0.1f +#define FAR_PLANE 100.0f + +#include //std::shared_ptr + +#include "entt/entity/registry.hpp" // entt::entity, entt::registry + +#include "inferno/scene/components.h" + +namespace Inferno { + + class Entity; + + class CameraSystem { + public: + void initialize(); + void update(); + void destroy(); + + void setRegistry(const std::shared_ptr& registry) { m_registry = registry; }; + + static inline CameraSystem& the() { return *s_instance; } + + private: + void updateOrthographic(TransformComponent& transform, OrthographicCameraComponment& orthographic); + void updatePerspective(TransformComponent& transform, PerspectiveCameraComponent& perspective); + + std::shared_ptr m_registry; + + static CameraSystem* s_instance; + }; + +} + +#endif // CAMERA_SYSTEM_H diff --git a/inferno/src/inferno/systems/transform.cpp b/inferno/src/inferno/systems/transform.cpp new file mode 100644 index 0000000..b310a14 --- /dev/null +++ b/inferno/src/inferno/systems/transform.cpp @@ -0,0 +1,50 @@ +#include // glm::translate, glm::rotate, glm::scale, glm::radians + +#include "inferno/assertions.h" +#include "inferno/log.h" +#include "inferno/scene/components.h" +#include "inferno/systems/transform.h" + +namespace Inferno { + + TransformSystem* TransformSystem::s_instance = nullptr; + + void TransformSystem::initialize() + { + ASSERT(!s_instance, "TransformSystem already exists!"); + s_instance = this; + + dbg(Log::Info) << "TransformSystem initialized"; + } + + void TransformSystem::update() + { + auto view = m_registry->view(); + + for (auto entity : view) { + + auto& component = view.get(entity); + + // Identity matrix + component.transform = glm::mat4(1.0f); + + // Translate + component.transform = glm::translate(component.transform, component.translate); + + // Rotate + component.transform = glm::rotate(component.transform, glm::radians(component.rotate.x), {1.0, 0.0, 0.0}); + component.transform = glm::rotate(component.transform, glm::radians(component.rotate.y), {0.0, 1.0, 0.0}); + component.transform = glm::rotate(component.transform, glm::radians(component.rotate.z), {0.0, 0.0, 1.0}); + + // Scale + component.transform = glm::scale(component.transform, component.scale); + } + } + + void TransformSystem::destroy() + { + delete s_instance; + s_instance = nullptr; + } + +} diff --git a/inferno/src/inferno/systems/transform.h b/inferno/src/inferno/systems/transform.h new file mode 100644 index 0000000..4cabaeb --- /dev/null +++ b/inferno/src/inferno/systems/transform.h @@ -0,0 +1,28 @@ +#ifndef TRANSFORM_SYSTEM_H +#define TRANSFORM_SYSTEM_H + +#include //std::shared_ptr + +#include "entt/entity/registry.hpp" // entt::entity, entt::registry + +namespace Inferno { + + class TransformSystem { + public: + void initialize(); + void update(); + void destroy(); + + void setRegistry(const std::shared_ptr& registry) { m_registry = registry; }; + + static inline TransformSystem& the() { return *s_instance; } + + private: + std::shared_ptr m_registry; + + static TransformSystem* s_instance; + }; + +} + +#endif // TRANSFORM_SYSTEM_H