3D C/C++ tutorials - OpenGL 1.5 - Vertex buffer object (VBO)
3D C/C++ tutorials -> OpenGL 1.5 -> Vertex buffer object (VBO)
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
To compile and run these tutorials some or all of these libraries are required: FreeImage 3.16.0, GLEW 1.11.0, GLUT 3.7.6 / GLUT for Dev-C++, GLM 0.9.5.4
win32_opengl_window.h
#include <windows.h>
#include <math.h>

#include "string.h"

#include <gl/glew.h> // http://glew.sourceforge.net/
#include <gl/wglew.h>

#include <FreeImage.h> // http://freeimage.sourceforge.net/

// ----------------------------------------------------------------------------------------------------------------------------

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glew32.lib")
#pragma comment(lib, "FreeImage.lib")

// ----------------------------------------------------------------------------------------------------------------------------

extern CString ModuleDirectory, ErrorLog;

// ----------------------------------------------------------------------------------------------------------------------------

extern int gl_max_texture_size, gl_max_texture_max_anisotropy_ext;

// ----------------------------------------------------------------------------------------------------------------------------

class CTexture
{
protected:
    GLuint Texture;

public:
    CTexture();
    ~CTexture();

    operator GLuint ();

    bool LoadTexture2D(char *FileName);
    void Destroy();

private:
    FIBITMAP *GetBitmap(char *FileName, int &Width, int &Height, int &BPP);
};

// ----------------------------------------------------------------------------------------------------------------------------

class COpenGLRenderer
{
protected:
    int Width, Height;

protected:
    CTexture Texture;
    GLuint VBO;

public:
    COpenGLRenderer();
    ~COpenGLRenderer();

    bool Init();
    void Render(float FrameTime);
    void Resize(int Width, int Height);
    void Destroy();
};

// ----------------------------------------------------------------------------------------------------------------------------

class COpenGLView
{
protected:
    char *Title;
    int Width, Height, Samples;
    HWND hWnd;
    HGLRC hGLRC;

public:
    COpenGLView();
    ~COpenGLView();

    bool Init(HINSTANCE hInstance, char *Title, int Width, int Height, int Samples);
    void Show(bool Maximized = false);
    void MessageLoop();
    void Destroy();

    void OnPaint();
    void OnSize(int Width, int Height);
};

// ----------------------------------------------------------------------------------------------------------------------------

LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);

// ----------------------------------------------------------------------------------------------------------------------------

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR sCmdLine, int iShow);
win32_opengl_window.cpp
#include "win32_opengl_window.h"

// ----------------------------------------------------------------------------------------------------------------------------

int gl_max_texture_size = 0, gl_max_texture_max_anisotropy_ext = 0;

// ----------------------------------------------------------------------------------------------------------------------------

CTexture::CTexture()
{
    Texture = 0;
}

CTexture::~CTexture()
{
}

CTexture::operator GLuint ()
{
    return Texture;
}

bool CTexture::LoadTexture2D(char *FileName)
{
    CString DirectoryFileName = ModuleDirectory + FileName;

    int Width, Height, BPP;

    FIBITMAP *dib = GetBitmap(DirectoryFileName, Width, Height, BPP);

    if(dib == NULL)
    {
        ErrorLog.Append("Error loading texture " + DirectoryFileName + "!\r\n");
        return false;
    }

    GLenum Format = 0;

    if(BPP == 32) Format = GL_BGRA;
    if(BPP == 24) Format = GL_BGR;

    if(Format == 0)
    {
        ErrorLog.Append("Unsupported texture format (%s)!\r\n", FileName);
        FreeImage_Unload(dib);
        return false;
    }

    BYTE *Data = FreeImage_GetBits(dib);

    if(!GLEW_VERSION_1_2)
    {
        int Pitch = FreeImage_GetPitch(dib);

        if(Format == GL_BGRA) Format = GL_RGBA;
        if(Format == GL_BGR) Format = GL_RGB;

        int bpp = BPP / 8;

        BYTE *line = Data;

        for(int y = 0; y < Height; y++)
        {
            BYTE *pixel = line;

            for(int x = 0; x < Width; x++)
            {
                BYTE Temp = pixel[0];
                pixel[0] = pixel[2];
                pixel[2] = Temp;

                pixel += bpp;
            }

            line += Pitch;
        }
    }

    Destroy();

    glGenTextures(1, &Texture);

    glBindTexture(GL_TEXTURE_2D, Texture);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GLEW_VERSION_1_4 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    if(GLEW_EXT_texture_filter_anisotropic)
    {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_texture_max_anisotropy_ext);
    }

    if(GLEW_VERSION_1_4)
    {
        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
    }

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, Format, GL_UNSIGNED_BYTE, Data);

    glBindTexture(GL_TEXTURE_2D, 0);

    FreeImage_Unload(dib);

    return true;
}

