@sceneview-sdk/react-native 4.26.0 → 4.28.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 +152 -32
- package/android/build.gradle.kts +21 -0
- package/android/src/main/java/io/github/sceneview/reactnative/ARSceneViewManager.kt +5 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewEvents.kt +8 -2
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewManager.kt +29 -1
- package/ios/SceneViewModule.swift +147 -114
- package/lib/commonjs/index.js +16 -9
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +17 -11
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +68 -12
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +4 -2
- package/react-native-sceneview.podspec +35 -10
- package/src/index.tsx +90 -29
|
@@ -18,6 +18,48 @@ struct RNModelData: Identifiable, Equatable {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// MARK: - Tap payload
|
|
22
|
+
|
|
23
|
+
/// Builds the JS `onTap` payload — the iOS counterpart of Android's single
|
|
24
|
+
/// `TapEvent.getEventData()`.
|
|
25
|
+
///
|
|
26
|
+
/// Both iOS views go through here so `nodeName` is written on *every* dispatch
|
|
27
|
+
/// path, never merely on the ones that happen to have a name: a missed tap
|
|
28
|
+
/// reports `NSNull()`, which crosses the bridge as the same JS `null` Android
|
|
29
|
+
/// emits via `putNull`. That is what lets the public `TapEvent.nodeName` be
|
|
30
|
+
/// typed `string | null` instead of `string | null | undefined` — a consumer
|
|
31
|
+
/// writes one `nodeName == null` guard.
|
|
32
|
+
///
|
|
33
|
+
/// This iOS `ARSceneView` can only resolve a surface point, never an entity, so
|
|
34
|
+
/// it always passes `nil`. That is a platform limitation, not the contract:
|
|
35
|
+
/// Android's AR view hit-tests the scene and does name the tapped model. Naming
|
|
36
|
+
/// it here needs an entity hit-test hook on `SceneViewSwift.ARSceneView`, which
|
|
37
|
+
/// does not exist yet (#2051).
|
|
38
|
+
///
|
|
39
|
+
/// Keeping this a single function (rather than a comment asking each call site
|
|
40
|
+
/// to remember the key) is the guard: a new tap source cannot omit `nodeName`
|
|
41
|
+
/// without skipping the only payload builder in the bridge.
|
|
42
|
+
func rnTapPayload(worldPosition: SIMD3<Float>, nodeName: String?) -> [String: Any] {
|
|
43
|
+
// Seeded with the null sentinel, then overwritten — so the key exists
|
|
44
|
+
// before any branch runs and no path can drop it.
|
|
45
|
+
var payload: [String: Any] = [
|
|
46
|
+
"x": worldPosition.x,
|
|
47
|
+
"y": worldPosition.y,
|
|
48
|
+
"z": worldPosition.z,
|
|
49
|
+
"nodeName": NSNull(),
|
|
50
|
+
]
|
|
51
|
+
// An empty name is "no model", not a model called "". Android already
|
|
52
|
+
// collapses it (`SceneViewManager.nodeName()` ends in
|
|
53
|
+
// `takeIf { it.isNotEmpty() }`); collapsing it here rather than at the call
|
|
54
|
+
// sites is what keeps the two platforms on ONE sentinel, for the same
|
|
55
|
+
// reason the key is seeded above — a future tap source cannot reintroduce
|
|
56
|
+
// the empty string without going around the only payload builder.
|
|
57
|
+
if let nodeName, !nodeName.isEmpty {
|
|
58
|
+
payload["nodeName"] = nodeName
|
|
59
|
+
}
|
|
60
|
+
return payload
|
|
61
|
+
}
|
|
62
|
+
|
|
21
63
|
// MARK: - SceneView (3D)
|
|
22
64
|
|
|
23
65
|
/// RCTViewManager subclass that bridges React Native's `<RNSceneView>`
|
|
@@ -34,35 +76,43 @@ class RNSceneViewManager: RCTViewManager {
|
|
|
34
76
|
}
|
|
35
77
|
}
|
|
36
78
|
|
|
37
|
-
///
|
|
38
|
-
///
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
case "firstPerson": return .firstPerson
|
|
43
|
-
default: return .orbit
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/// Observable state model shared between React props and SwiftUI view.
|
|
79
|
+
/// State model backing the React props.
|
|
80
|
+
///
|
|
81
|
+
/// Still an `ObservableObject` for shape consistency with `RNARSceneState`, which
|
|
82
|
+
/// genuinely drives SwiftUI; nothing observes this one since the 3D scene moved
|
|
83
|
+
/// onto `SceneViewerHostView`.
|
|
48
84
|
@MainActor
|
|
49
85
|
class RNSceneState: ObservableObject {
|
|
50
86
|
@Published var models: [RNModelData] = []
|
|
51
87
|
@Published var environmentPath: String?
|
|
88
|
+
|
|
89
|
+
/// Accepted, stored, and deliberately not applied — as before this bridge
|
|
90
|
+
/// moved onto the shared host.
|
|
91
|
+
///
|
|
92
|
+
/// `cameraControlMode` supersedes it and the two would contradict each other
|
|
93
|
+
/// (which wins for `cameraOrbit: false, cameraControlMode: "orbit"`?). Wiring
|
|
94
|
+
/// it up here would be a behaviour change dressed as a refactor, so it stays
|
|
95
|
+
/// inert; the TypeScript surface deprecates it.
|
|
52
96
|
@Published var cameraOrbit: Bool = true
|
|
53
|
-
@Published var cameraControlMode: CameraControlMode = .orbit
|
|
54
|
-
@Published var autoCenterContent: Bool = true
|
|
55
97
|
|
|
56
|
-
///
|
|
57
|
-
///
|
|
58
|
-
///
|
|
59
|
-
var
|
|
98
|
+
/// Wire name as it arrives from JS: `"orbit"`, `"pan"` or `"firstPerson"`.
|
|
99
|
+
/// `SceneViewerConfiguration` normalises an unrecognised value to `"orbit"`,
|
|
100
|
+
/// which is what the local mapping this replaces did.
|
|
101
|
+
@Published var cameraControlMode: String = "orbit"
|
|
102
|
+
|
|
103
|
+
@Published var autoCenterContent: Bool = true
|
|
60
104
|
}
|
|
61
105
|
|
|
62
|
-
/// UIView wrapper
|
|
106
|
+
/// UIView wrapper hosting the shared `SceneViewerHostView`.
|
|
107
|
+
///
|
|
108
|
+
/// This class owns the React prop bag; the scene itself — hosting SwiftUI in
|
|
109
|
+
/// UIKit, loading models, reconciling them — belongs to `SceneViewerHostView`
|
|
110
|
+
/// and is shared with the Flutter plugin and `sceneview-compose`. The AR wrapper
|
|
111
|
+
/// below keeps its own SwiftUI content: `ARSceneView` is anchor-driven and
|
|
112
|
+
/// shares nothing with the 3D viewer.
|
|
63
113
|
class RNSceneViewWrapper: UIView {
|
|
64
114
|
|
|
65
|
-
private
|
|
115
|
+
private let hostView = SceneViewerHostView()
|
|
66
116
|
private let sceneState = RNSceneState()
|
|
67
117
|
|
|
68
118
|
/// Event callback for tap events.
|
|
@@ -70,16 +120,21 @@ class RNSceneViewWrapper: UIView {
|
|
|
70
120
|
didSet {
|
|
71
121
|
let block = onTap
|
|
72
122
|
Task { @MainActor in
|
|
73
|
-
|
|
123
|
+
// `onTapEntity` rather than the host's flattened `onTap`: this
|
|
124
|
+
// prop reports the tapped *model* — its own origin and its file
|
|
125
|
+
// base name — where the flattened callback carries the hit's
|
|
126
|
+
// bounds centre and no name at all.
|
|
127
|
+
//
|
|
128
|
+
// The model root is what the host resolves, so a tap inside a
|
|
129
|
+
// USDZ that names its meshes reports the model, not `skin0`.
|
|
130
|
+
hostView.onTapEntity = { _, modelRoot in
|
|
74
131
|
// Mirrors the Android `TapEvent` payload: world-space
|
|
75
|
-
// coordinates of the tapped
|
|
76
|
-
|
|
77
|
-
block?(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"nodeName": entity.name,
|
|
82
|
-
])
|
|
132
|
+
// coordinates of the tapped model (`node.worldPosition`
|
|
133
|
+
// there) + its node name, `null` when nothing was hit.
|
|
134
|
+
block?(rnTapPayload(
|
|
135
|
+
worldPosition: modelRoot?.position(relativeTo: nil) ?? .zero,
|
|
136
|
+
nodeName: modelRoot.map { ($0.name as NSString).deletingPathExtension }
|
|
137
|
+
))
|
|
83
138
|
}
|
|
84
139
|
}
|
|
85
140
|
}
|
|
@@ -96,12 +151,55 @@ class RNSceneViewWrapper: UIView {
|
|
|
96
151
|
}
|
|
97
152
|
|
|
98
153
|
private func setupView() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
154
|
+
hostView.frame = bounds
|
|
155
|
+
hostView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
156
|
+
addSubview(hostView)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// Pushes the current props into the host view.
|
|
160
|
+
///
|
|
161
|
+
/// Called after every prop change. `applyConfiguration` compares field by
|
|
162
|
+
/// field and touches only what changed, so re-sending everything on each
|
|
163
|
+
/// prop update costs nothing.
|
|
164
|
+
@MainActor
|
|
165
|
+
private func applyState() {
|
|
166
|
+
let configuration = SceneViewerConfiguration()
|
|
167
|
+
|
|
168
|
+
configuration.models = sceneState.models.map { data in
|
|
169
|
+
let model = SceneViewerModel()
|
|
170
|
+
model.assetPath = data.path
|
|
171
|
+
// The per-entry id minted when the prop was decoded. A new
|
|
172
|
+
// `modelNodes` value mints new ids, which is what gives this prop
|
|
173
|
+
// its replace-the-whole-list semantics.
|
|
174
|
+
model.identity = data.id.uuidString
|
|
175
|
+
model.setScale(data.scale.x, data.scale.y, data.scale.z)
|
|
176
|
+
model.setPosition(data.position.x, data.position.y, data.position.z)
|
|
177
|
+
model.animationName = data.animation
|
|
178
|
+
// No named animation means play them all, which is this bridge's
|
|
179
|
+
// behaviour and not the Flutter one's.
|
|
180
|
+
model.autoPlayAllAnimations = true
|
|
181
|
+
// The file's name — with its extension, which the tap report
|
|
182
|
+
// strips. This is the only entity the bridge names, and the host
|
|
183
|
+
// resolves a tap to exactly it, so it is what JS receives as
|
|
184
|
+
// `nodeName`. Matches Android's `ModelNodeData.nodeName()`.
|
|
185
|
+
model.nodeName = sceneViewerModelFileName(data.path)
|
|
186
|
+
return model
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
configuration.cameraControlMode = sceneState.cameraControlMode
|
|
190
|
+
configuration.autoCenterContent = sceneState.autoCenterContent
|
|
191
|
+
// This bridge exposes no camera prop, so it authors no pose: without
|
|
192
|
+
// this, every prop change would re-assert the configuration's default
|
|
193
|
+
// pose and snap the camera back out of the auto-centre framing and away
|
|
194
|
+
// from wherever the user had orbited to.
|
|
195
|
+
configuration.cameraPoseAuthored = false
|
|
196
|
+
|
|
197
|
+
if let path = sceneState.environmentPath, !path.isEmpty {
|
|
198
|
+
configuration.environmentKind = "hdr"
|
|
199
|
+
configuration.environmentHdrPath = path
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
hostView.applyConfiguration(configuration)
|
|
105
203
|
}
|
|
106
204
|
|
|
107
205
|
// MARK: - React props
|
|
@@ -110,6 +208,7 @@ class RNSceneViewWrapper: UIView {
|
|
|
110
208
|
didSet {
|
|
111
209
|
Task { @MainActor in
|
|
112
210
|
sceneState.environmentPath = environment
|
|
211
|
+
applyState()
|
|
113
212
|
}
|
|
114
213
|
}
|
|
115
214
|
}
|
|
@@ -136,10 +235,12 @@ class RNSceneViewWrapper: UIView {
|
|
|
136
235
|
let animation = dict["animation"] as? String
|
|
137
236
|
return RNModelData(path: src, scale: scale, position: position, animation: animation)
|
|
138
237
|
} ?? []
|
|
238
|
+
applyState()
|
|
139
239
|
}
|
|
140
240
|
}
|
|
141
241
|
}
|
|
142
242
|
|
|
243
|
+
/// **Deprecated.** Superseded by `cameraControlMode`; see `RNSceneState.cameraOrbit`.
|
|
143
244
|
@objc var cameraOrbit: Bool = true {
|
|
144
245
|
didSet {
|
|
145
246
|
Task { @MainActor in
|
|
@@ -151,9 +252,10 @@ class RNSceneViewWrapper: UIView {
|
|
|
151
252
|
/// Camera interaction mode (v4.3.0, issue #1053).
|
|
152
253
|
@objc var cameraControlMode: String? {
|
|
153
254
|
didSet {
|
|
154
|
-
let
|
|
255
|
+
let raw = cameraControlMode
|
|
155
256
|
Task { @MainActor in
|
|
156
|
-
sceneState.cameraControlMode =
|
|
257
|
+
sceneState.cameraControlMode = raw ?? "orbit"
|
|
258
|
+
applyState()
|
|
157
259
|
}
|
|
158
260
|
}
|
|
159
261
|
}
|
|
@@ -163,78 +265,7 @@ class RNSceneViewWrapper: UIView {
|
|
|
163
265
|
didSet {
|
|
164
266
|
Task { @MainActor in
|
|
165
267
|
sceneState.autoCenterContent = autoCenterContent
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/// SwiftUI content view rendering `SceneViewSwift.SceneView` (issue #2067).
|
|
172
|
-
///
|
|
173
|
-
/// `SceneViewSwift` loads models **asynchronously** (`ModelNode.load(_:)` is
|
|
174
|
-
/// `async throws`) — there is no synchronous `ModelNode(String)` initialiser,
|
|
175
|
-
/// and the `@NodeBuilder` content builder composes static `EntityProvider`
|
|
176
|
-
/// values, not a SwiftUI `ForEach`. So the bridge loads each `RNModelData`
|
|
177
|
-
/// off the main actor's `Task`, wraps the resulting `ModelEntity`s in a
|
|
178
|
-
/// stable holder entity, and feeds that holder to `SceneView`'s **imperative**
|
|
179
|
-
/// `init(_ content: (Entity) -> Void)`.
|
|
180
|
-
///
|
|
181
|
-
/// `SceneView`'s content closure runs once when the underlying `RealityView`
|
|
182
|
-
/// is created, so the holder entity is built up front and re-populated in a
|
|
183
|
-
/// `.task(id:)` whenever the JS `modelNodes` prop changes — RealityKit picks
|
|
184
|
-
/// up the new children on its next render frame without recreating the view.
|
|
185
|
-
struct RNSceneViewContent: View {
|
|
186
|
-
@ObservedObject var state: RNSceneState
|
|
187
|
-
|
|
188
|
-
/// Stable parent entity handed to `SceneView`. Its children are the
|
|
189
|
-
/// loaded model entities; rebuilt by `loadModels()` on every prop change.
|
|
190
|
-
@State private var modelRoot = Entity()
|
|
191
|
-
|
|
192
|
-
var body: some View {
|
|
193
|
-
SceneView { root in
|
|
194
|
-
root.addChild(modelRoot)
|
|
195
|
-
}
|
|
196
|
-
.cameraControls(state.cameraControlMode)
|
|
197
|
-
.autoCenterContent(state.autoCenterContent)
|
|
198
|
-
// Forward entity taps to React Native's `onTap` prop (issue #2053).
|
|
199
|
-
.onEntityTapped { entity in
|
|
200
|
-
state.onTap?(entity)
|
|
201
|
-
}
|
|
202
|
-
// Reload whenever the JS `modelNodes` prop changes. Keyed on the
|
|
203
|
-
// model identities so an unrelated re-render does not re-download.
|
|
204
|
-
.task(id: state.models.map(\.id)) {
|
|
205
|
-
await loadModels()
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/// Loads every model in `state.models` and replaces `modelRoot`'s
|
|
210
|
-
/// children with the freshly loaded entities. `ModelNode.load(_:)` is
|
|
211
|
-
/// `@MainActor`-isolated, so this runs on the main actor as required by
|
|
212
|
-
/// RealityKit. A failed load is skipped (the others still render).
|
|
213
|
-
@MainActor
|
|
214
|
-
private func loadModels() async {
|
|
215
|
-
// Clear previous content before reloading.
|
|
216
|
-
for child in modelRoot.children {
|
|
217
|
-
child.removeFromParent()
|
|
218
|
-
}
|
|
219
|
-
for model in state.models {
|
|
220
|
-
do {
|
|
221
|
-
let node = try await ModelNode.load(model.path)
|
|
222
|
-
// `.task(id:)` cancels this task when the `modelNodes` prop
|
|
223
|
-
// changes mid-load. A cancelled task still resumes past the
|
|
224
|
-
// `await`, so bail out before mutating the scene — otherwise a
|
|
225
|
-
// superseded load leaks a stale model into `modelRoot`.
|
|
226
|
-
guard !Task.isCancelled else { return }
|
|
227
|
-
node.position(model.position)
|
|
228
|
-
node.scale(model.scale)
|
|
229
|
-
if let animation = model.animation {
|
|
230
|
-
node.playAnimation(named: animation)
|
|
231
|
-
} else if node.animationCount > 0 {
|
|
232
|
-
node.playAllAnimations()
|
|
233
|
-
}
|
|
234
|
-
modelRoot.addChild(node.entity)
|
|
235
|
-
} catch {
|
|
236
|
-
// A single bad path must not break the whole scene.
|
|
237
|
-
print("[RNSceneView] Failed to load model '\(model.path)': \(error)")
|
|
268
|
+
applyState()
|
|
238
269
|
}
|
|
239
270
|
}
|
|
240
271
|
}
|
|
@@ -284,12 +315,14 @@ class RNARSceneViewWrapper: UIView {
|
|
|
284
315
|
sceneState.onTap = { worldPosition in
|
|
285
316
|
// Mirrors the Android `TapEvent` payload. `ARSceneView`'s
|
|
286
317
|
// `onTapOnPlane` reports the tapped surface point, not a
|
|
287
|
-
// node, so `nodeName` is
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
318
|
+
// node, so `nodeName` is always `null` here — the same JS
|
|
319
|
+
// `null` Android emits via `putNull` and the 3D view emits
|
|
320
|
+
// for a tap that hit no model. It used to omit the key,
|
|
321
|
+
// which made JS see `undefined` on this one view.
|
|
322
|
+
//
|
|
323
|
+
// Android's AR view names the tapped model instead; this
|
|
324
|
+
// side cannot until #2051 adds an entity hit-test hook.
|
|
325
|
+
block?(rnTapPayload(worldPosition: worldPosition, nodeName: nil))
|
|
293
326
|
}
|
|
294
327
|
}
|
|
295
328
|
}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -7,6 +7,13 @@ 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 }; }
|
|
10
|
+
// `tsconfig.json` sets `"jsx": "react"` — the CLASSIC runtime — so every JSX
|
|
11
|
+
// element below compiles to `React.createElement(...)` (verified in the
|
|
12
|
+
// published `lib/commonjs/index.js`). Biome's `useImportType` only sees the
|
|
13
|
+
// `React.FC` type annotations, not the JSX lowering, so it offers a "safe fix"
|
|
14
|
+
// that would erase this import and break the shipped bundle at runtime.
|
|
15
|
+
// biome-ignore lint/style/useImportType: React is a VALUE import — see above.
|
|
16
|
+
|
|
10
17
|
// ---------------------------------------------------------------------------
|
|
11
18
|
// Node type interfaces
|
|
12
19
|
// ---------------------------------------------------------------------------
|
|
@@ -55,9 +62,9 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
55
62
|
// Native components (only available on Android and iOS)
|
|
56
63
|
// ---------------------------------------------------------------------------
|
|
57
64
|
|
|
58
|
-
const isNativeAvailable = _reactNative.Platform.OS ===
|
|
59
|
-
const NativeSceneView = isNativeAvailable ? (0, _reactNative.requireNativeComponent)(
|
|
60
|
-
const NativeARSceneView = isNativeAvailable ? (0, _reactNative.requireNativeComponent)(
|
|
65
|
+
const isNativeAvailable = _reactNative.Platform.OS === "android" || _reactNative.Platform.OS === "ios";
|
|
66
|
+
const NativeSceneView = isNativeAvailable ? (0, _reactNative.requireNativeComponent)("RNSceneView") : null;
|
|
67
|
+
const NativeARSceneView = isNativeAvailable ? (0, _reactNative.requireNativeComponent)("RNARSceneView") : null;
|
|
61
68
|
|
|
62
69
|
// ---------------------------------------------------------------------------
|
|
63
70
|
// Fallback for unsupported platforms
|
|
@@ -73,12 +80,12 @@ const UnsupportedView = ({
|
|
|
73
80
|
const fallbackStyles = _reactNative.StyleSheet.create({
|
|
74
81
|
container: {
|
|
75
82
|
flex: 1,
|
|
76
|
-
justifyContent:
|
|
77
|
-
alignItems:
|
|
78
|
-
backgroundColor:
|
|
83
|
+
justifyContent: "center",
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
backgroundColor: "#1a1a2e"
|
|
79
86
|
},
|
|
80
87
|
text: {
|
|
81
|
-
color:
|
|
88
|
+
color: "#aaa",
|
|
82
89
|
fontSize: 16
|
|
83
90
|
}
|
|
84
91
|
});
|
|
@@ -158,10 +165,10 @@ const NativeARRecorder = _reactNative.NativeModules.RNARRecorder;
|
|
|
158
165
|
class ARRecorder {
|
|
159
166
|
/** `true` when {@link ARRecorder} is supported on the current platform. */
|
|
160
167
|
static get isSupported() {
|
|
161
|
-
return _reactNative.Platform.OS ===
|
|
168
|
+
return _reactNative.Platform.OS === "ios" && NativeARRecorder != null;
|
|
162
169
|
}
|
|
163
170
|
rejectUnsupported() {
|
|
164
|
-
return Promise.reject(new Error(
|
|
171
|
+
return Promise.reject(new Error("ARRecorder is currently only supported on iOS. Android AR session " + "recording is tracked in issue #1051."));
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
/** Starts an AR session recording. */
|
|
@@ -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","NativeARRecorder","NativeModules","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;
|
|
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,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// `tsconfig.json` sets `"jsx": "react"` — the CLASSIC runtime — so every JSX
|
|
2
|
+
// element below compiles to `React.createElement(...)` (verified in the
|
|
3
|
+
// published `lib/commonjs/index.js`). Biome's `useImportType` only sees the
|
|
4
|
+
// `React.FC` type annotations, not the JSX lowering, so it offers a "safe fix"
|
|
5
|
+
// that would erase this import and break the shipped bundle at runtime.
|
|
6
|
+
// biome-ignore lint/style/useImportType: React is a VALUE import — see above.
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { NativeModules, Platform, requireNativeComponent, StyleSheet, Text, View } from "react-native";
|
|
3
9
|
|
|
4
10
|
// ---------------------------------------------------------------------------
|
|
5
11
|
// Node type interfaces
|
|
@@ -49,9 +55,9 @@ import { requireNativeComponent, NativeModules, Platform, View, Text, StyleSheet
|
|
|
49
55
|
// Native components (only available on Android and iOS)
|
|
50
56
|
// ---------------------------------------------------------------------------
|
|
51
57
|
|
|
52
|
-
const isNativeAvailable = Platform.OS ===
|
|
53
|
-
const NativeSceneView = isNativeAvailable ? requireNativeComponent(
|
|
54
|
-
const NativeARSceneView = isNativeAvailable ? requireNativeComponent(
|
|
58
|
+
const isNativeAvailable = Platform.OS === "android" || Platform.OS === "ios";
|
|
59
|
+
const NativeSceneView = isNativeAvailable ? requireNativeComponent("RNSceneView") : null;
|
|
60
|
+
const NativeARSceneView = isNativeAvailable ? requireNativeComponent("RNARSceneView") : null;
|
|
55
61
|
|
|
56
62
|
// ---------------------------------------------------------------------------
|
|
57
63
|
// Fallback for unsupported platforms
|
|
@@ -67,12 +73,12 @@ const UnsupportedView = ({
|
|
|
67
73
|
const fallbackStyles = StyleSheet.create({
|
|
68
74
|
container: {
|
|
69
75
|
flex: 1,
|
|
70
|
-
justifyContent:
|
|
71
|
-
alignItems:
|
|
72
|
-
backgroundColor:
|
|
76
|
+
justifyContent: "center",
|
|
77
|
+
alignItems: "center",
|
|
78
|
+
backgroundColor: "#1a1a2e"
|
|
73
79
|
},
|
|
74
80
|
text: {
|
|
75
|
-
color:
|
|
81
|
+
color: "#aaa",
|
|
76
82
|
fontSize: 16
|
|
77
83
|
}
|
|
78
84
|
});
|
|
@@ -151,10 +157,10 @@ const NativeARRecorder = NativeModules.RNARRecorder;
|
|
|
151
157
|
export class ARRecorder {
|
|
152
158
|
/** `true` when {@link ARRecorder} is supported on the current platform. */
|
|
153
159
|
static get isSupported() {
|
|
154
|
-
return Platform.OS ===
|
|
160
|
+
return Platform.OS === "ios" && NativeARRecorder != null;
|
|
155
161
|
}
|
|
156
162
|
rejectUnsupported() {
|
|
157
|
-
return Promise.reject(new Error(
|
|
163
|
+
return Promise.reject(new Error("ARRecorder is currently only supported on iOS. Android AR session " + "recording is tracked in issue #1051."));
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
/** Starts an AR session recording. */
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","
|
|
1
|
+
{"version":3,"names":["React","NativeModules","Platform","requireNativeComponent","StyleSheet","Text","View","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":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { type
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type NativeSyntheticEvent, type ViewStyle } from "react-native";
|
|
3
3
|
/** A 3D model loaded from a .glb / .gltf file. */
|
|
4
4
|
export interface ModelNode {
|
|
5
5
|
/** Asset path or URL to the glTF/GLB model. */
|
|
@@ -26,7 +26,7 @@ export interface ModelNode {
|
|
|
26
26
|
* cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
|
|
27
27
|
*/
|
|
28
28
|
export interface GeometryNode {
|
|
29
|
-
type:
|
|
29
|
+
type: "box" | "cube" | "sphere" | "cylinder" | "plane";
|
|
30
30
|
size?: [number, number, number];
|
|
31
31
|
position?: [number, number, number];
|
|
32
32
|
rotation?: [number, number, number];
|
|
@@ -52,7 +52,7 @@ export interface GeometryNode {
|
|
|
52
52
|
* cross-platform bridge-parity umbrella (#909).
|
|
53
53
|
*/
|
|
54
54
|
export interface LightNode {
|
|
55
|
-
type:
|
|
55
|
+
type: "directional" | "point" | "spot";
|
|
56
56
|
intensity?: number;
|
|
57
57
|
color?: string;
|
|
58
58
|
position?: [number, number, number];
|
|
@@ -63,12 +63,28 @@ export interface TapEvent {
|
|
|
63
63
|
x: number;
|
|
64
64
|
y: number;
|
|
65
65
|
z: number;
|
|
66
|
-
/**
|
|
67
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Name of the tapped model: its file's base name without extension
|
|
68
|
+
* (`models/robot.glb` → `robot`). Never an asset-internal mesh name — a tap
|
|
69
|
+
* inside a model always reports the model.
|
|
70
|
+
*
|
|
71
|
+
* **Measured on Android and iOS** (#3086) — the iOS run named the
|
|
72
|
+
* tapped model on every dispatch; see {@link SceneViewProps.onTap | `onTap`}.
|
|
73
|
+
*
|
|
74
|
+
* `null` — never `undefined` — when the tap hit no model: an untitled
|
|
75
|
+
* geometry node or nothing at all on Android, a plane or a miss in Android
|
|
76
|
+
* AR, and *every* `ARSceneView` tap on iOS, where `SceneViewSwift`'s
|
|
77
|
+
* `ARSceneView` exposes no entity hit-test hook and so can only resolve the
|
|
78
|
+
* surface point (#2051).
|
|
79
|
+
*
|
|
80
|
+
* Every native dispatch path writes the key, so `nodeName == null` is the one
|
|
81
|
+
* guard that covers both views on both platforms.
|
|
82
|
+
*/
|
|
83
|
+
nodeName: string | null;
|
|
68
84
|
}
|
|
69
85
|
export interface PlaneDetectedEvent {
|
|
70
86
|
id: string;
|
|
71
|
-
type:
|
|
87
|
+
type: "horizontal" | "vertical";
|
|
72
88
|
center: [number, number, number];
|
|
73
89
|
extent: [number, number];
|
|
74
90
|
}
|
|
@@ -81,7 +97,7 @@ export interface PlaneDetectedEvent {
|
|
|
81
97
|
* back to orbit (the per-mode switch is an iOS-first v4.3.0 addition —
|
|
82
98
|
* the Android side is tracked in issue #1051).
|
|
83
99
|
*/
|
|
84
|
-
export type CameraControlMode =
|
|
100
|
+
export type CameraControlMode = "orbit" | "pan" | "firstPerson";
|
|
85
101
|
export interface SceneViewProps {
|
|
86
102
|
style?: ViewStyle;
|
|
87
103
|
/** HDR environment asset path (e.g. "environments/studio.hdr"). */
|
|
@@ -100,7 +116,17 @@ export interface SceneViewProps {
|
|
|
100
116
|
* **iOS:** acknowledged but not yet rendered — see {@link LightNode}.
|
|
101
117
|
*/
|
|
102
118
|
lightNodes?: LightNode[];
|
|
103
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Enable default orbit camera controls. Default: `true`.
|
|
121
|
+
*
|
|
122
|
+
* @deprecated Superseded by {@link SceneViewProps.cameraControlMode}, and
|
|
123
|
+
* **inert on iOS**: the two would contradict each other — nothing can say
|
|
124
|
+
* which wins for `cameraOrbit: false, cameraControlMode: 'orbit'` — so the
|
|
125
|
+
* iOS bridge deliberately reads only `cameraControlMode`. Still honoured on
|
|
126
|
+
* Android. There is currently no way to freeze the camera from this bridge
|
|
127
|
+
* on iOS; `SceneViewSwift` has `cameraGesturesEnabled` but it is not exposed
|
|
128
|
+
* here yet.
|
|
129
|
+
*/
|
|
104
130
|
cameraOrbit?: boolean;
|
|
105
131
|
/**
|
|
106
132
|
* Camera interaction mode (v4.3.0). Default: `'orbit'`.
|
|
@@ -118,9 +144,39 @@ export interface SceneViewProps {
|
|
|
118
144
|
/**
|
|
119
145
|
* Called when the user taps inside the scene.
|
|
120
146
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
147
|
+
* Dispatched on **Android and iOS**. The iOS half was measured on an iPhone
|
|
148
|
+
* 17 Pro Max simulator — 5 taps on a rendering model, 5 dispatches, the
|
|
149
|
+
* model's name every time
|
|
150
|
+
* ({@link https://github.com/sceneview/sceneview/issues/3086 | #3086}).
|
|
151
|
+
* The sibling Flutter bridge still never fires its 3D `onTap` on iOS
|
|
152
|
+
* ({@link https://github.com/sceneview/sceneview/issues/3045 | #3045}); the
|
|
153
|
+
* same run showed that is Flutter's platform-view touch delivery, not this
|
|
154
|
+
* shared RealityKit path, so it does not apply here.
|
|
155
|
+
*
|
|
156
|
+
* The event payload carries the world-space position of the tapped model and
|
|
157
|
+
* its `nodeName` (the model file's base name without extension). A tap that
|
|
158
|
+
* hits no model reports `nodeName: null` — with
|
|
159
|
+
* the tapped node's real world position on Android when it landed on an
|
|
160
|
+
* (unnamed) geometry node, and `0, 0, 0` when it hit nothing at all. That
|
|
161
|
+
* `0, 0, 0` miss is **in practice Android-only**: iOS resolves the tap
|
|
162
|
+
* through RealityKit's entity-targeted gesture, which fires only on a hit,
|
|
163
|
+
* so a tap on empty space dispatches no `onTap` event at all — count taps
|
|
164
|
+
* and you will see fewer events on iOS, not a `0, 0, 0` one. (iOS emits
|
|
165
|
+
* `0, 0, 0` only if a hit entity resolves outside every loaded model, which
|
|
166
|
+
* the content root's contents make unreachable today.)
|
|
167
|
+
*
|
|
168
|
+
* On {@link ARSceneViewProps | `ARSceneView`} *what a hit reports* differs by
|
|
169
|
+
* platform:
|
|
170
|
+
* - **Android**: the AR view hit-tests the scene, so a tap on a model reports
|
|
171
|
+
* that model's file base name exactly as `SceneView` does; a tap on a plane
|
|
172
|
+
* or on nothing reports `null`.
|
|
173
|
+
* - **iOS**: always `null` — `SceneViewSwift.ARSceneView` exposes no entity
|
|
174
|
+
* hit-test hook, so the AR tap can only resolve the surface point. Tracked
|
|
175
|
+
* under {@link https://github.com/sceneview/sceneview/issues/2051 | #2051}.
|
|
176
|
+
*
|
|
177
|
+
* The *key* is written on every dispatch path of both views on both
|
|
178
|
+
* platforms, so one `nodeName == null` check remains the correct "the tap hit
|
|
179
|
+
* no model" test everywhere; it is never omitted and never `undefined`.
|
|
124
180
|
*/
|
|
125
181
|
onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
|
|
126
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"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"}
|