How to find clicked point in terrain coordinates?

Hi guys!



I tried to find a clicked point on my TerrainPage to raise the height of my terrain heightmap there. But I don't get the way of converting the clicked position into world coordinates to apply on the heightmap. This is my code:


protected void simpleUpdate() {
      // Get the mouse input device from the jME mouse
      // Is button 0 down? Button 0 is left click
      if (MouseInput.get().isButtonDown(0)) {
         Vector2f screenPos = new Vector2f();
         // Get the position that the mouse is pointing to
         screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
         // Get the world location of that X,Y value
         Vector3f start = display.getWorldCoordinates(screenPos, 0);
         Vector3f end = display.getWorldCoordinates(screenPos, 1);
         System.out.println( start );
         // Create a ray starting from the camera, and going in the direction
         // of the mouse's location
         Ray mouseRay = new Ray(start, end
               .subtractLocal(start).normalizeLocal());
         // Does the mouse's ray intersect the box's world bounds?
         pr.clear();
         rootNode.findPick(mouseRay, pr);

         //TODO get picked terrain point
         page.calculatePick(mouseRay, pr);
         for (int i = 0; i < pr.getNumber(); i++) {
            PickData pd = pr.getPickData(i);
                                //TODO what should I do here??
         }
      }
}



I know that there is a method

TerrainPage#page.setHeightMapValue(x, y, newVal)

, but I can't figure out how to get the correct values for (x,y). Can someone please, please tell me how to get the picked coordinates?

Have you tried this?

camera.getWorldCoordinates();

But I don't want to get the coordinates of the camera, I want to get the point that is clicked on the terrain surface. That's why the Ray is created: The Ray points from the current camera position through the mouse pointer onto the terrain. And I want to get the coordinates of the terrain surface point which is intersected by the Ray…

try it:



 

I am not a native speaker either, it is just that camera.getWorldCoordinates() returns the point in 3D space of a point in the screen. I just thought transforming screen coordinates (from a mouse event for instance) to world coordinates would be relevant here…

According to the documentation, the only two subclasses of PickResults is BoundingPickResults and TrianglePickResults. The most precise of these is TrianglePickResults which can return to you the triangle(s) (polygon) that's been picked. I don't think you can get 3D coordinates from the Spacial.findPick() results as it is implemented today. You may want to look into creating another subclass of PickResults such as "CoordinatePickResults" which will return the 3D coordinate  :smiley:

I once read about an algorithm doing a bubble search (maybe called "newton search") for the closest point to the surface. The algorithm iterated through the whole Ray, each step cuttin half of the Ray and examining if it was below the surface or not… but I can't find it again. This forum needs a moderator who archives the oldest threads… :wink: Anyway, I will try the code of highnik, it looks promising. Thanks for you help.

Ok, I tried again. It is not working… I already feel like a complete dumbass. :frowning: I'd be very happy if you could provide me with a hint about how to solve this. You can read my code syntax highlighted at http://rifers.org/paste/show/4901

I traced the code, and noticed that the TrianglePickData does not have the information about the intersection point filled out. Maybe there is a way to explicitly ask for that information before computing the pick? Unfortunately with your present code and the present implementation of TrianglePickResult, that info seems unavailable.

http://www.jmonkeyengine.com/jmeforum/index.php?topic=3257.msg29013#msg29013



The code has been updated since, to be used on a terrain page instead of block. But the idea is the same.

two common ways:


  1. Do a linear search(like "the librarian" posted) until getting below ground and after that find a more exact intersection point through the bisection or secant method. Easy and pretty fast but can miss small/steep hills etc depending on linear search delta.


  2. Trace the heightmap in the direction of the camera view vector with the good old Bresenham line algorithm and check against it's height. Very easy, pretty fast, never misses :wink:

