KiCad PCB EDA Suite
Loading...
Searching...
No Matches
bvh_pbrt.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2016 Mario Luzeiro <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
64
65#include "bvh_pbrt.h"
67#include <macros.h>
68
69#include <boost/range/algorithm/nth_element.hpp>
70#include <boost/range/algorithm/partition.hpp>
71#include <cstdlib>
72#include <vector>
73
74#include <stack>
75#include <wx/debug.h>
76
77#ifdef PRINT_STATISTICS_3D_VIEWER
78#include <stdio.h>
79#endif
80
81// BVHAccel Local Declarations
83{
85 {
87 bounds.Reset();
88 centroid = SFVEC3F( 0.0f );
89 }
90
91 BVHPrimitiveInfo( int aPrimitiveNumber, const BBOX_3D& aBounds ) :
92 primitiveNumber( aPrimitiveNumber ),
93 bounds( aBounds ),
94 centroid( .5f * aBounds.Min() + .5f * aBounds.Max() ) {}
95
99};
100
101
103{
104 // BVHBuildNode Public Methods
105 void InitLeaf( int first, int n, const BBOX_3D& b)
106 {
107 firstPrimOffset = first;
108 nPrimitives = n;
109 bounds = b;
110 children[0] = children[1] = nullptr;
111 }
112
113 void InitInterior( int axis, BVHBuildNode* c0, BVHBuildNode* c1 )
114 {
115 children[0] = c0;
116 children[1] = c1;
117 bounds.Set( c0->bounds );
118 bounds.Union( c1->bounds );
119 splitAxis = axis;
120 nPrimitives = 0;
121 }
122
126};
127
128
130{
132 uint32_t mortonCode;
133};
134
135
141
142
143// BVHAccel Utility Functions
144inline uint32_t LeftShift3( uint32_t x )
145{
146 wxASSERT( x <= (1 << 10) );
147
148 if( x == ( 1 << 10 ) )
149 --x;
150
151 x = ( x | ( x << 16 ) ) & 0b00000011000000000000000011111111;
152 // x = ---- --98 ---- ---- ---- ---- 7654 3210
153 x = ( x | ( x << 8 ) ) & 0b00000011000000001111000000001111;
154 // x = ---- --98 ---- ---- 7654 ---- ---- 3210
155 x = ( x | ( x << 4 ) ) & 0b00000011000011000011000011000011;
156 // x = ---- --98 ---- 76-- --54 ---- 32-- --10
157 x = ( x | ( x << 2 ) ) & 0b00001001001001001001001001001001;
158 // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
159
160 return x;
161}
162
163
164inline uint32_t EncodeMorton3( const SFVEC3F& v )
165{
166 wxASSERT( v.x >= 0 && v.x <= ( 1 << 10 ) );
167 wxASSERT( v.y >= 0 && v.y <= ( 1 << 10 ) );
168 wxASSERT( v.z >= 0 && v.z <= ( 1 << 10 ) );
169
170 return ( LeftShift3( v.z ) << 2 ) | ( LeftShift3( v.y ) << 1 ) | LeftShift3( v.x );
171}
172
173
174static void RadixSort( std::vector<MortonPrimitive>* v )
175{
176 std::vector<MortonPrimitive> tempVector( v->size() );
177
178 const int bitsPerPass = 6;
179 const int nBits = 30;
180
181 wxASSERT( ( nBits % bitsPerPass ) == 0 );
182
183 const int nPasses = nBits / bitsPerPass;
184
185 for( int pass = 0; pass < nPasses; ++pass )
186 {
187 // Perform one pass of radix sort, sorting _bitsPerPass_ bits
188 const int lowBit = pass * bitsPerPass;
189
190 // Set in and out vector pointers for radix sort pass
191 std::vector<MortonPrimitive>& in = ( pass & 1 ) ? tempVector : *v;
192 std::vector<MortonPrimitive>& out = ( pass & 1 ) ? *v : tempVector;
193
194 // Count number of zero bits in array for current radix sort bit
195 const int nBuckets = 1 << bitsPerPass;
196 int bucketCount[nBuckets] = {0};
197 const int bitMask = ( 1 << bitsPerPass ) - 1;
198
199 for( uint32_t i = 0; i < in.size(); ++i )
200 {
201 const MortonPrimitive &mp = in[i];
202 int bucket = ( mp.mortonCode >> lowBit ) & bitMask;
203
204 wxASSERT( ( bucket >= 0 ) && ( bucket < nBuckets ) );
205
206 ++bucketCount[bucket];
207 }
208
209 // Compute starting index in output array for each bucket
210 int startIndex[nBuckets];
211 startIndex[0] = 0;
212
213 for( int i = 1; i < nBuckets; ++i )
214 startIndex[i] = startIndex[i - 1] + bucketCount[i - 1];
215
216 // Store sorted values in output array
217 for( uint32_t i = 0; i < in.size(); ++i )
218 {
219 const MortonPrimitive& mp = in[i];
220 int bucket = (mp.mortonCode >> lowBit) & bitMask;
221 out[startIndex[bucket]++] = mp;
222 }
223 }
224
225 // Copy final result from _tempVector_, if needed
226 if( nPasses & 1 )
227 std::swap( *v, tempVector );
228}
229
230
231BVH_PBRT::BVH_PBRT( const CONTAINER_3D_BASE& aObjectContainer, int aMaxPrimsInNode,
232 SPLITMETHOD aSplitMethod ) :
233 m_maxPrimsInNode( std::min( 255, aMaxPrimsInNode ) ),
234 m_splitMethod( aSplitMethod )
235{
236 if( aObjectContainer.GetList().empty() )
237 {
238 m_nodes = nullptr;
239
240 return;
241 }
242
243 // Initialize the indexes of ray packet for partition traversal
244 for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i )
245 {
246 m_I[i] = i;
247 }
248
249 // Convert the objects list to vector of objects
250 aObjectContainer.ConvertTo( m_primitives );
251
252 wxASSERT( aObjectContainer.GetList().size() == m_primitives.size() );
253
254 // Initialize _primitiveInfo_ array for primitives
255 std::vector<BVHPrimitiveInfo> primitiveInfo( m_primitives.size() );
256
257 for( size_t i = 0; i < m_primitives.size(); ++i )
258 {
259 wxASSERT( m_primitives[i]->GetBBox().IsInitialized() );
260
261 primitiveInfo[i] = BVHPrimitiveInfo( i, m_primitives[i]->GetBBox() );
262 }
263
264 // Build BVH tree for primitives using _primitiveInfo_
265 int totalNodes = 0;
266
267 std::vector<const OBJECT_3D*> orderedPrims;
268 orderedPrims.clear();
269 orderedPrims.reserve( m_primitives.size() );
270
271 BVHBuildNode *root;
272
274 root = HLBVHBuild( primitiveInfo, &totalNodes, orderedPrims );
275 else
276 root = recursiveBuild( primitiveInfo, 0, m_primitives.size(), &totalNodes, orderedPrims );
277
278 wxASSERT( m_primitives.size() == orderedPrims.size() );
279
280 m_primitives.swap( orderedPrims );
281
282 // Compute representation of depth-first traversal of BVH tree
283 m_nodes = static_cast<LinearBVHNode*>( malloc( sizeof( LinearBVHNode ) * totalNodes ) );
284 m_nodesToFree.push_back( m_nodes );
285
286 for( int i = 0; i < totalNodes; ++i )
287 {
288 m_nodes[i].bounds.Reset();
289 m_nodes[i].primitivesOffset = 0;
290 m_nodes[i].nPrimitives = 0;
291 m_nodes[i].axis = 0;
292 }
293
294 uint32_t offset = 0;
295
296 flattenBVHTree( root, &offset );
297
298 wxASSERT( offset == (unsigned int)totalNodes );
299}
300
301
303{
304 if( !m_nodesToFree.empty() )
305 {
306 for( std::list<void *>::iterator ii = m_nodesToFree.begin(); ii != m_nodesToFree.end();
307 ++ii )
308 {
309 free( *ii );
310 *ii = nullptr;
311 }
312 }
313
314 m_nodesToFree.clear();
315}
316
317
319{
320 explicit ComparePoints( int d ) { dim = d; }
321
322 int dim;
323
324 bool operator()( const BVHPrimitiveInfo& a, const BVHPrimitiveInfo& b ) const
325 {
326 return a.centroid[dim] < b.centroid[dim];
327 }
328};
329
330
332{
333 explicit CompareToMid( int d, float m ) { dim = d; mid = m; }
334
335 int dim;
336 float mid;
337
338 bool operator()( const BVHPrimitiveInfo& a ) const
339 {
340 return a.centroid[dim] < mid;
341 }
342};
343
344
346{
347 CompareToBucket( int split, int num, int d, const BBOX_3D& b )
348 : centroidBounds( b )
349 {
351 nBuckets = num;
352 dim = d;
353 }
354
355 bool operator()( const BVHPrimitiveInfo& p ) const;
356
358
360};
361
362
364{
365 const float centroid = p.centroid[dim];
366
367 int b = nBuckets *
368 // Computes the offset (0.0 - 1.0) for one axis
369 ( ( centroid - centroidBounds.Min()[dim] ) /
370 ( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) );
371
372 if( b == nBuckets )
373 b = nBuckets - 1;
374
375 wxASSERT( ( b >= 0 ) && ( b < nBuckets ) );
376
377 return b <= splitBucket;
378}
379
380
382{
383 HLBVH_SAH_Evaluator( int split, int num, int d, const BBOX_3D& b )
384 : centroidBounds(b)
385 {
387 nBuckets = num; dim = d;
388 }
389
390 bool operator()( const BVHBuildNode* node ) const;
391
394};
395
396
398{
399 const float centroid = node->bounds.GetCenter( dim );
400
401 int b = nBuckets *
402 // Computes the offset (0.0 - 1.0) for one axis
403 ( ( centroid - centroidBounds.Min()[dim] ) /
404 ( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) );
405
406 if( b == nBuckets )
407 b = nBuckets - 1;
408
409 wxASSERT( b >= 0 && b < nBuckets );
410
411 return b <= minCostSplitBucket;
412}
413
414
416{
417 int count;
419};
420
421
422BVHBuildNode *BVH_PBRT::recursiveBuild ( std::vector<BVHPrimitiveInfo>& primitiveInfo,
423 int start, int end, int* totalNodes,
424 std::vector<const OBJECT_3D*>& orderedPrims )
425{
426 wxASSERT( totalNodes != nullptr );
427 wxASSERT( start >= 0 );
428 wxASSERT( end >= 0 );
429 wxASSERT( start != end );
430 wxASSERT( start < end );
431 wxASSERT( start <= (int)primitiveInfo.size() );
432 wxASSERT( end <= (int)primitiveInfo.size() );
433
434 (*totalNodes)++;
435
436 // !TODO: implement a memory arena
437 BVHBuildNode *node = static_cast<BVHBuildNode *>( malloc( sizeof( BVHBuildNode ) ) );
438 m_nodesToFree.push_back( node );
439
440 node->bounds.Reset();
441 node->firstPrimOffset = 0;
442 node->nPrimitives = 0;
443 node->splitAxis = 0;
444 node->children[0] = nullptr;
445 node->children[1] = nullptr;
446
447 // Compute bounds of all primitives in BVH node
448 BBOX_3D bounds;
449 bounds.Reset();
450
451 for( int i = start; i < end; ++i )
452 bounds.Union( primitiveInfo[i].bounds );
453
454 int nPrimitives = end - start;
455
456 if( nPrimitives == 1 )
457 {
458 // Create leaf _BVHBuildNode_
459 int firstPrimOffset = orderedPrims.size();
460
461 for( int i = start; i < end; ++i )
462 {
463 int primitiveNr = primitiveInfo[i].primitiveNumber;
464 wxASSERT( primitiveNr < (int)m_primitives.size() );
465 orderedPrims.push_back( m_primitives[ primitiveNr ] );
466 }
467
468 node->InitLeaf( firstPrimOffset, nPrimitives, bounds );
469 }
470 else
471 {
472 // Compute bound of primitive centroids, choose split dimension _dim_
473 BBOX_3D centroidBounds;
474 centroidBounds.Reset();
475
476 for( int i = start; i < end; ++i )
477 centroidBounds.Union( primitiveInfo[i].centroid );
478
479 const int dim = centroidBounds.MaxDimension();
480
481 // Partition primitives into two sets and build children
482 int mid = (start + end) / 2;
483
484 if( fabs( centroidBounds.Max()[dim] -
485 centroidBounds.Min()[dim] ) < (FLT_EPSILON + FLT_EPSILON) )
486 {
487 // Create leaf _BVHBuildNode_
488 const int firstPrimOffset = orderedPrims.size();
489
490 for( int i = start; i < end; ++i )
491 {
492 int primitiveNr = primitiveInfo[i].primitiveNumber;
493
494 wxASSERT( ( primitiveNr >= 0 ) && ( primitiveNr < (int) m_primitives.size() ) );
495
496 const OBJECT_3D* obj = static_cast<const OBJECT_3D*>( m_primitives[ primitiveNr ] );
497
498 wxASSERT( obj != nullptr );
499
500 orderedPrims.push_back( obj );
501 }
502
503 node->InitLeaf( firstPrimOffset, nPrimitives, bounds );
504 }
505 else
506 {
507 // Partition primitives based on _splitMethod_
508 switch( m_splitMethod )
509 {
511 {
512 // Partition primitives through node's midpoint
513 float pmid = centroidBounds.GetCenter( dim );
514
515 BVHPrimitiveInfo *midPtr = std::partition( &primitiveInfo[start],
516 &primitiveInfo[end - 1] + 1,
517 CompareToMid( dim, pmid ) );
518 mid = midPtr - &primitiveInfo[0];
519
520 wxASSERT( ( mid >= start ) && ( mid <= end ) );
521
522 if( ( mid != start ) && ( mid != end ) )
523 break;
524 }
525
526 // Intentionally fall through to SPLITMETHOD::EQUAL_COUNTS since prims
527 // with large overlapping bounding boxes may fail to partition
529
531 {
532 // Partition primitives into equally-sized subsets
533 mid = ( start + end ) / 2;
534
535 std::nth_element( &primitiveInfo[start], &primitiveInfo[mid],
536 &primitiveInfo[end - 1] + 1, ComparePoints( dim ) );
537
538 break;
539 }
540
541 case SPLITMETHOD::SAH:
542 default:
543 {
544 // Partition primitives using approximate SAH
545 if( nPrimitives <= 2 )
546 {
547 // Partition primitives into equally-sized subsets
548 mid = ( start + end ) / 2;
549
550 std::nth_element( &primitiveInfo[start], &primitiveInfo[mid],
551 &primitiveInfo[end - 1] + 1, ComparePoints( dim ) );
552 }
553 else
554 {
555 // Allocate _BucketInfo_ for SAH partition buckets
556 const int nBuckets = 12;
557
558 BucketInfo buckets[nBuckets];
559
560 for( int i = 0; i < nBuckets; ++i )
561 {
562 buckets[i].count = 0;
563 buckets[i].bounds.Reset();
564 }
565
566 // Initialize _BucketInfo_ for SAH partition buckets
567 for( int i = start; i < end; ++i )
568 {
569 int b = nBuckets *
570 centroidBounds.Offset( primitiveInfo[i].centroid )[dim];
571
572 if( b == nBuckets )
573 b = nBuckets - 1;
574
575 wxASSERT( b >= 0 && b < nBuckets );
576
577 buckets[b].count++;
578 buckets[b].bounds.Union( primitiveInfo[i].bounds );
579 }
580
581 // Compute costs for splitting after each bucket
582 float cost[nBuckets - 1];
583
584 for( int i = 0; i < ( nBuckets - 1 ); ++i )
585 {
586 BBOX_3D b0, b1;
587
588 b0.Reset();
589 b1.Reset();
590
591 int count0 = 0;
592 int count1 = 0;
593
594 for( int j = 0; j <= i; ++j )
595 {
596 if( buckets[j].count )
597 {
598 count0 += buckets[j].count;
599 b0.Union( buckets[j].bounds );
600 }
601 }
602
603 for( int j = i + 1; j < nBuckets; ++j )
604 {
605 if( buckets[j].count )
606 {
607 count1 += buckets[j].count;
608 b1.Union( buckets[j].bounds );
609 }
610 }
611
612 cost[i] = 1.0f + ( count0 * b0.SurfaceArea() +
613 count1 * b1.SurfaceArea() ) / bounds.SurfaceArea();
614 }
615
616 // Find bucket to split at that minimizes SAH metric
617 float minCost = cost[0];
618 int minCostSplitBucket = 0;
619
620 for( int i = 1; i < ( nBuckets - 1 ); ++i )
621 {
622 if( cost[i] < minCost )
623 {
624 minCost = cost[i];
625 minCostSplitBucket = i;
626 }
627 }
628
629 // Either create leaf or split primitives at selected SAH bucket
630 if( ( nPrimitives > m_maxPrimsInNode ) || ( minCost < (float) nPrimitives ) )
631 {
632 BVHPrimitiveInfo *pmid =
633 std::partition( &primitiveInfo[start], &primitiveInfo[end - 1] + 1,
634 CompareToBucket( minCostSplitBucket, nBuckets,
635 dim, centroidBounds ) );
636 mid = pmid - &primitiveInfo[0];
637
638 wxASSERT( ( mid >= start ) && ( mid <= end ) );
639 }
640 else
641 {
642 // Create leaf _BVHBuildNode_
643 const int firstPrimOffset = orderedPrims.size();
644
645 for( int i = start; i < end; ++i )
646 {
647 const int primitiveNr = primitiveInfo[i].primitiveNumber;
648
649 wxASSERT( primitiveNr < (int)m_primitives.size() );
650
651 orderedPrims.push_back( m_primitives[ primitiveNr ] );
652 }
653
654 node->InitLeaf( firstPrimOffset, nPrimitives, bounds );
655
656 return node;
657 }
658 }
659 break;
660 }
661 }
662
663 node->InitInterior( dim, recursiveBuild( primitiveInfo, start, mid, totalNodes,
664 orderedPrims ),
665 recursiveBuild( primitiveInfo, mid, end, totalNodes,
666 orderedPrims) );
667 }
668 }
669
670 return node;
671}
672
673
674BVHBuildNode *BVH_PBRT::HLBVHBuild( const std::vector<BVHPrimitiveInfo>& primitiveInfo,
675 int* totalNodes, std::vector<const OBJECT_3D*>& orderedPrims )
676{
677 // Compute bounding box of all primitive centroids
678 BBOX_3D bounds;
679 bounds.Reset();
680
681 for( unsigned int i = 0; i < primitiveInfo.size(); ++i )
682 bounds.Union( primitiveInfo[i].centroid );
683
684 // Compute Morton indices of primitives
685 std::vector<MortonPrimitive> mortonPrims( primitiveInfo.size() );
686
687 for( int i = 0; i < (int)primitiveInfo.size(); ++i )
688 {
689 // Initialize _mortonPrims[i]_ for _i_th primitive
690 const int mortonBits = 10;
691 const int mortonScale = 1 << mortonBits;
692
693 wxASSERT( primitiveInfo[i].primitiveNumber < (int)primitiveInfo.size() );
694
695 mortonPrims[i].primitiveIndex = primitiveInfo[i].primitiveNumber;
696
697 const SFVEC3F centroidOffset = bounds.Offset( primitiveInfo[i].centroid );
698
699 wxASSERT( ( centroidOffset.x >= 0.0f ) && ( centroidOffset.x <= 1.0f ) );
700 wxASSERT( ( centroidOffset.y >= 0.0f ) && ( centroidOffset.y <= 1.0f ) );
701 wxASSERT( ( centroidOffset.z >= 0.0f ) && ( centroidOffset.z <= 1.0f ) );
702
703 mortonPrims[i].mortonCode = EncodeMorton3( centroidOffset * SFVEC3F( (float)mortonScale ) );
704 }
705
706 // Radix sort primitive Morton indices
707 RadixSort( &mortonPrims );
708
709 // Create LBVH treelets at bottom of BVH
710
711 // Find intervals of primitives for each treelet
712 std::vector<LBVHTreelet> treeletsToBuild;
713
714 for( int start = 0, end = 1; end <= (int)mortonPrims.size(); ++end )
715 {
716 const uint32_t mask = 0b00111111111111000000000000000000;
717
718 if( ( end == (int) mortonPrims.size() )
719 || ( ( mortonPrims[start].mortonCode & mask )
720 != ( mortonPrims[end].mortonCode & mask ) ) )
721 {
722 // Add entry to _treeletsToBuild_ for this treelet
723 const int numPrimitives = end - start;
724 const int maxBVHNodes = 2 * numPrimitives;
725
726 // !TODO: implement a memory arena
727 BVHBuildNode *nodes = static_cast<BVHBuildNode *>( malloc( maxBVHNodes *
728 sizeof( BVHBuildNode ) ) );
729
730 m_nodesToFree.push_back( nodes );
731
732 for( int i = 0; i < maxBVHNodes; ++i )
733 {
734 nodes[i].bounds.Reset();
735 nodes[i].firstPrimOffset = 0;
736 nodes[i].nPrimitives = 0;
737 nodes[i].splitAxis = 0;
738 nodes[i].children[0] = nullptr;
739 nodes[i].children[1] = nullptr;
740 }
741
742 LBVHTreelet tmpTreelet;
743
744 tmpTreelet.startIndex = start;
745 tmpTreelet.numPrimitives = numPrimitives;
746 tmpTreelet.buildNodes = nodes;
747
748 treeletsToBuild.push_back( tmpTreelet );
749
750 start = end;
751 }
752 }
753
754 // Create LBVHs for treelets in parallel
755 int atomicTotal = 0;
756 int orderedPrimsOffset = 0;
757
758 orderedPrims.resize( m_primitives.size() );
759
760 for( int index = 0; index < (int)treeletsToBuild.size(); ++index )
761 {
762 // Generate _index_th LBVH treelet
763 int nodesCreated = 0;
764 const int firstBit = 29 - 12;
765
766 LBVHTreelet &tr = treeletsToBuild[index];
767
768 wxASSERT( tr.startIndex < (int)mortonPrims.size() );
769
770 tr.buildNodes = emitLBVH( tr.buildNodes, primitiveInfo, &mortonPrims[tr.startIndex],
771 tr.numPrimitives, &nodesCreated, orderedPrims,
772 &orderedPrimsOffset, firstBit );
773
774 atomicTotal += nodesCreated;
775 }
776
777 *totalNodes = atomicTotal;
778
779 // Initialize _finishedTreelets_ with treelet root node pointers
780 std::vector<BVHBuildNode *> finishedTreelets;
781 finishedTreelets.reserve( treeletsToBuild.size() );
782
783 for( int index = 0; index < (int)treeletsToBuild.size(); ++index )
784 finishedTreelets.push_back( treeletsToBuild[index].buildNodes );
785
786 // Create and return SAH BVH from LBVH treelets
787 return buildUpperSAH( finishedTreelets, 0, finishedTreelets.size(), totalNodes );
788}
789
790
791BVHBuildNode *BVH_PBRT::emitLBVH( BVHBuildNode* &buildNodes, const std::vector<BVHPrimitiveInfo>& primitiveInfo,
792 MortonPrimitive* mortonPrims, int nPrimitives, int* totalNodes,
793 std::vector<const OBJECT_3D*>& orderedPrims, int *orderedPrimsOffset, int bit )
794{
795 wxASSERT( nPrimitives > 0 );
796 wxASSERT( totalNodes != nullptr );
797 wxASSERT( orderedPrimsOffset != nullptr );
798 wxASSERT( nPrimitives > 0 );
799 wxASSERT( mortonPrims != nullptr );
800
801 if( ( bit == -1 ) || ( nPrimitives < m_maxPrimsInNode ) )
802 {
803 // Create and return leaf node of LBVH treelet
804 (*totalNodes)++;
805
806 BVHBuildNode *node = buildNodes++;
807 BBOX_3D bounds;
808 bounds.Reset();
809
810 int firstPrimOffset = *orderedPrimsOffset;
811 *orderedPrimsOffset += nPrimitives;
812
813 wxASSERT( ( firstPrimOffset + ( nPrimitives - 1 ) ) < (int) orderedPrims.size() );
814
815 for( int i = 0; i < nPrimitives; ++i )
816 {
817 const int primitiveIndex = mortonPrims[i].primitiveIndex;
818
819 wxASSERT( primitiveIndex < (int)m_primitives.size() );
820
821 orderedPrims[firstPrimOffset + i] = m_primitives[primitiveIndex];
822 bounds.Union( primitiveInfo[primitiveIndex].bounds );
823 }
824
825 node->InitLeaf( firstPrimOffset, nPrimitives, bounds );
826
827 return node;
828 }
829 else
830 {
831 int mask = 1 << bit;
832
833 // Advance to next subtree level if there's no LBVH split for this bit
834 if( ( mortonPrims[0].mortonCode & mask ) ==
835 ( mortonPrims[nPrimitives - 1].mortonCode & mask ) )
836 return emitLBVH( buildNodes, primitiveInfo, mortonPrims, nPrimitives, totalNodes,
837 orderedPrims, orderedPrimsOffset, bit - 1 );
838
839 // Find LBVH split point for this dimension
840 int searchStart = 0;
841 int searchEnd = nPrimitives - 1;
842
843 while( searchStart + 1 != searchEnd )
844 {
845 wxASSERT( searchStart != searchEnd );
846
847 const int mid = ( searchStart + searchEnd ) / 2;
848
849 if( ( mortonPrims[searchStart].mortonCode & mask ) ==
850 ( mortonPrims[mid].mortonCode & mask ) )
851 searchStart = mid;
852 else
853 {
854 wxASSERT( ( mortonPrims[mid].mortonCode & mask ) ==
855 ( mortonPrims[searchEnd].mortonCode & mask ) );
856 searchEnd = mid;
857 }
858 }
859
860 const int splitOffset = searchEnd;
861
862 wxASSERT( splitOffset <= ( nPrimitives - 1 ) );
863 wxASSERT( ( mortonPrims[splitOffset - 1].mortonCode & mask )
864 != ( mortonPrims[splitOffset].mortonCode & mask ) );
865
866 // Create and return interior LBVH node
867 (*totalNodes)++;
868
869 BVHBuildNode *node = buildNodes++;
870 BVHBuildNode *lbvh[2];
871
872 lbvh[0] = emitLBVH( buildNodes, primitiveInfo, mortonPrims, splitOffset,
873 totalNodes, orderedPrims, orderedPrimsOffset, bit - 1 );
874
875 lbvh[1] = emitLBVH( buildNodes, primitiveInfo, &mortonPrims[splitOffset],
876 nPrimitives - splitOffset, totalNodes, orderedPrims,
877 orderedPrimsOffset, bit - 1 );
878
879 const int axis = bit % 3;
880
881 node->InitInterior( axis, lbvh[0], lbvh[1] );
882
883 return node;
884 }
885}
886
887
888BVHBuildNode *BVH_PBRT::buildUpperSAH( std::vector<BVHBuildNode*>& treeletRoots, int start,
889 int end, int* totalNodes )
890{
891 wxASSERT( totalNodes != nullptr );
892 wxASSERT( start < end );
893 wxASSERT( end <= (int)treeletRoots.size() );
894
895 int nNodes = end - start;
896
897 if( nNodes == 1 )
898 return treeletRoots[start];
899
900 (*totalNodes)++;
901
902 BVHBuildNode* node = static_cast<BVHBuildNode*>( malloc( sizeof( BVHBuildNode ) ) );
903
904 m_nodesToFree.push_back( node );
905
906 node->bounds.Reset();
907 node->firstPrimOffset = 0;
908 node->nPrimitives = 0;
909 node->splitAxis = 0;
910 node->children[0] = nullptr;
911 node->children[1] = nullptr;
912
913 // Compute bounds of all nodes under this HLBVH node
914 BBOX_3D bounds;
915 bounds.Reset();
916
917 for( int i = start; i < end; ++i )
918 bounds.Union( treeletRoots[i]->bounds );
919
920 // Compute bound of HLBVH node centroids, choose split dimension _dim_
921 BBOX_3D centroidBounds;
922 centroidBounds.Reset();
923
924 for( int i = start; i < end; ++i )
925 {
926 SFVEC3F centroid = ( treeletRoots[i]->bounds.Min() + treeletRoots[i]->bounds.Max() ) * 0.5f;
927
928 centroidBounds.Union( centroid );
929 }
930
931 const int dim = centroidBounds.MaxDimension();
932
933 // FIXME: if this hits, what do we need to do?
934 // Make sure the SAH split below does something... ?
935 wxASSERT( centroidBounds.Max()[dim] != centroidBounds.Min()[dim] );
936
937 // Allocate _BucketInfo_ for SAH partition buckets
938 const int nBuckets = 12;
939
940 BucketInfo buckets[nBuckets];
941
942 for( int i = 0; i < nBuckets; ++i )
943 {
944 buckets[i].count = 0;
945 buckets[i].bounds.Reset();
946 }
947
948 // Initialize _BucketInfo_ for HLBVH SAH partition buckets
949 for( int i = start; i < end; ++i )
950 {
951 const float centroid = ( treeletRoots[i]->bounds.Min()[dim] +
952 treeletRoots[i]->bounds.Max()[dim] ) * 0.5f;
953 int b = nBuckets * ( ( centroid - centroidBounds.Min()[dim] )
954 / ( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) );
955
956 if( b == nBuckets )
957 b = nBuckets - 1;
958
959 wxASSERT( ( b >= 0 ) && ( b < nBuckets ) );
960
961 buckets[b].count++;
962 buckets[b].bounds.Union( treeletRoots[i]->bounds );
963 }
964
965 // Compute costs for splitting after each bucket
966 float cost[nBuckets - 1];
967
968 for( int i = 0; i < nBuckets - 1; ++i )
969 {
970 BBOX_3D b0, b1;
971 b0.Reset();
972 b1.Reset();
973
974 int count0 = 0, count1 = 0;
975
976 for( int j = 0; j <= i; ++j )
977 {
978 if( buckets[j].count )
979 {
980 count0 += buckets[j].count;
981 b0.Union( buckets[j].bounds );
982 }
983 }
984
985 for( int j = i + 1; j < nBuckets; ++j )
986 {
987 if( buckets[j].count )
988 {
989 count1 += buckets[j].count;
990 b1.Union( buckets[j].bounds );
991 }
992 }
993
994 cost[i] = .125f + ( count0 * b0.SurfaceArea() + count1 * b1.SurfaceArea() ) /
995 bounds.SurfaceArea();
996 }
997
998 // Find bucket to split at that minimizes SAH metric
999 float minCost = cost[0];
1000 int minCostSplitBucket = 0;
1001
1002 for( int i = 1; i < nBuckets - 1; ++i )
1003 {
1004 if( cost[i] < minCost )
1005 {
1006 minCost = cost[i];
1007 minCostSplitBucket = i;
1008 }
1009 }
1010
1011 // Split nodes and create interior HLBVH SAH node
1012 BVHBuildNode **pmid = std::partition( &treeletRoots[start], &treeletRoots[end - 1] + 1,
1013 HLBVH_SAH_Evaluator( minCostSplitBucket, nBuckets,
1014 dim, centroidBounds ) );
1015
1016 const int mid = pmid - &treeletRoots[0];
1017
1018 wxASSERT( ( mid > start ) && ( mid < end ) );
1019
1020 node->InitInterior( dim,
1021 buildUpperSAH( treeletRoots, start, mid, totalNodes ),
1022 buildUpperSAH( treeletRoots, mid, end, totalNodes ) );
1023
1024 return node;
1025}
1026
1027
1028int BVH_PBRT::flattenBVHTree( BVHBuildNode* node, uint32_t* offset )
1029{
1030 LinearBVHNode *linearNode = &m_nodes[*offset];
1031
1032 linearNode->bounds = node->bounds;
1033
1034 int myOffset = (*offset)++;
1035
1036 if( node->nPrimitives > 0 )
1037 {
1038 wxASSERT( ( !node->children[0] ) && ( !node->children[1] ) );
1039 wxASSERT( node->nPrimitives < 65536 );
1040
1041 linearNode->primitivesOffset = node->firstPrimOffset;
1042 linearNode->nPrimitives = node->nPrimitives;
1043 }
1044 else
1045 {
1046 // Create interior flattened BVH node
1047 linearNode->axis = node->splitAxis;
1048 linearNode->nPrimitives = 0;
1049 flattenBVHTree( node->children[0], offset );
1050 linearNode->secondChildOffset = flattenBVHTree( node->children[1], offset );
1051 }
1052
1053 return myOffset;
1054}
1055
1056
1057#define MAX_TODOS 64
1058
1059
1060bool BVH_PBRT::Intersect( const RAY& aRay, HITINFO& aHitInfo ) const
1061{
1062 if( !m_nodes )
1063 return false;
1064
1065 bool hit = false;
1066
1067 // Follow ray through BVH nodes to find primitive intersections
1068 int todoOffset = 0, nodeNum = 0;
1069 int todo[MAX_TODOS];
1070
1071 while( true )
1072 {
1073 const LinearBVHNode *node = &m_nodes[nodeNum];
1074
1075 wxASSERT( todoOffset < MAX_TODOS );
1076
1077 // Check ray against BVH node
1078 float hitBox = 0.0f;
1079
1080 const bool hitted = node->bounds.Intersect( aRay, &hitBox );
1081
1082 if( hitted && ( hitBox < aHitInfo.m_tHit ) )
1083 {
1084 if( node->nPrimitives > 0 )
1085 {
1086 // Intersect ray with primitives in leaf BVH node
1087 for( int i = 0; i < node->nPrimitives; ++i )
1088 {
1089 if( m_primitives[node->primitivesOffset + i]->Intersect( aRay, aHitInfo ) )
1090 {
1091 aHitInfo.m_acc_node_info = nodeNum;
1092 hit = true;
1093 }
1094 }
1095 }
1096 else
1097 {
1098 // Put far BVH node on _todo_ stack, advance to near node
1099 if( aRay.m_dirIsNeg[node->axis] )
1100 {
1101 todo[todoOffset++] = nodeNum + 1;
1102 nodeNum = node->secondChildOffset;
1103 }
1104 else
1105 {
1106 todo[todoOffset++] = node->secondChildOffset;
1107 nodeNum = nodeNum + 1;
1108 }
1109
1110 continue;
1111 }
1112 }
1113
1114 if( todoOffset == 0 )
1115 break;
1116
1117 nodeNum = todo[--todoOffset];
1118 }
1119
1120 return hit;
1121}
1122
1123
1125bool BVH_PBRT::Intersect( const RAY& aRay, HITINFO& aHitInfo, unsigned int aAccNodeInfo ) const
1126{
1127 if( !m_nodes )
1128 return false;
1129
1130 bool hit = false;
1131
1132 // Follow ray through BVH nodes to find primitive intersections
1133 int todoOffset = 0, nodeNum = aAccNodeInfo;
1134 int todo[MAX_TODOS];
1135
1136 while( true )
1137 {
1138 const LinearBVHNode *node = &m_nodes[nodeNum];
1139
1140 wxASSERT( todoOffset < MAX_TODOS );
1141
1142 // Check ray against BVH node
1143 float hitBox = 0.0f;
1144
1145 const bool hitted = node->bounds.Intersect( aRay, &hitBox );
1146
1147 if( hitted && ( hitBox < aHitInfo.m_tHit ) )
1148 {
1149 if( node->nPrimitives > 0 )
1150 {
1151 // Intersect ray with primitives in leaf BVH node
1152 for( int i = 0; i < node->nPrimitives; ++i )
1153 {
1154 if( m_primitives[node->primitivesOffset + i]->Intersect( aRay, aHitInfo ) )
1155 {
1156 //aHitInfo.m_acc_node_info = nodeNum;
1157 hit = true;
1158 }
1159 }
1160 }
1161 else
1162 {
1163 // Put far BVH node on _todo_ stack, advance to near node
1164 if( aRay.m_dirIsNeg[node->axis] )
1165 {
1166 todo[todoOffset++] = nodeNum + 1;
1167 nodeNum = node->secondChildOffset;
1168 }
1169 else
1170 {
1171 todo[todoOffset++] = node->secondChildOffset;
1172 nodeNum = nodeNum + 1;
1173 }
1174
1175 continue;
1176 }
1177 }
1178
1179 if( todoOffset == 0 )
1180 break;
1181
1182 nodeNum = todo[--todoOffset];
1183 }
1184
1185 return hit;
1186}
1187
1188
1189bool BVH_PBRT::IntersectP( const RAY& aRay, float aMaxDistance ) const
1190{
1191 if( !m_nodes )
1192 return false;
1193
1194 // Follow ray through BVH nodes to find primitive intersections
1195 int todoOffset = 0, nodeNum = 0;
1196 int todo[MAX_TODOS];
1197
1198 while( true )
1199 {
1200 const LinearBVHNode* node = &m_nodes[nodeNum];
1201
1202 wxASSERT( todoOffset < MAX_TODOS );
1203
1204 // Check ray against BVH node
1205 float hitBox = 0.0f;
1206
1207 const bool hitted = node->bounds.Intersect( aRay, &hitBox );
1208
1209 if( hitted && ( hitBox < aMaxDistance ) )
1210 {
1211 if( node->nPrimitives > 0 )
1212 {
1213 // Intersect ray with primitives in leaf BVH node
1214 for( int i = 0; i < node->nPrimitives; ++i )
1215 {
1216 const OBJECT_3D* obj = m_primitives[node->primitivesOffset + i];
1217
1218 if( obj->GetMaterial()->GetCastShadows()
1219 && obj->IntersectP( aRay, aMaxDistance ) )
1220 return true;
1221 }
1222 }
1223 else
1224 {
1225 // Put far BVH node on _todo_ stack, advance to near node
1226 if( aRay.m_dirIsNeg[node->axis] )
1227 {
1228 todo[todoOffset++] = nodeNum + 1;
1229 nodeNum = node->secondChildOffset;
1230 }
1231 else
1232 {
1233 todo[todoOffset++] = node->secondChildOffset;
1234 nodeNum = nodeNum + 1;
1235 }
1236
1237 continue;
1238 }
1239 }
1240
1241 if( todoOffset == 0 )
1242 break;
1243
1244 nodeNum = todo[--todoOffset];
1245 }
1246
1247 return false;
1248}
Defines math related functions.
int index
#define MAX_TODOS
uint32_t EncodeMorton3(const SFVEC3F &v)
Definition bvh_pbrt.cpp:164
uint32_t LeftShift3(uint32_t x)
Definition bvh_pbrt.cpp:144
static void RadixSort(std::vector< MortonPrimitive > *v)
Definition bvh_pbrt.cpp:174
This BVH implementation is based on the source code implementation from the book "Physically Based Re...
SPLITMETHOD
Definition bvh_pbrt.h:98
std::list< void * > m_nodesToFree
Definition bvh_pbrt.h:146
int flattenBVHTree(BVHBuildNode *node, uint32_t *offset)
LinearBVHNode * m_nodes
Definition bvh_pbrt.h:144
BVHBuildNode * HLBVHBuild(const std::vector< BVHPrimitiveInfo > &primitiveInfo, int *totalNodes, std::vector< const OBJECT_3D * > &orderedPrims)
Definition bvh_pbrt.cpp:674
BVHBuildNode * recursiveBuild(std::vector< BVHPrimitiveInfo > &primitiveInfo, int start, int end, int *totalNodes, std::vector< const OBJECT_3D * > &orderedPrims)
Definition bvh_pbrt.cpp:422
BVH_PBRT(const CONTAINER_3D_BASE &aObjectContainer, int aMaxPrimsInNode=4, SPLITMETHOD aSplitMethod=SPLITMETHOD::SAH)
Definition bvh_pbrt.cpp:231
std::vector< const OBJECT_3D * > m_primitives
Definition bvh_pbrt.h:143
BVHBuildNode * buildUpperSAH(std::vector< BVHBuildNode * > &treeletRoots, int start, int end, int *totalNodes)
Definition bvh_pbrt.cpp:888
const int m_maxPrimsInNode
Definition bvh_pbrt.h:141
bool Intersect(const RAY &aRay, HITINFO &aHitInfo) const override
bool IntersectP(const RAY &aRay, float aMaxDistance) const override
BVHBuildNode * emitLBVH(BVHBuildNode *&buildNodes, const std::vector< BVHPrimitiveInfo > &primitiveInfo, MortonPrimitive *mortonPrims, int nPrimitives, int *totalNodes, std::vector< const OBJECT_3D * > &orderedPrims, int *orderedPrimsOffset, int bit)
TODO: after implement memory arena, put const back to this functions.
Definition bvh_pbrt.cpp:791
SPLITMETHOD m_splitMethod
Definition bvh_pbrt.h:142
const std::list< OBJECT_3D * > & GetList() const
void ConvertTo(std::vector< const OBJECT_3D * > &aOutVector) const
bool GetCastShadows() const
Definition material.h:305
virtual bool IntersectP(const RAY &aRay, float aMaxDistance) const =0
const MATERIAL * GetMaterial() const
Definition object_3d.h:60
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
STL namespace.
#define RAYPACKET_RAYS_PER_PACKET
Definition raypacket.h:31
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
Manage a bounding box defined by two SFVEC3F min max points.
Definition bbox_3d.h:39
bool Intersect(const RAY &aRay, float *t) const
void Union(const SFVEC3F &aPoint)
Recalculate the bounding box adding a point.
Definition bbox_3d.cpp:98
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition bbox_3d.cpp:128
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition bbox_3d.h:188
SFVEC3F Offset(const SFVEC3F &p) const
Definition bbox_3d.cpp:257
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition bbox_3d.h:195
void Reset()
Reset the bounding box to zero and de-initialize it.
Definition bbox_3d.cpp:91
unsigned int MaxDimension() const
Definition bbox_3d.cpp:147
float SurfaceArea() const
Definition bbox_3d.cpp:170
int firstPrimOffset
Definition bvh_pbrt.cpp:125
void InitLeaf(int first, int n, const BBOX_3D &b)
Definition bvh_pbrt.cpp:105
void InitInterior(int axis, BVHBuildNode *c0, BVHBuildNode *c1)
Definition bvh_pbrt.cpp:113
BBOX_3D bounds
Definition bvh_pbrt.cpp:123
BVHBuildNode * children[2]
Definition bvh_pbrt.cpp:124
BVHPrimitiveInfo(int aPrimitiveNumber, const BBOX_3D &aBounds)
Definition bvh_pbrt.cpp:91
BBOX_3D bounds
Definition bvh_pbrt.cpp:418
bool operator()(const BVHPrimitiveInfo &a, const BVHPrimitiveInfo &b) const
Definition bvh_pbrt.cpp:324
ComparePoints(int d)
Definition bvh_pbrt.cpp:320
const BBOX_3D & centroidBounds
Definition bvh_pbrt.cpp:359
CompareToBucket(int split, int num, int d, const BBOX_3D &b)
Definition bvh_pbrt.cpp:347
bool operator()(const BVHPrimitiveInfo &p) const
Definition bvh_pbrt.cpp:363
CompareToMid(int d, float m)
Definition bvh_pbrt.cpp:333
bool operator()(const BVHPrimitiveInfo &a) const
Definition bvh_pbrt.cpp:338
Stores the hit information of a ray with a point on the surface of a object.
Definition hitinfo.h:32
unsigned int m_acc_node_info
( 4) The acc stores here the node that it hits
Definition hitinfo.h:38
float m_tHit
( 4) distance
Definition hitinfo.h:34
HLBVH_SAH_Evaluator(int split, int num, int d, const BBOX_3D &b)
Definition bvh_pbrt.cpp:383
const BBOX_3D & centroidBounds
Definition bvh_pbrt.cpp:393
bool operator()(const BVHBuildNode *node) const
Definition bvh_pbrt.cpp:397
int numPrimitives
Definition bvh_pbrt.cpp:138
BVHBuildNode * buildNodes
Definition bvh_pbrt.cpp:139
int secondChildOffset
interior
Definition bvh_pbrt.h:87
uint8_t axis
interior node: xyz
Definition bvh_pbrt.h:92
BBOX_3D bounds
Definition bvh_pbrt.h:81
uint16_t nPrimitives
0 -> interior node
Definition bvh_pbrt.h:91
int primitivesOffset
leaf
Definition bvh_pbrt.h:86
uint32_t mortonCode
Definition bvh_pbrt.cpp:132
Definition ray.h:59
unsigned int m_dirIsNeg[3]
Definition ray.h:71
VECTOR2I end
glm::vec3 SFVEC3F
Definition xv3d_types.h:40