Функция paintGL вызывается всякий раз, когда необходимо перерисовать виджет. Это напоминает функцию QWidget::paintEvent, но вместо функций класса QPainter здесь мы используем функции библиотеки OpenGL. Реальное рисование выполняется закрытой функцией draw.
В функции draw мы рисуем тетраэдр, учитывая повороты по осям x, у и z, а также цвета в массиве faceColors. Везде вызываются стандартные функции библиотеки OpenGL, за исключением вызова qglColor. Вместо этого мы могли бы использовать одну из функций OpenGL — glColor3d или glIndex — в зависимости от используемого режима.
08 GLfloat dy = GLfloat(event->y - lastPos.y) / height;
09 if (event->buttons & Qt::LeftButton) {
10 rotationX += 180 * dy;
11 rotationY += 180 * dx;
12 updateGL;
13 } else if (event->buttons & Qt::RightButton) {
14 rotationX += 180 * dy;
15 rotationZ += 180 * dx;
16 updateGL;
17 }
18 lastPos = event->pos;
19 }
Функции класса QWidget mousePressEvent и mouseMoveEvent переопределяются, чтобы разрешить пользователю
поворачивать изображение щелчком мышки и ее перемещением. Левая кнопка мышки позволяет пользователю поворачивать вокруг осей x и у, а правая кнопка мышки — вокруг осей x и z.
После модификации переменных rotationX и rotationY или rotationZ мы вызываем функцию updateGL для перерисовки сцены.
05 QColor color = QColorDialog::getColor(faceColors[face], this);
06 if (color.isValid) {
07 faceColors[face] = color;
08 updateGL;
09 }
10 }
11 }
Функция mouseDoubleClickEvent класса QWidget переопределяется, чтобы разрешить пользователю устанавливать цвет грани тетраэдра с помощью двойного щелчка. Мы вызываем закрытую функцию faceAtPosition для определения той грани, на которой находится курсор (если он вообще находится на какой-нибудь грани). При двойном щелчке по грани тетраэдра мы вызываем функцию QColorDialog::getColor для получения нового цвета для этой грани. Затем мы обновляем массив цветов faceColors новым цветом, и мы вызываем функцию updateGL для перерисовки экрана.
01 int Tetrahedron::faceAtPosition(const QPoint &pos)
02 {
03 const int MaxSize = 512;
04 GLuint buffer[MaxSize];
05 GLint viewport[4];
06 glGetIntegerv(GL_VIEWPORT, viewport);
07 glSelectBuffer(MaxSize, buffer);
08 glRenderMode(GL_SELECT);
09 glInitNames;
10 glPushName(0);
11 glMatrixMode(GL_PROJECTION);
12 glPushMatrix;
13 glLoadIdentity;
14 gluPickMatrix(GLdouble(pos.x),
15 GLdouble(viewport[3] - pos.y),
16 5.0, 5.0, viewport);
17 GLfloat x = GLfloat(width) / height;
18 glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
19 draw;
20 glMatrixMode(GL_PROJECTION);
21 glPopMatrix;
22 if (!glRenderMode(GL_RENDER))
23 return -1;
24 return buffer[3];
25 }
Функция faceAtPosition возвращает номер грани для заданной точки виджета или —1, если данная точка не попадает на грань. Программный код этой функции, выполненной с помощью средств OpenGL, немного сложен. Фактически мы переводим работу в режим GL_SELECT, чтобы воспользоваться возможностями OpenGL по идентификации элементов изображения, и затем получаем номер грани (ее «имя») из записи нажатия OpenGL.