Planning the Spontaneous

It's more than just a blueprint.

enum Pick { Me, Me., Me! }

Posted by Robert Chow on 13/10/2009

So I’ve actually done quite a bit last week – true, I stayed in far longer than is starting to render as unhealthy, but there’s not much else to do is this place you call… Devon.

Picking

I started to look into picking – using the mouse, and being able to recognize what object I am hovering over.  It’s another thing that we all take for granted, but if it’s done incorrectly, it can be a huge nightmare.  You don’t really want to have to have your mouse 10 pixels to left of the object you want to select, and what’s worse is when there is an object behind another, and you can’t get to it, no matter how hard you try.  So I looked into it, and decided to combine it with the layering problem I had.  And I think I’ve got quite a lot of it sussed.

The picking is hardware based so you get all the hardware acceleration qualities – that’s a good thing.  The whole thing is essentially 2 processes.

1) Attach a number to an object, draw it, and repeat.
2) To find what you’ve clicked on, take the mouse position, change the render mode to select mode, and it should… hopefully… return the number you’ve assigned to the object you’ve clicked on.

Simples.

The code is below.  There’s not much to show, it really is quite simple!

We first set up the picking buffer – this stores the properties of the objects that have been picked – essentially every object under the cursor, regardless of visibility.  We must also set the mode – here RenderMode is used for reference.

PickBuffer = new int[PICKING_BUFFER_SIZE];
Gl.glSelectBuffer(PICKING_BUFFER_SIZE, PickBuffer);
RenderMode = Gl.GL_RENDER;

Wehn we come to render the scene, we check to see which mode we are in.  If it is the normal render mode, then render as normal… as it suggests.  If it’s in select mode, we have to take this into consideration, and set up the appropriate functions to allow the hardware to pick up what the mouse has just clicked on.  To do this, we only add a couple of lines – one of them sets up the pick matrix, and the other initializes the name stack that will allow you to assign a name to each object.  The viewport[] is the viewport you set normally, with viewport[3] being the height.  You can be lazy and get this from OpenGL by calling Gl.glGetIntegerv(Gl.GL_VIEWPORT, viewport). The picking tolerance are the x and y values of how big the surface your mouse should cover.

if (RenderMode == Gl.GL_SELECT)
{
Glu.gluPickMatrix(xmouse, (double)(viewport[3] -ymouse), PICKING_TOLERANCE, PICKING_TOLERANCE, viewport);
Gl.glInitNames();
Gl.glPushName(0xffffffff);
}

You can then render the scene, as per usual, but each time you draw an object, load a name to it by using a one call before drawing each object.  Note that name must be an integer value.

Gl.glLoadName(name);

Now for the interesting part!  As the mouse is clicked, make sure you set the mode to select mode.  Render the scene again, using the mouse x and y values, and hopefully, after all of this, you should have the number of items under the cursor – here as numberOfHits – and the pick buffer should be filled with the information regarding these objects.  There is a small glitch with OpenGL however regarding some graphics cards – it doesn’t register unless you double pick.  That’s easy to solve – just do it twice if at first you get no numberOfHits.

RenderMode = Gl.GL_SELECT;
Gl.glRenderMode(RenderMode);
RenderScene();
RenderMode = Gl.GL_RENDER;
int numberOfHits = Gl.glRenderMode(RenderMode);

Lastly, we sort out the information in the pick buffer.  The object that is directly visible under the cursor will be the last thing in the pick buffer.  What will end in pickedID will be the object name of what you’ve just clicked on!

for(int i = 0, index = 0; i < nHits; i++ )
{
int nItems = PickBuffer[index++];
int zMin = PickBuffer[index++];
int zMax = PickBuffer[index++];

for(int j = 0; j < nitems; j++ )
{
pickedID = PickBuffer[index++];
}
}

Simples!

EDIT:  Not so simple actually.  After trying picking on different computers, they each fill the pick buffer differently – I presume this is due to the fact that this picking is hardware based.  The best way to solve this is to enable GL_CULL_FACE.  That way, anything that is behind what you’ve picked won’t get rendered, and therefore your picking buffer will only hold the one item you’ve picked on.  (I think…)

 

Advertisements

2 Responses to “enum Pick { Me, Me., Me! }”

  1. agentti said

    Hmm, am I missing something here?

    For the last bit of code, can you just not remove the outer loop, as i will always be nHits-1? Surely you know the size of the PickBuffer array so can just go directly to the end of the array and get pickedID?

    • Fraid not – I haven’t explained the full story here.
      The PickBuffer is a fixed size – it’s a buffer that has empty values when initialized, and then filled in from 0 till as far as it needs to go. It would make a lot more sense for it to just assign PickBuffer a new array, but OpenGL doesn’t work like that.
      nItems is number of names assigned to this object – you can stack names, and have subnames. This makes sense – for example, if you were to draw a body, you may have a subname arm, and then a further subname elbow (all integers of course).
      zMin and zMax are the distances within the zBuffer the object is – I’m not entirely sure on how these are calculated.
      And it’s because we don’t know how many names are assigned to the object does it mean we have to loop for pickedID to get to the explicit name assigned.
      True, the loop can be shortened a lot further if all we wanted was the very last PickedID. Might look a little something like this?!?

      int index = 0;
      for(int i = 0; i < nHits-1; ++i)
      {
      // increment by slot for nItems, zMin, zMax and names
      index += (3 + PickBuffer[index]);
      }
      pickedID = PickBuffer[index + 2 + PickBuffer[index]];

      I had to think about that one…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: