react-native-cesium 1.0.1 → 1.0.4
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 +109 -19
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +88 -1
- package/android/src/main/cpp/CesiumBridgeAndroid.h +11 -1
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +35 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +55 -0
- package/cpp/engine/GlobeCamera.cpp +22 -0
- package/cpp/engine/GlobeCamera.hpp +3 -0
- package/ios/CesiumBridge.h +15 -0
- package/ios/CesiumBridge.mm +68 -7
- package/ios/HybridCesiumView.swift +56 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +21 -1
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +24 -0
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/JQuaternion.hpp +69 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +8 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Quaternion.kt +47 -0
- package/nitrogen/generated/ios/ReactNativeCesium+autolinking.rb +2 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +46 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +17 -0
- package/nitrogen/generated/ios/swift/Func_void_Quaternion.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +30 -0
- package/nitrogen/generated/ios/swift/Quaternion.swift +44 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +5 -0
- package/nitrogen/generated/shared/c++/Quaternion.hpp +95 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/specs/cesium-view.nitro.ts +22 -1
package/README.md
CHANGED
|
@@ -18,13 +18,26 @@ Experimental Cesium rendering for React Native using [Nitro Modules](https://git
|
|
|
18
18
|
## Requirements
|
|
19
19
|
|
|
20
20
|
- React Native **new architecture**
|
|
21
|
+
- React Native Nitro Modules is a dependency
|
|
21
22
|
- Follow the native build instructions below: additional setup is required by you.
|
|
22
23
|
- A valid Cesium Ion access token and asset IDs for your content (get these from https://ion.cesium.com)
|
|
23
24
|
- **Disk and time:** building Cesium Native pulls **vcpkg** dependencies, can use **several GB** of disk, and often takes a long time on first build.
|
|
24
25
|
|
|
25
26
|
## Installation
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
First install the Nitro Modules runtime dependency:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
yarn add react-native-nitro-modules
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
or:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install react-native-nitro-modules
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then add `react-native-cesium`:
|
|
28
41
|
|
|
29
42
|
```bash
|
|
30
43
|
yarn add react-native-cesium
|
|
@@ -123,22 +136,54 @@ The **post-install** helper is currently needed to:
|
|
|
123
136
|
- prepend this package's `cpp/` headers before the locally built XCFramework headers
|
|
124
137
|
- exclude `x86_64` iOS simulator builds, because the simulator slice is Apple Silicon `arm64` only
|
|
125
138
|
|
|
139
|
+
### Android (Gradle automatic ensure-native)
|
|
140
|
+
|
|
141
|
+
Unlike iOS, you **do not** need to copy anything into your **application’s** `android/build.gradle` (or `settings.gradle`) so Cesium Native can be cloned and built automatically. React Native already includes this package as an Android library dependency; this repo’s **`android/build.gradle`** registers **`ensureCesiumNativeAndroid`** and makes **`preBuild`** depend on it.
|
|
142
|
+
|
|
143
|
+
What happens on **`./gradlew`** / Android Studio build:
|
|
144
|
+
|
|
145
|
+
1. **`preBuild`** runs **`ensureCesiumNativeAndroid`** before CMake compiles the JNI/native code.
|
|
146
|
+
2. That task checks for a marker file under the installed package:
|
|
147
|
+
`vendor/android/share/cesium-native/cmake/cesium-nativeConfig.cmake`.
|
|
148
|
+
3. If the marker **exists**, the task does nothing.
|
|
149
|
+
4. If the marker is **missing**, it runs (from the package root, e.g. `node_modules/react-native-cesium`):
|
|
150
|
+
`node scripts/cesium/ensure-native.mjs --android`
|
|
151
|
+
which may run **`update`** then a **`CESIUM_BUILD_ONLY=android`** **`build`**, same as a manual run (can take a long time).
|
|
152
|
+
5. If **`REACT_NATIVE_CESIUM_SKIP_NATIVE_BUILD=1`** is set and artifacts are still missing, the build **fails fast** with an error instead of running the script.
|
|
153
|
+
|
|
154
|
+
You still need a discoverable **Android NDK** (expand **Additional Android specific requirements** under [System dependencies](#system-dependencies) above) so that build can succeed.
|
|
155
|
+
|
|
126
156
|
### Building
|
|
127
157
|
|
|
128
158
|
**Automatic (default):** assuming you have done the above and have all the required dependencies in place, when the native output is missing, the package tries to build it for you:
|
|
129
159
|
|
|
130
160
|
- **iOS:** add **`pre_install`** in your app **`Podfile`** (one-time; see [Required Podfile hooks](#required-podfile-hooks-ios)) so **`pod install`** runs **`scripts/cesium/ensure-native.mjs --ios`**, which runs **`npm run update`** / **`yarn run update`** (if needed) and then **`CESIUM_BUILD_ONLY=ios`** build. The first run can take **a long time** and needs the same **system dependencies** as a manual build (CMake, Ninja, Xcode, disk space, etc.).
|
|
131
|
-
- **Android:**
|
|
161
|
+
- **Android:** no extra hooks in your app; see [Android (Gradle automatic ensure-native)](#android-gradle-automatic-ensure-native). Needs a discoverable **NDK**.
|
|
162
|
+
|
|
163
|
+
**Manual (optional):** run the **`update`** and **`build`** scripts defined in **`react-native-cesium`’s `package.json`**. They are **not** available as top-level commands in your app; you must run them **in the context of the installed package**.
|
|
164
|
+
|
|
165
|
+
From your **application project root** (where your app’s `package.json` lives), after `yarn add` / `npm install`:
|
|
166
|
+
|
|
167
|
+
**Yarn:**
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
yarn --cwd node_modules/react-native-cesium run update
|
|
171
|
+
yarn --cwd node_modules/react-native-cesium run build
|
|
172
|
+
```
|
|
132
173
|
|
|
133
|
-
**
|
|
174
|
+
**npm:**
|
|
134
175
|
|
|
135
176
|
```bash
|
|
136
|
-
|
|
137
|
-
|
|
177
|
+
npm run update --prefix node_modules/react-native-cesium
|
|
178
|
+
npm run build --prefix node_modules/react-native-cesium
|
|
138
179
|
```
|
|
139
180
|
|
|
140
|
-
|
|
141
|
-
|
|
181
|
+
**Alternative:** `cd node_modules/react-native-cesium` and run `yarn run update` / `yarn run build` (or the equivalent `npm run …`).
|
|
182
|
+
|
|
183
|
+
**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).
|
|
184
|
+
|
|
185
|
+
- `update` checks out **Cesium Native `v0.59.0`** into `vendor/cesium-native` under the package (next to its other files; typically ignored in app repos).
|
|
186
|
+
- `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`**.
|
|
142
187
|
|
|
143
188
|
Then install pods (iOS):
|
|
144
189
|
|
|
@@ -152,6 +197,7 @@ The public JS surface is exported from `react-native-cesium`:
|
|
|
152
197
|
|
|
153
198
|
- `CesiumView`
|
|
154
199
|
- `CameraState`
|
|
200
|
+
- `Quaternion`
|
|
155
201
|
- `CesiumMetrics`
|
|
156
202
|
- `CesiumViewProps`
|
|
157
203
|
- `CesiumViewMethods`
|
|
@@ -256,22 +302,65 @@ type CameraState = {
|
|
|
256
302
|
}
|
|
257
303
|
```
|
|
258
304
|
|
|
259
|
-
| Field | Type | Valid range / format | Description
|
|
260
|
-
| --- | --- | ---
|
|
261
|
-
| `latitude` | `number` | `-90..90` | Camera latitude in degrees.
|
|
262
|
-
| `longitude` | `number` | `-180..180` | Camera longitude in degrees.
|
|
263
|
-
| `altitude` | `number` | Meters above ellipsoid | Camera height in meters.
|
|
264
|
-
| `heading` | `number` | Degrees | Compass direction the camera faces. Example: `0` faces north, `90` faces east.
|
|
265
|
-
| `pitch` | `number` | Degrees | Tilt angle. Negative values look downward toward terrain; positive values tilt up toward horizon/sky.
|
|
266
|
-
| `roll` | `number` | Degrees | Bank/rotation around forward axis. Positive right bank, negative left bank.
|
|
267
|
-
| `verticalFovDeg` | `number` | `20..100` (clamped) | Vertical field of view
|
|
305
|
+
| Field | Type | Valid range / format | Description |
|
|
306
|
+
| --- | --- | --- |-------------------------------------------------------------------------------------------------------|
|
|
307
|
+
| `latitude` | `number` | `-90..90` | Camera latitude in degrees. |
|
|
308
|
+
| `longitude` | `number` | `-180..180` | Camera longitude in degrees. |
|
|
309
|
+
| `altitude` | `number` | Meters above ellipsoid | Camera height in meters. |
|
|
310
|
+
| `heading` | `number` | Degrees | Compass direction the camera faces. Example: `0` faces north, `90` faces east. |
|
|
311
|
+
| `pitch` | `number` | Degrees | Tilt angle. Negative values look downward toward terrain; positive values tilt up toward horizon/sky. |
|
|
312
|
+
| `roll` | `number` | Degrees | Bank/rotation around forward axis. Positive right bank, negative left bank. |
|
|
313
|
+
| `verticalFovDeg` | `number` | `20..100` (clamped) | Vertical field of view in degrees; see **Field of view** (next section). |
|
|
314
|
+
|
|
315
|
+
### Field of view: vertical vs horizontal, full vs half, and Cesium alignment
|
|
316
|
+
|
|
317
|
+
Use this when matching **Skia**, **custom HUDs**, or **CesiumJS** math to the same frustum as this view.
|
|
318
|
+
|
|
319
|
+
| Concept | What this library uses |
|
|
320
|
+
| --- | --- |
|
|
321
|
+
| **API field** | `verticalFovDeg` is the **full** vertical aperture in **degrees** (top to bottom through the view axis), not a half-angle and not the horizontal FOV. |
|
|
322
|
+
| **Horizontal FOV** | Not stored. With viewport width `w` and height `h` in pixels and `aspect = w / h`, the native tile `ViewState` uses **full** vertical FOV `vfov_rad = radians(verticalFovDeg)` and **full** horizontal FOV `hfov_rad = 2 * atan(tan(vfov_rad / 2) * aspect)`. |
|
|
323
|
+
| **Half-angles** | The projection uses the usual symmetric perspective form: `tan(vfov_rad / 2)` (and the same pattern horizontally via aspect). Those are **half** of the full vertical/horizontal FOV, in radians—standard for `tan` of frustum slopes, not an alternate “FOV definition.” |
|
|
324
|
+
| **Cesium Native `ViewState`** | `GlobeCamera` passes that `vfov_rad` and `hfov_rad` into `Cesium3DTilesSelection::ViewState` together with the viewport size—consistent with **full-angle** vertical/horizontal FOV in radians for culling. |
|
|
325
|
+
| **CesiumJS `PerspectiveFrustum` / `Camera.frustum`** | **Comparable vertical angle:** In CesiumJS, `PerspectiveFrustum#fovy` is the **full vertical** FOV in **radians** (derived from `fov` and `aspectRatio`). This package’s `verticalFovDeg` is always **vertical** in **degrees**; use `vfov_rad = Cesium.Math.toRadians(verticalFovDeg)` (or equivalent) to compare to `fovy` when the **same aspect ratio** and symmetric frustum assumptions apply. This repo uses **Cesium Native**, not CesiumJS—tile culling uses `ViewState` with `vfov_rad` / `hfov_rad` as above, not the JS frustum object. |
|
|
326
|
+
|
|
327
|
+
### `Quaternion`
|
|
328
|
+
|
|
329
|
+
Used for **camera-space view correction** with `setCameraQuaternion` (see below). Components are `w, x, y, z` (Hamilton convention). Non-unit values are normalized on the native side.
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
type Quaternion = {
|
|
333
|
+
w: number
|
|
334
|
+
x: number
|
|
335
|
+
y: number
|
|
336
|
+
z: number
|
|
337
|
+
}
|
|
338
|
+
```
|
|
268
339
|
|
|
269
340
|
### `CesiumViewMethods`
|
|
270
341
|
|
|
271
342
|
| Method | Signature | Description |
|
|
272
343
|
| --- | --- |-----------------------------------------------------------------------------------------------------------------------------------------------|
|
|
273
|
-
| `setCamera` | `(camera: CameraState) => void` | Applies a new camera state at runtime.
|
|
274
|
-
| `
|
|
344
|
+
| `setCamera` | `(camera: CameraState) => void` | Applies a new camera state at runtime (position, heading, pitch, roll, vertical FOV). Does **not** change the stored view-correction quaternion; use `setCameraQuaternion` when you need to update that. |
|
|
345
|
+
| `setCameraQuaternion` | `(camera: CameraState, viewCorrection: Quaternion) => void` | Same camera fields as `setCamera`, plus a rotation applied **in camera space after** heading/pitch/roll. Use this for screen-fixed adjustments (e.g. boresight calibration, aligning a synthetic horizon overlay) without expressing the offset as extra Euler angles. |
|
|
346
|
+
| `getCameraState` | `() => Promise<CameraState>` | Returns the current native camera snapshot (lat/lon/alt, HPR, VFOV). This is the underlying globe attitude; it does **not** encode the view correction into HPR. |
|
|
347
|
+
| `getViewCorrection` | `() => Promise<Quaternion>` | Returns the **smoothed** view-correction quaternion currently applied (identity `w=1,x=y=z=0` if you have never called `setCameraQuaternion`). |
|
|
348
|
+
|
|
349
|
+
#### Threading: `setCamera` / `setCameraQuaternion` vs `getCameraState` / `getViewCorrection`
|
|
350
|
+
|
|
351
|
+
| Call style | Supported / best practice |
|
|
352
|
+
| --- | --- |
|
|
353
|
+
| **`setCamera` / `setCameraQuaternion`** | **Synchronous** native updates. **Supported** from the **UI thread**, including **Reanimated worklets** and **`useAnimatedReaction`** when you call through a Nitro `hybridRef` (same pattern as driving camera demand from shared values). This is the intended path for high-frequency camera updates. |
|
|
354
|
+
| **`getCameraState` / `getViewCorrection`** | **Async** (`Promise`). **Call from the JavaScript thread** (e.g. `useEffect`, handlers, throttled HUD state)—not from worklets—unless you have a clear, tested pattern for async in your runtime. |
|
|
355
|
+
|
|
356
|
+
Avoid assuming **main-thread-only** vs **JS-thread-only** labels beyond the above: Nitro invokes the hybrid object on the thread that called the method; use sync setters on the UI/worklet path you already use for gestures, and reserve Promise-based getters for JS.
|
|
357
|
+
|
|
358
|
+
#### `setCamera` vs `setCameraQuaternion`
|
|
359
|
+
|
|
360
|
+
- Use **`setCamera`** alone when you only need the classic globe camera.
|
|
361
|
+
- Use **`setCameraQuaternion`** when you need both the usual `CameraState` **and** a small rotation relative to the uncorrected camera (HUD alignment, horizon line offset in screen space, lens/display calibration, etc.).
|
|
362
|
+
- You may **mix** the two: calling `setCamera` updates position and HPR but leaves the last view-correction target unchanged, so a calibration quaternion set earlier continues to apply until you change it with `setCameraQuaternion`.
|
|
363
|
+
- Prefer **`setCameraQuaternion`** on frames where you need to drive both the globe camera and the correction together so the native target stays consistent.
|
|
275
364
|
|
|
276
365
|
### `CesiumMetrics`
|
|
277
366
|
|
|
@@ -290,7 +379,8 @@ type CameraState = {
|
|
|
290
379
|
The example app in [`example/App.tsx`](./example/App.tsx) is the best current integration reference. It shows:
|
|
291
380
|
|
|
292
381
|
- creating and storing a Nitro `hybridRef`
|
|
293
|
-
- driving camera updates with `setCamera(...)`
|
|
382
|
+
- driving camera updates with `setCamera(...)` from a Reanimated **`useAnimatedReaction`** (UI-thread / worklet path; see **Threading** under `CesiumViewMethods` above)
|
|
383
|
+
- the `setCameraQuaternion` / `getViewCorrection` APIs for camera-space HUD alignment (the example does not demonstrate them yet; see **`CesiumViewMethods`** above)
|
|
294
384
|
- listening to `onMetrics`
|
|
295
385
|
- switching imagery layers
|
|
296
386
|
- presenting Cesium attribution from `creditsPlainText`
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
#include <android/native_window_jni.h>
|
|
7
7
|
#include <android/log.h>
|
|
8
8
|
|
|
9
|
+
#include <glm/gtc/quaternion.hpp>
|
|
10
|
+
|
|
9
11
|
#include <cmath>
|
|
10
12
|
#include <regex>
|
|
11
13
|
#include <sstream>
|
|
@@ -19,9 +21,15 @@ static const double kSmoothAlt = 25.0;
|
|
|
19
21
|
static const double kSmoothHdg = 30.0;
|
|
20
22
|
static const double kSmoothRoll = 50.0;
|
|
21
23
|
static const double kSmoothPitch = 50.0;
|
|
24
|
+
static const double kSmoothViewCorr = 50.0;
|
|
22
25
|
static const double kEpsLatLon = 1e-7;
|
|
23
26
|
static const double kEpsAlt = 0.1;
|
|
24
27
|
static const double kEpsAngleDeg = 0.05;
|
|
28
|
+
static const double kEpsQuatDot = 1e-8;
|
|
29
|
+
|
|
30
|
+
static double quatDotAbs(const glm::dquat& a, const glm::dquat& b) {
|
|
31
|
+
return std::abs(glm::dot(glm::normalize(a), glm::normalize(b)));
|
|
32
|
+
}
|
|
25
33
|
|
|
26
34
|
static double lerpAngleDeg(double a, double b, double t) {
|
|
27
35
|
double diff = std::fmod(b - a + 540.0, 360.0) - 180.0;
|
|
@@ -89,6 +97,10 @@ void CesiumBridgeAndroid::buildEngine() {
|
|
|
89
97
|
camTargetHeading_ = params.heading;
|
|
90
98
|
camTargetPitch_ = params.pitch;
|
|
91
99
|
camTargetRoll_ = params.roll;
|
|
100
|
+
camTargetViewQw_ = params.viewCorrection.w;
|
|
101
|
+
camTargetViewQx_ = params.viewCorrection.x;
|
|
102
|
+
camTargetViewQy_ = params.viewCorrection.y;
|
|
103
|
+
camTargetViewQz_ = params.viewCorrection.z;
|
|
92
104
|
|
|
93
105
|
auto* backendPtr = vulkanBackend_.get();
|
|
94
106
|
engine_->getResourcePreparer()->setGPUTextureCreator(
|
|
@@ -155,6 +167,32 @@ void CesiumBridgeAndroid::updateCamera(double lat, double lon, double alt,
|
|
|
155
167
|
camTargetHeading_ = heading;
|
|
156
168
|
camTargetPitch_ = pitch;
|
|
157
169
|
camTargetRoll_ = roll;
|
|
170
|
+
// Does not change camTargetViewQ* (view correction target).
|
|
171
|
+
forceRenderNextFrame_ = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
void CesiumBridgeAndroid::updateCameraQuaternion(double lat, double lon, double alt,
|
|
175
|
+
double heading, double pitch, double roll,
|
|
176
|
+
double qw, double qx, double qy, double qz) {
|
|
177
|
+
if (!initialized_) return;
|
|
178
|
+
camTargetLat_ = lat;
|
|
179
|
+
camTargetLon_ = lon;
|
|
180
|
+
camTargetAlt_ = alt;
|
|
181
|
+
camTargetHeading_ = heading;
|
|
182
|
+
camTargetPitch_ = pitch;
|
|
183
|
+
camTargetRoll_ = roll;
|
|
184
|
+
const double ql2 = qw * qw + qx * qx + qy * qy + qz * qz;
|
|
185
|
+
glm::dquat q;
|
|
186
|
+
if (ql2 < 1e-20) {
|
|
187
|
+
q = glm::dquat(1.0, 0.0, 0.0, 0.0);
|
|
188
|
+
} else {
|
|
189
|
+
const double inv = 1.0 / std::sqrt(ql2);
|
|
190
|
+
q = glm::dquat(qw * inv, qx * inv, qy * inv, qz * inv);
|
|
191
|
+
}
|
|
192
|
+
camTargetViewQw_ = q.w;
|
|
193
|
+
camTargetViewQx_ = q.x;
|
|
194
|
+
camTargetViewQy_ = q.y;
|
|
195
|
+
camTargetViewQz_ = q.z;
|
|
158
196
|
forceRenderNextFrame_ = true;
|
|
159
197
|
}
|
|
160
198
|
|
|
@@ -204,13 +242,15 @@ bool CesiumBridgeAndroid::shouldRenderNextFrame() {
|
|
|
204
242
|
if (!initialized_ || !engine_) return false;
|
|
205
243
|
|
|
206
244
|
const auto cur = engine_->camera().getParams();
|
|
245
|
+
glm::dquat tgt(camTargetViewQw_, camTargetViewQx_, camTargetViewQy_, camTargetViewQz_);
|
|
207
246
|
const bool cameraDirty =
|
|
208
247
|
std::abs(camTargetLat_ - cur.latitude) > kEpsLatLon ||
|
|
209
248
|
std::abs(camTargetLon_ - cur.longitude) > kEpsLatLon ||
|
|
210
249
|
std::abs(camTargetAlt_ - cur.altitude) > kEpsAlt ||
|
|
211
250
|
angleDeltaAbsDeg(cur.heading, camTargetHeading_) > kEpsAngleDeg ||
|
|
212
251
|
angleDeltaAbsDeg(cur.pitch, camTargetPitch_) > kEpsAngleDeg ||
|
|
213
|
-
angleDeltaAbsDeg(cur.roll, camTargetRoll_) > kEpsAngleDeg
|
|
252
|
+
angleDeltaAbsDeg(cur.roll, camTargetRoll_) > kEpsAngleDeg ||
|
|
253
|
+
(1.0 - quatDotAbs(cur.viewCorrection, tgt)) > kEpsQuatDot;
|
|
214
254
|
|
|
215
255
|
return forceRenderNextFrame_ ||
|
|
216
256
|
frameResult_->tilesLoading > 0 ||
|
|
@@ -231,12 +271,19 @@ void CesiumBridgeAndroid::renderFrame(double dt) {
|
|
|
231
271
|
const double aHdg = 1.0 - std::exp(-kSmoothHdg * dt);
|
|
232
272
|
const double aPitch = 1.0 - std::exp(-kSmoothPitch * dt);
|
|
233
273
|
const double aRoll = 1.0 - std::exp(-kSmoothRoll * dt);
|
|
274
|
+
const double aViewQ = 1.0 - std::exp(-kSmoothViewCorr * dt);
|
|
234
275
|
|
|
235
276
|
cur.altitude = cur.altitude + aAlt * (camTargetAlt_ - cur.altitude);
|
|
236
277
|
cur.heading = lerpAngleDeg(cur.heading, camTargetHeading_, aHdg);
|
|
237
278
|
cur.pitch = lerpAngleDeg(cur.pitch, camTargetPitch_, aPitch);
|
|
238
279
|
cur.roll = lerpAngleDeg(cur.roll, camTargetRoll_, aRoll);
|
|
239
280
|
|
|
281
|
+
glm::dquat cq = glm::normalize(cur.viewCorrection);
|
|
282
|
+
glm::dquat tq = glm::normalize(glm::dquat(camTargetViewQw_, camTargetViewQx_, camTargetViewQy_, camTargetViewQz_));
|
|
283
|
+
if (glm::dot(cq, tq) < 0.0)
|
|
284
|
+
tq = -tq;
|
|
285
|
+
cur.viewCorrection = glm::normalize(glm::slerp(cq, tq, aViewQ));
|
|
286
|
+
|
|
240
287
|
if (std::abs(camTargetAlt_ - cur.altitude) <= kEpsAlt)
|
|
241
288
|
cur.altitude = camTargetAlt_;
|
|
242
289
|
if (angleDeltaAbsDeg(cur.heading, camTargetHeading_) <= kEpsAngleDeg)
|
|
@@ -245,6 +292,8 @@ void CesiumBridgeAndroid::renderFrame(double dt) {
|
|
|
245
292
|
cur.pitch = camTargetPitch_;
|
|
246
293
|
if (angleDeltaAbsDeg(cur.roll, camTargetRoll_) <= kEpsAngleDeg)
|
|
247
294
|
cur.roll = camTargetRoll_;
|
|
295
|
+
if ((1.0 - quatDotAbs(cur.viewCorrection, tq)) <= kEpsQuatDot)
|
|
296
|
+
cur.viewCorrection = tq;
|
|
248
297
|
|
|
249
298
|
engine_->camera().setParams(cur);
|
|
250
299
|
engine_->updateFrame(viewportWidth_, viewportHeight_, *frameResult_);
|
|
@@ -313,6 +362,19 @@ double CesiumBridgeAndroid::readVerticalFovDeg() {
|
|
|
313
362
|
return engine_ ? engine_->camera().getVerticalFovDegrees() : 60.0;
|
|
314
363
|
}
|
|
315
364
|
|
|
365
|
+
double CesiumBridgeAndroid::readViewCorrectionW() {
|
|
366
|
+
return engine_ ? engine_->camera().getParams().viewCorrection.w : 1.0;
|
|
367
|
+
}
|
|
368
|
+
double CesiumBridgeAndroid::readViewCorrectionX() {
|
|
369
|
+
return engine_ ? engine_->camera().getParams().viewCorrection.x : 0.0;
|
|
370
|
+
}
|
|
371
|
+
double CesiumBridgeAndroid::readViewCorrectionY() {
|
|
372
|
+
return engine_ ? engine_->camera().getParams().viewCorrection.y : 0.0;
|
|
373
|
+
}
|
|
374
|
+
double CesiumBridgeAndroid::readViewCorrectionZ() {
|
|
375
|
+
return engine_ ? engine_->camera().getParams().viewCorrection.z : 0.0;
|
|
376
|
+
}
|
|
377
|
+
|
|
316
378
|
// ── JNI native method implementations ──────────────────────────────────────────
|
|
317
379
|
|
|
318
380
|
static CesiumBridgeAndroid* getBridge(jlong ptr) {
|
|
@@ -373,6 +435,14 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeUpdateCamera(
|
|
|
373
435
|
getBridge(ptr)->updateCamera(lat, lon, alt, heading, pitch, roll);
|
|
374
436
|
}
|
|
375
437
|
|
|
438
|
+
JNIEXPORT void JNICALL
|
|
439
|
+
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeUpdateCameraQuaternion(
|
|
440
|
+
JNIEnv*, jobject, jlong ptr, jdouble lat, jdouble lon, jdouble alt,
|
|
441
|
+
jdouble heading, jdouble pitch, jdouble roll,
|
|
442
|
+
jdouble qw, jdouble qx, jdouble qy, jdouble qz) {
|
|
443
|
+
getBridge(ptr)->updateCameraQuaternion(lat, lon, alt, heading, pitch, roll, qw, qx, qy, qz);
|
|
444
|
+
}
|
|
445
|
+
|
|
376
446
|
JNIEXPORT void JNICALL
|
|
377
447
|
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetVerticalFovDeg(
|
|
378
448
|
JNIEnv*, jobject, jlong ptr, jdouble deg) {
|
|
@@ -449,6 +519,23 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetVerticalFovDeg
|
|
|
449
519
|
return getBridge(ptr)->readVerticalFovDeg();
|
|
450
520
|
}
|
|
451
521
|
|
|
522
|
+
JNIEXPORT jdouble JNICALL
|
|
523
|
+
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetViewCorrectionW(JNIEnv*, jobject, jlong ptr) {
|
|
524
|
+
return getBridge(ptr)->readViewCorrectionW();
|
|
525
|
+
}
|
|
526
|
+
JNIEXPORT jdouble JNICALL
|
|
527
|
+
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetViewCorrectionX(JNIEnv*, jobject, jlong ptr) {
|
|
528
|
+
return getBridge(ptr)->readViewCorrectionX();
|
|
529
|
+
}
|
|
530
|
+
JNIEXPORT jdouble JNICALL
|
|
531
|
+
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetViewCorrectionY(JNIEnv*, jobject, jlong ptr) {
|
|
532
|
+
return getBridge(ptr)->readViewCorrectionY();
|
|
533
|
+
}
|
|
534
|
+
JNIEXPORT jdouble JNICALL
|
|
535
|
+
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetViewCorrectionZ(JNIEnv*, jobject, jlong ptr) {
|
|
536
|
+
return getBridge(ptr)->readViewCorrectionZ();
|
|
537
|
+
}
|
|
538
|
+
|
|
452
539
|
JNIEXPORT jdouble JNICALL
|
|
453
540
|
Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeGetMetricsFps(JNIEnv*, jobject, jlong ptr) {
|
|
454
541
|
return getBridge(ptr)->metricsFps();
|
|
@@ -22,6 +22,9 @@ struct CesiumBridgeAndroid {
|
|
|
22
22
|
void updateImageryAssetId(int64_t assetId);
|
|
23
23
|
void updateCamera(double lat, double lon, double alt,
|
|
24
24
|
double heading, double pitch, double roll);
|
|
25
|
+
void updateCameraQuaternion(double lat, double lon, double alt,
|
|
26
|
+
double heading, double pitch, double roll,
|
|
27
|
+
double qw, double qx, double qy, double qz);
|
|
25
28
|
void setVerticalFovDeg(double degrees);
|
|
26
29
|
void setMaximumScreenSpaceError(double v);
|
|
27
30
|
void setMaximumSimultaneousTileLoads(int v);
|
|
@@ -40,6 +43,10 @@ struct CesiumBridgeAndroid {
|
|
|
40
43
|
double readCameraPitch();
|
|
41
44
|
double readCameraRoll();
|
|
42
45
|
double readVerticalFovDeg();
|
|
46
|
+
double readViewCorrectionW();
|
|
47
|
+
double readViewCorrectionX();
|
|
48
|
+
double readViewCorrectionY();
|
|
49
|
+
double readViewCorrectionZ();
|
|
43
50
|
|
|
44
51
|
// Metrics
|
|
45
52
|
double metricsFps() const { return metricsFps_; }
|
|
@@ -52,7 +59,6 @@ struct CesiumBridgeAndroid {
|
|
|
52
59
|
|
|
53
60
|
private:
|
|
54
61
|
void buildEngine();
|
|
55
|
-
reactnativecesium::CameraParams makeCamTarget();
|
|
56
62
|
|
|
57
63
|
std::unique_ptr<reactnativecesium::VulkanBackend> vulkanBackend_;
|
|
58
64
|
std::unique_ptr<reactnativecesium::CesiumEngine> engine_;
|
|
@@ -78,6 +84,10 @@ private:
|
|
|
78
84
|
double camTargetHeading_ = 129.0;
|
|
79
85
|
double camTargetPitch_ = -45.0;
|
|
80
86
|
double camTargetRoll_ = 0.0;
|
|
87
|
+
double camTargetViewQw_ = 1.0;
|
|
88
|
+
double camTargetViewQx_ = 0.0;
|
|
89
|
+
double camTargetViewQy_ = 0.0;
|
|
90
|
+
double camTargetViewQz_ = 0.0;
|
|
81
91
|
bool forceRenderNextFrame_ = true;
|
|
82
92
|
|
|
83
93
|
// Metrics
|
|
@@ -25,6 +25,19 @@ class CesiumBridgeJNI {
|
|
|
25
25
|
fun updateImageryAssetId(assetId: Long) = nativeUpdateImageryAssetId(nativePtr, assetId)
|
|
26
26
|
fun updateCamera(lat: Double, lon: Double, alt: Double, heading: Double, pitch: Double, roll: Double) =
|
|
27
27
|
nativeUpdateCamera(nativePtr, lat, lon, alt, heading, pitch, roll)
|
|
28
|
+
|
|
29
|
+
fun updateCameraQuaternion(
|
|
30
|
+
lat: Double,
|
|
31
|
+
lon: Double,
|
|
32
|
+
alt: Double,
|
|
33
|
+
heading: Double,
|
|
34
|
+
pitch: Double,
|
|
35
|
+
roll: Double,
|
|
36
|
+
qw: Double,
|
|
37
|
+
qx: Double,
|
|
38
|
+
qy: Double,
|
|
39
|
+
qz: Double,
|
|
40
|
+
) = nativeUpdateCameraQuaternion(nativePtr, lat, lon, alt, heading, pitch, roll, qw, qx, qy, qz)
|
|
28
41
|
fun setVerticalFovDeg(deg: Double) = nativeSetVerticalFovDeg(nativePtr, deg)
|
|
29
42
|
fun setMaxSSE(v: Double) = nativeSetMaxSSE(nativePtr, v)
|
|
30
43
|
fun setMaxSimLoads(v: Int) = nativeSetMaxSimLoads(nativePtr, v)
|
|
@@ -43,6 +56,11 @@ class CesiumBridgeJNI {
|
|
|
43
56
|
fun getCameraRoll(): Double = nativeGetCameraRoll(nativePtr)
|
|
44
57
|
fun getVerticalFovDeg(): Double = nativeGetVerticalFovDeg(nativePtr)
|
|
45
58
|
|
|
59
|
+
fun getViewCorrectionW(): Double = nativeGetViewCorrectionW(nativePtr)
|
|
60
|
+
fun getViewCorrectionX(): Double = nativeGetViewCorrectionX(nativePtr)
|
|
61
|
+
fun getViewCorrectionY(): Double = nativeGetViewCorrectionY(nativePtr)
|
|
62
|
+
fun getViewCorrectionZ(): Double = nativeGetViewCorrectionZ(nativePtr)
|
|
63
|
+
|
|
46
64
|
fun getMetricsFps(): Double = nativeGetMetricsFps(nativePtr)
|
|
47
65
|
fun getMetricsTilesRendered(): Int = nativeGetMetricsTilesRendered(nativePtr)
|
|
48
66
|
fun getMetricsTilesLoading(): Int = nativeGetMetricsTilesLoading(nativePtr)
|
|
@@ -59,6 +77,19 @@ class CesiumBridgeJNI {
|
|
|
59
77
|
private external fun nativeUpdateIonAccessToken(ptr: Long, token: String, assetId: Long)
|
|
60
78
|
private external fun nativeUpdateImageryAssetId(ptr: Long, assetId: Long)
|
|
61
79
|
private external fun nativeUpdateCamera(ptr: Long, lat: Double, lon: Double, alt: Double, heading: Double, pitch: Double, roll: Double)
|
|
80
|
+
private external fun nativeUpdateCameraQuaternion(
|
|
81
|
+
ptr: Long,
|
|
82
|
+
lat: Double,
|
|
83
|
+
lon: Double,
|
|
84
|
+
alt: Double,
|
|
85
|
+
heading: Double,
|
|
86
|
+
pitch: Double,
|
|
87
|
+
roll: Double,
|
|
88
|
+
qw: Double,
|
|
89
|
+
qx: Double,
|
|
90
|
+
qy: Double,
|
|
91
|
+
qz: Double,
|
|
92
|
+
)
|
|
62
93
|
private external fun nativeSetVerticalFovDeg(ptr: Long, deg: Double)
|
|
63
94
|
private external fun nativeSetMaxSSE(ptr: Long, v: Double)
|
|
64
95
|
private external fun nativeSetMaxSimLoads(ptr: Long, v: Int)
|
|
@@ -74,6 +105,10 @@ class CesiumBridgeJNI {
|
|
|
74
105
|
private external fun nativeGetCameraPitch(ptr: Long): Double
|
|
75
106
|
private external fun nativeGetCameraRoll(ptr: Long): Double
|
|
76
107
|
private external fun nativeGetVerticalFovDeg(ptr: Long): Double
|
|
108
|
+
private external fun nativeGetViewCorrectionW(ptr: Long): Double
|
|
109
|
+
private external fun nativeGetViewCorrectionX(ptr: Long): Double
|
|
110
|
+
private external fun nativeGetViewCorrectionY(ptr: Long): Double
|
|
111
|
+
private external fun nativeGetViewCorrectionZ(ptr: Long): Double
|
|
77
112
|
private external fun nativeGetMetricsFps(ptr: Long): Double
|
|
78
113
|
private external fun nativeGetMetricsTilesRendered(ptr: Long): Int
|
|
79
114
|
private external fun nativeGetMetricsTilesLoading(ptr: Long): Int
|
|
@@ -17,6 +17,7 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
|
|
|
17
17
|
private var usingLowRefreshRate = false
|
|
18
18
|
private var lastFrameTimeNanos = 0L
|
|
19
19
|
private var lastPushedCamera: CameraState? = null
|
|
20
|
+
private var lastPushedViewCorrection: Quaternion? = null
|
|
20
21
|
private var renderLoopActive = false
|
|
21
22
|
|
|
22
23
|
override var ionAccessToken: String = ""
|
|
@@ -174,6 +175,24 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
|
|
|
174
175
|
pushCameraIfChanged(camera)
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
override fun setCameraQuaternion(camera: CameraState, viewCorrection: Quaternion) {
|
|
179
|
+
initialCamera = camera
|
|
180
|
+
pushCameraQuaternionIfChanged(camera, viewCorrection)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
override fun getViewCorrection(): Promise<Quaternion> {
|
|
184
|
+
val b = bridge
|
|
185
|
+
?: return Promise.resolved(Quaternion(w = 1.0, x = 0.0, y = 0.0, z = 0.0))
|
|
186
|
+
return Promise.resolved(
|
|
187
|
+
Quaternion(
|
|
188
|
+
w = b.getViewCorrectionW(),
|
|
189
|
+
x = b.getViewCorrectionX(),
|
|
190
|
+
y = b.getViewCorrectionY(),
|
|
191
|
+
z = b.getViewCorrectionZ(),
|
|
192
|
+
),
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
177
196
|
private fun pushCameraIfChanged(camera: CameraState) {
|
|
178
197
|
val b = bridge ?: return
|
|
179
198
|
val last = lastPushedCamera
|
|
@@ -193,6 +212,42 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
|
|
|
193
212
|
b.setVerticalFovDeg(camera.verticalFovDeg)
|
|
194
213
|
}
|
|
195
214
|
|
|
215
|
+
private fun pushCameraQuaternionIfChanged(camera: CameraState, viewCorrection: Quaternion) {
|
|
216
|
+
val b = bridge ?: return
|
|
217
|
+
val last = lastPushedCamera
|
|
218
|
+
val lastQ = lastPushedViewCorrection
|
|
219
|
+
if (last != null && lastQ != null &&
|
|
220
|
+
last.latitude == camera.latitude &&
|
|
221
|
+
last.longitude == camera.longitude &&
|
|
222
|
+
last.altitude == camera.altitude &&
|
|
223
|
+
last.heading == camera.heading &&
|
|
224
|
+
last.pitch == camera.pitch &&
|
|
225
|
+
last.roll == camera.roll &&
|
|
226
|
+
last.verticalFovDeg == camera.verticalFovDeg &&
|
|
227
|
+
lastQ.w == viewCorrection.w &&
|
|
228
|
+
lastQ.x == viewCorrection.x &&
|
|
229
|
+
lastQ.y == viewCorrection.y &&
|
|
230
|
+
lastQ.z == viewCorrection.z
|
|
231
|
+
) {
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
lastPushedCamera = camera
|
|
235
|
+
lastPushedViewCorrection = viewCorrection
|
|
236
|
+
b.updateCameraQuaternion(
|
|
237
|
+
camera.latitude,
|
|
238
|
+
camera.longitude,
|
|
239
|
+
camera.altitude,
|
|
240
|
+
camera.heading,
|
|
241
|
+
camera.pitch,
|
|
242
|
+
camera.roll,
|
|
243
|
+
viewCorrection.w,
|
|
244
|
+
viewCorrection.x,
|
|
245
|
+
viewCorrection.y,
|
|
246
|
+
viewCorrection.z,
|
|
247
|
+
)
|
|
248
|
+
b.setVerticalFovDeg(camera.verticalFovDeg)
|
|
249
|
+
}
|
|
250
|
+
|
|
196
251
|
private fun syncPropsTobridge() {
|
|
197
252
|
bridge?.setMaxSSE(maximumScreenSpaceError)
|
|
198
253
|
bridge?.setMaxSimLoads(maximumSimultaneousTileLoads.toInt())
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <CesiumGeospatial/GlobeTransforms.h>
|
|
4
4
|
|
|
5
5
|
#include <glm/gtc/matrix_transform.hpp>
|
|
6
|
+
#include <glm/gtc/quaternion.hpp>
|
|
6
7
|
|
|
7
8
|
#include <algorithm>
|
|
8
9
|
#include <cmath>
|
|
@@ -75,6 +76,27 @@ void GlobeCamera::recompute() const {
|
|
|
75
76
|
right = glm::normalize(rolledRight);
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
// 4. Optional camera-space correction (columns: right, camUp, forward).
|
|
80
|
+
const glm::dquat& qIn = params_.viewCorrection;
|
|
81
|
+
const double ql2 =
|
|
82
|
+
qIn.w * qIn.w + qIn.x * qIn.x + qIn.y * qIn.y + qIn.z * qIn.z;
|
|
83
|
+
glm::dquat qCorr;
|
|
84
|
+
if (ql2 < 1e-20) {
|
|
85
|
+
qCorr = glm::dquat(1.0, 0.0, 0.0, 0.0);
|
|
86
|
+
} else {
|
|
87
|
+
const double inv = 1.0 / std::sqrt(ql2);
|
|
88
|
+
qCorr = glm::dquat(qIn.w * inv, qIn.x * inv, qIn.y * inv, qIn.z * inv);
|
|
89
|
+
}
|
|
90
|
+
glm::dmat3 R_basis(right, camUp, fwd);
|
|
91
|
+
glm::dmat3 R_corr = glm::mat3_cast(qCorr);
|
|
92
|
+
glm::dmat3 R_final = R_basis * R_corr;
|
|
93
|
+
glm::dvec3 r0 = glm::normalize(glm::dvec3(R_final[0]));
|
|
94
|
+
glm::dvec3 r2 = glm::normalize(glm::dvec3(R_final[2]));
|
|
95
|
+
// Gram–Schmidt: preserve forward (column 2), rebuild right/up (matches right × fwd = camUp).
|
|
96
|
+
fwd = r2;
|
|
97
|
+
right = glm::normalize(r0 - glm::dot(r0, fwd) * fwd);
|
|
98
|
+
camUp = glm::normalize(glm::cross(right, fwd));
|
|
99
|
+
|
|
78
100
|
direction_ = fwd;
|
|
79
101
|
up_ = camUp;
|
|
80
102
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include <Cesium3DTilesSelection/ViewState.h>
|
|
6
6
|
|
|
7
7
|
#include <glm/glm.hpp>
|
|
8
|
+
#include <glm/gtc/quaternion.hpp>
|
|
8
9
|
#include <mutex>
|
|
9
10
|
|
|
10
11
|
namespace reactnativecesium {
|
|
@@ -16,6 +17,8 @@ struct CameraParams {
|
|
|
16
17
|
double heading = 220.0;
|
|
17
18
|
double pitch = -10.0;
|
|
18
19
|
double roll = 0.0;
|
|
20
|
+
/// Camera-space rotation applied after HPR (unit quaternion; w,x,y,z).
|
|
21
|
+
glm::dquat viewCorrection{1.0, 0.0, 0.0, 0.0};
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
class GlobeCamera {
|
package/ios/CesiumBridge.h
CHANGED
|
@@ -18,6 +18,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
18
18
|
heading:(double)heading
|
|
19
19
|
pitch:(double)pitch
|
|
20
20
|
roll:(double)roll;
|
|
21
|
+
/// Same as `updateCameraLatitude:...` plus camera-space view correction (w,x,y,z). Does not replace HPR; applied after HPR in the engine.
|
|
22
|
+
- (void)updateCameraQuaternionLatitude:(double)lat
|
|
23
|
+
longitude:(double)lon
|
|
24
|
+
altitude:(double)alt
|
|
25
|
+
heading:(double)heading
|
|
26
|
+
pitch:(double)pitch
|
|
27
|
+
roll:(double)roll
|
|
28
|
+
viewCorrectionW:(double)qw
|
|
29
|
+
x:(double)qx
|
|
30
|
+
y:(double)qy
|
|
31
|
+
z:(double)qz;
|
|
21
32
|
- (void)resize:(int)width height:(int)height;
|
|
22
33
|
- (BOOL)shouldRenderNextFrame;
|
|
23
34
|
- (void)markNeedsRender;
|
|
@@ -49,6 +60,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
49
60
|
- (double)readCameraPitch;
|
|
50
61
|
- (double)readCameraRoll;
|
|
51
62
|
- (double)readVerticalFovDeg;
|
|
63
|
+
- (double)readViewCorrectionW;
|
|
64
|
+
- (double)readViewCorrectionX;
|
|
65
|
+
- (double)readViewCorrectionY;
|
|
66
|
+
- (double)readViewCorrectionZ;
|
|
52
67
|
|
|
53
68
|
@end
|
|
54
69
|
|