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;
}