Skip to main content
GameDev.net gamedev.net
🔒 Locked

Resource management styles

Started by Ainokea Dec 21, 2004 at 10:00 PM 21 replies 2.8k views
Original Post
Ainokea
Ainokea
From what I have read in the forums there seems to be two diffrent ways of resource management: A) One global resource manager for every type of resource (sound, texture, mesh, etc.) For example the resource manager class can do this: ResourceManager->AddResource("Texture.bmp",RT_TEXTURE) and this: ResourceManager->AddResource("Mesh.wav",RT_MESH). B)One manager for every type of resource. For example the texture manager would do this: TextureManager->AddTexture("Texture.bmp"); and the sound file manager would do this: SoundManager->AddSound("Sound.wav"); What are the pros and cons of each method? If there is another method could you please tell me about it. Thanks.
______________With the flesh of a cow.
Drew_Benton
Drew_Benton
Well those are both good solutions. I don't know how I would implement the first one. It seems that method would require using typecasting and void*'s. I myself use the second method in my game engine. I think that way is a lot easier and more efficient to maintain.

A Pros:
* All in one spot
* Eliminates extra overhead
* Organized and easy to use
* Less code overall

A Cons:
* Hard to implement
* Hard to maintain/debug
* Hard to customize

B Pros:
* Easy to follow logically, no worries about messing up data types
* Easy to implement and maintain/debug
* Easy to customize

B Cons:
* Adds overhead per class
* Must develop a manager per class

If you could effeciently make A, I would do so, I honestly have not thought of making that type of resource manager, so now I'll give that a try. In my current engine, as I said before, I use B, and it works well. The only thing is with B is that it gives you no flexibility, you can add and store the resources easily, but how are you going to use them? It could easily take a turn for the worse, if it is not done right.

I know in one of my resource classes, OGL Font, I have add in the window's hwnd so I can get the HDC. Using A, it would require some customization, which defeats the general purpose of that method. If I use b, as I do now, I can just make a custom Add function per that class and that's it.

I hope this helps - if anything is not clear or I don't make sense, feel free to ask, just tell me [wink].

- Drew
leiavoia
leiavoia
Just off the top of my head:

- if you make seperate managers, you can pocket them into separate "modules". For instance, why include sound info for a (future) game project that didn't have sound? Or a mesh loader for a 2D game? etc. Also, if you had to make a code change, you only make it in one place and only recompile that.

but...

- it's convenient to have all that in one place, you know? Personally, i'd rather have one uber-manager than half dozen smaller ones. I'm sure someone will burn me for saying that. Having one manager would reduce code to some degree because you can, for instance, keep all the loading configuration in one place (i use simple XML to configure resources to load).

Quote:
With the flesh of a cow.


"One side is covered with the skin of a donkey while the other side is covered with the skin of a cow, held taught with string" - Mister One, Prayer Box
Jan Wassenberg
Jan Wassenberg
There's a middle route: a central "handle manager" is responsible for allocating storage for resources, and providing for safe lookup via handle. Separate resource modules, e.g. texture loader, ask h_mgr to allocate an instance of themselves, and then use the handle returned to access their data.

Advantages:
- efficient container (less allocations, and good locality WRT other resources)
- resources can be cached
- hotloading support is possible (if the underlying file has changed, the resource is told to reload itself).

A separate manager scheme would have problems with all of these. Still, these are heavy-duty features most useful in a large game, so you may not need them.


There are more details in another thread.
HTH+HAND+Happy Holidays :)
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
carb
carb
I agree with the poster above.

You could create separate classes for your resource managers, but then have a central manager class that offers entry points into each of them. This way, you can maintain separate implementations, but still have that grand common entry point. Not to mention the added effect of reducing dependencies and build times.

i.e.
ResourceManager->AddResource("Texture.bmp", RT_TEXTURE) calls TextureManager->Load(...)

ResourceManager->AddResource("Mesh.wav", RT_MESH) calls MeshManager->Load(...)

I believe this is a design pattern of some sort ...

- carb
Ben
Jan Wassenberg
Jan Wassenberg
hmm, that's "façade" (provide a common interface to similar subsystems). However, the RT_* scheme actually increases dependencies, insofar as when adding a resource type, you have to recompile all resource-using code.
It is usually better to have service providers register themselves with the central manager, which then checks at runtime which are available and dispatches based on filename extension. That way, RT_* are made unnecessary, and the central module isn't dependent on its users.

