From c0b58c58c25b2dc6ab0d5313fb6adb9ff8e79e32 Mon Sep 17 00:00:00 2001 From: sara01 Date: Wed, 5 Mar 2025 21:16:25 -0300 Subject: [PATCH] backup (arch): added primitive systems --- examples/main.cpp | 43 ++++++++++++++++--- tests/unit/test_slotmap.hpp | 4 +- vecs/include/vecs/archetype.hpp | 28 ++++++------ vecs/include/vecs/component_storage.hpp | 43 +++++++++++-------- vecs/include/vecs/data_structures/slotmap.hpp | 10 +++++ vecs/include/vecs/debug.hpp | 25 ++++++----- vecs/include/vecs/entity_storage.hpp | 6 +-- vecs/include/vecs/query.hpp | 21 +++++++++ vecs/include/vecs/types.hpp | 15 ++++--- vecs/include/vecs/world.hpp | 36 +++++++++++++--- 10 files changed, 164 insertions(+), 67 deletions(-) create mode 100644 vecs/include/vecs/query.hpp diff --git a/examples/main.cpp b/examples/main.cpp index 3f67881..43513cf 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -3,33 +3,62 @@ struct Position { float x, y, z; + friend std::ostream& operator<<(std::ostream& os, Position const& pos) { + return os << "Position { x: " << pos.x << ", y: " << pos.y << ", z: " << pos.z << " }"; + } }; struct Velocity { float dx, dy, dz; + friend std::ostream& operator<<(std::ostream& os, Velocity const& vel) { + return os << "Velocity { dx: " << vel.dx << ", dy: " << vel.dy << ", dz: " << vel.dz << " }"; + } }; struct Health { int value; constexpr void reset() noexcept { value = 100; }; + friend std::ostream& operator<<(std::ostream& os, Health const& health) { + return os << "Health { value: " << health.value << " }"; + } }; + int main() { using log_t = vecs::debug_t; + vecs::world_t world{}; log_t::log("-------------------------------------------------"); - auto position { Position { 1.0f, 1.0f, 0.0f } }; - auto velocity { Velocity { 0.0f, 2.0f, 0.0f } }; - auto health { Health { 50 } }; + auto position1 { Position { 1.0f, 1.0f, 0.0f } }; + auto velocity1 { Velocity { 0.0f, 2.0f, 0.0f } }; + auto health1 { Health { 50 } }; + + auto position2 { Position { 2.0f, 2.0f, 0.0f } }; + auto velocity2 { Velocity { 1.0f, 1.0f, 0.0f } }; + auto health2 { Health { 75 } }; - world.spawn_entity(position, velocity, health); - auto const entity_id = world.spawn_entity(velocity, health); + auto position3 { Position { 3.0f, 3.0f, 0.0f } }; + auto velocity3 { Velocity { 0.5f, 0.5f, 0.0f } }; + auto health3 { Health { 100 } }; + + world.spawn_entity(position1, velocity1, health1); + world.spawn_entity(position2, velocity2, health2); + auto const entity_id = world.spawn_entity(velocity1, health1); world.despawn_entity(entity_id); + world.spawn_entity(velocity3); - world.spawn_entity(velocity); - world.spawn_entity(health); + log_t::log("-------------------------------------------------"); + world.for_each( + [](Position const& pos, Velocity const& vel, Health const& health + ) { + log_t::log("System matched the following components in entity: "); + log_t::log(" ╰> ", log_t::YELLOW, pos); + log_t::log(" ╰> ", log_t::YELLOW, vel); + log_t::log(" ╰> ", log_t::YELLOW, health); + log_t::log(""); + }); return 0; } diff --git a/tests/unit/test_slotmap.hpp b/tests/unit/test_slotmap.hpp index 5256103..2983351 100644 --- a/tests/unit/test_slotmap.hpp +++ b/tests/unit/test_slotmap.hpp @@ -63,7 +63,7 @@ TEST_CASE("Insertion and deletion benchmarks.", "[!benchmark]") { } }; - BENCHMARK("UnorderedMap Insertior and deletion.") { + BENCHMARK("UnorderedMap Insertion and deletion.") { std::unordered_map unordered_map; for (int i = 0; i < num_elements; ++i) { unordered_map[i] = i; @@ -100,4 +100,4 @@ TEST_CASE("Insertion and deletion benchmarks.", "[!benchmark]") { return sum; }; -} \ No newline at end of file +} diff --git a/vecs/include/vecs/archetype.hpp b/vecs/include/vecs/archetype.hpp index fef73ff..218f9a7 100644 --- a/vecs/include/vecs/archetype.hpp +++ b/vecs/include/vecs/archetype.hpp @@ -3,6 +3,7 @@ // std #include #include +#include // lib #include "types.hpp" @@ -28,44 +29,43 @@ struct archetype_t final { return std::get<0>(_component_table).size(); } - std::vector - add_entity(vecs::entity_id_t const entity_id, Cs&&... components) { + std::vector + add_entity(vecs::entity_id_t const entity_id, Cs&&... components) noexcept { // Add a column. std::vector cmp_keys{}; + cmp_keys.reserve(_component_count); // C++17 fold expression for iterating each element in pack `components`. ([&](auto&& component) { using component_type_t = std::remove_reference_t; - auto& component_row = - std::get>(_component_table); + + auto& component_row = std::get< + component_row_t>(_component_table); vecs::component_key_t const key = component_row.push_back(component); cmp_keys.push_back(key); } (std::forward(components)), ...); - + return cmp_keys; } template vecs::component_key_t const - store_component(C&& component) { + store_component(C&& component) noexcept { auto& component_row = std::get>(_component_table); auto const key = component_row.push_back(component); return key; } - template void - for_each(F&& function) { - assert(!std::get<0>(_component_table).empty() - && "Component table is empty."); - + for_each(auto&& system) noexcept { vecs::usize const entity_count { std::get<0>(_component_table).size() }; for (vecs::usize entity_id{}; entity_id < entity_count; ++entity_id) { - std::apply([&function, entity_id](auto&... component_row) { - std::forward(function)(component_row[entity_id]...); + std::apply([&system, entity_id](auto&... component_row) { + vecs::debug_t::log(vecs::debug_t::LIGHT_BLUE, "Found Entity ID: ", entity_id); + std::forward(system)(component_row[entity_id]...); }, _component_table); } } @@ -108,7 +108,7 @@ struct archetype_t final { A column in the table represents an entity an it's components! */ template - using component_row_t = vecs::slotmap_t; + using component_row_t = vecs::slotmap_t; using component_table_t = std::tuple...>; component_table_t _component_table{}; }; diff --git a/vecs/include/vecs/component_storage.hpp b/vecs/include/vecs/component_storage.hpp index 9aef16e..df2a0e5 100644 --- a/vecs/include/vecs/component_storage.hpp +++ b/vecs/include/vecs/component_storage.hpp @@ -48,7 +48,7 @@ struct component_storage_t final { vecs::entity_id_t const entity_id, Cs&&... components ) noexcept { - mask_t mask = (_component_masks[typeid(Cs).hash_code()] | ...); + vecs::mask_t mask = (_component_masks[typeid(Cs).hash_code()] | ...); _entity_masks[entity_id] = mask; if (!_is_archetype_registered(mask)) { @@ -61,11 +61,18 @@ struct component_storage_t final { void remove_components(vecs::entity_id_t const entity_id) noexcept { - mask_t const mask = _entity_masks[entity_id]; - _entity_masks[entity_id] = mask_t{}; + vecs::mask_t const mask = _entity_masks[entity_id]; + _entity_masks[entity_id] = vecs::DEAD_ENTITY_ID; } - [[nodiscard]] mask_t + template + void + for_each(auto&& system) { + vecs::archetype_t& archetype = _get_archetype(); + archetype.for_each(std::forward(system)); + } + + [[nodiscard]] vecs::mask_t get_entity_mask(vecs::entity_id_t const entity_id) const noexcept { return _entity_masks[entity_id]; } @@ -78,7 +85,7 @@ struct component_storage_t final { <= std::numeric_limits::max() && "Component registration limit reached."); - component_id_t const component_id = component_info->hash_code(); + vecs::component_id_t const component_id = component_info->hash_code(); assert(!_component_masks.contains(component_id) && "Component already registered."); @@ -90,9 +97,9 @@ struct component_storage_t final { // This repeats for Velocity and Health. // Resulting in archetype.id = 0b111. // TODO - Remove this log. - component_name_t const component_name = component_info->name(); + vecs::component_name_t const component_name = component_info->name(); log_t::log(log_t::YELLOW, "Registered component: ", log_t::CLEAR, - component_name, " with mask: ", log_t::LIGHT_MAGENTA, _component_masks[component_id].to_string()); + component_name, " with mask: ", log_t::LIGHT_MAGENTA, "0b", _component_masks[component_id].to_string()); } } @@ -109,18 +116,20 @@ struct component_storage_t final { _archetypes[archetype.mask()] = std::any { std::move(archetype) }; } - template - [[nodiscard]] vecs::entity_id_t const - _add_entity(vecs::archetype_t const& archetype) noexcept { - + auto& + _get_entity_components(vecs::entity_id_t const entity_id) noexcept { + auto entity_mask = _entity_masks[entity_id]; + assert(_is_archetype_registered(entity_mask) + && "Archetype not registered"); + auto& archetype = _get_archetype(entity_mask); } template [[nodiscard]] vecs::mask_t _get_component_mask() noexcept { - component_id_t component_id = typeid(C).hash_code(); + vecs::component_id_t component_id = typeid(C).hash_code(); assert(_component_masks.contains(component_id) && "Component not registered."); @@ -141,9 +150,9 @@ struct component_storage_t final { } template - [[nodiscard]] archetype_t& + [[nodiscard]] vecs::archetype_t& _get_archetype() noexcept { - vecs::mask_t archetype_mask = _get_archetype_mask(); + vecs::mask_t const archetype_mask = _get_archetype_mask(); assert(_is_archetype_registered(archetype_mask) && "Archetype not registered."); @@ -164,13 +173,13 @@ struct component_storage_t final { std::array< std::type_info const*, sizeof...(RegisteredComponents)> const _registered_components_type_info{}; + + vecs::u64 _next_component_mask { 0b1 }; // Component Storage Data Layout. - std::unordered_map _component_masks{}; + std::unordered_map _component_masks{}; std::array _entity_masks{}; std::unordered_map _archetypes{}; - - vecs::u64 _next_component_mask { 0b1 }; }; } // namespace vecs diff --git a/vecs/include/vecs/data_structures/slotmap.hpp b/vecs/include/vecs/data_structures/slotmap.hpp index ae46cf5..422f1f4 100644 --- a/vecs/include/vecs/data_structures/slotmap.hpp +++ b/vecs/include/vecs/data_structures/slotmap.hpp @@ -128,6 +128,16 @@ class slotmap_t final { _init_freelist(); } + [[nodiscard]] constexpr T& + operator[](index_t index) { + return _data[index]; + } + + [[nodiscard]] constexpr T const& + operator[](index_t index) const { + return _data[index]; + } + private: constexpr void _init_freelist() noexcept { diff --git a/vecs/include/vecs/debug.hpp b/vecs/include/vecs/debug.hpp index f0708b6..98e2dbe 100644 --- a/vecs/include/vecs/debug.hpp +++ b/vecs/include/vecs/debug.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace vecs { @@ -34,7 +35,7 @@ class debug_t { ~debug_t() = delete; debug_t(const debug_t&) = delete; - static constexpr auto CLEAR { "\033[0m" }; + static constexpr auto CLEAR { "\033[0m" }; static constexpr auto BLACK { "\033[30m" }; static constexpr auto RED { "\033[31m" }; static constexpr auto GREEN { "\033[32m" }; @@ -43,7 +44,7 @@ class debug_t { static constexpr auto MAGENTA { "\033[35m" }; static constexpr auto CYAN { "\033[36m" }; static constexpr auto WHITE { "\033[37m" }; - static constexpr auto GRAY { "\033[90m" }; + static constexpr auto GRAY { "\033[90m" }; static constexpr auto LIGHT_BLACK { "\033[90m" }; static constexpr auto LIGHT_RED { "\033[91m" }; @@ -53,20 +54,20 @@ class debug_t { static constexpr auto LIGHT_MAGENTA { "\033[95m" }; static constexpr auto LIGHT_CYAN { "\033[96m" }; static constexpr auto LIGHT_WHITE { "\033[97m" }; - static constexpr auto LIGHT_GRAY { "\033[97m" }; + static constexpr auto LIGHT_GRAY { "\033[97m" }; static constexpr auto BOLD { "\033[1m" }; static constexpr auto UNDERLINE { "\033[4m" }; static constexpr auto INVERT { "\033[7m" }; - static constexpr auto BG_BLACK { "\033[40m" }; - static constexpr auto BG_RED { "\033[41m" }; - static constexpr auto BG_GREEN { "\033[42m" }; - static constexpr auto BG_YELLOW { "\033[43m" }; - static constexpr auto BG_BLUE { "\033[44m" }; - static constexpr auto BG_MAGENTA { "\033[45m" }; - static constexpr auto BG_CYAN { "\033[46m" }; - static constexpr auto BG_WHITE { "\033[47m" }; + static constexpr auto BG_BLACK { "\033[40m" }; + static constexpr auto BG_RED { "\033[41m" }; + static constexpr auto BG_GREEN { "\033[42m" }; + static constexpr auto BG_YELLOW { "\033[43m" }; + static constexpr auto BG_BLUE { "\033[44m" }; + static constexpr auto BG_MAGENTA { "\033[45m" }; + static constexpr auto BG_CYAN { "\033[46m" }; + static constexpr auto BG_WHITE { "\033[47m" }; static constexpr auto BG_GRAY { "\033[100m" }; static constexpr auto LIGHT_BG_BLACK { "\033[100m" }; @@ -121,6 +122,8 @@ class debug_t { log_message("[ERROR]: ", RED, std::forward(args)...); } + + private: template static void diff --git a/vecs/include/vecs/entity_storage.hpp b/vecs/include/vecs/entity_storage.hpp index fc6933b..5b34668 100644 --- a/vecs/include/vecs/entity_storage.hpp +++ b/vecs/include/vecs/entity_storage.hpp @@ -16,8 +16,6 @@ template struct entity_storage_t { using log_t = vecs::debug_t; - static constexpr vecs::usize DEAD_ENTITY_ID { 0b0 }; // 0b0 = No components = dead/invalid entity. - [[nodiscard]] constexpr vecs::entity_id_t const add_entity() noexcept { @@ -40,13 +38,13 @@ struct entity_storage_t { void add_component_keys( vecs::entity_id_t const entity_id, - std::vector cmp_keys + std::vector cmp_keys ) { _entity_keys[entity_id] = cmp_keys; } private: - std::array, Capacity> _entity_keys{}; + std::array, Capacity> _entity_keys{}; vecs::reusable_id_t _entity_ids{}; }; diff --git a/vecs/include/vecs/query.hpp b/vecs/include/vecs/query.hpp new file mode 100644 index 0000000..ee9f088 --- /dev/null +++ b/vecs/include/vecs/query.hpp @@ -0,0 +1,21 @@ +#pragma once + +// std +#include +#include + +// lib +#include "types.hpp" + +namespace vecs { + +template +struct query_t final { + + + +std::tuple>> data{}; + +}; + +} // namespace vecs diff --git a/vecs/include/vecs/types.hpp b/vecs/include/vecs/types.hpp index 9063257..d9eaf05 100644 --- a/vecs/include/vecs/types.hpp +++ b/vecs/include/vecs/types.hpp @@ -48,8 +48,10 @@ using wc16 = wchar_t; // wide chars such as L'Ω'. Omega (U+03A9). // ECS Types. -static constexpr vecs::usize MAX_ALIVE_ENTITIES { 16 }; // Arbitrarily set. -static constexpr vecs::usize MAX_REGISTRABLE_COMPONENTS { 8 }; // Arbitrarily set. +static constexpr vecs::usize MAX_ALIVE_ENTITIES { 16 }; // Arbitrarily set. +static constexpr vecs::usize MAX_REGISTRABLE_COMPONENTS { 8 }; // Arbitrarily set. +static constexpr vecs::usize MAX_INSTANCES_PER_COMPONENT { 64 }; // Arbitrarily set. +static constexpr vecs::usize DEAD_ENTITY_ID { 0b0 }; // 0b0 = No components = dead/invalid entity. /* Vanila ECS definition (informal, by: Sander Mertens, 2020). src: https://ajmmertens.medium.com/why-vanilla-ecs-is-not-enough-d7ed4e3bebe5 @@ -75,15 +77,18 @@ concept Component = std::is_trivially_move_assignable_v && // Components must not have custom move constructors. std::is_trivially_destructible_v; // Components must not have custom destructors. -/* A component can be one and only one of the registered components. +template +concept System = std::is_invocable_v; + +/* A component can only be an instance of a component This implies that all components must be known at compile time. Note: A std::variant is similar to a C union, but it is type-safe. Its size is the maximum size of all the alternative types in the variant. (e.g.), sizeof(component_t) = max(sizeof(Position), sizeof(Velocity), sizeof(Health)). */ -template -using component_t = std::variant; +template +using component_t = std::variant; using mask_t = std::bitset; diff --git a/vecs/include/vecs/world.hpp b/vecs/include/vecs/world.hpp index 3e23846..fb01fb3 100644 --- a/vecs/include/vecs/world.hpp +++ b/vecs/include/vecs/world.hpp @@ -2,6 +2,7 @@ #include "component_storage.hpp" #include "entity_storage.hpp" +#include "query.hpp" namespace vecs { @@ -15,12 +16,17 @@ struct world_t final { spawn_entity(Cs... components) noexcept { vecs::entity_id_t const entity_id = _entity_storage.add_entity(); auto cmp_keys = _component_storage.add_components( - entity_id, std::move(components)...); + entity_id, std::move(components)...); _entity_storage.add_component_keys(entity_id, cmp_keys); - + log_t::log(log_t::LIGHT_GREEN, "Spawned ", log_t::CLEAR, "entity with ID: ", entity_id); - log_t::log(" ╰> Mask: ", log_t::LIGHT_MAGENTA, _component_storage.get_entity_mask(entity_id)); + log_t::log(" ╰> Mask: ", log_t::LIGHT_MAGENTA, "0b", _component_storage.get_entity_mask(entity_id)); + log_t::log(" ╰> Component keys: "); + for (component_key_t const key : cmp_keys) { + log_t::log(" ╰> ", log_t::LIGHT_MAGENTA, "0x", std::hex, key); + } + return entity_id; } @@ -33,15 +39,31 @@ struct world_t final { log_t::log(" ╰> Mask cleared: ", log_t::LIGHT_MAGENTA, _component_storage.get_entity_mask(entity_id)); } + template + void for_each(auto&& system) { + _component_storage.template for_each( + std::forward(system)); + } + + template + [[nodiscard]] query_t + query() { + query_t query{}; + + assert(false && "Not implemented yet."); + + return query; + } + private: vecs::component_storage_t< - MAX_REGISTRABLE_COMPONENTS, - MAX_ALIVE_ENTITIES, + vecs::MAX_REGISTRABLE_COMPONENTS, + vecs::MAX_ALIVE_ENTITIES, RegisteredComponents...> _component_storage{}; vecs::entity_storage_t< - MAX_ALIVE_ENTITIES, - MAX_REGISTRABLE_COMPONENTS> _entity_storage{}; + vecs::MAX_ALIVE_ENTITIES, + vecs::MAX_REGISTRABLE_COMPONENTS> _entity_storage{}; }; } // namespace vecs \ No newline at end of file