void CTexture::Destroy()
{
    glDeleteTextures(1, &Texture);
    Texture = 0;
}

FIBITMAP *CTexture::GetBitmap(char *FileName, int &Width, int &Height, int &BPP)
{
    FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(FileName);

    if(fif == FIF_UNKNOWN)
    {
        fif = FreeImage_GetFIFFromFilename(FileName);
    }

    if(fif == FIF_UNKNOWN)
    {
        return NULL;
    }

    FIBITMAP *dib = NULL;

    if(FreeImage_FIFSupportsReading(fif))
    {
        dib = FreeImage_Load(fif, FileName);
    }

    if(dib != NULL)
    {
        int OriginalWidth = FreeImage_GetWidth(dib);
        int OriginalHeight = FreeImage_GetHeight(dib);

        Width = OriginalWidth;
        Height = OriginalHeight;

        if(Width == 0 || Height == 0)
        {
            FreeImage_Unload(dib);
            return NULL;
        }

        BPP = FreeImage_GetBPP(dib);

        if(Width > gl_max_texture_size) Width = gl_max_texture_size;
        if(Height > gl_max_texture_size) Height = gl_max_texture_size;

        if(!GLEW_ARB_texture_non_power_of_two)
        {
            Width = 1 << (int)floor((log((float)Width) / log(2.0f)) + 0.5f); 
            Height = 1 << (int)floor((log((float)Height) / log(2.0f)) + 0.5f);
        }

        if(Width != OriginalWidth || Height != OriginalHeight)
        {
            FIBITMAP *rdib = FreeImage_Rescale(dib, Width, Height, FILTER_BICUBIC);
            FreeImage_Unload(dib);
            dib = rdib;
        }
    }

    return dib;
}

// ----------------------------------------------------------------------------------------------------------------------------

COpenGLRenderer::COpenGLRenderer()
{
}

COpenGLRenderer::~COpenGLRenderer()
{
}

