Skip to main content
gamedev.net
🔒 Locked

Resource Manager

Started by mcguile25 Sep 19, 2005 at 10:01 AM 5 replies 1.6k views
Original Post
mcguile25
mcguile25
Hi, I'm trying to think of a good way to design a resource manager for my 3D game -- managing textures, sprites, models, fonts, etc. Did some google searches and searches here on gamedev and found some help. I'm assuming I would have a CResource to abstract what a resource is and then a CResourceManager to hold all allocated resources? I'm a bit confused when it comes to how the resources get created/returned to the programmer. Does the programmer ask the resource manager if a resource with X filename has been allocated and if so the manager returns a pointer to it? And what about if the resource doesn't exist. The programmer would have to load it manually and then pass off a pointer back to the manager? And what about deleting resources? Code samples are appreciated and so is general advice ;)
Raeldor
Raeldor
I actually used a template class to create a generic resource manager for shared resources. This can be used to manage resources for any class as long as it inherits from the SharedResource class. The template class uses a hash table (plenty of references to how this works, just google "hash table") to keep track of resources by name.

Here is the resource manager class...
	//	// shared resource class.  resources have resource names to identify them	//	class SharedResource	{	protected:		std::string m_resourceName;		// name that uniquely identifies resource										// usually filename		SharedResource(std::string in_resourceName) // protected, cannot instance this class		{			// save name			m_resourceName=in_resourceName;			// usage count is zero by default			UsageCount=0;		}	public:		__forceinline std::string GetResourceName() {return m_resourceName;}		virtual void Release()=0;		int UsageCount;			// number of references to this resource	};


Here is the resource manager template class...
	//	// if we ARE going to have a resource manager, then let's subclass from the hash	// table object and just have it operate on the objects loadfromfile, and	// usagecount and release methods and properties	//	//	// shared resource manager class	//	// this is for objects that are shared not instanced.  the same object is used	// multiple times and retains a usage count.  this is implemented using a	// simple hash table	//	template <class T> class SharedResourceManager	{	protected:		// do not use smart pointers, usage count should exclude manager		// otherwise we are hanging on to resources at the end of the program		typedef HashEntry< std::string, SmartPointer<T> > Entry;		// use searchable array that can store name and do fast find (hash table?)		HashTable<std::string, SmartPointer<T> > m_resources;	public:		// constructor		SharedResourceManager(DWORD in_size):m_resources(in_size, &NWHash)		{		};		// get without incrementing usage		T* GetResource(std::string in_name)		{			// try and find object in hash table first			Entry* entry=m_resources.Find(in_name);			if (entry != NULL)				return entry->Data;			return NULL;		};		// find or return null		T* UseResource(std::string in_name)		{			// try and find object in hash table first			Entry* entry=m_resources.Find(in_name);			if (entry != NULL)			{				// increase usage count and return object				entry->Data->UsageCount++;				return entry->Data;			}			return NULL;		};		// create resource		T* CreateResource(std::string in_name)		{			// make sure name doesn't already exist!			Entry* entry=m_resources.Find(in_name);			if (entry != NULL)				return NULL;			// create new resource			T* newResource=new T(in_name, this);//			SmartPointer<T> newResource=new T(in_name, this);			m_resources.Insert(in_name, newResource);			newResource->UsageCount++;			return newResource;		};		// decrement count and release when 0		// THIS SHOULD ONLY BE CALLED BY RESOURCES RELEASE FUNCTION!		void ReleaseResource(std::string in_name)		{			// try and find object in hash table first			Entry* entry=m_resources.Find(in_name);			if (entry != NULL)			{				// ensure resource usage count > 0				assert(entry->Data->UsageCount > 0);				// decrement usage count				entry->Data->UsageCount--;				// if usage count is zero				if (entry->Data->UsageCount == 0)				{					// delete from resource list and release resource					// do not delete, using smart pointers, so just null					m_resources.Remove(in_name);				}			}		};		int GetUsageCount(std::string in_name)		{			// try and find object in hash table first			Entry* entry=m_resources.Find(in_name);			if (entry != NULL)			{				return entry->Data->UsageCount;			}			// not found			return 0;		};		// get total number of resource		int GetResourceCount()		{			return m_resources.Count;		};	};

mcguile25
mcguile25
Thank you, that really helps. I'll probably implement mine like that. I'm also curious to see how others are doing it, anyone else want to show their resource manager code/pseudocode?
Telastyn
Telastyn
A far less elegant implimentation of what is essentially the same thing. This was done before I learned templates or much of the Library.

