[SOLVED]JMEDekstop and MousePicking

Hi to all.

I'm currently developing a scene editor and I've based my code on the RenParticle Editor.

I'm trying to implement a mouse picking function that picks the object on which I click with to select it but I'm getting strange results. Sometimes it picks the object and sometimes it doesn't and if I have more than one object on the scene it's not accurate or worse, it picks nothing.

I've put my picking code inside the CameraHandler class here's my code:


public void mousePressed(MouseEvent arg0) {

      last.x = arg0.getX();
      last.y = arg0.getY();

      int mods = arg0.getModifiers();

      if (mods == 16) {

         Vector2f screnPos = new Vector2f();
         screnPos.set(arg0.getX(), arg0.getY());

         TrianglePickResults results = new TrianglePickResults();
         results.setCheckDistance(true);
         
         Vector3f worldCoords = display.getWorldCoordinates(screnPos, 1.0f);
         
         final Ray mouseRay = new Ray(impl.cam.getLocation(), worldCoords
               .subtractLocal(impl.cam.getLocation()));
         
         mouseRay.getDirection().normalizeLocal();
         results.clear();
      

         impl.getSceneNode().findPick(mouseRay, results);



         if (results.getNumber() > 0) {
            
            for (int i=0; i<results.getNumber(); i++) {
               results.getPickData(i).getTargetMesh().setRandomColors();
               System.out.println(results.getPickData(i).getTargetMesh().getName());
            }
         }
         else {
            System.out.println("Nothing");
         }

      }
   }



It simply returns the name of the Mesh that it picks.
I guess the problem is the way I create the Ray or the way I get the world coordinates or the mouse coordinates.
I've found the code searching on the forum and I've tried to adapt it to my needs but it doesn't work.
Please can someone help me?
Thanks a lot!

Try this out:


       private PickResults picker = null;
   /**
    *  Checks if the current mouse position corresponds to any scene objects that are in the pickingList array.
    * */
   private Geometry detectPick()
   {
      Geometry pickedGeom;
       float xScreen = mouseAdapter.getXAbsolute(); // mouseAdapter is a class I use to be able to use the same classes in an applet and in a desktop application, because I've found it to behave differently for the two types of apps. For a desktop app, I believe MouseInput.get().getXAbsolute() will work here.
       float yScreen = mouseAdapter.getYAbsolute();
       
      Vector2f screenPos = new Vector2f(xScreen, yScreen);
      Vector3f worldCoordsStart = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 0);
      Vector3f worldCoordsEnd = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 1);
      
      Vector3f startPoint = worldCoordsStart;
      Vector3f endPoint = worldCoordsEnd.subtractLocal(worldCoordsStart);
      
      Ray rayLine = new Ray(startPoint, endPoint);
      
      picker = new TrianglePickResults();
      picker.setCheckDistance(true);
                mouseNode.findPick(rayLine, picker); // mouseNode is the node with the geometry to pick. For example, it can be the root node
      pickedGeom = null;   // I keep this stored as a class variable, because I need it for other purposes. This is the resulting pick.
      
      if (picker.getNumber() > 0)
      {
         float distance = picker.getPickData(0).getDistance()*2;
         for (int i = 0; i < picker.getNumber(); i++)
         {
                                // get the closest object
            if (picker.getPickData(i).getDistance() < distance)
            {
                                       // pickingList is just a list of objects I allow to pick               
               if(pickingList.contains(picker.getPickData(i).getTargetMesh().getName()))
               {
                  distance = picker.getPickData(i).getDistance();
                  pickedGeom = picker.getPickData(i).getTargetMesh();
               }            
            }
         }
      }
      
      return pickedGeom;
   }



Returns the geometry instead of the name, but I think you'll easily adapt it to your needs.

(This is the code I use in my application for picking)

First of all thank you so much Wizem for your quick answer!!! XD

Your code helped me a lot to get the picking more accurate but still I've got a problem.

As you may know, the RenParticleEditor let you navigate inside the view moving the camera in a smilar way of softwares like Maya so I modified you code to get the Start Point from the camera location and I think it works great for what I need.

The problem comes when I have to calculate the end point.

