Расширенные функции клавиатуры
В этом разделе мы собираемся выучить четыре новые функции для работы с клавиатурой.
Эти функции работают одновременно, освобождая нас нас от проблем автоматического повтора команд, что приводит к небольшой задержке в ожидании автоматического повтора.
Первая функция позволяет отключить клавиатурные повторы. Синтаксис выглядит следующим образом:
int glutSetKeyRepeat(int repeatMode);
Параметры:
- repeatMode - включение, отключение или восстановление настроек режима автоматического повтора команд. Смотрим ниже возможные значения.
Возможные значения для repeatMode являются следующими:
GLUT_KEY_REPEAT_OFF - отключить режим автоматического повтора.
GLUT_KEY_REPEAT_ON - включить режим автоматического повтора.
GLUT_KEY_REPEAT_DEFAULT - сбросить режим автоматического повтора команды в состояние по умолчанию.
Заметим, что эта функция работает в глобальном масштабе, то есть это будет влиять на режим повтора во всех окнах, а не только на те окна из наших приложений. Поэтому будьте осторожны при использовании этой функции, когда отключаете режим автоматического повтора команды, всегда сбросывайте его в исходное состояние перед завершением приложения. GLUT предоставляет нам безопасный способ, отключающий функцию обратного вызова для клавиатуры, когда повтор нажатия происходит в данный момент. Функция, которая обеспечивает эту функциональность представлена следующая:
int glutIgnoreKeyRepeat(int repeatMode);
Параметры:
- repeatMode - ноль - для автоповтора, не ноль отключает режим автоповтора.
В любом случае, мы остановим функцию обратного вызова, когда происходит повтор нажатия. Однако, если вы хотите создать действие, выполняемое в то время как клавиша нажата, вы должны знать, когда клавиша будет отпущена. GLUT предоставляет две функции, которые регистрируют обратные вызовы при отпускании клавиши.
void glutKeyboardUpFunc(void (*func)(unsigned char key,int x,int y)); void glutSpecialUpFunc(void (*func)(int key,int x, int y));
Параметры:
- *func - имя функции обратного вызова.
Аргументом будет имя функции, которая будет обрабатывать эти события. Остальные параметры те же, используемые при событиях, когда пользователь нажимает клавишу, так что освежим память, прочитав предидущий урок. В следующем разделе мы покажем, как использовать эти функции для повышения эффективности применения модернизируя последний пример кода.
Теперь мы применим расширенные функции клавиатуры. Подход заключается в проверке, когда нажата кнопка, чтобы начать движение, и остановке движения, когда клавиша отпущена. Поэтому мы собираемся отключить обратные вызовы при повторе нажатия в glutIgnoreKeyRepeat. При нажатии клавиши мы собираемся присвоить переменной ненулевое значение. После отпускания клавиши переменная будет установлена в ноль.
Поскольку ни обратные вызовы не активны при нажатой клавиши и отпущенной клавишей мы должны проверить эти переменные в функции визуализации и обновлять позицию и ориентацию камеры соответственно. В разделе инициализации у нас есть две новые переменные: deltaAngle и deltaMove. Эти переменные должны контролировать вращение и движение камеры соответственно. Эти две переменные принимают начальное нулевое значение, это означает, что изначально камера все еще статична. В начале нашего код, мы собираемся добавить две переменные для отслеживания статуса камеры, один для ориентации, deltaAngle, и другой для позиции, deltaMove.
//Ключи статуса камеры. Переменные инициализируются нулевыми значениями //когда клавиши не нажаты float deltaAngle = 0.0f; float deltaMove = 0;
В функции визуализации мы добавим некоторый код в начале, чтобы проверить эти переменные и обновлять положение и ориентацию камеры соответственно.
void renderScene(void) { if (deltaMove) computePos(deltaMove); if (deltaAngle) computeDir(deltaAngle); ...
Где computePos computeDir и определяются следующим образом:
void computePos(float deltaMove) { x += deltaMove * lx * 0.1f; z += deltaMove * lz * 0.1f; } void computeDir(float deltaAngle) { angle += deltaAngle; lx = sin(angle); lz = -cos(angle); }
Функции, которые реагируют на события нажатия и отпускания клавиши такжемодифицируются. Когда нажата кнопка мы устанавливаем соответствующей переменной ненулевое значение. При отпускании клавиши переменная вернется к нулевому значению.
void pressKey(int key, int xx, int yy) { switch (key) { case GLUT_KEY_LEFT : deltaAngle = -0.01f; break; case GLUT_KEY_RIGHT : deltaAngle = 0.01f; break; case GLUT_KEY_UP : deltaMove = 0.5f; break; case GLUT_KEY_DOWN : deltaMove = -0.5f; break; } } void releaseKey(int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT : case GLUT_KEY_RIGHT : deltaAngle = 0.0f;break; case GLUT_KEY_UP : case GLUT_KEY_DOWN : deltaMove = 0;break; } }
Наконец, в функцию main добавим новые строки:
- glutIgnoreKeyRepeat вызывается с ненулевым параметром для того, чтобы остановить повторное нажатие клавиши.
Впоследствии, glutSpecialUpFunc вызывается для регистрации функции обратного вызова для обработки события клавишу увеличения.
int main(int argc, char **argv) { // инициализация GLUT и создание окна glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(400,400); glutCreateWindow("Урок 6"); // регистрация вызовов glutDisplayFunc(renderScene); glutReshapeFunc(changeSize); glutIdleFunc(renderScene); glutSpecialFunc(pressKey); // Новые функции для регистрации glutIgnoreKeyRepeat(1); glutSpecialUpFunc(releaseKey); // OpenGL - инициализация функции теста glEnable(GL_DEPTH_TEST); // главный цикл glutMainLoop(); return 1; }
Итоговый код программы:
#include <stdlib.h> #include <math.h> #include <glut.h> // угол поворота камеры float angle=0.0; // координаты вектора направления движения камеры float lx=0.0f, lz=-1.0f; // XZ позиция камеры float x=0.0f, z=5.0f; //Ключи статуса камеры. Переменные инициализируются нулевыми значениями //когда клавиши не нажаты float deltaAngle = 0.0f; float deltaMove = 0; void changeSize(int w, int h) { // предотвращение деления на ноль if (h == 0) h = 1; float ratio = w * 1.0 / h; // используем матрицу проекции glMatrixMode(GL_PROJECTION); // обнуляем матрицу glLoadIdentity(); // установить параметры вьюпорта glViewport(0, 0, w, h); // установить корректную перспективу gluPerspective(45.0f, ratio, 0.1f, 100.0f); // вернуться к матрице проекции glMatrixMode(GL_MODELVIEW); } void drawSnowMan() { glColor3f(1.0f, 1.0f, 1.0f); // тело снеговика glTranslatef(0.0f ,0.75f, 0.0f); glutSolidSphere(0.75f,20,20); // голова снеговика glTranslatef(0.0f, 1.0f, 0.0f); glutSolidSphere(0.25f,20,20); // глаза снеговика glPushMatrix(); glColor3f(0.0f,0.0f,0.0f); glTranslatef(0.05f, 0.10f, 0.18f); glutSolidSphere(0.05f,10,10); glTranslatef(-0.1f, 0.0f, 0.0f); glutSolidSphere(0.05f,10,10); glPopMatrix(); // нос снеговика glColor3f(1.0f, 0.5f , 0.5f); glRotatef(0.0f,1.0f, 0.0f, 0.0f); glutSolidCone(0.08f,0.5f,10,2); } void computePos(float deltaMove) { x += deltaMove * lx * 0.1f; z += deltaMove * lz * 0.1f; } void computeDir(float deltaAngle) { angle += deltaAngle; lx = sin(angle); lz = -cos(angle); } void renderScene(void) { if (deltaMove) computePos(deltaMove); if (deltaAngle) computeDir(deltaAngle); //очистить буфер цвета и глубины glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // обнулить трансформацию glLoadIdentity(); // установить камеру gluLookAt( x, 1.0f, z, x+lx, 1.0f, z+lz, 0.0f, 1.0f, 0.0f ); // нарисуем "землю" glColor3f(0.9f, 0.9f, 0.9f); glBegin(GL_QUADS);// полигон с коондинатами glVertex3f(-100.0f, 0.0f, -100.0f); glVertex3f(-100.0f, 0.0f, 100.0f); glVertex3f( 100.0f, 0.0f, 100.0f); glVertex3f( 100.0f, 0.0f, -100.0f); glEnd(); // Нарисуем 64 снеговика for (int i = -4; i < 4; i++) for (int j=-4; j < 4; j++) { glPushMatrix(); glTranslatef(i*5.0, 0, j * 5.0); drawSnowMan(); glPopMatrix(); } glutSwapBuffers(); } void pressKey(int key, int xx, int yy) { switch (key) { case GLUT_KEY_LEFT: deltaAngle = -0.01f; break; case GLUT_KEY_RIGHT: deltaAngle = 0.01f; break; case GLUT_KEY_UP: deltaMove = 0.5f; break; case GLUT_KEY_DOWN: deltaMove = -0.5f; break; } } void releaseKey(int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT: case GLUT_KEY_RIGHT: deltaAngle = 0.0f; break; case GLUT_KEY_UP: case GLUT_KEY_DOWN: deltaMove = 0; break; } } int main(int argc, char **argv) { // инициализация GLUT и создание окна glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(400,400); glutCreateWindow("Урок 6"); // регистрация вызовов glutDisplayFunc(renderScene); glutReshapeFunc(changeSize); glutIdleFunc(renderScene); glutSpecialFunc(pressKey); // Новые функции для регистрации glutIgnoreKeyRepeat(1); glutSpecialUpFunc(releaseKey); // OpenGL - инициализация функции теста glEnable(GL_DEPTH_TEST); // главный цикл glutMainLoop(); return 1; }
Мы должны получить окно со сценой снеговиков и клавиши перемещения по сцене должны работать. Тестовый файл прикреплён.
Прикрепленный файл | Размер |
---|---|
testapp.rar | 10.48 кб |