@sceneview-sdk/react-native 4.13.0 → 4.15.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 +43 -11
- package/android/src/main/java/io/github/sceneview/reactnative/ARSceneViewManager.kt +139 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewEvents.kt +130 -0
- package/android/src/main/java/io/github/sceneview/reactnative/SceneViewManager.kt +25 -0
- package/ios/SceneViewModule.swift +207 -15
- package/lib/commonjs/index.js +18 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +18 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +61 -8
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/react-native-sceneview.podspec +10 -2
- package/src/index.tsx +61 -8
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
React Native bindings for [SceneView](https://sceneview.github.io) — 3D and AR scenes powered by Filament (Android) and RealityKit (iOS).
|
|
8
8
|
|
|
9
|
-
> **Status:** Alpha — 3D model loading works on both platforms. AR scene is
|
|
9
|
+
> **Status:** Alpha — 3D model loading works on both platforms. AR scene is
|
|
10
|
+
> functional on Android. The iOS native bridge compiles against the real
|
|
11
|
+
> `SceneViewSwift` API and is CI-verified (`.github/workflows/rn-ios-compile.yml`).
|
|
10
12
|
|
|
11
13
|
## Features
|
|
12
14
|
|
|
@@ -45,9 +47,18 @@ npm install github:sceneview/sceneview#v4.0.9 --save # path includes react-nati
|
|
|
45
47
|
cd ios && pod install
|
|
46
48
|
```
|
|
47
49
|
|
|
48
|
-
Requires iOS 17+ and Xcode 15+.
|
|
50
|
+
Requires iOS 17+ and Xcode 15+.
|
|
51
|
+
|
|
52
|
+
**The host app must add `SceneViewSwift` via Swift Package Manager.**
|
|
53
|
+
`SceneViewSwift` ships as a SwiftPM package only (no CocoaPods spec), so this
|
|
54
|
+
module's podspec deliberately does **not** declare it as a `s.dependency` —
|
|
55
|
+
add it once in Xcode (*File ▸ Add Package Dependencies…*):
|
|
56
|
+
|
|
49
57
|
- URL: `https://github.com/sceneview/SceneViewSwift`
|
|
50
|
-
- Version: `4.
|
|
58
|
+
- Version: `4.14.0` (or *Up to Next Major*)
|
|
59
|
+
|
|
60
|
+
The module's `ios/*.swift` `import SceneViewSwift` resolves against that
|
|
61
|
+
app-level package at build time.
|
|
51
62
|
|
|
52
63
|
### Android
|
|
53
64
|
|
|
@@ -112,10 +123,16 @@ Android side is tracked in issue #1051.
|
|
|
112
123
|
| Prop | Type | Default | Description |
|
|
113
124
|
|---------------------|------------|---------|-------------------------------------------|
|
|
114
125
|
| `planeDetection` | `boolean` | `true` | Enable plane detection |
|
|
115
|
-
| `depthOcclusion` | `boolean` | `false` |
|
|
116
|
-
| `instantPlacement` | `boolean` | `false` |
|
|
126
|
+
| `depthOcclusion` | `boolean` | `false` | **Not yet bridged** — accepted but not configured natively (#909) |
|
|
127
|
+
| `instantPlacement` | `boolean` | `false` | **Not yet bridged** — accepted but not configured natively (#909) |
|
|
117
128
|
| `onPlaneDetected` | `function` | — | Callback when a new plane is detected |
|
|
118
129
|
|
|
130
|
+
> ⚠️ `depthOcclusion` and `instantPlacement` are declared so the API surface is
|
|
131
|
+
> stable, but the native bridge does **not** yet apply them to the ARCore
|
|
132
|
+
> `Config`. Setting either has no visible effect today. Wiring them into the
|
|
133
|
+
> native AR session is tracked in the [#909](https://github.com/sceneview/sceneview/issues/909)
|
|
134
|
+
> bridge-parity umbrella.
|
|
135
|
+
|
|
119
136
|
### ModelNode interface
|
|
120
137
|
|
|
121
138
|
```ts
|
|
@@ -157,20 +174,35 @@ React Native JS
|
|
|
157
174
|
requireNativeComponent('RNSceneView' / 'RNARSceneView')
|
|
158
175
|
|
|
|
159
176
|
+---> Android: ViewManager -> ComposeView -> SceneView { } / ARSceneView { }
|
|
160
|
-
| (Filament, SceneView SDK
|
|
177
|
+
| (Filament, SceneView SDK)
|
|
161
178
|
|
|
|
162
179
|
+---> iOS: RCTViewManager -> UIHostingController -> SceneView / ARSceneView
|
|
163
|
-
(RealityKit, SceneViewSwift
|
|
180
|
+
(RealityKit, SceneViewSwift)
|
|
164
181
|
```
|
|
165
182
|
|
|
166
183
|
Props are mapped from the React Native bridge to native view parameters on each platform.
|
|
167
184
|
|
|
168
185
|
## Limitations
|
|
169
186
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
-
|
|
187
|
+
This bridge currently exposes a subset of the SceneView SDK. The honest
|
|
188
|
+
coverage map (tracked in [#909](https://github.com/sceneview/sceneview/issues/909)):
|
|
189
|
+
|
|
190
|
+
- **`geometryNodes` / `lightNodes`** — rendered natively on **Android** only;
|
|
191
|
+
the iOS RealityKit port is not yet bridged.
|
|
192
|
+
- **`depthOcclusion` / `instantPlacement`** — declared as props but **not
|
|
193
|
+
configured natively** on either platform. Setting them has no effect today.
|
|
194
|
+
- **`onTap` / `onPlaneDetected`** — declared, but the native side does not yet
|
|
195
|
+
dispatch these events, so the callbacks never fire.
|
|
196
|
+
- **`environment` on `ARSceneView`** — AR scenes use the camera feed; the HDR
|
|
197
|
+
environment is accepted but not applied.
|
|
198
|
+
- **`ModelNode.scale`** — parsed as a uniform float; the per-axis `[x, y, z]`
|
|
199
|
+
array form is not yet supported.
|
|
200
|
+
- **`cameraControlMode` `'pan'` / `'firstPerson'`** — iOS-only; fall back to
|
|
201
|
+
orbit on Android.
|
|
202
|
+
- Not yet bridged at all: `ViewNode` / `ImageNode` / `VideoNode` / `TextNode`,
|
|
203
|
+
camera positioning, advanced AR anchors, Augmented Faces/Images, and the
|
|
204
|
+
`sceneview-core` physics/collision/geometry-generation APIs.
|
|
205
|
+
- Only Android and iOS are supported; other platforms render a fallback message.
|
|
174
206
|
|
|
175
207
|
## Contributing
|
|
176
208
|
|
|
@@ -3,6 +3,7 @@ package io.github.sceneview.reactnative
|
|
|
3
3
|
import android.widget.FrameLayout
|
|
4
4
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
5
5
|
import androidx.compose.runtime.DisposableEffect
|
|
6
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
6
7
|
import androidx.compose.runtime.mutableStateListOf
|
|
7
8
|
import androidx.compose.runtime.mutableStateOf
|
|
8
9
|
import androidx.compose.runtime.remember
|
|
@@ -10,16 +11,24 @@ import androidx.compose.ui.Modifier
|
|
|
10
11
|
import androidx.compose.ui.platform.ComposeView
|
|
11
12
|
import com.facebook.react.bridge.ReadableArray
|
|
12
13
|
import com.facebook.react.bridge.ReadableType
|
|
14
|
+
import com.facebook.react.common.MapBuilder
|
|
13
15
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
14
16
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
17
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
16
18
|
import com.google.android.filament.LightManager
|
|
19
|
+
import com.google.ar.core.Config
|
|
20
|
+
import com.google.ar.core.Plane
|
|
21
|
+
import com.google.ar.core.Session
|
|
17
22
|
import io.github.sceneview.SurfaceType
|
|
23
|
+
import io.github.sceneview.ar.arcore.configure
|
|
24
|
+
import io.github.sceneview.ar.arcore.getUpdatedPlanes
|
|
25
|
+
import io.github.sceneview.gesture.GestureDetector
|
|
18
26
|
import io.github.sceneview.math.Size
|
|
19
27
|
import io.github.sceneview.rememberEngine
|
|
20
28
|
import io.github.sceneview.rememberMaterialLoader
|
|
21
29
|
import io.github.sceneview.rememberModelInstance
|
|
22
30
|
import io.github.sceneview.rememberModelLoader
|
|
31
|
+
import java.util.concurrent.atomic.AtomicReference
|
|
23
32
|
|
|
24
33
|
/**
|
|
25
34
|
* Per-instance AR scene state stored as a tag on the FrameLayout container.
|
|
@@ -31,6 +40,14 @@ class ARSceneViewState {
|
|
|
31
40
|
val planeDetection = mutableStateOf(true)
|
|
32
41
|
val depthOcclusion = mutableStateOf(false)
|
|
33
42
|
val instantPlacement = mutableStateOf(false)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* ARCore [Plane] instances already reported to JS via `onPlaneDetected`.
|
|
46
|
+
* ARCore returns the same `Plane` reference for a given trackable across
|
|
47
|
+
* frames, so reference identity is the correct de-duplication key — the
|
|
48
|
+
* event fires exactly once per newly-detected plane (issue #2053).
|
|
49
|
+
*/
|
|
50
|
+
val reportedPlanes = HashSet<Plane>()
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
/**
|
|
@@ -48,17 +65,86 @@ class ARSceneViewManager : SimpleViewManager<FrameLayout>() {
|
|
|
48
65
|
return view.tag as? ARSceneViewState ?: ARSceneViewState().also { view.tag = it }
|
|
49
66
|
}
|
|
50
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Applies the `depthOcclusion` / `instantPlacement` JS props to an ARCore
|
|
70
|
+
* [Config]. Shared by the session-creation `sessionConfiguration` callback
|
|
71
|
+
* and the live-session re-configure path so the two never diverge.
|
|
72
|
+
*
|
|
73
|
+
* `depthMode` is support-gated: enabling `Config.DepthMode.AUTOMATIC` on a
|
|
74
|
+
* device that does not support depth would make `Session.configure` throw,
|
|
75
|
+
* so it falls back to `DISABLED` when unsupported.
|
|
76
|
+
*/
|
|
77
|
+
private fun applyArConfig(
|
|
78
|
+
session: Session,
|
|
79
|
+
config: Config,
|
|
80
|
+
depthOcclusion: Boolean,
|
|
81
|
+
instantPlacement: Boolean,
|
|
82
|
+
) {
|
|
83
|
+
config.depthMode =
|
|
84
|
+
if (depthOcclusion &&
|
|
85
|
+
session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)
|
|
86
|
+
) {
|
|
87
|
+
Config.DepthMode.AUTOMATIC
|
|
88
|
+
} else {
|
|
89
|
+
Config.DepthMode.DISABLED
|
|
90
|
+
}
|
|
91
|
+
config.instantPlacementMode =
|
|
92
|
+
if (instantPlacement) {
|
|
93
|
+
Config.InstantPlacementMode.LOCAL_Y_UP
|
|
94
|
+
} else {
|
|
95
|
+
Config.InstantPlacementMode.DISABLED
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Registers `onTap` / `onPlaneDetected` so React Native delivers the native
|
|
101
|
+
* event dispatches to the matching JS props. Without these entries the
|
|
102
|
+
* events are silently dropped even when the native side dispatches them
|
|
103
|
+
* (issue #2053).
|
|
104
|
+
*/
|
|
105
|
+
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> =
|
|
106
|
+
MapBuilder.builder<String, Any>()
|
|
107
|
+
.put(TapEvent.NAME, MapBuilder.of("registrationName", "onTap"))
|
|
108
|
+
.put(
|
|
109
|
+
PlaneDetectedEvent.NAME,
|
|
110
|
+
MapBuilder.of("registrationName", "onPlaneDetected"),
|
|
111
|
+
)
|
|
112
|
+
.build()
|
|
113
|
+
|
|
51
114
|
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
52
115
|
val container = FrameLayout(reactContext)
|
|
53
116
|
val state = ARSceneViewState()
|
|
54
117
|
container.tag = state
|
|
55
118
|
|
|
119
|
+
// Tap gesture → JS `onTap` (issue #2053).
|
|
120
|
+
val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
|
|
121
|
+
override fun onSingleTapConfirmed(
|
|
122
|
+
e: android.view.MotionEvent,
|
|
123
|
+
node: io.github.sceneview.node.Node?,
|
|
124
|
+
) {
|
|
125
|
+
dispatchTapEvent(reactContext, container, node)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
56
129
|
val composeView = ComposeView(reactContext).apply {
|
|
57
130
|
setContent {
|
|
58
131
|
val engine = rememberEngine()
|
|
59
132
|
val modelLoader = rememberModelLoader(engine)
|
|
60
133
|
val materialLoader = rememberMaterialLoader(engine)
|
|
61
134
|
|
|
135
|
+
// Read the AR config flags so they recompose this content when
|
|
136
|
+
// JS toggles a prop (issue #2055).
|
|
137
|
+
val depthOcclusion = state.depthOcclusion.value
|
|
138
|
+
val instantPlacement = state.instantPlacement.value
|
|
139
|
+
|
|
140
|
+
// Live ARCore session, captured once it is created. The
|
|
141
|
+
// consumed `arsceneview:4.7.0` invokes `sessionConfiguration`
|
|
142
|
+
// ONLY at session creation — it never re-runs the lambda on a
|
|
143
|
+
// later recomposition — so a JS prop toggle would otherwise be
|
|
144
|
+
// a silent no-op. Holding the Session lets the LaunchedEffect
|
|
145
|
+
// below re-apply the Config to the running session (#2070).
|
|
146
|
+
val sessionRef = remember { AtomicReference<Session?>(null) }
|
|
147
|
+
|
|
62
148
|
io.github.sceneview.ar.ARSceneView(
|
|
63
149
|
modifier = Modifier.fillMaxSize(),
|
|
64
150
|
surfaceType = SurfaceType.TextureSurface,
|
|
@@ -66,6 +152,35 @@ class ARSceneViewManager : SimpleViewManager<FrameLayout>() {
|
|
|
66
152
|
modelLoader = modelLoader,
|
|
67
153
|
materialLoader = materialLoader,
|
|
68
154
|
planeRenderer = state.planeDetection.value,
|
|
155
|
+
onGestureListener = gestureListener,
|
|
156
|
+
// depthOcclusion / instantPlacement → ARCore Config at
|
|
157
|
+
// session creation. The typed `depthMode` /
|
|
158
|
+
// `instantPlacementMode` composable params (#1766) post-date
|
|
159
|
+
// the consumed arsceneview artifact, so the INITIAL values
|
|
160
|
+
// are applied via the stable `sessionConfiguration`
|
|
161
|
+
// callback. `sessionConfiguration` runs only once (at
|
|
162
|
+
// creation); runtime toggles are re-applied by the
|
|
163
|
+
// LaunchedEffect below (issue #2070).
|
|
164
|
+
sessionConfiguration = { session, config ->
|
|
165
|
+
applyArConfig(session, config, depthOcclusion, instantPlacement)
|
|
166
|
+
},
|
|
167
|
+
// Capture the live Session so prop toggles can re-configure
|
|
168
|
+
// it after creation (issue #2070).
|
|
169
|
+
onSessionCreated = { session ->
|
|
170
|
+
sessionRef.set(session)
|
|
171
|
+
},
|
|
172
|
+
// Per-frame plane diff → JS `onPlaneDetected`. Fires once
|
|
173
|
+
// per newly-tracked ARCore plane (issue #2053).
|
|
174
|
+
onSessionUpdated = { _, frame ->
|
|
175
|
+
for (plane in frame.getUpdatedPlanes()) {
|
|
176
|
+
if (plane.trackingState != com.google.ar.core.TrackingState.TRACKING) {
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
if (state.reportedPlanes.add(plane)) {
|
|
180
|
+
dispatchPlaneEvent(reactContext, container, plane)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
69
184
|
) {
|
|
70
185
|
state.modelPaths.forEach { model ->
|
|
71
186
|
val instance = rememberModelInstance(modelLoader, model.src)
|
|
@@ -158,6 +273,30 @@ class ARSceneViewManager : SimpleViewManager<FrameLayout>() {
|
|
|
158
273
|
)
|
|
159
274
|
}
|
|
160
275
|
}
|
|
276
|
+
|
|
277
|
+
// Re-apply the AR config to the LIVE session whenever JS
|
|
278
|
+
// toggles `depthOcclusion` / `instantPlacement` after the
|
|
279
|
+
// session was created. `sessionConfiguration` only runs once
|
|
280
|
+
// (at creation), so without this the toggle is a silent no-op
|
|
281
|
+
// on the running session (issue #2070). Keyed on the flag
|
|
282
|
+
// values so it re-runs on every change; `Session.configure`
|
|
283
|
+
// is a no-op for the initial values already applied at
|
|
284
|
+
// creation. ARCore's `configure` is safe to call on a
|
|
285
|
+
// resumed session.
|
|
286
|
+
LaunchedEffect(depthOcclusion, instantPlacement) {
|
|
287
|
+
sessionRef.get()?.let { session ->
|
|
288
|
+
runCatching {
|
|
289
|
+
session.configure { config ->
|
|
290
|
+
applyArConfig(
|
|
291
|
+
session,
|
|
292
|
+
config,
|
|
293
|
+
depthOcclusion,
|
|
294
|
+
instantPlacement,
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
161
300
|
}
|
|
162
301
|
}
|
|
163
302
|
container.addView(composeView)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
package io.github.sceneview.reactnative
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
5
|
+
import com.facebook.react.bridge.ReactContext
|
|
6
|
+
import com.facebook.react.bridge.WritableMap
|
|
7
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
8
|
+
import com.facebook.react.uimanager.events.Event
|
|
9
|
+
import com.google.ar.core.Plane
|
|
10
|
+
import io.github.sceneview.node.Node
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* RN Fabric event delivered to the JS `onTap` prop of `<SceneView>` / `<ARSceneView>`.
|
|
14
|
+
*
|
|
15
|
+
* Payload mirrors the TypeScript `TapEvent` interface in `src/index.tsx`:
|
|
16
|
+
* `{ x, y, z, nodeName? }`. Wired in [SceneViewManager] / [ARSceneViewManager]
|
|
17
|
+
* via `getExportedCustomDirectEventTypeConstants` (issue #2053).
|
|
18
|
+
*/
|
|
19
|
+
class TapEvent(
|
|
20
|
+
surfaceId: Int,
|
|
21
|
+
viewId: Int,
|
|
22
|
+
private val x: Float,
|
|
23
|
+
private val y: Float,
|
|
24
|
+
private val z: Float,
|
|
25
|
+
private val nodeName: String?,
|
|
26
|
+
) : Event<TapEvent>(surfaceId, viewId) {
|
|
27
|
+
|
|
28
|
+
override fun getEventName(): String = NAME
|
|
29
|
+
|
|
30
|
+
override fun getEventData(): WritableMap = Arguments.createMap().apply {
|
|
31
|
+
putDouble("x", x.toDouble())
|
|
32
|
+
putDouble("y", y.toDouble())
|
|
33
|
+
putDouble("z", z.toDouble())
|
|
34
|
+
if (nodeName != null) putString("nodeName", nodeName) else putNull("nodeName")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
companion object {
|
|
38
|
+
const val NAME = "topSceneViewTap"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* RN Fabric event delivered to the JS `onPlaneDetected` prop of `<ARSceneView>`.
|
|
44
|
+
*
|
|
45
|
+
* Payload mirrors the TypeScript `PlaneDetectedEvent` interface in `src/index.tsx`:
|
|
46
|
+
* `{ id, type, center, extent }`. Fires once per newly-detected ARCore plane
|
|
47
|
+
* (issue #2053).
|
|
48
|
+
*/
|
|
49
|
+
class PlaneDetectedEvent(
|
|
50
|
+
surfaceId: Int,
|
|
51
|
+
viewId: Int,
|
|
52
|
+
private val id: String,
|
|
53
|
+
private val type: String,
|
|
54
|
+
private val center: FloatArray,
|
|
55
|
+
private val extent: FloatArray,
|
|
56
|
+
) : Event<PlaneDetectedEvent>(surfaceId, viewId) {
|
|
57
|
+
|
|
58
|
+
override fun getEventName(): String = NAME
|
|
59
|
+
|
|
60
|
+
override fun getEventData(): WritableMap = Arguments.createMap().apply {
|
|
61
|
+
putString("id", id)
|
|
62
|
+
putString("type", type)
|
|
63
|
+
putArray("center", Arguments.fromArray(center))
|
|
64
|
+
putArray("extent", Arguments.fromArray(extent))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
companion object {
|
|
68
|
+
const val NAME = "topSceneViewPlaneDetected"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Dispatches a [TapEvent] for [view] through React Native's [EventDispatcher],
|
|
74
|
+
* carrying the tapped [node]'s name and world-space position (issue #2053).
|
|
75
|
+
*/
|
|
76
|
+
internal fun dispatchTapEvent(
|
|
77
|
+
reactContext: ReactContext,
|
|
78
|
+
view: View,
|
|
79
|
+
node: Node?,
|
|
80
|
+
) {
|
|
81
|
+
val surfaceId = UIManagerHelper.getSurfaceId(view)
|
|
82
|
+
val dispatcher =
|
|
83
|
+
UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) ?: return
|
|
84
|
+
val worldPosition = node?.worldPosition
|
|
85
|
+
dispatcher.dispatchEvent(
|
|
86
|
+
TapEvent(
|
|
87
|
+
surfaceId = surfaceId,
|
|
88
|
+
viewId = view.id,
|
|
89
|
+
x = worldPosition?.x ?: 0f,
|
|
90
|
+
y = worldPosition?.y ?: 0f,
|
|
91
|
+
z = worldPosition?.z ?: 0f,
|
|
92
|
+
nodeName = node?.name,
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Maps an ARCore [Plane] to a [PlaneDetectedEvent] and dispatches it for [view]
|
|
99
|
+
* through React Native's [EventDispatcher] (issue #2053).
|
|
100
|
+
*
|
|
101
|
+
* The JS `PlaneDetectedEvent` only distinguishes `'horizontal'` / `'vertical'`,
|
|
102
|
+
* so ARCore's two horizontal sub-types both collapse to `'horizontal'`.
|
|
103
|
+
*/
|
|
104
|
+
internal fun dispatchPlaneEvent(
|
|
105
|
+
reactContext: ReactContext,
|
|
106
|
+
view: View,
|
|
107
|
+
plane: Plane,
|
|
108
|
+
) {
|
|
109
|
+
val surfaceId = UIManagerHelper.getSurfaceId(view)
|
|
110
|
+
val dispatcher =
|
|
111
|
+
UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) ?: return
|
|
112
|
+
val type = when (plane.type) {
|
|
113
|
+
Plane.Type.VERTICAL -> "vertical"
|
|
114
|
+
else -> "horizontal"
|
|
115
|
+
}
|
|
116
|
+
val centerPose = plane.centerPose
|
|
117
|
+
dispatcher.dispatchEvent(
|
|
118
|
+
PlaneDetectedEvent(
|
|
119
|
+
surfaceId = surfaceId,
|
|
120
|
+
viewId = view.id,
|
|
121
|
+
// ARCore exposes no stable string id; the Plane's identity hash is
|
|
122
|
+
// stable for the lifetime of the trackable, which is all the JS
|
|
123
|
+
// `id` field needs.
|
|
124
|
+
id = System.identityHashCode(plane).toString(),
|
|
125
|
+
type = type,
|
|
126
|
+
center = floatArrayOf(centerPose.tx(), centerPose.ty(), centerPose.tz()),
|
|
127
|
+
extent = floatArrayOf(plane.extentX, plane.extentZ),
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
}
|
|
@@ -10,12 +10,14 @@ import androidx.compose.ui.Modifier
|
|
|
10
10
|
import androidx.compose.ui.platform.ComposeView
|
|
11
11
|
import com.facebook.react.bridge.ReadableArray
|
|
12
12
|
import com.facebook.react.bridge.ReadableType
|
|
13
|
+
import com.facebook.react.common.MapBuilder
|
|
13
14
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
14
15
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
16
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
16
17
|
import com.google.android.filament.LightManager
|
|
17
18
|
import io.github.sceneview.SceneView
|
|
18
19
|
import io.github.sceneview.SurfaceType
|
|
20
|
+
import io.github.sceneview.gesture.GestureDetector
|
|
19
21
|
import io.github.sceneview.math.Direction
|
|
20
22
|
import io.github.sceneview.math.Position
|
|
21
23
|
import io.github.sceneview.math.Rotation
|
|
@@ -69,11 +71,33 @@ class SceneViewManager : SimpleViewManager<FrameLayout>() {
|
|
|
69
71
|
return view.tag as? SceneViewState ?: SceneViewState().also { view.tag = it }
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Registers `onTap` so React Native delivers the native [TapEvent] dispatch
|
|
76
|
+
* to the JS `onTap` prop. Without this entry the event is silently dropped
|
|
77
|
+
* even when the native side dispatches it (issue #2053).
|
|
78
|
+
*/
|
|
79
|
+
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> =
|
|
80
|
+
MapBuilder.builder<String, Any>()
|
|
81
|
+
.put(TapEvent.NAME, MapBuilder.of("registrationName", "onTap"))
|
|
82
|
+
.build()
|
|
83
|
+
|
|
72
84
|
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
73
85
|
val container = FrameLayout(reactContext)
|
|
74
86
|
val state = SceneViewState()
|
|
75
87
|
container.tag = state
|
|
76
88
|
|
|
89
|
+
// Tap gesture → JS `onTap`. SceneView's GestureDetector resolves the
|
|
90
|
+
// tapped node (if any) via collision hit-testing, so the event carries
|
|
91
|
+
// the node name and its world-space position (issue #2053).
|
|
92
|
+
val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
|
|
93
|
+
override fun onSingleTapConfirmed(
|
|
94
|
+
e: android.view.MotionEvent,
|
|
95
|
+
node: io.github.sceneview.node.Node?,
|
|
96
|
+
) {
|
|
97
|
+
dispatchTapEvent(reactContext, container, node)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
77
101
|
val composeView = ComposeView(reactContext).apply {
|
|
78
102
|
setContent {
|
|
79
103
|
val engine = rememberEngine()
|
|
@@ -100,6 +124,7 @@ class SceneViewManager : SimpleViewManager<FrameLayout>() {
|
|
|
100
124
|
materialLoader = materialLoader,
|
|
101
125
|
cameraNode = cameraNode,
|
|
102
126
|
environment = environment ?: rememberEnvironment(environmentLoader),
|
|
127
|
+
onGestureListener = gestureListener,
|
|
103
128
|
) {
|
|
104
129
|
state.modelPaths.forEach { model ->
|
|
105
130
|
val instance = rememberModelInstance(modelLoader, model.src)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import React
|
|
3
|
+
import RealityKit
|
|
3
4
|
import SceneViewSwift
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
@@ -51,6 +52,11 @@ class RNSceneState: ObservableObject {
|
|
|
51
52
|
@Published var cameraOrbit: Bool = true
|
|
52
53
|
@Published var cameraControlMode: CameraControlMode = .orbit
|
|
53
54
|
@Published var autoCenterContent: Bool = true
|
|
55
|
+
|
|
56
|
+
/// Invoked from the SwiftUI content's `onEntityTapped` modifier so the
|
|
57
|
+
/// wrapper can forward the tap to React Native's `onTap` prop (issue #2053).
|
|
58
|
+
/// Not `@Published` — it is plumbing, not rendered state.
|
|
59
|
+
var onTap: ((Entity) -> Void)?
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
/// UIView wrapper that hosts a SwiftUI `SceneView` via UIHostingController.
|
|
@@ -60,7 +66,24 @@ class RNSceneViewWrapper: UIView {
|
|
|
60
66
|
private let sceneState = RNSceneState()
|
|
61
67
|
|
|
62
68
|
/// Event callback for tap events.
|
|
63
|
-
@objc var onTap: RCTDirectEventBlock?
|
|
69
|
+
@objc var onTap: RCTDirectEventBlock? {
|
|
70
|
+
didSet {
|
|
71
|
+
let block = onTap
|
|
72
|
+
Task { @MainActor in
|
|
73
|
+
sceneState.onTap = { entity in
|
|
74
|
+
// Mirrors the Android `TapEvent` payload: world-space
|
|
75
|
+
// coordinates of the tapped entity + its node name.
|
|
76
|
+
let p = entity.position(relativeTo: nil)
|
|
77
|
+
block?([
|
|
78
|
+
"x": p.x,
|
|
79
|
+
"y": p.y,
|
|
80
|
+
"z": p.z,
|
|
81
|
+
"nodeName": entity.name,
|
|
82
|
+
])
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
64
87
|
|
|
65
88
|
override init(frame: CGRect) {
|
|
66
89
|
super.init(frame: frame)
|
|
@@ -145,20 +168,75 @@ class RNSceneViewWrapper: UIView {
|
|
|
145
168
|
}
|
|
146
169
|
}
|
|
147
170
|
|
|
148
|
-
/// SwiftUI content view rendering SceneViewSwift.SceneView.
|
|
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.
|
|
149
185
|
struct RNSceneViewContent: View {
|
|
150
186
|
@ObservedObject var state: RNSceneState
|
|
151
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
|
+
|
|
152
192
|
var body: some View {
|
|
153
|
-
SceneView {
|
|
154
|
-
|
|
155
|
-
ModelNode(model.path)
|
|
156
|
-
.position(model.position)
|
|
157
|
-
.scale(model.scale)
|
|
158
|
-
}
|
|
193
|
+
SceneView { root in
|
|
194
|
+
root.addChild(modelRoot)
|
|
159
195
|
}
|
|
160
196
|
.cameraControls(state.cameraControlMode)
|
|
161
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)")
|
|
238
|
+
}
|
|
239
|
+
}
|
|
162
240
|
}
|
|
163
241
|
}
|
|
164
242
|
|
|
@@ -185,6 +263,11 @@ class RNARSceneState: ObservableObject {
|
|
|
185
263
|
@Published var planeDetection: Bool = true
|
|
186
264
|
@Published var depthOcclusion: Bool = false
|
|
187
265
|
@Published var instantPlacement: Bool = false
|
|
266
|
+
|
|
267
|
+
/// Invoked from `ARSceneView`'s `onTapOnPlane` so the wrapper can forward
|
|
268
|
+
/// the tap to React Native's `onTap` prop (issue #2053). Not `@Published` —
|
|
269
|
+
/// it is plumbing, not rendered state.
|
|
270
|
+
var onTap: ((SIMD3<Float>) -> Void)?
|
|
188
271
|
}
|
|
189
272
|
|
|
190
273
|
/// UIView wrapper that hosts a SwiftUI `ARSceneView` via UIHostingController.
|
|
@@ -194,9 +277,30 @@ class RNARSceneViewWrapper: UIView {
|
|
|
194
277
|
private let sceneState = RNARSceneState()
|
|
195
278
|
|
|
196
279
|
/// Event callback for tap events.
|
|
197
|
-
@objc var onTap: RCTDirectEventBlock?
|
|
280
|
+
@objc var onTap: RCTDirectEventBlock? {
|
|
281
|
+
didSet {
|
|
282
|
+
let block = onTap
|
|
283
|
+
Task { @MainActor in
|
|
284
|
+
sceneState.onTap = { worldPosition in
|
|
285
|
+
// Mirrors the Android `TapEvent` payload. `ARSceneView`'s
|
|
286
|
+
// `onTapOnPlane` reports the tapped surface point, not a
|
|
287
|
+
// node, so `nodeName` is left absent.
|
|
288
|
+
block?([
|
|
289
|
+
"x": worldPosition.x,
|
|
290
|
+
"y": worldPosition.y,
|
|
291
|
+
"z": worldPosition.z,
|
|
292
|
+
])
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
198
297
|
|
|
199
298
|
/// Event callback for plane detection events.
|
|
299
|
+
///
|
|
300
|
+
/// **iOS limitation (issue #2053):** SceneViewSwift's `ARSceneView` does
|
|
301
|
+
/// not expose a public per-plane-detected callback — only `onTapOnPlane`.
|
|
302
|
+
/// The block is accepted for API compatibility but is not yet invoked on
|
|
303
|
+
/// iOS; the TypeScript doc comment for `onPlaneDetected` discloses this.
|
|
200
304
|
@objc var onPlaneDetected: RCTDirectEventBlock?
|
|
201
305
|
|
|
202
306
|
override init(frame: CGRect) {
|
|
@@ -277,15 +381,99 @@ class RNARSceneViewWrapper: UIView {
|
|
|
277
381
|
}
|
|
278
382
|
}
|
|
279
383
|
|
|
280
|
-
/// SwiftUI content view rendering SceneViewSwift.ARSceneView.
|
|
384
|
+
/// SwiftUI content view rendering `SceneViewSwift.ARSceneView` (issue #2067).
|
|
385
|
+
///
|
|
386
|
+
/// `SceneViewSwift.ARSceneView` is a `UIViewRepresentable` with **no content
|
|
387
|
+
/// builder closure** — content is added imperatively to the underlying
|
|
388
|
+
/// `ARView` once the session starts (`onSessionStarted`) or on tap
|
|
389
|
+
/// (`onTapOnPlane`). The previous `ARSceneView(...) { anchor in ForEach … }`
|
|
390
|
+
/// trailing closure referenced API that does not exist and never compiled.
|
|
391
|
+
///
|
|
392
|
+
/// This bridge captures the `ARView` in `onSessionStarted`, then loads each
|
|
393
|
+
/// `RNModelData` (async, via `ModelNode.load(_:)`) into a single
|
|
394
|
+
/// `AnchorNode` anchored at the world origin. The models are (re)placed in a
|
|
395
|
+
/// `.task(id:)` keyed on the JS `modelNodes` prop so prop changes are honoured
|
|
396
|
+
/// after the session has already started.
|
|
281
397
|
struct RNARSceneViewContent: View {
|
|
282
398
|
@ObservedObject var state: RNARSceneState
|
|
283
399
|
|
|
400
|
+
/// Captured once the AR session starts so prop-driven model reloads can
|
|
401
|
+
/// add / remove content after `makeUIView`. Held in a reference box so a
|
|
402
|
+
/// SwiftUI body re-evaluation does not lose it.
|
|
403
|
+
@State private var sessionBox = ARSessionBox()
|
|
404
|
+
|
|
405
|
+
/// Reference holder for the captured `ARView` + content anchor. A class
|
|
406
|
+
/// keeps the references stable across `RNARSceneViewContent` value copies.
|
|
407
|
+
@MainActor
|
|
408
|
+
final class ARSessionBox {
|
|
409
|
+
weak var arView: ARView?
|
|
410
|
+
/// Anchor that owns every placed model. Added to the scene the first
|
|
411
|
+
/// time the session starts; its children are rebuilt on prop changes.
|
|
412
|
+
var contentAnchor: AnchorNode?
|
|
413
|
+
}
|
|
414
|
+
|
|
284
415
|
var body: some View {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
416
|
+
// Forward surface taps to React Native's `onTap` prop (issue #2053).
|
|
417
|
+
// `depthOcclusion` / `instantPlacement` are accepted as props but have
|
|
418
|
+
// no `ARSceneView` configuration knob in SceneViewSwift yet — the
|
|
419
|
+
// TypeScript doc comments disclose that iOS gap (issue #2055).
|
|
420
|
+
ARSceneView(
|
|
421
|
+
planeDetection: state.planeDetection ? .both : .none,
|
|
422
|
+
onTapOnPlane: { worldPosition, _ in
|
|
423
|
+
state.onTap?(worldPosition)
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
.onSessionStarted { arView in
|
|
427
|
+
sessionBox.arView = arView
|
|
428
|
+
let anchor = AnchorNode.world(position: .zero)
|
|
429
|
+
arView.scene.addAnchor(anchor.entity)
|
|
430
|
+
sessionBox.contentAnchor = anchor
|
|
431
|
+
}
|
|
432
|
+
// (Re)load models whenever the JS `modelNodes` prop changes — runs
|
|
433
|
+
// after `onSessionStarted` too, so the initial set is placed once the
|
|
434
|
+
// session is up.
|
|
435
|
+
.task(id: state.models.map(\.id)) {
|
|
436
|
+
await placeModels()
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/// Loads every model in `state.models` and replaces the content anchor's
|
|
441
|
+
/// children with the freshly loaded entities. Waits until the AR session
|
|
442
|
+
/// has provided a content anchor before placing anything.
|
|
443
|
+
@MainActor
|
|
444
|
+
private func placeModels() async {
|
|
445
|
+
// The session may not have started yet on the first invocation —
|
|
446
|
+
// poll briefly so the initial model set still lands.
|
|
447
|
+
var anchor = sessionBox.contentAnchor
|
|
448
|
+
var waited = 0
|
|
449
|
+
while anchor == nil && waited < 50 { // up to ~5 s
|
|
450
|
+
try? await Task.sleep(nanoseconds: 100_000_000)
|
|
451
|
+
waited += 1
|
|
452
|
+
anchor = sessionBox.contentAnchor
|
|
453
|
+
}
|
|
454
|
+
guard let anchor else { return }
|
|
455
|
+
// The poll above `await`s; if the `modelNodes` prop changed while we
|
|
456
|
+
// waited, this task was superseded — do not clear/repopulate the anchor.
|
|
457
|
+
guard !Task.isCancelled else { return }
|
|
458
|
+
anchor.removeAll()
|
|
459
|
+
for model in state.models {
|
|
460
|
+
do {
|
|
461
|
+
let node = try await ModelNode.load(model.path)
|
|
462
|
+
// `.task(id:)` cancels this task when the `modelNodes` prop
|
|
463
|
+
// changes mid-load. A cancelled task still resumes past the
|
|
464
|
+
// `await`, so bail out before mutating the scene — otherwise a
|
|
465
|
+
// superseded load leaks a stale model into the content anchor.
|
|
466
|
+
guard !Task.isCancelled else { return }
|
|
467
|
+
node.position(model.position)
|
|
468
|
+
node.scale(model.scale)
|
|
469
|
+
if let animation = model.animation {
|
|
470
|
+
node.playAnimation(named: animation)
|
|
471
|
+
} else if node.animationCount > 0 {
|
|
472
|
+
node.playAllAnimations()
|
|
473
|
+
}
|
|
474
|
+
anchor.add(node.entity)
|
|
475
|
+
} catch {
|
|
476
|
+
print("[RNARSceneView] Failed to load model '\(model.path)': \(error)")
|
|
289
477
|
}
|
|
290
478
|
}
|
|
291
479
|
}
|
|
@@ -306,7 +494,11 @@ class RNARRecorder: NSObject {
|
|
|
306
494
|
/// so multiple JS instances still drive one underlying recorder.
|
|
307
495
|
@MainActor private lazy var recorder = ARRecorder()
|
|
308
496
|
|
|
309
|
-
|
|
497
|
+
/// `RNARRecorder` extends `NSObject` directly (it is registered as an
|
|
498
|
+
/// `RCT_EXTERN_MODULE`, not a view manager), so this is **not** an
|
|
499
|
+
/// `override` — `NSObject` has no `requiresMainQueueSetup`. React Native
|
|
500
|
+
/// reads the static method via the bridge-module protocol. (#2067)
|
|
501
|
+
@objc static func requiresMainQueueSetup() -> Bool {
|
|
310
502
|
return true
|
|
311
503
|
}
|
|
312
504
|
|
package/lib/commonjs/index.js
CHANGED
|
@@ -13,9 +13,25 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
13
13
|
|
|
14
14
|
/** A 3D model loaded from a .glb / .gltf file. */
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* A procedural geometry node (box, sphere, cylinder, plane).
|
|
18
|
+
*
|
|
19
|
+
* Platform support:
|
|
20
|
+
* - **Android**: fully rendered.
|
|
21
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
22
|
+
* not currently map procedural geometry nodes. Tracked under the
|
|
23
|
+
* cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
|
|
24
|
+
*/
|
|
17
25
|
|
|
18
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* A light source in the scene.
|
|
28
|
+
*
|
|
29
|
+
* Platform support:
|
|
30
|
+
* - **Android**: fully rendered.
|
|
31
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
32
|
+
* not currently map declarative light nodes. Tracked under the
|
|
33
|
+
* cross-platform bridge-parity umbrella (#909).
|
|
34
|
+
*/
|
|
19
35
|
|
|
20
36
|
// ---------------------------------------------------------------------------
|
|
21
37
|
// Event payloads
|
|
@@ -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":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AASsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAEtB;AACA;AACA;;AAEA;;AAiBA;;AAmBA;;AASA;AACA;AACA;;AAkBA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;
|
|
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
|
@@ -7,9 +7,25 @@ import { requireNativeComponent, NativeModules, Platform, View, Text, StyleSheet
|
|
|
7
7
|
|
|
8
8
|
/** A 3D model loaded from a .glb / .gltf file. */
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* A procedural geometry node (box, sphere, cylinder, plane).
|
|
12
|
+
*
|
|
13
|
+
* Platform support:
|
|
14
|
+
* - **Android**: fully rendered.
|
|
15
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
16
|
+
* not currently map procedural geometry nodes. Tracked under the
|
|
17
|
+
* cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
|
|
18
|
+
*/
|
|
11
19
|
|
|
12
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* A light source in the scene.
|
|
22
|
+
*
|
|
23
|
+
* Platform support:
|
|
24
|
+
* - **Android**: fully rendered.
|
|
25
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
26
|
+
* not currently map declarative light nodes. Tracked under the
|
|
27
|
+
* cross-platform bridge-parity umbrella (#909).
|
|
28
|
+
*/
|
|
13
29
|
|
|
14
30
|
// ---------------------------------------------------------------------------
|
|
15
31
|
// Event payloads
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACEC,sBAAsB,EACtBC,aAAa,EACbC,QAAQ,EAGRC,IAAI,EACJC,IAAI,EACJC,UAAU,QACL,cAAc;;AAErB;AACA;AACA;;AAEA;;AAiBA;;AAmBA;;AASA;AACA;AACA;;AAkBA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;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":[]}
|
|
@@ -16,7 +16,15 @@ export interface ModelNode {
|
|
|
16
16
|
*/
|
|
17
17
|
animation?: string;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* A procedural geometry node (box, sphere, cylinder, plane).
|
|
21
|
+
*
|
|
22
|
+
* Platform support:
|
|
23
|
+
* - **Android**: fully rendered.
|
|
24
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
25
|
+
* not currently map procedural geometry nodes. Tracked under the
|
|
26
|
+
* cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
|
|
27
|
+
*/
|
|
20
28
|
export interface GeometryNode {
|
|
21
29
|
type: 'box' | 'cube' | 'sphere' | 'cylinder' | 'plane';
|
|
22
30
|
size?: [number, number, number];
|
|
@@ -34,7 +42,15 @@ export interface GeometryNode {
|
|
|
34
42
|
*/
|
|
35
43
|
unlit?: boolean;
|
|
36
44
|
}
|
|
37
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* A light source in the scene.
|
|
47
|
+
*
|
|
48
|
+
* Platform support:
|
|
49
|
+
* - **Android**: fully rendered.
|
|
50
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
51
|
+
* not currently map declarative light nodes. Tracked under the
|
|
52
|
+
* cross-platform bridge-parity umbrella (#909).
|
|
53
|
+
*/
|
|
38
54
|
export interface LightNode {
|
|
39
55
|
type: 'directional' | 'point' | 'spot';
|
|
40
56
|
intensity?: number;
|
|
@@ -72,9 +88,17 @@ export interface SceneViewProps {
|
|
|
72
88
|
environment?: string;
|
|
73
89
|
/** Model nodes to render in the scene. */
|
|
74
90
|
modelNodes?: ModelNode[];
|
|
75
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Geometry nodes to render in the scene.
|
|
93
|
+
*
|
|
94
|
+
* **iOS:** acknowledged but not yet rendered — see {@link GeometryNode}.
|
|
95
|
+
*/
|
|
76
96
|
geometryNodes?: GeometryNode[];
|
|
77
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Light nodes in the scene.
|
|
99
|
+
*
|
|
100
|
+
* **iOS:** acknowledged but not yet rendered — see {@link LightNode}.
|
|
101
|
+
*/
|
|
78
102
|
lightNodes?: LightNode[];
|
|
79
103
|
/** Enable default orbit camera controls. Default: true. */
|
|
80
104
|
cameraOrbit?: boolean;
|
|
@@ -91,17 +115,46 @@ export interface SceneViewProps {
|
|
|
91
115
|
* issue #1051.
|
|
92
116
|
*/
|
|
93
117
|
autoCenterContent?: boolean;
|
|
94
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Called when the user taps inside the scene.
|
|
120
|
+
*
|
|
121
|
+
* The event payload carries the world-space tap coordinates. On Android the
|
|
122
|
+
* tapped node's `nodeName` is included when the tap hits a node; on iOS AR
|
|
123
|
+
* the tap reports the surface point only, so `nodeName` is absent there.
|
|
124
|
+
*/
|
|
95
125
|
onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
|
|
96
126
|
}
|
|
97
127
|
export interface ARSceneViewProps extends SceneViewProps {
|
|
98
128
|
/** Enable plane detection. Default: true. */
|
|
99
129
|
planeDetection?: boolean;
|
|
100
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Enable depth occlusion (ARCore Depth API / LiDAR). Default: false.
|
|
132
|
+
*
|
|
133
|
+
* Platform support:
|
|
134
|
+
* - **Android**: wired to ARCore's `Config.DepthMode.AUTOMATIC` (the flag is
|
|
135
|
+
* ignored on devices that do not support the Depth API).
|
|
136
|
+
* - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
|
|
137
|
+
* exposes no scene-understanding occlusion knob. Tracked under #909.
|
|
138
|
+
*/
|
|
101
139
|
depthOcclusion?: boolean;
|
|
102
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* Enable instant placement (approximate hit-test before tracking).
|
|
142
|
+
* Default: false.
|
|
143
|
+
*
|
|
144
|
+
* Platform support:
|
|
145
|
+
* - **Android**: wired to ARCore's `Config.InstantPlacementMode.LOCAL_Y_UP`.
|
|
146
|
+
* - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
|
|
147
|
+
* exposes no instant-placement knob. Tracked under #909.
|
|
148
|
+
*/
|
|
103
149
|
instantPlacement?: boolean;
|
|
104
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Called when a new plane is detected.
|
|
152
|
+
*
|
|
153
|
+
* Platform support:
|
|
154
|
+
* - **Android**: fires once per newly-tracked ARCore plane.
|
|
155
|
+
* - **iOS**: not yet dispatched — SceneViewSwift's `ARSceneView` exposes no
|
|
156
|
+
* public per-plane-detected callback. Tracked under #909.
|
|
157
|
+
*/
|
|
105
158
|
onPlaneDetected?: (event: NativeSyntheticEvent<PlaneDetectedEvent>) => void;
|
|
106
159
|
}
|
|
107
160
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,oBAAoB,EAI1B,MAAM,cAAc,CAAC;AAMtB,kDAAkD;AAClD,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED
|
|
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.15.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",
|
|
@@ -11,11 +11,19 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.authors = "SceneView contributors"
|
|
12
12
|
|
|
13
13
|
s.platforms = { :ios => "17.0" }
|
|
14
|
-
s.source = { :git => "https://github.com/sceneview/sceneview.git", :tag => s.version }
|
|
14
|
+
s.source = { :git => "https://github.com/sceneview/sceneview.git", :tag => "v#{s.version}" }
|
|
15
15
|
s.source_files = "ios/**/*.{swift,m}"
|
|
16
16
|
|
|
17
17
|
s.dependency "React-Core"
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
# `SceneViewSwift` is distributed via Swift Package Manager only — there is
|
|
20
|
+
# no published CocoaPods spec for it — so it CANNOT be declared as a
|
|
21
|
+
# `s.dependency` here (CocoaPods would fail `pod install` resolving it).
|
|
22
|
+
# The host app must add it once via Xcode's SwiftPM integration:
|
|
23
|
+
# File ▸ Add Package Dependencies… ▸ https://github.com/sceneview/SceneViewSwift
|
|
24
|
+
# The module's `ios/*.swift` `import SceneViewSwift` then resolves at the
|
|
25
|
+
# app build, exactly like any RN native module with a SwiftPM dependency.
|
|
26
|
+
# See this module's README "iOS" section.
|
|
19
27
|
|
|
20
28
|
s.swift_version = "5.9"
|
|
21
29
|
end
|
package/src/index.tsx
CHANGED
|
@@ -31,7 +31,15 @@ export interface ModelNode {
|
|
|
31
31
|
animation?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* A procedural geometry node (box, sphere, cylinder, plane).
|
|
36
|
+
*
|
|
37
|
+
* Platform support:
|
|
38
|
+
* - **Android**: fully rendered.
|
|
39
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
40
|
+
* not currently map procedural geometry nodes. Tracked under the
|
|
41
|
+
* cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
|
|
42
|
+
*/
|
|
35
43
|
export interface GeometryNode {
|
|
36
44
|
type: 'box' | 'cube' | 'sphere' | 'cylinder' | 'plane';
|
|
37
45
|
size?: [number, number, number];
|
|
@@ -50,7 +58,15 @@ export interface GeometryNode {
|
|
|
50
58
|
unlit?: boolean;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* A light source in the scene.
|
|
63
|
+
*
|
|
64
|
+
* Platform support:
|
|
65
|
+
* - **Android**: fully rendered.
|
|
66
|
+
* - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
|
|
67
|
+
* not currently map declarative light nodes. Tracked under the
|
|
68
|
+
* cross-platform bridge-parity umbrella (#909).
|
|
69
|
+
*/
|
|
54
70
|
export interface LightNode {
|
|
55
71
|
type: 'directional' | 'point' | 'spot';
|
|
56
72
|
intensity?: number;
|
|
@@ -102,9 +118,17 @@ export interface SceneViewProps {
|
|
|
102
118
|
|
|
103
119
|
/** Model nodes to render in the scene. */
|
|
104
120
|
modelNodes?: ModelNode[];
|
|
105
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Geometry nodes to render in the scene.
|
|
123
|
+
*
|
|
124
|
+
* **iOS:** acknowledged but not yet rendered — see {@link GeometryNode}.
|
|
125
|
+
*/
|
|
106
126
|
geometryNodes?: GeometryNode[];
|
|
107
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* Light nodes in the scene.
|
|
129
|
+
*
|
|
130
|
+
* **iOS:** acknowledged but not yet rendered — see {@link LightNode}.
|
|
131
|
+
*/
|
|
108
132
|
lightNodes?: LightNode[];
|
|
109
133
|
|
|
110
134
|
/** Enable default orbit camera controls. Default: true. */
|
|
@@ -125,7 +149,13 @@ export interface SceneViewProps {
|
|
|
125
149
|
*/
|
|
126
150
|
autoCenterContent?: boolean;
|
|
127
151
|
|
|
128
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* Called when the user taps inside the scene.
|
|
154
|
+
*
|
|
155
|
+
* The event payload carries the world-space tap coordinates. On Android the
|
|
156
|
+
* tapped node's `nodeName` is included when the tap hits a node; on iOS AR
|
|
157
|
+
* the tap reports the surface point only, so `nodeName` is absent there.
|
|
158
|
+
*/
|
|
129
159
|
onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
|
|
130
160
|
}
|
|
131
161
|
|
|
@@ -133,13 +163,36 @@ export interface ARSceneViewProps extends SceneViewProps {
|
|
|
133
163
|
/** Enable plane detection. Default: true. */
|
|
134
164
|
planeDetection?: boolean;
|
|
135
165
|
|
|
136
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Enable depth occlusion (ARCore Depth API / LiDAR). Default: false.
|
|
168
|
+
*
|
|
169
|
+
* Platform support:
|
|
170
|
+
* - **Android**: wired to ARCore's `Config.DepthMode.AUTOMATIC` (the flag is
|
|
171
|
+
* ignored on devices that do not support the Depth API).
|
|
172
|
+
* - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
|
|
173
|
+
* exposes no scene-understanding occlusion knob. Tracked under #909.
|
|
174
|
+
*/
|
|
137
175
|
depthOcclusion?: boolean;
|
|
138
176
|
|
|
139
|
-
/**
|
|
177
|
+
/**
|
|
178
|
+
* Enable instant placement (approximate hit-test before tracking).
|
|
179
|
+
* Default: false.
|
|
180
|
+
*
|
|
181
|
+
* Platform support:
|
|
182
|
+
* - **Android**: wired to ARCore's `Config.InstantPlacementMode.LOCAL_Y_UP`.
|
|
183
|
+
* - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
|
|
184
|
+
* exposes no instant-placement knob. Tracked under #909.
|
|
185
|
+
*/
|
|
140
186
|
instantPlacement?: boolean;
|
|
141
187
|
|
|
142
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* Called when a new plane is detected.
|
|
190
|
+
*
|
|
191
|
+
* Platform support:
|
|
192
|
+
* - **Android**: fires once per newly-tracked ARCore plane.
|
|
193
|
+
* - **iOS**: not yet dispatched — SceneViewSwift's `ARSceneView` exposes no
|
|
194
|
+
* public per-plane-detected callback. Tracked under #909.
|
|
195
|
+
*/
|
|
143
196
|
onPlaneDetected?: (event: NativeSyntheticEvent<PlaneDetectedEvent>) => void;
|
|
144
197
|
}
|
|
145
198
|
|