// Created on: 2013-12-20 // Created by: Denis BOGOLEPOV // Copyright (c) 2013-2014 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #ifndef BVH_BinnedBuilder_HeaderFile #define BVH_BinnedBuilder_HeaderFile #include "BVH_QueueBuilder.hxx" #include #if defined(_WIN32) && defined(max) #undef max #endif #include //! Stores parameters of single bin (slice of AABB). template struct BVH_Bin { //! Creates new node bin. BVH_Bin() : Count(0) { } int Count; //!< Number of primitives in the bin BVH_Box Box; //!< AABB of primitives in the bin }; //! Performs construction of BVH tree using binned SAH algorithm. Number //! of bins controls BVH quality in cost of construction time (greater - //! better). For optimal results, use 32 - 48 bins. However, reasonable //! performance is provided even for 4 - 8 bins (it is only 10-20% lower //! in comparison with optimal settings). Note that multiple threads can //! be used only with thread safe BVH primitive sets. template class BVH_BinnedBuilder : public BVH_QueueBuilder { public: //! Type of the array of bins of BVH tree node. typedef BVH_Bin BVH_BinVector[Bins]; //! Describes split plane candidate. struct BVH_SplitPlane { BVH_Bin LftVoxel; BVH_Bin RghVoxel; }; //! Type of the array of split plane candidates. typedef BVH_SplitPlane BVH_SplitPlanes[Bins + 1]; public: //! Creates binned SAH BVH builder. BVH_BinnedBuilder(const int theLeafNodeSize = BVH_Constants_LeafNodeSizeDefault, const int theMaxTreeDepth = BVH_Constants_MaxTreeDepth, const bool theDoMainSplits = false, const int theNumOfThreads = 1) : BVH_QueueBuilder(theLeafNodeSize, theMaxTreeDepth, theNumOfThreads), myUseMainAxis(theDoMainSplits) { // } //! Releases resources of binned SAH BVH builder. ~BVH_BinnedBuilder() override = default; protected: //! Performs splitting of the given BVH node. typename BVH_QueueBuilder::BVH_ChildNodes buildNode(BVH_Set* theSet, BVH_Tree* theBVH, const int theNode) const override; //! Arranges node primitives into bins. virtual void getSubVolumes(BVH_Set* theSet, BVH_Tree* theBVH, const int theNode, BVH_BinVector& theBins, const int theAxis) const; private: // clang-format off bool myUseMainAxis; //!< Defines whether to search for the best split or use the widest axis // clang-format on }; //================================================================================================= template void BVH_BinnedBuilder::getSubVolumes(BVH_Set* theSet, BVH_Tree* theBVH, const int theNode, BVH_BinVector& theBins, const int theAxis) const { const T aMin = BVH::VecComp::Get(theBVH->MinPoint(theNode), theAxis); const T aMax = BVH::VecComp::Get(theBVH->MaxPoint(theNode), theAxis); const T anInverseStep = static_cast(Bins) / (aMax - aMin); for (int anIdx = theBVH->BegPrimitive(theNode); anIdx <= theBVH->EndPrimitive(theNode); ++anIdx) { typename BVH_Set::BVH_BoxNt aBox = theSet->Box(anIdx); int aBinIndex = BVH::IntFloor((theSet->Center(anIdx, theAxis) - aMin) * anInverseStep); if (aBinIndex < 0) { aBinIndex = 0; } else if (aBinIndex >= Bins) { aBinIndex = Bins - 1; } theBins[aBinIndex].Count++; theBins[aBinIndex].Box.Combine(aBox); } } namespace BVH { template int SplitPrimitives(BVH_Set* theSet, const BVH_Box& theBox, const int theBeg, const int theEnd, const int theBin, const int theAxis, const int theBins) { const T aMin = BVH::VecComp::Get(theBox.CornerMin(), theAxis); const T aMax = BVH::VecComp::Get(theBox.CornerMax(), theAxis); const T anInverseStep = static_cast(theBins) / (aMax - aMin); int aLftIdx(theBeg); int aRghIdx(theEnd); do { while (BVH::IntFloor((theSet->Center(aLftIdx, theAxis) - aMin) * anInverseStep) <= theBin && aLftIdx < theEnd) { ++aLftIdx; } while (BVH::IntFloor((theSet->Center(aRghIdx, theAxis) - aMin) * anInverseStep) > theBin && aRghIdx > theBeg) { --aRghIdx; } if (aLftIdx <= aRghIdx) { if (aLftIdx != aRghIdx) { theSet->Swap(aLftIdx, aRghIdx); } ++aLftIdx; --aRghIdx; } } while (aLftIdx <= aRghIdx); return aLftIdx; } template struct BVH_AxisSelector { typedef typename BVH::VectorType::Type BVH_VecNt; static int MainAxis(const BVH_VecNt& theSize) { if (theSize.y() > theSize.x()) { return theSize.y() > theSize.z() ? 1 : 2; } else { return theSize.z() > theSize.x() ? 2 : 0; } } }; template struct BVH_AxisSelector { typedef typename BVH::VectorType::Type BVH_VecNt; static int MainAxis(const BVH_VecNt& theSize) { return theSize.x() > theSize.y() ? 0 : 1; } }; } // namespace BVH //================================================================================================= template typename BVH_QueueBuilder::BVH_ChildNodes BVH_BinnedBuilder::buildNode( BVH_Set* theSet, BVH_Tree* theBVH, const int theNode) const { const int aNodeBegPrimitive = theBVH->BegPrimitive(theNode); const int aNodeEndPrimitive = theBVH->EndPrimitive(theNode); const int aNodeNbPrimitives = theBVH->NbPrimitives(theNode); if (aNodeNbPrimitives <= BVH_Builder::myLeafNodeSize) { // clang-format off return typename BVH_QueueBuilder::BVH_ChildNodes(); // node does not require partitioning // clang-format on } const BVH_Box anAABB(theBVH->MinPoint(theNode), theBVH->MaxPoint(theNode)); const typename BVH_Box::BVH_VecNt aSize = anAABB.Size(); // Parameters for storing best split int aMinSplitAxis = -1; int aMinSplitIndex = 0; int aMinSplitNumLft = 0; int aMinSplitNumRgh = 0; BVH_Box aMinSplitBoxLft; BVH_Box aMinSplitBoxRgh; double aMinSplitCost = std::numeric_limits::max(); const int aMainAxis = BVH::BVH_AxisSelector::MainAxis(aSize); // Find best split for (int anAxis = myUseMainAxis ? aMainAxis : 0; anAxis <= (myUseMainAxis ? aMainAxis : (std::min)(N - 1, 2)); ++anAxis) { if (BVH::VecComp::Get(aSize, anAxis) <= BVH::THE_NODE_MIN_SIZE) { continue; } BVH_BinVector aBinVector; getSubVolumes(theSet, theBVH, theNode, aBinVector, anAxis); BVH_SplitPlanes aSplitPlanes; for (int aLftSplit = 1, aRghSplit = Bins - 1; aLftSplit < Bins; ++aLftSplit, --aRghSplit) { aSplitPlanes[aLftSplit].LftVoxel.Count = aSplitPlanes[aLftSplit - 1].LftVoxel.Count + aBinVector[aLftSplit - 1].Count; aSplitPlanes[aRghSplit].RghVoxel.Count = aSplitPlanes[aRghSplit + 1].RghVoxel.Count + aBinVector[aRghSplit + 0].Count; aSplitPlanes[aLftSplit].LftVoxel.Box = aSplitPlanes[aLftSplit - 1].LftVoxel.Box; aSplitPlanes[aRghSplit].RghVoxel.Box = aSplitPlanes[aRghSplit + 1].RghVoxel.Box; aSplitPlanes[aLftSplit].LftVoxel.Box.Combine(aBinVector[aLftSplit - 1].Box); aSplitPlanes[aRghSplit].RghVoxel.Box.Combine(aBinVector[aRghSplit + 0].Box); } // Choose the best split (with minimum SAH cost) const double aParentArea = static_cast(anAABB.Area()); for (int aSplit = 1; aSplit < Bins; ++aSplit) { // SAH evaluation with proper normalization by parent surface area const double aLftArea = static_cast(aSplitPlanes[aSplit].LftVoxel.Box.Area()); const double aRghArea = static_cast(aSplitPlanes[aSplit].RghVoxel.Box.Area()); double aCost = (aLftArea / aParentArea) * aSplitPlanes[aSplit].LftVoxel.Count + (aRghArea / aParentArea) * aSplitPlanes[aSplit].RghVoxel.Count; if (aCost <= aMinSplitCost) { aMinSplitCost = aCost; aMinSplitAxis = anAxis; aMinSplitIndex = aSplit; aMinSplitBoxLft = aSplitPlanes[aSplit].LftVoxel.Box; aMinSplitBoxRgh = aSplitPlanes[aSplit].RghVoxel.Box; aMinSplitNumLft = aSplitPlanes[aSplit].LftVoxel.Count; aMinSplitNumRgh = aSplitPlanes[aSplit].RghVoxel.Count; } } } theBVH->SetInner(theNode); int aMiddle = -1; if (aMinSplitNumLft == 0 || aMinSplitNumRgh == 0 || aMinSplitAxis == -1) // case of objects with the same center { aMinSplitBoxLft.Clear(); aMinSplitBoxRgh.Clear(); aMiddle = (std::max)(aNodeBegPrimitive + 1, static_cast((aNodeBegPrimitive + aNodeEndPrimitive) / 2.f)); aMinSplitNumLft = aMiddle - aNodeBegPrimitive; for (int anIndex = aNodeBegPrimitive; anIndex < aMiddle; ++anIndex) { aMinSplitBoxLft.Combine(theSet->Box(anIndex)); } aMinSplitNumRgh = aNodeEndPrimitive - aMiddle + 1; for (int anIndex = aNodeEndPrimitive; anIndex >= aMiddle; --anIndex) { aMinSplitBoxRgh.Combine(theSet->Box(anIndex)); } } else { aMiddle = BVH::SplitPrimitives(theSet, anAABB, aNodeBegPrimitive, aNodeEndPrimitive, aMinSplitIndex - 1, aMinSplitAxis, Bins); } typedef typename BVH_QueueBuilder::BVH_PrimitiveRange Range; return typename BVH_QueueBuilder::BVH_ChildNodes(aMinSplitBoxLft, aMinSplitBoxRgh, Range(aNodeBegPrimitive, aMiddle - 1), Range(aMiddle, aNodeEndPrimitive)); } #endif // _BVH_BinnedBuilder_Header