bool COpenGLRenderer::Init()
{
    if(!GLEW_VERSION_1_5)
    {
        ErrorLog.Set("OpenGL 1.5 not supported!");
        return false;
    }

    bool Error = false;

    Error |= !Texture.LoadTexture2D("texture.jpg");

    if(Error)
    {
        return false;
    }

    float Data[] =
    {    // s, t, nx, ny, nz, x, y, z
        0.0f, 0.0f,  0.0f,  0.0f,  1.0f, -0.5f, -0.5f,  0.5f, 
        1.0f, 0.0f,  0.0f,  0.0f,  1.0f,  0.5f, -0.5f,  0.5f,
        1.0f, 1.0f,  0.0f,  0.0f,  1.0f,  0.5f,  0.5f,  0.5f,
        0.0f, 1.0f,  0.0f,  0.0f,  1.0f, -0.5f,  0.5f,  0.5f,
        0.0f, 0.0f,  0.0f,  0.0f, -1.0f,  0.5f, -0.5f, -0.5f,
        1.0f, 0.0f,  0.0f,  0.0f, -1.0f, -0.5f, -0.5f, -0.5f,
        1.0f, 1.0f,  0.0f,  0.0f, -1.0f, -0.5f,  0.5f, -0.5f,
        0.0f, 1.0f,  0.0f,  0.0f, -1.0f,  0.5f,  0.5f, -0.5f,
        0.0f, 0.0f,  1.0f,  0.0f,  0.0f,  0.5f, -0.5f,  0.5f,
        1.0f, 0.0f,  1.0f,  0.0f,  0.0f,  0.5f, -0.5f, -0.5f,
        1.0f, 1.0f,  1.0f,  0.0f,  0.0f,  0.5f,  0.5f, -0.5f,
        0.0f, 1.0f,  1.0f,  0.0f,  0.0f,  0.5f,  0.5f,  0.5f,
        0.0f, 0.0f, -1.0f,  0.0f,  0.0f, -0.5f, -0.5f, -0.5f,
        1.0f, 0.0f, -1.0f,  0.0f,  0.0f, -0.5f, -0.5f,  0.5f,
        1.0f, 1.0f, -1.0f,  0.0f,  0.0f, -0.5f,  0.5f,  0.5f,
        0.0f, 1.0f, -1.0f,  0.0f,  0.0f, -0.5f,  0.5f, -0.5f,
        0.0f, 0.0f,  0.0f,  1.0f,  0.0f, -0.5f,  0.5f,  0.5f,
        1.0f, 0.0f,  0.0f,  1.0f,  0.0f,  0.5f,  0.5f,  0.5f,
        1.0f, 1.0f,  0.0f,  1.0f,  0.0f,  0.5f,  0.5f, -0.5f,
        0.0f, 1.0f,  0.0f,  1.0f,  0.0f, -0.5f,  0.5f, -0.5f,
        0.0f, 0.0f,  0.0f, -1.0f,  0.0f, -0.5f, -0.5f, -0.5f,
        1.0f, 0.0f,  0.0f, -1.0f,  0.0f,  0.5f, -0.5f, -0.5f,
        1.0f, 1.0f,  0.0f, -1.0f,  0.0f,  0.5f, -0.5f,  0.5f,
        0.0f, 1.0f,  0.0f, -1.0f,  0.0f, -0.5f, -0.5f,  0.5f
    };

    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, 768, Data, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    GLfloat LightModelAmbient[] = {0.0f, 0.0f, 0.0f, 1.0f};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightModelAmbient);

    GLfloat LightAmbient[] = {0.25f, 0.25f, 0.25f, 1.0f};
    glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);

    GLfloat LightDiffuse[] = {0.75f, 0.75f, 0.75f, 1.0f};
    glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);

    GLfloat MaterialAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, MaterialAmbient);

    GLfloat MaterialDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialDiffuse);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);

    return true;
}

void COpenGLRenderer::Render(float FrameTime)
{
    static GLfloat LightPosition[] = {0.0f, 0.0f, 2.5f, 1.0f}, a = 0.0f;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0f, 0.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

    glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);

    glRotatef(a, 0.0f, 1.0f, 0.0f);
    glRotatef(a, 1.0f, 0.0f, 0.0f);

    glColor3f(1.0f, 1.0f, 1.0f);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, Texture);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, 32, (void*)0);

    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 32, (void*)8);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 32, (void*)20);

    glDrawArrays(GL_QUADS, 0, 24);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

    a += 22.5f * FrameTime;
}

void COpenGLRenderer::Resize(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    glViewport(0, 0, Width, Height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, (float)Width / (Height > 0 ? (float)Height : 1.0f), 0.125f, 512.0f);
}

void COpenGLRenderer::Destroy()
{
    Texture.Destroy();

    if(GLEW_VERSION_1_5)
    {
        glDeleteBuffers(1, &VBO);
    }
}

// ----------------------------------------------------------------------------------------------------------------------------

COpenGLRenderer OpenGLRenderer;

// ----------------------------------------------------------------------------------------------------------------------------

CString ModuleDirectory, ErrorLog;

