From 6b7a9e3c5aba3659dd4210a27335d14a334f7680 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sun, 17 Apr 2022 11:10:48 +0100 Subject: [PATCH] use consistant style for headers --- fggl/gfx/ogl/backend.cpp | 25 +- fggl/gfx/ogl/renderer.cpp | 415 ++++++++++++++-------------- fggl/gfx/window.cpp | 7 +- include/fggl/app.hpp | 206 +++++++------- include/fggl/data/heightmap.h | 59 ++-- include/fggl/data/model.hpp | 67 ++--- include/fggl/data/procedural.hpp | 20 +- include/fggl/data/procedure.hpp | 1 - include/fggl/data/storage.hpp | 38 ++- include/fggl/debug/debug.h | 9 +- include/fggl/ecs/component.hpp | 73 ++--- include/fggl/ecs/ecs.hpp | 205 +++++++------- include/fggl/ecs/utility.hpp | 1 + include/fggl/ecs3/ecs.hpp | 2 +- include/fggl/ecs3/fast/Container.h | 169 ++++++----- include/fggl/ecs3/fast/ecs.hpp | 230 +++++++-------- include/fggl/ecs3/module/module.h | 112 ++++---- include/fggl/ecs3/prototype/world.h | 402 +++++++++++++-------------- include/fggl/ecs3/types.hpp | 313 ++++++++++----------- include/fggl/ecs3/utils.hpp | 38 +-- include/fggl/gfx/atlas.hpp | 78 +++--- include/fggl/gfx/camera.hpp | 2 +- include/fggl/gfx/common.hpp | 5 +- include/fggl/gfx/compat.hpp | 29 +- include/fggl/gfx/input.hpp | 22 +- include/fggl/gfx/ogl/backend.hpp | 52 ++-- include/fggl/gfx/ogl/compat.hpp | 104 +++---- include/fggl/gfx/ogl/renderer.hpp | 42 ++- include/fggl/gfx/ogl/shader.hpp | 32 +-- include/fggl/gfx/paint.hpp | 172 ++++++------ include/fggl/gfx/renderer.hpp | 19 +- include/fggl/gfx/vector.hpp | 14 +- include/fggl/gfx/vulkan/vulkan.hpp | 27 +- include/fggl/gfx/window.hpp | 72 ++--- include/fggl/gfx/window_input.hpp | 23 +- include/fggl/gfx/windowing.hpp | 72 ++--- include/fggl/gui/containers.hpp | 106 +++---- include/fggl/gui/widget.hpp | 48 ++-- include/fggl/gui/widgets.hpp | 52 ++-- include/fggl/input/camera_input.h | 88 +++--- include/fggl/input/gamepad.hpp | 133 +++++---- include/fggl/input/input.hpp | 2 +- include/fggl/input/keyboard.hpp | 44 +-- include/fggl/input/mouse.hpp | 52 ++-- include/fggl/math/easing.hpp | 209 +++++++------- include/fggl/math/triangulation.hpp | 301 ++++++++++---------- include/fggl/math/types.hpp | 146 +++++----- include/fggl/scenes/Scene.h | 68 ++--- include/fggl/scenes/menu.hpp | 32 +-- include/fggl/util/chrono.hpp | 2 +- include/fggl/util/service.h | 72 ++--- include/fggl/util/states.hpp | 88 +++--- 52 files changed, 2307 insertions(+), 2293 deletions(-) diff --git a/fggl/gfx/ogl/backend.cpp b/fggl/gfx/ogl/backend.cpp index 99b0631..1eb7c94 100644 --- a/fggl/gfx/ogl/backend.cpp +++ b/fggl/gfx/ogl/backend.cpp @@ -1,27 +1,6 @@ -#include <fggl/gfx/ogl/backend.hpp> +#include <spdlog/spdlog.h> +#include <fggl/gfx/ogl/backend.hpp> #include <stdexcept> -#include <spdlog/spdlog.h> using namespace fggl::gfx; - -GlGraphics::GlGraphics(const Window& window) { - spdlog::debug("[OGL] attaching window context"); - window.activate(); - GLenum err = glewInit(); - if ( GLEW_OK != err ) { - throw std::runtime_error("couldn't init glew"); - } - - glViewport(0, 0, 1920, 1080); - spdlog::debug("[OGL] window context ready"); -} - -GlGraphics::~GlGraphics() { - spdlog::debug("[OGL] gl window context killed!"); -} - -void GlGraphics::clear() { - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); -} diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp index 410f146..4a0f570 100644 --- a/fggl/gfx/ogl/renderer.cpp +++ b/fggl/gfx/ogl/renderer.cpp @@ -1,14 +1,11 @@ +#include <fggl/util/service.h> #include <spdlog/spdlog.h> +#include <fggl/data/model.hpp> +#include <fggl/gfx/camera.hpp> #include <fggl/gfx/ogl/renderer.hpp> #include <fggl/gfx/paint.hpp> - #include <fggl/math/triangulation.hpp> -#include <fggl/data/model.hpp> -#include <fggl/util/service.h> - -#include <fggl/gfx/camera.hpp> - #include <glm/ext/matrix_clip_space.hpp> #include <glm/ext/matrix_transform.hpp> #include <glm/glm.hpp> @@ -17,247 +14,259 @@ /** * Future optimisations: - * recommended approach is to group stuff in to as few vao as possible - this will do one vao per mesh, aka bad. - * Add support for instanced rendering (particles) - * Look at packing vertex data in better ways (with profiling) + * recommended approach is to group stuff in to as few vao as possible - this + * will do one vao per mesh, aka bad. Add support for instanced rendering + * (particles) Look at packing vertex data in better ways (with profiling) * Support shader specialisation (ie, dynamic/streamed data) - * Follow best recommendations for Vertex attributes (ie normals not using floats, bytes for colour) + * Follow best recommendations for Vertex attributes (ie normals not using + * floats, bytes for colour) * * Future features: * Add support for models with weights (for animations) - * OpenGL ES for the FOSS thinkpad users who can't run anything even remotely modern + * OpenGL ES for the FOSS thinkpad users who can't run anything even remotely + * modern */ namespace fggl::gfx { - using data::Vertex2D; - using data::Mesh2D; - - GlRenderToken setupVertex2D() { - GlRenderToken token{}; - glGenVertexArrays(1, &token.vao); - glBindVertexArray(token.vao); +using data::Mesh2D; +using data::Vertex2D; - glGenBuffers(2, token.buffs); +GlRenderToken setupVertex2D() { + GlRenderToken token{}; + glGenVertexArrays(1, &token.vao); + glBindVertexArray(token.vao); - glBindBuffer(GL_ARRAY_BUFFER, token.buffs[0]); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), reinterpret_cast<void*>(offsetof(Vertex2D, position))); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), reinterpret_cast<void*>(offsetof(Vertex2D, colour))); + glGenBuffers(2, token.buffs); - glBindVertexArray(0); - return token; - } - - OpenGL4Backend::OpenGL4Backend() : fggl::gfx::Graphics() { - GLenum err = glewInit(); - if ( GLEW_OK != err ) { - throw std::runtime_error("couldn't init glew"); - } - glViewport(0, 0, 2560, 1440); + glBindBuffer(GL_ARRAY_BUFFER, token.buffs[0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), + reinterpret_cast<void*>(offsetof(Vertex2D, position))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), + reinterpret_cast<void*>(offsetof(Vertex2D, colour))); - m_token2D = setupVertex2D(); - - auto& locator = util::ServiceLocator::instance(); + glBindVertexArray(0); + return token; +} - auto storage = locator.get<data::Storage>(); - m_cache = std::make_unique<ShaderCache>(storage); +OpenGL4Backend::OpenGL4Backend(const Window& owner) : fggl::gfx::Graphics() { + // initialise GLEW, or fail + GLenum err = glewInit(); + if (GLEW_OK != err) { + throw std::runtime_error("couldn't init glew"); + } + + // setup the viewport based on the window's framebuffer + auto fbSize = owner.frameSize(); + glViewport(0, 0, fbSize.x, fbSize.y); + + // setup the shader cache + auto& locator = util::ServiceLocator::instance(); + auto storage = locator.get<data::Storage>(); + m_cache = std::make_unique<ShaderCache>(storage); + + // setup 2D rendering system + m_token2D = setupVertex2D(); + ShaderConfig shader2DConfig = ShaderFromName("shader2D"); + m_cache->load(shader2DConfig); +}; + +void OpenGL4Backend::clear() { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} - ShaderConfig shader2DConfig = ShaderFromName( "shader2D" ); - m_cache->load( shader2DConfig ); - }; +static void generateMesh(const gfx::Paint& paint, Mesh2D& mesh) { + for (auto& cmd : paint.cmds()) { + auto path = cmd.path; + + std::vector<data::Vertex2D> verts; + math::vec3 colour{1.0f, 1.0f, 1.0f}; + auto idx = 0; + auto colourIdx = 0; + + for (auto& type : path.m_types) { + if (type == PathType::PATH) { + verts.push_back({.position = path.m_points[idx++], .colour = colour}); + } else if (type == PathType::MOVE) { + // polygon finished + if (verts.size() < 3) { + // empty, point, or line + } else if (verts.size() == 3) { + // triangle + } else { + // polygon + math::fanTriangulation(verts, mesh); + } - void OpenGL4Backend::clear() { - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + verts.clear(); + verts.push_back({.position = path.m_points[idx++], .colour = colour}); + } else if (type == PathType::COLOUR) { + colour = path.m_colours[colourIdx++]; + } else { + // unsupported type + } } - static void generateMesh( const gfx::Paint& paint, Mesh2D& mesh ) { - for ( auto& cmd : paint.cmds() ) { - - auto path = cmd.path; - - std::vector<data::Vertex2D> verts; - math::vec3 colour{1.0f, 1.0f, 1.0f}; - auto idx = 0; - auto colourIdx = 0; - - for ( auto& type : path.m_types ) { - if ( type == PathType::PATH ) { - verts.push_back({ - .position = path.m_points[ idx++ ], - .colour = colour - }); - } else if ( type == PathType::MOVE ) { - // polygon finished - if ( verts.size() < 3 ) { - // empty, point, or line - } else if ( verts.size() == 3 ) { - // triangle - } else { - // polygon - math::fanTriangulation( verts, mesh ); - } - - verts.clear(); - verts.push_back({ - .position = path.m_points[ idx++ ], - .colour = colour - }); - } else if ( type == PathType::COLOUR ) { - colour = path.m_colours[ colourIdx++ ]; - } else { - // unsupported type - } - } - - if ( !verts.empty() ) { - math::fanTriangulation( verts, mesh ); - } - - } + if (!verts.empty()) { + math::fanTriangulation(verts, mesh); } + } +} - void OpenGL4Backend::draw2D( const gfx::Paint& paint ) { +void OpenGL4Backend::draw2D(const gfx::Paint& paint) { + // generate the mesh from a paint command list + data::Mesh2D mesh; + generateMesh(paint, mesh); - // generate the mesh from a paint command list - data::Mesh2D mesh; - generateMesh( paint, mesh ); + // render the resulting mesh + auto shader2D = m_cache->get("shader2D"); - // render the resulting mesh - auto shader2D = m_cache->get( "shader2D" ); + glUseProgram(shader2D); - glUseProgram( shader2D ); + auto projMat = glm::ortho(0.0f, 1920.0f, 0.0f, 1080.f); + glUniformMatrix4fv(glGetUniformLocation(shader2D, "projection"), 1, GL_FALSE, + glm::value_ptr(projMat)); - auto projMat = glm::ortho(0.0f, 1920.0f, 0.0f, 1080.f); - glUniformMatrix4fv( glGetUniformLocation(shader2D, "projection"), 1, GL_FALSE, glm::value_ptr( projMat ) ); + glBindVertexArray(m_token2D.vao); - glBindVertexArray( m_token2D.vao ); + glBindBuffer(GL_ARRAY_BUFFER, m_token2D.buffs[0]); + glBufferData(GL_ARRAY_BUFFER, mesh.vertexList.size() * sizeof(Vertex2D), + mesh.vertexList.data(), GL_DYNAMIC_DRAW); - glBindBuffer( GL_ARRAY_BUFFER, m_token2D.buffs[0] ); - glBufferData( GL_ARRAY_BUFFER, mesh.vertexList.size() * sizeof(Vertex2D), mesh.vertexList.data(), GL_DYNAMIC_DRAW ); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_token2D.buffs[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + mesh.indexList.size() * sizeof(uint32_t), mesh.indexList.data(), + GL_DYNAMIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_token2D.buffs[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indexList.size() * sizeof(uint32_t), mesh.indexList.data(), GL_DYNAMIC_DRAW); - - glDrawElements( GL_TRIANGLES, mesh.indexList.size(), GL_UNSIGNED_INT, (GLvoid*)0); + glDrawElements(GL_TRIANGLES, mesh.indexList.size(), GL_UNSIGNED_INT, + (GLvoid*)0); - glBindVertexArray( 0 ); - glUseProgram( 0 ); - //glDisable(GL_PRIMITIVE_RESTART); - } + glBindVertexArray(0); + glUseProgram(0); + // glDisable(GL_PRIMITIVE_RESTART); +} -template<typename T> +template <typename T> static GLuint createArrayBuffer(std::vector<T>& vertexData) { - GLuint buffId; - glGenBuffers(1, &buffId); - glBindBuffer(GL_ARRAY_BUFFER, buffId); - glBufferData(GL_ARRAY_BUFFER, - vertexData.size() * sizeof(T), - vertexData.data(), - GL_STATIC_DRAW); - return buffId; + GLuint buffId; + glGenBuffers(1, &buffId); + glBindBuffer(GL_ARRAY_BUFFER, buffId); + glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(T), + vertexData.data(), GL_STATIC_DRAW); + return buffId; } static GLuint createIndexBuffer(std::vector<uint32_t>& indexData) { - GLuint buffId; - glGenBuffers(1, &buffId); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - indexData.size() * sizeof(uint32_t), - indexData.data(), - GL_STATIC_DRAW); - return buffId; + GLuint buffId; + glGenBuffers(1, &buffId); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffId); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.size() * sizeof(uint32_t), + indexData.data(), GL_STATIC_DRAW); + return buffId; } - GlRenderToken MeshRenderer::upload(fggl::data::Mesh& mesh) { - GlRenderToken token{}; - glGenVertexArrays(1, &token.vao); - glBindVertexArray(token.vao); - - token.buffs[0] = createArrayBuffer<fggl::data::Vertex>( mesh.vertexList() ); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(fggl::data::Vertex), reinterpret_cast<void*>(offsetof(fggl::data::Vertex, posititon))); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(fggl::data::Vertex), reinterpret_cast<void*>(offsetof(fggl::data::Vertex, normal))); - - token.buffs[1] = createIndexBuffer( mesh.indexList() ); - token.idxOffset = 0; - token.idxSize = mesh.indexCount(); - token.restartVertex = mesh.restartVertex; - glBindVertexArray(0); - - return token; + GlRenderToken token{}; + glGenVertexArrays(1, &token.vao); + glBindVertexArray(token.vao); + + token.buffs[0] = createArrayBuffer<fggl::data::Vertex>(mesh.vertexList()); + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, 3, GL_FLOAT, GL_FALSE, sizeof(fggl::data::Vertex), + reinterpret_cast<void*>(offsetof(fggl::data::Vertex, posititon))); + glEnableVertexAttribArray(1); + glVertexAttribPointer( + 1, 3, GL_FLOAT, GL_FALSE, sizeof(fggl::data::Vertex), + reinterpret_cast<void*>(offsetof(fggl::data::Vertex, normal))); + + token.buffs[1] = createIndexBuffer(mesh.indexList()); + token.idxOffset = 0; + token.idxSize = mesh.indexCount(); + token.restartVertex = mesh.restartVertex; + glBindVertexArray(0); + + return token; } -//TODO(webpigeon): this shouldn't be hard-coded +// TODO(webpigeon): this shouldn't be hard-coded constexpr glm::vec3 DEFAULT_LIGHTPOS = glm::vec3(20.0F, 20.0F, 15.0F); -void MeshRenderer::render(fggl::ecs3::World& ecs, ecs3::entity_t camera, float dt) { - if ( camera == ecs::NULL_ENTITY ){ - spdlog::warn("tried to render a scene, but no camera exists!"); - return; - } - - auto entities = ecs.findMatching<GlRenderToken>(); - if ( entities.empty() ) { - spdlog::warn("asked to render, but no entities are renderable"); - return; - } - - total += dt; - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - glEnable(GL_DEPTH_TEST); - - // camera logic - auto *const camTransform = ecs.get<math::Transform>(camera); - auto *const camComp = ecs.get<gfx::Camera>(camera); - glm::mat4 proj = glm::perspective( camComp->fov, camComp->aspectRatio, camComp->nearPlane, camComp->farPlane); - glm::mat4 view = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() ); +void MeshRenderer::render(fggl::ecs3::World& ecs, ecs3::entity_t camera, + float dt) { + if (camera == ecs::NULL_ENTITY) { + spdlog::warn("tried to render a scene, but no camera exists!"); + return; + } + + auto entities = ecs.findMatching<GlRenderToken>(); + if (entities.empty()) { + spdlog::warn("asked to render, but no entities are renderable"); + return; + } + + total += dt; + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glEnable(GL_DEPTH_TEST); + + // camera logic + auto* const camTransform = ecs.get<math::Transform>(camera); + auto* const camComp = ecs.get<gfx::Camera>(camera); + glm::mat4 proj = glm::perspective(camComp->fov, camComp->aspectRatio, + camComp->nearPlane, camComp->farPlane); + glm::mat4 view = + glm::lookAt(camTransform->origin(), camComp->target, camTransform->up()); + + // lighting + glm::vec3 lightPos = DEFAULT_LIGHTPOS; + + // TODO(webpigeon): better performance if grouped by vao first + // TODO(webpigeon): the nvidia performance presentation said I shouldn't use + // uniforms for large data + for (auto& entity : entities) { + const auto& transform = ecs.get<fggl::math::Transform>(entity); + const auto& mesh = ecs.get<GlRenderToken>(entity); + + glm::mat4 model = transform->model(); + // model = glm::rotate(model, glm::radians(total/2048.0f * 360.0f), + //glm::vec3(0.0f,1.0f,0.0f)); + + auto shader = mesh->pipeline; + glUseProgram(shader); + + glUniformMatrix4fv(glGetUniformLocation(shader, "model"), 1, GL_FALSE, + glm::value_ptr(model)); + glUniformMatrix4fv(glGetUniformLocation(shader, "view"), 1, GL_FALSE, + glm::value_ptr(view)); + glUniformMatrix4fv(glGetUniformLocation(shader, "projection"), 1, GL_FALSE, + glm::value_ptr(proj)); // lighting - glm::vec3 lightPos = DEFAULT_LIGHTPOS; - - // TODO(webpigeon): better performance if grouped by vao first - // TODO(webpigeon): the nvidia performance presentation said I shouldn't use uniforms for large data - for ( auto& entity : entities ) { - const auto& transform = ecs.get<fggl::math::Transform>(entity); - const auto& mesh = ecs.get<GlRenderToken>(entity); - - glm::mat4 model = transform->model(); -// model = glm::rotate(model, glm::radians(total/2048.0f * 360.0f), glm::vec3(0.0f,1.0f,0.0f)); - - auto shader = mesh->pipeline; - glUseProgram( shader ); - - glUniformMatrix4fv( glGetUniformLocation(shader, "model"), 1, GL_FALSE, glm::value_ptr( model ) ); - glUniformMatrix4fv( glGetUniformLocation(shader, "view"), 1, GL_FALSE, glm::value_ptr( view ) ); - glUniformMatrix4fv( glGetUniformLocation(shader, "projection"), 1, GL_FALSE, glm::value_ptr( proj ) ); - - // lighting - GLint lightID = glGetUniformLocation(shader, "lightPos"); - if ( lightID != -1 ) { - glUniform3fv( lightID, 1, glm::value_ptr( lightPos ) ); - } + GLint lightID = glGetUniformLocation(shader, "lightPos"); + if (lightID != -1) { + glUniform3fv(lightID, 1, glm::value_ptr(lightPos)); + } - glBindVertexArray( mesh->vao ); + glBindVertexArray(mesh->vao); - if ( mesh->renderType == GlRenderType::triangle_strip) { - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(mesh->restartVertex); - } + if (mesh->renderType == GlRenderType::triangle_strip) { + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(mesh->restartVertex); + } - glDrawElements( mesh->renderType, mesh->idxSize, GL_UNSIGNED_INT, reinterpret_cast<void*>(mesh->idxOffset) ); - if ( mesh->renderType == GlRenderType::triangle_strip) { - glDisable(GL_PRIMITIVE_RESTART); - } + glDrawElements(mesh->renderType, mesh->idxSize, GL_UNSIGNED_INT, + reinterpret_cast<void*>(mesh->idxOffset)); + if (mesh->renderType == GlRenderType::triangle_strip) { + glDisable(GL_PRIMITIVE_RESTART); } + } - glBindVertexArray(0); + glBindVertexArray(0); } -} +} // namespace fggl::gfx diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp index 4c6b21c..c5097c3 100644 --- a/fggl/gfx/window.cpp +++ b/fggl/gfx/window.cpp @@ -250,4 +250,9 @@ void GlfwWindow::activate() const { assert( m_window != nullptr ); glfwMakeContextCurrent(m_window); } - +fggl::math::vec2i GlfwWindow::frameSize() const { + assert( m_window != nullptr ); + math::vec2i size; + glfwGetFramebufferSize(m_window, &size.x, &size.y); + return size; +} diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp index 160a58e..6b9732a 100644 --- a/include/fggl/app.hpp +++ b/include/fggl/app.hpp @@ -36,108 +36,110 @@ namespace fggl { - class App; - class AppState; - - using Identifer = std::string; - using AppMachine = util::StateMachine<AppState, Identifer>; - - class AppState { - public: - /** - * Create an Application State. - * - * A state is responsible for managing user interaction with the app. When created, the appstate - * is passed a reference to the application that owns it. The lifetime of the state is bounded - * by the lifetype of this object. - * - * @param owner a non-owned reference to the owner of the state. - */ - explicit AppState(App& owner) : m_owner( owner ) {} - - /** - * Update the underlying model of this state. - * - * States should not assume that one update means one render call, as the game loop may issue - * multiple updates per render or vice-versa depending on requriements. Update is intended for - * dispatching game-system related infomation. - */ - virtual void update() = 0; - - /** - * Perform actions neccerary for rendering the scene. - * - * When this method is invoked it is safe to assume that rendering of some form will take place - * after it returns and the rendering state should be updated to reflect this. The rendering - * environment will be passed in as an argument. - * - * It is not safe to assume the render target will always be the same, as the scene may be - * rendered in mutliple passes (eg, for VR requirements). - */ - virtual void render(gfx::Paint& paint) = 0; - - virtual void activate() {} - virtual void deactivate() {} - - protected: - App& m_owner; - }; - - class App { - public: - explicit App(const Identifer& name); - App(const Identifer& name, const Identifer& folderName); - - // class is non copy-able - App(const App& app) = delete; - App& operator=(App other) = delete; - - inline void setWindow( std::unique_ptr<gfx::Window>&& window ) { - m_window = std::move(window); - } - - /** - * Perform main game loop functions. - */ - int run(int argc, const char** argv); - - template<typename T> - T* add_state(const Identifer& name) { - static_assert( std::is_base_of<AppState,T>::value, "States must be AppStates"); - return m_states.put<T>(name, *this); - } - - template<typename T, typename... Args> - T& use(Args&& ...args) { - auto ptr = m_modules->load<T>(args...); - return *ptr; - } - - inline void change_state(const Identifer& name) { - m_states.active().deactivate(); - m_states.change(name); - m_states.active().activate(); - } - - inline AppState& active_state() const { - return m_states.active(); - } - - inline bool running() const { - return m_running; - } - - inline void running(bool state) { - m_running = state; - } - - private: - bool m_running; - std::unique_ptr<ecs3::TypeRegistry> m_types; - std::unique_ptr<ecs3::ModuleManager> m_modules; - std::unique_ptr<gfx::Window> m_window; - AppMachine m_states; - }; + class App; + + class AppState; + + using Identifer = std::string; + using AppMachine = util::StateMachine<AppState, Identifer>; + + class AppState { + public: + /** + * Create an Application State. + * + * A state is responsible for managing user interaction with the app. When created, the appstate + * is passed a reference to the application that owns it. The lifetime of the state is bounded + * by the lifetype of this object. + * + * @param owner a non-owned reference to the owner of the state. + */ + explicit AppState(App &owner) : m_owner(owner) {} + + /** + * Update the underlying model of this state. + * + * States should not assume that one update means one render call, as the game loop may issue + * multiple updates per render or vice-versa depending on requriements. Update is intended for + * dispatching game-system related infomation. + */ + virtual void update() = 0; + + /** + * Perform actions neccerary for rendering the scene. + * + * When this method is invoked it is safe to assume that rendering of some form will take place + * after it returns and the rendering state should be updated to reflect this. The rendering + * environment will be passed in as an argument. + * + * It is not safe to assume the render target will always be the same, as the scene may be + * rendered in mutliple passes (eg, for VR requirements). + */ + virtual void render(gfx::Paint &paint) = 0; + + virtual void activate() {} + + virtual void deactivate() {} + + protected: + App &m_owner; + }; + + class App { + public: + explicit App(const Identifer &name); + App(const Identifer &name, const Identifer &folderName); + + // class is non copy-able + App(const App &app) = delete; + App &operator=(App other) = delete; + + inline void setWindow(std::unique_ptr<gfx::Window> &&window) { + m_window = std::move(window); + } + + /** + * Perform main game loop functions. + */ + int run(int argc, const char **argv); + + template<typename T> + T *add_state(const Identifer &name) { + static_assert(std::is_base_of<AppState, T>::value, "States must be AppStates"); + return m_states.put<T>(name, *this); + } + + template<typename T, typename... Args> + T &use(Args &&...args) { + auto ptr = m_modules->load<T>(args...); + return *ptr; + } + + inline void change_state(const Identifer &name) { + m_states.active().deactivate(); + m_states.change(name); + m_states.active().activate(); + } + + inline AppState &active_state() const { + return m_states.active(); + } + + inline bool running() const { + return m_running; + } + + inline void running(bool state) { + m_running = state; + } + + private: + bool m_running; + std::unique_ptr<ecs3::TypeRegistry> m_types; + std::unique_ptr<ecs3::ModuleManager> m_modules; + std::unique_ptr<gfx::Window> m_window; + AppMachine m_states; + }; } diff --git a/include/fggl/data/heightmap.h b/include/fggl/data/heightmap.h index 48908bd..5043b24 100644 --- a/include/fggl/data/heightmap.h +++ b/include/fggl/data/heightmap.h @@ -9,36 +9,35 @@ namespace fggl::data { - constexpr std::size_t heightMaxX = 255; - constexpr std::size_t heightMaxZ = 255; - constexpr float heightSeaLevel = 0.0f; - - struct HeightMap { - constexpr static const char name[] = "Heightmap"; - float heightValues[heightMaxX * heightMaxZ]; - - void clear() { - for (float & heightValue : heightValues){ - heightValue = heightSeaLevel; - } - } - - [[nodiscard]] - inline float getValue(std::size_t x, std::size_t z) const { - return heightValues[x * heightMaxZ + z]; - } - - inline void setValue(std::size_t x, std::size_t z, float value) { - heightValues[x * heightMaxZ + z] = value; - } - }; - - - inline int idx(int x, int z, int zMax) { - return x * zMax + z; - } - - void generateHeightMesh(const data::HeightMap* heights, data::Mesh &mesh); + constexpr std::size_t heightMaxX = 255; + constexpr std::size_t heightMaxZ = 255; + constexpr float heightSeaLevel = 0.0f; + + struct HeightMap { + constexpr static const char name[] = "Heightmap"; + float heightValues[heightMaxX * heightMaxZ]; + + void clear() { + for (float &heightValue : heightValues) { + heightValue = heightSeaLevel; + } + } + + [[nodiscard]] + inline float getValue(std::size_t x, std::size_t z) const { + return heightValues[x * heightMaxZ + z]; + } + + inline void setValue(std::size_t x, std::size_t z, float value) { + heightValues[x * heightMaxZ + z] = value; + } + }; + + inline int idx(int x, int z, int zMax) { + return x * zMax + z; + } + + void generateHeightMesh(const data::HeightMap *heights, data::Mesh &mesh); } #endif //FGGL_HEIGHTMAP_H diff --git a/include/fggl/data/model.hpp b/include/fggl/data/model.hpp index 955231a..f59db12 100644 --- a/include/fggl/data/model.hpp +++ b/include/fggl/data/model.hpp @@ -13,41 +13,41 @@ namespace fggl::data { math::vec3 colour; }; - struct Vertex2D{ - fggl::math::vec2 position; - fggl::math::vec3 colour; - }; + struct Vertex2D { + fggl::math::vec2 position; + fggl::math::vec3 colour; + }; - struct Mesh2D { - std::vector<Vertex2D> vertexList; - std::vector<uint32_t> indexList; + struct Mesh2D { + std::vector<Vertex2D> vertexList; + std::vector<uint32_t> indexList; - inline std::size_t add_vertex(const Vertex2D& vertex) { - vertexList.push_back( vertex ); - return vertexList.size() - 1; - } + inline std::size_t add_vertex(const Vertex2D &vertex) { + vertexList.push_back(vertex); + return vertexList.size() - 1; + } - inline void add_index(uint32_t idx) { - indexList.push_back( idx ); - } - }; + inline void add_index(uint32_t idx) { + indexList.push_back(idx); + } + }; // comparison operators - inline bool operator<(const Vertex& lhs, const Vertex& rhs) { - return std::tie( lhs.posititon, lhs.normal, lhs.colour ) - < std::tie( rhs.posititon, rhs.normal, rhs.colour ); + inline bool operator<(const Vertex &lhs, const Vertex &rhs) { + return std::tie(lhs.posititon, lhs.normal, lhs.colour) + < std::tie(rhs.posititon, rhs.normal, rhs.colour); } - inline bool operator==(const Vertex& lhs, const Vertex& rhs) { + inline bool operator==(const Vertex &lhs, const Vertex &rhs) { return lhs.posititon == rhs.posititon && lhs.colour == rhs.colour && lhs.normal == rhs.normal; } - inline bool operator!=(const Vertex& lhs, const Vertex& rhs) { - return !(lhs==rhs); + inline bool operator!=(const Vertex &lhs, const Vertex &rhs) { + return !(lhs == rhs); } class Mesh { @@ -63,12 +63,12 @@ namespace fggl::data { */ inline void push(Vertex vert) { auto idx = indexOf(vert); - if ( idx == -1 ) { - idx = pushVertex(vert); - pushIndex(idx); - } else { - pushIndex(idx); - } + if (idx == -1) { + idx = pushVertex(vert); + pushIndex(idx); + } else { + pushIndex(idx); + } } /** @@ -100,7 +100,7 @@ namespace fggl::data { */ int indexOf(Vertex vert); - inline std::vector<Vertex>& vertexList() { + inline std::vector<Vertex> &vertexList() { return m_verts; } @@ -108,7 +108,7 @@ namespace fggl::data { return m_verts.size(); } - inline std::vector<uint32_t>& indexList() { + inline std::vector<uint32_t> &indexList() { return m_index; } @@ -116,10 +116,11 @@ namespace fggl::data { return m_index.size(); } - inline Vertex& vertex(int idx) { + inline Vertex &vertex(int idx) { return m_verts[idx]; } - unsigned int restartVertex; + + unsigned int restartVertex; private: std::vector<Vertex> m_verts; @@ -131,8 +132,8 @@ namespace fggl::data { Model() = default; ~Model() = default; - inline void append(const Mesh& mesh) { - m_meshes.push_back( mesh ); + inline void append(const Mesh &mesh) { + m_meshes.push_back(mesh); } private: diff --git a/include/fggl/data/procedural.hpp b/include/fggl/data/procedural.hpp index 35a2180..d8431a9 100644 --- a/include/fggl/data/procedural.hpp +++ b/include/fggl/data/procedural.hpp @@ -12,24 +12,28 @@ namespace fggl::data { Mesh make_quad_xz(); // simple shapes - Mesh make_cube(Mesh& mesh, const math::mat4& offset); - inline Mesh make_cube(Mesh& mesh) { + Mesh make_cube(Mesh &mesh, const math::mat4 &offset); + + inline Mesh make_cube(Mesh &mesh) { return make_cube(mesh, OFFSET_NONE); } // blockout shapes - Mesh make_slope(Mesh& mesh, const math::mat4& offset); - inline Mesh make_slope(Mesh& mesh) { + Mesh make_slope(Mesh &mesh, const math::mat4 &offset); + + inline Mesh make_slope(Mesh &mesh) { return make_slope(mesh, OFFSET_NONE); } - Mesh make_ditch(Mesh& mesh, const math::mat4& offset); - inline Mesh make_ditch(Mesh& mesh) { + Mesh make_ditch(Mesh &mesh, const math::mat4 &offset); + + inline Mesh make_ditch(Mesh &mesh) { return make_ditch(mesh, OFFSET_NONE); } - Mesh make_point(Mesh& mesh, const math::mat4& offset); - inline Mesh make_point(Mesh& mesh) { + Mesh make_point(Mesh &mesh, const math::mat4 &offset); + + inline Mesh make_point(Mesh &mesh) { return make_point(mesh, OFFSET_NONE); } } diff --git a/include/fggl/data/procedure.hpp b/include/fggl/data/procedure.hpp index 6354283..41843a1 100644 --- a/include/fggl/data/procedure.hpp +++ b/include/fggl/data/procedure.hpp @@ -9,7 +9,6 @@ namespace fggl::data { DataRegistry(); ~DataRegistry(); - }; } diff --git a/include/fggl/data/storage.hpp b/include/fggl/data/storage.hpp index 98ab993..6ae22d0 100644 --- a/include/fggl/data/storage.hpp +++ b/include/fggl/data/storage.hpp @@ -8,50 +8,48 @@ namespace fggl::data { template<typename T> - bool fggl_serialize(std::filesystem::path& data, const T* out); + bool fggl_serialize(std::filesystem::path &data, const T *out); template<typename T> - bool fggl_deserialize(std::filesystem::path& data, T* out); + bool fggl_deserialize(std::filesystem::path &data, T *out); + + enum StorageType { Data, User, Cache }; - enum StorageType {Data, User, Cache}; class Storage { public: template<typename T> - bool load(StorageType pool, const std::string& name, T* out) { + bool load(StorageType pool, const std::string &name, T *out) { auto path = resolvePath(pool, name); - if ( !std::filesystem::exists(path) ) { - //spdlog::warn("Path {} does not exist!", path.c_str()); + if (!std::filesystem::exists(path)) { + //spdlog::warn("Path {} does not exist!", path.c_str()); return false; } return fggl_deserialize<T>(path, out); } template<typename T> - void save(StorageType pool, const std::string& name, const T* out) { + void save(StorageType pool, const std::string &name, const T *out) { auto path = resolvePath(pool, name); fggl_serialize<T>(path, out); } - inline std::filesystem::path resolvePath(StorageType pool, const std::string& name) { + inline std::filesystem::path resolvePath(StorageType pool, const std::string &name) { std::filesystem::path path; - switch ( pool ) { - case Data: - path = std::filesystem::current_path() / "data"; - break; - case User: - path = "./user-data/"; - break; - case Cache: - path = "/tmp/fggl/"; - break; + switch (pool) { + case Data: path = std::filesystem::current_path() / "data"; + break; + case User: path = "./user-data/"; + break; + case Cache: path = "/tmp/fggl/"; + break; } - if ( !std::filesystem::exists(path) ) { + if (!std::filesystem::exists(path)) { std::filesystem::create_directories(path); } - + return path / name; } }; diff --git a/include/fggl/debug/debug.h b/include/fggl/debug/debug.h index 2457733..88419fb 100644 --- a/include/fggl/debug/debug.h +++ b/include/fggl/debug/debug.h @@ -9,7 +9,7 @@ namespace fggl::debug { - using DebugUIDraw = std::function<void(bool*)>; + using DebugUIDraw = std::function<void(bool *)>; struct DebugWindow { bool m_visible; @@ -18,16 +18,16 @@ namespace fggl::debug { class DebugUI { public: - explicit DebugUI(std::shared_ptr<gfx::GlfwWindow>& window); + explicit DebugUI(std::shared_ptr<gfx::GlfwWindow> &window); ~DebugUI(); void frameStart(); void draw(); - inline void addWindow(const std::string& name, DebugUIDraw window){ + inline void addWindow(const std::string &name, DebugUIDraw window) { m_windows[name] = DebugWindow{ .m_visible = true, - .m_callback = std::move(window) + .m_callback = std::move(window) }; } @@ -46,5 +46,4 @@ namespace fggl::debug { } - #endif diff --git a/include/fggl/ecs/component.hpp b/include/fggl/ecs/component.hpp index 51edda3..4e0e491 100644 --- a/include/fggl/ecs/component.hpp +++ b/include/fggl/ecs/component.hpp @@ -17,20 +17,21 @@ namespace fggl::ecs { class ComponentBase { public: using data_t = unsigned char; + virtual ~ComponentBase() {}; - // in place - virtual void destroy(data_t* data) const = 0; - virtual void move(data_t* src, data_t* dest) const = 0; - virtual void bulkMove(data_t* src, data_t* dest, std::size_t count) = 0; - virtual void construct(data_t* data) const = 0; + // in place + virtual void destroy(data_t *data) const = 0; + virtual void move(data_t *src, data_t *dest) const = 0; + virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) = 0; + virtual void construct(data_t *data) const = 0; - // virtual - virtual void* construct() const = 0; - virtual void* copyConstruct(const void* src) = 0; + // virtual + virtual void *construct() const = 0; + virtual void *copyConstruct(const void *src) = 0; - virtual const char* name() const = 0; - virtual const component_type_t id() const = 0; + virtual const char *name() const = 0; + virtual const component_type_t id() const = 0; virtual std::size_t size() const = 0; }; @@ -38,47 +39,47 @@ namespace fggl::ecs { template<class C> class Component : public ComponentBase { public: - virtual void destroy(data_t* data) const override { - C* location = std::launder(reinterpret_cast<C*>(data)); + virtual void destroy(data_t *data) const override { + C *location = std::launder(reinterpret_cast<C *>(data)); location->~C(); } - virtual const char* name() const override { - return C::name; - } + virtual const char *name() const override { + return C::name; + } - virtual const component_type_t id() const { - return Component<C>::typeID(); - } + virtual const component_type_t id() const { + return Component<C>::typeID(); + } - virtual void construct(unsigned char* data) const override { - new (data) C(); + virtual void construct(unsigned char *data) const override { + new(data) C(); } - void* copyConstruct(const void* src) override { - const C* srcPtr = (C*)src; - return new C(*srcPtr); - } + void *copyConstruct(const void *src) override { + const C *srcPtr = (C *) src; + return new C(*srcPtr); + } - void* construct() const override { - return new C(); - } + void *construct() const override { + return new C(); + } - virtual void move(data_t* src, data_t* dest) const override { + virtual void move(data_t *src, data_t *dest) const override { assert(src != nullptr); assert(dest != nullptr); - new (&dest[0]) C(std::move(*reinterpret_cast<C*>(src))); + new(&dest[0]) C(std::move(*reinterpret_cast<C *>(src))); } - virtual void bulkMove(data_t* src, data_t* dest, std::size_t count) { - if ( std::is_trivially_copyable<C>::value ) { - std::memcpy( dest, src, count * size() ); + virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) { + if (std::is_trivially_copyable<C>::value) { + std::memcpy(dest, src, count * size()); } else { - unsigned char* srcPtr = src; - unsigned char* destPtr = dest; + unsigned char *srcPtr = src; + unsigned char *destPtr = dest; - for ( std::size_t i = 0; i < count; ++i ){ - new (destPtr) C(std::move(*reinterpret_cast<C*>(srcPtr))); + for (std::size_t i = 0; i < count; ++i) { + new(destPtr) C(std::move(*reinterpret_cast<C *>(srcPtr))); srcPtr += sizeof(C); destPtr += sizeof(C); } diff --git a/include/fggl/ecs/ecs.hpp b/include/fggl/ecs/ecs.hpp index e62b683..b467357 100644 --- a/include/fggl/ecs/ecs.hpp +++ b/include/fggl/ecs/ecs.hpp @@ -17,35 +17,35 @@ namespace fggl::ecs { struct Archetype { constexpr static unsigned int default_cap = 0; const archToken_t type; - std::vector<ComponentBase::data_t*> data; + std::vector<ComponentBase::data_t *> data; std::vector<std::size_t> dataSizes; std::vector<entity_t> entities; - Archetype(const archToken_t& type_a); + Archetype(const archToken_t &type_a); - inline archToken_t create( const component_type_t cid ) const { - assert( !contains(cid) ); + inline archToken_t create(const component_type_t cid) const { + assert(!contains(cid)); // create the new type auto newType = type; - newType.push_back( cid ); - std::sort( newType.begin(), newType.end() ); + newType.push_back(cid); + std::sort(newType.begin(), newType.end()); return newType; } - inline bool contains( const component_type_t cid ) const { - return ( std::find( type.begin(), type.end(), cid ) != type.end() ); + inline bool contains(const component_type_t cid) const { + return (std::find(type.begin(), type.end(), cid) != type.end()); } }; class ECS { - struct Record { - Archetype* archetype; - std::size_t index; - }; - using componentmap_t = std::unordered_map<component_type_t, ComponentBase*>; - using entitymap_t = std::unordered_map<entity_t, Record>; - using archetype_t = std::vector<Archetype*>; + struct Record { + Archetype *archetype; + std::size_t index; + }; + using componentmap_t = std::unordered_map<component_type_t, ComponentBase *>; + using entitymap_t = std::unordered_map<entity_t, Record>; + using archetype_t = std::vector<Archetype *>; public: ECS(); @@ -58,52 +58,52 @@ namespace fggl::ecs { template<class C> void registerComponent() { component_type_t type = Component<C>::typeID(); - if ( m_componentMap.find( type ) != m_componentMap.end() ) + if (m_componentMap.find(type) != m_componentMap.end()) return; - m_componentMap.emplace( type, new Component<C> ); + m_componentMap.emplace(type, new Component<C>); } template<class C> bool isComponentRegistered() { component_type_t type = Component<C>::typeID(); - return ( m_componentMap.find(type) != m_componentMap.end() ); + return (m_componentMap.find(type) != m_componentMap.end()); } template<class C, typename... Args> - C* addComponent(const entity_t& id, Args&&... args) { + C *addComponent(const entity_t &id, Args &&... args) { component_type_t type = Component<C>::typeID(); - assert( isComponentRegistered<C>() ); + assert(isComponentRegistered<C>()); - Record& record = m_entityArchtypes[id]; - Archetype* oldArch = record.archetype; - C* newComp = nullptr; - Archetype* newArch = nullptr; + Record &record = m_entityArchtypes[id]; + Archetype *oldArch = record.archetype; + C *newComp = nullptr; + Archetype *newArch = nullptr; - if ( !oldArch ) { + if (!oldArch) { archToken_t newID(1, type); - const ComponentBase* const newCompType = m_componentMap[ type ]; + const ComponentBase *const newCompType = m_componentMap[type]; // fetch type - newArch = getArchetype( newID ); - assert( newArch->type.size() == 1 ); + newArch = getArchetype(newID); + assert(newArch->type.size() == 1); // calculate if we have enouph space to allocate - std::size_t emplacementPos = ensureCapacity( newArch, 0, newCompType ); - newComp = new (&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...); + std::size_t emplacementPos = ensureCapacity(newArch, 0, newCompType); + newComp = new(&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...); } else { // check if the arch contains the component - if ( oldArch->contains(type) ) { + if (oldArch->contains(type)) { return nullptr; } // create a new archetype with the component - auto newID = oldArch->create( type ); - newArch = getArchetype( newID ); + auto newID = oldArch->create(type); + newArch = getArchetype(newID); // relocate the old data to the new archetype - for ( std::size_t j=0; j < newID.size(); ++j ){ + for (std::size_t j = 0; j < newID.size(); ++j) { const component_type_t compType = newID[j]; - const ComponentBase* const comp = m_componentMap.at(compType); + const ComponentBase *const comp = m_componentMap.at(compType); // TODO this seems a little suspect - surely we could allocate all blocks at once? @@ -111,81 +111,81 @@ namespace fggl::ecs { int newOffset = ensureCapacity(newArch, j, comp); int oldIdx = getComponentIdx(oldArch, compType); - if ( oldIdx != -1 ) { - assert( oldArch->contains(compType) ); + if (oldIdx != -1) { + assert(oldArch->contains(compType)); const std::size_t compSize = comp->size(); const std::size_t oldOffset = record.index * compSize; - comp->move( &oldArch->data[oldIdx][oldOffset], - &newArch->data[j][newOffset] ); - comp->destroy( &oldArch->data[oldIdx][oldOffset] ); + comp->move(&oldArch->data[oldIdx][oldOffset], + &newArch->data[j][newOffset]); + comp->destroy(&oldArch->data[oldIdx][oldOffset]); } else { - assert( !oldArch->contains(compType) ); - newComp = new (&newArch->data[j][newOffset]) - C(std::forward<Args>(args)...); + assert(!oldArch->contains(compType)); + newComp = new(&newArch->data[j][newOffset]) + C(std::forward<Args>(args)...); } } // ensure the old archetype is still contigious const int lastEnt = oldArch->entities.size() - 1; - if ( lastEnt != record.index ) { - for ( std::size_t i=0; i < oldArch->type.size(); ++i ) { + if (lastEnt != record.index) { + for (std::size_t i = 0; i < oldArch->type.size(); ++i) { const component_type_t typeID = oldArch->type[i]; - const ComponentBase* const comp = m_componentMap[typeID]; - const std::size_t& compSize = comp->size(); + const ComponentBase *const comp = m_componentMap[typeID]; + const std::size_t &compSize = comp->size(); // shift the empty record to the end of the list std::size_t slotOffset = record.index * compSize; std::size_t lastOffset = lastEnt * compSize; // if we're not the last entity, swap - if ( slotOffset != lastOffset ) { - comp->move( &oldArch->data[i][lastOffset], - &oldArch->data[i][slotOffset] ); - comp->destroy( &oldArch->data[i][lastOffset] ); + if (slotOffset != lastOffset) { + comp->move(&oldArch->data[i][lastOffset], + &oldArch->data[i][slotOffset]); + comp->destroy(&oldArch->data[i][lastOffset]); } } // fix the position - oldArch->entities[ record.index ] = oldArch->entities[ lastEnt ]; + oldArch->entities[record.index] = oldArch->entities[lastEnt]; } oldArch->entities.pop_back(); } // register the new data with the new archetype - newArch->entities.push_back( id ); + newArch->entities.push_back(id); record.index = newArch->entities.size() - 1; record.archetype = newArch; return newComp; } template<class C> - void removeComponent(const entity_t& entityId); + void removeComponent(const entity_t &entityId); template<class C> - bool hasComponent(const entity_t& entityId) const { + bool hasComponent(const entity_t &entityId) const { const component_type_t componentID = Component<C>::typeID(); - const auto* arch = m_entityArchtypes.at(entityId).archetype; - if ( arch == nullptr ) { + const auto *arch = m_entityArchtypes.at(entityId).archetype; + if (arch == nullptr) { return false; } - return ( std::find( arch->type.begin(), arch->type.end(), componentID) != - arch->type.end() ); + return (std::find(arch->type.begin(), arch->type.end(), componentID) != + arch->type.end()); } template<class C> - C* getComponent(const entity_t& entityId) { - assert( hasComponent<C>( entityId ) ); + C *getComponent(const entity_t &entityId) { + assert(hasComponent<C>(entityId)); const auto type = Component<C>::typeID(); - const ComponentBase* const newComp = m_componentMap[type]; + const ComponentBase *const newComp = m_componentMap[type]; const auto record = m_entityArchtypes.at(entityId); - const auto* arch = record.archetype; + const auto *arch = record.archetype; // JWR: linear search... seems a little suspect, they're ordered after all - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - if ( arch->type[i] == type ) { - return reinterpret_cast<C*>(& (arch->data[i][ record.index * newComp->size() ]) ); + for (std::size_t i = 0; i < arch->type.size(); ++i) { + if (arch->type[i] == type) { + return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()])); } } @@ -193,75 +193,75 @@ namespace fggl::ecs { } template<class C> - const C* getComponent(const entity_t& entityId) const { - assert( hasComponent<C>( entityId ) ); - const auto type = Component<C>::typeID(); - - const ComponentBase* const newComp = m_componentMap.at(type); - const auto record = m_entityArchtypes.at(entityId); - const auto* arch = record.archetype; - - // JWR: linear search... seems a little suspect, they're ordered after all - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - if ( arch->type[i] == type ) { - return reinterpret_cast<C*>(& (arch->data[i][ record.index * newComp->size() ]) ); - } - } - - return nullptr; + const C *getComponent(const entity_t &entityId) const { + assert(hasComponent<C>(entityId)); + const auto type = Component<C>::typeID(); + + const ComponentBase *const newComp = m_componentMap.at(type); + const auto record = m_entityArchtypes.at(entityId); + const auto *arch = record.archetype; + + // JWR: linear search... seems a little suspect, they're ordered after all + for (std::size_t i = 0; i < arch->type.size(); ++i) { + if (arch->type[i] == type) { + return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()])); + } + } + + return nullptr; } template<class... Cs> std::vector<entity_t> getEntityWith() const { // construct the key archToken_t key; - (key.push_back( Component<Cs>::typeID() ), ...); + (key.push_back(Component<Cs>::typeID()), ...); // entities std::vector<entity_t> entities; - for ( Archetype* arch : m_archetypes ) { - if ( std::includes( arch->type.begin(), arch->type.end(), key.begin(), key.end() ) ) { - if( !arch->entities.empty() ) { - entities.insert( entities.begin(), arch->entities.begin(), arch->entities.end() ); + for (Archetype *arch : m_archetypes) { + if (std::includes(arch->type.begin(), arch->type.end(), key.begin(), key.end())) { + if (!arch->entities.empty()) { + entities.insert(entities.begin(), arch->entities.begin(), arch->entities.end()); } } } return entities; } - + private: entitymap_t m_entityArchtypes; archetype_t m_archetypes; entity_t m_entityIDCounter; componentmap_t m_componentMap; - Archetype* getArchetype(const archToken_t& id); + Archetype *getArchetype(const archToken_t &id); - inline std::string arch2str(Archetype* arch) { + inline std::string arch2str(Archetype *arch) { std::string str; - for ( const auto& type : arch->type ) { - str += std::to_string( type ); + for (const auto &type : arch->type) { + str += std::to_string(type); } return str; } - inline std::size_t ensureCapacity(Archetype* arch, const int idx, const ComponentBase* const comp) { - const std::size_t& compSize = comp->size(); + inline std::size_t ensureCapacity(Archetype *arch, const int idx, const ComponentBase *const comp) { + const std::size_t &compSize = comp->size(); std::size_t currSize = arch->entities.size() * compSize; std::size_t newSize = currSize + compSize; std::size_t cap = arch->dataSizes[idx]; - if ( newSize > arch->dataSizes[idx] ) { + if (newSize > arch->dataSizes[idx]) { arch->dataSizes[idx] *= 2; arch->dataSizes[idx] += compSize; // copy data over - unsigned char* newData = new unsigned char[arch->dataSizes[idx]]; - for ( std::size_t e=0; e<arch->entities.size(); ++e ) { + unsigned char *newData = new unsigned char[arch->dataSizes[idx]]; + for (std::size_t e = 0; e < arch->entities.size(); ++e) { const int offset = e * compSize; - comp->move( &arch->data[idx][offset], &newData[offset] ); - comp->destroy( &arch->data[idx][offset] ); + comp->move(&arch->data[idx][offset], &newData[offset]); + comp->destroy(&arch->data[idx][offset]); } // free the old data and swap the pointers @@ -272,10 +272,11 @@ namespace fggl::ecs { return currSize; } - inline int getComponentIdx(const Archetype* arch, const component_type_t goal) { + inline int getComponentIdx(const Archetype *arch, const component_type_t goal) { // JWR could do binary search for speedup - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - if ( arch->type[i] == goal ) return i; + for (std::size_t i = 0; i < arch->type.size(); ++i) { + if (arch->type[i] == goal) + return i; } return -1; } diff --git a/include/fggl/ecs/utility.hpp b/include/fggl/ecs/utility.hpp index 323074e..7906e4f 100644 --- a/include/fggl/ecs/utility.hpp +++ b/include/fggl/ecs/utility.hpp @@ -22,6 +22,7 @@ namespace fggl::ecs { return idCounter; } }; + template<class T> IDType TypeIdGenerator<T>::m_count = 0; } diff --git a/include/fggl/ecs3/ecs.hpp b/include/fggl/ecs3/ecs.hpp index b920ca6..bdd1fe0 100644 --- a/include/fggl/ecs3/ecs.hpp +++ b/include/fggl/ecs3/ecs.hpp @@ -6,7 +6,7 @@ namespace fggl::ecs3 { - using World = prototype::World; + using World = prototype::World; } diff --git a/include/fggl/ecs3/fast/Container.h b/include/fggl/ecs3/fast/Container.h index 6910cc4..4b6a529 100644 --- a/include/fggl/ecs3/fast/Container.h +++ b/include/fggl/ecs3/fast/Container.h @@ -13,92 +13,91 @@ namespace fggl::ecs3 { - - class Container { - public: - const RecordIdentifier m_identifier; - Container(const TypeRegistry& reg, RecordIdentifier id) : - m_types(reg), - m_identifier( id ), - backingStore(nullptr), - m_capacity(0), - m_size(0) { - }; - - ~Container() { - delete[] backingStore; - } - - std::size_t create(); - - void remove(std::size_t pos); - - std::size_t expand(Container& other, std::size_t otherPos, component_type_t newComp); - - void contract(Container& other, std::size_t otherPos); - - void ensure(std::size_t size); - - inline unsigned char* data_raw(component_type_t type) { - auto seek_id = m_identifier.idx(type); - - // you asked for something I don't contain... - if ( seek_id == m_identifier.count ) { - std::cerr << "asked for " << type << " from " << m_identifier << std::endl; - assert( seek_id != m_identifier.count ); - return nullptr; - } - - // figure out the offset - return backingStore + offsets[seek_id]; - } - - template<typename T> - inline T* data() { - auto comp_id = Component<T>::typeID(); - return (T*)data_raw(comp_id); - } - - template<typename T> - T* set(std::size_t entity, T* compData) { - auto* comps = data<T>(); - auto entityPos = idx(entity); - - auto compMeta = m_types.template meta<T>(); - - unsigned char* usrPtr = (unsigned char*)&comps[entityPos]; - compMeta->destroy(usrPtr); - compMeta->move((unsigned char*)compData, usrPtr); - - return &comps[entityPos]; - } - - [[nodiscard]] - inline std::size_t size() const { - return m_size; - } - - inline std::size_t idx(entity_t entity) { - auto* entityData = data<EntityMeta>(); - for (int i=0; i<m_size; i++) { - if ( entityData[i].id == entity ) { - return i; - } - } - return m_size; - } - - private: - const TypeRegistry& m_types; - unsigned char* backingStore; - std::size_t offsets[RecordIdentifier::MAX_COMPS]{}; - std::size_t m_size; - std::size_t m_capacity; - - void move(std::size_t newPos, Container& oldContainer, std::size_t oldPos); - }; + class Container { + public: + const RecordIdentifier m_identifier; + + Container(const TypeRegistry ®, RecordIdentifier id) : + m_types(reg), + m_identifier(id), + backingStore(nullptr), + m_capacity(0), + m_size(0) { + }; + + ~Container() { + delete[] backingStore; + } + + std::size_t create(); + + void remove(std::size_t pos); + + std::size_t expand(Container &other, std::size_t otherPos, component_type_t newComp); + + void contract(Container &other, std::size_t otherPos); + + void ensure(std::size_t size); + + inline unsigned char *data_raw(component_type_t type) { + auto seek_id = m_identifier.idx(type); + + // you asked for something I don't contain... + if (seek_id == m_identifier.count) { + std::cerr << "asked for " << type << " from " << m_identifier << std::endl; + assert(seek_id != m_identifier.count); + return nullptr; + } + + // figure out the offset + return backingStore + offsets[seek_id]; + } + + template<typename T> + inline T *data() { + auto comp_id = Component<T>::typeID(); + return (T *) data_raw(comp_id); + } + + template<typename T> + T *set(std::size_t entity, T *compData) { + auto *comps = data<T>(); + auto entityPos = idx(entity); + + auto compMeta = m_types.template meta<T>(); + + unsigned char *usrPtr = (unsigned char *) &comps[entityPos]; + compMeta->destroy(usrPtr); + compMeta->move((unsigned char *) compData, usrPtr); + + return &comps[entityPos]; + } + + [[nodiscard]] + inline std::size_t size() const { + return m_size; + } + + inline std::size_t idx(entity_t entity) { + auto *entityData = data<EntityMeta>(); + for (int i = 0; i < m_size; i++) { + if (entityData[i].id == entity) { + return i; + } + } + return m_size; + } + + private: + const TypeRegistry &m_types; + unsigned char *backingStore; + std::size_t offsets[RecordIdentifier::MAX_COMPS]{}; + std::size_t m_size; + std::size_t m_capacity; + + void move(std::size_t newPos, Container &oldContainer, std::size_t oldPos); + }; } - #endif //FGGL_ECS3_CONTAINER_H diff --git a/include/fggl/ecs3/fast/ecs.hpp b/include/fggl/ecs3/fast/ecs.hpp index 3d4ea7c..542106c 100644 --- a/include/fggl/ecs3/fast/ecs.hpp +++ b/include/fggl/ecs3/fast/ecs.hpp @@ -17,121 +17,121 @@ namespace fggl::ecs3::fast { - using entity_t = unsigned int; - constexpr entity_t NULL_ENTITY = 0; - - class World { - public: - explicit World(TypeRegistry& reg) : m_registry(reg), m_last(NULL_ENTITY) {} - - entity_t create() { - auto next = m_last++; - - auto arch = make_id(1, Component<EntityMeta>::typeID()); - auto& container = getContainer(arch); - - m_entities[next] = container.m_identifier; - - auto pos = container.create(); - auto* entityMeta = container.data<EntityMeta>(); - entityMeta[pos].id = next; - - return next; - } - - void remove(entity_t entity) { - auto arch = m_entities.at(entity); - auto container = m_records.at(arch); - - auto entPos = container.idx(entity); - container.remove(entPos); - } - - inline Container& getContainer(RecordIdentifier& arch) { - try { - return m_records.at(arch); - } catch (std::out_of_range& e) { - auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch})); - return v.first->second; - } - } - - template<typename T> - T* add(const entity_t entity){ - auto currArch = m_entities.at( entity ); - auto newArch = currArch.with<T>(); - m_entities[entity] = newArch; - - auto& oldContainer = m_records.at(currArch); - auto& newContainer = getContainer(newArch); - - auto oldPos = oldContainer.idx(entity); - - auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID()); - auto* data = newContainer.template data<T>(); - return &data[newPos]; - } - - template<typename T> - T* set(const entity_t entity, T* record) { - auto currArch = m_entities.at( entity ); - - // check we already have that component type... - if ( currArch.idx(Component<T>::typeID()) == currArch.count ) { - add<T>(entity); - currArch = m_entities.at( entity ); - } - - auto& container = m_records.at(currArch); - auto pos = container.idx(entity); - return container.set<T>(pos, record); - } - - template<typename T> - T* get(entity_t entity) { - auto currArch = m_entities.at( entity ); - auto pos = currArch.idx(entity); - auto& container = m_records.at(currArch); - - auto* data = container.template data<T>(); - return &data[pos]; - } - - template<typename T> - const T* get(entity_t entity) const { - auto currArch = m_entities.at( entity ); - auto pos = currArch.idx(entity); - auto& container = m_records.at(currArch); - - auto* data = container.template data<T>(); - return &data[pos]; - } - - template<typename T> - void remove(entity_t entity) { - auto currArch = m_entities.at( entity ); - auto newArch = currArch.without<T>(); - - auto& oldContainer = m_records[currArch]; - auto& newContainer = m_records[newArch]; - - auto oldPos = oldContainer.idx(entity); - auto newPos = newContainer.create(); - - m_records[newArch].contract(newPos, oldContainer, oldPos); - } - - template<typename... T> - std::vector<entity_t> findMatching() const { - return {}; - } - - private: - TypeRegistry& m_registry; - std::map<RecordIdentifier, Container> m_records; - std::map<entity_t, RecordIdentifier> m_entities; - entity_t m_last{}; - }; + using entity_t = unsigned int; + constexpr entity_t NULL_ENTITY = 0; + + class World { + public: + explicit World(TypeRegistry ®) : m_registry(reg), m_last(NULL_ENTITY) {} + + entity_t create() { + auto next = m_last++; + + auto arch = make_id(1, Component<EntityMeta>::typeID()); + auto &container = getContainer(arch); + + m_entities[next] = container.m_identifier; + + auto pos = container.create(); + auto *entityMeta = container.data<EntityMeta>(); + entityMeta[pos].id = next; + + return next; + } + + void remove(entity_t entity) { + auto arch = m_entities.at(entity); + auto container = m_records.at(arch); + + auto entPos = container.idx(entity); + container.remove(entPos); + } + + inline Container &getContainer(RecordIdentifier &arch) { + try { + return m_records.at(arch); + } catch (std::out_of_range &e) { + auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch})); + return v.first->second; + } + } + + template<typename T> + T *add(const entity_t entity) { + auto currArch = m_entities.at(entity); + auto newArch = currArch.with<T>(); + m_entities[entity] = newArch; + + auto &oldContainer = m_records.at(currArch); + auto &newContainer = getContainer(newArch); + + auto oldPos = oldContainer.idx(entity); + + auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID()); + auto *data = newContainer.template data<T>(); + return &data[newPos]; + } + + template<typename T> + T *set(const entity_t entity, T *record) { + auto currArch = m_entities.at(entity); + + // check we already have that component type... + if (currArch.idx(Component<T>::typeID()) == currArch.count) { + add<T>(entity); + currArch = m_entities.at(entity); + } + + auto &container = m_records.at(currArch); + auto pos = container.idx(entity); + return container.set<T>(pos, record); + } + + template<typename T> + T *get(entity_t entity) { + auto currArch = m_entities.at(entity); + auto pos = currArch.idx(entity); + auto &container = m_records.at(currArch); + + auto *data = container.template data<T>(); + return &data[pos]; + } + + template<typename T> + const T *get(entity_t entity) const { + auto currArch = m_entities.at(entity); + auto pos = currArch.idx(entity); + auto &container = m_records.at(currArch); + + auto *data = container.template data<T>(); + return &data[pos]; + } + + template<typename T> + void remove(entity_t entity) { + auto currArch = m_entities.at(entity); + auto newArch = currArch.without<T>(); + + auto &oldContainer = m_records[currArch]; + auto &newContainer = m_records[newArch]; + + auto oldPos = oldContainer.idx(entity); + auto newPos = newContainer.create(); + + m_records[newArch].contract(newPos, oldContainer, oldPos); + } + + template<typename... T> + std::vector<entity_t> findMatching() const { + return {}; + } + + private: + TypeRegistry &m_registry; + std::map<RecordIdentifier, Container> m_records; + std::map<entity_t, RecordIdentifier> m_entities; + entity_t m_last{}; + }; } diff --git a/include/fggl/ecs3/module/module.h b/include/fggl/ecs3/module/module.h index a71ae98..c65ea22 100644 --- a/include/fggl/ecs3/module/module.h +++ b/include/fggl/ecs3/module/module.h @@ -13,63 +13,63 @@ namespace fggl::ecs3 { - class Module { - public: - virtual ~Module() = default; - - [[nodiscard]] virtual std::string name() const = 0; - - virtual void onLoad(ModuleManager& manager, TypeRegistry& tr) {}; - - virtual void onUpdate(); - virtual void onFrameStart(); - virtual void onFrameEnd(); - }; - - class ModuleManager { - public: - explicit ModuleManager(TypeRegistry& types) : m_types(types), m_modules(){ } - ~ModuleManager() = default; - - template<typename C, typename... Args> - std::shared_ptr<C> load(Args&... args) { - auto ptr = std::make_shared<C>(args...); - m_modules[ptr->name()] = ptr; - - ptr->onLoad(*this, m_types); - //spdlog::info("loaded ECS module: {}", ptr->name()); - return ptr; - } - - template<typename C> - void onAdd(const callback_t& cb) { - m_types.callbackAdd( Component<C>::typeID(), cb); - } - - void onUpdate() { - for ( auto& [id,ptr] : m_modules ) { - ptr->onUpdate(); - } - } - - void onFrameStart() { - for ( auto& [id,ptr] : m_modules ) { - ptr->onFrameStart(); - } - } - - void onFrameEnd() { - for ( auto& [id,ptr] : m_modules ) { - ptr->onFrameEnd(); - } - } - - private: - TypeRegistry& m_types; - std::map<std::string, std::shared_ptr<Module>> m_modules; - }; + class Module { + public: + virtual ~Module() = default; + + [[nodiscard]] virtual std::string name() const = 0; + + virtual void onLoad(ModuleManager &manager, TypeRegistry &tr) {}; + + virtual void onUpdate(); + virtual void onFrameStart(); + virtual void onFrameEnd(); + }; + + class ModuleManager { + public: + explicit ModuleManager(TypeRegistry &types) : m_types(types), m_modules() {} + + ~ModuleManager() = default; + + template<typename C, typename... Args> + std::shared_ptr<C> load(Args &... args) { + auto ptr = std::make_shared<C>(args...); + m_modules[ptr->name()] = ptr; + + ptr->onLoad(*this, m_types); + //spdlog::info("loaded ECS module: {}", ptr->name()); + return ptr; + } + + template<typename C> + void onAdd(const callback_t &cb) { + m_types.callbackAdd(Component<C>::typeID(), cb); + } + + void onUpdate() { + for (auto &[id, ptr] : m_modules) { + ptr->onUpdate(); + } + } + + void onFrameStart() { + for (auto &[id, ptr] : m_modules) { + ptr->onFrameStart(); + } + } + + void onFrameEnd() { + for (auto &[id, ptr] : m_modules) { + ptr->onFrameEnd(); + } + } + + private: + TypeRegistry &m_types; + std::map<std::string, std::shared_ptr<Module>> m_modules; + }; } - #endif //FGGL_ECS3_MODULE_H diff --git a/include/fggl/ecs3/prototype/world.h b/include/fggl/ecs3/prototype/world.h index 1ffcaec..314668d 100644 --- a/include/fggl/ecs3/prototype/world.h +++ b/include/fggl/ecs3/prototype/world.h @@ -15,207 +15,207 @@ */ namespace fggl::ecs3::prototype { - - class Entity { - public: - bool m_abstract; - - explicit Entity(entity_t id) : m_id(id), m_abstract(false) {}; - Entity(const Entity& entity) : m_id(entity.m_id), m_components(entity.m_components) { - //spdlog::info("entity created fro copy: {}", m_id); - } - ~Entity() = default; - - template<typename C> - C* add() { - C* ptr = new C(); - m_components[Component<C>::typeID()] = ptr; - return ptr; - } - - void* add(std::shared_ptr<ComponentBase> t) { - void* ptr = t->construct(); - m_components[t->id()] = ptr; - return ptr; - } - - template<typename C> - C* set(const C* ptr) { - C* newPtr = new C(*ptr); - m_components[Component<C>::typeID()] = newPtr; - return newPtr; - } - - void* set(const std::shared_ptr<ComponentBase>& t, const void* ptr) { - void* newPtr = t->copyConstruct(ptr); - m_components[t->id()] = newPtr; - return newPtr; - } - - template<typename C> - C* get() { - void* ptr = m_components.at(Component<C>::typeID()); - return (C*)ptr; - } - - inline void* get(component_type_t t){ - return m_components.at(t); - } - - std::vector<component_type_t> getComponentIDs() { - std::vector<component_type_t> comps{}; - for (auto& [k,_] : m_components) { - comps.push_back(k); - } - return comps; - } - - bool hasComponents(std::vector<component_type_t>& Cs) { - for (auto c : Cs) { - if ( m_components.find(c) == m_components.end() ) { - return false; - } - } - return true; - } - - private: - entity_t m_id; - std::map<component_type_t, void*> m_components; - }; - - - class World { - public: - explicit World(TypeRegistry& reg) : m_types(reg), m_next(1), m_entities() {}; - ~World() = default; - - entity_t create(bool abstract) { - auto nextID = m_next++; - m_entities.emplace(nextID, nextID); - - auto& entity = m_entities.at(nextID); - entity.m_abstract = abstract; - - // meta data - auto* meta = entity.add<ecs3::EntityMeta>(); - meta->id = nextID; - meta->abstract = abstract; - - return nextID; - } - - entity_t copy(entity_t prototype) { - auto clone = create(false); - - auto components = getComponents(prototype); - for (auto component : components) { - auto protoComp = get(prototype, component); - set(clone, component, protoComp); - } - - return clone; - } - - void destroy(entity_t entity) { - // TOOD resolve and clean components - m_entities.erase(entity); - } - - inline TypeRegistry& types() { - return m_types; - } - - std::vector<entity_t> all() { - std::vector<entity_t> entities{}; - for( auto& [eid, entity] : m_entities) { - entities.push_back(eid); - } - return entities; - } - - std::vector<component_type_t> getComponents(entity_t entityID){ - std::vector<component_type_t> components{}; - auto& entity = m_entities.at(entityID); - auto comps = entity.getComponentIDs(); - for (auto id : comps) { - components.push_back(id); - } - return components; - } - - template<typename... Cs> - std::vector<entity_t> findMatching() { - // construct the key - std::vector<ecs::component_type_t> key; - (key.push_back( Component<Cs>::typeID() ), ...); - - // entities - std::vector<entity_t> entities{}; - for( auto& [eid, entity] : m_entities) { - if ( entity.hasComponents(key) && !entity.m_abstract ) { - entities.push_back(eid); - } - } - - return entities; - } - - template<typename C> - C* add(entity_t entity_id) { - //spdlog::info("component '{}' added to '{}'", C::name, entity_id); - auto& entity = m_entities.at(entity_id); - auto comp = entity.template add<C>(); - - m_types.fireAdd(this, entity_id, Component<C>::typeID()); - return comp; - } - - void* add(entity_t entity_id, component_type_t component_id) { - auto meta = m_types.meta(component_id); - auto& entity = m_entities.at(entity_id); - - void* ptr = entity.add(meta); - m_types.fireAdd(this, entity_id, meta->id()); - return ptr; - } - - template<typename C> - C* set(entity_t entity_id, const C* ptr) { - //spdlog::info("component '{}' set on '{}'", C::name, entity_id); - auto& entity = m_entities.at(entity_id); - auto comp = entity.set<C>(ptr); - - m_types.fireAdd(this, entity_id, Component<C>::typeID()); - return comp; - } - - void* set(entity_t entity_id, component_type_t cid, const void* ptr){ - auto& entity = m_entities.at(entity_id); - auto cMeta = m_types.meta(cid); - - auto comp = entity.set(cMeta, ptr); - m_types.fireAdd(this, entity_id, cid); - return comp; - } - - template<typename C> - C* get(entity_t entity_id) { - auto& entity = m_entities.at(entity_id); - return entity.get<C>(); - } - - void* get(entity_t entity_id, component_type_t t){ - auto& entity = m_entities.at(entity_id); - return entity.get(t); - } - - private: - TypeRegistry& m_types; - entity_t m_next; - std::map<entity_t, Entity> m_entities; - - }; + class Entity { + public: + bool m_abstract; + + explicit Entity(entity_t id) : m_id(id), m_abstract(false) {}; + + Entity(const Entity &entity) : m_id(entity.m_id), m_components(entity.m_components) { + //spdlog::info("entity created fro copy: {}", m_id); + } + + ~Entity() = default; + + template<typename C> + C *add() { + C *ptr = new C(); + m_components[Component<C>::typeID()] = ptr; + return ptr; + } + + void *add(std::shared_ptr<ComponentBase> t) { + void *ptr = t->construct(); + m_components[t->id()] = ptr; + return ptr; + } + + template<typename C> + C *set(const C *ptr) { + C *newPtr = new C(*ptr); + m_components[Component<C>::typeID()] = newPtr; + return newPtr; + } + + void *set(const std::shared_ptr<ComponentBase> &t, const void *ptr) { + void *newPtr = t->copyConstruct(ptr); + m_components[t->id()] = newPtr; + return newPtr; + } + + template<typename C> + C *get() { + void *ptr = m_components.at(Component<C>::typeID()); + return (C *) ptr; + } + + inline void *get(component_type_t t) { + return m_components.at(t); + } + + std::vector<component_type_t> getComponentIDs() { + std::vector<component_type_t> comps{}; + for (auto &[k, _] : m_components) { + comps.push_back(k); + } + return comps; + } + + bool hasComponents(std::vector<component_type_t> &Cs) { + for (auto c : Cs) { + if (m_components.find(c) == m_components.end()) { + return false; + } + } + return true; + } + + private: + entity_t m_id; + std::map<component_type_t, void *> m_components; + }; + + class World { + public: + explicit World(TypeRegistry ®) : m_types(reg), m_next(1), m_entities() {}; + ~World() = default; + + entity_t create(bool abstract) { + auto nextID = m_next++; + m_entities.emplace(nextID, nextID); + + auto &entity = m_entities.at(nextID); + entity.m_abstract = abstract; + + // meta data + auto *meta = entity.add<ecs3::EntityMeta>(); + meta->id = nextID; + meta->abstract = abstract; + + return nextID; + } + + entity_t copy(entity_t prototype) { + auto clone = create(false); + + auto components = getComponents(prototype); + for (auto component : components) { + auto protoComp = get(prototype, component); + set(clone, component, protoComp); + } + + return clone; + } + + void destroy(entity_t entity) { + // TOOD resolve and clean components + m_entities.erase(entity); + } + + inline TypeRegistry &types() { + return m_types; + } + + std::vector<entity_t> all() { + std::vector<entity_t> entities{}; + for (auto &[eid, entity] : m_entities) { + entities.push_back(eid); + } + return entities; + } + + std::vector<component_type_t> getComponents(entity_t entityID) { + std::vector<component_type_t> components{}; + auto &entity = m_entities.at(entityID); + auto comps = entity.getComponentIDs(); + for (auto id : comps) { + components.push_back(id); + } + return components; + } + + template<typename... Cs> + std::vector<entity_t> findMatching() { + // construct the key + std::vector<ecs::component_type_t> key; + (key.push_back(Component<Cs>::typeID()), ...); + + // entities + std::vector<entity_t> entities{}; + for (auto &[eid, entity] : m_entities) { + if (entity.hasComponents(key) && !entity.m_abstract) { + entities.push_back(eid); + } + } + + return entities; + } + + template<typename C> + C *add(entity_t entity_id) { + //spdlog::info("component '{}' added to '{}'", C::name, entity_id); + auto &entity = m_entities.at(entity_id); + auto comp = entity.template add<C>(); + + m_types.fireAdd(this, entity_id, Component<C>::typeID()); + return comp; + } + + void *add(entity_t entity_id, component_type_t component_id) { + auto meta = m_types.meta(component_id); + auto &entity = m_entities.at(entity_id); + + void *ptr = entity.add(meta); + m_types.fireAdd(this, entity_id, meta->id()); + return ptr; + } + + template<typename C> + C *set(entity_t entity_id, const C *ptr) { + //spdlog::info("component '{}' set on '{}'", C::name, entity_id); + auto &entity = m_entities.at(entity_id); + auto comp = entity.set<C>(ptr); + + m_types.fireAdd(this, entity_id, Component<C>::typeID()); + return comp; + } + + void *set(entity_t entity_id, component_type_t cid, const void *ptr) { + auto &entity = m_entities.at(entity_id); + auto cMeta = m_types.meta(cid); + + auto comp = entity.set(cMeta, ptr); + m_types.fireAdd(this, entity_id, cid); + return comp; + } + + template<typename C> + C *get(entity_t entity_id) { + auto &entity = m_entities.at(entity_id); + return entity.get<C>(); + } + + void *get(entity_t entity_id, component_type_t t) { + auto &entity = m_entities.at(entity_id); + return entity.get(t); + } + + private: + TypeRegistry &m_types; + entity_t m_next; + std::map<entity_t, Entity> m_entities; + + }; } diff --git a/include/fggl/ecs3/types.hpp b/include/fggl/ecs3/types.hpp index 4cce5ac..f6fe4e5 100644 --- a/include/fggl/ecs3/types.hpp +++ b/include/fggl/ecs3/types.hpp @@ -19,144 +19,145 @@ namespace fggl::ecs3 { using namespace fggl::ecs; }; - namespace prototype { - class World; - } - - using fggl::ecs::component_type_t; - class ModuleManager; - - using callback_t = std::function<void(prototype::World*, ecs3::entity_t)>; - struct TypeCallbacks { - std::vector<callback_t> add; - }; - - // core component types - struct EntityMeta { - constexpr static const char name[] = "meta"; - entity_t id; - bool abstract; - }; - - struct RecordIdentifier { - constexpr static std::size_t MAX_COMPS = 32; - component_type_t types[MAX_COMPS]; - std::size_t count; - - [[nodiscard]] - inline std::size_t idx(component_type_t t) const { - return utils::search(types, count, t); - } - - template<typename T> - [[nodiscard]] - RecordIdentifier with() const { - // check the caller wasn't a muppet - const auto typeID = ecs::Component<T>::typeID(); - if ( idx(typeID) != count ) { - return *this; - } - - RecordIdentifier re{}; - re.count = count + 1; - re.types[ count ] = ecs::Component<T>::typeID(); - - // add old types - for (std::size_t i = 0; i < count; ++i) { - re.types[i] = types[i]; - } - std::sort( re.types, re.types + re.count ); - return re; - } - - template<typename T> - [[nodiscard]] - RecordIdentifier without() const { - // check the caller wasn't a muppet - const auto typeID = ecs::Component<T>::typeID(); - const auto typeIdx = idx(typeID); - if ( typeIdx == count ) { - return *this; - } - - RecordIdentifier re{}; - re.count = count - 1; - - // add old types - for (std::size_t i = 0, j = 0; i < count; ++i) { - if (typeIdx != i) { - re.types[j] = types[i]; - j++; - } - } - std::sort( re.types, re.types + re.count ); - return re; - } - - bool operator<(const RecordIdentifier& other) const { - if ( count < other.count) { - return true; - } else if ( count > other.count ) { - return false; - } else { - for (int i = 0; i < count; i++) { - if ( types[i] != other.types[i] ) { - return types[i] < other.types[i]; - } - } - return false; - } - } - - bool operator==(const RecordIdentifier& arg) const { - if ( arg.count != count ) { - return false; - } - - for (int i=0; i<count; i++) { - if ( types[i] != arg.types[i] ) { - return false; - } - } - return true; - } - - bool operator!=(const RecordIdentifier& arg) const { - return !( *this == arg ); - } - }; - - std::ostream& operator<<(std::ostream& out, RecordIdentifier const& curr); - - inline RecordIdentifier make_id(std::size_t count, ...) { - assert(count < RecordIdentifier::MAX_COMPS); - - RecordIdentifier re{}; - - std::va_list args; - va_start(args, count); - for ( std::size_t i = 0; i < count; ++i ) { - re.types[i] = va_arg(args, component_type_t); - } - va_end(args); - re.count = count; - std::sort( re.types, re.types + count ); - - return re; - } + namespace prototype { + class World; + } + + using fggl::ecs::component_type_t; + + class ModuleManager; + + using callback_t = std::function<void(prototype::World *, ecs3::entity_t)>; + struct TypeCallbacks { + std::vector<callback_t> add; + }; + + // core component types + struct EntityMeta { + constexpr static const char name[] = "meta"; + entity_t id; + bool abstract; + }; + + struct RecordIdentifier { + constexpr static std::size_t MAX_COMPS = 32; + component_type_t types[MAX_COMPS]; + std::size_t count; + + [[nodiscard]] + inline std::size_t idx(component_type_t t) const { + return utils::search(types, count, t); + } + + template<typename T> + [[nodiscard]] + RecordIdentifier with() const { + // check the caller wasn't a muppet + const auto typeID = ecs::Component<T>::typeID(); + if (idx(typeID) != count) { + return *this; + } + + RecordIdentifier re{}; + re.count = count + 1; + re.types[count] = ecs::Component<T>::typeID(); + + // add old types + for (std::size_t i = 0; i < count; ++i) { + re.types[i] = types[i]; + } + std::sort(re.types, re.types + re.count); + return re; + } + + template<typename T> + [[nodiscard]] + RecordIdentifier without() const { + // check the caller wasn't a muppet + const auto typeID = ecs::Component<T>::typeID(); + const auto typeIdx = idx(typeID); + if (typeIdx == count) { + return *this; + } + + RecordIdentifier re{}; + re.count = count - 1; + + // add old types + for (std::size_t i = 0, j = 0; i < count; ++i) { + if (typeIdx != i) { + re.types[j] = types[i]; + j++; + } + } + std::sort(re.types, re.types + re.count); + return re; + } + + bool operator<(const RecordIdentifier &other) const { + if (count < other.count) { + return true; + } else if (count > other.count) { + return false; + } else { + for (int i = 0; i < count; i++) { + if (types[i] != other.types[i]) { + return types[i] < other.types[i]; + } + } + return false; + } + } + + bool operator==(const RecordIdentifier &arg) const { + if (arg.count != count) { + return false; + } + + for (int i = 0; i < count; i++) { + if (types[i] != arg.types[i]) { + return false; + } + } + return true; + } + + bool operator!=(const RecordIdentifier &arg) const { + return !(*this == arg); + } + }; + + std::ostream &operator<<(std::ostream &out, RecordIdentifier const &curr); + + inline RecordIdentifier make_id(std::size_t count, ...) { + assert(count < RecordIdentifier::MAX_COMPS); + + RecordIdentifier re{}; + + std::va_list args; + va_start(args, count); + for (std::size_t i = 0; i < count; ++i) { + re.types[i] = va_arg(args, component_type_t); + } + va_end(args); + re.count = count; + std::sort(re.types, re.types + count); + + return re; + } class TypeRegistry { public: - TypeRegistry() : m_last_virtual(9000), m_callbacks() { - // core types always exist - make<EntityMeta>(); - } - + TypeRegistry() : m_last_virtual(9000), m_callbacks() { + // core types always exist + make<EntityMeta>(); + } + template<typename T> void make() { auto type_id = Component<T>::typeID(); - if ( m_types.find( type_id ) != m_types.end() ) + if (m_types.find(type_id) != m_types.end()) return; m_types[type_id] = std::make_shared<Component<T>>(); } @@ -164,52 +165,52 @@ namespace fggl::ecs3 { template<typename T> bool exists() { auto type_id = Component<T>::typeID(); - return m_types.find( type_id ) != m_types.end(); + return m_types.find(type_id) != m_types.end(); } template<typename T> std::shared_ptr<fggl::ecs::ComponentBase> meta() const { auto type_id = Component<T>::typeID(); - return m_types.at( type_id ); + return m_types.at(type_id); } inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const { - return m_types.at( type_id ); + return m_types.at(type_id); } - inline component_type_t find(const char* name) const { - for (auto& [type, meta] : m_types) { - if ( std::strcmp(name, meta->name()) == 0) { - return type; - } - } - return 0; - } + inline component_type_t find(const char *name) const { + for (auto &[type, meta] : m_types) { + if (std::strcmp(name, meta->name()) == 0) { + return type; + } + } + return 0; + } inline void make_virtual(std::shared_ptr<ComponentBase> vtype) { auto type_id = m_last_virtual++; m_types[type_id] = std::move(vtype); } - void callbackAdd(component_type_t component, const callback_t& callback) { - m_callbacks[component].add.push_back(callback); - } + void callbackAdd(component_type_t component, const callback_t &callback) { + m_callbacks[component].add.push_back(callback); + } - void fireAdd(prototype::World* world, entity_t entity, component_type_t type) { - try { - auto& callbacks = m_callbacks.at(type).add; - for ( auto& callback : callbacks) { - callback(world, entity); - } - } catch ( std::out_of_range& e) { - //spdlog::debug("no callbacks for {}", m_types[type]->name()); - } - } + void fireAdd(prototype::World *world, entity_t entity, component_type_t type) { + try { + auto &callbacks = m_callbacks.at(type).add; + for (auto &callback : callbacks) { + callback(world, entity); + } + } catch (std::out_of_range &e) { + //spdlog::debug("no callbacks for {}", m_types[type]->name()); + } + } private: std::unordered_map<component_type_t, std::shared_ptr<fggl::ecs::ComponentBase>> m_types; component_type_t m_last_virtual; - std::map<component_type_t, TypeCallbacks> m_callbacks; + std::map<component_type_t, TypeCallbacks> m_callbacks; }; }; diff --git a/include/fggl/ecs3/utils.hpp b/include/fggl/ecs3/utils.hpp index 0881c12..3ebf17f 100644 --- a/include/fggl/ecs3/utils.hpp +++ b/include/fggl/ecs3/utils.hpp @@ -6,26 +6,26 @@ namespace fggl::utils { template<typename T> - std::size_t search(const T* data, const std::size_t size, const T& v) { - // empty list == not found - if ( size == 0) { - return size; - } + std::size_t search(const T *data, const std::size_t size, const T &v) { + // empty list == not found + if (size == 0) { + return size; + } - std::size_t left = 0; - std::size_t right = size - 1; - while ( left <= right ) { - std::size_t m = (left+right) / 2; - if ( data[m] == v ) { - return m; - } else if ( v < data[m] ) { - if ( m == 0 ) { - return size; - } - right = m - 1; - } else { - left = m + 1; - } + std::size_t left = 0; + std::size_t right = size - 1; + while (left <= right) { + std::size_t m = (left + right) / 2; + if (data[m] == v) { + return m; + } else if (v < data[m]) { + if (m == 0) { + return size; + } + right = m - 1; + } else { + left = m + 1; + } } return size; diff --git a/include/fggl/gfx/atlas.hpp b/include/fggl/gfx/atlas.hpp index 7eeef4e..50362ae 100644 --- a/include/fggl/gfx/atlas.hpp +++ b/include/fggl/gfx/atlas.hpp @@ -6,45 +6,45 @@ namespace fggl::gfx { - struct Bounds2D { - glm::ivec2 pos; - glm::ivec2 size; - }; - bool pack( std::vector<Bounds2D>& bounds); - - template<typename T> - class ImageAtlas { - public: - struct SubImage { - const T name; - Bounds2D bounds; - }; - - SubImage& locate( int id ) const; - SubImage& locate( const T& name ) const; - - static ImageAtlas<T>* pack( std::vector<SubImage>& images ) { - // extract bounds - std::vector<Bounds2D> bounds; - for( auto& image : images ) { - bounds.push_back( image.bounds ); - } - - // allocate the bounds back - auto result = gfx::pack( bounds ); - for ( std::size_t i = 0; i < images.size(); i++ ) { - images[i].bounds = bounds[i]; - } - - return new ImageAtlas<T>(images); - } - - private: - ImageAtlas(const std::vector<SubImage>& images) : m_images( images ) { - } - - std::vector<SubImage> m_images; - }; + struct Bounds2D { + glm::ivec2 pos; + glm::ivec2 size; + }; + bool pack(std::vector<Bounds2D> &bounds); + + template<typename T> + class ImageAtlas { + public: + struct SubImage { + const T name; + Bounds2D bounds; + }; + + SubImage &locate(int id) const; + SubImage &locate(const T &name) const; + + static ImageAtlas<T> *pack(std::vector<SubImage> &images) { + // extract bounds + std::vector<Bounds2D> bounds; + for (auto &image : images) { + bounds.push_back(image.bounds); + } + + // allocate the bounds back + auto result = gfx::pack(bounds); + for (std::size_t i = 0; i < images.size(); i++) { + images[i].bounds = bounds[i]; + } + + return new ImageAtlas<T>(images); + } + + private: + ImageAtlas(const std::vector<SubImage> &images) : m_images(images) { + } + + std::vector<SubImage> m_images; + }; }; diff --git a/include/fggl/gfx/camera.hpp b/include/fggl/gfx/camera.hpp index c1eaa43..3895e9e 100644 --- a/include/fggl/gfx/camera.hpp +++ b/include/fggl/gfx/camera.hpp @@ -6,7 +6,7 @@ namespace fggl::gfx { struct Camera { - constexpr const static char name[] = "Camera"; + constexpr const static char name[] = "Camera"; math::vec3 target = math::vec3(0.0f, 0.0f, 0.0f); float aspectRatio = 1280.0f / 720.0f; float fov = glm::radians(45.0f); diff --git a/include/fggl/gfx/common.hpp b/include/fggl/gfx/common.hpp index 71101f5..82845e2 100644 --- a/include/fggl/gfx/common.hpp +++ b/include/fggl/gfx/common.hpp @@ -14,12 +14,13 @@ namespace fggl::gfx { struct StaticMesh { - constexpr static const char name[] = "StaticMesh"; + constexpr static const char name[] = "StaticMesh"; data::Mesh mesh; std::string pipeline; inline StaticMesh() : mesh(), pipeline() {} - inline StaticMesh(const data::Mesh& aMesh, std::string aPipeline) : + + inline StaticMesh(const data::Mesh &aMesh, std::string aPipeline) : mesh(aMesh), pipeline(std::move(aPipeline)) {} }; diff --git a/include/fggl/gfx/compat.hpp b/include/fggl/gfx/compat.hpp index 3a1b370..32b766d 100644 --- a/include/fggl/gfx/compat.hpp +++ b/include/fggl/gfx/compat.hpp @@ -25,28 +25,27 @@ namespace fggl::gfx { GlfwContext context; inline explicit - ecsGlfwModule(std::shared_ptr<fggl::input::Input> inputs) : context(std::move(inputs) ) { + ecsGlfwModule(std::shared_ptr<fggl::input::Input> inputs) : context(std::move(inputs)) { } - inline - std::unique_ptr<GlfwWindow> createWindow(const std::string& title) { - auto window = std::make_unique<GlfwWindow>(); - window->title(title); - return window; - } + inline + std::unique_ptr<GlfwWindow> createWindow(const std::string &title) { + auto window = std::make_unique<GlfwWindow>(); + window->title(title); + return window; + } - void onUpdate() override { - context.pollEvents(); - } + void onUpdate() override { + context.pollEvents(); + } - [[nodiscard]] - std::string name() const override { - return "gfx::glfw"; - } + [[nodiscard]] + std::string name() const override { + return "gfx::glfw"; + } }; } - #endif diff --git a/include/fggl/gfx/input.hpp b/include/fggl/gfx/input.hpp index 5e29c8b..74d15b8 100644 --- a/include/fggl/gfx/input.hpp +++ b/include/fggl/gfx/input.hpp @@ -83,7 +83,7 @@ namespace fggl::gfx { TRIGGER_LEFT, TRIGGER_RIGHT }; - constexpr std::array<PadAxis,6> PadAxes = { + constexpr std::array<PadAxis, 6> PadAxes = { PadAxis::LEFT_X, PadAxis::LEFT_Y, PadAxis::RIGHT_X, @@ -107,10 +107,10 @@ namespace fggl::gfx { }; struct Joystick { - const char* name = nullptr; - const float* axes = nullptr; - const unsigned char* buttons = nullptr; - const unsigned char* hats = nullptr; + const char *name = nullptr; + const float *axes = nullptr; + const unsigned char *buttons = nullptr; + const unsigned char *hats = nullptr; int hatCount = 0; int axisCount = 0; int buttonCount = 0; @@ -120,7 +120,7 @@ namespace fggl::gfx { class Input { public: // this is a neccerry evil due to glfw's design :'( - static Input& instance() { + static Input &instance() { static Input *instance = new Input(); return *instance; } @@ -134,12 +134,12 @@ namespace fggl::gfx { void mouseBtn(const MouseButton btn, bool state); // mouse position - const double* mousePos() const; + const double *mousePos() const; double cursorDeltaX() const; double cursorDeltaY() const; // mouse scroll - const double* mouseScroll() const; + const double *mouseScroll() const; double scrollDeltaX() const; double scrollDeltaY() const; @@ -165,14 +165,14 @@ namespace fggl::gfx { bool mouseReleased(const MouseButton btn) const; // joysticks - void joystickConnect(int id, Joystick& data); + void joystickConnect(int id, Joystick &data); void joystickDisconnect(int id); bool hasJoystick(int id) const; - const Joystick& joystick(int id) const; + const Joystick &joystick(int id) const; // gamepads - void padState(int id, const PadState& state); + void padState(int id, const PadState &state); bool padDown(int id, PadButton btn); bool padPressed(int id, PadButton btn); diff --git a/include/fggl/gfx/ogl/backend.hpp b/include/fggl/gfx/ogl/backend.hpp index c30f2cb..047d713 100644 --- a/include/fggl/gfx/ogl/backend.hpp +++ b/include/fggl/gfx/ogl/backend.hpp @@ -12,69 +12,61 @@ */ namespace fggl::gfx { - class GlGraphics { - public: - GlGraphics(const Window& window); - ~GlGraphics(); - - void clear(); - }; - class Shader { public: Shader(); ~Shader(); inline void use() const { - glUseProgram( m_handle ); + glUseProgram(m_handle); } - inline GLuint uniform(const std::string& name) { - GLint loc = glGetUniformLocation( m_handle, name.c_str() ); - if ( loc == -1 ) { + inline GLuint uniform(const std::string &name) { + GLint loc = glGetUniformLocation(m_handle, name.c_str()); + if (loc == -1) { throw std::runtime_error("invalid shader uniform"); } return loc; } - inline void setInt(const std::string& name, const GLint value) { - setInt( uniform(name), value ); + inline void setInt(const std::string &name, const GLint value) { + setInt(uniform(name), value); } inline void setInt(GLuint idx, const GLint value) { - glUniform1i( idx, value ); + glUniform1i(idx, value); } - inline void setFloat(const std::string& name, const GLfloat value) { - setFloat( uniform(name), value ); + inline void setFloat(const std::string &name, const GLfloat value) { + setFloat(uniform(name), value); } inline void setFloat(GLuint idx, const GLfloat value) { - glUniform1f( idx, value ); + glUniform1f(idx, value); } - inline void setVec2(const std::string& name, const GLfloat* vec) { - setVec2( uniform(name), vec ); + inline void setVec2(const std::string &name, const GLfloat *vec) { + setVec2(uniform(name), vec); } - inline void setVec2(GLuint idx, const GLfloat* vec) { - glUniform2f( idx, vec[0], vec[1] ); + inline void setVec2(GLuint idx, const GLfloat *vec) { + glUniform2f(idx, vec[0], vec[1]); } - inline void setVec3(const std::string& name, const GLfloat* vec) { - setVec3( uniform(name), vec ); + inline void setVec3(const std::string &name, const GLfloat *vec) { + setVec3(uniform(name), vec); } - inline void setVec3(GLuint idx, const GLfloat* vec) { - glUniform3f( idx, vec[0], vec[1], vec[2] ); + inline void setVec3(GLuint idx, const GLfloat *vec) { + glUniform3f(idx, vec[0], vec[1], vec[2]); } - inline void setMat4(const std::string& name, const GLfloat* mat) { - setMat4( uniform(name), mat); + inline void setMat4(const std::string &name, const GLfloat *mat) { + setMat4(uniform(name), mat); } - inline void setMat4(GLuint idx, const GLfloat* mat) { - glUniformMatrix4fv( idx, 1, GL_FALSE, mat ); + inline void setMat4(GLuint idx, const GLfloat *mat) { + glUniformMatrix4fv(idx, 1, GL_FALSE, mat); } private: diff --git a/include/fggl/gfx/ogl/compat.hpp b/include/fggl/gfx/ogl/compat.hpp index 3c1cecd..187f46a 100644 --- a/include/fggl/gfx/ogl/compat.hpp +++ b/include/fggl/gfx/ogl/compat.hpp @@ -24,7 +24,7 @@ namespace fggl::gfx { - void generateHeightMesh(data::HeightMap* heightMap, data::Mesh); + void generateHeightMesh(data::HeightMap *heightMap, data::Mesh); // // fake module support - allows us to still RAII @@ -33,82 +33,83 @@ namespace fggl::gfx { fggl::gfx::MeshRenderer renderer; fggl::gfx::ShaderCache cache; - ecsOpenGLModule(Window& window, std::shared_ptr<fggl::data::Storage> storage) : - renderer(), - cache(std::move(storage)) { } + ecsOpenGLModule(Window &window, std::shared_ptr<fggl::data::Storage> storage) : + renderer(), + cache(std::move(storage)) {} - std::string name() const override { - return "gfx::opengl"; - } + std::string name() const override { + return "gfx::opengl"; + } - void uploadMesh(ecs3::World* world, ecs::entity_t entity) { - auto meshData = world->get<gfx::StaticMesh>(entity); + void uploadMesh(ecs3::World *world, ecs::entity_t entity) { + auto meshData = world->get<gfx::StaticMesh>(entity); - auto pipeline = cache.get(meshData->pipeline); - auto glMesh = renderer.upload(meshData->mesh); + auto pipeline = cache.get(meshData->pipeline); + auto glMesh = renderer.upload(meshData->mesh); - glMesh.pipeline = pipeline; - world->set<fggl::gfx::GlRenderToken>(entity, &glMesh); - } + glMesh.pipeline = pipeline; + world->set<fggl::gfx::GlRenderToken>(entity, &glMesh); + } - void uploadHeightmap(ecs3::World *world, ecs::entity_t entity) { - const auto heightmap = world->get<data::HeightMap>(entity); + void uploadHeightmap(ecs3::World *world, ecs::entity_t entity) { + const auto heightmap = world->get<data::HeightMap>(entity); - data::Mesh tmpMesh{}; - data::generateHeightMesh(heightmap, tmpMesh); - auto glMesh = renderer.upload( tmpMesh ); + data::Mesh tmpMesh{}; + data::generateHeightMesh(heightmap, tmpMesh); + auto glMesh = renderer.upload(tmpMesh); - auto pipeline = cache.get("phong"); - glMesh.pipeline = pipeline; - glMesh.renderType = GlRenderType::triangle_strip; - world->set<fggl::gfx::GlRenderToken>(entity, &glMesh); - } + auto pipeline = cache.get("phong"); + glMesh.pipeline = pipeline; + glMesh.renderType = GlRenderType::triangle_strip; + world->set<fggl::gfx::GlRenderToken>(entity, &glMesh); + } - void onLoad(ecs3::ModuleManager& manager, ecs3::TypeRegistry& types) override { - // TODO implement dependencies - types.make<fggl::gfx::StaticMesh>(); - types.make<fggl::data::HeightMap>(); - types.make<fggl::gfx::Camera>(); + void onLoad(ecs3::ModuleManager &manager, ecs3::TypeRegistry &types) override { + // TODO implement dependencies + types.make<fggl::gfx::StaticMesh>(); + types.make<fggl::data::HeightMap>(); + types.make<fggl::gfx::Camera>(); - // FIXME probably shouldn't be doing this... - types.make<fggl::input::FreeCamKeys>(); + // FIXME probably shouldn't be doing this... + types.make<fggl::input::FreeCamKeys>(); - // opengl - types.make<fggl::gfx::GlRenderToken>(); + // opengl + types.make<fggl::gfx::GlRenderToken>(); - // callbacks - auto upload_cb = [this](auto a, auto b) { this->uploadMesh(a, b); }; - manager.onAdd<fggl::gfx::StaticMesh>( upload_cb ); - manager.onAdd<fggl::data::HeightMap>( [this](auto a, auto b) { this->uploadHeightmap(a, b); }); - } + // callbacks + auto upload_cb = [this](auto a, auto b) { this->uploadMesh(a, b); }; + manager.onAdd<fggl::gfx::StaticMesh>(upload_cb); + manager.onAdd<fggl::data::HeightMap>([this](auto a, auto b) { this->uploadHeightmap(a, b); }); + } }; + using OglModule = std::shared_ptr<ecsOpenGLModule>; // // Loading related functions - should be handled in modules/data-driven // - inline void loadPipeline(OglModule& mod, const std::string& name, bool hasGeom) { + inline void loadPipeline(OglModule &mod, const std::string &name, bool hasGeom) { fggl::gfx::ShaderConfig config; config.name = name; - config.vertex = name+"_vert.glsl"; - config.fragment = name+"_frag.glsl"; - if ( hasGeom ) { - config.geometry = name+"_geom.glsl"; + config.vertex = name + "_vert.glsl"; + config.fragment = name + "_frag.glsl"; + if (hasGeom) { + config.geometry = name + "_geom.glsl"; } mod->cache.load(config); } - inline void loadBasicPipeline(OglModule& mod, const std::string &name) { + inline void loadBasicPipeline(OglModule &mod, const std::string &name) { loadPipeline(mod, name, false); } // // fake module/callbacks - our ECS doesn't have module/callback support yet. // - void onStaticMeshAdded(ecs3::World& ecs, ecs::entity_t entity, OglModule& mod) { - //spdlog::info("[CALLBACK] static mesh added, renderable?"); - /* + void onStaticMeshAdded(ecs3::World &ecs, ecs::entity_t entity, OglModule &mod) { + //spdlog::info("[CALLBACK] static mesh added, renderable?"); + /* auto meshData = ecs.get<gfx::StaticMesh>(entity); auto pipeline = mod->cache.get(meshData->pipeline); @@ -116,22 +117,21 @@ namespace fggl::gfx { glMesh.pipeline = pipeline; ecs.set<fggl::gfx::GlRenderToken>(entity, &glMesh); - */ + */ } - inline void renderMeshes(OglModule& mod, ecs3::World& ecs, float dt) { + inline void renderMeshes(OglModule &mod, ecs3::World &ecs, float dt) { // get the camera auto cameras = ecs.findMatching<fggl::gfx::Camera>(); - if ( cameras.empty() ) { + if (cameras.empty()) { return; } auto camera = cameras[0]; // get the models - mod->renderer.render(ecs, camera, dt); + mod->renderer.render(ecs, camera, dt); } } - #endif diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp index 59a3dd9..b4fbb92 100644 --- a/include/fggl/gfx/ogl/renderer.hpp +++ b/include/fggl/gfx/ogl/renderer.hpp @@ -1,22 +1,20 @@ #ifndef FGGL_GFX_RENDERER_H #define FGGL_GFX_RENDERER_H -#include <memory> -#include <vector> - #include <fggl/data/model.hpp> #include <fggl/ecs3/ecs.hpp> - #include <fggl/gfx/ogl/backend.hpp> #include <fggl/gfx/ogl/shader.hpp> - -#include <fggl/gfx/renderer.hpp> #include <fggl/gfx/paint.hpp> +#include <fggl/gfx/renderer.hpp> +#include <memory> +#include <vector> namespace fggl::gfx { enum GlRenderType { - triangles = GL_TRIANGLES, triangle_strip = GL_TRIANGLE_STRIP + triangles = GL_TRIANGLES, + triangle_strip = GL_TRIANGLE_STRIP }; struct GlRenderToken { @@ -34,33 +32,33 @@ namespace fggl::gfx { public: using token_t = GlRenderToken; - token_t upload(fggl::data::Mesh& mesh); + token_t upload(fggl::data::Mesh &mesh); - void render(ecs3::World& ecs, ecs3::entity_t camera, float dt); + void render(ecs3::World &ecs, ecs3::entity_t camera, float dt); float total; }; - class OpenGL4Backend : public Graphics { - public: - OpenGL4Backend(); - ~OpenGL4Backend() override = default; + class OpenGL4Backend : public Graphics { + public: + explicit OpenGL4Backend(const Window &owner); + ~OpenGL4Backend() override = default; - void clear() override; + void clear() override; - void draw2D(const Paint& paint) override; + void draw2D(const Paint &paint) override; - private: - GlRenderToken m_token2D; - std::unique_ptr<ShaderCache> m_cache; - }; + private: + GlRenderToken m_token2D; + std::unique_ptr<ShaderCache> m_cache; + }; - using OpenGL4 = OpenGL4Backend; + using OpenGL4 = OpenGL4Backend; - // specialisation hooks +// specialisation hooks using MeshRenderer = GlMeshRenderer; using MeshToken = GlRenderToken; -}; +}; // namespace fggl::gfx #endif diff --git a/include/fggl/gfx/ogl/shader.hpp b/include/fggl/gfx/ogl/shader.hpp index c74d89f..a6abc94 100644 --- a/include/fggl/gfx/ogl/shader.hpp +++ b/include/fggl/gfx/ogl/shader.hpp @@ -22,16 +22,16 @@ namespace fggl::gfx { bool hasGeom = false; }; - inline ShaderConfig ShaderFromName( const std::string& name ) { - return { - .name = name, - .vertex = name + "_vert.glsl", - .fragment = name + "_frag.glsl" - }; - } + inline ShaderConfig ShaderFromName(const std::string &name) { + return { + .name = name, + .vertex = name + "_vert.glsl", + .fragment = name + "_frag.glsl" + }; + } struct BinaryCache { - void* data = nullptr; + void *data = nullptr; GLsizei size = 0; GLenum format = GL_INVALID_ENUM; }; @@ -41,25 +41,25 @@ namespace fggl::gfx { ShaderCache(std::shared_ptr<fggl::data::Storage> storage); ~ShaderCache() = default; - GLuint load(const ShaderConfig& config); - GLuint getOrLoad(const ShaderConfig& config); + GLuint load(const ShaderConfig &config); + GLuint getOrLoad(const ShaderConfig &config); - GLuint get(const std::string& name); + GLuint get(const std::string &name); private: std::shared_ptr<fggl::data::Storage> m_storage; std::unordered_map<std::string, GLuint> m_shaders; // opengl operations - bool compileShader(const std::string&, GLuint); + bool compileShader(const std::string &, GLuint); // file io operations - bool loadFromDisk(GLuint pid, const ShaderConfig& config); - void saveToDisk(GLuint pid, const ShaderConfig& config); + bool loadFromDisk(GLuint pid, const ShaderConfig &config); + void saveToDisk(GLuint pid, const ShaderConfig &config); bool m_binary; - void cacheSave(GLuint pid, BinaryCache* cache); - bool cacheLoad(GLuint pid, const BinaryCache* cache); + void cacheSave(GLuint pid, BinaryCache *cache); + bool cacheLoad(GLuint pid, const BinaryCache *cache); }; } diff --git a/include/fggl/gfx/paint.hpp b/include/fggl/gfx/paint.hpp index cc2a3e1..6082674 100644 --- a/include/fggl/gfx/paint.hpp +++ b/include/fggl/gfx/paint.hpp @@ -6,92 +6,92 @@ namespace fggl::gfx { - using RadianAngle = float; - - enum class PathType { - MOVE, - PATH, - BAZIER2, - BAZIER3, - COLOUR, - CLOSE - }; - - struct Path2D { - inline explicit Path2D( math::vec2 start ) : m_points(), m_types() { - moveTo( start ); - } - - inline void moveTo(math::vec2 pos) { - m_points.push_back( pos ); - m_types.push_back( PathType::MOVE ); - } - - inline void pathTo(math::vec2 pos) { - m_points.push_back( pos ); - m_types.push_back( PathType::PATH ); - } - - void bezierTo(math::vec2 cp1, math::vec2 pos); - void bezierTo(math::vec2 cp1, math::vec2 cp2, math::vec2 pos); - - void arc(math::vec2 center, float radius, RadianAngle startAngle, RadianAngle endAngle, bool ccw); - - void colour(math::vec3 colour) { - m_colours.push_back( colour ); - m_types.push_back( PathType::COLOUR ); - } - - void close() { - pathTo( m_points[0] ); - m_types.push_back( PathType::CLOSE ); - } - - std::vector< math::vec2 > m_points; - std::vector< PathType > m_types; - std::vector< math::vec3 > m_colours; - }; - - enum class PaintType { - FILL, - STROKE - }; - - struct PaintCmd { - PaintType type; - Path2D path; - }; - - struct TextCmd { - const std::string text; - const math::vec2 pos; - }; - - class Paint { - public: - Paint() = default; - Paint(Paint& paint) = delete; - - inline void fill(Path2D& path) { - m_cmds.push_back( { PaintType::FILL, path } ); - } - - void stroke(Path2D& path) { - m_cmds.push_back( { PaintType::STROKE, path } ); - } - - void text(const std::string& text, const math::vec2& pos) { - m_text.push_back( { text, pos } ); - } - - const std::vector< PaintCmd >& cmds() const { - return m_cmds; - } - - private: - std::vector< PaintCmd > m_cmds; - std::vector< TextCmd > m_text; - }; + using RadianAngle = float; + + enum class PathType { + MOVE, + PATH, + BAZIER2, + BAZIER3, + COLOUR, + CLOSE + }; + + struct Path2D { + inline explicit Path2D(math::vec2 start) : m_points(), m_types() { + moveTo(start); + } + + inline void moveTo(math::vec2 pos) { + m_points.push_back(pos); + m_types.push_back(PathType::MOVE); + } + + inline void pathTo(math::vec2 pos) { + m_points.push_back(pos); + m_types.push_back(PathType::PATH); + } + + void bezierTo(math::vec2 cp1, math::vec2 pos); + void bezierTo(math::vec2 cp1, math::vec2 cp2, math::vec2 pos); + + void arc(math::vec2 center, float radius, RadianAngle startAngle, RadianAngle endAngle, bool ccw); + + void colour(math::vec3 colour) { + m_colours.push_back(colour); + m_types.push_back(PathType::COLOUR); + } + + void close() { + pathTo(m_points[0]); + m_types.push_back(PathType::CLOSE); + } + + std::vector<math::vec2> m_points; + std::vector<PathType> m_types; + std::vector<math::vec3> m_colours; + }; + + enum class PaintType { + FILL, + STROKE + }; + + struct PaintCmd { + PaintType type; + Path2D path; + }; + + struct TextCmd { + const std::string text; + const math::vec2 pos; + }; + + class Paint { + public: + Paint() = default; + Paint(Paint &paint) = delete; + + inline void fill(Path2D &path) { + m_cmds.push_back({PaintType::FILL, path}); + } + + void stroke(Path2D &path) { + m_cmds.push_back({PaintType::STROKE, path}); + } + + void text(const std::string &text, const math::vec2 &pos) { + m_text.push_back({text, pos}); + } + + const std::vector<PaintCmd> &cmds() const { + return m_cmds; + } + + private: + std::vector<PaintCmd> m_cmds; + std::vector<TextCmd> m_text; + }; } diff --git a/include/fggl/gfx/renderer.hpp b/include/fggl/gfx/renderer.hpp index c112cd2..5fe42c6 100644 --- a/include/fggl/gfx/renderer.hpp +++ b/include/fggl/gfx/renderer.hpp @@ -3,18 +3,17 @@ namespace fggl::gfx { - class RenderBackend { - public: - RenderBackend(); - virtual ~RenderBackend() = default; + class RenderBackend { + public: + RenderBackend(); + virtual ~RenderBackend() = default; - virtual void clear() = 0; - virtual void swap() = 0; + virtual void clear() = 0; + virtual void swap() = 0; - }; - - using RenderBackendPtr = std::unique_ptr<RenderBackend>; - using RenderBackendFactory = std::function< fggl::gfx::RenderBackendPtr&&() >; + }; + using RenderBackendPtr = std::unique_ptr<RenderBackend>; + using RenderBackendFactory = std::function<fggl::gfx::RenderBackendPtr &&()>; }; diff --git a/include/fggl/gfx/vector.hpp b/include/fggl/gfx/vector.hpp index 979f9e2..72d7649 100644 --- a/include/fggl/gfx/vector.hpp +++ b/include/fggl/gfx/vector.hpp @@ -6,14 +6,14 @@ namespace fggl::gfx { - struct Rectangle { - math::vec2 topLeft; - math::vec2 size; - }; + struct Rectangle { + math::vec2 topLeft; + math::vec2 size; + }; - struct Polygon { - std::vector<math::vec2> points; - }; + struct Polygon { + std::vector<math::vec2> points; + }; } diff --git a/include/fggl/gfx/vulkan/vulkan.hpp b/include/fggl/gfx/vulkan/vulkan.hpp index e32d6b3..39d8428 100644 --- a/include/fggl/gfx/vulkan/vulkan.hpp +++ b/include/fggl/gfx/vulkan/vulkan.hpp @@ -10,25 +10,24 @@ namespace fggl::gfx::vkgfx { - constexpr char const* EngineName = "FGGL"; + constexpr char const *EngineName = "FGGL"; - class VulkanGraphics { + class VulkanGraphics { - private: - vk::raii::Instance m_instance; - std::unique_ptr<vk::raii::SwapchainKHR> m_swapchain; - }; + private: + vk::raii::Instance m_instance; + std::unique_ptr<vk::raii::SwapchainKHR> m_swapchain; + }; - class VulkanContext { - public: - VulkanContext(); + class VulkanContext { + public: + VulkanContext(); - VulkanGraphics& createGraphics(); - - private: - vk::raii::Context m_context; - }; + VulkanGraphics &createGraphics(); + private: + vk::raii::Context m_context; + }; } diff --git a/include/fggl/gfx/window.hpp b/include/fggl/gfx/window.hpp index 3a4c6f2..73c8408 100644 --- a/include/fggl/gfx/window.hpp +++ b/include/fggl/gfx/window.hpp @@ -29,7 +29,7 @@ namespace fggl::gfx { FocusOnShow = GLFW_FOCUS_ON_SHOW }; - enum WindowHint { + enum WindowHint { Focused = GLFW_FOCUSED, Iconified = GLFW_ICONIFIED, Maximised = GLFW_MAXIMIZED, @@ -42,18 +42,20 @@ namespace fggl::gfx { NoError = GLFW_CONTEXT_NO_ERROR }; - class GlfwWindow : public Window { + class GlfwWindow : public Window { public: GlfwWindow(); ~GlfwWindow(); - GlfwWindow(Window&) = delete; - GlfwWindow(Window&&) = delete; + GlfwWindow(Window &) = delete; + GlfwWindow(Window &&) = delete; + + virtual math::vec2i frameSize() const override; // window <-> opengl stuff - void activate() const override; + void activate() const override; - void frameStart() override; - void frameEnd() override; + void frameStart() override; + void frameEnd() override; inline float width() const { return m_framesize.x; @@ -64,40 +66,40 @@ namespace fggl::gfx { } inline void framesize(int width, int height) { - m_framesize = math::vec2( width, height ); + m_framesize = math::vec2(width, height); } // window manager stuff [[nodiscard]] inline bool closeRequested() const { - assert( m_window != nullptr ); + assert(m_window != nullptr); return glfwWindowShouldClose(m_window); } - inline void title(const std::string& title) { - assert( m_window != nullptr ); - glfwSetWindowTitle( m_window, title.c_str() ); + inline void title(const std::string &title) { + assert(m_window != nullptr); + glfwSetWindowTitle(m_window, title.c_str()); } [[nodiscard]] inline bool fullscreen() const { - assert( m_window != nullptr ); - return glfwGetWindowMonitor( m_window ) != nullptr; + assert(m_window != nullptr); + return glfwGetWindowMonitor(m_window) != nullptr; } inline void fullscreen(bool state) { - assert( m_window != nullptr ); - if ( state ) { + assert(m_window != nullptr); + if (state) { auto monitor = glfwGetPrimaryMonitor(); - const auto mode = glfwGetVideoMode( monitor ); - glfwSetWindowMonitor( m_window, monitor, - 0, 0, - mode->width, mode->height, - mode->refreshRate); + const auto mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(m_window, monitor, + 0, 0, + mode->width, mode->height, + mode->refreshRate); } else { - glfwSetWindowMonitor( m_window, nullptr, - 0, 0, - 800, 600, 0); + glfwSetWindowMonitor(m_window, nullptr, + 0, 0, + 800, 600, 0); } } @@ -116,32 +118,32 @@ namespace fggl::gfx { } inline void visible(bool state) { - assert( m_window != nullptr ); - if ( state ) { - glfwShowWindow( m_window ); + assert(m_window != nullptr); + if (state) { + glfwShowWindow(m_window); } else { - glfwHideWindow( m_window ); + glfwHideWindow(m_window); } } - inline GLFWwindow* handle() { - assert( m_window != nullptr ); + inline GLFWwindow *handle() { + assert(m_window != nullptr); return m_window; } private: - GLFWwindow* m_window; + GLFWwindow *m_window; math::vec2 m_framesize; inline void set_hint(int hint, bool state) const { - assert( m_window != nullptr ); - glfwSetWindowAttrib( m_window, hint, state ); + assert(m_window != nullptr); + glfwSetWindowAttrib(m_window, hint, state); } [[nodiscard]] inline bool check_hint(int hint) const { - assert( m_window != nullptr ); - return glfwGetWindowAttrib( m_window, hint) == GLFW_TRUE; + assert(m_window != nullptr); + return glfwGetWindowAttrib(m_window, hint) == GLFW_TRUE; } }; diff --git a/include/fggl/gfx/window_input.hpp b/include/fggl/gfx/window_input.hpp index 153fcd9..1aff51f 100644 --- a/include/fggl/gfx/window_input.hpp +++ b/include/fggl/gfx/window_input.hpp @@ -7,12 +7,11 @@ #include <fggl/gfx/window.hpp> #include <fggl/input/input.hpp> - namespace fggl::gfx { class GlfwInputManager { public: - static GlfwInputManager& instance() { + static GlfwInputManager &instance() { static GlfwInputManager *instance = new GlfwInputManager(); return *instance; } @@ -21,28 +20,28 @@ namespace fggl::gfx { m_inputs = input; } - inline void frame() { - m_inputs->frame(0.0f); - } + inline void frame() { + m_inputs->frame(0.0f); + } inline bool alive() { return m_inputs != nullptr; } - inline input::MouseInput& mouse() { + inline input::MouseInput &mouse() { return m_inputs->mouse; } - inline input::KeyboardInput& keyboard() { + inline input::KeyboardInput &keyboard() { return m_inputs->keyboard; } - inline input::GamepadInput& gamepads() { + inline input::GamepadInput &gamepads() { return m_inputs->gamepads; } inline void onMouseMove(float x, float y) { - if (m_inputs != nullptr ) { + if (m_inputs != nullptr) { m_inputs->mouse.axis(input::MouseAxis::X, x); m_inputs->mouse.axis(input::MouseAxis::Y, y); } @@ -56,13 +55,13 @@ namespace fggl::gfx { } inline void onMouseButton(int btn, bool state) { - if ( m_inputs != nullptr ) { - m_inputs->mouse.button((fggl::input::MouseButton)btn, state); + if (m_inputs != nullptr) { + m_inputs->mouse.button((fggl::input::MouseButton) btn, state); } } inline void onKeyEvent(int scancode, bool state) { - if ( m_inputs != nullptr ) { + if (m_inputs != nullptr) { m_inputs->keyboard.set(scancode, state); } } diff --git a/include/fggl/gfx/windowing.hpp b/include/fggl/gfx/windowing.hpp index 9f912a0..6405553 100644 --- a/include/fggl/gfx/windowing.hpp +++ b/include/fggl/gfx/windowing.hpp @@ -7,40 +7,44 @@ namespace fggl::gfx { - class Graphics { - public: - virtual ~Graphics() = default; - virtual void clear() = 0; - - virtual void draw2D(const Paint& paint) = 0; - }; - - class Window { - public: - virtual ~Window() = default; - virtual void activate() const = 0; - - template<typename T> - void make_graphics() { - activate(); - m_graphics = std::make_unique<T>(); - } - - virtual void frameStart() = 0; - virtual void frameEnd() = 0; - - Graphics& graphics() { - return *m_graphics; - } - - protected: - std::unique_ptr<Graphics> m_graphics; - }; - - class WindowService { - public: - Window& create(); - }; + class Graphics { + public: + virtual ~Graphics() = default; + virtual void clear() = 0; + + virtual void draw2D(const Paint &paint) = 0; + }; + + class Window { + public: + virtual ~Window() = default; + virtual void activate() const = 0; + + template<typename T> + void make_graphics() { + activate(); + m_graphics = std::make_unique<T>(*this); + } + + // window-related + [[nodiscard]] + virtual math::vec2i frameSize() const = 0; + + virtual void frameStart() = 0; + virtual void frameEnd() = 0; + + Graphics &graphics() { + return *m_graphics; + } + + protected: + std::unique_ptr<Graphics> m_graphics; + }; + + class WindowService { + public: + Window &create(); + }; } // namespace fggl::gfx diff --git a/include/fggl/gui/containers.hpp b/include/fggl/gui/containers.hpp index 5192fcc..813e450 100644 --- a/include/fggl/gui/containers.hpp +++ b/include/fggl/gui/containers.hpp @@ -4,59 +4,59 @@ #include <fggl/gui/widget.hpp> namespace fggl::gui { - - class Widget { - public: - Widget() = default; - - virtual math::vec2 baseSize(); - - void size(math::vec2 size); - math::vec2 size() const; - - virtual bool contains(const math::vec2& point); - virtual Widget* getChildAt(const math::vec2& point); - - virtual void render(gfx::Paint& paint) = 0; - }; - - class Container : public Widget { - public: - Container() = default; - - void clear(); - Widget* getChildAt(const std::math& point) override; - - void add(Widget&& widget); - virtual void layout() = 0; - - private: - bool requireLayout; - std::vector<std::unique_ptr<Widget>> m_children; - }; - - enum class LayoutAxis { - LINE_AXIS, - PAGE_AXIS - }; - - /** - * A box-like container. - * This container lines child elements up along an axis. - * - * This is a fairly common approach in may layout frameworks, for example: - * * Java's BoxLayout - * * GTK's Boxes - * * QT's QBoxLayout - * * Android's XML box things - */ - class Box : public Container { - public: - Box( LayoutAxis axis ); - void layout() override; - private: - const LayoutAxis m_axis; - } + + class Widget { + public: + Widget() = default; + + virtual math::vec2 baseSize(); + + void size(math::vec2 size); + math::vec2 size() const; + + virtual bool contains(const math::vec2 &point); + virtual Widget *getChildAt(const math::vec2 &point); + + virtual void render(gfx::Paint &paint) = 0; + }; + + class Container : public Widget { + public: + Container() = default; + + void clear(); + Widget *getChildAt(const std::math &point) override; + + void add(Widget &&widget); + virtual void layout() = 0; + + private: + bool requireLayout; + std::vector<std::unique_ptr<Widget>> m_children; + }; + + enum class LayoutAxis { + LINE_AXIS, + PAGE_AXIS + }; + + /** + * A box-like container. + * This container lines child elements up along an axis. + * + * This is a fairly common approach in may layout frameworks, for example: + * * Java's BoxLayout + * * GTK's Boxes + * * QT's QBoxLayout + * * Android's XML box things + */ + class Box : public Container { + public: + Box(LayoutAxis axis); + void layout() override; + private: + const LayoutAxis m_axis; + } }; //namespace fggl::gui diff --git a/include/fggl/gui/widget.hpp b/include/fggl/gui/widget.hpp index a78b8e4..50a092d 100644 --- a/include/fggl/gui/widget.hpp +++ b/include/fggl/gui/widget.hpp @@ -5,30 +5,30 @@ #include <fggl/gfx/paint.hpp> namespace fggl::gui { - - class Widget { - public: - Widget() = default; - - virtual bool contains(const math::vec2& point); - virtual Widget* getChildAt(const math::vec2& point); - - virtual void render(gfx::Paint& paint) = 0; - }; - - class Container : public Widget { - public: - Container() = default; - - void clear(); - Widget* getChildAt(const std::math& point) override; - - void add(Widget&& widget); - - private: - bool requireLayout; - std::vector<std::unique_ptr<Widget>> m_children; - }; + + class Widget { + public: + Widget() = default; + + virtual bool contains(const math::vec2 &point); + virtual Widget *getChildAt(const math::vec2 &point); + + virtual void render(gfx::Paint &paint) = 0; + }; + + class Container : public Widget { + public: + Container() = default; + + void clear(); + Widget *getChildAt(const std::math &point) override; + + void add(Widget &&widget); + + private: + bool requireLayout; + std::vector<std::unique_ptr<Widget>> m_children; + }; }; //namespace fggl::gui diff --git a/include/fggl/gui/widgets.hpp b/include/fggl/gui/widgets.hpp index 5442c9c..7fff237 100644 --- a/include/fggl/gui/widgets.hpp +++ b/include/fggl/gui/widgets.hpp @@ -4,32 +4,32 @@ #include <fggl/gui/widget.hpp> namespace fggl::gui { - - class Button : public Widget { - public: - Button(); - void render(gfx::Paint& paint) override; - private: - const std::string m_value; - }; - - class Label : public Widget { - public: - Label(); - void render(gfx::Paint& paint) override; - }; - - class TextField : public Widget { - public: - TextField(); - void render(gfx::Paint& paint) override; - }; - - class Toggle : public Widget { - public: - Toggle(); - void render(gfx::Paint& paint) override; - }; + + class Button : public Widget { + public: + Button(); + void render(gfx::Paint &paint) override; + private: + const std::string m_value; + }; + + class Label : public Widget { + public: + Label(); + void render(gfx::Paint &paint) override; + }; + + class TextField : public Widget { + public: + TextField(); + void render(gfx::Paint &paint) override; + }; + + class Toggle : public Widget { + public: + Toggle(); + void render(gfx::Paint &paint) override; + }; }; //namespace fggl::gui diff --git a/include/fggl/input/camera_input.h b/include/fggl/input/camera_input.h index b2c2735..4e796b1 100644 --- a/include/fggl/input/camera_input.h +++ b/include/fggl/input/camera_input.h @@ -10,50 +10,50 @@ 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); + 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); } diff --git a/include/fggl/input/gamepad.hpp b/include/fggl/input/gamepad.hpp index 96a3ada..3b92dd3 100644 --- a/include/fggl/input/gamepad.hpp +++ b/include/fggl/input/gamepad.hpp @@ -50,49 +50,64 @@ namespace fggl::input { }; constexpr std::array<GamepadButtonRecord, 15> GamepadButtonsMicrosoft = {{ - {GamepadButton::A, "A"}, - {GamepadButton::B, "B"}, - {GamepadButton::X, "X"}, - {GamepadButton::Y, "Y"}, - {GamepadButton::BUMPER_LEFT, "Left Bumper"}, - {GamepadButton::BUMPER_RIGHT, "Right Bumper"}, - {GamepadButton::BACK, "Back"}, - {GamepadButton::START, "Start"}, - {GamepadButton::GUIDE, "Guide"}, - {GamepadButton::THUMB_LEFT, "Thumb Left"}, - {GamepadButton::THUMB_RIGHT, "Thumb Right"}, - {GamepadButton::DPAD_UP, "Dpad Up"}, - {GamepadButton::DPAD_RIGHT, "Dpad Right"}, - {GamepadButton::DPAD_DOWN, "Dpad Down"}, - {GamepadButton::DPAD_LEFT, "Dpad Left"}, - }}; + {GamepadButton::A, "A"}, + {GamepadButton::B, "B"}, + {GamepadButton::X, "X"}, + {GamepadButton::Y, "Y"}, + {GamepadButton::BUMPER_LEFT, + "Left Bumper"}, + {GamepadButton::BUMPER_RIGHT, + "Right Bumper"}, + {GamepadButton::BACK, "Back"}, + {GamepadButton::START, "Start"}, + {GamepadButton::GUIDE, "Guide"}, + {GamepadButton::THUMB_LEFT, + "Thumb Left"}, + {GamepadButton::THUMB_RIGHT, + "Thumb Right"}, + {GamepadButton::DPAD_UP, "Dpad Up"}, + {GamepadButton::DPAD_RIGHT, + "Dpad Right"}, + {GamepadButton::DPAD_DOWN, + "Dpad Down"}, + {GamepadButton::DPAD_LEFT, + "Dpad Left"}, + }}; constexpr std::array<GamepadAxisRecord, 6> GamepadAxes = {{ - {GamepadAxis::LEFT_X, "left stick left/right"}, - {GamepadAxis::LEFT_Y, "left stick up/down"}, - {GamepadAxis::RIGHT_X, "right stick left/right"}, - {GamepadAxis::RIGHT_Y, "right stick up/down"}, - {GamepadAxis::TRIGGER_LEFT, "left trigger"}, - {GamepadAxis::TRIGGER_RIGHT, "right trigger"}, - }}; + {GamepadAxis::LEFT_X, "left stick left/right"}, + {GamepadAxis::LEFT_Y, "left stick up/down"}, + {GamepadAxis::RIGHT_X, "right stick left/right"}, + {GamepadAxis::RIGHT_Y, "right stick up/down"}, + {GamepadAxis::TRIGGER_LEFT, "left trigger"}, + {GamepadAxis::TRIGGER_RIGHT, "right trigger"}, + }}; constexpr std::array<GamepadButtonRecord, 15> GamepadButtonsPlaystation = {{ - {GamepadButton::SQUARE, "Square"}, - {GamepadButton::CIRCLE, "Circle"}, - {GamepadButton::TRIANGLE, "Triangle"}, - {GamepadButton::CROSS, "Cross"}, - {GamepadButton::BUMPER_LEFT, "Left Bumper"}, - {GamepadButton::BUMPER_RIGHT, "Right Bumper"}, - {GamepadButton::BACK, "Back"}, - {GamepadButton::START, "Start"}, - {GamepadButton::GUIDE, "Guide"}, - {GamepadButton::THUMB_LEFT, "Thumb Left"}, - {GamepadButton::THUMB_RIGHT, "Thumb Right"}, - {GamepadButton::DPAD_UP, "Dpad Up"}, - {GamepadButton::DPAD_RIGHT, "Dpad Right"}, - {GamepadButton::DPAD_DOWN, "Dpad Down"}, - {GamepadButton::DPAD_LEFT, "Dpad Left"}, - }}; + {GamepadButton::SQUARE, "Square"}, + {GamepadButton::CIRCLE, "Circle"}, + {GamepadButton::TRIANGLE, + "Triangle"}, + {GamepadButton::CROSS, "Cross"}, + {GamepadButton::BUMPER_LEFT, + "Left Bumper"}, + {GamepadButton::BUMPER_RIGHT, + "Right Bumper"}, + {GamepadButton::BACK, "Back"}, + {GamepadButton::START, "Start"}, + {GamepadButton::GUIDE, "Guide"}, + {GamepadButton::THUMB_LEFT, + "Thumb Left"}, + {GamepadButton::THUMB_RIGHT, + "Thumb Right"}, + {GamepadButton::DPAD_UP, "Dpad Up"}, + {GamepadButton::DPAD_RIGHT, + "Dpad Right"}, + {GamepadButton::DPAD_DOWN, + "Dpad Down"}, + {GamepadButton::DPAD_LEFT, + "Dpad Left"}, + }}; struct GamepadState { std::bitset<GamepadButtonsMicrosoft.size()> buttons; @@ -104,76 +119,76 @@ namespace fggl::input { struct GamepadInput { public: inline bool present(size_t id) const { - assert( id < MaxControllers ); + assert(id < MaxControllers); return m_active[id]; } - inline void name(size_t id, const std::string& name) { - assert( id < MaxControllers ); + inline void name(size_t id, const std::string &name) { + assert(id < MaxControllers); m_names[id] = name; } inline std::string name(size_t id) const { - std::string name = m_names[id].empty() ? "gamepad "+std::to_string(id) : m_names[id]; - if ( !present(id) ) { + std::string name = m_names[id].empty() ? "gamepad " + std::to_string(id) : m_names[id]; + if (!present(id)) { return name + " (disconnected)"; } return name; } inline float axis(size_t id, GamepadAxis axis) const { - if ( !present(id) ) { + if (!present(id)) { return 0.0f; } - return m_current[id].axes[(int)axis]; + return m_current[id].axes[(int) axis]; } inline float axisDelta(size_t id, GamepadAxis axis) const { - if ( !present(id) ) { + if (!present(id)) { return 0.0f; } - return m_current[id].axes[(int)axis] - m_previous[id].axes[(int)axis]; + return m_current[id].axes[(int) axis] - m_previous[id].axes[(int) axis]; } inline bool button(size_t id, GamepadButton btn) const { - if ( !present(id) ) { + if (!present(id)) { return 0.0f; } - return m_current[id].buttons[(int)btn]; + return m_current[id].buttons[(int) btn]; } inline bool buttonPressed(size_t id, GamepadButton btn) const { - if ( !present(id) ) { + if (!present(id)) { return 0.0f; } - return m_current[id].buttons[(int)btn] && !m_previous[id].buttons[(int)btn]; + return m_current[id].buttons[(int) btn] && !m_previous[id].buttons[(int) btn]; } inline bool buttonReleased(size_t id, GamepadButton btn) const { - if ( !present(id) ) { + if (!present(id)) { return 0.0f; } - return !m_current[id].buttons[(int)btn] && m_previous[id].buttons[(int)btn]; + return !m_current[id].buttons[(int) btn] && m_previous[id].buttons[(int) btn]; } inline bool buttonChanged(size_t id, GamepadButton btn) const { - if ( !present(id) ) { + if (!present(id)) { return 0.0f; } - return m_current[id].buttons[(int)btn] != m_previous[id].buttons[(int)btn]; + return m_current[id].buttons[(int) btn] != m_previous[id].buttons[(int) btn]; } inline void frame(float dt) { m_previous = m_current; } - inline void update(size_t id, const GamepadState& state) { - assert( present(id) ); + inline void update(size_t id, const GamepadState &state) { + assert(present(id)); m_current[id] = state; } inline void setActive(size_t id, bool state) { - assert( id < MaxControllers ); + assert(id < MaxControllers); m_active[id] = state; } diff --git a/include/fggl/input/input.hpp b/include/fggl/input/input.hpp index a2f0307..9352945 100644 --- a/include/fggl/input/input.hpp +++ b/include/fggl/input/input.hpp @@ -12,7 +12,7 @@ namespace fggl::input { public: Input() = default; void frame(float dt); - + KeyboardInput keyboard; MouseInput mouse; GamepadInput gamepads; diff --git a/include/fggl/input/keyboard.hpp b/include/fggl/input/keyboard.hpp index 853e652..44e0b68 100644 --- a/include/fggl/input/keyboard.hpp +++ b/include/fggl/input/keyboard.hpp @@ -10,28 +10,28 @@ namespace fggl::input { using scancode_t = int; class KeyboardState { - public: - KeyboardState() = default; - - inline void clear() { - m_keys.clear(); - } - - inline void set(scancode_t code, bool state) { - if ( state ) { - m_keys.insert( code ); - } else { - m_keys.erase( code ); - } - } - - inline bool down(scancode_t scancode) const { - if ( m_keys.empty() ) - return false; - - return m_keys.count(scancode) > 0; - } - + public: + KeyboardState() = default; + + inline void clear() { + m_keys.clear(); + } + + inline void set(scancode_t code, bool state) { + if (state) { + m_keys.insert(code); + } else { + m_keys.erase(code); + } + } + + inline bool down(scancode_t scancode) const { + if (m_keys.empty()) + return false; + + return m_keys.count(scancode) > 0; + } + private: std::unordered_set<scancode_t> m_keys; }; diff --git a/include/fggl/input/mouse.hpp b/include/fggl/input/mouse.hpp index 09af8e9..5c7e6e8 100644 --- a/include/fggl/input/mouse.hpp +++ b/include/fggl/input/mouse.hpp @@ -36,70 +36,70 @@ namespace fggl::input { }; constexpr std::array<MouseButtonRecord, 8> MouseButtons = {{ - {MouseButton::LEFT, "Left"}, - {MouseButton::MIDDLE, "Middle"}, - {MouseButton::RIGHT, "Right"}, - {MouseButton::EXTRA_1, "Extra 1"}, - {MouseButton::EXTRA_2, "Extra 2"}, - {MouseButton::EXTRA_3, "Extra 3"}, - {MouseButton::EXTRA_4, "Extra 4"}, - {MouseButton::EXTRA_5, "Extra 5"}, - }}; + {MouseButton::LEFT, "Left"}, + {MouseButton::MIDDLE, "Middle"}, + {MouseButton::RIGHT, "Right"}, + {MouseButton::EXTRA_1, "Extra 1"}, + {MouseButton::EXTRA_2, "Extra 2"}, + {MouseButton::EXTRA_3, "Extra 3"}, + {MouseButton::EXTRA_4, "Extra 4"}, + {MouseButton::EXTRA_5, "Extra 5"}, + }}; constexpr std::array<MouseAxisRecord, 4> MouseAxes = {{ - {MouseAxis::X, "Left/Right"}, - {MouseAxis::Y, "Up/Down"}, - {MouseAxis::SCROLL_X, "Scroll X"}, - {MouseAxis::SCROLL_Y, "Scroll Y"} - }}; + {MouseAxis::X, "Left/Right"}, + {MouseAxis::Y, "Up/Down"}, + {MouseAxis::SCROLL_X, "Scroll X"}, + {MouseAxis::SCROLL_Y, "Scroll Y"} + }}; struct MouseState { float axis[MouseAxes.size()]; std::bitset<MouseButtons.size()> buttons; - void operator=(const MouseState& rhs); + void operator=(const MouseState &rhs); }; class MouseInput { public: - MouseInput() = default; - MouseInput(const MouseInput& rhs) = delete; - void operator=(const MouseInput& rhs) = delete; + MouseInput() = default; + MouseInput(const MouseInput &rhs) = delete; + void operator=(const MouseInput &rhs) = delete; inline void frame(float dt) { m_prev = m_curr; } inline void axis(MouseAxis axis, float value) { - m_curr.axis[(int)axis] = value; + m_curr.axis[(int) axis] = value; } inline float axis(MouseAxis axis) const { - return m_curr.axis[(int)axis]; + return m_curr.axis[(int) axis]; } inline float axisDelta(MouseAxis axis) const { - return m_curr.axis[(int)axis] - m_prev.axis[(int)axis]; + return m_curr.axis[(int) axis] - m_prev.axis[(int) axis]; } inline void button(MouseButton btn, bool state) { - m_curr.buttons[(int)btn] = state; + m_curr.buttons[(int) btn] = state; } inline bool down(MouseButton btn) const { - return m_curr.buttons[(int)btn]; + return m_curr.buttons[(int) btn]; } inline bool downPrev(MouseButton btn) const { - return m_prev.buttons[(int)btn]; + return m_prev.buttons[(int) btn]; } inline bool pressed(MouseButton btn) const { - return m_curr.buttons[(int)btn] && !m_prev.buttons[(int)btn]; + return m_curr.buttons[(int) btn] && !m_prev.buttons[(int) btn]; } inline bool released(MouseButton btn) const { - return !m_curr.buttons[(int)btn] && m_prev.buttons[(int)btn]; + return !m_curr.buttons[(int) btn] && m_prev.buttons[(int) btn]; } private: diff --git a/include/fggl/math/easing.hpp b/include/fggl/math/easing.hpp index db53218..9a1a82c 100644 --- a/include/fggl/math/easing.hpp +++ b/include/fggl/math/easing.hpp @@ -9,111 +9,110 @@ namespace fggl::math { - inline float lerp(float a, float b, float w) { - return (b - a) * w + a; - } - - inline float scale(float in, float inMin, float inMax, float outMin, float outMax) { - return ( (in - inMin) * (outMax - outMin) ) / (inMax - inMin); - } - - // - // Functions - // - using transformF = std::function<float(float)>; - - inline float scaleFilter(float in, float inMin, float inMax, float outMin, float outMax, transformF filter) { - float out = in - inMin; - out /= (inMax - inMin); - out = f(out); - out *= (outEnd - outStart); - return out + outMin; - } - - inline float mix(transformF a, transformF b, float weightB, float t) { - return ( (1 - weightB) * a(t) ) + ( weightB * b(t) ); - } - - inline float crossFade( transformF a, transformF b, float t) { - return ((1 - t) * a(t))+ (t * b); - } - - // - // building blocks - // - inline float scale(transformF a, float t) { - return t * f(t); - } - - inline float reverseScale(transformF a, float t) { - return (1 - t) * a(t); - } - - inline float Arch2(float t) { - return t * (1 - t); - } - - inline float BounceClampBottom( float t ){ - return fabs(t); - } - - inline float BounceClampTop( float t ) { - return 1.f - fabs( 1.f - t ); - } - - // - // Easing function library - // see Math for Game Programmers: Fast and Funky 1D Nonlinear Transformations, GDC 2015 - // - - inline float SmoothStart2(float t) { - return t * t; - } - - inline float SmoothStart3(float t) { - return t * t * t; - } - - inline float SmoothStart4(float t) { - return t * t * t * t; - } - - inline float SmoothStart5(float t) { - return t * t * t * t * t; - } - - inline float SmoothStop2(float t) { - constexpr tFlip = 1 - y; - return 1 - (tFlip * tFlip); - } - - inline float SmoothStop3(float t) { - constexpr tFlip = 1 - y; - return 1 - (tFlip * tFlip); - } - - inline float SmoothStop4(float t) { - constexpr tFlip = 1 - y; - return 1 - (tFlip * tFlip * tFlip * tFlip); - } - - inline float SmoothStop5(float t) { - constexpr tFlip = 1 - y; - return 1 - (tFlip * tFlip * tFlip * tFlip * tFlip); - } - - // - // Bezier curves - // - - inline float NormalizedBezier3( float B, float C, float t) { - const float s = 1.f - t; - const float t2 = t * t; - const float s2 = s * s; - const float t3 = t2 * t; - return (3.f*B*s2*t) + (3.f*C*s*t2) + t3; - } - + inline float lerp(float a, float b, float w) { + return (b - a) * w + a; + } + + inline float scale(float in, float inMin, float inMax, float outMin, float outMax) { + return ((in - inMin) * (outMax - outMin)) / (inMax - inMin); + } + + // + // Functions + // + using transformF = std::function<float(float)>; + + inline float scaleFilter(float in, float inMin, float inMax, float outMin, float outMax, transformF filter) { + float out = in - inMin; + out /= (inMax - inMin); + out = f(out); + out *= (outEnd - outStart); + return out + outMin; + } + + inline float mix(transformF a, transformF b, float weightB, float t) { + return ((1 - weightB) * a(t)) + (weightB * b(t)); + } + + inline float crossFade(transformF a, transformF b, float t) { + return ((1 - t) * a(t)) + (t * b); + } + + // + // building blocks + // + inline float scale(transformF a, float t) { + return t * f(t); + } + + inline float reverseScale(transformF a, float t) { + return (1 - t) * a(t); + } + + inline float Arch2(float t) { + return t * (1 - t); + } + + inline float BounceClampBottom(float t) { + return fabs(t); + } + + inline float BounceClampTop(float t) { + return 1.f - fabs(1.f - t); + } + + // + // Easing function library + // see Math for Game Programmers: Fast and Funky 1D Nonlinear Transformations, GDC 2015 + // + + inline float SmoothStart2(float t) { + return t * t; + } + + inline float SmoothStart3(float t) { + return t * t * t; + } + + inline float SmoothStart4(float t) { + return t * t * t * t; + } + + inline float SmoothStart5(float t) { + return t * t * t * t * t; + } + + inline float SmoothStop2(float t) { + constexpr tFlip = 1 - y; + return 1 - (tFlip * tFlip); + } + + inline float SmoothStop3(float t) { + constexpr tFlip = 1 - y; + return 1 - (tFlip * tFlip); + } + + inline float SmoothStop4(float t) { + constexpr tFlip = 1 - y; + return 1 - (tFlip * tFlip * tFlip * tFlip); + } + + inline float SmoothStop5(float t) { + constexpr tFlip = 1 - y; + return 1 - (tFlip * tFlip * tFlip * tFlip * tFlip); + } + + // + // Bezier curves + // + + inline float NormalizedBezier3(float B, float C, float t) { + const float s = 1.f - t; + const float t2 = t * t; + const float s2 = s * s; + const float t3 = t2 * t; + return (3.f * B * s2 * t) + (3.f * C * s * t2) + t3; + } } diff --git a/include/fggl/math/triangulation.hpp b/include/fggl/math/triangulation.hpp index 9861a1d..fa7a20f 100644 --- a/include/fggl/math/triangulation.hpp +++ b/include/fggl/math/triangulation.hpp @@ -6,157 +6,156 @@ namespace fggl::math { - using Polygon = std::vector<math::vec2>; - using PolygonVertex = std::vector<data::Vertex2D>; - - constexpr int POSITIVE = 1; - constexpr int NEGATIVE = -1; - constexpr int UNSET = 0; - - /** - * Put an angle in the range [-PI, PI]. - */ - inline float clampAngle( float radianAngle ) { - if ( radianAngle <= M_PI ) { - return radianAngle + M_PI_2; - } else if ( radianAngle > M_PI ) { - return radianAngle - M_PI_2; - } else { - return radianAngle; - } - } - - static void checkSign( float value, int& sign, int& firstSign, int& flips ) { - if ( value > 0 ) { - if ( sign == UNSET ) { - firstSign = POSITIVE; - } else if ( sign < 0 ) { - flips++; - } - } else if ( value < 0 ) { - if ( sign == UNSET ) { - firstSign = NEGATIVE; - } else if ( sign > 0 ) { - flips++; - } - sign = NEGATIVE; - } - } - - /** - * Check if a polygon is convex. - * - * see https://math.stackexchange.com/a/1745427 - */ - bool isConvex(const Polygon& polygon) { - if ( polygon.size() < 3 ) { - return false; - } - - const auto n = polygon.size(); - - auto wSign = UNSET; - - auto xSign = UNSET; - auto xFirstSign = UNSET; - auto xFlips = 0; - - auto ySign = UNSET; - auto yFirstSign = UNSET; - auto yFlips = 0; - - auto curr = polygon[ n - 1 ]; - auto next = polygon[ n ]; - - for ( auto& v : polygon ) { - auto prev = curr; - curr = next; - next = v; - - auto before = curr - prev; - auto after = next - curr; - - checkSign( after.x, xSign, xFirstSign, xFlips ); - if ( xFlips > 2 ) { - return false; - } - - checkSign( after.y, ySign, yFirstSign, yFlips ); - if ( yFlips > 2 ) { - return false; - } - - auto w = before.x * after.y - after.x * before.y; - if ( wSign == UNSET && w != 0 ) { - wSign = w; - } else if ( wSign > 0 && w < 0 ) { - return false; - } else if ( wSign < 0 && w > 0 ){ - return false; - } - } - - if ( xSign != UNSET && ( xFirstSign != UNSET ) && ( xSign != xFirstSign ) ) { - xFlips += 1; - } - if ( ySign != UNSET && ( yFirstSign != UNSET ) && ( ySign != yFirstSign ) ) { - yFlips += 1; - } - if ( xFlips != 2 || yFlips != 2 ) { - return false; - } - - return true; - } - - static data::Vertex2D pointToVertex( const math::vec2& point ) { - return data::Vertex2D{ - .position = point, - .colour = {1.0f, 1.0f, 1.0f} - }; - } - - /** - * Fast Triangulation for convex polygons. - */ - void fanTriangulation(const Polygon& polygon, data::Mesh2D& mesh) { - assert(polygon.size() >= 3); - - // add the first two points to the mesh - auto firstIdx = mesh.add_vertex( pointToVertex(polygon[0]) ); - auto prevIdx = mesh.add_vertex( pointToVertex(polygon[1]) ); - - // deal with the indicies - const auto nTris = polygon.size() - 2; - for ( auto i=0; i < nTris; i++ ) { - mesh.add_index( firstIdx ); - mesh.add_index( prevIdx ); - - auto currIdx = mesh.add_vertex( pointToVertex(polygon[i + 2]) ); - mesh.add_index( currIdx ); - prevIdx = currIdx; - } - } - - void fanTriangulation(const PolygonVertex polygon, data::Mesh2D& mesh) { - assert(polygon.size() >= 3); - - // add the first two points to the mesh - auto firstIdx = mesh.add_vertex( polygon[0] ); - auto prevIdx = mesh.add_vertex( polygon[1] ); - - // deal with the indicies - const auto nTris = polygon.size() - 2; - for ( auto i=0; i < nTris; i++ ) { - mesh.add_index( firstIdx ); - mesh.add_index( prevIdx ); - - auto currIdx = mesh.add_vertex( polygon[i + 2] ); - mesh.add_index( currIdx ); - prevIdx = currIdx; - } - } - + using Polygon = std::vector<math::vec2>; + using PolygonVertex = std::vector<data::Vertex2D>; + + constexpr int POSITIVE = 1; + constexpr int NEGATIVE = -1; + constexpr int UNSET = 0; + + /** + * Put an angle in the range [-PI, PI]. + */ + inline float clampAngle(float radianAngle) { + if (radianAngle <= M_PI) { + return radianAngle + M_PI_2; + } else if (radianAngle > M_PI) { + return radianAngle - M_PI_2; + } else { + return radianAngle; + } + } + + static void checkSign(float value, int &sign, int &firstSign, int &flips) { + if (value > 0) { + if (sign == UNSET) { + firstSign = POSITIVE; + } else if (sign < 0) { + flips++; + } + } else if (value < 0) { + if (sign == UNSET) { + firstSign = NEGATIVE; + } else if (sign > 0) { + flips++; + } + sign = NEGATIVE; + } + } + + /** + * Check if a polygon is convex. + * + * see https://math.stackexchange.com/a/1745427 + */ + bool isConvex(const Polygon &polygon) { + if (polygon.size() < 3) { + return false; + } + + const auto n = polygon.size(); + + auto wSign = UNSET; + + auto xSign = UNSET; + auto xFirstSign = UNSET; + auto xFlips = 0; + + auto ySign = UNSET; + auto yFirstSign = UNSET; + auto yFlips = 0; + + auto curr = polygon[n - 1]; + auto next = polygon[n]; + + for (auto &v : polygon) { + auto prev = curr; + curr = next; + next = v; + + auto before = curr - prev; + auto after = next - curr; + + checkSign(after.x, xSign, xFirstSign, xFlips); + if (xFlips > 2) { + return false; + } + + checkSign(after.y, ySign, yFirstSign, yFlips); + if (yFlips > 2) { + return false; + } + + auto w = before.x * after.y - after.x * before.y; + if (wSign == UNSET && w != 0) { + wSign = w; + } else if (wSign > 0 && w < 0) { + return false; + } else if (wSign < 0 && w > 0) { + return false; + } + } + + if (xSign != UNSET && (xFirstSign != UNSET) && (xSign != xFirstSign)) { + xFlips += 1; + } + if (ySign != UNSET && (yFirstSign != UNSET) && (ySign != yFirstSign)) { + yFlips += 1; + } + if (xFlips != 2 || yFlips != 2) { + return false; + } + + return true; + } + + static data::Vertex2D pointToVertex(const math::vec2 &point) { + return data::Vertex2D{ + .position = point, + .colour = {1.0f, 1.0f, 1.0f} + }; + } + + /** + * Fast Triangulation for convex polygons. + */ + void fanTriangulation(const Polygon &polygon, data::Mesh2D &mesh) { + assert(polygon.size() >= 3); + + // add the first two points to the mesh + auto firstIdx = mesh.add_vertex(pointToVertex(polygon[0])); + auto prevIdx = mesh.add_vertex(pointToVertex(polygon[1])); + + // deal with the indicies + const auto nTris = polygon.size() - 2; + for (auto i = 0; i < nTris; i++) { + mesh.add_index(firstIdx); + mesh.add_index(prevIdx); + + auto currIdx = mesh.add_vertex(pointToVertex(polygon[i + 2])); + mesh.add_index(currIdx); + prevIdx = currIdx; + } + } + + void fanTriangulation(const PolygonVertex polygon, data::Mesh2D &mesh) { + assert(polygon.size() >= 3); + + // add the first two points to the mesh + auto firstIdx = mesh.add_vertex(polygon[0]); + auto prevIdx = mesh.add_vertex(polygon[1]); + + // deal with the indicies + const auto nTris = polygon.size() - 2; + for (auto i = 0; i < nTris; i++) { + mesh.add_index(firstIdx); + mesh.add_index(prevIdx); + + auto currIdx = mesh.add_vertex(polygon[i + 2]); + mesh.add_index(currIdx); + prevIdx = currIdx; + } + } } // namespace fggl::util diff --git a/include/fggl/math/types.hpp b/include/fggl/math/types.hpp index 4d12beb..4730553 100644 --- a/include/fggl/math/types.hpp +++ b/include/fggl/math/types.hpp @@ -12,84 +12,91 @@ namespace fggl::math { // math types (aliased for ease of use) using vec4 = glm::vec4; + using vec3 = glm::vec3; + using vec3i = glm::ivec3; + using vec3f = glm::vec3; + using vec2 = glm::vec2; + using vec2i = glm::ivec2; + using vec2f = glm::vec2; + using mat4 = glm::mat4; using quat = glm::quat; - // fastFloor from OpenSimplex2 - inline int fastFloor(double x) { - int xi = (int)x; - return x < xi ? xi - 1 : xi; - } + // fastFloor from OpenSimplex2 + inline int fastFloor(double x) { + int xi = (int) x; + return x < xi ? xi - 1 : xi; + } // reference vectors - constexpr vec3 UP { 0.0f, 1.0f, 0.0f }; - constexpr vec3 FORWARD { 1.0f, 0.0f, 0.0f }; - constexpr vec3 RIGHT { 0.0f, 0.0f, 1.0f }; + constexpr vec3f UP{0.0f, 1.0f, 0.0f}; + constexpr vec3f FORWARD{1.0f, 0.0f, 0.0f}; + constexpr vec3f RIGHT{0.0f, 0.0f, 1.0f}; - inline glm::mat4 modelMatrix( const vec3 offset, const quat rotation ) { - return glm::translate( glm::mat4(1.0f), offset ) * glm::toMat4( rotation ); + inline glm::mat4 modelMatrix(const vec3 offset, const quat rotation) { + return glm::translate(glm::mat4(1.0f), offset) * glm::toMat4(rotation); } - inline glm::mat4 modelMatrix( glm::vec3 offset, glm::vec3 eulerAngles ) { - return modelMatrix( offset, glm::quat( eulerAngles ) ); + inline glm::mat4 modelMatrix(glm::vec3 offset, glm::vec3 eulerAngles) { + return modelMatrix(offset, glm::quat(eulerAngles)); } struct Transform { - constexpr static const char name[] = "Transform"; - - Transform() : m_local(1.0f), m_origin(0.0f), m_model(1.0f), m_rotation() { - } - - // local reference vectors - [[nodiscard]] - inline vec3 up() const { - return vec4( UP, 1.0 ) * m_local; - } - - [[nodiscard]] - inline vec3 forward() const { - return vec4( FORWARD, 1.0 ) * m_local; - } - - [[nodiscard]] - inline vec3 right() const { - return vec4( RIGHT, 1.0 ) * m_local; - } - - inline void translate(const vec3 change) { - m_origin += change; - update(); - } - - inline void origin(const vec3 pos) { - m_origin = pos; - update(); - } - - [[nodiscard]] - inline vec3 origin() const { - return m_origin; - } - - inline void euler(vec3 angles) { - m_rotation = quat( angles ); - update(); - } - - [[nodiscard]] - inline glm::vec3 euler() const { - return glm::eulerAngles(m_rotation); - } - - inline mat4 model() const { - mat4 tmp(1.0f); - tmp = glm::translate(tmp, m_origin); - tmp = tmp * glm::toMat4(m_rotation); - - return tmp; - } + constexpr static const char name[] = "Transform"; + + Transform() : m_local(1.0f), m_origin(0.0f), m_model(1.0f), m_rotation() { + } + + // local reference vectors + [[nodiscard]] + inline vec3 up() const { + return vec4(UP, 1.0) * m_local; + } + + [[nodiscard]] + inline vec3 forward() const { + return vec4(FORWARD, 1.0) * m_local; + } + + [[nodiscard]] + inline vec3 right() const { + return vec4(RIGHT, 1.0) * m_local; + } + + inline void translate(const vec3 change) { + m_origin += change; + update(); + } + + inline void origin(const vec3 pos) { + m_origin = pos; + update(); + } + + [[nodiscard]] + inline vec3 origin() const { + return m_origin; + } + + inline void euler(vec3 angles) { + m_rotation = quat(angles); + update(); + } + + [[nodiscard]] + inline glm::vec3 euler() const { + return glm::eulerAngles(m_rotation); + } + + inline mat4 model() const { + mat4 tmp(1.0f); + tmp = glm::translate(tmp, m_origin); + tmp = tmp * glm::toMat4(m_rotation); + + return tmp; + } private: mat4 m_local; // us -> parent @@ -100,7 +107,7 @@ namespace fggl::math { inline void update() { mat4 t(1.0f); t *= glm::toMat4(m_rotation); - t = glm::translate( t, m_origin ); + t = glm::translate(t, m_origin); m_local = t; } }; @@ -110,11 +117,10 @@ namespace fggl::math { // feels a bit strange to be doing this... namespace glm { - inline bool operator<(const vec3& lhs, const vec3& rhs) { - return std::tie( lhs.x, lhs.y, lhs.z ) - < std::tie( rhs.x, rhs.y, rhs.z ); + inline bool operator<(const vec3 &lhs, const vec3 &rhs) { + return std::tie(lhs.x, lhs.y, lhs.z) + < std::tie(rhs.x, rhs.y, rhs.z); } } - #endif diff --git a/include/fggl/scenes/Scene.h b/include/fggl/scenes/Scene.h index d4c9daf..0ca9987 100644 --- a/include/fggl/scenes/Scene.h +++ b/include/fggl/scenes/Scene.h @@ -11,38 +11,42 @@ namespace fggl::scenes { - class Scene { - public: - virtual ~Scene() = default; - - virtual void setup() = 0; - virtual void cleanup() = 0; - - virtual void update() = 0; - virtual void render() = 0; - }; - - class SceneManager { - public: - SceneManager() = default; - - void create(const std::string& name, std::shared_ptr<Scene> scene); - void activate(const std::string& name); - - inline void update() { - if ( m_active == nullptr ) { return; } - m_active->update(); - } - - inline void render() { - if ( m_active == nullptr ) { return; } - m_active->render(); - } - - private: - std::shared_ptr<Scene> m_active; - std::unordered_map<std::string, std::shared_ptr<Scene>> m_scenes; - }; + class Scene { + public: + virtual ~Scene() = default; + + virtual void setup() = 0; + virtual void cleanup() = 0; + + virtual void update() = 0; + virtual void render() = 0; + }; + + class SceneManager { + public: + SceneManager() = default; + + void create(const std::string &name, std::shared_ptr<Scene> scene); + void activate(const std::string &name); + + inline void update() { + if (m_active == nullptr) { + return; + } + m_active->update(); + } + + inline void render() { + if (m_active == nullptr) { + return; + } + m_active->render(); + } + + private: + std::shared_ptr<Scene> m_active; + std::unordered_map<std::string, std::shared_ptr<Scene>> m_scenes; + }; } diff --git a/include/fggl/scenes/menu.hpp b/include/fggl/scenes/menu.hpp index 14cd63e..6e36489 100644 --- a/include/fggl/scenes/menu.hpp +++ b/include/fggl/scenes/menu.hpp @@ -11,28 +11,28 @@ namespace fggl::scenes { - using callback = std::function<void(void)>; + using callback = std::function<void(void)>; - class BasicMenu : public AppState { - public: - BasicMenu(App& owner); + class BasicMenu : public AppState { + public: + BasicMenu(App &owner); - void update() override; - void render(gfx::Paint& paint) override; + void update() override; + void render(gfx::Paint &paint) override; - void activate() override; - void deactivate() override; + void activate() override; + void deactivate() override; - void add(const std::string& label, callback cb); + void add(const std::string &label, callback cb); - private: - std::shared_ptr<input::Input> m_inputs; - std::map<const std::string, callback> m_items; + private: + std::shared_ptr<input::Input> m_inputs; + std::map<const std::string, callback> m_items; - // menu state - std::string m_active; - math::vec2 m_cursorPos; - }; + // menu state + std::string m_active; + math::vec2 m_cursorPos; + }; } // namepace fggl::scenes diff --git a/include/fggl/util/chrono.hpp b/include/fggl/util/chrono.hpp index 64ca2e3..bca8312 100644 --- a/include/fggl/util/chrono.hpp +++ b/include/fggl/util/chrono.hpp @@ -4,7 +4,7 @@ namespace fggl::util { class Timer { - using time_t = unsigned long; + using time_t = unsigned long; public: inline void frequency(float freq) { m_freq = freq; diff --git a/include/fggl/util/service.h b/include/fggl/util/service.h index 4fe8bd8..d7f6044 100644 --- a/include/fggl/util/service.h +++ b/include/fggl/util/service.h @@ -11,42 +11,42 @@ namespace fggl::util { - class ServiceLocator { - inline static ServiceLocator *s_instance; - std::unordered_map<std::type_index, std::shared_ptr<void>> m_services; - ServiceLocator() = default; - - public: - // don't allow copying or moving - ServiceLocator(ServiceLocator& other) = delete; - ServiceLocator(ServiceLocator&& other) = delete; - - static ServiceLocator& instance() { - if ( s_instance == nullptr ) { - s_instance = new ServiceLocator(); - } - return *s_instance; - } - - template<typename T> - void supply(std::shared_ptr<T> ptr) { - auto info = std::type_index(typeid(T)); - m_services[info] = ptr; - } - - template<typename T> - std::shared_ptr<T> get() { - auto info = std::type_index(typeid(T)); - return std::static_pointer_cast<T>(m_services.at(info)); - } - - template<typename T> - std::shared_ptr<T> providePtr() { - auto info = std::type_index(typeid(T)); - return std::static_pointer_cast<T>(m_services.at(info)); - } - - }; + class ServiceLocator { + inline static ServiceLocator *s_instance; + std::unordered_map<std::type_index, std::shared_ptr<void>> m_services; + ServiceLocator() = default; + + public: + // don't allow copying or moving + ServiceLocator(ServiceLocator &other) = delete; + ServiceLocator(ServiceLocator &&other) = delete; + + static ServiceLocator &instance() { + if (s_instance == nullptr) { + s_instance = new ServiceLocator(); + } + return *s_instance; + } + + template<typename T> + void supply(std::shared_ptr<T> ptr) { + auto info = std::type_index(typeid(T)); + m_services[info] = ptr; + } + + template<typename T> + std::shared_ptr<T> get() { + auto info = std::type_index(typeid(T)); + return std::static_pointer_cast<T>(m_services.at(info)); + } + + template<typename T> + std::shared_ptr<T> providePtr() { + auto info = std::type_index(typeid(T)); + return std::static_pointer_cast<T>(m_services.at(info)); + } + + }; } diff --git a/include/fggl/util/states.hpp b/include/fggl/util/states.hpp index 29067a7..1eb2970 100644 --- a/include/fggl/util/states.hpp +++ b/include/fggl/util/states.hpp @@ -28,50 +28,50 @@ namespace fggl::util { - template<typename S, typename I> - class StateMachine { - public: - using Identifer = I; - using StateType = S; - - StateMachine() = default; - ~StateMachine() = default; - - // class is non copy-able - StateMachine(const StateMachine& app) = delete; - StateMachine& operator=(StateMachine other) = delete; - - template<typename T, typename... Args> - T* put(const Identifer& name, Args&&... args) { - static_assert( std::is_base_of<StateType,T>::value, "States must be AppStates"); - m_states[name] = std::make_unique<T>( std::forward<Args...>(args...) ); - - // no active scene? first is the active one - if ( m_active.empty() ) { - m_active = name; - } - - return (T*)(m_states[name].get()); - } - - void change(const Identifer& name) { - assertm( m_states.find(name) != m_states.end(), "state does not exist"); - - active().deactivate(); - m_active = name; - active().activate(); - } - - StateType& active() const { - assertm( m_states.find(m_active) != m_states.end(), "active state does not exist!"); - return *(m_states.at( m_active ).get()); - } - - private: - Identifer m_active; - std::unordered_map<Identifer, std::unique_ptr<StateType>> m_states; - - }; + template<typename S, typename I> + class StateMachine { + public: + using Identifer = I; + using StateType = S; + + StateMachine() = default; + ~StateMachine() = default; + + // class is non copy-able + StateMachine(const StateMachine &app) = delete; + StateMachine &operator=(StateMachine other) = delete; + + template<typename T, typename... Args> + T *put(const Identifer &name, Args &&... args) { + static_assert(std::is_base_of<StateType, T>::value, "States must be AppStates"); + m_states[name] = std::make_unique<T>(std::forward<Args...>(args...)); + + // no active scene? first is the active one + if (m_active.empty()) { + m_active = name; + } + + return (T *) (m_states[name].get()); + } + + void change(const Identifer &name) { + assertm(m_states.find(name) != m_states.end(), "state does not exist"); + + active().deactivate(); + m_active = name; + active().activate(); + } + + StateType &active() const { + assertm(m_states.find(m_active) != m_states.end(), "active state does not exist!"); + return *(m_states.at(m_active).get()); + } + + private: + Identifer m_active; + std::unordered_map<Identifer, std::unique_ptr<StateType>> m_states; + + }; } -- GitLab