// Created on: 2016-04-13 // Created by: Denis BOGOLEPOV // Copyright (c) 2013-2016 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. #include #ifdef HAVE_TBB // On Windows, function TryEnterCriticalSection has appeared in Windows NT // and is surrounded by #ifdef in MS VC++ 7.1 headers. // Thus to use it we need to define appropriate macro saying that we will // run on Windows NT 4.0 at least #if defined(_WIN32) && !defined(_WIN32_WINNT) #define _WIN32_WINNT 0x0501 #endif #include #endif // ======================================================================= // function : Perform // purpose : // ======================================================================= template void BVH_RadixSorter::Perform (BVH_Set* theSet) { Perform (theSet, 0, theSet->Size() - 1); } namespace BVH { // Radix sort STL predicate for 32-bit integer. class BitPredicate { Standard_Integer myBit; public: //! Creates new radix sort predicate. BitPredicate (const Standard_Integer theBit) : myBit (theBit) { // } //! Returns predicate value. bool operator() (const BVH_EncodedLink theLink) const { const Standard_Integer aMask = 1 << myBit; return !(theLink.first & aMask); // 0-bit to the left side } }; //! STL compare tool used in binary search algorithm. class BitComparator { Standard_Integer myBit; public: //! Creates new STL comparator. BitComparator (const Standard_Integer theBit) : myBit (theBit) { // } //! Checks left value for the given bit. bool operator() (BVH_EncodedLink theLink1, BVH_EncodedLink /*theLink2*/) { return !(theLink1.first & (1 << myBit)); } }; //! Tool object for sorting link array using radix sort algorithm. class RadixSorter { public: typedef NCollection_Array1::iterator LinkIterator; private: //! TBB functor class to run sorting. class Functor { //! Start element of exclusive sorting range. LinkIterator myStart; //! Final element of exclusive sorting range. LinkIterator myFinal; //! Bit number used for partition operation. Standard_Integer myDigit; public: //! Creates new sorting functor. Functor (LinkIterator theStart, LinkIterator theFinal, Standard_Integer theDigit) : myStart (theStart), myFinal (theFinal), myDigit (theDigit) { } //! Runs sorting function for the given range. void operator() () const { RadixSorter::Sort (myStart, myFinal, myDigit); } }; public: static void Sort (LinkIterator theStart, LinkIterator theFinal, Standard_Integer theDigit) { #ifdef HAVE_TBB if (theDigit < 24) { BVH::RadixSorter::perform (theStart, theFinal, theDigit); } else { LinkIterator anOffset = std::partition (theStart, theFinal, BitPredicate (theDigit)); tbb::parallel_invoke (Functor (theStart, anOffset, theDigit - 1), Functor (anOffset, theFinal, theDigit - 1)); } #else BVH::RadixSorter::perform (theStart, theFinal, theDigit); #endif } protected: // Performs MSD (most significant digit) radix sort. static void perform (LinkIterator theStart, LinkIterator theFinal, Standard_Integer theBit = 29) { while (theStart != theFinal && theBit >= 0) { LinkIterator anOffset = std::partition (theStart, theFinal, BitPredicate (theBit--)); perform (theStart, anOffset, theBit); theStart = anOffset; } } }; } // ======================================================================= // function : Perform // purpose : // ======================================================================= template void BVH_RadixSorter::Perform (BVH_Set* theSet, const Standard_Integer theStart, const Standard_Integer theFinal) { Standard_STATIC_ASSERT (N == 3 || N == 4); const Standard_Integer aDimensionX = 1024; const Standard_Integer aDimensionY = 1024; const Standard_Integer aDimensionZ = 1024; const BVH_VecNt aSceneMin = myBox.CornerMin(); const BVH_VecNt aSceneMax = myBox.CornerMax(); const T aReverseSizeX = static_cast (aDimensionX) / Max (static_cast (BVH::THE_NODE_MIN_SIZE), aSceneMax.x() - aSceneMin.x()); const T aReverseSizeY = static_cast (aDimensionY) / Max (static_cast (BVH::THE_NODE_MIN_SIZE), aSceneMax.y() - aSceneMin.y()); const T aReverseSizeZ = static_cast (aDimensionZ) / Max (static_cast (BVH::THE_NODE_MIN_SIZE), aSceneMax.z() - aSceneMin.z()); myEncodedLinks = new NCollection_Array1 (theStart, theFinal); // Step 1 -- Assign Morton code to each primitive for (Standard_Integer aPrimIdx = theStart; aPrimIdx <= theFinal; ++aPrimIdx) { const BVH_VecNt aCenter = theSet->Box (aPrimIdx).Center(); Standard_Integer aVoxelX = BVH::IntFloor ((aCenter.x() - aSceneMin.x()) * aReverseSizeX); Standard_Integer aVoxelY = BVH::IntFloor ((aCenter.y() - aSceneMin.y()) * aReverseSizeY); Standard_Integer aVoxelZ = BVH::IntFloor ((aCenter.z() - aSceneMin.z()) * aReverseSizeZ); aVoxelX = Max (0, Min (aVoxelX, aDimensionX - 1)); aVoxelY = Max (0, Min (aVoxelY, aDimensionY - 1)); aVoxelZ = Max (0, Min (aVoxelZ, aDimensionZ - 1)); aVoxelX = (aVoxelX | (aVoxelX << 16)) & 0x030000FF; aVoxelX = (aVoxelX | (aVoxelX << 8)) & 0x0300F00F; aVoxelX = (aVoxelX | (aVoxelX << 4)) & 0x030C30C3; aVoxelX = (aVoxelX | (aVoxelX << 2)) & 0x09249249; aVoxelY = (aVoxelY | (aVoxelY << 16)) & 0x030000FF; aVoxelY = (aVoxelY | (aVoxelY << 8)) & 0x0300F00F; aVoxelY = (aVoxelY | (aVoxelY << 4)) & 0x030C30C3; aVoxelY = (aVoxelY | (aVoxelY << 2)) & 0x09249249; aVoxelZ = (aVoxelZ | (aVoxelZ << 16)) & 0x030000FF; aVoxelZ = (aVoxelZ | (aVoxelZ << 8)) & 0x0300F00F; aVoxelZ = (aVoxelZ | (aVoxelZ << 4)) & 0x030C30C3; aVoxelZ = (aVoxelZ | (aVoxelZ << 2)) & 0x09249249; myEncodedLinks->ChangeValue (aPrimIdx) = BVH_EncodedLink ( aVoxelX | (aVoxelY << 1) | (aVoxelZ << 2), aPrimIdx); } // Step 2 -- Sort primitives by their Morton codes using radix sort BVH::RadixSorter::Sort (myEncodedLinks->begin(), myEncodedLinks->end(), 29); NCollection_Array1 aLinkMap (theStart, theFinal); for (Standard_Integer aLinkIdx = theStart; aLinkIdx <= theFinal; ++aLinkIdx) { aLinkMap (myEncodedLinks->Value (aLinkIdx).second) = aLinkIdx; } // Step 3 -- Rearranging primitive list according to Morton codes (in place) Standard_Integer aPrimIdx = theStart; while (aPrimIdx <= theFinal) { const Standard_Integer aSortIdx = aLinkMap (aPrimIdx); if (aPrimIdx != aSortIdx) { theSet->Swap (aPrimIdx, aSortIdx); std::swap (aLinkMap (aPrimIdx), aLinkMap (aSortIdx)); } else { ++aPrimIdx; } } }