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!