Using Scenekit for Hittesting Not Returning a Hit with Scnnode

Swift SCNNode subclass hittest always returns SCNNode *not* subclass

Just cast the object to your subclass:

// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])

for hit in hitResults {
if let hitnode = hit.node as? ExSCNNode {


}

SceneKit: Hit test not work with billboarded quads

You're retrieving the tap coordinates in annotationsDisplayView, but then passing those coordinates to sceneView for the hit test lookup. Unless those views are precisely on top of each other, you'll get a mismatch.

I think you want

    let location = gestureRecognize.location(in: sceneView)

instead.

I was curious whether the hit test would use the rotated version of the billboard. I confirmed that it does. Here's a working (Mac) sample.

In the SCNView subclass:

override func mouseDown(with theEvent: NSEvent) {
/* Called when a mouse click occurs */

// check what nodes are clicked
let p = self.convert(theEvent.locationInWindow, from: nil)
let hitResults = self.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
Swift.print("hit me")
}
for result in hitResults {
Swift.print(result.node.name)

// get its material
let material = result.node.geometry!.firstMaterial!

// highlight it
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5

// on completion - unhighlight
SCNTransaction.completionBlock = {
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5

material.emission.contents = NSColor.black

SCNTransaction.commit()
}

material.emission.contents = NSColor.yellow

SCNTransaction.commit()
}

super.mouseDown(with: theEvent)
}

And in the view controller:

override func awakeFromNib(){
super.awakeFromNib()

// create a new scene
let scene = SCNScene()

let side = CGFloat(2.0)
let quad1 = SCNNode.init(geometry: SCNPlane(width: side, height: side))
quad1.name = "green"
quad1.geometry?.firstMaterial?.diffuse.contents = NSColor.green

let quad2 = SCNNode.init(geometry: SCNPlane(width: side, height: side))
quad2.name = "red"
quad2.geometry?.firstMaterial?.diffuse.contents = NSColor.red

let quad3 = SCNNode.init(geometry: SCNPlane(width: side, height: side))
quad3.name = "blue"
quad3.geometry?.firstMaterial?.diffuse.contents = NSColor.blue

scene.rootNode.addChildNode(quad1)
scene.rootNode.addChildNode(quad2)
scene.rootNode.addChildNode(quad3)

let rotation = CGFloat(M_PI_2) * 0.9
quad1.position.y = side/2
quad1.eulerAngles.y = rotation

quad2.position.x = side/2
quad2.eulerAngles.x = rotation

// comment out these constraints to verify that they matter
quad1.constraints = [SCNBillboardConstraint()]
quad2.constraints = [SCNBillboardConstraint()]

// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)

// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)

// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = NSColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)

// set the scene to the view
self.gameView!.scene = scene

// allows the user to manipulate the camera
self.gameView!.allowsCameraControl = true

// show statistics such as fps and timing information
self.gameView!.showsStatistics = true

// configure the view
self.gameView!.backgroundColor = NSColor.black
}

Scenekit hitTestWithSegment only returns first hit (all of a sudden)

Apple decided to surprise everyone in IOS 11 by changing the default behavior of the hittest functions. Since IOS 11 you need to specfically tell the hittest to search for all nodes and not just return the first hit result:

hitTestOptions[SCNHitTestOption.searchMode] = SCNHitTestSearchMode.all.rawValue as NSNumber

This will set it back to returning an array of hitresults if multiple nodes were hit.

SceneKit hit test error while moving camera

I've started a new project and figured it out step by step when does the hittest go wrong. I didn't find it anywhere in the offical Apple documentation, but my experiences are the followings:
If you want to change the camera's position or any other property, you can do it by adding a new camera to a new node with new position, parameters, etc. then you set the SCNView's pointOfView property, you can do it animated like this:

SCNTransaction.begin()
SCNTransaction.setAnimationDuration(2.0)
sceneView.pointOfView = cameraNode
SCNTransaction.commit()

One important point here: the node that holding the new SCNCamera has to be added to the SCNScene's rootView, otherwise (if you add it to the rootView's childNode) the hittest will give you an error instead the SCNNode that you touched.

SKLabelNode Hit Test

I resolved my problem by setting the SCNHitTestOption.boundingBoxOnly option to true. This will conduct the hit test ignoring the node's geometry.

let hitTestResults = sceneView.hitTest(tapPoint, options: [.boundingBoxOnly: true])

Swift: can't detect hit test on SCNNode? See if vector3 is contained in a node?

When you're working with ARSCNView, there are two kinds of hit testing you can do, and they use entirely separate code paths.

  • Use the ARKit method hitTest(_:types:) if you want to hit test against the real world (or at least, against ARKit's estimate of where real-world features are). This returns ARHitTestResult objects, which tell you about real-world features like detected planes. In other words, use this method if you want to find a real object that anyone can see and touch without a device — like the table you're pointing your device at.
  • Use the SceneKit method hitTest(_:options:) if you want to hit test against SceneKit content; that is, to search for virtual 3D objects you've placed in the AR scene. This returns SCNHitTestResult objects, which tell you about things like nodes and geometry. Use this method if you want to find SceneKit nodes, the model (geometry) in a node, or the specific point on the geometry at a tap location.

In both cases the 3D position found by a hit test is the same, because ARSCNView makes sure that the virtual "world coordinates" space matches real-world space.

It looks like you're using the former but expecting the latter. When you do a SceneKit hit test, you get results if and only if there's a node under the hit test point — you don't need any kind of bounding box test because it's already being done for you.



Related Topics



Leave a reply



Submit