From bb9caf138b4c5e9a5690992e750e88b0a551293b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 17:42:18 +1100 Subject: [PATCH 01/16] Doc improvements. --- .../QuantizedMeshLoader.h | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h index 5ffe02940..2574243e3 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h @@ -70,10 +70,10 @@ class CESIUM3DTILESCONTENT_API QuantizedMeshLoader final { /** * @brief Create a {@link QuantizedMeshLoadResult} from the given data. * - * @param tileID The tile ID - * @param tileBoundingVoume The tile bounding volume - * @param url The URL - * @param data The actual input data + * @param tileID The tile ID. + * @param tileBoundingVoume The tile bounding volume. + * @param url The URL from which the data was loaded. + * @param data The actual tile data. * @return The {@link QuantizedMeshLoadResult} */ static QuantizedMeshLoadResult load( @@ -83,12 +83,32 @@ class CESIUM3DTILESCONTENT_API QuantizedMeshLoader final { const gsl::span& data, bool enableWaterMask); + /** + * @brief Parses the metadata (tile availability) from the given + * quantized-mesh terrain tile data. + * + * @param data The actual tile data. + * @param tileID The tile ID. + * @return The parsed metadata. + */ static QuantizedMeshMetadataResult loadMetadata( const gsl::span& data, const CesiumGeometry::QuadtreeTileID& tileID); + /** + * @brief Extracts tile availability information from a parsed layer.json + * or tile metadata extension. + * + * The actual availability information will be found in a property called + * `available`. + * + * @param layerJson The RapidJSON document containing the layer.json. + * @param startingLevel The first tile level number to which the availability + * information applies. + * @return The availability. + */ static QuantizedMeshMetadataResult loadAvailabilityRectangles( - const rapidjson::Document& metadata, + const rapidjson::Document& layerJson, uint32_t startingLevel); }; From 22b447e9acd0eac10fa3c078ae7725f51f0ee453 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 18:36:58 +1100 Subject: [PATCH 02/16] Add ImplicitTiling class. --- .../Cesium3DTilesContent/ImplicitTiling.h | 48 ++++++++++++++++ Cesium3DTilesContent/src/ImplicitTiling.cpp | 57 +++++++++++++++++++ .../src/ImplicitOctreeLoader.cpp | 39 +++---------- .../src/ImplicitOctreeLoader.h | 5 -- .../src/ImplicitQuadtreeLoader.cpp | 36 +++--------- .../src/ImplicitQuadtreeLoader.h | 5 -- 6 files changed, 123 insertions(+), 67 deletions(-) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h create mode 100644 Cesium3DTilesContent/src/ImplicitTiling.cpp diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h new file mode 100644 index 000000000..6591844a7 --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace CesiumGeometry { +struct QuadtreeTileID; +struct OctreeTileID; +} // namespace CesiumGeometry + +namespace Cesium3DTilesContent { + +/** + * @brief Helper functions for working with 3D Tiles implicit tiling. + */ +class ImplicitTiling { +public: + /** + * @brief Resolves a templatized implicit tiling URL with a quadtree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param quadtreeID The quadtree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::QuadtreeTileID& quadtreeID); + + /** + * @brief Resolves a templatized implicit tiling URL with an octree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param octreeID The octree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::OctreeTileID& octreeID); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTiling.cpp b/Cesium3DTilesContent/src/ImplicitTiling.cpp new file mode 100644 index 000000000..12db8f42a --- /dev/null +++ b/Cesium3DTilesContent/src/ImplicitTiling.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +namespace Cesium3DTilesContent { + +/*static*/ std::string ImplicitTiling::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::QuadtreeTileID& quadtreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&quadtreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(quadtreeID.level); + } + if (placeholder == "x") { + return std::to_string(quadtreeID.x); + } + if (placeholder == "y") { + return std::to_string(quadtreeID.y); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +/*static*/ std::string ImplicitTiling::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::OctreeTileID& octreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&octreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(octreeID.level); + } + if (placeholder == "x") { + return std::to_string(octreeID.x); + } + if (placeholder == "y") { + return std::to_string(octreeID.y); + } + if (placeholder == "z") { + return std::to_string(octreeID.z); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 10e3c3080..34cbf13ef 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -3,6 +3,7 @@ #include "logTileLoadResult.h" #include +#include #include #include #include @@ -274,8 +275,10 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = - resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); + std::string subtreeUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_subtreeUrlTemplate, + subtreeID); return SubtreeAvailability::loadSubtree( 3, asyncSystem, @@ -311,8 +314,10 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = - resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pOctreeID); + std::string tileUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_contentUrlTemplate, + *pOctreeID); return requestTileContent( pLogger, asyncSystem, @@ -389,30 +394,4 @@ void ImplicitOctreeLoader::addSubtreeAvailability( subtreeMortonID, std::move(subtreeAvailability)); } - -std::string ImplicitOctreeLoader::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&octreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(octreeID.level); - } - if (placeholder == "x") { - return std::to_string(octreeID.x); - } - if (placeholder == "y") { - return std::to_string(octreeID.y); - } - if (placeholder == "z") { - return std::to_string(octreeID.z); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h index bb6a88bc0..c49c59f95 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h @@ -53,11 +53,6 @@ class ImplicitOctreeLoader : public TilesetContentLoader { Cesium3DTilesContent::SubtreeAvailability&& subtreeAvailability); private: - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID); - std::string _baseUrl; std::string _contentUrlTemplate; std::string _subtreeUrlTemplate; diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 557596e00..db6365769 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -3,6 +3,7 @@ #include "logTileLoadResult.h" #include +#include #include #include #include @@ -297,8 +298,10 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = - resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); + std::string subtreeUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_subtreeUrlTemplate, + subtreeID); return SubtreeAvailability::loadSubtree( 2, asyncSystem, @@ -334,8 +337,10 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = - resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pQuadtreeID); + std::string tileUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_contentUrlTemplate, + *pQuadtreeID); return requestTileContent( pLogger, asyncSystem, @@ -410,27 +415,4 @@ void ImplicitQuadtreeLoader::addSubtreeAvailability( subtreeMortonID, std::move(subtreeAvailability)); } - -std::string ImplicitQuadtreeLoader::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&quadtreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(quadtreeID.level); - } - if (placeholder == "x") { - return std::to_string(quadtreeID.x); - } - if (placeholder == "y") { - return std::to_string(quadtreeID.y); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h index 60c962042..80c3dd36a 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h @@ -55,11 +55,6 @@ class ImplicitQuadtreeLoader : public TilesetContentLoader { Cesium3DTilesContent::SubtreeAvailability&& subtreeAvailability); private: - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID); - std::string _baseUrl; std::string _contentUrlTemplate; std::string _subtreeUrlTemplate; From 48c37e84bdf550d445820e51e9ff1ee416c9b60b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 22:54:15 +1100 Subject: [PATCH 03/16] More implicit tiling support. --- Cesium3DTilesContent/CMakeLists.txt | 2 + .../Cesium3DTilesContent/ImplicitTiling.h | 48 ---- .../ImplicitTilingUtilities.h | 160 ++++++++++++ .../SubtreeAvailability.h | 23 ++ Cesium3DTilesContent/src/ImplicitTiling.cpp | 57 ----- .../src/ImplicitTilingUtilities.cpp | 236 ++++++++++++++++++ .../src/SubtreeAvailability.cpp | 47 ++++ .../test/TestImplicitTilingUtilities.cpp | 104 ++++++++ .../src/ImplicitOctreeLoader.cpp | 23 +- .../src/ImplicitQuadtreeLoader.cpp | 22 +- 10 files changed, 580 insertions(+), 142 deletions(-) delete mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h delete mode 100644 Cesium3DTilesContent/src/ImplicitTiling.cpp create mode 100644 Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp create mode 100644 Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 649b0c327..854e36c96 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -60,6 +60,8 @@ target_link_libraries(Cesium3DTilesContent CesiumGltf CesiumGltfReader CesiumUtility + PRIVATE + libmorton ) install(TARGETS Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h deleted file mode 100644 index 6591844a7..000000000 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -namespace CesiumGeometry { -struct QuadtreeTileID; -struct OctreeTileID; -} // namespace CesiumGeometry - -namespace Cesium3DTilesContent { - -/** - * @brief Helper functions for working with 3D Tiles implicit tiling. - */ -class ImplicitTiling { -public: - /** - * @brief Resolves a templatized implicit tiling URL with a quadtree tile ID. - * - * @param baseUrl The base URL that is used to resolve the urlTemplate if it - * is a relative path. - * @param urlTemplate The templatized URL. - * @param quadtreeID The quadtree ID to use in resolving the parameters in the - * URL template. - * @return The resolved URL. - */ - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID); - - /** - * @brief Resolves a templatized implicit tiling URL with an octree tile ID. - * - * @param baseUrl The base URL that is used to resolve the urlTemplate if it - * is a relative path. - * @param urlTemplate The templatized URL. - * @param octreeID The octree ID to use in resolving the parameters in the - * URL template. - * @return The resolved URL. - */ - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID); -}; - -} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h new file mode 100644 index 000000000..808241dc1 --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include + +#include +#include + +namespace CesiumGeometry { +struct QuadtreeTileID; +struct OctreeTileID; +} // namespace CesiumGeometry + +namespace Cesium3DTilesContent { + +class QuadtreeChildIterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::QuadtreeTileID; + using pointer = CesiumGeometry::QuadtreeTileID*; + using reference = CesiumGeometry::QuadtreeTileID&; + + explicit QuadtreeChildIterator( + const CesiumGeometry::QuadtreeTileID& parentTileID, + bool isFirst) noexcept; + + const CesiumGeometry::QuadtreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::QuadtreeTileID* operator->() const { + return &this->_current; + } + QuadtreeChildIterator& operator++(); + QuadtreeChildIterator operator++(int); + + bool operator==(const QuadtreeChildIterator& rhs) const noexcept; + bool operator!=(const QuadtreeChildIterator& rhs) const noexcept; + +private: + CesiumGeometry::QuadtreeTileID _current; +}; + +class OctreeChildIterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::OctreeTileID; + using pointer = CesiumGeometry::OctreeTileID*; + using reference = CesiumGeometry::OctreeTileID&; + + explicit OctreeChildIterator( + const CesiumGeometry::OctreeTileID& parentTileID, + bool isFirst) noexcept; + + const CesiumGeometry::OctreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::OctreeTileID* operator->() const { + return &this->_current; + } + OctreeChildIterator& operator++(); + OctreeChildIterator operator++(int); + + bool operator==(const OctreeChildIterator& rhs) const noexcept; + bool operator!=(const OctreeChildIterator& rhs) const noexcept; + +private: + CesiumGeometry::OctreeTileID _current; +}; + +/** + * @brief Helper functions for working with 3D Tiles implicit tiling. + */ +class ImplicitTilingUtilities { +public: + /** + * @brief Resolves a templatized implicit tiling URL with a quadtree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param quadtreeID The quadtree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::QuadtreeTileID& quadtreeID); + + /** + * @brief Resolves a templatized implicit tiling URL with an octree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param octreeID The octree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::OctreeTileID& octreeID); + + /** + * @brief Computes the relative Morton index for a given quadtree tile within + * a subtree with the given quadtree ID. + * + * @param subtreeID The ID of the subtree the contains the tile. + * @param tileID The ID of the tile. + * @return The relative Morton index. + */ + static uint64_t computeRelativeMortonIndex( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID); + + /** + * @brief Computes the relative Morton index for a given octree tile within + * a subtree with the given octree ID. + * + * @param subtreeID The ID of the subtree the contains the tile. + * @param tileID The ID of the tile. + * @return The relative Morton index. + */ + static uint64_t computeRelativeMortonIndex( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID); + + static QuadtreeChildIterator + childrenBegin(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + static QuadtreeChildIterator + childrenEnd(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + static OctreeChildIterator + childrenBegin(const CesiumGeometry::OctreeTileID& tileID) noexcept; + static OctreeChildIterator + childrenEnd(const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Gets the quadtree tile IDs of the four children of a given quadtree + * tile. + * + * @param parentTileID The ID of the parent tile. + * @return The IDs of the four children. + */ + static std::array + getChildTileIDs(const CesiumGeometry::QuadtreeTileID& parentTileID); + + /** + * @brief Gets the octree tile IDs of the eight children of a given octree + * tile. + * + * @param parentTileID The ID of the parent tile. + * @return The IDs of the four children. + */ + static std::array + getChildTileIDs(const CesiumGeometry::OctreeTileID& parentTileID); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 29812234a..334b71d6b 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -5,6 +5,11 @@ #include +namespace CesiumGeometry { +struct QuadtreeTileID; +struct OctreeTileID; +} // namespace CesiumGeometry + namespace Cesium3DTilesContent { struct SubtreeConstantAvailability { bool constant; @@ -26,10 +31,28 @@ class SubtreeAvailability { std::vector&& contentAvailability, std::vector>&& buffers); + bool isTileAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID) const noexcept; + + bool isTileAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID) const noexcept; + bool isTileAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId) const noexcept; + bool isContentAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + uint64_t contentId) const noexcept; + + bool isContentAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + uint64_t contentId) const noexcept; + bool isContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, diff --git a/Cesium3DTilesContent/src/ImplicitTiling.cpp b/Cesium3DTilesContent/src/ImplicitTiling.cpp deleted file mode 100644 index 12db8f42a..000000000 --- a/Cesium3DTilesContent/src/ImplicitTiling.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include - -namespace Cesium3DTilesContent { - -/*static*/ std::string ImplicitTiling::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&quadtreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(quadtreeID.level); - } - if (placeholder == "x") { - return std::to_string(quadtreeID.x); - } - if (placeholder == "y") { - return std::to_string(quadtreeID.y); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} - -/*static*/ std::string ImplicitTiling::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&octreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(octreeID.level); - } - if (placeholder == "x") { - return std::to_string(octreeID.x); - } - if (placeholder == "y") { - return std::to_string(octreeID.y); - } - if (placeholder == "z") { - return std::to_string(octreeID.z); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} - -} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp new file mode 100644 index 000000000..e8fa46825 --- /dev/null +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -0,0 +1,236 @@ +#include +#include +#include +#include + +#include + +using namespace CesiumGeometry; + +namespace Cesium3DTilesContent { + +/*static*/ std::string ImplicitTilingUtilities::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const QuadtreeTileID& quadtreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&quadtreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(quadtreeID.level); + } + if (placeholder == "x") { + return std::to_string(quadtreeID.x); + } + if (placeholder == "y") { + return std::to_string(quadtreeID.y); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +/*static*/ std::string ImplicitTilingUtilities::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const OctreeTileID& octreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&octreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(octreeID.level); + } + if (placeholder == "x") { + return std::to_string(octreeID.x); + } + if (placeholder == "y") { + return std::to_string(octreeID.y); + } + if (placeholder == "z") { + return std::to_string(octreeID.z); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( + const QuadtreeTileID& subtreeID, + const QuadtreeTileID& tileID) { + uint32_t relativeTileLevel = tileID.level - subtreeID.level; + return libmorton::morton2D_64_encode( + tileID.x - (subtreeID.x << relativeTileLevel), + tileID.y - (subtreeID.y << relativeTileLevel)); +} + +/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( + const OctreeTileID& subtreeID, + const OctreeTileID& tileID) { + uint32_t relativeTileLevel = tileID.level - subtreeID.level; + return libmorton::morton3D_64_encode( + tileID.x - (subtreeID.x << relativeTileLevel), + tileID.y - (subtreeID.y << relativeTileLevel), + tileID.z - (subtreeID.z << relativeTileLevel)); +} + +QuadtreeChildIterator::QuadtreeChildIterator( + const CesiumGeometry::QuadtreeTileID& parentTileID, + bool isEnd) noexcept + : _current( + parentTileID.level + 1, + parentTileID.x << 1, + parentTileID.y << 1) { + if (isEnd) { + this->_current.y += 2; + } +} + +QuadtreeChildIterator& QuadtreeChildIterator::operator++() { + // Put an indication of the child in the two low bits of `value`. + // Bit 0 indicates left child (0) or right child (1). + // Bit 1 indicates front child (0) or back child (1). + uint32_t value = ((this->_current.y & 1) << 1) | (this->_current.x & 1); + + // Add one to the current value to get the value for the next child. + // Bit cascade from addition gives us exactly what we need. + ++value; + + // Set the tile coordinates based on the new value. + // Bit 0 in value replaces bit 0 of the X coordinate. + this->_current.x = (this->_current.x & ~1) | (value & 1); + + // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past + // the last child). So we just clear the low bit of the current Y coordinate + // and add this shifted value to produce the new Y coordinate. + this->_current.y = (this->_current.y & ~1) + (value >> 1); + + return *this; +} + +QuadtreeChildIterator QuadtreeChildIterator::operator++(int) { + QuadtreeChildIterator copy = *this; + ++copy; + return copy; +} + +bool QuadtreeChildIterator::operator==( + const QuadtreeChildIterator& rhs) const noexcept { + return this->_current == rhs._current; +} + +bool QuadtreeChildIterator::operator!=( + const QuadtreeChildIterator& rhs) const noexcept { + return this->_current != rhs._current; +} + +OctreeChildIterator::OctreeChildIterator( + const CesiumGeometry::OctreeTileID& parentTileID, + bool isEnd) noexcept + : _current( + parentTileID.level + 1, + parentTileID.x << 1, + parentTileID.y << 1, + parentTileID.z << 1) { + if (isEnd) { + this->_current.z += 2; + } +} + +OctreeChildIterator& OctreeChildIterator::operator++() { + // Put an indication of the child in the three low bits of `value`. + // Bit 0 indicates left child (0) or right child (1). + // Bit 1 indicates front child (0) or back child (1). + // Bit 2 indicates bottom child (0) or top child (1). + uint32_t value = ((this->_current.z & 1) << 2) | + ((this->_current.y & 1) << 1) | (this->_current.x & 1); + + // Add one to the current value to get the value for the next child. + // Bit cascade from addition gives us exactly what we need. + ++value; + + // Set the tile coordinates based on the new value. + // Bit 0 in value replaces bit 0 of the X coordinate. + // Bit 1 in the value replaces bit 0 of the Y coordinate. + this->_current.x = (this->_current.x & ~1) | (value & 1); + this->_current.y = (this->_current.y & ~1) | ((value >> 1) & 1); + + // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past + // the last child). So we just clear the low bit of the current Y coordinate + // and add this shifted value to produce the new Y coordinate. + this->_current.z = (this->_current.z & ~1) + (value >> 2); + + return *this; +} + +OctreeChildIterator OctreeChildIterator::operator++(int) { + OctreeChildIterator copy = *this; + ++copy; + return copy; +} + +bool OctreeChildIterator::operator==( + const OctreeChildIterator& rhs) const noexcept { + return this->_current == rhs._current; +} + +bool OctreeChildIterator::operator!=( + const OctreeChildIterator& rhs) const noexcept { + return this->_current != rhs._current; +} + +QuadtreeChildIterator ImplicitTilingUtilities::childrenBegin( + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return QuadtreeChildIterator(tileID, false); +} + +QuadtreeChildIterator ImplicitTilingUtilities::childrenEnd( + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return QuadtreeChildIterator(tileID, true); +} + +OctreeChildIterator ImplicitTilingUtilities::childrenBegin( + const CesiumGeometry::OctreeTileID& tileID) noexcept { + return OctreeChildIterator(tileID, false); +} + +OctreeChildIterator ImplicitTilingUtilities::childrenEnd( + const CesiumGeometry::OctreeTileID& tileID) noexcept { + return OctreeChildIterator(tileID, true); +} + +std::array +ImplicitTilingUtilities::getChildTileIDs(const QuadtreeTileID& parentTileID) { + uint32_t level = parentTileID.level + 1; + uint32_t x = parentTileID.x << 1; + uint32_t y = parentTileID.y << 1; + return std::array{ + QuadtreeTileID(level, x, y), + QuadtreeTileID(level, x + 1, y), + QuadtreeTileID(level, x, y + 1), + QuadtreeTileID(level, x + 1, y + 1)}; +} + +std::array +ImplicitTilingUtilities::getChildTileIDs(const OctreeTileID& parentTileID) { + uint32_t level = parentTileID.level + 1; + uint32_t x = parentTileID.x << 1; + uint32_t y = parentTileID.y << 1; + uint32_t z = parentTileID.z << 1; + return std::array{ + OctreeTileID(level, x, y, z), + OctreeTileID(level, x + 1, y, z), + OctreeTileID(level, x, y + 1, z), + OctreeTileID(level, x + 1, y + 1, z), + OctreeTileID(level, x, y, z + 1), + OctreeTileID(level, x + 1, y, z + 1), + OctreeTileID(level, x, y + 1, z + 1), + OctreeTileID(level, x + 1, y + 1, z + 1)}; +} + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 6a89bfb2a..fadbc70aa 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -1,5 +1,8 @@ +#include #include #include +#include +#include #include #include @@ -462,6 +465,26 @@ SubtreeAvailability::SubtreeAvailability( "Only support quadtree and octree"); } +bool SubtreeAvailability::isTileAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isTileAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx); +} + +bool SubtreeAvailability::isTileAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isTileAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx); +} + bool SubtreeAvailability::isTileAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId) const noexcept { @@ -471,6 +494,30 @@ bool SubtreeAvailability::isTileAvailable( this->_tileAvailability); } +bool SubtreeAvailability::isContentAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + uint64_t contentId) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isContentAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + contentId); +} + +bool SubtreeAvailability::isContentAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + uint64_t contentId) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isContentAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + contentId); +} + bool SubtreeAvailability::isContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp new file mode 100644 index 000000000..a25d79a06 --- /dev/null +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -0,0 +1,104 @@ +#include + +#include + +using namespace CesiumGeometry; +using namespace Cesium3DTilesContent; + +TEST_CASE("ImplicitTilingUtilities child tile iteration") { + SECTION("QuadtreeTileID") { + QuadtreeTileID parent(1, 2, 3); + + QuadtreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); + + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 6); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 6); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 7); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 7); + + ++it; + CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + } + + SECTION("OctreeTileID") { + OctreeTileID parent(1, 2, 3, 4); + + OctreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); + + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 6); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 6); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 7); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 7); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 6); + CHECK(it->z == 9); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 6); + CHECK(it->z == 9); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 7); + CHECK(it->z == 9); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 7); + CHECK(it->z == 9); + + ++it; + CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + } +} diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 34cbf13ef..abef3651b 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -3,7 +3,7 @@ #include "logTileLoadResult.h" #include -#include +#include #include #include #include @@ -147,21 +147,6 @@ std::vector populateSubtree( return children; } -bool isTileContentAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& octreeID, - const SubtreeAvailability& subtreeAvailability) { - uint32_t relativeTileLevel = octreeID.level - subtreeID.level; - uint64_t relativeTileMortonIdx = libmorton::morton3D_64_encode( - octreeID.x - (subtreeID.x << relativeTileLevel), - octreeID.y - (subtreeID.y << relativeTileLevel), - octreeID.z - (subtreeID.z << relativeTileLevel)); - return subtreeAvailability.isContentAvailable( - relativeTileLevel, - relativeTileMortonIdx, - 0); -} - CesiumAsync::Future requestTileContent( const std::shared_ptr& pLogger, const CesiumAsync::AsyncSystem& asyncSystem, @@ -275,7 +260,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = ImplicitTiling::resolveUrl( + std::string subtreeUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); @@ -301,7 +286,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { // subtree is available, so check if tile has content or not. If it has, then // request it - if (!isTileContentAvailable(subtreeID, *pOctreeID, subtreeIt->second)) { + if (!subtreeIt->second.isContentAvailable(subtreeID, *pOctreeID, 0)) { // check if tile has empty content return asyncSystem.createResolvedFuture(TileLoadResult{ TileEmptyContent{}, @@ -314,7 +299,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = ImplicitTiling::resolveUrl( + std::string tileUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_contentUrlTemplate, *pOctreeID); diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index db6365769..de99e3f7e 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -3,7 +3,7 @@ #include "logTileLoadResult.h" #include -#include +#include #include #include #include @@ -148,20 +148,6 @@ std::vector populateSubtree( return children; } -bool isTileContentAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& quadtreeID, - const SubtreeAvailability& subtreeAvailability) { - uint32_t relativeTileLevel = quadtreeID.level - subtreeID.level; - uint64_t relativeTileMortonIdx = libmorton::morton2D_64_encode( - quadtreeID.x - (subtreeID.x << relativeTileLevel), - quadtreeID.y - (subtreeID.y << relativeTileLevel)); - return subtreeAvailability.isContentAvailable( - relativeTileLevel, - relativeTileMortonIdx, - 0); -} - CesiumAsync::Future requestTileContent( const std::shared_ptr& pLogger, const CesiumAsync::AsyncSystem& asyncSystem, @@ -298,7 +284,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = ImplicitTiling::resolveUrl( + std::string subtreeUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); @@ -324,7 +310,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { // subtree is available, so check if tile has content or not. If it has, then // request it - if (!isTileContentAvailable(subtreeID, *pQuadtreeID, subtreeIt->second)) { + if (!subtreeIt->second.isContentAvailable(subtreeID, *pQuadtreeID, 0)) { // check if tile has empty content return asyncSystem.createResolvedFuture(TileLoadResult{ TileEmptyContent{}, @@ -337,7 +323,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = ImplicitTiling::resolveUrl( + std::string tileUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_contentUrlTemplate, *pQuadtreeID); From fdf1084887566b724eab67287c3b8f5e27f29025 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 16:49:40 +1100 Subject: [PATCH 04/16] Move quadtree computations into ImplicitTilingUtilities. --- .../ImplicitTilingUtilities.h | 232 +++++++++++++----- .../src/ImplicitTilingUtilities.cpp | 154 ++++++------ .../test/TestImplicitTilingUtilities.cpp | 145 +++++------ .../src/ImplicitQuadtreeLoader.cpp | 126 +++++----- 4 files changed, 376 insertions(+), 281 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 808241dc1..6bf604f32 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -4,6 +4,7 @@ #include #include +#include #include namespace CesiumGeometry { @@ -13,58 +14,96 @@ struct OctreeTileID; namespace Cesium3DTilesContent { -class QuadtreeChildIterator { +/** + * @brief A lightweight virtual container enumerating the quadtree IDs of the + * children of a given quadtree tile. + */ +class QuadtreeChildren { public: - using iterator_category = std::forward_iterator_tag; - using value_type = CesiumGeometry::QuadtreeTileID; - using pointer = CesiumGeometry::QuadtreeTileID*; - using reference = CesiumGeometry::QuadtreeTileID&; + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::QuadtreeTileID; + using difference_type = void; + using pointer = CesiumGeometry::QuadtreeTileID*; + using reference = CesiumGeometry::QuadtreeTileID&; - explicit QuadtreeChildIterator( - const CesiumGeometry::QuadtreeTileID& parentTileID, - bool isFirst) noexcept; + explicit iterator( + const CesiumGeometry::QuadtreeTileID& parentTileID, + bool isFirst) noexcept; - const CesiumGeometry::QuadtreeTileID& operator*() const { - return this->_current; - } - const CesiumGeometry::QuadtreeTileID* operator->() const { - return &this->_current; - } - QuadtreeChildIterator& operator++(); - QuadtreeChildIterator operator++(int); + const CesiumGeometry::QuadtreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::QuadtreeTileID* operator->() const { + return &this->_current; + } + iterator& operator++(); + iterator operator++(int); + + bool operator==(const iterator& rhs) const noexcept; + bool operator!=(const iterator& rhs) const noexcept; + + private: + CesiumGeometry::QuadtreeTileID _current; + }; - bool operator==(const QuadtreeChildIterator& rhs) const noexcept; - bool operator!=(const QuadtreeChildIterator& rhs) const noexcept; + using const_iterator = iterator; + + QuadtreeChildren(const CesiumGeometry::QuadtreeTileID& tileID) noexcept + : _tileID(tileID) {} + iterator begin() const noexcept; + iterator end() const noexcept; + constexpr int64_t size() const noexcept { return 4; } private: - CesiumGeometry::QuadtreeTileID _current; + CesiumGeometry::QuadtreeTileID _tileID; }; -class OctreeChildIterator { +/** + * @brief A lightweight virtual container enumerating the octree IDs of the + * children of a given octree tile. + */ +class OctreeChildren { public: - using iterator_category = std::forward_iterator_tag; - using value_type = CesiumGeometry::OctreeTileID; - using pointer = CesiumGeometry::OctreeTileID*; - using reference = CesiumGeometry::OctreeTileID&; + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::OctreeTileID; + using difference_type = void; + using pointer = CesiumGeometry::OctreeTileID*; + using reference = CesiumGeometry::OctreeTileID&; - explicit OctreeChildIterator( - const CesiumGeometry::OctreeTileID& parentTileID, - bool isFirst) noexcept; + explicit iterator( + const CesiumGeometry::OctreeTileID& parentTileID, + bool isFirst) noexcept; - const CesiumGeometry::OctreeTileID& operator*() const { - return this->_current; - } - const CesiumGeometry::OctreeTileID* operator->() const { - return &this->_current; - } - OctreeChildIterator& operator++(); - OctreeChildIterator operator++(int); + const CesiumGeometry::OctreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::OctreeTileID* operator->() const { + return &this->_current; + } + iterator& operator++(); + iterator operator++(int); - bool operator==(const OctreeChildIterator& rhs) const noexcept; - bool operator!=(const OctreeChildIterator& rhs) const noexcept; + bool operator==(const iterator& rhs) const noexcept; + bool operator!=(const iterator& rhs) const noexcept; + + private: + CesiumGeometry::OctreeTileID _current; + }; + + using const_iterator = iterator; + + OctreeChildren(const CesiumGeometry::OctreeTileID& tileID) noexcept + : _tileID(tileID) {} + iterator begin() const noexcept; + iterator end() const noexcept; + constexpr int64_t size() const noexcept { return 8; } private: - CesiumGeometry::OctreeTileID _current; + CesiumGeometry::OctreeTileID _tileID; }; /** @@ -102,9 +141,28 @@ class ImplicitTilingUtilities { const std::string& urlTemplate, const CesiumGeometry::OctreeTileID& octreeID); + /** + * @brief Computes the Morton index for a given quadtree tile within its + * level. + * + * @param tileID The ID of the tile. + * @return The Morton index. + */ + static uint64_t + computeMortonIndex(const CesiumGeometry::QuadtreeTileID& tileID); + + /** + * @brief Computes the Morton index for a given octree tile within its level. + * + * @param tileID The ID of the tile. + * @return The Morton index. + */ + static uint64_t + computeMortonIndex(const CesiumGeometry::OctreeTileID& tileID); + /** * @brief Computes the relative Morton index for a given quadtree tile within - * a subtree with the given quadtree ID. + * its level of a subtree root at the tile with the given quadtree ID. * * @param subtreeID The ID of the subtree the contains the tile. * @param tileID The ID of the tile. @@ -116,45 +174,101 @@ class ImplicitTilingUtilities { /** * @brief Computes the relative Morton index for a given octree tile within - * a subtree with the given octree ID. + * its level of a subtree rooted at the tile with the given octree ID. * * @param subtreeID The ID of the subtree the contains the tile. * @param tileID The ID of the tile. * @return The relative Morton index. */ static uint64_t computeRelativeMortonIndex( - const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& subtreeRootID, const CesiumGeometry::OctreeTileID& tileID); - static QuadtreeChildIterator - childrenBegin(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; - static QuadtreeChildIterator - childrenEnd(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + /** + * @brief Gets the ID of the root tile of the subtree that contains a given + * tile. + * + * @param subtreeLevels The number of levels in each sub-tree. For example, if + * this parameter is 4, then the first subtree starts at level 0 and + * contains tiles in levels 0 through 3, and the next subtree starts at + * level 4 and contains tiles in levels 4 through 7. + * @param tileID The tile ID for each to find the subtree root. + * @return The ID of the root tile of the subtree. + */ + static CesiumGeometry::QuadtreeTileID getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; - static OctreeChildIterator - childrenBegin(const CesiumGeometry::OctreeTileID& tileID) noexcept; - static OctreeChildIterator - childrenEnd(const CesiumGeometry::OctreeTileID& tileID) noexcept; + /** + * @brief Gets the ID of the root tile of the subtree that contains a given + * tile. + * + * @param subtreeLevels The number of levels in each sub-tree. For example, if + * this parameter is 4, then the first subtree starts at level 0 and + * contains tiles in levels 0 through 3, and the next subtree starts at + * level 4 and contains tiles in levels 4 through 7. + * @param tileID The tile ID for each to find the subtree root. + * @return The ID of the root tile of the subtree. + */ + static CesiumGeometry::OctreeTileID getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::OctreeTileID& tileID) noexcept; /** - * @brief Gets the quadtree tile IDs of the four children of a given quadtree + * @brief Converts an absolute tile ID to a tile ID relative to a given root * tile. * - * @param parentTileID The ID of the parent tile. - * @return The IDs of the four children. + * For example, if `rootID` and `tileID` are the same, this method returns + * `QuadtreeTileID(0, 0, 0)`. + * + * @param rootID The ID of the root tile that the returned ID should be + * relative to. + * @param tileID The absolute ID of the tile to compute a relative ID for. + * @return The relative tile ID. */ - static std::array - getChildTileIDs(const CesiumGeometry::QuadtreeTileID& parentTileID); + static CesiumGeometry::QuadtreeTileID absoluteTileIDToRelative( + const CesiumGeometry::QuadtreeTileID& rootID, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; /** - * @brief Gets the octree tile IDs of the eight children of a given octree + * @brief Converts an absolute tile ID to a tile ID relative to a given root * tile. * - * @param parentTileID The ID of the parent tile. - * @return The IDs of the four children. + * For example, if `rootID` and `tileID` are the same, this method returns + * `OctreeTileID(0, 0, 0, 0)`. + * + * @param rootID The ID of the root tile that the returned ID should be + * relative to. + * @param tileID The absolute ID of the tile to compute a relative ID for. + * @return The relative tile ID. + */ + static CesiumGeometry::OctreeTileID absoluteTileIDToRelative( + const CesiumGeometry::OctreeTileID& rootID, + const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Gets a lightweight virtual container for enumerating the quadtree + * IDs of the children of a given quadtree tile. + * + * @param tileID The tile ID of the parent tile for which to get children. + * @return The children. */ - static std::array - getChildTileIDs(const CesiumGeometry::OctreeTileID& parentTileID); + static QuadtreeChildren + getChildren(const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return QuadtreeChildren{tileID}; + } + + /** + * @brief Gets a lightweight virtual container for enumerating the octree + * IDs of the children of a given octree tile. + * + * @param tileID The tile ID of the parent tile for which to get children. + * @return The children. + */ + static OctreeChildren + getChildren(const CesiumGeometry::OctreeTileID& tileID) noexcept { + return OctreeChildren{tileID}; + } }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index e8fa46825..da9c65340 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -9,7 +9,7 @@ using namespace CesiumGeometry; namespace Cesium3DTilesContent { -/*static*/ std::string ImplicitTilingUtilities::resolveUrl( +std::string ImplicitTilingUtilities::resolveUrl( const std::string& baseUrl, const std::string& urlTemplate, const QuadtreeTileID& quadtreeID) { @@ -32,7 +32,7 @@ namespace Cesium3DTilesContent { return CesiumUtility::Uri::resolve(baseUrl, url); } -/*static*/ std::string ImplicitTilingUtilities::resolveUrl( +std::string ImplicitTilingUtilities::resolveUrl( const std::string& baseUrl, const std::string& urlTemplate, const OctreeTileID& octreeID) { @@ -58,26 +58,73 @@ namespace Cesium3DTilesContent { return CesiumUtility::Uri::resolve(baseUrl, url); } -/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( +uint64_t ImplicitTilingUtilities::computeMortonIndex( + const CesiumGeometry::QuadtreeTileID& tileID) { + return libmorton::morton2D_64_encode(tileID.x, tileID.y); +} + +uint64_t ImplicitTilingUtilities::computeMortonIndex( + const CesiumGeometry::OctreeTileID& tileID) { + return libmorton::morton3D_64_encode(tileID.x, tileID.y, tileID.z); +} + +uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( const QuadtreeTileID& subtreeID, const QuadtreeTileID& tileID) { - uint32_t relativeTileLevel = tileID.level - subtreeID.level; - return libmorton::morton2D_64_encode( - tileID.x - (subtreeID.x << relativeTileLevel), - tileID.y - (subtreeID.y << relativeTileLevel)); + return computeMortonIndex(absoluteTileIDToRelative(subtreeID, tileID)); } -/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( +uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( const OctreeTileID& subtreeID, const OctreeTileID& tileID) { - uint32_t relativeTileLevel = tileID.level - subtreeID.level; - return libmorton::morton3D_64_encode( - tileID.x - (subtreeID.x << relativeTileLevel), - tileID.y - (subtreeID.y << relativeTileLevel), - tileID.z - (subtreeID.z << relativeTileLevel)); + return computeMortonIndex(absoluteTileIDToRelative(subtreeID, tileID)); +} + +CesiumGeometry::QuadtreeTileID ImplicitTilingUtilities::getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + uint32_t subtreeLevel = tileID.level / subtreeLevels; + uint32_t levelsLeft = tileID.level % subtreeLevels; + return QuadtreeTileID( + subtreeLevel, + tileID.x >> levelsLeft, + tileID.y >> levelsLeft); } -QuadtreeChildIterator::QuadtreeChildIterator( +CesiumGeometry::OctreeTileID ImplicitTilingUtilities::getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::OctreeTileID& tileID) noexcept { + uint32_t subtreeLevel = tileID.level / subtreeLevels; + uint32_t levelsLeft = tileID.level % subtreeLevels; + return OctreeTileID( + subtreeLevel, + tileID.x >> levelsLeft, + tileID.y >> levelsLeft, + tileID.z >> levelsLeft); +} + +QuadtreeTileID ImplicitTilingUtilities::absoluteTileIDToRelative( + const QuadtreeTileID& rootID, + const QuadtreeTileID& tileID) noexcept { + uint32_t relativeTileLevel = tileID.level - rootID.level; + return QuadtreeTileID( + relativeTileLevel, + tileID.x - (rootID.x << relativeTileLevel), + tileID.y - (rootID.y << relativeTileLevel)); +} + +OctreeTileID ImplicitTilingUtilities::absoluteTileIDToRelative( + const OctreeTileID& rootID, + const OctreeTileID& tileID) noexcept { + uint32_t relativeTileLevel = tileID.level - rootID.level; + return OctreeTileID( + relativeTileLevel, + tileID.x - (rootID.x << relativeTileLevel), + tileID.y - (rootID.y << relativeTileLevel), + tileID.z - (rootID.z << relativeTileLevel)); +} + +QuadtreeChildren::iterator::iterator( const CesiumGeometry::QuadtreeTileID& parentTileID, bool isEnd) noexcept : _current( @@ -89,7 +136,7 @@ QuadtreeChildIterator::QuadtreeChildIterator( } } -QuadtreeChildIterator& QuadtreeChildIterator::operator++() { +QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Put an indication of the child in the two low bits of `value`. // Bit 0 indicates left child (0) or right child (1). // Bit 1 indicates front child (0) or back child (1). @@ -112,23 +159,23 @@ QuadtreeChildIterator& QuadtreeChildIterator::operator++() { return *this; } -QuadtreeChildIterator QuadtreeChildIterator::operator++(int) { - QuadtreeChildIterator copy = *this; +QuadtreeChildren::iterator QuadtreeChildren::iterator::operator++(int) { + iterator copy = *this; ++copy; return copy; } -bool QuadtreeChildIterator::operator==( - const QuadtreeChildIterator& rhs) const noexcept { +bool QuadtreeChildren::iterator::operator==( + const iterator& rhs) const noexcept { return this->_current == rhs._current; } -bool QuadtreeChildIterator::operator!=( - const QuadtreeChildIterator& rhs) const noexcept { +bool QuadtreeChildren::iterator::operator!=( + const iterator& rhs) const noexcept { return this->_current != rhs._current; } -OctreeChildIterator::OctreeChildIterator( +OctreeChildren::iterator::iterator( const CesiumGeometry::OctreeTileID& parentTileID, bool isEnd) noexcept : _current( @@ -141,7 +188,7 @@ OctreeChildIterator::OctreeChildIterator( } } -OctreeChildIterator& OctreeChildIterator::operator++() { +OctreeChildren::iterator& OctreeChildren::iterator::operator++() { // Put an indication of the child in the three low bits of `value`. // Bit 0 indicates left child (0) or right child (1). // Bit 1 indicates front child (0) or back child (1). @@ -168,69 +215,36 @@ OctreeChildIterator& OctreeChildIterator::operator++() { return *this; } -OctreeChildIterator OctreeChildIterator::operator++(int) { - OctreeChildIterator copy = *this; +OctreeChildren::iterator OctreeChildren::iterator::operator++(int) { + iterator copy = *this; ++copy; return copy; } -bool OctreeChildIterator::operator==( - const OctreeChildIterator& rhs) const noexcept { +bool OctreeChildren::iterator::operator==(const iterator& rhs) const noexcept { return this->_current == rhs._current; } -bool OctreeChildIterator::operator!=( - const OctreeChildIterator& rhs) const noexcept { +bool OctreeChildren::iterator::operator!=(const iterator& rhs) const noexcept { return this->_current != rhs._current; } -QuadtreeChildIterator ImplicitTilingUtilities::childrenBegin( - const CesiumGeometry::QuadtreeTileID& tileID) noexcept { - return QuadtreeChildIterator(tileID, false); +QuadtreeChildren::const_iterator +Cesium3DTilesContent::QuadtreeChildren::begin() const noexcept { + return const_iterator(this->_tileID, false); } -QuadtreeChildIterator ImplicitTilingUtilities::childrenEnd( - const CesiumGeometry::QuadtreeTileID& tileID) noexcept { - return QuadtreeChildIterator(tileID, true); +QuadtreeChildren::const_iterator QuadtreeChildren::end() const noexcept { + return const_iterator(this->_tileID, true); } -OctreeChildIterator ImplicitTilingUtilities::childrenBegin( - const CesiumGeometry::OctreeTileID& tileID) noexcept { - return OctreeChildIterator(tileID, false); +OctreeChildren::const_iterator +Cesium3DTilesContent::OctreeChildren::begin() const noexcept { + return const_iterator(this->_tileID, false); } -OctreeChildIterator ImplicitTilingUtilities::childrenEnd( - const CesiumGeometry::OctreeTileID& tileID) noexcept { - return OctreeChildIterator(tileID, true); -} - -std::array -ImplicitTilingUtilities::getChildTileIDs(const QuadtreeTileID& parentTileID) { - uint32_t level = parentTileID.level + 1; - uint32_t x = parentTileID.x << 1; - uint32_t y = parentTileID.y << 1; - return std::array{ - QuadtreeTileID(level, x, y), - QuadtreeTileID(level, x + 1, y), - QuadtreeTileID(level, x, y + 1), - QuadtreeTileID(level, x + 1, y + 1)}; -} - -std::array -ImplicitTilingUtilities::getChildTileIDs(const OctreeTileID& parentTileID) { - uint32_t level = parentTileID.level + 1; - uint32_t x = parentTileID.x << 1; - uint32_t y = parentTileID.y << 1; - uint32_t z = parentTileID.z << 1; - return std::array{ - OctreeTileID(level, x, y, z), - OctreeTileID(level, x + 1, y, z), - OctreeTileID(level, x, y + 1, z), - OctreeTileID(level, x + 1, y + 1, z), - OctreeTileID(level, x, y, z + 1), - OctreeTileID(level, x + 1, y, z + 1), - OctreeTileID(level, x, y + 1, z + 1), - OctreeTileID(level, x + 1, y + 1, z + 1)}; +OctreeChildren::const_iterator OctreeChildren::end() const noexcept { + return const_iterator(this->_tileID, true); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp index a25d79a06..f81926c25 100644 --- a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -2,6 +2,8 @@ #include +#include + using namespace CesiumGeometry; using namespace Cesium3DTilesContent; @@ -9,96 +11,67 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { SECTION("QuadtreeTileID") { QuadtreeTileID parent(1, 2, 3); - QuadtreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); - - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 6); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 6); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 7); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 7); - - ++it; - CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + QuadtreeChildren children = ImplicitTilingUtilities::getChildren(parent); + + // Check we can enumerate the children with a range-based for loop. + int count = 0; + for (const QuadtreeTileID& tileID : children) { + CHECK(tileID.level == 2); + CHECK((tileID.x == 4 || tileID.x == 5)); + CHECK((tileID.y == 6 || tileID.y == 7)); + ++count; + } + + CHECK(count == 4); + + // Check we have exactly the right children. + std::vector expected{ + QuadtreeTileID(2, 4, 6), + QuadtreeTileID(2, 5, 6), + QuadtreeTileID(2, 4, 7), + QuadtreeTileID(2, 5, 7)}; + auto mismatch = std::mismatch( + children.begin(), + children.end(), + expected.begin(), + expected.end()); + CHECK(mismatch.first == children.end()); + CHECK(mismatch.second == expected.end()); } SECTION("OctreeTileID") { OctreeTileID parent(1, 2, 3, 4); - OctreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); - - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 6); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 6); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 7); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 7); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 6); - CHECK(it->z == 9); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 6); - CHECK(it->z == 9); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 7); - CHECK(it->z == 9); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 7); - CHECK(it->z == 9); - - ++it; - CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + OctreeChildren children = ImplicitTilingUtilities::getChildren(parent); + + // Check we can enumerate the children with a range-based for loop. + int count = 0; + for (const OctreeTileID& tileID : children) { + CHECK(tileID.level == 2); + CHECK((tileID.x == 4 || tileID.x == 5)); + CHECK((tileID.y == 6 || tileID.y == 7)); + CHECK((tileID.z == 8 || tileID.z == 9)); + ++count; + } + + CHECK(count == 8); + + // Check we have exactly the right children. + std::vector expected{ + OctreeTileID(2, 4, 6, 8), + OctreeTileID(2, 5, 6, 8), + OctreeTileID(2, 4, 7, 8), + OctreeTileID(2, 5, 7, 8), + OctreeTileID(2, 4, 6, 9), + OctreeTileID(2, 5, 6, 9), + OctreeTileID(2, 4, 7, 9), + OctreeTileID(2, 5, 7, 9)}; + auto mismatch = std::mismatch( + children.begin(), + children.end(), + expected.begin(), + expected.end()); + CHECK(mismatch.first == children.end()); + CHECK(mismatch.second == expected.end()); } } diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index de99e3f7e..a9f95c568 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -84,63 +84,59 @@ BoundingVolume subdivideBoundingVolume( std::vector populateSubtree( const SubtreeAvailability& subtreeAvailability, uint32_t subtreeLevels, - uint32_t relativeTileLevel, - uint64_t relativeTileMortonID, + const CesiumGeometry::QuadtreeTileID& subtreeRootID, const Tile& tile, ImplicitQuadtreeLoader& loader) { + const CesiumGeometry::QuadtreeTileID& quadtreeID = + std::get(tile.getTileID()); + + uint32_t relativeTileLevel = quadtreeID.level - subtreeRootID.level; if (relativeTileLevel >= subtreeLevels) { return {}; } - const CesiumGeometry::QuadtreeTileID& quadtreeID = - std::get(tile.getTileID()); + QuadtreeChildren childIDs = ImplicitTilingUtilities::getChildren(quadtreeID); std::vector children; - children.reserve(4); - for (uint16_t y = 0; y < 2; ++y) { - uint32_t childY = (quadtreeID.y << 1) | y; - for (uint16_t x = 0; x < 2; ++x) { - uint32_t childX = (quadtreeID.x << 1) | x; - CesiumGeometry::QuadtreeTileID childID{ - quadtreeID.level + 1, - childX, - childY}; - - uint32_t childIndex = - static_cast(libmorton::morton2D_32_encode(x, y)); - uint64_t relativeChildMortonID = relativeTileMortonID << 2 | childIndex; - uint32_t relativeChildLevel = relativeTileLevel + 1; - if (relativeChildLevel == subtreeLevels) { - if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { - Tile& child = children.emplace_back(&loader); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); - } - } else { - if (subtreeAvailability.isTileAvailable( + children.reserve(childIDs.size()); + + for (const CesiumGeometry::QuadtreeTileID& childID : childIDs) { + uint64_t relativeChildMortonID = + ImplicitTilingUtilities::computeRelativeMortonIndex( + subtreeRootID, + childID); + + uint32_t relativeChildLevel = relativeTileLevel + 1; + if (relativeChildLevel == subtreeLevels) { + if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { + Tile& child = children.emplace_back(&loader); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); + } + } else { + if (subtreeAvailability.isTileAvailable( + relativeChildLevel, + relativeChildMortonID)) { + if (subtreeAvailability.isContentAvailable( relativeChildLevel, - relativeChildMortonID)) { - if (subtreeAvailability.isContentAvailable( - relativeChildLevel, - relativeChildMortonID, - 0)) { - children.emplace_back(&loader); - } else { - children.emplace_back(&loader, TileEmptyContent{}); - } - - Tile& child = children.back(); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); + relativeChildMortonID, + 0)) { + children.emplace_back(&loader); + } else { + children.emplace_back(&loader, TileEmptyContent{}); } + + Tile& child = children.back(); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); } } } @@ -256,18 +252,16 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { } // find the subtree ID - uint32_t subtreeLevelIdx = pQuadtreeID->level / this->_subtreeLevels; + CesiumGeometry::QuadtreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pQuadtreeID); + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; if (subtreeLevelIdx >= _loadedSubtrees.size()) { return asyncSystem.createResolvedFuture( TileLoadResult::createFailedResult(nullptr)); } - uint64_t levelLeft = pQuadtreeID->level % this->_subtreeLevels; - uint32_t subtreeLevel = this->_subtreeLevels * subtreeLevelIdx; - uint32_t subtreeX = pQuadtreeID->x >> levelLeft; - uint32_t subtreeY = pQuadtreeID->y >> levelLeft; - CesiumGeometry::QuadtreeTileID subtreeID{subtreeLevel, subtreeX, subtreeY}; - // the below morton index hash to the subtree assumes that tileID's components // x and y never exceed 32-bit. In other words, the max levels this loader can // support is 33 which will have 4^32 tiles in the level 32th. The 64-bit @@ -279,7 +273,8 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { // loader will serve up to 33 levels with the level 0 being relative to the // parent loader. The solution isn't implemented at the moment, as implicit // tilesets that exceeds 33 levels are expected to be very rare - uint64_t subtreeMortonIdx = libmorton::morton2D_64_encode(subtreeX, subtreeY); + uint64_t subtreeMortonIdx = + ImplicitTilingUtilities::computeMortonIndex(subtreeID); auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { @@ -343,27 +338,26 @@ ImplicitQuadtreeLoader::createTileChildren(const Tile& tile) { assert(pQuadtreeID != nullptr && "This loader only serves quadtree tile"); // find the subtree ID - uint32_t subtreeLevelIdx = pQuadtreeID->level / this->_subtreeLevels; + CesiumGeometry::QuadtreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pQuadtreeID); + + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; if (subtreeLevelIdx >= this->_loadedSubtrees.size()) { return {{}, TileLoadResultState::Failed}; } - uint64_t levelLeft = pQuadtreeID->level % this->_subtreeLevels; - uint32_t subtreeX = pQuadtreeID->x >> levelLeft; - uint32_t subtreeY = pQuadtreeID->y >> levelLeft; + uint64_t subtreeMortonIdx = + ImplicitTilingUtilities::computeMortonIndex(subtreeID); - uint64_t subtreeMortonIdx = libmorton::morton2D_64_encode(subtreeX, subtreeY); auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt != this->_loadedSubtrees[subtreeLevelIdx].end()) { - uint64_t relativeTileMortonIdx = libmorton::morton2D_64_encode( - pQuadtreeID->x - (subtreeX << levelLeft), - pQuadtreeID->y - (subtreeY << levelLeft)); auto children = populateSubtree( subtreeIt->second, this->_subtreeLevels, - static_cast(levelLeft), - relativeTileMortonIdx, + subtreeID, tile, *this); @@ -395,7 +389,7 @@ void ImplicitQuadtreeLoader::addSubtreeAvailability( } uint64_t subtreeMortonID = - libmorton::morton2D_64_encode(subtreeID.x, subtreeID.y); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); this->_loadedSubtrees[levelIndex].insert_or_assign( subtreeMortonID, From d263ffd49ade5405b22bfd97e9b3aa6e834b6fef Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 17:39:04 +1100 Subject: [PATCH 05/16] Make octree loader use ImplicitTilingUtilities. --- .../src/ImplicitOctreeLoader.cpp | 144 +++++++-------- .../src/ImplicitQuadtreeLoader.cpp | 2 +- .../test/TestImplicitOctreeLoader.cpp | 174 +++++++++--------- 3 files changed, 155 insertions(+), 165 deletions(-) diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index abef3651b..40ed21695 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -78,68 +78,59 @@ BoundingVolume subdivideBoundingVolume( std::vector populateSubtree( const SubtreeAvailability& subtreeAvailability, uint32_t subtreeLevels, - uint32_t relativeTileLevel, - uint64_t relativeTileMortonID, + const CesiumGeometry::OctreeTileID& subtreeRootID, const Tile& tile, ImplicitOctreeLoader& loader) { + const CesiumGeometry::OctreeTileID& octreeID = + std::get(tile.getTileID()); + + uint32_t relativeTileLevel = octreeID.level - subtreeRootID.level; if (relativeTileLevel >= subtreeLevels) { return {}; } - const CesiumGeometry::OctreeTileID& octreeID = - std::get(tile.getTileID()); + OctreeChildren childIDs = ImplicitTilingUtilities::getChildren(octreeID); std::vector children; - children.reserve(8); - for (uint16_t y = 0; y < 2; ++y) { - uint32_t childY = (octreeID.y << 1) | y; - for (uint16_t z = 0; z < 2; ++z) { - uint32_t childZ = (octreeID.z << 1) | z; - for (uint16_t x = 0; x < 2; ++x) { - uint32_t childX = (octreeID.x << 1) | x; - - CesiumGeometry::OctreeTileID childID{ - octreeID.level + 1, - childX, - childY, - childZ}; - - uint32_t childIndex = - static_cast(libmorton::morton3D_32_encode(x, y, z)); - uint64_t relativeChildMortonID = relativeTileMortonID << 3 | childIndex; - uint32_t relativeChildLevel = relativeTileLevel + 1; - if (relativeChildLevel == subtreeLevels) { - if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { - Tile& child = children.emplace_back(&loader); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); - } + children.reserve(childIDs.size()); + + for (const CesiumGeometry::OctreeTileID& childID : childIDs) { + uint64_t relativeChildMortonID = + ImplicitTilingUtilities::computeRelativeMortonIndex( + subtreeRootID, + childID); + + uint32_t relativeChildLevel = relativeTileLevel + 1; + if (relativeChildLevel == subtreeLevels) { + if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { + Tile& child = children.emplace_back(&loader); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); + } + } else { + if (subtreeAvailability.isTileAvailable( + relativeChildLevel, + relativeChildMortonID)) { + if (subtreeAvailability.isContentAvailable( + relativeChildLevel, + relativeChildMortonID, + 0)) { + children.emplace_back(&loader); } else { - if (subtreeAvailability.isTileAvailable( - relativeChildLevel, - relativeChildMortonID)) { - if (subtreeAvailability.isContentAvailable( - relativeChildLevel, - relativeChildMortonID, - 0)) { - children.emplace_back(&loader); - } else { - children.emplace_back(&loader, TileEmptyContent{}); - } - - Tile& child = children.back(); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); - } + children.emplace_back(&loader, TileEmptyContent{}); } + + Tile& child = children.back(); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); } } } @@ -237,25 +228,18 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { } // find the subtree ID - uint32_t subtreeLevelIdx = pOctreeID->level / this->_subtreeLevels; - if (subtreeLevelIdx >= this->_loadedSubtrees.size()) { - return asyncSystem.createResolvedFuture( + CesiumGeometry::OctreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pOctreeID); + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; + if (subtreeLevelIdx >= _loadedSubtrees.size()) { + return asyncSystem.createResolvedFuture( TileLoadResult::createFailedResult(nullptr)); } - uint64_t levelLeft = pOctreeID->level % this->_subtreeLevels; - uint32_t subtreeLevel = this->_subtreeLevels * subtreeLevelIdx; - uint32_t subtreeX = pOctreeID->x >> levelLeft; - uint32_t subtreeY = pOctreeID->y >> levelLeft; - uint32_t subtreeZ = pOctreeID->z >> levelLeft; - CesiumGeometry::OctreeTileID subtreeID{ - subtreeLevel, - subtreeX, - subtreeY, - subtreeZ}; - uint64_t subtreeMortonIdx = - libmorton::morton3D_64_encode(subtreeX, subtreeY, subtreeZ); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { @@ -315,33 +299,29 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileChildrenResult ImplicitOctreeLoader::createTileChildren(const Tile& tile) { const CesiumGeometry::OctreeTileID* pOctreeID = std::get_if(&tile.getTileID()); - assert(pOctreeID != nullptr && "This loader only serves quadtree tile"); + assert(pOctreeID != nullptr && "This loader only serves octree tile"); // find the subtree ID - uint32_t subtreeLevelIdx = pOctreeID->level / this->_subtreeLevels; + CesiumGeometry::OctreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pOctreeID); + + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; if (subtreeLevelIdx >= this->_loadedSubtrees.size()) { return {{}, TileLoadResultState::Failed}; } - uint64_t levelLeft = pOctreeID->level % this->_subtreeLevels; - uint32_t subtreeX = pOctreeID->x >> levelLeft; - uint32_t subtreeY = pOctreeID->y >> levelLeft; - uint32_t subtreeZ = pOctreeID->z >> levelLeft; - uint64_t subtreeMortonIdx = - libmorton::morton3D_64_encode(subtreeX, subtreeY, subtreeZ); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); + auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt != this->_loadedSubtrees[subtreeLevelIdx].end()) { - uint64_t relativeTileMortonIdx = libmorton::morton3D_64_encode( - pOctreeID->x - (subtreeX << levelLeft), - pOctreeID->y - (subtreeY << levelLeft), - pOctreeID->z - (subtreeZ << levelLeft)); auto children = populateSubtree( subtreeIt->second, this->_subtreeLevels, - static_cast(levelLeft), - relativeTileMortonIdx, + subtreeID, tile, *this); @@ -373,7 +353,7 @@ void ImplicitOctreeLoader::addSubtreeAvailability( } uint64_t subtreeMortonID = - libmorton::morton3D_64_encode(subtreeID.x, subtreeID.y, subtreeID.z); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); this->_loadedSubtrees[levelIndex].insert_or_assign( subtreeMortonID, diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index a9f95c568..f13a42d90 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include using namespace Cesium3DTilesContent; diff --git a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp index c881e8119..f7f49045a 100644 --- a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp @@ -198,6 +198,30 @@ TEST_CASE("Test implicit octree loader") { } } +namespace { + +const Tile& +findTile(const gsl::span& children, const OctreeTileID& tileID) { + auto it = std::find_if( + children.begin(), + children.end(), + [tileID](const Tile& tile) { + const OctreeTileID* pID = std::get_if(&tile.getTileID()); + if (!pID) + return false; + return *pID == tileID; + }); + REQUIRE(it != children.end()); + return *it; +} + +const Tile& +findTile(const std::vector& children, const OctreeTileID& tileID) { + return findTile(gsl::span(children), tileID); +} + +} // namespace + TEST_CASE("Test tile subdivision for implicit octree loader") { Cesium3DTilesContent::registerAllTileContentTypes(); @@ -238,10 +262,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_1_0_0_0 = tileChildren[0]; - CHECK( - std::get(tile_1_0_0_0.getTileID()) == - OctreeTileID(1, 0, 0, 0)); + const auto& tile_1_0_0_0 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 0)); const auto& box_1_0_0_0 = std::get(tile_1_0_0_0.getBoundingVolume()); CHECK(box_1_0_0_0.getCenter() == glm::dvec3(-10.0, -10.0, -10.0)); @@ -249,10 +271,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_0_0 = tileChildren[1]; - CHECK( - std::get(tile_1_1_0_0.getTileID()) == - OctreeTileID(1, 1, 0, 0)); + const auto& tile_1_1_0_0 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 0)); const auto& box_1_1_0_0 = std::get(tile_1_1_0_0.getBoundingVolume()); CHECK(box_1_1_0_0.getCenter() == glm::dvec3(10.0, -10.0, -10.0)); @@ -260,10 +280,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_1_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_0_0_1 = tileChildren[2]; - CHECK( - std::get(tile_1_0_0_1.getTileID()) == - OctreeTileID(1, 0, 0, 1)); + const auto& tile_1_0_0_1 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 1)); const auto& box_1_0_0_1 = std::get(tile_1_0_0_1.getBoundingVolume()); CHECK(box_1_0_0_1.getCenter() == glm::dvec3(-10.0, -10.0, 10.0)); @@ -271,10 +289,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_0_1 = tileChildren[3]; - CHECK( - std::get(tile_1_1_0_1.getTileID()) == - OctreeTileID(1, 1, 0, 1)); + const auto& tile_1_1_0_1 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 1)); const auto& box_1_1_0_1 = std::get(tile_1_1_0_1.getBoundingVolume()); CHECK(box_1_1_0_1.getCenter() == glm::dvec3(10.0, -10.0, 10.0)); @@ -282,10 +298,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_1_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_0_1_0 = tileChildren[4]; - CHECK( - std::get(tile_1_0_1_0.getTileID()) == - OctreeTileID(1, 0, 1, 0)); + const auto& tile_1_0_1_0 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 0)); const auto& box_1_0_1_0 = std::get(tile_1_0_1_0.getBoundingVolume()); CHECK(box_1_0_1_0.getCenter() == glm::dvec3(-10.0, 10.0, -10.0)); @@ -293,10 +307,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_1_0 = tileChildren[5]; - CHECK( - std::get(tile_1_1_1_0.getTileID()) == - OctreeTileID(1, 1, 1, 0)); + const auto& tile_1_1_1_0 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 0)); const auto& box_1_1_1_0 = std::get(tile_1_1_1_0.getBoundingVolume()); CHECK(box_1_1_1_0.getCenter() == glm::dvec3(10.0, 10.0, -10.0)); @@ -304,10 +316,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_1_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_0_1_1 = tileChildren[6]; - CHECK( - std::get(tile_1_0_1_1.getTileID()) == - OctreeTileID(1, 0, 1, 1)); + const auto& tile_1_0_1_1 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 1)); const auto& box_1_0_1_1 = std::get(tile_1_0_1_1.getBoundingVolume()); CHECK(box_1_0_1_1.getCenter() == glm::dvec3(-10.0, 10.0, 10.0)); @@ -315,10 +325,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_1_1 = tileChildren[7]; - CHECK( - std::get(tile_1_1_1_1.getTileID()) == - OctreeTileID(1, 1, 1, 1)); + const auto& tile_1_1_1_1 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 1)); const auto& box_1_1_1_1 = std::get(tile_1_1_1_1.getBoundingVolume()); CHECK(box_1_1_1_1.getCenter() == glm::dvec3(10.0, 10.0, 10.0)); @@ -331,7 +339,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { // check subdivide one of the root children { - auto& tile_1_1_0_0 = tile.getChildren()[1]; + const auto& tile_1_1_0_0 = + findTile(tile.getChildren(), OctreeTileID(1, 1, 0, 0)); auto tileChildrenResult = loader.createTileChildren(tile_1_1_0_0); CHECK(tileChildrenResult.state == TileLoadResultState::Success); @@ -339,10 +348,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_2_2_0_0 = tileChildren[0]; - CHECK( - std::get(tile_2_2_0_0.getTileID()) == - OctreeTileID(2, 2, 0, 0)); + const auto& tile_2_2_0_0 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 0)); const auto& box_2_2_0_0 = std::get(tile_2_2_0_0.getBoundingVolume()); CHECK(box_2_2_0_0.getCenter() == glm::dvec3(5.0, -15.0, -15.0)); @@ -350,10 +357,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_0_0 = tileChildren[1]; - CHECK( - std::get(tile_2_3_0_0.getTileID()) == - OctreeTileID(2, 3, 0, 0)); + const auto& tile_2_3_0_0 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 0)); const auto& box_2_3_0_0 = std::get(tile_2_3_0_0.getBoundingVolume()); CHECK(box_2_3_0_0.getCenter() == glm::dvec3(15.0, -15.0, -15.0)); @@ -361,10 +366,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_3_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_2_0_1 = tileChildren[2]; - CHECK( - std::get(tile_2_2_0_1.getTileID()) == - OctreeTileID(2, 2, 0, 1)); + const auto& tile_2_2_0_1 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 1)); const auto& box_2_2_0_1 = std::get(tile_2_2_0_1.getBoundingVolume()); CHECK(box_2_2_0_1.getCenter() == glm::dvec3(5.0, -15.0, -5.0)); @@ -372,10 +375,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_0_1 = tileChildren[3]; - CHECK( - std::get(tile_2_3_0_1.getTileID()) == - OctreeTileID(2, 3, 0, 1)); + const auto& tile_2_3_0_1 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 1)); const auto& box_2_3_0_1 = std::get(tile_2_3_0_1.getBoundingVolume()); CHECK(box_2_3_0_1.getCenter() == glm::dvec3(15.0, -15.0, -5.0)); @@ -383,10 +384,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_3_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_2_1_0 = tileChildren[4]; - CHECK( - std::get(tile_2_2_1_0.getTileID()) == - OctreeTileID(2, 2, 1, 0)); + const auto& tile_2_2_1_0 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 0)); const auto& box_2_2_1_0 = std::get(tile_2_2_1_0.getBoundingVolume()); CHECK(box_2_2_1_0.getCenter() == glm::dvec3(5.0, -5.0, -15.0)); @@ -394,10 +393,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_1_0 = tileChildren[5]; - CHECK( - std::get(tile_2_3_1_0.getTileID()) == - OctreeTileID(2, 3, 1, 0)); + const auto& tile_2_3_1_0 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 0)); const auto& box_2_3_1_0 = std::get(tile_2_3_1_0.getBoundingVolume()); CHECK(box_2_3_1_0.getCenter() == glm::dvec3(15.0, -5.0, -15.0)); @@ -405,10 +402,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_3_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_2_1_1 = tileChildren[6]; - CHECK( - std::get(tile_2_2_1_1.getTileID()) == - OctreeTileID(2, 2, 1, 1)); + const auto& tile_2_2_1_1 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 1)); const auto& box_2_2_1_1 = std::get(tile_2_2_1_1.getBoundingVolume()); CHECK(box_2_2_1_1.getCenter() == glm::dvec3(5.0, -5.0, -5.0)); @@ -416,10 +411,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_1_1 = tileChildren[7]; - CHECK( - std::get(tile_2_3_1_1.getTileID()) == - OctreeTileID(2, 3, 1, 1)); + const auto& tile_2_3_1_1 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 1)); const auto& box_2_3_1_1 = std::get(tile_2_3_1_1.getBoundingVolume()); CHECK(box_2_3_1_1.getCenter() == glm::dvec3(15.0, -5.0, -5.0)); @@ -469,7 +462,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_1_0_0_0 = tileChildren[0]; + const auto& tile_1_0_0_0 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 0)); const auto& region_1_0_0_0 = std::get(tile_1_0_0_0.getBoundingVolume()); CHECK(region_1_0_0_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -480,7 +474,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_0_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_1_0_0 = tileChildren[1]; + const auto& tile_1_1_0_0 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 0)); const auto& region_1_1_0_0 = std::get(tile_1_1_0_0.getBoundingVolume()); CHECK(region_1_1_0_0.getRectangle().getWest() == Approx(0.0)); @@ -491,7 +486,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_1_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_1_0_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_0_0_1 = tileChildren[2]; + const auto& tile_1_0_0_1 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 1)); const auto& region_1_0_0_1 = std::get(tile_1_0_0_1.getBoundingVolume()); CHECK(region_1_0_0_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -502,7 +498,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_0_1.getMinimumHeight() == Approx(50.0)); CHECK(region_1_0_0_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_0_1 = tileChildren[3]; + const auto& tile_1_1_0_1 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 1)); const auto& region_1_1_0_1 = std::get(tile_1_1_0_1.getBoundingVolume()); CHECK(region_1_1_0_0.getRectangle().getWest() == Approx(0.0)); @@ -513,7 +510,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_1_0_1.getMinimumHeight() == Approx(50.0)); CHECK(region_1_1_0_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_0_1_0 = tileChildren[4]; + const auto& tile_1_0_1_0 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 0)); const auto& region_1_0_1_0 = std::get(tile_1_0_1_0.getBoundingVolume()); CHECK(region_1_0_1_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -524,7 +522,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_1_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_1_1_0 = tileChildren[5]; + const auto& tile_1_1_1_0 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 0)); const auto& region_1_1_1_0 = std::get(tile_1_1_1_0.getBoundingVolume()); CHECK(region_1_1_1_0.getRectangle().getWest() == Approx(0.0)); @@ -535,7 +534,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_1_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_1_1_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_0_1_1 = tileChildren[6]; + const auto& tile_1_0_1_1 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 1)); const auto& region_1_0_1_1 = std::get(tile_1_0_1_1.getBoundingVolume()); CHECK(region_1_0_1_1.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -546,7 +546,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_1_1.getMinimumHeight() == Approx(50.0)); CHECK(region_1_0_1_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_1_1 = tileChildren[7]; + const auto& tile_1_1_1_1 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 1)); const auto& region_1_1_1_1 = std::get(tile_1_1_1_1.getBoundingVolume()); CHECK(region_1_1_1_1.getRectangle().getWest() == Approx(0.0)); @@ -562,14 +563,16 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { // check subdivide one of the root children { - auto& tile_1_1_0_0 = tile.getChildren()[1]; + auto& tile_1_1_0_0 = + findTile(tile.getChildren(), OctreeTileID(1, 1, 0, 0)); auto tileChildrenResult = loader.createTileChildren(tile_1_1_0_0); CHECK(tileChildrenResult.state == TileLoadResultState::Success); const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_2_2_0_0 = tileChildren[0]; + const auto& tile_2_2_0_0 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 0)); const auto& region_2_2_0_0 = std::get(tile_2_2_0_0.getBoundingVolume()); CHECK(region_2_2_0_0.getRectangle().getWest() == Approx(0.0)); @@ -582,7 +585,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_0_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_3_0_0 = tileChildren[1]; + const auto& tile_2_3_0_0 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 0)); const auto& region_2_3_0_0 = std::get(tile_2_3_0_0.getBoundingVolume()); CHECK(region_2_3_0_0.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -595,7 +599,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_3_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_3_0_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_2_0_1 = tileChildren[2]; + const auto& tile_2_2_0_1 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 1)); const auto& region_2_2_0_1 = std::get(tile_2_2_0_1.getBoundingVolume()); CHECK(region_2_2_0_1.getRectangle().getWest() == Approx(0.0)); @@ -608,7 +613,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_0_1.getMinimumHeight() == Approx(25.0)); CHECK(region_2_2_0_1.getMaximumHeight() == Approx(50.0)); - const auto& tile_2_3_0_1 = tileChildren[3]; + const auto& tile_2_3_0_1 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 1)); const auto& region_2_3_0_1 = std::get(tile_2_3_0_1.getBoundingVolume()); CHECK(region_2_3_0_1.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -621,7 +627,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_3_0_1.getMinimumHeight() == Approx(25.0)); CHECK(region_2_3_0_1.getMaximumHeight() == Approx(50.0)); - const auto& tile_2_2_1_0 = tileChildren[4]; + const auto& tile_2_2_1_0 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 0)); const auto& region_2_2_1_0 = std::get(tile_2_2_1_0.getBoundingVolume()); CHECK(region_2_2_1_0.getRectangle().getWest() == Approx(0.0)); @@ -634,7 +641,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_1_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_3_1_0 = tileChildren[5]; + const auto& tile_2_3_1_0 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 0)); const auto& region_2_3_1_0 = std::get(tile_2_3_1_0.getBoundingVolume()); CHECK(region_2_3_1_0.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -646,7 +654,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_3_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_3_1_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_2_1_1 = tileChildren[6]; + const auto& tile_2_2_1_1 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 1)); const auto& region_2_2_1_1 = std::get(tile_2_2_1_1.getBoundingVolume()); CHECK(region_2_2_1_1.getRectangle().getWest() == Approx(0.0)); @@ -659,7 +668,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_1_1.getMinimumHeight() == Approx(25.0)); CHECK(region_2_2_1_1.getMaximumHeight() == Approx(50.0)); - const auto& tile_2_3_1_1 = tileChildren[7]; + const auto& tile_2_3_1_1 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 1)); const auto& region_2_3_1_1 = std::get(tile_2_3_1_1.getBoundingVolume()); CHECK(region_2_3_1_1.getRectangle().getWest() == Approx(Math::PiOverTwo)); From 75f545bb719f6a1ba2e50b0519b9e68c120d8ae1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 18:04:23 +1100 Subject: [PATCH 06/16] Move implicit BV computations to ImplicitTilingUtilities. --- .../ImplicitTilingUtilities.h | 65 ++++++++- .../src/ImplicitTilingUtilities.cpp | 129 +++++++++++++++++- .../src/ImplicitOctreeLoader.cpp | 48 +------ .../src/ImplicitQuadtreeLoader.cpp | 48 +------ 4 files changed, 197 insertions(+), 93 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 6bf604f32..760ecfaf8 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -7,10 +7,14 @@ #include #include +namespace CesiumGeospatial { +class BoundingRegion; +class S2CellBoundingVolume; +} // namespace CesiumGeospatial + namespace CesiumGeometry { -struct QuadtreeTileID; -struct OctreeTileID; -} // namespace CesiumGeometry +class OrientedBoundingBox; +} namespace Cesium3DTilesContent { @@ -269,6 +273,61 @@ class ImplicitTilingUtilities { getChildren(const CesiumGeometry::OctreeTileID& tileID) noexcept { return OctreeChildren{tileID}; } + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeospatial::BoundingRegion computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeospatial::BoundingRegion computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( + const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index da9c65340..95257a97e 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include #include @@ -124,6 +127,124 @@ OctreeTileID ImplicitTilingUtilities::absoluteTileIDToRelative( tileID.z - (rootID.z << relativeTileLevel)); } +namespace { + +CesiumGeospatial::GlobeRectangle subdivideRectangle( + const CesiumGeospatial::GlobeRectangle& rootRectangle, + const CesiumGeometry::QuadtreeTileID& tileID, + double denominator) { + double latSize = + (rootRectangle.getNorth() - rootRectangle.getSouth()) / denominator; + double longSize = + (rootRectangle.getEast() - rootRectangle.getWest()) / denominator; + + double childWest = rootRectangle.getWest() + longSize * tileID.x; + double childEast = rootRectangle.getWest() + longSize * (tileID.x + 1); + + double childSouth = rootRectangle.getSouth() + latSize * tileID.y; + double childNorth = rootRectangle.getSouth() + latSize * (tileID.y + 1); + + return CesiumGeospatial::GlobeRectangle( + childWest, + childSouth, + childEast, + childNorth); +} + +} // namespace + +CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + double denominator = static_cast(1 << tileID.level); + return CesiumGeospatial::BoundingRegion{ + subdivideRectangle( + rootBoundingVolume.getRectangle(), + tileID, + denominator), + rootBoundingVolume.getMinimumHeight(), + rootBoundingVolume.getMaximumHeight()}; +} + +CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept { + double denominator = static_cast(1 << tileID.level); + double heightSize = (rootBoundingVolume.getMaximumHeight() - + rootBoundingVolume.getMinimumHeight()) / + denominator; + + double childMinHeight = + rootBoundingVolume.getMinimumHeight() + heightSize * tileID.z; + double childMaxHeight = + rootBoundingVolume.getMinimumHeight() + heightSize * (tileID.z + 1); + + return CesiumGeospatial::BoundingRegion{ + subdivideRectangle( + rootBoundingVolume.getRectangle(), + QuadtreeTileID(tileID.level, tileID.x, tileID.y), + denominator), + childMinHeight, + childMaxHeight}; +} + +CesiumGeometry::OrientedBoundingBox +ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); + const glm::dvec3& center = rootBoundingVolume.getCenter(); + + double denominator = static_cast(1 << tileID.level); + glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; + + glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; + glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; + glm::dvec3 childMin = min + xDim * double(tileID.x) + yDim * double(tileID.y); + glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + + yDim * double(tileID.y + 1) + halfAxes[2] * 2.0; + + return CesiumGeometry::OrientedBoundingBox( + (childMin + childMax) / 2.0, + glm::dmat3{xDim / 2.0, yDim / 2.0, halfAxes[2]}); +} + +CesiumGeometry::OrientedBoundingBox +ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept { + const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); + const glm::dvec3& center = rootBoundingVolume.getCenter(); + + double denominator = static_cast(1 << tileID.level); + glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; + + glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; + glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; + glm::dvec3 zDim = halfAxes[2] * 2.0 / denominator; + glm::dvec3 childMin = min + xDim * double(tileID.x) + + yDim * double(tileID.y) + zDim * double(tileID.z); + glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + + yDim * double(tileID.y + 1) + + zDim * double(tileID.z + 1); + + return CesiumGeometry::OrientedBoundingBox( + (childMin + childMax) / 2.0, + glm::dmat3{xDim / 2.0, yDim / 2.0, zDim / 2.0}); +} + +CesiumGeospatial::S2CellBoundingVolume +ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return CesiumGeospatial::S2CellBoundingVolume( + CesiumGeospatial::S2CellID::fromQuadtreeTileID( + rootBoundingVolume.getCellID().getFace(), + tileID), + rootBoundingVolume.getMinimumHeight(), + rootBoundingVolume.getMaximumHeight()); +} + QuadtreeChildren::iterator::iterator( const CesiumGeometry::QuadtreeTileID& parentTileID, bool isEnd) noexcept @@ -150,7 +271,7 @@ QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Bit 0 in value replaces bit 0 of the X coordinate. this->_current.x = (this->_current.x & ~1) | (value & 1); - // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Y coordinate // and add this shifted value to produce the new Y coordinate. @@ -206,10 +327,10 @@ OctreeChildren::iterator& OctreeChildren::iterator::operator++() { this->_current.x = (this->_current.x & ~1) | (value & 1); this->_current.y = (this->_current.y & ~1) | ((value >> 1) & 1); - // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past - // the last child). So we just clear the low bit of the current Y coordinate - // and add this shifted value to produce the new Y coordinate. + // the last child). So we just clear the low bit of the current Z coordinate + // and add this shifted value to produce the new Z coordinate. this->_current.z = (this->_current.z & ~1) + (value >> 2); return *this; diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 40ed21695..203654df1 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -11,59 +11,19 @@ #include #include +#include + using namespace Cesium3DTilesContent; namespace Cesium3DTilesSelection { namespace { struct BoundingVolumeSubdivision { BoundingVolume operator()(const CesiumGeospatial::BoundingRegion& region) { - const CesiumGeospatial::GlobeRectangle& globeRect = region.getRectangle(); - double denominator = static_cast(1 << tileID.level); - double latSize = - (globeRect.getNorth() - globeRect.getSouth()) / denominator; - double longSize = (globeRect.getEast() - globeRect.getWest()) / denominator; - double heightSize = - (region.getMaximumHeight() - region.getMinimumHeight()) / denominator; - - double childWest = globeRect.getWest() + longSize * tileID.x; - double childEast = globeRect.getWest() + longSize * (tileID.x + 1); - - double childSouth = globeRect.getSouth() + latSize * tileID.y; - double childNorth = globeRect.getSouth() + latSize * (tileID.y + 1); - - double childMinHeight = region.getMinimumHeight() + heightSize * tileID.z; - double childMaxHeight = - region.getMinimumHeight() + heightSize * (tileID.z + 1); - - return CesiumGeospatial::BoundingRegion{ - CesiumGeospatial::GlobeRectangle( - childWest, - childSouth, - childEast, - childNorth), - childMinHeight, - childMaxHeight}; + return ImplicitTilingUtilities::computeBoundingVolume(region, this->tileID); } BoundingVolume operator()(const CesiumGeometry::OrientedBoundingBox& obb) { - const glm::dmat3& halfAxes = obb.getHalfAxes(); - const glm::dvec3& center = obb.getCenter(); - - double denominator = static_cast(1 << tileID.level); - glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; - - glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; - glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; - glm::dvec3 zDim = halfAxes[2] * 2.0 / denominator; - glm::dvec3 childMin = min + xDim * double(tileID.x) + - yDim * double(tileID.y) + zDim * double(tileID.z); - glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + - yDim * double(tileID.y + 1) + - zDim * double(tileID.z + 1); - - return CesiumGeometry::OrientedBoundingBox( - (childMin + childMax) / 2.0, - glm::dmat3{xDim / 2.0, yDim / 2.0, zDim / 2.0}); + return ImplicitTilingUtilities::computeBoundingVolume(obb, this->tileID); } const CesiumGeometry::OctreeTileID& tileID; diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index f13a42d90..22b7a80b3 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -14,6 +14,7 @@ #include #include +#include using namespace Cesium3DTilesContent; @@ -21,55 +22,18 @@ namespace Cesium3DTilesSelection { namespace { struct BoundingVolumeSubdivision { BoundingVolume operator()(const CesiumGeospatial::BoundingRegion& region) { - const CesiumGeospatial::GlobeRectangle& globeRect = region.getRectangle(); - double denominator = static_cast(1 << tileID.level); - double latSize = - (globeRect.getNorth() - globeRect.getSouth()) / denominator; - double longSize = (globeRect.getEast() - globeRect.getWest()) / denominator; - - double childWest = globeRect.getWest() + longSize * tileID.x; - double childEast = globeRect.getWest() + longSize * (tileID.x + 1); - - double childSouth = globeRect.getSouth() + latSize * tileID.y; - double childNorth = globeRect.getSouth() + latSize * (tileID.y + 1); - - return CesiumGeospatial::BoundingRegion{ - CesiumGeospatial::GlobeRectangle( - childWest, - childSouth, - childEast, - childNorth), - region.getMinimumHeight(), - region.getMaximumHeight()}; + return ImplicitTilingUtilities::computeBoundingVolume(region, this->tileID); } BoundingVolume operator()(const CesiumGeospatial::S2CellBoundingVolume& s2Volume) { - return CesiumGeospatial::S2CellBoundingVolume( - CesiumGeospatial::S2CellID::fromQuadtreeTileID( - s2Volume.getCellID().getFace(), - tileID), - s2Volume.getMinimumHeight(), - s2Volume.getMaximumHeight()); + return ImplicitTilingUtilities::computeBoundingVolume( + s2Volume, + this->tileID); } BoundingVolume operator()(const CesiumGeometry::OrientedBoundingBox& obb) { - const glm::dmat3& halfAxes = obb.getHalfAxes(); - const glm::dvec3& center = obb.getCenter(); - - double denominator = static_cast(1 << tileID.level); - glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; - - glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; - glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; - glm::dvec3 childMin = - min + xDim * double(tileID.x) + yDim * double(tileID.y); - glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + - yDim * double(tileID.y + 1) + halfAxes[2] * 2.0; - - return CesiumGeometry::OrientedBoundingBox( - (childMin + childMax) / 2.0, - glm::dmat3{xDim / 2.0, yDim / 2.0, halfAxes[2]}); + return ImplicitTilingUtilities::computeBoundingVolume(obb, this->tileID); } const CesiumGeometry::QuadtreeTileID& tileID; From 5def156fdeab6d59df1c04ba0c32cf8f7abc0b85 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 18:10:51 +1100 Subject: [PATCH 07/16] Add computeLevelDenominator. --- .../Cesium3DTilesContent/ImplicitTilingUtilities.h | 13 +++++++++++++ .../src/ImplicitTilingUtilities.cpp | 13 +++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 760ecfaf8..61c8eda29 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -328,6 +328,19 @@ class ImplicitTilingUtilities { static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + /** + * @brief Computes the denominator for a given implicit tile level. + * + * Divide the root tile's geometric error by this value to get the standard + * geometric error for tiles on the level. Or divide each component of a + * bounding volume by this factor to get the size of the bounding volume along + * that axis for tiles of this level. + * + * @param level The tile level. + * @return The denominator for the level. + */ + static double computeLevelDenominator(uint32_t level) noexcept; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index 95257a97e..cce56d434 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -156,7 +156,7 @@ CesiumGeospatial::GlobeRectangle subdivideRectangle( CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept { - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); return CesiumGeospatial::BoundingRegion{ subdivideRectangle( rootBoundingVolume.getRectangle(), @@ -169,7 +169,7 @@ CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::OctreeTileID& tileID) noexcept { - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); double heightSize = (rootBoundingVolume.getMaximumHeight() - rootBoundingVolume.getMinimumHeight()) / denominator; @@ -195,7 +195,7 @@ ImplicitTilingUtilities::computeBoundingVolume( const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); const glm::dvec3& center = rootBoundingVolume.getCenter(); - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; @@ -216,7 +216,7 @@ ImplicitTilingUtilities::computeBoundingVolume( const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); const glm::dvec3& center = rootBoundingVolume.getCenter(); - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; @@ -245,6 +245,11 @@ ImplicitTilingUtilities::computeBoundingVolume( rootBoundingVolume.getMaximumHeight()); } +double +ImplicitTilingUtilities::computeLevelDenominator(uint32_t level) noexcept { + return static_cast(1 << level); +} + QuadtreeChildren::iterator::iterator( const CesiumGeometry::QuadtreeTileID& parentTileID, bool isEnd) noexcept From f004b778645a0fe2c05dd752e64fdae464326dad Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 19:20:09 +1100 Subject: [PATCH 08/16] Add some tests, fix some bugs. --- .../ImplicitTilingUtilities.h | 26 ++-- .../src/ImplicitTilingUtilities.cpp | 4 +- .../test/TestImplicitTilingUtilities.cpp | 147 ++++++++++++++++-- 3 files changed, 146 insertions(+), 31 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 61c8eda29..a71f3e385 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -145,6 +145,19 @@ class ImplicitTilingUtilities { const std::string& urlTemplate, const CesiumGeometry::OctreeTileID& octreeID); + /** + * @brief Computes the denominator for a given implicit tile level. + * + * Divide the root tile's geometric error by this value to get the standard + * geometric error for tiles on the level. Or divide each component of a + * bounding volume by this factor to get the size of the bounding volume along + * that axis for tiles of this level. + * + * @param level The tile level. + * @return The denominator for the level. + */ + static double computeLevelDenominator(uint32_t level) noexcept; + /** * @brief Computes the Morton index for a given quadtree tile within its * level. @@ -328,19 +341,6 @@ class ImplicitTilingUtilities { static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; - - /** - * @brief Computes the denominator for a given implicit tile level. - * - * Divide the root tile's geometric error by this value to get the standard - * geometric error for tiles on the level. Or divide each component of a - * bounding volume by this factor to get the size of the bounding volume along - * that axis for tiles of this level. - * - * @param level The tile level. - * @return The denominator for the level. - */ - static double computeLevelDenominator(uint32_t level) noexcept; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index cce56d434..e8dfec1ad 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -89,7 +89,7 @@ CesiumGeometry::QuadtreeTileID ImplicitTilingUtilities::getSubtreeRootID( uint32_t subtreeLevel = tileID.level / subtreeLevels; uint32_t levelsLeft = tileID.level % subtreeLevels; return QuadtreeTileID( - subtreeLevel, + subtreeLevel * subtreeLevels, tileID.x >> levelsLeft, tileID.y >> levelsLeft); } @@ -100,7 +100,7 @@ CesiumGeometry::OctreeTileID ImplicitTilingUtilities::getSubtreeRootID( uint32_t subtreeLevel = tileID.level / subtreeLevels; uint32_t levelsLeft = tileID.level % subtreeLevels; return OctreeTileID( - subtreeLevel, + subtreeLevel * subtreeLevels, tileID.x >> levelsLeft, tileID.y >> levelsLeft, tileID.z >> levelsLeft); diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp index f81926c25..f93121f44 100644 --- a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -1,6 +1,7 @@ #include #include +#include #include @@ -9,14 +10,14 @@ using namespace Cesium3DTilesContent; TEST_CASE("ImplicitTilingUtilities child tile iteration") { SECTION("QuadtreeTileID") { - QuadtreeTileID parent(1, 2, 3); + QuadtreeTileID parent(11, 2, 3); QuadtreeChildren children = ImplicitTilingUtilities::getChildren(parent); // Check we can enumerate the children with a range-based for loop. int count = 0; for (const QuadtreeTileID& tileID : children) { - CHECK(tileID.level == 2); + CHECK(tileID.level == 12); CHECK((tileID.x == 4 || tileID.x == 5)); CHECK((tileID.y == 6 || tileID.y == 7)); ++count; @@ -26,10 +27,10 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { // Check we have exactly the right children. std::vector expected{ - QuadtreeTileID(2, 4, 6), - QuadtreeTileID(2, 5, 6), - QuadtreeTileID(2, 4, 7), - QuadtreeTileID(2, 5, 7)}; + QuadtreeTileID(12, 4, 6), + QuadtreeTileID(12, 5, 6), + QuadtreeTileID(12, 4, 7), + QuadtreeTileID(12, 5, 7)}; auto mismatch = std::mismatch( children.begin(), children.end(), @@ -40,14 +41,14 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { } SECTION("OctreeTileID") { - OctreeTileID parent(1, 2, 3, 4); + OctreeTileID parent(11, 2, 3, 4); OctreeChildren children = ImplicitTilingUtilities::getChildren(parent); // Check we can enumerate the children with a range-based for loop. int count = 0; for (const OctreeTileID& tileID : children) { - CHECK(tileID.level == 2); + CHECK(tileID.level == 12); CHECK((tileID.x == 4 || tileID.x == 5)); CHECK((tileID.y == 6 || tileID.y == 7)); CHECK((tileID.z == 8 || tileID.z == 9)); @@ -58,14 +59,14 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { // Check we have exactly the right children. std::vector expected{ - OctreeTileID(2, 4, 6, 8), - OctreeTileID(2, 5, 6, 8), - OctreeTileID(2, 4, 7, 8), - OctreeTileID(2, 5, 7, 8), - OctreeTileID(2, 4, 6, 9), - OctreeTileID(2, 5, 6, 9), - OctreeTileID(2, 4, 7, 9), - OctreeTileID(2, 5, 7, 9)}; + OctreeTileID(12, 4, 6, 8), + OctreeTileID(12, 5, 6, 8), + OctreeTileID(12, 4, 7, 8), + OctreeTileID(12, 5, 7, 8), + OctreeTileID(12, 4, 6, 9), + OctreeTileID(12, 5, 6, 9), + OctreeTileID(12, 4, 7, 9), + OctreeTileID(12, 5, 7, 9)}; auto mismatch = std::mismatch( children.begin(), children.end(), @@ -75,3 +76,117 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { CHECK(mismatch.second == expected.end()); } } + +TEST_CASE("ImplicitTilingUtilities::resolveUrl") { + SECTION("quadtree") { + QuadtreeTileID tileID(11, 2, 3); + std::string url = ImplicitTilingUtilities::resolveUrl( + "https://example.com", + "tiles/{level}/{x}/{y}", + tileID); + CHECK(url == "https://example.com/tiles/11/2/3"); + } + + SECTION("octree") { + OctreeTileID tileID(11, 2, 3, 4); + std::string url = ImplicitTilingUtilities::resolveUrl( + "https://example.com", + "tiles/{level}/{x}/{y}/{z}", + tileID); + CHECK(url == "https://example.com/tiles/11/2/3/4"); + } +} + +TEST_CASE("ImplicitTilingUtilities::computeMortonIndex") { + SECTION("quadtree") { + QuadtreeTileID tileID(11, 2, 3); + CHECK( + ImplicitTilingUtilities::computeMortonIndex(tileID) == + libmorton::morton2D_64_encode(2, 3)); + } + + SECTION("quadtree") { + OctreeTileID tileID(11, 2, 3, 4); + CHECK( + ImplicitTilingUtilities::computeMortonIndex(tileID) == + libmorton::morton3D_64_encode(2, 3, 4)); + } +} + +TEST_CASE("ImplicitTilingUtilities::computeRelativeMortonIndex") { + SECTION("quadtree") { + QuadtreeTileID rootID(11, 2, 3); + QuadtreeTileID tileID(12, 5, 6); + CHECK( + ImplicitTilingUtilities::computeRelativeMortonIndex(rootID, tileID) == + 1); + } + + SECTION("octree") { + OctreeTileID rootID(11, 2, 3, 4); + OctreeTileID tileID(12, 5, 6, 8); + CHECK( + ImplicitTilingUtilities::computeRelativeMortonIndex(rootID, tileID) == + 1); + } +} + +TEST_CASE("ImplicitTilingUtilities::getSubtreeRootID") { + SECTION("quadtree") { + QuadtreeTileID tileID(10, 2, 3); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(5, tileID) == + QuadtreeTileID(10, 2, 3)); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(4, tileID) == + QuadtreeTileID(8, 0, 0)); + } + + SECTION("octree") { + OctreeTileID tileID(10, 2, 3, 4); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(5, tileID) == + OctreeTileID(10, 2, 3, 4)); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(4, tileID) == + OctreeTileID(8, 0, 0, 1)); + } +} + +TEST_CASE("ImplicitTilingUtilities::absoluteTileIDToRelative") { + SECTION("quadtree") { + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(11, 2, 3)) == QuadtreeTileID(11, 2, 3)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + QuadtreeTileID(11, 2, 3), + QuadtreeTileID(11, 2, 3)) == QuadtreeTileID(0, 0, 0)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + QuadtreeTileID(11, 2, 3), + QuadtreeTileID(12, 5, 7)) == QuadtreeTileID(1, 1, 1)); + } + + SECTION("octree") { + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + OctreeTileID(0, 0, 0, 0), + OctreeTileID(11, 2, 3, 4)) == OctreeTileID(11, 2, 3, 4)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + OctreeTileID(11, 2, 3, 4), + OctreeTileID(11, 2, 3, 4)) == OctreeTileID(0, 0, 0, 0)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + OctreeTileID(11, 2, 3, 4), + OctreeTileID(12, 5, 7, 9)) == OctreeTileID(1, 1, 1, 1)); + } +} + +TEST_CASE("ImplicitTilingUtilities::computeLevelDenominator") { + CHECK(ImplicitTilingUtilities::computeLevelDenominator(0) == 1.0); + CHECK(ImplicitTilingUtilities::computeLevelDenominator(1) == 2.0); + CHECK(ImplicitTilingUtilities::computeLevelDenominator(2) == 4.0); +} From a950b38b1fbc5e47092b35a3ddb74cc9e527e15a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 19:54:08 +1100 Subject: [PATCH 09/16] Add bounding volume tests. --- .../test/TestImplicitTilingUtilities.cpp | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp index f93121f44..83a7085ef 100644 --- a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include @@ -6,6 +9,7 @@ #include using namespace CesiumGeometry; +using namespace CesiumGeospatial; using namespace Cesium3DTilesContent; TEST_CASE("ImplicitTilingUtilities child tile iteration") { @@ -190,3 +194,186 @@ TEST_CASE("ImplicitTilingUtilities::computeLevelDenominator") { CHECK(ImplicitTilingUtilities::computeLevelDenominator(1) == 2.0); CHECK(ImplicitTilingUtilities::computeLevelDenominator(2) == 4.0); } + +TEST_CASE("ImplicitTilingUtilities::computeBoundingVolume") { + SECTION("OrientedBoundingBox") { + SECTION("quadtree") { + OrientedBoundingBox root(glm::dvec3(1.0, 2.0, 3.0), glm::dmat3(10.0)); + + OrientedBoundingBox l1x0y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 0)); + CHECK(l1x0y0.getCenter() == glm::dvec3(-4.0, -3.0, 3.0)); + CHECK(l1x0y0.getLengths() == glm::dvec3(10.0, 10.0, 20.0)); + + OrientedBoundingBox l1x1y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 1, 0)); + CHECK(l1x1y0.getCenter() == glm::dvec3(6.0, -3.0, 3.0)); + CHECK(l1x1y0.getLengths() == glm::dvec3(10.0, 10.0, 20.0)); + + OrientedBoundingBox l1x0y1 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 1)); + CHECK(l1x0y1.getCenter() == glm::dvec3(-4.0, 7.0, 3.0)); + CHECK(l1x0y1.getLengths() == glm::dvec3(10.0, 10.0, 20.0)); + } + + SECTION("octree") { + OrientedBoundingBox root(glm::dvec3(1.0, 2.0, 3.0), glm::dmat3(10.0)); + + OrientedBoundingBox l1x0y0z0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 0)); + CHECK(l1x0y0z0.getCenter() == glm::dvec3(-4.0, -3.0, -2.0)); + CHECK(l1x0y0z0.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + + OrientedBoundingBox l1x1y0z0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 1, 0, 0)); + CHECK(l1x1y0z0.getCenter() == glm::dvec3(6.0, -3.0, -2.0)); + CHECK(l1x1y0z0.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + + OrientedBoundingBox l1x0y1z0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 1, 0)); + CHECK(l1x0y1z0.getCenter() == glm::dvec3(-4.0, 7.0, -2.0)); + CHECK(l1x0y1z0.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + + OrientedBoundingBox l1x0y0z1 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 1)); + CHECK(l1x0y0z1.getCenter() == glm::dvec3(-4.0, -3.0, 8.0)); + CHECK(l1x0y0z1.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + } + } + + SECTION("BoundingRegion") { + SECTION("quadtree") { + BoundingRegion root(GlobeRectangle(1.0, 2.0, 3.0, 4.0), 10.0, 20.0); + + BoundingRegion l1x0y0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 0)); + CHECK(l1x0y0.getRectangle().getWest() == 1.0); + CHECK(l1x0y0.getRectangle().getSouth() == 2.0); + CHECK(l1x0y0.getRectangle().getEast() == 2.0); + CHECK(l1x0y0.getRectangle().getNorth() == 3.0); + CHECK(l1x0y0.getMinimumHeight() == 10.0); + CHECK(l1x0y0.getMaximumHeight() == 20.0); + + BoundingRegion l1x1y0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 1, 0)); + CHECK(l1x1y0.getRectangle().getWest() == 2.0); + CHECK(l1x1y0.getRectangle().getSouth() == 2.0); + CHECK(l1x1y0.getRectangle().getEast() == 3.0); + CHECK(l1x1y0.getRectangle().getNorth() == 3.0); + CHECK(l1x1y0.getMinimumHeight() == 10.0); + CHECK(l1x1y0.getMaximumHeight() == 20.0); + + BoundingRegion l1x0y1 = ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 1)); + CHECK(l1x0y1.getRectangle().getWest() == 1.0); + CHECK(l1x0y1.getRectangle().getSouth() == 3.0); + CHECK(l1x0y1.getRectangle().getEast() == 2.0); + CHECK(l1x0y1.getRectangle().getNorth() == 4.0); + CHECK(l1x0y1.getMinimumHeight() == 10.0); + CHECK(l1x0y1.getMaximumHeight() == 20.0); + } + + SECTION("octree") { + BoundingRegion root(GlobeRectangle(1.0, 2.0, 3.0, 4.0), 10.0, 20.0); + + BoundingRegion l1x0y0z0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 0)); + CHECK(l1x0y0z0.getRectangle().getWest() == 1.0); + CHECK(l1x0y0z0.getRectangle().getSouth() == 2.0); + CHECK(l1x0y0z0.getRectangle().getEast() == 2.0); + CHECK(l1x0y0z0.getRectangle().getNorth() == 3.0); + CHECK(l1x0y0z0.getMinimumHeight() == 10.0); + CHECK(l1x0y0z0.getMaximumHeight() == 15.0); + + BoundingRegion l1x1y0z0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 1, 0, 0)); + CHECK(l1x1y0z0.getRectangle().getWest() == 2.0); + CHECK(l1x1y0z0.getRectangle().getSouth() == 2.0); + CHECK(l1x1y0z0.getRectangle().getEast() == 3.0); + CHECK(l1x1y0z0.getRectangle().getNorth() == 3.0); + CHECK(l1x1y0z0.getMinimumHeight() == 10.0); + CHECK(l1x1y0z0.getMaximumHeight() == 15.0); + + BoundingRegion l1x0y1z0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 1, 0)); + CHECK(l1x0y1z0.getRectangle().getWest() == 1.0); + CHECK(l1x0y1z0.getRectangle().getSouth() == 3.0); + CHECK(l1x0y1z0.getRectangle().getEast() == 2.0); + CHECK(l1x0y1z0.getRectangle().getNorth() == 4.0); + CHECK(l1x0y1z0.getMinimumHeight() == 10.0); + CHECK(l1x0y1z0.getMaximumHeight() == 15.0); + + BoundingRegion l1x0y0z1 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 1)); + CHECK(l1x0y0z1.getRectangle().getWest() == 1.0); + CHECK(l1x0y0z1.getRectangle().getSouth() == 2.0); + CHECK(l1x0y0z1.getRectangle().getEast() == 2.0); + CHECK(l1x0y0z1.getRectangle().getNorth() == 3.0); + CHECK(l1x0y0z1.getMinimumHeight() == 15.0); + CHECK(l1x0y0z1.getMaximumHeight() == 20.0); + } + } + + SECTION("S2") { + SECTION("quadtree") { + S2CellBoundingVolume root( + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(0, 0, 0)), + 10.0, + 20.0); + + S2CellBoundingVolume l1x0y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 0)); + CHECK(l1x0y0.getCellID().getFace() == 1); + CHECK( + l1x0y0.getCellID().getID() == + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(1, 0, 0)).getID()); + CHECK(l1x0y0.getMinimumHeight() == 10.0); + CHECK(l1x0y0.getMaximumHeight() == 20.0); + + S2CellBoundingVolume l1x1y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 1, 0)); + CHECK(l1x1y0.getCellID().getFace() == 1); + CHECK( + l1x1y0.getCellID().getID() == + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(1, 1, 0)).getID()); + CHECK(l1x1y0.getMinimumHeight() == 10.0); + CHECK(l1x1y0.getMaximumHeight() == 20.0); + + S2CellBoundingVolume l1x0y1 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 1)); + CHECK(l1x0y1.getCellID().getFace() == 1); + CHECK( + l1x0y1.getCellID().getID() == + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(1, 0, 1)).getID()); + CHECK(l1x0y1.getMinimumHeight() == 10.0); + CHECK(l1x0y1.getMaximumHeight() == 20.0); + } + } +} From 3a558f37f58ec188c2a699b311665623259b2e1f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:10:52 +1100 Subject: [PATCH 10/16] Make sure libmorton is treated as a system library. --- Cesium3DTilesContent/CMakeLists.txt | 4 ++++ Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp | 1 - Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 854e36c96..1f0ec1b81 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -60,6 +60,10 @@ target_link_libraries(Cesium3DTilesContent CesiumGltf CesiumGltfReader CesiumUtility +) + +target_link_libraries_system( + Cesium3DTilesContent PRIVATE libmorton ) diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 203654df1..04d0e6589 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 22b7a80b3..e8dadf7df 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include From a91979eb3c28888227e29b7ecbfaa4e64263351b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:16:11 +1100 Subject: [PATCH 11/16] Fix clang warnings. --- Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index e8dfec1ad..62cd74568 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -274,13 +274,13 @@ QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. - this->_current.x = (this->_current.x & ~1) | (value & 1); + this->_current.x = (this->_current.x & ~1UL) | (value & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Y coordinate // and add this shifted value to produce the new Y coordinate. - this->_current.y = (this->_current.y & ~1) + (value >> 1); + this->_current.y = (this->_current.y & ~1UL) + (value >> 1); return *this; } @@ -329,14 +329,14 @@ OctreeChildren::iterator& OctreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. // Bit 1 in the value replaces bit 0 of the Y coordinate. - this->_current.x = (this->_current.x & ~1) | (value & 1); - this->_current.y = (this->_current.y & ~1) | ((value >> 1) & 1); + this->_current.x = (this->_current.x & ~1UL) | (value & 1); + this->_current.y = (this->_current.y & ~1UL) | ((value >> 1) & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Z coordinate // and add this shifted value to produce the new Z coordinate. - this->_current.z = (this->_current.z & ~1) + (value >> 2); + this->_current.z = (this->_current.z & ~1UL) + (value >> 2); return *this; } From 885d472df7f4c26e7de6aa4c020d04e263dbef34 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:19:11 +1100 Subject: [PATCH 12/16] Update CHANGES.md. --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 106d62e3e..317b08d9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,8 @@ - Added `transform` method to `CesiumGeometry::BoundingSphere`. - Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`. - Added `TileTransform` class to `Cesium3DTilesContent`, making it easier to create a `glm::dmat4` from the `transform` property of a `Cesium3DTiles::Tile`. +- Added `ImplicitTilingUtilities` class to `Cesium3DTilesContent`. +- Added overloads of `isTileAvailable` and `isContentAvailable` on the `SubtreeAvailability` class that take the subtree root tile ID and the tile ID of interest, instead of a relative level and Morton index. ##### Fixes :wrench: From 6497d291bf7c328da32b93e26f6d28cafb1e473d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:34:07 +1100 Subject: [PATCH 13/16] Fix GCC warning. --- Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index 62cd74568..5dd19c5b8 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -274,13 +274,14 @@ QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. - this->_current.x = (this->_current.x & ~1UL) | (value & 1); + const uint32_t one = 1; + this->_current.x = (this->_current.x & ~one) | (value & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Y coordinate // and add this shifted value to produce the new Y coordinate. - this->_current.y = (this->_current.y & ~1UL) + (value >> 1); + this->_current.y = (this->_current.y & ~one) + (value >> 1); return *this; } @@ -329,14 +330,15 @@ OctreeChildren::iterator& OctreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. // Bit 1 in the value replaces bit 0 of the Y coordinate. - this->_current.x = (this->_current.x & ~1UL) | (value & 1); - this->_current.y = (this->_current.y & ~1UL) | ((value >> 1) & 1); + const uint32_t one = 1; + this->_current.x = (this->_current.x & ~one) | (value & 1); + this->_current.y = (this->_current.y & ~one) | ((value >> 1) & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Z coordinate // and add this shifted value to produce the new Z coordinate. - this->_current.z = (this->_current.z & ~1UL) + (value >> 2); + this->_current.z = (this->_current.z & ~one) + (value >> 2); return *this; } From ebf44a1fd75a3880ae968dbdd81dcb90845549c6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 12:04:03 -0500 Subject: [PATCH 14/16] Add findTile to TestImplicitQuadtreeLoader --- .../test/TestImplicitQuadtreeLoader.cpp | 102 ++++++++---------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp index 00209e323..5e7b1b145 100644 --- a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp @@ -198,6 +198,31 @@ TEST_CASE("Test implicit quadtree loader") { } } +namespace { + +const Tile& +findTile(const gsl::span& children, const QuadtreeTileID& tileID) { + auto it = std::find_if( + children.begin(), + children.end(), + [tileID](const Tile& tile) { + const QuadtreeTileID* pID = + std::get_if(&tile.getTileID()); + if (!pID) + return false; + return *pID == tileID; + }); + REQUIRE(it != children.end()); + return *it; +} + +const Tile& +findTile(const std::vector& children, const QuadtreeTileID& tileID) { + return findTile(gsl::span(children), tileID); +} + +} // namespace + TEST_CASE("Test tile subdivision for implicit quadtree loader") { Cesium3DTilesContent::registerAllTileContentTypes(); @@ -238,10 +263,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_1_0_0 = tileChildren[0]; - CHECK( - std::get(tile_1_0_0.getTileID()) == - QuadtreeTileID(1, 0, 0)); + const auto& tile_1_0_0 = findTile(tileChildren, QuadtreeTileID(1, 0, 0)); const auto& box_1_0_0 = std::get(tile_1_0_0.getBoundingVolume()); CHECK(box_1_0_0.getCenter() == glm::dvec3(-10.0, -10.0, 0.0)); @@ -249,10 +271,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_1_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_1_1_0 = tileChildren[1]; - CHECK( - std::get(tile_1_1_0.getTileID()) == - QuadtreeTileID(1, 1, 0)); + const auto& tile_1_1_0 = findTile(tileChildren, QuadtreeTileID(1, 1, 0)); const auto& box_1_1_0 = std::get(tile_1_1_0.getBoundingVolume()); CHECK(box_1_1_0.getCenter() == glm::dvec3(10.0, -10.0, 0.0)); @@ -260,10 +279,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_1_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_1_0_1 = tileChildren[2]; - CHECK( - std::get(tile_1_0_1.getTileID()) == - QuadtreeTileID(1, 0, 1)); + const auto& tile_1_0_1 = findTile(tileChildren, QuadtreeTileID(1, 0, 1)); const auto& box_1_0_1 = std::get(tile_1_0_1.getBoundingVolume()); CHECK(box_1_0_1.getCenter() == glm::dvec3(-10.0, 10.0, 0.0)); @@ -271,10 +287,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_1_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_1_1_1 = tileChildren[3]; - CHECK( - std::get(tile_1_1_1.getTileID()) == - QuadtreeTileID(1, 1, 1)); + const auto& tile_1_1_1 = findTile(tileChildren, QuadtreeTileID(1, 1, 1)); const auto& box_1_1_1 = std::get(tile_1_1_1.getBoundingVolume()); CHECK(box_1_1_1.getCenter() == glm::dvec3(10.0, 10.0, 0.0)); @@ -294,10 +307,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_2_2_0 = tileChildren[0]; - CHECK( - std::get(tile_2_2_0.getTileID()) == - QuadtreeTileID(2, 2, 0)); + const auto& tile_2_2_0 = findTile(tileChildren, QuadtreeTileID(2, 2, 0)); const auto& box_2_2_0 = std::get(tile_2_2_0.getBoundingVolume()); CHECK(box_2_2_0.getCenter() == glm::dvec3(5.0, -15.0, 0.0)); @@ -305,10 +315,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_2_2_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_2_3_0 = tileChildren[1]; - CHECK( - std::get(tile_2_3_0.getTileID()) == - QuadtreeTileID(2, 3, 0)); + const auto& tile_2_3_0 = findTile(tileChildren, QuadtreeTileID(2, 3, 0)); const auto& box_2_3_0 = std::get(tile_2_3_0.getBoundingVolume()); CHECK(box_2_3_0.getCenter() == glm::dvec3(15.0, -15.0, 0.0)); @@ -316,10 +323,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_2_3_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_2_2_1 = tileChildren[2]; - CHECK( - std::get(tile_2_2_1.getTileID()) == - QuadtreeTileID(2, 2, 1)); + const auto& tile_2_2_1 = findTile(tileChildren, QuadtreeTileID(2, 2, 1)); const auto& box_2_2_1 = std::get(tile_2_2_1.getBoundingVolume()); CHECK(box_2_2_1.getCenter() == glm::dvec3(5.0, -5.0, 0.0)); @@ -327,10 +331,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_2_2_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_2_3_1 = tileChildren[3]; - CHECK( - std::get(tile_2_3_1.getTileID()) == - QuadtreeTileID(2, 3, 1)); + const auto& tile_2_3_1 = findTile(tileChildren, QuadtreeTileID(2, 3, 1)); const auto& box_2_3_1 = std::get(tile_2_3_1.getBoundingVolume()); CHECK(box_2_3_1.getCenter() == glm::dvec3(15.0, -5.0, 0.0)); @@ -379,7 +380,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_1_0_0 = tileChildren[0]; + const auto& tile_1_0_0 = findTile(tileChildren, QuadtreeTileID(1, 0, 0)); const auto& region_1_0_0 = std::get(tile_1_0_0.getBoundingVolume()); CHECK(region_1_0_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -389,7 +390,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_1_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_0 = tileChildren[1]; + const auto& tile_1_1_0 = findTile(tileChildren, QuadtreeTileID(1, 1, 0)); const auto& region_1_1_0 = std::get(tile_1_1_0.getBoundingVolume()); CHECK(region_1_1_0.getRectangle().getWest() == Approx(0.0)); @@ -399,7 +400,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_1_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_1_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_0_1 = tileChildren[2]; + const auto& tile_1_0_1 = findTile(tileChildren, QuadtreeTileID(1, 0, 1)); const auto& region_1_0_1 = std::get(tile_1_0_1.getBoundingVolume()); CHECK(region_1_0_1.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -409,7 +410,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_1_0_1.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_1 = tileChildren[3]; + const auto& tile_1_1_1 = findTile(tileChildren, QuadtreeTileID(1, 1, 1)); const auto& region_1_1_1 = std::get(tile_1_1_1.getBoundingVolume()); CHECK(region_1_1_1.getRectangle().getWest() == Approx(0.0)); @@ -431,7 +432,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_2_2_0 = tileChildren[0]; + const auto& tile_2_2_0 = findTile(tileChildren, QuadtreeTileID(2, 2, 0)); const auto& region_2_2_0 = std::get(tile_2_2_0.getBoundingVolume()); CHECK(region_2_2_0.getRectangle().getWest() == Approx(0.0)); @@ -442,7 +443,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_2_2_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_2_3_0 = tileChildren[1]; + const auto& tile_2_3_0 = findTile(tileChildren, QuadtreeTileID(2, 3, 0)); const auto& region_2_3_0 = std::get(tile_2_3_0.getBoundingVolume()); CHECK(region_2_3_0.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -453,7 +454,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_2_3_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_3_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_2_2_1 = tileChildren[2]; + const auto& tile_2_2_1 = findTile(tileChildren, QuadtreeTileID(2, 2, 1)); const auto& region_2_2_1 = std::get(tile_2_2_1.getBoundingVolume()); CHECK(region_2_2_1.getRectangle().getWest() == Approx(0.0)); @@ -464,7 +465,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_2_2_1.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_2_3_1 = tileChildren[3]; + const auto& tile_2_3_1 = findTile(tileChildren, QuadtreeTileID(2, 3, 1)); const auto& region_2_3_1 = std::get(tile_2_3_1.getBoundingVolume()); CHECK(region_2_3_1.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -511,34 +512,23 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_1_0_0 = tileChildren[0]; - CHECK( - std::get(tile_1_0_0.getTileID()) == - QuadtreeTileID(1, 0, 0)); + const auto& tile_1_0_0 = findTile(tileChildren, QuadtreeTileID(1, 0, 0)); const auto& box_1_0_0 = std::get(tile_1_0_0.getBoundingVolume()); CHECK(box_1_0_0.getCellID().toToken() == "04"); - const auto& tile_1_1_0 = tileChildren[1]; - CHECK( - std::get(tile_1_1_0.getTileID()) == - QuadtreeTileID(1, 1, 0)); + const auto& tile_1_1_0 = findTile(tileChildren, QuadtreeTileID(1, 1, 0)); const auto& box_1_1_0 = std::get(tile_1_1_0.getBoundingVolume()); CHECK(box_1_1_0.getCellID().toToken() == "1c"); - const auto& tile_1_0_1 = tileChildren[2]; - CHECK( - std::get(tile_1_0_1.getTileID()) == - QuadtreeTileID(1, 0, 1)); + const auto& tile_1_0_1 = findTile(tileChildren, QuadtreeTileID(1, 0, 1)); + ; const auto& box_1_0_1 = std::get(tile_1_0_1.getBoundingVolume()); CHECK(box_1_0_1.getCellID().toToken() == "0c"); - const auto& tile_1_1_1 = tileChildren[3]; - CHECK( - std::get(tile_1_1_1.getTileID()) == - QuadtreeTileID(1, 1, 1)); + const auto& tile_1_1_1 = findTile(tileChildren, QuadtreeTileID(1, 1, 1)); const auto& box_1_1_1 = std::get(tile_1_1_1.getBoundingVolume()); CHECK(box_1_1_1.getCellID().toToken() == "14"); From 96b632b0ef2fc51eadd18927ddc92fe40b4ca160 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 13:37:28 -0500 Subject: [PATCH 15/16] Doc tweaks --- .../ImplicitTilingUtilities.h | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index a71f3e385..16b56ef97 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -288,55 +288,60 @@ class ImplicitTilingUtilities { } /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit quadtree tile with the + * given ID as a bounding region. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The bounding region of the root tile. + * @param tileID The tile ID for which to compute the bounding region. + * @return The bounding region for the given implicit tile. */ static CesiumGeospatial::BoundingRegion computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit octree tile with the + * given ID as a bounding region. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The bounding region of the root tile. + * @param tileID The tile ID for which to compute the bounding region. + * @return The bounding region for the given implicit tile. */ static CesiumGeospatial::BoundingRegion computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::OctreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit quadtree tile + * with the given ID as an oriented bounding box. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The oriented bounding box of the root tile. + * @param tileID The tile ID for which to compute the oriented bounding box. + * @return The oriented bounding box for the given implicit tile. */ static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit octree tile with + * the given ID as an oriented bounding box. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The oriented bounding box of the root tile. + * @param tileID The tile ID for which to compute the oriented bounding box. + * @return The oriented bounding box for the given implicit tile. */ static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, const CesiumGeometry::OctreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit quadtree tile + * with the given ID as an S2 cell bounding volume. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The S2 cell bounding volume of the root tile. + * @param tileID The tile ID for which to compute the S2 cell bounding volume. + * @return The S2 cell bounding volume for the given implicit tile. */ static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, From bf812c9da1e2ae1fb1034d92fc58f36395bfbba8 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 13:55:36 -0500 Subject: [PATCH 16/16] Fix changelog entry --- CHANGES.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f8e8ce675..54dade1a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,9 +22,6 @@ ##### Fixes :wrench: - Fixed a bug in `OrientedBoundingBox::contains` where it didn't account for the bounding box's center. - -##### Fixes :wrench: - - Fixed compiler error when calling `PropertyAttributeView::forEachProperty`. ### v0.29.0 - 2023-11-01