diff --git a/src/Makefile b/src/Makefile index 734bfef..46e31a4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,8 @@ CXXFLAGS = -std=c++11 -g -Wall -INCLUDES = +INCLUDES = +# libGLEW needs to be >= 2.0.0 to use opengl core context +# don't know a good way to require this in make without forcing a specific version LDFLAGS = -lSDL2 -lGLEW -lGL all: @@ -10,4 +12,3 @@ all: clean: rm ../bin/hexgame - diff --git a/src/hexgame.cpp b/src/hexgame.cpp index 9e4b5bd..b0cfa7a 100644 --- a/src/hexgame.cpp +++ b/src/hexgame.cpp @@ -34,12 +34,13 @@ #include #endif -#include #include +#include #include #include "hexgame.h" #include "hexlib.h" +#include "renderer.h" //#include "gooey.h" using std::string; @@ -68,127 +69,8 @@ uint32 line_color = 0xFFFFFFFF; uint32 selected_fill_color = 0xF46000FF; uint32 fill_color = 0x5C5C5CFF; -struct { - real32 R = 75.0f / 255.0f; - real32 G = 135.0f / 255.0f; - real32 B = 135.0f / 255.0f; - real32 A = 1; -} gl_clear_color; - -void -renderHexagon(vector &vertices, uint32 fillColor, uint32 lineColor) -{ - glColor4ub( - uint8((fillColor & 0xFF000000) >> 24), - uint8((fillColor & 0x00FF0000) >> 16), - uint8((fillColor & 0x0000FF00) >> 8), - uint8((fillColor & 0x000000FF)) - ); - - glBegin(GL_POLYGON); - glVertex3d(vertices[0].x, vertices[0].y, 0); - glVertex3d(vertices[1].x, vertices[1].y, 0); - glVertex3d(vertices[2].x, vertices[2].y, 0); - glVertex3d(vertices[3].x, vertices[3].y, 0); - glVertex3d(vertices[4].x, vertices[4].y, 0); - glVertex3d(vertices[5].x, vertices[5].y, 0); - glEnd(); - - glColor4ub( - uint8((lineColor & 0xFF000000) >> 24), - uint8((lineColor & 0x00FF0000) >> 16), - uint8((lineColor & 0x0000FF00) >> 8), - uint8((lineColor & 0x000000FF)) - ); - glLineWidth(1.0f); - - glBegin(GL_LINE_LOOP); - glVertex3d(vertices[0].x, vertices[0].y, 0); - glVertex3d(vertices[1].x, vertices[1].y, 0); - glVertex3d(vertices[2].x, vertices[2].y, 0); - glVertex3d(vertices[3].x, vertices[3].y, 0); - glVertex3d(vertices[4].x, vertices[4].y, 0); - glVertex3d(vertices[5].x, vertices[5].y, 0); - glEnd(); -} - -void -setProjectionMatrix(int w, int h) -{ - w = (w == 0) ? 1 : w; // don't divide by zero - h = (h == 0) ? 1 : h; - glMatrixMode(GL_PROJECTION); - real32 a = 2.0f / (real32) w; - real32 b = 2.0f / (real32) h; - real32 matrix[16] = { - a, 0, 0, 0, - 0, b, 0, 0, - 0, 0, 1, 0, - -1, -1, 0, 1, - }; - glLoadMatrixf(matrix); - // TODO: Make helper function for glGetError(), also map codes to - // error strings - GLenum e = glGetError(); - if (e != GL_NO_ERROR) - { - stringstream ss; - ss << "setProjectionMatrix(), GLerror: " << e; - debugLog(ss.str()); - } -} - -uint32 -createRandomColor(uint8 alpha = 205) -{ - // http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution - // NOTE: gcc on mingw-w54 produces the same results every time without a - // non-deterministic source - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution uniI(0, 255); - - uint32 color = ( - (uniI(gen) << 24) // red - | (uniI(gen) << 16) // green - | (uniI(gen) << 8) // blue - | alpha // alpha - ); - return color; -} - -bool -InitGraphics(SDL_Handles &handles) -{ - // TODO: pretty much all of these calls need error handling :( - SDL_Init(SDL_INIT_VIDEO); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_Window *window = SDL_CreateWindow("hexgame", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - viewportDims.x, viewportDims.y, SDL_WINDOW_OPENGL); - if (window == NULL) { - stringstream e; - e << "Could not create window: " << SDL_GetError(); - debugLog(e.str()); - return false; - } - handles.window = window; - handles.glContext = SDL_GL_CreateContext(window); - SDL_GL_SetSwapInterval(1); // vsync - // enable blending - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // init opengl projection/model matrices - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - setProjectionMatrix(viewportDims.x, viewportDims.y); - - // TODO: Check for errors and return false - return true; -} - void -createScene() +createHexes(vector *hxi_array) { // create a hexagonal grid of hexagons int map_radius = 7; @@ -209,7 +91,7 @@ createScene() hxi.stored_color = hxi.current_color; hxi.vertices = polygon_corners(global_layout, hxi.hex); hxi.vertices.shrink_to_fit(); - hexInfoArray->push_back(hxi); + hxi_array->push_back(hxi); } } } @@ -221,7 +103,7 @@ hex_info *currentHex; vector selectedHexes; HexDrawMode globalDrawMode = NONE; - +/* // TODO: many globals used here void debugRender() @@ -264,34 +146,7 @@ debugRender() } } } - -void -render() -{ - glClearColor(gl_clear_color.R, gl_clear_color.G, gl_clear_color.B, - gl_clear_color.A); - glClear(GL_COLOR_BUFFER_BIT); - - // hexagons - for (hex_info hxi : *hexInfoArray) - { - renderHexagon(hxi.vertices, hxi.current_color, - (hxi.selected) ? selected_line_color : line_color); - } - - debugRender(); - //renderGooey(viewportDims); - - // TODO: Make helper function for glGetError(), also map codes to - // error strings - GLenum e = glGetError(); - if (e != GL_NO_ERROR) - { - stringstream ss; - ss << "render(), GLerror: " << e; - debugLog(ss.str()); - } -} +*/ v2i mapMouseToViewport(int32 x, int32 y) @@ -620,7 +475,7 @@ enterLoop(SDL_Handles &handles) } } - render(); + render(hexInfoArray); SDL_GL_SwapWindow(handles.window); } @@ -648,16 +503,27 @@ int main(int argc, char* argv[]) #endif { hexInfoArray = new vector; + createHexes(hexInfoArray); SDL_Handles handles; - if (InitGraphics(handles) == false) + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { stringstream ss; - ss << "Unable to initialize graphics, exiting"; + ss << "Error, SDL_Init: " << SDL_GetError(); debugLog(ss.str()); return 1; } - /* + if (!initRenderer(handles, viewportDims)) + { + stringstream ss; + ss << "Unable to initialize graphics, exiting"; + debugLog(ss.str()); + return 1; + } + + createScene(hexInfoArray); +#if 0 if (InitGooey(viewportDims) == false) { stringstream ss; @@ -665,9 +531,8 @@ int main(int argc, char* argv[]) debugLog(ss.str()); return 1; } - */ +#endif - createScene(); enterLoop(handles); cleanUp(handles); delete hexInfoArray; diff --git a/src/hexgame.h b/src/hexgame.h index d2006c0..2d3100a 100644 --- a/src/hexgame.h +++ b/src/hexgame.h @@ -43,6 +43,7 @@ struct SDL_Handles { SDL_Window *window; SDL_GLContext glContext; + SDL_DisplayMode currentDisplayMode; std::vector texSurfaces; }; diff --git a/src/renderer.h b/src/renderer.h new file mode 100644 index 0000000..f13ce6a --- /dev/null +++ b/src/renderer.h @@ -0,0 +1,296 @@ + +#ifndef RENDERER_H +#define RENDERER_H + +#include // malloc/calloc +// TODO: remove for better logging +#include // std::cout + +#include +#include +#include +#include + +#include "hexlib.h" +#include "hexgame.h" + + +// Interface + +bool initRenderer(SDL_Handles &handles, v2i vpDims); +void debugRender(HexDrawMode mode, hex_info *startHex, hex_info *currentHex); +void render(const vector *hexes); + + +// Implementation + +GLuint initShaders(); +// enable debug output https://www.khronos.org/opengl/wiki/OpenGL_Error +void openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, + GLsizei length, const GLchar* message, const void* userParam); +void createScene(vector* hexes); +void parseHexToGLBuffer(GLfloat* buf, int idx, Point origin, vector &verts); + +GLuint g_vertexbuffer; +GLuint g_vertex_buffer_triangle_count; +GLuint g_programID; +GLuint g_matrixID; +glm::mat4 g_projection_matrix; +glm::mat4 g_view_matrix; +glm::mat4 g_model_matrix; +glm::mat4 MVP; + +// TODO: could break this up into initGL(), initShaders(), initVertices(), initMatrices() ? +bool +initRenderer(SDL_Handles &handles, v2i vpDims) +{ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetSwapInterval(1); // vsync + SDL_GetCurrentDisplayMode(0, &handles.currentDisplayMode); + handles.window = + SDL_CreateWindow("hexgame", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + vpDims.x, vpDims.y, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); + + if (handles.window == NULL) + { + // TODO: log error SDL_GetError() + return false; + } + + handles.glContext = SDL_GL_CreateContext(handles.window); + + // Initialize GLEW + // glewExperimental is only needed in GLEW <= 1.13.0 + // we can require version 2.0.0+ + //glewExperimental = true; // Needed for core profile + GLenum err = glewInit(); + if (err != GLEW_OK) { + // TODO: better logging + std::cout << "Failed to initilize GLEW" << glewGetErrorString(err) << "\n"; + return false; + } + + // TODO: better logging + // see what kind of context we got + std::cout << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; + std::cout << "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; + std::cout << "opengl version: " << glGetString(GL_VERSION) << "\n"; + + // TODO: blending, these options break the http://www.opengl-tutorial.org tutorials atm + // Setup render state: alpha-blending enabled, polygon fill +#if 0 + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif + + glEnable (GL_DEBUG_OUTPUT); + glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); + g_programID = initShaders(); + g_matrixID = glGetUniformLocation(g_programID, "MVP"); // shader variable for mvp xform + + return true; +} + +// TODO: figure out how to keep track of shader state, and pass in struct here +GLuint +initShaders() +{ + GLuint VertexArrayID; + glGenVertexArrays(1, &VertexArrayID); + glBindVertexArray(VertexArrayID); + + const char * VertexShaderCode = + "#version 330 core\n" + "in vec3 vertexPosition_modelspace;\n" + "uniform mat4 MVP;\n" + "void main()\n" + "{\n" + " gl_Position = MVP * vec4(vertexPosition_modelspace, 1);\n" + "}"; + + const char * FragmentShaderCode = + "#version 330 core\n" + "out vec3 color;\n" + "void main()\n" + "{\n" + " color = vec3(0.5,0,0);\n" + "}"; + + GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); + GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(VertexShaderID, 1, &VertexShaderCode, NULL); + glShaderSource(FragmentShaderID, 1, &FragmentShaderCode, NULL); + glCompileShader(VertexShaderID); + glCompileShader(FragmentShaderID); + + GLuint programID = glCreateProgram(); + glAttachShader(programID, VertexShaderID); + glAttachShader(programID, FragmentShaderID); + glLinkProgram(programID); + + glDetachShader(programID, VertexShaderID); + glDetachShader(programID, FragmentShaderID); + glDeleteShader(VertexShaderID); + glDeleteShader(FragmentShaderID); + + return programID; +} + +void +createScene(vector* hexes) +{ + //////////////////// + // Model, View, Projection Matrices + + g_projection_matrix = glm::ortho( + 0.f, // left + 1280.0f, // right + 0.f, // bottom + 720.0f, // top + 0.1f, // zNear + 100.0f // zFar + ); + + g_view_matrix = glm::lookAt( + glm::vec3(0.0f, 0.0f, 2.0f), // camera position + glm::vec3(0.0f, 0.0f, 0.0f), // look at position + glm::vec3(0,1,0) // "up" vector + ); + + g_model_matrix = glm::mat4(1.0f); + MVP = g_projection_matrix * g_view_matrix * g_model_matrix; + + + //////////////////// + // Vertex Data + + // TODO: index duplicate vertices + // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/ + + // 6 triangles * 3 vertices per triange * 3 floats per vertex = 54 + int buf_index = 54; + size_t hex_count = hexes->size(); + size_t buf_size = 6 * 9 * hex_count; + GLfloat vbuf_data[buf_size] = { 0 }; + + for (int i = 0; i < (int) hex_count; i++) + { + hex_info hxi = (*hexes)[i]; + Point p(hxi.XPos, hxi.YPos); + vector verts = hxi.vertices; + parseHexToGLBuffer(vbuf_data, buf_index * i, p, verts); + } + + g_vertex_buffer_triangle_count = buf_size / 3; + + glGenBuffers(1, &g_vertexbuffer); + glBindBuffer(GL_ARRAY_BUFFER, g_vertexbuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vbuf_data), vbuf_data, GL_STATIC_DRAW); +} + +void +parseHexToGLBuffer(GLfloat buf[], int idx, Point origin, vector &verts) +{ + int buf_idx = idx; + // triangles + for (int i = 0; i < 6; i++) + { + if (i == 5) // wrap + { + // vertex 0 + buf[buf_idx + 0] = (GLfloat) origin.x; + buf[buf_idx + 1] = (GLfloat) origin.y; + buf[buf_idx + 2] = (GLfloat) 0.f; + + // vertex 1 + buf[buf_idx + 3] = (GLfloat) verts[i].x; + buf[buf_idx + 4] = (GLfloat) verts[i].y; + buf[buf_idx + 5] = (GLfloat) 0.f; + + // vertex 1 + buf[buf_idx + 6] = (GLfloat) verts[0].x; + buf[buf_idx + 7] = (GLfloat) verts[0].y; + buf[buf_idx + 8] = (GLfloat) 0.f; + } + else + { + // vertex 0 + buf[buf_idx + 0] = (GLfloat) origin.x; + buf[buf_idx + 1] = (GLfloat) origin.y; + buf[buf_idx + 2] = (GLfloat) 0.f; + + // vertex 1 + buf[buf_idx + 3] = (GLfloat) verts[i].x; + buf[buf_idx + 4] = (GLfloat) verts[i].y; + buf[buf_idx + 5] = (GLfloat) 0.f; + + // vertex 1 + buf[buf_idx + 6] = (GLfloat) verts[i + 1].x; + buf[buf_idx + 7] = (GLfloat) verts[i + 1].y; + buf[buf_idx + 8] = (GLfloat) 0.f; + } + + // we've added 9 GLfloats per loop + buf_idx += 9; + } +} + +struct +{ + real32 R = 75.0f / 255.0f; + real32 G = 135.0f / 255.0f; + real32 B = 135.0f / 255.0f; + real32 A = 1; +} gl_clear_color; + +void +openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, + GLsizei length, const GLchar* message, const void* userParam) +{ + // TODO: update error logging + std::cout << "openGLDebugCallback: " + << (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "") + << ", type: " << type + << ", severity: " << severity + << ", message: " << message << "\n"; +} + +void +render(const vector *hexes) +{ + glClearColor(gl_clear_color.R, gl_clear_color.G, gl_clear_color.B, + gl_clear_color.A); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glUseProgram(g_programID); + + // Send our transformation to the currently bound shader, + // in the "MVP" uniform + glUniformMatrix4fv(g_matrixID, 1, GL_FALSE, &MVP[0][0]); + + // 1rst attribute buffer : vertices + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, g_vertexbuffer); + glVertexAttribPointer( + 0, // attribute 0. No particular reason for 0, but must match the layout in the shader. + 3, // size + GL_FLOAT, // type + GL_FALSE, // normalized? + 0, // stride + (void*)0 // array buffer offset + ); + + glDrawArrays(GL_TRIANGLES, 0, g_vertex_buffer_triangle_count); + glDisableVertexAttribArray(0); +} + +#endif // RENDERER_H +