OpenGL Tutorial Notes

All of the following tutorial notes are based upon those at GameTutorials.com (http://www.gametutorials.com/Tutorials/tutorials.htm)


Tutorial #1 - Triangle, C++ source file Main.cpp

  • A basic OpenGL program that creates a window with an option for full screen mode and with double buffering.
  • Reference: http://www.gametutorials.com/Tutorials/OpenGL/OpenGL_Pg1.htm
    
    //***********************************************************************//
    //                                                                       //
    // - "Talk to me like I'm a 3 year old!" Programming Lessons -           //
    //                                                                       //
    // $Author: Ben Humphrey digiben@gametutorilas.com                       //
    //                                                                       //
    // $Program: Triangle                                                    //
    //                                                                       //
    // $Description: Init OpenGL and Draw a triangle to the screen           //
    //                                                                       //
    // $Date: 3/3/01                                                         //
    //                                                                       //
    //***********************************************************************//
    
    // This is a compiler directive that includes libraries (For Visual Studio)
    // You can manually include the libraries in the "Project->settings" menu under
    // the "Link" tab. You need these libraries to compile this program.
    #pragma comment(lib, "opengl32.lib")
    #pragma comment(lib, "glu32.lib")
    
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <gl\gl.h>           // Header File For The OpenGL32 Library
    #include <gl\glu.h>          // Header File For The GLu32 Library
    
    #define SCREEN_WIDTH 800     // We want our screen width 800 pixels
    #define SCREEN_HEIGHT 600    // We want our screen height 600 pixels
    #define SCREEN_DEPTH 16      // We want 16 bits per pixel
    
    // Global variables
    bool g_bFullScreen = TRUE;   // Set full screen as default
    HWND g_hWnd;                 // This is the handle for the window
    RECT g_rRect;                // This holds the window dimensions
    HDC g_hDC;                   // General HDC - (handle to device context)
    HGLRC g_hRC;                 // General OpenGL_DC - Our Rendering Context for OpenGL
    HINSTANCE g_hInstance;       // This holds the global hInstance for UnregisterClass() in DeInit()
    
    // Function prototypes
    // The window proc which handles all of window's messages.
    LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    // This is our main rendering function prototype. It's up here for now so MainLoop() can call it.
    void RenderScene();
    
    ///////////////////////////////// CHANGE TO FULL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This changes the screen to FULL SCREEN
    /////
    ///////////////////////////////// CHANGE TO FULL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void ChangeToFullScreen()
    {
        DEVMODE dmSettings;    // Device Mode variable
        memset(&dmSettings,0,sizeof(dmSettings));    // Makes Sure Memory's Cleared
    
        // Get current settings -- This function fills our the settings
        // This makes sure NT and Win98 machines change correctly
        if(!EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dmSettings))
        {
            // Display error message if we couldn't get display settings
            MessageBox(NULL, "Could Not Enum Display Settings", "Error", MB_OK);
            return;
        }
        dmSettings.dmPelsWidth = SCREEN_WIDTH;      // Selected Screen Width
        dmSettings.dmPelsHeight = SCREEN_HEIGHT;    // Selected Screen Height
    
        // This function actually changes the screen to full screen
        // CDS_FULLSCREEN Gets Rid Of Start Bar.
        // We always want to get a result from this function to check if we failed
        int result = ChangeDisplaySettings(&dmSettings,CDS_FULLSCREEN); 
        // Check if we didn't recieved a good return message From the function
        if(result != DISP_CHANGE_SUCCESSFUL)
        {
            // Display the error message and quit the program
            MessageBox(NULL, "Display Mode Not Compatible", "Error", MB_OK);
            PostQuitMessage(0);
        }
    }
    
    ///////////////////////////////// CREATE MY WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function creates a window, but doesn't have a message loop
    /////
    ///////////////////////////////// CREATE MY WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle,
                        bool bFullScreen, HINSTANCE hInstance)
    {
        HWND hWnd;
        WNDCLASS wndclass;
        memset(&wndclass, 0, sizeof(WNDCLASS));               // Init the size of the class
        wndclass.style = CS_HREDRAW | CS_VREDRAW;             // Regular drawing capabilities
        wndclass.lpfnWndProc = WinProc;                       // Pass our function pointer as the window procedure
        wndclass.hInstance = hInstance;                       // Assign our hInstance
        wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);     // General icon
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);       // An arrow for the cursor
        wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);   // A white window
        wndclass.lpszClassName = "GameTutorials";             // Assign the class name
    
        RegisterClass(&wndclass);                             // Register the class
    
        if(bFullScreen && !dwStyle)    // Check if we wanted full screen mode
        {   // Set the window properties for full screen mode
            dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
            ChangeToFullScreen();    // Go to full screen
            ShowCursor(FALSE);       // Hide the cursor
        }
        else if(!dwStyle)    // Assign styles to the window depending on the choice
            dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    
        g_hInstance = hInstance; // Assign our global hInstance to the window's hInstance
    
        // Below, we need to adjust the window to it's true requested size. If we say we
        // want a window that is 800 by 600, that means we want the client rectangle to
        // be that big, not the entire window. If we go into window mode, it will cut off
        // some of the client rect and stretch the remaining which causes slow down. We fix this below.
        RECT rWindow;
        rWindow.left = 0;           // Set Left Value To 0
        rWindow.right = width;      // Set Right Value To Requested Width
        rWindow.top = 0;            // Set Top Value To 0
        rWindow.bottom = height;    // Set Bottom Value To Requested Height
        AdjustWindowRect( &rWindow, dwStyle, false);    // Adjust Window To True Requested Size
    
        // Create the window
        hWnd = CreateWindow("GameTutorials", strWindowName, dwStyle, 0, 0,
                            rWindow.right - rWindow.left, rWindow.bottom - rWindow.top, 
                            NULL, NULL, hInstance, NULL);
        if(!hWnd) return NULL;    // If we could get a handle, return NULL
    
        ShowWindow(hWnd, SW_SHOWNORMAL);    // Show the window
        UpdateWindow(hWnd);                 // Draw the window
        SetFocus(hWnd);                     // Sets Keyboard Focus To The Window 
    
        return hWnd;
    }
    
    ///////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function Handles the main game loop
    /////
    ///////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    WPARAM MainLoop()
    {
        MSG msg;
    
        while(1) // Do our infinite loop
        { // Check if there was a message
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
            { 
                if(msg.message == WM_QUIT) // If the message wasnt to quit
                    break;
                TranslateMessage(&msg); // Find out what the message does
                DispatchMessage(&msg); // Execute the message
            }
            else // if there wasn't a message
            { 
                // Do computationally expensive things here. We want to render the scene
                // every frame, so we call our rendering function here. Even though the scene
                // doesn't change, it will bottle neck the message queue if we don't do something.
                // Usually WaitMessage() is used to make sure the app doesn't eat up the CPU.
                RenderScene();
            } 
        }
        return(msg.wParam); // Return from the program
    }
    
    ///////////////////////////////// SET UP PIXEL FORMAT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function sets the pixel format for OpenGL.
    /////
    ///////////////////////////////// SET UP PIXEL FORMAT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    bool bSetupPixelFormat(HDC hdc) 
    { 
        PIXELFORMATDESCRIPTOR pfd;
        int pixelformat; 
        pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Set the size of the structure
        pfd.nVersion = 1; // Always set this to 1
    
        // Pass in the appropriate OpenGL flags
        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 = SCREEN_DEPTH;       // Here we use our #define for the color bits
        pfd.cDepthBits = SCREEN_DEPTH;       // Depthbits is ignored for RGBA, but we do it anyway
        pfd.cAccumBits = 0;                  // No special bitplanes needed
        pfd.cStencilBits = 0;                // We desire no stencil bits
    
        // This gets us a pixel format that best matches the one passed in from the device
        if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == FALSE ) 
        { 
            MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); 
            return FALSE; 
        } 
    
        // This sets the pixel format that we extracted from above
        if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE) 
        { 
            MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); 
            return FALSE; 
        } 
    return TRUE; // Return a success!
    }
    
    //////////////////////////// RESIZE OPENGL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function resizes the viewport for OpenGL.
    /////
    //////////////////////////// RESIZE OPENGL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void SizeOpenGLScreen(int width, int height) // Initialize The GL Window
    {
        if (height==0) // Prevent A Divide By Zero error
        {
            height=1; // Make the Height Equal One
        }
    
        glViewport(0,0,width,height); // Make our viewport the whole window
        // We could make the view smaller inside
        // Our window if we wanted too.
        // The glViewport takes (x, y, width, height)
        // This basically means, what our our drawing boundries
    
        glMatrixMode(GL_PROJECTION);    // Select The Projection Matrix
        glLoadIdentity();               // Reset The Projection Matrix
    
        // Calculate The Aspect Ratio Of The Window
        // The parameters are:
        // (view angle, aspect ration of the width to the height, 
        // The closest distance to the camera before it clips, 
        // FOV // Ratio // The farthest distance before it stops drawing)
        gluPerspective(45.0f,(GLfloat)width/(GLfloat)height, 1 ,150.0f);
    
        // * Note * - The farthest distance should be at least 1 if you don't want some
        // funny artifacts when dealing with lighting and distance polygons. This is a special
        // thing that not many people know about. If it's less than 1 it creates little flashes
        // on far away polygons when lighting is enabled.
    
        glMatrixMode(GL_MODELVIEW);    // Select The Modelview Matrix
        glLoadIdentity();              // Reset The Modelview Matrix
    }
    
    ///////////////////////////////// INITIALIZE GL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function handles all the initialization for OpenGL.
    /////
    ///////////////////////////////// INITIALIZE GL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void InitializeOpenGL(int width, int height) 
    { 
        g_hDC = GetDC(g_hWnd);    // This sets our global HDC
                                  // We don't free this hdc until the end of our program
    
        if (!bSetupPixelFormat(g_hDC))    // This sets our pixel format/information
            PostQuitMessage (0);          // If there's an error, quit
    
        // We need now to create a rendering context AFTER we setup the pixel format.
        // A rendering context is different that a device context (hdc), but that is
        // What openGL uses to draw/render to. Because openGL can be used on
        // Macs/Linux/Windows/etc.. It has it's on type of rendering context that is
        // The same for EACH operating system, but it piggy backs our HDC information
        g_hRC = wglCreateContext(g_hDC);    // This creates a rendering context from our hdc
        wglMakeCurrent(g_hDC, g_hRC);       // This makes the rendering context we just created the one we want to use
        SizeOpenGLScreen(width, height);    // Setup the screen translations and viewport
    }
    
    ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function initializes the game window.
    /////
    ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void Init(HWND hWnd)
    {
        g_hWnd = hWnd;                                      // Assign the window handle to a global window handle
        GetClientRect(g_hWnd, &g_rRect);                    // Assign the windows rectangle to a global RECT
        InitializeOpenGL(g_rRect.right, g_rRect.bottom);    // Init OpenGL with the global rect
    
        // *Hint* We will put all our game init stuff here
        // Some things include loading models, textures and network initialization
    }
    
    ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function renders the entire scene.
    /////
    ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void RenderScene() 
    {
        nt i=0; 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer
        glLoadIdentity();    // Reset The View
    
        // Position View Up Vector
        gluLookAt(0, 0, 6, 0, 0, 0, 0, 1, 0);    // This determines where the camera's position and view is
                                                 // The position has an X Y and Z. Right now, we are standing at (0, 0, 6)
                                                 // The view also has an X Y and Z. We are looking at the center of the axis (0, 0, 0)
                                                 // The up vector is 3D too, so it has an X Y and Z. We say that up is (0, 1, 0)
                                                 // Unless you are making a game like Descent(TM), the up vector can stay the same.
                                                 // Below we say that we want to draw triangles 
        glBegin (GL_TRIANGLES);    // This is our BEGIN to draw
            glVertex3f(0, 1, 0);   // Here is the top point of the triangle
            glVertex3f(-1, 0, 0);  // Here is the left point of the triangle
            glVertex3f(1, 0, 0);   // Here is the right point of the triangle
        glEnd(); // This is the END of drawing
        // The code above draws a triangle to those points and fills it in.
        // You can have as many points inside the BEGIN and END, but it must be in three's.
        // Try GL_LINES or GL_QUADS. Lines are done in 2's and Quads done in 4's.
    
        // Notice that the 3 vertices are defined in counter-clockwise order (CCW).
        // CCW is OpenGL's default order for "front facing" order.  Thus, when face culling
        // is enabled and the cull mode is set to GL_BACK to cull back facing triangles, this
        // triangle will be considered to be facing front and it will not be culled.  However,
        // if the viewpoint were moved (i.e., call gluPerspective with different parameters)
        // so that it looks towards the other (back) side of this triangle, it would be culled
        // and not rendered (or, alternately, if the triangle were rotated so that its back
        // side faced towards the look from point).
    
        SwapBuffers(g_hDC);    // Swap the backbuffers to the foreground
    }
    
    ///////////////////////////////// DE INIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function cleans up and then posts a quit message to the window
    /////
    ///////////////////////////////// DE INIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void DeInit()
    {
        if (g_hRC) 
        {
            wglMakeCurrent(NULL, NULL);    // This frees our rendering memory and sets everything back to normal
            wglDeleteContext(g_hRC);       // Delete our OpenGL Rendering Context 
        }
    
        if (g_hDC) 
            ReleaseDC(g_hWnd, g_hDC);      // Release our HDC from memory
    
        if(g_bFullScreen)                  // If we were in full screen
        {
            ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop
            ShowCursor(TRUE);              // Show Mouse Pointer
        }
    
        UnregisterClass("GameTutorials", g_hInstance);    // Free the window class
        PostQuitMessage (0);                              // Post a QUIT message to the window
    }
    ///////////////////////////////// WIN MAIN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function handles registering and creating the window.
    /////
    ///////////////////////////////// WIN MAIN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow)
    { 
        HWND hWnd;
    
        // Check if we want full screen or not
        if(MessageBox(NULL, "Click Yes to go to full screen (Recommended)", "Options",
                      MB_YESNO | MB_ICONQUESTION) == IDNO)
            g_bFullScreen = FALSE;
    
        // Create our window with our function we create that passes in the:
        // Name, width, height, any flags for the window, if we want fullscreen of not, and the hInstance
        hWnd = CreateMyWindow("www.GameTutorials.com - First OpenGL Program", SCREEN_WIDTH, SCREEN_HEIGHT,
                              0, g_bFullScreen, hInstance);
        // If we never got a valid window handle, quit the program
        if(hWnd == NULL) return TRUE;
    
        // INIT OpenGL
        Init(hWnd); 
    
        // Run our message loop and after it's done, return the result
        return MainLoop(); 
    }
    
    ///////////////////////////////// WIN PROC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function handles the window messages.
    /////
    ///////////////////////////////// WIN PROC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    LRESULT CALLBACK WinProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        LONG lRet = 0; 
        PAINTSTRUCT ps;
    
        switch (uMsg)
        { 
            case WM_SIZE:    // If the window is resized
                if(!g_bFullScreen)    // Don't worry about this if we are in full screen (otherwise may cause problems)
                {   // LoWord=Width, HiWord=Height
                    SizeOpenGLScreen(LOWORD(lParam),HIWORD(lParam));
                    GetClientRect(hWnd, &g_rRect);    // Get the window rectangle
                }
                break; 
    
            case WM_PAINT:    // If we need to repaint the scene
                BeginPaint(hWnd, &ps);    // Init the paint struct 
                EndPaint(hWnd, &ps);      // EndPaint, Clean up
                break;
    
            case WM_KEYDOWN:
                if(wParam == VK_ESCAPE) DeInit(); // Quit if we pressed ESCAPE
                break;
    
            case WM_DESTROY:    // If the window is destroyed
                DeInit();       // Release memory and restore settings
                break; 
    
            default:    // Return by default
                lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); 
                break; 
        } 
        return lRet;    // Return by default
    }
    
    /////////////////////////////////////////////////////////////////////////////////
    //
    // * QUICK NOTES * 
    //
    // (Be sure to include opengl32.lib glu32.lib in your Link tab
    // of the menu Project->Settings These are libraries that OpenGL needs to use.
    // You might have to download them from www.opengl.org
    // You can visit my site for a graphical tutorial on how to include libraries)
    //
    // That's all there is to initializing openGL!
    // Now that you can display a triangle to the screen,
    // you can go onto many other cool things from here.
    // A lot of this (most of it) might be confusing the first time.
    // The only stuff you should REALLY understand is the RenderScene() function.
    // This function is what draws everything. The rest of the stuff is init junk
    // that you will very rarely ever look at again. You might add a line here
    // and there, but mostly it stays the same. In the next tutorials, we will
    // eventually just stick it in another .cpp file so we can just focus on the
    // important stuff that we will most likely be changing a lot. This will take
    // the stress of being overwhelmed from the other stuff. Like I said,
    // if you can understand that you have a camera, and it points in a direction,
    // and that you plot points to make a triangle, you are good for now :)
    //
    // Here are the basic steps to initializing OpenGL.
    //
    // 1) If you want to go full screen, give the option too. Granted, the Full
    //    screen code doesn't have anything to do with OpenGL, but I would always
    //    encourage giving a full screen option. It goes faster when it's in full
    //    screen mode which helps out on slower computers.
    //
    // 2) Create our window with the full screen information. We need to check if
    //    the user wants full screen first because our window properties depend on it.
    //
    // 3) Next, we need to setup the pixel format. This allows us to use openGL in 
    //    our window. We can also specify double buffering, if we want to draw to the
    //    window, or a bitmap, and if we want to allow GDI function calls. This is
    //    necessary if we want to do anything with OpenGL. You will most likely never
    //    need to change the code in SetupPixelFormat(). It's a one time coded thing :)
    //
    // 4) After setting the pixel format, we need to create and set a rendering context.
    //    This is done with the 2 functions wglCreateContext() and wglCreateContext().
    //
    // 5) Lastly, we need to setup our screen translations. If we didn't use OpenGL 
    //    this part would be complicated and tricky. Luckily, OpenGL keeps us from
    //    all the math needed to do the translations. We just tell OpenGL how large
    //    our viewport is (usually the whole screen) and which perspective we want to look
    //    at out world. After that, we are all set and we just need to DRAW SOMETHING!
    //
    // We plotted 3 points in this tutorial. 3 points make up a triangle.
    // We used the GL_TRIANGLES flag to tell OpenGL to render triangles.
    // 
    // Like so:
    //
    // glBegin (GL_TRIANGLES); 
    //     glVertex3f(0, 1, 0);  // Top point 
    //     glVertex3f(-1, 0, 0); // Bottom left point
    //     glVertex3f(1, 0, 0);  // Bottom right point
    // glEnd();
    //
    // If we wanted 2 triangles, we could say:
    //
    // glBegin (GL_TRIANGLES); 
    //     glVertex3f(0, 1, 0);  // Top point 
    //     glVertex3f(-1, 0, 0); // Bottom left point
    //     glVertex3f(1, 0, 0);  // Bottom right point
    //
    //     glVertex3f(0, 1, 1);  // Top point 
    //     glVertex3f(-1, 0, 1); // Bottom left point
    //     glVertex3f(1, 0, 1);  // Bottom right point
    // glEnd();
    //
    // Here is a horrible attempt to draw the 3D axis's
    //
    //             Y
    //             |
    //             |
    //             |________ X
    //              \
    //               \
    //                Z
    //
    // This is drawn in a rotated manner so you can see the Z axis.
    // Otherwise it would be coming out of the screen and you couldn't see it.
    // Positive Z comes out of the screen towards us, where -z goes into the screen
    // Positive X is on the right, negative X is on the left
    // Positive Y goes straight up, where negative Y goes straight down
    //
    // So here is what our triangle looks like with the axis
    //             Y
    //             |
    //            /|\
    //           /_|_\______ X
    //              \
    //               \
    //                Z
    //
    // As you can see, our triangle is half on the negative X side 
    // and half on the positive X side. It is also 0 on the Z axis,
    // so really, it's really seen as 2D more than 3D because we
    // aren't using the third dimension, which is Z.
    //
    // The next tutorial will be a lot smaller in comments because I will
    // chop off all the stuff that I already explain, so then we can just focus
    // on the RenderScene() function, rather than all the Init stuff. (Blah!)
    //
    // Once again, if it seems overwhelming.. it will get easier and MUCH more fun! 
    //
    // As for messing around with this tutorial, try changing camera positions
    // to see how it effects your view. Also, try plotting in other points and
    // other triangles/lines/Quads (rectangles) to see what you can create so far.
    //
    // One last note about this code. It might be a good idea, if you don't
    // understand anything about 3D or matrices (matrix math) to go on the internet
    // And do a little investigating and learning. It couldn't hurt. I will teach you
    // all the math you will need to know, but the more versed you are in math, the better.
    // If you are not good at math, or at least you don't know anything over algebra/geometry,
    // you are going to get a little frustrated. You will want to be pretty comfortable
    // with cosine and sine, as well as plane and vector math. You will want to know what
    // a vector is, and what purpose it serves. On my site I will most likely have a page
    // dedicated to teaching 3D concepts, but if not when you read this, use the internet.
    //
    // Don't get scared if you are not a math person, I wasn't really when I first started
    // doing 3D programming, but you learn REALLY fast, and it's fun when you use math
    // to do something cool. Yah, you heard me, math can be fun!
    //
    // 3D programming is by FAR the coolest thing to program.
    //
    //
    // Ben Humphrey (DigiBen)
    // Game Programmer
    // DigiBen@GameTutorials.com
    // Co-Web Host of www.GameTutorials.com
    //
    //

