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).
Related
I have an ID3D11Texture2D. The end goal is to take the image and embed it into html as a base64 encoded string (requirements are based on existing functionality of which I have no control). I can achieve this by saving the texture to file using DirectX::SaveWICTextureToFile, loading the texture as a byte array, then encoding the array in base64, but I'd like to do it without saving to a file if possible.
Ideally there would be some kind of SaveWICTextureToMemory function that can take the container format as a parameter, but so far I haven't found anything that deals with the format. I've checked out CreateWICTextureFromMemory and other functions in the WICTextureLoader package, but I didn't find exactly what I'm looking for (perhaps I missed it). Figuring SaveWICTextureToFile must generate a byte array to write to file, I tried to essentially unpack that function and strip away the file creation elements of it, but decided there was likely a simpler solution, which brought me here.
I don't have much code to look at, as I've only stubbed out many of the key functions, but this is generally where this stuff needs to live:
HRESULT ExportImages::GetAngleEncodedString(std::string& encoded, const DirectX::XMMATRIX& projectionMatrix, float radians) const
{
// Draws geometry to m_Texture2D
DrawAngle(projectionMatrix, radians);
unsigned const char* pngBytes = GetPNGBytes(m_Texture2D);
try
{
encoded = Base64Encode(pngBytes);
}
catch (...)
{
// TODO don't catch all, add resolution
return HRESULT(-1);
}
return S_OK;
}
unsigned const char* ExportImages::GetPNGBytes(ID3D11Texture2D* texture) const
{
// ???
}
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.
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'm building a simple generic engine for my true start in the making of games, and I am trying to be somehow organized and decent in the making of my engine, meaning I don't want it to be something I throw to the side once I make what I'm planning to.
I add objects to be displayed, drawObjects, and these can either move, not move, and have an animation, or not have one.
In case they DO have an animation, I want to initialize a single animationSet, and this animationSet will have xxx animationComp inside of it. As I'm trying to be neat and have worked abit on "optimizations" towards memory and cpu usage (such as sharing already-loaded image pointers, and whatever came across my mind), I wanted to not ask for possibly unused memory in arrays.
So I had animationSetS* animationSet = NULL; initially, planning to do a animationSet = animationSetS[spacesINEED]; after, only on the objects that needed animation that I added, being those that aren't animations a NULL and therefore not using memory (correct?).
And then this question popped up! (title)
struct animationComp {
SDL_Rect* clip;
int clipsize;
};
struct animationSetS {
animationComp* animation;
int currentFrame;
int currentAnimation;
int animationNumber;
};
struct drawObject { // Um objecto.
char* name;
SDL_Surface* surface;
bool draw = true;
float xPos;
float yPos;
bool willMove = false; // 0 - Won't move, 10 - Moves alot, TO IMPLEMENT
bool isSprite = false;
animationSetS* animationSet;
};
I dabble alot in my questions, sorry for that. For any clarifications reply here, I'll reply within 10 minutes for the next... 1 hour perhaps? Or more.
Thanks!
Setting the pointer to NULL means that you'll be able to add ASSERT(ptr != NULL); and KNOW that your pointer does not accidentally contain some rubbish value from whatever happens to be in the memory it was using.
So, if for some reason, you end up using the object before it's been properly set up, you can detect it.
It also helps if you sometimes don't use a field, you can still call delete stuff; [assuming it's allocated in the first place].
Note that leaving a variable uninitialized means that it can have ANY value within it's valid range [and for some types, outside the valid range - e.g. pointers and floating point values can be "values that are not allowed by the processor"]. This means that it's impossible to "tell" within the code if it has been initialized or not - but things will go horribly wrong if you don't initialize things!
If this should be really implemented in C++ (as you write), why don't you use the C++ Standard Library? Like
struct animationSetS {
std::vector< std::shared_ptr<animationComp> > animation;
// ...
}
when i run my program it compiles fine but when it runs i get this box which stops my program. the box says this Unhandled exception at 0x0039e9a7 in Mon.exe: 0xC0000005: Access violation reading location 0xcdcdcdcd. i don't even know what that means. when it breaks it point at this function.
void CXFileEntity::SetAnimationSet(unsigned int index)
{
if (index==m_currentAnimationSet)
return;
if (index>=m_numAnimationSets)
index=0;
// Remember current animation
m_currentAnimationSet=index;
// Get the animation set from the controller
LPD3DXANIMATIONSET set;
IT BREAKS HERE>>>>m_animController->GetAnimationSet(m_currentAnimationSet, &set );
// Note: for a smooth transition between animation sets we can use two tracks and assign the new set to the track
// not currently playing then insert Keys into the KeyTrack to do the transition between the tracks
// tracks can be mixed together so we can gradually change into the new animation
// Alternate tracks
DWORD newTrack = ( m_currentTrack == 0 ? 1 : 0 );
// Assign to our track
m_animController->SetTrackAnimationSet( newTrack, set );
set->Release();
// Clear any track events currently assigned to our two tracks
m_animController->UnkeyAllTrackEvents( m_currentTrack );
m_animController->UnkeyAllTrackEvents( newTrack );
// Add an event key to disable the currently playing track kMoveTransitionTime seconds in the future
m_animController->KeyTrackEnable( m_currentTrack, FALSE, m_currentTime + kMoveTransitionTime );
// Add an event key to change the speed right away so the animation completes in kMoveTransitionTime seconds
m_animController->KeyTrackSpeed( m_currentTrack, 0.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );
// Add an event to change the weighting of the current track (the effect it has blended with the secon track)
m_animController->KeyTrackWeight( m_currentTrack, 0.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );
// Enable the new track
m_animController->SetTrackEnable( newTrack, TRUE );
// Add an event key to set the speed of the track
m_animController->KeyTrackSpeed( newTrack, 1.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );
// Add an event to change the weighting of the current track (the effect it has blended with the first track)
// As you can see this will go from 0 effect to total effect(1.0f) in kMoveTransitionTime seconds and the first track goes from
// total to 0.0f in the same time.
m_animController->KeyTrackWeight( newTrack, 1.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );
// Remember current track
m_currentTrack = newTrack;
}
any idea?
UPDATE
this is the class
class CXFileEntity
{
private:
LPDIRECT3DDEVICE9 m_d3dDevice; // note: a pointer copy (not a good idea but for simplicities sake)
// Direct3D objects required for animation
LPD3DXFRAME m_frameRoot;
LPD3DXANIMATIONCONTROLLER m_animController;
D3DXMESHCONTAINER_EXTENDED* m_firstMesh;
// Bone data
D3DXMATRIX *m_boneMatrices;
UINT m_maxBones;
// Animation variables
unsigned int m_currentAnimationSet;
unsigned int m_numAnimationSets;
unsigned int m_currentTrack;
float m_currentTime;
float m_speedAdjust;
// Bounding sphere (for camera placement)
D3DXVECTOR3 m_sphereCentre;
float m_sphereRadius;
std::string m_filename;
void UpdateFrameMatrices(const D3DXFRAME *frameBase, const D3DXMATRIX *parentMatrix);
void UpdateSkinnedMesh(const D3DXFRAME *frameBase);
void DrawFrame(LPD3DXFRAME frame) const;
void DrawMeshContainer(LPD3DXMESHCONTAINER meshContainerBase, LPD3DXFRAME frameBase) const;
void SetupBoneMatrices(D3DXFRAME_EXTENDED *pFrame/*, LPD3DXMATRIX pParentMatrix*/);
public:
CXFileEntity(LPDIRECT3DDEVICE9 d3dDevice);
~CXFileEntity(void);
bool Load(const std::string &filename);
void FrameMove(float elapsedTime,const D3DXMATRIX *matWorld);
void Render() const;
void SetAnimationSet(unsigned int index);
void NextAnimation();
void AnimateFaster();
void AnimateSlower();
D3DXVECTOR3 GetInitialCameraPosition() const;
unsigned int GetCurrentAnimationSet() const {return m_currentAnimationSet;}
std::string GetAnimationSetName(unsigned int index);
std::string GetFilename() const {return m_filename;}
};
im releasing m_animController in the destructor
0xC0000005: Access violation reading
location 0xcdcdcdcd.
Well it's simple :
Access violation is an error where you tried to access some memory that are allowed to be accessed. For example you might be trying to use a null pointer.
Here the 0xcdcdcdcd part is a special adress put by visual studio when a pointer have been deleted (or more precisely, the object pointed by the pointer is destroyed and the pointer is set to this value) or non-initialized > (edit: I'm not sure anymore, will have to check the VS documentation). That's true only in Debug mode if my memory is correct.
So here, m_animController is in a wrong state (as it seems that it's the unique pointer of the expression where it crashes).
First, I would suggest that you make sure that delete have not been called before on this pointer. It might be obvious or it might be that you call delete in the object destructor and you didn't see that this object have bee destroyed or copied. If your object (CXFileEntity) must not be copied, make sure it can't be (by inheriting from boost::noncopiable or by putting it's copy constructor and copy operator in private with no implementation).
Next, if you really have to use raw pointers (instead of references or smart pointers) you'd better check all your pointers (and other contextual values) at the start of each function, using assertions (look for assertions in google and stackoverflow, there is a lot of discussions about that). Even better: check that pointers are valid each time you get them from functions. That will help you track early when a a pointer got in a state you didn't expect.
You attempted to access memory to which you do not have access. Most commonly this is because you are using a pointer that you have not initialized and it points to an address that's not in your process's memory space.
You should make sure that m_animController is initialized (and pointing to a valid object) before you dereference it with the -> operator.
You have a bad pointer somewhere. Based on the line where it breaks, I'm guessing m_animController has a bad value. Check that m_animController is initialized to a default value. And make sure you aren't deleting it somewhere before using it.
Note: 0xcdcdcdcd is a common value that some debuggers will initialize invalid pointers with, but it's not guaranteed.