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

Author Topic: Getting a sprite's vertices  (Read 8274 times)

0 Members and 1 Guest are viewing this topic.

Kojay

  • Full Member
  • ***
  • Posts: 104
    • View Profile
Getting a sprite's vertices
« on: December 19, 2013, 11:56:10 pm »
It is known that the need to draw sprites individually hurts performance and that one should prefer vertex arrays to that end. It is then a shame that sf::Sprite's internal vertices cannot be accessed.

What I 've found myself doing too often is making a class holding an array of four sf::Vertex, that can be appended to a VertexArray or similar container when it is time to draw. Often, this class will also need a getBounds method, setters for the texture rect and so on, making it similar to a sf::Sprite.

I would propose then that there is a way to use sf::Sprite for this purpose; either

a) sf::Sprite::getVertices() that returns an array of the 4 vertices or
b) sf::Sprite::appendTo() taking as parameter either
    i) a sf::VertexArray
    ii) an output iterator, to accomodate the user's container of sf::Vertex.
« Last Edit: December 19, 2013, 11:58:12 pm by Kojay »

Veltas

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: Getting a sprite's vertices
« Reply #1 on: December 19, 2013, 11:59:37 pm »
Might be better to return a reference to whatever is used to store the vertices internally, if at all... EDIT: Wait, that doesn't work.

Anyway, I agree with the proposal!

Anyone else got any ways that they have worked around this problem, or any particular reasons why this wouldn't be a problem?
« Last Edit: December 20, 2013, 12:21:33 am by Veltas »
< veltas> MJBrune: Debian GNU/Linux
* MJBrune mounts veltas

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Getting a sprite's vertices
« Reply #2 on: December 20, 2013, 02:08:09 am »
Drawing lots of sf::Sprites is slow because of a number of reasons: it handles its own transforms, vertex arrays, and texture binding. For most intensive applications you'll have to build your own optimised solution: batch sprites together, build and cache vertex arrays, spatially cull, etc.

As far as I can tell sf::Sprite is just a helper class, designed to easily draw textures to the screen. If you need anything more complex you should just build it yourself, from all the parts SFML also supplies. And because you can easily build the vertex array using sf::Sprites existing API I highly doubt that Laurent would add redundant functionality. :)

Kojay

  • Full Member
  • ***
  • Posts: 104
    • View Profile
Re: Getting a sprite's vertices
« Reply #3 on: December 21, 2013, 11:39:50 am »
By the same argument, the user could put together sf::Sprite themselves. Not to mention, that makes a lot of helper classes that got to be ditched as soon as one gets serious - that is not redundant?

