38 typedef typename allocator_type::pointer
pointer;
40 typedef typename allocator_type::reference
reference;
50 typedef std::array<std::array<std::array<aabb, 2>, 2>, 2> octants;
51 typedef std::array<std::array<std::array<aabb_2d, 2>, 2>, 2> octants_2d;
52 typedef std::array<std::array<std::array<node*, 2>, 2>, 2> children;
54 struct no_parent : std::logic_error { no_parent() : std::logic_error{
"neogfx::aabb_octree::node::no_parent" } {} };
55 struct no_children : std::logic_error { no_children() : std::logic_error{
"neogfx::aabb_octree::node::no_children" } {} };
57 node(
aabb_octree& aTree,
const aabb& aAabb) : iTree{ aTree }, iParent{
nullptr }, iDepth{ 1 }, iAabb{ aAabb }, iChildren {}
59 iTree.iDepth = std::max(iTree.iDepth, iDepth);
62 node(
const node& aParent,
const aabb& aAabb) : iTree{ aParent.iTree }, iParent{ &aParent }, iDepth{ aParent.iDepth + 1 }, iAabb { aAabb }, iChildren{}
64 iTree.iDepth = std::max(iTree.iDepth, iDepth);
70 if (has_child<0, 0, 0>())
71 remove_child<0, 0, 0>();
72 if (has_child<0, 1, 0>())
73 remove_child<0, 1, 0>();
74 if (has_child<1, 0, 0>())
75 remove_child<1, 0, 0>();
76 if (has_child<1, 1, 0>())
77 remove_child<1, 1, 0>();
78 if (has_child<0, 0, 1>())
79 remove_child<0, 0, 1>();
80 if (has_child<0, 1, 1>())
81 remove_child<0, 1, 1>();
82 if (has_child<1, 0, 1>())
83 remove_child<1, 0, 1>();
84 if (has_child<1, 1, 1>())
85 remove_child<1, 1, 1>();
87 parent().unsplit(
this);
90 bool has_parent()
const
92 return iParent !=
nullptr;
94 const node& parent()
const
102 return const_cast<node&
>(to_const(*this).parent());
104 uint32_t depth()
const
114 iTree.iDepth = std::max(iTree.iDepth, iDepth);
118 child<0, 0, 0>().add_entity(aEntity, aCollider);
120 child<0, 1, 0>().add_entity(aEntity, aCollider);
122 child<1, 0, 0>().add_entity(aEntity, aCollider);
124 child<1, 1, 0>().add_entity(aEntity, aCollider);
126 child<0, 0, 1>().add_entity(aEntity, aCollider);
128 child<0, 1, 1>().add_entity(aEntity, aCollider);
130 child<1, 0, 1>().add_entity(aEntity, aCollider);
132 child<1, 1, 1>().add_entity(aEntity, aCollider);
136 iEntities.push_back(aEntity);
137 if (iEntities.size() > BucketSize && (iAabb.max - iAabb.min).min() > iTree.minimum_octant_size())
143 if (aCollider.previousAabb && aCollider.currentAabb)
144 remove_entity(aEntity, aCollider,
aabb_union(*aCollider.previousAabb, *aCollider.currentAabb));
150 auto existing = std::find(iEntities.begin(), iEntities.end(), aEntity);
151 if (existing != iEntities.end())
152 iEntities.erase(existing);
155 child<0, 0, 0>().remove_entity(aEntity, aAabb);
157 child<0, 0, 1>().remove_entity(aEntity, aAabb);
159 child<0, 1, 0>().remove_entity(aEntity, aAabb);
161 child<0, 1, 1>().remove_entity(aEntity, aAabb);
163 child<1, 0, 0>().remove_entity(aEntity, aAabb);
165 child<1, 0, 1>().remove_entity(aEntity, aAabb);
167 child<1, 1, 0>().remove_entity(aEntity, aAabb);
169 child<1, 1, 1>().remove_entity(aEntity, aAabb);
171 iTree.destroy_node(*
this);
175 iTree.iDepth = std::max(iTree.iDepth, iDepth);
176 auto const& currentAabb = aCollider.currentAabb;
177 auto const& previousAabb = aCollider.previousAabb;
178 if (currentAabb == previousAabb)
181 add_entity(aEntity, aCollider);
183 remove_entity(aEntity, aCollider, previousAabb);
187 bool result = iEntities.empty();
188 if (has_child<0, 0, 0>())
189 result = child<0, 0, 0>().empty() && result;
190 if (has_child<0, 0, 1>())
191 result = child<0, 0, 1>().empty() && result;
192 if (has_child<0, 1, 0>())
193 result = child<0, 1, 0>().empty() && result;
194 if (has_child<0, 1, 1>())
195 result = child<0, 1, 1>().empty() && result;
196 if (has_child<1, 0, 0>())
197 result = child<1, 0, 0>().empty() && result;
198 if (has_child<1, 0, 1>())
199 result = child<1, 0, 1>().empty() && result;
200 if (has_child<1, 1, 0>())
201 result = child<1, 1, 0>().empty() && result;
202 if (has_child<1, 1, 1>())
203 result = child<1, 1, 1>().empty() && result;
206 const entity_list& entities()
const
210 template <
typename Visitor>
211 void visit(
const collider_type& aCandidate,
const Visitor& aVisitor)
const
213 if (aCandidate.currentAabb)
214 visit(*aCandidate.currentAabb, aVisitor);
216 template <
typename Visitor>
217 void visit(
const vec3& aPoint,
const Visitor& aVisitor)
const
221 template <
typename Visitor>
222 void visit(
const vec2& aPoint,
const Visitor& aVisitor)
const
226 template <
typename Visitor>
227 void visit(
const neogfx::aabb& aAabb,
const Visitor& aVisitor)
const
229 for (
auto e : entities())
233 child<0, 0, 0>().visit(aAabb, aVisitor);
235 child<0, 0, 1>().visit(aAabb, aVisitor);
237 child<0, 1, 0>().visit(aAabb, aVisitor);
239 child<0, 1, 1>().visit(aAabb, aVisitor);
241 child<1, 0, 0>().visit(aAabb, aVisitor);
243 child<1, 0, 1>().visit(aAabb, aVisitor);
245 child<1, 1, 0>().visit(aAabb, aVisitor);
247 child<1, 1, 1>().visit(aAabb, aVisitor);
249 template <
typename Visitor>
250 void visit(
const neogfx::aabb_2d& aAabb,
const Visitor& aVisitor)
const
252 for (
auto e : entities())
253 if (iTree.iEcs.component<
collider_type>().entity_record(e).currentAabb &&
256 if (has_child<0, 0, 0>() &&
aabb_intersects(iOctants2d[0][0][0], aAabb))
257 child<0, 0, 0>().visit(aAabb, aVisitor);
258 if (has_child<0, 0, 1>() &&
aabb_intersects(iOctants2d[0][0][1], aAabb))
259 child<0, 0, 1>().visit(aAabb, aVisitor);
260 if (has_child<0, 1, 0>() &&
aabb_intersects(iOctants2d[0][1][0], aAabb))
261 child<0, 1, 0>().visit(aAabb, aVisitor);
262 if (has_child<0, 1, 1>() &&
aabb_intersects(iOctants2d[0][1][1], aAabb))
263 child<0, 1, 1>().visit(aAabb, aVisitor);
264 if (has_child<1, 0, 0>() &&
aabb_intersects(iOctants2d[1][0][0], aAabb))
265 child<1, 0, 0>().visit(aAabb, aVisitor);
266 if (has_child<1, 0, 1>() &&
aabb_intersects(iOctants2d[1][0][1], aAabb))
267 child<1, 0, 1>().visit(aAabb, aVisitor);
268 if (has_child<1, 1, 0>() &&
aabb_intersects(iOctants2d[1][1][0], aAabb))
269 child<1, 1, 0>().visit(aAabb, aVisitor);
270 if (has_child<1, 1, 1>() &&
aabb_intersects(iOctants2d[1][1][1], aAabb))
271 child<1, 1, 1>().visit(aAabb, aVisitor);
273 template <
typename Visitor>
274 void visit_entities(
const Visitor& aVisitor)
const
276 for (
auto e : entities())
278 if (has_child<0, 0, 0>())
279 child<0, 0, 0>().visit_entities(aVisitor);
280 if (has_child<0, 0, 1>())
281 child<0, 0, 1>().visit_entities(aVisitor);
282 if (has_child<0, 1, 0>())
283 child<0, 1, 0>().visit_entities(aVisitor);
284 if (has_child<0, 1, 1>())
285 child<0, 1, 1>().visit_entities(aVisitor);
286 if (has_child<1, 0, 0>())
287 child<1, 0, 0>().visit_entities(aVisitor);
288 if (has_child<1, 0, 1>())
289 child<1, 0, 1>().visit_entities(aVisitor);
290 if (has_child<1, 1, 0>())
291 child<1, 1, 0>().visit_entities(aVisitor);
292 if (has_child<1, 1, 1>())
293 child<1, 1, 1>().visit_entities(aVisitor);
295 template <
typename Visitor>
296 void visit_aabbs(
const Visitor& aVisitor)
const
299 if (has_child<0, 0, 0>())
300 child<0, 0, 0>().visit_aabbs(aVisitor);
301 if (has_child<0, 0, 1>())
302 child<0, 0, 1>().visit_aabbs(aVisitor);
303 if (has_child<0, 1, 0>())
304 child<0, 1, 0>().visit_aabbs(aVisitor);
305 if (has_child<0, 1, 1>())
306 child<0, 1, 1>().visit_aabbs(aVisitor);
307 if (has_child<1, 0, 0>())
308 child<1, 0, 0>().visit_aabbs(aVisitor);
309 if (has_child<1, 0, 1>())
310 child<1, 0, 1>().visit_aabbs(aVisitor);
311 if (has_child<1, 1, 0>())
312 child<1, 1, 0>().visit_aabbs(aVisitor);
313 if (has_child<1, 1, 1>())
314 child<1, 1, 1>().visit_aabbs(aVisitor);
317 void populate_octants()
319 auto const& min = iAabb.min;
320 auto const& max = iAabb.max;
321 auto const& center = (min + max) / 2.0;
330 iOctants2d[0][0][0] =
aabb_2d{ ~min.xy, ~center.xy };
331 iOctants2d[0][1][0] =
aabb_2d{
vec2{ min.
x, center.y },
vec2{ center.
x, max.y } };
332 iOctants2d[1][0][0] =
aabb_2d{
vec2{ center.
x, min.y },
vec2{ max.
x, center.y } };
333 iOctants2d[1][1][0] =
aabb_2d{ ~center.xy, ~max.xy };
334 iOctants2d[0][0][1] =
aabb_2d{ ~min.xy, ~center.xy};
335 iOctants2d[0][1][1] =
aabb_2d{
vec2{ min.
x, center.y },
vec2{ center.
x, max.y } };
336 iOctants2d[1][0][1] =
aabb_2d{
vec2{ center.
x, min.y },
vec2{ max.
x, center.y } };
337 iOctants2d[1][1][1] =
aabb_2d{ ~center.xy, ~max.xy };
339 template <std::
size_t X, std::
size_t Y, std::
size_t Z>
342 if (iChildren == std::nullopt)
344 if ((*iChildren)[
X][
Y][
Z] ==
nullptr)
345 (*iChildren)[
X][
Y][
Z] = iTree.create_node(*
this, iOctants[
X][
Y][
Z]);
346 return *(*iChildren)[
X][
Y][
Z];
348 template <std::
size_t X, std::
size_t Y, std::
size_t Z>
349 bool has_child()
const
351 if (iChildren == std::nullopt)
353 return (*iChildren)[
X][
Y][
Z] !=
nullptr;
355 template <std::
size_t X, std::
size_t Y, std::
size_t Z>
356 bool remove_child(node* aDestroyedNode =
nullptr)
const
358 if (iChildren == std::nullopt)
360 if ((*iChildren)[
X][
Y][
Z] ==
nullptr)
362 if ((*iChildren)[
X][
Y][
Z] == aDestroyedNode || aDestroyedNode ==
nullptr)
364 auto n = (*iChildren)[
X][
Y][
Z];
365 (*iChildren)[
X][
Y][
Z] =
nullptr;
366 iTree.destroy_node(*n);
371 bool is_split()
const
373 return iChildren != std::nullopt;
377 for (
auto e : entities())
379 auto const& collider = iTree.iEcs.component<
collider_type>().entity_record(e);
381 child<0, 0, 0>().add_entity(e, collider);
383 child<0, 0, 1>().add_entity(e, collider);
385 child<0, 1, 0>().add_entity(e, collider);
387 child<0, 1, 1>().add_entity(e, collider);
389 child<1, 0, 0>().add_entity(e, collider);
391 child<1, 0, 1>().add_entity(e, collider);
393 child<1, 1, 0>().add_entity(e, collider);
395 child<1, 1, 1>().add_entity(e, collider);
399 void unsplit(node* aDestroyedNode)
401 bool haveChildren =
false;
402 if (!remove_child<0, 0, 0>(aDestroyedNode))
404 if (!remove_child<0, 1, 0>(aDestroyedNode))
406 if (!remove_child<1, 0, 0>(aDestroyedNode))
408 if (!remove_child<1, 1, 0>(aDestroyedNode))
410 if (!remove_child<0, 0, 1>(aDestroyedNode))
412 if (!remove_child<0, 1, 1>(aDestroyedNode))
414 if (!remove_child<1, 0, 1>(aDestroyedNode))
416 if (!remove_child<1, 1, 1>(aDestroyedNode))
419 iChildren = std::nullopt;
421 iTree.destroy_node(*
this);
429 octants_2d iOctants2d;
430 entity_list iEntities;
431 mutable std::optional<children> iChildren;
433 typedef typename allocator_type::template rebind<node>::other node_allocator;
436 iAllocator{ aAllocator },
438 iRootAabb{ aRootAabb },
441 iRootNode{ *this, aRootAabb },
442 iMinimumOctantSize{ aMinimumOctantSize },
443 iCollisionUpdateId{ 0 }
449 return iMinimumOctantSize;
455 new(&iRootNode) node{ *
this, iRootAabb };
459 iRootNode.add_entity(
entity, collider);
468 iRootNode.update_entity(
entity, collider);
471 template <
typename CollisionAction>
477 if (candidateInfo.destroyed)
480 if (++iCollisionUpdateId == 0)
481 iCollisionUpdateId = 1;
482 iRootNode.visit(candidateCollider, [&](
entity_id aHit)
484 if (candidateInfo.destroyed)
486 if (candidate < aHit)
489 if (hitInfo.destroyed)
492 if ((candidateCollider.mask & hitCollider.mask) == 0 && hitCollider.collisionEventId != iCollisionUpdateId)
494 hitCollider.collisionEventId = iCollisionUpdateId;
495 aCollisionAction(candidate, aHit);
501 template <
typename ResultContainer>
502 void pick(
const vec3& aPoint, ResultContainer& aResult, std::function<
bool(
entity_id aMatch,
const vec3& aPoint)> aColliderPredicate = [](
entity_id,
const vec3&) {
return true; })
const
504 iRootNode.visit(aPoint, [&](
entity_id aMatch)
507 if (!matchInfo.destroyed && aColliderPredicate(aMatch, aPoint))
508 aResult.insert(aResult.end(), aMatch);
511 template <
typename ResultContainer>
512 void pick(
const vec2& aPoint, ResultContainer& aResult, std::function<
bool(
entity_id aMatch,
const vec2& aPoint)> aColliderPredicate = [](
entity_id,
const vec2&) {
return true; })
const
514 iRootNode.visit(aPoint, [&](
entity_id aMatch)
517 if (!matchInfo.destroyed && aColliderPredicate(aMatch, aPoint))
518 aResult.insert(aResult.end(), aMatch);
521 template <
typename Visitor>
524 iRootNode.visit_aabbs(aVisitor);
529 iRootNode.add_entity(aItem);
533 iRootNode.remove_entity(aItem);
550 node* create_node(
const node& aParent,
const aabb& aAabb)
553 node* newNode = iAllocator.allocate(1);
554 iAllocator.construct(newNode, aParent, aAabb);
557 void destroy_node(node& aNode)
559 if (&aNode != &iRootNode && aNode.is_alive())
562 iAllocator.destroy(&aNode);
563 iAllocator.deallocate(&aNode, 1);
567 node_allocator iAllocator;
570 scalar iMinimumOctantSize;
572 mutable uint32_t iDepth;
574 mutable uint32_t iCollisionUpdateId;