To let you better understand here are two screenshots.


  1. The camera position and mouse cursor position when I click to get the object:




  2. The result I get (I created a line that start from cam.getLocation() function and it ends at worldCoordsEnd.subtract(impl.cam.getLocation() to show the result)





    As you can see the End point is wrong and it can intercept the object (if it intercepted it the object color would be set to random colors).

    Here’s my code:


private void ObjectPicking(MouseEvent evt)
   {
      float xScreen = evt.getX();
      float yScreen = evt.getY();
      
      Vector2f screenPos = new Vector2f(xScreen, yScreen);

      Vector3f worldCoordsEnd = display.getWorldCoordinates(screenPos, 1);
      
                //The start point taken from the camera location
      Vector3f startPoint = impl.cam.getLocation();

                //Here's how I calculated the end point
      Vector3f endPoint = worldCoordsEnd.subtract(impl.cam.getLocation());
      
      Ray ray = new Ray(startPoint, endPoint);
      
      picker = new TrianglePickResults();
      picker.setCheckDistance(true);
      
      impl.getSceneNode().findPick(ray, picker);
      
      pickedObject = null;
      
      Vector3f[] vertex = new Vector3f[2];
      vertex[0] = worldCoordsStart;
      vertex[1] = worldCoordsEnd;
      
      Line locLine = new Line("myLine", vertex, null, null, null);
      locLine.getDefaultColor().set(ColorRGBA.green);
      
      impl.getSceneNode().attachChild(locLine);
      
      if (picker.getNumber() > 0)
      {
         float distance = picker.getPickData(0).getDistance()*2;
         
         for (int i = 0; i < picker.getNumber(); i++)
         {
            if (picker.getPickData(i).getDistance() < distance)
            {
               distance = picker.getPickData(i).getDistance();
               picker.getPickData(i).getTargetMesh().setRandomColors();
            }
         }
      }
   }



Any idea how to calculate the end point?

Been away for a couple of days, sorry  :oops:



Actually, in my application the camera can also be freely moved and rotated, and my code works for any camera position; strange that it didn't for you.



I think the starting point shouldn't be as much from where the camera is in the world, so much as where the mouse is in the camera plane, which I believe would be exactly display.getWorldCoordinates(screenPos, 0);



The camera location should probably be something like where display.getWorldCoordinates(screenPos, 0) would be if the mouse was in the center of the screen.



And doesn't it look in the screenshot like the line is starting in the wrong place?



But, I think if you're set on getting the starting point from the camera location, it would make sense to get the ending point from the camera location and an offset from it's location. For example, where the camera would have been if the had been moved forward, or something like that (though I'm not sure if that would give any reliable picking results).

Sorry if it took me somedays to answer but I wasn't at home.

Anyway, after searching through the forum suddenly I've found the solution.

I guess the problem was in the creation of the ray so to create the ray I use this code:



Ray ray = display.getPickRay(screenPos, true, new Ray());



and here's the entire code for my picking function:


private void ObjectPicking(MouseEvent evt)
   {
      float xScreen = evt.getX();
      float yScreen = evt.getY();
      
      Vector2f screenPos = new Vector2f(xScreen, yScreen);
      Vector3f worldCoordsStart = display.getWorldCoordinates(screenPos, 0);
      Vector3f worldCoordsEnd = display.getWorldCoordinates(screenPos, 1);
      
      Vector3f startPoint = worldCoordsStart;
      Vector3f endPoint = worldCoordsEnd.subtractLocal(worldCoordsStart).normalizeLocal();
      
      Ray ray = display.getPickRay(screenPos, true, new Ray());
      
      picker = new TrianglePickResults();
      picker.setCheckDistance(true);
      
      impl.getSceneNode().findPick(ray, picker);
      
      pickedObject = null;
   
      if (picker.getNumber() > 0)
      {
         float distance = picker.getPickData(0).getDistance()*2;
         
         for (int i = 0; i < picker.getNumber(); i++)
         {
            if (picker.getPickData(i).getDistance() < distance)
            {
               distance = picker.getPickData(i).getDistance();
               picker.getPickData(i).getTargetMesh().setRandomColors();
            }
         }
      }
   }



I hope it will help other people who will come across this problem!
Thanks for your help!