Raycasting if for real world objects (eg, walls, the ground IRL), and HitTesting is for virtual content eg, your 3d models.
// ViewController.swift
// RayVsHit
//
// Created by Dan Monaghan on 8/08/22.
//
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
enum tapType : Int {
case hitTest
case rayCast
}
@IBOutlet var sceneView: ARSCNView!
var tapGesture = UITapGestureRecognizer()
var tapCounter = 0
var gestureSwitcher = UISwitch()
var currentTapType = tapType.hitTest
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
let scene = SCNScene(named: "art.scnassets/ship.scn")
sceneView.scene = scene
tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
sceneView.gestureRecognizers?.append(tapGesture)
gestureSwitcher.frame = CGRect(x: 50, y: 50, width: 0, height: 0)
gestureSwitcher.addTarget(self, action: #selector(handleSwitch(_:)), for: .touchUpInside)
sceneView.addSubview(gestureSwitcher)
}
@objc func handleSwitch(_ sender: UISwitch){
if sender.isOn {
currentTapType = .hitTest
} else if !sender.isOn{
currentTapType = .rayCast
}
}
@objc func handleTap(_ sender: UITapGestureRecognizer){
let nodeToAdd = SCNNode()
nodeToAdd.name = "testingNode"
if currentTapType == .rayCast {
print("ray casting")
guard let query = sceneView.raycastQuery(from: sender.location(in: sceneView), allowing: .estimatedPlane , alignment: .any) else { return }
if let result = sceneView.session.raycast(query).first {
print("result = \(String(describing: result))")
nodeToAdd.transform = SCNMatrix4(result.worldTransform)
let boxGeo = SCNBox(width: 0.04, height: 0.04, length: 0.04, chamferRadius: 0)
boxGeo.firstMaterial?.diffuse.contents = UIColor.systemBlue
nodeToAdd.geometry = boxGeo
sceneView.scene.rootNode.addChildNode(nodeToAdd)
}
}
if currentTapType == .hitTest{
if let hitResults = sceneView.hitTest(sender.location(in: sceneView), options: [:]) as [SCNHitTestResult]? {
if !hitResults.isEmpty {
if let hitRes : SCNHitTestResult = hitResults.first {
if hitRes.node.name == "testingNode" {
return
}
print("results = \(hitRes)")
let point = hitRes.worldCoordinates
nodeToAdd.position = point
let sphereGeo = SCNSphere(radius: 0.04)
sphereGeo.firstMaterial?.diffuse.contents = UIColor.systemRed
nodeToAdd.geometry = sphereGeo
sceneView.scene.rootNode.addChildNode(nodeToAdd)
}
}
}
}
tapCounter += 1
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARWorldTrackingConfiguration()
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
}