#include #include // calloc #include #include #include #include "aixlog.hpp" #include "render_group.h" #include "mesh.h" #include "util.h" #define INFO_LOG_MAX_LENGTH 312; // forward declarations render_object * allocateRenderObject(uint buffer_len, uint index_len = 0); void freeRenderObject(render_object* ro); bool convertMeshInfo(meMeshInfo* mesh, render_object* ro, bool use_normals); void sendIndexBufferToGL(gl_index_buffer* index_buffer, GLenum usage, GLenum target); void drawRenderObject(render_group* rg, render_object* ro, glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, glm::vec3 light_position, GLuint light_id, bool update_vertex_data); // interface render_group* rgInitSingle(rg_shader_program& sp, uint vertex_buffer_len, bool use_normals, uint index_buffer_len, GLenum draw_mode) { render_group* rg = UTIL_ALLOC(1, render_group); rg->shader = sp; rg->use_normals = use_normals; rg->draw_indexed = (index_buffer_len > 0); rg->draw_mode = draw_mode; rg->render_objects = UTIL_ALLOC(1, render_object*); rg->render_objects[0] = allocateRenderObject(vertex_buffer_len, index_buffer_len); rg->num_objects = 1; if (rg->render_objects[0] == nullptr) { LOG(ERROR) << "Allocation Error\n"; rgFree(rg); } return rg; } bool rgInitShaderProgram(rg_shader_program& sp, const char * vertex_code, const char * frag_code) { glGenVertexArrays(1, &sp.vertex_array_id); glBindVertexArray(sp.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); sp.program_id = glCreateProgram(); glAttachShader(sp.program_id, vertex_shader_id); glAttachShader(sp.program_id, fragment_shader_id); glLinkProgram(sp.program_id); sp.model_matrix_id = glGetUniformLocation(sp.program_id, "model"); sp.view_matrix_id = glGetUniformLocation(sp.program_id, "view"); sp.projection_matrix_id = glGetUniformLocation(sp.program_id, "projection"); sp.normal_matrix_id = glGetUniformLocation(sp.program_id, "normal_matrix"); glDetachShader(sp.program_id, vertex_shader_id); glDetachShader(sp.program_id, fragment_shader_id); glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); // TODO: quick hack to allow 'dynamic' stack allocation for msvc // also remove INFO_LOG_MAX_LENGTH define GLint isLinked = 0; glGetProgramiv(sp.program_id, GL_LINK_STATUS, &isLinked); if (isLinked == GL_FALSE) { GLint maxLength = INFO_LOG_MAX_LENGTH; glGetProgramiv(sp.program_id, GL_INFO_LOG_LENGTH, &maxLength); #ifdef _WIN32 GLchar infoLog[312] = { 0 }; #else GLchar infoLog[maxLength] = { 0 }; #endif glGetProgramInfoLog(sp.program_id, maxLength, &maxLength, &infoLog[0]); LOG(ERROR) << infoLog << "\n"; glDeleteProgram(sp.program_id); return false; } return true; } // TODO: move Entity initialization to Entity.h/.cpp to fix circular dependancy // then just call into convertMeshInfo directly bool rgInitEntity(Entity* e) { render_group* rg = UTIL_ALLOC(1, render_group); e->ren_group = rg; rg->draw_indexed = true; rg->draw_mode = GL_TRIANGLES; rg->use_normals = e->mesh_group.use_normals; uint num_meshes = rg->num_objects = e->mesh_group.num_meshes; // apply translation/rotation/scaling e->world_transform = glm::mat4(1.0); e->world_transform = glm::translate(e->world_transform, e->translation); e->world_transform = glm::scale(e->world_transform, e->scale); rg->render_objects = (render_object**) std::calloc(num_meshes, sizeof(render_object*)); for (uint i = 0; i < num_meshes; i++) { uint buffer_len = e->mesh_group.meshes[i]->num_vertices * 3; uint index_len = e->mesh_group.meshes[i]->num_indices; rg->render_objects[i] = allocateRenderObject(buffer_len, index_len); if (rg->render_objects[i] == nullptr) return false; // TODO: safely free memory here when convertMeshInfo returns false if (!convertMeshInfo(e->mesh_group.meshes[i], rg->render_objects[i], rg->use_normals)) return false; } return true; } void rgBufferData(gl_buffer* buffer, GLenum usage, GLenum target) { glGenBuffers(1, &buffer->buffer_id); glBindBuffer(target, buffer->buffer_id); glBufferData(target, buffer->buffer_len * sizeof(GLfloat), buffer->buffer, usage); } void rgDraw(render_group* rg, glm::mat4 model_matrix, glm::mat4 view_matrix, glm::mat4 projection_matrix, glm::vec3 light_position, GLuint light_id, bool update_vertex_data) { for (uint i = 0; i < rg->num_objects; i++) { drawRenderObject(rg, rg->render_objects[i], model_matrix, view_matrix, projection_matrix, light_position, light_id, update_vertex_data); } } void rgFree(render_group* rg) { if (rg == nullptr) { LOG(ERROR) << "tried to free nullptr\n"; return; } for (uint i = 0; i < rg->num_objects; i++) freeRenderObject(rg->render_objects[i]); utilSafeFree(rg->render_objects); utilSafeFree(rg); } // internal render_object * allocateRenderObject(uint buffer_len, uint index_len) { render_object* ro = UTIL_ALLOC(1, render_object); if (ro == nullptr) return nullptr; ro->vertex_buffer.buffer = UTIL_ALLOC(buffer_len, GLfloat); ro->vertex_buffer.buffer_len = buffer_len; ro->normal_buffer.buffer = UTIL_ALLOC(buffer_len, GLfloat); ro->normal_buffer.buffer_len = buffer_len; ro->color_buffer.buffer = UTIL_ALLOC(buffer_len, GLfloat); ro->color_buffer.buffer_len = buffer_len; if (index_len > 0) { ro->index_buffer.buffer = UTIL_ALLOC(index_len, uint); ro->index_buffer.buffer_len = index_len; } if (ro->vertex_buffer.buffer == nullptr || ro->normal_buffer.buffer == nullptr || ro->color_buffer.buffer == nullptr || ((index_len > 0) && (ro->vertex_buffer.buffer == nullptr))) { freeRenderObject(ro); return nullptr; } return ro; } void freeRenderObject(render_object* ro) { utilSafeFree(ro->vertex_buffer.buffer); utilSafeFree(ro->normal_buffer.buffer); utilSafeFree(ro->color_buffer.buffer); utilSafeFree(ro->index_buffer.buffer); utilSafeFree(ro); } void sendIndexBufferToGL(gl_index_buffer* index_buffer, GLenum usage, GLenum target) { glGenBuffers(1, &index_buffer->buffer_id); glBindBuffer(target, index_buffer->buffer_id); glBufferData(target, index_buffer->buffer_len * sizeof(uint), index_buffer->buffer, usage); } bool convertMeshInfo(meMeshInfo* mesh, render_object* ro, bool use_normals) { uint vertex_buf_len = mesh->num_vertices * 3; GLfloat* vertex_buf = ro->vertex_buffer.buffer; GLfloat* color_buf = ro->color_buffer.buffer; GLfloat* normal_buf = ro->normal_buffer.buffer; uint* index_buf = ro->index_buffer.buffer; if (!vertex_buf || !color_buf || !normal_buf || !index_buf) return false; // dump vertices, colors, and normals into render_group buffers uint vertex_index = 0; uint vertex_prop_index = 0; for (uint i = 0; i < vertex_buf_len; i++) { const glm::vec3& vertex = mesh->vertices[vertex_index]; const glm::vec3& normal = 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 (use_normals) { 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] = mesh->diffuse_color[vertex_prop_index]; vertex_prop_index++; if (vertex_prop_index == 3) { vertex_prop_index = 0; vertex_index++; } } // dump indices for (uint i = 0; i < mesh->num_indices; i++) index_buf[i] = mesh->indices[i]; rgBufferData(&ro->vertex_buffer, GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER); rgBufferData(&ro->color_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER); if (use_normals) rgBufferData(&ro->normal_buffer, GL_STATIC_DRAW, GL_ARRAY_BUFFER); sendIndexBufferToGL(&ro->index_buffer, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); return true; } void drawRenderObject(render_group* rg, render_object* ro, 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->shader.program_id); glUniformMatrix4fv(rg->shader.model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]); glUniformMatrix4fv(rg->shader.view_matrix_id, 1, GL_FALSE, &view_matrix[0][0]); glUniformMatrix4fv(rg->shader.projection_matrix_id, 1, GL_FALSE, &projection_matrix[0][0]); // 1st attribute buffer : vertices glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, ro->vertex_buffer.buffer_id); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); if (update_vertex_data) { glBufferSubData(GL_ARRAY_BUFFER, 0, ro->vertex_buffer.buffer_len * sizeof(GLfloat), ro->vertex_buffer.buffer); } // 2nd attribute buffer : colors if (ro->color_buffer.buffer) { glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, ro->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, ro->color_buffer.buffer_len * sizeof(GLfloat), ro->color_buffer.buffer); } // 3rd attribute buffer: normals if (rg->use_normals) { glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, ro->normal_buffer.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->shader.normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]); } // draw if (rg->draw_indexed) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ro->index_buffer.buffer_id); glDrawElements(rg->draw_mode, ro->index_buffer.buffer_len, GL_UNSIGNED_INT, 0); } else { glDrawArrays(rg->draw_mode, 0, ro->vertex_buffer.buffer_len / 3); } // cleanup glDisableVertexAttribArray(0); if (ro->color_buffer.buffer) glDisableVertexAttribArray(1); if (rg->use_normals) glDisableVertexAttribArray(2); }