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.
Files changed (59) hide show
  1. package/README.md +64 -11
  2. package/ReactNativeCesium.podspec +1 -1
  3. package/android/CMakeLists.txt +5 -3
  4. package/android/src/main/cpp/CesiumBridgeAndroid.cpp +197 -227
  5. package/android/src/main/cpp/CesiumBridgeAndroid.h +48 -49
  6. package/android/src/main/cpp/cpp-adapter.cpp +2 -2
  7. package/android/src/main/cpp/vulkan/VulkanBackend.cpp +1010 -569
  8. package/android/src/main/cpp/vulkan/VulkanBackend.h +121 -12
  9. package/android/src/main/cpp/vulkan/shaders/sky.frag +20 -10
  10. package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +28 -0
  11. package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +295 -58
  12. package/cpp/engine/CameraSmoother.cpp +99 -0
  13. package/cpp/engine/CameraSmoother.hpp +29 -0
  14. package/cpp/engine/CameraTargetState.cpp +49 -0
  15. package/cpp/engine/CameraTargetState.hpp +121 -0
  16. package/cpp/engine/CesiumEngine.cpp +206 -60
  17. package/cpp/engine/CesiumEngine.hpp +77 -10
  18. package/cpp/engine/EngineTunables.hpp +79 -0
  19. package/cpp/engine/GeoidConverter.cpp +36 -0
  20. package/cpp/engine/GeoidConverter.hpp +10 -0
  21. package/cpp/engine/GlobeCamera.cpp +7 -2
  22. package/cpp/engine/MetricsAggregator.cpp +71 -0
  23. package/cpp/engine/MetricsAggregator.hpp +54 -0
  24. package/cpp/engine/TileLifecycleManager.cpp +0 -12
  25. package/cpp/engine/TileLifecycleManager.hpp +0 -3
  26. package/cpp/third_party/egm96/EGM96.c +289 -0
  27. package/cpp/third_party/egm96/EGM96.h +45 -0
  28. package/cpp/third_party/egm96/EGM96_data.h +65372 -0
  29. package/ios/CesiumBridge.h +24 -5
  30. package/ios/CesiumBridge.mm +149 -271
  31. package/ios/HybridCesiumView.swift +197 -40
  32. package/ios/metal/MetalBackend.mm +72 -23
  33. package/lib/typescript/src/specs/cesium-view.nitro.d.ts +26 -0
  34. package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
  35. package/nitrogen/generated/android/c++/JCesiumMetrics.hpp +5 -1
  36. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +109 -1
  37. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +24 -0
  38. package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +48 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CameraState.kt +25 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CesiumMetrics.kt +32 -2
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +72 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Quaternion.kt +19 -0
  43. package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +30 -0
  44. package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +85 -1
  45. package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +60 -0
  46. package/nitrogen/generated/ios/swift/CesiumMetrics.swift +7 -2
  47. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +12 -0
  48. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +288 -0
  49. package/nitrogen/generated/shared/c++/CesiumMetrics.hpp +5 -1
  50. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +24 -0
  51. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +25 -1
  52. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +132 -0
  53. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +13 -1
  54. package/nitrogen/generated/shared/json/CesiumViewConfig.json +12 -0
  55. package/package.json +4 -4
  56. package/scripts/cesium/clone.mjs +1 -1
  57. package/src/specs/cesium-view.nitro.ts +29 -0
  58. package/cpp/engine/DeletionQueue.cpp +0 -28
  59. package/cpp/engine/DeletionQueue.hpp +0 -28
@@ -1,6 +1,7 @@
1
1
  #include "CesiumBridgeAndroid.h"
2
2
 
3
- #include "engine/CesiumEngine.hpp"
3
+ #include "engine/CameraSmoother.hpp"
4
+ #include "engine/EngineTunables.hpp"
4
5
  #include "vulkan/VulkanBackend.h"
5
6
 
6
7
  #include <android/native_window_jni.h>
@@ -8,55 +9,18 @@
8
9
 
9
10
  #include <glm/gtc/quaternion.hpp>
10
11
 
12
+ #include <algorithm>
11
13
  #include <cmath>
12
- #include <regex>
13
- #include <sstream>
14
14
 
15
15
  #define LOG_TAG "CesiumBridge"
16
16
  #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
17
17
  #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
18
18
 
