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.


Исходный файл смотрите здесь. Исполняемый файл здесь.