react-native-cesium 1.2.2 → 1.2.4

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 (40) hide show
  1. package/README.md +100 -26
  2. package/android/CMakeLists.txt +1 -2
  3. package/android/src/main/cpp/CesiumBridgeAndroid.cpp +264 -130
  4. package/android/src/main/cpp/CesiumBridgeAndroid.h +70 -27
  5. package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +68 -44
  6. package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +160 -187
  7. package/cpp/engine/CameraIntegrator.cpp +374 -0
  8. package/cpp/engine/CameraIntegrator.hpp +146 -0
  9. package/cpp/engine/CesiumEngine.cpp +3 -1
  10. package/cpp/engine/EngineTunables.hpp +80 -21
  11. package/cpp/engine/GlobeCamera.cpp +17 -6
  12. package/cpp/engine/GlobeCamera.hpp +11 -8
  13. package/cpp/engine/test/CameraIntegratorTest.cpp +205 -0
  14. package/cpp/engine/test/build_and_run.sh +27 -0
  15. package/example/README.md +1 -1
  16. package/ios/CesiumBridge.h +53 -28
  17. package/ios/CesiumBridge.mm +172 -86
  18. package/ios/HybridCesiumView.swift +213 -145
  19. package/lib/typescript/src/specs/cesium-view.nitro.d.ts +77 -13
  20. package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
  21. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +90 -9
  22. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +19 -3
  23. package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +20 -0
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +57 -3
  25. package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +9 -9
  26. package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +82 -9
  27. package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +25 -0
  28. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +14 -3
  29. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +210 -16
  30. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +19 -3
  31. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +20 -4
  32. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +55 -0
  33. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +5 -0
  34. package/nitrogen/generated/shared/json/CesiumViewConfig.json +5 -0
  35. package/package.json +2 -2
  36. package/src/specs/cesium-view.nitro.ts +93 -14
  37. package/cpp/engine/CameraSmoother.cpp +0 -71
  38. package/cpp/engine/CameraSmoother.hpp +0 -25
  39. package/cpp/engine/CameraTargetState.cpp +0 -140
  40. package/cpp/engine/CameraTargetState.hpp +0 -103
@@ -1,6 +1,5 @@
1
1
  #include "CesiumBridgeAndroid.h"
2
2
 
3
- #include "engine/CameraSmoother.hpp"
4
3
  #include "engine/EngineTunables.hpp"
5
4
  #include "engine/GeoidConverter.hpp"
6
5
  #include "vulkan/VulkanBackend.h"
@@ -11,12 +10,19 @@
11
10
  #include <glm/gtc/quaternion.hpp>
12
11
 
13
12
  #include <algorithm>
13
+ #include <chrono>
14
14
  #include <cmath>
15
15
 
16
16
  #define LOG_TAG "CesiumBridge"
17
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
18
17
  #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
19
18
 
