Friday, March 20, 2009

Current Features (March 2009)

Derange has been in development for quite some time now, so I figured it might be a good idea to review which features have been implemented so far to see where I’m currently standing. Many of these features have been rewritten from scratch at least a couple of times as I learned better ways to implement them.

Implemented features

  • 3D graphics rendering using Ogre3D v1.6 (Shoggoth).
  • Scriptable API using Lua v5.1 and LuaBind v0.7.
  • Input device management using OIS v1.2.
  • Support for multiple Locations with separate resource groups that can be initialized using DotScene files.
  • Importing of DotScene files using OgreMax v1.6.23, creating necessary game objects as needed.
  • Configuration through XML files using Xerces-C v3.0 for parsing and validating against XML schemas.
  • Efficient resource management using Ogre3D’s capabilities.
  • Flexible game object design, tightly bound to Ogre3D’s scene graph implementation.
  • Multiple Controllers can be combined and attached to game objects to separate representation from the actual game logic applied.
  • Easy to create game objects using a factory class which only requires the names of the object and the state to initialize it to.
  • Skeletal animation support with smooth blending between different animations.
  • Actors rotate smoothly in the direction they’re moving to using Slerp interpolation.
  • Actors move using configurable speed and inertia parameters.
  • Third person player control, with movement relative to the current camera’s position using the keyboard.

Near-middle future plans

  • Collision detection, possibly physics simulation too. Need to figure out the pros and cons of the different third party physics engines available to decide.
  • Automatic camera selection based on target proximity.
  • Walking through predefined path nodes.
  • Path finding.
  • Mouse picking.
  • Overlay manager for HUD display, menus, and on-screen debugging.

Thursday, March 19, 2009

Resource subsystem – part two

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
  1. Undefined: This is the initial state for all Resources. Basically means that the engine knows nothing about them yet.
  2. 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.
  3. 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.
  4. 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.

ResourcesClass 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.