Skip to content

Commit

Permalink
Merge pull request #783 from CesiumGS/height-query
Browse files Browse the repository at this point in the history
Add height-query function to Tileset
  • Loading branch information
j9liu authored Sep 27, 2024
2 parents d7fd72d + b56b1a2 commit f646271
Show file tree
Hide file tree
Showing 17 changed files with 1,225 additions and 150 deletions.
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@

##### Breaking Changes :mega:

- Renamed `shouldContentContinueUpdating` to `getMightHaveLatentChildren` and `setContentShouldContinueUpdating` to `setMightHaveLatentChildren` on the `Tile` class.
- `LoadedRasterOverlayImage` now has a single `errorList` property instead of separate `errors` and `warnings` properties.

##### Additions :tada:

- Added `sampleHeightMostDetailed` method to `Tileset`.
- `AxisAlignedBox` now has `constexpr` constructors.

##### Fixes :wrench:

- Fixed a bug that prevented use of `Tileset` with a nullptr `IPrepareRendererResources`.
- Fixed a bug in `IntersectionTests::rayOBBParametric` that could cause incorrect results for some oriented bounding boxes.
- `GltfUtilities::intersectRayGltfModel` now reports a warning when given a model it can't compute the intersection with because it uses required extensions that are not supported.
- Errors while loading raster overlays are now logged. Previously, they were silently ignored in many cases.
- A raster overlay image failing to load will no longer completely prevent the geometry tile to which it is attached from rendering. Instead, once the raster overlay fails, the geometry tile will be shown without the raster overlay.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <CesiumGeospatial/Cartographic.h>

#include <string>
#include <vector>

namespace Cesium3DTilesSelection {

/**
* @brief The result of sampling heights with
* {@link Tileset::sampleHeightMostDetailed}.
*/
struct SampleHeightResult {
/**
* @brief The positions and their sampled heights.
*
* For each resulting position, its longitude and latitude values will match
* values from its input. Its height will either be the height sampled from
* the tileset at that position, or the original input height if the sample
* was unsuccessful. To determine which, look at the value of
* {@link SampleHeightResult::sampleSuccess} at the same index.
*/
std::vector<CesiumGeospatial::Cartographic> positions;

/**
* @brief The success of each sample.
*
* Each entry specifies whether the height for the position at the
* corresponding index was successfully sampled. If true, then
* {@link SampleHeightResult::positions} has a valid height sampled from the
* tileset at this index. If false, the height could not be sampled, leaving
* the height in {@link SampleHeightResult::positions} unchanged from the
* original input height.
*/
std::vector<bool> sampleSuccess;

/**
* @brief Any warnings that occurred while sampling heights.
*/
std::vector<std::string> warnings;
};

} // namespace Cesium3DTilesSelection
22 changes: 18 additions & 4 deletions Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,24 @@ class CESIUM3DTILESSELECTION_API Tile final {

void setState(TileLoadState state) noexcept;

bool shouldContentContinueUpdating() const noexcept;
/**
* @brief Gets a flag indicating whether this tile might have latent children.
* Latent children don't exist in the `_children` property, but can be created
* by the {@link TilesetContentLoader}.
*
* When true, this tile might have children that can be created by the
* TilesetContentLoader but aren't yet reflected in the `_children` property.
* For example, in implicit tiling, we save memory by only creating explicit
* Tile instances from implicit availability as those instances are needed.
* When this flag is true, the creation of those explicit instances hasn't
* happened yet for this tile.
*
* If this flag is false, the children have already been created, if they
* exist. The tile may still have no children because it is a leaf node.
*/
bool getMightHaveLatentChildren() const noexcept;

void
setContentShouldContinueUpdating(bool shouldContentContinueUpdating) noexcept;
void setMightHaveLatentChildren(bool mightHaveLatentChildren) noexcept;

// Position in bounding-volume hierarchy.
Tile* _pParent;
Expand All @@ -528,7 +542,7 @@ class CESIUM3DTILESSELECTION_API Tile final {
TileContent _content;
TilesetContentLoader* _pLoader;
TileLoadState _loadState;
bool _shouldContentContinueUpdating;
bool _mightHaveLatentChildren;

// mapped raster overlay
std::vector<RasterMappedTo3DTile> _rasterTiles;
Expand Down
28 changes: 28 additions & 0 deletions Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Library.h"
#include "RasterOverlayCollection.h"
#include "SampleHeightResult.h"
#include "Tile.h"
#include "TilesetContentLoader.h"
#include "TilesetExternals.h"
Expand All @@ -15,6 +16,7 @@

#include <rapidjson/fwd.h>

#include <list>
#include <memory>
#include <optional>
#include <string>
Expand All @@ -23,6 +25,8 @@
namespace Cesium3DTilesSelection {
class TilesetContentManager;
class TilesetMetadata;
class TilesetHeightQuery;
class TilesetHeightRequest;

/**
* @brief A <a
Expand Down Expand Up @@ -270,6 +274,27 @@ class CESIUM3DTILESSELECTION_API Tileset final {
*/
CesiumAsync::Future<const TilesetMetadata*> loadMetadata();

/**
* @brief Initiates an asynchronous query for the height of this tileset at a
* list of cartographic positions (longitude and latitude). The most detailed
* available tiles are used to determine each height.
*
* The height of the input positions is ignored. The output height is
* expressed in meters above the ellipsoid (usually WGS84), which should not
* be confused with a height above mean sea level.
*
* Note that {@link Tileset::updateView} must be called periodically, or else
* the returned `Future` will never resolve. If you are not using this tileset
* for visualization, you can call `updateView` with an empty list of
* frustums.
*
* @param positions The positions for which to sample heights.
* @return A future that asynchronously resolves to the result of the height
* query.
*/
CesiumAsync::Future<SampleHeightResult> sampleHeightMostDetailed(
const std::vector<CesiumGeospatial::Cartographic>& positions);

private:
/**
* @brief The result of traversing one branch of the tile hierarchy.
Expand Down Expand Up @@ -495,6 +520,7 @@ class CESIUM3DTILESSELECTION_API Tileset final {

std::vector<TileLoadTask> _mainThreadLoadQueue;
std::vector<TileLoadTask> _workerThreadLoadQueue;
std::vector<Tile*> _heightQueryLoadQueue;

Tile::LoadedLinkedList _loadedTiles;

Expand All @@ -509,6 +535,8 @@ class CESIUM3DTILESSELECTION_API Tileset final {
CesiumUtility::IntrusivePointer<TilesetContentManager>
_pTilesetContentManager;

std::list<TilesetHeightRequest> _heightRequests;

void addTileToLoadQueue(
Tile& tile,
TileLoadPriorityGroup priorityGroup,
Expand Down
16 changes: 8 additions & 8 deletions Cesium3DTilesSelection/src/Tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Tile::Tile(
_content{std::forward<TileContentArgs>(args)...},
_pLoader{pLoader},
_loadState{loadState},
_shouldContentContinueUpdating{true} {}
_mightHaveLatentChildren{true} {}

Tile::Tile(Tile&& rhs) noexcept
: _pParent(rhs._pParent),
Expand All @@ -74,7 +74,7 @@ Tile::Tile(Tile&& rhs) noexcept
_content(std::move(rhs._content)),
_pLoader{rhs._pLoader},
_loadState{rhs._loadState},
_shouldContentContinueUpdating{rhs._shouldContentContinueUpdating} {
_mightHaveLatentChildren{rhs._mightHaveLatentChildren} {
// since children of rhs will have the parent pointed to rhs,
// we will reparent them to this tile as rhs will be destroyed after this
for (Tile& tile : this->_children) {
Expand Down Expand Up @@ -105,7 +105,7 @@ Tile& Tile::operator=(Tile&& rhs) noexcept {
this->_content = std::move(rhs._content);
this->_pLoader = rhs._pLoader;
this->_loadState = rhs._loadState;
this->_shouldContentContinueUpdating = rhs._shouldContentContinueUpdating;
this->_mightHaveLatentChildren = rhs._mightHaveLatentChildren;
}

return *this;
Expand Down Expand Up @@ -227,12 +227,12 @@ void Tile::setParent(Tile* pParent) noexcept { this->_pParent = pParent; }

void Tile::setState(TileLoadState state) noexcept { this->_loadState = state; }

bool Tile::shouldContentContinueUpdating() const noexcept {
return this->_shouldContentContinueUpdating;
bool Tile::getMightHaveLatentChildren() const noexcept {
return this->_mightHaveLatentChildren;
}

void Tile::setContentShouldContinueUpdating(
bool shouldContentContinueUpdating) noexcept {
this->_shouldContentContinueUpdating = shouldContentContinueUpdating;
void Tile::setMightHaveLatentChildren(bool mightHaveLatentChildren) noexcept {
this->_mightHaveLatentChildren = mightHaveLatentChildren;
}

} // namespace Cesium3DTilesSelection
123 changes: 105 additions & 18 deletions Cesium3DTilesSelection/src/Tileset.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "TileUtilities.h"
#include "TilesetContentManager.h"
#include "TilesetHeightQuery.h"

#include <Cesium3DTilesSelection/ITileExcluder.h>
#include <Cesium3DTilesSelection/TileID.h>
Expand Down Expand Up @@ -45,13 +46,18 @@ Tileset::Tileset(
_previousFrameNumber(0),
_distances(),
_childOcclusionProxies(),
_pTilesetContentManager{new TilesetContentManager(
_externals,
_options,
RasterOverlayCollection{_loadedTiles, externals, options.ellipsoid},
std::vector<CesiumAsync::IAssetAccessor::THeader>{},
std::move(pCustomLoader),
std::move(pRootTile))} {}
_pTilesetContentManager{
new TilesetContentManager(
_externals,
_options,
RasterOverlayCollection{
_loadedTiles,
externals,
options.ellipsoid},
std::vector<CesiumAsync::IAssetAccessor::THeader>{},
std::move(pCustomLoader),
std::move(pRootTile)),
} {}

Tileset::Tileset(
const TilesetExternals& externals,
Expand All @@ -63,11 +69,16 @@ Tileset::Tileset(
_previousFrameNumber(0),
_distances(),
_childOcclusionProxies(),
_pTilesetContentManager{new TilesetContentManager(
_externals,
_options,
RasterOverlayCollection{_loadedTiles, externals, options.ellipsoid},
url)} {}
_pTilesetContentManager{
new TilesetContentManager(
_externals,
_options,
RasterOverlayCollection{
_loadedTiles,
externals,
options.ellipsoid},
url),
} {}

Tileset::Tileset(
const TilesetExternals& externals,
Expand All @@ -90,6 +101,10 @@ Tileset::Tileset(
ionAssetEndpointUrl)} {}

Tileset::~Tileset() noexcept {
TilesetHeightRequest::failHeightRequests(
this->_heightRequests,
"Tileset is being destroyed.");

this->_pTilesetContentManager->unloadAll();
if (this->_externals.pTileOcclusionProxyPool) {
this->_externals.pTileOcclusionProxyPool->destroyPool();
Expand Down Expand Up @@ -325,6 +340,15 @@ Tileset::updateView(const std::vector<ViewState>& frustums, float deltaTime) {

Tile* pRootTile = this->getRootTile();
if (!pRootTile) {
// If the root tile is marked as ready, but doesn't actually exist, then
// the tileset couldn't load. Fail any outstanding height requests.
if (!this->_heightRequests.empty() && this->_pTilesetContentManager &&
this->_pTilesetContentManager->getRootTileAvailableEvent().isReady()) {
TilesetHeightRequest::failHeightRequests(
this->_heightRequests,
"Height requests could not complete because the tileset failed to "
"load.");
}
return result;
}

Expand Down Expand Up @@ -358,6 +382,13 @@ Tileset::updateView(const std::vector<ViewState>& frustums, float deltaTime) {
result = ViewUpdateResult();
}

TilesetHeightRequest::processHeightRequests(
*this->_pTilesetContentManager,
this->_options,
this->_loadedTiles,
this->_heightRequests,
this->_heightQueryLoadQueue);

result.workerThreadTileLoadQueueLength =
static_cast<int32_t>(this->_workerThreadLoadQueue.size());
result.mainThreadTileLoadQueueLength =
Expand Down Expand Up @@ -525,6 +556,27 @@ CesiumAsync::Future<const TilesetMetadata*> Tileset::loadMetadata() {
});
}

CesiumAsync::Future<SampleHeightResult>
Tileset::sampleHeightMostDetailed(const std::vector<Cartographic>& positions) {
if (positions.empty()) {
return this->_asyncSystem.createResolvedFuture<SampleHeightResult>({});
}

Promise promise = this->_asyncSystem.createPromise<SampleHeightResult>();

std::vector<TilesetHeightQuery> queries;
queries.reserve(positions.size());

for (const CesiumGeospatial::Cartographic& position : positions) {
queries.emplace_back(position, this->_options.ellipsoid);
}

this->_heightRequests.emplace_back(
TilesetHeightRequest{std::move(queries), promise});

return promise.getFuture();
}

static void markTileNonRendered(
TileSelectionState::Result lastResult,
Tile& tile,
Expand Down Expand Up @@ -1427,17 +1479,52 @@ void Tileset::_processWorkerThreadLoadQueue() {
return;
}

std::vector<TileLoadTask>& queue = this->_workerThreadLoadQueue;
std::sort(queue.begin(), queue.end());
std::sort(
this->_workerThreadLoadQueue.begin(),
this->_workerThreadLoadQueue.end());

// Select tiles alternately from the two queues. Each frame, switch which
// queue we pull the first tile from. The goal is to schedule both height
// query and visualization tile loads fairly.
auto visIt = this->_workerThreadLoadQueue.begin();
auto queryIt = this->_heightQueryLoadQueue.begin();

bool nextIsVis = (this->_previousFrameNumber % 2) == 0;

while (this->_pTilesetContentManager->getNumberOfTilesLoading() <
maximumSimultaneousTileLoads) {
// Tell tiles from the current queue to load until one of them actually
// does. Calling loadTileContent might not actually start the loading
// process
int32_t originalNumberOfTilesLoading =
this->_pTilesetContentManager->getNumberOfTilesLoading();
if (nextIsVis) {
while (visIt != this->_workerThreadLoadQueue.end() &&
originalNumberOfTilesLoading ==
this->_pTilesetContentManager->getNumberOfTilesLoading()) {
this->_pTilesetContentManager->loadTileContent(*visIt->pTile, _options);
++visIt;
}
} else {
while (queryIt != this->_heightQueryLoadQueue.end() &&
originalNumberOfTilesLoading ==
this->_pTilesetContentManager->getNumberOfTilesLoading()) {
this->_pTilesetContentManager->loadTileContent(**queryIt, _options);
++queryIt;
}
}

for (TileLoadTask& task : queue) {
this->_pTilesetContentManager->loadTileContent(*task.pTile, _options);
if (this->_pTilesetContentManager->getNumberOfTilesLoading() >=
maximumSimultaneousTileLoads) {
if (visIt == this->_workerThreadLoadQueue.end() &&
queryIt == this->_heightQueryLoadQueue.end()) {
// No more work in either queue
break;
}

// Get the next tile from the other queue.
nextIsVis = !nextIsVis;
}
}

void Tileset::_processMainThreadLoadQueue() {
CESIUM_TRACE("Tileset::_processMainThreadLoadQueue");
// Process deferred main-thread load tasks with a time budget.
Expand Down
Loading

0 comments on commit f646271

Please sign in to comment.