#define STB_IMAGE_IMPLEMENTATION #include "renderer.h" bool init_renderer(Renderer *renderer, u32 width, u32 height) { renderer->m_width = width; renderer->m_height = height; if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { log(1, "%s error: failed to initialize GLAD\n", __FUNCTION__); return false; } if (!GLAD_GL_VERSION_4_6) { log(1, "%s error: failed to get at least OpenGL 4.6\n", __FUNCTION__); return false; } { bool success = init_frame_buffer(&renderer->frame_buffer, width, height); if (!success) { log(1, "%s error: could not init Framebuffer\n", __FUNCTION__); return false; } log(1, "%s: framebuffer succesfully initialized\n", __FUNCTION__); } { bool success = load_texture(&renderer->texture, "assets/textures/crate.png"); if (!success) { log(1, "%s: texture loading failed\n", __FUNCTION__); return false; } log(1, "%s: texture successfully loaded\n", __FUNCTION__); } { bool success = init_vertex_buffer(&renderer->vertex_buffer); if (!success) { log(1, "%s: vertex buffer successfully created\n", __FUNCTION__); return false; } } { bool success = init_uniform_buffer(&renderer->uniform_buffer); if (!success) { log(1, "%s: uniform buffer successfully created\n", __FUNCTION__); return false; } } { bool success = load_shaders(&renderer->shader, "shaders/basic.vert", "shaders/basic.frag"); if (!success) { log(1, "%s: basic shader loading failed\n", __FUNCTION__); return false; } log(1, "%s: basic shaders succesfully loaded\n", __FUNCTION__); } { bool success = load_shaders(&renderer->alt_shader, "shaders/alt.vert", "shaders/alt.frag"); if (!success) { log(1, "%s: alt shader loading failed\n", __FUNCTION__); return false; } log(1, "%s: alt shaders succesfully loaded\n", __FUNCTION__); } return true; } void set_renderer_size(Renderer *renderer, u32 width, u32 height) { resize_frame_buffer(&renderer->frame_buffer, width, height); glViewport(0, 0, width, height); } void draw_renderer(Renderer *renderer) { bind_frame_buffer(&renderer->frame_buffer); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); Vector3 camera_position = Vector3(0.4f, 0.3f, 1.0f); Vector3 camera_look_at_position = Vector3(0.0f, 0.0f, 0.0f); Vector3 camera_up_vector = Vector3(0.0f, 1.0f, 0.0f); renderer->projection_matrix = glm::perspective(glm::radians(90.0f), static_cast(renderer->m_width) / static_cast(renderer->m_height), 0.1f, 100.0f); f32 dt = glfwGetTime(); Matrix4 model = Matrix4(1.0); if (!renderer->use_alt_shader) { use_shader(&renderer->shader); model = glm::rotate(Matrix4(1.0f), dt, Vector3(0.0f, 0.0f, 1.0f)); } else { use_shader(&renderer->alt_shader); model = glm::rotate(Matrix4(1.0f), -dt, Vector3(0.0f, 0.0f, 1.0f)); } renderer->view_matrix = glm::lookAt(camera_position, camera_look_at_position, camera_up_vector) * model; upload_uniform_buffer_data(&renderer->uniform_buffer, renderer->view_matrix, renderer->projection_matrix); bind_texture(&renderer->texture); bind_vertex_buffer(&renderer->vertex_buffer); draw_vertex_buffer(GL_TRIANGLES, 0, renderer->triangle_count * 3); unbind_vertex_buffer(); unbind_texture(); unbind_frame_buffer(&renderer->frame_buffer); draw_frame_buffer(&renderer->frame_buffer); } void upload_renderer_data(Renderer *renderer, Mesh *vertex_data) { renderer->triangle_count = vertex_data->vertices.size() / 3; upload_vertex_buffer_data(&renderer->vertex_buffer, vertex_data); } void cleanup_renderer(Renderer *renderer) { cleanup_shader(&renderer->shader); cleanup_shader(&renderer->alt_shader); cleanup_texture(&renderer->texture); cleanup_vertex_buffer(&renderer->vertex_buffer); cleanup_frame_buffer(&renderer->frame_buffer); } /* * Frame buffer */ bool init_frame_buffer(Frame_Buffer *frame_buffer, u32 width, u32 height) { frame_buffer->width = width; frame_buffer->height = height; glGenFramebuffers(1, &frame_buffer->buffer); glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer->buffer); glEnable(GL_FRAMEBUFFER_SRGB); glGenTextures(1, &frame_buffer->color_tex); glBindTexture(GL_TEXTURE_2D, frame_buffer->color_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, frame_buffer->color_tex, 0); log(1, "%s: added color buffer\n", __FUNCTION__); glGenRenderbuffers(1, &frame_buffer->depth_buffer); glBindRenderbuffer(GL_RENDERBUFFER, frame_buffer->depth_buffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frame_buffer->depth_buffer); glBindRenderbuffer(GL_RENDERBUFFER, 0); log(1, "%s: added depth render buffer\n", __FUNCTION__); glBindFramebuffer(GL_FRAMEBUFFER, 0); return check_frame_buffer_complete(frame_buffer); } bool check_frame_buffer_complete(Frame_Buffer *frame_buffer) { glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer->buffer); GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (result != GL_FRAMEBUFFER_COMPLETE) { log(1, "%s error: framebuffer is NOT complete\n", __FUNCTION__); return false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); log(1, "%s: framebuffer is complete\n", __FUNCTION__); return true; } bool resize_frame_buffer(Frame_Buffer *frame_buffer, u32 width, u32 height) { frame_buffer->width = width; frame_buffer->height = height; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDeleteTextures(1, &frame_buffer->color_tex); glDeleteRenderbuffers(1, &frame_buffer->depth_buffer); glDeleteFramebuffers(1, &frame_buffer->buffer); return init_frame_buffer(frame_buffer, width, height); } void draw_frame_buffer(Frame_Buffer *frame_buffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, frame_buffer->buffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer(0, 0, frame_buffer->width, frame_buffer->height, 0, 0, frame_buffer->width, frame_buffer->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } void bind_frame_buffer(Frame_Buffer *frame_buffer) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_buffer->buffer); } void unbind_frame_buffer(Frame_Buffer *frame_buffer) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void cleanup_frame_buffer(Frame_Buffer *frame_buffer) { unbind_frame_buffer(frame_buffer); glDeleteTextures(1, &frame_buffer->color_tex); glDeleteRenderbuffers(1, &frame_buffer->depth_buffer); glDeleteFramebuffers(1, &frame_buffer->buffer); } /* * Vertex buffer */ bool init_vertex_buffer(Vertex_Buffer *vertex_buffer) { glGenVertexArrays(1, &vertex_buffer->vao); glGenBuffers(1, &vertex_buffer->vbo); glBindVertexArray(vertex_buffer->vao); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer->vbo); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) offsetof(Vertex, position)); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) offsetof(Vertex, color)); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) offsetof(Vertex, uv)); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); return true; } void cleanup_vertex_buffer(Vertex_Buffer *vertex_buffer) { glDeleteBuffers(1, &vertex_buffer->vbo); glDeleteVertexArrays(1, &vertex_buffer->vao); } void upload_vertex_buffer_data(Vertex_Buffer *vertex_buffer, Mesh *vertex_data) { glBindVertexArray(vertex_buffer->vao); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer->vbo); glBufferData(GL_ARRAY_BUFFER, vertex_data->vertices.size() * sizeof(Vertex), &vertex_data->vertices.at(0), GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void bind_vertex_buffer(Vertex_Buffer *vertex_buffer) { glBindVertexArray(vertex_buffer->vao); } void unbind_vertex_buffer() { glBindVertexArray(0); } void draw_vertex_buffer(u32 mode, u32 start, u32 num) { glDrawArrays(mode, start, num); } /* * Uniform buffer */ bool init_uniform_buffer(Uniform_Buffer *uniform_buffer) { glGenBuffers(1, &uniform_buffer->ubo_buffer); glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer->ubo_buffer); glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(Matrix4), nullptr, GL_STATIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); return true; } void upload_uniform_buffer_data(Uniform_Buffer *uniform_buffer, Matrix4 view_matrix, Matrix4 projection_matrix) { glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer->ubo_buffer); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(Matrix4), glm::value_ptr(view_matrix)); glBufferSubData(GL_UNIFORM_BUFFER, sizeof(Matrix4), sizeof(Matrix4), glm::value_ptr(projection_matrix)); glBindBufferRange(GL_UNIFORM_BUFFER, 0, uniform_buffer->ubo_buffer, 0, 2 * sizeof(Matrix4)); glBindBuffer(GL_UNIFORM_BUFFER, 0); } void cleanup(Uniform_Buffer *uniform_buffer) { glDeleteBuffers(1, &uniform_buffer->ubo_buffer); } /* * Texture */ bool load_texture(Texture *texture, string filepath) { texture->name = filepath; stbi_set_flip_vertically_on_load(true); u8* texture_data = stbi_load(filepath.c_str(), &texture->width, &texture->height, &texture->number_of_channels, 0); if (!texture_data) { stbi_image_free(texture_data); return false; } glGenTextures(1, &texture->texture); glBindTexture(GL_TEXTURE_2D, texture->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); stbi_image_free(texture_data); return true; } void bind_texture(Texture *texture) { glBindTexture(GL_TEXTURE_2D, texture->texture); } void unbind_texture() { glBindTexture(GL_TEXTURE_2D, 0); } void cleanup_texture(Texture *texture) { glDeleteTextures(1, &texture->texture); } /* * Shaders */ bool load_shaders(Shader *shader, string vertex_shader_filepath, string fragment_shader_filepath) { u32 vertex_shader = load_shader(vertex_shader_filepath, GL_VERTEX_SHADER); if (!vertex_shader) { return false; } u32 fragment_shader = load_shader(fragment_shader_filepath, GL_FRAGMENT_SHADER); if (!fragment_shader) { return false; } shader->program = glCreateProgram(); glAttachShader(shader->program, vertex_shader); glAttachShader(shader->program, fragment_shader); glLinkProgram(shader->program); s32 is_program_linked; glGetProgramiv(shader->program, GL_LINK_STATUS, &is_program_linked); if (!is_program_linked) { glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return false; } s32 ubo_index = glGetUniformBlockIndex(shader->program, "Matrices"); glUniformBlockBinding(shader->program, ubo_index, 0); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return true; } void cleanup_shader(Shader *shader) { glDeleteProgram(shader->program); } u32 load_shader(string filepath, u32 shader_type) { string shader_as_text; shader_as_text = load_file_to_string(filepath); const char*shader_source = shader_as_text.c_str(); u32 shader = glCreateShader(shader_type); glShaderSource(shader, 1, (const GLchar**) &shader_source, 0); glCompileShader(shader); s32 is_shader_compiled; glGetShaderiv(shader, GL_COMPILE_STATUS, &is_shader_compiled); if (!is_shader_compiled) { return 0; } return shader; } void use_shader(Shader *shader) { glUseProgram(shader->program); }