19
+ CesiumBridgeAndroid::~CesiumBridgeAndroid() {
20
+ // shutdown() is idempotent — calling it from the dtor in addition to the
21
+ // explicit nativeShutdown JNI path is safe and guarantees the
22
+ // ANativeWindow ref is released even if the lifecycle hooks misfire.
23
+ shutdown();
24
+ }
25
+
20
26
  void CesiumBridgeAndroid::init(JNIEnv* env, jobject surface, int width, int height,
21
27
  const std::string& cacheDir,
22
28
  const std::string& cacertPath) {
@@ -25,29 +31,34 @@ void CesiumBridgeAndroid::init(JNIEnv* env, jobject surface, int width, int heig
25
31
  viewportWidth_ = width;
26
32
  viewportHeight_ = height;
27
33
 
28
- ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
29
- if (!window) {
34
+ // ANativeWindow_fromSurface() acquires one strong reference on the
35
+ // returned window; we own it now and must call ANativeWindow_release() in
36
+ // shutdown() (or before re-acquiring) to avoid leaking it on every view
37
+ // remount.
38
+ nativeWindow_ = ANativeWindow_fromSurface(env, surface);
39
+ if (!nativeWindow_) {
30
40
  LOGE("Failed to get ANativeWindow from Surface");
31
41
  return;
32
42
  }
33
43
 
34
44
  vulkanBackend_ = std::make_unique<reactnativecesium::VulkanBackend>();
35
45
  vulkanBackend_->setCacheDir(cacheDir);
36
- vulkanBackend_->initialize(window, width, height);
46
+ vulkanBackend_->initialize(nativeWindow_, width, height);
37
47
 
38
48
  frameResult_ = std::make_unique<reactnativecesium::FrameResult>();
39
49
  buildEngine();
40
50
  initialized_ = true;
41
- target_.requestForceRender();
51
+ forceRender_.store(true, std::memory_order_release);
42
52
  }
43
53
 
44
54
  void CesiumBridgeAndroid::buildEngine() {
45
55
  engine_ = std::make_unique<reactnativecesium::CesiumEngine>();
46
56
  engine_->initialize(config_);
47
57
 
48
- // Seed the demand target from the engine's initial camera so the first
49
- // frame is not a "snap from default" jump.
50
- target_.setAll(engine_->camera().getParams());
58
+ // Seed the integrator from the engine's initial camera so the first frame
59
+ // is not a "snap from default" jump.
60
+ integrator_ = std::make_unique<reactnativecesium::CameraIntegrator>(
61
+ engine_->camera().getParams());
51
62
 
52
63
  auto* backendPtr = vulkanBackend_.get();
53
64
  engine_->getResourcePreparer()->setGPUTextureCreator(
@@ -71,20 +82,42 @@ void CesiumBridgeAndroid::buildEngine() {
71
82
  void CesiumBridgeAndroid::applyEngineConfig() {
72
83
  if (!engine_) return;
73
84
  engine_->updateConfig(config_);
74
- target_.requestForceRender();
85
+ forceRender_.store(true, std::memory_order_release);
75
86
  }
76
87
 
77
88
  void CesiumBridgeAndroid::shutdown() {
89
+ // Mark uninitialised up-front so any racing prop setters become no-ops
90
+ // instead of touching half-destroyed state.
91
+ initialized_ = false;
92
+
78
93
  if (engine_) engine_->shutdown();
79
94
  if (vulkanBackend_) vulkanBackend_->shutdown();
80
- initialized_ = false;
95
+
96
+ // Release the engine (and therefore its SqliteCache) here rather than
97
+ // waiting for ~CesiumBridgeAndroid. During fast refresh the next
98
+ // CesiumView can mount and open the same cesium_cache.db file before
99
+ // this JNI peer is destroyed; freeing the unique_ptr now closes the
100
+ // SQLite handle synchronously and avoids "database is locked"
101
+ // warnings from the new engine's SqliteCache.
102
+ integrator_.reset();
103
+ engine_.reset();
104
+ vulkanBackend_.reset();
105
+ frameResult_.reset();
106
+
107
+ // Release the ANativeWindow ref acquired in init(). Order matters: the
108
+ // Vulkan backend's vkDestroySurfaceKHR must have run first (which it has,
109
+ // inside vulkanBackend_->shutdown() above) before we drop our reference.
110
+ if (nativeWindow_) {
111
+ ANativeWindow_release(nativeWindow_);
112
+ nativeWindow_ = nullptr;
113
+ }
81
114
  }
82
115
 
83
116
  void CesiumBridgeAndroid::resize(int width, int height) {
84
117
  viewportWidth_ = width;
85
118
  viewportHeight_ = height;
86
119
  if (vulkanBackend_) vulkanBackend_->resize(width, height);
87
- target_.requestForceRender();
120
+ forceRender_.store(true, std::memory_order_release);
88
121
  }
89
122
 
90
123
  void CesiumBridgeAndroid::updateIonAccessToken(const std::string& token,
@@ -98,36 +131,77 @@ void CesiumBridgeAndroid::updateImageryAssetId(int64_t assetId) {
98
131
  config_.ionImageryAssetId = (assetId <= 0) ? 1 : assetId;
99
132
  if (initialized_ && engine_) {
100
133
  engine_->setImageryAssetId(config_.ionImageryAssetId);
101
- target_.requestForceRender();
134
+ forceRender_.store(true, std::memory_order_release);
102
135
  }
103
136
  }
104
137
 
105
- void CesiumBridgeAndroid::updateCamera(double lat, double lon, double alt,
106
- double heading, double pitch, double roll) {
107
- if (!initialized_) return;
108
- target_.setHpr(lat, lon, alt, heading, pitch, roll);
109
- }
110
-
111
- void CesiumBridgeAndroid::updateCameraQuaternion(
112
- double lat, double lon, double alt, double heading, double pitch,
113
- double roll, double qw, double qx, double qy, double qz) {
114
- if (!initialized_) return;
115
- target_.setHpr(lat, lon, alt, heading, pitch, roll);
116
- const double ql2 = qw * qw + qx * qx + qy * qy + qz * qz;
117
- glm::dquat q;
118
- if (ql2 < 1e-20) {
119
- q = glm::dquat(1.0, 0.0, 0.0, 0.0);
120
- } else {
121
- const double inv = 1.0 / std::sqrt(ql2);
122
- q = glm::dquat(qw * inv, qx * inv, qy * inv, qz * inv);
123
- }
124
- target_.setViewCorrection(q);
138
+ // ── Per-DoF camera demand setters ─────────────────────────────────────────
139
+
140
+ void CesiumBridgeAndroid::setPosition(double lat, double lon) {
141
+ if (!initialized_ || !integrator_) return;
142
+ integrator_->setPosition(lat, lon);
143
+ forceRender_.store(true, std::memory_order_release);
144
+ }
145
+
146
+ void CesiumBridgeAndroid::setAltitude(double alt) {
147
+ if (!initialized_ || !integrator_) return;
148
+ integrator_->setAltitude(alt);
149
+ forceRender_.store(true, std::memory_order_release);
150
+ }
151
+
152
+ void CesiumBridgeAndroid::setHeading(double headingDeg) {
153
+ if (!initialized_ || !integrator_) return;
154
+ integrator_->setHeading(headingDeg);
155
+ forceRender_.store(true, std::memory_order_release);
156
+ }
157
+
158
+ void CesiumBridgeAndroid::setAttitude(double pitchDeg, double rollDeg) {
159
+ if (!initialized_ || !integrator_) return;
160
+ integrator_->setAttitude(pitchDeg, rollDeg);
161
+ forceRender_.store(true, std::memory_order_release);
162
+ }
163
+
164
+ void CesiumBridgeAndroid::setViewCorrection(double qw, double qx, double qy, double qz) {
165
+ if (!initialized_ || !integrator_) return;
166
+ integrator_->setViewCorrection(glm::dquat(qw, qx, qy, qz));
167
+ forceRender_.store(true, std::memory_order_release);
168
+ }
169
+
170
+ void CesiumBridgeAndroid::teleport(double lat, double lon, double alt,
171
+ double heading, double pitch, double roll,
172
+ double vfov) {
173
+ if (!initialized_ || !integrator_) return;
174
+ reactnativecesium::CameraParams p;
175
+ p.latitude = lat;
176
+ p.longitude = lon;
177
+ p.altitude = alt;
178
+ p.heading = heading;
179
+ p.pitch = pitch;
180
+ p.roll = roll;
181
+ p.verticalFov = vfov;
182
+ p.viewCorrection = integrator_->getActual().viewCorrection;
183
+ integrator_->teleport(p);
184
+ engine_->camera().setParams(p);
185
+ forceRender_.store(true, std::memory_order_release);
125
186
  }
126
187
 
127
188
  void CesiumBridgeAndroid::setVerticalFovDeg(double degrees) {
128
- if (!initialized_) return;
129
- engine_->camera().setVerticalFovDegrees(degrees);
130
- target_.requestForceRender();
189
+ if (!initialized_ || !integrator_) return;
190
+ integrator_->setVerticalFov(degrees);
191
+ forceRender_.store(true, std::memory_order_release);
192
+ }
193
+
194
+ void CesiumBridgeAndroid::setRateCaps(double yawDegSec, double pitchDegSec,
195
+ double rollDegSec, double climbMps,
196
+ double groundMps) {
197
+ if (!initialized_ || !integrator_) return;
198
+ reactnativecesium::CameraRateCaps caps;
199
+ caps.maxYawRateDegSec = yawDegSec;
200
+ caps.maxPitchRateDegSec = pitchDegSec;
201
+ caps.maxRollRateDegSec = rollDegSec;
202
+ caps.maxClimbRateMps = climbMps;
203
+ caps.maxGroundSpeedMps = groundMps;
204
+ integrator_->setRateCaps(caps);
131
205
  }
132
206
 
133
207
  void CesiumBridgeAndroid::setMaximumScreenSpaceError(double v) {
@@ -145,7 +219,7 @@ void CesiumBridgeAndroid::setLoadingDescendantLimit(int32_t v) {
145
219
 
146
220
  void CesiumBridgeAndroid::setMsaaSampleCount(int samples) {
147
221
  if (vulkanBackend_) vulkanBackend_->setMsaaSampleCount(samples);
148
- target_.requestForceRender();
222
+ forceRender_.store(true, std::memory_order_release);
149
223
  }
150
224
 
151
225
  void CesiumBridgeAndroid::setMaximumCachedMiB(int32_t v) {
@@ -190,13 +264,9 @@ void CesiumBridgeAndroid::setLodTransitionLength(double v) {
190
264
  if (initialized_) applyEngineConfig();
191
265
  }
192
266
  void CesiumBridgeAndroid::setSqliteCacheMaxRows(int32_t v) {
193
- // Re-creating SqliteCache requires a tileset rebuild, so we only honour the
194
- // value on next init.
195
267
  config_.sqliteCacheMaxRows = std::max(64, v);
196
268
  }
197
269
  void CesiumBridgeAndroid::setTaskProcessorThreads(int32_t v) {
198
- // Worker pool is sized once per CesiumEngine. We mirror it for the next
199
- // initialize().
200
270
  config_.taskProcessorThreads = std::max(0, v);
201
271
  }
202
272
  void CesiumBridgeAndroid::setMinAltitudeAboveTerrain(float v) {
@@ -204,70 +274,55 @@ void CesiumBridgeAndroid::setMinAltitudeAboveTerrain(float v) {
204
274
  if (initialized_) applyEngineConfig();
205
275
  }
206
276
 
207
- void CesiumBridgeAndroid::markNeedsRender() { target_.requestForceRender(); }
277
+ void CesiumBridgeAndroid::markNeedsRender() {
278
+ forceRender_.store(true, std::memory_order_release);
279
+ }
208
280
 
209
281
  bool CesiumBridgeAndroid::shouldRenderNextFrame() {
210
- if (!initialized_ || !engine_) return false;
211
-
212
- if (target_.consumeForceRender()) {
213
- // Force-render request observed: keep it sticky for one frame so we make
214
- // it through to the actual GPU encode below.
215
- target_.requestForceRender();
216
- return true;
217
- }
218
- if (frameResult_->tilesLoading > 0 || !frameResult_->tilesetActive) {
219
- return true;
220
- }
282
+ if (!initialized_ || !engine_ || !frameResult_) return false;
283
+ if (forceRender_.load(std::memory_order_acquire)) return true;
284
+ if (frameResult_->tilesLoading > 0 || !frameResult_->tilesetActive) return true;
221
285
  // Keep rendering at full speed until we have actual terrain tiles loaded.
222
286
  // This ensures tiles start loading immediately instead of falling into idle
223
287
  // mode (250ms intervals) before any content appears.
224
- if (frameResult_->tilesRendered < 5) {
225
- return true;
226
- }
227
- if (target_.isDirty()) {
228
- const auto cur = engine_->camera().getParams();
229
- const auto tgt = target_.snapshot();
230
- if (reactnativecesium::CameraTargetState::deltaExceedsEpsilon(cur, tgt)) {
231
- return true;
232
- }
233
- target_.clearDirty();
234
- }
288
+ if (frameResult_->tilesRendered < 5) return true;
289
+ if (integrator_ && integrator_->isActive()) return true;
235
290
  return false;
236
291
  }
237
292
 
238
- void CesiumBridgeAndroid::renderFrame(double dt) {
239
- if (!initialized_) return;
293
+ void CesiumBridgeAndroid::renderFrame(double nowSeconds) {
294
+ if (!initialized_ || !integrator_) return;
295
+ if (!engine_ || !vulkanBackend_ || !frameResult_) return;
240
296
 
241
- // Consume the force-render flag here (it may have been re-armed by
242
- // shouldRenderNextFrame; either way we're rendering this frame).
243
- (void)target_.consumeForceRender();
297
+ forceRender_.store(false, std::memory_order_release);
244
298
 
245
- const auto cur = engine_->camera().getParams();
246
- const auto tgt = target_.snapshot();
247
- auto smooth = reactnativecesium::CameraSmoother::step(cur, tgt, dt);
299
+ // The integrator owns its own clock — see CameraIntegrator::step().
300
+ // `nowSeconds` is still used below for the metrics-only dt EMA.
301
+ auto actual = integrator_->step();
248
302
 
249
303
  // ── Terrain floor clamp ───────────────────────────────────────────────
250
304
  const float minAbove = engine_->getConfig().minAltitudeAboveTerrain;
251
305
  if (minAbove > 0.0f) {
252
306
  const double geoidOffset =
253
- reactnativecesium::mslToEllipsoidMeters(smooth.latitude, smooth.longitude, 0.0);
307
+ reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, 0.0);
254
308
  const double camEllipsoid =
255
- reactnativecesium::mslToEllipsoidMeters(smooth.latitude, smooth.longitude, smooth.altitude);
309
+ reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, actual.altitude);
256
310
  const double minEllipsoid =
257
311
  static_cast<double>(engine_->terrainFloorEllipsoidMeters()) + minAbove;
258
312
  if (camEllipsoid < minEllipsoid) {
259
- smooth.altitude = minEllipsoid - geoidOffset;
313
+ const double clampedMsl = minEllipsoid - geoidOffset;
314
+ integrator_->clampActualAltitude(clampedMsl);
315
+ actual.altitude = clampedMsl;
260
316
  }
261
317
  }
262
318
 
263
- engine_->camera().setParams(smooth);
264
-
265
- // If we converged, clear the dirty flag so the next idle vsync can early-out.
266
- if (!reactnativecesium::CameraTargetState::deltaExceedsEpsilon(smooth, tgt)) {
267
- target_.clearDirty();
268
- }
319
+ engine_->camera().setParams(actual);
269
320
 
270
321
  engine_->updateFrame(viewportWidth_, viewportHeight_, *frameResult_);
322
+ // Metrics-only dt; integrator already advanced by wall clock above.
323
+ double dt = (lastTickSeconds_ > 0.0) ? (nowSeconds - lastTickSeconds_) : (1.0 / 60.0);
324
+ lastTickSeconds_ = nowSeconds;
325
+ if (dt > 0.5 || dt <= 0.0) dt = 1.0 / 60.0;
271
326
  metrics_.tick(dt, *frameResult_, !config_.tlsCaBundlePath.empty());
272
327
 
273
328
  reactnativecesium::FrameParams params;
@@ -276,38 +331,65 @@ void CesiumBridgeAndroid::renderFrame(double dt) {
276
331
  vulkanBackend_->endFrame();
277
332
  }
278
333
 
279
- double CesiumBridgeAndroid::readCameraLatitude() {
280
- return engine_ ? engine_->camera().getParams().latitude : 0.0;
334
+ // ── Actual camera readback ─────────────────────────────────────────────────
335
+
336
+ double CesiumBridgeAndroid::readActualLatitude() {
337
+ return integrator_ ? integrator_->getActual().latitude : 0.0;
338
+ }
339
+ double CesiumBridgeAndroid::readActualLongitude() {
340
+ return integrator_ ? integrator_->getActual().longitude : 0.0;
341
+ }
342
+ double CesiumBridgeAndroid::readActualAltitude() {
343
+ return integrator_ ? integrator_->getActual().altitude : 0.0;
344
+ }
345
+ double CesiumBridgeAndroid::readActualHeading() {
346
+ return integrator_ ? integrator_->getActual().heading : 0.0;
347
+ }
348
+ double CesiumBridgeAndroid::readActualPitch() {
349
+ return integrator_ ? integrator_->getActual().pitch : 0.0;
350
+ }
351
+ double CesiumBridgeAndroid::readActualRoll() {
352
+ return integrator_ ? integrator_->getActual().roll : 0.0;
353
+ }
354
+ double CesiumBridgeAndroid::readActualVerticalFovDeg() {
355
+ return integrator_ ? integrator_->getActual().verticalFov : 60.0;
356
+ }
357
+
358
+ // ── Demand camera readback ─────────────────────────────────────────────────
359
+
360
+ double CesiumBridgeAndroid::readDemandLatitude() {
361
+ return integrator_ ? integrator_->getDemand().latitude : 0.0;
281
362
  }
282
- double CesiumBridgeAndroid::readCameraLongitude() {
283
- return engine_ ? engine_->camera().getParams().longitude : 0.0;
363
+ double CesiumBridgeAndroid::readDemandLongitude() {
364
+ return integrator_ ? integrator_->getDemand().longitude : 0.0;
284
365
  }
285
- double CesiumBridgeAndroid::readCameraAltitude() {
286
- return engine_ ? engine_->camera().getParams().altitude : 0.0;
366
+ double CesiumBridgeAndroid::readDemandAltitude() {
367
+ return integrator_ ? integrator_->getDemand().altitude : 0.0;
287
368
  }
288
- double CesiumBridgeAndroid::readCameraHeading() {
289
- return engine_ ? engine_->camera().getParams().heading : 0.0;
369
+ double CesiumBridgeAndroid::readDemandHeading() {
370
+ return integrator_ ? integrator_->getDemand().heading : 0.0;
290
371
  }
291
- double CesiumBridgeAndroid::readCameraPitch() {
292
- return engine_ ? engine_->camera().getParams().pitch : 0.0;
372
+ double CesiumBridgeAndroid::readDemandPitch() {
373
+ return integrator_ ? integrator_->getDemand().pitch : 0.0;
293
374
  }
294
- double CesiumBridgeAndroid::readCameraRoll() {
295
- return engine_ ? engine_->camera().getParams().roll : 0.0;
375
+ double CesiumBridgeAndroid::readDemandRoll() {
376
+ return integrator_ ? integrator_->getDemand().roll : 0.0;
296
377
  }
297
- double CesiumBridgeAndroid::readVerticalFovDeg() {
298
- return engine_ ? engine_->camera().getVerticalFovDegrees() : 60.0;
378
+ double CesiumBridgeAndroid::readDemandVerticalFovDeg() {
379
+ return integrator_ ? integrator_->getDemand().verticalFov : 60.0;
299
380
  }
381
+
300
382
  double CesiumBridgeAndroid::readViewCorrectionW() {
301
- return engine_ ? engine_->camera().getParams().viewCorrection.w : 1.0;
383
+ return integrator_ ? integrator_->getActual().viewCorrection.w : 1.0;
302
384
  }
303
385
  double CesiumBridgeAndroid::readViewCorrectionX() {
304
- return engine_ ? engine_->camera().getParams().viewCorrection.x : 0.0;
386
+ return integrator_ ? integrator_->getActual().viewCorrection.x : 0.0;
305
387
  }
306
388
  double CesiumBridgeAndroid::readViewCorrectionY() {
307
- return engine_ ? engine_->camera().getParams().viewCorrection.y : 0.0;
389
+ return integrator_ ? integrator_->getActual().viewCorrection.y : 0.0;
308
390
  }
309
391
  double CesiumBridgeAndroid::readViewCorrectionZ() {
310
- return engine_ ? engine_->camera().getParams().viewCorrection.z : 0.0;
392
+ return integrator_ ? integrator_->getActual().viewCorrection.z : 0.0;
311
393
  }
312
394
 
313
395
  // ── JNI native method implementations ──────────────────────────────────────────
@@ -363,19 +445,38 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeUpdateImageryAsse
363
445
  getBridge(ptr)->updateImageryAssetId(assetId);
364
446
  }
365
447
 
448
+ // ── Per-DoF camera setters ─────────────────────────────────────────────────
366
449
  JNIEXPORT void JNICALL
367
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeUpdateCamera(
368
- JNIEnv*, jobject, jlong ptr, jdouble lat, jdouble lon, jdouble alt,
369
- jdouble heading, jdouble pitch, jdouble roll) {
370
- getBridge(ptr)->updateCamera(lat, lon, alt, heading, pitch, roll);
450
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetPosition(
451
+ JNIEnv*, jobject, jlong ptr, jdouble lat, jdouble lon) {
452
+ getBridge(ptr)->setPosition(lat, lon);
453
+ }
454
+ JNIEXPORT void JNICALL
455
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetAltitude(
456
+ JNIEnv*, jobject, jlong ptr, jdouble alt) {
457
+ getBridge(ptr)->setAltitude(alt);
458
+ }
459
+ JNIEXPORT void JNICALL
460
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetHeading(
461
+ JNIEnv*, jobject, jlong ptr, jdouble headingDeg) {
462
+ getBridge(ptr)->setHeading(headingDeg);
463
+ }
464
+ JNIEXPORT void JNICALL
465
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetAttitude(
466
+ JNIEnv*, jobject, jlong ptr, jdouble pitchDeg, jdouble rollDeg) {
467
+ getBridge(ptr)->setAttitude(pitchDeg, rollDeg);
468
+ }
469
+ JNIEXPORT void JNICALL
470
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetViewCorrection(
471
+ JNIEnv*, jobject, jlong ptr, jdouble qw, jdouble qx, jdouble qy, jdouble qz) {
472
+ getBridge(ptr)->setViewCorrection(qw, qx, qy, qz);
371
473
  }
372
-
373
474
  JNIEXPORT void JNICALL
374
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeUpdateCameraQuaternion(
375
- JNIEnv*, jobject, jlong ptr, jdouble lat, jdouble lon, jdouble alt,
376
- jdouble heading, jdouble pitch, jdouble roll,
377
- jdouble qw, jdouble qx, jdouble qy, jdouble qz) {
378
- getBridge(ptr)->updateCameraQuaternion(lat, lon, alt, heading, pitch, roll, qw, qx, qy, qz);
475
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeTeleport(
476
+ JNIEnv*, jobject, jlong ptr,
477
+ jdouble lat, jdouble lon, jdouble alt,
478
+ jdouble heading, jdouble pitch, jdouble roll, jdouble vfov) {
479
+ getBridge(ptr)->teleport(lat, lon, alt, heading, pitch, roll, vfov);
379
480
  }
380
481
 
381
482
  JNIEXPORT void JNICALL
@@ -384,6 +485,15 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetVerticalFovDeg
384
485
  getBridge(ptr)->setVerticalFovDeg(deg);
385
486
  }
386
487
 
488
+ JNIEXPORT void JNICALL
489
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetRateCaps(
490
+ JNIEnv*, jobject, jlong ptr,
491
+ jdouble yawDegSec, jdouble pitchDegSec, jdouble rollDegSec,
492
+ jdouble climbMps, jdouble groundMps) {
493
+ getBridge(ptr)->setRateCaps(yawDegSec, pitchDegSec, rollDegSec,
494
+ climbMps, groundMps);
495
+ }
496
+
387
497
  JNIEXPORT void JNICALL
388
498
  Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetMaxSSE(JNIEnv*, jobject, jlong ptr, jdouble v) {
389
499
  getBridge(ptr)->setMaximumScreenSpaceError(v);
@@ -415,43 +525,67 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeShouldRenderNextF
415
525
  }
416
526
 
417
527
  JNIEXPORT void JNICALL
418
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeRenderFrame(JNIEnv*, jobject, jlong ptr, jdouble dt) {
419
- getBridge(ptr)->renderFrame(dt);
528
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeRenderFrame(JNIEnv*, jobject, jlong ptr, jdouble nowSeconds) {
529
+ getBridge(ptr)->renderFrame(nowSeconds);
420
530
  }
421
531
 
532
+ // ── Actual / demand readbacks ──────────────────────────────────────────────
422
533
  JNIEXPORT jdouble JNICALL
423
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetCameraLat(JNIEnv*, jobject, jlong ptr) {
424
- return getBridge(ptr)->readCameraLatitude();
534
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualLatitude(JNIEnv*, jobject, jlong ptr) {
535
+ return getBridge(ptr)->readActualLatitude();
425
536
  }
426
-
427
537
  JNIEXPORT jdouble JNICALL
428
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetCameraLon(JNIEnv*, jobject, jlong ptr) {
429
- return getBridge(ptr)->readCameraLongitude();
538
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualLongitude(JNIEnv*, jobject, jlong ptr) {
539
+ return getBridge(ptr)->readActualLongitude();
430
540
  }
431
-
432
541
  JNIEXPORT jdouble JNICALL
433
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetCameraAlt(JNIEnv*, jobject, jlong ptr) {
434
- return getBridge(ptr)->readCameraAltitude();
542
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualAltitude(JNIEnv*, jobject, jlong ptr) {
543
+ return getBridge(ptr)->readActualAltitude();
435
544
  }
436
-
437
545
  JNIEXPORT jdouble JNICALL
438
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetCameraHeading(JNIEnv*, jobject, jlong ptr) {
439
- return getBridge(ptr)->readCameraHeading();
546
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualHeading(JNIEnv*, jobject, jlong ptr) {
547
+ return getBridge(ptr)->readActualHeading();
440
548
  }
441
-
442
549
  JNIEXPORT jdouble JNICALL
443
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetCameraPitch(JNIEnv*, jobject, jlong ptr) {
444
- return getBridge(ptr)->readCameraPitch();
550
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualPitch(JNIEnv*, jobject, jlong ptr) {
551
+ return getBridge(ptr)->readActualPitch();
445
552
  }
446
-
447
553
  JNIEXPORT jdouble JNICALL
448
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetCameraRoll(JNIEnv*, jobject, jlong ptr) {
449
- return getBridge(ptr)->readCameraRoll();
554
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualRoll(JNIEnv*, jobject, jlong ptr) {
555
+ return getBridge(ptr)->readActualRoll();
556
+ }
557
+ JNIEXPORT jdouble JNICALL
558
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetActualVerticalFovDeg(JNIEnv*, jobject, jlong ptr) {
559
+ return getBridge(ptr)->readActualVerticalFovDeg();
450
560
  }
451
561
 
452
562
  JNIEXPORT jdouble JNICALL
453
- Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetVerticalFovDeg(JNIEnv*, jobject, jlong ptr) {
454
- return getBridge(ptr)->readVerticalFovDeg();
563
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandLatitude(JNIEnv*, jobject, jlong ptr) {
564
+ return getBridge(ptr)->readDemandLatitude();
565
+ }
566
+ JNIEXPORT jdouble JNICALL
567
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandLongitude(JNIEnv*, jobject, jlong ptr) {
568
+ return getBridge(ptr)->readDemandLongitude();
569
+ }
570
+ JNIEXPORT jdouble JNICALL
571
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandAltitude(JNIEnv*, jobject, jlong ptr) {
572
+ return getBridge(ptr)->readDemandAltitude();
573
+ }
574
+ JNIEXPORT jdouble JNICALL
575
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandHeading(JNIEnv*, jobject, jlong ptr) {
576
+ return getBridge(ptr)->readDemandHeading();
577
+ }
578
+ JNIEXPORT jdouble JNICALL
579
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandPitch(JNIEnv*, jobject, jlong ptr) {
580
+ return getBridge(ptr)->readDemandPitch();
581
+ }
582
+ JNIEXPORT jdouble JNICALL
583
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandRoll(JNIEnv*, jobject, jlong ptr) {
584
+ return getBridge(ptr)->readDemandRoll();
585
+ }
586
+ JNIEXPORT jdouble JNICALL
587
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetDemandVerticalFovDeg(JNIEnv*, jobject, jlong ptr) {
588
+ return getBridge(ptr)->readDemandVerticalFovDeg();
455
589
  }
456
590
 
457
591
  JNIEXPORT jdouble JNICALL