#ifndef INTERPOLATEDVAR_H #define INTERPOLATEDVAR_H #ifdef _WIN32 #pragma once #endif #include "utllinkedlist.h" #include "rangecheckedvar.h" #include "lerp_functions.h" #include "animationlayer.h" #include "convar.h" #include "memdbgon.h" #define COMPARE_HISTORY(a,b) \ ( memcmp( m_VarHistory[a].GetValue(), m_VarHistory[b].GetValue(), sizeof(Type)*GetMaxCount() ) == 0 ) #define LATCH_ANIMATION_VAR (1<<0) #define LATCH_SIMULATION_VAR (1<<1) #define EXCLUDE_AUTO_LATCH (1<<2) #define EXCLUDE_AUTO_INTERPOLATE (1<<3) #define INTERPOLATE_LINEAR_ONLY (1<<4) #define INTERPOLATE_OMIT_UPDATE_LAST_NETWORKED (1<<5) #define EXTRA_INTERPOLATION_HISTORY_STORED 0.05f extern float g_flLastPacketTimestamp; inline void Interpolation_SetLastPacketTimeStamp(float timestamp) { Assert(timestamp > 0); g_flLastPacketTimestamp = timestamp; } class CInterpolationContext { public: CInterpolationContext() { m_bOldAllowExtrapolation = s_bAllowExtrapolation; m_flOldLastTimeStamp = s_flLastTimeStamp; s_bAllowExtrapolation = false; m_pNext = s_pHead; s_pHead = this; } ~CInterpolationContext() { s_bAllowExtrapolation = m_bOldAllowExtrapolation; s_flLastTimeStamp = m_flOldLastTimeStamp; Assert(s_pHead == this); s_pHead = m_pNext; } static void EnableExtrapolation(bool state) { s_bAllowExtrapolation = state; } static bool IsThereAContext() { return s_pHead != NULL; } static bool IsExtrapolationAllowed() { return s_bAllowExtrapolation; } static void SetLastTimeStamp(float timestamp) { s_flLastTimeStamp = timestamp; } static float GetLastTimeStamp() { return s_flLastTimeStamp; } private: CInterpolationContext* m_pNext; bool m_bOldAllowExtrapolation; float m_flOldLastTimeStamp; static CInterpolationContext* s_pHead; static bool s_bAllowExtrapolation; static float s_flLastTimeStamp; }; extern ConVar cl_extrapolate_amount; template< class T > inline T ExtrapolateInterpolatedVarType(const T& oldVal, const T& newVal, float divisor, float flExtrapolationAmount) { return newVal; } inline Vector ExtrapolateInterpolatedVarType(const Vector& oldVal, const Vector& newVal, float divisor, float flExtrapolationAmount) { return Lerp(1.0f + flExtrapolationAmount * divisor, oldVal, newVal); } inline float ExtrapolateInterpolatedVarType(const float& oldVal, const float& newVal, float divisor, float flExtrapolationAmount) { return Lerp(1.0f + flExtrapolationAmount * divisor, oldVal, newVal); } inline QAngle ExtrapolateInterpolatedVarType(const QAngle& oldVal, const QAngle& newVal, float divisor, float flExtrapolationAmount) { return Lerp(1.0f + flExtrapolationAmount * divisor, oldVal, newVal); } abstract_class IInterpolatedVar { public: virtual ~IInterpolatedVar() {} virtual void Setup(void* pValue, int type) = 0; virtual void SetInterpolationAmount(float seconds) = 0; virtual void NoteLastNetworkedValue() = 0; virtual bool NoteChanged(float changetime, bool bUpdateLastNetworkedValue) = 0; virtual void Reset() = 0; virtual int Interpolate(float currentTime) = 0; virtual int GetType() const = 0; virtual void RestoreToLastNetworked() = 0; virtual void Copy(IInterpolatedVar* pSrc) = 0; virtual const char* GetDebugName() = 0; virtual void SetDebugName(const char* pName) = 0; virtual void SetDebug(bool bDebug) = 0; }; template< typename Type, bool IS_ARRAY > struct CInterpolatedVarEntryBase { CInterpolatedVarEntryBase() { value = NULL; count = 0; changetime = 0; } ~CInterpolatedVarEntryBase() { delete[] value; value = NULL; } void FastTransferFrom(CInterpolatedVarEntryBase& src) { Assert(!value); value = src.value; count = src.count; changetime = src.changetime; src.value = 0; src.count = 0; } CInterpolatedVarEntryBase& operator=(const CInterpolatedVarEntryBase& src) { delete[] value; value = NULL; count = 0; if (src.value) { count = src.count; value = new Type[count]; for (int i = 0; i < count; i++) { value[i] = src.value[i]; } } return *this; } Type* GetValue() { return value; } const Type* GetValue() const { return value; } void Init(int maxCount) { if (!maxCount) { DeleteEntry(); } else { if (maxCount != count) { DeleteEntry(); } if (!value) { count = maxCount; value = new Type[maxCount]; } } Assert(count == maxCount); } Type* NewEntry(const Type* pValue, int maxCount, float time) { changetime = time; Init(maxCount); if (value && maxCount) { memcpy(value, pValue, maxCount * sizeof(Type)); } return value; } void DeleteEntry() { delete[] value; value = NULL; count = 0; } float changetime; int count; Type* value; private: CInterpolatedVarEntryBase(const CInterpolatedVarEntryBase& src); }; template struct CInterpolatedVarEntryBase { CInterpolatedVarEntryBase() {} ~CInterpolatedVarEntryBase() {} const Type* GetValue() const { return &value; } Type* GetValue() { return &value; } void Init(int maxCount) { Assert(maxCount == 1); } Type* NewEntry(const Type* pValue, int maxCount, float time) { Assert(maxCount == 1); changetime = time; memcpy(&value, pValue, maxCount * sizeof(Type)); return &value; } void FastTransferFrom(CInterpolatedVarEntryBase& src) { *this = src; } void DeleteEntry() {} float changetime; Type value; }; template class CSimpleRingBuffer { public: CSimpleRingBuffer(int startSize = 4) { m_pElements = 0; m_maxElement = 0; m_firstElement = 0; m_count = 0; m_growSize = 16; EnsureCapacity(startSize); } ~CSimpleRingBuffer() { delete[] m_pElements; m_pElements = NULL; } inline int Count() const { return m_count; } int Head() const { return (m_count > 0) ? 0 : InvalidIndex(); } bool IsIdxValid(int i) const { return (i >= 0 && i < m_count) ? true : false; } bool IsValidIndex(int i) const { return IsIdxValid(i); } static int InvalidIndex() { return -1; } T& operator[](int i) { Assert(IsIdxValid(i)); i += m_firstElement; i = WrapRange(i); return m_pElements[i]; } const T& operator[](int i) const { Assert(IsIdxValid(i)); i += m_firstElement; i = WrapRange(i); return m_pElements[i]; } void EnsureCapacity(int capSize) { if (capSize > m_maxElement) { int newMax = m_maxElement + ((capSize + m_growSize - 1) / m_growSize) * m_growSize; T* pNew = new T[newMax]; for (int i = 0; i < m_maxElement; i++) { pNew[i].FastTransferFrom(m_pElements[WrapRange(i + m_firstElement)]); } m_firstElement = 0; m_maxElement = newMax; delete[] m_pElements; m_pElements = pNew; } } int AddToHead() { EnsureCapacity(m_count + 1); int i = m_firstElement + m_maxElement - 1; m_count++; i = WrapRange(i); m_firstElement = i; return 0; } int AddToHead(const T& elem) { AddToHead(); m_pElements[m_firstElement] = elem; return 0; } int AddToTail() { EnsureCapacity(m_count + 1); m_count++; return WrapRange(m_firstElement + m_count - 1); } void RemoveAll() { m_count = 0; m_firstElement = 0; } void RemoveAtHead() { if (m_count > 0) { m_firstElement = WrapRange(m_firstElement + 1); m_count--; } } void Truncate(int newLength) { if (newLength < m_count) { Assert(newLength >= 0); m_count = newLength; } } private: inline int WrapRange(int i) const { return (i >= m_maxElement) ? (i - m_maxElement) : i; } T* m_pElements; unsigned short m_maxElement; unsigned short m_firstElement; unsigned short m_count; unsigned short m_growSize; }; template< typename Type, bool IS_ARRAY> class CInterpolatedVarArrayBase : public IInterpolatedVar { public: friend class CInterpolatedVarPrivate; CInterpolatedVarArrayBase(const char* pDebugName = "no debug name"); virtual ~CInterpolatedVarArrayBase(); public: virtual void Setup(void* pValue, int type); virtual void SetInterpolationAmount(float seconds); virtual void NoteLastNetworkedValue(); virtual bool NoteChanged(float changetime, bool bUpdateLastNetworkedValue); virtual void Reset(); virtual int Interpolate(float currentTime); virtual int GetType() const; virtual void RestoreToLastNetworked(); virtual void Copy(IInterpolatedVar* pInSrc); virtual const char* GetDebugName() { return m_pDebugName; } public: bool NoteChanged(float changetime, float interpolation_amount, bool bUpdateLastNetworkedValue); int Interpolate(float currentTime, float interpolation_amount); void DebugInterpolate(Type* pOut, float currentTime); void GetDerivative(Type* pOut, float currentTime); void GetDerivative_SmoothVelocity(Type* pOut, float currentTime); void ClearHistory(); void AddToHead(float changeTime, const Type* values, bool bFlushNewer); const Type& GetPrev(int iArrayIndex = 0) const; const Type& GetCurrent(int iArrayIndex = 0) const; float GetInterval() const; bool IsValidIndex(int i); Type* GetHistoryValue(int index, float& changetime, int iArrayIndex = 0); int GetHead() { return 0; } int GetNext(int i) { int next = i + 1; if (!m_VarHistory.IsValidIndex(next)) return m_VarHistory.InvalidIndex(); return next; } void SetHistoryValuesForItem(int item, Type& value); void SetLooping(bool looping, int iArrayIndex = 0); void SetMaxCount(int newmax); int GetMaxCount() const; float GetOldestEntry(); void SetDebugName(const char* pName) { m_pDebugName = pName; } virtual void SetDebug(bool bDebug) { m_bDebug = bDebug; } bool GetInterpolationInfo(float currentTime, int* pNewer, int* pOlder, int* pOldest); protected: typedef CInterpolatedVarEntryBase CInterpolatedVarEntry; typedef CSimpleRingBuffer< CInterpolatedVarEntry > CVarHistory; friend class CInterpolationInfo; class CInterpolationInfo { public: bool m_bHermite; int oldest; int older; int newer; float frac; }; protected: void RemoveOldEntries(float oldesttime); void RemoveEntriesPreviousTo(float flTime); bool GetInterpolationInfo( CInterpolationInfo* pInfo, float currentTime, float interpolation_amount, int* pNoMoreChanges); void TimeFixup_Hermite( CInterpolatedVarEntry& fixup, CInterpolatedVarEntry*& prev, CInterpolatedVarEntry*& start, CInterpolatedVarEntry*& end); void TimeFixup2_Hermite( CInterpolatedVarEntry& fixup, CInterpolatedVarEntry*& prev, CInterpolatedVarEntry*& start, float dt ); void _Extrapolate( Type* pOut, CInterpolatedVarEntry* pOld, CInterpolatedVarEntry* pNew, float flDestinationTime, float flMaxExtrapolationAmount ); void _Interpolate(Type* out, float frac, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end); void _Interpolate_Hermite(Type* out, float frac, CInterpolatedVarEntry* pOriginalPrev, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end, bool looping = false); void _Derivative_Hermite(Type* out, float frac, CInterpolatedVarEntry* pOriginalPrev, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end); void _Derivative_Hermite_SmoothVelocity(Type* out, float frac, CInterpolatedVarEntry* b, CInterpolatedVarEntry* c, CInterpolatedVarEntry* d); void _Derivative_Linear(Type* out, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end); bool ValidOrder(); protected: Type* m_pValue; CVarHistory m_VarHistory; Type* m_LastNetworkedValue; float m_LastNetworkedTime; byte m_fType; byte m_nMaxCount; byte* m_bLooping; float m_InterpolationAmount; const char* m_pDebugName; bool m_bDebug : 1; }; template< typename Type, bool IS_ARRAY > inline CInterpolatedVarArrayBase::CInterpolatedVarArrayBase(const char* pDebugName) { m_pDebugName = pDebugName; m_pValue = NULL; m_fType = LATCH_ANIMATION_VAR; m_InterpolationAmount = 0.0f; m_nMaxCount = 0; m_LastNetworkedTime = 0; m_LastNetworkedValue = NULL; m_bLooping = NULL; m_bDebug = false; } template< typename Type, bool IS_ARRAY > inline CInterpolatedVarArrayBase::~CInterpolatedVarArrayBase() { ClearHistory(); delete[] m_bLooping; delete[] m_LastNetworkedValue; } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::Setup(void* pValue, int type) { m_pValue = (Type*)pValue; m_fType = type; } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::SetInterpolationAmount(float seconds) { m_InterpolationAmount = seconds; } template< typename Type, bool IS_ARRAY > inline int CInterpolatedVarArrayBase::GetType() const { return m_fType; } template< typename Type, bool IS_ARRAY > void CInterpolatedVarArrayBase::NoteLastNetworkedValue() { memcpy(m_LastNetworkedValue, m_pValue, m_nMaxCount * sizeof(Type)); m_LastNetworkedTime = g_flLastPacketTimestamp; } template< typename Type, bool IS_ARRAY > inline bool CInterpolatedVarArrayBase::NoteChanged(float changetime, float interpolation_amount, bool bUpdateLastNetworkedValue) { Assert(m_pValue); bool bRet = true; if (m_VarHistory.Count()) { if (memcmp(m_pValue, m_VarHistory[0].GetValue(), sizeof(Type) * m_nMaxCount) == 0) { bRet = false; } } if (m_bDebug) { char const* pDiffString = bRet ? "differs" : "identical"; } AddToHead(changetime, m_pValue, true); if (bUpdateLastNetworkedValue) { NoteLastNetworkedValue(); } #if 0 RemoveOldEntries(gpGlobals->curtime - interpolation_amount - 2.0f); #else #endif return bRet; } template< typename Type, bool IS_ARRAY > inline bool CInterpolatedVarArrayBase::NoteChanged(float changetime, bool bUpdateLastNetworkedValue) { return NoteChanged(changetime, m_InterpolationAmount, bUpdateLastNetworkedValue); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::RestoreToLastNetworked() { Assert(m_pValue); memcpy(m_pValue, m_LastNetworkedValue, m_nMaxCount * sizeof(Type)); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::ClearHistory() { for (int i = 0; i < m_VarHistory.Count(); i++) { m_VarHistory[i].DeleteEntry(); } m_VarHistory.RemoveAll(); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::AddToHead(float changeTime, const Type* values, bool bFlushNewer) { MEM_ALLOC_CREDIT_CLASS(); int newslot; if (bFlushNewer) { while (m_VarHistory.Count()) { if ((m_VarHistory[0].changetime + 0.0001f) > changeTime) { m_VarHistory.RemoveAtHead(); } else { break; } } newslot = m_VarHistory.AddToHead(); } else { newslot = m_VarHistory.AddToHead(); for (int i = 1; i < m_VarHistory.Count(); i++) { if (m_VarHistory[i].changetime <= changeTime) break; m_VarHistory[newslot].FastTransferFrom(m_VarHistory[i]); newslot = i; } } CInterpolatedVarEntry* e = &m_VarHistory[newslot]; e->NewEntry(values, m_nMaxCount, changeTime); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::Reset() { ClearHistory(); if (m_pValue) { memcpy(m_LastNetworkedValue, m_pValue, m_nMaxCount * sizeof(Type)); } } template< typename Type, bool IS_ARRAY > inline float CInterpolatedVarArrayBase::GetOldestEntry() { float lastVal = 0; if (m_VarHistory.Count()) { lastVal = m_VarHistory[m_VarHistory.Count() - 1].changetime; } return lastVal; } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::RemoveOldEntries(float oldesttime) { int newCount = m_VarHistory.Count(); for (int i = m_VarHistory.Count(); --i > 2; ) { if (m_VarHistory[i].changetime > oldesttime) break; newCount = i; } m_VarHistory.Truncate(newCount); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::RemoveEntriesPreviousTo(float flTime) { for (int i = 0; i < m_VarHistory.Count(); i++) { if (m_VarHistory[i].changetime < flTime) { m_VarHistory.Truncate(i + 3); break; } } } template< typename Type, bool IS_ARRAY > inline bool CInterpolatedVarArrayBase::GetInterpolationInfo( typename CInterpolatedVarArrayBase::CInterpolationInfo* pInfo, float currentTime, float interpolation_amount, int* pNoMoreChanges ) { Assert(m_pValue); CVarHistory& varHistory = m_VarHistory; float targettime = currentTime - interpolation_amount; pInfo->m_bHermite = false; pInfo->frac = 0; pInfo->oldest = pInfo->older = pInfo->newer = varHistory.InvalidIndex(); for (int i = 0; i < varHistory.Count(); i++) { pInfo->older = i; float older_change_time = m_VarHistory[i].changetime; if (older_change_time == 0.0f) break; if (targettime < older_change_time) { pInfo->newer = pInfo->older; continue; } if (pInfo->newer == varHistory.InvalidIndex()) { pInfo->newer = pInfo->older; if (pNoMoreChanges) *pNoMoreChanges = 1; return true; } float newer_change_time = varHistory[pInfo->newer].changetime; float dt = newer_change_time - older_change_time; if (dt > 0.0001f) { pInfo->frac = (targettime - older_change_time) / (newer_change_time - older_change_time); pInfo->frac = MIN(pInfo->frac, 2.0f); int oldestindex = i + 1; if (!(m_fType & INTERPOLATE_LINEAR_ONLY) && varHistory.IsIdxValid(oldestindex)) { pInfo->oldest = oldestindex; float oldest_change_time = varHistory[oldestindex].changetime; float dt2 = older_change_time - oldest_change_time; if (dt2 > 0.0001f) { pInfo->m_bHermite = true; } } if (pNoMoreChanges && pInfo->newer == m_VarHistory.Head()) { if (COMPARE_HISTORY(pInfo->newer, pInfo->older)) { if (!pInfo->m_bHermite || COMPARE_HISTORY(pInfo->newer, pInfo->oldest)) *pNoMoreChanges = 1; } } } return true; } if (pInfo->newer != varHistory.InvalidIndex()) { pInfo->older = pInfo->newer; return true; } pInfo->newer = pInfo->older; return (pInfo->older != varHistory.InvalidIndex()); } template< typename Type, bool IS_ARRAY > inline bool CInterpolatedVarArrayBase::GetInterpolationInfo(float currentTime, int* pNewer, int* pOlder, int* pOldest) { CInterpolationInfo info; bool result = GetInterpolationInfo(&info, currentTime, m_InterpolationAmount, NULL); if (pNewer) *pNewer = (int)info.newer; if (pOlder) *pOlder = (int)info.older; if (pOldest) *pOldest = (int)info.oldest; return result; } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::DebugInterpolate(Type* pOut, float currentTime) { float interpolation_amount = m_InterpolationAmount; int noMoreChanges = 0; CInterpolationInfo info; GetInterpolationInfo(&info, currentTime, interpolation_amount, &noMoreChanges); CVarHistory& history = m_VarHistory; if (info.m_bHermite) { _Interpolate_Hermite(pOut, info.frac, &history[info.oldest], &history[info.older], &history[info.newer]); } else if (info.newer == info.older) { int realOlder = info.newer + 1; if (CInterpolationContext::IsExtrapolationAllowed() && IsValidIndex(realOlder) && history[realOlder].changetime != 0.0 && interpolation_amount > 0.000001f && CInterpolationContext::GetLastTimeStamp() <= m_LastNetworkedTime) { _Extrapolate(pOut, &history[realOlder], &history[info.newer], currentTime - interpolation_amount, cl_extrapolate_amount.GetFloat()); } else { _Interpolate(pOut, info.frac, &history[info.older], &history[info.newer]); } } else { _Interpolate(pOut, info.frac, &history[info.older], &history[info.newer]); } } template< typename Type, bool IS_ARRAY > inline int CInterpolatedVarArrayBase::Interpolate(float currentTime, float interpolation_amount) { int noMoreChanges = 0; CInterpolationInfo info; if (!GetInterpolationInfo(&info, currentTime, interpolation_amount, &noMoreChanges)) return noMoreChanges; CVarHistory& history = m_VarHistory; if (m_bDebug) { Msg("%s Interpolate at %f%s\n", GetDebugName(), currentTime, noMoreChanges ? " [value will hold]" : ""); } #ifdef INTERPOLATEDVAR_PARANOID_MEASUREMENT Type* backupValues = (Type*)_alloca(m_nMaxCount * sizeof(Type)); memcpy(backupValues, m_pValue, sizeof(Type) * m_nMaxCount); #endif if (info.m_bHermite) { _Interpolate_Hermite(m_pValue, info.frac, &history[info.oldest], &history[info.older], &history[info.newer]); } else if (info.newer == info.older) { int realOlder = info.newer + 1; if (CInterpolationContext::IsExtrapolationAllowed() && IsValidIndex(realOlder) && history[realOlder].changetime != 0.0 && interpolation_amount > 0.000001f && CInterpolationContext::GetLastTimeStamp() <= m_LastNetworkedTime) { _Extrapolate(m_pValue, &history[realOlder], &history[info.newer], currentTime - interpolation_amount, cl_extrapolate_amount.GetFloat()); } else { _Interpolate(m_pValue, info.frac, &history[info.older], &history[info.newer]); } } else { _Interpolate(m_pValue, info.frac, &history[info.older], &history[info.newer]); } #ifdef INTERPOLATEDVAR_PARANOID_MEASUREMENT if (memcmp(backupValues, m_pValue, sizeof(Type) * m_nMaxCount) != 0) { extern int g_nInterpolatedVarsChanged; extern bool g_bRestoreInterpolatedVarValues; ++g_nInterpolatedVarsChanged; if (g_bRestoreInterpolatedVarValues) { memcpy(m_pValue, backupValues, sizeof(Type) * m_nMaxCount); return noMoreChanges; } } #endif RemoveEntriesPreviousTo(currentTime - interpolation_amount - EXTRA_INTERPOLATION_HISTORY_STORED); return noMoreChanges; } template< typename Type, bool IS_ARRAY > void CInterpolatedVarArrayBase::GetDerivative(Type* pOut, float currentTime) { CInterpolationInfo info; if (!GetInterpolationInfo(&info, currentTime, m_InterpolationAmount, NULL)) return; if (info.m_bHermite) { _Derivative_Hermite(pOut, info.frac, &m_VarHistory[info.oldest], &m_VarHistory[info.older], &m_VarHistory[info.newer]); } else { _Derivative_Linear(pOut, &m_VarHistory[info.older], &m_VarHistory[info.newer]); } } template< typename Type, bool IS_ARRAY > void CInterpolatedVarArrayBase::GetDerivative_SmoothVelocity(Type* pOut, float currentTime) { CInterpolationInfo info; if (!GetInterpolationInfo(&info, currentTime, m_InterpolationAmount, NULL)) return; CVarHistory& history = m_VarHistory; bool bExtrapolate = false; int realOlder = 0; if (info.m_bHermite) { _Derivative_Hermite_SmoothVelocity(pOut, info.frac, &history[info.oldest], &history[info.older], &history[info.newer]); return; } else if (info.newer == info.older && CInterpolationContext::IsExtrapolationAllowed()) { realOlder = info.newer + 1; if (IsValidIndex(realOlder) && history[realOlder].changetime != 0.0) { if (m_InterpolationAmount > 0.000001f && CInterpolationContext::GetLastTimeStamp() <= (currentTime - m_InterpolationAmount)) { bExtrapolate = true; } } } if (bExtrapolate) { _Derivative_Linear(pOut, &history[realOlder], &history[info.newer]); float flDestTime = currentTime - m_InterpolationAmount; float diff = flDestTime - history[info.newer].changetime; diff = clamp(diff, 0.f, cl_extrapolate_amount.GetFloat() * 2); if (diff > cl_extrapolate_amount.GetFloat()) { float scale = 1 - (diff - cl_extrapolate_amount.GetFloat()) / cl_extrapolate_amount.GetFloat(); for (int i = 0; i < m_nMaxCount; i++) { pOut[i] *= scale; } } } else { _Derivative_Linear(pOut, &history[info.older], &history[info.newer]); } } template< typename Type, bool IS_ARRAY > inline int CInterpolatedVarArrayBase::Interpolate(float currentTime) { return Interpolate(currentTime, m_InterpolationAmount); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::Copy(IInterpolatedVar* pInSrc) { CInterpolatedVarArrayBase* pSrc = dynamic_cast*>(pInSrc); if (!pSrc || pSrc->m_nMaxCount != m_nMaxCount) { if (pSrc) { AssertMsg3(false, "pSrc->m_nMaxCount (%i) != m_nMaxCount (%i) for %s.", pSrc->m_nMaxCount, m_nMaxCount, m_pDebugName); } else { AssertMsg(false, "pSrc was null in CInterpolatedVarArrayBase::Copy."); } return; } Assert((m_fType & ~EXCLUDE_AUTO_INTERPOLATE) == (pSrc->m_fType & ~EXCLUDE_AUTO_INTERPOLATE)); Assert(m_pDebugName == pSrc->GetDebugName()); for (int i = 0; i < m_nMaxCount; i++) { m_LastNetworkedValue[i] = pSrc->m_LastNetworkedValue[i]; m_bLooping[i] = pSrc->m_bLooping[i]; } m_LastNetworkedTime = pSrc->m_LastNetworkedTime; m_VarHistory.RemoveAll(); for (int i = 0; i < pSrc->m_VarHistory.Count(); i++) { int newslot = m_VarHistory.AddToTail(); CInterpolatedVarEntry* dest = &m_VarHistory[newslot]; CInterpolatedVarEntry* src = &pSrc->m_VarHistory[i]; dest->NewEntry(src->GetValue(), m_nMaxCount, src->changetime); } } template< typename Type, bool IS_ARRAY > inline const Type& CInterpolatedVarArrayBase::GetPrev(int iArrayIndex) const { Assert(m_pValue); Assert(iArrayIndex >= 0 && iArrayIndex < m_nMaxCount); if (m_VarHistory.Count() > 1) { return m_VarHistory[1].GetValue()[iArrayIndex]; } return m_pValue[iArrayIndex]; } template< typename Type, bool IS_ARRAY > inline const Type& CInterpolatedVarArrayBase::GetCurrent(int iArrayIndex) const { Assert(m_pValue); Assert(iArrayIndex >= 0 && iArrayIndex < m_nMaxCount); if (m_VarHistory.Count() > 0) { return m_VarHistory[0].GetValue()[iArrayIndex]; } return m_pValue[iArrayIndex]; } template< typename Type, bool IS_ARRAY > inline float CInterpolatedVarArrayBase::GetInterval() const { if (m_VarHistory.Count() > 1) { return m_VarHistory[0].changetime - m_VarHistory[1].changetime; } return 0.0f; } template< typename Type, bool IS_ARRAY > inline bool CInterpolatedVarArrayBase::IsValidIndex(int i) { return m_VarHistory.IsValidIndex(i); } template< typename Type, bool IS_ARRAY > inline Type* CInterpolatedVarArrayBase::GetHistoryValue(int index, float& changetime, int iArrayIndex) { Assert(iArrayIndex >= 0 && iArrayIndex < m_nMaxCount); if (m_VarHistory.IsIdxValid(index)) { CInterpolatedVarEntry* entry = &m_VarHistory[index]; changetime = entry->changetime; return &entry->GetValue()[iArrayIndex]; } else { changetime = 0.0f; return NULL; } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::SetHistoryValuesForItem(int item, Type& value) { Assert(item >= 0 && item < m_nMaxCount); for (int i = 0; i < m_VarHistory.Count(); i++) { CInterpolatedVarEntry* entry = &m_VarHistory[i]; entry->GetValue()[item] = value; } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::SetLooping(bool looping, int iArrayIndex) { Assert(iArrayIndex >= 0 && iArrayIndex < m_nMaxCount); m_bLooping[iArrayIndex] = looping; } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::SetMaxCount(int newmax) { bool changed = (newmax != m_nMaxCount) ? true : false; newmax = MAX(1, newmax); m_nMaxCount = newmax; if (changed) { delete[] m_bLooping; delete[] m_LastNetworkedValue; m_bLooping = new byte[m_nMaxCount]; m_LastNetworkedValue = new Type[m_nMaxCount]; memset(m_bLooping, 0, sizeof(byte) * m_nMaxCount); memset(m_LastNetworkedValue, 0, sizeof(Type) * m_nMaxCount); Reset(); } } template< typename Type, bool IS_ARRAY > inline int CInterpolatedVarArrayBase::GetMaxCount() const { return m_nMaxCount; } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::_Interpolate(Type* out, float frac, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end) { Assert(start); Assert(end); if (start == end) { for (int i = 0; i < m_nMaxCount; i++) { out[i] = end->GetValue()[i]; Lerp_Clamp(out[i]); } return; } Assert(frac >= 0.0f && frac <= 1.0f); for (int i = 0; i < m_nMaxCount; i++) { if (m_bLooping[i]) { out[i] = LoopingLerp(frac, start->GetValue()[i], end->GetValue()[i]); } else { out[i] = Lerp(frac, start->GetValue()[i], end->GetValue()[i]); } Lerp_Clamp(out[i]); } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::_Extrapolate( Type* pOut, CInterpolatedVarEntry* pOld, CInterpolatedVarEntry* pNew, float flDestinationTime, float flMaxExtrapolationAmount ) { if (fabs(pOld->changetime - pNew->changetime) < 0.001f || flDestinationTime <= pNew->changetime) { for (int i = 0; i < m_nMaxCount; i++) pOut[i] = pNew->GetValue()[i]; } else { float flExtrapolationAmount = MIN(flDestinationTime - pNew->changetime, flMaxExtrapolationAmount); float divisor = 1.0f / (pNew->changetime - pOld->changetime); for (int i = 0; i < m_nMaxCount; i++) { pOut[i] = ExtrapolateInterpolatedVarType(pOld->GetValue()[i], pNew->GetValue()[i], divisor, flExtrapolationAmount); } } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::TimeFixup2_Hermite( typename CInterpolatedVarArrayBase::CInterpolatedVarEntry& fixup, typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& prev, typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& start, float dt1 ) { float dt2 = start->changetime - prev->changetime; if (fabs(dt1 - dt2) > 0.0001f && dt2 > 0.0001f) { float frac = dt1 / dt2; fixup.changetime = start->changetime - dt1; for (int i = 0; i < m_nMaxCount; i++) { if (m_bLooping[i]) { fixup.GetValue()[i] = LoopingLerp(1 - frac, prev->GetValue()[i], start->GetValue()[i]); } else { fixup.GetValue()[i] = Lerp(1 - frac, prev->GetValue()[i], start->GetValue()[i]); } } prev = &fixup; } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::TimeFixup_Hermite( typename CInterpolatedVarArrayBase::CInterpolatedVarEntry& fixup, typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& prev, typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& start, typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& end) { TimeFixup2_Hermite(fixup, prev, start, end->changetime - start->changetime); } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::_Interpolate_Hermite( Type* out, float frac, CInterpolatedVarEntry* prev, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end, bool looping) { Assert(start); Assert(end); CDisableRangeChecks disableRangeChecks; CInterpolatedVarEntry fixup; fixup.Init(m_nMaxCount); TimeFixup_Hermite(fixup, prev, start, end); for (int i = 0; i < m_nMaxCount; i++) { if (m_bLooping[i]) { out[i] = LoopingLerp_Hermite(frac, prev->GetValue()[i], start->GetValue()[i], end->GetValue()[i]); } else { out[i] = Lerp_Hermite(frac, prev->GetValue()[i], start->GetValue()[i], end->GetValue()[i]); } Lerp_Clamp(out[i]); } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::_Derivative_Hermite( Type* out, float frac, CInterpolatedVarEntry* prev, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end) { Assert(start); Assert(end); CDisableRangeChecks disableRangeChecks; CInterpolatedVarEntry fixup; fixup.value = (Type*)_alloca(sizeof(Type) * m_nMaxCount); TimeFixup_Hermite(fixup, prev, start, end); float divisor = 1.0f / (end->changetime - start->changetime); for (int i = 0; i < m_nMaxCount; i++) { Assert(!m_bLooping[i]); out[i] = Derivative_Hermite(frac, prev->GetValue()[i], start->GetValue()[i], end->GetValue()[i]); out[i] *= divisor; } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::_Derivative_Hermite_SmoothVelocity( Type* out, float frac, CInterpolatedVarEntry* b, CInterpolatedVarEntry* c, CInterpolatedVarEntry* d) { CInterpolatedVarEntry fixup; fixup.Init(m_nMaxCount); TimeFixup_Hermite(fixup, b, c, d); for (int i = 0; i < m_nMaxCount; i++) { Type prevVel = (c->GetValue()[i] - b->GetValue()[i]) / (c->changetime - b->changetime); Type curVel = (d->GetValue()[i] - c->GetValue()[i]) / (d->changetime - c->changetime); out[i] = Lerp(frac, prevVel, curVel); } } template< typename Type, bool IS_ARRAY > inline void CInterpolatedVarArrayBase::_Derivative_Linear( Type* out, CInterpolatedVarEntry* start, CInterpolatedVarEntry* end) { if (start == end || fabs(start->changetime - end->changetime) < 0.0001f) { for (int i = 0; i < m_nMaxCount; i++) { out[i] = start->GetValue()[i] * 0; } } else { float divisor = 1.0f / (end->changetime - start->changetime); for (int i = 0; i < m_nMaxCount; i++) { out[i] = (end->GetValue()[i] - start->GetValue()[i]) * divisor; } } } template< typename Type, bool IS_ARRAY > inline bool CInterpolatedVarArrayBase::ValidOrder() { float newestchangetime = 0.0f; bool first = true; for (int i = 0; i < m_VarHistory.Count(); i++) { CInterpolatedVarEntry* entry = &m_VarHistory[i]; if (first) { first = false; newestchangetime = entry->changetime; continue; } if (entry->changetime > newestchangetime) { Assert(0); return false; } newestchangetime = entry->changetime; } return true; } template< typename Type, int COUNT > class CInterpolatedVarArray : public CInterpolatedVarArrayBase { public: CInterpolatedVarArray(const char* pDebugName = "no debug name") : CInterpolatedVarArrayBase(pDebugName) { this->SetMaxCount(COUNT); } }; template< typename Type > class CInterpolatedVar : public CInterpolatedVarArrayBase< Type, false > { public: CInterpolatedVar(const char* pDebugName = NULL) : CInterpolatedVarArrayBase< Type, false >(pDebugName) { this->SetMaxCount(1); } }; #include "memdbgoff.h" #endif