1

I am writing in Swift and trying to obtain the RealityKit camera's rotation. I've successfully gotten the position using:

xsettings.xcam = arView.session.currentFrame?.camera.transform.columns.3.x ?? 0);
xsettings.ycam = arView.session.currentFrame?.camera.transform.columns.3.y ?? 0);
xsettings.zcam = arView.session.currentFrame?.camera.transform.columns.3.z ?? 0);

this works excellently, but I haven't found a rotation solution that seems to work as well. Currently I am doing this:

xsettings.xcamrot =  arView.session.currentFrame?.camera.eulerAngles[0] ?? 0;
xsettings.ycamrot =  arView.session.currentFrame?.camera.eulerAngles[1] ?? 0;
xsettings.zcamrot =  arView.session.currentFrame?.camera.eulerAngles[2] ?? 0;

but it doesn't seem to work correctly, there is a lot of weirdness on the roll (eulerAngles[2]) and just some inconsistency overall, at least compared to the positioning which is excellent. Just curious if there is a better way to access the camera's rotation?

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
briwil78
  • 11
  • 2

2 Answers2

2

It's not weird. An orientation of ARKit's or RealityKit's camera is expressed as roll (z), pitch (x), and yaw (y). Thus you can easily get right values with expressions you've mentioned earlier:

arView.session.currentFrame?.camera.eulerAngles.x
arView.session.currentFrame?.camera.eulerAngles.y
arView.session.currentFrame?.camera.eulerAngles.z

However, the order of rotation is ZYX. Read about it here.

And several words about subscript and dot notation. Each two lines are identical:

DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
    // Pitch
    print(arView.session.currentFrame?.camera.eulerAngles[0])  // -0.6444593
    print(arView.session.currentFrame?.camera.eulerAngles.x)   // -0.6444593
    // Yaw
    print(arView.session.currentFrame?.camera.eulerAngles[1])  // -0.69380975
    print(arView.session.currentFrame?.camera.eulerAngles.y)   // -0.69380975
    // Roll
    print(arView.session.currentFrame?.camera.eulerAngles[2])  // -1.5064332
    print(arView.session.currentFrame?.camera.eulerAngles.z)   // -1.5064332
}

That's because ARView camera's eulerAngles instance property is SIMD3<Float> (a.k.a. simd_float3) type that supports subscript and dot notation.

On the other hand, ARSCNView pointOfView's eulerAngles instance property is SCNVector3 type that doesn't support subscript but supports dot notation.

P.S.

You don't need to (and can't) assign a rotation order explicitly because it's implicit inner rotation mechanism.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • 1
    Thank you, I stumbled upon this shortly after posting. It does seem better, but I'm confused, is there a difference between, say, eulerAngles.x and eulerAngles[0]? Or is it, as you say, I had the order wrong? – briwil78 Jan 26 '21 at 19:56
  • 1
    Thank you for this, but I guess I need to look further into this, because it is still being "weird", meaning that while the position behaving correctly, I am getting things like having the x.rotation at -31 one frame, and then +31 the next frame. – briwil78 Jan 26 '21 at 22:06
  • 1) It depends on orientation of your virtual World Grid. Each time you launch your AR app a World Grid is oriented differently, because it's built on the very first frame of running ARSession and depends on your device orientation (in real world) at a particular moment. You can't guarantee the same orientation of your device each time your app is launched. For instance: previous time your 3D virtual grid is real-world-North-oriented, but next time it will be real-world-east-oriented. 2) Sometimes (not often) a gimbal lock occurs in iPhone's gyroscope – https://en.wikipedia.org/wiki/Gimbal_lock – Andy Jazz Jan 27 '21 at 06:11
1

You might be better off taking the quaternion for rotation, depending on what you're wanting to do with the output.

You can also use arView.cameraTransform to get the camera's transform. From there, translation can be taken from arView.cameraTransform.translation.{x,y,z}, and quaternion rotation with arView.cameraTransform.rotation. One of the benefits of a quaternion here is that you will not have a problem with rotation order.

If you still wanted to get Euler rotations, you can always use MDLTransform:
MDLTransform(matrix: self.cameraTransform.matrix).rotation.{x,y,z}

maxxfrazer
  • 1,173
  • 6
  • 15
  • This was the most sufficient solution for me. It does not require any math and the rotation can be easily and directly applied to an entity. Still not getting the difference between the currentFrame.camera.transform matrix and arView.cameraTransform… – wider-spider Jun 30 '21 at 10:35
  • I posted a dedicated question for this here: https://stackoverflow.com/questions/68193399/what-is-the-difference-between-arview-session-currentframe-camera-transform-and – wider-spider Jun 30 '21 at 10:59