2

I've created an app using the RealityKit template file. Inside RealityComposer there are multiple scenes, all the scenes use image recognition that activates some animations.

Inside Xcode I have to load all the scenes as anchors and append those anchors to arView.scene.anchors array. The issue is an obvious one, as I present the physical 2D image one after the other I get multiple anchors piled on top of each other which is not desirable. I'm aware of arView.scene.anchors.removeAll() prior to loading the new anchor but my issue is this:

How do I check when a certain image has appeared to therefore remove the existing anchor and load the correct one? I've tried to look for something like there is in ARKit as didUpdate but I can't see anything similar in RealityKit.

Many thanks

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
JeremyRaven
  • 119
  • 1
  • 10

1 Answers1

1

Foreword

RealityKit's AnchorEntity(.image) coming from RC, matches ARKit's ARImageTrackingConfig. When iOS device recognises a reference image, it creates Image Anchor (that conforms to ARTrackable protocol) that tethers a corresponding 3D model. And, as you understand, you must show just one reference image at a time (in your particular case AR app can't operate normally when you give it two or more images simultaneously).


Code snippet showing how if condition logic might look like:

import SwiftUI
import RealityKit

struct ContentView : View {
    var body: some View {            
        return ARViewContainer().edgesIgnoringSafeArea(.all)
    }
}

struct ARViewContainer: UIViewRepresentable {

    func makeUIView(context: Context) -> ARView {    
        let arView = ARView(frame: .zero)

        let id02Scene = try! Experience.loadID2()            
        print(id02Scene)     // prints scene hierarchy
        
        let anchor = id02Scene.children[0]
        print(anchor.components[AnchoringComponent] as Any)
        
        if anchor.components[AnchoringComponent] == AnchoringComponent(
                      .image(group: "Experience.reality", 
                              name: "assets/MainID_4b51de84.jpeg")) {
            
            arView.scene.anchors.removeAll()
            print("LOAD SCENE")
            arView.scene.anchors.append(id02Scene)
        }
        return arView
    }
 
    func updateUIView(_ uiView: ARView, context: Context) { }
}

ID2 scene hierarchy printed in console:

enter image description here

P.S.

You should implement SwiftUI Coordinator class (read about it here), and inside Coordinator use ARSessionDelegate's session(_:didUpdate:) instance method to update anchors properties at 60 fps.

Also you may use the following logic – if anchor of scene 1 is active or anchor of scene 3 is active, just delete all anchors from collection and load scene 2.

var arView = ARView(frame: .zero)

let id01Scene = try! Experience.loadID1()
let id02Scene = try! Experience.loadID2()
let id03Scene = try! Experience.loadID3()

func makeUIView(context: Context) -> ARView {    
    arView.session.delegate = context.coordinator

    arView.scene.anchors.append(id01Scene)
    arView.scene.anchors.append(id02Scene)
    arView.scene.anchors.append(id03Scene)
    return arView
}

...

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    if arView.scene.anchors[0].isActive || arView.scene.anchors[2].isActive {
        arView.scene.anchors.removeAll()
        arView.scene.anchors.append(id02Scene)
        print("Load Scene Two")
    }
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • Thanks for that however Im getting this error "Cannot find 'anchorInRealityComposerScene' in scope". Im thinking that I should be placing my own custom name in there or else it is out of scope somehow. – JeremyRaven Jan 12 '21 at 20:57
  • `anchorInRealityComposerScene` is abstract name of your anchor in scene's hierarchy... – Andy Jazz Jan 12 '21 at 21:00
  • I see you are referring to anchors inside the Composer Scene but the only access to anchors that I can see is just using "arView.scene.anchors.components[AnchoringComponent]" which throws an error "AnchorCollection has no member 'components'" – JeremyRaven Jan 12 '21 at 21:43
  • If I am to use the reference from loading the Experience scene can I actually load all the scenes at once into one variable so that I can check that variable.components? All I have found online is to load scene by scene but I need a variable that references all the scenes at once dont I? – JeremyRaven Jan 12 '21 at 21:57
  • Of course I mean anchor entity from each scene inside `Experience.rcproject`. – Andy Jazz Jan 12 '21 at 22:08
  • Publish your code and share your scene via file sharing service (https://dropmefiles.com is OK) – and I'll try to help you. You gave a minimum information about your project. – Andy Jazz Jan 12 '21 at 22:11
  • https://dropmefiles.com/zrcxY This is quite embarrassing as I was just hacking the template file in Xcode and its a real mess, see what you can do to help. Really appreciate it. jeremyraven@icloud.com – JeremyRaven Jan 12 '21 at 22:44
  • Yes but if I copy and paste the code inside makeUIView 7 times and change the names to match each scene in RealityComposer, will it load the correct scene at the right time when it recognises the corresponding image? Thanks – JeremyRaven Jan 13 '21 at 00:13
  • What tactics you choose for loading scenes is up to you. I just showed how you can get to a specific Image Anchor, which, when triggered, will load a needed 3D model. It's your app, and you are an AR app builder)) – Andy Jazz Jan 13 '21 at 00:23
  • And one more comment – Your AR app can't operate normally if it'll simultaneously see two or more images for detection. Just single one at a time. – Andy Jazz Jan 13 '21 at 00:28
  • Really appreciate your help. I guess Im not understanding how the mechanics work with image recognition as i want the image seen through the camera to determine which scene to load therefore which anchor to add to the arview.scene – JeremyRaven Jan 13 '21 at 00:33
  • Mechanics is simple: Event -> Anchor -> Model. If model learning algorithm (ML) recognises the reference image, it creates an ImageAnchor that tethers a 3D model. – Andy Jazz Jan 13 '21 at 00:37
  • If you wanna each time remove a previous model and load a new model, you definitely need to feed a camera with just one image at a time (in your particular case). That's all. – Andy Jazz Jan 13 '21 at 00:40
  • And remember two things – 1) Each ImageAnchor (RealityKit's AnchorEntity) is located in the very center of its corresponding reference image. 2) AnchorEntity(.image) matches ARKit's ARImageTrackingConfiguration (not ARWorldTrackingConfiguration) and it's very important... – Andy Jazz Jan 13 '21 at 00:48
  • https://dropmefiles.com/sIC9U Yes but the logic I am missing is in the following link. Only the last anchor8 is loaded even if the camera sees the other images one at a time. I see that removeall removes everything else but I just want the anchors to load when the right image appears. – JeremyRaven Jan 13 '21 at 01:05
  • Have you succeeded, Jeremy? – Andy Jazz Jan 13 '21 at 19:37
  • 1
    Almost there! :) I just have model anchors persisting after the reference image in the camera view disappears. So I get multiple entities on the screen at the same time. I think I need to use the updateUIView() method here? – JeremyRaven Jan 13 '21 at 19:45
  • 100% sure – you definitely need to update these anchors at 60 fps. IMHO you need SwiftUI `Coordinator` (like here: https://stackoverflow.com/questions/60582392/swiftui-passing-data-from-swiftuiview-to-scenekit/60590065?) but instead of `renderer(_:updateAtTime:)` use ARSessionDelegate's `session(_:didUpdate:)` instance method. – Andy Jazz Jan 14 '21 at 04:55