如何使用 SceneKit 实现 3D 指南针
How to implement a 3D compass with SceneKit
目前我对制作 3D 罗盘的功能感到困惑。使用 SceneKit,我加载了一个箭头对象,然后像下面的代码一样获取传感器数据(效果是this video)
let motionManager = CMMotionManager()
motionManager.deviceMotionUpdateInterval = 1.0/60.0
if motionManager.isDeviceMotionAvailable {
motionManager.startDeviceMotionUpdates(to: OperationQueue.main, withHandler: { (devMotion, error) -> Void in
arrow.orientation = SCNQuaternion(-CGFloat((motionManager.deviceMotion?.attitude.quaternion.x)!), -CGFloat((motionManager.deviceMotion?.attitude.quaternion.y)!), -CGFloat((motionManager.deviceMotion?.attitude.quaternion.z)!), CGFloat((motionManager.deviceMotion?.attitude.quaternion.w)!))
})}
完成此操作后,如果用户在任何轴上旋转设备,箭头也可以旋转。由于这是一个 3D 指南针,我需要箭头来指明方向。于是我通过CLLocationManager去取航向数据,然后问题就来了
let gq1: GLKQuaternion = GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(heading), 0, 0, 1)
arrow.orientation = SCNVector4(gq1.x,gq1.y,gq1.z,gq1.w)
上面的代码在 2D 中表现完美,如下图所示,它正确指向东方。但是我想要它在 3D 环境中,所以我在下面做了
motionManager.deviceMotionUpdateInterval = 1.0/60.0
if motionManager.isDeviceMotionAvailable {
motionManager.startDeviceMotionUpdates(to: OperationQueue.main, withHandler: { (devMotion, error) -> Void in
arrow.orientation = orient(q: (motionManager.deviceMotion?.attitude.quaternion)!, angle: heading)
}
func orient(q:CMQuaternion,angle:Float) -> SCNQuaternion{
GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(-angle), 0, 0, 1)
let gq1: GLKQuaternion = GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(-angle), 0, 0, 1)
// add a rotation in z axis
let gq2: GLKQuaternion = GLKQuaternionMake(Float(q.x), Float(q.y), Float(q.z), Float(q.w))
// the current orientation
let qp: GLKQuaternion = GLKQuaternionMultiply(gq1, gq2)
// get the "new" orientation
var rq = CMQuaternion()
rq.x = Double(qp.x)
rq.y = Double(qp.y)
rq.z = Double(qp.z)
rq.w = Double(qp.w)
return SCNVector4(-Float(rq.x), -Float(rq.y), -Float(rq.z), Float(rq.w))
}
存在错误(this video 显示),如果我围绕 z 轴旋转设备,箭头仍然围绕 y 轴旋转。我做错了什么吗?
如果我用这种方法绕z轴旋转设备,方向数据会连续出来。结果,我无法使用 CLLocationManager 来实现我的目标。这个thread就是我想要的
目前我对制作 3D 罗盘的功能感到困惑。使用 SceneKit,我加载了一个箭头对象,然后像下面的代码一样获取传感器数据(效果是this video)
let motionManager = CMMotionManager()
motionManager.deviceMotionUpdateInterval = 1.0/60.0
if motionManager.isDeviceMotionAvailable {
motionManager.startDeviceMotionUpdates(to: OperationQueue.main, withHandler: { (devMotion, error) -> Void in
arrow.orientation = SCNQuaternion(-CGFloat((motionManager.deviceMotion?.attitude.quaternion.x)!), -CGFloat((motionManager.deviceMotion?.attitude.quaternion.y)!), -CGFloat((motionManager.deviceMotion?.attitude.quaternion.z)!), CGFloat((motionManager.deviceMotion?.attitude.quaternion.w)!))
})}
完成此操作后,如果用户在任何轴上旋转设备,箭头也可以旋转。由于这是一个 3D 指南针,我需要箭头来指明方向。于是我通过CLLocationManager去取航向数据,然后问题就来了
let gq1: GLKQuaternion = GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(heading), 0, 0, 1)
arrow.orientation = SCNVector4(gq1.x,gq1.y,gq1.z,gq1.w)
上面的代码在 2D 中表现完美,如下图所示,它正确指向东方。但是我想要它在 3D 环境中,所以我在下面做了
motionManager.deviceMotionUpdateInterval = 1.0/60.0
if motionManager.isDeviceMotionAvailable {
motionManager.startDeviceMotionUpdates(to: OperationQueue.main, withHandler: { (devMotion, error) -> Void in
arrow.orientation = orient(q: (motionManager.deviceMotion?.attitude.quaternion)!, angle: heading)
}
func orient(q:CMQuaternion,angle:Float) -> SCNQuaternion{
GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(-angle), 0, 0, 1)
let gq1: GLKQuaternion = GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(-angle), 0, 0, 1)
// add a rotation in z axis
let gq2: GLKQuaternion = GLKQuaternionMake(Float(q.x), Float(q.y), Float(q.z), Float(q.w))
// the current orientation
let qp: GLKQuaternion = GLKQuaternionMultiply(gq1, gq2)
// get the "new" orientation
var rq = CMQuaternion()
rq.x = Double(qp.x)
rq.y = Double(qp.y)
rq.z = Double(qp.z)
rq.w = Double(qp.w)
return SCNVector4(-Float(rq.x), -Float(rq.y), -Float(rq.z), Float(rq.w))
}
存在错误(this video 显示),如果我围绕 z 轴旋转设备,箭头仍然围绕 y 轴旋转。我做错了什么吗?
如果我用这种方法绕z轴旋转设备,方向数据会连续出来。结果,我无法使用 CLLocationManager 来实现我的目标。这个thread就是我想要的