Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
- `RasterOverlay::createTileProvider` now receives a reference to `CreateRasterOverlayTileProviderParameters` instead of a large number of individual parameters.
- The constructor parameters for `RasterOverlayTileProvider` and `QuadtreeRasterOverlayTileProvider` have changed.
- The `getCredit` method has been removed from `RasterOverlayCreditProvider`. Use `getCredits` instead.
- Changed the converters in Cesium3DTilesContent to call `GltfReader::readGltfAndExternalData`. The `TilesetContentManger` does not call `resolveExternalData` anymore.

##### Additions :tada:

- Added the concept of a `CreditSource`. Every `Credit` in a `CreditSystem` has a source, and these can be mapped back to `Tileset` and `RasterOverlayTileProvider` (via their `getCreditSource` methods) in order to determine which dataset created which credits.
- Added `TilesetViewGroup::isCreditReferenced`, which can be used to determine if a particular view group references a particular `Credit`.
- Added `CreditReferencer::isCreditReferenced`, which can be used to determine if the referencer is currently referencing a particular `Credit`.
- `CreditSystem::getSnapshot` now takes an optional parameter specifying if and how to filter `Credits` with identical HTML strings.
- Added `CesiumGltfReader::readGltfAndExternalData`, which reads any external data before postprocessing, which includes draco and meshopt decompression.

##### Fixes :wrench:

Expand Down
23 changes: 21 additions & 2 deletions Cesium3DTilesContent/src/BinaryToGltfConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,26 @@ CesiumAsync::Future<GltfConverterResult> BinaryToGltfConverter::convert(
const std::span<const std::byte>& gltfBinary,
const CesiumGltfReader::GltfReaderOptions& options,
const AssetFetcher& assetFetcher) {
return assetFetcher.asyncSystem.createResolvedFuture(
convertImmediate(gltfBinary, options, assetFetcher));
return _gltfReader
.readGltfAndExternalData(
gltfBinary,
assetFetcher.asyncSystem,
assetFetcher.requestHeaders,
assetFetcher.pAssetAccessor,
assetFetcher.baseUrl,
options)
.thenInWorkerThread(
[&assetFetcher](CesiumGltfReader::GltfReaderResult&& gltfResult) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not safe to lambda capture a parameter by reference if the lambda will outlive the function invocation.

if (gltfResult.model) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the convertImmediate function above has been inlined here, so it can be removed.

gltfResult.model->extras["gltfUpAxis"] =
static_cast<std::underlying_type_t<CesiumGeometry::Axis>>(
assetFetcher.upAxis);
}
GltfConverterResult result;
result.model = std::move(gltfResult.model);
result.errors.errors = std::move(gltfResult.errors);
result.errors.warnings = std::move(gltfResult.warnings);
return result;
});
}
} // namespace Cesium3DTilesContent
48 changes: 11 additions & 37 deletions Cesium3DTilesContent/src/I3dmToGltfConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <Cesium3DTilesContent/GltfConverters.h>
#include <Cesium3DTilesContent/I3dmToGltfConverter.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/HttpHeaders.h>
#include <CesiumGeospatial/LocalHorizontalCoordinateSystem.h>
#include <CesiumGltf/Accessor.h>
#include <CesiumGltf/AccessorUtility.h>
Expand Down Expand Up @@ -642,42 +641,17 @@ CesiumAsync::Future<ConvertedI3dm> convertI3dmContent(
}
};

