Иллюстрированный самоучитель по Visual Studio .NET

Взаимодействие классов

Управление объектом с помощью мыши

Алгоритм управления ориентацией объекта с помощью мыши мы разработали ранее. Вы помните, что перемещение курсора мыши при нажатой кнопке должно вращать изображение, причем горизонтальное перемещение вращает его вокруг вертикальной оси Y, а вертикальное – вокруг горизонтальной оси X. Если одновременно с мышью нажата клавиша CTRL, то объект перемещается (glTranslatef) вдоль осей X и Y. Наконец, с помощью правой кнопки изображение перемещается вдоль оси Z, то есть приближается или отдаляется.

Таймер помогает нам в том, что продолжает вращение, если очередной квант перемещения мышью стал выше порога чувствительности. Скорость вращения имеет два пространственных компонента, которые пропорциональны разности двух последовательных во времени координат курсора. Чем быстрее движется курсор при нажатой левой кнопке, тем большая разность координат будет обнаружена в обработчике сообщения WM_MOUSEMOVE. Именно в этой функции оценивается желаемая скорость вращения.

Описанный алгоритм обеспечивает гибкое и довольно естественное управление ориентацией объекта, но, как вы помните, он имеет недостаток, который проявляется, когда модуль угла поворота вдоль первой из вращаемых (с помощью glRotate) осей, в нашем случае – это ось X, превышает 90 градусов. Вам, читатель, я рекомендовал самостоятельно решить эту проблему и устранить недостаток. Ниже приводится одно из возможных решений. Если вы, читатель, найдете более изящное, буду рад получить его от вас. Для начала следует ввести в состав класса COpenGL функцию нормировки углов вращения, которая, учитывая периодичность процесса, ограничивает их так, чтобы они не выходили из диапазона (-360°, 360°):

void COpenGL::LimitAngles()
{
//====== Нормирование углов поворота так,
//====== чтобы они были в диапазоне (-360°, +360°)
while (m_AngleX < -360.f)
m_AngleX += 360.f;
while (m_AngleX > 360.f)
m_AngleX -= 360.f;
while (m_AngleY < -360.f)
m_AngleY += 360.f;
while (m_AngleY > 360.f)
m_AngleY -= 360.f;
}

Затем следует вставить вызовы этой функции в те точки программы, где изменяются значения углов. Кроме того, надо менять знак приращение m_dx, если абсолютная величина угла m_AngleX попадает в диапазон (90°, 270°). Это надо делать при обработке сообщения WM_MOUSEMOVE. Ниже приведена новая версия функции обработки этого сообщения, а также сообщения WM_TIMER, в которое также следует ввести вызов функции нормировки:

LRESULT COpenGL::OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM IParam, BOOL& bHandled)
{
//====== Если был захват
if (m_bCaptured)
{
//====== Вычисляем желаемую скорость вращения
short xPos = (short)LOWORD(IParam);
short yPos = (short)HIWORD(1Param);
m_dy = float(yPos – m_yPos)/20.f;
m_dx = float(xPos – m_xPos)/20.f;
//====== Если одновременно была нажата CTRL,
if (wParam & MK_CONTROL)
{
//=== Изменяем коэффициенты сдвига изображения
m_xTrans += m_dx;
m_yTrans -= m_dy;
}
else
{
//====== Если была нажата правая кнопка
if (m_bRightButton)
//====== Усредняем величину сдвига
m_zTrans += (m_dx + m_dy)/2.f;
else
{
//====== Иначе, изменяем углы поворота
//====== Сначала нормируем оба угла
LiraitAngles();
//=== Затем вычисляем модуль одного из них
double a = fabs(m_AngleX);
// и изменяем знак приращения(если надо)
if (90. < а && а < 270.) m_dx = -m_dx;
m_AngleX += m_dy;
m_AngleY += m_dx;
}
}
// В любом случае запоминаем новое положение мыши
m_xPos = xPos;
m_yPos = yPos;
FireViewChange();
}
bHandled = TRUE; return 0;
}
LRESULT COpenGL::OnTimer (UINT /*uMsg*/, WPARAM
/*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
//====== Нормировка углов поворота
LimitAngles ();
//====== Увеличиваем эти углы
m_AngleX += m_dy; m_AngleY += m_dx;
//====== Просим перерисовать окно
FireViewChange();
bHandled = TRUE;
return 0;
}
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.