A Simple C++ OpenGL Shader Loader – Improved

I wrote one back in 2013, but I’ve learnt some things since then, so I’ve re-written it all with some changes. You can still load/compile shaders from strings or files, and then link/validate/use the resulting shader programs, only this time its all a little bit cleaner and more robust.

The main deficiencies of the previous incarnation were:

  • The Shader class wasn’t required for what I was using this stuff for, so I stripped it out – now you just create a ShaderProgram directly,
  • Error logs weren’t shown for any shader or shader program errors,
  • The shader program didn’t get validated,
  • Abuse of exit(-1) rather than throwing unchecked runtime_errors,
  • Inclusion of using clauses in headers, and
  • Possible wonky static declaration and initialisation of const_iterator error in uniform location getter meant that it might have only “got” the uniform location correcty on first run, when the const_iterator was declared – but in practice I haven’t seen any issues, perhaps because I spotted and fixed it but forgot to update article though…

Anyway, below is an improved version that fixes all of the above – here’s how you use it…

Usage

// Start by declaring a pointer to a ShaderProgram. A ShaderProgram can only be instantiated when there
// is a valid OpenGL context (i.e. window) - so you may like to declare the ShaderProgram globally, then
// instantiate it later on when you have an OpenGL context.
ShaderProgram *shaderProgram;
 
// ...later on when we have a OpenGL context...
shaderProgram = new ShaderProgram();
 
// To provide the source code for the vertex and fragment shaders you can either initialise from strings or from files:
shaderProgram->initFromStrings(vertexShaderString, fragmentShaderString);
shaderProgram->initFromFiles(vertexShaderFilename, fragmentShaderFilename);
 
// Add attributes to suit
shaderProgram->addAttribute("vertexLocation");
shaderProgram->addAttribute("vertexColour");
 
// Add uniforms to suit
shaderProgram->addUniform("mvpMatrix");
 
// Enable attributes
// 1.) Create and bind to a vertex array object (VAO)...
// 2.) Create and bind to a vertex buffer object (VBO) and specify your vertex attrib pointers
// 3.) Provide geometry data then unbind from the VBO
// --- Now you can enable vertex attributes with, for example:
             glEnableVertexAttribArray( shaderProgram->attribute("vertexLocation") );
             glEnableVertexAttribArray( shaderProgram->attribute("vertexColour") );
// 5.) Unbind from VAO
 
// Finally, to draw the geometry using the shader program you can use code similar to this:
// Note: This code assumes you're using GLM for your matrices, modify as appropriate if not. Also
// note that to use glm::value_ptr you must "#include <glm/gtc/type_ptr.hpp>"
shaderProgram->use();
             glUniformMatrix4fv(shaderProgram->uniform("mvpMatrix"), 1, GL_FALSE, glm::value_ptr(mvpMatrix) );
             <YOUR-DRAW-CALL-HERE> i.e. glDrawArrays(GL_TRIANGLES, 0, 100) etc.
shaderProgram->disable();

ShaderProgram Class Source Code

Here’s the code for the class itself:

/***
author     : r3dux
version    : 0.3 - 15/01/2014
description: Gets GLSL source code either provided as strings or can load from filenames,
             compiles the shaders, creates a shader program which the shaders are linked
             to, then the program is validated and is ready for use via myProgram.use(),
             <draw-stuff-here> then calling myProgram.disable();
 
             Attributes and uniforms are stored in <string, int> maps and can be added
             via calls to addAttribute(<name-of-attribute>) and then the attribute
             index can be obtained via myProgram.attribute(<name-of-attribute>) - Uniforms
             work in the exact same way.
***/
 
#ifndef SHADER_PROGRAM_HPP
#define SHADER_PROGRAM_HPP
 
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
 
class ShaderProgram
{
private:
	// static DEBUG flag - if set to false then, errors aside, we'll run completely silent
	static const bool DEBUG = true;
 
	// We'll use an enum to differentiate between shaders and shader programs when querying the info log
	enum class ObjectType
	{
		SHADER, PROGRAM
	};
 
	// Shader program and individual shader Ids
	GLuint programId;
	GLuint vertexShaderId;
	GLuint fragmentShaderId;
 
	// How many shaders are attached to the shader program
	GLuint shaderCount;
 
	// Map of attributes and their binding locations
	std::map<std::string, int> attributeMap;
 
	// Map of uniforms and their binding locations
	std::map<std::string, int> uniformMap;
 
	// Has this shader program been initialised?
	bool initialised;
 
	// ---------- PRIVATE METHODS ----------
 
	// Private method to compile a shader of a given type
	GLuint compileShader(std::string shaderSource, GLenum shaderType)
	{
		std::string shaderTypeString;
		switch (shaderType)
		{
			case GL_VERTEX_SHADER:
				shaderTypeString = "GL_VERTEX_SHADER";
				break;
			case GL_FRAGMENT_SHADER:
				shaderTypeString = "GL_FRAGMENT_SHADER";
				break;
			case GL_GEOMETRY_SHADER:
				throw std::runtime_error("Geometry shaders are unsupported at this time.");
				break;
			default:
				throw std::runtime_error("Bad shader type enum in compileShader.");
				break;
		}
 
		// Generate a shader id
		// Note: Shader id will be non-zero if successfully created.
		GLuint shaderId = glCreateShader(shaderType);
		if (shaderId == 0)
		{
			// Display the shader log via a runtime_error
			throw std::runtime_error("Could not create shader of type " + shaderTypeString + ": " + getInfoLog(ObjectType::SHADER, shaderId) );
		}
 
		// Get the source string as a pointer to an array of characters
		const char *shaderSourceChars = shaderSource.c_str();
 
		// Attach the GLSL source code to the shader
		// Params: GLuint shader, GLsizei count, const GLchar **string, const GLint *length
		// Note: The pointer to an array of source chars will be null terminated, so we don't need to specify the length and can instead use NULL.
		glShaderSource(shaderId, 1, &shaderSourceChars, NULL);
 
		// Compile the shader
		glCompileShader(shaderId);
 
		// Check the compilation status and throw a runtime_error if shader compilation failed
		GLint shaderStatus;
		glGetShaderiv(shaderId, GL_COMPILE_STATUS, &shaderStatus);
		if (shaderStatus == GL_FALSE)
		{
			throw std::runtime_error(shaderTypeString + " compilation failed: " + getInfoLog(ObjectType::SHADER, shaderId) );
		}
		else
		{
			if (DEBUG)
			{
				std::cout << shaderTypeString << " shader compilation successful." << std::endl;
			}
		}
 
		// If everything went well, return the shader id
		return shaderId;
	}
 
	// Private method to compile/attach/link/verify the shaders.
	// Note: Rather than returning a boolean as a success/fail status we'll just consider
	// a failure here to be an unrecoverable error and throw a runtime_error.
	void initialise(std::string vertexShaderSource, std::string fragmentShaderSource)
	{
		// Compile the shaders and return their id values
		vertexShaderId   = compileShader(vertexShaderSource,   GL_VERTEX_SHADER);
		fragmentShaderId = compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER);
 
		// Attach the compiled shaders to the shader program
		glAttachShader(programId, vertexShaderId);
		glAttachShader(programId, fragmentShaderId);
 
		// Link the shader program - details are placed in the program info log
		glLinkProgram(programId);
 
		// Once the shader program has the shaders attached and linked, the shaders are no longer required.
		// If the linking failed, then we're going to abort anyway so we still detach the shaders.
		glDetachShader(programId, vertexShaderId);
		glDetachShader(programId, fragmentShaderId);
 
		// Check the program link status and throw a runtime_error if program linkage failed.
		GLint programLinkSuccess = GL_FALSE;
		glGetProgramiv(programId, GL_LINK_STATUS, &programLinkSuccess);
		if (programLinkSuccess == GL_TRUE)
		{
			if (DEBUG)
			{
				std::cout << "Shader program link successful." << std::endl;
			}
		}
		else
		{
			throw std::runtime_error("Shader program link failed: " + getInfoLog(ObjectType::PROGRAM, programId) );
		}
 
		// Validate the shader program
		glValidateProgram(programId);
 
		// Check the validation status and throw a runtime_error if program validation failed
		GLint programValidatationStatus;
		glGetProgramiv(programId, GL_VALIDATE_STATUS, &programValidatationStatus);
		if (programValidatationStatus == GL_TRUE)
		{
			if (DEBUG)
			{
				std::cout << "Shader program validation successful." << std::endl;
			}
		}
		else
		{
			throw std::runtime_error("Shader program validation failed: " + getInfoLog(ObjectType::PROGRAM, programId) );
		}
 
		// Finally, the shader program is initialised
		initialised = true;
	}
 
	// Private method to load the shader source code from a file
	std::string loadShaderFromFile(const std::string filename)
	{
		// Create an input filestream and attempt to open the specified file
		std::ifstream file( filename.c_str() );
 
		// If we couldn't open the file we'll bail out
		if ( !file.good() )
		{
			throw std::runtime_error("Failed to open file: " + filename);
		}
 
		// Otherwise, create a string stream...
		std::stringstream stream;
 
		// ...and dump the contents of the file into it.
		stream << file.rdbuf();
 
		// Now that we've read the file we can close it
		file.close();
 
		// Finally, convert the stringstream into a string and return it
		return stream.str();
	}
 
	// Private method to return the current shader program info log as a string
	std::string getInfoLog(ObjectType type, int id)
	{
		GLint infoLogLength;
		if (type == ObjectType::SHADER)
		{
			glGetShaderiv(id, GL_INFO_LOG_LENGTH, &infoLogLength);
		}
		else // type must be ObjectType::PROGRAM
		{
			glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLogLength);
		}
 
		GLchar *infoLog = new GLchar[infoLogLength + 1];
		if (type == ObjectType::SHADER)
		{
			glGetShaderInfoLog(id, infoLogLength, NULL, infoLog);
		}
		else // type must be ObjectType::PROGRAM
		{
			glGetProgramInfoLog(id, infoLogLength, NULL, infoLog);
		}
 
		// Convert the info log to a string
		std::string infoLogString(infoLog);
 
		// Delete the char array version of the log
		delete[] infoLog;
 
		// Finally, return the string version of the info log
		return infoLogString;
	}
 
public:
	// Constructor
	ShaderProgram()
	{
		// We start in a non-initialised state - calling initFromFiles() or initFromStrings() will
		// initialise us.
		initialised = false;
 
		// Generate a unique Id / handle for the shader program
		// Note: We MUST have a valid rendering context before generating the programId or we'll segfault!
		programId = glCreateProgram();
		glUseProgram(programId);
 
		// Initially, we have zero shaders attached to the program
		shaderCount = 0;
	}
 
	// Destructor
	~ShaderProgram()
	{
		// Delete the shader program from the graphics card memory to
		// free all the resources it's been using
		glDeleteProgram(programId);
	}
 
	// Method to initialise a shader program from shaders provided as files
	void initFromFiles(std::string vertexShaderFilename, std::string fragmentShaderFilename)
	{
		// Get the shader file contents as strings
		std::string vertexShaderSource   = loadShaderFromFile(vertexShaderFilename);
		std::string fragmentShaderSource = loadShaderFromFile(fragmentShaderFilename);
 
		initialise(vertexShaderSource, fragmentShaderSource);
	}
 
	// Method to initialise a shader program from shaders provided as strings
	void initFromStrings(std::string vertexShaderSource, std::string fragmentShaderSource)
	{
		initialise(vertexShaderSource, fragmentShaderSource);
	}
 
	// Method to enable the shader program - we'll suggest this for inlining
	inline void use()
	{
		// Santity check that we're initialised and ready to go...
		if (initialised)
		{
			glUseProgram(programId);
		}
		else
		{
			std::string msg = "Shader program " + programId;
			msg += " not initialised - aborting.";
			throw std::runtime_error(msg);
		}
	}
 
	// Method to disable the shader - we'll also suggest this for inlining
	inline void disable()
	{
		glUseProgram(0);
	}
 
	// Method to return the bound location of a named attribute, or -1 if the attribute was not found
	GLuint attribute(const std::string attributeName)
	{
		// You could do this method with the single line:
		//
		//		return attributeMap[attribute];
		//
		// BUT, if you did, and you asked it for a named attribute which didn't exist
		// like: attributeMap["FakeAttrib"] then the method would return an invalid
		// value which will likely cause the program to segfault. So we're making sure
		// the attribute asked for exists, and if it doesn't then we alert the user & bail.
 
		// Create an iterator to look through our attribute map (only create iterator on first run -
		// reuse it for all further calls).
		static std::map<std::string, int>::const_iterator attributeIter;
 
		// Try to find the named attribute
		attributeIter = attributeMap.find(attributeName);
 
		// Not found? Bail.
		if ( attributeIter == attributeMap.end() )
		{
			throw std::runtime_error("Could not find attribute in shader program: " + attributeName);
		}
 
		// Otherwise return the attribute location from the attribute map
		return attributeMap[attributeName];
	}
 
	// Method to returns the bound location of a named uniform
	GLuint uniform(const std::string uniformName)
	{
		// Note: You could do this method with the single line:
		//
		// 		return uniformLocList[uniform];
		//
		// But we're not doing that. Explanation in the attribute() method above.
 
		// Create an iterator to look through our uniform map (only create iterator on first run -
		// reuse it for all further calls).
		static std::map<std::string, int>::const_iterator uniformIter;
 
		// Try to find the named uniform
		uniformIter = uniformMap.find(uniformName);
 
		// Found it? Great - pass it back! Didn't find it? Alert user and halt.
		if ( uniformIter == uniformMap.end() )
		{
			throw std::runtime_error("Could not find uniform in shader program: " + uniformName);
		}
 
		// Otherwise return the attribute location from the uniform map
		return uniformMap[uniformName];
	}
 
	// Method to add an attribute to the shader and return the bound location
	int addAttribute(const std::string attributeName)
	{
		// Add the attribute location value for the attributeName key
		attributeMap[attributeName] = glGetAttribLocation( programId, attributeName.c_str() );
 
		// Check to ensure that the shader contains an attribute with this name
		if (attributeMap[attributeName] == -1)
		{
			throw std::runtime_error("Could not add attribute: " + attributeName + " - location returned -1.");
		}
		else // Valid attribute location? Inform user if we're in debug mode.
		{
			if (DEBUG)
			{
				std::cout << "Attribute " << attributeName << " bound to location: " << attributeMap[attributeName] << std::endl;
			}
		}
 
		// Return the attribute location
		return attributeMap[attributeName];
	}
 
	// Method to add a uniform to the shader and return the bound location
	int addUniform(const std::string uniformName)
	{
		// Add the uniform location value for the uniformName key
		uniformMap[uniformName] = glGetUniformLocation( programId, uniformName.c_str() );
 
		// Check to ensure that the shader contains a uniform with this name
		if (uniformMap[uniformName] == -1)
		{
			throw std::runtime_error("Could not add uniform: " + uniformName + " - location returned -1.");
		}
		else // Valid uniform location? Inform user if we're in debug mode.
		{
			if (DEBUG)
			{
				std::cout << "Uniform " << uniformName << " bound to location: " << uniformMap[uniformName] << std::endl;
			}
		}
 
		// Return the uniform location
		return uniformMap[uniformName];
	}
 
}; // End of class
 
#endif // SHADER_PROGRAM_HPP

Note: I’m not convinced that the compiler will even consider inlining the use() and disable() methods when the program is built as a hpp – I think I’d have to break it into .h and .cpp files for that… other than that I’m thinking the above code is pretty clean, robust and usable.

GLFW3 Basecode with FPS Camera Controls

Basecode is funny thing – when you start a new project, do you really start from scratch? A complete blank slate? Or do you make a copy of the last project you worked on which is similar and modify it? Often, you’re going to want to start from some pre-existing functional base, but what’s stable and functional enough? Do you really want to go with a framework like Cinder or Processing to hold your code? Or go with a full-on engine like Unity or Unreal Engine 4 or some other engine?

I’m going to write a game at some point in the future, and I want to go it (almost) alone – I don’t want to be locked into someone elses constructs and patterns, or drag-and-drop functionality in which I have absolutely no idea how it works – I want to think for myself and create what’s basically my own engine, where I understand how it fits together and how each piece works. This doesn’t necessarily mean that everything needs to be worked out from first principles, but it should be possible to make all the important architectural decisions. This means that I want precise control over:

  • At least one OpenGL window, with controllable context details (preferably multiple windows with a shared projection)
  • Painless keyboard and mouse handlers
  • File handling of common types (load and use this 3D model/sound file/settings file)
  • Help with prototyping via simple drawing calls

Which brings us back to basecode being a funny thing – you get to make the architectural decisions, and live with the consequences. If you decide to go with an engine, then you’re going to learn the engine – not the fundamental technologies or aspects of the code that make the engine work. So if you grab some fantastic engine and you go:

  1. Load this spaceship model, which is made of these different materials,
  2. There’s a light which is at (1000, 200, 300) in world space (and perhaps a dozen other lights),
  3. Draw the spaceship from my (i.e the camera’s) location.

But what does that actually teach you, as a developer? How do you load the model from file? How is the lighting model applied to the vertices? Where the hell is the spaceship in relation to you, let alone the surface normals of the spaceship with regard to the light-source(s) with regard to the camera? In an engine, you don’t care – you let the engine work it out for you, and you learn nothing. Or maybe you learn the engine – which means you learn to trust someone else to think instead of you having to think for yourself.

Which finally brings us back to basecode being a funny thing… I’ve been thinking about this for weeks, and below is the OpenGL/GLFW3 basecode I’ve written to open a window, draw some grids for orientation, and allow for ‘FPS-esque’ mouse and keyboard controls. The main.cpp is listed below, which shows you how the program itself runs – everything else you’ll need to look at for yourself – but I promise you this:

  • Every single piece of this code is clear in its use and serves a purpose.
  • Every single piece of this code performs its job in the simplest, most straight forward manner possible. If the option is to be clever or readable, then I pick readable every time. Saying that, I think I used an inline if-statement once i.e. “if (raining) ? putUpUmbrella() : keepUmbrellaDown();”. Honestly, when you see it, you’ll be okay.
  • Every single piece of this code is documented to explain not only WHAT the code is doing, but (where appropriate) WHY it is doing it. When I used to work as as Subsystem Integration and Test engineer, we would write software build instructions with the goal that your Mum should be able to build the software image from the simple, accurate, non-ambiguous instructions. If you didn’t think your Mum could build it, then you re-worked the instructions until you thought that she could.

I’ll add some additional utility classes to this over time, but for now, this basecode will get a window with FPS controls up and running and display some grids via shaders for orientation – and everything should be simple, straight-forward and clear. Enjoy!

Code::Blocks projects for both Windows and Linux (libraries included for Windows) can be found here: GLFW3_Basecode_Nov_2014.7z.

Update – Feb 2015: There were issues using this code in Visual Studio 2010 as it doesn’t support strongly typed enums or the R” notation (although VS2012 onwards does), and the libraries packaged were the Code::Blocks versions (which was intended – the above version is specifically for Code::Blocks) – so here’s a modified & fully working Visual Studio 2010 version: GLFW3-Basecode-VS2010.7z.

/***
Project: GLFW3 Basecode
Version: 0.5
Author : r3dux
Date   : 21/1/2014
Purpose: Basecode to setup an OpenGL context with FPS camera controls and draw some grids.
***/
 
#include <iostream>
 
// Define that we're using the static version of GLEW (glew32s) so that it gets built
// into our final executable.
// NOTE: This MUST be defined before importing GLEW!
#define GLEW_STATIC
 
// Include the GL Extension Wrangler. Note: GLEW should always be the very first include
#include <GL/glew.h>
 
#include <GLFW/glfw3.h>                 // Include GL Framework. Note: This pulls in GL.h for us.
 
// Include the GL Mathematics library
#define GLM_FORCE_RADIANS               // We must work in radians in newer versions of GLM...
#include <glm/glm.hpp>                  // ...so now that's defined we can import GLM itself.
 
// Include our custom classes
#include "Camera.h"
#include "Grid.h"
#include "Utils.h"
 
// Save ourselves some typing...
using std::cout;
using std::endl;
using glm::vec3;
using glm::vec4;
using glm::mat4;
using glm::mat3;
 
// ---------- Global variables ----------
 
// Window and projection settings
GLsizei windowWidth       = 800;
GLsizei windowHeight      = 600;
float vertFieldOfViewDegs = 45.0f;
float nearClipDistance    = 1.0f;
float farClipDistance     = 2000.0f;
 
// Misc
int  frameCount = 0;              // How many frames we've drawn
int  frameRate  = 60;             // Target frame rate -we'll assume a 60Hz refresh for now
bool leftMouseButtonDown = false; // We'll only look around when the left mouse button is down
 
// Matricies
mat4 projectionMatrix; // The projection matrix is used to perform the 3D to 2D conversion i.e. it maps from eye space to clip space.
mat4 viewMatrix;       // The view matrix maps the world coordinate system into eye cordinates (i.e. world space to eye space)
mat4 modelMatrix;      // The model matrix maps an object's local coordinate system into world coordinates (i.e. model space to world space)
 
// Pointers to two grids
Grid *upperGrid, *lowerGrid;
 
// Camera. Params: location, rotation (degrees), window width & height
Camera camera(vec3(0.0f), vec3(0.0f), windowWidth, windowHeight);
 
// Callback function to resize the window and set the viewport to the correct size
void resizeWindow(GLFWwindow *window, GLsizei newWidth, GLsizei newHeight)
{
    // Keep track of the new width and height of the window
    windowWidth  = float(newWidth);
    windowHeight = float(newHeight);
 
    // Recalculate the projection matrix
    projectionMatrix = glm::perspective(vertFieldOfViewDegs, GLfloat(windowWidth) / GLfloat(windowHeight), nearClipDistance, farClipDistance);
 
    // Viewport is the entire window
    glViewport(0, 0, windowWidth, windowHeight);
 
    // Update the midpoint location in the camera class because it uses these values, too
    camera.updateWindowMidpoint(windowWidth, windowHeight);
}
 
// Callback function to handle keypresses
void handleKeypress(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    // User hit ESC? Set the window to close
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE);
    }
    else
    {
        camera.handleKeypress(key, action);
    }
}
 
// Callback function to handle mouse movement
void handleMouseMove(GLFWwindow *window, double mouseX, double mouseY)
{
    // We'll only look around when the left mouse button is down
    if (leftMouseButtonDown)
    {
        camera.handleMouseMove(window, mouseX, mouseY);
    }
}
 
// Callback function to handle mouse button presses
void handleMouseButton(GLFWwindow *window, int button, int action, int mods)
{
    // Button press involves left mouse button?
    if (button == GLFW_MOUSE_BUTTON_1)
    {
        if (action == GLFW_PRESS)
        {
            glfwSetCursorPos(window, windowWidth / 2, windowHeight / 2);
            leftMouseButtonDown = true;
        }
        else // Action must be GLFW_RELEASE
        {
            leftMouseButtonDown = false;
        }
    }
}
 
// Function to set up our OpenGL rendering context
void initGL(GLFWwindow *window)
{
    // ---------- Initialise GLEW ----------
 
    // Enable glewExperimental which ensures that all extensions with valid entry points will be exposed.
    glewExperimental = true;
 
    // Note: We MUST have an OpenGL rendering context open to initialise GLEW successfully!
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        cout << "GLEW error: " << glewGetErrorString(err) << endl;
        glfwTerminate();
    }
    cout << "GLEW intialised successfully. Using GLEW version: " << glewGetString(GLEW_VERSION) << endl << endl;
 
    // Depending on the OpenGL context settings, calling glewInit() can sometimes cause a GL_INVALID_ENUM error.
    // As this issue isn't really our code's fault, we'll check the error here to clear it.
    //
    // Cause: In a core profile context, GL_EXTENSIONS is an invalid constant to pass to glGetString (...). You
    // must use the new glGetStringi (...) function. GLEW does not do this by default, given a core context
    // without being informed to use glGetStringi (...), GLEW will use glGetString (...) and will cause GL to
    // generate a GL_INVALID_ENUM error. In order to get GLEW to use glGetStringi (...) (which you should ONLY
    // do in an OpenGL 3.0+ context), set glewExperimental = true; before calling glewInit (...).
    //
    // Source: http://stackoverflow.com/questions/19453439/solved-opengl-error-gl-invalid-enum-0x0500-while-glewinit
    checkGLError("glewInit - harmless / ignore");
 
    // ---------- Setup OpenGL Options ----------
 
    glViewport( 0, 0, GLsizei(windowWidth), GLsizei(windowHeight) ); // Viewport is entire window
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);                            // Clear to black with full alpha
    glEnable(GL_DEPTH_TEST);                                         // Enable depth testing
    glDepthFunc(GL_LEQUAL);                                          // Specify depth testing function
    glClearDepth(1.0);                                               // Clear the full extent of the depth buffer (default)
    glEnable(GL_CULL_FACE);                                          // Enable face culling
    glCullFace(GL_BACK);                                             // Cull back faces of polygons
    glFrontFace(GL_CCW);                                             // Counter-clockwise winding indicates a forward facing polygon (default)
 
    // ---------- Setup GLFW Callback Functions ----------
 
    glfwSetWindowSizeCallback(window, resizeWindow);                 // Register window resize functiom
    glfwSetKeyCallback(window, handleKeypress);                      // Register keyboard handler function
    glfwSetCursorPosCallback(window, handleMouseMove);               // Register mouse movement handler function
    glfwSetMouseButtonCallback(window, handleMouseButton);           // Register mouse button handler function
 
    // ---------- Setup GLFW Options ----------
 
    glfwSwapInterval(1);                                             // Swap buffers every frame (i.e. lock to VSync)
    glfwSetInputMode(window, GLFW_CURSOR_DISABLED, GL_FALSE);        // Do not hide the mouse cursor
    glfwSetWindowPos(window, 200, 200);                              // Push the top-left of the window out from the top-left corner of the screen
    glfwSetCursorPos(window, windowWidth / 2, windowHeight / 2);     // Move the mouse cursor to the centre of the window
 
    checkGLError("InitGL");
}
 
// Function to perform our drawing
void drawFrame()
{
    // Move the camera
    camera.move(1.0f/ frameRate);
 
    // ---------- Matrix operations ----------
 
    // Reset our View matrix
    viewMatrix = mat4(1.0f);
 
    // Perform camera rotation
    viewMatrix = glm::rotate(viewMatrix, camera.getXRotationRads(), X_AXIS);
    viewMatrix = glm::rotate(viewMatrix, camera.getYRotationRads(), Y_AXIS);
 
    // Translate to our camera position
    viewMatrix = glm::translate(viewMatrix, -camera.getLocation() );
 
    // Create an identity matrix for the model matrix
    modelMatrix = mat4(1.0f);
 
    // ---------- Drawing operations ----------
 
    mat4 mvpMatrix = projectionMatrix * viewMatrix * modelMatrix;
    upperGrid->draw(mvpMatrix);
    lowerGrid->draw(mvpMatrix);
}
 
int main()
{
    // ----- Initialiise GLFW, specify window hints & open a context -----
 
    // IMPORTANT: glfwInit resets all window hints, so we must call glfwInit FIRST and THEN we supply window hints!
    if (!glfwInit())
    {
        cout << "glfwInit failed!" << endl;
        exit(-1);
    }
 
    // Further reading on GLFW window hints: http://www.glfw.org/docs/latest/window.html#window_hints
 
    // If we want to use a a core profile (i.e. no legacy fixed-pipeline functionality) or if we want to
    // use forward compatible mode (i.e. only non-deprecated features of a given OpenGL version available)
    // then we MUST specify the MAJOR.MINOR context version we want to use FIRST!
    //glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    //glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 
    // Ask for 4x Anti-Aliasing
    glfwWindowHint(GLFW_SAMPLES, 4);
 
    // Create a window. Params: width, height, title, *monitor, *share
    GLFWwindow* window = glfwCreateWindow(GLsizei(windowWidth), GLsizei(windowHeight), "GLFW3 Basecode | Use WSAD to move & LMB to look around - Nov 2014 | r3dux.org ", NULL, NULL);
    if (!window)
    {
        cout << "Failed to create window - bad context MAJOR.MINOR version?" << endl;
        getKeypressThenExit();
    }
 
    // Make the current OpenGL context active
    glfwMakeContextCurrent(window);
 
    // Display the details of our OpenGL window
    displayWindowProperties(window);
 
    // -------------- Set up our OpenGL settings ---------------
 
    initGL(window);
 
    // ---------- Set up our grids ----------
 
    // Instantiate our grids. Params: Width, Depth, level (i.e. location of y-axis), number of grid lines
    upperGrid = new Grid(1000.0f, 1000.0f,  200.0f, 20);
    lowerGrid = new Grid(1000.0f, 1000.0f, -200.0f, 20);
 
    // ---------- Set up our matricies ----------
 
    // Specify the projection matrix
    projectionMatrix = glm::perspective(vertFieldOfViewDegs, GLfloat(windowWidth) / GLfloat(windowHeight), nearClipDistance, farClipDistance);
 
    // Reset the view and model and view matrices to identity
    viewMatrix  = mat4(1.0f);
    modelMatrix = mat4(1.0f);
 
    // ---------- Main loop ----------
 
    while ( !glfwWindowShouldClose(window) )
    {
        frameCount++;
 
        // Clear the screen and depth buffer
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 
        // Draw our frame
        drawFrame();
 
        // Swap the back and front buffers to display the frame we just rendered
        glfwSwapBuffers(window);
 
        // Poll for input
        glfwPollEvents();
    }
 
    // Check the final error state
    // NOTE: This MUST be called while we still have a valid rendering context (i.e. before we call glfwTerminate() )
    checkGLError("End");
 
    // Destroy the window and exit
    glfwDestroyWindow(window);
    glfwTerminate();
 
    return 0;
}

How To: Load an OpenGL Texture using the FreeImage library (or FreeImagePlus, technically)

I’d been using DevIL (or OpenIL if you prefer) as my image loader library, only it hasn’t been updated in a long time and can be a pain to build properly, so I’ve needed to move onto something else. The DevIL fork, ResIL, isn’t quite ready for prime-time and my attempts to build it have resulted in wasted hours and failure (I’ve fixed multiple build errors on the way – but then I hit stuff I can’t see how to fix without trying to re-package the entire thing).

So, new image library, huh? I had a google and saw the FreeImage library (and it’s FreeImagePlus version for C++ – documentation for which can be found here) – and it just happens to be in the Arch community repos so a quick sudo pacman -S freeimage later and we’re ready to rock… Only it didn’t come with any examples, so I dug around on forums and found some code that just about worked, but I wasn’t a big fan of it – so I thought I’d rewrite it into a nicer, more robust method – and I think I’ve achieved just that.

A textured quad at an angle.

The loadTexture Method

 
// Method to load an image into a texture using the freeimageplus library. Returns the texture ID or dies trying.
GLuint loadTexture(string filenameString, GLenum minificationFilter = GL_LINEAR, GLenum magnificationFilter = GL_LINEAR)
{
    // Get the filename as a pointer to a const char array to play nice with FreeImage
    const char* filename = filenameString.c_str();
 
    // Determine the format of the image.
    // Note: The second paramter ('size') is currently unused, and we should use 0 for it.
    FREE_IMAGE_FORMAT format = FreeImage_GetFileType(filename , 0);
 
    // Image not found? Abort! Without this section we get a 0 by 0 image with 0 bits-per-pixel but we don't abort, which
    // you might find preferable to dumping the user back to the desktop.
    if (format == -1)
    {
        cout << "Could not find image: " << filenameString << " - Aborting." << endl;
        exit(-1);
    }
 
    // Found image, but couldn't determine the file format? Try again...
    if (format == FIF_UNKNOWN)
    {
        cout << "Couldn't determine file format - attempting to get from file extension..." << endl;
 
        // ...by getting the filetype from the filename extension (i.e. .PNG, .GIF etc.)
        // Note: This is slower and more error-prone that getting it from the file itself,
        // also, we can't use the 'U' (unicode) variant of this method as that's Windows only.
        format = FreeImage_GetFIFFromFilename(filename);
 
        // Check that the plugin has reading capabilities for this format (if it's FIF_UNKNOWN,
        // for example, then it won't have) - if we can't read the file, then we bail out =(
        if ( !FreeImage_FIFSupportsReading(format) )
        {
            cout << "Detected image format cannot be read!" << endl;
            exit(-1);
        }
    }
 
    // If we're here we have a known image format, so load the image into a bitap
    FIBITMAP* bitmap = FreeImage_Load(format, filename);
 
    // How many bits-per-pixel is the source image?
    int bitsPerPixel =  FreeImage_GetBPP(bitmap);
 
    // Convert our image up to 32 bits (8 bits per channel, Red/Green/Blue/Alpha) -
    // but only if the image is not already 32 bits (i.e. 8 bits per channel).
    // Note: ConvertTo32Bits returns a CLONE of the image data - so if we
    // allocate this back to itself without using our bitmap32 intermediate
    // we will LEAK the original bitmap data, and valgrind will show things like this:
    //
    // LEAK SUMMARY:
    //  definitely lost: 24 bytes in 2 blocks
    //  indirectly lost: 1,024,874 bytes in 14 blocks    <--- Ouch.
    //
    // Using our intermediate and cleaning up the initial bitmap data we get:
    //
    // LEAK SUMMARY:
    //  definitely lost: 16 bytes in 1 blocks
    //  indirectly lost: 176 bytes in 4 blocks
    //
    // All above leaks (192 bytes) are caused by XGetDefault (in /usr/lib/libX11.so.6.3.0) - we have no control over this.
    //
    FIBITMAP* bitmap32;
    if (bitsPerPixel == 32)
    {
        cout << "Source image has " << bitsPerPixel << " bits per pixel. Skipping conversion." << endl;
        bitmap32 = bitmap;
    }
    else
    {
        cout << "Source image has " << bitsPerPixel << " bits per pixel. Converting to 32-bit colour." << endl;
        bitmap32 = FreeImage_ConvertTo32Bits(bitmap);
    }
 
    // Some basic image info - strip it out if you don't care
    int imageWidth  = FreeImage_GetWidth(bitmap32);
    int imageHeight = FreeImage_GetHeight(bitmap32);
    cout << "Image: " << filenameString << " is size: " << imageWidth << "x" << imageHeight << "." << endl;
 
    // Get a pointer to the texture data as an array of unsigned bytes.
    // Note: At this point bitmap32 ALWAYS holds a 32-bit colour version of our image - so we get our data from that.
    // Also, we don't need to delete or delete[] this textureData because it's not on the heap (so attempting to do
    // so will cause a crash) - just let it go out of scope and the memory will be returned to the stack.
    GLubyte* textureData = FreeImage_GetBits(bitmap32);
 
    // Generate a texture ID and bind to it
    GLuint tempTextureID;
    glGenTextures(1, &tempTextureID);
    glBindTexture(GL_TEXTURE_2D, tempTextureID);
 
    // Construct the texture.
    // Note: The 'Data format' is the format of the image data as provided by the image library. FreeImage decodes images into
    // BGR/BGRA format, but we want to work with it in the more common RGBA format, so we specify the 'Internal format' as such.
    glTexImage2D(GL_TEXTURE_2D,    // Type of texture
                 0,                // Mipmap level (0 being the top level i.e. full size)
                 GL_RGBA,          // Internal format
                 imageWidth,       // Width of the texture
                 imageHeight,      // Height of the texture,
                 0,                // Border in pixels
                 GL_BGRA,          // Data format
                 GL_UNSIGNED_BYTE, // Type of texture data
                 textureData);     // The image data to use for this texture
 
    // Specify our minification and magnification filters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minificationFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnificationFilter);
 
    // If we're using MipMaps, then we'll generate them here.
    // Note: The glGenerateMipmap call requires OpenGL 3.0 as a minimum.
    if (minificationFilter == GL_LINEAR_MIPMAP_LINEAR   ||
        minificationFilter == GL_LINEAR_MIPMAP_NEAREST  ||
        minificationFilter == GL_NEAREST_MIPMAP_LINEAR  ||
        minificationFilter == GL_NEAREST_MIPMAP_NEAREST)
    {
        glGenerateMipmap(GL_TEXTURE_2D);
    }
 
    // Check for OpenGL texture creation errors
    GLenum glError = glGetError();
    if(glError)
    {
        cout << "There was an error loading the texture: "<< filenameString << endl;
 
        switch (glError)
        {
            case GL_INVALID_ENUM:
                cout << "Invalid enum." << endl;
                break;
 
            case GL_INVALID_VALUE:
                cout << "Invalid value." << endl;
                break;
 
            case GL_INVALID_OPERATION:
                cout << "Invalid operation." << endl;
 
            default:
                cout << "Unrecognised GLenum." << endl;
                break;
        }
 
        cout << "See https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml for further details." << endl;
    }
 
    // Unload the 32-bit colour bitmap
    FreeImage_Unload(bitmap32);
 
    // If we had to do a conversion to 32-bit colour, then unload the original
    // non-32-bit-colour version of the image data too. Otherwise, bitmap32 and
    // bitmap point at the same data, and that data's already been free'd, so
    // don't attempt to free it again! (or we'll crash).
    if (bitsPerPixel != 32)
    {
        FreeImage_Unload(bitmap);
    }
 
    // Finally, return the texture ID
    return tempTextureID;
}

Usage

To use the method, just set up your code something like this – in your main .cpp file at the top have:

#include <iostream>
#include <cstdlib>
 
// OpenGL/GLEW/GLFW/SDL/Whatever headers
 
// Include the FreeImagePlus library (don't forget to link your project to the .so/.a or to the lib/dll on Windows)
#include <FreeImagePlus.h>
 
using std::string;
using std::cout;
using std::endl;
 
// Create a handle for our texture
GLuint textureID;

In your initialisation/setup method add something like this:

//  ----- Initialise the FreeImage library -----
// Note: Flag is whether we should load ONLY local (built-in) libraries, so
// false means 'no, use external libraries also', and 'true' means - use built
// in libs only, so it's like using the library as a static version of itself.
FreeImage_Initialise(true);

In your main method, once you’ve create an OpenGL context (i.e. window) and called your initialise/setup method, then you can load a texture to use like this:

// Load an image and bind it to a texture
textureID = loadTexture("TestImage.png");
 
// Or load an image using trilinear filtering via mipmaps for minification and GL_LINEAR (the method's default) for magnification
textureID = loadTexture("TestImage.png", GL_LINEAR_MIPMAP_LINEAR);
 
// Or load an image using low quality mipmaps for minification and GL_NEAREST for magnification.
// The point is that you can set the minification and magnification parameters separately, not necessarily that you'd choose these interpolation options.
textureID = loadTexture("TestImage.png", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST);

Finally, on shutdown you may (optionally, as long as you’re not using the Linux/Mac .a version of the library) like to add the following:

// As we're using a .so version of the freeimageplus library on Linux we
// don't need to call DeInitialise (we would only really HAVE to do so on Linux
// or Mac [not Windows!] when using the .a version) - but it doesn't hurt to do it anyway.
FreeImage_DeInitialise();

Wrap-up

Not a great deal to say – it works, it should be relatively robust, it doesn’t leak memory (or at least none that we control), and the code is commented to the hilt and explains why it’s doing things instead of just what it’s doing (some people don’t like verbose code like this – but I teach a lot of programming so I just try to explain everything as clearly as possible as I go – and find I prefer it to ‘terse’ code that I have no idea WTF it’s up to or why it’s bothering).

Many thanks to the FreeImage devs for the library – it’s rather feature-packed and I quite like it (some good example code wouldn’t go amiss though!) =D

Hope you find the code useful! (Bugs? Comments? Suggestions? Feedback always welcome!).

Bitwise Operators in C++

I’ve been perfectly happy with logical operators since I started programming a long, long time ago. That is, your standard AND (&&), OR (||) and NOT (!) operators perform the relevant logical operations so you can say:

if ((isColdOut) || (isRaining)) { wearCoat(); }

or,

if ((isSunny) && (haveSunglasses)) { wearSunglasses(); }

or,

if (!batteryHasCharge) { chargePhone(); }

But… my understanding of bitwise operators was sketchier. So let’s take a look at the bitwise operators and give them a runthrough to see what they do.

Bitwise Operator List

Let’s start by listing the available bitwise operators:

Bitwise Operator Symbol
& Bitwise AND
| Bitwise Inclusive OR
^ Bitwise Exclusive OR
~ Bitwise NOT
<< Bitwise Left-shift
>> Bitwise Right-shift

So we have 6 basic operators, but we also have the compound versions of these operators, so instead of saying:

A = A & B;

We can say:

A &= B;

And they’ll mean the exact same thing – just for completeness, let’s throw those in a table as well:

Bitwise Operator Symbol
&= Bitwise Compound AND
|= Bitwise Compound Inclusive OR
^= Bitwise Compound Exclusive OR
~= Bitwise Compound NOT
<<= Bitwise Compound Left-shift
>>= Bitwise Compound Right-shift

So now that we have these operators, let’s see what we can do with them…

Continue reading Bitwise Operators in C++

The This Pointer in C++

2017-02 Update

I didn’t know about boxing and unboxing of primitives to objects (or much about references as it happens) when I wrote this back in 2013. Primitives like int/bool/float/char/double etc. DO get copied via the default copy constructor because they have default working copy/assignment functions. However if the Example class held an instance of, say, a DeepCopyWang object (it was the first thing I could think of) – it wouldn’t copy cleanly with the default assignment or copy constructors – any new object would just hold a reference to the original DeepCopyWang object and its properties. That is, the new object trying to be a copy of the first would be a shallow copy (i.e. it may contain references to properties of the first object) and not a deep copy (i.e. completely separate objects and all properties there-in which can be individually manipulated). So take the below with a suitably sized pinch of salt.


Still on my fundamentals trip, I’m hitting up the ‘this’ pointer. Every class that you create has a ‘this’ pointer invisibly assigned to it by the compiler. Let’s look at a simple class to see what’s going on:

class Example
{
private:
	int a;
public:
	void setA(int value) { a = value; }
	int  getA()          { return a;  }
};

When you write the above code the compiler does some fun things with it, such as invisibly adding four methods:

  • A default constructor (that takes no parameters) which is automatically executed when you instantiate an object of this type,
  • A destructor (again no parameters) which is automatically executed when an object of this type is deleted or goes out of scope,
  • A copy constructor (that takes another object of this type) and performs a shallow copy from the source object to the (new) destination object, and
  • An assignment operator (that takes another object of this type) and which again performs a shallow copy from that object the the object you’re assigning to.

If we explicitly write these four methods into our class, we end up with our (exactly, exactly equivalent) code now being:

class Example
{
private:
	int a;
public:
	// Constructor
	Example()
	{
		// Do nothing
	}
 
	// Destructor
	~Example()
	{
		// Do nothing
	}
 
	// Copy constructor
	Example(const Example& rhs)
	{
		a = rhs.a;
	}
 
	// Overloaded assignment operator
	Example& operator=(const Example& rhs)
	{
		if (this == &rhs)
			return *this;
 
		a = rhs.a;
		return *this;
	}
 
	void setA(int value) { a = value; }
	int  getA()          { return a;  }
};

You can substitute either of these classes into a project, compile it (in Release mode if you have both in there and just comment each out in turn!), and you’ll end up with byte-wise identical executables down to the very last bit. Not only are they functionally equivalent, they’re absolutely equivalent – as the compiler sees them, it’s the exact same code. Don’t take my word for it – try it out, if you’d like!

The ‘this’ pointer’s already being used, but what exactly is it doing? Well, let’s drill down into the nuts and bolts of it and take a look…

Continue reading The This Pointer in C++