Compare commits

...

303 Commits

Author SHA1 Message Date
Janine Liu 5b3b30e0d9
Merge pull request #1276 from CesiumGS/removed-credit-sources
cesium-native / Quick Checks (push) Has been cancelled Details
cesium-native / Linting (push) Has been cancelled Details
cesium-native / Documentation (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (Debug, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (RelWithDebInfo, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 3.1.39) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 4.0.13) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (64, 4.0.13) (push) Has been cancelled Details
Report credits with destroyed source as removed.
2025-11-25 10:30:10 -05:00
Ashley Rogers 5745cb84b4
Merge pull request #1279 from CesiumGS/more-emscripten
More build system improvements to support Cesium for Unity on the web
2025-11-25 10:25:34 -05:00
Ashley Rogers 8666d2a783
Merge pull request #1278 from CesiumGS/install-zlib
Install zlib when CesiumCurl is enabled
2025-11-25 10:20:25 -05:00
Kevin Ring 2685803664 Build system improvements.
1. Set CMAKE_FIND_ROOT_PATH. The vcpkg toolchain will do this itself if
   it manages to run, but it won't run when cesium-native's
   CMakeLists.txt is add_subdirectory'd into another project. By making
   sure this is set, we avoid the need to set `CMAKE_FIND_ROOT_PATH_MODE_LIBRARY`
   similar to `BOTH` when cross-compiling in order for the build to be
   able to find the vcpkg dependencies.
2. Detect the triplet as `wasm32-emscripten` if _either_
   `CESIUM_TARGET_WASM` is explicitly set, or the `CMAKE_SYSTEM_NAME` is
   `Emscripten`. This is important when detect-vspkg-triplet is invoked
   from outside cesium-native's top-level CMakeLists.txt (e.g., when
   cesium-native is included via add_subdirectory).
