646 lines
22 KiB
C++
646 lines
22 KiB
C++
// method definitions extracted from CryModel.*
|
|
|
|
#include "stdafx.h"
|
|
#include <StlUtils.h>
|
|
#include <CryCompiledFile.h>
|
|
#include "ControllerManager.h"
|
|
#include "Controller.h"
|
|
#include "ControllerCryBone.h"
|
|
#include "ControllerPackedBSpline.h"
|
|
#include "FileMapping.h"
|
|
#include "ChunkFileReader.h"
|
|
#include "CVars.h"
|
|
#include "CryModelAnimationContainer.h"
|
|
|
|
#if defined(LINUX)
|
|
# include "CryAnimationInfo.h"
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
CControllerManager::CControllerManager():
|
|
#ifdef DEBUG_STD_CONTAINERS
|
|
m_arrAnims ("CControllerManager.Anims"),
|
|
m_arrAnimByFile ("CControllerManager.AnimByFile"),
|
|
#endif
|
|
m_nCachedSizeofThis (0),
|
|
m_nLastCheckedUnloadCandidate(0)
|
|
{
|
|
// we reserve the place for future animations. The best performance will be
|
|
// archived when this number is >= actual number of animations that can be used,
|
|
// and not much greater
|
|
m_arrAnims.reserve (0x100);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// finds the animation by name. Returns -1 if no animation was found
|
|
// Returns the animation ID if it was found
|
|
int CControllerManager::FindAnimationByFile (const string& sAnimFileName)
|
|
{
|
|
std::vector<int>::iterator it = std::lower_bound(m_arrAnimByFile.begin(), m_arrAnimByFile.end(), sAnimFileName.c_str(), AnimationIdPred(m_arrAnims));
|
|
if (it != m_arrAnimByFile.end() && !stricmp(m_arrAnims[*it].strFileName.c_str(),sAnimFileName.c_str()))
|
|
return *it;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
CControllerManager::~CControllerManager()
|
|
{
|
|
#ifdef _DEBUG
|
|
for (AnimationArray::iterator it = m_arrAnims.begin(); it!= m_arrAnims.end(); ++it)
|
|
assert (it->nRefCount == 0);
|
|
#endif
|
|
for(int nAttempt = 0; nAttempt < 40 && !m_setPendingAnimLoads.empty(); ++nAttempt)
|
|
{
|
|
g_GetLog()->LogToFile("\003%d asynch loading are pending, waiting...", m_setPendingAnimLoads.size());
|
|
g_GetStreamEngine()->Wait(200,IStreamEngine::FLAGS_DISABLE_CALLBACK_TIME_QUOTA);
|
|
}
|
|
if (!m_setPendingAnimLoads.empty())
|
|
g_GetLog()->LogWarning ("\002%d asynchronous operations are left; destructing the controller manager anyway, because they seem to be dangling.If a crash happens after this, perhaps this is because some async operation took _very_ long to complete.", m_setPendingAnimLoads.size());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// loads the animation with the specified name; if the animation is already loaded,
|
|
// then just returns its id
|
|
// The caller MUST TAKE CARE to bind the animation if it's already loaded before it has registered itself within this manager
|
|
// RETURNS:
|
|
// The global animation id.
|
|
// -1 if the animation couldn't be loaded
|
|
// SIDE EFFECT NOTES:
|
|
// This function does not put up a warning in the case the file was not found.
|
|
// The caller must do so. But if the file was found and was corrupted, more detailed
|
|
// error information (like where the error occured etc.) may be put into the log
|
|
int CControllerManager::StartLoadAnimation (const string& strFileName, float fScale, unsigned nFlags)
|
|
{
|
|
int nAnimId = FindAnimationByFile (strFileName);
|
|
bool bRecordExists = (nAnimId >= 0);
|
|
if (bRecordExists && m_arrAnims[nAnimId].IsLoaded())
|
|
{
|
|
// we have already such file loaded
|
|
return nAnimId;
|
|
}
|
|
else
|
|
{
|
|
selfValidate();
|
|
if (!bRecordExists)
|
|
{
|
|
// add a new animation structure that will hold the info about the new animation.
|
|
// the new animation id is the index of this structure in the array
|
|
nAnimId = (int)m_arrAnims.size();
|
|
m_arrAnims.resize (nAnimId + 1);
|
|
}
|
|
|
|
Animation& Anim = m_arrAnims[nAnimId];
|
|
Anim.nFlags = nFlags;
|
|
|
|
if (!bRecordExists)
|
|
Anim.strFileName = strFileName;
|
|
|
|
Anim.fScale = fScale;
|
|
|
|
if (!bRecordExists)// no need to insert if we reload the file
|
|
{
|
|
m_arrAnimByFile.insert (std::lower_bound(m_arrAnimByFile.begin(), m_arrAnimByFile.end(), nAnimId, AnimationIdPred(m_arrAnims)), nAnimId);
|
|
}
|
|
selfValidate();
|
|
}
|
|
|
|
return nAnimId;
|
|
}
|
|
|
|
// loads the animation info, if not loaded yet
|
|
bool CControllerManager::LoadAnimationInfo(int nGlobalAnimId)
|
|
{
|
|
if ((unsigned)nGlobalAnimId < m_arrAnims.size())
|
|
{
|
|
if (m_arrAnims[nGlobalAnimId].IsInfoLoaded())
|
|
return true;
|
|
return LoadAnimation(nGlobalAnimId);
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Loads the animation controllers into the existing animation record.
|
|
// May be used to load the animation the first time, or reload it upon request.
|
|
// Does nothing if there are already controls in the existing animation record.
|
|
bool CControllerManager::LoadAnimation(int nAnimId)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
|
|
Animation& Anim = m_arrAnims[nAnimId];
|
|
if (Anim.IsLoaded())
|
|
return true;
|
|
|
|
if (Anim.nFlags & Animation::FLAGS_LOAD_PENDING)
|
|
{
|
|
// we already have this animation being loaded
|
|
return true;
|
|
}
|
|
|
|
unsigned nFileSize = g_GetStreamEngine()->GetFileSize(Anim.strFileName.c_str());
|
|
|
|
if (!nFileSize) // no such file
|
|
{
|
|
if (!(Anim.nFlags & GlobalAnimation::FLAGS_DISABLE_LOAD_ERROR_LOG))
|
|
g_GetLog()->LogError ("\003CControllerManager::LoadAnimation: file loading %s file not found", Anim.strFileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (Anim.IsInfoLoaded())
|
|
{ // we can start loading asynchronously
|
|
// this structure will keep the info until the completion routine is called
|
|
PendingAnimLoad_AutoPtr pAnimLoad = new PendingAnimLoad;
|
|
pAnimLoad->nAnimId = nAnimId;
|
|
pAnimLoad->pFile = new CFileMapping;
|
|
pAnimLoad->pFile->attach (malloc (nFileSize), nFileSize);
|
|
pAnimLoad->nFrameId = g_nFrameID;
|
|
|
|
// these parameters tell that the file must be read directly into the file mapping object
|
|
// the file mapping object will be passed to the chunk reader that will parse the data
|
|
StreamReadParams params;
|
|
params.dwUserData = (DWORD_PTR)(PendingAnimLoad*)pAnimLoad;
|
|
params.nSize = nFileSize;
|
|
params.pBuffer = pAnimLoad->pFile->getData();
|
|
|
|
// now we're prepared to read, register the pending operation and start reading
|
|
m_setPendingAnimLoads.insert (pAnimLoad);
|
|
// mark the animation as being loaded
|
|
Anim.nFlags |= Animation::FLAGS_LOAD_PENDING;
|
|
// we're not interested in the pointer to the read stream - we only need to know when it's finished
|
|
pAnimLoad->pStream = g_GetStreamEngine()->StartRead("Animation", Anim.strFileName.c_str(), this, ¶ms);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// if the info hasn't still been loaded, we need to load synchronously
|
|
// try to read the file
|
|
CChunkFileReader Reader;
|
|
if (!Reader.open (Anim.strFileName))
|
|
{
|
|
if (!(Anim.nFlags & GlobalAnimation::FLAGS_DISABLE_LOAD_ERROR_LOG))
|
|
g_GetLog()->LogError ("\003CControllerManager::LoadAnimation: file loading %s, last error is: %s", Anim.strFileName.c_str(), Reader.getLastError());
|
|
return false;
|
|
}
|
|
return LoadAnimation(nAnimId, &Reader);
|
|
}
|
|
}
|
|
|
|
void CControllerManager::StreamOnComplete (IReadStream* pStream, unsigned nError)
|
|
{
|
|
PendingAnimLoad_AutoPtr pAnimLoad = (PendingAnimLoad*)pStream->GetUserData();
|
|
assert (m_setPendingAnimLoads.find (pAnimLoad) != m_setPendingAnimLoads.end());
|
|
Animation& Anim = m_arrAnims[pAnimLoad->nAnimId];
|
|
m_setPendingAnimLoads.erase(pAnimLoad);
|
|
// flag the animation as being loaded
|
|
Anim.nFlags &= ~Animation::FLAGS_LOAD_PENDING;
|
|
if (nError)
|
|
{
|
|
#if defined(LINUX)
|
|
g_GetLog()->LogError ("\003Asynchronous load of file %s has failed, error code 0x%X", Anim.strFileName.c_str(), nError);
|
|
#else
|
|
g_GetLog()->LogError ("\003Asynchronous load of file %s has failed (%d of %d bytes read), error code 0x%X", Anim.strFileName.c_str(), pStream->GetBytesRead(), pAnimLoad->pFile->getSize(), nError);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
CChunkFileReader Reader;
|
|
if (!Reader.open (pAnimLoad->pFile))
|
|
#if defined(LINUX)
|
|
g_GetLog()->LogError ("\003Error: Asynchronous load of file %s has completed successfully, but the data can't be parsed. The last \"%s\".", Anim.strFileName.c_str(), Reader.getLastError());
|
|
#else
|
|
g_GetLog()->LogError ("\003Error: Asynchronous load of file %s has completed successfully (%d of expected %d bytes read), but the data can't be parsed. The last \"%s\".", Anim.strFileName.c_str(), pStream->GetBytesRead(), pAnimLoad->pFile->getSize(), Reader.getLastError());
|
|
#endif
|
|
else
|
|
{
|
|
if (LoadAnimation (pAnimLoad->nAnimId, &Reader))
|
|
{
|
|
++g_nAsyncAnimCounter;
|
|
g_nAsyncAnimFrameDelays += g_nFrameID - pAnimLoad->nFrameId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// immediately load the given animation from the already opened reader
|
|
bool CControllerManager::LoadAnimation (int nAnimId, CChunkFileReader* pReader)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
|
|
Animation& Anim = m_arrAnims[nAnimId];
|
|
|
|
// check the file header for validity
|
|
const FILE_HEADER& fh = pReader->getFileHeader();
|
|
|
|
if(fh.Version != AnimFileVersion || fh.FileType != FileType_Anim)
|
|
{
|
|
g_GetLog()->LogError ("\003CControllerManager::LoadAnimation: file version error or not an animation file: %s", Anim.strFileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
// prepare the array of controllers and the counter for them
|
|
Anim.arrCtrls.resize(pReader->numChunksOfType (ChunkType_Controller));
|
|
unsigned nController = 0;
|
|
|
|
m_nCachedSizeofThis = 0;
|
|
// scan the chunks and load all controllers and time data into the animation structure
|
|
for (int nChunk = 0; nChunk < pReader->numChunks (); ++nChunk)
|
|
{
|
|
// this is the chunk header in the chunk table at the end of the file
|
|
const CHUNK_HEADER& chunkHeader = pReader->getChunkHeader(nChunk);
|
|
// this is the chunk raw data, starts with the chunk header/descriptor structure
|
|
const void* pChunk = pReader->getChunkData (nChunk);
|
|
unsigned nChunkSize = pReader->getChunkSize(nChunk);
|
|
|
|
switch (chunkHeader.ChunkType)
|
|
{
|
|
case ChunkType_Controller:
|
|
switch (chunkHeader.ChunkVersion)
|
|
{
|
|
case CONTROLLER_CHUNK_DESC_0826::VERSION:
|
|
{
|
|
// load and add a controller constructed from the controller chunk
|
|
const CONTROLLER_CHUNK_DESC_0826* pCtrlChunk = static_cast<const CONTROLLER_CHUNK_DESC_0826*>(pChunk);
|
|
IController_AutoPtr pController = LoadController (Anim.fScale, pCtrlChunk, nChunkSize);
|
|
if (!pController)
|
|
{
|
|
g_GetLog()->LogError ("\002CControllerManager::LoadAnimation: error loading v826 controller: %s", Anim.strFileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
assert (nController < Anim.arrCtrls.size());
|
|
Anim.arrCtrls[nController++] = pController;
|
|
}
|
|
break;
|
|
|
|
case CONTROLLER_CHUNK_DESC_0827::VERSION:
|
|
{
|
|
const CONTROLLER_CHUNK_DESC_0827* pCtrlChunk = static_cast<const CONTROLLER_CHUNK_DESC_0827*>(pChunk);
|
|
CControllerCryBone_AutoPtr pController = new CControllerCryBone ();
|
|
if (pController->Load (pCtrlChunk, nChunkSize, Anim.fScale))
|
|
{
|
|
assert (nController < Anim.arrCtrls.size());
|
|
Anim.arrCtrls[nController++] = static_cast<IController*>(pController);
|
|
}
|
|
else
|
|
{
|
|
g_GetLog()->LogError ("\002CControllerManager::LoadAnimation: error loading v827 controller: %s", Anim.strFileName.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
g_GetLog()->LogError ("\003Unsupported controller chunk 0x%08X version 0x%08X in file %s. Please re-export the file.", chunkHeader.ChunkID, chunkHeader.ChunkVersion, Anim.strFileName.c_str());
|
|
return false;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ChunkType_Timing:
|
|
{
|
|
// memorize the timing info
|
|
const TIMING_CHUNK_DESC* pTimingChunk = static_cast<const TIMING_CHUNK_DESC*> (pChunk);
|
|
Anim.nTicksPerFrame = pTimingChunk->TicksPerFrame;
|
|
Anim.fSecsPerTick = pTimingChunk->SecsPerTick;
|
|
Anim.rangeGlobal = pTimingChunk->global_range;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nController != Anim.arrCtrls.size())
|
|
{
|
|
g_GetLog()->LogError ("\003%d controllers (%d expected) loaded from file %s. Please re-export the file. The animations will be discarded.",
|
|
nController, Anim.arrCtrls.size(), Anim.strFileName.c_str());
|
|
}
|
|
|
|
std::sort(
|
|
Anim.arrCtrls.begin(),
|
|
Anim.arrCtrls.end(),
|
|
AnimCtrlSortPred()
|
|
);
|
|
|
|
Anim.OnInfoLoaded();
|
|
FireAnimationGlobalLoad(nAnimId);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// updates the animation from the chunk of AnimInfo
|
|
bool CControllerManager::UpdateAnimation (int nGlobalAnimId, const CCFAnimInfo* pAnimInfo)
|
|
{
|
|
if ((unsigned)nGlobalAnimId >= m_arrAnims.size())
|
|
return false;
|
|
|
|
GlobalAnimation& Anim = m_arrAnims[nGlobalAnimId];
|
|
|
|
#if !defined(LINUX)
|
|
/* if (Anim.IsInfoLoaded())
|
|
{
|
|
assert (Anim.fSecsPerTick == pAnimInfo->fSecsPerTick);
|
|
assert (Anim.nTicksPerFrame == pAnimInfo->nTicksPerFrame);
|
|
assert (Anim.rangeGlobal.start == pAnimInfo->nRangeStart);
|
|
assert (Anim.rangeGlobal.end == pAnimInfo->nRangeEnd);
|
|
return true;
|
|
}*/
|
|
#endif
|
|
|
|
Anim.fSecsPerTick = pAnimInfo->fSecsPerTick;
|
|
Anim.nTicksPerFrame = pAnimInfo->nTicksPerFrame;
|
|
Anim.rangeGlobal.start = pAnimInfo->nRangeStart;
|
|
Anim.rangeGlobal.end = pAnimInfo->nRangeEnd;
|
|
Anim.nFlags |= pAnimInfo->nAnimFlags;
|
|
Anim.OnInfoLoaded();
|
|
|
|
FireAnimationGlobalLoad(nGlobalAnimId);
|
|
return true;
|
|
}
|
|
|
|
// notify everybody that the given animation OR its info has been just loaded
|
|
void CControllerManager::FireAnimationGlobalLoad (int nAnimId)
|
|
{
|
|
for (std::vector<CryModelAnimationContainer*>::iterator it = m_arrClients.begin(); it != m_arrClients.end(); ++it)
|
|
(*it)->OnAnimationGlobalLoad(nAnimId);
|
|
}
|
|
|
|
// notifies the controller manager that another client uses the given animation.
|
|
// these calls must be balanced with AnimationRelease() calls
|
|
void CControllerManager::AnimationAddRef (int nGlobalAnimId, CryModelAnimationContainer* pClient)
|
|
{
|
|
assert ((unsigned)nGlobalAnimId< m_arrAnims.size());
|
|
// the new client, though it might have received "OnLoad" events from this global animation,
|
|
// might have missed it because it wasn't interested in them; now we re-send this event if needed
|
|
m_arrAnims[nGlobalAnimId].AddRef();
|
|
if (m_arrAnims[nGlobalAnimId].IsLoaded())
|
|
pClient->OnAnimationGlobalLoad(nGlobalAnimId);
|
|
}
|
|
|
|
// notifies the controller manager that this client doesn't use the given animation any more.
|
|
// these calls must be balanced with AnimationAddRef() calls
|
|
void CControllerManager::AnimationRelease (int nGlobalAnimId, CryModelAnimationContainer* pClient)
|
|
{
|
|
assert ((unsigned)nGlobalAnimId< m_arrAnims.size());
|
|
// if the given client is still interested in unload events, it will receive them all anyway,
|
|
// so we don't force anything but pure release. Normally during this call the client doesn't need
|
|
// any information about the animation being released
|
|
m_arrAnims[nGlobalAnimId].Release();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// returns the total number of animations hold in memory (for statistics)
|
|
unsigned CControllerManager::NumAnimations()
|
|
{
|
|
return (unsigned)m_arrAnims.size();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// Loads the controller from the chunk, returns the result in the autopointer
|
|
// It is important that the return type is autoptr: it lets the procedure gracefully destruct
|
|
// half-constructed animation in case of an error.
|
|
IController_AutoPtr CControllerManager::LoadController (float fScale, const CONTROLLER_CHUNK_DESC_0826* pChunk, int nSize)
|
|
{
|
|
// different controllers are constructed depending on the representation of the raw data:
|
|
// spline or original crybone
|
|
IController_AutoPtr pController;
|
|
switch (pChunk->type)
|
|
{
|
|
case CTRL_BSPLINE_2O:
|
|
case CTRL_BSPLINE_1O:
|
|
case CTRL_BSPLINE_2C:
|
|
case CTRL_BSPLINE_1C:
|
|
{
|
|
// one of the fixed-point spline formats
|
|
CControllerPackedBSpline_AutoPtr pCtrl = new CControllerPackedBSpline();
|
|
if (pCtrl->Load(pChunk, nSize, fScale))
|
|
pController = static_cast<IController*>(static_cast<CControllerPackedBSpline*>(pCtrl));
|
|
}
|
|
break;
|
|
|
|
case CTRL_CRYBONE:
|
|
{
|
|
// the old bone format
|
|
CControllerCryBone_AutoPtr pCtrl = new CControllerCryBone ();
|
|
if (pCtrl->Load(pChunk, fScale))
|
|
pController = static_cast<IController*>(static_cast<CControllerCryBone*>(pCtrl));
|
|
}
|
|
break;
|
|
}
|
|
|
|
return pController;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// finds controller with the given nControllerID among controller in the animation
|
|
// identified by nGlobalAnimID
|
|
IController* CControllerManager::GetController (int nGlobalAnimID, unsigned nControllerID)
|
|
{
|
|
Animation& Anim = m_arrAnims[nGlobalAnimID];
|
|
return Anim.GetController(nControllerID);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// logs controller usage statistics. Used for debugging only
|
|
void CControllerManager::LogUsageStats()
|
|
{
|
|
size_t nCtrlUnused = 0;
|
|
size_t nCtrlTotal = 0;
|
|
size_t nCtrlUsage = 0;
|
|
AnimationArray::const_iterator it = m_arrAnims.begin(), itEnd = it + m_arrAnims.size();
|
|
for (; it != itEnd; ++it)
|
|
{
|
|
const Animation& Anim = *it;
|
|
nCtrlTotal += Anim.arrCtrls.size();
|
|
Animation::ControllerArray::const_iterator itCtrl = Anim.arrCtrls.begin(), itCtrlEnd = itCtrl + Anim.arrCtrls.size();
|
|
for (; itCtrl != itCtrlEnd; ++itCtrl)
|
|
{
|
|
IController* pCtrl = *itCtrl;
|
|
if (pCtrl->NumRefs() == 1)
|
|
++nCtrlUnused;
|
|
else
|
|
nCtrlUsage += pCtrl->NumRefs()-1;
|
|
}
|
|
}
|
|
|
|
g_GetLog()->LogToFile ("%u controllers, %u (%.1f percent) unused, %.2f average refs per used controller",
|
|
nCtrlTotal, nCtrlUnused, (nCtrlUnused*100.0f/nCtrlTotal),
|
|
float(nCtrlUsage)/(nCtrlTotal-nCtrlUnused));
|
|
}
|
|
|
|
|
|
// returns the structure describing the animation data, given the global anim id
|
|
CControllerManager::Animation& CControllerManager::GetAnimation (int nAnimID)
|
|
{
|
|
if ((unsigned)nAnimID < m_arrAnims.size())
|
|
return m_arrAnims[nAnimID];
|
|
else
|
|
{
|
|
assert (0);
|
|
static Animation dummy;
|
|
return dummy;
|
|
}
|
|
}
|
|
|
|
void CControllerManager::DumpAnims()
|
|
{
|
|
// approximately calculating the size of the map
|
|
std::vector<int>::const_iterator it;
|
|
const std::vector<int>::const_iterator itBegin = m_arrAnimByFile.begin(), itEnd = m_arrAnimByFile.end();
|
|
unsigned nMaxNameLength = 0;
|
|
for (it=itBegin; it != itEnd; ++it)
|
|
{
|
|
unsigned nNameLength = (unsigned)m_arrAnims[*it].strFileName.length();
|
|
if (nNameLength > nMaxNameLength)
|
|
nMaxNameLength = nNameLength;
|
|
}
|
|
|
|
nMaxNameLength += 2;
|
|
|
|
g_GetLog()->LogToFile ("\001%*s kbytes started ticks", nMaxNameLength, "Animation Memory Usage Dump");
|
|
g_GetLog()->LogToConsole ("\001%*s kbytes started ticks", nMaxNameLength, "Animation Memory Usage Dump");
|
|
|
|
// the size of the array of controllers
|
|
size_t nSize = 0;
|
|
|
|
typedef std::multimap<size_t, int> SizeMap;
|
|
SizeMap mapSizes;
|
|
for (int nAnim = 0; nAnim < (int)m_arrAnims.size(); ++nAnim)
|
|
{
|
|
size_t nSizeAnimation = m_arrAnims[nAnim].sizeofThis();
|
|
nSize += nSizeAnimation;
|
|
mapSizes.insert (SizeMap::value_type(nSizeAnimation, nAnim));
|
|
}
|
|
|
|
for (SizeMap::reverse_iterator itSize = mapSizes.rbegin(); itSize != mapSizes.rend(); ++itSize)
|
|
{
|
|
int nAnim = itSize->second;
|
|
Animation& anim = m_arrAnims[nAnim];
|
|
|
|
g_GetLog()->LogToFile ("\001%*s : %6.1f%10u%10u%s%s %d refs", nMaxNameLength, anim.strFileName.c_str(), itSize->first/1024.0f, anim.nTickCount, anim.nStartCount, anim.IsLoaded()?"":anim.IsInfoLoaded()?",Unloaded":",Not Loaded",anim.nRefCount?"":",Not Used:", anim.nRefCount);
|
|
g_GetLog()->LogToConsole ("\001%*s : %6.1f%10u%10u%s%s %d refs", nMaxNameLength, anim.strFileName.c_str(), itSize->first/1024.0f, anim.nTickCount, anim.nStartCount, anim.IsLoaded()?"":anim.IsInfoLoaded()?",Unloaded":",Not Loaded",anim.nRefCount?"":",Not Used:", anim.nRefCount);
|
|
}
|
|
|
|
g_GetLog()->LogToFile ("\001%*s : %6.1f", nMaxNameLength, "TOTAL", nSize / 1024.0f);
|
|
g_GetLog()->LogToConsole ("\001%*s : %6.1f", nMaxNameLength, "TOTAL", nSize / 1024.0f);
|
|
}
|
|
|
|
// unreferences the controllers and makes the animation unloaded
|
|
// before this operation, all bones must be unbound
|
|
// returns true if the animation was really unloaded (if it had been unloaded before, returns false)
|
|
bool CControllerManager::UnloadAnimation (int nGlobAnimId)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
|
|
if ((unsigned)nGlobAnimId>= m_arrAnims.size())
|
|
return false;
|
|
|
|
if (m_arrAnims[nGlobAnimId].arrCtrls.empty())
|
|
return false;
|
|
else
|
|
{
|
|
m_nCachedSizeofThis = 0;
|
|
for (std::vector<CryModelAnimationContainer*>::iterator it = m_arrClients.begin(); it!= m_arrClients.end(); ++it)
|
|
(*it)->OnAnimationGlobalUnload (nGlobAnimId);
|
|
#if !defined(LINUX)
|
|
assert (m_arrAnims[nGlobAnimId].MaxControllerRefCount()==1);
|
|
#endif
|
|
m_arrAnims[nGlobAnimId].arrCtrls.clear();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
void CControllerManager::OnStartAnimation (int nGlobalAnimId)
|
|
{
|
|
assert ((unsigned)nGlobalAnimId < m_arrAnims.size());
|
|
LoadAnimation(nGlobalAnimId);
|
|
m_arrAnims[nGlobalAnimId].OnStart();
|
|
}
|
|
|
|
void CControllerManager::OnTickAnimation(int nGlobalAnimId)
|
|
{
|
|
assert ((unsigned)nGlobalAnimId < m_arrAnims.size());
|
|
LoadAnimation(nGlobalAnimId);
|
|
m_arrAnims[nGlobalAnimId].OnTick();
|
|
}
|
|
|
|
void CControllerManager::OnApplyAnimation(int nGlobalAnimId)
|
|
{
|
|
assert ((unsigned)nGlobalAnimId < m_arrAnims.size());
|
|
LoadAnimation(nGlobalAnimId);
|
|
m_arrAnims[nGlobalAnimId].OnApply();
|
|
}
|
|
|
|
// puts the size of the whole subsystem into this sizer object, classified,
|
|
// according to the flags set in the sizer
|
|
void CControllerManager::GetSize(class ICrySizer* pSizer)
|
|
{
|
|
#if ENABLE_GET_MEMORY_USAGE
|
|
SIZER_SUBCOMPONENT_NAME(pSizer, "Keys");
|
|
|
|
size_t nSize = sizeof(*this);
|
|
if (m_nCachedSizeofThis)
|
|
nSize = m_nCachedSizeofThis;
|
|
else
|
|
{
|
|
// approximately calculating the size of the map
|
|
unsigned nMaxNameLength = 0;
|
|
nSize += sizeofArray (m_arrAnimByFile);
|
|
for (AnimationArray::const_iterator it = m_arrAnims.begin(); it != m_arrAnims.end(); ++it)
|
|
nSize += it->sizeofThis();
|
|
|
|
m_nCachedSizeofThis = nSize;
|
|
}
|
|
pSizer->AddObject(this, nSize);
|
|
|
|
#endif
|
|
}
|
|
|
|
size_t CControllerManager::Animation::sizeofThis ()const
|
|
{
|
|
size_t nSize = sizeof(*this) + sizeofArray (arrCtrls)+ strFileName.capacity() + 1;
|
|
ControllerArray::const_iterator it = arrCtrls.begin(), itEnd = it + arrCtrls.size();
|
|
for (; it != itEnd; ++it)
|
|
nSize += (*it)->sizeofThis();
|
|
return nSize;
|
|
}
|
|
|
|
void CControllerManager::selfValidate()
|
|
{
|
|
#ifdef _DEBUG
|
|
assert (m_arrAnimByFile.size()==m_arrAnims.size());
|
|
for (int i = 0; i < (int)m_arrAnimByFile.size()-1; ++i)
|
|
assert (stricmp(m_arrAnims[m_arrAnimByFile[i]].strFileName.c_str(), m_arrAnims[m_arrAnimByFile[i+1]].strFileName.c_str()) < 0);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CControllerManager::Update()
|
|
{
|
|
if (g_GetCVars()->ca_AnimationUnloadDelay() < 30)
|
|
return;
|
|
|
|
if (m_nLastCheckedUnloadCandidate < m_arrAnims.size())
|
|
{
|
|
Animation &GlobalAnim = m_arrAnims[m_nLastCheckedUnloadCandidate];
|
|
if (
|
|
GlobalAnim.IsAutoUnload() &&
|
|
GlobalAnim.nLastAccessFrameId + g_GetCVars()->ca_AnimationUnloadDelay() < g_nFrameID
|
|
)
|
|
{
|
|
if (UnloadAnimation(m_nLastCheckedUnloadCandidate))
|
|
if (g_GetCVars()->ca_Debug())
|
|
g_GetLog()->LogToFile ("\004Unloaded animation %s", GlobalAnim.strFileName.c_str());
|
|
}
|
|
++m_nLastCheckedUnloadCandidate;
|
|
}
|
|
else
|
|
m_nLastCheckedUnloadCandidate = 0;
|
|
} |