diff --git a/libs/gfx/include/psemek/gfx/painter.hpp b/libs/gfx/include/psemek/gfx/painter.hpp new file mode 100644 index 00000000..a8b8cb83 --- /dev/null +++ b/libs/gfx/include/psemek/gfx/painter.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace psemek::gfx +{ + + struct painter + { + using color = color_rgba; + + painter(); + ~painter(); + + // 2D + void quad(geom::point const & center, float width, color const & c); + void circle(geom::point const & center, float radius, color const & c); + void line(geom::point const & p0, geom::point const & p1, float width, color const & c, bool smooth = true); + + // Should be called on each frame + void render(geom::matrix const & transform); + + private: + struct impl; + std::unique_ptr pimpl_; + struct impl & impl() { return *pimpl_; } + }; + +} diff --git a/libs/gfx/source/gfx/painter.cpp b/libs/gfx/source/gfx/painter.cpp new file mode 100644 index 00000000..c1b88f65 --- /dev/null +++ b/libs/gfx/source/gfx/painter.cpp @@ -0,0 +1,142 @@ +#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); + } + +} diff --git a/todo.md b/todo.md index 2374ebda..815bc5e2 100644 --- a/todo.md +++ b/todo.md @@ -1,6 +1,5 @@ * Design affine transforms in geom & use them instead of matrices when appropriate * Implement pixmap font rendering -* Create a simple generic primive painter * Design ui system * Add platform deployment tools * Add color utilities