69#include <boost/range/algorithm/nth_element.hpp>
70#include <boost/range/algorithm/partition.hpp>
77#ifdef PRINT_STATISTICS_3D_VIEWER
94 centroid( .5f * aBounds.Min() + .5f * aBounds.Max() ) {}
146 wxASSERT( x <= (1 << 10) );
148 if( x == ( 1 << 10 ) )
151 x = ( x | ( x << 16 ) ) & 0b00000011000000000000000011111111;
153 x = ( x | ( x << 8 ) ) & 0b00000011000000001111000000001111;
155 x = ( x | ( x << 4 ) ) & 0b00000011000011000011000011000011;
157 x = ( x | ( x << 2 ) ) & 0b00001001001001001001001001001001;
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 ) );
176 std::vector<MortonPrimitive> tempVector( v->size() );
178 const int bitsPerPass = 6;
179 const int nBits = 30;
181 wxASSERT( ( nBits % bitsPerPass ) == 0 );
183 const int nPasses = nBits / bitsPerPass;
185 for(
int pass = 0; pass < nPasses; ++pass )
188 const int lowBit = pass * bitsPerPass;
191 std::vector<MortonPrimitive>& in = ( pass & 1 ) ? tempVector : *v;
192 std::vector<MortonPrimitive>& out = ( pass & 1 ) ? *v : tempVector;
195 const int nBuckets = 1 << bitsPerPass;
196 int bucketCount[nBuckets] = {0};
197 const int bitMask = ( 1 << bitsPerPass ) - 1;
199 for( uint32_t i = 0; i < in.size(); ++i )
202 int bucket = ( mp.
mortonCode >> lowBit ) & bitMask;
204 wxASSERT( ( bucket >= 0 ) && ( bucket < nBuckets ) );
206 ++bucketCount[bucket];
210 int startIndex[nBuckets];
213 for(
int i = 1; i < nBuckets; ++i )
214 startIndex[i] = startIndex[i - 1] + bucketCount[i - 1];
217 for( uint32_t i = 0; i < in.size(); ++i )
220 int bucket = (mp.
mortonCode >> lowBit) & bitMask;
221 out[startIndex[bucket]++] = mp;
227 std::swap( *v, tempVector );
236 if( aObjectContainer.
GetList().empty() )
255 std::vector<BVHPrimitiveInfo> primitiveInfo(
m_primitives.size() );
259 wxASSERT( m_primitives[i]->GetBBox().IsInitialized() );
261 primitiveInfo[i] = BVHPrimitiveInfo( i, m_primitives[i]->GetBBox() );
267 std::vector<const OBJECT_3D*> orderedPrims;
268 orderedPrims.clear();
274 root =
HLBVHBuild( primitiveInfo, &totalNodes, orderedPrims );
286 for(
int i = 0; i < totalNodes; ++i )
289 m_nodes[i].primitivesOffset = 0;
298 wxASSERT( offset == (
unsigned int)totalNodes );
375 wxASSERT( ( b >= 0 ) && ( b <
nBuckets ) );
423 int start,
int end,
int* totalNodes,
424 std::vector<const OBJECT_3D*>& orderedPrims )
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() );
451 for(
int i = start; i <
end; ++i )
452 bounds.
Union( primitiveInfo[i].bounds );
454 int nPrimitives =
end - start;
456 if( nPrimitives == 1 )
459 int firstPrimOffset = orderedPrims.size();
461 for(
int i = start; i <
end; ++i )
463 int primitiveNr = primitiveInfo[i].primitiveNumber;
468 node->
InitLeaf( firstPrimOffset, nPrimitives, bounds );
474 centroidBounds.
Reset();
476 for(
int i = start; i <
end; ++i )
477 centroidBounds.
Union( primitiveInfo[i].centroid );
482 int mid = (start +
end) / 2;
484 if( fabs( centroidBounds.
Max()[dim] -
485 centroidBounds.
Min()[dim] ) < (FLT_EPSILON + FLT_EPSILON) )
488 const int firstPrimOffset = orderedPrims.size();
490 for(
int i = start; i <
end; ++i )
492 int primitiveNr = primitiveInfo[i].primitiveNumber;
494 wxASSERT( ( primitiveNr >= 0 ) && ( primitiveNr < (
int)
m_primitives.size() ) );
498 wxASSERT( obj !=
nullptr );
500 orderedPrims.push_back( obj );
503 node->
InitLeaf( firstPrimOffset, nPrimitives, bounds );
513 float pmid = centroidBounds.
GetCenter( dim );
516 &primitiveInfo[
end - 1] + 1,
518 mid = midPtr - &primitiveInfo[0];
520 wxASSERT( ( mid >= start ) && ( mid <=
end ) );
522 if( ( mid != start ) && ( mid !=
end ) )
533 mid = ( start +
end ) / 2;
535 std::nth_element( &primitiveInfo[start], &primitiveInfo[mid],
545 if( nPrimitives <= 2 )
548 mid = ( start +
end ) / 2;
550 std::nth_element( &primitiveInfo[start], &primitiveInfo[mid],
556 const int nBuckets = 12;
560 for(
int i = 0; i < nBuckets; ++i )
562 buckets[i].
count = 0;
567 for(
int i = start; i <
end; ++i )
570 centroidBounds.
Offset( primitiveInfo[i].centroid )[dim];
575 wxASSERT( b >= 0 && b < nBuckets );
578 buckets[b].
bounds.
Union( primitiveInfo[i].bounds );
582 float cost[nBuckets - 1];
584 for(
int i = 0; i < ( nBuckets - 1 ); ++i )
594 for(
int j = 0; j <= i; ++j )
596 if( buckets[j].count )
598 count0 += buckets[j].
count;
599 b0.
Union( buckets[j].bounds );
603 for(
int j = i + 1; j < nBuckets; ++j )
605 if( buckets[j].count )
607 count1 += buckets[j].
count;
608 b1.
Union( buckets[j].bounds );
617 float minCost = cost[0];
618 int minCostSplitBucket = 0;
620 for(
int i = 1; i < ( nBuckets - 1 ); ++i )
622 if( cost[i] < minCost )
625 minCostSplitBucket = i;
630 if( ( nPrimitives >
m_maxPrimsInNode ) || ( minCost < (
float) nPrimitives ) )
633 std::partition( &primitiveInfo[start], &primitiveInfo[
end - 1] + 1,
635 dim, centroidBounds ) );
636 mid = pmid - &primitiveInfo[0];
638 wxASSERT( ( mid >= start ) && ( mid <=
end ) );
643 const int firstPrimOffset = orderedPrims.size();
645 for(
int i = start; i <
end; ++i )
647 const int primitiveNr = primitiveInfo[i].primitiveNumber;
654 node->
InitLeaf( firstPrimOffset, nPrimitives, bounds );
675 int* totalNodes, std::vector<const OBJECT_3D*>& orderedPrims )
681 for(
unsigned int i = 0; i < primitiveInfo.size(); ++i )
682 bounds.
Union( primitiveInfo[i].centroid );
685 std::vector<MortonPrimitive> mortonPrims( primitiveInfo.size() );
687 for(
int i = 0; i < (int)primitiveInfo.size(); ++i )
690 const int mortonBits = 10;
691 const int mortonScale = 1 << mortonBits;
693 wxASSERT( primitiveInfo[i].primitiveNumber < (
int)primitiveInfo.size() );
695 mortonPrims[i].primitiveIndex = primitiveInfo[i].primitiveNumber;
697 const SFVEC3F centroidOffset = bounds.
Offset( primitiveInfo[i].centroid );
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 ) );
712 std::vector<LBVHTreelet> treeletsToBuild;
714 for(
int start = 0,
end = 1;
end <= (int)mortonPrims.size(); ++
end )
716 const uint32_t mask = 0b00111111111111000000000000000000;
718 if( (
end == (
int) mortonPrims.size() )
719 || ( ( mortonPrims[start].mortonCode & mask )
720 != ( mortonPrims[
end].mortonCode & mask ) ) )
723 const int numPrimitives =
end - start;
724 const int maxBVHNodes = 2 * numPrimitives;
732 for(
int i = 0; i < maxBVHNodes; ++i )
748 treeletsToBuild.push_back( tmpTreelet );
756 int orderedPrimsOffset = 0;
763 int nodesCreated = 0;
764 const int firstBit = 29 - 12;
768 wxASSERT( tr.
startIndex < (
int)mortonPrims.size() );
772 &orderedPrimsOffset, firstBit );
774 atomicTotal += nodesCreated;
777 *totalNodes = atomicTotal;
780 std::vector<BVHBuildNode *> finishedTreelets;
781 finishedTreelets.reserve( treeletsToBuild.size() );
784 finishedTreelets.push_back( treeletsToBuild[
index].buildNodes );
787 return buildUpperSAH( finishedTreelets, 0, finishedTreelets.size(), totalNodes );
793 std::vector<const OBJECT_3D*>& orderedPrims,
int *orderedPrimsOffset,
int bit )
795 wxASSERT( nPrimitives > 0 );
796 wxASSERT( totalNodes !=
nullptr );
797 wxASSERT( orderedPrimsOffset !=
nullptr );
798 wxASSERT( nPrimitives > 0 );
799 wxASSERT( mortonPrims !=
nullptr );
810 int firstPrimOffset = *orderedPrimsOffset;
811 *orderedPrimsOffset += nPrimitives;
813 wxASSERT( ( firstPrimOffset + ( nPrimitives - 1 ) ) < (
int) orderedPrims.size() );
815 for(
int i = 0; i < nPrimitives; ++i )
821 orderedPrims[firstPrimOffset + i] =
m_primitives[primitiveIndex];
822 bounds.
Union( primitiveInfo[primitiveIndex].bounds );
825 node->
InitLeaf( firstPrimOffset, nPrimitives, bounds );
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 );
841 int searchEnd = nPrimitives - 1;
843 while( searchStart + 1 != searchEnd )
845 wxASSERT( searchStart != searchEnd );
847 const int mid = ( searchStart + searchEnd ) / 2;
849 if( ( mortonPrims[searchStart].mortonCode & mask ) ==
850 ( mortonPrims[mid].mortonCode & mask ) )
854 wxASSERT( ( mortonPrims[mid].mortonCode & mask ) ==
855 ( mortonPrims[searchEnd].mortonCode & mask ) );
860 const int splitOffset = searchEnd;
862 wxASSERT( splitOffset <= ( nPrimitives - 1 ) );
863 wxASSERT( ( mortonPrims[splitOffset - 1].mortonCode & mask )
864 != ( mortonPrims[splitOffset].mortonCode & mask ) );
872 lbvh[0] =
emitLBVH( buildNodes, primitiveInfo, mortonPrims, splitOffset,
873 totalNodes, orderedPrims, orderedPrimsOffset, bit - 1 );
875 lbvh[1] =
emitLBVH( buildNodes, primitiveInfo, &mortonPrims[splitOffset],
876 nPrimitives - splitOffset, totalNodes, orderedPrims,
877 orderedPrimsOffset, bit - 1 );
879 const int axis = bit % 3;
889 int end,
int* totalNodes )
891 wxASSERT( totalNodes !=
nullptr );
892 wxASSERT( start <
end );
893 wxASSERT(
end <= (
int)treeletRoots.size() );
895 int nNodes =
end - start;
898 return treeletRoots[start];
917 for(
int i = start; i <
end; ++i )
918 bounds.
Union( treeletRoots[i]->bounds );
922 centroidBounds.
Reset();
924 for(
int i = start; i <
end; ++i )
926 SFVEC3F centroid = ( treeletRoots[i]->bounds.Min() + treeletRoots[i]->bounds.
Max() ) * 0.5f;
928 centroidBounds.
Union( centroid );
935 wxASSERT( centroidBounds.
Max()[dim] != centroidBounds.
Min()[dim] );
938 const int nBuckets = 12;
942 for(
int i = 0; i < nBuckets; ++i )
944 buckets[i].
count = 0;
949 for(
int i = start; i <
end; ++i )
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] ) );
959 wxASSERT( ( b >= 0 ) && ( b < nBuckets ) );
962 buckets[b].
bounds.
Union( treeletRoots[i]->bounds );
966 float cost[nBuckets - 1];
968 for(
int i = 0; i < nBuckets - 1; ++i )
974 int count0 = 0, count1 = 0;
976 for(
int j = 0; j <= i; ++j )
978 if( buckets[j].count )
980 count0 += buckets[j].
count;
981 b0.
Union( buckets[j].bounds );
985 for(
int j = i + 1; j < nBuckets; ++j )
987 if( buckets[j].count )
989 count1 += buckets[j].
count;
990 b1.
Union( buckets[j].bounds );
999 float minCost = cost[0];
1000 int minCostSplitBucket = 0;
1002 for(
int i = 1; i < nBuckets - 1; ++i )
1004 if( cost[i] < minCost )
1007 minCostSplitBucket = i;
1012 BVHBuildNode **pmid = std::partition( &treeletRoots[start], &treeletRoots[
end - 1] + 1,
1014 dim, centroidBounds ) );
1016 const int mid = pmid - &treeletRoots[0];
1018 wxASSERT( ( mid > start ) && ( mid <
end ) );
1034 int myOffset = (*offset)++;
1068 int todoOffset = 0, nodeNum = 0;
1078 float hitBox = 0.0f;
1082 if( hitted && ( hitBox < aHitInfo.
m_tHit ) )
1101 todo[todoOffset++] = nodeNum + 1;
1107 nodeNum = nodeNum + 1;
1114 if( todoOffset == 0 )
1117 nodeNum = todo[--todoOffset];
1133 int todoOffset = 0, nodeNum = aAccNodeInfo;
1143 float hitBox = 0.0f;
1147 if( hitted && ( hitBox < aHitInfo.
m_tHit ) )
1166 todo[todoOffset++] = nodeNum + 1;
1172 nodeNum = nodeNum + 1;
1179 if( todoOffset == 0 )
1182 nodeNum = todo[--todoOffset];
1195 int todoOffset = 0, nodeNum = 0;
1205 float hitBox = 0.0f;
1209 if( hitted && ( hitBox < aMaxDistance ) )
1228 todo[todoOffset++] = nodeNum + 1;
1234 nodeNum = nodeNum + 1;
1241 if( todoOffset == 0 )
1244 nodeNum = todo[--todoOffset];
Defines math related functions.
uint32_t EncodeMorton3(const SFVEC3F &v)
uint32_t LeftShift3(uint32_t x)
static void RadixSort(std::vector< MortonPrimitive > *v)
This BVH implementation is based on the source code implementation from the book "Physically Based Re...
std::list< void * > m_nodesToFree
int flattenBVHTree(BVHBuildNode *node, uint32_t *offset)
BVHBuildNode * HLBVHBuild(const std::vector< BVHPrimitiveInfo > &primitiveInfo, int *totalNodes, std::vector< const OBJECT_3D * > &orderedPrims)
BVHBuildNode * recursiveBuild(std::vector< BVHPrimitiveInfo > &primitiveInfo, int start, int end, int *totalNodes, std::vector< const OBJECT_3D * > &orderedPrims)
BVH_PBRT(const CONTAINER_3D_BASE &aObjectContainer, int aMaxPrimsInNode=4, SPLITMETHOD aSplitMethod=SPLITMETHOD::SAH)
std::vector< const OBJECT_3D * > m_primitives
BVHBuildNode * buildUpperSAH(std::vector< BVHBuildNode * > &treeletRoots, int start, int end, int *totalNodes)
const int m_maxPrimsInNode
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.
SPLITMETHOD m_splitMethod
const std::list< OBJECT_3D * > & GetList() const
void ConvertTo(std::vector< const OBJECT_3D * > &aOutVector) const
bool GetCastShadows() const
virtual bool IntersectP(const RAY &aRay, float aMaxDistance) const =0
const MATERIAL * GetMaterial() const
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 ...
#define RAYPACKET_RAYS_PER_PACKET
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.
bool Intersect(const RAY &aRay, float *t) const
void Union(const SFVEC3F &aPoint)
Recalculate the bounding box adding a point.
SFVEC3F GetCenter() const
Return the center point of the bounding box.
const SFVEC3F & Min() const
Return the minimum vertex pointer.
SFVEC3F Offset(const SFVEC3F &p) const
const SFVEC3F & Max() const
Return the maximum vertex pointer.
void Reset()
Reset the bounding box to zero and de-initialize it.
unsigned int MaxDimension() const
float SurfaceArea() const
void InitLeaf(int first, int n, const BBOX_3D &b)
void InitInterior(int axis, BVHBuildNode *c0, BVHBuildNode *c1)
BVHBuildNode * children[2]
BVHPrimitiveInfo(int aPrimitiveNumber, const BBOX_3D &aBounds)
bool operator()(const BVHPrimitiveInfo &a, const BVHPrimitiveInfo &b) const
const BBOX_3D & centroidBounds
CompareToBucket(int split, int num, int d, const BBOX_3D &b)
bool operator()(const BVHPrimitiveInfo &p) const
CompareToMid(int d, float m)
bool operator()(const BVHPrimitiveInfo &a) const
Stores the hit information of a ray with a point on the surface of a object.
unsigned int m_acc_node_info
( 4) The acc stores here the node that it hits
float m_tHit
( 4) distance
HLBVH_SAH_Evaluator(int split, int num, int d, const BBOX_3D &b)
const BBOX_3D & centroidBounds
bool operator()(const BVHBuildNode *node) const
BVHBuildNode * buildNodes
int secondChildOffset
interior
uint8_t axis
interior node: xyz
uint16_t nPrimitives
0 -> interior node
unsigned int m_dirIsNeg[3]