Tutorial no.3 - Selection and Picking
Introduction
Sometimes, it's necessary to know which object you've drawn on the screen was selected by the user.Wether you need it for your mapeditor to obtain which wall was selected by the user or for a strategy game to determine which building the player selected, it's very easy to implement once you know how it works.As I haven't seen any good tutorials about OpenGL's selection buffer (especially none written in Delphi) and I really needed it for the mapeditor of the ZornGL engine, I tried to figure out myself how to do it.And in this tutorial I will show you how to use it in your own app.I also included a simple app with source.

Step 1 - Pushing your objectnames on the Name Stack
The most important thing you'll need for selection is the Name Stack.This is the place where OpenGL will store the "names" (despite the name,you push integer values on the stack) that will later be returned to you to determine the selected objects.As all other OpenGL stacks, you can push names on the stack.Although you can pop names from the stack, you won't have to use it in any way.Note that all commands to manipulate the Name Stack are only valid if you're in selection mode.But more on that later.First take a look at this little example and it's description beneath :



const
 Sun   = 1;
 Mars  = 2;
 Earth = 3;
 Moon  = 4;

procedure TForm1.DrawScene;
begin
glInitNames;
glPushName(0);
...
glLoadName(Sun);
glCallList(SunList);
...
glLoadName(Mars);
glCallList(MarsList);
...
end;

Implementation of the selection mode is very easy as you can see.First you'll have to empty the Name Stack.And then you MUST push at least one name on the stack.If you don't do it, the next glLoadName command won't load a name on the stack, but will generate a GL_INVALID_OPERATION error.
After our Name Stack has been set up we load a new name on our stack for every object in our scene (or better said all objects that the user is able to select).After you've loaded the name on the stack you draw your object.It makes no difference if you call a displaylist or do it with glBegin and glEnd.As you know, all Name Stack commands are only used when in Selection mode.
As mentioned above, this is very easy to implement.But there's a big drawback,namely the maximum size of the Name Stack.Every OpenGL implementation must at least offer a Name Stack size of 64 names (My GeForce4 Ti4400 with NVidia's Detonator 28.80 offers a Name Stack depth of 128 names).This is not enough,especially for complex scenes.If you need to retrieve information on more objects than your Name Stack is deep,then you'll have to use another method like casting a ray on where your mousecursor is.

Step 2 - Finding the selected objects
Now we come to the core of the matter : determing which object is under the cursor.This is a little bit more complicated, but take a look at the procedure below and it's description :

function TForm1.GetSelectBufferHit : Integer;
var
 SelectBuffer : array[0..512] of TGLUInt;
 Viewport     : TVector4i;
 Hits,i       : Integer;
 HitZValue    : TGLUInt;
 Hit          : TGLUInt;
begin
ActivateRenderingContext(FDC, FRC);
glGetIntegerv(GL_VIEWPORT, @viewport);
glSelectBuffer(512, @SelectBuffer);
glRenderMode(GL_SELECT);
glInitNames;
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix;
glLoadIdentity;
gluPickMatrix(mx, viewport[3]-my, 1.0, 1.0, viewport);
gluPerspective(45.0, ClientWidth/ClientHeight, 0.1, 1000);
DrawScene;
glMatrixMode(GL_PROJECTION);
glPopMatrix;
Hits := glRenderMode(GL_RENDER);
DeactivateRenderingContext;
Hit       := High(TGLUInt);
HitZValue := High(TGLUInt);
for i := 0 to Hits-1 do
 if SelectBuffer[(i*4)+1] < HitZValue then
  begin
  Hit       := SelectBuffer[(i*4)+3];
  HitZValue := SelectBuffer[(i*4)+1];
  end;
Result := Hit;
end;



If you look at the var section of this function you will see that you need an Array of TGLUint called SelectBuffer.This will be the place where OpenGL will store all hits with their z-values.Another drawback of the OpenGL selection mode is that you don't know how many hits you will get, so you must use a fixed arraysize.But this isn't very critical as your array is local.
After saving our current Viewport we use the glSelectBuffer(size : Integer;buffer : PGLUint) command to tell OpenGL the size of our SelectBuffer and where it's residing in memory.
Now we switch from rendering mode to selection mode using the glRenderMode(mode : cardinal) command with GL_SELECT, init the Name Stack and push one name on it.
Then it's time to create a new projection matrix.As we want the user be able to pick an object by clicking on it, we create a new matrix with the gluPickMatrix(x, y, width, height: TGLdouble; viewport: TVector4i) command.This new matrix is centered at our cursorposition (x,y) and is 1x1 Pixel (width,height).
Now we will again have to set our perspective's fov, aspect and zfar values before we draw our scene again.But this time our scene will be drawn in selection mode and all commands dealing with the name stack will get executed.
After that we restore our projectionmatrix and change to rendering mode.But now, the glRender command returns the number of hits, that we will store for later use.
As you now have the number of hits, it's easy to get the object the user has picked.As you should know, this is the object with the lowest z-value (i.e. nearest to the user).
To find this object, you'll have to know about the contents of the selectionbuffer.The buffer consits of hit records and a hit records consists of the following items in order :

- Number of names on the stack at the time the hit occured
- Minimum z-coordinate of all vertices of the picked primitve
- Maximum z-coordinate of all vertices of the picked primitve
- Contents of the Name Stack at the time of the hit, with the bottommost element first

The only entry you need is the second one that'll provide information on the lowest z-coordinate and the last one that will tell you which object was picked.These can be accessed via SelectBuffer[(n*4)+1] and SelectBuffer[(n*4)+3]where n is the hit you're currently processing.This is what the above procedure does in the loop at it's end.If an object's z-value is lower than the stored z-value we replace it and store the name of the object.
If the loop is through, we have the name of the object which was under the mouse cursor and was nearest to the viewer!

The sample application
I created a little sample app that simulates a small universe.If you click on one of the planets, you'll see which one you clicked on.



Download

Download the selection demo including the sourcecode