19
- // Per-DOF smoothing constants (match iOS CesiumBridge.mm)
20
- static const double kSmoothAlt = 25.0;
21
- static const double kSmoothHdg = 30.0;
22
- static const double kSmoothRoll = 50.0;
23
- static const double kSmoothPitch = 50.0;
24
- static const double kSmoothViewCorr = 50.0;
25
- static const double kEpsLatLon = 1e-7;
26
- static const double kEpsAlt = 0.1;
27
- static const double kEpsAngleDeg = 0.05;
28
- static const double kEpsQuatDot = 1e-8;
29
-
30
- static double quatDotAbs(const glm::dquat& a, const glm::dquat& b) {
31
- return std::abs(glm::dot(glm::normalize(a), glm::normalize(b)));
32
- }
33
-
34
- static double lerpAngleDeg(double a, double b, double t) {
35
- double diff = std::fmod(b - a + 540.0, 360.0) - 180.0;
36
- return a + diff * t;
37
- }
38
-
39
- static double angleDeltaAbsDeg(double a, double b) {
40
- return std::abs(std::fmod(b - a + 540.0, 360.0) - 180.0);
41
- }
42
-
43
- static std::string stripHtmlToPlain(const std::string& html) {
44
- if (html.empty()) return "";
45
- static const std::regex tagRx("<[^>]+>");
46
- std::string plain = std::regex_replace(html, tagRx, " ");
47
- static const std::regex wsRx("\\s+");
48
- plain = std::regex_replace(plain, wsRx, " ");
49
- // Trim
50
- size_t start = plain.find_first_not_of(" \t\n\r");
51
- size_t end = plain.find_last_not_of(" \t\n\r");
52
- if (start == std::string::npos) return "";
53
- return plain.substr(start, end - start + 1);
54
- }
55
-
56
19
  void CesiumBridgeAndroid::init(JNIEnv* env, jobject surface, int width, int height,
57
- const std::string& cacheDir, const std::string& cacertPath) {
58
- cacheDir_ = cacheDir;
59
- cacertPath_ = cacertPath;
20
+ const std::string& cacheDir,
21
+ const std::string& cacertPath) {
22
+ config_.cacheDatabasePath = cacheDir.empty() ? "" : cacheDir + "/cesium_cache.db";
23
+ config_.tlsCaBundlePath = cacertPath;
60
24
  viewportWidth_ = width;
61
25
  viewportHeight_ = height;
62
26
 
@@ -67,40 +31,22 @@ void CesiumBridgeAndroid::init(JNIEnv* env, jobject surface, int width, int heig
67
31
  }
68
32
 
69
33
  vulkanBackend_ = std::make_unique<reactnativecesium::VulkanBackend>();
34
+ vulkanBackend_->setCacheDir(cacheDir);
70
35
  vulkanBackend_->initialize(window, width, height);
71
36
 
72
37
  frameResult_ = std::make_unique<reactnativecesium::FrameResult>();
73
38
  buildEngine();
74
39
  initialized_ = true;
75
- forceRenderNextFrame_ = true;
76
-
77
- LOGI("CesiumBridgeAndroid initialized %dx%d", width, height);
40
+ target_.requestForceRender();
78
41
  }
79
42
 
80
43
  void CesiumBridgeAndroid::buildEngine() {
81
- reactnativecesium::EngineConfig cfg;
82
- cfg.ionAccessToken = ionAccessToken_;
83
- cfg.ionAssetId = ionTilesetAssetId_;
84
- cfg.cacheDatabasePath = cacheDir_.empty() ? "" : cacheDir_ + "/cesium_cache.db";
85
- cfg.tlsCaBundlePath = cacertPath_;
86
- cfg.maximumScreenSpaceError = std::max(1.0, maxSSE_);
87
- cfg.maximumSimultaneousTileLoads = static_cast<int32_t>(std::max(0.0, std::round(maxSimLoads_)));
88
- cfg.loadingDescendantLimit = static_cast<int32_t>(std::max(1.0, std::round(loadDescLim_)));
89
-
90
44
  engine_ = std::make_unique<reactnativecesium::CesiumEngine>();
91
- engine_->initialize(cfg);
92
-
93
- auto params = engine_->camera().getParams();
94
- camTargetLat_ = params.latitude;
95
- camTargetLon_ = params.longitude;
96
- camTargetAlt_ = params.altitude;
97
- camTargetHeading_ = params.heading;
98
- camTargetPitch_ = params.pitch;
99
- camTargetRoll_ = params.roll;
100
- camTargetViewQw_ = params.viewCorrection.w;
101
- camTargetViewQx_ = params.viewCorrection.x;
102
- camTargetViewQy_ = params.viewCorrection.y;
103
- camTargetViewQz_ = params.viewCorrection.z;
45
+ engine_->initialize(config_);
46
+
47
+ // Seed the demand target from the engine's initial camera so the first
48
+ // frame is not a "snap from default" jump.
49
+ target_.setAll(engine_->camera().getParams());
104
50
 
105
51
  auto* backendPtr = vulkanBackend_.get();
106
52
  engine_->getResourcePreparer()->setGPUTextureCreator(
@@ -121,6 +67,12 @@ void CesiumBridgeAndroid::buildEngine() {
121
67
  });
122
68
  }
123
69
 
