I'm using glScissor()
in my application and it works properly, but I met a problem:
I have my Window
object for which drawing area is specified by glScissor()
and inside this area, I'm drawing my ListView
object for which the drawing area should also be specified with glScissor()
as I don't want to draw it all.
In a code I could represent it as:
Window::draw()
{
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw some components...
mListView.draw(); // mListView is an object of ListView type
glDisable(GL_SCISSOR_TEST);
}
ListView::draw()
{
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw a chosen part of ListView here
glDisable(GL_SCISSOR_TEST);
}
But of course in this situation enable/disable calls are wrong:
glEnable(GL_SCISSOR_TEST);
glEnable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
If I remove those internal glEnable/glDisable calls (the ones in ListView), I would anyway end up with two glScissor() calls which also seems to be wrong.
I would like to somehow achieve both scissor effects, I mean - that Window should draw only in it's scissored area and internal ListView also only in it's scissored area.
As you can see in a picture, with red rectangle I have marked scissor area for Window
which WORKS, and with a blue rectangle I marked an area on which I'd like to draw my ListView
. That's why I was trying to use nested scissors, but I know it was useless. So basically my question is, what could be the best approach to achieve this?
Since OpenGL is a state machine and the scissor rect, like any other state, is overwritten when calling glScissor
the next time, you have to properly restore the window's scissor rect after drawing the list view. This can either be done by just letting the window manage it:
Window::draw()
{
// draw some components with their own scissors
mListView.draw(); // mListView is an object of ListView type
glScissor(x, y, width, height);
glEnable(GL_SCISSOR_TEST);
// draw other stuff using window's scissor
glDisable(GL_SCISSOR_TEST);
}
But it might be more flexible to let the individual components restore the scissor state themselves, especially if used in such a hierarchical manner. For this you can either use the deprecated glPush/PopAttrib
functions to save and restore the scissor rect:
ListView::draw()
{
glPushAttrib(GL_SCISSOR_BIT);
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw a chosen part of ListView here
glDisable(GL_SCISSOR_TEST);
glPopAttrib();
}
Or you save and restore the scissor state yourself:
ListView::draw()
{
// save
int rect[4];
bool on = glIsEnabled(GL_SCISSOR_TEST);
glGetIntegerv(GL_SCISSOR_BOX, rect);
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw a chosen part of ListView here
// restore
glScissor(rect[0], rect[1], rect[2], rect[3]);
if(!on)
glDisable(GL_SCISSOR_TEST);
}
This can of course be automated with a nice RAII wrapper, but that is free for you to excercise.
edited: General case.
Every time you set the scissor state with glScissor
, you set the scissor state. It does not nest, and it does not stack, so you cannot "subscissor" with nested calls to glScissor
. You'll have to manually compute the rectangular intersection of your ListView
and your Window
bounding rects, and then scissor to that when drawing ListView
.
In the general case, you'll be manually maintaining a stack of scissor rectangles. As you draw each sub-element, you intersect the sub-element's bounding rect against the current top of the stack, and use that as the scissor for that sub-element. Push the new subrect onto the stack when painting children, and pop it when returning back up the hierarchy.
If you're painting other contents to Window
, you'll also have to make sure to handle overdraw correctly; either by setting the Z ordering and enabling depth buffering, or by disabling depth buffering and painting your contents back-to-front. Scissoring won't help you mask the Window
contents behind the ListView
, since scissors can only be rectangular regions.
OpenGL is a state machine. You can call glScissor, glEnable and glDisable as often as you'd like to. They don't act like opening-closing braces that must be matched. If your calls "fold" like this, that's no problem. Just don't expect the one scissor to merge with the other one; it will merely change/overwrite the previous setting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With