/* * <Short Description> * Copyright (C) 2023 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <blt/gfx/renderer/batch_2d_renderer.h> #include <blt/gfx/renderer/2d_textured.vert> #include <blt/gfx/renderer/2d_line.vert> #include <blt/gfx/renderer/2d_textured.frag> #include <blt/gfx/renderer/2d_textured_circle.frag> #include <blt/gfx/renderer/2d_line.frag> // https://stackoverflow.com/questions/60440682/drawing-a-line-in-modern-opengl float square_vertices[20] = { // positions // texture coords 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left -0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left }; const unsigned int square_indices[6] = { 0, 1, 3, // first triangle 1, 2, 3 // second triangle }; float line_vertices[20] = { // positions // texture coords 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left -0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left }; // 0, 1 (top right) // 5, 6 (bottom right) // 10, 11 (bottom left) // 15, 16 (top left) namespace blt::gfx { void batch_renderer_2d::create() { { vbo_t vertices_vbo; ebo_t indices_vbo; vertices_vbo.create(); indices_vbo.create(); vertices_vbo.allocate(sizeof(square_vertices), square_vertices); indices_vbo.allocate(sizeof(square_indices), square_indices); square_vao = new vertex_array(); auto tb = square_vao->bindVBO(vertices_vbo, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); square_vao->bindVBO(tb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); square_vao->bindElement(indices_vbo); } { vbo_t vertices_vbo; ebo_t indices_vbo; vertices_vbo.create(); indices_vbo.create(); vertices_vbo.allocate(sizeof(line_vertices), line_vertices); indices_vbo.allocate(sizeof(square_indices), square_indices); line_vao = new vertex_array(); auto tb = line_vao->bindVBO(vertices_vbo, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); line_vao->bindVBO(tb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); line_vao->bindElement(indices_vbo); } square_shader = new shader_t(shader_2d_textured_vert, shader_2d_textured_frag); square_shader->bindAttribute(0, "vertex"); square_shader->bindAttribute(1, "uv_in"); point_shader = new shader_t(shader_2d_textured_vert, shader_2d_textured_cirlce_frag); point_shader->bindAttribute(0, "vertex"); point_shader->bindAttribute(1, "uv_in"); } void batch_renderer_2d::cleanup() { delete square_vao; delete square_shader; delete point_shader; } template<typename T> void find_min_and_max(T& container, blt::f32& min, blt::f32& max) { for (auto& textures : container) { for (auto& colors : textures.second) { for (auto& blend_factors : colors.second) { for (auto& obj : blend_factors.second) { min = std::min(min, obj.z_index); max = std::max(max, obj.z_index); } } } } } void batch_renderer_2d::render(bool transparency) { if (transparency) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } glEnable(GL_DEPTH_TEST); draw_count_ = 0; square_shader->bind(); square_vao->bind(); glActiveTexture(GL_TEXTURE0); // annoying blt::f32 min = std::numeric_limits<blt::f32>::max(); blt::f32 max = std::numeric_limits<blt::f32>::min(); find_min_and_max(complex_rectangles, min, max); find_min_and_max(complex_points, min, max); find_min_and_max(complex_lines, min, max); blt::f32 denominator = 1.0f / (max - min); for (auto& textures : complex_rectangles) { // resource manager handles the check for empty string if (auto val = resources.get(textures.first)) val.value()->bind(); for (auto& colors : textures.second) { square_shader->setVec4("color", colors.first); for (auto& blend_factors : colors.second) { square_shader->setVec4("use_texture", blend_factors.first); for (auto& rect_obj : blend_factors.second) { auto& rect = rect_obj.obj; blt::mat4x4 model; model.translate(rect.pos.x(), rect.pos.y(), 0.0f); model.scale(rect.size.x(), rect.size.y(), 1); if (rect.rotation != 0) model.rotateZ(blt::toRadians(rect.rotation)); square_shader->setMatrix("model", model); square_shader->setFloat("z_index", (rect_obj.z_index - min) * denominator); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); draw_count_++; } blend_factors.second.clear(); } colors.second.clear(); } textures.second.clear(); } point_shader->bind(); for (auto& textures : complex_points) { // resource manager handles the check for empty string if (auto val = resources.get(textures.first)) val.value()->bind(); for (auto& colors : textures.second) { point_shader->setVec4("color", colors.first); for (auto& blend_factors : colors.second) { point_shader->setVec4("use_texture", blend_factors.first); for (auto& point_obj : blend_factors.second) { auto& point = point_obj.obj; blt::mat4x4 model; model.translate(point.pos.x(), point.pos.y(), 0.0f); model.scale(point.scale, point.scale, 1); point_shader->setMatrix("model", model); point_shader->setFloat("z_index", (point_obj.z_index - min) * denominator); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); draw_count_++; } blend_factors.second.clear(); } colors.second.clear(); } textures.second.clear(); } blt::mat4x4 model; square_shader->bind(); square_shader->setMatrix("model", model); line_vao->bind(); auto& buf = line_vao->getBuffer(0); buf.bind(); for (auto& textures : complex_lines) { // resource manager handles the check for empty string if (auto val = resources.get(textures.first)) val.value()->bind(); for (auto& colors : textures.second) { square_shader->setVec4("color", colors.first); for (auto& blend_factors : colors.second) { square_shader->setVec4("use_texture", blend_factors.first); for (auto& line_obj : blend_factors.second) { auto& line = line_obj.obj; // 0, 1 (top right) // 5, 6 (bottom right) // 10, 11 (bottom left) // 15, 16 (top left) blt::vec2 dir = (line.p1 - line.p2).normalize(); blt::vec2 right = {dir.y(), -dir.x()}; blt::vec2 left = {-dir.y(), dir.x()}; auto bottom_left = line.p1 + left * line.thickness; auto bottom_right = line.p1 + right * line.thickness; auto top_left = line.p2 + left * line.thickness; auto top_right = line.p2 + right * line.thickness; line_vertices[0] = top_right.x(); line_vertices[1] = top_right.y(); line_vertices[5] = bottom_right.x(); line_vertices[6] = bottom_right.y(); line_vertices[10] = bottom_left.x(); line_vertices[11] = bottom_left.y(); line_vertices[15] = top_left.x(); line_vertices[16] = top_left.y(); buf.update(sizeof(line_vertices), line_vertices); square_shader->setFloat("z_index", (line_obj.z_index - min) * denominator); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); draw_count_++; } blend_factors.second.clear(); } colors.second.clear(); } textures.second.clear(); } glDisable(GL_DEPTH_TEST); if (transparency) glDisable(GL_BLEND); } }