Введение
обработчиков сообщений Windows
Наш объект,
будучи активизирован в рамках окна контейнера, будет реагировать на сообщения
Windows. Он должен управляться мышью, поддерживать вращение с помощью таймера,
устанавливать нужный формат при создании своего окна и т. д. Введите в класс
copenGL способность реагировать на следующие сообщения:
WM_ERASEBKGND,
WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONUP, WM_MOUSEMOVE, WM_CREATE,
WM_DESTROY, WM_SIZE, WM_TIMER.
Для этого:
- Поставьте курсор на строку
с именем класса COpenGL в окне ClassView и дайте команду Properties из контекстного
меню.
- Нажмите кнопку Messages
на панели инструментов окна Properties.
- Для того чтобы после
введения обработчика окно свойств не убегало, переведите его в режим Floating
и оттащите в сторону. В окне Class View должен быть выбран класс COpenGL
- По очереди для всех перечисленных
сообщений укажите действие <Add> в правом столбце таблицы Properties.
Обработчик
сообщения OnEraseBkgnd вызывается операционной системой в те моменты, когда
фон окна должен быть стерт, например при изменении размеров окна. Родительская
версия этой функции или обработка по умолчанию использует для стирания (закрашивания)
кисть, указанную в структуре WNDCLASS при ее регистрации. Если надо отменить
стирание фона, то наша версия функции обработки должна установить специальный
флаг, который говорит о том, что сообщение обработано, иначе окно останется
помеченным как нуждающееся в стирании фона. Введите в файл реализации класса
COpenGL код обработки сообщения:
LRESULT
COpenGL::OnEraseBkgnd(UINT /*uMsg*/, WPARAM
/*wParam*/,
LPARAM /*lParam*/, BOOL& bHandled)
{
//======
Устанавливаем флаг завершения обработки
bHandled
= TRUE;
return
0;
}
Отметьте, что
прототип функции обработки отличается от того, который принят в MFC. Там он
имеет вид af x_msg BOOL OnEraseBkgnd(CDC* pDC); и определен в классе CWnd. Наш
класс COpenGL среди своих многочисленных предков имеет класс CComControl, который
происходит от класса CWindowlmpl, а тот, в свою очередь, является потомком класса
cwindow. Последний выполняет в ATL ту же роль, что и класс cwnd в MFC, но не
несет с собой бремени наследования от CObject. Это в основном и ускоряет функционирование
ATL-приложений.
Примечание
В заготовке тела функций
обработки все параметры закомментированы. Это сделано для того, чтобы упростить
работу компилятору, так как далеко не все параметры задействованы постоянно.
Если параметр необходимее его нужно сделать видимым для компилятора, убрав
знаки комментария. Сделайте это для параметра bHandled.
Теперь введите
в класс обработчик сообщения WM_CREATE и заполните его кодами, которые готовят
окно и устанавливают некоторые параметры OpenGL:
LRESULT
COpenGL::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/,'LPARAM /*lParam*/, BOOL&
bHandled)
//=======
Описатель формата окна OpenGL
PIXELFORMATDESCRIPTOR
pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
//
Размер структуры
1,
//
Номер версии
PFD_DRAW_TO_WINDOW
|
//
Поддержка
GDI
PFD_SUPPORT_OPENGL |
//
Поддержка OpenGL
PFD_DOUBLEBUFFER,
//
Двойная буферизация
PFD_TYPE_RGBA,
//
Формат RGBA, не палитра
24,
//
Количество плоскостей
//
в каждом буфере цвета
24,
0,
//
Для компонента Red
24,
0,
//
Для компонента Green
24,
0,
//
Для компонента Blue
24,
0,
//
Для компонента Alpha
0,
//
Количество плоскостей
//
буфера Accumulation
0,
//
То же для компонента Red
0,
//
для компонента Green
0,
//
для компонента Blue
0,
// для компонента Alpha
32,
// Глубина Z-буфера
0,
// Глубина буфера Stencil
0,
// Глубина буфера Auxiliary
0,
// Теперь игнорируется
0,
// Количество плоскостей
0,
// Теперь игнорируется
0,
// Цвет прозрачной маски
0
// Теперь игнорируется
};
//
Добываем дежурный контекст и просим выбрать ближайший
m_hdc
= GetDCO ;
int
iD = ChoosePixelFormat(m_hdc, &pfd) ;
if
( !ID )
{
ATLASSERT(FALSE);
return
-1;
}
//======
Пытаемся установить этот формат
if
( ISetPixelFormat (m_hdc, iD, &pfd))
{
ATLASSERT(FALSE);
return
-1;
}
//======
Пытаемся создать контекст передачи OpenGL
if
( !(m_hRC = wglCreateContext (m_hdc)))
{
ATLASSERT(FALSE);
return
-1;
}
//======
Пытаемся выбрать его в качестве текущего
if
( !wglMakeCurrent (m_hdc, m_hRC))
{
ATLASSERT(FALSE);
return
-1;
}
//======
Теперь можно посылать команды OpenGL
glEnable
(GL_LIGHTING) ;
//
Будет освещение
glEnable
(GL_LIGHTO) ;
//
Только 1 источник
glEnable
(GL_DEPTH_TEST) ;
//
Учитывать глубину (ось Z)
//======
Учитывать цвет материала поверхности
glEnable
(GL_COLOR_MATERIAL) ;
//======
Устанавливаем цвет фона
SetBkColor
() ;
bHandled
= TRUE;
return
0;
}
Класс copenGL
должен реагировать на сообщение WM_SIZE и корректировать видимый объем сцены.
Мы будем использовать режим просмотра с учетом перспективы. Его определяет функция
gluPerspective. Введите в класс copenGL обработку WM_SIZE и вставьте в нее следующие
коды:
LRESULT
COpenGL: :OnSize(UINT /*uMsg*/, WPARAM /*wParam*/,
LPARAM
IParam, BOOL& bHandled)
{
//
Распаковываем длинный параметр и узнаем размеры окна
UINT
сх = LOWORD ( IParam) , су = HIWORD (IParam) ;
//======
Вычисляем максимальные диспропорции окна
double
dAspect = cx<=cy ? double (су) /сх
:
double (сх) /су;
//====
Задаем тип текущей матрицы (матрица проекции)
glMatrixMode
(GL_PROJECTION) ;
//======
Приравниваем ее к единичной диагональной
glLoadldentity
() ;
//==
Параметры перспективы (45 градусов - угол обзора)
gluPerspective
(45., dAspect, 1., 10000.);
glViewport
(0, 0, сх, су); DrawScene () ;
bHandled
= TRUE;
return
0;
}
Функция glViewport,
как вы помните, задает прямоугольник просмотра. При закрытии окна внедренного
объекта необходимо освободить память, занимаемую контекстом передачи, и отказаться
от услуг таймера, с помощью которого мы будем производить анимацию вращения
изображения. Введите в класс обработчик сообщения WM_DESTROY и измените ее стартовый
код, как показано ниже:
LRESULT
COpenGL: :OnDestroy (UINT /*uMsg*/, WPARAM
/*wParam*/,
LPARAM /*lParam*/, BOOL& bHandled)
{
KillTimer(l);
if
(m_hRC)
{
wglDeleteContext(m_hRC);
m_hRC = 0;
}
bHandled
= TRUE;
return
0;
}
Инициализация
переменных
В конструктор
класса вставьте код установки начальных значений переменных, с помощью которых
пользователь сможет управлять сценой Open GL:
COpenGL:
: COpenGL()
{
//======
Контекст передачи пока отсутствует
m_hRC
= 0;
//======
Начальный разворот изображения
m_AngleX
= 35. f;
m_AngleY
= 20. f;
//======
Угол зрения для матрицы проекции
m_AngleView
= 45. f;
//======
Начальный цвет фона
m_clrFillColor
= RGB (255,245,255);
//======
Начальный режим заполнения
//======
внутренних точек полигона
m_FillMode
= GL_FILL;
//======
Подготовка графика по умолчанию
DefaultGraphic
();
//===
Начальное смещение относительно центра сцены
//===
Сдвиг назад на полуторный размер объекта
m_zTrans
= -1.5f*m_fRangeX;
m_xTrans
= m_yTrans = 0.f ;
//
Начальные значения квантов смещения (для анимации)
m_dx
= m_dy = 0.f;
//===
Мыть не захвачена
m_bCaptured
= false;
//===
Правая кнопка не была нажата
m_bRightButton
= false;
//===
Рисуем четырехугольниками m_bQuad = true;
//======
Начальный значения параметров освещения
m_LightParam[OJ
= 50; // X position
m_LightParam[l]
= 80; // Y position
m_LightParam[2]
= 100; // Z position
m_LightParam[3]
= 15; // Ambient light
m_LightPararn[4]
= 70; // Diffuse light
m_LightParam[5]
= 100; // Specular light
m_LightParam[6]
= 100; // Ambient material
m_LightParam[7]
= 100; // Diffuse material
m_LightParam[8]
= 40; // Specular material
m_LightParam[9]
= 70; // Shininess material
m_LightParam[10]
= 0; // Emission material
}
Функция
перерисовки
Перерисовка
изображения OpenGL состоит в том, что обнуляется буфер цвета и буфер глубины
— буфер третьей координаты. Затем в матрицу моделирования (GL_MODELVIEW), которая
уже выбрана в качестве текущей, загружается единичная матрица (glLoadldentity).
После этого происходит установка освещения, с тем чтобы на него не действовали
преобразования сдвига и вращения. Лишь после этого матрица моделирования домножается
на матрицу трансляции и матрицу вращений. Чтобы рассмотреть изображение, достаточно
иметь возможность вращать его вокруг двух осей (X и Y). Поэтому мы домножаем
матрицу моделирования на две матрицы вращений (glRotatef). Сначала вращаем вокруг
оси X, затем вокруг оси Y:
HRESULT
COpenGL: :OnDraw (ATL_DRAWINFO& di)
{
glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadldentity{);
//======
Установка параметров освещения
SetLight
();
//======
Формирование матрицы моделирования
glTranslatef(m_xTrans,m_yTrans,m_zTrans);
glRotatef
(m_AngleX, l.0f, 0.0f, 0.0f );
glRotatef
(m_AngleY, 0.0f, l.0f, 0.0f );
//======
Вызов рисующих команд из списка
glCallList(1);
//======
Переключение буферов
SwapBuffers(m_hdc);
return
S_OK;
}
|