#include #include #include #include static const char vertex_source[] = R"(#version 330 uniform mat4 u_transform; layout (location = 0) in vec4 in_position; layout (location = 1) in vec4 in_color; out vec4 color; void main() { gl_Position = u_transform * in_position; color = in_color; } )"; static const char fragment_source[] = R"(#version 330 in vec4 color; out vec4 out_color; void main() { out_color = color; } )"; namespace psemek::gfx { struct painter::impl { struct vertex { geom::point position; color_rgba color; }; gfx::program program{vertex_source, fragment_source}; gfx::indexed_mesh mesh; std::vector vertices; std::vector indices; impl() { mesh.setup, gfx::normalized>(); } }; painter::painter() : pimpl_{std::make_unique()} {} painter::~painter() = default; void painter::quad(geom::point const & p, float width, color const & c) { std::uint32_t const base = impl().vertices.size(); float const r = width / 2.f; impl().vertices.push_back({{p[0] - r, p[1] - r, 0.f}, c}); impl().vertices.push_back({{p[0] + r, p[1] - r, 0.f}, c}); impl().vertices.push_back({{p[0] - r, p[1] + r, 0.f}, c}); impl().vertices.push_back({{p[0] + r, p[1] + r, 0.f}, c}); impl().indices.push_back(base + 0); impl().indices.push_back(base + 1); impl().indices.push_back(base + 3); impl().indices.push_back(base + 0); impl().indices.push_back(base + 3); impl().indices.push_back(base + 2); } void painter::circle(geom::point const & p, float r, color const & c) { std::uint32_t const base = impl().vertices.size(); int const quality = 24; impl().vertices.push_back({{p[0], p[1], 0.f}, c}); for (int i = 0; i < quality; ++i) { float const a = (geom::pi * 2.f * i) / quality; impl().vertices.push_back({{p[0] + r * std::cos(a), p[1] + r * std::sin(a), 0.f}, c}); } for (int i = 0; i < quality; ++i) { impl().indices.push_back(base); impl().indices.push_back(base + 1 + i); impl().indices.push_back(base + 1 + ((i + 1) % quality)); } } void painter::line(geom::point const & p0, geom::point const & p1, float width, color const & c, bool smooth) { std::uint32_t const base = impl().vertices.size(); float const r = width / 2.f; auto const d = geom::normalized(p1 - p0); geom::vector const o { -d[1], d[0] }; impl().vertices.push_back({{p0[0] + r * o[0], p0[1] + r * o[1], 0.f}, c}); impl().vertices.push_back({{p0[0] - r * o[0], p0[1] - r * o[1], 0.f}, c}); impl().vertices.push_back({{p1[0] + r * o[0], p1[1] + r * o[1], 0.f}, c}); impl().vertices.push_back({{p1[0] - r * o[0], p1[1] - r * o[1], 0.f}, c}); impl().indices.push_back(base + 0); impl().indices.push_back(base + 1); impl().indices.push_back(base + 3); impl().indices.push_back(base + 0); impl().indices.push_back(base + 3); impl().indices.push_back(base + 2); if (smooth) { circle(p0, r, c); circle(p1, r, c); } } void painter::render(geom::matrix const & transform) { impl().mesh.load(impl().vertices, impl().indices, gl::STREAM_DRAW); impl().vertices.clear(); impl().indices.clear(); impl().program.bind(); impl().program["u_transform"] = transform; impl().mesh.draw(gl::TRIANGLES); } }