From 1a9e29844b663dfde8b93f19e6adf25cd0da1c4f Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 12 Feb 2025 23:12:07 +0300 Subject: [PATCH] Implement TCB interpolation for vectors and scalars (#2379) --- components/nif/nifkey.hpp | 94 ++++++++++++++++++++++++-------- components/nifosg/controller.hpp | 2 +- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 604cf92c33..bd362101c6 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -3,7 +3,9 @@ #ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP #define OPENMW_COMPONENTS_NIF_NIFKEY_HPP +#include #include +#include #include "exception.hpp" #include "niffile.hpp" @@ -17,7 +19,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 +30,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 +104,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& key : tcbKeys) { - float time; - nif->read(time); - readTBC(*nif, key); - mKeys[time] = key; + nif->read(key.mTime); + key.mValue = ((*nif).*getValue)(); + nif->read(key.mTension); + nif->read(key.mContinuity); + nif->read(key.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,12 +148,52 @@ 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; + + std::sort(keys.begin(), keys.end(), [](const auto& a, const auto& b) { return a.mTime < b.mTime; }); + for (size_t i = 0; i < keys.size(); ++i) + { + TCBKey& curr = keys[i]; + const TCBKey& prev = (i == 0) ? curr : keys[i - 1]; + const TCBKey& next = (i == keys.size() - 1) ? curr : keys[i + 1]; + const float prevLen = curr.mTime - prev.mTime; + const float nextLen = next.mTime - curr.mTime; + if (prevLen + nextLen <= 0.f) + continue; + + const U prevDelta = curr.mValue - prev.mValue; + const U nextDelta = next.mValue - curr.mValue; + const float t = curr.mTension; + const float c = curr.mContinuity; + const float b = curr.mBias; + + U x{}, y{}, z{}, w{}; + if (prevLen > 0.f) + x = prevDelta / prevLen * (1 - t) * (1 - c) * (1 + b); + if (nextLen > 0.f) + y = nextDelta / nextLen * (1 - t) * (1 + c) * (1 - b); + if (prevLen > 0.f) + z = prevDelta / prevLen * (1 - t) * (1 + c) * (1 + b); + if (nextLen > 0.f) + w = nextDelta / nextLen * (1 - t) * (1 - c) * (1 - b); + + curr.mInTan = (x + y) * prevLen / (prevLen + nextLen); + curr.mOutTan = (z + 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>; 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); }