JOGL, color picking

If you would like to be able to select objects in your scene, color picking is one possibility. Not that difficult and complex to realize.

OpenGL has two buffers, a back and a front one. This is useful to avoid that a scene not yet rendered completely could be delivered to the screen and shown to the user. So OpenGL renders in the back buffer and then “swap” all the content in the front one.

All what you have to do is simply disable the automatic buffer swapping when you are creating the canvas, by typing:

//  Disable auto swapping for buffers
gLCanvas.setAutoSwapBufferMode(autoSwapBuffers);

with autoSwapBuffers equals to false.

Then add at the end of the display() something like:


if (!autoSwapBuffers) {
    if (disableManualBufferSwapping) {
        disableManualBufferSwapping = false;
    } else {
        gLCanvas.swapBuffers();
    }
}

To manually swap buffer when the autoSwapBuffers is false. Moreover you can temporary disable it.

Supposing to have a unique pipeline containing all the elements you want to render, each of them may use its own index to generate a specific color.

Color index = new Color(-selection_index);
gl2.glColor3f(index.getRed() / 255.0f, index.getGreen() / 255.0f, 
index.getBlue() / 255.0f);

Each object will use this color to render itself only in the back buffer for picking purposes. User won’t see any strange color because we will disable temporary the buffer swapping by setting disableManualBufferSwapping to true.

Then in the mouse listener whenever the user click to select we need to:
– save the point
– set backBufferRenderingOnly to true
– refresh (I suppose you call the refresh every time you need, so no animator)

In the display() if backBufferRenderingOnly is true, we know we have to render only in the back buffer.


if (backBufferRenderingOnly) {
   //
   //  Rendering into the back buffer for color picking
   //
   Color index;
   int i;
   backBufferRenderingOnly = false;
   FloatBuffer pixels = FloatBuffer.allocate(4);
   RenderingObject object;

   //  Select the back buffer, once for all
   gl2.glDrawBuffer(GL2.GL_BACK);

   ArrayList objects = getRenderingObjects();

   if (!objects.isEmpty()) {
       /*
       * Rendering all objects.
       */
       for (int j = 0; j < objects.size(); j++) {
          ...
       }
                
       int[] viewport = new int[4];

       gl2.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);

       gl2.glReadBuffer(GL2.GL_BACK);
       gl2.glReadPixels(picking.x, viewport[3] - picking.y, 
                             1, 1, GL2.GL_RGBA, GL2.GL_FLOAT, pixels);

       index = new Color(pixels.get(0), pixels.get(1), pixels.get(2));

       i = index.getRGB();

       // -1 means click in the background
       if (i != -1) {

          // This will deselect all the previous selected items
          for (int j = 0; j < objects.size(); j++) {
              object = objects.get(j);
              object.setSelected(false);
          }

          object = getRenderingObjects().get(-i - 2);

          object.setSelected(true);
          this.selectedObject = object;
                    
       } 
       // Click user is on nothing
       else {
          this.selectedObject = null;
          for (int j = 0; j < objects.size(); j++) {
             object = objects.get(j);
             object.setSelected(false);
          }
       }
       //  Disable manual buffer swapping for one time
       disableManualBufferSwapping = true;
    }
}

And then you should be done 🙂

Annunci