2025-11-25 19:14:31 +11:00
Kevin Ring dc53b707d1 Install zlib when CesiumCurl is enabled. 2025-11-25 14:05:13 +11:00
Kevin Ring b497c643ae Report credits with destroyed source as removed. 2025-11-25 12:32:08 +11:00
Janine Liu b8b1417c5b
Merge pull request #1273 from CesiumGS/credit-sources
cesium-native / Quick Checks (push) Waiting to run Details
cesium-native / Linting (push) Waiting to run Details
cesium-native / Documentation (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (Debug, windows-2022) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (RelWithDebInfo, windows-2022) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, macos-13) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, ubuntu-22.04) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, gcc, ubuntu-24.04) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, macos-13) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, ubuntu-22.04) (push) Waiting to run Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, gcc, ubuntu-24.04) (push) Waiting to run Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 3.1.39) (push) Waiting to run Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 4.0.13) (push) Waiting to run Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (64, 4.0.13) (push) Waiting to run Details
Add CreditSources to CreditSystem, refactor RasterOverlay::createTileProvider
2025-11-24 11:02:35 -05:00
Kevin Ring 612352eee9 Move TestCreditSystem to CesiumUtility.
cesium-native / Quick Checks (push) Has been cancelled Details
cesium-native / Linting (push) Has been cancelled Details
cesium-native / Documentation (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (Debug, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (RelWithDebInfo, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 3.1.39) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 4.0.13) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (64, 4.0.13) (push) Has been cancelled Details
2025-11-24 13:41:31 +11:00
Kevin Ring f8c4fd7530 Improve misleading comment in test. 2025-11-24 13:36:12 +11:00
Kevin Ring 9aea35d9c3
Fix typo.
Co-authored-by: Janine Liu <32226860+j9liu@users.noreply.github.com>
2025-11-24 13:35:44 +11:00
Kevin Ring 473f3ccbd5 Clang warnings. 2025-11-24 13:30:40 +11:00
Kevin Ring 54b3b5764c Remove changelog entry that didn't actually happen. 2025-11-24 12:29:40 +11:00
Kevin Ring 8f2c1a9f54 Use copydoc to avoid doc duplication. 2025-11-24 12:29:02 +11:00
Kevin Ring 1b4700ecd2 Improve doc. 2025-11-24 12:29:02 +11:00
Kevin Ring a5d8e17300
Improve doc.
Co-authored-by: Janine Liu <32226860+j9liu@users.noreply.github.com>
2025-11-24 12:24:47 +11:00
Kevin Ring 3bff311dee Add tests. 2025-11-24 12:01:16 +11:00
Kevin Ring ed344cddb3 Only filter in favor of referenced credits. 2025-11-24 11:18:28 +11:00
Kevin Ring fbbaf4452a Add tests for credit filtering.
cesium-native / Quick Checks (push) Has been cancelled Details
cesium-native / Linting (push) Has been cancelled Details
cesium-native / Documentation (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (Debug, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (RelWithDebInfo, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 3.1.39) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 4.0.13) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (64, 4.0.13) (push) Has been cancelled Details
2025-11-21 19:18:54 +11:00
Kevin Ring fd8aaef4be Fix Doxygen error. 2025-11-21 18:50:34 +11:00
Kevin Ring 32e509eb95 Simplify removed credit tracking.
There's no good reason this was so complicated before.
2025-11-21 18:01:52 +11:00
Kevin Ring 002d00e3f8 Add credit filtering option to getSnapshot. 2025-11-21 17:41:34 +11:00
Kevin Ring 77fff1077a Re-add useless property.
Removing it probably isn't the right thing, and anyway it doesn't belong
in this PR.
2025-11-20 22:12:40 +11:00
Kevin Ring a8df93e235 clang-tidy 2025-11-20 19:40:08 +11:00
Kevin Ring ced27961e4 Update CHANGES.md. 2025-11-20 19:38:48 +11:00
Kevin Ring 7213eed62f Fix silly logic error. 2025-11-20 19:37:34 +11:00
Kevin Ring 551c2bb67c Doc tweaks. 2025-11-20 19:32:59 +11:00
Kevin Ring 642bd2dbc5 Only override credit source if not specified. 2025-11-20 19:16:42 +11:00
Kevin Ring d6b2882148 Correct ownership and credit source for IonRasterOverlay. 2025-11-20 19:09:52 +11:00
Kevin Ring 8ede044fc4 Remove unused property. 2025-11-20 18:44:48 +11:00
Kevin Ring c1b5107758 Only add non-empty credit. 2025-11-20 18:39:32 +11:00
Kevin Ring 9db1f17a11 Fix doc. 2025-11-20 18:26:19 +11:00
Kevin Ring c3b7c131b3 Revert some unnecessary changes. 2025-11-20 18:24:17 +11:00
Kevin Ring b59305d4dd Fix use of removed function. 2025-11-20 18:18:26 +11:00
Kevin Ring 8c7ee9507a Remove declaration for undefined function, improve doc. 2025-11-20 17:27:08 +11:00
Kevin Ring 65dacbdb1b Options -> Parameters 2025-11-20 17:22:18 +11:00
Kevin Ring 436bb9be86 Factor out some common code. 2025-11-20 16:24:33 +11:00
Kevin Ring 2757937ea2 Improve doc. 2025-11-20 15:48:04 +11:00
Kevin Ring 11cf3812d2 Remove "referenced by raster" stuff from TilesetViewGroup.
We can use CreditSources instead, now.
2025-11-20 12:32:24 +11:00
Kevin Ring 47e8533e18 Use `@ref` instead of `\ref`. 2025-11-20 12:32:02 +11:00
Kevin Ring e4f46ba4e6 Remove unnecessary includes. 2025-11-20 12:29:46 +11:00
Kevin Ring a911d9f0ac clang-tidy 2025-11-20 11:34:52 +11:00
Kevin Ring c71daed34a Better fix for GCC warning. 2025-11-20 11:31:13 +11:00
Kevin Ring cce023d8ae Revert "Fix rather unnecessary GCC warnings."
This reverts commit 8d9fc928bb.
2025-11-20 11:28:49 +11:00
Kevin Ring 8d9fc928bb Fix rather unnecessary GCC warnings. 2025-11-20 11:27:23 +11:00
Kevin Ring e554a7d72c clang-tidy, remove unneeded getOwner function. 2025-11-20 10:24:15 +11:00
Kevin Ring 0b5616089e Default values for RasterOverlayExternals fields. 2025-11-20 10:04:52 +11:00
Kevin Ring 79a1c7bcdc Fixes for doc and 32-bit Emscripten. 2025-11-20 09:14:13 +11:00
Kevin Ring 4cabc9aaf9 Initialize pCreator in tile provider factory. 2025-11-20 09:09:26 +11:00
Kevin Ring d5920f6b5d Unsigned credit id and generation. 2025-11-20 07:58:45 +11:00
Kevin Ring 7a7f50877c Merge remote-tracking branch 'origin/main' into credit-sources 2025-11-20 07:34:16 +11:00
Kevin Ring 2cecdd9c2d createTileProvider refactoring. 2025-11-19 23:17:18 +11:00
Kevin Ring 81bcaf7755 CreditSource for GoogleMapTilesRasterOverlay. 2025-11-19 10:09:32 +11:00
Kevin Ring 541f55289c BingMapsRasterOverlay use CreditSource. 2025-11-19 10:07:45 +11:00
Kevin Ring 7a6f14ae86 Simpler lambda capture. 2025-11-19 09:28:35 +11:00
Tim Moore 3e68c7a1c2
Merge pull request #1272 from rbois-bentley/fix_modp_b64_search
Fix modp_b64 search fail when path to ezvcpkg contains a directory named debug
2025-11-18 19:00:04 +01:00
Kevin Ring 32ced86a1f Start adding CreditSource to raster overlays. 2025-11-18 22:26:28 +11:00
Kevin Ring a6a56c9ecf Tileset creates a CreditSource and associates its credits with it. 2025-11-18 21:27:17 +11:00
Kevin Ring 081906aeff Add some CreditReferencer generation tests. 2025-11-18 20:36:28 +11:00
Kevin Ring 684b9742d2 Generation-aware CreditReferencer. 2025-11-18 19:58:50 +11:00
Kevin Ring 5564afd7cb Initial implementation of credit sources. 2025-11-18 19:40:02 +11:00
Kevin Ring d3fb02136d Merge remote-tracking branch 'origin/main' into dev 2025-11-17 13:01:42 +11:00
Kevin Ring 577ec52e8c Bump to v0.54.0. 2025-11-17 11:30:10 +11:00
Richard Bois 2bdeadac3d When searching for modp_b64 libraries, ignore all presence of /debug in the search paths except those ending with /debug. 2025-11-14 14:45:58 -05:00
Ludovic Cheval 864e328ed8 properly call release method instead of pointer reset 2025-11-13 08:39:09 -05:00
Ludovic Cheval 9fe157b4e2 Correct arg description in doc comment 2025-11-12 09:09:20 -05:00
Ludovic Cheval 2421678466 Correct test 2025-11-12 09:08:48 -05:00
Ludovic Cheval 8271d59a3b Apply formatting 2025-11-11 08:45:15 -05:00
Ludovic Cheval ca5104f3b7 #1260 - Update ViewGroup to check internal references; Introduce new referencer for raster overlay credits; 2025-11-10 11:37:17 -05:00
Ludovic Cheval cefcafd82a #1260 - Add a method in CreditReferencer to check if a credit is referenced or not 2025-11-10 11:36:07 -05:00
Kevin Ring e876c133c8
Merge pull request #1267 from brendan-duncan/experiment_wasm
Add support for building cesium-native with Unity Web
2025-11-10 14:56:17 +11:00
Kevin Ring e9a9415539 Better implementation of hashing on 32/64 bit platforms.
Because function overloading didn't work the way I had hoped.
2025-11-10 14:02:26 +11:00
Kevin Ring d92188a276 Implement 32-bit hash combine function from Boost. 2025-11-10 13:40:33 +11:00
Kevin Ring c5ea386972 Add option to disable libjpeg-turbo.
Instead of always disabling it for Emscripten, when this is really only
need when building for Unity.
2025-11-10 13:16:50 +11:00
Kevin Ring 8b86d7f3d7 Use any version >= 24. 2025-11-07 23:11:03 +11:00
Kevin Ring 3f372f75a4 Use Node 24 for WASM64 tests. 2025-11-07 23:09:57 +11:00
Kevin Ring 77715ec5e5 Fix 64-bit Emscripten build. 2025-11-07 21:04:56 +11:00
Kevin Ring 198c22c795 Fix typo. 2025-11-07 17:00:31 +11:00
Kevin Ring 110382a8db Add WASM64 build. 2025-11-07 16:50:38 +11:00
Kevin Ring d9d31300d7 Clean up and centralize compiler flags. 2025-11-07 16:41:47 +11:00
Kevin Ring 65b6c866a0 clang-tidy 2025-11-06 22:59:14 +11:00
Kevin Ring c96485f98c Yet another attempt at the data build. 2025-11-06 22:53:27 +11:00
Kevin Ring 22e9dbcd7c Remove unnecessary package.json. 2025-11-06 17:53:36 +11:00
Kevin Ring f42203eb71 Improve web server for testing WebAssembly builds. 2025-11-06 17:51:21 +11:00
Kevin Ring ccba7b2510 More coding convention tweaks. 2025-11-06 17:50:48 +11:00
Kevin Ring 00c35a1ffc Remove tidyhtml specific compiler option. 2025-11-06 17:37:32 +11:00
Kevin Ring 221611fd21 Fix data build that I thought was working... 2025-11-06 17:24:39 +11:00
Kevin Ring 58e8bafe71 Remove workaround for tidyhtml.
cesium-native doesn't depend on tidy-html, so a workaround for it
doesn't belong here.
2025-11-06 16:06:19 +11:00
Kevin Ring eecc1e9411 Fix (and simplify) test data embedding. 2025-11-06 15:36:39 +11:00
Kevin Ring 5f06bc153e Minor tweaks for coding conventions. 2025-11-06 15:36:23 +11:00
Kevin Ring b23852cddd Merge remote-tracking branch 'origin/main' into experiment_wasm 2025-11-06 11:10:05 +11:00
Kevin Ring d1bffc5d37 Switch back to main vcpkg repo. 2025-11-06 10:28:31 +11:00
Kevin Ring 1e9f39df84 Add no-dso option on Emscripten 3.1.39. 2025-11-05 22:38:24 +11:00
Kevin Ring de6f08b7bc Try removing openssl overlay port again. 2025-11-05 16:22:06 +11:00
Kevin Ring b33640a9ff Use vcpkg from my PR.
https://github.com/microsoft/vcpkg/pull/48137
2025-11-05 15:52:20 +11:00
Kevin Ring 0aaaee207e Fix hash and feature name. 2025-11-04 20:53:08 +11:00
Kevin Ring 25cc1e89ca Only install [core] features of ktx. 2025-11-04 20:07:25 +11:00
Kevin Ring 16be63a245 Use KTX-Software branch from my PR.
https://github.com/KhronosGroup/KTX-Software/pull/1075
2025-11-04 19:22:58 +11:00
Kevin Ring ab8de592a4 Bump to v0.53.0, update changelog. 2025-11-03 10:33:17 +11:00
Kevin Ring f24824b430
Merge pull request #1256 from CesiumGS/geojson-overlay-fixes
Fix GeoJSON rasterization issues
2025-11-03 10:29:00 +11:00
Kevin Ring bd1d4eee4a clang-format 2025-11-03 10:22:27 +11:00
Kevin Ring 9c3e6f36fe
Update CHANGES.md 2025-11-03 10:21:37 +11:00
Kevin Ring 6fefe44a38 Fix computeNormalizedCoordinates, add a test. 2025-11-03 10:10:44 +11:00
Kevin Ring 5828ffd207 Merge remote-tracking branch 'origin/main' into geojson-overlay-fixes 2025-11-03 09:25:31 +11:00
Kevin Ring 551174f38d Revert "Account for line width when calcuating rectangles"
This reverts commit 55bbae20fe.
2025-11-03 09:13:47 +11:00
Kevin Ring 8d9ee6fee6 Suppress incorrect clang-tidy warning.
As of glm v1.0.2, and at least on Windows, an include of
`<glm/gtc/quaternion.hpp>` is required in order for
`glm::dmat4x4(rotationQuat)` to compile successfully. Otherwise, the
compiler complains about missing `mat4_cast`.

However, clang-tidy does not recognize that this header is required, and
throws a `misc-include-cleaner` warning. Possibly because it's not
required under clang.
2025-10-29 17:00:45 +11:00
Kevin Ring 52e5736b4a Try again to remove blend2d and upgrade vcpkg.
Now that yesterday's vcpkg breakage has been reverted.
2025-10-29 14:49:15 +11:00
Ashley Rogers 216314428b Merge branch 'geojson-overlay-fixes' of github.com:CesiumGS/cesium-native into geojson-overlay-fixes 2025-10-28 14:24:28 -04:00
Ashley Rogers 55bbae20fe Account for line width when calcuating rectangles 2025-10-28 14:24:07 -04:00
Ashley Rogers c0de4d1da8 Re-add seedForObject 2025-10-28 10:40:21 -04:00
Janine Liu afec183d53
Merge pull request #1231 from CesiumGS/gltf-tuner-kring
Further improvements to the GltfModifier in #1186
2025-10-28 09:56:56 -04:00
Kevin Ring 12ee3f00b5 Revert change that was only necessary after glm upgrade. 2025-10-28 20:55:47 +11:00
Kevin Ring 3080d41edb Re-add blend2d overlay port.
This is an exact copy of:
0cb190f991/ports/blend2d
2025-10-28 19:42:04 +11:00
Kevin Ring b800becaae Revert to 2025.09.17 version of vcpkg.
The pre-release version I tried to use to get the blend2d changes has a
problem that causes builds to fail when using the vcpkg executable
included in the GitHub Actions images. See here:
https://github.com/microsoft/vcpkg/pull/30396#issuecomment-3452587376

So I'll restore the blend2d changes as an overlay port in a separate
commit.
2025-10-28 19:38:38 +11:00
Kevin Ring f66d9dacf6 More thorough removal of cpp-httplib from wasm builds. 2025-10-28 19:33:48 +11:00
Kevin Ring 077e9f3765 Disable cpp-httplib on Emscripten, rather than downgrading it.
The older version has known CVEs, and anyway it's only used to host a
web server for an OAuth2 flow, which isn't going to work on the web
anyway.
2025-10-28 18:43:15 +11:00
Kevin Ring 60faebd4d2 Update vcpkg and remove blend2d overlay port.
This vcpkg version is not tagged, but was chosen because it includes
https://github.com/microsoft/vcpkg/pull/47969.
2025-10-28 18:07:33 +11:00
Kevin Ring c9cff48b84 Give TileLoadResult::glTFUpAxis a default value. 2025-10-28 10:58:10 +11:00
Kevin Ring a0e17767a0 Merge remote-tracking branch 'origin/main' into gltf-tuner-kring 2025-10-28 10:44:01 +11:00
Tim Moore 23ab650a0a
Merge pull request #1268 from CesiumGS/trouble-with-triplets
Set VCPKG_TARGET_TRIPLET even when using ezvcpkg
2025-10-27 12:08:49 +01:00
Kevin Ring c913278446 Re-add blend2d overlay port.
This is needed until https://github.com/microsoft/vcpkg/pull/47969 is
merged and we've updated to a vcpkg version that includes it.

Without it, builds with blend2d's `jit` feature disabled (Emscripten)
fail because the package erroneously attempts to
`find_dependency(asmjit)` even when there's no asmjit to find (because
it's disabled).
2025-10-27 18:58:50 +11:00
Kevin Ring 0e53ac995d Remove blend2d overlay.
Once again it doesn't seem to be necessary locally. Let's see what CI
says.
2025-10-27 17:14:08 +11:00
Kevin Ring a93b082380 Remove debug step. 2025-10-27 16:47:21 +11:00
Kevin Ring eaf77c07f1 Don't remove directory.
Even though I'm unclear why it's a problem.
2025-10-27 16:43:04 +11:00
Kevin Ring 84a9574e7b Don't let actions/checkout delete everything. 2025-10-27 16:39:38 +11:00
Kevin Ring 5e554d3638 More github actions flailing. 2025-10-27 16:34:16 +11:00
Kevin Ring e390112e21 Debug failed sparse checkout. 2025-10-27 16:32:07 +11:00
Kevin Ring 97883d1451 Add overlay port for ada-url in Emscripten 3.1.39 build. 2025-10-27 16:24:14 +11:00
Kevin Ring a72529e9cb Add Emscripten 4.0.13 build. 2025-10-27 15:44:36 +11:00
Kevin Ring 000d27cbb1 Remove ada-url overlay port.
It doesn't seem to be doing anything useful, and the build is fine
locally without it.
2025-10-27 14:48:48 +11:00
Kevin Ring d0427df530 Consistently use non-header-only spdlog. 2025-10-27 11:17:24 +11:00
Kevin Ring f6d83fb859 Fix dodgy store of a reference to a temporary in a global variable.
This appears to have been the cause of the crash in the WebAssembly
tests.
2025-10-24 19:48:18 +11:00
Kevin Ring 74e0ba7eb1 Set VCPKG_TARGET_TRIPLET. 2025-10-23 17:07:17 +11:00
Brendan Duncan 8d3d70a930 set EXIT_RUNTIME so test runner exits 2025-10-22 21:47:23 -06:00
Kevin Ring 62ef5734da Don't let OpenSSL try to dlopen in wasm builds. 2025-10-23 10:17:28 +11:00
Kevin Ring e4c0150a4d Back to ezvcpkg for now. 2025-10-22 21:50:49 +11:00
Kevin Ring 614458ed9b Don't use ezvcpg for wasm build, attempt to run tests. 2025-10-22 20:19:33 +11:00
Kevin Ring b40e3ecbc0 Use wasmXX-emscripten-cesium triplets.
Rather than overlaying the built-in wasm32-emscripten.
2025-10-22 19:54:10 +11:00
Kevin Ring 0656963653 Update from review. 2025-10-22 17:43:07 +11:00
Kevin Ring 6778fe613c clang-format 2025-10-22 16:17:29 +11:00
Janine Liu 410300c26a Minor doc/readability tweaks 2025-10-21 15:40:18 -04:00
Janine Liu c6687f4ddf Fix changelog 2025-10-21 11:33:42 -04:00
Janine Liu a6528aea33 Merge branch 'main' into gltf-tuner-kring 2025-10-21 11:32:22 -04:00
Kevin Ring f54db5f321 clang-tidy 2025-10-21 20:38:55 +11:00
Kevin Ring fb0123673e Make openssl configure script exectuable.
To fix all the posixy platforms.
2025-10-21 19:55:10 +11:00
Kevin Ring f245947101 clang-format 2025-10-21 18:33:21 +11:00
Kevin Ring 39c1ac8c62 Use the overlay triplets. 2025-10-21 18:29:13 +11:00
Kevin Ring 167a728aa0 Merge remote-tracking branch 'origin/main' into experiment_wasm 2025-10-21 16:41:23 +11:00
Brendan Duncan 5d44fd4670 fix wasm build errors 2025-10-20 22:40:24 -06:00
Kevin Ring aa5646dbf5
Merge pull request #1264 from CesiumGS/azure-maps
Add `AzureMapsRasterOverlay`
2025-10-21 09:58:02 +11:00
Kevin Ring 49a4409a8f Safer lambda captures. 2025-10-21 09:24:47 +11:00
Janine Liu 4f96d33d6b
Merge pull request #1265 from CesiumGS/zlib-ng-cleanup
Clean up dependencies, especially zlib-ng.
2025-10-20 15:46:57 -04:00
Janine Liu c89796a0ac Remove other unused header 2025-10-20 11:27:09 -04:00
Janine Liu dc10421be2
Linting 2025-10-20 11:14:00 -04:00
Janine Liu 7dc335dcf8 Update changelog for Uri 2025-10-20 10:48:13 -04:00
Janine Liu 91d2f5a3ca ensureTrailingSlash as a non-static function 2025-10-20 10:45:44 -04:00
Janine Liu 15af0de635 Remove unused refresh functions 2025-10-20 10:37:50 -04:00
Kevin Ring d66de9af3b Better job name. 2025-10-20 21:54:26 +11:00
Kevin Ring 9787c25d84 Attemp to add wasm build to CI. 2025-10-20 21:46:45 +11:00
Brendan Duncan c754a5fdec clang-format 2025-10-19 21:13:22 -06:00
Brendan Duncan cf18fd06b4 Merge branch 'main' into experiment_wasm 2025-10-19 13:41:36 -06:00
David Lively 0e6df07ccd Added null check 2025-10-17 14:44:30 -05:00
Kevin Ring 32d275ef0b Clean up dependencies, especially zlib-ng. 2025-10-17 17:19:41 +11:00
Janine Liu a9640afc13 Remove duplicate tests 2025-10-16 13:39:30 -04:00
Janine Liu dbe2c1f15d Formatting 2025-10-15 18:12:55 -04:00
Janine Liu 7016cdf1b3 Add Uri test, doc tweaks 2025-10-15 18:01:44 -04:00
Janine Liu 121d9c2678 More fixes for doc / lint / etc. 2025-10-15 17:41:27 -04:00
Janine Liu af5c118986 Formatting and linting 2025-10-15 17:18:57 -04:00
Janine Liu 8f8a101aa4 Replace credit image with white version, filter out empty credits 2025-10-15 17:01:08 -04:00
Janine Liu 63f1353457 Fix bad index typo 2025-10-15 16:08:26 -04:00
Janine Liu 7a8e96a857 Fix on-screen credit behavior 2025-10-15 15:55:14 -04:00
Janine Liu b2b9a1cd0d Don't mass-join copyrights 2025-10-15 14:55:42 -04:00
Janine Liu 7210acd3d3 Fix initialization order 2025-10-15 13:39:07 -04:00
Janine Liu fa7b2d2bcc Don't forget to pass through api-version 2025-10-15 12:11:23 -04:00
Janine Liu 315f5a55c3 Parse copyrights, add missing tilesetId types 2025-10-15 12:07:08 -04:00
Kevin Ring 751cf36965
Merge branch 'main' into geojson-overlay-fixes 2025-10-15 18:02:28 +11:00
Kevin Ring 753e35379b
Merge branch 'main' into gltf-tuner-kring 2025-10-15 17:55:25 +11:00
Janine Liu 6b35ee3f87 Ensure trailing slash in base URL 2025-10-14 15:53:09 -04:00
Janine Liu 7425f5caef Fix typo, update changelog 2025-10-14 15:32:14 -04:00
Janine Liu 6cbd974f2e Merge branch 'main' into azure-maps 2025-10-14 15:30:55 -04:00
Janine Liu bef3bd23a6 Use correct ion endpoint, add other tilesetId types 2025-10-14 15:24:13 -04:00
Janine Liu 70a7f91aed Add more tilesetId options 2025-10-14 14:24:29 -04:00
Janine Liu 00e8a9729d Parse Azure 2D maps from ion 2025-10-14 14:24:01 -04:00
Janine Liu d8631d0a6d Fix incorrect tile Y 2025-10-14 13:41:25 -04:00
Janine Liu af5bf48cc8 Actually include subscription key 2025-10-14 11:29:53 -04:00
Janine Liu 9e0db1b8c9 Fix incorrect template parameter 2025-10-14 11:23:00 -04:00
Janine Liu 05502dd87b Don't consider 'map' part of the base URL 2025-10-13 15:30:38 -04:00
Janine Liu 308f87f09a Parse tileSize as string, not number 2025-10-13 14:54:42 -04:00
Janine Liu 0efdec7b43 Parse JSON from Azure Maps service 2025-10-13 13:24:29 -04:00
Janine Liu 6da0610f3c Reflect vcpkg updates in changelog 2025-10-13 11:30:58 -04:00
Janine Liu f0de38ccdb Start adding AzureMapsRasterOverlay [skip ci] 2025-10-10 15:22:38 -04:00
Janine Liu e732d04cb9 Use @ref in Google doc 2025-10-10 15:21:25 -04:00
Janine Liu 21aa95f481 Make Bing constructor mimic Google's 2025-10-10 15:20:51 -04:00
Janine Liu ac2ea44bfd
Merge branch 'main' into gltf-tuner-kring 2025-10-09 14:22:06 -04:00
David Lively 237f51cc38
Merge pull request #1258 from CesiumGS/google-maps-empty-credit
Don't parse non-object credits from Google 2D Maps
2025-10-07 12:51:15 -05:00
Janine Liu 33cba63e85 Don't try to parse a non-object credit 2025-10-07 10:38:22 -04:00
Brendan Duncan 3b1b0f9e73 don't check in package-lock.json 2025-10-05 20:01:17 -06:00
Brendan Duncan de5ed6e7ba code cleanup 2025-10-05 17:13:42 -06:00
Brendan Duncan 733bc2c05d clean up to match main 2025-10-05 17:11:41 -06:00
Brendan Duncan 99f1404fe7 clean-up merge changes 2025-10-05 17:07:32 -06:00
Brendan Duncan abf28c5ecc Fix merge errors 2025-10-05 16:56:04 -06:00
Brendan Duncan 1c13589457 Merge branch 'main' into experiment_wasm
# Conflicts:
#	CHANGES.md
#	Cesium3DTilesSelection/src/RasterOverlayCollection.cpp
#	CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h
#	CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp
2025-10-05 15:16:11 -06:00
Ashley Rogers 3ab91770f7 More clang-tidy fix 2025-10-01 09:22:19 -04:00
Ashley Rogers e30f39940b Another clang-tidy fix 2025-09-30 17:33:19 -04:00
Ashley Rogers de2621a5e7 clang-tidy fix 2025-09-30 17:17:15 -04:00
Ashley Rogers 8aa8664a83 Update CHANGES 2025-09-30 17:16:11 -04:00
Ashley Rogers a891d3f996 Fix vector rasterizer antimeridian handling 2025-09-30 17:14:18 -04:00
Ashley Rogers 602bf3b452 Use path instead of polygon for multi ring polygons 2025-09-30 14:13:51 -04:00
Brendan Duncan 50b1530722 don't need specialized wasm compiler flags anymore 2025-09-22 08:05:31 -06:00
Brendan Duncan 56f2dc8e8b fix wasm build after merger 2025-09-20 19:49:52 -06:00
Brendan Duncan 1aa2788d33 Merge branch 'main' into experiment_wasm
# Conflicts:
#	CMakeLists.txt
2025-09-20 11:33:19 -06:00
Kevin Ring 6a6819b7af Clean up handling of the modifier not modifying. 2025-09-08 14:16:30 +10:00
Kevin Ring 9cb588a405 Remove unnecessary lambda capture. 2025-09-08 09:51:28 +10:00
Kevin Ring f4716c85e8 Tweak for coding standards. 2025-09-08 09:48:19 +10:00
Kevin Ring 03efa8a1cf Move changelog entry to the right section. 2025-09-08 09:25:31 +10:00
Kevin Ring 276b3a698d Merge remote-tracking branch 'origin/main' into gltf-tuner-kring 2025-09-08 08:30:02 +10:00
Brendan Duncan 3f416be76d Add EM_CONFIG to VCPKG_ENV_PASSTHROUGH_UNTRACKED 2025-09-03 22:32:37 -06:00
Brendan Duncan 90d1c96818 disable blend2d wasm install for now 2025-08-31 18:24:32 -06:00
Kevin Ring 8884c086dd clang-tidy 2025-08-29 14:26:39 +10:00
Kevin Ring 619e180227 Fix dodgy test. 2025-08-28 13:58:32 +10:00
Kevin Ring 618f285bdc Fix bad merge. 2025-08-28 07:56:45 +10:00
Brendan Duncan b7330ea1b5 fix typo from wasm32 builds 2025-08-27 12:00:48 -06:00
Brendan Duncan a37c73e776 Add option to build wasm64 2025-08-27 09:39:46 -06:00
Kevin Ring 47b961556b IPrepareRendererResources is optional. Add test. 2025-08-27 21:26:32 +10:00
Kevin Ring 8987f0ecd7 Tests, GltfModifier privately inherits LoadTileRequester. 2025-08-27 20:22:43 +10:00
Brendan Duncan 6e43196dd0 install blend2d for wasm builds 2025-08-26 13:38:13 -06:00
Kevin Ring de4d12dfe2 Merge remote-tracking branch 'origin/main' into gltf-tuner-kring 2025-08-26 20:08:51 +10:00
Kevin Ring be1812a06a clang-tidy. 2025-08-26 19:22:31 +10:00
Kevin Ring cf8014d616 Update CHANGES.md. 2025-08-26 19:10:43 +10:00
Kevin Ring 47f07ae907 Fix tests. 2025-08-26 16:40:23 +10:00
Kevin Ring e7c5f3d665 More state transition cleanup. 2025-08-26 15:34:10 +10:00
Kevin Ring aed82a6bc6 Slight reorg and renames. 2025-08-26 13:27:23 +10:00
Brendan Duncan 88c3716272 clean up 32-bit warmings 2025-08-25 15:38:10 -06:00
Brendan Duncan 1ff50143bc 32-bit warning fixes 2025-08-25 08:51:23 -06:00
Kevin Ring 4796e16f86 Doc, reduce priority of GltfModifier loading. 2025-08-25 22:18:38 +10:00
Kevin Ring a4f66ed062 More consistent criteria between load queue and actual load. 2025-08-25 22:16:07 +10:00
Kevin Ring 113f8702b9 Small tweaks. 2025-08-25 20:04:51 +10:00
Kevin Ring 3ffbbc5a15 GltfModifier may run on ContentLoaded, too, and must not be unloaded. 2025-08-25 19:08:59 +10:00
Brendan Duncan 1e1663bba7 Add HAS_FUTIME to emscripten defines for tidyhtml 2025-08-23 21:59:24 -06:00
Brendan Duncan 8329b52169 Start working on size_t warnings on 32-bit 2025-08-23 10:28:49 -06:00
Brendan Duncan a5e925c597 wasm build fix 2025-08-22 21:21:25 -06:00
Brendan Duncan f465fd9435 wasm build updates 2025-08-22 21:08:23 -06:00
Kevin Ring 77bbca121a Fix doc links. 2025-08-22 20:00:48 +10:00
Kevin Ring 852d3cc131 "needs loading" logic cleanup. 2025-08-22 19:34:36 +10:00
Kevin Ring e9f170b926 More doc. 2025-08-22 19:11:10 +10:00
Kevin Ring 2332a727b7 Doc and cleanup. 2025-08-22 18:40:04 +10:00
Kevin Ring 0f9d3dce1c Fix modification of tiles in ContentLoaded state. 2025-08-22 15:43:51 +10:00
Kevin Ring f02dd5f114 Pass version to GltfModifier, fix load requester. 2025-08-21 17:19:42 +10:00
Kevin Ring 99a0aa6f08 Merge remote-tracking branch 'origin/main' into gltf-tuner-kring 2025-08-21 11:07:23 +10:00
Kevin Ring cd2ca6997e Move GltfModifier version to an extension. 2025-08-20 16:34:08 +10:00
Kevin Ring ad640dd72b Minor cleanup. 2025-08-20 16:03:26 +10:00
Kevin Ring 57262a6eae Remove unnecessary includes. 2025-08-20 15:50:49 +10:00
Kevin Ring c0631035b9 Remove more unused version parameters. 2025-08-20 15:47:30 +10:00
Kevin Ring f7e116bc7f Remove commented-out code. 2025-08-20 15:44:06 +10:00
Kevin Ring 5a427f7447 Remove unused version parameter. 2025-08-20 15:29:52 +10:00
Kevin Ring fc2bc11cf3 clang-tidy 2025-08-20 15:19:26 +10:00
Kevin Ring c20dafac57 Merge remote-tracking branch 'origin/more-tileset-parsing' into gltf-tuner-kring 2025-08-20 15:05:28 +10:00
Kevin Ring f07af52bc6 clang-tidy 2025-08-19 22:07:04 +10:00
Kevin Ring a124802e3a Fix hang on shutdown. 2025-08-19 18:58:31 +10:00
Kevin Ring 4d2d125fbb Revert unnecessary change. 2025-08-19 17:09:50 +10:00
Kevin Ring 1dbfd68f67 Reapply modification to all loaded tiles. 2025-08-19 17:06:24 +10:00
Brendan Duncan ca2f95b1ce comment out use of std::formatter 2025-08-18 23:42:02 -06:00
Kevin Ring 20fe18b00b Start using TileLoadRequester to apply GltfModifier after load. 2025-08-18 19:05:33 +10:00
Kevin Ring 0e04d023b8 Improved GltfModifier reapplication behavior. 2025-08-15 22:02:32 +10:00
Kevin Ring 1b34e31d33 clang-tidy 2025-08-13 12:43:33 +10:00
Kevin Ring 15e19045c6 Doc fixes. 2025-08-13 11:50:38 +10:00
Kevin Ring b4b104d94b clang-tidy 2025-08-13 11:50:18 +10:00
Kevin Ring 728fb20fc0 Cleanup for clarity. 2025-08-13 10:58:56 +10:00
Kevin Ring ae039df78a Extract out GltfModifer re-apply. 2025-08-13 10:04:38 +10:00
Brendan Duncan 852ba2ddaf fix non-wasm build 2025-08-12 17:16:39 -06:00
Kevin Ring 8fac814750 Fix macOS build. 2025-08-13 05:50:46 +10:00
Kevin Ring effb99af71 GltfModifier input/output, future-ify apply. 2025-08-12 20:58:19 +10:00
Kevin Ring e6c63b3342 Doc and slight reorg. 2025-08-12 13:48:36 +10:00
Kevin Ring f5314830d8 GltfModifier works even when root is not tileset.json. 2025-08-12 11:11:35 +10:00
Kevin Ring b6f90e3041 Merge remote-tracking branch 'GhisBntly/advviz-gltf-tuner' into gltf-tuner-kring 2025-08-12 10:32:17 +10:00
Kevin Ring 87aae94cbf Merge remote-tracking branch 'origin/more-tileset-parsing' into advviz-gltf-tuner 2025-08-12 10:31:56 +10:00
Kevin Ring 12614410f7 Fix test failures. 2025-08-12 10:22:19 +10:00
Kevin Ring f6396a8d86 Merge remote-tracking branch 'origin/more-tileset-parsing' into gltf-tuner-kring 2025-08-12 08:34:20 +10:00
Kevin Ring 21707e1484 Add GltfModifier::onRegister. 2025-08-11 21:31:34 +10:00
Kevin Ring 287a24adff Merge remote-tracking branch 'origin/more-tileset-parsing' into advviz-gltf-tuner 2025-08-11 17:57:31 +10:00
Kevin Ring 67e35d7efd gltfModifier -> pGltfModifier. 2025-08-11 15:16:28 +10:00
Kevin Ring 6e19881ea3 Merge remote-tracking branch 'origin/const-get-root-tile' into advviz-gltf-tuner 2025-08-11 13:20:45 +10:00
Brendan Duncan 0ab8381d61 Fix up wasm32 cmake build 2025-08-07 22:22:44 -06:00
Brendan Duncan c71c3263ca change MAXIMUM_MEMORY to wasm32 limit 2025-08-07 22:21:51 -06:00
Brendan Duncan 22f50cefb5 re-add blend2d to wasm build 2025-08-06 21:34:08 -06:00
Brendan Duncan 70185b6304 changes to get compilation going 2025-08-06 09:28:52 -06:00
Brendan Duncan 7703c0c7fc disable blend2d and CesiumVectorData from wasm builds 2025-08-05 22:41:29 -06:00
Brendan Duncan 89263366e9 add missing cmake updates 2025-08-05 22:23:16 -06:00
Brendan Duncan 2c830aa233 WIP experiment with wasm32 2025-08-04 11:37:43 -06:00
Ghislain Cottat c5fc641212 fix another condition (consistency with isRenderable) 2025-06-17 13:16:05 +02:00
Ghislain Cottat b3eb6e134f remove duplicate include
confused by clang-format having moved the other one...
2025-06-17 13:16:05 +02:00
Ghislain Cottat e8e4fad106 fix condition now that modelVersion is an optional 2025-06-16 18:25:33 +02:00
Ghislain Cottat e2485f2dda missing include 2025-06-16 18:25:33 +02:00
Ghislain Cottat 8012240b26 fix comment 2025-06-16 18:25:33 +02:00
Ghislain Cottat ef80d583d1 rename param + clarify comparison
std::optional operators work but be careful with ordering comparisons!
Here it's better to test difference anyway
2025-06-14 15:38:38 +02:00
Ghislain Cottat 2b7af4c1b7 comment 2025-06-11 15:10:05 +02:00
Ghislain Cottat 581f1dd35d clang-format 2025-06-04 09:54:32 +02:00
Ghislain Cottat 031204a6a1 clang-format 2025-06-04 08:45:19 +02:00
Ghislain Cottat c60e7dbbb1 revert MSVC's clang-format'd lines that displease CI's 2025-06-03 21:29:51 +02:00
Ghislain Cottat 29d30d9330 tuner becomes modifiers, use std::optional for versions 2025-06-03 18:17:08 +02:00
Ghislain Cottat 29445be9fd clang-format 2025-06-03 11:52:59 +02:00
Ghislain Cottat 3828bbeea0
make this optional to indicate a lack of "versioning" for clients that don't need it
Co-authored-by: Janine Liu <32226860+j9liu@users.noreply.github.com>
2025-06-03 11:52:20 +02:00
Ghislain Cottat 4c9c7e28f1 fix clang-format, documentation and clang/gcc warning 2025-06-02 18:18:59 +02:00
Ghislain Cottat 297d6d66b1 Add an optional glTF model tuning stage after tile loading ends
but before the tile can be displayed
2025-05-28 16:22:51 +02:00
125 changed files with 5483 additions and 1850 deletions

View File

@ -217,3 +217,70 @@ jobs:
run: |
cd build
ctest -V
EmscriptenBuild:
strategy:
fail-fast: false
matrix:
version: [ "3.1.39", "4.0.13" ]
memory: [ "32", "64" ]
exclude:
- version: "3.1.39"
memory: "64"
name: Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory
env:
CACHE_KEY: "emscripten-${{matrix.version}}-${{matrix.memory}}"
runs-on: windows-2022
steps:
- name: Check out repository code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Use NodeJS 24 for WebAssembly 64-bit memory support
if: ${{ matrix.memory == '64' }}
uses: actions/setup-node@v6
with:
node-version: '>=24'
- name: Checkout vcpkg 2025.02.14 packages for use with Emscripten 3.1.39
if: ${{ matrix.version == '3.1.39' }}
uses: actions/checkout@v4
with:
repository: microsoft/vcpkg
clean: false
ref: 2025.02.14
path: extern/vcpkg/temp
sparse-checkout-cone-mode: false
sparse-checkout: |
ports/ada-url
- name: Move overlay ports to the correct location
if: ${{ matrix.version == '3.1.39' }}
run: |
mv extern/vcpkg/temp/ports/* extern/vcpkg/ports/
- name: Add OpenSSL "no-dso" option on older Emscripten versions
if: ${{ matrix.version == '3.1.39' }}
run: |
echo "
if(PORT MATCHES "openssl")
set(VCPKG_CONFIGURE_MAKE_OPTIONS "no-dso")
endif()
" >> extern/vcpkg/triplets/wasm32-emscripten-cesium.cmake
- name: Install latest CMake 3 and Ninja
uses: lukka/get-cmake@latest
with:
cmakeVersion: "3.31.6"
- name: Install nasm
uses: ilammy/setup-nasm@v1
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{matrix.version}}
- name: Verify
run: emcc -v
- name: Compile Debug Configuration
run: |
$env:VCPKG_ROOT="$env:VCPKG_INSTALLATION_ROOT"
$MEMORYPROPERTY="${{matrix.memory}}" -eq "64" ? "-DCESIUM_WASM64=ON" : ""
emcmake cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug $MEMORYPROPERTY
cmake --build build --config Debug --parallel
- name: Test Debug Configuration
run: |
node build/CesiumNativeTests/cesium-native-tests.js

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
build
build-*
doxygen
Testing
node_modules
@ -8,7 +9,5 @@ CMakeSettings.json
.cache
*.DS_Store
test.db
build-wsl
.idea
build-debug
clang-tidy.log

View File

@ -1,5 +1,57 @@
# Change Log
### ? - ?
##### Breaking Changes :mega:
- `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.
##### 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.
##### Fixes :wrench:
- The cmake install process previously didn't install `zlib`, which is required by `libcurl`.
### v0.54.0 - 2025-11-17
##### Additions :tada:
- Cesium Native can now be built with Emscripten.
### v0.53.0 - 2025-11-03
##### Breaking Changes :mega:
- Upgraded vcpkg to `2025.09.17`. This was previously done in v0.52.0 and reverted in v0.52.1.
- Removed `refreshTileProviderWithNewKey` from `BingMapsRasterOverlay` and `refreshTileProviderWithNewUrlAndHeaders` from `TileMapServiceRasterOverlay`. These were no longer used after the raster overlay refactor in `v0.52.0`.
##### Additions :tada:
- Added `AzureMapsRasterOverlay`.
- Added `Uri::ensureTrailingSlash`, which is helpful when the `Uri` represents a base URL.
- Added `GltfModifier`, which can be used to modify tile glTFs as they load, as well as apply new modifications to them later.
##### Fixes :wrench:
- Fixed a bug in `GoogleMapTilesRasterOverlay` that tried to parse credits from an erroneous viewport service response.
- Fixed issues with `GeoJsonRasterOverlay` with certain types of data.
- Polygons with holes should now display correctly.
- Using a GeoJSON file with data on either side of the antimeridian should now display correctly instead of causing the entire overlay to disappear.
- Fixed a bug with credits not showing on-screen when `showCreditsOnScreen` was enabled on `GoogleMapTilesRasterOverlay`.
### v0.52.1 - 2025-10-01
##### Breaking Changes :mega:
- Reverted vcpkg update that could interfere with builds on headless macOS.
### v0.52.0 - 2025-10-01
##### Breaking Changes :mega:
@ -19,6 +71,7 @@
- Removed `getOverlays`, `getTileProviders`, and `getPlaceholderTileProviders` from `RasterOverlayCollection`. Use `getActivatedOverlays` instead.
- `SharedAssetDepot` now uses a templatized "context" instead of separate `AsyncSystem` and `IAssetAccessor` parameters. It defaults to `SharedAssetContext`.
- Removed `RasterOverlay::getCredits`, which was not actually used anywhere. Use `RasterOverlayTileProvider::addCredits` instead.
- Upgraded vcpkg to `2025.09.17`.
##### Additions :tada:

View File

@ -4,6 +4,14 @@ if (NOT VCPKG_LIBRARY_LINKAGE)
set(VCPKG_LIBRARY_LINKAGE static)
endif()
get_filename_component(toolchainFile "${CMAKE_TOOLCHAIN_FILE}" NAME)
if(toolchainFile STREQUAL "Emscripten.cmake")
set(CESIUM_TARGET_WASM ON)
# Include the toolchain directly as ezvcpkg will overwrite the
# toolchain before it's loaded
include(${CMAKE_TOOLCHAIN_FILE})
endif()
# By default, Use ezvcpkg to install dependencies. But don't use
# ezvcpkg if it appears that this configuration is using vcpkg
# manifest mode already, either by building cesium-native directly,
@ -24,6 +32,13 @@ endif()
option(CESIUM_USE_EZVCPKG "use ezvcpkg helper" ${CESIUM_USE_EZVCPKG_DEFAULT})
option(CESIUM_DISABLE_CURL "Disable cesium-native's use of libcurl" OFF)
option(CESIUM_DISABLE_LIBJPEG_TURBO "Disable cesium-native's use of libjpeg-turbo. JPEG images will be decoded with STB instead." OFF)
option(CESIUM_WASM64 "Enable 64-bit WebAssembly target" OFF)
if (CESIUM_TARGET_WASM)
# Make sure curl is disabled on wasm builds, as it is not supported.
set(CESIUM_DISABLE_CURL ON)
endif()
if(CESIUM_USE_EZVCPKG)
# Keep vcpkg from running in manifset mode. It will try to because
@ -50,12 +65,28 @@ if (NOT VCPKG_TRIPLET)
elseif(DETECTED_VCPKG_TRIPLET STREQUAL "x64-windows")
# cesium-native requires static linking on Windows
set(VCPKG_TRIPLET "x64-windows-static-md")
elseif(DETECTED_VCPKG_TRIPLET STREQUAL "wasm32-emscripten")
# Use our custom triplet for wasm builds. Most importantly, this
# enables multithreading support. Also switch to 64-bit wasm if
# requested.
if (CESIUM_WASM64)
set(VCPKG_TRIPLET "wasm64-emscripten-cesium")
else()
set(VCPKG_TRIPLET "wasm32-emscripten-cesium")
endif()
else()
set(VCPKG_TRIPLET "${DETECTED_VCPKG_TRIPLET}")
endif()
if (NOT CESIUM_USE_EZVCPKG)
set(VCPKG_TARGET_TRIPLET "${VCPKG_TRIPLET}")
endif()
# If we're using ezvcpkg, ezvcpkg will update CMAKE_TOOLCHAIN_FILE to point to the vcpkg toolchain.
# Which means that when we hit the `project` function call below, cmake will load the vcpkg
# toolchain file. If VCPKG_TARGET_TRIPLET isn't set by that time, vcpkg will set it itself, and
# maybe not to what we want. So set VCPKG_TARGET_TRIPLET explicit here so that we're sure to get
# the right one.
#
# If we're _not_ using ezvcpkg, then we also must set VCPKG_TARGET_TRIPLET, but for a different reason.
# VCPKG_TRIPLET is only an ezvcpkg thing, vcpkg itself only knows about VCPKG_TARGET_TRIPLET.
set(VCPKG_TARGET_TRIPLET "${VCPKG_TRIPLET}")
endif()
message(STATUS "VCPKG_TRIPLET ${VCPKG_TRIPLET}")
@ -80,6 +111,10 @@ if (NOT VCPKG_OVERLAY_TRIPLETS)
endif()
endif()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/extern/vcpkg/triplets")
list(APPEND VCPKG_OVERLAY_TRIPLETS "${CMAKE_CURRENT_SOURCE_DIR}/extern/vcpkg/triplets")
endif()
message(STATUS "VCPKG_OVERLAY_TRIPLETS ${VCPKG_OVERLAY_TRIPLETS}")
# These packages are used in the public headers of Cesium libraries, so we need to distribute the headers and binaries
@ -91,13 +126,13 @@ set(PACKAGES_PUBLIC asyncplusplus expected-lite fmt glm rapidjson spdlog stb ada
# to distribute the binaries for linking, but not the headers, as downstream consumers don't need them
# OpenSSL and abseil are both dependencies of s2geometry
set(PACKAGES_PRIVATE
abseil draco ktx modp-base64 meshoptimizer openssl s2geometry
libjpeg-turbo sqlite3 tinyxml2 libwebp zlib-ng picosha2
earcut-hpp cpp-httplib[core] libmorton zstd
abseil draco ktx[core] modp-base64 meshoptimizer openssl s2geometry
sqlite3 tinyxml2 libwebp zlib-ng picosha2
earcut-hpp libmorton zstd
)
# asmjit needed by blend2d on non-iOS platforms (iOS doesn't support JIT)
if(NOT IOS AND NOT VCPKG_CMAKE_SYSTEM_NAME MATCHES "iOS")
# asmjit needed by blend2d on non-iOS platforms (iOS and Wasm don't support JIT)
if(NOT CESIUM_TARGET_WASM AND NOT IOS AND NOT VCPKG_CMAKE_SYSTEM_NAME MATCHES "iOS")
list(APPEND PACKAGES_PRIVATE blend2d asmjit)
else()
# Use [core] feature to disable default jit feature.
@ -105,19 +140,40 @@ else()
endif()
if(NOT CESIUM_DISABLE_CURL)
list(APPEND PACKAGES_PRIVATE curl)
list(APPEND PACKAGES_PRIVATE curl zlib)
endif()
if(NOT CESIUM_DISABLE_LIBJPEG_TURBO)
list(APPEND PACKAGES_PRIVATE libjpeg-turbo)
endif()
# We use cpp-httplib to host a local web server for OAuth2 authorization. That's not
# going to work at all on the web, and the latest versions of cpp-httplib only support
# 64-bit platforms anyway, so skip it entirely for WebAssembly builds.
if(NOT CESIUM_TARGET_WASM)
list(APPEND PACKAGES_PRIVATE "cpp-httplib[core]")
endif()
# Packages only used for testing
set(PACKAGES_TEST doctest)
if(CESIUM_TARGET_WASM)
# vcpkg will attempt to second-guess our CMAKE_C_COMPILER setting, choosing to go with the value of CC instead.
# While normally this is the correct value to go with, for wasm we need to be using emcc and em++.
# So we set CC and CXX to emcc and em++ here so vcpkg will pick them up properly.
# Does this make sense? No. Does it work? Somehow. ¯\_()_/¯
set(ENV{CC} ${CMAKE_C_COMPILER})
set(ENV{CXX} ${CMAKE_CXX_COMPILER})
endif()
if(CESIUM_USE_EZVCPKG)
set(PACKAGES_ALL ${PACKAGES_PUBLIC})
list(APPEND PACKAGES_ALL ${PACKAGES_PRIVATE})
list(APPEND PACKAGES_ALL ${PACKAGES_TEST})
ezvcpkg_fetch(
COMMIT 2025.09.17
COMMIT afc0a2e01ae104a2474216a2df0e8d78516fd5af
REPO microsoft/vcpkg
PACKAGES ${PACKAGES_ALL}
# Clean the build trees after building, so that we don't use a ton a disk space on the CI cache
CLEAN_BUILDTREES
@ -138,10 +194,20 @@ endif()
include("cmake/defaults.cmake")
project(cesium-native
VERSION 0.52.0
VERSION 0.54.0
LANGUAGES CXX C
)
if(CESIUM_TARGET_WASM)
if(CESIUM_WASM64)
set(CMAKE_SIZEOF_VOID_P 8)
else()
set(CMAKE_SIZEOF_VOID_P 4)
endif()
# Tells emscripten to output an HTML harness for the generated WASM
set(CMAKE_EXECUTABLE_SUFFIX ".html")
endif()
include(GNUInstallDirs)
include(CMakeDependentOption)
@ -284,10 +350,16 @@ endif()
# On the CI builds, I have to do this explicitly for some reason or it fails to find the vcpkg packages.
# The toolchain is supposed to manage this, but I haven't figured out why it isn't yet.
# One reason the toolchain won't manage this: it's not being run at all. That happens when this CMakeLists.txt
# is invoked from another one via add_subdirectory, and the parent project has already loaded the toolchain
# before ezvcpkg sets it.
list(APPEND CMAKE_PREFIX_PATH "${PACKAGE_BUILD_DIR}/share/s2")
list(APPEND CMAKE_PREFIX_PATH "${PACKAGE_BUILD_DIR}/share")
list(APPEND CMAKE_PREFIX_PATH "${PACKAGE_BUILD_DIR}")
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
list(APPEND CMAKE_FIND_ROOT_PATH "${PACKAGE_BUILD_DIR}/debug")
endif()
list(APPEND CMAKE_FIND_ROOT_PATH "${PACKAGE_BUILD_DIR}")
# Find the VCPKG dependnecies
# Note that while we could push these into the extern/CMakeLists.txt as an organization tidy-up, that would require
@ -295,26 +367,24 @@ list(APPEND CMAKE_PREFIX_PATH "${PACKAGE_BUILD_DIR}")
# they won't be visible in this scope nor any of the subdirectories for the actual libraries.
#
# However, for some of the vcpkg built libraries where they don't provide a prope cmake config file, we have to declare
# and imporeted library target ourselves. This is the case for modp_b64::modp_b64, picosha2::picosha2 and earcut. In
# an imported library target ourselves. This is the case for modp_b64::modp_b64, picosha2::picosha2 and earcut. In
# these cases, we *do* have the somewhat ugly and verbose details in the extern/CMakeLists.txt file.
#
# XXX Above comment should be obsoleted by these first calls to
# find_package, which resolve to our own modules that provide
# targets. If needed, they can be installed with CMake config files
# etc.
find_package(zlib-ng REQUIRED)
find_package(modp_b64 REQUIRED)
find_package(ada CONFIG REQUIRED)
find_package(Async++ CONFIG REQUIRED)
find_package(blend2d CONFIG REQUIRED)
find_package(doctest CONFIG REQUIRED)
find_package(draco CONFIG REQUIRED)
find_package(expected-lite CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(httplib CONFIG REQUIRED)
find_package(Ktx CONFIG REQUIRED)
find_package(libmorton CONFIG REQUIRED)
find_package(libjpeg-turbo CONFIG REQUIRED)
find_package(meshoptimizer CONFIG REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(s2 CONFIG REQUIRED)
@ -322,9 +392,10 @@ find_package(spdlog CONFIG REQUIRED)
find_package(tinyxml2 CONFIG REQUIRED)
find_package(unofficial-sqlite3 CONFIG REQUIRED)
find_package(WebP CONFIG REQUIRED)
find_package(blend2d CONFIG REQUIRED)
# asmjit should not be included with iOS builds as iOS doesn't support JIT compilation.
if(NOT IOS AND NOT VCPKG_CMAKE_SYSTEM_NAME MATCHES "iOS")
find_package(zlib-ng CONFIG REQUIRED)
# asmjit should not be included with iOS or Wasm builds as they don't support JIT compilation.
if(NOT CESIUM_TARGET_WASM AND NOT IOS AND NOT VCPKG_CMAKE_SYSTEM_NAME MATCHES "iOS")
find_package(asmjit CONFIG REQUIRED)
endif()
@ -332,9 +403,12 @@ if(NOT CESIUM_DISABLE_CURL)
find_package(CURL REQUIRED)
endif()
if(NOT CESIUM_DISABLE_LIBJPEG_TURBO)
find_package(libjpeg-turbo CONFIG REQUIRED)
endif()
if(NOT CESIUM_DISABLE_CURL)
find_package(CURL REQUIRED)
if(NOT CESIUM_TARGET_WASM)
find_package(httplib CONFIG REQUIRED)
endif()
# Private Library (s2geometry)
@ -386,7 +460,6 @@ if(CESIUM_INSTALL_STATIC_LIBS AND CESIUM_INSTALL_HEADERS)
DESTINATION ${CMAKE_INSTALL_DATADIR}/cesium-native/cmake)
install(FILES
"${CMAKE_CURRENT_LIST_DIR}/cmake/modules/Findzlib-ng.cmake"
"${CMAKE_CURRENT_LIST_DIR}/cmake/modules/Findmodp_b64.cmake"
DESTINATION ${CMAKE_INSTALL_DATADIR}/cesium-native/cmake)

View File

@ -234,7 +234,7 @@ public:
bool isContentAvailable(
const CesiumGeometry::QuadtreeTileID& subtreeId,
const CesiumGeometry::QuadtreeTileID& tileId,
uint64_t contentId) const noexcept;
size_t contentId) const noexcept;
/**
* @brief Determines if content for a given tile in the octree is available.
@ -247,7 +247,7 @@ public:
bool isContentAvailable(
const CesiumGeometry::OctreeTileID& subtreeId,
const CesiumGeometry::OctreeTileID& tileId,
uint64_t contentId) const noexcept;
size_t contentId) const noexcept;
/**
* @brief Determines if content for a given tile in the subtree is available.
@ -262,7 +262,7 @@ public:
bool isContentAvailable(
uint32_t relativeTileLevel,
uint64_t relativeTileMortonId,
uint64_t contentId) const noexcept;
size_t contentId) const noexcept;
/**
* @brief Sets the availability state of the content for a given tile in the
@ -276,7 +276,7 @@ public:
void setContentAvailable(
const CesiumGeometry::QuadtreeTileID& subtreeId,
const CesiumGeometry::QuadtreeTileID& tileId,
uint64_t contentId,
size_t contentId,
bool isAvailable) noexcept;
/**
@ -291,7 +291,7 @@ public:
void setContentAvailable(
const CesiumGeometry::OctreeTileID& subtreeId,
const CesiumGeometry::OctreeTileID& tileId,
uint64_t contentId,
size_t contentId,
bool isAvailable) noexcept;
/**
@ -309,7 +309,7 @@ public:
void setContentAvailable(
uint32_t relativeTileLevel,
uint64_t relativeTileMortonId,
uint64_t contentId,
size_t contentId,
bool isAvailable) noexcept;
/**

View File

@ -1014,7 +1014,10 @@ void copyStringsToBuffers(
for (const auto& str : arrayMember.GetArray()) {
OffsetType byteLength = static_cast<OffsetType>(
str.GetStringLength() * sizeof(rapidjson::Value::Ch));
std::memcpy(valueBuffer.data() + offset, str.GetString(), byteLength);
std::memcpy(
valueBuffer.data() + offset,
str.GetString(),
size_t(byteLength));
std::memcpy(
offsetBuffer.data() + offsetIndex * sizeof(OffsetType),
&offset,
@ -1075,8 +1078,7 @@ void updateStringArrayProperty(
++it;
}
const uint64_t totalByteLength =
totalCharCount * sizeof(rapidjson::Value::Ch);
const size_t totalByteLength = totalCharCount * sizeof(rapidjson::Value::Ch);
std::vector<std::byte> valueBuffer;
std::vector<std::byte> stringOffsetBuffer;
PropertyComponentType stringOffsetType = PropertyComponentType::None;

View File

@ -823,7 +823,7 @@ void decodeDracoMetadata(
const std::unique_ptr<draco::PointCloud>& pPointCloud,
rapidjson::Document& batchTableJson,
PntsContent& parsedContent) {
const uint64_t pointsLength = parsedContent.pointsLength;
const size_t pointsLength = parsedContent.pointsLength;
std::vector<std::byte>& data = parsedContent.dracoBatchTableBinary;
const auto& dracoMetadataSemantics = parsedContent.dracoMetadataSemantics;

View File

@ -281,7 +281,7 @@ void SubtreeAvailability::setTileAvailable(
bool SubtreeAvailability::isContentAvailable(
const CesiumGeometry::QuadtreeTileID& subtreeId,
const CesiumGeometry::QuadtreeTileID& tileId,
uint64_t contentId) const noexcept {
size_t contentId) const noexcept {
uint64_t relativeTileMortonIdx =
ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId);
return this->isContentAvailable(
@ -293,7 +293,7 @@ bool SubtreeAvailability::isContentAvailable(
bool SubtreeAvailability::isContentAvailable(
const CesiumGeometry::OctreeTileID& subtreeId,
const CesiumGeometry::OctreeTileID& tileId,
uint64_t contentId) const noexcept {
size_t contentId) const noexcept {
uint64_t relativeTileMortonIdx =
ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId);
return this->isContentAvailable(
@ -305,7 +305,7 @@ bool SubtreeAvailability::isContentAvailable(
bool SubtreeAvailability::isContentAvailable(
uint32_t relativeTileLevel,
uint64_t relativeTileMortonId,
uint64_t contentId) const noexcept {
size_t contentId) const noexcept {
if (contentId >= this->_contentAvailability.size())
return false;
return isAvailable(
@ -317,7 +317,7 @@ bool SubtreeAvailability::isContentAvailable(
void SubtreeAvailability::setContentAvailable(
const CesiumGeometry::QuadtreeTileID& subtreeId,
const CesiumGeometry::QuadtreeTileID& tileId,
uint64_t contentId,
size_t contentId,
bool isAvailable) noexcept {
uint64_t relativeTileMortonIdx =
ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId);
@ -331,7 +331,7 @@ void SubtreeAvailability::setContentAvailable(
void SubtreeAvailability::setContentAvailable(
const CesiumGeometry::OctreeTileID& subtreeId,
const CesiumGeometry::OctreeTileID& tileId,
uint64_t contentId,
size_t contentId,
bool isAvailable) noexcept {
uint64_t relativeTileMortonIdx =
ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId);
@ -345,7 +345,7 @@ void SubtreeAvailability::setContentAvailable(
void SubtreeAvailability::setContentAvailable(
uint32_t relativeTileLevel,
uint64_t relativeTileMortonId,
uint64_t contentId,
size_t contentId,
bool isAvailable) noexcept {
if (contentId < this->_contentAvailability.size()) {
this->setAvailable(
@ -518,7 +518,7 @@ bool SubtreeAvailability::isAvailableUsingBufferView(
const SubtreeBufferViewAvailability* bufferViewAvailability =
std::get_if<SubtreeBufferViewAvailability>(&availabilityView);
const uint64_t byteIndex = availabilityBitIndex / 8;
const size_t byteIndex = size_t(availabilityBitIndex / 8);
if (byteIndex >= bufferViewAvailability->view.size()) {
return false;
}
@ -541,7 +541,7 @@ void SubtreeAvailability::setAvailableUsingBufferView(
const SubtreeBufferViewAvailability* pBufferViewAvailability =
std::get_if<SubtreeBufferViewAvailability>(&availabilityView);
const uint64_t byteIndex = availabilityBitIndex / 8;
const size_t byteIndex = size_t(availabilityBitIndex / 8);
if (byteIndex >= pBufferViewAvailability->view.size()) {
// Attempting to set an invalid tile. Assert, but otherwise ignore it.
CESIUM_ASSERT(false);

View File

@ -67,8 +67,8 @@ void markTileAvailableForQuadtree(
uint64_t availabilityBitIndex =
numOfTilesFromRootToParentLevel +
libmorton::morton2D_64_encode(tileID.x, tileID.y);
const uint64_t byteIndex = availabilityBitIndex / 8;
const uint64_t bitIndex = availabilityBitIndex % 8;
const size_t byteIndex = size_t(availabilityBitIndex / 8);
const size_t bitIndex = size_t(availabilityBitIndex % 8);
available[byteIndex] |= std::byte(1 << bitIndex);
}
@ -79,7 +79,7 @@ void markSubtreeAvailableForQuadtree(
libmorton::morton2D_64_encode(tileID.x, tileID.y);
const uint64_t byteIndex = availabilityBitIndex / 8;
const uint64_t bitIndex = availabilityBitIndex % 8;
available[byteIndex] |= std::byte(1 << bitIndex);
available[(size_t)byteIndex] |= std::byte(1 << bitIndex);
}
using SubtreeContentInput =
@ -140,17 +140,17 @@ SubtreeContent createSubtreeContent(
: 0;
std::vector<std::byte> availabilityBuffer(
bufferSize + bufferSize + subtreeBufferSize);
size_t(bufferSize + bufferSize + subtreeBufferSize));
std::span<std::byte> contentAvailabilityBuffer(
availabilityBuffer.data(),
bufferSize);
size_t(bufferSize));
std::span<std::byte> tileAvailabilityBuffer(
availabilityBuffer.data() + bufferSize,
bufferSize);
size_t(bufferSize));
std::span<std::byte> subtreeAvailabilityBuffer(
availabilityBuffer.data() + bufferSize + bufferSize,
subtreeBufferSize);
size_t(subtreeBufferSize));
SubtreeAvailability::AvailabilityView tileAvailability = std::visit(
GetAvailabilityView{tileAvailabilityBuffer, false},
@ -486,9 +486,9 @@ TEST_CASE("Test SubtreeAvailability methods") {
subtree.bufferViews[1].buffer = 1;
subtree.bufferViews[2].buffer = 2;
contentAvailabilityBuffer.resize(bufferSize);
tileAvailabilityBuffer.resize(bufferSize);
subtreeAvailabilityBuffer.resize(subtreeBufferSize);
contentAvailabilityBuffer.resize(size_t(bufferSize));
tileAvailabilityBuffer.resize(size_t(bufferSize));
subtreeAvailabilityBuffer.resize(size_t(subtreeBufferSize));
subtree.buffers[0].byteLength = subtree.bufferViews[0].byteLength =
int64_t(bufferSize);
@ -622,18 +622,18 @@ TEST_CASE("Test parsing subtree format") {
subtreeHeader.jsonByteLength = subtreeJsonBuffer.GetSize();
subtreeHeader.binaryByteLength = subtreeBuffers.buffers.size();
std::vector<std::byte> buffer(
std::vector<std::byte> buffer(size_t(
sizeof(subtreeHeader) + subtreeHeader.jsonByteLength +
subtreeHeader.binaryByteLength);
subtreeHeader.binaryByteLength));
std::memcpy(buffer.data(), &subtreeHeader, sizeof(subtreeHeader));
std::memcpy(
buffer.data() + sizeof(subtreeHeader),
subtreeJsonBuffer.GetString(),
subtreeHeader.jsonByteLength);
size_t(subtreeHeader.jsonByteLength));
std::memcpy(
buffer.data() + sizeof(subtreeHeader) + subtreeHeader.jsonByteLength,
subtreeBuffers.buffers.data(),
subtreeHeader.binaryByteLength);
size_t(subtreeHeader.binaryByteLength));
// mock the request
auto pMockResponse = std::make_unique<SimpleAssetResponse>(
@ -711,18 +711,18 @@ TEST_CASE("Test parsing subtree format") {
subtreeHeader.jsonByteLength = subtreeJsonBuffer.GetSize();
subtreeHeader.binaryByteLength = subtreeContent.buffers.size();
std::vector<std::byte> buffer(
std::vector<std::byte> buffer(size_t(
sizeof(subtreeHeader) + subtreeHeader.jsonByteLength +
subtreeHeader.binaryByteLength);
subtreeHeader.binaryByteLength));
std::memcpy(buffer.data(), &subtreeHeader, sizeof(subtreeHeader));
std::memcpy(
buffer.data() + sizeof(subtreeHeader),
subtreeJsonBuffer.GetString(),
subtreeHeader.jsonByteLength);
size_t(subtreeHeader.jsonByteLength));
std::memcpy(
buffer.data() + sizeof(subtreeHeader) + subtreeHeader.jsonByteLength,
subtreeContent.buffers.data(),
subtreeHeader.binaryByteLength);
size_t(subtreeHeader.binaryByteLength));
// mock the request
auto pMockResponse = std::make_unique<SimpleAssetResponse>(
@ -799,14 +799,14 @@ TEST_CASE("Test parsing subtree format") {
subtreeHeader.jsonByteLength = subtreeJsonBuffer.GetSize();
subtreeHeader.binaryByteLength = 0;
std::vector<std::byte> buffer(
std::vector<std::byte> buffer(size_t(
sizeof(subtreeHeader) + subtreeHeader.jsonByteLength +
subtreeHeader.binaryByteLength);
subtreeHeader.binaryByteLength));
std::memcpy(buffer.data(), &subtreeHeader, sizeof(subtreeHeader));
std::memcpy(
buffer.data() + sizeof(subtreeHeader),
subtreeJsonBuffer.GetString(),
subtreeHeader.jsonByteLength);
size_t(subtreeHeader.jsonByteLength));
// mock the request
auto pMockResponse = std::make_unique<SimpleAssetResponse>(

View File

@ -165,12 +165,12 @@ Future<ReadJsonResult<Subtree>> SubtreeFileReader::loadBinary(
}
ReadJsonResult<Subtree> result = this->_reader.readFromJson(
data.subspan(sizeof(SubtreeHeader), header->jsonByteLength));
data.subspan(sizeof(SubtreeHeader), size_t(header->jsonByteLength)));
if (result.value) {
std::span<const std::byte> binaryChunk = data.subspan(
sizeof(SubtreeHeader) + header->jsonByteLength,
header->binaryByteLength);
sizeof(SubtreeHeader) + size_t(header->jsonByteLength),
size_t(header->binaryByteLength));
if (binaryChunk.size() > 0) {
if (result.value->buffers.empty()) {
@ -207,7 +207,7 @@ Future<ReadJsonResult<Subtree>> SubtreeFileReader::loadBinary(
buffer.cesium.data = std::vector<std::byte>(
binaryChunk.begin(),
binaryChunk.begin() + buffer.byteLength);
binaryChunk.begin() + ptrdiff_t(buffer.byteLength));
}
}

View File

@ -52,7 +52,7 @@ target_link_libraries(Cesium3DTilesSelection
CesiumQuantizedMeshTerrain
CesiumRasterOverlays
CesiumUtility
spdlog::spdlog spdlog::spdlog_header_only
spdlog::spdlog
# PRIVATE
libmorton::libmorton
draco::draco

View File

@ -0,0 +1,273 @@
#pragma once
#include <Cesium3DTilesSelection/GltfModifierState.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileLoadRequester.h>
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumGltf/Model.h>
#include <spdlog/fwd.h>
#include <memory>
#include <optional>
namespace CesiumAsync {
class IAssetAccessor;
} // namespace CesiumAsync
namespace Cesium3DTilesSelection {
class TilesetMetadata;
class TilesetContentManager;
/**
* @brief The input to @ref GltfModifier::apply.
*/
struct GltfModifierInput {
/**
* @brief The version of the @ref GltfModifier, as returned by
* @ref GltfModifier::getCurrentVersion at the start of the modification.
*
* This is provided because calling @ref GltfModifier::getCurrentVersion
* may return a newer version if @ref GltfModifier::trigger is called
* again while @ref GltfModifier::apply is running in a worker thread.
*/
int64_t version;
/**
* @brief The async system that can be used to do work in threads.
*/
CesiumAsync::AsyncSystem asyncSystem;
/**
* @brief An asset accessor that can be used to obtain additional assets.
*/
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor;
/**
* @brief The logger.
*/
std::shared_ptr<spdlog::logger> pLogger;
/**
* @brief The model to be modified.
*/
const CesiumGltf::Model& previousModel;
/**
* @brief The transformation of the model's coordinates to the tileset's
* coordinate system.
*/
glm::dmat4 tileTransform;
};
/**
* @brief The output of @ref GltfModifier::apply.
*/
struct GltfModifierOutput {
/**
* @brief The new, modified model.
*/
CesiumGltf::Model modifiedModel;
};
/**
* @brief An abstract class that allows modifying a tile's glTF model after it
* has been loaded.
*
* An example modification is merging or splitting the primitives in the glTF.
* Merging primitives can lead to improved rendering performance. Splitting
* primitives allows different materials to be assigned to parts that were
* initially in the same primitive.
*
* The `GltfModifier` can be applied several times during the lifetime of the
* model, depending on current needs. For this reason, the `GltfModifer` has a
* @ref getCurrentVersion, which can be incremented by calling
* @ref trigger. When the version is incremented, the `GltfModifier` will be
* re-applied to all previously-modified models.
*
* The version number of a modified glTF is stored in the
* @ref GltfModifierVersionExtension extension.
*
* A just-constructed modifier is considered nilpotent, meaning nothing will
* happen until @ref trigger has been called at least once.
*
* The @ref apply function is called from a worker thread. All other methods
* must only be called from the main thread.
*/
class GltfModifier : private TileLoadRequester {
public:
/**
* @brief Gets the current version number, or `std::nullopt` if the
* `GltfModifier` is currently inactive.
*
* Returns `std::nullopt` when in the default nilpotent state where glTFs will
* not be modified at all. Calling @ref trigger once will set the version
* number to 0 and activate the `GltfModifier`. Calling it successive times
* will increment the version number and re-apply modification to all
* previously-modified models.
*/
std::optional<int64_t> getCurrentVersion() const;
/**
* @brief Checks if this `GltfModifier` is active.
*
* This method returns true if the current version is greater than or equal to
* 0, indicating that @ref trigger has been called at least once.
*/
bool isActive() const;
/**
* @brief Call this the first time to activate this `GltfModifier` after it
* has been constructed in its default nilpotent state and set the
* @ref getCurrentVersion to 0. Call it successive times to increment
* @ref getCurrentVersion and reapply modification to all
* previously-modified models without unloading them.
*
* While the `GltfModifier` is being reapplied for a new version, the display
* may show a mix of tiles with the old and new versions.
*/
void trigger();
/**
* @brief Implement this method to apply custom modification to a glTF model.
* It is called by the @ref Tileset from within a worker thread.
*
* This method will be called for each @ref Tile during the content load
* process if @ref trigger has been called at least once. It will also be
* called again for already-loaded tiles for successive calls to
* @ref trigger.
*
* @param input The input to the glTF modification.
* @return A future that resolves to a @ref GltfModifierOutput with the
* new model, or to `std::nullopt` if the model does not need to be modified.
*/
virtual CesiumAsync::Future<std::optional<GltfModifierOutput>>
apply(GltfModifierInput&& input) = 0;
/**
* @brief Checks if the given tile needs to be processed by this
* `GltfModifier` in a worker thread.
*
* @param tile The tile to check.
* @returns `true` if the tile needs to be processed by the `GltfModifier` in
* a worker thread, or `false` otherwise.
*/
bool needsWorkerThreadModification(const Tile& tile) const;
/**
* @brief Checks if the given tile needs to be processed by this
* `GltfModifier` in the main thread.
* @param tile The tile to check.
* @returns `true` if the tile needs to be processed by the `GltfModifier` in
* the main thread, or `false` otherwise.
*/
bool needsMainThreadModification(const Tile& tile) const;
protected:
GltfModifier();
virtual ~GltfModifier();
/**
* @brief Notifies this instance that it has been registered with a
* @ref Tileset.
*
* This method is called after the tileset's root tile is known but
* before @ref Tileset::getRootTileAvailableEvent has been raised.
*
* This method is called from the main thread. Override this method to respond
* to this event.
*
* @param asyncSystem The async system with which to do background work.
* @param pAssetAccessor The asset accessor to use to retrieve any additional
* assets.
* @param pLogger The logger to which to log errors and warnings that occur
* during preparation of the `GltfModifier`.
* @param tilesetMetadata The metadata associated with the tileset.
* @param rootTile The root tile of the tileset.
* @returns A future that resolves when the `GltfModifier` is ready to modify
* glTF instances for this tileset. Tileset loading will not proceed until
* this future resolves. If the future rejects, tileset load will proceed but
* the `GltfModifier` will not be used.
*/
virtual CesiumAsync::Future<void> onRegister(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<spdlog::logger>& pLogger,
const TilesetMetadata& tilesetMetadata,
const Tile& rootTile);
private:
/**
* @private
*
* @brief Called by @ref Tileset when this instance has been registered
* with it. To add custom behavior on registration, override the other
* overload of this method.
*/
CesiumAsync::Future<void> onRegister(
TilesetContentManager& contentManager,
const TilesetMetadata& tilesetMetadata,
const Tile& rootTile);
/**
* @private
*
* @brief Called by @ref Tileset when this instance has been unregistered
* from it.
*/
void onUnregister(TilesetContentManager& contentManager);
/**
* @private
*
* @brief Called by @ref Tileset when the given tile leaves the
* @ref TileLoadState::ContentLoading state but it was loaded with an
* older @ref GltfModifier version. The tile will be queued for a call
* to @ref apply in a worker thread.
*
* This method is called from the main thread.
*
* @param tile The tile that has just left the
* @ref TileLoadState::ContentLoading state.
*/
void onOldVersionContentLoadingComplete(const Tile& tile);
/**
* @private
*
* @brief Called by @ref Tileset when @ref apply has finished running on a
* previously-loaded tile. The tile will be queued to finish its loading in
* the main thread.
*
* This method is called from the main thread.
*
* @param tile The tile that has just been processed by @ref apply.
*/
void onWorkerThreadApplyComplete(const Tile& tile);
// TileLoadRequester implementation
double getWeight() const override;
bool hasMoreTilesToLoadInWorkerThread() const override;
const Tile* getNextTileToLoadInWorkerThread() override;
bool hasMoreTilesToLoadInMainThread() const override;
const Tile* getNextTileToLoadInMainThread() override;
std::optional<int64_t> _currentVersion;
const Tile* _pRootTile;
// Ideally these would be weak pointers, but we don't currently have a good
// way to do that.
std::vector<Tile::ConstPointer> _workerThreadQueue;
std::vector<Tile::ConstPointer> _mainThreadQueue;
friend class TilesetContentManager;
friend class MockTilesetContentManagerForGltfModifier;
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,23 @@
#pragma once
namespace Cesium3DTilesSelection {
/** The state of the @ref GltfModifier process for a given tile content's model.
*/
enum class GltfModifierState {
/** Modifier is not currently processing this tile. */
Idle,
/** Worker-thread phase is in progress. */
WorkerRunning,
/**
* The worker-thread phase is complete, but the main-thread phase is not done
* yet. When the main thread phase eventually runs, it will call @ref
* TileRenderContent::replaceWithModifiedModel and then transition the @ref
* GltfModifier back to the `Idle` state.
*/
WorkerDone,
};
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,67 @@
#pragma once
#include <Cesium3DTilesSelection/Library.h>
#include <CesiumUtility/ExtensibleObject.h>
#include <optional>
namespace CesiumGltf {
struct Model;
}
namespace Cesium3DTilesSelection {
/** @brief An extension holding the "version" of a glTF produced by
* {@link GltfModifier}.
*/
struct CESIUM3DTILESSELECTION_API GltfModifierVersionExtension
: public CesiumUtility::ExtensibleObject {
/**
* @brief Gets the version number of the given model, or `std::nullopt` if
* the model does not have the `GltfModifierVersionExtension` attached to it
* yet.
*/
static std::optional<int64_t>
getVersion(const CesiumGltf::Model& model) noexcept;
/**
* @brief Sets the version number of the given model to the given value.
*
* This method creates the `GltfModifierVersionExtension` and attaches it to
* the `Model` if it does not already exist. If it does exist, the
* version number is updated.
*/
static void setVersion(CesiumGltf::Model& model, int64_t version) noexcept;
/**
* @brief The original name of this type.
*/
static constexpr const char* TypeName = "GltfModifierVersionExtension";
/**
* @brief The official name of the extension. This should be the same as its
* key in the `extensions` object.
* */
static constexpr const char* ExtensionName =
"CESIUM_INTERNAL_gltf_modifier_version";
/**
* @brief The current {@link GltfModifier} version number of the model.
*/
int64_t version = 0;
/**
* @brief Calculates the size in bytes of this object, including the contents
* of all collections, pointers, and strings. This will NOT include the size
* of any extensions attached to the object. Calling this method may be slow
* as it requires traversing the object's entire structure.
*/
int64_t getSizeBytes() const {
int64_t accum = 0;
accum += int64_t(sizeof(GltfModifierVersionExtension));
accum += CesiumUtility::ExtensibleObject::getSizeBytes() -
int64_t(sizeof(CesiumUtility::ExtensibleObject));
return accum;
}
};
} // namespace Cesium3DTilesSelection

View File

@ -26,6 +26,7 @@
namespace Cesium3DTilesSelection {
class TilesetContentLoader;
class GltfModifier;
#ifdef CESIUM_DEBUG_TILE_UNLOADING
class TileReferenceCountTracker {
@ -538,18 +539,24 @@ public:
/**
* @brief Determines if this tile requires worker-thread loading.
*
* @param pModifier The optional glTF modifier. If not `nullptr`, this method
* will return true if the tile needs worker thread glTF modification. See
* {@link TilesetExternals::pGltfModifier}.
* @return true if this Tile needs further work done in a worker thread to
* load it; otherwise, false.
*/
bool needsWorkerThreadLoading() const noexcept;
bool needsWorkerThreadLoading(const GltfModifier* pModifier) const noexcept;
/**
* @brief Determines if this tile requires main-thread loading.
*
* @param pModifier The optional glTF modifier. If not `nullptr`, this method
* will return true if the tile needs worker thread glTF modification. See
* {@link TilesetExternals::pGltfModifier}.
* @return true if this Tile needs further work done in the main thread to
* load it; otherwise, false.
*/
bool needsMainThreadLoading() const noexcept;
bool needsMainThreadLoading(const GltfModifier* pModifier) const noexcept;
/**
* @brief Adds a reference to this tile. A live reference will keep this tile

View File

@ -1,5 +1,6 @@
#pragma once
#include <Cesium3DTilesSelection/GltfModifierState.h>
#include <Cesium3DTilesSelection/Library.h>
#include <Cesium3DTilesSelection/TilesetMetadata.h>
#include <CesiumGeospatial/Projection.h>
@ -161,17 +162,19 @@ public:
void setCredits(const std::vector<CesiumUtility::Credit>& credits);
/**
* @brief Get the render resources created for the glTF model of the content
* @brief Get the renderer resources created for the glTF model of the
* content.
*
* @return The render resources that is created for the glTF model
* @return The renderer resources that are created for the glTF model
*/
void* getRenderResources() const noexcept;
/**
* @brief Set the render resources created for the glTF model of the content
* @brief Set the renderer resources created for the glTF model of the
* content.
*
* @param pRenderResources The render resources that is created for the glTF
* model
* @param pRenderResources The renderer resources that are created for the
* glTF model.
*/
void setRenderResources(void* pRenderResources) noexcept;
@ -195,9 +198,67 @@ public:
*/
void setLodTransitionFadePercentage(float percentage) noexcept;
/**
* @brief Gets the state of the {@link GltfModifier} processing of this
* tile's content.
* */
GltfModifierState getGltfModifierState() const noexcept;
/**
* @brief Sets the state of the {@link GltfModifier} processing of this
* tile's content.
*/
void setGltfModifierState(GltfModifierState modifierState) noexcept;
/**
* @brief Gets the modified model produced by the {@link GltfModifier} that is
* not yet available for rendering.
* */
const std::optional<CesiumGltf::Model>& getModifiedModel() const noexcept;
/**
* @brief Gets the renderer resources for the modified model produced by the
* {@link GltfModifier} that is not yet available for rendering. These resources
* are created by {@link IPrepareRendererResources::prepareInLoadThread}.
*/
void* getModifiedRenderResources() const noexcept;
/**
* @brief Stores the modified model and associated renderer resources produced
* by the {@link GltfModifier} that are not yet available for rendering. The
* renderer resources are created by
* {@link IPrepareRendererResources::prepareInLoadThread}.
*/
void setModifiedModelAndRenderResources(
CesiumGltf::Model&& modifiedModel,
void* pModifiedRenderResources) noexcept;
/**
* @brief Resets the modified model and renderer resources after they have
* been determined to be outdated and have been freed with
* {@link IPrepareRendererResources::free}.
*/
void resetModifiedModelAndRenderResources() noexcept;
/**
* @brief Overwrites this instance's model and renderer resources with the
* modified ones produced by {@link GltfModifier}. The new model and
* resources become eligible for rendering.
*
* After this method returns, {@link getModifiedModel} will return
* `std::nullopt` and {@link getModifiedRenderResources} will return
* `nullptr`.
*/
void replaceWithModifiedModel() noexcept;
private:
CesiumGltf::Model _model;
void* _pRenderResources;
GltfModifierState _modifierState;
std::optional<CesiumGltf::Model> _modifiedModel;
void* _pModifiedRenderResources;
CesiumRasterOverlays::RasterOverlayDetails _rasterOverlayDetails;
std::vector<CesiumUtility::Credit> _credits;
float _lodTransitionFadePercentage;

View File

@ -106,6 +106,12 @@ public:
*/
void unregister() noexcept;
/**
* @brief Determines if this requester is currently registered with a
* {@link Tileset}.
*/
bool isRegistered() const noexcept;
protected:
/**
* @brief Constructs a new instance.
@ -142,7 +148,7 @@ private:
CesiumUtility::IntrusivePointer<TilesetContentManager>
_pTilesetContentManager;
friend class Tileset;
friend class TilesetContentManager;
};
} // namespace Cesium3DTilesSelection

View File

@ -78,7 +78,7 @@ struct CESIUM3DTILESSELECTION_API TileLoadResult {
/**
* @brief The up axis of glTF content.
*/
CesiumGeometry::Axis glTFUpAxis;
CesiumGeometry::Axis glTFUpAxis = CesiumGeometry::Axis::Y;
/**
* @brief A tile can potentially store a more fit bounding volume along with

View File

@ -141,6 +141,12 @@ public:
*/
void setShowCreditsOnScreen(bool showCreditsOnScreen) noexcept;
/**
* @brief Gets the @ref CesiumUtility::CreditSource that identifies this
* tileset's credits with the @ref CesiumUtility::CreditSystem.
*/
const CesiumUtility::CreditSource& getCreditSource() const noexcept;
/**
* @brief Gets the {@link TilesetExternals} that summarize the external
* interfaces used by this tileset.

View File

@ -18,7 +18,9 @@ class CreditSystem;
}
namespace Cesium3DTilesSelection {
class IPrepareRendererResources;
class GltfModifier;
/**
* @brief External interfaces used by a {@link Tileset}.
@ -76,6 +78,14 @@ public:
*/
CesiumUtility::IntrusivePointer<TilesetSharedAssetSystem> pSharedAssetSystem =
TilesetSharedAssetSystem::getDefault();
/**
* Optional user-controlled tile loading post-processing stage that can modify
* the glTF meshes (e.g., split or merge them).
*
* @see Cesium3DTilesSelection::GltfModifier
*/
std::shared_ptr<GltfModifier> pGltfModifier = {};
};
} // namespace Cesium3DTilesSelection

View File

@ -9,11 +9,13 @@
#include <CesiumUtility/TreeTraversalState.h>
#include <cstddef>
#include <memory>
#include <unordered_map>
#include <vector>
namespace Cesium3DTilesSelection {
class GltfModifier;
class Tile;
class Tileset;
class TilesetContentManager;
@ -96,8 +98,13 @@ public:
* undefined behavior in release builds.
*
* @param task The tile load task to add to the queue.
* @param pModifier The optional glTF modifier. If not `nullptr`, this method
* will also add the tile to load queue if it needs glTF modification. See
* {@link TilesetExternals::pGltfModifier}.
*/
void addToLoadQueue(const TileLoadTask& task);
void addToLoadQueue(
const TileLoadTask& task,
const std::shared_ptr<GltfModifier>& pModifier = nullptr);
/**
* @brief A checkpoint within this view group's load queue.
@ -157,8 +164,11 @@ public:
size_t getMainThreadLoadQueueLength() const;
/**
* @brief Starts a new frame, clearing the set of tiles to be loaded so that a
* new set can be selected.
* @brief Starts a new frame.
*
* This method clears the set of tiles to be loaded so that a new set can be
* selected. It also makes the current tile selection state the previous one
* and releases references to tiles in the old previous one.
*
* @param tileset The tileset that is starting the new frame.
* @param frameState The state of the new frame.
@ -167,11 +177,9 @@ public:
startNewFrame(const Tileset& tileset, const TilesetFrameState& frameState);
/**
* @brief Finishes the current frame by making the current tile selection
* state the previous one and releasing references to tiles in the old
* previous one.
* @brief Finishes the current frame.
*
* This method also updates the load progress percentage returned by
* This method updates the load progress percentage returned by
* {@link getPreviousLoadProgressPercentage} and makes sure credits used by
* this view group have been referenced on the
* {@link CesiumUtility::CreditSystem}.
@ -218,6 +226,24 @@ public:
/** @inheritdoc */
const Tile* getNextTileToLoadInMainThread() override;
/**
* @brief Checks if a given credit is referenced in the most recently
* completed frame.
*
* Note that this method checks the most recently _completed_ frame. So, after
* a call to @ref finishFrame (the common case), this method will check the
* frame that was just finished. If called in between calls to @ref
* startNewFrame and @ref finishFrame (i.e., during the course of a call to
* @ref Tileset::updateViewGroup), it will check the frame prior to the
* current, in-progress one, because the current one has not yet been
* completed.
*
* @param credit The credit to test.
* @return True if the credit was referenced in this view group's most
* recently completed frame.
*/
bool isCreditReferenced(CesiumUtility::Credit credit) const noexcept;
private:
double _weight = 1.0;
std::vector<TileLoadTask> _mainThreadLoadQueue;

View File

@ -0,0 +1,232 @@
#include "TilesetContentManager.h"
#include <Cesium3DTilesSelection/GltfModifier.h>
#include <Cesium3DTilesSelection/GltfModifierState.h>
#include <Cesium3DTilesSelection/GltfModifierVersionExtension.h>
#include <Cesium3DTilesSelection/LoadedTileEnumerator.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileContent.h>
#include <Cesium3DTilesSelection/TileLoadRequester.h>
#include <Cesium3DTilesSelection/TilesetExternals.h>
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumGltf/Model.h>
#include <CesiumUtility/Assert.h>
#include <spdlog/logger.h>
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>
namespace Cesium3DTilesSelection {
GltfModifier::GltfModifier()
: _currentVersion(),
_pRootTile(nullptr),
_workerThreadQueue(),
_mainThreadQueue(){};
GltfModifier::~GltfModifier() = default;
std::optional<int64_t> GltfModifier::getCurrentVersion() const {
return this->_currentVersion;
}
bool GltfModifier::isActive() const {
return this->getCurrentVersion().has_value();
}
void GltfModifier::trigger() {
if (!this->_currentVersion) {
this->_currentVersion = 0;
} else {
++(*this->_currentVersion);
}
if (!this->isRegistered()) {
return;
}
// Add all already-loaded tiles to this requester's worker thread load queue.
// Tiles that are in ContentLoading will be added to this queue when they
// finish.
LoadedConstTileEnumerator enumerator(this->_pRootTile);
for (const Tile& tile : enumerator) {
if (this->needsWorkerThreadModification(tile)) {
this->_workerThreadQueue.emplace_back(&tile);
}
}
}
bool GltfModifier::needsWorkerThreadModification(const Tile& tile) const {
std::optional<int64_t> modelVersion = this->getCurrentVersion();
if (!modelVersion)
return false;
// If the tile is not loaded at all, there's no need to modify it.
if (tile.getState() != TileLoadState::Done &&
tile.getState() != TileLoadState::ContentLoaded) {
return false;
}
const TileRenderContent* pRenderContent =
tile.getContent().getRenderContent();
// If a tile has no render content, there's nothing to modify.
if (!pRenderContent)
return false;
// We can't modify a tile for which modification is already in progress.
if (pRenderContent->getGltfModifierState() ==
GltfModifierState::WorkerRunning) {
return false;
}
// If modification is WorkerDone, and the version is already up-to-date, we
// don't need to do it again. But if it's outdated, we want to run the worker
// thread modification again.
if (pRenderContent->getGltfModifierState() == GltfModifierState::WorkerDone) {
const std::optional<CesiumGltf::Model>& maybeModifiedModel =
pRenderContent->getModifiedModel();
bool hasUpToDateModifiedModel =
maybeModifiedModel && GltfModifierVersionExtension::getVersion(
*maybeModifiedModel) == modelVersion;
return !hasUpToDateModifiedModel;
} else {
// Worker is idle. Modification is needed if the model version is out of
// date.
CESIUM_ASSERT(
pRenderContent->getGltfModifierState() == GltfModifierState::Idle);
bool hasUpToDateModel = GltfModifierVersionExtension::getVersion(
pRenderContent->getModel()) == modelVersion;
return !hasUpToDateModel;
}
}
bool GltfModifier::needsMainThreadModification(const Tile& tile) const {
std::optional<int64_t> modelVersion = this->getCurrentVersion();
if (!modelVersion)
return false;
// Only tiles already Done loading need main thread modification. For
// ContentLoaded, the modified mesh is applied by the normal transition to
// Done.
if (tile.getState() != TileLoadState::Done)
return false;
// Only tiles with render content can be modified.
const TileRenderContent* pRenderContent =
tile.getContent().getRenderContent();
if (!pRenderContent)
return false;
// We only need to do main thread processing after the worker thread
// processing has completed.
if (pRenderContent->getGltfModifierState() != GltfModifierState::WorkerDone) {
return false;
}
// We only need to do main thread processing if there's a modified model.
const std::optional<CesiumGltf::Model>& maybeModifiedModel =
pRenderContent->getModifiedModel();
if (!maybeModifiedModel)
return false;
// If the version of the modified model is wrong (outdated), there's no point
// in doing main thread processing. We need to do worker thread processing
// again first.
if (GltfModifierVersionExtension::getVersion(*maybeModifiedModel) !=
modelVersion) {
return false;
}
return true;
}
CesiumAsync::Future<void> GltfModifier::onRegister(
TilesetContentManager& contentManager,
const TilesetMetadata& tilesetMetadata,
const Tile& rootTile) {
this->_pRootTile = &rootTile;
contentManager.registerTileRequester(*this);
const TilesetExternals& externals = contentManager.getExternals();
return this->onRegister(
externals.asyncSystem,
externals.pAssetAccessor,
externals.pLogger,
tilesetMetadata,
rootTile);
}
void GltfModifier::onUnregister(TilesetContentManager& /* contentManager */) {
this->unregister();
this->_mainThreadQueue.clear();
this->_workerThreadQueue.clear();
}
CesiumAsync::Future<void> GltfModifier::onRegister(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& /* pAssetAccessor */,
const std::shared_ptr<spdlog::logger>& /* pLogger */,
const TilesetMetadata& /* tilesetMetadata */,
const Tile& /* rootTile */) {
return asyncSystem.createResolvedFuture();
}
void GltfModifier::onOldVersionContentLoadingComplete(const Tile& tile) {
CESIUM_ASSERT(
tile.getState() == TileLoadState::ContentLoaded ||
tile.getState() == TileLoadState::Failed ||
tile.getState() == TileLoadState::FailedTemporarily);
if (tile.getState() == TileLoadState::ContentLoaded) {
// Tile just transitioned from ContentLoading -> ContentLoaded, but it did
// so based on the load version. Add it to the worker thread queue in order
// to re-run the GltfModifier on it.
if (this->isRegistered() && this->needsWorkerThreadModification(tile)) {
this->_workerThreadQueue.emplace_back(&tile);
}
}
}
void GltfModifier::onWorkerThreadApplyComplete(const Tile& tile) {
// GltfModifier::apply just finished, so now we need to do the main-thread
// processing of the new version. But if the new version is already outdated,
// we need to do worker thread modification (again) instead of main thread
// modification.
if (this->isRegistered()) {
if (tile.needsMainThreadLoading(this)) {
this->_mainThreadQueue.emplace_back(&tile);
} else if (tile.needsWorkerThreadLoading(this)) {
this->_workerThreadQueue.emplace_back(&tile);
}
}
}
double GltfModifier::getWeight() const { return 0.5; }
bool GltfModifier::hasMoreTilesToLoadInWorkerThread() const {
return !this->_workerThreadQueue.empty();
}
const Tile* GltfModifier::getNextTileToLoadInWorkerThread() {
CESIUM_ASSERT(!this->_workerThreadQueue.empty());
const Tile* pResult = this->_workerThreadQueue.back().get();
this->_workerThreadQueue.pop_back();
return pResult;
}
bool GltfModifier::hasMoreTilesToLoadInMainThread() const {
return !this->_mainThreadQueue.empty();
}
const Tile* GltfModifier::getNextTileToLoadInMainThread() {
CESIUM_ASSERT(!this->_mainThreadQueue.empty());
const Tile* pResult = this->_mainThreadQueue.back().get();
this->_mainThreadQueue.pop_back();
return pResult;
}
} // namespace Cesium3DTilesSelection

View File

@ -0,0 +1,25 @@
#include <Cesium3DTilesSelection/GltfModifierVersionExtension.h>
#include <CesiumGltf/Model.h>
#include <cstdint>
#include <optional>
namespace Cesium3DTilesSelection {
/* static */ std::optional<int64_t> GltfModifierVersionExtension::getVersion(
const CesiumGltf::Model& model) noexcept {
const auto* pExtension = model.getExtension<GltfModifierVersionExtension>();
return pExtension ? std::make_optional(pExtension->version) : std::nullopt;
}
/* static */ void GltfModifierVersionExtension::setVersion(
CesiumGltf::Model& model,
int64_t version) noexcept {
auto* pExtension = model.getExtension<GltfModifierVersionExtension>();
if (!pExtension) {
pExtension = &model.addExtension<GltfModifierVersionExtension>();
}
pExtension->version = version;
}
} // namespace Cesium3DTilesSelection

View File

@ -1,5 +1,7 @@
#include "TilesetContentManager.h"
#include <Cesium3DTilesSelection/GltfModifier.h>
#include <Cesium3DTilesSelection/GltfModifierVersionExtension.h>
#include <Cesium3DTilesSelection/RasterMappedTo3DTile.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileContent.h>
@ -18,6 +20,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
@ -271,16 +274,34 @@ bool anyRasterOverlaysNeedLoading(const Tile& tile) noexcept {
} // namespace
bool Tile::needsWorkerThreadLoading() const noexcept {
TileLoadState state = this->getState();
return state == TileLoadState::Unloaded ||
state == TileLoadState::FailedTemporarily ||
anyRasterOverlaysNeedLoading(*this);
bool Tile::needsWorkerThreadLoading(
const GltfModifier* pModifier) const noexcept {
if (this->getState() == TileLoadState::Unloaded ||
this->getState() == TileLoadState::FailedTemporarily)
return true;
if (pModifier && pModifier->needsWorkerThreadModification(*this))
return true;
if (anyRasterOverlaysNeedLoading(*this))
return true;
return false;
}
bool Tile::needsMainThreadLoading() const noexcept {
return this->getState() == TileLoadState::ContentLoaded &&
this->isRenderContent();
bool Tile::needsMainThreadLoading(
const GltfModifier* pModifier) const noexcept {
// Only render content needs main thread loading.
if (!this->isRenderContent())
return false;
if (this->getState() == TileLoadState::ContentLoaded)
return true;
if (pModifier && pModifier->needsMainThreadModification(*this))
return true;
return false;
}
void Tile::setParent(Tile* pParent) noexcept {

View File

@ -1,8 +1,11 @@
#include <Cesium3DTilesSelection/GltfModifierState.h>
#include <Cesium3DTilesSelection/TileContent.h>
#include <CesiumGltf/Model.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/CreditSystem.h>
#include <memory>
#include <optional>
#include <utility>
#include <variant>
#include <vector>
@ -14,22 +17,69 @@ namespace Cesium3DTilesSelection {
TileRenderContent::TileRenderContent(CesiumGltf::Model&& model)
: _model{std::move(model)},
_pRenderResources{nullptr},
_modifierState{GltfModifierState::Idle},
_modifiedModel{},
_pModifiedRenderResources(nullptr),
_rasterOverlayDetails{},
_credits{},
_lodTransitionFadePercentage{0.0f} {}
const CesiumGltf::Model& TileRenderContent::getModel() const noexcept {
return _model;
return this->_model;
}
CesiumGltf::Model& TileRenderContent::getModel() noexcept { return _model; }
CesiumGltf::Model& TileRenderContent::getModel() noexcept {
return this->_model;
}
void TileRenderContent::setModel(const CesiumGltf::Model& model) {
_model = model;
this->_model = model;
}
void TileRenderContent::setModel(CesiumGltf::Model&& model) {
_model = std::move(model);
this->_model = std::move(model);
}
GltfModifierState TileRenderContent::getGltfModifierState() const noexcept {
return this->_modifierState;
}
void TileRenderContent::setGltfModifierState(
GltfModifierState modifierState) noexcept {
this->_modifierState = modifierState;
}
const std::optional<CesiumGltf::Model>&
TileRenderContent::getModifiedModel() const noexcept {
return this->_modifiedModel;
}
void TileRenderContent::setModifiedModelAndRenderResources(
CesiumGltf::Model&& modifiedModel,
void* pModifiedRenderResources) noexcept {
this->_modifiedModel = std::move(modifiedModel);
this->_pModifiedRenderResources = pModifiedRenderResources;
}
void* TileRenderContent::getModifiedRenderResources() const noexcept {
return this->_pModifiedRenderResources;
}
void TileRenderContent::resetModifiedModelAndRenderResources() noexcept {
this->_pModifiedRenderResources = nullptr;
this->_modifiedModel.reset();
}
void TileRenderContent::replaceWithModifiedModel() noexcept {
CESIUM_ASSERT(this->_modifiedModel);
if (this->_modifiedModel) {
this->_model = std::move(*this->_modifiedModel);
// reset after move because this is tested for nullopt in
// Tile::needsWorkerThreadLoading:
this->_modifiedModel.reset();
this->_pRenderResources = this->_pModifiedRenderResources;
this->_pModifiedRenderResources = nullptr;
}
}
const RasterOverlayDetails&
@ -92,20 +142,20 @@ TileContent::TileContent(std::unique_ptr<TileExternalContent>&& content)
: _contentKind{std::move(content)} {}
void TileContent::setContentKind(TileUnknownContent content) {
_contentKind = content;
this->_contentKind = content;
}
void TileContent::setContentKind(TileEmptyContent content) {
_contentKind = content;
this->_contentKind = content;
}
void TileContent::setContentKind(
std::unique_ptr<TileExternalContent>&& content) {
_contentKind = std::move(content);
this->_contentKind = std::move(content);
}
void TileContent::setContentKind(std::unique_ptr<TileRenderContent>&& content) {
_contentKind = std::move(content);
this->_contentKind = std::move(content);
}
bool TileContent::isUnknownContent() const noexcept {

View File

@ -14,6 +14,10 @@ void TileLoadRequester::unregister() noexcept {
}
}
bool TileLoadRequester::isRegistered() const noexcept {
return this->_pTilesetContentManager != nullptr;
}
TileLoadRequester::TileLoadRequester() noexcept
: _pTilesetContentManager(nullptr) {}

View File

@ -170,6 +170,10 @@ void Tileset::setShowCreditsOnScreen(bool showCreditsOnScreen) noexcept {
}
}
const CesiumUtility::CreditSource& Tileset::getCreditSource() const noexcept {
return this->_pTilesetContentManager->getCreditSource();
}
const Tile* Tileset::getRootTile() const noexcept {
return this->_pTilesetContentManager->getRootTile();
}
@ -468,18 +472,7 @@ void Tileset::loadTiles() {
}
void Tileset::registerLoadRequester(TileLoadRequester& requester) {
if (requester._pTilesetContentManager == this->_pTilesetContentManager) {
return;
}
if (requester._pTilesetContentManager != nullptr) {
requester._pTilesetContentManager->unregisterTileRequester(requester);
}
requester._pTilesetContentManager = this->_pTilesetContentManager;
if (requester._pTilesetContentManager != nullptr) {
requester._pTilesetContentManager->registerTileRequester(requester);
}
this->_pTilesetContentManager->registerTileRequester(requester);
}
int32_t Tileset::getNumberOfTilesLoaded() const {
@ -1115,6 +1108,24 @@ bool Tileset::_kickDescendantsAndRenderTile(
selectionState.kick();
});
// If any kicked tiles were rendered last frame, add them to the
// tilesFadingOut. This is unlikely! It would imply that a tile rendered last
// frame has suddenly become unrenderable, and therefore eligible for kicking.
//
// In general, it's possible that a Tile previously traversed has been deleted
// completely, so we have to be careful about dereferencing the Tile pointers
// given to the callback below. However, we can be certain that a Tile that
// was rendered last frame has _not_ been deleted yet.
traversalState.forEachPreviousDescendant(
[&result](
const Tile::Pointer& pTile,
const TileSelectionState& previousState) {
addToTilesFadingOutIfPreviouslyRendered(
previousState.getResult(),
*pTile,
result);
});
// Remove all descendants from the render list and add this tile.
std::vector<Tile::ConstPointer>& renderList = result.tilesToRenderThisFrame;
renderList.erase(
@ -1140,8 +1151,8 @@ bool Tileset::_kickDescendantsAndRenderTile(
getPreviousState(frameState.viewGroup, tile).getResult();
const bool wasRenderedLastFrame =
lastFrameSelectionState == TileSelectionState::Result::Rendered;
const bool wasReallyRenderedLastFrame =
wasRenderedLastFrame && tile.isRenderable();
const bool isRenderable = tile.isRenderable();
const bool wasReallyRenderedLastFrame = wasRenderedLastFrame && isRenderable;
if (!wasReallyRenderedLastFrame &&
traversalDetails.notYetRenderableCount >
@ -1165,10 +1176,8 @@ bool Tileset::_kickDescendantsAndRenderTile(
queuedForLoad = true;
}
bool isRenderable = tile.isRenderable();
traversalDetails.allAreRenderable = isRenderable;
traversalDetails.anyWereRenderedLastFrame =
isRenderable && wasRenderedLastFrame;
traversalDetails.anyWereRenderedLastFrame = wasReallyRenderedLastFrame;
return queuedForLoad;
}
@ -1515,7 +1524,8 @@ void Tileset::addTileToLoadQueue(
TileLoadPriorityGroup priorityGroup,
double priority) {
frameState.viewGroup.addToLoadQueue(
TileLoadTask{&tile, priorityGroup, priority});
TileLoadTask{&tile, priorityGroup, priority},
this->_externals.pGltfModifier);
}
Tileset::TraversalDetails Tileset::createTraversalDetailsForSingleTile(

View File

@ -6,6 +6,9 @@
#include "TilesetJsonLoader.h"
#include <Cesium3DTilesSelection/BoundingVolume.h>
#include <Cesium3DTilesSelection/GltfModifier.h>
#include <Cesium3DTilesSelection/GltfModifierState.h>
#include <Cesium3DTilesSelection/GltfModifierVersionExtension.h>
#include <Cesium3DTilesSelection/IPrepareRendererResources.h>
#include <Cesium3DTilesSelection/RasterMappedTo3DTile.h>
#include <Cesium3DTilesSelection/RasterOverlayCollection.h>
@ -45,6 +48,7 @@
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlayUtilities.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Math.h>
#include <CesiumUtility/ReferenceCounted.h>
@ -71,6 +75,7 @@
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
@ -555,7 +560,8 @@ postProcessContentInWorkerThread(
TileLoadResult&& result,
std::vector<CesiumGeospatial::Projection>&& projections,
TileContentLoadInfo&& tileLoadInfo,
const std::any& rendererOptions) {
const std::any& rendererOptions,
const std::shared_ptr<GltfModifier>& pGltfModifier) {
CESIUM_ASSERT(
result.state == TileLoadResultState::Success &&
"This function requires result to be success");
@ -581,6 +587,9 @@ postProcessContentInWorkerThread(
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(
@ -593,8 +602,9 @@ postProcessContentInWorkerThread(
.thenInWorkerThread([result = std::move(result),
projections = std::move(projections),
tileLoadInfo = std::move(tileLoadInfo),
rendererOptions](CesiumGltfReader::GltfReaderResult&&
gltfResult) mutable {
version,
pGltfModifier](CesiumGltfReader::GltfReaderResult&&
gltfResult) mutable {
if (!gltfResult.errors.empty()) {
if (result.pCompletedRequest) {
SPDLOG_LOGGER_ERROR(
@ -627,12 +637,11 @@ postProcessContentInWorkerThread(
}
if (!gltfResult.model) {
return tileLoadInfo.asyncSystem.createResolvedFuture(
TileLoadResultAndRenderResources{
TileLoadResult::createFailedResult(
result.pAssetAccessor,
nullptr),
nullptr});
return tileLoadInfo.asyncSystem
.createResolvedFuture(TileLoadResult::createFailedResult(
result.pAssetAccessor,
nullptr))
.thenPassThrough(std::move(tileLoadInfo));
}
result.contentKind = std::move(*gltfResult.model);
@ -645,6 +654,51 @@ postProcessContentInWorkerThread(
std::move(projections),
tileLoadInfo);
if (pGltfModifier && version) {
// Apply the glTF modifier right away, otherwise it will be
// triggered immediately after the renderer-side resources
// have been created, which is both inefficient and a cause
// of visual glitches (the model will appear briefly in its
// unmodified state before stabilizing)
const CesiumGltf::Model& model =
std::get<CesiumGltf::Model>(result.contentKind);
return pGltfModifier
->apply(GltfModifierInput{
.version = *version,
.asyncSystem = tileLoadInfo.asyncSystem,
.pAssetAccessor = tileLoadInfo.pAssetAccessor,
.pLogger = tileLoadInfo.pLogger,
.previousModel = model,
.tileTransform = tileLoadInfo.tileTransform})
.thenInWorkerThread(
[result = std::move(result), version](
std::optional<GltfModifierOutput>&& modified) mutable {
if (modified) {
result.contentKind = std::move(modified->modifiedModel);
}
CesiumGltf::Model* pModel =
std::get_if<CesiumGltf::Model>(&result.contentKind);
if (pModel) {
GltfModifierVersionExtension::setVersion(
*pModel,
*version);
}
return result;
})
.thenPassThrough(std::move(tileLoadInfo));
} else {
return tileLoadInfo.asyncSystem
.createResolvedFuture(std::move(result))
.thenPassThrough(std::move(tileLoadInfo));
}
})
.thenInWorkerThread([rendererOptions](
std::tuple<TileContentLoadInfo, TileLoadResult>&&
tuple) {
auto& [tileLoadInfo, result] = tuple;
// create render resources
if (tileLoadInfo.pPrepareRendererResources) {
return tileLoadInfo.pPrepareRendererResources->prepareInLoadThread(
@ -659,6 +713,20 @@ postProcessContentInWorkerThread(
}
});
}
std::optional<Credit> createUserCredit(
const TilesetOptions& tilesetOptions,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const CreditSource& creditSource) {
if (!tilesetOptions.credit || !pCreditSystem)
return std::nullopt;
return pCreditSystem->createCredit(
creditSource,
*tilesetOptions.credit,
tilesetOptions.showCreditsOnScreen);
}
} // namespace
TilesetContentManager::TilesetContentManager(
@ -669,13 +737,8 @@ TilesetContentManager::TilesetContentManager(
: _externals{externals},
_requestHeaders{tilesetOptions.requestHeaders},
_pLoader{std::move(pLoader)},
_pRootTile{std::move(pRootTile)},
_userCredit(
(tilesetOptions.credit && externals.pCreditSystem)
? std::optional<Credit>(externals.pCreditSystem->createCredit(
tilesetOptions.credit.value(),
tilesetOptions.showCreditsOnScreen))
: std::nullopt),
_pRootTile{nullptr},
_userCredit(),
_tilesetCredits{},
_overlayCollection(
LoadedTileEnumerator(pRootTile.get()),
@ -697,14 +760,27 @@ TilesetContentManager::TilesetContentManager(
_roundRobinValueWorker(0.0),
_roundRobinValueMain(0.0),
_requesterFractions(),
_requestersWithRequests() {
_requestersWithRequests(),
_creditSource(externals.pCreditSystem) {
CESIUM_ASSERT(this->_pLoader != nullptr);
this->_userCredit = createUserCredit(
tilesetOptions,
externals.pCreditSystem,
this->_creditSource);
this->_upsampler.setOwner(*this);
CESIUM_ASSERT(this->_pLoader != nullptr);
this->_overlayCollection.setLoadedTileEnumerator(
LoadedTileEnumerator(this->_pRootTile.get()));
this->_pLoader->setOwner(*this);
this->_rootTileAvailablePromise.resolve();
this->notifyTileStartLoading(nullptr);
this->registerGltfModifier(pRootTile.get())
.thenInMainThread([this, pRootTile = std::move(pRootTile)]() mutable {
this->_pRootTile = std::move(pRootTile);
this->_overlayCollection.setLoadedTileEnumerator(
LoadedTileEnumerator(this->_pRootTile.get()));
this->_pLoader->setOwner(*this);
this->_rootTileAvailablePromise.resolve();
this->notifyTileDoneLoading(this->_pRootTile.get());
});
}
TilesetContentManager::TilesetContentManager(
@ -715,12 +791,7 @@ TilesetContentManager::TilesetContentManager(
_requestHeaders{tilesetOptions.requestHeaders},
_pLoader{},
_pRootTile{},
_userCredit(
(tilesetOptions.credit && externals.pCreditSystem)
? std::optional<Credit>(externals.pCreditSystem->createCredit(
tilesetOptions.credit.value(),
tilesetOptions.showCreditsOnScreen))
: std::nullopt),
_userCredit(),
_tilesetCredits{},
_overlayCollection(
LoadedTileEnumerator(nullptr),
@ -742,7 +813,13 @@ TilesetContentManager::TilesetContentManager(
_roundRobinValueWorker(0.0),
_roundRobinValueMain(0.0),
_requesterFractions(),
_requestersWithRequests() {
_requestersWithRequests(),
_creditSource(externals.pCreditSystem) {
this->_userCredit = createUserCredit(
tilesetOptions,
externals.pCreditSystem,
this->_creditSource);
this->_upsampler.setOwner(*this);
if (!url.empty()) {
@ -846,6 +923,13 @@ TilesetContentManager::TilesetContentManager(
return asyncSystem.createResolvedFuture(std::move(result));
}
})
.thenInMainThread(
[thiz](TilesetContentLoaderResult<TilesetContentLoader>&& result) {
return thiz->registerGltfModifier(result.pRootTile.get())
.thenImmediately([result = std::move(result)]() mutable {
return std::move(result);
});
})
.thenInMainThread(
[thiz, errorCallback = tilesetOptions.loadErrorCallback](
TilesetContentLoaderResult<TilesetContentLoader>&& result) {
@ -876,12 +960,7 @@ TilesetContentManager::TilesetContentManager(
_requestHeaders{tilesetOptions.requestHeaders},
_pLoader{},
_pRootTile{},
_userCredit(
(tilesetOptions.credit && externals.pCreditSystem)
? std::optional<Credit>(externals.pCreditSystem->createCredit(
tilesetOptions.credit.value(),
tilesetOptions.showCreditsOnScreen))
: std::nullopt),
_userCredit(),
_tilesetCredits{},
_overlayCollection(
LoadedTileEnumerator(nullptr),
@ -903,7 +982,13 @@ TilesetContentManager::TilesetContentManager(
_roundRobinValueWorker(0.0),
_roundRobinValueMain(0.0),
_requesterFractions(),
_requestersWithRequests() {
_requestersWithRequests(),
_creditSource(externals.pCreditSystem) {
this->_userCredit = createUserCredit(
tilesetOptions,
externals.pCreditSystem,
this->_creditSource);
this->_upsampler.setOwner(*this);
if (loaderFactory.isValid()) {
@ -928,6 +1013,13 @@ TilesetContentManager::TilesetContentManager(
loaderFactory
.createLoader(externals, tilesetOptions, authorizationChangeListener)
.thenInMainThread(
[thiz](TilesetContentLoaderResult<TilesetContentLoader>&& result) {
return thiz->registerGltfModifier(result.pRootTile.get())
.thenImmediately([result = std::move(result)]() mutable {
return std::move(result);
});
})
.thenInMainThread(
[thiz, errorCallback = tilesetOptions.loadErrorCallback](
TilesetContentLoaderResult<TilesetContentLoader>&& result) {
@ -981,11 +1073,203 @@ TilesetContentManager::~TilesetContentManager() noexcept {
this->_destructionCompletePromise.resolve();
}
void TilesetContentManager::reapplyGltfModifier(
Tile& tile,
const TilesetOptions& tilesetOptions,
TileRenderContent* pRenderContent) noexcept {
// An existing modified model must be the wrong version (or we wouldn't be
// here), so discard it.
if (pRenderContent->getModifiedModel()) {
CESIUM_ASSERT(
pRenderContent->getGltfModifierState() ==
GltfModifierState::WorkerDone);
this->_externals.pPrepareRendererResources->free(
tile,
pRenderContent->getModifiedRenderResources(),
nullptr);
pRenderContent->resetModifiedModelAndRenderResources();
pRenderContent->setGltfModifierState(GltfModifierState::Idle);
}
this->notifyTileStartLoading(&tile);
pRenderContent->setGltfModifierState(GltfModifierState::WorkerRunning);
const CesiumGltf::Model& previousModel = pRenderContent->getModel();
const TilesetExternals& externals = this->getExternals();
// Get the version here, in the main thread, because it may change while the
// worker is running.
CESIUM_ASSERT(externals.pGltfModifier->getCurrentVersion());
int64_t version = externals.pGltfModifier->getCurrentVersion().value_or(-1);
// It is safe to capture the TilesetExternals and Model by reference because
// the TilesetContentManager guarantees both will continue to exist and are
// immutable while modification is in progress.
//
// This is largely true of Tile as well, except that Tiles are nodes in a
// larger graph, and the entire graph is _not_ guaranteed to be immutable. So
// best to avoid handing a `Tile` instance to a worker thread.
//
// We capture an IntrusivePointer to a Tile in the main thread continuations
// at the end in order to keep the Tile, its content, and the
// TilesetContentManager alive during the entire process.
externals.asyncSystem
.runInWorkerThread([&externals,
&previousModel,
version,
tileTransform = tile.getTransform()] {
return externals.pGltfModifier->apply(GltfModifierInput{
.version = version,
.asyncSystem = externals.asyncSystem,
.pAssetAccessor = externals.pAssetAccessor,
.pLogger = externals.pLogger,
.previousModel = previousModel,
.tileTransform = tileTransform});
})
.thenInWorkerThread([&externals,
&previousModel,
pRenderContent,
tileTransform = tile.getTransform(),
tileBoundingVolume = tile.getBoundingVolume(),
tileContentBoundingVolume =
tile.getContentBoundingVolume(),
rendererOptions = tilesetOptions.rendererOptions](
std::optional<GltfModifierOutput>&& modified) {
TileLoadResult tileLoadResult;
tileLoadResult.state = TileLoadResultState::Success;
tileLoadResult.pAssetAccessor = externals.pAssetAccessor;
tileLoadResult.rasterOverlayDetails =
pRenderContent->getRasterOverlayDetails();
tileLoadResult.initialBoundingVolume = tileBoundingVolume;
tileLoadResult.initialContentBoundingVolume = tileContentBoundingVolume;
{
const CesiumGltf::Model& model =
modified ? modified->modifiedModel : previousModel;
const auto it = model.extras.find("gltfUpAxis");
if (it != model.extras.end()) {
tileLoadResult.glTFUpAxis = static_cast<CesiumGeometry::Axis>(
it->second.getSafeNumberOrDefault(1));
}
}
if (modified) {
tileLoadResult.contentKind = std::move(modified->modifiedModel);
} else {
tileLoadResult.contentKind = previousModel;
}
if (modified && externals.pPrepareRendererResources) {
return externals.pPrepareRendererResources->prepareInLoadThread(
externals.asyncSystem,
std::move(tileLoadResult),
tileTransform,
rendererOptions);
} else {
return externals.asyncSystem
.createResolvedFuture<TileLoadResultAndRenderResources>(
TileLoadResultAndRenderResources{
std::move(tileLoadResult),
nullptr});
}
})
.thenInMainThread([this, version, pTile = Tile::Pointer(&tile)](
TileLoadResultAndRenderResources&& pair) {
TileRenderContent* pRenderContent =
pTile->getContent().getRenderContent();
CESIUM_ASSERT(pRenderContent != nullptr);
this->notifyTileDoneLoading(pTile.get());
if (std::holds_alternative<TileUnknownContent>(
pair.result.contentKind)) {
// GltfModifier did not actually modify the model.
pRenderContent->resetModifiedModelAndRenderResources();
pRenderContent->setGltfModifierState(GltfModifierState::Idle);
// Even though the GltfModifier chose not to modify anything, the
// model is now up-to-date with the version that triggered this run.
GltfModifierVersionExtension::setVersion(
pRenderContent->getModel(),
version);
} else if (pTile->getState() == TileLoadState::ContentLoaded) {
// This Tile is in the ContentLoaded state, which means that it was
// (partially) loaded before the GltfModifier triggered to the current
// version. In that case, we need to free the previous renderer
// resources but can then send the tile through the rest of the normal
// pipeline. We know a Tile in the ContentLoaded state isn't already
// being rendered.
if (this->_externals.pPrepareRendererResources) {
this->_externals.pPrepareRendererResources->free(
*pTile,
pRenderContent->getRenderResources(),
nullptr);
}
pRenderContent->setModel(
std::move(std::get<CesiumGltf::Model>(pair.result.contentKind)));
pRenderContent->setRenderResources(pair.pRenderResources);
pRenderContent->setGltfModifierState(GltfModifierState::Idle);
// The model is now up-to-date with the version that triggered this
// run.
GltfModifierVersionExtension::setVersion(
pRenderContent->getModel(),
version);
} else {
// This is a reapply of the GltfModifier to an already loaded and
// potentially rendered tile.
CESIUM_ASSERT(pTile->getState() == TileLoadState::Done);
pRenderContent->setGltfModifierState(GltfModifierState::WorkerDone);
// The modified model is up-to-date with the version that triggered
// this run.
CesiumGltf::Model& modifiedModel =
std::get<CesiumGltf::Model>(pair.result.contentKind);
GltfModifierVersionExtension::setVersion(modifiedModel, version);
pRenderContent->setModifiedModelAndRenderResources(
std::move(modifiedModel),
pair.pRenderResources);
this->_externals.pGltfModifier->onWorkerThreadApplyComplete(*pTile);
}
})
.catchInMainThread(
[this, pTile = Tile::Pointer(&tile), &externals, version](
std::exception&& e) {
pTile->getContent().getRenderContent()->setGltfModifierState(
GltfModifierState::WorkerDone);
this->notifyTileDoneLoading(pTile.get());
SPDLOG_LOGGER_ERROR(
externals.pLogger,
"An unexpected error occurred when reapplying GltfModifier: {}",
e.what());
// Even though the GltfModifier failed, we mark the model up-to-date
// with the version that triggered this run so that we won't try to
// run again for this version.
TileRenderContent* pRenderContent =
pTile->getContent().getRenderContent();
CESIUM_ASSERT(pRenderContent != nullptr);
GltfModifierVersionExtension::setVersion(
pRenderContent->getModel(),
version);
});
}
void TilesetContentManager::loadTileContent(
Tile& tile,
const TilesetOptions& tilesetOptions) {
CESIUM_TRACE("TilesetContentManager::loadTileContent");
if (this->_externals.pGltfModifier &&
this->_externals.pGltfModifier->needsWorkerThreadModification(tile)) {
TileRenderContent* pRenderContent = tile.getContent().getRenderContent();
CESIUM_ASSERT(pRenderContent != nullptr);
this->reapplyGltfModifier(tile, tilesetOptions, pRenderContent);
return;
}
if (tile.getState() == TileLoadState::Unloading) {
// We can't load a tile that is unloading; it has to finish unloading first.
return;
@ -1079,9 +1363,13 @@ void TilesetContentManager::loadTileContent(
CesiumUtility::IntrusivePointer<TilesetContentManager> thiz = this;
pLoader->loadTileContent(loadInput)
.thenImmediately([tileLoadInfo = std::move(tileLoadInfo),
.thenImmediately([asyncSystem = this->_externals.asyncSystem,
pAssetAccessor = this->_externals.pAssetAccessor,
pLogger = this->_externals.pLogger,
tileLoadInfo = std::move(tileLoadInfo),
projections = std::move(projections),
rendererOptions = tilesetOptions.rendererOptions](
rendererOptions = tilesetOptions.rendererOptions,
pGltfModifier = _externals.pGltfModifier](
TileLoadResult&& result) mutable {
// the reason we run immediate continuation, instead of in the
// worker thread, is that the loader may run the task in the main
@ -1092,17 +1380,18 @@ void TilesetContentManager::loadTileContent(
// worker thread if the content is a render content
if (result.state == TileLoadResultState::Success) {
if (std::holds_alternative<CesiumGltf::Model>(result.contentKind)) {
auto asyncSystem = tileLoadInfo.asyncSystem;
return asyncSystem.runInWorkerThread(
[result = std::move(result),
projections = std::move(projections),
tileLoadInfo = std::move(tileLoadInfo),
rendererOptions]() mutable {
rendererOptions,
pGltfModifier]() mutable {
return postProcessContentInWorkerThread(
std::move(result),
std::move(projections),
std::move(tileLoadInfo),
rendererOptions);
rendererOptions,
pGltfModifier);
});
}
}
@ -1114,6 +1403,19 @@ void TilesetContentManager::loadTileContent(
.thenInMainThread([pTile, thiz](TileLoadResultAndRenderResources&& pair) {
setTileContent(*pTile, std::move(pair.result), pair.pRenderResources);
thiz->notifyTileDoneLoading(pTile.get());
if (thiz->_externals.pGltfModifier) {
const TileRenderContent* pRenderContent =
pTile->getContent().getRenderContent();
CESIUM_ASSERT(!pRenderContent || !pRenderContent->getModifiedModel());
if (pRenderContent &&
GltfModifierVersionExtension::getVersion(
pRenderContent->getModel()) !=
thiz->_externals.pGltfModifier->getCurrentVersion()) {
thiz->_externals.pGltfModifier->onOldVersionContentLoadingComplete(
*pTile);
}
}
})
.catchInMainThread([pLogger = this->_externals.pLogger, pTile, thiz](
std::exception&& e) {
@ -1175,6 +1477,31 @@ UnloadTileContentResult TilesetContentManager::unloadTileContent(Tile& tile) {
}
TileLoadState state = tile.getState();
// Test if a glTF modifier is in progress.
if (this->_externals.pGltfModifier) {
TileRenderContent* pRenderContent = tile.getContent().getRenderContent();
if (pRenderContent) {
switch (pRenderContent->getGltfModifierState()) {
case GltfModifierState::WorkerRunning:
// Worker thread is running, we cannot unload yet.
return UnloadTileContentResult::Keep;
case GltfModifierState::WorkerDone:
// Free temporary render resources.
CESIUM_ASSERT(pRenderContent->getModifiedRenderResources());
if (this->_externals.pPrepareRendererResources) {
this->_externals.pPrepareRendererResources->free(
tile,
pRenderContent->getModifiedRenderResources(),
nullptr);
}
pRenderContent->resetModifiedModelAndRenderResources();
pRenderContent->setGltfModifierState(GltfModifierState::Idle);
break;
default:
break;
}
}
}
if (state == TileLoadState::Unloaded) {
return UnloadTileContentResult::Remove;
}
@ -1353,13 +1680,46 @@ int64_t TilesetContentManager::getTotalDataUsed() const noexcept {
void TilesetContentManager::finishLoading(
Tile& tile,
const TilesetOptions& tilesetOptions) {
CESIUM_ASSERT(tile.getState() == TileLoadState::ContentLoaded);
// Run the main thread part of loading.
TileContent& content = tile.getContent();
TileRenderContent* pRenderContent = content.getRenderContent();
CESIUM_ASSERT(pRenderContent != nullptr);
// If this tile doesn't have render content, there's nothing to finish
// loading.
TileRenderContent* pRenderContent = content.getRenderContent();
if (pRenderContent == nullptr)
return;
if (this->_externals.pGltfModifier &&
this->_externals.pGltfModifier->needsMainThreadModification(tile)) {
// Free outdated render resources before replacing them.
if (this->_externals.pPrepareRendererResources) {
this->_externals.pPrepareRendererResources->free(
tile,
nullptr,
pRenderContent->getRenderResources());
}
// Replace model and render resources with the newly modified versions,
// discarding the old ones
pRenderContent->replaceWithModifiedModel();
// Run the main thread part of loading.
if (this->_externals.pPrepareRendererResources) {
pRenderContent->setRenderResources(
this->_externals.pPrepareRendererResources->prepareInMainThread(
tile,
pRenderContent->getRenderResources()));
} else {
pRenderContent->setRenderResources(nullptr);
}
pRenderContent->setGltfModifierState(GltfModifierState::Idle);
return;
}
// None of the processing below makes sense until the Tile hits the
// ContentLoaded state.
if (tile.getState() != TileLoadState::ContentLoaded)
return;
// add copyright
CreditSystem* pCreditSystem = this->_externals.pCreditSystem.get();
@ -1372,6 +1732,7 @@ void TilesetContentManager::finishLoading(
for (const std::string_view& creditString : creditStrings) {
credits.emplace_back(pCreditSystem->createCredit(
this->_creditSource,
std::string(creditString),
tilesetOptions.showCreditsOnScreen));
}
@ -1379,6 +1740,7 @@ void TilesetContentManager::finishLoading(
pRenderContent->setCredits(credits);
}
// Run the main thread part of loading.
void* pWorkerRenderResources = pRenderContent->getRenderResources();
if (this->_externals.pPrepareRendererResources) {
void* pMainThreadRenderResources =
@ -1480,6 +1842,16 @@ void TilesetContentManager::clearChildrenRecursively(Tile* pTile) noexcept {
void TilesetContentManager::registerTileRequester(
TileLoadRequester& requester) {
if (requester._pTilesetContentManager.get() == this) {
return;
}
if (requester._pTilesetContentManager != nullptr) {
requester._pTilesetContentManager->unregisterTileRequester(requester);
}
requester._pTilesetContentManager = this;
CESIUM_ASSERT(
std::find(
this->_requesters.begin(),
@ -1496,6 +1868,8 @@ void TilesetContentManager::unregisterTileRequester(
if (it != this->_requesters.end()) {
this->_requesters.erase(it);
}
requester._pTilesetContentManager = nullptr;
}
namespace {
@ -1671,14 +2045,7 @@ void TilesetContentManager::processMainThreadLoadRequests(
if (pToLoad->_referenceCount == 0)
continue;
// We double-check that the tile is still in the ContentLoaded state here,
// in case something (such as a child that needs to upsample from this
// parent) already pushed the tile into the Done state. Because in that
// case, calling finishLoading here would assert or crash.
if (pToLoad->getState() == TileLoadState::ContentLoaded &&
pToLoad->isRenderContent()) {
this->finishLoading(*pToLoad, options);
}
this->finishLoading(*pToLoad, options);
auto time = std::chrono::system_clock::now();
if (timeBudget > 0.0 && time >= end) {
@ -1689,6 +2056,13 @@ void TilesetContentManager::processMainThreadLoadRequests(
void TilesetContentManager::markTilesetDestroyed() noexcept {
this->_tilesetDestroyed = true;
// Unregister the GltfModifier. This will not destroy it, but it will ensure
// it releases any references it holds to this TilesetContentManager so that
// the TilesetContentManager can be destroyed.
if (this->_externals.pGltfModifier) {
this->_externals.pGltfModifier->onUnregister(*this);
}
}
void TilesetContentManager::releaseReference() const {
@ -1706,6 +2080,17 @@ void TilesetContentManager::releaseReference() const {
}
}
TilesetExternals& TilesetContentManager::getExternals() {
return this->_externals;
}
const TilesetExternals& TilesetContentManager::getExternals() const {
return this->_externals;
}
const CreditSource& TilesetContentManager::getCreditSource() const noexcept {
return this->_creditSource;
}
void TilesetContentManager::setTileContent(
Tile& tile,
TileLoadResult&& result,
@ -1950,9 +2335,11 @@ void TilesetContentManager::propagateTilesetContentLoaderResult(
this->_tilesetCredits.reserve(
this->_tilesetCredits.size() + result.credits.size());
for (const auto& creditResult : result.credits) {
this->_tilesetCredits.emplace_back(_externals.pCreditSystem->createCredit(
creditResult.creditText,
creditResult.showOnScreen));
this->_tilesetCredits.emplace_back(
this->_externals.pCreditSystem->createCredit(
this->_creditSource,
creditResult.creditText,
creditResult.showOnScreen));
}
this->_requestHeaders = std::move(result.requestHeaders);
@ -1963,4 +2350,27 @@ void TilesetContentManager::propagateTilesetContentLoaderResult(
LoadedTileEnumerator(this->_pRootTile.get()));
this->_pLoader->setOwner(*this);
}
CesiumAsync::Future<void>
TilesetContentManager::registerGltfModifier(const Tile* pRootTile) {
std::shared_ptr<GltfModifier> pGltfModifier =
this->getExternals().pGltfModifier;
if (pGltfModifier && pRootTile) {
const TileExternalContent* pExternal =
pRootTile->getContent().getExternalContent();
return pGltfModifier
->onRegister(
*this,
pExternal ? pExternal->metadata : TilesetMetadata(),
*pRootTile)
.catchInMainThread([this](std::exception&&) {
// Disable the failed glTF modifier.
this->getExternals().pGltfModifier.reset();
});
}
return this->getExternals().asyncSystem.createResolvedFuture();
}
} // namespace Cesium3DTilesSelection

View File

@ -180,6 +180,11 @@ public:
void markTilesetDestroyed() noexcept;
void releaseReference() const;
TilesetExternals& getExternals();
const TilesetExternals& getExternals() const;
const CesiumUtility::CreditSource& getCreditSource() const noexcept;
private:
static void setTileContent(
Tile& tile,
@ -201,6 +206,11 @@ private:
void notifyTileUnloading(const Tile* pTile) noexcept;
void reapplyGltfModifier(
Tile& tile,
const TilesetOptions& tilesetOptions,
TileRenderContent* pRenderContent) noexcept;
template <class TilesetContentLoaderType>
void propagateTilesetContentLoaderResult(
TilesetLoadType type,
@ -208,6 +218,8 @@ private:
loadErrorCallback,
TilesetContentLoaderResult<TilesetContentLoaderType>&& result);
CesiumAsync::Future<void> registerGltfModifier(const Tile* pRootTile);
TilesetExternals _externals;
std::vector<CesiumAsync::IAssetAccessor::THeader> _requestHeaders;
std::unique_ptr<TilesetContentLoader> _pLoader;
@ -242,5 +254,7 @@ private:
// These are scratch space, stored here to avoid heap allocations.
std::vector<double> _requesterFractions;
std::vector<TileLoadRequester*> _requestersWithRequests;
CesiumUtility::CreditSource _creditSource;
};
} // namespace Cesium3DTilesSelection

View File

@ -43,7 +43,9 @@ ViewUpdateResult& TilesetViewGroup::getViewUpdateResult() {
return this->_updateResult;
}
void TilesetViewGroup::addToLoadQueue(const TileLoadTask& task) {
void TilesetViewGroup::addToLoadQueue(
const TileLoadTask& task,
const std::shared_ptr<GltfModifier>& pModifier) {
Tile* pTile = task.pTile;
CESIUM_ASSERT(pTile != nullptr);
@ -61,9 +63,9 @@ void TilesetViewGroup::addToLoadQueue(const TileLoadTask& task) {
[&](const TileLoadTask& task) { return task.pTile == pTile; }) ==
this->_mainThreadLoadQueue.end());
if (pTile->needsWorkerThreadLoading()) {
if (pTile->needsWorkerThreadLoading(pModifier.get())) {
this->_workerThreadLoadQueue.emplace_back(task);
} else if (pTile->needsMainThreadLoading()) {
} else if (pTile->needsMainThreadLoading(pModifier.get())) {
this->_mainThreadLoadQueue.emplace_back(task);
} else if (
pTile->getState() == TileLoadState::ContentLoading ||
@ -257,4 +259,9 @@ const Tile* TilesetViewGroup::getNextTileToLoadInMainThread() {
return pResult;
}
bool TilesetViewGroup::isCreditReferenced(
CesiumUtility::Credit credit) const noexcept {
return this->_previousFrameCredits.isCreditReferenced(credit);
}
} // namespace Cesium3DTilesSelection

View File

@ -3,6 +3,7 @@
#include <Cesium3DTilesSelection/Tile.h>
namespace Cesium3DTilesSelection {
void MockTilesetContentManagerTestFixture::setTileLoadState(
Cesium3DTilesSelection::Tile& tile,
Cesium3DTilesSelection::TileLoadState loadState) {
@ -14,4 +15,11 @@ void MockTilesetContentManagerTestFixture::setTileShouldContinueUpdating(
bool shouldContinueUpdating) {
tile.setMightHaveLatentChildren(shouldContinueUpdating);
}
void MockTilesetContentManagerTestFixture::setTileContent(
Cesium3DTilesSelection::Tile& tile,
Cesium3DTilesSelection::TileContent&& content) {
tile._content = std::move(content);
};
} // namespace Cesium3DTilesSelection

View File

@ -12,5 +12,10 @@ public:
static void setTileShouldContinueUpdating(
Cesium3DTilesSelection::Tile& tile,
bool shouldContinueToBeUpdating);
static void setTileContent(
Cesium3DTilesSelection::Tile& tile,
Cesium3DTilesSelection::TileContent&& content);
};
} // namespace Cesium3DTilesSelection

View File

@ -1,200 +0,0 @@
#include <CesiumUtility/CreditSystem.h>
#include <doctest/doctest.h>
#include <string>
#include <vector>
using namespace CesiumUtility;
TEST_CASE("Test basic credit handling") {
CreditSystem creditSystem;
std::string html0 = "<html>Credit0</html>";
std::string html1 = "<html>Credit1</html>";
std::string html2 = "<html>Credit2</html>";
Credit credit0 = creditSystem.createCredit(html0);
Credit credit1 = creditSystem.createCredit(html1);
Credit credit2 = creditSystem.createCredit(html2);
REQUIRE(creditSystem.getHtml(credit1) == html1);
// Frame 0: Add 0 and 1
{
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
const CreditsSnapshot& snapshot0 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow0{credit0, credit1};
REQUIRE(snapshot0.currentCredits == expectedShow0);
std::vector<Credit> expectedHide0{};
REQUIRE(snapshot0.removedCredits == expectedHide0);
}
// Start frame 1: Add 2, remove 0
{
creditSystem.addCreditReference(credit2);
creditSystem.removeCreditReference(credit0);
const CreditsSnapshot& snapshot1 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow1{credit1, credit2};
REQUIRE(snapshot1.currentCredits == expectedShow1);
std::vector<Credit> expectedHide1{credit0};
REQUIRE(snapshot1.removedCredits == expectedHide1);
}
// Start frame 2: Add nothing, remove 1 and 2
{
creditSystem.removeCreditReference(credit1);
creditSystem.removeCreditReference(credit2);
const CreditsSnapshot& snapshot2 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow2{};
REQUIRE(snapshot2.currentCredits == expectedShow2);
std::vector<Credit> expectedHide2{credit1, credit2};
REQUIRE(snapshot2.removedCredits == expectedHide2);
}
// Start frame 3: Add nothing, remove nothing
{
const CreditsSnapshot& snapshot3 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow3{};
REQUIRE(snapshot3.currentCredits == expectedShow3);
std::vector<Credit> expectedHide3{};
REQUIRE(snapshot3.removedCredits == expectedHide3);
}
// Start frame 4: Add 2, remove nothing
{
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot4 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow4{credit2};
REQUIRE(snapshot4.currentCredits == expectedShow4);
std::vector<Credit> expectedHide4{};
REQUIRE(snapshot4.removedCredits == expectedHide4);
}
// Start frame 5: Remove and then re-add 2
{
creditSystem.removeCreditReference(credit2);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot5 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow5{credit2};
REQUIRE(snapshot5.currentCredits == expectedShow5);
std::vector<Credit> expectedHide5{};
REQUIRE(snapshot5.removedCredits == expectedHide5);
}
// Start frame 6: Add and then remove 1
{
creditSystem.addCreditReference(credit1);
creditSystem.removeCreditReference(credit1);
const CreditsSnapshot& snapshot6 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow6{credit2};
REQUIRE(snapshot6.currentCredits == expectedShow6);
std::vector<Credit> expectedHide6{};
REQUIRE(snapshot6.removedCredits == expectedHide6);
}
}
TEST_CASE("Test wrong credit handling") {
CreditSystem creditSystemA;
CreditSystem creditSystemB;
std::string html0 = "<html>Credit0</html>";
std::string html1 = "<html>Credit1</html>";
Credit creditA0 = creditSystemA.createCredit(html0);
Credit creditA1 = creditSystemA.createCredit(html1);
/*Credit creditB0 = */ creditSystemB.createCredit(html0);
// NOTE: This is using a Credit from a different credit
// system, which coincidentally has a valid ID here.
// This is not (and can hardly be) checked right now,
// so this returns a valid HTML string:
REQUIRE(creditSystemB.getHtml(creditA0) == html0);
REQUIRE(creditSystemB.getHtml(creditA1) != html1);
}
TEST_CASE("Test sorting credits by frequency") {
CreditSystem creditSystem;
std::string html0 = "<html>Credit0</html>";
std::string html1 = "<html>Credit1</html>";
std::string html2 = "<html>Credit2</html>";
Credit credit0 = creditSystem.createCredit(html0);
Credit credit1 = creditSystem.createCredit(html1);
Credit credit2 = creditSystem.createCredit(html2);
REQUIRE(creditSystem.getHtml(credit1) == html1);
for (int i = 0; i < 3; i++) {
creditSystem.addCreditReference(credit0);
}
for (int i = 0; i < 2; i++) {
creditSystem.addCreditReference(credit1);
}
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot0 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow0{credit0, credit1, credit2};
REQUIRE(snapshot0.currentCredits == expectedShow0);
for (int i = 0; i < 2; i++) {
creditSystem.addCreditReference(credit2);
}
for (int i = 0; i < 2; i++) {
creditSystem.removeCreditReference(credit0);
}
const CreditsSnapshot& snapshot1 = creditSystem.getSnapshot();
std::vector<Credit> expectedShow1{credit2, credit1, credit0};
REQUIRE(snapshot1.currentCredits == expectedShow1);
}
TEST_CASE("Test setting showOnScreen on credits") {
CreditSystem creditSystem;
std::string html0 = "<html>Credit0</html>";
std::string html1 = "<html>Credit1</html>";
std::string html2 = "<html>Credit2</html>";
Credit credit0 = creditSystem.createCredit(html0, true);
Credit credit1 = creditSystem.createCredit(html1, false);
Credit credit2 = creditSystem.createCredit(html2, true);
REQUIRE(creditSystem.getHtml(credit1) == html1);
CHECK(creditSystem.shouldBeShownOnScreen(credit0) == true);
CHECK(creditSystem.shouldBeShownOnScreen(credit1) == false);
CHECK(creditSystem.shouldBeShownOnScreen(credit2) == true);
creditSystem.setShowOnScreen(credit0, false);
creditSystem.setShowOnScreen(credit1, true);
creditSystem.setShowOnScreen(credit2, true);
CHECK(creditSystem.shouldBeShownOnScreen(credit0) == false);
CHECK(creditSystem.shouldBeShownOnScreen(credit1) == true);
CHECK(creditSystem.shouldBeShownOnScreen(credit2) == true);
}

View File

@ -0,0 +1,192 @@
#include "MockTilesetContentManager.h"
#include "TilesetContentManager.h"
#include <Cesium3DTilesSelection/EllipsoidTilesetLoader.h>
#include <Cesium3DTilesSelection/GltfModifier.h>
#include <Cesium3DTilesSelection/GltfModifierVersionExtension.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileContent.h>
#include <Cesium3DTilesSelection/TileLoadRequester.h>
#include <CesiumGeometry/BoundingSphere.h>
#include <CesiumGltf/Model.h>
#include <CesiumNativeTests/SimpleAssetAccessor.h>
#include <CesiumNativeTests/SimpleTaskProcessor.h>
#include <CesiumUtility/CreditSystem.h>
#include <doctest/doctest.h>
#include <memory>
using namespace Cesium3DTilesSelection;
using namespace CesiumAsync;
using namespace CesiumNativeTests;
using namespace CesiumUtility;
namespace Cesium3DTilesSelection {
class MockTilesetContentManagerForGltfModifier {
public:
static TileLoadRequester* getTileLoadRequester(GltfModifier& modifier) {
return &modifier;
}
static const TileLoadRequester*
getTileLoadRequester(const GltfModifier& modifier) {
return &modifier;
}
static void
onOldVersionContentLoadingComplete(GltfModifier& modifier, const Tile& tile) {
modifier.onOldVersionContentLoadingComplete(tile);
}
static void
onUnregister(GltfModifier& modifier, TilesetContentManager& contentManager) {
return modifier.onUnregister(contentManager);
}
};
} // namespace Cesium3DTilesSelection
namespace {
class MockGltfModifier : public GltfModifier {
public:
CesiumAsync::Future<std::optional<GltfModifierOutput>>
apply(GltfModifierInput&& input) override {
++applyCallCount;
return input.asyncSystem
.createResolvedFuture<std::optional<GltfModifierOutput>>(std::nullopt);
}
int32_t applyCallCount = 0;
};
} // namespace
TEST_CASE("GltfModifier") {
TilesetExternals externals{
.pAssetAccessor = std::make_shared<SimpleAssetAccessor>(
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>()),
.pPrepareRendererResources = nullptr,
.asyncSystem = AsyncSystem(std::make_shared<SimpleTaskProcessor>()),
.pCreditSystem = std::make_shared<CreditSystem>(),
.pGltfModifier = std::make_shared<MockGltfModifier>()};
std::unique_ptr<Tileset> pTileset =
EllipsoidTilesetLoader::createTileset(externals, TilesetOptions{});
pTileset->getRootTileAvailableEvent().waitInMainThread();
Tile* pTile = const_cast<Tile*>(pTileset->getRootTile());
std::shared_ptr<GltfModifier> pModifier =
pTileset->getExternals().pGltfModifier;
MockTilesetContentManagerTestFixture::setTileLoadState(
*pTile,
TileLoadState::ContentLoaded);
TileContent content;
content.setContentKind(
std::make_unique<TileRenderContent>(CesiumGltf::Model{}));
MockTilesetContentManagerTestFixture::setTileContent(
*pTile,
std::move(content));
pTile->addReference();
SUBCASE("has empty load queues on construction") {
const TileLoadRequester* pRequester =
MockTilesetContentManagerForGltfModifier::getTileLoadRequester(
*pModifier);
CHECK_FALSE(pRequester->hasMoreTilesToLoadInWorkerThread());
CHECK_FALSE(pRequester->hasMoreTilesToLoadInMainThread());
}
SUBCASE("queues tiles for worker thread loading after trigger") {
TileLoadRequester* pRequester =
MockTilesetContentManagerForGltfModifier::getTileLoadRequester(
*pModifier);
// Initially inactive, no queuing should happen
MockTilesetContentManagerForGltfModifier::
onOldVersionContentLoadingComplete(*pModifier, *pTile);
CHECK_FALSE(pRequester->hasMoreTilesToLoadInWorkerThread());
// After trigger, tile should be queued
pModifier->trigger();
CHECK(pRequester->hasMoreTilesToLoadInWorkerThread());
// Get next tile should return our tile and empty the queue
const Tile* pNext = pRequester->getNextTileToLoadInWorkerThread();
CHECK(pNext == pTile);
CHECK_FALSE(pRequester->hasMoreTilesToLoadInWorkerThread());
SUBCASE("multiple tiles") {
REQUIRE(!pTile->getChildren().empty());
Tile* pTile2 = &pTile->getChildren()[0];
// Make the second tile loaded, too.
MockTilesetContentManagerTestFixture::setTileLoadState(
*pTile2,
TileLoadState::ContentLoaded);
TileContent content2;
content2.setContentKind(
std::make_unique<TileRenderContent>(CesiumGltf::Model{}));
MockTilesetContentManagerTestFixture::setTileContent(
*pTile2,
std::move(content2));
// Add a reference for the content, and for a child with a reference.
pTile2->addReference();
pTile->addReference();
pModifier->trigger();
CHECK(pRequester->hasMoreTilesToLoadInWorkerThread());
const Tile* pNext1 = pRequester->getNextTileToLoadInWorkerThread();
CHECK(pRequester->hasMoreTilesToLoadInWorkerThread());
const Tile* pNext2 = pRequester->getNextTileToLoadInWorkerThread();
CHECK_FALSE(pRequester->hasMoreTilesToLoadInWorkerThread());
bool bothTilesReturned = (pNext1 == pTile && pNext2 == pTile2) ||
(pNext1 == pTile2 && pNext2 == pTile);
CHECK(bothTilesReturned);
}
}
SUBCASE("clears load queues on unregister") {
const TileLoadRequester* pRequester =
MockTilesetContentManagerForGltfModifier::getTileLoadRequester(
*pModifier);
pModifier->trigger();
CHECK(pRequester->hasMoreTilesToLoadInWorkerThread());
pTileset.reset();
CHECK_FALSE(pRequester->hasMoreTilesToLoadInWorkerThread());
CHECK_FALSE(pRequester->hasMoreTilesToLoadInMainThread());
}
SUBCASE("trigger causes modifier to be reapplied and version number to be "
"updated") {
const TileRenderContent* pRenderContent =
pTile->getContent().getRenderContent();
REQUIRE(pRenderContent);
CHECK(
GltfModifierVersionExtension::getVersion(pRenderContent->getModel()) ==
std::nullopt);
pModifier->trigger();
REQUIRE(pModifier->getCurrentVersion() == 0);
while (GltfModifierVersionExtension::getVersion(
pRenderContent->getModel()) != 0) {
pTileset->loadTiles();
externals.asyncSystem.dispatchMainThreadTasks();
}
}
}

View File

@ -110,6 +110,8 @@ TEST_CASE("TileLoadRequester") {
auto pTileset = EllipsoidTilesetLoader::createTileset(externals);
externals.asyncSystem.dispatchMainThreadTasks();
const Tile* pRoot = pTileset->getRootTile();
REQUIRE(pRoot != nullptr);
REQUIRE(pRoot->getChildren().size() == 2);
@ -177,6 +179,8 @@ TEST_CASE("TileLoadRequester") {
std::move(pRootTile),
options);
externals.asyncSystem.dispatchMainThreadTasks();
const Tile* pRoot = pTileset->getRootTile();
REQUIRE(pRoot != nullptr);
REQUIRE(pRoot->getChildren().size() == 100);

View File

@ -4,6 +4,8 @@
#include "TilesetJsonLoader.h"
#include <Cesium3DTilesContent/registerAllTileContentTypes.h>
#include <Cesium3DTilesSelection/GltfModifier.h>
#include <Cesium3DTilesSelection/GltfModifierVersionExtension.h>
#include <Cesium3DTilesSelection/RasterOverlayCollection.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileLoadResult.h>
@ -38,6 +40,7 @@
#include <CesiumNativeTests/SimpleAssetResponse.h>
#include <CesiumNativeTests/SimpleTaskProcessor.h>
#include <CesiumNativeTests/readFile.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/DebugColorizeTilesRasterOverlay.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
@ -496,6 +499,8 @@ TEST_CASE("Test tile state machine") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
// test manager loading
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, options);
@ -603,6 +608,8 @@ TEST_CASE("Test tile state machine") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
// test manager loading
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, options);
@ -681,6 +688,8 @@ TEST_CASE("Test tile state machine") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
// test manager loading
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, options);
@ -781,6 +790,8 @@ TEST_CASE("Test tile state machine") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
Tile& tile = *pManager->getRootTile();
Tile& upsampledTile = tile.getChildren().back();
@ -938,6 +949,8 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
// test the gltf model
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, {});
@ -1008,6 +1021,8 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
// test the gltf model
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, options);
@ -1074,6 +1089,8 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, {});
pManager->waitUntilIdle();
@ -1122,7 +1139,8 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
// add raster overlay
pManager->getRasterOverlayCollection().add(
new DebugColorizeTilesRasterOverlay("DebugOverlay"));
asyncSystem.dispatchMainThreadTasks();
pManager->waitUntilIdle();
SUBCASE(
"Generate raster overlay details when tile doesn't have loose region") {
@ -1312,24 +1330,13 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
class AlwaysMoreDetailProvider : public RasterOverlayTileProvider {
public:
AlwaysMoreDetailProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<CesiumUtility::Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::Rectangle& coverageRectangle)
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
coverageRectangle) {}
@ -1358,29 +1365,18 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
AlwaysMoreDetailRasterOverlay() : RasterOverlay("AlwaysMoreDetail") {}
CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<
CesiumUtility::CreditSystem>& /* pCreditSystem */,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override {
return asyncSystem.createResolvedFuture(CreateTileProviderResult(
CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>(
new AlwaysMoreDetailProvider(
pOwner ? pOwner : this,
asyncSystem,
pAssetAccessor,
nullptr,
std::nullopt,
pPrepareRendererResources,
pLogger,
CesiumGeospatial::GeographicProjection(),
projectRectangleSimple(
return parameters.externals.asyncSystem.createResolvedFuture(
CreateTileProviderResult(
CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>(
new AlwaysMoreDetailProvider(
this,
parameters,
CesiumGeospatial::GeographicProjection(),
GlobeRectangle::MAXIMUM)))));
projectRectangleSimple(
CesiumGeospatial::GeographicProjection(),
GlobeRectangle::MAXIMUM)))));
}
};
@ -1417,7 +1413,8 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
pManager->getRasterOverlayCollection().add(
new AlwaysMoreDetailRasterOverlay());
asyncSystem.dispatchMainThreadTasks();
pManager->waitUntilIdle();
SUBCASE(
"Generate raster overlay details when tile doesn't have loose region") {
@ -1639,7 +1636,8 @@ TEST_CASE("Test the tileset content manager's post processing for gltf") {
// add raster overlay
pManager->getRasterOverlayCollection().add(
new DebugColorizeTilesRasterOverlay("DebugOverlay"));
asyncSystem.dispatchMainThreadTasks();
pManager->waitUntilIdle();
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, {});
@ -1827,9 +1825,158 @@ TEST_CASE("IPrepareRendererResources::prepareInLoadThread parameters") {
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, options);
pManager->waitUntilIdle();
pManager->unloadTileContent(tile);
}
}
TEST_CASE("Test glTF modifier state machine") {
Cesium3DTilesContent::registerAllTileContentTypes();
// create mock tileset externals
auto pMockedAssetAccessor = std::make_shared<SimpleAssetAccessor>(
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>{});
auto pMockedPrepareRendererResources =
std::make_shared<SimplePrepareRendererResource>();
CesiumAsync::AsyncSystem asyncSystem{std::make_shared<SimpleTaskProcessor>()};
auto pMockedCreditSystem = std::make_shared<CreditSystem>();
TilesetExternals externals{
pMockedAssetAccessor,
pMockedPrepareRendererResources,
asyncSystem,
pMockedCreditSystem};
class SimpleGltfModifier : public GltfModifier {
public:
SimpleGltfModifier() {}
int applyCallCount = 0;
CesiumAsync::Future<std::optional<GltfModifierOutput>>
apply(GltfModifierInput&& input) override {
++applyCallCount;
GltfModifierOutput output{.modifiedModel = input.previousModel};
return input.asyncSystem.createResolvedFuture(
std::make_optional(std::move(output)));
}
int onRegisterCallCount = 0;
CesiumAsync::Future<void> onRegister(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>&,
const std::shared_ptr<spdlog::logger>&,
const TilesetMetadata&,
const Tile&) override {
++onRegisterCallCount;
return asyncSystem.createResolvedFuture();
}
};
auto pGltfModifier = std::make_shared<SimpleGltfModifier>();
externals.pGltfModifier = pGltfModifier;
auto pMockedLoader = std::make_unique<SimpleTilesetContentLoader>();
pMockedLoader->mockLoadTileContent = {
CesiumGltf::Model(),
CesiumGeometry::Axis::Y,
std::nullopt,
std::nullopt,
std::nullopt,
nullptr,
nullptr,
{},
TileLoadResultState::Success};
// create tile
auto pRootTile = std::make_unique<Tile>(pMockedLoader.get());
// Give the tile an ID so it is eligible for unloading.
pRootTile->setTileID("foo");
// create manager
TilesetOptions options{};
options.contentOptions.generateMissingNormalsSmooth = true;
IntrusivePointer<TilesetContentManager> pManager = new TilesetContentManager{
externals,
options,
std::move(pMockedLoader),
std::move(pRootTile)};
pManager->waitUntilIdle();
CHECK(pGltfModifier->onRegisterCallCount == 1);
// test manager loading
Tile& tile = *pManager->getRootTile();
pManager->loadTileContent(tile, options);
pManager->waitUntilIdle();
pManager->updateTileContent(tile, options);
CHECK(tile.getState() == TileLoadState::Done);
CHECK(tile.getContent().isRenderContent());
// Constructed modifier is nilpotent until the first call to trigger(), so:
CHECK(pGltfModifier->applyCallCount == 0);
CHECK(pMockedPrepareRendererResources->totalAllocation == 1);
int expectedCallCount = 1;
auto const& applyModifier = [&]() {
pGltfModifier->trigger();
CHECK(tile.needsWorkerThreadLoading(pGltfModifier.get()));
// Start worker-thread phase of glTF modifier.
pManager->loadTileContent(tile, options);
// Unloading should be refused while worker-thread is running.
CHECK(pManager->unloadTileContent(tile) == UnloadTileContentResult::Keep);
// Wait completion of worker-thread phase.
pManager->waitUntilIdle();
CHECK(!tile.needsWorkerThreadLoading(pGltfModifier.get()));
CHECK(tile.needsMainThreadLoading(pGltfModifier.get()));
CHECK(pGltfModifier->applyCallCount == expectedCallCount);
// The temporary renderer resource should have been created.
CHECK(pMockedPrepareRendererResources->totalAllocation == 2);
SUBCASE("Perform main-thread phase of glTF modifier") {
pManager->finishLoading(tile, options);
CHECK(!tile.needsWorkerThreadLoading(pGltfModifier.get()));
CHECK(!tile.needsMainThreadLoading(pGltfModifier.get()));
// The temporary renderer resource should have been freed.
CHECK(pGltfModifier->applyCallCount == expectedCallCount);
CHECK(pMockedPrepareRendererResources->totalAllocation == 1);
}
};
// Increment modifier version, thus requiring a first glTF modifier of the
// already loaded tile
applyModifier();
// Unload tile so that we can now test loading the tile with an _active_
// modifier
CHECK(pManager->unloadTileContent(tile));
CHECK(pMockedPrepareRendererResources->totalAllocation == 0);
++expectedCallCount;
// loaded tile below will be already processed by the glTF modifier
pManager->loadTileContent(tile, options);
pManager->waitUntilIdle();
pManager->updateTileContent(tile, options);
CHECK(tile.getState() == TileLoadState::Done);
CHECK(tile.getContent().isRenderContent());
// After the tile is loaded, glTF modifier should not be needed,
// as it has already been done as part of the loading.
CHECK(!tile.needsWorkerThreadLoading(pGltfModifier.get()));
CHECK(!tile.needsMainThreadLoading(pGltfModifier.get()));
CHECK(pGltfModifier->applyCallCount == expectedCallCount);
CHECK(pMockedPrepareRendererResources->totalAllocation == 1);
++expectedCallCount;
// Increment modifier version, thus requiring a new glTF modifier.
applyModifier();
SUBCASE("Unload tile after main-thread phase of glTF modifier") {
CHECK(pManager->unloadTileContent(tile));
CHECK(pMockedPrepareRendererResources->totalAllocation == 0);
}
}

View File

@ -40,7 +40,7 @@ cesium_target_include_directories(
target_link_libraries(CesiumAsync
PUBLIC
CesiumUtility
spdlog::spdlog spdlog::spdlog_header_only
spdlog::spdlog
Async++
PRIVATE
unofficial::sqlite3::sqlite3

View File

@ -44,6 +44,12 @@ target_link_libraries(CesiumClientCommon
PRIVATE
picosha2::picosha2
modp_b64::modp_b64
httplib::httplib
OpenSSL::Crypto
)
if (NOT CESIUM_TARGET_WASM)
target_link_libraries(CesiumClientCommon
PRIVATE
httplib::httplib
)
endif()

View File

@ -13,7 +13,6 @@
#include <CesiumUtility/joinToString.h>
#include <fmt/format.h>
#include <httplib.h>
#include <modp_b64.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
@ -34,6 +33,13 @@
#include <utility>
#include <vector>
// Using httplib for the internal HTTP server to receive the OAuth2 redirect URI
// is certainly not going to work on the web. And cpp-httplib only supports
// 64-bit platforms, so we can't even build it for 32-bit emscripten targets.
#ifndef __EMSCRIPTEN__
#include <httplib.h>
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
@ -59,6 +65,7 @@ using namespace CesiumUtility;
namespace CesiumClientCommon {
namespace {
#ifndef __EMSCRIPTEN__
std::string encodeBase64(const std::vector<uint8_t>& bytes) {
const size_t count = modp_b64_encode_len(bytes.size());
std::string result(count, 0);
@ -72,7 +79,7 @@ std::string encodeBase64(const std::vector<uint8_t>& bytes) {
// in [RFC7636 Appendix A](https://tools.ietf.org/html/rfc7636#appendix-A)
const size_t firstPaddingIndex = result.find('=');
if (firstPaddingIndex != std::string::npos) {
result.erase(result.begin() + int64_t(firstPaddingIndex), result.end());
result.erase(result.begin() + ptrdiff_t(firstPaddingIndex), result.end());
}
std::replace(result.begin(), result.end(), '+', '-');
std::replace(result.begin(), result.end(), '/', '_');
@ -170,17 +177,28 @@ std::string createAuthorizationErrorHtml(
exception.what(),
applicationName);
}
#endif // #ifndef __EMSCRIPTEN__
} // namespace
CesiumAsync::Future<Result<OAuth2TokenResponse>> OAuth2PKCE::authorize(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& friendlyApplicationName,
const OAuth2ClientOptions& clientOptions,
const std::vector<std::string>& scopes,
std::function<void(const std::string&)>&& openUrlCallback,
const std::string& tokenEndpointUrl,
const std::string& authorizeBaseUrl) {
[[maybe_unused]] const std::shared_ptr<CesiumAsync::IAssetAccessor>&
pAssetAccessor,
[[maybe_unused]] const std::string& friendlyApplicationName,
[[maybe_unused]] const OAuth2ClientOptions& clientOptions,
[[maybe_unused]] const std::vector<std::string>& scopes,
[[maybe_unused]] std::function<void(const std::string&)>&& openUrlCallback,
[[maybe_unused]] const std::string& tokenEndpointUrl,
[[maybe_unused]] const std::string& authorizeBaseUrl) {
#ifdef __EMSCRIPTEN__
// Currently we just fail the authorization attempt in Emscripten / web
// builds. In theory, we can do a more web-oriented authorization flow here
// instead.
return asyncSystem.createResolvedFuture<Result<OAuth2TokenResponse>>(
Result<OAuth2TokenResponse>(
ErrorList::error("OAuth2 PKCE authorization is not supported in "
"Emscripten / WebAssembly builds.")));
#else // #ifdef __EMSCRIPTEN__
auto promise = asyncSystem.createPromise<Result<OAuth2TokenResponse>>();
std::shared_ptr<httplib::Server> pServer =
@ -332,6 +350,7 @@ CesiumAsync::Future<Result<OAuth2TokenResponse>> OAuth2PKCE::authorize(
openUrlCallback(authorizeUrl);
return promise.getFuture();
#endif // #ifdef __EMSCRIPTEN__ #else
}
CesiumAsync::Future<Result<OAuth2TokenResponse>>

View File

@ -5,6 +5,8 @@
#include <CesiumGeospatial/Library.h>
#include <CesiumUtility/Math.h>
#include <glm/ext/vector_double2.hpp>
#include <optional>
namespace CesiumGeospatial {
@ -259,6 +261,18 @@ public:
*/
GlobeRectangle computeUnion(const GlobeRectangle& other) const noexcept;
/**
* @brief Computes the normalized position of the given cartographic
* coordinate on this globe rectangle, with (0.0, 0.0) representing southwest
* and (1.0, 1.0) representing northeast. The returned coordinates vary
* linearly with longitude and latitude within the rectangle.
*
* @param cartographic The cartographic coordinate to transform.
* @return The normalized coordinates.
*/
glm::dvec2
computeNormalizedCoordinates(const Cartographic& cartographic) const noexcept;
/**
* @brief Splits this rectangle at the anti-meridian (180 degrees longitude),
* if necessary.

View File

@ -3,6 +3,7 @@
#include <CesiumUtility/Math.h>
#include <glm/common.hpp>
#include <glm/ext/vector_double2.hpp>
#include <optional>
#include <utility>
@ -142,6 +143,22 @@ GlobeRectangle::computeUnion(const GlobeRectangle& other) const noexcept {
glm::max(this->_north, other._north));
}
glm::dvec2 GlobeRectangle::computeNormalizedCoordinates(
const Cartographic& cartographic) const noexcept {
double east = this->_east;
double cartoLong = cartographic.longitude;
if (east < this->_west) {
east += CesiumUtility::Math::TwoPi;
if (cartoLong < this->_west) {
cartoLong += CesiumUtility::Math::TwoPi;
}
}
return glm::dvec2(
(cartoLong - this->_west) / (east - this->_west),
(cartographic.latitude - this->_south) / (this->_north - this->_south));
}
std::pair<GlobeRectangle, std::optional<GlobeRectangle>>
GlobeRectangle::splitAtAntiMeridian() const noexcept {
if (this->_west <= this->_east) {

View File

@ -199,3 +199,60 @@ TEST_CASE("GlobeRectangle::contains") {
CHECK(wrapping.contains(Cartographic(-3.14, 0.2)));
CHECK(!wrapping.contains(Cartographic(0.0, 0.2)));
}
TEST_CASE("GlobeRectangle::computeNormalizedCoordinates") {
GlobeRectangle simple(0.0, 0.0, 1.0, 1.0);
CHECK(
simple.computeNormalizedCoordinates(Cartographic(0.1, 0.1)) ==
glm::dvec2(0.1, 0.1));
CHECK(
simple.computeNormalizedCoordinates(Cartographic(0, 0.0)) ==
glm::dvec2(0.0, 0.0));
CHECK(
simple.computeNormalizedCoordinates(Cartographic(1.0, 1.0)) ==
glm::dvec2(1.0, 1.0));
GlobeRectangle wrapping =
GlobeRectangle::fromDegrees(175.0, 0.0, -175.0, 10.0);
CHECK(
wrapping.computeNormalizedCoordinates(
Cartographic::fromDegrees(175.0, 5.0)) == glm::dvec2(0.0, 0.5));
CHECK(
wrapping.computeNormalizedCoordinates(
Cartographic::fromDegrees(180.0, 10.0)) == glm::dvec2(0.5, 1.0));
CHECK(
wrapping.computeNormalizedCoordinates(
Cartographic::fromDegrees(-180.0, 0.0)) == glm::dvec2(0.5, 0.0));
CHECK(Math::equalsEpsilon(
wrapping.computeNormalizedCoordinates(
Cartographic::fromDegrees(-177.5, 0.0)),
glm::dvec2(0.75, 0.0),
Math::Epsilon6));
CHECK(
wrapping.computeNormalizedCoordinates(
Cartographic::fromDegrees(-175, 0.0)) == glm::dvec2(1.0, 0.0));
GlobeRectangle tile = GlobeRectangle::fromDegrees(0.5, 0, 1.0, 0.5);
CHECK(Math::equalsEpsilon(
tile.computeNormalizedCoordinates(Cartographic::fromDegrees(0.25, 0.25)),
glm::dvec2(-0.5, 0.5),
Math::Epsilon6));
CHECK(Math::equalsEpsilon(
tile.computeNormalizedCoordinates(Cartographic::fromDegrees(0.5, 0.75)),
glm::dvec2(0, 1.5),
Math::Epsilon6));
CHECK(Math::equalsEpsilon(
tile.computeNormalizedCoordinates(Cartographic::fromDegrees(0.75, 0.25)),
glm::dvec2(0.5, 0.5),
Math::Epsilon6));
GlobeRectangle bigWrapping =
GlobeRectangle::fromDegrees(179.0, -10.0, 10.0, 10.0);
CHECK(Math::equalsEpsilon(
bigWrapping.computeNormalizedCoordinates(
Cartographic::fromDegrees(5.0, 0.0)),
// Rectangle width is 191 degrees, position is 5 degrees west of east
// edge.
glm::dvec2(186.0 / 191.0, 0.5),
Math::Epsilon6));
}

View File

@ -2823,7 +2823,7 @@ private:
}
uint64_t totalLength = stringOffsets.back();
result.data.resize(totalLength);
result.data.resize(size_t(totalLength));
for (size_t i = 0; i < strings.size(); ++i) {
std::memcpy(
result.data.data() + stringOffsets[i],

View File

@ -44,6 +44,7 @@
#include <glm/ext/quaternion_double.hpp>
#include <glm/ext/vector_float3.hpp>
#include <glm/geometric.hpp>
#include <glm/gtc/quaternion.hpp> // NOLINT(misc-include-cleaner)
#include <glm/gtx/norm.hpp>
#include <algorithm>
@ -408,7 +409,7 @@ ErrorList Model::merge(Model&& rhs) {
std::copy(
pRhsDefaultScene->nodes.begin(),
pRhsDefaultScene->nodes.end(),
newScene.nodes.begin() + int64_t(originalNodeCount));
newScene.nodes.begin() + ptrdiff_t(originalNodeCount));
// No need to update indices because they've already been updated when
// we copied them from rhs to this.

View File

@ -28,7 +28,7 @@ template <typename T>
PropertyViewStatusType checkOffsetsBuffer(
const std::span<const std::byte>& offsetBuffer,
size_t valueBufferSize,
size_t instanceCount,
uint64_t instanceCount,
bool checkBitSize,
PropertyViewStatusType offsetsNotSortedError,
PropertyViewStatusType offsetOutOfBoundsError) noexcept {

View File

@ -50,6 +50,7 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
@ -1158,8 +1159,8 @@ void deleteBufferRange(
// Actually remove the bytes from the buffer.
pBuffer->byteLength -= bytesToRemove;
pBuffer->cesium.data.erase(
pBuffer->cesium.data.begin() + start,
pBuffer->cesium.data.begin() + end);
pBuffer->cesium.data.begin() + ptrdiff_t(start),
pBuffer->cesium.data.begin() + ptrdiff_t(end));
}
} // namespace

View File

@ -62,6 +62,20 @@ target_link_libraries(CesiumGltfReader
meshoptimizer::meshoptimizer
modp_b64::modp_b64
KTX::ktx
WebP::webp WebP::webpdecoder
$<IF:$<TARGET_EXISTS:libjpeg-turbo::turbojpeg>,libjpeg-turbo::turbojpeg,libjpeg-turbo::turbojpeg-static>
WebP::webp
WebP::webpdecoder
)
if(CESIUM_DISABLE_LIBJPEG_TURBO)
target_compile_definitions(
CesiumGltfReader
PRIVATE
CESIUM_DISABLE_LIBJPEG_TURBO
)
else()
target_link_libraries(
CesiumGltfReader
PRIVATE
$<IF:$<TARGET_EXISTS:libjpeg-turbo::turbojpeg>,libjpeg-turbo::turbojpeg,libjpeg-turbo::turbojpeg-static>
)
endif()

View File

@ -245,7 +245,7 @@ GltfReaderResult readBinaryGltf(
buffer.cesium.data = std::vector<std::byte>(
binaryChunk.begin(),
binaryChunk.begin() + buffer.byteLength);
binaryChunk.begin() + (ptrdiff_t)buffer.byteLength);
}
return result;

View File

@ -5,7 +5,6 @@
#include <CesiumUtility/Tracing.h>
#include <ktx.h>
#include <turbojpeg.h>
#include <webp/decode.h>
#include <algorithm>
@ -28,6 +27,10 @@
#define STB_IMAGE_RESIZE_STATIC
#include <stb_image_resize2.h>
#ifndef CESIUM_DISABLE_LIBJPEG_TURBO
#include <turbojpeg.h>
#endif
namespace CesiumGltfReader {
using namespace CesiumGltf;
@ -287,6 +290,7 @@ ImageReaderResult ImageDecoder::readImage(
}
{
#ifndef CESIUM_DISABLE_LIBJPEG_TURBO
tjhandle tjInstance = tjInitDecompress();
int inSubsamp, inColorspace;
if (!tjDecompressHeader3(
@ -316,7 +320,9 @@ ImageReaderResult ImageDecoder::readImage(
result.errors.emplace_back("Unable to decode JPEG");
result.pImage = nullptr;
}
} else {
} else
#endif // !CESIUM_DISABLE_LIBJPEG_TURBO
{
CESIUM_TRACE("Decode PNG");
image.bytesPerChannel = 1;
image.channels = 4;
@ -349,7 +355,9 @@ ImageReaderResult ImageDecoder::readImage(
result.errors.emplace_back(stbi_failure_reason());
}
}
#ifndef CESIUM_DISABLE_LIBJPEG_TURBO
tjDestroy(tjInstance);
#endif
}
return result;
}

View File

@ -76,7 +76,7 @@ std::unique_ptr<draco::Mesh> decodeBufferViewToDracoMesh(
const std::span<const std::byte> data(
buffer.cesium.data.data() + bufferView.byteOffset,
static_cast<uint64_t>(bufferView.byteLength));
static_cast<size_t>(bufferView.byteLength));
draco::DecoderBuffer decodeBuffer;
decodeBuffer.Init(reinterpret_cast<const char*>(data.data()), data.size());

View File

@ -613,6 +613,7 @@ TEST_CASE("Writes glb with binaryChunkByteAlignment of 8") {
REQUIRE(glbBytesExtraPadding.size() == 88);
}
#ifndef __EMSCRIPTEN__
TEST_CASE("Reports an error if asked to write a GLB larger than 4GB") {
CesiumGltf::Model model;
model.asset.version = "2.0";
@ -628,3 +629,4 @@ TEST_CASE("Reports an error if asked to write a GLB larger than 4GB") {
REQUIRE(!result.errors.empty());
CHECK(result.gltfBytes.empty());
}
#endif // __EMSCRIPTEN__

View File

@ -23,7 +23,7 @@ using namespace CesiumNativeTests;
using namespace CesiumUtility;
namespace {
const std::string& REDIRECT_PATH = "/dummy/auth/path";
const std::string REDIRECT_PATH = "/dummy/auth/path";
const int REDIRECT_PORT = 49013;
std::shared_ptr<Connection>
@ -169,4 +169,4 @@ TEST_CASE("CesiumITwinClient::Connection::geospatialFeatureCollections") {
collection.storageCrs ==
"https://www.opengis.net/def/crs/EPSG/0/32615");
}
}
}

View File

@ -38,6 +38,14 @@ cesium_glob_files(test_headers
)
set(test_include_directories ${CMAKE_CURRENT_LIST_DIR}/include)
if(CESIUM_TARGET_WASM)
target_link_options(
cesium-native-tests
PRIVATE
"-sEXIT_RUNTIME=1"
)
endif()
# Iterate through all targets, extracting their private sources and test sources / test headers
foreach(target ${cesium_native_targets})
get_target_property(target_test_sources ${target} TEST_SOURCES)
@ -75,6 +83,41 @@ foreach(target ${cesium_native_targets})
endif()
endforeach()
if(CESIUM_TARGET_WASM)
file(GLOB directories_list LIST_DIRECTORIES true "${CMAKE_SOURCE_DIR}/Cesium*")
set(FILE_PACKAGER_ARGS "")
set(TEST_DATA_FILES "")
foreach(dir ${directories_list})
if(IS_DIRECTORY ${dir}/test/data)
list(APPEND FILE_PACKAGER_ARGS "--embed" "${dir}/test/data")
file(GLOB_RECURSE data_files "${dir}/test/data/*")
list(APPEND TEST_DATA_FILES ${data_files})
endif()
endforeach()
list(APPEND FILE_PACKAGER_ARGS "--embed" "${CMAKE_SOURCE_DIR}/data")
if(CESIUM_WASM64)
list(APPEND FILE_PACKAGER_ARGS "--wasm64")
endif()
set(CESIUM_NATIVE_TESTS_DATA_OBJ ${CMAKE_CURRENT_BINARY_DIR}/cesium-native-tests-data.o)
add_custom_command(
COMMENT "Generating test data object file"
OUTPUT ${CESIUM_NATIVE_TESTS_DATA_OBJ}
COMMAND ${EMSCRIPTEN_ROOT_PATH}/tools/file_packager none.data ${FILE_PACKAGER_ARGS} --obj-output=${CESIUM_NATIVE_TESTS_DATA_OBJ}
VERBATIM
DEPENDS ${TEST_DATA_FILES}
)
target_sources(
cesium-native-tests
PRIVATE
${CESIUM_NATIVE_TESTS_DATA_OBJ}
)
endif()
target_sources(
cesium-native-tests
PRIVATE
@ -99,9 +142,12 @@ PRIVATE
target_compile_definitions(cesium-native-tests
PRIVATE
CESIUM_NATIVE_DATA_DIR=\"${CMAKE_CURRENT_LIST_DIR}/../data\"
CESIUM_NATIVE_DATA_DIR=\"${CMAKE_SOURCE_DIR}/data\"
)
include(CTest)
include(doctest)
doctest_discover_tests(cesium-native-tests)
if(NOT CESIUM_TARGET_WASM)
# doctest_discover_tests can't handle the target being an html file, so we just avoid it on a wasm build
doctest_discover_tests(cesium-native-tests)
endif()

View File

@ -43,10 +43,10 @@ std::unique_ptr<SimpleAssetResponse> readFileUri(const std::string& uri) {
if (!file) {
return response(404);
}
std::streamsize size = file.tellg();
std::streamoff size = file.tellg();
file.seekg(0, std::ios::beg);
result.resize(static_cast<size_t>(size));
file.read(reinterpret_cast<char*>(result.data()), size);
result.resize(size_t(size));
file.read(reinterpret_cast<char*>(result.data()), std::streamsize(size));
if (!file) {
return response(503);
} else {

View File

@ -12,11 +12,11 @@ std::vector<std::byte> readFile(const std::filesystem::path& fileName) {
std::ifstream file(fileName, std::ios::binary | std::ios::ate);
REQUIRE(file);
std::streamsize size = file.tellg();
std::streamoff size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<std::byte> buffer(static_cast<size_t>(size));
file.read(reinterpret_cast<char*>(buffer.data()), size);
std::vector<std::byte> buffer{size_t(size)};
file.read(reinterpret_cast<char*>(buffer.data()), std::streamsize(size));
return buffer;
}

View File

@ -506,7 +506,7 @@ void addSkirts(
westEdgeIndices.begin(),
westEdgeIndices.end(),
sortEdgeIndices.begin(),
sortEdgeIndices.begin() + westVertexCount,
sortEdgeIndices.begin() + ptrdiff_t(westVertexCount),
[&uvsAndHeights](auto lhs, auto rhs) noexcept {
return uvsAndHeights[lhs].y < uvsAndHeights[rhs].y;
});
@ -539,7 +539,7 @@ void addSkirts(
southEdgeIndices.begin(),
southEdgeIndices.end(),
sortEdgeIndices.begin(),
sortEdgeIndices.begin() + southVertexCount,
sortEdgeIndices.begin() + ptrdiff_t(southVertexCount),
[&uvsAndHeights](auto lhs, auto rhs) noexcept {
return uvsAndHeights[lhs].x > uvsAndHeights[rhs].x;
});
@ -572,7 +572,7 @@ void addSkirts(
eastEdgeIndices.begin(),
eastEdgeIndices.end(),
sortEdgeIndices.begin(),
sortEdgeIndices.begin() + eastVertexCount,
sortEdgeIndices.begin() + ptrdiff_t(eastVertexCount),
[&uvsAndHeights](auto lhs, auto rhs) noexcept {
return uvsAndHeights[lhs].y > uvsAndHeights[rhs].y;
});
@ -605,7 +605,7 @@ void addSkirts(
northEdgeIndices.begin(),
northEdgeIndices.end(),
sortEdgeIndices.begin(),
sortEdgeIndices.begin() + northVertexCount,
sortEdgeIndices.begin() + ptrdiff_t(northVertexCount),
[&uvsAndHeights](auto lhs, auto rhs) noexcept {
return uvsAndHeights[lhs].x < uvsAndHeights[rhs].x;
});
@ -754,11 +754,10 @@ QuantizedMeshMetadataResult processMetadata(
// decode position without skirt, but preallocate position buffer to include
// skirt as well
std::vector<std::byte> outputPositionsBuffer(
static_cast<uint64_t>((vertexCount + skirtVertexCount) * 3) *
sizeof(float));
size_t((vertexCount + skirtVertexCount) * 3) * sizeof(float));
std::span<float> outputPositions(
reinterpret_cast<float*>(outputPositionsBuffer.data()),
static_cast<size_t>((vertexCount + skirtVertexCount) * 3));
size_t((vertexCount + skirtVertexCount) * 3));
size_t positionOutputIndex = 0;
const glm::dvec3 center(

View File

@ -51,6 +51,7 @@ target_link_libraries(CesiumRasterOverlays
CesiumUtility
CesiumVectorData
nonstd::expected-lite
spdlog::spdlog
PRIVATE
tinyxml2::tinyxml2
)

View File

@ -0,0 +1,219 @@
#pragma once
#include <CesiumAsync/IAssetRequest.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <functional>
#include <memory>
namespace CesiumRasterOverlays {
/**
* @brief Supported values for @ref AzureMapsSessionParameters::tilesetId. See
* the [official
* documentation](https://learn.microsoft.com/en-us/rest/api/maps/render/get-map-tile?tabs=HTTP#tilesetid)
* for all standard values. Note that some tileset IDs return vector data, which
* is currently unsupported.
*/
struct AzureMapsTilesetId {
/**
* @brief All layers with Azure Maps' main style.
*/
inline static const std::string baseRoad = "microsoft.base.road";
/**
* @brief All layers with Azure Maps' dark grey style.
*/
inline static const std::string baseDarkGrey = "microsoft.base.darkgrey";
/**
* @brief Label data in Azure Maps' main style.
*/
inline static const std::string baseLabelsRoad = "microsoft.base.labels.road";
/**
* @brief Label data in Azure Maps' dark grey style.
*/
inline static const std::string baseLabelsDarkGrey =
"microsoft.base.labels.darkgrey";
/**
* @brief Road, boundary and label data in Azure Maps' main style.
*/
inline static const std::string baseHybridRoad = "microsoft.base.hybrid.road";
/**
* @brief Road, boundary and label data in Azure Maps' dark grey style.
*/
inline static const std::string baseHybridDarkGrey =
"microsoft.base.hybrid.darkgrey";
/**
* @brief A combination of satellite or aerial imagery. Only available in S1
* and G2 pricing SKU.
*/
inline static const std::string imagery = "microsoft.imagery";
/**
* @brief Shaded relief and terra layers.
*/
inline static const std::string terra = "microsoft.terra.main";
/**
* @brief Weather radar tiles. Latest weather radar images including areas of
* rain, snow, ice and mixed conditions. For more information on the Azure
* Maps Weather service coverage, see [Azure Maps weather services
* coverage](https://learn.microsoft.com/en-us/azure/azure-maps/weather-coverage).
* For more information on Radar data, see [Weather services in Azure
* Maps](https://learn.microsoft.com/en-us/azure/azure-maps/weather-services-concepts#radar-images).
*/
inline static const std::string weatherRadar = "microsoft.weather.radar.main";
/**
* @brief Weather infrared tiles. Latest Infrared Satellite images show
* clouds by their temperature. For more information on the Azure Maps Weather
* service coverage, see [Azure Maps weather services
* coverage](https://learn.microsoft.com/en-us/azure/azure-maps/weather-coverage).
* For more information on Radar data, see [Weather services in Azure
* Maps](https://learn.microsoft.com/en-us/azure/azure-maps/weather-services-concepts#radar-images).
*/
inline static const std::string weatherInfrared =
"microsoft.weather.infrared.main";
/**
* @brief Absolute traffic tiles in Azure Maps' main style.
*/
inline static const std::string trafficAbsolute =
"microsoft.traffic.absolute.main";
/**
* @brief Relative traffic tiles in Azure Maps' main style.
*/
inline static const std::string trafficRelativeMain =
"microsoft.traffic.relative.main";
/**
* @brief Relative traffic tiles in Azure Maps' dark style.
*/
inline static const std::string trafficRelativeDark =
"microsoft.traffic.relative.dark";
/**
* @brief Delay traffic tiles in Azure Maps' main style.
*/
inline static const std::string trafficDelay = "microsoft.traffic.delay.main";
/**
* @brief Reduced traffic tiles in Azure Maps' main style.
*/
inline static const std::string trafficReduced =
"microsoft.traffic.reduced.main";
};
/**
* @brief Session parameters for an Azure Maps overlay.
*/
struct AzureMapsSessionParameters {
/**
* @brief The Azure Maps subscription key to use.
*/
std::string key;
/**
* @brief The version number of Azure Maps API.
*/
std::string apiVersion{"2024-04-01"};
/**
* @brief A tileset is a collection of raster or vector data broken up into a
* uniform grid of square tiles at preset zoom levels. Every tileset has a
* tilesetId to use when making requests. The tilesetId for tilesets created
* using [Azure Maps Creator](https://aka.ms/amcreator) are generated through
* the [Tileset Create
* API](https://learn.microsoft.com/en-us/rest/api/maps-creator/tileset).
*
* The supported ready-to-use tilesets supplied by Azure Maps are listed
* [here](https://learn.microsoft.com/en-us/rest/api/maps/render/get-map-tile?view=rest-maps-2025-01-01&tabs=HTTP#tilesetid).
*/
std::string tilesetId{AzureMapsTilesetId::baseRoad};
/**
* @brief The language in which search results should be returned. Should be
* one of the supported [IETF language
* tags](https://en.wikipedia.org/wiki/IETF_language_tag), case insensitive.
* When data in the specified language is not available for a specific field,
* default language is used.
*
* Refer to [Supported
* Languages](https://learn.microsoft.com/en-us/azure/azure-maps/supported-languages)
* for details.
*/
std::string language{"en-US"};
/**
* @brief The view (also called the "user region") of a certain country/region
* for which to show the correct maps. Quoting the Azure Maps' documentation:
*
* "Different countries/regions have different views of such regions, and the
* View parameter allows your application to comply with the view required by
* the country/region your application will be serving. By default, the View
* parameter is set to "Unified" even if you haven't defined it in the
* request. It is your responsibility to determine the location of your users,
* and then set the View parameter correctly for that location. Alternatively,
* you have the option to set 'View=Auto', which will return the map data
* based on the IP address of the request. The View parameter in Azure Maps
* must be used in compliance with applicable laws, including those regarding
* mapping, of the country/region where maps, images and other data and third
* party content that you are authorized to access via Azure Maps is made
* available. Example: view=IN."
*
* Refer to [Supported Views](https://aka.ms/AzureMapsLocalizationViews) for
* details and to see the available Views.
*/
std::string view{"US"};
/**
* @brief Whether or not the @ref AzureMapsRasterOverlay should show the
* Azure Maps logo.
*
* Azure requires the logo to be shown, so setting this to false is only
* valid when something else is already showing the logo.
*/
bool showLogo = true;
/**
* @brief The base URL for the Azure Maps API.
*/
std::string apiBaseUrl{"https://atlas.microsoft.com/"};
};
/**
* @brief A @ref RasterOverlay that retrieves imagery from the Azure Maps API.
*/
class CESIUMRASTEROVERLAYS_API AzureMapsRasterOverlay final
: public RasterOverlay {
public:
/**
* @brief Creates a new instance.
*
* @param name The user-given name of this overlay layer.
* @param sessionParameters The session parameters for this overlay.
* @param overlayOptions The @ref RasterOverlayOptions for this instance.
*/
AzureMapsRasterOverlay(
const std::string& name,
const AzureMapsSessionParameters& sessionParameters,
const RasterOverlayOptions& overlayOptions = {});
virtual ~AzureMapsRasterOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:
AzureMapsSessionParameters _sessionParameters;
};
} // namespace CesiumRasterOverlays

View File

@ -1,7 +1,6 @@
#pragma once
#include <CesiumAsync/IAssetRequest.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
@ -107,32 +106,10 @@ public:
virtual ~BingMapsRasterOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
/**
* @brief Refresh a previously-created tile provider using a new key.
*
* Calling this method on a tile provider that was not created by this \ref
* BingMapsRasterOverlay will lead to undefined behavior.
*
* @param pProvider The previously-created tile provider.
* @param newKey The new key to use.
*/
CesiumAsync::Future<void> refreshTileProviderWithNewKey(
const CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>&
pProvider,
const std::string& newKey);
private:
static const std::string BING_LOGO_HTML;
std::string _url;
std::string _key;
std::string _mapStyle;

View File

@ -0,0 +1,42 @@
#pragma once
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlayExternals.h>
#include <CesiumUtility/IntrusivePointer.h>
namespace CesiumUtility {
class CreditSource;
}
namespace CesiumRasterOverlays {
class RasterOverlay;
/**
* @brief Parameters passed to \ref RasterOverlay::createTileProvider.
*/
struct CESIUMRASTEROVERLAYS_API CreateRasterOverlayTileProviderParameters {
/**
* @brief The external interfaces for use by the raster overlay tile provider.
*/
RasterOverlayExternals externals;
/**
* @brief The overlay that owns the overlay that is creating the tile
* provider.
*
* If nullptr, this overlay is not aggregated, and the owner of the tile
* provider is the overlay that created it.
*/
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner{nullptr};
/**
* @brief The credit source that the new tile provider should use for its
* credits.
*
* If nullptr, a new credit source will be created for the tile provider.
*/
std::shared_ptr<CesiumUtility::CreditSource> pCreditSource{nullptr};
};
} // namespace CesiumRasterOverlays

View File

@ -24,13 +24,7 @@ public:
* @copydoc RasterOverlay::createTileProvider
*/
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
};

View File

@ -86,13 +86,7 @@ public:
virtual ~GeoJsonDocumentRasterOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:

View File

@ -252,8 +252,8 @@ struct GoogleMapTilesNewSessionParameters {
};
/**
* @brief A `RasterOverlay` that retrieves imagery from the [Google Maps Tiles
* API](https://developers.google.com/maps/documentation/tile).
* @brief A @ref RasterOverlay that retrieves imagery from the [Google Maps
* Tiles API](https://developers.google.com/maps/documentation/tile).
*/
class CESIUMRASTEROVERLAYS_API GoogleMapTilesRasterOverlay
: public RasterOverlay {
@ -286,24 +286,12 @@ public:
/** @inheritdoc */
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:
CesiumAsync::Future<CreateTileProviderResult> createNewSession(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner) const;
const CreateRasterOverlayTileProviderParameters& parameters) const;
std::optional<GoogleMapTilesNewSessionParameters> _newSessionParameters;
std::optional<GoogleMapTilesExistingSession> _existingSession;

View File

@ -62,13 +62,7 @@ public:
void setAssetOptions(const std::optional<std::string>& options) noexcept;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
protected:
@ -126,11 +120,10 @@ private:
};
/** @private */
struct Bing {
std::string key;
struct Azure2D {
std::string url;
std::string mapStyle;
std::string culture;
std::string tilesetId;
std::string key;
};
/** @private */
@ -144,7 +137,21 @@ private:
uint32_t tileHeight;
};
std::variant<std::monostate, TileMapService, Bing, Google2D> options{};
/** @private */
struct Bing {
std::string key;
std::string url;
std::string mapStyle;
std::string culture;
};
std::variant<std::monostate, TileMapService, Azure2D, Google2D, Bing>
options{};
void parseAzure2DOptions(const rapidjson::Document& ionResponse);
void parseGoogle2DOptions(const rapidjson::Document& ionResponse);
void parseBingOptions(const rapidjson::Document& ionResponse);
void parseTileMapServiceOptions(const rapidjson::Document& ionResponse);
};
static std::unordered_map<std::string, ExternalAssetEndpoint> endpointCache;

View File

@ -30,39 +30,26 @@ namespace CesiumRasterOverlays {
*/
class CESIUMRASTEROVERLAYS_API QuadtreeRasterOverlayTileProvider
: public RasterOverlayTileProvider {
public:
/**
* @brief Creates a new instance.
*
* @param pOwner The raster overlay that created this tile provider.
* @param asyncSystem The async system used to do work in threads.
* @param pAssetAccessor The interface used to obtain assets (tiles, etc.) for
* this raster overlay.
* @param pCreditSystem The credit system that receives this tile provider's
* credits.
* @param credit The {@link CesiumUtility::Credit} for this tile provider, if it exists.
* @param pPrepareRendererResources The interface used to prepare raster
* images for rendering.
* @param pLogger The logger to which to send messages about the tile provider
* and tiles.
* @param pCreator The \ref RasterOverlay that directly created this instance.
* This will become the owner of this instance if another owner is not
* specified in \ref CreateRasterOverlayTileProviderParameters::pOwner.
* @param parameters The parameters for creating the tile provider.
* @param projection The {@link CesiumGeospatial::Projection}.
* @param tilingScheme The tiling scheme to be used by this {@link QuadtreeRasterOverlayTileProvider}.
* @param coverageRectangle The {@link CesiumGeometry::Rectangle}.
* @param coverageRectangle The rectangle that bounds all the area covered by
* this overlay, expressed in projected coordinates.
* @param minimumLevel The minimum quadtree tile level.
* @param maximumLevel The maximum quadtree tile level.
* @param imageWidth The image width.
* @param imageHeight The image height.
*/
QuadtreeRasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<CesiumUtility::Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,

View File

@ -28,8 +28,9 @@ namespace CesiumRasterOverlays {
class ActivatedRasterOverlay;
class IPrepareRasterOverlayRendererResources;
class RasterOverlayTileProvider;
class RasterOverlayExternals;
class RasterOverlayTileProvider;
struct CreateRasterOverlayTileProviderParameters;
/**
* @brief Options for loading raster overlays.
@ -219,28 +220,12 @@ public:
* @brief Begins asynchronous creation of a tile provider for this overlay
* and eventually returns it via a Future.
*
* @param asyncSystem The async system used to do work in threads.
* @param pAssetAccessor The interface used to download assets like overlay
* metadata and tiles.
* @param pCreditSystem The {@link CesiumUtility::CreditSystem} to use when creating a
* per-TileProvider {@link CesiumUtility::Credit}.
* @param pPrepareRendererResources The interface used to prepare raster
* images for rendering.
* @param pLogger The logger to which to send messages about the tile provider
* and tiles.
* @param pOwner The overlay that owns this overlay, or nullptr if this
* overlay is not aggregated.
* @param parameters The parameters for creating the tile provider.
* @return The future that resolves to the tile provider when it is ready, or
* to error details in the case of an error.
*/
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const = 0;
const CreateRasterOverlayTileProviderParameters& parameters) const = 0;
private:
struct DestructionCompleteDetails {

View File

@ -24,7 +24,7 @@ public:
* This may only be `nullptr` if the raster overlay does not attempt to
* download any resources.
*/
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor;
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor = nullptr;
/**
* @brief The @ref IPrepareRasterOverlayRendererResources that is used to
@ -34,7 +34,7 @@ public:
* for raster overlays.
*/
std::shared_ptr<IPrepareRasterOverlayRendererResources>
pPrepareRendererResources;
pPrepareRendererResources = nullptr;
/**
* @brief The async system to use to do work in threads.
@ -49,7 +49,7 @@ public:
* While not recommended, this may be `nullptr` if the client does not need to
* receive credits.
*/
std::shared_ptr<CesiumUtility::CreditSystem> pCreditSystem;
std::shared_ptr<CesiumUtility::CreditSystem> pCreditSystem = nullptr;
/**
* @brief A spdlog logger that will receive log messages.

View File

@ -25,6 +25,7 @@ namespace CesiumRasterOverlays {
class RasterOverlay;
class RasterOverlayTile;
class IPrepareRasterOverlayRendererResources;
struct CreateRasterOverlayTileProviderParameters;
/**
* @brief Summarizes the result of loading an image of a {@link RasterOverlay}.
@ -139,32 +140,17 @@ public:
/**
* @brief Creates a new instance.
*
* @param pOwner The raster overlay that created this tile provider.
* @param externals The external interfaces for use by the raster overlay.
* @param pCreator The \ref RasterOverlay that directly created this instance.
* This will become the owner of this instance if another owner is not
* specified in \ref CreateRasterOverlayTileProviderParameters::pOwner.
* @param parameters The parameters for creating the tile provider.
* @param projection The {@link CesiumGeospatial::Projection}.
* @param coverageRectangle The rectangle that bounds all the area covered by
* this overlay, expressed in projected coordinates.
*/
RasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const RasterOverlayExternals& externals,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::Rectangle& coverageRectangle) noexcept;
/**
* @brief Creates a new instance.
* @deprecated Use the overload that takes a \ref RasterOverlayExternals
* instead.
*/
RasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<CesiumUtility::Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::Rectangle& coverageRectangle) noexcept;
@ -233,12 +219,27 @@ public:
const CesiumGeometry::Rectangle& getCoverageRectangle() const noexcept;
/**
* @brief Get the per-TileProvider {@link CesiumUtility::Credit} if one exists.
* @deprecated Implement {@link addCredits} instead.
* @brief Gets the @ref CesiumUtility::CreditSource that identifies this
* raster overlay's credits with the @ref CesiumUtility::CreditSystem.
*/
[[deprecated(
"Use addCredits instead.")]] const std::optional<CesiumUtility::Credit>&
getCredit() const noexcept;
const CesiumUtility::CreditSource& getCreditSource() const noexcept;
/**
* @brief Gets the collection of credits that should be shown whenever this
* tile provider is shown.
*
* If called on a non-const instance, the returned collection may be modified
* to add or remove credits.
*
* The credits in this collection will be added to the @ref
* CesiumUtility::CreditReferencer in @ref addCredits.
*/
std::vector<CesiumUtility::Credit>& getCredits() noexcept;
/**
* @copydoc getCredits
*/
const std::vector<CesiumUtility::Credit>& getCredits() const noexcept;
/**
* @brief Loads the image for a tile.
@ -286,9 +287,10 @@ private:
CesiumUtility::IntrusivePointer<RasterOverlay> _pOwner;
RasterOverlayExternals _externals;
std::optional<CesiumUtility::Credit> _credit;
std::vector<CesiumUtility::Credit> _credits;
CesiumGeospatial::Projection _projection;
CesiumGeometry::Rectangle _coverageRectangle;
std::optional<DestructionCompleteDetails> _destructionCompleteDetails;
std::shared_ptr<CesiumUtility::CreditSource> _pCreditSource;
};
} // namespace CesiumRasterOverlays

View File

@ -50,13 +50,7 @@ public:
virtual ~RasterizedPolygonsOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
/**

View File

@ -106,36 +106,9 @@ public:
virtual ~TileMapServiceRasterOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
/**
* @brief Refresh a previously-created tile provider using a new URL and
* request headers. This is primarily useful to refresh an access token after
* it expires.
*
* Calling this method on a tile provider that was not created by this \ref
* TileMapServiceRasterOverlay will lead to undefined behavior.
*
* @param pProvider The previously-created tile provider.
* @param newUrl The new URL to use. If `std::nullopt`, the existing URL is
* unchanged.
* @param newHeaders The new request headers to use. If `std::nullopt`, the
* existing headers are unchanged.
*/
CesiumAsync::Future<void> refreshTileProviderWithNewUrlAndHeaders(
const CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>&
pProvider,
const std::optional<std::string>& newUrl,
const std::optional<std::vector<CesiumAsync::IAssetAccessor::THeader>>&
newHeaders);
private:
std::string _url;
std::vector<CesiumAsync::IAssetAccessor::THeader> _headers;

View File

@ -119,13 +119,7 @@ public:
_options(urlTemplateOptions) {}
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:

View File

@ -88,13 +88,7 @@ public:
virtual ~WebMapServiceRasterOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:

View File

@ -140,13 +140,7 @@ public:
virtual ~WebMapTileServiceRasterOverlay() override;
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:

View File

@ -0,0 +1,591 @@
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/IAssetResponse.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGeospatial/WebMercatorProjection.h>
#include <CesiumJsonReader/JsonObjectJsonHandler.h>
#include <CesiumJsonReader/JsonReader.h>
#include <CesiumRasterOverlays/AzureMapsRasterOverlay.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/ErrorList.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/JsonHelpers.h>
#include <CesiumUtility/JsonValue.h>
#include <CesiumUtility/Uri.h>
#include <CesiumUtility/joinToString.h>
#include <fmt/format.h>
#include <nonstd/expected.hpp>
#include <rapidjson/document.h>
#include <spdlog/logger.h>
#include <spdlog/spdlog.h>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <memory>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
using namespace CesiumAsync;
using namespace CesiumGeometry;
using namespace CesiumGeospatial;
using namespace CesiumJsonReader;
using namespace CesiumUtility;
namespace {
std::unordered_map<std::string, std::vector<std::byte>> sessionCache;
} // namespace
namespace CesiumRasterOverlays {
const std::string AZURE_MAPS_LOGO_HTML =
"<img alt=\"Microsoft Azure\" "
"src=\"data:image/"
"png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAAM0AAAAnCAYAAACsY8yNAAAAAXNSR0IB2cksfwAAAAlwSFlzAA"
"ALEwAACxMBAJqcGAAACaFJREFUeJztWweMVUUU3QUUUURBUIqBFZBFUbGgQQFDlCi2KAr2Hl2w"
"oRRFxQgaVDYrxIINBCyASrHFFRCU2KMIlgixF4wVdBELosD1HuaO7/757/333vJ1/"
"8c5ycnfd+fOvGl35s6dtyUlHh4eHh4eHh4eHh4eHh4e/2/8ckL7mWm4pl/"
"7GT+d2q7lmLklZ4x6rmRmGt4wv+"
"TYum6vh8dmgw2B0pCNZh0bze5sNJVsCJSSV9Z1ez08NhveaDw8UsIbjYdHSnij2TJBRE2ZBzB7"
"MTvVdX22KGzpRsMTphVzMLPDf/"
"3uugK39RLmV8yNZPBVXddpi0IhGQ0P7vHMvxRXMJvFtYF1Zjr5hom8DfM7mTjfM1vnq98KFdzG"
"E5l/Uia+yVPZw1Uf/46dLB/"
"lFh0KzGj6UTb6x+"
"RpK4OocaWk7cfcILL1zL3z2XeFBm5fPebr0l7sMmczd2R2ZjZg7sMcz5xci7KR/y2nnyv/"
"jXYUPIrAaGZjMuTIMyEkjzWarZnXM99g3oTnfPdfIYHMOeYb6YP3nbRy5jpJW1CLsrsx10p+"
"uxAtYzbKXwuKBAVsNHaA4FZ1idBvwvxC9Fa7RvN/"
"A7e7BfMH6YNFTloX1T+1MZp7Vf5n5RduYI/8taBIUMBGM40C33xchP4pauW71TUa/"
"m3IPJNZwTyN2cDJD5ejB3MM80HmI8xK5uFWV95RIb9wf/"
"qKzlXMrVRZcH2GMCcxHybjBp3D3C6i7nsyR4rudCmzd4jetlL3KtGdLO/"
"e39E7nzmMuUb64EOpt+Vo1T/LlbxrrjGRsrdn/"
"ip5n8O7tQFF5NnLeX8onT4GG4a036adpOTHKDl22AOZN0o/"
"7qr0OjCHMu8m45VcziyLa3NOFLDRnM58W/"
"7+lFnf0S1lzpH0xWQiRhbWaODPfy2yz0lNYP67GXMq82fKxk/"
"MtqL3ocg+Zl6kJs+m8pjbMK+jIOCggXPUIuYhTt37q3ppzHf0Dma+TNkHe2AVGSPdVnR/"
"C9FJgthdmTLH5Swyi80KeYZHsFNInssTvHut08dAU6ecVirtHSVfoOSXkvFILHqKDuqqo4gWH5"
"EywNQoYKPBSjJcPR/q6JZRMOHRaYmNhswONIcy8RnzRSkTE6G9M6BrKXAZ/"
"ymPeZdTzifMJZQ50b8lWd3IBC5+FDkCGLOYj4lsgWpfRzLGa/"
"EHmbPZCiXDDlsl+v+m0cwSXdSxlchuUmVUhOTBmKwLoZ3A+"
"B3o9DFQG6OpcdrUk8wCZwNEcN3hXt5PwbigLgfGtT0UP/"
"ffbUoarh5QNnHVGW13GVtdOmDMvJIpKXlUzOC4RrM7BSs7ttd6Sne8yDGRd6Z0RoMVer3IsRL1"
"sGWTcQcGosyQAYXu4TKQuDTcV5WD95yo6gejfkLlnSjyQUo2QunDkHvL3whgLFJ69zCbS1p9yt"
"zxYOS7Sb3bMVeK/CUyRm3ZTZX3gpL/"
"42JGjElz9S6cZUpFrscm0RmJ9Y6mIBhRTRKYoc03Giwu8Exaow/JuI/"
"2bIdFpovK14OCxfahJPXOxvSaNmlZb9qqBmvm1G/"
"y+5OlrVOycUynukZTSsGhEx1TLnqNKPDdbxVZGqNZLjIM+"
"pExdbIDmnXo5ecH1DvDVttmFKxsG6Q+2m2ZSM45S/IhVG6NETtXYycd/"
"VKpyhkk8rwHAihzRznPqcNLqm05vzogs4jYuqFPy1Ta5hrNSCdPhUrrF1KX+ZJWy/"
"ur6TWrU3Ils+"
"Nfz5SO3ji3pCYlL4vp2AyjERlWELuljxDZafKMVaubyBIZjchsyPQVCpm0Tp3sgGLFaqLk2A0+"
"UPXoHJF/iapXdzIBALvSbZA6XEiyk0ies1SeaRHlHq107hVZXo2GzOK0TPLA/"
"cMq3UbxFlXmhBzlNCZzdWD7qndEHwO1MRrXddeRPgRZ7nP4iaRtTNIP2ZheQyn5J7PT+"
"uqSSprH9UvHVGcakbWkYHK+SuYQ+"
"qQ8v8fcRvSSGk250ns0rnvUgOLwuLWS70DGXQPgM7eJyD9Pva8PmRUad0f6QhaLAi4Ou0qewSo"
"tKnJ4kNKZLbJ8Gw1cOrtTor5fSj9a6sM3AiVRkcJrVXun5OhjoDZG08XJM5sSIkk/"
"ZKPAjUbkQ0WGAcQtt3VdhimdpEbTUelVx3UPRRsNVs8vJA2rcFlE/"
"lfU+7oreVcyQYRfVToCBjsxL1ay+yLKPczVofwbzXhKDhh+"
"35AysDtZg3mNHFfT6WPANZrWKi2p0Tyi0hBMGhzFJP2QjeIwmnYUrHir5BeTbQ+"
"lk9RocGC2h1FM+"
"rKYOkUZDe5sXo2ZMA1VfTFxWjjpONTDZXtX1f08ynS9MNHCzj1DlM4lIktqNAtztVn0cR6zrhm"
"CLaMjOEOVe49TBiKFb0oaIm/"
"7RrxruSqjzEk7QKUlNRp93su4z8oPisBoJG0+"
"ZQIumo6mpQkEVCtdBBrcS08cxJvK36FGI2nnqnKwo7RQaTAqHTKfJvJDyPkIlZ+"
"vVnowBkQD7eSHsZ3s6MMA7C6HxaOTyHMZTWf1jo9yjYPoX0DBjj43h14zpYdzXzuRY0F4XL0z8"
"jzLaQuV3mAnbZJKS2o0fVXapkhhyDsRFcy6X0qG4jEaHI713UdPJz2N0eDSUF9GYqWHK4KV82n"
"mL5R9TxNmNLjY1KskJnKVlIOzjJ1M2G06Sx58BQA3bILUeQQFB1PsgDZCOFCVC/"
"cPExBnodskvwVC8TYMnMtosMvqeyYckPElROglH5mbf4uLY8btedHDjjtcZOeq9q+R+"
"s8IIXbj8ykI9qDvx5H5uuExyryYTGo08CYWq/"
"S3pJ8RVELIfyqZYEzo51nxKB6jwQS1W33WSknpvwg4lII7DRf47D3WaCQddyTLIsoBECzoo/"
"RHRughkjbcKRufhYR9DWD1MbkaKf1Io5F0HMjd2/GsMSETfNFfh+f8lwrKNPClInO/PI/"
"Cprsi5lMRbbxNPScyGtUXb1M00K7yXO2KRmEZDb5XqhJmhXBZdpykZa2OZNwem7e3yBAyvU5k1"
"1D2TlEucqyqCA3j/PAQmW+h7MXbVZIfv/"
"Xd94oOImmYODiALpaycLEJt6tVyDtvJuNuLiXj1iEMegQ5X3OTcfGwK95OZjXHJMDdyB1kXBD3"
"0yIEJ0ZJfQeF1BP9MVTKQh2rKfx7t71VX14R1mZHv6XSHyvvqUpI28/oQ5zn8A0gvta4U/"
"qkqdIdot5ZoeS7RNQLruOlUh7GBTvOM9L/"
"vaLGMx4FZDQeHsUBbzQeHinhjcbDIyW80Xh4pIQ3Gg8PDw8PDw8PDw8PDw8PDw8PDw8Pj9T4G6"
"U/URjRT3IrAAAAAElFTkSuQmCC\"/>";
namespace {
Rectangle createRectangle(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner) {
return WebMercatorProjection::computeMaximumProjectedRectangle(
pOwner->getOptions().ellipsoid);
}
QuadtreeTilingScheme createTilingScheme(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner) {
return QuadtreeTilingScheme(createRectangle(pOwner), 1, 1);
}
} // namespace
class AzureMapsRasterOverlayTileProvider final
: public QuadtreeRasterOverlayTileProvider {
public:
AzureMapsRasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const std::string& credit,
const std::string& baseUrl,
const std::string& apiVersion,
const std::string& tilesetId,
const std::string& key,
const std::string& tileEndpoint,
uint32_t minimumLevel,
uint32_t maximumLevel,
uint32_t imageSize,
bool showLogo);
virtual ~AzureMapsRasterOverlayTileProvider() = default;
void update(const AzureMapsRasterOverlayTileProvider& newProvider) {
this->_baseUrl = newProvider._baseUrl;
this->_apiVersion = newProvider._apiVersion;
this->_tilesetId = newProvider._tilesetId;
this->_key = newProvider._key;
this->_tileEndpoint = newProvider._tileEndpoint;
}
CesiumAsync::Future<void> loadCredits();
protected:
virtual CesiumAsync::Future<LoadedRasterOverlayImage> loadQuadtreeTileImage(
const CesiumGeometry::QuadtreeTileID& tileID) const override;
private:
std::string _baseUrl;
std::string _apiVersion;
std::string _tilesetId;
std::string _key;
std::string _tileEndpoint;
};
AzureMapsRasterOverlay::AzureMapsRasterOverlay(
const std::string& name,
const AzureMapsSessionParameters& sessionParameters,
const RasterOverlayOptions& overlayOptions)
: RasterOverlay(name, overlayOptions),
_sessionParameters(sessionParameters) {
Uri baseUrl(this->_sessionParameters.apiBaseUrl);
baseUrl.ensureTrailingSlash();
this->_sessionParameters.apiBaseUrl = baseUrl.toString();
}
AzureMapsRasterOverlay::~AzureMapsRasterOverlay() = default;
Future<RasterOverlay::CreateTileProviderResult>
AzureMapsRasterOverlay::createTileProvider(
const CreateRasterOverlayTileProviderParameters& parameters) const {
Uri tilesetUri(this->_sessionParameters.apiBaseUrl, "map/tileset");
UriQuery tilesetQuery(tilesetUri);
tilesetQuery.setValue("api-version", this->_sessionParameters.apiVersion);
tilesetQuery.setValue("tilesetId", this->_sessionParameters.tilesetId);
tilesetQuery.setValue("subscription-key", this->_sessionParameters.key);
tilesetQuery.setValue("language", this->_sessionParameters.language);
tilesetQuery.setValue("view", this->_sessionParameters.view);
tilesetUri.setQuery(tilesetQuery.toQueryString());
std::string tilesetUrl = std::string(tilesetUri.toString());
IntrusivePointer<const AzureMapsRasterOverlay> thiz = this;
auto handleResponse =
[parameters, thiz](
const std::shared_ptr<IAssetRequest>& pRequest,
const std::span<const std::byte>& data) -> CreateTileProviderResult {
JsonObjectJsonHandler handler{};
ReadJsonResult<JsonValue> response = JsonReader::readJson(data, handler);
if (!response.value) {
ErrorList errorList{};
errorList.errors = std::move(response.errors);
errorList.warnings = std::move(response.warnings);
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = errorList.format("Failed to parse response from Azure "
"Maps tileset API service:")});
}
if (!response.value->isObject()) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = "Response from Azure Maps tileset API service was not a "
"JSON object."});
}
const JsonValue::Object& responseObject = response.value->getObject();
uint32_t tileSize = 256u;
uint32_t minimumLevel = 0u;
uint32_t maximumLevel = 22u;
// "tileSize" is an optional enum property that is delivered as a string.
std::string tileSizeEnum = "256";
auto it = responseObject.find("tileSize");
if (it != responseObject.end() && it->second.isString()) {
tileSizeEnum = it->second.getString();
}
try {
tileSize = static_cast<uint32_t>(std::stoul(tileSizeEnum));
} catch (std::exception&) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = "Response from Azure Maps tileset API service did not "
"contain a valid "
"'tileSize' property."});
}
it = responseObject.find("minzoom");
if (it != responseObject.end() && it->second.isNumber()) {
minimumLevel = it->second.getSafeNumberOrDefault<uint32_t>(0);
} else if (it != responseObject.end()) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = "Response from Azure Maps tileset API service did not "
"contain a valid "
"'minzoom' property."});
}
it = responseObject.find("maxzoom");
if (it != responseObject.end() && it->second.isNumber()) {
maximumLevel = it->second.getSafeNumberOrDefault<uint32_t>(0);
} else if (it != responseObject.end()) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = "Response from Azure Maps tileset API service did not "
"contain a valid "
"'maxzoom' property."});
}
std::vector<std::string> tiles;
it = responseObject.find("tiles");
if (it != responseObject.end()) {
tiles = it->second.getArrayOfStrings(std::string());
}
if (tiles.empty()) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = "Response from Azure Maps tileset API service did not "
"contain a valid "
"'tiles' property."});
}
std::string tileEndpoint;
for (const std::string& endpoint : tiles) {
// Azure Maps documentation: "If multiple endpoints are specified, clients
// may use any combination of endpoints. All endpoints MUST return the
// same content for the same URL."
// This just picks the first non-empty string endpoint.
if (!endpoint.empty()) {
tileEndpoint = endpoint;
break;
}
}
if (tileEndpoint.empty()) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
RasterOverlayLoadType::TileProvider,
pRequest,
"Azure Maps returned no valid endpoints for the given tilesetId."});
}
std::string topLevelCredit;
it = responseObject.find("attribution");
if (it != responseObject.end() && it->second.isString()) {
topLevelCredit = it->second.getString();
}
auto* pProvider = new AzureMapsRasterOverlayTileProvider(
thiz,
parameters,
topLevelCredit,
thiz->_sessionParameters.apiBaseUrl,
thiz->_sessionParameters.apiVersion,
thiz->_sessionParameters.tilesetId,
thiz->_sessionParameters.key,
tileEndpoint,
minimumLevel,
maximumLevel,
tileSize,
thiz->_sessionParameters.showLogo);
// Start loading credits, but don't wait for the load to finish.
pProvider->loadCredits();
return pProvider;
};
auto cacheResultIt = sessionCache.find(tilesetUrl);
if (cacheResultIt != sessionCache.end()) {
return parameters.externals.asyncSystem.createResolvedFuture(
handleResponse(nullptr, std::span<std::byte>(cacheResultIt->second)));
}
return parameters.externals.pAssetAccessor
->get(parameters.externals.asyncSystem, tilesetUrl)
.thenInMainThread(
[tilesetUrl,
handleResponse](std::shared_ptr<IAssetRequest>&& pRequest)
-> CreateTileProviderResult {
const IAssetResponse* pResponse = pRequest->response();
if (!pResponse) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
RasterOverlayLoadType::TileProvider,
pRequest,
"No response received from Azure Maps imagery tileset "
"service."});
}
if (pResponse->statusCode() < 200 ||
pResponse->statusCode() >= 300) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message = fmt::format(
"Azure Maps API tileset request service returned "
"HTTP status code {}.",
pResponse->statusCode())});
}
CreateTileProviderResult handleResponseResult =
handleResponse(pRequest, pResponse->data());
// If the response successfully created a tile provider, cache it.
if (handleResponseResult) {
sessionCache[tilesetUrl] = std::vector<std::byte>(
pResponse->data().begin(),
pResponse->data().end());
}
return handleResponseResult;
});
}
namespace {
CesiumAsync::Future<rapidjson::Document> fetchAttributionData(
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<spdlog::logger>& pLogger,
const std::string& apiBaseUrl,
const std::string& apiVersion,
const std::string& tilesetId,
const std::string& key,
uint32_t zoom,
const Rectangle& rectangleDegrees) {
std::vector<std::string> bounds(4);
bounds[0] = std::to_string(rectangleDegrees.minimumX); // west
bounds[1] = std::to_string(rectangleDegrees.minimumY); // south
bounds[2] = std::to_string(rectangleDegrees.maximumX); // east
bounds[3] = std::to_string(rectangleDegrees.maximumY); // north
Uri attributionUri(apiBaseUrl, "map/attribution", true);
UriQuery query(attributionUri);
query.setValue("api-version", apiVersion);
query.setValue("subscription-key", key);
query.setValue("tilesetId", tilesetId);
query.setValue("zoom", std::to_string(zoom));
query.setValue("bounds", joinToString(bounds, ","));
attributionUri.setQuery(query.toQueryString());
return pAssetAccessor
->get(asyncSystem, std::string(attributionUri.toString()))
.thenInMainThread(
[pLogger](const std::shared_ptr<IAssetRequest>& pRequest)
-> rapidjson::Document {
const IAssetResponse* pResponse = pRequest->response();
rapidjson::Document document;
if (!pResponse) {
SPDLOG_LOGGER_ERROR(
pLogger,
"No response received from Azure Maps API attribution "
"service.");
return document;
}
if (pResponse->statusCode() < 200 ||
pResponse->statusCode() >= 300) {
SPDLOG_LOGGER_ERROR(
pLogger,
"Error response {} received from Azure Maps API attribution "
"service URL {}.",
pResponse->statusCode(),
pRequest->url());
return document;
}
document.Parse(
reinterpret_cast<const char*>(pResponse->data().data()),
pResponse->data().size());
if (document.HasParseError()) {
SPDLOG_LOGGER_ERROR(
pLogger,
"Error when parsing Azure Maps API attribution service JSON "
"from URL {}, error code {} at byte offset {}.",
pRequest->url(),
document.GetParseError(),
document.GetErrorOffset());
}
return document;
});
}
} // namespace
AzureMapsRasterOverlayTileProvider::AzureMapsRasterOverlayTileProvider(
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const std::string& credit,
const std::string& baseUrl,
const std::string& apiVersion,
const std::string& tilesetId,
const std::string& key,
const std::string& tileEndpoint,
uint32_t minimumLevel,
uint32_t maximumLevel,
uint32_t imageSize,
bool showLogo)
: QuadtreeRasterOverlayTileProvider(
pCreator,
parameters,
WebMercatorProjection(pCreator->getOptions().ellipsoid),
createTilingScheme(pCreator),
createRectangle(pCreator),
minimumLevel,
maximumLevel,
imageSize,
imageSize),
_baseUrl(baseUrl),
_apiVersion(apiVersion),
_tilesetId(tilesetId),
_key(key),
_tileEndpoint(tileEndpoint) {
if (parameters.externals.pCreditSystem) {
if (!credit.empty()) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
credit,
pCreator->getOptions().showCreditsOnScreen));
}
if (showLogo) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
AZURE_MAPS_LOGO_HTML,
true));
}
}
}
CesiumAsync::Future<LoadedRasterOverlayImage>
AzureMapsRasterOverlayTileProvider::loadQuadtreeTileImage(
const CesiumGeometry::QuadtreeTileID& tileID) const {
Uri uri(CesiumUtility::Uri::substituteTemplateParameters(
this->_tileEndpoint,
[this, &tileID](const std::string& key) {
if (key == "z") {
return std::to_string(tileID.level);
}
if (key == "x") {
return std::to_string(tileID.x);
}
if (key == "y") {
return std::to_string(
tileID.computeInvertedY(this->getTilingScheme()));
}
return key;
}));
UriQuery query(uri);
query.setValue("subscription-key", this->_key);
uri.setQuery(query.toQueryString());
std::string url = std::string(uri.toString());
LoadTileImageFromUrlOptions options;
options.allowEmptyImages = true;
options.moreDetailAvailable = tileID.level < this->getMaximumLevel();
options.rectangle = this->getTilingScheme().tileToRectangle(tileID);
return this->loadTileImageFromUrl(url, {}, std::move(options));
}
Future<void> AzureMapsRasterOverlayTileProvider::loadCredits() {
const uint32_t maximumZoomLevel = this->getMaximumLevel();
IntrusivePointer thiz = this;
std::vector<Future<std::vector<std::string>>> creditFutures;
creditFutures.reserve(maximumZoomLevel + 1);
for (size_t i = 0; i <= maximumZoomLevel; ++i) {
creditFutures.emplace_back(
fetchAttributionData(
this->getAssetAccessor(),
this->getAsyncSystem(),
this->getLogger(),
this->_baseUrl,
this->_apiVersion,
this->_tilesetId,
this->_key,
uint32_t(i),
Rectangle(-180.0, -90.0, 180.0, 90.0))
.thenInMainThread([](rapidjson::Document&& document) {
if (document.HasParseError() || !document.IsObject()) {
return std::vector<std::string>();
}
return JsonHelpers::getStrings(document, "copyrights");
}));
}
return this->getAsyncSystem()
.all(std::move(creditFutures))
.thenInMainThread([thiz](
std::vector<std::vector<std::string>>&& results) {
std::set<std::string> uniqueCredits;
for (size_t i = 0; i < results.size(); i++) {
const std::vector<std::string>& credits = results[i];
for (size_t j = 0; j < credits.size(); j++) {
if (!credits[j].empty()) {
uniqueCredits.insert(credits[j]);
}
}
}
for (const std::string& credit : uniqueCredits) {
thiz->getCredits().emplace_back(thiz->getCreditSystem()->createCredit(
thiz->getCreditSource(),
credit,
thiz->getOwner().getOptions().showCreditsOnScreen));
}
});
}
} // namespace CesiumRasterOverlays

View File

@ -2,11 +2,12 @@
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/IAssetResponse.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGeospatial/WebMercatorProjection.h>
#include <CesiumRasterOverlays/BingMapsRasterOverlay.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
@ -21,8 +22,6 @@
#include <nonstd/expected.hpp>
#include <rapidjson/document.h>
#include <rapidjson/pointer.h>
#include <spdlog/logger.h>
#include <spdlog/spdlog.h>
#include <cstddef>
#include <cstdint>
@ -46,6 +45,11 @@ struct CoverageArea {
uint32_t zoomMax;
};
struct CreditStringAndCoverageAreas {
std::string credit;
std::vector<CoverageArea> coverageAreas;
};
struct CreditAndCoverageAreas {
Credit credit;
std::vector<CoverageArea> coverageAreas;
@ -68,7 +72,9 @@ const std::string BingMapsStyle::CANVAS_GRAY = "CanvasGray";
const std::string BingMapsStyle::ORDNANCE_SURVEY = "OrdnanceSurvey";
const std::string BingMapsStyle::COLLINS_BART = "CollinsBart";
const std::string BingMapsRasterOverlay::BING_LOGO_HTML =
namespace {
const std::string BING_LOGO_HTML =
"<a href=\"http://www.bing.com\"><img "
"src=\"data:image/"
"png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAATCAMAAAAj1DqpAAAAq1BMVEUAAAD////"
@ -94,18 +100,24 @@ const std::string BingMapsRasterOverlay::BING_LOGO_HTML =
"OXfbBoeDOo8wHpy8lKpvoafRoG6YgXFYKP4GSj63gtwWfhHzl7Skq9JTshAAAAAElFTkSuQmCC"
"\" title=\"Bing Imagery\"/></a>";
Rectangle createRectangle(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner) {
return WebMercatorProjection::computeMaximumProjectedRectangle(
pOwner->getOptions().ellipsoid);
}
QuadtreeTilingScheme createTilingScheme(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner) {
return QuadtreeTilingScheme(createRectangle(pOwner), 2, 2);
}
} // namespace
class BingMapsTileProvider final : public QuadtreeRasterOverlayTileProvider {
public:
BingMapsTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
Credit bingCredit,
const std::vector<CreditAndCoverageAreas>& perTileCredits,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const std::vector<CreditStringAndCoverageAreas>& perTileCredits,
const std::string& baseUrl,
const std::string& urlTemplate,
const std::vector<std::string>& subdomains,
@ -113,32 +125,41 @@ public:
uint32_t height,
uint32_t minimumLevel,
uint32_t maximumLevel,
const std::string& culture,
const CesiumGeospatial::Ellipsoid& ellipsoid)
const std::string& culture)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
bingCredit,
pPrepareRendererResources,
pLogger,
WebMercatorProjection(ellipsoid),
QuadtreeTilingScheme(
WebMercatorProjection::computeMaximumProjectedRectangle(
ellipsoid),
2,
2),
WebMercatorProjection::computeMaximumProjectedRectangle(ellipsoid),
pCreator,
parameters,
WebMercatorProjection(pCreator->getOptions().ellipsoid),
createTilingScheme(pCreator),
createRectangle(pCreator),
minimumLevel,
maximumLevel,
width,
height),
_credits(perTileCredits),
_credits(),
_baseUrl(baseUrl),
_urlTemplate(urlTemplate),
_culture(culture),
_subdomains(subdomains) {}
_subdomains(subdomains) {
if (parameters.externals.pCreditSystem) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
BING_LOGO_HTML,
pCreator->getOptions().showCreditsOnScreen));
this->_credits.reserve(perTileCredits.size());
for (const CreditStringAndCoverageAreas& creditStringAndCoverageAreas :
perTileCredits) {
this->_credits.emplace_back(CreditAndCoverageAreas{
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
creditStringAndCoverageAreas.credit,
pCreator->getOptions().showCreditsOnScreen),
creditStringAndCoverageAreas.coverageAreas});
}
}
}
virtual ~BingMapsTileProvider() = default;
@ -280,19 +301,11 @@ namespace {
* \endcode
*
* @param pResource The JSON value for the resource
* @param pCreditSystem The `CreditSystem` that will create one credit for
* each attribution
* @return The `CreditAndCoverageAreas` objects that have been parsed, or an
* empty vector if pCreditSystem is nullptr.
* @return The `CreditStringAndCoverageAreas` objects that have been parsed.
*/
std::vector<CreditAndCoverageAreas> collectCredits(
const rapidjson::Value* pResource,
const std::shared_ptr<CreditSystem>& pCreditSystem,
bool showCreditsOnScreen) {
std::vector<CreditAndCoverageAreas> credits;
if (!pCreditSystem) {
return credits;
}
std::vector<CreditStringAndCoverageAreas>
collectCredits(const rapidjson::Value* pResource) {
std::vector<CreditStringAndCoverageAreas> credits;
const auto attributionsIt = pResource->FindMember("imageryProviders");
if (attributionsIt != pResource->MemberEnd() &&
@ -339,11 +352,9 @@ std::vector<CreditAndCoverageAreas> collectCredits(
const auto creditString = attribution.FindMember("attribution");
if (creditString != attribution.MemberEnd() &&
creditString->value.IsString()) {
credits.push_back(
{pCreditSystem->createCredit(
creditString->value.GetString(),
showCreditsOnScreen),
coverageAreas});
credits.emplace_back(CreditStringAndCoverageAreas{
creditString->value.GetString(),
coverageAreas});
}
}
}
@ -354,13 +365,7 @@ std::vector<CreditAndCoverageAreas> collectCredits(
Future<RasterOverlay::CreateTileProviderResult>
BingMapsRasterOverlay::createTileProvider(
const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
IntrusivePointer<const RasterOverlay> pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
Uri metadataUri(
this->_url,
"REST/v1/Imagery/Metadata/" + this->_mapStyle,
@ -377,20 +382,10 @@ BingMapsRasterOverlay::createTileProvider(
std::string metadataUrl = std::string(metadataUri.toString());
pOwner = pOwner ? pOwner : this;
const CesiumGeospatial::Ellipsoid& ellipsoid = this->getOptions().ellipsoid;
IntrusivePointer<const BingMapsRasterOverlay> thiz = this;
auto handleResponse =
[pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
pPrepareRendererResources,
pLogger,
ellipsoid,
baseUrl = this->_url,
culture = this->_culture](
[thiz, parameters](
const std::shared_ptr<IAssetRequest>& pRequest,
const std::span<const std::byte>& data) -> CreateTileProviderResult {
rapidjson::Document response;
@ -448,39 +443,31 @@ BingMapsRasterOverlay::createTileProvider(
"Bing Maps tile imageUrl is missing or empty."});
}
bool showCredits = pOwner->getOptions().showCreditsOnScreen;
std::vector<CreditAndCoverageAreas> credits =
collectCredits(pResource, pCreditSystem, showCredits);
Credit bingCredit =
pCreditSystem->createCredit(BING_LOGO_HTML, showCredits);
std::vector<CreditStringAndCoverageAreas> credits =
collectCredits(pResource);
return new BingMapsTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
bingCredit,
thiz,
parameters,
credits,
pPrepareRendererResources,
pLogger,
baseUrl,
thiz->_url,
urlTemplate,
subdomains,
width,
height,
0,
maximumLevel,
culture,
ellipsoid);
thiz->_culture);
};
auto cacheResultIt = sessionCache.find(metadataUrl);
if (cacheResultIt != sessionCache.end()) {
return asyncSystem.createResolvedFuture(
return parameters.externals.asyncSystem.createResolvedFuture(
handleResponse(nullptr, std::span<std::byte>(cacheResultIt->second)));
}
return pAssetAccessor->get(asyncSystem, metadataUrl)
return parameters.externals.pAssetAccessor
->get(parameters.externals.asyncSystem, metadataUrl)
.thenInMainThread(
[metadataUrl,
handleResponse](std::shared_ptr<IAssetRequest>&& pRequest)
@ -509,55 +496,4 @@ BingMapsRasterOverlay::createTileProvider(
});
}
Future<void> BingMapsRasterOverlay::refreshTileProviderWithNewKey(
const IntrusivePointer<RasterOverlayTileProvider>& pProvider,
const std::string& newKey) {
this->_key = newKey;
return this
->createTileProvider(
pProvider->getAsyncSystem(),
pProvider->getAssetAccessor(),
pProvider->getCreditSystem(),
pProvider->getPrepareRendererResources(),
pProvider->getLogger(),
&pProvider->getOwner())
.thenInMainThread([pProvider](CreateTileProviderResult&& result) {
if (!result) {
SPDLOG_LOGGER_WARN(
pProvider->getLogger(),
"Could not refresh Bing Maps raster overlay with a new key: {}.",
result.error().message);
return;
}
// Use static_cast instead of dynamic_cast here to avoid the need for
// RTTI, and because we are certain of the type.
// NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast)
BingMapsTileProvider* pOldBing =
static_cast<BingMapsTileProvider*>(pProvider.get());
BingMapsTileProvider* pNewBing =
static_cast<BingMapsTileProvider*>(result->get());
// NOLINTEND(cppcoreguidelines-pro-type-static-cast-downcast)
if (pOldBing->getCoverageRectangle().getLowerLeft() !=
pNewBing->getCoverageRectangle().getLowerLeft() ||
pOldBing->getCoverageRectangle().getUpperRight() !=
pNewBing->getCoverageRectangle().getUpperRight() ||
pOldBing->getHeight() != pNewBing->getHeight() ||
pOldBing->getWidth() != pNewBing->getWidth() ||
pOldBing->getMinimumLevel() != pNewBing->getMinimumLevel() ||
pOldBing->getMaximumLevel() != pNewBing->getMaximumLevel() ||
pOldBing->getProjection() != pNewBing->getProjection()) {
SPDLOG_LOGGER_WARN(
pProvider->getLogger(),
"Could not refresh Bing Maps raster overlay with a new key "
"because some metadata properties changed unexpectedly upon "
"refresh.");
return;
}
pOldBing->update(*pNewBing);
});
}
} // namespace CesiumRasterOverlays

View File

@ -1,23 +1,18 @@
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumGltf/ImageAsset.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/DebugColorizeTilesRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/SpanHelper.h>
#include <spdlog/logger.h>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <utility>
@ -32,24 +27,14 @@ namespace {
class DebugTileProvider : public RasterOverlayTileProvider {
public:
DebugTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const Ellipsoid& ellipsoid)
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters)
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
nullptr,
std::nullopt,
pPrepareRendererResources,
pLogger,
GeographicProjection(ellipsoid),
GeographicProjection::computeMaximumProjectedRectangle(ellipsoid)) {
}
pCreator,
parameters,
GeographicProjection(pCreator->getOptions().ellipsoid),
GeographicProjection::computeMaximumProjectedRectangle(
pCreator->getOptions().ellipsoid)) {}
virtual CesiumAsync::Future<LoadedRasterOverlayImage>
loadTileImage(const RasterOverlayTile& overlayTile) override {
@ -91,21 +76,9 @@ DebugColorizeTilesRasterOverlay::DebugColorizeTilesRasterOverlay(
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult>
DebugColorizeTilesRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& /* pCreditSystem */,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
pOwner = pOwner ? pOwner : this;
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
IntrusivePointer<RasterOverlayTileProvider>(new DebugTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pPrepareRendererResources,
pLogger,
this->getOptions().ellipsoid)));
const CreateRasterOverlayTileProviderParameters& parameters) const {
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
IntrusivePointer<RasterOverlayTileProvider>(
new DebugTileProvider(this, parameters)));
}

View File

@ -5,21 +5,14 @@
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <optional>
namespace CesiumRasterOverlays {
EmptyRasterOverlayTileProvider::EmptyRasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem) noexcept
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters) noexcept
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
nullptr,
nullptr,
std::nullopt,
nullptr,
nullptr,
pCreator,
parameters,
CesiumGeospatial::GeographicProjection(),
CesiumGeometry::Rectangle()) {}

View File

@ -9,8 +9,8 @@ class EmptyRasterOverlayTileProvider
public:
EmptyRasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<
const CesiumRasterOverlays::RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem) noexcept;
const CesiumRasterOverlays::RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters) noexcept;
protected:
virtual CesiumAsync::Future<CesiumRasterOverlays::LoadedRasterOverlayImage>

View File

@ -1,10 +1,9 @@
#include "CesiumGeometry/Rectangle.h"
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/BoundingRegionBuilder.h>
#include <CesiumGeospatial/Cartographic.h>
#include <CesiumGeospatial/Ellipsoid.h>
@ -12,6 +11,7 @@
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGltf/ImageAsset.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/GeoJsonDocumentRasterOverlay.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
@ -19,7 +19,6 @@
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumVectorData/GeoJsonDocument.h>
#include <CesiumVectorData/GeoJsonObject.h>
@ -30,7 +29,6 @@
#include <glm/common.hpp>
#include <glm/ext/vector_int2.hpp>
#include <nonstd/expected.hpp>
#include <spdlog/logger.h>
#include <algorithm>
#include <cstddef>
@ -514,32 +512,22 @@ private:
public:
GeoJsonDocumentRasterOverlayTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const GeoJsonDocumentRasterOverlayOptions& options,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const GeoJsonDocumentRasterOverlayOptions& geoJsonOptions,
std::shared_ptr<CesiumVectorData::GeoJsonDocument>&& pDocument)
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
std::nullopt,
pPrepareRendererResources,
pLogger,
GeographicProjection(options.ellipsoid),
pCreator,
parameters,
GeographicProjection(geoJsonOptions.ellipsoid),
projectRectangleSimple(
GeographicProjection(options.ellipsoid),
GeographicProjection(geoJsonOptions.ellipsoid),
GlobeRectangle::MAXIMUM)),
_pDocument(std::move(pDocument)),
_defaultStyle(options.defaultStyle),
_defaultStyle(geoJsonOptions.defaultStyle),
_tree(),
_ellipsoid(options.ellipsoid),
_mipLevels(options.mipLevels) {
_ellipsoid(geoJsonOptions.ellipsoid),
_mipLevels(geoJsonOptions.mipLevels) {
CESIUM_ASSERT(this->_pDocument);
this->_tree = buildQuadtree(this->_pDocument, this->_defaultStyle);
}
@ -643,27 +631,14 @@ GeoJsonDocumentRasterOverlay::~GeoJsonDocumentRasterOverlay() = default;
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult>
GeoJsonDocumentRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
pOwner = pOwner ? pOwner : this;
IntrusivePointer<const GeoJsonDocumentRasterOverlay> thiz = this;
return std::move(
const_cast<GeoJsonDocumentRasterOverlay*>(this)->_documentFuture)
.thenInMainThread(
[pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
pPrepareRendererResources,
pLogger,
options =
this->_options](std::shared_ptr<GeoJsonDocument>&& pDocument)
[thiz, parameters](std::shared_ptr<GeoJsonDocument>&& pDocument)
-> CreateTileProviderResult {
if (!pDocument) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
@ -674,13 +649,9 @@ GeoJsonDocumentRasterOverlay::createTileProvider(
return IntrusivePointer<RasterOverlayTileProvider>(
new GeoJsonDocumentRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
pPrepareRendererResources,
pLogger,
options,
thiz,
parameters,
thiz->_options,
std::move(pDocument)));
});
}

View File

@ -13,14 +13,13 @@
#include <CesiumJsonReader/JsonReader.h>
#include <CesiumJsonWriter/JsonObjectWriter.h>
#include <CesiumJsonWriter/PrettyJsonWriter.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/GoogleMapTilesRasterOverlay.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/CreditReferencer.h>
#include <CesiumUtility/ErrorList.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/JsonHelpers.h>
@ -140,14 +139,8 @@ class GoogleMapTilesRasterOverlayTileProvider
: public QuadtreeRasterOverlayTileProvider {
public:
GoogleMapTilesRasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<CesiumUtility::Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const std::string& apiBaseUrl,
const std::string& session,
const std::string& key,
@ -161,9 +154,6 @@ public:
CesiumAsync::Future<void> loadCredits();
virtual void addCredits(
CesiumUtility::CreditReferencer& creditReferencer) noexcept override;
protected:
virtual CesiumAsync::Future<LoadedRasterOverlayImage> loadQuadtreeTileImage(
const CesiumGeometry::QuadtreeTileID& tileID) const override;
@ -175,21 +165,11 @@ private:
std::string _apiBaseUrl;
std::string _session;
std::string _key;
std::optional<Credit> _googleCredit;
std::optional<Credit> _credits;
mutable QuadtreeRectangleAvailability _availableTiles;
mutable QuadtreeRectangleAvailability _availableAvailability;
};
void ensureTrailingSlash(std::string& url) {
Uri uri(url);
std::string_view path = uri.getPath();
if (path.empty() || path.back() != '/') {
uri.setPath(std::string(path) + '/');
url = uri.toString();
}
}
} // namespace
namespace CesiumRasterOverlays {
@ -201,7 +181,9 @@ GoogleMapTilesRasterOverlay::GoogleMapTilesRasterOverlay(
: RasterOverlay(name, overlayOptions),
_newSessionParameters(newSessionParameters),
_existingSession(std::nullopt) {
ensureTrailingSlash(this->_newSessionParameters->apiBaseUrl);
Uri baseUrl(this->_newSessionParameters->apiBaseUrl);
baseUrl.ensureTrailingSlash();
this->_newSessionParameters->apiBaseUrl = baseUrl.toString();
}
GoogleMapTilesRasterOverlay::GoogleMapTilesRasterOverlay(
@ -211,40 +193,23 @@ GoogleMapTilesRasterOverlay::GoogleMapTilesRasterOverlay(
: RasterOverlay(name, overlayOptions),
_newSessionParameters(std::nullopt),
_existingSession(existingSession) {
ensureTrailingSlash(this->_existingSession->apiBaseUrl);
Uri baseUrl(this->_existingSession->apiBaseUrl);
baseUrl.ensureTrailingSlash();
this->_existingSession->apiBaseUrl = baseUrl.toString();
}
Future<RasterOverlay::CreateTileProviderResult>
GoogleMapTilesRasterOverlay::createTileProvider(
const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
IntrusivePointer<const RasterOverlay> pOwner) const {
pOwner = pOwner ? pOwner : this;
const CreateRasterOverlayTileProviderParameters& parameters) const {
if (this->_newSessionParameters) {
return this->createNewSession(
asyncSystem,
pAssetAccessor,
pCreditSystem,
pPrepareRendererResources,
pLogger,
pOwner);
return this->createNewSession(parameters);
} else if (this->_existingSession) {
const GoogleMapTilesExistingSession& session = *this->_existingSession;
IntrusivePointer pTileProvider =
new GoogleMapTilesRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
std::nullopt,
pPrepareRendererResources,
pLogger,
this,
parameters,
session.apiBaseUrl,
session.session,
session.key,
@ -264,25 +229,20 @@ GoogleMapTilesRasterOverlay::createTileProvider(
return CreateTileProviderResult(pTileProvider);
});
} else {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = nullptr,
.message =
"GoogleMapTilesRasterOverlay is not configured with either "
"new session parameters or an existing session."}));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = nullptr,
.message =
"GoogleMapTilesRasterOverlay is not configured with either "
"new session parameters or an existing session."}));
}
}
Future<RasterOverlay::CreateTileProviderResult>
GoogleMapTilesRasterOverlay::createNewSession(
const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
CESIUM_ASSERT(this->_newSessionParameters);
Uri createSessionUri(
@ -334,36 +294,34 @@ GoogleMapTilesRasterOverlay::createNewSession(
writeJsonValue(requestPayload, writer);
std::vector<std::byte> requestPayloadBytes = writer.toBytes();
return pAssetAccessor
IntrusivePointer<const GoogleMapTilesRasterOverlay> thiz = this;
return parameters.externals.pAssetAccessor
->request(
asyncSystem,
parameters.externals.asyncSystem,
"POST",
std::string(createSessionUri.toString()),
{{"Content-Type", "application/json"}},
requestPayloadBytes)
.thenInMainThread(
[this,
asyncSystem,
pAssetAccessor,
pCreditSystem,
pPrepareRendererResources,
pLogger,
pOwner](std::shared_ptr<IAssetRequest>&& pRequest)
[thiz, parameters](std::shared_ptr<IAssetRequest>&& pRequest)
-> Future<CreateTileProviderResult> {
const IAssetResponse* pResponse = pRequest->response();
if (!pResponse) {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"No response received from Google Map Tiles API "
"createSession service."}));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"No response received from Google Map Tiles API "
"createSession service."}));
}
if (pResponse->statusCode() < 200 ||
pResponse->statusCode() >= 300) {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
return parameters.externals.asyncSystem.createResolvedFuture<
CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
@ -382,7 +340,8 @@ GoogleMapTilesRasterOverlay::createNewSession(
errorList.errors = std::move(response.errors);
errorList.warnings = std::move(response.warnings);
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
return parameters.externals.asyncSystem.createResolvedFuture<
CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
@ -392,13 +351,14 @@ GoogleMapTilesRasterOverlay::createNewSession(
}
if (!response.value->isObject()) {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service was not a JSON object."}));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service was not a JSON object."}));
}
const JsonValue::Object& responseObject =
@ -406,42 +366,45 @@ GoogleMapTilesRasterOverlay::createNewSession(
auto it = responseObject.find("session");
if (it == responseObject.end() || !it->second.isString()) {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service did not contain a valid "
"'session' property."}));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service did not contain a valid "
"'session' property."}));
}
std::string session = it->second.getString();
it = responseObject.find("tileWidth");
if (it == responseObject.end() || !it->second.isNumber()) {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service did not contain a valid "
"'tileWidth' property."}));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service did not contain a valid "
"'tileWidth' property."}));
}
int32_t tileWidth = it->second.getSafeNumberOrDefault<int32_t>(256);
it = responseObject.find("tileHeight");
if (it == responseObject.end() || !it->second.isNumber()) {
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service did not contain a valid "
"'tileHeight' property."}));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
.type = RasterOverlayLoadType::TileProvider,
.pRequest = pRequest,
.message =
"Response from Google Map Tiles API "
"createSession service did not contain a valid "
"'tileHeight' property."}));
}
int32_t tileHeight =
@ -449,16 +412,11 @@ GoogleMapTilesRasterOverlay::createNewSession(
IntrusivePointer pTileProvider =
new GoogleMapTilesRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
std::nullopt,
pPrepareRendererResources,
pLogger,
this->_newSessionParameters->apiBaseUrl,
thiz,
parameters,
thiz->_newSessionParameters->apiBaseUrl,
session,
this->_newSessionParameters->key,
thiz->_newSessionParameters->key,
maximumZoomLevel,
static_cast<uint32_t>(tileWidth),
static_cast<uint32_t>(tileHeight),
@ -494,14 +452,8 @@ QuadtreeTilingScheme createTilingScheme(
GoogleMapTilesRasterOverlayTileProvider::
GoogleMapTilesRasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<CesiumUtility::Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const std::string& apiBaseUrl,
const std::string& session,
const std::string& key,
@ -510,16 +462,11 @@ GoogleMapTilesRasterOverlayTileProvider::
uint32_t imageHeight,
bool showLogo)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
WebMercatorProjection(pOwner->getOptions().ellipsoid),
createTilingScheme(pOwner),
createRectangle(pOwner),
pCreator,
parameters,
WebMercatorProjection(pCreator->getOptions().ellipsoid),
createTilingScheme(pCreator),
createRectangle(pCreator),
0,
maximumLevel,
imageWidth,
@ -527,26 +474,14 @@ GoogleMapTilesRasterOverlayTileProvider::
_apiBaseUrl(apiBaseUrl),
_session(session),
_key(key),
_googleCredit(),
_credits(),
_availableTiles(createTilingScheme(pOwner), maximumLevel),
_availableAvailability(createTilingScheme(pOwner), maximumLevel) {
if (pCreditSystem && showLogo) {
this->_googleCredit =
pCreditSystem->createCredit(GOOGLE_MAPS_LOGO_HTML, true);
}
}
void GoogleMapTilesRasterOverlayTileProvider::addCredits(
CesiumUtility::CreditReferencer& creditReferencer) noexcept {
QuadtreeRasterOverlayTileProvider::addCredits(creditReferencer);
if (this->_googleCredit) {
creditReferencer.addCreditReference(*this->_googleCredit);
}
if (this->_credits) {
creditReferencer.addCreditReference(*this->_credits);
_availableTiles(createTilingScheme(pCreator), maximumLevel),
_availableAvailability(createTilingScheme(pCreator), maximumLevel) {
if (parameters.externals.pCreditSystem && showLogo) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
GOOGLE_MAPS_LOGO_HTML,
true));
}
}
@ -711,7 +646,6 @@ CesiumAsync::Future<rapidjson::Document> fetchViewportData(
pRequest->url(),
document.GetParseError(),
document.GetErrorOffset());
return document;
}
return document;
@ -889,7 +823,7 @@ Future<void> GoogleMapTilesRasterOverlayTileProvider::loadCredits() {
uint32_t(i),
Rectangle(-180.0, -90.0, 180.0, 90.0))
.thenInMainThread([](rapidjson::Document&& document) {
if (document.HasParseError()) {
if (document.HasParseError() || !document.IsObject()) {
return std::string();
}
@ -978,7 +912,10 @@ Future<void> GoogleMapTilesRasterOverlayTileProvider::loadCredits() {
}
// Create a single credit from this giant string.
thiz->_credits = thiz->getCreditSystem()->createCredit(joined, false);
thiz->getCredits().emplace_back(thiz->getCreditSystem()->createCredit(
thiz->getCreditSource(),
joined,
thiz->getOwner().getOptions().showCreditsOnScreen));
});
}

