Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ARKit – Get current position of ARCamera in a scene

I'm in the process of learning both ARKit and Scenekit concurrently, and it's been a bit of a challenge.

With a ARWorldTrackingSessionConfiguration session created, I was wondering if anyone knew of a way to get the position of the user's 'camera' in the scene session. The idea is I want to animate an object towards the user's current position.

let reaperScene = SCNScene(named: "reaper.dae")!
let reaperNode = reaperScene.rootNode.childNode(withName: "reaper", recursively: true)!
reaperNode.position = SCNVector3Make(0, 0, -1)
let scene = SCNScene()
scene.rootNode.addChildNode(reaperNode)

// some unknown amount of time later   
let currentCameraPosition = sceneView.pointOfView?.position
let moveAction = SCNAction.move(to: currentCameraPosition!, duration: 1.0)
reaperNode.runAction(moveAction)

However, it seems that currentCameraPosition is always [0,0,0], even though I am moving the camera around. Any idea what I'm doing wrong? Eventually the idea is I would rotate the object around an invisible sphere until it is in front of the camera and then animate it in, doing something similar to this: Rotate SCNCamera node looking at an object around an imaginary sphere (that way the user sees the object animate towards them)

Thanks for any help.

like image 234
Ryan Pfister Avatar asked Jul 13 '17 14:07

Ryan Pfister


3 Answers

Set yourself as the ARSession.delegate. Than you can implement session(_:didUpdate:) which will give you an ARFrame for every frame processed in your session. The frame has an camera property that holds information on the cameras transform, rotation and position.

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    // Do something with the new transform
    let currentTransform = frame.camera.transform
    doSomething(with: currentTransform)
}

As rickster pointed out you always can get the current ARFrame and the camera position through it by calling session.currentFrame. This is useful if you need the position just once, eg to move a node where the camera has been but you should use the delegate method if you want to get updates on the camera's position.

like image 103
jlsiewert Avatar answered Nov 17 '22 23:11

jlsiewert


I know it had been solved but i have a little neat solution for it .. I would prefere adding a renderer delegate method.. it's a method in ARSCNViewDelegate

func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
    guard let pointOfView = sceneView.pointOfView else { return }
    let transform = pointOfView.transform
    let orientation = SCNVector3(-transform.m31, -transform.m32, transform.m33)
    let location = SCNVector3(transform.m41, transform.m42, transform.m43)
    let currentPositionOfCamera = orientation + location
    print(currentPositionOfCamera)
}

of course you can't by default add the two SCNVector3 out of the box.. so you need to paste out of the class the following

func +(lhv:SCNVector3, rhv:SCNVector3) -> SCNVector3 {
     return SCNVector3(lhv.x + rhv.x, lhv.y + rhv.y, lhv.z + rhv.z)
}
like image 33
Mohamed Emad Hegab Avatar answered Nov 17 '22 22:11

Mohamed Emad Hegab


ARKit + SceneKit

For your convenience you can create a ViewController extension with an instance method session(_:didUpdate:) where an update will be occured.

import ARKit
import SceneKit

extension ViewController: ARSessionDelegate {

    func session(_ session: ARSession, didUpdate frame: ARFrame) {
        let transform = frame.camera.transform
        let position = transform.columns.3
        print(position.x, position.y, position.z)     // UPDATING        
    }
}

class ViewController: UIViewController {

    @IBOutlet var sceneView: ARSCNView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        sceneView.session.delegate = self             // ARSESSION DELEGATE

        let config = ARWorldTrackingConfiguration()
        sceneView.session.run(config)
    }
}

RealityKit

In RealityKit, ARView's object contains a camera's transform as well:

import RealityKit
import UIKit
import Combine

class ViewController: UIViewController {

    @IBOutlet var arView: ARView!
    var subs: [AnyCancellable] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        arView.scene.subscribe(to: SceneEvents.Update.self) { _ in            
            let camTransform = self.arView.cameraTransform.matrix
            print(camTransform)                             // UPDATING  
        }.store(in: &subs)
    }
}
like image 7
Andy Jazz Avatar answered Nov 17 '22 21:11

Andy Jazz