ARKit – Get current position of ARCamera in a scene

Viewed 25555

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.

3 Answers

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)
}

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)
    }
}
Related