BVH3-Impl.h
Go to the documentation of this file.
1 /*************************************************************************
2 > File Name: BVH3-Impl.h
3 > Project Name: CubbyFlow
4 > Author: Chan-Ho Chris Ohk
5 > Purpose: Bounding Volume Hierarchy (BVH) in 3D.
6 > Created Time: 2017/10/17
7 > Copyright (c) 2018, Chan-Ho Chris Ohk
8 *************************************************************************/
9 #ifndef CUBBYFLOW_BVH3_IMPL_H
10 #define CUBBYFLOW_BVH3_IMPL_H
11 
12 #include <numeric>
13 
14 namespace CubbyFlow
15 {
16  template <typename T>
17  BVH3<T>::Node::Node() : flags(0)
18  {
19  child = std::numeric_limits<size_t>::max();
20  }
21 
22  template <typename T>
23  void BVH3<T>::Node::InitLeaf(size_t it, const BoundingBox3D& b)
24  {
25  flags = 3;
26  item = it;
27  bound = b;
28  }
29 
30  template <typename T>
31  void BVH3<T>::Node::InitInternal(uint8_t axis, size_t c, const BoundingBox3D& b)
32  {
33  flags = axis;
34  child = c;
35  bound = b;
36  }
37 
38  template <typename T>
39  bool BVH3<T>::Node::IsLeaf() const
40  {
41  return flags == 3;
42  }
43 
44  template <typename T>
46  {
47  // Do nothing
48  }
49 
50  template <typename T>
51  void BVH3<T>::Build(const std::vector<T>& items,
52  const std::vector<BoundingBox3D>& itemsBounds)
53  {
54  m_items = items;
55  m_itemBounds = itemsBounds;
56 
57  if (m_items.empty())
58  {
59  return;
60  }
61 
62  m_nodes.clear();
63 
64  for (size_t i = 0; i < m_items.size(); ++i)
65  {
66  m_bound.Merge(m_itemBounds[i]);
67  }
68 
69  std::vector<size_t> itemIndices(m_items.size());
70  std::iota(std::begin(itemIndices), std::end(itemIndices), 0);
71 
72  Build(0, itemIndices.data(), m_items.size(), 0);
73  }
74 
75  template <typename T>
77  {
78  m_bound = BoundingBox3D();
79  m_items.clear();
80  m_itemBounds.clear();
81  m_nodes.clear();
82  }
83 
84  template <typename T>
86  const Vector3D& pt,
87  const NearestNeighborDistanceFunc3<T>& distanceFunc) const
88  {
90  best.distance = std::numeric_limits<double>::max();
91  best.item = nullptr;
92 
93  // Prepare to traverse BVH
94  static const int maxTreeDepth = 8 * sizeof(size_t);
95  const Node* todo[maxTreeDepth];
96  size_t todoPos = 0;
97 
98  // Traverse BVH nodes
99  const Node* node = m_nodes.data();
100  while (node != nullptr)
101  {
102  if (node->IsLeaf())
103  {
104  double dist = distanceFunc(m_items[node->item], pt);
105  if (dist < best.distance)
106  {
107  best.distance = dist;
108  best.item = &m_items[node->item];
109  }
110 
111  // Grab next node to process from todo stack
112  if (todoPos > 0)
113  {
114  // Dequeue
115  --todoPos;
116  node = todo[todoPos];
117  }
118  else
119  {
120  break;
121  }
122  }
123  else
124  {
125  const double bestDistSqr = best.distance * best.distance;
126 
127  const Node* left = node + 1;
128  const Node* right = &m_nodes[node->child];
129 
130  // If pt is inside the box, then the closestLeft and Right will be
131  // identical to pt. This will make distMinLeftSqr and
132  // distMinRightSqr zero, meaning that such a box will have higher
133  // priority.
134  Vector3D closestLeft = left->bound.Clamp(pt);
135  Vector3D closestRight = right->bound.Clamp(pt);
136 
137  double distMinLeftSqr = closestLeft.DistanceSquaredTo(pt);
138  double distMinRightSqr = closestRight.DistanceSquaredTo(pt);
139 
140  bool shouldVisitLeft = distMinLeftSqr < bestDistSqr;
141  bool shouldVisitRight = distMinRightSqr < bestDistSqr;
142 
143  const Node* firstChild;
144  const Node* secondChild;
145 
146  if (shouldVisitLeft && shouldVisitRight)
147  {
148  if (distMinLeftSqr < distMinRightSqr)
149  {
150  firstChild = left;
151  secondChild = right;
152  }
153  else
154  {
155  firstChild = right;
156  secondChild = left;
157  }
158 
159  // Enqueue secondChild in todo stack
160  todo[todoPos] = secondChild;
161  ++todoPos;
162  node = firstChild;
163  }
164  else if (shouldVisitLeft)
165  {
166  node = left;
167  }
168  else if (shouldVisitRight)
169  {
170  node = right;
171  }
172  else
173  {
174  if (todoPos > 0)
175  {
176  // Dequeue
177  --todoPos;
178  node = todo[todoPos];
179  }
180  else
181  {
182  break;
183  }
184  }
185  }
186  }
187 
188  return best;
189  }
190 
191  template <typename T>
192  inline bool BVH3<T>::IsIntersects(const BoundingBox3D& box,
193  const BoxIntersectionTestFunc3<T>& testFunc) const
194  {
195  if (!m_bound.Overlaps(box))
196  {
197  return false;
198  }
199 
200  // prepare to traverse BVH for box
201  static const int maxTreeDepth = 8 * sizeof(size_t);
202  const Node* todo[maxTreeDepth];
203  size_t todoPos = 0;
204 
205  // traverse BVH nodes for box
206  const Node* node = m_nodes.data();
207 
208  while (node != nullptr)
209  {
210  if (node->IsLeaf())
211  {
212  if (testFunc(m_items[node->item], box))
213  {
214  return true;
215  }
216 
217  // grab next node to process from todo stack
218  if (todoPos > 0)
219  {
220  // Dequeue
221  --todoPos;
222  node = todo[todoPos];
223  }
224  else
225  {
226  break;
227  }
228  }
229  else
230  {
231  // get node children pointers for box
232  const Node* firstChild = node + 1;
233  const Node* secondChild = const_cast<Node*>(&m_nodes[node->child]);
234 
235  // advance to next child node, possibly enqueue other child
236  if (!firstChild->bound.Overlaps(box))
237  {
238  node = secondChild;
239  }
240  else if (!secondChild->bound.Overlaps(box))
241  {
242  node = firstChild;
243  }
244  else
245  {
246  // enqueue secondChild in todo stack
247  todo[todoPos] = secondChild;
248  ++todoPos;
249  node = firstChild;
250  }
251  }
252  }
253 
254  return false;
255  }
256 
257  template <typename T>
258  inline bool BVH3<T>::IsIntersects(const Ray3D& ray,
259  const RayIntersectionTestFunc3<T>& testFunc) const
260  {
261  if (!m_bound.Intersects(ray))
262  {
263  return false;
264  }
265 
266  // prepare to traverse BVH for ray
267  static const int maxTreeDepth = 8 * sizeof(size_t);
268  const Node* todo[maxTreeDepth];
269  size_t todoPos = 0;
270 
271  // traverse BVH nodes for ray
272  const Node* node = m_nodes.data();
273 
274  while (node != nullptr)
275  {
276  if (node->IsLeaf())
277  {
278  if (testFunc(m_items[node->item], ray))
279  {
280  return true;
281  }
282 
283  // grab next node to process from todo stack
284  if (todoPos > 0)
285  {
286  // Dequeue
287  --todoPos;
288  node = todo[todoPos];
289  }
290  else
291  {
292  break;
293  }
294  }
295  else
296  {
297  // get node children pointers for ray
298  const Node* firstChild;
299  const Node* secondChild;
300 
301  if (ray.direction[node->flags] > 0.0)
302  {
303  firstChild = node + 1;
304  secondChild = const_cast<Node*>(&m_nodes[node->child]);
305  }
306  else
307  {
308  firstChild = const_cast<Node*>(&m_nodes[node->child]);
309  secondChild = node + 1;
310  }
311 
312  // advance to next child node, possibly enqueue other child
313  if (!firstChild->bound.Intersects(ray))
314  {
315  node = secondChild;
316  }
317  else if (!secondChild->bound.Intersects(ray))
318  {
319  node = firstChild;
320  }
321  else
322  {
323  // enqueue secondChild in todo stack
324  todo[todoPos] = secondChild;
325  ++todoPos;
326  node = firstChild;
327  }
328  }
329  }
330 
331  return false;
332  }
333 
334  template <typename T>
336  const BoundingBox3D& box, const BoxIntersectionTestFunc3<T>& testFunc,
337  const IntersectionVisitorFunc3<T>& visitorFunc) const
338  {
339  if (!m_bound.Overlaps(box))
340  {
341  return;
342  }
343 
344  // prepare to traverse BVH for box
345  static const int maxTreeDepth = 8 * sizeof(size_t);
346  const Node* todo[maxTreeDepth];
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  {
382  node = secondChild;
383  }
384  else if (!secondChild->bound.Overlaps(box))
385  {
386  node = firstChild;
387  }
388  else
389  {
390  // enqueue secondChild in todo stack
391  todo[todoPos] = secondChild;
392  ++todoPos;
393  node = firstChild;
394  }
395  }
396  }
397  }
398 
399  template <typename T>
401  const Ray3D& ray, const RayIntersectionTestFunc3<T>& testFunc,
402  const IntersectionVisitorFunc3<T>& visitorFunc) const
403  {
404  if (!m_bound.Intersects(ray))
405  {
406  return;
407  }
408 
409  // prepare to traverse BVH for ray
410  static const int maxTreeDepth = 8 * sizeof(size_t);
411  const Node* todo[maxTreeDepth];
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  {
458  node = secondChild;
459  }
460  else if (!secondChild->bound.Intersects(ray))
461  {
462  node = firstChild;
463  }
464  else
465  {
466  // enqueue secondChild in todo stack
467  todo[todoPos] = secondChild;
468  ++todoPos;
469  node = firstChild;
470  }
471  }
472  }
473  }
474 
475  template <typename T>
477  const Ray3D& ray, const GetRayIntersectionFunc3<T>& testFunc) const
478  {
480  best.distance = std::numeric_limits<double>::max();
481  best.item = nullptr;
482 
483  if (!m_bound.Intersects(ray))
484  {
485  return best;
486  }
487 
488  // prepare to traverse BVH for ray
489  static const int maxTreeDepth = 8 * sizeof(size_t);
490  const Node* todo[maxTreeDepth];
491  size_t todoPos = 0;
492 
493  // traverse BVH nodes for ray
494  const Node* node = m_nodes.data();
495 
496  while (node != nullptr)
497  {
498  if (node->IsLeaf())
499  {
500  double dist = testFunc(m_items[node->item], ray);
501  if (dist < best.distance)
502  {
503  best.distance = dist;
504  best.item = m_items.data() + node->item;
505  }
506 
507  // grab next node to process from todo stack
508  if (todoPos > 0)
509  {
510  // Dequeue
511  --todoPos;
512  node = todo[todoPos];
513  }
514  else
515  {
516  break;
517  }
518  }
519  else
520  {
521  // get node children pointers for ray
522  const Node* firstChild;
523  const Node* secondChild;
524 
525  if (ray.direction[node->flags] > 0.0)
526  {
527  firstChild = node + 1;
528  secondChild = const_cast<Node*>(&m_nodes[node->child]);
529  }
530  else
531  {
532  firstChild = const_cast<Node*>(&m_nodes[node->child]);
533  secondChild = node + 1;
534  }
535 
536  // advance to next child node, possibly enqueue other child
537  if (!firstChild->bound.Intersects(ray))
538  {
539  node = secondChild;
540  }
541  else if (!secondChild->bound.Intersects(ray))
542  {
543  node = firstChild;
544  }
545  else
546  {
547  // enqueue secondChild in todo stack
548  todo[todoPos] = secondChild;
549  ++todoPos;
550  node = firstChild;
551  }
552  }
553  }
554 
555  return best;
556  }
557 
558  template <typename T>
560  {
561  return m_bound;
562  }
563 
564  template <typename T>
566  {
567  return m_items.begin();
568  }
569 
570  template <typename T>
572  {
573  return m_items.end();
574  }
575 
576  template <typename T>
578  {
579  return m_items.begin();
580  }
581 
582  template <typename T>
584  {
585  return m_items.end();
586  }
587 
588  template <typename T>
590  {
591  return m_items.size();
592  }
593 
594  template <typename T>
595  const T& BVH3<T>::GetItem(size_t i) const
596  {
597  return m_items[i];
598  }
599 
600  template <typename T>
601  size_t BVH3<T>::Build(size_t nodeIndex, size_t* itemIndices, size_t nItems, size_t currentDepth)
602  {
603  // add a node
604  m_nodes.push_back(Node());
605 
606  // Initialize leaf node if termination criteria met
607  if (nItems == 1)
608  {
609  m_nodes[nodeIndex].InitLeaf(itemIndices[0], m_itemBounds[itemIndices[0]]);
610  return currentDepth + 1;
611  }
612 
613  // find the mid-point of the bounding box to use as a qsplit pivot
614  BoundingBox3D nodeBound;
615  for (size_t i = 0; i < nItems; ++i)
616  {
617  nodeBound.Merge(m_itemBounds[itemIndices[i]]);
618  }
619 
620  Vector3D d = nodeBound.upperCorner - nodeBound.lowerCorner;
621 
622  // choose which axis to split along
623  uint8_t axis;
624  if (d.x > d.y && d.x > d.z)
625  {
626  axis = 0;
627  }
628  else
629  {
630  axis = (d.y > d.z) ? 1 : 2;
631  }
632 
633  double pivot = 0.5 * (nodeBound.upperCorner[axis] + nodeBound.lowerCorner[axis]);
634 
635  // classify primitives with respect to split
636  size_t midPoint = QSplit(itemIndices, nItems, pivot, axis);
637 
638  // recursively Initialize children m_nodes
639  size_t d0 = Build(nodeIndex + 1, itemIndices, midPoint, currentDepth + 1);
640  m_nodes[nodeIndex].InitInternal(axis, m_nodes.size(), nodeBound);
641  size_t d1 = Build(m_nodes[nodeIndex].child, itemIndices + midPoint, nItems - midPoint, currentDepth + 1);
642 
643  return std::max(d0, d1);
644  }
645 
646  template <typename T>
647  size_t BVH3<T>::QSplit(size_t* itemIndices, size_t numItems, double pivot, uint8_t axis)
648  {
649  size_t ret = 0;
650 
651  for (size_t i = 0; i < numItems; ++i)
652  {
653  BoundingBox3D b = m_itemBounds[itemIndices[i]];
654  double centroid = 0.5f * (b.lowerCorner[axis] + b.upperCorner[axis]);
655 
656  if (centroid < pivot)
657  {
658  std::swap(itemIndices[i], itemIndices[ret]);
659  ret++;
660  }
661  }
662 
663  if (ret == 0 || ret == numItems)
664  {
665  ret = numItems >> 1;
666  }
667 
668  return ret;
669  }
670 }
671 
672 #endif
3-D vector class.
Definition: Vector3.h:26
Bounding Volume Hierarchy (BVH) in 3D.
Definition: BVH3.h:26
typename ContainerType::iterator Iterator
Definition: BVH3.h:30
Vector3< T > direction
The direction of the ray.
Definition: Ray3.h:32
const T * item
Definition: NearestNeighborQueryEngine3.h:20
Iterator begin()
Returns the begin iterator of the item.
Definition: BVH3-Impl.h:565
std::function< bool(const T &, const BoundingBox3D &)> BoxIntersectionTestFunc3
Box-item intersection test function.
Definition: IntersectionQueryEngine3.h:31
T DistanceSquaredTo(const Vector &other) const
Returns the squared distance to the other vector.
Definition: Vector3-Impl.h:332
const T * item
Definition: IntersectionQueryEngine3.h:21
BoundingBox3< double > BoundingBox3D
Double-type 3-D BoundingBox.
Definition: BoundingBox3.h:129
std::function< double(const T &, const Ray3D &)> GetRayIntersectionFunc3
Ray-item closest intersection evaluation function.
Definition: IntersectionQueryEngine3.h:39
double distance
Definition: IntersectionQueryEngine3.h:22
Definition: pybind11Utils.h:24
3-D axis-aligned bounding box class.
Definition: BoundingBox3.h:44
Closest intersection query result.
Definition: IntersectionQueryEngine3.h:19
Iterator end()
Returns the end iterator of the item.
Definition: BVH3-Impl.h:571
double distance
Definition: NearestNeighborQueryEngine3.h:21
std::function< void(const T &)> IntersectionVisitorFunc3
Visitor function which is invoked for each intersecting item.
Definition: IntersectionQueryEngine3.h:43
std::function< bool(const T &, const Ray3D &)> RayIntersectionTestFunc3
Ray-item intersection test function.
Definition: IntersectionQueryEngine3.h:35
typename ContainerType::const_iterator ConstIterator
Definition: BVH3.h:31
Nearest neighbor query result.
Definition: NearestNeighborQueryEngine3.h:18
Class for 3-D ray.
Definition: Ray3.h:23
Vector3< double > Vector3D
Double-type 3D vector.
Definition: Vector3.h:353
std::function< double(const T &, const Vector3D &)> NearestNeighborDistanceFunc3
Nearest neighbor distance measure function.
Definition: NearestNeighborQueryEngine3.h:26