KeyLine System
Introduction
In many cases there is a need in changing some "in game" parameters dynamically, depending on time or something else (e.g. lighting conditions). For example, one needs to change some parameter depending on time: the parameter animation can be stored as a sequence of animation keys: values and their time. To calculate the parameter value at any given moment of time it's needed to find the previous and the next keys and make some kind of interpolation between their values.
KeyLine system handles key sequences and takes care of interpolation between key values, providing GUI for data manipulation as well. It is a somewhat data-driven system, meta-information is stored in the XML format.
KeyLine scripts are located in data/scripts/keyline directory of Unigine SDK.
Entities
Param
Param is a type of an atomic parameter, whose value needs to be changed dynamically (see ParamBase class). Param can be serialized to XML and deserialized from it by using meta-data (ParamDef). There are functions associated with the parameter, which specify how parameter's value is applied to some environment (setter) or retieved from it (getter).
There is a pre-defined set of "basic" parameter types available out-of-the box: ParamFloat, ParamInt, ParamVec4 classes.
ParamDef
ParamDef is a definition of a parameter (type, ParamEditor, min/max values, default value, setter/getter functions). ParamDef class is used to create parameter instances by its definition.
ParamEditor
ParamEditor is a definition of GUI editor type, which is used to edit parameter's value (see ParamEditorBase class).
Key
Key is a set of parameters grouped by some subject (see KeyBase class).
KeyDef
KeyDef is a definition of a key (set of its parameters' definitions). KeyDef class is used to create Key instances by its definition.
KeyLine
KeyLine is a sequence of keys (of the same type), each of which is identified by a position. KeyLine is responsible for smooth interpolation between keys. When KeyLine cursor position (current position) is behind the last key, KeyLine makes interpolation between the last and the first keys (see KeyLine class).
KeyLineManager
KeyLineManager handles set of KeyLines (see KeyLineManager). It is also responsible for global settings of min/max positions.
File Formats
keyman
*.keyman file contains settings of KeyLineManager in XML format.
The keyline_manager node must be a root of the node hierarchy. This node has the following mandatory integer attributes: min (minimal position for all KeyLines in the KeyLineManager) and max (maximal position for all KeyLines). Besides that, integer attribute duration specifies the time period during which the KeyLineManager will pass from the minimal to the maximal position.
keyline_manager node can contain zero or more children keyline nodes, each of them must contain file attribute, which points to *.keyline file. For example:
<?xml version="1.0" encoding="utf-8"?>
<keyline_manager min="0" max="86399" duration="1440">
<keyline file="demos/tropics/tropics_sky.keyline"/>
<keyline file="demos/tropics/tropics_render.keyline"/>
</keyline_manager>
keyline
*.keyline file contains keyline data in XML format. Only one keyLine can be stored in a file.
keyline node must be a root of the node hierarchy, it has mandatory name attribute. min and max attributes has the same meaning as keyline_manager ones, but their values will be ignored, because they are overriden by KeyLineManager. keyline node must have key_def node as a child.
key_def node can be defined in two different ways:
<!--
key_def (key definition) node: reference format.
file (required) - Used to specify file, which contains key definition in the format described below.
-->
<key_def file="demos/tropics/tropics_timeline.keydef"/>
<!--
key_def node - full format.
attributes:
keyline (required, if key_def is placed into separate file and is optional, if key_def is placed into keyline node).
Used to identify key_def.
-->
<key_def keyline="scattering">
<!--
place parameter definitions here
-->
<!--
param_def (parameter definition) node.
attributes:
type (required) - String identificator of a parameter type. Used to create a parameter instances by name.
name (required) - Unique parameter name. Used to identify the parameter in a key.
editor (required) String identificator of a parameter editor type. Used to generate GUI for editing keys.
-->
<param_def type="float" name="energy" editor="float_slider">
<!--
getter function name for parameter (optional)
-->
<getter>engine.render.getScatteringEnergy</getter>
<!--
setter function name for parameter (optional)
-->
<setter>engine.render.setScatteringEnergy</setter>
<!--
min value for parameter (optional)
-->
<min name="energy">0</min>
<!--
max value for parameter (optional)
-->
<max name="energy">40</max>
<!--
default value for parameter (optional)
-->
<default_value name="energy">15</default_value>
</param_def>
</key_def>
keyline node has children key nodes, which contain keys' data. If there are no keys in the keyline, one key will be created automatically. This key will be filled using parameters retrieved by getter functions from the environment (this may be usefull when starting from previously tuned non-sequenced parameters).
key node has the following format:
<!--
key node. Contains set of parameter values.
attributes:
pos (required, unique) - key position on keyline.
-->
<key pos="43200">
<!--
param node. Contains parameter data.
attributes:
name (required, uniquie) - parameter name, which is also used by the system as a parameter identificator
-->
<param name="areal">30</param>
<param name="greenstein">0.9</param>
<param name="mie_color">1 0.972549 0.858824 1</param>
</key>
keydef
*.keydef file contains definition for key types in XML format.
definition node must be a root of a node hierarchy. version attribute is reserved for future implementations. definition node contains set of key_def nodes in the full format, which is described in keyline files definition.
Example:
<?xml version="1.0" encoding="utf-8"?>
<definitions version="1.0">
<key_def keyline="scattering">
<!-- ... -->
</key_def>
<key_def keyline="sky">
<!-- ... -->
</key_def>
<key_def keyline="render">
<!-- ... -->
</key_def>
</definitions>
How to Use
Getting started
Step 1. Prepare getter and setters for parameters
namespace Foo {
void setterFunc(DataType v) {
// some work to set the parameter
}
DataType getterFunc() {
// retrieve the parameter value
}
}
Step 2. Prepare keyline manager files
Create keyline manager file (test.keyman, for example). Define keylines:
<?xml version="1.0" encoding="utf-8"?>
<keyline_manager min="0" max="86399" duration="2160">
<keyline file="test.keyline"/>
</keyline_manager>
Create keyline file (test.keyline, for example):
<?xml version="1.0" encoding="utf-8"?>
<keyline name="test keyline">
<key_def>
<param_def type="float" name="param 1" editor="float_editline">
<getter>Foo::getterFunc</getter>
<setter>Foo::setterFunc</setter>
</param_def>
</key_def>
</keyline>
Step 3. Include keyline manager headers to your script
// include all required keyline manager headers:
#include <keyline.h>
// include basic parameters (optional), if you want to use float, vec4 or int paramter types and their editors:
#include <params_basic.h>
Step 4. Initialize keyline manager
KeyLineManager manager;
void init() {
// register basic parameters to allow their creation by name (needed for loading from file)
KeyLine::registerBasicParams();
// create and load keyline
manager = new KeyLine::KeyLineManager();
manager.load("test.keyman");
}
Step 5. Update position and call apply, when needed
int update() {
if(play_mode) {
manager.play(); // update position
}
manager.apply(); // commit params (call setter function for each param in manager)
}
Using GUI for editing
KeyLine provides GUI editors, which allows user to manipulate keys and change parameter values. Refer to the following to use GUI editor in your project.
Step 1. Include headers
#include <keyline/keyline_window.h>
Step 2. Write initialization, shutdown and update code
void init() {
Gui gui = engine.getGui();
KeylineWindow::init(gui);
KeyEditorWindow::init(gui);
}
void shutdown() {
KeylineWindow::shutdown();
KeyEditorWindow::shutdown();
}
void update() {
KeylineWindow::update();
KeyEditorWindow::update();
}
Step 3. Showing the window
void show_window() {
// set callbacks (optional)
KeylineWindow::setOnSaveCallback("on_save_manager");
KeylineWindow::setOnResetCallback("on_reset_manager");
KeylineWindow::setOnHideCallback("on_editor_closed");
KeylineWindow::setOnSetCaptionCallback("gen_pos_caption");
// show window
KeylineWindow::show(manager);
}
/* callbacks (optional)
*/
void on_save_manager() {
/* Hits when user want to save keyline manager to file */
}
void on_reset_manager() {
/* Hits when user want to discard changes and reload keyline manager */
}
void on_editor_closed(int is_changed) {
/* Hits when editor window is closed. is_changed == 1, when there are unsaved changes in keyline manager */
}
string gen_pos_caption(float pos) {
/* generate position caption for keyline window GUI. Returns a formatted string */
}
Using custom parameters
If basic parameters don't cover all of your needs, you can write your own parameter types.
To do this you must inherit your class from KeyLine::ParamBase and override at least load, save and clone methods. To create your own parameter editors inherit from KeyLine::ParamEditorBase class. Then you must write functions for creation your parameters:
/* creation function for custom parameter
*/
ParamBase createCustomParam() {
// add here some extra initialization, if needed
return new CustomParam("foo");
}
/* creation function for custom param editor
*/
ParamEditorBase createCustomParamEditor(ParamDef def,Gui gui) {
// add here some extra initialization, if needed
return new CustomParamEditor(gui);
}
addParamType("foo","createCustomParam");
addEditorType("foo_editor","createCustomParamEditor");
GUI
KeyLine system automatically generates GUI for editing keys.