diff --git a/ApplicationLibCode/ReservoirDataModel/RigMainGrid.cpp b/ApplicationLibCode/ReservoirDataModel/RigMainGrid.cpp index 1dd95f9224..6b19a14d08 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigMainGrid.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigMainGrid.cpp @@ -889,6 +889,7 @@ std::tuple RigMainGrid::min std::vector RigMainGrid::findIntersectingCells( const cvf::BoundingBox& inputBB ) const { cvf::ref searchTree; + size_t cellsPerBoundingBox = 1; { // Lazily build the cell search tree under a lock to avoid a use-after-free / data race when // findIntersectingCells is called concurrently from multiple threads (e.g. OpenMP regions). @@ -900,13 +901,21 @@ std::vector RigMainGrid::findIntersectingCells( const cvf::BoundingBox& // Keep a local reference so the tree stays alive if another thread rebuilds m_cellSearchTree // while this thread performs the read-only intersection query below. - searchTree = m_cellSearchTree; + searchTree = m_cellSearchTree; + cellsPerBoundingBox = m_cellSearchTreeCellsPerBoundingBox; } std::vector cellIndices; if ( searchTree.notNull() ) { searchTree->findIntersections( inputBB, &cellIndices ); + + if ( cellsPerBoundingBox > 1 ) + { + // A leaf of the tree contains several cells. Remove the cells not intersecting the input bounding box, so + // the result is the same as for a tree with one cell per leaf. + std::erase_if( cellIndices, [&]( size_t cellIndex ) { return !cellBoundingBox( cellIndex ).intersects( inputBB ); } ); + } } return cellIndices; } @@ -916,22 +925,16 @@ std::vector RigMainGrid::findIntersectingCells( const cvf::BoundingBox& //-------------------------------------------------------------------------------------------------- void RigMainGrid::doBuildCellSearchTree( std::string* aabbTreeInfo ) const { - const double maxNumberOfLeafNodes = 4000000; - // Use the total cell count (including LGR cells) when deciding the number of cells per bounding box. - // The non-optimized buildCellSearchTree() creates one leaf per cell across totalCellCount(), so basing - // the threshold on the main grid cell count only can route LGR-heavy grids into that path and exhaust - // memory while building the search tree. - const double factor = std::ceil( totalCellCount() / maxNumberOfLeafNodes ); - const size_t cellsPerBoundingBox = std::max( size_t( 1 ), static_cast( factor ) ); + // Each leaf of the tree contains several cells along K. A tree with one cell per leaf becomes very deep for large + // grids and is slow to build. For very large grids, increase the number of cells per leaf to limit the memory use. + // Use the total cell count (including LGR cells) when deciding the number of cells per leaf. + const size_t minCellsPerBoundingBox = 8; + const double maxNumberOfLeafNodes = 4000000; + const double factor = std::ceil( totalCellCount() / maxNumberOfLeafNodes ); + const size_t cellsPerBoundingBox = std::max( minCellsPerBoundingBox, static_cast( factor ) ); - if ( cellsPerBoundingBox > 1 ) - { - buildCellSearchTreeOptimized( cellsPerBoundingBox ); - } - else - { - buildCellSearchTree(); - } + buildCellSearchTree( cellsPerBoundingBox ); + m_cellSearchTreeCellsPerBoundingBox = cellsPerBoundingBox; if ( aabbTreeInfo ) { @@ -943,88 +946,28 @@ void RigMainGrid::doBuildCellSearchTree( std::string* aabbTreeInfo ) const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigMainGrid::buildCellSearchTree() const +void RigMainGrid::buildCellSearchTree( size_t cellsPerBoundingBox ) const { - if ( m_cellSearchTree.isNull() ) - { - // build tree - - size_t cellCount = totalCellCount(); - - std::vector cellIndicesForBoundingBoxes; - std::vector cellBoundingBoxes; - -#pragma omp parallel - { - int numberOfThreads = RiaOpenMPTools::availableThreadCount(); - size_t threadCellCount = std::ceil( cellCount / static_cast( numberOfThreads ) ); - - std::vector threadIndicesForBoundingBoxes; - std::vector threadBoundingBoxes; - - threadIndicesForBoundingBoxes.reserve( threadCellCount ); - threadBoundingBoxes.reserve( threadCellCount ); - -#pragma omp for - for ( int cIdx = 0; cIdx < (int)cellCount; ++cIdx ) - { - auto& cell = this->cell( cIdx ); - if ( cell.isInvalid() ) continue; - - const std::array& cellIndices = cell.cornerIndices(); - - cvf::BoundingBox cellBB; - for ( size_t i : cellIndices ) - { - cellBB.add( m_nodes[i] ); - } - - if ( cellBB.isValid() ) - { - threadIndicesForBoundingBoxes.emplace_back( cIdx ); - threadBoundingBoxes.emplace_back( cellBB ); - } - } - - threadIndicesForBoundingBoxes.shrink_to_fit(); - threadBoundingBoxes.shrink_to_fit(); - -#pragma omp critical( critical_section_RigMainGrid_buildCellSearchTree ) - { - cellIndicesForBoundingBoxes.insert( cellIndicesForBoundingBoxes.end(), - threadIndicesForBoundingBoxes.begin(), - threadIndicesForBoundingBoxes.end() ); - - cellBoundingBoxes.insert( cellBoundingBoxes.end(), threadBoundingBoxes.begin(), threadBoundingBoxes.end() ); - } - } - m_cellSearchTree = new cvf::BoundingBoxTree; - m_cellSearchTree->buildTreeFromBoundingBoxes( cellBoundingBoxes, &cellIndicesForBoundingBoxes ); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RigMainGrid::buildCellSearchTreeOptimized( size_t cellsPerBoundingBox ) const -{ - // map from main grid cell index to list of LGR cells with main grid cell as parent cell - // used to speed up cell children lookup during search tree building + // Map from main grid cell index to the valid LGR cells inside it, including cells of nested LGRs. LGR cells + // without a main grid cell get a leaf of their own. std::map> subCellIndicesForMainGridCells; + std::vector subCellIndicesWithoutMainGridCell; for ( auto& subGrid : m_localGrids ) { - if ( subGrid->parentGrid() == this ) + for ( size_t localIdx = 0; localIdx < subGrid->cellCount(); localIdx++ ) { - for ( size_t localIdx = 0; localIdx < subGrid->cellCount(); localIdx++ ) + const auto& localCell = subGrid->cell( localIdx ); + if ( localCell.isInvalid() ) continue; + + const int reservoirCellIndex = static_cast( subGrid->reservoirCellIndex( localIdx ) ); + if ( localCell.mainGridCellIndex() < cellCount() ) { - const auto& localCell = subGrid->cell( localIdx ); - if ( localCell.isInvalid() ) continue; - if ( !subCellIndicesForMainGridCells.contains( localCell.mainGridCellIndex() ) ) - { - subCellIndicesForMainGridCells[localCell.mainGridCellIndex()] = {}; - } - subCellIndicesForMainGridCells[localCell.mainGridCellIndex()].push_back( (int)subGrid->reservoirCellIndex( localIdx ) ); + subCellIndicesForMainGridCells[localCell.mainGridCellIndex()].push_back( reservoirCellIndex ); + } + else + { + subCellIndicesWithoutMainGridCell.push_back( reservoirCellIndex ); } } } @@ -1053,36 +996,33 @@ void RigMainGrid::buildCellSearchTreeOptimized( size_t cellsPerBoundingBox ) con while ( ( kCount < cellsPerBoundingBox ) && ( k + kCount < cellCountK() ) ) { - size_t cellIdx = cellIndexFromIJK( i, j, k + kCount ); - const auto& rigCell = cell( cellIdx ); + size_t cellIdx = cellIndexFromIJK( i, j, k + kCount ); - if ( !rigCell.isInvalid() ) + if ( !cell( cellIdx ).isInvalid() ) { aggregatedCellIndices.push_back( static_cast( cellIdx ) ); - if ( subCellIndicesForMainGridCells.contains( cellIdx ) ) - { - const auto& subCellIndices = subCellIndicesForMainGridCells[cellIdx]; - aggregatedCellIndices.insert( aggregatedCellIndices.end(), subCellIndices.begin(), subCellIndices.end() ); - } - - const std::array& cellIndices = rigCell.cornerIndices(); - - cvf::BoundingBox cellBB; - for ( size_t i : cellIndices ) - { - cellBB.add( m_nodes[i] ); - } - + cvf::BoundingBox cellBB = cellBoundingBox( cellIdx ); if ( cellBB.isValid() ) accumulatedBB.add( cellBB ); } + + // Add the LGR cells also when the main grid cell is invalid + if ( auto it = subCellIndicesForMainGridCells.find( cellIdx ); it != subCellIndicesForMainGridCells.end() ) + { + for ( int subCellIdx : it->second ) + { + aggregatedCellIndices.push_back( subCellIdx ); + + cvf::BoundingBox cellBB = cellBoundingBox( subCellIdx ); + if ( cellBB.isValid() ) accumulatedBB.add( cellBB ); + } + } kCount++; } k += kCount; - kCount = 0; - threadCellIndicesForBoundingBoxes[myThread].emplace_back( aggregatedCellIndices ); + threadCellIndicesForBoundingBoxes[myThread].emplace_back( std::move( aggregatedCellIndices ) ); threadCellBoundingBoxes[myThread].emplace_back( accumulatedBB ); } } @@ -1095,16 +1035,35 @@ void RigMainGrid::buildCellSearchTreeOptimized( size_t cellsPerBoundingBox ) con for ( auto i = 0; i < threadCount; i++ ) { cellIndicesForBoundingBoxes.insert( cellIndicesForBoundingBoxes.end(), - threadCellIndicesForBoundingBoxes[i].begin(), - threadCellIndicesForBoundingBoxes[i].end() ); + std::make_move_iterator( threadCellIndicesForBoundingBoxes[i].begin() ), + std::make_move_iterator( threadCellIndicesForBoundingBoxes[i].end() ) ); cellBoundingBoxes.insert( cellBoundingBoxes.end(), threadCellBoundingBoxes[i].begin(), threadCellBoundingBoxes[i].end() ); } + for ( int subCellIdx : subCellIndicesWithoutMainGridCell ) + { + cellIndicesForBoundingBoxes.push_back( { subCellIdx } ); + cellBoundingBoxes.push_back( cellBoundingBox( subCellIdx ) ); + } + m_cellSearchTree = new cvf::BoundingBoxTree; m_cellSearchTree->buildTreeFromBoundingBoxesOptimized( cellBoundingBoxes, cellIndicesForBoundingBoxes ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::BoundingBox RigMainGrid::cellBoundingBox( size_t reservoirCellIndex ) const +{ + cvf::BoundingBox cellBB; + for ( size_t nodeIndex : cell( reservoirCellIndex ).cornerIndices() ) + { + cellBB.add( m_nodes[nodeIndex] ); + } + return cellBB; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ReservoirDataModel/RigMainGrid.h b/ApplicationLibCode/ReservoirDataModel/RigMainGrid.h index 3872ccb570..429423afe7 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigMainGrid.h +++ b/ApplicationLibCode/ReservoirDataModel/RigMainGrid.h @@ -154,9 +154,9 @@ private: static std::array defaultMapAxes(); - void doBuildCellSearchTree( std::string* aabbTreeInfo = nullptr ) const; - void buildCellSearchTree() const; - void buildCellSearchTreeOptimized( size_t cellsPerBoundingBox ) const; + void doBuildCellSearchTree( std::string* aabbTreeInfo = nullptr ) const; + void buildCellSearchTree( size_t cellsPerBoundingBox ) const; + cvf::BoundingBox cellBoundingBox( size_t reservoirCellIndex ) const; private: std::vector m_nodes; ///< Global vertex table @@ -185,5 +185,6 @@ private: mutable bool m_isFaceNormalsOutwards; mutable bool m_isFaceNormalsOutwardsComputed; mutable cvf::ref m_cellSearchTree; + mutable size_t m_cellSearchTreeCellsPerBoundingBox = 1; mutable std::mutex m_cellSearchTreeMutex; }; diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt index 646c75f38a..8da8372a89 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists.txt +++ b/ApplicationLibCode/UnitTests/CMakeLists.txt @@ -31,6 +31,7 @@ set(SOURCE_UNITTEST_FILES ${CMAKE_CURRENT_LIST_DIR}/RigSgasResultCalculator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigPorvSoilSgasResultCalculator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigReservoir-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RigMainGridSearchTree-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigResdataGridConverter-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigGridExportAdapter-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigNonUniformRefinement-Test.cpp diff --git a/ApplicationLibCode/UnitTests/RigMainGridSearchTree-Test.cpp b/ApplicationLibCode/UnitTests/RigMainGridSearchTree-Test.cpp new file mode 100644 index 0000000000..848f6ef42c --- /dev/null +++ b/ApplicationLibCode/UnitTests/RigMainGridSearchTree-Test.cpp @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "gtest/gtest.h" + +#include "RiaTestDataDirectory.h" + +#include "RifReaderOpmCommon.h" + +#include "RigEclipseCaseData.h" +#include "RigMainGrid.h" + +#include + +#include + +//-------------------------------------------------------------------------------------------------- +/// The search tree stores several cells in each leaf. Verify that the result is the same as when testing the bounding +/// box of every valid cell, including LGR cells. +//-------------------------------------------------------------------------------------------------- +TEST( RigMainGridSearchTree, FindIntersectingCellsMatchesAllCellsTest ) +{ + QDir baseFolder( TEST_MODEL_DIR ); + QString filename = baseFolder.absoluteFilePath( "TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID" ); + ASSERT_TRUE( QFile::exists( filename ) ); + + cvf::ref caseData = new RigEclipseCaseData( nullptr ); + cvf::ref reader = new RifReaderOpmCommon; + ASSERT_TRUE( reader->open( filename, caseData.p() ) ); + + RigMainGrid* mainGrid = caseData->mainGrid(); + mainGrid->computeCachedData(); + ASSERT_GT( mainGrid->gridCount(), 1u ); + + std::vector cellBoundingBoxes( mainGrid->totalCellCount() ); + for ( size_t cellIdx = 0; cellIdx < mainGrid->totalCellCount(); cellIdx++ ) + { + if ( mainGrid->cell( cellIdx ).isInvalid() ) continue; + + for ( size_t nodeIdx : mainGrid->cell( cellIdx ).cornerIndices() ) + { + cellBoundingBoxes[cellIdx].add( mainGrid->nodes()[nodeIdx] ); + } + } + + const std::vector halfSizes = { 0.1, 20.0, 200.0 }; + const size_t queryCountPerGrid = 20; + + // Query around cells in the main grid and in each LGR + size_t queryCount = 0; + for ( size_t gridIdx = 0; gridIdx < mainGrid->gridCount(); gridIdx++ ) + { + const RigGridBase* grid = mainGrid->gridByIndex( gridIdx ); + const size_t stride = std::max( size_t( 1 ), grid->cellCount() / queryCountPerGrid ); + + for ( size_t localIdx = 0; localIdx < grid->cellCount(); localIdx += stride ) + { + const size_t cellIdx = grid->reservoirCellIndex( localIdx ); + if ( mainGrid->cell( cellIdx ).isInvalid() ) continue; + + const cvf::Vec3d center = mainGrid->cell( cellIdx ).center(); + const double halfSize = halfSizes[queryCount++ % halfSizes.size()]; + + cvf::BoundingBox queryBB( center - cvf::Vec3d( halfSize, halfSize, halfSize ), center + cvf::Vec3d( halfSize, halfSize, halfSize ) ); + + std::vector expected; + for ( size_t i = 0; i < cellBoundingBoxes.size(); i++ ) + { + if ( cellBoundingBoxes[i].isValid() && cellBoundingBoxes[i].intersects( queryBB ) ) expected.push_back( i ); + } + + std::vector found = mainGrid->findIntersectingCells( queryBB ); + std::sort( found.begin(), found.end() ); + + EXPECT_EQ( expected, found ) << "Query around cell " << cellIdx << " in grid " << gridIdx; + } + } + + EXPECT_GT( queryCount, 2 * queryCountPerGrid ); +} diff --git a/Fwk/VizFwk/LibGeometry/cvfBoundingBoxTree.cpp b/Fwk/VizFwk/LibGeometry/cvfBoundingBoxTree.cpp index c1fe30472d..1ff186ed72 100644 --- a/Fwk/VizFwk/LibGeometry/cvfBoundingBoxTree.cpp +++ b/Fwk/VizFwk/LibGeometry/cvfBoundingBoxTree.cpp @@ -134,11 +134,11 @@ namespace cvf { public: AABBTreeNodeLeaf(); - std::vector ids() const; - void setIds(const std::vector& ids); - + size_t index() const; + void setIndex(size_t index); + private: - std::vector m_ids; ///< An list of IDs of the leaf node. The interpretation of these values depends on which tree the node is in. + size_t m_index; ///< Index of the leaf in the input data. The interpretation of this value depends on which tree the node is in. }; //================================================================================================================================= @@ -186,7 +186,7 @@ namespace cvf { bool intersect(const AABBTreeNode* pA, const AABBTreeNode* pB) const; AABBTreeNodeInternal* createNode(); - AABBTreeNodeLeaf* createOrAssignLeaf(size_t leafIndex, const std::vector& bbIds); + AABBTreeNodeLeaf* createOrAssignLeaf(size_t leafIndex); private: static void deleteInternalNodesBottomUp(AABBTreeNode* node); @@ -220,7 +220,7 @@ namespace cvf { { BoundingBoxTreeImpl() {} - void buildTree(const std::vector& boundingBoxes, const std::vector>& ids); + void buildTree(std::vector&& boundingBoxes, std::vector&& ids, std::vector&& idOffsets); private: friend class BoundingBoxTree; @@ -229,9 +229,15 @@ namespace cvf { void findIntersections(const cvf::BoundingBox& bb, std::vector& bbIds) const; void findIntersections(const cvf::BoundingBox& bb, const AABBTreeNode* node, std::vector& ids) const; + void appendLeafIds(const AABBTreeNodeLeaf* leaf, std::vector& ids) const; std::vector m_validBoundingBoxes; - std::vector> m_validOptionalBoundingBoxIds; + + // The IDs are stored in flat arrays instead of in each leaf, to avoid one allocation per leaf. + // If m_idOffsets is empty, leaf i has the single ID m_ids[i], or the ID i if m_ids is also empty. + // Otherwise, the IDs of leaf i are m_ids[m_idOffsets[i]] to m_ids[m_idOffsets[i + 1] - 1]. + std::vector m_ids; + std::vector m_idOffsets; }; } @@ -413,24 +419,25 @@ void AABBTreeNodeInternal::setRight(AABBTreeNode* right) //-------------------------------------------------------------------------------------------------- AABBTreeNodeLeaf::AABBTreeNodeLeaf() { - m_type = AB_LEAF; + m_type = AB_LEAF; + m_index = 0; } //-------------------------------------------------------------------------------------------------- -/// +/// //-------------------------------------------------------------------------------------------------- -std::vector AABBTreeNodeLeaf::ids() const +size_t AABBTreeNodeLeaf::index() const { - return m_ids; + return m_index; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void AABBTreeNodeLeaf::setIds(const std::vector& ids) +void AABBTreeNodeLeaf::setIndex(size_t index) { - m_ids = ids; + m_index = index; } //-------------------------------------------------------------------------------------------------- @@ -779,10 +786,10 @@ cvf::AABBTreeNodeInternal* AABBTree::createNode() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -cvf::AABBTreeNodeLeaf* AABBTree::createOrAssignLeaf(size_t leafIndex, const std::vector& bbIds) +cvf::AABBTreeNodeLeaf* AABBTree::createOrAssignLeaf(size_t leafIndex) { cvf::AABBTreeNodeLeaf* leaf = &m_leafPool[leafIndex]; - leaf->setIds(bbIds); + leaf->setIndex(leafIndex); return leaf; } @@ -821,17 +828,17 @@ void AABBTree::deleteInternalNodesBottomUp(AABBTreeNode* node) //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void BoundingBoxTreeImpl::buildTree(const std::vector& boundingBoxes, const std::vector>& ids) +void BoundingBoxTreeImpl::buildTree(std::vector&& boundingBoxes, std::vector&& ids, std::vector&& idOffsets) { // Assign data used in the tree construction - m_validBoundingBoxes = boundingBoxes; - m_validOptionalBoundingBoxIds = ids; - + m_validBoundingBoxes = std::move(boundingBoxes); + m_ids = std::move(ids); + m_idOffsets = std::move(idOffsets); + AABBTree::buildTree(); - // Release the memory used by the bounding boxes and ids, as this information is now distributed in the tree - m_validBoundingBoxes.clear(); - m_validOptionalBoundingBoxIds.clear(); + // Release the memory used by the bounding boxes, as this information is now distributed in the tree + std::vector().swap(m_validBoundingBoxes); } //-------------------------------------------------------------------------------------------------- @@ -850,10 +857,7 @@ cvf::BoundingBox BoundingBoxTreeImpl::createLeaves() #pragma omp for for (int i = 0; i < (int)m_validBoundingBoxes.size(); i++) { - std::vector bbIds = {i}; - if (!m_validOptionalBoundingBoxIds.empty()) bbIds = m_validOptionalBoundingBoxIds[i]; - - AABBTreeNodeLeaf* leaf = createOrAssignLeaf(i, bbIds); + AABBTreeNodeLeaf* leaf = createOrAssignLeaf(i); leaf->setBoundingBox(m_validBoundingBoxes[i]); @@ -893,13 +897,8 @@ void BoundingBoxTreeImpl::findIntersections(const cvf::BoundingBox& bb, const AA { if (node->type() == AB_LEAF) { - const AABBTreeNodeLeaf* leaf = static_cast(node); - { - auto leafIds = leaf->ids(); - - ids.insert(ids.end(), leafIds.begin(), leafIds.end()); - return; - } + appendLeafIds(static_cast(node), ids); + return; } else if (node->type() == AB_INTERNAL) { @@ -912,7 +911,28 @@ void BoundingBoxTreeImpl::findIntersections(const cvf::BoundingBox& bb, const AA } //-------------------------------------------------------------------------------------------------- -/// +/// +//-------------------------------------------------------------------------------------------------- +void BoundingBoxTreeImpl::appendLeafIds(const AABBTreeNodeLeaf* leaf, std::vector& ids) const +{ + const size_t leafIndex = leaf->index(); + + if (!m_idOffsets.empty()) + { + ids.insert(ids.end(), m_ids.begin() + m_idOffsets[leafIndex], m_ids.begin() + m_idOffsets[leafIndex + 1]); + } + else if (!m_ids.empty()) + { + ids.push_back(static_cast(m_ids[leafIndex])); + } + else + { + ids.push_back(leafIndex); + } +} + +//-------------------------------------------------------------------------------------------------- +/// //-------------------------------------------------------------------------------------------------- BoundingBoxTree::BoundingBoxTree() { @@ -938,11 +958,11 @@ void BoundingBoxTree::buildTreeFromBoundingBoxes(const std::vectorsize()); std::vector validBoundingBoxes; - std::vector> validOptionalBoundingBoxIds; + std::vector validBoundingBoxIds; validBoundingBoxes.reserve(boundingBoxes.size()); if (optionalBoundingBoxIds) - validOptionalBoundingBoxIds.reserve(optionalBoundingBoxIds->size()); + validBoundingBoxIds.reserve(optionalBoundingBoxIds->size()); for (size_t i = 0; i < boundingBoxes.size(); ++i) { @@ -951,15 +971,12 @@ void BoundingBoxTree::buildTreeFromBoundingBoxes(const std::vector ids = {static_cast(id)}; - validOptionalBoundingBoxIds.push_back(ids); + validBoundingBoxIds.push_back(static_cast((*optionalBoundingBoxIds)[i])); } } } - - m_implTree->buildTree(validBoundingBoxes, validOptionalBoundingBoxIds); + + m_implTree->buildTree(std::move(validBoundingBoxes), std::move(validBoundingBoxIds), {}); } //-------------------------------------------------------------------------------------------------- @@ -969,18 +986,24 @@ void BoundingBoxTree::buildTreeFromBoundingBoxesOptimized(const std::vector>& optionalBoundingBoxIds) { std::vector validBoundingBoxes; - std::vector> validOptionalBoundingBoxIds; - + std::vector validBoundingBoxIds; + std::vector validBoundingBoxIdOffsets; + + validBoundingBoxes.reserve(boundingBoxes.size()); + validBoundingBoxIdOffsets.reserve(boundingBoxes.size() + 1); + validBoundingBoxIdOffsets.push_back(0); + for (int i = 0; i < (int)boundingBoxes.size(); ++i) { if (boundingBoxes[i].isValid()) { validBoundingBoxes.push_back(boundingBoxes[i]); - validOptionalBoundingBoxIds.push_back(optionalBoundingBoxIds[i]); + validBoundingBoxIds.insert(validBoundingBoxIds.end(), optionalBoundingBoxIds[i].begin(), optionalBoundingBoxIds[i].end()); + validBoundingBoxIdOffsets.push_back(validBoundingBoxIds.size()); } } - - m_implTree->buildTree(validBoundingBoxes, validOptionalBoundingBoxIds); + + m_implTree->buildTree(std::move(validBoundingBoxes), std::move(validBoundingBoxIds), std::move(validBoundingBoxIdOffsets)); } //--------------------------------------------------------------------------------------------------