struct  d3d_texture{        IDirect3DTexture9       *tptr;        unsigned long           ref;        char                    **filename;};list<d3d_texture *>                     textures;hash_map<string,d3d_imagerc *>          image_definition_table;struct  d3d_imagerc{        string                  rc;        char                    *filename;        float                   minu;        float                   maxu;        float                   minv;        float                   maxv;        void                    setuv(d3d_tq *);        d3d_imagerc             (string inirc, const char *infilename, float inminu=0, float inmaxu=1, float inminv=0, float inmaxv=1){                rc=inirc;                filename=(char *)malloc(cmdlen);                bzero(filename,cmdlen);                strncpy(filename,infilename,cmdlen-1);                minu=inminu;                maxu=inmaxu;                minv=inminv;                maxv=inmaxv;                image_definition_table[rc]=this;        }        d3d_imagerc             (){                rc="unknown";                filename=0;                minu=0;                maxu=1;                minv=0;                maxv=1;                image_definition_table[rc]=this;        }};IDirect3DTexture9       *texturefromfile(char **);void                    unloadtexture(IDirect3DTexture9 *);IDirect3DTexture9       *loadtexture(string,d3d_tq *);IDirect3DTexture9               *texturefromfile(char **infilename){//// Load texture from filename and return it.//d3d_texture                     *t;list<d3d_texture *>::iterator   v;HRESULT                         e;string                          st;for (v=textures.begin();v!=textures.end();++v){        t=*v;        if (*t->filename==*infilename){                t->ref++;                return(t->tptr);        }}t=new d3d_texture;t->ref=1;t->filename=infilename;e=D3DXCreateTextureFromFile(guiroot->d3ddev,*infilename,&t->tptr);// TODO: check for actual errors.if (e!=D3D_OK){        st=string("")+ "texturefromfile: Failed to load file: " + *infilename;        console->newtext(st.c_str());        delete t;        return(0);}textures.push_back(t);return(t->tptr);}void                            unloadtexture(IDirect3DTexture9 *intptr){//// Unload [or dereference] d3d_texture with matching intptr//list<d3d_texture *>::iterator            v;d3d_texture     *t;if (!intptr){return;}for (v=textures.begin();v!=textures.end();++v){        t=*v;        if (t->tptr==intptr){                t->ref--;                if (t->ref){                        return;                }                t->tptr->Release();                delete t;                textures.erase(v);                return;        }}}IDirect3DTexture9               *loadtexture(string inirc, d3d_tq *intq){//// Load texture from image resource identifier and modify intq's texture coords as well.//hash_map<string,d3d_imagerc *>::iterator                v;d3d_imagerc                                             *ircptr;IDirect3DTexture9                                       *rtn;char                                                    *buf;for (v=image_definition_table.begin();v!=image_definition_table.end();++v){        if (v->first==inirc){                ircptr=v->second;                ircptr->setuv(intq);                rtn=texturefromfile(&ircptr->filename);                if (!rtn){                        buf=(char *)malloc(cmdlen);                        sprintf(buf,"Unable to load file: %s",ircptr->filename);                        console->newtext(buf);                        free(buf);                }                return(rtn);        }}return(0);}
WhatEver
WhatEver
I'm currently working on a resource manager that loads resources from a file or resources from inside of your program. Names and their associated data are stored in a hierarchy much like you would see in a filemanager. I'm using templates so you can create a tree that can store any type of data.

Here's a sample of it at work:

S3Dvoid RND(){	//the '4' tells the template how large a search cache to use	s3d_resourceman<text_resource, 4> TextManager;	text_resource* TextResource;	//load text from files	TextManager.Load("objects/actors/test.txt", NULL);	TextManager.Load("objects/gliLog.txt", NULL);	TextManager.Load("test.log", NULL);	//set base folder to start seaching in	TextManager.SetFolder("objects/");	//get class containing glilog.txt	TextResource=TextManager.Find("gliLog.txt", true, true);	//TextResource=TextManager.Find("test.txt", true, true);}


text_resource is a class with a Load member function, so when TextManager.Load is called, it passes it on to the templated text_resource::Load function and processes the filename and parameter, which in this case, parameter is NULL.
technic
technic
This might sound a little C# centric but it can be translated to C++ easily: I use a hashtable where the keys are the filename of the object and the value is the data itself. The resource manager class is a singleton which has basically one publically exposed method which returns an object based on a filename. It figures out what kind of file I'm trying to load by the looking at the extension of the file. Psuedo code:

1. First call something like FileManager.GetInstance().Load(filename); This single Load() function returns a C# object which is cast to the correct type by the calling code which is asking for the load. In C++ it could return a void* perhaps.
2. FileManager checks if (filename) is already loaded in the hashtable, if so it returns the already loaded resource immediately.
3. If it's not in the hashtable, it checks the extension to figure out if it's a sound, texture, HLSL .fx file, or model. It loads it, puts it in the hashtable, and returns it.

This has worked out really well for the small projects I've done at home.
paulecoyote
paulecoyote
tangent: CResource? I think you've been coerced by the evils of class wizards with naming classes begining with C [wink]
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.