Mouse events on geom

I’m trying to turn a geom into a button without of the DirectGui classes. What do I use to trigger an even when the geom is clicked on. I found this in the DirectGuiWidget code:

        suppressFlags = 0
        if self['suppressMouse']:
            suppressFlags |= MouseWatcherRegion.SFMouseButton
            suppressFlags |= MouseWatcherRegion.SFMousePosition

Is this what I’m looking for?

So, do I understand correctly that what you have is the following: a model of some sort, parented under aspect2d, which you want to be clickable?

If so, do you require that the clicking conform exactly to the shape of the model, or would a rectangular region be enough? If the latter, you could perhaps add a MouseWatcherRegion to the standard MouseWatcher (see the “addRegion” method of its base-class, MouseWatcherBase), and then accept the relevant events for it.

(You could probably do this manually as well, by creating a task-method that gets the current mouse-position and tests it against a rectangle that you define–just note that mouse-positions are in render2d coordinates, not aspect2d!)

If you want clicks to conform to the shape of the geom, then that can be done–but I won’t get into that unless you indicate that it is what you want, as the above is simpler, I think!

I would like the clicks to conform to the geoms shape. I hope this isn’t too complex. Also would the same apply to models parented to render?

Well, if it’s parented to render then you can just use collision-rays and some collision-geometry. (I recommend resisting the temptation to have the ray collide with visible geometry, unless performance really isn’t a concern.) That should conform to the shape of the collision-geometry, at least, and should be fairly straightforward–the “CollisionRay” class even has a “setFromLens” method that allows you to align it to match a 2D position (such as the mouse-position) in the perspective of a given camera!

I honestly don’t know whether the same is feasible when the object is parented below aspect2d–it seems like it might well be, but I’m not confident in that. It might be worth trying nevertheless, of course!

If none of the above works, we can perhaps consider other approaches!

from direct.gui.DirectGui import *
import direct.directbase.DirectStart
from panda3d.core import *
from direct.task.Task import Task
from direct.showbase.DirectObject import DirectObject

class MyButton(DirectObject):
    def __init__(self,geom):
        for x in geom.findAllMatches('**/+GeomNode'):
            geomNode = x.node() # only one GeomNode here

        self.buttons = aspect2d.attachNewNode('buttons')
        geom.reparentTo(self.buttons)
        geomNode.setIntoCollideMask(BitMask32.bit(1)) #Do I use geomNode or geom?

        self.picker = CollisionTraverser()
        self.pq = CollisionHandlerQueue()
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(BitMask32.bit(1))
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.picker.addCollider(self.pickerNP, self.pq)

        self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
        self.accept("mouse1-up", self.toggle)

    def mouseTask(self,task):
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode,mpos.getX(),mpos.getY())
            self.picker.traverse(self.buttons)
            if self.pq.getNumEntries() > 0:
                print('mouseIsOnGeom')
        return Task.cont
    def toggle(self):
        print('clicked')

test = loader.loadModel('test.egg')
btn = MyButton(test)
run()

I’ve hacked something rough by copying Tut-Chessboard from the samples. It’s not picking when the mouse hovers over the geom for some reason and I don’t really know why. In line 14, do I use geomNode or geom for the CollideMask?

edit: I think it’s fairly easy to check if the mouse is touching 3d objects thanks to Tut-ChessBoard :smiley: only issue now is aspect2d

Regarding the following, just a caveat:

It looks like you’re having the ray collide with visible geometry here. As I noted above, this can introduce performance issues, as visible geometry isn’t optimised for collision-testing, I believe. If performance isn’t a major concern (such as if you’re only dealing with very simple geometry, and the rest of the project already runs at a good frame-rate), then this may well be fine; otherwise, you might want to consider using dedicated collision geometry.

As to aspect2d, I see that you’re using the default camera (e.g. when you access base.camNode), which I think is the camera used for the 3D scene, and not for things like render2d and aspect2d. Have you tried instead using base.camera2d or base.camera2dp? (I’m afraid that I don’t know what the difference between them is, offhand! ^^; )

I’ve edited a couple of lines and it works! Thank you :smiley:

self.pickerNP = base.camera2d.attachNewNode(self.pickerNode) #changed to camera2d
self.pickerRay.setFromLens(base.cam2d.node(),mpos.getX(),mpos.getY()) #changed to cam2d

Performance is important for my application so what do you suggest instead of checking with visible geometry? Couldn’t find anything in the manual

It seems that Collision is being checked against a box around the geom instead of the geom itself. I rendered the geom to base.render instead of aspect2d and same issue.
PS: I viewed the model in pview and there isn’t a box around it. I feel like this has something to do with this line maybe?

geomNode.setIntoCollideMask(BitMask32.bit(1))

Here is the .egg file I’m using : hex.egg (651 Bytes)

Ah, I’m glad that it does! :slight_smile:

Just provide collision geometry. This can be made up in code (see the manual entry on collision solids, and perhaps some of the surrounding pages for context), or, if you want a more form-fitting piece of geometry, if can be created as part of the model.

However, see my further answers below…

If I load that model in PView, all that I see is a square–it looks like there’s a texture that it expects to have.

If that texture is making the model appear non-square, then I imagine that the box that your ray is hitting is, in fact, the geometry, which is the square–applying a texture doesn’t change the underlying geometry, even if it’s transparent. In that case you’re just seeing through the transparent parts–but they’re still there.

I think that that line is making the visible geometry available for the ray to collide with–that is, that it causes the ray to collide with visible geometry, but only the specific visible geometry to which that mask is applied.

That said, if you have only one (or a handful) or such objects being picked, then colliding with them shouldn’t be too bad, I suspect–and nowhere near as bad as just making the ray collide with all visible geometry, I daresay.

Hmm… I do have a question, if I may: why do you want to eschew DirectGUI for this?

Ahhh yes that makes sense now, I thought the .egg file conformed to the texture shape but it seems not. My question now is how do I make the .egg file conform to the texture (I’ve been using egg-texture-cards to make the .egg file).
Okay I’ll have a read through the collisions section.

At first I thought the DirectGUI wasn’t making the bounding box the same as the image’s and thought it would be easier to make the Geom clickable. Now that you’ve identified the problem, the issue wasn’t with DirectButton after all!

That’s… trickier. You could perhaps try checking the pixel on which you’re clicking, I suppose…

Hum. But perhaps it might be a better idea to look at the problem from another direction: what are you actually trying to do with this?

Looking at the name of the egg-file, are you perhaps making a hexagonally-tiled map? If so, you could perhaps just model hexagonal geometry in a modelling program such as Blender, and then use that. This would provide geometry of the desired shape for clicking, and also allow you to include actual collision-geometry via logic-tags that YABEE recognises.

(Another option might be to just determine the relevant tile mathematically, rather than via collisions–but the collision method is likely simpler!)

Yup! If you have a lot of these objects, I don’t know whether DirectGUI or ray-picking would be better–but then, if this is a hex-grid map that you’re making, I might also be concerned about the number of nodes that you’d end up with in your scene-graph.

Yes the .egg file is a hexagon and there’s only a few of them, so a few hexagonal buttons. I’ll probably make a hexagonal .obj file and just use that. Thank you very much! You’ve been of much help :smiley:

Ah, fair enough–that sounds good then, I think! :slight_smile:

It’s my pleasure! I’m glad if I’ve been of service. :slight_smile:

1 Like