Press ESC to close

Ground Detection with ARKit

Hello friends, in this article, we will talk about how we detect the ground with ARKit and how we add a model when we detect the ground. As you know, ARKit is used in many different ways and for many different purposes. One of these purposes is to detect the floor on which the user is walking. For this we can easily detect it with Swift ARKit and add a field here. You can find the articles we created with ARKit here.

In the sample project we will do, when we detect the ground, we will put a picture of lava. Let’s put a SceneKit on our screen, as in the designs we made before, and then we can start writing code.

We need to give a configuration to the SceneView that we have created. While giving the configuration, we should set the horizontal part to the plane detection part. In this way, when Apple’s API finds a horizontal space, it will fall into the didAdd method. When it comes to this method, we add an image consisting of lava as a node.

There are also functions that show how to delete and update in the example below. You can find the latest version of the project here.

import UIKit
import ARKit

class ViewController: UIViewController {

    @IBOutlet weak var sceneView: ARSCNView!
    let configuration = ARWorldTrackingConfiguration()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setUI()
    }

    func setUI() {
        // MARK: sceneView
        sceneView.delegate = self
        sceneView.debugOptions = [.showWorldOrigin, .showFeaturePoints]
        configuration.planeDetection = .horizontal
        sceneView.session.run(configuration)
    }
    
    func createLava() -> SCNNode {
        let lavaNode = SCNNode(geometry: SCNPlane(width: 1, height: 1))
        lavaNode.geometry?.firstMaterial?.diffuse.contents =  UIImage(named: "lava")
        lavaNode.geometry?.firstMaterial?.isDoubleSided = true
        lavaNode.position = SCNVector3(0, 0, -1)
        lavaNode.eulerAngles = SCNVector3(90.degreesToRadians, 0, 0)
        return lavaNode
    }
    
    func createLava(planeAnchor: ARPlaneAnchor) -> SCNNode {
        let lavaNode = SCNNode(geometry: SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)))
        lavaNode.geometry?.firstMaterial?.diffuse.contents =  UIImage(named: "lava")
        lavaNode.geometry?.firstMaterial?.isDoubleSided = true
        lavaNode.position = SCNVector3(planeAnchor.center.x, planeAnchor.center.y, planeAnchor.center.z)
        lavaNode.eulerAngles = SCNVector3(90.degreesToRadians, 0, 0)
        return lavaNode
    }
}

extension ViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
        print("new flat surface detected, new arplaneanchor added")
        let lavaNode = createLava(planeAnchor: planeAnchor)
        node.addChildNode(lavaNode)
    }
    
    func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
        print("updating floor's anchor")
        node.enumerateChildNodes { childNode, _ in
            childNode.removeFromParentNode()
        }
        let lavaNode = createLava(planeAnchor: planeAnchor)
        node.addChildNode(lavaNode)
    }
    
    func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {
        guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
        node.enumerateChildNodes { childNode, _ in
            childNode.removeFromParentNode()
        }
    }
}

extension Int {
    var degreesToRadians: Double { return Double(self) * .pi / 180}
}

If you have questions, you can reach us by sending an e-mail or comment. Good work.

 

Leave a Reply

Your email address will not be published. Required fields are marked *