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 std::ranges::for_each(
80 node->items, [this, &distanceFunc, &pt, &best](size_t itemIdx) {
81 const double distance = distanceFunc(m_items[itemIdx], pt);
82
83 if (distance < best.distance)
84 {
85 best.distance = distance;
86 best.item = &m_items[itemIdx];
87 }
88 });
89 }
90 else
91 {
92 using NodeDistBox = std::tuple<const Node*, double, BoundingBox3D>;
93
94 const double bestDistSqr = best.distance * best.distance;
95 std::array<NodeDistBox, 8> childDistSqrPairs;
96 const auto midPoint = bound.MidPoint();
97
98 for (int i = 0; i < 8; ++i)
99 {
100 const Node* child = &m_nodes[node->firstChild + i];
101 const auto childBound =
102 BoundingBox3D(bound.Corner(i), midPoint);
103 Vector3D cp = childBound.Clamp(pt);
105
107 std::make_tuple(child, distMinSqr, childBound);
108 }
109
111 [](const NodeDistBox& a, const NodeDistBox& b) {
112 return std::get<1>(a) > std::get<1>(b);
113 });
114
115 std::ranges::for_each(
118 if (std::get<1>(childPair) < bestDistSqr)
119 {
120 todo.emplace(std::get<0>(childPair),
121 std::get<2>(childPair));
122 }
123 });
124 }
125
126 // Grab next node to process from todo stack
127 if (todo.empty())
128 {
129 break;
130 }
131
132 node = todo.top().first;
133 bound = todo.top().second;
134 todo.pop();
135 }
136
137 return best;
138}
139
140template <typename T>
143{
144 return Intersects(box, testFunc, 0, m_bbox);
145}
146
147template <typename T>
150{
151 return Intersects(ray, testFunc, 0, m_bbox);
152}
153
154template <typename T>
158{
159 ForEachIntersectingItem(box, testFunc, visitorFunc, 0, m_bbox);
160}
161
162template <typename T>
166{
167 ForEachIntersectingItem(ray, testFunc, visitorFunc, 0, m_bbox);
168}
169
170template <typename T>
172 const Ray3D& ray, const GetRayIntersectionFunc3<T>& testFunc) const
173{
175 best.distance = std::numeric_limits<double>::max();
176 best.item = nullptr;
177
178 return ClosestIntersection(ray, testFunc, 0, m_bbox, best);
179}
180
181template <typename T>
183{
184 return m_items.begin();
185}
186
187template <typename T>
189{
190 return m_items.end();
191}
192
193template <typename T>
195{
196 return m_items.begin();
197}
198
199template <typename T>
201{
202 return m_items.end();
203}
204
205template <typename T>
207{
208 return m_items.size();
209}
210
211template <typename T>
212const T& Octree<T>::GetItem(size_t i) const
213{
214 return m_items[i];
215}
216
217template <typename T>
219{
220 return m_nodes.size();
221}
222
223template <typename T>
224const std::vector<size_t>& Octree<T>::GetItemsAtNode(size_t nodeIdx) const
225{
226 return m_nodes[nodeIdx].items;
227}
228
229template <typename T>
230size_t Octree<T>::GetChildIndex(size_t nodeIdx, size_t childIdx) const
231{
232 return m_nodes[nodeIdx].firstChild + childIdx;
233}
234
235template <typename T>
237{
238 return m_bbox;
239}
240
241template <typename T>
243{
244 return m_maxDepth;
245}
246
247template <typename T>
248void Octree<T>::Build(size_t nodeIdx, size_t depth, const BoundingBox3D& bound,
250{
251 if (depth >= m_maxDepth || m_nodes[nodeIdx].items.empty())
252 {
253 return;
254 }
255
256 const size_t firstChild = m_nodes[nodeIdx].firstChild = m_nodes.size();
257 m_nodes.resize(m_nodes[nodeIdx].firstChild + 8);
258
259 BoundingBox3D bboxPerNode[8];
260
261 for (int i = 0; i < 8; ++i)
262 {
263 bboxPerNode[i] = BoundingBox3D{ bound.Corner(i), bound.MidPoint() };
264 }
265
266 auto& currentItems = m_nodes[nodeIdx].items;
267 for (size_t i = 0; i < currentItems.size(); ++i)
268 {
269 size_t currentItem = currentItems[i];
270 for (int j = 0; j < 8; ++j)
271 {
272 if (testFunc(m_items[currentItem], bboxPerNode[j]))
273 {
274 m_nodes[firstChild + j].items.push_back(currentItem);
275 }
276 }
277 }
278
279 // Remove non-leaf data
280 currentItems.clear();
281
282 // Refine
283 for (int i = 0; i < 8; ++i)
284 {
285 Build(firstChild + i, depth + 1, bboxPerNode[i], testFunc);
286 }
287}
288
289template <typename T>
290bool Octree<T>::Intersects(const BoundingBox3D& box,
291 const BoxIntersectionTestFunc3<T>& testFunc,
292 size_t nodeIdx, const BoundingBox3D& bound) const
293{
294 if (!box.Overlaps(bound))
295 {
296 return false;
297 }
298
299 const Node& node = m_nodes[nodeIdx];
300
301 if (!node.items.empty())
302 {
303 for (size_t itemIdx : node.items)
304 {
305 if (testFunc(m_items[itemIdx], box))
306 {
307 return true;
308 }
309 }
310 }
311
312 if (node.firstChild != std::numeric_limits<size_t>::max())
313 {
314 for (int i = 0; i < 8; ++i)
315 {
316 if (Intersects(box, testFunc, node.firstChild + i,
317 BoundingBox3D{ bound.Corner(i), bound.MidPoint() }))
318 {
319 return true;
320 }
321 }
322 }
323
324 return false;
325}
326
327template <typename T>
328bool Octree<T>::Intersects(const Ray3D& ray,
329 const RayIntersectionTestFunc3<T>& testFunc,
330 size_t nodeIdx, const BoundingBox3D& bound) const
331{
332 if (!bound.Intersects(ray))
333 {
334 return false;
335 }
336
337 const Node& node = m_nodes[nodeIdx];
338
339 if (!node.items.empty())
340 {
341 for (size_t itemIdx : node.items)
342 {
343 if (testFunc(m_items[itemIdx], ray))
344 {
345 return true;
346 }
347 }
348 }
349
350 if (node.firstChild != std::numeric_limits<size_t>::max())
351 {
352 for (int i = 0; i < 8; ++i)
353 {
354 if (Intersects(ray, testFunc, node.firstChild + i,
355 BoundingBox3D{ bound.Corner(i), bound.MidPoint() }))
356 {
357 return true;
358 }
359 }
360 }
361
362 return false;
363}
364
365template <typename T>
366void Octree<T>::ForEachIntersectingItem(
367 const BoundingBox3D& box, const BoxIntersectionTestFunc3<T>& testFunc,
368 const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
369 const BoundingBox3D& bound) const
370{
371 if (!box.Overlaps(bound))
372 {
373 return;
374 }
375
376 const Node& node = m_nodes[nodeIdx];
377
378 if (!node.items.empty())
379 {
380 for (size_t itemIdx : node.items)
381 {
382 if (testFunc(m_items[itemIdx], box))
383 {
384 visitorFunc(m_items[itemIdx]);
385 }
386 }
387 }
388
389 if (node.firstChild != std::numeric_limits<size_t>::max())
390 {
391 for (int i = 0; i < 8; ++i)
392 {
393 ForEachIntersectingItem(
394 box, testFunc, visitorFunc, node.firstChild + i,
395 BoundingBox3D{ bound.Corner(i), bound.MidPoint() });
396 }
397 }
398}
399
400template <typename T>
401void Octree<T>::ForEachIntersectingItem(
402 const Ray3D& ray, const RayIntersectionTestFunc3<T>& testFunc,
403 const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
404 const BoundingBox3D& bound) const
405{
406 if (!bound.Intersects(ray))
407 {
408 return;
409 }
410
411 const Node& node = m_nodes[nodeIdx];
412
413 if (!node.items.empty())
414 {
415 for (size_t itemIdx : node.items)
416 {
417 if (testFunc(m_items[itemIdx], ray))
418 {
419 visitorFunc(m_items[itemIdx]);
420 }
421 }
422 }
423
424 if (node.firstChild != std::numeric_limits<size_t>::max())
425 {
426 for (int i = 0; i < 8; ++i)
427 {
428 ForEachIntersectingItem(
429 ray, testFunc, visitorFunc, node.firstChild + i,
430 BoundingBox3D{ bound.Corner(i), bound.MidPoint() });
431 }
432 }
433}
434
435template <typename T>
436ClosestIntersectionQueryResult3<T> Octree<T>::ClosestIntersection(
437 const Ray3D& ray, const GetRayIntersectionFunc3<T>& testFunc,
438 size_t nodeIdx, const BoundingBox3D& bound,
439 ClosestIntersectionQueryResult3<T> best) const
440{
441 if (!bound.Intersects(ray))
442 {
443 return best;
444 }
445
446 const Node& node = m_nodes[nodeIdx];
447
448 if (!node.items.empty())
449 {
450 for (size_t itemIdx : node.items)
451 {
452 double dist = testFunc(m_items[itemIdx], ray);
453 if (dist < best.distance)
454 {
455 best.distance = dist;
456 best.item = &m_items[itemIdx];
457 }
458 }
459 }
460
461 if (node.firstChild != std::numeric_limits<size_t>::max())
462 {
463 for (int i = 0; i < 8; ++i)
464 {
465 best = ClosestIntersection(
466 ray, testFunc, node.firstChild + i,
467 BoundingBox3D{ bound.Corner(i), bound.MidPoint() }, best);
468 }
469 }
470
471 return best;
472}
473} // namespace CubbyFlow
474
475#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:189
VectorType Corner(size_t idx) const
Returns corner position. Index starts from x-first order.
Definition BoundingBox-Impl.hpp:235
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
Generic octree data structure.
Definition Octree.hpp:31
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
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
Iterator begin()
Returns the begin iterator of the item.
Definition Octree-Impl.hpp:182
Iterator end()
Returns the end iterator of the item.
Definition Octree-Impl.hpp:188
Class for N-D ray.
Definition Ray.hpp:26
Definition pybind11Utils.hpp:22
constexpr size_t ZERO_SIZE
Zero size_t.
Definition Constants.hpp:20
Matrix< T, Rows, 1 > Vector
Definition Matrix.hpp:719
BoundingBox3< double > BoundingBox3D
Definition BoundingBox.hpp:163