Simple C++ Snippets

These are some simple Win32 C++ snippets I often find myself coming back to when I want to mess around with something. Instead of spending a long time setting up a good environment with strong libraries available I quite often want “just a window” to start doing stuff with. That in itself is much easier than you may often find described online.

So be warned, ugly code ahead!

Part 1, windows and GL context
A C++ program to display a static window; you can’t do anything with it though, it does not process events.

#include <windows.h>
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd = CreateWindow("edit", NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    while(true);
    return 0;
}

We can add event processing, but this is more involved. So let’s split that up. First we need to define a custom window “class” or type that describes mostly which callback this kind of window should use.

HWND CustomWindow(const char* name, HINSTANCE hInstance, WNDPROC callback)
{
    WNDCLASSEX WndClsEx = { 0 };
    WndClsEx.cbSize = sizeof(WNDCLASSEX);
    WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
    WndClsEx.lpfnWndProc = callback;
    WndClsEx.lpszClassName = name;
    WndClsEx.hInstance = hInstance;
    RegisterClassEx(&WndClsEx);

    return CreateWindow(name, name, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
}

We initialize to 0 for safety. The window class can also get default icons, cursors, colors etc. but those can also be changed with Win32 functions later on, so I tend not to bother with them here.

When having many types of windows I tend to use OOP, with only one custom window type, and have an std::map to go from HWND to wrapped instance.then I just call soemthing like WindowBaseClass::instances.find(hWnd)->Update();.

The above function expects a callback argument, this is a function pointer adhering to the Win32 callback. A very basic one allows the user to use ALT+F4 and the close button to end the program completely. Note that this is not something you wish to do in a multi window application; in which case you probably should check for the number of windows before quitting.

LRESULT CALLBACK CustomWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
    case WM_DESTROY:
        PostQuitMessage(WM_QUIT);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}

Now let’s do a proper event loop which will defer message to the custom window proc and exit as required by the quit message.

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd = CustomWindow("UI", hInstance, CustomWindowProc);
    MSG msg;
    do
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                return msg.wParam;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    } while(true);
    return msg.wParam;
}

The thing I do the most is extend a window into an openGL context to start rendering in openGL, ignoring any actual Win32 stuff and just testing some graphics thing. This function enriches a created window with an openGL context and returns it’s handle.

HDC GLWindow(HWND hWnd)
{
    /// Creates & "makes current" (activates) an OpenGL target inside the given window
    HDC hDC = GetDC(hWnd);
    static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0 };
    SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd);
    wglMakeCurrent(hDC, wglCreateContext(hDC));
    return hDC;
}

The simple example we started with can become this instead:

#include <windows.h>
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd = CreateWindow("edit", NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    HDC hDC = GLWindow(hWnd);
    glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
    do
    {
        glClear(GL_COLOR_BUFFER_BIT);
        glColor3f(1.0f, 0.9f, 0.8f);
        glRecti(-1, -1, 1, 1);
        SwapBuffers(hDC);
    } while(true);
    return 0;
}

OpenGL has a default viewport with the (-1, -1) coordinate at the bottom left and (1, 1) at the top right. A useful thing is to shift this to have (0, 0) at the bottom left and the window (width, height) at the top right.

void GLPixelSpace(HWND hWnd)
{
    /// Make viewport coordinates match pixel coordinates; requires a "Current" GL context (wglMakeCurrent, as initialized for us by GLWindow).
    glTranslatef(-1.0f, -1.0f, 0.0f);
    // Get the window's draw-able area
    RECT area;
    GetClientRect(hWnd, &area);
    // Compute scale so 1 pixel matches 1 unit.
    glScalef(2.0f / (area.right - area.left), 2.0f / (area.bottom - area.top), 1.0f);
}

Call this once before entering the drawing loop to have it as default, or use

glPushMatrix();
glLoadIdentity();
GLPixelSpace(hWnd);
// Pixel-space drawing code here
glPopMatrix();

One other useful alternative is the have the (0, 0) coordinate at the TOP left instead:

