Crazy Eddies GUI System  0.7.0
The Beginners Guide to resource loading with ResourceProviders

Table of Contents

Author
Paul D Turner

This tutorial introduces the CEGUI::ResourceProvider concept, explains how to set up resource groups and directories in the DefaultResourceProvider and how to specify the default resource groups to be used by various parts of the system.


What is a ResourceProvider?

CEGUI uses a ResourceProvider object to provide a bridge between the CEGUI library and external file loading systems - whether this might be the basic underlying file system of the machine executing the code, or something a bit more exotic such as the resource management sub-systems offered by the Ogre3D and Irrlicht engines; by providing custom implementations of the ResourceProvider interface, it is possible for CEGUI to seamlessly integrate with all of these systems.


DefaultResourceProvider Explained

CEGUI's default resource provider, DefaultResourceProvider, supplies some basic functionality for those who do not yet have, or do not need, a more sophisticated alternative. As well as supplying the functions required by CEGUI for actually loading file data, DefaultResourceProvider also has some rudimentary support for 'resource groups'. Here, a resource group is basically a label given to a directory location on the system. This allows one to have logical grouping of files within directories and then to be able to refer to those locations via a simple label rather than hard coded paths. This then means that should you need to change the location of some data files, you just need to update the resource group location instead of updating path information throughout your code and XML files.

For users of the Ogre3D library - or users with other custom resource providers not derived from DefaultResourceProvider - you should not follow the parts of this tutorial that deal with defining resouce groups and their directories (you should especially ignore any example code that casts to DefaultResouceProvider). These users should follow the usual procedures for using the underlying resouce system in use (for example, in Ogre3D you might set up your resource locations in code using the Ogre::ResourceManager or via a resources.cfg file).

This said, all users should follow the parts of this tutorial dealing with setting the default resouce groups for the various resource classes, as detailed in Specifying Default Resource Groups.

Specifying Resource Groups and Directories

The DefaultResourceProvider allows you to define any number of named resource groups and to specify a directory to be accessed by each of those groups. What this means is that you can create a resource group, say "imagesets", and assign a directory to that group, for example "./mygame/datafiles/gui/imagesets/". When loading an Imageset through the ImagesetManager, you might then specify the resource group to be used as "imagesets" and the system will look in the predefined location "./mygame/datafiles/gui/imagesets/". Note that at present each resource group may only have a single directory associated with it.

A small code example is in order to clarify what has been said. Instead of loading resources by giving an explicit path, like this:

Imageset& wlis = ImagesetManager::getSingleton().create(
"./mygame/datafiles/gui/imagesets/WindowsLook.imageset");

At initialisation time, you set up a resource group in the default resource provider, like this:

DefaultResourceProvider* rp = static_cast<DefaultResourceProvider*>(
rp->setResourceGroupDirectory("imagesets", "./mygame/datafiles/gui/imagesets/");

Then later on in the code, when you need to load an imageset, just do this:

Imageset& wlis = ImagesetManager::getSingleton().create(
"WindowsLook.imageset", "imagesets");

Note how when creating the imageset from a file we do not specify any path information; the path information is obtained from the resource group specified, in the example this is "imagesets". We will later show you how you set default resource groups for each of the resource types - then you do not have to specify the group when you load a resource (unless you're loading it from a group other than the default, of course).

Something important to consider is that when using this resource group approach, data files that contain references to other data files should not contain relative path information - they should, in general, just have the actual file name of the file being referred to - this way the resource group system can be put to better use, and it also makes it easier to move files around later - since rather than having to 'fix up' all the relative paths, you just have to update the resource group paths instead.


Specifying Default Resource Groups

Each of the core system classes that represents a loadable resource has static members to set and get a default resource group. This resource group will be used when loading the specific data files needed by a given class - in the case of the Imageset class, the default resource group should reference a directory holding the imageset xml and texture image files.

For each of the resource consuming classes, the static members are named the same (special exception is the xerces based XML paser - see below):

const String& getDefaultResourceGroup();
void setDefaultResourceGroup(const String& groupname);

The following is a list of the core resource loading classes and the resources that they load:

There is one special exception, as mentioned above, this is the Xerces-C++ based XML parser. For this there is a special resource group setting to specify where the .xsd schema files - used for XML schema validation - can be found. For this special case, you instead use the PropertySet interface and access a property named SchemaDefaultResourceGroup. The use of the property interface mainly serves to eliminate the need to link directly with the xerces xml based parser module just to set the default schema resource group.

Because you may not know ahead of time which XML parser module is actually being used - and therefore may not know whether the property exists - you should check the existence of the propery before setting it. This is better than checking the XML parser ID string for "Xerces" because it allows you to seamlessly work with any future parser module that may also offer validation (so long as it exposes the same property).

For example:

// setup default group for validation schemas
if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))
parser->setProperty("SchemaDefaultResourceGroup", "schemas");

One final thing to consider, is that the ResourceProvider class also has a default resource group. This should be considered a global or master default; it is used whenever a specific resource loading class has no default of it's own specified. This would prove useful if you have all your data in a single directory.


A final, Complete Example

To close, we will show how you might perform the initialisation of resource groups and their target directories to access the data files within the datafiles directory that comes with CEGUI, and how we assign the default groups to be used for all of the resource types.

After initialising the core CEGUI::System object as usual, we then specify a set of resource groups and their target directories (this assumes the working directory will be the 'bin' directory within the CEGUI package:

// initialise the required dirs for the DefaultResourceProvider
rp->setResourceGroupDirectory("schemes", "../datafiles/schemes/");
rp->setResourceGroupDirectory("imagesets", "../datafiles/imagesets/");
rp->setResourceGroupDirectory("fonts", "../datafiles/fonts/");
rp->setResourceGroupDirectory("layouts", "../datafiles/layouts/");
rp->setResourceGroupDirectory("looknfeels", "../datafiles/looknfeel/");
rp->setResourceGroupDirectory("lua_scripts", "../datafiles/lua_scripts/");
// This is only really needed if you are using Xerces and need to
// specify the schemas location
rp->setResourceGroupDirectory("schemas", "../datafiles/xml_schemas/");

Now that is done, we have a nice set of resource groups defined with their target directories set. Finally, to get the system to use these directories, we set the default resource groups to be used:

// set the default resource groups to be used
// setup default group for validation schemas
if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))
parser->setProperty("SchemaDefaultResourceGroup", "schemas");

Conclusion

This has been a brief introduction to the ResouceProvider system used by CEGUI. You have seen how to create and use resource groups with CEGUI's DefaultResourceProvider, and how to specify default resource groups for each resource type used by CEGUI.