Loading...
Searching...
No Matches
Octree-Impl.hpp
Go to the documentation of this file.
1// This code is based on Jet framework.
2// Copyright (c) 2018 Doyub Kim
3// CubbyFlow is voxel-based fluid simulation engine for computer games.
4// Copyright (c) 2020 CubbyFlow Team
5// Core Part: Chris Ohk, Junwoo Hwang, Jihong Sin, Seungwoo Yoo
6// AI Part: Dongheon Cho, Minseo Kim
7// We are making my contributions/submissions to this project solely in our
8// personal capacity and are not conveying any rights to any intellectual
9// property of any third parties.
10
11#ifndef CUBBYFLOW_OCTREE_IMPL_HPP
12#define CUBBYFLOW_OCTREE_IMPL_HPP
13
14#include <numeric>
15#include <stack>
16
17namespace CubbyFlow
18{
19template <typename T>
20bool Octree<T>::Node::IsLeaf() const
21{
22 return firstChild == std::numeric_limits<size_t>::max();
23}
24
25template <typename T>
26void Octree<T>::Build(const std::vector<T>& items, const BoundingBox3D& bound,
28 size_t maxDepth)
29{
30 // Reset items
31 m_maxDepth = maxDepth;
32 m_items = items;
33 m_nodes.clear();
34
35 // Normalize bounding box
36 m_bbox = bound;
37 const double maxEdgeLen =
38 std::max({ m_bbox.Width(), m_bbox.Height(), m_bbox.Depth() });
39 m_bbox.upperCorner =
40 m_bbox.lowerCorner + Vector3D{ maxEdgeLen, maxEdgeLen, maxEdgeLen };
41
42 // Build
43 m_nodes.resize(1);
44 m_nodes[0].items.resize(m_items.size());
45 std::iota(m_nodes[0].items.begin(), m_nodes[0].items.end(), ZERO_SIZE);
46
47 Build(0, 1, m_bbox, testFunc);
48}
49
50template <typename T>
52{
53 m_maxDepth = 1;
54 m_items.clear();
55 m_nodes.clear();
56 m_bbox = BoundingBox3D();
57}
58
59template <typename T>
61 const Vector3D& pt,
63{
65 best.distance = std::numeric_limits<double>::max();
66 best.item = nullptr;
67
68 // Prepare to traverse octree
69 std::stack<std::pair<const Node*, BoundingBox3D>> todo;
70
71 // Traverse octree nodes
72 const Node* node = m_nodes.data();
73 BoundingBox3D bound = m_bbox;
74
75 while (node != nullptr)
76 {
77 if (node->IsLeaf())
78 {
79 for (size_t itemIdx : node->items)
80 {
81 double d = distanceFunc(m_items[itemIdx], pt);
82 if (d < best.distance)
83 {
84 best.distance = d;
85 best.item = &m_items[itemIdx];
86 }
87 }
88
89 // Grab next node to process from todo stack
90 if (todo.empty())
91 {
92 break;
93 }
94
95 node = todo.top().first;
96 bound = todo.top().second;
97 todo.pop();
98 }
99 else
100 {
101 using NodeDistBox = std::tuple<const Node*, double, BoundingBox3D>;
102
103 const double bestDistSqr = best.distance * best.distance;
104 std::array<NodeDistBox, 8> childDistSqrPairs;
105 const auto midPoint = bound.MidPoint();
106
107 for (int i = 0; i < 8; ++i)
108 {
109 const Node* child = &m_nodes[node->firstChild + i];
110 const auto childBound =
111 BoundingBox3D(bound.Corner(i), midPoint);
112 Vector3D cp = childBound.Clamp(pt);
114
116 std::make_tuple(child, distMinSqr, childBound);
117 }
118
120 [](const NodeDistBox& a, const NodeDistBox& b) {
121 return std::get<1>(a) > std::get<1>(b);
122 });
123
124 for (int i = 0; i < 8; ++i)
125 {
126 const auto& childPair = childDistSqrPairs[i];
127
128 if (std::get<1>(childPair) < bestDistSqr)
129 {
130 todo.emplace(std::get<0>(childPair),
131 std::get<2>(childPair));
132 }
133 }
134
135 if (todo.empty())
136 {
137 break;
138 }
139
140 node = todo.top().first;
141 bound = todo.top().second;
142 todo.pop();
143 }
144 }
145
146 return best;
147}
148
149template <typename T>
152{
153 return Intersects(box, testFunc, 0, m_bbox);
154}
155
156template <typename T>
159{
160 return Intersects(ray, testFunc, 0, m_bbox);
161}
162
163template <typename T>
167{
168 ForEachIntersectingItem(box, testFunc, visitorFunc, 0, m_bbox);
169}
170
171template <typename T>
175{
176 ForEachIntersectingItem(ray, testFunc, visitorFunc, 0, m_bbox);
177}
178
179template <typename T>
181 const Ray3D& ray, const GetRayIntersectionFunc3<T>& testFunc) const
182{
184 best.distance = std::numeric_limits<double>::max();
185 best.item = nullptr;
186
187 return ClosestIntersection(ray, testFunc, 0, m_bbox, best);
188}
189
190template <typename T>
192{
193 return m_items.begin();
194}
195
196template <typename T>
198{
199 return m_items.end();
200}
201
202template <typename T>
204{
205 return m_items.begin();
206}
207
208template <typename T>
210{
211 return m_items.end();
212}
213
214template <typename T>
216{
217 return m_items.size();
218}
219
220template <typename T>
221const T& Octree<T>::GetItem(size_t i) const
222{
223 return m_items[i];
224}
225
226template <typename T>
228{
229 return m_nodes.size();
230}
231
232template <typename T>
233const std::vector<size_t>& Octree<T>::GetItemsAtNode(size_t nodeIdx) const
234{
235 return m_nodes[nodeIdx].items;
236}
237
238template <typename T>
239size_t Octree<T>::GetChildIndex(size_t nodeIdx, size_t childIdx) const
240{
241 return m_nodes[nodeIdx].firstChild + childIdx;
242}
243
244template <typename T>
246{
247 return m_bbox;
248}
249
250template <typename T>
252{
253 return m_maxDepth;
254}
255
256template <typename T>
257void Octree<T>::Build(size_t nodeIdx, size_t depth, const BoundingBox3D& bound,
259{
260 if (depth < m_maxDepth && !m_nodes[nodeIdx].items.empty())
261 {
262 const size_t firstChild = m_nodes[nodeIdx].firstChild = m_nodes.size();
263 m_nodes.resize(m_nodes[nodeIdx].firstChild + 8);
264
266
267 for (int i = 0; i < 8; ++i)
268 {
269 bboxPerNode[i] = BoundingBox3D{ bound.Corner(i), bound.MidPoint() };
270 }
271
272 auto& currentItems = m_nodes[nodeIdx].items;
273 for (size_t i = 0; i < currentItems.size(); ++i)
274 {
275 size_t currentItem = currentItems[i];
276 for (int j = 0; j < 8; ++j)
277 {
278 if (testFunc(m_items[currentItem], bboxPerNode[j]))
279 {
280 m_nodes[firstChild + j].items.push_back(currentItem);
281 }
282 }
283 }
284
285 // Remove non-leaf data
286 currentItems.clear();
287
288 // Refine
289 for (int i = 0; i < 8; ++i)
290 {
291 Build(firstChild + i, depth + 1, bboxPerNode[i], testFunc);
292 }
293 }
294}
295
296template <typename T>
299 size_t nodeIdx, const BoundingBox3D& bound) const
300{
301 if (!box.Overlaps(bound))
302 {
303 return false;
304 }
305
306 const Node& node = m_nodes[nodeIdx];
307
308 if (!node.items.empty())
309 {
310 for (size_t itemIdx : node.items)
311 {
312 if (testFunc(m_items[itemIdx], box))
313 {
314 return true;
315 }
316 }
317 }
318
319 if (node.firstChild != std::numeric_limits<size_t>::max())
320 {
321 for (int i = 0; i < 8; ++i)
322 {
323 if (Intersects(box, testFunc, node.firstChild + i,
324 BoundingBox3D{ bound.Corner(i), bound.MidPoint() }))
325 {
326 return true;
327 }
328 }
329 }
330
331 return false;
332}
333
334template <typename T>
337 size_t nodeIdx, const BoundingBox3D& bound) const
338{
339 if (!bound.Intersects(ray))
340 {
341 return false;
342 }
343
344 const Node& node = m_nodes[nodeIdx];
345
346 if (!node.items.empty())
347 {
348 for (size_t itemIdx : node.items)
349 {
350 if (testFunc(m_items[itemIdx], ray))
351 {
352 return true;
353 }
354 }
355 }
356
357 if (node.firstChild != std::numeric_limits<size_t>::max())
358 {
359 for (int i = 0; i < 8; ++i)
360 {
361 if (Intersects(ray, testFunc, node.firstChild + i,
362 BoundingBox3D{ bound.Corner(i), bound.MidPoint() }))
363 {
364 return true;
365 }
366 }
367 }
368
369 return false;
370}
371
372template <typename T>
376 const BoundingBox3D& bound) const
377{
378 if (!box.Overlaps(bound))
379 {
380 return;
381 }
382
383 const Node& node = m_nodes[nodeIdx];
384
385 if (!node.items.empty())
386 {
387 for (size_t itemIdx : node.items)
388 {
389 if (testFunc(m_items[itemIdx], box))
390 {
391 visitorFunc(m_items[itemIdx]);
392 }
393 }
394 }
395
396 if (node.firstChild != std::numeric_limits<size_t>::max())
397 {
398 for (int i = 0; i < 8; ++i)
399 {
400 ForEachIntersectingItem(
401 box, testFunc, visitorFunc, node.firstChild + i,
402 BoundingBox3D{ bound.Corner(i), bound.MidPoint() });
403 }
404 }
405}
406
407template <typename T>
411 const BoundingBox3D& bound) const
412{
413 if (!bound.Intersects(ray))
414 {
415 return;
416 }
417
418 const Node& node = m_nodes[nodeIdx];
419
420 if (!node.items.empty())
421 {
422 for (size_t itemIdx : node.items)
423 {
424 if (testFunc(m_items[itemIdx], ray))
425 {
426 visitorFunc(m_items[itemIdx]);
427 }
428 }
429 }
430
431 if (node.firstChild != std::numeric_limits<size_t>::max())
432 {
433 for (int i = 0; i < 8; ++i)
434 {
435 ForEachIntersectingItem(
436 ray, testFunc, visitorFunc, node.firstChild + i,
437 BoundingBox3D{ bound.Corner(i), bound.MidPoint() });
438 }
439 }
440}
441
442template <typename T>
445 size_t nodeIdx, const BoundingBox3D& bound,
447{
448 if (!bound.Intersects(ray))
449 {
450 return best;
451 }
452
453 const Node& node = m_nodes[nodeIdx];
454
455 if (!node.items.empty())
456 {
457 for (size_t itemIdx : node.items)
458 {
459 double dist = testFunc(m_items[itemIdx], ray);
460 if (dist < best.distance)
461 {
462 best.distance = dist;
463 best.item = &m_items[itemIdx];
464 }
465 }
466 }
467
468 if (node.firstChild != std::numeric_limits<size_t>::max())
469 {
470 for (int i = 0; i < 8; ++i)
471 {
472 best = ClosestIntersection(
473 ray, testFunc, node.firstChild + i,
474 BoundingBox3D{ bound.Corner(i), bound.MidPoint() }, best);
475 }
476 }
477
478 return best;
479}
480} // namespace CubbyFlow
481
482#endif
N-D axis-aligned bounding box class.
Definition BoundingBox.hpp:47
VectorType MidPoint() const
Returns the mid-point of this box.
Definition BoundingBox-Impl.hpp:194
VectorType Corner(size_t idx) const
Returns corner position. Index starts from x-first order.
Definition BoundingBox-Impl.hpp:240
ValueType DistanceSquaredTo(const MatrixExpression< T, R, C, E > &other) const
Returns the squared distance to the other vector.
Definition Matrix.hpp:30
Iterator begin()
Definition Matrix-Impl.hpp:272
Pointer data()
Definition Matrix-Impl.hpp:298
Iterator end()
Definition Matrix-Impl.hpp:285
NearestNeighborQueryResult3< T > Nearest(const Vector3D &pt, const NearestNeighborDistanceFunc3< T > &distanceFunc) const override
Definition Octree-Impl.hpp:60
void Build(const std::vector< T > &items, const BoundingBox3D &bound, const BoxIntersectionTestFunc3< T > &testFunc, size_t maxDepth)
Definition Octree-Impl.hpp:26
const T & GetItem(size_t i) const
Returns the item at i.
Definition Octree-Impl.hpp:221
void Clear()
Clears all the contents of this instance.
Definition Octree-Impl.hpp:51
typename ContainerType::iterator Iterator
Definition Octree.hpp:34
typename ContainerType::const_iterator ConstIterator
Definition Octree.hpp:35
size_t GetMaxDepth() const
Returns the maximum depth of the tree.
Definition Octree-Impl.hpp:251
ClosestIntersectionQueryResult3< T > ClosestIntersection(const Ray3D &ray, const GetRayIntersectionFunc3< T > &testFunc) const override
Returns the closest intersection for given ray.
Definition Octree-Impl.hpp:180
size_t GetChildIndex(size_t nodeIdx, size_t childIdx) const
Returns a child's index for given node.
Definition Octree-Impl.hpp:239
Iterator begin()
Returns the begin iterator of the item.
Definition Octree-Impl.hpp:191
const BoundingBox3D & GetBoundingBox() const
Returns the bounding box of this octree.
Definition Octree-Impl.hpp:245
size_t GetNumberOfNodes() const
Returns the number of octree nodes.
Definition Octree-Impl.hpp:227
size_t GetNumberOfItems() const
Returns the number of items.
Definition Octree-Impl.hpp:215
bool Intersects(const BoundingBox3D &box, const BoxIntersectionTestFunc3< T > &testFunc) const override
Returns true if given box intersects with any of the stored items.
Definition Octree-Impl.hpp:150
const std::vector< size_t > & GetItemsAtNode(size_t nodeIdx) const
Returns the list of the items for given node index.
Definition Octree-Impl.hpp:233
void ForEachIntersectingItem(const BoundingBox3D &box, const BoxIntersectionTestFunc3< T > &testFunc, const IntersectionVisitorFunc< T > &visitorFunc) const override
Invokes visitorFunc for every intersecting items.
Definition Octree-Impl.hpp:164
Iterator end()
Returns the end iterator of the item.
Definition Octree-Impl.hpp:197
Class for N-D ray.
Definition Ray.hpp:26
Definition pybind11Utils.hpp:21
constexpr size_t ZERO_SIZE
Zero size_t.
Definition Constants.hpp:20
Ray3< double > Ray3D
Definition Ray.hpp:77
Matrix< T, Rows, 1 > Vector
Definition Matrix.hpp:738
BoundingBox3< double > BoundingBox3D
Definition BoundingBox.hpp:163