Simulant  21.12-574
A portable game engine for Windows, OSX, Linux, Dreamcast, and PSP
loose_quadtree.h
1 #pragma once
2 
3 #include <cstdint>
4 #include "../../math/aabb.h"
5 #include "../../frustum.h"
6 #include "../../generic/check_signature.h"
7 
8 namespace smlt {
9 
10 static constexpr int64_t ipow(int64_t base, int exp, int64_t result = 1) {
11  return exp < 1 ? result : ipow(base*base, exp/2, (exp % 2) ? result*base : result);
12 }
13 
14 /*
15  * Fast loose-Quadtree implementation with O(1) insertions, built on a linear
16  * array for good cache locality.
17  *
18  * TreeData is a structure which contains data needed across the whole tree,
19  * this may usually contain the vertex data for the mesh being inserted.
20  *
21  * NodeData is a structure for storing data on the individual nodes, this is usually
22  * index data for the mesh.
23  */
24 
25 typedef uint8_t Level;
26 typedef uint16_t GridCoord;
27 
28 template<typename TreeData, typename NodeData>
29 class Quadtree {
30 public:
31  struct Node {
32  Level level = 0;
33  GridCoord grid[2] = {0, 0};
34  NodeData* data = nullptr;
35  uint32_t child_indexes[4];
36 
37  Vec3 centre;
38  float size;
39  };
40 
41  using TraverseCallback = void(Quadtree::Node*);
42 
43  typedef TreeData tree_data_type;
44  typedef NodeData node_data_type;
45 
46  Quadtree(const AABB& bounds, uint8_t max_level_count=4, std::shared_ptr<TreeData> tree_data=std::shared_ptr<TreeData>()):
47  tree_data_(tree_data),
48  root_width_(bounds.max_dimension()),
49  bounds_(bounds),
50  centre_(bounds_.centre()) {
51 
52  /* Make sure the bounds are square */
53  float maxd = root_width_;
54  auto halfd = maxd / 2.0f;
55 
56  auto half = Vec3(halfd, 0, halfd);
57  bounds_ = AABB(centre_, half);
58 
59  /* Grow the tree to whatever size we passed in */
60  grow(max_level_count);
61  }
62 
63  virtual ~Quadtree() {
64  /* Make sure we delete the node data */
65  for(auto& node: nodes_) {
66  delete node.data;
67  node.data = nullptr;
68  }
69  }
70 
71  bool is_leaf(Node& node) const {
72  return node.level == (levels_ - 1);
73  }
74 
75  Quadtree::Node* find_destination_for_sphere(const Vec3& centre, float radius) {
76  auto diameter = radius * 2.0f;
77  auto level_and_node_width = level_for_width(diameter);
78 
79  /* Calculate the cell index to insert the sphere */
80  auto half_width = root_width_ * 0.5f;
81 
82  assert(centre.x <= bounds_.max().x && centre.x >= bounds_.min().x);
83  assert(centre.z <= bounds_.max().z && centre.z >= bounds_.min().z);
84 
85  auto x = (GridCoord) ((centre.x + half_width - centre_.x) / level_and_node_width.second);
86  auto z = (GridCoord) ((centre.z + half_width - centre_.z) / level_and_node_width.second);
87 
88  assert(x >= 0);
89  assert(z >= 0);
90 
91  auto level_width = ipow(2, level_and_node_width.first);
92  assert(x <= level_width);
93  assert(z <= level_width);
94 
95  /* This handles the case that the center was on the cusp of the cell
96  * in which case the above calculation will result in an out of range
97  * index */
98  x = std::min<GridCoord>(x, level_width - 1);
99  z = std::min<GridCoord>(z, level_width - 1);
100 
101  auto idx = calc_index(level_and_node_width.first, x, z);
102  assert(idx < nodes_.size());
103 
104  return &nodes_[idx];
105  }
106 
107  Quadtree::Node* find_destination_for_triangle(const Vec3* vertices) {
108  /*
109  * Return the node that this triangle should be
110  * inserted into (the actual insertion won't happen as that's implementation specific
111  * depending on the NodeData).
112  */
113 
114  AABB bounds(vertices, 3);
115  auto centre = bounds.centre();
116  auto radius = bounds.max_dimension() / 2.0f;
117  return find_destination_for_sphere(centre, radius);
118  }
119 
120  void traverse(std::function<void (Quadtree::Node*)> cb) {
121  if(nodes_.empty()) {
122  return;
123  }
124 
125  std::function<void (Quadtree::Node&)> visitor = [&](Quadtree::Node& node) {
126  cb(&node);
127 
128  if(!is_leaf(node)) {
129  for(auto child: node.child_indexes) {
130  assert(child < nodes_.size());
131  visitor(nodes_[child]);
132  }
133  }
134  };
135 
136  visitor(nodes_[0]);
137  }
138 
139  template<typename Callback>
140  void traverse_visible(const Frustum& frustum, const Callback& cb) {
141  check_signature<Callback, TraverseCallback>();
142 
143  if(nodes_.empty()) {
144  return;
145  }
146 
147  _visible_visitor(frustum, cb, nodes_[0]);
148  }
149 
150  AABB bounds() const { return bounds_; }
151 
152  TreeData* data() const { return tree_data_.get(); }
153 private:
154  template<typename Callback>
155  void _visible_visitor(const Frustum& frustum, const Callback& callback, Quadtree::Node& node) {
156  if(frustum.intersects_cube(node.centre, node.size * 2.0f)) {
157  callback(&node);
158 
159  if(!is_leaf(node)) {
160  for(auto child: node.child_indexes) {
161  assert(child < nodes_.size());
162  _visible_visitor(frustum, callback, nodes_[child]);
163  }
164  }
165  }
166  }
167 
168  std::pair<Level, float> level_for_width(float obj_width) {
169  /*
170  * Given the diameter of the object
171  * this returns the level the object will fit, and the node width at that level
172  *
173  * I *know* there's a non-iterative way to calculate this, so if you know, let me know!
174  */
175 
176  assert(obj_width <= root_width_);
177 
178  if(obj_width > root_width_) {
179  // Should never happen, but this is the safest thing to do if it does
180  return std::make_pair(0, root_width_);
181  }
182 
183  auto node_width = root_width_;
184  Level depth = 0;
185 
186  while(node_width >= obj_width) {
187  ++depth;
188  node_width *= 0.5f;
189 
190  if(depth == levels_) {
191  break;
192  }
193  }
194 
195  assert(depth >= 1);
196 
197  // Off-by-one
198  return std::make_pair(depth - 1, node_width * 2);
199  }
200 
201  void calc_bounds(Quadtree::Node& node) const {
202  auto grid_width = ipow(2, node.level);
203  auto cell_width = bounds_.max_dimension() / grid_width;
204 
205  Vec3 min(node.grid[0] * cell_width, 0, node.grid[1] * cell_width);
206  min += bounds_.min();
207 
208  float hw = cell_width * 0.5f;
209  node.centre = min + Vec3(hw, hw, hw);
210  node.size = cell_width;
211  }
212 
213  static uint32_t calc_base(uint8_t level) {
214  if(level == 0) return 0;
215  if(level == 1) return 1;
216  return calc_base(level - 1) + ipow(ipow(2, level - 1), 2);
217  }
218 
219  static uint32_t calc_index(Level k, GridCoord x, GridCoord z) {
220  auto level_base = calc_base(k);
221  auto level_width = ipow(2, k);
222 
223  assert(x < level_width);
224  assert(z < level_width);
225 
226  auto idx = x + level_width * z;
227  return level_base + idx;
228  }
229 
230  static void calc_child_indexes(Quadtree::Node& node) {
231  uint8_t count = 0;
232  for(uint32_t z = 0; z <= 1; ++z) {
233  for(uint32_t x = 0; x <= 1; ++x) {
234  node.child_indexes[count++] = calc_index(
235  node.level + 1,
236  2 * node.grid[0] + x,
237  2 * node.grid[1] + z
238  );
239  }
240  }
241  }
242 
243  void grow(uint8_t required_levels) {
244 
245  auto required_nodes = 0;
246  for(uint8_t i = 0; i < required_levels; ++i) {
247  auto nodes_across = ipow(2, i);
248  required_nodes += ipow(nodes_across, 2);
249  }
250 
251  levels_ = required_levels;
252  nodes_.resize(required_nodes, Quadtree::Node());
253 
254  for(auto k = 0; k < levels_; ++k) {
255  auto nodes_across = ipow(2, k);
256 
257  for(GridCoord z = 0; z < nodes_across; ++z) {
258  for(GridCoord x = 0; x < nodes_across; ++x) {
259  auto idx = calc_index(k, x, z);
260  assert(idx < nodes_.size());
261  auto& new_node = nodes_[idx];
262 
263  assert(!new_node.data);
264 
265  new_node.grid[0] = x;
266  new_node.grid[1] = z;
267  new_node.level = k;
268  new_node.data = new NodeData();
269 
270  calc_child_indexes(new_node);
271  calc_bounds(new_node);
272  }
273  }
274  }
275  }
276 
277  std::shared_ptr<TreeData> tree_data_;
278 
279  float root_width_;
280  AABB bounds_;
281  Vec3 centre_;
282 
283  Level levels_ = 0;
284  std::vector<Quadtree::Node> nodes_;
285 
286  friend class LooseQuadtreeTests;
287 };
288 
289 }
smlt::Vec3
Definition: vec3.h:23
half_float::half
Definition: half.hpp:923
smlt::Quadtree
Definition: loose_quadtree.h:29
smlt
Definition: animation.cpp:25
half_float::detail::exp
expr exp(half arg)
Definition: half.hpp:2160
smlt::Quadtree::Node
Definition: loose_quadtree.h:31
smlt::AABB
Definition: aabb.h:22