获取3D模型的高度及其变换后的屏幕坐标

时间:2018-03-21 16:49:00

标签: ios swift arkit

我正在使用ARKit呈现Collada(* .dae)文件。作为我ARSCNView的叠加层,我添加了SKScene,只显示了一个消息气泡(尚未显示文字)。

目前,我知道如何修改气泡的位置,使它看起来总是在我的3D模型的脚下。我这样做:

func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval)  {
    if let overlay = sceneView.overlaySKScene as? BubbleMessageScene {            
        guard let borisNode = sceneView.scene.rootNode.childNode(withName: "boris", recursively: true) else { return }

        let boxWorldCoordinates = sceneView.scene.rootNode.convertPosition(borisNode.position, from:sceneView.scene.rootNode.parent)
        let screenCoordinates = self.sceneView.projectPoint(boxWorldCoordinates)
        let boxY = overlay.size.height - CGFloat(screenCoordinates.y)

        overlay.bubbleNode?.position.x = CGFloat(screenCoordinates.x) - (overlay.bubbleNode?.size.width)!/2
        overlay.bubbleNode?.position.y = boxY
    }
}

然而,我的气泡始终位于3D模型的,因为我只能获取模型的SCNNode位置,并在其中锚定。我希望它成为我模特的头脑。

有没有办法可以获得3D模型的高度,然后是变换后的屏幕坐标,所以无论我在手机的哪个位置,看起来气泡信息总是在头部旁边?

enter image description here

2 个答案:

答案 0 :(得分:3)

每个SCNNode都有一个boundingBox属性:

  

对象边界框的最小和最大角点。

所以这意味着:

  

场景工具包使用两个标识其角点的点来定义局部坐标空间中的边界框,这两个点隐式确定标记其极限的六个轴对齐平面。例如,如果几何体的边界框具有最小角{-1,0,2}和最大角{3,4,5},则几何体顶点数据中的所有点都具有介于-1.0和3.0之间的x坐标值,包容性。

如果您查看SceneKit编辑器,您还可以以米为单位查看模型的大小(我只是说这是您可以参考的一个点,以便检查计算):

enter image description here

在我的例子中,我使用的是上面大小的Pokemon模型。

我缩放了模型(你可能也做了),例如:

 pokemonModel.scale = SCNVector3(0.01, 0.01, 0.01)

因此,为了获得boundingBox的{​​{1}},我们可以这样做:

SCNNode

这样称呼它:

/// Returns The Original Width & Height Of An SCNNode
///
/// - Parameter node: SCNNode
func getSizeOfModel(_ node: SCNNode){

     //1. Get The Size Of The Node Without Scale
     let (minVec, maxVec) = node.boundingBox
     let unScaledHeight = maxVec.y - minVec.y
     let unScaledWidth = maxVec.x - minVec.x

     print("""
         UnScaled Height = \(unScaledHeight)
         UnScaled Width = \(unScaledWidth)
           """)
    }

现在当然,因为我们的SCNNode已被缩放,所以这没有多大帮助,所以显然我们需要通过重写函数来考虑这一点:

 getSizeOfModel(pokemonModel)

将会这样称呼:

/// Returns The Original & Scaled With & Height On An SCNNode
///
/// - Parameters:
///   - node: SCNode
///   - scalar: Float
func getOriginalAndScaledSizeOfNode(_ node: SCNNode, scalar: Float){

     //1. Get The Size Of The Node Without Scale
     let (minVec, maxVec) = node.boundingBox
     let unScaledHeight = maxVec.y - minVec.y
     let unScaledWidth = maxVec.x - minVec.x

     print("""
         UnScaled Height = \(unScaledHeight)
         UnScaled Width = \(unScaledWidth)
         """)


     //2. Get The Size Of The Node With Scale
     let max = node.boundingBox.max
     let maxScale = SCNVector3(max.x * scalar, max.y * scalar, max.z * scalar)

     let min = node.boundingBox.min
     let minScale = SCNVector3(min.x * scalar, min.y * scalar, min.z * scalar)

     let heightOfNodeScaled = maxScale.y - minScale.y
     let widthOfNodeScaled = maxScale.x - minScale.x

     print("""
         Scaled Height = \(heightOfNodeScaled)
         Scaled Width = \(widthOfNodeScaled)
         """)

}

完成此操作后,您说您想在模型上方放置一个“气泡”,然后可以这样做:

 getOriginalAndScaledSizeOfNode(pokemonModel, scalar: 0.01)

这会产生以下结果(我也在其他一些不幸的口袋妖怪上测试过):

enter image description here

您可能还想在计算中添加一些“填充”,以便节点比模型的顶部高一点,例如:

func getSizeOfNodeAndPositionBubble(_ node: SCNNode, scalar: Float){

     //1. Get The Size Of The Node Without Scale
     let (minVec, maxVec) = node.boundingBox
     let unScaledHeight = maxVec.y - minVec.y
     let unScaledWidth = maxVec.x - minVec.x

     print("""
         UnScaled Height = \(unScaledHeight)
         UnScaled Width = \(unScaledWidth)
         """)

     //2. Get The Size Of The Node With Scale
     let max = node.boundingBox.max
     let maxScale = SCNVector3(max.x * scalar, max.y * scalar, max.z * scalar)

     let min = node.boundingBox.min
     let minScale = SCNVector3(min.x * scalar, min.y * scalar, min.z * scalar)

     let heightOfNodeScaled = maxScale.y - minScale.y
     let widthOfNodeScaled = maxScale.x - minScale.x

     print("""
         Scaled Height = \(heightOfNodeScaled)
         Scaled Width = \(widthOfNodeScaled)
         """)

     //3. Create A Buubble
     let pointNodeHolder = SCNNode()
     let pointGeometry = SCNSphere(radius: 0.04)
     pointGeometry.firstMaterial?.diffuse.contents = UIColor.cyan
     pointNodeHolder.geometry = pointGeometry

     //4. Place The Bubble At The Origin Of The Model, At The Models Origin + It's Height & At The Z Position

     pointNodeHolder.position = SCNVector3(node.position.x, node.position.y + heightOfNodeScaled, node.position.z)
          self.augmentedRealityView.scene.rootNode.addChildNode(pointNodeHolder)

}

我在数学方面并不擅长,而且它使用SCNNode作为气泡而不是 pointNodeHolder.position = SCNVector3(node.position.x, node.position.y + heightOfNodeScaled + 0.1, node.position.z) ,但希望它会指向正确的方向......

答案 1 :(得分:0)

你可以得到borisNode.boundingBox:(float3,float3)来计算节点的大小,得到2个元组的元组,然后通过从另一个点减去y来计算高度。最后按照你得到的数字移动叠加层的Y位置。