// ----------------------------------------------------------------------------------------------------------------------------

void GetModuleDirectory()
{
    char *moduledirectory = new char[256];
    GetModuleFileName(GetModuleHandle(NULL), moduledirectory, 256);
    *(strrchr(moduledirectory, '\\') + 1) = 0;
    ModuleDirectory = moduledirectory;
    delete [] moduledirectory;
}

// ----------------------------------------------------------------------------------------------------------------------------

COpenGLView::COpenGLView()
{
}

COpenGLView::~COpenGLView()
{
}

bool COpenGLView::Init(HINSTANCE hInstance, char *Title, int Width, int Height, int Samples)
{
    this->Title = Title;
    this->Width = Width;
    this->Height = Height;

    WNDCLASSEX WndClassEx;

    memset(&WndClassEx, 0, sizeof(WNDCLASSEX));

    WndClassEx.cbSize = sizeof(WNDCLASSEX);
    WndClassEx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    WndClassEx.lpfnWndProc = WndProc;
    WndClassEx.hInstance = hInstance;
    WndClassEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    WndClassEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    WndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClassEx.lpszClassName = "Win32OpenGLWindow";

    if(RegisterClassEx(&WndClassEx) == 0)
    {
        ErrorLog.Set("RegisterClassEx failed!");
        return false;
    }

    DWORD Style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

    hWnd = CreateWindowEx(WS_EX_APPWINDOW, WndClassEx.lpszClassName, Title, Style, 0, 0, Width, Height, NULL, NULL, hInstance, NULL);

    if(hWnd == NULL)
    {
        ErrorLog.Set("CreateWindowEx failed!");
        return false;
    }

    HDC hDC = GetDC(hWnd);

    if(hDC == NULL)
    {
        ErrorLog.Set("GetDC failed!");
        return false;
    }

    PIXELFORMATDESCRIPTOR pfd;

    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));

    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;

    int PixelFormat = ChoosePixelFormat(hDC, &pfd);

    if(PixelFormat == 0)
    {
        ErrorLog.Set("ChoosePixelFormat failed!");
        return false;
    }

    static int MSAAPixelFormat = 0;

    if(SetPixelFormat(hDC, MSAAPixelFormat == 0 ? PixelFormat : MSAAPixelFormat, &pfd) == FALSE)
    {
        ErrorLog.Set("SetPixelFormat failed!");
        return false;
    }

    hGLRC = wglCreateContext(hDC);

    if(hGLRC == NULL)
    {
        ErrorLog.Set("wglCreateContext failed!");
        return false;
    }

    if(wglMakeCurrent(hDC, hGLRC) == FALSE)
    {
        ErrorLog.Set("wglMakeCurrent failed!");
        return false;
    }

    if(glewInit() != GLEW_OK)
    {
        ErrorLog.Set("glewInit failed!");
        return false;
    }

    if(MSAAPixelFormat == 0 && Samples > 0)
    {
        if(WGLEW_ARB_pixel_format && GLEW_ARB_multisample)
        {
            while(Samples > 0)
            {
                UINT NumFormats = 0;

                int PFAttribs[] =
                {
                    WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
                    WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
                    WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
                    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
                    WGL_COLOR_BITS_ARB, 32,
                    WGL_DEPTH_BITS_ARB, 24,
                    WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
                    WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
                    WGL_SAMPLES_ARB, Samples,
                    0
                };

                if(wglChoosePixelFormatARB(hDC, PFAttribs, NULL, 1, &MSAAPixelFormat, &NumFormats) == TRUE && NumFormats > 0) break;

                Samples--;
            }

            wglDeleteContext(hGLRC);
            DestroyWindow(hWnd);
            UnregisterClass(WndClassEx.lpszClassName, hInstance);

            return Init(hInstance, Title, Width, Height, Samples);
        }
        else
        {
            Samples = 0;
        }
    }

    this->Samples = Samples;

    GetModuleDirectory();

    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_max_texture_size);

    if(GLEW_EXT_texture_filter_anisotropic)
    {
        glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy_ext);
    }

    if(WGLEW_EXT_swap_control)
    {
        wglSwapIntervalEXT(0);
    }

    return OpenGLRenderer.Init();
}