A problem that will eventually come up here is when loader code takes additional parameters (*) - that's not possible with the current interface. Adding separate AddTexture etc. routines with the correct signature would defeat the point, and having users call the central entry point with varargs is a bit ugly. What I do is have the resource module provide a snd_load function, which is fine for simple use and provides for extra parameters. In cases where one central entry point would be useful (+), such a routine as described above could be added on top, but wouldn't be the only means of allocating resources. Maybe it's a matter of taste, but I think it is nicer in general to explicitly request a "sound" be loaded - provided you know what the filename is supposed to be.


* example: at sound open time, we must know whether the file is to be streamed or loaded into memory.

+ example: pre-loading a list of resource files during idle time, so that they are cached and immediately available when later requested. Prerequisite is of course that no additional parameters are needed (i.e. defaults are sufficient).


Again though, such SwEng issues become important for large projects, and might be overkill for smaller games; don't know what you're working on.
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
graveyard filla
graveyard filla
out of curiosity, what would the return type be on AddResource() ? i was thinking you could make an ABC called Resource, and then make Texture_Resource, Sound_Resource, etc, all inherit from it. each type of resource would just have a pointer to the specific resource (either a pointer to a texture, or a pointer to a sound, or whatever).
FTA, my 2D futuristic action MMORPG
rick_appleton
rick_appleton
Jan is correct on all points.

I am currently using such a central resource manager. It accepts filenames, or character arrays along with an extension. From this info (file extension) it deduces which loader to use (these need to be registered) and passes the string to that loader (loaders only work with strings in my engine, not files). This makes if VERY easy to load all kinds of resources through a single interface. This resource manager returns pointers to IResource objects. IResource is an empty base class, which all resources (mesh, sound, texture) must inherit.

This has as disadvantage that I need to cast all resources before using them. And the second point against it (as Jan has said) is that I cannot give extra arguments to the loaders (no mipmaps, stream this sound file, etc).

Mostly due to the second point, I plan on rewriting the manager as multiple managers which can accept specific arguments. But I haven't started designing that yet, so it's possible I'll use a hybrid approach.

As to the disadvantage of having to make seperate managers, that is mostly moot using a template base manager which the seperate managers can extend.
Ainokea
Ainokea
Thanks guys and rating++ for all!
______________With the flesh of a cow.
Jan Wassenberg
Jan Wassenberg
graveyard filla:
Yep, good question. That interface leads into into downcast land, which is unsafe, yet I don't know exactly under what conditions it fails.
Backing up a bit, the function would have to return a reference to the resource somehow, so that it may be used (member functions called, freed etc.). Not being specific to any one resource type, it has to return a pointer to the base class. Downcasting to a more-derived type means the user makes assumptions about how the manager's object is laid out in memory. Obviously this will break if the two are compiled differently, but it should work otherwise. Still, this is considered bad practice and should be avoided. My resource manager is written with C-style OO (i.e. vtbl built manually via macros), which has other advantages and sidesteps this problem.


rick appleton: hmm, only passing strings to the loaders is an interesting approach. You eliminate load-file-into-memory logic from each one, but how is streaming handled? That's very important for large music files.

It is possible to pass on extra parameters as mentioned (via varargs), I just think it's not-so-good design.

No matter how you implement the multiple managers, I think the 3 goals of efficient allocation, caching, and hotloading require one (central) instance of code to work (by definition). How would those look in your template scheme?


I'm very interested in this topic, and thinking about writing an article. Many PC games nowadays have obscene load times that I figure could be much reduced with some work into the resource system.


Ainokea: glad to help!
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
jtrask
jtrask
Hmmm. This is certainly a tricky problem, to which I've given much thought but not found nearly as much for answers. Are you asking specificly about the use of managers, whether they should be central or split up into individual ones, as the discussion has been going, or how to deal with the problem in general? An interesting (though slightly ugly) approach I've seen in a few tutorials is to have no manager at all; if you have a TextureResource class, it can store a static (pointer to a linked list)/(std::vector)/(...) of all instances of itself and automatically add itself to the list in its constructor, remove itself and "delete this;" in the destructor. Though it's kind of ugly, this ties in well to reference counting and other high-level memory management tasks, it's easy to deal with inheriting a "PNGTextureResource" class, etc. Also, what about template classes? Though the loader for different types of resources will be different, the manager itself will be basically the same. If I have a PNGTextureResource class with multiple constructors each taking a file handle, path, raw data, etc. as the argument, couldn't I then have a ResourceManager PNGManager;? The ResourceManager handles the allocation/deallocation of memory, any appropriate logging or other tracking, more low-level management, any of that. None of these ideas, I think, are perfect, but the point is there's about a million ways to manage the memory. How do you know which is best?
rick_appleton
rick_appleton
Quote:
rick appleton: hmm, only passing strings to the loaders is an interesting approach. You eliminate load-file-into-memory logic from each one, but how is streaming handled? That's very important for large music files.

At the moment I don't handle streaming at all (no sound yet). However, I don't think it'll be a problem. I oversimplified a little bit, and I'm not sure we're talking about the same thing. So just to clarify: I pass either a string containing a filename, OR a string containing data and a string containing extension to the the loader. The first option (containing a filename), loads the file from disk into a string, and then it passes the work on to the second function. However, I have a layer over this string (BufferReader) which abstracts reading-from-file, reading-from-memory, and possible read-from-network so the fileloaders can load from anywhere. I'm assuming streaming would work with this setup easily, since I needn't load the entire file at once. I'd have to create a StreamingBufferReader, but the loader wouldn't notice that I think.

Quote:
It is possible to pass on extra parameters as mentioned (via varargs), I just think it's not-so-good design.

I totally agree, which is why I said it wasn't possible. I should have been more specific.

Quote:
No matter how you implement the multiple managers, I think the 3 goals of efficient allocation, caching, and hotloading require one (central) instance of code to work (by definition). How would those look in your template scheme?

I would assume mostly the same. You'd make the central manager, and a template module manager that interfaces with the central manager. The separate managers instanciate from the template and add their own specific functions.

Quote:
I'm very interested in this topic, and thinking about writing an article. Many PC games nowadays have obscene load times that I figure could be much reduced with some work into the resource system.

I totally agree here. Feel free to contact me if you want to bounce around some thoughts.
MGB
MGB
There was a very useful (and large) thread on the GD Algorithms mailing list about this a few months ago, titled "Resource Managing". Starts with the bottom post here
_DarkWIng_
_DarkWIng_
I have a desing for a simmilar system as rick for my next engine. I pass the manager only filename and a config class with additional information. Config class is specific to each resource type so loader knows what to expect. But there is one thing I just can't figure out how to do. In some cases each format can be loaded in more then one way. For example: you could return .tga as texture or as raw image. How do you guys handle this?

ps: My current engine uses templated resource manager base class and then I just write a resource specific code and number of loaders.
You should never let your fears become the boundaries of your dreams.
Seriema
Seriema
When I was struggling with this I came to the conclusion, for my game that is, that I don't have to nor should I overgeneralize. For what purpose? So everything can inherit from some überclass and it's OOP etc. but for no real reason? It was only causing problems, cast this and that. How are we gonna deal with multiple parameters? etc.

Sure, you can load a config file with lesser code. But then you have to write more code just to use the stuff. And you're loosing the data controll you have if you specificly load stuff from a config file, i.e. "first comes a texture name, then comes a banana, etc".

Sounds, textures, and other reasources are handled differently in the game. There is no real point in trying to force them under one roof.

The only reason I could see for some kind of IResource class would be for some inhouse memory cleaner/logger, debug info and/or reference counting. But that could be privatly inherited for the functionality, so it doesn't have to be a interface.

Those were my thoughts for our game. But it could be different for your game, and if so: how and why?
[ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made c
Jan Wassenberg
Jan Wassenberg
Merry Christmas, folks. The somewhat delayed reply:

Quote:
if you have a TextureResource class, it can store a static (pointer to a linked list)/(std::vector)/(...) of all instances of itself and automatically add itself to the list

hmm, I see no advantage over inheriting from IResource, which would take care of that.

Quote:
ResourceManager PNGManager; The ResourceManager handles the allocation/deallocation of memory, any appropriate logging or other tracking, more low-level management, any of that.

Sounds good. A similar OOB (i.e. C-style emulation of classes) approach does have 2 advantages, though: fixed class memory layout => downcasting is safe, and you get RTTI on the cheap.

Quote:
None of these ideas, I think, are perfect, but the point is there's about a million ways to manage the memory. How do you know which is best?

A happy medium of: 1) coders who're implementing the resource loaders not screaming at you 2) speed and memory efficiency :)


