Compare commits
459 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5b3b30e0d9 | |
|
|
5745cb84b4 | |
|
|
8666d2a783 | |
|
|
2685803664 | |
|
|
dc53b707d1 | |
|
|
b497c643ae | |
|
|
b8b1417c5b | |
|
|
612352eee9 | |
|
|
f8c4fd7530 | |
|
|
9aea35d9c3 | |
|
|
473f3ccbd5 | |
|
|
54b3b5764c | |
|
|
8f2c1a9f54 | |
|
|
1b4700ecd2 | |
|
|
a5d8e17300 | |
|
|
3bff311dee | |
|
|
ed344cddb3 | |
|
|
fbbaf4452a | |
|
|
fd8aaef4be | |
|
|
32e509eb95 | |
|
|
002d00e3f8 | |
|
|
77fff1077a | |
|
|
a8df93e235 | |
|
|
ced27961e4 | |
|
|
7213eed62f | |
|
|
551c2bb67c | |
|
|
642bd2dbc5 | |
|
|
d6b2882148 | |
|
|
8ede044fc4 | |
|
|
c1b5107758 | |
|
|
9db1f17a11 | |
|
|
c3b7c131b3 | |
|
|
b59305d4dd | |
|
|
8c7ee9507a | |
|
|
65dacbdb1b | |
|
|
436bb9be86 | |
|
|
2757937ea2 | |
|
|
11cf3812d2 | |
|
|
47e8533e18 | |
|
|
e4f46ba4e6 | |
|
|
a911d9f0ac | |
|
|
c71daed34a | |
|
|
cce023d8ae | |
|
|
8d9fc928bb | |
|
|
e554a7d72c | |
|
|
0b5616089e | |
|
|
79a1c7bcdc | |
|
|
4cabc9aaf9 | |
|
|
d5920f6b5d | |
|
|
7a7f50877c | |
|
|
2cecdd9c2d | |
|
|
81bcaf7755 | |
|
|
541f55289c | |
|
|
7a6f14ae86 | |
|
|
3e68c7a1c2 | |
|
|
32ced86a1f | |
|
|
a6a56c9ecf | |
|
|
081906aeff | |
|
|
684b9742d2 | |
|
|
5564afd7cb | |
|
|
d3fb02136d | |
|
|
577ec52e8c | |
|
|
2bdeadac3d | |
|
|
864e328ed8 | |
|
|
9fe157b4e2 | |
|
|
2421678466 | |
|
|
8271d59a3b | |
|
|
ca5104f3b7 | |
|
|
cefcafd82a | |
|
|
e876c133c8 | |
|
|
e9a9415539 | |
|
|
d92188a276 | |
|
|
c5ea386972 | |
|
|
8b86d7f3d7 | |
|
|
3f372f75a4 | |
|
|
77715ec5e5 | |
|
|
198c22c795 | |
|
|
110382a8db | |
|
|
d9d31300d7 | |
|
|
65b6c866a0 | |
|
|
c96485f98c | |
|
|
22e9dbcd7c | |
|
|
f42203eb71 | |
|
|
ccba7b2510 | |
|
|
00c35a1ffc | |
|
|
221611fd21 | |
|
|
58e8bafe71 | |
|
|
eecc1e9411 | |
|
|
5f06bc153e | |
|
|
b23852cddd | |
|
|
d1bffc5d37 | |
|
|
1e9f39df84 | |
|
|
de6f08b7bc | |
|
|
b33640a9ff | |
|
|
0aaaee207e | |
|
|
25cc1e89ca | |
|
|
16be63a245 | |
|
|
ab8de592a4 | |
|
|
f24824b430 | |
|
|
bd1d4eee4a | |
|
|
9c3e6f36fe | |
|
|
6fefe44a38 | |
|
|
5828ffd207 | |
|
|
551174f38d | |
|
|
8d9ee6fee6 | |
|
|
52e5736b4a | |
|
|
216314428b | |
|
|
55bbae20fe | |
|
|
c0de4d1da8 | |
|
|
afec183d53 | |
|
|
12ee3f00b5 | |
|
|
3080d41edb | |
|
|
b800becaae | |
|
|
f66d9dacf6 | |
|
|
077e9f3765 | |
|
|
60faebd4d2 | |
|
|
c9cff48b84 | |
|
|
a0e17767a0 | |
|
|
23ab650a0a | |
|
|
c913278446 | |
|
|
0e53ac995d | |
|
|
a93b082380 | |
|
|
eaf77c07f1 | |
|
|
84a9574e7b | |
|
|
5e554d3638 | |
|
|
e390112e21 | |
|
|
97883d1451 | |
|
|
a72529e9cb | |
|
|
000d27cbb1 | |
|
|
d0427df530 | |
|
|
f6d83fb859 | |
|
|
74e0ba7eb1 | |
|
|
8d3d70a930 | |
|
|
62ef5734da | |
|
|
e4c0150a4d | |
|
|
614458ed9b | |
|
|
b40e3ecbc0 | |
|
|
0656963653 | |
|
|
6778fe613c | |
|
|
410300c26a | |
|
|
c6687f4ddf | |
|
|
a6528aea33 | |
|
|
f54db5f321 | |
|
|
fb0123673e | |
|
|
f245947101 | |
|
|
39c1ac8c62 | |
|
|
167a728aa0 | |
|
|
5d44fd4670 | |
|
|
aa5646dbf5 | |
|
|
49a4409a8f | |
|
|
4f96d33d6b | |
|
|
c89796a0ac | |
|
|
dc10421be2 | |
|
|
7dc335dcf8 | |
|
|
91d2f5a3ca | |
|
|
15af0de635 | |
|
|
d66de9af3b | |
|
|
9787c25d84 | |
|
|
c754a5fdec | |
|
|
cf18fd06b4 | |
|
|
0e6df07ccd | |
|
|
32d275ef0b | |
|
|
a9640afc13 | |
|
|
dbe2c1f15d | |
|
|
7016cdf1b3 | |
|
|
121d9c2678 | |
|
|
af5c118986 | |
|
|
8f8a101aa4 | |
|
|
63f1353457 | |
|
|
7a8e96a857 | |
|
|
b2b9a1cd0d | |
|
|
7210acd3d3 | |
|
|
fa7b2d2bcc | |
|
|
315f5a55c3 | |
|
|
751cf36965 | |
|
|
753e35379b | |
|
|
6b35ee3f87 | |
|
|
7425f5caef | |
|
|
6cbd974f2e | |
|
|
bef3bd23a6 | |
|
|
70a7f91aed | |
|
|
00e8a9729d | |
|
|
d8631d0a6d | |
|
|
af5bf48cc8 | |
|
|
9e0db1b8c9 | |
|
|
05502dd87b | |
|
|
308f87f09a | |
|
|
0efdec7b43 | |
|
|
6da0610f3c | |
|
|
f0de38ccdb | |
|
|
e732d04cb9 | |
|
|
21aa95f481 | |
|
|
ac2ea44bfd | |
|
|
237f51cc38 | |
|
|
33cba63e85 | |
|
|
3b1b0f9e73 | |
|
|
de5ed6e7ba | |
|
|
733bc2c05d | |
|
|
99f1404fe7 | |
|
|
abf28c5ecc | |
|
|
1c13589457 | |
|
|
3ab91770f7 | |
|
|
126a412a79 | |
|
|
29ff862523 | |
|
|
e91de71eeb | |
|
|
e30f39940b | |
|
|
de2621a5e7 | |
|
|
8aa8664a83 | |
|
|
a891d3f996 | |
|
|
602bf3b452 | |
|
|
600e676aff | |
|
|
ef078549b3 | |
|
|
d31196ed1b | |
|
|
e984451f44 | |
|
|
97a9c66788 | |
|
|
77ea05044e | |
|
|
84c00b5db6 | |
|
|
f10b1f98b6 | |
|
|
b43d18683f | |
|
|
ef46820ba4 | |
|
|
dac6fdacb5 | |
|
|
77a5381d4d | |
|
|
4be4c99a3d | |
|
|
2a06cdc3a4 | |
|
|
52c2c78566 | |
|
|
7f7d7eba1a | |
|
|
f9ea77fd91 | |
|
|
063ee500cc | |
|
|
0ca86eed13 | |
|
|
39df3e83dd | |
|
|
44eb767a30 | |
|
|
767209dc70 | |
|
|
f69c37450a | |
|
|
4a3a6ab78a | |
|
|
5daafdc914 | |
|
|
5c50a06892 | |
|
|
6714378f6b | |
|
|
6a41886652 | |
|
|
cb0baa97de | |
|
|
672a44eb96 | |
|
|
e0c37de97d | |
|
|
d515dac685 | |
|
|
ca48199b33 | |
|
|
9b3cb28be0 | |
|
|
e39a3d0b38 | |
|
|
111487d891 | |
|
|
6a7a10a0c7 | |
|
|
522378c35e | |
|
|
8dc2f5fbb5 | |
|
|
5c28e257c0 | |
|
|
e456bcfa6e | |
|
|
51e10da3b7 | |
|
|
4fec544fab | |
|
|
5178fc8466 | |
|
|
588cc665b5 | |
|
|
3b2765be6a | |
|
|
fb3192665f | |
|
|
9bb8a0aab2 | |
|
|
d98cd44bf1 | |
|
|
b0e08b75e1 | |
|
|
4c957652e4 | |
|
|
18572e9f2f | |
|
|
8ceb44cf11 | |
|
|
824c9d797a | |
|
|
457812d3c4 | |
|
|
972a26aaad | |
|
|
9ac9b3f6d6 | |
|
|
0963272a47 | |
|
|
4ba025d672 | |
|
|
39c4a81458 | |
|
|
3f9cdd2cb8 | |
|
|
ee99bb2b21 | |
|
|
f6f7d035fc | |
|
|
bccdda17c0 | |
|
|
6eddef9f18 | |
|
|
2fb112556b | |
|
|
4d711374ec | |
|
|
8bcda4a515 | |
|
|
965e38446f | |
|
|
68f9206141 | |
|
|
984bf1739e | |
|
|
c25a218d4c | |
|
|
bbd606f982 | |
|
|
726b621ca3 | |
|
|
4552af8541 | |
|
|
70861d92a8 | |
|
|
37dc5843c1 | |
|
|
99118d2f8e | |
|
|
50b1530722 | |
|
|
332f1230cd | |
|
|
63522f1464 | |
|
|
d2f2149378 | |
|
|
55a00b9817 | |
|
|
c17f010f45 | |
|
|
dbdd79ca99 | |
|
|
bc74699a46 | |
|
|
cc60025989 | |
|
|
e55517f2aa | |
|
|
f5dcf4b5c0 | |
|
|
f74e96c003 | |
|
|
112dfa6968 | |
|
|
56f2dc8e8b | |
|
|
1aa2788d33 | |
|
|
5de826e8e7 | |
|
|
cda7db7654 | |
|
|
a374ae533a | |
|
|
4c31993899 | |
|
|
8991c8b513 | |
|
|
020a852d05 | |
|
|
45125ca439 | |
|
|
8f1dd4a25a | |
|
|
df527e3e61 | |
|
|
95d2d482bf | |
|
|
2c78104671 | |
|
|
bcf3771f87 | |
|
|
d3eaad1b1c | |
|
|
2ce70e3fc5 | |
|
|
1e96cde79d | |
|
|
2044ee4508 | |
|
|
9189d6a72e | |
|
|
94c4a3c021 | |
|
|
6f095361c9 | |
|
|
d1263ae8f3 | |
|
|
e20012ef05 | |
|
|
ce18f3caab | |
|
|
29ea6f876f | |
|
|
9a0238cbb0 | |
|
|
a6ff55c7e6 | |
|
|
df11774f91 | |
|
|
7f97b89a3c | |
|
|
f58293707c | |
|
|
7bc746033b | |
|
|
ba7ff5d9a4 | |
|
|
ba8782d67a | |
|
|
25ad6e04b7 | |
|
|
5233089008 | |
|
|
cc15117120 | |
|
|
76d13f797e | |
|
|
a6a447c5c5 | |
|
|
bee5d04d2d | |
|
|
17a363824c | |
|
|
ec7bb3f661 | |
|
|
38ba06ecfc | |
|
|
7a8e4c9fee | |
|
|
77c36df0a1 | |
|
|
97a7e9d2d1 | |
|
|
b5fc62f045 | |
|
|
c792367f8f | |
|
|
4d5916ceb2 | |
|
|
00e9d0691d | |
|
|
20caa9e560 | |
|
|
0c2478d1a5 | |
|
|
e8bcd9efc1 | |
|
|
fd116c8011 | |
|
|
0e189db99e | |
|
|
f49954ee2b | |
|
|
a734a85a42 | |
|
|
67a7202277 | |
|
|
d30f3c00c1 | |
|
|
6a6819b7af | |
|
|
9cb588a405 | |
|
|
f4716c85e8 | |
|
|
03efa8a1cf | |
|
|
276b3a698d | |
|
|
1d59866a91 | |
|
|
83fbb305ad | |
|
|
fb2c245ef0 | |
|
|
3f416be76d | |
|
|
e48f24ee9a | |
|
|
ca546ffb72 | |
|
|
3a706aff27 | |
|
|
90d1c96818 | |
|
|
f513a96e39 | |
|
|
8884c086dd | |
|
|
619e180227 | |
|
|
618f285bdc | |
|
|
b7330ea1b5 | |
|
|
a37c73e776 | |
|
|
47b961556b | |
|
|
8987f0ecd7 | |
|
|
6e43196dd0 | |
|
|
de4d12dfe2 | |
|
|
be1812a06a | |
|
|
cf8014d616 | |
|
|
47f07ae907 | |
|
|
e7c5f3d665 | |
|
|
aed82a6bc6 | |
|
|
88c3716272 | |
|
|
1ff50143bc | |
|
|
4796e16f86 | |
|
|
a4f66ed062 | |
|
|
113f8702b9 | |
|
|
3ffbbc5a15 | |
|
|
1e1663bba7 | |
|
|
8329b52169 | |
|
|
a5e925c597 | |
|
|
f465fd9435 | |
|
|
77bbca121a | |
|
|
852d3cc131 | |
|
|
e9f170b926 | |
|
|
2332a727b7 | |
|
|
0f9d3dce1c | |
|
|
f02dd5f114 | |
|
|
99a0aa6f08 | |
|
|
cd2ca6997e | |
|
|
ad640dd72b | |
|
|
57262a6eae | |
|
|
c0631035b9 | |
|
|
f7e116bc7f | |
|
|
5a427f7447 | |
|
|
fc2bc11cf3 | |
|
|
c20dafac57 | |
|
|
f07af52bc6 | |
|
|
a124802e3a | |
|
|
4d2d125fbb | |
|
|
1dbfd68f67 | |
|
|
ca2f95b1ce | |
|
|
20fe18b00b | |
|
|
0e04d023b8 | |
|
|
1b34e31d33 | |
|
|
15e19045c6 | |
|
|
b4b104d94b | |
|
|
728fb20fc0 | |
|
|
ae039df78a | |
|
|
852ba2ddaf | |
|
|
8fac814750 | |
|
|
effb99af71 | |
|
|
e6c63b3342 | |
|
|
f5314830d8 | |
|
|
b6f90e3041 | |
|
|
87aae94cbf | |
|
|
12614410f7 | |
|
|
f6396a8d86 | |
|
|
21707e1484 | |
|
|
287a24adff | |
|
|
67e35d7efd | |
|
|
6e19881ea3 | |
|
|
0ab8381d61 | |
|
|
c71c3263ca | |
|
|
22f50cefb5 | |
|
|
70185b6304 | |
|
|
7703c0c7fc | |
|
|
89263366e9 | |
|
|
2c830aa233 | |
|
|
c5fc641212 | |
|
|
b3eb6e134f | |
|
|
e8e4fad106 | |
|
|
e2485f2dda | |
|
|
8012240b26 | |
|
|
ef80d583d1 | |
|
|
2b7af4c1b7 | |
|
|
581f1dd35d | |
|
|
031204a6a1 | |
|
|
c60e7dbbb1 | |
|
|
29d30d9330 | |
|
|
29445be9fd | |
|
|
3828bbeea0 | |
|
|
4c9c7e28f1 | |
|
|
297d6d66b1 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
100
CHANGES.md
100
CHANGES.md
|
|
@ -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:
|
||||
|
|
|
|||
125
CMakeLists.txt
125
CMakeLists.txt
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ target_link_libraries(Cesium3DTilesSelection
|
|||
CesiumQuantizedMeshTerrain
|
||||
CesiumRasterOverlays
|
||||
CesiumUtility
|
||||
spdlog::spdlog spdlog::spdlog_header_only
|
||||
spdlog::spdlog
|
||||
# PRIVATE
|
||||
libmorton::libmorton
|
||||
draco::draco
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ void TileLoadRequester::unregister() noexcept {
|
|||
}
|
||||
}
|
||||
|
||||
bool TileLoadRequester::isRegistered() const noexcept {
|
||||
return this->_pTilesetContentManager != nullptr;
|
||||
}
|
||||
|
||||
TileLoadRequester::TileLoadRequester() noexcept
|
||||
: _pTilesetContentManager(nullptr) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -12,5 +12,10 @@ public:
|
|||
static void setTileShouldContinueUpdating(
|
||||
Cesium3DTilesSelection::Tile& tile,
|
||||
bool shouldContinueToBeUpdating);
|
||||
|
||||
static void setTileContent(
|
||||
Cesium3DTilesSelection::Tile& tile,
|
||||
Cesium3DTilesSelection::TileContent&& content);
|
||||
};
|
||||
|
||||
} // namespace Cesium3DTilesSelection
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -46,9 +46,12 @@ target_link_libraries(CesiumRasterOverlays
|
|||
CesiumGltf
|
||||
CesiumGltfContent
|
||||
CesiumGltfReader
|
||||
CesiumJsonReader
|
||||
CesiumJsonWriter
|
||||
CesiumUtility
|
||||
CesiumVectorData
|
||||
nonstd::expected-lite
|
||||
spdlog::spdlog
|
||||
PRIVATE
|
||||
tinyxml2::tinyxml2
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Reference in New Issue