diff --git a/demo/main.cpp b/demo/main.cpp index 4a9c9038b01a2aa761169eed71c4427c65511795..55ffb9f4f535871fdb3834b697a48d0f90f15d6e 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -4,6 +4,7 @@ #include <utility> #include <fggl/gfx/window.hpp> #include <fggl/gfx/camera.hpp> +#include <fggl/input/camera_input.h> #include <fggl/gfx/compat.hpp> #include <fggl/gfx/ogl/compat.hpp> @@ -20,10 +21,6 @@ constexpr bool showNormals = false; -template <typename T> int sgn(T val) { - return (T(0) < val) - (val < T(0)); -} - // prototype of resource discovery void discover(const std::filesystem::path& base) { @@ -55,160 +52,6 @@ camera_type cam_mode = cam_free; using namespace fggl::input; using InputManager = std::shared_ptr<fggl::input::Input>; -void process_arcball(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::entity_t cam) { - // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ - auto* camTransform = ecs.get<fggl::math::Transform>(cam); - auto* camComp = ecs.get<fggl::gfx::Camera>(cam); - auto& mouse = input->mouse; - - glm::vec4 position(camTransform->origin(), 1.0f); - glm::vec4 pivot(camComp->target, 1.0f); - glm::mat4 view = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() ); - glm::vec3 viewDir = -glm::transpose(view)[2]; - glm::vec3 rightDir = glm::transpose(view)[0]; - - float deltaAngleX = ( 2 * M_PI ); - float deltaAngleY = ( M_PI ); - float xAngle = ( -mouse.axisDelta(fggl::input::MouseAxis::X) ) * deltaAngleX; - float yAngle = ( -mouse.axisDelta(fggl::input::MouseAxis::Y) ) * deltaAngleY; - - auto cosAngle = glm::dot( viewDir, fggl::math::UP ); - if ( cosAngle * sgn(deltaAngleY) > 0.99f ) { - deltaAngleY = 0; - } - - // rotate the camera around the pivot on the first axis - glm::mat4x4 rotationMatrixX(1.0f); - rotationMatrixX = glm::rotate( rotationMatrixX, xAngle, fggl::math::UP ); - position = ( rotationMatrixX * ( position - pivot ) ) + pivot; - - // rotate the camera aroud the pivot on the second axis - glm::mat4x4 rotationMatrixY(1.0f); - rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir ); - glm::vec3 finalPos = ( rotationMatrixY * ( position - pivot ) ) + pivot; - - camTransform->origin( finalPos ); -} - -constexpr float ROT_SPEED = 0.05f; -constexpr float PAN_SPEED = 0.05f; -constexpr glm::mat4 MAT_IDENTITY(1.0f); - - -void process_freecam(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::entity_t cam) { - float rotationValue = 0.0f; - glm::vec3 translation(0.0f); - - auto& keyboard = input->keyboard; - auto code_q = glfwGetKeyScancode(GLFW_KEY_Q); - auto code_e = glfwGetKeyScancode(GLFW_KEY_E); - auto code_w = glfwGetKeyScancode(GLFW_KEY_W); - auto code_s = glfwGetKeyScancode(GLFW_KEY_S); - auto code_d = glfwGetKeyScancode(GLFW_KEY_D); - auto code_a = glfwGetKeyScancode(GLFW_KEY_A); - - - // calulate rotation (user input) - if ( keyboard.down( code_q ) ) { - rotationValue = ROT_SPEED; - } else if ( keyboard.down(code_e) ) { - rotationValue = -ROT_SPEED; - } - - // calulate movement (user input) - if ( keyboard.down(code_w) ) { - translation -= fggl::math::RIGHT; - } - - if ( keyboard.down(code_s) ) { - translation += fggl::math::RIGHT; - } - - if ( keyboard.down(code_d) ) { - translation += fggl::math::FORWARD; - } - - if ( keyboard.down(code_a) ) { - translation -= fggl::math::FORWARD; - } - - // apply rotation/movement - auto camTransform = ecs.get<fggl::math::Transform>(cam); - auto camComp = ecs.get<fggl::gfx::Camera>(cam); - - glm::vec4 position( camTransform->origin(), 1.0f ); - glm::vec4 pivot( camComp->target, 1.0f ); - - // apply movement - if ( translation != glm::vec3(0.0f) ) { - const auto rotation = (position - pivot); - const float angle = atan2( rotation.x, rotation.z ); - const auto rotationMat = glm::rotate( MAT_IDENTITY, angle, fggl::math::UP ); - - auto deltaMove = (rotationMat * glm::vec4( translation, 1.0f )) * PAN_SPEED; - deltaMove.w = 0.0f; - - position += deltaMove; - pivot += deltaMove; - } - - // apply rotation - if ( rotationValue != 0.0f ) { - glm::mat4 rotation = glm::rotate( MAT_IDENTITY, rotationValue, fggl::math::UP ); - position = ( rotation * ( position - pivot ) ) + pivot; - } - - camTransform->origin( position ); - camComp->target = pivot; -} - -void process_edgescroll(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::entity_t cam) { - glm::vec3 translation(0.0f); - - auto& mouse = input->mouse; - - // calulate movement (user input) - if ( mouse.axis( MouseAxis::Y ) < 0.9f ) { - translation -= fggl::math::RIGHT; - } - - if ( mouse.axis( MouseAxis::Y) > -0.9f ) { - translation += fggl::math::RIGHT; - } - - if ( mouse.axis( MouseAxis::X) > -0.9f ) { - translation += fggl::math::FORWARD; - } - - if ( mouse.axis( MouseAxis::X ) < 0.9f ) { - translation -= fggl::math::FORWARD; - } - - // apply rotation/movement - auto camTransform = ecs.get<fggl::math::Transform>(cam); - auto camComp = ecs.get<fggl::gfx::Camera>(cam); - - glm::vec4 position( camTransform->origin(), 1.0f ); - glm::vec4 pivot( camComp->target, 1.0f ); - - // apply movement - if ( translation != glm::vec3(0.0f) ) { - const auto rotation = (position - pivot); - const float angle = atan2( rotation.x, rotation.z ); - const auto rotationMat = glm::rotate( MAT_IDENTITY, angle, fggl::math::UP ); - - auto deltaMove = (rotationMat * glm::vec4( translation, 1.0f )) * PAN_SPEED; - deltaMove.w = 0.0f; - - position += deltaMove; - pivot += deltaMove; - } - - // move camera - camTransform->origin( position ); - camComp->target = pivot; -} - void process_camera(fggl::ecs3::World& ecs, InputManager input) { auto cameras = ecs.findMatching<fggl::gfx::Camera>(); @@ -225,18 +68,16 @@ void process_camera(fggl::ecs3::World& ecs, InputManager input) { float delta = input->mouse.axis( fggl::input::MouseAxis::SCROLL_Y ); if ( (glm::length( dir ) < 25.0f && delta < 0.0f) || (glm::length( dir ) > 2.5f && delta > 0.0f) ) motion -= (forward * delta); - camTransform->origin( camTransform->origin() + motion ); if ( cam_mode == cam_arcball || input->mouse.button( fggl::input::MouseButton::MIDDLE ) ) { - process_arcball(ecs, input, cam); + fggl::input::process_arcball(ecs, *input, cam); } else if ( cam_mode == cam_free ) { - process_freecam(ecs, input, cam); + fggl::input::process_freecam(ecs, *input, cam); } - process_edgescroll( ecs, input, cam ); + fggl::input::process_edgescroll( ecs, *input, cam ); } - class MenuScene : public fggl::scenes::Scene { public: @@ -280,9 +121,18 @@ public: auto prototype = m_world.create(false); m_world.add(prototype, types.find(fggl::math::Transform::name)); m_world.add(prototype, types.find(fggl::gfx::Camera::name)); + m_world.add(prototype, types.find(fggl::input::FreeCamKeys::name)); auto camTf = m_world.get<fggl::math::Transform>(prototype); camTf->origin( glm::vec3(0.0f, 3.0f, 3.0f) ); + + auto cameraKeys = m_world.get<fggl::input::FreeCamKeys>(prototype); + cameraKeys->forward = glfwGetKeyScancode(GLFW_KEY_W); + cameraKeys->backward = glfwGetKeyScancode(GLFW_KEY_S); + cameraKeys->left = glfwGetKeyScancode(GLFW_KEY_A); + cameraKeys->right = glfwGetKeyScancode(GLFW_KEY_D); + cameraKeys->rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q); + cameraKeys->rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); } // create building prototype diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index 155d5bd41d5aee4e6a85447ab64a543f9485c462..19de7ba3c24a5bd9d5b4ea8fff119608278d96b0 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -6,7 +6,10 @@ add_library(fggl fggl.cpp data/procedural.cpp ecs3/fast/Container.cpp ecs3/prototype/world.cpp - ecs3/module/module.cpp scenes/Scene.cpp scenes/Scene.h util/service.h) + scenes/Scene.cpp + ecs3/module/module.cpp + input/camera_input.cpp +) target_include_directories(fggl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) # Graphics backend diff --git a/fggl/gfx/ogl/compat.hpp b/fggl/gfx/ogl/compat.hpp index a29b7357072a368300861c7ad964d927405e525d..9c792ebac623b81355ff164e1bb9f9f85a58373e 100644 --- a/fggl/gfx/ogl/compat.hpp +++ b/fggl/gfx/ogl/compat.hpp @@ -19,6 +19,7 @@ #include <fggl/gfx/camera.hpp> #include <fggl/ecs/ecs.hpp> #include <utility> +#include <fggl/input/camera_input.h> namespace fggl::gfx { @@ -52,6 +53,9 @@ namespace fggl::gfx { types.make<fggl::gfx::StaticMesh>(); types.make<fggl::gfx::Camera>(); + // FIXME probably shouldn't be doing this... + types.make<fggl::input::FreeCamKeys>(); + // opengl types.make<fggl::gfx::GlRenderToken>(); diff --git a/fggl/input/camera_input.cpp b/fggl/input/camera_input.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdfffcdf65fa552eea6e36f47c028a26667d3caa --- /dev/null +++ b/fggl/input/camera_input.cpp @@ -0,0 +1,150 @@ +// +// Created by webpigeon on 20/11/2021. +// + +#include <fggl/ecs3/ecs.hpp> +#include <fggl/input/input.hpp> + +#include <fggl/gfx/camera.hpp> +#include <fggl/input/camera_input.h> + +namespace fggl::input { + + void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) { + // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ + auto *camTransform = ecs.get<fggl::math::Transform>(cam); + auto *camComp = ecs.get<fggl::gfx::Camera>(cam); + auto &mouse = input.mouse; + + glm::vec4 position(camTransform->origin(), 1.0f); + glm::vec4 pivot(camComp->target, 1.0f); + glm::mat4 view = glm::lookAt(camTransform->origin(), camComp->target, camTransform->up()); + glm::vec3 viewDir = -glm::transpose(view)[2]; + glm::vec3 rightDir = glm::transpose(view)[0]; + + float deltaAngleX = (2 * M_PI); + float deltaAngleY = (M_PI); + float xAngle = (-mouse.axisDelta(fggl::input::MouseAxis::X)) * deltaAngleX; + float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY; + + // rotate the camera around the pivot on the first axis + glm::mat4x4 rotationMatrixX(1.0f); + rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP); + position = (rotationMatrixX * (position - pivot)) + pivot; + + // rotate the camera aroud the pivot on the second axis + glm::mat4x4 rotationMatrixY(1.0f); + rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir); + glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot; + + camTransform->origin(finalPos); + } + + void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) { + float rotationValue = 0.0f; + glm::vec3 translation(0.0f); + + auto &keyboard = input.keyboard; + auto* settings = ecs.get<FreeCamKeys>(cam); + + // calculate rotation (user input) + if (keyboard.down(settings->rotate_cw)) { + rotationValue = ROT_SPEED; + } else if (keyboard.down(settings->rotate_ccw)) { + rotationValue = -ROT_SPEED; + } + + // calculate movement (user input) + if (keyboard.down(settings->forward)) { + translation -= fggl::math::RIGHT; + } + + if (keyboard.down(settings->backward)) { + translation += fggl::math::RIGHT; + } + + if (keyboard.down(settings->right)) { + translation += fggl::math::FORWARD; + } + + if (keyboard.down(settings->left)) { + translation -= fggl::math::FORWARD; + } + + // apply rotation/movement + auto camTransform = ecs.get<fggl::math::Transform>(cam); + auto camComp = ecs.get<fggl::gfx::Camera>(cam); + + glm::vec4 position(camTransform->origin(), 1.0f); + glm::vec4 pivot(camComp->target, 1.0f); + + // apply movement + if (translation != glm::vec3(0.0f)) { + const auto rotation = (position - pivot); + const float angle = atan2f(rotation.x, rotation.z); + const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP); + + auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED; + deltaMove.w = 0.0f; + + position += deltaMove; + pivot += deltaMove; + } + + // apply rotation + if (rotationValue != 0.0f) { + glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP); + position = (rotation * (position - pivot)) + pivot; + } + + camTransform->origin(position); + camComp->target = pivot; + } + + void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) { + glm::vec3 translation(0.0f); + + auto &mouse = input.mouse; + + // calculate movement (user input) + if (mouse.axis(MouseAxis::Y) < 0.9f) { + translation -= fggl::math::RIGHT; + } + + if (mouse.axis(MouseAxis::Y) > -0.9f) { + translation += fggl::math::RIGHT; + } + + if (mouse.axis(MouseAxis::X) > -0.9f) { + translation += fggl::math::FORWARD; + } + + if (mouse.axis(MouseAxis::X) < 0.9f) { + translation -= fggl::math::FORWARD; + } + + // apply rotation/movement + auto camTransform = ecs.get<fggl::math::Transform>(cam); + auto camComp = ecs.get<fggl::gfx::Camera>(cam); + + glm::vec4 position(camTransform->origin(), 1.0f); + glm::vec4 pivot(camComp->target, 1.0f); + + // apply movement + if (translation != glm::vec3(0.0f)) { + const auto rotation = (position - pivot); + const float angle = atan2f(rotation.x, rotation.z); + const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP); + + auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED; + deltaMove.w = 0.0f; + + position += deltaMove; + pivot += deltaMove; + } + + // move camera + camTransform->origin(position); + camComp->target = pivot; + } +} diff --git a/fggl/input/camera_input.h b/fggl/input/camera_input.h new file mode 100644 index 0000000000000000000000000000000000000000..b2c273573ee307400f7cbc8f42c9eec479ff2bec --- /dev/null +++ b/fggl/input/camera_input.h @@ -0,0 +1,60 @@ +// +// Created by webpigeon on 20/11/2021. +// + +#ifndef FGGL_CAMERA_INPUT_H +#define FGGL_CAMERA_INPUT_H + +#include <fggl/ecs3/ecs.hpp> +#include <fggl/math/types.hpp> + +namespace fggl::input { + + constexpr float ROT_SPEED = 0.05f; + constexpr float PAN_SPEED = 0.05f; + constexpr math::mat4 MAT_IDENTITY(1.0f); + + struct FreeCamKeys { + constexpr const static char name[] = "FreeCameraKeys"; + scancode_t forward; + scancode_t backward; + scancode_t left; + scancode_t right; + scancode_t rotate_cw; + scancode_t rotate_ccw; + }; + + /** + * Process the camera based on rotation around a fixed point. + * + * @param ecs the world that contains the camera + * @param input the input module to read the mouse location from + * @param cam the ID of the camera entity + */ + void process_arcball(fggl::ecs3::World& ecs, const Input& input, fggl::ecs::entity_t cam); + + /** + * Process free (floating) camera movement. + * + * @param ecs the world that contains the camera + * @param input the input module to read the mouse location from + * @param cam the ID of the camera entity + */ + void process_freecam(fggl::ecs3::World& ecs, const Input& input, fggl::ecs::entity_t cam); + + /** + * Input processing for moving the camera when the mouse is close to the edge of the screen. + * + * This function deals with ensuring the camera moves if the user moves their mouse button close + * to the edge of the screen. It will apply this as movement to the camera entity passed in as + * an argument. + * + * @param ecs the world that contains the camera + * @param input the input module to read the mouse location from + * @param cam the ID of the camera entity + */ + void process_edgescroll(fggl::ecs3::World& ecs, const Input& input, fggl::ecs::entity_t cam); + +} + +#endif //FGGL_CAMERA_INPUT_H