вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


Интерактивное взаимодействие с объектами сцены


В некторых приложениях проектируемый объект или моделируемое явление просто отображается на экране. Часто требуется организовать интерактивное взаимодействие с объектами (выделение, перемещение, масштабирование, вращение и т.п.) OpenGL предоставляет простой и эффективный механизм для выбора объектов (определения, какой объект отображён под курсором мыши), а их модификация осуществляется уже известными функциями. Этот механизм реализуется следующим образом: в процессе псевдовизуализации отслеживается интересующая нас точка и, если в этой точке отображается векторный или растровый примитив, то информация о нем будет предоставлена программе. Всё это происходит в следующем порядке:

  1. Создаётся специальный массив, в котором будет сохраняться информация о выбранном объекте glSelectBuffer(GLsizei size, GLuint *buffer).
  2. Перейти в режим выбора с помощью функции glRenderMode(GL_SELECT).
  3. Определить точку и область вокруг нее, которая будет отслеживаться.
  4. Сформировать стек объектов для псевдовизуализации с использоваием glInitNames(), glLoadName(), glPushName() и glPopName().
  5. Вернуться в режим отображения с помощью функции glRenderMode(GL_RENDER).

Рассмотрим простой пример, визуализации пульта управления и взаимодействия с ним. Установка системы координат осуществляется при изменении размеров окна:

void Reshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluOrtho2D(-10.,10.,-10.,10.);
}

На пульте управления будут отображаться одинаковые треугольные кнопки направленные в разные стороны. Воспользуемся механизмом списка для формирования образа кнопки:

void Init(void)
{
//Формирование списка для отображения треугольника
    glNewList(100,GL_COMPILE);
    glBegin(GL_TRIANGLES);
        glVertex2d(.0,.0);
        glVertex2d(1.,1.);
        glVertex2d(1.,-1.);
    glEnd();
    glEndList();
}

Отображение сцены осуществляется с помощью функции DrawDesk в режиме GL_RENDER.

void Draw(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    DrawDesk(GL_RENDER);
    glFlush();
    glutSwapBuffers();
}

В режиме отображения GL_SELECT осуществляются дополнительные вызовы функции glLoadName, которая позволяет присвоить идентификатор набору примитивов, который формируется после её вызова и до следующего вызова glLoadName.

void DrawScene(GLenum mode)
{
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslated(shift[0],shift[1],0.);

    if(mode == GL_SELECT)
        glLoadName(1);

    glPushMatrix();
    glTranslated(-3.,0.,0.);
    glCallList(100);
    glPopMatrix();

    if(mode == GL_SELECT)
        glLoadName(2);

    glPushMatrix();
    glTranslated(3.,0.,0.);
    glRotated(180.,0.,0.,1.);
    glCallList(100);
    glPopMatrix();

    if(mode == GL_SELECT)
        glLoadName(3);

    glPushMatrix();
    glTranslated(0.,3.,0.);
    glRotated(-90.,0.,0.,1.);
    glCallList(100);
    glPopMatrix();

    if(mode == GL_SELECT)
        glLoadName(4);

    glPushMatrix();
    glTranslated(0.,-3.,0.);
    glRotated(90.,0.,0.,1.);
    glCallList(100);
    glPopMatrix();
}

Интерактивное взаимодействие чаще всего организуется с использованием мыши:

void Mouse(int button, int state, int x, int y)
{
// Если нажата левая кнопка мыши
     if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
    // Получить параметры порта вывода
        GLint viewport[4];
        glGetIntegerv(GL_VIEWPORT, viewport);
    // Определить массив, в котором будет сохраняться информация о выбранных объектах
        GLuint selectBuf[10];
        glSelectBuffer(10, selectBuf);

// Перейти в режим выбора
    glRenderMode(GL_SELECT);

// Очистить список идентификаторов объектов
    glInitNames();
// Перейти на нулевой уровень стека идентификаторов
    glPushName(0);

// Перейти в режим проекционной матрицы
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
// Определить положение и размеры отслеживаемой области
    gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y), 3, 3, viewport);

// Установить систему коордиант
    gluOrtho2D(-10.,10.,-10.,10.);
// Выполнить псевдотображение сцены
    DrawDesk(GL_SELECT);

// Вернуться в режим проекционной матрицы
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

// Дождаться окончания всех команд
    glFlush();

// Вернуться в режим визуализации и обработать выбранные объекты
    if(glRenderMode(GL_RENDER) > 0)
    {
        switch (selectBuf[3])
        {
        case 1:
            {
                shift[0]-=.1;
                break;
            }
        case 2:
            {
                shift[0]+=.1;
                break;
            }
        case 3:
            {
                shift[1]+=.1;
                break;
            }
        case 4:
            {
                shift[1]-=.1;
                break;
            }
        }
        glutPostRedisplay();
    }
    }
}

Массив, в котором сохранена информация о выбранных объектах в простейшем случае имеет следующую структуру:

  • [0] - количество выбранных объектов;
  • [1] и [2] - минимальная и максимальная координата z вершин всех выбранных примитивов пересекающихся с плоскастями отсечения (актуальна для трёхмерных объектов);
  • начиная с [3] содержатся идентификаторы выбранных объектов.