1
0
Fork 0

Replace SGInterpolator with new advanced interpolation system.

Allow for advanced animations using easing functions and adapters
for interpolating specific property types (eg. CSS colors).
Old behavior should not have changed.
This commit is contained in:
Thomas Geymayer 2013-03-16 16:43:55 +01:00
parent 2a2beec658
commit 4b21dc51ee
7 changed files with 160 additions and 77 deletions

View file

@ -14,6 +14,7 @@ set(SOURCES
fg_io.cxx
fg_os_common.cxx
fg_props.cxx
FGInterpolator.cxx
globals.cxx
locale.cxx
logger.cxx
@ -30,6 +31,7 @@ set(HEADERS
fg_init.hxx
fg_io.hxx
fg_props.hxx
FGInterpolator.hxx
globals.hxx
locale.hxx
logger.hxx

View file

@ -0,0 +1,15 @@
/*
* FGInterpolator.cxx
*
* Created on: 16.03.2013
* Author: tom
*/
#include "FGInterpolator.hxx"
#include <simgear/scene/util/ColorInterpolator.hxx>
//------------------------------------------------------------------------------
FGInterpolator::FGInterpolator()
{
addInterpolatorFactory<simgear::ColorInterpolator>("color");
}

View file

@ -0,0 +1,21 @@
/*
* FGInterpolator.hxx
*
* Created on: 16.03.2013
* Author: tom
*/
#ifndef FG_INTERPOLATOR_HXX_
#define FG_INTERPOLATOR_HXX_
#include <simgear/props/PropertyInterpolationMgr.hxx>
class FGInterpolator:
public simgear::PropertyInterpolationMgr
{
public:
FGInterpolator();
};
#endif /* FG_INTERPOLATOR_HXX_ */

View file

@ -23,7 +23,6 @@
#include <simgear/structure/event_mgr.hxx>
#include <simgear/sound/soundmgr_openal.hxx>
#include <simgear/timing/sg_time.hxx>
#include <simgear/misc/interpolator.hxx>
#include <simgear/io/HTTPRequest.hxx>
#include <FDM/flight.hxx>
@ -45,6 +44,7 @@
#include "fg_os.hxx"
#include "fg_commands.hxx"
#include "fg_props.hxx"
#include "FGInterpolator.hxx"
#include "globals.hxx"
#include "logger.hxx"
#include "util.hxx"
@ -883,13 +883,15 @@ do_property_randomize (const SGPropertyNode * arg)
* Built-in command: interpolate a property value over time
*
* property: the name of the property value to interpolate.
* type: the interpolation type ("numeric", "color", etc.)
* easing: name of easing function (see http://easings.net/)
* value[0..n] any number of constant values to interpolate
* time/rate[0..n] time between each value, number of time elements must
* match those of value elements. Instead of time also rate can
* be used which automatically calculates the time to change
* the property value at the given speed.
* -or-
* property[1..n] any number of target values taken from named properties
* property[1..n+1] any number of target values taken from named properties
* time/rate[0..n] time between each value, number of time elements must
* match those of value elements. Instead of time also rate can
* be used which automatically calculates the time to change
@ -898,63 +900,82 @@ do_property_randomize (const SGPropertyNode * arg)
static bool
do_property_interpolate (const SGPropertyNode * arg)
{
SGPropertyNode * prop = get_prop(arg);
FGInterpolator* mgr =
static_cast<FGInterpolator*>
(
globals->get_subsystem_mgr()
->get_group(SGSubsystemMgr::INIT)
->get_subsystem("prop-interpolator")
);
simgear::PropertyList valueNodes = arg->getChildren( "value" );
simgear::PropertyList timeNodes = arg->getChildren( "time" );
simgear::PropertyList rateNodes = arg->getChildren( "rate" );
if( !mgr )
{
SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available");
return false;
}
if( !timeNodes.empty() && !rateNodes.empty() )
// mustn't specify time and rate
SGPropertyNode * prop = get_prop(arg);
simgear::PropertyList time_nodes = arg->getChildren("time");
simgear::PropertyList rate_nodes = arg->getChildren("rate");
if( !time_nodes.empty() && !rate_nodes.empty() )
// mustn't specify time and rate
return false;
simgear::PropertyList::size_type num_times = time_nodes.empty()
? rate_nodes.size()
: time_nodes.size();
simgear::PropertyList value_nodes = arg->getChildren("value");
if( value_nodes.empty() )
{
simgear::PropertyList prop_nodes = arg->getChildren("property");
// must have one more property node
if( prop_nodes.size() != num_times + 1 )
return false;
simgear::PropertyList::size_type num_times = timeNodes.empty()
? rateNodes.size()
: timeNodes.size();
value_nodes.reserve(num_times);
for( size_t i = 1; i < prop_nodes.size(); ++i )
value_nodes.push_back( fgGetNode(prop_nodes[i]->getStringValue()) );
}
boost::scoped_array<double> value;
boost::scoped_array<double> time;
// must match
if( value_nodes.size() != num_times )
return false;
if( valueNodes.size() > 0 ) {
// must match
if( num_times != valueNodes.size() )
return false;
value.reset( new double[valueNodes.size()] );
for( simgear::PropertyList::size_type n = 0; n < valueNodes.size(); n++ ) {
value[n] = valueNodes[n]->getDoubleValue();
}
} else {
valueNodes = arg->getChildren("property");
// must have one more property node
if( valueNodes.size() - 1 != num_times )
return false;
value.reset( new double[valueNodes.size()-1] );
for( simgear::PropertyList::size_type n = 0; n < valueNodes.size()-1; n++ ) {
value[n] = fgGetNode(valueNodes[n+1]->getStringValue(), "/null")->getDoubleValue();
}
double_list deltas;
deltas.reserve(num_times);
if( !time_nodes.empty() )
{
for( size_t i = 0; i < num_times; ++i )
deltas.push_back( time_nodes[i]->getDoubleValue() );
}
else
{
for( size_t i = 0; i < num_times; ++i )
{
// TODO calculate delta based on property type
double delta = value_nodes[i]->getDoubleValue()
- ( i > 0
? value_nodes[i - 1]->getDoubleValue()
: prop->getDoubleValue()
);
deltas.push_back( fabs(delta / rate_nodes[i]->getDoubleValue()) );
}
}
time.reset( new double[num_times] );
if( !timeNodes.empty() ) {
for( simgear::PropertyList::size_type n = 0; n < num_times; n++ ) {
time[n] = timeNodes[n]->getDoubleValue();
}
} else {
for( simgear::PropertyList::size_type n = 0; n < num_times; n++ ) {
double delta = value[n]
- (n > 0 ? value[n - 1] : prop->getDoubleValue());
time[n] = fabs(delta / rateNodes[n]->getDoubleValue());
}
}
mgr->interpolate
(
prop,
arg->getStringValue("type", "numeric"),
value_nodes,
deltas,
arg->getStringValue("easing", "linear")
);
((SGInterpolator*)globals->get_subsystem_mgr()
->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator"))
->interpolate(prop, num_times, value.get(), time.get() );
return true;
return true;
}
/**

View file

@ -51,7 +51,6 @@
#include <simgear/misc/strutils.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/misc/interpolator.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/model/particles.hxx>
@ -107,6 +106,7 @@
#include "fg_io.hxx"
#include "fg_commands.hxx"
#include "fg_props.hxx"
#include "FGInterpolator.hxx"
#include "options.hxx"
#include "globals.hxx"
#include "logger.hxx"
@ -570,7 +570,9 @@ void fgCreateSubsystems() {
// Initialize the property interpolator subsystem. Put into the INIT
// group because the "nasal" subsystem may need it at GENERAL take-down.
////////////////////////////////////////////////////////////////////
globals->add_subsystem("interpolator", new SGInterpolator, SGSubsystemMgr::INIT);
globals->add_subsystem( "prop-interpolator",
new FGInterpolator,
SGSubsystemMgr::INIT );
////////////////////////////////////////////////////////////////////

View file

@ -33,8 +33,8 @@
// subsystem includes
#include <Aircraft/controls.hxx>
#include <simgear/misc/interpolator.hxx>
#include <Main/fg_props.hxx>
#include <Main/FGInterpolator.hxx>
#include <Main/fg_io.hxx>
#include <FDM/fdm_shell.hxx>
#include <Environment/environment_mgr.hxx>
@ -76,7 +76,7 @@ SGSubsystem* createSubsystemByName(const std::string& name)
MAKE_SUB(FGControls, "controls");
MAKE_SUB(FGSoundManager, "sound");
MAKE_SUB(SGInterpolator, "interpolator");
MAKE_SUB(FGInterpolator, "prop-interpolator")
MAKE_SUB(FGProperties, "properties");
MAKE_SUB(FDMShell, "fdm");
MAKE_SUB(FGEnvironmentMgr, "environment");

View file

@ -23,7 +23,6 @@
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/interpolator.hxx>
#include <simgear/structure/commands.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/structure/event_mgr.hxx>
@ -46,7 +45,7 @@
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <Main/fg_props.hxx>
#include <Main/FGInterpolator.hxx>
using std::map;
@ -514,29 +513,52 @@ static naRef f_cmdarg(naContext c, naRef me, int argc, naRef* args)
// value/delta numbers.
static naRef f_interpolate(naContext c, naRef me, int argc, naRef* args)
{
SGPropertyNode* node;
naRef prop = argc > 0 ? args[0] : naNil();
if(naIsString(prop)) node = fgGetNode(naStr_data(prop), true);
else if(naIsGhost(prop)) node = *(SGPropertyNode_ptr*)naGhost_ptr(prop);
else return naNil();
naRef curve = argc > 1 ? args[1] : naNil();
if(!naIsVector(curve)) return naNil();
int nPoints = naVec_size(curve) / 2;
double* values = new double[nPoints];
double* deltas = new double[nPoints];
for(int i=0; i<nPoints; i++) {
values[i] = naNumValue(naVec_get(curve, 2*i)).num;
deltas[i] = naNumValue(naVec_get(curve, 2*i+1)).num;
}
((SGInterpolator*)globals->get_subsystem_mgr()
->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator"))
->interpolate(node, nPoints, values, deltas);
delete[] values;
delete[] deltas;
FGInterpolator* mgr =
static_cast<FGInterpolator*>
(
globals->get_subsystem_mgr()
->get_group(SGSubsystemMgr::INIT)
->get_subsystem("prop-interpolator")
);
if( !mgr )
{
SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available");
return naNil();
};
SGPropertyNode* node;
naRef prop = argc > 0 ? args[0] : naNil();
if(naIsString(prop)) node = fgGetNode(naStr_data(prop), true);
else if(naIsGhost(prop)) node = *(SGPropertyNode_ptr*)naGhost_ptr(prop);
else return naNil();
naRef curve = argc > 1 ? args[1] : naNil();
if(!naIsVector(curve)) return naNil();
int nPoints = naVec_size(curve) / 2;
simgear::PropertyList value_nodes;
value_nodes.reserve(nPoints);
double_list deltas;
deltas.reserve(nPoints);
for( int i = 0; i < nPoints; ++i )
{
SGPropertyNode* val = new SGPropertyNode;
val->setDoubleValue(naNumValue(naVec_get(curve, 2*i)).num);
value_nodes.push_back(val);
deltas.push_back(naNumValue(naVec_get(curve, 2*i+1)).num);
}
mgr->interpolate
(
node,
"numeric",
value_nodes,
deltas,
"linear"
);
return naNil();
}
// This is a better RNG than the one in the default Nasal distribution