2

I want to do the followings automatically:

  • (1) asynchronously load a 3D model, for example a vase 50 cm tall
  • (2) detect the point P on the floor that is vertically down from the world origin (initial location of the device) and set P as the new world origin
  • (3) place the 3D model from (1) at P acquired in (2).

So, in effect, I'd see a vase standing at where I was if I were to step aside.

The fact that I need to wait on both (1) and (2) before I can perform (3), and that I want to perform all of them automatically got me really lost.

How should I approach this problem?

I tried to generate an AnchorEntity with the following:

let floorAnchorEntity = AnchorEntity(plane: [.horizontal],
                            classification: [.floor],
                             minimumBounds: [0.5, 0.5])
floorAnchorEntity.name = "floor anchor entity"
arView.scene.addAnchor(floorAnchorEntity)

And load a model 'synchronously' and tether to the anchor entity:

do {
    let modelEntity = try Entity.load(named: modelName)
    floorAnchorEntity.addChild(modelEntity)
} catch {
    assertionFailure("could not load assets.")
}

I was expecting to see the model on the floor but I do not see any thing on the screen. Additionally, 'isActive' flag of the anchor entity is 'false'.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
D.Park
  • 53
  • 5

1 Answers1

1

Setting new World Origin

The following approach helps you asynchronously load a Reality Composer scene, extract a model, then create and track a horizontal plane anchor (with a tethered model), and finally set a new World Origin with the help of Combine's subscriber.

However, there will be some discrepancy between the pose of the anchor and the brand-new World Origin if your tracking is bad. So, after 4 seconds, we rectify model's transform.

import RealityKit
import UIKit
import Combine

class ViewController: UIViewController {        
    @IBOutlet var arView = ARView(frame: .zero)
    var subs: [Cancellable] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()            
        arView.debugOptions = [.showWorldOrigin]

        Experience.loadBoxAsync { result in
            switch result {
                case let .success(scene):
                    guard let model = scene.steelBox else { return }

                    let anchor = AnchorEntity(.plane(.horizontal,
                                     classification: .floor,
                                      minimumBounds: [0.5, 0.5]))
                    anchor.addChild(model)
       
                    var sub = arView.scene.subscribe(to: SceneEvents.DidAddEntity.self) { _ in
                        self.arView.session.setWorldOrigin(relativeTransform: 
                                            model.transformMatrix(relativeTo: nil))
                    }
                    if subs.count == 0 { subs.append(sub) }            
                    arView.scene.anchors.append(anchor)
        
                    DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
                        print(model.position(relativeTo: nil))  // 1
                        model.setTransformMatrix(matrix_identity_float4x4, relativeTo: nil) 
                        print(model.position(relativeTo: nil))  // 2
                    }
                case let .failure(error):
                    print("Failed to asynchronously load scene due to \(error)")
            }
        }    
    }
}

// 1st print result:       SIMD3<Float>(-0.011, 0.018, -0.005)

// 2nd print result:       SIMD3<Float>(0.0, 0.0, 0.0)

P. S.

For more info, read this post.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • 1
    Thanks @Andy Jazz! You even updated your answer while I was tinkering with your first answer :D. I posted a related question here https://stackoverflow.com/q/76255008/21516248. If you can take a look and answer it I'd be most grateful. – D.Park May 15 '23 at 14:18