4 #include "../../math/aabb.h"
5 #include "../../frustum.h"
6 #include "../../generic/check_signature.h"
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);
25 typedef uint8_t Level;
26 typedef uint16_t GridCoord;
28 template<
typename TreeData,
typename NodeData>
33 GridCoord grid[2] = {0, 0};
34 NodeData* data =
nullptr;
35 uint32_t child_indexes[4];
43 typedef TreeData tree_data_type;
44 typedef NodeData node_data_type;
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()),
50 centre_(bounds_.centre()) {
53 float maxd = root_width_;
54 auto halfd = maxd / 2.0f;
57 bounds_.set_min(centre_ -
half);
58 bounds_.set_max(centre_ +
half);
61 grow(max_level_count);
66 for(
auto& node: nodes_) {
72 bool is_leaf(Node& node)
const {
73 return node.level == (levels_ - 1);
76 Quadtree::Node* find_destination_for_sphere(
const Vec3& centre,
float radius) {
77 auto diameter = radius * 2.0f;
78 auto level_and_node_width = level_for_width(diameter);
81 auto half_width = root_width_ * 0.5f;
83 assert(centre.x <= bounds_.max().x && centre.x >= bounds_.min().x);
84 assert(centre.z <= bounds_.max().z && centre.z >= bounds_.min().z);
86 auto x = (GridCoord) ((centre.x + half_width - centre_.x) / level_and_node_width.second);
87 auto z = (GridCoord) ((centre.z + half_width - centre_.z) / level_and_node_width.second);
92 auto level_width = ipow(2, level_and_node_width.first);
93 assert(x <= level_width);
94 assert(z <= level_width);
99 x = std::min<GridCoord>(x, level_width - 1);
100 z = std::min<GridCoord>(z, level_width - 1);
102 auto idx = calc_index(level_and_node_width.first, x, z);
103 assert(idx < nodes_.size());
108 Quadtree::Node* find_destination_for_triangle(
const Vec3* vertices) {
115 AABB bounds(vertices, 3);
116 auto centre = bounds.centre();
117 auto radius = bounds.max_dimension() / 2.0f;
118 return find_destination_for_sphere(centre, radius);
121 void traverse(std::function<
void (Quadtree::Node*)> cb) {
126 std::function<void (Quadtree::Node&)> visitor = [&](Quadtree::Node& node) {
130 for(
auto child: node.child_indexes) {
131 assert(child < nodes_.size());
132 visitor(nodes_[child]);
140 template<
typename Callback>
141 void traverse_visible(
const Frustum& frustum,
const Callback& cb) {
142 check_signature<Callback, TraverseCallback>();
148 _visible_visitor(frustum, cb, nodes_[0]);
151 AABB bounds()
const {
return bounds_; }
153 TreeData* data()
const {
return tree_data_.get(); }
155 template<
typename Callback>
156 void _visible_visitor(
const Frustum& frustum,
const Callback& callback, Quadtree::Node& node) {
157 if(frustum.intersects_cube(node.centre, node.size * 2.0f)) {
161 for(
auto child: node.child_indexes) {
162 assert(child < nodes_.size());
163 _visible_visitor(frustum, callback, nodes_[child]);
169 std::pair<Level, float> level_for_width(
float obj_width) {
177 assert(obj_width <= root_width_);
179 if(obj_width > root_width_) {
181 return std::make_pair(0, root_width_);
184 auto node_width = root_width_;
187 while(node_width >= obj_width) {
191 if(depth == levels_) {
199 return std::make_pair(depth - 1, node_width * 2);
202 void calc_bounds(Quadtree::Node& node)
const {
203 auto grid_width = ipow(2, node.level);
204 auto cell_width = bounds_.max_dimension() / grid_width;
206 Vec3 min(node.grid[0] * cell_width, 0, node.grid[1] * cell_width);
207 min += bounds_.min();
209 float hw = cell_width * 0.5f;
210 node.centre = min + Vec3(hw, hw, hw);
211 node.size = cell_width;
214 static uint32_t calc_base(uint8_t level) {
215 if(level == 0)
return 0;
216 if(level == 1)
return 1;
217 return calc_base(level - 1) + ipow(ipow(2, level - 1), 2);
220 static uint32_t calc_index(Level k, GridCoord x, GridCoord z) {
221 auto level_base = calc_base(k);
222 auto level_width = ipow(2, k);
224 assert(x < level_width);
225 assert(z < level_width);
227 auto idx = x + level_width * z;
228 return level_base + idx;
231 static void calc_child_indexes(Quadtree::Node& node) {
233 for(uint32_t z = 0; z <= 1; ++z) {
234 for(uint32_t x = 0; x <= 1; ++x) {
235 node.child_indexes[count++] = calc_index(
237 2 * node.grid[0] + x,
244 void grow(uint8_t required_levels) {
246 auto required_nodes = 0;
247 for(uint8_t i = 0; i < required_levels; ++i) {
248 auto nodes_across = ipow(2, i);
249 required_nodes += ipow(nodes_across, 2);
252 levels_ = required_levels;
253 nodes_.resize(required_nodes, Quadtree::Node());
255 for(
auto k = 0; k < levels_; ++k) {
256 auto nodes_across = ipow(2, k);
258 for(GridCoord z = 0; z < nodes_across; ++z) {
259 for(GridCoord x = 0; x < nodes_across; ++x) {
260 auto idx = calc_index(k, x, z);
261 assert(idx < nodes_.size());
262 auto& new_node = nodes_[idx];
264 assert(!new_node.data);
266 new_node.grid[0] = x;
267 new_node.grid[1] = z;
269 new_node.data =
new NodeData();
271 calc_child_indexes(new_node);
272 calc_bounds(new_node);
278 std::shared_ptr<TreeData> tree_data_;
285 std::vector<Quadtree::Node> nodes_;
287 friend class LooseQuadtreeTests;