Can't get textured triangle to display.

I’m just now learning the sequence of commands necessary to apply a texture to a triangle in OpenGL, but I can’t seem to get my code to display a textured triangle. I think I’m creating the Texture object properly and filling it with data correctly, but the best I get is a black triangle over whatever color I set with glClearColor(). Here is the source for the main file:

#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
using namespace std;
/* If using gl3.h */
/* Ensure we are using opengl's core profile only */
#include "vgl.h"
#define PROGRAM_NAME "Tutorial1"

#include "BufferOffset.h"
#include "LoadShaders.h"
#include "test.h"

SDL_Window *	mainWindow; /* Our window handle */
SDL_GLContext	mainContext; /* Our OpenGL context handle */

enum VAO_IDs { Vertices, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Texture_IDs { Texture, NumTextures };

GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
GLuint Textures[NumTextures];

const GLsizei NumVertices = 3;

struct vertex {
	GLfloat pos[2];
	GLfloat texCoord[2];
};

GLint vPositionLoc, vTexCoordLoc;
GLint colorMapLoc;
GLuint program;

bool running = true;

/* SDL timer executed every 1/60th of a second (60 frames per second). */
Uint32 timer(Uint32 interval, void * param) {
	return interval;
}
 
//-----------------------------------------------------------------------------
//
// display
//

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT);

	glUseProgram(program);
	glBindVertexArray(VAOs[Vertices]);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, Textures[Texture]);
	glDrawArrays(GL_TRIANGLES, 0, NumVertices);
}

//-----------------------------------------------------------------------------
//
// init
//

void init(void) {
	glGenVertexArrays(NumVAOs, VAOs);
	glBindVertexArray(VAOs[Vertices]);

	vertex vertices[NumVertices] = {
		{ { -1.0f, -1.0f, }, { 0.0f, 0.0f, }, }, // Lower left
		{ { 1.0f, -1.0f, }, { 1.0f, 0.0f, }, }, // Lower right
		{ { 0.0f, 1.0f, }, { 0.5f, 1.0f, }, }, // Upper middle
	};

	glGenBuffers(NumBuffers, Buffers);
	glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	ShaderInfo shaders = {
		GL_VERTEX_SHADER, "triangles.vert",
		GL_FRAGMENT_SHADER, "triangles.frag",
	};

	program = LoadShaders(shaders);
	glUseProgram(program);

	vPositionLoc = glGetAttribLocation(program, "vPosition");
	glVertexAttribPointer(vPositionLoc, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), BUFFER_OFFSET(offsetof(vertex, pos)));
	glEnableVertexAttribArray(vPositionLoc);

	vTexCoordLoc = glGetAttribLocation(program, "vTexCoord");
	glVertexAttribPointer(vTexCoordLoc, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), BUFFER_OFFSET(offsetof(vertex, texCoord)));
	glEnableVertexAttribArray(vTexCoordLoc);

	glGenTextures(NumTextures, Textures);
	glBindTexture(GL_TEXTURE_2D, Textures[Texture]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, test.bytes_per_pixel, test.width, test.height, 0, GL_RGB, GL_UNSIGNED_BYTE, test.pixel_data);
	colorMapLoc = glGetUniformLocation(program, "colorMap");
	glUniform1i(colorMapLoc, 0);

	/* Clear our buffer with a black background */
	glClearColor ( 0.0, 0.0, 0.0, 1.0 );

	SDL_AddTimer(16, timer, NULL);
}

//-----------------------------------------------------------------------------
//
// process events
//

void process_events(void) {
	SDL_Event event;
	while (SDL_PollEvent(&event)) {
		switch (event.type) {
			case SDL_QUIT:
				running = false;
				break;
			case SDL_WINDOWEVENT:
				switch (event.window.event) {
					case SDL_WINDOWEVENT_SHOWN:
					case SDL_WINDOWEVENT_EXPOSED:
					case SDL_WINDOWEVENT_MAXIMIZED:
					case SDL_WINDOWEVENT_RESTORED:
						display();
						break;
					case SDL_WINDOWEVENT_RESIZED:
						glViewport(0, 0, event.window.data1, event.window.data2);
						break;
					case SDL_WINDOWEVENT_CLOSE:
						running = false;
						break;
					default:
						break;
				}
				break;
		}
	}
}

/* A simple function that prints a message, the error code returned by SDL,
 * and quits the application */
void sdldie(const char *msg) {
	printf("%s: %s
", msg, SDL_GetError());
	SDL_Quit();
	exit(1);
}
 
 
void checkSDLError(int line = -1) {
#ifndef NDEBUG
        const char *error = SDL_GetError();
        if (*error != '\0') {
                printf("SDL Error: %s
", error);
                if (line != -1)
                        printf(" + line: %i
", line);
                SDL_ClearError();
        }
#endif
}
 
/* Our program's entry point. */
int main(int argc, char *argv[])
{
	if (SDL_Init(SDL_INIT_VIDEO) < 0) /* Initialize SDL's Video subsystem */
	sdldie("Unable to initialize SDL"); /* Or die on error */

	/* Request opengl 3.3 context.
	* SDL doesn't have the ability to choose which profile at this time of writing,
	* but it should default to the core profile */
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

	/* Turn on double buffering with a 24bit Z buffer.
	* You may need to change this to 16 or 32 for your system */
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

	/* Create our window centered at 512x512 resolution */
	mainWindow = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
			512, 512, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
	if (!mainWindow) /* Die if creation failed */
		sdldie("Unable to create window");
 
	checkSDLError(__LINE__);

	/* Create our opengl context and attach it to our window */
	mainContext = SDL_GL_CreateContext(mainWindow);
	checkSDLError(__LINE__);

	/* This makes our buffer swap syncronized with the monitor's vertical refresh */
	SDL_GL_SetSwapInterval(1);

	init();

	while (running) {
		process_events();
		display();
		SDL_GL_SwapWindow(mainWindow);
	}

	/* Delete our opengl context, destroy our window, and shutdown SDL */
	glDeleteProgram(program);
	glDeleteTextures(NumTextures, Textures);
	SDL_GL_DeleteContext(mainContext);
	SDL_DestroyWindow(mainWindow);
	SDL_Quit();

	return EXIT_SUCCESS;
}

The vertex shader:

#version 330 core

in vec4 vPosition;
in vec2 vTexCoord;

out vec2 fTexCoord;

void main() {
	gl_Position = vPosition;
	fTexCoord = vTexCoord;
}

And the fragment shader:

#version 330 core

in vec2 fTexCoord;

uniform sampler2D colorMap;

void main() {
	gl_FragColor = texture(colorMap, fTexCoord.st);
}

hi there,
Change this line



glTexImage2D(GL_TEXTURE_2D, 0, test.bytes_per_pixel, test.width, test.height, 0, GL_RGB, GL_UNSIGNED_BYTE, test.pixel_data);    

to this



glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, test.width, test.height, 0, GL_RGB, GL_UNSIGNED_BYTE, test.pixel_data);    

And secondly make the texture coordinate varying smooth for both the vertex and the fragment shader i.e.


//vertex shader
smooth out vec2 fTexCoord;

//fragment shader
smooth in vec2 fTexCoord;

See if this helps.

I actually discovered the little GL_RGB fix just before I checked this thread for your post, but I thank you anyways for your help. The fix makes my program work perfectly now.

I now have another problem: I’m trying to render simple texture-based, fixed-width text and a Tetris block playfield, but for some reason the code for playfield rendering doesn’t work now, but it does display incorrect results if I use the text’s vertex array instead of the block’s vertex array. Here is the code to the main source file, with the playfield rendering using the text’s vertex array to render:

#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
using namespace std;
#include "vgl.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "BufferOffset.h"
#include "LoadShaders.h"
#include "blocks.c"
#include "chars.c"
#include "original.h"

#define PROGRAM_NAME "OpenGL Tetris"

SDL_Window *	mainWindow; /* Our window handle */
SDL_GLContext	mainContext; /* Our OpenGL context handle */

enum VAO_IDs { BlockVertices, FontVertices, NumVAOs };
enum Buffer_IDs { BlockBuffer, FontBuffer, NumBuffers };

const GLsizei NumBlockTextures = 7;

GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
GLuint BlockTextures[NumBlockTextures];
GLuint FontTexture;

const GLsizei NumBlockVertices = 36;
const GLsizei NumFontVertices = 6;

struct vertex {
	GLfloat pos[3];
	GLfloat texCoord[2];
};

GLint vPositionLoc, vTexCoordLoc;
GLint colorMapLoc;
GLint vTransformLoc;
GLuint program;

bool running = true;
original * op = original_create(1);
unsigned input = INP_NONE;

glm::mat4 Projection = glm::perspective(45.0f, 1.0f, 0.1f, 100.0f);
glm::mat4 View = glm::lookAt(glm::vec3(0.0f, 0.0f, 40.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 Model = glm::mat4(1.0f);

void DrawText(const char * msg, float x, float y, float z) {
	glBindVertexArray(VAOs[FontVertices]);
	glBindTexture(GL_TEXTURE_2D, FontTexture);

	Model = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z));
	glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));

	vertex fontVertices[NumFontVertices] = {
		{ { 1.0f, 1.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },
		{ { 1.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },

		{ { 1.0f, 1.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },
	};
	int c;
	for (size_t i = 0; msg[i] != '\0'; i++) {
		c = msg[i] - 0x20;
		fontVertices[0].texCoord[0] = ((c + 1) * 8.0f) / 512.0f;
		fontVertices[1].texCoord[0] = (c * 8.0f) / 512.0f;
		fontVertices[2].texCoord[0] = ((c + 1) * 8.0f) / 512.0f;

		fontVertices[3].texCoord[0] = ((c + 1) * 8.0f) / 512.0f;
		fontVertices[4].texCoord[0] = (c * 8.0f) / 512.0f;
		fontVertices[5].texCoord[0] = (c * 8.0f) / 512.0f;

		glBindBuffer(GL_ARRAY_BUFFER, Buffers[FontBuffer]);
		glBufferData(GL_ARRAY_BUFFER, sizeof(fontVertices), fontVertices, GL_STATIC_DRAW);
		glDrawArrays(GL_TRIANGLES, 0, NumFontVertices);

		Model = glm::translate(Model, glm::vec3(1.0f, 0.0f, 0.0f));
		glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));
	}
}

/* SDL timer executed every 1/60th of a second (60 frames per second). */
Uint32 timer(Uint32 interval, void * param) {
	original_do_frame(op, input);
	return interval;
}
 
//-----------------------------------------------------------------------------
//
// display
//

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glUseProgram(program);
	glActiveTexture(GL_TEXTURE0);

	DrawText("HELLO", -11.0f, 0.0f, 0.0f);

	// Use the font's vertices for block rendering. Block rendering doesn't
	// work at all when using the block's vertices for some reason.
	glBindVertexArray(VAOs[FontVertices]);

	// Render next piece
	if (op->next != 7) {
		glBindTexture(GL_TEXTURE_2D, BlockTextures[op->next]);
		for (int i = 0; i < 4; i++) {
			Model = glm::translate(glm::mat4(1.0f), glm::vec3(-2.0f, 14.0f, 0.0f));
			Model = glm::translate(Model, glm::vec3(pcs[op->next][0][0][i], -pcs[op->next][0][1][i], 0.0f));
			glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));
			glDrawArrays(GL_TRIANGLES, 0, NumBlockVertices);
		}
	}

	// Render active piece
	if (op->game_status == GAME_GOING || op->game_status == GAME_OVER) {
		if (op->pctype != 7) {
			glBindTexture(GL_TEXTURE_2D, BlockTextures[op->pctype]);
			for (int i = 0; i < 4; i++) {
				Model = glm::translate(glm::mat4(1.0f), glm::vec3(-5.0f, 10.f, 0.0f));
				Model = glm::translate(Model, glm::vec3(op->pcx + pcs[op->pctype][op->pcdir][0][i], -(op->pcy + pcs[op->pctype][op->pcdir][1][i]), 0.0f));
				glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));
				glDrawArrays(GL_TRIANGLES, 0, NumBlockVertices);
			}
		}
	}

	// Render playfield
	Model = glm::translate(glm::mat4(1.0f), glm::vec3(-5.0f, 10.f, 0.0f));
	for (int y = 0; y < 20; y++) {
		for (int x = 0; x < 10; x++) {
			glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));
			if (op->field[y][x] != 7) {
				glBindTexture(GL_TEXTURE_2D, BlockTextures[op->field[y][x]]);
				glDrawArrays(GL_TRIANGLES, 0, NumBlockVertices);
			}
			Model = glm::translate(Model, glm::vec3(1.0f, 0.0f, 0.0f));
		}
		Model = glm::translate(Model, glm::vec3(-10.0f, -1.0f, 0.0f));
	}
}

//-----------------------------------------------------------------------------
//
// init
//

void init(void) {
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glGenVertexArrays(NumVAOs, VAOs);
	glGenBuffers(NumBuffers, Buffers);

	glBindVertexArray(VAOs[BlockVertices]);
	vertex blockVertices[NumBlockVertices] = {
		// Front face
		{ { 1.0f, 1.0f, 1.0f, }, { 1.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 1.0f, }, { 0.0f, 0.0f, }, },
		{ { 1.0f, 0.0f, 1.0f, }, { 1.0f, 0.0f, }, },

		{ { 1.0f, 1.0f, 1.0f, }, { 1.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 1.0f, }, { 0.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 1.0f, }, { 0.0f, 0.0f, }, },

		// Left face
		{ { 0.0f, 1.0f, 1.0f, }, { 1.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },
		{ { 0.0f, 0.0f, 1.0f, }, { 1.0f, 0.0f, }, },

		{ { 0.0f, 1.0f, 1.0f, }, { 1.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },

		// Right face
		{ { 1.0f, 1.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 1.0f, }, { 0.0f, 0.0f, }, },
		{ { 1.0f, 0.0f, 0.0f, }, { 1.0f, 0.0f, }, },

		{ { 1.0f, 1.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 1.0f, 1.0f, 1.0f, }, { 0.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 1.0f, }, { 0.0f, 0.0f, }, },

		// Bottom face
		{ { 0.0f, 0.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 1.0f, }, { 0.0f, 0.0f, }, },
		{ { 0.0f, 0.0f, 1.0f, }, { 1.0f, 0.0f, }, },

		{ { 0.0f, 0.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 1.0f, }, { 0.0f, 0.0f, }, },

		// Top face
		{ { 1.0f, 1.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 1.0f, }, { 0.0f, 0.0f, }, },
		{ { 1.0f, 1.0f, 1.0f, }, { 1.0f, 0.0f, }, },

		{ { 1.0f, 1.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 1.0f, }, { 0.0f, 0.0f, }, },

		// Back face
		{ { 0.0f, 1.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { 1.0f, 0.0f, }, },

		{ { 0.0f, 1.0f, 0.0f, }, { 1.0f, 1.0f, }, },
		{ { 1.0f, 1.0f, 0.0f, }, { 0.0f, 1.0f, }, },
		{ { 1.0f, 0.0f, 0.0f, }, { 0.0f, 0.0f, }, },
	};
	glBindBuffer(GL_ARRAY_BUFFER, Buffers[BlockBuffer]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(blockVertices), blockVertices, GL_STATIC_DRAW);

	int c = '0' - 0x20;
	glBindVertexArray(VAOs[FontVertices]);
	vertex fontVertices[NumFontVertices] = {
		{ { 1.0f, 1.0f, 0.0f, }, { ((c+1)*8.0f)/512.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { (c*8.0f)/512.0f, 0.0f, }, },
		{ { 1.0f, 0.0f, 0.0f, }, { ((c+1)*8.0f)/512.0f, 0.0f, }, },

		{ { 1.0f, 1.0f, 0.0f, }, { ((c+1)*8.0f)/512.0f, 1.0f, }, },
		{ { 0.0f, 1.0f, 0.0f, }, { (c*8.0f)/512.0f, 1.0f, }, },
		{ { 0.0f, 0.0f, 0.0f, }, { (c*8.0f)/512.0f, 0.0f, }, },
	};
	glBindBuffer(GL_ARRAY_BUFFER, Buffers[FontBuffer]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(fontVertices), fontVertices, GL_STATIC_DRAW);

	ShaderInfo shaders = {
		GL_VERTEX_SHADER, "triangles.vert",
		GL_FRAGMENT_SHADER, "triangles.frag",
	};

	program = LoadShaders(shaders);
	glUseProgram(program);

	vPositionLoc = glGetAttribLocation(program, "vPosition");
	glVertexAttribPointer(vPositionLoc, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), BUFFER_OFFSET(offsetof(vertex, pos)));
	glEnableVertexAttribArray(vPositionLoc);

	vTexCoordLoc = glGetAttribLocation(program, "vTexCoord");
	glVertexAttribPointer(vTexCoordLoc, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), BUFFER_OFFSET(offsetof(vertex, texCoord)));
	glEnableVertexAttribArray(vTexCoordLoc);

	glGenTextures(NumBlockTextures, BlockTextures);
	for (GLsizei i = 0; i < NumBlockTextures; i++) {
		glBindTexture(GL_TEXTURE_2D, BlockTextures[i]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, blocks[i].width, blocks[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, blocks[i].pixel_data);
	}
	colorMapLoc = glGetUniformLocation(program, "colorMap");
	glUniform1i(colorMapLoc, 0);

	glGenTextures(1, &FontTexture);
	glBindTexture(GL_TEXTURE_2D, FontTexture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, chars.width, chars.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, chars.pixel_data);

	vTransformLoc = glGetUniformLocation(program, "vTransform");
	glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));

	/* Clear our buffer with a black background */
	glClearColor ( 0.0, 0.0, 0.0, 1.0 );

	SDL_AddTimer(16, timer, NULL);
}

//-----------------------------------------------------------------------------
//
// process events
//

void process_events(void) {
	SDL_Event event;
	while (SDL_PollEvent(&event)) {
		switch (event.type) {
			case SDL_QUIT:
				running = false;
				break;
			case SDL_WINDOWEVENT:
				switch (event.window.event) {
					case SDL_WINDOWEVENT_SHOWN:
					case SDL_WINDOWEVENT_EXPOSED:
					case SDL_WINDOWEVENT_MAXIMIZED:
					case SDL_WINDOWEVENT_RESTORED:
						display();
						break;
					case SDL_WINDOWEVENT_RESIZED:
						glViewport(0, 0, event.window.data1, event.window.data2);
						Projection = glm::perspective(45.0f, ((float)event.window.data1) / event.window.data2, 0.1f, 100.0f);
						glUniformMatrix4fv(vTransformLoc, 1, GL_FALSE, glm::value_ptr(Projection * View * Model));
						break;
					case SDL_WINDOWEVENT_CLOSE:
						running = false;
						break;
					default:
						break;
				}
				break;
			case SDL_KEYDOWN:
				switch (event.key.keysym.sym) {
					case SDLK_o:
						input |= INP_UP;
						break;
					case SDLK_q:
						input |= INP_DOWN;
						break;
					case SDLK_SEMICOLON:
						input |= INP_LEFT;
						break;
					case SDLK_j:
						input |= INP_RIGHT;
						break;
					case SDLK_m:
						input |= INP_1;
						break;
					case SDLK_w:
						input |= INP_2;
						break;
					case SDLK_v:
						input |= INP_3;
						break;
					case SDLK_SPACE:
						input |= INP_4;
						break;
					default:
						break;
				}
				break;
			case SDL_KEYUP:
				switch (event.key.keysym.sym) {
					case SDLK_o:
						input &= !INP_UP;
						break;
					case SDLK_q:
						input &= !INP_DOWN;
						break;
					case SDLK_SEMICOLON:
						input &= !INP_LEFT;
						break;
					case SDLK_j:
						input &= !INP_RIGHT;
						break;
					case SDLK_m:
						input &= !INP_1;
						break;
					case SDLK_w:
						input &= !INP_2;
						break;
					case SDLK_v:
						input &= !INP_3;
						break;
					case SDLK_SPACE:
						input &= !INP_4;
						break;
					default:
						break;
				}
				break;
			default:
				break;
		}
	}
}

/* A simple function that prints a message, the error code returned by SDL,
 * and quits the application */
void sdldie(const char *msg) {
	printf("%s: %s
", msg, SDL_GetError());
	SDL_Quit();
	exit(1);
}
 
 
void checkSDLError(int line = -1) {
#ifndef NDEBUG
        const char *error = SDL_GetError();
        if (*error != '\0') {
                printf("SDL Error: %s
", error);
                if (line != -1)
                        printf(" + line: %i
", line);
                SDL_ClearError();
        }
#endif
}
 
/* Our program's entry point. */
int main(int argc, char *argv[])
{
	if (SDL_Init(SDL_INIT_VIDEO) < 0) /* Initialize SDL's Video subsystem */
	sdldie("Unable to initialize SDL"); /* Or die on error */

	/* Request opengl 3.3 context.
	* SDL doesn't have the ability to choose which profile at this time of writing,
	* but it should default to the core profile */
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

	/* Turn on double buffering with a 24bit Z buffer.
	* You may need to change this to 16 or 32 for your system */
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

	/* Create our window centered at 512x512 resolution */
	mainWindow = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
			512, 512, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
	if (!mainWindow) /* Die if creation failed */
		sdldie("Unable to create window");
 
	checkSDLError(__LINE__);

	/* Create our opengl context and attach it to our window */
	mainContext = SDL_GL_CreateContext(mainWindow);
	checkSDLError(__LINE__);

	/* This makes our buffer swap syncronized with the monitor's vertical refresh */
	SDL_GL_SetSwapInterval(1);

	init();
	while (running) {
		process_events();
		display();
		SDL_GL_SwapWindow(mainWindow);
	}

	/* Delete our opengl context, destroy our window, and shutdown SDL */
	glDeleteProgram(program);
	glDeleteTextures(NumBlockTextures, BlockTextures);
	glDeleteTextures(1, &FontTexture);
	glDeleteVertexArrays(NumVAOs, VAOs);
	glDeleteBuffers(NumBuffers, Buffers);
	SDL_GL_DeleteContext(mainContext);
	SDL_DestroyWindow(mainWindow);
	SDL_Quit();

	return EXIT_SUCCESS;
}