初始提交: UE5.3项目基础框架

This commit is contained in:
2025-10-14 11:14:54 +08:00
commit 721d9fd98e
5334 changed files with 316782 additions and 0 deletions

View File

@ -0,0 +1,97 @@
#pragma once
#include "Library.h"
#include <CesiumGeometry/BoundingSphere.h>
#include <CesiumGeometry/OrientedBoundingBox.h>
#include <CesiumGeospatial/BoundingRegion.h>
#include <CesiumGeospatial/BoundingRegionWithLooseFittingHeights.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/S2CellBoundingVolume.h>
#include <optional>
#include <variant>
namespace Cesium3DTilesSelection {
/**
* @brief A bounding volume.
*
* This is a `std::variant` for different types of bounding volumes.
*
* @see CesiumGeometry::BoundingSphere
* @see CesiumGeometry::OrientedBoundingBox
* @see CesiumGeospatial::BoundingRegion
* @see CesiumGeospatial::BoundingRegionWithLooseFittingHeights
* @see CesiumGeospatial::S2CellBoundingVolume
*/
typedef std::variant<
CesiumGeometry::BoundingSphere,
CesiumGeometry::OrientedBoundingBox,
CesiumGeospatial::BoundingRegion,
CesiumGeospatial::BoundingRegionWithLooseFittingHeights,
CesiumGeospatial::S2CellBoundingVolume>
BoundingVolume;
/**
* @brief Transform the given {@link BoundingVolume} with the given matrix.
*
* If the given bounding volume is a {@link CesiumGeometry::BoundingSphere}
* or {@link CesiumGeometry::OrientedBoundingBox}, then it will be transformed
* with the given matrix. Bounding regions will not be transformed.
*
* @param transform The transform matrix.
* @param boundingVolume The bounding volume.
* @return The transformed bounding volume.
*/
CESIUM3DTILESSELECTION_API BoundingVolume transformBoundingVolume(
const glm::dmat4x4& transform,
const BoundingVolume& boundingVolume);
/**
* @brief Returns the center of the given {@link BoundingVolume}.
*
* @param boundingVolume The bounding volume.
* @return The center point.
*/
CESIUM3DTILESSELECTION_API glm::dvec3
getBoundingVolumeCenter(const BoundingVolume& boundingVolume);
/**
* @brief Estimates the bounding {@link CesiumGeospatial::GlobeRectangle} of the
* given {@link BoundingVolume}.
*
* @param boundingVolume The bounding volume.
* @param ellipsoid The ellipsoid to use for globe calculations.
* @return The bounding {@link CesiumGeospatial::GlobeRectangle}.
*/
CESIUM3DTILESSELECTION_API std::optional<CesiumGeospatial::GlobeRectangle>
estimateGlobeRectangle(
const BoundingVolume& boundingVolume,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Returns the bounding region if the bounding volume is a
* {@link CesiumGeospatial::BoundingRegion} or a {@link CesiumGeospatial::BoundingRegionWithLooseFittingHeights}.
*
* @param boundingVolume The bounding volume.
* @return A pointer to the bounding region, or nullptr is the bounding volume
* is not a bounding region.
*/
CESIUM3DTILESSELECTION_API const CesiumGeospatial::BoundingRegion*
getBoundingRegionFromBoundingVolume(const BoundingVolume& boundingVolume);
/**
* @brief Returns an oriented bounding box that contains the given {@link BoundingVolume}.
*
* @param boundingVolume The bounding volume.
* @param ellipsoid The ellipsoid used for this {@link BoundingVolume}.
* @return The oriented bounding box.
*/
CESIUM3DTILESSELECTION_API CesiumGeometry::OrientedBoundingBox
getOrientedBoundingBoxFromBoundingVolume(
const BoundingVolume& boundingVolume,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,48 @@
#pragma once
#include <memory>
#include <string>
namespace Cesium3DTilesSelection {
class Tile;
class Tileset;
/**
* @brief Helps debug the tile selection algorithm by recording the state of
* tiles each frame to a SQLite database.
*/
class DebugTileStateDatabase {
public:
/**
* @brief Creates a new instance.
*
* @param databaseFilename The full path and filename of the output SQLite
* database.
*/
DebugTileStateDatabase(const std::string& databaseFilename);
~DebugTileStateDatabase() noexcept;
/**
* @brief Records the state of all tiles that are currently loaded by the
* given tileset.
*
* @param frameNumber The current frame number.
* @param tileset The tileset.
*/
void recordAllTileStates(int32_t frameNumber, const Tileset& tileset);
/**
* @brief Records the state of a given tile.
*
* @param frameNumber The current frame number.
* @param tile The tile.
*/
void recordTileState(int32_t frameNumber, const Tile& tile);
private:
struct Impl;
std::unique_ptr<Impl> _pImpl;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,67 @@
#pragma once
#include "ITilesetHeightSampler.h"
#include <Cesium3DTilesSelection/Tileset.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>
namespace Cesium3DTilesSelection {
/**
* @brief A loader that will generate a tileset by tesselating the surface of an
* ellipsoid, producing a simple globe tileset without any terrain features.
*/
class EllipsoidTilesetLoader : public TilesetContentLoader,
public ITilesetHeightSampler {
public:
/**
* @brief Constructs a new instance.
*
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
*/
EllipsoidTilesetLoader(
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Creates a new tileset with this loader.
*
* @param externals The external interfaces to use.
* @param options Additional options for the tileset.
*/
static std::unique_ptr<Tileset> createTileset(
const TilesetExternals& externals,
const TilesetOptions& options = TilesetOptions{});
CesiumAsync::Future<TileLoadResult>
loadTileContent(const TileLoadInput& input) override;
TileChildrenResult createTileChildren(
const Tile& tile,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) override;
ITilesetHeightSampler* getHeightSampler() override;
CesiumAsync::Future<SampleHeightResult> sampleHeights(
const CesiumAsync::AsyncSystem& asyncSystem,
std::vector<CesiumGeospatial::Cartographic>&& positions) override;
private:
struct Geometry {
std::vector<uint16_t> indices;
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> normals;
};
void createChildTile(
const Tile& parent,
std::vector<Tile>& children,
const CesiumGeometry::QuadtreeTileID& childID) const;
CesiumGeospatial::BoundingRegion
createBoundingRegion(const CesiumGeometry::QuadtreeTileID& quadtreeID) const;
Geometry createGeometry(const Tile& tile) const;
CesiumGltf::Model createModel(const Geometry& geometry) const;
CesiumGeospatial::GeographicProjection _projection;
CesiumGeometry::QuadtreeTilingScheme _tilingScheme;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,177 @@
#pragma once
#include "Library.h"
#include "TileLoadResult.h"
#include <CesiumAsync/Future.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <glm/vec2.hpp>
#include <any>
#include <span>
namespace CesiumAsync {
class AsyncSystem;
}
namespace CesiumGeometry {
struct Rectangle;
}
namespace CesiumGltf {
struct Model;
} // namespace CesiumGltf
namespace CesiumRasterOverlays {
class RasterOverlayTile;
}
namespace Cesium3DTilesSelection {
class Tile;
/**
* The data of a loaded tile together with a pointer to "render resources" data
* representing the result of \ref
* IPrepareRendererResources::prepareInLoadThread "prepareInLoadThread".
*/
struct TileLoadResultAndRenderResources {
/**
* @brief The \ref TileLoadResult passed to \ref
* IPrepareRendererResources::prepareInLoadThread "prepareInLoadThread" in the
* first place.
*/
TileLoadResult result;
/**
* @brief A pointer to the render resources for this tile.
* Cesium Native doesn't know what this pointer means, and doesn't need to
* know what it means. This pointer is stored in a tile's content as a \ref
* TileRenderContent only so that it can be returned to the implementing
* application as needed and used for rendering there.
*/
void* pRenderResources{nullptr};
};
/**
* @brief When implemented for a rendering engine, allows renderer resources to
* be created and destroyed under the control of a {@link Tileset}.
*
* It is not supposed to be used directly by clients. It is implemented
* for specific rendering engines to provide an infrastructure for preparing the
* data of a {@link Tile} so that it can be used for rendering.
*
* Instances of this class are associated with a {@link Tileset}, in the
* {@link TilesetExternals} structure that is passed to the constructor.
*/
class CESIUM3DTILESSELECTION_API IPrepareRendererResources
: public CesiumRasterOverlays::IPrepareRasterOverlayRendererResources {
public:
virtual ~IPrepareRendererResources() = default;
/**
* @brief Prepares renderer resources for the given tile. This method is
* invoked in the load thread.
*
* @param asyncSystem The AsyncSystem used to do work in threads.
* @param tileLoadResult The tile data loaded so far.
* @param transform The tile's transformation.
* @param rendererOptions Renderer options associated with the tile from
* {@link TilesetOptions::rendererOptions}.
* @returns A future that resolves to the loaded tile data along with
* arbitrary "render resources" data representing the result of the load
* process. The loaded data may be the same as was originally given to this
* method, or it may be modified. The render resources are passed to
* {@link prepareInMainThread} as the `pLoadThreadResult` parameter.
*/
virtual CesiumAsync::Future<TileLoadResultAndRenderResources>
prepareInLoadThread(
const CesiumAsync::AsyncSystem& asyncSystem,
TileLoadResult&& tileLoadResult,
const glm::dmat4& transform,
const std::any& rendererOptions) = 0;
/**
* @brief Further prepares renderer resources.
*
* This is called after {@link prepareInLoadThread}, and unlike that method,
* this one is called from the same thread that called
* {@link Tileset::updateView}.
*
* @param tile The tile to prepare.
* @param pLoadThreadResult The value returned from
* {@link prepareInLoadThread}.
* @returns Arbitrary data representing the result of the load process.
* Note that the value returned by {@link prepareInLoadThread} will _not_ be
* automatically preserved and passed to {@link free}. If you need to free
* that value, do it in this method before returning. If you need that value
* later, add it to the object returned from this method.
*/
virtual void* prepareInMainThread(Tile& tile, void* pLoadThreadResult) = 0;
/**
* @brief Frees previously-prepared renderer resources.
*
* This method is always called from the thread that called
* {@link Tileset::updateView} or deleted the tileset.
*
* @param tile The tile for which to free renderer resources.
* @param pLoadThreadResult The result returned by
* {@link prepareInLoadThread}. If {@link prepareInMainThread} has
* already been called, this parameter will be `nullptr`.
* @param pMainThreadResult The result returned by
* {@link prepareInMainThread}. If {@link prepareInMainThread} has
* not yet been called, this parameter will be `nullptr`.
*/
virtual void free(
Tile& tile,
void* pLoadThreadResult,
void* pMainThreadResult) noexcept = 0;
/**
* @brief Attaches a raster overlay tile to a geometry tile.
*
* @param tile The geometry tile.
* @param overlayTextureCoordinateID The ID of the overlay texture coordinate
* set to use.
* @param rasterTile The raster overlay tile to add. The raster tile will have
* been previously prepared with a call to {@link prepareRasterInLoadThread}
* followed by {@link prepareRasterInMainThread}.
* @param pMainThreadRendererResources The renderer resources for this raster
* tile, as created and returned by {@link prepareRasterInMainThread}.
* @param translation The translation to apply to the texture coordinates
* identified by `overlayTextureCoordinateID`. The texture coordinates to use
* to sample the raster image are computed as `overlayTextureCoordinates *
* scale + translation`.
* @param scale The scale to apply to the texture coordinates identified by
* `overlayTextureCoordinateID`. The texture coordinates to use to sample the
* raster image are computed as `overlayTextureCoordinates * scale +
* translation`.
*/
virtual void attachRasterInMainThread(
const Tile& tile,
int32_t overlayTextureCoordinateID,
const CesiumRasterOverlays::RasterOverlayTile& rasterTile,
void* pMainThreadRendererResources,
const glm::dvec2& translation,
const glm::dvec2& scale) = 0;
/**
* @brief Detaches a raster overlay tile from a geometry tile.
*
* @param tile The geometry tile.
* @param overlayTextureCoordinateID The ID of the overlay texture coordinate
* set to which the raster tile was previously attached.
* @param rasterTile The raster overlay tile to remove.
* @param pMainThreadRendererResources The renderer resources for this raster
* tile, as created and returned by {@link prepareRasterInMainThread}.
*/
virtual void detachRasterInMainThread(
const Tile& tile,
int32_t overlayTextureCoordinateID,
const CesiumRasterOverlays::RasterOverlayTile& rasterTile,
void* pMainThreadRendererResources) noexcept = 0;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,31 @@
#pragma once
namespace Cesium3DTilesSelection {
class Tile;
/**
* @brief An interface that allows tiles to be excluded from loading and
* rendering when provided in {@link TilesetOptions::excluders}.
*/
class ITileExcluder {
public:
virtual ~ITileExcluder() = default;
/**
* @brief Indicates the start of a new frame, initiated with a call to {@link Tileset::updateView}.
*/
virtual void startNewFrame() noexcept {}
/**
* @brief Determines whether a given tile should be excluded.
*
* @param tile The tile to test
* @return true if this tile and all of its descendants in the bounding volume
* hierarchy should be excluded from loading and rendering.
* @return false if this tile should be included.
*/
virtual bool shouldExclude(const Tile& tile) const noexcept = 0;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,36 @@
#pragma once
#include "Library.h"
#include "SampleHeightResult.h"
#include <CesiumAsync/Future.h>
#include <CesiumGeospatial/Cartographic.h>
#include <vector>
namespace CesiumAsync {
class AsyncSystem;
}
namespace Cesium3DTilesSelection {
/**
* @brief An interface to query heights from a tileset that can do so
* efficiently without necessarily downloading individual tiles.
*/
class CESIUM3DTILESSELECTION_API ITilesetHeightSampler {
public:
/**
* @brief Queries the heights at a list of locations.
*
* @param asyncSystem The async system used to do work in threads.
* @param positions The positions at which to query heights. The height field
* of each {@link CesiumGeospatial::Cartographic} is ignored.
* @return A future that will be resolved when the heights have been queried.
*/
virtual CesiumAsync::Future<SampleHeightResult> sampleHeights(
const CesiumAsync::AsyncSystem& asyncSystem,
std::vector<CesiumGeospatial::Cartographic>&& positions) = 0;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,18 @@
#pragma once
/**
* @brief Classes that implement the 3D Tiles standard
*
* @mermaid-interactive{dependencies/Cesium3DTilesSelection}
*/
namespace Cesium3DTilesSelection {}
#if defined(_WIN32) && defined(CESIUM_SHARED)
#ifdef CESIUM3DTILESSELECTION_BUILDING
#define CESIUM3DTILESSELECTION_API __declspec(dllexport)
#else
#define CESIUM3DTILESSELECTION_API __declspec(dllimport)
#endif
#else
#define CESIUM3DTILESSELECTION_API
#endif

View File

@ -0,0 +1,247 @@
#pragma once
#include "IPrepareRendererResources.h"
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <memory>
namespace Cesium3DTilesSelection {
class Tile;
/**
* @brief The result of applying a {@link CesiumRasterOverlays::RasterOverlayTile} to geometry.
*
* Instances of this class are used by a {@link Tile} in order to map
* imagery data that is given as {@link CesiumRasterOverlays::RasterOverlayTile} instances
* to the 2D region that is covered by the tile geometry.
*/
class RasterMappedTo3DTile final {
public:
/**
* @brief The states indicating whether the raster tile is attached to the
* geometry.
*/
enum class AttachmentState {
/**
* @brief This raster tile is not yet attached to the geometry at all.
*/
Unattached = 0,
/**
* @brief This raster tile is attached to the geometry, but it is a
* temporary, low-res version usable while the full-res version is loading.
*/
TemporarilyAttached = 1,
/**
* @brief This raster tile is attached to the geometry.
*/
Attached = 2
};
/**
* @brief Creates a new instance.
*
* @param pRasterTile The {@link CesiumRasterOverlays::RasterOverlayTile} that is mapped to the
* geometry.
* @param textureCoordinateIndex The index of the texture coordinates to use
* with this mapped raster overlay.
*/
RasterMappedTo3DTile(
const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTile>& pRasterTile,
int32_t textureCoordinateIndex);
/**
* @brief Returns a {@link CesiumRasterOverlays::RasterOverlayTile} that is currently loading.
*
* The caller has to check the exact state of this tile, using
* {@link Tile::getState}.
*
* @return The tile that is loading, or `nullptr`.
*/
CesiumRasterOverlays::RasterOverlayTile* getLoadingTile() noexcept {
return this->_pLoadingTile.get();
}
/** @copydoc getLoadingTile */
const CesiumRasterOverlays::RasterOverlayTile*
getLoadingTile() const noexcept {
return this->_pLoadingTile.get();
}
/**
* @brief Returns the {@link CesiumRasterOverlays::RasterOverlayTile} that represents the imagery
* data that is ready to render.
*
* This will be `nullptr` when the tile data has not yet been loaded.
*
* @return The tile, or `nullptr`.
*/
CesiumRasterOverlays::RasterOverlayTile* getReadyTile() noexcept {
return this->_pReadyTile.get();
}
/** @copydoc getReadyTile */
const CesiumRasterOverlays::RasterOverlayTile* getReadyTile() const noexcept {
return this->_pReadyTile.get();
}
/**
* @brief Returns an identifier for the texture coordinates of this tile.
*
* The texture coordinates for this raster are found in the glTF as an
* attribute named `_CESIUMOVERLAY_n` where `n` is this value.
*
* @return The texture coordinate ID.
*/
int32_t getTextureCoordinateID() const noexcept {
return this->_textureCoordinateID;
}
/**
* @brief Sets the texture coordinate ID.
*
* @see getTextureCoordinateID
*
* @param textureCoordinateID The ID.
*/
void setTextureCoordinateID(int32_t textureCoordinateID) noexcept {
this->_textureCoordinateID = textureCoordinateID;
}
/**
* @brief Returns the translation that converts between the geometry texture
* coordinates and the texture coordinates that should be used to sample this
* raster texture.
*
* `rasterCoordinates = geometryCoordinates * scale + translation`
*
* @returns The translation.
*/
const glm::dvec2& getTranslation() const noexcept {
return this->_translation;
}
/**
* @brief Returns the scaling that converts between the geometry texture
* coordinates and the texture coordinates that should be used to sample this
* raster texture.
*
* @see getTranslation
*
* @returns The scaling.
*/
const glm::dvec2& getScale() const noexcept { return this->_scale; }
/**
* @brief Indicates whether this overlay tile is currently attached to its
* owning geometry tile.
*
* When a raster overlay tile is attached to a geometry tile,
* {@link IPrepareRendererResources::attachRasterInMainThread} is invoked.
* When it is detached,
* {@link IPrepareRendererResources::detachRasterInMainThread} is invoked.
*/
AttachmentState getState() const noexcept { return this->_state; }
/**
* @brief Update this tile during the update of its owner.
*
* This is only supposed to be called by
* `TilesetContentManager::updateDoneState`. It will return whether there is a
* more detailed version of the raster data available.
*
* @param prepareRendererResources The {@link IPrepareRendererResources} used to
* create render resources for raster overlay
* @param tile The owner tile.
* @return The {@link CesiumRasterOverlays::RasterOverlayTile::MoreDetailAvailable} state.
*/
CesiumRasterOverlays::RasterOverlayTile::MoreDetailAvailable
update(IPrepareRendererResources& prepareRendererResources, Tile& tile);
/**
* @copydoc CesiumRasterOverlays::RasterOverlayTile::isMoreDetailAvailable
*/
bool isMoreDetailAvailable() const noexcept;
/**
* @brief Detach the raster from the given tile.
* @param prepareRendererResources The IPrepareRendererResources used to
* detach raster overlay from the tile geometry
* @param tile The owner tile.
*/
void detachFromTile(
IPrepareRendererResources& prepareRendererResources,
Tile& tile) noexcept;
/**
* @brief Does a throttled load of the mapped {@link CesiumRasterOverlays::RasterOverlayTile}.
*
* @return If the mapped tile is already in the process of loading or it has
* already finished loading, this method does nothing and returns true. If too
* many loads are already in progress, this method does nothing and returns
* false. Otherwise, it begins the asynchronous process to load the tile and
* returns true.
*/
bool loadThrottled() noexcept;
/**
* @brief Creates a maping between a {@link CesiumRasterOverlays::RasterOverlay} and a {@link Tile}.
*
* The returned mapping will be to a placeholder {@link CesiumRasterOverlays::RasterOverlayTile} if
* the overlay's tile provider is not yet ready (i.e. it's still a
* placeholder) or if the overlap between the tile and the raster overlay
* cannot yet be determined because the projected rectangle of the tile is not
* yet known.
*
* Returns a pointer to the created `RasterMappedTo3DTile` in the Tile's
* {@link Tile::getMappedRasterTiles} collection. Note that this pointer may
* become invalid as soon as another item is added to or removed from this
* collection.
*
* @param maximumScreenSpaceError The maximum screen space error that is used
* for the current tile
* @param tileProvider The overlay tile provider to map to the tile. This may
* be a placeholder if the tile provider is not yet ready.
* @param placeholder The placeholder tile provider for this overlay. This is
* always a placeholder, even if the tile provider is already ready.
* @param tile The tile to which to map the overlay.
* @param missingProjections The list of projections for which there are not
* yet any texture coordiantes. On return, the given overlay's Projection may
* be added to this collection if the Tile does not yet have texture
* coordinates for the Projection and the Projection is not already in the
* collection.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
* @return A pointer the created mapping, which may be to a placeholder, or
* nullptr if no mapping was created at all because the Tile does not overlap
* the raster overlay.
*/
static RasterMappedTo3DTile* mapOverlayToTile(
double maximumScreenSpaceError,
CesiumRasterOverlays::RasterOverlayTileProvider& tileProvider,
CesiumRasterOverlays::RasterOverlayTileProvider& placeholder,
Tile& tile,
std::vector<CesiumGeospatial::Projection>& missingProjections,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
private:
void computeTranslationAndScale(const Tile& tile);
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlayTile>
_pLoadingTile;
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlayTile>
_pReadyTile;
int32_t _textureCoordinateID;
glm::dvec2 _translation;
glm::dvec2 _scale;
AttachmentState _state;
bool _originalFailed;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,217 @@
#pragma once
#include "Library.h"
#include "TilesetExternals.h"
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCounted.h>
#include <CesiumUtility/Tracing.h>
#include <memory>
#include <span>
#include <vector>
namespace Cesium3DTilesSelection {
/**
* @brief A collection of {@link CesiumRasterOverlays::RasterOverlay} instances that are associated
* with a {@link Tileset}.
*
* The raster overlay instances may be added to the raster overlay collection
* of a tileset that is returned with {@link Tileset::getOverlays}. When the
* tileset is loaded, one {@link CesiumRasterOverlays::RasterOverlayTileProvider} will be created
* for each raster overlay that had been added. The raster overlay tile provider
* instances will be passed to the {@link CesiumRasterOverlays::RasterOverlayTile} instances that
* they create when the tiles are updated.
*/
class CESIUM3DTILESSELECTION_API RasterOverlayCollection final {
public:
/**
* @brief Creates a new instance.
*
* @param loadedTiles The list of loaded tiles. The collection does not own
* this list, so the list needs to be kept alive as long as the collection's
* lifetime
* @param externals A collection of loading system to load a raster overlay
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
*/
RasterOverlayCollection(
Tile::LoadedLinkedList& loadedTiles,
const TilesetExternals& externals,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) noexcept;
/**
* @brief Deleted Copy constructor.
*
* @param rhs The other instance.
*/
RasterOverlayCollection(const RasterOverlayCollection& rhs) = delete;
/**
* @brief Move constructor.
*
* @param rhs The other instance.
*/
RasterOverlayCollection(RasterOverlayCollection&& rhs) noexcept = default;
/**
* @brief Deleted copy assignment.
*
* @param rhs The other instance.
*/
RasterOverlayCollection&
operator=(const RasterOverlayCollection& rhs) = delete;
/**
* @brief Move assignment.
*
* @param rhs The other instance.
*/
RasterOverlayCollection&
operator=(RasterOverlayCollection&& rhs) noexcept = default;
~RasterOverlayCollection() noexcept;
/**
* @brief Adds the given {@link CesiumRasterOverlays::RasterOverlay} to this collection.
*
* @param pOverlay The pointer to the overlay. This may not be `nullptr`.
*/
void add(const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlay>& pOverlay);
/**
* @brief Remove the given {@link CesiumRasterOverlays::RasterOverlay} from this collection.
*/
void remove(const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlay>& pOverlay) noexcept;
/**
* @brief Gets the overlays in this collection.
*/
const std::vector<
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlay>>&
getOverlays() const;
/**
* @brief Gets the tile providers in this collection. Each tile provider
* corresponds with the overlay at the same position in the collection
* returned by {@link getOverlays}.
*/
const std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>&
getTileProviders() const;
/**
* @brief Gets the placeholder tile providers in this collection. Each
* placeholder tile provider corresponds with the overlay at the same position
* in the collection returned by {@link getOverlays}.
*/
const std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>&
getPlaceholderTileProviders() const;
/**
* @brief Finds the tile provider for a given overlay.
*
* If the specified raster overlay is not part of this collection, this method
* will return nullptr.
*
* If the overlay's real tile provider hasn't finished being
* created yet, a placeholder will be returned. That is, its
* {@link CesiumRasterOverlays::RasterOverlayTileProvider::isPlaceholder} method will return true.
*
* @param overlay The overlay for which to obtain the tile provider.
* @return The tile provider, if any, corresponding to the raster overlay.
*/
CesiumRasterOverlays::RasterOverlayTileProvider* findTileProviderForOverlay(
CesiumRasterOverlays::RasterOverlay& overlay) noexcept;
/**
* @copydoc findTileProviderForOverlay
*/
const CesiumRasterOverlays::RasterOverlayTileProvider*
findTileProviderForOverlay(
const CesiumRasterOverlays::RasterOverlay& overlay) const noexcept;
/**
* @brief Finds the placeholder tile provider for a given overlay.
*
* If the specified raster overlay is not part of this collection, this method
* will return nullptr.
*
* This method will return the placeholder tile provider even if the real one
* has been created. This is useful to create placeholder tiles when the
* rectangle in the overlay's projection is not yet known.
*
* @param overlay The overlay for which to obtain the tile provider.
* @return The placeholder tile provider, if any, corresponding to the raster
* overlay.
*/
CesiumRasterOverlays::RasterOverlayTileProvider*
findPlaceholderTileProviderForOverlay(
CesiumRasterOverlays::RasterOverlay& overlay) noexcept;
/**
* @copydoc findPlaceholderTileProviderForOverlay
*/
const CesiumRasterOverlays::RasterOverlayTileProvider*
findPlaceholderTileProviderForOverlay(
const CesiumRasterOverlays::RasterOverlay& overlay) const noexcept;
/**
* @brief A constant iterator for {@link CesiumRasterOverlays::RasterOverlay} instances.
*/
typedef std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlay>>::const_iterator const_iterator;
/**
* @brief Returns an iterator at the beginning of this collection.
*/
const_iterator begin() const noexcept;
/**
* @brief Returns an iterator at the end of this collection.
*/
const_iterator end() const noexcept;
/**
* @brief Gets the number of overlays in the collection.
*/
size_t size() const noexcept;
private:
// We store the list of overlays and tile providers in this separate class
// so that we can separate its lifetime from the lifetime of the
// RasterOverlayCollection. We need to do this because the async operations
// that create tile providers from overlays need to have somewhere to write
// the result. And we can't extend the lifetime of the entire
// RasterOverlayCollection until the async operations complete because the
// RasterOverlayCollection has a pointer to the tile LoadedLinkedList, which
// is owned externally and may become invalid before the async operations
// complete.
struct OverlayList
: public CesiumUtility::ReferenceCountedNonThreadSafe<OverlayList> {
std::vector<
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlay>>
overlays{};
std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>
tileProviders{};
std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>
placeholders{};
};
Tile::LoadedLinkedList* _pLoadedTiles;
TilesetExternals _externals;
CesiumGeospatial::Ellipsoid _ellipsoid;
CesiumUtility::IntrusivePointer<OverlayList> _pOverlays;
CESIUM_TRACE_DECLARE_TRACK_SET(_loadingSlots, "Raster Overlay Loading Slot")
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,54 @@
#pragma once
#include "ITileExcluder.h"
#include "Library.h"
#include <CesiumUtility/IntrusivePointer.h>
namespace CesiumRasterOverlays {
class RasterizedPolygonsOverlay;
}
namespace Cesium3DTilesSelection {
/**
* @brief When provided to {@link TilesetOptions::excluders}, uses the polygons
* owned by a {@link CesiumRasterOverlays::RasterizedPolygonsOverlay} to exclude tiles that are
* entirely inside any of the polygon from loading. This is useful when the
* polygons will be used for clipping.
*/
class CESIUM3DTILESSELECTION_API RasterizedPolygonsTileExcluder
: public Cesium3DTilesSelection::ITileExcluder {
public:
/**
* @brief Constructs a new instance.
*
* @param pOverlay The overlay definining the polygons.
*/
RasterizedPolygonsTileExcluder(
const CesiumUtility::IntrusivePointer<
const CesiumRasterOverlays::RasterizedPolygonsOverlay>&
pOverlay) noexcept;
/**
* @brief Determines whether a given tile is entirely inside a polygon and
* therefore should be excluded.
*
* @param tile The tile to check.
* @return true if the tile should be excluded because it is entirely inside a
* polygon.
*/
virtual bool shouldExclude(const Tile& tile) const noexcept override;
/**
* @brief Gets the overlay defining the polygons.
*/
const CesiumRasterOverlays::RasterizedPolygonsOverlay& getOverlay() const;
private:
CesiumUtility::IntrusivePointer<
const CesiumRasterOverlays::RasterizedPolygonsOverlay>
_pOverlay;
};
} // namespace Cesium3DTilesSelection

View File

@ -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

View File

@ -0,0 +1,563 @@
#pragma once
#include "BoundingVolume.h"
#include "Library.h"
#include "RasterMappedTo3DTile.h"
#include "TileContent.h"
#include "TileID.h"
#include "TileRefine.h"
#include "TileSelectionState.h"
#include <CesiumUtility/DoublyLinkedList.h>
#include <glm/common.hpp>
#include <atomic>
#include <limits>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <vector>
namespace Cesium3DTilesSelection {
class TilesetContentLoader;
/**
* The current state of this tile in the loading process.
*/
enum class TileLoadState {
/**
* @brief This tile is in the process of being unloaded, but could not be
* fully unloaded because an asynchronous process is using its loaded data.
*/
Unloading = -2,
/**
* @brief Something went wrong while loading this tile, but it may be a
* temporary problem.
*/
FailedTemporarily = -1,
/**
* @brief The tile is not yet loaded at all, beyond the metadata in
* tileset.json.
*/
Unloaded = 0,
/**
* @brief The tile content is currently being loaded.
*
* Note that while a tile is in this state, its {@link Tile::getContent},
* and {@link Tile::getState}, methods may be called from the load thread,
* and the state may change due to the internal loading process.
*/
ContentLoading = 1,
/**
* @brief The tile content has finished loading.
*/
ContentLoaded = 2,
/**
* @brief The tile is completely done loading.
*/
Done = 3,
/**
* @brief Something went wrong while loading this tile and it will not be
* retried.
*/
Failed = 4,
};
/**
* @brief A tile in a {@link Tileset}.
*
* The tiles of a tileset form a hierarchy, where each tile may contain
* renderable content, and each tile has an associated bounding volume.
*
* The actual hierarchy is represented with the {@link Tile::getParent}
* and {@link Tile::getChildren} functions.
*
* The renderable content is provided as a {@link TileContent}
* from the {@link Tile::getContent} function.
* The {@link Tile::getGeometricError} function returns the geometric
* error of the representation of the renderable content of a tile.
*
* The {@link BoundingVolume} is given by the {@link Tile::getBoundingVolume}
* function. This bounding volume encloses the renderable content of the
* tile itself, as well as the renderable content of all children, yielding
* a spatially coherent hierarchy of bounding volumes.
*
* The bounding volume of the content of an individual tile is given
* by the {@link Tile::getContentBoundingVolume} function.
*
*/
class CESIUM3DTILESSELECTION_API Tile final {
public:
/**
* @brief Construct a tile with unknown content and a loader that is used to
* load the content of this tile. Tile has Unloaded status when initializing
* with this constructor.
*
* @param pLoader The {@link TilesetContentLoader} that is used to load the tile.
*/
explicit Tile(TilesetContentLoader* pLoader) noexcept;
/**
* @brief Construct a tile with an external content and a loader that is
* associated with this tile. Tile has ContentLoaded status when initializing
* with this constructor.
*
* @param pLoader The {@link TilesetContentLoader} that is assiocated with this tile.
* @param externalContent External content that is associated with this tile.
*/
Tile(
TilesetContentLoader* pLoader,
std::unique_ptr<TileExternalContent>&& externalContent) noexcept;
/**
* @brief Construct a tile with an empty content and a loader that is
* associated with this tile. Tile has ContentLoaded status when initializing
* with this constructor.
*
* @param pLoader The {@link TilesetContentLoader} that is assiocated with this tile.
* @param emptyContent A content tag indicating that the tile has no content.
*/
Tile(TilesetContentLoader* pLoader, TileEmptyContent emptyContent) noexcept;
/**
* @brief Default destructor, which clears all resources associated with this
* tile.
*/
~Tile() noexcept = default;
/**
* @brief Copy constructor.
*
* @param rhs The other instance.
*/
Tile(const Tile& rhs) = delete;
/**
* @brief Move constructor.
*
* @param rhs The other instance.
*/
Tile(Tile&& rhs) noexcept;
/**
* @brief Copy constructor.
*
* @param rhs The other instance.
*/
Tile& operator=(const Tile& rhs) = delete;
/**
* @brief Move assignment operator.
*
* @param rhs The other instance.
*/
Tile& operator=(Tile&& rhs) noexcept;
/**
* @brief Returns the parent of this tile in the tile hierarchy.
*
* This will be the `nullptr` if this is the root tile.
*
* @return The parent.
*/
Tile* getParent() noexcept { return this->_pParent; }
/** @copydoc Tile::getParent() */
const Tile* getParent() const noexcept { return this->_pParent; }
/**
* @brief Returns a *view* on the children of this tile.
*
* The returned span will become invalid when this tile is destroyed.
*
* @return The children of this tile.
*/
std::span<Tile> getChildren() noexcept {
return std::span<Tile>(this->_children);
}
/** @copydoc Tile::getChildren() */
std::span<const Tile> getChildren() const noexcept {
return std::span<const Tile>(this->_children);
}
/**
* @brief Assigns the given child tiles to this tile.
*
* This function is not supposed to be called by clients.
*
* @param children The child tiles.
* @throws `std::runtime_error` if this tile already has children.
*/
void createChildTiles(std::vector<Tile>&& children);
/**
* @brief Returns the {@link BoundingVolume} of this tile.
*
* This is a bounding volume that encloses the content of this tile,
* as well as the content of all child tiles.
*
* @see Tile::getContentBoundingVolume
*
* @return The bounding volume.
*/
const BoundingVolume& getBoundingVolume() const noexcept {
return this->_boundingVolume;
}
/**
* @brief Set the {@link BoundingVolume} of this tile.
*
* This function is not supposed to be called by clients.
*
* @param value The bounding volume.
*/
void setBoundingVolume(const BoundingVolume& value) noexcept {
this->_boundingVolume = value;
}
/**
* @brief Returns the viewer request volume of this tile.
*
* The viewer request volume is an optional {@link BoundingVolume} that
* may be associated with a tile. It allows controlling the rendering
* process of the tile content: If the viewer request volume is present,
* then the content of the tile will only be rendered when the viewer
* (i.e. the camera position) is inside the viewer request volume.
*
* @return The viewer request volume, or an empty optional.
*/
const std::optional<BoundingVolume>& getViewerRequestVolume() const noexcept {
return this->_viewerRequestVolume;
}
/**
* @brief Set the viewer request volume of this tile.
*
* This function is not supposed to be called by clients.
*
* @param value The viewer request volume.
*/
void
setViewerRequestVolume(const std::optional<BoundingVolume>& value) noexcept {
this->_viewerRequestVolume = value;
}
/**
* @brief Returns the geometric error of this tile.
*
* This is the error, in meters, introduced if this tile is rendered and its
* children are not. This is used to compute screen space error, i.e., the
* error measured in pixels.
*
* @return The geometric error of this tile, in meters.
*/
double getGeometricError() const noexcept { return this->_geometricError; }
/**
* @brief Set the geometric error of the contents of this tile.
*
* This function is not supposed to be called by clients.
*
* @param value The geometric error, in meters.
*/
void setGeometricError(double value) noexcept {
this->_geometricError = value;
}
/**
* @brief Gets the tile's geometric error as if by calling
* {@link getGeometricError}, except that if the error is smaller than
* {@link CesiumUtility::Math::Epsilon5} the returned geometric error is instead computed as
* half of the parent tile's (non-zero) geometric error.
*
* This is useful for determining when to refine what would ordinarily be a
* leaf tile, for example to attach more detailed raster overlays to it.
*
* If this tile and all of its ancestors have a geometric error less than
* {@link CesiumUtility::Math::Epsilon5}, returns {@link CesiumUtility::Math::Epsilon5}.
*
* @return The non-zero geometric error.
*/
double getNonZeroGeometricError() const noexcept;
/**
* @brief Returns whether to unconditionally refine this tile.
*
* This is useful in cases such as with external tilesets, where instead of a
* tile having any content, it points to an external tileset's root. So the
* tile always needs to be refined otherwise the external tileset will not be
* displayed.
*
* @return Whether to uncoditionally refine this tile.
*/
bool getUnconditionallyRefine() const noexcept {
return glm::isinf(this->_geometricError);
}
/**
* @brief Marks that this tile should be unconditionally refined.
*
* This function is not supposed to be called by clients.
*/
void setUnconditionallyRefine() noexcept {
this->_geometricError = std::numeric_limits<double>::infinity();
}
/**
* @brief The refinement strategy of this tile.
*
* Returns the {@link TileRefine} value that indicates the refinement strategy
* for this tile. This is `Add` when the content of the
* child tiles is *added* to the content of this tile during refinement, and
* `Replace` when the content of the child tiles *replaces*
* the content of this tile during refinement.
*
* @return The refinement strategy.
*/
TileRefine getRefine() const noexcept { return this->_refine; }
/**
* @brief Set the refinement strategy of this tile.
*
* This function is not supposed to be called by clients.
*
* @param value The refinement strategy.
*/
void setRefine(TileRefine value) noexcept { this->_refine = value; }
/**
* @brief Gets the transformation matrix for this tile.
*
* This matrix does _not_ need to be multiplied with the tile's parent's
* transform as this has already been done.
*
* @return The transform matrix.
*/
const glm::dmat4x4& getTransform() const noexcept { return this->_transform; }
/**
* @brief Set the transformation matrix for this tile.
*
* This function is not supposed to be called by clients.
*
* @param value The transform matrix.
*/
void setTransform(const glm::dmat4x4& value) noexcept {
this->_transform = value;
}
/**
* @brief Returns the {@link TileID} of this tile.
*
* This function is not supposed to be called by clients.
*
* @return The tile ID.
*/
const TileID& getTileID() const noexcept { return this->_id; }
/**
* @brief Set the {@link TileID} of this tile.
*
* This function is not supposed to be called by clients.
*
* @param id The tile ID.
*/
void setTileID(const TileID& id) noexcept { this->_id = id; }
/**
* @brief Returns the {@link BoundingVolume} of the renderable content of this
* tile.
*
* The content bounding volume is a bounding volume that tightly fits only the
* renderable content of the tile. This enables tighter view frustum culling,
* making it possible to exclude from rendering any content not in the view
* frustum.
*
* @see Tile::getBoundingVolume
*/
const std::optional<BoundingVolume>&
getContentBoundingVolume() const noexcept {
return this->_contentBoundingVolume;
}
/**
* @brief Set the {@link BoundingVolume} of the renderable content of this
* tile.
*
* This function is not supposed to be called by clients.
*
* @param value The content bounding volume
*/
void setContentBoundingVolume(
const std::optional<BoundingVolume>& value) noexcept {
this->_contentBoundingVolume = value;
}
/**
* @brief Returns the {@link TileSelectionState} of this tile.
*
* This function is not supposed to be called by clients.
*
* @return The last selection state
*/
TileSelectionState& getLastSelectionState() noexcept {
return this->_lastSelectionState;
}
/** @copydoc Tile::getLastSelectionState() */
const TileSelectionState& getLastSelectionState() const noexcept {
return this->_lastSelectionState;
}
/**
* @brief Set the {@link TileSelectionState} of this tile.
*
* This function is not supposed to be called by clients.
*
* @param newState The new stace
*/
void setLastSelectionState(const TileSelectionState& newState) noexcept {
this->_lastSelectionState = newState;
}
/**
* @brief Determines the number of bytes in this tile's geometry and texture
* data.
*/
int64_t computeByteSize() const noexcept;
/**
* @brief Returns the raster overlay tiles that have been mapped to this tile.
*/
std::vector<RasterMappedTo3DTile>& getMappedRasterTiles() noexcept {
return this->_rasterTiles;
}
/** @copydoc Tile::getMappedRasterTiles() */
const std::vector<RasterMappedTo3DTile>&
getMappedRasterTiles() const noexcept {
return this->_rasterTiles;
}
/**
* @brief Get the content of the tile.
*/
const TileContent& getContent() const noexcept { return _content; }
/** @copydoc Tile::getContent() const */
TileContent& getContent() noexcept { return _content; }
/**
* @brief Determines if this tile is currently renderable.
*/
bool isRenderable() const noexcept;
/**
* @brief Determines if this tile has mesh content.
*/
bool isRenderContent() const noexcept;
/**
* @brief Determines if this tile has external tileset content.
*/
bool isExternalContent() const noexcept;
/**
* @brief Determines if this tile has empty content.
*/
bool isEmptyContent() const noexcept;
/**
* @brief get the loader that is used to load the tile content.
*/
TilesetContentLoader* getLoader() const noexcept;
/**
* @brief Returns the {@link TileLoadState} of this tile.
*/
TileLoadState getState() const noexcept;
private:
struct TileConstructorImpl {};
template <
typename... TileContentArgs,
typename TileContentEnable = std::enable_if_t<
std::is_constructible_v<TileContent, TileContentArgs&&...>,
int>>
Tile(
TileConstructorImpl tag,
TileLoadState loadState,
TilesetContentLoader* pLoader,
TileContentArgs&&... args);
void setParent(Tile* pParent) noexcept;
void setState(TileLoadState state) 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 setMightHaveLatentChildren(bool mightHaveLatentChildren) noexcept;
// Position in bounding-volume hierarchy.
Tile* _pParent;
std::vector<Tile> _children;
// Properties from tileset.json.
// These are immutable after the tile leaves TileState::Unloaded.
TileID _id;
BoundingVolume _boundingVolume;
std::optional<BoundingVolume> _viewerRequestVolume;
std::optional<BoundingVolume> _contentBoundingVolume;
double _geometricError;
TileRefine _refine;
glm::dmat4x4 _transform;
// Selection state
TileSelectionState _lastSelectionState;
// tile content
CesiumUtility::DoublyLinkedListPointers<Tile> _loadedTilesLinks;
TileContent _content;
TilesetContentLoader* _pLoader;
TileLoadState _loadState;
bool _mightHaveLatentChildren;
// mapped raster overlay
std::vector<RasterMappedTo3DTile> _rasterTiles;
friend class TilesetContentManager;
friend class MockTilesetContentManagerTestFixture;
public:
/**
* @brief A {@link CesiumUtility::DoublyLinkedList} for tile objects.
*/
typedef CesiumUtility::DoublyLinkedList<Tile, &Tile::_loadedTilesLinks>
LoadedLinkedList;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,306 @@
#pragma once
#include "Library.h"
#include "TilesetMetadata.h"
#include <CesiumGeospatial/Projection.h>
#include <CesiumGltf/Model.h>
#include <CesiumRasterOverlays/RasterOverlayDetails.h>
#include <CesiumUtility/CreditSystem.h>
#include <memory>
#include <variant>
#include <vector>
namespace Cesium3DTilesSelection {
/**
* @brief A content tag that indicates the {@link TilesetContentLoader} does not
* know if a tile's content will point to a mesh content or an external
* tileset. The content of the tile is only known when the loader loads the tile
* to inspect the content.
*/
struct CESIUM3DTILESSELECTION_API TileUnknownContent {};
/**
* @brief A content tag that indicates a tile has no content.
*
* There are two possible ways to handle a tile with no content:
*
* 1. Treat it as a placeholder used for more efficient culling, but
* never render it. Refining to this tile is equivalent to refining
* to its children.
* 2. Treat it as an indication that nothing need be rendered in this
* area at this level-of-detail. In other words, "render" it as a
* hole. To have this behavior, the tile should _not_ have content at
* all.
*
* We distinguish whether the tileset creator wanted (1) or (2) by
* comparing this tile's geometricError to the geometricError of its
* parent tile. If this tile's error is greater than or equal to its
* parent, treat it as (1). If it's less, treat it as (2).
*
* For a tile with no parent there's no difference between the
* behaviors.
*/
struct CESIUM3DTILESSELECTION_API TileEmptyContent {};
/**
* @brief A content tag that indicates a tile content points to an
* external tileset. When this tile is loaded, all the tiles in the
* external tileset will become children of this external content tile
*/
struct CESIUM3DTILESSELECTION_API TileExternalContent {
/**
* @brief The metadata associated with this tileset.
*/
TilesetMetadata metadata;
};
/**
* @brief A content tag that indicates a tile has a glTF model content and
* render resources for the model
*/
class CESIUM3DTILESSELECTION_API TileRenderContent {
public:
/**
* @brief Construct the content with a glTF model
*
* @param model A glTF model that will be owned by this content
*/
TileRenderContent(CesiumGltf::Model&& model);
/**
* @brief Retrieve a glTF model that is owned by this content
*
* @return A glTF model that is owned by this content
*/
const CesiumGltf::Model& getModel() const noexcept;
/**
* @brief Retrieve a glTF model that is owned by this content
*
* @return A glTF model that is owned by this content
*/
CesiumGltf::Model& getModel() noexcept;
/**
* @brief Set the glTF model for this content
*
* @param model A glTF model that will be owned by this content
*/
void setModel(const CesiumGltf::Model& model);
/**
* @brief Set the glTF model for this content
*
* @param model A glTF model that will be owned by this content
*/
void setModel(CesiumGltf::Model&& model);
/**
* @brief Get the {@link CesiumRasterOverlays::RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model
*
* @return The {@link CesiumRasterOverlays::RasterOverlayDetails} that is owned by this content
*/
const CesiumRasterOverlays::RasterOverlayDetails&
getRasterOverlayDetails() const noexcept;
/**
* @brief Get the {@link CesiumRasterOverlays::RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model
*
* @return The {@link CesiumRasterOverlays::RasterOverlayDetails} that is owned by this content
*/
CesiumRasterOverlays::RasterOverlayDetails&
getRasterOverlayDetails() noexcept;
/**
* @brief Set the {@link CesiumRasterOverlays::RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model
*
* @param rasterOverlayDetails The {@link CesiumRasterOverlays::RasterOverlayDetails} that will be owned by this content
*/
void setRasterOverlayDetails(
const CesiumRasterOverlays::RasterOverlayDetails& rasterOverlayDetails);
/**
* @brief Set the {@link CesiumRasterOverlays::RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model
*
* @param rasterOverlayDetails The {@link CesiumRasterOverlays::RasterOverlayDetails} that will be owned by this content
*/
void setRasterOverlayDetails(
CesiumRasterOverlays::RasterOverlayDetails&& rasterOverlayDetails);
/**
* @brief Get the list of \ref CesiumUtility::Credit "Credit" of the content
*
* @return The list of \ref CesiumUtility::Credit "Credit" of the content
*/
const std::vector<CesiumUtility::Credit>& getCredits() const noexcept;
/**
* @brief Get the list of \ref CesiumUtility::Credit "Credit" of the content
*
* @return The list of \ref CesiumUtility::Credit "Credit" of the content
*/
std::vector<CesiumUtility::Credit>& getCredits() noexcept;
/**
* @brief Set the list of \ref CesiumUtility::Credit "Credit" for the content
*
* @param credits The list of \ref CesiumUtility::Credit "Credit" to be owned
* by the content
*/
void setCredits(std::vector<CesiumUtility::Credit>&& credits);
/**
* @brief Set the list of \ref CesiumUtility::Credit "Credit" for the content
*
* @param credits The list of \ref CesiumUtility::Credit "Credit" to be owned
* by the content
*/
void setCredits(const std::vector<CesiumUtility::Credit>& credits);
/**
* @brief Get the render resources created for the glTF model of the content
*
* @return The render resources that is created for the glTF model
*/
void* getRenderResources() const noexcept;
/**
* @brief Set the render resources created for the glTF model of the content
*
* @param pRenderResources The render resources that is created for the glTF
* model
*/
void setRenderResources(void* pRenderResources) noexcept;
/**
* @brief Get the fade percentage that this tile during an LOD transition.
*
* This will be used when {@link TilesetOptions::enableLodTransitionPeriod}
* is true. Tile fades can be used to make LOD transitions appear less abrupt
* and jarring. It is up to client implementations how to render the fade
* percentage, but dithered fading is recommended.
*
* @return The fade percentage.
*/
float getLodTransitionFadePercentage() const noexcept;
/**
* @brief Set the fade percentage of this tile during an LOD transition with.
* Not to be used by clients.
*
* @param percentage The new fade percentage.
*/
void setLodTransitionFadePercentage(float percentage) noexcept;
private:
CesiumGltf::Model _model;
void* _pRenderResources;
CesiumRasterOverlays::RasterOverlayDetails _rasterOverlayDetails;
std::vector<CesiumUtility::Credit> _credits;
float _lodTransitionFadePercentage;
};
/**
* @brief A tile content container that can store and query the content type
* that is currently being owned by the tile
*/
class CESIUM3DTILESSELECTION_API TileContent {
using TileContentKindImpl = std::variant<
TileUnknownContent,
TileEmptyContent,
std::unique_ptr<TileExternalContent>,
std::unique_ptr<TileRenderContent>>;
public:
/**
* @brief Construct an unknown content for a tile. This constructor
* is useful when the tile content is known after its content is downloaded by
* {@link TilesetContentLoader}
*/
TileContent();
/**
* @brief Construct an empty content for a tile
*/
TileContent(TileEmptyContent content);
/**
* @brief Construct an external content for a tile whose content
* points to an external tileset
*/
TileContent(std::unique_ptr<TileExternalContent>&& content);
/**
* @brief Set an unknown content tag for a tile. This constructor
* is useful when the tile content is known after its content is downloaded by
* {@link TilesetContentLoader}
*/
void setContentKind(TileUnknownContent content);
/**
* @brief Construct an empty content tag for a tile
*/
void setContentKind(TileEmptyContent content);
/**
* @brief Set an external content for a tile whose content
* points to an external tileset
*/
void setContentKind(std::unique_ptr<TileExternalContent>&& content);
/**
* @brief Set a glTF model content for a tile
*/
void setContentKind(std::unique_ptr<TileRenderContent>&& content);
/**
* @brief Query if a tile has an unknown content
*/
bool isUnknownContent() const noexcept;
/**
* @brief Query if a tile has an empty content
*/
bool isEmptyContent() const noexcept;
/**
* @brief Query if a tile has an external content which points to
* an external tileset
*/
bool isExternalContent() const noexcept;
/**
* @brief Query if a tile has an glTF model content
*/
bool isRenderContent() const noexcept;
/**
* @brief Get the {@link TileRenderContent} which stores the glTF model
* and render resources of the tile
*/
const TileRenderContent* getRenderContent() const noexcept;
/**
* @brief Get the {@link TileRenderContent} which stores the glTF model
* and render resources of the tile
*/
TileRenderContent* getRenderContent() noexcept;
/**
* @brief Get the {@link TileExternalContent} which stores the details of
* the external tileset.
*/
const TileExternalContent* getExternalContent() const noexcept;
/**
* @brief Get the {@link TileExternalContent} which stores the details of
* the external tileset.
*/
TileExternalContent* getExternalContent() noexcept;
private:
TileContentKindImpl _contentKind;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,61 @@
#pragma once
#include "Library.h"
#include <CesiumGeometry/OctreeTileID.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <string>
#include <variant>
namespace Cesium3DTilesSelection {
/**
* @brief An identifier for a {@link Tile} inside the tile hierarchy.
*
* This ID is stored in the tile as the {@link Tile::getTileID}.
* It is assigned to the tile at construction time, and may be
* used to identify and access the children of a given tile.
*
* Depending on the exact type of the tile and its contents, this
* identifier may have different forms:
*
* * A `std::string`: This is an explicitly-described tile and
* the ID is the URL of the tile's content.
* * A {@link CesiumGeometry::QuadtreeTileID}: This is an implicit
* tile in the quadtree. The URL of the tile's content is formed
* by instantiating the context's template URL with this ID.
* * A {@link CesiumGeometry::OctreeTileID}: This is an implicit
* tile in the octree. The URL of the tile's content is formed
* by instantiating the context's template URL with this ID.
* * A {@link CesiumGeometry::UpsampledQuadtreeNode}: This tile doesn't
* have any content, but content for it can be created by subdividing
* the parent tile's content.
*/
typedef std::variant<
std::string,
CesiumGeometry::QuadtreeTileID,
CesiumGeometry::OctreeTileID,
CesiumGeometry::UpsampledQuadtreeNode>
TileID;
/**
* @brief Utility functions related to {@link TileID} objects.
*/
struct CESIUM3DTILESSELECTION_API TileIdUtilities {
/**
* @brief Creates an unspecified string representation of the given {@link
* TileID}.
*
* The returned string will contain information about the given tile ID,
* depending on its type. The exact format and contents of this string
* is not specified. This is mainly intended for printing informative
* log messages.
*
* @param tileId The tile ID
* @return The string
*/
static std::string createTileIdString(const TileID& tileId);
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,162 @@
#pragma once
#include "BoundingVolume.h"
#include "TileContent.h"
#include <CesiumAsync/IAssetRequest.h>
#include <CesiumGeometry/Axis.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGltf/Model.h>
#include <CesiumRasterOverlays/RasterOverlayDetails.h>
#include <functional>
#include <memory>
#include <optional>
#include <variant>
namespace Cesium3DTilesSelection {
class Tile;
/**
* @brief Store the content of the tile after finishing
* loading tile using {@link TilesetContentLoader::loadTileContent}:
*
* 1. Returning {@link TileUnknownContent} means that the loader doesn't know
* the content of the tile. This content type is useful when loader fails to
* load the tile content; or a background task is running to determine the tile
* content and the loader wants the client to retry later at some point in the
* future
*
* 2. Returning {@link TileEmptyContent} means that this tile has no content and mostly used
* for efficient culling during the traversal process
*
* 3. Returning {@link TileExternalContent} means that this tile points to an external tileset
*
* 4. Returning {@link CesiumGltf::Model} means that this tile has glTF model
*/
using TileContentKind = std::variant<
TileUnknownContent,
TileEmptyContent,
TileExternalContent,
CesiumGltf::Model>;
/**
* @brief Indicate the status of {@link Cesium3DTilesSelection::TilesetContentLoader::loadTileContent} and
* {@link Cesium3DTilesSelection::TilesetContentLoader::createTileChildren} operations
*/
enum class TileLoadResultState {
/**
* @brief The operation is successful and all the fields in {@link TileLoadResult}
* or {@link TileChildrenResult} are applied to the tile
*/
Success,
/**
* @brief The operation is failed and __none__ of the fields in {@link TileLoadResult}
* or {@link TileChildrenResult} are applied to the tile
*/
Failed,
/**
* @brief The operation requires the client to retry later due to some
* background work happenning and
* __none__ of the fields in {@link TileLoadResult} or {@link TileChildrenResult} are applied to the tile
*/
RetryLater
};
/**
* @brief Store the result of loading a tile content after
* invoking {@link TilesetContentLoader::loadTileContent}
*/
struct CESIUM3DTILESSELECTION_API TileLoadResult {
/**
* @brief The content type of the tile.
*/
TileContentKind contentKind;
/**
* @brief The up axis of glTF content.
*/
CesiumGeometry::Axis glTFUpAxis;
/**
* @brief A tile can potentially store a more fit bounding volume along with
* its content. If this field is set, the tile's bounding volume will be
* updated after the loading is finished.
*/
std::optional<BoundingVolume> updatedBoundingVolume;
/**
* @brief A tile can potentially store a more fit content bounding volume
* along with its content. If this field is set, the tile's content bounding
* volume will be updated after the loading is finished.
*/
std::optional<BoundingVolume> updatedContentBoundingVolume;
/**
* @brief Holds details of the {@link TileRenderContent} that are useful
* for raster overlays.
*/
std::optional<CesiumRasterOverlays::RasterOverlayDetails>
rasterOverlayDetails;
/**
* @brief The asset accessor that was used to retrieve this tile, and that
* should be used to retrieve further resources referenced by the tile.
*/
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor;
/**
* @brief The request that is created to download the tile content.
*/
std::shared_ptr<CesiumAsync::IAssetRequest> pCompletedRequest;
/**
* @brief A callback that is invoked in the main thread immediately when the
* loading is finished. This callback is useful when the content request has
* other fields like geometric error,
* children (in the case of {@link TileExternalContent}), etc, to override the existing fields.
*/
std::function<void(Tile&)> tileInitializer;
/**
* @brief The result of loading a tile. Note that if the state is Failed or
* RetryLater, __none__ of the fields above (including {@link TileLoadResult::tileInitializer}) will be
* applied to a tile when the loading is finished
*/
TileLoadResultState state;
/**
* @brief The ellipsoid that this tile uses.
*
* This value is only guaranteed to be accurate when {@link TileLoadResult::state} is equal to {@link TileLoadResultState::Success}.
*/
CesiumGeospatial::Ellipsoid ellipsoid =
CesiumGeospatial::Ellipsoid::UNIT_SPHERE;
/**
* @brief Create a result with Failed state
*
* @param pAssetAccessor The \ref CesiumAsync::IAssetAccessor "IAssetAccessor"
* used to load tiles.
* @param pCompletedRequest The failed request
*/
static TileLoadResult createFailedResult(
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor,
std::shared_ptr<CesiumAsync::IAssetRequest> pCompletedRequest);
/**
* @brief Create a result with RetryLater state
*
* @param pAssetAccessor The \ref CesiumAsync::IAssetAccessor "IAssetAccessor"
* used to load tiles.
* @param pCompletedRequest The failed request
*/
static TileLoadResult createRetryLaterResult(
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor,
std::shared_ptr<CesiumAsync::IAssetRequest> pCompletedRequest);
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,147 @@
#pragma once
#include "Library.h"
#include "Tile.h"
#include <cstdint>
#include <unordered_map>
#include <vector>
namespace Cesium3DTilesSelection {
/**
* @brief The occlusion state of a tile as reported by the renderer proxy.
*/
enum class CESIUM3DTILESSELECTION_API TileOcclusionState {
/**
* @brief The renderer does not yet know if the tile's bounding volume is
* occluded or not.
*
* This can be due to the typical occlusion delay caused by buffered
* rendering or otherwise be due to postponed occlusion queries. We can
* choose to wait for the occlusion information to become available before
* commiting to load the tile. This might prevent unneeded tile loads at the
* cost of a small delay.
*/
OcclusionUnavailable,
/**
* @brief The tile's bounding volume is known by the renderer to be visible.
*/
NotOccluded,
/**
* @brief The tile's bounding volume is known by the renderer to be occluded.
*/
Occluded
};
/**
* @brief An interface for client renderers to use to represent tile bounding
* volumes that should be occlusion tested.
*/
class CESIUM3DTILESSELECTION_API TileOcclusionRendererProxy {
public:
/**
* @brief Get the occlusion state for this tile. If this is
* OcclusionUnavailable, the traversal may decide to wait for the occlusion
* result to become available in future frames.
*
* Client implementation note: Do not return OcclusionUnavailable if the
* occlusion for this tile will _never_ become available, otherwise the tile
* may not refine while waiting for occlusion. In such a case return
* NotOccluded so the traversal can assume it is _known_ to be visible.
*
* @return The occlusion state of this tile.
*/
virtual TileOcclusionState getOcclusionState() const = 0;
protected:
friend class TileOcclusionRendererProxyPool;
/**
* @brief Reset this proxy to target a new tile. If nullptr, this proxy is
* back in the pool and will not be used for further occlusion until reset
* is called again with an actual tile.
*
* @param pTile The tile that this proxy represents or nullptr if the proxy
* is back in the pool.
*/
virtual void reset(const Tile* pTile) = 0;
private:
bool _usedLastFrame = false;
TileOcclusionRendererProxy* _pNext = nullptr;
};
/**
* @brief A pool of {@link TileOcclusionRendererProxy} objects. Allows quick
* remapping of tiles to occlusion renderer proxies so new proxies do not have
* to be created for each new tile requesting occlusion results.
*/
class CESIUM3DTILESSELECTION_API TileOcclusionRendererProxyPool {
public:
/**
* @brief Constructs a new instance.
*
* @param maximumPoolSize The maximum number of
* {@link TileOcclusionRendererProxy} instances that may exist in this pool.
*/
TileOcclusionRendererProxyPool(int32_t maximumPoolSize);
/**
* @brief Destroys this pool.
*/
virtual ~TileOcclusionRendererProxyPool();
/**
* @brief Destroy the pool.
*/
void destroyPool();
/**
* @brief Get the {@link TileOcclusionRendererProxy} mapped to the tile.
* Attempts to create a new mapping if one does not exist already by
* assigning a proxy from the free list.
*
* @param tile The tile.
* @param currentFrame The current frame number.
* @return The occlusion proxy mapped to this tile, or nullptr if one can't
* be made.
*/
const TileOcclusionRendererProxy*
fetchOcclusionProxyForTile(const Tile& tile, int32_t currentFrame);
/**
* @brief Prunes the occlusion proxy mappings and removes any mappings that
* were unused the last frame. Any mapping corresponding to a tile that was
* not visited will have been unused. Occlusion proxies from removed mappings
* will be returned to the free list.
*/
void pruneOcclusionProxyMappings();
protected:
/**
* @brief Create a {@link TileOcclusionRendererProxy}.
*
* @return A new occlusion proxy.
*/
virtual TileOcclusionRendererProxy* createProxy() = 0;
/**
* @brief Destroy a {@link TileOcclusionRendererProxy} that is done being used.
*
* @param pProxy The proxy to be destroyed.
*/
virtual void destroyProxy(TileOcclusionRendererProxy* pProxy) = 0;
private:
// Singly linked list representing the free proxies in the pool
TileOcclusionRendererProxy* _pFreeProxiesHead;
int32_t _currentSize;
int32_t _maxSize;
// The currently used proxies in the pool
std::unordered_map<const Tile*, TileOcclusionRendererProxy*>
_tileToOcclusionProxyMappings;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,23 @@
#pragma once
namespace Cesium3DTilesSelection {
/**
* @brief Refinement strategies for a {@link Cesium3DTilesSelection::Tile}.
*/
enum class TileRefine {
/**
* @brief The content of the child tiles will be added to the content of the
* parent tile.
*/
Add = 0,
/**
* @brief The content of the child tiles will replace the content of the
* parent tile.
*/
Replace = 1
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,168 @@
#pragma once
#include "Library.h"
#include <cstdint>
namespace Cesium3DTilesSelection {
/**
* @brief A description of the state of a {@link Tile} during the rendering
* process
*
* Instances of this class combine a frame number and a
* {@link TileSelectionState::Result} that describes the actual state of the
* tile.
* Instances of this class are stored in a {@link Tile}, and are used to track
* the state of the tile during the rendering process. The {@link Tileset}
* updates this state while traversing the tile hierarchy, tracking whether a
* tile was rendered, culled, or refined in the last frame.
*/
class TileSelectionState final {
public:
/**
* @brief Enumeration of possible results of a {@link TileSelectionState}
*/
enum class CESIUM3DTILESSELECTION_API Result {
/**
* @brief There was no selection result.
*
* This may be the case when the tile wasn't visited last frame.
*/
None = 0,
/**
* @brief This tile was deemed not visible and culled.
*/
Culled = 1,
/**
* @brief The tile was selected for rendering.
*/
Rendered = 2,
/**
* @brief This tile did not meet the required screen-space error and was
* refined.
*/
Refined = 3,
/**
* @brief This tile was rendered but then removed from the render list
*
* This tile was originally rendered, but it got kicked out of the render
* list in favor of an ancestor because it is not yet renderable.
*/
RenderedAndKicked = 4,
/**
* @brief This tile was refined but then removed from the render list
*
* This tile was originally refined, but its rendered descendants got kicked
* out of the render list in favor of an ancestor because it is not yet
* renderable.
*/
RefinedAndKicked = 5
};
/**
* @brief Initializes a new instance with
* {@link TileSelectionState::Result::None}
*/
constexpr TileSelectionState() noexcept
: _frameNumber(0), _result(Result::None) {}
/**
* @brief Initializes a new instance with a given
* {@link TileSelectionState::Result}.
*
* @param frameNumber The frame number in which the selection took place.
* @param result The result of the selection.
*/
constexpr TileSelectionState(int32_t frameNumber, Result result) noexcept
: _frameNumber(frameNumber), _result(result) {}
/**
* @brief Gets the frame number in which selection took place.
*/
constexpr int32_t getFrameNumber() const noexcept {
return this->_frameNumber;
}
/**
* @brief Gets the result of selection.
*
* The given frame number must match the frame number in which selection last
* took place. Otherwise, {@link TileSelectionState::Result::None} is
* returned.
*
* @param frameNumber The previous frame number.
* @return The {@link TileSelectionState::Result}
*/
constexpr Result getResult(int32_t frameNumber) const noexcept {
if (this->_frameNumber != frameNumber) {
return Result::None;
}
return this->_result;
}
/**
* @brief Determines if this tile or its descendents were kicked from the
* render list.
*
* In other words, if its last selection result was
* {@link TileSelectionState::Result::RenderedAndKicked} or
* {@link TileSelectionState::Result::RefinedAndKicked}.
*
* @param frameNumber The previous frame number.
* @return `true` if the tile was kicked, and `false` otherwise
*/
constexpr bool wasKicked(int32_t frameNumber) const noexcept {
const Result result = this->getResult(frameNumber);
return result == Result::RenderedAndKicked ||
result == Result::RefinedAndKicked;
}
/**
* @brief Gets the original selection result prior to being kicked.
*
* If the tile wasn't kicked, the original value is returned.
*
* @param frameNumber The previous frame number.
* @return The {@link TileSelectionState::Result} prior to being kicked.
*/
constexpr Result getOriginalResult(int32_t frameNumber) const noexcept {
const Result result = this->getResult(frameNumber);
switch (result) {
case Result::RefinedAndKicked:
return Result::Refined;
case Result::RenderedAndKicked:
return Result::Rendered;
default:
return result;
}
}
/**
* @brief Marks this tile as "kicked".
*/
constexpr void kick() noexcept {
switch (this->_result) {
case Result::Rendered:
this->_result = Result::RenderedAndKicked;
break;
case Result::Refined:
this->_result = Result::RefinedAndKicked;
break;
default:
break;
}
}
private:
int32_t _frameNumber;
Result _result;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,572 @@
#pragma once
#include "Library.h"
#include "RasterOverlayCollection.h"
#include "SampleHeightResult.h"
#include "Tile.h"
#include "TilesetContentLoader.h"
#include "TilesetExternals.h"
#include "TilesetLoadFailureDetails.h"
#include "TilesetOptions.h"
#include "ViewState.h"
#include "ViewUpdateResult.h"
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <rapidjson/fwd.h>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <vector>
namespace Cesium3DTilesSelection {
class TilesetContentManager;
class TilesetMetadata;
class TilesetHeightQuery;
struct TilesetHeightRequest;
class TilesetSharedAssetSystem;
/**
* @brief A <a
* href="https://github.com/CesiumGS/3d-tiles/tree/master/specification">3D
* Tiles tileset</a>, used for streaming massive heterogeneous 3D geospatial
* datasets.
*/
class CESIUM3DTILESSELECTION_API Tileset final {
public:
/**
* @brief Constructs a new instance with a given custom tileset loader.
* @param externals The external interfaces to use.
* @param pCustomLoader The custom loader used to load the tileset and tile
* content.
* @param pRootTile The root tile that is associated with the custom loader
* @param options Additional options for the tileset.
*/
Tileset(
const TilesetExternals& externals,
std::unique_ptr<TilesetContentLoader>&& pCustomLoader,
std::unique_ptr<Tile>&& pRootTile,
const TilesetOptions& options = TilesetOptions());
/**
* @brief Constructs a new instance with a given `tileset.json` URL.
* @param externals The external interfaces to use.
* @param url The URL of the `tileset.json`.
* @param options Additional options for the tileset.
*/
Tileset(
const TilesetExternals& externals,
const std::string& url,
const TilesetOptions& options = TilesetOptions());
/**
* @brief Constructs a new instance with the given asset ID on <a
* href="https://cesium.com/ion/">Cesium ion</a>.
* @param externals The external interfaces to use.
* @param ionAssetID The ID of the Cesium ion asset to use.
* @param ionAccessToken The Cesium ion access token authorizing access to the
* asset.
* @param options Additional options for the tileset.
* @param ionAssetEndpointUrl The URL of the ion asset endpoint. Defaults
* to Cesium ion but a custom endpoint can be specified.
*/
Tileset(
const TilesetExternals& externals,
int64_t ionAssetID,
const std::string& ionAccessToken,
const TilesetOptions& options = TilesetOptions(),
const std::string& ionAssetEndpointUrl = "https://api.cesium.com/");
/**
* @brief Destroys this tileset.
*
* Destroying the tileset will immediately (before the destructor returns)
* unload as much tile content as possible. However, tiles that are currently
* in the process of being loaded cannot be unloaded immediately. These tiles
* will be unloaded asynchronously some time after this destructor returns. To
* be notified of completion of the async portion of the tileset destruction,
* subscribe to {@link getAsyncDestructionCompleteEvent}.
*/
~Tileset() noexcept;
/**
* @brief A future that resolves when this Tileset has been destroyed (i.e.
* its destructor has been called) and all async operations that it was
* executing have completed.
*/
CesiumAsync::SharedFuture<void>& getAsyncDestructionCompleteEvent();
/**
* @brief A future that resolves when the details of the root tile of this
* tileset are available. The root tile's content (e.g., 3D model), however,
* will not necessarily be loaded yet.
*/
CesiumAsync::SharedFuture<void>& getRootTileAvailableEvent();
/**
* @brief Get tileset credits.
*/
const std::vector<CesiumUtility::Credit>& getTilesetCredits() const noexcept;
/**
* @brief Sets whether or not the tileset's credits should be shown on screen.
* @param showCreditsOnScreen Whether the credits should be shown on screen.
*/
void setShowCreditsOnScreen(bool showCreditsOnScreen) noexcept;
/**
* @brief Gets the {@link TilesetExternals} that summarize the external
* interfaces used by this tileset.
*/
TilesetExternals& getExternals() noexcept { return this->_externals; }
/**
* @brief Gets the {@link TilesetExternals} that summarize the external
* interfaces used by this tileset.
*/
const TilesetExternals& getExternals() const noexcept {
return this->_externals;
}
/**
* @brief Returns the {@link CesiumAsync::AsyncSystem} that is used for
* dispatching asynchronous tasks.
*/
CesiumAsync::AsyncSystem& getAsyncSystem() noexcept {
return this->_asyncSystem;
}
/** @copydoc Tileset::getAsyncSystem() */
const CesiumAsync::AsyncSystem& getAsyncSystem() const noexcept {
return this->_asyncSystem;
}
/** @copydoc Tileset::getOptions() */
const TilesetOptions& getOptions() const noexcept { return this->_options; }
/**
* @brief Gets the {@link TilesetOptions} of this tileset.
*/
TilesetOptions& getOptions() noexcept { return this->_options; }
/**
* @brief Gets the {@link CesiumGeospatial::Ellipsoid} used by this tileset.
*/
const CesiumGeospatial::Ellipsoid& getEllipsoid() const {
return this->_options.ellipsoid;
}
/** @copydoc Tileset::getEllipsoid */
CesiumGeospatial::Ellipsoid& getEllipsoid() noexcept {
return this->_options.ellipsoid;
}
/**
* @brief Gets the root tile of this tileset.
*
* This may be `nullptr` if there is currently no root tile.
*/
Tile* getRootTile() noexcept;
/** @copydoc Tileset::getRootTile() */
const Tile* getRootTile() const noexcept;
/**
* @brief Returns the {@link RasterOverlayCollection} of this tileset.
*/
RasterOverlayCollection& getOverlays() noexcept;
/** @copydoc Tileset::getOverlays() */
const RasterOverlayCollection& getOverlays() const noexcept;
/**
* @brief Returns the {@link TilesetSharedAssetSystem} of this tileset.
*/
TilesetSharedAssetSystem& getSharedAssetSystem() noexcept;
/** @copydoc Tileset::getSharedAssetSystem() */
const TilesetSharedAssetSystem& getSharedAssetSystem() const noexcept;
/**
* @brief Updates this view but waits for all tiles that meet sse to finish
* loading and ready to be rendered before returning the function. This method
* is significantly slower than {@link Tileset::updateView} and should only be
* used for capturing movie or non-realtime situation.
* @param frustums The {@link ViewState}s that the view should be updated for
* @returns The set of tiles to render in the updated view. This value is only
* valid until the next call to `updateView` or until the tileset is
* destroyed, whichever comes first.
*/
const ViewUpdateResult&
updateViewOffline(const std::vector<ViewState>& frustums);
/**
* @brief Updates this view, returning the set of tiles to render in this
* view.
* @param frustums The {@link ViewState}s that the view should be updated for
* @param deltaTime The amount of time that has passed since the last call to
* updateView, in seconds.
* @returns The set of tiles to render in the updated view. This value is only
* valid until the next call to `updateView` or until the tileset is
* destroyed, whichever comes first.
*/
const ViewUpdateResult&
updateView(const std::vector<ViewState>& frustums, float deltaTime = 0.0f);
/**
* @brief Gets the total number of tiles that are currently loaded.
*/
int32_t getNumberOfTilesLoaded() const;
/**
* @brief Estimate the percentage of the tiles for the current view that have
* been loaded.
*/
float computeLoadProgress() noexcept;
/**
* @brief Invokes a function for each tile that is currently loaded.
*
* @param callback The function to invoke.
*/
void forEachLoadedTile(const std::function<void(Tile& tile)>& callback);
/**
* @brief Invokes a function for each tile that is currently loaded.
*
* @param callback The function to invoke.
*/
void forEachLoadedTile(
const std::function<void(const Tile& tile)>& callback) const;
/**
* @brief Gets the total number of bytes of tile and raster overlay data that
* are currently loaded.
*/
int64_t getTotalDataBytes() const noexcept;
/**
* @brief Gets the {@link TilesetMetadata} associated with the main or
* external tileset.json that contains a given tile. If the metadata is not
* yet loaded, this method returns nullptr.
*
* If this tileset's root tile is not yet available, this method returns
* nullptr.
*
* If the tileset has a {@link TilesetMetadata::schemaUri}, it will not
* necessarily have been loaded yet.
*
* If the provided tile is not the root tile of a tileset.json, this method
* walks up the {@link Tile::getParent} chain until it finds the closest
* root and then returns the metadata associated with the corresponding
* tileset.json.
*
* Consider calling {@link loadMetadata} instead, which will return a future
* that only resolves after the root tile is loaded and the `schemaUri`, if
* any, has been resolved.
*
* @param pTile The tile. If this parameter is nullptr, the metadata for the
* main tileset.json is returned.
* @return The found metadata, or nullptr if the root tile is not yet loaded.
*/
const TilesetMetadata* getMetadata(const Tile* pTile = nullptr) const;
/**
* @brief Asynchronously loads the metadata associated with the main
* tileset.json.
*
* Before the returned future resolves, the root tile of this tileset will be
* loaded and the {@link TilesetMetadata::schemaUri} will be loaded if one
* has been specified.
*
* If the tileset or `schemaUri` fail to load, the returned future will
* reject.
*
* @return A shared future that resolves to the loaded metadata. Once this
* future resolves, {@link getMetadata} can be used to synchronously obtain
* the same metadata instance.
*/
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.
*
* Instances of this structure are created by the `_visit...` functions,
* and summarize the information that was gathered during the traversal
* of the respective branch, so that this information can be used by
* the parent to decide on the further traversal process.
*/
struct TraversalDetails {
/**
* @brief Whether all selected tiles in this tile's subtree are renderable.
*
* This is `true` if all selected (i.e. not culled or refined) tiles in this
* tile's subtree are renderable. If the subtree is renderable, we'll render
* it; no drama.
*/
bool allAreRenderable = true;
/**
* @brief Whether any tile in this tile's subtree was rendered in the last
* frame.
*
* This is `true` if any tiles in this tile's subtree were rendered last
* frame. If any were, we must render the subtree rather than this tile,
* because rendering this tile would cause detail to vanish that was visible
* last frame, and that's no good.
*/
bool anyWereRenderedLastFrame = false;
/**
* @brief The number of selected tiles in this tile's subtree that are not
* yet renderable.
*
* Counts the number of selected tiles in this tile's subtree that are
* not yet ready to be rendered because they need more loading. Note that
* this value will _not_ necessarily be zero when
* `allAreRenderable` is `true`, for subtle reasons.
* When `allAreRenderable` and `anyWereRenderedLastFrame` are both `false`,
* we will render this tile instead of any tiles in its subtree and the
* `allAreRenderable` value for this tile will reflect only whether _this_
* tile is renderable. The `notYetRenderableCount` value, however, will
* still reflect the total number of tiles that we are waiting on, including
* the ones that we're not rendering. `notYetRenderableCount` is only reset
* when a subtree is removed from the render queue because the
* `notYetRenderableCount` exceeds the
* {@link TilesetOptions::loadingDescendantLimit}.
*/
uint32_t notYetRenderableCount = 0;
};
/**
* @brief Input information that is constant throughout the traversal.
*
* An instance of this structure is created upon entry of the top-level
* `_visitTile` function, for the traversal for a certain frame, and
* passed on through the traversal.
*/
struct FrameState {
const std::vector<ViewState>& frustums;
std::vector<double> fogDensities;
int32_t lastFrameNumber;
int32_t currentFrameNumber;
};
TraversalDetails _renderLeaf(
const FrameState& frameState,
Tile& tile,
double tilePriority,
ViewUpdateResult& result);
TraversalDetails _renderInnerTile(
const FrameState& frameState,
Tile& tile,
ViewUpdateResult& result);
bool _kickDescendantsAndRenderTile(
const FrameState& frameState,
Tile& tile,
ViewUpdateResult& result,
TraversalDetails& traversalDetails,
size_t firstRenderedDescendantIndex,
size_t workerThreadLoadQueueIndex,
size_t mainThreadLoadQueueIndex,
bool queuedForLoad,
double tilePriority);
TileOcclusionState
_checkOcclusion(const Tile& tile, const FrameState& frameState);
TraversalDetails _visitTile(
const FrameState& frameState,
uint32_t depth,
bool meetsSse,
bool ancestorMeetsSse,
Tile& tile,
double tilePriority,
ViewUpdateResult& result);
struct CullResult {
// whether we should visit this tile
bool shouldVisit = true;
// whether this tile was culled (Note: we might still want to visit it)
bool culled = false;
};
// TODO: abstract these into a composable culling interface.
void _frustumCull(
const Tile& tile,
const FrameState& frameState,
bool cullWithChildrenBounds,
CullResult& cullResult);
void _fogCull(
const FrameState& frameState,
const std::vector<double>& distances,
CullResult& cullResult);
bool _meetsSse(
const std::vector<ViewState>& frustums,
const Tile& tile,
const std::vector<double>& distances,
bool culled) const noexcept;
TraversalDetails _visitTileIfNeeded(
const FrameState& frameState,
uint32_t depth,
bool ancestorMeetsSse,
Tile& tile,
ViewUpdateResult& result);
TraversalDetails _visitVisibleChildrenNearToFar(
const FrameState& frameState,
uint32_t depth,
bool ancestorMeetsSse,
Tile& tile,
ViewUpdateResult& result);
/**
* @brief When called on an additive-refined tile, queues it for load and adds
* it to the render list.
*
* For replacement-refined tiles, this method does nothing and returns false.
*
* @param tile The tile to potentially load and render.
* @param result The current view update result.
* @param tilePriority The load priority of this tile.
* priority.
* @param queuedForLoad True if this tile has already been queued for loading.
* @return true The additive-refined tile was queued for load and added to the
* render list.
* @return false The non-additive-refined tile was ignored.
*/
bool _loadAndRenderAdditiveRefinedTile(
Tile& tile,
ViewUpdateResult& result,
double tilePriority,
bool queuedForLoad);
void _processWorkerThreadLoadQueue();
void _processMainThreadLoadQueue();
void _unloadCachedTiles(double timeBudget) noexcept;
void _markTileVisited(Tile& tile) noexcept;
void _updateLodTransitions(
const FrameState& frameState,
float deltaTime,
ViewUpdateResult& result) const noexcept;
TilesetExternals _externals;
CesiumAsync::AsyncSystem _asyncSystem;
TilesetOptions _options;
int32_t _previousFrameNumber;
ViewUpdateResult _updateResult;
enum class TileLoadPriorityGroup {
/**
* @brief Low priority tiles that aren't needed right now, but
* are being preloaded for the future.
*/
Preload = 0,
/**
* @brief Medium priority tiles that are needed to render the current view
* the appropriate level-of-detail.
*/
Normal = 1,
/**
* @brief High priority tiles that are causing extra detail to be rendered
* in the scene, potentially creating a performance problem and aliasing
* artifacts.
*/
Urgent = 2
};
struct TileLoadTask {
/**
* @brief The tile to be loaded.
*/
Tile* pTile;
/**
* @brief The priority group (low / medium / high) in which to load this
* tile.
*
* All tiles in a higher priority group are given a chance to load before
* any tiles in a lower priority group.
*/
TileLoadPriorityGroup group;
/**
* @brief The priority of this tile within its priority group.
*
* Tiles with a _lower_ value for this property load sooner!
*/
double priority;
bool operator<(const TileLoadTask& rhs) const noexcept {
if (this->group == rhs.group)
return this->priority < rhs.priority;
else
return this->group > rhs.group;
}
};
std::vector<TileLoadTask> _mainThreadLoadQueue;
std::vector<TileLoadTask> _workerThreadLoadQueue;
std::vector<Tile*> _heightQueryLoadQueue;
Tile::LoadedLinkedList _loadedTiles;
// Holds computed distances, to avoid allocating them on the heap during tile
// selection.
std::vector<double> _distances;
// Holds the occlusion proxies of the children of a tile. Store them in this
// scratch variable so that it can allocate only when growing bigger.
std::vector<const TileOcclusionRendererProxy*> _childOcclusionProxies;
CesiumUtility::IntrusivePointer<TilesetContentManager>
_pTilesetContentManager;
std::list<TilesetHeightRequest> _heightRequests;
void addTileToLoadQueue(
Tile& tile,
TileLoadPriorityGroup priorityGroup,
double priority);
static TraversalDetails createTraversalDetailsForSingleTile(
const FrameState& frameState,
const Tile& tile,
const TileSelectionState& lastFrameSelectionState);
Tileset(const Tileset& rhs) = delete;
Tileset& operator=(const Tileset& rhs) = delete;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,172 @@
#pragma once
#include "BoundingVolume.h"
#include "Library.h"
#include "TileContent.h"
#include "TileLoadResult.h"
#include "TilesetOptions.h"
#include <Cesium3DTilesSelection/SampleHeightResult.h>
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumGeometry/Axis.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGltf/Model.h>
#include <CesiumRasterOverlays/RasterOverlayDetails.h>
#include <spdlog/logger.h>
#include <functional>
#include <memory>
#include <optional>
#include <vector>
namespace Cesium3DTilesSelection {
class Tile;
class ITilesetHeightSampler;
/**
* @brief Store the parameters that are needed to load a tile
*/
struct CESIUM3DTILESSELECTION_API TileLoadInput {
/**
* @brief Creates a new instance
*
* @param tile The {@link Tile} that the content belongs to.
* @param contentOptions The content options the {@link TilesetContentLoader} will use to process the content of the tile.
* @param asyncSystem The async system to use for tile content loading.
* @param pAssetAccessor The asset accessor to make further requests with.
* @param pLogger The logger that will be used
* @param requestHeaders The request headers that will be attached to the
* request.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
*/
TileLoadInput(
const Tile& tile,
const TilesetContentOptions& contentOptions,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<spdlog::logger>& pLogger,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& requestHeaders,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief The tile that the {@link TilesetContentLoader} will request the server for the content.
*/
const Tile& tile;
/**
* @brief The content options the {@link TilesetContentLoader} will use to process the content of the tile.
*/
const TilesetContentOptions& contentOptions;
/**
* @brief The async system to run the loading in worker thread or main thread.
*/
const CesiumAsync::AsyncSystem& asyncSystem;
/**
* @brief The asset accessor to make requests for the tile content over the
* wire.
*/
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor;
/**
* @brief The logger that receives details of loading errors and warnings.
*/
const std::shared_ptr<spdlog::logger>& pLogger;
/**
* @brief The request headers that will be attached to the request.
*/
const std::vector<CesiumAsync::IAssetAccessor::THeader>& requestHeaders;
/**
* @brief The ellipsoid that this tileset uses.
*/
const CesiumGeospatial::Ellipsoid& ellipsoid;
};
/**
* @brief Store the result of creating tile's children after
* invoking {@link TilesetContentLoader::createTileChildren}
*/
struct CESIUM3DTILESSELECTION_API TileChildrenResult {
/**
* @brief The children of this tile.
*/
std::vector<Tile> children;
/**
* @brief The result of creating children for
* this tile. Note that: when receiving RetryLater status, client
* needs to load this tile's content first or its parent's content. The reason
* is that some tileset formats store the tile's children along with its
* content or store a whole subtree for every n-th level tile (e.g Quantized
* Mesh format). So unless the tile's content or the root tile of the subtree
* the tile is in is loaded, the loader won't know how to create the tile's
* children.
*/
TileLoadResultState state;
};
/**
* @brief The loader interface to load the tile content
*/
class CESIUM3DTILESSELECTION_API TilesetContentLoader {
public:
/**
* @brief Default virtual destructor
*/
virtual ~TilesetContentLoader() = default;
/**
* @brief Load the tile content.
*
* @param input The {@link TileLoadInput} that has the tile info and loading systems to load this tile's content
* @return The future of {@link TileLoadResult} that stores the tile's content
*/
virtual CesiumAsync::Future<TileLoadResult>
loadTileContent(const TileLoadInput& input) = 0;
/**
* @brief Create the tile's children.
*
* Note that: when receiving RetryLater status, client
* needs to load this tile's content first or its parent's content. The reason
* is that some tileset formats store the tile's children along with its
* content or store a whole subtree for every n-th level tile (e.g Quantized
* Mesh format). So unless the tile's content or the root tile of the subtree
* the tile is in is loaded, the loader won't know how to create the tile's
* children.
*
* @param tile The tile to create children for.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
* @return The {@link TileChildrenResult} that stores the tile's children
*/
virtual TileChildrenResult createTileChildren(
const Tile& tile,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) = 0;
/**
* @brief Gets an interface that can be used to efficiently query heights from
* this tileset.
*
* Some loaders may be able to query heights very efficiently by using a web
* service or by using an analytical model, e.g., when the "terrain" is a
* simple ellipsoid.
*
* For loaders that have no particular way to query heights, this method will
* return `nullptr`, signaling that heights should be computed by downloading
* and sampling individual tiles.
*
* @return The interface that can be used to efficiently query heights from
* this loader, or `nullptr` if this loader has no particular way to do that.
* The returned instance must have a lifetime that is at least as long as the
* loader itself.
*/
virtual ITilesetHeightSampler* getHeightSampler() { return nullptr; }
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,82 @@
#pragma once
#include "Library.h"
#include "TileOcclusionRendererProxy.h"
#include "TilesetSharedAssetSystem.h"
#include "spdlog-cesium.h"
#include <CesiumAsync/AsyncSystem.h>
#include <memory>
namespace CesiumAsync {
class IAssetAccessor;
class ITaskProcessor;
} // namespace CesiumAsync
namespace CesiumUtility {
class CreditSystem;
}
namespace Cesium3DTilesSelection {
class IPrepareRendererResources;
/**
* @brief External interfaces used by a {@link Tileset}.
*
* Not supposed to be used by clients.
*/
class CESIUM3DTILESSELECTION_API TilesetExternals final {
public:
/**
* @brief An external {@link CesiumAsync::IAssetAccessor}.
*/
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor;
/**
* @brief An external {@link IPrepareRendererResources}.
*/
std::shared_ptr<IPrepareRendererResources> pPrepareRendererResources;
/**
* @brief The async system to use to do work in threads.
*
* The tileset will automatically call
* {@link CesiumAsync::AsyncSystem::dispatchMainThreadTasks} from
* {@link Tileset::updateView}.
*/
CesiumAsync::AsyncSystem asyncSystem;
/**
* @brief An external {@link CesiumUtility::CreditSystem} that can be used to manage credit
* strings and track which which credits to show and remove from the screen
* each frame.
*/
std::shared_ptr<CesiumUtility::CreditSystem> pCreditSystem;
/**
* @brief A spdlog logger that will receive log messages.
*
* If not specified, defaults to `spdlog::default_logger()`.
*/
std::shared_ptr<spdlog::logger> pLogger = spdlog::default_logger();
/**
* @brief A pool of renderer proxies to determine the occlusion state of
* tile bounding volumes.
*
* If not specified, the traversal will not attempt to leverage occlusion
* information.
*/
std::shared_ptr<TileOcclusionRendererProxyPool> pTileOcclusionProxyPool =
nullptr;
/**
* @brief The shared asset system used to facilitate sharing of common assets,
* such as images, between and within tilesets.
*/
CesiumUtility::IntrusivePointer<TilesetSharedAssetSystem> pSharedAssetSystem =
TilesetSharedAssetSystem::getDefault();
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,60 @@
#pragma once
#include <memory>
#include <string>
namespace CesiumAsync {
class IAssetRequest;
}
namespace Cesium3DTilesSelection {
class Tileset;
/**
* @brief The type of load that failed in `TilesetLoadFailureDetails`.
*/
enum class TilesetLoadType {
/**
* @brief An unknown load error.
*/
Unknown,
/**
* @brief A Cesium ion asset endpoint.
*/
CesiumIon,
/**
* @brief The root tileset.json.
*/
TilesetJson
};
/**
* Information on a tileset that failed to load.
*/
class TilesetLoadFailureDetails {
public:
/**
* @brief The tileset that encountered the load failure.
*/
const Tileset* pTileset = nullptr;
/**
* @brief The type of request that failed to load.
*/
TilesetLoadType type = TilesetLoadType::Unknown;
/**
* The status code of the HTTP response.
*/
uint16_t statusCode{200};
/**
* @brief A human-readable explanation of what failed.
*/
std::string message = "";
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,77 @@
#pragma once
#include "Library.h"
#include <Cesium3DTiles/GroupMetadata.h>
#include <Cesium3DTiles/Schema.h>
#include <CesiumAsync/SharedFuture.h>
#include <optional>
#include <string>
#include <vector>
namespace CesiumAsync {
class AsyncSystem;
class IAssetAccessor;
} // namespace CesiumAsync
namespace Cesium3DTilesSelection {
/**
* @brief Holds the metadata associated with a {@link Tileset} or an external
* tileset.
*/
class CESIUM3DTILESSELECTION_API TilesetMetadata {
public:
~TilesetMetadata() noexcept;
/**
* @brief An object defining the structure of metadata classes and enums. When
* this is defined, then `schemaUri` shall be undefined.
*/
std::optional<Cesium3DTiles::Schema> schema;
/**
* @brief The URI (or IRI) of the external schema file. When this is defined,
* then `schema` shall be undefined.
*/
std::optional<std::string> schemaUri;
/**
* @brief An array of groups that tile content may belong to. Each element of
* this array is a metadata entity that describes the group. The tile content
* `group` property is an index into this array.
*/
std::vector<Cesium3DTiles::GroupMetadata> groups;
/**
* @brief A metadata entity that is associated with this tileset.
*/
std::optional<Cesium3DTiles::MetadataEntity> metadata;
/**
* @brief Asynchronously loads the {@link schema} from the {@link schemaUri}.
* If the {@link schemaUri} does not contain a value, this method does
* nothing and returns an already-resolved future.
*
* Calling this method multiple times will return the same shared future each
* time, unless the {@link schemaUri} is changed. In that case, when this
* method is called, the previous load is canceled and the new one begins.
*
* @param asyncSystem The async system used to do work in threads.
* @param pAssetAccessor The asset accessor used to request the schema from
* the schemaUri.
* @return A future that resolves when the schema has been loaded from the
* schemaUri.
*/
CesiumAsync::SharedFuture<void>& loadSchemaUri(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor);
private:
std::optional<CesiumAsync::SharedFuture<void>> _loadingFuture;
std::optional<std::string> _loadingSchemaUri;
std::shared_ptr<bool> _pLoadingCanceled;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,333 @@
#pragma once
#include "Library.h"
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGltf/Ktx2TranscodeTargets.h>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
namespace Cesium3DTilesSelection {
class ITileExcluder;
class TilesetLoadFailureDetails;
/**
* @brief Options for configuring the parsing of a {@link Tileset}'s content
* and construction of Gltf models.
*/
struct CESIUM3DTILESSELECTION_API TilesetContentOptions {
/**
* @brief Whether to include a water mask within the Gltf extras.
*
* Currently only applicable for quantized-mesh tilesets that support the
* water mask extension.
*/
bool enableWaterMask = false;
/**
* @brief Whether to generate smooth normals when normals are missing in the
* original Gltf.
*
* According to the Gltf spec: "When normals are not specified, client
* implementations should calculate flat normals." However, calculating flat
* normals requires duplicating vertices. This option allows the gltfs to be
* sent with explicit smooth normals when the original gltf was missing
* normals.
*/
bool generateMissingNormalsSmooth = false;
/**
* @brief For each possible input transmission format, this struct names
* the ideal target gpu-compressed pixel format to transcode to.
*/
CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets;
/**
* @brief Whether or not to transform texture coordinates during load when
* textures have the `KHR_texture_transform` extension. Set this to false if
* texture coordinates will be transformed another way, such as in a vertex
* shader.
*/
bool applyTextureTransform = true;
};
/**
* @brief Defines the fog density at a certain height.
*
* @see TilesetOptions::fogDensityTable
*/
struct CESIUM3DTILESSELECTION_API FogDensityAtHeight {
/** @brief The height. */
double cameraHeight;
/** @brief The fog density. */
double fogDensity;
};
/**
* @brief Additional options for configuring a {@link Tileset}.
*/
struct CESIUM3DTILESSELECTION_API TilesetOptions {
/**
* @brief A credit text for this tileset, if needed.
*/
std::optional<std::string> credit;
/**
* @brief Whether or not to display tileset's credits on the screen.
*/
bool showCreditsOnScreen = false;
/**
* @brief The maximum number of pixels of error when rendering this tileset.
* This is used to select an appropriate level-of-detail.
*
* When a tileset uses the older layer.json / quantized-mesh format rather
* than 3D Tiles, this value is effectively divided by 8.0. So the default
* value of 16.0 corresponds to the standard value for quantized-mesh terrain
* of 2.0.
*/
double maximumScreenSpaceError = 16.0;
/**
* @brief The maximum number of tiles that may simultaneously be in the
* process of loading.
*/
uint32_t maximumSimultaneousTileLoads = 20;
/**
* @brief The maximum number of subtrees that may simultaneously be in the
* process of loading.
*/
uint32_t maximumSimultaneousSubtreeLoads = 20;
/**
* @brief Indicates whether the ancestors of rendered tiles should be
* preloaded. Setting this to true optimizes the zoom-out experience and
* provides more detail in newly-exposed areas when panning. The down side is
* that it requires loading more tiles.
*/
bool preloadAncestors = true;
/**
* @brief Indicates whether the siblings of rendered tiles should be
* preloaded. Setting this to true causes tiles with the same parent as a
* rendered tile to be loaded, even if they are culled. Setting this to true
* may provide a better panning experience at the cost of loading more tiles.
*/
bool preloadSiblings = true;
/**
* @brief The number of loading descendant tiles that is considered "too
* many". If a tile has too many loading descendants, that tile will be loaded
* and rendered before any of its descendants are loaded and rendered. This
* means more feedback for the user that something is happening at the cost of
* a longer overall load time. Setting this to 0 will cause each tile level to
* be loaded successively, significantly increasing load time. Setting it to a
* large number (e.g. 1000) will minimize the number of tiles that are loaded
* but tend to make detail appear all at once after a long wait.
*/
uint32_t loadingDescendantLimit = 20;
/**
* @brief Never render a tileset with missing tiles.
*
* When true, the tileset will guarantee that the tileset will never be
* rendered with holes in place of tiles that are not yet loaded. It does this
* by refusing to refine a parent tile until all of its child tiles are ready
* to render. Thus, when the camera moves, we will always have something -
* even if it's low resolution - to render any part of the tileset that
* becomes visible. When false, overall loading will be faster, but
* newly-visible parts of the tileset may initially be blank.
*/
bool forbidHoles = false;
/**
* @brief Enable culling of tiles against the frustum.
*/
bool enableFrustumCulling = true;
/**
* @brief Enable culling of occluded tiles, as reported by the renderer.
*/
bool enableOcclusionCulling = true;
/**
* @brief Wait to refine until the occlusion state of a tile is known.
*
* Only applicable when enableOcclusionInfo is true. Enabling this option may
* cause a small delay between when a tile is needed according to the SSE and
* when the tile load is kicked off. On the other hand, this delay could
* allow the occlusion system to avoid loading a tile entirely if it is
* found to be unnecessary a few frames later.
*/
bool delayRefinementForOcclusion = true;
/**
* @brief Enable culling of tiles that cannot be seen through atmospheric fog.
*/
bool enableFogCulling = true;
/**
* @brief Whether culled tiles should be refined until they meet
* culledScreenSpaceError.
*
* When true, any culled tile from a disabled culling stage will be refined
* until it meets the specified culledScreenSpaceError. Otherwise, its
* screen-space error check will be disabled altogether and it will not bother
* to refine any futher.
*/
bool enforceCulledScreenSpaceError = true;
/**
* @brief The screen-space error to refine until for culled tiles from
* disabled culling stages.
*
* When enforceCulledScreenSpaceError is true, culled tiles from disabled
* culling stages will be refined until they meet this screen-space error
* value.
*/
double culledScreenSpaceError = 64.0;
/**
* @brief The maximum number of bytes that may be cached.
*
* Note that this value, even if 0, will never
* cause tiles that are needed for rendering to be unloaded. However, if the
* total number of loaded bytes is greater than this value, tiles will be
* unloaded until the total is under this number or until only required tiles
* remain, whichever comes first.
*/
int64_t maximumCachedBytes = 512 * 1024 * 1024;
/**
* @brief A table that maps the camera height above the ellipsoid to a fog
* density. Tiles that are in full fog are culled. The density of the fog
* increases as this number approaches 1.0 and becomes less dense as it
* approaches zero. The more dense the fog is, the more aggressively the tiles
* are culled. For example, if the camera is a height of 1000.0m above the
* ellipsoid, increasing the value to 3.0e-3 will cause many tiles close to
* the viewer be culled. Decreasing the value will push the fog further from
* the viewer, but decrease performance as more of the tiles are rendered.
* Tiles are culled when `1.0 - glm::exp(-(distance * distance * fogDensity *
* fogDensity))` is >= 1.0.
*/
std::vector<FogDensityAtHeight> fogDensityTable = {
{359.393, 2.0e-5}, {800.749, 2.0e-4}, {1275.6501, 1.0e-4},
{2151.1192, 7.0e-5}, {3141.7763, 5.0e-5}, {4777.5198, 4.0e-5},
{6281.2493, 3.0e-5}, {12364.307, 1.9e-5}, {15900.765, 1.0e-5},
{49889.0549, 8.5e-6}, {78026.8259, 6.2e-6}, {99260.7344, 5.8e-6},
{120036.3873, 5.3e-6}, {151011.0158, 5.2e-6}, {156091.1953, 5.1e-6},
{203849.3112, 4.2e-6}, {274866.9803, 4.0e-6}, {319916.3149, 3.4e-6},
{493552.0528, 2.6e-6}, {628733.5874, 2.2e-6}, {1000000.0, 0.0}};
/**
* @brief Whether to render tiles directly under the camera, even if they're
* not in the view frustum.
*
* This is useful for detecting the camera's collision with terrain and other
* models. NOTE: This option currently only works with tiles that use a
* `region` as their bounding volume. It is ignored for other bounding volume
* types.
*/
bool renderTilesUnderCamera = true;
/**
* @brief A list of interfaces that are given an opportunity to exclude tiles
* from loading and rendering. If any of the excluders indicate that a tile
* should not be loaded, it will not be loaded.
*/
std::vector<std::shared_ptr<ITileExcluder>> excluders;
/**
* @brief A callback function that is invoked when a tileset resource fails to
* load.
*
* Tileset resources include a Cesium ion asset endpoint, a tileset's root
* tileset.json or layer.json, an individual tile's content, or an implicit
* tiling subtree.
*/
std::function<void(const TilesetLoadFailureDetails&)> loadErrorCallback;
/**
* @brief Whether to keep tiles loaded during a transition period when
* switching to a different LOD tile.
*
* For each tile, {@link TileRenderContent::getLodTransitionFadePercentage} will
* indicate to the client how faded to render the tile throughout the
* transition. Tile fades can be used to mask LOD transitions and make them
* appear less abrupt and jarring.
*/
bool enableLodTransitionPeriod = false;
/**
* @brief How long it should take to transition between tiles of different
* LODs, in seconds.
*
* When a tile refines or unrefines to a higher or lower LOD tile, a fade
* can optionally be applied to smooth the transition. This value determines
* how many seconds the whole transition should take. Note that the old tile
* doesn't start fading out until the new tile fully fades in.
*/
float lodTransitionLength = 1.0f;
/**
* @brief Whether to kick descendants while a tile is still fading in.
*
* This does not delay loading of descendants, but it keeps them off the
* render list while the tile is fading in. If this is false, the tile
* currently fading in will pop in to full opacity if descendants are
* rendered (this counteracts the benefits of LOD transition blending).
*
*/
bool kickDescendantsWhileFadingIn = true;
/**
* @brief A soft limit on how long (in milliseconds) to spend on the
* main-thread part of tile loading each frame (each call to
* Tileset::updateView). A value of 0.0 indicates that all pending
* main-thread loads should be completed each tick.
*
* Setting this to too low of a value will impede overall tile load progress,
* creating a discernable load latency.
*/
double mainThreadLoadingTimeLimit = 0.0;
/**
* @brief A soft limit on how long (in milliseconds) to spend unloading
* cached tiles each frame (each call to Tileset::updateView). A value of 0.0
* indicates that the tile cache should not throttle unloading tiles.
*/
double tileCacheUnloadTimeLimit = 0.0;
/**
* @brief Options for configuring the parsing of a {@link Tileset}'s content
* and construction of Gltf models.
*/
TilesetContentOptions contentOptions;
/**
* @brief Arbitrary data that will be passed to {@link IPrepareRendererResources::prepareInLoadThread}.
*
* This object is copied and given to tile preparation threads,
* so it must be inexpensive to copy.
*/
std::any rendererOptions;
/**
* @brief The ellipsoid to use for this tileset.
* This value shouldn't be changed after the tileset is constructed. If you
* need to change a tileset's ellipsoid, please recreate the tileset.
*
* If no ellipsoid is set, Ellipsoid::WGS84 will be used by default.
*/
CesiumGeospatial::Ellipsoid ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,22 @@
#pragma once
#include <CesiumGltfReader/GltfSharedAssetSystem.h>
namespace Cesium3DTilesSelection {
/**
* @brief Contains assets that are potentially shared across multiple Tilesets.
*/
class TilesetSharedAssetSystem
: public CesiumGltfReader::GltfSharedAssetSystem {
public:
/**
* @brief Obtains an `IntrusivePointer` to the `TilesetSharedAssetSystem`
* singleton.
*/
static CesiumUtility::IntrusivePointer<TilesetSharedAssetSystem> getDefault();
virtual ~TilesetSharedAssetSystem() = default;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,182 @@
#pragma once
#include "BoundingVolume.h"
#include "Library.h"
#include <CesiumGeometry/CullingVolume.h>
#include <CesiumGeometry/Plane.h>
#include <CesiumGeospatial/Cartographic.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <glm/mat3x3.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <vector>
namespace Cesium3DTilesSelection {
/**
* @brief The state of the view that is used during the traversal of a tileset.
*
* An instance of a view state can be created with the {@link create} function.
*/
class CESIUM3DTILESSELECTION_API ViewState final {
// TODO: Add support for orthographic and off-center perspective frustums
public:
/**
* @brief Creates a new instance of a view state.
*
* @param position The position of the eye point of the camera.
* @param direction The view direction vector of the camera.
* @param up The up vector of the camera.
* @param viewportSize The size of the viewport, in pixels.
* @param horizontalFieldOfView The horizontal field-of-view (opening)
* angle of the camera, in radians.
* @param verticalFieldOfView The vertical field-of-view (opening)
* angle of the camera, in radians.
* @param ellipsoid The ellipsoid that will be used to compute the
* {@link ViewState#getPositionCartographic cartographic position}
* from the cartesian position.
* Default value: {@link CesiumGeospatial::Ellipsoid::WGS84}.
*/
static ViewState create(
const glm::dvec3& position,
const glm::dvec3& direction,
const glm::dvec3& up,
const glm::dvec2& viewportSize,
double horizontalFieldOfView,
double verticalFieldOfView,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Gets the position of the camera in Earth-centered, Earth-fixed
* coordinates.
*/
const glm::dvec3& getPosition() const noexcept { return this->_position; }
/**
* @brief Gets the look direction of the camera in Earth-centered, Earth-fixed
* coordinates.
*/
const glm::dvec3& getDirection() const noexcept { return this->_direction; }
/**
* @brief Gets the up direction of the camera in Earth-centered, Earth-fixed
* coordinates.
*/
const glm::dvec3& getUp() const noexcept { return this->_up; }
/**
* @brief Gets the position of the camera as a longitude / latitude / height.
*
* The result may be `std::nullopt` if the Cartesian position is
* very near the center of the Ellipsoid.
*/
const std::optional<CesiumGeospatial::Cartographic>&
getPositionCartographic() const noexcept {
return this->_positionCartographic;
}
/**
* @brief Gets the size of the viewport in pixels.
*/
const glm::dvec2& getViewportSize() const noexcept {
return this->_viewportSize;
}
/**
* @brief Gets the horizontal field-of-view angle in radians.
*/
double getHorizontalFieldOfView() const noexcept {
return this->_horizontalFieldOfView;
}
/**
* @brief Gets the vertical field-of-view angle in radians.
*/
double getVerticalFieldOfView() const noexcept {
return this->_verticalFieldOfView;
}
/**
* @brief Returns whether the given {@link BoundingVolume} is visible for this
* camera
*
* Returns whether the given bounding volume is visible for this camera,
* meaning that the given volume is at least partially contained in
* the frustum of this camera.
*
* @return Whether the bounding volume is visible
*/
bool
isBoundingVolumeVisible(const BoundingVolume& boundingVolume) const noexcept;
/**
* @brief Computes the squared distance to the given {@link BoundingVolume}.
*
* Computes the squared euclidean distance from the position of this camera
* to the closest point of the given bounding volume.
*
* @param boundingVolume The bounding volume
* @returns The squared distance
*/
double computeDistanceSquaredToBoundingVolume(
const BoundingVolume& boundingVolume) const noexcept;
/**
* @brief Computes the screen space error from a given geometric error
*
* Computes the screen space error (SSE) that results from the given
* geometric error, when it is viewed with this camera from the given
* distance.
*
* The given distance will be clamped to a small positive value if
* it is negative or too close to zero.
*
* @param geometricError The geometric error
* @param distance The viewing distance
* @return The screen space error
*/
double computeScreenSpaceError(double geometricError, double distance)
const noexcept;
private:
/**
* @brief Creates a new instance.
*
* @param position The position of the eye point of the camera.
* @param direction The view direction vector of the camera.
* @param up The up vector of the camera.
* @param viewportSize The size of the viewport, in pixels.
* @param horizontalFieldOfView The horizontal field-of-view (opening)
* angle of the camera, in radians.
* @param verticalFieldOfView The vertical field-of-view (opening)
* angle of the camera, in radians.
*/
ViewState(
const glm::dvec3& position,
const glm::dvec3& direction,
const glm::dvec3& up,
const glm::dvec2& viewportSize,
double horizontalFieldOfView,
double verticalFieldOfView,
const std::optional<CesiumGeospatial::Cartographic>& positionCartographic,
const CesiumGeospatial::Ellipsoid& ellipsoid);
const glm::dvec3 _position;
const glm::dvec3 _direction;
const glm::dvec3 _up;
const glm::dvec2 _viewportSize;
const double _horizontalFieldOfView;
const double _verticalFieldOfView;
const CesiumGeospatial::Ellipsoid _ellipsoid;
const double _sseDenominator;
const std::optional<CesiumGeospatial::Cartographic> _positionCartographic;
const CullingVolume _cullingVolume;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,91 @@
#pragma once
#include "Library.h"
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace Cesium3DTilesSelection {
class Tile;
/**
* @brief Reports the results of {@link Tileset::updateView}.
*
* Users of a {@link Tileset} will call {@link Tileset::updateView} and receive
* this structure so that they can update the state of their rendering system
* accordingly. The tileset will internally keep track the current state of the
* tiles as their {@link Tile::getLastSelectionState} throughout the rendering
* process, and use this structure to provide information about the state
* changes of tiles to clients.
*/
class CESIUM3DTILESSELECTION_API ViewUpdateResult final {
public:
/**
* @brief The tiles that were selected by the tileset traversal this frame.
* These tiles should be rendered by the client.
*
* Tiles in this list may be fading in if
* {@link TilesetOptions::enableLodTransitionPeriod} is true.
*/
std::vector<Tile*> tilesToRenderThisFrame;
/**
* @brief Tiles on this list are no longer selected for rendering.
*
* If {@link TilesetOptions::enableLodTransitionPeriod} is true they may be
* fading out. If a tile's {TileRenderContent::lodTransitionPercentage} is 0
* or lod transitions are disabled, the tile should be hidden right away.
*/
std::unordered_set<Tile*> tilesFadingOut;
/**
* @brief The number of tiles in the worker thread load queue.
*/
int32_t workerThreadTileLoadQueueLength = 0;
/**
* @brief The number of tiles in the main thread load queue.
*/
int32_t mainThreadTileLoadQueueLength = 0;
/**
* @brief The number of tiles visited during tileset traversal this frame.
*/
uint32_t tilesVisited = 0;
/**
* @brief The number of culled tiles visited during tileset traversal this
* frame.
*/
uint32_t culledTilesVisited = 0;
/**
* @brief The number of tiles that were skipped because they were culled
* during tileset traversal this frame.
*/
uint32_t tilesCulled = 0;
/**
* @brief The number of tiles occluded this frame.
*/
uint32_t tilesOccluded = 0;
/**
* @brief The number of tiles still waiting to obtain a \ref
* TileOcclusionState this frame.
*/
uint32_t tilesWaitingForOcclusionResults = 0;
/**
* @brief The number of tiles kicked from the render list this frame.
*/
uint32_t tilesKicked = 0;
/**
* @brief The maximum depth of the tile tree visited this frame.
*/
uint32_t maxDepthVisited = 0;
/**
* @brief The frame number. This is incremented every time \ref
* Tileset::updateView is called.
*/
int32_t frameNumber = 0;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,13 @@
#pragma once
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <spdlog/spdlog.h>
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif