react-native-cesium 1.2.11 → 1.2.13
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 +13 -2
- package/cpp/engine/CesiumEngine.cpp +191 -66
- package/ios/CesiumBridge.mm +6 -5
- package/package.json +1 -1
|
@@ -341,9 +341,20 @@ CameraParams CameraIntegrator::step() {
|
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
void CameraIntegrator::clampActualAltitude(double newAltMsl) {
|
|
344
|
+
const auto now = Clock::now();
|
|
344
345
|
std::lock_guard<std::mutex> g(mutex_);
|
|
345
|
-
|
|
346
|
-
|
|
346
|
+
|
|
347
|
+
// Instead of snapping, feed the clamped altitude through the alpha-beta
|
|
348
|
+
// tracker for smooth interpolation. This matches how heading/pitch/roll work.
|
|
349
|
+
updateAlphaBeta(alt_, newAltMsl, now,
|
|
350
|
+
tunables::kAlphaAlt, tunables::kBetaAlt, false);
|
|
351
|
+
|
|
352
|
+
// Only clamp DOWNWARD velocity to prevent descending through terrain.
|
|
353
|
+
// Allow upward velocity for smooth ascents over rising terrain.
|
|
354
|
+
if (alt_.velocity < 0.0) {
|
|
355
|
+
alt_.velocity = 0.0;
|
|
356
|
+
}
|
|
357
|
+
|
|
347
358
|
// Do not touch demand_.altitude — getDemand() should still report what the
|
|
348
359
|
// user asked for.
|
|
349
360
|
}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
#endif
|
|
5
5
|
|
|
6
6
|
#include "CesiumEngine.hpp"
|
|
7
|
+
#include "GeoidConverter.hpp"
|
|
7
8
|
|
|
9
|
+
#include <iostream>
|
|
8
10
|
|
|
9
11
|
#include <Cesium3DTilesContent/registerAllTileContentTypes.h>
|
|
10
12
|
#include <Cesium3DTilesSelection/BoundingVolume.h>
|
|
@@ -35,6 +37,7 @@
|
|
|
35
37
|
#include <algorithm>
|
|
36
38
|
#include <cmath>
|
|
37
39
|
#include <cstring>
|
|
40
|
+
#include <limits>
|
|
38
41
|
#include <thread>
|
|
39
42
|
|
|
40
43
|
|
|
@@ -608,17 +611,21 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
608
611
|
const CameraParams camParams = trackTerrainFloor ? camera_.getParams() : CameraParams{};
|
|
609
612
|
const double camLatRad = glm::radians(camParams.latitude);
|
|
610
613
|
const double camLonRad = glm::radians(camParams.longitude);
|
|
614
|
+
|
|
615
|
+
// Expected ground ellipsoid height at camera location (from geoid).
|
|
616
|
+
// Used to filter out elevated structures (buildings) during floor sampling.
|
|
617
|
+
const double expectedGroundEllipsoid = trackTerrainFloor
|
|
618
|
+
? mslToEllipsoidMeters(camParams.latitude, camParams.longitude, 0.0)
|
|
619
|
+
: 0.0;
|
|
620
|
+
|
|
611
621
|
float bestTerrainFloor = terrainFloorEllipsoidM_; // carry previous value if no tile matches
|
|
612
622
|
double bestHorizDistSq = std::numeric_limits<double>::max();
|
|
613
623
|
bool floorScanFound = false;
|
|
614
624
|
|
|
615
|
-
// Horizontal search radius for floor sampling
|
|
616
|
-
//
|
|
617
|
-
constexpr double kFloorSearchRadiusM =
|
|
625
|
+
// Horizontal search radius for floor sampling.
|
|
626
|
+
// Increased to 200m to ensure robust sampling across terrain LODs and camera speeds.
|
|
627
|
+
constexpr double kFloorSearchRadiusM = 200.0;
|
|
618
628
|
constexpr double kFloorSearchRadiusSq = kFloorSearchRadiusM * kFloorSearchRadiusM;
|
|
619
|
-
// 3D fallback: if no vertices within horizontal radius, use closest 3D vertex.
|
|
620
|
-
double bestDist3D = std::numeric_limits<double>::max();
|
|
621
|
-
float bestDist3DFloor = 0.0f;
|
|
622
629
|
|
|
623
630
|
// ── floor-scan cache lookup ─────────────────────────────────────────
|
|
624
631
|
// Skip the per-vertex scan when the camera has moved less than ~10 cm in
|
|
@@ -644,6 +651,10 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
644
651
|
// ENU basis at the camera — horizontal distance to each terrain vertex.
|
|
645
652
|
glm::dvec3 floorEast(1.0, 0.0, 0.0);
|
|
646
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
|
+
|
|
647
658
|
if (runFloorScan) {
|
|
648
659
|
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
649
660
|
const glm::dmat4 enuToEcef =
|
|
@@ -651,6 +662,58 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
651
662
|
cameraPos, ellipsoid);
|
|
652
663
|
floorEast = glm::normalize(glm::dvec3(enuToEcef[0]));
|
|
653
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
|
+
}
|
|
654
717
|
}
|
|
655
718
|
|
|
656
719
|
// ── fast path ────────────────────────────────────────────────────────
|
|
@@ -669,48 +732,83 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
669
732
|
|
|
670
733
|
// Floor scan (run once per res so multi-primitive tiles aren't scanned
|
|
671
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.
|
|
672
737
|
if (runFloorScan && c.res != lastScanRes) {
|
|
673
738
|
lastScanRes = c.res;
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
(c.wmTileBounds.
|
|
678
|
-
static_cast<float>(
|
|
679
|
-
|
|
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
|
+
|
|
680
761
|
for (const auto& prim : c.res->primitives) {
|
|
681
762
|
if (prim.altitudes.empty() || prim.localPositions.empty()) continue;
|
|
682
763
|
const size_t vc = prim.localPositions.size();
|
|
764
|
+
|
|
683
765
|
for (size_t vi = 0; vi < vc; ++vi) {
|
|
684
|
-
const glm::dvec3 vECEF =
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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;
|
|
780
|
+
}
|
|
698
781
|
}
|
|
699
782
|
}
|
|
700
|
-
|
|
701
|
-
//
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
+
}
|
|
706
797
|
}
|
|
707
798
|
}
|
|
708
799
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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;
|
|
811
|
+
}
|
|
714
812
|
}
|
|
715
813
|
}
|
|
716
814
|
|
|
@@ -797,44 +895,71 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
797
895
|
static_cast<float>(rect.getEast()),
|
|
798
896
|
static_cast<float>(rect.getNorth()));
|
|
799
897
|
|
|
800
|
-
// ── Terrain floor:
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
|
|
804
|
-
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
|
+
|
|
805
913
|
for (const auto& prim : res->primitives) {
|
|
806
914
|
if (prim.altitudes.empty() || prim.localPositions.empty()) continue;
|
|
807
915
|
const size_t vertexCount = prim.localPositions.size();
|
|
916
|
+
|
|
808
917
|
for (size_t vi = 0; vi < vertexCount; ++vi) {
|
|
809
|
-
const glm::dvec3 vECEF =
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if (
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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;
|
|
932
|
+
}
|
|
823
933
|
}
|
|
824
934
|
}
|
|
825
|
-
|
|
826
|
-
//
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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
|
+
}
|
|
831
949
|
}
|
|
832
950
|
}
|
|
833
951
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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;
|
|
838
963
|
}
|
|
839
964
|
}
|
|
840
965
|
}
|
package/ios/CesiumBridge.mm
CHANGED
|
@@ -384,17 +384,18 @@
|
|
|
384
384
|
auto actual = _integrator->step();
|
|
385
385
|
|
|
386
386
|
// ── Terrain floor clamp ───────────────────────────────────────────────
|
|
387
|
-
//
|
|
388
|
-
//
|
|
387
|
+
// Simple collision detection: if camera goes below terrain + offset, snap it up.
|
|
388
|
+
// Equivalent to CesiumJS's preRender terrain clamping.
|
|
389
389
|
const float minAbove = _engine->getConfig().minAltitudeAboveTerrain;
|
|
390
390
|
if (minAbove > 0.0f && _engine->terrainFloorKnown()) {
|
|
391
391
|
const double geoidN =
|
|
392
392
|
reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, 0.0);
|
|
393
|
-
const double
|
|
394
|
-
|
|
393
|
+
const double floorEllipsoid = static_cast<double>(_engine->terrainFloorEllipsoidMeters());
|
|
394
|
+
const double floorMsl = floorEllipsoid - geoidN;
|
|
395
395
|
const double minMsl = floorMsl + static_cast<double>(minAbove);
|
|
396
|
+
|
|
397
|
+
// Simple clamp: if below minimum, snap to minimum
|
|
396
398
|
if (actual.altitude < minMsl) {
|
|
397
|
-
_integrator->clampActualAltitude(minMsl);
|
|
398
399
|
actual.altitude = minMsl;
|
|
399
400
|
}
|
|
400
401
|
}
|