# Nearest vertex (or polygon) of a ray collision

I need to determine the nearest vertex of a collision (from a mouse click ray collision) on a large geomnode with 20-25,000 vertices. (Ultimately, I need to identify the polygon face on which the mouse click occurred.)

This is for a mostly flat ‘terrain like’ surface where the user has an overhead cam view. Since most collisions, it seems, involves objects of a certain general shape, I’m not sure how to approach this problem for a mostly-flat plane. I thought about a CollisionPlane, but I’m not sure how that would play out for parts of the ‘terrain’ that are at a much higher z position than the plane. I’m fairly sure that clicking on “high” mountain or hill areas would result in collisions far behind the actual poly face.

Anyone have any ideas for a strategy?

I found a sort of similar post here but there isn’t much to read:
discourse.panda3d.org/viewtopic … c8bce4ae6f

I’m not optimistic about following the suggestion there, which in my case would mean creating up to 25,000 CollisionPolygon solids for each face, and I imagine Python (maybe even C++) choking on that request given that they are the “most expensive” to use. Perhaps through some miracle this isn’t the case?

Well, the naive thing to do would be to enable collisions with the actual, visible geometry. This can be done simply by setting the collideMask appropriately on your GeomNode (the one that contains your 25,000 vertices).

It will be considerably more expensive to collide a GeomNode than with a CollisionNode, but maybe it’s good enough in your case. Why make more work for yourself if you don’t need it? You’ll get back an (x, y, z) point of collision, which you’ll have to use to figure out the particular part of the mesh yourself.

If it turns out that the GeomNode is too slow, you can try pre-computing a CollisionNode mesh; this will be easy to do if you’re loading the geometry from an egg file (just set the appropriate egg flags).

David

Actually I forgot that I had to split my vertices up to around 125k in total, heh.

Anyway, I didn’t know that was possible. I’ll look into it, and post the results, at least.

If your mesh is completely static, you can also pre-process it with egg-octree (search the forums). This will subdivide the mesh hierarchically for optimal collision processing.

David

Not static, the point here is for mouse interaction to reshape and/or retexture the mesh as it’s a specialized map editor.

However, I think simply colliding with visible geometry is going to work. There’s a drop in framerate from 60 to about 28 or so when doing constant collisions, but this seems fine. Just need to work out how to turn the picker ray on and off (I guess just add to/remove from the scene graph on mouse down/up?) and I think I can move on to the next challenge.

Looks like I keep a solid 60 fps when NOT doing ray collisions nonstop. But a question:

I’m adding a collider on a mouse button up event via:

No problem here - but why do I seemingly NEED to call

self.cTrav.clearColliders()

in order to turn off the ray collision when a collision happens? I’ve tried using

self.cTrav.removeCollider(self.NPpicker)

in the same place to remove the collider (rather than clearColliders()) but instead my fps drops far below 30 and the collideEventAgain handler still keeps getting called. Why wasn’t the collider removed?

Hmm, is removeCollider() returning false or 0? If it is, it means that it failed to find the NodePath you passed it. Perhaps you have already called self.NPpicker.removeNode(), thus invalidating the NodePath, or something like that?

David

hm, a specialized map editor … can you reshape in X/Y or only in Z?

X,Y - undecided still, possibly (vertex drag is easy enough, not sure about creating new or destroying vertexes). I’m sure it’s possible somehow but I’ve yet to think about how the algorithm(s) needed would work. Definitely Z coords and “textures”… of course this is all vaporware until it’s proven I can complete it. But so far, Panda has been able to meet my requirements.

Good idea on checking the return value, I missed that detail in the API docs, however… it’s returning a 1. So I’m kinda confused there.

Relevant main class vars:

``````        self.cTrav = CollisionTraverser()
self.collisionHandler = CollisionHandlerEvent()

pickerNode = CollisionNode('mouseRayCnode')
self.NPpicker = self.camera.attachNewNode(pickerNode)

self.pickerRay = CollisionRay()

Event handlers w/irrelevant code removed:

``````
def collideEventIn(self, entry):
self.cTrav.clearColliders()
#self.cTrav.removeCollider(self.NPpicker)

def mousePick(self, status):
if status == "up":
if self.mouseWatcherNode.hasMouse():
mpos = self.mouseWatcherNode.getMouse()
self.pickerRay.setFromLens(self.camNode,
mpos.getX(),
mpos.getY())``````

I’ve gotta be doing something wacky here. (Note the collideEventIn() with the two methods of collider removal. I’ve verified all the events are firing as expected…)

Hmm, is it possible it got added multiple times? Try “print base.cTrav” to list all of the current colliders, before and after you call removeCollider().

Hmm, here’s some more findings.

The first time I click, collideEventIn() is triggered, which calls removeCollider (and there was only one, confirmed).

The second time I click, collideEventAgain() is triggered–which I did not have the handler call removeCollider() for, so it kept colliding.

What I’m confused about still is that I called clearColliders() ONLY in collideEventIn(), but did NOT call it in collideEventAgain(), yet the infinite “again” collisions never occurred.

So this works fine (fps stays at 60):

``````    def collideEventIn(self, entry):
np_from = entry.getFromNodePath()
np_into = entry.getIntoNodePath()
print base.cTrav
self.cTrav.removeCollider(self.NPpicker)
print base.cTrav

def collideEventAgain(self, entry):
np_from = entry.getFromNodePath()
np_into = entry.getIntoNodePath()
print base.cTrav
self.cTrav.removeCollider(self.NPpicker)
print base.cTrav

def mousePick(self, status):
if status == "up":
if self.mouseWatcherNode.hasMouse():
mpos = self.mouseWatcherNode.getMouse()
self.pickerRay.setFromLens(self.camNode,
mpos.getX(),
mpos.getY())``````

But so does this, which seems odd to me (fps stays at 60):

``````    def collideEventIn(self, entry):
np_from = entry.getFromNodePath()
np_into = entry.getIntoNodePath()
print base.cTrav
self.cTrav.clearColliders()
print base.cTrav

def collideEventAgain(self, entry):
print base.cTrav

def mousePick(self, status):
if status == "up":
if self.mouseWatcherNode.hasMouse():
mpos = self.mouseWatcherNode.getMouse()
self.pickerRay.setFromLens(self.camNode,
mpos.getX(),
mpos.getY())``````

One difference seems to be that when I call clearColliders, it leaves the handler alone, but if I call removeCollider() in both functions, the handler is removed.

Here is the console output for each.

clearColliders() in collideEventIn() ONLY:

``````CollisionTraverser, 1 colliders and 1 handlers:
render/camera/mouseRayCnode handled by CollisionHandlerEvent
ray, o (0.0187639 1 -0.173205), d (1873.82 99863.4 -17296.8)

CollisionTraverser, 0 colliders and 1 handlers:``````

removeCollider() in collideEventIn() and collideEventAgain():

``````CollisionTraverser, 1 colliders and 1 handlers:
render/camera/mouseRayCnode handled by CollisionHandlerEvent
ray, o (0.0707254 1 -0.0750555), d (7062.88 99863.4 -7495.3)

CollisionTraverser, 0 colliders and 0 handlers:``````

Given that the API says this:
clearColliders()
Completely empties the set of collision nodes and their associated handlers.

Is this a bug?

Ah, this is indeed a bug. clearColliders() is failing to correctly empty the set of handlers. I will put in a fix, my apologies for the bug.

David

Well either way it’s not a problem for me so no worries. Thanks for the help.