BVHBuildNode *BVH::recursiveBuild(int start, int end, int *numTotalNodes) { AABoundingBox bbox; for(int i=start; i<end; i++){ bbox.join(m_buildData[i].m_bbox); } BVHBuildNode *node = new BVHBuildNode(); (*numTotalNodes)++; int numTriangles = end - start; if(numTriangles == 1){ createLeafNode(start,end,bbox,node, numTotalNodes); } else{ AABoundingBox bboxCentroids; for(int i=start; i<end; i++){ bboxCentroids.join(m_buildData[i].m_centroid); } int dim = bboxCentroids.getMaximumExtent(); if(bboxCentroids.min(dim) == bboxCentroids.max(dim)){ createLeafNode(start,end,bbox,node,numTotalNodes); } else{ float splitCost; int splitBucket; findSAHSplit(dim,start,end,bboxCentroids,bbox, &splitCost, &splitBucket); if(numTriangles > 6 || splitCost < numTriangles){ BVHTriangleInfo *midPtr = std::partition(&m_buildData[start], &m_buildData[end], CompareToBucket(dim, splitBucket, 12, bboxCentroids)); int mid = midPtr - &m_buildData[0]; if(mid == start || mid == end){ mid = start+1; } node->initInterior(dim, recursiveBuild(start,mid,numTotalNodes), recursiveBuild(mid,end,numTotalNodes)); } else{ createLeafNode(start,end,bbox,node,numTotalNodes); } } } 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; }
BVHBuildNode *BVHAccel::recursiveBuild(MemoryArena &buildArena, vector<BVHPrimitiveInfo> &buildData, uint32_t start, uint32_t end, uint32_t *totalNodes, vector<Reference<Primitive> > &orderedPrims) { Assert(start != end); (*totalNodes)++; BVHBuildNode *node = buildArena.Alloc<BVHBuildNode>(); // Compute bounds of all primitives in BVH node BBox bbox; for (uint32_t i = start; i < end; ++i) bbox = Union(bbox, buildData[i].bounds); uint32_t nPrimitives = end - start; if (nPrimitives == 1) { // Create leaf _BVHBuildNode_ uint32_t firstPrimOffset = orderedPrims.size(); for (uint32_t i = start; i < end; ++i) { uint32_t primNum = buildData[i].primitiveNumber; orderedPrims.push_back(primitives[primNum]); } node->InitLeaf(firstPrimOffset, nPrimitives, bbox); } else { // Compute bound of primitive centroids, choose split dimension _dim_ BBox centroidBounds; for (uint32_t i = start; i < end; ++i) centroidBounds = Union(centroidBounds, buildData[i].centroid); int dim = centroidBounds.MaximumExtent(); // Partition primitives into two sets and build children uint32_t mid = (start + end) / 2; if (centroidBounds.pMax[dim] == centroidBounds.pMin[dim]) { // Create leaf _BVHBuildNode_ uint32_t firstPrimOffset = orderedPrims.size(); for (uint32_t i = start; i < end; ++i) { uint32_t primNum = buildData[i].primitiveNumber; orderedPrims.push_back(primitives[primNum]); } node->InitLeaf(firstPrimOffset, nPrimitives, bbox); return node; } // Partition primitives based on _splitMethod_ switch (splitMethod) { case SPLIT_MIDDLE: { // Partition primitives through node's midpoint float pmid = .5f * (centroidBounds.pMin[dim] + centroidBounds.pMax[dim]); BVHPrimitiveInfo *midPtr = std::partition(&buildData[start], &buildData[end-1]+1, CompareToMid(dim, pmid)); mid = midPtr - &buildData[0]; 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_EQUAL_COUNTS: { // Partition primitives into equally-sized subsets mid = (start + end) / 2; std::nth_element(&buildData[start], &buildData[mid], &buildData[end-1]+1, ComparePoints(dim)); break; } case SPLIT_SAH: default: { // Partition primitives using approximate SAH if (nPrimitives <= 4) { // Partition primitives into equally-sized subsets mid = (start + end) / 2; std::nth_element(&buildData[start], &buildData[mid], &buildData[end-1]+1, ComparePoints(dim)); } else { // Allocate _BucketInfo_ for SAH partition buckets const int nBuckets = 12; struct BucketInfo { BucketInfo() { count = 0; } int count; BBox bounds; }; BucketInfo buckets[nBuckets]; // Initialize _BucketInfo_ for SAH partition buckets for (uint32_t i = start; i < end; ++i) { int b = nBuckets * ((buildData[i].centroid[dim] - centroidBounds.pMin[dim]) / (centroidBounds.pMax[dim] - centroidBounds.pMin[dim])); if (b == nBuckets) b = nBuckets-1; Assert(b >= 0 && b < nBuckets); buckets[b].count++; buckets[b].bounds = Union(buckets[b].bounds, buildData[i].bounds); } // Compute costs for splitting after each bucket float cost[nBuckets-1]; for (int i = 0; i < nBuckets-1; ++i) { BBox b0, b1; int count0 = 0, count1 = 0; for (int j = 0; j <= i; ++j) { b0 = Union(b0, buckets[j].bounds); count0 += buckets[j].count; } for (int j = i+1; j < nBuckets; ++j) { b1 = Union(b1, buckets[j].bounds); count1 += buckets[j].count; } cost[i] = .125f + (count0*b0.SurfaceArea() + count1*b1.SurfaceArea()) / bbox.SurfaceArea(); } // Find bucket to split at that minimizes SAH metric float minCost = cost[0]; uint32_t minCostSplit = 0; for (int i = 1; i < nBuckets-1; ++i) { if (cost[i] < minCost) { minCost = cost[i]; minCostSplit = i; } } // Either create leaf or split primitives at selected SAH bucket if (nPrimitives > maxPrimsInNode || minCost < nPrimitives) { BVHPrimitiveInfo *pmid = std::partition(&buildData[start], &buildData[end-1]+1, CompareToBucket(minCostSplit, nBuckets, dim, centroidBounds)); mid = pmid - &buildData[0]; } else { // Create leaf _BVHBuildNode_ uint32_t firstPrimOffset = orderedPrims.size(); for (uint32_t i = start; i < end; ++i) { uint32_t primNum = buildData[i].primitiveNumber; orderedPrims.push_back(primitives[primNum]); } node->InitLeaf(firstPrimOffset, nPrimitives, bbox); return node; } } break; } } node->InitInterior(dim, recursiveBuild(buildArena, buildData, start, mid, totalNodes, orderedPrims), recursiveBuild(buildArena, buildData, mid, end, totalNodes, orderedPrims)); } return node; }