Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

glScissor() call inside another glScissor()

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.

EDIT

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?

enter image description here

like image 677
Piotr Chojnacki Avatar asked Feb 01 '13 11:02

Piotr Chojnacki


3 Answers

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.

like image 166
Christian Rau Avatar answered Oct 05 '22 16:10

Christian Rau


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.

like image 37
sheu Avatar answered Oct 05 '22 15:10

sheu


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.

like image 39
datenwolf Avatar answered Oct 05 '22 14:10

datenwolf