I have the following helper functions that works fine:
func rotationToEuler(rotation: SCNVector4) -> SCNVector3 {
let node = SCNNode()
node.rotation = rotation
return node.eulerAngles
}
func eulerToRotation(eulerAngles: SCNVector3) -> SCNVector4 {
let node = SCNNode()
node.eulerAngles = eulerAngles
return node.rotation
}
However, it creates the dummy node for conversion, which feels a bit "hacky" because we are not doing anything else with the node (e.g. adding it to the scene, etc).
I am wondering if there's a better way to do so?
You can use the Spatial API.
import Spatial
import simd
func rotationToEuler(rotation: SCNVector4) -> SCNVector3 {
SCNVector3(
Rotation3D(angle: .radians(Double(rotation.w)), axis: .init(x: rotation.x, y: rotation.y, z: rotation.z))
.eulerAngles(order: .xyz)
.angles
)
}
func eulerToRotation(eulerAngles: SCNVector3) -> SCNVector4 {
let rotation = Rotation3D(eulerAngles: .init(angles: [eulerAngles.x, eulerAngles.y, eulerAngles.z], order: .xyz))
return SCNVector4(
simd_double4(rotation.axis.vector, rotation.angle.radians)
)
}
Note that simd
isn't strictly necessary here. I just used it so that I don't have to convert between floats and doubles 4 times in eulerToRotation
.
For iOS 16:
func rotationToEuler(rotation: SCNVector4) -> SCNVector3 {
SCNVector3(
Rotation3D(angle: .init(radians: Double(rotation.w)), axis: .init(x: rotation.x, y: rotation.y, z: rotation.z))
.eulerAngles(order: .pitchYawRoll)
.angles
)
}
func eulerToRotation(eulerAngles: SCNVector3) -> SCNVector4 {
let rotation = Rotation3D(eulerAngles: .init(angles: [eulerAngles.x, eulerAngles.y, eulerAngles.z], order: .pitchYawRoll))
return SCNVector4(
simd_double4(rotation.axis.vector, rotation.angle.radians)
)
}