Last active
September 30, 2024 07:53
-
-
Save ArnCarveris/f88abc33993fb0d2c5790c6ac018cf7b to your computer and use it in GitHub Desktop.
EnTT Hierarchy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <entt/entity/registry.hpp> | |
namespace entt | |
{ | |
/*! @brief TODO */ | |
template<typename Entity> | |
class Hierarchy final { | |
using traits_type = entt_traits<Entity>; | |
using component_type = typename Registry<Entity>::component_type; | |
using view_type = SparseSet<Entity>; | |
using view_iterator_type = typename view_type::iterator_type; | |
using fn_type = void(*)(Registry<Entity>&, Entity); | |
struct Handler final { | |
fn_type bind; | |
fn_type unbind; | |
fn_type destroy; | |
}; | |
using handler_make_fn_type = Handler(*)(); | |
template<typename Component> | |
struct Child final { | |
Entity parent; | |
}; | |
template<typename... Component> | |
struct Children final { | |
Entity eldest; | |
Entity youngest; | |
}; | |
using Binding = Children<>; | |
static void child_capture(Registry<Entity>& registry, Entity child) { | |
if (registry.attachee<Binding>() == child) { | |
return; | |
} | |
auto& binding = registry.get<Binding>(); | |
binding.eldest = std::min(binding.eldest, child); | |
binding.youngest = std::max(binding.youngest, child); | |
} | |
template<typename Component> | |
static void child_capture_first(Registry<Entity>& registry, Entity child) { | |
auto parent = registry.attachee<Binding>(); | |
if (parent == child) { | |
return; | |
} | |
child_capture(registry, child); | |
registry.assign<Children<Component>>(parent, child, child); | |
registry.construction<Component>().disconnect<&Hierarchy<Entity>::child_capture_first<Component>>(); | |
registry.construction<Component>().connect<&Hierarchy<Entity>::child_capture_rest<Component>>(); | |
} | |
template<typename Component> | |
static void child_capture_rest(Registry<Entity>& registry, Entity child) { | |
auto parent = registry.attachee<Binding>(); | |
if (parent == child) { | |
return; | |
} | |
child_capture(registry, child); | |
registry.get<Children<Component>>(parent).youngest = child; | |
} | |
template<typename Component> | |
static void child_bound_first(Registry<Entity>& registry, Entity child) { | |
auto parent = registry.attachee<Binding>(); | |
if (parent == child) { | |
return; | |
} | |
child_capture(registry, child); | |
registry.assign<Child<Component>>(child, parent); | |
registry.assign<Children<Component>>(parent, child, child); | |
registry.construction<Component>().disconnect<&Hierarchy<Entity>::child_bound_first<Component>>(); | |
registry.construction<Component>().connect<&Hierarchy<Entity>::child_bound_rest<Component>>(); | |
} | |
template<typename Component> | |
static void child_bound_rest(Registry<Entity>& registry, Entity child) { | |
auto parent = registry.attachee<Binding>(); | |
if (parent == child) { | |
return; | |
} | |
child_capture(registry, child); | |
registry.assign<Child<Component>>(child, parent); | |
registry.get<Children<Component>>(parent).youngest = child; | |
} | |
template<typename Component> | |
static void child_bound_destroy(Registry<Entity>& registry, Entity child) { | |
if (!registry.has<Child<Component>>(child)) { | |
return; | |
} | |
auto& component = registry.get<Child<Component>>(child); | |
if (component.parent == child) { | |
return; | |
} | |
registry.remove<Child<Component>>(child); | |
auto& children = registry.get<Children<Component>>(component.parent); | |
if (children.eldest == child) { | |
++children.eldest; | |
} | |
else if (children.youngest == child) { | |
--children.youngest; | |
} | |
if (children.eldest == children.youngest) { | |
registry.remove<Children<Component>>(component.parent); | |
} | |
} | |
template<typename Component> | |
static Handler make_capture_handler() { | |
return Handler{ | |
[](Registry<Entity>& registry, Entity child) { | |
registry.construction<Component>().connect<&Hierarchy<Entity>::child_capture_first<Component>>(); | |
}, | |
[](Registry<Entity>& registry, Entity child) { | |
registry.construction<Component>().disconnect<&Hierarchy<Entity>::child_capture_first<Component>>(); | |
registry.construction<Component>().disconnect<&Hierarchy<Entity>::child_capture_rest<Component>>(); | |
}, | |
[](Registry<Entity>& registry, Entity child) {} | |
}; | |
} | |
template<typename Component> | |
static Handler make_bound_handler() { | |
return Handler{ | |
[](Registry<Entity>& registry, Entity child) { | |
registry.construction<Component>().connect<&Hierarchy<Entity>::child_bound_first<Component>>(); | |
registry.destruction<Component>().connect<&Hierarchy<Entity>::child_bound_destroy<Component>>(); | |
}, | |
[](Registry<Entity>& registry, Entity child) { | |
registry.construction<Component>().disconnect<&Hierarchy<Entity>::child_bound_first<Component>>(); | |
registry.construction<Component>().disconnect<&Hierarchy<Entity>::child_bound_rest<Component>>(); | |
}, | |
[](Registry<Entity>& registry, Entity child) { | |
registry.destruction<Component>().disconnect<&Hierarchy<Entity>::child_bound_destroy<Component>>(); | |
} | |
}; | |
} | |
template<typename Component> | |
Handler& handler(handler_make_fn_type fn = nullptr) { | |
auto cid = registry.type<Component>(); | |
auto it = handlers.find(cid); | |
if (it == handlers.end()) | |
{ | |
it = handlers.emplace(cid, fn()).first; | |
} | |
return it->second; | |
} | |
void handler_bind(Registry<Entity>& registry, Entity parent) { | |
using limits = std::numeric_limits<Entity>; | |
registry.replace<Binding>(tag_t{}, limits::max(), limits::min()); | |
for (auto& handler : handlers) { | |
handler.second.bind(registry, parent); | |
} | |
} | |
void handler_unbind(Registry<Entity>& registry, Entity parent) { | |
auto& binding = registry.get<Binding>(); | |
registry.accommodate<Binding>(parent, binding); | |
for (auto& handler : handlers) { | |
handler.second.unbind(registry, parent); | |
} | |
} | |
void handler_destroy(Registry<Entity>& registry, Entity entity) { | |
for (auto& handler : handlers) { | |
handler.second.destroy(registry, entity); | |
} | |
} | |
public: | |
/*! @brief TODO */ | |
template<typename Component> | |
using child_type = Child<Component>; | |
/*! @brief TODO */ | |
template<typename Component> | |
using children_type = Children<Component>; | |
/*! @brief TODO */ | |
Hierarchy() = delete; | |
/*! @brief TODO */ | |
Hierarchy(const Hierarchy&) = delete; | |
/*! @brief TODO */ | |
Hierarchy(Registry<Entity>& registry) : registry{ registry } { | |
registry.construction<Binding>(tag_t{}).connect<Hierarchy<Entity>, &Hierarchy<Entity>::handler_bind>(this); | |
registry.destruction<Binding>(tag_t{}).connect<Hierarchy<Entity>, &Hierarchy<Entity>::handler_unbind>(this); | |
} | |
/*! @brief TODO */ | |
~Hierarchy() | |
{ | |
registry.construction<Binding>(tag_t{}).disconnect<Hierarchy<Entity>, &Hierarchy<Entity>::handler_bind>(this); | |
registry.destruction<Binding>(tag_t{}).disconnect<Hierarchy<Entity>, &Hierarchy<Entity>::handler_unbind>(this); | |
handler_destroy(registry, Entity{}); | |
} | |
/*! @brief TODO */ | |
template<typename... Component> | |
void capture() { | |
using accumulator_type = int[]; | |
accumulator_type accumulator = { (handler<Component>(&make_capture_handler<Component>), 0)... }; | |
(void)accumulator; | |
} | |
/*! @brief TODO */ | |
template<typename... Component> | |
void bound() { | |
using accumulator_type = int[]; | |
accumulator_type accumulator = { (handler<Component>(&make_bound_handler<Component>), 0)... }; | |
(void)accumulator; | |
} | |
/*! @brief TODO */ | |
void bind(Entity parent) { | |
registry.assign<Binding>(tag_t{}, parent); | |
} | |
/*! @brief TODO */ | |
void unbind() { | |
registry.remove<Binding>(); | |
} | |
/*! @brief TODO */ | |
template<typename Component> | |
View<Entity, Child<Component>, Component> child_view() { | |
return registry.view<Child<Component>, Component>(); | |
} | |
/*! @brief TODO */ | |
template<typename Component> | |
View<Entity, Children<Component>, Component> children_view() { | |
return registry.view<Children<Component>, Component>(); | |
} | |
/*! @brief TODO */ | |
template<typename Component> | |
void sort() { | |
registry.sort<Child<Component>>( | |
[](const auto& lhs, const auto& rhs) { | |
return lhs.parent < rhs.parent; | |
} | |
); | |
registry.sort<Component, Child<Component>>(); | |
} | |
/*! @brief TODO */ | |
template<typename Func> | |
void each(Children<>& children, Func&& func) { | |
for (auto pos = children.youngest + 1; pos > children.eldest; --pos) { | |
auto entity = pos - 1; | |
assert(registry.fast(entity)); | |
func(entity); | |
} | |
} | |
/*! @brief TODO */ | |
template<typename Component, typename Func> | |
void each(Children<Component>& children, Func&& func) { | |
auto view = registry.view<Component>(); | |
if (children.eldest == children.youngest) { | |
func(children.eldest, view.get(children.youngest)); | |
return; | |
} | |
auto begin = view.find(children.youngest); | |
auto end = view.find(children.eldest); | |
if (begin == end) { | |
return; | |
} | |
for (++end; begin != end; ++begin) | |
{ | |
auto entity = *begin; | |
func(entity, view.get(entity)); | |
} | |
} | |
/*! @brief TODO */ | |
template<typename... Component, typename Func> | |
inline void each(Entity parent, Func&& func) { | |
each(registry.get<Children<Component...>>(parent), std::move(func)); | |
} | |
/*! @brief TODO */ | |
template<typename... Component, typename View, typename Func> | |
inline void each(View& view, Entity parent, Func&& func) { | |
each(view.template get<Children<Component...>>(parent), std::move(func)); | |
} | |
private: | |
Registry<Entity>& registry; | |
std::unordered_map<component_type, Handler> handlers; | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "hierarchy.hpp" | |
namespace entt | |
{ | |
template<typename Entity> | |
void hierarchy_test(Registry<Entity>& registry, Hierarchy<Entity>& hierarchy, int parents, int childs) { | |
{ | |
struct a_t {}; | |
struct b_t {}; | |
auto e0 = registry.create(); | |
auto e1 = registry.create(); | |
auto e2 = registry.create(); | |
auto e3 = registry.create(); | |
auto e4 = registry.create(); | |
registry.assign<a_t>(e0); | |
registry.assign<a_t>(e1); | |
registry.assign<a_t>(e2); | |
registry.assign<a_t>(e4); | |
registry.assign<b_t>(e0); | |
registry.assign<b_t>(e2); | |
registry.assign<b_t>(e3); | |
registry.assign<b_t>(e4); | |
auto view = registry.view<a_t, b_t>(); | |
auto begin = view.find(e4); | |
auto end = view.find(e0); | |
for (++end; begin != end; ++begin) | |
{ | |
auto e = *begin; | |
assert(e == e0 || e == e2 || e == e4); | |
} | |
} { | |
hierarchy.capture<int, double>(); | |
hierarchy.bound<float>(); | |
for (int i = 0; i < parents; ++i) { | |
auto parent = registry.create(); | |
registry.assign<int>(parent) = int(i * 10); | |
hierarchy.bind(parent); | |
for (int j = 0; j < childs; ++j) { | |
auto child = registry.create(); | |
registry.assign<int>(child) = int(i * 100 + j); | |
if (j % 2 == 0) { | |
registry.assign<float>(child) = float(j); | |
} | |
else { | |
registry.assign<double>(child) = double(j); | |
} | |
} | |
hierarchy.unbind(); | |
{ | |
int cnt = 0; | |
hierarchy.each(parent, [&cnt](auto entity) { | |
++cnt; | |
}); | |
assert(cnt == childs); | |
} | |
} { | |
auto view = hierarchy.children_view<int>(); | |
for (auto parent : view) | |
{ | |
auto& parent_comp = view.get<int>(parent); | |
hierarchy.each<int>(view, parent, [](auto child, auto& comp) { | |
auto boolean = false; | |
boolean = true; | |
}); | |
} | |
} | |
hierarchy.sort<float>(); | |
{ | |
auto view = hierarchy.child_view<float>(); | |
for (auto entity : view) | |
{ | |
auto& num = view.get<float>(entity); | |
auto& child = view.get<Hierarchy<Entity>::child_type<float>>(entity); | |
auto boolean = false; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can you explain how this works?