mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-08-03 15:27:13 -04:00
Merge branch 'quatinterp' into 'master'
Implement quadratic/TCB interpolation for quaternions (#2379) Closes #2379 See merge request OpenMW/openmw!4802
This commit is contained in:
commit
107778e13e
@ -94,6 +94,29 @@ namespace Misc
|
||||
}
|
||||
return 1 << depth;
|
||||
}
|
||||
|
||||
inline osg::Quat quatexp(const osg::Quat& q)
|
||||
{
|
||||
const float imagNorm2 = q.asVec3().length2();
|
||||
const float e = std::exp(q.w());
|
||||
if (imagNorm2 < 1e-6f)
|
||||
return { 0.f, 0.f, 0.f, e };
|
||||
|
||||
const float imagNorm = std::sqrt(imagNorm2);
|
||||
const float f = e * std::sin(imagNorm) / imagNorm;
|
||||
return { q.x() * f, q.y() * f, q.z() * f, e * std::cos(imagNorm) };
|
||||
}
|
||||
|
||||
inline osg::Quat quatlog(const osg::Quat& q)
|
||||
{
|
||||
const float imagNorm2 = q.asVec3().length2();
|
||||
const float norm = q.length();
|
||||
if (imagNorm2 < 1e-6f)
|
||||
return { 0.f, 0.f, 0.f, std::log(norm) };
|
||||
|
||||
const float f = std::acos(q.w() / norm) / std::sqrt(imagNorm2);
|
||||
return { q.x() * f, q.y() * f, q.z() * f, std::log(norm) };
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
|
||||
#include "exception.hpp"
|
||||
#include "niffile.hpp"
|
||||
#include "nifstream.hpp"
|
||||
@ -28,7 +30,7 @@ namespace Nif
|
||||
{
|
||||
T mValue;
|
||||
T mInTan; // Only for Quadratic interpolation, and never for QuaternionKeyList
|
||||
T mOutTan; // Only for Quadratic interpolation, and never for QuaternionKeyList
|
||||
T mOutTan; // For quaternions these are generated
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -107,6 +109,7 @@ namespace Nif
|
||||
readQuadratic(*nif, key);
|
||||
mKeys.emplace_back(time, key);
|
||||
}
|
||||
generateQuadraticTangents(mKeys);
|
||||
}
|
||||
else if (mInterpolationType == InterpolationType_TCB)
|
||||
{
|
||||
@ -155,6 +158,30 @@ namespace Nif
|
||||
|
||||
static void readQuadratic(NIFStream& nif, KeyT<osg::Quat>& key) { readValue(nif, key); }
|
||||
|
||||
template <typename U>
|
||||
static void generateQuadraticTangents(std::vector<std::pair<float, KeyT<U>>>& keys)
|
||||
{
|
||||
// These are predefined for all types except quaternions
|
||||
}
|
||||
|
||||
static void generateQuadraticTangents(std::vector<std::pair<float, KeyT<osg::Quat>>>& keys)
|
||||
{
|
||||
if (keys.size() <= 1)
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < keys.size(); ++i)
|
||||
{
|
||||
KeyT<osg::Quat>& curr = keys[i].second;
|
||||
const std::size_t prevIndex = i == 0 ? 0 : i - 1;
|
||||
const std::size_t nextIndex = i == keys.size() - 1 ? i : i + 1;
|
||||
const osg::Quat inv = curr.mValue.inverse();
|
||||
const osg::Quat& prev = keys[prevIndex].second.mValue;
|
||||
const osg::Quat& next = keys[nextIndex].second.mValue;
|
||||
const osg::Quat len = Misc::quatlog(inv * prev) + Misc::quatlog(inv * next);
|
||||
curr.mInTan = curr.mOutTan = curr.mValue * Misc::quatexp(len * -0.25f);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static void generateTCBTangents(std::vector<TCBKey<U>>& keys)
|
||||
{
|
||||
@ -176,8 +203,8 @@ namespace Nif
|
||||
const float w = (1.f - curr.mTension) * (1.f - curr.mContinuity) * (1.f - curr.mBias);
|
||||
const U prevDelta = prev != nullptr ? curr.mValue - prev->mValue : next->mValue - curr.mValue;
|
||||
const U nextDelta = next != nullptr ? next->mValue - curr.mValue : curr.mValue - prev->mValue;
|
||||
curr.mInTan = (prevDelta * x + nextDelta * y) * prevLen / (prevLen + nextLen);
|
||||
curr.mOutTan = (prevDelta * z + nextDelta * w) * nextLen / (prevLen + nextLen);
|
||||
curr.mInTan = (prevDelta * x + nextDelta * y) * (prevLen / (prevLen + nextLen));
|
||||
curr.mOutTan = (prevDelta * z + nextDelta * w) * (nextLen / (prevLen + nextLen));
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +215,32 @@ namespace Nif
|
||||
|
||||
static void generateTCBTangents(std::vector<TCBKey<osg::Quat>>& keys)
|
||||
{
|
||||
// TODO: implement TCB interpolation for quaternions
|
||||
if (keys.size() <= 1)
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < keys.size(); ++i)
|
||||
{
|
||||
TCBKey<osg::Quat>& curr = keys[i];
|
||||
const TCBKey<osg::Quat>* prev = (i == 0) ? nullptr : &keys[i - 1];
|
||||
const TCBKey<osg::Quat>* next = (i == keys.size() - 1) ? nullptr : &keys[i + 1];
|
||||
const float prevLen = prev != nullptr && next != nullptr ? curr.mTime - prev->mTime : 1.f;
|
||||
const float nextLen = prev != nullptr && next != nullptr ? next->mTime - curr.mTime : 1.f;
|
||||
if (prevLen + nextLen == 0.f)
|
||||
continue;
|
||||
const float x = (1.f - curr.mTension) * (1.f - curr.mContinuity) * (1.f + curr.mBias);
|
||||
const float y = (1.f - curr.mTension) * (1.f + curr.mContinuity) * (1.f - curr.mBias);
|
||||
const float z = (1.f - curr.mTension) * (1.f + curr.mContinuity) * (1.f + curr.mBias);
|
||||
const float w = (1.f - curr.mTension) * (1.f - curr.mContinuity) * (1.f - curr.mBias);
|
||||
const osg::Quat inv = curr.mValue.inverse();
|
||||
const osg::Quat prevDelta = prev != nullptr ? Misc::quatlog(prev->mValue.inverse() * curr.mValue)
|
||||
: Misc::quatlog(inv * next->mValue);
|
||||
const osg::Quat nextDelta = next != nullptr ? Misc::quatlog(inv * next->mValue)
|
||||
: Misc::quatlog(prev->mValue.inverse() * curr.mValue);
|
||||
const osg::Quat t1 = (prevDelta * x + nextDelta * y) * (nextLen / (prevLen + nextLen));
|
||||
const osg::Quat t2 = (prevDelta * z + nextDelta * w) * (prevLen / (prevLen + nextLen));
|
||||
curr.mInTan = curr.mValue * Misc::quatexp((prevDelta - t1) * 0.5f);
|
||||
curr.mOutTan = curr.mValue * Misc::quatexp((t2 - nextDelta) * 0.5f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -165,7 +165,16 @@ namespace NifOsg
|
||||
{
|
||||
case Nif::InterpolationType_Constant:
|
||||
return fraction > 0.5f ? b.mValue : a.mValue;
|
||||
// TODO: Implement Quadratic and TBC interpolation
|
||||
case Nif::InterpolationType_Quadratic:
|
||||
case Nif::InterpolationType_TCB:
|
||||
{
|
||||
// Spherical quadrangle interpolation
|
||||
osg::Quat from, to, result;
|
||||
from.slerp(fraction, a.mValue, b.mValue);
|
||||
to.slerp(fraction, a.mOutTan, b.mInTan);
|
||||
result.slerp(2.f * fraction * (1.f - fraction), from, to);
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
{
|
||||
osg::Quat result;
|
||||
|
Loading…
x
Reference in New Issue
Block a user