Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to flip textures horizontally and vertically (mirroring textures)  (Read 15011 times)

0 Members and 1 Guest are viewing this topic.

Jamin Grey

  • Newbie
  • *
  • Posts: 25
    • View Profile
    • Of Stranger Flames
I wrote a function that allows you to draw a sf::Texture without using a sf::Sprite, and also allows you to flip/mirror the texture. I needed this for my own project, but hopefully others find it useful as well.

Here's the code:
void SfmlDrawTexture(sf::RenderTarget &destination, const sf::Vector2f &location, const sf::Texture &texture,
                                         sf::IntRect subRect, const sf::Color &coloration, float rotation,
                                         bool flipHorizontally, bool flipVertically, sf::BlendMode blendMode, const sf::Shader *shader)
{
        //If no rect is specified, use the entire texture.
        if(subRect.width == 0 || subRect.height == 0)
        {
                subRect.top = 0;
                subRect.left = 0;
                subRect.width = texture.getSize().x;
                subRect.height = texture.getSize().y;
        }
       
        //Set the position in space.
        sf::Transform translation;
        translation.translate(location);
       
        //Set the rotation (rotated around the center, since this sf::Transform wasn't moved).
        sf::Transform rotationTransform;
        rotationTransform.rotate(rotation);

        //Setup the render state.
        sf::RenderStates states(blendMode, (translation * rotationTransform), &texture, shader);
       
        //Setup the vertices and their attributes.
        sf::Vertex vertices[4];
       
        //The transparency:
        vertices[0].color = coloration;
        vertices[1].color = coloration;
        vertices[2].color = coloration;
        vertices[3].color = coloration;
       
        //The pre-transform position and size:
        float widthBeforeTransform = static_cast<float>(subRect.width);
        float heightBeforeTransform = static_cast<float>(subRect.height);
    vertices[0].position = sf::Vector2f(0, 0);
    vertices[1].position = sf::Vector2f(0, heightBeforeTransform);
    vertices[2].position = sf::Vector2f(widthBeforeTransform, heightBeforeTransform);
    vertices[3].position = sf::Vector2f(widthBeforeTransform, 0);
       
        //Calculate the texture coordinates:
        float left   = static_cast<float>(subRect.left);
    float right  = left + subRect.width;
    float top    = static_cast<float>(subRect.top);
    float bottom = top + subRect.height;
       
        //If we're mirroring, swap the texture coordinates vertically and/or horizontally.
        if(flipVertically)              std::swap(left, right);
        if(flipHorizontally)    std::swap(top, bottom);

        //Set the texture coordinates:
    vertices[0].texCoords = sf::Vector2f(left, top);
    vertices[1].texCoords = sf::Vector2f(left, bottom);
    vertices[2].texCoords = sf::Vector2f(right, bottom);
    vertices[3].texCoords = sf::Vector2f(right, top);
       
        //Use the sf::RenderTarget to draw the vertices using the sf::RenderStates we set up.
        destination.draw(vertices, 4, sf::Quads, states);
}
 

To handle mirroring, I swapped the texture coordinates... currently this only works because SFML doesn't disable back-face culling (at least, not in SFML 2.0), but whatever. =)

It'd be nice if this (flipping/mirroring) was available as a feature for sf::Sprites.
Of Stranger Flames - My work-in-progress para-historical (and classically-inspired) 2D rpg.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: How to flip textures horizontally and vertically (mirroring textures)
« Reply #1 on: September 14, 2013, 05:51:23 am »
Can't you flip sprites simply by passing negative numbers to setScale?

Jamin Grey

  • Newbie
  • *
  • Posts: 25
    • View Profile
    • Of Stranger Flames
Re: How to flip textures horizontally and vertically (mirroring textures)
« Reply #2 on: September 14, 2013, 08:29:41 pm »
I still needed a function that draws a single texture without using a sprite - I've nothing against sf::Sprite, and use it, but in a few parts of my code, sf::Sprite gets in the way more than it helps. In other areas, it's really useful.

I've heard that negative scales flip textures, but the information wasn't available - the last I heard (after searching these forums before writing it myself) was Laurent saying, "It doesn't work, but I'll consider it later" (2011 or somewhere thereabouts).

I checked the documentation, and it wasn't mentioned there. [sf::Transformable 2.0][sf::Transformable 2.1]
I also searched what others were doing on the forum, and they had weird clunky solutions using modified RenderTextures or by modifying SFML's sourcecode.

