Points, Lines, Triangles, Quadrilaterals, and Polygons

The previous post (HelloTriangle) drew a single white triangle. It is possible to draw points, lines, triangles, quads (four sided polygons), and polygons. This post will look at drawing those objects.

The starting point for the code used in this post is the HelloTriangle project that was developed in the HelloTriangle post. Since the HelloTriangle project will be the starting point for a few of these posts, I suggest that you create a new project. It can be in the same solution as the HelloTriangle project. Copy the 6 source files from HelloTriangle to the new project and then in the Solution Explorer, added them to the project. Before attempting to build the new project, you will have to update the project properties VC++ Directories -> Include Directories  and -> Library Directories to point to the wxWidgets include and library directories. In Configuration Properties -> General, set the Character Set to match the settings that you used when building the wxWidgets libraries. If you need help with this, see the instructions in Creating wxWidgets Programs with Visual Studio 2015 – Part 1.

Next, you need to add the nupengl.core NuGet package to the project. See Visual Studio, wxWidgets, and OpenGL if you need assistance.

At this point, the project should build and run to produce a white triangle on a black background.

In TriangleCanvas::SetupGraphics, the vertices of the triangle are defined as:

	float points[] = {
		0.0f, 0.5f,
		0.5f, -0.5f,
		-0.5f, -0.5f,
	};

In TriangleCanvas::OnPaint, see the line containing:

    glDrawArrays(GL_TRIANGLES, 0, 3);

draws the single triangle whose vertices are defined in the points array. The GL_TRIANGLES parameter specifies that triangles should be drawn. There are a number of other parameters that specify additional objects: points, lines, quadrilaterals, and polygons. This post will look at each of the possible combinations.

Points

To draw points, just change GL_TRIANGLES to GL_POINTS. Build and run the program. The result is a set of three white pixels, one at each vertex defined in points.

Lines

GL_LINES

Changing GL_TRIANGLES to GL_LINES in glDrawArrays draws line segments from v0 to v1, from v2, to v3, and so forth. The lines may intersect. If the points array contains an odd number of points, the last point cannot be used. With the points array as it is currently defined, only one line will be drawn: from {0.0, 0.5} to {0.5, -0.5}. To draw more line segments, additional data points must be added to the points array. Say instead of a filled in triangle, you want to draw lines around the triangle. To do that, the points array must be modified to:

    float points[] = {
        0.0f, 0.5f,
        0.5f, -0.5f,
        0.5f, -0.5f,
        -0.5f, -0.5f,
        -0.5f, -0.5f,
        0.0f, 0.5f
    };

and the call to glDrawArrays in OnPaint must be changed to:

	glDrawArrays(GL_LINES, 0, 6);

GL_LINES says to draw lines, and the 6 says there are 6 vertices specified.

Here is the resulting display:

trianglelines

GL_LINE_STRIP

With GL_LINE_STRIP, the lines are drawn from v0 to v1, v1 to v2, and so forth. To draw the same lines as above using GL_LINE_STRIP, the points array should be set to:

	float points[] = {
		0.0f, 0.5f,
		0.5f, -0.5f,
		-0.5f, -0.5f,
		0.0f, 0.5f
	};

Remember to change the number of vertices in the glDrawArrays call to 4.

GL_LINE_LOOP

Finally for lines, GL_LINE_LOOP draws lines like GL_LINE_STRIP, but closes the figure by adding a line from the last vertex specified to the first vertex. To use GL_LINE_LOOP to draw the edges of the triangle, change the points array back to:

	float points[] = {
		0.0f, 0.5f,
		0.5f, -0.5f,
		-0.5f, -0.5f
	};

and the glDrawArrays line to:

	glDrawArrays(GL_LINE_LOOP, 0, 3);

Triangles

GL_TRIANGLES

