EBGeometry
Compact, header-only C++ library for fast evaluation of signed distance functions
Loading...
Searching...
No Matches
EBGeometry_TreeBVH.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022 Robert Marskar <robert.marskar@sintef.no>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
17#ifndef EBGEOMETRY_TREEBVH_HPP
18#define EBGEOMETRY_TREEBVH_HPP
19
20// Std includes
21#include <algorithm>
22#include <cmath>
23#include <cstddef>
24#include <cstdint>
25#include <functional>
26#include <iterator>
27#include <memory>
28#include <type_traits>
29#include <utility>
30#include <vector>
31
32// Our includes
33#include "EBGeometry_Array.hpp"
35#include "EBGeometry_Macros.hpp"
36#include "EBGeometry_Math.hpp"
38#include "EBGeometry_SFC.hpp"
39#include "EBGeometry_Vec.hpp"
40
41namespace EBGeometry {
42
43namespace BVH {
44
48namespace detail {
49
59template <class P>
61inline void
62appendTreeLeaf(std::vector<P>& a_dst, const PrimitiveList<P>& a_leafPrims)
63{
64 // NB: do NOT reserve(a_dst.size() + a_leafPrims.size()) here. appendTreeLeaf is called once per
65 // leaf as the whole tree is packed, and reserving to an each-time-slightly-larger *exact* size
66 // defeats std::vector's geometric growth -- it forces a fresh reallocation (copying every
67 // element already appended) on every leaf, making a whole build O(N^2) in the primitive count.
68 // Plain push_back grows the buffer geometrically and keeps construction linear.
69 for (const auto& p : a_leafPrims) {
70 a_dst.push_back(*p);
71 }
72}
73
86template <class List, size_t K>
88inline void
89requireNonEmptyPartitions(const Array<List, K>& a_partitions, const size_t a_numInput, const char* a_who)
90{
91 for (size_t k = 0; k < K; k++) {
92 EBGEOMETRY_REQUIRE(!a_partitions[k].empty(),
93 "%s: the partitioner returned an empty partition (%zu of %zu, from %zu primitives). A leaf "
94 "must hold at least one primitive; a partitioner must return %zu non-empty partitions.",
95 a_who,
96 k,
97 K,
98 a_numInput,
99 K);
100 }
101}
102
114template <class P>
116inline void
117appendAliased(std::vector<P>& a_dst, const std::shared_ptr<std::vector<P>>& a_block)
118{
119 if (a_dst.empty()) {
120 a_dst = std::move(*a_block);
121 }
122 else {
123 a_dst.insert(a_dst.end(), std::make_move_iterator(a_block->begin()), std::make_move_iterator(a_block->end()));
124 }
125}
126
136inline void
137requireLeafSetting(const char* a_who, const Construction a_construction, const ConstructionOptions& a_options) noexcept
138{
139 switch (a_construction) {
143 EBGEOMETRY_REQUIRE(a_options.maxLeafSize > 0,
144 "%s: ConstructionOptions::maxLeafSize must be positive for a top-down construction",
145 a_who);
146
147 return;
148 }
153 a_options.targetLeafSize > 0,
154 "%s: ConstructionOptions::targetLeafSize must be positive for a space-filling-curve construction",
155 a_who);
156
157 return;
158 }
160 default: {
161 return;
162 }
163 }
164}
165
166} // namespace detail
167
175template <class X, size_t K>
176auto EqualCounts = [](std::vector<X> a_primitives) -> Array<std::vector<X>, K> {
177 static_assert(K >= 2, "EqualCounts<X, K>: branching factor K must be at least 2");
178
179 EBGEOMETRY_EXPECT(!a_primitives.empty());
180
181 const int length = a_primitives.size() / K;
182 int remain = a_primitives.size() % K;
183
184 int begin = 0;
185 int end = 0;
186
187 Array<std::vector<X>, K> chunks;
188
189 for (size_t k = 0; k < K; k++) {
190 end += (remain > 0) ? length + 1 : length;
191 remain--;
192
193 // Move the [begin, end) slice out -- the input is taken by value and not reused, so the elements
194 // (e.g. shared_ptr primitive handles) transfer without copying or refcount churn.
195 chunks[k] = std::vector<X>(std::make_move_iterator(a_primitives.begin() + begin),
196 std::make_move_iterator(a_primitives.begin() + end));
197
198 begin = end;
199 }
200
201 return chunks;
202};
203
213template <class T, class P, class BV, size_t K>
215 EBGEOMETRY_EXPECT(!a_primsAndBVs.empty());
216
217 Vec3T<T> lo = +Vec3T<T>::max();
218 Vec3T<T> hi = -Vec3T<T>::max();
219
220 for (const auto& pbv : a_primsAndBVs) {
221 lo = min(lo, pbv.first->getCentroid());
222 hi = max(hi, pbv.first->getCentroid());
223 }
224
225 const size_t splitDir = (hi - lo).maxDir(true);
226
227 // The input is taken by value; sort it in place (no working copy) and move it into EqualCounts.
228 std::sort(a_primsAndBVs.begin(),
229 a_primsAndBVs.end(),
230 [splitDir](const PrimAndBV<P, BV>& pbv1, const PrimAndBV<P, BV>& pbv2) -> bool {
231 return pbv1.first->getCentroid(splitDir) < pbv2.first->getCentroid(splitDir);
232 });
233
234 return BVH::EqualCounts<PrimAndBV<P, BV>, K>(std::move(a_primsAndBVs));
235};
236
246template <class T, class P, class BV, size_t K>
248 EBGEOMETRY_EXPECT(!a_primsAndBVs.empty());
249
250 Vec3T<T> lo = +Vec3T<T>::max();
251 Vec3T<T> hi = -Vec3T<T>::max();
252
253 for (const auto& pbv : a_primsAndBVs) {
254 lo = min(lo, pbv.second.getCentroid());
255 hi = max(hi, pbv.second.getCentroid());
256 }
257
258 const size_t splitDir = (hi - lo).maxDir(true);
259
260 // The input is taken by value; sort it in place (no working copy) and move it into EqualCounts.
261 std::sort(a_primsAndBVs.begin(),
262 a_primsAndBVs.end(),
263 [splitDir](const PrimAndBV<P, BV>& pbv1, const PrimAndBV<P, BV>& pbv2) -> bool {
264 return pbv1.second.getCentroid()[splitDir] < pbv2.second.getCentroid()[splitDir];
265 });
266
267 return BVH::EqualCounts<PrimAndBV<P, BV>, K>(std::move(a_primsAndBVs));
268};
269
288template <class T, class P, class BV>
289inline size_t
291 const size_t a_begin,
292 const size_t a_end,
293 const bool a_longestAxisOnly = false) noexcept
294{
295 static_assert(std::is_same_v<BV, EBGeometry::BoundingVolumes::AABBT<T>>, "SAH2WaySplit requires BV == AABBT<T>");
296
297 EBGEOMETRY_EXPECT(a_begin < a_end);
298
299 constexpr int BINS = 32;
300
301 const size_t N = a_end - a_begin;
302
303 // Centroid bounding box
304 Vec3T<T> clo = +Vec3T<T>::max();
305 Vec3T<T> chi = -Vec3T<T>::max();
306
307 for (size_t i = a_begin; i < a_end; i++) {
308 const auto& c = a_list[i].second.getCentroid();
309
310 clo = min(clo, c);
311 chi = max(chi, c);
312 }
313
314 T bestCost = Math::Limits<T>::max();
315 T bestPlane = T(0);
316 int bestAxis = -1;
317
318 Vec3T<T> binLo[BINS];
319 Vec3T<T> binHi[BINS];
320
321 int binCnt[BINS];
322
323 T leftArea[BINS - 1];
324 int leftCnt[BINS - 1];
325
326 const int firstAxis = a_longestAxisOnly ? (chi - clo).maxDir(true) : 0;
327 const int lastAxis = a_longestAxisOnly ? firstAxis : 2;
328
329 for (int axis = firstAxis; axis <= lastAxis; axis++) {
330 const T lo = clo[axis];
331 const T hi = chi[axis];
332 const T ext = hi - lo;
333
334 // An extent so small that BINS / ext overflows cannot be binned; leave that axis out.
335 const T scale = T(BINS) / ext;
336
337 if (!(ext > T(0)) || !std::isfinite(scale)) {
338 continue;
339 }
340
341 for (int b = 0; b < BINS; b++) {
342 binLo[b] = Vec3T<T>::max();
343 binHi[b] = -Vec3T<T>::max();
344 binCnt[b] = 0;
345 }
346
347 for (size_t i = a_begin; i < a_end; i++) {
348 // Clamped in floating point before the conversion, which is undefined past int's range.
349 const T x = (a_list[i].second.getCentroid()[axis] - lo) * scale;
350 const int b = (x > T(0)) ? ((x < T(BINS - 1)) ? static_cast<int>(x) : BINS - 1) : 0;
351 binLo[b] = min(binLo[b], a_list[i].second.getLowCorner());
352 binHi[b] = max(binHi[b], a_list[i].second.getHighCorner());
353 binCnt[b] = binCnt[b] + 1;
354 }
355
356 // Left prefix: accumulated AABB and count for bins [0..b]
357 Vec3T<T> rlo = Vec3T<T>::max();
358 Vec3T<T> rhi = -Vec3T<T>::max();
359 int rcnt = 0;
360
361 for (int b = 0; b < BINS - 1; b++) {
362 if (binCnt[b] > 0) {
363 rlo = min(rlo, binLo[b]);
364 rhi = max(rhi, binHi[b]);
365 }
366
367 rcnt = rcnt + binCnt[b];
368 leftArea[b] = (rcnt > 0) ? BV(rlo, rhi).getArea() : T(0);
369 leftCnt[b] = rcnt;
370 }
371
372 // Right suffix sweep; evaluate SAH cost at each candidate split boundary
373 Vec3T<T> rrlo = Vec3T<T>::max();
374 Vec3T<T> rrhi = -Vec3T<T>::max();
375 int rrcnt = 0;
376
377 for (int b = BINS - 1; b >= 1; b--) {
378 if (binCnt[b] > 0) {
379 rrlo = min(rrlo, binLo[b]);
380 rrhi = max(rrhi, binHi[b]);
381 }
382
383 rrcnt += binCnt[b];
384
385 if (leftCnt[b - 1] > 0 && rrcnt > 0) {
386 const T cost = leftArea[b - 1] * T(leftCnt[b - 1]) + BV(rrlo, rrhi).getArea() * T(rrcnt);
387
388 if (cost < bestCost) {
389 bestCost = cost;
390 bestAxis = axis;
391 bestPlane = lo + T(b) / scale;
392 }
393 }
394 }
395 }
396
397 // All axes degenerate — equal split
398 if (bestAxis < 0) {
399 return a_begin + N / 2;
400 }
401
402 auto mid = std::partition(
403 a_list.begin() + a_begin, a_list.begin() + a_end, [bestAxis, bestPlane](const PrimAndBV<P, BV>& pbv) noexcept {
404 return pbv.second.getCentroid()[bestAxis] < bestPlane;
405 });
406
407 const size_t splitIdx = static_cast<size_t>(std::distance(a_list.begin(), mid));
408
409 // Guard: if everything ended up on one side, fall back to equal split
410 return (splitIdx == a_begin || splitIdx == a_end) ? a_begin + N / 2 : splitIdx;
411}
412
427template <class T, class P, class BV>
428inline void
430 const size_t a_begin,
431 const size_t a_end,
432 const size_t a_K,
433 std::vector<std::pair<size_t, size_t>>& a_groups,
434 const bool a_longestAxisOnly = false)
435{
436 static_assert(std::is_same_v<BV, EBGeometry::BoundingVolumes::AABBT<T>>, "SAHKWaySplit requires BV == AABBT<T>");
437
438 if (a_K <= 1 || a_begin >= a_end) {
439 a_groups.emplace_back(a_begin, a_end);
440
441 return;
442 }
443
444 const size_t K1 = a_K / 2;
445 const size_t K2 = a_K - K1;
446
447 // Clamp the SAH split so the left half has >= K1 elements and the right has >= K2.
448 // This guarantees neither recursive call receives an under-populated range, which
449 // would cause empty sub-lists to reach the TreeBVH constructor and crash on
450 // AABBT(vector::front()) when the vector is empty.
451 // The clamp is valid whenever a_end - a_begin >= a_K = K1 + K2.
452 const size_t rawMid = SAH2WaySplit<T, P, BV>(a_list, a_begin, a_end, a_longestAxisOnly);
453 const size_t mid = Math::max(a_begin + K1, Math::min(a_end - K2, rawMid));
454
455 SAHKWaySplit<T, P, BV>(a_list, a_begin, mid, K1, a_groups, a_longestAxisOnly);
456 SAHKWaySplit<T, P, BV>(a_list, mid, a_end, K2, a_groups, a_longestAxisOnly);
457}
458
487template <class T, class P, class BV, size_t K, bool LongestAxisOnly = false>
489 EBGEOMETRY_EXPECT(!a_primsAndBVs.empty());
490
491 // The input is taken by value; partition it in place (no working copy). SAHKWaySplit reorders it
492 // and yields K [begin, end) index ranges, which we move out (disjoint, each moved once).
493 std::vector<std::pair<size_t, size_t>> groups;
494 groups.reserve(K);
495
496 SAHKWaySplit<T, P, BV>(a_primsAndBVs, 0, a_primsAndBVs.size(), K, groups, LongestAxisOnly);
497
499 for (size_t k = 0; k < K; k++) {
500 const auto [b, e] = groups[k];
501 result[k] = PrimAndBVList<P, BV>(std::make_move_iterator(a_primsAndBVs.begin() + b),
502 std::make_move_iterator(a_primsAndBVs.begin() + e));
503 }
504
505 return result;
506};
507
523template <class T, class P, class BV>
524inline size_t
525Midpoint2WaySplit(PrimAndBVList<P, BV>& a_list, const size_t a_begin, const size_t a_end) noexcept
526{
527 EBGEOMETRY_EXPECT(a_begin < a_end);
528
529 const size_t N = a_end - a_begin;
530
531 Vec3T<T> lo = +Vec3T<T>::max();
532 Vec3T<T> hi = -Vec3T<T>::max();
533
534 for (size_t i = a_begin; i < a_end; i++) {
535 const auto& c = a_list[i].second.getCentroid();
536
537 lo = min(lo, c);
538 hi = max(hi, c);
539 }
540
541 const size_t splitDir = (hi - lo).maxDir(true);
542 const T midpoint = T(0.5) * (lo[splitDir] + hi[splitDir]);
543
544 auto mid = std::partition(
545 a_list.begin() + a_begin, a_list.begin() + a_end, [splitDir, midpoint](const PrimAndBV<P, BV>& pbv) noexcept {
546 return pbv.second.getCentroid()[splitDir] < midpoint;
547 });
548
549 const size_t splitIdx = static_cast<size_t>(std::distance(a_list.begin(), mid));
550
551 // Guard: if every centroid ended up on one side (e.g. all coincide on the split axis, or the
552 // distribution is skewed entirely to one side of the midpoint), fall back to an equal-count
553 // split so neither resulting group is ever empty.
554 return (splitIdx == a_begin || splitIdx == a_end) ? a_begin + N / 2 : splitIdx;
555}
556
572template <class T, class P, class BV>
573inline void
575 const size_t a_begin,
576 const size_t a_end,
577 const size_t a_K,
578 std::vector<std::pair<size_t, size_t>>& a_groups)
579{
580 if (a_K <= 1 || a_begin >= a_end) {
581 a_groups.emplace_back(a_begin, a_end);
582
583 return;
584 }
585
586 const size_t K1 = a_K / 2;
587 const size_t K2 = a_K - K1;
588
589 // Clamp so the left half has >= K1 elements and the right has >= K2, guaranteeing neither
590 // recursive call receives an under-populated range (see SAHKWaySplit's identical clamp for why:
591 // an empty sub-list reaching the TreeBVH constructor crashes on AABBT(vector::front())).
592 const size_t rawMid = Midpoint2WaySplit<T, P, BV>(a_list, a_begin, a_end);
593 const size_t mid = Math::max(a_begin + K1, Math::min(a_end - K2, rawMid));
594
595 MidpointKWaySplit<T, P, BV>(a_list, a_begin, mid, K1, a_groups);
596 MidpointKWaySplit<T, P, BV>(a_list, mid, a_end, K2, a_groups);
597}
598
620template <class T, class P, class BV, size_t K>
622 EBGEOMETRY_EXPECT(!a_primsAndBVs.empty());
623
624 // The input is taken by value; partition it in place (no working copy). MidpointKWaySplit reorders
625 // it and yields K [begin, end) index ranges, which we move out (disjoint, each moved once). This
626 // is the crux of the partitioner's cost: the midpoint split itself is a couple of std::partition
627 // passes, so moving rather than copying the lists is what keeps it cheap.
628 std::vector<std::pair<size_t, size_t>> groups;
629 groups.reserve(K);
630
631 MidpointKWaySplit<T, P, BV>(a_primsAndBVs, 0, a_primsAndBVs.size(), K, groups);
632
634 for (size_t k = 0; k < K; k++) {
635 const auto [b, e] = groups[k];
636 result[k] = PrimAndBVList<P, BV>(std::make_move_iterator(a_primsAndBVs.begin() + b),
637 std::make_move_iterator(a_primsAndBVs.begin() + e));
638 }
639
640 return result;
641};
642
652template <class T, class P, class BV, size_t K>
654 [](const BVH::TreeBVH<T, P, BV, K>& a_node) noexcept -> bool { return (a_node.getPrimitives()).size() < K; };
655
677template <class T, class P, class BV, size_t K>
679{
680 static_assert(std::is_floating_point_v<T>, "TreeBVH: T must be a floating-point type");
681 static_assert(K >= 2, "TreeBVH: branching factor K must be at least 2");
682
683public:
688
692 using Vec3 = Vec3T<T>;
693
698
702 using NodePtr = std::shared_ptr<Node>;
703
708
713
718
723 TreeBVH(const std::vector<PrimAndBV<P, BV>>& a_primsAndBVs);
724
732 TreeBVH(std::vector<PrimAndBV<P, BV>>&& a_primsAndBVs);
733
737 ~TreeBVH() noexcept = default;
738
749 TreeBVH(const TreeBVH& a_other) = delete;
750
757 TreeBVH&
758 operator=(const TreeBVH& a_other) = delete;
759
766 TreeBVH(TreeBVH&& a_other) noexcept = default;
767
773 TreeBVH&
774 operator=(TreeBVH&& a_other) noexcept = default;
775
784 inline void
785 topDownSortAndPartition(const Partitioner& a_partitioner = BVCentroidPartitioner<T, P, BV, K>,
786 const LeafPredicate& a_stopCrit = DefaultLeafPredicate<T, P, BV, K>);
787
800 template <typename S>
801 inline void
802 bottomUpSortAndPartition(size_t a_targetLeafSize = 1);
803
808 [[nodiscard]] inline bool
809 isLeaf() const noexcept;
810
815 [[nodiscard]] inline bool
816 isPartitioned() const noexcept;
817
822 [[nodiscard]] inline const BV&
823 getBoundingVolume() const noexcept;
824
830 [[nodiscard]] inline const PrimitiveList&
831 getPrimitives() const noexcept;
832
838 [[nodiscard]] inline const std::vector<BV>&
839 getBoundingVolumes() const noexcept;
840
847 [[nodiscard]] inline T
848 getDistanceToBoundingVolume(const Vec3& a_point) const noexcept;
849
855 [[nodiscard]] inline const Array<std::shared_ptr<TreeBVH<T, P, BV, K>>, K>&
856 getChildren() const noexcept;
857
870 [[nodiscard]] inline std::shared_ptr<TreeBVH<T, P, BV, K>>
871 deepCopy() const;
872
903 template <class NodeKey>
904 inline void
905 traverse(const BVH::LeafEvaluator<P>& a_leafEvaluator,
906 const BVH::PrunePredicate<Node, NodeKey>& a_prunePredicate,
907 const BVH::ChildOrderer<Node, NodeKey, K>& a_childOrderer,
908 const BVH::NodeKeyFactory<Node, NodeKey>& a_nodeKeyFactory) const;
909
933 template <class BVConstructor>
934 inline const BV&
935 refit(const BVConstructor& a_bvConstructor);
936
943 [[nodiscard]] EBGEOMETRY_HOST
944 inline PackedBVH<T, P, K>
945 pack(Pool& a_pool) const;
946
960 template <class Q, class Converter>
961 [[nodiscard]] EBGEOMETRY_HOST
962 inline PackedBVH<T, Q, K>
963 packWith(Pool& a_pool, Converter&& a_converter) const;
964
965protected:
970
975
979 std::vector<std::shared_ptr<const P>> m_primitives;
980
984 std::vector<BV> m_boundingVolumes;
985
989 Array<std::shared_ptr<TreeBVH<T, P, BV, K>>, K> m_children;
990
995 inline PrimitiveList&
996 getPrimitives() noexcept;
997
1002 inline std::vector<BV>&
1004
1011 inline void
1012 setChildren(const Array<std::shared_ptr<TreeBVH<T, P, BV, K>>, K>& a_children);
1013};
1014
1015} // namespace BVH
1016
1017} // namespace EBGeometry
1018
1019#include "EBGeometry_TreeBVHImplem.hpp"
1020
1021// pack() and packWith() call the PackedBVH constructor that takes a TreeBVH, defined there.
1022#include "EBGeometry_BVHBuild.hpp"
1023
1024#endif
A fixed-size array that compiles for both host and device.
Host-side BVH construction: the PackedBVH constructors that pack a TreeBVH, and PackedBVH's static bu...
Declarations of bounding volume types used in bounding volume hierarchies.
#define EBGEOMETRY_HOST
Mark a function as callable from host code only (no-op on host compilers).
Definition EBGeometry_GPU.hpp:83
Utility macros for EBGeometry.
#define EBGEOMETRY_REQUIRE(cond,...)
Always-on check of user input and one-time host setup.
Definition EBGeometry_Macros.hpp:84
#define EBGEOMETRY_EXPECT(cond)
Runtime precondition assertion for EBGeometry.
Definition EBGeometry_Macros.hpp:155
Scalar helpers and numeric limits that compile for both host and device.
The flattened, device-callable bounding volume hierarchy: PackedBVH, its node layout,...
Declaration of various space-filling curves.
Declaration of 2D and 3D point/vector classes with templated precision, and free functions on them.
Forward declaration of the linearised BVH, defined below.
Definition EBGeometry_PackedBVH.hpp:357
Forward declaration of the tree-structured BVH, defined in EBGeometry_TreeBVH.hpp....
Definition EBGeometry_TreeBVH.hpp:679
BVH::Partitioner< P, BV, K > Partitioner
Alias for the partitioner type.
Definition EBGeometry_TreeBVH.hpp:707
bool isLeaf() const noexcept
Return true if this is a leaf node (no children, non-empty primitive list).
const std::vector< BV > & getBoundingVolumes() const noexcept
Get the per-primitive bounding volumes stored in this node.
void bottomUpSortAndPartition(size_t a_targetLeafSize=1)
Recursively partition this node bottom-up along a space-filling curve.
const BV & refit(const BVConstructor &a_bvConstructor)
Refit this subtree's bounding volumes in place after its primitives have moved.
std::vector< BV > m_boundingVolumes
Per-primitive bounding volumes. Non-empty for leaf nodes only.
Definition EBGeometry_TreeBVH.hpp:984
TreeBVH()
Default constructor. Creates an empty interior node.
~TreeBVH() noexcept=default
Destructor.
const Array< std::shared_ptr< TreeBVH< T, P, BV, K > >, K > & getChildren() const noexcept
Get the K child nodes of this interior node.
std::shared_ptr< TreeBVH< T, P, BV, K > > deepCopy() const
Produce an independent deep copy of this tree.
BVH::PrimitiveList< P > PrimitiveList
Alias for the primitive list type.
Definition EBGeometry_TreeBVH.hpp:687
bool m_partitioned
True after topDownSortAndPartition() or bottomUpSortAndPartition() has been called.
Definition EBGeometry_TreeBVH.hpp:974
std::vector< std::shared_ptr< const P > > m_primitives
Primitives stored in this node. Non-empty for leaf nodes only.
Definition EBGeometry_TreeBVH.hpp:979
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_point to this node's bounding volume.
PackedBVH< T, P, K > pack(Pool &a_pool) const
Flatten this tree into a cache-friendly PackedBVH with the same primitive type.
Array< std::shared_ptr< TreeBVH< T, P, BV, K > >, K > m_children
K child nodes. Non-null for interior nodes only.
Definition EBGeometry_TreeBVH.hpp:989
void topDownSortAndPartition(const Partitioner &a_partitioner=BVCentroidPartitioner< T, P, BV, K >, const LeafPredicate &a_stopCrit=DefaultLeafPredicate< T, P, BV, K >)
Recursively partition this node top-down.
void setChildren(const Array< std::shared_ptr< TreeBVH< T, P, BV, K > >, K > &a_children)
Set the K child nodes, converting this node from a leaf into an interior node.
TreeBVH(const std::vector< PrimAndBV< P, BV > > &a_primsAndBVs)
Construct a leaf node from a set of (primitive, BV) pairs.
BVH::LeafPredicate< T, P, BV, K > LeafPredicate
Alias for the stop-function type.
Definition EBGeometry_TreeBVH.hpp:712
PackedBVH< T, Q, K > packWith(Pool &a_pool, Converter &&a_converter) const
Flatten and convert this tree into a PackedBVH with a different primitive type Q.
BV m_boundingVolume
Bounding volume enclosing all primitives in this subtree.
Definition EBGeometry_TreeBVH.hpp:969
void traverse(const BVH::LeafEvaluator< P > &a_leafEvaluator, const BVH::PrunePredicate< Node, NodeKey > &a_prunePredicate, const BVH::ChildOrderer< Node, NodeKey, K > &a_childOrderer, const BVH::NodeKeyFactory< Node, NodeKey > &a_nodeKeyFactory) const
Recursion-free BVH traversal using an explicit LIFO stack (depth-first order).
TreeBVH(std::vector< PrimAndBV< P, BV > > &&a_primsAndBVs)
Construct a leaf node holding the given (primitive, bounding volume) pairs, moved in.
const BV & getBoundingVolume() const noexcept
Get the bounding volume for this node.
std::shared_ptr< Node > NodePtr
Alias for a shared pointer to a node.
Definition EBGeometry_TreeBVH.hpp:702
bool isPartitioned() const noexcept
Return true if the tree has already been partitioned.
Growable bump arena over a MemoryResource.
Definition EBGeometry_Pool.hpp:183
Three-dimensional vector class with arithmetic operators.
Definition EBGeometry_Vec.hpp:258
static constexpr Vec3T< T > max() noexcept
Return the most-positive representable vector.
void appendTreeLeaf(std::vector< P > &a_dst, const PrimitiveList< P > &a_leafPrims)
Append one TreeBVH leaf's primitives to a flat primitive array under construction.
Definition EBGeometry_TreeBVH.hpp:62
void requireLeafSetting(const char *a_who, const Construction a_construction, const ConstructionOptions &a_options) noexcept
Abort unless the leaf-size setting that a_construction reads is positive.
Definition EBGeometry_TreeBVH.hpp:137
void appendAliased(std::vector< P > &a_dst, const std::shared_ptr< std::vector< P > > &a_block)
Materialise a single contiguous buffer into a flat primitive array under construction.
Definition EBGeometry_TreeBVH.hpp:117
void requireNonEmptyPartitions(const Array< List, K > &a_partitions, const size_t a_numInput, const char *a_who)
Abort unless every partition a partitioner returned holds at least one primitive.
Definition EBGeometry_TreeBVH.hpp:89
size_t SAH2WaySplit(PrimAndBVList< P, BV > &a_list, const size_t a_begin, const size_t a_end, const bool a_longestAxisOnly=false) noexcept
Internal helper: 2-way binned SAH split on the sub-range [a_begin, a_end).
Definition EBGeometry_TreeBVH.hpp:290
auto EqualCounts
Utility: split a vector into K almost-equal contiguous chunks.
Definition EBGeometry_TreeBVH.hpp:176
std::vector< std::shared_ptr< const P > > PrimitiveList
Convenience alias for a list of shared primitive pointers.
Definition EBGeometry_PackedBVH.hpp:215
std::function< bool(const NodeType &a_node, const NodeKey &a_nodeKey)> PrunePredicate
Node-visit predicate for TreeBVH::traverse().
Definition EBGeometry_PackedBVH.hpp:288
std::function< Array< PrimAndBVList< P, BV >, K >(PrimAndBVList< P, BV > a_primsAndBVs)> Partitioner
Polymorphic partitioner: splits a list of (primitive, BV) pairs into K sub-lists.
Definition EBGeometry_PackedBVH.hpp:253
std::vector< PrimAndBV< P, BV > > PrimAndBVList
Convenience alias for a list of (primitive, bounding-volume) pairs.
Definition EBGeometry_PackedBVH.hpp:237
std::function< bool(const TreeBVH< T, P, BV, K > &a_node)> LeafPredicate
Predicate for deciding when a TreeBVH node should become a leaf (i.e., no further splitting).
Definition EBGeometry_PackedBVH.hpp:265
std::pair< std::shared_ptr< const P >, BV > PrimAndBV
Convenience alias for a (primitive, bounding-volume) pair.
Definition EBGeometry_PackedBVH.hpp:229
std::function< void(const PrimitiveList< P > &a_primitives)> LeafEvaluator
Leaf-evaluation callback for TreeBVH::traverse.
Definition EBGeometry_PackedBVH.hpp:274
std::function< NodeKey(const NodeType &a_node)> NodeKeyFactory
Node-key factory called once per node during TreeBVH::traverse().
Definition EBGeometry_PackedBVH.hpp:312
size_t Midpoint2WaySplit(PrimAndBVList< P, BV > &a_list, const size_t a_begin, const size_t a_end) noexcept
Internal helper: 2-way spatial-midpoint split on the sub-range [a_begin, a_end).
Definition EBGeometry_TreeBVH.hpp:525
auto PrimitiveCentroidPartitioner
Partitioner that sorts primitives by centroid along the longest axis and splits into K pieces.
Definition EBGeometry_TreeBVH.hpp:214
void MidpointKWaySplit(PrimAndBVList< P, BV > &a_list, const size_t a_begin, const size_t a_end, const size_t a_K, std::vector< std::pair< size_t, size_t > > &a_groups)
Internal helper: recursively split [a_begin, a_end) into a_K groups via 2-way midpoint splits.
Definition EBGeometry_TreeBVH.hpp:574
auto BVCentroidPartitioner
Partitioner that sorts primitives by bounding-volume centroid along the longest axis and splits into ...
Definition EBGeometry_TreeBVH.hpp:247
auto BinnedSAHPartitioner
Partitioner using binned SAH with recursive 2-way subdivision into K groups.
Definition EBGeometry_TreeBVH.hpp:488
auto DefaultLeafPredicate
Default stop function: stop partitioning when the node holds fewer than K primitives.
Definition EBGeometry_TreeBVH.hpp:653
std::function< void(Array< std::pair< std::shared_ptr< const NodeType >, NodeKey >, K > &a_children)> ChildOrderer
Child-ordering callback for TreeBVH traversal.
Definition EBGeometry_PackedBVH.hpp:300
Construction
The preset BVH construction methods that the library's own BVH users accept.
Definition EBGeometry_PackedBVH.hpp:65
@ Nested
Bottom-up: leaves of consecutive primitives along a Nested (row-major) curve.
@ Hilbert
Bottom-up: leaves of consecutive primitives along a Hilbert curve.
@ Morton
Bottom-up: leaves of consecutive primitives along a Morton (Z-order) curve.
void SAHKWaySplit(PrimAndBVList< P, BV > &a_list, const size_t a_begin, const size_t a_end, const size_t a_K, std::vector< std::pair< size_t, size_t > > &a_groups, const bool a_longestAxisOnly=false)
Internal helper: recursively split [a_begin, a_end) into a_K groups via 2-way SAH.
Definition EBGeometry_TreeBVH.hpp:429
auto MidpointPartitioner
Partitioner that recursively bisects primitives by spatial midpoint (no sorting).
Definition EBGeometry_TreeBVH.hpp:621
constexpr T max(const T &a_a, const T &a_b) noexcept
The larger of two values.
Definition EBGeometry_Math.hpp:61
constexpr T min(const T &a_a, const T &a_b) noexcept
The smaller of two values.
Definition EBGeometry_Math.hpp:44
Namespace containing all of EBGeometry's functionality.
Definition EBGeometry_Affine.hpp:26
Vec2T< T > min(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Component-wise minimum of two vectors.
T length(const Vec2T< T > &v) noexcept
Euclidean length of a 2D vector.
Vec2T< T > max(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Component-wise maximum of two vectors.
Fixed-size array with host- and device-callable members.
Definition EBGeometry_Array.hpp:33
Leaf-size settings for the preset construction methods, one per family of methods.
Definition EBGeometry_PackedBVH.hpp:115
Numeric limits, with the meanings of the std::numeric_limits members of the same names.
Definition EBGeometry_Math.hpp:91