SCNKit: Hit test doesn't hit node's dynamically modified geometry

Viewed 220

I'm facing an issue where SCNView.hitTest does not detect hits against geometry that I'm modifying dynamically on the cpu.

Here's the overview: I have a node that uses an SCNGeometry created from a MTLBuffer of vertices:

func createGeometry(vertexBuffer: MTLBuffer, vertexCount: Int) -> SCNGeometry {
    let geometry = SCNGeometry(sources: [
        SCNGeometrySource.init(
            buffer: vertexBuffer,
            vertexFormat: .float3,
            semantic: .vertex,
            vertexCount: vertexCount,
            dataOffset: 0,
            dataStride: MemoryLayout<SIMD3<Float>>.stride),
    ], elements: [
        SCNGeometryElement(indices: ..., primitiveType: .triangles)
    ])
}


let vertexBuffer: MTLBuffer = // shared buffer
let vertexCount = ...

let node = SCNNode(geometry: createGeometry(vertexBuffer: vertexBuffer, vertexCount: vertexCount))

As the app is running, I then dynamically modify the vertex buffer in the SceneKit update loop:

// In SceneKit update function

var ptr = vertexBuffer.contents().bindMemory(to: SIMD3<Float>.self, capacity: vertexCount)

for i in 0..<vertexCount {
    ptr[i] = // modify vertex
}

This dynamic geometry is correctly rendered by SceneKit. However when I then try hit testing against node using SCNView.hitTest, no hits are detected against the modified geometry.

I can work around this by re-creating the node's geometry after modifying the data:

// after updating data
node.geometry = createGeometry(vertexBuffer: vertexBuffer, vertexCount: vertexCount)

However this feels like a hack.

What is the proper way to have hit testing work reliably for a node with dynamically changing SCNGeometry?

2 Answers

I think there's no proper way to make hit-testing work reliably in your situation. It would apparently be possible if it didn't depend on the SceneKit/Metal render loop and delegate pattern. But since it entirely depends on them, this is an unrealistically expensive operation to recreate SCNGeometry's instances, as you said earlier. So, I totally agree with @HamidYusifli.

When you perform a hit-test search, SceneKit looks for SCNGeometry objects along the ray you specify. For each intersection between the ray and a geometry, SceneKit creates a hit-test result to provide information about both the SCNNode object containing the geometry and the location of the intersection on the geometry’s surface.

The problem in your case is that when you modify the buffer’s contents (MTLBuffer) at render time, SceneKit does not know about it, and therefore cannot update SCNGeometry object which is used for performing hit-test.

So the only way I can see to solve this issue is to recreate your SCNGeometry object.

Related