旋转对象,使点击的点面向摄像机

时间:2015-01-26 17:06:55

标签: swift scenekit

我很难解释这些情况所以请耐心等待。

image of a globe

我想要的是当用户点击白色注释时,该点将滚动到中心(与地球一起)

我还希望能够以编程方式执行此操作,滚动到我为地球提供x / y坐标时的某个点

我正在使用以下函数来计算基于x / y坐标的SCNVector3

func positionForCoordinates(coordinates: CGPoint, radius: CGFloat) -> SCNVector3  {
    let s = coordinates.x
    let t = coordinates.y
    let r = radius

    let x = r * cos(s) * sin(t)
    let y = r * sin(s) * sin(t)
    let z = r * cos(t)

    return SCNVector3(x: Float(x), y: Float(y), z: Float(z))
}

它的数学实际上是在逃避我。

1 个答案:

答案 0 :(得分:2)

让我们根据您的问题假设,我们知道以下

  • 您将其放置在地球上的注释的坐标(纬度/经度),并且您希望模拟将地球模型旋转到其中心
  • 相机正对着屏幕中央
  • 我们将使用相机围绕地球模型旋转,而不是旋转地球本身

我们可以利用SCNNodeSCNCamera的动画属性,这样我们就可以更轻松地制作动画,而不是手动渲染渲染循环中的值。

首先 - 设置相机

func setupCamera(scene: SCNScene) {
  cameraOrbit = SCNNode()
  cameraNode = SCNNode()
  camera = SCNCamera()

  // camera stuff
  camera.usesOrthographicProjection = true
  camera.orthographicScale = 10
  camera.zNear = 1
  camera.zFar = 100

  // initially position is far away as we will animate moving into the globe
  cameraNode.position = SCNVector3(x: 0, y: 0, z: 70)
  cameraNode.camera = camera
  cameraOrbit = SCNNode()
  cameraOrbit.addChildNode(cameraNode)
  scene.rootNode.addChildNode(cameraOrbit)
}

Camera节点通过SCNCamera属性设置为camera实例,并被包装在另一个节点内,因为我们将用它来操纵它的旋转。

为了简洁,我留下了用于在类中定义这些变量的代码。

第二 - 实施转换方法

我们需要一种方法,将地图坐标(纬度/经度)转换为旋转角度,以便我们将其插入SCNNode.eulerAngles,以便围绕地球模型旋转我们的相机。

/**
  Get rotation angle of sphere along x and y direction from input map coordinate to show such location at the center of view.

  - Parameter from: Map coordinate to get rotation angle for sphere

  - Returns: Tuple of rotation angle in form (x:, y:)
*/
func rotationXY(from coordinate: CLLocationCoordinate2D) -> (x: Double, y: Double) {
    // convert map coordiante to texture coordinate
    let v = 0.5 - (coordinate.latitude / 180.0)
    let u = (coordinate.longitude / 360.0) + 0.5

    // convert texture coordinate to rotation angles
    let angleX = (u-0.5) * 2 * Double.pi
    let angleY = (0.5-v) * -Double.pi

    return (x: angleX, y: angleY)
}

从代码中,我们需要将地图坐标转换为纹理坐标,其中放置纹理即。将纹理漫射到球体上以正常渲染而不修改球体节点的旋转角度,并且相机的位置沿z轴放置(即x = 0,y = 0,z = N),此类纹理的中心将是显示在相机上。因此,在等式中,我们考虑0.5来适应这一点。

之后我们将结果转换为角度(弧度)。沿着x方向,我们可以将球体旋转360度以完全环绕它。对于y方向,需要180度环绕它。

  

请注意,我没有删除0.5这两种情况,因为它在每次转换中都是按原样进行,并且更清楚地看到解释。您可以通过删除代码来简化它。

第三 - 动画

另外,我还会包括缩放级别。

假设您允许放大0.0到10.0的级别。我们使用orthographicScale SCNNode属性来模拟我们上面设置的正交类型相机的放大。相反,orthographicScale与正常理解中的缩放级别相反。因此,在缩放级别10.0时,orthographicScale将为0.0,以实现放大效果。

/**
  Rotate camera around globe to specified coordinate.

  - Parameter to: Location in coordinate (latitude/longitude)
  - Parameter zoomLevel: Zoom level in range 0.0 - 10.0.
  - Parameter duration: Duration for rotation
  - Parameter completion: Delegate when rotation completes to notify back to user
*/
func flyGlobeTo(to location: CLLocationCoordinate2D, zoomLevel: Double, duration: Double, completion: (()->Void)?=nil) {
    // make a call to our conversion method
    let rotation = self.rotationXY(from: location)

    SCNTransaction.begin()
    SCNTransaction.animationDuration = duration
    SCNTransaction.completionBlock = {
        completion?()
    }
    self.cameraOrbit.eulerAngles.x = Float(rotation.y)
    self.cameraOrbit.eulerAngles.y = Float(rotation.x)
    self.camera.orthographicScale = 10.0 - zoomLevel    // calculate value from inverse from orthographicScale
    SCNTransaction.commit()
}

就是这样。每当您需要飞到地图上的目标坐标时,只需使用flyGlobeTo()方法。确保在主线程中调用它。动画是通过SCNTransaction完成的,而不是通过UIView.animate完成的,显然它是不同的技术,但我在这里也注意到它是我第一次使用后者。