react-native-cesium 1.2.18 → 1.2.19

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 (42) hide show
  1. package/README.md +1 -1
  2. package/android/CMakeLists.txt +1 -0
  3. package/android/build.gradle +1 -1
  4. package/android/gradle.properties +4 -4
  5. package/android/src/main/cpp/CesiumBridgeAndroid.cpp +5 -15
  6. package/android/src/main/cpp/CesiumBridgeAndroid.h +2 -0
  7. package/cpp/engine/CameraIntegrator.cpp +82 -47
  8. package/cpp/engine/CameraIntegrator.hpp +27 -8
  9. package/cpp/engine/CesiumEngine.cpp +152 -304
  10. package/cpp/engine/CesiumEngine.hpp +38 -28
  11. package/cpp/engine/EngineTunables.hpp +6 -5
  12. package/cpp/engine/GltfToMesh.cpp +14 -5
  13. package/cpp/engine/ResourcePreparer.cpp +3 -3
  14. package/cpp/engine/ResourcePreparer.hpp +1 -1
  15. package/cpp/engine/TerrainFloor.hpp +70 -0
  16. package/cpp/engine/TerrainHeight.hpp +113 -0
  17. package/cpp/engine/test/CameraIntegratorTest.cpp +264 -0
  18. package/ios/CesiumBridge.mm +7 -27
  19. package/lib/typescript/src/specs/cesium-view.nitro.d.ts +7 -6
  20. package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
  21. package/nitrogen/generated/android/ReactNativeCesiumOnLoad.cpp +1 -1
  22. package/nitrogen/generated/android/c++/JCameraState.hpp +1 -1
  23. package/nitrogen/generated/android/c++/JCesiumMetrics.hpp +1 -1
  24. package/nitrogen/generated/android/c++/JQuaternion.hpp +1 -1
  25. package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +177 -131
  26. package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.hpp +6 -1
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CameraState.kt +1 -1
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CesiumMetrics.kt +1 -1
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Func_void_CameraState.kt +0 -2
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Func_void_CesiumMetrics.kt +0 -2
  31. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +2 -0
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Quaternion.kt +1 -1
  33. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/views/HybridCesiumViewManager.kt +26 -10
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/views/HybridCesiumViewStateUpdater.kt +2 -2
  35. package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +211 -161
  36. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +34 -335
  37. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +107 -77
  38. package/package.json +12 -12
  39. package/scripts/cesium/config.mjs +1 -1
  40. package/scripts/cesium/vcpkg-triplets/arm64-ios-simulator.cmake +3 -0
  41. package/scripts/cesium/vcpkg-triplets/arm64-ios.cmake +4 -0
  42. package/src/specs/cesium-view.nitro.ts +7 -6
package/README.md CHANGED
@@ -184,7 +184,7 @@ npm run build --prefix node_modules/react-native-cesium
184
184
 
185
185
  **Yarn/npm workspaces:** if `react-native-cesium` is a workspace package in your monorepo, use your tool’s workspace runner, e.g. `yarn workspace react-native-cesium run update` (exact syntax depends on your workspace layout).
186
186
 
187
- - `update` checks out **Cesium Native `v0.59.0`** into `vendor/cesium-native` under the package (next to its other files; typically ignored in app repos).
187
+ - `update` checks out **Cesium Native `v0.65.0`** into `vendor/cesium-native` under the package (next to its other files; typically ignored in app repos).
188
188
  - `build` runs `scripts/cesium/build.mjs` (**CMake**, **vcpkg** under `vendor/vcpkg` unless **`VCPKG_ROOT`** is set, **Ninja** on macOS) and writes **`vendor/ios/CesiumNative.xcframework`** and/or **`vendor/android`** depending on **`CESIUM_BUILD_ONLY`**.
189
189
 
190
190
  Then install pods (iOS):
@@ -151,6 +151,7 @@ target_link_libraries(
151
151
  CesiumGltf
152
152
  CesiumGltfReader
153
153
  CesiumGltfContent
154
+ CesiumImage
154
155
  CesiumGeospatial
155
156
  CesiumGeometry
156
157
  CesiumAsync
@@ -5,7 +5,7 @@ buildscript {
5
5
  }
6
6
 
7
7
  dependencies {
8
- classpath "com.android.tools.build:gradle:8.8.0"
8
+ classpath "com.android.tools.build:gradle:9.2.1"
9
9
  }
10
10
  }
11
11
 
