forked from mirror/openmw-tes3mp
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.
This commit is contained in:
parent
f5f3d18b8e
commit
695fcf41c4
5 changed files with 151 additions and 106 deletions
|
@ -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 ©, 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 ©,
|
|||
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 ©, const osg::CopyOp ©op)
|
||||
: 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())
|
||||
{
|
||||
if (keys.size() == 0)
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
QuaternionInterpolator mRotations;
|
||||
|
||||
Nif::FloatKeyMapPtr mXRotations;
|
||||
Nif::FloatKeyMapPtr mYRotations;
|
||||
Nif::FloatKeyMapPtr mZRotations;
|
||||
FloatInterpolator mXRotations;
|
||||
FloatInterpolator mYRotations;
|
||||
FloatInterpolator mZRotations;
|
||||
|
||||
Nif::Vector3KeyMapPtr mTranslations;
|
||||
Nif::FloatKeyMapPtr mScales;
|
||||
|
||||
using ValueInterpolator::interpKey;
|
||||
|
||||
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 ©,
|
|||
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…
Reference in a new issue