Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: [Résolu] Meilleure technique de clipping ?  (Lu 3485 fois)

0 Membres et 2 Invités sur ce sujet

Excellium

  • Jr. Member
  • **
  • Messages: 70
    • Voir le profil
[Résolu] Meilleure technique de clipping ?
« le: Février 09, 2014, 05:24:57 pm »
Salut chère communauté !  :)

Je travaille actuellement sur un gros projet, mais je bloque malheureusement sur une technique en particulier.
Il s'agit d'utiliser une classe Canevas, dérivant de sf::Drawable et sf::Transformable et pouvant contenir d'autres sf::Drawable, donc potentiellement d'autres Canevas.
Le but est d'obtenir des espaces de dessin pouvant être décris les uns par rapport aux autres, jusque là, rien de sorcier.
Là ou je bloque, c'est pour la gestion du clipping. En effet, je souhaiterai que cet espace de dessin soit "fermé", qu'aucune entité dessinable enfant (peu importe son degrés de parenté), n'en sorte.

Après quelques recherches voici les pistes qui me sont données :

- Utiliser sf::View et son viewport : d'un côté ça m'arrange d'ajouter le système des vues à Canevas, d'un autre je n'ai jamais réussi à l'adapter correctement à mon problème malgré la doc.,
- Utiliser comme il sera présenté par la suite la fonction glScissor de openGL, solution intéressante mais néanmoins limitée dans mon cas, puisque dans mon exemple, seul le dernier appel à cette fonction est effectif,
- Utiliser les clipPlanes de openGL, mais parait-il ils sont très gourmands en ressources,
- Utiliser le stencil buffer, solution conseillée sur de nombreux forums.

J'ai donc simplifié le tout pour faire un exemple minimal directement compilable, utilisant la fonction glScissor :

#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>

struct Canevas : public sf::Drawable, public sf::Transformable {

        std::vector<sf::Drawable*> drawables;
        sf::FloatRect clipping;

protected:

        void draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
                states.transform.combine(getTransform());

                sf::Vector2f pos = states.transform.transformPoint({clipping.left, clipping.height + clipping.top});

                glPushAttrib(GL_SCISSOR_BIT);
                glEnable(GL_SCISSOR_TEST);
                glScissor(pos.x, target.getSize().y - pos.y, clipping.width, clipping.height);

                for( auto& drawable : drawables )
                        target.draw(*drawable, states);

                glDisable(GL_SCISSOR_TEST);
                glPopAttrib();
        }
};

int main()
{
        sf::RenderWindow window(sf::VideoMode(400, 400, 32), "Test");
        window.setVerticalSyncEnabled(true);

        /// CANEVAS 1 ///

        sf::RectangleShape shape1;
        shape1.setFillColor(sf::Color::Blue);
        shape1.setSize({200, 200});

        Canevas canevas1;
        canevas1.setPosition({50, 50});
        canevas1.clipping = {0, 0, 200, 200};
        canevas1.drawables.push_back(&shape1);

        /// CANEVAS 2 ///

        sf::RectangleShape shape2;
        shape2.setFillColor(sf::Color::Green);
        shape2.setSize({200, 200});

        sf::RectangleShape shape3;
        shape3.setPosition(50, 50);
        shape3.setFillColor(sf::Color::Red);
        shape3.setSize({200, 200});

        Canevas canevas2;
        canevas2.setPosition({50, 50});
        canevas2.clipping = {0, 0, 200, 200};
        canevas2.drawables.push_back(&shape2);
        canevas2.drawables.push_back(&shape3);

        /// CANEVAS 2 -> CANEVAS 1 ///

        canevas1.drawables.push_back(&canevas2);

        while( window.isOpen() )
    {
        sf::Event event;
     
        while( window.pollEvent(event) )
        {    
            if( event.type == sf::Event::Closed )
                window.close();
         
            else if( event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape )
                window.close();
        }

        window.clear();
        window.draw(canevas1);
        window.display();
    }

    return 0;
}



La vignette du milieu présente le résultat de l'exemple minimal, celle de gauche est le résultat du clipping désactivé et celle de droite le résultat attendu.

Quelle est d'après-vous la meilleure technique à employer, sachant que je recherche simplicité et performances avant-tout ?

Merci de votre soutien !  :D
« Modifié: Février 09, 2014, 07:15:58 pm par Excellium »
"Everything should be made as simple as possible, but not simpler."

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : Meilleure technique de clipping ?
« Réponse #1 le: Février 09, 2014, 06:11:56 pm »
Il n'y a que la solution 1 qui soit valable, toutes les autres sont du bricolage hasardeux : le dessin d'entités SFML n'est pas prévu pour être combiné avec des appels OpenGL externes. Le résultat n'est pas garanti, et même si ça marche maintenant ça peut ne plus être vrai sur une autre machine ou dans une prochaine version.

Donc moi je partirais sur la 1, et pour régler le problème de l'imbrication des canvas, il suffit à chaque fois de calculer l'intersection du canvas courant avec ses parents pour avoir le vrai rectangle de clipping.

Sinon il y a une autre solution que tu n'as pas envisagé : utiliser sf::RenderTexture. Une fois que tu as l'intérieur de ton canevas dans une texture, il est facile de limiter ce que tu affiches via la fonction Sprite::setTextureRect. C'est pas optimal, mais pour ce genre d'utilisation ça peut éventuellement être pas mal.
Laurent Gomila - SFML developer

Excellium

  • Jr. Member
  • **
  • Messages: 70
    • Voir le profil