@@ -1,5 +1,5 @@
1
- ReactNativeCesium_kotlinVersion=2.1.20
2
- ReactNativeCesium_minSdkVersion=23
3
- ReactNativeCesium_targetSdkVersion=35
4
- ReactNativeCesium_compileSdkVersion=34
1
+ ReactNativeCesium_kotlinVersion=2.2.0
2
+ ReactNativeCesium_minSdkVersion=24
3
+ ReactNativeCesium_targetSdkVersion=36
4
+ ReactNativeCesium_compileSdkVersion=37
5
5
  ReactNativeCesium_ndkVersion=27.1.12297006
@@ -1,7 +1,7 @@
1
1
  #include "CesiumBridgeAndroid.h"
2
2
 
3
3
  #include "engine/EngineTunables.hpp"
4
- #include "engine/GeoidConverter.hpp"
4
+ #include "engine/TerrainFloor.hpp"
5
5
  #include "vulkan/VulkanBackend.h"
6
6
 
7
7
  #include <android/native_window_jni.h>
@@ -191,6 +191,7 @@ void CesiumBridgeAndroid::teleport(double lat, double lon, double alt,
191
191
  p.verticalFov = vfov;
192
192
  p.viewCorrection = integrator_->getActual().viewCorrection;
193
193
  integrator_->teleport(p);
194
+ reactnativecesium::noteTerrainSlew(*integrator_, terrainLead_);
194
195
  engine_->camera().setParams(p);
195
196
  forceRender_.store(true, std::memory_order_release);
196
197
  }
@@ -309,20 +310,9 @@ void CesiumBridgeAndroid::renderFrame(double nowSeconds) {
309
310
  // The integrator owns its own clock — see CameraIntegrator::step().
310
311
  // `nowSeconds` is still used below for the metrics-only dt EMA.
311
312
  auto actual = integrator_->step();
312
-
313
- // ── Terrain floor clamp ───────────────────────────────────────────────
314
- const float minAbove = engine_->getConfig().minAltitudeAboveTerrain;
315
- if (minAbove > 0.0f && engine_->terrainFloorKnown()) {
316
- const double geoidN =
317
- reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, 0.0);
318
- const double floorMsl =
319
- static_cast<double>(engine_->terrainFloorEllipsoidMeters()) - geoidN;
320
- const double minMsl = floorMsl + static_cast<double>(minAbove);
321
- if (actual.altitude < minMsl) {
322
- integrator_->clampActualAltitude(minMsl);
323
- actual.altitude = minMsl;
324
- }
325
- }
313
+ actual = reactnativecesium::applyTerrainFloor(
314
+ *engine_, *integrator_, actual, terrainLead_,
315
+ static_cast<double>(engine_->getConfig().minAltitudeAboveTerrain));
326
316
 
327
317
  engine_->camera().setParams(actual);
328
318
 
@@ -8,6 +8,7 @@
8
8
  #include "engine/CameraIntegrator.hpp"
9
9
  #include "engine/CesiumEngine.hpp"
10
10
  #include "engine/MetricsAggregator.hpp"
11
+ #include "engine/TerrainFloor.hpp"
11
12
 
12
13
  struct ANativeWindow;
13
14
 
@@ -144,5 +145,6 @@ private:
144
145
 
145
146
  reactnativecesium::EngineConfig config_;
146
147
  reactnativecesium::MetricsAggregator metrics_;
148
+ reactnativecesium::TerrainLeadState terrainLead_{};
147
149
  double lastTickSeconds_ = 0.0;
148
150
  };
@@ -110,7 +110,7 @@ void CameraIntegrator::updateAlphaBeta(
110
110
  }
111
111
 
