[Bug] Clipping issue with Panel and canvas.

Started by Dnake, 28 May 2015, 17:09:26

Dnake

Hello,
I found a bug occurring in following situation:
-A widget is inside a Panel
-This widget overlap the bounds of the panel (so it should be clipped).
-A Canvas is also somewhere else.
In this case, the widget is not clipped at all at the bounds of the the Panel.
Have a look at this code:

#include <TGUI/Gui.hpp>
#include <TGUI/Panel.hpp>
#include <TGUI/Button.hpp>
#include <TGUI/Canvas.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(400, 300), "TGUI window");
    tgui::Gui gui(window);
    gui.setGlobalFont("DejaVuSans.ttf");

auto panel = tgui::Panel::create();
panel->setSize(tgui::bindWidth(gui.getContainer(), 0.5f), tgui::bindHeight(gui.getContainer(), 0.5f));
panel->setBackgroundColor(sf::Color::Transparent);
gui.add(panel);

    auto play = tgui::Button::create();
    play->setSize(tgui::bindWidth(gui.getContainer()), tgui::bindHeight(gui.getContainer()));
    play->setText("Play");
    panel->add(play);

    tgui::Canvas::Ptr canvas = tgui::Canvas::create({300, 300});
    gui.add(canvas);
    sf::Texture tex;
    tex.loadFromFile("Linux.jpg");
    sf::Sprite spr(tex);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
            else if (event.type == sf::Event::Resized)
                gui.setView(sf::View{{0, 0, (float)event.size.width, (float)event.size.height}});
            gui.handleEvent(event);
        }
        window.clear();
        canvas->clear();
        canvas->draw(spr);
        canvas->display();
        gui.draw();
        window.display();
    }
    return EXIT_SUCCESS;
}

The button fills totally the window but the panel owning the button don't.

I'm using TGUI-0.7dev, SFML-2.3 (both latest snapshot) and gcc-4.9.

texus

This is a really strange issue. It only occurs when the clear/draw/display of the canvas is done right before drawing the gui. Drawing the sprite to the canvas before the main loop makes the code work as expected. However a sf::RenderTexture does not seem to cause this behavior, while Canvas is just a simple wrapper around it. I'm still trying to figure out the difference between the two that breaks this code.

Calling "window.resetGLStates()" right before "gui.draw()" seems to solve it, so you can use that for now.

"tgui::bindWidth(gui.getContainer(), 0.5f)" can be written as "tgui::bindWidth(gui, 0.5f)" btw.

texus

This one is really tricky and I have decided not to fix it (for the reason, see last paragraph of this post).

Just don't put the function calls on the Canvas between the clear and display of the window. Putting it right above it will work fine. If you do it this way then you can't create any problems.

The error only occurs when calling sf::RenderTexture::display right before calling gui.draw(). If any other draw call is between it then the code will work. The problem is that RenderTexture activates its own opengl context. All draw calls (also the one in tgui) will reset the context. But before tgui makes any draw calls it uses opengl for the clipping, and in this case it does that on the wrong context.

The solution on my side would be to simply call m_window->activate(true) inside the gui.draw call. Unfortunately this activate function is private. I know that I can easily call the function anyway, but I simply don't want to.

The reason I'm not going to fix it is because this code will be thrown away in the near future (hopefully). The only opengl code that I use is for clipping, and the only reason I implement clipping in my own code is because sfml does not provide clipping masks yet. So once sfml implements clipping masks I should be able to ditch all my opengl code and this problem will solve itself. The clipping masks in sfml are actually already kinda implemented, they just haven't been able to agree on a design yet. So I do expect it to be added to sfml before tgui 0.7.0 is released.

Dnake

In the code I posted, the fix you offer is easy to implement, but I found this bug in a game I'm developping with a high abstraction level with many classes, so it is no this obvious to move functions calls on the Canvas outside the clear/display calls on the window.
So you also mentionned I can call window.resetGLStates() or window.activate() right before gui.draw(), is there any side effect of this method in place of moving the calls on Canvas?

(And I knew for the canvas code shortening, I just copy-pasted parts of you example codes)

Thanks :)

texus

#4
There shouldn't be any side effects of resetGLStates as long as you don't use opengl directly. But if you want to go this way then perhaps it would be better to put "window.pushGLStates()" and "window.popGLStates()" around the gui.draw call. That should also do the trick without messing with other code.

I understand how hard it is to find a bug like this, so I appreciate that you took the time to dig out where it went wrong exactly and report the bug.

QuoteI just copy-pasted parts of you example codes
Ah, I didn't knew it contained that code. I forgot to update it apparently after I allowed the bind function to take the Gui as parameter directly.