Mouse Picking on TerrainPage? [SOLVED]

I know this has probably been asked quite a few times, and I've looked through as many threads as my eyes can handle…



I'm simply trying to get the coordinates of a clicked position on a TerrainPage for something like moving a character in an RTS or whatnot.  I have my mouse listeners all hooked up and all that good stuff, however I can't seem to figure out how to grab the correct position.  My own attempts ended in simply getting the Page and Block meshes, and the following attempt gave results that were accurate sometimes.  Wouldn't something as common as mouse picking be already in jME, as opposed to everyone rewriting what already could exist?



As you can tell, I'm simply moving a character to the position I receive - even though many times it's multiple positions.  I was unable to determine which position to use, as it seemed that index 0 was not always correct…


   public void onMousePick(int xCoord, int yCoord)
   {
      Vector2f screenPos = new Vector2f();
      // Get the position that the mouse is pointing to
      screenPos.set(xCoord, yCoord);
      // Get the world location of that X,Y value
      Vector3f start = getCamera().getWorldCoordinates(screenPos, 0);
      Vector3f end = getCamera().getWorldCoordinates(screenPos, 1);

      Ray ray = new Ray(start, end.subtractLocal(start).normalizeLocal());
      TrianglePickResults pr =  new TrianglePickResults();
      pr.clear();
      
      terrain.calculatePick(ray, pr);
      int num = pr.getNumber();
      if(num < 1)
      {
         System.out.println("No PickResults!");
      }
      else
      {
         for (int i = 0; i < num; i++)
         {
            GeomBatch targetMesh = pr.getPickData(0).getTargetMesh();

            ArrayList<Integer> results = new ArrayList<Integer>();
            ((TriangleBatch)targetMesh).findTrianglePick(ray, results);
            Triangle[] tris = new Triangle[((TriangleBatch)targetMesh).getTriangleCount()];
            ((TriangleBatch)targetMesh).getMeshAsTriangles(tris);

            for (Integer triangleResult : results)
            {
               Triangle triangle = tris[triangleResult];
               Vector3f pos = triangle.getCenter();
               System.out.println("PICKED POS: " + pos);
               try{
                  gameData.getPlayerCharacter().getNode().getLocalTranslation().set(pos);
               }catch(Exception e){
                  // かまうない
               }
            }
         }
      }
   }



Thanks in advance!

There is a working demo of this for Terra ( if you use TerrainPage or Block substitute the instanceof catch) in the test RaiseLowerHeightMapTest, click on the terrain and the height at that point is changed



look at the method

public void detectClick(boolean up)





the code is



public void detectClick(boolean up) {
      Vector2f screenPos = new Vector2f(MouseInput.get().getXAbsolute(), MouseInput.get().getYAbsolute());
      Vector3f startPoint = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 0);
      // Get the world location of that X,Y value - far
      Vector3f endPoint = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 1);
      Ray ray = new Ray(startPoint, endPoint.subtract(startPoint));

      results.clear();
      rootNode.findPick(ray, results);
      
      Vector3f loc = new Vector3f();
      Vector3f[] vertex = new Vector3f[3];
      boolean foundMeshHit = false;
      for(int pickIndex = 0; pickIndex < results.getNumber(); pickIndex++) {
         TriMesh mesh = (TriMesh) results.getPickData(pickIndex).getTargetMesh().getParentGeom();
         
         if(mesh instanceof TerraBlockMesh) {
            
            for (int j = 0; j < mesh.getTriangleCount(); j++) {
               mesh.getTriangle(j, vertex);
                                    
                    foundMeshHit = (ray.intersectWhere(vertex[0].addLocal(mesh.getWorldTranslation()),
                        vertex[1].addLocal(mesh.getWorldTranslation()),
                        vertex[2].addLocal(mesh.getWorldTranslation()), loc));
                    if(foundMeshHit){        
                       TerraBlockMesh terraMesh = (TerraBlockMesh)mesh;
                       //System.out.println(terraMesh);
                       int x = (int)(vertex[0].x / tv.getScale().x);
                       int z = (int)(vertex[0].z / tv.getScale().z);
                       
                       XYKey key = terraMesh.xy;
                       TerraBlockData terraData = tm.getTerraData(key.x, key.y);
                       
                       int point = tm.getHeightMapValue(x, z);
                       
                       tm.setHeightMapValue(x, z , 2000);
                       boolean hasLockedBounds = terraMesh.hasLockedBounds();
                       if(hasLockedBounds) {
                          terraMesh.unlockBounds();
                       }
                       
                       terraData.updateTerra();
                       terraData.updateMesh();
                       terraData.getTerraMesh().buildTerra(terraData);
                       
                       if(hasLockedBounds) {
                          terraMesh.lockBounds();
                       }
                    }
            }
         }
      }
   }




Thank you very much  :slight_smile:



This works much better than what I had been using.  Occasionally I get results that are past my actual target (a hill, for example), but not always.  I might just need to see about adding a distance check for the closest pick (isn't it supposed to be index  zero of the pick data?).



Anyway, here's what I'm using now.  I assume it might help others having the same problem.


   public void onMousePick(int xCoord, int yCoord)
   {
      Vector2f screenPos = new Vector2f(xCoord, yCoord);
      Vector3f startPoint = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 0);
      // Get the world location of that X,Y value - far
      Vector3f endPoint = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 1);
      Ray ray = new Ray(startPoint, endPoint.subtract(startPoint));

      TrianglePickResults results =  new TrianglePickResults();
      results.clear();
      rootNode.findPick(ray, results);
      
      Vector3f loc = new Vector3f();
      Vector3f[] vertex = new Vector3f[3];
      boolean foundMeshHit = false;
      for(int pickIndex = 0; pickIndex < results.getNumber(); pickIndex++)
      {
         TriMesh mesh = (TriMesh) results.getPickData(pickIndex).getTargetMesh().getParentGeom();
         
         if(mesh instanceof TerrainBlock)
         {
            for (int j = 0; j < mesh.getTriangleCount(); j++)
            {
               mesh.getTriangle(j, vertex);
                                    
                    foundMeshHit = (ray.intersectWhere(vertex[0].addLocal(mesh.getWorldTranslation()),
                        vertex[1].addLocal(mesh.getWorldTranslation()),
                        vertex[2].addLocal(mesh.getWorldTranslation()), loc));
                    if(foundMeshHit)
                    {
                       System.out.println("Hit at: "+loc.x+", "+loc.y+", "+loc.z);
                       gameData.getPlayerCharacter().getNode().getLocalTranslation().set(loc);
                       break;
                    }
            }
         }
      }
   }

sory i'm very stupid to understand it's code.

Where should i put this code?

Is this code part of the other class?

thank for advance.