Ray-picking while ignoring back-faces

There may be a ‘built-in’ way to ignore back-face intersections when ray-casting, but I couldn’t find it in the docs. Here’s how I did it:

# Initialize ray-cast collision so we can find the point of a mesh clicked on with the mouse
    def init_raycast(self):

        traverser = self.traverser = CollisionTraverser('mouse picker')
        handler =  self.coll_handler = CollisionHandlerQueue()

        # make a new collision node
        pickerNode = CollisionNode('mouseRay')
        # attach it to the camera
        pickerNP = camera.attachNewNode(pickerNode)
        # set it to collide with default (i.e. everything, currently)
        pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        # make a new collision ray
        pickerRay = self.picker_ray = CollisionRay()
        # add it to the collision node
        pickerNode.addSolid(pickerRay)
        # add the pickerNode (via its path) & the handler to the collision traverser
        traverser.addCollider(pickerNP, handler)


def get_mouse_pick(self) -> CollisionEntry:
        # Make sure the mouse is in the bounds of the panda3d window
        if not base.mouseWatcherNode.hasMouse():
            return None
        # Get the mouse screen coordinates.
        mpos = base.mouseWatcherNode.getMouse()

        # set the direction of the collision ray based on the camera's settings
        # and the clicked screen-space position
        base.picker_ray.setFromLens(base.camNode, mpos.getX(), mpos.getY())

        # kick off the collision detection
        base.traverser.traverse(base.render)
        handler = base.coll_handler

        num_entries = handler.getNumEntries()
        # if there were any collision entries...
        if num_entries > 0:
            # sort the entries in distance order (closest first)
            handler.sort_entries()
            # find the closest collision that isn't on a back-face
            index = -1                                   
            dot = 1
            while dot > 0 and index < num_entries: 
                index+=1               
                closest = handler.get_entry(index)
                # get the surface normal of the collision, relative to the camera
                surface_norm = closest.get_surface_normal(base.camera)
                dot = surface_norm.dot(base.picker_ray.direction)        
            return closest
        return None

I’m curious if there’s a better way to do this. Let me know!

2 Likes

Hi Dustin,

There is no built-in way to back-face cull collision intersections, and that is a perfectly fine way of implementing it in application code. I had to do the exact same thing in one of my projects.

Brian