PDA

View Full Version : 3D picking problem - experts please help!



heygirls_uk
08-09-2004, 10:42 AM
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(&amp;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;

}

08-09-2004, 11:50 AM
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);:)

08-09-2004, 11:53 AM
i see it in yourt code, but it's in the wrong function :)

08-09-2004, 11:56 AM
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.

heygirls_uk
08-09-2004, 04:18 PM
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.

08-09-2004, 11:29 PM
can you post the changed code?

heygirls_uk
08-09-2004, 11:53 PM
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:

08-10-2004, 11:19 AM
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

08-10-2004, 11:26 AM
the only difference in modes from render's point of view is the pick matrix and pushing names - thats it :)

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

08-10-2004, 11:41 AM
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.

heygirls_uk
08-13-2004, 05:24 AM
Thanks for the help, I seem to have sussed it now.