70
+ void CesiumBridgeAndroid::applyEngineConfig() {
71
+ if (!engine_) return;
72
+ engine_->updateConfig(config_);
73
+ target_.requestForceRender();
74
+ }
75
+
124
76
  void CesiumBridgeAndroid::shutdown() {
125
77
  if (engine_) engine_->shutdown();
126
78
  if (vulkanBackend_) vulkanBackend_->shutdown();
@@ -131,213 +83,178 @@ void CesiumBridgeAndroid::resize(int width, int height) {
131
83
  viewportWidth_ = width;
132
84
  viewportHeight_ = height;
133
85
  if (vulkanBackend_) vulkanBackend_->resize(width, height);
134
- forceRenderNextFrame_ = true;
86
+ target_.requestForceRender();
135
87
  }
136
88
 
137
- void CesiumBridgeAndroid::updateIonAccessToken(const std::string& token, int64_t assetId) {
138
- if (!initialized_) return;
139
- ionAccessToken_ = token;
140
- ionTilesetAssetId_ = assetId;
141
-
142
- reactnativecesium::EngineConfig cfg;
143
- cfg.ionAccessToken = ionAccessToken_;
144
- cfg.ionAssetId = ionTilesetAssetId_;
145
- cfg.cacheDatabasePath = cacheDir_.empty() ? "" : cacheDir_ + "/cesium_cache.db";
146
- cfg.tlsCaBundlePath = cacertPath_;
147
- cfg.maximumScreenSpaceError = std::max(1.0, maxSSE_);
148
- cfg.maximumSimultaneousTileLoads = static_cast<int32_t>(std::max(0.0, std::round(maxSimLoads_)));
149
- cfg.loadingDescendantLimit = static_cast<int32_t>(std::max(1.0, std::round(loadDescLim_)));
150
-
151
- engine_->updateConfig(cfg);
152
- forceRenderNextFrame_ = true;
89
+ void CesiumBridgeAndroid::updateIonAccessToken(const std::string& token,
90
+ int64_t assetId) {
91
+ config_.ionAccessToken = token;
92
+ config_.ionAssetId = assetId;
93
+ if (initialized_) applyEngineConfig();
153
94
  }
154
95
 
155
96
  void CesiumBridgeAndroid::updateImageryAssetId(int64_t assetId) {
156
- if (!initialized_) return;
157
- engine_->setImageryAssetId(assetId);
158
- forceRenderNextFrame_ = true;
97
+ config_.ionImageryAssetId = (assetId <= 0) ? 1 : assetId;
98
+ if (initialized_ && engine_) {
99
+ engine_->setImageryAssetId(config_.ionImageryAssetId);
100
+ target_.requestForceRender();
101
+ }
159
102
  }
160
103
 
161
104
  void CesiumBridgeAndroid::updateCamera(double lat, double lon, double alt,
162
- double heading, double pitch, double roll) {
105
+ double heading, double pitch, double roll) {
163
106
  if (!initialized_) return;
164
- camTargetLat_ = lat;
165
- camTargetLon_ = lon;
166
- camTargetAlt_ = alt;
167
- camTargetHeading_ = heading;
168
- camTargetPitch_ = pitch;
169
- camTargetRoll_ = roll;
170
- // Does not change camTargetViewQ* (view correction target).
171
- forceRenderNextFrame_ = true;
172
- }
173
-
174
- void CesiumBridgeAndroid::updateCameraQuaternion(double lat, double lon, double alt,
175
- double heading, double pitch, double roll,
176
- double qw, double qx, double qy, double qz) {
107
+ target_.setHpr(lat, lon, alt, heading, pitch, roll);
108
+ }
109
+
110
+ void CesiumBridgeAndroid::updateCameraQuaternion(
111
+ double lat, double lon, double alt, double heading, double pitch,
112
+ double roll, double qw, double qx, double qy, double qz) {
177
113
  if (!initialized_) return;
178
- camTargetLat_ = lat;
179
- camTargetLon_ = lon;
180
- camTargetAlt_ = alt;
181
- camTargetHeading_ = heading;
182
- camTargetPitch_ = pitch;
183
- camTargetRoll_ = roll;
114
+ target_.setHpr(lat, lon, alt, heading, pitch, roll);
184
115
  const double ql2 = qw * qw + qx * qx + qy * qy + qz * qz;
185
- glm::dquat q;
116
+ glm::dquat q;
186
117
  if (ql2 < 1e-20) {
187
118
  q = glm::dquat(1.0, 0.0, 0.0, 0.0);
188
119
  } else {
189
120
  const double inv = 1.0 / std::sqrt(ql2);
190
121
  q = glm::dquat(qw * inv, qx * inv, qy * inv, qz * inv);
191
122
  }
192
- camTargetViewQw_ = q.w;
193
- camTargetViewQx_ = q.x;
194
- camTargetViewQy_ = q.y;
195
- camTargetViewQz_ = q.z;
196
- forceRenderNextFrame_ = true;
123
+ target_.setViewCorrection(q);
197
124
  }
198
125
 
199
126
  void CesiumBridgeAndroid::setVerticalFovDeg(double degrees) {
200
127
  if (!initialized_) return;
201
128
  engine_->camera().setVerticalFovDegrees(degrees);
202
- forceRenderNextFrame_ = true;
129
+ target_.requestForceRender();
203
130
  }
204
131
 
205
132
  void CesiumBridgeAndroid::setMaximumScreenSpaceError(double v) {
206
- maxSSE_ = v;
207
- if (!initialized_) return;
208
- reactnativecesium::EngineConfig cfg;
209
- cfg.ionAccessToken = ionAccessToken_;
210
- cfg.ionAssetId = ionTilesetAssetId_;
211
- cfg.cacheDatabasePath = cacheDir_.empty() ? "" : cacheDir_ + "/cesium_cache.db";
212
- cfg.tlsCaBundlePath = cacertPath_;
213
- cfg.maximumScreenSpaceError = std::max(1.0, maxSSE_);
214
- cfg.maximumSimultaneousTileLoads = static_cast<int32_t>(std::max(0.0, std::round(maxSimLoads_)));
215
- cfg.loadingDescendantLimit = static_cast<int32_t>(std::max(1.0, std::round(loadDescLim_)));
216
- engine_->updateConfig(cfg);
217
- forceRenderNextFrame_ = true;
218
- }
219
-
220
- void CesiumBridgeAndroid::setMaximumSimultaneousTileLoads(int v) {
221
- maxSimLoads_ = static_cast<double>(v);
222
- if (!initialized_) return;
223
- setMaximumScreenSpaceError(maxSSE_); // reuses config rebuild path
133
+ config_.maximumScreenSpaceError = v;
134
+ if (initialized_) applyEngineConfig();
224
135
  }
225
-
226
- void CesiumBridgeAndroid::setLoadingDescendantLimit(int v) {
227
- loadDescLim_ = static_cast<double>(v);
228
- if (!initialized_) return;
229
- setMaximumScreenSpaceError(maxSSE_);
136
+ void CesiumBridgeAndroid::setMaximumSimultaneousTileLoads(int32_t v) {
137
+ config_.maximumSimultaneousTileLoads = v;
138
+ if (initialized_) applyEngineConfig();
139
+ }
140
+ void CesiumBridgeAndroid::setLoadingDescendantLimit(int32_t v) {
141
+ config_.loadingDescendantLimit = v;
142
+ if (initialized_) applyEngineConfig();
230
143
  }
231
144
 
232
145
  void CesiumBridgeAndroid::setMsaaSampleCount(int samples) {
233
146
  if (vulkanBackend_) vulkanBackend_->setMsaaSampleCount(samples);
234
- forceRenderNextFrame_ = true;
147
+ target_.requestForceRender();
235
148
  }
236
149
 
237
- void CesiumBridgeAndroid::markNeedsRender() {
238
- forceRenderNextFrame_ = true;
150
+ void CesiumBridgeAndroid::setMaximumCachedMiB(int32_t v) {
151
+ config_.maximumCachedBytes =
152
+ static_cast<int64_t>(std::max(16, v)) * 1024LL * 1024LL;
153
+ if (initialized_) applyEngineConfig();
154
+ }
155
+ void CesiumBridgeAndroid::setPreloadAncestors(bool v) {
156
+ config_.preloadAncestors = v;
157
+ if (initialized_) applyEngineConfig();
158
+ }
159
+ void CesiumBridgeAndroid::setPreloadSiblings(bool v) {
160
+ config_.preloadSiblings = v;
161
+ if (initialized_) applyEngineConfig();
162
+ }
163
+ void CesiumBridgeAndroid::setForbidHoles(bool v) {
164
+ config_.forbidHoles = v;
165
+ if (initialized_) applyEngineConfig();
166
+ }
167
+ void CesiumBridgeAndroid::setEnableWaterMask(bool v) {
168
+ config_.enableWaterMask = v;
169
+ if (initialized_) applyEngineConfig();
170
+ }
171
+ void CesiumBridgeAndroid::setEnableFogCulling(bool v) {
172
+ config_.enableFogCulling = v;
173
+ if (initialized_) applyEngineConfig();
174
+ }
175
+ void CesiumBridgeAndroid::setEnforceCulledScreenSpaceError(bool v) {
176
+ config_.enforceCulledScreenSpaceError = v;
177
+ if (initialized_) applyEngineConfig();
178
+ }
179
+ void CesiumBridgeAndroid::setCulledScreenSpaceError(double v) {
180
+ config_.culledScreenSpaceError = v;
181
+ if (initialized_) applyEngineConfig();
182
+ }
183
+ void CesiumBridgeAndroid::setEnableLodTransitionPeriod(bool v) {
184
+ config_.enableLodTransitionPeriod = v;
185
+ if (initialized_) applyEngineConfig();
186
+ }
187
+ void CesiumBridgeAndroid::setLodTransitionLength(double v) {
188
+ config_.lodTransitionLength = v;
189
+ if (initialized_) applyEngineConfig();
190
+ }
191
+ void CesiumBridgeAndroid::setSqliteCacheMaxRows(int32_t v) {
192
+ // Re-creating SqliteCache requires a tileset rebuild, so we only honour the
193
+ // value on next init.
194
+ config_.sqliteCacheMaxRows = std::max(64, v);
195
+ }
196
+ void CesiumBridgeAndroid::setTaskProcessorThreads(int32_t v) {
197
+ // Worker pool is sized once per CesiumEngine. We mirror it for the next
198
+ // initialize().
199
+ config_.taskProcessorThreads = std::max(0, v);
239
200
  }
240
201
 
202
+ void CesiumBridgeAndroid::markNeedsRender() { target_.requestForceRender(); }
203
+
241
204
  bool CesiumBridgeAndroid::shouldRenderNextFrame() {
242
205
  if (!initialized_ || !engine_) return false;
243
206
 
244
- const auto cur = engine_->camera().getParams();
245
- glm::dquat tgt(camTargetViewQw_, camTargetViewQx_, camTargetViewQy_, camTargetViewQz_);
246
- const bool cameraDirty =
247
- std::abs(camTargetLat_ - cur.latitude) > kEpsLatLon ||
248
- std::abs(camTargetLon_ - cur.longitude) > kEpsLatLon ||
249
- std::abs(camTargetAlt_ - cur.altitude) > kEpsAlt ||
250
- angleDeltaAbsDeg(cur.heading, camTargetHeading_) > kEpsAngleDeg ||
251
- angleDeltaAbsDeg(cur.pitch, camTargetPitch_) > kEpsAngleDeg ||
252
- angleDeltaAbsDeg(cur.roll, camTargetRoll_) > kEpsAngleDeg ||
253
- (1.0 - quatDotAbs(cur.viewCorrection, tgt)) > kEpsQuatDot;
254
-
255
- return forceRenderNextFrame_ ||
256
- frameResult_->tilesLoading > 0 ||
257
- !frameResult_->tilesetActive ||
258
- cameraDirty;
207
+ if (target_.consumeForceRender()) {
208
+ // Force-render request observed: keep it sticky for one frame so we make
209
+ // it through to the actual GPU encode below.
210
+ target_.requestForceRender();
211
+ return true;
212
+ }
213
+ if (frameResult_->tilesLoading > 0 || !frameResult_->tilesetActive) {
214
+ return true;
215
+ }
216
+ // Keep rendering at full speed until we have actual terrain tiles loaded.
217
+ // This ensures tiles start loading immediately instead of falling into idle
218
+ // mode (250ms intervals) before any content appears.
219
+ if (frameResult_->tilesRendered < 5) {
220
+ return true;
221
+ }
222
+ if (target_.isDirty()) {
223
+ const auto cur = engine_->camera().getParams();
224
+ const auto tgt = target_.snapshot();
225
+ if (reactnativecesium::CameraTargetState::deltaExceedsEpsilon(cur, tgt)) {
226
+ return true;
227
+ }
228
+ target_.clearDirty();
229
+ }
230
+ return false;
259
231
  }
260
232
 
261
233
  void CesiumBridgeAndroid::renderFrame(double dt) {
262
234
  if (!initialized_) return;
263
235
 
264
- auto cur = engine_->camera().getParams();
265
-
266
- // Smooth follower: track camera target per DOF (same as iOS)
267
- cur.latitude = camTargetLat_;
268
- cur.longitude = camTargetLon_;
269
-
270
- const double aAlt = 1.0 - std::exp(-kSmoothAlt * dt);
271
- const double aHdg = 1.0 - std::exp(-kSmoothHdg * dt);
272
- const double aPitch = 1.0 - std::exp(-kSmoothPitch * dt);
273
- const double aRoll = 1.0 - std::exp(-kSmoothRoll * dt);
274
- const double aViewQ = 1.0 - std::exp(-kSmoothViewCorr * dt);
275
-
276
- cur.altitude = cur.altitude + aAlt * (camTargetAlt_ - cur.altitude);
277
- cur.heading = lerpAngleDeg(cur.heading, camTargetHeading_, aHdg);
278
- cur.pitch = lerpAngleDeg(cur.pitch, camTargetPitch_, aPitch);
279
- cur.roll = lerpAngleDeg(cur.roll, camTargetRoll_, aRoll);
280
-
281
- glm::dquat cq = glm::normalize(cur.viewCorrection);
282
- glm::dquat tq = glm::normalize(glm::dquat(camTargetViewQw_, camTargetViewQx_, camTargetViewQy_, camTargetViewQz_));
283
- if (glm::dot(cq, tq) < 0.0)
284
- tq = -tq;
285
- cur.viewCorrection = glm::normalize(glm::slerp(cq, tq, aViewQ));
286
-
287
- if (std::abs(camTargetAlt_ - cur.altitude) <= kEpsAlt)
288
- cur.altitude = camTargetAlt_;
289
- if (angleDeltaAbsDeg(cur.heading, camTargetHeading_) <= kEpsAngleDeg)
290
- cur.heading = camTargetHeading_;
291
- if (angleDeltaAbsDeg(cur.pitch, camTargetPitch_) <= kEpsAngleDeg)
292
- cur.pitch = camTargetPitch_;
293
- if (angleDeltaAbsDeg(cur.roll, camTargetRoll_) <= kEpsAngleDeg)
294
- cur.roll = camTargetRoll_;
295
- if ((1.0 - quatDotAbs(cur.viewCorrection, tq)) <= kEpsQuatDot)
296
- cur.viewCorrection = tq;
297
-
298
- engine_->camera().setParams(cur);
299
- engine_->updateFrame(viewportWidth_, viewportHeight_, *frameResult_);
236
+ // Consume the force-render flag here (it may have been re-armed by
237
+ // shouldRenderNextFrame; either way we're rendering this frame).
238
+ (void)target_.consumeForceRender();
300
239
 
301
- const double instFps = (dt > 1e-6) ? (1.0 / dt) : 0.0;
302
- fpsEma_ = (fpsEma_ <= 1e-6) ? instFps : (fpsEma_ * 0.85 + instFps * 0.15);
303
-
304
- if (++metricsTick_ >= 20) {
305
- metricsTick_ = 0;
306
- metricsFps_ = fpsEma_;
307
- metricsTilesRendered_ = frameResult_->tilesRendered;
308
- metricsTilesLoading_ = frameResult_->tilesLoading;
309
- metricsTilesVisited_ = frameResult_->tilesVisited;
310
- metricsIonTokenConfigured_ = frameResult_->ionTokenConfigured;
311
- metricsTilesetReady_ = frameResult_->tilesetActive;
312
-
313
- if (!frameResult_->creditHtmlLines.empty()) {
314
- std::string joined;
315
- for (const auto& html : frameResult_->creditHtmlLines) {
316
- if (!joined.empty()) joined += "|";
317
- joined += html;
318
- }
319
- if (joined != lastCreditHtmlJoined_) {
320
- lastCreditHtmlJoined_ = joined;
321
- std::string credits;
322
- for (const auto& html : frameResult_->creditHtmlLines) {
323
- std::string chunk = stripHtmlToPlain(html);
324
- if (chunk.empty()) continue;
325
- if (!credits.empty()) credits += " · ";
326
- credits += chunk;
327
- }
328
- metricsCreditsPlainText_ = credits;
329
- }
330
- } else {
331
- lastCreditHtmlJoined_.clear();
332
- metricsCreditsPlainText_.clear();
333
- }
240
+ const auto cur = engine_->camera().getParams();
241
+ const auto tgt = target_.snapshot();
242
+ const auto smooth = reactnativecesium::CameraSmoother::step(
243
+ cur, tgt, dt, target_.recentIntervalSec());
244
+ engine_->camera().setParams(smooth);
245
+
246
+ // If we converged, clear the dirty flag so the next idle vsync can early-out.
247
+ if (!reactnativecesium::CameraTargetState::deltaExceedsEpsilon(smooth, tgt)) {
248
+ target_.clearDirty();
334
249
  }
335
250
 
251
+ engine_->updateFrame(viewportWidth_, viewportHeight_, *frameResult_);
252
+ metrics_.tick(dt, *frameResult_, !config_.tlsCaBundlePath.empty());
253
+
336
254
  reactnativecesium::FrameParams params;
337
255
  vulkanBackend_->beginFrame(params);
338
256
  vulkanBackend_->drawScene(*frameResult_);
339
257
  vulkanBackend_->endFrame();
340
- forceRenderNextFrame_ = false;
341
258
  }
342
259
 
343
260
  double CesiumBridgeAndroid::readCameraLatitude() {
@@ -361,7 +278,6 @@ double CesiumBridgeAndroid::readCameraRoll() {
361
278
  double CesiumBridgeAndroid::readVerticalFovDeg() {
362
279
  return engine_ ? engine_->camera().getVerticalFovDegrees() : 60.0;
363
280
  }
364
-
365
281
  double CesiumBridgeAndroid::readViewCorrectionW() {
366
282
  return engine_ ? engine_->camera().getParams().viewCorrection.w : 1.0;
367
283
  }
@@ -566,9 +482,63 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetMetricsTileset
566
482
  return getBridge(ptr)->metricsTilesetReady() ? JNI_TRUE : JNI_FALSE;
567
483
  }
568
484
 
485
+ JNIEXPORT jboolean JNICALL
486
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetMetricsTlsConfigured(JNIEnv*, jobject, jlong ptr) {
487
+ return getBridge(ptr)->metricsTlsConfigured() ? JNI_TRUE : JNI_FALSE;
488
+ }
489
+
569
490
  JNIEXPORT jstring JNICALL
570
491
  Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetMetricsCreditsPlainText(JNIEnv* env, jobject, jlong ptr) {
571
492
  return env->NewStringUTF(getBridge(ptr)->metricsCreditsPlainText().c_str());
572
493
  }
573
494
 
495
+ JNIEXPORT void JNICALL
496
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetMaximumCachedMiB(JNIEnv*, jobject, jlong ptr, jint v) {
497
+ getBridge(ptr)->setMaximumCachedMiB(v);
498
+ }
499
+ JNIEXPORT void JNICALL
500
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetPreloadAncestors(JNIEnv*, jobject, jlong ptr, jboolean v) {
501
+ getBridge(ptr)->setPreloadAncestors(v == JNI_TRUE);
502
+ }
503
+ JNIEXPORT void JNICALL
504
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetPreloadSiblings(JNIEnv*, jobject, jlong ptr, jboolean v) {
505
+ getBridge(ptr)->setPreloadSiblings(v == JNI_TRUE);
506
+ }
507
+ JNIEXPORT void JNICALL
508
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetForbidHoles(JNIEnv*, jobject, jlong ptr, jboolean v) {
509
+ getBridge(ptr)->setForbidHoles(v == JNI_TRUE);
510
+ }
511
+ JNIEXPORT void JNICALL
512
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetEnableWaterMask(JNIEnv*, jobject, jlong ptr, jboolean v) {
513
+ getBridge(ptr)->setEnableWaterMask(v == JNI_TRUE);
514
+ }
515
+ JNIEXPORT void JNICALL
516
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetEnableFogCulling(JNIEnv*, jobject, jlong ptr, jboolean v) {
517
+ getBridge(ptr)->setEnableFogCulling(v == JNI_TRUE);
518
+ }
519
+ JNIEXPORT void JNICALL
520
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetEnforceCulledScreenSpaceError(JNIEnv*, jobject, jlong ptr, jboolean v) {
521
+ getBridge(ptr)->setEnforceCulledScreenSpaceError(v == JNI_TRUE);
522
+ }
523
+ JNIEXPORT void JNICALL
524
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetCulledScreenSpaceError(JNIEnv*, jobject, jlong ptr, jdouble v) {
525
+ getBridge(ptr)->setCulledScreenSpaceError(v);
526
+ }
527
+ JNIEXPORT void JNICALL
528
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetEnableLodTransitionPeriod(JNIEnv*, jobject, jlong ptr, jboolean v) {
529
+ getBridge(ptr)->setEnableLodTransitionPeriod(v == JNI_TRUE);
530
+ }
531
+ JNIEXPORT void JNICALL
532
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetLodTransitionLength(JNIEnv*, jobject, jlong ptr, jdouble v) {
533
+ getBridge(ptr)->setLodTransitionLength(v);
534
+ }
535
+ JNIEXPORT void JNICALL
536
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetSqliteCacheMaxRows(JNIEnv*, jobject, jlong ptr, jint v) {
537
+ getBridge(ptr)->setSqliteCacheMaxRows(v);
538
+ }
539
+ JNIEXPORT void JNICALL
540
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetTaskProcessorThreads(JNIEnv*, jobject, jlong ptr, jint v) {
541
+ getBridge(ptr)->setTaskProcessorThreads(v);
542
+ }
543
+
574
544
  } // extern "C"
@@ -4,13 +4,21 @@
4
4
  #include <memory>
5
5
  #include <string>
6
6
 
7
+ #include "engine/CameraTargetState.hpp"
8
+ #include "engine/CesiumEngine.hpp"
9
+ #include "engine/MetricsAggregator.hpp"
10
+
7
11
  namespace reactnativecesium {
8
12
  class VulkanBackend;
9
- class CesiumEngine;
10
13
  struct FrameResult;
11
- struct CameraParams;
12
- }
14
+ } // namespace reactnativecesium
13
15
 
16
+ // Thin Android shell. All cross-platform logic (camera demand state +
17
+ // smoothing, FPS / metrics aggregation, credit HTML stripping) lives in
18
+ // shared C++ next to CesiumEngine. This class only owns:
19
+ // - the JNI/window surface,
20
+ // - the Vulkan backend instance, and
21
+ // - the EngineConfig snapshot mirrored from JS props.
14
22
  struct CesiumBridgeAndroid {
15
23
  void init(JNIEnv* env, jobject surface, int width, int height,
16
24
  const std::string& cacheDir, const std::string& cacertPath);
@@ -27,10 +35,24 @@ struct CesiumBridgeAndroid {
27
35
  double qw, double qx, double qy, double qz);
28
36
  void setVerticalFovDeg(double degrees);
29
37
  void setMaximumScreenSpaceError(double v);
30
- void setMaximumSimultaneousTileLoads(int v);
31
- void setLoadingDescendantLimit(int v);
38
+ void setMaximumSimultaneousTileLoads(int32_t v);
39
+ void setLoadingDescendantLimit(int32_t v);
32
40
  void setMsaaSampleCount(int samples);
33
41
 
42
+ // Optional knobs (default values mirror EngineConfig defaults).
43
+ void setMaximumCachedMiB(int32_t v);
44
+ void setPreloadAncestors(bool v);
45
+ void setPreloadSiblings(bool v);
46
+ void setForbidHoles(bool v);
47
+ void setEnableWaterMask(bool v);
48
+ void setEnableFogCulling(bool v);
49
+ void setEnforceCulledScreenSpaceError(bool v);
50
+ void setCulledScreenSpaceError(double v);
51
+ void setEnableLodTransitionPeriod(bool v);
52
+ void setLodTransitionLength(double v);
53
+ void setSqliteCacheMaxRows(int32_t v);
54
+ void setTaskProcessorThreads(int32_t v);
55
+
34
56
  void markNeedsRender();
35
57
  bool shouldRenderNextFrame();
36
58
  void renderFrame(double dt);
@@ -48,57 +70,34 @@ struct CesiumBridgeAndroid {
48
70
  double readViewCorrectionY();
49
71
  double readViewCorrectionZ();
50
72
 
51
- // Metrics
52
- double metricsFps() const { return metricsFps_; }
53
- int metricsTilesRendered() const { return metricsTilesRendered_; }
54
- int metricsTilesLoading() const { return metricsTilesLoading_; }
55
- int metricsTilesVisited() const { return metricsTilesVisited_; }
56
- bool metricsIonTokenConfigured() const { return metricsIonTokenConfigured_; }
57
- bool metricsTilesetReady() const { return metricsTilesetReady_; }
58
- const std::string& metricsCreditsPlainText() const { return metricsCreditsPlainText_; }
73
+ // Metrics readback (delegated to MetricsAggregator)
74
+ double metricsFps() const { return metrics_.latest().fps; }
75
+ int metricsTilesRendered() const { return metrics_.latest().tilesRendered; }
76
+ int metricsTilesLoading() const { return metrics_.latest().tilesLoading; }
77
+ int metricsTilesVisited() const { return metrics_.latest().tilesVisited; }
78
+ bool metricsIonTokenConfigured() const { return metrics_.latest().ionTokenConfigured; }
79
+ bool metricsTilesetReady() const { return metrics_.latest().tilesetReady; }
80
+ bool metricsTlsConfigured() const { return metrics_.latest().tlsConfigured; }
81
+ const std::string& metricsCreditsPlainText() const {
82
+ return metrics_.latest().creditsPlainText;
83
+ }
59
84
 
60
85
  private:
61
86
  void buildEngine();
87
+ // Push the current `config_` state to the live engine (runtime-mutable
88
+ // fields go through tileset_->getOptions(); token/asset id changes force
89
+ // a tileset rebuild).
90
+ void applyEngineConfig();
62
91
 
63
92
  std::unique_ptr<reactnativecesium::VulkanBackend> vulkanBackend_;
64
93
  std::unique_ptr<reactnativecesium::CesiumEngine> engine_;
65
94
  std::unique_ptr<reactnativecesium::FrameResult> frameResult_;
66
95
 
67
- int viewportWidth_ = 0;
68
- int viewportHeight_ = 0;
69
- bool initialized_ = false;
70
-
71
- std::string cacheDir_;
72
- std::string cacertPath_;
73
- std::string ionAccessToken_;
74
- int64_t ionTilesetAssetId_ = 1;
75
-
76
- double maxSSE_ = 32.0;
77
- double maxSimLoads_ = 12.0;
78
- double loadDescLim_ = 20.0;
79
-
80
- // Camera demand target
81
- double camTargetLat_ = 46.15;
82
- double camTargetLon_ = 7.35;
83
- double camTargetAlt_ = 12000.0;
84
- double camTargetHeading_ = 129.0;
85
- double camTargetPitch_ = -45.0;
86
- double camTargetRoll_ = 0.0;
87
- double camTargetViewQw_ = 1.0;
88
- double camTargetViewQx_ = 0.0;
89
- double camTargetViewQy_ = 0.0;
90
- double camTargetViewQz_ = 0.0;
91
- bool forceRenderNextFrame_ = true;
96
+ int viewportWidth_ = 0;
97
+ int viewportHeight_ = 0;
98
+ bool initialized_ = false;
92
99
 
93
- // Metrics
94
- double fpsEma_ = 0.0;
95
- int metricsTick_ = 0;
96
- double metricsFps_ = 0.0;
97
- int metricsTilesRendered_ = 0;
98
- int metricsTilesLoading_ = 0;
99
- int metricsTilesVisited_ = 0;
100
- bool metricsIonTokenConfigured_ = false;
101
- bool metricsTilesetReady_ = false;
102
- std::string metricsCreditsPlainText_;
103
- std::string lastCreditHtmlJoined_;
100
+ reactnativecesium::EngineConfig config_;
101
+ reactnativecesium::CameraTargetState target_;
102
+ reactnativecesium::MetricsAggregator metrics_;
104
103
  };