Tutorial #2 - FramesPerSecond, C++ source files: main.h, Init.cpp, Main.cpp

  • An OpenGL program that creates a window, renders an animated rotating triangle, and displays the frame rate in the window title bar.
  • Reference: http://www.gametutorials.com/Tutorials/OpenGL/OpenGL_Pg1.htm
  • File: main.h
    #ifndef _MAIN_H
    #define _MAIN_H
    
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <gl\gl.h>               // Header File For The OpenGL32 Library
    #include <gl\glu.h>              // Header File For The GLu32 Library
    
    #define SCREEN_WIDTH 800         // We want our screen width 800 pixels
    #define SCREEN_HEIGHT 600        // We want our screen height 600 pixels
    #define SCREEN_DEPTH 16          // We want 16 bits per pixel
    
    extern bool g_bFullScreen;       // Set full screen as default
    extern HWND g_hWnd;              // This is the handle for the window
    extern RECT g_rRect;             // This holds the window dimensions
    extern HDC g_hDC;                // General HDC - (handle to device context)
    extern HGLRC g_hRC;              // General OpenGL_DC - Our Rendering Context for OpenGL
    extern HINSTANCE g_hInstance;    // This holds our window hInstance
    
    // This is our MAIN() for windows
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow);
    
    // The window proc which handles all of window's messages.
    LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    // This controls our main program loop
    WPARAM MainLoop();
    
    // This changes the screen to full screen mode
    void ChangeToFullScreen();
    
    // This is our own function that makes creating a window modular and easy
    HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle,
                        bool bFullScreen, HINSTANCE hInstance);
    
    // This allows us to configure our window for OpenGL and backbuffered
    bool bSetupPixelFormat(HDC hdc);
    
    // This inits our screen translations and projections
    void SizeOpenGLScreen(int width, int height);
    
    // This sets up OpenGL
    void InitializeOpenGL(int width, int height);
    
    // This initializes the whole program
    void Init(HWND hWnd);
    
    // This draws everything to the screen
    void RenderScene();
    
    // This frees all our memory in our program
    void DeInit();
    
    #endif 
    
    /////////////////////////////////////////////////////////////////////////////////
    //
    // * QUICK NOTES * 
    //
    // Nothing new was added to this file concerning this tutorial
    //
    //
    // Ben Humphrey (DigiBen)
    // Game Programmer
    // DigiBen@GameTutorials.com
    // Co-Web Host of www.GameTutorials.com
    //
    //
    /////////////////////////////////////////////////////////////////////////////////
  • File: Init.cpp
    //***********************************************************************//
    //                                                                       //
    // - "Talk to me like I'm a 3 year old!" Programming Lessons -           //
    //                                                                       //
    // $Author: Ben Humphrey digiben@gametutorials.com                       //
    //                                                                       //
    // $Program: Frames Per Second                                           //
    //                                                                       //
    // $Description: Demonstrates calculating the frames per second          //
    //                                                                       //
    // $Date: 1/03/02                                                        //
    //                                                                       //
    //***********************************************************************//
    
    // Here is the function call tree for this file's functions
    // (excluding calls to Win32 functions)
    // Note: Several functions are used for the sake of using modular design,
    //       but except for SizeOpenGLScreen and DeInit they could have just
    //       as well been placed into one WinMain function (e.g., reference:
    //       "3D Games" textbook Appendix A.12 pp 775-777).
    //
    //                                   winMain
    //                      ____________/   |   \_________
    //                     /                |             \
    //             CreateMyWindow          Init         MainLoop
    //                    |                 | 
    //         ChangeToFullScreen    InitializeOpenGL
    //                                      | 
    //                               bSetupPixelFormat
    //                                      | 
    //                               SizeOpenGLScreen
    //
    //
    // (in file Main.cpp)             WndProc
    //                               /       \
    //                   SizeOpenGLScreen   DeInit
    //
    
    
    #include "main.h"
    
    ///////////////////////////////// WIN MAIN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function handles registering and creating the window.
    /////
    ///////////////////////////////// WIN MAIN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow)
    { 
        HWND hWnd;
    
        // Set the full screen option to false since you can't view the FPS in full screen.
        g_bFullScreen = false;
    
        // Create our window with our function we create that passes in the:
        // Name, width, height, any flags for the window, if we want fullscreen of not, and the hInstance
        hWnd = CreateMyWindow("First OpenGL Program", SCREEN_WIDTH, SCREEN_HEIGHT, 0,
                              g_bFullScreen, hInstance);
    
        // If we never got a valid window handle, quit the program
        if(hWnd == NULL) return TRUE;
    
        // INIT OpenGL
        Init(hWnd); 
    
        // Run our message loop and after it's done, return the result
        return MainLoop(); 
    }
    
    ///////////////////////////////// CREATE MY WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function creates a window, but doesn't have a message loop
    /////
    ///////////////////////////////// CREATE MY WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle,
                        bool bFullScreen, HINSTANCE hInstance)
    {
        HWND hWnd;
        WNDCLASS wndclass;
    
        memset(&wndclass, 0, sizeof(WNDCLASS));                // Init the size of the class
        wndclass.style = CS_HREDRAW | CS_VREDRAW;              // Regular drawing capabilities
        wndclass.lpfnWndProc = WinProc;                        // Pass our function pointer as the window procedure
        wndclass.hInstance = hInstance;                        // Assign our hInstance
        wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);      // General icon
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);        // An arrow for the cursor
        wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);    // A white window
        wndclass.lpszClassName = "GameTutorials";              // Assign the class name
        RegisterClass(&wndclass);                              // Register the class
    
        if(bFullScreen && !dwStyle)  // Check if we wanted full screen mode
        {   // Set the window properties for full screen mode
            dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
            ChangeToFullScreen();    // Go to full screen
            ShowCursor(FALSE);       // Hide the cursor
        }
        else if(!dwStyle)     // Assign styles to the window depending on the choice
            dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    
        g_hInstance = hInstance;     // Assign our global hInstance to the window's hInstance
        RECT rWindow;
        rWindow.left = 0;            // Set Left Value To 0
        rWindow.right = width;       // Set Right Value To Requested Width
        rWindow.top = 0;             // Set Top Value To 0
        rWindow.bottom = height;     // Set Bottom Value To Requested Height
        AdjustWindowRect( &rWindow, dwStyle, false);     // Adjust Window To True Requested Size
    
        // Create the window
        hWnd = CreateWindow("GameTutorials", strWindowName, dwStyle, 0, 0,
                            rWindow.right - rWindow.left, rWindow.bottom - rWindow.top, 
                            NULL, NULL, hInstance, NULL);
        if(!hWnd) return NULL; // If we could get a handle, return NULL
    
        ShowWindow(hWnd, SW_SHOWNORMAL);     // Show the window
        UpdateWindow(hWnd);                  // Draw the window
        SetFocus(hWnd);                      // Sets Keyboard Focus To The Window 
    
        return hWnd;
    }
    
    
    ///////////////////////////////// CHANGE TO FULL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This changes the screen to FULL SCREEN
    /////
    ///////////////////////////////// CHANGE TO FULL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void ChangeToFullScreen()
    {
        DEVMODE dmSettings;     // Device Mode variable
        memset(&dmSettings,0,sizeof(dmSettings));     // Makes Sure Memory's Cleared
    
        // Get current settings -- This function fills our the settings
        // This makes sure NT and Win98 machines change correctly
        if(!EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dmSettings))
        {
            // Display error message if we couldn't get display settings
            MessageBox(NULL, "Could Not Enum Display Settings", "Error", MB_OK);
            return;
        }
        dmSettings.dmPelsWidth = SCREEN_WIDTH;       // Selected Screen Width
        dmSettings.dmPelsHeight = SCREEN_HEIGHT;     // Selected Screen Height
    
        // This function actually changes the screen to full screen
        // CDS_FULLSCREEN Gets Rid Of Start Bar.
        // We always want to get a result from this function to check if we failed
        int result = ChangeDisplaySettings(&dmSettings,CDS_FULLSCREEN); 
        // Check if we didn't receive a good return message From the function
        if(result != DISP_CHANGE_SUCCESSFUL)
        {
            // Display the error message and quit the program
            MessageBox(NULL, "Display Mode Not Compatible", "Error", MB_OK);
            PostQuitMessage(0);
        }
    }
    
    ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function initializes the game window.
    /////
    ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void Init(HWND hWnd)
    {
        g_hWnd = hWnd;    // Assign the window handle to a global window handle
        GetClientRect(g_hWnd, &g_rRect);    // Assign the windows rectangle to a global RECT
        InitializeOpenGL(g_rRect.right, g_rRect.bottom);    // Init OpenGL with the global rect
    }
    
    ///////////////////////////////// INITIALIZE GL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function handles all the initialization for OpenGL.
    /////
    ///////////////////////////////// INITIALIZE GL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void InitializeOpenGL(int width, int height) 
    { 
        g_hDC = GetDC(g_hWnd);    // This sets our global HDC
        // We don't free this hdc until the end of our program
    
        if (!bSetupPixelFormat(g_hDC))    // This sets our pixel format/information
        PostQuitMessage (0);              // If there's an error, quit
    
        // We need now to create a rendering context AFTER we setup the pixel format.
        // A rendering context is different that a device context (hdc), but that is
        // What openGL uses to draw/render to. Because openGL can be used on
        // Macs/Linux/Windows/etc.. It has it's on type of rendering context that is
        // The same for EACH operating system, but it piggy backs our HDC information
        g_hRC = wglCreateContext(g_hDC);    // This creates a rendering context from our hdc
        wglMakeCurrent(g_hDC, g_hRC);       // This makes the rendering context we just created the one we want to use
        SizeOpenGLScreen(width, height);    // Setup the screen translations and viewport
    }
    
    ///////////////////////////////// SET UP PIXEL FORMAT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function sets the pixel format for OpenGL.
    /////
    ///////////////////////////////// SET UP PIXEL FORMAT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    bool bSetupPixelFormat(HDC hdc) 
    { 
        PIXELFORMATDESCRIPTOR pfd; 
        int pixelformat;
        pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);    // Set the size of the structure
        pfd.nVersion = 1;    // Always set this to 1
                             // Pass in the appropriate OpenGL flags
        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 = SCREEN_DEPTH;       // Here we use our #define for the color bits
        pfd.cDepthBits = SCREEN_DEPTH;       // Depthbits is ignored for RGBA, but we do it anyway
        pfd.cAccumBits = 0;                  // No special bitplanes needed
        pfd.cStencilBits = 0;                // We desire no stencil bits
    
        // This gets us a pixel format that best matches the one passed in from the device
        if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == FALSE ) 
        { 
            MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); 
            return FALSE; 
        } 
    
        // This sets the pixel format that we extracted from above
        if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE) 
        { 
            MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); 
            return FALSE; 
        } 
        return TRUE; // Return a success!
    }
    
    //////////////////////////// RESIZE OPENGL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function resizes the viewport for OpenGL.
    ///// It is called at program initialization and also from MainLoop (WndProc)
    ///// whenever the screen window is resized.
    /////
    //////////////////////////// RESIZE OPENGL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void SizeOpenGLScreen(int width, int height)
    {
        if (height==0)    // Prevent A Divide By Zero error
        {
            height=1;     // Make the Height Equal One
        }
    
        glViewport(0,0,width,height);    // Make our viewport the whole window
                                         // We could make the view smaller inside
                                         // Our window if we wanted too.
                                         // The glViewport takes (x, y, width, height)
                                         // This basically means, what our our drawing boundries
    
        glMatrixMode(GL_PROJECTION);    // Select The Projection Matrix
        glLoadIdentity();               // Reset The Projection Matrix
    
        // Calculate The Aspect Ratio Of The Window
        // The parameters are:
        // (view angle, aspect ration of the width to the height, 
        // The closest distance to the camera before it clips, 
        // FOV // Ratio // The farthest distance before it stops drawing)
        gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1 ,150.0f);
    
        glMatrixMode(GL_MODELVIEW);    // Select The Modelview Matrix
        glLoadIdentity();              // Reset The Modelview Matrix
    }
    
    ///////////////////////////////// DE INIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function cleans up and then posts a quit message to the window
    /////
    ///////////////////////////////// DE INIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void DeInit()
    {
        if (g_hRC) 
        {
            wglMakeCurrent(NULL, NULL);    // This frees our rendering memory and sets everything back to normal
            wglDeleteContext(g_hRC);       // Delete our OpenGL Rendering Context 
        }
    
        if (g_hDC) 
            ReleaseDC(g_hWnd, g_hDC);    // Release our HDC from memory
    
        if(g_bFullScreen)    // If we were in full screen
        {
            ChangeDisplaySettings(NULL,0);    // If So Switch Back To The Desktop
            ShowCursor(TRUE);                 // Show Mouse Pointer
        }
        UnregisterClass("GameTutorials", g_hInstance);    // Free the window class
        PostQuitMessage (0);    // Post a QUIT message to the window
    }
    
    /////////////////////////////////////////////////////////////////////////////////
    //
    // * QUICK NOTES * 
    //
    // Nothing new was added to this file concerning this tutorial, except we took out
    // full screen capabilities because the FPS can't be see in full screen, only in
    // window mode. I decided not to use rendered fonts to make the tutorial less
    // complicated. 
    //
    //
    // Ben Humphrey (DigiBen)
    // Game Programmer
    // DigiBen@GameTutorials.com
    // Co-Web Host of www.GameTutorials.com
    //
    //
  • File: Main.cpp
    //***********************************************************************//
    //                                                                       //
    // - "Talk to me like I'm a 3 year old!" Programming Lessons -           //
    //                                                                       //
    // $Author: Ben Humphrey digiben@gametutorials.com                       //
    //                                                                       //
    // $Program: Frames Per Second                                           //
    //                                                                       //
    // $Description: Demonstrates calculating the frames per second          //
    //                                                                       //
    // $Date: 1/03/02                                                        //
    //                                                                       //
    //***********************************************************************//
    
    // This is a compiler directive that includes libraries (For Visual Studio)
    // You can manually include the libraries in the "Project->settings" menu under
    // the "Link" tab. You need these libraries to compile this program.
    #pragma comment(lib, "opengl32.lib")
    #pragma comment(lib, "glu32.lib")
    
    #include "main.h" // This includes our header file
    
    // This code was taken from the Color triangle tutorial. The tutorial demonstrates
    // how to calculate the amount of frames your program renders a second (fps). There is
    // a spinning triangle in the center of the screen to demonstrate something going on. The
    // frames per second are displayed ONLY in window mode in the window's title bar.
    
    // Global variables
    bool g_bFullScreen = true;    // Set full screen as default
    HWND g_hWnd;                  // This is the handle for the window
    RECT g_rRect;                 // This holds the window dimensions
    HDC g_hDC;                    // General HDC - (handle to device context)
    HGLRC g_hRC;                  // General OpenGL_DC - Our Rendering Context for OpenGL
    HINSTANCE g_hInstance;        // This holds the global hInstance for UnregisterClass() in DeInit()
    
    ///////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function Handles the main game loop
    /////
    ///////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    WPARAM MainLoop()
    {
        MSG msg;
        while(1)    // Do our infinite loop
        {   // Check if there was a message
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
            { 
                if(msg.message == WM_QUIT)    // If the message wasnt to quit
                    break;
                TranslateMessage(&msg);       // Find out what the message does
                DispatchMessage(&msg);        // Execute the message
            }
            else    // if there wasn't a message
            { 
                RenderScene();    // Render the scene every frame so it calculates the frame rate
            } 
        }
    
        return(msg.wParam);    // Return from the program
    }
    /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
    
    ///////////////////////////////// CALCULATE FRAMES PER SECOND \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function calculates the frames we render each second and displays it to the window title
    /////
    ///////////////////////////////// CALCULATE FRAMES PER SECOND \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void CalculateFrameRate()
    {
        // Below we create a bunch of static variables because we want to keep the information
        // in these variables after the function quits. We could make these global but that would
        // be somewhat messy and superfluous. Note, that normally you don't want to display this to
        // the window title bar. This is because it's slow and doesn't work in full screen.
        // Try using the 3D/2D font's. You can check out the tutorials at www.gametutorials.com.
        static float framesPerSecond = 0.0f;    // This will store our fps
        static float lastTime = 0.0f;           // This will hold the time from the last frame
        static char strFrameRate[50] = {0};     // We will store the string here for the window title
    
        // Here we get the current tick count and multiply it by 0.001 to convert it from milliseconds to seconds.
        // GetTickCount() returns milliseconds (1000 ms = 1 second) so we want something more intuitive to work with.
        float currentTime = GetTickCount() * 0.001f; 
    
        // Increase the frame counter
        ++framesPerSecond;
    
        // Now we want to subtract the current time by the last time that was stored. If it is greater than 1
        // that means a second has passed and we need to display the new frame rate. Of course, the first time
        // will always be greater than 1 because lastTime = 0. The first second will NOT be true, but the remaining
        // ones will. The 1.0 represents 1 second. Let's say we got 12031 (12.031) from GetTickCount for the currentTime,
        // and the lastTime had 11230 (11.230). Well, 12.031 - 11.230 = 0.801, which is NOT a full second. So we try again
        // the next frame. Once the currentTime - lastTime comes out to be greater than a second (> 1), we calculate the
        // frames for this last second.
        if( currentTime - lastTime > 1.0f )
        {
            // Here we set the lastTime to the currentTime. This will be used as the starting point for the next second.
            // This is because GetTickCount() counts up, so we need to create a delta that subtract the current time from.
            lastTime = currentTime;
    
            // Copy the frames per second into a string to display in the window title bar
            sprintf(strFrameRate, "Current Frames Per Second: %d", int(framesPerSecond));
    
            // Set the window title bar to our string
            SetWindowText(g_hWnd, strFrameRate);
    
            // Reset the frames per second
            framesPerSecond = 0;
        }
    }
    /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
    ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function renders the entire scene.
    /////
    ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    void RenderScene() 
    {
        int i=0; 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer
        glLoadIdentity();                                      // Reset The matrix
    
        // Position View Up Vector
        gluLookAt(0, 0, 6, 0, 0, 0, 0, 1, 0); // This determines where the camera's position and view is
    
        /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
        // Here we create a static variable that will count forever. This is used for the rotation degree
        static float rotY = 0;
        // Pass in our current rotation value to glRotatef() for rotation around the Y axis (0, 1, 0)
        glRotatef(rotY, 0, 1, 0);
        // Increase the rotation by 2 degrees
        rotY += 2;
    
        /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
        // Below we say that we want to draw triangles
        glBegin (GL_TRIANGLES);       // This is our BEGIN to draw
            glColor3ub(255, 0, 0);    // Make the top vertex RED
            glVertex3f(0, 1, 0);      // Here is the top point of the triangle
            glColor3ub(0, 255, 0);    // Make the left vertex GREEN
            glVertex3f(-1, 0, 0);     // Here is the left point of the triangle
            glColor3ub(0, 0, 255);    // Make the right vertex BLUE
            glVertex3f(1, 0, 0);      // Here is the right point of the triangle
        glEnd();    // This is the END of drawing
    
        SwapBuffers(g_hDC); // Swap the backbuffers to the foreground
    
        /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
        // Call our new function to count and calculate the frames for each second.
        // This needs to be called every time the screen renders so we get a correct frame rate.
        // Because of its static variables inside, we don't need any globals or outside variables.
        CalculateFrameRate();
        /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
    }
    
    ///////////////////////////////// WIN PROC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    ///// This function handles the window messages.
    /////
    ///////////////////////////////// WIN PROC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    LRESULT CALLBACK WinProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        LONG lRet = 0; 
        PAINTSTRUCT ps;
    
        switch (uMsg)
        { 
            case WM_SIZE:    // If the window is resized
                if(!g_bFullScreen)    // Do this only if we are NOT in full screen
                {
                    SizeOpenGLScreen(LOWORD(lParam),HIWORD(lParam));   // LoWord=Width, HiWord=Height
                    GetClientRect(hWnd, &g_rRect);                     // Get the window rectangle
                }
                break; 
    
            case WM_PAINT:    // If we need to repaint the scene
                BeginPaint(hWnd, &ps);    // Init the paint struct 
                EndPaint(hWnd, &ps);      // EndPaint, Clean up
                break;
    
            case WM_KEYDOWN:
                if(wParam == VK_ESCAPE) DeInit(); // Quit if we pressed ESCAPE
                    break;
    
            case WM_DESTROY:    // If the window is destroyed
                DeInit();       // Release memory and restore settings
                break; 
    
            default:    // Return by default
                lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); 
                break; 
        } 
    
        return lRet;    // Return by default
    }
    
    /////////////////////////////////////////////////////////////////////////////////
    //
    // * QUICK NOTES * 
    //
    // Pretty easy huh? This tutorial just shows a simple way to calculate the frames per second.
    // The best way to do this would be to use QueryPerformanceCounter(). QueryPerformanceCounter()
    // is the most accurate way to check the system clock, but isn't absolutely necessary.
    //
    // Keep in mind that you don't ideally want to show the frames per second in the title bar.
    // This slows down the application a bit, so it is best to use a font that is rendered in the view port.
    //
    // Another thing to recognize is that OpenGL will only render 60 frames per second if your monitor's mhz
    // is set to 60 mhz, etc... If you want to have higher frames rates you will need to increase your
    // monitor frequency. This is because OpenGL waits for the synchronization of the monitor before rendering
    // each frame. 
    //
    // Once again, a tutorial that every graphics programmer must have.
    //
    // Enjoy!
    //
    //
    // Ben Humphrey (DigiBen)
    // Game Programmer
    // DigiBen@GameTutorials.com
    // Co-Web Host of www.GameTutorials.com
    //
    /////////////////////////////////////////////////////////////////////////////////

Up