/* * * 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 . */ #ifndef BLT_WITH_GRAPHICS_BATCH_2D_RENDERER_H #define BLT_WITH_GRAPHICS_BATCH_2D_RENDERER_H #include "blt/gfx/model.h" #include "blt/gfx/shader.h" #include "blt/gfx/framebuffer.h" #include "blt/gfx/renderer/resource_manager.h" #include "blt/gfx/renderer/postprocess.h" #include #include #include #include #include namespace blt::gfx { struct rectangle2d_t { vec2f pos, size; f32 rotation = 0; rectangle2d_t(const f32 x, const f32 y, const f32 width, const f32 height, const f32 rotation): pos(x, y), size(width, height), rotation(rotation) {} rectangle2d_t(const f32 x, const f32 y, const f32 width, const f32 height): pos(x, y), size(width, height) {} rectangle2d_t(const vec2f pos, const vec2f size, const f32 rotation): pos(pos), size(size), rotation(rotation) {} rectangle2d_t(const vec2f pos, const vec2f size): pos(pos), size(size) {} }; struct line2d_t { vec2f p1; vec2f p2; f32 thickness = 1; line2d_t(const f32 px1, const f32 py1, const f32 px2, const f32 py2): p1(px1, py1), p2(px2, py2) {} line2d_t(const vec2f p1, const vec2f p2): p1(p1), p2(p2) {} line2d_t(const f32 px1, const f32 py1, const f32 px2, const f32 py2, const f32 thickness): p1(px1, py1), p2(px2, py2), thickness(thickness) {} line2d_t(const vec2f p1, const vec2f p2, const f32 thickness): p1(p1), p2(p2), thickness(thickness) {} }; struct point2d_t { vec2f pos; float scale = 1; point2d_t(const float x, const float y, const float scale): pos(x, y), scale(scale) {} point2d_t(const float x, const float y): pos(x, y) {} point2d_t(const vec2f pos, const float scale): pos(pos), scale(scale) {} explicit point2d_t(const vec2f pos): pos(pos) {} }; struct render_info_t { private: constexpr static color4 disabled = color4(0, 0, 0, 0); constexpr static vec4 empty{0, 0, 0, 1}; constexpr static vec4 full{1, 1, 1, 1}; render_info_t(const std::string_view texture, color4 color, color4 blend): texture_name(texture), color(color), blend(blend) {} public: // texture to use std::string texture_name; // color to use color4 color; // how much to blend the texture into the color? note blending is always additive! color4 blend; // should we outline this object? bool outline = false; // what color should we outline with? color4 outline_color; render_info_t() = default; static render_info_t make_info(const std::string_view texture, const color4 outline = disabled) { render_info_t info{texture, empty, full}; if (outline != disabled) { info.outline = true; info.outline_color = outline; } return info; } static render_info_t make_info(const color4 color, const color4 outline = disabled) { render_info_t info{"", color, empty}; if (outline != disabled) { info.outline = true; info.outline_color = outline; } return info; } static render_info_t make_info(const std::string_view texture, const color4 color, const color4 blend, const color4 outline = disabled) { render_info_t info{texture, color, blend}; if (outline != disabled) { info.outline = true; info.outline_color = outline; } return info; } }; class batch_renderer_2d { private: std::unique_ptr engine; template struct render_object_t { f32 z_index; T obj; render_object_t(const float 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; template using object_container = hashmap_t>>; private: std::unique_ptr square_vao = nullptr; std::unique_ptr line_vao = nullptr; std::unique_ptr square_shader = nullptr; std::unique_ptr point_shader = nullptr; resource_manager& resources; matrix_state_manager& state; // texture name -> draw info struct { object_container complex_rectangles; object_container complex_points; object_container complex_lines; size_t draw_count = 0; f32 z_min = std::numeric_limits::max(); f32 z_max = std::numeric_limits::min(); } draw; template static void insert_obj(object_container& map, const render_info_t& info, const E& obj) { map[info.texture_name].emplace_back(info, obj); } // called at the end of the render function void render_reset(); // called before invocation of draw_objects void pre_reset(); // called after draw_objects() void post_reset(); void draw_objects(); inline void update_z_index(f32 z_index) { draw.z_min = std::min(draw.z_min, -z_index); draw.z_max = std::max(draw.z_max, -z_index); } public: explicit batch_renderer_2d(resource_manager& resources, matrix_state_manager& state): resources(resources), state(state) { engine = pp_engine_t::make_multi_pp(std::make_unique()); } void create(); void drawRectangleInternal(std::string_view texture, const rectangle2d_t& rectangle, f32 z_index = 0); void drawRectangleInternal(const vec4& color, const rectangle2d_t& rectangle, f32 z_index = 0); void drawRectangleInternal(const render_info_t& draw_info, const rectangle2d_t& rectangle, f32 z_index = 0); void drawLineInternal(std::string_view texture, const line2d_t& line, f32 z_index = 0); void drawLineInternal(const vec4& color, const line2d_t& line, f32 z_index = 0); void drawLineInternal(const render_info_t& draw_info, const line2d_t& line, f32 z_index = 0); void drawPointInternal(std::string_view texture, const point2d_t& point, f32 z_index = 0); void drawPointInternal(const vec4& color, const point2d_t& point, f32 z_index = 0); void drawPointInternal(const render_info_t& draw_info, const point2d_t& point, f32 z_index = 0); template void drawRectangle(const T& render_info, P... p) { drawRectangleInternal(render_info, {p...}); } template void drawPoint(const T& render_info, P... p) { drawPointInternal(render_info, {p...}); } template void drawLine(const T& render_info, P... p) { drawLineInternal(render_info, {p...}); } 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 cleanup(); [[nodiscard]] size_t draw_count() const { return draw.draw_count; } }; } #endif //BLT_WITH_GRAPHICS_BATCH_2D_RENDERER_H