EBGeometry
Compact, header-only C++ library for fast evaluation of signed distance functions
Loading...
Searching...
No Matches
EBGeometry_BVH.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
11#ifndef EBGEOMETRY_BVH_HPP
12#define EBGEOMETRY_BVH_HPP
13
14// Std includes
15#include <algorithm>
16#include <array>
17#include <cstddef>
18#include <cstdint>
19#include <functional>
20#include <iterator>
21#include <limits>
22#include <memory>
23#include <type_traits>
24#include <utility>
25#include <vector>
26
27#if defined(__SSE4_1__) || defined(__AVX__)
28#include <immintrin.h>
29#endif
30
31// Our includes
33#include "EBGeometry_Macros.hpp"
34#include "EBGeometry_SFC.hpp"
35#include "EBGeometry_Vec.hpp"
36
37namespace EBGeometry {
38
42namespace BVH {
43
47enum class Build
48{
49 TopDown,
50 Morton,
51 Nested,
52 SAH
55};
56
69{
70 size_t maxClusterSize = 8;
71};
72
89template <typename T>
90[[nodiscard]] constexpr size_t
92{
93 static_assert(std::is_floating_point_v<T>, "BVH::DefaultBranchingRatio requires a floating-point T");
94#if defined(__AVX512F__)
95 if constexpr (std::is_same_v<T, double>) {
96 return 8;
97 }
98 else {
99 return 16;
100 }
101#elif defined(__AVX__)
102 if constexpr (std::is_same_v<T, double>) {
103 return 4;
104 }
105 else {
106 return 8;
107 }
108#else
109 return 4;
110#endif
111}
112
117template <class T, class P, class BV, size_t K>
118class TreeBVH;
119
124template <class P>
125using PrimitiveList = std::vector<std::shared_ptr<const P>>;
126
144template <class P>
146{
151 using StorageType = std::shared_ptr<const P>;
152
158 [[nodiscard]] static const P&
159 get(const StorageType& a_stored) noexcept
160 {
161 return *a_stored;
162 }
163
171 static void
172 appendTreeLeaf(std::vector<StorageType>& a_dst, const PrimitiveList<P>& a_leafPrims)
173 {
174 a_dst.insert(a_dst.end(), a_leafPrims.begin(), a_leafPrims.end());
175 }
176
186 static void
187 appendAliased(std::vector<StorageType>& a_dst, const std::shared_ptr<std::vector<P>>& a_block)
188 {
189 a_dst.reserve(a_dst.size() + a_block->size());
190
191 for (size_t i = 0; i < a_block->size(); i++) {
192 a_dst.emplace_back(a_block, &(*a_block)[i]);
193 }
194 }
195};
196
206template <class P>
208{
212 using StorageType = P;
213
219 [[nodiscard]] static const P&
220 get(const StorageType& a_stored) noexcept
221 {
222 return a_stored;
223 }
224
234 static void
235 appendTreeLeaf(std::vector<StorageType>& a_dst, const PrimitiveList<P>& a_leafPrims)
236 {
237 // NB: do NOT reserve(a_dst.size() + a_leafPrims.size()) here. appendTreeLeaf is called once per
238 // leaf as the whole tree is packed, and reserving to an each-time-slightly-larger *exact* size
239 // defeats std::vector's geometric growth -- it forces a fresh reallocation (copying every
240 // element already appended) on every leaf, making a whole build O(N^2) in the primitive count.
241 // Both the identity pack() constructor and the direct top-down PackedBVH constructor append
242 // leaves this way, so the quadratic cost would hit every ValueStorage build over many leaves.
243 // Plain push_back grows the buffer geometrically and keeps construction linear.
244 for (const auto& p : a_leafPrims) {
245 a_dst.push_back(*p);
246 }
247 }
248
260 static void
261 appendAliased(std::vector<StorageType>& a_dst, const std::shared_ptr<std::vector<P>>& a_block)
262 {
263 if (a_dst.empty()) {
264 a_dst = std::move(*a_block);
265 }
266 else {
267 a_dst.insert(a_dst.end(), std::make_move_iterator(a_block->begin()), std::make_move_iterator(a_block->end()));
268 }
269 }
270};
271
278template <class T, class P, size_t K, class StoragePolicy = SharedPtrStorage<P>>
279class PackedBVH;
280
286template <class P, class BV>
287using PrimAndBV = std::pair<std::shared_ptr<const P>, BV>;
288
294template <class P, class BV>
295using PrimAndBVList = std::vector<PrimAndBV<P, BV>>;
296
310template <class P, class BV, size_t K>
311using Partitioner = std::function<std::array<PrimAndBVList<P, BV>, K>(PrimAndBVList<P, BV> a_primsAndBVs)>;
312
322template <class T, class P, class BV, size_t K>
323using LeafPredicate = std::function<bool(const TreeBVH<T, P, BV, K>& a_node)>;
324
331template <class P>
332using LeafEvaluator = std::function<void(const PrimitiveList<P>& a_primitives)>;
333
347template <class P, class StoragePolicy = SharedPtrStorage<P>>
348using PackedLeafEvaluator = std::function<void(
349 const std::vector<typename StoragePolicy::StorageType>& a_primitives, size_t a_offset, size_t a_count)>;
350
361template <class NodeType, class NodeKey>
362using PrunePredicate = std::function<bool(const NodeType& a_node, const NodeKey& a_nodeKey)>;
363
373template <class NodeType, class NodeKey, size_t K>
375 std::function<void(std::array<std::pair<std::shared_ptr<const NodeType>, NodeKey>, K>& a_children)>;
376
385template <class NodeKey, size_t K>
386using PackedChildOrderer = std::function<void(std::array<std::pair<uint32_t, NodeKey>, K>& a_children)>;
387
397template <class NodeType, class NodeKey>
398using NodeKeyFactory = std::function<NodeKey(const NodeType& a_node)>;
399
407template <class X, size_t K>
408auto EqualCounts = [](std::vector<X> a_primitives) noexcept -> std::array<std::vector<X>, K> {
409 static_assert(K >= 2, "EqualCounts<X, K>: branching factor K must be at least 2");
410
412
413 const int length = a_primitives.size() / K;
414 int remain = a_primitives.size() % K;
415
416 int begin = 0;
417 int end = 0;
418
419 std::array<std::vector<X>, K> chunks;
420
421 for (size_t k = 0; k < K; k++) {
422 end += (remain > 0) ? length + 1 : length;
423 remain--;
424
425 // Move the [begin, end) slice out -- the input is taken by value and not reused, so the elements
426 // (e.g. shared_ptr primitive handles) transfer without copying or refcount churn.
427 chunks[k] = std::vector<X>(std::make_move_iterator(a_primitives.begin() + begin),
428 std::make_move_iterator(a_primitives.begin() + end));
429
430 begin = end;
431 }
432
433 return chunks;
434};
435
445template <class T, class P, class BV, size_t K>
447 [](PrimAndBVList<P, BV> a_primsAndBVs) noexcept -> std::array<PrimAndBVList<P, BV>, K> {
449
452
453 for (const auto& pbv : a_primsAndBVs) {
454 lo = min(lo, pbv.first->getCentroid());
455 hi = max(hi, pbv.first->getCentroid());
456 }
457
458 const size_t splitDir = (hi - lo).maxDir(true);
459
460 // The input is taken by value; sort it in place (no working copy) and move it into EqualCounts.
461 std::sort(a_primsAndBVs.begin(),
462 a_primsAndBVs.end(),
463 [splitDir](const PrimAndBV<P, BV>& pbv1, const PrimAndBV<P, BV>& pbv2) -> bool {
464 return pbv1.first->getCentroid(splitDir) < pbv2.first->getCentroid(splitDir);
465 });
466
467 return BVH::EqualCounts<PrimAndBV<P, BV>, K>(std::move(a_primsAndBVs));
468};
469
479template <class T, class P, class BV, size_t K>
482
485
486 for (const auto& pbv : a_primsAndBVs) {
487 lo = min(lo, pbv.second.getCentroid());
488 hi = max(hi, pbv.second.getCentroid());
489 }
490
491 const size_t splitDir = (hi - lo).maxDir(true);
492
493 // The input is taken by value; sort it in place (no working copy) and move it into EqualCounts.
494 std::sort(a_primsAndBVs.begin(),
495 a_primsAndBVs.end(),
496 [splitDir](const PrimAndBV<P, BV>& pbv1, const PrimAndBV<P, BV>& pbv2) -> bool {
497 return pbv1.second.getCentroid()[splitDir] < pbv2.second.getCentroid()[splitDir];
498 });
499
500 return BVH::EqualCounts<PrimAndBV<P, BV>, K>(std::move(a_primsAndBVs));
501};
502
521template <class T, class P, class BV>
522inline size_t
524 const size_t a_begin,
525 const size_t a_end,
526 const bool a_longestAxisOnly = false) noexcept
527{
528 static_assert(std::is_same_v<BV, EBGeometry::BoundingVolumes::AABBT<T>>, "SAH2WaySplit requires BV == AABBT<T>");
529
531
532 constexpr int BINS = 32;
533
534 const size_t N = a_end - a_begin;
535
536 // Centroid bounding box
539
540 for (size_t i = a_begin; i < a_end; i++) {
541 const auto& c = a_list[i].second.getCentroid();
542
543 clo = min(clo, c);
544 chi = max(chi, c);
545 }
546
547 T bestCost = std::numeric_limits<T>::max();
548 T bestPlane = T(0);
549 int bestAxis = -1;
550
553
554 int binCnt[BINS];
555
556 T leftArea[BINS - 1];
557 int leftCnt[BINS - 1];
558
559 const int firstAxis = a_longestAxisOnly ? (chi - clo).maxDir(true) : 0;
560 const int lastAxis = a_longestAxisOnly ? firstAxis : 2;
561
562 for (int axis = firstAxis; axis <= lastAxis; axis++) {
563 const T lo = clo[axis];
564 const T hi = chi[axis];
565 const T ext = hi - lo;
566
567 if (ext <= T(0)) {
568 continue;
569 }
570
571 const T scale = T(BINS) / ext;
572
573 for (int b = 0; b < BINS; b++) {
574 binLo[b] = Vec3T<T>::max();
575 binHi[b] = -Vec3T<T>::max();
576 binCnt[b] = 0;
577 }
578
579 for (size_t i = a_begin; i < a_end; i++) {
580 const int b = std::min(BINS - 1, (int)((a_list[i].second.getCentroid()[axis] - lo) * scale));
581 binLo[b] = min(binLo[b], a_list[i].second.getLowCorner());
582 binHi[b] = max(binHi[b], a_list[i].second.getHighCorner());
583 binCnt[b] = binCnt[b] + 1;
584 }
585
586 // Left prefix: accumulated AABB and count for bins [0..b]
589 int rcnt = 0;
590
591 for (int b = 0; b < BINS - 1; b++) {
592 if (binCnt[b] > 0) {
593 rlo = min(rlo, binLo[b]);
594 rhi = max(rhi, binHi[b]);
595 }
596
597 rcnt = rcnt + binCnt[b];
598 leftArea[b] = (rcnt > 0) ? BV(rlo, rhi).getArea() : T(0);
599 leftCnt[b] = rcnt;
600 }
601
602 // Right suffix sweep; evaluate SAH cost at each candidate split boundary
605 int rrcnt = 0;
606
607 for (int b = BINS - 1; b >= 1; b--) {
608 if (binCnt[b] > 0) {
609 rrlo = min(rrlo, binLo[b]);
610 rrhi = max(rrhi, binHi[b]);
611 }
612
613 rrcnt += binCnt[b];
614
615 if (leftCnt[b - 1] > 0 && rrcnt > 0) {
616 const T cost = leftArea[b - 1] * T(leftCnt[b - 1]) + BV(rrlo, rrhi).getArea() * T(rrcnt);
617
618 if (cost < bestCost) {
619 bestCost = cost;
620 bestAxis = axis;
621 bestPlane = lo + T(b) / scale;
622 }
623 }
624 }
625 }
626
627 // All axes degenerate — equal split
628 if (bestAxis < 0) {
629 return a_begin + N / 2;
630 }
631
632 auto mid = std::partition(
633 a_list.begin() + a_begin, a_list.begin() + a_end, [bestAxis, bestPlane](const PrimAndBV<P, BV>& pbv) noexcept {
634 return pbv.second.getCentroid()[bestAxis] < bestPlane;
635 });
636
637 const size_t splitIdx = static_cast<size_t>(std::distance(a_list.begin(), mid));
638
639 // Guard: if everything ended up on one side, fall back to equal split
640 return (splitIdx == a_begin || splitIdx == a_end) ? a_begin + N / 2 : splitIdx;
641}
642
657template <class T, class P, class BV>
658inline void
660 const size_t a_begin,
661 const size_t a_end,
662 const size_t a_K,
663 std::vector<std::pair<size_t, size_t>>& a_groups,
664 const bool a_longestAxisOnly = false) noexcept
665{
666 static_assert(std::is_same_v<BV, EBGeometry::BoundingVolumes::AABBT<T>>, "SAHKWaySplit requires BV == AABBT<T>");
667
669 a_groups.emplace_back(a_begin, a_end);
670
671 return;
672 }
673
674 const size_t K1 = a_K / 2;
675 const size_t K2 = a_K - K1;
676
677 // Clamp the SAH split so the left half has >= K1 elements and the right has >= K2.
678 // This guarantees neither recursive call receives an under-populated range, which
679 // would cause empty sub-lists to reach the TreeBVH constructor and crash on
680 // AABBT(vector::front()) when the vector is empty.
681 // The clamp is valid whenever a_end - a_begin >= a_K = K1 + K2.
683 const size_t mid = std::max(a_begin + K1, std::min(a_end - K2, rawMid));
684
687}
688
715template <class T, class P, class BV, size_t K, bool LongestAxisOnly = false>
718
719 // The input is taken by value; partition it in place (no working copy). SAHKWaySplit reorders it
720 // and yields K [begin, end) index ranges, which we move out (disjoint, each moved once).
721 std::vector<std::pair<size_t, size_t>> groups;
722 groups.reserve(K);
723
725
726 std::array<PrimAndBVList<P, BV>, K> result;
727 for (size_t k = 0; k < K; k++) {
728 const auto [b, e] = groups[k];
729 result[k] = PrimAndBVList<P, BV>(std::make_move_iterator(a_primsAndBVs.begin() + b),
730 std::make_move_iterator(a_primsAndBVs.begin() + e));
731 }
732
733 return result;
734};
735
751template <class T, class P, class BV>
752inline size_t
753Midpoint2WaySplit(PrimAndBVList<P, BV>& a_list, const size_t a_begin, const size_t a_end) noexcept
754{
756
757 const size_t N = a_end - a_begin;
758
761
762 for (size_t i = a_begin; i < a_end; i++) {
763 const auto& c = a_list[i].second.getCentroid();
764
765 lo = min(lo, c);
766 hi = max(hi, c);
767 }
768
769 const size_t splitDir = (hi - lo).maxDir(true);
770 const T midpoint = T(0.5) * (lo[splitDir] + hi[splitDir]);
771
772 auto mid = std::partition(
773 a_list.begin() + a_begin, a_list.begin() + a_end, [splitDir, midpoint](const PrimAndBV<P, BV>& pbv) noexcept {
774 return pbv.second.getCentroid()[splitDir] < midpoint;
775 });
776
777 const size_t splitIdx = static_cast<size_t>(std::distance(a_list.begin(), mid));
778
779 // Guard: if every centroid ended up on one side (e.g. all coincide on the split axis, or the
780 // distribution is skewed entirely to one side of the midpoint), fall back to an equal-count
781 // split so neither resulting group is ever empty.
782 return (splitIdx == a_begin || splitIdx == a_end) ? a_begin + N / 2 : splitIdx;
783}
784
800template <class T, class P, class BV>
801inline void
803 const size_t a_begin,
804 const size_t a_end,
805 const size_t a_K,
806 std::vector<std::pair<size_t, size_t>>& a_groups) noexcept
807{
809 a_groups.emplace_back(a_begin, a_end);
810
811 return;
812 }
813
814 const size_t K1 = a_K / 2;
815 const size_t K2 = a_K - K1;
816
817 // Clamp so the left half has >= K1 elements and the right has >= K2, guaranteeing neither
818 // recursive call receives an under-populated range (see SAHKWaySplit's identical clamp for why:
819 // an empty sub-list reaching the TreeBVH constructor crashes on AABBT(vector::front())).
821 const size_t mid = std::max(a_begin + K1, std::min(a_end - K2, rawMid));
822
825}
826
848template <class T, class P, class BV, size_t K>
851
852 // The input is taken by value; partition it in place (no working copy). MidpointKWaySplit reorders
853 // it and yields K [begin, end) index ranges, which we move out (disjoint, each moved once). This
854 // is the crux of the partitioner's cost: the midpoint split itself is a couple of std::partition
855 // passes, so the plumbing (copies) is what dominated before -- now eliminated.
856 std::vector<std::pair<size_t, size_t>> groups;
857 groups.reserve(K);
858
860
861 std::array<PrimAndBVList<P, BV>, K> result;
862 for (size_t k = 0; k < K; k++) {
863 const auto [b, e] = groups[k];
864 result[k] = PrimAndBVList<P, BV>(std::make_move_iterator(a_primsAndBVs.begin() + b),
865 std::make_move_iterator(a_primsAndBVs.begin() + e));
866 }
867
868 return result;
869};
870
880template <class T, class P, class BV, size_t K>
882 [](const BVH::TreeBVH<T, P, BV, K>& a_node) noexcept -> bool { return (a_node.getPrimitives()).size() < K; };
883
901template <class T, class P, class BV, size_t K>
902class TreeBVH : public std::enable_shared_from_this<TreeBVH<T, P, BV, K>>
903{
904 static_assert(std::is_floating_point_v<T>, "TreeBVH: T must be a floating-point type");
905 static_assert(K >= 2, "TreeBVH: branching factor K must be at least 2");
906
907public:
912
916 using Vec3 = Vec3T<T>;
917
922
926 using NodePtr = std::shared_ptr<Node>;
927
932
937
942
947 TreeBVH(const std::vector<PrimAndBV<P, BV>>& a_primsAndBVs);
948
956 TreeBVH(std::vector<PrimAndBV<P, BV>>&& a_primsAndBVs);
957
962
974
981 TreeBVH&
983
991
997 TreeBVH&
999
1006 inline void
1009
1018 inline void
1020
1025 [[nodiscard]] inline bool
1027
1032 [[nodiscard]] inline bool
1034
1039 [[nodiscard]] inline const BV&
1041
1049
1055 [[nodiscard]] inline const std::vector<BV>&
1057
1064 [[nodiscard]] inline T
1066
1072 [[nodiscard]] inline const std::array<std::shared_ptr<TreeBVH<T, P, BV, K>>, K>&
1074
1087 [[nodiscard]] inline std::shared_ptr<TreeBVH<T, P, BV, K>>
1089
1121 inline void
1126
1135 [[nodiscard]] inline std::shared_ptr<PackedBVH<T, P, K, StoragePolicy>>
1137
1153 [[nodiscard]] inline std::shared_ptr<PackedBVH<T, Q, K, StoragePolicy>>
1155
1156protected:
1161
1166
1170 std::vector<std::shared_ptr<const P>> m_primitives;
1171
1175 std::vector<BV> m_boundingVolumes;
1176
1180 std::array<std::shared_ptr<TreeBVH<T, P, BV, K>>, K> m_children;
1181
1188
1193 inline std::vector<BV>&
1195
1200 inline void
1201 setChildren(const std::array<std::shared_ptr<TreeBVH<T, P, BV, K>>, K>& a_children) noexcept;
1202};
1203
1236template <class T, class P, size_t K, class StoragePolicy>
1238{
1239 static_assert(std::is_floating_point_v<T>, "PackedBVH: T must be a floating-point type");
1240 static_assert(K >= 2, "PackedBVH: branching factor K must be at least 2");
1241
1242public:
1247
1253 using StorageType = typename StoragePolicy::StorageType;
1254
1261 struct Node
1262 {
1266 BV m_bv{};
1267
1271 uint32_t m_primOff{};
1272
1276 uint32_t m_numPrims{};
1277
1281 std::array<uint32_t, K> m_childOff{};
1282
1287 inline void
1288 setBoundingVolume(const BV& a_bv) noexcept
1289 {
1290 m_bv = a_bv;
1291 }
1292
1297 inline void
1299 {
1300 m_primOff = a_off;
1301 }
1302
1307 inline void
1309 {
1310 m_numPrims = a_n;
1311 }
1312
1318 inline void
1320 {
1322 m_childOff[a_k] = a_off;
1323 }
1324
1329 [[nodiscard]] inline const BV&
1331 {
1332 return m_bv;
1333 }
1334
1339 [[nodiscard]] inline uint32_t
1341 {
1342 return m_primOff;
1343 }
1344
1349 [[nodiscard]] inline uint32_t
1351 {
1352 return m_numPrims;
1353 }
1354
1359 [[nodiscard]] inline const std::array<uint32_t, K>&
1361 {
1362 return m_childOff;
1363 }
1364
1369 [[nodiscard]] inline bool
1371 {
1372 return m_numPrims > 0;
1373 }
1374
1380 [[nodiscard]] inline T
1382 {
1383 return m_bv.getDistance(a_point);
1384 }
1385
1394 [[nodiscard]] inline T
1396 {
1397 return m_bv.getDistance2(a_point);
1398 }
1399 };
1400
1404 PackedBVH() = delete;
1405
1414
1445 template <class Q, class Converter>
1447
1476 template <class S = SFC::Morton>
1477 inline PackedBVH(std::vector<std::pair<P, BV>> a_primsAndBVs, size_t a_targetLeafSize, S a_sfc = S{});
1478
1507 inline PackedBVH(std::vector<std::pair<P, BV>> a_primsAndBVs,
1510
1528 inline PackedBVH(std::vector<std::pair<P, BV>> a_primsAndBVs, BVH::ClusterSpec a_spec);
1529
1534 inline ~PackedBVH() = default;
1535
1548 PackedBVH(const PackedBVH& a_other) = default;
1549
1555 PackedBVH&
1556 operator=(const PackedBVH& a_other) = default;
1557
1564 PackedBVH(PackedBVH&& a_other) noexcept = default;
1565
1571 PackedBVH&
1572 operator=(PackedBVH&& a_other) noexcept = default;
1573
1578 [[nodiscard]] inline const std::vector<StorageType>&
1580
1587
1594 [[nodiscard]] inline BV
1595 computeBoundingVolume() const noexcept;
1596
1637 inline void
1642
1680 inline void
1681 pruneTraverse(const Vec3T<T>& a_point,
1682 State& a_state,
1685
1686protected:
1697 : m_linearNodes(std::move(a_linearNodes)), m_primitives(std::move(a_primitives))
1698 {
1699 this->buildSoA();
1700 }
1701
1705 std::vector<Node> m_linearNodes;
1706
1710 std::vector<StorageType> m_primitives;
1711
1719 {
1723 alignas(sizeof(T) * K) T m_lo[3][K];
1724
1728 alignas(sizeof(T) * K) T m_hi[3][K];
1729 };
1730
1734 std::vector<ChildAABBSoA> m_childAabbSoA;
1735
1740 inline void
1742};
1743
1744} // namespace BVH
1745
1746} // namespace EBGeometry
1747
1748#include "EBGeometry_BVHImplem.hpp"
1749
1750#endif
Declarations of bounding volume types used in bounding volume hierarchies.
Utility macros for EBGeometry.
#define EBGEOMETRY_EXPECT(cond)
Runtime precondition assertion for EBGeometry.
Definition EBGeometry_Macros.hpp:62
Declaration of various space-filling curves.
Declaration of 2D and 3D point/vector classes with templated precision. Used with DCEL tools.
Forward declaration of the linearised BVH. Needed so that TreeBVH::pack() and TreeBVH::packWith() can...
Definition EBGeometry_BVH.hpp:1238
PackedBVH()=delete
Deleted default constructor. Use TreeBVH::pack() or TreeBVH::packWith() to construct.
~PackedBVH()=default
Destructor.
void buildSoA()
Populate m_childAabbSoA from the completed m_linearNodes array.
std::vector< StorageType > m_primitives
Global primitive list in leaf-traversal order.
Definition EBGeometry_BVH.hpp:1710
typename StoragePolicy::StorageType StorageType
Storage representation of one entry in the primitive array, as determined by StoragePolicy (std::shar...
Definition EBGeometry_BVH.hpp:1253
PackedBVH & operator=(const PackedBVH &a_other)=default
Copy assignment operator.
PackedBVH(const TreeBVH< T, P, BV, K > &a_tree)
Construct by packing a TreeBVH (identity primitive type).
std::vector< ChildAABBSoA > m_childAabbSoA
Per-node SoA AABB cache used by the SIMD traversal in pruneTraverse().
Definition EBGeometry_BVH.hpp:1734
PackedBVH & operator=(PackedBVH &&a_other) noexcept=default
Move assignment operator.
PackedBVH(std::vector< std::pair< P, BV > > a_primsAndBVs, size_t a_targetLeafSize, S a_sfc=S{})
Construct directly from a flat primitive list, without ever building a TreeBVH.
PackedBVH(const PackedBVH &a_other)=default
Copy constructor.
PackedBVH(std::vector< std::pair< P, BV > > a_primsAndBVs, const BVH::Partitioner< P, BV, K > &a_partitioner=BVCentroidPartitioner< T, P, BV, K >, const BVH::LeafPredicate< T, P, BV, K > &a_stopCrit=DefaultLeafPredicate< T, P, BV, K >)
Construct directly from a flat primitive list via top-down (optionally SAH) recursive partitioning,...
const std::vector< StorageType > & getPrimitives() const noexcept
Get the global primitive list (in leaf-traversal order).
PackedBVH(const TreeBVH< T, Q, BV, K > &a_tree, Converter &&a_converter)
Construct by packing a TreeBVH with primitive-type conversion.
PackedBVH(PackedBVH &&a_other) noexcept=default
Move constructor.
PackedBVH(std::vector< std::pair< P, BV > > a_primsAndBVs, BVH::ClusterSpec a_spec)
Construct directly via ClusterSAH: cluster primitives, then SAH over the clusters.
std::vector< Node > m_linearNodes
Flat depth-first node array.
Definition EBGeometry_BVH.hpp:1705
Forward declaration of the tree-structured BVH. Needed by LeafPredicate and the default-partitioner l...
Definition EBGeometry_BVH.hpp:903
BVH::Partitioner< P, BV, K > Partitioner
Alias for the partitioner type.
Definition EBGeometry_BVH.hpp:931
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.
std::vector< BV > m_boundingVolumes
Per-primitive bounding volumes. Non-empty for leaf nodes only.
Definition EBGeometry_BVH.hpp:1175
std::shared_ptr< PackedBVH< T, P, K, StoragePolicy > > pack() const
Flatten this tree into a cache-friendly PackedBVH with the same primitive type.
void bottomUpSortAndPartition()
Recursively partition this node bottom-up along a space-filling curve.
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_BVH.hpp:911
bool m_partitioned
True after topDownSortAndPartition() or bottomUpSortAndPartition() has been called.
Definition EBGeometry_BVH.hpp:1165
std::vector< std::shared_ptr< const P > > m_primitives
Primitives stored in this node. Non-empty for leaf nodes only.
Definition EBGeometry_BVH.hpp:1170
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.
std::array< std::shared_ptr< TreeBVH< T, P, BV, K > >, K > m_children
K child nodes. Non-null for interior nodes only.
Definition EBGeometry_BVH.hpp:1180
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.
BVH::LeafPredicate< T, P, BV, K > LeafPredicate
Alias for the stop-function type.
Definition EBGeometry_BVH.hpp:936
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 noexcept
Recursion-free BVH traversal using an explicit LIFO stack (depth-first order).
std::shared_ptr< PackedBVH< T, Q, K, StoragePolicy > > packWith(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_BVH.hpp:1160
TreeBVH() noexcept
Default constructor. Creates an empty interior node.
const std::array< std::shared_ptr< TreeBVH< T, P, BV, K > >, K > & getChildren() const noexcept
Get the K child nodes of this interior node.
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_BVH.hpp:926
void setChildren(const std::array< std::shared_ptr< TreeBVH< T, P, BV, K > >, K > &a_children) noexcept
Set the K child nodes, converting this node from a leaf into an interior node.
bool isPartitioned() const noexcept
Return true if the tree has already been partitioned.
Axis-aligned bounding box (AABB) enclosing a set of 3D points.
Definition EBGeometry_BoundingVolumes.hpp:247
Three-dimensional vector class with arithmetic operators.
Definition EBGeometry_Vec.hpp:225
static constexpr Vec3T< T > max() noexcept
Return the most-positive representable vector.
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_BVH.hpp:523
auto EqualCounts
Utility: split a vector into K almost-equal contiguous chunks.
Definition EBGeometry_BVH.hpp:408
std::function< std::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_BVH.hpp:311
std::vector< std::shared_ptr< const P > > PrimitiveList
Convenience alias for a list of shared primitive pointers.
Definition EBGeometry_BVH.hpp:125
std::function< bool(const NodeType &a_node, const NodeKey &a_nodeKey)> PrunePredicate
Node-visit predicate for BVH traversal.
Definition EBGeometry_BVH.hpp:362
std::function< void(const std::vector< typename StoragePolicy::StorageType > &a_primitives, size_t a_offset, size_t a_count)> PackedLeafEvaluator
Leaf-evaluation callback for PackedBVH::traverse.
Definition EBGeometry_BVH.hpp:349
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) noexcept
Internal helper: recursively split [a_begin, a_end) into a_K groups via 2-way SAH.
Definition EBGeometry_BVH.hpp:659
std::vector< PrimAndBV< P, BV > > PrimAndBVList
Convenience alias for a list of (primitive, bounding-volume) pairs.
Definition EBGeometry_BVH.hpp:295
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_BVH.hpp:323
std::pair< std::shared_ptr< const P >, BV > PrimAndBV
Convenience alias for a (primitive, bounding-volume) pair.
Definition EBGeometry_BVH.hpp:287
std::function< void(const PrimitiveList< P > &a_primitives)> LeafEvaluator
Leaf-evaluation callback for TreeBVH::traverse.
Definition EBGeometry_BVH.hpp:332
std::function< void(std::array< std::pair< std::shared_ptr< const NodeType >, NodeKey >, K > &a_children)> ChildOrderer
Child-ordering callback for TreeBVH traversal.
Definition EBGeometry_BVH.hpp:375
std::function< NodeKey(const NodeType &a_node)> NodeKeyFactory
Node-key factory called once per node during BVH traversal.
Definition EBGeometry_BVH.hpp:398
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_BVH.hpp:753
auto PrimitiveCentroidPartitioner
Partitioner that sorts primitives by centroid along the longest axis and splits into K pieces.
Definition EBGeometry_BVH.hpp:446
auto BVCentroidPartitioner
Partitioner that sorts primitives by bounding-volume centroid along the longest axis and splits into ...
Definition EBGeometry_BVH.hpp:480
auto BinnedSAHPartitioner
Partitioner using binned SAH with recursive 2-way subdivision into K groups.
Definition EBGeometry_BVH.hpp:716
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) noexcept
Internal helper: recursively split [a_begin, a_end) into a_K groups via 2-way midpoint splits.
Definition EBGeometry_BVH.hpp:802
auto DefaultLeafPredicate
Default stop function: stop partitioning when the node holds fewer than K primitives.
Definition EBGeometry_BVH.hpp:881
Build
Enum for specifying the BVH construction strategy.
Definition EBGeometry_BVH.hpp:48
@ Nested
Bottom-up construction along a Nested space-filling curve.
@ TopDown
Recursive top-down partitioning.
@ Morton
Bottom-up construction along a Morton space-filling curve.
constexpr size_t DefaultBranchingRatio() noexcept
Returns the SIMD-optimal BVH branching factor for type T on the current target ISA.
Definition EBGeometry_BVH.hpp:91
auto MidpointPartitioner
Partitioner that recursively bisects primitives by spatial midpoint (no sorting).
Definition EBGeometry_BVH.hpp:849
std::function< void(std::array< std::pair< uint32_t, NodeKey >, K > &a_children)> PackedChildOrderer
Child-ordering callback for PackedBVH traversal.
Definition EBGeometry_BVH.hpp:386
Namespace containing all of EBGeometry's functionality.
Definition EBGeometry_AnalyticDistanceFunctions.hpp:31
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.
Configuration for the ClusterSAH direct PackedBVH construction path.
Definition EBGeometry_BVH.hpp:69
size_t maxClusterSize
Maximum primitives per cluster (the leaf/bucket granularity). Must be > 0.
Definition EBGeometry_BVH.hpp:70
SoA layout of K children's AABBs for a single interior node.
Definition EBGeometry_BVH.hpp:1719
Compact BVH node stored in the flat node array.
Definition EBGeometry_BVH.hpp:1262
const std::array< uint32_t, K > & getChildOffsets() const noexcept
Get the child index table.
Definition EBGeometry_BVH.hpp:1360
void setBoundingVolume(const BV &a_bv) noexcept
Set the bounding volume for this node.
Definition EBGeometry_BVH.hpp:1288
uint32_t getNumPrimitives() const noexcept
Get the primitive count.
Definition EBGeometry_BVH.hpp:1350
T getDistanceToBoundingVolume2(const Vec3T< T > &a_point) const noexcept
Get the squared distance from a_point to this node's bounding volume.
Definition EBGeometry_BVH.hpp:1395
uint32_t getPrimitivesOffset() const noexcept
Get the primitive offset (leaf nodes only).
Definition EBGeometry_BVH.hpp:1340
void setNumPrimitives(uint32_t a_n) noexcept
Set the primitive count for this leaf node.
Definition EBGeometry_BVH.hpp:1308
const BV & getBoundingVolume() const noexcept
Get the bounding volume.
Definition EBGeometry_BVH.hpp:1330
void setPrimitivesOffset(uint32_t a_off) noexcept
Set the primitive offset for this leaf node.
Definition EBGeometry_BVH.hpp:1298
T getDistanceToBoundingVolume(const Vec3T< T > &a_point) const noexcept
Get the distance from a_point to this node's bounding volume.
Definition EBGeometry_BVH.hpp:1381
void setChildOffset(uint32_t a_off, size_t a_k) noexcept
Set the depth-first index of the k-th child.
Definition EBGeometry_BVH.hpp:1319
bool isLeaf() const noexcept
Return true if this is a leaf node.
Definition EBGeometry_BVH.hpp:1370
Default storage policy for PackedBVH: primitives stored as std::shared_ptr<const P>,...
Definition EBGeometry_BVH.hpp:146
static const P & get(const StorageType &a_stored) noexcept
Dereference stored element to the primitive it refers to.
Definition EBGeometry_BVH.hpp:159
static void appendAliased(std::vector< StorageType > &a_dst, const std::shared_ptr< std::vector< P > > &a_block)
Materialise a converting packWith() constructor's single contiguous conversion buffer into PackedBVH'...
Definition EBGeometry_BVH.hpp:187
static void appendTreeLeaf(std::vector< StorageType > &a_dst, const PrimitiveList< P > &a_leafPrims)
Append one TreeBVH leaf's primitives to PackedBVH's flat primitive array.
Definition EBGeometry_BVH.hpp:172
std::shared_ptr< const P > StorageType
Storage representation: a shared pointer to a const primitive, as PackedBVH has always stored it.
Definition EBGeometry_BVH.hpp:151
Storage policy that stores primitives directly by value, with no pointer indirection at all.
Definition EBGeometry_BVH.hpp:208
static void appendAliased(std::vector< StorageType > &a_dst, const std::shared_ptr< std::vector< P > > &a_block)
Materialise a converting packWith() constructor's single contiguous conversion buffer into PackedBVH'...
Definition EBGeometry_BVH.hpp:261
static void appendTreeLeaf(std::vector< StorageType > &a_dst, const PrimitiveList< P > &a_leafPrims)
Append one TreeBVH leaf's primitives to PackedBVH's flat primitive array.
Definition EBGeometry_BVH.hpp:235
static const P & get(const StorageType &a_stored) noexcept
Identity access – the stored element already is the primitive.
Definition EBGeometry_BVH.hpp:220