@sceneview-sdk/react-native 3.6.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 +201 -0
- package/README.md +139 -0
- package/android/build.gradle.kts +36 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/io/github/sceneview/reactnative/ARSceneViewManager.kt +125 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewManager.kt +137 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewModule.kt +31 -0
- package/ios/RNSceneViewManager.m +24 -0
- package/ios/SceneViewModule.swift +259 -0
- package/lib/commonjs/index.js +103 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +95 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/src/index.d.ts +98 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +71 -0
- package/react-native-sceneview.podspec +21 -0
- package/src/index.tsx +184 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Objective-C bridge — required by React Native to register view managers.
|
|
2
|
+
|
|
3
|
+
#import <React/RCTViewManager.h>
|
|
4
|
+
|
|
5
|
+
@interface RCT_EXTERN_MODULE(RNSceneViewManager, RCTViewManager)
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_VIEW_PROPERTY(environment, NSString)
|
|
8
|
+
RCT_EXPORT_VIEW_PROPERTY(modelNodes, NSArray)
|
|
9
|
+
RCT_EXPORT_VIEW_PROPERTY(cameraOrbit, BOOL)
|
|
10
|
+
RCT_EXPORT_VIEW_PROPERTY(onTap, RCTDirectEventBlock)
|
|
11
|
+
|
|
12
|
+
@end
|
|
13
|
+
|
|
14
|
+
@interface RCT_EXTERN_MODULE(RNARSceneViewManager, RCTViewManager)
|
|
15
|
+
|
|
16
|
+
RCT_EXPORT_VIEW_PROPERTY(environment, NSString)
|
|
17
|
+
RCT_EXPORT_VIEW_PROPERTY(modelNodes, NSArray)
|
|
18
|
+
RCT_EXPORT_VIEW_PROPERTY(planeDetection, BOOL)
|
|
19
|
+
RCT_EXPORT_VIEW_PROPERTY(depthOcclusion, BOOL)
|
|
20
|
+
RCT_EXPORT_VIEW_PROPERTY(instantPlacement, BOOL)
|
|
21
|
+
RCT_EXPORT_VIEW_PROPERTY(onTap, RCTDirectEventBlock)
|
|
22
|
+
RCT_EXPORT_VIEW_PROPERTY(onPlaneDetected, RCTDirectEventBlock)
|
|
23
|
+
|
|
24
|
+
@end
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import React
|
|
3
|
+
import SceneViewSwift
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
// MARK: - Shared model data
|
|
7
|
+
|
|
8
|
+
struct RNModelData: Identifiable, Equatable {
|
|
9
|
+
let id = UUID()
|
|
10
|
+
let path: String
|
|
11
|
+
let scale: SIMD3<Float>
|
|
12
|
+
let position: SIMD3<Float>
|
|
13
|
+
let animation: String?
|
|
14
|
+
|
|
15
|
+
static func == (lhs: RNModelData, rhs: RNModelData) -> Bool {
|
|
16
|
+
lhs.id == rhs.id
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// MARK: - SceneView (3D)
|
|
21
|
+
|
|
22
|
+
/// RCTViewManager subclass that bridges React Native's `<RNSceneView>`
|
|
23
|
+
/// to SceneViewSwift's `SceneView` (RealityKit-based).
|
|
24
|
+
@objc(RNSceneViewManager)
|
|
25
|
+
class RNSceneViewManager: RCTViewManager {
|
|
26
|
+
|
|
27
|
+
override func view() -> UIView! {
|
|
28
|
+
return RNSceneViewWrapper()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Observable state model shared between React props and SwiftUI view.
|
|
37
|
+
@MainActor
|
|
38
|
+
class RNSceneState: ObservableObject {
|
|
39
|
+
@Published var models: [RNModelData] = []
|
|
40
|
+
@Published var environmentPath: String?
|
|
41
|
+
@Published var cameraOrbit: Bool = true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// UIView wrapper that hosts a SwiftUI `SceneView` via UIHostingController.
|
|
45
|
+
class RNSceneViewWrapper: UIView {
|
|
46
|
+
|
|
47
|
+
private var hostingController: UIHostingController<RNSceneViewContent>?
|
|
48
|
+
private let sceneState = RNSceneState()
|
|
49
|
+
|
|
50
|
+
/// Event callback for tap events.
|
|
51
|
+
@objc var onTap: RCTDirectEventBlock?
|
|
52
|
+
|
|
53
|
+
override init(frame: CGRect) {
|
|
54
|
+
super.init(frame: frame)
|
|
55
|
+
setupView()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
required init?(coder: NSCoder) {
|
|
59
|
+
super.init(coder: coder)
|
|
60
|
+
setupView()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private func setupView() {
|
|
64
|
+
let content = RNSceneViewContent(state: sceneState)
|
|
65
|
+
let hosting = UIHostingController(rootView: content)
|
|
66
|
+
hosting.view.frame = bounds
|
|
67
|
+
hosting.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
68
|
+
addSubview(hosting.view)
|
|
69
|
+
hostingController = hosting
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// MARK: - React props
|
|
73
|
+
|
|
74
|
+
@objc var environment: String? {
|
|
75
|
+
didSet {
|
|
76
|
+
Task { @MainActor in
|
|
77
|
+
sceneState.environmentPath = environment
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc var modelNodes: [[String: Any]]? {
|
|
83
|
+
didSet {
|
|
84
|
+
Task { @MainActor in
|
|
85
|
+
sceneState.models = modelNodes?.compactMap { dict -> RNModelData? in
|
|
86
|
+
guard let src = dict["src"] as? String else { return nil }
|
|
87
|
+
let scale: SIMD3<Float>
|
|
88
|
+
if let arr = dict["scale"] as? [NSNumber], arr.count >= 3 {
|
|
89
|
+
scale = SIMD3(arr[0].floatValue, arr[1].floatValue, arr[2].floatValue)
|
|
90
|
+
} else if let s = (dict["scale"] as? NSNumber)?.floatValue {
|
|
91
|
+
scale = SIMD3(repeating: s)
|
|
92
|
+
} else {
|
|
93
|
+
scale = SIMD3(repeating: 1.0)
|
|
94
|
+
}
|
|
95
|
+
let position: SIMD3<Float>
|
|
96
|
+
if let arr = dict["position"] as? [NSNumber], arr.count >= 3 {
|
|
97
|
+
position = SIMD3(arr[0].floatValue, arr[1].floatValue, arr[2].floatValue)
|
|
98
|
+
} else {
|
|
99
|
+
position = .zero
|
|
100
|
+
}
|
|
101
|
+
let animation = dict["animation"] as? String
|
|
102
|
+
return RNModelData(path: src, scale: scale, position: position, animation: animation)
|
|
103
|
+
} ?? []
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@objc var cameraOrbit: Bool = true {
|
|
109
|
+
didSet {
|
|
110
|
+
Task { @MainActor in
|
|
111
|
+
sceneState.cameraOrbit = cameraOrbit
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// SwiftUI content view rendering SceneViewSwift.SceneView.
|
|
118
|
+
struct RNSceneViewContent: View {
|
|
119
|
+
@ObservedObject var state: RNSceneState
|
|
120
|
+
|
|
121
|
+
var body: some View {
|
|
122
|
+
SceneView {
|
|
123
|
+
ForEach(state.models) { model in
|
|
124
|
+
ModelNode(model.path)
|
|
125
|
+
.position(model.position)
|
|
126
|
+
.scale(model.scale)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// MARK: - ARSceneView
|
|
133
|
+
|
|
134
|
+
/// RCTViewManager subclass that bridges React Native's `<RNARSceneView>`
|
|
135
|
+
/// to SceneViewSwift's `ARSceneView` (ARKit + RealityKit).
|
|
136
|
+
@objc(RNARSceneViewManager)
|
|
137
|
+
class RNARSceneViewManager: RCTViewManager {
|
|
138
|
+
|
|
139
|
+
override func view() -> UIView! {
|
|
140
|
+
return RNARSceneViewWrapper()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Observable state for AR scene configuration.
|
|
149
|
+
@MainActor
|
|
150
|
+
class RNARSceneState: ObservableObject {
|
|
151
|
+
@Published var models: [RNModelData] = []
|
|
152
|
+
@Published var planeDetection: Bool = true
|
|
153
|
+
@Published var depthOcclusion: Bool = false
|
|
154
|
+
@Published var instantPlacement: Bool = false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// UIView wrapper that hosts a SwiftUI `ARSceneView` via UIHostingController.
|
|
158
|
+
class RNARSceneViewWrapper: UIView {
|
|
159
|
+
|
|
160
|
+
private var hostingController: UIHostingController<RNARSceneViewContent>?
|
|
161
|
+
private let sceneState = RNARSceneState()
|
|
162
|
+
|
|
163
|
+
/// Event callback for tap events.
|
|
164
|
+
@objc var onTap: RCTDirectEventBlock?
|
|
165
|
+
|
|
166
|
+
/// Event callback for plane detection events.
|
|
167
|
+
@objc var onPlaneDetected: RCTDirectEventBlock?
|
|
168
|
+
|
|
169
|
+
override init(frame: CGRect) {
|
|
170
|
+
super.init(frame: frame)
|
|
171
|
+
setupView()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
required init?(coder: NSCoder) {
|
|
175
|
+
super.init(coder: coder)
|
|
176
|
+
setupView()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func setupView() {
|
|
180
|
+
let content = RNARSceneViewContent(state: sceneState)
|
|
181
|
+
let hosting = UIHostingController(rootView: content)
|
|
182
|
+
hosting.view.frame = bounds
|
|
183
|
+
hosting.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
184
|
+
addSubview(hosting.view)
|
|
185
|
+
hostingController = hosting
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// MARK: - React props
|
|
189
|
+
|
|
190
|
+
@objc var planeDetection: Bool = true {
|
|
191
|
+
didSet {
|
|
192
|
+
Task { @MainActor in
|
|
193
|
+
sceneState.planeDetection = planeDetection
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@objc var depthOcclusion: Bool = false {
|
|
199
|
+
didSet {
|
|
200
|
+
Task { @MainActor in
|
|
201
|
+
sceneState.depthOcclusion = depthOcclusion
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@objc var instantPlacement: Bool = false {
|
|
207
|
+
didSet {
|
|
208
|
+
Task { @MainActor in
|
|
209
|
+
sceneState.instantPlacement = instantPlacement
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@objc var modelNodes: [[String: Any]]? {
|
|
215
|
+
didSet {
|
|
216
|
+
Task { @MainActor in
|
|
217
|
+
sceneState.models = modelNodes?.compactMap { dict -> RNModelData? in
|
|
218
|
+
guard let src = dict["src"] as? String else { return nil }
|
|
219
|
+
let scale: SIMD3<Float>
|
|
220
|
+
if let arr = dict["scale"] as? [NSNumber], arr.count >= 3 {
|
|
221
|
+
scale = SIMD3(arr[0].floatValue, arr[1].floatValue, arr[2].floatValue)
|
|
222
|
+
} else if let s = (dict["scale"] as? NSNumber)?.floatValue {
|
|
223
|
+
scale = SIMD3(repeating: s)
|
|
224
|
+
} else {
|
|
225
|
+
scale = SIMD3(repeating: 1.0)
|
|
226
|
+
}
|
|
227
|
+
let position: SIMD3<Float>
|
|
228
|
+
if let arr = dict["position"] as? [NSNumber], arr.count >= 3 {
|
|
229
|
+
position = SIMD3(arr[0].floatValue, arr[1].floatValue, arr[2].floatValue)
|
|
230
|
+
} else {
|
|
231
|
+
position = .zero
|
|
232
|
+
}
|
|
233
|
+
let animation = dict["animation"] as? String
|
|
234
|
+
return RNModelData(path: src, scale: scale, position: position, animation: animation)
|
|
235
|
+
} ?? []
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@objc var environment: String? {
|
|
241
|
+
didSet {
|
|
242
|
+
// AR scenes use camera feed; environment affects lighting only.
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// SwiftUI content view rendering SceneViewSwift.ARSceneView.
|
|
248
|
+
struct RNARSceneViewContent: View {
|
|
249
|
+
@ObservedObject var state: RNARSceneState
|
|
250
|
+
|
|
251
|
+
var body: some View {
|
|
252
|
+
ARSceneView { anchor in
|
|
253
|
+
ForEach(state.models) { model in
|
|
254
|
+
ModelNode(model.path)
|
|
255
|
+
.scale(model.scale)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.SceneView = exports.ARSceneView = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Node type interfaces
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/** A 3D model loaded from a .glb / .gltf file. */
|
|
15
|
+
|
|
16
|
+
/** A procedural geometry node (box, sphere, cylinder, plane). */
|
|
17
|
+
|
|
18
|
+
/** A light source in the scene. */
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Event payloads
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Props
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Native components (only available on Android and iOS)
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
const isNativeAvailable = _reactNative.Platform.OS === 'android' || _reactNative.Platform.OS === 'ios';
|
|
33
|
+
const NativeSceneView = isNativeAvailable ? (0, _reactNative.requireNativeComponent)('RNSceneView') : null;
|
|
34
|
+
const NativeARSceneView = isNativeAvailable ? (0, _reactNative.requireNativeComponent)('RNARSceneView') : null;
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Fallback for unsupported platforms
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
const UnsupportedView = ({
|
|
41
|
+
name
|
|
42
|
+
}) => /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
43
|
+
style: fallbackStyles.container
|
|
44
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
45
|
+
style: fallbackStyles.text
|
|
46
|
+
}, name, " is not supported on this platform"));
|
|
47
|
+
const fallbackStyles = _reactNative.StyleSheet.create({
|
|
48
|
+
container: {
|
|
49
|
+
flex: 1,
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
backgroundColor: '#1a1a2e'
|
|
53
|
+
},
|
|
54
|
+
text: {
|
|
55
|
+
color: '#aaa',
|
|
56
|
+
fontSize: 16
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Public components
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A 3D scene rendered with Filament (Android) or RealityKit (iOS).
|
|
66
|
+
*
|
|
67
|
+
* ```tsx
|
|
68
|
+
* <SceneView
|
|
69
|
+
* environment="environments/studio.hdr"
|
|
70
|
+
* modelNodes={[{ src: "models/robot.glb" }]}
|
|
71
|
+
* />
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
const SceneView = props => {
|
|
75
|
+
if (!NativeSceneView) {
|
|
76
|
+
return /*#__PURE__*/_react.default.createElement(UnsupportedView, {
|
|
77
|
+
name: "SceneView"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return /*#__PURE__*/_react.default.createElement(NativeSceneView, props);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* An augmented-reality scene using ARCore (Android) or ARKit (iOS).
|
|
85
|
+
*
|
|
86
|
+
* ```tsx
|
|
87
|
+
* <ARSceneView
|
|
88
|
+
* planeDetection
|
|
89
|
+
* modelNodes={[{ src: "models/chair.glb", position: [0, 0, -1] }]}
|
|
90
|
+
* />
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
exports.SceneView = SceneView;
|
|
94
|
+
const ARSceneView = props => {
|
|
95
|
+
if (!NativeARSceneView) {
|
|
96
|
+
return /*#__PURE__*/_react.default.createElement(UnsupportedView, {
|
|
97
|
+
name: "ARSceneView"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return /*#__PURE__*/_react.default.createElement(NativeARSceneView, props);
|
|
101
|
+
};
|
|
102
|
+
exports.ARSceneView = ARSceneView;
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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;AAQsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAEtB;AACA;AACA;;AAEA;;AAiBA;;AAWA;;AASA;AACA;AACA;;AAkBA;AACA;AACA;;AAoCA;AACA;AACA;;AAEA,MAAMG,iBAAiB,GAAGC,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACC,EAAE,KAAK,KAAK;AAE5E,MAAMC,eAAe,GAAGH,iBAAiB,GACrC,IAAAI,mCAAsB,EAAiB,aAAa,CAAC,GACrD,IAAI;AAER,MAAMC,iBAAiB,GAAGL,iBAAiB,GACvC,IAAAI,mCAAsB,EAAmB,eAAe,CAAC,GACzD,IAAI;;AAER;AACA;AACA;;AAEA,MAAME,eAA2C,GAAGA,CAAC;EAAEC;AAAK,CAAC,kBAC3Dd,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACZ,YAAA,CAAAa,IAAI;EAACC,KAAK,EAAEC,cAAc,CAACC;AAAU,gBACpCnB,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACZ,YAAA,CAAAiB,IAAI;EAACH,KAAK,EAAEC,cAAc,CAACG;AAAK,GAAEP,IAAI,EAAC,oCAAwC,CAC5E,CACP;AAED,MAAMI,cAAc,GAAGI,uBAAU,CAACC,MAAM,CAAC;EACvCJ,SAAS,EAAE;IACTK,IAAI,EAAE,CAAC;IACPC,cAAc,EAAE,QAAQ;IACxBC,UAAU,EAAE,QAAQ;IACpBC,eAAe,EAAE;EACnB,CAAC;EACDN,IAAI,EAAE;IACJO,KAAK,EAAE,MAAM;IACbC,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;;AAEF;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,SAAmC,GAAIC,KAAK,IAAK;EAC5D,IAAI,CAACrB,eAAe,EAAE;IACpB,oBAAOV,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACF,eAAe;MAACC,IAAI,EAAC;IAAW,CAAE,CAAC;EAC7C;EACA,oBAAOd,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACL,eAAe,EAAKqB,KAAQ,CAAC;AACvC,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AATAC,OAAA,CAAAF,SAAA,GAAAA,SAAA;AAUO,MAAMG,WAAuC,GAAIF,KAAK,IAAK;EAChE,IAAI,CAACnB,iBAAiB,EAAE;IACtB,oBAAOZ,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACF,eAAe;MAACC,IAAI,EAAC;IAAa,CAAE,CAAC;EAC/C;EACA,oBAAOd,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACH,iBAAiB,EAAKmB,KAAQ,CAAC;AACzC,CAAC;AAACC,OAAA,CAAAC,WAAA,GAAAA,WAAA","ignoreList":[]}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { requireNativeComponent, Platform, View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Node type interfaces
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
/** A 3D model loaded from a .glb / .gltf file. */
|
|
9
|
+
|
|
10
|
+
/** A procedural geometry node (box, sphere, cylinder, plane). */
|
|
11
|
+
|
|
12
|
+
/** A light source in the scene. */
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Event payloads
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Props
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Native components (only available on Android and iOS)
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
const isNativeAvailable = Platform.OS === 'android' || Platform.OS === 'ios';
|
|
27
|
+
const NativeSceneView = isNativeAvailable ? requireNativeComponent('RNSceneView') : null;
|
|
28
|
+
const NativeARSceneView = isNativeAvailable ? requireNativeComponent('RNARSceneView') : null;
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Fallback for unsupported platforms
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
const UnsupportedView = ({
|
|
35
|
+
name
|
|
36
|
+
}) => /*#__PURE__*/React.createElement(View, {
|
|
37
|
+
style: fallbackStyles.container
|
|
38
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
39
|
+
style: fallbackStyles.text
|
|
40
|
+
}, name, " is not supported on this platform"));
|
|
41
|
+
const fallbackStyles = StyleSheet.create({
|
|
42
|
+
container: {
|
|
43
|
+
flex: 1,
|
|
44
|
+
justifyContent: 'center',
|
|
45
|
+
alignItems: 'center',
|
|
46
|
+
backgroundColor: '#1a1a2e'
|
|
47
|
+
},
|
|
48
|
+
text: {
|
|
49
|
+
color: '#aaa',
|
|
50
|
+
fontSize: 16
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Public components
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A 3D scene rendered with Filament (Android) or RealityKit (iOS).
|
|
60
|
+
*
|
|
61
|
+
* ```tsx
|
|
62
|
+
* <SceneView
|
|
63
|
+
* environment="environments/studio.hdr"
|
|
64
|
+
* modelNodes={[{ src: "models/robot.glb" }]}
|
|
65
|
+
* />
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export const SceneView = props => {
|
|
69
|
+
if (!NativeSceneView) {
|
|
70
|
+
return /*#__PURE__*/React.createElement(UnsupportedView, {
|
|
71
|
+
name: "SceneView"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return /*#__PURE__*/React.createElement(NativeSceneView, props);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* An augmented-reality scene using ARCore (Android) or ARKit (iOS).
|
|
79
|
+
*
|
|
80
|
+
* ```tsx
|
|
81
|
+
* <ARSceneView
|
|
82
|
+
* planeDetection
|
|
83
|
+
* modelNodes={[{ src: "models/chair.glb", position: [0, 0, -1] }]}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export const ARSceneView = props => {
|
|
88
|
+
if (!NativeARSceneView) {
|
|
89
|
+
return /*#__PURE__*/React.createElement(UnsupportedView, {
|
|
90
|
+
name: "ARSceneView"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return /*#__PURE__*/React.createElement(NativeARSceneView, props);
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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;;AAWA;;AASA;AACA;AACA;;AAkBA;AACA;AACA;;AAoCA;AACA;AACA;;AAEA,MAAMC,iBAAiB,GAAGJ,QAAQ,CAACK,EAAE,KAAK,SAAS,IAAIL,QAAQ,CAACK,EAAE,KAAK,KAAK;AAE5E,MAAMC,eAAe,GAAGF,iBAAiB,GACrCL,sBAAsB,CAAiB,aAAa,CAAC,GACrD,IAAI;AAER,MAAMQ,iBAAiB,GAAGH,iBAAiB,GACvCL,sBAAsB,CAAmB,eAAe,CAAC,GACzD,IAAI;;AAER;AACA;AACA;;AAEA,MAAMS,eAA2C,GAAGA,CAAC;EAAEC;AAAK,CAAC,kBAC3DX,KAAA,CAAAY,aAAA,CAACT,IAAI;EAACU,KAAK,EAAEC,cAAc,CAACC;AAAU,gBACpCf,KAAA,CAAAY,aAAA,CAACR,IAAI;EAACS,KAAK,EAAEC,cAAc,CAACE;AAAK,GAAEL,IAAI,EAAC,oCAAwC,CAC5E,CACP;AAED,MAAMG,cAAc,GAAGT,UAAU,CAACY,MAAM,CAAC;EACvCF,SAAS,EAAE;IACTG,IAAI,EAAE,CAAC;IACPC,cAAc,EAAE,QAAQ;IACxBC,UAAU,EAAE,QAAQ;IACpBC,eAAe,EAAE;EACnB,CAAC;EACDL,IAAI,EAAE;IACJM,KAAK,EAAE,MAAM;IACbC,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;;AAEF;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,SAAmC,GAAIC,KAAK,IAAK;EAC5D,IAAI,CAACjB,eAAe,EAAE;IACpB,oBAAOR,KAAA,CAAAY,aAAA,CAACF,eAAe;MAACC,IAAI,EAAC;IAAW,CAAE,CAAC;EAC7C;EACA,oBAAOX,KAAA,CAAAY,aAAA,CAACJ,eAAe,EAAKiB,KAAQ,CAAC;AACvC,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,WAAuC,GAAID,KAAK,IAAK;EAChE,IAAI,CAAChB,iBAAiB,EAAE;IACtB,oBAAOT,KAAA,CAAAY,aAAA,CAACF,eAAe;MAACC,IAAI,EAAC;IAAa,CAAE,CAAC;EAC/C;EACA,oBAAOX,KAAA,CAAAY,aAAA,CAACH,iBAAiB,EAAKgB,KAAQ,CAAC;AACzC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type ViewStyle, type NativeSyntheticEvent } from 'react-native';
|
|
3
|
+
/** A 3D model loaded from a .glb / .gltf file. */
|
|
4
|
+
export interface ModelNode {
|
|
5
|
+
/** Asset path or URL to the glTF/GLB model. */
|
|
6
|
+
src: string;
|
|
7
|
+
/** World-space position [x, y, z]. Default: [0, 0, 0]. */
|
|
8
|
+
position?: [number, number, number];
|
|
9
|
+
/** Euler rotation in degrees [x, y, z]. Default: [0, 0, 0]. */
|
|
10
|
+
rotation?: [number, number, number];
|
|
11
|
+
/** Scale factor. Can be uniform (number) or per-axis [x, y, z]. */
|
|
12
|
+
scale?: number | [number, number, number];
|
|
13
|
+
/**
|
|
14
|
+
* Animation name to play automatically.
|
|
15
|
+
* If provided (non-null), auto-animate is enabled on the native side.
|
|
16
|
+
*/
|
|
17
|
+
animation?: string;
|
|
18
|
+
}
|
|
19
|
+
/** A procedural geometry node (box, sphere, cylinder, plane). */
|
|
20
|
+
export interface GeometryNode {
|
|
21
|
+
type: 'box' | 'sphere' | 'cylinder' | 'plane';
|
|
22
|
+
size?: [number, number, number];
|
|
23
|
+
position?: [number, number, number];
|
|
24
|
+
rotation?: [number, number, number];
|
|
25
|
+
scale?: number | [number, number, number];
|
|
26
|
+
/** Hex color string, e.g. "#FF5500". */
|
|
27
|
+
color?: string;
|
|
28
|
+
}
|
|
29
|
+
/** A light source in the scene. */
|
|
30
|
+
export interface LightNode {
|
|
31
|
+
type: 'directional' | 'point' | 'spot';
|
|
32
|
+
intensity?: number;
|
|
33
|
+
color?: string;
|
|
34
|
+
position?: [number, number, number];
|
|
35
|
+
direction?: [number, number, number];
|
|
36
|
+
}
|
|
37
|
+
export interface TapEvent {
|
|
38
|
+
/** World-space coordinates of the tap. */
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
z: number;
|
|
42
|
+
/** Name of the tapped node, if any. */
|
|
43
|
+
nodeName?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface PlaneDetectedEvent {
|
|
46
|
+
id: string;
|
|
47
|
+
type: 'horizontal' | 'vertical';
|
|
48
|
+
center: [number, number, number];
|
|
49
|
+
extent: [number, number];
|
|
50
|
+
}
|
|
51
|
+
export interface SceneViewProps {
|
|
52
|
+
style?: ViewStyle;
|
|
53
|
+
/** HDR environment asset path (e.g. "environments/studio.hdr"). */
|
|
54
|
+
environment?: string;
|
|
55
|
+
/** Model nodes to render in the scene. */
|
|
56
|
+
modelNodes?: ModelNode[];
|
|
57
|
+
/** Geometry nodes to render in the scene. */
|
|
58
|
+
geometryNodes?: GeometryNode[];
|
|
59
|
+
/** Light nodes in the scene. */
|
|
60
|
+
lightNodes?: LightNode[];
|
|
61
|
+
/** Enable default orbit camera controls. Default: true. */
|
|
62
|
+
cameraOrbit?: boolean;
|
|
63
|
+
/** Called when the user taps inside the scene. */
|
|
64
|
+
onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
|
|
65
|
+
}
|
|
66
|
+
export interface ARSceneViewProps extends SceneViewProps {
|
|
67
|
+
/** Enable plane detection. Default: true. */
|
|
68
|
+
planeDetection?: boolean;
|
|
69
|
+
/** Enable depth occlusion (ARCore Depth API / LiDAR). Default: false. */
|
|
70
|
+
depthOcclusion?: boolean;
|
|
71
|
+
/** Enable instant placement (approximate hit-test before tracking). Default: false. */
|
|
72
|
+
instantPlacement?: boolean;
|
|
73
|
+
/** Called when a new plane is detected. */
|
|
74
|
+
onPlaneDetected?: (event: NativeSyntheticEvent<PlaneDetectedEvent>) => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A 3D scene rendered with Filament (Android) or RealityKit (iOS).
|
|
78
|
+
*
|
|
79
|
+
* ```tsx
|
|
80
|
+
* <SceneView
|
|
81
|
+
* environment="environments/studio.hdr"
|
|
82
|
+
* modelNodes={[{ src: "models/robot.glb" }]}
|
|
83
|
+
* />
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare const SceneView: React.FC<SceneViewProps>;
|
|
87
|
+
/**
|
|
88
|
+
* An augmented-reality scene using ARCore (Android) or ARKit (iOS).
|
|
89
|
+
*
|
|
90
|
+
* ```tsx
|
|
91
|
+
* <ARSceneView
|
|
92
|
+
* planeDetection
|
|
93
|
+
* modelNodes={[{ src: "models/chair.glb", position: [0, 0, -1] }]}
|
|
94
|
+
* />
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare const ARSceneView: React.FC<ARSceneViewProps>;
|
|
98
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sceneview-sdk/react-native",
|
|
3
|
+
"version": "3.6.1",
|
|
4
|
+
"description": "React Native bindings for SceneView — 3D and AR scenes powered by Filament (Android) and RealityKit (iOS)",
|
|
5
|
+
"main": "lib/commonjs/index",
|
|
6
|
+
"module": "lib/module/index",
|
|
7
|
+
"types": "lib/typescript/src/index.d.ts",
|
|
8
|
+
"react-native": "src/index",
|
|
9
|
+
"source": "src/index",
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"lib",
|
|
13
|
+
"android",
|
|
14
|
+
"ios",
|
|
15
|
+
"react-native-sceneview.podspec",
|
|
16
|
+
"!**/__tests__"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"typescript": "tsc --noEmit",
|
|
20
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
21
|
+
"build": "bob build",
|
|
22
|
+
"prepare": "bob build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"react-native",
|
|
26
|
+
"3d",
|
|
27
|
+
"ar",
|
|
28
|
+
"sceneview",
|
|
29
|
+
"filament",
|
|
30
|
+
"realitykit",
|
|
31
|
+
"arcore",
|
|
32
|
+
"arkit"
|
|
33
|
+
],
|
|
34
|
+
"author": "SceneView Contributors",
|
|
35
|
+
"homepage": "https://sceneview.github.io",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/sceneview/sceneview.git",
|
|
39
|
+
"directory": "react-native/react-native-sceneview"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/sceneview/sceneview/issues"
|
|
43
|
+
},
|
|
44
|
+
"license": "Apache-2.0",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": ">=18.0.0",
|
|
47
|
+
"react-native": ">=0.72.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/react": "^18.2.0",
|
|
51
|
+
"@types/react-native": "^0.72.0",
|
|
52
|
+
"react": "^18.2.0",
|
|
53
|
+
"react-native": "^0.84.1",
|
|
54
|
+
"react-native-builder-bob": "^0.23.0",
|
|
55
|
+
"typescript": "^5.3.0"
|
|
56
|
+
},
|
|
57
|
+
"react-native-builder-bob": {
|
|
58
|
+
"source": "src",
|
|
59
|
+
"output": "lib",
|
|
60
|
+
"targets": [
|
|
61
|
+
"commonjs",
|
|
62
|
+
"module",
|
|
63
|
+
[
|
|
64
|
+
"typescript",
|
|
65
|
+
{
|
|
66
|
+
"project": "tsconfig.build.json"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|