Picking a LineSegs

You sound slightly annoyed with my question, and I apologize if I haven’t worked with the underlying work done by collisions before. Thanks to your tip, though, I read up on collision masks. After printing out the mask the CollisionNodes had, It was obvious why the CollisionRay wasn’t noticing them:

>>> print CollisionNode('cNode').getIntoCollideMask()
 0000 0000 0000 1111 1111 1111 1111 1111
>>> print GeomNode.getDefaultCollideMask()
 0000 0000 0001 0000 0000 0000 0000 0000

At this point I had a choice: set the bitmask of my CollisionNode or don’t set them. I went with setting them to a custom bitmask with just bit 12 (completely arbitrary btw) on, so that if I need to distinguish between types of collisions later on, it would be easier.

So now my code that creates the CollisionNode and then the CollisionWire is as follows:

# draw the wire (assumes code from previous posts in this thread)
lineSegs = self._draw_line_segs(parent, (here, there))
lineSegs.setTag('pickable', 'true')

# add a tube around the wire so it can be picked
hx, hy, hz = here.getX(), here.getY(), here.getZ()
tx, ty, tz = there.getX(), there.getY(), there.getZ()
tubeRadius = 0.6
tube = CollisionTube(hx, hy, hz, tx, ty, tz, tubeRadius)
cNode = CollisionNode('wireCollisionTube')
cNode.setIntoCollideMask(BitMask32.bit(12))
tubeNp = parent.attachNewNode(cNode)
tubeNp.node().addSolid(tube)
tubeNp.show()  # optional, just for debugging

Note the new line that sets the “into” collision mask. Oh by the way, at first, led astray by the “self.pickerNode.setFromCollideMask” line in the Picker class, I tried setting the “from” collision mask on my CollisionNode. That doesn’t work, but does as soon as the “into” mask is used instead.

Okay, the the Picker class remains largely the same, but for posterity here it is anyhow. I can’t bold-face the changes inside a code tag, but I’ve added comments at the appropriate places.

class Picker(object):

    def __init__(self):
        # CHANGED: combine all required masks
        mask = GeomNode.getDefaultCollideMask()
        mask |= BitMask32.bit(12)

        self.queue = CollisionHandlerQueue()
        self.pickerNode = CollisionNode('MouseRay')
        self.pickerNP = base.camera.attachNewNode(self.pickerNode)
        # CHANGED: set "from" collision mask to the new combined version
        self.pickerNode.setFromCollideMask(mask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.traverser = CollisionTraverser()
        self.traverser.addCollider(self.pickerNP, self.queue)

    def __call__(self, *args, **kwargs):
        mpos = kwargs.get('mpos', None if not args else args[0])
        return self.get_object_hit(mpos)

    def get_object_hit(self, mpos=None):
        if mpos is None:
            if not base.mouseWatcherNode.hasMouse():
                return None
            mpos = base.mouseWatcherNode.getMouse()

        self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
        self.traverser.traverse(render)

        if not self.queue.getNumEntries():
            return None

        self.queue.sortEntries()
        np = self.queue.getEntry(0).getIntoNodePath()
        while np != render:
            if np.getTag('pickable') == 'true' and not np.isHidden():
            return np
            np = np.getParent()
        return None

Useful links:

Thanks as usual, David!