Using gluUnProject in a mouseListener

I am trying to implement a feature where the screen location is centered at the spot where a user double clicks. I have added a MouseListener to my GLJPanel and here is its mouseClickced method:

public void mouseClicked( MouseEvent e )
{
    if ( e.getClickCount( ) > 1 )
    {
        int x = e.getX( );
        int y = e.getY( );
        int realY = lastViewport[3] - y - 1;
        System.out.println("x = " + x + "; y = " + y + "; realY = " + realY);
        
        // Figure out the z-window coordinate of the galaxy to pass to gluUnProject
        float z = (viewingDistance - NEAR_CLIP_PLANE) / (FAR_CLIP_PLANE - NEAR_CLIP_PLANE);
        System.out.println("z = " + z);

        double[] objPos = new double[3];
        glu.gluUnProject( x, realY, z, lastModelViewMatrix, 0, lastProjectionMatrix, 0, lastViewport, 0, objPos, 0 );
        lookX = -(float) objPos[0];
        lookY = (float) objPos[1];
        System.out.println("lookX = " + lookX + "; lookY = " + lookY);
        repaint( );
    }
}

I’m trying to use gluUnProject to find the x,y coordinate (in object space) of the screen coordinate clicked. There are two issues:

  1. First, I’ve had to cache the model and projection matrices from the last time in display(). I use the following to get them

        // Cache these matrices for later use in determining pixels per unit.
         gl.glGetIntegerv( GL.GL_VIEWPORT, lastViewport, 0 );
         gl.glGetDoublev( GL.GL_MODELVIEW_MATRIX, lastModelViewMatrix, 0 );
         gl.glGetDoublev( GL.GL_PROJECTION_MATRIX, lastProjectionMatrix, 0 );
    

If I don’t cache them, but try retrieving them in the mouseClicked method, they always come back all zero. I assume this is because something is not initialized in the GL context outside of the display() routine. Anyway caching seems to work fine.

  1. My real problem is getting the proper z-window value to pass into gluUnProject. The docs indicate it should be in the range [0,1] with 0 representing the near clip pane and 1 the far. My code above seems to work fine when the object I’m double clicking is positioned at either the near or far planes. However, for some reason, it doesn’t seem to work when the object is in between. If I set my viewing distance such that the object is exactly halfway between the near and far planes, and pass 0.5f for the z, the location returned doesn’t appear to be correct. I can’t figure out the problem. I know it’s a long shot that someone can help here, but if you have any ideas…

Bill

Just a follow up…

I was reading on page 141 in the Red Book about the “Transformed Depth Coordinate”. It says “As the transformed depth coordinate moves farther away from the near clipping plane, its location becomes increasingly less precise”. This sort of seems like what is going on except that it seems to get more precise again as it approaches the far plane. Is this possibly what is happening? If so, it seems the imprecision is orders of magnitude greater than what I’d expect. I really need gluUnProject to work better when the object is in between the two planes.

If it matters at all, this is on a fully updated Mac OS X MacBook.

Sorry, but you don’t have a current OpenGL context within the context of your mouse event listener’s callback. You’ll need to store off the x and y coordinates of the click, set some state indicating that a click occurred, and call display() / repaint() in order to call gluUnproject from your display callback.

Wow! Really! Since I have the matrices cached from my last display call, I’d think that I wouldn’t need a valid GL context. After all, the GLU routine doesn’t know anything about my GL. That would really make things kludgy.

I have gotten it to work in a different way although if what you say is true then maybe it is fragile. From the mouseClicked, I call gluUnProject twice, once with a z of 0 and again with a z of 1. Then I take a weighted average of the two points, weighted by what the true z value is. This sems to work really well and I would think that this is something like what the gluUnProject code would do. Here is the new mouseClicked method:

public void mouseClicked( MouseEvent e )
{
    if ( e.getClickCount( ) > 1 )
    {
        int x = e.getX( );
        int y = e.getY( );
        int realY = lastViewport[3] - y - 1;
        System.out.println("x = " + x + "; y = " + y + "; realY = " + realY);
        
        // Figure out the z-window coordinate of the galaxy to pass to gluUnProject
        float z = (viewingDistance - NEAR_CLIP_PLANE) / (FAR_CLIP_PLANE - NEAR_CLIP_PLANE);
        System.out.println("z = " + z);

        double[] objPosNear = new double[3];
        double[] objPosFar = new double[3];
        glu.gluUnProject( x, realY, 0, lastModelViewMatrix, 0, lastProjectionMatrix, 0, lastViewport, 0, objPosNear, 0 );
        glu.gluUnProject( x, realY, 1, lastModelViewMatrix, 0, lastProjectionMatrix, 0, lastViewport, 0, objPosFar, 0 );

        // Generate a weighted average
        lookX = -(float) ( ( 1 - z ) * objPosNear[0] + z * objPosFar[0] );
        lookY = (float) ( ( 1 - z ) * objPosNear[1] + z * objPosFar[1] );
        System.out.println( "lookX = " + lookX + "; lookY = " + lookY );
        repaint( );
    }
}

We changed the signatures of the GLU routines in the early JSR-231 betas so they fetch the current GL object from the current context rather than having it passed in as an argument, to match the C signatures.

Looking at the implementation of gluUnproject, you’re right, it doesn’t need a current context. If you wanted to read the depth buffer at the (x, y) coordinates to pass in a correct winz value to the routine you would definitely need a current context.

Ken,

Thanks for the info. Somehow it needs to be documented that the current GL context is an “implicit” parameter of the GLU routines. I’ll certainly keep that in mind in the future.