Fixing an object oriented wrapper I am creating for bindbc.sfml

75 Views Asked by At

I am trying to create object oriented wrappers around bindbc.sfml, this is because I don't like the C-style syntax of CSFML.

The C-style syntax is not right -- in my opinion -- for an object oriented language. Dealing with pointers all the time is also unsafe.

This is not to say that CSFML isn't good -- it's great, and I've made some apps using bindbc-sfml. I just want to extend it to my liking with object oriented wrappers that can more closely match the C++ SFML syntax.

For the wrappers, I created a Shape class. This Shape class is seen in the original C++ SFML implementation:

class Shape : Transformable, Drawable {
    void setTexture(sfTexture* texture, bool resetRect) {
        ptr.sfShape_setTexture(texture, resetRect);
    }

    void setTextureRect(IntRect rect) {
        ptr.sfShape_setTextureRect(rect.to_sfIntRect());
    }

    void setFillColor(Color color) {
        ptr.sfShape_setFillColor(color.to_sfColor());
    }

    void setOutlineColor(Color color) {
        ptr.sfShape_setOutlineColor(color.to_sfColor());
    }

    void setOutlineThickness(float thickness) {
        ptr.sfShape_setOutlineThickness(thickness);
    }

    const(sfTexture)* getTexture() {
        return ptr.sfShape_getTexture();
    }

    IntRect getTextureRect() {
        return ptr.sfShape_getTextureRect().toIntRect();
    }

    Color getFillColor() {
        return ptr.sfShape_getFillColor().toColor();
    }

    Color getOutlineColor() {
        return ptr.sfShape_getOutlineColor().toColor();
    }

    float getOutlineThickness() {
        return ptr.sfShape_getOutlineThickness();
    }

    size_t getPointCount() nothrow {
        return ptr.sfShape_getPointCount();
    }

    Vector2f getPoint(size_t index) nothrow {
        return ptr.sfShape_getPoint(index).toVector2f_noThrow();
    }

    FloatRect getLocalBounds() {
        return ptr.sfShape_getLocalBounds().toFloatRect();
    }

    FloatRect getGlobalBounds() {
        return ptr.sfShape_getGlobalBounds().toFloatRect();
    }

    private sfShape* ptr;
}

The sfShape pointer isn't currently initialized, I'll get to that issue soon.

As you can see, Shape extends the Transformable class and the Drawable interface. This again roughly matches what's seen in SFML. SFML.NET also did a similar wrapper for their CSFML C# bindings. What's great about SFML.NET is that you don't even know that you're using CSFML, this is because it feels just like C++ SFML.

Now, I will create a RectangleShape which will be a subclass of the Shape class:

(Btw I took a lot of inspiration from SFML.NET when it comes to these wrappers.)

class RectangleShape : Shape {
    this(Vector2f size) {
        _size = size;
        setSize(_size);
    }

    Vector2f getSize() {
        return _size;
    }

    void setSize(Vector2f size) {
        _size = size;
    }

    override {
        size_t getPointCount() {
            return 4;
        }

        Vector2f getPoint(size_t index) {
            final switch (index) {
                case 0:
                    return Vector2f(0, 0);
                case 1:
                    return Vector2f(_size.x, 0);
                case 2:
                    return Vector2f(_size.x, _size.y);
                case 3:
                    return Vector2f(0, _size.y);
            }
        }
    }

    private Vector2f _size;
}

As you can see, the Rectangle class only overrides the getPointCount and getPoint methods.

These are the methods that the superclass - Shape - will use to construct the shape object for it to actually be drawable.

Now, let us add the following code to the Shape class so that we can construct a Shape via these two methods, which we assume that the child provides us a good implementation for:

class Shape : Transformable, Drawable {
    this() {
        ptr = sfShape_create(&getPointCount, &getPoint, cast(void*)this);
    }

    extern(C) private static ulong getPointCount(void* data) nothrow {
        return (cast(Shape)data).getPointCount();
    }

    extern(C) private static sfVector2f getPoint(size_t index, void* data) nothrow {
        return (cast(Shape)data).getPoint(index).to_sfVector2f_noThrow();
    }

I hear you asking, what's going on here?

We are providing two callbacks to the getPointCount and getPoint methods via function pointers, and we're passing in the current object to the data void* pointer. It's kind of hard to understand, but if you read through it carefully you should get a rough idea of what's going on.

Now, when we create a new instance of Rectangle, I will assume that the constructor will be called, the sf_shape ptr will be initialized correctly (as it will be utilizing the crucial getPoint and getPointCount methods) and everything will be OK.

This is the following test code I had:

void main() {
    loadSFML();

    RectangleShape rectangleShape = new RectangleShape(Vector2f(50, 50));
    rectangleShape.setPosition(Vector2f(50, 50));
    rectangleShape.setFillColor(Color.Blue);

    RenderWindow renderWindow = new RenderWindow(sfVideoMode(500, 500), "Tests", sfWindowStyle.sfDefaultStyle, null);
    sfEvent event;

    while (renderWindow.isOpen()) {
        while (renderWindow.pollEvent(&event)) {
            if (event.type == sfEventType.sfEvtClosed) {
                renderWindow.close();
            }
        }

        renderWindow.clear(Color.Yellow);
        renderWindow.ptr.sfRenderWindow_drawShape(rectangleShape.ptr, null);
        renderWindow.display();
    }
}

I would read through this line by line to get a good idea of what's going on.

Really, for demonstration purposes, we're using the renderWindow's ptr variable for drawing. When I can get this to work I will create wrapper functions so that it's nicer to use, but for now it's not important.

What I'd expect to pop up on screen is a 50x50 rectangle, filled with a blue color, at the position 50x50 on the screen.

Upon running the application, I don't see anything -- it's just a yellow screen.

I am very confused why this is the case, it seems like I've done everything fine, but I've obviously made a mistake somewhere in my implementation. I don't know specifically if it's an issue on my end, or a bug in bindbc-sfml, but this issue has infuriated me, because I am not getting what I expected to show up on screen.

2

There are 2 best solutions below

0
thebluepandabear On BEST ANSWER

Fixed it by calling sfShape_update here:

class RectangleShape : Shape {
    this(Vector2f size) {
        _size = size;
        setSize(_size);
        
        ptr.sfShape_update();
    }
0
Drifton Aloft On

i've been working on a similar project well more of a implementation of the old DSFML library to work with bindbc-sfml bindings, the way the DSFML Library handled the shape class to treat it as a VertexArray in the Shape class reimpliment the Shape algorithms in the various sub classes Like Rectangle and then call the VertexArray DrawCall with render window as a reference.

Implementing the Shape class to use VertexArrays allows for you to create custom transformable shapes you derived yourself from shape

nudsfml is the library i've been working on - https://github.com/driftonaloft/nudsfml ) !!(audio and networking are not well tested or probably really working) dsfml - https://github.com/Jebbs/DSFML as reference