void GLPixelSpace_FlipY(HWND hWnd)
{
    /// Make viewport coordinates match pixel coordinates; requires a "Current" GL context (wglMakeCurrent, as initialized for us by GLWindow).
    glTranslatef(-1.0f, 1.0f, 0.0f);
    // Get the window's draw-able area
    RECT area;
    GetClientRect(hWnd, &area);
    // Compute scale so 1 pixel matches 1 unit.
    glScalef(2.0f / (area.right - area.left), -2.0f / (area.bottom - area.top), -1.0f);
}

Part 2, openGL fonts using FTGL & FreeType2
A related thing is font rendering. There are tons of options, I’m going to reference the openGL font survey about that.

I just want to supply a TTF file I already have with my application and use that inside of it. I want a flexible system that is fast, can batch, does not flood my memory too greedily when rendering various sizes and has good kerning with font-metrics capabilities.

GLX which seems linux only.
GLC which is Adobe Type 1 fonts only, seems fairly old, has issues with rotating fonts & aliasing.
GLUT’s default font renderer, which is not friendly to introducing new fonts.
GLTT which seems the first decent flexible library.
FTGL which seems GLTT 2.0 using the more modern FreeType 2.0 library (instead of 1.0).
WGL which I tried and flickered and it is in general hard to customize appearance / do anti-aliasing.
GLF is last and seems just as fine as GLTT, be it also outdated with it’s own font file format which as no tools or documentation.

Texture mapping fonts is also not an option for on the fly text display as we are limited to predefined texture atlasses for specific glyphs of specific sizes, which is a lot of manual labour; though I would probably recommend it for a game, combined with BMFont it’s pretty powerful and fast to render large amounts of static (even 3D) text in a single draw call, with minimal memory usage if the atlas is a distance field as well as described here.

So with that out of the way, imagine a rant about the poor distribution of binaries for both FreeType and FTGL. I was happy to discover both these projects (on sourceforge) had a project setup for a ton of build environments on various platforms with various toolsets. It was very easy to copy the vc2008 project, open it in vs2012, auto-upgrade and change the output paths to match a new vs2012 target directory.

I have attached compiled lib and dll binaries from both FTGL and FreeType compiled on windows 7 using visual studio 2012 update 4.

ftgl-2.1.3-rc5__with__freetype-2.6__binaries

Here is a header for intelligently loading windows, openGL and FTGL in the right order, including the required libraries as we go. All you need to do in your project settings is set up the additional include and additional library directories if you’re placing these files elsewhere.

// settings.h
#pragma once


#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>


#pragma comment(lib, "opengl32.lib")
#include <gl/gl.h>


#define FTGL_LIBRARY_STATIC
#ifdef NDEBUG
#pragma comment(lib, "freetype26")
#ifdef FTGL_LIBRARY_STATIC
#pragma comment(lib, "ftgl_static")
#else
#pragma comment(lib, "ftgl")
#endif
#else
#pragma comment(lib, "freetype26d")
#ifdef FTGL_LIBRARY_STATIC
#pragma comment(lib, "ftgl_static_D")
#else
#pragma comment(lib, "ftgl_D")
#endif
#endif
#include <FTGL/ftgl.h>

