Click here to Skip to main content
15,879,474 members
Articles / Multimedia / OpenGL

GLFW: A (Simple) OpenGL Framework Library

Rate me:
Please Sign up or sign in to vote.
4.89/5 (24 votes)
23 Dec 2015CPOL11 min read 139.7K   4.3K   99   17
An easy to use library to quickly setup and run OpenGL applications.

GLFW Sample Image

Background

I will try to keep things as much simple as possible but I must suppose you have at least a basic knowledge on how to write a Win32 application and a minimal knowledge of OpenGL programming in Win32 environment.

Introduction

In my opinion, one of the most complicated (and/or boring) things to do when writing a OpenGL application is to prepare the needed environment. Even if the setup process of a plain Win32 application which draws a 3D scene on a window using OpenGL is quite easy when compared to the same process done using Direct3D, it involves a lot steps that can create some trouble to beginners. This problem can be brilliantly solved using the OpenGL Utility Toolkit library (best known as GLUT) which allows to setup and run an OpenGL application, starting from a plain Win32 console application, writing some functions, and doing some library routine calls. However, even if GLUT is really cool, it does not fit all possible situations: it does not have all the flexibility of a "clean" Win32 application, and it cannot be used for commercial applications. For this reason, I've decided to write my own framework/library which allows to simplify the application setup process of my OpenGL applications and provide them with some other useful stuff.

Introducing the GLFW library

The OpenGL (simple) Framework or GLFW is a static library which can be linked to your Win32 application to simplify the process needed to create and setup a window suitable to render a 3D or 2D scene using OpenGL. The library also provides you other useful features such as:

  • Support for multiple windows rendering on up to 10 different windows.
  • Automatic import of entry points of all OpenGL core functions, from version 1.2 up to the current implementation version of your system.
  • Utility functions to handle OpenGL extensions.

GLFW has been designed to be easy to use, and it is provided with a detailed documentation written in Compiled HTML format (CHM). This documentation provides the reference to all the \c glfwXXX() routines, and a programming guide which explains how to use the library. I've also added some sample application code that can be found into the \Examples directory.

The library is still in BETA phase so, the development of some of the above features is still in progress (or they need a deep test/optimization session); however, I think it is already usable, at least as a demonstration of its features. To get the list of current limitations and problems of the library, see the "Known Issues" pages in the documentation file.

About import of OpenGL v1.2+ routines

As you probably know, the Microsoft support for OpenGL has been stopped on version 1.1. However, you still can access the OpenGL version 1.2 and above routines from your graphic cards drivers, by using a special function called wglGetProcAddress() which is similar to the GetProcAddress() routine used to get entry points of functions contained in DLL libraries. Due to the high number of routines, it is a common practice to retrieve only the entry points of the OpenGL routines that we know will be used (or that have a higher probability to be used) in our application. In my opinion, this is another boring issue, so I've decided to let the GLFW library retrieve all the routine entry points for you: when the first window is created, the library detects which OpenGL version is supported by your system and automatically retrieve all the routine pointers up to that version, and they will have the same name as the related routine (i.e., the name of pointer to the glWindowPos2i() function will be glWindowPos2i). These pointers are available to be accessed by the user simply by including the glfw.h header file. (They are declared as extern inside the glfw_ext.h header which is included in glfw.h. Please do not include the glfw_ext.h header directly in your code!). Unsupported routine pointers and pointers to core OpenGL functions which belong to an OpenGL version that is not supported on your system are set to NULL so, always check for NULL-pointing conditions before use them to call the related function, or don't be surprised if your system crashes really often ;-)

GLFW tutorial

In this section, I will show an example usage of the GLFW: I will show you how to setup and run an OpenGL application using my library. The output generated will be a simple RGB-filled triangle in a black window; however, even if it is not really sophisticated, it will perfectly fit our requirement. The operation consists of various steps that we will analyze individually. Let's begin.

Step 1 - Create a Win32 project and include all the needed stuff

The first thing to do is to install the library. To do this, follow the instructions you will find in the "GLFW Installation" section of the library documentation. Once you have done that, create a new Win32 project (the procedure depends on the used compiler). The compiler will automatically generate a .cpp source file for you. Now, it is time to add all the needed stuff (libraries and header files). This can be done directly form the code:

// Include files
#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>

#include "glfw.h"    // GLFW library header file.

// Import libraries
#ifdef _DEBUG
    #pragma comment ( lib, "glfwVC6d.lib" )
#else
    #pragma comment ( lib, "glfwVC6r.lib" )
#endif

In the above code, we are using the Visual C++ 6.0 compiler and so we are linking the glfwVC6x.lib library variant. Notice that there is no link directive for the opengl32.lib and glu32.lib libraries, and there is no #include directive for standard OpenGL include files (gl.h and glu.h). This is because the GLFW library automatically links the OpenGL library for you, and all the needed header files are included inside the glfw.h header.

