vkmEngine 1.0.0
A C++ game engine · vkmengine.com
Loading...
Searching...
No Matches
hierarchy_operations.h
1#pragma once
2
3#include <cstddef>
4#include <vector>
5
6#include <glm/glm.hpp>
7
8#include "ecs/scene.h"
9#include "ecs/component/core/hierarchy.h"
10#include "ecs/component/core/transform.h"
11
12namespace Vkm::Engine::HierarchyOperations {
13
26void setParent(Scene& scene, EntityId child, EntityId parent, EntityId before = {});
27
36void removeFromParent(Scene& scene, EntityId entity);
37
47glm::mat4 computeWorldMatrix(const Scene& scene, EntityId entity);
48
55constexpr uint32_t MAX_DEPTH = 32;
56
66void warnWalkBound(const char* walk, size_t bound);
67
68namespace detail {
69
78class SearchQueue {
79 public:
80 SearchQueue();
81 ~SearchQueue();
82
83 SearchQueue(const SearchQueue& other) = delete;
84 SearchQueue& operator=(const SearchQueue& other) = delete;
85
86 SearchQueue(SearchQueue && other) = delete;
87 SearchQueue& operator=(SearchQueue && other) = delete;
88
89 public:
90 std::vector<EntityId>& entries() { return m_queue; }
91
92 private:
93 std::vector<EntityId> m_queue;
94};
95
96} // namespace detail
97
109template<typename Fn>
110void forEachChild(const Scene& scene, EntityId parent, Fn&& fn) {
111 const Hierarchy* node = scene.tryGet<Hierarchy>(parent);
112 if (!node) return;
113
114 const size_t bound = scene.entityCount();
115 size_t step = 0;
116 for (EntityId child = node->firstChild; child; ++step) {
117 if (step >= bound) {
118 warnWalkBound("sibling-list walk", bound);
119 return;
120 }
121 // Read before fn, which may remove this child's Hierarchy.
122 const Hierarchy* held = scene.tryGet<Hierarchy>(child);
123 const EntityId next = held ? held->nextSibling : EntityId{};
124 fn(child);
125 child = next;
126 }
127}
128
140template<typename Fn>
141void forSelfAndAncestors(const Scene& scene, EntityId entity, Fn&& fn) {
142 EntityId at = entity;
143 for (uint32_t step = 0; at; ++step) {
144 if (step >= MAX_DEPTH) {
145 warnWalkBound("ancestor walk", MAX_DEPTH);
146 return;
147 }
148 if (!fn(at)) return;
149 const Hierarchy* node = scene.tryGet<Hierarchy>(at);
150 if (!node) return;
151 at = node->parent;
152 }
153}
154
166template<typename Match>
167EntityId findInSelfOrDescendantsIf(const Scene& scene, EntityId root, Match match) {
168 if (!root) return {};
169 if (match(root)) return root;
170
171 detail::SearchQueue queue;
172 std::vector<EntityId>& pending = queue.entries();
173 pending.push_back(root);
174 const size_t bound = scene.entityCount();
175 for (size_t i = 0; i < pending.size(); ++i) {
176 if (pending.size() > bound) {
177 warnWalkBound("breadth-first search", bound);
178 return {};
179 }
180 EntityId found{};
181 forEachChild(scene, pending[i], [&](EntityId child) {
182 if (!found && match(child)) found = child;
183 pending.push_back(child);
184 });
185 if (found) return found;
186 }
187 return {};
188}
189
200template<typename T>
201EntityId findInSelfOrDescendants(const Scene& scene, EntityId root) {
202 return findInSelfOrDescendantsIf(scene, root, [&](EntityId id) { return scene.has<T>(id); });
203}
204
213template<typename T>
214EntityId findInSelfOrAncestors(const Scene& scene, EntityId leaf) {
215 EntityId found{};
216 forSelfAndAncestors(scene, leaf, [&](EntityId at) {
217 if (scene.has<T>(at)) found = at;
218 return !found;
219 });
220 return found;
221}
222
234inline bool isAncestorOf(const Scene& scene, EntityId ancestor, EntityId node) {
235 if (!ancestor || !node || ancestor == node) return false;
236
237 const Hierarchy* at = scene.tryGet<Hierarchy>(node);
238 for (size_t step = 0; at && at->parent && step < scene.entityCount(); ++step) {
239 if (at->parent == ancestor) return true;
240 at = scene.tryGet<Hierarchy>(at->parent);
241 }
242 return false;
243}
244
253void destroyHierarchy(Scene& scene, EntityId entity);
254
267std::vector<EntityId> collectSubtree(const Scene& scene, EntityId root);
268
269} // namespace Vkm::Engine::HierarchyOperations