diff --git a/libs/gfx/include/psemek/gfx/painter.hpp b/libs/gfx/include/psemek/gfx/painter.hpp index 6b004fe1..5c8180ab 100644 --- a/libs/gfx/include/psemek/gfx/painter.hpp +++ b/libs/gfx/include/psemek/gfx/painter.hpp @@ -53,9 +53,14 @@ namespace psemek::gfx 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); + // 2D text geom::vector text_size(std::string_view str, font f = font::font_9x12, float scale = 1.f); void text(geom::point const & p, std::string_view str, text_options const & opts); + // 3D + void axes(geom::point const & p, float length, float width); + void sphere(geom::point const & p, float radius, color const & c, int quality = 6); + // Should be called on each frame void render(geom::matrix const & transform); diff --git a/libs/gfx/source/painter.cpp b/libs/gfx/source/painter.cpp index 60d9c0a6..2d4dfcb5 100644 --- a/libs/gfx/source/painter.cpp +++ b/libs/gfx/source/painter.cpp @@ -292,6 +292,131 @@ namespace psemek::gfx } } + void painter::axes(geom::point const & p, float length, float width) + { + geom::vector const d[3] + { + { 1.f, 0.f, 0.f }, + { 0.f, 1.f, 0.f }, + { 0.f, 0.f, 1.f }, + }; + + color const c[3] + { + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, + }; + + for (int i = 0; i < 3; ++i) + { + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + std::uint32_t const base = impl().vertices.size(); + + impl().vertices.push_back({p + d[i] * length, c[i]}); + impl().vertices.push_back({p + d[j] * width, c[i]}); + impl().vertices.push_back({p + d[k] * width, c[i]}); + impl().vertices.push_back({p - d[j] * width, c[i]}); + impl().vertices.push_back({p - d[k] * width, c[i]}); + + impl().indices.push_back(base + 0); + impl().indices.push_back(base + 1); + impl().indices.push_back(base + 2); + impl().indices.push_back(base + 0); + impl().indices.push_back(base + 2); + impl().indices.push_back(base + 3); + impl().indices.push_back(base + 0); + impl().indices.push_back(base + 3); + impl().indices.push_back(base + 4); + impl().indices.push_back(base + 0); + impl().indices.push_back(base + 4); + impl().indices.push_back(base + 1); + } + + std::uint32_t const base = impl().vertices.size(); + + color const white {255, 255, 255, 255}; + + impl().vertices.push_back({p - d[0] * width, white}); + impl().vertices.push_back({p - d[1] * width, white}); + impl().vertices.push_back({p - d[2] * width, white}); + + impl().indices.push_back(base + 0); + impl().indices.push_back(base + 2); + impl().indices.push_back(base + 1); + } + + void painter::sphere(geom::point const & p, float radius, color const & c, int quality) + { + std::uint32_t const base = impl().vertices.size(); + + impl().vertices.push_back({p - geom::vector{0.f, 0.f, radius}, c}); + impl().vertices.push_back({p + geom::vector{0.f, 0.f, radius}, c}); + + auto const ray = [](float ax, float ay) + { + return geom::vector{std::cos(ax) * std::cos(ay), std::sin(ax) * std::cos(ay), std::sin(ay)}; + }; + + for (int y = - quality + 1; y < quality; ++y) + { + for (int x = 0; x < quality * 4; ++x) + { + float ax = (x * geom::pi) / quality / 2.f; + float ay = (y * geom::pi) / quality / 2.f; + + impl().vertices.push_back({p + radius * ray(ax, ay), c}); + } + } + + auto const index = [quality, base](int x, int y) + { + if (y == -quality) + return base; + if (y == quality) + return base + 1; + + return static_cast(2 + (y + quality - 1) * 4 * quality + x); + }; + + for (int x = 0; x < quality * 4; ++x) + { + int xx = (x + 1) % (quality * 4); + + impl().indices.push_back(index(x, - quality + 1)); + impl().indices.push_back(index(x, - quality)); + impl().indices.push_back(index(xx, - quality + 1)); + } + + for (int y = - quality + 1; y + 1 < quality; ++y) + { + for (int x = 0; x < quality * 4; ++x) + { + int yy = y + 1; + int xx = (x + 1) % (quality * 4); + + impl().indices.push_back(index(x, y)); + impl().indices.push_back(index(xx, y)); + impl().indices.push_back(index(xx, yy)); + + impl().indices.push_back(index(x, y)); + impl().indices.push_back(index(xx, yy)); + impl().indices.push_back(index(x, yy)); + } + } + + for (int x = 0; x < quality * 4; ++x) + { + int xx = (x + 1) % (quality * 4); + + impl().indices.push_back(index(x, quality - 1)); + impl().indices.push_back(index(xx, quality - 1)); + impl().indices.push_back(index(x, quality)); + } + } + void painter::render(geom::matrix const & transform) { impl().mesh.load(impl().vertices, impl().indices, gl::STREAM_DRAW); diff --git a/todo.md b/todo.md index 815bc5e2..64efc6e5 100644 --- a/todo.md +++ b/todo.md @@ -3,3 +3,4 @@ * Design ui system * Add platform deployment tools * Add color utilities +* gfx::painter should support normals & minimal lighting (maybe split into separate 2D & 3D painters?)