In my previous post, I explained how the Resource subsystem interacted with resource managers. This time, I’ll show you in more detail how each Derange resource manager deals with their specific resources using Ogre’s resource management scheme. Again, I’ll try to stick to what’s relevant to the engine, because that is quite a lengthy topic.
Resource life cycle
A Resource goes through several state transitions until it is actually loaded. This is taken care of by its corresponding Resource manager silently. Here’s a chart with the different states and what they mean:
| State | Declaration? | Instance? | Available? |
| Undefined | No | No | No |
| Declared | Yes | No | No |
| Unloaded | Yes | Yes | No |
| Loaded | Yes | Yes | Yes |
-
Undefined: This is the initial state for all Resources. Basically means that the engine knows nothing about them yet.
-
Declared: The engine knows about the Resource, but no steps have been taken towards loading it, and there is no valid instance yet. Also, no memory is used at this stage.
-
Unloaded: Resources enter this state when the ResourceGroup they belong to is initialized. By this time, they are only using a small amount of memory: enough to hold an instance, but with no available content yet. They can also be forced back into this state by manually unloading them or when their ResourceManager exceeds its memory quota, which means the instance is kept, but not the actual content.
-
Loaded: This is the “fully active” state of a Resource. It means there’s a valid instance available and the data it contains has been loaded into memory, so it is available for the game to use. Resources are loaded automatically when they are needed, unless their ResourceManager exceeded its memory quota.
Class diagram showing Resource and Serializer hierarchies. (click to enlarge)
Serializers
Whenever a ResourceManager needs to load a Resource from disk, a Serializer is used. They are classes that implement the logic needed to read or write files of a certain type.
Typically, a ResourceManager is asked to load a resource of a given name. Then it creates an instance of the appropriate Serializer and calls its load() method passing a DataStream with the file contents and a Resource instance as parameters. The file is then read and parsed in order to initialize the Resource with the content. And that’s pretty much all they do.
Parsing scripts
Some Resource types might depend on other Resources being loaded. Take Material scripts, for example. You might be loading many of them that use the same Texture, and it would be really sloppy to have it reloaded every time a Material requests it.
Instead of that, it is declared, which basically means that it will be loaded only once when the ResourceGroup containing the declaration is bulk loaded. This saves a considerable amount of loading time, depending on the times it is referenced.
Loading Scenes
Scenes are stored in DotScene format, as exported from 3DStudio Max by OgreMax. This is basically an XML file defining each object in the scene, their properties, and SceneNode relationships.
OgreMax also provides code to create the necessary Ogre objects when importing it into the engine, and a callback class that can be used to trap specific events generated by the process.
Derange traps some of these events. For example, if an Ogre Camera is imported, a suitable GameObject of type “Camera” will be created and attached to it as an UserDefinedObject. This saves the need to have two different scene graphs, since each GameObject can be reached from its Ogre counterpart.
As far as loading goes, the DotSceneSerializer only takes care of loading the .scene file and store the XML text contained into a DotScene resource. All importing is triggered from the resource itself.
Loading Game Objects
GameObject definitions are also XML files, but they contain several GameObjectStates that can be applied to an object, and are referenced by an unique name.
There’s only one GameObject class. It’s the GameObjectState that defines the type of GameObject: cameras, lights, actors, path nodes, etc. I will explain the details on these in another post.
This Resource, however, is a little different from the others: In order to load a GameObject, not only its name is required. we also need the name of the GameObjectState to apply. To achieve this, Ogre provides a way to define creation parameters per Resource through the StringInterface class. This is essentially a list of properties that can either be used by a Serializer as loading parameters (In this case, the name of the GameObjectState to load) or by an editor to display onscreen.