/* * * 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/renderer/resource_manager.h" #include #include #include #include #include #include #include #include namespace blt::gfx { struct rectangle2d_t { blt::vec2f pos, size; blt::f32 rotation = 0; rectangle2d_t(blt::f32 x, blt::f32 y, blt::f32 width, blt::f32 height, blt::f32 rotation): pos(x, y), size(width, height), rotation(rotation) {} rectangle2d_t(blt::f32 x, blt::f32 y, blt::f32 width, blt::f32 height): pos(x, y), size(width, height) {} rectangle2d_t(blt::vec2f pos, blt::vec2f size, blt::f32 rotation): pos(pos), size(size), rotation(rotation) {} rectangle2d_t(blt::vec2f pos, blt::vec2f size): pos(pos), size(size) {} }; struct line2d_t { blt::vec2f p1; blt::vec2f p2; blt::f32 thickness = 1; line2d_t(blt::f32 px1, blt::f32 py1, blt::f32 px2, blt::f32 py2): p1(px1, py1), p2(px2, py2) {} line2d_t(blt::vec2f p1, blt::vec2f p2): p1(p1), p2(p2) {} line2d_t(blt::f32 px1, blt::f32 py1, blt::f32 px2, blt::f32 py2, blt::f32 thickness): p1(px1, py1), p2(px2, py2), thickness(thickness) {} line2d_t(blt::vec2f p1, blt::vec2f p2, blt::f32 thickness): p1(p1), p2(p2), thickness(thickness) {} }; struct point2d_t { blt::vec2f pos; float scale = 1; point2d_t(float x, float y, float scale): pos(x, y), scale(scale) {} point2d_t(float x, float y): pos(x, y) {} point2d_t(vec2f pos, float scale): pos(pos), scale(scale) {} explicit point2d_t(vec2f pos): pos(pos) {} }; struct draw_state { // texture to use std::string texture_name; // color to use blt::color4 color; // how much to blend the texture into the color? note blending is always additive! blt::color4 blend; // should we outline this object? bool outline; // what color should we outline with? blt::color4 outline_color; }; class batch_renderer_2d { private: struct vec_hash { std::size_t operator()(const blt::vec4& key) const { using namespace blt::mem; return type_cast(key.x()) ^ type_cast(key.y()) ^ type_cast(key.y()) ^ type_cast(key.z()); } }; template struct render_object_t { blt::f32 z_index; T obj; render_object_t(float z_index, 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; private: vertex_array* square_vao = nullptr; vertex_array* line_vao = nullptr; shader_t* square_shader = nullptr; shader_t* point_shader = nullptr; resource_manager& resources; // texture -> color -> blend factor -> list of rectangles blt::hashmap_t, vec_hash>, vec_hash>> complex_rectangles; blt::hashmap_t, vec_hash>, vec_hash>> complex_points; blt::hashmap_t, vec_hash>, vec_hash>> complex_lines; size_t draw_count_ = 0; public: explicit batch_renderer_2d(resource_manager& resources): resources(resources) {} void create(); inline void drawRectangleInternal(std::string_view texture, const rectangle2d_t& rectangle, blt::f32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 1}; const static blt::vec4 full{1, 1, 1, 1}; complex_rectangles[texture][empty][full].emplace_back(-z_index, rectangle); } inline void drawRectangleInternal(const blt::vec4& color, const rectangle2d_t& rectangle, blt::f32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 0}; complex_rectangles[""][color][empty].emplace_back(-z_index, rectangle); } inline void drawRectangleInternal(const draw_state& draw_info, const rectangle2d_t& rectangle, blt::f32 z_index = 0) { complex_rectangles[draw_info.texture_name][draw_info.color][draw_info.blend].emplace_back(-z_index, rectangle); } inline void drawLineInternal(std::string_view texture, const line2d_t& line, blt::f32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 1}; const static blt::vec4 full{1, 1, 1, 1}; complex_lines[texture][empty][full].emplace_back(-z_index, line); } inline void drawLineInternal(const blt::vec4& color, const line2d_t& line, blt::f32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 0}; complex_lines[""][color][empty].emplace_back(-z_index, line); } inline void drawLineInternal(const draw_state& draw_info, const line2d_t& line, blt::f32 z_index = 0) { complex_lines[draw_info.texture_name][draw_info.color][draw_info.blend].emplace_back(-z_index, line); } inline void drawPointInternal(std::string_view texture, const point2d_t& point, blt::f32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 1}; const static blt::vec4 full{1, 1, 1, 1}; complex_points[texture][empty][full].emplace_back(-z_index, point); } inline void drawPointInternal(const blt::vec4& color, const point2d_t& point, blt::f32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 0}; complex_points[""][color][empty].emplace_back(-z_index, point); } inline void drawPointInternal(const draw_state& draw_info, const point2d_t& point, blt::f32 z_index = 0) { complex_points[draw_info.texture_name][draw_info.color][draw_info.blend].emplace_back(-z_index, point); } template inline void drawRectangle(const T& render_info, P... p) { drawRectangleInternal(render_info, {p...}); } template inline void drawPoint(const T& render_info, P... p) { drawPointInternal(render_info, {p...}); } template inline void drawLine(const T& render_info, P... p) { drawLineInternal(render_info, {p...}); } template inline void drawRectangle(const T& render_info, blt::f32 z_index, P... p) { drawRectangleInternal(render_info, {p...}, z_index); } template inline void drawPoint(const T& render_info, blt::f32 z_index, P... p) { drawPointInternal(render_info, {p...}, z_index); } template inline void drawLine(const T& render_info, blt::f32 z_index, P... p) { drawLineInternal(render_info, {p...}, z_index); } void render(bool transparency = true); void cleanup(); [[nodiscard]] inline size_t draw_count() const { return draw_count_; } }; } #endif //BLT_WITH_GRAPHICS_BATCH_2D_RENDERER_H