The simple GL_TRIANGLES draws filled triangles whose vertices are specifed as groups of three (v0, v1, v2 for the first rectangle, v3, v4, v5 for the second triangle, and so forth). As an example, let’s draw two triangles. The first will be the same as the triangle in the HelloTriangle example. Change the points array to:

	float points[] = {
		0.0f, 0.5f,
		0.5f, -0.5f,
		-0.5f, -0.5f,
		-0.9f, 0.9f,
		-0.1f, 0.9f,
		-0.5f, 0.1f
	};

and the glDrawArrays call to:

	glDrawArrays(GL_TRIANGLES, 0, 6);

The 6 specifies 6 vertices.

Here is the resulting display:

twotriangles

GL_TRIANGLE_STRIP

GL_TRIANGLE_STRIP draws a series of triangles using the vertices v0, v1, v2, then v2, v1, v3, then v2, v3, v4, and so forth. The order is to ensure that the triangles are all drawn with the same orientation so that the triangles can all form part of the same surface.

Here is the same set of vertices plotted using GL_TRIANGLE_STRIP instead of GL_TRIANGLES:

trianglestrip

I leave it to the reader to determine how this image was created.

GL_TRIANGLE_FAN

GL_TRIANGLE_FAN works the same as GL_TRIANGLE_STRIP except that the vertices are v0, v1, v2, then v0, v2, v3, then v0, v3, v4 and so forth.

The vertices specified in the points array, above, do not make a particularly good example. Instead, change the points array to:

	float points[] = {
		0.0f, 0.0f,
		0.5f, 0.0f,
		0.35f, 0.35f,
		0.0f, 0.5f,
		-0.7f, 0.2f,
		-0.2f, -0.5f
	};

Drawing these points as a GL_TRIANGLE_FAN results in the following display:

trianglefan

Quadrilaterals

In various examples, you will see references to GL_QUADS and GL_QUAD_STRIP. These types have been deprecated and therefore should not be used in new code. To draw the equivalent, you should use two triangles per quadrilateral.

Polygons

Polygons (GL_POLYGON) are also deprecated. Triangles should be used to build up your polygons.

HelloTriangle

The first program that you typically write in any programming language is HelloWorld, where you simply print out the words: “Hello World!” to the console. When you graduate to using a windowing toolkit, the Hello World program displays a window with “Hello World!” in it. The post Creating wxWidgets Programs with Visual Studio 2015 – Part 2 gives an example using wxWidgets. In OpenGL, about the simplest program you can write is HelloTriangle which displays a white triangle on a black background; there is no text. I will develop HelloTriangle in this post.

HelloTriangle will be developed using Visual Studio 2015, wxWidgets, and OpenGL. Actually, Visual Studio is simply the development environment used by me. You could in fact use any environment you want provided it supports wxWidgets and GLEW (for OpenGL).

To begin, we need to create a bare-bones wxWidgets application. This is discussed in Creating wxWidgets Programs with Visual Studio 2015 – Part 1. Call the project HelloTriangle. Change MyProjectApp to HelloTriangleApp. Once you have that program working, you should install the NuGet package called nupengl.core, to include GLEW. Alternatively, you could simply install GLEW on your system and use that. Installing nupengl.core is discussed in Visual Studio, wxWidgets, and OpenGL. Once this is done, we have all of the parts needed to begin coding HelloTriangle.

This post will follow along with the tutorials at open.gl. In the tutorials on that website, the Context creation page shows how to set up an OpenGL program using SFML, SDL, and GLFW. We will use wxWidgets instead. The Drawing polygons page shows how to draw a triangle and then colour the triangle using vertex shaders and fragment shaders. We will not be using shaders; this post simply draws a white triangle on a black background. The code shown in this post can then be modified to use shaders.

If you have followed the steps above, you have a very simple program that simply displays an empty window. This will now be modified. We will add two new classes, called TriangleWindow and TriangleCanvas.

TriangleCanvas subclasses wxGLCanvas. wxGLCanvas is the wxWidgets class that displays OpenGL graphics. In the TriangleCanvas class, we will initialize OpenGL, create the wxGLContext required for OpenGL, and set up the graphics for drawing in the constructor. The paint event handler will paint the background and the triangle. This code is in the OnPaint method. Finally, the destructor does some clean up.

