Hello,
I am currently working on an app that features multiple environments in which I combine Reality Composer Pro scenes with objects managed at runtime as well as make heavy use of RealityView attachments that modify the appearance of certain objects. Is it possible to keep track of an AR anchor when transitioning between immersive spaces?
About my app:
There are two main contexts/scenes in the app that the user progresses through. The first takes place in AR and is non-interactive and driven by a timeline animation. The second is in VR and allows the user to change materials of select models. Both scenes need to be placed relative to a real-life object that functions as an image anchor. Anchoring is necessary for visual purposes in AR context and it would be nice to use it in the VR context as well in order to provide passive haptics to the user.
If the user doesn't have access to the physical object, we make use of plane-based anchoring. Either way, we would like to keep the anchor's position across the scenes.
Hi @tomires
It was great connecting with you on Wednesday. If I understand correctly, you want to transition from mixed immersion to full immersion while maintaining the real-world position of an AR anchor. This is possible. Here's how.
- Instead of closing the old space then opening the new one, conditionally render views in a single immersive space. The former changes the scene's origin and the latter does not.
- Use a state variable to change the immersion style of the current immersive space.
Here's a code snippet. To create and run the app, create a new project then replace the generated code with the snippets below.
AppModel.swift
// Define an enum with entries for each immersive scene.
enum Scenes : String, CaseIterable, Identifiable {
case red
case green
var id: Self { self }
}
//...
class AppModel {
// Add a variable to store the current scene.
var currentScene = Scenes.red
}
<YourAppName>App.swift
//...
// Set the immersion style based on the current scene.
let immersionStyle:ImmersionStyle = appModel.currentScene == .red ? .mixed : .full
ImmersiveSpace(id: appModel.immersiveSpaceID) {
ImmersiveView()
//...
}
.immersionStyle(selection: .constant(immersionStyle), in: .mixed, .full)
ContentView.swift
// Display a picker to transition between scenes.
struct ContentView: View {
@Environment(AppModel.self) private var appModel
var body: some View {
@Bindable var appModel = appModel
VStack {
Picker("Scene", selection: $appModel.currentScene) {
ForEach(Scenes.allCases) {
Text($0.rawValue)
}
}
ToggleImmersiveSpaceButton()
}
.padding()
}
}
ImmersiveView.swift
struct ImmersiveView: View {
@Environment(AppModel.self) private var appModel
@State private var session = ARKitSession()
@State private var anchorTransform:Transform?
var body: some View {
@Bindable var appModel = appModel
VStack {
// Render the view for the current scene and pass it the anchor's transform.
if let anchorTransform {
switch appModel.currentScene {
case .red:
RedView(anchorTransform: anchorTransform)
case .green:
GreenView(anchorTransform: anchorTransform)
}
}
}
.task {
guard ImageTrackingProvider.isSupported else {
print("Image tracking is not supported.")
return
}
let referenceImages = ReferenceImage.loadReferenceImages(inGroupNamed: "AR Resources")
let imageTrackingProvider = ImageTrackingProvider(referenceImages: referenceImages)
try? await session.run([imageTrackingProvider])
for await update in imageTrackingProvider.anchorUpdates {
if update.event == .added || update.event == .removed {
anchorTransform = Transform(matrix: update.anchor.originFromAnchorTransform)
}
// TODO handle remove
}
}
}
}
struct RedView: View {
let anchorTransform:Transform
var body: some View {
RealityView { content in
let entity = ModelEntity(mesh: .generateSphere(radius: 0.2), materials: [SimpleMaterial(color: .red, isMetallic: false)])
entity.transform = anchorTransform
content.add(entity)
}
}
}
struct GreenView: View {
let anchorTransform:Transform
var body: some View {
RealityView { content in
let entity = ModelEntity(mesh: .generateSphere(radius: 0.2), materials: [SimpleMaterial(color: .green, isMetallic: false)])
entity.transform = anchorTransform
content.add(entity)
}
}
}