112
112
  void CameraIntegrator::extrapolate(
113
- AlphaBeta& s, TimePoint now, double maxRateAbs, double demand) {
113
+ AlphaBeta& s, TimePoint now, double maxRateAbs, double demand, bool wrapAngle) {
114
114
  double dt = std::chrono::duration<double>(now - s.tState).count();
115
115
  if (dt <= 0.0) {
116
116
  s.tState = now;
@@ -166,8 +166,9 @@ void CameraIntegrator::extrapolate(
166
166
  // sensor streams always deliver a new update before the grace window
167
167
  // expires, so this term is never active for them.
168
168
  if (measurementsStopped && tunables::kDemandPullTauSec > 1.0e-6) {
169
- s.value += (1.0 - std::exp(-dt / tunables::kDemandPullTauSec))
170
- * (demand - s.value);
169
+ const double delta = wrapAngle ? shortAngleDeltaDeg(s.value, demand)
170
+ : (demand - s.value);
171
+ s.value += (1.0 - std::exp(-dt / tunables::kDemandPullTauSec)) * delta;
171
172
  }
172
173
  }
173
174
 
@@ -203,21 +204,11 @@ void CameraIntegrator::setAltitude(double altitudeMeters) {
203
204
  }
204
205
 
205
206
  altitudeMeters = std::min(altitudeMeters, kMaxAltMeters);
206
-
207
- // Adaptive alpha based on altitude error magnitude:
208
- // - Large changes (pinch gesture, cliffs) → fast (α ≈ 0.7)
209
- // - Small changes (gentle terrain) → smooth (α ≈ 0.3)
210
- const double error = std::abs(altitudeMeters - alt_.value);
211
- constexpr double kMinAlpha = 0.3; // Smooth for small changes
212
- constexpr double kMaxAlpha = 0.7; // Fast for large changes
213
- constexpr double kErrorScale = 20.0; // 20m error → max alpha
214
-
215
- const double adaptiveAlpha = kMinAlpha + (kMaxAlpha - kMinAlpha) *
216
- std::min(error / kErrorScale, 1.0);
217
-
207
+
218
208
  updateAlphaBeta(alt_, altitudeMeters, now,
219
- adaptiveAlpha, tunables::kBetaAlt, false);
209
+ tunables::kAlphaAlt, tunables::kBetaAlt, false);
220
210
  demand_.altitude = altitudeMeters;
211
+ enforceTerrainFloor();
221
212
  }
222
213
 
223
214
  void CameraIntegrator::setHeading(double headingDeg) {
@@ -261,13 +252,8 @@ void CameraIntegrator::setViewCorrection(const glm::dquat& q) {
261
252
  }
262
253
 
263
254
  void CameraIntegrator::setVerticalFov(double degrees) {
264
- const auto now = Clock::now();
265
255
  std::lock_guard<std::mutex> g(mutex_);
266
-
267
- const double residual = degrees - vfov_.value;
268
- vfov_.value += tunables::kAlphaVfov * residual;
269
- vfov_.velocity = 0.0;
270
- vfov_.tState = now;
256
+ demand_.verticalFov = degrees;
271
257
  }
272
258
 
273
259
  void CameraIntegrator::teleport(const CameraParams& target) {
@@ -305,6 +291,12 @@ void CameraIntegrator::teleport(const CameraParams& target) {
305
291
  if (demand_.verticalFov <= 0.0) {
306
292
  demand_.verticalFov = vfov_.value;
307
293
  }
294
+
295
+ // A slew lands on the requested altitude. The next rendered frame samples
296
+ // the terrain at the new position and establishes a new floor.
297
+ terrainFloorActive_ = false;
298
+ terrainFloorMsl_ = 0.0;
299
+ lastStep_ = now;
308
300
  }
309
301
 
310
302
  CameraParams CameraIntegrator::step() {
@@ -328,13 +320,27 @@ CameraParams CameraIntegrator::step() {
328
320
  latLonCapDegSec = glm::degrees(groundCap / kEarthMeanRadiusM);
329
321
  }
330
322
 
331
- extrapolate(lat_, now, latLonCapDegSec, demand_.latitude);
332
- extrapolate(lon_, now, latLonCapDegSec, demand_.longitude);
333
- extrapolate(alt_, now, climbCap, demand_.altitude);
334
- extrapolate(hdg_, now, yawCap, demand_.heading);
335
- extrapolate(pitch_, now, pitchCap, demand_.pitch);
336
- extrapolate(roll_, now, rollCap, demand_.roll);
337
- extrapolate(vfov_, now, 0.0, demand_.verticalFov);
323
+ extrapolate(lat_, now, latLonCapDegSec, demand_.latitude, false);
324
+ extrapolate(lon_, now, latLonCapDegSec, demand_.longitude, true);
325
+ extrapolate(alt_, now, climbCap, demand_.altitude, false);
326
+ extrapolate(hdg_, now, yawCap, demand_.heading, true);
327
+ extrapolate(pitch_, now, pitchCap, demand_.pitch, true);
328
+ extrapolate(roll_, now, rollCap, demand_.roll, true);
329
+
330
+ double frameDt = 0.0;
331
+ if (lastStep_.time_since_epoch().count() > 0) {
332
+ frameDt = std::chrono::duration<double>(now - lastStep_).count();
333
+ if (frameDt < 0.0) frameDt = 0.0;
334
+ if (frameDt > tunables::kMaxFrameDtSec) frameDt = tunables::kMaxFrameDtSec;
335
+ }
336
+ lastStep_ = now;
337
+
338
+ if (frameDt > 0.0 && tunables::kVfovTauSec > 1.0e-6) {
339
+ const double blend = 1.0 - std::exp(-frameDt / tunables::kVfovTauSec);
340
+ vfov_.value += blend * (demand_.verticalFov - vfov_.value);
341
+ vfov_.velocity = 0.0;
342
+ vfov_.tState = now;
343
+ }
338
344
 
339
345
  hdg_.value = wrap360(hdg_.value);
340
346
  lat_.value = std::clamp(lat_.value, -90.0, 90.0);
@@ -343,12 +349,19 @@ CameraParams CameraIntegrator::step() {
343
349
  // continuous value for short-arc maths and the camera transform doesn't
344
350
  // care.
345
351
 
346
- // viewCorrection: SLERP toward the latest target.
352
+ // viewCorrection: time-based SLERP toward the latest target.
347
353
  glm::dquat cur = glm::normalize(viewCorrActual_);
348
354
  glm::dquat tgt = glm::normalize(viewCorrTarget_);
349
355
  if (glm::dot(cur, tgt) < 0.0) tgt = -tgt;
350
- viewCorrActual_ = glm::normalize(
351
- glm::slerp(cur, tgt, tunables::kAlphaViewCorr));
356
+ double slerpT = 1.0;
357
+ if (tunables::kViewCorrTauSec > 1.0e-6) {
358
+ slerpT = (frameDt > 0.0)
359
+ ? (1.0 - std::exp(-frameDt / tunables::kViewCorrTauSec))
360
+ : 0.0;
361
+ }
362
+ viewCorrActual_ = glm::normalize(glm::slerp(cur, tgt, slerpT));
363
+
364
+ enforceTerrainFloor();
352
365
 
353
366
  CameraParams out;
354
367
  out.latitude = lat_.value;
@@ -362,23 +375,40 @@ CameraParams CameraIntegrator::step() {
362
375
  return out;
363
376
  }
364
377
 
365
- void CameraIntegrator::clampActualAltitude(double newAltMsl) {
366
- const auto now = Clock::now();
378
+ void CameraIntegrator::setTerrainFloor(double floorMsl) {
367
379
  std::lock_guard<std::mutex> g(mutex_);
368
-
369
- // Instead of snapping, feed the clamped altitude through the alpha-beta
370
- // tracker for smooth interpolation. This matches how heading/pitch/roll work.
371
- updateAlphaBeta(alt_, newAltMsl, now,
372
- tunables::kAlphaAlt, tunables::kBetaAlt, false);
373
-
374
- // Only clamp DOWNWARD velocity to prevent descending through terrain.
375
- // Allow upward velocity for smooth ascents over rising terrain.
376
- if (alt_.velocity < 0.0) {
380
+ if (!std::isfinite(floorMsl)) return;
381
+ terrainFloorActive_ = true;
382
+ terrainFloorMsl_ = std::min(floorMsl, kMaxAltMeters);
383
+ enforceTerrainFloor();
384
+ }
385
+
386
+ void CameraIntegrator::clearTerrainFloor() {
387
+ std::lock_guard<std::mutex> g(mutex_);
388
+ terrainFloorActive_ = false;
389
+ terrainFloorMsl_ = 0.0;
390
+ }
391
+
392
+ void CameraIntegrator::clampActualAltitude(double newAltMsl) {
393
+ setTerrainFloor(newAltMsl);
394
+ }
395
+
396
+ void CameraIntegrator::enforceTerrainFloor() {
397
+ if (!terrainFloorActive_) return;
398
+
399
+ const bool inContact =
400
+ demand_.altitude <= terrainFloorMsl_ + tunables::kEpsAltitudeMeters;
401
+ if (inContact) {
402
+ // Contour contact: the floor sample owns altitude, both directions.
403
+ // Residual vertical velocity would carry the camera into the next face.
404
+ alt_.value = terrainFloorMsl_;
377
405
  alt_.velocity = 0.0;
406
+ return;
407
+ }
408
+ if (alt_.value < terrainFloorMsl_) {
409
+ alt_.value = terrainFloorMsl_;
410
+ if (alt_.velocity < 0.0) alt_.velocity = 0.0;
378
411
  }
379
-
380
- // Do not touch demand_.altitude — getDemand() should still report what the
381
- // user asked for.
382
412
  }
383
413
 
384
414
  void CameraIntegrator::setRateCaps(const CameraRateCaps& caps) {
@@ -410,7 +440,12 @@ bool CameraIntegrator::isActive() const {
410
440
  if (std::abs(demand_.latitude - lat_.value) > tunables::kEpsLatLon) return true;
411
441
  if (std::abs(shortAngleDeltaDeg(lon_.value, demand_.longitude))
412
442
  > tunables::kEpsLatLon) return true;
413
- if (std::abs(demand_.altitude - alt_.value) > tunables::kEpsAltitudeMeters) return true;
443
+ if (terrainFloorActive_ &&
444
+ demand_.altitude <= terrainFloorMsl_ + tunables::kEpsAltitudeMeters) {
445
+ if (std::abs(alt_.value - terrainFloorMsl_) > tunables::kEpsAltitudeMeters) return true;
446
+ } else if (std::abs(demand_.altitude - alt_.value) > tunables::kEpsAltitudeMeters) {
447
+ return true;
448
+ }
414
449
  if (angleAbsDeltaDeg(demand_.heading, hdg_.value) > tunables::kEpsAngleDeg) return true;
415
450
  if (angleAbsDeltaDeg(demand_.pitch, pitch_.value) > tunables::kEpsAngleDeg) return true;
416
451
  if (angleAbsDeltaDeg(demand_.roll, roll_.value) > tunables::kEpsAngleDeg) return true;
@@ -73,11 +73,18 @@ public:
73
73
  // values and an instant runaway.
74
74
  CameraParams step();
75
75
 
76
- // Terrain-floor write-back hook. Replaces the *actual* altitude value with
77
- // `newAltMsl` and zeroes vertical velocity so the integrator does not
78
- // immediately try to descend back through the floor. The *demand* altitude
79
- // is left untouched — getDemand().altitude continues to reflect what the
80
- // consumer asked for.
76
+ // Terrain floor in metres MSL. While active, the rendered altitude is never
77
+ // below this value. When the demand altitude is at or below the floor the
78
+ // camera rides it exactly (uphill and downhill), so a later setAltitude
79
+ // under the surface cannot pull the rendered camera back inside. Demand
80
+ // itself is left untouched.
81
+ void setTerrainFloor(double floorMsl);
82
+
83
+ // Drops the floor so the next slew or the next frame can establish a new
84
+ // one. Used by teleport().
85
+ void clearTerrainFloor();
86
+
87
+ // Sets the terrain floor to `newAltMsl`. Same as setTerrainFloor.
81
88
  void clampActualAltitude(double newAltMsl);
82
89
 
83
90
  // Runtime override of the rate caps (driven by view props). 0 = uncapped
@@ -122,9 +129,14 @@ private:
122
129
  double alpha, double beta, bool wrapAngle);
123
130
 
124
131
  // Advance the running estimate to `now`, clamp velocity to `maxRateAbs`
125
- // (per second; 0 = uncapped). For angles, the resulting value is also
126
- // normalised to its canonical range by the caller as needed.
127
- static void extrapolate(AlphaBeta& s, TimePoint now, double maxRateAbs, double demand);
132
+ // (per second; 0 = uncapped). `wrapAngle` makes the silence demand-pull
133
+ // take the short arc (longitude, heading, pitch, roll).
134
+ static void extrapolate(AlphaBeta& s, TimePoint now, double maxRateAbs,
135
+ double demand, bool wrapAngle);
136
+
137
+ // Snap the rendered altitude onto the floor when the camera is in contact
138
+ // or currently below it. Demand is not modified.
139
+ void enforceTerrainFloor();
128
140
 
129
141
  mutable std::mutex mutex_;
130
142
 
@@ -141,6 +153,13 @@ private:
141
153
 
142
154
  // Runtime overrides; 0 = use compile-time defaults from EngineTunables.
143
155
  CameraRateCaps caps_{};
156
+
157
+ // Last step() timestamp. Drives the dt-based fov blend and view-correction
158
+ // slerp. Epoch means "no step yet".
159
+ TimePoint lastStep_{};
160
+
161
+ bool terrainFloorActive_ = false;
162
+ double terrainFloorMsl_ = 0.0;
144
163
  };
145
164
 
146
165
  } // namespace reactnativecesium