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 { struct KeyMapT {
typedef std::map< float, KeyT<T> > MapType; typedef std::map< float, KeyT<T> > MapType;
typedef T ValueType;
typedef KeyT<T> KeyType;
static const unsigned int sLinearInterpolation = 1; static const unsigned int sLinearInterpolation = 1;
static const unsigned int sQuadraticInterpolation = 2; static const unsigned int sQuadraticInterpolation = 2;
static const unsigned int sTBCInterpolation = 3; static const unsigned int sTBCInterpolation = 3;

@ -86,56 +86,23 @@ KeyframeController::KeyframeController(const KeyframeController &copy, const osg
KeyframeController::KeyframeController(const Nif::NiKeyframeData *data) KeyframeController::KeyframeController(const Nif::NiKeyframeData *data)
: mRotations(data->mRotations) : mRotations(data->mRotations)
, mXRotations(data->mXRotations) , mXRotations(data->mXRotations, 0.f)
, mYRotations(data->mYRotations) , mYRotations(data->mYRotations, 0.f)
, mZRotations(data->mZRotations) , mZRotations(data->mZRotations, 0.f)
, mTranslations(data->mTranslations) , mTranslations(data->mTranslations, osg::Vec3f())
, mScales(data->mScales) , 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 osg::Quat KeyframeController::getXYZRotation(float time) const
{ {
float xrot = 0, yrot = 0, zrot = 0; float xrot = 0, yrot = 0, zrot = 0;
if (mXRotations.get()) if (!mXRotations.empty())
xrot = interpKey(mXRotations->mKeys, time); xrot = mXRotations.interpKey(time);
if (mYRotations.get()) if (!mYRotations.empty())
yrot = interpKey(mYRotations->mKeys, time); yrot = mYRotations.interpKey(time);
if (mZRotations.get()) if (!mZRotations.empty())
zrot = interpKey(mZRotations->mKeys, time); zrot = mZRotations.interpKey(time);
osg::Quat xr(xrot, osg::Vec3f(1,0,0)); osg::Quat xr(xrot, osg::Vec3f(1,0,0));
osg::Quat yr(yrot, osg::Vec3f(0,1,0)); osg::Quat yr(yrot, osg::Vec3f(0,1,0));
osg::Quat zr(zrot, osg::Vec3f(0,0,1)); 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 osg::Vec3f KeyframeController::getTranslation(float time) const
{ {
if(mTranslations.get() && mTranslations->mKeys.size() > 0) if(!mTranslations.empty())
return interpKey(mTranslations->mKeys, time); return mTranslations.interpKey(time);
return osg::Vec3f(); return osg::Vec3f();
} }
@ -162,12 +129,12 @@ void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv)
Nif::Matrix3& rot = userdata->mRotationScale; Nif::Matrix3& rot = userdata->mRotationScale;
bool setRot = false; 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; setRot = true;
} }
else if (mXRotations.get() || mYRotations.get() || mZRotations.get()) else if (!mXRotations.empty() || !mYRotations.empty() || !mZRotations.empty())
{ {
mat.setRotate(getXYZRotation(time)); mat.setRotate(getXYZRotation(time));
setRot = true; 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 rot.mValues[i][j] = mat(j,i); // NB column/row major difference
float& scale = userdata->mScale; float& scale = userdata->mScale;
if(mScales.get() && !mScales->mKeys.empty()) if(!mScales.empty())
scale = interpKey(mScales->mKeys, time); scale = mScales.interpKey(time);
for (int i=0;i<3;++i) for (int i=0;i<3;++i)
for (int j=0;j<3;++j) for (int j=0;j<3;++j)
mat(i,j) *= scale; mat(i,j) *= scale;
if(mTranslations.get() && !mTranslations->mKeys.empty()) if(!mTranslations.empty())
mat.setTrans(interpKey(mTranslations->mKeys, time)); mat.setTrans(mTranslations.interpKey(time));
trans->setMatrix(mat); trans->setMatrix(mat);
} }
@ -216,7 +183,7 @@ GeomMorpherController::GeomMorpherController(const GeomMorpherController &copy,
GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data) GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data)
{ {
for (unsigned int i=0; i<data->mMorphs.size(); ++i) 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) void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)
@ -228,11 +195,11 @@ void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable
return; return;
float input = getInputValue(nv); float input = getInputValue(nv);
int i = 0; 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; float val = 0;
if (!(*it)->mKeys.empty()) if (!(*it).empty())
val = interpKey((*it)->mKeys, input); val = it->interpKey(input);
val = std::max(0.f, std::min(1.f, val)); val = std::max(0.f, std::min(1.f, val));
osgAnimation::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i); osgAnimation::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i);
@ -252,10 +219,10 @@ UVController::UVController()
} }
UVController::UVController(const Nif::NiUVData *data, std::set<int> textureUnits) UVController::UVController(const Nif::NiUVData *data, std::set<int> textureUnits)
: mUTrans(data->mKeyList[0]) : mUTrans(data->mKeyList[0], 0.f)
, mVTrans(data->mKeyList[1]) , mVTrans(data->mKeyList[1], 0.f)
, mUScale(data->mKeyList[2]) , mUScale(data->mKeyList[2], 1.f)
, mVScale(data->mKeyList[3]) , mVScale(data->mKeyList[3], 1.f)
, mTextureUnits(textureUnits) , mTextureUnits(textureUnits)
{ {
} }
@ -282,10 +249,10 @@ void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
if (hasInput()) if (hasInput())
{ {
float value = getInputValue(nv); float value = getInputValue(nv);
float uTrans = interpKey(mUTrans->mKeys, value, 0.0f); float uTrans = mUTrans.interpKey(value);
float vTrans = interpKey(mVTrans->mKeys, value, 0.0f); float vTrans = mVTrans.interpKey(value);
float uScale = interpKey(mUScale->mKeys, value, 1.0f); float uScale = mUScale.interpKey(value);
float vScale = interpKey(mVScale->mKeys, value, 1.0f); float vScale = mVScale.interpKey(value);
osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1); osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
mat.setTrans(uTrans, vTrans, 0); mat.setTrans(uTrans, vTrans, 0);
@ -340,7 +307,7 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)
} }
AlphaController::AlphaController(const Nif::NiFloatData *data) 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) AlphaController::AlphaController(const AlphaController &copy, const osg::CopyOp &copyop)
: StateSetUpdater(copy, copyop), Controller(copy), ValueInterpolator() : StateSetUpdater(copy, copyop), Controller(copy)
, mData(copy.mData) , mData(copy.mData)
{ {
} }
@ -366,7 +333,7 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
{ {
if (hasInput()) 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::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
diffuse.a() = value; diffuse.a() = value;
@ -375,7 +342,7 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
} }
MaterialColorController::MaterialColorController(const Nif::NiPosData *data) 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()) 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::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
diffuse.set(value.x(), value.y(), value.z(), diffuse.a()); diffuse.set(value.x(), value.y(), value.z(), diffuse.a());

@ -42,38 +42,116 @@ namespace osgAnimation
namespace NifOsg namespace NifOsg
{ {
// interpolation of keyframes
template <typename MapT, typename InterpolationFunc>
class ValueInterpolator class ValueInterpolator
{ {
protected: public:
template <typename T> ValueInterpolator()
T interpKey (const std::map< float, Nif::KeyT<T> >& keys, float time, T defaultValue = T()) const : 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) if (empty())
return defaultValue; return mDefaultVal;
const typename MapT::MapType & keys = mKeys->mKeys;
if(time <= keys.begin()->first) if(time <= keys.begin()->first)
return keys.begin()->second.mValue; 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()) if (it != keys.end())
{ {
float aTime = it->first; 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 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; float aLastTime = last->first;
const Nif::KeyT<T>* aLastKey = &last->second; const typename MapT::KeyType* aLastKey = &last->second;
float a = (time - aLastTime) / (aTime - aLastTime); float a = (time - aLastTime) / (aTime - aLastTime);
return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a);
return InterpolationFunc()(aLastKey->mValue, aKey->mValue, a);
} }
else else
return keys.rbegin()->second.mValue; 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 class ControllerFunction : public SceneUtil::ControllerFunction
{ {
private: private:
@ -98,7 +176,7 @@ namespace NifOsg
}; };
/// Must be set on an osgAnimation::MorphGeometry. /// 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: public:
GeomMorpherController(const Nif::NiMorphData* data); GeomMorpherController(const Nif::NiMorphData* data);
@ -110,10 +188,10 @@ namespace NifOsg
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable); virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable);
private: 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: public:
KeyframeController(const Nif::NiKeyframeData *data); KeyframeController(const Nif::NiKeyframeData *data);
@ -127,23 +205,19 @@ namespace NifOsg
virtual void operator() (osg::Node*, osg::NodeVisitor*); virtual void operator() (osg::Node*, osg::NodeVisitor*);
private: private:
Nif::QuaternionKeyMapPtr mRotations; QuaternionInterpolator mRotations;
Nif::FloatKeyMapPtr mXRotations;
Nif::FloatKeyMapPtr mYRotations;
Nif::FloatKeyMapPtr mZRotations;
Nif::Vector3KeyMapPtr mTranslations;
Nif::FloatKeyMapPtr mScales;
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; 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: public:
UVController(); UVController();
@ -156,10 +230,10 @@ namespace NifOsg
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv);
private: private:
Nif::FloatKeyMapPtr mUTrans; FloatInterpolator mUTrans;
Nif::FloatKeyMapPtr mVTrans; FloatInterpolator mVTrans;
Nif::FloatKeyMapPtr mUScale; FloatInterpolator mUScale;
Nif::FloatKeyMapPtr mVScale; FloatInterpolator mVScale;
std::set<int> mTextureUnits; std::set<int> mTextureUnits;
}; };
@ -180,10 +254,10 @@ namespace NifOsg
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); 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: private:
Nif::FloatKeyMapPtr mData; FloatInterpolator mData;
public: public:
AlphaController(const Nif::NiFloatData *data); AlphaController(const Nif::NiFloatData *data);
@ -197,10 +271,10 @@ namespace NifOsg
META_Object(NifOsg, AlphaController) META_Object(NifOsg, AlphaController)
}; };
class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller, public ValueInterpolator class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{ {
private: private:
Nif::Vector3KeyMapPtr mData; Vec3Interpolator mData;
public: public:
MaterialColorController(const Nif::NiPosData *data); MaterialColorController(const Nif::NiPosData *data);

@ -127,7 +127,7 @@ void GrowFadeAffector::operate(osgParticle::Particle* particle, double /* dt */)
} }
ParticleColorAffector::ParticleColorAffector(const Nif::NiColorData *clrdata) 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 */) void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)
{ {
float time = static_cast<float>(particle->getAge()/particle->getLifeTime()); 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)); particle->setColorRange(osgParticle::rangev4(color, color));
} }

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

Loading…
Cancel
Save