I think I did a little step forward. I used this code to find the clicked triangle position from my TerrainPage:



   protected void simpleUpdate() {
      // Get the mouse input device from the jME mouse
      // Is button 0 down? Button 0 is left click
      if (MouseInput.get().isButtonDown(0)) {
         System.out.println("LEFT CLICK!");

         Vector2f screenPos = new Vector2f();
//         Get the position that the mouse is pointing to
         screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
//         Get the world location of that X,Y value
         Vector3f start = display.getWorldCoordinates(screenPos, 0);
         Vector3f end = display.getWorldCoordinates(screenPos, 1);

         Ray ray = new Ray(start, end.subtractLocal(start).normalizeLocal());
         rootNode.calculatePick(ray, pr);
         int num = pr.getNumber();
         if(num < 1) {
            System.out.println("No PickResults!");
         } else {
//            System.out.println(num + " PickResults!");
            for (int i = 0; i < num; i++) {
               PickData data  = pr.getPickData(i);
               GeomBatch targetMesh = data.getTargetMesh();
//               System.out.println(i + ": " + targetMesh.getClass());
               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 TRI: " + triangle);
                  System.out.println("PICKED POS: " + pos);
                  page.addHeightMapValue((int)pos.x, (int)pos.z, 1);
                  page.updateFromHeightMap();
               }
            }
         }
      }



But when I use pos to set the height of the terrain with page.setHeightMapValue((int)pos.x, (int)pos.y, 100) or something similiar, it sometimes crashes with an ArrayIndexOutOfBoundException and if not it raises the terrain on some unpredictable position. Is there a coordinate error or something with my position finding algorithm?

Looks like this is really a hard thing to do in JME. Didn't anyone create a basic terrain editor before or do you just want to keep it secret to sell you games?
MrCoder said:

two common ways:

1. Do a linear search(like "the librarian" posted) until getting below ground and after that find a more exact intersection point through the bisection or secant method. Easy and pretty fast but can miss small/steep hills etc depending on linear search delta.

2. Trace the heightmap in the direction of the camera view vector with the good old Bresenham line algorithm and check against it's height. Very easy, pretty fast, never misses ;)


I use 1) because my terrain is tiled so I only need to choose the closest hexagon tile.
But method 2) sounds interesting (even learned a new name ;) ), do you know of any existing implementation? Maybe it could even be moved into the TerrainPage class or something...

Yes! I did it! I finally got to a state where I can… well, control where the terrain is raising/lowering. My code is available here: http://rifers.org/paste/show/4904



It is not perfect, but I works good enough to take a deeper look into handling of the camera. Moving the camera has to be unrelated to moving the mouse, as the mouse pointer is now used to determine the coordinates. So I want to add a keyboard handler to control the camera and use the AbsoluteMouse only to click on terrain. :slight_smile:

I just read the Wiki article: http://www.jmonkeyengine.com/wiki/doku.php?id=spatial in the User's Guide, and noticed they mention that :

If you are to use triangle accurate intersections a call to Spatial

desertrunner said:

Spatial doesn't have that method, so I can't call it.


You are right!! Spatial does not have an updateCollisionTree() method... I guess it was removed, but there is a CollisionTreeManager. I am going off-topic but, this changes should be made to the CVS (by someone who knows how to use it, I would do it, but I am certainly not the one with the knowledge).
duenez said:

I am going off-topic but, this changes should be made to the CVS (by someone who knows how to use it, I would do it, but I am certainly not the one with the knowledge).

Which changes?

Nevermind, these post have definitely not been my best/brightest…  ://

it's the classic algorithm for drawing lines, just google it and you'll find tons of implementations(people have used it for lots of things through the years, like raycasting for raycasting engines like wolfenstein etc)

But this is not a problem with the basic maths, this is a problem of getting the coordinates out of JME, isn't it? It is a problem with my use of AbsoluteMouse or the TerrainPage or something JME specific. I bet in a few days I'll laugh about it, but at the moment I'm freaking out! It looks like my code isn't using the actual camera position to build the Ray, and so the calculation of the resulting coordinates on the terrain are wrong.