View File

@ -3,7 +3,9 @@
#include <CesiumAsync/IAssetResponse.h>
#include <CesiumAsync/NetworkAssetDescriptor.h>
#include <CesiumAsync/SharedAssetDepot.h>
#include <CesiumRasterOverlays/AzureMapsRasterOverlay.h>
#include <CesiumRasterOverlays/BingMapsRasterOverlay.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/GoogleMapTilesRasterOverlay.h>
#include <CesiumRasterOverlays/IonRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
@ -23,7 +25,6 @@
#include <fmt/format.h>
#include <nonstd/expected.hpp>
#include <rapidjson/document.h>
#include <spdlog/logger.h>
#include <spdlog/spdlog.h>
#include <chrono>
@ -63,8 +64,8 @@ public:
expected<AggregatedTileProviderSuccess, RasterOverlayLoadFailureDetails>;
struct CreateTileProvider {
IntrusivePointer<const RasterOverlay> pOwner;
RasterOverlayExternals externals;
IntrusivePointer<const IonRasterOverlay> pCreator;
CreateRasterOverlayTileProviderParameters parameters;
SharedFuture<AggregatedTileProviderResult>
operator()(const IntrusivePointer<ExternalAssetEndpoint>& pEndpoint);
@ -74,12 +75,14 @@ public:
DerivedValue<IntrusivePointer<ExternalAssetEndpoint>, CreateTileProvider>;
TileProvider(
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const IntrusivePointer<RasterOverlayTileProvider>& pInitialProvider,
const NetworkAssetDescriptor& descriptor,
TileProviderFactoryType&& tileProviderFactory)
: RasterOverlayTileProvider(
&pInitialProvider->getOwner(),
pInitialProvider->getExternals(),
pCreator,
parameters,
pInitialProvider->getProjection(),
pInitialProvider->getCoverageRectangle()),
_descriptor(descriptor),
@ -87,20 +90,26 @@ public:
_credits() {}
static CesiumAsync::Future<RasterOverlay::CreateTileProviderResult> create(
const RasterOverlayExternals& externals,
const NetworkAssetDescriptor& descriptor,
const IntrusivePointer<const RasterOverlay>& pOwner) {
const IntrusivePointer<const IonRasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const NetworkAssetDescriptor& descriptor) {
auto pFactory = std::make_unique<TileProvider::TileProviderFactoryType>(
TileProvider::TileProviderFactoryType(TileProvider::CreateTileProvider{
.pOwner = pOwner,
.externals = externals}));
return TileProvider::getTileProvider(externals, descriptor, *pFactory)
.pCreator = pCreator,
.parameters = parameters}));
return TileProvider::getTileProvider(
parameters.externals,
descriptor,
*pFactory)
.thenInMainThread(
[descriptor, pFactory = std::move(pFactory)](
[descriptor, pFactory = std::move(pFactory), pCreator, parameters](
AggregatedTileProviderResult&& result) mutable
-> CreateTileProviderResult {
if (result) {
IntrusivePointer p = new TileProvider(
pCreator,
parameters,
result.value().pAggregated,
descriptor,
std::move(*pFactory));
@ -278,15 +287,7 @@ void IonRasterOverlay::setAssetOptions(
Future<RasterOverlay::CreateTileProviderResult>
IonRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
pOwner = pOwner ? pOwner : this;
const CreateRasterOverlayTileProviderParameters& parameters) const {
NetworkAssetDescriptor descriptor;
descriptor.url = this->_overlayUrl;
@ -304,15 +305,87 @@ IonRasterOverlay::createTileProvider(
descriptor.url = uri.toString();
}
RasterOverlayExternals externals{
.pAssetAccessor = pAssetAccessor,
.pPrepareRendererResources = pPrepareRendererResources,
.asyncSystem = asyncSystem,
.pCreditSystem = pCreditSystem,
.pLogger = pLogger,
};
// The aggregated tile provider should be owned by this overlay and use a
// common credit source. But the conditions account for the possibility that
// the IonRasterOverlay itself is aggregated by some other overlay.
CreateRasterOverlayTileProviderParameters parametersCopy = parameters;
if (parametersCopy.pOwner == nullptr) {
parametersCopy.pOwner = this;
}
if (parametersCopy.pCreditSource == nullptr) {
parametersCopy.pCreditSource =
std::make_shared<CreditSource>(parameters.externals.pCreditSystem);
}
return TileProvider::create(externals, descriptor, pOwner);
return TileProvider::create(this, parametersCopy, descriptor);
}
void IonRasterOverlay::ExternalAssetEndpoint::parseAzure2DOptions(
const rapidjson::Document& ionResponse) {
const auto optionsIt = ionResponse.FindMember("options");
if (optionsIt == ionResponse.MemberEnd() || !optionsIt->value.IsObject()) {
return;
}
const auto& ionOptions = optionsIt->value;
ExternalAssetEndpoint::Azure2D& azure2D =
this->options.emplace<ExternalAssetEndpoint::Azure2D>();
azure2D.url = JsonHelpers::getStringOrDefault(ionOptions, "url", "");
azure2D.tilesetId =
JsonHelpers::getStringOrDefault(ionOptions, "tilesetId", "");
azure2D.key =
JsonHelpers::getStringOrDefault(ionOptions, "subscription-key", "");
}
void IonRasterOverlay::ExternalAssetEndpoint::parseGoogle2DOptions(
const rapidjson::Document& ionResponse) {
const auto optionsIt = ionResponse.FindMember("options");
if (optionsIt == ionResponse.MemberEnd() || !optionsIt->value.IsObject()) {
return;
}
const auto& ionOptions = optionsIt->value;
ExternalAssetEndpoint::Google2D& google2D =
this->options.emplace<ExternalAssetEndpoint::Google2D>();
google2D.url =
JsonHelpers::getStringOrDefault(ionOptions, "url", google2D.url);
google2D.key = JsonHelpers::getStringOrDefault(ionOptions, "key", "");
google2D.session = JsonHelpers::getStringOrDefault(ionOptions, "session", "");
google2D.expiry = JsonHelpers::getStringOrDefault(ionOptions, "expiry", "");
google2D.tileWidth =
JsonHelpers::getUint32OrDefault(ionOptions, "tileWidth", 256);
google2D.tileHeight =
JsonHelpers::getUint32OrDefault(ionOptions, "tileHeight", 256);
google2D.imageFormat = JsonHelpers::getStringOrDefault(
ionOptions,
"imageFormat",
GoogleMapTilesImageFormat::jpeg);
}
void IonRasterOverlay::ExternalAssetEndpoint::parseBingOptions(
const rapidjson::Document& ionResponse) {
const auto optionsIt = ionResponse.FindMember("options");
if (optionsIt == ionResponse.MemberEnd() || !optionsIt->value.IsObject()) {
return;
}
const auto& ionOptions = optionsIt->value;
ExternalAssetEndpoint::Bing& bing =
this->options.emplace<ExternalAssetEndpoint::Bing>();
bing.url = JsonHelpers::getStringOrDefault(ionOptions, "url", "");
bing.key = JsonHelpers::getStringOrDefault(ionOptions, "key", "");
bing.mapStyle =
JsonHelpers::getStringOrDefault(ionOptions, "mapStyle", "AERIAL");
bing.culture = JsonHelpers::getStringOrDefault(ionOptions, "culture", "");
}
void IonRasterOverlay::ExternalAssetEndpoint::parseTileMapServiceOptions(
const rapidjson::Document& ionResponse) {
ExternalAssetEndpoint::TileMapService& tileMapService =
this->options.emplace<ExternalAssetEndpoint::TileMapService>();
tileMapService.url = JsonHelpers::getStringOrDefault(ionResponse, "url", "");
tileMapService.accessToken =
JsonHelpers::getStringOrDefault(ionResponse, "accessToken", "");
}
/* static */ CesiumUtility::IntrusivePointer<IonRasterOverlay::EndpointDepot>
@ -393,36 +466,25 @@ IonRasterOverlay::getEndpointCache() {
"externalType",
"unknown");
if (endpoint.externalType == "BING") {
const auto optionsIt = response.FindMember("options");
if (optionsIt == response.MemberEnd() ||
!optionsIt->value.IsObject()) {
if (endpoint.externalType == "AZURE_MAPS") {
endpoint.parseAzure2DOptions(response);
if (!std::holds_alternative<ExternalAssetEndpoint::Azure2D>(
endpoint.options)) {
endpoint.pRequestThatFailed = std::move(pRequest);
return ResultPointer<ExternalAssetEndpoint>(
new ExternalAssetEndpoint(std::move(endpoint)),
ErrorList::error(fmt::format(
"Cesium ion Bing Maps raster overlay metadata "
"Cesium ion Azure Maps raster overlay metadata "
"response does not contain 'options' or it is "
"not an object.")));
}
const auto& options = optionsIt->value;
ExternalAssetEndpoint::Bing& bing =
endpoint.options.emplace<ExternalAssetEndpoint::Bing>();
bing.url =
JsonHelpers::getStringOrDefault(options, "url", "");
bing.key =
JsonHelpers::getStringOrDefault(options, "key", "");
bing.mapStyle = JsonHelpers::getStringOrDefault(
options,
"mapStyle",
"AERIAL");
bing.culture =
JsonHelpers::getStringOrDefault(options, "culture", "");
} else if (endpoint.externalType == "GOOGLE_2D_MAPS") {
const auto optionsIt = response.FindMember("options");
if (optionsIt == response.MemberEnd() ||
!optionsIt->value.IsObject()) {
endpoint.parseGoogle2DOptions(response);
if (!std::holds_alternative<
ExternalAssetEndpoint::Google2D>(
endpoint.options)) {
endpoint.pRequestThatFailed = std::move(pRequest);
return ResultPointer<ExternalAssetEndpoint>(
new ExternalAssetEndpoint(std::move(endpoint)),
@ -431,45 +493,21 @@ IonRasterOverlay::getEndpointCache() {
"metadata response does not contain 'options' or "
"it is not an object.")));
}
} else if (endpoint.externalType == "BING") {
endpoint.parseBingOptions(response);
const auto& options = optionsIt->value;
ExternalAssetEndpoint::Google2D& google2D =
endpoint.options
.emplace<ExternalAssetEndpoint::Google2D>();
google2D.url = JsonHelpers::getStringOrDefault(
options,
"url",
google2D.url);
google2D.key =
JsonHelpers::getStringOrDefault(options, "key", "");
google2D.session =
JsonHelpers::getStringOrDefault(options, "session", "");
google2D.expiry =
JsonHelpers::getStringOrDefault(options, "expiry", "");
google2D.tileWidth = JsonHelpers::getUint32OrDefault(
options,
"tileWidth",
256);
google2D.tileHeight = JsonHelpers::getUint32OrDefault(
options,
"tileHeight",
256);
google2D.imageFormat = JsonHelpers::getStringOrDefault(
options,
"imageFormat",
GoogleMapTilesImageFormat::jpeg);
if (!std::holds_alternative<ExternalAssetEndpoint::Bing>(
endpoint.options)) {
endpoint.pRequestThatFailed = std::move(pRequest);
return ResultPointer<ExternalAssetEndpoint>(
new ExternalAssetEndpoint(std::move(endpoint)),
ErrorList::error(fmt::format(
"Cesium ion Bing Maps raster overlay metadata "
"response does not contain 'options' or it is "
"not an object.")));
}
} else {
ExternalAssetEndpoint::TileMapService& tileMapService =
endpoint.options
.emplace<ExternalAssetEndpoint::TileMapService>();
tileMapService.url =
JsonHelpers::getStringOrDefault(response, "url", "");
tileMapService.accessToken =
JsonHelpers::getStringOrDefault(
response,
"accessToken",
"");
endpoint.parseTileMapServiceOptions(response);
}
const auto attributionsIt =
@ -539,7 +577,7 @@ IonRasterOverlay::TileProvider::CreateTileProvider::operator()(
const IntrusivePointer<ExternalAssetEndpoint>& pEndpoint) {
if (pEndpoint == nullptr ||
std::holds_alternative<std::monostate>(pEndpoint->options)) {
return this->externals.asyncSystem
return this->parameters.externals.asyncSystem
.createResolvedFuture<AggregatedTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
RasterOverlayLoadType::CesiumIon,
@ -549,25 +587,27 @@ IonRasterOverlay::TileProvider::CreateTileProvider::operator()(
}
IntrusivePointer<RasterOverlay> pOverlay = nullptr;
if (pEndpoint->externalType == "BING") {
CESIUM_ASSERT(std::holds_alternative<ExternalAssetEndpoint::Bing>(
if (pEndpoint->externalType == "AZURE_MAPS") {
CESIUM_ASSERT(std::holds_alternative<ExternalAssetEndpoint::Azure2D>(
pEndpoint->options));
ExternalAssetEndpoint::Bing& bing =
std::get<ExternalAssetEndpoint::Bing>(pEndpoint->options);
pOverlay = new BingMapsRasterOverlay(
this->pOwner->getName(),
bing.url,
bing.key,
bing.mapStyle,
bing.culture,
this->pOwner->getOptions());
ExternalAssetEndpoint::Azure2D& azure2D =
std::get<ExternalAssetEndpoint::Azure2D>(pEndpoint->options);
pOverlay = new AzureMapsRasterOverlay(
this->pCreator->getName(),
AzureMapsSessionParameters{
.key = azure2D.key,
.tilesetId = azure2D.tilesetId,
.showLogo = false,
.apiBaseUrl = azure2D.url,
},
this->pCreator->getOptions());
} else if (pEndpoint->externalType == "GOOGLE_2D_MAPS") {
CESIUM_ASSERT(std::holds_alternative<ExternalAssetEndpoint::Google2D>(
pEndpoint->options));
ExternalAssetEndpoint::Google2D& google2D =
std::get<ExternalAssetEndpoint::Google2D>(pEndpoint->options);
pOverlay = new GoogleMapTilesRasterOverlay(
this->pOwner->getName(),
this->pCreator->getName(),
GoogleMapTilesExistingSession{
.key = google2D.key,
.session = google2D.session,
@ -578,43 +618,49 @@ IonRasterOverlay::TileProvider::CreateTileProvider::operator()(
.showLogo = false,
.apiBaseUrl = google2D.url,
},
this->pOwner->getOptions());
this->pCreator->getOptions());
} else if (pEndpoint->externalType == "BING") {
CESIUM_ASSERT(std::holds_alternative<ExternalAssetEndpoint::Bing>(
pEndpoint->options));
ExternalAssetEndpoint::Bing& bing =
std::get<ExternalAssetEndpoint::Bing>(pEndpoint->options);
pOverlay = new BingMapsRasterOverlay(
this->pCreator->getName(),
bing.url,
bing.key,
bing.mapStyle,
bing.culture,
this->pCreator->getOptions());
} else {
CESIUM_ASSERT(std::holds_alternative<ExternalAssetEndpoint::TileMapService>(
pEndpoint->options));
ExternalAssetEndpoint::TileMapService& tileMapService =
std::get<ExternalAssetEndpoint::TileMapService>(pEndpoint->options);
pOverlay = new TileMapServiceRasterOverlay(
this->pOwner->getName(),
this->pCreator->getName(),
tileMapService.url,
std::vector<CesiumAsync::IAssetAccessor::THeader>{std::make_pair(
"Authorization",
"Bearer " + tileMapService.accessToken)},
TileMapServiceRasterOverlayOptions(),
this->pOwner->getOptions());
this->pCreator->getOptions());
}
std::vector<Credit> credits;
if (this->externals.pCreditSystem) {
if (this->parameters.externals.pCreditSystem) {
credits.reserve(pEndpoint->attributions.size());
for (const AssetEndpointAttribution& attribution :
pEndpoint->attributions) {
credits.emplace_back(this->externals.pCreditSystem->createCredit(
attribution.html,
!attribution.collapsible ||
this->pOwner->getOptions().showCreditsOnScreen));
credits.emplace_back(
this->parameters.externals.pCreditSystem->createCredit(
attribution.html,
!attribution.collapsible ||
this->pCreator->getOptions().showCreditsOnScreen));
}
}
return pOverlay
->createTileProvider(
this->externals.asyncSystem,
this->externals.pAssetAccessor,
this->externals.pCreditSystem,
this->externals.pPrepareRendererResources,
this->externals.pLogger,
this->pOwner)
return pOverlay->createTileProvider(parameters)
.thenImmediately([credits = std::move(credits)](
CreateTileProviderResult&& result) mutable {
if (result) {
@ -629,5 +675,4 @@ IonRasterOverlay::TileProvider::CreateTileProvider::operator()(
})
.share();
}
} // namespace CesiumRasterOverlays

View File

@ -20,7 +20,6 @@
#include <glm/common.hpp>
#include <glm/exponential.hpp>
#include <glm/ext/vector_double2.hpp>
#include <spdlog/logger.h>
#include <algorithm>
#include <cstddef>
@ -52,14 +51,8 @@ constexpr double pixelTolerance = 0.01;
namespace CesiumRasterOverlays {
QuadtreeRasterOverlayTileProvider::QuadtreeRasterOverlayTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
@ -68,13 +61,8 @@ QuadtreeRasterOverlayTileProvider::QuadtreeRasterOverlayTileProvider(
uint32_t imageWidth,
uint32_t imageHeight) noexcept
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
coverageRectangle),
_minimumLevel(minimumLevel),

View File

@ -7,6 +7,7 @@
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayExternals.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
@ -19,7 +20,6 @@
#include <spdlog/spdlog.h>
#include <exception>
#include <memory>
#include <optional>
#include <string>
#include <utility>
@ -32,19 +32,12 @@ namespace {
class PlaceholderTileProvider : public RasterOverlayTileProvider {
public:
PlaceholderTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Ellipsoid& ellipsoid) noexcept
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
std::nullopt,
nullptr,
spdlog::default_logger(),
pCreator,
parameters,
CesiumGeospatial::GeographicProjection(ellipsoid),
CesiumGeometry::Rectangle()) {}
@ -93,14 +86,13 @@ CesiumUtility::IntrusivePointer<ActivatedRasterOverlay> RasterOverlay::activate(
IntrusivePointer<ActivatedRasterOverlay> pResult =
new ActivatedRasterOverlay(externals, this, ellipsoid);
CreateRasterOverlayTileProviderParameters parameters{
.externals = externals,
.pOwner = nullptr,
.pCreditSource = nullptr};
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult> future =
this->createTileProvider(
externals.asyncSystem,
externals.pAssetAccessor,
externals.pCreditSystem,
externals.pPrepareRendererResources,
externals.pLogger,
this);
this->createTileProvider(parameters);
// This continuation, by capturing pResult, keeps the instance from being
// destroyed. But it does not keep the RasterOverlayCollection itself alive.
@ -115,7 +107,7 @@ CesiumUtility::IntrusivePointer<ActivatedRasterOverlay> RasterOverlay::activate(
"Error while creating tile provider: {0}",
e.what())});
})
.thenInMainThread([pResult, externals](
.thenInMainThread([pResult, parameters](
RasterOverlay::CreateTileProviderResult&& result) {
IntrusivePointer<RasterOverlayTileProvider> pProvider = nullptr;
if (result) {
@ -124,7 +116,9 @@ CesiumUtility::IntrusivePointer<ActivatedRasterOverlay> RasterOverlay::activate(
// Report error creating the tile provider.
const RasterOverlayLoadFailureDetails& failureDetails =
result.error();
SPDLOG_LOGGER_ERROR(externals.pLogger, failureDetails.message);
SPDLOG_LOGGER_ERROR(
parameters.externals.pLogger,
failureDetails.message);
if (pResult->getOverlay().getOptions().loadErrorCallback) {
pResult->getOverlay().getOptions().loadErrorCallback(
failureDetails);
@ -134,7 +128,7 @@ CesiumUtility::IntrusivePointer<ActivatedRasterOverlay> RasterOverlay::activate(
// all.
pProvider = new EmptyRasterOverlayTileProvider(
&pResult->getOverlay(),
externals.asyncSystem);
parameters);
}
pResult->setTileProvider(pProvider);
@ -149,8 +143,9 @@ RasterOverlay::createPlaceholder(
const CesiumGeospatial::Ellipsoid& ellipsoid) const {
return new PlaceholderTileProvider(
this,
externals.asyncSystem,
externals.pAssetAccessor,
nullptr,
CreateRasterOverlayTileProviderParameters{
.externals = externals,
.pOwner = nullptr,
.pCreditSource = nullptr},
ellipsoid);
}

View File

@ -37,11 +37,14 @@ RasterOverlayTile::RasterOverlayTile(
RasterOverlayTile::~RasterOverlayTile() {
this->_pActivatedOverlay->removeTile(this);
RasterOverlayTileProvider& tileProvider =
*this->_pActivatedOverlay->getTileProvider();
RasterOverlayTileProvider* pTileProvider =
this->_pActivatedOverlay->getTileProvider();
if (!pTileProvider)
return;
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources = tileProvider.getPrepareRendererResources();
pPrepareRendererResources = pTileProvider->getPrepareRendererResources();
if (pPrepareRendererResources) {
void* pLoadThreadResult =

View File

@ -6,11 +6,13 @@
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGltfReader/ImageDecoder.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayExternals.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/CreditReferencer.h>
#include <CesiumUtility/ErrorList.h>
#include <CesiumUtility/IntrusivePointer.h>
@ -37,39 +39,24 @@ using namespace CesiumUtility;
namespace CesiumRasterOverlays {
RasterOverlayTileProvider::RasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const RasterOverlayExternals& externals,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::Rectangle& coverageRectangle) noexcept
: _pOwner(const_intrusive_cast<RasterOverlay>(pOwner)),
_externals(externals),
_credit(),
: _pOwner(const_intrusive_cast<RasterOverlay>(
parameters.pOwner ? parameters.pOwner : pCreator)),
_externals(parameters.externals),
_credits(),
_projection(projection),
_coverageRectangle(coverageRectangle),
_destructionCompleteDetails() {}
RasterOverlayTileProvider::RasterOverlayTileProvider(
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumGeospatial::Projection& projection,
const Rectangle& coverageRectangle) noexcept
: RasterOverlayTileProvider(
pOwner,
RasterOverlayExternals{
.pAssetAccessor = pAssetAccessor,
.pPrepareRendererResources = pPrepareRendererResources,
.asyncSystem = asyncSystem,
.pCreditSystem = pCreditSystem,
.pLogger = pLogger},
projection,
coverageRectangle) {
this->_credit = credit;
_destructionCompleteDetails(),
_pCreditSource(
parameters.pCreditSource ? parameters.pCreditSource
: std::make_shared<CreditSource>(
parameters.externals.pCreditSystem)) {
CESIUM_ASSERT(
this->_pCreditSource->getCreditSystem() ==
parameters.externals.pCreditSystem.get());
}
RasterOverlayTileProvider::~RasterOverlayTileProvider() noexcept {
@ -139,15 +126,31 @@ RasterOverlayTileProvider::getCoverageRectangle() const noexcept {
return this->_coverageRectangle;
}
const std::optional<CesiumUtility::Credit>&
RasterOverlayTileProvider::getCredit() const noexcept {
return _credit;
const CesiumUtility::CreditSource&
RasterOverlayTileProvider::getCreditSource() const noexcept {
return *this->_pCreditSource;
}
std::vector<CesiumUtility::Credit>&
RasterOverlayTileProvider::getCredits() noexcept {
return this->_credits;
}
const std::vector<CesiumUtility::Credit>&
RasterOverlayTileProvider::getCredits() const noexcept {
return this->_credits;
}
void RasterOverlayTileProvider::addCredits(
CreditReferencer& creditReferencer) noexcept {
if (this->_credit) {
creditReferencer.addCreditReference(*this->_credit);
CESIUM_ASSERT(
creditReferencer.getCreditSystem().get() ==
this->_pCreditSource->getCreditSystem());
for (const CesiumUtility::Credit& credit : this->_credits) {
CESIUM_ASSERT(
this->getCreditSystem()->getCreditSource(credit) ==
this->_pCreditSource.get());
creditReferencer.addCreditReference(credit);
}
}

View File

@ -591,7 +591,7 @@ RasterOverlayUtilities::upsampleGltfForRasterOverlays(
// We're assuming here that nothing references primitives by index, so we
// can remove them without any drama.
if (!keep) {
mesh.primitives.erase(mesh.primitives.begin() + int64_t(i));
mesh.primitives.erase(mesh.primitives.begin() + ptrdiff_t(i));
--i;
}
containsPrimitives |= !mesh.primitives.empty();

View File

@ -5,22 +5,20 @@
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGltf/ImageAsset.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterizedPolygonsOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <glm/common.hpp>
#include <glm/ext/vector_double2.hpp>
#include <glm/geometric.hpp>
#include <spdlog/fwd.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>
@ -177,23 +175,14 @@ private:
public:
RasterizedPolygonsTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const std::vector<CartographicPolygon>& polygons,
bool invertSelection)
: RasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
nullptr,
std::nullopt,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
// computeCoverageRectangle(projection, polygons)),
projectRectangleSimple(
@ -252,27 +241,17 @@ RasterizedPolygonsOverlay::~RasterizedPolygonsOverlay() = default;
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult>
RasterizedPolygonsOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& /*pCreditSystem*/,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
pOwner = pOwner ? pOwner : this;
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
IntrusivePointer<RasterOverlayTileProvider>(
new RasterizedPolygonsTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pPrepareRendererResources,
pLogger,
this->_projection,
this->_polygons,
this->_invertSelection)));
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(
IntrusivePointer<RasterOverlayTileProvider>(
new RasterizedPolygonsTileProvider(
this,
parameters,
this->_projection,
this->_polygons,
this->_invertSelection)));
}
} // namespace CesiumRasterOverlays

View File

@ -9,22 +9,19 @@
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGeospatial/WebMercatorProjection.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/TileMapServiceRasterOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/ErrorList.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Uri.h>
#include <glm/common.hpp>
#include <nonstd/expected.hpp>
#include <spdlog/logger.h>
#include <spdlog/spdlog.h>
#include <tinyxml2.h>
#include <cstddef>
@ -88,14 +85,9 @@ class TileMapServiceTileProvider final
: public QuadtreeRasterOverlayTileProvider {
public:
TileMapServiceTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
std::optional<std::string> credit,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
@ -108,13 +100,8 @@ public:
uint32_t maximumLevel,
const std::vector<TileMapServiceTileset>& tileSets)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
tilingScheme,
coverageRectangle,
@ -125,7 +112,15 @@ public:
_url(url),
_headers(headers),
_fileExtension(fileExtension),
_tileSets(tileSets) {}
_tileSets(tileSets) {
if (parameters.externals.pCreditSystem && credit) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
*credit,
pCreator->getOptions().showCreditsOnScreen));
}
}
virtual ~TileMapServiceTileProvider() = default;
@ -301,37 +296,19 @@ Future<GetXmlDocumentResult> getXmlDocument(
Future<RasterOverlay::CreateTileProviderResult>
TileMapServiceRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
std::string xmlUrl = this->_url;
pOwner = pOwner ? pOwner : this;
IntrusivePointer<const TileMapServiceRasterOverlay> thiz = this;
std::optional<Credit> credit = std::nullopt;
if (pCreditSystem && this->_options.credit) {
credit = pCreditSystem->createCredit(
*this->_options.credit,
pOwner->getOptions().showCreditsOnScreen);
}
return getXmlDocument(asyncSystem, pAssetAccessor, xmlUrl, this->_headers)
return getXmlDocument(
parameters.externals.asyncSystem,
parameters.externals.pAssetAccessor,
xmlUrl,
this->_headers)
.thenInMainThread(
[pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
options = this->_options,
url = this->_url,
headers = this->_headers](
GetXmlDocumentResult&& xml) -> CreateTileProviderResult {
[thiz,
parameters](GetXmlDocumentResult&& xml) -> CreateTileProviderResult {
if (!xml) {
return nonstd::make_unexpected(std::move(xml).error());
}
@ -341,11 +318,11 @@ TileMapServiceRasterOverlay::createTileProvider(
tinyxml2::XMLElement* pTileFormat =
pRoot->FirstChildElement("TileFormat");
std::string fileExtension = options.fileExtension.value_or(
std::string fileExtension = thiz->_options.fileExtension.value_or(
getAttributeString(pTileFormat, "extension").value_or("png"));
uint32_t tileWidth = options.tileWidth.value_or(
uint32_t tileWidth = thiz->_options.tileWidth.value_or(
getAttributeUint32(pTileFormat, "width").value_or(256));
uint32_t tileHeight = options.tileHeight.value_or(
uint32_t tileHeight = thiz->_options.tileHeight.value_or(
getAttributeUint32(pTileFormat, "height").value_or(256));
uint32_t minimumLevel = std::numeric_limits<uint32_t>::max();
@ -377,7 +354,7 @@ TileMapServiceRasterOverlay::createTileProvider(
}
const CesiumGeospatial::Ellipsoid& ellipsoid =
pOwner->getOptions().ellipsoid;
thiz->getOptions().ellipsoid;
CesiumGeospatial::GlobeRectangle tilingSchemeRectangle =
CesiumGeospatial::GeographicProjection::MAXIMUM_GLOBE_RECTANGLE;
@ -386,8 +363,8 @@ TileMapServiceRasterOverlay::createTileProvider(
uint32_t rootTilesX = 1;
bool isRectangleInDegrees = false;
if (options.projection) {
projection = options.projection.value();
if (thiz->_options.projection) {
projection = thiz->_options.projection.value();
} else {
std::string projectionName =
getAttributeString(pTilesets, "profile").value_or("mercator");
@ -441,14 +418,14 @@ TileMapServiceRasterOverlay::createTileProvider(
minimumLevel = glm::min(minimumLevel, maximumLevel);
minimumLevel = options.minimumLevel.value_or(minimumLevel);
maximumLevel = options.maximumLevel.value_or(maximumLevel);
minimumLevel = thiz->_options.minimumLevel.value_or(minimumLevel);
maximumLevel = thiz->_options.maximumLevel.value_or(maximumLevel);
CesiumGeometry::Rectangle coverageRectangle =
projectRectangleSimple(projection, tilingSchemeRectangle);
if (options.coverageRectangle) {
coverageRectangle = options.coverageRectangle.value();
if (thiz->_options.coverageRectangle) {
coverageRectangle = thiz->_options.coverageRectangle.value();
} else {
tinyxml2::XMLElement* pBoundingBox =
pRoot->FirstChildElement("BoundingBox");
@ -486,31 +463,27 @@ TileMapServiceRasterOverlay::createTileProvider(
rootTilesX,
1);
std::string updatedUrl = url;
std::string updatedUrl = thiz->_url;
std::string urlPath = Uri::getPath(url);
std::string urlPath = Uri::getPath(thiz->_url);
if (!(urlPath.size() < 4)) {
if (urlPath.substr(urlPath.size() - 4, 4) != ".xml") {
if (urlPath[urlPath.size() - 1] != '/') {
urlPath += "/";
updatedUrl = Uri::setPath(url, urlPath);
updatedUrl = Uri::setPath(thiz->_url, urlPath);
}
}
}
return new TileMapServiceTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
thiz,
parameters,
thiz->_options.credit,
projection,
tilingScheme,
coverageRectangle,
updatedUrl,
headers,
thiz->_headers,
!fileExtension.empty() ? "." + fileExtension : fileExtension,
tileWidth,
tileHeight,
@ -520,63 +493,4 @@ TileMapServiceRasterOverlay::createTileProvider(
});
}
Future<void>
TileMapServiceRasterOverlay::refreshTileProviderWithNewUrlAndHeaders(
const IntrusivePointer<RasterOverlayTileProvider>& pProvider,
const std::optional<std::string>& newUrl,
const std::optional<std::vector<CesiumAsync::IAssetAccessor::THeader>>&
newHeaders) {
if (newUrl) {
this->_url = *newUrl;
}
if (newHeaders) {
this->_headers = *newHeaders;
}
return this
->createTileProvider(
pProvider->getAsyncSystem(),
pProvider->getAssetAccessor(),
pProvider->getCreditSystem(),
pProvider->getPrepareRendererResources(),
pProvider->getLogger(),
&pProvider->getOwner())
.thenInMainThread([pProvider](CreateTileProviderResult&& result) {
if (!result) {
SPDLOG_LOGGER_WARN(
pProvider->getLogger(),
"Could not refresh Bing Maps raster overlay with a new key: {}.",
result.error().message);
return;
}
// Use static_cast instead of dynamic_cast here to avoid the need for
// RTTI, and because we are certain of the type.
// NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast)
TileMapServiceTileProvider* pOld =
static_cast<TileMapServiceTileProvider*>(pProvider.get());
TileMapServiceTileProvider* pNew =
static_cast<TileMapServiceTileProvider*>(result->get());
// NOLINTEND(cppcoreguidelines-pro-type-static-cast-downcast)
if (pOld->getCoverageRectangle().getLowerLeft() !=
pNew->getCoverageRectangle().getLowerLeft() ||
pOld->getCoverageRectangle().getUpperRight() !=
pNew->getCoverageRectangle().getUpperRight() ||
pOld->getHeight() != pNew->getHeight() ||
pOld->getWidth() != pNew->getWidth() ||
pOld->getMinimumLevel() != pNew->getMinimumLevel() ||
pOld->getMaximumLevel() != pNew->getMaximumLevel() ||
pOld->getProjection() != pNew->getProjection()) {
SPDLOG_LOGGER_WARN(
pProvider->getLogger(),
"Could not refresh Tile Map Service raster overlay with a new "
"URL and request headers because some metadata properties "
"changed unexpectedly upon refresh.");
return;
}
pOld->update(*pNew);
});
}
} // namespace CesiumRasterOverlays

View File

@ -8,20 +8,16 @@
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGeospatial/WebMercatorProjection.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/UrlTemplateRasterOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Math.h>
#include <CesiumUtility/Uri.h>
#include <spdlog/logger.h>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
@ -39,14 +35,9 @@ class UrlTemplateRasterOverlayTileProvider final
: public QuadtreeRasterOverlayTileProvider {
public:
UrlTemplateRasterOverlayTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
std::optional<std::string> credit,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
@ -57,13 +48,8 @@ public:
uint32_t minimumLevel,
uint32_t maximumLevel)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
tilingScheme,
coverageRectangle,
@ -72,7 +58,15 @@ public:
width,
height),
_url(url),
_headers(headers) {}
_headers(headers) {
if (parameters.externals.pCreditSystem && credit) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
*credit,
pCreator->getOptions().showCreditsOnScreen));
}
}
virtual ~UrlTemplateRasterOverlayTileProvider() = default;
@ -137,24 +131,9 @@ private:
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult>
UrlTemplateRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
pOwner = pOwner ? pOwner : this;
std::optional<Credit> credit = std::nullopt;
if (pCreditSystem && this->_options.credit) {
credit = pCreditSystem->createCredit(
*this->_options.credit,
pOwner->getOptions().showCreditsOnScreen);
}
CesiumGeospatial::Projection projection = _options.projection.value_or(
CesiumGeospatial::WebMercatorProjection(pOwner->getOptions().ellipsoid));
const CreateRasterOverlayTileProviderParameters& parameters) const {
CesiumGeospatial::Projection projection = this->_options.projection.value_or(
CesiumGeospatial::WebMercatorProjection(this->getOptions().ellipsoid));
CesiumGeospatial::GlobeRectangle tilingSchemeRectangle =
CesiumGeospatial::WebMercatorProjection::MAXIMUM_GLOBE_RECTANGLE;
@ -165,34 +144,30 @@ UrlTemplateRasterOverlay::createTileProvider(
rootTilesX = 2;
}
CesiumGeometry::Rectangle coverageRectangle =
_options.coverageRectangle.value_or(
this->_options.coverageRectangle.value_or(
projectRectangleSimple(projection, tilingSchemeRectangle));
CesiumGeometry::QuadtreeTilingScheme tilingScheme =
_options.tilingScheme.value_or(CesiumGeometry::QuadtreeTilingScheme(
this->_options.tilingScheme.value_or(CesiumGeometry::QuadtreeTilingScheme(
coverageRectangle,
rootTilesX,
1));
return asyncSystem
return parameters.externals.asyncSystem
.createResolvedFuture<RasterOverlay::CreateTileProviderResult>(
new UrlTemplateRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
this,
parameters,
this->_options.credit,
projection,
tilingScheme,
coverageRectangle,
_url,
_headers,
_options.tileWidth,
_options.tileHeight,
_options.minimumLevel,
_options.maximumLevel));
this->_url,
this->_headers,
this->_options.tileWidth,
this->_options.tileHeight,
this->_options.minimumLevel,
this->_options.maximumLevel));
}
} // namespace CesiumRasterOverlays

View File

@ -8,21 +8,19 @@
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/WebMapServiceRasterOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Math.h>
#include <CesiumUtility/Uri.h>
#include <fmt/format.h>
#include <nonstd/expected.hpp>
#include <spdlog/logger.h>
#include <tinyxml2.h>
#include <cstddef>
@ -149,14 +147,9 @@ class WebMapServiceTileProvider final
: public QuadtreeRasterOverlayTileProvider {
public:
WebMapServiceTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
std::optional<std::string> credit,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
@ -170,13 +163,8 @@ public:
uint32_t minimumLevel,
uint32_t maximumLevel)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
tilingScheme,
coverageRectangle,
@ -188,7 +176,15 @@ public:
_headers(headers),
_version(version),
_layers(layers),
_format(format) {}
_format(format) {
if (parameters.externals.pCreditSystem && credit) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
*credit,
pCreator->getOptions().showCreditsOnScreen));
}
}
virtual ~WebMapServiceTileProvider() = default;
@ -268,13 +264,7 @@ WebMapServiceRasterOverlay::~WebMapServiceRasterOverlay() = default;
Future<RasterOverlay::CreateTileProviderResult>
WebMapServiceRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
std::string xmlUrlGetcapabilities =
CesiumUtility::Uri::substituteTemplateParameters(
@ -295,27 +285,15 @@ WebMapServiceRasterOverlay::createTileProvider(
return "{" + placeholder + "}";
});
pOwner = pOwner ? pOwner : this;
IntrusivePointer<const WebMapServiceRasterOverlay> thiz = this;
std::optional<Credit> credit = std::nullopt;
if (pCreditSystem && this->_options.credit) {
credit = pCreditSystem->createCredit(
*this->_options.credit,
pOwner->getOptions().showCreditsOnScreen);
}
return pAssetAccessor->get(asyncSystem, xmlUrlGetcapabilities, this->_headers)
return parameters.externals.pAssetAccessor
->get(
parameters.externals.asyncSystem,
xmlUrlGetcapabilities,
this->_headers)
.thenInMainThread(
[pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
options = this->_options,
url = this->_baseUrl,
headers = this->_headers](std::shared_ptr<IAssetRequest>&& pRequest)
[thiz, parameters](std::shared_ptr<IAssetRequest>&& pRequest)
-> CreateTileProviderResult {
const IAssetResponse* pResponse = pRequest->response();
if (!pResponse) {
@ -347,15 +325,18 @@ WebMapServiceRasterOverlay::createTileProvider(
"element."});
}
const WebMapServiceRasterOverlayOptions& wmsOptions =
thiz->_options;
std::string validationError;
if (!validateCapabilities(pRoot, options, validationError)) {
if (!validateCapabilities(pRoot, wmsOptions, validationError)) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
RasterOverlayLoadType::TileProvider,
std::move(pRequest),
validationError});
}
const Ellipsoid& ellipsoid = pOwner->getOptions().ellipsoid;
const Ellipsoid& ellipsoid = thiz->getOptions().ellipsoid;
const auto projection =
CesiumGeospatial::GeographicProjection(ellipsoid);
@ -374,25 +355,24 @@ WebMapServiceRasterOverlay::createTileProvider(
rootTilesY);
return new WebMapServiceTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
thiz,
parameters,
wmsOptions.credit,
projection,
tilingScheme,
coverageRectangle,
url,
headers,
options.version,
options.layers,
options.format,
options.tileWidth < 1 ? 1 : uint32_t(options.tileWidth),
options.tileHeight < 1 ? 1 : uint32_t(options.tileHeight),
options.minimumLevel < 0 ? 0 : uint32_t(options.minimumLevel),
options.maximumLevel < 0 ? 0 : uint32_t(options.maximumLevel));
thiz->_baseUrl,
thiz->_headers,
wmsOptions.version,
wmsOptions.layers,
wmsOptions.format,
wmsOptions.tileWidth < 1 ? 1 : uint32_t(wmsOptions.tileWidth),
wmsOptions.tileHeight < 1 ? 1 : uint32_t(wmsOptions.tileHeight),
wmsOptions.minimumLevel < 0 ? 0
: uint32_t(wmsOptions.minimumLevel),
wmsOptions.maximumLevel < 0
? 0
: uint32_t(wmsOptions.maximumLevel));
});
}

View File

@ -7,24 +7,21 @@
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGeospatial/WebMercatorProjection.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/WebMapTileServiceRasterOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Uri.h>
#include <nonstd/expected.hpp>
#include <spdlog/logger.h>
#include <algorithm>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
@ -40,14 +37,9 @@ class WebMapTileServiceTileProvider final
: public QuadtreeRasterOverlayTileProvider {
public:
WebMapTileServiceTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
std::optional<std::string> credit,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
@ -66,13 +58,8 @@ public:
const std::optional<std::map<std::string, std::string>>& dimensions,
const std::vector<std::string>& subdomains)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
tilingScheme,
coverageRectangle,
@ -89,7 +76,15 @@ public:
_tileMatrixSetID(std::move(_tileMatrixSetID)),
_labels(tileMatrixLabels),
_staticDimensions(dimensions),
_subdomains(subdomains) {}
_subdomains(subdomains) {
if (parameters.externals.pCreditSystem && credit) {
this->getCredits().emplace_back(
parameters.externals.pCreditSystem->createCredit(
this->getCreditSource(),
*credit,
pCreator->getOptions().showCreditsOnScreen));
}
}
virtual ~WebMapTileServiceTileProvider() = default;
@ -204,22 +199,7 @@ WebMapTileServiceRasterOverlay::~WebMapTileServiceRasterOverlay() = default;
Future<RasterOverlay::CreateTileProviderResult>
WebMapTileServiceRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
pOwner = pOwner ? pOwner : this;
std::optional<Credit> credit = std::nullopt;
if (pCreditSystem && this->_options.credit) {
credit = pCreditSystem->createCredit(
*this->_options.credit,
pOwner->getOptions().showCreditsOnScreen);
}
const CreateRasterOverlayTileProviderParameters& parameters) const {
bool hasError = false;
std::string errorMessage;
@ -245,7 +225,7 @@ WebMapTileServiceRasterOverlay::createTileProvider(
}
CesiumGeospatial::Projection projection = _options.projection.value_or(
CesiumGeospatial::WebMercatorProjection(pOwner->getOptions().ellipsoid));
CesiumGeospatial::WebMercatorProjection(this->getOptions().ellipsoid));
CesiumGeospatial::GlobeRectangle tilingSchemeRectangle =
CesiumGeospatial::WebMercatorProjection::MAXIMUM_GLOBE_RECTANGLE;
uint32_t rootTilesX = 1;
@ -265,40 +245,36 @@ WebMapTileServiceRasterOverlay::createTileProvider(
1));
if (hasError) {
return asyncSystem
return parameters.externals.asyncSystem
.createResolvedFuture<RasterOverlay::CreateTileProviderResult>(
nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
RasterOverlayLoadType::TileProvider,
nullptr,
errorMessage}));
}
return asyncSystem
return parameters.externals.asyncSystem
.createResolvedFuture<RasterOverlay::CreateTileProviderResult>(
new WebMapTileServiceTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
this,
parameters,
this->_options.credit,
projection,
tilingScheme,
coverageRectangle,
_url,
_headers,
this->_url,
this->_headers,
useKVP,
format,
tileWidth,
tileHeight,
minimumLevel,
maximumLevel,
_options.layer,
_options.style,
_options.tileMatrixSetID,
_options.tileMatrixLabels,
_options.dimensions,
_options.subdomains));
this->_options.layer,
this->_options.style,
this->_options.tileMatrixSetID,
this->_options.tileMatrixLabels,
this->_options.dimensions,
this->_options.subdomains));
}
} // namespace CesiumRasterOverlays

View File

@ -1,3 +1,4 @@
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/UrlTemplateRasterOverlay.h>
@ -12,31 +13,19 @@ public:
MyRasterOverlay() : RasterOverlay("name", {}) {}
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
};
//! [use-url-template]
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult>
MyRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
const CreateRasterOverlayTileProviderParameters& parameters) const {
// Create a new raster overlay with a URL template.
CesiumGeometry::Rectangle coverageRectangle = CesiumGeospatial::
WebMercatorProjection::computeMaximumProjectedRectangle();
UrlTemplateRasterOverlayOptions options{
UrlTemplateRasterOverlayOptions urlTemplateOptions{
.credit = "Copyright (c) Some Amazing Source",
.projection = CesiumGeospatial::WebMercatorProjection(),
.tilingScheme =
@ -53,16 +42,15 @@ MyRasterOverlay::createTileProvider(
this->getName(),
"https://example.com/level-{z}/column-{x}/row-{y}.png",
{},
options);
urlTemplateOptions);
CreateRasterOverlayTileProviderParameters parametersCopy = parameters;
if (!parametersCopy.pOwner) {
parametersCopy.pOwner = this;
}
// Get that raster overlay's tile provider.
return pUrlTemplate->createTileProvider(
asyncSystem,
pAssetAccessor,
pCreditSystem,
pPrepareRendererResources,
pLogger,
pOwner != nullptr ? pOwner : this);
return pUrlTemplate->createTileProvider(parametersCopy);
}
//! [use-url-template]

View File

@ -12,6 +12,7 @@
#include <CesiumNativeTests/SimpleAssetAccessor.h>
#include <CesiumNativeTests/SimpleAssetRequest.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
@ -47,14 +48,8 @@ namespace {
class TestTileProvider : public QuadtreeRasterOverlayTileProvider {
public:
TestTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const IntrusivePointer<const RasterOverlay>& pCreator,
const CreateRasterOverlayTileProviderParameters& parameters,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
@ -63,13 +58,8 @@ public:
uint32_t imageWidth,
uint32_t imageHeight) noexcept
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
pCreditSystem,
credit,
pPrepareRendererResources,
pLogger,
pCreator,
parameters,
projection,
tilingScheme,
coverageRectangle,
@ -114,27 +104,12 @@ public:
: RasterOverlay(name, options) {}
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CreditSystem>& /* pCreditSystem */,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const CreateRasterOverlayTileProviderParameters& parameters)
const override {
if (!pOwner) {
pOwner = this;
}
return asyncSystem.createResolvedFuture<CreateTileProviderResult>(
new TestTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
nullptr,
std::nullopt,
pPrepareRendererResources,
pLogger,
return parameters.externals.asyncSystem
.createResolvedFuture<CreateTileProviderResult>(new TestTileProvider(
this,
parameters,
WebMercatorProjection(Ellipsoid::WGS84),
QuadtreeTilingScheme(
WebMercatorProjection::computeMaximumProjectedRectangle(

View File

@ -7,6 +7,7 @@
#include <CesiumNativeTests/readFile.h>
#include <CesiumNativeTests/waitForFuture.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/CreateRasterOverlayTileProviderParameters.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/TileMapServiceRasterOverlay.h>
@ -125,12 +126,12 @@ TEST_CASE("TileMapServiceRasterOverlay") {
RasterOverlay::CreateTileProviderResult result = waitForFuture(
asyncSystem,
pRasterOverlay->createTileProvider(
asyncSystem,
pMockAssetAccessor,
nullptr,
nullptr,
spdlog::default_logger(),
nullptr));
CreateRasterOverlayTileProviderParameters{
.externals =
{.pAssetAccessor = pMockAssetAccessor,
.asyncSystem = asyncSystem,
.pLogger = spdlog::default_logger()},
}));
REQUIRE(result);
}
@ -160,12 +161,12 @@ TEST_CASE("TileMapServiceRasterOverlay") {
RasterOverlay::CreateTileProviderResult result = waitForFuture(
asyncSystem,
pRasterOverlay->createTileProvider(
asyncSystem,
pMockAssetAccessor,
nullptr,
nullptr,
spdlog::default_logger(),
nullptr));
CreateRasterOverlayTileProviderParameters{
.externals =
{.pAssetAccessor = pMockAssetAccessor,
.asyncSystem = asyncSystem,
.pLogger = spdlog::default_logger()},
}));
REQUIRE(result);
}
@ -215,12 +216,12 @@ TEST_CASE("TileMapServiceRasterOverlay") {
RasterOverlay::CreateTileProviderResult result = waitForFuture(
asyncSystem,
pRasterOverlay->createTileProvider(
asyncSystem,
pMockAssetAccessor,
nullptr,
nullptr,
spdlog::default_logger(),
nullptr));
CreateRasterOverlayTileProviderParameters{
.externals =
{.pAssetAccessor = pMockAssetAccessor,
.asyncSystem = asyncSystem,
.pLogger = spdlog::default_logger()},
}));
REQUIRE(result);
}
@ -237,12 +238,13 @@ TEST_CASE("TileMapServiceRasterOverlay") {
RasterOverlay::CreateTileProviderResult result = waitForFuture(
asyncSystem,
pRasterOverlayWithCredit->createTileProvider(
asyncSystem,
pMockAssetAccessor,
pCreditSystem,
nullptr,
spdlog::default_logger(),
nullptr));
CreateRasterOverlayTileProviderParameters{
.externals =
{.pAssetAccessor = pMockAssetAccessor,
.asyncSystem = asyncSystem,
.pCreditSystem = pCreditSystem,
.pLogger = spdlog::default_logger()},
}));
REQUIRE(result);
@ -266,12 +268,12 @@ TEST_CASE("TileMapServiceRasterOverlay") {
RasterOverlay::CreateTileProviderResult result = waitForFuture(
asyncSystem,
pRasterOverlayWithCredit->createTileProvider(
asyncSystem,
pMockAssetAccessor,
nullptr,
nullptr,
spdlog::default_logger(),
nullptr));
CreateRasterOverlayTileProviderParameters{
.externals =
{.pAssetAccessor = pMockAssetAccessor,
.asyncSystem = asyncSystem,
.pLogger = spdlog::default_logger()},
}));
REQUIRE(result);