Here is the header file for TriangleCanvas:

#pragma once
#include <memory>
#include "wx/glcanvas.h"

class TriangleCanvas : public wxGLCanvas
{
public:
    TriangleCanvas(wxWindow* parent, wxWindowID id = wxID_ANY,
        const int* attribList = 0, const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize, long style = 0L,
        const wxString& name = L"GLCanvas",
        const wxPalette& palette = wxNullPalette);

    virtual ~TriangleCanvas();
    TriangleCanvas(const TriangleCanvas& tc) = delete;
    TriangleCanvas(TriangleCanvas&& tc) = delete;
    TriangleCanvas& operator=(const TriangleCanvas& tc) = delete;
    TriangleCanvas& operator=(TriangleCanvas&& tc) = delete;

private:
    void InitializeGLEW();
    void SetupGraphics();
    void OnPaint(wxPaintEvent& event);

    std::unique_ptr<wxGLContext> m_context;
    GLuint m_vbo; // vertex buffer pointer
    GLuint m_vao; // vertex array pointer
};

The private methods InitializeGLEW and SetupGraphics are helper methods that are called in the constructor. m_context holds the OpenGL context and m_vbo and m_vao are properties used by OpenGL.

Here is the constructor:

TriangleCanvas::TriangleCanvas(wxWindow* parent, wxWindowID id, 
        const int* attribList, const wxPoint& pos, const wxSize& size,
        long style, const wxString& name, const wxPalette& palette)
	: wxGLCanvas(parent, id, attribList, pos, size, style, name, palette),
	m_vbo(0), m_vao(0)
{
	m_context = std::make_unique<wxGLContext>(this);
	Bind(wxEVT_PAINT, &TriangleCanvas::OnPaint, this);
	
	SetCurrent(*m_context);
	InitializeGLEW();
	SetupGraphics();
}

This code is straight-forward. It creates an OpenGL context for the TriangleCanvas, and then binds the OnPaint method to the paint event. Following this, the OpenGL context is set and the two helper methods are called to complete the initialization. The helper methods will only succeed if the context has been set.

InitializeGLEW simply initializes the GLEW so that the OpenGL methods can be accessed. The method is shown here:

void TriangleCanvas::InitializeGLEW()
{
	glewExperimental = true;
	GLenum err = glewInit();
	if (err != GLEW_OK) {
		const GLubyte* msg = glewGetErrorString(err);
		throw std::exception(reinterpret_cast<const char*>(msg));
	}
}

glewInit() must be called to obtain information on the supported extensions from the graphics driver. Experimental and pre-release drivers may not report every available extension through the standard call to glewInit; in that case, GLEW will report that the extension is not supported. By setting glewExperimental to true (or GL_TRUE) before calling glewInit, all of the extensions are exposed.

Following the call to glewInit, the code checks the status returned from the glewInit call, and throws an exception if the call failed. I have not included any code in this program to catch the exception, so the program will simply terminate. Catching the exception is left as an exercise for the reader.

This is the code for the SetupGraphics method:

void TriangleCanvas::SetupGraphics()
{
	// define vertices
	float points[] = {
		0.0f, 0.5f,
		0.5f, -0.5f,
		-0.5f, -0.5f
	};
	// upload vertex data
	glGenBuffers(1, &m_vbo);
	glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
	// setup vertex array objects
	glGenVertexArrays(1, &m_vao);
	glBindVertexArray(m_vao);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
}

I will not provide an explanation of this code; it is available on the Drawing polygons page on open.gl.

The glGenBuffers and glGenVertexArrays calls in the code above allocate buffers on the graphics card. These buffers must be released before the program terminates or a memory leak will result. The code in the TriangleCanvas destructor performs this task:

TriangleCanvas::~TriangleCanvas()
{
	SetCurrent(*m_context);
	glDeleteVertexArrays(1, &m_vao);
	glDeleteBuffers(1, &m_vbo);
}

All of the drawing is performed in the OnPaint method:

void TriangleCanvas::OnPaint(wxPaintEvent& event)
{
	SetCurrent(*m_context);

	// set background to black
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// draw the graphics
	glDrawArrays(GL_TRIANGLES, 0, 3);
	// and display
	glFlush();
	SwapBuffers();
}

This code is quite simple. The context is set. the glClearColor and glClear calls set a black background, and glDrawArrays draws the triangle. glFlush is called to force the drawing to the backup buffer, and SwapBuffers swaps the buffers to display the buffer that OnPaint just drew on.

That completes the TriangleCanvas.cpp file except for the preprocessor directives at the top of the file:

#include "wx/wxprec.h"
#include <GL/glew.h>
#include "TriangleCanvas.h"

#pragma comment(lib, "glew32.lib")

The TriangleWindow class is very simple. All it does is create a TriangleCanvas object and attach it to the window. Here is the content of TriangleWindow.h:

#pragma once

class TriangleWindow : public wxFrame
{
public:
	TriangleWindow(wxWindow* parent, const std::wstring& title, const wxPoint& pos = wxDefaultPosition,
		const wxSize& size = wxDefaultSize);
	virtual ~TriangleWindow();
	TriangleWindow(const TriangleWindow& tw) = delete;
	TriangleWindow(TriangleWindow&& tw) = delete;
	TriangleWindow& operator=(const TriangleWindow& tw) = delete;
	TriangleWindow& operator=(TriangleWindow&&) = delete;
};

and here is the content of TriangleWindow.cpp:

#include "wx/wxprec.h"
#include "TriangleCanvas.h"
#include "TriangleWindow.h"

const int triCanvasID = 2000;			// TriangleCanvas widget ID

TriangleWindow::TriangleWindow(wxWindow* parent, const std::wstring& title, const wxPoint& pos,
	const wxSize& size)
	: wxFrame(parent, wxID_ANY, title, pos, size, wxMINIMIZE_BOX | wxCLOSE_BOX | 
		wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN)
{
	TriangleCanvas* canvas = new TriangleCanvas(this, triCanvasID, nullptr, { 0, 0 },
	{ 800, 800 });
	Fit();
	Centre();
}

TriangleWindow::~TriangleWindow()
{
}

Displaying the window with the triangle requires only two changes to HelloTriangleApp.cpp. Add a #include directive for TriangleWindow.h, and replace the first line of HelloTriangleApp::OnInit with

	TriangleWindow* mainFrame = new TriangleWindow(nullptr, L"Hello Triangle!");

Now build and run the application. Here is the resulting window:

HelloTriangle

That’s all there is to it.

Visual Studio, wxWidgets, and OpenGL

The immediately previous post discussed OpenGL and many potential libraries and windowing toolkits that work with OpenGL. In this post, I will look at what I use for OpenGL programming on Windows 10 desktop:

  • Visual Studio 2015 Community Edition;
  • wxWidgets; and,
  • GLEW, the OpenGL Extension Wrangler.

The selection of a development environment is very much a personal choice. After using a number of batch build methods and Integrated Development Environments over the last few years, I have settled on Visual Studio as the best choice for me. It contains a C++ compiler that is C++14 compatible and even has the C++17 features that have been approved by the C++ Standards Committee. Visual Studio 2015 also integrates git for source control, and unit testing via Microsoft Unit Test Framework. There is a memory leak detector included in Debug mode. Note that the leak detector only works on the memory allocated on the CPU and does not detect memory leaks in the GPU.

I will be developing some complex applications, so one of the Standard Windowing Toolkit mentioned in my post on OpenGL Graphics, or something similar is required. Since I develop on Windows 10 for the desktop, then any of the standard toolkits will work. Win32 is a C interface that requires a lot of coding to perform any windowing tasks. MFC is a thin shell over Win32. While it is C++, it predates C++99 and retains many of the C structures used in Win32. The other full C++ windowing toolkits, wxWidgets, Qt, and GTKMM, are all multi-platform, and more advanced and C++ friendly than MFC; any one of them would work.

I chose wxWidgets because it is the only one of the multi-platform toolkits that displays using the native look and feel on Windows. Being multi-platform, it has the advantage over the Windows only toolkits in that any code I develop can be easily ported to other operating systems and windowing systems.  It can even be used atop GTK+ on Windows desktops, although that seems to be counter-productive.

Using wxWidgets with Visual Studio is covered in three posts on this blog:

gl.h declares OpenGL 1.1 functions, many of which have been deprecated. It has been supplemented with glu.h and glaux.h, which have also been deprecated. In order to access the full functionality of the OpenGL API as supported by your graphics card, a loading library is required. I use GLEW because it is easily installed for use with Visual Studio, and a number of the tutorials online use it. The OpenGL functions are directly callable, unlike some of the other loading libraries that require you to call a function to retrieve a pointer to the requested function before calling the function. Source code and 32- and 64-bit binaries for GLEW are available for Windows on Sourceforge. Alternatively, GLEW is included along with FreeGLUT and FLFW in a NuGet package called nupengl.core.

Before loading nupengl.core, you must first create a simple wxWidgets project. The process for this is described in Creating wxWidgets Programs with Visual Studio 2015 – Part 1. Select the platform you are building your application for (x86 or x64), and build the project. Once you have done this, you can install nupengl.core. To do this, select the solution in the Solution Explorer, then select the Tools->NuGet Package Manager->Manage NuGet Packages for Solution... menu item. This opens the Nuget – Solution tab shown below:

nupengl

Select the nupengl.core package, then select the projects that you want to include the package in (Hello Triangle in this case). Now click the Install button to install the nupengl.core. nuplegl.core.redist is installed at the same time as nupengl.core, so there is no need to install it explicitly.

You are now ready to create your first OpenGL application. That is the topic of the next post.

OpenGL Graphics

There are a lot of web pages and websites that discuss OpenGL programming. So why am I writing yet another post about this? Because I had a hard time developing a simple OpenGL based program using Visual Studio. The are several reasons reasons for this:

  1. Some of the pages and posts are very old, with code dating back to OpenGL 1.0. The current specification is 4.5. Other authors state that you should not code this way because there is heavy use of deprecated functions.
  2. Every page starts at a different place; for example, some of the sample code starts with SFML, SDL, or GLFW, others with GLUT or FreeGLUT, and still others start with wxWidgets or Qt or the Win32 API. That’s a lot of acronyms and alternatives. The biggest problem is that while the code MAY show you how to do things, they don’t really explain what each of these choices are and why they chose the specific windowing API that they used.
  3. Every author makes many assumptions about what you already know. “Just code this and it will work”, they say, but when you try it, it doesn’t because the build system can’t find the header files needed, or the libraries. If you ever do get an executable file, it crashes when you run it.
  4. Many of the websites and books on OpenGL programming provide their own libraries that hide parts of OpenGL. Don’t teach me to use your libraries, teach me OpenGL programming!

OpenGL and the Rendering Pipeline

 

OpenGL is a specification (an API); it is not a library. That is, you cannot find a website that provides a download for an OpenGL library. Instead, the OpenGL specification is provided as part of your operating system or its drivers. For example, on Microsoft Windows, OpenGL functions are included in the driver for you graphics card, on Mac/OS X, Apple has provided an implementation, and on Linux, OpenGL is provided as an extension to the X Windowing system.

Note that OpenGL is concerned only with rendering graphics; it does not provide functions for animation, timing, file I/O, image file format processing, or a GUI.

OpenGL provides a C API, not C++, but of course is callable from C++.

For more information about OpenGL and the rendering pipeline see the following references:

Alphabet Soup

The C++ header file, gl.h, contains declarations only for OpenGL 1.1. At the time of writing this post, the most up-to-date specification is OpenGL 4.5. Then there are the header files glu.h and glaux.h. How do we get access to the most up-to-date functionality? That is the job of extension loading libraries.

Extension Loading Libraries

On the OpenGL website, these are referred to as OpenGL loading libraries. A few of these libraries are mentioned below:

GLEW

The OpenGL Extension Wrangler (GLEW) is an open source, cross platform C/C++ extension loading library. The version that is current as of the writing of this post is 1.13.0, which contains support for OpenGL 4.5, OpenGL extensions, WGL extensions, and GLX extensions. Source code for GLEW is available from GitHub and Sourceforge. The Sourceforge GLEW page also provides Windows binaries.

The Windows binaries are also included in a NuGet package for Visual Studio. This package will be mentioned in a future post on OpenGL programming with Visual Studio.

GL3W

GL3W is a simple OpenGL core profile loading library. How simple you may ask? There are only 3 functions: gl3wInit, gl3wIsSupported, and gl3wGetProcAddress. More information and the source code are available on GitHub.

glLoadGen

glLoadGen, the OpenGL Load Generator, is a LUA script that you use to create OpenGL header files and loading code for your specific needs. More information and the script are available on the glLoadGen wiki. Because glLoadGen generates the headers and code from the latest OpenGL specifications, the headers and code that you generate will always be up-to-date when you generate them.

glad

glad is a multi-language GL/GLES/EGL/GLX/WGL loader-generator written in python. Like glLoadGen, its inputs are the latest OpenGL specifications, and it generates only the functionality that you need. The source code for glad is available on GitHub.

glsdk

The Unofficial OpenGL Software Development Kit is a large toolkit that contains GL Load, to gain access to OpenGL functions, GL Image to load image files into memory and OpenGL textures, GL Utility to provide simple text drawing, a matrix stack and mouse-based camera and object manipulation, GL Mesh to render mesh data, OpenGL Mathematics to provide useful math classes and functions for OpenGL, GLFW (described later in this post), FreeGLUT (also described later in this post), the Boost libraries, and the OpenGL Load Generator which is a command-line script tool for generating OpenGL loader code. glsdk is a cross-platform build system. Note: glsdk is still in alpha release at this time.

glbinding

glbinding is a generated, cross-platform C++ binding for OpenGL based on the new xml-based OpenGL API specification. It is a full fledged OpenGL API binding that is generated using python scripts. glbinding is available on GitHub.

Windowing Toolkits

As mentioned above, OpenGL’s only function is to render graphics. How can we program all of the other stuff that a graphical program needs: windowing, image file processing, file I/O, and so forth? The answer to that question depends on what hardware and operating system you are using. You can use standard windowing toolkits like Win32, MFC, wxWidgets, Qt, GTK+ and FLTK. There are also a few utility toolkits that provide cross-platform Window management and a GUI toolkit and API. These toolkits include GLUT, FreeGLUT, GLFW, GLUI UI Library, NGL, SDL, and SFML. Each of these is discussed below.

Utility Toolkits

GLUT

GLUT, the OpenGL Utility Toolkit is a window system independent toolkit for writing OpenGL programs. It provides a portable API that allows you to write a single OpenGL program that can be compiled across all PC and workstation platforms. GLUT is a simple toolkit that does not provide all functionality that you might need in a large application; you should use one of the standard windowing toolkits for that.

GLUT is not open source. It has also not been updated since about 2000, so you should look at alternatives.

FreeGLUT

FreeGLUT is an open source alternative to the GLUT toolkit. You should use FreeGLUT rather than GLUT.

The source code and Windows binaries are available on Sourceforge.

GLFW

GLFW is an open-source, multi-platform library for creating windows with OpenGL contexts and for receiving input and events. The source code is available on GitHub.

GLUI UI Library

GLUI is a GLUT-based C++ UI library that provides controls to OpenGL applications. It is window system independent. It can be used with either GLUT or FreeGLUT. This library is quite old, with the last update in 2006. Source code is available on Sourceforge.

NGL

NGL allows you to build your user interface as a composition of widgets and behaviours. NGL takes care of positioning, resizing, anchoring and texture stretching. It supports both single and multithreaded applications. The source code and documentation are available on GitHub.

SDL

SDL, the Simple DirectMedia Layer, is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL. The source code is available on the SDL website.

SFML

SFML provides interfaces to system, window, graphics, audio and network components of your PC to ease the development of games and multimedia applications. Source code and binary downloads are available on the SFML website.

Standard Windowing Toolkits

Win32

Win32 is the standard C interface to the Windows GUI. Information on programming OpenGL in Win32 is provided in MSDN. It includes information on WGL extensions.

nupengl.core and nupengl.core.redist Nuget packages are provided in Visual Studio to support OpenGL 3 and 4. These packages will be discussed in a future post.

There are simpler ways of programming OpenGL for Microsoft Windows. See the additional toolkits below.

MFC

As with Win32, it is possible to use OpenGL with MFC. There are a few web pages that describe what you need to do, so use your search engine to find them.

Does anybody start new projects using MFC anymore?

wxWidgets

wxWidgets is a cross-platform C++ library for developing widget- and window-based applications on Windows, Mac OS/X, Linux and some other platforms. wxWidgets has the advantage of giving applications a truly native look and feel because it uses the platform’s native API. This is the only windowing toolkit that does this. Source code and binaries are available on the wxWidgets website.

wxWidgets supports OpenGL through the use of the wxGLCanvas widget. Sample OpenGL applications using wxWidgets will be developed in future posts.

Qt

Qt is another cross-platform windowing library. There are a number of different license requirements, so be sure to check before you begin using Qt. Qt libraries and source code are included on some platforms (e.g. Linux) because Qt is the library used by the KDE windowing system. Downloads are available on the Qt website. Applications developed with Qt look the same on every platform because Qt does not use a platform’s native API.

Qt supplies the QOpenGLWidget class for rendering OpenGL graphics.

GTKMM

GTKMM is the C++ wrapper over GTK+. GTK+ is a multi-platform toolkit for creating graphical user interfaces. GTK+ is part of the GNU Project and is licensed under the GNU LPGL. It is the toolkit used as the base for the GNOME windowing system and others. GTK+ is included with most Linux distributions for that reason. The GTK+ source code is available on the GTK+ website for those platforms that do not otherwise provide it.

Like Qt, GTK+ does not use a platform’s native API.

FLTK

FLTK, the Fast Light ToolKit, is a cross-platform C++ GUI toolkit. It is lighter weight than the toolkits mentioned above. FLTK source code and documentation are available from the FLTK website. A separate widget or window is not provided by FLTK for OpenGL rendering. Rather, the FLTK documentation suggests subclassing the Fl_Gl_Window class.

So Which Windowing Toolkit Should You Use?

That depends on a number of factors:

  1. What is the target platform (hardware and operating system) for your application? Is there more than one?
  2. Must the application have the native look and feel on every platform?
  3. How complex is your application: simple, complex?
  4. Are you already familiar with some of the toolkits?

So here are potential answers to these questions:

  1. If you are developing for only one platform, then you can use any of the toolkits that are specific to that platform, or any of the multi-platform toolkits. If you are developing for multiple environments, then you will want to use one of the cross-platform toolkits.
  2. If the application must have the native look and feel on every platform, you are very limited. You must either use wxWidgets if that toolkit supports every one of your target platforms, or you must write windowing code for each of the environments, either as separate programs, or with platform specific code inside #ifdef — #endif preprocessor directives.
  3. If your application has simple to moderate complexity, then one of the utility toolkits will probably do the job. For complex applications, that is, applications with a lot of user interaction (menus, widgets, windows and dialogs), then one of the standard windowing toolkits should be chosen based on the other answers.
  4. If you are already familiar with one or more of the toolkits and those toolkits fit your requirements, then obviously you will save time by use one of those toolkits. However, you should at least consider the other toolkits to see it they are a better fit for your application.

What am I using? That is the subject of the next post.