Search code examples
swiftxcodeaugmented-realityscenekitarkit

Create ARRaycastQuery from 3d object


I am developing an app using ARKit and SceneKit.

Is it possible to create raycast query using ARRaycastQuery from an object in the scene?

Scene

As per the picture above, I am trying to cast a ray from [3d object] and find a plane in that direction.

This is what I am trying:

    let location = objectNode.simdWorldPosition
    let startRayPoint = simd_float3(location.x, location.y, location.z + 2.0)
    let endRayPoint = simd_float3(location.x, location.y, location.z - 2.0)

    let query = ARRaycastQuery(origin: startRayPoint, direction: endRayPoint, allowing: .estimatedPlane, alignment: .any)
    timer =  Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
        if let result = self.sceneView.castRay(for: query).first {
            print("castRay result: \(result)")
        } else {
            print("failed to cast ray")
        }
    }

I tried to get the location of the [object] and start ray from 2m away and direction to 2m forward by manipulating the 'z' axis. However, the query always fails, what am I doing wrong? Is it even possible?


Solution

  • If you're using ARKit you can implement 3 different ray-casting methods:

    • raycast(_:)

    This instance method checks once for intersections between a ray (the ray you create from a screen point you're interested in) and real-world surfaces.

    • raycastQuery(from:allowing:alignment:)

    This instance method creates a raycast query that originates from a point on the view, aligned with the center of the camera's field of view.

    • trackedRaycast(_:updateHandler:)

    This instance method repeats a raycast query over time to notify you of updated surfaces in the physical environment.

    But as I see you need to implement a method that creates a ray from one 3d object that must hit another 3d object in a scene. In that case you need to implement a RealityKit's instance method that can be used inside ARView's scene:

    raycast(from:to:query:mask:relativeTo:)
    

    This method performs a convex ray cast against all the geometry in the scene for a ray between two end points.

    func raycast(from startPosition: SIMD3<Float>, 
                     to endPosition: SIMD3<Float>, 
                              query: CollisionCastQueryType = .all, 
                               mask: CollisionGroup = .all, 
         relativeTo referenceEntity: Entity? = nil) -> [CollisionCastHit]
    

    In real code it might look like this:

    import RealityKit
    
    let raycasts: [CollisionCastHit] = arView.scene.raycast(from: [2, 2, 2], 
                                                              to: [9, 9, 9], 
                                                           query: .all,  
                                                            mask: .all, 
                                                      relativeTo: nil)
    
    guard let rayCast: CollisionCastHit = raycasts.first
    else { return }
    
    print(rayCast.entity.name)
    print(rayCast.distance)
    

    But remember!

    The method ignores entities that lack a CollisionComponent. So, before using aforementioned method you need to assign collision shape for a future hit-model.