But even if setScale with negative numbers *does* work, it'd need to be added to the documentation (or maybe I'm just not seeing it?), and even if it did work, why *should* negative setScales flip textures? That's unintuitive (despite OpenGL doing the same thing), and a setHorizontallyFlipped(bool) and setVerticallyFlipped(bool) would be much more self-documenting in written code and easier to search for, right?
Of Stranger Flames - My work-in-progress para-historical (and classically-inspired) 2D rpg.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to flip textures horizontally and vertically (mirroring textures)
« Reply #3 on: September 14, 2013, 11:11:40 pm »
These functions were in the SFML 1.6 API, and removed in SFML 2.0.

There are two ways to do it:

1. A negative scale factor. There's no need to document it, this is what a negative scale does, if you know what a scale factor is. However it's not a perfect equivalent: it's not a flip of the texture, but a symetry of the sprite (i.e. the sprite's coordinates will change too).

2. A textureRect with negative width/height. A negative width means that left and right texture coordinates are swapped, which is a texture flip (same reasoning for height).
This one is a trick, and I don't remember if it is properly documented. At least, it's written everywhere on this forum.
Laurent Gomila - SFML developer

Jamin Grey

  • Newbie
  • *
  • Posts: 25
    • View Profile
    • Of Stranger Flames
Re: How to flip textures horizontally and vertically (mirroring textures)
« Reply #4 on: September 15, 2013, 02:30:12 am »
Odd, I searched the forums multiple times. :-X Clearly I wasn't using the correct search terms. ("mirroring texture", "texture flipping", etc...). I think I was limiting my search to just the Graphics subforum, but even so, that should've turned up something.

If I couldn't find it, when actively trying to, perhaps it should be made more prominently displayed someplace? Not that you should work to make things easier for me specifically, but I'm a fairly stubborn person who really does search to find a solution before posting asking for help - so other users might also be having difficulty finding that information. Or maybe it's just me having difficulty. ;)

When I searched the documentation, I didn't even think of checking sf::Sprite::setTextureRect (but I can't find it documented there either). What's the proper way to do it? If I do: setTextureRect(sf::IntRect(50, 50, -20, -20)), does that mean it'll grab the equivalent of (50,50,20,20) and then flip the texture, or does that mean it'll grab the equivalent of (30,30,50,50) flipped? I'm betting the latter, but if you're planning to document the feature, that'd be a good thing to mention.

Though, I still think the change:
Code: [Select]
//At "SFML/Graphics/Sprite.cpp" (line 161)
void Sprite::updateTexCoords()
{
    float left   = static_cast<float>(m_textureRect.left);
    float right  = left + m_textureRect.width;
    float top    = static_cast<float>(m_textureRect.top);
    float bottom = top + m_textureRect.height;

//----<added>----
if(m_flipHorizontally) std::swap(left, right);
if(m_flipVertically) std::swap(top, bottom);
//----</added>----

    m_vertices[0].texCoords = Vector2f(left, top);
    m_vertices[1].texCoords = Vector2f(left, bottom);
    m_vertices[2].texCoords = Vector2f(right, bottom);
    m_vertices[3].texCoords = Vector2f(right, top);
}

//----<added>----
void Sprite::setHorizontallyFlipped(bool flipped)
{
    m_flipHorizontally = flipped;
this->updateTexCoords();
}

void Sprite::setVerticallyFlipped(bool flipped)
{
    m_flipVertically = flipped;
this->updateTexCoords();
}
//----</added>----

...would be alot easier for users to understand. At least, it'd be alot easier for me to understand!  :)
« Last Edit: September 15, 2013, 02:38:19 am by Jamin Grey »
Of Stranger Flames - My work-in-progress para-historical (and classically-inspired) 2D rpg.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to flip textures horizontally and vertically (mirroring textures)
« Reply #5 on: September 15, 2013, 09:26:45 am »
Quote
If I do: setTextureRect(sf::IntRect(50, 50, -20, -20)), does that mean it'll grab the equivalent of (50,50,20,20) and then flip the texture, or does that mean it'll grab the equivalent of (30,30,50,50) flipped?
The latter.

Sorry for the short answer, I don't have the time to elaborate on the flip functions or documentation right now. This is a long story ;D
Laurent Gomila - SFML developer

Jamin Grey

  • Newbie
  • *
  • Posts: 25
    • View Profile
    • Of Stranger Flames
Re: How to flip textures horizontally and vertically (mirroring textures)
« Reply #6 on: September 15, 2013, 07:55:39 pm »
Np, thanks for having responded.
« Last Edit: September 15, 2013, 07:58:05 pm by Jamin Grey »
Of Stranger Flames - My work-in-progress para-historical (and classically-inspired) 2D rpg.

 

anything