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.
- package/README.md +1 -1
- package/android/CMakeLists.txt +1 -0
- package/android/build.gradle +1 -1
- package/android/gradle.properties +4 -4
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +5 -15
- package/android/src/main/cpp/CesiumBridgeAndroid.h +2 -0
- package/cpp/engine/CameraIntegrator.cpp +82 -47
- package/cpp/engine/CameraIntegrator.hpp +27 -8
- package/cpp/engine/CesiumEngine.cpp +152 -304
- package/cpp/engine/CesiumEngine.hpp +38 -28
- package/cpp/engine/EngineTunables.hpp +6 -5
- package/cpp/engine/GltfToMesh.cpp +14 -5
- package/cpp/engine/ResourcePreparer.cpp +3 -3
- package/cpp/engine/ResourcePreparer.hpp +1 -1
- package/cpp/engine/TerrainFloor.hpp +70 -0
- package/cpp/engine/TerrainHeight.hpp +113 -0
- package/cpp/engine/test/CameraIntegratorTest.cpp +264 -0
- package/ios/CesiumBridge.mm +7 -27
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +7 -6
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/ReactNativeCesiumOnLoad.cpp +1 -1
- package/nitrogen/generated/android/c++/JCameraState.hpp +1 -1
- package/nitrogen/generated/android/c++/JCesiumMetrics.hpp +1 -1
- package/nitrogen/generated/android/c++/JQuaternion.hpp +1 -1
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +177 -131
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.hpp +6 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CameraState.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CesiumMetrics.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Func_void_CameraState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Func_void_CesiumMetrics.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Quaternion.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/views/HybridCesiumViewManager.kt +26 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/views/HybridCesiumViewStateUpdater.kt +2 -2
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +211 -161
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +34 -335
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +107 -77
- package/package.json +12 -12
- package/scripts/cesium/config.mjs +1 -1
- package/scripts/cesium/vcpkg-triplets/arm64-ios-simulator.cmake +3 -0
- package/scripts/cesium/vcpkg-triplets/arm64-ios.cmake +4 -0
- package/src/specs/cesium-view.nitro.ts +7 -6
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#endif
|
|
5
5
|
|
|
6
6
|
#include "CesiumEngine.hpp"
|
|
7
|
-
#include "
|
|
7
|
+
#include "TerrainHeight.hpp"
|
|
8
8
|
|
|
9
9
|
#include <iostream>
|
|
10
10
|
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
#include <algorithm>
|
|
38
38
|
#include <cmath>
|
|
39
39
|
#include <cstring>
|
|
40
|
-
#include <limits>
|
|
41
40
|
#include <thread>
|
|
42
41
|
|
|
43
42
|
|
|
@@ -404,9 +403,7 @@ void CesiumEngine::applyImageryOverlay(int64_t targetAssetId) {
|
|
|
404
403
|
lastDrawSignature_ = 0;
|
|
405
404
|
stableSigFrames_ = 0;
|
|
406
405
|
cachedDraws_.clear();
|
|
407
|
-
|
|
408
|
-
// re-scans before establishing a new entry.
|
|
409
|
-
floorCacheValid_ = false;
|
|
406
|
+
heightCache_.valid = false;
|
|
410
407
|
}
|
|
411
408
|
|
|
412
409
|
void CesiumEngine::destroyTileset() {
|
|
@@ -422,8 +419,7 @@ void CesiumEngine::destroyTileset() {
|
|
|
422
419
|
lastDrawSignature_ = 0;
|
|
423
420
|
stableSigFrames_ = 0;
|
|
424
421
|
cachedDraws_.clear();
|
|
425
|
-
|
|
426
|
-
floorCacheValid_ = false;
|
|
422
|
+
clearResidentTerrain();
|
|
427
423
|
}
|
|
428
424
|
|
|
429
425
|
void CesiumEngine::setImageryAssetId(int64_t assetId) {
|
|
@@ -445,6 +441,126 @@ inline uint64_t fnv1a64Mix(uint64_t h, uint64_t v) {
|
|
|
445
441
|
}
|
|
446
442
|
} // namespace
|
|
447
443
|
|
|
444
|
+
void CesiumEngine::clearResidentTerrain() {
|
|
445
|
+
residentTiles_.clear();
|
|
446
|
+
residentSignature_ = 0;
|
|
447
|
+
heightCache_.valid = false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
bool CesiumEngine::sampleTerrainEllipsoidMeters(double latitudeDeg,
|
|
451
|
+
double longitudeDeg,
|
|
452
|
+
float& outEllipsoidMeters) {
|
|
453
|
+
if (residentTiles_.empty() || !std::isfinite(latitudeDeg) || !std::isfinite(longitudeDeg)) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ~0.5 m cell. A hover reuses the sample; moving to the next cell rescans.
|
|
458
|
+
constexpr double kCellDeg = 4.5e-6;
|
|
459
|
+
const int latCell = static_cast<int>(std::floor(latitudeDeg / kCellDeg));
|
|
460
|
+
const int lonCell = static_cast<int>(std::floor(longitudeDeg / kCellDeg));
|
|
461
|
+
if (heightCache_.valid &&
|
|
462
|
+
heightCache_.signature == residentSignature_ &&
|
|
463
|
+
heightCache_.latCell == latCell &&
|
|
464
|
+
heightCache_.lonCell == lonCell) {
|
|
465
|
+
outEllipsoidMeters = heightCache_.height;
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const double latRad = glm::radians(latitudeDeg);
|
|
470
|
+
const double lonRad = glm::radians(longitudeDeg);
|
|
471
|
+
|
|
472
|
+
const ResidentTerrainTile* tried[8] = {};
|
|
473
|
+
int triedCount = 0;
|
|
474
|
+
|
|
475
|
+
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
476
|
+
const glm::dvec3 origin = ellipsoid.cartographicToCartesian(
|
|
477
|
+
CesiumGeospatial::Cartographic(lonRad, latRad, 0.0));
|
|
478
|
+
const glm::dmat4 enuToEcef =
|
|
479
|
+
CesiumGeospatial::GlobeTransforms::eastNorthUpToFixedFrame(origin, ellipsoid);
|
|
480
|
+
const glm::dvec3 east = glm::normalize(glm::dvec3(enuToEcef[0]));
|
|
481
|
+
const glm::dvec3 north = glm::normalize(glm::dvec3(enuToEcef[1]));
|
|
482
|
+
|
|
483
|
+
auto contains = [&](const ResidentTerrainTile& tile) {
|
|
484
|
+
if (latRad < tile.south || latRad > tile.north) return false;
|
|
485
|
+
if (tile.west <= tile.east) return lonRad >= tile.west && lonRad <= tile.east;
|
|
486
|
+
return lonRad >= tile.west || lonRad <= tile.east;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
auto sampleTile = [&](const ResidentTerrainTile& tile, float& outHeight) {
|
|
490
|
+
if (!tile.res) return false;
|
|
491
|
+
bool found = false;
|
|
492
|
+
float best = 0.0f;
|
|
493
|
+
for (const auto& prim : tile.res->primitives) {
|
|
494
|
+
const size_t nVerts = prim.localPositions.size();
|
|
495
|
+
if (nVerts == 0 || prim.altitudes.size() != nVerts) continue;
|
|
496
|
+
const size_t nTri = prim.indices.size() / 3;
|
|
497
|
+
for (size_t t = 0; t < nTri; ++t) {
|
|
498
|
+
const uint32_t i0 = prim.indices[t * 3];
|
|
499
|
+
const uint32_t i1 = prim.indices[t * 3 + 1];
|
|
500
|
+
const uint32_t i2 = prim.indices[t * 3 + 2];
|
|
501
|
+
if (i0 >= nVerts || i1 >= nVerts || i2 >= nVerts) continue;
|
|
502
|
+
|
|
503
|
+
auto project = [&](uint32_t i, double& e, double& n) {
|
|
504
|
+
const glm::dvec3 world = glm::dvec3(prim.localPositions[i]) + prim.rtcCenter;
|
|
505
|
+
const glm::dvec3 delta = world - origin;
|
|
506
|
+
e = glm::dot(delta, east);
|
|
507
|
+
n = glm::dot(delta, north);
|
|
508
|
+
};
|
|
509
|
+
double e0, n0, e1, n1, e2, n2;
|
|
510
|
+
project(i0, e0, n0);
|
|
511
|
+
project(i1, e1, n1);
|
|
512
|
+
project(i2, e2, n2);
|
|
513
|
+
|
|
514
|
+
float h = 0.0f;
|
|
515
|
+
if (!horizontalTriangleHeight(0.0, 0.0,
|
|
516
|
+
e0, n0, prim.altitudes[i0],
|
|
517
|
+
e1, n1, prim.altitudes[i1],
|
|
518
|
+
e2, n2, prim.altitudes[i2],
|
|
519
|
+
h)) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
if (!found || h > best) {
|
|
523
|
+
best = h;
|
|
524
|
+
found = true;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (!found) return false;
|
|
529
|
+
outHeight = best;
|
|
530
|
+
return true;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// Finest containing tile first. If its mesh misses the point (a crack
|
|
534
|
+
// between tiles), try the next-finest. At most a handful of LODs overlap.
|
|
535
|
+
for (int attempt = 0; attempt < 8; ++attempt) {
|
|
536
|
+
const ResidentTerrainTile* best = nullptr;
|
|
537
|
+
for (const auto& tile : residentTiles_) {
|
|
538
|
+
if (!contains(tile)) continue;
|
|
539
|
+
bool skipped = false;
|
|
540
|
+
for (int i = 0; i < triedCount; ++i) {
|
|
541
|
+
if (tried[i] == &tile) { skipped = true; break; }
|
|
542
|
+
}
|
|
543
|
+
if (skipped) continue;
|
|
544
|
+
if (!best || tile.geometricError < best->geometricError) best = &tile;
|
|
545
|
+
}
|
|
546
|
+
if (!best) break;
|
|
547
|
+
|
|
548
|
+
float height = 0.0f;
|
|
549
|
+
if (sampleTile(*best, height)) {
|
|
550
|
+
heightCache_.valid = true;
|
|
551
|
+
heightCache_.tile = best->res;
|
|
552
|
+
heightCache_.latCell = latCell;
|
|
553
|
+
heightCache_.lonCell = lonCell;
|
|
554
|
+
heightCache_.signature = residentSignature_;
|
|
555
|
+
heightCache_.height = height;
|
|
556
|
+
outEllipsoidMeters = height;
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
if (triedCount < 8) tried[triedCount++] = best;
|
|
560
|
+
}
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
|
|
448
564
|
void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
449
565
|
result.localPositions.clear();
|
|
450
566
|
result.altitudes.clear();
|
|
@@ -475,6 +591,7 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
475
591
|
lastDrawSignature_ = 0;
|
|
476
592
|
stableSigFrames_ = 0;
|
|
477
593
|
cachedDraws_.clear();
|
|
594
|
+
clearResidentTerrain();
|
|
478
595
|
lifecycle_.advanceFrame();
|
|
479
596
|
return;
|
|
480
597
|
}
|
|
@@ -604,116 +721,35 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
604
721
|
stableSigFrames_ > tunables::kMaxFramesInFlight &&
|
|
605
722
|
!cachedDraws_.empty();
|
|
606
723
|
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
// geometry signature). The cached value is only trusted for non-zero
|
|
634
|
-
// signatures — sig==0 means a fade or rebuild is in flight, in which case
|
|
635
|
-
// we always re-scan and refresh the cache afterwards.
|
|
636
|
-
bool floorScanFromCache = false;
|
|
637
|
-
if (trackTerrainFloor && floorCacheValid_ && signature != 0ULL &&
|
|
638
|
-
signature == floorCacheSignature_) {
|
|
639
|
-
const double dLon = std::abs(camParams.longitude - floorCacheCamLonDeg_);
|
|
640
|
-
const double dLat = std::abs(camParams.latitude - floorCacheCamLatDeg_);
|
|
641
|
-
if (dLon < 1e-6 && dLat < 1e-6) {
|
|
642
|
-
bestTerrainFloor = floorCacheValue_;
|
|
643
|
-
floorScanFromCache = true;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
// When tracking is off, suppress the per-tile scan branches (legacy
|
|
647
|
-
// behaviour already gated on trackTerrainFloor; this just keeps the cache
|
|
648
|
-
// semantics aligned).
|
|
649
|
-
const bool runFloorScan = trackTerrainFloor && !floorScanFromCache;
|
|
650
|
-
|
|
651
|
-
// ENU basis at the camera — horizontal distance to each terrain vertex.
|
|
652
|
-
glm::dvec3 floorEast(1.0, 0.0, 0.0);
|
|
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
|
-
|
|
658
|
-
if (runFloorScan) {
|
|
659
|
-
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
660
|
-
const glm::dmat4 enuToEcef =
|
|
661
|
-
CesiumGeospatial::GlobeTransforms::eastNorthUpToFixedFrame(
|
|
662
|
-
cameraPos, ellipsoid);
|
|
663
|
-
floorEast = glm::normalize(glm::dvec3(enuToEcef[0]));
|
|
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;
|
|
724
|
+
// Capture the tiles this frame actually drew. The next render samples
|
|
725
|
+
// height from this list before updateViewGroup can release it. Bounds and
|
|
726
|
+
// geometric error only — no per-vertex work on the draw path.
|
|
727
|
+
if (noVisibleTerrain) {
|
|
728
|
+
clearResidentTerrain();
|
|
729
|
+
} else {
|
|
730
|
+
residentTiles_.clear();
|
|
731
|
+
residentTiles_.reserve(updateResult.tilesToRenderThisFrame.size());
|
|
732
|
+
for (const auto& tile : updateResult.tilesToRenderThisFrame) {
|
|
733
|
+
if (!tile) continue;
|
|
734
|
+
const auto* rc = tile->getContent().getRenderContent();
|
|
735
|
+
if (!rc) continue;
|
|
736
|
+
const auto* res = static_cast<const TileGPUResources*>(rc->getRenderResources());
|
|
737
|
+
if (!res || res->primitives.empty()) continue;
|
|
738
|
+
const CesiumGeospatial::BoundingRegion* region =
|
|
739
|
+
Cesium3DTilesSelection::getBoundingRegionFromBoundingVolume(tile->getBoundingVolume());
|
|
740
|
+
if (!region) continue;
|
|
741
|
+
const auto& rect = region->getRectangle();
|
|
742
|
+
ResidentTerrainTile entry;
|
|
743
|
+
entry.res = res;
|
|
744
|
+
entry.geometricError = tile->getGeometricError();
|
|
745
|
+
entry.west = rect.getWest();
|
|
746
|
+
entry.south = rect.getSouth();
|
|
747
|
+
entry.east = rect.getEast();
|
|
748
|
+
entry.north = rect.getNorth();
|
|
749
|
+
residentTiles_.push_back(entry);
|
|
716
750
|
}
|
|
751
|
+
residentSignature_ = signature;
|
|
752
|
+
if (heightCache_.signature != residentSignature_) heightCache_.valid = false;
|
|
717
753
|
}
|
|
718
754
|
|
|
719
755
|
// ── fast path ────────────────────────────────────────────────────────
|
|
@@ -726,92 +762,9 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
726
762
|
// detect this via result.geometrySignature and use their cached slot data.
|
|
727
763
|
if (canFastPath) {
|
|
728
764
|
result.draws.reserve(cachedDraws_.size());
|
|
729
|
-
const TileGPUResources* lastScanRes = nullptr;
|
|
730
765
|
for (const auto& c : cachedDraws_) {
|
|
731
766
|
lifecycle_.stampTileUsed(const_cast<TileGPUResources*>(c.res));
|
|
732
767
|
|
|
733
|
-
// Floor scan (run once per res so multi-primitive tiles aren't scanned
|
|
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.
|
|
737
|
-
if (runFloorScan && c.res != lastScanRes) {
|
|
738
|
-
lastScanRes = c.res;
|
|
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
|
-
|
|
761
|
-
for (const auto& prim : c.res->primitives) {
|
|
762
|
-
if (prim.altitudes.empty() || prim.localPositions.empty()) continue;
|
|
763
|
-
const size_t vc = prim.localPositions.size();
|
|
764
|
-
|
|
765
|
-
for (size_t vi = 0; vi < vc; ++vi) {
|
|
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
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
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
|
-
}
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
}
|
|
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
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
768
|
const glm::dvec3 rtcCamRel = c.rtcCenter - glm::dvec3(cameraPos);
|
|
816
769
|
const glm::dmat4 translateD = glm::translate(glm::dmat4(1.0), rtcCamRel);
|
|
817
770
|
|
|
@@ -845,20 +798,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
845
798
|
result.cameraLon = paramsFP.longitude;
|
|
846
799
|
result.cameraAlt = paramsFP.altitude;
|
|
847
800
|
|
|
848
|
-
if (trackTerrainFloor && (floorScanFound || floorScanFromCache)) {
|
|
849
|
-
terrainFloorEllipsoidM_ = bestTerrainFloor;
|
|
850
|
-
terrainFloorValid_ = true;
|
|
851
|
-
// P5: refresh the floor cache on a fresh scan; skip when we hit the
|
|
852
|
-
// cache (the stored value already reflects this signature).
|
|
853
|
-
if (runFloorScan && signature != 0ULL) {
|
|
854
|
-
floorCacheValid_ = true;
|
|
855
|
-
floorCacheSignature_ = signature;
|
|
856
|
-
floorCacheCamLonDeg_ = camParams.longitude;
|
|
857
|
-
floorCacheCamLatDeg_ = camParams.latitude;
|
|
858
|
-
floorCacheValue_ = bestTerrainFloor;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
801
|
lifecycle_.advanceFrame();
|
|
863
802
|
return;
|
|
864
803
|
}
|
|
@@ -894,74 +833,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
894
833
|
static_cast<float>(rect.getSouth()),
|
|
895
834
|
static_cast<float>(rect.getEast()),
|
|
896
835
|
static_cast<float>(rect.getNorth()));
|
|
897
|
-
|
|
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
|
-
|
|
913
|
-
for (const auto& prim : res->primitives) {
|
|
914
|
-
if (prim.altitudes.empty() || prim.localPositions.empty()) continue;
|
|
915
|
-
const size_t vertexCount = prim.localPositions.size();
|
|
916
|
-
|
|
917
|
-
for (size_t vi = 0; vi < vertexCount; ++vi) {
|
|
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
|
-
}
|
|
933
|
-
}
|
|
934
|
-
}
|
|
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
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
}
|
|
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;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
836
|
}
|
|
966
837
|
|
|
967
838
|
for (const auto& prim : res->primitives) {
|
|
@@ -1209,29 +1080,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
1209
1080
|
result.cameraLon = params.longitude;
|
|
1210
1081
|
result.cameraAlt = params.altitude;
|
|
1211
1082
|
|
|
1212
|
-
if (trackTerrainFloor && (floorScanFound || floorScanFromCache)) {
|
|
1213
|
-
terrainFloorEllipsoidM_ = bestTerrainFloor;
|
|
1214
|
-
terrainFloorValid_ = true;
|
|
1215
|
-
// refresh the floor cache on a fresh scan (slow path always runs a
|
|
1216
|
-
// scan unless the cache short-circuit fired above). Only memoise when
|
|
1217
|
-
// the geometry signature is stable enough to key on.
|
|
1218
|
-
if (runFloorScan && signature != 0ULL) {
|
|
1219
|
-
floorCacheValid_ = true;
|
|
1220
|
-
floorCacheSignature_ = signature;
|
|
1221
|
-
floorCacheCamLonDeg_ = camParams.longitude;
|
|
1222
|
-
floorCacheCamLatDeg_ = camParams.latitude;
|
|
1223
|
-
floorCacheValue_ = bestTerrainFloor;
|
|
1224
|
-
} else if (!runFloorScan) {
|
|
1225
|
-
// Cache hit on the slow path is unusual but possible (e.g. fade
|
|
1226
|
-
// started this frame after a stable cache was warm). Nothing to do
|
|
1227
|
-
// — terrainFloorEllipsoidM_ is already the cached value.
|
|
1228
|
-
} else {
|
|
1229
|
-
// Cannot key on signature=0; invalidate so the next stable frame
|
|
1230
|
-
// re-populates the cache deterministically.
|
|
1231
|
-
floorCacheValid_ = false;
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
1083
|
lifecycle_.advanceFrame();
|
|
1236
1084
|
}
|
|
1237
1085
|
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
#include <CesiumUtility/IntrusivePointer.h>
|
|
13
13
|
|
|
14
14
|
#include <chrono>
|
|
15
|
+
#include <cstdint>
|
|
15
16
|
#include <memory>
|
|
16
17
|
#include <string>
|
|
18
|
+
#include <vector>
|
|
17
19
|
|
|
18
20
|
namespace Cesium3DTilesSelection {
|
|
19
21
|
class Tileset;
|
|
@@ -103,8 +105,9 @@ struct EngineConfig {
|
|
|
103
105
|
int32_t taskProcessorThreads = 0;
|
|
104
106
|
|
|
105
107
|
// ── Camera constraints ───────────────────────────────────────────────
|
|
106
|
-
//
|
|
107
|
-
// 0
|
|
108
|
+
// Clearance above the rendered terrain surface, in metres. The floor is
|
|
109
|
+
// always enforced. 0 means the camera may sit on the surface and may not
|
|
110
|
+
// go below it.
|
|
108
111
|
float minAltitudeAboveTerrain = 0.0f;
|
|
109
112
|
};
|
|
110
113
|
|
|
@@ -142,11 +145,13 @@ public:
|
|
|
142
145
|
|
|
143
146
|
const EngineConfig& getConfig() const { return config_; }
|
|
144
147
|
|
|
145
|
-
// Ellipsoid height (metres) of the terrain surface
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
bool
|
|
148
|
+
// Ellipsoid height (metres) of the rendered terrain surface at lat/lon,
|
|
149
|
+
// interpolated from the finest resident tile. False when no triangle
|
|
150
|
+
// contains the point (tiles not loaded yet, or the point is off the mesh).
|
|
151
|
+
// Uses the tiles captured by the previous updateFrame.
|
|
152
|
+
bool sampleTerrainEllipsoidMeters(double latitudeDeg,
|
|
153
|
+
double longitudeDeg,
|
|
154
|
+
float& outEllipsoidMeters);
|
|
150
155
|
|
|
151
156
|
private:
|
|
152
157
|
void createTileset();
|
|
@@ -185,11 +190,6 @@ private:
|
|
|
185
190
|
GlobeCamera camera_;
|
|
186
191
|
TileLifecycleManager lifecycle_;
|
|
187
192
|
|
|
188
|
-
// Ellipsoid height of the terrain surface at the camera nadir (horizontal
|
|
189
|
-
// nearest-neighbour sample). Only written after a successful tile scan.
|
|
190
|
-
float terrainFloorEllipsoidM_ = 0.0f;
|
|
191
|
-
bool terrainFloorValid_ = false;
|
|
192
|
-
|
|
193
193
|
// Wall-clock timestamp of the previous updateFrame call. Used to compute
|
|
194
194
|
// deltaTime for Tileset::updateViewGroup, which drives LOD transition fading.
|
|
195
195
|
// Zero-initialised "not yet set" state — first frame passes 0 deltaTime.
|
|
@@ -224,22 +224,32 @@ private:
|
|
|
224
224
|
// is guaranteed to contain the right bytes — fast path becomes legal.
|
|
225
225
|
int stableSigFrames_ = 0;
|
|
226
226
|
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
227
|
+
// Tiles drawn by the last updateFrame. The next frame's terrain query
|
|
228
|
+
// reads this list before updateViewGroup can free the previous selection.
|
|
229
|
+
struct ResidentTerrainTile {
|
|
230
|
+
const TileGPUResources* res = nullptr;
|
|
231
|
+
double geometricError = 0.0;
|
|
232
|
+
double west = 0.0;
|
|
233
|
+
double south = 0.0;
|
|
234
|
+
double east = 0.0;
|
|
235
|
+
double north = 0.0;
|
|
236
|
+
};
|
|
237
|
+
std::vector<ResidentTerrainTile> residentTiles_;
|
|
238
|
+
uint64_t residentSignature_ = 0;
|
|
239
|
+
|
|
240
|
+
// One-entry hover cache. Keyed by resident signature, finest-tile identity,
|
|
241
|
+
// and a sub-metre lat/lon cell. A miss scans that one tile's triangles.
|
|
242
|
+
struct TerrainHeightCache {
|
|
243
|
+
bool valid = false;
|
|
244
|
+
const TileGPUResources* tile = nullptr;
|
|
245
|
+
int latCell = 0;
|
|
246
|
+
int lonCell = 0;
|
|
247
|
+
uint64_t signature = 0;
|
|
248
|
+
float height = 0.0f;
|
|
249
|
+
};
|
|
250
|
+
TerrainHeightCache heightCache_;
|
|
251
|
+
|
|
252
|
+
void clearResidentTerrain();
|
|
243
253
|
};
|
|
244
254
|
|
|
245
255
|
} // namespace reactnativecesium
|
|
@@ -63,16 +63,17 @@ inline constexpr int32_t kDefaultLoadingDescendantLimit = 20;
|
|
|
63
63
|
// steady-state lag at constant motion) while remaining instant under a
|
|
64
64
|
// 60 Hz gesture-driven worklet.
|
|
65
65
|
//
|
|
66
|
-
// `viewCorrection` (a unit quaternion)
|
|
67
|
-
//
|
|
68
|
-
// `verticalFovDeg`
|
|
66
|
+
// `viewCorrection` (a unit quaternion) slerps toward the latest demand with a
|
|
67
|
+
// time constant, so a single long frame does not close half the error.
|
|
68
|
+
// `verticalFovDeg` eases toward its demand with the same kind of time constant
|
|
69
|
+
// (no velocity term).
|
|
69
70
|
inline constexpr double kAlphaPos = 0.6; inline constexpr double kBetaPos = 0.05;
|
|
70
71
|
inline constexpr double kAlphaAlt = 0.6; inline constexpr double kBetaAlt = 0.05;
|
|
71
72
|
inline constexpr double kAlphaHdg = 0.55; inline constexpr double kBetaHdg = 0.05;
|
|
72
73
|
inline constexpr double kAlphaPitch = 0.75; inline constexpr double kBetaPitch = 0.05;
|
|
73
74
|
inline constexpr double kAlphaRoll = 0.75; inline constexpr double kBetaRoll = 0.05;
|
|
74
|
-
inline constexpr double
|
|
75
|
-
inline constexpr double
|
|
75
|
+
inline constexpr double kVfovTauSec = 0.12; // seconds, dt-based blend
|
|
76
|
+
inline constexpr double kViewCorrTauSec = 0.08; // seconds, dt-based slerp
|
|
76
77
|
|
|
77
78
|
// Convergence epsilons (used to decide when the integrator has settled and
|
|
78
79
|
// the render loop can drop back to idle).
|