Question about picking

Hello,

I have a little problem with picking.

1-
First, I use the GLCanvas component, and I don’t animate it. When I want to refresh the image, I explicitly call glCanvas.display().
To do picking, I detect the mousePressed event on my glCanvas, and I call my picking method (pickRay) .

public void mousePressed(MouseEvent evt) 
    {    
        if ( evt.getButton() == MouseEvent.BUTTON1)
        {
            mousePos.x = evt.getX();
            mousePos.y = evt.getY();  
            
            if(glcanvas.isVisible())
                pickRay(glcanvas.getGL(), glcanvas.getGLU(), mousePos.x, mousePos.y);

            //bSelection = true;              
            //glCanvas.display();
        }         
    }
private boolean pickRay(GL gl, GLU glu, double mouse_x, double mouse_y) //--- problème avec le IntBuffer -> class abstraite
    {  // This Is Where Selection Is Done
        System.out.println("debut picking");
        
        IntBuffer buffer = BufferUtils.newIntBuffer(512);
        int hits;                                         // The Number Of Objects That We Selected

        // The Size Of The Viewport. [0] Is <x>, [1] Is <y>, [2] Is <width>, [3] Is <height>
        int[] viewport = new int[4];

        // This Sets The Array <viewport> To The Size And Location Of The Screen Relative To The Window
        gl.glGetIntegerv(GL.GL_VIEWPORT, viewport);
        gl.glSelectBuffer(512, buffer);                   // Tell OpenGL To Use Our Array For Selection

        // Puts OpenGL In Selection Mode. Nothing Will Be Drawn.  Object ID's and Extents Are Stored In The Buffer.
        gl.glRenderMode(GL.GL_SELECT);

        gl.glInitNames();                       // Initializes The Name Stack
        gl.glPushName(0);                       // Push 0 (At Least One Entry) Onto The Stack

        gl.glMatrixMode(GL.GL_PROJECTION);                  // Selects The Projection Matrix
        gl.glPushMatrix();                                          // Push The Projection Matrix
            gl.glLoadIdentity();                                        // Resets The Matrix
    
            // This Creates A Matrix That Will Zoom Up To A Small Portion Of The Screen, Where The Mouse Is.
            glu.gluPickMatrix( (double)mouse_x,  (double)(viewport[3]-mouse_y), 2.0f, 10.0f, viewport);
    
            // Apply The Perspective Matrix
            glu.gluPerspective(45.0f, (float) viewport[2] / (float)viewport[3], 0.1f, 100.0f);  // Calculate The Aspect Ratio Of The Window
            gl.glMatrixMode(GL.GL_MODELVIEW);               // Select The Modelview Matrix
            drawScene(gl);                        // Render The Targets To The Selection Buffer
            gl.glMatrixMode(GL.GL_PROJECTION);              // Select The Projection Matrix
        gl.glPopMatrix();                                           // Pop The Projection Matrix
        gl.glMatrixMode(GL.GL_MODELVIEW);               // Select The Modelview Matrix
        int cont[] = new int[1];
        gl.glGetIntegerv(GL.GL_NAME_STACK_DEPTH, cont);
        hits = gl.glRenderMode(GL.GL_RENDER);           // Switch To Render Mode, Find Out How Many
        
        if (hits > 0)
        {
            System.out.println("un objet trouvé");
            int choose = buffer.get(3);                                 // Make Our Selection The First Object
            int depth = buffer.get(1);                              // Store How Far Away It Is
            int last = 0;
            for (int loop = 0; loop < hits; loop++)
            {       // Loop Through All The Detected Hits
                // If This Object Is Closer To Us Than The One We Have Selected
                if (buffer.get(last+1) < (int)depth)
                {
                    choose = buffer.get(last+3);            // Select The Closer Object
                    depth = buffer.get(last+1);         // Store How Far Away It Is
                }
                last += buffer.get(last) + 3;
             }

             try
             {
                 String s = "" + choose;
                //my treatments ........

             }
             catch(NullPointerException e)
             {  
                 /*e.printStackTrace();*/
                 {
                     System.out.println("PickRay: No Object Selected !");
                 }
             }             
        }
        
        return false;
    }

Could anyone tell me if this code is ok?

Then, calling pickRay from the moussePressed event doesn’t work, the method pickRay is well called, but there isn’t any object picked detected (and all my objects are correctly identified with glLoadName…).

But if I put a boolean variable (bSelection) to true when the mousePressed event is called, then I call glCanvas.display(), and then, in my display() method, I call the pickRay method when bSelection is true, my objects are correctly picked.

So, why DO we have to call pickRay from the display method ???

2-
Then, I have another problem. It seems that I have an “intermediate” image where my scene is drawn two times.
When I press down the mouse, the image present on my glCanvas is the scene drawn two times, at different places…
And I have to call glCanvas.display in the mouseReleased method in order to have a good image. (Or to call two times the glCanvas.display() method in the mousePressed event).

This problem doesn’t appear visually if I launch the animator, because I think we don’t have the time to see this image when the frame rate is over 20 or 100…

So if someone could help me… It is probably an error on the pickRay method, or a problem with my buffers (perhaps I should use swapBuffer somewhere? or desactivate autoSwapBuffer? )

Thank you.

I solved my problem of the scene drawn two times, it was only that I drew the scene one time for picking (in the pickRay method) and another time in the display method (and this is the display method which called the pickRay method).

I have only to test in the display method in which mode I am (selection or not) and therefore to call in the display method the drawing of the scene or not.

Anything derived from the GLDrawable object is only usable in that particular method from the GLEventListener interface.

Another solution, where you don’t have to draw the scene twice is to only record the mouse events and then handle them later in the display method.

private Vector mouseEvents = new Vector();

public void mouseClicked( MouseEvent me ) {
mouseEvents.add( me );
}

private void handleEvents( GLDrawable glDrawable ) {
for( MouseEvent me : mouseEvents ) {
…do the things that are comonly done in the mouseClicked method…
}
mouseEvents.clear();
}

public void display( GLDrawable glDrawable ) {
final GL gl = glDrawable.getGL();
handleEvents( glDrawable );
…draw things…
}