You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
687 lines
18 KiB
687 lines
18 KiB
|
|
#ifndef RENDERER_H |
|
#define RENDERER_H |
|
|
|
#include <vector> |
|
#include <cmath> // trig functions |
|
#include <cstdlib> // calloc |
|
|
|
// TODO: decide on extension library |
|
//#include <GL/glew.h> |
|
#include <GL/gl3w.h> |
|
|
|
#include <SDL.h> |
|
#include <glm/glm.hpp> |
|
#include <glm/geometric.hpp> |
|
#include <glm/gtc/matrix_transform.hpp> |
|
|
|
#include "hexlib.h" |
|
#include "hexgame.h" |
|
|
|
#if 0 |
|
#define PROJ_TYPE ORTHOGRAPHIC |
|
#else |
|
#define PROJ_TYPE PERSPECTIVE |
|
#endif |
|
|
|
const char * VERTEX_SHADER_CODE = |
|
"#version 330 core\n" |
|
"in vec3 vertexPosition_modelspace;\n" |
|
"in vec3 vertexColor;\n" |
|
"out vec3 fragmentColor;\n" |
|
"uniform mat4 MVP;\n" |
|
"void main()\n" |
|
"{\n" |
|
" gl_Position = MVP * vec4(vertexPosition_modelspace, 1);\n" |
|
" fragmentColor = vertexColor;\n" |
|
"}"; |
|
|
|
const char * FRAGMENT_SHADER_CODE = |
|
"#version 330 core\n" |
|
"in vec3 fragmentColor;\n" |
|
"out vec3 color;\n" |
|
"void main()\n" |
|
"{\n" |
|
" color = fragmentColor;\n" |
|
"}"; |
|
|
|
const char * LINE_FRAGMENT_SHADER_CODE = |
|
"#version 330 core\n" |
|
"out vec3 color;\n" |
|
"void main()\n" |
|
"{\n" |
|
" color = vec3(0,0,0);\n" |
|
"}"; |
|
|
|
const char * DEBUG_FRAGMENT_SHADER_CODE = |
|
"#version 330 core\n" |
|
"out vec3 color;\n" |
|
"void main()\n" |
|
"{\n" |
|
" color = vec3(1,0,0);\n" |
|
"}"; |
|
|
|
typedef struct clear_col |
|
{ |
|
real32 R; |
|
real32 G; |
|
real32 B; |
|
real32 A; |
|
} clear_col; |
|
clear_col g_clear_col { 75.f / 255.f, 135.f / 255.f, 135.f / 255.f, 1.f }; |
|
|
|
typedef struct gl_matrix_info |
|
{ |
|
glm::mat4 projection; |
|
glm::mat4 view; |
|
glm::mat4 model; |
|
glm::mat4 MVP; |
|
} gl_matrix_info; |
|
|
|
enum projection_type |
|
{ |
|
PERSPECTIVE, |
|
ORTHOGRAPHIC, |
|
}; |
|
|
|
struct camera |
|
{ |
|
glm::vec3 position; |
|
float hAngle; |
|
float vAngle; |
|
glm::vec3 target; |
|
glm::vec3 forward; |
|
glm::vec3 up; |
|
glm::vec3 left; |
|
}; |
|
|
|
typedef struct gl_buffer |
|
{ |
|
GLuint buffer_id; |
|
size_t buffer_len; // NOTE: number of elements in buffer |
|
GLfloat* buffer; |
|
} gl_buffer; |
|
|
|
typedef struct gl_render_group |
|
{ |
|
// NOTE: For now the renderFrame function will assume these are the same size |
|
gl_buffer vertex_buffer; |
|
gl_buffer color_buffer; |
|
GLuint program_id; |
|
GLuint matrix_id; // NOTE: reference to a vertex shader's MVP matrix uniform |
|
GLuint vertex_array_id; |
|
} gl_render_group; |
|
|
|
gl_matrix_info g_scene_matrices; |
|
gl_render_group g_filled_hex_render_group; |
|
gl_render_group g_hex_line_render_group; |
|
gl_render_group g_debug_render_group; |
|
camera g_camera; |
|
|
|
// Interface |
|
|
|
bool initRenderer(SDL_Handles &handles, v2i vpDims); |
|
bool addTexture(SDL_Handles &handles, std::string path); |
|
bool createScene(std::vector<hex_info>* hexes); |
|
void moveCamera(bool up, bool left, bool down, bool right); |
|
void rotateCamera(int32 xrel, int32 yrel); |
|
void renderFrame(const std::vector<hex_info> *hexes); |
|
void renderDebug(std::vector<Point> &vertices); |
|
void freeBuffers(); |
|
|
|
// forward declarations |
|
|
|
bool initShaderProgram(gl_render_group &rg, const char * vertex_code, const char * frag_code, const char * mat_id); |
|
// 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 fillTriangleBufferFromHex(GLfloat buf[], int idx, const hex_info &hex); |
|
void fillColorBuffer(GLfloat buf[], int len, std::vector<hex_info> *hexes); |
|
void convertColor(GLfloat buf[], uint32 color); |
|
void fillHexLineBuffer(GLfloat buf[], int len, std::vector<hex_info>* hexes); |
|
bool checkGLBufferSize(GLenum buf_type, int expected_size, int line_num); |
|
bool initGLBufferObject(gl_buffer* buf_obj, int len, GLenum usage, GLfloat data[]); |
|
void drawRenderGroup(gl_render_group* rg, GLenum draw_mode, bool update_vertex_data); |
|
void initMatrices(projection_type p); |
|
|
|
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); |
|
|
|
// TODO: decide on extension library |
|
gl3wInit(); |
|
#if 0 |
|
// 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) { |
|
LOG(ERROR) << "Failed to initilize GLEW" << glewGetErrorString(err) << "\n"; |
|
return false; |
|
} |
|
#endif |
|
|
|
LOG(INFO) << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; |
|
LOG(INFO)<< "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; |
|
LOG(INFO) << "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 1 |
|
glEnable(GL_BLEND); |
|
glBlendEquation(GL_FUNC_ADD); |
|
glBlendFunc(GL_ONE, GL_SRC_ALPHA); |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
#endif |
|
|
|
// TODO: glDebugMessageCallback is only availabe from >v4.3 |
|
// check and warn if context doesn't support this function here |
|
glEnable (GL_DEBUG_OUTPUT); |
|
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); |
|
// hide VRAM debug messages |
|
glDebugMessageControl(GL_DONT_CARE, 33361, GL_DONT_CARE, 0, 0, GL_FALSE); |
|
|
|
// TODO: check for errors when compiling shaders |
|
initShaderProgram(g_filled_hex_render_group, VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE, "MVP"); |
|
initShaderProgram(g_hex_line_render_group, VERTEX_SHADER_CODE, LINE_FRAGMENT_SHADER_CODE, "MVP"); |
|
initShaderProgram(g_debug_render_group, VERTEX_SHADER_CODE, DEBUG_FRAGMENT_SHADER_CODE, "MVP"); |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
addTexture(SDL_Handles &handles, std::string path) |
|
{ |
|
// testing |
|
LOG(INFO) << "Loading image: " << path << "\n"; |
|
SDL_Surface* image = IMG_Load(path.c_str()); |
|
|
|
if (!image) |
|
{ |
|
LOG(ERROR) << "IMG_Load: " << IMG_GetError() << "\n"; |
|
return 1; |
|
} |
|
|
|
GLuint tex_id; |
|
glGenTextures(1, &tex_id); |
|
glBindTexture(GL_TEXTURE_2D, tex_id); |
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
|
|
|
// store opengl id in SDL_Surface.userdat |
|
image->userdata = (void*)(intptr_t) tex_id; |
|
handles.texSurfaces.push_back(image); |
|
|
|
return true; |
|
} |
|
|
|
// NOTE: mat_id should match the matrix variable name in the shader source |
|
bool |
|
initShaderProgram(gl_render_group &rg, const char * vertex_code, const char * frag_code, const char * mat_id) |
|
{ |
|
glGenVertexArrays(1, &rg.vertex_array_id); |
|
glBindVertexArray(rg.vertex_array_id); |
|
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); |
|
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER); |
|
|
|
glShaderSource(vertex_shader_id, 1, &vertex_code, NULL); |
|
glShaderSource(fragment_shader_id, 1, &frag_code, NULL); |
|
glCompileShader(vertex_shader_id); |
|
glCompileShader(fragment_shader_id); |
|
|
|
rg.program_id = glCreateProgram(); |
|
glAttachShader(rg.program_id, vertex_shader_id); |
|
glAttachShader(rg.program_id, fragment_shader_id); |
|
glLinkProgram(rg.program_id); |
|
|
|
rg.matrix_id = glGetUniformLocation(rg.program_id, mat_id); |
|
|
|
glDetachShader(rg.program_id, vertex_shader_id); |
|
glDetachShader(rg.program_id, fragment_shader_id); |
|
glDeleteShader(vertex_shader_id); |
|
glDeleteShader(fragment_shader_id); |
|
|
|
// TODO: check for errors when compiling shaders |
|
return true; |
|
} |
|
|
|
void |
|
initMatrices(projection_type p) |
|
{ |
|
if (p == PERSPECTIVE) |
|
{ |
|
g_scene_matrices.projection = glm::perspective( |
|
glm::radians(45.f), // FoV |
|
16.f / 9.f, // ascpect ratio |
|
0.1f, // near clip plane |
|
1000.0f // far clip plane |
|
); |
|
|
|
g_camera.position = glm::vec3(640,0,100); |
|
g_camera.target = glm::vec3(640,500,0); |
|
// inital rotation should match target direction |
|
glm::vec3 &p = g_camera.position; |
|
glm::vec3 &t = g_camera.target; |
|
g_camera.hAngle = 0; |
|
g_camera.vAngle = glm::atan((t.z - p.z) / (t.y - p.y)); |
|
|
|
////// |
|
camera &c = g_camera; |
|
float &h = c.hAngle; |
|
float &v = c.vAngle; |
|
c.forward = glm::vec3( |
|
glm::cos(v) * glm::sin(h), |
|
glm::cos(v) * glm::cos(h), |
|
glm::sin(v) |
|
); |
|
////// |
|
|
|
g_camera.up = glm::vec3(0,1,0); |
|
g_camera.left = glm::normalize(glm::cross(g_camera.up, g_camera.forward)); |
|
g_camera.up = glm::cross(g_camera.forward, g_camera.left); |
|
|
|
g_scene_matrices.view = glm::lookAt( |
|
g_camera.position, // camera position |
|
g_camera.position + g_camera.forward, |
|
g_camera.up // "up" vector |
|
); |
|
} |
|
else // ORTHO |
|
{ |
|
// left, right, bottom, top, zNear, zFar |
|
g_scene_matrices.projection = glm::ortho(0.f, 1280.0f, 0.f, 720.0f, 0.1f, 100.0f); |
|
g_scene_matrices.view = glm::lookAt( |
|
glm::vec3(0.0f, 0.0f, 1.0f), // camera position |
|
glm::vec3(0.0f, 0.0f, 0.0f), // look at position |
|
glm::vec3(0,1,0) // "up" vector |
|
); |
|
} |
|
|
|
g_scene_matrices.model = glm::mat4(1.0f); |
|
g_scene_matrices.MVP = g_scene_matrices.projection * g_scene_matrices.view * g_scene_matrices.model; |
|
} |
|
|
|
bool |
|
createScene(std::vector<hex_info>* hexes) |
|
{ |
|
initMatrices(PROJ_TYPE); |
|
|
|
// Vertex Data |
|
|
|
// TODO: index duplicate vertices |
|
// http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/ |
|
int hex_count = (int) hexes->size(); |
|
// 6 triangles * 3 vertices per triangle * 3 floats per vertex = 54 |
|
int vbuf_len = hex_count * 6 * 3 * 3; |
|
|
|
// TODO: surely there's a way to use indexed drawing for line vertices eg) line_loop |
|
// and still use one buffer for multiple shapes/paths gldrawarraysintanced?, gldrawelements? |
|
// https://gamedev.stackexchange.com/questions/104310/opengl-4-5-primitive-restart-vs-base-index |
|
int line_vertices_per_hex = 6 * 2; // 12 vertices since we're using line segments atm |
|
//gl_buffer *line_vertex_buffer = &g_hex_line_render_group.vertex_buffer; |
|
int line_buf_len = hexes->size() * line_vertices_per_hex * 3; // 3 floats per vertex |
|
|
|
// temporary buffers |
|
GLfloat* vbuf = (GLfloat*) std::calloc(vbuf_len, sizeof(GLfloat)); |
|
GLfloat* cbuf = (GLfloat*) std::calloc(vbuf_len, sizeof(GLfloat)); |
|
GLfloat* line_buf = (GLfloat*) std::calloc(vbuf_len, sizeof(GLfloat)); |
|
|
|
for (int i = 0; i < hex_count; i++) |
|
fillTriangleBufferFromHex(vbuf, 54 * i, (*hexes)[i]); |
|
|
|
|
|
if (!initGLBufferObject(&g_filled_hex_render_group.vertex_buffer, vbuf_len, GL_DYNAMIC_DRAW, vbuf)) |
|
return false; |
|
|
|
// color data for hex vertices |
|
|
|
fillColorBuffer(cbuf, vbuf_len, hexes); |
|
|
|
if (!initGLBufferObject(&g_filled_hex_render_group.color_buffer, vbuf_len, GL_DYNAMIC_DRAW, cbuf)) |
|
return false; |
|
|
|
// hex line vertex data |
|
|
|
fillHexLineBuffer(line_buf, line_buf_len, hexes); |
|
if (!initGLBufferObject(&g_hex_line_render_group.vertex_buffer, line_buf_len, GL_STATIC_DRAW, line_buf)) |
|
return false; |
|
|
|
// free temporary buffers |
|
std::free(vbuf); |
|
std::free(cbuf); |
|
std::free(line_buf); |
|
|
|
// debug draw vertexes |
|
|
|
// 4 vertices, 3 floats per vertex |
|
int len = 4 * 3; |
|
if (!initGLBufferObject(&g_debug_render_group.vertex_buffer, len, GL_DYNAMIC_DRAW, 0)) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
void |
|
moveCamera(bool up, bool left, bool down, bool right) |
|
{ |
|
if (!up && !left && !down && !right) |
|
return; |
|
|
|
glm::mat4 &m = g_scene_matrices.view; |
|
float d = 5.f; // units per frame |
|
|
|
glm::vec3 f = g_camera.forward; |
|
glm::vec3 u = g_camera.up; |
|
glm::vec3 old = g_camera.position; |
|
glm::vec3 &p = g_camera.position; |
|
if (up) p += (f * d); |
|
if (down) p -= (f * d); |
|
if (left) |
|
{ |
|
glm::vec3 l = glm::cross(f, u); |
|
p -= l * d; |
|
} |
|
if (right) |
|
{ |
|
glm::vec3 r = glm::cross(u, f); |
|
p -= r * d; |
|
} |
|
|
|
glm::vec3 diff = old - p; |
|
m = glm::translate(m, diff); |
|
g_scene_matrices.MVP = g_scene_matrices.projection * m * g_scene_matrices.model; |
|
} |
|
|
|
void |
|
rotateCamera(int32 xrel, int32 yrel) |
|
{ |
|
camera &c = g_camera; |
|
float &h = c.hAngle; |
|
float &v = c.vAngle; |
|
h += 0.005f * xrel; |
|
v -= 0.005f * yrel; |
|
c.forward = glm::vec3( |
|
glm::cos(v) * glm::sin(h), |
|
glm::cos(v) * glm::cos(h), |
|
glm::sin(v) |
|
); |
|
|
|
glm::mat4 &m = g_scene_matrices.view; |
|
m = glm::lookAt(c.position, c.position + c.forward, c.up); |
|
g_scene_matrices.MVP = g_scene_matrices.projection * m * g_scene_matrices.model; |
|
} |
|
|
|
void |
|
fillTriangleBufferFromHex(GLfloat buf[], int idx, const hex_info &hex) |
|
{ |
|
// triangles |
|
for (int i = 0; i < 6; i++) |
|
{ |
|
// vertex 0 |
|
buf[idx + 0] = (GLfloat) hex.XPos; |
|
buf[idx + 1] = (GLfloat) hex.YPos; |
|
buf[idx + 2] = (GLfloat) 0.f; |
|
|
|
// vertex 1 |
|
buf[idx + 3] = (GLfloat) hex.vertices[i].x; |
|
buf[idx + 4] = (GLfloat) hex.vertices[i].y; |
|
buf[idx + 5] = (GLfloat) 0.f; |
|
|
|
if (i == 5) // re-use the first point for the last triangle |
|
{ |
|
// vertex 2 |
|
buf[idx + 6] = (GLfloat) hex.vertices[0].x; |
|
buf[idx + 7] = (GLfloat) hex.vertices[0].y; |
|
buf[idx + 8] = (GLfloat) 0.f; |
|
} |
|
else |
|
{ |
|
// vertex 2 |
|
buf[idx + 6] = (GLfloat) hex.vertices[i + 1].x; |
|
buf[idx + 7] = (GLfloat) hex.vertices[i + 1].y; |
|
buf[idx + 8] = (GLfloat) 0.f; |
|
} |
|
|
|
// we've added 9 GLfloats per loop |
|
idx += 9; |
|
} |
|
} |
|
|
|
// NOTE: helper for fillColorBuffer() to convert uint32 color to GLfloat triplet |
|
void |
|
convertColor(GLfloat buf[3], uint32 color) |
|
{ |
|
// NOTE: not using the alpha values for now |
|
buf[0] = (GLfloat) ((color >> 24) & 0xFF) / (GLfloat) 255; |
|
buf[1] = (GLfloat) ((color >> 16) & 0xFF) / (GLfloat) 255; |
|
buf[2] = (GLfloat) ((color >> 8) & 0xFF) / (GLfloat) 255; |
|
} |
|
|
|
void |
|
fillColorBuffer(GLfloat buf[], int len, std::vector<hex_info>* hexes) |
|
{ |
|
int buf_idx; |
|
int buf_len_per_hex = 54; // NOTE: 3 * 3 * 6 |
|
GLfloat color_buf[3]; |
|
for (int i = 0; i < (int) hexes->size(); i++) |
|
{ |
|
buf_idx = i * buf_len_per_hex; |
|
hex_info hxi = (*hexes)[i]; |
|
convertColor(color_buf, hxi.current_color); |
|
|
|
for (int j = 0; j < buf_len_per_hex; j+=3) |
|
{ |
|
buf[buf_idx + j] = color_buf[0]; |
|
buf[buf_idx + j + 1] = color_buf[1]; |
|
buf[buf_idx + j + 2] = color_buf[2]; |
|
} |
|
} |
|
} |
|
|
|
void |
|
fillHexLineBuffer(GLfloat buf[], int len, std::vector<hex_info>* hexes) |
|
{ |
|
Point p1, p2; |
|
int idx = 0; |
|
for (int i = 0; i < (int) hexes->size(); i++) |
|
{ |
|
hex_info hxi = (*hexes)[i]; |
|
for (int j = 0; j < 6; j ++) |
|
{ |
|
if (j == 5) // wrap |
|
{ |
|
p1 = hxi.vertices[j]; |
|
p2 = hxi.vertices[0]; |
|
} |
|
else |
|
{ |
|
p1 = hxi.vertices[j]; |
|
p2 = hxi.vertices[j + 1]; |
|
} |
|
|
|
buf[idx + 0] = p1.x; |
|
buf[idx + 1] = p1.y; |
|
buf[idx + 2] = 0.f; |
|
buf[idx + 3] = p2.x; |
|
buf[idx + 4] = p2.y; |
|
buf[idx + 5] = 0.f; |
|
idx += 6; |
|
} |
|
} |
|
} |
|
|
|
// NOTE: this will only work after a call to glBindBuffer |
|
bool |
|
checkGLBufferSize(GLenum buf_type, int expected_size, int line_num) |
|
{ |
|
GLint gl_size; |
|
glGetBufferParameteriv(buf_type, GL_BUFFER_SIZE ,&gl_size); |
|
|
|
if (expected_size != gl_size) |
|
{ |
|
LOG(ERROR) << "line: " << line_num << "gl buffer size mismatch\n"; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
initGLBufferObject(gl_buffer* buf_obj, int len, GLenum usage, GLfloat data[]) |
|
{ |
|
buf_obj->buffer_len = (size_t) len; |
|
buf_obj->buffer = (GLfloat*) std::calloc(len, sizeof(GLfloat)); |
|
|
|
if (data) |
|
{ |
|
for (int i = 0; i < len; i++) |
|
buf_obj->buffer[i] = data[i]; |
|
} |
|
|
|
glGenBuffers(1, &buf_obj->buffer_id); |
|
glBindBuffer(GL_ARRAY_BUFFER, buf_obj->buffer_id); |
|
glBufferData(GL_ARRAY_BUFFER, len * sizeof(GLfloat), buf_obj->buffer, usage); |
|
|
|
if (!checkGLBufferSize(GL_ARRAY_BUFFER, len * sizeof(GLfloat), __LINE__)) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
void |
|
openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
|
GLsizei length, const GLchar* message, const void* userParam) |
|
{ |
|
LOG((type == GL_DEBUG_TYPE_ERROR) ? ERROR : DEBUG) |
|
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "") |
|
<< ", type: " << type |
|
<< ", severity: " << severity |
|
<< ", message: " << message << "\n"; |
|
} |
|
|
|
void |
|
drawRenderGroup(gl_render_group* rg, GLenum draw_mode, bool update_vertex_data = false) |
|
{ |
|
glUseProgram(rg->program_id); |
|
// Send our transformation to the currently bound shader, in the "g_MVP" uniform |
|
glUniformMatrix4fv(rg->matrix_id, 1, GL_FALSE, &g_scene_matrices.MVP[0][0]); |
|
|
|
// 1st attribute buffer : vertices |
|
glEnableVertexAttribArray(0); |
|
glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_buffer.buffer_id); |
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); |
|
|
|
if (update_vertex_data) |
|
{ |
|
glBufferSubData(GL_ARRAY_BUFFER, 0, rg->vertex_buffer.buffer_len * sizeof(GLfloat), |
|
rg->vertex_buffer.buffer); |
|
} |
|
|
|
// 2nd attribute buffer : colors |
|
if (rg->color_buffer.buffer) |
|
{ |
|
glEnableVertexAttribArray(1); |
|
glBindBuffer(GL_ARRAY_BUFFER, rg->color_buffer.buffer_id); |
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); |
|
|
|
// TODO: maybe add an option to not send color data every frame? |
|
glBufferSubData(GL_ARRAY_BUFFER, 0, rg->color_buffer.buffer_len * sizeof(GLfloat), |
|
rg->color_buffer.buffer); |
|
} |
|
// draw |
|
glDrawArrays(draw_mode, 0, rg->vertex_buffer.buffer_len / 3); |
|
|
|
// cleanup |
|
glDisableVertexAttribArray(0); |
|
if (rg->color_buffer.buffer) |
|
glDisableVertexAttribArray(1); |
|
} |
|
|
|
void |
|
renderFrame(std::vector<hex_info> *hexes) |
|
{ |
|
glClearColor(g_clear_col.R, g_clear_col.G, g_clear_col.B, g_clear_col.A); |
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
|
|
|
// filled hexes |
|
|
|
// get new colors every frame |
|
gl_render_group* rg = &g_filled_hex_render_group; |
|
fillColorBuffer(rg->color_buffer.buffer, rg->color_buffer.buffer_len, hexes); |
|
drawRenderGroup(rg, GL_TRIANGLES); |
|
|
|
// hex lines |
|
|
|
drawRenderGroup(&g_hex_line_render_group, GL_LINES); |
|
} |
|
|
|
void |
|
renderDebug(std::vector<Point> &vertices) |
|
{ |
|
// TODO: indexed line drawing |
|
real64 buf[4 * 3] = { |
|
vertices[0].x, vertices[0].y, 0, |
|
vertices[1].x, vertices[1].y, 0, |
|
vertices[2].x, vertices[2].y, 0, |
|
vertices[3].x, vertices[3].y, 0, |
|
}; |
|
|
|
// copy vertexes to render group |
|
gl_render_group* rg = &g_debug_render_group; |
|
for (int i = 0; i < 12; i++) |
|
rg->vertex_buffer.buffer[i] = buf[i]; |
|
|
|
drawRenderGroup(rg, GL_LINE_LOOP, true); |
|
} |
|
|
|
void |
|
freeBuffers() |
|
{ |
|
std::vector<gl_render_group> groups = { |
|
g_filled_hex_render_group, |
|
g_hex_line_render_group, |
|
g_debug_render_group |
|
}; |
|
|
|
for (gl_render_group group : groups) |
|
{ |
|
if (group.vertex_buffer.buffer) |
|
{ |
|
std::free(group.vertex_buffer.buffer); |
|
group.vertex_buffer.buffer = 0; |
|
} |
|
|
|
if (group.color_buffer.buffer) |
|
{ |
|
std::free(group.color_buffer.buffer); |
|
group.color_buffer.buffer = 0; |
|
} |
|
} |
|
|
|
} |
|
|
|
#endif // RENDERER_H |
|
|
|
|