Site Sections

Saturday, November 17, 2007

Texture/Model Manager Tutorial

Finally it has arrived, my tutorials on Texture/Model Managers that I had promised two weeks ago. It took me some time to get a chance to add it to my blog, I have been playing with Photoshop a bit this past week and just haven't had the motivation to work on my engine. I was adding a Height map that I found on RandomChaos 3D's site

Well, it was really frustrating me, so I am taking a break from the 3D stuff for a few weeks, going to start on a 2D engine so I can learn some more .Net stuff in building a level editor/event editor for a traditional RPG that I have in mind, (thus the photoshop work).
Regardless, Texture managers (and perhaps model managers) are still useful tools for 2D and I will be porting them over to my new engine. So I still feel that they are an important topic to cover here on this blog.

Texture Manager

So managers are a useful tool for storing information that can be resource intensive (such as textures and models) that have the potential of being generated more than once when they do not need to be. For instance, say we have 6 types of wall textures for our level, 2 types of ceiling textures, and 4 types of floor textures. Yet each of these textures will be used many times by every room in our level, whether they are simply tiled, or if they are going to be blended in our pixel shader that we use for our walls. Regardless, we still do not need to load these static textures multiple times into memory, not to mention, if we ever want to change what texture we have on our walls, or whatever we are texturing, we do not want to have to load new textures in the middle of our runtime loop.

Thus the texture manager comes into play. Here I have use the same principles that the Hazy Mind engine uses for shaders. A simple approach that uses a map to map texture objects to named indexes. Below is the code for my Texture2DManager:



using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.Collections;

namespace HMEngine3D.TextureManager
{
/**
* Texture2DManager
* Handles loading static textures for static textured objects
*/
public class Texture2DManager
{
// The table of textures
private static Hashtable mTextures = new Hashtable();
// The table of new texture asset locations
private static Hashtable mTextureAssets = new Hashtable();

/**
* AddTexture
* Adds a new Texture to the table of managed textures
*
* Parameters:
* aNewTextureAsset - The location of the new texture to add.
* aTextureLabel - a Name to reference the new texture by.
*/
public static void AddTexture(string aNewTextureAsset, string aTextureLabel)
{
// check for valid parameters
if (aTextureLabel.CompareTo("") != 0 &&
aNewTextureAsset.CompareTo("") != 0 &&
mTextureAssets.Contains(aTextureLabel) == false)
{
// add the new texture to the table
mTextureAssets.Add(aTextureLabel, aNewTextureAsset);
}
}

/**
* GetTexture
* Returns the Texture of the given name if it exists in the table.
*
* Parameters:
* aTextureLabel - The name of the shader to retrieve.
*
* Return Values:
* aOut - The texture in the table of the given name,
* null is returned if it does not exist.
*/
public static void GetTexture(ref string aTextureLabel, out Texture2D aOut)
{
// check to see if the label is valid
if (aTextureLabel != null && mTextures.ContainsKey(aTextureLabel))
{
// if so return the texture
aOut = mTextures[aTextureLabel] as Texture2D;
}
else
{
// the Texture is invalid, returning null
aOut = null;
}
}

/**
* LoadGraphicsContent
* Loads all the Textures in the table into memory from file.
*
* Parameters:
* aDevice - The Graphics Device to load to.
* aLoader - The Content Manager to use for loading the textures.
*/
public static void LoadGraphicsContent(GraphicsDevice aDevice,
ContentManager aLoader)
{
// loop through the textures in the table
foreach (string lTextureLabel in mTextureAssets.Keys)
{
// load the shader into memory from file
Texture2D lTexture = null;
lTexture = aLoader.Load(mTextureAssets[lTextureLabel] as string) as Texture2D;
if (lTexture != null)
{
if (mTextures.ContainsKey(lTextureLabel) == false)
{
mTextures.Add(lTextureLabel, lTexture);
}
}
}
}

/**
* EmptyTextureManager
* Empties out the texture manager of all currently loaded Textures.
*/
public static void EmptyTextureManager()
{
mTextureAssets.Clear();
mTextures.Clear();
}
}
}


You can see here that I used some of the optimizations that I discussed in my last post, the out reference passing as well as some in (ref) reference passing of variables in parameter lists. This isn't a serious optimization but it doesn't hurt. So here we can see that we can use these basic functions to add and retrieve texture assets from this manager. We want to add the textures that we want to load prior to the call that we will make to the LoadGraphicsContent function. This will allow the texture manager to load all of the texture assets all at once.

Since this is a static class, we will use this much in the same way that we already use our Shader manager. To keep this short and to the point, the other places that need code added to them are

  1. Any object that currently loads a texture, needs to be modified to simply keep a reference to their desired texture and add some code to retrieve their desired texture after the content has been loaded. Do not put code to search the manager in your draw loop, this is in-efficient and will slow down your engine!! Only search the manager when you need to switch texture assets, this is why the engine returns a reference to the object.
  2. In our main game class, we need to add some code to load our desired textures, for instance if you have a map parser, keep a list of used textures in a section of your map file. Then when you parse this, (I recommend that it be one of the first things you do) have it build your list of textures/labels prior to referencing them from your objects.
  3. We will also need to add in our engine a call to the LoadGraphicsContent function for our texture manager, this should be near the top of our Engine's LoadGraphicsContent function, this will allow us to use the other objects in our game to use their LoadGraphicsContent functions to retrieve their texture assets from the TextureManager
Thats it for the texture manager. Now we have a simple storage class for all the large texture assets in our game. Give it a try!

Static Model Manager

I'm not going to get in depth in the code for the Static Model Manager since it is the same as the Texture2DManager, just for different object types. Suffice to say, the reason it is for Static Models only is that, if you plan on having any sort of animation of your models, you will need to keep different references to them. It will give you trouble if you modify your model every time you switch objects. Use this for world objects that do not move (thus the term static).

If you have any problems porting the Texture2DManager over to a StaticModelManager, feel free to drop me a line and I will try and give you a hand.

Keep On Coding!