Compare commits

...

459 Commits

Author SHA1 Message Date
Janine Liu 5b3b30e0d9
Merge pull request #1276 from CesiumGS/removed-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
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
Kevin Ring 126a412a79
Merge pull request #1257 from CesiumGS/duplicate-google-logo
Avoid duplicate Google logo
2025-10-01 13:27:15 +10:00
Kevin Ring 29ff862523 Update declaration. 2025-10-01 13:14:00 +10:00
Kevin Ring e91de71eeb Avoid duplicate Google logo. 2025-10-01 12:59:01 +10: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
Kevin Ring 600e676aff Bump to v0.52.0. 2025-09-30 20:22:33 +10:00
Kevin Ring ef078549b3
Merge pull request #1254 from CesiumGS/google-raster-overlay-from-ion
Add GoogleMapTilesRasterOverlay
2025-09-30 20:15:02 +10:00
Kevin Ring d31196ed1b clang-format 2025-09-30 09:57:08 +10:00
Kevin Ring e984451f44
Fully explain conditional in comment.
Co-authored-by: Janine Liu <32226860+j9liu@users.noreply.github.com>
2025-09-30 09:51:02 +10:00
Ashley Rogers 97a9c66788
Merge pull request #1252 from CesiumGS/asset-endpoint-as-shared-asset
Improve raster overlay token refresh by treating `ExternalAssetEndpoint` as a `SharedAsset`
2025-09-29 17:29:19 -04:00
Janine Liu 77ea05044e Add tests for StringHelpers 2025-09-29 16:49:42 -04:00
Ashley Rogers 84c00b5db6 Merge from main 2025-09-29 16:18:51 -04:00
Ashley Rogers f10b1f98b6
Merge pull request #1253 from CesiumGS/errorlist-improvements
Add new `format` and `log` methods to `ErrorList`
2025-09-29 16:17:16 -04:00
Janine Liu b43d18683f Small suggestions from review 2025-09-29 15:44:59 -04:00
Janine Liu ef46820ba4 unabbreviate sw, ne 2025-09-29 15:42:18 -04:00
Janine Liu dac6fdacb5 @link -> @ref [skip ci] 2025-09-29 15:40:05 -04:00
Janine Liu 77a5381d4d
Merge branch 'main' into asset-endpoint-as-shared-asset 2025-09-29 10:27:02 -04:00
Kevin Ring 4be4c99a3d
Merge pull request #1250 from ftrastourlux/fix-issue-1225-terrain-header-not-propagated
Fix issue 1225 terrain header not propagated
2025-09-29 20:47:36 +10:00
Kevin Ring 2a06cdc3a4 clang-tidy 2025-09-29 19:58:08 +10:00
Kevin Ring 52c2c78566 clang-tidy 2025-09-29 19:27:09 +10:00
Kevin Ring 7f7d7eba1a clang-format 2025-09-29 17:44:19 +10:00
Kevin Ring f9ea77fd91 Readd missing changelog title. 2025-09-29 17:39:25 +10:00
Kevin Ring 063ee500cc Fix merge problem. 2025-09-29 17:35:52 +10:00
Kevin Ring 0ca86eed13 Merge remote-tracking branch 'origin/main' into fix-issue-1225-terrain-header-not-propagated 2025-09-29 17:19:12 +10:00
Kevin Ring 39df3e83dd Add IonRasterOverlay::setAssetOptions. 2025-09-29 17:10:25 +10:00
Kevin Ring 44eb767a30 Include URL in viewport service error messages. 2025-09-29 13:12:39 +10:00
Kevin Ring 767209dc70 Display VCPKG_PLATFORM_TOOLSET_VERSION during cmake configure. 2025-09-29 13:12:21 +10:00
Kevin Ring f69c37450a More missing headers, update CHANGES.md. 2025-09-29 10:45:08 +10:00
Kevin Ring 4a3a6ab78a
Merge branch 'main' into asset-endpoint-as-shared-asset 2025-09-29 10:29:29 +10:00
Kevin Ring 5daafdc914
Merge pull request #1246 from CesiumGS/revert-polygon-overlay
Revert change to have `RasterizedPolygonsOverlay` use Blend2D
2025-09-29 10:27:58 +10:00
Kevin Ring 5c50a06892
Merge branch 'main' into revert-polygon-overlay 2025-09-29 10:26:57 +10:00
Kevin Ring 6714378f6b Add missing headers.
Hopefully clang-tidy will agree.
2025-09-29 10:13:15 +10:00
Kevin Ring 6a41886652 Merge remote-tracking branch 'origin/asset-endpoint-as-shared-asset' into google-raster-overlay-from-ion 2025-09-29 10:08:32 +10:00
Kevin Ring cb0baa97de Add Google logo. 2025-09-29 09:59:10 +10:00
Kevin Ring 672a44eb96 Still more clang-tidy. 2025-09-28 20:55:23 +10:00
Kevin Ring e0c37de97d More clang-tidy 2025-09-27 21:32:10 +10:00
Kevin Ring d515dac685 Merge remote-tracking branch 'origin/main' into errorlist-improvements 2025-09-27 21:27:30 +10:00
Kevin Ring ca48199b33 Include [Error] and [Warning] in message. 2025-09-27 21:20:53 +10:00
Kevin Ring 9b3cb28be0
Merge branch 'main' into asset-endpoint-as-shared-asset 2025-09-27 20:13:21 +10:00
Kevin Ring e39a3d0b38 clang-tidy 2025-09-27 20:09:19 +10:00
Kevin Ring 111487d891 Add missing doc. 2025-09-27 19:02:06 +10:00
Kevin Ring 6a7a10a0c7
Merge pull request #1255 from CesiumGS/sample-height-ellipsoid
Don't hardcode WGS84 ellipsoid in `TilesetHeightQuery`
2025-09-27 18:16:57 +10:00
Janine Liu 522378c35e
Merge branch 'main' into errorlist-improvements 2025-09-26 15:16:37 -04:00
Janine Liu 8dc2f5fbb5 Formatting 2025-09-26 15:15:37 -04:00
Janine Liu 5c28e257c0 Don't hardcode WGS84 in TilesetHeightQuery 2025-09-26 14:55:17 -04:00
Kevin Ring e456bcfa6e Disable questionable tile availability checking. 2025-09-26 22:16:06 +10:00
Kevin Ring 51e10da3b7 Fix clang/gcc errors. 2025-09-26 22:15:20 +10:00
Kevin Ring 4fec544fab Remove "by level" credit system. 2025-09-26 21:49:21 +10:00
Kevin Ring 5178fc8466 Show all google credits as one credit. 2025-09-26 21:15:00 +10:00
Kevin Ring 588cc665b5 Show Google credits. 2025-09-26 19:26:07 +10:00
Kevin Ring 3b2765be6a Credit rework. 2025-09-26 18:29:11 +10:00
Kevin Ring fb3192665f Working Google-through-ion. 2025-09-26 13:08:00 +10:00
Kevin Ring 9bb8a0aab2 Reorg to support Google via ion. 2025-09-26 10:57:34 +10:00
Ashley Rogers d98cd44bf1
Merge pull request #1238 from CesiumGS/shared-asset-invalidate
Add `SharedAssetDepot::invalidate`
2025-09-25 17:00:33 -04:00
Ashley Rogers b0e08b75e1
Merge branch 'main' into shared-asset-invalidate 2025-09-25 10:44:08 -04:00
Kevin Ring 4c957652e4 Merge remote-tracking branch 'origin/asset-endpoint-as-shared-asset' into google-raster-overlay-from-ion 2025-09-25 16:51:14 +10:00
Kevin Ring 18572e9f2f Tile availability implementation. 2025-09-25 16:48:59 +10:00
Kevin Ring 8ceb44cf11 Google uses its own custom tile provider. 2025-09-25 10:21:59 +10:00
Janine Liu 824c9d797a
Merge pull request #1244 from CesiumGS/raster-overlay-wrappability
Refactor `RasterOverlayTileProvider` to make it easier to "decorate" it
2025-09-24 13:16:16 -04:00
Kevin Ring 457812d3c4
Merge pull request #1251 from GhisBntly/upgrade-vcpkg-baseline-for-CVE-2025-53629
Move vcpkg baseline to latest, to fix cpp-httplib's CVE-2025-53629 …
2025-09-24 20:13:19 +10:00
Kevin Ring 972a26aaad
Apparently manifest mode baseline must be a commit hash. 2025-09-24 11:14:58 +10:00
Kevin Ring 9ac9b3f6d6
Use tag instead of commit hash in manifest file, too. 2025-09-24 08:05:25 +10:00
Kevin Ring 0963272a47
Use tag instead of commit hash for vcpkg version. 2025-09-24 08:04:51 +10:00
Ghislain Cottat 4ba025d672 Move vcpkg baseline to 2025.09.17, to fix cpp-httplib's CVE-2025-53629 in 0.20.1 (need 0.23+) 2025-09-23 17:01:58 +02:00
Ashley Rogers 39c4a81458 Update CHANGES.md 2025-09-23 10:20:46 -04:00
Kevin Ring 3f9cdd2cb8 Simplest working GoogleMapTilesRasterOverlay. 2025-09-23 16:11:07 +10:00
Kevin Ring ee99bb2b21 Merge remote-tracking branch 'origin/errorlist-improvements' into google-raster-overlay 2025-09-23 14:29:29 +10:00
Kevin Ring f6f7d035fc Merge remote-tracking branch 'origin/errorlist-improvements' into asset-endpoint-as-shared-asset 2025-09-23 14:29:07 +10:00
Kevin Ring bccdda17c0 Add new ErrorList methods, and tests. 2025-09-23 14:26:21 +10:00
Kevin Ring 6eddef9f18 Initial steps toward Google Map Tiles raster overlay. 2025-09-23 14:11:12 +10:00
Kevin Ring 2fb112556b
Fix typo. 2025-09-23 11:39:33 +10:00
Kevin Ring 4d711374ec Merge remote-tracking branch 'origin/shared-asset-invalidate' into asset-endpoint-as-shared-asset 2025-09-23 10:57:05 +10:00
Kevin Ring 8bcda4a515 Update CHANGES.md. 2025-09-23 10:48:25 +10:00
Kevin Ring 965e38446f Merge remote-tracking branch 'origin/main' into shared-asset-invalidate 2025-09-23 10:47:30 +10:00
Kevin Ring 68f9206141 Incorporate SharedAssetDepot changes from asset-endpoint-as-shared-asset 2025-09-23 10:45:10 +10:00
Kevin Ring 984bf1739e Merge remote-tracking branch 'origin/raster-overlay-wrappability' into asset-endpoint-as-shared-asset 2025-09-23 08:25:40 +10:00
Kevin Ring c25a218d4c clang-format 2025-09-23 08:18:24 +10:00
Kevin Ring bbd606f982 Less overflowy arithmetic. 2025-09-23 08:16:22 +10:00
Kevin Ring 726b621ca3 Flip parameter order. 2025-09-23 08:14:15 +10:00
Kevin Ring 4552af8541 Add TransformIterator tests. 2025-09-23 08:07:39 +10:00
Kevin Ring 70861d92a8
Update Cesium3DTilesSelection/src/RasterOverlayCollection.cpp
Co-authored-by: Janine Liu <32226860+j9liu@users.noreply.github.com>
2025-09-23 07:58:12 +10:00
Kevin Ring 37dc5843c1
Update CesiumRasterOverlays/include/CesiumRasterOverlays/ActivatedRasterOverlay.h
Co-authored-by: Janine Liu <32226860+j9liu@users.noreply.github.com>
2025-09-23 07:35:42 +10:00
Janine Liu 99118d2f8e @link -> @ref 2025-09-22 16:31:52 -04:00
Brendan Duncan 50b1530722 don't need specialized wasm compiler flags anymore 2025-09-22 08:05:31 -06:00
Kevin Ring 332f1230cd clang-tidy 2025-09-22 21:49:04 +10:00
Kevin Ring 63522f1464 Update CHANGES.md. 2025-09-22 21:19:00 +10:00
Kevin Ring d2f2149378 Fix clang/gcc error. 2025-09-22 21:03:05 +10:00
Kevin Ring 55a00b9817 clang-tidy 2025-09-22 20:52:58 +10:00
Kevin Ring c17f010f45 Fix doc. 2025-09-22 20:45:01 +10:00
Kevin Ring dbdd79ca99 SharedAssetDepot context, avoid repeated token refresh success msgs. 2025-09-22 20:39:10 +10:00
Kevin Ring bc74699a46 DerivedValue improvements and tests. 2025-09-22 18:22:40 +10:00
Kevin Ring cc60025989 Fix clang/gcc errors. 2025-09-22 17:05:04 +10:00
Kevin Ring e55517f2aa Add doc. 2025-09-22 16:40:30 +10:00
Kevin Ring f5dcf4b5c0 Pass through failed request on token refresh.
This fixes the token troubleshooting panel.
2025-09-22 15:50:28 +10:00
Kevin Ring f74e96c003 Report token refresh success/failure. 2025-09-22 14:06:21 +10:00
Kevin Ring 112dfa6968 Factor out common code. 2025-09-22 13:30:26 +10: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
Frédéric Trastour 5de826e8e7
Merge branch 'main' into fix-issue-1225-terrain-header-not-propagated 2025-09-19 15:28:22 +02:00
Kevin Ring cda7db7654 Put similar code in one place, getting ready to consolidate. 2025-09-19 21:48:30 +10:00
Kevin Ring a374ae533a Token refresh reliability. 2025-09-19 21:11:10 +10:00
Kevin Ring 4c31993899 Print token refresh message. 2025-09-19 17:59:41 +10:00
Kevin Ring 8991c8b513 Fully-implemented token refresh, maybe. 2025-09-19 17:22:06 +10:00
Kevin Ring 020a852d05 Implement SharedAssetDepot::invalidate(asset) 2025-09-19 15:55:00 +10:00
Kevin Ring 45125ca439 Time based token refresh (instead of 401-error-based) 2025-09-19 15:06:34 +10:00
Kevin Ring 8f1dd4a25a Add DerivedValue class. 2025-09-19 15:05:47 +10:00
Kevin Ring df527e3e61 Add RasterOverlayTileProvider::getExternals. 2025-09-19 15:05:29 +10:00
Frederic Trastour 95d2d482bf Updated CHANGES.md 2025-09-18 14:50:30 +02:00
Kevin Ring 2c78104671 Merge remote-tracking branch 'origin/raster-overlay-wrappability' into asset-endpoint-as-shared-asset 2025-09-18 11:46:36 +10:00
Kevin Ring bcf3771f87 Merge remote-tracking branch 'origin/main' into raster-overlay-wrappability 2025-09-18 11:40:00 +10:00
Kevin Ring d3eaad1b1c clang-tidy 2025-09-18 07:25:47 +10:00
Ashley Rogers 2ce70e3fc5 clang-tidy fixes 2025-09-17 15:28:45 -04:00
Ashley Rogers 1e96cde79d Format 2025-09-17 14:55:22 -04:00
Ashley Rogers 2044ee4508 Revert polygon overlay implementation changes 2025-09-17 14:47:39 -04:00
Janine Liu 9189d6a72e
Merge pull request #1240 from CesiumGS/blend2d-ios
Make sure vcpkg dependencies with [features] are installed.
2025-09-17 14:24:56 -04:00
Janine Liu 94c4a3c021
Merge branch 'main' into blend2d-ios 2025-09-17 14:03:12 -04:00
Frederic Trastour 6f095361c9 Fix issue #1225 - Custom headers are just for accessing the top .json file but are not used when accessing subfiles (.terrain) 2025-09-17 18:23:05 +02:00
Janine Liu d1263ae8f3
Merge pull request #1243 from CesiumGS/tile-and-tile-provider-move
Add move constructor / assignment operator for TileProviderAndTile.
2025-09-17 10:15:47 -04:00
Kevin Ring e20012ef05 Fix placeholders and missing projections. 2025-09-17 21:13:01 +10:00
Kevin Ring ce18f3caab Fix placeholders and missing projections. 2025-09-17 21:04:36 +10:00
Kevin Ring 29ea6f876f Merge remote-tracking branch 'origin/raster-overlay-wrappability' into asset-endpoint-as-shared-asset 2025-09-17 14:59:29 +10:00
Kevin Ring 9a0238cbb0 Merge remote-tracking branch 'origin/shared-asset-invalidate' into asset-endpoint-as-shared-asset 2025-09-17 14:59:09 +10:00
Kevin Ring a6ff55c7e6
Merge branch 'main' into blend2d-ios 2025-09-17 14:56:03 +10:00
Kevin Ring df11774f91 Enable copy constructor/assignment on TileProviderAndTile. 2025-09-17 14:05:14 +10:00
Janine Liu 7f97b89a3c
Merge branch 'main' into tile-and-tile-provider-move 2025-09-16 16:30:07 -04:00
Janine Liu f58293707c
Merge branch 'main' into shared-asset-invalidate 2025-09-16 15:30:22 -04:00
Janine Liu 7bc746033b
Merge pull request #1239 from CesiumGS/raster-overlay-const-tile
Add `RasterOverlayExternals`, make `RasterOverlayTileProvider::loadTileImage` receive a const `RasterOverlayTile`
2025-09-16 15:28:28 -04:00
Janine Liu ba7ff5d9a4 Minor doc tweaks 2025-09-16 13:25:29 -04:00
Kevin Ring ba8782d67a More doc. 2025-09-16 21:33:41 +10:00
Kevin Ring 25ad6e04b7 Add doc. 2025-09-16 19:36:02 +10:00
Kevin Ring 5233089008 Add move constructor / assignment operator for TileProviderAndTile. 2025-09-16 19:12:05 +10:00
Kevin Ring cc15117120 Add overload for non-const RasterOverlay. 2025-09-16 18:57:12 +10:00
Kevin Ring 76d13f797e Doc tweaks. 2025-09-16 18:19:58 +10:00
Kevin Ring a6a447c5c5 More clang-tidy 2025-09-15 20:29:46 +10:00
Kevin Ring bee5d04d2d clang-tidy. 2025-09-15 19:51:43 +10:00
Kevin Ring 17a363824c Doc fixes and small tweaks. 2025-09-15 19:30:56 +10:00
Kevin Ring ec7bb3f661 Remove unnecessary method. 2025-09-15 17:37:13 +10:00
Kevin Ring 38ba06ecfc Remove unused code. 2025-09-15 16:50:05 +10:00
Kevin Ring 7a8e4c9fee Add TransformIterator, remove unnecessary _overlays. 2025-09-15 16:45:04 +10:00
Kevin Ring 77c36df0a1 Remove unnecessary OverlayList. 2025-09-15 14:37:43 +10:00
Kevin Ring 97a7e9d2d1 RasterOverlay::activate. 2025-09-15 09:10:39 +10:00
Kevin Ring b5fc62f045 Factory function for ActivatedRasterOverlay. 2025-09-12 22:18:08 +10:00
Kevin Ring c792367f8f Make ActivatedRasterOverlay public and in CesiumRasterOverlays. 2025-09-12 21:09:22 +10:00
Kevin Ring 4d5916ceb2 Make sure vcpkg dependencies with [features] are installed. 2025-09-11 20:32:46 +10:00
Kevin Ring 00e9d0691d Don't crash when there are no overlays. 2025-09-10 19:18:20 +10:00
Kevin Ring 20caa9e560 Add ActivatedRasterOverlay. 2025-09-10 19:13:43 +10:00
Kevin Ring 0c2478d1a5 clang-tidy 2025-09-09 20:21:35 +10:00
Kevin Ring e8bcd9efc1 Fix typo. 2025-09-09 18:31:29 +10:00
Kevin Ring fd116c8011 loadTileImage receives const RasterOverlayTile. 2025-09-09 18:24:28 +10:00
Kevin Ring 0e189db99e Merge remote-tracking branch 'origin/shared-asset-invalidate' into asset-endpoint-as-shared-asset 2025-09-08 18:36:10 +10:00
Kevin Ring f49954ee2b _isInvalidated doesn't need to be atomic. 2025-09-08 18:16:54 +10:00
Kevin Ring a734a85a42 Add comment and changelog entry. 2025-09-08 17:47:34 +10:00
Kevin Ring 67a7202277 Add SharedAssetDepot::invalidate. 2025-09-08 17:21:25 +10:00
Kevin Ring d30f3c00c1 Doc tweak. 2025-09-08 16:56:29 +10: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
Kevin Ring 1d59866a91 clang-tidy 2025-09-05 14:38:08 +10:00
Kevin Ring 83fbb305ad Fix compile error. 2025-09-05 11:09:18 +10:00
Kevin Ring fb2c245ef0 Use SharedAsset for ion imagery endpoint. 2025-09-04 22:15:11 +10:00
Brendan Duncan 3f416be76d Add EM_CONFIG to VCPKG_ENV_PASSTHROUGH_UNTRACKED 2025-09-03 22:32:37 -06:00
Kevin Ring e48f24ee9a Asset invalidation tests. 2025-09-04 08:21:05 +10:00
Kevin Ring ca546ffb72 Add SharedAssetDepot::invalidate. 2025-09-03 21:15:46 +10:00
Kevin Ring 3a706aff27 Merge remote-tracking branch 'origin/main' into asset-endpoint-as-shared-asset 2025-09-03 18:37:37 +10:00
Brendan Duncan 90d1c96818 disable blend2d wasm install for now 2025-08-31 18:24:32 -06:00
Kevin Ring f513a96e39 WIP treat ExternalAssetEndpoint as a SharedAsset. 2025-08-31 21:31:43 +10: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
161 changed files with 9815 additions and 3144 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,105 @@
# 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:
- `RasterOverlayTileProvider::loadTileImage` now receives a const `RasterOverlayTile`.
- `SharedAssetDepot` now uses a templatized "context" instead of separate `AsyncSystem` and `IAssetAccessor` parameters. It defaults to `SharedAssetContext`.
- Removed the following from `RasterOverlayTileProvider`:
- The constructor overloads that were used to create a placeholder tile provider.
- `isPlaceholder`
- `getTile`
- `getTileDataBytes`
- `getNumberOfTilesLoading`
- `removeTile`
- `loadTile`
- `loadTileThrottled`
- `RasterMappedTo3DTile::mapOverlayToTile` now takes an `ActivatedRasterOverlay` instead of a `RasterOverlayTileProvider`.
- 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:
- Added `GoogleMapTilesRasterOverlay`.
- Added `invalidate` method to `SharedAssetDepot`.
- Added `RasterOverlayExternals` class. This is similar to `TilesetExternals` and is a more convenient way to pass around the various external interfaces that raster overlays use.
- Added `ActivatedRasterOverlay`, encapsulating most of the functionality that was previously found on `RasterOverlayTileProvider`.
- Added `addTileOverlays` and `updateTileOverlays` to `RasterOverlayCollection`.
- `RasterOverlayCollection::add` and `remove` now take a pointer to a const `RasterOverlay`.
- Added `CesiumUtility::TransformIterator`.
- Added `CesiumUtility::DerivedValue`.
- Added `RasterOverlayTileProvider::getExternals`.
- Added new `log` and `format` methods to `ErrorList`.
- Added `AsyncSystem::enterMainThread`.
- Added `JsonObjectJsonHandler::ValueType`.
- Added `trimWhitespace` and `splitOnCharacter` to `StringHelpers`.
- Added `IonRasterOverlay::setAssetOptions`, providing the ability to supply asset-specific options to Cesium ion when requesting an asset endpoint.
##### Fixes :wrench:
- The Cesium ion token for raster overlays is now automatically refreshed every 55 minutes. Previously, it would refresh on a 401 HTTP status code, which could cause extraneous session usage if the raster overlay ever returned a 401 error for a non-token-related reason.
- Reverted change to `RasterizedPolygonsOverlay` that could produce crashes with certain tilesets.
- Fixed a bug where `TilesetHeightQuery` would always sample the WGS84 ellipsoid, even if a different one was supplied.
- Fixed a build system bug that prevented `libblend2d.a` from being installed for iOS.
- Fixed a bug when loading terrain where custom HTTP headers were not propagated through all terrain loading requests, preventing authentication tokens and API keys from working correctly with authenticated terrain services.
- Added a move constructor and assignment operator to `TileProviderAndTile`. This is important to prevent it from inadvertently incrementing/decrementing non-thread-safe reference counts from the wrong thread while being moved.
- `LoadedTileEnumerator` now provides non-const access to enumerated `Tile` instances, even if the enumerator itself is const.
### v0.51.0 - 2025-09-02
##### Breaking Changes :mega:

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,16 +65,33 @@ 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}")
message(STATUS "VCPKG_TARGET_TRIPLET ${VCPKG_TARGET_TRIPLET}")
message(STATUS "VCPKG_PLATFORM_TOOLSET_VERSION $ENV{VCPKG_PLATFORM_TOOLSET_VERSION}")
if (NOT VCPKG_OVERLAY_PORTS)
if (DEFINED ENV{VCPKG_OVERLAY_PORTS})
@ -79,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
@ -90,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.
@ -104,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 dbe35ceb30c688bf72e952ab23778e009a578f18
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
@ -137,10 +194,20 @@ endif()
include("cmake/defaults.cmake")
project(cesium-native
VERSION 0.51.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)
@ -172,8 +239,8 @@ set(CESIUM_CLANG_TIDY_USE_THREADS 14 CACHE STRING "Sets the number of threads fo
if(CESIUM_INSTALL_STATIC_LIBS OR CESIUM_INSTALL_HEADERS AND EZVCPKG_PACKAGES_DIR)
foreach(PACKAGE ${PACKAGES_PUBLIC})
string(REGEX REPLACE "\[.*\]" "" PACKAGE ${PACKAGE})
set(PACKAGE_DIR ${PACKAGE_BASE_DIR}/${PACKAGE}_${VCPKG_TRIPLET})
string(REGEX REPLACE "\\[.*\\]" "" PACKAGE "${PACKAGE}")
set(PACKAGE_DIR "${PACKAGE_BASE_DIR}/${PACKAGE}_${VCPKG_TRIPLET}")
message(DEBUG "PACKAGE_DIR ${PACKAGE_DIR}")
if(CESIUM_INSTALL_HEADERS AND NOT PACKAGE IN_LIST CESIUM_EXCLUDE_INSTALL_HEADERS)
@ -194,7 +261,8 @@ endif()
if(CESIUM_INSTALL_STATIC_LIBS AND NOT VCPKG_MANIFEST_MODE)
foreach(PACKAGE ${PACKAGES_PRIVATE})
set(PACKAGE_DIR ${PACKAGE_BASE_DIR}/${PACKAGE}_${VCPKG_TRIPLET})
string(REGEX REPLACE "\\[.*\\]" "" PACKAGE "${PACKAGE}")
set(PACKAGE_DIR "${PACKAGE_BASE_DIR}/${PACKAGE}_${VCPKG_TRIPLET}")
message(DEBUG "PACKAGE_DIR ${PACKAGE_DIR}")
if (NOT PACKAGE IN_LIST CESIUM_EXCLUDE_INSTALL_STATIC_LIBS AND EXISTS ${PACKAGE_DIR}/lib)
install(
@ -282,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
@ -293,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)
@ -320,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()
@ -330,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)
@ -384,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

