Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
TBvhBuilder.ipp
Go to the documentation of this file.
1#pragma once
2
7#include "Math/math.h"
9
10#include <Common/assertion.h>
11#include <Common/logging.h>
12#include <Common/utility.h>
13
14#include <algorithm>
15#include <cmath>
16
17namespace ph::math
18{
19
20template<std::size_t N, typename Item, typename ItemToAABB>
21inline TBvhBuilder<N, Item, ItemToAABB>
22::TBvhBuilder(
23 ItemToAABB itemToAABB,
24 BvhParams params)
25
26 : m_infoBuffer()
27 , m_infoNodes()
28 , m_params(params)
29 , m_itemToAABB(std::move(itemToAABB))
30{
31 PH_ASSERT_IN_RANGE_INCLUSIVE(params.numSahBuckets, 2, MAX_SAH_BUCKETS);
32}
33
34template<std::size_t N, typename Item, typename ItemToAABB>
37-> const InfoNodeType*
38{
39 clearBuildData();
40
41 m_infoBuffer.resize(items.size());
42 for(std::size_t i = 0; i < items.size(); i++)
43 {
44 m_infoBuffer[i] = ItemInfoType(m_itemToAABB(items[i]), items[i]);
45 }
46
47 // To have stable node pointers, pre-allocate enough nodes beforehand. We calculate the maximum
48 // nodes required for a BVH with maximum items in node = 1 and branch factor = 2. This is quite
49 // pessimistic for wide BVHs, and a tighter bound is left for future work.
50 const std::size_t maxLeaves = items.size();
51 const std::size_t maxNodes = 2 * maxLeaves - 1;
52 m_infoNodes.reserve(maxNodes);
53
54 const InfoNodeType* rootNode = nullptr;
55 switch(m_params.splitMethod)
56 {
58 rootNode = buildBvhInfoNodeRecursive<EBvhNodeSplitMethod::EqualItems>(
59 m_infoBuffer);
60 break;
61
63 rootNode = buildBvhInfoNodeRecursive<EBvhNodeSplitMethod::SAH_Buckets_OneAxis>(
64 m_infoBuffer);
65 break;
66
67 default:
68 PH_DEFAULT_LOG(Warning,
69 "BVH{} builder: unsupported BVH split method", N);
70 break;
71 }
72
73 PH_ASSERT_EQ(m_infoBuffer.size(), items.size());
74 PH_ASSERT_LE(m_infoNodes.size(), maxNodes);
75
76 // Verify the nodes by doing a full traversal
77 PH_ASSERT_EQ(m_infoNodes.size(), calcTotalNodes(rootNode));
78 PH_ASSERT_EQ(m_infoBuffer.size(), calcTotalItems(rootNode));
79
80 // As noted earlier, we appreciate a tighter bound
81 PH_DEFAULT_DEBUG_LOG(
82 "BVH{} builder: node buffer utilization = {}",
83 N, static_cast<float>(m_infoNodes.size()) / maxNodes);
84
85 return rootNode;
86}
87
88template<std::size_t N, typename Item, typename ItemToAABB>
91{
92 m_infoBuffer.clear();
93 m_infoNodes.clear();
94}
95
96template<std::size_t N, typename Item, typename ItemToAABB>
99-> std::size_t
100{
101 return m_infoNodes.size();
102}
103
104template<std::size_t N, typename Item, typename ItemToAABB>
107-> std::size_t
108{
109 return m_infoBuffer.size();
110}
111
112template<std::size_t N, typename Item, typename ItemToAABB>
113template<EBvhNodeSplitMethod SPLIT_METHOD>
116 const TSpan<ItemInfoType> itemInfos)
117-> const InfoNodeType*
118{
119 // Creating a new node must not cause reallocation
120 PH_ASSERT_LT(m_infoNodes.size(), m_infoNodes.capacity());
121
122 m_infoNodes.push_back(InfoNodeType{});
123 InfoNodeType* const node = &m_infoNodes.back();
124
125 AABB3D nodeAABB(itemInfos.empty() ? AABB3D(Vector3R(0)) : itemInfos.front().aabb);
126 for(const ItemInfoType& itemInfo : itemInfos)
127 {
128 nodeAABB = AABB3D::makeUnioned(nodeAABB, itemInfo.aabb);
129 }
130
131 // Makes no sense to split
132 if(itemInfos.size() <= 1)
133 {
134 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
135
136#if PH_DEBUG
137 if(itemInfos.empty())
138 {
139 PH_DEFAULT_LOG(Warning,
140 "BVH{} builder: leaf node without item detected", N);
141 }
142#endif
143 }
144 // Try to split with `SPLIT_METHOD`
145 else
146 {
147 AABB3D centroidsAABB(itemInfos.front().aabbCentroid);
148 for(const ItemInfoType& itemInfo : itemInfos)
149 {
150 centroidsAABB = AABB3D::makeUnioned(centroidsAABB, AABB3D(itemInfo.aabbCentroid));
151 }
152
153 Vector3R extents = centroidsAABB.getExtents();
154#if PH_DEBUG
155 if(!extents.isNonNegative())
156 {
157 PH_DEFAULT_LOG(Warning,
158 "BVH{} builder: negative AABB extent detected", N);
159 extents.absLocal();
160 }
161#endif
162
163 const auto maxDimension = extents.maxDimension();
164 if(centroidsAABB.getMinVertex()[maxDimension] == centroidsAABB.getMaxVertex()[maxDimension])
165 {
166 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
167 }
168 // Specialized binary node splitting
169 else if constexpr(N == 2)
170 {
171 bool isSplitted = false;
172 TSpan<ItemInfoType> negativeChildItems;
173 TSpan<ItemInfoType> positiveChildItems;
174 if constexpr(SPLIT_METHOD == EBvhNodeSplitMethod::EqualItems)
175 {
176 isSplitted = binarySplitWithEqualItems(
177 itemInfos,
178 maxDimension,
179 &negativeChildItems,
180 &positiveChildItems);
181 }
182 else if constexpr(SPLIT_METHOD == EBvhNodeSplitMethod::SAH_Buckets_OneAxis)
183 {
184 isSplitted = binarySplitWithSahBuckets(
185 itemInfos,
186 maxDimension,
187 nodeAABB,
188 centroidsAABB,
189 &negativeChildItems,
190 &positiveChildItems);
191
192 // Potentially fallback to equal-item split if needed
193 if(!isSplitted && itemInfos.size() > m_params.maxNodeItems)
194 {
195 isSplitted = binarySplitWithEqualItems(
196 itemInfos,
197 maxDimension,
198 &negativeChildItems,
199 &positiveChildItems);
200 }
201 }
202 else
203 {
204 PH_DEFAULT_DEBUG_LOG(
205 "BVH{} builder: unsupported BVH split method detected", N);
206 isSplitted = false;
207 }// end split method
208
209#if PH_DEBUG
210 if(isSplitted && (negativeChildItems.empty() || positiveChildItems.empty()))
211 {
212 PH_DEFAULT_DEBUG_LOG(
213 "BVH{} builder: bad binary split detected: #neg-child={}, #pos-child={}",
214 N, negativeChildItems.size(), positiveChildItems.size());
215 isSplitted = false;
216 }
217#endif
218
219 if(isSplitted)
220 {
221 *node = InfoNodeType::makeInternal(
222 {
223 buildBvhInfoNodeRecursive<SPLIT_METHOD>(negativeChildItems),
224 buildBvhInfoNodeRecursive<SPLIT_METHOD>(positiveChildItems)
225 },
226 maxDimension);
227 }
228 else
229 {
230 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
231 }
232 }
233 // Generalized N-wide node splitting
234 else
235 {
236 bool isSplitted = false;
237 std::array<TSpan<ItemInfoType>, N> itemParts;
238 if constexpr(SPLIT_METHOD == EBvhNodeSplitMethod::EqualItems)
239 {
240 isSplitted = splitWithEqualItems(
241 itemInfos,
242 maxDimension,
243 &itemParts);
244 }
245 else if constexpr(SPLIT_METHOD == EBvhNodeSplitMethod::SAH_Buckets_OneAxis)
246 {
247 isSplitted = splitWithSahBuckets(
248 itemInfos,
249 maxDimension,
250 nodeAABB,
251 centroidsAABB,
252 &itemParts);
253
254 // Potentially fallback to equal-item split if needed
255 if(!isSplitted && itemInfos.size() > m_params.maxNodeItems)
256 {
257 isSplitted = splitWithEqualItems(
258 itemInfos,
259 maxDimension,
260 &itemParts);
261 }
262 }
263 else
264 {
265 PH_DEFAULT_DEBUG_LOG(
266 "BVH{} builder: unsupported BVH split method detected", N);
267 isSplitted = false;
268 }// end split method
269
270 if(isSplitted)
271 {
272 std::array<const InfoNodeType*, N> children{};
273 for(std::size_t ci = 0; ci < N; ++ci)
274 {
275 if(!itemParts[ci].empty())
276 {
277 children[ci] = buildBvhInfoNodeRecursive<SPLIT_METHOD>(itemParts[ci]);
278 }
279 }
280
281 *node = InfoNodeType::makeInternal(
282 children,
283 maxDimension);
284 }
285 else
286 {
287 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
288 }
289 }
290 }
291
292 return node;
293}
294
295template<std::size_t N, typename Item, typename ItemToAABB>
296inline std::size_t TBvhBuilder<N, Item, ItemToAABB>
297::calcTotalNodes(const InfoNodeType* const node)
298{
299 if(!node)
300 {
301 return 0;
302 }
303
304 std::size_t result = 1;
305 for(std::size_t ci = 0; ci < node->numChildren(); ++ci)
306 {
307 result += node->getChild(ci) ? calcTotalNodes(node->getChild(ci)) : 0;
308 }
309 return result;
310}
311
312template<std::size_t N, typename Item, typename ItemToAABB>
313inline std::size_t TBvhBuilder<N, Item, ItemToAABB>
315{
316 if(!node)
317 {
318 return 0;
319 }
320
321 std::size_t result = node->getItems().size();
322 for(std::size_t ci = 0; ci < node->numChildren(); ++ci)
323 {
324 result += node->getChild(ci) ? calcTotalItems(node->getChild(ci)) : 0;
325 }
326 return result;
327}
328
329template<std::size_t N, typename Item, typename ItemToAABB>
330inline std::size_t TBvhBuilder<N, Item, ItemToAABB>
331::calcMaxDepth(const InfoNodeType* const node)
332{
333 if(!node)
334 {
335 return 0;
336 }
337
338 std::size_t maxDepth = 0;
339 for(std::size_t ci = 0; ci < node->numChildren(); ++ci)
340 {
341 // Only non-empty child can add one more depth
342 if(node->getChild(ci))
343 {
344 maxDepth = std::max(calcMaxDepth(node->getChild(ci)) + 1, maxDepth);
345 }
346 }
347 return maxDepth;
348}
349
350template<std::size_t N, typename Item, typename ItemToAABB>
353 const TSpan<ItemInfoType> itemInfos,
354 const std::size_t splitDimension,
355 TSpan<ItemInfoType>* const out_negativePart,
356 TSpan<ItemInfoType>* const out_positivePart)
357{
358 static_assert(N == 2, "Requires a binary BVH builder.");
359
360 // Binary variant is more strict: cannot split if number of items < 2
361 PH_ASSERT_GE(itemInfos.size(), 2);
362
363 const std::size_t midIndex = itemInfos.size() / 2 - 1;
364
365 auto sortedItemInfos = itemInfos;
366 std::nth_element(
367 sortedItemInfos.begin(),
368 sortedItemInfos.begin() + midIndex,
369 sortedItemInfos.end(),
370 [splitDimension](const ItemInfoType& a, const ItemInfoType& b)
371 {
372 return a.aabbCentroid[splitDimension] < b.aabbCentroid[splitDimension];
373 });
374
375 PH_ASSERT(out_negativePart);
376 PH_ASSERT(out_positivePart);
377 *out_negativePart = sortedItemInfos.subspan(0, midIndex + 1);
378 *out_positivePart = sortedItemInfos.subspan(midIndex + 1);
379
380 PH_ASSERT(!out_negativePart->empty() && !out_positivePart->empty());
381 return true;
382}
383
384template<std::size_t N, typename Item, typename ItemToAABB>
387 const TSpan<ItemInfoType> itemInfos,
388 const std::size_t splitDimension,
389 const AABB3D& itemsAABB,
390 const AABB3D& itemsCentroidAABB,
391 TSpan<ItemInfoType>* const out_negativePart,
392 TSpan<ItemInfoType>* const out_positivePart)
393{
394 static_assert(N == 2, "Requires a binary BVH builder.");
395
396 // Binary variant is more strict: cannot split if number of items < 2
397 PH_ASSERT_GE(itemInfos.size(), 2);
398
399 const auto dim = splitDimension;
400 const real rcpSplitExtent = safe_rcp(itemsCentroidAABB.getExtents()[dim]);
401
402 PH_ASSERT_GE(rcpSplitExtent, 0.0_r);
403
404 std::array<SahBucket, MAX_SAH_BUCKETS> buckets{};
405 for(const ItemInfoType& itemInfo : itemInfos)
406 {
407 const real factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
408 const auto bucketIndex = clamp<std::size_t>(static_cast<std::size_t>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
409
410 buckets[bucketIndex].aabb = buckets[bucketIndex].isEmpty()
411 ? itemInfo.aabb: buckets[bucketIndex].aabb.unionWith(itemInfo.aabb);
412 buckets[bucketIndex].numItems++;
413 }
414
415 std::array<real, MAX_SAH_BUCKETS - 1> splitCosts{};
416 for(std::size_t i = 0; i < m_params.numSahBuckets - 1; ++i)
417 {
418 std::size_t numNegPartItems = 0;
419 auto negPartAABB = AABB3D::makeEmpty();
420 for(std::size_t j = 0; j <= i; ++j)
421 {
422 numNegPartItems += buckets[j].numItems;
423 negPartAABB.unionWith(buckets[j].aabb);
424 }
425
426 std::size_t numPosPartItems = 0;
427 auto posPartAABB = AABB3D::makeEmpty();
428 for(std::size_t j = i + 1; j < m_params.numSahBuckets; ++j)
429 {
430 numPosPartItems += buckets[j].numItems;
431 posPartAABB.unionWith(buckets[j].aabb);
432 }
433
434 // Safe clamping probabilities to [0, 1] as the AABBs may be empty, point, plane,
435 // or being super large to be Inf/NaN
436 const real probTestingNegPart = safe_clamp(
437 negPartAABB.getSurfaceArea() / itemsAABB.getSurfaceArea(), 0.0_r, 1.0_r);
438 const real probTestingPosPart = safe_clamp(
439 posPartAABB.getSurfaceArea() / itemsAABB.getSurfaceArea(), 0.0_r, 1.0_r);
440
441 splitCosts[i] =
442 m_params.traversalCost +
443 static_cast<real>(numNegPartItems) * m_params.interactCost * probTestingNegPart +
444 static_cast<real>(numPosPartItems) * m_params.interactCost * probTestingPosPart;
445 }
446
447 const auto minCostIndex = static_cast<std::size_t>(
448 std::min_element(splitCosts.begin(), splitCosts.begin() + m_params.numSahBuckets) - splitCosts.begin());
449 const real minSplitCost = splitCosts[minCostIndex];
450 const real noSplitCost = m_params.interactCost * static_cast<real>(itemInfos.size());
451
452 PH_ASSERT(out_negativePart);
453 PH_ASSERT(out_positivePart);
454 if(minSplitCost < noSplitCost || itemInfos.size() > m_params.maxNodeItems)
455 {
456 auto sortedItemInfos = itemInfos;
457 auto posPartBegin = std::partition(
458 sortedItemInfos.begin(),
459 sortedItemInfos.end(),
460 [this, itemsCentroidAABB, dim, rcpSplitExtent, minCostIndex](const ItemInfoType& itemInfo)
461 {
462 const real factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
463 const auto bucketIndex = clamp<std::size_t>(static_cast<std::size_t>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
464 return bucketIndex <= minCostIndex;
465 });
466
467 *out_negativePart = sortedItemInfos.subspan(0, posPartBegin - sortedItemInfos.begin());
468 *out_positivePart = sortedItemInfos.subspan(posPartBegin - sortedItemInfos.begin());
469 return !out_negativePart->empty() && !out_positivePart->empty();
470 }
471 else
472 {
473 return false;
474 }
475}
476
477template<std::size_t N, typename Item, typename ItemToAABB>
478inline bool TBvhBuilder<N, Item, ItemToAABB>
479::splitWithEqualItems(
480 TSpan<ItemInfoType> itemInfos,
481 const std::size_t splitDimension,
482 std::array<TSpan<ItemInfoType>, N>* const out_parts)
483{
484 PH_ASSERT(out_parts);
485
486 // Partition `itemInfos` into N parts. This is done by a O(n*N/2) method where n is the number
487 // of items. Divide and conquer can achieve O(n*logN), but the gain should only be significant
488 // for N > 4. We keep this simpler approach for now.
489 auto sortedItemInfos = itemInfos;
490 bool hasAnySplit = true;
491 for(std::size_t i = 0; i < N; ++i)
492 {
493 const auto [beginIdx, endIdx] = ith_evenly_divided_range(i, itemInfos.size(), N);
494
495 // No need to rearrange for empty range and the last part
496 if(beginIdx < endIdx && i < N - 1)
497 {
498 std::nth_element(
499 sortedItemInfos.begin() + beginIdx,
500 sortedItemInfos.begin() + endIdx - 1,// nth, the last element in this interval
501 sortedItemInfos.end(),
502 [splitDimension](const ItemInfoType& a, const ItemInfoType& b)
503 {
504 return a.aabbCentroid[splitDimension] < b.aabbCentroid[splitDimension];
505 });
506 }
507
508 (*out_parts)[i] = sortedItemInfos.subspan(beginIdx, endIdx - beginIdx);
509 hasAnySplit = hasAnySplit && (*out_parts)[i].size() < itemInfos.size();
510 }
511 return hasAnySplit;
512}
513
514template<std::size_t N, typename Item, typename ItemToAABB>
515inline bool TBvhBuilder<N, Item, ItemToAABB>
516::splitWithSahBuckets(
517 const TSpan<ItemInfoType> itemInfos,
518 const std::size_t splitDimension,
519 const AABB3D& itemsAABB,
520 const AABB3D& itemsCentroidAABB,
521 std::array<TSpan<ItemInfoType>, N>* const out_parts)
522{
523 PH_ASSERT(out_parts);
524
525 const auto dim = splitDimension;
526 const auto rcpSplitExtent = safe_rcp(itemsCentroidAABB.getExtents()[dim]);
527
528 PH_ASSERT_GE(rcpSplitExtent, 0.0_r);
529
530 std::array<SahBucket, MAX_SAH_BUCKETS> buckets{};
531 for(const ItemInfoType& itemInfo : itemInfos)
532 {
533 const real factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
534 const auto bucketIndex = clamp<std::size_t>(static_cast<std::size_t>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
535
536 buckets[bucketIndex].aabb = buckets[bucketIndex].isEmpty()
537 ? itemInfo.aabb: buckets[bucketIndex].aabb.unionWith(itemInfo.aabb);
538 buckets[bucketIndex].numItems++;
539 }
540
541 const auto noSplitCost = static_cast<real>(m_params.interactCost * itemInfos.size());
542 const auto emptySplitEnds = make_array<std::size_t, N>(m_params.numSahBuckets);
543
544 auto bestSplitCost = noSplitCost;
545 auto bestSplitEnds = emptySplitEnds;
546 splitWithSahBucketsBacktracking(
547 dim,
548 itemsAABB,
549 {buckets.begin(), m_params.numSahBuckets},
550 0,
551 0,
552 emptySplitEnds,
553 m_params.traversalCost,
554 &bestSplitCost,
555 &bestSplitEnds);
556
557 if(bestSplitCost < noSplitCost || itemInfos.size() > m_params.maxNodeItems)
558 {
559 // Partition `itemInfos` into N parts. The complexity of the current implementation is similar
560 // to `splitWithEqualItems()`.
561 auto sortedItemInfos = itemInfos;
562 bool hasAnySplit = true;
563 for(std::size_t i = 0; i < N; ++i)
564 {
565 const auto splitBegin = i > 0 ? bestSplitEnds[i - 1] : 0;
566 const auto splitEnd = bestSplitEnds[i];
567
568 // No need to rearrange for empty range and the last part
569 auto nextPartBegin = sortedItemInfos.end();
570 if(splitBegin < splitEnd && i < N - 1)
571 {
572 nextPartBegin = std::partition(
573 sortedItemInfos.begin(),
574 sortedItemInfos.end(),
575 [this, itemsCentroidAABB, dim, rcpSplitExtent, splitEnd](const ItemInfoType& itemInfo)
576 {
577 const auto factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
578 const auto bucketIndex = clamp<std::size_t>(static_cast<std::size_t>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
579 return bucketIndex < splitEnd;
580 });
581 }
582
583 const auto numPartItems = nextPartBegin - sortedItemInfos.begin();
584 (*out_parts)[i] = sortedItemInfos.subspan(0, numPartItems);
585 sortedItemInfos = sortedItemInfos.subspan(numPartItems);
586 hasAnySplit = hasAnySplit && (*out_parts)[i].size() < itemInfos.size();
587 }
588 return hasAnySplit;
589 }
590 else
591 {
592 return false;
593 }
594}
595
596template<std::size_t N, typename Item, typename ItemToAABB>
597inline void TBvhBuilder<N, Item, ItemToAABB>
598::splitWithSahBucketsBacktracking(
599 const std::size_t splitDimension,
600 const AABB3D& itemsAABB,
601 const TSpanView<SahBucket> buckets,
602 const std::size_t numSplits,
603 const std::size_t splitBegin,
604 const std::array<std::size_t, N>& splitEnds,
605 const real cost,
606 real* const out_bestCost,
607 std::array<std::size_t, N>* const out_bestSplitEnds) const
608{
609 PH_ASSERT(out_bestCost);
610 PH_ASSERT(out_bestSplitEnds);
611
612 // No need to explore further if cost is already too high
613 if(cost >= *out_bestCost)
614 {
615 return;
616 }
617
618 // Finish due to running out of bucket or no more split can be made
619 if(splitBegin == buckets.size() || numSplits == N)
620 {
621 PH_ASSERT_LT(cost, *out_bestCost);
622 *out_bestCost = cost;
623 *out_bestSplitEnds = splitEnds;
624
625 return;
626 }
627
628 PH_ASSERT_LT(splitBegin, buckets.size());
629 PH_ASSERT_LT(numSplits, N);
630
631 // If this is the last split, the only choice is taking all remaining buckets
632 std::size_t splitEnd = numSplits == N - 1 ? buckets.size() : splitBegin + 1;
633
634 // Recursively explore all possible split ends
635 while(splitEnd <= buckets.size())
636 {
637 auto newSplitEnds = splitEnds;
638 newSplitEnds[numSplits] = splitEnd;
639
640 std::size_t numItems = 0;
641 auto aabb = AABB3D::makeEmpty();
642 for(std::size_t bi = splitBegin; bi < splitEnd; ++bi)
643 {
644 numItems += buckets[bi].numItems;
645 aabb.unionWith(buckets[bi].aabb);
646 }
647
648 // Safe clamping probabilities to [0, 1] as the AABBs may be empty, point, plane,
649 // or being super large to be Inf/NaN
650 const auto testProb = safe_clamp(
651 aabb.getSurfaceArea() / itemsAABB.getSurfaceArea(), 0.0_r, 1.0_r);
652
653 const auto newCost = static_cast<real>(
654 cost + numItems * m_params.interactCost * testProb);
655
656 splitWithSahBucketsBacktracking(
657 splitDimension,
658 itemsAABB,
659 buckets,
660 numSplits + 1,
661 splitEnd,
662 newSplitEnds,
663 newCost,
664 out_bestCost,
665 out_bestSplitEnds);
666
667 // If new cost is already worse, all further splits will only be of higher costs
668 if(newCost >= *out_bestCost)
669 {
670 break;
671 }
672
673 ++splitEnd;
674 }
675}
676
677}// end namespace ph::math
Definition BvhParams.h:20
uint32 numSahBuckets
Definition BvhParams.h:26
T getSurfaceArea() const
Get the surface area of the bound.
Definition TAABB3D.ipp:177
static TAABB3D makeUnioned(const TAABB3D &a, const TAABB3D &b)
Definition TAABB3D.ipp:26
static TAABB3D makeEmpty()
Definition TAABB3D.ipp:15
TVector3< T > getExtents() const
Get the side lengths of the bound.
Definition TAABB3D.ipp:171
const TVector3< T > & getMinVertex() const
Get the corner vertex of the minimum (—) octant.
Definition TAABB3D.ipp:146
Definition TBvhBuilder.h:25
General BVH node packed with additional information. This node type is typically used for building ot...
Definition TLinearDepthFirstWideBvh.h:21
auto getItems() const -> TSpanView< ItemInfoType >
Definition TBvhInfoNode.ipp:137
auto getChild(std::size_t childIdx) const -> const TBvhInfoNode *
Definition TBvhInfoNode.ipp:127
static constexpr std::size_t numChildren()
Definition TBvhInfoNode.ipp:65
Definition TBvhItemInfo.h:11
Miscellaneous math utilities.
Math functions and utilities.
Definition TransformInfo.h:10
T safe_clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound], fallback to lowerBound. If a floating-point value is non-...
Definition math.h:98
T safe_rcp(const T value)
Calculates the reciprocal of a value, fallback to 0. If the resulting reciprocal is non-finite (e....
Definition math.h:117
TAABB3D< real > AABB3D
Definition TAABB3D.h:21
TVector3< real > Vector3R
Definition math_fwd.h:52
std::pair< std::size_t, std::size_t > ith_evenly_divided_range(const std::size_t rangeIndex, const std::size_t totalSize, const std::size_t numDivisions)
Gets the i-th evenly divided range.
Definition math.h:379
T clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound]. None of value, lowerBound and upperBound can be NaN,...
Definition math.h:77
std::span< const T, EXTENT > TSpanView
Same as TSpan, except that the objects are const-qualified. Note that for pointer types,...
Definition TSpan.h:19
std::span< T, EXTENT > TSpan
A contiguous sequence of objects of type T. Effectively the same as std::span.
Definition TSpan.h:12
Definition TAABB2D.h:96