View File

@ -41,7 +41,7 @@ cesium_target_include_directories(
target_link_libraries(
CesiumUtility
PUBLIC
zlib-ng::zlib-ng
zlib-ng::zlib
spdlog::spdlog
glm::glm
ada::ada

View File

@ -106,9 +106,17 @@ public:
*/
void releaseAllReferences() noexcept;
/**
* @brief Tests if a credit is referenced by this referencer.
*
* @param credit The credit to test if it is referenced.
*/
bool isCreditReferenced(Credit credit) const noexcept;
private:
std::shared_ptr<CreditSystem> _pCreditSystem;
std::vector<int32_t> _references;
std::vector<uint32_t> _generations;
};
} // namespace CesiumUtility

View File

@ -3,6 +3,8 @@
#include <CesiumUtility/Library.h>
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
@ -10,6 +12,45 @@
namespace CesiumUtility {
class CreditSystem;
/**
* @brief Specifies how credit system snapshots should handle multiple
* credits with the same HTML string.
*/
enum class CreditFilteringMode : uint8_t {
/**
* @brief No filtering is performed. Each unique @ref Credit is reported.
*/
None = 0,
/**
* @brief Credits are filtered so that each reported credit has a combination
* of HTML string and @ref CreditSystem::shouldBeShownOnScreen value that is
* unique from all the other reported credits.
*
* If multiple credits have the same HTML string but different
* @ref CreditSystem::shouldBeShownOnScreen values, they will be reported as
* separate credits.
*
* It is unspecified which of the multiple credits with the same properties
* will be reported.
*/
UniqueHtmlAndShowOnScreen = 1,
/**
* @brief Credits with identical HTML strings are reported as one Credit even
* if they have a different @ref CreditSource or @ref
* CreditSystem::shouldBeShownOnScreen value.
*
* It is unspecified which of the multiple credits with the same source will
* be reported. However, it is guaranteed that if any of the multiple credits
* has @ref CreditSystem::shouldBeShownOnScreen set to `true`, the reported
* credit will also have it set to `true`.
*/
UniqueHtml = 2
};
/**
* @brief Represents an HTML string that should be shown on screen to attribute
* third parties for used data, imagery, etc. Acts as a handle into a
@ -21,18 +62,75 @@ public:
* @brief Returns `true` if two credit objects have the same ID.
*/
bool operator==(const Credit& rhs) const noexcept {
return this->id == rhs.id;
return this->_id == rhs._id && this->_generation == rhs._generation;
}
private:
size_t id;
uint32_t _id;
uint32_t _generation;
Credit(size_t id_) noexcept : id(id_) {}
Credit(uint32_t id, uint32_t generation) noexcept
: _id(id), _generation(generation) {}
friend class CreditSystem;
friend class CreditReferencer;
};
/**
* @brief Represents a source of credits, such as a tileset or raster overlay,
* provided to a @ref CreditSystem.
*
* While the @ref CreditSystem does not directly map credit source instances to
* tilesets or raster overlays (or vice-versa), a tileset or raster overlay can
* be queried for its credit source instance and that instance can be compared
* against one known to the credit system.
*
* When the last reference to a credit source is released, all credits
* associated with that source are invalidated as well.
*/
class CESIUMUTILITY_API CreditSource {
public:
/**
* @brief Constructs a new credit source associated with a given credit
* system.
*
* @param creditSystem The credit system to associate with this source.
*/
CreditSource(CreditSystem& creditSystem) noexcept;
/**
* @brief Constructs a new credit source associated with a given credit
* system.
*
* @param pCreditSystem The credit system to associate with this source. This
* is allowed to be nullptr in order to enable simpler client code when
* working with an optional credit system.
*/
CreditSource(const std::shared_ptr<CreditSystem>& pCreditSystem) noexcept;
~CreditSource() noexcept;
/**
* @brief Gets the @ref CreditSystem associated with this source.
*
* This may be nullptr if this source was never associated with a credit
* system, or if the credit system has been destroyed.
*/
CreditSystem* getCreditSystem() noexcept;
/**
* @copydoc getCreditSystem
*/
const CreditSystem* getCreditSystem() const noexcept;
private:
void notifyCreditSystemDestroyed() noexcept;
CreditSystem* _pCreditSystem;
friend class CreditSystem;
};
/**
* @brief A snapshot of the credits currently active in a {@link CreditSystem}.
*/
@ -57,18 +155,56 @@ struct CreditsSnapshot {
class CESIUMUTILITY_API CreditSystem final {
public:
/**
* @brief Inserts a credit string
* @brief Constructs a new instance.
*/
CreditSystem() noexcept = default;
~CreditSystem() noexcept;
CreditSystem(const CreditSystem&) = delete;
CreditSystem& operator=(const CreditSystem&) = delete;
/**
* @brief Inserts a credit string.
*
* @param source The source of the credit. This should be an instance created
* and owned by a tileset, raster overlay, or other data source.
* @param html The HTML string for the credit.
* @param showOnScreen Whether or not the credit should be shown on screen.
* Credits not shown on the screen should be shown in a separate popup window.
* @return If this string already exists from the same source, returns a
* Credit handle to the existing entry. Otherwise returns a Credit handle to a
* new entry.
*/
Credit createCredit(
const CreditSource& source,
std::string&& html,
bool showOnScreen = false);
/** @copydoc createCredit */
Credit createCredit(
const CreditSource& source,
const std::string& html,
bool showOnScreen = false);
/**
* @brief Inserts a credit string associated with the @ref
* getDefaultCreditSource.
*
* @return If this string already exists, returns a Credit handle to the
* existing entry. Otherwise returns a Credit handle to a new entry.
*
* @deprecated Use the overload that takes a @ref CreditSource.
*/
Credit createCredit(std::string&& html, bool showOnScreen = false);
/**
* @brief Inserts a credit string
* @brief Inserts a credit string associated with the @ref
* getDefaultCreditSource.
*
* @return If this string already exists, returns a Credit handle to the
* existing entry. Otherwise returns a Credit handle to a new entry.
*
* @deprecated Use the overload that takes a @ref CreditSource.
*/
Credit createCredit(const std::string& html, bool showOnScreen = false);
@ -83,26 +219,40 @@ public:
void setShowOnScreen(Credit credit, bool showOnScreen) noexcept;
/**
* @brief Get the HTML string for this credit
* @brief Get the HTML string for this credit.
*/
const std::string& getHtml(Credit credit) const noexcept;
/**
* @brief Gets the source of this credit.
*
* @return The source of this credit, or nullptr if the credit is invalid or
* was created by a \ref CreditSource that has been destroyed.
*/
const CreditSource* getCreditSource(Credit credit) const noexcept;
/**
* @brief Adds a reference to a credit, incrementing its reference count. The
* referenced credit will be shown until its reference count goes back down to
* zero.
*
* @param credit The credit to reference.
* @returns `true` if the credit was valid and the reference was added.
* `false` if the credit was created by a @ref CreditSource that has been
* destroyed and so the reference could not be added.
*/
void addCreditReference(Credit credit);
bool addCreditReference(Credit credit);
/**
* @brief Removes a reference from a credit, decrementing its reference count.
* When the reference count goes to zero, this credit will no longer be shown.
*
* @param credit The credit from which to remove a reference.
* @returns `true` if the credit was valid and the reference was removed.
* `false` if the credit was created by a @ref CreditSource that has been
* destroyed and so the reference could not be removed.
*/
void removeCreditReference(Credit credit);
bool removeCreditReference(Credit credit);
/**
* @brief Gets a snapshot of the credits. The returned instance is only valid
@ -111,27 +261,72 @@ public:
* The snapshot will include a sorted list of credits that are currently
* active, as well as a list of credits that have been removed since the last
* snapshot.
*
* @param filteringMode Specifies how multiple credits with the same HTML
* string should be reported in the snapshot.
*/
const CreditsSnapshot& getSnapshot() noexcept;
const CreditsSnapshot& getSnapshot(
CreditFilteringMode filteringMode =
CreditFilteringMode::UniqueHtml) noexcept;
/**
* @brief Gets the default credit source used when no other source is
* specified.
*
* @deprecated Instead of using the default, create a CreditSource instance
* for each tileset, raster overlay, or other data source.
*/
const CreditSource& getDefaultCreditSource() const noexcept;
private:
void addBulkReferences(const std::vector<int32_t>& references) noexcept;
void releaseBulkReferences(const std::vector<int32_t>& references) noexcept;
struct CreditRecord {
std::string html{};
bool showOnScreen{false};
int32_t referenceCount{0};
bool shownLastSnapshot{0};
uint32_t generation{0};
const CreditSource* pSource{nullptr};
uint32_t previousCreditWithSameHtml{INVALID_CREDIT_INDEX};
uint32_t nextCreditWithSameHtml{INVALID_CREDIT_INDEX};
};
void addBulkReferences(
const std::vector<int32_t>& references,
const std::vector<uint32_t>& generations) noexcept;
void releaseBulkReferences(
const std::vector<int32_t>& references,
const std::vector<uint32_t>& generations) noexcept;
void createCreditSource(CreditSource& creditSource) noexcept;
void destroyCreditSource(CreditSource& creditSource) noexcept;
uint32_t filterCreditForSnapshot(
CreditFilteringMode filteringMode,
const CreditRecord& record) noexcept;
const std::string INVALID_CREDIT_MESSAGE =
"Error: Invalid Credit, cannot get HTML string.";
struct CreditRecord {
std::string html;
bool showOnScreen;
int32_t referenceCount;
bool shownLastSnapshot;
};
static const uint32_t INVALID_CREDIT_INDEX{
std::numeric_limits<uint32_t>::max()};
std::vector<CreditSource*> _creditSources;
std::vector<CreditRecord> _credits;
std::vector<Credit> _creditsToNoLongerShowThisSnapshot;
CreditsSnapshot _snapshot;
std::vector<int32_t> _referenceCountScratch;
// These are credits that were shown in the last snapshot but whose
// CreditSources have since been destroyed. They need to be reported in
// removedCredits in the next snapshot.
std::vector<Credit> _shownCreditsDestroyed;
// Each entry in this vector is an index into _credits that is unused and can
// be reused for a new credit.
std::vector<size_t> _unusedCreditRecords;
CreditSource _defaultCreditSource{*this};
friend class CreditReferencer;
friend class CreditSource;
};
} // namespace CesiumUtility

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstddef>
#include <cstdint>
namespace CesiumUtility {
@ -17,7 +18,7 @@ struct Hash {
* @param second The second hash value.
* @return A new hash value which is a combination of the two.
*/
static std::size_t combine(std::size_t first, std::size_t second);
static size_t combine(size_t first, size_t second);
};
} // namespace CesiumUtility

