Selecting is much like picking, but is more general. The same concept of hits is used, and the same name stack manipulation routines apply. The returned data buffer is filled in the same way. The only difference is in the way the selecting region is defined.
The selecting region corresponds to a rhomboid in world coordinates. Any drawing primitive that intersects with this rhomboid causes a hit and is recorded in the same manner as in picking. There is no special call to set the selecting volume; instead, the matrix manipulation routines are used.
Like picking, no actual drawing occurs while the system is in select mode. Instead, all drawing primitives are transformed through the geometry pipeline and compared to the canonical 3-D unit cube clipping volume. If the drawing primitive is inside this volume, it causes a hit. If it is wholly outside, there is no hit recorded. Transforming through the pipeline means that every drawing primitive is run through the combination of the modeling, viewing, and projection transformations. If, after these transformations, any portion of the primitive ends up inside the volume -w ≤ x, y, z ≤ +w, a hit has occurred.
To perform selecting, you must set up your viewing/projection matrices correctly. You can set up the matrices either immediately before or after a call to the gselect subroutine. The gselect subroutine, unlike the pick subroutine, does not alter the matrix stack.
In most applications, only the viewing matrix is changed, not the projection matrix. The viewing matrix controls what portion of the world space ends up inside the NDC unit cube (-w ≤ x, y, z ≤ +w). Also, the individual modeling routines are not changed because they only serve to embed individual subportions of the drawing into the world coordinate frame. The usual process is to draw the same primitives in the same places they previously occupied while selecting is going on.
To end selecting mode, call the endselect subroutine. The returned data is in exactly the same format as for the endpick subroutine.
The gselect subroutine turns on the selection mode. The gselect and pick subroutines are identical, except that the gselect subroutine is more general. It allows you to define an arbitrary rhomboid in world coordinates as the selecting region. The syntax is as follows:
void gselect(Int16 buffer[], Int32 numnames)
The numnames parameter specifies the maximum number of values that the buffer can store. Each drawing routine that intersects the selecting region causes a hit. The contents of the name stack, preceded by the length of the name stack, are written into the buffer only if this is the first hit since the last time that the name stack was touched. The name stack is controlled in the same way as in picking.
The endselect subroutine takes the system out of selecting mode mode and returns the number of times the name stack was dumped into the buffer. If this number is positive, the buffer was large enough to contain all of the name stacks written to it. If the number is negative, the buffer was too small to store all the name lists. The magnitude of the returned number is the number of name stacks that were recorded. The syntax is as follows:
Int32 endselect(Int16 buffer[])
The buffer parameter contains copies of the name stack that were recorded as hits occurred. As explained previously, not every hit causes the name stack to be recorded; only the first hit after the name stack has been touched is recorded. When stored in the buffer, each name stack is preceded by the length of the name stack. If the name stack is empty when a hit occurs, the length is recorded as 0 (zero), and the stack is not recorded.
The following program demonstrates a simple application of selecting. This program draws a planet and then draws a box representing the ship each time the left mouse button is pressed. The program prints CRASH and exits when the ship collides with the planet:
/*** crash.c - example of selecting ***/
#include <gl/gl.h> #include <gl/device.h> #define BUFSIZE 50 #define PLANET 109 #define SHIPWIDTH 20 #define SHIPHEIGHT 10
void drawplanet() { circfi(200, 200, 20); }
int main() { short dev, val; short buffer[BUFSIZE]; int count, i; float shipx, shipy, shipz; int xorigin, yorigin;
minsize(300, 300); (void) winopen("crash"); getorigin(&xorigin, &yorigin); qdevice(LEFTMOUSE); qdevice(ESCKEY); color(BLACK); clear();
for (i = 0; i < BUFSIZE; i++) buffer[i] = 0;
color(RED); drawplanet(); while (TRUE) { dev = qread(&val); switch (dev) { case LEFTMOUSE: if (val) { shipz = 0; shipx = getvaluator(MOUSEX) - xorigin; shipy = getvaluator(MOUSEY) - yorigin;
color(BLUE); rect(shipx, shipy, shipx + SHIPWIDTH, shipy + SHIPHEIGHT);
/* specify the selecting region to be a box surrounding the rocket ship */ pushmatrix(); ortho2(shipx, shipx + SHIPWIDTH, shipy, shipy + SHIPHEIGHT); initnames(); gselect(buffer, BUFSIZE); /*enter selecting mode*/ loadname(PLANET); drawplanet(); /* no actual drawing takes place */ count = endselect(buffer); /* exit select mode */ popmatrix();
/* check to see if PLANET was selected */ if ((count > 0) && (buffer[0] == 1) && (buffer[1] == PLANET) ) { printf("CRASH!\n"); } } break;
case ESCKEY: return 0; break; } } }