Mouse ray collision with multiple cameras/viewports

Hi all!

I came across this awesome tutorial about collisions: Panda3d Collisions made simple

I added a CollisionHandlerEvent like in step5/step6 of the tutorial.

self.colHandler = CollisionHandlerEvent()
id = randrange(1000)
self.pickerNode = CollisionNode('mouseray')
self.pickerNP = self.camera.attachNewNode(self.pickerNode)
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.pickerNode.setFromCollideMask(BitMask32.bit(0))
base.cTrav.addCollider(self.pickerNP, self.colHandler)

self.colHandler.addInPattern('%fn-into-%in')
self.colHandler.addOutPattern('%fn-out-%in')

self.accept('mouseray-into-collider' ,self.collideEventIn)
self.accept('mouseray-out-collider', self.collideEventOut)

###### collider code
self.collider = self.model.attachNewNode(CollisionNode('collider'))
self.collider.node().addSolid(CollisionSphere(0, 0, 0, 1))

######
def updateMouseRay(self):
        if self.mouseWatcher.hasMouse():
            mpos = self.mouseWatcher.getMouse()
            self.pickerRay.setFromLens(self.camera.node(), mpos.getX(), mpos.getY())
            self.pickerNode.addSolid(self.pickerRay)

It works well if I only use one camera in the scene. But my program supports multiple viewports into the same world (splitscreen). So basically, updateMouseRay is called for each step for each viewport but only if the mouse is within the viewport’s display region.
If I do not change the collision node’s name for each new viewport, it works for one part of the splitscreen but as one would expect, not for the other one (depending on which one you move the mouse first). As soon as the mouse comes across the other viewport it infinitely throws the following error and finally crashes. What does this error message tell me?

:mathutil(warning): BoundingLine::contains_geometric() called with BoundingLine

Assuming that it would surely be a problem to have multiple collision nodes with the same name, the next idea was to try to uniquely name those by adding an id in the end for each camera (e.g. self.pickerNode = CollisionNode(‘mouseray%d’%id) and accordingly for the accept methods). But this throws the following error as soon as I move the mouse into the window:

:collide(error): Invalid attempt to detect collision from CollisionRay into CollisionRay!

This means that a CollisionRay object attempted to test for an
intersection into a CollisionRay object.  This intersection
test has not yet been defined; it is possible the CollisionRay
object is not intended to be collidable.  Consider calling
set_into_collide_mask(0) on the CollisionRay object, or
set_from_collide_mask(0) on the CollisionRay object.

Does that mean that if I have multiple collision rays for the mouse, they interfere with each other? Is there a way to prevent that?

Does anyone have a hint how I could make this work?

I’m not sure that I have a full answer for you–I haven’t really worked much with multiple viewports that I recall–but I have a few individual answers, I think:

This actually isn’t a problem, I believe: I’ve done it myself, as I recall! (Indeed, it can be quite useful for event-purposes.)

In short, you would seem to have left your rays’ “into” collide-masks non-zero. As a result, the two rays, both of which are “active” colliders–i.e. not passive “into”-only objects–attempt to collide with each other. And such a collision isn’t supported, hence the error.

It should be possible to fix this simply by setting your rays’ “into” collide-masks to zero. Something like this:

self.pickerNode.setIntoCollideMask(0)

# Or, if you prefer, you can have the BitMask class generate
# a zeroed BitMask32 object via the static "allOff" method:
mask = BitMask32.allOff()
self.pickerNode.setIntoCollideMask(mask)

Hi Thaumaturge!

Thanks for your quick reply as always :slight_smile:

Setting the IntoCollideMask to 0 makes it work! Thanks also for the hint with the bitmask classes. I didn’t really get how those bitmasks work yet. I will read some more about it. I guess it becomes more important the more things you try to collide with each other :smiley:

1 Like

It’s my pleasure! :slight_smile:

That’s what I’ve generally found, I believe.

As to how bit-masks work, here’s a short-ish description, if you’re interested (and presuming that I recall correctly):

Bit-masks can be thought of as collections of “flags”. If two objects might potentially collide, they only do so if their relevant bit-masks have at least one “flag” set in common; otherwise, the collision doesn’t happen. More specifically, the collision happens if the “from” mask of the “from”-object has at least one “flag” set in common with with the “into” mask of the “into”-object.

These “flags” are, in fact, just the bits of the bit-mask: “setting a flag” here means “setting that bit to 1”.

So, if we start with a “zero” bit-mask:

...0000 0000 0000

And we call “setBit(5)”, we set the “flag”/bit at position 5:

...0000 0010 0000
          ^               
Position: 54 3210           

Now, if it’s tested against another mask, it will only match if the other mask also has the “flag”/bit at position five set.

If there were more bits set, only one of them need match, if I recall correctly.