Open GL Super Bible

Previous Table of Contents Next


Working with Selection Mode

As mentioned, OpenGL can operate in three different rendering modes. The default mode is GL_RENDER, in which all the drawing actually occurs on screen. To use selection, we must change the rendering mode to selection by calling the OpenGL function:

glRenderMode(GL_SELECTION);

When we actually want to draw again, we call

glRenderMode(GL_RENDER);

to place OpenGL back in rendering mode. The third rendering mode is GL_FEEDBACK, discussed later in this chapter.

The naming code in Listing 19-1 has no effect unless we first switch the rendering mode to selection mode. Most often, you will use the same function to render the scene in both GL_RENDER mode and GL_SELECTION modes, as we have done here.

Listing 19-2 is the code that is triggered by the clicking of the left mouse button. This code gets the mouse coordinates from lParam and passes them to ProcessSelection, which will process the mouse click for this example.

Listing 19-2 Code that responds to the left mouse button click

case WM_LBUTTONDOWN:
        {
        int xPos = LOWORD(lParam);  // horizontal position of cursor
        int yPos = HIWORD(lParam);  // vertical position of cursor

        // Render in selection mode and display results
        ProcessSelection(xPos, yPos);
        }

The Selection Buffer

The selection buffer is filled with hit records during the rendering process. A hit record is generated whenever a primitive or collection of primitives is rendered that would have been contained in the viewing volume. Under normal conditions, this is simply anything that would have appeared on screen.

The selection buffer is an array of unsigned integers, and each hit record occupies at least four elements of the array. The first array index contains the number of names that are on the names stack when the hit occurs. For the PLANETS example (Listing 19-1), this will always be 1. The next two entries contain the minimum and maximum window z coordinates of all the vertices contained by the viewing volume since the last hit record. This value, which ranges from [0,1], is scaled to the size of an unsigned integer (2^321) for storage in the selection buffer. This pattern, illustrated in Figure 19-1, is then repeated for all the hit records contained in the selection buffer.


Figure 19-1  Hit record format of the selection buffer

The format of the selection buffer gives you no way of knowing how many hit records you will need to parse. This is because the selection buffer is not actually filled until you switch the rendering mode back to GL_RENDER. When you do this with the glRenderMode function, the return value of glRenderMode returns the number of hit records copied.

Listing 19-3 shows the processing function called when a mouse click occurs for the PLANETS example program. It shows the selection buffer being allocated and specified with glSelectBuffer. This function takes two arguments: the length of the buffer and a pointer to the buffer itself.

Listing 19-3 Function to process the mouse click

// Process the selection, which is triggered by a right mouse
// click at (xPos, yPos).
#define BUFFER_LENGTH 64
void ProcessSelection(int xPos, int yPos)
        {
        // Space for selection buffer
        GLuint selectBuff[BUFFER_LENGTH];

        // Hit counter and viewport storage
        GLint hits, viewport[4];

        // Set up selection buffer
        glSelectBuffer(BUFFER_LENGTH, selectBuff);

        // Get the viewport
        glGetIntegerv(GL_VIEWPORT, viewport);

        // Switch to projection and save the matrix
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();

        // Change render mode
        glRenderMode(GL_SELECT);

        // Establish new clipping volume to be unit cube around
        // mouse cursor point (xPos, yPos) and extending two pixels
        // in the vertical and horizontal direction
        glLoadIdentity();
        gluPickMatrix(xPos, yPos, 2,2, viewport);

        // Apply perspective matrix
        gluPerspective(45.0f, fAspect, 1.0, 425.0);

        // Draw the scene
        RenderScene();

        // Collect the hits
        hits = glRenderMode(GL_RENDER);

        // If a single hit occurred, display the info.
        if(hits == 1)
                ProcessPlanet(selectBuff[3]);

        // Restore the projection matrix
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();

        // Go back to modelview for normal rendering
        glMatrixMode(GL_MODELVIEW);
        }


Previous Table of Contents Next