void COpenGLView::Show(bool Maximized)
{
    RECT dRect, wRect, cRect;

    GetWindowRect(GetDesktopWindow(), &dRect);
    GetWindowRect(hWnd, &wRect);
    GetClientRect(hWnd, &cRect);

    wRect.right += Width - cRect.right;
    wRect.bottom += Height - cRect.bottom;
    wRect.right -= wRect.left;
    wRect.bottom -= wRect.top;
    wRect.left = dRect.right / 2 - wRect.right / 2;
    wRect.top = dRect.bottom / 2 - wRect.bottom / 2;

    MoveWindow(hWnd, wRect.left, wRect.top, wRect.right, wRect.bottom, FALSE);

    ShowWindow(hWnd, Maximized ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL);
}

void COpenGLView::MessageLoop()
{
    MSG Msg;

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
}

void COpenGLView::Destroy()
{
    OpenGLRenderer.Destroy();
    wglDeleteContext(hGLRC);
    DestroyWindow(hWnd);
}

void COpenGLView::OnPaint()
{
    static DWORD LastFPSTime = GetTickCount(), LastFrameTime = LastFPSTime, FPS = 0;

    PAINTSTRUCT ps;

    HDC hDC = BeginPaint(hWnd, &ps);

    DWORD Time = GetTickCount();

    float FrameTime = (Time - LastFrameTime) * 0.001f;

    LastFrameTime = Time;

    if(Time - LastFPSTime > 1000)
    {
        CString Text = Title;

        Text.Append(" - %dx%d", Width, Height);
        Text.Append(", ATF %dx", gl_max_texture_max_anisotropy_ext);
        Text.Append(", MSAA %dx", Samples);
        Text.Append(", FPS: %d", FPS);
        Text.Append(" - %s", (char*)glGetString(GL_RENDERER));

        SetWindowText(hWnd, Text);

        LastFPSTime = Time;
        FPS = 0;
    }
    else
    {
        FPS++;
    }

    OpenGLRenderer.Render(FrameTime);

    SwapBuffers(hDC);

    EndPaint(hWnd, &ps);

    InvalidateRect(hWnd, NULL, FALSE);
}

void COpenGLView::OnSize(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    OpenGLRenderer.Resize(Width, Height);
}

// ----------------------------------------------------------------------------------------------------------------------------

COpenGLView OpenGLView;

// ----------------------------------------------------------------------------------------------------------------------------

LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uiMsg)
    {
        case WM_CLOSE:
            PostQuitMessage(0);
            break;

        case WM_PAINT:
            OpenGLView.OnPaint();
            break;

        case WM_SIZE:
            OpenGLView.OnSize(LOWORD(lParam), HIWORD(lParam));
            break;

        default:
            return DefWindowProc(hWnd, uiMsg, wParam, lParam);
    }

    return 0;
}

// ----------------------------------------------------------------------------------------------------------------------------

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR sCmdLine, int iShow)
{
    char *AppName = "Vertex buffer object (VBO)";

    if(OpenGLView.Init(hInstance, AppName, 800, 600, 4))
    {
        OpenGLView.Show();
        OpenGLView.MessageLoop();
    }
    else
    {
        MessageBox(NULL, ErrorLog, AppName, MB_OK | MB_ICONERROR);
    }

    OpenGLView.Destroy();

    return 0;
}
Download
vertex_buffer_object.zip (Visual Studio 2005 Professional)
vertex_buffer_object_devcpp4992.zip (Dev-Cpp 4.9.9.2)
© 2010 - 2016 Bc. Michal Belanec, michalbelanec (at) centrum (dot) sk
Last update June 25, 2016
OpenGL® is a registered trademark of Silicon Graphics Inc.