Chapter 7
Инициализация или как написать приложение с нуля
7.3 Windows-приложение - Win32 Application
Достоинством является непосредственное взаимодействие с WinAPI. Начальная
инициализация несколько усложняется, но зато вы имеете полноценное windows-приложение.
Такой тип приложения подходит для написания серьезных больших программ.
Кто-нибудь, конечно, скажет, что приложение непереносимо.
Вам нужно написать работающее
приложение для windows, а не неработающее, но переносимое приложение.
По поводу переносимости, хочу заметить следующее. В стандарте по языку
Си сказано, что код на языке Си может быть платформенно независимым и
платформенно зависимым. Из этого следует, что для обеспечения переносимости
большой программы, вам придется делать несколько вариантов и затачивать
ее под конкретные платформы. Код, относящийся к OpenGL, практически переносим.
Непереносима только начальная инициализация. Конечно, вы можете попробовать
Java-приложение, но тут возникают свои сложности. Так что, выбор за вами.
Создайте проект Win32 Application. Инструкции смотри в предыдущем разделе.
Только имена дайте win и win.c. Теперь будем писать файл win.c. Внесите
комментарии, заголовочные файлы и функции display и resize, см. предыдущий
раздел. Из функций display и resize уберите слово CALLBACK. А в функции
display замениете auxSwapBuffers() на
glFinish();
SwapBuffers(wglGetCurrentDC());
После включения заголовочных файлов объявите следующие глобальные переменные.
HWND hWnd;
HGLRC hGLRC;
HDC hDC;
Теперь вставьте код функции, которая устанавливает параметры контекста
воспроизведения OpenGL.
int SetWindowPixelFormat()
{
int m_GLPixelIndex;
PIXELFORMATDESCRIPTOR pfd;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cRedBits = 8;
pfd.cRedShift = 16;
pfd.cGreenBits = 8;
pfd.cGreenShift = 8;
pfd.cBlueBits = 8;
pfd.cBlueShift = 0;
pfd.cAlphaBits = 0;
pfd.cAlphaShift = 0;
pfd.cAccumBits = 64;
pfd.cAccumRedBits = 16;
pfd.cAccumGreenBits = 16;
pfd.cAccumBlueBits = 16;
pfd.cAccumAlphaBits = 0;
pfd.cDepthBits = 32;
pfd.cStencilBits = 8;
pfd.cAuxBuffers = 0;
pfd.iLayerType = PFD_MAIN_PLANE;
pfd.bReserved = 0;
pfd.dwLayerMask = 0;
pfd.dwVisibleMask = 0;
pfd.dwDamageMask = 0;
m_GLPixelIndex = ChoosePixelFormat( hDC, &pfd);
if(m_GLPixelIndex==0) // Let's choose a default index.
{
m_GLPixelIndex = 1;
if(DescribePixelFormat(hDC,m_GLPixelIndex,sizeof(PIXELFORMATDESCRIPTOR),&pfd)==0)
return 0;
}
if (SetPixelFormat( hDC, m_GLPixelIndex, &pfd)==FALSE)
return 0;
return 1;
}
Информацию о структуре PIXELFORMATDESCRIPTOR смотрите в справочнике.
Я пользуюсь MSDN. Сейчас MSDN входит в MS Developer Studio. Редактировать
параметры этой структуры вам вряд ли придется. А если придется,
то я не смогу тут описать все. Перевести справочник я, конечно, могу,
но это вам вряд ли поможет. Книга не предназначена для этого. Здесь рассматриваются
конкретные примеры и упражнения.
Теперь напишем функцию обработки сообщений нашего окна.
LRESULT CALLBACK WindowFunc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
float pos[4] = {3,3,3,1};
float dir[3] = {-1,-1,-1};
PAINTSTRUCT ps;
switch(msg)
{
// сообщение WM_CREATE приходит
// один раз при создании окна
case WM_CREATE:
// получаем контекст устройства нашего окна
hDC = GetDC(hWnd);
// устанавливаем параметры контекста воспроизведения OpenGL
SetWindowPixelFormat();
// создаем контекст воспроизведения OpenGL
hGLRC = wglCreateContext(hDC);
// делаем его текущим
wglMakeCurrent(hDC, hGLRC);
// далее см. предыдущий раздел
glEnable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);
break;
// это сообщение приходит при уничтожении окна
case WM_DESTROY:
// удаляем созданный выше
// контекст воспроизведения OpenGL
if (hGLRC)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hGLRC);
}
// освобождаем контекст устройства нашего окна
ReleaseDC(hWnd, hDC);
PostQuitMessage(0);
break;
// это сообщение приходит всякий раз,
// когда нужно перерисовать окно
case WM_PAINT:
BeginPaint(hWnd, &ps);
display();
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
resize( LOWORD(lParam), HIWORD(lParam) );
break;
default:
return DefWindowProc(hWnd,msg,wParam,lParam);
}
return 0;
}
И последнее, осталось написать функцию WinMain.
int WINAPI WinMain(HINSTANCE hThisInst,
HINSTANCE hPrevInst,
LPSTR str,int nWinMode)
{
MSG msg;
WNDCLASS wcl;
wcl.hInstance=hThisInst;
wcl.lpszClassName = "OpenGLWinClass";
wcl.lpfnWndProc = WindowFunc;
wcl.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wcl.hIcon = NULL;
wcl.hCursor = LoadCursor(NULL,IDC_ARROW);
wcl.lpszMenuName = NULL;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;
wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
RegisterClass(&wcl);
hWnd = CreateWindow(
"OpenGLWinClass",
"Win API Template",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
200,
150,
400,
420,
HWND_DESKTOP, NULL,
hThisInst, NULL);
ShowWindow(hWnd,nWinMode);
UpdateWindow(hWnd);
while(1)
{
while( PeekMessage(&msg,NULL,0,0,PM_NOREMOVE) )
if(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
return 0;
display();
}
return 0;
}
OpenGL требует свойства WS_CLIPCHILDREN и WS_CLIPSIBLINGS для окна в Windows.
Поэтому были добавлены эти свойства при создании окна в функцию Createwindow.
Также обратите внимание, что функция display вызывается в бесконечном
цикле. Она вызывается, когда в очереди сообщений окна нет ничего. Эта
же функция вызывается, когда нужно отрисовать окно заново - обработчик
WM_PAINT.
Исходный файл смотрите здесь.
Исполняемый файл здесь.
|