#include #include "dumbLog.h" #include "render_object.h" // forward declarations struct default_render_object { glm::mat4 node_xform; GLuint tex_id; GLuint vertex_buffer_id; GLuint normal_buffer_id; GLuint uv_buffer_id; GLuint index_buffer_id; uint index_buffer_count; }; struct simple_render_object { glm::mat4 model_transform; GLuint vertex_buffer_id; GLuint vertex_color_buffer_id; uint vertex_count; GLenum draw_mode; }; struct render_objects { void* objects; uint count; mesh_t mesh_type; }; void drawDefault(render_objects* r_objs, glm::mat4 world_transform, camera& cam, shader_wrapper sw, light_group* lights); void drawSimple(render_objects* r_objs, glm::mat4 world_transform, camera& cam, shader_wrapper sw, light_group* lights); inline void enableGLFloatBuffer(uint buffer_id, uint location); bool initGLFloatBuffer(glm::vec3* buffer, uint count, GLuint& buffer_id, GLenum usage = GL_STATIC_DRAW); bool initGLIndexBuffer(uint* buffer, uint num_indices, GLuint& buffer_id); bool initGLTexture(const util_image image, GLuint& tex_id); bool loadMeshIntoGL(default_render_object* ro_out, mesh_info* mi_in); inline void updateMatrices(default_shader_program* shader, camera& cam, glm::mat4 world_xform, glm::mat4 node_xform); // interface render_objects* roInitModel(mesh_group mg) { uint count = mg.num_meshes; assert(count > 0); render_objects* r_objs = UTIL_ALLOC(1, render_objects); r_objs->objects = UTIL_ALLOC(count, default_render_object); r_objs->count = count; r_objs->mesh_type = DEFAULT_MESHES; default_render_object* objects = (default_render_object*) r_objs->objects; for (uint i = 0; i < count; i++) { if (!loadMeshIntoGL(&objects[i], mg.meshes[i])) { roFree(r_objs); return nullptr; } } return r_objs; } render_objects* roInitSimpleMesh(simple_mesh& mesh_in, GLenum draw_mode) { render_objects* r_objs = UTIL_ALLOC(1, render_objects); r_objs->count = 1; r_objs->mesh_type = SIMPLE_MESH; r_objs->objects = UTIL_ALLOC(1, simple_render_object); simple_render_object* objects = (simple_render_object*) r_objs->objects; if (initGLFloatBuffer(mesh_in.vertices, mesh_in.num_vertices, objects->vertex_buffer_id, GL_DYNAMIC_DRAW) && initGLFloatBuffer(mesh_in.vert_colors, mesh_in.num_vertices, objects->vertex_color_buffer_id, GL_DYNAMIC_DRAW)) { objects->model_transform = mesh_in.model_transform; objects->vertex_count = mesh_in.num_vertices; objects->draw_mode = draw_mode; return r_objs; } LOG(Error) << "Failed to initialize render_object\n"; roFree(r_objs); return nullptr; } void roFree(render_objects* r_objs) { if (r_objs->mesh_type == SIMPLE_MESH) { // } else if (r_objs->mesh_type == DEFAULT_MESHES) { default_render_object* objects = (default_render_object*) r_objs->objects; for (uint i = 0; i < r_objs->count; i++) { glDeleteBuffers(1, &objects[i].vertex_buffer_id); glDeleteBuffers(1, &objects[i].normal_buffer_id); glDeleteBuffers(1, &objects[i].uv_buffer_id); glDeleteBuffers(1, &objects[i].index_buffer_id); glDeleteTextures(1, &objects[i].tex_id); } utilSafeFree(r_objs->objects); utilSafeFree(r_objs); } } // TODO: update projection * view matrices once per frame here void roDraw(render_objects* r_objs, glm::mat4 world_transform, camera& cam, shader_wrapper sw, light_group* lights) { if (r_objs->mesh_type == SIMPLE_MESH) drawSimple(r_objs, world_transform, cam, sw, lights); else if (r_objs->mesh_type == DEFAULT_MESHES) drawDefault(r_objs, world_transform, cam, sw, lights); } void roUpdateSimpleMesh(render_objects* r_objs, simple_mesh* mesh, GLenum draw_mode) { assert(r_objs != nullptr && r_objs->objects != nullptr); assert(mesh != nullptr && mesh->vertices != nullptr); simple_render_object* sro = (simple_render_object*) r_objs->objects; assert(sro->vertex_count == mesh->num_vertices); sro->draw_mode = draw_mode; glBindBuffer(GL_ARRAY_BUFFER, sro->vertex_buffer_id); glBufferSubData( GL_ARRAY_BUFFER, 0, 3 * sro->vertex_count * sizeof(GLfloat), mesh->vertices); glBindBuffer(GL_ARRAY_BUFFER, sro->vertex_color_buffer_id); glBufferSubData( GL_ARRAY_BUFFER, 0, 3 * sro->vertex_count * sizeof(GLfloat), mesh->vert_colors); } // internal void drawDefault(render_objects* r_objs, glm::mat4 world_transform, camera& cam, shader_wrapper sw, light_group* lights) { default_shader_program* shader = sw.default_shader; default_render_object* objects = (default_render_object*) r_objs->objects; glUseProgram(shader->program_id); updateMatrices(shader, cam, world_transform, objects->node_xform); if (lights->needs_update) lightsUpdate(lights, shader); for (uint i = 0; i < r_objs->count; i++) { enableGLFloatBuffer(objects[i].vertex_buffer_id, 0); enableGLFloatBuffer(objects[i].normal_buffer_id, 1); // TODO: could pass in a stride parameter here to enableGLFloatBuffer() // could then use a 2d buffer for uv coords enableGLFloatBuffer(objects[i].uv_buffer_id, 2); glBindTexture(GL_TEXTURE_2D, objects[i].tex_id); glUniform1i(shader->sampler_id, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, objects[i].index_buffer_id); glDrawElements(GL_TRIANGLES, objects[i].index_buffer_count, GL_UNSIGNED_INT, 0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); } glUseProgram(0); } void drawSimple(render_objects* r_objs, glm::mat4 world_transform, camera& cam, shader_wrapper sw, light_group* lights) { simple_render_object* ro = (simple_render_object*) r_objs->objects; simple_shader_program* shader = (simple_shader_program*) sw.simple_shader; glUseProgram(shader->program_id); cam.MVP = cam.projection * cam.view * ro->model_transform; glUniformMatrix4fv( shader->world_transform_id, 1, GL_FALSE, &world_transform[0][0]); glUniformMatrix4fv(shader->MVP_id, 1, GL_FALSE, &cam.MVP[0][0]); enableGLFloatBuffer(ro->vertex_buffer_id, 0); enableGLFloatBuffer(ro->vertex_color_buffer_id, 1); glDrawArrays(ro->draw_mode, 0, ro->vertex_count); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glUseProgram(0); } inline void enableGLFloatBuffer(uint buffer_id, uint location) { glEnableVertexAttribArray(location); glBindBuffer(GL_ARRAY_BUFFER, buffer_id); glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); } bool initGLFloatBuffer(glm::vec3* buffer, uint count, GLuint& buffer_id, GLenum usage) { glGenBuffers(1, &buffer_id); glBindBuffer(GL_ARRAY_BUFFER, buffer_id); glBufferData(GL_ARRAY_BUFFER, count * 3 * sizeof(GLfloat), // NOTE: 3 floats per vertex prop buffer, usage); return (glGetError() == GL_NO_ERROR); } bool initGLIndexBuffer(uint* buffer, uint num_indices, GLuint& buffer_id) { glGenBuffers(1, &buffer_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id); glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_indices * sizeof(uint), buffer, GL_STATIC_DRAW); return (glGetError() == GL_NO_ERROR); } bool initGLTexture(const util_image image, GLuint& tex_id) { glGenTextures(1, &tex_id); glBindTexture(GL_TEXTURE_2D, tex_id); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); GLenum pixel_format = (image.num_channels == 3) ? GL_RGB : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0, pixel_format, image.w, image.h, 0, pixel_format, 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); return (glGetError() == GL_NO_ERROR); } bool loadMeshIntoGL(default_render_object* ro_out, mesh_info* mi_in) { assert(mi_in != nullptr && ro_out != nullptr); if (initGLFloatBuffer(mi_in->vertices, mi_in->num_vertices, ro_out->vertex_buffer_id) && initGLFloatBuffer(mi_in->normals, mi_in->num_vertices, ro_out->normal_buffer_id) && // FIXME: this is broken now with tinygltf, need to use vec2 initGLFloatBuffer(mi_in->texture_coords, mi_in->num_vertices, ro_out->uv_buffer_id) && initGLIndexBuffer(mi_in->indices, mi_in->num_indices, ro_out->index_buffer_id) && initGLTexture(mi_in->diffuse_texture, ro_out->tex_id)) { ro_out->node_xform = mi_in->model_transform; ro_out->index_buffer_count = mi_in->num_indices; return true; } LOG(Error) << "Failed to initialize render_object\n"; return false; } // TODO: really only need to update the view and projection matrices once per // frame, maybe add another interface function in render_object to call from // renRenderFrame inline void updateMatrices(default_shader_program* shader, camera& cam, glm::mat4 world_xform, glm::mat4 node_xform) { glUniformMatrix4fv( shader->world_transform_id, 1, GL_FALSE, &world_xform[0][0]); glUniformMatrix4fv(shader->model_matrix_id, 1, GL_FALSE, &node_xform[0][0]); glUniformMatrix4fv(shader->view_matrix_id, 1, GL_FALSE, &cam.view[0][0]); glUniformMatrix4fv(shader->projection_matrix_id, 1, GL_FALSE, &cam.projection[0][0]); glm::mat3 normal_matrix = glm::transpose( glm::inverse(glm::mat3(cam.model))); glUniformMatrix3fv(shader->normal_matrix_id, 1, GL_FALSE, &normal_matrix[0][0]); }