/* * * 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; float rotation = 0; rectangle2d_t(float x, float y, float width, float height, float rotation): pos(x, y), size(width, height), rotation(rotation) {} rectangle2d_t(float x, float y, float width, float height): pos(x, y), size(width, height) {} rectangle2d_t(blt::vec2f pos, blt::vec2f size, float 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; float thickness = 1; line2d_t(float px1, float py1, float px2, float py2): p1(px1, py1), p2(px2, py2) {} line2d_t(blt::vec2f p1, blt::vec2f p2): p1(p1), p2(p2) {} line2d_t(float px1, float py1, float px2, float py2, float thickness): p1(px1, py1), p2(px2, py2), thickness(thickness) {} line2d_t(blt::vec2f p1, blt::vec2f p2, float 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::vec4 color; // how much to blend the texture into the color? note blending is always additive! blt::vec4 blend; }; 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()); } }; struct render_object_t { std::string texture; blt::vec4 color; blt::vec4 texture_blend_factor; std::variant 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; // z-index -> draw object std::map> draw_objects; size_t draw_count_ = 0; public: explicit batch_renderer_2d(resource_manager& resources): resources(resources) {} void create(); inline void drawRectangle(std::string_view texture, const rectangle2d_t& rectangle, blt::i32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 1}; const static blt::vec4 full{1, 1, 1, 1}; draw_objects[z_index].push_back(render_object_t{std::string(texture), empty, full, rectangle}); } inline void drawRectangle(const blt::vec4& color, const rectangle2d_t& rectangle, blt::i32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 0}; draw_objects[z_index].push_back(render_object_t{"", color, empty, rectangle}); } inline void drawRectangle(const draw_state& draw_info, const rectangle2d_t& rectangle, blt::i32 z_index = 0) { draw_objects[z_index].push_back(render_object_t{draw_info.texture_name, draw_info.color, draw_info.blend, rectangle}); } inline void drawLine(std::string_view texture, const line2d_t& line, blt::i32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 1}; const static blt::vec4 full{1, 1, 1, 1}; draw_objects[z_index].push_back(render_object_t{std::string(texture), empty, full, line}); } inline void drawLine(const blt::vec4& color, const line2d_t& line, blt::i32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 0}; draw_objects[z_index].push_back(render_object_t{"", color, empty, line}); } inline void drawLine(const draw_state& draw_info, const line2d_t& line, blt::i32 z_index = 0) { draw_objects[z_index].push_back(render_object_t{draw_info.texture_name, draw_info.color, draw_info.blend, line}); } inline void drawPoint(std::string_view texture, const point2d_t& point, blt::i32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 1}; const static blt::vec4 full{1, 1, 1, 1}; draw_objects[z_index].push_back(render_object_t{std::string(texture), empty, full, point}); } inline void drawPoint(const blt::vec4& color, const point2d_t& point, blt::i32 z_index = 0) { const static blt::vec4 empty{0, 0, 0, 0}; draw_objects[z_index].push_back(render_object_t{"", color, empty, point}); } inline void drawPoint(const draw_state& draw_info, const point2d_t& point, blt::i32 z_index = 0) { draw_objects[z_index].push_back(render_object_t{draw_info.texture_name, draw_info.color, draw_info.blend, point}); } template inline void drawRectangle(const T& render_info, P... p) { drawRectangle(render_info, {p...}); } template inline void drawPoint(const T& render_info, P... p) { drawPoint(render_info, {p...}); } template inline void drawLine(const T& render_info, P... p) { drawLine(render_info, {p...}); } template inline void drawRectangle(const T& render_info, blt::i32 z_index, P... p) { drawRectangle(render_info, {p...}, z_index); } template inline void drawPoint(const T& render_info, blt::i32 z_index, P... p) { drawPoint(render_info, {p...}, z_index); } template inline void drawLine(const T& render_info, blt::i32 z_index, P... p) { drawLine(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