react-native-cesium 1.2.6 → 1.2.8
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/android/CMakeLists.txt +24 -9
- package/android/src/main/cpp/vulkan/VulkanBackend.cpp +166 -67
- package/android/src/main/cpp/vulkan/VulkanBackend.h +60 -19
- package/android/src/main/cpp/vulkan/shaders/terrain.frag +36 -8
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +14 -1
- package/cpp/engine/CameraIntegrator.cpp +32 -10
- package/cpp/engine/CameraIntegrator.hpp +1 -1
- package/cpp/engine/CesiumEngine.cpp +487 -34
- package/cpp/engine/CesiumEngine.hpp +73 -4
- package/cpp/engine/EngineTunables.hpp +7 -0
- package/cpp/renderer/RenderTypes.hpp +13 -0
- package/ios/CesiumBridge.h +9 -3
- package/ios/CesiumBridge.mm +60 -5
- package/ios/HybridCesiumView.swift +25 -8
- package/ios/metal/MetalBackend.h +22 -2
- package/ios/metal/MetalBackend.mm +248 -93
- package/package.json +1 -1
|
@@ -27,7 +27,7 @@ layout(push_constant) uniform PC {
|
|
|
27
27
|
float translationY; // byte 112
|
|
28
28
|
float scaleX; // byte 116
|
|
29
29
|
float scaleY; // byte 120
|
|
30
|
-
float
|
|
30
|
+
float lodFade; // byte 124
|
|
31
31
|
} pc;
|
|
32
32
|
|
|
33
33
|
layout(set = 1, binding = 0) uniform sampler2D overlayTex;
|
|
@@ -56,6 +56,20 @@ vec3 hypsometricColor(float alt, float steep) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
void main() {
|
|
59
|
+
#ifdef TERRAIN_DITHER
|
|
60
|
+
// ── LOD transition dither (IGN / Jorge Jimenez) ─────────────────────────────
|
|
61
|
+
// Compiled only into the dither pipeline so the solid pipeline keeps the GPU's
|
|
62
|
+
// early-Z optimisation enabled at full speed. Interleaved Gradient Noise
|
|
63
|
+
// produces a per-pixel threshold approximating blue noise without a texture
|
|
64
|
+
// lookup; the pattern is far less perceptible than Bayer 4×4.
|
|
65
|
+
// lodFade=1 → all pass; lodFade=0 → all discard.
|
|
66
|
+
{
|
|
67
|
+
float ign = fract(52.9829189 *
|
|
68
|
+
fract(0.06711056 * gl_FragCoord.x + 0.00583715 * gl_FragCoord.y));
|
|
69
|
+
if (pc.lodFade <= ign) { discard; }
|
|
70
|
+
}
|
|
71
|
+
#endif
|
|
72
|
+
|
|
59
73
|
// ── World position (RTC reconstruction) ────────────────────────────────────
|
|
60
74
|
// wp = tile-local pos + RTC centre (both float32 at ~6.4 Mm scale).
|
|
61
75
|
// ~0.75 m ULP — far better than eyeRelPos + cameraEcef at altitude.
|
|
@@ -86,10 +100,16 @@ void main() {
|
|
|
86
100
|
vec2 texUV = fragUV * vec2(pc.scaleX, pc.scaleY)
|
|
87
101
|
+ vec2(pc.translationX, pc.translationY);
|
|
88
102
|
texUV.y = 1.0 - texUV.y;
|
|
103
|
+
// Return the overlay RGBA directly — preserves any transparency at tile
|
|
104
|
+
// edges and avoids incorrectly stamping the coastline outline on top of
|
|
105
|
+
// satellite imagery. The dither discard above fires before this return,
|
|
106
|
+
// so LOD fading still works correctly on overlay tiles.
|
|
89
107
|
outColor = texture(overlayTex, texUV);
|
|
90
108
|
return;
|
|
91
109
|
}
|
|
92
110
|
|
|
111
|
+
vec3 lit;
|
|
112
|
+
{
|
|
93
113
|
// ── Hypsometric path ──────────────────────────────────────────────────────
|
|
94
114
|
vec3 dpdx_v = dFdx(localPos), dpdy_v = dFdy(localPos);
|
|
95
115
|
vec3 rawN = cross(dpdx_v, dpdy_v);
|
|
@@ -115,24 +135,32 @@ void main() {
|
|
|
115
135
|
vec3 oceanBlue = vec3(0.04, 0.26, 0.52);
|
|
116
136
|
float landAlt = (pc.hasWaterMask != 0u) ? max(rawAlt, 0.0) : rawAlt;
|
|
117
137
|
vec3 landLit = hypsometricColor(landAlt, steep) * (amb + diff * 0.72 + rim);
|
|
118
|
-
|
|
138
|
+
lit = mix(landLit, oceanBlue, waterVal);
|
|
119
139
|
|
|
120
140
|
// 1 km lat/lon grid — fades when sub-pixel to suppress Moiré.
|
|
121
141
|
const float gridLat = 1000.0 / 6371000.0;
|
|
122
|
-
|
|
142
|
+
// cosLat from pxy (already computed above for the Bowring iteration) —
|
|
143
|
+
// avoids a second cos() transcendental. pxy/a_e is the geocentric cosine;
|
|
144
|
+
// the ~0.2 % geodetic difference is imperceptible at grid-line scale.
|
|
145
|
+
float cosLat = max(pxy / 6378137.0, 0.001);
|
|
123
146
|
float gridLon = gridLat / cosLat;
|
|
124
147
|
float wLat = fwidth(lat) * 1.5, wLon = fwidth(lon) * 1.5;
|
|
125
148
|
float gridVis = clamp(min(gridLat / max(wLat, 1e-9),
|
|
126
149
|
gridLon / max(wLon, 1e-9)), 0.0, 1.0);
|
|
127
|
-
|
|
128
|
-
|
|
150
|
+
// GLSL mod() is floor-based (always non-negative) — equivalent to the
|
|
151
|
+
// original floor dance but one instruction cleaner.
|
|
152
|
+
float latCell = mod(lat, gridLat);
|
|
153
|
+
float lonCell = mod(lon, gridLon);
|
|
129
154
|
float dLat = min(latCell, gridLat - latCell);
|
|
130
155
|
float dLon = min(lonCell, gridLon - lonCell);
|
|
131
|
-
|
|
132
|
-
|
|
156
|
+
// Linear ramp instead of smoothstep cubic — visually identical for a
|
|
157
|
+
// 1.5 px anti-aliased line, saves two multiplies per component.
|
|
158
|
+
float gridA = max(clamp(1.0 - dLat / max(wLat, 1e-9), 0.0, 1.0),
|
|
159
|
+
clamp(1.0 - dLon / max(wLon, 1e-9), 0.0, 1.0)) * gridVis;
|
|
133
160
|
lit = mix(lit, vec3(0.15, 0.15, 0.15), gridA * 0.6);
|
|
161
|
+
} // end hypsometric block
|
|
134
162
|
|
|
135
|
-
// Coastline outline from water mask.
|
|
163
|
+
// Coastline outline from water mask (hypsometric path only — overlay returns above).
|
|
136
164
|
if (pc.isEllipsoidFallback == 0u) {
|
|
137
165
|
float dWater = fwidth(waterVal);
|
|
138
166
|
float coastPx = abs(waterVal - 0.5) / max(dWater * 0.5, 0.01);
|
|
@@ -29,6 +29,10 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
|
|
|
29
29
|
private var idleProbeAccumulator = 0.0
|
|
30
30
|
private var renderLoopActive = false
|
|
31
31
|
private val renderInFlight = AtomicBoolean(false)
|
|
32
|
+
// Last System.nanoTime() when tilesLoading was > 0. Used to keep the render
|
|
33
|
+
// loop at full speed for a grace period after loading finishes — prevents the
|
|
34
|
+
// cascade of idle gaps between LOD refinement phases. 0 = not yet seen.
|
|
35
|
+
private var lastLoadingTimeNs: Long = 0L
|
|
32
36
|
|
|
33
37
|
/// Set in `surfaceDestroyed` before any teardown work begins. Render-thread
|
|
34
38
|
/// callbacks and choreographer frame ticks check this flag and bail out so
|
|
@@ -199,11 +203,20 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
|
|
|
199
203
|
return
|
|
200
204
|
}
|
|
201
205
|
|
|
202
|
-
|
|
206
|
+
// Post-loading grace period: keep the loop at full speed for 2 seconds
|
|
207
|
+
// after the last non-zero loading frame so LOD transitions complete quickly
|
|
208
|
+
// and the next refinement level is discovered without a multi-second idle gap.
|
|
209
|
+
val nowNs = System.nanoTime()
|
|
210
|
+
val inGracePeriod = lastLoadingTimeNs > 0L &&
|
|
211
|
+
(nowNs - lastLoadingTimeNs) < 2_000_000_000L
|
|
212
|
+
|
|
213
|
+
val shouldRender = b.shouldRenderNextFrame() || inGracePeriod
|
|
203
214
|
val nextImmediate: Boolean
|
|
204
215
|
if (shouldRender) {
|
|
205
216
|
idleProbeAccumulator = 0.0
|
|
206
217
|
b.renderFrame(nowSeconds)
|
|
218
|
+
// Stamp after renderFrame so the freshly-updated tile counts are used.
|
|
219
|
+
if (b.getMetricsTilesLoading() > 0) lastLoadingTimeNs = nowNs
|
|
207
220
|
nextImmediate = true
|
|
208
221
|
} else {
|
|
209
222
|
// Approximate dt for the idle probe; not used for motion.
|
|
@@ -15,6 +15,10 @@ namespace {
|
|
|
15
15
|
// isolation without linking CesiumGeospatial. Matches Ellipsoid::WGS84.
|
|
16
16
|
inline constexpr double kEarthMeanRadiusM = 6371008.7714150595;
|
|
17
17
|
|
|
18
|
+
// Hard ceiling on altitude (metres MSL). Enforced on both the demand and the
|
|
19
|
+
// running estimate so no code path can push the camera above this value.
|
|
20
|
+
inline constexpr double kMaxAltMeters = 100'000.0;
|
|
21
|
+
|
|
18
22
|
inline double shortAngleDeltaDeg(double from, double to) {
|
|
19
23
|
return std::fmod(to - from + 540.0, 360.0) - 180.0;
|
|
20
24
|
}
|
|
@@ -105,7 +109,8 @@ void CameraIntegrator::updateAlphaBeta(
|
|
|
105
109
|
s.tLastMeas = tz;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
void CameraIntegrator::extrapolate(
|
|
112
|
+
void CameraIntegrator::extrapolate(
|
|
113
|
+
AlphaBeta& s, TimePoint now, double maxRateAbs, double demand) {
|
|
109
114
|
double dt = std::chrono::duration<double>(now - s.tState).count();
|
|
110
115
|
if (dt <= 0.0) {
|
|
111
116
|
s.tState = now;
|
|
@@ -121,9 +126,14 @@ void CameraIntegrator::extrapolate(AlphaBeta& s, TimePoint now, double maxRateAb
|
|
|
121
126
|
// grace window and the decay τ scale with the EWMA of inter-arrival times
|
|
122
127
|
// so a 60 Hz worklet bleeds in ~50 ms while a 1 Hz GPS glide is left
|
|
123
128
|
// alone between fixes.
|
|
129
|
+
//
|
|
130
|
+
// silence and grace are hoisted out of the block so the demand-pull term
|
|
131
|
+
// below can reuse them without a second chrono subtraction.
|
|
132
|
+
double silence = -1.0; // negative sentinel: "no measurement seen yet"
|
|
133
|
+
double grace = 0.0;
|
|
124
134
|
if (s.tLastMeas.time_since_epoch().count() > 0) {
|
|
125
|
-
|
|
126
|
-
|
|
135
|
+
silence = std::chrono::duration<double>(now - s.tLastMeas).count();
|
|
136
|
+
grace = tunables::kVelocitySilenceGraceFactor * s.meanIntervalSec;
|
|
127
137
|
if (silence > grace) {
|
|
128
138
|
const double tau = tunables::kVelocityBleedTauFactor * s.meanIntervalSec;
|
|
129
139
|
if (tau > 1.0e-6) {
|
|
@@ -139,6 +149,16 @@ void CameraIntegrator::extrapolate(AlphaBeta& s, TimePoint now, double maxRateAb
|
|
|
139
149
|
}
|
|
140
150
|
s.value += s.velocity * dt;
|
|
141
151
|
s.tState = now;
|
|
152
|
+
|
|
153
|
+
// Demand pull — guarantees convergence to the last demanded value for
|
|
154
|
+
// one-shot API calls (e.g. setAltitude from a button tap). Active only
|
|
155
|
+
// when measurements have stopped (silence > grace); continuous gesture or
|
|
156
|
+
// sensor streams always deliver a new update before the grace window
|
|
157
|
+
// expires, so this term is never active for them.
|
|
158
|
+
if (silence > grace && tunables::kDemandPullTauSec > 1.0e-6) {
|
|
159
|
+
s.value += (1.0 - std::exp(-dt / tunables::kDemandPullTauSec))
|
|
160
|
+
* (demand - s.value);
|
|
161
|
+
}
|
|
142
162
|
}
|
|
143
163
|
|
|
144
164
|
// ── Demand setters ────────────────────────────────────────────────────────
|
|
@@ -172,6 +192,7 @@ void CameraIntegrator::setAltitude(double altitudeMeters) {
|
|
|
172
192
|
return;
|
|
173
193
|
}
|
|
174
194
|
|
|
195
|
+
altitudeMeters = std::min(altitudeMeters, kMaxAltMeters);
|
|
175
196
|
updateAlphaBeta(alt_, altitudeMeters, now,
|
|
176
197
|
tunables::kAlphaAlt, tunables::kBetaAlt, false);
|
|
177
198
|
demand_.altitude = altitudeMeters;
|
|
@@ -285,16 +306,17 @@ CameraParams CameraIntegrator::step() {
|
|
|
285
306
|
latLonCapDegSec = glm::degrees(groundCap / kEarthMeanRadiusM);
|
|
286
307
|
}
|
|
287
308
|
|
|
288
|
-
extrapolate(lat_, now, latLonCapDegSec);
|
|
289
|
-
extrapolate(lon_, now, latLonCapDegSec);
|
|
290
|
-
extrapolate(alt_, now, climbCap);
|
|
291
|
-
extrapolate(hdg_, now, yawCap);
|
|
292
|
-
extrapolate(pitch_, now, pitchCap);
|
|
293
|
-
extrapolate(roll_, now, rollCap);
|
|
294
|
-
extrapolate(vfov_, now, 0.0);
|
|
309
|
+
extrapolate(lat_, now, latLonCapDegSec, demand_.latitude);
|
|
310
|
+
extrapolate(lon_, now, latLonCapDegSec, demand_.longitude);
|
|
311
|
+
extrapolate(alt_, now, climbCap, demand_.altitude);
|
|
312
|
+
extrapolate(hdg_, now, yawCap, demand_.heading);
|
|
313
|
+
extrapolate(pitch_, now, pitchCap, demand_.pitch);
|
|
314
|
+
extrapolate(roll_, now, rollCap, demand_.roll);
|
|
315
|
+
extrapolate(vfov_, now, 0.0, demand_.verticalFov);
|
|
295
316
|
|
|
296
317
|
hdg_.value = wrap360(hdg_.value);
|
|
297
318
|
lat_.value = std::clamp(lat_.value, -90.0, 90.0);
|
|
319
|
+
alt_.value = std::min(alt_.value, kMaxAltMeters);
|
|
298
320
|
// Longitude is allowed to drift over ±180; consumers usually want the raw
|
|
299
321
|
// continuous value for short-arc maths and the camera transform doesn't
|
|
300
322
|
// care.
|
|
@@ -124,7 +124,7 @@ private:
|
|
|
124
124
|
// Advance the running estimate to `now`, clamp velocity to `maxRateAbs`
|
|
125
125
|
// (per second; 0 = uncapped). For angles, the resulting value is also
|
|
126
126
|
// normalised to its canonical range by the caller as needed.
|
|
127
|
-
static void extrapolate(AlphaBeta& s, TimePoint now, double maxRateAbs);
|
|
127
|
+
static void extrapolate(AlphaBeta& s, TimePoint now, double maxRateAbs, double demand);
|
|
128
128
|
|
|
129
129
|
mutable std::mutex mutex_;
|
|
130
130
|
|