From b9026ec8da8052e0376bd19a34ee8c2b17a89d7c Mon Sep 17 00:00:00 2001 From: Medusa Slockbower Date: Mon, 6 Oct 2025 12:47:11 -0400 Subject: [PATCH] - Adjustments to component system design. - Added indexed parameters to format strings --- CMakeLists.txt | 1 + cmake/gcc.cmake | 2 +- include/fennec/gfx3d/mesh_instance.h | 47 +++++ include/fennec/lang/startup.h | 2 +- include/fennec/lang/typed.h | 5 + include/fennec/langproc/strings/format.h | 110 +++++++---- include/fennec/renderers/opengl/lib/buffer.h | 4 +- include/fennec/scene/component.h | 191 ++++--------------- include/fennec/scene/scene_node.h | 37 +++- source/platform/linux/platform.cpp | 2 +- test/main.cpp | 2 +- test/tests/langproc/test_format.h | 3 +- 12 files changed, 207 insertions(+), 199 deletions(-) create mode 100644 include/fennec/gfx3d/mesh_instance.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5577ed2..41f6fa1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,7 @@ add_library(fennec STATIC # EXTRA SOURCES ======================================================================================================== ${FENNEC_EXTRA_SOURCES} + include/fennec/gfx3d/mesh_instance.h ) add_dependencies(fennec metaprogramming fennec-dependencies) diff --git a/cmake/gcc.cmake b/cmake/gcc.cmake index 74fc81d..53569fa 100644 --- a/cmake/gcc.cmake +++ b/cmake/gcc.cmake @@ -18,7 +18,7 @@ # this script sets flags and variables for gnu and gnu-like compilers -add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror") +add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror" "-fms-extensions") fennec_add_link_options("-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates") diff --git a/include/fennec/gfx3d/mesh_instance.h b/include/fennec/gfx3d/mesh_instance.h new file mode 100644 index 0000000..03041a4 --- /dev/null +++ b/include/fennec/gfx3d/mesh_instance.h @@ -0,0 +1,47 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +/// +/// \file mesh_instance.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_GFX3D_MESH_INSTANCE_H +#define FENNEC_GFX3D_MESH_INSTANCE_H +#include + +namespace fennec +{ + +class mesh_instance : component { +public: + + +private: +}; + +} + +#endif // FENNEC_GFX3D_MESH_INSTANCE_H \ No newline at end of file diff --git a/include/fennec/lang/startup.h b/include/fennec/lang/startup.h index d4de864..9f299b4 100644 --- a/include/fennec/lang/startup.h +++ b/include/fennec/lang/startup.h @@ -20,7 +20,7 @@ #define FENNEC_LANG_STARTUP_H // Helper for running a function before main() -#define STATIC_CONSTRUCTOR(f) \ +#define FENNEC_STATIC_CONSTRUCTOR(f) \ inline static void f(void); \ struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \ inline static void f(void) diff --git a/include/fennec/lang/typed.h b/include/fennec/lang/typed.h index c79d60c..3d3f9c5 100644 --- a/include/fennec/lang/typed.h +++ b/include/fennec/lang/typed.h @@ -54,6 +54,11 @@ public: typed(TypeT*) : type(typeuuid()) { } + + template + static uint64_t id() { + return typeuuid(); + } }; } diff --git a/include/fennec/langproc/strings/format.h b/include/fennec/langproc/strings/format.h index 4dee0c9..200b0f9 100644 --- a/include/fennec/langproc/strings/format.h +++ b/include/fennec/langproc/strings/format.h @@ -66,12 +66,16 @@ struct format_string { private: static constexpr size_t npos = -1; - enum token_ : uint8_t { - token_text = 0, - token_param, + enum token_ : bool { + token_text = false, + token_param = true, }; - using token = pair; + struct token { + uint8_t type; + string_view view; + size_t param; + }; constexpr const char* find(const char* start, char c) { while (*start != c && *start != '\0') { @@ -80,47 +84,64 @@ private: return start; } + constexpr size_t parse_index(const char* start, size_t n) { + size_t x = 0; + while (n-- > 0) { + x *= 10; + x += *start - '0'; + } + return x; + } + // Constructors & Destructor =========================================================================================== public: format_string() = delete; - format_string(const char (&str)[N]) + constexpr format_string(const char (&str)[N]) : _tokens(), _num_tokens(0) { - static_assert(is_constant_evaluated(), "Runtime format strings are not supported."); - // TODO: Numbered Parameters - size_t params = 0; - for (size_t i = 0; i < N; ++i) { - size_t n = this->find(str + i, '{') - str; + size_t params = 0; + for (size_t i = 0; i < N - 1; ++i) { + size_t l = this->find(str + i, '{') - str; // Push the current token - if (n - i > 0) { - _tokens[_num_tokens++] = { token_text, { &str[i], n - i } }; + if (l - i > 0) { + _tokens[_num_tokens++] = { token_text, { &str[i], l - i }, npos }; } // no more braces, break - if (n >= N) { + if (l >= N) { break; } // escaped brace - if (str[n + 1] == '{') { - _tokens[_num_tokens++] = { token_text, { &str[n], 1 } }; - i = n + 1; + if (str[l + 1] == '{') { + _tokens[_num_tokens++] = { token_text, { &str[l], 1 }, npos }; + i = l + 1; continue; } - size_t e = this->find(str + n, '}') - str; - //static_assert(e >= N, "Malformed format string, mismatched brace"); + size_t e = this->find(str + l, '}') - str; + assertd(e < N, "Malformed format string, mismatched brace"); - _tokens[_num_tokens++] = { token_param, { &str[n], e - n + 1 } }; - ++params; + // Colon found + size_t c = this->find(str + l, ':') - str; + if (c < e) { + size_t n = c - l; + if (n > 1) { // Check if index is provided + params = this->parse_index(str + l + 1, n - 1); + } + l = c; + } else if (e - l > 1) { // check if index is provided + params = this->parse_index(str + l + 1, e - l - 1); + } + + assertd(params < sizeof...(ArgsT), "Malformed format string, parameter count does not match argument count."); + _tokens[_num_tokens++] = { token_param, { &str[l + 1], e - l - 1 }, params++ }; i = e; } - - //static_assert(params == sizeof...(ArgsT), "Malformed format string, parameter count does not match argument count."); } constexpr format_string(const format_string&) = default; @@ -129,33 +150,48 @@ public: ~format_string() = default; string format(ArgsT&&...args) const { - return this->_format(0, fennec::forward(args)...); + string parts[N]; + + // Copy raw text tokens + size_t i = 0; + for (i = 0; i < _num_tokens; ++i) { + const token& tok = _tokens[i]; + if (not tok.type) { + parts[i] = tok.view; + } + } + + // Copy arguments + this->_format(0, parts, fennec::forward(args)...); + + string res; + for (i = 0; i < _num_tokens; ++i) { + res += parts[i]; + } + return res; } private: array _tokens; size_t _num_tokens; - string _format(size_t) const { - return string{""}; + void _format(size_t) const { } template - string _format(size_t i, HeadT&& head, RestT&&...rest) const { - string res; - for (; i < _num_tokens; ++i) { - const auto& token = _tokens[i]; - - if (token.first == token_param) { - formatter fmt; - res += fmt(token.second, head); - res += this->_format(i + 1, fennec::forward(rest)...); + void _format(size_t p, string (&parts)[N], HeadT&& head, RestT&&...rest) const { + size_t i = 0; + for (const token& tok : _tokens) { + if (i >= _num_tokens) { break; } - - res += token.second; + if (tok.param == p) { + formatter fmt; + parts[i] = fmt(tok.view, head); + } + ++i; } - return res; + this->_format(p + 1, fennec::forward(rest)...); } }; diff --git a/include/fennec/renderers/opengl/lib/buffer.h b/include/fennec/renderers/opengl/lib/buffer.h index 22f136b..eed39bc 100644 --- a/include/fennec/renderers/opengl/lib/buffer.h +++ b/include/fennec/renderers/opengl/lib/buffer.h @@ -55,9 +55,9 @@ private: GLenum res; // Set READ/DRAW/COPY - if constexpr (read) { + if constexpr (map_read) { res = GL_STREAM_READ; - } else if constexpr (write) { + } else if constexpr (map_write) { res = GL_STREAM_DRAW; } else { res = GL_STREAM_COPY; diff --git a/include/fennec/scene/component.h b/include/fennec/scene/component.h index c1cb442..06e17f7 100644 --- a/include/fennec/scene/component.h +++ b/include/fennec/scene/component.h @@ -21,30 +21,27 @@ #include #include -#include #include #include +#include #include #include #include +#define FENNEC_REGISTER_COMPONENT(T, p) \ + FENNEC_STATIC_CONSTRUCTOR(T) { \ + component::register_type(p) \ + } + namespace fennec { -struct component_t { - uint64_t type; - size_t id; -}; - class component : public typed { public: // TYPEDEFS & CONSTANTS ================================================================================================ - using component_create = size_t (*)(size_t, size_t); - using component_get = component* (*)(size_t); - using component_destroy = void (*)(size_t); - using component_tick = void (*)(size_t, double); - using component_frame = void (*)(size_t, uint64_t); + using component_create = component* (*)(size_t, size_t); + using component_destroy = void (*)(component*); // TYPE OPERATIONS ===================================================================================================== @@ -52,31 +49,22 @@ public: struct typeinfo { string name; component_create create; - component_destroy destroy; - component_get get; - component_tick tick; - component_frame frame; typeinfo() : name("") - , create(nullptr), destroy(nullptr) - , get(nullptr) - , tick(nullptr), frame(nullptr) { + , create(nullptr) { } typeinfo(const string& name, - component_create create, component_destroy destroy, - component_get get, - component_tick tick, component_frame frame) + component_create create) : name(name) - , create(create), destroy(destroy), get(get) - , tick(tick), frame(frame) { + , create(create) { } }; struct typeentry { - string name; - uint64_t id; + string name; + uint64_t type; }; using typelist_t = dynarray>; @@ -92,15 +80,13 @@ private: static constexpr size_t npos = typetree_t::npos; static constexpr size_t root = typetree_t::root; - static void _register_type( uint64_t id, const path& path, - component_create create, component_destroy destroy, - component_get get, - component_tick tick, component_frame frame) { + static void _register_type( uint64_t id, const path& path, + component_create create) { // Register the type if (id > _typelist.size()) { _typelist.resize(id + 1); } - _typelist[id] = typeinfo{ path.filename(), create, destroy, get, tick, frame }; + _typelist[id] = typeinfo{ path.filename(), create }; // Create tree entry size_t node = root; @@ -112,7 +98,7 @@ private: node = _typetree.child(parent); - while (node != npos && _typetree[node].id != id) { + while (node != npos && _typetree[node].type != id) { node = _typetree.next(node); } @@ -125,33 +111,6 @@ private: } } - struct noderef { - size_t scene, node; - }; - - using compstorage_t = object_pool; // Holds refs to default allocated components - using defaultstorage_t = map; // Maps types to respective storage - - inline static defaultstorage_t _default_storage; - - static auto& _type_info(uint64_t type) { - return _typelist[type]; - } - - template - static auto& _type_info() { - return _typelist[uuid()]; - } - - static auto& _type_storage(uint64_t type) { - return *_default_storage[type]; - } - - template - static auto& _type_storage() { - return *_default_storage[uuid()]; - } - /// /// \brief Default creation function for a component /// \tparam ComponentT Type of the component @@ -159,60 +118,39 @@ private: /// \returns The component created by this function template static constexpr component* default_create(size_t scene, size_t node) { - auto& storage = _type_storage(); - return storage.insert(new ComponentT(scene, node)); - } - - /// - /// \brief Default destruction function for a component - /// \tparam ComponentT Type of the component - /// \param node The node the component is associated with - template - static constexpr void default_destroy(size_t comp) { - auto& storage = _type_storage(); - - delete storage[comp]; - storage.erase(comp); - } - - /// - /// \brief Default retrieval function for a component - /// \tparam ComponentT Type of the component - /// \param node The node the component is associated with - /// \returns The component found by this function - template - static constexpr component* default_get(size_t comp) { - auto& storage = _type_storage(); - return storage[comp]; + return new ComponentT(scene, node); } // Public Registry ===================================================================================================== public: - /// - /// \brief Get an uuid for the specified component type - /// \tparam ComponentT The component type - /// \returns An uuid that is associated with the type + static constexpr const typetree_t& typelist() { + return _typetree; + } + template - static uint64_t uuid() { - return typeuuid(); + static constexpr const typeinfo& type_info() { + const auto& it = _typelist[id()]; + assert(it, "Unregistered type provided to component::type_info, see stacktrace for more info."); + return *it; + } + + static constexpr const typeinfo& type_info(uint64_t type) { + const auto& it = _typelist[type]; + assert(it, "Unregistered type provided to component::type_info, see stacktrace for more info."); + return *it; } /// /// \brief Register a type with the component system /// \tparam ComponentT The component type - /// \param name The name of the type + /// \param path The name of the type /// \param create The function used to create a component, given a scene node. **MUST NOT BE NULL** - /// \param get The function used to find a component, given a scene node. **MUST NOT BE NULL** - /// \param destroy The function used to destroy a component of a scene node. **MUST NOT BE NULL** template - static void register_type(const cstring& name, - component_tick tick, component_frame frame, - component_create create = default_create, - component_get get = default_get, - component_destroy destroy = default_destroy) { - component::_register_type(uuid(), name, create, destroy, get, tick, frame); + static void register_type(const path& path, + component_create create = default_create) { + component::_register_type(id(), path, create); } /// @@ -220,72 +158,25 @@ public: /// \param type The type to create /// \param node The node to associate the component with /// \returns The created component - static size_t create(uint64_t type, size_t scene, size_t node) { + static component* create(uint64_t type, size_t scene, size_t node) { auto& typei = _typelist[type]; return typei->create(scene, node); } /// - /// \brief Create a component of type `ComponentT` + /// \brief Create a component of type `type` /// \tparam ComponentT The type to create /// \param node The node to associate the component with /// \returns The created component template - static size_t create(size_t scene, size_t node) { - auto& typei = _typelist[uuid()]; + static component* create(size_t scene, size_t node) { + auto& typei = _typelist[id()]; return typei->create(scene, node); } - /// - /// \brief Get a component of type `type` from id `id` - /// \param type The type of component - /// \param id The id of the component instance - /// \return - static component* get(const component_t& c) { - auto& typei = _typelist[c.type]; - return typei->get(c.id); - } - - template - static component* get(size_t id) { - auto& typei = _typelist[uuid()]; - return typei->get(id); - } - - static void destroy(size_t type, size_t comp) { - auto& typei = _typelist[type]; - return typei->destroy(comp); - } - - template - static void destroy(size_t comp) { - auto& typei = _typelist[uuid()]; - return typei->destroy(comp); - } - - static void tick(size_t comp, double dT) { - for (auto& it : _typelist) { - if (it->tick) { - it->tick(comp, dT); - } - } - } - - static void frame(size_t comp, uint64_t f) { - for (auto& it : _typelist) { - if (it->frame) { - it->frame(comp, f); - } - } - } - - static const auto& type_list() { - return _typelist; - } - // MEMBERS ============================================================================================================= public: - const size_t node; + const size_t node; template component(ComponentT* type, size_t node) diff --git a/include/fennec/scene/scene_node.h b/include/fennec/scene/scene_node.h index a33c12f..f297235 100644 --- a/include/fennec/scene/scene_node.h +++ b/include/fennec/scene/scene_node.h @@ -35,20 +35,47 @@ #include #include +#include + namespace fennec { +struct scene_node_ref { + size_t scene; + size_t id; +}; + struct scene_node : typed { - const size_t scene; - const size_t id; + +// Public Members ====================================================================================================== +public: + union { + struct { + const size_t scene; + const size_t id; + }; + const scene_node_ref ref; + }; string name; - scene_node(size_t id, size_t scene, const string& name) - : id(id), scene(scene), name(name) { + scene_node(size_t scene, size_t id, string name) + : scene(scene), id(id), name(std::move(name)) { } + template + component* add_component() { + _components.push_back(component::create(scene, id)); + return _components.back(); + } + + component* add_component(uint64_t type) { + _components.push_back(component::create(type, scene, id)); + return _components.back(); + } + + private: - dynarray _components; + dynarray _components; }; } diff --git a/source/platform/linux/platform.cpp b/source/platform/linux/platform.cpp index 4d4f5c8..3175c5d 100644 --- a/source/platform/linux/platform.cpp +++ b/source/platform/linux/platform.cpp @@ -24,7 +24,7 @@ namespace fennec { -STATIC_CONSTRUCTOR(_init_linux) { +FENNEC_STATIC_CONSTRUCTOR(_init_linux) { static linux_platform platform; } diff --git a/test/main.cpp b/test/main.cpp index 7f1fb48..433ef20 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -48,7 +48,7 @@ int main(int, char **) fennec::test::fennec_test_math(); fennec_test_spacer(3); - fennec_test_header("format processing library"); + fennec_test_header("language processing library"); fennec_test_spacer(2); fennec::test::fennec_test_langproc(); fennec_test_spacer(3); diff --git a/test/tests/langproc/test_format.h b/test/tests/langproc/test_format.h index 6b3c241..7dc3ab4 100644 --- a/test/tests/langproc/test_format.h +++ b/test/tests/langproc/test_format.h @@ -40,7 +40,8 @@ namespace test inline void fennec_test_langproc_format() { - fennec_test_run(fennec::format("{}", "Hello World!"), string("Hello World!")); + fennec_test_run(fennec::format("{}", "Hello World!"), string("Hello World!")); + fennec_test_run(fennec::format("{0}", "Hello World!"), string("Hello World!")); } }