View File

@ -356,6 +356,12 @@ public:
static std::string
setPath(const std::string& uri, const std::string& newPath);
/**
* @brief Ensures that the Uri's path ends with a slash, modifying itself if
* necessary. Useful when the Uri is used as a base URL.
*/
void ensureTrailingSlash();
private:
std::optional<ada::url_aggregator> _url = std::nullopt;
bool _hasScheme = false;

View File

@ -10,12 +10,16 @@ CreditReferencer::CreditReferencer() noexcept : CreditReferencer(nullptr) {}
CreditReferencer::CreditReferencer(
const std::shared_ptr<CreditSystem>& pCreditSystem) noexcept
: _pCreditSystem(pCreditSystem) {}
: _pCreditSystem(pCreditSystem), _references(), _generations() {}
CreditReferencer::CreditReferencer(const CreditReferencer& rhs) noexcept
: _pCreditSystem(rhs._pCreditSystem), _references(rhs._references) {
: _pCreditSystem(rhs._pCreditSystem),
_references(rhs._references),
_generations(rhs._generations) {
if (this->_pCreditSystem) {
this->_pCreditSystem->addBulkReferences(this->_references);
this->_pCreditSystem->addBulkReferences(
this->_references,
this->_generations);
}
}
@ -23,12 +27,15 @@ CreditReferencer&
CreditReferencer::operator=(const CreditReferencer& rhs) noexcept {
if (this != &rhs) {
if (this->_pCreditSystem) {
this->_pCreditSystem->addBulkReferences(rhs._references);
this->_pCreditSystem->addBulkReferences(
rhs._references,
rhs._generations);
}
this->releaseAllReferences();
this->_pCreditSystem = rhs._pCreditSystem;
this->_references = rhs._references;
this->_generations = rhs._generations;
}
return *this;
@ -43,6 +50,7 @@ CreditReferencer& CreditReferencer::operator=(CreditReferencer&& rhs) noexcept {
this->releaseAllReferences();
this->_pCreditSystem = std::move(rhs._pCreditSystem);
this->_references = std::move(rhs._references);
this->_generations = std::move(rhs._generations);
}
return *this;
@ -65,21 +73,50 @@ void CreditReferencer::addCreditReference(Credit credit) noexcept {
if (!this->_pCreditSystem)
return;
if (this->_references.size() <= credit.id) {
this->_references.resize(credit.id + 1);
if (!this->_pCreditSystem->addCreditReference(credit)) {
// This credit was created by a CreditSource that has been destroyed. Ignore
// it.
return;
}
++this->_references[credit.id];
this->_pCreditSystem->addCreditReference(credit);
if (this->_references.size() <= credit._id) {
this->_references.resize(credit._id + 1);
this->_generations.resize(credit._id + 1);
}
if (this->_generations[credit._id] != credit._generation) {
// The existing references (if any) refer to an older generation of this
// credit (i.e., from a previous CreditSource that has been destroyed). So
// reset the reference count to zero.
this->_references[credit._id] = 0;
this->_generations[credit._id] = credit._generation;
}
++this->_references[credit._id];
}
void CreditReferencer::releaseAllReferences() noexcept {
if (!this->_pCreditSystem)
return;
this->_pCreditSystem->releaseBulkReferences(this->_references);
this->_pCreditSystem->releaseBulkReferences(
this->_references,
this->_generations);
this->_references.clear();
this->_generations.clear();
}
bool CreditReferencer::isCreditReferenced(Credit credit) const noexcept {
if (!this->_pCreditSystem)
return false;
if (this->_references.size() <= credit._id ||
this->_generations[credit._id] != credit._generation) {
return false;
}
return this->_references[credit._id] > 0;
}
} // namespace CesiumUtility

Some files were not shown because too many files have changed in this diff Show More