BVHBuildNode *CBVH_PBRT::buildUpperSAH( std::vector<BVHBuildNode *> &treeletRoots, int start, int end, int *totalNodes ) { wxASSERT( totalNodes != NULL ); wxASSERT( start < end ); wxASSERT( end <= (int)treeletRoots.size() ); int nNodes = end - start; if( nNodes == 1 ) return treeletRoots[start]; (*totalNodes)++; BVHBuildNode *node = static_cast<BVHBuildNode *>( _mm_malloc( sizeof( BVHBuildNode ), L1_CACHE_LINE_SIZE ) ); m_addresses_pointer_to_mm_free.push_back( node ); node->bounds.Reset(); node->firstPrimOffset = 0; node->nPrimitives = 0; node->splitAxis = 0; node->children[0] = NULL; node->children[1] = NULL; // Compute bounds of all nodes under this HLBVH node CBBOX bounds; bounds.Reset(); for( int i = start; i < end; ++i ) bounds.Union( treeletRoots[i]->bounds ); // Compute bound of HLBVH node centroids, choose split dimension _dim_ CBBOX centroidBounds; centroidBounds.Reset(); for( int i = start; i < end; ++i ) { SFVEC3F centroid = (treeletRoots[i]->bounds.Min() + treeletRoots[i]->bounds.Max()) * 0.5f; centroidBounds.Union(centroid); } const int dim = centroidBounds.MaxDimension(); // FIXME: if this hits, what do we need to do? // Make sure the SAH split below does something... ? wxASSERT( centroidBounds.Max()[dim] != centroidBounds.Min()[dim] ); // Allocate _BucketInfo_ for SAH partition buckets const int nBuckets = 12; BucketInfo buckets[nBuckets]; for( int i = 0; i < nBuckets; ++i ) { buckets[i].count = 0; buckets[i].bounds.Reset(); } // Initialize _BucketInfo_ for HLBVH SAH partition buckets for( int i = start; i < end; ++i ) { const float centroid = ( treeletRoots[i]->bounds.Min()[dim] + treeletRoots[i]->bounds.Max()[dim] ) * 0.5f; int b = nBuckets * ( (centroid - centroidBounds.Min()[dim] ) / (centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) ); if( b == nBuckets ) b = nBuckets - 1; wxASSERT( (b >= 0) && (b < nBuckets) ); buckets[b].count++; buckets[b].bounds.Union( treeletRoots[i]->bounds ); } // Compute costs for splitting after each bucket float cost[nBuckets - 1]; for( int i = 0; i < nBuckets - 1; ++i ) { CBBOX b0, b1; b0.Reset(); b1.Reset(); int count0 = 0, count1 = 0; for( int j = 0; j <= i; ++j ) { if( buckets[j].count ) { count0 += buckets[j].count; b0.Union( buckets[j].bounds ); } } for( int j = i + 1; j < nBuckets; ++j ) { if( buckets[j].count ) { count1 += buckets[j].count; b1.Union( buckets[j].bounds ); } } cost[i] = .125f + ( count0 * b0.SurfaceArea() + count1 * b1.SurfaceArea() ) / bounds.SurfaceArea(); } // Find bucket to split at that minimizes SAH metric float minCost = cost[0]; int minCostSplitBucket = 0; for( int i = 1; i < nBuckets - 1; ++i ) { if( cost[i] < minCost ) { minCost = cost[i]; minCostSplitBucket = i; } } // Split nodes and create interior HLBVH SAH node BVHBuildNode **pmid = std::partition( &treeletRoots[start], &treeletRoots[end - 1] + 1, HLBVH_SAH_Evaluator( minCostSplitBucket, nBuckets, dim, centroidBounds ) ); const int mid = pmid - &treeletRoots[0]; wxASSERT( (mid > start) && (mid < end) ); node->InitInterior( dim, buildUpperSAH( treeletRoots, start, mid, totalNodes ), buildUpperSAH( treeletRoots, mid, end, totalNodes ) ); return node; }
BVHBuildNode *CBVH_PBRT::recursiveBuild ( std::vector<BVHPrimitiveInfo> &primitiveInfo, int start, int end, int *totalNodes, CONST_VECTOR_OBJECT &orderedPrims ) { wxASSERT( totalNodes != NULL ); wxASSERT( start >= 0 ); wxASSERT( end >= 0 ); wxASSERT( start != end ); wxASSERT( start < end ); wxASSERT( start <= (int)primitiveInfo.size() ); wxASSERT( end <= (int)primitiveInfo.size() ); (*totalNodes)++; // !TODO: implement an memory Arena BVHBuildNode *node = static_cast<BVHBuildNode *>( _mm_malloc( sizeof( BVHBuildNode ), L1_CACHE_LINE_SIZE ) ); m_addresses_pointer_to_mm_free.push_back( node ); node->bounds.Reset(); node->firstPrimOffset = 0; node->nPrimitives = 0; node->splitAxis = 0; node->children[0] = NULL; node->children[1] = NULL; // Compute bounds of all primitives in BVH node CBBOX bounds; bounds.Reset(); for( int i = start; i < end; ++i ) bounds.Union( primitiveInfo[i].bounds ); int nPrimitives = end - start; if( nPrimitives == 1 ) { // Create leaf _BVHBuildNode_ int firstPrimOffset = orderedPrims.size(); for( int i = start; i < end; ++i ) { int primitiveNr = primitiveInfo[i].primitiveNumber; wxASSERT( primitiveNr < (int)m_primitives.size() ); orderedPrims.push_back( m_primitives[ primitiveNr ] ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); } else { // Compute bound of primitive centroids, choose split dimension _dim_ CBBOX centroidBounds; centroidBounds.Reset(); for( int i = start; i < end; ++i ) centroidBounds.Union( primitiveInfo[i].centroid ); const int dim = centroidBounds.MaxDimension(); // Partition primitives into two sets and build children int mid = (start + end) / 2; if( fabs( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) < (FLT_EPSILON + FLT_EPSILON) ) { // Create leaf _BVHBuildNode_ const int firstPrimOffset = orderedPrims.size(); for( int i = start; i < end; ++i ) { int primitiveNr = primitiveInfo[i].primitiveNumber; wxASSERT( (primitiveNr >= 0) && (primitiveNr < (int)m_primitives.size()) ); const COBJECT *obj = static_cast<const COBJECT *>( m_primitives[ primitiveNr ] ); wxASSERT( obj != NULL ); orderedPrims.push_back( obj ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); } else { // Partition primitives based on _splitMethod_ switch( m_splitMethod ) { case SPLIT_MIDDLE: { // Partition primitives through node's midpoint float pmid = centroidBounds.GetCenter( dim ); BVHPrimitiveInfo *midPtr = std::partition( &primitiveInfo[start], &primitiveInfo[end - 1] + 1, CompareToMid( dim, pmid ) ); mid = midPtr - &primitiveInfo[0]; wxASSERT( (mid >= start) && (mid <= end) ); if( (mid != start) && (mid != end) ) // for lots of prims with large overlapping bounding boxes, this // may fail to partition; in that case don't break and fall through // to SPLIT_EQUAL_COUNTS break; } case SPLIT_EQUALCOUNTS: { // Partition primitives into equally-sized subsets mid = (start + end) / 2; std::nth_element( &primitiveInfo[start], &primitiveInfo[mid], &primitiveInfo[end - 1] + 1, ComparePoints( dim ) ); break; } case SPLIT_SAH: default: { // Partition primitives using approximate SAH if( nPrimitives <= 2 ) { // Partition primitives into equally-sized subsets mid = (start + end) / 2; std::nth_element( &primitiveInfo[start], &primitiveInfo[mid], &primitiveInfo[end - 1] + 1, ComparePoints( dim ) ); } else { // Allocate _BucketInfo_ for SAH partition buckets const int nBuckets = 12; BucketInfo buckets[nBuckets]; for( int i = 0; i < nBuckets; ++i ) { buckets[i].count = 0; buckets[i].bounds.Reset(); } // Initialize _BucketInfo_ for SAH partition buckets for( int i = start; i < end; ++i ) { int b = nBuckets * centroidBounds.Offset( primitiveInfo[i].centroid )[dim]; if( b == nBuckets ) b = nBuckets - 1; wxASSERT( b >= 0 && b < nBuckets ); buckets[b].count++; buckets[b].bounds.Union( primitiveInfo[i].bounds ); } // Compute costs for splitting after each bucket float cost[nBuckets - 1]; for( int i = 0; i < (nBuckets - 1); ++i ) { CBBOX b0, b1; b0.Reset(); b1.Reset(); int count0 = 0; int count1 = 0; for( int j = 0; j <= i; ++j ) { if( buckets[j].count ) { count0 += buckets[j].count; b0.Union( buckets[j].bounds ); } } for( int j = i + 1; j < nBuckets; ++j ) { if( buckets[j].count ) { count1 += buckets[j].count; b1.Union( buckets[j].bounds ); } } cost[i] = 1.0f + ( count0 * b0.SurfaceArea() + count1 * b1.SurfaceArea() ) / bounds.SurfaceArea(); } // Find bucket to split at that minimizes SAH metric float minCost = cost[0]; int minCostSplitBucket = 0; for( int i = 1; i < (nBuckets - 1); ++i ) { if( cost[i] < minCost ) { minCost = cost[i]; minCostSplitBucket = i; } } // Either create leaf or split primitives at selected SAH // bucket if( (nPrimitives > m_maxPrimsInNode) || (minCost < (float)nPrimitives) ) { BVHPrimitiveInfo *pmid = std::partition( &primitiveInfo[start], &primitiveInfo[end - 1] + 1, CompareToBucket( minCostSplitBucket, nBuckets, dim, centroidBounds ) ); mid = pmid - &primitiveInfo[0]; wxASSERT( (mid >= start) && (mid <= end) ); } else { // Create leaf _BVHBuildNode_ const int firstPrimOffset = orderedPrims.size(); for( int i = start; i < end; ++i ) { const int primitiveNr = primitiveInfo[i].primitiveNumber; wxASSERT( primitiveNr < (int)m_primitives.size() ); orderedPrims.push_back( m_primitives[ primitiveNr ] ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); return node; } } break; } } node->InitInterior( dim, recursiveBuild( primitiveInfo, start, mid, totalNodes, orderedPrims ), recursiveBuild( primitiveInfo, mid, end, totalNodes, orderedPrims) ); } } return node; }