4 #include "../../math/aabb.h"
5 #include "../../frustum.h"
6 #include "../../generic/check_signature.h"
10 namespace _loose_octree {
11 static constexpr int32_t ipow(int32_t base, int32_t exp, int32_t result = 1) {
12 return exp < 1 ? result : ipow(base * base, exp / 2, (exp % 2) ? result * base : result);
27 typedef uint8_t Level;
28 typedef uint16_t GridCoord;
30 template<
typename TreeData,
typename NodeData>
35 GridCoord grid[3] = {0, 0, 0};
36 NodeData* data =
nullptr;
37 uint32_t child_indexes[8];
45 typedef TreeData tree_data_type;
46 typedef NodeData node_data_type;
48 Octree(
const AABB& bounds, uint8_t max_level_count=4, std::shared_ptr<TreeData> tree_data=std::shared_ptr<TreeData>()):
49 tree_data_(tree_data),
50 root_width_(bounds.max_dimension()),
52 centre_(bounds_.centre()) {
55 float maxd = root_width_;
56 auto halfd = maxd / 2.0f;
58 auto half =
Vec3(halfd, halfd, halfd);
63 grow(max_level_count);
68 for(
auto& node: nodes_) {
74 bool is_leaf(Node& node)
const {
75 return node.level == (levels_ - 1);
78 Octree::Node* find_destination_for_sphere(
const Vec3& centre,
float radius) {
79 auto diameter = radius * 2;
80 auto level_and_node_width = level_for_width(diameter);
83 auto half_width = root_width_ * 0.5f;
86 const float E = 0.001f;
88 assert(almost_lequal(centre.x, bounds_.max().x, E) && almost_gequal(centre.x, bounds_.min().x, E));
89 assert(almost_lequal(centre.y, bounds_.max().y, E) && almost_gequal(centre.y, bounds_.min().y, E));
90 assert(almost_lequal(centre.z, bounds_.max().z, E) && almost_gequal(centre.z, bounds_.min().z, E));
92 auto x = (GridCoord) ((centre.x + half_width - centre_.x) / level_and_node_width.second);
93 auto y = (GridCoord) ((centre.y + half_width - centre_.y) / level_and_node_width.second);
94 auto z = (GridCoord) ((centre.z + half_width - centre_.z) / level_and_node_width.second);
100 auto level_width = _loose_octree::ipow(2, level_and_node_width.first);
101 assert(x <= level_width);
102 assert(y <= level_width);
103 assert(z <= level_width);
108 x = std::min<GridCoord>(x, level_width - 1);
109 y = std::min<GridCoord>(y, level_width - 1);
110 z = std::min<GridCoord>(z, level_width - 1);
112 auto idx = calc_index(level_and_node_width.first, x, y, z);
113 assert(idx < nodes_.size());
118 Octree::Node* find_destination_for_triangle(
const Vec3* vertices) {
125 AABB bounds(vertices, 3);
126 auto centre = bounds.centre();
127 auto radius = bounds.max_dimension() / 2.0f;
128 return find_destination_for_sphere(centre, radius);
131 void traverse(std::function<
void (Octree::Node*)> cb) {
136 std::function<void (Octree::Node&)> visitor = [&](Octree::Node& node) {
140 for(
auto child: node.child_indexes) {
141 assert(child < nodes_.size());
142 visitor(nodes_[child]);
150 template<
typename Callback>
151 void traverse_visible(
const Frustum& frustum,
const Callback& cb) {
152 check_signature<Callback, TraverseCallback>();
158 _visible_visitor(frustum, cb, nodes_[0]);
161 AABB bounds()
const {
return bounds_; }
162 TreeData* data()
const {
return tree_data_.get(); }
164 template<
typename Callback>
165 void _visible_visitor(
const Frustum& frustum,
const Callback& callback, Octree::Node& node) {
166 if(frustum.intersects_cube(node.centre, node.size * 2.0f)) {
170 for(
auto child: node.child_indexes) {
171 assert(child < nodes_.size());
172 _visible_visitor(frustum, callback, nodes_[child]);
178 std::pair<Level, float> level_for_width(
float obj_width) {
186 assert(obj_width <= root_width_);
188 if(obj_width > root_width_) {
190 return std::make_pair(0, root_width_);
193 auto node_width = root_width_;
196 while(node_width >= obj_width) {
200 if(depth == levels_) {
208 return std::make_pair(depth - 1, node_width * 2);
211 void calc_bounds(Octree::Node& node)
const {
212 auto grid_width = _loose_octree::ipow(2, node.level);
213 auto cell_width = bounds_.max_dimension() / grid_width;
215 Vec3 min(node.grid[0] * cell_width, node.grid[1] * cell_width, node.grid[2] * cell_width);
216 min += bounds_.min();
218 float hw = cell_width * 0.5f;
219 node.centre = min + Vec3(hw, hw, hw);
220 node.size = cell_width;
223 static uint32_t calc_base(uint8_t level) {
224 if(level == 0)
return 0;
225 if(level == 1)
return 1;
226 return calc_base(level - 1) + _loose_octree::ipow(_loose_octree::ipow(2, level - 1), 3);
229 static uint32_t calc_index(Level k, GridCoord x, GridCoord y, GridCoord z) {
230 auto level_base = calc_base(k);
231 auto level_width = _loose_octree::ipow(2, k);
233 assert(x < level_width);
234 assert(y < level_width);
235 assert(z < level_width);
237 auto idx = x + level_width * y + level_width * level_width * z;
238 return level_base + idx;
241 static void calc_child_indexes(Octree::Node& node) {
244 for(uint32_t z = 0; z <= 1; ++z) {
245 for(uint32_t y = 0; y <= 1; ++y) {
246 for(uint32_t x = 0; x <= 1; ++x) {
247 node.child_indexes[count++] = calc_index(
249 2 * node.grid[0] + x,
250 2 * node.grid[1] + y,
258 void grow(uint8_t required_levels) {
260 auto required_nodes = 0;
261 for(uint8_t i = 0; i < required_levels; ++i) {
262 auto nodes_across = _loose_octree::ipow(2, i);
263 required_nodes += _loose_octree::ipow(nodes_across, 3);
266 levels_ = required_levels;
267 nodes_.resize(required_nodes, Octree::Node());
269 for(
auto k = 0; k < levels_; ++k) {
270 auto nodes_across = _loose_octree::ipow(2, k);
272 for(GridCoord z = 0; z < nodes_across; ++z) {
273 for(GridCoord y = 0; y < nodes_across; ++y) {
274 for(GridCoord x = 0; x < nodes_across; ++x) {
275 auto idx = calc_index(k, x, y, z);
276 assert(idx < nodes_.size());
278 auto& new_node = nodes_[idx];
280 assert(!new_node.data);
282 new_node.grid[0] = x;
283 new_node.grid[1] = y;
284 new_node.grid[2] = z;
286 new_node.data =
new NodeData();
288 calc_child_indexes(new_node);
289 calc_bounds(new_node);
296 std::shared_ptr<TreeData> tree_data_;
303 std::vector<Octree::Node> nodes_;
305 friend class LooseOctreeTests;