Step 2 - Create the application skeleton

As you probably know, all Win32 applications which display windows need at least two routines: the WinMain() function, which is the program entry point, and the Windows messages handler procedure. These two functions should be automatically created for you by the compiler when you have created the project. In all the cases, be sure that they look like the code below:

//
// Window message handler.
//
static LRESULT WINAPI MsgProc ( HWND hWnd, UINT iMsg, 
               WPARAM wParam, LPARAM lParam )
{
    switch( iMsg )
    {
        case WM_DESTROY:    // Destroy window.
            break;

        case WM_SIZE:    // Change the window size.
            break;

        case WM_PAINT:    // Repaint window.
            ValidateRect( hWnd, NULL );
            break;
    }

    return DefWindowProc ( hWnd, iMsg, wParam, lParam );

} // MsgProc()


//
// WinMain routine.
// 
INT APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, INT nCmdShow )
{
    MSG sMsg; 

    // Begin the messages pump.
    while ( GetMessage( &sMsg, NULL, 0, 0 ) )
    {
        TranslateMessage( &sMsg );
        DispatchMessage( &sMsg );
    }

    return 0;

} // WinMain()

If you take a look at the MsgProc() function, you will notice that there are three messages handler cases in the switch statement: WM_DESTROY, WM_SIZE, and WM_PAINT. These three messages are the ones we must necessarily take care of.

Step 3 - Create a window with GLFW

Now, we must add the code to create the window we will use to draw. This will be done using the routines provided by the GLFW library: the following is the new version of the WinMain() routine, modified to add the window creation code:

//
// WinMain routine.
// 
INT APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, INT nCmdShow )
{
    MSG sMsg; 

    // Initialize the GLFW library.
    glfwInit();

    GLWF_WINPAR    sWinPar;  // Window parameters data structure.
    GLFW_WCTXT    *pWinCntx;// Window Context pointer.

    // Initialize and fill the window parameters data structure:
    ZeroMemory( (PVOID)&sWinPar, sizeof( GLWF_WINPAR ) );
    sWinPar.m_iPosX    = 100;
    sWinPar.m_iPosY    = 100;
    sWinPar.m_iWidth = 800;
    sWinPar.m_iHeight = 600;
    sWinPar.m_iBpp = GLFW_32BPP;
    sWinPar.m_iZDepth = GLFW_16BPP;
    sWinPar.m_szTitle = _T( "My 1st GLFW application." );
    sWinPar.n_bFullScreen = FALSE;
    sWinPar.m_fpWindProc = MsgProc;

    // Create the window:
    glfwCreateWindow( &sWinPar, &pWinCntx );

    // Set the Window Context as current:
    glfwSetCurrWinContext( pWinCntx );

    // Begin the messages pump.
    while ( GetMessage( &sMsg, NULL, 0, 0 ) )
    {
        TranslateMessage( &sMsg );
        DispatchMessage( &sMsg );
    }

    return 0;

} // WinMain()

The above code will create a 800 x 600 x32 bpp blank window located on the screen, at position (100, 100). The first interesting thing to notice is the call to the glfwInit() routine. This function will initialize the GLFW library before starting to work with it. Now comes the code to create the window: the function declares a GLWF_WINPAR data structure (sWinPar) and a pointer to a GLFW_WCTXT data structure (pWinCntx). The sWinPar data structure is used to define the parameters of the window to be created: size, position, bit depths, title etc... Notice also that the pointer to the window message handler procedure MsgProc() that we created before is used to set the value of the m_fpWindProc field of the structure. Once the fields of this structure have been set with the desired values, it is time to call the glfwCreateWindow() function. This routine takes as parameters the pointer to the sWinPar structure and the pointer to the pWinCntx data structure pointer (yes, a pointer to a pointer to a data structure). The pWinCntx parameter will be used to return the pointer to the allocated Window Context to the caller. Once glfwCreateWindow() is called, the window is created. The last thing we need to do is to set the returned Window Context as the currently active one. This is done by the glfwSetCurrWinContext() routine call. Notice that in the above code sample, we have intentionally left out error checking. Remember to always check the values returned by the called glwfXXX() functions in your applications, or they can crash without any apparent reason. The current result of our efforts can be seen in the following picture:

Output of Tutorial step 3

Step 4 - Handle window messages

