Isometric Tilemap - Can OpenGL help me with the mouse?

Hi, i am working on a little engine for isometric 2D like games. I used to work only with 2D which was pretty much work compared to the work i have now with OpenGL thanks to lwjgl :slight_smile:

What i want to do is to calculate which tile is under my mouse. This isn’t a problem as long as you have a flat map. But i am going to have a map where the usual approach doesn’t fit very well because of great height-differences.
I am a real noob to OpenGL and i hope, that it provides some help on this subject. Is there a good way to determine which sprite is under the mouse? I am feeling like there has to be a function that takes screencoordinates and gives a sprite back.
Another question is, if openGL has some function that can determine, if an object is (at least partially) drawn to the screen or if its unvisible(eg due to scrolling).

I think a little more insight is needed so you can understand what i have in front of me :wink:

This is how the 2D Map looks like. There is only a plain white Texture, but i can change it like i want^^

http://img13.imageshack.us/img13/6791/map1z.png

The Light/Shadow is calculated with the normals of the planes and a sun vector. Just amazing compared to the non-opengl version is, that i can easily move the sun around and simulate a day-cycle :slight_smile:

The Map consists out of Tiles and Nodes. Nodes can be seen here:

http://img683.imageshack.us/img683/6898/map3dk.png

There can be arbitrary jumps between heights. That makes mousemapping hard or improper.

The Tiles can be seen here:

http://img808.imageshack.us/img808/52/map2c.png

The blue lines mark the tiles, the red ones divide each tile into 4 triangles. I had to do that because quads won’t be plane all the time.

I would be very happy if someone could help me with my little game. If you have any more questions i’d be glad to answer them :wink:

PS: i am sorry if similar topics have been discussed. I couldn’t find them, if they exist. Only the usual Mouse-To-Map questions with easier terrain.

Hi Rongo,

The best thing to start with would be to google “opengl picking” as that seems to be what you want.

I am working on my first game and it has tiles as well though and I didn’t want to draw everything twice (necessary for picking) and I didn’t bother creating color information for each tile (as every tile needs to have a unique color to use picking). I ended up creating a function that approximates the tile you’re currently selecting (including rotation) and then builds up 2d shapes of the possible tiles and does contains() using the mouse position to find out which tile I was currently selecting. The code executes in 0.3ms or something on my laptop so it’s barely noticeable on the fps.

A screenshot of it working (see highlighted square):

http://www.octopus.se/filer/tiles.jpg

Regarding the visibility of squares, calculate your viewport and only draw what is inside :slight_smile:

Thanks a lot for the answer(s), your Screenshot looks very nice :slight_smile:

I already thougth about calculating the mouseposition and that would also be my first choice. Can you briefly explain how you do the approximation? On your picture i can see that you have hills - so there could be a very big difference in coordinates but only a small in pixels between two spots. That makes the approximation difficult for me (i hope it is understandable what i mean).
Did you write your contains() method yourself or does OpenGL offer that (or LWJGL)?

I’ll now look if i can find out something about this mysterious viewport you are talking of. As i said - noob to OpenGL :wink:

This is my function to get the tile based upon mouse pos. As I said it’s my first game (and first java project) so the gurus on this forum will probably find 237 things that could be improved :stuck_out_tongue: Main thing for me at the moment is that it works and is quick enough to not limit the framerate.


    //Get the tile in the 3D space where the passed 2D applet position resides
    private Point3D getSquare(final Point2D point2D) {
        if (point2D == null) {
            return null;
        }
        final Polygon quad = new Polygon();
        final Point3D realPos = new Point3D(0, 0, 0);
        final Point2D currentPos = new Point2D(0, 0);
        Point3D[] corners3D = null;
        final Point2D[] corners2D = new Point2D[4];
        final java.awt.geom.Point2D awtPoint2D = new java.awt.geom.Point2D.Float(0, 0);
        corners2D[0] = new Point2D(0, 0);
        corners2D[1] = new Point2D(0, 0);
        corners2D[2] = new Point2D(0, 0);
        corners2D[3] = new Point2D(0, 0);
        final LinkedList<Point2D> cornersChecked = new LinkedList<Point2D>();
        Math3D.generateArrays();
        for (float i = LandTileFunctions.getMaxHeight(); i >= 0; i--) {
            Math3D.getPoint3DfromPoint2D(point2D, i, realPos);
            for (int j = -1; j < 2; j++) {
                for (int k = -1; k < 2; k++) {
                    currentPos.setLocation(MyMath.floor(realPos.getX() + j), MyMath.floor(realPos.getY() + k));
                    if (!cornersChecked.contains(currentPos)) {
                        cornersChecked.add(currentPos.clone());
                        corners3D = LandTileFunctions.getTileInformation(currentPos); //Get the 3D points of all corners of the passed tile
                        if (corners3D != null) {
                            for (int l = 0; l < 4; l++) {
                                Math3D.getPoint2DfromPoint3D(corners3D[l], corners2D[l]);
                                quad.addPoint((int)corners2D[l].getX(), (int)corners2D[l].getY());
                            }
                            awtPoint2D.setLocation(point2D.getX(), point2D.getY());
                            if (quad.contains(awtPoint2D)) {
                                return corners3D[0];
                            } else {
                                quad.reset();
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

The used functions in the Math3D class looks like this:


    private static FloatBuffer modelview = BufferUtils.createFloatBuffer(16); //The model view for caching

    private static FloatBuffer projection = BufferUtils.createFloatBuffer(16); //The projection for caching

    private static IntBuffer viewport = BufferUtils.createIntBuffer(16); //The view port for caching

    //Generate the private variables before calling the below functions that rely on them
    public static void generateArrays() {
        GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview);
        GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);
        GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);
        modelview.rewind();
        projection.rewind();
        viewport.rewind();
    }

    //Get the position inside the application from a passed position on the applet together with depth
    public static void getPoint3DfromPoint2D(Point2D p, float zPos, Point3D realPos) {
        if (p == null) {
            return;
        }
        final FloatBuffer windowCoordsF = FloatBuffer.allocate(3);
        final float[] vec = new float[3];
        //Get the coordinates at the far plane
        GLU.gluUnProject(p.getX(), p.getY(), 1f, modelview, projection, viewport, windowCoordsF);
        //Get the vector that goes from the UnProject to the camera
        vec[0] = Camera.getX() - windowCoordsF.get(0);
        vec[1] = Camera.getY() - windowCoordsF.get(1);
        vec[2] = Camera.getZ() - windowCoordsF.get(2);
        //Calculate X and Y based on a known Z
        windowCoordsF.put(0, (zPos - windowCoordsF.get(2)) / vec[2] * vec[0] + windowCoordsF.get(0));
        windowCoordsF.put(1, (zPos - windowCoordsF.get(2)) / vec[2] * vec[1] + windowCoordsF.get(1));
        windowCoordsF.put(2, zPos);
        realPos.setLocation(windowCoordsF.get(0), windowCoordsF.get(1), windowCoordsF.get(2));
    }

    //Get the position on the applet from a passed position inside the application
    public static void getPoint2DfromPoint3D(Point3D p, Point2D AppletPos) {
        if (p == null) {
            return;
        }
        final FloatBuffer windowCoords = FloatBuffer.allocate(3);
        //Get the coordinates at the screen
        GLU.gluProject(p.getX(), p.getY(), p.getZ(), modelview, projection, viewport, windowCoords);
        AppletPos.setLocation(windowCoords.get(0), windowCoords.get(1));
    }

Be aware that if you walk down this route you’ll only be able to select the tiles, while if you implement picking you’ll be able to use it for small details on the screen as well.