diff --git a/demo/main.cpp b/demo/main.cpp index f58a6351a3fcf66ac05e68ec6afd9c947cb8f87f..c0ef3059d754485e3b2738d65b908f3a590b6393 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -3,6 +3,7 @@ #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> @@ -17,10 +18,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) { @@ -52,161 +49,7 @@ 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::gfx::Window& window, fggl::ecs3::World& ecs, InputManager input) { +void process_camera(fggl::gfx::Window& window, fggl::ecs3::World& ecs, const InputManager& input) { auto cameras = ecs.findMatching<fggl::gfx::Camera>(); fggl::ecs3::entity_t cam = cameras[0]; @@ -221,18 +64,18 @@ void process_camera(fggl::gfx::Window& window, fggl::ecs3::World& ecs, InputMana 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 ); } int main(int argc, char* argv[]) { + // setup ECS fggl::ecs3::TypeRegistry types; fggl::ecs3::ModuleManager modules(types); @@ -269,9 +112,18 @@ int main(int argc, char* argv[]) { auto prototype = ecs.create(false); ecs.add(prototype, types.find(fggl::math::Transform::name)); ecs.add(prototype, types.find(fggl::gfx::Camera::name)); + ecs.add(prototype, types.find(fggl::input::FreeCamKeys::name)); auto camTf = ecs.get<fggl::math::Transform>(prototype); camTf->origin( glm::vec3(0.0f, 3.0f, 3.0f) ); + + auto cameraKeys = ecs.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 1bd8aec5da984df76d65488a9b019a21d15734be..000d4172a89c1b52c2d6d971cdbaaa8a0ce563f2 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -6,7 +6,7 @@ add_library(fggl fggl.cpp data/procedural.cpp ecs3/fast/Container.cpp ecs3/prototype/world.cpp - ecs3/module/module.cpp) + ecs3/module/module.cpp input/camera_input.h 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 fd761c62bf38044fc31ab4b82c22ca4780694405..e489790b501ff29c8c35325a6d184656a4d24bc1 100644 --- a/fggl/gfx/ogl/compat.hpp +++ b/fggl/gfx/ogl/compat.hpp @@ -18,6 +18,7 @@ #include <fggl/gfx/common.hpp> #include <fggl/gfx/camera.hpp> #include <fggl/ecs/ecs.hpp> +#include <fggl/input/camera_input.h> namespace fggl::gfx { @@ -51,6 +52,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..d24d9093f9fb530c128cbf9abd507c18a31ad74d --- /dev/null +++ b/fggl/input/camera_input.h @@ -0,0 +1,59 @@ +// +// Created by webpigeon on 20/11/2021. +// + +#ifndef FGGL_CAMERA_INPUT_H +#define FGGL_CAMERA_INPUT_H + +#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