Loading...
Searching...
No Matches
BVH-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_BVH_IMPL_HPP
12#define CUBBYFLOW_BVH_IMPL_HPP
13
14#include <numeric>
15
16namespace CubbyFlow
17{
18template <typename T, size_t N>
19BVH<T, N>::Node::Node() : flags(0)
20{
21 child = std::numeric_limits<size_t>::max();
22}
23
24template <typename T, size_t N>
25void BVH<T, N>::Node::InitLeaf(size_t it, const BoundingBox<double, N>& b)
26{
27 flags = static_cast<char>(N);
28 item = it;
29 bound = b;
30}
31
32template <typename T, size_t N>
33void BVH<T, N>::Node::InitInternal(uint8_t axis, size_t c,
34 const BoundingBox<double, N>& b)
35{
36 flags = axis;
37 child = c;
38 bound = b;
40
41template <typename T, size_t N>
42bool BVH<T, N>::Node::IsLeaf() const
44 return flags == static_cast<char>(N);
45}
46
47template <typename T, size_t N>
49 const ConstArrayView1<T>& items,
51{
52 m_items = items;
53 m_itemBounds = itemsBounds;
54
55 if (m_items.IsEmpty())
56 {
57 return;
58 }
59
60 m_nodes.Clear();
61 m_bound = BoundingBox<double, N>{};
63 for (size_t i = 0; i < m_items.Length(); ++i)
64 {
65 m_bound.Merge(m_itemBounds[i]);
66 }
67
68 Array1<size_t> itemIndices(m_items.Length());
69 std::iota(std::begin(itemIndices), std::end(itemIndices), 0);
70
71 Build(0, itemIndices.data(), m_items.Length(), 0);
72}
73
74template <typename T, size_t N>
76{
77 m_bound = BoundingBox<double, N>{};
78 m_items.Clear();
79 m_itemBounds.Clear();
80 m_nodes.Clear();
81}
83template <typename T, size_t N>
87{
89 best.distance = std::numeric_limits<double>::max();
90 best.item = nullptr;
92 // Prepare to traverse BVH
93 static const int kMaxTreeDepth = 8 * sizeof(size_t);
94 const Node* todo[kMaxTreeDepth];
95 size_t todoPos = 0;
96
97 // Traverse BVH nodes
98 const Node* node = m_nodes.data();
99 while (node != nullptr)
101 if (node->IsLeaf())
102 {
103 if (double dist = distanceFunc(m_items[node->item], pt);
104 dist < best.distance)
105 {
106 best.distance = dist;
107 best.item = &m_items[node->item];
108 }
110 // Grab next node to process from todo stack
111 if (todoPos > 0)
113 // Dequeue
114 --todoPos;
116 }
117 else
118 {
119 break;
120 }
121 }
122 else
123 {
124 const double bestDistSqr = best.distance * best.distance;
125
126 const Node* left = node + 1;
127 const Node* right = &m_nodes[node->child];
128
129 // If pt is inside the box, then the closestLeft and Right will be
130 // identical to pt. This will make distMinLeftSqr and
131 // distMinRightSqr zero, meaning that such a box will have higher
132 // priority.
133 Vector<double, N> closestLeft = left->bound.Clamp(pt);
134 Vector<double, N> closestRight = right->bound.Clamp(pt);
135
136 const double distMinLeftSqr = closestLeft.DistanceSquaredTo(pt);
137 const double distMinRightSqr = closestRight.DistanceSquaredTo(pt);
138
139 const bool shouldVisitLeft = distMinLeftSqr < bestDistSqr;
140 const bool shouldVisitRight = distMinRightSqr < bestDistSqr;
141
142 if (shouldVisitLeft && shouldVisitRight)
143 {
144 const Node* secondChild;
145 const Node* firstChild;
146
147 if (distMinLeftSqr < distMinRightSqr)
148 {
149 firstChild = left;
150 secondChild = right;
151 }
152 else
153 {
154 firstChild = right;
155 secondChild = left;
156 }
157
158 // Enqueue secondChild in todo stack
159 todo[todoPos] = secondChild;
160 ++todoPos;
161 node = firstChild;
162 }
163 else if (shouldVisitLeft)
164 {
165 node = left;
166 }
167 else if (shouldVisitRight)
168 {
169 node = right;
170 }
171 else
172 {
173 if (todoPos > 0)
174 {
175 // Dequeue
176 --todoPos;
177 node = todo[todoPos];
178 }
179 else
180 {
181 break;
182 }
183 }
184 }
185 }
186
187 return best;
188}
189
190template <typename T, size_t N>
193{
194 if (!m_bound.Overlaps(box))
195 {
196 return false;
197 }
198
199 // prepare to traverse BVH for box
200 static const int MAX_TREE_DEPTH = 8 * sizeof(size_t);
201 const Node* todo[MAX_TREE_DEPTH];
202 size_t todoPos = 0;
203
204 // traverse BVH nodes for box
205 const Node* node = m_nodes.data();
206
207 while (node != nullptr)
208 {
209 if (node->IsLeaf())
210 {
211 if (testFunc(m_items[node->item], box))
212 {
213 return true;
214 }
215
216 // grab next node to process from todo stack
217 if (todoPos > 0)
218 {
219 // Dequeue
220 --todoPos;
221 node = todo[todoPos];
222 }
223 else
224 {
225 break;
226 }
227 }
228 else
229 {
230 // get node children pointers for box
231 const Node* firstChild = node + 1;
232 const Node* secondChild = const_cast<Node*>(&m_nodes[node->child]);
233
234 // advance to next child node, possibly enqueue other child
235 if (!firstChild->bound.Overlaps(box))
236 {
238 }
239 else if (!secondChild->bound.Overlaps(box))
240 {
241 node = firstChild;
242 }
243 else
244 {
245 // enqueue secondChild in todo stack
247 ++todoPos;
248 node = firstChild;
249 }
250 }
251 }
252
253 return false;
254}
255
256template <typename T, size_t N>
259{
260 if (!m_bound.Intersects(ray))
261 {
262 return false;
263 }
264
265 // prepare to traverse BVH for ray
266 static const int kMaxTreeDepth = 8 * sizeof(size_t);
267 const Node* todo[kMaxTreeDepth];
268 size_t todoPos = 0;
269
270 // traverse BVH nodes for ray
271 const Node* node = m_nodes.data();
272
273 while (node != nullptr)
274 {
275 if (node->IsLeaf())
276 {
277 if (testFunc(m_items[node->item], ray))
278 {
279 return true;
280 }
281
282 // grab next node to process from todo stack
283 if (todoPos > 0)
284 {
285 // Dequeue
286 --todoPos;
287 node = todo[todoPos];
288 }
289 else
290 {
291 break;
292 }
293 }
294 else
295 {
296 // get node children pointers for ray
297 const Node* firstChild;
298 const Node* secondChild;
299
300 if (ray.direction[node->flags] > 0.0)
301 {
302 firstChild = node + 1;
303 secondChild = const_cast<Node*>(&m_nodes[node->child]);
304 }
305 else
306 {
307 firstChild = const_cast<Node*>(&m_nodes[node->child]);
308 secondChild = node + 1;
309 }
310
311 // advance to next child node, possibly enqueue other child
312 if (!firstChild->bound.Intersects(ray))
313 {
315 }
316 else if (!secondChild->bound.Intersects(ray))
317 {
318 node = firstChild;
319 }
320 else
321 {
322 // enqueue secondChild in todo stack
324 ++todoPos;
325 node = firstChild;
326 }
327 }
328 }
329
330 return false;
331}
332
333template <typename T, size_t N>
338{
339 if (!m_bound.Overlaps(box))
340 {
341 return;
342 }
343
344 // prepare to traverse BVH for box
345 static const int MAX_TREE_DEPTH = 8 * sizeof(size_t);
346 const Node* todo[MAX_TREE_DEPTH];
347 size_t todoPos = 0;
348
349 // traverse BVH nodes for box
350 const Node* node = m_nodes.data();
351
352 while (node != nullptr)
353 {
354 if (node->IsLeaf())
355 {
356 if (testFunc(m_items[node->item], box))
357 {
358 visitorFunc(m_items[node->item]);
359 }
360
361 // grab next node to process from todo stack
362 if (todoPos > 0)
363 {
364 // Dequeue
365 --todoPos;
366 node = todo[todoPos];
367 }
368 else
369 {
370 break;
371 }
372 }
373 else
374 {
375 // get node children pointers for box
376 const Node* firstChild = node + 1;
377 const Node* secondChild = const_cast<Node*>(&m_nodes[node->child]);
378
379 // advance to next child node, possibly enqueue other child
380 if (!firstChild->bound.Overlaps(box))
381 {
383 }
384 else if (!secondChild->bound.Overlaps(box))
385 {
386 node = firstChild;
387 }
388 else
389 {
390 // enqueue secondChild in todo stack
392 ++todoPos;
393 node = firstChild;
394 }
395 }
396 }
397}
398
399template <typename T, size_t N>
403{
404 if (!m_bound.Intersects(ray))
405 {
406 return;
407 }
408
409 // prepare to traverse BVH for ray
410 static const int MAX_TREE_DEPTH = 8 * sizeof(size_t);
411 const Node* todo[MAX_TREE_DEPTH];
412 size_t todoPos = 0;
413
414 // traverse BVH nodes for ray
415 const Node* node = m_nodes.data();
416
417 while (node != nullptr)
418 {
419 if (node->IsLeaf())
420 {
421 if (testFunc(m_items[node->item], ray))
422 {
423 visitorFunc(m_items[node->item]);
424 }
425
426 // grab next node to process from todo stack
427 if (todoPos > 0)
428 {
429 // Dequeue
430 --todoPos;
431 node = todo[todoPos];
432 }
433 else
434 {
435 break;
436 }
437 }
438 else
439 {
440 // get node children pointers for ray
441 const Node* firstChild;
442 const Node* secondChild;
443
444 if (ray.direction[node->flags] > 0.0)
445 {
446 firstChild = node + 1;
447 secondChild = const_cast<Node*>(&m_nodes[node->child]);
448 }
449 else
450 {
451 firstChild = const_cast<Node*>(&m_nodes[node->child]);
452 secondChild = node + 1;
453 }
454
455 // advance to next child node, possibly enqueue other child
456 if (!firstChild->bound.Intersects(ray))
457 {
459 }
460 else if (!secondChild->bound.Intersects(ray))
461 {
462 node = firstChild;
463 }
464 else
465 {
466 // enqueue secondChild in todo stack
468 ++todoPos;
469 node = firstChild;
470 }
471 }
472 }
473}
474
475template <typename T, size_t N>
477 const Ray<double, N>& ray,
479{
481 best.distance = std::numeric_limits<double>::max();
482 best.item = nullptr;
483
484 if (!m_bound.Intersects(ray))
485 {
486 return best;
487 }
488
489 // prepare to traverse BVH for ray
490 static const int MAX_TREE_DEPTH = 8 * sizeof(size_t);
491 const Node* todo[MAX_TREE_DEPTH];
492 size_t todoPos = 0;
493
494 // traverse BVH nodes for ray
495 const Node* node = m_nodes.data();
496
497 while (node != nullptr)
498 {
499 if (node->IsLeaf())
500 {
501 if (double dist = testFunc(m_items[node->item], ray);
502 dist < best.distance)
503 {
504 best.distance = dist;
505 best.item = m_items.data() + node->item;
506 }
507
508 // grab next node to process from todo stack
509 if (todoPos > 0)
510 {
511 // Dequeue
512 --todoPos;
513 node = todo[todoPos];
514 }
515 else
516 {
517 break;
518 }
519 }
520 else
521 {
522 // get node children pointers for ray
523 const Node* firstChild;
524 const Node* secondChild;
525
526 if (ray.direction[node->flags] > 0.0)
527 {
528 firstChild = node + 1;
529 secondChild = const_cast<Node*>(&m_nodes[node->child]);
530 }
531 else
532 {
533 firstChild = const_cast<Node*>(&m_nodes[node->child]);
534 secondChild = node + 1;
535 }
536
537 // advance to next child node, possibly enqueue other child
538 if (!firstChild->bound.Intersects(ray))
539 {
541 }
542 else if (!secondChild->bound.Intersects(ray))
543 {
544 node = firstChild;
545 }
546 else
547 {
548 // enqueue secondChild in todo stack
550 ++todoPos;
551 node = firstChild;
552 }
553 }
554 }
555
556 return best;
557}
558
559template <typename T, size_t N>
561{
562 return m_bound;
563}
564
565template <typename T, size_t N>
567{
568 return m_items.begin();
569}
570
571template <typename T, size_t N>
573{
574 return m_items.end();
575}
576
577template <typename T, size_t N>
579{
580 return m_items.begin();
581}
582
583template <typename T, size_t N>
585{
586 return m_items.end();
587}
588
589template <typename T, size_t N>
591{
592 return m_items.Length();
593}
594
595template <typename T, size_t N>
596const T& BVH<T, N>::Item(size_t i) const
597{
598 return m_items[i];
599}
600
601template <typename T, size_t N>
603{
604 return m_nodes.Length();
605}
606
607template <typename T, size_t N>
608std::pair<size_t, size_t> BVH<T, N>::Children(size_t i) const
609{
610 if (IsLeaf(i))
611 {
612 return std::make_pair(std::numeric_limits<size_t>::max(),
613 std::numeric_limits<size_t>::max());
614 }
615
616 return std::make_pair(i + 1, m_nodes[i].child);
617}
618
619template <typename T, size_t N>
620bool BVH<T, N>::IsLeaf(size_t i) const
621{
622 return m_nodes[i].IsLeaf();
623}
624
625template <typename T, size_t N>
627{
628 return m_nodes[i].bound;
629}
630
631template <typename T, size_t N>
633{
634 if (IsLeaf(i))
635 {
636 return m_nodes[i].item + begin();
637 }
638
639 return end();
640}
641
642template <typename T, size_t N>
644{
645 if (IsLeaf(i))
646 {
647 return m_nodes[i].item + begin();
648 }
649
650 return end();
651}
652
653template <typename T, size_t N>
654size_t BVH<T, N>::Build(size_t nodeIndex, size_t* itemIndices, size_t nItems,
655 size_t currentDepth)
656{
657 // add a node
658 m_nodes.Append(Node{});
659
660 // initialize leaf node if termination criteria met
661 if (nItems == 1)
662 {
663 m_nodes[nodeIndex].InitLeaf(itemIndices[0],
664 m_itemBounds[itemIndices[0]]);
665 return currentDepth + 1;
666 }
667
668 // find the mid-point of the bounding box to use as a qsplit pivot
669 BoundingBox<double, N> nodeBound;
670 for (size_t i = 0; i < nItems; ++i)
671 {
672 nodeBound.Merge(m_itemBounds[itemIndices[i]]);
673 }
674
675 Vector<double, N> d = nodeBound.upperCorner - nodeBound.lowerCorner;
676
677 // choose which axis to split along
678 auto axis = static_cast<uint8_t>(d.DominantAxis());
679
680 const double pivot =
681 0.5 * (nodeBound.upperCorner[axis] + nodeBound.lowerCorner[axis]);
682
683 // classify primitives with respect to split
684 const size_t midPoint = QSplit(itemIndices, nItems, pivot, axis);
685
686 // recursively initialize children _nodes
687 const size_t d0 =
688 Build(nodeIndex + 1, itemIndices, midPoint, currentDepth + 1);
689 m_nodes[nodeIndex].InitInternal(axis, m_nodes.Length(), nodeBound);
690 const size_t d1 = Build(m_nodes[nodeIndex].child, itemIndices + midPoint,
691 nItems - midPoint, currentDepth + 1);
692
693 return std::max(d0, d1);
694}
695
696template <typename T, size_t N>
697size_t BVH<T, N>::QSplit(size_t* itemIndices, size_t numItems, double pivot,
698 uint8_t axis)
699{
700 size_t ret = 0;
701
702 for (size_t i = 0; i < numItems; ++i)
703 {
704 BoundingBox<double, N> b = m_itemBounds[itemIndices[i]];
705
706 if (const double centroid =
707 0.5f * (b.lowerCorner[axis] + b.upperCorner[axis]);
708 centroid < pivot)
709 {
710 std::swap(itemIndices[i], itemIndices[ret]);
711 ret++;
712 }
713 }
714
715 if (ret == 0 || ret == numItems)
716 {
717 ret = numItems >> 1;
718 }
719
720 return ret;
721}
722} // namespace CubbyFlow
723
724#endif
Generic N-dimensional array class interface.
Definition ArrayView.hpp:26
Bounding Volume Hierarchy (BVH) in N-D.
Definition BVH.hpp:32
bool IsLeaf(size_t i) const
Returns true if i-th node is a leaf node.
Definition BVH-Impl.hpp:620
Iterator begin()
Returns the begin Iterator of the item.
Definition BVH-Impl.hpp:566
Iterator end()
Returns the end Iterator of the item.
Definition BVH-Impl.hpp:572
ValueType DistanceSquaredTo(const MatrixExpression< T, R, C, E > &other) const
Returns the squared distance to the other vector.
Definition Matrix.hpp:30
Pointer data()
Definition Matrix-Impl.hpp:298
Definition pybind11Utils.hpp:21