react-native-cesium 1.0.1
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/LICENSE +21 -0
- package/NOTICE +12 -0
- package/README.md +308 -0
- package/ReactNativeCesium.podspec +54 -0
- package/android/CMakeLists.txt +146 -0
- package/android/build.gradle +178 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/assets/cacert.pem +3529 -0
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +487 -0
- package/android/src/main/cpp/CesiumBridgeAndroid.h +94 -0
- package/android/src/main/cpp/cpp-adapter.cpp +17 -0
- package/android/src/main/cpp/vulkan/VulkanBackend.cpp +1781 -0
- package/android/src/main/cpp/vulkan/VulkanBackend.h +190 -0
- package/android/src/main/cpp/vulkan/shaders/sky.frag +86 -0
- package/android/src/main/cpp/vulkan/shaders/sky.vert +9 -0
- package/android/src/main/cpp/vulkan/shaders/terrain.frag +144 -0
- package/android/src/main/cpp/vulkan/shaders/terrain.vert +25 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +84 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +225 -0
- package/android/src/main/java/com/reactnativecesium/ReactNativeCesiumPackage.kt +28 -0
- package/cpp/engine/CesiumEngine.cpp +413 -0
- package/cpp/engine/CesiumEngine.hpp +90 -0
- package/cpp/engine/DeletionQueue.cpp +28 -0
- package/cpp/engine/DeletionQueue.hpp +28 -0
- package/cpp/engine/GlobeCamera.cpp +181 -0
- package/cpp/engine/GlobeCamera.hpp +64 -0
- package/cpp/engine/GltfToMesh.cpp +154 -0
- package/cpp/engine/GltfToMesh.hpp +20 -0
- package/cpp/engine/ResourcePreparer.cpp +224 -0
- package/cpp/engine/ResourcePreparer.hpp +96 -0
- package/cpp/engine/TaskProcessor.cpp +61 -0
- package/cpp/engine/TaskProcessor.hpp +43 -0
- package/cpp/engine/TileLifecycleManager.cpp +30 -0
- package/cpp/engine/TileLifecycleManager.hpp +25 -0
- package/cpp/fmt/format.h +9 -0
- package/cpp/renderer/IGPUBackend.hpp +22 -0
- package/cpp/renderer/RenderTypes.hpp +112 -0
- package/cpp/renderer/ShaderTypes.hpp +29 -0
- package/example/README.md +43 -0
- package/example/react-native-cesium-example.jpg +0 -0
- package/ios/CesiumBridge.h +55 -0
- package/ios/CesiumBridge.mm +393 -0
- package/ios/HybridCesiumView.swift +299 -0
- package/ios/ReactNativeCesium-umbrella.h +16 -0
- package/ios/metal/MetalBackend.h +82 -0
- package/ios/metal/MetalBackend.mm +839 -0
- package/ios/react_native_cesium_ensure_native.rb +12 -0
- package/ios/react_native_cesium_post_install.rb +30 -0
- package/lib/commonjs/CesiumView.js +13 -0
- package/lib/commonjs/CesiumView.js.map +1 -0
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/cesium-view.nitro.js +6 -0
- package/lib/commonjs/specs/cesium-view.nitro.js.map +1 -0
- package/lib/module/CesiumView.js +9 -0
- package/lib/module/CesiumView.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/cesium-view.nitro.js +4 -0
- package/lib/module/specs/cesium-view.nitro.js.map +1 -0
- package/lib/typescript/src/CesiumView.d.ts +5 -0
- package/lib/typescript/src/CesiumView.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +43 -0
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -0
- package/nitro.json +30 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/ReactNativeCesium+autolinking.cmake +83 -0
- package/nitrogen/generated/android/ReactNativeCesium+autolinking.gradle +27 -0
- package/nitrogen/generated/android/ReactNativeCesiumOnLoad.cpp +58 -0
- package/nitrogen/generated/android/ReactNativeCesiumOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JCameraState.hpp +81 -0
- package/nitrogen/generated/android/c++/JCesiumMetrics.hpp +81 -0
- package/nitrogen/generated/android/c++/JFunc_void_CesiumMetrics.hpp +78 -0
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +178 -0
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +83 -0
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +92 -0
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CameraState.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CesiumMetrics.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Func_void_CesiumMetrics.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +126 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/ReactNativeCesiumOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/views/HybridCesiumViewManager.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/views/HybridCesiumViewStateUpdater.kt +23 -0
- package/nitrogen/generated/ios/ReactNativeCesium+autolinking.rb +60 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.cpp +57 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +164 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Umbrella.hpp +53 -0
- package/nitrogen/generated/ios/ReactNativeCesiumAutolinking.mm +33 -0
- package/nitrogen/generated/ios/ReactNativeCesiumAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +157 -0
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +167 -0
- package/nitrogen/generated/ios/swift/CameraState.swift +59 -0
- package/nitrogen/generated/ios/swift/CesiumMetrics.swift +59 -0
- package/nitrogen/generated/ios/swift/Func_void_CameraState.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CesiumMetrics.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +65 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +306 -0
- package/nitrogen/generated/shared/c++/CameraState.hpp +107 -0
- package/nitrogen/generated/shared/c++/CesiumMetrics.hpp +107 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +42 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +90 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +182 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +122 -0
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +19 -0
- package/package.json +140 -0
- package/react-native.config.js +16 -0
- package/scripts/cesium/build.mjs +901 -0
- package/scripts/cesium/clean.mjs +35 -0
- package/scripts/cesium/clone.mjs +32 -0
- package/scripts/cesium/ensure-native.mjs +116 -0
- package/scripts/cesium/vcpkg-ports/zstd/fix-emscripten-and-clang-cl.patch +31 -0
- package/scripts/cesium/vcpkg-ports/zstd/fix-windows-rc-compile.patch +13 -0
- package/scripts/cesium/vcpkg-ports/zstd/no-static-suffix.patch +13 -0
- package/scripts/cesium/vcpkg-ports/zstd/portfile.cmake +77 -0
- package/scripts/cesium/vcpkg-ports/zstd/usage +4 -0
- package/scripts/cesium/vcpkg-ports/zstd/vcpkg.json +23 -0
- package/scripts/cesium/vcpkg-triplets/arm64-ios-simulator.cmake +10 -0
- package/scripts/cesium/vcpkg-triplets/arm64-ios.cmake +14 -0
- package/src/CesiumView.tsx +13 -0
- package/src/index.ts +7 -0
- package/src/specs/cesium-view.nitro.ts +51 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Example App
|
|
2
|
+
|
|
3
|
+
This app is the reference integration for `react-native-cesium`. For the current public API, installation notes, and platform support status, start with the root [`README.md`](../README.md).
|
|
4
|
+
|
|
5
|
+
## What it demonstrates
|
|
6
|
+
|
|
7
|
+
- rendering `CesiumView` inside a React Native screen
|
|
8
|
+
- wiring a Nitro `hybridRef`
|
|
9
|
+
- driving the camera with `setCamera(...)`
|
|
10
|
+
- receiving runtime telemetry through `onMetrics`
|
|
11
|
+
- showing Cesium attribution text from `creditsPlainText`
|
|
12
|
+
|
|
13
|
+
## Before you run it
|
|
14
|
+
|
|
15
|
+
1. The example **`ios/Podfile`** includes **`react_native_cesium_ensure_native`** in **`pre_install`**, so the first **`pod install`** can clone and build Cesium Native automatically (long first run; see root README). You can still run **`yarn update`** and **`CESIUM_BUILD_ONLY=ios yarn build`** from the repo root first if you prefer. After changing `.env`, restart Metro so the Babel env transform re-reads the file.
|
|
16
|
+
|
|
17
|
+
2. Copy `example/.env_example` to `example/.env` and set `CESIUM_ION_ACCESS_TOKEN` to your own Cesium Ion access token.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cp .env_example .env
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
3. Install example dependencies and CocoaPods:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd example
|
|
27
|
+
yarn install
|
|
28
|
+
cd ios && pod install && cd ..
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Run the example
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
yarn start
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
In a second terminal:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
yarn ios
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The example links the library locally using `link:..`, so changes in the package are reflected directly while developing.
|
|
Binary file
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <MetalKit/MetalKit.h>
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
@interface CesiumBridge : NSObject
|
|
7
|
+
|
|
8
|
+
- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer
|
|
9
|
+
width:(int)width
|
|
10
|
+
height:(int)height
|
|
11
|
+
cacheDir:(NSString *)cacheDir;
|
|
12
|
+
|
|
13
|
+
- (void)updateIonAccessToken:(NSString *)token assetId:(int64_t)assetId;
|
|
14
|
+
- (void)updateImageryAssetId:(int64_t)assetId;
|
|
15
|
+
- (void)updateCameraLatitude:(double)lat
|
|
16
|
+
longitude:(double)lon
|
|
17
|
+
altitude:(double)alt
|
|
18
|
+
heading:(double)heading
|
|
19
|
+
pitch:(double)pitch
|
|
20
|
+
roll:(double)roll;
|
|
21
|
+
- (void)resize:(int)width height:(int)height;
|
|
22
|
+
- (BOOL)shouldRenderNextFrame;
|
|
23
|
+
- (void)markNeedsRender;
|
|
24
|
+
- (void)renderFrameWithDt:(double)dt;
|
|
25
|
+
- (void)shutdown;
|
|
26
|
+
|
|
27
|
+
// Camera / globe
|
|
28
|
+
- (void)setVerticalFovDeg:(double)degrees;
|
|
29
|
+
|
|
30
|
+
// Tileset tuning (triggers rebuild when changed)
|
|
31
|
+
- (void)setMaximumScreenSpaceError:(double)v;
|
|
32
|
+
- (void)setMaximumSimultaneousTileLoads:(int)v;
|
|
33
|
+
- (void)setLoadingDescendantLimit:(int)v;
|
|
34
|
+
|
|
35
|
+
- (void)setMsaaSampleCount:(int)samples;
|
|
36
|
+
|
|
37
|
+
/// Throttled metrics consumed by the Swift hybrid view.
|
|
38
|
+
@property (nonatomic, readonly) double metricsFps;
|
|
39
|
+
@property (nonatomic, readonly) NSInteger metricsTilesRendered;
|
|
40
|
+
@property (nonatomic, readonly) NSInteger metricsTilesLoading;
|
|
41
|
+
@property (nonatomic, readonly) NSInteger metricsTilesVisited;
|
|
42
|
+
@property (nonatomic, readonly) BOOL metricsIonTokenConfigured;
|
|
43
|
+
@property (nonatomic, readonly) BOOL metricsTilesetReady;
|
|
44
|
+
@property (nonatomic, readonly) NSString *metricsCreditsPlainText;
|
|
45
|
+
- (double)readCameraLatitude;
|
|
46
|
+
- (double)readCameraLongitude;
|
|
47
|
+
- (double)readCameraAltitude;
|
|
48
|
+
- (double)readCameraHeading;
|
|
49
|
+
- (double)readCameraPitch;
|
|
50
|
+
- (double)readCameraRoll;
|
|
51
|
+
- (double)readVerticalFovDeg;
|
|
52
|
+
|
|
53
|
+
@end
|
|
54
|
+
|
|
55
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#import "CesiumBridge.h"
|
|
2
|
+
#import <CoreFoundation/CoreFoundation.h>
|
|
3
|
+
|
|
4
|
+
#include "engine/CesiumEngine.hpp"
|
|
5
|
+
#include "metal/MetalBackend.h"
|
|
6
|
+
|
|
7
|
+
#include <atomic>
|
|
8
|
+
#include <cmath>
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <string>
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
// Strips HTML tags and collapses whitespace to produce plain-text attribution.
|
|
15
|
+
static NSString* stripHtmlToPlain(NSString* html) {
|
|
16
|
+
if (html.length == 0) return @"";
|
|
17
|
+
NSError* err = nil;
|
|
18
|
+
NSRegularExpression* tagRx =
|
|
19
|
+
[NSRegularExpression regularExpressionWithPattern:@"<[^>]+>" options:0 error:&err];
|
|
20
|
+
NSString* plain = [tagRx stringByReplacingMatchesInString:html
|
|
21
|
+
options:0
|
|
22
|
+
range:NSMakeRange(0, html.length)
|
|
23
|
+
withTemplate:@" "];
|
|
24
|
+
NSRegularExpression* wsRx =
|
|
25
|
+
[NSRegularExpression regularExpressionWithPattern:@"\\s+" options:0 error:&err];
|
|
26
|
+
plain = [wsRx stringByReplacingMatchesInString:plain
|
|
27
|
+
options:0
|
|
28
|
+
range:NSMakeRange(0, plain.length)
|
|
29
|
+
withTemplate:@" "];
|
|
30
|
+
return [plain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
double lerpAngleDeg(double a, double b, double t) {
|
|
34
|
+
double diff = std::fmod(b - a + 540.0, 360.0) - 180.0;
|
|
35
|
+
return a + diff * t;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
double angleDeltaAbsDeg(double a, double b) {
|
|
39
|
+
return std::abs(std::fmod(b - a + 540.0, 360.0) - 180.0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // namespace
|
|
43
|
+
|
|
44
|
+
// ── Per-DOF smoothing. ────────────────────────────────────────────────────────
|
|
45
|
+
static const double kSmoothAlt = 25.0;
|
|
46
|
+
static const double kSmoothHdg = 30.0;
|
|
47
|
+
static const double kSmoothRoll = 50.0;
|
|
48
|
+
static const double kSmoothPitch = 50.0;
|
|
49
|
+
static const double kEpsLatLon = 1e-7;
|
|
50
|
+
static const double kEpsAlt = 0.1;
|
|
51
|
+
static const double kEpsAngleDeg = 0.05;
|
|
52
|
+
|
|
53
|
+
@implementation CesiumBridge {
|
|
54
|
+
std::unique_ptr<reactnativecesium::MetalBackend> _metalBackend;
|
|
55
|
+
std::unique_ptr<reactnativecesium::CesiumEngine> _engine;
|
|
56
|
+
reactnativecesium::FrameResult _frameResult;
|
|
57
|
+
int _viewportWidth;
|
|
58
|
+
int _viewportHeight;
|
|
59
|
+
BOOL _initialized;
|
|
60
|
+
|
|
61
|
+
NSString* _cacheDir;
|
|
62
|
+
NSString* _ionAccessToken;
|
|
63
|
+
int64_t _ionTilesetAssetId;
|
|
64
|
+
|
|
65
|
+
double _maxSSE;
|
|
66
|
+
double _maxSimLoads;
|
|
67
|
+
double _loadDescLim;
|
|
68
|
+
|
|
69
|
+
// Demand target — incoming props/calls write here; render loop smooths toward it.
|
|
70
|
+
reactnativecesium::CameraParams _camTarget;
|
|
71
|
+
BOOL _forceRenderNextFrame;
|
|
72
|
+
|
|
73
|
+
BOOL _suspended;
|
|
74
|
+
|
|
75
|
+
double _fpsEma;
|
|
76
|
+
int _metricsTick;
|
|
77
|
+
|
|
78
|
+
double _metricsFps;
|
|
79
|
+
NSInteger _metricsTilesRendered;
|
|
80
|
+
NSInteger _metricsTilesLoading;
|
|
81
|
+
NSInteger _metricsTilesVisited;
|
|
82
|
+
BOOL _metricsIonTokenConfigured;
|
|
83
|
+
BOOL _metricsTilesetReady;
|
|
84
|
+
NSString* _metricsCreditsPlainText;
|
|
85
|
+
|
|
86
|
+
NSString* _lastCreditHtmlJoined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (reactnativecesium::EngineConfig)makeEngineConfig {
|
|
90
|
+
reactnativecesium::EngineConfig config;
|
|
91
|
+
if (_ionAccessToken) {
|
|
92
|
+
config.ionAccessToken = std::string([_ionAccessToken UTF8String]);
|
|
93
|
+
}
|
|
94
|
+
config.ionAssetId = _ionTilesetAssetId;
|
|
95
|
+
config.cacheDatabasePath =
|
|
96
|
+
_cacheDir ? std::string([_cacheDir UTF8String]) + "/cesium_cache.db" : "";
|
|
97
|
+
NSString* caPem = [[NSBundle mainBundle] pathForResource:@"cacert" ofType:@"pem"];
|
|
98
|
+
if (caPem.length > 0) {
|
|
99
|
+
config.tlsCaBundlePath = std::string([caPem fileSystemRepresentation]);
|
|
100
|
+
}
|
|
101
|
+
config.maximumScreenSpaceError = std::max(1.0, _maxSSE);
|
|
102
|
+
config.maximumSimultaneousTileLoads =
|
|
103
|
+
static_cast<int32_t>(std::max(0.0, std::round(_maxSimLoads)));
|
|
104
|
+
config.loadingDescendantLimit =
|
|
105
|
+
static_cast<int32_t>(std::max(1.0, std::round(_loadDescLim)));
|
|
106
|
+
return config;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer
|
|
110
|
+
width:(int)width
|
|
111
|
+
height:(int)height
|
|
112
|
+
cacheDir:(NSString *)cacheDir {
|
|
113
|
+
self = [super init];
|
|
114
|
+
if (self) {
|
|
115
|
+
_viewportWidth = width;
|
|
116
|
+
_viewportHeight = height;
|
|
117
|
+
_initialized = NO;
|
|
118
|
+
_cacheDir = [cacheDir copy];
|
|
119
|
+
_ionAccessToken = nil;
|
|
120
|
+
_ionTilesetAssetId = 1;
|
|
121
|
+
_maxSSE = 32.0;
|
|
122
|
+
_maxSimLoads = 12.0;
|
|
123
|
+
_loadDescLim = 20.0;
|
|
124
|
+
_forceRenderNextFrame = YES;
|
|
125
|
+
_suspended = NO;
|
|
126
|
+
_fpsEma = 0.0;
|
|
127
|
+
_metricsTick = 0;
|
|
128
|
+
_metricsFps = 0.0;
|
|
129
|
+
_metricsTilesRendered = 0;
|
|
130
|
+
_metricsTilesLoading = 0;
|
|
131
|
+
_metricsTilesVisited = 0;
|
|
132
|
+
_metricsIonTokenConfigured = NO;
|
|
133
|
+
_metricsTilesetReady = NO;
|
|
134
|
+
_metricsCreditsPlainText = @"";
|
|
135
|
+
_lastCreditHtmlJoined = @"";
|
|
136
|
+
|
|
137
|
+
_metalBackend = std::make_unique<reactnativecesium::MetalBackend>();
|
|
138
|
+
_metalBackend->initialize((__bridge void*)layer, width, height);
|
|
139
|
+
|
|
140
|
+
[self buildEngine];
|
|
141
|
+
_initialized = YES;
|
|
142
|
+
|
|
143
|
+
[[NSNotificationCenter defaultCenter]
|
|
144
|
+
addObserver:self selector:@selector(appWillResignActive:)
|
|
145
|
+
name:UIApplicationWillResignActiveNotification object:nil];
|
|
146
|
+
[[NSNotificationCenter defaultCenter]
|
|
147
|
+
addObserver:self selector:@selector(appDidBecomeActive:)
|
|
148
|
+
name:UIApplicationDidBecomeActiveNotification object:nil];
|
|
149
|
+
}
|
|
150
|
+
return self;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
- (void)appWillResignActive:(NSNotification *)note { _suspended = YES; }
|
|
154
|
+
- (void)appDidBecomeActive:(NSNotification *)note {
|
|
155
|
+
_suspended = NO;
|
|
156
|
+
_forceRenderNextFrame = YES;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
- (void)buildEngine {
|
|
160
|
+
reactnativecesium::EngineConfig cfg = [self makeEngineConfig];
|
|
161
|
+
static std::atomic<int> caWarned{0};
|
|
162
|
+
if (cfg.tlsCaBundlePath.empty() && caWarned.fetch_add(1) == 0) {
|
|
163
|
+
NSLog(@"[ReactNativeCesium] cacert.pem not in main bundle — libcurl may fail TLS to "
|
|
164
|
+
@"api.cesium.com on device. Ensure the pod includes ios/cacert.pem (run pod install).");
|
|
165
|
+
}
|
|
166
|
+
_engine.reset();
|
|
167
|
+
_engine = std::make_unique<reactnativecesium::CesiumEngine>();
|
|
168
|
+
_engine->initialize(cfg);
|
|
169
|
+
|
|
170
|
+
// Sync demand target with engine's initial camera defaults.
|
|
171
|
+
_camTarget = _engine->camera().getParams();
|
|
172
|
+
|
|
173
|
+
auto* backendPtr = _metalBackend.get();
|
|
174
|
+
_engine->getResourcePreparer()->setGPUTextureCreator(
|
|
175
|
+
[backendPtr](const uint8_t* pixels, int32_t w, int32_t h) -> void* {
|
|
176
|
+
return backendPtr->createRasterTexture(pixels, w, h);
|
|
177
|
+
});
|
|
178
|
+
_engine->getResourcePreparer()->setGPUTextureDeleter(
|
|
179
|
+
[](void* tex) {
|
|
180
|
+
if (tex) CFRelease(tex);
|
|
181
|
+
});
|
|
182
|
+
// Water mask textures are identical MTLTexture objects — createRasterTexture
|
|
183
|
+
// is reused. On Metal, the binding slot (texture index 1) is set at draw
|
|
184
|
+
// time so no separate factory is needed.
|
|
185
|
+
NSLog(@"[WM-BRIDGE] Wiring up water mask texture creator/deleter");
|
|
186
|
+
_engine->getResourcePreparer()->setWaterMaskTextureCreator(
|
|
187
|
+
[backendPtr](const uint8_t* pixels, int32_t w, int32_t h) -> void* {
|
|
188
|
+
NSLog(@"[WM-BRIDGE] waterMaskCreator called w=%d h=%d pixels=%p", w, h, pixels);
|
|
189
|
+
void* tex = backendPtr->createRasterTexture(pixels, w, h);
|
|
190
|
+
NSLog(@"[WM-BRIDGE] waterMaskCreator => tex=%p", tex);
|
|
191
|
+
return tex;
|
|
192
|
+
});
|
|
193
|
+
_engine->getResourcePreparer()->setWaterMaskTextureDeleter(
|
|
194
|
+
[](void* tex) {
|
|
195
|
+
NSLog(@"[WM-BRIDGE] waterMaskDeleter called tex=%p", tex);
|
|
196
|
+
if (tex) CFRelease(tex);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
- (void)updateIonAccessToken:(NSString *)token assetId:(int64_t)assetId {
|
|
201
|
+
if (!_initialized) return;
|
|
202
|
+
_ionAccessToken = [token copy];
|
|
203
|
+
_ionTilesetAssetId = assetId;
|
|
204
|
+
_engine->updateConfig([self makeEngineConfig]);
|
|
205
|
+
_forceRenderNextFrame = YES;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
- (void)updateImageryAssetId:(int64_t)assetId {
|
|
209
|
+
if (!_initialized) return;
|
|
210
|
+
_engine->setImageryAssetId(assetId);
|
|
211
|
+
_forceRenderNextFrame = YES;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
- (void)updateCameraLatitude:(double)lat
|
|
215
|
+
longitude:(double)lon
|
|
216
|
+
altitude:(double)alt
|
|
217
|
+
heading:(double)heading
|
|
218
|
+
pitch:(double)pitch
|
|
219
|
+
roll:(double)roll {
|
|
220
|
+
if (!_initialized) return;
|
|
221
|
+
_camTarget.latitude = lat;
|
|
222
|
+
_camTarget.longitude = lon;
|
|
223
|
+
_camTarget.altitude = alt;
|
|
224
|
+
_camTarget.heading = heading;
|
|
225
|
+
_camTarget.pitch = pitch;
|
|
226
|
+
_camTarget.roll = roll;
|
|
227
|
+
_forceRenderNextFrame = YES;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
- (void)resize:(int)width height:(int)height {
|
|
231
|
+
_viewportWidth = width;
|
|
232
|
+
_viewportHeight = height;
|
|
233
|
+
if (_metalBackend) _metalBackend->resize(width, height);
|
|
234
|
+
_forceRenderNextFrame = YES;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
- (void)setVerticalFovDeg:(double)degrees {
|
|
238
|
+
if (!_initialized) return;
|
|
239
|
+
_engine->camera().setVerticalFovDegrees(degrees);
|
|
240
|
+
_forceRenderNextFrame = YES;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
- (void)setMaximumScreenSpaceError:(double)v {
|
|
244
|
+
_maxSSE = v;
|
|
245
|
+
if (!_initialized) return;
|
|
246
|
+
_engine->updateConfig([self makeEngineConfig]);
|
|
247
|
+
_forceRenderNextFrame = YES;
|
|
248
|
+
}
|
|
249
|
+
- (void)setMaximumSimultaneousTileLoads:(int)v {
|
|
250
|
+
_maxSimLoads = static_cast<double>(v);
|
|
251
|
+
if (!_initialized) return;
|
|
252
|
+
_engine->updateConfig([self makeEngineConfig]);
|
|
253
|
+
_forceRenderNextFrame = YES;
|
|
254
|
+
}
|
|
255
|
+
- (void)setLoadingDescendantLimit:(int)v {
|
|
256
|
+
_loadDescLim = static_cast<double>(v);
|
|
257
|
+
if (!_initialized) return;
|
|
258
|
+
_engine->updateConfig([self makeEngineConfig]);
|
|
259
|
+
_forceRenderNextFrame = YES;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
- (void)setMsaaSampleCount:(int)samples {
|
|
263
|
+
if (_metalBackend) _metalBackend->setMsaaSampleCount(samples);
|
|
264
|
+
_forceRenderNextFrame = YES;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
- (void)markNeedsRender {
|
|
268
|
+
_forceRenderNextFrame = YES;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
- (BOOL)shouldRenderNextFrame {
|
|
272
|
+
if (!_initialized || _suspended || !_engine) {
|
|
273
|
+
return NO;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const auto cur = _engine->camera().getParams();
|
|
277
|
+
const bool cameraDirty =
|
|
278
|
+
std::abs(_camTarget.latitude - cur.latitude) > kEpsLatLon ||
|
|
279
|
+
std::abs(_camTarget.longitude - cur.longitude) > kEpsLatLon ||
|
|
280
|
+
std::abs(_camTarget.altitude - cur.altitude) > kEpsAlt ||
|
|
281
|
+
angleDeltaAbsDeg(cur.heading, _camTarget.heading) > kEpsAngleDeg ||
|
|
282
|
+
angleDeltaAbsDeg(cur.pitch, _camTarget.pitch) > kEpsAngleDeg ||
|
|
283
|
+
angleDeltaAbsDeg(cur.roll, _camTarget.roll) > kEpsAngleDeg;
|
|
284
|
+
|
|
285
|
+
return _forceRenderNextFrame ||
|
|
286
|
+
_frameResult.tilesLoading > 0 ||
|
|
287
|
+
!_frameResult.tilesetActive ||
|
|
288
|
+
cameraDirty;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
- (void)renderFrameWithDt:(double)dt {
|
|
292
|
+
if (!_initialized || _suspended) return;
|
|
293
|
+
|
|
294
|
+
@autoreleasepool {
|
|
295
|
+
auto cur = _engine->camera().getParams();
|
|
296
|
+
|
|
297
|
+
// ── Smooth follower: track _camTarget per DOF ────────────────────────────
|
|
298
|
+
// lat/lon: direct copy — pan gestures must feel instant.
|
|
299
|
+
cur.latitude = _camTarget.latitude;
|
|
300
|
+
cur.longitude = _camTarget.longitude;
|
|
301
|
+
// alt/hdg/roll/pitch: exponential decay toward target.
|
|
302
|
+
const double aAlt = 1.0 - std::exp(-kSmoothAlt * dt);
|
|
303
|
+
const double aHdg = 1.0 - std::exp(-kSmoothHdg * dt);
|
|
304
|
+
const double aPitch = 1.0 - std::exp(-kSmoothPitch * dt);
|
|
305
|
+
const double aRoll = 1.0 - std::exp(-kSmoothRoll * dt);
|
|
306
|
+
cur.altitude = cur.altitude + aAlt * (_camTarget.altitude - cur.altitude);
|
|
307
|
+
cur.heading = lerpAngleDeg(cur.heading, _camTarget.heading, aHdg);
|
|
308
|
+
cur.pitch = lerpAngleDeg(cur.pitch, _camTarget.pitch, aPitch);
|
|
309
|
+
cur.roll = lerpAngleDeg(cur.roll, _camTarget.roll, aRoll);
|
|
310
|
+
|
|
311
|
+
if (std::abs(_camTarget.altitude - cur.altitude) <= kEpsAlt) {
|
|
312
|
+
cur.altitude = _camTarget.altitude;
|
|
313
|
+
}
|
|
314
|
+
if (angleDeltaAbsDeg(cur.heading, _camTarget.heading) <= kEpsAngleDeg) {
|
|
315
|
+
cur.heading = _camTarget.heading;
|
|
316
|
+
}
|
|
317
|
+
if (angleDeltaAbsDeg(cur.pitch, _camTarget.pitch) <= kEpsAngleDeg) {
|
|
318
|
+
cur.pitch = _camTarget.pitch;
|
|
319
|
+
}
|
|
320
|
+
if (angleDeltaAbsDeg(cur.roll, _camTarget.roll) <= kEpsAngleDeg) {
|
|
321
|
+
cur.roll = _camTarget.roll;
|
|
322
|
+
}
|
|
323
|
+
_engine->camera().setParams(cur);
|
|
324
|
+
|
|
325
|
+
_engine->updateFrame(_viewportWidth, _viewportHeight, _frameResult);
|
|
326
|
+
|
|
327
|
+
const double instFps = (dt > 1e-6) ? (1.0 / dt) : 0.0;
|
|
328
|
+
_fpsEma = (_fpsEma <= 1e-6) ? instFps : (_fpsEma * 0.85 + instFps * 0.15);
|
|
329
|
+
|
|
330
|
+
if (++_metricsTick >= 20) {
|
|
331
|
+
_metricsTick = 0;
|
|
332
|
+
_metricsFps = _fpsEma;
|
|
333
|
+
_metricsTilesRendered = _frameResult.tilesRendered;
|
|
334
|
+
_metricsTilesLoading = _frameResult.tilesLoading;
|
|
335
|
+
_metricsTilesVisited = _frameResult.tilesVisited;
|
|
336
|
+
_metricsIonTokenConfigured = _frameResult.ionTokenConfigured;
|
|
337
|
+
_metricsTilesetReady = _frameResult.tilesetActive;
|
|
338
|
+
if (!_frameResult.creditHtmlLines.empty()) {
|
|
339
|
+
NSMutableString* joined = [NSMutableString string];
|
|
340
|
+
for (const auto& html : _frameResult.creditHtmlLines) {
|
|
341
|
+
if (joined.length > 0) [joined appendString:@"|"];
|
|
342
|
+
[joined appendString:[NSString stringWithUTF8String:html.c_str()]];
|
|
343
|
+
}
|
|
344
|
+
if (![_lastCreditHtmlJoined isEqualToString:joined]) {
|
|
345
|
+
_lastCreditHtmlJoined = [joined copy];
|
|
346
|
+
NSMutableString* credits = [NSMutableString string];
|
|
347
|
+
for (const auto& html : _frameResult.creditHtmlLines) {
|
|
348
|
+
NSString* chunk = stripHtmlToPlain(
|
|
349
|
+
[NSString stringWithUTF8String:html.c_str()]);
|
|
350
|
+
if (chunk.length == 0) continue;
|
|
351
|
+
if (credits.length > 0) [credits appendString:@" · "];
|
|
352
|
+
[credits appendString:chunk];
|
|
353
|
+
}
|
|
354
|
+
_metricsCreditsPlainText = [credits copy];
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
_lastCreditHtmlJoined = @"";
|
|
358
|
+
_metricsCreditsPlainText = @"";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
reactnativecesium::FrameParams params;
|
|
363
|
+
_metalBackend->beginFrame(params);
|
|
364
|
+
_metalBackend->drawScene(_frameResult);
|
|
365
|
+
_metalBackend->endFrame();
|
|
366
|
+
_forceRenderNextFrame = NO;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
- (double)metricsFps { return _metricsFps; }
|
|
371
|
+
- (NSInteger)metricsTilesRendered { return _metricsTilesRendered; }
|
|
372
|
+
- (NSInteger)metricsTilesLoading { return _metricsTilesLoading; }
|
|
373
|
+
- (NSInteger)metricsTilesVisited { return _metricsTilesVisited; }
|
|
374
|
+
- (BOOL)metricsIonTokenConfigured { return _metricsIonTokenConfigured; }
|
|
375
|
+
- (BOOL)metricsTilesetReady { return _metricsTilesetReady; }
|
|
376
|
+
- (NSString*)metricsCreditsPlainText { return _metricsCreditsPlainText ?: @""; }
|
|
377
|
+
|
|
378
|
+
- (double)readCameraLatitude { return _engine ? _engine->camera().getParams().latitude : 0.0; }
|
|
379
|
+
- (double)readCameraLongitude { return _engine ? _engine->camera().getParams().longitude : 0.0; }
|
|
380
|
+
- (double)readCameraAltitude { return _engine ? _engine->camera().getParams().altitude : 0.0; }
|
|
381
|
+
- (double)readCameraHeading { return _engine ? _engine->camera().getParams().heading : 0.0; }
|
|
382
|
+
- (double)readCameraPitch { return _engine ? _engine->camera().getParams().pitch : 0.0; }
|
|
383
|
+
- (double)readCameraRoll { return _engine ? _engine->camera().getParams().roll : 0.0; }
|
|
384
|
+
- (double)readVerticalFovDeg { return _engine ? _engine->camera().getVerticalFovDegrees() : 60.0; }
|
|
385
|
+
|
|
386
|
+
- (void)shutdown {
|
|
387
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
388
|
+
if (_engine) _engine->shutdown();
|
|
389
|
+
if (_metalBackend) _metalBackend->shutdown();
|
|
390
|
+
_initialized = NO;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
@end
|