I want my render class to support clipping. I would like to set different clip rect to different objects.
struct Rect
{
int x = 0, y = 0, w = 0, h = 0;
};
struct Vertex
{
int x = 0, y = 0;
uint color = 0xffffffff;
};
struct DrawCall
{
Rect clipRect = Rect(0);
uint elemCount = 0;
};
class Renderer
{
public:
Renderer() = deafault;
void Init();
void Prepare();
void DrawText(std::string text, int x, int y, uint color);
void PresentScene();
void UpdateClipRect();
void PushClipRect(const Rect& rect);
void PopClipRect();
std::vector<Vertex> vtxBuff;
std::vector<uint> elemBuff;
std::vector<Rect> clipRects;
std::vector<DrawCall> drawCalls;
uint vao = 0, vbo = 0, ebo = 0;
};
void Renderer::Init()
{
CreateVao();
BindVao();
CreateVbo();
BindVbo();
AddVboAttribs(...);
AddVboAttribs(...);
CreateEbo();
}
void Renderer::Prepare()
{
vtxBuff.clear();
elemBuff.clear();
clipRects.clear();
drawCalls.clear();
}
void Renderer::DrawText(std::string text, int x, int y, uint color)
{
int i = vtxBuff.size();
for(int i = 0; i < text.size(); ++i)
{
//example values
vtxBuff.push_back({x, y, color});
vtxBuff.push_back({x, y, color});
vtxBuff.push_back({x, y, color});
vtxBuff.push_back({x, y, color});
elemBuff.push_back(i);
elemBuff.push_back(i + 1);
elemBuff.push_back(i + 2);
elemBuff.push_back(i + 2);
elemBuff.push_back(i + 3);
elemBuff.push_back(i);
DrawCall& currentDraw = drawCalls[drawCall.size() - 1];
currentDraw.elemCount += 6;
}
}
void Renderer::PresentScene()
{
EnableScissorTest();
EnableBlendFunc(...);
BindVbo();
AddVboData(vtxBuff.size() * sizeof(Vertex), vtxBuff.data();
BindEbo();
AddEboData(elemBuff.size() * sizeof(uint), elemBuff.data();
for(const auto& drawCall : drawCalls)
{
Rect clipRect = drawCall.clipRect;
if (clipRect.x < windowSizeW && clipRect.y < windowSizeH && clipRect.w >= 0.0f && clipRect.h >= 0.0f)
{
glScissor(clipRect.x, clipRect.y, clipRect.w, clipRect.h);
bindTexture(); //if needed
DrawElements(drawCall.elemCount, 0);
}
}
}
void Renderer::UpdateClipRect()
{
const Rect currentClip = clipRects.size() ? clipRects[clipRects.size() - 1] : Rect(0.0f, 0.0f, windowSizeW, windowSizeH);
DrawCall* currentDraw = drawCalls.size() > 0 ? &drawCalls[drawCalls.size() - 1] : nullptr;
if (!currentDraw || currentDraw->elemCount != 0)
{
DrawCall drawCall;
drawCall.clipRect = currentClip;
drawCalls.push_back(drawCall);
return;
}
if(currentDraw->elementCount == 0)
{
//if we have nothing to draw, we do not need to have draw call
drawCalls.pop_back();
}
else
{
currentDraw->clipRect = currentClip;
}
}
void Renderer::PushClipRect(const Rect& rect)
{
clipRects.push_back(rect);
UpdateClipRect();
}
void Renderer::PopClipRect()
{
clipRects.pop_back();
UpdateClipRect();
}
int main()
{
OpenWindow();
InitOpenGL();
Renderer renderer;
Renderer.Init();
InitShaders(vtx, frag);
AddOrtho(0.0f, 1280.0f, 0.0f, 720.0f);
while(running)
{
glDisable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
renderer.Prepare();
renderer.PushClipRect(Rect(0, 0, 1280, 720);
renderer.DrawText("SomeText", 0, 250, 0xffffffff);
renderer.DrawText("SomeText2", 0, 200, 0xffffffff);
//renderer.PopClipRect();
renderer.PushClipRect(Rect(500, 0, 120, 720);
renderer.DrawText("Clipped text display", 504, 510, 0xffffffff);
//renderer.PopClipRect();
renderer.PresentScene();
SwapWindows();
}
}
It renders only "SomeText" and "SomeText2". "Clipped text display" is totally invisible. It gets visible when I remove renderer.PushClipRect(Rect(500, 0, 120, 720);
or set it to be again renderer.PushClipRect(Rect(0, 0, 1280, 720);
(because second draw call is removed and it uses the first one, as it should).
it works when I set renderer.PushClipRect(Rect(500, 0, 120, 720);
above the "SomeText", but then "SomeText" and "SomeText2" are invisible.
Poping does not change anything.
Why does it work like this? How to fix it?
User contributions licensed under CC BY-SA 3.0