diff --git a/build.sh b/build.sh index 9e20a961c782d0d331c13772692930e21e656002..0fc82c0d206cd049ea486d8ef44b29d943cb1ef7 100755 --- a/build.sh +++ b/build.sh @@ -43,5 +43,5 @@ fi #EXE="gdb $EXE" pushd demo -$EXE ../build/demo/FgglDemo > /tmp/fggl.log 2>&1 +$EXE gdb ../build/demo/FgglDemo popd diff --git a/demo/main.cpp b/demo/main.cpp index 7c3b7f13161b1b808f87f0345d8bc4733e8cee3b..d865c9897f0b5137a5038126b01ea69c34db7cb8 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -9,6 +9,8 @@ #include <fggl/ecs2/ecs.cpp> #include <fggl/gfx/ecs.hpp> +#include <fggl/gfx/ecs-cam.hpp> + #include <fggl/math/ecs.hpp> #include <fggl/data/procedural.hpp> @@ -50,206 +52,6 @@ void discover(std::filesystem::path base) { } -enum camera_type { cam_free, cam_arcball }; -camera_type cam_mode = cam_free; - -struct CameraHacks { - fggl::gfx::Window& window; - fggl::gfx::Input& input; -}; - -void CameraArcball(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c); -void CameraFree(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c); -void CameraEdgeScroll(fggl::gfx::Input& input, fggl::components::Transform& t, fggl::components::Camera& c); - -//TODO proper input system -void CameraInput(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { - CameraHacks* hacks = (CameraHacks*)( it.ctx() ); - fggl::gfx::Window& window = hacks->window; - fggl::gfx::Input& input = hacks->input; - - if ( glfwGetKey(window.handle(), GLFW_KEY_F2) == GLFW_PRESS ) { - cam_mode = cam_free; - } - if ( glfwGetKey(window.handle(), GLFW_KEY_F3) == GLFW_PRESS ) { - cam_mode = cam_arcball; - } - - if ( cam_mode == cam_arcball || input.mouseDown( fggl::gfx::MOUSE_2 ) ) { - CameraArcball(it, t, c); - } else if ( cam_mode == cam_free ) { - CameraFree(it, t, c); - } - - // scroll wheel - auto& camTransform = t[0]; - auto& camComp = c[0]; - - CameraEdgeScroll(input, camTransform, camComp); - - const glm::vec3 dir = ( camTransform.origin() - camComp.target ); - const glm::vec3 forward = glm::normalize( dir ); - - glm::vec3 motion(0.0f); - float delta = (float)input.scrollDeltaY(); - 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 ); -} - -void CameraArcball(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { - CameraHacks* hacks = (CameraHacks*)( it.ctx() ); - const fggl::gfx::Window& window = hacks->window; - const fggl::gfx::Input& input = hacks->input; - - auto& camTransform = t[0]; - auto& camComp = c[0]; - - // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ - 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 / window.width() ); - float deltaAngleY = ( M_PI / window.height() ); - float xAngle = ( input.cursorDeltaX() * window.width() ) * deltaAngleX; - float yAngle = ( input.cursorDeltaY() * window.height() ) * 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 ); - auto 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 CameraFree(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { - CameraHacks* hacks = (CameraHacks*)( it.ctx() ); - fggl::gfx::Window& window = hacks->window; - fggl::gfx::Input& input = hacks->input; - - auto& camTransform = t[0]; - auto& camComp = c[0]; - - float rotationValue = 0.0f; - glm::vec3 translation(0.0f); - - // calulate rotation (user input) - if ( glfwGetKey(window.handle(), GLFW_KEY_Q) == GLFW_PRESS ) { - rotationValue = ROT_SPEED; - } else if ( glfwGetKey(window.handle(), GLFW_KEY_E) == GLFW_PRESS ) { - rotationValue = -ROT_SPEED; - } - - // calulate movement (user input) - if ( glfwGetKey(window.handle(), GLFW_KEY_W) == GLFW_PRESS ) { - translation -= fggl::math::RIGHT; - } - - if ( glfwGetKey(window.handle(), GLFW_KEY_S) == GLFW_PRESS ) { - translation += fggl::math::RIGHT; - } - - if ( glfwGetKey(window.handle(), GLFW_KEY_D) == GLFW_PRESS ) { - translation += fggl::math::FORWARD; - } - - if ( glfwGetKey(window.handle(), GLFW_KEY_A) == GLFW_PRESS ) { - translation -= fggl::math::FORWARD; - } - - 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 CameraEdgeScroll(fggl::gfx::Input& input, fggl::components::Transform& t, fggl::components::Camera& c) { - auto cursor = input.mousePos(); - - float rotationValue = 0.0f; - glm::vec3 translation(0.0f); - - // calulate movement (user input) - if ( cursor[1] > 0.95f ) { - translation += fggl::math::RIGHT; - } - - if ( cursor[1] < -0.95f ) { - translation -= fggl::math::RIGHT; - } - - if ( cursor[0] > 0.95f ) { - translation += fggl::math::FORWARD; - } - - if ( cursor[0] < -0.95f ) { - translation -= fggl::math::FORWARD; - } - - glm::vec4 position( t.origin(), 1.0f ); - glm::vec4 pivot( c.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; - } - - t.origin( position ); - c.target = pivot; -} - void loadShader(fggl::gfx::ShaderCache& cache, const char* name, bool hasGeom) { fggl::gfx::ShaderConfig config; config.name = name; @@ -278,9 +80,6 @@ int main(int argc, char* argv[]) { fggl::data::Storage storage; discover( storage.resolvePath(fggl::data::Data, "res") ); - // Hacks to make ECS work, should probably be fixed... - CameraHacks hacks{win, fggl::gfx::Input::instance()}; - fggl::gfx::ShaderCache cache(storage); loadShader(cache, "unlit", false); loadShader(cache, "phong", false); @@ -292,8 +91,12 @@ int main(int argc, char* argv[]) { // load the modules world.import<fggl::components::Math>(); world.import<fggl::components::Gfx>(); + world.import<fggl::components::CameraMod>(); world.import<fggl::systems::OpenGL>(); + world.import<fggl::systems::CameraInput>(); + + world.ecs().set<fggl::components::GfxHacks>({ &win }); // make camera auto camEnt = world.create("camera"); @@ -311,9 +114,10 @@ int main(int argc, char* argv[]) { fggl::data::Mesh mesh = fggl::data::make_quad_xz(); auto token = meshRenderer.upload( mesh ); - floorEnt.set<fggl::components::GfxToken>( { } ); + floorEnt.set<fggl::components::GfxToken>( token ); } + // bunker prefab constexpr float HALF_PI = M_PI / 2.0f; constexpr int nSections = 2; auto bunkerPrototype = world.prefab("bunker"); @@ -361,10 +165,6 @@ int main(int argc, char* argv[]) { fggl::gfx::Input& input = fggl::gfx::Input::instance(); - world.ecs().system<fggl::components::Transform, fggl::components::Camera>() - .ctx( &hacks ) - .iter( CameraInput ); - bool joystickWindow = true; bool gamepadWindow = true; diff --git a/fggl/gfx/CMakeLists.txt b/fggl/gfx/CMakeLists.txt index 961857887e88901d4ec059d044e0f45ce0aaa1d2..c64d93f8fe37ee9ba5bfd9ab562df4ae887aa40b 100644 --- a/fggl/gfx/CMakeLists.txt +++ b/fggl/gfx/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(fggl input.cpp shader.cpp ogl.cpp + ecs-cam.cpp ) # OpenGL Backend diff --git a/fggl/gfx/ecs-cam.cpp b/fggl/gfx/ecs-cam.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b7c995b0c631d2c03f879fea2715b9f4eecf3f3 --- /dev/null +++ b/fggl/gfx/ecs-cam.cpp @@ -0,0 +1,201 @@ +#include <fggl/gfx/ecs.hpp> +#include <fggl/gfx/ecs-cam.hpp> + +#include <fggl/gfx/window.hpp> + +using fggl::math::sgn; + +namespace fggl::systems { + + constexpr float ROT_SPEED = 0.05f; + constexpr float PAN_SPEED = 0.05f; + constexpr glm::mat4 MAT_IDENTITY(1.0f); + + enum camera_type { cam_free, cam_arcball }; + camera_type cam_mode = cam_free; + + void ProcessCameraInput(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { + //FIXME only required because the imput system is not finished + fggl::gfx::Window* window = it.world().get<fggl::components::GfxHacks>()->window; + fggl::gfx::Input& input = fggl::gfx::Input::instance(); + + //TODO proper input system + if ( glfwGetKey(window->handle(), GLFW_KEY_F2) == GLFW_PRESS ) { + cam_mode = cam_free; + } + if ( glfwGetKey(window->handle(), GLFW_KEY_F3) == GLFW_PRESS ) { + cam_mode = cam_arcball; + } + + if ( cam_mode == cam_arcball || input.mouseDown( fggl::gfx::MOUSE_2 ) ) { + CameraArcball(it, t, c); + } else if ( cam_mode == cam_free ) { + CameraFree(it, t, c); + } + + // scroll wheel + auto& camTransform = t[0]; + auto& camComp = c[0]; + + CameraEdgeScroll(input, camTransform, camComp); + + const glm::vec3 dir = ( camTransform.origin() - camComp.target ); + const glm::vec3 forward = glm::normalize( dir ); + + glm::vec3 motion(0.0f); + float delta = (float)input.scrollDeltaY(); + 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 ); + } + + void CameraArcball(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { + const fggl::gfx::Input& input = fggl::gfx::Input::instance(); + + auto& camTransform = t[0]; + auto& camComp = c[0]; + + // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ + 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 = ( input.cursorDeltaX() ) * deltaAngleX; + float yAngle = ( input.cursorDeltaY() ) * 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 ); + auto finalPos = ( rotationMatrixY * ( position - pivot ) ) + pivot; + + camTransform.origin( finalPos ); + } + + + void CameraFree(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { + + //FIXME only required because the imput system is not finished + fggl::gfx::Window* window = it.world().get<fggl::components::GfxHacks>()->window; + const fggl::gfx::Input& input = fggl::gfx::Input::instance(); + + auto& camTransform = t[0]; + auto& camComp = c[0]; + + float rotationValue = 0.0f; + glm::vec3 translation(0.0f); + + // calulate rotation (user input) + if ( glfwGetKey(window->handle(), GLFW_KEY_Q) == GLFW_PRESS ) { + rotationValue = ROT_SPEED; + } else if ( glfwGetKey(window->handle(), GLFW_KEY_E) == GLFW_PRESS ) { + rotationValue = -ROT_SPEED; + } + + // calulate movement (user input) + if ( glfwGetKey(window->handle(), GLFW_KEY_W) == GLFW_PRESS ) { + translation -= fggl::math::RIGHT; + } + + if ( glfwGetKey(window->handle(), GLFW_KEY_S) == GLFW_PRESS ) { + translation += fggl::math::RIGHT; + } + + if ( glfwGetKey(window->handle(), GLFW_KEY_D) == GLFW_PRESS ) { + translation += fggl::math::FORWARD; + } + + if ( glfwGetKey(window->handle(), GLFW_KEY_A) == GLFW_PRESS ) { + translation -= fggl::math::FORWARD; + } + + 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 CameraEdgeScroll(fggl::gfx::Input& input, fggl::components::Transform& t, fggl::components::Camera& c) { + auto cursor = input.mousePos(); + + float rotationValue = 0.0f; + glm::vec3 translation(0.0f); + + // calulate movement (user input) + if ( cursor[1] > 0.95f ) { + translation += fggl::math::RIGHT; + } + + if ( cursor[1] < -0.95f ) { + translation -= fggl::math::RIGHT; + } + + if ( cursor[0] > 0.95f ) { + translation += fggl::math::FORWARD; + } + + if ( cursor[0] < -0.95f ) { + translation -= fggl::math::FORWARD; + } + + glm::vec4 position( t.origin(), 1.0f ); + glm::vec4 pivot( c.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; + } + + t.origin( position ); + c.target = pivot; + } + +}; diff --git a/fggl/gfx/ecs-cam.hpp b/fggl/gfx/ecs-cam.hpp new file mode 100644 index 0000000000000000000000000000000000000000..19e8b717ced4a379e7d6ab92f10319d6bbe56847 --- /dev/null +++ b/fggl/gfx/ecs-cam.hpp @@ -0,0 +1,48 @@ +#ifndef FGGL_GFX_ECS_CAM_H +#define FGGL_GFX_ECS_CAM_H + +#include <fggl/ecs2/ecs.hpp> +#include <fggl/math/types.hpp> +#include <fggl/math/ecs.hpp> + +#include <fggl/gfx/input.hpp> + +namespace fggl::components { + + struct Camera { + math::vec3 target = math::vec3(0.0f, 0.0f, 0.0f); + float aspectRatio = 1280.0f / 720.0f; + float fov = glm::radians(45.0f); + float nearPlane = 0.1f; + float farPlane = 100.0f; + }; + + class CameraMod { + public: + inline CameraMod(ecs2::impl::world& world) { + world.module<CameraMod>("cam"); + world.component<Camera>("camera"); + } + }; +}; + +namespace fggl::systems { + + void CameraArcball(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c); + void CameraFree(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c); + void CameraEdgeScroll(fggl::gfx::Input& input, fggl::components::Transform& t, fggl::components::Camera& c); + void ProcessCameraInput(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c); + + class CameraInput { + public: + inline CameraInput(ecs2::impl::world& world) { + world.module<CameraInput>("CamInput"); + + world.system<fggl::components::Transform, fggl::components::Camera>() + .iter( ProcessCameraInput ); + } + }; + +} + +#endif diff --git a/fggl/gfx/ecs.hpp b/fggl/gfx/ecs.hpp index 6201de7ab076f22186983baaf8f117a313117d07..33d2b94eb11f439447eb757774fea8f45d1828c1 100644 --- a/fggl/gfx/ecs.hpp +++ b/fggl/gfx/ecs.hpp @@ -1,6 +1,7 @@ #ifndef FGGL_GFX_ECS_H #define FGGL_GFX_ECS_H +#include "window.hpp" #include <fggl/ecs2/ecs.hpp> #include <fggl/math/types.hpp> @@ -23,12 +24,9 @@ namespace fggl::components { math::mat4 proj; }; - struct Camera { - math::vec3 target = math::vec3(0.0f, 0.0f, 0.0f); - float aspectRatio = 1280.0f / 720.0f; - float fov = glm::radians(45.0f); - float nearPlane = 0.1f; - float farPlane = 100.0f; + // FIXME imperfect abstraction - needs fixing + struct GfxHacks { + fggl::gfx::Window* window; }; class Gfx { @@ -36,7 +34,6 @@ namespace fggl::components { inline Gfx(ecs2::impl::world& world) { world.module<Gfx>("gfx"); world.component<GfxToken>("token"); - world.component<Camera>("camera"); world.component<GfxMat>("mat"); } }; diff --git a/fggl/gfx/renderer.cpp b/fggl/gfx/renderer.cpp index 4f1b3c4208b496623b756c2acd1690be8be08bab..d1b1fdb4a7298d7b3f56eca8455c0c028b6af826 100644 --- a/fggl/gfx/renderer.cpp +++ b/fggl/gfx/renderer.cpp @@ -4,6 +4,7 @@ #include <fggl/math/ecs.hpp> #include <fggl/gfx/ecs.hpp> +#include <fggl/gfx/ecs-cam.hpp> #include <fggl/data/model.hpp> diff --git a/fggl/math/types.hpp b/fggl/math/types.hpp index e4b3c4612651aef354fd78c2446cc701cdbf2a15..bb201f22e591cc7c82d6b0512155dfc12311a872 100644 --- a/fggl/math/types.hpp +++ b/fggl/math/types.hpp @@ -23,6 +23,10 @@ namespace fggl::math { constexpr vec3 FORWARD { 1.0f, 0.0f, 0.0f }; constexpr vec3 RIGHT { 0.0f, 0.0f, 1.0f }; + template <typename T> int sgn(T val) { + return (T(0) < val) - (val < T(0)); + } + inline glm::mat4 modelMatrix( const vec3 offset, const quat rotation ) { return glm::translate( glm::mat4(1.0f), offset ) * glm::toMat4( rotation ); }