diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 604cf92c33..5a755c8d0d 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -4,6 +4,7 @@ #define OPENMW_COMPONENTS_NIF_NIFKEY_HPP #include +#include #include "exception.hpp" #include "niffile.hpp" @@ -17,7 +18,7 @@ namespace Nif InterpolationType_Unknown = 0, InterpolationType_Linear = 1, InterpolationType_Quadratic = 2, - InterpolationType_TBC = 3, + InterpolationType_TCB = 3, InterpolationType_XYZ = 4, InterpolationType_Constant = 5 }; @@ -28,18 +29,19 @@ namespace Nif T mValue; T mInTan; // Only for Quadratic interpolation, and never for QuaternionKeyList T mOutTan; // Only for Quadratic interpolation, and never for QuaternionKeyList - - // FIXME: Implement TBC interpolation - /* - float mTension; // Only for TBC interpolation - float mBias; // Only for TBC interpolation - float mContinuity; // Only for TBC interpolation - */ }; - using FloatKey = KeyT; - using Vector3Key = KeyT; - using Vector4Key = KeyT; - using QuaternionKey = KeyT; + + template + struct TCBKey + { + float mTime; + T mValue{}; + T mInTan{}; + T mOutTan{}; + float mTension; + float mContinuity; + float mBias; + }; template struct KeyMapT @@ -101,15 +103,20 @@ namespace Nif mKeys[time] = key; } } - else if (mInterpolationType == InterpolationType_TBC) + else if (mInterpolationType == InterpolationType_TCB) { - for (size_t i = 0; i < count; i++) + std::vector> tcbKeys(count); + for (TCBKey& tcbKey : tcbKeys) { - float time; - nif->read(time); - readTBC(*nif, key); - mKeys[time] = key; + nif->read(tcbKey.mTime); + tcbKey.mValue = ((*nif).*getValue)(); + nif->read(tcbKey.mTension); + nif->read(tcbKey.mContinuity); + nif->read(tcbKey.mBias); } + generateTCBTangents(tcbKeys); + for (TCBKey& key : tcbKeys) + mKeys[key.mTime] = KeyType{ std::move(key.mValue), std::move(key.mInTan), std::move(key.mOutTan) }; } else if (mInterpolationType == InterpolationType_XYZ) { @@ -140,14 +147,43 @@ namespace Nif static void readQuadratic(NIFStream& nif, KeyT& key) { readValue(nif, key); } - static void readTBC(NIFStream& nif, KeyT& key) + template + static void generateTCBTangents(std::vector>& keys) { - readValue(nif, key); - /*key.mTension = */ nif.get(); - /*key.mBias = */ nif.get(); - /*key.mContinuity = */ nif.get(); + if (keys.size() <= 1) + return; + + for (std::size_t i = 0; i < keys.size(); ++i) + { + TCBKey& curr = keys[i]; + const TCBKey* prev = (i == 0) ? nullptr : &keys[i - 1]; + const TCBKey* 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 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); + } + } + + static void generateTCBTangents(std::vector>& keys) + { + // TODO: is this even legal? + } + + static void generateTCBTangents(std::vector>& keys) + { + // TODO: implement TCB interpolation for quaternions } }; + using FloatKeyMap = KeyMapT>; using Vector3KeyMap = KeyMapT>; using Vector4KeyMap = KeyMapT>; diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 99d3df9545..468668ce76 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -131,6 +131,7 @@ namespace NifOsg case Nif::InterpolationType_Constant: return fraction > 0.5f ? b.mValue : a.mValue; case Nif::InterpolationType_Quadratic: + case Nif::InterpolationType_TCB: { // Using a cubic Hermite spline. // b1(t) = 2t^3 - 3t^2 + 1 @@ -147,7 +148,6 @@ namespace NifOsg const float b4 = t3 - t2; return a.mValue * b1 + b.mValue * b2 + a.mOutTan * b3 + b.mInTan * b4; } - // TODO: Implement TBC interpolation default: return a.mValue + ((b.mValue - a.mValue) * fraction); }