3D picking problem - experts please help!

Hi,

I’ve put together some code based on the 3D picking example from http://www.gametutorials.com/Tutorials/opengl/OpenGL_Pg3.htm (some excellent tutorials for OpenGl using .NET). The problem is each time I click an object it seems to think all the objects have been selected. I’ve spent some time going over this and can’t see what I’ve done wrong. Please help! Thanks.

#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>

	int sphereRed = 101, sphereGreen = 102, sphereBlue = 103, sphereBlack = 104;
	int oldX, oldY, width, height, mode, clickReleased = 0;
	float rotateSceneX = 0, rotateSceneY = 0, rotateSceneZ = 0, sceneSpinX = 0, sceneSpinY = 0, sceneSpinZ = 0;
	float zoom = 1.4f, ratio = 1.5f;

		void render() {

			width = glutGet(GLUT_WINDOW_WIDTH);
			height = glutGet(GLUT_WINDOW_HEIGHT);

			glInitNames();
			
			glEnable(GL_COLOR_MATERIAL);
			glViewport(0, 0, width, height);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluOrtho2D(0, width, 0, height);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			glViewport(0, 0, width, height);

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			glOrtho(-ratio-zoom, ratio+zoom, -ratio -zoom, ratio+zoom, -10, 25);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			gluLookAt(1.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

			//Main Rotation and Translation
			glRotatef(rotateSceneX, 1.0f, 0.0f, 0.0f);
			glRotatef(rotateSceneY, 0.0f, 1.0f, 0.0f);
			glRotatef(rotateSceneZ, 0.0f, 0.0f, -1.0f);

			if (mode == GL_SELECT) {
				glPushName(sphereRed);
			}
			glPushMatrix();
			glColor3f(1.0f,0.0f,0.0f);
			glutSolidSphere(0.5, 10, 10);
			glPopMatrix();
			if (mode == GL_SELECT) {
				glPopName();
			}

			if (mode == GL_SELECT) {
				glPushName(sphereGreen);
			}
			glPushMatrix();
			glColor3f(0.0f,1.0f,0.0f);
			glTranslatef(3.2f, 0.1f, 0.2f);
			glutSolidSphere(0.5, 10, 10);
			glPopMatrix();
			if (mode == GL_SELECT) {
				glPopName();
			}

			if (mode == GL_SELECT) {
				glPushName(sphereBlue);
			}
			glPushMatrix();
			glColor3f(0.0f,0.0f,1.0f);
			glTranslatef(1.4f, 0.0f, 0.0f);
			glutSolidSphere(0.6, 10, 10);
			glPopMatrix();
			if (mode == GL_SELECT) {
				glPopName();
			}

			if (mode == GL_SELECT) {
				glPushName(sphereBlack);
			}
			glPushMatrix();
			glColor3f(0.0f,0.0f,0.0f);
			glTranslatef(-1.2f, 0.1f, 0.6f);
			glutSolidSphere(0.5, 10, 10);
			glPopMatrix();
			if (mode == GL_SELECT) {
				glPopName();
			}
			glutSwapBuffers();
		}

		void Display() {
			mode = GL_RENDER;
			render();
		}

		void setLighting() {

			float diffuse[] = {0.4, 0.4, 0.4, 1.0};
			float specular[] = {0.5, 0.5, 1.5};
			float position[] = {0.0, 1.0, 1.0, 0.0};
			float lightVal = 1.0;

			//light model
			glEnable(GL_LIGHTING);
			glEnable(GL_LIGHT0); 
			glEnable(GL_LIGHT1); 
			glEnable(GL_DEPTH_TEST);

			glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
			glMaterialf(GL_FRONT, GL_SHININESS, 75); 
			glMaterialfv(GL_BACK, GL_SPECULAR, specular); 
			glMaterialf(GL_BACK, GL_SHININESS, 75); 
			glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); 
			glLightfv(GL_LIGHT0, GL_POSITION, position);
			glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); 
			glLightfv(GL_LIGHT1, GL_POSITION, position); 

			//glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lightVal);
		}
		
		void InitLight() {
			float lightPosition0[] = {1.0f, 1.0f, 0.0f, 0.0f};
			float lightPosition1[] = {1.0f, -1.0f, -1.0f, 0.0f};

			glLightModeli(GL_LIGHT_MODEL_AMBIENT, GL_TRUE);
			glLightfv(GL_LIGHT0, GL_POSITION, lightPosition0);
			glLightfv(GL_LIGHT1, GL_POSITION, lightPosition1);
			glEnable(GL_LIGHTING);
			glEnable(GL_LIGHT0);
			glEnable(GL_LIGHT1);
			glEnable(GL_DEPTH_TEST);
			glDisable(GL_CULL_FACE);
			setLighting();
		}
		
		
		void Keyboard(unsigned char key, int x, int y) {
			switch(key) {
				case 27:   //esc
					exit(0);
					break;
				case 97:  //a
					zoom+=0.1f;
					break;
				case 122:  //z
					zoom-=0.1f;
					break;
				default:
					break;
			}
			glutPostRedisplay();
		}
		
		
		void Motion(int x, int y) {
			if (clickReleased == 0) {
				rotateSceneZ = y - oldY;
				rotateSceneY = x - oldX;
				rotateSceneZ += sceneSpinZ;
				rotateSceneY += sceneSpinY;
			}
			glutPostRedisplay();
		}

		void Reshape(int width, int height) {
			glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
		}
		
		int retrievePickedObject(int x, int y) {

			int objectsFound = 0;
			int viewportCoords[4];
			int i = 0;
			unsigned int selectBuffer[32];				
														
			glSelectBuffer(32, selectBuffer);
			glGetIntegerv(GL_VIEWPORT, viewportCoords);

			glMatrixMode(GL_PROJECTION);
			glPushMatrix();
				glRenderMode(GL_SELECT);
				glLoadIdentity();
				glInitNames();
				gluPickMatrix(x, viewportCoords[3] - y, 2, 2, viewportCoords);
				gluOrtho2D(0, width, 0, height);
				glOrtho(-ratio-zoom, ratio+zoom, -ratio -zoom, ratio+zoom, -10, 25);
				mode = GL_SELECT;
				glMatrixMode(GL_MODELVIEW);						
				render();
				objectsFound = glRenderMode(GL_RENDER);
				glMatrixMode(GL_PROJECTION);
			glPopMatrix();

			if (objectsFound > 0) {		
				unsigned int lowestDepth = selectBuffer[1];
				int selectedObject = (int)selectBuffer[3];

				for(i = 1; i < objectsFound; i++) {
					if(selectBuffer[(i * 4) + 1] < lowestDepth) {
						lowestDepth = (unsigned int)selectBuffer[(i * 4) + 1];
						selectedObject = (int)selectBuffer[(i * 4) + 3];
					}
				}
				return selectedObject;
			}

			// We didn't click on any objects so return 0
			return 0;			


		}

		void Mouse(int button, int state, int x, int y) {

			//find object picked
			int obj = 0;
			obj = retrievePickedObject(x, y);

			//x and y track mouse movement on screen
			oldX = x;
			oldY = y;
			if (state == 1) {	//click released
				sceneSpinX = rotateSceneX;
				sceneSpinY = rotateSceneY;
				sceneSpinZ = rotateSceneZ;
				
				if (sceneSpinX >= 360.0f) {
					sceneSpinX = 0.0f;
				}
				if (sceneSpinY >= 360.0f) {
					sceneSpinY = 0.0f;
				}
				if (sceneSpinZ >= 360.0f) {
					sceneSpinZ = 0.0f;
				}

				clickReleased = 1;
			}
			else {
				clickReleased = 0;
			}
			glutPostRedisplay();
		}

		int main(int argc, char **argv) {

			glutInit(&argc, argv);		
			glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB);
			glutInitWindowSize(800, 800);
			glutInitWindowPosition(50, 50);
			glutCreateWindow("Multiple Viewports");

			glutDisplayFunc(Display);
			glutKeyboardFunc(Keyboard);
			glutMotionFunc(Motion);
			glutMouseFunc(Mouse);
			glutReshapeFunc(Reshape);

			InitLight();
			glutMainLoop();
		
		return 0;

	}

		




	



  

