How to Get the Scnview Camera Position When Using Allowscameracontrol

Can I get the SCNView camera position when using allowsCameraControl?

When you enable allowsCameraControl, SceneKit inserts its own camera node into the scene (as an immediate child of the scene's root node), and sets that node as the view's pointOfView. Read the properties of that node to find out the camera's position and orientation, or the properties of the node's camera attribute to find the camera's field of view, depth limits, etc.

Default camera position not changing?

A new camera is created, leaving the original one unchanged.

If you show the inspector using the showsStatistics property, you'll notice the Point of View changes from the camera you had (even if it is "Untitled") to kSCNFreeViewCameraName.

Sadly there is not much documentation about this behavior but you might be able to find it in the node hierarchy.

Let us know what you find!

Can I observe the camera transform of a SCNView?

When you set allowsCameraControl to true, SceneKit adds a Camera as a child of the rootNode.

As such to access information from the camera you can do something like this in the following delegate callback:

 func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {

//1. Get The Camera From The ARSCNScene
if let currentPointOfView = augmentedRealityView?.pointOfView{

let pitch = currentPointOfView.eulerAngles.x
let yaw = currentPointOfView.eulerAngles.y
let roll = currentPointOfView.eulerAngles.z

print("""
Pitch = \(degreesFrom(pitch))
Yaw = \(degreesFrom(yaw))
Roll = \(degreesFrom(roll))
""")

}
}

/// Convert Radians To Degrees
///
/// - Parameter radian: Float
/// - Returns: Float
func degreesFrom( _ radian: Float) -> Float{

return radian * Float(180.0 / Double.pi)

}

Whereby Pitch, Yaw & Roll refer to the following:
Sample Image

Hope this gets you started...

How to ensure SceneKit camera always points at specific position

I assume you use the SCNView.allowsCameraControl = true to move the camera around. This built-in setting of SceneKit is actually only for debug purposes of your scene. It is not suited for anything when you want to have dedicated control over your camera movement.

You should instead try to implement a camera orbit, see https://stackoverflow.com/a/25674762/3358138.

I could reproduce your problem with the "wobbling" center of your scene, see Playground Gist https://gist.github.com/dirkolbrich/e2c247619b28a287c464abbc0595e23c.

A camera orbit solves this "wobbling" and let’s the camera stay on center, see Playground Gist https://gist.github.com/dirkolbrich/9e4dffb3026d0540d6edf6877f27d1e4.

Child object not following default camera in SceneKit

The problem was, that at the time

scnView.pointOfView?.addChild(plane)

was executed, the actual poV of the scene was still the old camera and not the generated one by allowsCameraControl. poV switches to the generated one as soon as the user makes first touch input.

SceneKit unprojectPoint() with custom camera

After days of testing I figured out a workaround and now I can place the nodes correctly where they should be.

My node tree was is like this:

  • RootNode
    • CameraNode
    • atomNodes
      • atom (individual spheres)

Therefore, all I had to do was to convert the unprojected position from the RootNode (which I suppose is the one that the camera takes the reference from) to the atomNodes, thus:

let unprojected = unprojectPoint(SCNVector3(location.x, location.y, 0.99))                                     
let position = atomNodes.convertPosition(unprojected, from: rootNode)

The 0.99 is just a nice Z position in my view for the spheres to be placed. (More info here)

My advice would be to always check the node tree because the positions are relative to each other.

How to set SCNView camera from ARFrame camera?

ARSession can be run as standalone entity (it must not necessarily be connected to a view). Use the following code to get what you expect:

import SceneKit
import ARKit

class ViewController: UIViewController {

@IBOutlet var sceneKitView: SCNView!
let session = ARSession()
var arCameraTransform: simd_float4x4?
let scnCamera = SCNNode()

override func viewDidLoad() {
super.viewDidLoad()

sceneKitView.scene = SCNScene()
sceneKitView.backgroundColor = .black

let cylinder = SCNCylinder(radius: 0.1, height: 1)
cylinder.firstMaterial?.diffuse.contents = UIColor.red
let node = SCNNode(geometry: cylinder)
node.position.z = -0.75
sceneKitView.scene?.rootNode.addChildNode(node)

scnCamera.camera = SCNCamera()
sceneKitView.scene?.rootNode.addChildNode(scnCamera)

self.session.delegate = self
self.session.run(ARWorldTrackingConfiguration())
}
}

...

extension ViewController: ARSessionDelegate {

func session(_ session: ARSession, didUpdate frame: ARFrame) {

self.arCameraTransform = frame.camera.transform
scnCamera.simdTransform = self.arCameraTransform!
print(sceneKitView.pointOfView?.simdTransform.columns.3 as Any)
}
}

P.S.

This works for .landscape orientation...

SCNCamera made programmatically not in the correct position

After a while, I discovered that you can actually add empty nodes directly within the Scene Builder. I originally only wanted a programmatic answer, as I wanted to make a camera orbit node like the questions I linked to. Now I can add an empty node, I can make the child of the orbit the camera.

This requires no code, unless you want to access the nodes (e.g. changing position or rotation):

gameCameraNode = gameView.pointOfView // Use camera object from scene
gameCameraOrbitNode = gameCameraNode.parent // Use camera orbit object from scene

Here are the steps to create an orbit node:

1) Drag it in from the Objects Library:
Sample Image

2) Setup up your Scene Graph like so:

Sample Image



Related Topics



Leave a reply



Submit