SFML community forums

Help => Graphics => Topic started by: YesBox on May 30, 2022, 04:44:24 pm

Title: Transforming Individual Quad/Sprite in Vertex Array
Post by: YesBox on May 30, 2022, 04:44:24 pm
Hello, is there an official way to rotate/mirror an individual quad/sprite in a vertex array? All the documentation I've seen so far is for applying a transform to an entire vertex array.

Title: Re: Transforming Individual Quad/Sprite in Vertex Array
Post by: eXpl0it3r on June 01, 2022, 08:26:14 am
Transforming the entire vertex array has a dedicated API and is often what you want, thus you see it more.

Transforming a single quad is just doing math on those four vertices.
You can use a standalone sf::Transform to help you with the transformation matrix and then call transformPoint for every vertex: https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Transform.php#ab42a0bb7a252c6d221004f6372ce5fdc
Title: Re: Transforming Individual Quad/Sprite in Vertex Array
Post by: YesBox on June 01, 2022, 05:59:37 pm
I can't figure out how to implement what you're describing. I tried creating a sf::Transform, set the rotation to 90, and then apply that to each point in the quad. Doesn't work.

Here is my code that works, but is a hacky way to accomplish what I need. Note that gameMapPassed is a container with tiles to update in the game, and gameMap_ is a container that contains all game world tiles.


//...
finder = textureMap_.at( TileType::TILE_ATLAS_VARIANT ).getSize().x / TILE_SIZE;
//...

for( const auto& tile : gameMapPassed ) {
        int index = tile.first;

        sf::Vertex* quad = &vtexMap_.at( VertexLevel::USER_TILE )[gameMap_.at( index ).mutableTile.tile_id * 4];
        TileType tileType = tile.second.staticTile->type;
        int tileNumber = static_cast<int>( tileType );

        int x = index % mapWidth_;
        int y = index / mapWidth_;
        int tx = tileNumber % finder;
        int ty = tileNumber / finder;
       
        int rotation = tile.second.mutableTile.rotation;
        int rotaHelp {};

        switch( rotation ) {
                case 0: rotaHelp = 0; break;
                case 90: rotaHelp = 1; break;
                case 180: rotaHelp = 2; break;
                case 270: rotaHelp = 3; break;
        }

        quad[0].position = sf::Vector2f( x * TILE_SIZE, y * TILE_SIZE );                               
        quad[1].position = sf::Vector2f( ( x + 1 ) * TILE_SIZE, y * TILE_SIZE );
        quad[2].position = sf::Vector2f( ( x + 1 ) * TILE_SIZE, ( y + 1 ) * TILE_SIZE );
        quad[3].position = sf::Vector2f( x * TILE_SIZE, ( y + 1 ) * TILE_SIZE );

        if( tile.second.mutableTile.mirror == false ) {
                quad[( 0 + rotaHelp ) % 4].texCoords = sf::Vector2f( tx * TILE_SIZE, ty * TILE_SIZE ); 
                quad[( 1 + rotaHelp ) % 4].texCoords = sf::Vector2f( ( tx + 1 ) * TILE_SIZE, ty * TILE_SIZE );
                quad[( 2 + rotaHelp ) % 4].texCoords = sf::Vector2f( ( tx + 1 ) * TILE_SIZE, ( ty + 1 ) * TILE_SIZE );
                quad[( 3 + rotaHelp ) % 4].texCoords = sf::Vector2f( tx * TILE_SIZE, ( ty + 1 ) * TILE_SIZE );
        }
        else { 
                quad[( 1 + rotaHelp ) % 4].texCoords = sf::Vector2f( tx * TILE_SIZE, ty * TILE_SIZE );
                quad[( 0 + rotaHelp ) % 4].texCoords = sf::Vector2f( ( tx + 1 ) * TILE_SIZE, ty * TILE_SIZE );
                quad[( 3 + rotaHelp ) % 4].texCoords = sf::Vector2f( ( tx + 1 ) * TILE_SIZE, ( ty + 1 ) * TILE_SIZE );
                quad[( 2 + rotaHelp ) % 4].texCoords = sf::Vector2f( tx * TILE_SIZE, ( ty + 1 ) * TILE_SIZE );
        }
}
 

Essentially, by changing the order when filling out the quad[], you can rotate and/or mirror and individual tiles in the game. (e.g. 0 + rotaHelp could start at quad[2]. Then % 4 ensures the value wraps around to 0.).

It's long winded to describe why the rotation logic works. Im less certain why the mirror logic works, but it does. I discovered it by playing around with the ordeing some more.
Title: Re: Transforming Individual Quad/Sprite in Vertex Array
Post by: Hapax on June 11, 2022, 05:13:29 pm
I can't figure out how to implement what you're describing. I tried creating a sf::Transform, set the rotation to 90, and then apply that to each point in the quad. Doesn't work.

It's worth noting that any rotation will rotate around its (0, 0) (local) so I presume that's what you mean by "doesn't work."

Try negatively translating each point by the position you with to place the origin of the rotation, performing the rotation and then positively translating the point back.
NOTE: this will all be local co-ordinates.
FYI (in the unlikely situation you didn't know): in this case, translating a point effectively means change its position i.e. move it.
Title: Re: Transforming Individual Quad/Sprite in Vertex Array
Post by: AnhBao on June 26, 2022, 03:11:20 pm
You can use rotation matrix, something like:
    float alpha = 3.14f/2, sine = std::sin(alpha), cosine = std::cos(alpha);
    sf::Vector2f offset{(v[0].position + v[4].position)/2.f};
    for(int i = 0; i < 4; ++ i)
    {
        sf::Vector2f pos = v[i].position - offset;
        v[i].position = sf::Vector2f{pos.x*cosine - pos.y*sine, pos.x*sine + pos.y*cosine} + offset;
    }
This code rotates a quad including 4 vertex 90 degrees clockwise.