for one thing, your picking rectangle is the size of the viewport. try calling this, right before glOrtho2D:

gluPickMatrix(mouseX, height - 1 - mouseY, mouseWidth, mouseHeight, viewport);

:slight_smile:

i see it in yourt code, but it’s in the wrong function :slight_smile:

your retrievePickedObject function should just call render, then process the results. you really dont want matrix stuff in that function. just leave all that to render.

I’ve given this a go but it doesnt make any difference - it’s still returning all four objects each time. For anyone who’s interested, you can save the code as a .c file, compile and run as it is to witness this strange behaviour.

can you post the changed code?

Here is the updated code:

int retrievePickedObject(int x, int y) {

int objectsFound = 0;
int viewportCoords[4];
int i = 0;
unsigned int selectBuffer[32];
		
	glSelectBuffer(32, selectBuffer);
	glGetIntegerv(GL_VIEWPORT, viewportCoords);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
		glRenderMode(GL_SELECT);
		glLoadIdentity();
		glInitNames();
		//gluPickMatrix(x, viewportCoords[3] - y, 2, 2, viewportCoords);
		gluPickMatrix(x, height - 1 - y, 2, 2, viewportCoords);
		//gluOrtho2D(0, width, 0, height);
		//glOrtho(-ratio-zoom, ratio+zoom, -ratio -zoom, ratio+zoom, -10, 25);
		mode = GL_SELECT;
		glMatrixMode(GL_MODELVIEW);						
		render();
		objectsFound = glRenderMode(GL_RENDER);
		glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	if (objectsFound > 0) {		
		unsigned int lowestDepth = selectBuffer[1];
		int selectedObject = (int)selectBuffer[3];

		for(i = 1; i < objectsFound; i++) {
			if(selectBuffer[(i * 4) + 1] < lowestDepth) {
				lowestDepth = (unsigned int)selectBuffer[(i * 4) + 1];
				selectedObject = (int)selectBuffer[(i * 4) + 3];
			}
		}
		return selectedObject;
	}

        // We didn't click on any objects so return 0
	return 0;			

}

Getting rid of the MatrixMode calls makes no difference. It’s still returning four objects. :confused:

here’s what im thinking this function should do:

// no matrix stuff here at all
// keep things simple and logical
 
glSelectBuffer();
glRenderMode(GL_SELECT);
glInitNames();
render(..., GL_SELECT); 
int num = glRenderMode( GL_RENDER );
 
// now process hits...

now, in render(…, mode):

...
 
glMatrixMode(GL_PROJECTION);
if( mode == GL_SELECT )
    gluPickMatrix();
glOrtho2D();
glMatrixMode(GL_MODELVIEW);
 
...
//set all other matrices normally and draw as
//usual, pushing names only if in select mode
 

the only difference in modes from render’s point of view is the pick matrix and pushing names - thats it :slight_smile:

oh, i forgot to load an identity in my projection matrix above before the pick matrix. im sure you would have caught that anyway.

i hate to say it, but your hit processing code needs work too.

for each record you have in order:

  1. number of names in stack at time of hit
  2. min and max depth values
  3. name stack contents at time of hit, bottom up

when you parse the hit record keep this in mind. i wouldn’t hard-code anything in terms of an offset. each record is variable in length.

Thanks for the help, I seem to have sussed it now.