Files
FC1/CryAnimation/CryKeyInterpolation.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

80 lines
2.7 KiB
C++

// Actually, the following files might have been made static methods of corresponding
// key structures; but the key structures are declared in a common interface file for now.
#include "stdafx.h"
#include "CryKeyInterpolation.h"
// interpolates the given key linearly out of the given left and right keys, given the time
void InterpolateCryBoneKey (const CryBoneKey& keyLeft, const CryBoneKey& keyRight, int nTime, CryBoneKey& keyOutput)
{
keyOutput.time = nTime;
float fTime = float (nTime-keyLeft.time) / (keyRight.time-keyLeft.time);
keyOutput.relpos = keyLeft.relpos + (keyRight.relpos - keyLeft.relpos) * fTime;
keyOutput.relquat = Slerp (keyLeft.relquat, keyRight.relquat, fTime);
keyOutput.relquat.Normalize();
}
// check whether the difference between the two keys is within the specified bounds:
// the Dot product of the quaternions (1==none), and the difference between positions (0=none)
// NOTE: the position delta is defined as a square
bool IsErrorSmall (const CryBoneKey& key1, const CryBoneKey& key2, float fMaxPosDelta2, float fMinQuatDot)
{
return GetLengthSquared((key1.relpos - key2.relpos)) < fMaxPosDelta2
&& fabs(key1.relquat|(key2.relquat)) > fMinQuatDot;
}
// Reduces keyframes that can be interpolated by surrounding keys.
// The error metric is max[1-|QDot|] max[]
// Returns the count of optimized keys, and the reduced keys themselves in the passed in array.
unsigned OptimizeKeys (CryBoneKey* pBoneKeys, unsigned nNumKeys, float fPosError, float fQuatError)
{
// for each starting interval key
if (nNumKeys > 2)
for (unsigned i = 0; i < nNumKeys-2; ++i)
{
CryBoneKey& keyLeft = pBoneKeys[i];
unsigned nBest = i + 1; // best interval end
// for each ending interval key
for (unsigned j = i+2; j < nNumKeys; ++j)
{
CryBoneKey& keyRight = pBoneKeys[j];
nBest = j; // starting with empty interval (no keys to reduce)
// for each key inside the interval
for (unsigned k = i+1; k < j; ++k)
{
CryBoneKey& keyMiddle = pBoneKeys[k];
CryBoneKey keyInterp;
InterpolateCryBoneKey(keyLeft, keyRight, keyMiddle.time, keyInterp);
if (!IsErrorSmall(keyMiddle, keyInterp, fPosError, fQuatError))
{
nBest = j - 1; // this interval is bad, but the previous one was good
j = nNumKeys; // stop iterating
break;
}
}
}
if (nBest > i+1)
{
memmove (&pBoneKeys[i+1], &pBoneKeys[nBest], (nNumKeys-nBest)*sizeof(CryBoneKey));
nNumKeys -= nBest - i - 1;
}
}
while (nNumKeys > 1 && IsErrorSmall(pBoneKeys[nNumKeys-1], pBoneKeys[nNumKeys-2], fPosError, fQuatError))
{
nNumKeys--;
}
while (nNumKeys > 1 && IsErrorSmall(pBoneKeys[0], pBoneKeys[1], fPosError, fQuatError))
{
nNumKeys--;
memmove (pBoneKeys, pBoneKeys+1, nNumKeys*sizeof(CryBoneKey));
}
return nNumKeys;
}