Интерактивное взаимодействие с объектами сцены
В некторых приложениях проектируемый объект или моделируемое явление просто отображается на экране. Часто требуется организовать интерактивное взаимодействие с объектами (выделение,
перемещение, масштабирование, вращение и т.п.) OpenGL предоставляет простой и эффективный механизм для выбора объектов (определения, какой объект отображён под курсором мыши), а их модификация осуществляется
уже известными функциями. Этот механизм реализуется следующим образом: в процессе псевдовизуализации отслеживается интересующая нас точка и, если в этой точке отображается векторный или растровый примитив,
то информация о нем будет предоставлена программе. Всё это происходит в следующем порядке:
- Создаётся специальный массив, в котором будет сохраняться информация о выбранном объекте
glSelectBuffer(GLsizei size, GLuint *buffer) .
- Перейти в режим выбора с помощью функции
glRenderMode(GL_SELECT) .
- Определить точку и область вокруг нее, которая будет отслеживаться.
- Сформировать стек объектов для псевдовизуализации с использоваием
glInitNames() , glLoadName() , glPushName() и glPopName() .
- Вернуться в режим отображения с помощью функции
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] содержатся идентификаторы выбранных объектов.
|