I'm making a small game using SFML 2.2
I'll start with my code. As I already had about 4000 lines of working code I made a texture manager for storing all textures that are used at a time. I used texures earlier of course but I needed a way to manage them. It looks like this:
//Declarations
class Manager
{
private:
static map<string, Texture > textures;
public:
static Texture & add(const string & name, const string & directory);
static Texture & resolve(const string & name);
};
//Definitions
Texture & Manager::add(const string & name, const string & directory)
{
// Check, whether a said texture was already maped
map<string, Texture>::const_iterator it = textures.find(name);
if (it != textures.end())
{
cout << "Said element was loaded" << endl;
return textures.at(name);
}
// Adding a new texture
Texture tex;
tex.loadFromFile(directory);
textures[name] = tex;
cout << "Texture added" << endl;
return textures.at(name);
}
Texture & Manager::resolve(const string & name)
{
cout << "Resolved" << endl;
return textures.at(name);
}
map<string, Texture > Manager::textures;
As you can see I have a global map accesed by two methods. Both methods return a reference to a certain texture. So as my project had already quiet a lot of textures I decided to check if my manager works fine and replaced loading one texture with method add and binding it to sprite with resolve. As I compiled code and run it everything worked good untill I went back to menu of my game and decided to "play again". Now, when it came to load the texture again, method add correctly recognized name (key) as already used and method resolve returned correctly texture. At least "Resolved" appeared on the screen. And this is the point when error ocures.
But when I moved calling add from method, where the texture is used to the constructor of the class that uses it, it works fine again. And it's called the same amount of times; once per "play game".
Here is the not working code:
void Engine::set_up(int & lvl)
{
srm::Manager::add("Loading screen", "data/loading_screen.png");
Sprite spr;
spr.setTexture(srm::Manager::resolve("Loading screen"));
// ...
}
Also not working code:
void Engine::set_up(int & lvl)
{
Sprite spr;
spr.setTexture(srm::Manager::add("Loading screen", "data/loading_screen.png");
// ...
}
And working:
//Constructor
Engine(){ srm::Manager::add("Loading screen", "data/loading_screen.png"); }
//Method
void Engine::set_up(int & lvl)
{
Sprite spr;
spr.setTexture(srm::Manager::resolve("Loading screen"));
// ...
}
I feel I made a mistake in my Manager. Here is what the error says:
Microsoft Visual C++ Runtime Library - Error
There is used word "heap" so it just confirmed me that those error is related to my Manager. I made a research in the web and I have found this:
Debug Assertion Failed: _CrtIsValidHeapPointer(pUserData)
I understand what cause the error but I can't understand where my mistake is. The weirdest thing is that those calling add in constructor works and calling it elsewhere doesn't!
I'm sorry if something isn't clear, I'll answer your questions then.
Does anybody know any solution to this issue? Can you explain me, what's wrong in my code? Thank you in advance!
EDIT
Ok, I tracked stack in debug mode and found the culprit. These are sf::Text objects which have in field value
Information not avaliable, no symbols loaded for
sfml-graphics-d-2.dll
and in every test it appears in other Text object. Always there are problems with strings, it's always "Unable to read memory" in string parts of my texts. I don't know why, for me it's not even related to my problem. However, when I changed my code back to this:
void Engine::set_up(int & lvl)
{
//srm::Manager::add("Loading screen", "data/loading_screen.png");
Texture texture;
texture.loadFromFile("data/loading_screen.png");
Sprite spr;
spr.setTexture(texture);
//spr.setTexture(srm::Pojemnik::zwroc("Ekran wczytywania"));
so a classic local sf::Texture object and binding it to sprite, everything works perfectly with no problem.
So as I said, when I put
srm::Manager::add("Loading screen", "data/loading_screen.png");
to the constructor or when I don't use my Manager class at all everything works, when I want to use my code - it hangs.
Related
This is my texture manager class.
class TextureManager
{
public:
static std::map<std::string, Texture2D> Textures;
static Texture2D LoadTexture(const GLchar *file, GLboolean alpha,
std::string name);
// Retrieves a stored texture
static Texture2D GetTexture(std::string name);
// Properly de-allocates all loaded resources
static void Clear();
private:
TextureManager() { }
// Loads a single texture from file
static Texture2D loadTextureFromFile(const GLchar *file, GLboolean
alpha);
};
what i plan to do is to give the path of the image as the string parameter of the map .
when i need to load another image i will first check the Map with the given path if the image is loaded or not.
I have two questions.
1) Is this a acceptable work flow to check if the image is loaded or not
2) Can we use the path of the image as a string value of the map.
First, before I even answer the question, I'd strongly recommend NOT making the member vars and functions of this class static. I know initially it may seem sensible to maintain a single global set of textures, however in many cases this quickly becomes a little rubbish.
For example, let's say I have textures for a level in a game, and a set for the GUI. I then need to dump the old level textures, and load the new set for the next level, without touching those for the GUI. If all the textures exist within a single object, then it will require a little bit of work to figure out which ones I need to delete. If however they are in two texture managers (one for the GUI, one for the level), then all I need to do is delete the texture manager for the level, and create a new one for the next level.
Your current design (simply nuke all the textures) would cause havok for any loading screens / GUI textures that may be present when loading a new level.
There is nothing fundamentally wrong with using the file path as a key for your texture lookup, BUT there are a few edge cases you may need to address before it will become a robust class:
Convert all backslashes to forward slashes: e.g. C:\files\foo.jpg ---> convert to ---> C:/files/foo.jpg. This avoids the issue on windows that you can use / or .
On Windows ONLY, convert all the characters to lowercase. i.e. "C:/foo.txt", "c:/FoO.TxT", etc: they all refer to the same file. On linux/mac, those are different files. On windows they are the same file.
Beware of relative v.s. absolute paths. Ideally you'd ONLY ever use relative paths, and those would be your keys. This avoids the issue of loading C:/files/foo.jpg and ./foo.jpg.
Beware of "./foo.jpg" and "foo.jpg"
If you can handle those cases, then it should work reliably. I would however suggest a slight change to the API for purely performance reasons:
typedef uint32_t UniqueTextureId;
class TextureManager
{
private:
// increment and return for each new texture loaded.
// zero should indicate an invalid texture
UniqueTextureId m_idGenerator = 0;
// a lookup to turn the file path into a unique ID
std::unordered_map<std::string, UniqueTextureId> m_filePathToId;
// a lookup to get the texture from an ID
std::unordered_map<UniqueTextureId, Texture2D> m_idToTexture;
// a method to take a raw filepath and...
// 1. convert \ to /
// 2. make lower case on windows
// 3. convert any absolute paths to relative paths
// 4. If a paths starts with ./, remove the first two chars.
std::string tidyUpFilePath(std::string str);
// turn a file path into an integer
UniqueTextureId filePathToId(std::string messyPath)
{
std::string tidyPath = tidyUpFilePath(messyPath);
const auto& it = m_filePathToId.find(tidyPath);
if(it != m_filePathToId.end())
{
return it->second;
}
return 0;
}
public:
// return a unique ID for this texture (which can probably be
// unique only within the current TextureManager).
UniqueTextureId LoadTexture(const GLchar *file, GLboolean alpha, std::string name);
// Do not return a Texture2D object! (return a pointer instead)
// if you return a copy of the texture, there is a good chance
// the destructor will end up calling glDeleteTextures each
// time the returned object is destroyed, which will cause a right
// bother for the next frame!
const Texture2D* GetTexture(UniqueTextureId id)
{
const auto& it = m_idToTexture.find(id);
if(it != m_idToTexture.end())
{
return &it->second;
}
// Just to guard against the case where the id is invalid.
return nullptr;
}
};
One other word of caution. If you are in the situation where there may be multiple calls to loadTexture(), you should probably consider reference counting the textures. This would allow you to have a nice symmetric deleteTexture() method (where it first decrements the ref count, and only deletes the texture when the ref count hits zero).
I'm entirely new to SDL 2 , and I'm hoping to find some help with making my very first proper program for a class in it. We've been provided with some code already for use in this project, which is why I'm not simply using a BlitSurface function to make this solution. If that is indeed the better solution, I'll switch over to that. This is part of a State to be used when the program runs, showing a title image.
I am getting a break error due to a pointer issue in the following code:
void MenuState::Enter()
{
//Is to load the title image used for the State
Sprite* extBackgroundSprite = met_extSystem.met_pointextSpriteManager- >CreateSprite("../assets/Testimage1.bmp" , 0 , 0 , 768 , 1024);
}
Which refers to a Sprite made by a SpriteManager class and CreateSprite function, as seen here:
Sprite * SpriteManager::CreateSprite(const std::string & point_stringFilePath, int point_intX, int point_intY, int point_intWidth, int point_intHeight)
{
auto iter = met_arraypointextTextures.find(point_stringFilePath); //breaks here
if (iter == met_arraypointextTextures.end())
//If the iterator cannot locate the sprite we need in our already loaded memory,
//it needs to be loaded into our map to create pointers
{
SDL_Surface* extSurface = SDL_LoadBMP(point_stringFilePath.c_str());
SDL_Texture* extTexture = SDL_CreateTextureFromSurface(met_pointextRenderer, extSurface);
SDL_FreeSurface(extSurface);
met_arraypointextTextures.insert(std::pair<std::string, SDL_Texture*>(point_stringFilePath, extTexture));
iter = met_arraypointextTextures.find(point_stringFilePath);
}
//Creates the sprite, adds a new index point via pushback
Sprite* extSprite = new Sprite(iter->second, point_intX, point_intY, point_intWidth, point_intHeight);
met_arraypointextSprites.push_back(extSprite);
return extSprite;
}
I hope this is enough information and code to present my problem. If not, let me know! And thank you in advance.
Turns out the issue was impossible to solve with the information I provided. The pointer did indeed need to be initialized, but with arguments found in the constructor, which I had not provided here.
i got a ridiculous problem.
i have a class within inside an array member.i have a get method and a set method for the array.
the problem is that i call the set(to update) method to change the variables within the array and i see with the debugger that the variables do actually update.then when i call immediately the get method just after the set method i found the variables of the array been changed back to their ancient values.
here is the code approximately :
object.updatFunction();//sort of set method
//nothing in between
Type variable=object.getFunction();
added code:
void Cube::updtCornersNextToCentr()
{
int iHalfSide=m_SideSize/2;
int centerX(m_Center.x()),centerY(m_Center.y()),centerZ(m_Center.z());
m_CubeCornerVertices[0].setX(centerX-iHalfSide);
m_CubeCornerVertices[0].setY(centerY+iHalfSide);
m_CubeCornerVertices[0].setZ(centerZ-iHalfSide);
m_CubeCornerVertices[1].setX(centerX+iHalfSide);
m_CubeCornerVertices[1].setY(centerY+iHalfSide);
m_CubeCornerVertices[1].setZ(centerZ-iHalfSide);
//.......
m_CubeCornerVertices[7].setX(centerX+iHalfSide);
m_CubeCornerVertices[7].setY(centerY-iHalfSide);
m_CubeCornerVertices[7].setZ(centerZ+iHalfSide);
}
QVector3D * Cube::getCubeCornerVertices()const
{
static QVector3D temp[8];
for(int i=0;i<8;i++)
{
temp[i]=m_CubeCornerVertices[i];
}
return &temp[0];
}
The problem was really ridiculous, i didn’t want to let this post ambiguous.it’s a very beginner fault and it’s all about a missing « & » that caused me to update a copy.
Actually i did above simplify write the next code :
object.updatFunction();//sort of set method
//nothing in between
Type variable=object.getFunction();
And the more real code was something like:
m_WorldSpace->getCube().updtCornersNextToCentr()
//nothing in between
const QVector3D corners[8]=m_WorldSpace->getCube().getCubeCornerVertices();
all the problem was in the getCube() function which instead of being something like this :
Cube& WorldSpace::getCube()//here is the missing "&"
{
return m_Cube;
}
I wrote this
Cube WorldSpace::getCube()//this caused to get and update just a temporary copy
{
return m_Cube;
}
someone can say that i got multiple levels of getters which blinded me.
thank you.
According to the documentation, one can load an sf::Texture three different ways: from file, from stream, from memory. I think I need the latter, but I'm not even sure about that. And if I do, I can't figure out how to use it.
I have a method, which, given an url to work with, needs to return an sf::Sprite. I'm successfully downloading the binary data, and storing it in a string (since Http::Response::getBody() returns that). I know for a fact, that this is the right data, because if I write and save it to a file, I can view the image. I don't want a file however, I just need the image displayed.
Here is the function:
sf::Sprite Downloader::GetPicture(const std::string &url)
{
sf::Http http;
std::string host = url.substr(0, url.find("net/")+4 );
std::string uri = url.substr(url.find("net/")+4, url.size());
http.setHost(host);
request.setUri(uri);
response = http.sendRequest(request);
std::string data = response.getBody();
//THIS IS WRONG
sf::Texture texture;
texture.loadFromMemory((void*)data, sizeof(data));
return sf::Sprite(texture);
/* THIS WORKS, BUT I DON'T WANT TO SAVE, REOPEN, THEN DELETE AT THE END
std::ofstream fileout("test.jpg", std::ios::binary);
fileout << data;
fileout.close();
*/
}
Here is loadFromMemory's signature for the lazy (the void* confuses me, that's probably the problem).
Also, this might be a completely wrong way to do it; maybe extending sf::InputStream, and using loadFromStream?
The problem is, returned sf::Sprite has 0×0 dimension and 0 size, that's how I know it's wrong.
There are two mistakes in your code. One was spotted by Casey, that is you should use texture.loadFromMemory(data.data(), data.length()).
The problem with texture.loadFromMemory((void*)data, sizeof(data)); is that you will load some garbage and not necessarily the picture data; you will load the other attributes of the string (like an integer for the length, and maybe a pointer for the data itself).
The other one is related to the white square problem: you return a sprite but not the texture. After the function returns, the texture is destroyed and the sprite will display only a white rectangle instead.
You could do something like:
sf::Texture dlpicture(std::string host, std::string uri)
{
sf::Http http(host);
sf::Http::Request request(uri);
auto response = http.sendRequest(request);
auto data = response.getBody();
sf::Texture text;
text.loadFromMemory(data.data(), data.length());
return text;
}
which you can try with sf::Texture texture = dlpicture("www.sfml-dev.org", "download/goodies/sfml-logo-small.png");.
SFML indeed support jpg format but not all its variants. Progressive jpg is not supported for example. You'll have to use another format.
I've had to completely rewrite this problem as I've found out a lot more about it now.
Background:
My programme is drawing some 3d objects under directx11. I have a class that contains the data, pointers, and functions needed to draw the required 3d objects. Everything was working well. I could create many different 3d objects and draw them wherever I wanted. Great!
Then I needed to put them in a container and into a vector so I didn't have to create each object manually, this was where the trouble started; it would crash 1 time in 5 or so.
Unhandled exception at 0x00C308C1 in SpritesNTextN3D.exe: 0xC0000005: Access violation reading location 0xFFFFFFFF.
It crashed when using vectors and maps. I continued this line of enquiry and tried using a pointer and new:
ThreeD_Cube* threed_cube_p;
threed_cube_p = new ThreeD_Cube;
This also caused it to crash when I ran its draw function.
threed_cube_p->draw(threeD, camera, d3dContext_mp);
However if created as a standard object:
ThreeD_Cube threed_cube_;
The draw function never crashes.
threed_cube_-.draw(threeD, camera, d3dContext_mp);
Likewise, creating a pointer to threed_cube_ works as expected.
Question:
What is new doing that the default constructor isn't. Is there anything I should be looking at to resolve this problem?
It seems you have a good constructor, but bad/insufficient (default) assignment operator and bad/insufficient (default) copy constructor.
Let's see why some parts of your code works but some not:
//threed_cube_vec[0].draw(threeD, camera, d3dContext_); // doesnt work!!!
It tells you what's in threed_cube_vec[0] is a bad/corrupted object.
ThreeD_Cube test = threed_cube_vec[0]; // But, if I copy it...
In this line, (for some reason) firstly the constructor is called, which gives you a good object. Then the "=" is called, partially the object is modified, but the object is still good since it was already good before the "="
ThreeD_Cube* test = &threed_cube_vec[0];
As for a pointer, it is the essentially object threed_cube_vec[0] itself, so still corrupted.
ThreeD_Cube test = threed_cube_vec[0];
vector<ThreeD_Cube> test2;
test2.push_back(test);
test2[0].draw(threeD, camera, d3dContext_);
This does not fixed the problem as you said. "test" is a good object, but when you push_back(test) into test2, a copy is pushed back [if you change it to test2.push_back(std::move(test) , the problem could be gone]. Since the copy constructor is incomplete, the object in test2[0] is corrupted. Similar scenario happens with your map.
Conclusion: if an object is originated from the constructor, you get a good object; if an object is originated from a copy constructor, it is corrupted.
A quick test you can do: resize your vector after you declare it, the error should be gone temporarily.
Crash after m = XMMatrixIdentity() - aligment memory in classes?
This topic covers the answer to my problem. I eventually tracked it down to XMMATRIX causing crashes with new due to memory alignment.
Here's a shortened version of the problem:
void matrix_test_pointer()
{
XMMATRIX* xmmatrix_p;
xmmatrix_p = new XMMATRIX;
*xmmatrix_p = XMMatrixIdentity(); // this is where it crashes
}
void matrix_test()
{
XMMATRIX xmmatrix;
xmmatrix = XMMatrixIdentity();
}
int main()
{
string wait;
matrix_test();
cout << "matrix_test() completed.\n";
matrix_test_pointer();
cout << "matrix_test_pointer() completed.\n"; // If it does you are lucky :)
cin >> wait;
return 0;
}
Chances are matrix_test will complete but it will crash before pointer will complete.