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

Auteur Sujet: Appliquer un sf::Transform à un sf::Sprite et à un sf::shape dans un draw.  (Lu 10240 fois)

0 Membres et 1 Invité sur ce sujet

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Appliquer un sf::Transform à un sf::Sprite et à un sf::shape dans un draw.
« Réponse #30 le: Septembre 04, 2013, 12:11:29 pm »
Bon voici le code de ma propre interface transformable :
#ifndef TRANSF_ENTITY
#define TRANSF_ENTITY
class TransformableEntity  {
public :
    TransformableEntity(Vec2f position, Vec2f size, Vec2f origin) {
        localBounds = FloatRect(position.x, position.y, size.x, size.y);
        m_position = position;
        m_size = size;
        m_center = Vec2f (position.x + size.x * 0.5f, position.y + size.y * 0.5f);
        m_origin = origin;
        m_scale = Vec2f(1.f, 1.f);

        needToUpdate = true;
        inverseNeedToUpdate = true;
    }
    FloatRect getLocalBounds() {
        return localBounds;
    }
    void setCenter(Vec2f center) {
        Vec2f position = Vec2f (center.x - m_scale.x * m_origin.x, center.y - m_scale.y * m_origin.y);
        Vec2f t = position - m_position;
        move(t);
        needToUpdate = true;
        inverseNeedToUpdate = true;
    }
    Vec2f getCenter() {
        return m_center;
    }
    void setPosition(Vec2f position) {
        Vec2f t = position - m_position;
        move(t);
        needToUpdate = true;
        inverseNeedToUpdate = true;
    }
    Vec2f getSize() {
        return m_size;
    }
    Vec2f getPosition() {
        return m_position;
    }
    void setRotation (float angle) {
        m_rotation = static_cast<float>(fmod(angle, 360));
        if (m_rotation < 0)
            m_rotation += 360.f;
        angle  = -m_rotation * 3.141592654f / 180.f;
        float cosinus = Math::cosinus(angle);
        float sinus = Math::sinus(angle);
        float sxc = cosinus * m_scale.x;
        float sxs = sinus * m_scale.y;
        float syc = cosinus * m_scale.x;
        float sys = sinus * m_scale.y;
        m_size.x = (sxc + sys) * localBounds.width;
        m_size.y = (-sxs + syc) * localBounds.height;
        needToUpdate = true;
        inverseNeedToUpdate = true;
        onRotate(angle);
    }
    void setScale(Vec2f scale) {
        m_scale = scale;
        float angle  = -m_rotation * 3.141592654f / 180.f;
        float cosinus = Math::cosinus(angle);
        float sinus = Math::sinus(angle);
        float sxc = cosinus * m_scale.x;
        float sxs = sinus * m_scale.y;
        float syc = cosinus * m_scale.x;
        float sys = sinus * m_scale.y;
        m_size.x = (sxc + sys) * localBounds.width;
        m_size.y = (-sxs + syc) * localBounds.height;
        needToUpdate = true;
        inverseNeedToUpdate = true;
        onScale(scale);
    }
    void scale(Vec2f s) {
        setScale(m_scale * s);
    }
    void rotate (float angle) {
        setRotation(m_rotation + angle);
    }
    void move (Vec2f t) {
        m_position += t;
        m_center += t;
        needToUpdate = true;
        inverseNeedToUpdate = true;
        onMove(t);
    }
    void setOrigin(Vec2f origin) {
       m_origin = origin;
       needToUpdate = true;
       inverseNeedToUpdate = true;
    }

    Vec2f getScale() {
        return m_scale;
    }
    float getRotation() {
        return m_rotation;
    }
    Vec2f getOrigin() {
        return m_origin;
    }
    void setSize (Vec2f size) {
        setScale(size / m_size);
        needToUpdate = true;
        inverseNeedToUpdate = true;
    }

    FloatRect getGlobalBounds() {
        FloatRect globalBounds = getTransform().transformRect(localBounds);
    }
    const sf::Transform getTransform() const {
         // Recompute the combined transform if needed
        if (needToUpdate) {

            float angle  = -m_rotation * 3.141592654f / 180.f;
            float cosine = static_cast<float>(std::cos(angle));
            float sine   = static_cast<float>(std::sin(angle));
            float sxc    = m_scale.x * cosine;
            float syc    = m_scale.y * cosine;
            float sxs    = m_scale.x * sine;
            float sys    = m_scale.y * sine;
            float tx     = -m_origin.x * sxc - m_origin.y * sys + m_position.x;
            float ty     =  m_origin.x * sxs - m_origin.y * syc + m_position.y;

            const_cast<TransformableEntity*>(this)->m_transform = sf::Transform( sxc, sys, tx,
                                      -sxs, syc, ty,
                                      0.f, 0.f, 1.f);
            const_cast<TransformableEntity*>(this)->needToUpdate = false;
        }
        return m_transform;
    }
    const sf::Transform& getInverseTransform()
    {
        // Recompute the inverse transform if needed
        if (inverseNeedToUpdate)
        {
            const_cast<TransformableEntity*>(this)->m_inverseTransform = getTransform().getInverse();
            const_cast<TransformableEntity*>(this)->inverseNeedToUpdate = false;
        }

        return m_inverseTransform;
    }
protected :
    virtual void onRotate(float angle) {}
    virtual void onScale(Vec2f s) {}
    virtual void onMove(Vec2f t) {}
private :
    FloatRect localBounds;
    Vec2f m_position, m_size, m_center,m_scale,m_origin;
    float m_rotation;
    sf::Transform m_transform;
    sf::Transform m_inverseTransform;
    bool needToUpdate, inverseNeedToUpdate;
};
#endif // TRANSF_ENTITY
 

Mais il reste un soucis, avec ce code, je ne peux pas par exemple, pour un sprite, mettre sa position en fonction du coin supérieur gauche c'est à dire quand l'origine est le coin supérieur gauche de la tile, puis changer l'origine et faire les transformations autour du centre de la tile.

Je pense que je vais ré-écrire une classe Sprite se sera plus simple que de devoir faire un calcul en retirant la moitié de la taille de ma tile si je veux la positionner en foncton du coin supérieur gauche puis faire des transformations autour de son centre.

Sinon de temps en temps je me retrouve avec une tile inclinée à 45° ce qui est bizarre.
J'ai rien dit j'avais oublié d'initialiser m_rotation a 0.

Sinon quelque questions :

-Y a t'il moyen de rendre une classe abstraite même si elle n'a pas de méthode virtuelle pure ? (Comme en java.)
-Et si je veux pouvoir redéfinir les méthodes onRotate, on Scale et onMove mais je veux que seul mon interface puisse appelé ses méthodes, que dois je faire, déclarer ses méthode en privé ?
« Modifié: Septembre 04, 2013, 12:13:21 pm par Lolilolight »

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Appliquer un sf::Transform à un sf::Sprite et à un sf::shape dans un draw.
« Réponse #31 le: Septembre 04, 2013, 12:43:36 pm »
Bon finalement j'ai trouvé il suffisait de déclarer les méthodes virtuelles en privé et le constructeur en protected de ma classe transformable.

Bon bah il ne me reste plus qu'à changer ma classe Tile en utilisant un vertexarray plutôt que un sprite et se sera parfait. :)

Je me demande si je ne vais pas aussi remettre à jour la matrice à chaque transformation plutôt que à chaque getTransform histoire de pouvoir faire des transformations en changeant l'origine.