@ -182,14 +182,9 @@ public:
explicit LoadedTileEnumerator(Tile* pRootTile) noexcept;
/** @brief Returns an iterator starting at the first tile. */
const_iterator begin() const noexcept;
iterator begin() const noexcept;
/** @brief Returns an iterator starting after the last tile. */
const_iterator end() const noexcept;
/** @brief Returns an iterator starting at the first tile. */
iterator begin() noexcept;
/** @brief Returns an iterator starting after the last tile. */
iterator end() noexcept;
iterator end() const noexcept;
private:
Tile* _pRootTile;

View File

@ -8,6 +8,10 @@
#include <memory>
namespace CesiumRasterOverlays {
class ActivatedRasterOverlay;
}
namespace Cesium3DTilesSelection {
class Tile;
@ -206,10 +210,7 @@ public:
*
* @param maximumScreenSpaceError The maximum screen space error that is used
* for the current tile
* @param tileProvider The overlay tile provider to map to the tile. This may
* be a placeholder if the tile provider is not yet ready.
* @param placeholder The placeholder tile provider for this overlay. This is
* always a placeholder, even if the tile provider is already ready.
* @param activatedOverlay The activated overlay to map to the tile.
* @param tile The tile to which to map the overlay.
* @param missingProjections The list of projections for which there are not
* yet any texture coordiantes. On return, the given overlay's Projection may
@ -223,8 +224,7 @@ public:
*/
static RasterMappedTo3DTile* mapOverlayToTile(
double maximumScreenSpaceError,
CesiumRasterOverlays::RasterOverlayTileProvider& tileProvider,
CesiumRasterOverlays::RasterOverlayTileProvider& placeholder,
CesiumRasterOverlays::ActivatedRasterOverlay& activatedOverlay,
Tile& tile,
std::vector<CesiumGeospatial::Projection>& missingProjections,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);

View File

@ -4,19 +4,56 @@
#include <Cesium3DTilesSelection/LoadedTileEnumerator.h>
#include <Cesium3DTilesSelection/TilesetExternals.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCounted.h>
#include <CesiumUtility/Tracing.h>
#include <CesiumUtility/TransformIterator.h>
#include <memory>
#include <span>
#include <vector>
namespace CesiumRasterOverlays {
class ActivatedRasterOverlay;
}
namespace Cesium3DTilesSelection {
class LoadedTileEnumerator;
struct TilesetOptions;
/**
* @brief Captures the tile overlay status as produced by
* @ref RasterOverlayCollection::updateTileOverlays.
*/
struct TileRasterOverlayStatus {
/**
* @brief The index of the first entry in @ref Tile::getMappedRasterTiles,
* if any, for which more overlay detail is available than is shown by this
* @ref Tile.
*
* If this is a leaf @ref Tile, an overlay with more detail available will
* necessitate upsampling of the leaf geometry so that the overlay can be
* rendered at full resolution.
*/
std::optional<size_t> firstIndexWithMoreDetailAvailable;
/**
* @brief The index of the first entry in @ref Tile::getMappedRasterTiles,
* if any, for which the availability of more overlay detail is not yet known.
*/
std::optional<size_t> firstIndexWithUnknownAvailability;
/**
* @brief The index of the first entry in @ref Tile::getMappedRasterTiles,
* if any, for which texture coordinates for the overlay's projection are not
* yet available on the @ref Tile.
*/
std::optional<size_t> firstIndexWithMissingProjection;
};
/**
* @brief A collection of {@link CesiumRasterOverlays::RasterOverlay} instances that are associated
@ -89,100 +126,93 @@ public:
RasterOverlayCollection&
operator=(RasterOverlayCollection&& rhs) noexcept = default;
/**
* @brief Destroys the collection.
*/
~RasterOverlayCollection() noexcept;
/**
* @brief Gets the activated raster overlays in this collection.
*/
const std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::ActivatedRasterOverlay>>&
getActivatedOverlays() const noexcept;
/**
* @brief Adds the given {@link CesiumRasterOverlays::RasterOverlay} to this collection.
*
* @param pOverlay The pointer to the overlay. This may not be `nullptr`.
*/
void add(const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlay>& pOverlay);
const CesiumRasterOverlays::RasterOverlay>& pOverlay) noexcept;
/**
* @brief Remove the given {@link CesiumRasterOverlays::RasterOverlay} from this collection.
* @brief Remove the given {@link CesiumRasterOverlays::RasterOverlay} from
* this collection.
*/
void remove(const CesiumUtility::IntrusivePointer<
const CesiumRasterOverlays::RasterOverlay>& pOverlay) noexcept;
/**
* @brief Remove the given {@link CesiumRasterOverlays::RasterOverlay} from
* this collection.
*/
void remove(const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlay>& pOverlay) noexcept;
/**
* @brief Gets the overlays in this collection.
* @brief Remove the given {@link CesiumRasterOverlays::ActivatedRasterOverlay}
* from this collection.
*/
const std::vector<
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlay>>&
getOverlays() const;
void
remove(const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::ActivatedRasterOverlay>& pActivated) noexcept;
/**
* @brief Gets the tile providers in this collection. Each tile provider
* corresponds with the overlay at the same position in the collection
* returned by {@link getOverlays}.
* @brief Adds raster overlays to a new {@link Tile}.
*
* The tile should be in the {@link TileLoadState::Unloaded} or
* {@link TileLoadState::FailedTemporarily} state.
*
* Any existing raster overlays on the tile will be cleared.
*
* @param tile The tile for which to add the overlays.
* @param tilesetOptions The {@link TilesetOptions} for the tileset to which
* the tile belongs.
* @returns The list of projections required by the overlays that were added
* to the tile.
*/
const std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>&
getTileProviders() const;
std::vector<CesiumGeospatial::Projection>
addTileOverlays(Tile& tile, const TilesetOptions& tilesetOptions) noexcept;
/**
* @brief Gets the placeholder tile providers in this collection. Each
* placeholder tile provider corresponds with the overlay at the same position
* in the collection returned by {@link getOverlays}.
* @brief Updates the raster overlays associated with a tile.
*
* This method is called internally for each tile that is rendered, and gives
* the raster overlay system a chance to replace raster overlay placeholders
* with real tiles. Its return value is also used to determine whether or not
* this tile should be upsampled in order to attach further raster overlay
* detail.
*
* @param tile The tile for which to update the overlays.
* @param tilesetOptions The {@link TilesetOptions} for the tileset to which
* the tile belongs.
* @returns Details of the raster overlays attached to this tile.
*/
const std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>&
getPlaceholderTileProviders() const;
TileRasterOverlayStatus
updateTileOverlays(Tile& tile, const TilesetOptions& tilesetOptions) noexcept;
/**
* @brief Finds the tile provider for a given overlay.
*
* If the specified raster overlay is not part of this collection, this method
* will return nullptr.
*
* If the overlay's real tile provider hasn't finished being
* created yet, a placeholder will be returned. That is, its
* {@link CesiumRasterOverlays::RasterOverlayTileProvider::isPlaceholder} method will return true.
*
* @param overlay The overlay for which to obtain the tile provider.
* @return The tile provider, if any, corresponding to the raster overlay.
*/
CesiumRasterOverlays::RasterOverlayTileProvider* findTileProviderForOverlay(
CesiumRasterOverlays::RasterOverlay& overlay) noexcept;
/**
* @copydoc findTileProviderForOverlay
*/
const CesiumRasterOverlays::RasterOverlayTileProvider*
findTileProviderForOverlay(
const CesiumRasterOverlays::RasterOverlay& overlay) const noexcept;
/**
* @brief Finds the placeholder tile provider for a given overlay.
*
* If the specified raster overlay is not part of this collection, this method
* will return nullptr.
*
* This method will return the placeholder tile provider even if the real one
* has been created. This is useful to create placeholder tiles when the
* rectangle in the overlay's projection is not yet known.
*
* @param overlay The overlay for which to obtain the tile provider.
* @return The placeholder tile provider, if any, corresponding to the raster
* overlay.
*/
CesiumRasterOverlays::RasterOverlayTileProvider*
findPlaceholderTileProviderForOverlay(
CesiumRasterOverlays::RasterOverlay& overlay) noexcept;
/**
* @copydoc findPlaceholderTileProviderForOverlay
*/
const CesiumRasterOverlays::RasterOverlayTileProvider*
findPlaceholderTileProviderForOverlay(
const CesiumRasterOverlays::RasterOverlay& overlay) const noexcept;
private:
struct GetOverlayFunctor;
public:
/**
* @brief A constant iterator for {@link CesiumRasterOverlays::RasterOverlay} instances.
*/
typedef std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlay>>::const_iterator const_iterator;
using const_iterator = CesiumUtility::TransformIterator<
GetOverlayFunctor,
std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::ActivatedRasterOverlay>>::const_iterator>;
/**
* @brief Returns an iterator at the beginning of this collection.
@ -200,31 +230,21 @@ public:
size_t size() const noexcept;
private:
// We store the list of overlays and tile providers in this separate class
// so that we can separate its lifetime from the lifetime of the
// RasterOverlayCollection. We need to do this because the async operations
// that create tile providers from overlays need to have somewhere to write
// the result. And we can't extend the lifetime of the entire
// RasterOverlayCollection until the async operations complete because the
// RasterOverlayCollection has a LoadedTileEnumerator, which is owned
// externally and may become invalid before the async operations complete.
struct OverlayList
: public CesiumUtility::ReferenceCountedNonThreadSafe<OverlayList> {
std::vector<
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlay>>
overlays{};
std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>
tileProviders{};
std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>>
placeholders{};
struct GetOverlayFunctor {
CesiumUtility::IntrusivePointer<const CesiumRasterOverlays::RasterOverlay>
operator()(const CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::ActivatedRasterOverlay>& p) const {
return &p->getOverlay();
}
};
LoadedTileEnumerator _loadedTiles;
TilesetExternals _externals;
CesiumGeospatial::Ellipsoid _ellipsoid;
CesiumUtility::IntrusivePointer<OverlayList> _pOverlays;
std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::ActivatedRasterOverlay>>
_activatedOverlays;
CESIUM_TRACE_DECLARE_TRACK_SET(_loadingSlots, "Raster Overlay Loading Slot")
};

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

@ -10,7 +10,6 @@
#include <Cesium3DTilesSelection/TilesetOptions.h>
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/HttpHeaders.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/IAssetResponse.h>
#include <CesiumGeometry/Axis.h>
@ -114,6 +113,7 @@ TileLoadResult convertToTileLoadResult(
TilesetContentLoaderResult<LayerJsonTerrainLoader>
convertToTilesetContentLoaderResult(
const Ellipsoid& ellipsoid,
std::vector<CesiumAsync::IAssetAccessor::THeader>&& requestHeaders,
LoadLayersResult&& loadLayersResult) {
if (loadLayersResult.errors) {
TilesetContentLoaderResult<LayerJsonTerrainLoader> result;
@ -176,7 +176,7 @@ convertToTilesetContentLoaderResult(
std::move(pLoader),
std::move(pRootTile),
std::move(credits),
std::vector<IAssetAccessor::THeader>{},
std::move(requestHeaders),
std::move(loadLayersResult.errors)};
}
@ -395,6 +395,7 @@ Future<LoadLayersResult> loadLayersRecursive(
pAssetAccessor,
tilingScheme,
useWaterMask,
requestHeaders,
loadLayersResult = std::move(loadLayersResult)](
std::shared_ptr<IAssetRequest>&& pCompletedRequest) mutable {
const CesiumAsync::IAssetResponse* pResponse =
@ -434,16 +435,13 @@ Future<LoadLayersResult> loadLayersRecursive(
std::move(loadLayersResult));
}
const CesiumAsync::HttpHeaders& completedRequestHeaders =
pCompletedRequest->headers();
std::vector<IAssetAccessor::THeader> flatHeaders(
completedRequestHeaders.begin(),
completedRequestHeaders.end());
// Use the original user headers instead of completed request
// headers
return loadLayersRecursive(
asyncSystem,
pAssetAccessor,
pCompletedRequest->url(),
flatHeaders,
requestHeaders,
layerJson,
tilingScheme,
useWaterMask,
@ -567,6 +565,7 @@ LayerJsonTerrainLoader::createLoader(
ellipsoid,
asyncSystem = externals.asyncSystem,
pAssetAccessor = externals.pAssetAccessor,
requestHeaders,
useWaterMask](
std::shared_ptr<CesiumAsync::IAssetRequest>&& pCompletedRequest) {
const CesiumAsync::IAssetResponse* pResponse =
@ -591,24 +590,20 @@ LayerJsonTerrainLoader::createLoader(
return asyncSystem.createResolvedFuture(std::move(result));
}
const CesiumAsync::HttpHeaders& completedRequestHeaders =
pCompletedRequest->headers();
std::vector<IAssetAccessor::THeader> flatHeaders(
completedRequestHeaders.begin(),
completedRequestHeaders.end());
return loadLayerJson(
asyncSystem,
pAssetAccessor,
pCompletedRequest->url(),
flatHeaders,
requestHeaders,
pResponse->data(),
useWaterMask,
ellipsoid);
})
.thenInMainThread([ellipsoid](LoadLayersResult&& loadLayersResult) {
.thenInMainThread([ellipsoid,
requestHeaders](LoadLayersResult&& loadLayersResult) {
return convertToTilesetContentLoaderResult(
ellipsoid,
std::vector<CesiumAsync::IAssetAccessor::THeader>(requestHeaders),
std::move(loadLayersResult));
});
}
@ -630,9 +625,11 @@ Cesium3DTilesSelection::LayerJsonTerrainLoader::createLoader(
layerJson,
contentOptions.enableWaterMask,
ellipsoid)
.thenInMainThread([ellipsoid](LoadLayersResult&& loadLayersResult) {
.thenInMainThread([ellipsoid,
requestHeaders](LoadLayersResult&& loadLayersResult) {
return convertToTilesetContentLoaderResult(
ellipsoid,
std::vector<CesiumAsync::IAssetAccessor::THeader>(requestHeaders),
std::move(loadLayersResult));
});
}

View File

@ -84,27 +84,14 @@ bool LoadedConstTileEnumerator::const_iterator::operator!=(
LoadedTileEnumerator::LoadedTileEnumerator(Tile* pRootTile) noexcept
: _pRootTile(pRootTile) {}
LoadedTileEnumerator::const_iterator
LoadedTileEnumerator::begin() const noexcept {
if (meetsCriteriaForEnumeration(this->_pRootTile))
return const_iterator(this->_pRootTile);
else
return const_iterator(nullptr);
}
LoadedTileEnumerator::const_iterator
LoadedTileEnumerator::end() const noexcept {
return const_iterator(nullptr);
}
LoadedTileEnumerator::iterator LoadedTileEnumerator::begin() noexcept {
LoadedTileEnumerator::iterator LoadedTileEnumerator::begin() const noexcept {
if (meetsCriteriaForEnumeration(this->_pRootTile))
return iterator(this->_pRootTile);
else
return iterator(nullptr);
}
LoadedTileEnumerator::iterator LoadedTileEnumerator::end() noexcept {
LoadedTileEnumerator::iterator LoadedTileEnumerator::end() const noexcept {
return iterator(nullptr);
}

View File

@ -7,6 +7,7 @@
#include <CesiumGeospatial/BoundingRegion.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlayUtilities.h>
@ -246,18 +247,12 @@ bool RasterMappedTo3DTile::loadThrottled() noexcept {
return true;
}
RasterOverlayTileProvider& provider = pLoading->getTileProvider();
return provider.loadTileThrottled(*pLoading);
ActivatedRasterOverlay& activated = pLoading->getActivatedOverlay();
return activated.loadTileThrottled(*pLoading);
}
namespace {
IntrusivePointer<RasterOverlayTile>
getPlaceholderTile(RasterOverlayTileProvider& tileProvider) {
// Rectangle and geometric error don't matter for a placeholder.
return tileProvider.getTile(Rectangle(), glm::dvec2(0.0));
}
std::optional<Rectangle> getPreciseRectangleFromBoundingVolume(
const Projection& projection,
const BoundingVolume& boundingVolume) {
@ -288,12 +283,12 @@ int32_t addProjectionToList(
RasterMappedTo3DTile* addRealTile(
Tile& tile,
RasterOverlayTileProvider& provider,
ActivatedRasterOverlay& activatedOverlay,
const Rectangle& rectangle,
const glm::dvec2& screenPixels,
int32_t textureCoordinateIndex) {
IntrusivePointer<RasterOverlayTile> pTile =
provider.getTile(rectangle, screenPixels);
activatedOverlay.getTile(rectangle, screenPixels);
if (!pTile) {
return nullptr;
} else {
@ -307,19 +302,19 @@ RasterMappedTo3DTile* addRealTile(
/*static*/ RasterMappedTo3DTile* RasterMappedTo3DTile::mapOverlayToTile(
double maximumScreenSpaceError,
RasterOverlayTileProvider& tileProvider,
RasterOverlayTileProvider& placeholder,
ActivatedRasterOverlay& activatedOverlay,
Tile& tile,
std::vector<Projection>& missingProjections,
const CesiumGeospatial::Ellipsoid& ellipsoid) {
if (tileProvider.isPlaceholder()) {
if (activatedOverlay.getTileProvider() == nullptr) {
// Provider not created yet, so add a placeholder tile.
return &tile.getMappedRasterTiles().emplace_back(
getPlaceholderTile(placeholder),
activatedOverlay.getPlaceholderTile(),
-1);
}
const Projection& projection = tileProvider.getProjection();
const Projection& projection =
activatedOverlay.getTileProvider()->getProjection();
// If the tile is loaded, use the precise rectangle computed from the content.
const TileContent& content = tile.getContent();
@ -340,7 +335,12 @@ RasterMappedTo3DTile* addRealTile(
projection,
*pRectangle,
ellipsoid);
return addRealTile(tile, tileProvider, *pRectangle, screenPixels, index);
return addRealTile(
tile,
activatedOverlay,
*pRectangle,
screenPixels,
index);
} else {
// We don't have a precise rectangle for this projection, which means the
// tile was loaded before we knew we needed this projection. We'll need to
@ -350,7 +350,7 @@ RasterMappedTo3DTile* addRealTile(
int32_t textureCoordinateIndex =
existingIndex + addProjectionToList(missingProjections, projection);
return &tile.getMappedRasterTiles().emplace_back(
getPlaceholderTile(placeholder),
activatedOverlay.getPlaceholderTile(),
textureCoordinateIndex);
}
}
@ -360,7 +360,7 @@ RasterMappedTo3DTile* addRealTile(
addProjectionToList(missingProjections, projection);
std::optional<Rectangle> maybeRectangle =
getPreciseRectangleFromBoundingVolume(
tileProvider.getProjection(),
activatedOverlay.getTileProvider()->getProjection(),
tile.getBoundingVolume());
if (maybeRectangle) {
const glm::dvec2 screenPixels =
@ -372,14 +372,14 @@ RasterMappedTo3DTile* addRealTile(
ellipsoid);
return addRealTile(
tile,
tileProvider,
activatedOverlay,
*maybeRectangle,
screenPixels,
textureCoordinateIndex);
} else {
// No precise rectangle yet, so return a placeholder for now.
return &tile.getMappedRasterTiles().emplace_back(
getPlaceholderTile(placeholder),
activatedOverlay.getPlaceholderTile(),
textureCoordinateIndex);
}
}

View File

@ -1,28 +1,20 @@
#include "EmptyRasterOverlayTileProvider.h"
#include <Cesium3DTilesSelection/LoadedTileEnumerator.h>
#include <Cesium3DTilesSelection/RasterMappedTo3DTile.h>
#include <Cesium3DTilesSelection/RasterOverlayCollection.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TilesetExternals.h>
#include <CesiumAsync/Future.h>
#include <Cesium3DTilesSelection/TilesetOptions.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <fmt/format.h>
#include <glm/ext/vector_double2.hpp>
#include <nonstd/expected.hpp>
#include <spdlog/spdlog.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <utility>
#include <vector>
using namespace CesiumGeometry;
@ -32,16 +24,6 @@ using namespace CesiumUtility;
namespace Cesium3DTilesSelection {
namespace {
// We use these to avoid a heap allocation just to return empty vectors.
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlay>>
emptyOverlays{};
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>>
emptyTileProviders{};
} // namespace
RasterOverlayCollection::RasterOverlayCollection(
const LoadedTileEnumerator& loadedTiles,
const TilesetExternals& externals,
@ -49,17 +31,13 @@ RasterOverlayCollection::RasterOverlayCollection(
: _loadedTiles(loadedTiles),
_externals{externals},
_ellipsoid(ellipsoid),
_pOverlays(nullptr) {}
_activatedOverlays() {}
RasterOverlayCollection::~RasterOverlayCollection() noexcept {
if (this->_pOverlays) {
OverlayList& list = *this->_pOverlays;
if (!list.overlays.empty()) {
for (int64_t i = static_cast<int64_t>(list.overlays.size() - 1); i >= 0;
--i) {
this->remove(list.overlays[static_cast<size_t>(i)].get());
}
}
for (int64_t i = static_cast<int64_t>(this->_activatedOverlays.size()) - 1;
i >= 0;
--i) {
this->remove(this->_activatedOverlays[static_cast<size_t>(i)]);
}
}
@ -68,36 +46,26 @@ void RasterOverlayCollection::setLoadedTileEnumerator(
this->_loadedTiles = loadedTiles;
}
const std::vector<CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::ActivatedRasterOverlay>>&
RasterOverlayCollection::getActivatedOverlays() const noexcept {
return this->_activatedOverlays;
}
void RasterOverlayCollection::add(
const CesiumUtility::IntrusivePointer<RasterOverlay>& pOverlay) {
// CESIUM_TRACE_USE_TRACK_SET(this->_loadingSlots);
const IntrusivePointer<const RasterOverlay>& pOverlay) noexcept {
this->_activatedOverlays.emplace_back(pOverlay->activate(
RasterOverlayExternals{
.pAssetAccessor = this->_externals.pAssetAccessor,
.pPrepareRendererResources =
this->_externals.pPrepareRendererResources,
.asyncSystem = this->_externals.asyncSystem,
.pCreditSystem = this->_externals.pCreditSystem,
.pLogger = this->_externals.pLogger},
this->_ellipsoid));
if (!this->_pOverlays)
this->_pOverlays = new OverlayList();
IntrusivePointer<OverlayList> pList = this->_pOverlays;
pList->overlays.emplace_back(pOverlay);
IntrusivePointer<RasterOverlayTileProvider> pPlaceholder =
pOverlay->createPlaceholder(
this->_externals.asyncSystem,
this->_externals.pAssetAccessor,
this->_ellipsoid);
pList->tileProviders.emplace_back(pPlaceholder);
pList->placeholders.emplace_back(pPlaceholder);
// CESIUM_TRACE_BEGIN_IN_TRACK("createTileProvider");
CesiumAsync::Future<RasterOverlay::CreateTileProviderResult> future =
pOverlay->createTileProvider(
this->_externals.asyncSystem,
this->_externals.pAssetAccessor,
this->_externals.pCreditSystem,
this->_externals.pPrepareRendererResources,
this->_externals.pLogger,
nullptr);
CesiumRasterOverlays::RasterOverlayTile* pPlaceholderTile =
this->_activatedOverlays.back()->getPlaceholderTile();
// Add a placeholder for this overlay to existing geometry tiles.
for (Tile& tile : this->_loadedTiles) {
@ -118,75 +86,44 @@ void RasterOverlayCollection::add(
// `setTileContent` will clear them out if necessary.
if (tile.getContent().isRenderContent() ||
tileState == TileLoadState::ContentLoading) {
tile.getMappedRasterTiles().emplace_back(
pPlaceholder->getTile(Rectangle(), glm::dvec2(0.0)),
-1);
tile.getMappedRasterTiles().emplace_back(pPlaceholderTile, -1);
}
}
}
// This continuation, by capturing pList, keeps the OverlayList from being
// destroyed. But it does not keep the RasterOverlayCollection itself alive.
std::move(future)
.catchInMainThread(
[](const std::exception& e)
-> RasterOverlay::CreateTileProviderResult {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{
RasterOverlayLoadType::Unknown,
nullptr,
fmt::format(
"Error while creating tile provider: {0}",
e.what())});
})
.thenInMainThread([pOverlay,
pList,
pLogger = this->_externals.pLogger,
asyncSystem = this->_externals.asyncSystem](
RasterOverlay::CreateTileProviderResult&& result) {
IntrusivePointer<RasterOverlayTileProvider> pProvider = nullptr;
if (result) {
pProvider = *result;
} else {
// Report error creating the tile provider.
const RasterOverlayLoadFailureDetails& failureDetails =
result.error();
SPDLOG_LOGGER_ERROR(pLogger, failureDetails.message);
if (pOverlay->getOptions().loadErrorCallback) {
pOverlay->getOptions().loadErrorCallback(failureDetails);
}
// Create a tile provider that does not provide any tiles at all.
pProvider = new EmptyRasterOverlayTileProvider(pOverlay, asyncSystem);
}
auto it =
std::find(pList->overlays.begin(), pList->overlays.end(), pOverlay);
// Find the overlay's current location in the list.
// It's possible it has been removed completely.
if (it != pList->overlays.end()) {
std::int64_t index = it - pList->overlays.begin();
pList->tileProviders[size_t(index)] = pProvider;
}
// CESIUM_TRACE_END_IN_TRACK("createTileProvider");
});
}
void RasterOverlayCollection::remove(
const CesiumUtility::IntrusivePointer<RasterOverlay>& pOverlay) noexcept {
if (!this->_pOverlays)
const IntrusivePointer<const RasterOverlay>& pOverlay) noexcept {
auto it = std::find_if(
this->_activatedOverlays.begin(),
this->_activatedOverlays.end(),
[pOverlay](
const IntrusivePointer<ActivatedRasterOverlay>& pCheck) noexcept {
return &pCheck->getOverlay() == pOverlay;
});
if (it == this->_activatedOverlays.end()) {
return;
}
this->remove(*it);
}
void RasterOverlayCollection::remove(
const IntrusivePointer<RasterOverlay>& pOverlay) noexcept {
this->remove(IntrusivePointer<const RasterOverlay>(pOverlay));
}
void RasterOverlayCollection::remove(
const IntrusivePointer<ActivatedRasterOverlay>& pActivated) noexcept {
// Remove all mappings of this overlay to geometry tiles.
auto removeCondition = [pOverlay](
const RasterMappedTo3DTile& mapped) noexcept {
return (
(mapped.getLoadingTile() &&
pOverlay == &mapped.getLoadingTile()->getTileProvider().getOwner()) ||
(mapped.getReadyTile() &&
pOverlay == &mapped.getReadyTile()->getTileProvider().getOwner()));
};
auto removeCondition =
[pActivated](const RasterMappedTo3DTile& mapped) noexcept {
return (
(mapped.getLoadingTile() &&
pActivated == &mapped.getLoadingTile()->getActivatedOverlay()) ||
(mapped.getReadyTile() &&
pActivated == &mapped.getReadyTile()->getActivatedOverlay()));
};
auto pPrepareRenderResources =
this->_externals.pPrepareRendererResources.get();
@ -205,133 +142,121 @@ void RasterOverlayCollection::remove(
mapped.erase(firstToRemove, mapped.end());
}
OverlayList& list = *this->_pOverlays;
CESIUM_ASSERT(list.overlays.size() == list.tileProviders.size());
CESIUM_ASSERT(list.overlays.size() == list.placeholders.size());
auto it = std::find_if(
list.overlays.begin(),
list.overlays.end(),
[pOverlay](const IntrusivePointer<RasterOverlay>& pCheck) noexcept {
return pCheck == pOverlay;
});
if (it == list.overlays.end()) {
auto it = std::find(
this->_activatedOverlays.begin(),
this->_activatedOverlays.end(),
pActivated);
if (it == this->_activatedOverlays.end()) {
return;
}
int64_t index = it - list.overlays.begin();
list.overlays.erase(list.overlays.begin() + index);
list.tileProviders.erase(list.tileProviders.begin() + index);
list.placeholders.erase(list.placeholders.begin() + index);
this->_activatedOverlays.erase(it);
}
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlay>>&
RasterOverlayCollection::getOverlays() const {
if (!this->_pOverlays)
return emptyOverlays;
std::vector<CesiumGeospatial::Projection>
RasterOverlayCollection::addTileOverlays(
Tile& tile,
const TilesetOptions& tilesetOptions) noexcept {
// When a tile temporarily fails to load, it may still
// have mapped raster tiles, so clear them here
tile.getMappedRasterTiles().clear();
return this->_pOverlays->overlays;
}
std::vector<CesiumGeospatial::Projection> projections;
const CesiumGeospatial::Ellipsoid& ellipsoid = tilesetOptions.ellipsoid;
/**
* @brief Gets the tile providers in this collection. Each tile provider
* corresponds with the overlay at the same position in the collection
* returned by {@link getOverlays}.
*/
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>>&
RasterOverlayCollection::getTileProviders() const {
if (!this->_pOverlays)
return emptyTileProviders;
for (size_t i = 0; i < this->_activatedOverlays.size(); ++i) {
ActivatedRasterOverlay& activatedOverlay = *this->_activatedOverlays[i];
return this->_pOverlays->tileProviders;
}
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>>&
RasterOverlayCollection::getPlaceholderTileProviders() const {
if (!this->_pOverlays)
return emptyTileProviders;
return this->_pOverlays->placeholders;
}
RasterOverlayTileProvider* RasterOverlayCollection::findTileProviderForOverlay(
RasterOverlay& overlay) noexcept {
// Call the const version
const RasterOverlayTileProvider* pResult = this->findTileProviderForOverlay(
const_cast<const RasterOverlay&>(overlay));
return const_cast<RasterOverlayTileProvider*>(pResult);
}
const RasterOverlayTileProvider*
RasterOverlayCollection::findTileProviderForOverlay(
const RasterOverlay& overlay) const noexcept {
if (!this->_pOverlays)
return nullptr;
const auto& overlays = this->_pOverlays->overlays;
const auto& tileProviders = this->_pOverlays->tileProviders;
CESIUM_ASSERT(overlays.size() == tileProviders.size());
for (size_t i = 0; i < overlays.size() && i < tileProviders.size(); ++i) {
if (overlays[i].get() == &overlay)
return tileProviders[i].get();
RasterMappedTo3DTile* pMapped = RasterMappedTo3DTile::mapOverlayToTile(
tilesetOptions.maximumScreenSpaceError,
activatedOverlay,
tile,
projections,
ellipsoid);
if (pMapped) {
// Try to load now, but if the mapped raster tile is a placeholder this
// won't do anything.
pMapped->loadThrottled();
}
}
return nullptr;
return projections;
}
RasterOverlayTileProvider*
RasterOverlayCollection::findPlaceholderTileProviderForOverlay(
RasterOverlay& overlay) noexcept {
// Call the const version
const RasterOverlayTileProvider* pResult =
this->findPlaceholderTileProviderForOverlay(
const_cast<const RasterOverlay&>(overlay));
return const_cast<RasterOverlayTileProvider*>(pResult);
}
TileRasterOverlayStatus RasterOverlayCollection::updateTileOverlays(
Tile& tile,
const TilesetOptions& tilesetOptions) noexcept {
TileRasterOverlayStatus result{};
const RasterOverlayTileProvider*
RasterOverlayCollection::findPlaceholderTileProviderForOverlay(
const RasterOverlay& overlay) const noexcept {
if (!this->_pOverlays)
return nullptr;
std::vector<RasterMappedTo3DTile>& rasterTiles = tile.getMappedRasterTiles();
const auto& overlays = this->_pOverlays->overlays;
const auto& placeholders = this->_pOverlays->placeholders;
for (size_t i = 0; i < rasterTiles.size(); ++i) {
RasterMappedTo3DTile& mappedRasterTile = rasterTiles[i];
CESIUM_ASSERT(overlays.size() == placeholders.size());
RasterOverlayTile* pLoadingTile = mappedRasterTile.getLoadingTile();
if (pLoadingTile &&
pLoadingTile->getState() == RasterOverlayTile::LoadState::Placeholder) {
ActivatedRasterOverlay& activated = pLoadingTile->getActivatedOverlay();
for (size_t i = 0; i < overlays.size() && i < placeholders.size(); ++i) {
if (overlays[i].get() == &overlay)
return placeholders[i].get();
// Try to replace this placeholder with real tiles.
if (activated.getTileProvider() != nullptr) {
// Remove the existing placeholder mapping
rasterTiles.erase(
rasterTiles.begin() +
static_cast<std::vector<RasterMappedTo3DTile>::difference_type>(i));
// Add a new mapping.
std::vector<CesiumGeospatial::Projection> missingProjections;
RasterMappedTo3DTile::mapOverlayToTile(
tilesetOptions.maximumScreenSpaceError,
activated,
tile,
missingProjections,
tilesetOptions.ellipsoid);
if (!missingProjections.empty()) {
if (!result.firstIndexWithMissingProjection)
result.firstIndexWithMissingProjection = i;
break;
}
--i;
}
continue;
}
const RasterOverlayTile::MoreDetailAvailable moreDetailAvailable =
mappedRasterTile.update(
*this->_externals.pPrepareRendererResources,
tile);
if (moreDetailAvailable == RasterOverlayTile::MoreDetailAvailable::Yes &&
!result.firstIndexWithMoreDetailAvailable) {
result.firstIndexWithMoreDetailAvailable = i;
} else if (
moreDetailAvailable ==
RasterOverlayTile::MoreDetailAvailable::Unknown &&
!result.firstIndexWithUnknownAvailability) {
result.firstIndexWithUnknownAvailability = i;
}
}
return nullptr;
return result;
}
RasterOverlayCollection::const_iterator
RasterOverlayCollection::begin() const noexcept {
if (!this->_pOverlays)
return emptyOverlays.begin();
return this->_pOverlays->overlays.begin();
return const_iterator(GetOverlayFunctor{}, this->_activatedOverlays.begin());
}
RasterOverlayCollection::const_iterator
RasterOverlayCollection::end() const noexcept {
if (!this->_pOverlays)
return emptyOverlays.end();
return this->_pOverlays->overlays.end();
return const_iterator(GetOverlayFunctor{}, this->_activatedOverlays.end());
}
size_t RasterOverlayCollection::size() const noexcept {
if (!this->_pOverlays)
return 0;
return this->_pOverlays->overlays.size();
return this->_activatedOverlays.size();
}
} // 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>
@ -38,12 +41,14 @@
#include <CesiumGltf/Image.h>
#include <CesiumGltfContent/GltfUtilities.h>
#include <CesiumGltfReader/GltfReader.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayDetails.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#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>
@ -70,6 +75,7 @@
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
@ -351,44 +357,6 @@ void createQuadtreeSubdividedChildren(
ne.setTransform(parent.getTransform());
}
std::vector<CesiumGeospatial::Projection> mapOverlaysToTile(
Tile& tile,
RasterOverlayCollection& overlays,
const TilesetOptions& tilesetOptions) {
// when tile fails temporarily, it may still have mapped raster tiles, so
// clear it here
tile.getMappedRasterTiles().clear();
std::vector<CesiumGeospatial::Projection> projections;
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>>&
tileProviders = overlays.getTileProviders();
const std::vector<CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>>&
placeholders = overlays.getPlaceholderTileProviders();
CESIUM_ASSERT(tileProviders.size() == placeholders.size());
const CesiumGeospatial::Ellipsoid& ellipsoid = tilesetOptions.ellipsoid;
for (size_t i = 0; i < tileProviders.size() && i < placeholders.size(); ++i) {
RasterOverlayTileProvider& tileProvider = *tileProviders[i];
RasterOverlayTileProvider& placeholder = *placeholders[i];
RasterMappedTo3DTile* pMapped = RasterMappedTo3DTile::mapOverlayToTile(
tilesetOptions.maximumScreenSpaceError,
tileProvider,
placeholder,
tile,
projections,
ellipsoid);
if (pMapped) {
// Try to load now, but if the mapped raster tile is a placeholder this
// won't do anything.
pMapped->loadThrottled();
}
}
return projections;
}
// Really, these two methods are risky and could easily result in bugs, but
// they're private methods used only in this class, so it's easier to just tell
// clang-tidy to ignore them.
@ -592,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");
@ -618,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(
@ -630,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(
@ -664,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);
@ -682,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(
@ -696,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(
@ -706,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()),
@ -734,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(
@ -752,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),
@ -779,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()) {
@ -883,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) {
@ -913,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),
@ -940,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()) {
@ -965,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) {
@ -1018,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;
@ -1081,7 +1328,7 @@ void TilesetContentManager::loadTileContent(
// map raster overlay to tile
std::vector<CesiumGeospatial::Projection> projections =
mapOverlaysToTile(tile, this->_overlayCollection, tilesetOptions);
this->_overlayCollection.addTileOverlays(tile, tilesetOptions);
// begin loading tile
notifyTileStartLoading(&tile);
@ -1116,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
@ -1129,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);
});
}
}
@ -1151,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) {
@ -1212,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;
}
@ -1316,9 +1606,9 @@ void TilesetContentManager::waitUntilIdle() {
this->_externals.asyncSystem.dispatchMainThreadTasks();
rasterOverlayTilesLoading = 0;
for (const auto& pTileProvider :
this->_overlayCollection.getTileProviders()) {
rasterOverlayTilesLoading += pTileProvider->getNumberOfTilesLoading();
for (const auto& pActivated :
this->_overlayCollection.getActivatedOverlays()) {
rasterOverlayTilesLoading += pActivated->getNumberOfTilesLoading();
}
}
}
@ -1379,9 +1669,9 @@ int32_t TilesetContentManager::getNumberOfTilesLoaded() const noexcept {
int64_t TilesetContentManager::getTotalDataUsed() const noexcept {
int64_t bytes = this->_tilesDataUsed;
for (const auto& pTileProvider :
this->_overlayCollection.getTileProviders()) {
bytes += pTileProvider->getTileDataBytes();
for (const auto& pActivated :
this->_overlayCollection.getActivatedOverlays()) {
bytes += pActivated->getTileDataBytes();
}
return bytes;
@ -1390,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();
@ -1409,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));
}
@ -1416,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 =
@ -1517,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(),
@ -1533,6 +1868,8 @@ void TilesetContentManager::unregisterTileRequester(
if (it != this->_requesters.end()) {
this->_requesters.erase(it);
}
requester._pTilesetContentManager = nullptr;
}
namespace {
@ -1708,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) {
@ -1726,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 {
@ -1743,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,
@ -1865,76 +2213,32 @@ void TilesetContentManager::updateDoneState(
TileContent& content = tile.getContent();
const TileRenderContent* pRenderContent = content.getRenderContent();
if (pRenderContent) {
bool moreRasterDetailAvailable = false;
bool skippedUnknown = false;
std::vector<RasterMappedTo3DTile>& rasterTiles =
tile.getMappedRasterTiles();
for (size_t i = 0; i < rasterTiles.size(); ++i) {
RasterMappedTo3DTile& mappedRasterTile = rasterTiles[i];
TileRasterOverlayStatus status =
this->_overlayCollection.updateTileOverlays(tile, tilesetOptions);
RasterOverlayTile* pLoadingTile = mappedRasterTile.getLoadingTile();
if (pLoadingTile && pLoadingTile->getState() ==
RasterOverlayTile::LoadState::Placeholder) {
RasterOverlayTileProvider* pProvider =
this->_overlayCollection.findTileProviderForOverlay(
pLoadingTile->getOverlay());
RasterOverlayTileProvider* pPlaceholder =
this->_overlayCollection.findPlaceholderTileProviderForOverlay(
pLoadingTile->getOverlay());
// Try to replace this placeholder with real tiles.
if (pProvider && pPlaceholder && !pProvider->isPlaceholder()) {
// Remove the existing placeholder mapping
rasterTiles.erase(
rasterTiles.begin() +
static_cast<std::vector<RasterMappedTo3DTile>::difference_type>(
i));
--i;
// Add a new mapping.
std::vector<CesiumGeospatial::Projection> missingProjections;
RasterMappedTo3DTile::mapOverlayToTile(
tilesetOptions.maximumScreenSpaceError,
*pProvider,
*pPlaceholder,
tile,
missingProjections,
ellipsoid);
if (!missingProjections.empty()) {
// The mesh doesn't have the right texture coordinates for this
// overlay's projection, so we need to kick it back to the unloaded
// state to fix that.
// In the future, we could add the ability to add the required
// texture coordinates without starting over from scratch.
unloadTileContent(tile);
return;
}
}
continue;
}
const RasterOverlayTile::MoreDetailAvailable moreDetailAvailable =
mappedRasterTile.update(
*this->_externals.pPrepareRendererResources,
tile);
if (moreDetailAvailable ==
RasterOverlayTile::MoreDetailAvailable::Unknown &&
!moreRasterDetailAvailable) {
skippedUnknown = true;
}
moreRasterDetailAvailable |=
moreDetailAvailable == RasterOverlayTile::MoreDetailAvailable::Yes;
if (status.firstIndexWithMissingProjection) {
// The mesh doesn't have the right texture coordinates for this
// overlay's projection, so we need to kick it back to the unloaded
// state to fix that.
// In the future, we could add the ability to add the required
// texture coordinates without starting over from scratch.
unloadTileContent(tile);
return;
}
bool hasMoreDetailAvailable =
status.firstIndexWithMoreDetailAvailable.has_value();
bool hasUnknown = status.firstIndexWithUnknownAvailability.has_value();
bool doSubdivide =
hasMoreDetailAvailable &&
(!hasUnknown || *status.firstIndexWithUnknownAvailability >
*status.firstIndexWithMoreDetailAvailable);
// If this tile still has no children after it's done loading, but it does
// have raster tiles that are not the most detailed available, create fake
// children to hang more detailed rasters on by subdividing this tile.
if (!skippedUnknown && moreRasterDetailAvailable &&
tile.getChildren().empty()) {
if (doSubdivide && tile.getChildren().empty()) {
createQuadtreeSubdividedChildren(ellipsoid, tile, this->_upsampler);
}
} else {
@ -2031,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);
@ -2044,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

@ -101,8 +101,8 @@ Ray createRay(const Cartographic& position, const Ellipsoid& ellipsoid) {
ellipsoid.getMaximumRadius() * rayOriginHeightFraction);
return Ray(
Ellipsoid::WGS84.cartographicToCartesian(startPosition),
-Ellipsoid::WGS84.geodeticSurfaceNormal(startPosition));
ellipsoid.cartographicToCartesian(startPosition),
-ellipsoid.geodeticSurfaceNormal(startPosition));
}
} // namespace

View File

@ -7,6 +7,7 @@
#include <Cesium3DTilesSelection/TilesetFrameState.h>
#include <Cesium3DTilesSelection/TilesetViewGroup.h>
#include <Cesium3DTilesSelection/ViewUpdateResult.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumUtility/Assert.h>
@ -42,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);
@ -60,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 ||
@ -180,11 +183,11 @@ void TilesetViewGroup::finishFrame(
// per-raster overlay credit
const RasterOverlayCollection& overlayCollection = tileset.getOverlays();
for (auto& pTileProvider : overlayCollection.getTileProviders()) {
const std::optional<Credit>& overlayCredit = pTileProvider->getCredit();
if (overlayCredit) {
this->_currentFrameCredits.addCreditReference(overlayCredit.value());
}
for (auto& pActivated : overlayCollection.getActivatedOverlays()) {
if (pActivated->getTileProvider() == nullptr)
continue;
pActivated->getTileProvider()->addCredits(this->_currentFrameCredits);
}
// Add per-tile credits for tiles selected this frame.
@ -256,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

@ -423,6 +423,173 @@ TEST_CASE("Test create layer json terrain loader") {
CHECK(layers[0].tileTemplateUrls[0] == "{z}/{x}/{y}.terrain?v={version}");
CHECK(layers[0].extensionsToRequest == "octvertexnormals-watermask");
}
SUBCASE("Verify custom headers are passed to all HTTP requests") {
// Create a custom asset accessor that tracks headers for each request
class HeaderTrackingAssetAccessor : public CesiumAsync::IAssetAccessor {
public:
HeaderTrackingAssetAccessor(
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>&&
mockRequests)
: mockCompletedRequests{std::move(mockRequests)} {}
CesiumAsync::Future<std::shared_ptr<CesiumAsync::IAssetRequest>>
get(const CesiumAsync::AsyncSystem& asyncSystem,
const std::string& url,
const std::vector<THeader>& headers) override {
// Temporary logging for debugging
spdlog::info("HTTP Request to: {}", url);
spdlog::info("Headers count: {}", headers.size());
for (const auto& header : headers) {
spdlog::info(" {} = {}", header.first, header.second);
}
// Store the headers for this URL
requestHeaders[url] = headers;
// Return the mock request if available
auto it = mockCompletedRequests.find(url);
if (it != mockCompletedRequests.end()) {
return asyncSystem.createResolvedFuture<
std::shared_ptr<CesiumAsync::IAssetRequest>>(it->second);
}
// Return a 404 response if no mock is available
auto pMock404Response = std::make_unique<SimpleAssetResponse>(
static_cast<uint16_t>(404),
"doesn't matter",
CesiumAsync::HttpHeaders{},
std::vector<std::byte>{});
auto pMock404Request = std::make_shared<SimpleAssetRequest>(
"GET",
url,
CesiumAsync::HttpHeaders{},
std::move(pMock404Response));
return asyncSystem
.createResolvedFuture<std::shared_ptr<CesiumAsync::IAssetRequest>>(
pMock404Request);
}
CesiumAsync::Future<std::shared_ptr<CesiumAsync::IAssetRequest>> request(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::string& /* verb */,
const std::string& url,
const std::vector<THeader>& headers,
const std::span<const std::byte>&) override {
return get(asyncSystem, url, headers);
}
void tick() noexcept override {}
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>
mockCompletedRequests;
std::map<std::string, std::vector<THeader>> requestHeaders;
};
// Setup mock files
auto layerJsonPath =
testDataPath / "CesiumTerrainTileJson" / "ParentUrl.tile.json";
auto parentJsonPath =
testDataPath / "CesiumTerrainTileJson" / "Parent.tile.json";
auto tileContentPath =
testDataPath / "CesiumTerrainTileJson" / "tile.terrain";
auto pHeaderTrackingAccessor =
std::make_shared<HeaderTrackingAssetAccessor>(
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>{
{"layer.json", createMockAssetRequest(layerJsonPath)},
{"Parent/layer.json", createMockAssetRequest(parentJsonPath)},
{"0/0/0.terrain?v=1.0.0",
createMockAssetRequest(tileContentPath)}});
TilesetExternals customExternals{
pHeaderTrackingAccessor,
pMockedPrepareRendererResources,
asyncSystem,
pMockedCreditSystem};
// Define custom headers
std::vector<CesiumAsync::IAssetAccessor::THeader> customHeaders = {
{"Authorization", "Bearer test-token-123"},
{"X-Custom-Header", "custom-value"},
{"User-Agent", "CesiumNative-Test/1.0"}};
// **DEBUG POINT 2**: Set breakpoint here to inspect custom headers before
// loader creation
auto loaderFuture = LayerJsonTerrainLoader::createLoader(
customExternals,
{},
"layer.json",
customHeaders);
asyncSystem.dispatchMainThreadTasks();
auto loaderResult = loaderFuture.wait();
// **DEBUG POINT 3**: Set breakpoint here to inspect loader creation results
CHECK(loaderResult.pLoader);
CHECK(loaderResult.pRootTile);
CHECK(!loaderResult.errors);
// Verify that custom headers were passed to all requests
CHECK(pHeaderTrackingAccessor->requestHeaders.size() >= 2);
// **DEBUG POINT 4**: Set breakpoint here to inspect captured headers
auto mainLayerHeaders =
pHeaderTrackingAccessor->requestHeaders.find("layer.json");
REQUIRE(mainLayerHeaders != pHeaderTrackingAccessor->requestHeaders.end());
CHECK(mainLayerHeaders->second.size() == customHeaders.size());
for (size_t i = 0; i < customHeaders.size(); ++i) {
CHECK(mainLayerHeaders->second[i].first == customHeaders[i].first);
CHECK(mainLayerHeaders->second[i].second == customHeaders[i].second);
}
// Check headers for parent layer.json request
auto parentLayerHeaders =
pHeaderTrackingAccessor->requestHeaders.find("Parent/layer.json");
REQUIRE(
parentLayerHeaders != pHeaderTrackingAccessor->requestHeaders.end());
CHECK(parentLayerHeaders->second.size() == customHeaders.size());
for (size_t i = 0; i < customHeaders.size(); ++i) {
CHECK(parentLayerHeaders->second[i].first == customHeaders[i].first);
CHECK(parentLayerHeaders->second[i].second == customHeaders[i].second);
}
// Now test tile content loading with custom headers
// Create a tile and load it with custom headers
Tile tile(loaderResult.pLoader.get());
tile.setTileID(QuadtreeTileID(0, 0, 0));
tile.setBoundingVolume(BoundingRegionWithLooseFittingHeights{
{GlobeRectangle(-Math::OnePi, -Math::PiOverTwo, 0.0, Math::PiOverTwo),
-1000.0,
9000.0,
Ellipsoid::WGS84}});
// Create TileLoadInput with correct constructor parameters
TileLoadInput loadInput(
tile,
{}, // TilesetContentOptions
asyncSystem,
pHeaderTrackingAccessor,
spdlog::default_logger(),
customHeaders,
Ellipsoid::WGS84);
auto tileLoadResultFuture =
loaderResult.pLoader->loadTileContent(loadInput);
asyncSystem.dispatchMainThreadTasks();
auto tileLoadResult = tileLoadResultFuture.wait();
// Verify tile content request used custom headers
auto tileContentHeaders =
pHeaderTrackingAccessor->requestHeaders.find("0/0/0.terrain?v=1.0.0");
REQUIRE(
tileContentHeaders != pHeaderTrackingAccessor->requestHeaders.end());
CHECK(tileContentHeaders->second.size() == customHeaders.size());
for (size_t i = 0; i < customHeaders.size(); ++i) {
CHECK(tileContentHeaders->second[i].first == customHeaders[i].first);
CHECK(tileContentHeaders->second[i].second == customHeaders[i].second);
}
}
}
TEST_CASE("Test load layer json tile content") {

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,29 +1330,18 @@ 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) {}
CesiumAsync::Future<LoadedRasterOverlayImage>
loadTileImage(RasterOverlayTile& overlayTile) override {
loadTileImage(const RasterOverlayTile& overlayTile) override {
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pImage;
CesiumGltf::ImageAsset& image = pImage.emplace();
image.width = 1;
@ -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

@ -306,4 +306,45 @@ TEST_CASE("Tileset height queries") {
0.0,
Math::Epsilon1));
}
SUBCASE("stacked-cubes on custom ellipsoid") {
// This tileset has two cubes on top of each other, each in a different
// tile, so we can test that the height of the top one is returned.
// Relative to the WGS84 ellipsoid, the bottom cube has a height of 78.0
// meters, and the upper cube has a height of 83.0 meters.
std::string url =
"file://" +
Uri::nativePathToUriPath(StringHelpers::toStringUtf8(
(testDataPath / "stacked-cubes" / "tileset.json").u8string()));
CesiumGeospatial::Ellipsoid ellipsoid(
CesiumGeospatial::Ellipsoid::WGS84.getRadii() - glm::dvec3(15.0));
TilesetOptions options;
options.ellipsoid = ellipsoid;
Tileset tileset(externals, url, options);
Cartographic samplePosition = Cartographic::fromDegrees(10.0, 45.0, 0.0);
Future<SampleHeightResult> future =
tileset.sampleHeightMostDetailed({samplePosition});
while (!future.isReady()) {
tileset.loadTiles();
}
SampleHeightResult results = future.waitInMainThread();
CHECK(results.warnings.empty());
REQUIRE(results.positions.size() == 1);
glm::dvec3 rayDirection = -ellipsoid.geodeticSurfaceNormal(samplePosition);
glm::dvec3 difference = glm::dvec3(15.0) * rayDirection;
CHECK(results.sampleSuccess[0]);
CHECK(Math::equalsEpsilon(
results.positions[0].height,
83.0 + glm::length(difference),
0.0,
Math::Epsilon1));
}
}

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

@ -291,6 +291,23 @@ public:
*/
bool dispatchOneMainThreadTask();
/**
* @brief An object that denotes a scope for the current thread acting as the
* "main thread". When this object is constructed, the current thread becomes
* the main thread. When it's destroyed, the current thread is no longer
* treated as the main thread.
*/
using MainThreadScope = CesiumImpl::ImmediateScheduler<
CesiumImpl::QueuedScheduler>::SchedulerScope;
/**
* @brief Enters a scope in which the current thread is treated as the "main
* thread". It is essential that no other thread be acting as the main thread
* at the same time, or undefined behavior will result. The scope continues
* until the returned object is destroyed.
*/
MainThreadScope enterMainThread() const;
/**
* @brief Creates a new thread pool that can be used to run continuations.
*

View File

@ -23,16 +23,39 @@ template <typename T> class SharedAsset;
namespace CesiumAsync {
/**
* @brief The default context passed to \ref SharedAssetDepot factory functions.
*/
struct SharedAssetContext {
/**
* @brief The async system.
*/
AsyncSystem asyncSystem;
/**
* @brief The asset accessor.
*/
std::shared_ptr<IAssetAccessor> pAssetAccessor;
};
/**
* @brief A depot for {@link CesiumUtility::SharedAsset} instances, which are potentially shared between multiple objects.
*
* @tparam TAssetType The type of asset stored in this depot. This should
* be derived from {@link CesiumUtility::SharedAsset}.
* @tparam TAssetKey The key type used to uniquely identify assets in this
* depot.
* @tparam TContext The type of context passed to the factory function when
* creating a new asset. This defaults to \ref SharedAssetContext. This type
* must contain a field named `asyncSystem` of type \ref AsyncSystem.
*/
template <typename TAssetType, typename TAssetKey>
template <
typename TAssetType,
typename TAssetKey,
typename TContext = SharedAssetContext>
class CESIUMASYNC_API SharedAssetDepot
: public CesiumUtility::ReferenceCountedThreadSafe<
SharedAssetDepot<TAssetType, TAssetKey>>,
SharedAssetDepot<TAssetType, TAssetKey, TContext>>,
public CesiumUtility::IDepotOwningAsset<TAssetType> {
public:
/**
@ -47,7 +70,8 @@ public:
*
* Default is 16MiB.
*/
int64_t inactiveAssetSizeLimitBytes = static_cast<int64_t>(16 * 1024 * 1024);
std::atomic<int64_t> inactiveAssetSizeLimitBytes =
static_cast<int64_t>(16 * 1024 * 1024);
/**
* @brief Signature for the callback function that will be called to fetch and
@ -65,8 +89,7 @@ public:
*/
using FactorySignature =
CesiumAsync::Future<CesiumUtility::ResultPointer<TAssetType>>(
const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const TContext& context,
const TAssetKey& key);
/**
@ -84,16 +107,45 @@ public:
* @brief Gets an asset from the depot if it already exists, or creates it
* using the depot's factory if it does not.
*
* @param asyncSystem The async system.
* @param pAssetAccessor The asset accessor to use to download assets, if
* necessary.
* @param context The context to pass to the factory function.
* @param assetKey The key uniquely identifying the asset to get or create.
* @return A shared future that resolves when the asset is ready or fails.
*/
SharedFuture<CesiumUtility::ResultPointer<TAssetType>> getOrCreate(
const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
const TAssetKey& assetKey);
SharedFuture<CesiumUtility::ResultPointer<TAssetType>>
getOrCreate(const TContext& context, const TAssetKey& assetKey);
/**
* @brief Invalidates the previously-cached asset with the given key, so that
* the next call to {@link getOrCreate} will create the asset instead of
* returning the existing one.
*
* Anyone already using the existing asset may continue to do so.
*
* If an asset with the given key does not exist in the depot, this method
* does nothing.
*
* @param assetKey The asset key to invalidate.
* @returns True if the asset was invalidated; false if the asset key does not
* exist in the depot or was already invalidated.
*/
bool invalidate(const TAssetKey& assetKey);
/**
* @brief Invalidates the previously-cached asset, so that the next call to
* {@link getOrCreate} will create the asset instead of returning the existing
* one.
*
* Anyone already using the existing asset may continue to do so.
*
* If the asset is not associated with the depot, or if it has already been
* invalidated, this method does nothing. If another asset with the same key
* already exists in the depot, invalidating this one will not affect it.
*
* @param asset The asset to invalidate.
* @returns True if the asset was invalidated; false if the asset is not owned
* by the depot or was already invalidated.
*/
bool invalidate(TAssetType& asset);
/**
* @brief Returns the total number of distinct assets contained in this depot,
@ -120,7 +172,8 @@ public:
int64_t getInactiveAssetTotalSizeBytes() const;
// Disable copy
void operator=(const SharedAssetDepot<TAssetType, TAssetKey>& other) = delete;
void operator=(
const SharedAssetDepot<TAssetType, TAssetKey, TContext>& other) = delete;
private:
struct LockHolder;
@ -159,6 +212,14 @@ private:
void unmarkDeletionCandidateUnderLock(const TAssetType& asset);
/**
* @brief Invalidates the asset with the given key.
*
* The depot lock must be held when this is called, and it will be released by
* the time this method returns.
*/
bool invalidateUnderLock(LockHolder&& lock, const TAssetKey& assetKey);
/**
* @brief An entry for an asset owned by this depot. This is reference counted
* so that we can keep it alive during async operations.
@ -254,6 +315,11 @@ private:
// list.
int64_t _totalDeletionCandidateMemoryUsage;
// The number of assets that have been invalidated but that have not been
// deleted yet. Such assets hold a pointer to the depot, so the depot must be
// kept alive for their entire lifetime.
int64_t _liveInvalidatedAssets;
// Mutex serializing access to _assets, _assetsByPointer, _deletionCandidates,
// and any AssetEntry owned by this depot.
mutable std::mutex _mutex;
@ -264,23 +330,25 @@ private:
// This instance keeps a reference to itself whenever it is managing active
// assets, preventing it from being destroyed even if all other references to
// it are dropped.
CesiumUtility::IntrusivePointer<SharedAssetDepot<TAssetType, TAssetKey>>
CesiumUtility::IntrusivePointer<
SharedAssetDepot<TAssetType, TAssetKey, TContext>>
_pKeepAlive;
};
template <typename TAssetType, typename TAssetKey>
SharedAssetDepot<TAssetType, TAssetKey>::SharedAssetDepot(
template <typename TAssetType, typename TAssetKey, typename TContext>
SharedAssetDepot<TAssetType, TAssetKey, TContext>::SharedAssetDepot(
std::function<FactorySignature> factory)
: _assets(),
_assetsByPointer(),
_deletionCandidates(),
_totalDeletionCandidateMemoryUsage(0),
_liveInvalidatedAssets(0),
_mutex(),
_factory(std::move(factory)),
_pKeepAlive(nullptr) {}
template <typename TAssetType, typename TAssetKey>
SharedAssetDepot<TAssetType, TAssetKey>::~SharedAssetDepot() {
template <typename TAssetType, typename TAssetKey, typename TContext>
SharedAssetDepot<TAssetType, TAssetKey, TContext>::~SharedAssetDepot() {
// Ideally, when the depot is destroyed, all the assets it owns would become
// independent assets. But this is extremely difficult to manage in a
// thread-safe manner.
@ -302,14 +370,14 @@ SharedAssetDepot<TAssetType, TAssetKey>::~SharedAssetDepot() {
// this destructor from being called except when all of its assets are also
// in the _deletionCandidates list.
CESIUM_ASSERT(this->_liveInvalidatedAssets == 0);
CESIUM_ASSERT(this->_assets.size() == this->_deletionCandidates.size());
}
template <typename TAssetType, typename TAssetKey>
template <typename TAssetType, typename TAssetKey, typename TContext>
SharedFuture<CesiumUtility::ResultPointer<TAssetType>>
SharedAssetDepot<TAssetType, TAssetKey>::getOrCreate(
const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
SharedAssetDepot<TAssetType, TAssetKey, TContext>::getOrCreate(
const TContext& context,
const TAssetKey& assetKey) {
// We need to take care here to avoid two assets starting to load before the
// first asset has added an entry and set its maybePendingAsset field.
@ -324,7 +392,7 @@ SharedAssetDepot<TAssetType, TAssetKey>::getOrCreate(
// Asset is currently loading.
return *entry.maybePendingAsset;
} else {
return asyncSystem.createResolvedFuture(entry.toResultUnderLock())
return context.asyncSystem.createResolvedFuture(entry.toResultUnderLock())
.share();
}
}
@ -338,18 +406,19 @@ SharedAssetDepot<TAssetType, TAssetKey>::getOrCreate(
// So we jump through some hoops here to publish "this thread is working
// on it", then unlock the mutex, and _then_ actually call the factory
// function.
Promise<void> promise = asyncSystem.createPromise<void>();
Promise<void> promise = context.asyncSystem.template createPromise<void>();
// We haven't loaded or started to load this asset yet.
// Let's do that now.
CesiumUtility::IntrusivePointer<SharedAssetDepot<TAssetType, TAssetKey>>
CesiumUtility::IntrusivePointer<
SharedAssetDepot<TAssetType, TAssetKey, TContext>>
pDepot = this;
CesiumUtility::IntrusivePointer<AssetEntry> pEntry = new AssetEntry(assetKey);
auto future =
promise.getFuture()
.thenImmediately([pDepot, pEntry, asyncSystem, pAssetAccessor]() {
return pDepot->_factory(asyncSystem, pAssetAccessor, pEntry->key);
.thenImmediately([pDepot, pEntry, context]() {
return pDepot->_factory(context, pEntry->key);
})
.catchImmediately([](std::exception&& e) {
return CesiumUtility::Result<
@ -401,40 +470,65 @@ SharedAssetDepot<TAssetType, TAssetKey>::getOrCreate(
return sharedFuture;
}
template <typename TAssetType, typename TAssetKey>
size_t SharedAssetDepot<TAssetType, TAssetKey>::getAssetCount() const {
template <typename TAssetType, typename TAssetKey, typename TContext>
bool SharedAssetDepot<TAssetType, TAssetKey, TContext>::invalidate(
const TAssetKey& assetKey) {
LockHolder lock = this->lock();
return this->invalidateUnderLock(std::move(lock), assetKey);
}
template <typename TAssetType, typename TAssetKey, typename TContext>
bool SharedAssetDepot<TAssetType, TAssetKey, TContext>::invalidate(
TAssetType& asset) {
LockHolder lock = this->lock();
auto it = this->_assetsByPointer.find(&asset);
if (it == this->_assetsByPointer.end())
return false;
AssetEntry* pEntry = it->second;
CESIUM_ASSERT(pEntry);
return this->invalidateUnderLock(std::move(lock), pEntry->key);
}
template <typename TAssetType, typename TAssetKey, typename TContext>
size_t
SharedAssetDepot<TAssetType, TAssetKey, TContext>::getAssetCount() const {
LockHolder lock = this->lock();
return this->_assets.size();
}
template <typename TAssetType, typename TAssetKey>
size_t SharedAssetDepot<TAssetType, TAssetKey>::getActiveAssetCount() const {
template <typename TAssetType, typename TAssetKey, typename TContext>
size_t
SharedAssetDepot<TAssetType, TAssetKey, TContext>::getActiveAssetCount() const {
LockHolder lock = this->lock();
return this->_assets.size() - this->_deletionCandidates.size();
}
template <typename TAssetType, typename TAssetKey>
size_t SharedAssetDepot<TAssetType, TAssetKey>::getInactiveAssetCount() const {
template <typename TAssetType, typename TAssetKey, typename TContext>
size_t
SharedAssetDepot<TAssetType, TAssetKey, TContext>::getInactiveAssetCount()
const {
LockHolder lock = this->lock();
return this->_deletionCandidates.size();
}
template <typename TAssetType, typename TAssetKey>
int64_t
SharedAssetDepot<TAssetType, TAssetKey>::getInactiveAssetTotalSizeBytes()
const {
template <typename TAssetType, typename TAssetKey, typename TContext>
int64_t SharedAssetDepot<TAssetType, TAssetKey, TContext>::
getInactiveAssetTotalSizeBytes() const {
LockHolder lock = this->lock();
return this->_totalDeletionCandidateMemoryUsage;
}
template <typename TAssetType, typename TAssetKey>
typename SharedAssetDepot<TAssetType, TAssetKey>::LockHolder
SharedAssetDepot<TAssetType, TAssetKey>::lock() const {
template <typename TAssetType, typename TAssetKey, typename TContext>
typename SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder
SharedAssetDepot<TAssetType, TAssetKey, TContext>::lock() const {
return LockHolder{this};
}
template <typename TAssetType, typename TAssetKey>
void SharedAssetDepot<TAssetType, TAssetKey>::markDeletionCandidate(
template <typename TAssetType, typename TAssetKey, typename TContext>
void SharedAssetDepot<TAssetType, TAssetKey, TContext>::markDeletionCandidate(
const TAssetType& asset,
bool threadOwnsDepotLock) {
if (threadOwnsDepotLock) {
@ -445,9 +539,24 @@ void SharedAssetDepot<TAssetType, TAssetKey>::markDeletionCandidate(
}
}
template <typename TAssetType, typename TAssetKey>
void SharedAssetDepot<TAssetType, TAssetKey>::markDeletionCandidateUnderLock(
const TAssetType& asset) {
template <typename TAssetType, typename TAssetKey, typename TContext>
void SharedAssetDepot<TAssetType, TAssetKey, TContext>::
markDeletionCandidateUnderLock(const TAssetType& asset) {
if (asset._isInvalidated) {
// This asset is no longer tracked by the depot, so delete it.
--this->_liveInvalidatedAssets;
delete &asset;
// If this depot is not managing any live assets, then we no longer need to
// keep it alive.
if (this->_assets.size() == this->_deletionCandidates.size() &&
this->_liveInvalidatedAssets == 0) {
this->_pKeepAlive.reset();
}
return;
}
// Verify that the reference count is still zero.
// See: https://github.com/CesiumGS/cesium-native/issues/1073
if (asset._referenceCount != 0) {
@ -494,13 +603,14 @@ void SharedAssetDepot<TAssetType, TAssetKey>::markDeletionCandidateUnderLock(
// If this depot is not managing any live assets, then we no longer need to
// keep it alive.
if (this->_assets.size() == this->_deletionCandidates.size()) {
if (this->_assets.size() == this->_deletionCandidates.size() &&
this->_liveInvalidatedAssets == 0) {
this->_pKeepAlive.reset();
}
}
template <typename TAssetType, typename TAssetKey>
void SharedAssetDepot<TAssetType, TAssetKey>::unmarkDeletionCandidate(
template <typename TAssetType, typename TAssetKey, typename TContext>
void SharedAssetDepot<TAssetType, TAssetKey, TContext>::unmarkDeletionCandidate(
const TAssetType& asset,
bool threadOwnsDepotLock) {
if (threadOwnsDepotLock) {
@ -511,9 +621,15 @@ void SharedAssetDepot<TAssetType, TAssetKey>::unmarkDeletionCandidate(
}
}
template <typename TAssetType, typename TAssetKey>
void SharedAssetDepot<TAssetType, TAssetKey>::unmarkDeletionCandidateUnderLock(
const TAssetType& asset) {
template <typename TAssetType, typename TAssetKey, typename TContext>
void SharedAssetDepot<TAssetType, TAssetKey, TContext>::
unmarkDeletionCandidateUnderLock(const TAssetType& asset) {
// This asset better not already be invalidated. That would imply this asset
// was resurrected after its reference count hit zero. This should only be
// possible if the asset depot returned a pointer to the asset, which it
// will not do for one that is invalidated.
CESIUM_ASSERT(!asset._isInvalidated);
auto it = this->_assetsByPointer.find(const_cast<TAssetType*>(&asset));
CESIUM_ASSERT(it != this->_assetsByPointer.end());
if (it == this->_assetsByPointer.end()) {
@ -536,9 +652,54 @@ void SharedAssetDepot<TAssetType, TAssetKey>::unmarkDeletionCandidateUnderLock(
this->_pKeepAlive = this;
}
template <typename TAssetType, typename TAssetKey>
template <typename TAssetType, typename TAssetKey, typename TContext>
bool SharedAssetDepot<TAssetType, TAssetKey, TContext>::invalidateUnderLock(
LockHolder&& lock,
const TAssetKey& assetKey) {
auto it = this->_assets.find(assetKey);
if (it == this->_assets.end())
return false;
AssetEntry* pEntry = it->second.get();
CESIUM_ASSERT(pEntry);
// This will remove the asset from the deletion candidates list, if it's
// there.
CesiumUtility::ResultPointer<TAssetType> assetResult =
pEntry->toResultUnderLock();
bool wasInvalidated = false;
if (assetResult.pValue) {
if (!assetResult.pValue->_isInvalidated) {
wasInvalidated = true;
assetResult.pValue->_isInvalidated = true;
++this->_liveInvalidatedAssets;
}
this->_assetsByPointer.erase(assetResult.pValue.get());
}
// Detach the asset from the AssetEntry, so that its lifetime is controlled by
// reference counting.
pEntry->pAsset.release();
// Remove the asset entry. This won't immediately delete the asset, because
// `assetResult` above still holds a reference to it. But once that goes out
// of scope, too, the asset _may_ be destroyed.
this->_assets.erase(it);
// Unlock the mutex before allowing `assetResult` to go out of scope. When it
// goes out of scope, the asset may be destroyed. If it is, that would cause
// us to try to re-enter the lock, which is not allowed.
lock.unlock();
return wasInvalidated;
}
template <typename TAssetType, typename TAssetKey, typename TContext>
CesiumUtility::ResultPointer<TAssetType>
SharedAssetDepot<TAssetType, TAssetKey>::AssetEntry::toResultUnderLock() const {
SharedAssetDepot<TAssetType, TAssetKey, TContext>::AssetEntry::
toResultUnderLock() const {
// This method is called while the calling thread already owns the depot
// mutex. So we must take care not to lock it again, which could happen if
// the asset is currently unreferenced and we naively create an
@ -552,16 +713,17 @@ SharedAssetDepot<TAssetType, TAssetKey>::AssetEntry::toResultUnderLock() const {
return CesiumUtility::ResultPointer<TAssetType>(p, errorsAndWarnings);
}
template <typename TAssetType, typename TAssetKey>
SharedAssetDepot<TAssetType, TAssetKey>::LockHolder::LockHolder(
template <typename TAssetType, typename TAssetKey, typename TContext>
SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::LockHolder(
const CesiumUtility::IntrusivePointer<const SharedAssetDepot>& pDepot_)
: pDepot(pDepot_), lock(pDepot_->_mutex) {}
template <typename TAssetType, typename TAssetKey>
SharedAssetDepot<TAssetType, TAssetKey>::LockHolder::~LockHolder() = default;
template <typename TAssetType, typename TAssetKey, typename TContext>
SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::~LockHolder() =
default;
template <typename TAssetType, typename TAssetKey>
void SharedAssetDepot<TAssetType, TAssetKey>::LockHolder::unlock() {
template <typename TAssetType, typename TAssetKey, typename TContext>
void SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::unlock() {
this->lock.unlock();
}

View File

@ -22,6 +22,10 @@ bool AsyncSystem::dispatchOneMainThreadTask() {
return this->_pSchedulers->mainThread.dispatchZeroOrOneContinuation();
}
AsyncSystem::MainThreadScope AsyncSystem::enterMainThread() const {
return this->_pSchedulers->mainThread.immediate.scope();
}
ThreadPool AsyncSystem::createThreadPool(int32_t numberOfThreads) const {
return ThreadPool(numberOfThreads);
}

View File

@ -238,7 +238,7 @@ CesiumIonAssetAccessor::refreshTokenInMainThread(
SPDLOG_LOGGER_INFO(
this->_pLogger,
"Refreshing Cesium ion token for url {}.",
"Refreshing Cesium ion token for URL {}.",
this->_assetEndpointUrl);
this->_tokenRefreshInProgress =
@ -280,7 +280,7 @@ CesiumIonAssetAccessor::refreshTokenInMainThread(
url = this->_assetEndpointUrl]() {
SPDLOG_LOGGER_INFO(
pLogger,
"Successfuly refreshed Cesium ion token for url {}.",
"Successfully refreshed Cesium ion token for URL {}.",
url);
return update;

View File

@ -25,14 +25,19 @@ public:
int64_t getSizeBytes() const { return int64_t(this->someValue.size()); }
};
IntrusivePointer<SharedAssetDepot<TestAsset, std::string>> createDepot() {
return new SharedAssetDepot<TestAsset, std::string>(
[](const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& /* pAssetAccessor */,
const std::string& assetKey) {
struct JustAsyncSystemContext {
AsyncSystem asyncSystem;
};
IntrusivePointer<
SharedAssetDepot<TestAsset, std::string, JustAsyncSystemContext>>
createDepot() {
return new SharedAssetDepot<TestAsset, std::string, JustAsyncSystemContext>(
[](const JustAsyncSystemContext& context, const std::string& assetKey) {
IntrusivePointer<TestAsset> p = new TestAsset();
p->someValue = assetKey;
return asyncSystem.createResolvedFuture(ResultPointer<TestAsset>(p));
return context.asyncSystem.createResolvedFuture(
ResultPointer<TestAsset>(p));
});
}
@ -42,12 +47,13 @@ TEST_CASE("SharedAssetDepot") {
std::shared_ptr<SimpleTaskProcessor> pTaskProcessor =
std::make_shared<SimpleTaskProcessor>();
AsyncSystem asyncSystem(pTaskProcessor);
JustAsyncSystemContext context{asyncSystem};
SUBCASE("getOrCreate can create assets") {
auto pDepot = createDepot();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(asyncSystem, nullptr, "one").waitInMainThread();
pDepot->getOrCreate(context, "one").waitInMainThread();
REQUIRE(assetOne.pValue != nullptr);
}
@ -56,8 +62,8 @@ TEST_CASE("SharedAssetDepot") {
"the same key") {
auto pDepot = createDepot();
auto futureOne = pDepot->getOrCreate(asyncSystem, nullptr, "one");
auto futureTwo = pDepot->getOrCreate(asyncSystem, nullptr, "one");
auto futureOne = pDepot->getOrCreate(context, "one");
auto futureTwo = pDepot->getOrCreate(context, "one");
ResultPointer<TestAsset> assetOne = futureOne.waitInMainThread();
ResultPointer<TestAsset> assetTwo = futureTwo.waitInMainThread();
@ -70,7 +76,7 @@ TEST_CASE("SharedAssetDepot") {
auto pDepot = createDepot();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(asyncSystem, nullptr, "one").waitInMainThread();
pDepot->getOrCreate(context, "one").waitInMainThread();
CHECK(pDepot->getAssetCount() == 1);
CHECK(pDepot->getActiveAssetCount() == 1);
@ -87,7 +93,7 @@ TEST_CASE("SharedAssetDepot") {
auto pDepot = createDepot();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(asyncSystem, nullptr, "one").waitInMainThread();
pDepot->getOrCreate(context, "one").waitInMainThread();
CHECK(pDepot->getAssetCount() == 1);
CHECK(pDepot->getActiveAssetCount() == 1);
@ -100,7 +106,7 @@ TEST_CASE("SharedAssetDepot") {
CHECK(pDepot->getInactiveAssetCount() == 1);
ResultPointer<TestAsset> assetTwo =
pDepot->getOrCreate(asyncSystem, nullptr, "one").waitInMainThread();
pDepot->getOrCreate(context, "one").waitInMainThread();
CHECK(pDepot->getAssetCount() == 1);
CHECK(pDepot->getActiveAssetCount() == 1);
@ -114,9 +120,9 @@ TEST_CASE("SharedAssetDepot") {
int64_t(std::string("one").size() + 1);
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(asyncSystem, nullptr, "one").waitInMainThread();
pDepot->getOrCreate(context, "one").waitInMainThread();
ResultPointer<TestAsset> assetTwo =
pDepot->getOrCreate(asyncSystem, nullptr, "two").waitInMainThread();
pDepot->getOrCreate(context, "two").waitInMainThread();
assetOne.pValue.reset();
@ -133,12 +139,13 @@ TEST_CASE("SharedAssetDepot") {
SUBCASE("is kept alive until all of its assets are unreferenced") {
auto pDepot = createDepot();
SharedAssetDepot<TestAsset, std::string>* pDepotRaw = pDepot.get();
SharedAssetDepot<TestAsset, std::string, JustAsyncSystemContext>*
pDepotRaw = pDepot.get();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(asyncSystem, nullptr, "one").waitInMainThread();
pDepot->getOrCreate(context, "one").waitInMainThread();
ResultPointer<TestAsset> assetTwo =
pDepot->getOrCreate(asyncSystem, nullptr, "two!!").waitInMainThread();
pDepot->getOrCreate(context, "two!!").waitInMainThread();
pDepot.reset();
@ -151,4 +158,73 @@ TEST_CASE("SharedAssetDepot") {
assetOne.pValue.reset();
}
SUBCASE("recreates invalidated asset") {
auto pDepot = createDepot();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(context, "one").waitInMainThread();
REQUIRE(assetOne.pValue != nullptr);
pDepot->invalidate("one");
ResultPointer<TestAsset> assetOne2 =
pDepot->getOrCreate(context, "one").waitInMainThread();
REQUIRE(assetOne.pValue != assetOne2.pValue);
CHECK(assetOne.pValue->someValue == "one");
CHECK(assetOne2.pValue->someValue == "one");
}
SUBCASE("is kept alive for as long as invalidated assets are alive") {
auto pDepot = createDepot();
SharedAssetDepot<TestAsset, std::string, JustAsyncSystemContext>*
pDepotRaw = pDepot.get();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(context, "one").waitInMainThread();
REQUIRE(assetOne.pValue != nullptr);
pDepot->invalidate("one");
pDepot.reset();
REQUIRE(assetOne.pValue->getDepot() == pDepotRaw);
CHECK(pDepotRaw->getInactiveAssetTotalSizeBytes() == 0);
assetOne.pValue.reset();
}
SUBCASE("invalidated assets don't count against inactive asset size") {
auto pDepot = createDepot();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(context, "one").waitInMainThread();
REQUIRE(assetOne.pValue != nullptr);
assetOne.pValue.reset();
CHECK(pDepot->getInactiveAssetTotalSizeBytes() > 0);
pDepot->invalidate("one");
CHECK(pDepot->getInactiveAssetTotalSizeBytes() == 0);
}
SUBCASE("can invalidate an asset that was never valid") {
auto pDepot = createDepot();
pDepot->invalidate("one");
}
SUBCASE("can invalidate the same asset twice") {
auto pDepot = createDepot();
ResultPointer<TestAsset> assetOne =
pDepot->getOrCreate(context, "one").waitInMainThread();
REQUIRE(assetOne.pValue != nullptr);
pDepot->invalidate("one");
pDepot->invalidate("one");
}
}

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

@ -36,6 +36,7 @@
#include <CesiumGltfContent/SkirtMeshMetadata.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/JsonValue.h>
#include <CesiumUtility/StringHelpers.h>
#include <fmt/format.h>
#include <glm/ext/matrix_double4x4.hpp>
@ -49,6 +50,7 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
@ -290,45 +292,14 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) {
}
}
namespace {
std::string_view trimWhitespace(const std::string_view& s) {
size_t end = s.find_last_not_of(" \t");
if (end == std::string::npos)
return {};
std::string_view trimmedRight = s.substr(0, end + 1);
size_t start = trimmedRight.find_first_not_of(" \t");
if (start == std::string::npos)
return {};
return trimmedRight.substr(start);
}
} // namespace
std::vector<std::string_view>
GltfUtilities::parseGltfCopyright(const std::string_view& s) {
std::vector<std::string_view> result;
if (s.empty())
return result;
size_t start = 0;
auto addPart = [&](size_t end) {
std::string_view trimmed = trimWhitespace(s.substr(start, end - start));
if (!trimmed.empty())
result.emplace_back(std::move(trimmed));
};
for (size_t i = 0, length = s.size(); i < length; ++i) {
if (s[i] == ';') {
addPart(i);
start = i + 1;
}
}
addPart(s.size());
return result;
return CesiumUtility::StringHelpers::splitOnCharacter(
s,
';',
CesiumUtility::StringHelpers::SplitOptions{
.trimWhitespace = true,
.omitEmptyParts = true});
}
namespace {
@ -1188,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

@ -12,6 +12,7 @@
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/IAssetRequest.h>
#include <CesiumAsync/IAssetResponse.h>
#include <CesiumAsync/SharedAssetDepot.h>
#include <CesiumAsync/SharedFuture.h>
#include <CesiumGltf/Buffer.h>
#include <CesiumGltf/BufferView.h>
@ -244,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;
@ -596,8 +597,9 @@ void CesiumGltfReader::GltfReader::postprocessGltf(
} else {
// We have a depot, so fetch this asset via that depot.
return options.pSharedAssetSystem->pImage->getOrCreate(
asyncSystem,
pAssetAccessor,
SharedAssetContext{
.asyncSystem = asyncSystem,
.pAssetAccessor = pAssetAccessor},
assetKey);
}
};
@ -643,8 +645,9 @@ void CesiumGltfReader::GltfReader::postprocessGltf(
} else {
// We have a depot, so fetch this asset via that depot.
return options.pSharedAssetSystem->pExternalMetadataSchema->getOrCreate(
asyncSystem,
pAssetAccessor,
SharedAssetContext{
.asyncSystem = asyncSystem,
.pAssetAccessor = pAssetAccessor},
assetKey);
}
};

View File

@ -1,6 +1,6 @@
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/SharedAssetDepot.h>
#include <CesiumGltf/ImageAsset.h>
#include <CesiumGltf/Schema.h>
#include <CesiumGltfReader/GltfSharedAssetSystem.h>
@ -10,7 +10,6 @@
#include <CesiumUtility/Result.h>
#include <functional>
#include <memory>
using namespace CesiumAsync;
using namespace CesiumGltf;
@ -24,19 +23,17 @@ CesiumUtility::IntrusivePointer<GltfSharedAssetSystem> createDefault() {
new GltfSharedAssetSystem();
p->pImage.emplace(std::function(
[](const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
[](const SharedAssetContext& context,
const NetworkImageAssetDescriptor& key)
-> Future<ResultPointer<ImageAsset>> {
return key.load(asyncSystem, pAssetAccessor);
return key.load(context.asyncSystem, context.pAssetAccessor);
}));
p->pExternalMetadataSchema.emplace(std::function(
[](const AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
[](const SharedAssetContext& context,
const NetworkSchemaAssetDescriptor& key)
-> Future<ResultPointer<Schema>> {
return key.load(asyncSystem, pAssetAccessor);
return key.load(context.asyncSystem, context.pAssetAccessor);
}));
return p;

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

@ -12,6 +12,11 @@ namespace CesiumJsonReader {
*/
class CESIUMJSONREADER_API JsonObjectJsonHandler : public JsonHandler {
public:
/**
* @brief The type of value this handler produces.
*/
using ValueType = CesiumUtility::JsonValue;
JsonObjectJsonHandler() noexcept;
/**

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

@ -46,9 +46,12 @@ target_link_libraries(CesiumRasterOverlays
CesiumGltf
CesiumGltfContent
CesiumGltfReader
CesiumJsonReader
CesiumJsonWriter
CesiumUtility
CesiumVectorData
nonstd::expected-lite
spdlog::spdlog
PRIVATE
tinyxml2::tinyxml2
)

View File

@ -0,0 +1,300 @@
#pragma once
#include <CesiumAsync/Future.h>
#include <CesiumAsync/Promise.h>
#include <CesiumAsync/SharedFuture.h>
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCounted.h>
#include <glm/vec2.hpp>
namespace CesiumRasterOverlays {
class RasterOverlay;
class RasterOverlayExternals;
class RasterOverlayTile;
class RasterOverlayTileProvider;
struct TileProviderAndTile;
} // namespace CesiumRasterOverlays
namespace CesiumRasterOverlays {
/**
* @brief Holds a tile and its corresponding tile provider. Used as the return
* value of @ref ActivatedRasterOverlay::loadTile.
*/
struct TileProviderAndTile {
/** @brief A \ref CesiumUtility::IntrusivePointer to the \ref
* RasterOverlayTileProvider used for this tile. */
CesiumUtility::IntrusivePointer<RasterOverlayTileProvider> pTileProvider;
/** @brief A \ref CesiumUtility::IntrusivePointer to the \ref
* RasterOverlayTile used for this tile. */
CesiumUtility::IntrusivePointer<RasterOverlayTile> pTile;
/**
* @brief Constructs an instance.
* @param pTileProvider_ The tile provider used for this tile.
* @param pTile_ The tile.
*/
TileProviderAndTile(
const CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>&
pTileProvider_,
const CesiumUtility::IntrusivePointer<RasterOverlayTile>&
pTile_) noexcept;
~TileProviderAndTile() noexcept;
/**
* @brief Copy constructor.
*/
TileProviderAndTile(const TileProviderAndTile&) noexcept;
/**
* @brief Copy assignment operator.
*/
TileProviderAndTile& operator=(const TileProviderAndTile&) noexcept;
/**
* @brief Move constructor.
*/
TileProviderAndTile(TileProviderAndTile&&) noexcept;
/**
* @brief Move assignment operator.
*/
TileProviderAndTile& operator=(TileProviderAndTile&&) noexcept;
};
/**
* @brief A @ref RasterOverlay that has been activated for use. While a
* @ref RasterOverlayTileProvider can be used directly to load images,
* this class provides additional functionality for managing @ref
* RasterOverlayTile lifecycle and state.
*
* To create an instance of this class, call @ref RasterOverlay::activate.
*/
class CESIUMRASTEROVERLAYS_API ActivatedRasterOverlay
: public CesiumUtility::ReferenceCountedNonThreadSafe<
ActivatedRasterOverlay> {
public:
/**
* @brief Constructs a new instance.
*
* Consider calling @ref RasterOverlay::activate instead of using the
* constructor directly.
*
* @param externals The external interfaces to use.
* @param pOverlay The overlay to activate.
* @param ellipsoid The @ref CesiumGeospatial::Ellipsoid.
*/
ActivatedRasterOverlay(
const RasterOverlayExternals& externals,
const CesiumUtility::IntrusivePointer<const RasterOverlay>& pOverlay,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) noexcept;
/**
* @brief Destroys the instance. Use @ref addReference and
* @ref releaseReference instead of destroying this instance directly.
*/
~ActivatedRasterOverlay() noexcept;
/**
* @brief Gets a shared future that resolves when this instance is ready to
* provide tiles.
*
* It is safe to call @ref getTile before this future resolves, but the
* returned tile will be a placeholder.
*/
CesiumAsync::SharedFuture<void>& getReadyEvent();
/**
* @brief Gets the @ref RasterOverlay that was activated to create this
* instance.
*/
const CesiumRasterOverlays::RasterOverlay& getOverlay() const noexcept;
/**
* @brief Gets the tile provider created for this activated overlay. This will
* be `nullptr` before `getReadyEvent` resolves.
*/
const CesiumRasterOverlays::RasterOverlayTileProvider*
getTileProvider() const noexcept;
/** @copydoc getTileProvider */
CesiumRasterOverlays::RasterOverlayTileProvider* getTileProvider() noexcept;
/**
* @brief Sets the tile provider for this activated overlay.
*
* It is usually unnecessary to call this method because
* @ref RasterOverlay::activate will call it automatically at the
* appropriate time.
*
* Calling this method will resolve the @ref getReadyEvent.
*
* @param pTileProvider The tile provider. This must not be `nullptr`.
*/
void setTileProvider(
const CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>&
pTileProvider);
/**
* @brief Gets the placeholder tile provider.
*
* The placeholder may be used prior to @ref getReadyEvent resolving,
* but it will create placeholder tiles only.
*/
const CesiumRasterOverlays::RasterOverlayTileProvider*
getPlaceholderTileProvider() const noexcept;
/** @copydoc getPlaceholderTileProvider */
CesiumRasterOverlays::RasterOverlayTileProvider*
getPlaceholderTileProvider() noexcept;
/**
* @brief Gets the placeholder tile created by the @ref
* getPlaceholderTileProvider.
*/
const CesiumRasterOverlays::RasterOverlayTile*
getPlaceholderTile() const noexcept;
/** @copydoc getPlaceholderTile */
CesiumRasterOverlays::RasterOverlayTile* getPlaceholderTile() noexcept;
/**
* @brief Returns a new @ref RasterOverlayTile with the given
* specifications.
*
* The returned tile will not start loading immediately. To start loading,
* call @ref ActivatedRasterOverlay::loadTile or
* @ref ActivatedRasterOverlay::loadTileThrottled.
*
* @param rectangle The rectangle that the returned image must cover. It is
* allowed to cover a slightly larger rectangle in order to maintain pixel
* alignment. It may also cover a smaller rectangle when the overlay itself
* does not cover the entire rectangle.
* @param targetScreenPixels The maximum number of pixels on the screen that
* this tile is meant to cover. The overlay image should be approximately this
* many pixels divided by the @ref
* RasterOverlayOptions::maximumScreenSpaceError in order to achieve the
* desired level-of-detail, but it does not need to be exactly this size.
* @return The tile.
*/
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlayTile>
getTile(
const CesiumGeometry::Rectangle& rectangle,
const glm::dvec2& targetScreenPixels);
/**
* @brief Gets the number of bytes of tile data that are currently loaded.
*/
int64_t getTileDataBytes() const noexcept;
/**
* @brief Returns the number of tiles that are currently loading.
*/
uint32_t getNumberOfTilesLoading() const noexcept;
/**
* @brief Removes a no-longer-referenced tile from this provider's cache and
* deletes it.
*
* This function is not supposed to be called by client. Calling this method
* on a tile with a reference count greater than 0 will result in undefined
* behavior.
*
* @param pTile The tile, which must have no oustanding references.
*/
void removeTile(RasterOverlayTile* pTile) noexcept;
/**
* @brief Loads a tile immediately, without throttling requests.
*
* If the tile is not in the `RasterOverlayTile::LoadState::Unloaded` state,
* this method returns without doing anything. Otherwise, it puts the tile
* into the `RasterOverlayTile::LoadState::Loading` state and begins the
* asynchronous process to load the tile. When the process completes, the tile
* will be in the `RasterOverlayTile::LoadState::Loaded` or
* `RasterOverlayTile::LoadState::Failed` state.
*
* Calling this method on many tiles at once can result in very slow
* performance. Consider using @ref loadTileThrottled instead.
*
* @param tile The tile to load.
* @return A future that, when the tile is loaded, resolves to the loaded tile
* and the tile provider that loaded it.
*/
CesiumAsync::Future<TileProviderAndTile> loadTile(RasterOverlayTile& tile);
/**
* @brief Loads a tile, unless there are too many tile loads already in
* progress.
*
* If the tile is not in the `Tile::LoadState::Unloading` state, this method
* returns true without doing anything. If too many tile loads are
* already in flight, it returns false without doing anything. Otherwise, it
* puts the tile into the `RasterOverlayTile::LoadState::Loading` state,
* begins the asynchronous process to load the tile, and returns true. When
* the process completes, the tile will be in the
* `RasterOverlayTile::LoadState::Loaded` or
* `RasterOverlayTile::LoadState::Failed` state.
*
* The number of allowable simultaneous tile requests is provided in the
* @ref RasterOverlayOptions::maximumSimultaneousTileLoads property of
* @ref RasterOverlay::getOptions.
*
* @param tile The tile to load.
* @returns True if the tile load process is started or is already complete,
* false if the load could not be started because too many loads are already
* in progress.
*/
bool loadTileThrottled(RasterOverlayTile& tile);
private:
CesiumAsync::Future<TileProviderAndTile>
doLoad(RasterOverlayTile& tile, bool isThrottledLoad);
/**
* @brief Begins the process of loading of a tile.
*
* This method should be called at the beginning of the tile load process.
*
* @param isThrottledLoad True if the load was originally throttled.
*/
void beginTileLoad(bool isThrottledLoad) noexcept;
/**
* @brief Finalizes loading of a tile.
*
* This method should be called at the end of the tile load process,
* no matter whether the load succeeded or failed.
*
* @param isThrottledLoad True if the load was originally throttled.
*/
void finalizeTileLoad(bool isThrottledLoad) noexcept;
CesiumUtility::IntrusivePointer<const CesiumRasterOverlays::RasterOverlay>
_pOverlay;
CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>
_pPlaceholderTileProvider;
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlayTile>
_pPlaceholderTile;
CesiumUtility::IntrusivePointer<
CesiumRasterOverlays::RasterOverlayTileProvider>
_pTileProvider;
int64_t _tileDataBytes;
int32_t _totalTilesCurrentlyLoading;
int32_t _throttledTilesCurrentlyLoading;
CesiumAsync::Promise<void> _readyPromise;
CesiumAsync::SharedFuture<void> _readyEvent;
};
} // namespace CesiumRasterOverlays

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

@ -0,0 +1,300 @@
#pragma once
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumUtility/JsonValue.h>
#include <optional>
#include <string>
namespace CesiumRasterOverlays {
/**
* @brief Holds the parameters for an existing Google Maps Tiles session.
*/
struct GoogleMapTilesExistingSession {
/**
* @brief The Google Map Tiles API key to use.
*/
std::string key;
/**
* @brief The session token value to include in all Map Tiles API requests.
*/
std::string session;
/**
* @brief A string that contains the time (in seconds since the epoch) at
* which the token expires.
*
* Google documents that a session token is valid for two weeks from its
* creation time, but that this policy might change without notice.
*/
std::string expiry;
/**
* @brief The width of the tiles measured in pixels.
*/
uint32_t tileWidth;
/**
* @brief The height of the tiles measured in pixels.
*/
uint32_t tileHeight;
/**
* @brief The image format, which can be either @ref
* GoogleMapTilesImageFormat::png or @ref GoogleMapTilesImageFormat::jpeg.
*/
std::string imageFormat;
/**
* @brief Whether or not the @ref GoogleMapTilesRasterOverlay should show the
* Google Maps logo.
*
* Google 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 Google Maps Tiles API.
*/
std::string apiBaseUrl{"https://tile.googleapis.com/"};
};
/**
* @brief Standard values for @ref GoogleMapTilesNewSessionParameters::mapType.
*/
struct GoogleMapTilesMapType {
/**
* @brief The standard Google Maps painted map tiles.
*/
inline static const std::string roadmap = "roadmap";
/**
* @brief Satellite imagery.
*/
inline static const std::string satellite = "satellite";
/**
* @brief Terrain imagery. When selecting terrain as the map type, you must
* also set the @ref GoogleMapTilesNewSessionParameters::layerTypes property
* to @ref GoogleMapTilesLayerType::layerRoadmap.
*/
inline static const std::string terrain = "terrain";
/**
* @brief Street View panoramas. This is a documented map type supported by
* the service, but it is unlikely to work well as a raster overlay.
*/
inline static const std::string streetview = "streetview";
};
/**
* @brief Standard values for @ref
* GoogleMapTilesNewSessionParameters::layerTypes.
*/
struct GoogleMapTilesLayerType {
/**
* @brief Required if you specify terrain as the map type. Can also be
* optionally overlaid on the satellite map type. Has no effect on roadmap
* tiles.
*/
inline static const std::string layerRoadmap = "layerRoadmap";
/**
* @brief Shows Street View-enabled streets and locations using blue outlines
* on the map.
*/
inline static const std::string layerStreetview = "layerStreetview";
/**
* @brief Displays current traffic conditions.
*/
inline static const std::string layerTraffic = "layerTraffic";
};
/**
* @brief Standard values for @ref
* GoogleMapTilesNewSessionParameters::imageFormat.
*/
struct GoogleMapTilesImageFormat {
/**
* @brief Portable Network Graphics format. This format supports transparency.
*/
inline static const std::string png = "png";
/**
* @brief Joint Photographic Experts Group format. This format doesn't support
* transparency.
*/
inline static const std::string jpeg = "jpeg";
};
/**
* @brief Standard values for @ref GoogleMapTilesNewSessionParameters::scale.
*/
struct GoogleMapTilesScale {
/**
* @brief The default.
*/
inline static const std::string scaleFactor1x = "scaleFactor1x";
/**
* @brief Doubles label size and removes minor feature labels.
*/
inline static const std::string scaleFactor2x = "scaleFactor2x";
/**
* @brief Quadruples label size and removes minor feature labels.
*/
inline static const std::string scaleFactor4x = "scaleFactor4x";
};
/**
* @brief Holds the parameters for starting a new Google Maps Tiles session.
*/
struct GoogleMapTilesNewSessionParameters {
/**
* @brief The Google Map Tiles API key to use.
*/
std::string key;
/**
* @brief The type of base map. See @ref GoogleMapTilesMapType for standard
* values.
*/
std::string mapType{"satellite"};
/**
* @brief An [IETF language
* tag](https://en.wikipedia.org/wiki/IETF_language_tag) that specifies the
* language used to display information on the tiles. For example, `en-US`
* specifies the English language as spoken in the United States.
*/
std::string language{"en-US"};
/**
* @brief A [Common Locale Data Repository](https://cldr.unicode.org/) region
* identifier (two uppercase letters) that represents the physical location of
* the user. For example, `US`.
*/
std::string region{"US"};
/**
* @brief Specifies the file format to return. See @ref
* GoogleMapTilesImageFormat for standard values.
*
* If you don't specify an `imageFormat`, then the best format for the tile is
* chosen automatically.
*/
std::optional<std::string> imageFormat{};
/**
* @brief Scales-up the size of map elements (such as road labels), while
* retaining the tile size and coverage area of the default tile. See @ref
* GoogleMapTilesScale for standard value.
*
* Increasing the scale also reduces the number of labels on the map, which
* reduces clutter.
*/
std::optional<std::string> scale{};
/**
* @brief Specifies whether to return high-resolution tiles.
*
* If the scale-factor is increased, `highDpi` is used to increase the size of
* the tile. Normally, increasing the scale factor enlarges the resulting tile
* into an image of the same size, which lowers quality. With `highDpi`, the
* resulting size is also increased, preserving quality. DPI stands for Dots
* per Inch, and High DPI means the tile renders using more dots per inch than
* normal. If `true`, then the number of pixels in each of the x and y
* dimensions is multiplied by the scale factor (that is , 2x or 4x). The
* coverage area of the tile remains unchanged. This parameter works only with
* @ref scale values of @ref GoogleMapTilesScale::scaleFactor2x or @ref
* GoogleMapTilesScale::scaleFactor4x. It has no effect on @ref
* GoogleMapTilesScale::scaleFactor1x scale tiles.
*/
std::optional<bool> highDpi{};
/**
* @brief An array of values that specifies the layer types added to the map.
* See @ref GoogleMapTilesLayerType for standard values.
*/
std::optional<std::vector<std::string>> layerTypes{};
/**
* @brief An array of JSON style objects that specify the appearance and
* detail level of map features such as roads, parks, and built-up areas.
*
* Styling is used to customize the standard Google base map. The `styles`
* parameter is valid only if the map type is @ref
* GoogleMapTilesMapType::roadmap. For the complete style syntax, see the
* [Style
* Reference](https://developers.google.com/maps/documentation/tile/style-reference).
*/
std::optional<CesiumUtility::JsonValue::Array> styles{};
/**
* @brief A boolean value that specifies whether @ref layerTypes should be
* rendered as a separate overlay, or combined with the base imagery.
*
* When `true`, the base map isn't displayed. If you haven't defined any
* `layerTypes`, then this value is ignored.
*/
std::optional<bool> overlay{};
/**
* @brief The base URL for the Google Maps Tiles API.
*/
std::string apiBaseUrl{"https://tile.googleapis.com/"};
};
/**
* @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 {
public:
/**
* @brief Constructs a new overlay that will start a new Google Maps Tiles
* session with the specified parameters.
*
* @param name The user-given name of this overlay layer.
* @param newSessionParameters The parameters for starting a new session.
* @param overlayOptions The @ref RasterOverlayOptions for this instance.
*/
GoogleMapTilesRasterOverlay(
const std::string& name,
const GoogleMapTilesNewSessionParameters& newSessionParameters,
const RasterOverlayOptions& overlayOptions = {});
/**
* @brief Constructs a new overlay that will use an existing Google Maps Tiles
* session that was previously started.
*
* @param name The user-given name of this overlay layer.
* @param existingSession The parameters for the existing session.
* @param overlayOptions The @ref RasterOverlayOptions for this instance.
*/
GoogleMapTilesRasterOverlay(
const std::string& name,
const GoogleMapTilesExistingSession& existingSession,
const RasterOverlayOptions& overlayOptions = {});
/** @inheritdoc */
virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CreateRasterOverlayTileProviderParameters& parameters)
const override;
private:
CesiumAsync::Future<CreateTileProviderResult> createNewSession(
const CreateRasterOverlayTileProviderParameters& parameters) const;
std::optional<GoogleMapTilesNewSessionParameters> _newSessionParameters;
std::optional<GoogleMapTilesExistingSession> _existingSession;
};
} // namespace CesiumRasterOverlays

View File

@ -1,11 +1,20 @@
#pragma once
#include <CesiumAsync/IAssetRequest.h>
#include <CesiumAsync/NetworkAssetDescriptor.h>
#include <CesiumAsync/SharedAssetDepot.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumUtility/SharedAsset.h>
#include <chrono>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
namespace CesiumRasterOverlays {
@ -36,14 +45,24 @@ public:
const std::string& ionAssetEndpointUrl = "https://api.cesium.com/");
virtual ~IonRasterOverlay() override;
/**
* @brief Gets the additional `options` to be passed to the asset endpoint.
*
* @returns An optional JSON string describing parameters that are specific to
* the asset.
*/
const std::optional<std::string>& getAssetOptions() const noexcept;
/**
* @brief Sets the additional `options` to be passed to the asset endpoint.
*
* @param options An optional JSON string describing parameters that are
* specific to the asset.
*/
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:
@ -72,34 +91,77 @@ protected:
private:
std::string _overlayUrl;
std::string _ionAccessToken;
bool _needsAuthHeader = false;
bool _needsAuthHeader;
std::optional<std::string> _assetOptions;
class TileProvider;
struct AssetEndpointAttribution {
std::string html;
bool collapsible = true;
};
struct ExternalAssetEndpoint {
std::string externalType;
std::string url;
std::string mapStyle;
std::string key;
std::string culture;
std::string accessToken;
std::vector<AssetEndpointAttribution> attributions;
struct ExternalAssetEndpoint
: public CesiumUtility::SharedAsset<ExternalAssetEndpoint> {
ExternalAssetEndpoint() noexcept = default;
~ExternalAssetEndpoint() noexcept = default;
ExternalAssetEndpoint(const ExternalAssetEndpoint&) noexcept = default;
ExternalAssetEndpoint(ExternalAssetEndpoint&&) noexcept = default;
std::chrono::steady_clock::time_point requestTime{};
std::string externalType{};
std::vector<AssetEndpointAttribution> attributions{};
std::shared_ptr<CesiumAsync::IAssetRequest> pRequestThatFailed{};
/** @private */
struct TileMapService {
std::string url;
std::string accessToken;
};
/** @private */
struct Azure2D {
std::string url;
std::string tilesetId;
std::string key;
};
/** @private */
struct Google2D {
std::string url;
std::string key;
std::string session;
std::string expiry;
std::string imageFormat;
uint32_t tileWidth;
uint32_t tileHeight;
};
/** @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;
CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const ExternalAssetEndpoint& endpoint,
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;
using EndpointDepot = CesiumAsync::SharedAssetDepot<
ExternalAssetEndpoint,
CesiumAsync::NetworkAssetDescriptor,
RasterOverlayExternals>;
static CesiumUtility::IntrusivePointer<EndpointDepot> getEndpointCache();
};
} // namespace CesiumRasterOverlays

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,
@ -125,7 +112,7 @@ protected:
private:
virtual CesiumAsync::Future<LoadedRasterOverlayImage>
loadTileImage(RasterOverlayTile& overlayTile) override final;
loadTileImage(const RasterOverlayTile& overlayTile) override final;
struct LoadedQuadtreeImage
: public CesiumUtility::SharedAsset<LoadedQuadtreeImage> {

View File

@ -26,8 +26,11 @@ class CreditSystem;
namespace CesiumRasterOverlays {
class ActivatedRasterOverlay;
class IPrepareRasterOverlayRendererResources;
class RasterOverlayExternals;
class RasterOverlayTileProvider;
struct CreateRasterOverlayTileProviderParameters;
/**
* @brief Options for loading raster overlays.
@ -173,32 +176,32 @@ public:
}
/**
* @brief Gets the credits for this overlay.
* @brief Activates this overlay.
*
* This method is called by a @ref
* Cesium3DTilesSelection::RasterOverlayCollection when an overlay is added to
* it. The returned @ref ActivatedRasterOverlay is used by the collection to
* manage the overlay.
*
* @param externals The external interfaces for use by the raster overlay.
* @param ellipsoid The @ref CesiumGeospatial::Ellipsoid.
* @return The activated overlay.
*/
const std::vector<CesiumUtility::Credit>& getCredits() const noexcept {
return this->_credits;
}
/**
* @brief Gets the credits for this overlay.
*/
std::vector<CesiumUtility::Credit>& getCredits() noexcept {
return this->_credits;
}
CesiumUtility::IntrusivePointer<ActivatedRasterOverlay> activate(
const RasterOverlayExternals& externals,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) const;
/**
* @brief Create a placeholder tile provider can be used in place of the real
* one while {@link createTileProvider} completes asynchronously.
*
* @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 externals The external interfaces for use by the raster overlay.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
* @return The placeholder.
*/
CesiumUtility::IntrusivePointer<RasterOverlayTileProvider> createPlaceholder(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const RasterOverlayExternals& externals,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) const;
@ -217,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

@ -0,0 +1,62 @@
#pragma once
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumUtility/CreditSystem.h>
#include <spdlog/spdlog.h>
#include <memory>
namespace CesiumRasterOverlays {
/**
* @brief External interfaces used by a @ref RasterOverlay.
*/
class CESIUMRASTEROVERLAYS_API RasterOverlayExternals final {
public:
/**
* @brief The @ref CesiumAsync::IAssetAccessor that is used to download
* raster overlay tiles and other assets.
*
* This may only be `nullptr` if the raster overlay does not attempt to
* download any resources.
*/
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor = nullptr;
/**
* @brief The @ref IPrepareRasterOverlayRendererResources that is used to
* create renderer-specific resources for raster overlay tiles.
*
* This may be `nullptr` if the renderer does not need to create any resources
* for raster overlays.
*/
std::shared_ptr<IPrepareRasterOverlayRendererResources>
pPrepareRendererResources = nullptr;
/**
* @brief The async system to use to do work in threads.
*/
CesiumAsync::AsyncSystem asyncSystem;
/**
* @brief The @ref CesiumUtility::CreditSystem that can be used to manage
* credit strings and periodically query which credits to show and which to
* remove from the screen.
*
* While not recommended, this may be `nullptr` if the client does not need to
* receive credits.
*/
std::shared_ptr<CesiumUtility::CreditSystem> pCreditSystem = nullptr;
/**
* @brief A spdlog logger that will receive log messages.
*
* If not specified, defaults to `spdlog::default_logger()`.
*/
std::shared_ptr<spdlog::logger> pLogger = spdlog::default_logger();
};
} // namespace CesiumRasterOverlays

View File

@ -15,6 +15,7 @@ struct Credit;
namespace CesiumRasterOverlays {
class ActivatedRasterOverlay;
class RasterOverlay;
class RasterOverlayTileProvider;
@ -25,7 +26,7 @@ class RasterOverlayTileProvider;
* an associated image, which us used as an imagery overlay
* for tile geometry. The connection between the imagery data
* and the actual tile geometry is established via the
* {@link Cesium3DTilesSelection::RasterMappedTo3DTile} class, which combines a
* @ref Cesium3DTilesSelection::RasterMappedTo3DTile class, which combines a
* raster overlay tile with texture coordinates, to map the
* image on the geometry of a {@link Cesium3DTilesSelection::Tile}.
*/
@ -95,22 +96,22 @@ public:
* The {@link getState} of this instance will always be
* {@link LoadState::Placeholder}.
*
* @param tileProvider The {@link RasterOverlayTileProvider}. This object
* _must_ remain valid for the entire lifetime of the tile. If the tile
* provider is destroyed before the tile, undefined behavior will result.
* @param activatedOverlay The {@link ActivatedRasterOverlay}. This object
* _must_ remain valid for the entire lifetime of the tile. If the activated
* overlay is destroyed before the tile, undefined behavior will result.
*/
RasterOverlayTile(RasterOverlayTileProvider& tileProvider) noexcept;
RasterOverlayTile(ActivatedRasterOverlay& activatedOverlay) noexcept;
/**
* @brief Creates a new instance.
*
* The tile will start in the `Unloaded` state, and will not begin loading
* until {@link RasterOverlayTileProvider::loadTile} or
* {@link RasterOverlayTileProvider::loadTileThrottled} is called.
* until {@link ActivatedRasterOverlay::loadTile} or
* {@link ActivatedRasterOverlay::loadTileThrottled} is called.
*
* @param tileProvider The {@link RasterOverlayTileProvider}. This object
* _must_ remain valid for the entire lifetime of the tile. If the tile
* provider is destroyed before the tile, undefined behavior may result.
* @param activatedOverlay The {@link ActivatedRasterOverlay}. This object
* _must_ remain valid for the entire lifetime of the tile. If the activated
* overlay is destroyed before the tile, undefined behavior will result.
* @param targetScreenPixels The maximum number of pixels on the screen that
* this tile is meant to cover. The overlay image should be approximately this
* many pixels divided by the
@ -122,7 +123,7 @@ public:
* itself does not cover the entire rectangle.
*/
RasterOverlayTile(
RasterOverlayTileProvider& tileProvider,
ActivatedRasterOverlay& activatedOverlay,
const glm::dvec2& targetScreenPixels,
const CesiumGeometry::Rectangle& imageryRectangle) noexcept;
@ -130,26 +131,24 @@ public:
~RasterOverlayTile();
/**
* @brief Returns the {@link RasterOverlayTileProvider} that created this instance.
* @brief Gets the activated overlay that created this instance.
*/
RasterOverlayTileProvider& getTileProvider() noexcept {
return *this->_pTileProvider;
}
ActivatedRasterOverlay& getActivatedOverlay() noexcept;
/** @copydoc getActivatedOverlay */
const ActivatedRasterOverlay& getActivatedOverlay() const noexcept;
/**
* @brief Returns the {@link RasterOverlayTileProvider} that created this instance.
* @brief Returns the {@link RasterOverlayTileProvider} that is responsible
* for loading this tile's image.
*/
const RasterOverlayTileProvider& getTileProvider() const noexcept {
return *this->_pTileProvider;
}
RasterOverlayTileProvider& getTileProvider() noexcept;
/** @copydoc getTileProvider */
const RasterOverlayTileProvider& getTileProvider() const noexcept;
/**
* @brief Returns the {@link RasterOverlay} that created this instance.
*/
RasterOverlay& getOverlay() noexcept;
/**
* @brief Returns the {@link RasterOverlay} that created this instance.
* @brief Gets the {@link RasterOverlay} associated with this instance.
*/
const RasterOverlay& getOverlay() const noexcept;
@ -245,17 +244,24 @@ public:
return this->_moreDetailAvailable;
}
private:
friend class RasterOverlayTileProvider;
/**
* @brief Sets the load state of this tile.
*
* This function is not supposed to be called by clients.
*
* @private
*/
void setState(LoadState newState) noexcept;
private:
friend class ActivatedRasterOverlay;
// This is a raw pointer instead of an IntrusivePointer in order to avoid
// circular references, particularly among a placeholder tile provider and
// placeholder tile. However, to avoid undefined behavior, the tile provider
// is required to outlive the tile. In normal use, the RasterOverlayCollection
// ensures that this is true.
RasterOverlayTileProvider* _pTileProvider;
ActivatedRasterOverlay* _pActivatedOverlay;
glm::dvec2 _targetScreenPixels;
CesiumGeometry::Rectangle _rectangle;
std::vector<CesiumUtility::Credit> _tileCredits;

View File

@ -4,6 +4,7 @@
#include <CesiumGeospatial/Projection.h>
#include <CesiumGltfReader/GltfReader.h>
#include <CesiumRasterOverlays/Library.h>
#include <CesiumRasterOverlays/RasterOverlayExternals.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/ErrorList.h>
@ -15,11 +16,16 @@
#include <optional>
namespace CesiumUtility {
class CreditReferencer;
}
namespace CesiumRasterOverlays {
class RasterOverlay;
class RasterOverlayTile;
class IPrepareRasterOverlayRendererResources;
struct CreateRasterOverlayTileProviderParameters;
/**
* @brief Summarizes the result of loading an image of a {@link RasterOverlay}.
@ -107,8 +113,8 @@ struct LoadTileImageFromUrlOptions {
* a valid 0x0 image. If false, such a response will be reported as an
* error.
*
* {@link RasterOverlayTileProvider::loadTile} and
* {@link RasterOverlayTileProvider::loadTileThrottled} will treat such an
* {@link ActivatedRasterOverlay::loadTile} and
* {@link ActivatedRasterOverlay::loadTileThrottled} will treat such an
* image as "failed" and use the quadtree parent (or ancestor) image
* instead, but will not report any error.
*
@ -121,21 +127,6 @@ struct LoadTileImageFromUrlOptions {
class RasterOverlayTileProvider;
/**
* @brief Holds a tile and its corresponding tile provider. Used as the return
* value of {@link RasterOverlayTileProvider::loadTile}.
*/
struct TileProviderAndTile {
/** @brief A \ref CesiumUtility::IntrusivePointer to the \ref
* RasterOverlayTileProvider used for this tile. */
CesiumUtility::IntrusivePointer<RasterOverlayTileProvider> pTileProvider;
/** @brief A \ref CesiumUtility::IntrusivePointer to the \ref
* RasterOverlayTile used for this tile. */
CesiumUtility::IntrusivePointer<RasterOverlayTile> pTile;
~TileProviderAndTile() noexcept;
};
/**
* @brief Provides individual tiles for a {@link RasterOverlay} on demand.
*
@ -146,54 +137,20 @@ class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider
: public CesiumUtility::ReferenceCountedNonThreadSafe<
RasterOverlayTileProvider> {
public:
/**
* Constructs a placeholder tile provider.
*
* @see RasterOverlayTileProvider::isPlaceholder
*
* @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 ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
*/
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,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) noexcept;
/**
* @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 coverageRectangle The rectangle that bounds all the area covered by
* this overlay, expressed in projected coordinates.
*/
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;
@ -207,187 +164,83 @@ public:
*/
CesiumAsync::SharedFuture<void>& getAsyncDestructionCompleteEvent();
/**
* @brief Returns whether this is a placeholder.
*
* For many types of {@link RasterOverlay}, we can't create a functioning
* `RasterOverlayTileProvider` right away. For example, we may not know the
* bounds of the overlay, or what projection it uses, until after we've
* (asynchronously) loaded a metadata service that gives us this information.
*
* So until that real `RasterOverlayTileProvider` becomes available, we use
* a placeholder. When {@link RasterOverlayTileProvider::getTile} is invoked
* on a placeholder, it returns a {@link RasterOverlayTile} that is also
* a placeholder. And whenever we see a placeholder `RasterOverlayTile` in
* {@link Cesium3DTilesSelection::RasterMappedTo3DTile::update}, we check if the corresponding `RasterOverlay` is
* ready yet. Once it's ready, we remove the placeholder tile and replace
* it with the real tiles.
*
* So the placeholder system gives us a way to defer the mapping of raster
* overlay tiles to geometry tiles until that mapping can be determined.
*/
bool isPlaceholder() const noexcept { return this->_pPlaceholder != nullptr; }
/**
* @brief Returns the {@link RasterOverlay} that created this instance.
*/
RasterOverlay& getOwner() noexcept { return *this->_pOwner; }
RasterOverlay& getOwner() noexcept;
/** @copydoc getOwner */
const RasterOverlay& getOwner() const noexcept { return *this->_pOwner; }
const RasterOverlay& getOwner() const noexcept;
/**
* @brief Get the external interfaces for use by the tile provider.
*/
const RasterOverlayExternals& getExternals() const noexcept;
/**
* @brief Get the system to use for asychronous requests and threaded work.
*/
const std::shared_ptr<CesiumAsync::IAssetAccessor>&
getAssetAccessor() const noexcept {
return this->_pAssetAccessor;
}
getAssetAccessor() const noexcept;
/**
* @brief Get the credit system that receives credits from this tile provider.
*/
const std::shared_ptr<CesiumUtility::CreditSystem>&
getCreditSystem() const noexcept {
return this->_pCreditSystem;
}
getCreditSystem() const noexcept;
/**
* @brief Gets the async system used to do work in threads.
*/
const CesiumAsync::AsyncSystem& getAsyncSystem() const noexcept {
return this->_asyncSystem;
}
const CesiumAsync::AsyncSystem& getAsyncSystem() const noexcept;
/**
* @brief Gets the interface used to prepare raster overlay images for
* rendering.
*/
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
getPrepareRendererResources() const noexcept {
return this->_pPrepareRendererResources;
}
getPrepareRendererResources() const noexcept;
/**
* @brief Gets the logger to which to send messages about the tile provider
* and tiles.
*/
const std::shared_ptr<spdlog::logger>& getLogger() const noexcept {
return this->_pLogger;
}
const std::shared_ptr<spdlog::logger>& getLogger() const noexcept;
/**
* @brief Returns the {@link CesiumGeospatial::Projection} of this instance.
*/
const CesiumGeospatial::Projection& getProjection() const noexcept {
return this->_projection;
}
const CesiumGeospatial::Projection& getProjection() const noexcept;
/**
* @brief Returns the coverage {@link CesiumGeometry::Rectangle} of this
* instance.
*/
const CesiumGeometry::Rectangle& getCoverageRectangle() const noexcept {
return this->_coverageRectangle;
}
const CesiumGeometry::Rectangle& getCoverageRectangle() const noexcept;
/**
* @brief Returns a new {@link RasterOverlayTile} with the given
* specifications.
*
* The returned tile will not start loading immediately. To start loading,
* call {@link RasterOverlayTileProvider::loadTile} or
* {@link RasterOverlayTileProvider::loadTileThrottled}.
*
* @param rectangle The rectangle that the returned image must cover. It is
* allowed to cover a slightly larger rectangle in order to maintain pixel
* alignment. It may also cover a smaller rectangle when the overlay itself
* does not cover the entire rectangle.
* @param targetScreenPixels The maximum number of pixels on the screen that
* this tile is meant to cover. The overlay image should be approximately this
* many pixels divided by the
* {@link RasterOverlayOptions::maximumScreenSpaceError} in order to achieve
* the desired level-of-detail, but it does not need to be exactly this size.
* @return The tile.
* @brief Gets the @ref CesiumUtility::CreditSource that identifies this
* raster overlay's credits with the @ref CesiumUtility::CreditSystem.
*/
CesiumUtility::IntrusivePointer<RasterOverlayTile> getTile(
const CesiumGeometry::Rectangle& rectangle,
const glm::dvec2& targetScreenPixels);
const CesiumUtility::CreditSource& getCreditSource() const noexcept;
/**
* @brief Gets the number of bytes of tile data that are currently loaded.
* @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.
*/
int64_t getTileDataBytes() const noexcept { return this->_tileDataBytes; }
std::vector<CesiumUtility::Credit>& getCredits() noexcept;
/**
* @brief Returns the number of tiles that are currently loading.
* @copydoc getCredits
*/
uint32_t getNumberOfTilesLoading() const noexcept {
CESIUM_ASSERT(this->_totalTilesCurrentlyLoading > -1);
return static_cast<uint32_t>(this->_totalTilesCurrentlyLoading);
}
const std::vector<CesiumUtility::Credit>& getCredits() const noexcept;
/**
* @brief Removes a no-longer-referenced tile from this provider's cache and
* deletes it.
*
* This function is not supposed to be called by client. Calling this method
* in a tile with a reference count greater than 0 will result in undefined
* behavior.
*
* @param pTile The tile, which must have no oustanding references.
*/
void removeTile(RasterOverlayTile* pTile) noexcept;
/**
* @brief Get the per-TileProvider {@link CesiumUtility::Credit} if one exists.
*/
const std::optional<CesiumUtility::Credit>& getCredit() const noexcept {
return _credit;
}
/**
* @brief Loads a tile immediately, without throttling requests.
*
* If the tile is not in the `Tile::LoadState::Unloaded` state, this method
* returns without doing anything. Otherwise, it puts the tile into the
* `Tile::LoadState::Loading` state and begins the asynchronous process
* to load the tile. When the process completes, the tile will be in the
* `Tile::LoadState::Loaded` or `Tile::LoadState::Failed` state.
*
* Calling this method on many tiles at once can result in very slow
* performance. Consider using {@link loadTileThrottled} instead.
*
* @param tile The tile to load.
* @return A future that, when the tile is loaded, resolves to the loaded tile
* and the tile provider that loaded it.
*/
CesiumAsync::Future<TileProviderAndTile> loadTile(RasterOverlayTile& tile);
/**
* @brief Loads a tile, unless there are too many tile loads already in
* progress.
*
* If the tile is not in the `Tile::LoadState::Unloading` state, this method
* returns true without doing anything. If too many tile loads are
* already in flight, it returns false without doing anything. Otherwise, it
* puts the tile into the `Tile::LoadState::Loading` state, begins the
* asynchronous process to load the tile, and returns true. When the process
* completes, the tile will be in the `Tile::LoadState::Loaded` or
* `Tile::LoadState::Failed` state.
*
* The number of allowable simultaneous tile requests is provided in the
* {@link RasterOverlayOptions::maximumSimultaneousTileLoads} property of
* {@link RasterOverlay::getOptions}.
*
* @param tile The tile to load.
* @returns True if the tile load process is started or is already complete,
* false if the load could not be started because too many loads are already
* in progress.
*/
bool loadTileThrottled(RasterOverlayTile& tile);
protected:
/**
* @brief Loads the image for a tile.
*
@ -395,8 +248,22 @@ protected:
* @return A future that resolves to the image or error information.
*/
virtual CesiumAsync::Future<LoadedRasterOverlayImage>
loadTileImage(RasterOverlayTile& overlayTile) = 0;
loadTileImage(const RasterOverlayTile& overlayTile) = 0;
/**
* @brief Adds this tile provider's credits to a credit referencer.
*
* The added credits will be displayed whenever the @ref RasterOverlay that
* owns this tile provider is displayed. To show tile-specific credits, add
* them to @ref LoadedRasterOverlayImage::credits in the instance returned by
* @ref loadTileImage.
*
* @param creditReferencer The credit referencer to which to add credits.
*/
virtual void
addCredits(CesiumUtility::CreditReferencer& creditReferencer) noexcept;
protected:
/**
* @brief Loads an image from a URL and optionally some request headers.
*
@ -412,29 +279,6 @@ protected:
const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers = {},
LoadTileImageFromUrlOptions&& options = {}) const;
private:
CesiumAsync::Future<TileProviderAndTile>
doLoad(RasterOverlayTile& tile, bool isThrottledLoad);
/**
* @brief Begins the process of loading of a tile.
*
* This method should be called at the beginning of the tile load process.
*
* @param isThrottledLoad True if the load was originally throttled.
*/
void beginTileLoad(bool isThrottledLoad) noexcept;
/**
* @brief Finalizes loading of a tile.
*
* This method should be called at the end of the tile load process,
* no matter whether the load succeeded or failed.
*
* @param isThrottledLoad True if the load was originally throttled.
*/
void finalizeTileLoad(bool isThrottledLoad) noexcept;
private:
struct DestructionCompleteDetails {
CesiumAsync::Promise<void> promise;
@ -442,22 +286,11 @@ private:
};
CesiumUtility::IntrusivePointer<RasterOverlay> _pOwner;
CesiumAsync::AsyncSystem _asyncSystem;
std::shared_ptr<CesiumAsync::IAssetAccessor> _pAssetAccessor;
std::shared_ptr<CesiumUtility::CreditSystem> _pCreditSystem;
std::optional<CesiumUtility::Credit> _credit;
std::shared_ptr<IPrepareRasterOverlayRendererResources>
_pPrepareRendererResources;
std::shared_ptr<spdlog::logger> _pLogger;
RasterOverlayExternals _externals;
std::vector<CesiumUtility::Credit> _credits;
CesiumGeospatial::Projection _projection;
CesiumGeometry::Rectangle _coverageRectangle;
CesiumUtility::IntrusivePointer<RasterOverlayTile> _pPlaceholder;
int64_t _tileDataBytes;
int32_t _totalTilesCurrentlyLoading;
int32_t _throttledTilesCurrentlyLoading;
std::optional<DestructionCompleteDetails> _destructionCompleteDetails;
CESIUM_TRACE_DECLARE_TRACK_SET(
_loadingSlots,
"Raster Overlay Tile Loading Slot")
std::shared_ptr<CesiumUtility::CreditSource> _pCreditSource;
};
} // namespace CesiumRasterOverlays

View File

@ -153,10 +153,10 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities {
* this method.
*
* The `target screen pixels` returned here may be further modified by the
* raster overlay's {@link RasterOverlayTileProvider::getTile} method. In particular, it
* will usually be divided by the raster overlay's `maximum screen space
* error` of the raster overlay (not to be confused with the `maximum screen
* space error` of the tileset, mentioned above).
* raster overlay's {@link ActivatedRasterOverlay::getTile} method. In
* particular, it will usually be divided by the raster overlay's `maximum
* screen space error` of the raster overlay (not to be confused with the
* `maximum screen space error` of the tileset, mentioned above).
*
* @param geometricError The geometric error of the tile.
* @param maximumScreenSpaceError The maximum screen-space error used to

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,371 @@
#include <CesiumAsync/Future.h>
#include <CesiumAsync/SharedFuture.h>
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGltf/ImageAsset.h>
#include <CesiumRasterOverlays/ActivatedRasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayExternals.h>
#include <CesiumRasterOverlays/RasterOverlayTile.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Tracing.h>
#include <glm/ext/vector_double2.hpp>
#include <spdlog/logger.h>
#include <any>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <memory>
#include <utility>
#include <vector>
using namespace CesiumGeometry;
using namespace CesiumGeospatial;
using namespace CesiumGltf;
using namespace CesiumUtility;
namespace CesiumRasterOverlays {
ActivatedRasterOverlay::ActivatedRasterOverlay(
const RasterOverlayExternals& externals,
const IntrusivePointer<const RasterOverlay>& pOverlay,
const Ellipsoid& ellipsoid) noexcept
: _pOverlay(pOverlay),
_pPlaceholderTileProvider(
pOverlay->createPlaceholder(externals, ellipsoid)),
_pPlaceholderTile(new RasterOverlayTile(*this)),
_pTileProvider(nullptr),
_tileDataBytes(0),
_totalTilesCurrentlyLoading(0),
_throttledTilesCurrentlyLoading(0),
_readyPromise(externals.asyncSystem.createPromise<void>()),
_readyEvent(this->_readyPromise.getFuture().share()) {}
ActivatedRasterOverlay::~ActivatedRasterOverlay() noexcept {
// Explicitly release the placeholder first, because RasterOverlayTiles must
// be destroyed before the tile provider that created them.
if (this->_pPlaceholderTile) {
CESIUM_ASSERT(this->_pPlaceholderTile->getReferenceCount() == 1);
this->_pPlaceholderTile.reset();
}
}
CesiumAsync::SharedFuture<void>& ActivatedRasterOverlay::getReadyEvent() {
return this->_readyEvent;
}
const RasterOverlay& ActivatedRasterOverlay::getOverlay() const noexcept {
return *this->_pOverlay;
}
const CesiumRasterOverlays::RasterOverlayTileProvider*
ActivatedRasterOverlay::getTileProvider() const noexcept {
return this->_pTileProvider;
}
CesiumRasterOverlays::RasterOverlayTileProvider*
ActivatedRasterOverlay::getTileProvider() noexcept {
return this->_pTileProvider;
}
void ActivatedRasterOverlay::setTileProvider(
const IntrusivePointer<RasterOverlayTileProvider>& pTileProvider) {
CESIUM_ASSERT(pTileProvider != nullptr);
bool hadValue = this->_pTileProvider != nullptr;
this->_pTileProvider = pTileProvider;
if (!hadValue && this->_pTileProvider != nullptr) {
this->_readyPromise.resolve();
}
}
const CesiumRasterOverlays::RasterOverlayTileProvider*
ActivatedRasterOverlay::getPlaceholderTileProvider() const noexcept {
return this->_pPlaceholderTileProvider;
}
CesiumRasterOverlays::RasterOverlayTileProvider*
ActivatedRasterOverlay::getPlaceholderTileProvider() noexcept {
return this->_pPlaceholderTileProvider;
}
const CesiumRasterOverlays::RasterOverlayTile*
ActivatedRasterOverlay::getPlaceholderTile() const noexcept {
return this->_pPlaceholderTile;
}
CesiumRasterOverlays::RasterOverlayTile*
ActivatedRasterOverlay::getPlaceholderTile() noexcept {
return this->_pPlaceholderTile;
}
IntrusivePointer<RasterOverlayTile> ActivatedRasterOverlay::getTile(
const CesiumGeometry::Rectangle& rectangle,
const glm::dvec2& targetScreenPixels) {
if (this->_pTileProvider == nullptr) {
return this->_pPlaceholderTile;
}
if (!rectangle.overlaps(this->_pTileProvider->getCoverageRectangle())) {
return nullptr;
}
return new RasterOverlayTile(*this, targetScreenPixels, rectangle);
}
int64_t ActivatedRasterOverlay::getTileDataBytes() const noexcept {
return this->_tileDataBytes;
}
uint32_t ActivatedRasterOverlay::getNumberOfTilesLoading() const noexcept {
CESIUM_ASSERT(this->_totalTilesCurrentlyLoading > -1);
return static_cast<uint32_t>(this->_totalTilesCurrentlyLoading);
}
void ActivatedRasterOverlay::removeTile(RasterOverlayTile* pTile) noexcept {
CESIUM_ASSERT(pTile->getReferenceCount() == 0);
if (pTile->getImage()) {
this->_tileDataBytes -= pTile->getImage()->sizeBytes;
}
}
CesiumAsync::Future<TileProviderAndTile>
ActivatedRasterOverlay::loadTile(RasterOverlayTile& tile) {
if (!this->_pTileProvider) {
// Refuse to load if the tile provider isn't ready yet.
return this->_pPlaceholderTileProvider->getAsyncSystem()
.createResolvedFuture(
TileProviderAndTile{this->_pPlaceholderTileProvider, nullptr});
}
return this->doLoad(tile, false);
}
bool ActivatedRasterOverlay::loadTileThrottled(RasterOverlayTile& tile) {
if (tile.getState() != RasterOverlayTile::LoadState::Unloaded) {
return true;
}
if (this->_throttledTilesCurrentlyLoading >=
this->_pOverlay->getOptions().maximumSimultaneousTileLoads) {
return false;
}
this->doLoad(tile, true);
return true;
}
namespace {
struct LoadResult {
RasterOverlayTile::LoadState state = RasterOverlayTile::LoadState::Unloaded;
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pImage = nullptr;
CesiumGeometry::Rectangle rectangle = {};
std::vector<Credit> credits = {};
void* pRendererResources = nullptr;
bool moreDetailAvailable = true;
};
/**
* @brief Processes the given `LoadedRasterOverlayImage`, producing a
* `LoadResult`.
*
* This function is intended to be called on the worker thread.
*
* If the given `loadedImage` contains no valid image data, then a
* `LoadResult` with the state `RasterOverlayTile::LoadState::Failed` will be
* returned.
*
* Otherwise, the image data will be passed to
* `IPrepareRasterOverlayRendererResources::prepareRasterInLoadThread`, and the
* function will return a `LoadResult` with the image, the prepared renderer
* resources, and the state `RasterOverlayTile::LoadState::Loaded`.
*
* @param tileId The @ref TileID - only used for logging
* @param pPrepareRendererResources The `IPrepareRasterOverlayRendererResources`
* @param pLogger The logger
* @param loadedImage The `LoadedRasterOverlayImage`
* @param rendererOptions Renderer options
* @return The `LoadResult`
*/
LoadResult createLoadResultFromLoadedImage(
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
LoadedRasterOverlayImage&& loadedImage,
const std::any& rendererOptions) {
if (!loadedImage.pImage) {
loadedImage.errorList.logError(pLogger, "Failed to load image for tile");
LoadResult result;
result.state = RasterOverlayTile::LoadState::Failed;
return result;
}
if (loadedImage.errorList.hasErrors()) {
loadedImage.errorList.logError(
pLogger,
"Errors while loading image for tile");
}
if (!loadedImage.errorList.warnings.empty()) {
loadedImage.errorList.logWarning(
pLogger,
"Warnings while loading image for tile");
}
CesiumGltf::ImageAsset& image = *loadedImage.pImage;
const int32_t bytesPerPixel = image.channels * image.bytesPerChannel;
const int64_t requiredBytes =
static_cast<int64_t>(image.width) * image.height * bytesPerPixel;
if (image.width > 0 && image.height > 0 &&
image.pixelData.size() >= static_cast<size_t>(requiredBytes)) {
CESIUM_TRACE(
"Prepare Raster " + std::to_string(image.width) + "x" +
std::to_string(image.height) + "x" + std::to_string(image.channels) +
"x" + std::to_string(image.bytesPerChannel));
void* pRendererResources = nullptr;
if (pPrepareRendererResources) {
pRendererResources = pPrepareRendererResources->prepareRasterInLoadThread(
image,
rendererOptions);
}
LoadResult result;
result.state = RasterOverlayTile::LoadState::Loaded;
result.pImage = loadedImage.pImage;
result.rectangle = loadedImage.rectangle;
result.credits = std::move(loadedImage.credits);
result.pRendererResources = pRendererResources;
result.moreDetailAvailable = loadedImage.moreDetailAvailable;
return result;
}
LoadResult result;
result.pRendererResources = nullptr;
result.state = RasterOverlayTile::LoadState::Failed;
result.moreDetailAvailable = false;
return result;
}
} // namespace
CesiumAsync::Future<TileProviderAndTile>
ActivatedRasterOverlay::doLoad(RasterOverlayTile& tile, bool isThrottledLoad) {
if (tile.getState() != RasterOverlayTile::LoadState::Unloaded) {
// Already loading or loaded, do nothing.
return this->_pTileProvider->getAsyncSystem().createResolvedFuture(
TileProviderAndTile{this->_pTileProvider, nullptr});
}
// Don't let this tile be destroyed while it's loading.
tile.setState(RasterOverlayTile::LoadState::Loading);
this->beginTileLoad(isThrottledLoad);
// Keep the tile and tile provider alive while the async operation is in
// progress.
IntrusivePointer<RasterOverlayTile> pTile = &tile;
IntrusivePointer<ActivatedRasterOverlay> thiz = this;
return this->_pTileProvider->loadTileImage(tile)
.thenInWorkerThread(
[pPrepareRendererResources =
this->_pTileProvider->getPrepareRendererResources(),
pLogger = this->_pTileProvider->getLogger(),
rendererOptions = this->_pOverlay->getOptions().rendererOptions](
LoadedRasterOverlayImage&& loadedImage) {
return createLoadResultFromLoadedImage(
pPrepareRendererResources,
pLogger,
std::move(loadedImage),
rendererOptions);
})
.thenInMainThread(
[thiz, pTile, isThrottledLoad](LoadResult&& result) noexcept {
pTile->_rectangle = result.rectangle;
pTile->_pRendererResources = result.pRendererResources;
pTile->_pImage = std::move(result.pImage);
pTile->_tileCredits = std::move(result.credits);
pTile->_moreDetailAvailable =
result.moreDetailAvailable
? RasterOverlayTile::MoreDetailAvailable::Yes
: RasterOverlayTile::MoreDetailAvailable::No;
pTile->setState(result.state);
if (pTile->getImage() != nullptr) {
ImageAsset& imageCesium = *pTile->getImage();
// If the image size hasn't been overridden, store the pixelData
// size now. We'll add this number to our total memory usage now,
// and remove it when the tile is later unloaded, and we must use
// the same size in each case.
if (imageCesium.sizeBytes < 0) {
imageCesium.sizeBytes = int64_t(imageCesium.pixelData.size());
}
thiz->_tileDataBytes += imageCesium.sizeBytes;
}
thiz->finalizeTileLoad(isThrottledLoad);
return TileProviderAndTile{thiz->getTileProvider(), pTile};
})
.catchInMainThread(
[thiz, pTile, isThrottledLoad](const std::exception& /*e*/) {
pTile->_pRendererResources = nullptr;
pTile->_pImage = nullptr;
pTile->_tileCredits = {};
pTile->_moreDetailAvailable =
RasterOverlayTile::MoreDetailAvailable::No;
pTile->setState(RasterOverlayTile::LoadState::Failed);
thiz->finalizeTileLoad(isThrottledLoad);
return TileProviderAndTile{thiz->getTileProvider(), pTile};
});
}
void ActivatedRasterOverlay::beginTileLoad(bool isThrottledLoad) noexcept {
++this->_totalTilesCurrentlyLoading;
if (isThrottledLoad) {
++this->_throttledTilesCurrentlyLoading;
}
}
void ActivatedRasterOverlay::finalizeTileLoad(bool isThrottledLoad) noexcept {
--this->_totalTilesCurrentlyLoading;
if (isThrottledLoad) {
--this->_throttledTilesCurrentlyLoading;
}
}
TileProviderAndTile::TileProviderAndTile(
const CesiumUtility::IntrusivePointer<RasterOverlayTileProvider>&
pTileProvider_,
const CesiumUtility::IntrusivePointer<RasterOverlayTile>& pTile_) noexcept
: pTileProvider(pTileProvider_), pTile(pTile_) {}
TileProviderAndTile::~TileProviderAndTile() noexcept {
// Ensure the tile is released before the tile provider.
pTile = nullptr;
pTileProvider = nullptr;
}
TileProviderAndTile::TileProviderAndTile(const TileProviderAndTile&) noexcept =
default;
TileProviderAndTile&
TileProviderAndTile::operator=(const TileProviderAndTile&) noexcept = default;
TileProviderAndTile::TileProviderAndTile(TileProviderAndTile&&) noexcept =
default;
TileProviderAndTile&
TileProviderAndTile::operator=(TileProviderAndTile&&) noexcept = default;
} // namespace CesiumRasterOverlays

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;
@ -188,12 +209,6 @@ protected:
options.allowEmptyImages = true;
options.moreDetailAvailable = tileID.level < this->getMaximumLevel();
options.rectangle = this->getTilingScheme().tileToRectangle(tileID);
std::vector<Credit>& tileCredits = options.credits =
this->getOwner().getCredits();
const std::optional<Credit>& thisCredit = this->getCredit();
if (thisCredit.has_value()) {
tileCredits.push_back(*thisCredit);
}
const CesiumGeospatial::GlobeRectangle tileRectangle =
CesiumGeospatial::unprojectRectangleSimple(
@ -210,7 +225,7 @@ protected:
bingTileLevel <= coverageArea.zoomMax &&
coverageArea.rectangle.computeIntersection(tileRectangle)
.has_value()) {
tileCredits.push_back(creditAndCoverageAreas.credit);
options.credits.push_back(creditAndCoverageAreas.credit);
break;
}
}
@ -286,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() &&
@ -345,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});
}
}
}
@ -360,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,
@ -383,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;
@ -454,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)
@ -515,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,27 +27,17 @@ 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(RasterOverlayTile& overlayTile) override {
loadTileImage(const RasterOverlayTile& overlayTile) override {
LoadedRasterOverlayImage result;
// Indicate that there is no more detail available so that tiles won't get
@ -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,31 +5,22 @@
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <optional>
using namespace CesiumRasterOverlays;
namespace Cesium3DTilesSelection {
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()) {}
CesiumAsync::Future<CesiumRasterOverlays::LoadedRasterOverlayImage>
EmptyRasterOverlayTileProvider::loadTileImage(
CesiumRasterOverlays::RasterOverlayTile& /*overlayTile*/) {
const CesiumRasterOverlays::RasterOverlayTile& /*overlayTile*/) {
return this->getAsyncSystem()
.createResolvedFuture<CesiumRasterOverlays::LoadedRasterOverlayImage>({});
}
} // namespace Cesium3DTilesSelection
} // namespace CesiumRasterOverlays

View File

@ -2,19 +2,20 @@
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
namespace Cesium3DTilesSelection {
namespace CesiumRasterOverlays {
class EmptyRasterOverlayTileProvider
: public CesiumRasterOverlays::RasterOverlayTileProvider {
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>
loadTileImage(CesiumRasterOverlays::RasterOverlayTile& overlayTile) override;
loadTileImage(
const CesiumRasterOverlays::RasterOverlayTile& overlayTile) override;
};
} // namespace Cesium3DTilesSelection
} // namespace CesiumRasterOverlays

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,38 +512,28 @@ 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);
}
virtual CesiumAsync::Future<LoadedRasterOverlayImage>
loadTileImage(RasterOverlayTile& overlayTile) override {
loadTileImage(const RasterOverlayTile& overlayTile) override {
// Choose the texture size according to the geometry screen size and raster
// SSE, but no larger than the maximum texture size.
const RasterOverlayOptions& options = this->getOwner().getOptions();
@ -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)));
});
}

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