Optimize ValueInterpolator / KeyframeController

Cache the current position in the animation track and attempt to reuse it in the next frame.

Decent speed up for the Update phase, about 0.3 ms faster in Balmora.
openmw-38
scrawl 9 years ago
parent f5f3d18b8e
commit 695fcf41c4

@ -37,6 +37,9 @@ template<typename T, T (NIFStream::*getValue)()>
struct KeyMapT {
typedef std::map< float, KeyT<T> > MapType;
typedef T ValueType;
typedef KeyT<T> KeyType;
static const unsigned int sLinearInterpolation = 1;
static const unsigned int sQuadraticInterpolation = 2;
static const unsigned int sTBCInterpolation = 3;

@ -86,56 +86,23 @@ KeyframeController::KeyframeController(const KeyframeController &copy, const osg
KeyframeController::KeyframeController(const Nif::NiKeyframeData *data)
: mRotations(data->mRotations)
, mXRotations(data->mXRotations)
, mYRotations(data->mYRotations)
, mZRotations(data->mZRotations)
, mTranslations(data->mTranslations)
, mScales(data->mScales)
, mXRotations(data->mXRotations, 0.f)
, mYRotations(data->mYRotations, 0.f)
, mZRotations(data->mZRotations, 0.f)
, mTranslations(data->mTranslations, osg::Vec3f())
, mScales(data->mScales, 1.f)
{
}
osg::Quat KeyframeController::interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time)
{
if(time <= keys.begin()->first)
return keys.begin()->second.mValue;
Nif::QuaternionKeyMap::MapType::const_iterator it = keys.lower_bound(time);
if (it != keys.end())
{
float aTime = it->first;
const Nif::QuaternionKey* aKey = &it->second;
assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function
Nif::QuaternionKeyMap::MapType::const_iterator last = --it;
float aLastTime = last->first;
const Nif::QuaternionKey* aLastKey = &last->second;
float a = (time - aLastTime) / (aTime - aLastTime);
osg::Quat v1 = aLastKey->mValue;
osg::Quat v2 = aKey->mValue;
// don't take the long path
if (v1.x()*v2.x() + v1.y()*v2.y() + v1.z()*v2.z() + v1.w()*v2.w() < 0) // dotProduct(v1,v2)
v1 = -v1;
osg::Quat result;
result.slerp(a, v1, v2);
return result;
}
else
return keys.rbegin()->second.mValue;
}
osg::Quat KeyframeController::getXYZRotation(float time) const
{
float xrot = 0, yrot = 0, zrot = 0;
if (mXRotations.get())
xrot = interpKey(mXRotations->mKeys, time);
if (mYRotations.get())
yrot = interpKey(mYRotations->mKeys, time);
if (mZRotations.get())
zrot = interpKey(mZRotations->mKeys, time);
if (!mXRotations.empty())
xrot = mXRotations.interpKey(time);
if (!mYRotations.empty())
yrot = mYRotations.interpKey(time);
if (!mZRotations.empty())
zrot = mZRotations.interpKey(time);
osg::Quat xr(xrot, osg::Vec3f(1,0,0));
osg::Quat yr(yrot, osg::Vec3f(0,1,0));
osg::Quat zr(zrot, osg::Vec3f(0,0,1));
@ -144,8 +111,8 @@ osg::Quat KeyframeController::getXYZRotation(float time) const
osg::Vec3f KeyframeController::getTranslation(float time) const
{
if(mTranslations.get() && mTranslations->mKeys.size() > 0)
return interpKey(mTranslations->mKeys, time);
if(!mTranslations.empty())
return mTranslations.interpKey(time);
return osg::Vec3f();
}
@ -162,12 +129,12 @@ void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv)
Nif::Matrix3& rot = userdata->mRotationScale;
bool setRot = false;
if(mRotations.get() && !mRotations->mKeys.empty())
if(!mRotations.empty())
{
mat.setRotate(interpKey(mRotations->mKeys, time));
mat.setRotate(mRotations.interpKey(time));
setRot = true;
}
else if (mXRotations.get() || mYRotations.get() || mZRotations.get())
else if (!mXRotations.empty() || !mYRotations.empty() || !mZRotations.empty())
{
mat.setRotate(getXYZRotation(time));
setRot = true;
@ -186,15 +153,15 @@ void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv)
rot.mValues[i][j] = mat(j,i); // NB column/row major difference
float& scale = userdata->mScale;
if(mScales.get() && !mScales->mKeys.empty())
scale = interpKey(mScales->mKeys, time);
if(!mScales.empty())
scale = mScales.interpKey(time);
for (int i=0;i<3;++i)
for (int j=0;j<3;++j)
mat(i,j) *= scale;
if(mTranslations.get() && !mTranslations->mKeys.empty())
mat.setTrans(interpKey(mTranslations->mKeys, time));
if(!mTranslations.empty())
mat.setTrans(mTranslations.interpKey(time));
trans->setMatrix(mat);
}
@ -216,7 +183,7 @@ GeomMorpherController::GeomMorpherController(const GeomMorpherController &copy,
GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data)
{
for (unsigned int i=0; i<data->mMorphs.size(); ++i)
mKeyFrames.push_back(data->mMorphs[i].mKeyFrames);
mKeyFrames.push_back(FloatInterpolator(data->mMorphs[i].mKeyFrames));
}
void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)
@ -228,11 +195,11 @@ void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable
return;
float input = getInputValue(nv);
int i = 0;
for (std::vector<Nif::FloatKeyMapPtr>::iterator it = mKeyFrames.begin()+1; it != mKeyFrames.end(); ++it,++i)
for (std::vector<FloatInterpolator>::iterator it = mKeyFrames.begin()+1; it != mKeyFrames.end(); ++it,++i)
{
float val = 0;
if (!(*it)->mKeys.empty())
val = interpKey((*it)->mKeys, input);
if (!(*it).empty())
val = it->interpKey(input);
val = std::max(0.f, std::min(1.f, val));
osgAnimation::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i);
@ -252,10 +219,10 @@ UVController::UVController()
}
UVController::UVController(const Nif::NiUVData *data, std::set<int> textureUnits)
: mUTrans(data->mKeyList[0])
, mVTrans(data->mKeyList[1])
, mUScale(data->mKeyList[2])
, mVScale(data->mKeyList[3])
: mUTrans(data->mKeyList[0], 0.f)
, mVTrans(data->mKeyList[1], 0.f)
, mUScale(data->mKeyList[2], 1.f)
, mVScale(data->mKeyList[3], 1.f)
, mTextureUnits(textureUnits)
{
}
@ -282,10 +249,10 @@ void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
if (hasInput())
{
float value = getInputValue(nv);
float uTrans = interpKey(mUTrans->mKeys, value, 0.0f);
float vTrans = interpKey(mVTrans->mKeys, value, 0.0f);
float uScale = interpKey(mUScale->mKeys, value, 1.0f);
float vScale = interpKey(mVScale->mKeys, value, 1.0f);
float uTrans = mUTrans.interpKey(value);
float vTrans = mVTrans.interpKey(value);
float uScale = mUScale.interpKey(value);
float vScale = mVScale.interpKey(value);
osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
mat.setTrans(uTrans, vTrans, 0);
@ -340,7 +307,7 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)
}
AlphaController::AlphaController(const Nif::NiFloatData *data)
: mData(data->mKeyList)
: mData(data->mKeyList, 1.f)
{
}
@ -350,7 +317,7 @@ AlphaController::AlphaController()
}
AlphaController::AlphaController(const AlphaController &copy, const osg::CopyOp &copyop)
: StateSetUpdater(copy, copyop), Controller(copy), ValueInterpolator()
: StateSetUpdater(copy, copyop), Controller(copy)
, mData(copy.mData)
{
}
@ -366,7 +333,7 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
{
if (hasInput())
{
float value = interpKey(mData->mKeys, getInputValue(nv));
float value = mData.interpKey(getInputValue(nv));
osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
diffuse.a() = value;
@ -375,7 +342,7 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
}
MaterialColorController::MaterialColorController(const Nif::NiPosData *data)
: mData(data->mKeyList)
: mData(data->mKeyList, osg::Vec3f(1,1,1))
{
}
@ -400,7 +367,7 @@ void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *n
{
if (hasInput())
{
osg::Vec3f value = interpKey(mData->mKeys, getInputValue(nv));
osg::Vec3f value = mData.interpKey(getInputValue(nv));
osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
diffuse.set(value.x(), value.y(), value.z(), diffuse.a());

@ -42,38 +42,116 @@ namespace osgAnimation
namespace NifOsg
{
// interpolation of keyframes
template <typename MapT, typename InterpolationFunc>
class ValueInterpolator
{
protected:
template <typename T>
T interpKey (const std::map< float, Nif::KeyT<T> >& keys, float time, T defaultValue = T()) const
public:
ValueInterpolator()
: mDefaultVal(typename MapT::ValueType())
{
}
ValueInterpolator(boost::shared_ptr<MapT> keys, typename MapT::ValueType defaultVal = typename MapT::ValueType())
: mKeys(keys)
, mDefaultVal(defaultVal)
{
if (keys)
{
mLastLowKey = mKeys->mKeys.end();
mLastHighKey = mKeys->mKeys.end();
}
}
typename MapT::ValueType interpKey(float time) const
{
if (keys.size() == 0)
return defaultValue;
if (empty())
return mDefaultVal;
const typename MapT::MapType & keys = mKeys->mKeys;
if(time <= keys.begin()->first)
return keys.begin()->second.mValue;
typename std::map< float, Nif::KeyT<T> >::const_iterator it = keys.lower_bound(time);
// retrieve the current position in the map, optimized for the most common case
// where time moves linearly along the keyframe track
typename MapT::MapType::const_iterator it = mLastHighKey;
if (mLastHighKey != keys.end())
{
if (time > mLastHighKey->first)
{
// try if we're there by incrementing one
++mLastLowKey;
++mLastHighKey;
it = mLastHighKey;
}
if (mLastHighKey == keys.end() || (time < mLastLowKey->first || time > mLastHighKey->first))
it = keys.lower_bound(time); // still not there, reorient by performing lower_bound check on the whole map
}
else
it = keys.lower_bound(time);
// now do the actual interpolation
if (it != keys.end())
{
float aTime = it->first;
const Nif::KeyT<T>* aKey = &it->second;
const typename MapT::KeyType* aKey = &it->second;
// cache for next time
mLastHighKey = it;
assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function
typename std::map< float, Nif::KeyT<T> >::const_iterator last = --it;
typename MapT::MapType::const_iterator last = --it;
mLastLowKey = last;
float aLastTime = last->first;
const Nif::KeyT<T>* aLastKey = &last->second;
const typename MapT::KeyType* aLastKey = &last->second;
float a = (time - aLastTime) / (aTime - aLastTime);
return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a);
return InterpolationFunc()(aLastKey->mValue, aKey->mValue, a);
}
else
return keys.rbegin()->second.mValue;
}
bool empty() const
{
return !mKeys || mKeys->mKeys.empty();
}
private:
mutable typename MapT::MapType::const_iterator mLastLowKey;
mutable typename MapT::MapType::const_iterator mLastHighKey;
boost::shared_ptr<MapT> mKeys;
typename MapT::ValueType mDefaultVal;
};
struct LerpFunc
{
template <typename ValueType>
inline ValueType operator()(const ValueType& a, const ValueType& b, float fraction)
{
return a + ((b - a) * fraction);
}
};
struct QuaternionSlerpFunc
{
inline osg::Quat operator()(const osg::Quat& a, const osg::Quat& b, float fraction)
{
osg::Quat result;
result.slerp(fraction, a, b);
return result;
}
};
typedef ValueInterpolator<Nif::QuaternionKeyMap, QuaternionSlerpFunc> QuaternionInterpolator;
typedef ValueInterpolator<Nif::FloatKeyMap, LerpFunc> FloatInterpolator;
typedef ValueInterpolator<Nif::Vector3KeyMap, LerpFunc> Vec3Interpolator;
class ControllerFunction : public SceneUtil::ControllerFunction
{
private:
@ -98,7 +176,7 @@ namespace NifOsg
};
/// Must be set on an osgAnimation::MorphGeometry.
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller, public ValueInterpolator
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller
{
public:
GeomMorpherController(const Nif::NiMorphData* data);
@ -110,10 +188,10 @@ namespace NifOsg
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable);
private:
std::vector<Nif::FloatKeyMapPtr> mKeyFrames;
std::vector<FloatInterpolator> mKeyFrames;
};
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller, public ValueInterpolator
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
{
public:
KeyframeController(const Nif::NiKeyframeData *data);
@ -127,23 +205,19 @@ namespace NifOsg
virtual void operator() (osg::Node*, osg::NodeVisitor*);
private:
Nif::QuaternionKeyMapPtr mRotations;
Nif::FloatKeyMapPtr mXRotations;
Nif::FloatKeyMapPtr mYRotations;
Nif::FloatKeyMapPtr mZRotations;
Nif::Vector3KeyMapPtr mTranslations;
Nif::FloatKeyMapPtr mScales;
QuaternionInterpolator mRotations;
using ValueInterpolator::interpKey;
FloatInterpolator mXRotations;
FloatInterpolator mYRotations;
FloatInterpolator mZRotations;
osg::Quat interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time);
Vec3Interpolator mTranslations;
FloatInterpolator mScales;
osg::Quat getXYZRotation(float time) const;
};
class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator
class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{
public:
UVController();
@ -156,10 +230,10 @@ namespace NifOsg
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv);
private:
Nif::FloatKeyMapPtr mUTrans;
Nif::FloatKeyMapPtr mVTrans;
Nif::FloatKeyMapPtr mUScale;
Nif::FloatKeyMapPtr mVScale;
FloatInterpolator mUTrans;
FloatInterpolator mVTrans;
FloatInterpolator mUScale;
FloatInterpolator mVScale;
std::set<int> mTextureUnits;
};
@ -180,10 +254,10 @@ namespace NifOsg
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
};
class AlphaController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator
class AlphaController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{
private:
Nif::FloatKeyMapPtr mData;
FloatInterpolator mData;
public:
AlphaController(const Nif::NiFloatData *data);
@ -197,10 +271,10 @@ namespace NifOsg
META_Object(NifOsg, AlphaController)
};
class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator
class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{
private:
Nif::Vector3KeyMapPtr mData;
Vec3Interpolator mData;
public:
MaterialColorController(const Nif::NiPosData *data);

@ -127,7 +127,7 @@ void GrowFadeAffector::operate(osgParticle::Particle* particle, double /* dt */)
}
ParticleColorAffector::ParticleColorAffector(const Nif::NiColorData *clrdata)
: mData(*clrdata)
: mData(clrdata->mKeyMap, osg::Vec4f(1,1,1,1))
{
}
@ -145,7 +145,7 @@ ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector &copy,
void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)
{
float time = static_cast<float>(particle->getAge()/particle->getLifeTime());
osg::Vec4f color = interpKey(mData.mKeyMap->mKeys, time, osg::Vec4f(1,1,1,1));
osg::Vec4f color = mData.interpKey(time);
particle->setColorRange(osgParticle::rangev4(color, color));
}

@ -130,7 +130,8 @@ namespace NifOsg
float mCachedDefaultSize;
};
class ParticleColorAffector : public osgParticle::Operator, public ValueInterpolator
typedef ValueInterpolator<Nif::Vector4KeyMap, LerpFunc> Vec4Interpolator;
class ParticleColorAffector : public osgParticle::Operator
{
public:
ParticleColorAffector(const Nif::NiColorData* clrdata);
@ -142,7 +143,7 @@ namespace NifOsg
virtual void operate(osgParticle::Particle* particle, double dt);
private:
Nif::NiColorData mData;
Vec4Interpolator mData;
};
class GravityAffector : public osgParticle::Operator

Loading…
Cancel
Save