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

Author Topic: Class for multi-layer sprites rendering  (Read 5598 times)

0 Members and 1 Guest are viewing this topic.

s4game

  • Newbie
  • *
  • Posts: 8
    • View Profile
Class for multi-layer sprites rendering
« on: July 05, 2024, 11:14:11 am »
Hello there
I've been trying to create a solution for the ability to regulate at which layer the sprite will be drawn. Basically I created a class that does the same thing as sf::RenderWindow does, except setting the priority in the rendering.
Here is how it looks:

winhndl.h:
#pragma once
#include <SFML/Graphics.hpp>
#include <vector>

class WindowHandler
{
private:
    sf::RenderWindow win;
    std::vector<sf::Drawable> buffer[5];
public:
    WindowHandler(const sf::RenderWindow& window);

    void draw(const sf::Drawable& targ, int priority); // 0 - top priority, 4 - bottom priority
    void render();
    void setView(const sf::View& camera);
};
 



winhndl.cpp:
#include "winhndl.h"

WindowHandler::WindowHandler(const sf::RenderWindow& window)
{
    win.create(window.getSystemHandle(), window.getSettings());
}

void WindowHandler::draw(const sf::Drawable& targ, int priority)
{
    buffer[priority].push_back(targ);
}

void WindowHandler::render()
{
    win.clear();
    for(int i = 4; i >= 0; i--)
    {
        for(const auto& t : buffer[i])
        {
            win.draw(t);
        }
        buffer[i].clear();
    }
    win.display();
}

void WindowHandler::setView(const sf::View& camera)
{
    win.setView(camera);
}
 


But when I build the project I am met with the two errors:
Quote
invalid new-expression of abstract class type 'sf::Drawable'
Quote
static assertion failed: result type must be constructible from input type

I am not even sure if I have chosen the right datatype for buffer array. I have been stuck on this problem for a few days now.

Thanks in advance

Hapax

  • Hero Member
  • *****
  • Posts: 3385
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Class for multi-layer sprites rendering
« Reply #1 on: July 06, 2024, 10:40:17 pm »
sf::Drawable is a virtual "base" type. To be an 'actual' type, it requires to be derived/inherited. For example, any of the pre-defined shapes such as sf::RectangleShape, sf::CircleShape and sf::Sprite etc..

You can, however, hold pointers to that base type in an attempt to hold different children types.
Be careful though because it requires casting to and from the different types when needed. This means that something, somewhere, needs to know what type this is, if you need any of the children's features. You can, of course, use the base stuff such as draw easily.

Note that an std::vector is not an array (or std::array). Where arrays take [] to determine its size, a vector does not. It takes a parameter instead (so () or {}) but it can also be created empty and elements/items can added individually later.

If you are wanting an array of vectors (or a vector or arrays), it would be better to specify it like this:
std::array<std::vector<const sf::Drawable*>, 5u> layers{}; // an array of size 5, each with a vector of drawable pointers.

e.g.
std::vector<const sf::Drawable*> buffer(5u); // or std::array<sf::Drawable*> buffer[5u];

void WindowHandler::draw(const sf::Drawable& targ, int priority)
{
    buffer[priority] = dynamic_cast<const sf::Drawable*>(&targ);
}

void WindowHandler::render()
{
    win.clear();
    for(int i = 4; i >= 0; i--)
    {
        for(const auto& t : buffer[i])
        {
            if (t != nullptr)
                win.draw(*t); // i expect this to work...
        }
        buffer[i].clear();
    }
    win.display();
}
 

I haven't tested this exact code; a lot of it is copied directly from your post.

Another way to approach this, is to create a structure that holds priority and the drawable's pointer and then have a vector of those. Then, you can sort them easily using that priority.

e.g.
struct OneThing
{
    int priority
    sf::Drawable* drawable;
}
std::vector<OneThing> things{};
std::sort(things.begin(), things.end(), [](OneThing lhs, OneThing rhs){ return lhs.priority > rhs.priority; });
 



EDIT:
modified the cast from "reinterpret_cast" to "dynamic_cast".
« Last Edit: July 10, 2024, 09:28:31 pm by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

s4game

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Class for multi-layer sprites rendering
« Reply #2 on: July 09, 2024, 08:32:31 pm »
I tried your modified class variables and functions (without the struct) and now the previous errors are gone. But now the program gives out the Segmentation fault Exception in RenderTarget.cpp draw method:

...
void RenderTarget::draw(const Drawable& drawable, const RenderStates& states)
{
    drawable.draw(*this, states);//<--- Segfault here
}
...

Though, I did notice that it only throws the exception when the WindowHandler::render() method is called.
Any ideas why this is happening?

Hapax

  • Hero Member
  • *****
  • Posts: 3385
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Class for multi-layer sprites rendering
« Reply #3 on: July 10, 2024, 09:27:35 pm »
You'll need to make sure that all of the pointers are valid. That is, both 'not null' and 'of type sf::Drawable*'.

Presuming your using my exact code, is it crashing on where I wrote "i expect this to work"?

Thinking about it, I think I used the wrong cast. It should not be "reinterpret_cast", it should instead be "dynamic_cast". I'll edit my post.

I've also realised that that loop doesn't need to be a reference. i.e. "const auto t" instead of "const auto& t" since it's just a pointer and a reference to a pointer seems to make little sense (unless in very specific scenario).

Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

s4game

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Class for multi-layer sprites rendering
« Reply #4 on: July 10, 2024, 10:36:31 pm »
After changing everything you said, it still gives off the Segfault exception. And yeah, it happens at the target.draw(...) line (in WindowHandler::render()). I checked the pointer address in the buffer array, and it seems to be pointing to some memory address (0x5ffda8). Although I could not seem to check the type (I am not really sure how to do it in VScode). I am quite inexperienced in memory management, so sorry if I am missing something.

kojack

  • Sr. Member
  • ****
  • Posts: 345
  • C++/C# game dev teacher.
    • View Profile
Re: Class for multi-layer sprites rendering
« Reply #5 on: July 11, 2024, 05:44:18 am »
Presuming your using my exact code, is it crashing on where I wrote "i expect this to work"?

Thinking about it, I think I used the wrong cast. It should not be "reinterpret_cast", it should instead be "dynamic_cast". I'll edit my post.

Exactly as it is, the code shouldn't actually compile. The problem is the top is:
std::vector<const sf::Drawable*> buffer(5u);
(a vector of 5 drawable pointers)
instead of:
std::vector<const sf::Drawable*> buffer[5u];
(an array of 5 vectors of drawable pointers)

The rest of the code treats buffer as an array of vectors (such as buffer.clear() which won't compile if buffer is a vector).