Toast Engine
Loading...
Searching...
No Matches
aabb_tree.hpp
Go to the documentation of this file.
1
8
9#pragma once
10#include "aabb.hpp"
11#include "shape.hpp"
12
13#include <algorithm>
14#include <array>
15#include <cstddef>
16#include <cstdint>
17#include <limits>
18#include <optional>
19#include <toast/export.hpp>
20#include <toast/log.hpp>
21#include <vector>
22
23namespace physics {
24
25using TreeNodeID = uint32_t;
26inline constexpr TreeNodeID null_node = std::numeric_limits<TreeNodeID>::max();
27
28struct TOAST_API TreeNode {
29 AABB bounds {};
30 TreeNodeID parent = null_node;
31 TreeNodeID left = null_node;
32 TreeNodeID right = null_node;
33 TreeNodeID next_free = null_node;
34
35 ShapeID shape {};
36 int height = -1;
37
38 [[nodiscard]]
39 auto isAllocated() const -> bool;
40
41 [[nodiscard]]
42 auto isLeaf() const -> bool;
43};
44
45struct TOAST_API AABBTreeDebugNode {
46 AABB bounds {};
47 TreeNodeID id = null_node;
48 int height = -1;
49 bool leaf = false;
50};
51
52class TOAST_API AABBTree {
53public:
54 explicit AABBTree(size_t expected_shapes = 0);
55
56 [[nodiscard]]
57 auto insert(ShapeID shape, const AABB& bounds) -> TreeNodeID;
58 void remove(TreeNodeID leaf_id);
59 [[nodiscard]]
60 auto updateLeaf(TreeNodeID leaf_id, const AABB& tight_bounds) -> bool;
61 [[nodiscard]]
62 auto query(const AABB& bounds, ShapeID ignored_shape = {}) const -> std::vector<ShapeID>;
63 template<typename Visit>
64 void raycast(glm::vec3 origin, glm::vec3 inv_dir, float max_distance, Visit&& visit) const;
65 [[nodiscard]]
66 auto debugNodes() const -> std::vector<AABBTreeDebugNode>;
67 [[nodiscard]]
68 auto size() const -> size_t;
69 [[nodiscard]]
70 auto validate() const -> bool;
71
72private:
73 [[nodiscard]]
74 static auto fatMargin() -> float;
75
76 auto allocateNode() -> TreeNodeID;
77 void freeNode(TreeNodeID node_id);
78 void insertLeaf(TreeNodeID leaf_id);
79 void detachLeaf(TreeNodeID leaf_id);
80 void refitAncestors(TreeNodeID node_id);
81 auto balance(TreeNodeID node_id) -> TreeNodeID;
82 void recalculate(TreeNodeID node_id);
83
84 std::vector<TreeNode> m_nodes;
85 TreeNodeID m_root = null_node;
86 TreeNodeID m_free_list = null_node;
87 size_t m_active_node_count = 0;
88};
89
90template<typename Visit>
91void AABBTree::raycast(glm::vec3 origin, glm::vec3 inv_dir, float max_distance, Visit&& visit) const {
92 if (m_root == null_node) {
93 return;
94 }
95
96 // Depth first with the nearer child on top
97 struct Pending {
98 TreeNodeID node;
99 float t_enter;
100 };
101
102 std::array<Pending, 64> stack;
103 size_t count = 0;
104
105 float reach = max_distance;
106 const auto enters = [&](TreeNodeID id, float& t_enter) {
107 const std::optional<AABB::RayHit> hit = m_nodes[id].bounds.intersectRay(origin, inv_dir);
108 if (!hit || hit->t_max < 0.0f || hit->t_min > reach) {
109 return false;
110 }
111 t_enter = std::max(hit->t_min, 0.0f);
112 return true;
113 };
114
115 float t_root = 0.0f;
116 if (!enters(m_root, t_root)) {
117 return;
118 }
119 stack[count++] = {m_root, t_root};
120
121 while (count > 0) {
122 const Pending pending = stack[--count];
123 // The reach may have shrunk
124 if (pending.t_enter > reach) {
125 continue;
126 }
127
128 const TreeNode& node = m_nodes[pending.node];
129 if (node.isLeaf()) {
130 reach = std::min(reach, visit(node.shape, pending.t_enter));
131 continue;
132 }
133
134 float t_left = 0.0f;
135 float t_right = 0.0f;
136 const bool hits_left = enters(node.left, t_left);
137 const bool hits_right = enters(node.right, t_right);
138 TOAST_ASSERT(count + 2 <= stack.size(), "Physics", "AABB tree ray stack is too small for the tree");
139 if (count + 2 > stack.size()) {
140 return;
141 }
142
143 // The farther child goes in first
144 if (hits_left && hits_right) {
145 if (t_left <= t_right) {
146 stack[count++] = {node.right, t_right};
147 stack[count++] = {node.left, t_left};
148 } else {
149 stack[count++] = {node.left, t_left};
150 stack[count++] = {node.right, t_right};
151 }
152 } else if (hits_left) {
153 stack[count++] = {node.left, t_left};
154 } else if (hits_right) {
155 stack[count++] = {node.right, t_right};
156 }
157 }
158}
159
160}
Axis-aligned bounds node.
Public logging interface for the engine.
Shape identifiers, geometry, and runtime records.
Definition aabb_tree.hpp:45
Definition aabb.hpp:16
Definition shape.hpp:29
Definition aabb_tree.hpp:28