From ad6ef343001b7a4b1c0b96572969bbbd9f3568bc Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 18 Mar 2025 20:52:43 -0400 Subject: [PATCH] add curve rendering --- CMakeLists.txt | 2 +- include/blt/gfx/renderer/batch_2d_renderer.h | 132 ++-- src/blt/gfx/renderer/batch_2d_renderer.cpp | 784 +++++++++++-------- src/blt/gfx/window.cpp | 4 +- 4 files changed, 537 insertions(+), 385 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ff1005..5fa98cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25) include(FetchContent) -set(BLT_GRAPHICS_VERSION 1.1.8) +set(BLT_GRAPHICS_VERSION 1.1.9) set(BLT_GRAPHICS_TEST_VERSION 0.0.1) project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION}) diff --git a/include/blt/gfx/renderer/batch_2d_renderer.h b/include/blt/gfx/renderer/batch_2d_renderer.h index 2d8f2dc..aa403b1 100644 --- a/include/blt/gfx/renderer/batch_2d_renderer.h +++ b/include/blt/gfx/renderer/batch_2d_renderer.h @@ -70,24 +70,75 @@ namespace blt::gfx {} }; + struct curve2d_mesh_data_t + { + struct line_vertex_t + { + vec3 pos; + vec2 uv; + }; + + struct draw_t + { + std::unique_ptr vao; + i32 count; + }; + + [[nodiscard]] draw_t to_vertex_array() const; + size_t populate_vertex_array(vertex_array_t& va) const; + + [[nodiscard]] std::vector calculate_vertices() const; + + curve2d_mesh_data_t& with(const curve2d_mesh_data_t& mesh) + { + lines.insert(lines.end(), mesh.lines.begin(), mesh.lines.end()); + return *this; + } + + curve2d_mesh_data_t& set_thickness(const f32 thickness) + { + for (auto& line : lines) + line.thickness = thickness; + return *this; + } + + std::vector lines; + }; + + class curve2d_t + { + public: + curve2d_t(vec2 p0, vec2 p1, vec2 p2); + curve2d_t(vec2 p0, vec2 p1, vec2 p2, vec2 p3); + + [[nodiscard]] vec2 get_point(f32 t) const; + + [[nodiscard]] std::vector to_lines(i32 segments, f32 thickness = 1.0) const; + + [[nodiscard]] curve2d_mesh_data_t to_mesh(i32 segments, f32 thickness = 1.0) const; + + private: + vec2 m_p0, m_p1, m_p2, m_p3; + }; + struct point2d_t { vec2f pos; - float scale = 1; + f32 scale = 1; - point2d_t(const float x, const float y, const float scale): pos(x, y), scale(scale) + point2d_t(const f32 x, const f32 y, const f32 scale): pos(x, y), scale(scale) {} - point2d_t(const float x, const float y): pos(x, y) + point2d_t(const f32 x, const f32 y): pos(x, y) {} - point2d_t(const vec2f pos, const float scale): pos(pos), scale(scale) + point2d_t(const vec2f pos, const f32 scale): pos(pos), scale(scale) {} explicit point2d_t(const vec2f pos): pos(pos) {} - point2d_t apply_scale(float s) const + [[nodiscard]] point2d_t apply_scale(const f32 s) const { return {pos, scale * s}; } @@ -143,13 +194,14 @@ namespace blt::gfx f32 z_index; T obj; - render_object_t(const float z_index, const T obj): z_index(z_index), obj(obj) + render_object_t(const f32 z_index, const T obj): z_index(z_index), obj(obj) {} }; using rectangle2d_obj_t = render_object_t; using point2d_obj_t = render_object_t; using line2d_obj_t = render_object_t; + using curve2d_obj_t = render_object_t; template using object_container = hashmap_t>>; @@ -157,6 +209,7 @@ namespace blt::gfx private: std::unique_ptr square_vao; std::unique_ptr line_vao; + std::unique_ptr curve_vao; std::unique_ptr square_shader; std::unique_ptr point_shader; resource_manager& resources; @@ -168,6 +221,7 @@ namespace blt::gfx object_container complex_rectangles; object_container complex_points; object_container complex_lines; + object_container complex_curves; size_t draw_count = 0; f32 z_min = std::numeric_limits::max(); f32 z_max = std::numeric_limits::min(); @@ -188,15 +242,17 @@ namespace blt::gfx // called after draw_objects() void post_reset(); - void draw_points(const f32 denominator); + void draw_points(f32 denominator); - void draw_lines(const f32 denominator); + void draw_lines(f32 denominator); - void draw_rectangles(const f32 denominator); + void draw_rectangles(f32 denominator); + + void draw_curves(f32 denominator); void draw_objects(); - inline void update_z_index(f32 z_index) + void update_z_index(const f32 z_index) { draw.z_min = std::min(draw.z_min, -z_index); draw.z_max = std::max(draw.z_max, -z_index); @@ -214,61 +270,31 @@ namespace blt::gfx void create(); - void drawRectangleInternal(std::string_view texture, const rectangle2d_t& rectangle, f32 z_index = 0); + void drawRectangle(const rectangle2d_t& rectangle, std::string_view texture, f32 z_index = 0); - void drawRectangleInternal(const vec4& color, const rectangle2d_t& rectangle, f32 z_index = 0); + void drawRectangle(const rectangle2d_t& rectangle, const vec4& color, f32 z_index = 0); - void drawRectangleInternal(const render_info_t& draw_info, const rectangle2d_t& rectangle, f32 z_index = 0); + void drawRectangle(const rectangle2d_t& rectangle, const render_info_t& draw_info, f32 z_index = 0); - void drawLineInternal(std::string_view texture, const line2d_t& line, f32 z_index = 0); + void drawLine(const line2d_t& line, std::string_view texture, f32 z_index = 0); - void drawLineInternal(const vec4& color, const line2d_t& line, f32 z_index = 0); + void drawLine(const line2d_t& line, const vec4& color, f32 z_index = 0); - void drawLineInternal(const render_info_t& draw_info, const line2d_t& line, f32 z_index = 0); + void drawLine(const line2d_t& line, const render_info_t& draw_info, f32 z_index = 0); - void drawPointInternal(std::string_view texture, const point2d_t& point, f32 z_index = 0); + void drawPoint(const point2d_t& point, std::string_view texture, f32 z_index = 0); - void drawPointInternal(const vec4& color, const point2d_t& point, f32 z_index = 0); + void drawPoint(const point2d_t& point, const vec4& color, f32 z_index = 0); - void drawPointInternal(const render_info_t& draw_info, const point2d_t& point, f32 z_index = 0); + void drawPoint(const point2d_t& point, const render_info_t& draw_info, f32 z_index = 0); - template - void drawRectangle(const T& render_info, P... p) - { - drawRectangleInternal(render_info, {p...}); - } + void drawCurve(const curve2d_mesh_data_t& curve, std::string_view texture, f32 z_index = 0); - template - void drawPoint(const T& render_info, P... p) - { - drawPointInternal(render_info, {p...}); - } + void drawCurve(const curve2d_mesh_data_t& curve, const vec4& color, f32 z_index = 0); - template - void drawLine(const T& render_info, P... p) - { - drawLineInternal(render_info, {p...}); - } + void drawCurve(const curve2d_mesh_data_t& curve, const render_info_t& draw_info, f32 z_index = 0); - template - void drawRectangle(const T& render_info, f32 z_index, P... p) - { - drawRectangleInternal(render_info, {p...}, z_index); - } - - template - void drawPoint(const T& render_info, f32 z_index, P... p) - { - drawPointInternal(render_info, {p...}, z_index); - } - - template - void drawLine(const T& render_info, f32 z_index, P... p) - { - drawLineInternal(render_info, {p...}, z_index); - } - - void render(i32 width, i32 height, bool transparency = true); + void render(i32 width, i32 height, bool transparency = true, bool postprocessing = false); void cleanup(); diff --git a/src/blt/gfx/renderer/batch_2d_renderer.cpp b/src/blt/gfx/renderer/batch_2d_renderer.cpp index 098f60a..6d77b0f 100644 --- a/src/blt/gfx/renderer/batch_2d_renderer.cpp +++ b/src/blt/gfx/renderer/batch_2d_renderer.cpp @@ -22,23 +22,23 @@ // 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 + // 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 + 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 + // 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) @@ -48,322 +48,448 @@ float line_vertices[20] = { namespace blt::gfx { - void batch_renderer_2d::create() - { - { - vertex_buffer_t vertices_vbo; - element_buffer_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 = std::make_unique(); - auto vb = vertex_array_t::make_vbo(vertices_vbo); - square_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); - square_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); - square_vao->bindElement(indices_vbo); - } - { - vertex_buffer_t vertices_vbo; - element_buffer_t indices_vbo; - - vertices_vbo.create(); - indices_vbo.create(); - - vertices_vbo.allocate(sizeof(line_vertices), line_vertices, GL_DYNAMIC_DRAW); - indices_vbo.allocate(sizeof(square_indices), square_indices, GL_DYNAMIC_DRAW); - - line_vao = std::make_unique(); - auto vb = vertex_array_t::make_vbo(vertices_vbo); - line_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); - line_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); - line_vao->bindElement(indices_vbo); - } - - square_shader = shader_t::make_unique(shader_2d_textured_vert, shader_2d_textured_frag); - square_shader->bindAttribute(0, "vertex"); - square_shader->bindAttribute(1, "uv_in"); - - point_shader = shader_t::make_unique(shader_2d_textured_vert, shader_2d_textured_cirlce_frag); - point_shader->bindAttribute(0, "vertex"); - point_shader->bindAttribute(1, "uv_in"); - - engine->create(); - } - - void batch_renderer_2d::drawRectangleInternal(const std::string_view texture, const rectangle2d_t& rectangle, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_rectangles, render_info_t::make_info(texture), {-z_index, rectangle}); - } - - void batch_renderer_2d::drawRectangleInternal(const vec4& color, const rectangle2d_t& rectangle, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_rectangles, render_info_t::make_info(color), {-z_index, rectangle}); - } - - void batch_renderer_2d::drawRectangleInternal(const render_info_t& draw_info, const rectangle2d_t& rectangle, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_rectangles, draw_info, {-z_index, rectangle}); - } - - void batch_renderer_2d::drawLineInternal(const std::string_view texture, const line2d_t& line, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_lines, render_info_t::make_info(texture), {-z_index, line}); - } - - void batch_renderer_2d::drawLineInternal(const vec4& color, const line2d_t& line, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_lines, render_info_t::make_info(color), {-z_index, line}); - } - - void batch_renderer_2d::drawLineInternal(const render_info_t& draw_info, const line2d_t& line, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_lines, draw_info, {-z_index, line}); - } - - void batch_renderer_2d::drawPointInternal(const std::string_view texture, const point2d_t& point, const f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_points, render_info_t::make_info(texture), {-z_index, point}); - } - - void batch_renderer_2d::drawPointInternal(const vec4& color, const point2d_t& point, f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_points, render_info_t::make_info(color), {-z_index, point}); - } - - void batch_renderer_2d::drawPointInternal(const render_info_t& draw_info, const point2d_t& point, f32 z_index) - { - update_z_index(z_index); - insert_obj(draw.complex_points, draw_info, {-z_index, point}); - } - - void batch_renderer_2d::cleanup() - { - engine->cleanup(); - engine = nullptr; - square_vao = nullptr; - line_vao = nullptr; - square_shader = nullptr; - point_shader = nullptr; - } - - void batch_renderer_2d::render(i32, i32, const bool transparency) - { - //draw_buffer.bind(); - //draw_buffer.updateBuffersStorage(width, height); - //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - if (transparency) - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - glEnable(GL_DEPTH_TEST); - glActiveTexture(GL_TEXTURE0); - - engine->bind(); - draw_objects(); - - glDisable(GL_DEPTH_TEST); - if (transparency) - glDisable(GL_BLEND); - - engine->render(); - render_reset(); - } + curve2d_mesh_data_t::draw_t curve2d_mesh_data_t::to_vertex_array() const + { + const auto vertices = calculate_vertices(); + auto vao = std::make_unique(); -// void batch_renderer_2d::draw_objects() -// { -// pre_reset(); -// const f32 denominator = 1.0f / (draw.z_max - draw.z_min); -// -// glEnable(GL_STENCIL_TEST); -// glClear(GL_STENCIL_BUFFER_BIT); -// glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); -// glStencilMask(0xFF); -// -// glStencilFunc(GL_ALWAYS, 1, 0xFF); -// draw_points(true, false, denominator); -// -// glDisable(GL_DEPTH_TEST); -// glStencilFunc(GL_EQUAL, 1, 0xFF); -// draw_points(false, true, denominator); -// glEnable(GL_DEPTH_TEST); -// -// glStencilFunc(GL_ALWAYS, 2, 0xFF); -// draw_rectangles(true, false, denominator); -// -// glDisable(GL_DEPTH_TEST); -// glStencilFunc(GL_EQUAL, 2, 0xFF); -// draw_rectangles(false, true, denominator); -// glEnable(GL_DEPTH_TEST); -// -// glStencilFunc(GL_ALWAYS, 4, 0xFF); -// draw_lines(true, false, denominator); -// -// glDisable(GL_DEPTH_TEST); -// glStencilFunc(GL_EQUAL, 4, 0xFF); -// draw_lines(false, true, denominator); -// glEnable(GL_DEPTH_TEST); -// -// glStencilMask(0x00); -// glDisable(GL_STENCIL_TEST); -// post_reset(); -// } - void batch_renderer_2d::draw_objects() - { - pre_reset(); - const f32 denominator = 1.0f / (draw.z_max - draw.z_min); - - glClear(GL_STENCIL_BUFFER_BIT); - draw_points(denominator); - draw_rectangles(denominator); - draw_lines(denominator); - post_reset(); - } - - void batch_renderer_2d::render_reset() - { - draw.z_min = std::numeric_limits::max(); - draw.z_max = std::numeric_limits::min(); - } - - void batch_renderer_2d::pre_reset() - { - draw.draw_count = 0; - } - - void batch_renderer_2d::post_reset() - { - - } - - void batch_renderer_2d::draw_points(const f32 denominator) - { - point_shader->bind(); - square_vao->bind(); - for (auto& [texture, objects] : draw.complex_points) - { - // resource manager handles the check for empty string - if (auto val = resources.get(texture)) - val.value()->bind(); - for (auto& [render_info, object] : objects) - { - auto& [z_index, point] = object; - - mat4x4 model; - model.translate(point.pos.x(), point.pos.y(), 0.0f); - model.scale(point.scale, point.scale, 1.0); - point_shader->setVec4("color", render_info.color); - point_shader->setVec4("use_texture", render_info.blend); - - //point_shader->setVec4("outline_color", render_info.outline_color); - point_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); - point_shader->setMatrix("model", model); - - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - draw.draw_count++; - } - objects.clear(); - } - } - - void batch_renderer_2d::draw_lines(const f32 denominator) - { - mat4x4 empty_model; - square_shader->setMatrix("model", empty_model); - line_vao->bind(); - auto& buf = line_vao->getBuffer(0); - buf.bind(); - for (auto& [texture, objects] : draw.complex_lines) - { - // resource manager handles the check for empty string - if (auto val = resources.get(texture)) - val.value()->bind(); - for (auto& [render_info, object] : objects) - { - auto& [z_index, line] = object; - - float thickness = line.thickness; - square_shader->setVec4("color", render_info.color); - square_shader->setVec4("use_texture", render_info.blend); - - //square_shader->setVec4("outline_color", render_info.outline_color); - square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); - - // 0, 1 (top right) - // 5, 6 (bottom right) - // 10, 11 (bottom left) - // 15, 16 (top left) - vec2 dir = (line.p1 - line.p2).normalize(); - vec2 right = {dir.y(), -dir.x()}; - vec2 left = {-dir.y(), dir.x()}; - - auto bottom_left = line.p1 + left * thickness; - auto bottom_right = line.p1 + right * thickness; - - auto top_left = line.p2 + left * thickness; - auto top_right = line.p2 + right * 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); - - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - draw.draw_count++; - } - objects.clear(); - } - } - - void batch_renderer_2d::draw_rectangles(const f32 denominator) - { - square_shader->bind(); - square_vao->bind(); - for (auto& [texture, objects] : draw.complex_rectangles) - { - // resource manager handles the check for empty string - if (auto val = resources.get(texture)) - val.value()->bind(); - for (auto& [render_info, object] : objects) - { - auto& [z_index, rect] = object; - - mat4x4 model; - model.translate(rect.pos); - model.scale(rect.size); - square_shader->setVec4("color", render_info.color); - square_shader->setVec4("use_texture", render_info.blend); - if (rect.rotation != 0) - model.rotateZ(toRadians(rect.rotation)); - - //square_shader->setVec4("outline_color", render_info.outline_color); - square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); - square_shader->setMatrix("model", model); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - draw.draw_count++; - } - objects.clear(); - } - } + vertex_buffer_t vertex_buffer; + vertex_buffer.create(); + vertex_buffer.allocate(static_cast(vertices.size() * sizeof(line_vertex_t)), vertices.data()); + + const auto vb = vertex_array_t::make_vbo(vertex_buffer); + vao->bindVBO(vb, 0, 3, GL_FLOAT, sizeof(line_vertex_t), 0); + vao->bindVBO(vb, 1, 2, GL_FLOAT, sizeof(line_vertex_t), sizeof(vec3)); + + return {std::move(vao), static_cast(vertices.size())}; + } + + size_t curve2d_mesh_data_t::populate_vertex_array(vertex_array_t& va) const + { + const auto vertices = calculate_vertices(); + va.getBuffer(0).update(static_cast(vertices.size() * sizeof(line_vertex_t)), vertices.data()); + return vertices.size(); + } + + std::vector curve2d_mesh_data_t::calculate_vertices() const + { + std::vector vertices; + + for (const auto [i, line] : enumerate(lines)) + { + const float thickness = line.thickness; + const vec2 dir = (line.p1 - line.p2).normalize(); + const vec2 right = {dir.y(), -dir.x()}; + const vec2 left = {-dir.y(), dir.x()}; + + auto bottom_left = line.p1 + left * thickness; + auto bottom_right = line.p1 + right * thickness; + + auto top_left = line.p2 + left * thickness; + auto top_right = line.p2 + right * thickness; + + vertices.push_back({make_vec3(bottom_right), vec2{1, 0}}); + vertices.push_back({make_vec3(bottom_left), vec2{0, 0}}); + + if (i == lines.size() - 1) + { + vertices.push_back({make_vec3(top_right), vec2{1, 1}}); + vertices.push_back({make_vec3(top_left), vec2{0, 1}}); + } else + { + auto& next = lines[i + 1]; + vec2 next_dir = (next.p1 - next.p2).normalize(); + vec2 next_right = {next_dir.y(), -next_dir.x()}; + vec2 next_left = {-next_dir.y(), next_dir.x()}; + + auto next_bottom_left = next.p1 + next_left * next.thickness; + auto next_bottom_right = next.p1 + next_right * next.thickness; + + auto avg_top_left = (next_bottom_left + top_left) / 2.0f; + auto avg_top_right = (next_bottom_right + top_right) / 2.0f; + + vertices.push_back({make_vec3(avg_top_right), vec2{1, 1}}); + vertices.push_back({make_vec3(avg_top_left), vec2{0, 1}}); + } + } + + return vertices; + } + + curve2d_t::curve2d_t(const vec2 p0, const vec2 p1, const vec2 p2): m_p0(p0), m_p1(p1), m_p2(p1), m_p3(p2) + {} + + curve2d_t::curve2d_t(const vec2 p0, const vec2 p1, const vec2 p2, const vec2 p3): m_p0(p0), m_p1(p1), m_p2(p2), m_p3(p3) + {} + + vec2 curve2d_t::get_point(const float t) const + { + const auto t_inv = 1.0f - t; + const auto t_inv_sq = t_inv * t_inv; + const auto t_inv_cub = t_inv_sq * t_inv; + const auto t_sq = t * t; + const auto t_cub = t_sq * t; + return t_inv_cub * m_p0 + 3 * t_inv_sq * t * m_p1 + 3 * t_inv * t_sq * m_p2 + t_cub * m_p3; + } + + std::vector curve2d_t::to_lines(const i32 segments, const float thickness) const + { + std::vector lines; + float t = 0; + const float diff = 1.0f / static_cast(segments); + + for (i32 i = 0; i < segments; ++i) + { + auto begin = get_point(t); + t += diff; + auto end = get_point(t); + + lines.emplace_back(begin, end, thickness); + } + + return lines; + } + + curve2d_mesh_data_t curve2d_t::to_mesh(const i32 segments, const float thickness) const + { + curve2d_mesh_data_t mesh_data; + mesh_data.lines = to_lines(segments, thickness); + return mesh_data; + } + + void batch_renderer_2d::create() + { + { + vertex_buffer_t vertices_vbo; + element_buffer_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 = std::make_unique(); + auto vb = vertex_array_t::make_vbo(vertices_vbo); + square_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); + square_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); + square_vao->bindElement(indices_vbo); + } + { + vertex_buffer_t vertices_vbo; + element_buffer_t indices_vbo; + + vertices_vbo.create(); + indices_vbo.create(); + + vertices_vbo.allocate(sizeof(line_vertices), line_vertices, GL_DYNAMIC_DRAW); + indices_vbo.allocate(sizeof(square_indices), square_indices, GL_DYNAMIC_DRAW); + + line_vao = std::make_unique(); + const auto vb = vertex_array_t::make_vbo(vertices_vbo); + line_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); + line_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); + line_vao->bindElement(indices_vbo); + } + { + curve_vao = std::make_unique(); + + vertex_buffer_t vertex_buffer; + vertex_buffer.create(); + vertex_buffer.allocate(0, GL_DYNAMIC_DRAW); + + const auto vb = vertex_array_t::make_vbo(vertex_buffer); + curve_vao->bindVBO(vb, 0, 3, GL_FLOAT, sizeof(curve2d_mesh_data_t::line_vertex_t), 0); + curve_vao->bindVBO(vb, 1, 2, GL_FLOAT, sizeof(curve2d_mesh_data_t::line_vertex_t), sizeof(vec3)); + } + + square_shader = shader_t::make_unique(shader_2d_textured_vert, shader_2d_textured_frag); + square_shader->bindAttribute(0, "vertex"); + square_shader->bindAttribute(1, "uv_in"); + + point_shader = shader_t::make_unique(shader_2d_textured_vert, shader_2d_textured_cirlce_frag); + point_shader->bindAttribute(0, "vertex"); + point_shader->bindAttribute(1, "uv_in"); + + engine->create(); + } + + void batch_renderer_2d::drawRectangle(const rectangle2d_t& rectangle, const std::string_view texture, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_rectangles, render_info_t::make_info(texture), {-z_index, rectangle}); + } + + void batch_renderer_2d::drawRectangle(const rectangle2d_t& rectangle, const vec4& color, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_rectangles, render_info_t::make_info(color), {-z_index, rectangle}); + } + + void batch_renderer_2d::drawRectangle(const rectangle2d_t& rectangle, const render_info_t& draw_info, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_rectangles, draw_info, {-z_index, rectangle}); + } + + void batch_renderer_2d::drawLine(const line2d_t& line, const std::string_view texture, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_lines, render_info_t::make_info(texture), {-z_index, line}); + } + + void batch_renderer_2d::drawLine(const line2d_t& line, const vec4& color, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_lines, render_info_t::make_info(color), {-z_index, line}); + } + + void batch_renderer_2d::drawLine(const line2d_t& line, const render_info_t& draw_info, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_lines, draw_info, {-z_index, line}); + } + + void batch_renderer_2d::drawPoint(const point2d_t& point, const std::string_view texture, const f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_points, render_info_t::make_info(texture), {-z_index, point}); + } + + void batch_renderer_2d::drawPoint(const point2d_t& point, const vec4& color, f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_points, render_info_t::make_info(color), {-z_index, point}); + } + + void batch_renderer_2d::drawPoint(const point2d_t& point, const render_info_t& draw_info, f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_points, draw_info, {-z_index, point}); + } + + void batch_renderer_2d::drawCurve(const curve2d_mesh_data_t& curve, std::string_view texture, f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_curves, render_info_t::make_info(texture), {-z_index, curve}); + } + + void batch_renderer_2d::drawCurve(const curve2d_mesh_data_t& curve, const vec4& color, f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_curves, render_info_t::make_info(color), {-z_index, curve}); + } + + void batch_renderer_2d::drawCurve(const curve2d_mesh_data_t& curve, const render_info_t& draw_info, f32 z_index) + { + update_z_index(z_index); + insert_obj(draw.complex_curves, draw_info, {-z_index, curve}); + } + + void batch_renderer_2d::cleanup() + { + engine->cleanup(); + engine = nullptr; + square_vao = nullptr; + line_vao = nullptr; + curve_vao = nullptr; + square_shader = nullptr; + point_shader = nullptr; + } + + void batch_renderer_2d::render(i32, i32, const bool transparency, const bool postprocessing) + { + //draw_buffer.bind(); + //draw_buffer.updateBuffersStorage(width, height); + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (transparency) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + glEnable(GL_DEPTH_TEST); + glActiveTexture(GL_TEXTURE0); + + if (engine && postprocessing) + engine->bind(); + draw_objects(); + + glDisable(GL_DEPTH_TEST); + if (transparency) + glDisable(GL_BLEND); + + if (engine && postprocessing) + engine->render(); + render_reset(); + } + + void batch_renderer_2d::draw_objects() + { + pre_reset(); + const f32 denominator = 1.0f / (draw.z_max - draw.z_min); + + glClear(GL_STENCIL_BUFFER_BIT); + draw_points(denominator); + draw_rectangles(denominator); + draw_lines(denominator); + draw_curves(denominator); + post_reset(); + } + + void batch_renderer_2d::render_reset() + { + draw.z_min = std::numeric_limits::max(); + draw.z_max = std::numeric_limits::min(); + } + + void batch_renderer_2d::pre_reset() + { + draw.draw_count = 0; + } + + void batch_renderer_2d::post_reset() + {} + + void batch_renderer_2d::draw_points(const f32 denominator) + { + point_shader->bind(); + square_vao->bind(); + for (auto& [texture, objects] : draw.complex_points) + { + // resource manager handles the check for empty string + if (auto val = resources.get(texture)) + val.value()->bind(); + for (auto& [render_info, object] : objects) + { + auto& [z_index, point] = object; + + mat4x4 model; + model.translate(point.pos.x(), point.pos.y(), 0.0f); + model.scale(point.scale, point.scale, 1.0); + point_shader->setVec4("color", render_info.color); + point_shader->setVec4("use_texture", render_info.blend); + + //point_shader->setVec4("outline_color", render_info.outline_color); + point_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); + point_shader->setMatrix("model", model); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + draw.draw_count++; + } + objects.clear(); + } + } + + void batch_renderer_2d::draw_lines(const f32 denominator) + { + mat4x4 empty_model; + square_shader->setMatrix("model", empty_model); + line_vao->bind(); + auto& buf = line_vao->getBuffer(0); + buf.bind(); + for (auto& [texture, objects] : draw.complex_lines) + { + // resource manager handles the check for empty string + if (auto val = resources.get(texture)) + val.value()->bind(); + for (auto& [render_info, object] : objects) + { + auto& [z_index, line] = object; + + float thickness = line.thickness; + square_shader->setVec4("color", render_info.color); + square_shader->setVec4("use_texture", render_info.blend); + + //square_shader->setVec4("outline_color", render_info.outline_color); + square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); + + // 0, 1 (top right) + // 5, 6 (bottom right) + // 10, 11 (bottom left) + // 15, 16 (top left) + vec2 dir = (line.p1 - line.p2).normalize(); + vec2 right = {dir.y(), -dir.x()}; + vec2 left = {-dir.y(), dir.x()}; + + auto bottom_left = line.p1 + left * thickness; + auto bottom_right = line.p1 + right * thickness; + + auto top_left = line.p2 + left * thickness; + auto top_right = line.p2 + right * 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); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + draw.draw_count++; + } + objects.clear(); + } + } + + void batch_renderer_2d::draw_curves(const f32 denominator) + { + curve_vao->bind(); + mat4x4 empty_model; + square_shader->setMatrix("model", empty_model); + for (auto& [texture, objects] : draw.complex_curves) + { + if (auto val = resources.get(texture)) + val.value()->bind(); + for (auto& [render_info, object] : objects) + { + auto& [z_index, curve] = object; + square_shader->setVec4("color", render_info.color); + square_shader->setVec4("use_texture", render_info.blend); + square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); + + const auto count = curve.populate_vertex_array(*curve_vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, static_cast(count)); + + draw.draw_count++; + } + } + } + + void batch_renderer_2d::draw_rectangles(const f32 denominator) + { + square_shader->bind(); + square_vao->bind(); + for (auto& [texture, objects] : draw.complex_rectangles) + { + // resource manager handles the check for empty string + if (auto val = resources.get(texture)) + val.value()->bind(); + for (auto& [render_info, object] : objects) + { + auto& [z_index, rect] = object; + + mat4x4 model; + model.translate(rect.pos); + model.scale(rect.size); + square_shader->setVec4("color", render_info.color); + square_shader->setVec4("use_texture", render_info.blend); + if (rect.rotation != 0) + model.rotateZ(toRadians(rect.rotation)); + + //square_shader->setVec4("outline_color", render_info.outline_color); + square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); + square_shader->setMatrix("model", model); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + draw.draw_count++; + } + objects.clear(); + } + } } diff --git a/src/blt/gfx/window.cpp b/src/blt/gfx/window.cpp index f9e5729..24b9cfb 100644 --- a/src/blt/gfx/window.cpp +++ b/src/blt/gfx/window.cpp @@ -35,11 +35,11 @@ void gl_error_callback(GLenum source, GLenum type, GLuint id, GLenum severity, G return; if (type == GL_DEBUG_TYPE_ERROR) { - BLT_ERROR("[OpenGL Error] message = '%s', type = 0x%x, severity = 0x%x, source = 0x%x, id = %d", + BLT_ERROR("[OpenGL Error] message = '{}', type = 0x{:x}, severity = 0x{:x}, source = 0x{:x}, id = {}", message, type, severity, source, id); } else { - BLT_WARN("[OpenGL Error] message = '%s', type = 0x%x, severity = 0x%x, source = 0x%x, id = %d", + BLT_WARN("[OpenGL Error] message = '{}', type = 0x{:x}, severity = 0x{:x}, source = 0x{:x}, id = {}", message, type, severity, source, id); } }