2

I'm creating a car simulation using SFML.

As a matter of organization and logic, I created a single class "car", which also inherits sf::RectangleShape, and within this class there are other SFML objects, among them a Texture and a method to setup it.

I want to have multiple cars, so I created a vector of class "car".

In this example, I left only 2 cars with the images: "car-red.png" and "car-black.png". enter image description here

Here is an extract from the logic I'm using (I did a test program to make it easier to understand):

#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>
#define debug(x) std::cout << #x << "=" << x << std::endl;

using namespace std;

class car : public sf::RectangleShape {
public:
    string s;
    sf::Texture tex;
    sf::Sprite img;

    void imgInit(string imgFile) {
        tex.loadFromFile(imgFile);
        img.setTexture(tex);
        s = imgFile;
    }
};
int main()
{
    vector<car> vecRet;
    car objRet;

    objRet.imgInit("car-red.png");
    objRet.setSize(sf::Vector2f(150, 70));
    objRet.setFillColor(sf::Color::Yellow);
    vecRet.push_back(objRet);

    objRet.imgInit("car-black.png");
    objRet.setPosition(sf::Vector2f(300, 300));
    objRet.img.setPosition(objRet.getPosition());
    vecRet.push_back(objRet);

    debug(vecRet[0].s);
    debug(vecRet[1].s);
    debug(vecRet[0].img.getTexture());
    debug(vecRet[1].img.getTexture());

    sf::RenderWindow window(sf::VideoMode(500,500), "Window", sf::Style::Close);
    window.setFramerateLimit(120);

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

        window.clear();
        window.draw(vecRet[0]);
        window.draw(vecRet[1]);
        window.draw(vecRet[0].img);
        window.draw(vecRet[1].img);
        window.display();
    }

    return EXIT_SUCCESS;
}

There are two problems I can not solve:


1) Even doing push_back of the two cars, only the last image prevails. Apparently, push_back refers to a single RAM address for the image.

Then the result looks like this: enter image description here

That is, the second image (car-black.png) is probably overlapping the address of the first image.

The curious thing is that this only happens with the Texture class. In the example, the program debug a string within the class and in this case there is no overlap:

vecRet[0].s=car-red.png
vecRet[1].s=car-black.png

However the Texture objects within the class vector are pointing to the same memory address:

vecRet[0].img.getTexture()=000000C57A5FF250
vecRet[1].img.getTexture()=000000C57A5FF250

How to solve this?

2) The second problem is that, for each vecRet.push_back(objRet), the following errors appear in the console:

An internal OpenGL call failed in Texture.cpp(98).
Expression:
   glFlush()
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

What is this?

Rogério Dec
  • 801
  • 8
  • 31
  • Have you checked that e.g. [`loadFromFile`](https://www.sfml-dev.org/documentation/2.5.0/classsf_1_1Texture.php#a8e1b56eabfe33e2e0e1cb03712c7fcc7) was successful? How about other functions you use that could fail? You really must have some error handling! – Some programmer dude Jun 02 '18 at 01:33
  • 1
    The documentation on the copy constructor of sf::Texture is not clear so this might be relevant https://www.sfml-dev.org/tutorials/2.1/graphics-sprite.php#the-white-square-problem – arynaq Jun 02 '18 at 01:40
  • @arynaq That is a good find, and probably the reason for the duplicated image. – Some programmer dude Jun 02 '18 at 02:03
  • @arynaq. Cool, now I understand that what's stored is just the pointer. But what could I do to have the two images in two different areas of RAM with two different addresses? – Rogério Dec Jun 02 '18 at 02:12
  • Since the docs dont say anything about making explicit copies, I guess just make a global storage class somewhere and refer to the textures there is an option. – arynaq Jun 02 '18 at 02:38

1 Answers1

2

Your first problem happens because you only instantiate a single car, but you load both images onto it. The second problem might be caused by the fact that push_back() copies the elements into the vector, resulting in the creation of four cars in total, rather than two.

Try this code:

vector<car> vecRet(2);

vecRet[0].imgInit("car-red.png");
vecRet[0].setSize(sf::Vector2f(150, 70));
vecRet[0].setFillColor(sf::Color::Yellow);

vecRet[1].imgInit("car-black.png");
vecRet[1].setPosition(sf::Vector2f(300, 300));
vecRet[1].img.setPosition(vecRet[1].getPosition());
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • But since the first `push_back` creates a *copy* of the object, the modifications to the original object should not affect the copy in the vector? Perhaps it's a bug with the `sf::Texture` copy constructor? Or that it uses some internal cache and all copies will use the same texture once loaded? I don't know, but the copying should really not be a problem as I see it. – Some programmer dude Jun 02 '18 at 01:29
  • @Someprogrammerdude: the evidence is before us: `car` does not have "value semantics." It behaves more like a pointer. So copying it does not create a new resource. – John Zwinck Jun 02 '18 at 01:41
  • Only partially, considering that the string inside `car` is copied correctly. I would say this is more a problem with SFML, and that a workaround like yours might (unfortunately) be needed. It does prevent something like [the prototype pattern](https://en.wikipedia.org/wiki/Prototype_pattern) though, which the OP is (probably unknowingly) using. – Some programmer dude Jun 02 '18 at 01:52
  • @John Zwinck, in fact this workaround works. But in this case, it would be more practical to use array, would not be? The problem is that I do not have a fixed number of cars, so vector would be the most useful for not limiting the number of items, using `push_back`. Apparently this should be a limitation of `sf::Texture` class. – Rogério Dec Jun 02 '18 at 02:05
  • @RogérioDec: My solution does not in any way limit the number of cars you can have. Just make a `for` loop to populate the vector from `0` to `N`. – John Zwinck Jun 02 '18 at 10:15