3

I need to detect when the virtual objects gets in contact with the real world object using ARKit.

Is there any way to find it out?

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Developer
  • 39
  • 2

1 Answers1

3

At first you need to create a collision category struct that conforms to OptionSet protocol and has properties with bitset types:

import ARKit

struct Category: OptionSet {

    let rawValue: Int
    
    static let sphereCategory = Category(rawValue: 1 << 0)
    static let targetCategory = Category(rawValue: 1 << 1)
}

Then set a physics delegate to SCNPhysicsContactDelegate protocol inside lifecycle method:

class ViewController: UIViewController, SCNPhysicsContactDelegate {

    @IBOutlet var sceneView: ARSCNView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        sceneView.scene = SCNScene()
        sceneView.scene.physicsWorld.contactDelegate = self

        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.horizontal]
        sceneView.session.run(config)
    }
}

SCNPhysicsContactDelegate contains 3 optional physicsWorld() methods (we'll use 1st later):

public protocol SCNPhysicsContactDelegate: NSObjectProtocol {
        
    optional func physicsWorld(_ world: SCNPhysicsWorld, 
                      didBegin contact: SCNPhysicsContact)

    optional func physicsWorld(_ world: SCNPhysicsWorld, 
                     didUpdate contact: SCNPhysicsContact)

    optional func physicsWorld(_ world: SCNPhysicsWorld, 
                        didEnd contact: SCNPhysicsContact)
}

After this define categoryBitMask and collisionBitMask for sphere collider:

fileprivate func createSphere() -> SCNNode {

    var sphere = SCNNode()
    sphere.geometry = SCNSphere(radius: 0.1)

    sphere.physicsBody =  .init(type: .kinematic, 
                               shape: .init(geometry: sphere.geometry!, 
                                             options: nil))

    sphere.physicsBody?.isAffectedByGravity = true

    sphere.physicsBody?.categoryBitMask = Category.sphereCategory.rawValue
    sphere.physicsBody?.collisionBitMask = Category.targetCategory.rawValue
    sphere.physicsBody?.contactTestBitMask = Category.targetCategory.rawValue

    return sphere
}

...and define bit-masks in reversed order for real world detected plane:

fileprivate func visualizeDetectedPlane() -> SCNNode {

    var plane = SCNNode()
    plane.geometry = SCNPlane(width: 0.7, height: 0.7)

    plane.physicsBody =  .init(type: .kinematic, 
                              shape: .init(geometry: plane.geometry!, 
                                            options: nil))

    plane.physicsBody?.isAffectedByGravity = false

    plane.physicsBody?.categoryBitMask = Category.targetCategory.rawValue
    plane.physicsBody?.collisionBitMask = Category.sphereCategory.rawValue
    plane.physicsBody?.contactTestBitMask = Category.sphereCategory.rawValue

    return plane
}

And only when you've added your SCNPlanes to real-world detected planes and append an SCNSphere to your SCNScene, you could use physicsWorld(_:didBegin:) instance method to detect collisions:

func physicsWorld(_ world: SCNPhysicsWorld, 
         didBegin contact: SCNPhysicsContact) {

    if contact.nodeA.physicsBody?.categoryBitMask == 
                                          Category.targetCategory.rawValue |
       contact.nodeB.physicsBody?.categoryBitMask == 
                                          Category.targetCategory.rawValue {

        print("BOOM!")
    }
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • sorry couldn't understand what is real world detected plane – Developer Apr 27 '20 at 05:11
  • Is it possible to achieve the same thing in RealityKit? – Kawe Aug 19 '21 at 10:04
  • @KeyhanKamangar, look at this post – https://stackoverflow.com/questions/60191009/reality-composer-custom-collision-between-entities-of-different-scenes/60192226#60192226 – Andy Jazz Aug 19 '21 at 10:07
  • And also look at this one – https://stackoverflow.com/questions/67580940/realitykit-which-entity-is-intersecting-with-other-entity/67584426#67584426 – Andy Jazz Aug 19 '21 at 10:14
  • 1
    @AndyFedoroff Thank you very much. I checked both answers but I couldn't get the desired result. I'm loading an Entity (.usdz) and trying to add collisions so that it would collide with real-world objects like a wall or a sofa. – Kawe Aug 19 '21 at 11:39
  • If you need to use scene reconstructed real-world objects with a virtual geometry, just look at Cupertino's [sample](https://developer.apple.com/documentation/arkit/content_anchors/visualizing_and_interacting_with_a_reconstructed_scene) code about using LiDAR. – Andy Jazz Aug 19 '21 at 11:44
  • 1
    I checked that but it's now working and the model can pass through walls or other objects – Kawe Aug 19 '21 at 12:04
  • 1
    Don't understand: `working` or `not working`? – Andy Jazz Aug 19 '21 at 12:05
  • Sorry, "Not working". Do you want to move it to chat? – Kawe Aug 19 '21 at 12:06
  • Check Scene Reconstruction sample code – it's awesome, and that's what you need. – Andy Jazz Aug 19 '21 at 12:08
  • I've checked that but not working. Do you want to make it a question and post my code on SO? – Kawe Aug 19 '21 at 12:09
  • 1
    Of course, you can post this as a question, but I currently have no time to answer any question. I've got extremely busy schedule. – Andy Jazz Aug 19 '21 at 12:12