测量水平平面ARKIT

时间:2018-03-26 09:12:34

标签: ios arkit

如何在放置物体之前使用ARKit Scenekit测量水平平面?我想在放置物品之前有一个房间模型。提前谢谢!

1 个答案:

答案 0 :(得分:0)

提前知道房间的大小很棘手......

测量任何检测到的平面的大小不是:

每次检测到水平或垂直曲面时(假设您已启用它们),将生成ARPlaneAnchor:

  

当您运行启用了planeDetection选项的全局跟踪AR会话时,会话会自动将锚点列表添加到每个平面ARKit使用后向摄像头检测到的ARPlaneAnchor对象。每个平面锚提供有关估计的表面位置和形状的信息。

这在以下委托回调中调用:

 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { }

因此你可以得到ARPlaneAnchor的宽度:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

        //1. Get The Current ARPlaneAnchor
        guard let anchor = anchor as? ARPlaneAnchor else { return }

        //2. Log The Initial Width & Height
        print("""
            Initial Width = \(anchor.extent.x)
            Initial Height = \(anchor.extent.z)
            """)

 }

这个初始解决方案存在一个问题,因为ARPlaneAnchor通过以下回调得到更新(例如它的大小更改):

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { }

因此,如果您想跟踪ARPlaneAnchor的更新大小,则需要考虑这一点。

让我们来看看如何做到这一点:

首先我们创建自己的SCNNode Subclass名为PlaneNode,即使在更新时也会返回平面大小。

请注意,您不需要创建子类来获得相同的结果,尽管我可以轻松地重用它:

    class PlaneNode: SCNNode {

    let DEFAULT_IMAGE: String = "defaultGrid"
    let NAME: String = "PlaneNode"
    var planeGeometry: SCNPlane
    var planeAnchor: ARPlaneAnchor

    var widthInfo: String!
    var heightInfo: String!
    var alignmentInfo: String!

    //---------------
    //MARK: LifeCycle
    //---------------

    /// Inititialization
    ///
    /// - Parameters:
    ///   - anchor: ARPlaneAnchor
    ///   - node: SCNNode
    ///   - node: Bool
    init(anchor: ARPlaneAnchor, node: SCNNode, image: Bool, identifier: Int, opacity: CGFloat = 0.25){

        //1. Create The SCNPlaneGeometry
        self.planeAnchor = anchor
        self.planeGeometry = SCNPlane(width: CGFloat(anchor.extent.x), height: CGFloat(anchor.extent.z))
        let planeNode = SCNNode(geometry: planeGeometry)

        super.init()

        //2. If The Image Bool Is True We Use The Default Image From The Assets Bundle
        let planeMaterial = SCNMaterial()

        if image{

            planeMaterial.diffuse.contents = UIImage(named: DEFAULT_IMAGE)

        }else{

            planeMaterial.diffuse.contents = UIColor.cyan
        }

        //3. Set The Geometries Contents
        self.planeGeometry.materials = [planeMaterial]

        //4. Set The Position Of The PlaneNode
        planeNode.simdPosition = float3(self.planeAnchor.center.x, 0, self.planeAnchor.center.z)

        //5. Rotate It On It's XAxis
        planeNode.eulerAngles.x = -.pi / 2

        //6. Set The Opacity Of The Node
        planeNode.opacity = opacity

        //7. Add The PlaneNode
        node.addChildNode(planeNode)

        //8. Set The Nodes ID
        node.name = "\(NAME) \(identifier)"

    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }


    /// Updates The Size Of The Plane As & When The ARPlaneAnchor Has Been Updated
    ///
    /// - Parameter anchor: ARPlaneAnchor
    func update(_ anchor: ARPlaneAnchor) {

        self.planeAnchor = anchor

        self.planeGeometry.width = CGFloat(anchor.extent.x)
        self.planeGeometry.height = CGFloat(anchor.extent.z)

        self.position = SCNVector3Make(anchor.center.x, 0.01, anchor.center.z)

        returnPlaneInfo()
    }

    //-----------------------
    //MARK: Plane Information
    //-----------------------

    /// Returns The Size Of The ARPlaneAnchor & Its Alignment
    func returnPlaneInfo(){

        let widthOfPlane = self.planeAnchor.extent.x
        let heightOfPlane = self.planeAnchor.extent.z

        var planeAlignment: String!

        switch planeAnchor.alignment {

        case .horizontal:
            planeAlignment = "Horizontal"
        case .vertical:
            planeAlignment = "Vertical"
        }

        #if DEBUG
        print("""
            Width Of Plane =  \(String(format: "%.2fm", widthOfPlane))
            Height Of Plane =  \(String(format: "%.2fm", heightOfPlane))
            Plane Alignment = \(planeAlignment)
            """)
        #endif

        self.widthInfo = String(format: "%.2fm", widthOfPlane)
        self.heightInfo = String(format: "%.2fm", heightOfPlane)
        self.alignmentInfo = planeAlignment
      }

    }

创建了我们的子类后,我们需要在ViewController中使用它。您通常会获得多个ARPlaneAnchor,但在此示例中我们假设会有一个。{/ p>

所以我们将创建一个引用我们的PlaneNode的变量:

var planeNode:PlaneNode?

然后在ARSCNViewDelegate中我们将创建我们的PlaneNode:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    //1. Get The Current ARPlaneAnchor
    guard let anchor = anchor as? ARPlaneAnchor else { return }

    //2. Create The PlaneNode
    if planeNode == nil{
        planeNode = PlaneNode(anchor: anchor, node: node, image: true, identifier: 0, opacity: 1)
        node.addChildNode(planeNode!)
        planeNode?.name = String("Detected Plane")
    }

}

然后您需要做的就是跟踪PlaneNode的更新,例如:

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {

        guard let anchor = anchor as? ARPlaneAnchor, let existingPlane = planeNode else { return }
        existingPlane.update(anchor)
}

如果一切按计划进行,您应该在consoleLog:

中看到类似的内容

平面宽度= 0.07米

平面高度= 0.15米

平面对齐=可选(“水平”)

希望这足以让你开始......