react-native-cesium 1.0.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -11
- package/ReactNativeCesium.podspec +1 -1
- package/android/CMakeLists.txt +5 -3
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +197 -227
- package/android/src/main/cpp/CesiumBridgeAndroid.h +48 -49
- package/android/src/main/cpp/cpp-adapter.cpp +2 -2
- package/android/src/main/cpp/vulkan/VulkanBackend.cpp +1010 -569
- package/android/src/main/cpp/vulkan/VulkanBackend.h +121 -12
- package/android/src/main/cpp/vulkan/shaders/sky.frag +20 -10
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +28 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +295 -58
- package/cpp/engine/CameraSmoother.cpp +99 -0
- package/cpp/engine/CameraSmoother.hpp +29 -0
- package/cpp/engine/CameraTargetState.cpp +49 -0
- package/cpp/engine/CameraTargetState.hpp +121 -0
- package/cpp/engine/CesiumEngine.cpp +206 -60
- package/cpp/engine/CesiumEngine.hpp +77 -10
- package/cpp/engine/EngineTunables.hpp +79 -0
- package/cpp/engine/GeoidConverter.cpp +36 -0
- package/cpp/engine/GeoidConverter.hpp +10 -0
- package/cpp/engine/GlobeCamera.cpp +7 -2
- package/cpp/engine/MetricsAggregator.cpp +71 -0
- package/cpp/engine/MetricsAggregator.hpp +54 -0
- package/cpp/engine/TileLifecycleManager.cpp +0 -12
- package/cpp/engine/TileLifecycleManager.hpp +0 -3
- package/cpp/third_party/egm96/EGM96.c +289 -0
- package/cpp/third_party/egm96/EGM96.h +45 -0
- package/cpp/third_party/egm96/EGM96_data.h +65372 -0
- package/ios/CesiumBridge.h +24 -5
- package/ios/CesiumBridge.mm +149 -271
- package/ios/HybridCesiumView.swift +197 -40
- package/ios/metal/MetalBackend.mm +72 -23
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +26 -0
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JCesiumMetrics.hpp +5 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +109 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +24 -0
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +48 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CameraState.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CesiumMetrics.kt +32 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +72 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Quaternion.kt +19 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +30 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +85 -1
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +60 -0
- package/nitrogen/generated/ios/swift/CesiumMetrics.swift +7 -2
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +12 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +288 -0
- package/nitrogen/generated/shared/c++/CesiumMetrics.hpp +5 -1
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +25 -1
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +132 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +13 -1
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +12 -0
- package/package.json +4 -4
- package/scripts/cesium/clone.mjs +1 -1
- package/src/specs/cesium-view.nitro.ts +29 -0
- package/cpp/engine/DeletionQueue.cpp +0 -28
- package/cpp/engine/DeletionQueue.hpp +0 -28
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "EngineTunables.hpp"
|
|
4
|
+
#include "GlobeCamera.hpp"
|
|
5
|
+
|
|
6
|
+
#include <atomic>
|
|
7
|
+
#include <chrono>
|
|
8
|
+
#include <mutex>
|
|
9
|
+
|
|
10
|
+
namespace reactnativecesium {
|
|
11
|
+
|
|
12
|
+
// Thread-safe holder for the *demand* camera (what the JS side last asked for).
|
|
13
|
+
//
|
|
14
|
+
// Writers: the React Native JS / worklet thread, via setCamera / setCameraQuaternion.
|
|
15
|
+
// Readers: the dedicated render thread (per frame), via shouldRenderNextFrame /
|
|
16
|
+
// renderFrame.
|
|
17
|
+
//
|
|
18
|
+
// We use a small mutex around `target_` because the writes are uncontended and
|
|
19
|
+
// the read side only happens once per frame; the cost is negligible compared to
|
|
20
|
+
// the cost of *not* synchronising a 56-byte struct that contains a glm::dquat.
|
|
21
|
+
//
|
|
22
|
+
// `dirty_` and `forceRender_` are independent atomic flags so the read side can
|
|
23
|
+
// poll without taking the lock when the camera has not moved.
|
|
24
|
+
class CameraTargetState {
|
|
25
|
+
public:
|
|
26
|
+
CameraTargetState() = default;
|
|
27
|
+
|
|
28
|
+
// Replace the entire target (does not touch viewCorrection if you want to
|
|
29
|
+
// preserve it — use setHpr instead).
|
|
30
|
+
void setAll(const CameraParams& target) {
|
|
31
|
+
{
|
|
32
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
33
|
+
target_ = target;
|
|
34
|
+
}
|
|
35
|
+
noteUpdateCadence();
|
|
36
|
+
dirty_.store(true, std::memory_order_release);
|
|
37
|
+
forceRender_.store(true, std::memory_order_release);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Update lat/lon/alt + heading/pitch/roll, leaving viewCorrection unchanged.
|
|
41
|
+
void setHpr(double lat, double lon, double alt,
|
|
42
|
+
double heading, double pitch, double roll) {
|
|
43
|
+
{
|
|
44
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
45
|
+
target_.latitude = lat;
|
|
46
|
+
target_.longitude = lon;
|
|
47
|
+
target_.altitude = alt;
|
|
48
|
+
target_.heading = heading;
|
|
49
|
+
target_.pitch = pitch;
|
|
50
|
+
target_.roll = roll;
|
|
51
|
+
}
|
|
52
|
+
noteUpdateCadence();
|
|
53
|
+
dirty_.store(true, std::memory_order_release);
|
|
54
|
+
forceRender_.store(true, std::memory_order_release);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void setViewCorrection(const glm::dquat& q) {
|
|
58
|
+
{
|
|
59
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
60
|
+
target_.viewCorrection = q;
|
|
61
|
+
}
|
|
62
|
+
noteUpdateCadence();
|
|
63
|
+
dirty_.store(true, std::memory_order_release);
|
|
64
|
+
forceRender_.store(true, std::memory_order_release);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Snapshot the demand target (read side; one mutex per frame).
|
|
68
|
+
CameraParams snapshot() const {
|
|
69
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
70
|
+
return target_;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Lightweight, lock-free check used by the render thread to decide whether
|
|
74
|
+
// it should bother taking a full snapshot at all.
|
|
75
|
+
bool isDirty() const noexcept {
|
|
76
|
+
return dirty_.load(std::memory_order_acquire);
|
|
77
|
+
}
|
|
78
|
+
void clearDirty() noexcept {
|
|
79
|
+
dirty_.store(false, std::memory_order_release);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
bool consumeForceRender() noexcept {
|
|
83
|
+
return forceRender_.exchange(false, std::memory_order_acq_rel);
|
|
84
|
+
}
|
|
85
|
+
void requestForceRender() noexcept {
|
|
86
|
+
forceRender_.store(true, std::memory_order_release);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Compares snapshot to "current" (the engine's current camera) and returns
|
|
90
|
+
// true if any DoF differs by more than the matching epsilon. Used to decide
|
|
91
|
+
// whether to render this frame.
|
|
92
|
+
static bool deltaExceedsEpsilon(const CameraParams& a, const CameraParams& b);
|
|
93
|
+
|
|
94
|
+
// EWMA of the inter-arrival interval (seconds) of demand updates. Used by
|
|
95
|
+
// CameraSmoother to scale the lat/lon time constant: high update rate ⇒
|
|
96
|
+
// tiny τ (snappy gestures); low update rate ⇒ larger τ (smooth GPS catch-up).
|
|
97
|
+
// Lock-free read on the render-thread hot path.
|
|
98
|
+
double recentIntervalSec() const noexcept {
|
|
99
|
+
return ewmaIntervalSec_.load(std::memory_order_acquire);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private:
|
|
103
|
+
// Folds the time since the previous demand update into ewmaIntervalSec_.
|
|
104
|
+
// Called under (or after) the target_ mutex; the EWMA itself is atomic so
|
|
105
|
+
// the read side never blocks. Writes are uncontended (only JS-thread).
|
|
106
|
+
void noteUpdateCadence() noexcept;
|
|
107
|
+
|
|
108
|
+
mutable std::mutex mutex_;
|
|
109
|
+
CameraParams target_;
|
|
110
|
+
std::atomic<bool> dirty_{false};
|
|
111
|
+
std::atomic<bool> forceRender_{true};
|
|
112
|
+
|
|
113
|
+
// Cadence tracker. lastUpdateTime_ is only touched on the writer (JS)
|
|
114
|
+
// thread, so it does not need synchronisation; ewmaIntervalSec_ is read
|
|
115
|
+
// by the render thread without taking the mutex.
|
|
116
|
+
std::chrono::steady_clock::time_point lastUpdateTime_{};
|
|
117
|
+
bool hasLastUpdate_{false};
|
|
118
|
+
std::atomic<double> ewmaIntervalSec_{0.0};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
} // namespace reactnativecesium
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
#include "CesiumEngine.hpp"
|
|
7
7
|
|
|
8
|
+
#ifdef __ANDROID__
|
|
9
|
+
#include <android/log.h>
|
|
10
|
+
#define ENGINE_LOGI(...) __android_log_print(ANDROID_LOG_INFO, "CesiumEngine", __VA_ARGS__)
|
|
11
|
+
#else
|
|
12
|
+
#define ENGINE_LOGI(...)
|
|
13
|
+
#endif
|
|
14
|
+
|
|
8
15
|
#include <Cesium3DTilesContent/registerAllTileContentTypes.h>
|
|
9
16
|
#include <Cesium3DTilesSelection/BoundingVolume.h>
|
|
10
17
|
#include <Cesium3DTilesSelection/Tile.h>
|
|
@@ -17,8 +24,8 @@
|
|
|
17
24
|
#include <CesiumAsync/SqliteCache.h>
|
|
18
25
|
#include <CesiumCurl/CurlAssetAccessor.h>
|
|
19
26
|
#include <CesiumRasterOverlays/IonRasterOverlay.h>
|
|
27
|
+
#include <CesiumRasterOverlays/RasterOverlay.h>
|
|
20
28
|
#include <CesiumUtility/CreditSystem.h>
|
|
21
|
-
#include <CesiumUtility/IntrusivePointer.h>
|
|
22
29
|
|
|
23
30
|
#ifdef CESIUM_ENGINE_UNDEF_NDEBUG
|
|
24
31
|
# undef NDEBUG
|
|
@@ -31,24 +38,30 @@
|
|
|
31
38
|
|
|
32
39
|
#include <algorithm>
|
|
33
40
|
#include <cmath>
|
|
41
|
+
#include <thread>
|
|
34
42
|
|
|
35
43
|
namespace reactnativecesium {
|
|
36
44
|
|
|
45
|
+
int32_t CesiumEngine::resolveTaskThreadCount(int32_t requested) {
|
|
46
|
+
if (requested > 0) return requested;
|
|
47
|
+
unsigned int hw = std::thread::hardware_concurrency();
|
|
48
|
+
if (hw == 0) return 4;
|
|
49
|
+
// Reserve one core for the UI / display thread; clamp into a sensible band.
|
|
50
|
+
int32_t target = static_cast<int32_t>(hw) - 1;
|
|
51
|
+
if (target < 2) target = 2;
|
|
52
|
+
if (target > 8) target = 8;
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
CesiumEngine::CesiumEngine()
|
|
38
|
-
: taskProcessor_(std::make_shared<TaskProcessor>(
|
|
57
|
+
: taskProcessor_(std::make_shared<TaskProcessor>(
|
|
58
|
+
static_cast<uint32_t>(resolveTaskThreadCount(0)))),
|
|
39
59
|
asyncSystem_(taskProcessor_) {
|
|
40
60
|
Cesium3DTilesContent::registerAllTileContentTypes();
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
CesiumEngine::~CesiumEngine() { shutdown(); }
|
|
44
64
|
|
|
45
|
-
bool CesiumEngine::tilesetOptionsMatch(const EngineConfig& a,
|
|
46
|
-
const EngineConfig& b) const {
|
|
47
|
-
return a.maximumScreenSpaceError == b.maximumScreenSpaceError &&
|
|
48
|
-
a.maximumSimultaneousTileLoads == b.maximumSimultaneousTileLoads &&
|
|
49
|
-
a.loadingDescendantLimit == b.loadingDescendantLimit;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
65
|
void CesiumEngine::buildEllipsoidMesh() {
|
|
53
66
|
// Tessellate a WGS84 ellipsoid slightly inset so terrain tiles (at true
|
|
54
67
|
// surface height) reliably overdraw it via the reversed-Z depth test.
|
|
@@ -92,11 +105,6 @@ void CesiumEngine::buildEllipsoidMesh() {
|
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
107
|
|
|
95
|
-
// Append the fallback ellipsoid as a draw.
|
|
96
|
-
// RTC convention for the fallback: the tile's "RTC centre" is the camera
|
|
97
|
-
// position itself, so local positions == old camera-relative positions. The
|
|
98
|
-
// per-draw MVP is therefore the same rotation-only VP used before RTC was
|
|
99
|
-
// introduced, keeping the fallback on the same code path.
|
|
100
108
|
void CesiumEngine::appendEllipsoidDraws(FrameResult& result) const {
|
|
101
109
|
if (ellipsoidPositions_.empty() || ellipsoidIndices_.empty()) return;
|
|
102
110
|
|
|
@@ -115,13 +123,10 @@ void CesiumEngine::appendEllipsoidDraws(FrameResult& result) const {
|
|
|
115
123
|
float* uvOut = result.uvs.data() + baseVertex * 2;
|
|
116
124
|
|
|
117
125
|
for (const auto& posEcef : ellipsoidPositions_) {
|
|
118
|
-
// Local position = ECEF - camera (same as old eye-relative, since camera is the RTC centre).
|
|
119
126
|
const glm::dvec3 local = posEcef - cameraPos;
|
|
120
127
|
*posOut++ = static_cast<float>(local.x);
|
|
121
128
|
*posOut++ = static_cast<float>(local.y);
|
|
122
129
|
*posOut++ = static_cast<float>(local.z);
|
|
123
|
-
// Fallback geoid should represent land by default when terrain tiles are not
|
|
124
|
-
// loaded yet, so keep altitude at sea level for hypsometric land colouring.
|
|
125
130
|
*altOut++ = 0.0f;
|
|
126
131
|
*uvOut++ = 0.5f;
|
|
127
132
|
*uvOut++ = 0.5f;
|
|
@@ -140,8 +145,6 @@ void CesiumEngine::appendEllipsoidDraws(FrameResult& result) const {
|
|
|
140
145
|
draw.hasUVs = false;
|
|
141
146
|
draw.isEllipsoidFallback = true;
|
|
142
147
|
draw.overlayTexture = nullptr;
|
|
143
|
-
// Ellipsoid RTC centre = camera → local pos = eye-relative → use the
|
|
144
|
-
// rotation-only VP (identical to the pre-RTC vertex transform).
|
|
145
148
|
draw.mvpMatrix = result.vpMatrix;
|
|
146
149
|
draw.rtcCenterEcef = result.cameraEcef;
|
|
147
150
|
result.draws.push_back(draw);
|
|
@@ -149,18 +152,39 @@ void CesiumEngine::appendEllipsoidDraws(FrameResult& result) const {
|
|
|
149
152
|
|
|
150
153
|
void CesiumEngine::initialize(const EngineConfig& config) {
|
|
151
154
|
config_ = config;
|
|
155
|
+
// Ensure ionImageryAssetId default is honoured if the caller passed 0/<0.
|
|
156
|
+
if (config_.ionImageryAssetId <= 0) {
|
|
157
|
+
config_.ionImageryAssetId = 1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Re-create the worker pool if the requested thread count differs from
|
|
161
|
+
// the autodetected default we used in the constructor.
|
|
162
|
+
const int32_t desiredThreads = resolveTaskThreadCount(config_.taskProcessorThreads);
|
|
163
|
+
if (taskProcessor_) {
|
|
164
|
+
// We can only resize the pool by replacing it, which would break
|
|
165
|
+
// asyncSystem_. Skip when it's the same to preserve in-flight tasks.
|
|
166
|
+
// Note: TaskProcessor stores its size privately, so we conservatively
|
|
167
|
+
// recreate only when an explicit override is provided.
|
|
168
|
+
if (config_.taskProcessorThreads > 0) {
|
|
169
|
+
taskProcessor_->waitUntilIdle();
|
|
170
|
+
taskProcessor_ = std::make_shared<TaskProcessor>(
|
|
171
|
+
static_cast<uint32_t>(desiredThreads));
|
|
172
|
+
asyncSystem_ = CesiumAsync::AsyncSystem(taskProcessor_);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
152
175
|
|
|
153
176
|
auto logger = spdlog::default_logger();
|
|
154
177
|
|
|
155
178
|
CesiumCurl::CurlAssetAccessorOptions curlOpts;
|
|
156
|
-
if (!
|
|
157
|
-
curlOpts.certificateFile =
|
|
179
|
+
if (!config_.tlsCaBundlePath.empty()) {
|
|
180
|
+
curlOpts.certificateFile = config_.tlsCaBundlePath;
|
|
158
181
|
}
|
|
159
182
|
auto curlAccessor = std::make_shared<CesiumCurl::CurlAssetAccessor>(curlOpts);
|
|
160
183
|
|
|
161
|
-
if (!
|
|
184
|
+
if (!config_.cacheDatabasePath.empty()) {
|
|
162
185
|
auto cache = std::make_shared<CesiumAsync::SqliteCache>(
|
|
163
|
-
logger,
|
|
186
|
+
logger, config_.cacheDatabasePath,
|
|
187
|
+
static_cast<uint64_t>(std::max<int32_t>(64, config_.sqliteCacheMaxRows)));
|
|
164
188
|
assetAccessor_ = std::make_shared<CesiumAsync::CachingAssetAccessor>(
|
|
165
189
|
logger, curlAccessor, cache);
|
|
166
190
|
} else {
|
|
@@ -174,7 +198,7 @@ void CesiumEngine::initialize(const EngineConfig& config) {
|
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
buildEllipsoidMesh();
|
|
177
|
-
createTileset(
|
|
201
|
+
createTileset();
|
|
178
202
|
}
|
|
179
203
|
|
|
180
204
|
void CesiumEngine::shutdown() {
|
|
@@ -183,30 +207,51 @@ void CesiumEngine::shutdown() {
|
|
|
183
207
|
}
|
|
184
208
|
|
|
185
209
|
void CesiumEngine::updateConfig(const EngineConfig& config) {
|
|
210
|
+
// Only token / asset id changes force a full tileset rebuild. Everything
|
|
211
|
+
// else is mutated in place via tileset_->getOptions() to avoid the very
|
|
212
|
+
// expensive teardown + re-load from the root.
|
|
186
213
|
const bool needRebuild =
|
|
187
214
|
config.ionAccessToken != config_.ionAccessToken ||
|
|
188
|
-
config.ionAssetId
|
|
189
|
-
|
|
215
|
+
config.ionAssetId != config_.ionAssetId;
|
|
216
|
+
|
|
217
|
+
// Imagery overlay is its own change set.
|
|
218
|
+
const bool overlayChanged =
|
|
219
|
+
config.ionImageryAssetId != config_.ionImageryAssetId;
|
|
190
220
|
|
|
191
221
|
config_ = config;
|
|
222
|
+
if (config_.ionImageryAssetId <= 0) {
|
|
223
|
+
config_.ionImageryAssetId = 1;
|
|
224
|
+
}
|
|
192
225
|
|
|
193
|
-
if (
|
|
226
|
+
if (needRebuild) {
|
|
227
|
+
destroyTileset();
|
|
228
|
+
if (!config_.ionAccessToken.empty()) {
|
|
229
|
+
createTileset();
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
194
233
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
234
|
+
if (tileset_) {
|
|
235
|
+
applyRuntimeConfig(config_);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (overlayChanged) {
|
|
239
|
+
applyImageryOverlay(config_.ionImageryAssetId);
|
|
198
240
|
}
|
|
199
241
|
}
|
|
200
242
|
|
|
201
|
-
void CesiumEngine::createTileset(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
243
|
+
void CesiumEngine::createTileset() {
|
|
244
|
+
if (config_.ionAccessToken.empty()) {
|
|
245
|
+
spdlog::warn("createTileset: ion access token is empty, skipping tileset creation");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
205
248
|
|
|
206
249
|
if (!creditSystem_) {
|
|
207
250
|
creditSystem_ = std::make_shared<CesiumUtility::CreditSystem>();
|
|
208
251
|
}
|
|
209
252
|
|
|
253
|
+
spdlog::info("createTileset: creating tileset with assetId={}", config_.ionAssetId);
|
|
254
|
+
|
|
210
255
|
auto logger = spdlog::default_logger();
|
|
211
256
|
|
|
212
257
|
Cesium3DTilesSelection::TilesetExternals externals{
|
|
@@ -214,38 +259,141 @@ void CesiumEngine::createTileset(const std::string& token,
|
|
|
214
259
|
logger, nullptr};
|
|
215
260
|
|
|
216
261
|
Cesium3DTilesSelection::TilesetOptions opts;
|
|
217
|
-
opts.maximumCachedBytes =
|
|
262
|
+
opts.maximumCachedBytes = static_cast<int64_t>(
|
|
263
|
+
std::max<int64_t>(16LL * 1024LL * 1024LL, config_.maximumCachedBytes));
|
|
218
264
|
opts.maximumSimultaneousTileLoads =
|
|
219
265
|
static_cast<uint32_t>(std::max(0, config_.maximumSimultaneousTileLoads));
|
|
220
266
|
opts.loadingDescendantLimit =
|
|
221
267
|
static_cast<uint32_t>(std::max(1, config_.loadingDescendantLimit));
|
|
222
268
|
opts.maximumScreenSpaceError =
|
|
223
269
|
std::max(1.0, config_.maximumScreenSpaceError);
|
|
224
|
-
opts.preloadAncestors =
|
|
225
|
-
opts.preloadSiblings =
|
|
226
|
-
opts.forbidHoles =
|
|
227
|
-
opts.contentOptions.enableWaterMask =
|
|
270
|
+
opts.preloadAncestors = config_.preloadAncestors;
|
|
271
|
+
opts.preloadSiblings = config_.preloadSiblings;
|
|
272
|
+
opts.forbidHoles = config_.forbidHoles;
|
|
273
|
+
opts.contentOptions.enableWaterMask = config_.enableWaterMask;
|
|
274
|
+
opts.enableFogCulling = config_.enableFogCulling;
|
|
275
|
+
opts.enforceCulledScreenSpaceError = config_.enforceCulledScreenSpaceError;
|
|
276
|
+
opts.culledScreenSpaceError = std::max(1.0, config_.culledScreenSpaceError);
|
|
277
|
+
opts.enableLodTransitionPeriod = config_.enableLodTransitionPeriod;
|
|
278
|
+
opts.lodTransitionLength = std::max(0.0, config_.lodTransitionLength);
|
|
228
279
|
|
|
229
280
|
tileset_ = std::make_unique<Cesium3DTilesSelection::Tileset>(
|
|
230
|
-
externals,
|
|
281
|
+
externals, config_.ionAssetId, config_.ionAccessToken, opts);
|
|
282
|
+
|
|
283
|
+
spdlog::info("createTileset: tileset created successfully");
|
|
284
|
+
|
|
285
|
+
currentOverlay_ = nullptr;
|
|
286
|
+
currentImageryAssetId_ = 1;
|
|
287
|
+
if (config_.ionImageryAssetId > 0 && config_.ionImageryAssetId != 1) {
|
|
288
|
+
applyImageryOverlay(config_.ionImageryAssetId);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
231
291
|
|
|
232
|
-
|
|
292
|
+
bool CesiumEngine::applyRuntimeConfig(const EngineConfig& cfg) {
|
|
293
|
+
if (!tileset_) return false;
|
|
294
|
+
auto& opts = tileset_->getOptions();
|
|
295
|
+
bool changed = false;
|
|
296
|
+
|
|
297
|
+
const double newSse =
|
|
298
|
+
std::max(1.0, cfg.maximumScreenSpaceError);
|
|
299
|
+
if (opts.maximumScreenSpaceError != newSse) {
|
|
300
|
+
opts.maximumScreenSpaceError = newSse;
|
|
301
|
+
changed = true;
|
|
302
|
+
}
|
|
303
|
+
const uint32_t newSim =
|
|
304
|
+
static_cast<uint32_t>(std::max(0, cfg.maximumSimultaneousTileLoads));
|
|
305
|
+
if (opts.maximumSimultaneousTileLoads != newSim) {
|
|
306
|
+
opts.maximumSimultaneousTileLoads = newSim;
|
|
307
|
+
changed = true;
|
|
308
|
+
}
|
|
309
|
+
const uint32_t newDesc =
|
|
310
|
+
static_cast<uint32_t>(std::max(1, cfg.loadingDescendantLimit));
|
|
311
|
+
if (opts.loadingDescendantLimit != newDesc) {
|
|
312
|
+
opts.loadingDescendantLimit = newDesc;
|
|
313
|
+
changed = true;
|
|
314
|
+
}
|
|
315
|
+
const int64_t newCacheBytes = std::max<int64_t>(
|
|
316
|
+
16LL * 1024LL * 1024LL, cfg.maximumCachedBytes);
|
|
317
|
+
if (opts.maximumCachedBytes != newCacheBytes) {
|
|
318
|
+
opts.maximumCachedBytes = newCacheBytes;
|
|
319
|
+
changed = true;
|
|
320
|
+
}
|
|
321
|
+
if (opts.preloadAncestors != cfg.preloadAncestors) {
|
|
322
|
+
opts.preloadAncestors = cfg.preloadAncestors;
|
|
323
|
+
changed = true;
|
|
324
|
+
}
|
|
325
|
+
if (opts.preloadSiblings != cfg.preloadSiblings) {
|
|
326
|
+
opts.preloadSiblings = cfg.preloadSiblings;
|
|
327
|
+
changed = true;
|
|
328
|
+
}
|
|
329
|
+
if (opts.forbidHoles != cfg.forbidHoles) {
|
|
330
|
+
opts.forbidHoles = cfg.forbidHoles;
|
|
331
|
+
changed = true;
|
|
332
|
+
}
|
|
333
|
+
// Note: contentOptions.enableWaterMask only takes effect on tiles loaded
|
|
334
|
+
// after the change. Existing in-memory tiles keep their water mask state.
|
|
335
|
+
if (opts.contentOptions.enableWaterMask != cfg.enableWaterMask) {
|
|
336
|
+
opts.contentOptions.enableWaterMask = cfg.enableWaterMask;
|
|
337
|
+
changed = true;
|
|
338
|
+
}
|
|
339
|
+
if (opts.enableFogCulling != cfg.enableFogCulling) {
|
|
340
|
+
opts.enableFogCulling = cfg.enableFogCulling;
|
|
341
|
+
changed = true;
|
|
342
|
+
}
|
|
343
|
+
if (opts.enforceCulledScreenSpaceError != cfg.enforceCulledScreenSpaceError) {
|
|
344
|
+
opts.enforceCulledScreenSpaceError = cfg.enforceCulledScreenSpaceError;
|
|
345
|
+
changed = true;
|
|
346
|
+
}
|
|
347
|
+
const double newCulled =
|
|
348
|
+
std::max(1.0, cfg.culledScreenSpaceError);
|
|
349
|
+
if (opts.culledScreenSpaceError != newCulled) {
|
|
350
|
+
opts.culledScreenSpaceError = newCulled;
|
|
351
|
+
changed = true;
|
|
352
|
+
}
|
|
353
|
+
if (opts.enableLodTransitionPeriod != cfg.enableLodTransitionPeriod) {
|
|
354
|
+
opts.enableLodTransitionPeriod = cfg.enableLodTransitionPeriod;
|
|
355
|
+
changed = true;
|
|
356
|
+
}
|
|
357
|
+
const double newLodLen = std::max(0.0, cfg.lodTransitionLength);
|
|
358
|
+
if (opts.lodTransitionLength != newLodLen) {
|
|
359
|
+
opts.lodTransitionLength = newLodLen;
|
|
360
|
+
changed = true;
|
|
361
|
+
}
|
|
362
|
+
return changed;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
void CesiumEngine::applyImageryOverlay(int64_t targetAssetId) {
|
|
366
|
+
if (!tileset_) return;
|
|
367
|
+
if (targetAssetId == currentImageryAssetId_) return;
|
|
368
|
+
|
|
369
|
+
if (currentOverlay_) {
|
|
370
|
+
tileset_->getOverlays().remove(currentOverlay_);
|
|
371
|
+
currentOverlay_ = nullptr;
|
|
372
|
+
}
|
|
373
|
+
currentImageryAssetId_ = (targetAssetId <= 0) ? 1 : targetAssetId;
|
|
374
|
+
|
|
375
|
+
if (currentImageryAssetId_ != 1 && !config_.ionAccessToken.empty()) {
|
|
233
376
|
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlay> ov =
|
|
234
|
-
new CesiumRasterOverlays::IonRasterOverlay(
|
|
377
|
+
new CesiumRasterOverlays::IonRasterOverlay(
|
|
378
|
+
"imagery", currentImageryAssetId_, config_.ionAccessToken);
|
|
235
379
|
tileset_->getOverlays().add(ov);
|
|
380
|
+
currentOverlay_ = ov;
|
|
236
381
|
}
|
|
237
382
|
}
|
|
238
383
|
|
|
239
384
|
void CesiumEngine::destroyTileset() {
|
|
385
|
+
currentOverlay_ = nullptr;
|
|
386
|
+
currentImageryAssetId_ = 1;
|
|
240
387
|
tileset_.reset();
|
|
241
|
-
taskProcessor_
|
|
388
|
+
if (taskProcessor_) {
|
|
389
|
+
taskProcessor_->waitUntilIdle();
|
|
390
|
+
}
|
|
242
391
|
}
|
|
243
392
|
|
|
244
393
|
void CesiumEngine::setImageryAssetId(int64_t assetId) {
|
|
245
394
|
if (config_.ionAccessToken.empty()) return;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
createTileset(config_.ionAccessToken, config_.ionAssetId, assetId);
|
|
395
|
+
config_.ionImageryAssetId = (assetId <= 0) ? 1 : assetId;
|
|
396
|
+
applyImageryOverlay(config_.ionImageryAssetId);
|
|
249
397
|
}
|
|
250
398
|
|
|
251
399
|
void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
@@ -278,20 +426,24 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
278
426
|
return;
|
|
279
427
|
}
|
|
280
428
|
|
|
281
|
-
result.localPositions.reserve(
|
|
282
|
-
result.altitudes.reserve(
|
|
283
|
-
result.uvs.reserve(
|
|
284
|
-
result.indices.reserve(
|
|
429
|
+
result.localPositions.reserve(tunables::kFrameResultPositionFloatReserve);
|
|
430
|
+
result.altitudes.reserve(tunables::kFrameResultPositionFloatReserve / 3);
|
|
431
|
+
result.uvs.reserve(tunables::kFrameResultPositionFloatReserve * 2);
|
|
432
|
+
result.indices.reserve(tunables::kFrameResultIndexUint32Reserve);
|
|
285
433
|
|
|
286
434
|
asyncSystem_.dispatchMainThreadTasks();
|
|
287
435
|
|
|
288
|
-
// Ellipsoid fallback is appended first so real terrain tiles (drawn later)
|
|
289
|
-
// overdraw it via the reversed-Z depth test.
|
|
290
|
-
appendEllipsoidDraws(result);
|
|
291
|
-
|
|
292
436
|
const auto viewState = camera_.computeViewState(w, h);
|
|
293
437
|
const auto& updateResult = tileset_->updateView({viewState});
|
|
294
438
|
|
|
439
|
+
// Only emit the fallback ellipsoid when no real terrain is being rendered
|
|
440
|
+
// this frame. Otherwise it would be over-drawn anyway (~8k vertices and
|
|
441
|
+
// ~24k indices of throwaway transform + memcpy per frame).
|
|
442
|
+
const bool noVisibleTerrain = updateResult.tilesToRenderThisFrame.empty();
|
|
443
|
+
if (noVisibleTerrain) {
|
|
444
|
+
appendEllipsoidDraws(result);
|
|
445
|
+
}
|
|
446
|
+
|
|
295
447
|
if (creditSystem_) {
|
|
296
448
|
const CesiumUtility::CreditsSnapshot& snap =
|
|
297
449
|
creditSystem_->getSnapshot(CesiumUtility::CreditFilteringMode::UniqueHtml);
|
|
@@ -339,8 +491,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
339
491
|
!prim.uvs.empty() && prim.uvs.size() == vertexCount;
|
|
340
492
|
|
|
341
493
|
// ── RTC: copy tile-local positions & altitudes ────────────────────────
|
|
342
|
-
// Positions are already relative to prim.rtcCenter (small float values).
|
|
343
|
-
// Altitudes are computed in double in GltfToMesh — sub-millimetre precision.
|
|
344
494
|
result.localPositions.resize(result.localPositions.size() + vertexCount * 3);
|
|
345
495
|
result.altitudes.resize(result.altitudes.size() + vertexCount);
|
|
346
496
|
result.uvs.resize(result.uvs.size() + vertexCount * 2);
|
|
@@ -369,10 +519,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
369
519
|
}
|
|
370
520
|
|
|
371
521
|
// ── RTC: per-tile MVP matrix (double → float) ─────────────────────────
|
|
372
|
-
// Compute the camera-relative offset of the tile's RTC centre in double
|
|
373
|
-
// and bake it into the MVP matrix before casting to float32. This means
|
|
374
|
-
// the large camera↔tile translation is resolved with double precision;
|
|
375
|
-
// the vertex shader only ever adds small tile-local offsets on top of it.
|
|
376
522
|
const glm::dvec3 rtcCamRel =
|
|
377
523
|
prim.rtcCenter - glm::dvec3(cameraPos);
|
|
378
524
|
const glm::dmat4 translateD =
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include "../renderer/RenderTypes.hpp"
|
|
4
|
+
#include "EngineTunables.hpp"
|
|
4
5
|
#include "GlobeCamera.hpp"
|
|
5
6
|
#include "ResourcePreparer.hpp"
|
|
6
7
|
#include "TaskProcessor.hpp"
|
|
@@ -8,6 +9,7 @@
|
|
|
8
9
|
|
|
9
10
|
#include <CesiumAsync/AsyncSystem.h>
|
|
10
11
|
#include <CesiumAsync/IAssetAccessor.h>
|
|
12
|
+
#include <CesiumUtility/IntrusivePointer.h>
|
|
11
13
|
|
|
12
14
|
#include <memory>
|
|
13
15
|
#include <string>
|
|
@@ -20,16 +22,62 @@ namespace CesiumUtility {
|
|
|
20
22
|
class CreditSystem;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
namespace CesiumRasterOverlays {
|
|
26
|
+
class RasterOverlay;
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
namespace reactnativecesium {
|
|
24
30
|
|
|
31
|
+
// Mirror of Cesium Native TilesetOptions / SqliteCache + a few of our own
|
|
32
|
+
// knobs. Default values match the values previously hardcoded in
|
|
33
|
+
// CesiumEngine.cpp so existing call sites that pass an EngineConfig with only
|
|
34
|
+
// the few "core" fields populated continue to behave as before.
|
|
25
35
|
struct EngineConfig {
|
|
36
|
+
// ── Cesium Ion / network ──────────────────────────────────────────────
|
|
26
37
|
std::string ionAccessToken;
|
|
27
|
-
int64_t ionAssetId
|
|
38
|
+
int64_t ionAssetId = 1;
|
|
39
|
+
int64_t ionImageryAssetId = 1; // 1 == "no overlay (use built-in colours)"
|
|
28
40
|
std::string cacheDatabasePath;
|
|
29
41
|
std::string tlsCaBundlePath;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
|
|
43
|
+
// ── Tileset selection (TilesetOptions) ───────────────────────────────
|
|
44
|
+
double maximumScreenSpaceError = tunables::kDefaultMaximumScreenSpaceError;
|
|
45
|
+
int32_t maximumSimultaneousTileLoads = tunables::kDefaultMaximumSimultaneousTileLoads;
|
|
46
|
+
int32_t loadingDescendantLimit = tunables::kDefaultLoadingDescendantLimit;
|
|
47
|
+
|
|
48
|
+
// RAM budget for decoded geometry/textures held by the live tileset.
|
|
49
|
+
int64_t maximumCachedBytes = tunables::kDefaultMaxCachedBytes;
|
|
50
|
+
|
|
51
|
+
// Pre-load extra tiles around what is strictly needed for this view —
|
|
52
|
+
// smoothes panning / hides pop-in but increases peak load pressure.
|
|
53
|
+
bool preloadAncestors = true;
|
|
54
|
+
bool preloadSiblings = true;
|
|
55
|
+
|
|
56
|
+
// Refuse to render holes in the terrain (Cesium will keep ancestor tiles
|
|
57
|
+
// visible until all children load). Relax during fast pans to lower
|
|
58
|
+
// upload pressure.
|
|
59
|
+
bool forbidHoles = true;
|
|
60
|
+
|
|
61
|
+
// Water-mask textures (carries coastline shading data). Cheap to disable on
|
|
62
|
+
// memory-constrained devices.
|
|
63
|
+
bool enableWaterMask = true;
|
|
64
|
+
|
|
65
|
+
// ── Optional / off-by-default selection knobs ────────────────────────
|
|
66
|
+
bool enableFogCulling = false;
|
|
67
|
+
bool enforceCulledScreenSpaceError = true;
|
|
68
|
+
double culledScreenSpaceError = 64.0;
|
|
69
|
+
bool enableLodTransitionPeriod = false;
|
|
70
|
+
double lodTransitionLength = 1.0;
|
|
71
|
+
// Seconds an unused tile remains in memory before being evicted.
|
|
72
|
+
double tileCacheUnloadTimeInSeconds = 5.0;
|
|
73
|
+
|
|
74
|
+
// ── Disk cache (SqliteCache) ─────────────────────────────────────────
|
|
75
|
+
int32_t sqliteCacheMaxRows = tunables::kDefaultSqliteCacheMaxRows;
|
|
76
|
+
|
|
77
|
+
// ── Worker pool ──────────────────────────────────────────────────────
|
|
78
|
+
// 0 means "auto-detect" via std::thread::hardware_concurrency() clamped to
|
|
79
|
+
// [2, 8]. Override only for benchmarking / regression testing.
|
|
80
|
+
int32_t taskProcessorThreads = 0;
|
|
33
81
|
};
|
|
34
82
|
|
|
35
83
|
class CesiumEngine {
|
|
@@ -42,6 +90,12 @@ public:
|
|
|
42
90
|
|
|
43
91
|
void initialize(const EngineConfig& config);
|
|
44
92
|
void shutdown();
|
|
93
|
+
|
|
94
|
+
// Apply config changes. Splits cleanly into:
|
|
95
|
+
// - "tileset must be rebuilt" (token / assetId changed) → destroy + recreate,
|
|
96
|
+
// - "runtime-mutable" (SSE / load limits / cache bytes / ...) →
|
|
97
|
+
// mutate tileset_->getOptions() in place,
|
|
98
|
+
// - "imagery overlay only" → IRasterOverlay add/remove.
|
|
45
99
|
void updateConfig(const EngineConfig& config);
|
|
46
100
|
|
|
47
101
|
void updateFrame(double viewportWidth, double viewportHeight, FrameResult& result);
|
|
@@ -53,17 +107,26 @@ public:
|
|
|
53
107
|
|
|
54
108
|
ResourcePreparer* getResourcePreparer() const { return resourcePreparer_.get(); }
|
|
55
109
|
|
|
110
|
+
// Expose a few useful read-only bits to platform bridges (avoids leaking
|
|
111
|
+
// EngineConfig everywhere).
|
|
112
|
+
bool tilesetReady() const { return tileset_ != nullptr; }
|
|
113
|
+
int64_t currentImageryAssetId() const { return currentImageryAssetId_; }
|
|
114
|
+
|
|
56
115
|
private:
|
|
57
|
-
void createTileset(
|
|
58
|
-
int64_t assetId,
|
|
59
|
-
int64_t imageryAssetId = 1);
|
|
116
|
+
void createTileset();
|
|
60
117
|
void destroyTileset();
|
|
61
118
|
|
|
62
|
-
|
|
119
|
+
// Apply runtime-mutable bits of `cfg` to the live tileset. Returns true if a
|
|
120
|
+
// value actually changed (callers can use this to trigger force-render).
|
|
121
|
+
bool applyRuntimeConfig(const EngineConfig& cfg);
|
|
122
|
+
|
|
123
|
+
// Apply imagery overlay change (target assetId of 1 means "no overlay").
|
|
124
|
+
void applyImageryOverlay(int64_t targetAssetId);
|
|
125
|
+
|
|
126
|
+
static int32_t resolveTaskThreadCount(int32_t requested);
|
|
63
127
|
|
|
64
128
|
// Builds a tessellated WGS84 ellipsoid mesh (inset ~20 m) used as a
|
|
65
|
-
//
|
|
66
|
-
// are absent or not yet loaded.
|
|
129
|
+
// fallback background while terrain tiles are not yet ready.
|
|
67
130
|
void buildEllipsoidMesh();
|
|
68
131
|
void appendEllipsoidDraws(FrameResult& result) const;
|
|
69
132
|
|
|
@@ -77,6 +140,10 @@ private:
|
|
|
77
140
|
|
|
78
141
|
std::unique_ptr<Cesium3DTilesSelection::Tileset> tileset_;
|
|
79
142
|
|
|
143
|
+
// Currently-active overlay — kept so we can remove it cheaply when the
|
|
144
|
+
// imagery asset id changes.
|
|
145
|
+
CesiumUtility::IntrusivePointer<CesiumRasterOverlays::RasterOverlay>
|
|
146
|
+
currentOverlay_;
|
|
80
147
|
int64_t currentImageryAssetId_ = 1;
|
|
81
148
|
|
|
82
149
|
GlobeCamera camera_;
|