react-native-cesium 1.2.17 → 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/android/src/main/cpp/vulkan/VulkanBackend.cpp +40 -4
- 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
package/README.md
CHANGED
|
@@ -184,7 +184,7 @@ npm run build --prefix node_modules/react-native-cesium
|
|
|
184
184
|
|
|
185
185
|
**Yarn/npm workspaces:** if `react-native-cesium` is a workspace package in your monorepo, use your tool’s workspace runner, e.g. `yarn workspace react-native-cesium run update` (exact syntax depends on your workspace layout).
|
|
186
186
|
|
|
187
|
-
- `update` checks out **Cesium Native `v0.
|
|
187
|
+
- `update` checks out **Cesium Native `v0.65.0`** into `vendor/cesium-native` under the package (next to its other files; typically ignored in app repos).
|
|
188
188
|
- `build` runs `scripts/cesium/build.mjs` (**CMake**, **vcpkg** under `vendor/vcpkg` unless **`VCPKG_ROOT`** is set, **Ninja** on macOS) and writes **`vendor/ios/CesiumNative.xcframework`** and/or **`vendor/android`** depending on **`CESIUM_BUILD_ONLY`**.
|
|
189
189
|
|
|
190
190
|
Then install pods (iOS):
|
package/android/CMakeLists.txt
CHANGED
package/android/build.gradle
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
ReactNativeCesium_kotlinVersion=2.
|
|
2
|
-
ReactNativeCesium_minSdkVersion=
|
|
3
|
-
ReactNativeCesium_targetSdkVersion=
|
|
4
|
-
ReactNativeCesium_compileSdkVersion=
|
|
1
|
+
ReactNativeCesium_kotlinVersion=2.2.0
|
|
2
|
+
ReactNativeCesium_minSdkVersion=24
|
|
3
|
+
ReactNativeCesium_targetSdkVersion=36
|
|
4
|
+
ReactNativeCesium_compileSdkVersion=37
|
|
5
5
|
ReactNativeCesium_ndkVersion=27.1.12297006
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#include "CesiumBridgeAndroid.h"
|
|
2
2
|
|
|
3
3
|
#include "engine/EngineTunables.hpp"
|
|
4
|
-
#include "engine/
|
|
4
|
+
#include "engine/TerrainFloor.hpp"
|
|
5
5
|
#include "vulkan/VulkanBackend.h"
|
|
6
6
|
|
|
7
7
|
#include <android/native_window_jni.h>
|
|
@@ -191,6 +191,7 @@ void CesiumBridgeAndroid::teleport(double lat, double lon, double alt,
|
|
|
191
191
|
p.verticalFov = vfov;
|
|
192
192
|
p.viewCorrection = integrator_->getActual().viewCorrection;
|
|
193
193
|
integrator_->teleport(p);
|
|
194
|
+
reactnativecesium::noteTerrainSlew(*integrator_, terrainLead_);
|
|
194
195
|
engine_->camera().setParams(p);
|
|
195
196
|
forceRender_.store(true, std::memory_order_release);
|
|
196
197
|
}
|
|
@@ -309,20 +310,9 @@ void CesiumBridgeAndroid::renderFrame(double nowSeconds) {
|
|
|
309
310
|
// The integrator owns its own clock — see CameraIntegrator::step().
|
|
310
311
|
// `nowSeconds` is still used below for the metrics-only dt EMA.
|
|
311
312
|
auto actual = integrator_->step();
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (minAbove > 0.0f && engine_->terrainFloorKnown()) {
|
|
316
|
-
const double geoidN =
|
|
317
|
-
reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, 0.0);
|
|
318
|
-
const double floorMsl =
|
|
319
|
-
static_cast<double>(engine_->terrainFloorEllipsoidMeters()) - geoidN;
|
|
320
|
-
const double minMsl = floorMsl + static_cast<double>(minAbove);
|
|
321
|
-
if (actual.altitude < minMsl) {
|
|
322
|
-
integrator_->clampActualAltitude(minMsl);
|
|
323
|
-
actual.altitude = minMsl;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
313
|
+
actual = reactnativecesium::applyTerrainFloor(
|
|
314
|
+
*engine_, *integrator_, actual, terrainLead_,
|
|
315
|
+
static_cast<double>(engine_->getConfig().minAltitudeAboveTerrain));
|
|
326
316
|
|
|
327
317
|
engine_->camera().setParams(actual);
|
|
328
318
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#include "engine/CameraIntegrator.hpp"
|
|
9
9
|
#include "engine/CesiumEngine.hpp"
|
|
10
10
|
#include "engine/MetricsAggregator.hpp"
|
|
11
|
+
#include "engine/TerrainFloor.hpp"
|
|
11
12
|
|
|
12
13
|
struct ANativeWindow;
|
|
13
14
|
|
|
@@ -144,5 +145,6 @@ private:
|
|
|
144
145
|
|
|
145
146
|
reactnativecesium::EngineConfig config_;
|
|
146
147
|
reactnativecesium::MetricsAggregator metrics_;
|
|
148
|
+
reactnativecesium::TerrainLeadState terrainLead_{};
|
|
147
149
|
double lastTickSeconds_ = 0.0;
|
|
148
150
|
};
|
|
@@ -607,6 +607,32 @@ void VulkanBackend::createSwapchain() {
|
|
|
607
607
|
VkSwapchainCreateInfoKHR swapInfo{};
|
|
608
608
|
swapInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
609
609
|
swapInfo.surface = surface_;
|
|
610
|
+
// ── Surface rotation (Android pre-rotation) ──────────────────────────────
|
|
611
|
+
// We deliberately let the Android Compositor handle device rotation instead
|
|
612
|
+
// of pre-rotating the scene ourselves: present with an IDENTITY transform so
|
|
613
|
+
// the presentation engine rotates the rendered image to the current device
|
|
614
|
+
// orientation before scan-out.
|
|
615
|
+
//
|
|
616
|
+
// The alternative — preTransform = caps.currentTransform — only renders
|
|
617
|
+
// correctly if the app ALSO swaps the swapchain extent for 90/270 rotations
|
|
618
|
+
// AND bakes a matching rotation into every MVP/VP matrix. Doing neither (as
|
|
619
|
+
// before) made the scene render sideways once the device left its identity
|
|
620
|
+
// orientation: in landscape Android reports ROTATE_90/270, the compositor
|
|
621
|
+
// was told "already handled", and the un-rotated terrain appeared rotated
|
|
622
|
+
// 90° (ground stuck on the side of the screen). Since the swapchain image is
|
|
623
|
+
// the engine's only render target and modern Android display processors
|
|
624
|
+
// rotate essentially for free, the compositor path is both correct and cheap.
|
|
625
|
+
//
|
|
626
|
+
// Consequence: while the device is rotated, preTransform != currentTransform,
|
|
627
|
+
// so vkAcquireNextImageKHR / vkQueuePresentKHR return VK_SUBOPTIMAL_KHR every
|
|
628
|
+
// frame. That is expected here and must NOT trigger swapchain recreation (see
|
|
629
|
+
// beginFrame() / endFrame()); genuine size/orientation changes are driven by
|
|
630
|
+
// surfaceChanged() -> resize() instead.
|
|
631
|
+
VkSurfaceTransformFlagBitsKHR preTransform = caps.currentTransform;
|
|
632
|
+
if (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
|
|
633
|
+
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
|
634
|
+
}
|
|
635
|
+
|
|
610
636
|
swapInfo.minImageCount = imageCount;
|
|
611
637
|
swapInfo.imageFormat = swapchainFormat_;
|
|
612
638
|
swapInfo.imageColorSpace = colorSpace;
|
|
@@ -614,7 +640,7 @@ void VulkanBackend::createSwapchain() {
|
|
|
614
640
|
swapInfo.imageArrayLayers = 1;
|
|
615
641
|
swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
616
642
|
swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
617
|
-
swapInfo.preTransform =
|
|
643
|
+
swapInfo.preTransform = preTransform;
|
|
618
644
|
swapInfo.compositeAlpha = compositeAlpha;
|
|
619
645
|
swapInfo.presentMode = pickPresentMode();
|
|
620
646
|
swapInfo.clipped = VK_TRUE;
|
|
@@ -2076,13 +2102,18 @@ void VulkanBackend::beginFrame(const FrameParams& /*params*/) {
|
|
|
2076
2102
|
frameBegan_ = false;
|
|
2077
2103
|
return;
|
|
2078
2104
|
}
|
|
2079
|
-
|
|
2105
|
+
// Only OUT_OF_DATE forces a recreate. VK_SUBOPTIMAL_KHR is expected on every
|
|
2106
|
+
// frame while the device is rotated: we present with an IDENTITY preTransform
|
|
2107
|
+
// and let the compositor rotate (see createSwapchain). The acquired image is
|
|
2108
|
+
// still valid, so we proceed and use it. Genuine size/orientation changes
|
|
2109
|
+
// arrive via surfaceChanged() -> resize() -> needsSwapchainRecreate_.
|
|
2110
|
+
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
|
2080
2111
|
recreateSwapchain();
|
|
2081
2112
|
result = vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX,
|
|
2082
2113
|
imageAvailableSemaphores_[frameIndex_],
|
|
2083
2114
|
VK_NULL_HANDLE, &imageIndex_);
|
|
2084
2115
|
}
|
|
2085
|
-
if (result != VK_SUCCESS) {
|
|
2116
|
+
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
|
2086
2117
|
frameBegan_ = false;
|
|
2087
2118
|
return;
|
|
2088
2119
|
}
|
|
@@ -2355,7 +2386,12 @@ void VulkanBackend::endFrame() {
|
|
|
2355
2386
|
presentInfo.pImageIndices = &imageIndex_;
|
|
2356
2387
|
|
|
2357
2388
|
VkResult result = vkQueuePresentKHR(presentQueue_, &presentInfo);
|
|
2358
|
-
|
|
2389
|
+
// VK_SUBOPTIMAL_KHR is expected every frame while the device is rotated (we
|
|
2390
|
+
// present an IDENTITY preTransform and let the compositor rotate — see
|
|
2391
|
+
// createSwapchain), so it must NOT trigger a recreate or we'd thrash the
|
|
2392
|
+
// swapchain every frame. Real changes come through surfaceChanged() ->
|
|
2393
|
+
// resize() -> needsSwapchainRecreate_.
|
|
2394
|
+
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
|
2359
2395
|
needsSwapchainRecreate_ = true;
|
|
2360
2396
|
}
|
|
2361
2397
|
|
|
@@ -110,7 +110,7 @@ void CameraIntegrator::updateAlphaBeta(
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
void CameraIntegrator::extrapolate(
|
|
113
|
-
AlphaBeta& s, TimePoint now, double maxRateAbs, double demand) {
|
|
113
|
+
AlphaBeta& s, TimePoint now, double maxRateAbs, double demand, bool wrapAngle) {
|
|
114
114
|
double dt = std::chrono::duration<double>(now - s.tState).count();
|
|
115
115
|
if (dt <= 0.0) {
|
|
116
116
|
s.tState = now;
|
|
@@ -166,8 +166,9 @@ void CameraIntegrator::extrapolate(
|
|
|
166
166
|
// sensor streams always deliver a new update before the grace window
|
|
167
167
|
// expires, so this term is never active for them.
|
|
168
168
|
if (measurementsStopped && tunables::kDemandPullTauSec > 1.0e-6) {
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
const double delta = wrapAngle ? shortAngleDeltaDeg(s.value, demand)
|
|
170
|
+
: (demand - s.value);
|
|
171
|
+
s.value += (1.0 - std::exp(-dt / tunables::kDemandPullTauSec)) * delta;
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
|
|
@@ -203,21 +204,11 @@ void CameraIntegrator::setAltitude(double altitudeMeters) {
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
altitudeMeters = std::min(altitudeMeters, kMaxAltMeters);
|
|
206
|
-
|
|
207
|
-
// Adaptive alpha based on altitude error magnitude:
|
|
208
|
-
// - Large changes (pinch gesture, cliffs) → fast (α ≈ 0.7)
|
|
209
|
-
// - Small changes (gentle terrain) → smooth (α ≈ 0.3)
|
|
210
|
-
const double error = std::abs(altitudeMeters - alt_.value);
|
|
211
|
-
constexpr double kMinAlpha = 0.3; // Smooth for small changes
|
|
212
|
-
constexpr double kMaxAlpha = 0.7; // Fast for large changes
|
|
213
|
-
constexpr double kErrorScale = 20.0; // 20m error → max alpha
|
|
214
|
-
|
|
215
|
-
const double adaptiveAlpha = kMinAlpha + (kMaxAlpha - kMinAlpha) *
|
|
216
|
-
std::min(error / kErrorScale, 1.0);
|
|
217
|
-
|
|
207
|
+
|
|
218
208
|
updateAlphaBeta(alt_, altitudeMeters, now,
|
|
219
|
-
|
|
209
|
+
tunables::kAlphaAlt, tunables::kBetaAlt, false);
|
|
220
210
|
demand_.altitude = altitudeMeters;
|
|
211
|
+
enforceTerrainFloor();
|
|
221
212
|
}
|
|
222
213
|
|
|
223
214
|
void CameraIntegrator::setHeading(double headingDeg) {
|
|
@@ -261,13 +252,8 @@ void CameraIntegrator::setViewCorrection(const glm::dquat& q) {
|
|
|
261
252
|
}
|
|
262
253
|
|
|
263
254
|
void CameraIntegrator::setVerticalFov(double degrees) {
|
|
264
|
-
const auto now = Clock::now();
|
|
265
255
|
std::lock_guard<std::mutex> g(mutex_);
|
|
266
|
-
|
|
267
|
-
const double residual = degrees - vfov_.value;
|
|
268
|
-
vfov_.value += tunables::kAlphaVfov * residual;
|
|
269
|
-
vfov_.velocity = 0.0;
|
|
270
|
-
vfov_.tState = now;
|
|
256
|
+
demand_.verticalFov = degrees;
|
|
271
257
|
}
|
|
272
258
|
|
|
273
259
|
void CameraIntegrator::teleport(const CameraParams& target) {
|
|
@@ -305,6 +291,12 @@ void CameraIntegrator::teleport(const CameraParams& target) {
|
|
|
305
291
|
if (demand_.verticalFov <= 0.0) {
|
|
306
292
|
demand_.verticalFov = vfov_.value;
|
|
307
293
|
}
|
|
294
|
+
|
|
295
|
+
// A slew lands on the requested altitude. The next rendered frame samples
|
|
296
|
+
// the terrain at the new position and establishes a new floor.
|
|
297
|
+
terrainFloorActive_ = false;
|
|
298
|
+
terrainFloorMsl_ = 0.0;
|
|
299
|
+
lastStep_ = now;
|
|
308
300
|
}
|
|
309
301
|
|
|
310
302
|
CameraParams CameraIntegrator::step() {
|
|
@@ -328,13 +320,27 @@ CameraParams CameraIntegrator::step() {
|
|
|
328
320
|
latLonCapDegSec = glm::degrees(groundCap / kEarthMeanRadiusM);
|
|
329
321
|
}
|
|
330
322
|
|
|
331
|
-
extrapolate(lat_, now, latLonCapDegSec, demand_.latitude);
|
|
332
|
-
extrapolate(lon_, now, latLonCapDegSec, demand_.longitude);
|
|
333
|
-
extrapolate(alt_, now, climbCap, demand_.altitude);
|
|
334
|
-
extrapolate(hdg_, now, yawCap, demand_.heading);
|
|
335
|
-
extrapolate(pitch_, now, pitchCap, demand_.pitch);
|
|
336
|
-
extrapolate(roll_, now, rollCap, demand_.roll);
|
|
337
|
-
|
|
323
|
+
extrapolate(lat_, now, latLonCapDegSec, demand_.latitude, false);
|
|
324
|
+
extrapolate(lon_, now, latLonCapDegSec, demand_.longitude, true);
|
|
325
|
+
extrapolate(alt_, now, climbCap, demand_.altitude, false);
|
|
326
|
+
extrapolate(hdg_, now, yawCap, demand_.heading, true);
|
|
327
|
+
extrapolate(pitch_, now, pitchCap, demand_.pitch, true);
|
|
328
|
+
extrapolate(roll_, now, rollCap, demand_.roll, true);
|
|
329
|
+
|
|
330
|
+
double frameDt = 0.0;
|
|
331
|
+
if (lastStep_.time_since_epoch().count() > 0) {
|
|
332
|
+
frameDt = std::chrono::duration<double>(now - lastStep_).count();
|
|
333
|
+
if (frameDt < 0.0) frameDt = 0.0;
|
|
334
|
+
if (frameDt > tunables::kMaxFrameDtSec) frameDt = tunables::kMaxFrameDtSec;
|
|
335
|
+
}
|
|
336
|
+
lastStep_ = now;
|
|
337
|
+
|
|
338
|
+
if (frameDt > 0.0 && tunables::kVfovTauSec > 1.0e-6) {
|
|
339
|
+
const double blend = 1.0 - std::exp(-frameDt / tunables::kVfovTauSec);
|
|
340
|
+
vfov_.value += blend * (demand_.verticalFov - vfov_.value);
|
|
341
|
+
vfov_.velocity = 0.0;
|
|
342
|
+
vfov_.tState = now;
|
|
343
|
+
}
|
|
338
344
|
|
|
339
345
|
hdg_.value = wrap360(hdg_.value);
|
|
340
346
|
lat_.value = std::clamp(lat_.value, -90.0, 90.0);
|
|
@@ -343,12 +349,19 @@ CameraParams CameraIntegrator::step() {
|
|
|
343
349
|
// continuous value for short-arc maths and the camera transform doesn't
|
|
344
350
|
// care.
|
|
345
351
|
|
|
346
|
-
// viewCorrection: SLERP toward the latest target.
|
|
352
|
+
// viewCorrection: time-based SLERP toward the latest target.
|
|
347
353
|
glm::dquat cur = glm::normalize(viewCorrActual_);
|
|
348
354
|
glm::dquat tgt = glm::normalize(viewCorrTarget_);
|
|
349
355
|
if (glm::dot(cur, tgt) < 0.0) tgt = -tgt;
|
|
350
|
-
|
|
351
|
-
|
|
356
|
+
double slerpT = 1.0;
|
|
357
|
+
if (tunables::kViewCorrTauSec > 1.0e-6) {
|
|
358
|
+
slerpT = (frameDt > 0.0)
|
|
359
|
+
? (1.0 - std::exp(-frameDt / tunables::kViewCorrTauSec))
|
|
360
|
+
: 0.0;
|
|
361
|
+
}
|
|
362
|
+
viewCorrActual_ = glm::normalize(glm::slerp(cur, tgt, slerpT));
|
|
363
|
+
|
|
364
|
+
enforceTerrainFloor();
|
|
352
365
|
|
|
353
366
|
CameraParams out;
|
|
354
367
|
out.latitude = lat_.value;
|
|
@@ -362,23 +375,40 @@ CameraParams CameraIntegrator::step() {
|
|
|
362
375
|
return out;
|
|
363
376
|
}
|
|
364
377
|
|
|
365
|
-
void CameraIntegrator::
|
|
366
|
-
const auto now = Clock::now();
|
|
378
|
+
void CameraIntegrator::setTerrainFloor(double floorMsl) {
|
|
367
379
|
std::lock_guard<std::mutex> g(mutex_);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
380
|
+
if (!std::isfinite(floorMsl)) return;
|
|
381
|
+
terrainFloorActive_ = true;
|
|
382
|
+
terrainFloorMsl_ = std::min(floorMsl, kMaxAltMeters);
|
|
383
|
+
enforceTerrainFloor();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
void CameraIntegrator::clearTerrainFloor() {
|
|
387
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
388
|
+
terrainFloorActive_ = false;
|
|
389
|
+
terrainFloorMsl_ = 0.0;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
void CameraIntegrator::clampActualAltitude(double newAltMsl) {
|
|
393
|
+
setTerrainFloor(newAltMsl);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
void CameraIntegrator::enforceTerrainFloor() {
|
|
397
|
+
if (!terrainFloorActive_) return;
|
|
398
|
+
|
|
399
|
+
const bool inContact =
|
|
400
|
+
demand_.altitude <= terrainFloorMsl_ + tunables::kEpsAltitudeMeters;
|
|
401
|
+
if (inContact) {
|
|
402
|
+
// Contour contact: the floor sample owns altitude, both directions.
|
|
403
|
+
// Residual vertical velocity would carry the camera into the next face.
|
|
404
|
+
alt_.value = terrainFloorMsl_;
|
|
377
405
|
alt_.velocity = 0.0;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (alt_.value < terrainFloorMsl_) {
|
|
409
|
+
alt_.value = terrainFloorMsl_;
|
|
410
|
+
if (alt_.velocity < 0.0) alt_.velocity = 0.0;
|
|
378
411
|
}
|
|
379
|
-
|
|
380
|
-
// Do not touch demand_.altitude — getDemand() should still report what the
|
|
381
|
-
// user asked for.
|
|
382
412
|
}
|
|
383
413
|
|
|
384
414
|
void CameraIntegrator::setRateCaps(const CameraRateCaps& caps) {
|
|
@@ -410,7 +440,12 @@ bool CameraIntegrator::isActive() const {
|
|
|
410
440
|
if (std::abs(demand_.latitude - lat_.value) > tunables::kEpsLatLon) return true;
|
|
411
441
|
if (std::abs(shortAngleDeltaDeg(lon_.value, demand_.longitude))
|
|
412
442
|
> tunables::kEpsLatLon) return true;
|
|
413
|
-
if (
|
|
443
|
+
if (terrainFloorActive_ &&
|
|
444
|
+
demand_.altitude <= terrainFloorMsl_ + tunables::kEpsAltitudeMeters) {
|
|
445
|
+
if (std::abs(alt_.value - terrainFloorMsl_) > tunables::kEpsAltitudeMeters) return true;
|
|
446
|
+
} else if (std::abs(demand_.altitude - alt_.value) > tunables::kEpsAltitudeMeters) {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
414
449
|
if (angleAbsDeltaDeg(demand_.heading, hdg_.value) > tunables::kEpsAngleDeg) return true;
|
|
415
450
|
if (angleAbsDeltaDeg(demand_.pitch, pitch_.value) > tunables::kEpsAngleDeg) return true;
|
|
416
451
|
if (angleAbsDeltaDeg(demand_.roll, roll_.value) > tunables::kEpsAngleDeg) return true;
|
|
@@ -73,11 +73,18 @@ public:
|
|
|
73
73
|
// values and an instant runaway.
|
|
74
74
|
CameraParams step();
|
|
75
75
|
|
|
76
|
-
// Terrain
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
76
|
+
// Terrain floor in metres MSL. While active, the rendered altitude is never
|
|
77
|
+
// below this value. When the demand altitude is at or below the floor the
|
|
78
|
+
// camera rides it exactly (uphill and downhill), so a later setAltitude
|
|
79
|
+
// under the surface cannot pull the rendered camera back inside. Demand
|
|
80
|
+
// itself is left untouched.
|
|
81
|
+
void setTerrainFloor(double floorMsl);
|
|
82
|
+
|
|
83
|
+
// Drops the floor so the next slew or the next frame can establish a new
|
|
84
|
+
// one. Used by teleport().
|
|
85
|
+
void clearTerrainFloor();
|
|
86
|
+
|
|
87
|
+
// Sets the terrain floor to `newAltMsl`. Same as setTerrainFloor.
|
|
81
88
|
void clampActualAltitude(double newAltMsl);
|
|
82
89
|
|
|
83
90
|
// Runtime override of the rate caps (driven by view props). 0 = uncapped
|
|
@@ -122,9 +129,14 @@ private:
|
|
|
122
129
|
double alpha, double beta, bool wrapAngle);
|
|
123
130
|
|
|
124
131
|
// Advance the running estimate to `now`, clamp velocity to `maxRateAbs`
|
|
125
|
-
// (per second; 0 = uncapped).
|
|
126
|
-
//
|
|
127
|
-
static void extrapolate(AlphaBeta& s, TimePoint now, double maxRateAbs,
|
|
132
|
+
// (per second; 0 = uncapped). `wrapAngle` makes the silence demand-pull
|
|
133
|
+
// take the short arc (longitude, heading, pitch, roll).
|
|
134
|
+
static void extrapolate(AlphaBeta& s, TimePoint now, double maxRateAbs,
|
|
135
|
+
double demand, bool wrapAngle);
|
|
136
|
+
|
|
137
|
+
// Snap the rendered altitude onto the floor when the camera is in contact
|
|
138
|
+
// or currently below it. Demand is not modified.
|
|
139
|
+
void enforceTerrainFloor();
|
|
128
140
|
|
|
129
141
|
mutable std::mutex mutex_;
|
|
130
142
|
|
|
@@ -141,6 +153,13 @@ private:
|
|
|
141
153
|
|
|
142
154
|
// Runtime overrides; 0 = use compile-time defaults from EngineTunables.
|
|
143
155
|
CameraRateCaps caps_{};
|
|
156
|
+
|
|
157
|
+
// Last step() timestamp. Drives the dt-based fov blend and view-correction
|
|
158
|
+
// slerp. Epoch means "no step yet".
|
|
159
|
+
TimePoint lastStep_{};
|
|
160
|
+
|
|
161
|
+
bool terrainFloorActive_ = false;
|
|
162
|
+
double terrainFloorMsl_ = 0.0;
|
|
144
163
|
};
|
|
145
164
|
|
|
146
165
|
} // namespace reactnativecesium
|