В этом шаге мы создадим скелет приложения, который мы будем использовать во
всех дальнейших проектах. Я, например, сделал проект skeleton и спрятал в
надежное место ;)
Прежде всего создайте пустой проект Win32 Application. Назовем его
skeleton. Добавьте в проект пустой файл skeleton.cpp ( или
.c, поскольку мы пока не будем использовать с++)
Теперь настроим проект: Меню Project->Settings, заладка Link
и в editbox "Object/Library Modules" добавим библиотеки:
opengl32.lib, glu32.lib ( в принципе, можно обойтись и только
opengl32.lib, но glu32.lib имеет несколько удобных функций для
настроек ViewPort-а)
Нажимаем "Ok" и, как пишут в американских книжках, мы готовы пить кофе
;) Ну ладно, шутки в сторону.
Пишем заголовочные файлы: #include <windows.h> // это, сами понимаете для того что б win заработало
#include <gl/gl.h> // OpenGL
#include <gl/glu.h> // GLU
Теперь объявим несколько глобальных переменных: static HGLRC hRC; // Это контекст рендеринга
static HDC hDC; // А это стандартный контекст устройства windows
BOOL keys[256]; // это массивчик, который нам пригодится
// для обработки нажатия клавиш
Теперь объявляем функции: GLvoid Initial(GLsizei Width, GLsizei Height);
// по названию ясно, это инициализация всей страны (т.е. OpenGL)
GLvoid Resize(GLsizei Width, GLsizei Height);
// эта маленькая функция будет вызываться при получении окном сообщения WM_SIZE
GLvoid Draw(GLvoid); // это центр вселенной для OpenGL
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Ну это все и так знают. Функция окна в Win32
Переходим к реализации функций: GLvoid Initial(GLsizei Width, GLsizei Height)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);// устанавливаем цвет для очистки буфера цвета
glClearDepth(1.0); // устенавливаем параметр для очистки буфера глубины
glDepthFunc(GL_LESS); // настройка Z буфера
glEnable(GL_DEPTH_TEST); // и, наконец, включение
glShadeModel(GL_SMOOTH); // выбираете режим затенения ( flat или smooth )
glMatrixMode(GL_PROJECTION);// устанавливаем текушей матрицей матрицу проекта
glLoadIdentity(); // обнуляем эту самую матрицу
// настраиваем перспективу ( вот она, функция из glu32 )
gluPerspective(45.0f, (GLfloat)Width / (GLfloat)Height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW); // и переключаемся в модельную матрицу
}
Следующую функцию я комментировать не буду, мне кажется и так все понятно. GLvoid Resize(GLsizei Width, GLsizei Height)
{
if(Height==0)
Height = 1;
glViewport(0, 0, Width, Height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)Width / (GLfloat)Height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
}
Функция Draw пока очень маленькая, но она будет расти, и будет это
делать очень быстро: GLvoid Draw(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // очищаем буферы
glLoadIdentity(); // обнуляем модельную матрицу
}
А сейчас самое интересное. Функция WndProc. В ней мы должны установить
формат пикселей и будем обрабатывать сообщения: LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
RECT Screen; // Это структурка в которую мы поместим размеры области экрана
GLuint PixelFormat; // формат пикселя
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),// размер структуры
1, // версия ?
PFD_DRAW_TO_WINDOW| // format must support Window
PFD_SUPPORT_OPENGL| // format must support OpenGL
PFD_DOUBLEBUFFER, // must support double buffer
PFD_TYPE_RGBA, // требуется RGBA формат
16, // 16Bit 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, // accumulation buffer bits ignored
16, // 16bit z-buffer (depth buffer)
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main drawing layer
0, // reserved
0, 0, 0 // layer mask ignored
};
// Начинаем обрабатывать сообщения
switch(msg)
{
case WM_CREATE: // на этапе создания приложения мы должны настроить
// формат пикселей и инициализироваит библиотеку
hDC = GetDC(hWnd); // получаем контекст windows
// Следующие несколько строк настраивают формат пикселей
PixelFormat = ChoosePixelFormat(hDC, &pfd);
if(!PixelFormat){
MessageBox(0, "Can't find suitable PixelFormat",
"Error", MB_OK|MB_ICONERROR);
PostQuitMessage(0);
break;
}
if(!SetPixelFormat(hDC, PixelFormat, &pfd)) {
MessageBox(0, "Can't set The PixelFormat",
"Error", MB_OK|MB_ICONERROR);
PostQuitMessage(0);
break;
}
hRC = wglCreateContext(hDC); // создаем контекст рендеринга
if(!hRC) {
MessageBox(0, "Can't Create Render Device Context",
"Error",MB_OK|MB_ICONERROR);
PostQuitMessage(0);
break;
}
if(!wglMakeCurrent(hDC, hRC)) { // устанавливаем его текущим
MessageBox(0, "Can't set current Render Device Context",
"Error",MB_OK|MB_ICONERROR);
PostQuitMessage(0);
break;
}
GetClientRect(hWnd, &Screen); // получаем клиентскую область
Initial(Screen.right, Screen.bottom);// инициализация OpenGL
break;
case WM_CLOSE:
case WM_DESTROY: // по CLOSE и/или DESTROY
ChangeDisplaySettings(NULL, 0); // восстанавливаем установки
wglMakeCurrent(hDC, NULL); // устанавливаем обычный device context
wglDeleteContext(hRC); // удаляем контекст рендеринга
ReleaseDC(hWnd, hDC); // освобождаем device context
PostQuitMessage(0);
break;
case WM_SIZE: // ну это для resize ( хотя как сделать resize
// для fuulscreen application я не знаю ;) )
Resize(LOWORD(lParam), HIWORD(lParam));
break;
default:
return( DefWindowProc(hWnd, msg, wParam, lParam));
}
return(0);
}
Дальше все стандартно за исключением того, что заполняется структурка
DEVMODE и устанавливаются DisplaySettings: int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShowCmd)
{
MSG msg;
WNDCLASS wc;
HWND hWnd;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "Lirik";
if(!RegisterClass(&wc))
{
MessageBox(0,"Error Create window class","Error", MB_OK|MB_ICONERROR);
return FALSE;
}
hWnd = CreateWindow("Skeleton",
"Skeleton",
WS_POPUP |
WS_CLIPCHILDREN |
WS_CLIPSIBLINGS,
0,
0,
800,
600,
NULL,
NULL,
hInst,
NULL);
if(!hWnd){
MessageBox(0,"Error Create window","Error",MB_OK|MB_ICONERROR);
return FALSE;
}
DEVMODE dmScreenSettings;
memset(&dmScreenSettings, 0, sizeof(DEVMODE));
dmScreenSettings.dmSize = sizeof(DEVMODE);
dmScreenSettings.dmPelsWidth = 800;
dmScreenSettings.dmPelsHeight = 600;
dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
SetFocus(hWnd);
while(1)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
return TRUE;
}
}
Draw();
SwapBuffers(hDC);
if(keys[VK_ESCAPE]) SendMessage(hWnd, WM_CLOSE, 0, 0);
}
}
Теперь, если мы все правильно сделали, вы увидите просто черный экранчик и
мышиный курсорчик.
Ну вы не расстраивайтесь.
В следующем шаге мы на этом черненьком экранчике сделаем беленькие фигурки, а
потом цветные, а потом 3-х мерные, а потом : стоп, не все сразу.
|