EBGeometry  1.0
EBGeometry_BVH.hpp
Go to the documentation of this file.
1 /* EBGeometry
2  * Copyright © 2022 Robert Marskar
3  * Please refer to Copyright.txt and LICENSE in the EBGeometry root directory.
4  */
5 
12 #ifndef EBGeometry_BVH
13 #define EBGeometry_BVH
14 
15 // Std includes
16 #include <memory>
17 #include <vector>
18 #include <array>
19 #include <functional>
20 #include <queue>
21 
22 // Our includes
23 #include "EBGeometry_Vec.hpp"
24 #include "EBGeometry_SFC.hpp"
26 
30 namespace BVH {
31 
35  enum class Build
36  {
37  TopDown,
38  Morton,
39  Nested
40  };
41 
48  template <class T, class P, class BV, size_t K>
49  class NodeT;
50 
56  template <class T, class P, class BV, size_t K>
57  class LinearNodeT;
58 
64  template <class T, class P, class BV, size_t K>
65  class LinearBVH;
66 
71  template <class P>
72  using PrimitiveListT = std::vector<std::shared_ptr<const P>>;
73 
77  template <class P, class BV>
78  using PrimAndBV = std::pair<std::shared_ptr<const P>, BV>;
79 
84  template <class P, class BV>
85  using PrimAndBVListT = std::vector<PrimAndBV<P, BV>>;
86 
93  template <class P, class BV, size_t K>
94  using PartitionerT = std::function<std::array<PrimAndBVListT<P, BV>, K>(const PrimAndBVListT<P, BV>& a_primsAndBVs)>;
95 
104  template <class T, class P, class BV, size_t K>
105  using StopFunctionT = std::function<bool(const NodeT<T, P, BV, K>& a_node)>;
106 
111  template <class P>
112  using Updater = std::function<void(const PrimitiveListT<P>& a_primitives)>;
113 
121  template <class NodeType, class Meta>
122  using Visiter = std::function<bool(const NodeType& a_node, const Meta& a_meta)>;
123 
130  template <class NodeType, class Meta, size_t K>
131  using Sorter = std::function<void(std::array<std::pair<std::shared_ptr<const NodeType>, Meta>, K>& a_children)>;
132 
136  template <class NodeType, class Meta>
137  using MetaUpdater = std::function<Meta(const NodeType& a_node)>;
138 
142  template <class X, size_t K>
143  auto equalCounts = [](const std::vector<X>& a_primitives) noexcept -> std::array<std::vector<X>, K> {
144  int length = a_primitives.size() / K;
145  int remain = a_primitives.size() % K;
146 
147  int begin = 0;
148  int end = 0;
149 
150  std::array<std::vector<X>, K> chunks;
151 
152  for (size_t k = 0; k < K; k++) {
153  end += (remain > 0) ? length + 1 : length;
154  remain--;
155 
156  chunks[k] = std::vector<X>(a_primitives.begin() + begin, a_primitives.begin() + end);
157 
158  begin = end;
159  }
160 
161  return chunks;
162  };
163 
168  template <class T, class P, class BV, size_t K>
170  [](const PrimAndBVListT<P, BV>& a_primsAndBVs) noexcept -> std::array<PrimAndBVListT<P, BV>, K> {
171  Vec3T<T> lo = Vec3T<T>::max();
172  Vec3T<T> hi = -Vec3T<T>::max();
173 
174  for (const auto& pbv : a_primsAndBVs) {
175  lo = min(lo, pbv.first->getCentroid());
176  hi = max(hi, pbv.first->getCentroid());
177  }
178 
179  const size_t splitDir = (hi - lo).maxDir(true);
180 
181  // Sort the primitives along the above coordinate direction.
182  PrimAndBVListT<P, BV> sortedPrimsAndBVs(a_primsAndBVs);
183 
184  std::sort(sortedPrimsAndBVs.begin(),
185  sortedPrimsAndBVs.end(),
186  [splitDir](const PrimAndBV<P, BV>& pbv1, const PrimAndBV<P, BV>& pbv2) -> bool {
187  return pbv1.first->getCentroid(splitDir) < pbv2.first->getCentroid(splitDir);
188  });
189 
190  return BVH::equalCounts<PrimAndBV<P, BV>, K>(sortedPrimsAndBVs);
191  };
192 
197  template <class T, class P, class BV, size_t K>
198  auto BVCentroidPartitioner = [](const PrimAndBVListT<P, BV>& a_primsAndBVs) -> std::array<PrimAndBVListT<P, BV>, K> {
199  Vec3T<T> lo = Vec3T<T>::max();
200  Vec3T<T> hi = -Vec3T<T>::max();
201 
202  for (const auto& pbv : a_primsAndBVs) {
203  lo = min(lo, pbv.second.getCentroid());
204  hi = max(hi, pbv.second.getCentroid());
205  }
206 
207  const size_t splitDir = (hi - lo).maxDir(true);
208 
209  // Sort the primitives along the above coordinate direction.
210  PrimAndBVListT<P, BV> sortedPrimsAndBVs(a_primsAndBVs);
211 
212  std::sort(sortedPrimsAndBVs.begin(),
213  sortedPrimsAndBVs.end(),
214  [splitDir](const PrimAndBV<P, BV>& pbv1, const PrimAndBV<P, BV>& pbv2) -> bool {
215  return pbv1.second.getCentroid()[splitDir] < pbv2.second.getCentroid()[splitDir];
216  });
217 
218  return BVH::equalCounts<PrimAndBV<P, BV>, K>(sortedPrimsAndBVs);
219  };
220 
225  template <class T, class P, class BV, size_t K>
227  [](const BVH::NodeT<T, P, BV, K>& a_node) noexcept -> bool { return (a_node.getPrimitives()).size() < K; };
228 
236  template <class T, class P, class BV, size_t K>
237  class NodeT : public std::enable_shared_from_this<NodeT<T, P, BV, K>>
238  {
239  public:
244 
248  using Vec3 = Vec3T<T>;
249 
254 
258  using NodePtr = std::shared_ptr<Node>;
259 
264 
269 
273  NodeT() noexcept;
274 
279  NodeT(const std::vector<PrimAndBV<P, BV>>& a_primsAndBVs) noexcept;
280 
284  virtual ~NodeT() noexcept;
285 
294  inline void
295  topDownSortAndPartition(const Partitioner& a_partitioner = BVCentroidPartitioner<T, P, BV, K>,
296  const StopFunction& a_stopCrit = DefaultStopFunction<T, P, BV, K>) noexcept;
297 
307  template <typename S>
308  inline void
310 
314  inline bool
315  isLeaf() const noexcept;
316 
320  inline bool
321  isPartitioned() const noexcept;
322 
327  inline const BV&
328  getBoundingVolume() const noexcept;
329 
334  inline const PrimitiveList&
335  getPrimitives() const noexcept;
336 
341  inline const std::vector<BV>&
342  getBoundingVolumes() const noexcept;
343 
350  inline T
351  getDistanceToBoundingVolume(const Vec3& a_point) const noexcept;
352 
357  inline const std::array<std::shared_ptr<NodeT<T, P, BV, K>>, K>&
358  getChildren() const noexcept;
359 
368  template <class Meta>
369  inline void
370  traverse(const BVH::Updater<P>& a_updater,
371  const BVH::Visiter<Node, Meta>& a_visiter,
372  const BVH::Sorter<Node, Meta, K>& a_sorter,
373  const BVH::MetaUpdater<Node, Meta>& a_metaUpdater) const noexcept;
374 
381  inline std::shared_ptr<LinearBVH<T, P, BV, K>>
382  flattenTree() const noexcept;
383 
384  protected:
389 
394 
398  std::vector<std::shared_ptr<const P>> m_primitives;
399 
403  std::vector<BV> m_boundingVolumes;
404 
408  std::array<std::shared_ptr<NodeT<T, P, BV, K>>, K> m_children;
409 
414  inline PrimitiveList&
415  getPrimitives() noexcept;
416 
421  inline std::vector<BV>&
422  getBoundingVolumes() noexcept;
423 
429  inline void
430  setChildren(const std::array<std::shared_ptr<NodeT<T, P, BV, K>>, K>& a_children) noexcept;
431 
444  inline size_t
445  flattenTree(std::vector<std::shared_ptr<LinearNodeT<T, P, BV, K>>>& a_linearNodes,
446  std::vector<std::shared_ptr<const P>>& a_sortedPrimitives,
447  size_t& a_offset) const noexcept;
448  };
449 
480  template <class T, class P, class BV, size_t K>
482  {
483  public:
487  using Vec3 = Vec3T<T>;
488 
492  inline LinearNodeT() noexcept;
493 
497  inline virtual ~LinearNodeT();
498 
503  inline void
504  setBoundingVolume(const BV& a_boundingVolume) noexcept;
505 
509  inline void
510  setPrimitivesOffset(const size_t a_primitivesOffset) noexcept;
511 
516  inline void
517  setNumPrimitives(const size_t a_numPrimitives) noexcept;
518 
524  inline void
525  setChildOffset(const size_t a_childOffset, const size_t a_whichChild) noexcept;
526 
531  inline const BV&
532  getBoundingVolume() const noexcept;
533 
538  inline const size_t&
539  getPrimitivesOffset() const noexcept;
540 
545  inline const size_t&
546  getNumPrimitives() const noexcept;
547 
552  inline const std::array<size_t, K>&
553  getChildOffsets() const noexcept;
554 
558  inline bool
559  isLeaf() const noexcept;
560 
564  inline bool
565  isPartitioned() const noexcept;
566 
573  inline T
574  getDistanceToBoundingVolume(const Vec3& a_point) const noexcept;
575 
582  inline std::vector<T>
583  getDistances(const Vec3& a_point, const std::vector<std::shared_ptr<const P>>& a_primitives) const noexcept;
584 
585  protected:
590 
595 
599  size_t m_primitivesOffset;
600 
604  size_t m_numPrimitives;
605 
609  std::array<size_t, K> m_childOffsets;
610  };
611 
615  template <class T, class P, class BV, size_t K>
616  class LinearBVH
617  {
618  public:
622  using Vec3 = Vec3T<T>;
623 
628 
632  using PrimitiveList = std::vector<std::shared_ptr<const P>>;
633 
637  inline LinearBVH() = default;
638 
642  inline LinearBVH(const LinearBVH&) = default;
643 
649  inline LinearBVH(const std::vector<std::shared_ptr<const LinearNodeT<T, P, BV, K>>>& a_linearNodes,
650  const std::vector<std::shared_ptr<const P>>& a_primitives);
651 
657  inline LinearBVH(const std::vector<std::shared_ptr<LinearNodeT<T, P, BV, K>>>& a_linearNodes,
658  const std::vector<std::shared_ptr<const P>>& a_primitives);
659 
663  inline virtual ~LinearBVH();
664 
668  inline const BV&
669  getBoundingVolume() const noexcept;
670 
679  template <class Meta>
680  inline void
681  traverse(const BVH::Updater<P>& a_updater,
682  const BVH::Visiter<LinearNode, Meta>& a_visiter,
683  const BVH::Sorter<LinearNode, Meta, K>& a_sorter,
684  const BVH::MetaUpdater<LinearNode, Meta>& a_metaUpdater) const noexcept;
685 
686  protected:
690  std::vector<std::shared_ptr<const LinearNodeT<T, P, BV, K>>> m_linearNodes;
691 
696  std::vector<std::shared_ptr<const P>> m_primitives;
697  };
698 } // namespace BVH
699 
700 #include "EBGeometry_NamespaceFooter.hpp"
701 
702 #include "EBGeometry_BVHImplem.hpp"
703 
704 #endif
Declaration of various space-filling curves.
Declaration of 2D and 3D point/vector classes with templated precision. Used with DCEL tools.
Vec2T< T > max(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Maximum function. Returns new vector with component-wise minimums.
T length(const Vec2T< T > &v) noexcept
Length function.
Vec2T< T > min(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Minimum function. Returns new vector with component-wise minimums.
Forward declare linear BVH class.
Definition: EBGeometry_BVH.hpp:617
LinearBVH()=default
Disallowed. Use the full constructor please.
LinearBVH(const LinearBVH &)=default
Copy constructor.
const BV & getBoundingVolume() const noexcept
Get the bounding volume for this BVH.
std::vector< std::shared_ptr< const P > > PrimitiveList
Alias for list of primitives.
Definition: EBGeometry_BVH.hpp:632
virtual ~LinearBVH()
Destructor. Does nothing.
LinearBVH(const std::vector< std::shared_ptr< const LinearNodeT< T, P, BV, K >>> &a_linearNodes, const std::vector< std::shared_ptr< const P >> &a_primitives)
Full constructor. Associates the nodes and primitives.
LinearBVH(const std::vector< std::shared_ptr< LinearNodeT< T, P, BV, K >>> &a_linearNodes, const std::vector< std::shared_ptr< const P >> &a_primitives)
Full constructor. Associates the nodes and primitives.
Forward declare linear node class.
Definition: EBGeometry_BVH.hpp:482
LinearNodeT() noexcept
Constructor.
Forward declare the BVH node since it is needed for the polymorphic lambdas.
Definition: EBGeometry_BVH.hpp:238
std::shared_ptr< Node > NodePtr
Alias for node type pointer.
Definition: EBGeometry_BVH.hpp:258
PrimitiveListT< P > PrimitiveList
Alias for list of primitives.
Definition: EBGeometry_BVH.hpp:243
std::vector< std::shared_ptr< const P > > m_primitives
Primitives list. This will be empty for regular nodes.
Definition: EBGeometry_BVH.hpp:398
const std::vector< BV > & getBoundingVolumes() const noexcept
Get the bounding volumes for the primitives in this node (can be empty if regular node)
void setChildren(const std::array< std::shared_ptr< NodeT< T, P, BV, K >>, K > &a_children) noexcept
Explicitly set this node's children.
const BV & getBoundingVolume() const noexcept
Get bounding volume.
std::shared_ptr< LinearBVH< T, P, BV, K > > flattenTree() const noexcept
Flatten everything beneath this node into a depth-first sorted BVH hierarchy.
bool isLeaf() const noexcept
Get node type.
bool m_partitioned
Determines whether or not the partitioning function has already been called.
Definition: EBGeometry_BVH.hpp:393
NodeT() noexcept
Default constructor which sets a regular node.
BV m_boundingVolume
Bounding volume object for enclosing everything in this node.
Definition: EBGeometry_BVH.hpp:388
const PrimitiveList & getPrimitives() const noexcept
Get the primitives stored in this node.
T getDistanceToBoundingVolume(const Vec3 &a_point) const noexcept
Get the distance from a 3D point to the bounding volume.
void topDownSortAndPartition(const Partitioner &a_partitioner=BVCentroidPartitioner< T, P, BV, K >, const StopFunction &a_stopCrit=DefaultStopFunction< T, P, BV, K >) noexcept
Function for using top-down construction of the bounding volume hierarchy.
std::vector< BV > m_boundingVolumes
List of bounding volumes for the primitives. This will be empty for regular nodes.
Definition: EBGeometry_BVH.hpp:403
PartitionerT< P, BV, K > Partitioner
Alias for partitioner.
Definition: EBGeometry_BVH.hpp:263
bool isPartitioned() const noexcept
Check if BVH is already partitioned.
void bottomUpSortAndPartition() noexcept
Function for doing bottom-up construction using a specified space-filling curve.
StopFunctionT< T, P, BV, K > StopFunction
Alias for stop function.
Definition: EBGeometry_BVH.hpp:268
const std::array< std::shared_ptr< NodeT< T, P, BV, K > >, K > & getChildren() const noexcept
Return this node's children.
void traverse(const BVH::Updater< P > &a_updater, const BVH::Visiter< Node, Meta > &a_visiter, const BVH::Sorter< Node, Meta, K > &a_sorter, const BVH::MetaUpdater< Node, Meta > &a_metaUpdater) const noexcept
Recursion-less BVH traversal algorithm. The user inputs the update rule, a pruning criterion,...
std::array< std::shared_ptr< NodeT< T, P, BV, K > >, K > m_children
Children nodes.
Definition: EBGeometry_BVH.hpp:408
Three-dimensional vector class with arithmetic operators.
Definition: EBGeometry_Vec.hpp:218
static constexpr Vec3T< T > max() noexcept
Return a vector with maximum representable components.
Namespace for various bounding volume hierarchy (BVH) functionality.
Definition: EBGeometry_BVH.hpp:30
std::vector< PrimAndBV< P, BV > > PrimAndBVListT
List of primitives and their bounding volumes.
Definition: EBGeometry_BVH.hpp:85
auto DefaultStopFunction
Simple stop function which ends the recursion when there aren't enough primitives in the node.
Definition: EBGeometry_BVH.hpp:226
std::pair< std::shared_ptr< const P >, BV > PrimAndBV
Alias for a list geometric primitive and BV.
Definition: EBGeometry_BVH.hpp:78
auto BVCentroidPartitioner
Simple partitioner which sorts the BVs based on their bounding volume centroids, and then splits into...
Definition: EBGeometry_BVH.hpp:198
Build
Enum for specifying whether or not the construction is top-down or bottom-up.
Definition: EBGeometry_BVH.hpp:36
std::function< bool(const NodeType &a_node, const Meta &a_meta)> Visiter
Visiter pattern for LinearBVH::traverse. Must return true if we should visit the node and false other...
Definition: EBGeometry_BVH.hpp:122
std::function< void(const PrimitiveListT< P > &a_primitives)> Updater
Updater for tree traversal.
Definition: EBGeometry_BVH.hpp:112
std::function< void(std::array< std::pair< std::shared_ptr< const NodeType >, Meta >, K > &a_children)> Sorter
Sorting criterion for which child node to visit first. This takes an input list of child nodes and so...
Definition: EBGeometry_BVH.hpp:131
std::vector< std::shared_ptr< const P > > PrimitiveListT
List of primitives.
Definition: EBGeometry_BVH.hpp:72
auto PrimitiveCentroidPartitioner
Simple partitioner which sorts the primitives based on their centroids, and then splits into K pieces...
Definition: EBGeometry_BVH.hpp:169
std::function< Meta(const NodeType &a_node)> MetaUpdater
Updater for when user wants to add some meta-data to his BVH traversal.
Definition: EBGeometry_BVH.hpp:137
std::function< std::array< PrimAndBVListT< P, BV >, K >(const PrimAndBVListT< P, BV > &a_primsAndBVs)> PartitionerT
Polymorphic partitioner for splitting a list of primitives and BVs into K new subsets.
Definition: EBGeometry_BVH.hpp:94
std::function< bool(const NodeT< T, P, BV, K > &a_node)> StopFunctionT
Stop function for deciding when a BVH node can't be divided into sub-volumes.
Definition: EBGeometry_BVH.hpp:105
auto equalCounts
Function for splitting a vector of some size into K almost-equal chunks. This is a utility function.
Definition: EBGeometry_BVH.hpp:143