Picking issues

Hi,

I’m having some issues with picking on a heightfield and thought maybe someone here could help me.
So, here is the situation:
-we created a mesh corresponding to a heightfield. We display it, there is no problem, no hole in, everything seems to be ok.
-we want to pick the exact altitude of this mesh/heightfield at one point. So we use what seems to me to be the standard collisions in panda.
-most time, it worked correctly, but in some case, the collision doesn’t work and my CollisionHandlerQueue is empty even if I’m sure the mesh and the ray should have collide.

Here is the code where I initialize the picking objects:

        self.picker     = CollisionTraverser()        
        self.pq         = CollisionHandlerQueue()     
        self.pickerNode = CollisionNode('mouseRay')
           self.pickerNode.setCollideMask(self.heightfield_mesh.getTopNode().getNetCollideMask())
        self.picker_ray = CollisionRay()
        self.picker_ray.setDirection(0,0,-1)
        self.pickerNode.addSolid(self.picker_ray)     
        self.pickerNP = render.attachNewNode(self.pickerNode)
        self.picker.addCollider(self.pickerNP, self.pq)
        self.drawing.mesh.reparentTo(render)

And here is where I do the picking:

    def get_altitude(self, pos):
        self.picker_ray.setOrigin(pos[0],pos[1],1000)
        self.picker.traverse(self.heightfield_mesh)
        
        altitude = 1000

        if self.pq.getNumEntries() > 0:
            entry = self.pq.getEntry(0)
            pos_in_world = entry.getSurfacePoint(render)
            altitude= pos_in_world[2]

Do I do something wrong?
What really surprises me is that in most case it worked perfectly, and on just some points it fails.

I have been suggested that maybe the issue come from the fact that the intersection point is just on a vertex or an edge of a triangle.
The fact that sometimes when a picking “failed” I get the same no-collision result when moving for a short distance on a direction and a correct result when moving in another direction even of a very small distance seems to confirm it.

Does it seems possible to you? Is it a normal/known situation?

I also noticed that I often got several collision results from a picking, all the result being in fact the same point.

Sure, this is very common. Collision detection is never 100% reliable, not in any engine. You’ll have to be prepared for the occasional misses.

David

If you can afford it, you might try doubling down on your terrain geometry- create a new mesh explicitly for collision (don’t actually render it), oversizing each triangle slightly so they overlap/intersect on points that were previously ambiguous vertices/edges. You’d start to see some slight inaccuracy around some edges, but you should be more likely to get ~some~ collision registering.

Hi.
I will use this thread to ask a question about picker.
I just found that

self.picker     = CollisionTraverser() 
[...]
self.picker.traverse(self.root_node)

doesn’t return any collision
but

self.picker     = CollisionTraverser() 
[...]
self.root_node.reparentTo(render)
self.picker.traverse(self.root_node)

return the correct result.

Now, I never use render at all. I though it was just a normal nodePath.
(I have created more than one nodePath and I change scene with, base.cam.node().setScene())
So at this time, render is just an empty NodePath.

Does render has something special when it come for CollisionTraverser ?
Or is there any other thing like setScene that I should use ?

Well, that depends, but I assume you are traversing like this:

self.picker.traverse(render)

which would explain why reparenting to “render” makes it work.

Or perhaps you are relying on base.cTrav for some reason, which is automatically traversed onto render every frame.

But you’re right, there’s nothing special about render. It’s just a node.

David

Ok.
@pro_rsoft
like I said pro_rsoft, I only use

self.picker.traverse(self.root_node)

@drwr
How can I see if I use base.cTrav ? I meean, get Traverser from PandaModules and use it directly:

  self.picker     = CollisionTraverser()        #Make a traverser
        self.pq         = CollisionHandlerQueue()     #Make a handler
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNode.setFromCollideMask(BitMask32.bit(bitmask))
        # self.pickerNode.setIntoCollideMask(BitMask32.bit(bitmask))
        self.pickerRay = CollisionRay()               #Make our ray
        self.pickerNode.addSolid(self.pickerRay)      #Add it to the collision node
        self.pickerNP = self.root_node.attachNewNode(self.pickerNode)
        self.picker.addCollider(self.pickerNP, self.pq)

 def _obj_under_cursor(self, tag, bitmask) :
        self.change_picker_bitmask(bitmask = bitmask)
        # ==
        selectable = None
        self.picker.traverse(self.root_node)
        num      = ''
        num_axis = ''
        if self.pq.getNumEntries() > 0:
[...]

If you don’t know whether you’re using base.cTrav, you’re not.

I assure you, though, that collisions do work correctly when not parented under render; we do that all the time. So something else must be going on in your case that only makes it appear to be working only when under render.

David

That’s really unnecessary, since it’s already done at collision check, when calculating the distance of the collision point to each of triangle edge.
It would be a good addition if the espilon is exposed to python. Even better if each collision solid has its own adjustable espilon, so a thin solid like a ray could use a larger one.