Re : Meilleure technique de clipping ?
« Réponse #2 le: Février 09, 2014, 06:21:32 pm »
Merci Laurent, je vais tester les deux solutions que tu me proposes et poster mes résultats sur ce forum.
"Everything should be made as simple as possible, but not simpler."

Excellium

  • Jr. Member
  • **
  • Messages: 70
    • Voir le profil
Re : Meilleure technique de clipping ?
« Réponse #3 le: Février 09, 2014, 07:14:54 pm »
L'imbrication des canevas semble bien fonctionner, bien que je sois parti sur la technique 2.
Je vais tester la technique de la sf::RenderTexture de mon côté pour ne pas polluer le forum avec un monologue.
Je passe donc ce post en résolu, mais si quelqu'un à des suggestions ou voudrait me conseiller une autre façon de faire je suis tout ouïe.  ::)

#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>

struct Canevas : public sf::Drawable, public sf::Transformable {

        std::vector<sf::Drawable*> drawables;
        sf::FloatRect clipping;
        Canevas* parent_ = nullptr;

protected:

        void draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
                states.transform.combine(getTransform());

                sf::FloatRect corrected_clipping = clipping;

                if( parent_ )
                {
                        sf::FloatRect parent_clipping = {parent_->clipping.left - getPosition().x, parent_->clipping.top - getPosition().y, parent_->clipping.width, parent_->clipping.height};
                        clipping.intersects(parent_clipping, corrected_clipping);
                }

                sf::Vector2f pos = states.transform.transformPoint({corrected_clipping.left, corrected_clipping.height + corrected_clipping.top});

                glPushAttrib(GL_SCISSOR_BIT);
                glEnable(GL_SCISSOR_TEST);
                glScissor(pos.x, target.getSize().y - pos.y, corrected_clipping.width, corrected_clipping.height);

                for( auto& drawable : drawables )
                        target.draw(*drawable, states);

                glDisable(GL_SCISSOR_TEST);
                glPopAttrib();
        }
};

int main()
{
        sf::RenderWindow window(sf::VideoMode(400, 400, 32), "Test");
        window.setVerticalSyncEnabled(true);

        /// CANEVAS 1 ///

        sf::RectangleShape shape1;
        shape1.setFillColor(sf::Color::Blue);
        shape1.setSize({200, 200});

        Canevas canevas1;
        canevas1.setPosition({50, 50});
        canevas1.clipping = {0, 0, 200, 200};
        canevas1.drawables.push_back(&shape1);

        /// CANEVAS 2 ///

        sf::RectangleShape shape2;
        shape2.setFillColor(sf::Color::Green);
        shape2.setSize({200, 200});

        sf::RectangleShape shape3;
        shape3.setPosition(50, 50);
        shape3.setFillColor(sf::Color::Red);
        shape3.setSize({200, 200});

        Canevas canevas2;
        canevas2.setPosition({50, 50});
        canevas2.clipping = {0, 0, 200, 200};
        canevas2.drawables.push_back(&shape2);
        canevas2.drawables.push_back(&shape3);

        /// CANEVAS 2 -> CANEVAS 1 ///

        canevas1.drawables.push_back(&canevas2);
        canevas2.parent_ = &canevas1;

        while( window.isOpen() )
    {
        sf::Event event;
     
        while( window.pollEvent(event) )
        {    
            if( event.type == sf::Event::Closed )
                window.close();
         
            else if( event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape )
                window.close();
        }

        window.clear();
        window.draw(canevas1);
        window.display();
    }

    return 0;
}
"Everything should be made as simple as possible, but not simpler."

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : [Résolu] Meilleure technique de clipping ?
« Réponse #4 le: Février 09, 2014, 08:18:28 pm »
A tes risques et périls.
Laurent Gomila - SFML developer

Higestromm

  • Jr. Member
  • **
  • Messages: 52
    • Voir le profil
Re : [Résolu] Meilleure technique de clipping ?
« Réponse #5 le: Juin 29, 2015, 04:53:19 pm »
Bonjour,

Je reviens sur ce sujet car je souhaite également utiliser une zone de clipping dans un projet de GUI.

Donc j'ai des Widgets qui héritent de Drawable et Transformable et qui contiennent eux aussi une liste de Widget.

Jusqu'ici tout vas bien et on se trouve a peu près dans le même cas de figure qu'en haut.

Donc je m'attaque au clipping et je lit a peu près partout que sf::view est une bonne solution. Par contre j'ai du mal a comprendre comment cela pourrais m'aider car finalement, si je comprend bien, un sf::view va simplement prendre la zone aux coordonnée par un rectangle pour le "coller" dans un autre endroit.

Mais du coup, cela implique que ce qui déborde de mon Widget vas tout de même déborder. C'est comme si je découpais un bout de mon écran pour le recoller au même endroit.

J'imagine qu'il y a un truc que j'ai pas du bien comprendre...

Sinon j'ai réfléchis à un système de Buffer avec des tsf::textures mais la je me pose des questions sur la consommation mémoire d'un truc pareil et surtout comment gérer des Widget redimensionnables sans allouer l'équivalent d'un écran par fenêtre, même petite.

Enfin bref si je pouvais avoir un petit éclaircissement ça serait cool :)

Higestromm

  • Jr. Member
  • **
  • Messages: 52
    • Voir le profil
Re : [Résolu] Meilleure technique de clipping ?
« Réponse #6 le: Juin 29, 2015, 05:04:42 pm »
Bon... j crois que j'ai ma réponse ici :

http://fr.sfml-dev.org/forums/index.php?topic=7581.0

 

anything