Picking problems

Hi,

I’m trying to pick objects using a Picker class that I copied and modified from somewhere on the Panda3d site.

It has a function getObjectHits() to do the picking. It looks like this:

   def getObjectHit(self, targetmdl):
      self.pickedObj=None #be sure to reset this 
      #self.pickerRay.setFromLens(base.camNode, mpos.getX(),mpos.getY()) 
      
      #I think the problem has something to do with these 2 lines
      self.pickerRay.setOrigin(camera.getPos())
      self.pickerRay.setDirection(Vec3(targetmdl.getPos() - camera.getPos()))
      
      self.picker.traverse(render) 
      #the function never proceeds further than this
      if self.queue.getNumEntries() > 0:
         self.queue.sortEntries() 
         self.pickedObj=self.queue.getEntry(0).getIntoNodePath() 
         #print "num entries > 0"

         parent=self.pickedObj.getParent() 
         self.pickedObj=None 
         
         while parent != render: 
            if parent.getTag('pickable')=='true': 
               self.pickedObj=parent 
               return parent 
            else: 
               parent=parent.getParent() 
      return None 

This function was originally designed to pick objects with the pickerRay set from the camera and mouse coordinates as can be seen from the commented out line with self.pickerRay.setFromLens(). I would like to have the function pick along a ray I have specified. I’m trying to set the pickerRay with setOrigin() and setDirection(). I wonder if the vector calculation I’m passing into setDirection() is wrong? I just found it from somewhere and I think it calculates a direction vector from the camera position to the position of targetmdl. Correct me if I’m wrong. I’ve also tried normalizing the result but it didn’t help. It seems that “if self.queue.getNumEntries() > 0:” is never greater than 0.

      self.pickerRay.setOrigin(camera.getPos())
      self.pickerRay.setDirection(Vec3(targetmdl.getPos() - camera.getPos())) 

If your pickerRay is parented to the camera, then it already inherits the camera’s transform. This means that (0, 0, 0) in the picker ray’s space is the same point that camera.getPos() is in world space. It follows that if you set your pickerRay to camera.getPos(), you are actually putting it at camera.getPos() + camera.getPos() in world space.

One easy solution is to parent the pickerRay to render, instead of to the camera. If all of your nodes, including the camera and targetmdl, are parented to render, then the logic above should work. This is the flat hierarchy preferred by many newcomers to Panda. It works fine, and it’s simple and easy to understand: you don’t have to think about coordinate spaces, and you can compute the delta between objects with subtraction, e.g. (targetmdl.getPos() - camera.getPos()).

If you wanted to do it the Panda way, though, taking advantage of the scene graph, you might do something like this instead:

      self.pickerRay.setOrigin(0, 0, 0)
      self.pickerRay.setDirection(Vec3(targetmdl.getPos(camera))) 

David

Thank you once again David.

Ok. I got it pretty much working now, except that when the camera is in certain position I get an assertion error:


AssertionError: _direction != LPoint3f::zero() at line 115 of c:\temp\mkpr\panda3d-1.1.0\panda\src\collide\collisionRay.I

The error is generated by this line of code:


self.pickerRay.setDirection(Vec3(targetmdl.getPos(camera)))

What should I do to fix it?

That will happen whenever targetmdl is in exactly the same place as camera. In this case, there is no particular direction the ray should face to look at targetmdl (the vector is (0, 0, 0)).

There are a number of possible workarounds, but probably the best thing to do is to test for this case and handle it separately (by not creating a pick ray, for instance).

David

Yes, now it works beautifully. Thanks.