Now is the time to modify the MsgProc() routine, adding some code to handle the messages sent to our window. As told before, we must take care of at least the following three messages: WM_SIZE, WM_PAINT, and WM_DESTROY. The first two messages are sent to our window when it is resized by the user and when it is time to redraw the scene, respectively. We will define the two functions to be called in these cases, but we will delay their implementation until the next step (as they involve OpenGL code). We will instead focus on the last message: WM_DESTROY. This message is sent to our window when it is destroyed as the user has closed the application or because the glfwDestroyWindow() routine has been called. In this case, we must release the allocated Window Context, and to do this, we must use the glfwDestroyContext() routine. This function requires as argument the pointer to the Window Context to be released. This can be obtained from the window handler using the glfwGetWinContext() routine or, if we know that the Window Context to be released is the currently active one, using the glfwGetCurrWinContext() function. We will use the first way, and now we can update the code of our application:

//
// Used when window size is changed.
//
static void ChangeWindowSize ( GLsizei iWidth, GLsizei iHeight )
{
    // Do something..

} // ChangeWindowSize()


//
// Render the scene using OpenGL
//
static void RenderTheScene ( void )
{
    // Do something..

} // RenderTheScene()


//
// Window message handler.
//
static LRESULT WINAPI MsgProc ( HWND hWnd, UINT iMsg, 
               WPARAM wParam, LPARAM lParam )
{
    // Get the context of the window:
    GLFW_WCTXT *pContext;
    pContext = glfwGetWinContext( hWnd );

    switch( iMsg )
    {
        case WM_DESTROY:    // Destroy window.
            // Destroy the Window Context:
            glfwDestroyContext( pContext );
            
            // Send the quit message: 
            PostQuitMessage( 0 );
            break;

        case WM_SIZE:        // Change the window size.
            ChangeWindowSize( LOWORD(lParam), HIWORD(lParam) );
            break;

        case WM_PAINT:        // Repaint window.
            RenderTheScene();
            SwapBuffers( pContext->m_hDC );
            ValidateRect( pContext->m_hWin, NULL );
            break;
    }

    return DefWindowProc( hWnd, iMsg, wParam, lParam );

} // MsgProc()

Step 5 - Add OpenGL code

OK, now we have arrived at the fun part! We must write the code for the two routines we left blank before: ChangeWindowSize() and RenderTheScene(). Let's begin from ChangeWindowSize(): this function will be called when the user resizes the window. Here it is necessary to reset the projection matrix and the view port:

#define NEAR_PLANE    1.0
#define FAR_PLANE    200.0
#define FIELD_OF_VIEW    60.0

//
// Used when window size is changed.
//
static void ChangeWindowSize ( GLsizei iWidth, GLsizei iHeight )
{
    GLfloat    fAspectRatio;

    if ( iHeight == 0 )
        iHeight = 1;

    // Set the new view port:
    glViewport( 0, 0, iWidth, iHeight );

    // Setup the projection matrix:
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    fAspectRatio = ( (GLfloat)iWidth ) / ( (GLfloat)iHeight );
    gluPerspective( FIELD_OF_VIEW, fAspectRatio, 
                    NEAR_PLANE, FAR_PLANE );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    
    // Place the camera:
    gluLookAt( 0.0f, 0.0f, 80.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 );

} // ChangeWindowSize()

Now, it's time to write the rendering code. To give a simple example, our RenderTheScene() function will draw a colored triangle on the screen. Here is the code:

//
// Render the scene using OpenGL
//
static void RenderTheScene ( void )
{
    // Clear the screen.
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // Draw the scene:
    glBegin( GL_TRIANGLES );
        glColor3f( 1.0f, 0.0f, 0.0f );
        glVertex3f( -25.0f, -25.0f, 0.0f );

        glColor3f( 0.0f, 1.0f, 0.0f );
        glVertex3f( 0.0f, 25.0f, 0.0f );

        glColor3f( 0.0f, 0.0f, 1.0f );
        glVertex3f( 25.0f, -25.0f, 0.0f );
    glEnd();
    glFlush();

} // RenderTheScene()

The final thing to do is to add some code to setup the OpenGL environment in the WinMain() routine, immediately after the call to the glfwSetCurrWinContext() function:

...
// Set the Window Context as current:
glfwSetCurrWinContext( pWinCntx );

// Set the shade model:
glShadeModel( GL_SMOOTH );

// Set the clear screen color:
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

// Begin the messages pump.
...

Step 6 - Have Fun!

Now our application is complete (and this tutorial is complete too). You can see the result in the picture below. You can now modify the code to handle some more Windows messages, and maybe add a timer to animate the scene by rotating the triangle. Notice that if you wish to see the application run in full screen mode instead of in windowed mode, just change the value used in the m_fpWindProc field of the sWinPar structure from FALSE to TRUE. More details on this last issue can be found in the documentation in the "Write a Full Screen Application" section.

Final output of Tutorial

The demo application

