2: Basic OpenGL Startup/Shutdown:

 

Over here, we’ll create a very simple skeleton for opening an OpenGL window.

First, we include some additional header files from the OpenGL subsystem:

 

// OpenGL header files

#include <gl\gl.h>

#include <gl\glu.h>

 

Now, we use the #pragma command to link to the library files, instead of messing up with the compiler options:

 

// Include library files

#pragma comment( lib, "opengl32.LIB" )

#pragma comment( lib, "glu32.LIB" )

 

We then include the same abstract  Renderer class from the previous tutorial:

 

// Define a Renderer Base Interface

struct Renderer

{

      virtual void Init( HWND& hwnd ) = 0;

      virtual void Close() = 0;

      virtual bool IsRunning() = 0;

      virtual void BeginScene() = 0;

      virtual void EndScene() = 0;

};

 

Now, we define the RendererGL class. This class tries to encapsulate the OpenGL functionality and to provide a simple interface. The main API encapsulator methods are: Close(), Init(), BeginScene(), EndScene(). The method Get() is used as a singleton instance, providing only one instance of the class at a time. The method IsRunning() is used to know if the device was created ok. The method DoFrame() is located here temporarily as an example; its purpose is to show how the BeginScene()/EndScene() should be called.

 

class RendererGL

{

      public:

            virtual ~RendererGL()

            {

                  Close();

            }

 

            void Init( HWND& hhwnd )

            {

                  // If already initialized, skip

                  if( is_running )

                        return;

 

                  hwnd = hhwnd;

                  hdc = GetDC( hwnd );

 

                  // Set up pixel format description

                  PIXELFORMATDESCRIPTOR pfd;

                  ZeroMemory( &pfd, sizeof( pfd ) );

                  int pixelformat;

                  pfd.nSize = sizeof( PIXELFORMATDESCRIPTOR );                // Set the size of the structure

                  pfd.nVersion = 1;                                                 // Always set this to 1

                  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;

                  pfd.dwLayerMask = PFD_MAIN_PLANE;                           // We want the standard mask (this is ignored anyway)

                  pfd.iPixelType = PFD_TYPE_RGBA;                                   // We want RGB and Alpha pixel type

                  pfd.cColorBits = 24;                                 

                  pfd.cDepthBits = 32;                                 

                  pfd.cAccumBits = 0;                                                     // No special bitplanes needed

                  pfd.cStencilBits = 0;                                             // We desire no stencil bits

                  if( !( pixelformat = ChoosePixelFormat( hdc, &pfd ) ) )

                  {

                        MessageBox( NULL, "Init() ChoosePixelFormat failed", "Error", MB_OK );

                        return;

                  }

 

                  if( !SetPixelFormat( hdc, pixelformat, &pfd ) )

                  {

                        MessageBox( NULL, "Init() SetPixelFormat failed", "Error", MB_OK );

                        return;

                  }

 

                  hrc = wglCreateContext( hdc );

                  if( !wglMakeCurrent( hdc, hrc ) )

                  {

                        MessageBox( NULL, "Init() wglMakeCurrent failed", "Error", MB_OK );

                        return;

                  }

 

                  is_running = true;

            }

 

            // Close Direct3d device and objects

            void Close()

            {

                  if( hrc )

                  {

                        wglMakeCurrent( NULL, NULL );

                        wglDeleteContext( hrc );

                        hrc = NULL;

                  }

 

                  if( hdc )

                  {

                        ReleaseDC( hwnd, hdc );

                        hdc = NULL;

                  }

 

                  is_running = false;

            }

 

            // Is the renderer running?

            bool IsRunning() { return is_running; }

 

            // Prepare Direct3d for drawing a new frame/scene

            void BeginScene()

            {

                  if( is_running )

                  {

                        glClearColor( 0, 0, 1, 0 );

                        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

                  }

            }

 

            // Flush and draw the data onscreen

            void EndScene()

            {

                  if( is_running )

                  {

                        glFlush();

                        SwapBuffers( hdc );

                  }

            }

 

            // Called each drawing frame

            void DoFrame()

            {

                  if( is_running )

                  {

                        this->BeginScene();

 

                        // Perform any rendering here...

 

                        this->EndScene();

                  }

            }

 

            // Gets the instance of the renderer

            static RendererGL& Get()

            {

                  static RendererGL rgl;

                  return rgl;

            }

 

      protected:

            bool is_running;

            HGLRC hrc;

            HDC hdc;

            HWND hwnd;

 

            // Constructor hidden here to avoid duplicity

            RendererGL() : is_running( false ), hrc( NULL ), hwnd( NULL ), hdc( NULL )

            {

            }

};

 

Now, on the Window Procedure, which is exactly the same as the previous tutorial:

// Windows Message Procedure

LRESULT WINAPI MsgProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )

{

    switch( msg )

    {

 

            case WM_PAINT:

                  {

                        // Do not draw over the Direct3d area if it is initialized

                        if( !RendererGL::Get().IsRunning() )

                        {

                              // Simply clear the window to white, and display a message centered

                              RECT rect;

                              PAINTSTRUCT ps;

                              HDC hdc = BeginPaint( hwnd, &ps );

                              GetClientRect( hwnd, &rect );

                              FillRect( hdc, &rect, CreateSolidBrush( 0x00ffffff ) );

                              SetTextColor( hdc, 0 );

                              ::DrawText( hdc, "Basic Window", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );

                              EndPaint( hwnd, &ps );

                        }

                        return 0;

                  }

 

            case WM_DESTROY:

                  {

                        // Close the Direct3d stuff since the window is closing/destroying

                        RendererGL::Get().Close();

                        PostQuitMessage( 0 );

                        return 0;

                  }

    }

 

    return DefWindowProc( hwnd, msg, wparam, lparam );

}

 

Finally, the WinMain function, which remains exactly the same:

// Main Win32 Entry Point

int WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )

{

      // Register the window class

      WNDCLASSEX wc = { sizeof( WNDCLASSEX ), CS_OWNDC, MsgProc, 0, 0, GetModuleHandle( NULL ), NULL, NULL,

                                    NULL, NULL, WINDOWS_CLASS, NULL };

      RegisterClassEx( &wc );

 

      // Create the application's window

      HWND hwnd = CreateWindow( WINDOWS_CLASS, WINDOWS_CAPTION, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT,

                        CW_USEDEFAULT, SCREEN_X, SCREEN_Y, GetDesktopWindow(), NULL, wc.hInstance, NULL );

 

      // Show the window

      ShowWindow( hwnd, SW_SHOWDEFAULT );

      UpdateWindow( hwnd );

 

      // After the window is created, initialize Direct3d

      RendererGL::Get().Init( hwnd );

 

      // Enter the message loop

      MSG msg;

      ZeroMemory( &msg, sizeof( msg ) );

 

      // Process all messages and draw while idle

      while( msg.message != WM_QUIT )

      {

            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )

            {

                  TranslateMessage( &msg );

                  DispatchMessage( &msg );

            }

            else

            {

                  // When Windows doesn't send a message to our application, execute a frame

                  RendererGL::Get().DoFrame();

            }

      }

 

      // Unregister the class

      UnregisterClass( WINDOWS_CLASS, wc.hInstance );

 

      return 0;

}