SFML community forums

General => Feature requests => Topic started by: wdmitry on March 23, 2024, 01:28:47 pm

Title: export vertexes from the Shape class
Post by: wdmitry on March 23, 2024, 01:28:47 pm
The Shape class has this members. I assume it contains all the information required to draw basic sfml shapes.
But when you have too many shapes to draw, each shape require an individual draw call

Instead it would be possible to export memory to this vertexes of the shapes from the Shape class.
So that the user can build a huge array of vertexes from this shapes and just batch draw it in a single call.

VertexArray    m_vertices{PrimitiveType::TriangleFan};          //!< Vertex array containing the fill geometry
VertexArray    m_outlineVertices{PrimitiveType::TriangleStrip}; //!< Vertex array containing the outline geometry
 
Title: Re: export vertexes from the Shape class
Post by: eXpl0it3r on March 25, 2024, 11:01:06 am
What you're essentially asking for is sprite/shape batching.

I believe it's a feature we will implement eventually, but it requires some underlying changes and API decisions that aren't trivial to solve it in a generic way.

Your best option is probably to build your own class around a vertex array and create your own batching.
But make sure you're actually experiencing performance impacts. Premature optimization is the root of all evil, etc ;)
Title: Re: export vertexes from the Shape class
Post by: wdmitry on March 25, 2024, 01:29:27 pm
yes so far I think its an overkill. 1000 draw calls work just fine.
also calculating vertex again from shapes is cpu consuming.
Title: Re: export vertexes from the Shape class
Post by: Hapax on March 27, 2024, 03:15:22 pm
You can get each of the vertices by simply getting the shape's points:
for (std::size_t i{ 0u }; i < shape.getPointCount() ; ++i)
    std::cout << "Point " << i << ": (" << shape.getPoint(i).x << ", " << shape.getPoint(i).y << ")" << std::endl;

However, this is likely not how you would build the shape to draw (other than using its original primitive type).
Much more likely is you'd like to be able to build that shape with separated triangles (as any shape can be built this way) as you can then batch them together using that same primitive.
So, you could extract the triangles from the original shapes and then draw them as triangles. Since everything is just triangles, you can also batch them together if required.
To extract the triangles from a shape, you could use this:
https://github.com/SFML/SFML/wiki/Source%3A-Triangles-Extractor

Note that best results are observed if shapes don't change often and/or there are very many (to reduce those significant draw calls) and they are causing an speed issue.
Title: Re: export vertexes from the Shape class
Post by: wdmitry on May 06, 2024, 01:14:33 pm
hm yess. I also just realized that the triangles are in stripes mode, but I need a separate triangles.

another related question is. if I build my own shape using vertex array. then I see this issue.
whenever I set the position (which is 60 frames per second) I need to update all the array of vertexes to move them to a new position.

which could be avoided if I assign pointers. so that each vertex know it's position `*shapePos + shift`
is there a better way to move all the vertex positions rather than for loop?
Title: Re: export vertexes from the Shape class
Post by: FRex on May 09, 2024, 01:40:07 am
You can use a sf::Transform (or better - sf::Transformable).

I have my own helper class (called maker in the code) that lets me format Text with bold, italics, color and outline changing per character, and in the end I use an sf::Transform like so:

            sf::RenderStates states;
            states.texture = &font->getTexture(charsize);
            sf::Transformable trans;
            const auto bounds = maker.getBounds();
            trans.setPosition(kTileSize * sf::Vector2f(pos));
            const float scale = std::min(kTileSize / right(bounds), kTileSize / bottom(bounds));
            trans.setScale(scale, scale);
            states.transform = trans.getTransform();
            RENDERER_HISTORY(m_target->draw(maker.getTextVeritces(), states));
 

You should look into how ::draw() is implemented in SFML drawables, it's almost always just doing
states.transform *= getTransform();
since they derive from transformable (that's where setPosition, setRotation, etc. all come from). Some also add texture (sf::Sprite, sf::Text, even sf::Shape actually).
Title: Re: export vertexes from the Shape class
Post by: Hapax on May 13, 2024, 10:40:20 pm
It's likely a good idea to contain the vertex array within another class. The class would be the "drawable object" and the vertex array would be its "visible representation."

This is how SFML shapes (and many other drawables) do it; the SFML shapes store a vertex array within its class. They also inherit from both sf::Drawable (so it can have the draw method) and sf::Transformable - as FRex mentioned - (so the entire shape can be transformed (moved/rotated/scaled)) at once without having to make those calculations. In fact, the transformation calculations are done on the graphics card when doing it this way so it's worth learning about this method.

The usual form of a drawable would be something like:

class DrawableName : public sf::Drawable, public sf::Transformable
{
public:
    DrawableName();

private:
    sf::VertexArray m_vertices;
    void draw(sf::RenderTarget& target, sf::RenderStates states) const;
}

// this is the constructor
DrawableName::DrawableName()
    : m_vertices(6u)
{
}
// this is the draw method that can be called later like this: window.draw(drawableName);
void DrawableName::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    states.transform = getTransform();
    target.draw(m_vertices, states);
}
You can, of course, add other methods as you like to control things like size and colours or any other vertex manipulation.
Note that since the class inherits from sf::Transformable, you can also use the setPosition(), setScale(), setRotation(), move(), scale(), rotate() methods to manipulate the entire shape. Of course, getPosition, getScale and getRotation is also provided.

Slightly more advanced:
instead of using a vertex array, you can instead use an std::vector of sf::Vertex i.e.:
std::vector<sf::Vertex> m_vertices
To draw it then, you would need something like:
target.draw(m_vertices.data(), m_vertices.size(), sf::PrimitiveType::Triangles, states);
Note here that you must specify the primitive type but you can choose to store it separately as a member of the class, somewhere in the cpp file, or express it directly as in this example.



Note that if you use SFML 3, the draw method needs the sf::RenderStates to be passed as a reference, not as a copy.