diff --git a/src/graphics/renderTarget.cpp b/src/graphics/renderTarget.cpp index 50b8a795..e2abe768 100644 --- a/src/graphics/renderTarget.cpp +++ b/src/graphics/renderTarget.cpp @@ -10,6 +10,7 @@ #include "vectorUtils.h" #include #include +#include #include @@ -31,6 +32,7 @@ static std::vector lines_index_data; static std::vector points_vertex_data; static std::vector points_index_data; +static std::vector> clip_region_stack; struct ImageInfo { @@ -1255,7 +1257,6 @@ void RenderTarget::applyBuffer(sp::Texture* texture, std::vector &da void RenderTarget::finish(sp::Texture* texture) { - applyBuffer(texture, vertex_data, index_data, GL_TRIANGLES); applyBuffer(texture, lines_vertex_data, lines_index_data, GL_LINES); applyBuffer(texture, points_vertex_data, points_index_data, GL_POINTS); @@ -1274,9 +1275,67 @@ glm::ivec2 RenderTarget::getPhysicalSize() return physical_size; } -glm::ivec2 RenderTarget::virtualToPixelPosition(glm::vec2 v) +glm::ivec2 RenderTarget::virtualToPixelPosition(glm::vec2 virtual_position) +{ + return {virtual_position.x * physical_size.x / virtual_size.x, virtual_position.y * physical_size.y / virtual_size.y}; +} + +void RenderTarget::pushClipRegion(sp::Rect virtual_rect) +{ + // Flush geometry + finish(); + + glm::ivec2 px_min = virtualToPixelPosition(virtual_rect.position); + glm::ivec2 px_max = virtualToPixelPosition(virtual_rect.position + virtual_rect.size); + GLint px = px_min.x; + GLint py = px_min.y; + GLint pw = px_max.x - px_min.x; + GLint ph = px_max.y - px_min.y; + + // Convert coordinates to OpenGL bottom-left origin. + GLint py_gl = physical_size.y - (py + ph); + + // Add the clip region. + std::array new_rect = {px, py_gl, pw, ph}; + + // If nested, intersect this rect with the previous rect in the stack. + // Newly stacked rects shouldn't expand the active clipping region. + if (!clip_region_stack.empty()) + { + const auto& prev = clip_region_stack.back(); + GLint ix = std::max(new_rect[0], prev[0]); + GLint iy = std::max(new_rect[1], prev[1]); + GLint ix2 = std::min(new_rect[0] + new_rect[2], prev[0] + prev[2]); + GLint iy2 = std::min(new_rect[1] + new_rect[3], prev[1] + prev[3]); + new_rect[0] = ix; + new_rect[1] = iy; + new_rect[2] = std::max(0, ix2 - ix); + new_rect[3] = std::max(0, iy2 - iy); + } + + // Clip the render to the clip region. + clip_region_stack.push_back(new_rect); + glScissor(new_rect[0], new_rect[1], new_rect[2], new_rect[3]); + glEnable(GL_SCISSOR_TEST); +} + +void RenderTarget::popClipRegion() { - return {v.x * physical_size.x / virtual_size.x, v.y * physical_size.y / virtual_size.y}; + // Flush geometry + finish(); + + // Pop the top clip region. + if (!clip_region_stack.empty()) clip_region_stack.pop_back(); + + // If that was the last rect, or there weren't any, stop clipping. + // Otherwise, clip to the back rect. + if (clip_region_stack.empty()) + glDisable(GL_SCISSOR_TEST); + else + { + const auto& top = clip_region_stack.back(); + glScissor(top[0], top[1], top[2], top[3]); + } } } diff --git a/src/graphics/renderTarget.h b/src/graphics/renderTarget.h index 23db9868..cfe75645 100644 --- a/src/graphics/renderTarget.h +++ b/src/graphics/renderTarget.h @@ -1,5 +1,4 @@ -#ifndef SP_GRAPHICS_RENDERTARGET_H -#define SP_GRAPHICS_RENDERTARGET_H +#pragma once #include #include @@ -72,6 +71,14 @@ class RenderTarget : sp::NonCopyable void drawStretchedHVClipped(sp::Rect rect, sp::Rect clip_rect, float corner_size, std::string_view texture, glm::u8vec4 color={255,255,255,255}); void finish(); + + // Functions for using rect masks with glScissor to clip a render. + + // Stack nested rects to determine the clip region's bounds. + void pushClipRegion(sp::Rect virtual_rect); + // Remove a nested rect from the clip region. + void popClipRegion(); + struct VertexData { glm::vec2 position; @@ -91,5 +98,3 @@ class RenderTarget : sp::NonCopyable }; } - -#endif//SP_GRAPHICS_RENDERTARGET_H