Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add height-query function to Tileset #783

Merged
merged 82 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
509deaa
update and comment intersection functions
Jan 2, 2024
35eb018
initial commit for height query class
Jan 2, 2024
6c97c53
Merge branch 'gltf-ray-intersect' into height-query
Jan 2, 2024
468595a
clang-format
Jan 2, 2024
4a767e9
add support for add-refine and cwt
Jan 3, 2024
89f5151
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 May 6, 2024
8df6666
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 May 16, 2024
530d4c2
Updates from intersectRayGltfModelParametric changes
csciguy8 May 16, 2024
98dbde9
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 May 24, 2024
053021e
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 May 28, 2024
565c255
Let ::getHeightsAtCoordinates return full Cartographic coordinates, n…
csciguy8 May 28, 2024
3f3944b
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 May 31, 2024
dd6dece
Update to new hit results, and rework height return data to show if t…
csciguy8 May 31, 2024
2f86112
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 Jun 13, 2024
29250be
Changes to support new IntersectResult
csciguy8 Jun 13, 2024
ce119c5
Fix closest hit logix
csciguy8 Jun 13, 2024
ec8f8ec
Modify return of _getHeightsAtCoordinates to support warnings
csciguy8 Jun 20, 2024
2668014
update from base branch
csciguy8 Jul 8, 2024
6bb57cf
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 Jul 8, 2024
1076b4d
fix format
csciguy8 Jul 8, 2024
7e12ede
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 Jul 10, 2024
82f56eb
Rework ::_processHeightRequests to split load work and intersect work
csciguy8 Jul 11, 2024
0d2a165
Don't add to load queue if tile already exists
csciguy8 Jul 11, 2024
5eba1aa
process request tile load all at once, rather than one at a time
csciguy8 Jul 11, 2024
fe35ba3
Fix release build warning
csciguy8 Jul 11, 2024
44b5ea3
fix build warning
csciguy8 Jul 12, 2024
c2b47f0
Reorg to change height finder to terrain query class
csciguy8 Jul 12, 2024
48d1b8b
move include path to fix build error
csciguy8 Jul 12, 2024
9690052
Merge branch 'gltf-ray-intersect' into height-query
csciguy8 Jul 24, 2024
07acc3a
Merge branch 'main' into height-query
csciguy8 Jul 29, 2024
73e213b
Fix compile error
csciguy8 Jul 29, 2024
5f6e9e4
process all height requests on every tick, rather than one at a time
csciguy8 Jul 31, 2024
57cf869
fix warning
csciguy8 Jul 31, 2024
7a1eae8
formatting
csciguy8 Jul 31, 2024
0af2f35
fix bug where height queries could unnecessarily wait unneeded tiles …
csciguy8 Jul 31, 2024
5a0e06e
add support for warnings that occur during tile traversal
csciguy8 Aug 1, 2024
cc8b03f
remove unnecessary check
csciguy8 Aug 1, 2024
7e16171
Merge branch 'main' into height-query
csciguy8 Aug 1, 2024
84493a7
Fix content bounding volume test
csciguy8 Aug 5, 2024
c0f5f18
Merge remote-tracking branch 'origin/main' into height-query
kring Aug 26, 2024
2accb6a
Incremental tree search in tryCompleteHeightRequest.
kring Aug 27, 2024
360154b
Remove use of variable from debug code.
kring Aug 27, 2024
68341f6
Fix an unknown bug in rayOBBParametric by rewriting it.
kring Aug 28, 2024
ef273ac
More performant rayOBBParametric.
kring Aug 28, 2024
06c47d8
Merge remote-tracking branch 'origin/main' into height-query
kring Sep 10, 2024
3be4524
We only need ContentLoaded for height queries.
kring Sep 10, 2024
1f77880
Use addTileToLoadQueue, no need to search existing first.
kring Sep 10, 2024
852710e
Round-robin scheduling of vis and height query loads.
kring Sep 10, 2024
f7450a2
Incremental traversal for additive-refined tiles, too.
kring Sep 10, 2024
5310d25
Remove unused lambda capture.
kring Sep 11, 2024
1f01f5a
Small tweaks and comments.
kring Sep 11, 2024
adea293
visitHeightRequests -> processHeightRequests
kring Sep 11, 2024
f270e4d
Allow height queries on a tileset that hasn't loaded at all yet.
kring Sep 11, 2024
fbb880b
Allow a null IPrepareRendererResources.
kring Sep 11, 2024
0a9e745
Add very basic height query tests.
kring Sep 11, 2024
dcc6c09
Don't do a full search if there are already additive candidates.
kring Sep 11, 2024
349bc22
Add external and implicit tileset tests.
kring Sep 11, 2024
b7a4244
Height queries against implicit tilesets.
kring Sep 11, 2024
d9ff8ac
Report a warning for unsupported extensions.
kring Sep 13, 2024
11d9f8e
Rename TerrainQuery to TilesetHeightQuery.
kring Sep 13, 2024
7c167ee
Minor reorg and documentation.
kring Sep 13, 2024
06d9e79
Move height query functionality out of Tileset.cpp.
kring Sep 13, 2024
51a5322
Cleanup and doc.
kring Sep 15, 2024
ca62417
Keep query tiles loaded until we're done with them.
kring Sep 16, 2024
5753669
Better marking of visited tiles.
kring Sep 16, 2024
3a37ba1
Fix "done loading" criteria.
kring Sep 16, 2024
bc0770a
Update CHANGES, small cleanup.
kring Sep 16, 2024
4850751
Reject height requests when tileset fails to load.
kring Sep 23, 2024
f7a75b1
Merge branch 'main' into height-query
j9liu Sep 25, 2024
7645a91
Remove unnecessary linebreak
j9liu Sep 25, 2024
23308ba
Doc tweaks
j9liu Sep 25, 2024
74acd1a
Additional tweak
j9liu Sep 26, 2024
85226ee
Update comment.
kring Sep 26, 2024
e401f59
Update comment.
kring Sep 26, 2024
352e609
Update doc.
kring Sep 26, 2024
e0323e9
Only create latent children if there are not any children yet.
kring Sep 26, 2024
63d6e4d
heightSampled -> sampleSuccess.
kring Sep 26, 2024
b48893a
tilesNeedingLoading -> tileLoadSet
kring Sep 26, 2024
87a4e68
Update doc.
kring Sep 27, 2024
ad18abf
Add sampleHeightMostDetailed test against a broken tileset.
kring Sep 27, 2024
fb38ac9
Fail height requests when Tileset is destroyed.
kring Sep 27, 2024
b56b1a2
Merge remote-tracking branch 'origin/main' into height-query
kring Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading