I populate my 3D-space with spheres that contain smaller spheres. When the spheres doesn’t have anything inside of them, my selection routine works fine. But when my spheres contain smaller spheres I don’t get any selections. I think that this has to do with glPushName and glPushMatrix but I don’t know how.
See pseudocode for the drawing method below.
for (int i = 0; i < counter; i++) {
// When I comment these lines inside loop everything works
gl.glPushMatrix();
// draw smaller sphere
gl.glPopMatrix();
}
glPushName(uniqueId)
gl.glPushMatrix()
// Draw larger sphere
gl.glPopMatrix()
gl.glPopName();
Selection routines
private int[] setupSelection() {
int objectsFound = 0; // This will hold the amount of objects clicked
int viewportCoords[] = new int [4]; // We need an array to hold view port coordinates
int selectBuffer[] = new int [32];
gl.glSelectBuffer(32, selectBuffer); // Setup selection buffer to accept object ID's
gl.glGetIntegerv(GL.GL_VIEWPORT, viewportCoords); // Get the current view port coordinates
gl.glMatrixMode(GL.GL_PROJECTION); // We want to now effect projection matrix
gl.glPushMatrix(); // We push on a new matrix so we don't effect our 3D projection
// This makes it so it doesn't change the frame buffer if we render into it, instead,
// a record of the names of primitives that would have been drawn if the render mode was
// GL_RENDER are now stored in the selection array (selectBuffer).
gl.glRenderMode(GL.GL_SELECT); // Allows us to render the objects, but not change the frame buffer
gl.glLoadIdentity(); // Reset our projection matrix
// gluPickMatrix allows us to create a projection matrix that is around our
// cursor. This basically only allows rendering in the region that we specify.
// If an object is rendered into that region, then it saves that objects ID for us
// The first 2 parameters are the X and Y position to start from, then the next 2
// are the width and height of the region from the starting point. The last parameter is
// of course our view port coordinates. You will notice we subtract "y" from the
// BOTTOM view port coordinate. We do this to flip the Y coordinates around. The 0 y
// coordinate starts from the bottom, which is opposite to window's coordinates.
// We also give a 2 by 2 region to look for an object in. This can be changed to preference.
glu.gluPickMatrix(selectedPoint.getX(), viewportCoords[3] - selectedPoint.getY(),
2, 2, viewportCoords);
// Next, we just call our normal gluPerspective() function, exactly as we did on startup.
// This is to multiply the perspective matrix by the pick matrix we created up above.
glu.gluPerspective(45.0f,(float)HEIGTH/(float)WIDTH,0.1f,150.0f);
gl.glMatrixMode(GL.GL_MODELVIEW); // Go back into our model view matrix
return selectBuffer;
}
private int getSelection(int[] selectBuffer) {
int objectsFound;
// If we return to our normal render mode from select mode, glRenderMode returns
// the number of objects that were found in our specified region (specified in gluPickMatrix())
// Return to render mode and get the number of objects found
objectsFound = gl.glRenderMode(GL.GL_RENDER);
gl.glMatrixMode(GL.GL_PROJECTION); // Put our projection matrix back to normal.
gl.glPopMatrix(); // Stop effecting our projection matrix
gl.glMatrixMode(GL.GL_MODELVIEW); // Go back to our normal model view matrix
// PHEW! That was some stuff confusing stuff. Now we are out of the clear and should have
// an ID of the object we clicked on. objectsFound should be at least 1 if we found an object.
System.out.println("Objects found : "+objectsFound);
if (objectsFound > 0) {
// If we found more than one object, we need to check the depth values
// of all the objects found. The object with the LEAST depth value is
// the closest object that we clicked on. Depending on what you are doing,
// you might want ALL the objects that you clicked on (if some objects were
// behind the closest one), but for this tutorial we just care about the one
// in front. So, how do we get the depth value? Well, The selectionBuffer
// holds it. For every object there is 4 values. The first value is
// "the number of names in the name stack at the time of the event, followed
// by the minimum and maximum depth values of all vertices that hit since the
// previous event, then followed by the name stack contents, bottom name first." - MSDN
// The only ones we care about are the minimum depth value (the second value) and
// the object ID that was passed into glLoadName() (This is the fourth value).
// So, [0 - 3] is the first object's data, [4 - 7] is the second object's data, etc...
// Be carefull though, because if you are displaying 2D text in front, it will
// always find that as the lowest object. So make sure you disable text when
// rendering the screen for the object test. I use a flag for RenderScene().
// So, lets get the object with the lowest depth!
// Set the lowest depth to the first object to start it off.
// 1 is the first object's minimum Z value.
// We use an unsigned int so we don't get a warning with selectBuffer below.
int lowestDepth = selectBuffer[1];
// Set the selected object to the first object to start it off.
// 3 is the first object's object ID we passed into glLoadName().
int selectedObject = selectBuffer[3];
// Go through all of the objects found, but start at the second one
for (int i = 1; i < objectsFound; i++) {
// Check if the current objects depth is lower than the current lowest
// Notice we times i by 4 (4 values for each object) and add 1 for the depth.
if (selectBuffer[(i * 4) + 1] < lowestDepth) {
// Set the current lowest depth
lowestDepth = selectBuffer[(i * 4) + 1];
// Set the current object ID
selectedObject = selectBuffer[(i * 4) + 3];
}
}
System.out.println("Selected object : "+selectedObject);
return selectedObject;
}
return 0;
}
Best Regards,
Sami