react-native-cesium 1.2.18 → 1.2.20
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 +11 -8
- 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 +44 -18
- package/android/src/main/cpp/CesiumBridgeAndroid.h +9 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +3 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +9 -0
- package/cpp/engine/CameraIntegrator.cpp +82 -47
- package/cpp/engine/CameraIntegrator.hpp +27 -8
- package/cpp/engine/CesiumEngine.cpp +183 -312
- package/cpp/engine/CesiumEngine.hpp +41 -28
- package/cpp/engine/EngineTunables.hpp +6 -5
- package/cpp/engine/GeoidConverter.cpp +32 -11
- package/cpp/engine/GeoidConverter.hpp +41 -0
- 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/cpp/engine/test/GeoidConverterTest.cpp +77 -0
- package/cpp/engine/test/build_and_run.sh +20 -6
- package/ios/CesiumBridge.h +5 -0
- package/ios/CesiumBridge.mm +61 -32
- package/ios/HybridCesiumView.swift +11 -0
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +34 -9
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +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++/JHybridCesiumViewSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/JQuaternion.hpp +1 -1
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +182 -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 +8 -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/ReactNativeCesium-Swift-Cxx-Bridge.hpp +15 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +7 -0
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +217 -161
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +24 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +36 -335
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +110 -77
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +1 -0
- 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 +34 -9
|
@@ -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
|
|
|
@@ -213,6 +212,21 @@ void CesiumEngine::shutdown() {
|
|
|
213
212
|
creditSystem_.reset();
|
|
214
213
|
}
|
|
215
214
|
|
|
215
|
+
void CesiumEngine::drainAsyncWork() {
|
|
216
|
+
// Worker tasks post main-thread continuations and return. Those
|
|
217
|
+
// continuations (Ion raster-overlay loads in particular) can queue more
|
|
218
|
+
// worker tasks, so pump both sides until the pool stays idle.
|
|
219
|
+
for (int pass = 0; pass < 16; ++pass) {
|
|
220
|
+
if (taskProcessor_) {
|
|
221
|
+
taskProcessor_->waitUntilIdle();
|
|
222
|
+
}
|
|
223
|
+
asyncSystem_.dispatchMainThreadTasks();
|
|
224
|
+
}
|
|
225
|
+
if (taskProcessor_) {
|
|
226
|
+
taskProcessor_->waitUntilIdle();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
216
230
|
void CesiumEngine::updateConfig(const EngineConfig& config) {
|
|
217
231
|
// Only token / asset id changes force a full tileset rebuild. Everything
|
|
218
232
|
// else is mutated in place via tileset_->getOptions() to avoid the very
|
|
@@ -404,26 +418,31 @@ void CesiumEngine::applyImageryOverlay(int64_t targetAssetId) {
|
|
|
404
418
|
lastDrawSignature_ = 0;
|
|
405
419
|
stableSigFrames_ = 0;
|
|
406
420
|
cachedDraws_.clear();
|
|
407
|
-
|
|
408
|
-
// re-scans before establishing a new entry.
|
|
409
|
-
floorCacheValid_ = false;
|
|
421
|
+
heightCache_.valid = false;
|
|
410
422
|
}
|
|
411
423
|
|
|
412
424
|
void CesiumEngine::destroyTileset() {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (
|
|
417
|
-
|
|
425
|
+
// Detach the imagery overlay while this engine still owns it. Dropping
|
|
426
|
+
// currentOverlay_ before in-flight IonRasterOverlay tasks finish frees the
|
|
427
|
+
// overlay out from under their destructors (heap corruption).
|
|
428
|
+
if (tileset_ && currentOverlay_) {
|
|
429
|
+
tileset_->getOverlays().remove(currentOverlay_);
|
|
418
430
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
//
|
|
431
|
+
drainAsyncWork();
|
|
432
|
+
|
|
433
|
+
// Drop draw-cache pointers before the tileset frees TileGPUResources.
|
|
422
434
|
lastDrawSignature_ = 0;
|
|
423
435
|
stableSigFrames_ = 0;
|
|
424
436
|
cachedDraws_.clear();
|
|
425
|
-
|
|
426
|
-
|
|
437
|
+
clearResidentTerrain();
|
|
438
|
+
|
|
439
|
+
tileset_.reset();
|
|
440
|
+
currentImageryAssetId_ = 1;
|
|
441
|
+
// Tileset destruction schedules further async cleanup. Drain that while
|
|
442
|
+
// this engine still owns the overlay, then release it. Dropping the
|
|
443
|
+
// overlay first frees it under those continuations.
|
|
444
|
+
drainAsyncWork();
|
|
445
|
+
currentOverlay_ = nullptr;
|
|
427
446
|
}
|
|
428
447
|
|
|
429
448
|
void CesiumEngine::setImageryAssetId(int64_t assetId) {
|
|
@@ -445,6 +464,126 @@ inline uint64_t fnv1a64Mix(uint64_t h, uint64_t v) {
|
|
|
445
464
|
}
|
|
446
465
|
} // namespace
|
|
447
466
|
|
|
467
|
+
void CesiumEngine::clearResidentTerrain() {
|
|
468
|
+
residentTiles_.clear();
|
|
469
|
+
residentSignature_ = 0;
|
|
470
|
+
heightCache_.valid = false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
bool CesiumEngine::sampleTerrainEllipsoidMeters(double latitudeDeg,
|
|
474
|
+
double longitudeDeg,
|
|
475
|
+
float& outEllipsoidMeters) {
|
|
476
|
+
if (residentTiles_.empty() || !std::isfinite(latitudeDeg) || !std::isfinite(longitudeDeg)) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ~0.5 m cell. A hover reuses the sample; moving to the next cell rescans.
|
|
481
|
+
constexpr double kCellDeg = 4.5e-6;
|
|
482
|
+
const int latCell = static_cast<int>(std::floor(latitudeDeg / kCellDeg));
|
|
483
|
+
const int lonCell = static_cast<int>(std::floor(longitudeDeg / kCellDeg));
|
|
484
|
+
if (heightCache_.valid &&
|
|
485
|
+
heightCache_.signature == residentSignature_ &&
|
|
486
|
+
heightCache_.latCell == latCell &&
|
|
487
|
+
heightCache_.lonCell == lonCell) {
|
|
488
|
+
outEllipsoidMeters = heightCache_.height;
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const double latRad = glm::radians(latitudeDeg);
|
|
493
|
+
const double lonRad = glm::radians(longitudeDeg);
|
|
494
|
+
|
|
495
|
+
const ResidentTerrainTile* tried[8] = {};
|
|
496
|
+
int triedCount = 0;
|
|
497
|
+
|
|
498
|
+
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
499
|
+
const glm::dvec3 origin = ellipsoid.cartographicToCartesian(
|
|
500
|
+
CesiumGeospatial::Cartographic(lonRad, latRad, 0.0));
|
|
501
|
+
const glm::dmat4 enuToEcef =
|
|
502
|
+
CesiumGeospatial::GlobeTransforms::eastNorthUpToFixedFrame(origin, ellipsoid);
|
|
503
|
+
const glm::dvec3 east = glm::normalize(glm::dvec3(enuToEcef[0]));
|
|
504
|
+
const glm::dvec3 north = glm::normalize(glm::dvec3(enuToEcef[1]));
|
|
505
|
+
|
|
506
|
+
auto contains = [&](const ResidentTerrainTile& tile) {
|
|
507
|
+
if (latRad < tile.south || latRad > tile.north) return false;
|
|
508
|
+
if (tile.west <= tile.east) return lonRad >= tile.west && lonRad <= tile.east;
|
|
509
|
+
return lonRad >= tile.west || lonRad <= tile.east;
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
auto sampleTile = [&](const ResidentTerrainTile& tile, float& outHeight) {
|
|
513
|
+
if (!tile.res) return false;
|
|
514
|
+
bool found = false;
|
|
515
|
+
float best = 0.0f;
|
|
516
|
+
for (const auto& prim : tile.res->primitives) {
|
|
517
|
+
const size_t nVerts = prim.localPositions.size();
|
|
518
|
+
if (nVerts == 0 || prim.altitudes.size() != nVerts) continue;
|
|
519
|
+
const size_t nTri = prim.indices.size() / 3;
|
|
520
|
+
for (size_t t = 0; t < nTri; ++t) {
|
|
521
|
+
const uint32_t i0 = prim.indices[t * 3];
|
|
522
|
+
const uint32_t i1 = prim.indices[t * 3 + 1];
|
|
523
|
+
const uint32_t i2 = prim.indices[t * 3 + 2];
|
|
524
|
+
if (i0 >= nVerts || i1 >= nVerts || i2 >= nVerts) continue;
|
|
525
|
+
|
|
526
|
+
auto project = [&](uint32_t i, double& e, double& n) {
|
|
527
|
+
const glm::dvec3 world = glm::dvec3(prim.localPositions[i]) + prim.rtcCenter;
|
|
528
|
+
const glm::dvec3 delta = world - origin;
|
|
529
|
+
e = glm::dot(delta, east);
|
|
530
|
+
n = glm::dot(delta, north);
|
|
531
|
+
};
|
|
532
|
+
double e0, n0, e1, n1, e2, n2;
|
|
533
|
+
project(i0, e0, n0);
|
|
534
|
+
project(i1, e1, n1);
|
|
535
|
+
project(i2, e2, n2);
|
|
536
|
+
|
|
537
|
+
float h = 0.0f;
|
|
538
|
+
if (!horizontalTriangleHeight(0.0, 0.0,
|
|
539
|
+
e0, n0, prim.altitudes[i0],
|
|
540
|
+
e1, n1, prim.altitudes[i1],
|
|
541
|
+
e2, n2, prim.altitudes[i2],
|
|
542
|
+
h)) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (!found || h > best) {
|
|
546
|
+
best = h;
|
|
547
|
+
found = true;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (!found) return false;
|
|
552
|
+
outHeight = best;
|
|
553
|
+
return true;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
// Finest containing tile first. If its mesh misses the point (a crack
|
|
557
|
+
// between tiles), try the next-finest. At most a handful of LODs overlap.
|
|
558
|
+
for (int attempt = 0; attempt < 8; ++attempt) {
|
|
559
|
+
const ResidentTerrainTile* best = nullptr;
|
|
560
|
+
for (const auto& tile : residentTiles_) {
|
|
561
|
+
if (!contains(tile)) continue;
|
|
562
|
+
bool skipped = false;
|
|
563
|
+
for (int i = 0; i < triedCount; ++i) {
|
|
564
|
+
if (tried[i] == &tile) { skipped = true; break; }
|
|
565
|
+
}
|
|
566
|
+
if (skipped) continue;
|
|
567
|
+
if (!best || tile.geometricError < best->geometricError) best = &tile;
|
|
568
|
+
}
|
|
569
|
+
if (!best) break;
|
|
570
|
+
|
|
571
|
+
float height = 0.0f;
|
|
572
|
+
if (sampleTile(*best, height)) {
|
|
573
|
+
heightCache_.valid = true;
|
|
574
|
+
heightCache_.tile = best->res;
|
|
575
|
+
heightCache_.latCell = latCell;
|
|
576
|
+
heightCache_.lonCell = lonCell;
|
|
577
|
+
heightCache_.signature = residentSignature_;
|
|
578
|
+
heightCache_.height = height;
|
|
579
|
+
outEllipsoidMeters = height;
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
if (triedCount < 8) tried[triedCount++] = best;
|
|
583
|
+
}
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
|
|
448
587
|
void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
449
588
|
result.localPositions.clear();
|
|
450
589
|
result.altitudes.clear();
|
|
@@ -475,6 +614,7 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
475
614
|
lastDrawSignature_ = 0;
|
|
476
615
|
stableSigFrames_ = 0;
|
|
477
616
|
cachedDraws_.clear();
|
|
617
|
+
clearResidentTerrain();
|
|
478
618
|
lifecycle_.advanceFrame();
|
|
479
619
|
return;
|
|
480
620
|
}
|
|
@@ -604,116 +744,35 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
604
744
|
stableSigFrames_ > tunables::kMaxFramesInFlight &&
|
|
605
745
|
!cachedDraws_.empty();
|
|
606
746
|
|
|
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;
|
|
747
|
+
// Capture the tiles this frame actually drew. The next render samples
|
|
748
|
+
// height from this list before updateViewGroup can release it. Bounds and
|
|
749
|
+
// geometric error only — no per-vertex work on the draw path.
|
|
750
|
+
if (noVisibleTerrain) {
|
|
751
|
+
clearResidentTerrain();
|
|
752
|
+
} else {
|
|
753
|
+
residentTiles_.clear();
|
|
754
|
+
residentTiles_.reserve(updateResult.tilesToRenderThisFrame.size());
|
|
755
|
+
for (const auto& tile : updateResult.tilesToRenderThisFrame) {
|
|
756
|
+
if (!tile) continue;
|
|
757
|
+
const auto* rc = tile->getContent().getRenderContent();
|
|
758
|
+
if (!rc) continue;
|
|
759
|
+
const auto* res = static_cast<const TileGPUResources*>(rc->getRenderResources());
|
|
760
|
+
if (!res || res->primitives.empty()) continue;
|
|
761
|
+
const CesiumGeospatial::BoundingRegion* region =
|
|
762
|
+
Cesium3DTilesSelection::getBoundingRegionFromBoundingVolume(tile->getBoundingVolume());
|
|
763
|
+
if (!region) continue;
|
|
764
|
+
const auto& rect = region->getRectangle();
|
|
765
|
+
ResidentTerrainTile entry;
|
|
766
|
+
entry.res = res;
|
|
767
|
+
entry.geometricError = tile->getGeometricError();
|
|
768
|
+
entry.west = rect.getWest();
|
|
769
|
+
entry.south = rect.getSouth();
|
|
770
|
+
entry.east = rect.getEast();
|
|
771
|
+
entry.north = rect.getNorth();
|
|
772
|
+
residentTiles_.push_back(entry);
|
|
716
773
|
}
|
|
774
|
+
residentSignature_ = signature;
|
|
775
|
+
if (heightCache_.signature != residentSignature_) heightCache_.valid = false;
|
|
717
776
|
}
|
|
718
777
|
|
|
719
778
|
// ── fast path ────────────────────────────────────────────────────────
|
|
@@ -726,92 +785,9 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
726
785
|
// detect this via result.geometrySignature and use their cached slot data.
|
|
727
786
|
if (canFastPath) {
|
|
728
787
|
result.draws.reserve(cachedDraws_.size());
|
|
729
|
-
const TileGPUResources* lastScanRes = nullptr;
|
|
730
788
|
for (const auto& c : cachedDraws_) {
|
|
731
789
|
lifecycle_.stampTileUsed(const_cast<TileGPUResources*>(c.res));
|
|
732
790
|
|
|
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
791
|
const glm::dvec3 rtcCamRel = c.rtcCenter - glm::dvec3(cameraPos);
|
|
816
792
|
const glm::dmat4 translateD = glm::translate(glm::dmat4(1.0), rtcCamRel);
|
|
817
793
|
|
|
@@ -845,20 +821,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
845
821
|
result.cameraLon = paramsFP.longitude;
|
|
846
822
|
result.cameraAlt = paramsFP.altitude;
|
|
847
823
|
|
|
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
824
|
lifecycle_.advanceFrame();
|
|
863
825
|
return;
|
|
864
826
|
}
|
|
@@ -894,74 +856,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
894
856
|
static_cast<float>(rect.getSouth()),
|
|
895
857
|
static_cast<float>(rect.getEast()),
|
|
896
858
|
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
859
|
}
|
|
966
860
|
|
|
967
861
|
for (const auto& prim : res->primitives) {
|
|
@@ -1209,29 +1103,6 @@ void CesiumEngine::updateFrame(double w, double h, FrameResult& result) {
|
|
|
1209
1103
|
result.cameraLon = params.longitude;
|
|
1210
1104
|
result.cameraAlt = params.altitude;
|
|
1211
1105
|
|
|
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
1106
|
lifecycle_.advanceFrame();
|
|
1236
1107
|
}
|
|
1237
1108
|
|