Open GL Super Bible

Previous Table of Contents Next


Picking

Picking occurs when you use the mouse position to create and use a modified viewing volume during selection. By creating a smaller viewing volume positioned in your scene under the mouse position, only objects that would be drawn within that viewing volume will generate hit records. By examining the selection buffer, you can then see which objects, if any, were clicked on by the mouse.

The gluPickMatrix function is a handy utility that will create a matrix describing the new viewing volume:

void gluPickMatrix(GLdouble x, GLdouble y, GLdouble width, GLdouble
height, GLint viewport[4]);

The x and y parameters are the center of the desired viewing volume in window coordinates. The mouse position can be plugged in here, and the viewing volume will be centered directly underneath the mouse. The width and height parameters then specify the dimensions of the viewing volume in window pixels. For clicks near an object, use a large value; for clicks right next to the object or directly on the object, use a smaller value. The viewport array contains the window coordinates of the currently defined viewport. This can easily be obtained by calling

glGetIntegerv(GL_VIEWPORT, viewport);

To use gluPickMatrix, you should first save the current Projection matrix state (thus saving the current viewing volume). Then call glLoadIdentity to create a unit-viewing volume. Calling gluPickMatrix then translates this viewing volume to the correct location. Finally, you must apply any further perspective projections you may have applied to your original scene; otherwise, you wonít get a true mapping. Hereís how itís done for the PLANETS example (from Listing 19-3):

// 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);

In this segment, the viewing volume is saved first. Then selection mode is entered, the viewing volume is modified to include only the area beneath the mouse cursor, and the scene is redrawn by calling RenderScene. After the scene is rendered, we call glRenderMode again to place OpenGL back into normal rendering mode and get a count of generated hit records.

In the next segment, if a hit occurred (for this example, there is either one hit or none), we pass the entry in the selection buffer that contains the name of the object selected or our ProcessPlanet function. Finally, we restore the Projection matrix (thus the old viewing volume is restored) and switch the active matrix stack back to the Modelview matrix, which is usually the default.

// 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);

The ProcessPlanet function simply displays a message box telling which planet was clicked on. This code is not shown because it is fairly trivial, consisting of no more than a switch and some message-box function calls.

The output from PLANETS is shown in Figure 19-2, where you can see the result of clicking on the second planet from the Sun.


Figure 19-2  Output from PLANETS, after clicking on a planet

Hierarchical Picking

For the PLANETS example, we didnít push any names on the stack, but rather just replaced the existing one. This single name residing on the name stack was then the only name returned in the selection buffer. We can also get multiple names when a selection hit occurs, by placing more than one name on the name stack. This is useful, for instance, in drill-down situations when you need to know not only that a particular bolt was selected, but that it belonged to a particular wheel, on a particular car, and so forth.

To demonstrate multiple names being returned on the names stack, we will stick with the astronomy theme of our previous example. Figure 19-3 shows two planets (okay, so use a little imagination)óa large blue planet with a single moon, and a smaller red planet with two moons.


Figure 19-3  Two planets with their respective moons

Rather than just identify the planet or moon thatís clicked on, we want to also identify the planet that is associated with the particular moon. The code in Listing 19-4 shows our new rendering code for this scene. We push the names of the moons onto the names stack so that it will contain the name of the planet as well as the name of the moon when selected.

Listing 19-4 Rendering code for the MOONS example program

#define EARTH   1
#define MARS    2
#define MOON1   3
#define MOON2   4

// Called to draw scene
void RenderScene(void)
        {
        // Clear the window with current clearing color
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Save the matrix state and do the rotations
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();

        // Translate the whole scene out and into view
        glTranslatef(0.0f, 0.0f, -300.0f);

        // Initialize the names stack
        glInitNames();
        glPushName(0);

        // Draw the Earth
        glPushMatrix();
        glRGB(0,0,255);
        glTranslatef(-100.0f,0.0f,0.0f);
        glLoadName(EARTH);
        auxSolidSphere(30.0f);

        // Draw the Moon
        glTranslatef(45.0f, 0.0f, 0.0f);
        glRGB(220,220,220);
        glPushName(MOON1);
        auxSolidSphere(5.0f);
        glPopName();
        glPopMatrix();

        // Draw Mars
        glRGB(255,0,0);
        glPushMatrix();
        glTranslatef(100.0f, 0.0f, 0.0f);
        glLoadName(MARS);
        auxSolidSphere(20.0f);

        // Draw Moon1
        glTranslatef(-40.0f, 40.0f, 0.0f);
        glRGB(220,220,220);
        glPushName(MOON1);
        auxSolidSphere(5.0f);
        glPopName();

        // Draw Moon2
        glTranslatef(0.0f, -80.0f, 0.0f);
        glPushName(MOON2);
        auxSolidSphere(5.0f);
        glPopName();
        glPopMatrix();

        // Restore the matrix state
        glPopMatrix(); // Modelview matrix

        // Flush drawing commands
        glFlush();
        }


Previous Table of Contents Next