The demo application will show you a really simple example of multiple window rendering using the GLFW library. It will create two different windows, both showing a spinning cube. Due to its simplicity, I think that examining the code is better than seeing it run on your PC (in future, I will write a more complex and interesting demo bit; for now, I think this one is enough).

Future development

As I've already mentioned, the development of the library is not yet complete as there are still a lot of things I need/wish to do. Even if I do not have a definitive plan on the improvements to add in version 1.01.00R (the first to be considered as official "release"), it will surely contain the fixes to all the points listed in the "Known issues" section of the documentation. I think I will also add a minimal support for vector math (at least, a way to calculate surface normals), maybe using the SSE2 technology...

Conclusions

I wish to point out that my intent is not to write something better than the GLUT library: I just whish to provide to all newbie OpenGL programmers an alternative way to speed up the setting up of OpenGL applications, keeping in tact all the power and flexibility of a pure Win32 application. If you take a look at the code of all the core routines of the GLFW library, you will notice that they make use of glXXX(), gluXXX(), and (obviously) wglXXX() functions, so you can consider GLFW as a sort of "High Level" wrapper for classic OS-related OpenGL routines. However, it also provides some other useful features (the list will increase in the next release of the library).

As I usually do in my job, I'm waiting for your feedback: you can contact me through the CodeProject comments system. Please send me all your doubts, questions, comments, suggestions, and bug reports.

Acknowledgements

A really, really big thank you to DoxyGen development group! You guys saved my life ;-)

Bibliography and web references

Here is the list of books I've used as reference during the development of the library:

  • Shreiner, Woo, Neider, Davis: "OpenGL Programming Guide 5th Edition", Addison- Wesley.
  • Shreider: "OpenGL Reference Manual, 4th Edition", Addison-Wesley.
  • Wright, Lipchak: "OpenGL SuperBible, 3rd Edition", SAMS.
  • Fosner: "OpenGL Programming for Windows 95 and Windows NT", Addison-Wesley.
  • LaMothe: "Tricks of Windows Game Programming Gurus, 2nd Edition", SAMS.

Web Resources:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Nextworks S.r.l.
Italy Italy
He's Born in 1976 in Carrara (Italy) and he works as Software developer since 2000.
His other interests and hobbies are Karate, Soccer, VideoGames (RPG, FPS, FlySims) and Mountain Trekking.

Comments and Discussions

 
Praiseit is neat Pin
Southmountain13-Feb-21 17:27
Southmountain13-Feb-21 17:27 
Questionuseless tutorial Pin
iani_3d27-Dec-15 1:16
iani_3d27-Dec-15 1:16 
AnswerRe: useless tutorial -> I KNOW! Pin
Simone Serponi27-Dec-15 4:55
Simone Serponi27-Dec-15 4:55 
QuestionThis is a sample for MS VS 8.0. For older versions useless. Pin
Petrov Vladimir24-Dec-15 20:11
Petrov Vladimir24-Dec-15 20:11 
AnswerRe: This is a sample for MS VS 8.0. For older versions useless. Pin
Simone Serponi25-Dec-15 0:19
Simone Serponi25-Dec-15 0:19 
QuestionMissing header files Pin
csthompsondo23-Dec-15 8:44
csthompsondo23-Dec-15 8:44 
AnswerRe: Missing header files Pin
Simone Serponi23-Dec-15 13:30
Simone Serponi23-Dec-15 13:30 
Generalsome problems Pin
liuming_hz19-Aug-10 23:48
liuming_hz19-Aug-10 23:48 
GeneralGLFW already exist: I will rename the project Pin
Simone Serponi8-Nov-06 22:25
Simone Serponi8-Nov-06 22:25 
GeneralGLFW already exists Pin
schmedly4-Nov-06 6:37
schmedly4-Nov-06 6:37 
GeneralRe: GLFW already exists Pin
Simone Serponi5-Nov-06 22:13
Simone Serponi5-Nov-06 22:13 
GeneralRe: GLFW already exists Pin
Simone Serponi8-Nov-06 22:26
Simone Serponi8-Nov-06 22:26 
Generalmissing headerfiles Pin
Taulie12-Sep-06 20:18
Taulie12-Sep-06 20:18 
GeneralRe: missing headerfiles Pin
Simone Serponi12-Sep-06 21:47
Simone Serponi12-Sep-06 21:47 
GeneralRe: missing headerfiles Pin
tk0livecn4-Dec-08 16:45
tk0livecn4-Dec-08 16:45 
GeneralA good article and some questions... Pin
Jun Du1-Sep-06 5:19
Jun Du1-Sep-06 5:19 
GeneralRe: A good article and some questions... Pin
Simone Serponi1-Sep-06 5:53
Simone Serponi1-Sep-06 5:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.