-
Notifications
You must be signed in to change notification settings - Fork 73
ConfigFiles
#Dealing with SceneEngine configuration files
Many of the scene engine objects require a lot of configuration options. These options control the lighting and animation conditions. Often these options need to be given specific values by the artists. But the granularity for this may be unclear.
For example -- once per level? Different values for different regions with in the same level? Single global setting for the entire game?
Some settings might be set at game startup, and never change. Others might change as the player moves through the world, or changes configuration options.
Some settings might even by animated. That is, they might change according to the time of day. Or they might be assigned special animated values in cut scenes.
And, on top of all of this, we want to be reasonably version robust. That means all of the configuration settings should not be invalidated every time the engine upgrades to a new version.
So... How do we deal with all of this?
##EntityInterface and the editor
The level editor interacts with the SceneEngine primarily through the EntityInterface library. This library presents many of the configuration settings in the SceneEngine as simple "entities". An entity is a set of key name and value pairs (attributes), which is part of a tree hierarchy of entities.
This simple view provides a simple unified interface. That's handy for interfacing with the GUI features.
This may also be useful pattern for animating configuration settings. Since every property has an unambiguous name, it provides a clear target for an animation curve to graft on to (or scripts, etc).
This pattern also allows for partial changes (eg, setting only a single property in an entity of many properties). That's important in the level editor... But may not always be needed in a runtime app.
##Layered inheritance structure
In an ideal world, our configuration options would exist in a layered structured, whereby each layer could either provide a override for a property value, or just inherit the previous value.
Think of the configuration options in Visual Studio, or the material system in XLE. In these systems, we can implement layers that only partially effect the configuration state. This might be useful for quality modes.
Consider that a single "entity" might have some attributes that should be set according to the current quality mode. And other attributes that should be set according to the current level. In case like this, we can mix settings together by allowing one configuration to be a partial layer over the other.
This would also be useful for providing adaptive "defaults" for certain types of objects. For example, the defaults for all lights might vary according to quality mode (but a specific light might set an overriden setting).
##Minimize serialization and deserialization implementation work
Ideally we want to minimize the quantity of code involved in serialization and deserialization for configuration types. Interacting with the EntityInterface is one form of serialization. Another form is reading and writing from disk. It would be good if these could share code (otherwise we would need to maintain multiple similar functions).
Sometimes we might need to do a little processing during serialization. For example, sometimes the data provided to the EntityInterface from the level editor isn't in the ideal form we want -- and so there is a little bit of "munging" work to do. Other times, we may want to make certain objects version robust by handling version upgrades in the deserialize step. Some serialization patterns can make this hard to do.
Also, configuration settings can often be complex types (with sub-containers and possibly even pointers to other types). The more complex the type is, the more difficult it is to provide very generic serialisation options.
##Reflection based systems
Some engines and languages implement serialization via a reflection system. This is possible to do in C++ -- but requires some custom code and macros (etc).
There are some interesting existing libraries available. But many of these use boost -- either boost::fusion, or boost preprocessor macros. Ideally we would want to avoid adding a large set of dependencies on boost.
There are some interesting references available:
- https://github.com/HeliumProject/Reflect
- http://www.extreme.indiana.edu/reflcpp/
- https://github.com/rticommunity/rticonnextdds-reflex
- http://pfultz2.com/blog/2012/07/31/reflection-in-under-100-lines/
- http://kifri.fri.uniza.sk/~chochlik/mirror-lib/html/
- https://github.com/tegesoft/camp
- https://github.com/Cylix/Reflex
##Properties interface
It's also worth consider how "properties" work in C# to facilitate reflection. Many of the problems involving reflection based serialization occur when values stored in data don't match the actual class members exactly. Introducing the concept of "properties" helps add a layer between the two. Any extra processing required can be performed in get/set methods.
In C++, it might be interesting to use lambdas as property functions (filling in trivial default implementations as necessary).
This might provide a useful alternative to the standard patterns used for implementing reflection... One that could ultimately be more powerful.