@sceneview-sdk/react-native 4.3.5 → 4.5.0
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 +35 -9
- package/android/src/main/java/io/github/sceneview/reactnative/ARRecorderModule.kt +46 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewManager.kt +31 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewModule.kt +3 -1
- package/ios/RNSceneViewManager.m +21 -0
- package/ios/SceneViewModule.swift +98 -0
- package/lib/commonjs/index.js +80 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +80 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +61 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +108 -0
package/README.md
CHANGED
|
@@ -91,15 +91,21 @@ import { ARSceneView } from '@sceneview-sdk/react-native';
|
|
|
91
91
|
|
|
92
92
|
### Props — SceneView
|
|
93
93
|
|
|
94
|
-
| Prop
|
|
95
|
-
|
|
96
|
-
| `style`
|
|
97
|
-
| `environment`
|
|
98
|
-
| `modelNodes`
|
|
99
|
-
| `geometryNodes`
|
|
100
|
-
| `lightNodes`
|
|
101
|
-
| `cameraOrbit`
|
|
102
|
-
| `
|
|
94
|
+
| Prop | Type | Default | Description |
|
|
95
|
+
|---------------------|----------------------|-----------|------------------------------------------|
|
|
96
|
+
| `style` | `ViewStyle` | — | Standard React Native style |
|
|
97
|
+
| `environment` | `string` | — | HDR environment asset path |
|
|
98
|
+
| `modelNodes` | `ModelNode[]` | `[]` | Models to render |
|
|
99
|
+
| `geometryNodes` | `GeometryNode[]` | `[]` | Geometry nodes (forward-compatible) |
|
|
100
|
+
| `lightNodes` | `LightNode[]` | `[]` | Light nodes (forward-compatible) |
|
|
101
|
+
| `cameraOrbit` | `boolean` | `true` | Enable orbit camera controls |
|
|
102
|
+
| `cameraControlMode` | `CameraControlMode` | `'orbit'` | Camera mode (v4.3.0). `'pan'`/`'firstPerson'` are iOS-only |
|
|
103
|
+
| `autoCenterContent` | `boolean` | `true` | Auto-centre content on first frame (v4.3.0, iOS-first) |
|
|
104
|
+
| `onTap` | `function` | — | Tap callback (event pending) |
|
|
105
|
+
|
|
106
|
+
`cameraControlMode` `'pan'` and `'firstPerson'` are iOS-only in v4.3.0; on
|
|
107
|
+
Android they fall back to orbit. `autoCenterContent` is iOS-first — the
|
|
108
|
+
Android side is tracked in issue #1051.
|
|
103
109
|
|
|
104
110
|
### Props — ARSceneView (extends SceneView)
|
|
105
111
|
|
|
@@ -122,6 +128,26 @@ interface ModelNode {
|
|
|
122
128
|
}
|
|
123
129
|
```
|
|
124
130
|
|
|
131
|
+
### AR recording (v4.3.0 — iOS)
|
|
132
|
+
|
|
133
|
+
`ARRecorder` records an AR session to a `.mov` video (iOS via ReplayKit):
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { ARRecorder } from '@sceneview-sdk/react-native';
|
|
137
|
+
|
|
138
|
+
const recorder = new ARRecorder();
|
|
139
|
+
|
|
140
|
+
await recorder.start();
|
|
141
|
+
// ... later ...
|
|
142
|
+
const path = await recorder.stop();
|
|
143
|
+
await recorder.saveToPhotoLibrary(path);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`ARRecorder` is iOS-only; on Android every method rejects with an
|
|
147
|
+
`UNSUPPORTED` error (ARCore session recording is tracked in issue #1051).
|
|
148
|
+
Check `ARRecorder.isSupported` before use. The host iOS app must declare
|
|
149
|
+
`NSPhotoLibraryAddUsageDescription` in `Info.plist` for `saveToPhotoLibrary`.
|
|
150
|
+
|
|
125
151
|
## Architecture
|
|
126
152
|
|
|
127
153
|
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package io.github.sceneview.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
|
+
import com.facebook.react.bridge.ReactMethod
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* React Native bridge for AR session recording (v4.3.0, issue #1053).
|
|
10
|
+
*
|
|
11
|
+
* Exposed to JS as `NativeModules.RNARRecorder`.
|
|
12
|
+
*
|
|
13
|
+
* On iOS this is backed by SceneViewSwift's `ARRecorder` (ReplayKit screen
|
|
14
|
+
* capture). On Android, ARCore's `io.github.sceneview.ar.recording.ARRecorder`
|
|
15
|
+
* records a *replayable session dataset* — a fundamentally different artifact
|
|
16
|
+
* from a video, and one that needs `Session`/`Frame` access the Fabric
|
|
17
|
+
* platform-view bridge does not currently expose.
|
|
18
|
+
*
|
|
19
|
+
* Rather than silently no-op, every method rejects with `UNSUPPORTED` so the
|
|
20
|
+
* JS `ARRecorder` class (which already gates `Platform.OS !== 'ios'`) and any
|
|
21
|
+
* direct caller get an honest, actionable error. The Android bridge is
|
|
22
|
+
* tracked in issue #1051.
|
|
23
|
+
*/
|
|
24
|
+
class ARRecorderModule(
|
|
25
|
+
reactContext: ReactApplicationContext,
|
|
26
|
+
) : ReactContextBaseJavaModule(reactContext) {
|
|
27
|
+
|
|
28
|
+
override fun getName(): String = "RNARRecorder"
|
|
29
|
+
|
|
30
|
+
private fun rejectUnsupported(promise: Promise) {
|
|
31
|
+
promise.reject(
|
|
32
|
+
"UNSUPPORTED",
|
|
33
|
+
"ARRecorder is not supported on Android via the React Native bridge. " +
|
|
34
|
+
"ARCore session recording is tracked in issue #1051.",
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@ReactMethod
|
|
39
|
+
fun start(promise: Promise) = rejectUnsupported(promise)
|
|
40
|
+
|
|
41
|
+
@ReactMethod
|
|
42
|
+
fun stop(outputPath: String?, promise: Promise) = rejectUnsupported(promise)
|
|
43
|
+
|
|
44
|
+
@ReactMethod
|
|
45
|
+
fun saveToPhotoLibrary(movPath: String, promise: Promise) = rejectUnsupported(promise)
|
|
46
|
+
}
|
|
@@ -39,6 +39,19 @@ class SceneViewState {
|
|
|
39
39
|
val lightNodes = mutableStateListOf<LightNodeData>()
|
|
40
40
|
val environmentPath = mutableStateOf<String?>(null)
|
|
41
41
|
val orbitEnabled = mutableStateOf(true)
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* v4.3.0 camera control mode (#1053). Stored for parity with the iOS
|
|
45
|
+
* bridge; Android's `SceneView` composable already orbits by default,
|
|
46
|
+
* so `pan` / `firstPerson` currently fall back to orbit (tracked in #1051).
|
|
47
|
+
*/
|
|
48
|
+
val cameraControlMode = mutableStateOf("orbit")
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* v4.3.0 content auto-centring (#1053). iOS-first; the Android
|
|
52
|
+
* library-level implementation is tracked in #1051.
|
|
53
|
+
*/
|
|
54
|
+
val autoCenterContent = mutableStateOf(true)
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
/**
|
|
@@ -303,6 +316,24 @@ class SceneViewManager : SimpleViewManager<FrameLayout>() {
|
|
|
303
316
|
fun setCameraOrbit(view: FrameLayout, enabled: Boolean) {
|
|
304
317
|
getState(view).orbitEnabled.value = enabled
|
|
305
318
|
}
|
|
319
|
+
|
|
320
|
+
@ReactProp(name = "cameraControlMode")
|
|
321
|
+
fun setCameraControlMode(view: FrameLayout, mode: String?) {
|
|
322
|
+
// v4.3.0 camera modes (#1053). The Android `SceneView` composable
|
|
323
|
+
// already uses an orbit manipulator by default; `pan` and
|
|
324
|
+
// `firstPerson` are iOS-first additions. Acknowledged here so
|
|
325
|
+
// cross-platform JS code does not crash the prop setter — the
|
|
326
|
+
// per-mode switch for Android is tracked in issue #1051.
|
|
327
|
+
getState(view).cameraControlMode.value = mode ?: "orbit"
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
@ReactProp(name = "autoCenterContent", defaultBoolean = true)
|
|
331
|
+
fun setAutoCenterContent(view: FrameLayout, enabled: Boolean) {
|
|
332
|
+
// v4.3.0 content auto-centring (#1053) is iOS-first; the Android
|
|
333
|
+
// library-level implementation is tracked in issue #1051.
|
|
334
|
+
// Acknowledged so cross-platform JS code does not crash.
|
|
335
|
+
getState(view).autoCenterContent.value = enabled
|
|
336
|
+
}
|
|
306
337
|
}
|
|
307
338
|
|
|
308
339
|
data class ModelNodeData(
|
|
@@ -20,7 +20,9 @@ class SceneViewPackage : ReactPackage {
|
|
|
20
20
|
|
|
21
21
|
override fun createNativeModules(
|
|
22
22
|
reactContext: ReactApplicationContext
|
|
23
|
-
): List<NativeModule> =
|
|
23
|
+
): List<NativeModule> = listOf(
|
|
24
|
+
ARRecorderModule(reactContext),
|
|
25
|
+
)
|
|
24
26
|
|
|
25
27
|
override fun createViewManagers(
|
|
26
28
|
reactContext: ReactApplicationContext
|
package/ios/RNSceneViewManager.m
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// Objective-C bridge — required by React Native to register view managers.
|
|
2
2
|
|
|
3
3
|
#import <React/RCTViewManager.h>
|
|
4
|
+
#import <React/RCTBridgeModule.h>
|
|
4
5
|
|
|
5
6
|
@interface RCT_EXTERN_MODULE(RNSceneViewManager, RCTViewManager)
|
|
6
7
|
|
|
7
8
|
RCT_EXPORT_VIEW_PROPERTY(environment, NSString)
|
|
8
9
|
RCT_EXPORT_VIEW_PROPERTY(modelNodes, NSArray)
|
|
9
10
|
RCT_EXPORT_VIEW_PROPERTY(cameraOrbit, BOOL)
|
|
11
|
+
// v4.3.0 camera + content APIs (issue #1053)
|
|
12
|
+
RCT_EXPORT_VIEW_PROPERTY(cameraControlMode, NSString)
|
|
13
|
+
RCT_EXPORT_VIEW_PROPERTY(autoCenterContent, BOOL)
|
|
10
14
|
RCT_EXPORT_VIEW_PROPERTY(onTap, RCTDirectEventBlock)
|
|
11
15
|
|
|
12
16
|
@end
|
|
@@ -22,3 +26,20 @@ RCT_EXPORT_VIEW_PROPERTY(onTap, RCTDirectEventBlock)
|
|
|
22
26
|
RCT_EXPORT_VIEW_PROPERTY(onPlaneDetected, RCTDirectEventBlock)
|
|
23
27
|
|
|
24
28
|
@end
|
|
29
|
+
|
|
30
|
+
// v4.3.0 AR recording native module (issue #1053). ReplayKit-backed,
|
|
31
|
+
// iOS-only — the JS `ARRecorder` class gates non-iOS platforms.
|
|
32
|
+
@interface RCT_EXTERN_MODULE(RNARRecorder, NSObject)
|
|
33
|
+
|
|
34
|
+
RCT_EXTERN_METHOD(start:(RCTPromiseResolveBlock)resolve
|
|
35
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
36
|
+
|
|
37
|
+
RCT_EXTERN_METHOD(stop:(NSString *)outputPath
|
|
38
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
39
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
40
|
+
|
|
41
|
+
RCT_EXTERN_METHOD(saveToPhotoLibrary:(NSString *)movPath
|
|
42
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
43
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
44
|
+
|
|
45
|
+
@end
|
|
@@ -33,12 +33,24 @@ class RNSceneViewManager: RCTViewManager {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/// Maps the wire string sent from JS to a `CameraControlMode` (v4.3.0).
|
|
37
|
+
/// Unknown values fall back to `.orbit`.
|
|
38
|
+
func rnCameraControlMode(_ raw: String?) -> CameraControlMode {
|
|
39
|
+
switch raw {
|
|
40
|
+
case "pan": return .pan
|
|
41
|
+
case "firstPerson": return .firstPerson
|
|
42
|
+
default: return .orbit
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
/// Observable state model shared between React props and SwiftUI view.
|
|
37
47
|
@MainActor
|
|
38
48
|
class RNSceneState: ObservableObject {
|
|
39
49
|
@Published var models: [RNModelData] = []
|
|
40
50
|
@Published var environmentPath: String?
|
|
41
51
|
@Published var cameraOrbit: Bool = true
|
|
52
|
+
@Published var cameraControlMode: CameraControlMode = .orbit
|
|
53
|
+
@Published var autoCenterContent: Bool = true
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
/// UIView wrapper that hosts a SwiftUI `SceneView` via UIHostingController.
|
|
@@ -112,6 +124,25 @@ class RNSceneViewWrapper: UIView {
|
|
|
112
124
|
}
|
|
113
125
|
}
|
|
114
126
|
}
|
|
127
|
+
|
|
128
|
+
/// Camera interaction mode (v4.3.0, issue #1053).
|
|
129
|
+
@objc var cameraControlMode: String? {
|
|
130
|
+
didSet {
|
|
131
|
+
let mode = rnCameraControlMode(cameraControlMode)
|
|
132
|
+
Task { @MainActor in
|
|
133
|
+
sceneState.cameraControlMode = mode
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// Whether to auto-centre scene content (v4.3.0, issue #1053).
|
|
139
|
+
@objc var autoCenterContent: Bool = true {
|
|
140
|
+
didSet {
|
|
141
|
+
Task { @MainActor in
|
|
142
|
+
sceneState.autoCenterContent = autoCenterContent
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
115
146
|
}
|
|
116
147
|
|
|
117
148
|
/// SwiftUI content view rendering SceneViewSwift.SceneView.
|
|
@@ -126,6 +157,8 @@ struct RNSceneViewContent: View {
|
|
|
126
157
|
.scale(model.scale)
|
|
127
158
|
}
|
|
128
159
|
}
|
|
160
|
+
.cameraControls(state.cameraControlMode)
|
|
161
|
+
.autoCenterContent(state.autoCenterContent)
|
|
129
162
|
}
|
|
130
163
|
}
|
|
131
164
|
|
|
@@ -257,3 +290,68 @@ struct RNARSceneViewContent: View {
|
|
|
257
290
|
}
|
|
258
291
|
}
|
|
259
292
|
}
|
|
293
|
+
|
|
294
|
+
// MARK: - AR Recorder native module (v4.3.0, issue #1053)
|
|
295
|
+
|
|
296
|
+
/// React Native bridge for SceneViewSwift's `ARRecorder` — record-only
|
|
297
|
+
/// AR session capture via ReplayKit. Exposed to JS as `NativeModules.RNARRecorder`.
|
|
298
|
+
///
|
|
299
|
+
/// iOS-only; the JS `ARRecorder` class guards non-iOS platforms before
|
|
300
|
+
/// calling into this module.
|
|
301
|
+
@objc(RNARRecorder)
|
|
302
|
+
class RNARRecorder: NSObject {
|
|
303
|
+
|
|
304
|
+
/// A single recorder instance shared by the JS `ARRecorder` API —
|
|
305
|
+
/// ReplayKit's `RPScreenRecorder` is itself a process-wide singleton,
|
|
306
|
+
/// so multiple JS instances still drive one underlying recorder.
|
|
307
|
+
@MainActor private lazy var recorder = ARRecorder()
|
|
308
|
+
|
|
309
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
310
|
+
return true
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@objc func start(
|
|
314
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
315
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
316
|
+
) {
|
|
317
|
+
Task { @MainActor in
|
|
318
|
+
do {
|
|
319
|
+
try await recorder.startRecording()
|
|
320
|
+
resolve(nil)
|
|
321
|
+
} catch {
|
|
322
|
+
reject("AR_RECORDER_START_FAILED", error.localizedDescription, error)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@objc func stop(
|
|
328
|
+
_ outputPath: String?,
|
|
329
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
330
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
331
|
+
) {
|
|
332
|
+
Task { @MainActor in
|
|
333
|
+
do {
|
|
334
|
+
let outputURL = outputPath.map { URL(fileURLWithPath: $0) }
|
|
335
|
+
let url = try await recorder.stopRecording(outputURL: outputURL)
|
|
336
|
+
resolve(url.path)
|
|
337
|
+
} catch {
|
|
338
|
+
reject("AR_RECORDER_STOP_FAILED", error.localizedDescription, error)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@objc func saveToPhotoLibrary(
|
|
344
|
+
_ movPath: String,
|
|
345
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
346
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
347
|
+
) {
|
|
348
|
+
Task { @MainActor in
|
|
349
|
+
do {
|
|
350
|
+
try await ARRecorder.saveToPhotoLibrary(URL(fileURLWithPath: movPath))
|
|
351
|
+
resolve(nil)
|
|
352
|
+
} catch {
|
|
353
|
+
reject("AR_RECORDER_SAVE_FAILED", error.localizedDescription, error)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.SceneView = exports.ARSceneView = void 0;
|
|
6
|
+
exports.SceneView = exports.ARSceneView = exports.ARRecorder = void 0;
|
|
7
7
|
var _react = _interopRequireDefault(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -25,6 +25,16 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
25
25
|
// Props
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Camera interaction mode for a {@link SceneView} (v4.3.0).
|
|
30
|
+
*
|
|
31
|
+
* Platform support:
|
|
32
|
+
* - **iOS**: all three modes are wired through `.cameraControls(_:)`.
|
|
33
|
+
* - **Android**: `'orbit'` is the default; `'pan'` / `'firstPerson'` fall
|
|
34
|
+
* back to orbit (the per-mode switch is an iOS-first v4.3.0 addition —
|
|
35
|
+
* the Android side is tracked in issue #1051).
|
|
36
|
+
*/
|
|
37
|
+
|
|
28
38
|
// ---------------------------------------------------------------------------
|
|
29
39
|
// Native components (only available on Android and iOS)
|
|
30
40
|
// ---------------------------------------------------------------------------
|
|
@@ -99,5 +109,74 @@ const ARSceneView = props => {
|
|
|
99
109
|
}
|
|
100
110
|
return /*#__PURE__*/_react.default.createElement(NativeARSceneView, props);
|
|
101
111
|
};
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// AR recording (v4.3.0 — iOS via ReplayKit, see issue #1053)
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
/** Native module backing {@link ARRecorder}. Present only on iOS. */
|
|
102
118
|
exports.ARSceneView = ARSceneView;
|
|
119
|
+
const NativeARRecorder = _reactNative.NativeModules.RNARRecorder;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Records an AR session to a video file (v4.3.0).
|
|
123
|
+
*
|
|
124
|
+
* iOS port of SceneViewSwift's `ARRecorder` — record-only via ReplayKit,
|
|
125
|
+
* producing a QuickTime `.mov`.
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* const recorder = new ARRecorder();
|
|
129
|
+
* await recorder.start();
|
|
130
|
+
* // ... later ...
|
|
131
|
+
* const path = await recorder.stop();
|
|
132
|
+
* await recorder.saveToPhotoLibrary(path);
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* Platform support:
|
|
136
|
+
* - **iOS**: full support via `RPScreenRecorder`.
|
|
137
|
+
* - **Android**: not yet bridged. ARCore session recording produces a
|
|
138
|
+
* replayable dataset (not a video) and needs deeper `Session`/`Frame`
|
|
139
|
+
* access than the Fabric bridge exposes. Every method rejects with an
|
|
140
|
+
* error on Android until issue #1051 lands the Android side.
|
|
141
|
+
*/
|
|
142
|
+
class ARRecorder {
|
|
143
|
+
/** `true` when {@link ARRecorder} is supported on the current platform. */
|
|
144
|
+
static get isSupported() {
|
|
145
|
+
return _reactNative.Platform.OS === 'ios' && NativeARRecorder != null;
|
|
146
|
+
}
|
|
147
|
+
rejectUnsupported() {
|
|
148
|
+
return Promise.reject(new Error('ARRecorder is currently only supported on iOS. Android AR session ' + 'recording is tracked in issue #1051.'));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Starts an AR session recording. */
|
|
152
|
+
start() {
|
|
153
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
154
|
+
return this.rejectUnsupported();
|
|
155
|
+
}
|
|
156
|
+
return NativeARRecorder.start();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Stops the in-progress recording and resolves with the path of the
|
|
161
|
+
* written `.mov` file.
|
|
162
|
+
*
|
|
163
|
+
* @param outputPath optional destination path; when omitted the native
|
|
164
|
+
* side picks a temp location.
|
|
165
|
+
*/
|
|
166
|
+
stop(outputPath) {
|
|
167
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
168
|
+
return this.rejectUnsupported();
|
|
169
|
+
}
|
|
170
|
+
return NativeARRecorder.stop(outputPath ?? null);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Saves a recorded `.mov` file to the device's photo library (iOS). */
|
|
174
|
+
saveToPhotoLibrary(movPath) {
|
|
175
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
176
|
+
return this.rejectUnsupported();
|
|
177
|
+
}
|
|
178
|
+
return NativeARRecorder.saveToPhotoLibrary(movPath);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
exports.ARRecorder = ARRecorder;
|
|
103
182
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","isNativeAvailable","Platform","OS","NativeSceneView","requireNativeComponent","NativeARSceneView","UnsupportedView","name","createElement","View","style","fallbackStyles","container","Text","text","StyleSheet","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","exports","ARSceneView"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","isNativeAvailable","Platform","OS","NativeSceneView","requireNativeComponent","NativeARSceneView","UnsupportedView","name","createElement","View","style","fallbackStyles","container","Text","text","StyleSheet","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","exports","ARSceneView","NativeARRecorder","NativeModules","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { requireNativeComponent, Platform, View, Text, StyleSheet } from 'react-native';
|
|
2
|
+
import { requireNativeComponent, NativeModules, Platform, View, Text, StyleSheet } from 'react-native';
|
|
3
3
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Node type interfaces
|
|
@@ -19,6 +19,16 @@ import { requireNativeComponent, Platform, View, Text, StyleSheet } from 'react-
|
|
|
19
19
|
// Props
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Camera interaction mode for a {@link SceneView} (v4.3.0).
|
|
24
|
+
*
|
|
25
|
+
* Platform support:
|
|
26
|
+
* - **iOS**: all three modes are wired through `.cameraControls(_:)`.
|
|
27
|
+
* - **Android**: `'orbit'` is the default; `'pan'` / `'firstPerson'` fall
|
|
28
|
+
* back to orbit (the per-mode switch is an iOS-first v4.3.0 addition —
|
|
29
|
+
* the Android side is tracked in issue #1051).
|
|
30
|
+
*/
|
|
31
|
+
|
|
22
32
|
// ---------------------------------------------------------------------------
|
|
23
33
|
// Native components (only available on Android and iOS)
|
|
24
34
|
// ---------------------------------------------------------------------------
|
|
@@ -92,4 +102,73 @@ export const ARSceneView = props => {
|
|
|
92
102
|
}
|
|
93
103
|
return /*#__PURE__*/React.createElement(NativeARSceneView, props);
|
|
94
104
|
};
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// AR recording (v4.3.0 — iOS via ReplayKit, see issue #1053)
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
/** Native module backing {@link ARRecorder}. Present only on iOS. */
|
|
111
|
+
|
|
112
|
+
const NativeARRecorder = NativeModules.RNARRecorder;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Records an AR session to a video file (v4.3.0).
|
|
116
|
+
*
|
|
117
|
+
* iOS port of SceneViewSwift's `ARRecorder` — record-only via ReplayKit,
|
|
118
|
+
* producing a QuickTime `.mov`.
|
|
119
|
+
*
|
|
120
|
+
* ```ts
|
|
121
|
+
* const recorder = new ARRecorder();
|
|
122
|
+
* await recorder.start();
|
|
123
|
+
* // ... later ...
|
|
124
|
+
* const path = await recorder.stop();
|
|
125
|
+
* await recorder.saveToPhotoLibrary(path);
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* Platform support:
|
|
129
|
+
* - **iOS**: full support via `RPScreenRecorder`.
|
|
130
|
+
* - **Android**: not yet bridged. ARCore session recording produces a
|
|
131
|
+
* replayable dataset (not a video) and needs deeper `Session`/`Frame`
|
|
132
|
+
* access than the Fabric bridge exposes. Every method rejects with an
|
|
133
|
+
* error on Android until issue #1051 lands the Android side.
|
|
134
|
+
*/
|
|
135
|
+
export class ARRecorder {
|
|
136
|
+
/** `true` when {@link ARRecorder} is supported on the current platform. */
|
|
137
|
+
static get isSupported() {
|
|
138
|
+
return Platform.OS === 'ios' && NativeARRecorder != null;
|
|
139
|
+
}
|
|
140
|
+
rejectUnsupported() {
|
|
141
|
+
return Promise.reject(new Error('ARRecorder is currently only supported on iOS. Android AR session ' + 'recording is tracked in issue #1051.'));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Starts an AR session recording. */
|
|
145
|
+
start() {
|
|
146
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
147
|
+
return this.rejectUnsupported();
|
|
148
|
+
}
|
|
149
|
+
return NativeARRecorder.start();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Stops the in-progress recording and resolves with the path of the
|
|
154
|
+
* written `.mov` file.
|
|
155
|
+
*
|
|
156
|
+
* @param outputPath optional destination path; when omitted the native
|
|
157
|
+
* side picks a temp location.
|
|
158
|
+
*/
|
|
159
|
+
stop(outputPath) {
|
|
160
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
161
|
+
return this.rejectUnsupported();
|
|
162
|
+
}
|
|
163
|
+
return NativeARRecorder.stop(outputPath ?? null);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Saves a recorded `.mov` file to the device's photo library (iOS). */
|
|
167
|
+
saveToPhotoLibrary(movPath) {
|
|
168
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
169
|
+
return this.rejectUnsupported();
|
|
170
|
+
}
|
|
171
|
+
return NativeARRecorder.saveToPhotoLibrary(movPath);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
95
174
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","requireNativeComponent","Platform","View","Text","StyleSheet","isNativeAvailable","OS","NativeSceneView","NativeARSceneView","UnsupportedView","name","createElement","style","fallbackStyles","container","text","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","ARSceneView"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACEC,sBAAsB,EACtBC,QAAQ,EAGRC,IAAI,EACJC,IAAI,EACJC,UAAU,QACL,cAAc;;AAErB;AACA;AACA;;AAEA;;AAiBA;;AAmBA;;AASA;AACA;AACA;;AAkBA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["React","requireNativeComponent","NativeModules","Platform","View","Text","StyleSheet","isNativeAvailable","OS","NativeSceneView","NativeARSceneView","UnsupportedView","name","createElement","style","fallbackStyles","container","text","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","ARSceneView","NativeARRecorder","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -56,6 +56,16 @@ export interface PlaneDetectedEvent {
|
|
|
56
56
|
center: [number, number, number];
|
|
57
57
|
extent: [number, number];
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Camera interaction mode for a {@link SceneView} (v4.3.0).
|
|
61
|
+
*
|
|
62
|
+
* Platform support:
|
|
63
|
+
* - **iOS**: all three modes are wired through `.cameraControls(_:)`.
|
|
64
|
+
* - **Android**: `'orbit'` is the default; `'pan'` / `'firstPerson'` fall
|
|
65
|
+
* back to orbit (the per-mode switch is an iOS-first v4.3.0 addition —
|
|
66
|
+
* the Android side is tracked in issue #1051).
|
|
67
|
+
*/
|
|
68
|
+
export type CameraControlMode = 'orbit' | 'pan' | 'firstPerson';
|
|
59
69
|
export interface SceneViewProps {
|
|
60
70
|
style?: ViewStyle;
|
|
61
71
|
/** HDR environment asset path (e.g. "environments/studio.hdr"). */
|
|
@@ -68,6 +78,19 @@ export interface SceneViewProps {
|
|
|
68
78
|
lightNodes?: LightNode[];
|
|
69
79
|
/** Enable default orbit camera controls. Default: true. */
|
|
70
80
|
cameraOrbit?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Camera interaction mode (v4.3.0). Default: `'orbit'`.
|
|
83
|
+
*
|
|
84
|
+
* `'pan'` and `'firstPerson'` are iOS-only; on Android they fall back to
|
|
85
|
+
* orbit. See {@link CameraControlMode}.
|
|
86
|
+
*/
|
|
87
|
+
cameraControlMode?: CameraControlMode;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the scene auto-centres its content on the first stable frame
|
|
90
|
+
* (v4.3.0). Default: `true`. iOS-first; the Android side is tracked in
|
|
91
|
+
* issue #1051.
|
|
92
|
+
*/
|
|
93
|
+
autoCenterContent?: boolean;
|
|
71
94
|
/** Called when the user taps inside the scene. */
|
|
72
95
|
onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
|
|
73
96
|
}
|
|
@@ -103,4 +126,42 @@ export declare const SceneView: React.FC<SceneViewProps>;
|
|
|
103
126
|
* ```
|
|
104
127
|
*/
|
|
105
128
|
export declare const ARSceneView: React.FC<ARSceneViewProps>;
|
|
129
|
+
/**
|
|
130
|
+
* Records an AR session to a video file (v4.3.0).
|
|
131
|
+
*
|
|
132
|
+
* iOS port of SceneViewSwift's `ARRecorder` — record-only via ReplayKit,
|
|
133
|
+
* producing a QuickTime `.mov`.
|
|
134
|
+
*
|
|
135
|
+
* ```ts
|
|
136
|
+
* const recorder = new ARRecorder();
|
|
137
|
+
* await recorder.start();
|
|
138
|
+
* // ... later ...
|
|
139
|
+
* const path = await recorder.stop();
|
|
140
|
+
* await recorder.saveToPhotoLibrary(path);
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* Platform support:
|
|
144
|
+
* - **iOS**: full support via `RPScreenRecorder`.
|
|
145
|
+
* - **Android**: not yet bridged. ARCore session recording produces a
|
|
146
|
+
* replayable dataset (not a video) and needs deeper `Session`/`Frame`
|
|
147
|
+
* access than the Fabric bridge exposes. Every method rejects with an
|
|
148
|
+
* error on Android until issue #1051 lands the Android side.
|
|
149
|
+
*/
|
|
150
|
+
export declare class ARRecorder {
|
|
151
|
+
/** `true` when {@link ARRecorder} is supported on the current platform. */
|
|
152
|
+
static get isSupported(): boolean;
|
|
153
|
+
private rejectUnsupported;
|
|
154
|
+
/** Starts an AR session recording. */
|
|
155
|
+
start(): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Stops the in-progress recording and resolves with the path of the
|
|
158
|
+
* written `.mov` file.
|
|
159
|
+
*
|
|
160
|
+
* @param outputPath optional destination path; when omitted the native
|
|
161
|
+
* side picks a temp location.
|
|
162
|
+
*/
|
|
163
|
+
stop(outputPath?: string): Promise<string>;
|
|
164
|
+
/** Saves a recorded `.mov` file to the device's photo library (iOS). */
|
|
165
|
+
saveToPhotoLibrary(movPath: string): Promise<void>;
|
|
166
|
+
}
|
|
106
167
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sceneview-sdk/react-native",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "React Native bindings for SceneView — 3D and AR scenes powered by Filament (Android) and RealityKit (iOS)",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
package/src/index.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import {
|
|
3
3
|
requireNativeComponent,
|
|
4
|
+
NativeModules,
|
|
4
5
|
Platform,
|
|
5
6
|
type ViewStyle,
|
|
6
7
|
type NativeSyntheticEvent,
|
|
@@ -82,6 +83,17 @@ export interface PlaneDetectedEvent {
|
|
|
82
83
|
// Props
|
|
83
84
|
// ---------------------------------------------------------------------------
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Camera interaction mode for a {@link SceneView} (v4.3.0).
|
|
88
|
+
*
|
|
89
|
+
* Platform support:
|
|
90
|
+
* - **iOS**: all three modes are wired through `.cameraControls(_:)`.
|
|
91
|
+
* - **Android**: `'orbit'` is the default; `'pan'` / `'firstPerson'` fall
|
|
92
|
+
* back to orbit (the per-mode switch is an iOS-first v4.3.0 addition —
|
|
93
|
+
* the Android side is tracked in issue #1051).
|
|
94
|
+
*/
|
|
95
|
+
export type CameraControlMode = 'orbit' | 'pan' | 'firstPerson';
|
|
96
|
+
|
|
85
97
|
export interface SceneViewProps {
|
|
86
98
|
style?: ViewStyle;
|
|
87
99
|
|
|
@@ -98,6 +110,21 @@ export interface SceneViewProps {
|
|
|
98
110
|
/** Enable default orbit camera controls. Default: true. */
|
|
99
111
|
cameraOrbit?: boolean;
|
|
100
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Camera interaction mode (v4.3.0). Default: `'orbit'`.
|
|
115
|
+
*
|
|
116
|
+
* `'pan'` and `'firstPerson'` are iOS-only; on Android they fall back to
|
|
117
|
+
* orbit. See {@link CameraControlMode}.
|
|
118
|
+
*/
|
|
119
|
+
cameraControlMode?: CameraControlMode;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Whether the scene auto-centres its content on the first stable frame
|
|
123
|
+
* (v4.3.0). Default: `true`. iOS-first; the Android side is tracked in
|
|
124
|
+
* issue #1051.
|
|
125
|
+
*/
|
|
126
|
+
autoCenterContent?: boolean;
|
|
127
|
+
|
|
101
128
|
/** Called when the user taps inside the scene. */
|
|
102
129
|
onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
|
|
103
130
|
}
|
|
@@ -190,3 +217,84 @@ export const ARSceneView: React.FC<ARSceneViewProps> = (props) => {
|
|
|
190
217
|
}
|
|
191
218
|
return <NativeARSceneView {...props} />;
|
|
192
219
|
};
|
|
220
|
+
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// AR recording (v4.3.0 — iOS via ReplayKit, see issue #1053)
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
/** Native module backing {@link ARRecorder}. Present only on iOS. */
|
|
226
|
+
interface RNARRecorderModule {
|
|
227
|
+
start(): Promise<void>;
|
|
228
|
+
stop(outputPath?: string | null): Promise<string>;
|
|
229
|
+
saveToPhotoLibrary(movPath: string): Promise<void>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const NativeARRecorder: RNARRecorderModule | undefined =
|
|
233
|
+
NativeModules.RNARRecorder;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Records an AR session to a video file (v4.3.0).
|
|
237
|
+
*
|
|
238
|
+
* iOS port of SceneViewSwift's `ARRecorder` — record-only via ReplayKit,
|
|
239
|
+
* producing a QuickTime `.mov`.
|
|
240
|
+
*
|
|
241
|
+
* ```ts
|
|
242
|
+
* const recorder = new ARRecorder();
|
|
243
|
+
* await recorder.start();
|
|
244
|
+
* // ... later ...
|
|
245
|
+
* const path = await recorder.stop();
|
|
246
|
+
* await recorder.saveToPhotoLibrary(path);
|
|
247
|
+
* ```
|
|
248
|
+
*
|
|
249
|
+
* Platform support:
|
|
250
|
+
* - **iOS**: full support via `RPScreenRecorder`.
|
|
251
|
+
* - **Android**: not yet bridged. ARCore session recording produces a
|
|
252
|
+
* replayable dataset (not a video) and needs deeper `Session`/`Frame`
|
|
253
|
+
* access than the Fabric bridge exposes. Every method rejects with an
|
|
254
|
+
* error on Android until issue #1051 lands the Android side.
|
|
255
|
+
*/
|
|
256
|
+
export class ARRecorder {
|
|
257
|
+
/** `true` when {@link ARRecorder} is supported on the current platform. */
|
|
258
|
+
static get isSupported(): boolean {
|
|
259
|
+
return Platform.OS === 'ios' && NativeARRecorder != null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private rejectUnsupported(): Promise<never> {
|
|
263
|
+
return Promise.reject(
|
|
264
|
+
new Error(
|
|
265
|
+
'ARRecorder is currently only supported on iOS. Android AR session ' +
|
|
266
|
+
'recording is tracked in issue #1051.'
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Starts an AR session recording. */
|
|
272
|
+
start(): Promise<void> {
|
|
273
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
274
|
+
return this.rejectUnsupported();
|
|
275
|
+
}
|
|
276
|
+
return NativeARRecorder.start();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Stops the in-progress recording and resolves with the path of the
|
|
281
|
+
* written `.mov` file.
|
|
282
|
+
*
|
|
283
|
+
* @param outputPath optional destination path; when omitted the native
|
|
284
|
+
* side picks a temp location.
|
|
285
|
+
*/
|
|
286
|
+
stop(outputPath?: string): Promise<string> {
|
|
287
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
288
|
+
return this.rejectUnsupported();
|
|
289
|
+
}
|
|
290
|
+
return NativeARRecorder.stop(outputPath ?? null);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Saves a recorded `.mov` file to the device's photo library (iOS). */
|
|
294
|
+
saveToPhotoLibrary(movPath: string): Promise<void> {
|
|
295
|
+
if (!ARRecorder.isSupported || !NativeARRecorder) {
|
|
296
|
+
return this.rejectUnsupported();
|
|
297
|
+
}
|
|
298
|
+
return NativeARRecorder.saveToPhotoLibrary(movPath);
|
|
299
|
+
}
|
|
300
|
+
}
|