return getGltf()
.thenImmediately([options, assetFetcher, baseUri](
GltfConverterResult&& converterResult) {
if (converterResult.model.has_value()) {
CesiumGltfReader::GltfReaderResult readerResult{
std::move(converterResult.model),
{},
{}};
CesiumAsync::HttpHeaders externalRequestHeaders(
assetFetcher.requestHeaders.begin(),
assetFetcher.requestHeaders.end());
return CesiumGltfReader::GltfReader::resolveExternalData(
assetFetcher.asyncSystem,
baseUri,
externalRequestHeaders,
assetFetcher.pAssetAccessor,
options,
std::move(readerResult));
}
return assetFetcher.asyncSystem.createResolvedFuture(
CesiumGltfReader::GltfReaderResult{
std::nullopt,
std::move(converterResult.errors.errors),
{}});
})
.thenImmediately(
[convertedI3dm = std::move(convertedI3dm)](
CesiumGltfReader::GltfReaderResult&& readerResult) mutable {
if (readerResult.model)
convertedI3dm.gltfResult.model = std::move(readerResult.model);
CesiumUtility::ErrorList resolvedExternalErrors{
std::move(readerResult.errors),
{}};
convertedI3dm.gltfResult.errors.merge(resolvedExternalErrors);
return std::move(convertedI3dm);
});
return getGltf().thenImmediately(
[convertedI3dm = std::move(convertedI3dm)](
GltfConverterResult&& converterResult) mutable {
if (converterResult.model)
convertedI3dm.gltfResult.model = std::move(converterResult.model);
CesiumUtility::ErrorList converterErrors{
std::move(converterResult.errors.errors),
std::move(converterResult.errors.warnings)};
convertedI3dm.gltfResult.errors.merge(converterErrors);
return std::move(convertedI3dm);
});
}

glm::dmat4
Expand Down
83 changes: 6 additions & 77 deletions Cesium3DTilesSelection/src/TilesetContentManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
#include <CesiumGeospatial/Projection.h>
#include <CesiumGltf/Image.h>
#include <CesiumGltfContent/GltfUtilities.h>
#include <CesiumGltfReader/GltfReader.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayDetails.h>
Expand Down Expand Up @@ -566,85 +565,16 @@ postProcessContentInWorkerThread(
result.state == TileLoadResultState::Success &&
"This function requires result to be success");

CesiumGltf::Model& model = std::get<CesiumGltf::Model>(result.contentKind);

// Download any external image or buffer urls in the gltf if there are any
CesiumGltfReader::GltfReaderResult gltfResult{std::move(model), {}, {}};

CesiumAsync::HttpHeaders requestHeaders;
std::string baseUrl;
if (result.pCompletedRequest) {
requestHeaders = result.pCompletedRequest->headers();
baseUrl = result.pCompletedRequest->url();
}

CesiumGltfReader::GltfReaderOptions gltfOptions;
gltfOptions.ktx2TranscodeTargets =
tileLoadInfo.contentOptions.ktx2TranscodeTargets;
gltfOptions.applyTextureTransform =
tileLoadInfo.contentOptions.applyTextureTransform;
if (tileLoadInfo.pSharedAssetSystem) {
gltfOptions.pSharedAssetSystem = tileLoadInfo.pSharedAssetSystem;
}

std::optional<int64_t> version =
pGltfModifier ? pGltfModifier->getCurrentVersion() : std::nullopt;

