copenglselectionpickingmouse-picking

Why Do I Always Zoom in When Picking? (OpenGL)


I have the following code:

#define GLUT_DISABLE_ATEXIT_HACK

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

GLfloat ctrlptsBezier[4][4][3] =
{
      {
        {-2.0, -2.0, 1.0},
        {-0.5, -2.0, 0.0},
        {0.5, -2.0, 0.0},
        {2.0, -2.0, 1.0}},
      {
        {-2, -0.5, -1.0},
        {-0.5, -0.5, 0.0},
        {0.5, -0.5, 0.0},
        {2.0, -0.5, 0.0}},
      {
        {-2, 0.5, 0.0},
        {-0.5, 0.5, -2.0},
        {0.5, 0.5, 0.0},
        {2.0, 0.5, 0.0}},
      {
        {-2.0, 2.0, 1.0},
        {-0.5, 2.0, 0.0},
        {0.5, 2.0, 0.0},
        {2.0, 2.0, 1.0}}
};

int uSteps = 30;
int vSteps = 30;

bool shade = false;

GLfloat black [] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat blue[] = { 0.0, 0.0, 1.0, 1.0 };
GLfloat green[] = { 0.0, 1.0, 0.0, 1.0 };
GLfloat peach[] = { 1.0, 0.5, 0.5, 1.0 };
GLfloat purple[] = { 0.5, 0.0, 0.5, 1.0 };
GLfloat red[] = { 1.0, 0.0, 0.0, 1.0 };
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat yellow[] = { 1.0, 1.0, 0.0, 1.0 };

//Initial eye/viewpoint coords.
double xPos = -20.0, zPos = -20.0;
double eyeHeight = 4.5;
double eyeIncline = -0.5;

double theta = 0.0; //Rotation angle in rads

//Movement params (camera)
double posIncr = 0.25;
double thetaIncr = 0.1;

#define BUFSIZE 512 //Pick buffer size

void setObjLight( bool s )
{
int shadeNumber = GL_FLAT;

if ( shade == true )
  shadeNumber = GL_SMOOTH;

glShadeModel(shadeNumber);
}

void setLights(void)
{
setObjLight(shade);
}

void drawRoom(void)
{

GLfloat floor_color[] = { 0.5, 0.5, 0.5, 1.0 }; //Grey floor
GLfloat blue_wall[] = { 0.0, 0.0, 1.0, 1.0 };
GLfloat green_wall[] = { 0.0, 1.0, 0.0, 1.0 };
GLfloat purple_wall[] = { 0.5, 0.0, 0.5, 1.0 };

//FLOOR

glMaterialfv(GL_FRONT, GL_DIFFUSE, floor_color);
glBegin(GL_POLYGON);
  glNormal3f(0.0, 1.0, 0.0);
  glVertex3f(-25.0, 0.0, -25.0);
  glVertex3f( 25.0, 0.0, -25.0);
  glVertex3f( 25.0, 0.0,  25.0);
  glVertex3f(-25.0, 0.0,  25.0);
glEnd();

//WALLS

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, blue_wall);

glPushMatrix();
glTranslatef(0.0, 0.0, 25.0);
glBegin(GL_POLYGON);
  glNormal3f(0.0, 1.0, 0.0);
  glVertex3f(-25.0,-25.0,0.0);
  glVertex3f(25,-25.0,0);
  glVertex3f(25, 25, 0);
  glVertex3f(-25, 25, 0);
glEnd();
glPopMatrix();

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, green_wall);

  glPushMatrix();
  glTranslatef(0.0, 0.0, -25.0);
  glBegin(GL_POLYGON);
     glNormal3f(0.0, 1.0, 0.0);
     glVertex3f(-25.0,-25.0,0.0);
     glVertex3f(25,-25.0,0);
     glVertex3f(25, 25, 0);
     glVertex3f(-25, 25, 0);
 glEnd();
 glPopMatrix();

 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, purple_wall);

   glPushMatrix();
   glTranslatef(25.0, 0.0, 0.0);
   glRotatef(90.0, 0.0, 1.0, 0.0);
   glBegin(GL_POLYGON);
        glNormal3f(0.0, 1.0, 0.0);
        glVertex3f(-25.0,-25.0,0.0);
        glVertex3f(25,-25.0,0);
        glVertex3f(25, 25, 0);
        glVertex3f(-25, 25, 0);
   glEnd();
   glPopMatrix();

}

