Partage
  • Partager sur Facebook
  • Partager sur Twitter

Une texture qui ne veut pas rester charger

Sujet résolu
    3 septembre 2018 à 19:43:47

    Bonjours a tous,

    Toujours dans mon ECS, je me retrouve face a un problème :

    J'ai un composant comme ceci

    struct sprite_component
    {
    	sf::Texture texture_data;
    	sf::Sprite sprite_data;
    	id id_data;
    };

    Pour sa création, j'utilise cette fonction :

    ecs::sprite_component create_sprite_component(std::string const& path, ecs::id const& id)
    {
    	sf::Texture texture;
    	texture.loadFromFile(path);
    
    	ecs::sprite_component s_component{ texture };
    	s_component.sprite_data = sf::Sprite{ s_component.texture_data };
    	s_component.id_data = id;
    
    	return s_component;
    };

    Le but est de sauvegarder la texture dans le composant et de s'en servir pour initialiser le sprite, ce qui permet que maintenir la texture charger

    Malgré cela, je me retrouve quand même avec un carre blanc comme texture pour mon sprite

    J'ai louper quelque chose pour maintenir la texture charger ?

    Merci d'avance pour votre lecture / aide

    PS : Comment est utiliser la fonction

    ecs::id ecs::add_player(stage & stage, player_infos const & infos, sf::Texture const& text)
    {
    	const auto id{ create_entity(stage) };
    	stage.entities.push_back(id);
    	stage.physics.push_back(physic_component{ physic{ infos.position, infos.size }, id });
    	stage.celerities.push_back(celerity_component{ celerity{ 0, 0 }, id });
    	stage.speeds.push_back(speed_component{ infos.speed, id });
    	stage.types.push_back(type_component{ type::mob, id });
    	stage.sprites.push_back(create_sprite_component(infos.sprite_path, id));
    	add_animation(stage, id, infos.animation);
    
    	return id;
    }

    -
    Edité par K4kugen 3 septembre 2018 à 19:47:32

    • Partager sur Facebook
    • Partager sur Twitter
      3 septembre 2018 à 21:20:06

      Hello,

      La texture est perdue pendant le push_back. Par contre je ne saurai expliquer pourquoi. Si tu veux y remédier, utilise un pointeur pour stocker la texture dans sprite_component :

      struct sprite_component
      {
          sprite_component(const sf::Texture& t) :
              texture_data{std::make_unique<sf::Texture>(t)},
              sprite_data{*texture_data}
          {}
      
          std::unique_ptr<sf::Texture> texture_data{};
          sf::Sprite sprite_data{};
      };
      sprite_component create_sprite_component(const std::string& path)
      {
          sf::Texture texture{};
          texture.loadFromFile(path);
      
          sprite_component s_component{texture};
      
          return s_component;
      }

      Note que j'ai viré l'initialisation du sprite de la fonction create_sprite_component, il est directement initialisé dans le constructeur de sprite_component.


      -
      Edité par Guit0Xx 3 septembre 2018 à 21:20:39

      • Partager sur Facebook
      • Partager sur Twitter

      ...

        4 septembre 2018 à 0:37:48

        Salut,

        De toute manière, tu dois te dire qu'il est particulièrement inutile d'avoir -- au final -- autant de copies des données propres à texture que de composants qui l'utilisent:

        Quand on pense que cette image (dont je t'accorde que cela ferait une bien piètre texture :D ) nécessitera 24*22 = 528 pixels soit 2112 bytes (vu qu'elle est au format Rgba) pour être représentée en mémoire, alors qu'un pointeur ou un identifiant pourrait tout aussi bien permettre d'accéder aux données qu'elle contient en ne nécessitant que 8 bytes maximum (c'est la taille d'un unsigned long long, quand meme :D ), imagine la quantité de mémoire que tu pourrais perdre si tu voulais l'utiliser comme texture que pour, mettons 10 sprites!!!

        En utilisant un identifiant ou un pointeur, qui te permettra d'accéder à la texture, tu aurais besoin d'à peine 80 bytes, alors que, en voulant copier les données qui permettent de représenter cette texture, tu aurais besoin de ... 21 120 bytes.

        Et je ne te parle là que d'une toute petite texture!!! Mais, imagine le résultat avec une texture de 100 pixels de coté: nous passerions de 80 bytes (car la taille d'un pointeur ou d'un identifiant ne changerait pas) à ... 100*100*4*10 (hauteur * largeur * COLOR_DEPTH * nombre_de_spites) = 400 000 bytes.

        Tout cela pour quoi? mais pour rien, en sommes...

        En somme, tu dois bien te dire que ce n'est pas un hasard si la texture est stockée sous la forme d'un pointeur dans la classe sf::sprite(tu le verra à la ligne 219) avec l'implémentation (directement tirée des sources que j'ai) sous une forme proche de

        Sprite::Sprite(const Texture& texture) :
        m_texture    (NULL),
        m_textureRect()
        {
            setTexture(texture);
        }
        /* et comme il fait appel à setTexture */
        //////////////////////////////////////////////////////
        void Sprite::setTexture(const Texture& texture, bool resetRect)
        {
            // Recompute the texture area if requested, or if there was no valid texture & rect before
            if (resetRect || (!m_texture && (m_textureRect == sf::IntRect())))
                setTextureRect(IntRect(0, 0, texture.getSize().x, texture.getSize().y));
        
            // Assign the new texture
            /* comme tu le vois, il se contente de prendre
             * l'adresse de la texture
             */
            m_texture = &texture;
        }
        

        Du coup, ce que tu devrais faire, c'est veiller à séparer la texture du sprite au niveau de tes composants... Par exemple en ayant un TextureComponent qui pourrait ressembler à

        struct TextureComponent{
            IdType  id; /* hé oui, tu aurais une entité qui ne 
                         * serait composée que de la texture 
                         */
            sf::Texture data; // pour pouvoir l'utiliser
            std::string filename; /* pour savoir de quel fichier elle vient
                                   */
        }

        et un composant SpriteComponent qui ressemblerait à quelque chose comme

        struct SpriteComponent{
            IdType id; /* le lien vers l'entité qui utilise le sprite
                        */
            IdType textureId;  // le lien vers la texture utilisée
            sf::Sprite sprite; // le sprite à afficher
            /* toutes autres informations utiles */
        }

        Et, bien sur, tu aurais une fonction create_textureComponent(TypeId id, std::string const & filename) qui pourrait ressembler à

        create_textureComponent(TypeId id, std::string const & filename){
            TextureComponent cmp;
            cmp.filename = filename;
            cmp.texture.loadFromFile(filename);
        }

        voire même une fonction IdType create_ewIdTextureComponent(std::string const & filename)qui pourrait ressembler à

        void create_newIdTextureComponent(std::string const & filename){
            auto id = createEntity();
            create_textureComponent(id, filename);
        }

        Et, tant qu'à faire, une fonction TypeId findTextureId(std::string const & filename), dont je vais te laisser fournir l'implémentation, mais qui permettra de retrouver l'entité à laquelle est rattachée une texture sur base du nom du fichier à partir duquel elle aura été chargée.

        (sans oublier une fonction qui permettra de récupérer une référence constante sur le TextureComponent associé à un identifiant particulier : findTextureComponent(TypeId id) )

        Tout cela te permettra d'avoir ta fonction qui ressemblera à

        ecs::sprite_component create_sprite_component(std::string const& path, ecs::id const& id)
        {
            auto textureId = findTextureId(path);
            if(textureId == 0) {
                textureId = create_newTextureComponent(path);
            }
            auto const & textureData = findTextureComponent(textureId).data;
            ecs::sprite_component component;
            component.sprite_ = sf::Sprite{ textureData };
            component.id = id;
            component.textureId = textureId;
            return component;
        };

        De cette manière, tu pourras créer 2000 sprites qui utilisent la même texture si tu y tiens, elle ne sera chargée qu'une seule fois, copiée une fois (lorsque tu ajoutera le TextureComponent à la collection qui permettra de le maintenir en mémoire), et réutilisée "autant de fois que nécessaire" ;)

        PS: j'ai utilisé le type IdType comme s'il s'agissait d'un alias de type sur size_t ou sur unsigned long long...  A toi d'adapter ton code en conséquence si ce n'est pas le cas ;)

        -
        Edité par koala01 4 septembre 2018 à 0:40:56

        • Partager sur Facebook
        • Partager sur Twitter
        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
          5 septembre 2018 à 16:45:29

          Merci beaucoup Koala01 pour ton aide, je n'avais encore jamais penser a utiliser une entité dans un composant, mais c'est un aspect interessant, je le garde en tete pour la suite

          J'avais aussi complètement zapper le fais d'utiliser plusieurs fois le même sprite, c'est sur que l'utilisation d'un id ou d'un pointeur est bien moins coûteux en mémoire (et merci pour le petit passage sur les sources sfml, je n'avais pas du tous penser a y jeter un œil)

          Et encore merci pour la flopper de fonction associer, en plus ça me permet de voir quelque réflexe de conception que je n'ai pas encore

          (Je t'oublie pas Guit0Xx, merci de m'avoir débloqué et appris que ça déconnais sur le push_back)

          PS : dans mon code le "id" est un alias de type sur un size_t, donc pas de soucis

          -
          Edité par K4kugen 5 septembre 2018 à 16:46:02

          • Partager sur Facebook
          • Partager sur Twitter

          Une texture qui ne veut pas rester charger

          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
          • Editeur
          • Markdown