#include #include // calloc // TODO: decide on extension library //#include #include #include #include #include #include "aixlog.hpp" #include "render_group.h" #include "mesh.h" gl_render_group* rgCreateGroup() { gl_render_group* rg = (gl_render_group *) std::calloc(1, sizeof(gl_render_group)); return rg; } bool rgInitShaderProgram(gl_render_group* rg, const char * vertex_code, const char * frag_code, const char* model_name, const char* view_name, const char* projection_name) { 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->model_matrix_id = glGetUniformLocation(rg->program_id, model_name); rg->view_matrix_id = glGetUniformLocation(rg->program_id, view_name); rg->projection_matrix_id = glGetUniformLocation(rg->program_id, projection_name); rg->normal_matrix_id = glGetUniformLocation(rg->program_id, "normal_matrix"); glDetachShader(rg->program_id, vertex_shader_id); glDetachShader(rg->program_id, fragment_shader_id); glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); // check for errors GLint isLinked = 0; glGetProgramiv(rg->program_id, GL_LINK_STATUS, &isLinked); if (isLinked == GL_FALSE) { GLint maxLength = 0; glGetProgramiv(rg->program_id, GL_INFO_LOG_LENGTH, &maxLength); // The maxLength includes the NULL character GLchar infoLog[maxLength]; glGetProgramInfoLog(rg->program_id, maxLength, &maxLength, &infoLog[0]); LOG(ERROR) << infoLog << "\n"; // The program is useless now. So delete it. glDeleteProgram(rg->program_id); return false; } return true; } void rgInitGLBufferObject(gl_buffer* buf_obj, uint len, GLenum usage, GLenum target, GLfloat data[]) { // TODO: why not just assing the pointer to the render group instead of copying here? buf_obj->buffer_len = len; buf_obj->buffer = (GLfloat*) std::calloc(len, sizeof(GLfloat)); if (data) { for (uint i = 0; i < len; i++) buf_obj->buffer[i] = data[i]; } glGenBuffers(1, &buf_obj->buffer_id); glBindBuffer(target, buf_obj->buffer_id); glBufferData(target, len * sizeof(GLfloat), buf_obj->buffer, usage); } // 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 rgFillColorBuffer(GLfloat buf[], int len, std::vector* 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]; } } } bool rgInitEntity(gl_render_group* rg, Entity* e) { bool retVal = true; bool useNormals = rg->use_normals = (e->mesh->normals != nullptr); uint vertex_buf_len = e->mesh->num_vertices * 3; GLfloat* vertex_buf = (GLfloat*) std::calloc(vertex_buf_len, sizeof(GLfloat)); GLfloat* color_buf = (GLfloat*) std::calloc(vertex_buf_len, sizeof(GLfloat)); GLfloat* normal_buf; if (useNormals) normal_buf = (GLfloat*) std::calloc(vertex_buf_len, sizeof(GLfloat)); // dump vertices, colors, and normals into temporary buffers uint vertex_index = 0; uint vertex_prop_index = 0; for (uint i = 0; i < vertex_buf_len; i++) { const glm::vec3& vertex = e->mesh->vertices[vertex_index]; const glm::vec3& normal = e->mesh->normals[vertex_index]; switch (vertex_prop_index) { case 0: vertex_buf[i] = vertex.x; break; case 1: vertex_buf[i] = vertex.y; break; case 2: vertex_buf[i] = vertex.z; break; } if (useNormals) { switch (vertex_prop_index) { case 0: normal_buf[i] = normal.x; break; case 1: normal_buf[i] = normal.y; break; case 2: normal_buf[i] = normal.z; break; } } color_buf[i] = e->mesh->diffuse_color[vertex_prop_index]; vertex_prop_index++; if (vertex_prop_index == 3) { vertex_prop_index = 0; vertex_index++; } } // send vertex and color data to OpenGL rgInitGLBufferObject(&rg->vertex_buffer, vertex_buf_len, GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER, vertex_buf); rgInitGLBufferObject(&rg->color_buffer, vertex_buf_len, GL_STATIC_DRAW, GL_ARRAY_BUFFER, color_buf); if (useNormals) { rgInitGLBufferObject(&rg->vertex_normals, vertex_buf_len, GL_STATIC_DRAW, GL_ARRAY_BUFFER, normal_buf); } // TODO: combine this somehow with rgInitGLBufferObject() // dump indices into temp buffer gl_index_buffer index_buffer; index_buffer.buffer_len = e->mesh->num_indices; index_buffer.indices = (uint *) std::calloc(e->mesh->num_indices, sizeof(uint)); for (uint i = 0; i < e->mesh->num_indices; i++) index_buffer.indices[i] = e->mesh->indices[i]; rg->index_buffer = index_buffer; glGenBuffers(1, &rg->index_buffer.buffer_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rg->index_buffer.buffer_id); glBufferData(GL_ELEMENT_ARRAY_BUFFER, e->mesh->num_indices * sizeof(uint), rg->index_buffer.indices, GL_STATIC_DRAW); // free temp buffers std::free(vertex_buf); if (useNormals) std::free(normal_buf); std::free(color_buf); vertex_buf = normal_buf = color_buf = nullptr; return retVal; } void rgDraw(gl_render_group* rg, GLenum draw_mode, glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, glm::vec3 light_position, GLuint light_id, bool update_vertex_data) { glUseProgram(rg->program_id); glUniformMatrix4fv(rg->model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]); glUniformMatrix4fv(rg->view_matrix_id, 1, GL_FALSE, &view_matrix[0][0]); glUniformMatrix4fv(rg->projection_matrix_id, 1, GL_FALSE, &projection_matrix[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); } // 3rd attribute buffer: normals if (rg->use_normals) { glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_normals.buffer_id); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); // TODO: testing lighting glUniform3f(light_id, light_position.x, light_position.y, light_position.z); glm::mat3 normal_matrix = glm::transpose(glm::inverse(glm::mat3(model_matrix))); glUniformMatrix3fv(rg->normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]); } // draw glDrawArrays(draw_mode, 0, rg->vertex_buffer.buffer_len / 3); // cleanup glDisableVertexAttribArray(0); if (rg->color_buffer.buffer) glDisableVertexAttribArray(1); } void rgDrawIndexed(gl_render_group* rg, GLenum draw_mode, glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, glm::vec3 light_position, GLuint light_id) { glUseProgram(rg->program_id); glUniformMatrix4fv(rg->model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]); glUniformMatrix4fv(rg->view_matrix_id, 1, GL_FALSE, &view_matrix[0][0]); glUniformMatrix4fv(rg->projection_matrix_id, 1, GL_FALSE, &projection_matrix[0][0]); // TODO: testing lighting glUniform3f(light_id, light_position.x, light_position.y, light_position.z); glm::mat3 normal_matrix = glm::transpose(glm::inverse(glm::mat3(model_matrix))); glUniformMatrix3fv(rg->normal_matrix_id, 1, GL_FALSE, &normal_matrix[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); // 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); } // 3rd attribe buffer : normals if (rg->use_normals) { glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_normals.buffer_id); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); } // draw glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rg->index_buffer.buffer_id); glDrawElements(draw_mode, rg->index_buffer.buffer_len, GL_UNSIGNED_INT, 0); // cleanup glDisableVertexAttribArray(0); if (rg->color_buffer.buffer) glDisableVertexAttribArray(1); if (rg->use_normals) glDisableVertexAttribArray(2); } void rgFree(gl_render_group* rg) { if (rg == nullptr) return; utilSafeFree(rg->vertex_buffer.buffer); utilSafeFree(rg->vertex_normals.buffer); utilSafeFree(rg->index_buffer.indices); utilSafeFree(rg->color_buffer.buffer); utilSafeFree(rg); }