swift3scenekit

Position a SceneKit object in front of SCNCamera's current orientation


I would like to create a new SceneKit node when the user taps the screen, and have it appear directly in front of the camera at a set distance. For testing, this will be a SCNText reads reads "you tapped here". It should also be at right angles to the line of sight - that is, "face on" to the camera.

So, given the self.camera.orientation SCNVector4 (or similar), how can I:

  1. produce the SCNVector3 that puts the node in "front" of the camera at a given distance?
  2. produce the SCNVector4 (or SCNQuaternion) that orients the node so it is "facing" the camera?

I suspect that the answer to (2) is that it's the same as the `self.camera.orientation? But (1) has me a little lost.

I see a number of similar questions, like this one, but no answers.


Solution

  • (Swift 4)

    Hey, you can use this simpler function if you want to put the object a certain position relative to another node (e.g. the camera node) and also in the same orientation as the reference node:

    func updatePositionAndOrientationOf(_ node: SCNNode, withPosition position: SCNVector3, relativeTo referenceNode: SCNNode) {
        let referenceNodeTransform = matrix_float4x4(referenceNode.transform)
    
        // Setup a translation matrix with the desired position
        var translationMatrix = matrix_identity_float4x4
        translationMatrix.columns.3.x = position.x
        translationMatrix.columns.3.y = position.y
        translationMatrix.columns.3.z = position.z
    
        // Combine the configured translation matrix with the referenceNode's transform to get the desired position AND orientation
        let updatedTransform = matrix_multiply(referenceNodeTransform, translationMatrix)
        node.transform = SCNMatrix4(updatedTransform)
    }
    

    If you'd like to put 'node' 2m right in front of a certain 'cameraNode', you'd call it like:

    let position = SCNVector3(x: 0, y: 0, z: -2)
    updatePositionAndOrientationOf(node, withPosition: position, relativeTo: cameraNode)
    

    Edit: Getting the camera node

    To get the camera node, it depends if you're using SceneKit, ARKit, or other framework. Below are examples for ARKit and SceneKit.

    With ARKit, you have ARSCNView to render the 3D objects of an SCNScene overlapping the camera content. You can get the camera node from ARSCNView's pointOfView property:

    let cameraNode = sceneView.pointOfView
    

    For SceneKit, you have an SCNView that renders the 3D objects of an SCNScene. You can create camera nodes and position them wherever you want, so you'd do something like:

    let scnScene = SCNScene()
    // (Configure scnScene here if necessary)
    scnView.scene = scnScene
    let cameraNode = SCNNode()
    cameraNode.camera = SCNCamera()
    cameraNode.position = SCNVector3(0, 5, 10)  // For example
    scnScene.rootNode.addChildNode(cameraNode)
    

    Once a camera node has been setup, you can access the current camera in the same way as ARKit:

    let cameraNode = scnView.pointOfView