80 lines
2.7 KiB
C++
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;
|
|
}
|