1

I just started learning how to use SceneKit yesterday, so I may get some stuff wrong or incorrect. I am trying to make my cameraNode look at a SCNVector3 point in the scene.

I am trying to make my app available to people below iOS 11.0. However, the look(at:) function is only for iOS 11.0+.

Here is my function where I initialise the camera:

func initCamera() {
    cameraNode = SCNNode()
    cameraNode.camera = SCNCamera()
    cameraNode.position = SCNVector3(5, 12, 10)
    if #available(iOS 11.0, *) {
        cameraNode.look(at: SCNVector3(0, 5, 0)) // Calculate the look angle
    } else {
        // How can I calculate the orientation? <-----------
    }
    print(cameraNode.rotation) // Prints: SCNVector4(x: -0.7600127, y: 0.62465125, z: 0.17941462, w: 0.7226559)
    gameScene.rootNode.addChildNode(cameraNode)
}

The orientation of SCNVector4(x: -0.7600127, y: 0.62465125, z: 0.17941462, w: 0.7226559) in degrees is x: -43.5, y: 35.8, z: 10.3, and I don't understand w. (Also, why isn't z = 0? I thought z was the roll...?)

Here is my workings out for recreating what I thought the Y-angle should be:

enter image description here

So I worked it out to be 63.4 degrees, but the returned rotation shows that it should be 35.8 degrees. Is there something wrong with my calculations, do I not fully understand SCNVector4, or is there another method to do this?

I looked at Explaining in Detail the ScnVector4 method for what SCNVector4 is, but I still don't really understand what w is for. It says that w is the 'angle of rotation' which I thought was what I thought X, Y & Z were for.


If you have any questions, please ask!

George
  • 25,988
  • 10
  • 79
  • 133

2 Answers2

2

Although @rickster has given the explanations of the properties of the node, I have figured out a method to rotate the node to look at a point using maths (trigonometry).

Here is my code:

// Extension for Float
extension Float {
    /// Convert degrees to radians
    func asRadians() -> Float {
        return self * Float.pi / 180
    }
}

and also:

// Extension for SCNNode
extension SCNNode {
    /// Look at a SCNVector3 point
    func lookAt(_ point: SCNVector3) {
        // Find change in positions
        let changeX = self.position.x - point.x // Change in X position
        let changeY = self.position.y - point.y // Change in Y position
        let changeZ = self.position.z - point.z // Change in Z position

        // Calculate the X and Y angles
        let angleX = atan2(changeZ, changeY) * (changeZ > 0 ? -1 : 1)
        let angleY = atan2(changeZ, changeX)

        // Calculate the X and Y rotations
        let xRot = Float(-90).asRadians() - angleX // X rotation
        let yRot = Float(90).asRadians() - angleY // Y rotation
        self.eulerAngles = SCNVector3(CGFloat(xRot), CGFloat(yRot), 0) // Rotate
    }
}

And you call the function using:

cameraNode.lookAt(SCNVector3(0, 5, 0))

Hope this helps people in the future!

George
  • 25,988
  • 10
  • 79
  • 133
  • Yeah, that works. Note too that `SCNAction.rotateTo` is equivalent to setting `eulerAngles` directly if the animation duration is zero. – rickster Oct 05 '18 at 22:59
1

There are three ways to express a 3D rotation in SceneKit:

  1. What you're doing on paper is calculating separate angles around the x, y, and z axes. These are called Euler angles, or pitch, yaw, and roll. You might get results that more resemble your hand-calculations if you use eulerAngles or simdEulerAngles instead of `rotation. (Or you might not, because one of the difficulties of an Euler-angle system is that you have to apply each of those three rotations in the correct order.)

  2. simdRotation or rotation uses a four-component vector (float4 or SCNVector4) to express an axis-angle representation of the rotation. This relies on a bit of math that isn't obvious for many newcomers to 3D graphics: the result of any sequence of rotations around different axes can be minimally expressed as a single rotation around a new axis.

    For example, a rotation of π/2 radians (90°) around the z-axis (0,0,1) followed by a rotation of π/2 around the y-axis (0,1,0) has the same result as a rotation of 2π/3 around the axis (-1/√3, 1/√3, 1/√3).

    This is where you're getting confused about the x, y, z, and w components of a SceneKit rotation vector — the first three components are lengths, expressing a 3D vector, and the fourth is a rotation in radians around that vector.

  3. Quaternions are another way to express 3D rotation (and one that's even further off the beaten path for those of us with the formal math education common to undergraduate computer science curricula, but not crazy advanced, either). These have lots of great features for 3D graphics, like being easy to compose and interpolate between. In SceneKit, the simdOrientation or orientation property lets you work with a node's rotation as a quaternion.

    Explaining how quaternions work is too much for one SO answer, but the practical upshot is this: if you're working with a good vector math library (like the SIMD library built into iOS 9 and later), you can basically treat them as opaque — just convert from whichever other rotation representation is easiest for you, and reap the benefits.

rickster
  • 124,678
  • 26
  • 272
  • 326