Quote:
However, I have a layer over this string (BufferReader) which abstracts reading-from-file, reading-from-memory, and possible read-from-network so the fileloaders can load from anywhere. I'm assuming streaming would work with this setup easily, since I needn't load the entire file at once.

Ah, ok. Looking good.


Quote:
I would assume mostly the same. You'd make the central manager, and a template module manager that interfaces with the central manager. The separate managers instanciate from the template and add their own specific functions.

Gotcha.


Quote:
Feel free to contact me if you want to bounce around some thoughts.

Thanks, but I'd prefer to continue the discussion here, so that all may benefit.

Quote:
There was a very useful (and large) thread on the GD Algorithms mailing list about this a few months ago, titled "Resource Managing". Starts with the bottom post here

Yes, thanks, I read that list (quite a bit of traffic though, heh). Good discussion there; as I recall, most use a central manager system in some form. Some folks only return a proxy object (necessary for hotloading), while myself and others add a Handle layer of indirection on top. IMO this is worth the slight overhead (with clever handle -> resource lookup algorithm), since more error checking is possible. Both schemes can protect against type mismatch, double-free, and some use-after-free cases, but handles ensure a particular piece of code allocated the resource, which must still be valid.

Quote:
In some cases each format can be loaded in more then one way. For example: you could return .tga as texture or as raw image. How do you guys handle this?

Tricky, a good question. With my resource code structure, this particular problem doesn't come up, since all "textures" are raw images until they are explicitly uploaded. But in general, I don't think you can do anything about this if only using a central point of access.
(Optional) resource-specific APIs (e.g. tex_load vs. img_load) are useful for this purpose.


Quote:
When I was struggling with this I came to the conclusion, for my game that is, that I don't have to nor should I overgeneralize. For what purpose? So everything can inherit from some überclass and it's OOP etc. but for no real reason? It was only causing problems, cast this and that. How are we gonna deal with multiple parameters? etc.

Good point; "YAGNI" (you aren't going to need it). However, for my purposes, generalized code is good: all projects use the blazingly fast async file loading routines. Careful (and more complicated than single-use) design is needed to cover different uses like game archive loading, photo viewer and file hashing.
(Sideswipe at OOP: it is my experience that C-based designs are more reusable; if your deep-class-hierarchy OO design doesn't foresee a particular use, you can basically throw away the code. It's easier to get this right in C, at least for myself.)


Quote:
Sounds, textures, and other reasources are handled differently in the game. There is no real point in trying to force them under one roof.
The only reason I could see for some kind of IResource class would be for some inhouse memory cleaner/logger, debug info and/or reference counting. But that could be privatly inherited for the functionality, so it doesn't have to be a interface.

I disagree. All resources have in common: the need for safe access via handle; provision for being hotloaded; memory allocation. This shared functionality can profitably go under the one roof :)
You're right about the interface, though: there is no need to provide only one central entry point; resource modules can provide an interface of their own, implemented via the central manager.

E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
rick_appleton
rick_appleton
Just thought of a halfway solution, and though I'd throw it on here to get your opinions.

- A single resource manager that manages all resources.
- Each resource type has a state machine that stores the settings to load a resource of that type. These settings can be changed before calling the main loading function. If all resources of that type are loaded with the same settings, the loading is still a one-liner, else you can change the settings prior to loading the object.

- Still need to cast from base-resource to other resource before using (might this be solveble with templates?)
- Do we use a state machine, or only per-load settings (after which the settings revert to default)? Preferably let the user choose which one of these.

I'll try and work this idea out a bit more in the next few days.
Seriema
Seriema
All I see with the all resources under one roof is the same problem, how to get the actual resource.

If you need functionality, use private inheritance. If you need an interface, use public inheritance. As it is now in this thread, it's clear that what the resources share is functionality and not interface.

What I can think of right now is a templated ResourceManager, possibly singleton. And all resources can be safely copy constructed, since it's reference counted or something nifty :)