void drawShapes()
{
//SPHERE

glPushMatrix();

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, blue);
glNormal3f(0.0, 1.0, 0.0);
glTranslatef(0.0, 2.0, 0.0);
glLoadName(1);
glutSolidSphere (2.0, 20, 16);

glPopMatrix();
glFlush();

//CONE

glPushMatrix();

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, purple);
glNormal3f(0.0, 1.0, 0.0);
glTranslatef(5.0, 0.0, 0.0);
glRotatef(-90.0, 1.0, 0.0, 0.0);
glLoadName(2);
glutSolidCone (2.0, 3, 20, 16);

glPopMatrix();
glFlush();

glLoadName(0);

}

void drawBezier()
{
glEnable(GL_MAP2_VERTEX_3);
glPushMatrix();

glTranslatef(10, 2, 15);

glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlptsBezier[0][0][0]);
glMapGrid2f(uSteps, 0.0, 1.0, vSteps, 0.0, 1.0);
glEvalMesh2(GL_FILL, 0, uSteps, 0, vSteps);

glPopMatrix();

glPushMatrix();
glTranslatef(13, 2, 18);
glRotatef(-90, 0.0, 1.0, 0.0);

glMaterialfv(GL_FRONT, GL_DIFFUSE, red);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlptsBezier[0][0][0]);
glMapGrid2f(uSteps, 0.0, 1.0, vSteps, 0.0, 1.0);
glEvalMesh2(GL_FILL, 0, uSteps, 0, vSteps);

glPopMatrix();

glPushMatrix();
glTranslatef(7, 2, 18);
glRotatef(90, 0.0, 1.0, 0.0);

glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlptsBezier[0][0][0]);
glMapGrid2f(uSteps, 0.0, 1.0, vSteps, 0.0, 1.0);
glEvalMesh2(GL_FILL, 0, uSteps, 0, vSteps);

glPopMatrix();

glPushMatrix();
glTranslatef(10, 2, 21);
glRotatef(180, 0.0, 1.0, 0.0);

glMaterialfv(GL_FRONT, GL_DIFFUSE, peach);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlptsBezier[0][0][0]);
glMapGrid2f(uSteps, 0.0, 1.0, vSteps, 0.0, 1.0);
glEvalMesh2(GL_FILL, 0, uSteps, 0, vSteps);

glPopMatrix();

glPushMatrix();
glTranslatef(10, 5, 18);
glRotatef(90.0, 1.0, 0.0, 0.0);

glMaterialfv(GL_FRONT, GL_DIFFUSE, black);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlptsBezier[0][0][0]);
glMapGrid2f(uSteps, 0.0, 1.0, vSteps, 0.0, 1.0);
glEvalMesh2(GL_FILL, 0, uSteps, 0, vSteps);

glPopMatrix();
//Sphere for filler
glPushMatrix();
glTranslatef(10, 2, 18);
glRotatef(90.0, 1.0, 0.0, 0.0);

glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
glutSolidSphere(3.0, 20, 16);

glPopMatrix();

glFlush();

}

void drawScene(void)
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

double atx = xPos + cos(theta);
double atz = zPos + sin(theta);
double atHeight = eyeHeight + eyeIncline;
gluLookAt(xPos, eyeHeight, zPos, atx, atHeight, atz, 0.0, 1.0, 0.0);

glPushMatrix();
setLights();

drawRoom();

drawShapes();

glLoadName(3);
drawBezier();

glPopMatrix();
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawScene();
glutSwapBuffers();
}

void setProjection(void)
{
gluPerspective(60.0, 1.0, 0.1, 100.0);
}

void init(void)
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LEQUAL);
glEnable(GL_LIGHTING);
glEnable(GL_FLAT); //Flat shading initially
glShadeModel(GL_FLAT);
//LIGHTING
       GLfloat specular[]= {1.0, 1.0, 1.0, 1.0};
       GLfloat diffuse[]= {1.0, 1.0, 1.0, 1.0};
       GLfloat ambient[]= {1.0, 1.0, 1.0, 1.0};
       GLfloat shininess= {100.0};
       GLfloat light_ambient[]= {0.0, 0.0, 0.0, 1.0};
       GLfloat light_diffuse[]= {1.0, 1.0, 1.0, 1.0};
       GLfloat light_specular[]= {1.0, 1.0, 1.0, 1.0};
       GLfloat light_position[]= {10.0, 10.0, 10.0, 0.0};

       glLightfv(GL_LIGHT0, GL_POSITION, light_position);
       glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
       glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
       glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

       glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
       glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
       glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
       glMaterialf(GL_FRONT, GL_SHININESS, shininess);
