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

Author Topic: OO Sprite Batch  (Read 5985 times)

0 Members and 1 Guest are viewing this topic.

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
OO Sprite Batch
« on: January 20, 2015, 06:27:18 pm »
So, yesterday I thought to myself, ooh, having a sprite batch of sorts in SFML would be very nice, so I started on one, and in my ignorance failed to check the wiki/forum for results. Which... resulted in this:

https://github.com/dabbertorres/SwiftSpriteBatch

For those who don't know what a SpriteBatch is or does, it is essentially a container of vertices that all use one texture. So, rather than having 1 draw call per sf::Sprite, it allows you to have only 1 draw call for (almost) any number of vertices, or swift::Sprites, which turns out to be much faster.

It works very much like the sprite batch on the wiki, but rather than working based off indices, it uses classes.

It consists of 2 classes, a SpriteBatch class, and a Sprite Class.

Here's the example from the Github page:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>

#include "SpriteBatch.hpp"
#include "Sprite.hpp"

int main(int argc, char **argv)
{
    sf::RenderWindow window;

    window.create({800, 600, 32}, "SFML");

    sf::Texture texture;
    if(!texture.loadFromFile("ftlGrizzly.png"))
    {
        std::cerr << "error loading texture\n";
    }

    swift::SpriteBatch batch(texture, 2);

    swift::Sprite spriteOne(batch);
    spriteOne.setOrigin({spriteOne.getLocalBounds().width / 2, spriteOne.getLocalBounds().height / 2});
    spriteOne.setScale({2, 2});
    spriteOne.setPosition({400, 400});
    spriteOne.scale({0.5, 0.5});
    swift::Sprite spriteTwo(batch);
    spriteTwo.setPosition({400, 200});

    while(window.isOpen())
    {
        sf::Event event;
        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                    window.close();
                    break;
                default:
                    break;
            }
        }

        window.clear();
        window.draw(batch);
        window.display();
    }

    return 0;
}
 

The constructor of the SpriteBatch takes a sf::Texture, and the number of Sprites you would like to have. I went with locking the size because Sprites work with pointers, and if the memory needs to be relocated due to resizing, that invalidates pointers, which causes a whole bunch of issues.

There are 2 constructors for Sprite, both take the batch you want to add the Sprite to, and 1 takes a sf::IntRect to set as the texture rectangle.
Upon destruction of a Sprite, the vertices the Sprite used are kept, but all with position (0, 0) and color (0, 0, 0, 0). This effectively makes it non-visible, without invalidating pointers.
I tried to keep similar (if not the exact same) behavior as an sf::Sprite in regards to functions to call on swift::Sprite. In my testing, it seems to act the same, so here's to hoping it is truly that way. Hehe.

I'd love any feedback anyone has and am happy to answer any questions anyone may have!

EDIT: Should add that it uses C++11.
« Last Edit: January 20, 2015, 06:30:28 pm by dabbertorres »

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: OO Sprite Batch
« Reply #1 on: January 21, 2015, 10:42:32 am »
Thought about doing something similar in the past, but never found the time to do so. However, I would have made a different approach that still "draws" the single sprites rather than omitting their draw call. It's an interesting approach though, maybe I should think of some mix between both strategies. :)

Rhimlock

  • Jr. Member
  • **
  • Posts: 73
    • View Profile
Re: OO Sprite Batch
« Reply #2 on: January 21, 2015, 03:06:02 pm »
I think you might have a problem with invalid pointers in certain situations.

You use:
vertices = batch.addSprite();
where vertices ist a vector<sf::Vertex *>.

But if your Vertex-vector in SpriteBatch gets reallocated because it grows to big, your Pointers in the Sprite wont be valid anymore.
« Last Edit: January 21, 2015, 04:08:05 pm by Ghosa »

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
Re: OO Sprite Batch
« Reply #3 on: January 21, 2015, 05:05:37 pm »
Mario, hmm, that is an interesting idea!

Ghosa, I have a limit on the size of the vector for that reason. The vector never actually grows, preventing the situation you described from happening.

I went with a vector over an array just because it allows for run time size setting, rather than compile time.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
AW: Re: OO Sprite Batch
« Reply #4 on: January 21, 2015, 05:30:10 pm »
You use:
vertices = batch.addSprite();
where vertices ist a vector<sf::Vertex *>.

But if your Vertex-vector in SpriteBatch gets reallocated because it grows to big, your Pointers in the Sprite wont be valid anymore.
I haven't taken a closer look at the code, but this statement itself is not true.
Yes a vector can reallocate its elements, but that is only an issue, if you hold a pointer to an element of the vector, because the address this pointer is pointing towards doesn't hold the element anymore since it got relocated.
But what we have here is a vector of pointers, thus as long as the object, "referenced" by the pointer, stays alive, the pointer in the vector will be valid. If the vector reallocates its elements, the "content" of the elements won't change, thus the pointer will still point to the same address after reallocation.

I hope that made sense. :D
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Rhimlock

  • Jr. Member
  • **
  • Posts: 73
    • View Profile
Re: OO Sprite Batch
« Reply #5 on: January 22, 2015, 10:08:03 am »
I think you missunderstood me.

SpriteBatch has a std::vector<sf::Vertex>

Sprite has Pointers to the vertices in SpriteBatch with std::vector<sf::Vertex *>.

So if SpriteBatch should reallocate its vertices, the Pointer in Sprite won't be valid anymore.

Please correct me if I got it wrong.

But I didn't see, that he won't add elements to his SpriteBatch-Vector if a certain size is reached (in which case he will just return an nullptr). So the Pointers in the return will stay valid.

std::array<sf::Vertex*, 4> SpriteBatch::addSprite()
{
if((spriteNum + 1) * 4 <= vertices.size())
{
unsigned int s = spriteNum * 4;
spriteNum++;
return {&vertices[s], &vertices[s + 1], &vertices[s + 2], &vertices[s + 3]};
}
else
return {nullptr};
}

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
AW: OO Sprite Batch
« Reply #6 on: January 22, 2015, 11:59:26 am »
Yeah I mainly concentrated on the code and didn't notice your text referred to some other code. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/