Here is a full code sample that has a simple or a custom window (#define SIMPLE) which uses the above file to render a rect and a piece of text in a win32 window using openGl.

/*
References

http://www.functionx.com/win32/Lesson01c.htm
For window with custom class setup

http://sizecoding.blogspot.nl/2007/10/tiny-opengl-windowing-code.html
For basic wglContext setup

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx
For buffer swaps between messages

http://ftgl.sourceforge.net/docs/html/ftgl-tutorial.html
For basic font creation

http://stackoverflow.com/questions/28151464/how-to-change-color-in-rgb-format-in-ftgl-opengl
For requiring FTGLUseTextureFont to have glColor work as expected.

http://stackoverflow.com/questions/28313786/undefined-symbol-in-static-library-but-exists-when-in-same-vs-solution
For knowing to define FTGL_LIBRARY_STATIC

https://www.opengl.org/archives/resources/features/fontsurvey/
For the font options which showed FTGL as my preferred option.
*/


#include "settings.h"


// #define SIMPLE


HWND Window(HINSTANCE hInstance)
{
    /// Creates an arbitrary default window
    return CreateWindow("edit", NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
}


LRESULT CALLBACK CustomWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
    case WM_DESTROY:
        PostQuitMessage(WM_QUIT);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}


HWND CustomWindow(const char* name, HINSTANCE hInstance, WNDPROC callback)
{
    WNDCLASSEX WndClsEx = { 0 };
    WndClsEx.cbSize = sizeof(WNDCLASSEX);
    WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
    WndClsEx.lpfnWndProc = callback;
    // WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    // WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
    // WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClsEx.lpszClassName = name;
    WndClsEx.hInstance = hInstance;
    // WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    RegisterClassEx(&WndClsEx);

    return CreateWindow(name, name, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
}


HDC GLWindow(HWND hWnd)
{
    /// Creates & "makes current" (activates) an OpenGL target inside the given window
    HDC hDC = GetDC(hWnd);
    static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0 };
    SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd);
    wglMakeCurrent(hDC, wglCreateContext(hDC));
    return hDC;
}


void GLPixelSpace(HWND hWnd)
{
    /// Make viewport coordinates match pixel coordinates; requires a "Current" GL context (wglMakeCurrent, as initialized for us by GLWindow).
    glTranslatef(-1.0f, -1.0f, 0.0f);
    // Get the window's draw-able area
    RECT area;
    GetClientRect(hWnd, &area);
    // Compute scale so 1 pixel matches 1 unit.
    glScalef(2.0f / (area.right - area.left), 2.0f / (area.bottom - area.top), 1.0f);
}


void GLPixelSpace_FlipY(HWND hWnd)
{
    /// Make viewport coordinates match pixel coordinates; requires a "Current" GL context (wglMakeCurrent, as initialized for us by GLWindow).
    glTranslatef(-1.0f, 1.0f, 0.0f);
    // Get the window's draw-able area
    RECT area;
    GetClientRect(hWnd, &area);
    // Compute scale so 1 pixel matches 1 unit.
    glScalef(2.0f / (area.right - area.left), -2.0f / (area.bottom - area.top), -1.0f);
}


void Draw(FTGLTextureFont& font)
{
    // Draw background
    glClear(GL_COLOR_BUFFER_BIT);

    // Draw foreground
    // Set foreground color
    glColor3f(1.0f, 0.9f, 0.8f);
    glRecti(0, 0, 200, 100);
    // Set foreground color
    glColor3f(0.2f, 0.3f, 0.4f);
    font.Render("Hello World!");
}


int ExecSimple(HDC hDC)
{
    /// Simple render loop
    FTGLTextureFont font("C:/Windows/Fonts/Roboto-Light.ttf");
    font.FaceSize(32);
    do
    {
        Draw(font);
        SwapBuffers(hDC);
    } while(true);
    return 0;
}


int Exec(HDC hDC)
{
    /// Render loop with windows messages
    FTGLTextureFont font("C:/Windows/Fonts/Roboto-Light.ttf");
    font.FaceSize(32);
    MSG msg;
    do
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                return msg.wParam;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        Draw(font);
        SwapBuffers(hDC);
    } while(true);
    return msg.wParam;
}


INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
#ifdef SIMPLE
    HWND hWnd = Window(hInstance);
    HDC hDC = GLWindow(hWnd);
    GLPixelSpace(hWnd);
    glClearColor(0.1f, 0.2f, 0.3f, 1.0f); // Set background color
    return ExecSimple(hDC);
#else
    HWND hWnd = CustomWindow("UI", hInstance, CustomWindowProc);
    HDC hDC = GLWindow(hWnd);
    GLPixelSpace(hWnd);
    glClearColor(0.1f, 0.2f, 0.3f, 1.0f); // Set background color
    return Exec(hDC);
#endif
}

Leave a Reply

Your email address will not be published. Required fields are marked *