In other words:
template <class RESOURCETYPE, typename IDTYPE>class cResourceManager : public cYourSingleton{public:  ~cResourceManager()  {   /* clean up */  }  cResourceManager* create( const IDTYPE& id )  {      Container::iterator r = resources.find(id);      if( r == resources.end() )        r = load( id );      return new RESOURCETYPE(*(r->second));  }     private:  Container::iterator load( const IDTYPE& id )  {     RESOURCETYPE *r = new RESOURCETYPE;     r->load(id);     return resources.insert( make_pair(id, r) ).first; // you COULD use add() for consistency, but right now I don't care :P  }  typedef std::map<IDTYPE, RESOURCETYPE*> Container;  Container resources;};////////////////////////////////////////////////////////template <class TYPE, typename IDTYPE>class iResource{  typedef cResourceManager<TYPE, IDTYPE> Manager;  typedef TYPE Type;  typedef IDTYPE IdType;  typedef const IdType& IdParam;  friend class Manager;  iResource()  {    /* start a new counter, for reference counting. Or whatever */  }  iResource(*iResource)  {    /* appropriate stuff for copy constructing, like incrementing counter */  }  ~iResource() // nope, it isn't virtual because we're not interfacing  {    /* decrement counter, call the manager to remove itself, whatever */  }  int protected:  virtual void load(IdParam id) = 0;  /* add all your resource management stuff here if you want, like a ref counter */};////////////////////////////////////////////////////////class cTexture : protected iResource<cTexture, std::string>{public:  typedef iResource<cTexture, std::string> Resource;  using Resource::Manager; // or if this doesn't work, typedefprotected:  // The ctor's are protected so that you can't create resources without the manager  cTexture() : Resource() {} // you don't have to call Resource explicitly like this, I think  cTexture(const cTexture& other) : Resource(other) {}  virtual void load( Resource::IdParam id )  {    /* load a texture to itself */  }};


now if you want a texture:
cTexture *newTex = cTexture::Manager::instance()->create("myTexture.tga");

Haven't tested or anything, so :P
The actual reference counting etc. I'll leave that part to you since this is just a small proof of concept type of thing :)

Another way to get the functionality would be through aggregation, i.e. has a iResource instead of privately inheriting.

Either way, if you want controll over a resource I think the best way is to go through a central manager for that resource. At least for creation, and maybe also destruction.

Maybe a proxy object could/should be used for reference counting, hot loading, safe handle? I.e. what's returned from the manager::create() should be what you want to use.

[Edited by - Seriema on December 29, 2004 6:03:02 AM]
[ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made c
greldik
greldik
Instead of answering directly to the OP's question I thought I would talk about the implementation details of my resource manager. It might be interesting to some.
The system is not complete yet but this is how it will work in large.

First of all, I use a global RM that handles all resources. The RM only support one internal format. All other formats are converted with an external tool or at startup (a one time thing for all new data files)

It must be possible to create a resource from scratch. For example, a dynamic texture or a vertex buffer for particles. This functionality is used when new data files are converted to the internal format. A TGA loader can create a texture and call Export on it to convert it.

Each resource class implements an Export/Import pair.
The Export function writes static information to a TOC and allocated data to a DATA chunk.
When Import is called the TOC info is passed as a parameter. This is used by the class to allocate memory. An async stream is then used to read the data.

Here is some pseudo code for export/import
COjbect::Export(RM){   RM.WriteTOCInfo(m_Size);   RM.WriteData(m_pData, m_Size);}COjbect::Import(TOCInfo, AsyncStream){   m_Size = TOCInfo.ReadInt32();   m_pData = new char[m_Size];   AsyncStream.Read(m_pData, m_Size, this, CallbackFunc);}
The idea here is that the TOC info is relativly small and all its data have already been read by the RM. So the Import function is very fast since it only copies a small amount of data and register larger chunks on the async stream. All parameters the object needs can be placed in the TOC.
The async stream could be implemented to read data with priority. Then it's possible to read low resolution mipmaps on all textures before higher resolutions and so forth.

A problem is that the system can be difficult to maintain across platforms since the data format is platform dependant.

Downcasting is necessary here as well. But I think it's worth it over having a bunch of specialized resource managers.

Please comment!
rick_appleton
rick_appleton
Did some thinking on what I mentioned earlier.

What I want from my resource loader:
1 One line-loaders using only the file-name (or datablob + extension) if I want to load a resource using the current settings.
2 One line-loaders if I want to load a resource with settings specific to this resource only.
3 Block options. Set the options somehow. Load multiple resources without specifying options. Options need to go back to default after loading these resources (preferably automatically).
4 Single entry-point for resources is preferred, but not mandatory.
5 Users of resources handles should preferably not know that they are using a managed resource. (I'll come back to this point later)

Current design:
- Single resource manager. Manages base resource types --> All resource types must inherit from this base resource.
- Users of the resource should not know something special is happening with the resource (ie. that it is managed) --> inside the resource manager another layer is created around the resource. This layer takes care of the ref-counting.
- Using the following monostate resource options as an example, points 1,2 and 3 can be achieved:
TextureOptions{public:  TextureOptions():mSingleLoadOptions(false) {};  TextureOptions( bool mipmap = true, EFilterType filtertype = EFT_LINEAR )  {    // Set the flag so we can reset previous values during destruction    mSingleLoadOptions = true;    // Copy the current settings    StoreSettings();    // Set the new settings for this load only    mMipmap = mipmap;    mFilterType = filtertype;  };  ~TextureOptions()  {    if (mSingleLoadOptions)    {      // Restore previous settings      mSingleLoadOptions = false;      RestoreSettings();    }  };private:  void StoreSettings()  {    mMipmap_Store = mMipmap;    mFilterType_Store = mFilterType;  };  void RestoreSettings()  {    mMipmap = mMipmap_Store;    mFilterType = mFilterType_Store;  };public:  static bool mMipmap;  static EFilterType mFilterType;private:  bool mSingleLoadOptions;  bool mMipmap_Store;  EFilterType mFilterType_Store;}

Point 1 is achieved by creating a new TextureOptions in the texture loading function. This then provides access to the default values.
Point 3 is achieved by creating an instance of TextureOptions, changing the public static member values to what we wish, and then calling the Load routine. There it will create another instance of TextureOptions, and access the changed options.
Point 2 is slightly different. It assumes that the loader has 2 signatures:
ResourceManager::Load(resource* res, std::string filename); // standard implementationResourceManager::Load(resource* res, std::string filename, void* options){  Load(res, std::string filename);  delete options;}

We can call the second one with something like the following:
ResourceManager::GetInstance().Load(&myTex, "mytexture.tga", new TextureOptions(false, EFT_BILINEAR))

This load will create a new TextureOptions using the variables as input. This sets the flag, stores the current settings, and then overwrites the current settings. The load call simply calls the regular load call without options. Inside the loading function, a new instance is created, accessing the changed settings and loading the texture with these specific settings. This function then returns succes or load as usual to the ResourceManager. The first called function of the loader (the one accepting options) then deletes the options on the heap. The destructor then restores the previous settings. Effectively nothing is done with the options on the heap, since everything needing to be done happens in the constructor/destructor.

I've been trying to devise a way to automate the production of the TextureOptions, since all ResourceOptions will be identical in structure. I think it's possible using some #define magic to get this class to build automatically by giving it a list of member variable types, names, and default values, but I haven't had access to a compiler yet to try this out.


Point 5 is interesting to achieve, since it would make sure the ResourceManager is totally transparent to the user. IE. a handle to a texture object, should behave exactly as the texture object itself.
Example (using OpenGL, and it is somewhat trivial):
class Texture{  unsigned int texID;}Texture myTexture;ResourceManager::GetInstance().Load(&myTexture, "mytexture.tga");glBindTexture(GL_TEXTURE_2D, myTexture.texID);glBindTexture(GL_TEXTURE_2D, myTexture);

Preferably both these ways of binding the texture should work. I've tried to get this working, and had is almost working, but it basically became a mess :D. Somehow the handle needs to inherit from the resource type to get this working. I'll see if I can find the code for this somewhere (although I'm afraid I've deleted it).

Edit: just realized that this concept would break down entirely when using asynch loading in combination with deferred loading.

[Edited by - rick_appleton on December 31, 2004 7:00:37 AM]

Topic Locked

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

Sign in to reply to this topic.