Search code examples
swiftscenekitarkitscnvector3

How to get the SCNVector3 position of the camera in relation to it's direction ARKit Swift


I am trying to attach an object in front of the camera, but the issue is that it is always in relation to the initial camera direction. How can I adjust/get the SCNVector3 position to place the object in front, even if the direction of the camera is up or down?

This is how I do it now:

let ballShape = SCNSphere(radius: 0.03)
let ballNode = SCNNode(geometry: ballShape)
let viewPosition = sceneView.pointOfView!.position
ballNode.position = SCNVector3Make(viewPosition.x, viewPosition.y, viewPosition.z - 0.4)
sceneView.scene.rootNode.addChildNode(ballNode)

Solution

  • Edited to better answer the question now that it's clarified in a comment

    New Answer:

    You are using only the position of the camera, so if the camera is rotated, it doesn't affect the ball.

    What you can do is get the transform matrix of the ball and multiply it by the transform matrix of the camera, that way the ball position will be relative to the full transformation of the camera, including rotation.

    e.g.

    let ballShape = SCNSphere(radius: 0.03)
    let ballNode = SCNNode(geometry: ballShape)
    ballNode.position = SCNVector3Make(0.0, 0.0, -0.4)
    let ballMatrix = ballNode.transform
    let cameraMatrix = sceneView.pointOfView!.transform
    let newBallMatrix = SCNMatrix4Mult(ballMatrix, cameraMatrix)
    ballNode.transform = newBallMatrix
    sceneView.scene.rootNode.addChildNode(ballNode)
    

    Or if you only want the SCNVector3 position, to answer exactly to your question (this way the ball will not rotate):

    ...
    let newBallMatrix = SCNMatrix4Mult(ballMatrix, cameraMatrix)
    let newBallPosition = SCNVector3Make(newBallMatrix.m41, newBallMatrix.m42, newBallMatrix.m43)
    ballNode.position = newBallPosition
    sceneView.scene.rootNode.addChildNode(ballNode)
    

    Old Answer:

    You are using only the position of the camera, so when the camera rotates, it doesn't affect the ball.

    SceneKit uses a hierarchy of nodes, so when a node is "child" of another node, it follows the position, rotation and scale of its "parent". The proper way of attaching an object to another object, in this case the camera, is to make it "child" of the camera.

    Then, when you set the position, rotation or any other aspect of the transform of the "child" node, you are setting it relative to its parent. So if you set the position to SCNVector3Make(0.0, 0.0, -0.4), it's translated -0.4 units in Z on top of its "parent" translation.

    So to make what you want, it should be:

    let ballShape = SCNSphere(radius: 0.03)
    let ballNode = SCNNode(geometry: ballShape)
    ballNode.position = SCNVector3Make(0.0, 0.0, -0.4)
    let cameraNode = sceneView.pointOfView
    cameraNode?.addChildNode(ballNode)
    

    This way, when the camera rotates, the ball follows exactly its rotation, but separated -0.4 units from the camera.