Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Piotr Szawdyński
Biblioteki C++

Ograniczanie obszaru modyfikowania sceny

[lekcja] Rozdział 10. Niniejszy rozdział pokazuje w jaki sposób można ograniczyć obszar rysowania elementów na scenie (tekst, sprajty itp).
Biblioteka SFML jest narzędziem, które do tej pory było omawiane przeze mnie w świetle glorii i chwały... niestety wszystko co ma blaski ma i również swe cienie, a teraz poznamy jeden z nich. Zanim jednak przejdę do sedna sprawy postaram się rzucić nieco światła na rangę problemu, który zostanie poruszony w niniejszym rozdziale.

Wyobraźmy sobie, że chcemy stworzyć sobie kontrolkę pola tekstowego. Pole tekstowe ma zazwyczaj to do siebie, że ma określone wymiary, a wewnątrz tego pola umieszczamy tekst. Ideą pola tekstowego jest również to, aby wpisana treść nie wychodziła poza jego obszar nawet jeżeli tekst jest zbyt długi by go całego wyświetlić. Do opisanego wyżej problemu zapewne podszedłbyś tak:
C/C++
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow oknoAplikacji( sf::VideoMode( 320, 240, 32 ), "Kurs SFML - http://cpp0x.pl" );
    sf::String tekst;
    tekst.SetSize( 12 );
    tekst.SetText( "to jest bardzo dlugi napis" );
    tekst.SetColor( sf::Color::Red );
    while( oknoAplikacji.IsOpened() )
    {
        sf::Event zdarzenie;
        while( oknoAplikacji.GetEvent( zdarzenie ) )
        {
            if( zdarzenie.Type == sf::Event::Closed )
                 oknoAplikacji.Close();
           
            if( zdarzenie.Type == sf::Event::KeyPressed && zdarzenie.Key.Code == sf::Key::Escape )
                 oknoAplikacji.Close();
           
            if( zdarzenie.Type == sf::Event::MouseButtonPressed && zdarzenie.MouseButton.Button == sf::Mouse::Middle )
                 oknoAplikacji.Close();
           
        }
        oknoAplikacji.Clear( sf::Color( 0, 0, 0 ) );
        oknoAplikacji.Draw( sf::Shape::Rectangle( 10, 10, 100, 30, sf::Color::White, 2, sf::Color::Green ) );
        tekst.SetPosition( 12, 12 );
        oknoAplikacji.Draw( tekst );
        oknoAplikacji.Display();
    }
    return 0;
}
Powyższe podejście jest moim zdaniem całkowicie uzasadnione i naturalne, jednak efekt z pewnością nie będzie dla Ciebie zadowalający. Zrzut ekranu z działania powyższej aplikacji wygląda tak:

Pierwsze co prawdopodobnie Ci przyszło do głowy to 'ograniczyć w jakiś sposób obszar w którym tekst może być rysowany' - idea dobra, jednak SFML nie posiada takiego narzędzia, którym moglibyśmy to osiągnąć. Drugą metodą byłoby liczenie ile tekstu się zmieści i wyświetlanie tylko tego co się zmieści we wskazanym obszarze - niestety metoda ta wymaga stosunkowo dużego nakładu pracy, a i tak prędzej czy później natrafimy na przypadek gdzie takie ręczne wyliczanki nie będą po prostu możliwe.

Choć już czujesz w powietrzu złowrogą energię i czarne chmury zbierają się nad Tobą to możesz odetchnąć z ulgą, bowiem SFML ma furtkę zwaną OpenGL. Wszystko co jest renderowane w oknie stworzonym za pomocą biblioteki SFML jest renderowane przy pomocy biblioteki OpenGL. Biblioteka OpenGL posiada z kolei narzędzie dzięki któremu możesz ograniczyć obszar renderowania do wskazanego fragmentu ekranu. Wszystko co zostanie narysowane poza wskazanym obszarem zostanie po prostu zignorowane. Brzmi pięknie? Myślę, że tak i teraz dowiesz się jak osiągnąć cel, który przez chwilę wydawał się być niemożliwy do osiągnięcia :)

Aby określić obszar, który może być modyfikowany należy skorzystać z funkcji glScissor biblioteki OpenGL. Skorzystanie z tej funkcji nie wymaga od Ciebie dołączania żadnych plików nagłówkowych ponieważ biblioteka SFML jest oparta o bibliotekę OpenGL i dołącza wszelkie niezbędne pliki wspomnianej biblioteki. Funkcja glScissor przyjmuje cztery argumenty i są nimi kolejno:
  • położenie lewej krawędzi prostokąta obcinającego względem lewej krawędzi okna aplikacji;
  • położenie dolnej krawędzi prostokąta obcinającego względem dolnej krawędzi okna aplikacji;
  • szerokość prostokąta obcinającego;
  • wysokość prostokąta obcinającego.
W myśl opisanej wyżej idei do wcześniej wspomnianej aplikacji powinniśmy dopisać następujący wiersz:
C/C++
::glScissor( 12, oknoAplikacji.GetHeight() - 28, 86, 16 );
Wywołanie tej funkcji nie odniesie jednak skutku jeżeli nie włączymy mechanizmu obcinania. W bibliotece OpenGL wszelkie mechanizmy włącza się przy pomocy funkcji glEnable, natomiast funkcja glDisable służy do wyłączania mechanizmów. Kod, który należy poprawić to:
C/C++
::glEnable( GL_SCISSOR_TEST );
::glScissor( 12, oknoAplikacji.GetHeight() - 28, 86, 16 );
tekst.SetPosition( 12, 12 );
oknoAplikacji.Draw( tekst );
::glDisable( GL_SCISSOR_TEST );
Po naniesieniu wyżej opisanych poprawek nasz kod aplikacji będzie wyglądał tak:
C/C++
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow oknoAplikacji( sf::VideoMode( 320, 240, 32 ), "Kurs SFML - http://cpp0x.pl" );
    sf::String tekst;
    tekst.SetSize( 12 );
    tekst.SetText( "to jest bardzo dlugi napis" );
    tekst.SetColor( sf::Color::Red );
    while( oknoAplikacji.IsOpened() )
    {
        sf::Event zdarzenie;
        while( oknoAplikacji.GetEvent( zdarzenie ) )
        {
            if( zdarzenie.Type == sf::Event::Closed )
                 oknoAplikacji.Close();
           
            if( zdarzenie.Type == sf::Event::KeyPressed && zdarzenie.Key.Code == sf::Key::Escape )
                 oknoAplikacji.Close();
           
            if( zdarzenie.Type == sf::Event::MouseButtonPressed && zdarzenie.MouseButton.Button == sf::Mouse::Middle )
                 oknoAplikacji.Close();
           
        }
        oknoAplikacji.Clear( sf::Color( 0, 0, 0 ) );
        oknoAplikacji.Draw( sf::Shape::Rectangle( 10, 10, 100, 30, sf::Color::White, 2, sf::Color::Green ) );
        ::glEnable( GL_SCISSOR_TEST );
        ::glScissor( 12, oknoAplikacji.GetHeight() - 28, 86, 16 );
        tekst.SetPosition( 12, 12 );
        oknoAplikacji.Draw( tekst );
        ::glDisable( GL_SCISSOR_TEST );
        oknoAplikacji.Display();
    }
    return 0;
}
Efekt, który uzyskamy po naniesieniu wspomnianych poprawek będzie następujący:

Wilk jest więc syty i owca cała - to by było na tyle w tym rozdziale :)