react-native-cesium 1.2.12 → 1.2.14
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/cpp/engine/CameraIntegrator.cpp +38 -5
- package/cpp/engine/CesiumEngine.cpp +181 -101
- package/ios/CesiumBridge.mm +16 -6
- package/package.json +3 -3
- package/scripts/cesium/config.mjs +1 -1
|
@@ -147,7 +147,17 @@ void CameraIntegrator::extrapolate(
|
|
|
147
147
|
if (maxRateAbs > 0.0) {
|
|
148
148
|
s.velocity = clampAbs(s.velocity, maxRateAbs);
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
|
|
151
|
+
// While measurements are still arriving (gesture / sensor stream), advance
|
|
152
|
+
// along the learned velocity for smooth inter-frame motion. Once they stop
|
|
153
|
+
// (fling decay finished, finger lifted), do not coast on that velocity —
|
|
154
|
+
// only demand-pull toward the last set value. Coasting past demand is what
|
|
155
|
+
// users perceive as the camera "springing back" after a fling.
|
|
156
|
+
const bool measurementsStopped =
|
|
157
|
+
silence > grace && s.tLastMeas.time_since_epoch().count() > 0;
|
|
158
|
+
if (!measurementsStopped) {
|
|
159
|
+
s.value += s.velocity * dt;
|
|
160
|
+
}
|
|
151
161
|
s.tState = now;
|
|
152
162
|
|
|
153
163
|
// Demand pull — guarantees convergence to the last demanded value for
|
|
@@ -155,7 +165,7 @@ void CameraIntegrator::extrapolate(
|
|
|
155
165
|
// when measurements have stopped (silence > grace); continuous gesture or
|
|
156
166
|
// sensor streams always deliver a new update before the grace window
|
|
157
167
|
// expires, so this term is never active for them.
|
|
158
|
-
if (
|
|
168
|
+
if (measurementsStopped && tunables::kDemandPullTauSec > 1.0e-6) {
|
|
159
169
|
s.value += (1.0 - std::exp(-dt / tunables::kDemandPullTauSec))
|
|
160
170
|
* (demand - s.value);
|
|
161
171
|
}
|
|
@@ -193,8 +203,20 @@ void CameraIntegrator::setAltitude(double altitudeMeters) {
|
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
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
|
+
|
|
196
218
|
updateAlphaBeta(alt_, altitudeMeters, now,
|
|
197
|
-
|
|
219
|
+
adaptiveAlpha, tunables::kBetaAlt, false);
|
|
198
220
|
demand_.altitude = altitudeMeters;
|
|
199
221
|
}
|
|
200
222
|
|
|
@@ -341,9 +363,20 @@ CameraParams CameraIntegrator::step() {
|
|
|
341
363
|
}
|
|
342
364
|
|
|
343
365
|
void CameraIntegrator::clampActualAltitude(double newAltMsl) {
|
|
366
|
+
const auto now = Clock::now();
|
|
344
367
|
std::lock_guard<std::mutex> g(mutex_);
|
|
345
|
-
|
|
346
|
-
|
|
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) {
|
|
377
|
+
alt_.velocity = 0.0;
|
|
378
|
+
}
|
|
379
|
+
|
|
347
380
|
// Do not touch demand_.altitude — getDemand() should still report what the
|
|
348
381
|
// user asked for.
|
|
349
382
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include "CesiumEngine.hpp"
|
|
7
7
|
#include "GeoidConverter.hpp"
|
|
8
8
|
|
|
9
|
+
#include <iostream>
|
|
9
10
|
|
|
10
11
|
#include <Cesium3DTilesContent/registerAllTileContentTypes.h>
|
|
11
12
|
#include <Cesium3DTilesSelection/BoundingVolume.h>
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
#include <algorithm>
|
|
37
38
|
#include <cmath>
|
|
38
39
|
#include <cstring>
|
|
40
|
+
#include <limits>
|
|
39
41
|
#include <thread>
|
|
40
42
|
|
|
41
43
|
|
|
@@ -609,25 +611,21 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
609
611
|
const CameraParams camParams = trackTerrainFloor ? camera_.getParams() : CameraParams{};
|
|
610
612
|
const double camLatRad = glm::radians(camParams.latitude);
|
|
611
613
|
const double camLonRad = glm::radians(camParams.longitude);
|
|
612
|
-
|
|
614
|
+
|
|
613
615
|
// Expected ground ellipsoid height at camera location (from geoid).
|
|
614
616
|
// Used to filter out elevated structures (buildings) during floor sampling.
|
|
615
617
|
const double expectedGroundEllipsoid = trackTerrainFloor
|
|
616
618
|
? mslToEllipsoidMeters(camParams.latitude, camParams.longitude, 0.0)
|
|
617
619
|
: 0.0;
|
|
618
|
-
|
|
620
|
+
|
|
619
621
|
float bestTerrainFloor = terrainFloorEllipsoidM_; // carry previous value if no tile matches
|
|
620
622
|
double bestHorizDistSq = std::numeric_limits<double>::max();
|
|
621
623
|
bool floorScanFound = false;
|
|
622
624
|
|
|
623
625
|
// Horizontal search radius for floor sampling.
|
|
624
|
-
|
|
626
|
+
// Increased to 200m to ensure robust sampling across terrain LODs and camera speeds.
|
|
627
|
+
constexpr double kFloorSearchRadiusM = 200.0;
|
|
625
628
|
constexpr double kFloorSearchRadiusSq = kFloorSearchRadiusM * kFloorSearchRadiusM;
|
|
626
|
-
// Only accept vertices within this tolerance of expected ground (filters out buildings).
|
|
627
|
-
constexpr double kGroundToleranceM = 10.0;
|
|
628
|
-
// 3D fallback: if no ground-level vertices found, use closest 3D vertex.
|
|
629
|
-
double bestDist3D = std::numeric_limits<double>::max();
|
|
630
|
-
float bestDist3DFloor = 0.0f;
|
|
631
629
|
|
|
632
630
|
// ── floor-scan cache lookup ─────────────────────────────────────────
|
|
633
631
|
// Skip the per-vertex scan when the camera has moved less than ~10 cm in
|
|
@@ -653,6 +651,10 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
653
651
|
// ENU basis at the camera — horizontal distance to each terrain vertex.
|
|
654
652
|
glm::dvec3 floorEast(1.0, 0.0, 0.0);
|
|
655
653
|
glm::dvec3 floorNorth(0.0, 1.0, 0.0);
|
|
654
|
+
glm::dvec3 floorScanPos = cameraPos; // Always scan directly below camera
|
|
655
|
+
glm::dvec3 floorScanAheadPos = cameraPos; // Also scan ahead if moving
|
|
656
|
+
bool useLookAhead = false;
|
|
657
|
+
|
|
656
658
|
if (runFloorScan) {
|
|
657
659
|
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
658
660
|
const glm::dmat4 enuToEcef =
|
|
@@ -660,6 +662,58 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
660
662
|
cameraPos, ellipsoid);
|
|
661
663
|
floorEast = glm::normalize(glm::dvec3(enuToEcef[0]));
|
|
662
664
|
floorNorth = glm::normalize(glm::dvec3(enuToEcef[1]));
|
|
665
|
+
|
|
666
|
+
// ── Look-ahead terrain sampling ──────────────────────────────────────
|
|
667
|
+
// Sample terrain BOTH below camera AND ahead. Use the maximum height to
|
|
668
|
+
// prevent clipping through terrain between current position and look-ahead.
|
|
669
|
+
if (floorCacheValid_) {
|
|
670
|
+
// Convert previous lat/lon to ECEF to get movement vector
|
|
671
|
+
const double prevLonRad = floorCacheCamLonDeg_ * M_PI / 180.0;
|
|
672
|
+
const double prevLatRad = floorCacheCamLatDeg_ * M_PI / 180.0;
|
|
673
|
+
const glm::dvec3 prevCamPos = ellipsoid.cartographicToCartesian(
|
|
674
|
+
CesiumGeospatial::Cartographic(prevLonRad, prevLatRad, camParams.altitude));
|
|
675
|
+
|
|
676
|
+
// Calculate horizontal movement since last frame
|
|
677
|
+
const glm::dvec3 movement3D = cameraPos - prevCamPos;
|
|
678
|
+
const double moveEast = glm::dot(movement3D, floorEast);
|
|
679
|
+
const double moveNorth = glm::dot(movement3D, floorNorth);
|
|
680
|
+
const double horizMovement = std::sqrt(moveEast * moveEast + moveNorth * moveNorth);
|
|
681
|
+
|
|
682
|
+
// Look ahead by 5x recent movement (minimum 10m, max 100m)
|
|
683
|
+
constexpr double kMinLookAhead = 10.0; // meters
|
|
684
|
+
constexpr double kMaxLookAhead = 100.0; // meters
|
|
685
|
+
constexpr double kLookAheadMultiplier = 5.0;
|
|
686
|
+
|
|
687
|
+
double lookAheadDist = std::clamp(
|
|
688
|
+
horizMovement * kLookAheadMultiplier,
|
|
689
|
+
kMinLookAhead,
|
|
690
|
+
kMaxLookAhead);
|
|
691
|
+
|
|
692
|
+
if (horizMovement > 0.1) { // Only look ahead if actually moving
|
|
693
|
+
// Project sampling point forward in direction of movement
|
|
694
|
+
const glm::dvec3 moveDir = glm::normalize(
|
|
695
|
+
floorEast * moveEast + floorNorth * moveNorth);
|
|
696
|
+
floorScanAheadPos = cameraPos + moveDir * lookAheadDist;
|
|
697
|
+
useLookAhead = true;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Convert floor scan positions to lat/lon for tile bounds checking
|
|
703
|
+
// Camera position (always used)
|
|
704
|
+
double floorScanLonRad = camLonRad;
|
|
705
|
+
double floorScanLatRad = camLatRad;
|
|
706
|
+
|
|
707
|
+
// Look-ahead position (used when moving)
|
|
708
|
+
double floorScanAheadLonRad = camLonRad;
|
|
709
|
+
double floorScanAheadLatRad = camLatRad;
|
|
710
|
+
if (runFloorScan && useLookAhead) {
|
|
711
|
+
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
712
|
+
const auto scanCartographic = ellipsoid.cartesianToCartographic(floorScanAheadPos);
|
|
713
|
+
if (scanCartographic) {
|
|
714
|
+
floorScanAheadLonRad = scanCartographic->longitude;
|
|
715
|
+
floorScanAheadLatRad = scanCartographic->latitude;
|
|
716
|
+
}
|
|
663
717
|
}
|
|
664
718
|
|
|
665
719
|
// ── fast path ────────────────────────────────────────────────────────
|
|
@@ -678,65 +732,82 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
678
732
|
|
|
679
733
|
// Floor scan (run once per res so multi-primitive tiles aren't scanned
|
|
680
734
|
// twice). Skipped when the P5 cache hit above produced a usable value.
|
|
735
|
+
// Sample terrain at BOTH camera position and look-ahead position.
|
|
736
|
+
// Find lowest terrain at each position, then use the MAXIMUM of the two.
|
|
681
737
|
if (runFloorScan && c.res != lastScanRes) {
|
|
682
738
|
lastScanRes = c.res;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
(c.wmTileBounds.
|
|
687
|
-
static_cast<float>(
|
|
688
|
-
|
|
739
|
+
|
|
740
|
+
// Check if camera position is in tile bounds
|
|
741
|
+
const bool camInside =
|
|
742
|
+
(c.wmTileBounds.x <= static_cast<float>(floorScanLonRad) &&
|
|
743
|
+
static_cast<float>(floorScanLonRad) <= c.wmTileBounds.z) &&
|
|
744
|
+
(c.wmTileBounds.y <= static_cast<float>(floorScanLatRad) &&
|
|
745
|
+
static_cast<float>(floorScanLatRad) <= c.wmTileBounds.w);
|
|
746
|
+
|
|
747
|
+
// Check if look-ahead position is in tile bounds (when moving)
|
|
748
|
+
const bool aheadInside = useLookAhead &&
|
|
749
|
+
(c.wmTileBounds.x <= static_cast<float>(floorScanAheadLonRad) &&
|
|
750
|
+
static_cast<float>(floorScanAheadLonRad) <= c.wmTileBounds.z) &&
|
|
751
|
+
(c.wmTileBounds.y <= static_cast<float>(floorScanAheadLatRad) &&
|
|
752
|
+
static_cast<float>(floorScanAheadLatRad) <= c.wmTileBounds.w);
|
|
753
|
+
|
|
754
|
+
if (camInside || aheadInside) {
|
|
755
|
+
// Track lowest terrain at each position separately
|
|
756
|
+
float terrainAtCam = std::numeric_limits<float>::max();
|
|
757
|
+
float terrainAhead = std::numeric_limits<float>::max();
|
|
758
|
+
bool foundAtCam = false;
|
|
759
|
+
bool foundAhead = false;
|
|
760
|
+
|
|
689
761
|
for (const auto& prim : c.res->primitives) {
|
|
690
762
|
if (prim.altitudes.empty() || prim.localPositions.empty()) continue;
|
|
691
763
|
const size_t vc = prim.localPositions.size();
|
|
764
|
+
|
|
692
765
|
for (size_t vi = 0; vi < vc; ++vi) {
|
|
693
|
-
const glm::dvec3 vECEF =
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
if (!floorScanFound || vAlt < bestTerrainFloor) {
|
|
708
|
-
bestTerrainFloor = vAlt;
|
|
709
|
-
bestHorizDistSq = horizSq;
|
|
710
|
-
floorScanFound = true;
|
|
766
|
+
const glm::dvec3 vECEF = glm::dvec3(prim.localPositions[vi]) + prim.rtcCenter;
|
|
767
|
+
const float vAlt = prim.altitudes[vi];
|
|
768
|
+
|
|
769
|
+
// Sample terrain at camera position - find LOWEST
|
|
770
|
+
if (camInside) {
|
|
771
|
+
const glm::dvec3 deltaCam = vECEF - floorScanPos;
|
|
772
|
+
const double deCam = glm::dot(deltaCam, floorEast);
|
|
773
|
+
const double dnCam = glm::dot(deltaCam, floorNorth);
|
|
774
|
+
const double horizSqCam = deCam * deCam + dnCam * dnCam;
|
|
775
|
+
|
|
776
|
+
if (horizSqCam <= kFloorSearchRadiusSq) {
|
|
777
|
+
if (!foundAtCam || vAlt < terrainAtCam) {
|
|
778
|
+
terrainAtCam = vAlt;
|
|
779
|
+
foundAtCam = true;
|
|
711
780
|
}
|
|
712
781
|
}
|
|
713
782
|
}
|
|
714
|
-
|
|
715
|
-
//
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
783
|
+
|
|
784
|
+
// Sample terrain at look-ahead position - find LOWEST
|
|
785
|
+
if (aheadInside) {
|
|
786
|
+
const glm::dvec3 deltaAhead = vECEF - floorScanAheadPos;
|
|
787
|
+
const double deAhead = glm::dot(deltaAhead, floorEast);
|
|
788
|
+
const double dnAhead = glm::dot(deltaAhead, floorNorth);
|
|
789
|
+
const double horizSqAhead = deAhead * deAhead + dnAhead * dnAhead;
|
|
790
|
+
|
|
791
|
+
if (horizSqAhead <= kFloorSearchRadiusSq) {
|
|
792
|
+
if (!foundAhead || vAlt < terrainAhead) {
|
|
793
|
+
terrainAhead = vAlt;
|
|
794
|
+
foundAhead = true;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
720
797
|
}
|
|
721
798
|
}
|
|
722
799
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
// Ultimate fallback: use geoid-based ground estimate
|
|
737
|
-
if (!floorScanFound) {
|
|
738
|
-
bestTerrainFloor = static_cast<float>(expectedGroundEllipsoid);
|
|
739
|
-
floorScanFound = true;
|
|
800
|
+
|
|
801
|
+
// Use the MAXIMUM of the two terrain samples (prevents clipping through either)
|
|
802
|
+
if (foundAtCam && foundAhead) {
|
|
803
|
+
bestTerrainFloor = std::max(terrainAtCam, terrainAhead);
|
|
804
|
+
floorScanFound = true;
|
|
805
|
+
} else if (foundAtCam) {
|
|
806
|
+
bestTerrainFloor = terrainAtCam;
|
|
807
|
+
floorScanFound = true;
|
|
808
|
+
} else if (foundAhead) {
|
|
809
|
+
bestTerrainFloor = terrainAhead;
|
|
810
|
+
floorScanFound = true;
|
|
740
811
|
}
|
|
741
812
|
}
|
|
742
813
|
}
|
|
@@ -824,62 +895,71 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
824
895
|
static_cast<float>(rect.getEast()),
|
|
825
896
|
static_cast<float>(rect.getNorth()));
|
|
826
897
|
|
|
827
|
-
// ── Terrain floor:
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
|
|
831
|
-
rect.contains(CesiumGeospatial::Cartographic(
|
|
898
|
+
// ── Terrain floor: sample at both camera and look-ahead positions ────
|
|
899
|
+
// Find lowest terrain at each position, then use the MAXIMUM of the two.
|
|
900
|
+
// Suppressed when the cache returned a usable value.
|
|
901
|
+
const bool camInside = runFloorScan &&
|
|
902
|
+
rect.contains(CesiumGeospatial::Cartographic(floorScanLonRad, floorScanLatRad, 0.0));
|
|
903
|
+
const bool aheadInside = runFloorScan && useLookAhead &&
|
|
904
|
+
rect.contains(CesiumGeospatial::Cartographic(floorScanAheadLonRad, floorScanAheadLatRad, 0.0));
|
|
905
|
+
|
|
906
|
+
if (camInside || aheadInside) {
|
|
907
|
+
// Track lowest terrain at each position separately
|
|
908
|
+
float terrainAtCam = std::numeric_limits<float>::max();
|
|
909
|
+
float terrainAhead = std::numeric_limits<float>::max();
|
|
910
|
+
bool foundAtCam = false;
|
|
911
|
+
bool foundAhead = false;
|
|
912
|
+
|
|
832
913
|
for (const auto& prim : res->primitives) {
|
|
833
914
|
if (prim.altitudes.empty() || prim.localPositions.empty()) continue;
|
|
834
915
|
const size_t vertexCount = prim.localPositions.size();
|
|
916
|
+
|
|
835
917
|
for (size_t vi = 0; vi < vertexCount; ++vi) {
|
|
836
|
-
const glm::dvec3 vECEF =
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
if (!floorScanFound || vAlt < bestTerrainFloor) {
|
|
851
|
-
bestTerrainFloor = vAlt;
|
|
852
|
-
bestHorizDistSq = horizSq;
|
|
853
|
-
floorScanFound = true;
|
|
918
|
+
const glm::dvec3 vECEF = glm::dvec3(prim.localPositions[vi]) + prim.rtcCenter;
|
|
919
|
+
const float vAlt = prim.altitudes[vi];
|
|
920
|
+
|
|
921
|
+
// Sample terrain at camera position - find LOWEST
|
|
922
|
+
if (camInside) {
|
|
923
|
+
const glm::dvec3 deltaCam = vECEF - floorScanPos;
|
|
924
|
+
const double deCam = glm::dot(deltaCam, floorEast);
|
|
925
|
+
const double dnCam = glm::dot(deltaCam, floorNorth);
|
|
926
|
+
const double horizSqCam = deCam * deCam + dnCam * dnCam;
|
|
927
|
+
|
|
928
|
+
if (horizSqCam <= kFloorSearchRadiusSq) {
|
|
929
|
+
if (!foundAtCam || vAlt < terrainAtCam) {
|
|
930
|
+
terrainAtCam = vAlt;
|
|
931
|
+
foundAtCam = true;
|
|
854
932
|
}
|
|
855
933
|
}
|
|
856
934
|
}
|
|
857
|
-
|
|
858
|
-
//
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
935
|
+
|
|
936
|
+
// Sample terrain at look-ahead position - find LOWEST
|
|
937
|
+
if (aheadInside) {
|
|
938
|
+
const glm::dvec3 deltaAhead = vECEF - floorScanAheadPos;
|
|
939
|
+
const double deAhead = glm::dot(deltaAhead, floorEast);
|
|
940
|
+
const double dnAhead = glm::dot(deltaAhead, floorNorth);
|
|
941
|
+
const double horizSqAhead = deAhead * deAhead + dnAhead * dnAhead;
|
|
942
|
+
|
|
943
|
+
if (horizSqAhead <= kFloorSearchRadiusSq) {
|
|
944
|
+
if (!foundAhead || vAlt < terrainAhead) {
|
|
945
|
+
terrainAhead = vAlt;
|
|
946
|
+
foundAhead = true;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
863
949
|
}
|
|
864
950
|
}
|
|
865
951
|
}
|
|
866
|
-
|
|
867
|
-
//
|
|
868
|
-
if (
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
// Ultimate fallback: use geoid-based ground estimate
|
|
879
|
-
if (!floorScanFound) {
|
|
880
|
-
bestTerrainFloor = static_cast<float>(expectedGroundEllipsoid);
|
|
881
|
-
floorScanFound = true;
|
|
882
|
-
}
|
|
952
|
+
|
|
953
|
+
// Use the MAXIMUM of the two terrain samples (prevents clipping through either)
|
|
954
|
+
if (foundAtCam && foundAhead) {
|
|
955
|
+
bestTerrainFloor = std::max(terrainAtCam, terrainAhead);
|
|
956
|
+
floorScanFound = true;
|
|
957
|
+
} else if (foundAtCam) {
|
|
958
|
+
bestTerrainFloor = terrainAtCam;
|
|
959
|
+
floorScanFound = true;
|
|
960
|
+
} else if (foundAhead) {
|
|
961
|
+
bestTerrainFloor = terrainAhead;
|
|
962
|
+
floorScanFound = true;
|
|
883
963
|
}
|
|
884
964
|
}
|
|
885
965
|
}
|
package/ios/CesiumBridge.mm
CHANGED
|
@@ -383,19 +383,29 @@
|
|
|
383
383
|
// `nowSeconds` is still used below for the FPS-EMA dt only.
|
|
384
384
|
auto actual = _integrator->step();
|
|
385
385
|
|
|
386
|
-
// ── Terrain floor
|
|
387
|
-
//
|
|
388
|
-
//
|
|
386
|
+
// ── Terrain floor following using integrator's alpha-beta smoothing ───
|
|
387
|
+
// Set demand altitude to terrain floor, let the integrator's alpha-beta
|
|
388
|
+
// tracker smoothly interpolate. The tracker already provides adaptive
|
|
389
|
+
// exponential easing (alpha=0.6, beta=0.05 in EngineTunables.hpp).
|
|
389
390
|
const float minAbove = _engine->getConfig().minAltitudeAboveTerrain;
|
|
390
391
|
if (minAbove > 0.0f && _engine->terrainFloorKnown()) {
|
|
391
392
|
const double geoidN =
|
|
392
393
|
reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, 0.0);
|
|
393
|
-
const double
|
|
394
|
-
|
|
394
|
+
const double floorEllipsoid = static_cast<double>(_engine->terrainFloorEllipsoidMeters());
|
|
395
|
+
const double floorMsl = floorEllipsoid - geoidN;
|
|
395
396
|
const double minMsl = floorMsl + static_cast<double>(minAbove);
|
|
397
|
+
|
|
398
|
+
// Continuously update demand to follow terrain
|
|
399
|
+
const double demandAlt = _integrator->getDemand().altitude;
|
|
400
|
+
if (demandAlt <= 0.0) {
|
|
401
|
+
_integrator->setAltitude(minMsl);
|
|
402
|
+
actual = _integrator->getActual();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Safety floor: hard clamp if already below terrain (shouldn't happen with look-ahead)
|
|
396
406
|
if (actual.altitude < minMsl) {
|
|
397
407
|
_integrator->clampActualAltitude(minMsl);
|
|
398
|
-
actual
|
|
408
|
+
actual = _integrator->getActual();
|
|
399
409
|
}
|
|
400
410
|
}
|
|
401
411
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-cesium",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.14",
|
|
4
4
|
"description": "Experimental Cesium rendering for React Native via Nitro",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/module/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"node": ">=18"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"react-native-nitro-modules": "^0.35.
|
|
67
|
+
"react-native-nitro-modules": "^0.35.9"
|
|
68
68
|
},
|
|
69
69
|
"publishConfig": {
|
|
70
70
|
"access": "public",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"@types/jest": "^29.5.14",
|
|
78
78
|
"@types/react": "19.2.14",
|
|
79
79
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
80
|
-
"nitrogen": "0.35.
|
|
80
|
+
"nitrogen": "0.35.9",
|
|
81
81
|
"react": "19.2.3",
|
|
82
82
|
"react-native": "0.85.3",
|
|
83
83
|
"react-native-builder-bob": "^0.41.0",
|