根据地球坐标快速修改节点旋转

问题描述 投票:0回答:1

我有一个用 swift 和 SceneKit 构建的 3d 地球仪,为了表示用户当前位置,我使用了一个圆环(甜甜圈形状的对象)。用户位置可以是世界上的任何地方,并且简单地将节点添加到地球并不会使其齐平(与地球对齐)。我必须修改节点 eulerAngles 才能使其与地球持平。在给定任何独特位置的情况下,我的功能无法将形状与地球正确对齐。我希望该函数根据任意一组纬度和经度来定位圆环齐平。

    let dotNode = SCNNode(geometry: lowerCircle)
 
    dotNode.eulerAngles = getRotation(lat: viewModel.currentLocation?.lat ?? 0.0, long: viewModel.currentLocation?.long ?? 0.0)

    func getRotation(lat: Double, long: Double) -> SCNVector3 {
        let latRad = lat * .pi / 180.0
        let lonRad = long * .pi / 180.0

        let rotationX = Float(-latRad)
        let rotationY = Float(lonRad)
        let rotationZ = Float(lonRad)

        return SCNVector3Make(rotationX, rotationY, rotationZ)
        
        //example
        //slightly correct rotation for Saudi Arabia
        //SCNVector3Make(.pi / 3.75, 0, -.pi / 4)
    }
ios swift vector scenekit
1个回答
0
投票

A torus,在 3D 图形或建模的上下文中(例如在 Swift 中使用的 SceneKit 框架中,带有

SCNTorus
),是类似于甜甜圈或戒指的几何形状。

在您的例子中,环面用于表示地球上的特定位置,可能是用户的当前位置。

由于地球是一个球体,仅将环面放置在基于纬度和经度的点上是不够的。环面还需要定向,使其与地球表面齐平。这需要调整其旋转以与该特定点的地球曲率正确对齐。

环面是地球节点的子节点。

圆环是地球节点的子节点,应用于圆环的任何旋转或定位都是相对于地球节点的。这简化了将环面与特定地理位置的地球表面对齐的过程。您无需考虑整个场景的坐标系,只需相对于地球节点调整环面即可。

那就是:

func getTorusOrientation(lat: Double, long: Double) -> SCNVector4 {
    let latRad = lat * .pi / 180.0
    let lonRad = long * .pi / 180.0

    // Calculate position vector
    let x = cos(latRad) * cos(lonRad)
    let y = cos(latRad) * sin(lonRad)
    let z = sin(latRad)

    let positionVector = SCNVector3(x, y, z)

    // Updated up vector (assuming Y-axis is 'up' in the coordinate system)
    let upVector = SCNVector3(0, 1, 0)

    // Calculate cross-product
    let crossProduct = positionVector.cross(upVector)

    // Calculate angle for rotation
    let angle = acos(positionVector.dot(upVector))

    return SCNVector4(crossProduct.x, crossProduct.y, crossProduct.z, angle)
}

extension SCNVector3 {
    func cross(_ vector: SCNVector3) -> SCNVector3 {
        return SCNVector3(
            self.y * vector.z - self.z * vector.y,
            self.z * vector.x - self.x * vector.z,
            self.x * vector.y - self.y * vector.x
        )
    }

    func dot(_ vector: SCNVector3) -> Float {
        return (self.x * vector.x + self.y * vector.y + self.z * vector.z)
    }
}

// Usage
dotNode.orientation = getTorusOrientation(lat: viewModel.currentLocation?.lat ?? 0.0, 
                                          long: viewModel.currentLocation?.long ?? 0.0)

positionVector
使用纬度 (
lat
) 和经度 (
long
) 值计算。该矢量从地球中心(假定为该坐标系中的原点)指向地球表面上环面应放置的位置。

upVector
设置为
(0, 1, 0)
,这是在许多 3D 坐标系中定义“向上”方向的标准方法。

计算

crossProduct

positionVector
upVector
:它给出了我们需要围绕其旋转环面的轴。该轴垂直于环面的位置向量和初始向上向量,这正是正确方向所需的。

旋转的

angle

 使用 
positionVector
upVector
 之间的点积计算。该角度是圆环需要绕 
crossProduct
 轴旋转才能与指定地理位置的地球表面正确对齐的量。

SCNVector4 轴创建的

crossProduct
angle
 然后用于设置环面的方向。这确保环面不仅位于正确的纬度和经度,而且方向正确,使其看起来与地球表面齐平。


请注意,SceneKit 中的

SCNTorus

 可以重用上面定义的 
getTorusOrientation()

// Create a torus let torus = SCNTorus(ringRadius: 1.0, pipeRadius: 0.1) let torusNode = SCNNode(geometry: torus) // Set the torus orientation (using the previously discussed getTorusOrientation function) torusNode.orientation = getTorusOrientation(lat: viewModel.currentLocation?.lat ?? 0.0, long: viewModel.currentLocation?.long ?? 0.0) // Add the torus node to the earth node earthNode.addChildNode(torusNode)
您将创建一个具有特定半径值的 

SCNTorus

 对象,然后将其包装在 
SCNNode
 中(即 
torusNode
)。该节点的方向是使用 
getTorusOrientation
 函数设置的,使其在地球上正确对齐。最后,将环面节点作为子节点添加到地球节点,以维护正确定位和方向所需的层次关系。

© www.soinside.com 2019 - 2024. All rights reserved.