c++sfmlsprite-sheetvertex-array

How do you flip texture in VertexArray?


I have a spritesheet animation drawn using a vertexarray. As an example let's say I have an arrow pointing to the right with a simple animation. I would like to be able to flip the texture on the x axis so that the arrow can also point left without the need to double the size of my spritesheet.

I know that this is possible with sprites by using a negative scale factor, but in cases where I have to use vertexarray I would like to know how this is possible. I tried giving the texcoords a negative width but it did not work. Here's my example code that isolates the issue as much as possible:

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

int main()
{
    // CREATE THE WINDOW

    sf::RenderWindow window(sf::VideoMode(200, 200), "Arrow");
    window.setFramerateLimit(20);

    // CREATE GRAPHICS

    sf::Texture texArrow;
    texArrow.loadFromFile("Arrow.png");

    sf::VertexArray vaArrow;
    sf::RenderStates rsArrow;
    rsArrow.texture = &texArrow;

    vaArrow.setPrimitiveType(sf::Quads);
    vaArrow.resize(4);

    vaArrow[0].position = sf::Vector2f(0, 0);
    vaArrow[1].position = sf::Vector2f(100, 0);
    vaArrow[2].position = sf::Vector2f(100, 100);
    vaArrow[3].position = sf::Vector2f(0, 100);

    vaArrow[0].texCoords = sf::Vector2f(0, 0);
    vaArrow[1].texCoords = sf::Vector2f(100, 0);
    vaArrow[2].texCoords = sf::Vector2f(100, 100);
    vaArrow[3].texCoords = sf::Vector2f(0, 100);

    // ARROW DIRECTION STATES

    enum Directions { right, left};
    Directions dir = right;

    float AnimationFrame = 0;

    // GAME LOOP

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) { window.close(); }

            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { dir = right; }
            else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { dir = left; }
        }

        // UPDATE

        switch (dir)
        {
            case right: // NORMAL

                std::cout << "Arrow is now pointing RIGHT" << std::endl;

                vaArrow[0].texCoords = sf::Vector2f(AnimationFrame * 100        ,   0);
                vaArrow[1].texCoords = sf::Vector2f(AnimationFrame * 100 + 100  ,   0);
                vaArrow[2].texCoords = sf::Vector2f(AnimationFrame * 100 + 100  ,   100);
                vaArrow[3].texCoords = sf::Vector2f(AnimationFrame * 100        ,   100);

                break;

            case left: // FLIPPED

                std::cout << "Arrow is now pointing LEFT" << std::endl;

                // THIS IS WHAT I HAVE TRIED BUT IT DOESN'T WORK:

                vaArrow[0].texCoords = sf::Vector2f((AnimationFrame * 100) * -1         ,   0);
                vaArrow[1].texCoords = sf::Vector2f((AnimationFrame * 100 + 100) * -1   ,   0);
                vaArrow[2].texCoords = sf::Vector2f((AnimationFrame * 100 + 100) * -1   ,   100);
                vaArrow[3].texCoords = sf::Vector2f((AnimationFrame * 100) * -1         ,   100);

                break;
        }

        AnimationFrame++;

        if (AnimationFrame == 5) { AnimationFrame = 0; } // Always loop 0 1 2 3 4

        // RENDER

        window.clear();
        window.draw(vaArrow, rsArrow);
        window.display();
    }

    return 0;
}

Spritesheet for the example:

Arrow


Solution

  • There are many days to solve this, but your actual idea isn't that far off. Did you notice that you're basically stretching one end of the arrow over your whole quad?

    The solution is pretty simple: By default. Textures aren't repeated, they're clamped (meaning that the pixels on the edges are repeated infinitely), which causes the glitch you've noticed.

    So all you have to do to get your approach working is to set the texture to be repeated:

    texArrow.setRepeated(true);