NFC.cool
NFC.cool

Reputation: 3303

SCNQuaternion Multiplication

I'm trying to perform a SCNQuaternion Multiplication in SceneKit with Swift. The code below is in F# (Xamarin iOS Development). I'm trying to translate that code into Swift.

I'm stuck at the line: let dqm = SCNQuaternion.Multiply... SceneKit doesn't have a member called multiply.

Any idea how I can do this?

    let rr = CMAttitudeReferenceFrame.XArbitraryCorrectedZVertical
    this.motion.DeviceMotionUpdateInterval <- float (1.0f / 30.0f)
    this.motion.StartDeviceMotionUpdates (rr,NSOperationQueue.CurrentQueue, (fun d e ->
        let a = this.motion.DeviceMotion.Attitude
        let q = a.Quaternion
        let dq = new SCNQuaternion(float32 q.x, float32 q.y, float32 q.z, float32 q.w)
        let dqm = SCNQuaternion.Multiply (dq, SCNQuaternion.FromAxisAngle(SCNVector3.UnitZ, piover2))
        camNode.Orientation <- dqm
        ()
    ))

Upvotes: 4

Views: 4739

Answers (1)

rickster
rickster

Reputation: 126157

SceneKit doesn't include a library for doing quaternion math. SCNQuaternion is just a type alias for SCNVector4 — in other words, it's a place to hold the four coefficients of a quaternion, but you're left to do your own math to interpret or work with them.

In Xcode 9 / iOS 11 / macOS 10.13 High Sierra / etc, there's a better way to deal with quaternions (and other vector/matrix math) in SceneKit: the SIMD library includes two quaternion types (for double and float), and SceneKit duplicates all the SCNVector/SCNMatrix/SCNQuaternion accessors to provide SIMD versions. There's lots of great reasons to use SIMD types instead of the SCN ones:

  • SIMD types are used in multiple iOS/macOS/etc frameworks, so you can use them to pass vector/matrix/quaternion values between (e.g.) SceneKit, ARKit, and Model I/O without casting or converting.
  • The Metal shader language uses the same library for its vector/matrix types, so your math code is easily portable between CPU and GPU.
  • In Swift, SIMD types appear as structs with initializers, properties and methods, so most(*) of your code can be Swift-friendly.
  • In Swift and C++, operator overloading covers all the basic operations, so your math code and read and write like math.
  • All of the above is implemented using compiler intrinsics, so they use the native vector instruction set of the CPU your code is running on (x86 SSE, ARM NEON) to go fast.

Unfortunately, CoreMotion hasn't (yet?) adopted the SIMD library for its vector/matrix/quaternion types, so you'll need to convert your input CMQuaternion to simd_quatf before working with SIMD functions or SceneKit SIMD-based API. If you find yourself doing that often, you might want an extension for it:

extension simd_quatf {
    init(_ cmq: CMQuaternion) {
        self.init(ix: Float(cmq.x), iy: Float(cmq.y), iz: Float(cmq.z), r: Float(cmq.w))
    }
}

Then (assuming I'm reading your F# right), your code becomes something like this in Swift:

let zAxis = float3(x: 0, y: 0, z: 1)
let rotateAroundZ = simd_quatf(angle: .pi/2, axis: zAxis)
manager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: queue) { motion, error in
    guard let motion = motion else { /* handle error and */ return }

    let deviceQuaternion = simd_quatf(motion.attitude.quaternion)
    cameraNode.simdOrientation = deviceQuaternion * rotateAroundZ
}

(*) Some of the useful SIMD operations are still C-style global functions: Check simd/quaternion.h for stuff like slerp and Bézier interpolation.


For earlier Xcode / SDK releases...

  • Prior to Xcode 9 /iOS 11 / macOS 10.13 / etc, there's no quaternions in SIMD, and no SIMD—specific accessors in SceneKit. For everything but quaternions, it can still be useful to take the SCN types, convert them component-wise to the equivalent SIMD types, and do your math there.

  • Apple does ship a quaternion library in GLKit. Use GLKQuaternionMake to make a GLKQuaternion from the four components of your SCNQuaternion. Then you can multiply it, slerp it, whatever using GLKQuaternion functions. When done, use the components of the result to make another SCNQuaternion.

  • In Swift 1.x (Xcode 6.x), the SIMD library isn't available from Swift. GLKit math is a halfway decent substitute, but you're better off just using a newer Xcode — remember you can target all the way back to iOS 7 / macOS 10.9 Mavericks even with Swift 4 (and the SIMD library is available as far back as iOS 8 / macOS 10.10 Yosemite).

Upvotes: 10

Related Questions