auto asyncSystem = tileLoadInfo.asyncSystem;
auto pAssetAccessor = result.pAssetAccessor;
return CesiumGltfReader::GltfReader::resolveExternalData(
asyncSystem,
baseUrl,
requestHeaders,
pAssetAccessor,
gltfOptions,
std::move(gltfResult))
.thenInWorkerThread([result = std::move(result),
projections = std::move(projections),
tileLoadInfo = std::move(tileLoadInfo),
version,
pGltfModifier](CesiumGltfReader::GltfReaderResult&&
gltfResult) mutable {
if (!gltfResult.errors.empty()) {
if (result.pCompletedRequest) {
SPDLOG_LOGGER_ERROR(
tileLoadInfo.pLogger,
"Failed resolving external glTF buffers from {}:\n- {}",
result.pCompletedRequest->url(),
CesiumUtility::joinToString(gltfResult.errors, "\n- "));
} else {
SPDLOG_LOGGER_ERROR(
tileLoadInfo.pLogger,
"Failed resolving external glTF buffers:\n- {}",
CesiumUtility::joinToString(gltfResult.errors, "\n- "));
}
}

if (!gltfResult.warnings.empty()) {
if (result.pCompletedRequest) {
SPDLOG_LOGGER_WARN(
tileLoadInfo.pLogger,
"Warning when resolving external gltf buffers from "
"{}:\n- {}",
result.pCompletedRequest->url(),
CesiumUtility::joinToString(gltfResult.warnings, "\n- "));
} else {
SPDLOG_LOGGER_ERROR(
tileLoadInfo.pLogger,
"Warning resolving external glTF buffers:\n- {}",
CesiumUtility::joinToString(gltfResult.warnings, "\n- "));
}
}

if (!gltfResult.model) {
return tileLoadInfo.asyncSystem
.createResolvedFuture(TileLoadResult::createFailedResult(
result.pAssetAccessor,
nullptr))
.thenPassThrough(std::move(tileLoadInfo));
}

result.contentKind = std::move(*gltfResult.model);
return asyncSystem
.runInWorkerThread([result = std::move(result),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

postProcessContentInWorkerThread is already running in a worker thread, so this runInWorkerThread is unnecessary.

projections = std::move(projections),
tileLoadInfo = std::move(tileLoadInfo),
version,
pGltfModifier]() mutable {
result.initialBoundingVolume = tileLoadInfo.tileBoundingVolume;
result.initialContentBoundingVolume =
tileLoadInfo.tileContentBoundingVolume;
Expand All @@ -653,7 +583,6 @@ postProcessContentInWorkerThread(
result,
std::move(projections),
tileLoadInfo);

if (pGltfModifier && version) {
// Apply the glTF modifier right away, otherwise it will be
// triggered immediately after the renderer-side resources
Expand Down
41 changes: 33 additions & 8 deletions Cesium3DTilesSelection/test/TestTilesetContentManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Math.h>
#include <CesiumUtility/Uri.h>

#include <doctest/doctest.h>
#include <glm/common.hpp>
Expand Down Expand Up @@ -898,8 +899,9 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
SUBCASE("Resolve external buffers") {
// create mock loader
CesiumGltfReader::GltfReader gltfReader;
std::vector<std::byte> gltfBoxFile =
readFile(testDataPath / "gltf" / "box" / "Box.gltf");
std::filesystem::path boxPath = testDataPath / "gltf" / "box";
std::string fileName = (boxPath / "Box.gltf").string();
std::vector<std::byte> gltfBoxFile = readFile(fileName);
auto modelReadResult = gltfReader.readGltf(gltfBoxFile);

// check that this model has external buffer and it's not loaded
Expand All @@ -916,9 +918,37 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
CHECK(buffer.cesium.data.size() == 0);
}

// add external buffer to the completed request
std::filesystem::path binPath = boxPath / "Box0.bin";
pMockedAssetAccessor->mockCompletedRequests.insert(
{Uri::nativePathToUriPath(binPath.string()),
createMockRequest(binPath)});

// Load the model and resolve external content
CesiumGltf::Model tileModel;
{
auto future =
gltfReader
.readGltfAndExternalData(
gltfBoxFile,
asyncSystem,
CesiumAsync::HttpHeaders(),
pMockedAssetAccessor,
Uri::nativePathToUriPath(fileName))
.thenInMainThread(
[&tileModel](
CesiumGltfReader::GltfReaderResult&& externalReadResult) {
CHECK(externalReadResult.errors.empty());
CHECK(externalReadResult.warnings.empty());
CHECK(externalReadResult.model);
tileModel = std::move(*externalReadResult.model);
return externalReadResult;
});
future.waitInMainThread();
}
auto pMockedLoader = std::make_unique<SimpleTilesetContentLoader>();
pMockedLoader->mockLoadTileContent = {
std::move(*modelReadResult.model),
std::move(tileModel),
CesiumGeometry::Axis::Y,
std::nullopt,
std::nullopt,
Expand All @@ -930,11 +960,6 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
Ellipsoid::WGS84};
pMockedLoader->mockCreateTileChildren = {{}, TileLoadResultState::Failed};

// add external buffer to the completed request
pMockedAssetAccessor->mockCompletedRequests.insert(
{"Box0.bin",
createMockRequest(testDataPath / "gltf" / "box" / "Box0.bin")});

// create tile
auto pRootTile = std::make_unique<Tile>(pMockedLoader.get());

Expand Down
40 changes: 40 additions & 0 deletions CesiumGltfReader/include/CesiumGltfReader/GltfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,46 @@ class CESIUMGLTFREADER_API GltfReader {
const std::span<const std::byte>& data,
const GltfReaderOptions& options = GltfReaderOptions()) const;

/**
* @brief Read a glTF or binary glTF (GLB) from a buffer and then resolve
* external references.
*
* @param data The data bytes of the glTF file
* @param asyncSystem The async system to use for resolving external data.
* @param headers http headers needed to make the request.
* @param pAssetAccessor The asset accessor to use to make the necessary
* requests.
* @param baseUrl The url to which all external urls are relative
* @param options Options for how to read the glTF.
*/
CesiumAsync::Future<GltfReaderResult> readGltfAndExternalData(
const std::span<const std::byte>& data,
const CesiumAsync::AsyncSystem& asyncSystem,
const CesiumAsync::HttpHeaders& headers,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& baseUrl = {},
const GltfReaderOptions& options = GltfReaderOptions()) const;

/**
* @brief Read a glTF or binary glTF (GLB) from a buffer and then resolve
* external references.
*
* @param data The data bytes of the glTF file
* @param asyncSystem The async system to use for resolving external data.
* @param headers http headers needed to make the request.
* @param pAssetAccessor The asset accessor to use to make the necessary
* requests.
* @param baseUrl The url to which all external urls are relative
* @param options Options for how to read the glTF.
*/
CesiumAsync::Future<GltfReaderResult> readGltfAndExternalData(
const std::span<const std::byte>& data,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& baseUrl = {},
const GltfReaderOptions& options = GltfReaderOptions()) const;

/**
* @brief Reads a glTF or binary glTF file from a URL and resolves external
* buffers and images.
Expand Down
47 changes: 47 additions & 0 deletions CesiumGltfReader/src/GltfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,53 @@ GltfReaderResult GltfReader::readGltf(
return result;
}

CesiumAsync::Future<GltfReaderResult> GltfReader::readGltfAndExternalData(
const std::span<const std::byte>& data,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& baseUrl,
const GltfReaderOptions& options) const {
CesiumAsync::HttpHeaders httpHeaders;
httpHeaders.insert(headers.begin(), headers.end());
Copy link
Member

@kring kring Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
CesiumAsync::HttpHeaders httpHeaders;
httpHeaders.insert(headers.begin(), headers.end());
CesiumAsync::HttpHeaders httpHeaders(headers.begin(), headers.end());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kring for fixing my broken code!

return readGltfAndExternalData(
data,
asyncSystem,
httpHeaders,
pAssetAccessor,
baseUrl,
options);
}

CesiumAsync::Future<GltfReaderResult> GltfReader::readGltfAndExternalData(
const std::span<const std::byte>& data,
const CesiumAsync::AsyncSystem& asyncSystem,
const CesiumAsync::HttpHeaders& headers,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& baseUrl,
const GltfReaderOptions& options) const {

const CesiumJsonReader::JsonReaderOptions& context = this->getExtensions();
GltfReaderResult result = isBinaryGltf(data) ? readBinaryGltf(context, data)
: readJsonGltf(context, data);

if (!result.model) {
return asyncSystem.createResolvedFuture(std::move(result));
}

return resolveExternalData(
asyncSystem,
baseUrl,
headers,
pAssetAccessor,
options,
std::move(result))
.thenInWorkerThread([options](GltfReaderResult&& result) {
postprocess(result, options);
return std::move(result);
});
}

CesiumAsync::Future<GltfReaderResult> GltfReader::loadGltf(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::string& uri,
Expand Down
2 changes: 1 addition & 1 deletion cmake/macros/configure_cesium_library.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ function(configure_cesium_library targetName)
if (MSVC)
target_compile_options(${targetName} PRIVATE /W4 /WX /wd4201 /bigobj /w45038 /w44254 /w44242 /w44191 /w45220)
else()
target_compile_options(${targetName} PRIVATE -Werror -Wall -Wextra -Wconversion -Wpedantic -Wshadow -Wsign-conversion -Wno-unknown-pragmas)
target_compile_options(${targetName} PRIVATE -Wall -Wextra -Wconversion -Wpedantic -Wshadow -Wsign-conversion -Wno-unknown-pragmas)
endif()

set_target_properties(${targetName} PROPERTIES
Expand Down
Loading