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;
}