An alternate proposal for either SFML or Thor would be a similar class designed precisely to that end, namely batching sprites together. It doesn't get in the way of the user's customized solution - getVertices() or appendTo() is only called if the sprite has been determined to be visible, whatever the means employed.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Getting a sprite's vertices
« Reply #4 on: December 21, 2013, 04:44:14 pm »
Not to mention, that makes a lot of helper classes that got to be ditched as soon as one gets serious - that is not redundant?
Sprites are much easier to use than vertex arrays, and even in "serious" situations they have their use cases. You need to draw a lot of sprites until you reach the limit and have to switch to vertex arrays -- typically, this happens for repeated patterns such as tilemaps or particle systems. There is no reason to use vertex arrays for every single character in the game, especially not if they use different textures (the verbosity of sf::VertexArray doesn't pay off if only a few objects are drawn).

Don't forget that a core philosophy of SFML consists of being simple to use.


An alternate proposal for either SFML or Thor would be a similar class designed precisely to that end, namely batching sprites together.
Could you elaborate what you imagine its API to look like? Would each batched object still have its own transform that you can set independently?
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Kojay

  • Full Member
  • ***
  • Posts: 104
    • View Profile
Re: Getting a sprite's vertices
« Reply #5 on: December 22, 2013, 04:16:23 pm »
It could be simply

class Sprite {
public:
        void setTextureRect(const sf::IntRect& rectangle);
        void setColor(const sf::Color& color);
        sf::FloatRect getBounds() const;
        void appendTo(const sf::VertexArray& array) const;
        void appendTo(OutputIterator result)
}
 

Since a transform would be applied to the vertices rather than the RenderStates, it may pay to keep the class suited for sprites that aren't meant to be scaled or rotated (such as the aforementioned tilemaps, also sprites that achieve change of facing by shifting the texture rect rather than being rotated); in which case a setter for position would be sufficient.

Quote
There is no reason to use vertex arrays for every single character in the game, especially not if they use different textures

Even if the textures could be concatenated? I can see the benefit of sf::Sprite for prototyping, however achieving maximum performance would always be on my todo list.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Getting a sprite's vertices
« Reply #6 on: December 22, 2013, 04:37:20 pm »
It could be simply [...]
Yes, but you mentioned an alternate approach, so I thought you would have an idea besides extending the sf::Sprite interface.

Because this one sounds very cumbersome to use: The user has to keep both sf::Sprite and sf::VertexArray, and he needs to synchronize them manually. The whole rendering and transform information is effectively duplicated.

I can see the benefit of sf::Sprite for prototyping, however achieving maximum performance would always be on my todo list.
Maximum performance is an unclear objective, you can never reach that. It is important to profile the application, find the bottlenecks and optimize them specifically. In more complex games, other tasks such as pathfinding or physics may be the real performance killer, in such cases it's pointless to optimize the last cycle out of rendering.

I am not against features that allow the usage of "efficient" sprites (through vertex arrays), but it shouldn't be even more complicated than what's currently possible. The reason why vertex arrays are so tedious to use is that you have to handcraft everything on your own. With higher-level helper functions (create vertices from a texture rect and transform, set the color for multiple vertices, etc.) this task would be much simpler. And exactly these functions are already implemented in sf::Sprite, therefore a solution should aim at reusing them where possible.

The question is whether such a sprite batch would still require sprites? sf::Sprite would then just be an intermediate class -- between the game logic that sets the attributes and the vertex array which is drawn. If there were an interface similar to what sf::Sprite provides, but internally directly operating on a sf::VertexArray, that would be nice.
« Last Edit: December 22, 2013, 04:44:48 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Kojay

  • Full Member
  • ***
  • Posts: 104
    • View Profile
Re: Getting a sprite's vertices
« Reply #7 on: December 22, 2013, 05:12:07 pm »
No indeed, the Sprite I wrote about above is not sf::Sprite - that's the alternate proposal, a similar class.

Quote
And exactly these functions are already implemented in sf::Sprite, therefore a solution should aim at reusing them where possible.
Which was what prompted me to post in the first place.

Just to be sure we 're on the same page, here's a quick example:

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

class Sprite {
public:
    Sprite(const sf::Vector2f& position) {
        const float side = 50.f;

        vertices[0].position = position;
        vertices[1].position = position + sf::Vector2f(side, 0);
        vertices[2].position = position + sf::Vector2f(side,side);
        vertices[3].position = position + sf::Vector2f(0, side);
    }

    void appendTo  (sf::VertexArray& array) {
        for (const auto& vertex : vertices)
            array.append(vertex);
    }

private:
    std::array<sf::Vertex, 4> vertices;
};



int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 800), "Sprites");

    std::vector<Sprite> sprites;

    for (int i=0; i<8; ++i)
        sprites.emplace_back(sf::Vector2f(i*100, i*100));

    sf::VertexArray toDraw(sf::Quads);

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

        //update
        toDraw.clear();
        for (auto& sprite : sprites)
            sprite.appendTo(toDraw);

        //draw
        window.clear();
        window.draw(toDraw);
        window.display();
    }

    return 0;
}
 

I don't see this as any more cumbersome than iterating through a structure of sf::Drawables.

krzat

  • Full Member
  • ***
  • Posts: 107
    • View Profile
Re: Getting a sprite's vertices
« Reply #8 on: December 28, 2013, 12:37:23 am »
Okay, here is SpriteBatch I use: SpriteBatch. It's very useful for drawing tilemap and particles (as mentioned before). Kinda necessary on C#, since each native call has big overhead.

I'm not sure if the library should contain such class though, but I see two other solutions:
1. Add Draw methods from SpriteBatch to VertexArray (without Texture ofc.), so we will have something like:
append(Sprite sprite), //tricky, because texture will be ignored
append(FloatRect rec, IntRect src, Color color),
etc.
Now everyone can easily draw particles / tiles and this approach enforces single texture.

2. Integrate SpriteBatch into RenderTarget. Could potentially improve performance without any API changes. I'm not sure though if it's possible to compare two RenderStates fast enough (you have to draw/push buffered vertices each time the state changes).
« Last Edit: December 28, 2013, 12:42:22 am by krzat »
SFML.Utils - useful extensions for SFML.Net