Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
66 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Introduction
 A Possible Solution

 Printable version
 Discuss this article
 in the forums


A Possible Solution

Enter the Singleton. A Singleton is an object that has at most one instance of itself in memory at any given time. Voodoo witchcraft?! Arcane magic's!? Not really. In code, a Singleton looks like this:

// In the header file
class CSingleton 
{
  static class CSingleton *m_Singleton;
  static Csingleton *GetSingleton (void);
};

// In the implementation file
CSingleton *CSingleton::m_Singleton = 0;

CSingleton *CSingleton::GetSingleton (void) 
{
  if (m_Singleton == 0)
    m_Singleton = new CSingleton;
  return m_Singleton;
}

By keeping a pointer itself as a static variable, the Singleton object isn't 're-created' with every instance of the class. So with the first call to GetSingleton, a Csingleton object pointer is allocated in memory, or the static variable m_Singleton. Does that make any sense? If not, don't worry, these links might help explain things a little better...

http://www.codeproject.com/tips/singleton.asp
http://www.inquiry.com/techtips/cpp_pro/10min/10min0200.asp

Well now that you are a complete expert on the concept of a Singleton, let's start working on our OpenGL texture manager, shall we?

The Code

class CTextureManager {
public :
  CTextureManager (void);
  ~CTextureManager (void);
  static CTextureManager &GetSingleton (void);

private :	// This is called automaticaly! Don't do it yourself!
  static void Initialize (void);
public :
  static void Destroy (void);

public :	// Usage / Implumentation
  int LoadTexture (const char *szFilename, int nTextureID = -1);
  int LoadTextureFromMemory (UBYTE *pData, int nWidth, int nHeight, int nBPP, int nTextureID = -1);

  void FreeTexture (int nID);
  void FreeAll (void);

public :	// Debug / Utilitarian
  char *GetErrorMessage (void);
	
  int	GetNumTextures (void);
  int GetAvailableSpace (void);
  int GetTexID (int nIndex);

private :
  static CTextureManager *m_Singleton;

  UBYTE *LoadBitmapFile (const char *filename, int &nWidth, int &nHeight, int &nBPP);
  UBYTE *LoadTargaFile (const char *filename, int &nWidth, int &nHeight, int &nBPP);

  int GetNewTextureID (int nPossibleTextureID);  // get a new one if one isn't provided
  bool CheckSize (int nDimension);

private :
  char szErrorMessage [80];  // they arn't bugs, their features!
  int nNumTextures;
  int nAvailable;            // available space in the nTexID array
  int *nTexIDs;
};

Get the full class header and implementation here.

As you can see, the code itself is very self-explanatory. In fact, the only crucial functions are LoadTexture(), LoadTextureFromMemory(), FreeTexture(), and FreeAll(). The first two (as you would guess) load and register new textures into OpenGL, while the last two free textures from memory.

Within those four methods lies the code to handle the variables nNumTextures, nAvailable and nTexIDs. These variables respectively hold the number of loaded textures, amount of available slots in the texture ID list, and the lift itself.

The class itself has the functionality to load Targa (TGA) files and 24-bit color Bitmaps (BMP), however, it can be easily expanded to incorporate other file types via new methods for loading the graphics files internally or via the LoadTextureFromMemory() function. Either way, this Singleton class will all but "hold your hand" when dealing with textures in OpenGL.

The most crucial part of the code is that all of the 'real' data is in the CTextureManager::m_Singleton object. In other words, if you want to access nNumTextures, you actually want m_Singleton->nNumTextures! This is all because the class is still a Singleton, and to be such, all of the data must be stored in one place: the Singleton pointer.

In Conclusion

The topics covered in this article may seem overwhelming at first, but keep in mind that having a Singleton texture manager is a good idea in the long run. As the number of graphics and textures used in your game begins to grow exponentially, a solid ID convention and an object to oversee texture operations will save MANY hours of bug tracking.

Questions? Comments? Flames?
Christopher.Smith@Trinity.edu
www.cs.trinity.edu/~csmith8
Chris Smith