Почему я всегда увеличиваю масштаб при выборе? (OpenGL)

У меня есть следующий код:

#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;
}

Простите меня за длину. Я пытаюсь заставить работать выбор, чтобы я мог выбирать один объект за раз, чтобы делать что-то. Судя по моим распечаткам, он работает так, как я ожидаю, за исключением одного препятствия: когда вы выбираете объект, вы в конечном итоге сильно увеличиваете масштаб, в основном внутри выбранной формы. Я прочесал свой код и внес некоторые изменения/отладку, но не могу найти проблемную строку. Может ли кто-нибудь указать мне в правильном направлении, почему выбор дает такое странное поведение?


person Rome_Leader    schedule 27.11.2014    source источник
comment
Вероятно, потому что вы только нажимаете и выталкиваете матрицу представления модели в void pick( int button, int state, int x, int y ). Вы заменяете матрицу проекции, так как не удосужились вызвать glMatrixMode (GL_PROJECTION) перед помещением текущей матрицы на вершину стека.   -  person Andon M. Coleman    schedule 27.11.2014
comment
Немного запутался в том, что вы имеете в виду. В функции выбора я вызываю оба. Прочитав, у меня сложилось впечатление, что мне нужно было вспомнить матрицу просмотра модели после настройки проекции (также сделанной в пикировке). Если я выталкиваю после вызова перспективы и нажимаю новую матрицу для просмотра модели, я получаю то же поведение.   -  person Rome_Leader    schedule 27.11.2014
comment
Вы поместили в стек матрицу представления модели, а не матрицу проекции (поскольку текущий режим матрицы — GL_MODELVIEW в начале pick (...)). Конечным результатом является то, что после возврата функции вы извлекаете матрицу представления модели из стека, но матрица проекции просачивается.   -  person Andon M. Coleman    schedule 27.11.2014
comment
Аааа, понял! Спасибо за дальнейшее объяснение!   -  person Rome_Leader    schedule 28.11.2014


Ответы (1)


Ваша функция pick изменяет матрицу проекции с помощью gluperspective (чтобы увеличить область выбора), но не может восстановить предыдущее состояние матрицы: это можно легко исправить, обернув рисующую часть функции набором glPushMatrix и glPopMatrix в то время как в режиме матрицы GL_PROJECTION.

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();
person didierc    schedule 27.11.2014
comment
Спасибо! Очень полезно, как и исходный комментарий к посту. Теперь мне просто нужно изучить свой код, чтобы выяснить, почему каждая фигура получает 1 за свое имя! - person Rome_Leader; 28.11.2014
comment
Спасибо, @AndonM.Coleman заслуживает как минимум столько же похвал, сколько и я, поскольку он опубликовал ответ, пока я был занят опробованием вашего кода (интересно, кстати). - person didierc; 28.11.2014