Thread: Resource management...again

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Resource management...again

    I've been recoding my entire game engine to deal with resources in a much better fashion.

    However I was wondering how some of you are approaching the problem.

    Here are the resources that I'm handling right now:

    • Textures
    • Materials
    • Lights
    • Shaders
    • Meshes


    These are basic resources. But now I also have a class called CShip which is a spaceship in my game. But you can see a problem here.

    Each ship needs a mesh, materials, textures, and a shader. These are all simply DWORDs or an ID_ARRAY like this:

    Code:
    struct ID_ARRAY
    {
      DWORD *pArray;
      DWORD dwNumItems;
    };
    The basic idea is I have managers for all those resource listed. They control the creation and destruction of the resources...in a roundabout way. Since meshes need nearly all of the resources to work correctly I decided to add meshes via a mesh manager which then also has pointers to the other various managers. Each one of the managers is then told to add materials, textures, etc from my X files. These add functions return a DWORD indicating the ID for that added object. This value is then stored in the mesh class by the manager class (the manager is a friend of the mesh class). Now each mesh has the needed information. In my rendering system each manager is responsible for rendering it's own objects and building the final render list utilizing frustum culling and so forth.

    This seems very odd to me b/c meshes require other resources and it seems they should be a part of the mesh rather than sep from it. But this introduces a whole plethora of problems in itself.

    How are you guys solving these issues?

    In short my system is:

    1. All objects/resources are managed by container classes
    2. All objects/resources are created via the container class when applicable
    3. Objects/Resources cannot be accessed without using the container class.
    4. The object container class updates the objects, builds the final render list from the master list, and renders the objects which includes setting textures, vertex/pixel shaders, etc, etc.


    So to illustrate let's say you want to load a ship model called fighter.x

    Code:
    //Create shader mgr 
    m_pShaderMgr=new CShaderMgr();
    
    //Init with device
    m_pShaderMgr->Create(m_pDevice);
    
    //Add shaders and assign IDs - so we can easily access later
    m_pShaderMgr->AddShader("AmbDifSpec.fx",BASE_SHADER);
    m_pShaderMgr->AddShader("Bloom.fx",BLOOM_SHADER);
    m_pShaderMgr->AddShader("Blend.fx",BLEND_SHADER);
    m_pShaderMgr->AddShader("Atmosphere.fx",ATMOSPHERE_SHADER);
    
    m_pShipMgr=new CShipMgr();
    m_pShipMgr->Create(m_pDevice);
    
    //Create the ship object and add the ship ID to the BASE_SHADER list
    m_pShaderMgr->AddObject(m_pShipMgr->AddShipFromX("fighter.x",0),BASE_SHADER);
    The ship container class would then open the x file, convert the Direct3D materials to CMaterial objects and add them to the material container and store the ID's in the mesh class, add any textures in the file to the texture container and add them to the mesh class, etc and return the ID of the ship.

    In the final render objects with similar shaders are grouped together and drawn in batches and so on.

    This has taken me a very long time to code and I'm still not sure if I like it or not.

    In my system a CGameObject is a renderable object and a CResource is a non-renderable engine resource such as a texture, material, etc. (even though you can render a texture it's still not a mesh or an actual game object).

    Comments? I'd be interested in how you are managing all of this Bob. I've got a headache from figuring this out on paper and then attempting to code it. Just for a release of tension I must say Resources suck
    Last edited by VirtualAce; 11-28-2006 at 05:49 AM.

  2. #2
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    This seems very odd to me b/c meshes require other resources and it seems they should be a part of the mesh rather than sep from it.
    How do you mean they're seperate from the mesh? As in they're not rendered/set in the same loop or at the same time as the mesh?

    In my system a CGameObject is a renderable object and a CResource is a non-renderable engine resource such as a texture, material, etc. (even though you can render a texture it's still not a mesh or an actual game object).
    What's the point of having two base classes? I just have a class CObject, which inherits CMesh, which inherits CMs3d and CQ3Bsp (so each file format eventually takes care of itself, but gets dumped into a generic type). Then I base all game entities like lights and staticmeshes on CObject. That way I can loop through an array of CObjects and call a Render() function without having to loop through each entity individually (although lights still require me to access the light array at some point).

    I also noticed that you're adding objects to your shader manager. Seems a bit strange. Why not just create a shader when the object is created, and bind an ID in the object to the shader ID?
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  3. #3

    Join Date
    May 2005
    Posts
    1,042
    In my rendering system each manager is responsible for rendering it's own objects and building the final render list utilizing frustum culling and so forth.
    ...
    Comments? I'd be interested in how you are managing all of this Bob. I've got a headache from figuring this out on paper and then attempting to code it. Just for a release of tension I must say Resources suck
    In all honesty, I think that you can appreciate many aspects of how I do things but that you wouldn't really like my layout. Keep in mind my renderer (I have a class called GLRenderer) is not as sophisticated as yours, so with respect to specifically a renderer, I simply cannot help you. I have not ventured into anything but fixed pipeline GL rendering.

    I don't have a class for managing things such as a spaceship, but in creating an object, say a hovertank, I use basically the same principles as you.

    I don't do anything silly with memory, I always allocate memory with these macros which check for allocation errors:

    Code:
    #ifndef	MEM_MAN_H
    #define	MEM_MAN_H
    
    #include	<assert.h>
    #include	<crtdbg.h>
    
    #define	NT_SAFE_ALLOC(dest,type,size) dest = new type[size]; _ASSERTE(_CrtCheckMemory()); 
    #define	NT_SAFE_DELETE(array)	if(array){delete[] array; array = NULL;}
    #define	NT_ZERO_MEM(array,struct_type,num)	memset(array,0,sizeof(struct_type)*num);
    
    #endif

    Here's how I make some textures:
    Code:
    	gSkySphere.texture	=	gpTextureManager->CreateTexture("sphericalsky03.jpg");
    	...
    	//this is an alpha texture but it is handled automatically
    	ParticleTexture = gpTextureManager->CreateTexture("particle.tga");
    Here's how I allocate a new hovertank:
    Code:
    	NT_SAFE_ALLOC(pTest_Hovertank,Hovertank,1)
    The hovertank class has a static model. There is only one model that is actually loaded, this is the master copy, I count the number of hovertanks in existance, and I create the master model when the first hovertank is made (in the constructor), and I delete the master copy when none are left. Each child hovertank simply reuses the master copy. This is how I manage models! The model is an internal format, NT_MODEL, which is optimized for rendering.

    The physics manager holds a list of rigid bodies. In the main source file I setup the properties for all of the rigid bodies, then I simply add them to the physics manager, e.g.:
    Code:
    	pTest_Hovertank->mStaticFriction	=	.1f;
    	pTest_Hovertank->mDynamicFriction	=	.1f;
    	pTest_Hovertank->mStiffness	=	.35f;
    	pTest_Hovertank->SetMass( 250.0f ); //automatically sets up inertia matrix for sphere
    	pTest_Hovertank->mAirCushion = Air_Cushion::Air_Cushion(pTest_Hovertank->GetMass(),pTest_Hovertank->mRadius,
    		170.0f,pTest_Hovertank->mRadius + 35.0f,5.0f,5.0f,10);
    	
    gpPhysicsManager->ADD_RIGID_BODY(pTest_Hovertank);
    Here is how my main cycle loop looks:

    If enough time has passed:
    {
    -Gather inputs
    -Update framerate
    -Reset the physics time buffer
    -Update all objects AI before physics is run
    -Render all physics frames (run the physics manager)
    -Run each object's render hook. Each object adds itself to the GLRenderer, the objects do not render themselves. A hovertank simply adds an NT_MODEL to the GLRenderer, but only if the sphere is in the frustum. If debug mode is on, I can also add a wireframe sphere around the hovertank, or debug lines, or anything else I want, because the rendering does actually happen until the next step. I am thinking that when I want to incorporate shaders I will write a shader manager, and attach shaders to each type of object. This will get tricky when dealing with the order of rendering, juggling textures, lights, etc (which is exactly the probs you are having, lol)
    -Render the scene (including text)
    }
    else
    {
    Add the time passed to the physics time buffer
    }

    Physics runs at 100.0f FPS, the graphics runs at 100.0f FPS or less. If the rendering is for some reason going only at 50.0f FPS then the physics time buffer notices it has enough to run 2 physics frames, so it goes ahead and runs 2 physics frames (the delta time per physics frame is constant, which assists in keeping numerical stability).

    I can't really think of anything else to say that could really give you any insight. I think that you have engineered your resource management more than I have, but I'm afraid that you may be over engineering it to the point that it's getting too cumbersome to be useful in some regards. I mean, there are all sorts of clever ways to sort objects based on what objects share the same texture on the first texture pass, and clever culling algorithms, etc, but I made this VERY simple:

    -Every type of thing that I can possibly draw has a data type
    -Each object culls itself, the RENDERER knows nothing about CULLING. The renderer *just* draws anything given to it, it's a hungry hungry drawing machine and cares about nothing else in its existence.

    I don't have to specify which objects are renderable or not, there's no 'renderable' baseclass or anything like that. If something wants to be drawn, it just needs to add stuff to the renderer, then the stuff it added will get drawn later on.
    Last edited by BobMcGee123; 11-28-2006 at 03:22 PM.
    I'm not immature, I'm refined in the opposite direction.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Thanks for the comments both of you b/c they are very helpful.

    Psychopath you are right about the shader issue and I've corrected this. However your method of inheritance seems a bit too deeply nested for me. My inheritance never goes beyond 1 derivation unless there is no way around it. The idea behind the managers is I don't want objects creating and destroying resources and a game object and a resource are quite different.

    A texture, material, or shader may belong to a game object but they are not game objects in and of themselves. They are simple resources being used by game objects. Thus the reason for two classes.

    I have now created a CRenderSys class which has been made a friend of all the classes it needs to access in order to render correctly. CRenderSys iterates the master list from CObjectMgr which contains all the CGameObjects in the current space sector. Because of the sparsity of the objects I decided not to use some type of spatial partioning but I may later. The CRenderSys class builds the render list which is a simple uni directional linked list. The list is built by checking to see if the bounding volume of the object (represented by min and max vectors) lies within the frustum. If the return value is CULL_INTERSECT or CULL_IN the object is added to the linked list. If the return value is CULL_OUT the object is ignored. This is ONLY for rendering. All objects in the sector must be updated each frame and I've not come up with a better way of doing this and yet still make it appear to be a lively sector of space.

    Again I'm still working this system and perfecting it for my needs so it is a work in progress.
    To illustrate I'll show you the object order for a simple spaceship.

    CGameObject->CShip (CShip inherits from CGameObject)

    CObjectMgr (CObjectMgr is a container class for CGameObject)

    CShipMgr (manages a collection of DWORDs representing all ships in CObjectMgr)
    - pointer to master object manager
    CShipMgr::AddShip()
    • Creates the ship object(a CGameObject)
    • Adds the ship via CObjectMgr->AddObject(CGameObject *pObj)
    • Saves the DWORD returned from AddObject in it's collection of DWORDs
    • Returns the DWORD to the caller



    The reason for all this indirection is that the creation for a ship is much different than say for a planet. By deriving all the objects from a common base I can define some base functionality and then get specific in the derived classes. Also, in turn, by creating a base object manager that handles CGameObject's I can add any object derived from CGameObject to the object manager. So the object manager is the ONLY class that actually handles the objects. All other manager classes are simply handling DWORDs representing ID's for their objects - which are actually indexes into the master list of objects.

    Resources are handled in much the same way. So in the end only two classes actually hold any sort of objects and the rest just make managing the master list of objects simpler by handling the indexes of certain types of objects in the master list.


    Index-object in master resource list
    ---------------------
    0 - CTexture
    1 - CShader
    2 - CTexture
    3 - CSoundFX
    4 - CMusicTrack
    5- CLight


    Collections for managers with list represented above
    -------------------------------------------------------------------
    CTexMgr
    -----------
    0,2

    CShaderMgr
    ---------------
    1

    CLightMgr
    ---------------
    5

    CSoundFXMgr
    -----------------
    3

    CMusicTrackMgr
    --------------------
    4

    See the convenience of this? To destroy all the objects I just do

    Code:
    CObjectMgr::~CObjectMgr()
    {
      for (DWORD i=0;i<static_cast<DWORD>(m_vObjects.size());i++)
      {
         delete m_vObjects[i];
      }
    }

    Done. Now all resource objects have been deleted and their memory freed regardless of the type of resource.

    To add an object from a manager class:

    Code:
    DWORD CShipMgr::AddShip(SHIP_PARAMS ShipParams)
    {
       //Create the ship object
       CShip *pShip=new CShip;
       pShip->Create(ShipParams);
    
       //Add the object to the master list
       m_vObjectIDs.push_back(m_spObjectMgr->AddObject((CShip *)pShip));
    
       //Return the index in our local list which holds the ID (index) of the created object in the master list
       return static_cast<DWORD>(m_vObjectIDs.size()-1);
    }
    SHIP_PARAMS of course would specify the filename of the model and various ship parameters such as position, speed, weight, etc, etc, etc.

    Notice the ship manager class creates the ship object. In fact CShip's constructor is private and CShipMgr is a friend of the class. This means I have complete control over who is creating objects.


    Sorry so long but it's a bit hard to explain.
    Last edited by VirtualAce; 11-29-2006 at 05:51 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Sorting out a resource manager
    By psychopath in forum Game Programming
    Replies: 1
    Last Post: 11-10-2008, 07:12 PM
  2. More resource management troubles
    By VirtualAce in forum Game Programming
    Replies: 4
    Last Post: 09-25-2007, 02:23 AM
  3. CreateProcess with Resource of executable, not the Filename
    By Ktulu in forum Windows Programming
    Replies: 4
    Last Post: 11-04-2006, 01:07 AM
  4. Mmk, I give up, lets try your way. (Resource Management)
    By Shamino in forum Game Programming
    Replies: 31
    Last Post: 01-18-2006, 09:54 AM
  5. Serial Communications in C
    By ExDigit in forum Windows Programming
    Replies: 7
    Last Post: 01-09-2002, 10:52 AM