diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e51ba6..5577ed2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,8 @@ add_library(fennec STATIC # SCENE ================================================================================================================ include/fennec/scene/scene.h include/fennec/scene/component.h + include/fennec/scene/scene_node.h + include/fennec/scene/node2d.h # Renderers ============================================================================================================ @@ -217,9 +219,10 @@ add_library(fennec STATIC # langproc ================================================================================================================ # Strings - include/fennec/langproc/strings/cstring.h include/fennec/langproc/strings/locale.h + include/fennec/langproc/strings/cstring.h include/fennec/langproc/strings/string.h + include/fennec/langproc/strings/format.h include/fennec/langproc/strings/detail/_ctype.h @@ -244,8 +247,6 @@ add_library(fennec STATIC # EXTRA SOURCES ======================================================================================================== ${FENNEC_EXTRA_SOURCES} - include/fennec/langproc/strings/format.h - include/fennec/scene/components/transform2d.h ) add_dependencies(fennec metaprogramming fennec-dependencies) diff --git a/include/fennec/containers/object_pool.h b/include/fennec/containers/object_pool.h index 076cbb1..981e229 100644 --- a/include/fennec/containers/object_pool.h +++ b/include/fennec/containers/object_pool.h @@ -48,7 +48,8 @@ struct object_pool { // Definitions ========================================================================================================= public: using value_t = TypeT; - using table_t = allocation; + using elem_t = optional; + using table_t = dynarray; using freed_t = list; @@ -119,7 +120,8 @@ public: /// \returns a reference to the object with id `i` constexpr value_t& operator[](size_t i) { assert(i < capacity(), "Index out of Bounds!"); - return _table[i]; + assert(_table[i], "Attempted to access null object.") + return *_table[i]; } /// @@ -128,7 +130,8 @@ public: /// \returns a const-qualified reference to the object with id `i` constexpr const value_t& operator[](size_t i) const { assert(i < capacity(), "Index out of Bounds!"); - return _table[i]; + assert(_table[i], "Attempted to access null object.") + return *_table[i]; } /// @} @@ -169,7 +172,7 @@ public: /// \brief Erase an object from the pool /// \param i The id of the object constexpr void erase(size_t i) { - fennec::destruct(&_table[i]); + _table[i] = nullopt; _freed.push_back(i); --_size; } @@ -177,8 +180,11 @@ public: /// /// \brief Clear the object pool constexpr void clear() { - dynarray free(capacity(), false); + for (auto& it : _table) { + it = nullopt; + } _size = 0; + _freed.clear(); } /// @} @@ -201,10 +207,10 @@ private: template size_t _insert(ArgsT&&...args) { size_t i = _next_free(); - if (i >= _table.capacity()) { - _table.creallocate(fennec::max(_table.size() * 2, size_t(8))); + if (i >= _table.size()) { + _table.resize(fennec::max(_table.size() * 2, size_t(8))); } - fennec::construct(&_table[i], fennec::forward(args)...); + _table[i].emplace(fennec::forward(args)...); return i; } }; diff --git a/include/fennec/containers/rdtree.h b/include/fennec/containers/rdtree.h index 527088d..3fc1fbe 100644 --- a/include/fennec/containers/rdtree.h +++ b/include/fennec/containers/rdtree.h @@ -58,9 +58,9 @@ public: protected: struct node { - optional value; - size_t parent, child, prev, next; - size_t depth, num_children; + value_t value; + size_t parent, child, prev, next; + size_t depth, num_children; constexpr node() : value(nullopt) @@ -315,25 +315,15 @@ public: /// /// \param i The id of the node to access /// \returns A reference to the value of the node wrapped in an optional - constexpr value_t* operator[](size_t i) { - auto& it = _table[i].value; - if (it) { - return &*_table[i].value; - } else { - return nullptr; - } + constexpr value_t& operator[](size_t i) { + return _table[i].value; } /// /// \param i The id of the node to access /// \returns A const-qualified reference to the value of the node wrapped in an optional - constexpr const value_t* operator[](size_t i) const { - const auto& it = _table[i].value; - if (it) { - return &*_table[i].value; - } else { - return nullptr; - } + constexpr const value_t& operator[](size_t i) const { + return _table[i].value; } @@ -420,7 +410,7 @@ public: while (i != npos) { uint8_t mode = traversal_control_continue; if (_table[i].value) { - mode = visit(*_table[i].value, i); + mode = visit(_table[i].value, i); } if (mode == traversal_control_break) { break; diff --git a/include/fennec/langproc/compile/tokenizer.h b/include/fennec/langproc/compile/tokenizer.h index fd44438..6381faf 100644 --- a/include/fennec/langproc/compile/tokenizer.h +++ b/include/fennec/langproc/compile/tokenizer.h @@ -32,6 +32,8 @@ #define FENNEC_LANGPROC_FORMAT_TOKENIZER_H #include +#include +#include #include // @@ -55,7 +57,7 @@ namespace fennec { struct escape_sequence { - virtual size_t operator[](const std::string& str, size_t i) = 0; + virtual size_t operator[](const string& str, size_t i) = 0; }; struct tokenizer { @@ -91,12 +93,12 @@ private: list res; priority_queue> idx; - for (size_t i = 0; i < line.size(); ++i) { - - for (char c : delimiter) { - idx.emplace() + for (char c : delimiter) { + size_t i = 0; + while (i != line.size()) { + size_t n = line.find(c, i); + // TODO } - } return res; diff --git a/include/fennec/langproc/filesystem/path.h b/include/fennec/langproc/filesystem/path.h index 917660b..b1f83bc 100644 --- a/include/fennec/langproc/filesystem/path.h +++ b/include/fennec/langproc/filesystem/path.h @@ -19,6 +19,7 @@ #ifndef FENNEC_LANGPROC_IO_PATH_H #define FENNEC_LANGPROC_IO_PATH_H +#include #include namespace fennec @@ -32,6 +33,12 @@ namespace fennec struct path { public: +// Definitions ========================================================================================================= + + class iterator; + friend iterator; + + // Static Functions ==================================================================================================== /// \brief Get the current working directory @@ -154,6 +161,11 @@ public: return _str == p._str; } + string filename() const { + size_t i = _str.rfind('/'); + return _str.substring(i + 1); + } + const string& str() const { return _str; } const char* cstr() const { return _str.cstr(); } @@ -238,6 +250,81 @@ public: return working; } + +// Iterator ============================================================================================================ + + iterator begin() const { + return iterator(this, 0); + } + + iterator end() const { + return iterator(this, _str.size()); + } + + class iterator { + public: + constexpr iterator(const path* path, size_t p) + : _str(&path->_str) + , _pos(p) { + + // Handle end() + if (p == _str->size()) { + return; + } + + // Handle rooted paths +#ifdef FENNEC_PLATFORM_WINDOWS + if ((*_str)[1] == ':') { + _pos = max(_pos, size_t(3)); + } +#else + if ((*_str)[0] == '/') { + _pos = max(_pos, size_t(1)); + } +#endif + + // Ensure we are at the start of a directory/file name + if (_pos != 0 && (*_str)[_pos - 1] != '/') { + _pos = _str->find('/', _pos) + 1; + } + } + + constexpr iterator(const iterator&) = default; + constexpr iterator(iterator&&) noexcept = default; + + constexpr string operator*() const { + if ((*_str)[_pos] == '/') { + return string(""); + } + + size_t e = _str->find('/', _pos); + return _str->substring(_pos, e - _pos); + } + + constexpr iterator& operator++() { + _pos = min(_str->find('/', _pos) + 1, _str->size()); + return *this; + } + + constexpr iterator operator++(int) { + iterator it = *this; + this->operator++(); + return it; + } + + constexpr bool operator==(const iterator& rhs) const { + return _str == rhs._str and _pos == rhs._pos; + } + + constexpr bool operator!=(const iterator& rhs) const { + return _str != rhs._str or _pos != rhs._pos; + } + + private: + const string* _str; + size_t _pos; + }; + private: string _str; }; diff --git a/include/fennec/memory/allocator.h b/include/fennec/memory/allocator.h index 3a7dea8..f15b07e 100644 --- a/include/fennec/memory/allocator.h +++ b/include/fennec/memory/allocator.h @@ -627,7 +627,7 @@ public: return _data; } -private: +protected: alloc_t _alloc; // Allocator object value_t* _data; // Handle for the memory block size_t _capacity; // Capacity of the memory block in elements. diff --git a/include/fennec/scene/component.h b/include/fennec/scene/component.h index d9b05f4..190a0ba 100644 --- a/include/fennec/scene/component.h +++ b/include/fennec/scene/component.h @@ -21,19 +21,27 @@ #include #include +#include #include +#include #include #include +#include namespace fennec { -class component : typed { +struct component_t { + uint64_t type; + size_t id; +}; + +class component : public typed { public: // TYPEDEFS & CONSTANTS ================================================================================================ - using component_create = component (*)(size_t); - using component_find = component* (*)(size_t); + 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); @@ -41,44 +49,146 @@ public: // TYPE OPERATIONS ===================================================================================================== - struct type_info { + struct typeinfo { string name; component_create create; - component_find find; component_destroy destroy; + component_get get; component_tick tick; component_frame frame; + + typeinfo() + : name("") + , create(nullptr), destroy(nullptr) + , get(nullptr) + , tick(nullptr), frame(nullptr) { + } + + typeinfo(const string& name, + component_create create, component_destroy destroy, + component_get get, + component_tick tick, component_frame frame) + : name(name) + , create(create), destroy(destroy), get(get) + , tick(tick), frame(frame) { + } }; + struct typeentry { + string name; + uint64_t id; + }; + + using typelist_t = dynarray>; + using typetree_t = rdtree; + + +// Private Registry ==================================================================================================== private: - // Private Registry - static auto& _type_list() { - static dynarray> type_list; - return type_list; - } + static typelist_t _typelist; // Actual list of types + static typetree_t _typetree; // Tree for displaying types under subfolders - static void _register_type( uint64_t id, const cstring& name, - component_create create, component_find find, component_destroy destroy, - component_tick tick, component_frame frame) { - auto& type_list = _type_list(); - if (id > type_list.size()) { - type_list.resize(id + 1); + 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) { + // Register the type + if (id > _typelist.size()) { + _typelist.resize(id + 1); + } + _typelist[id] = typeinfo{ path.filename(), create, destroy, get, tick, frame }; + + // Create tree entry + size_t node = root; + path::iterator it = path.begin(); + while (it != path.end()) { + string name = *it++; + size_t parent = node; + bool end = it == path.end(); + + node = _typetree.child(parent); + + while (node != npos && _typetree[node].id != id) { + node = _typetree.next(node); + } + + if (node == npos) { + node = _typetree.emplace( + parent, npos, + name, end ? id : nullid + ); + } } - type_list[id] = type_info{ name, create, find, destroy, tick, frame }; } - static void _unregister_type(uint64_t id) { - _type_list()[id] = nullopt; + 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]; } - static auto& _default_storage() { - static map> data; - return data; + 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 + /// \param node The node to associate the component with + /// \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]; + } + + +// Public Registry ===================================================================================================== public: - // Public Registry /// /// \brief Get an uuid for the specified component type @@ -89,66 +199,30 @@ public: return typeuuid(); } - /// - /// \brief Default storage function for default creation, retrieval, and destruction - template - static constexpr auto& default_storage() { - auto& data = _default_storage(); - return data[uuid()]; - } - - /// - /// \brief Default creation function for a component - /// \tparam ComponentT Type of the component - /// \param node The node to associate the component with - /// \returns The component created by this function - template - static constexpr component* default_create(size_t node) { - auto& storage = default_storage(); - if (not storage[node]) { - storage.emplace(node); - } - return storage[node]; - } - - /// - /// \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_find(size_t node) { - auto& storage = default_storage(); - return storage[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 node) { - default_storage().erase(node); - } - /// /// \brief Register a type with the component system /// \tparam ComponentT The component type /// \param name The name of the type /// \param create The function used to create a component, given a scene node. **MUST NOT BE NULL** - /// \param find The function used to find 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, + static void register_type(const cstring& name, + component_tick tick, component_frame frame, component_create create = default_create, - component_find find = default_find, + component_get get = default_get, component_destroy destroy = default_destroy) { - component::_register_type(uuid(), name, create, find, destroy, tick, frame); + component::_register_type(uuid(), name, create, destroy, get, tick, frame); } - template - static void unregister_type() { - component::_unregister_type(uuid()); + /// + /// \brief Create a component of type `type` + /// \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) { + auto& typei = _typelist[type]; + return typei->create(scene, node); } /// @@ -157,42 +231,60 @@ public: /// \param node The node to associate the component with /// \returns The created component template - static component* create(size_t node) { - auto& type = _type_list()[uuid()]; - return type ? *type->create(node) : nullptr; + static size_t create(size_t scene, size_t node) { + auto& typei = _typelist[uuid()]; + 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(uint64_t type, size_t id) { + auto& typei = _typelist[type]; + return typei->get(id); } template - static component* find(size_t node) { - auto& type = _type_list()[uuid()]; - return type ? *type->find(node) : nullptr; + static component* get(size_t comp) { + auto& typei = _typelist[uuid()]; + return typei->get(comp); + } + + static void destroy(size_t type, size_t comp) { + auto& typei = _typelist[type]; + return typei->destroy(comp); } template - static void destroy(size_t node) { - auto& type = _type_list()[uuid()]; - return type ? *type->destroy(node) : nullptr; + static void destroy(size_t comp) { + auto& typei = _typelist[uuid()]; + return typei->destroy(comp); } - static void tick(size_t node, double dT) { - for (auto& it : _type_list()) { - it->tick(node, dT); + static void tick(size_t comp, double dT) { + for (auto& it : _typelist) { + if (it->tick) { + it->tick(comp, dT); + } } } - static void frame(size_t node, uint64_t f) { - for (auto& it : _type_list()) { - it->tick(node, f); + 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 _type_list(); + return _typelist; } // MEMBERS ============================================================================================================= public: - const type_info* type; const size_t node; template diff --git a/include/fennec/scene/components/transform2d.h b/include/fennec/scene/node2d.h similarity index 78% rename from include/fennec/scene/components/transform2d.h rename to include/fennec/scene/node2d.h index f91a152..7a98ce1 100644 --- a/include/fennec/scene/components/transform2d.h +++ b/include/fennec/scene/node2d.h @@ -33,12 +33,12 @@ #include #include -#include +#include namespace fennec { -struct transform2d : component { +struct node2d : scene_node { // Definitions ========================================================================================================= enum mobility_ : bool { @@ -52,34 +52,19 @@ public: /// /// \brief Default Constructor, initializes an identity matrix. - transform2d(size_t node) - : component(this, node) + node2d(size_t id, size_t scene, const string& name) + : scene_node(name, id, scene) + , _mobility(mobility_free) , _position(0, 0) , _scale(1, 1) , _shear(0, 0) , _rotation(0) { } - /// - /// \brief Component Constructor, composes the internal matrix using the components. - /// \param pos Position as vec2 - /// \param scl Scale as vec2 - /// \param rot Rotation as degrees - /// \param skw Skew as vec2 - transform2d(size_t node, const vec2& pos, const vec2& scl, float rot, const vec2& skw = vec2(0, 0)) - : component(this, node) - , _position(pos) - , _scale(scl) - , _shear(skw) - , _rotation(rot) - , _dirty(true) - , _local(matrix()) { - } + node2d(const node2d&) = default; + node2d(node2d&&) noexcept = default; - transform2d(const transform2d&) = default; - transform2d(transform2d&&) noexcept = default; - - ~transform2d() = default; + ~node2d() = default; // Access ============================================================================================================== @@ -99,53 +84,79 @@ public: return _shear; } + constexpr bool mobility() const { + return _mobility; + } + // Modifiers =========================================================================================================== constexpr void translate(const vec2& x) { - _position += x; + if (_mobility) { + _position += x; + } } constexpr void scale(const vec2& s) { - _scale *= s; + if (_mobility) { + _scale *= s; + } } constexpr void rotate(float r) { - _rotation += r; + if (_mobility) { + _rotation += r; + } } constexpr void shear(const vec2& s) { - _shear += s; + if (_mobility) { + _shear += s; + } } constexpr void commit() { - if (not _dirty) { + if (not _mobility) { return; } - _local = fennec::rotation(_rotation); - _local *= fennec::shear(_shear); - _local *= fennec::scaling(_scale); - _local *= fennec::translation(_position); + // Get parent + + + _recalculate(); + + + // Propagate down + } constexpr const mat3& local() { - commit(); return _local; } constexpr const mat3& global() { - + return _global; } // Fields ============================================================================================================== private: + bool _mobility; vec2 _position; vec2 _scale; vec2 _shear; float _rotation; - bool _dirty; - mat3 _local; + mat3 _local, _global; + + +// Helpers ============================================================================================================= + + constexpr void _recalculate(const mat3& parent) { + _local = fennec::rotation(_rotation); + _local *= fennec::shear(_shear); + _local *= fennec::scaling(_scale); + _local *= fennec::translation(_position); + _global = parent * _local; + } }; } diff --git a/include/fennec/scene/scene.h b/include/fennec/scene/scene.h index ae158fb..4fadfa9 100644 --- a/include/fennec/scene/scene.h +++ b/include/fennec/scene/scene.h @@ -20,7 +20,10 @@ #define FENNEC_CORE_SCENE_H #include +#include #include +#include + namespace fennec { @@ -30,9 +33,10 @@ namespace fennec /// \details This structure only contains the names of nodes and defers storage for components to /// their respective systems. Simple components may be isolated, \see components.h for more info. /// The hierarchy should be displayed using pre-order traversal. -class scene : public rdtree { -public: +class scene : public rdtree { +// Access ============================================================================================================== +public: /// /// \brief Find a node by name. /// \details If multiple nodes have the same name, finds the first one in pre-order traversal. @@ -42,13 +46,14 @@ public: list parse; parse.push_back(root); while (not parse.empty()) { - if (*_data[parse.front()].value == name) { - return parse.front(); + size_t n = parse.front(); + if (_table[n].value->name == name) { + return n; } // Pre-Order traversal - parse.push_front(next(parse.front())); - parse.push_front(child(parse.front())); + parse.push_front(next(n)); + parse.push_front(child(n)); parse.pop_front(); } return npos; @@ -63,13 +68,14 @@ public: list parse; parse.push_back(root); while (not parse.empty()) { - if (*_data[parse.front()].value == name) { - return parse.front(); + size_t n = parse.front(); + if (_table[n].value->name == name) { + return n; } // Pre-Order traversal - parse.push_front(next(parse.front())); - parse.push_front(child(parse.front())); + parse.push_front(next(n)); + parse.push_front(child(n)); parse.pop_front(); } return npos; diff --git a/include/fennec/scene/scene_node.h b/include/fennec/scene/scene_node.h new file mode 100644 index 0000000..a33c12f --- /dev/null +++ b/include/fennec/scene/scene_node.h @@ -0,0 +1,56 @@ +// ===================================================================================================================== +// 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 node.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_SCENE_NODE_H +#define FENNEC_SCENE_NODE_H + +#include +#include +#include + +namespace fennec +{ + +struct scene_node : typed { + const size_t scene; + const size_t id; + string name; + + scene_node(size_t id, size_t scene, const string& name) + : id(id), scene(scene), name(name) { + } + +private: + dynarray _components; +}; + +} + +#endif // FENNEC_SCENE_NODE_H \ No newline at end of file