Support stencils in ui::painter
This commit is contained in:
parent
015307cbec
commit
b298859f1e
3 changed files with 147 additions and 2 deletions
|
|
@ -17,6 +17,10 @@ namespace psemek::ui
|
|||
virtual void draw_image(geom::box<float, 2> const & rect, gfx::texture_2d const & tex, gfx::color_rgba const & color = {0, 0, 0, 0}) = 0;
|
||||
virtual void draw_subimage(geom::box<float, 2> const & rect, gfx::texture_2d const & tex, geom::box<float, 2> const & part, gfx::color_rgba const & color = {0, 0, 0, 0}) = 0;
|
||||
|
||||
virtual void begin_stencil() = 0;
|
||||
virtual void commit_stencil() = 0;
|
||||
virtual void end_stencil() = 0;
|
||||
|
||||
virtual ~painter() {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ namespace psemek::ui
|
|||
void draw_image(geom::box<float, 2> const & rect, gfx::texture_2d const & tex, gfx::color_rgba const & color) override;
|
||||
void draw_subimage(geom::box<float, 2> const & rect, gfx::texture_2d const & tex, geom::box<float, 2> const & part, gfx::color_rgba const & color = {0, 0, 0, 0}) override;
|
||||
|
||||
void begin_stencil() override;
|
||||
void commit_stencil() override;
|
||||
void end_stencil() override;
|
||||
|
||||
void render(geom::matrix<float, 4, 4> const & transform);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -183,6 +183,23 @@ void main()
|
|||
return b1.texture == b2.texture;
|
||||
}
|
||||
|
||||
struct stencil_batch
|
||||
{
|
||||
std::optional<bool> enable_stencil;
|
||||
std::optional<bool> enable_depth;
|
||||
std::optional<bool> enable_color;
|
||||
|
||||
GLenum sfail, dfail, dpass;
|
||||
GLenum func;
|
||||
GLint ref;
|
||||
GLuint mask;
|
||||
};
|
||||
|
||||
bool operator == (stencil_batch const &, stencil_batch const &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct painter_impl::impl
|
||||
|
|
@ -198,7 +215,12 @@ void main()
|
|||
gfx::program textured_program;
|
||||
gfx::mesh textured_mesh;
|
||||
|
||||
std::vector<std::variant<colored_batch, bitmap_text_batch, textured_batch>> batches;
|
||||
int stencil_level = 0;
|
||||
static constexpr int max_stencil_level = 8;
|
||||
|
||||
geom::box<float, 2> draw_bbox;
|
||||
|
||||
std::vector<std::variant<colored_batch, bitmap_text_batch, textured_batch, stencil_batch>> batches;
|
||||
|
||||
template <typename Batch>
|
||||
Batch & batch(Batch && n)
|
||||
|
|
@ -251,6 +273,8 @@ void main()
|
|||
batch.vertices.push_back({rect.corner(1.f, 1.f), depth, color});
|
||||
|
||||
batch.indices.insert(batch.indices.end(), {base + 0, base + 1, base + 2, base + 2, base + 1, base + 3});
|
||||
|
||||
impl().draw_bbox |= rect;
|
||||
}
|
||||
|
||||
void painter_impl::draw_triangle(geom::triangle<geom::point<float, 2>> const & tri, gfx::color_rgba const & color)
|
||||
|
|
@ -265,6 +289,10 @@ void main()
|
|||
batch.vertices.push_back({tri[2], depth, color});
|
||||
|
||||
batch.indices.insert(batch.indices.end(), {base + 0, base + 1, base + 2});
|
||||
|
||||
impl().draw_bbox |= tri[0];
|
||||
impl().draw_bbox |= tri[1];
|
||||
impl().draw_bbox |= tri[2];
|
||||
}
|
||||
|
||||
void painter_impl::draw_glyph(font const & f, char32_t c, geom::box<float, 2> const & rect, gfx::color_rgba const & color)
|
||||
|
|
@ -301,6 +329,8 @@ void main()
|
|||
batch.vertices.push_back({round(rect.corner(1.f, 1.f)), depth, color, tc(1.f, 1.f)});
|
||||
|
||||
batch.indices.insert(batch.indices.end(), {base + 0, base + 1, base + 2, base + 2, base + 1, base + 3});
|
||||
|
||||
impl().draw_bbox |= rect;
|
||||
}
|
||||
|
||||
void painter_impl::draw_image(geom::box<float, 2> const & rect, gfx::texture_2d const & tex, gfx::color_rgba const & color)
|
||||
|
|
@ -324,6 +354,82 @@ void main()
|
|||
batch.vertices.push_back({rect.corner(1.f, 1.f), depth, color, part.corner(1.f, 1.f)});
|
||||
|
||||
batch.indices.insert(batch.indices.end(), {base + 0, base + 1, base + 2, base + 2, base + 1, base + 3});
|
||||
|
||||
impl().draw_bbox |= rect;
|
||||
}
|
||||
|
||||
void painter_impl::begin_stencil()
|
||||
{
|
||||
if (impl().stencil_level == impl().max_stencil_level)
|
||||
throw std::runtime_error("Only 8 recursive stencil levels are supported");
|
||||
|
||||
auto & batch = impl().batch<stencil_batch>({});
|
||||
if (impl().stencil_level == 0)
|
||||
batch.enable_stencil = true;
|
||||
batch.enable_color = false;
|
||||
batch.enable_depth = false;
|
||||
|
||||
batch.func = gl::EQUAL;
|
||||
batch.ref = 2 * (1 << impl().stencil_level) - 1;
|
||||
batch.mask = (1 << impl().stencil_level) - 1;
|
||||
|
||||
batch.sfail = gl::KEEP;
|
||||
batch.dfail = gl::KEEP;
|
||||
batch.dpass = gl::REPLACE;
|
||||
|
||||
++impl().stencil_level;
|
||||
}
|
||||
|
||||
void painter_impl::commit_stencil()
|
||||
{
|
||||
auto & batch = impl().batch<stencil_batch>({});
|
||||
batch.enable_color = true;
|
||||
batch.enable_depth = true;
|
||||
|
||||
batch.func = gl::EQUAL;
|
||||
batch.ref = (1 << impl().stencil_level) - 1;
|
||||
batch.mask = (1 << impl().stencil_level) - 1;
|
||||
|
||||
batch.sfail = gl::KEEP;
|
||||
batch.dfail = gl::KEEP;
|
||||
batch.dpass = gl::KEEP;
|
||||
}
|
||||
|
||||
void painter_impl::end_stencil()
|
||||
{
|
||||
{
|
||||
auto & clear_batch = impl().batch<stencil_batch>({});
|
||||
clear_batch.enable_color = false;
|
||||
clear_batch.enable_depth = false;
|
||||
|
||||
clear_batch.func = gl::EQUAL;
|
||||
clear_batch.ref = (1 << impl().stencil_level) / 2 - 1;
|
||||
clear_batch.mask = (1 << impl().stencil_level) / 2 - 1;
|
||||
|
||||
clear_batch.sfail = gl::KEEP;
|
||||
clear_batch.dfail = gl::KEEP;
|
||||
clear_batch.dpass = gl::REPLACE;
|
||||
}
|
||||
|
||||
draw_rect(impl().draw_bbox, {0, 0, 0, 255});
|
||||
|
||||
{
|
||||
auto & batch = impl().batch<stencil_batch>({});
|
||||
if (impl().stencil_level == 1)
|
||||
batch.enable_stencil = false;
|
||||
batch.enable_color = true;
|
||||
batch.enable_depth = true;
|
||||
|
||||
batch.func = gl::NEVER;
|
||||
batch.ref = 0;
|
||||
batch.mask = 0;
|
||||
|
||||
batch.sfail = gl::KEEP;
|
||||
batch.dfail = gl::KEEP;
|
||||
batch.dpass = gl::KEEP;
|
||||
}
|
||||
|
||||
--impl().stencil_level;
|
||||
}
|
||||
|
||||
void painter_impl::render(geom::matrix<float, 4, 4> const & transform)
|
||||
|
|
@ -337,7 +443,8 @@ void main()
|
|||
|
||||
gl::ActiveTexture(gl::TEXTURE0);
|
||||
|
||||
auto batch_visitor = util::overload([&](colored_batch const & b){
|
||||
auto batch_visitor = util::overload(
|
||||
[&](colored_batch const & b){
|
||||
impl().colored_program.bind();
|
||||
impl().colored_mesh.load(b.vertices, b.indices, gl::TRIANGLES);
|
||||
impl().colored_mesh.draw();
|
||||
|
|
@ -357,6 +464,34 @@ void main()
|
|||
b.texture->bind();
|
||||
impl().textured_mesh.load(b.vertices, b.indices, gl::TRIANGLES);
|
||||
impl().textured_mesh.draw();
|
||||
},
|
||||
[&](stencil_batch const & b){
|
||||
if (b.enable_stencil)
|
||||
{
|
||||
if (*b.enable_stencil)
|
||||
gl::Enable(gl::STENCIL_TEST);
|
||||
else
|
||||
gl::Disable(gl::STENCIL_TEST);
|
||||
}
|
||||
|
||||
if (b.enable_depth)
|
||||
{
|
||||
if (*b.enable_depth)
|
||||
gl::DepthMask(gl::TRUE);
|
||||
else
|
||||
gl::DepthMask(gl::FALSE);
|
||||
}
|
||||
|
||||
if (b.enable_color)
|
||||
{
|
||||
if (*b.enable_color)
|
||||
gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
|
||||
else
|
||||
gl::ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
|
||||
}
|
||||
|
||||
gl::StencilOp(b.sfail, b.dfail, b.dpass);
|
||||
gl::StencilFunc(b.func, b.ref, b.mask);
|
||||
});
|
||||
|
||||
for (auto const & b : impl().batches)
|
||||
|
|
@ -364,6 +499,8 @@ void main()
|
|||
|
||||
impl().depth = 0;
|
||||
impl().batches.clear();
|
||||
impl().stencil_level = 0;
|
||||
impl().draw_bbox = {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue