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
|
@@ -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,15 +145,20 @@ 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();
|
|
153
158
|
void destroyTileset();
|
|
159
|
+
// Wait until worker tasks and the main-thread continuations they post have
|
|
160
|
+
// finished. Safe to call with or without a live tileset.
|
|
161
|
+
void drainAsyncWork();
|
|
154
162
|
|
|
155
163
|
// Apply runtime-mutable bits of `cfg` to the live tileset. Returns true if a
|
|
156
164
|
// value actually changed (callers can use this to trigger force-render).
|
|
@@ -185,11 +193,6 @@ private:
|
|
|
185
193
|
GlobeCamera camera_;
|
|
186
194
|
TileLifecycleManager lifecycle_;
|
|
187
195
|
|
|
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
196
|
// Wall-clock timestamp of the previous updateFrame call. Used to compute
|
|
194
197
|
// deltaTime for Tileset::updateViewGroup, which drives LOD transition fading.
|
|
195
198
|
// Zero-initialised "not yet set" state — first frame passes 0 deltaTime.
|
|
@@ -224,22 +227,32 @@ private:
|
|
|
224
227
|
// is guaranteed to contain the right bytes — fast path becomes legal.
|
|
225
228
|
int stableSigFrames_ = 0;
|
|
226
229
|
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
230
|
+
// Tiles drawn by the last updateFrame. The next frame's terrain query
|
|
231
|
+
// reads this list before updateViewGroup can free the previous selection.
|
|
232
|
+
struct ResidentTerrainTile {
|
|
233
|
+
const TileGPUResources* res = nullptr;
|
|
234
|
+
double geometricError = 0.0;
|
|
235
|
+
double west = 0.0;
|
|
236
|
+
double south = 0.0;
|
|
237
|
+
double east = 0.0;
|
|
238
|
+
double north = 0.0;
|
|
239
|
+
};
|
|
240
|
+
std::vector<ResidentTerrainTile> residentTiles_;
|
|
241
|
+
uint64_t residentSignature_ = 0;
|
|
242
|
+
|
|
243
|
+
// One-entry hover cache. Keyed by resident signature, finest-tile identity,
|
|
244
|
+
// and a sub-metre lat/lon cell. A miss scans that one tile's triangles.
|
|
245
|
+
struct TerrainHeightCache {
|
|
246
|
+
bool valid = false;
|
|
247
|
+
const TileGPUResources* tile = nullptr;
|
|
248
|
+
int latCell = 0;
|
|
249
|
+
int lonCell = 0;
|
|
250
|
+
uint64_t signature = 0;
|
|
251
|
+
float height = 0.0f;
|
|
252
|
+
};
|
|
253
|
+
TerrainHeightCache heightCache_;
|
|
254
|
+
|
|
255
|
+
void clearResidentTerrain();
|
|
243
256
|
};
|
|
244
257
|
|
|
245
258
|
} // 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).
|
|
@@ -3,34 +3,55 @@
|
|
|
3
3
|
#include "../third_party/egm96/EGM96.h"
|
|
4
4
|
|
|
5
5
|
#include <cmath>
|
|
6
|
+
#include <limits>
|
|
6
7
|
|
|
7
8
|
namespace reactnativecesium {
|
|
8
9
|
|
|
9
10
|
namespace {
|
|
10
11
|
|
|
11
12
|
double normalizeLongitudeDegrees(double longitudeDeg) {
|
|
12
|
-
if (!std::isfinite(longitudeDeg)) return 0.0;
|
|
13
13
|
double wrapped = std::fmod(longitudeDeg + 180.0, 360.0);
|
|
14
14
|
if (wrapped < 0.0) wrapped += 360.0;
|
|
15
15
|
return wrapped - 180.0;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// Geoid undulation N, where HAE = MSL + N. NaN when the position or the
|
|
19
|
+
// model result is non-finite; callers then return the input height unchanged.
|
|
20
|
+
double geoidUndulationMeters(double latitudeDeg, double longitudeDeg) {
|
|
21
|
+
if (!std::isfinite(latitudeDeg) || !std::isfinite(longitudeDeg)) {
|
|
22
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const double undulation = egm96_compute_altitude_offset(
|
|
26
|
+
latitudeDeg, normalizeLongitudeDegrees(longitudeDeg));
|
|
27
|
+
if (!std::isfinite(undulation)) {
|
|
28
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
29
|
+
}
|
|
30
|
+
return undulation;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
double applyUndulation(double heightMeters,
|
|
34
|
+
double latitudeDeg,
|
|
35
|
+
double longitudeDeg,
|
|
36
|
+
double sign) {
|
|
37
|
+
if (!std::isfinite(heightMeters)) return heightMeters;
|
|
38
|
+
const double undulation = geoidUndulationMeters(latitudeDeg, longitudeDeg);
|
|
39
|
+
if (!std::isfinite(undulation)) return heightMeters;
|
|
40
|
+
return heightMeters + sign * undulation;
|
|
41
|
+
}
|
|
42
|
+
|
|
18
43
|
} // namespace
|
|
19
44
|
|
|
20
45
|
double mslToEllipsoidMeters(double latitudeDeg,
|
|
21
46
|
double longitudeDeg,
|
|
22
47
|
double mslMeters) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return mslMeters;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const double normalizedLon = normalizeLongitudeDegrees(longitudeDeg);
|
|
29
|
-
const double geoidOffset =
|
|
30
|
-
egm96_compute_altitude_offset(latitudeDeg, normalizedLon);
|
|
48
|
+
return applyUndulation(mslMeters, latitudeDeg, longitudeDeg, 1.0);
|
|
49
|
+
}
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
|
|
51
|
+
double ellipsoidToMslMeters(double latitudeDeg,
|
|
52
|
+
double longitudeDeg,
|
|
53
|
+
double ellipsoidMeters) {
|
|
54
|
+
return applyUndulation(ellipsoidMeters, latitudeDeg, longitudeDeg, -1.0);
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
} // namespace reactnativecesium
|
|
@@ -7,4 +7,45 @@ double mslToEllipsoidMeters(double latitudeDeg,
|
|
|
7
7
|
double longitudeDeg,
|
|
8
8
|
double mslMeters);
|
|
9
9
|
|
|
10
|
+
/// Converts WGS84 ellipsoidal height (HAE) to orthometric height (MSL).
|
|
11
|
+
double ellipsoidToMslMeters(double latitudeDeg,
|
|
12
|
+
double longitudeDeg,
|
|
13
|
+
double ellipsoidMeters);
|
|
14
|
+
|
|
15
|
+
/// `ellipsoidHeight` selects HAE→MSL. Otherwise `altitudeMeters` is already MSL.
|
|
16
|
+
inline double demandAltitudeToMslMeters(double latitudeDeg,
|
|
17
|
+
double longitudeDeg,
|
|
18
|
+
double altitudeMeters,
|
|
19
|
+
bool ellipsoidHeight) {
|
|
20
|
+
if (!ellipsoidHeight) return altitudeMeters;
|
|
21
|
+
return ellipsoidToMslMeters(latitudeDeg, longitudeDeg, altitudeMeters);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// `ellipsoidHeight` selects MSL→HAE. Otherwise `mslMeters` is already the
|
|
25
|
+
/// demand datum. Inverse of `demandAltitudeToMslMeters`.
|
|
26
|
+
inline double mslToDemandAltitudeMeters(double latitudeDeg,
|
|
27
|
+
double longitudeDeg,
|
|
28
|
+
double mslMeters,
|
|
29
|
+
bool ellipsoidHeight) {
|
|
30
|
+
if (!ellipsoidHeight) return mslMeters;
|
|
31
|
+
return mslToEllipsoidMeters(latitudeDeg, longitudeDeg, mslMeters);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Mean-sea-level change below this is left alone when a horizontal move
|
|
35
|
+
/// re-expresses an ellipsoidal height. One centimetre.
|
|
36
|
+
inline constexpr double kHorizontalMoveMslEpsilonMeters = 0.01;
|
|
37
|
+
|
|
38
|
+
/// Keeps the same ellipsoidal height when the horizontal position changes.
|
|
39
|
+
/// `oldMslMeters` is mean sea level at the old point; the result is mean sea
|
|
40
|
+
/// level at the new point.
|
|
41
|
+
inline double mslAfterHorizontalMove(double oldLatitudeDeg,
|
|
42
|
+
double oldLongitudeDeg,
|
|
43
|
+
double oldMslMeters,
|
|
44
|
+
double newLatitudeDeg,
|
|
45
|
+
double newLongitudeDeg) {
|
|
46
|
+
const double hae =
|
|
47
|
+
mslToEllipsoidMeters(oldLatitudeDeg, oldLongitudeDeg, oldMslMeters);
|
|
48
|
+
return ellipsoidToMslMeters(newLatitudeDeg, newLongitudeDeg, hae);
|
|
49
|
+
}
|
|
50
|
+
|
|
10
51
|
} // namespace reactnativecesium
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
#include <cmath>
|
|
12
12
|
#include <cstring>
|
|
13
|
+
#include <optional>
|
|
13
14
|
|
|
14
15
|
namespace reactnativecesium {
|
|
15
16
|
|
|
@@ -64,12 +65,17 @@ GltfToMesh::convert(const CesiumGltf::Model& model,
|
|
|
64
65
|
|
|
65
66
|
const CesiumGltf::PositionAccessorType posAccessor =
|
|
66
67
|
CesiumGltf::getPositionAccessorView(gltf, primitive);
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
const CesiumGltf::AccessorViewStatus posStatus =
|
|
69
|
+
std::visit(CesiumGltf::StatusFromAccessor{}, posAccessor);
|
|
70
|
+
if (posStatus != CesiumGltf::AccessorViewStatus::Valid)
|
|
71
|
+
return;
|
|
72
|
+
|
|
73
|
+
const int64_t vertexCount =
|
|
74
|
+
std::visit(CesiumGltf::CountFromAccessor{}, posAccessor);
|
|
75
|
+
if (vertexCount <= 0)
|
|
69
76
|
return;
|
|
70
77
|
|
|
71
78
|
const glm::dmat4 worldTransform = rootTransform * nodeTransform;
|
|
72
|
-
const int64_t vertexCount = posAccessor.size();
|
|
73
79
|
|
|
74
80
|
// RTC centre = translation column of the world transform.
|
|
75
81
|
// After applyRtcCenter this is the tile's ECEF centre embedded by
|
|
@@ -82,8 +88,11 @@ GltfToMesh::convert(const CesiumGltf::Model& model,
|
|
|
82
88
|
prim.altitudes.reserve(static_cast<size_t>(vertexCount));
|
|
83
89
|
|
|
84
90
|
for (int64_t i = 0; i < vertexCount; ++i) {
|
|
85
|
-
const
|
|
86
|
-
|
|
91
|
+
const std::optional<glm::dvec3> pos = std::visit(
|
|
92
|
+
CesiumGltf::PositionFromAccessor{i}, posAccessor);
|
|
93
|
+
if (!pos)
|
|
94
|
+
continue;
|
|
95
|
+
const glm::dvec3 gltfLocal = *pos;
|
|
87
96
|
// Absolute ECEF in double precision.
|
|
88
97
|
const glm::dvec3 ecef(worldTransform * glm::dvec4(gltfLocal, 1.0));
|
|
89
98
|
// Tile-local position (small float, retains full float32 precision
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#include "ResourcePreparer.hpp"
|
|
2
2
|
|
|
3
3
|
#include <Cesium3DTilesSelection/Tile.h>
|
|
4
|
-
#include <
|
|
4
|
+
#include <CesiumImage/ImageAsset.h>
|
|
5
5
|
#include <CesiumGltf/Model.h>
|
|
6
6
|
#include <CesiumUtility/JsonValue.h>
|
|
7
7
|
|
|
@@ -117,12 +117,12 @@ void ResourcePreparer::free(Cesium3DTilesSelection::Tile& /*tile*/,
|
|
|
117
117
|
|
|
118
118
|
// ── Raster overlay pipeline ─────────────────────────────────────────────────
|
|
119
119
|
|
|
120
|
-
void* ResourcePreparer::prepareRasterInLoadThread(
|
|
120
|
+
void* ResourcePreparer::prepareRasterInLoadThread(CesiumImage::ImageAsset& image,
|
|
121
121
|
const std::any&) {
|
|
122
122
|
if (image.pixelData.empty() || image.width <= 0 || image.height <= 0) {
|
|
123
123
|
return nullptr;
|
|
124
124
|
}
|
|
125
|
-
if (image.compressedPixelFormat !=
|
|
125
|
+
if (image.compressedPixelFormat != CesiumImage::GpuCompressedPixelFormat::NONE) {
|
|
126
126
|
return nullptr;
|
|
127
127
|
}
|
|
128
128
|
if (image.bytesPerChannel != 1) return nullptr;
|
|
@@ -64,7 +64,7 @@ public:
|
|
|
64
64
|
void free(Cesium3DTilesSelection::Tile& tile, void* pLoadThreadResult,
|
|
65
65
|
void* pMainThreadResult) noexcept override;
|
|
66
66
|
|
|
67
|
-
void* prepareRasterInLoadThread(
|
|
67
|
+
void* prepareRasterInLoadThread(CesiumImage::ImageAsset& image,
|
|
68
68
|
const std::any& rendererOptions) override;
|
|
69
69
|
void* prepareRasterInMainThread(
|
|
70
70
|
CesiumRasterOverlays::RasterOverlayTile& rasterTile,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "CameraIntegrator.hpp"
|
|
4
|
+
#include "CesiumEngine.hpp"
|
|
5
|
+
#include "GeoidConverter.hpp"
|
|
6
|
+
#include "TerrainHeight.hpp"
|
|
7
|
+
|
|
8
|
+
#include <algorithm>
|
|
9
|
+
|
|
10
|
+
namespace reactnativecesium {
|
|
11
|
+
|
|
12
|
+
// Previous horizontal position and whether a real mesh sample has landed.
|
|
13
|
+
// Shared by the iOS and Android render loops so both follow the same floor.
|
|
14
|
+
struct TerrainLeadState {
|
|
15
|
+
bool hasPrev = false;
|
|
16
|
+
bool latched = false;
|
|
17
|
+
double prevLatDeg = 0.0;
|
|
18
|
+
double prevLonDeg = 0.0;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// A position slew drops the old floor and the one-frame lead. The next
|
|
22
|
+
// rendered frame samples terrain at the new position.
|
|
23
|
+
inline void noteTerrainSlew(CameraIntegrator& integrator, TerrainLeadState& lead) {
|
|
24
|
+
integrator.clearTerrainFloor();
|
|
25
|
+
lead = {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Samples the resident mesh at the stepped camera and one frame ahead, then
|
|
29
|
+
// holds the rendered altitude to surface + clearance before the frame is drawn.
|
|
30
|
+
// Demand is not modified. Returns the camera to render.
|
|
31
|
+
inline CameraParams applyTerrainFloor(CesiumEngine& engine,
|
|
32
|
+
CameraIntegrator& integrator,
|
|
33
|
+
const CameraParams& stepped,
|
|
34
|
+
TerrainLeadState& lead,
|
|
35
|
+
double clearanceMeters) {
|
|
36
|
+
const double clearance = std::max(0.0, clearanceMeters);
|
|
37
|
+
|
|
38
|
+
double leadLat = stepped.latitude;
|
|
39
|
+
double leadLon = stepped.longitude;
|
|
40
|
+
const bool sampleLead = terrainLeadLatLon(
|
|
41
|
+
stepped.latitude, stepped.longitude,
|
|
42
|
+
lead.prevLatDeg, lead.prevLonDeg, lead.hasPrev,
|
|
43
|
+
leadLat, leadLon);
|
|
44
|
+
|
|
45
|
+
float nadir = 0.0f;
|
|
46
|
+
float ahead = 0.0f;
|
|
47
|
+
const bool hasNadir =
|
|
48
|
+
engine.sampleTerrainEllipsoidMeters(stepped.latitude, stepped.longitude, nadir);
|
|
49
|
+
const bool hasAhead = sampleLead &&
|
|
50
|
+
engine.sampleTerrainEllipsoidMeters(leadLat, leadLon, ahead);
|
|
51
|
+
|
|
52
|
+
float surface = 0.0f;
|
|
53
|
+
const bool haveMesh = resolveSurfaceEllipsoid(hasNadir, nadir, hasAhead, ahead, surface);
|
|
54
|
+
const double geoidN = mslToEllipsoidMeters(stepped.latitude, stepped.longitude, 0.0);
|
|
55
|
+
if (haveMesh) {
|
|
56
|
+
integrator.setTerrainFloor(static_cast<double>(surface) - geoidN + clearance);
|
|
57
|
+
lead.latched = true;
|
|
58
|
+
} else if (!lead.latched) {
|
|
59
|
+
// No mesh yet: stay at or above the ellipsoid. Once a mesh sample has
|
|
60
|
+
// landed, a later miss keeps the last floor instead of dropping to sea level.
|
|
61
|
+
integrator.setTerrainFloor(-geoidN + clearance);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
lead.prevLatDeg = stepped.latitude;
|
|
65
|
+
lead.prevLonDeg = stepped.longitude;
|
|
66
|
+
lead.hasPrev = true;
|
|
67
|
+
return integrator.getActual();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
} // namespace reactnativecesium
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cmath>
|
|
5
|
+
|
|
6
|
+
namespace reactnativecesium {
|
|
7
|
+
|
|
8
|
+
// Pure terrain-floor helpers. No Cesium types, so the host harness can test
|
|
9
|
+
// contour rules without a tileset.
|
|
10
|
+
|
|
11
|
+
// Ignore a lead step shorter than this (hover) or longer than this (a slew,
|
|
12
|
+
// not one frame of travel).
|
|
13
|
+
inline constexpr double kTerrainLeadMinMeters = 0.05;
|
|
14
|
+
inline constexpr double kTerrainLeadMaxMeters = 80.0;
|
|
15
|
+
inline constexpr double kEarthRadiusMeters = 6371008.7714150595;
|
|
16
|
+
|
|
17
|
+
// Horizontal barycentric height. (pe, pn) and the triangle are in metres in
|
|
18
|
+
// the same local east/north frame. Near-vertical triangles (terrain skirts)
|
|
19
|
+
// have ~0 horizontal area and are rejected. On a hit, outHeight is the
|
|
20
|
+
// interpolated ellipsoid height in metres.
|
|
21
|
+
inline bool horizontalTriangleHeight(double pe, double pn,
|
|
22
|
+
double e0, double n0, float h0,
|
|
23
|
+
double e1, double n1, float h1,
|
|
24
|
+
double e2, double n2, float h2,
|
|
25
|
+
float& outHeight) {
|
|
26
|
+
const double v0e = e1 - e0;
|
|
27
|
+
const double v0n = n1 - n0;
|
|
28
|
+
const double v1e = e2 - e0;
|
|
29
|
+
const double v1n = n2 - n0;
|
|
30
|
+
const double v2e = pe - e0;
|
|
31
|
+
const double v2n = pn - n0;
|
|
32
|
+
|
|
33
|
+
const double dot00 = v0e * v0e + v0n * v0n;
|
|
34
|
+
const double dot01 = v0e * v1e + v0n * v1n;
|
|
35
|
+
const double dot11 = v1e * v1e + v1n * v1n;
|
|
36
|
+
const double dot02 = v0e * v2e + v0n * v2n;
|
|
37
|
+
const double dot12 = v1e * v2e + v1n * v2n;
|
|
38
|
+
|
|
39
|
+
// denom = (2 * signed horizontal area)^2.
|
|
40
|
+
const double denom = dot00 * dot11 - dot01 * dot01;
|
|
41
|
+
if (std::abs(denom) < 1.0e-12) return false;
|
|
42
|
+
|
|
43
|
+
const double e20 = e2 - e1;
|
|
44
|
+
const double n20 = n2 - n1;
|
|
45
|
+
const double edge2 = e20 * e20 + n20 * n20;
|
|
46
|
+
const double longest = std::sqrt(std::max(dot00, std::max(dot11, edge2)));
|
|
47
|
+
const double twiceArea = std::sqrt(std::abs(denom));
|
|
48
|
+
// Width of the triangle across its longest edge. Terrain skirts are
|
|
49
|
+
// vertical, so float error leaves them only millimetres wide.
|
|
50
|
+
if (longest < 1.0e-6 || twiceArea / longest < 0.05) return false;
|
|
51
|
+
|
|
52
|
+
const double inv = 1.0 / denom;
|
|
53
|
+
const double u = (dot11 * dot02 - dot01 * dot12) * inv;
|
|
54
|
+
const double v = (dot00 * dot12 - dot01 * dot02) * inv;
|
|
55
|
+
const double w = 1.0 - u - v;
|
|
56
|
+
|
|
57
|
+
constexpr double kEdge = -1.0e-4;
|
|
58
|
+
if (u < kEdge || v < kEdge || w < kEdge) return false;
|
|
59
|
+
|
|
60
|
+
outHeight = static_cast<float>(w * static_cast<double>(h0) +
|
|
61
|
+
u * static_cast<double>(h1) +
|
|
62
|
+
v * static_cast<double>(h2));
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Fresh each frame. The higher of the nadir sample and the one-frame lead.
|
|
67
|
+
// A lower lead (past a crest) does not keep the previous peak. A higher lead
|
|
68
|
+
// (valley turning uphill) raises the floor in this frame. No memory.
|
|
69
|
+
inline bool resolveSurfaceEllipsoid(bool hasNadir, float nadir,
|
|
70
|
+
bool hasAhead, float ahead,
|
|
71
|
+
float& outHeight) {
|
|
72
|
+
if (hasNadir && hasAhead) {
|
|
73
|
+
outHeight = std::max(nadir, ahead);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (hasNadir) {
|
|
77
|
+
outHeight = nadir;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (hasAhead) {
|
|
81
|
+
outHeight = ahead;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// One frame ahead of (latDeg, lonDeg) along the last step. Returns false when
|
|
88
|
+
// there is no distinct lead; out* are then equal to the current position.
|
|
89
|
+
inline bool terrainLeadLatLon(double latDeg, double lonDeg,
|
|
90
|
+
double prevLatDeg, double prevLonDeg,
|
|
91
|
+
bool hasPrev,
|
|
92
|
+
double& outLatDeg, double& outLonDeg) {
|
|
93
|
+
outLatDeg = latDeg;
|
|
94
|
+
outLonDeg = lonDeg;
|
|
95
|
+
if (!hasPrev) return false;
|
|
96
|
+
|
|
97
|
+
const double dLat = latDeg - prevLatDeg;
|
|
98
|
+
double dLon = lonDeg - prevLonDeg;
|
|
99
|
+
dLon = std::fmod(dLon + 540.0, 360.0) - 180.0;
|
|
100
|
+
|
|
101
|
+
const double latRad = latDeg * (3.14159265358979323846 / 180.0);
|
|
102
|
+
const double northM = dLat * (3.14159265358979323846 / 180.0) * kEarthRadiusMeters;
|
|
103
|
+
const double eastM =
|
|
104
|
+
dLon * (3.14159265358979323846 / 180.0) * kEarthRadiusMeters * std::cos(latRad);
|
|
105
|
+
const double dist = std::hypot(eastM, northM);
|
|
106
|
+
if (dist < kTerrainLeadMinMeters || dist > kTerrainLeadMaxMeters) return false;
|
|
107
|
+
|
|
108
|
+
outLatDeg = latDeg + dLat;
|
|
109
|
+
outLonDeg = lonDeg + dLon;
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
} // namespace reactnativecesium
|