MCF и OpenGL.
"Подать сюда MFС!!! - кричил он, топая
всеми 4-мя лапами."
Сижу тут как-то, программку сочиняю, тут смотрю,
царь зверей пожаловал. Вопрос задать пришел.
Спрашивает как же OpenGL в MFC то вставить?
Сначала думал отмажусь, потом смотрю,
настойчивый такой царь попался. Письма шлет,
желает знать как же все-таки её туда вставить-то.
Вот и решил я примерчик на MFC состроить дабы
цари меньше утруждали себя, а больше на солнышке
бы нежились, чтоб у царей спокойно и хорошо все
было, тогда и нам, простым зверушкам жить хорошо
будет. И так поехали.
Для начала сделаем приложение MFC как диалог.
Я назвал его BitScroll. Как это делать? Смотрите
шаги по MFC.
Теперь, при помощи визарда добавим функцию
BOOL CBitScrollDlg::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
return CDialog::PreCreateWindow(cs);
}
Помните, мы устанавливали слиль окна в функции CreateWindow
? Так вот это действие по смыслу тоже самое.
Напомню как это выглядело в Win32API:
hWnd = CreateWindow("Skeleton", "Skeleton", WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 50, 50,700, 400, NULL,
NULL, hInst ,NULL);
Теперь обратимся к функции OnInitDialog(). В ней
сначала вызывается функция базового класса, т.е. CDialog,
а потом устанавливаются иконки для диалога.
Давайте вставим наш код между иконками и CDialog::OnInitDialog().
SetWindowPos(&wndTop, 0, 0, WIDTH, HEIGHT, SWP_NOMOVE);
pDC = GetDC();
CenterWindow();
Init();
SetTimer(1,SPEED, NULL);
Теперь посмотрим, что мы сделали. Сначала
сделаем окно нужного нам размера (макросы WIDTH
и HEIGHT объявлены так #define WIDTH 640 и #define HEIGHT
480 в заголовочном файле).
Затем получим контекст для рисования.
Установимся в центр вселеной и... вот, тут самое
интересное, тут мы вызываем нашу собственную
функцию, которая будет инициализировать OpenGL
(напоминаю, что тоже самое делала ф-ция Initial в
программе на Win32API).
А потом включаем таймер, чтобы обеспечить
анимацию. Обратимся теперь к Init(). Выглядит
она итак:
void CBitScrollDlg::Init()
{
CRect rect;
HGLRC hrc;
if (!bSetupPixelFormat())
return;
hrc = wglCreateContext(pDC->GetSafeHdc());
ASSERT(hrc != NULL);
wglMakeCurrent(pDC->GetSafeHdc(), hrc);
GetClientRect(&rect);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)rect.right /
(GLfloat)rect.bottom, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
}
Что мы тут делаем? Прежде всего вызываем ф-ию SetupPixelFormat(),
это опять наша функция и мы посмотрим ее чуть
позже.
Далее, как и раньше, получаем контекст
рендеринга (маленькая деталь, ранее pDC(а
точнее эта переменная называлась hDC) была
объявлена как static HDC hDC, сейчас контекст
рисования является пременной типа CDC, а ф-ция wglCreateContext
и другие ф-ции OpenGL требуют в качестве
аргумента переменную типа HDC. Поэтому мы
получаем этот hardware context с помощью pDC->GetSafeHdc()).
Затем делаем этот контекст текущим и
настраиваем область отображения, так как это
делалось в Initial() (Win32API)
Функция bSetupPixelFormat() содержит следующее:
BOOL CBitScrollDlg::bSetupPixelFormat()
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int pixelformat;
if ((pixelformat = ChoosePixelFormat(pDC->GetSafeHdc(), &pfd)) == 0)
{
MessageBox("ChoosePixelFormat failed");
return FALSE;
}
if (SetPixelFormat(pDC->GetSafeHdc(), pixelformat, &pfd) == FALSE)
{
MessageBox("SetPixelFormat failed");
return FALSE;
}
return TRUE;
}
Как не трудно заметить, она почти полностью
взята из Win32API приложения за исключением того,
что hDC заменена на pDC->GetSafeHdc()
Теперь добавим в нашу программку обработчик от
таймера (мы его недавно сделали в ф-ции Init())
void CBitScrollDlg::OnTimer(UINT nIDEvent)
{
DrawScene();
CDialog::OnTimer(nIDEvent);
}
Все тривиально. По смыслу ясно, что каждый раз,
когда срабатывает обработчик таймера (а делает
он это часто) рисуется сцена. Таким образом
получается анимация. Посмотрим на эту ф-цию:
void CBitScrollDlg::DrawScene()
{
static GLfloat angle = 0;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -2.0f);
glRotatef(angle, 1.0f, 0.5f, 0.3f);
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 1.0f);
glVertex3f(-0.5f, 0.5f, 0.0f);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.5f, 0.5f, 0.0f);
glColor3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.5f, -0.5f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glEnd();
glFinish();
SwapBuffers(wglGetCurrentDC());
angle += 0.5f;
}
По моему проще уже некуда. Я надеюсь, что все
ясно.
Ну вот казалось бы и все. Единственное, что
остается сделать - убраться за собой. Т.е. надо при
выходе удалить контекст рендеринга и убить
таймер.
Эти вещи надо сделать в 2-х обработчиках OnClose
и OnDestroy. Посмотрим на них:
void CBitScrollDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
HGLRC hrc;
KillTimer(1);
hrc = ::wglGetCurrentContext();
::wglMakeCurrent(NULL, NULL);
if (hrc)
::wglDeleteContext(hrc);
CDialog::OnClose();
}
и
void CBitScrollDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
HGLRC hrc;
KillTimer(1);
hrc = ::wglGetCurrentContext();
::wglMakeCurrent(NULL, NULL);
if (hrc)
::wglDeleteContext(hrc);
}
Она приактически одинаковые. Работают так же
как и case WM_CLOSE в Win32API. Т.е. убиваем таймер,
получаем контекст рендеринга, если он есть -
удаляем его.
Ну вот и все. Еще добавлю, что в проекте есть
ф-ция OnSize для обработки изменений размеров
окна. В данном случае она не нужна, но если
вставлять OpenGL в SDI или MDI, то она вам
понадобится. |