// /LIGHTING
glEnable(GL_NORMALIZE);
glClearColor (0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
}

void specialKey(int k, int x, int y) //Camera movement commands with arrow keys
{
switch (k) {
  case GLUT_KEY_UP:
     xPos += posIncr * cos(theta);
     zPos += posIncr * sin(theta);
     break;
  case GLUT_KEY_DOWN:
     xPos -= posIncr * cos(theta);
     zPos -= posIncr * sin(theta);
     break;
  case GLUT_KEY_LEFT:
     theta -= thetaIncr;
     break;
  case GLUT_KEY_RIGHT:
     theta += thetaIncr;
     break;
  case GLUT_KEY_PAGE_UP:
     eyeIncline += 0.5;
     break;
  case GLUT_KEY_PAGE_DOWN:
     eyeIncline -= 0.5;
     break;
  case GLUT_KEY_HOME:
     eyeHeight += 0.5;
     break;
  case GLUT_KEY_END:
     eyeHeight -= 0.5;
     break;
  default:
     return;
  }
glutPostRedisplay();
}

void key(unsigned char k, int x, int y)
{
if ( k == 27 ) //ESC to close
  exit(0);

glutPostRedisplay();
}

void processHits(GLint hits, GLuint buffer[])
{
unsigned int j;
GLuint names, *ptr;
float z1, z2;

printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;

shade = !shade;

setLights();
glutPostRedisplay();

for (int i = 0; i < hits; i++)
{   /*  for each hit  */
      names = *ptr;
      printf (" number of names for hit = %d\n", names); ptr++;
      z1 =  (float) *ptr/0x7fffffff; ptr++;
      z2 =  (float) *ptr/0x7fffffff; ptr++;
      printf("  z1 is %g;",z1);
      printf(" z2 is %g\n", z2);
      printf ("   the name is ");

      for (j = 0; j < names; j++) { /*  for each name */

         printf ("%d ", *ptr); ptr++;

      }
      printf ("\n");
  }
}

void pick( int button, int state, int x, int y )
{
GLuint selectBuf[BUFSIZE];
GLint hits;
GLint viewport[4];

if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN) {

  return;

}

glSelectBuffer(BUFSIZE, selectBuf);
glRenderMode(GL_SELECT);


glPushMatrix();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glGetIntegerv(GL_VIEWPORT, viewport);
gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y), 5.0, 5.0, viewport); //5x5 pixel area    around the mouse for selecting
gluPerspective(75, 1.0, 0.1, 100); //NEED

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glInitNames();
glPushName(0);

drawScene();

glPopMatrix();
glFlush();

hits = glRenderMode(GL_RENDER); //# of hits assigned
processHits(hits, selectBuf);

}

void reshape(int w, int h){

glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(75, w/h,1,150);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutCreateWindow("Room");
init();
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutSpecialFunc(specialKey);
glutReshapeFunc(reshape);
glutMouseFunc(pick);
glutMainLoop();
return 0;
}

Forgive me for the length. I am trying to get picking to work so that I can select one object at a time to do things with. According to my print outs, it is functioning as I expect, except for one hurdle: When you pick an object, you end up extremely zoomed in, basically inside the selected shape. I have combed my code, and made some changes/debugging efforts, but I can't find the problem line. Can anyone point me in the right direction as to why picking gives this strange behavior?


Solution

  • Your function pick change the projection matrix with gluperspective (to zoom on the picking area), but it fails to restore the previous matrix state: it can be easily fixed by wrapping the the drawing part of the function with a set of glPushMatrix and glPopMatrix while in matrix GL_PROJECTION mode.

    void pick( int button, int state, int x, int y )
    {
      GLuint selectBuf[BUFSIZE];
      GLint hits;
      GLint viewport[4];
    
     if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN) {
         return;
      }
    
      glSelectBuffer(BUFSIZE, selectBuf);
      glRenderMode(GL_SELECT);
    
    
      glPushMatrix();
      glMatrixMode(GL_PROJECTION);
      glPushMatrix(); // save projection settings
      glLoadIdentity();
      glGetIntegerv(GL_VIEWPORT, viewport);
      gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y), 5.0, 5.0, viewport); //5x5 pixel area around the mouse for selecting
    
      gluPerspective(75, 1.0, 0.1, 100); //NEED
    
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
    
      glInitNames();
      glPushName(0);
    
      drawScene();
      // restore previous projection settings
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);   
      glPopMatrix();
      glFlush();