4 changed files with 322 additions and 159 deletions
@ -0,0 +1,296 @@
|
||||
|
||||
#ifndef RENDERER_H |
||||
#define RENDERER_H |
||||
|
||||
#include <cstdlib> // malloc/calloc |
||||
// TODO: remove for better logging
|
||||
#include <iostream> // std::cout |
||||
|
||||
#include <GL/glew.h> |
||||
#include <SDL2/SDL.h> |
||||
#include <glm/glm.hpp> |
||||
#include <glm/gtc/matrix_transform.hpp> |
||||
|
||||
#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<hex_info> *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<hex_info>* hexes); |
||||
void parseHexToGLBuffer(GLfloat* buf, int idx, Point origin, vector<Point> &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<hex_info>* 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<Point> 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<Point> &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<hex_info> *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
|
||||
|
||||
Loading…
Reference in new issue