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

229 lines
6.3 KiB
C++

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// History:
// 22 Sep 2002 :- Created by Sergiy Migdalskiy <sergiy@crytek.de>
//
// Contains:
// class - loading context - responsible for loading the linearized bone hierarchy from the BONEANIM
// chunk
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "MathUtils.h"
#include "CryBoneHierarchyLoader.h"
#include "ChunkFileReader.h"
#include "CgfUtils.h"
CryBoneHierarchyLoader::CryBoneHierarchyLoader ():
#ifdef DEBUG_STD_CONTAINERS
m_arrBones("CryBoneHierarchyLoader.Bones"),
m_arrIdToIndex ("CryBoneHierarchyLoader.indexidmaps"),
m_arrIndexToId ("CryBoneHierarchyLoader.indexidmaps"),
#endif
m_pChunkBoneAnim(NULL),
m_pChunkBoneAnimSize(0),
m_pBoneAnimRawData (NULL),
m_pBoneAnimRawDataEnd (NULL),
m_szLastError ("")
{
}
unsigned CryBoneHierarchyLoader::load (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize)
{
m_arrBones.clear();
m_arrIndexToId.clear();
m_arrIdToIndex.clear();
m_pChunkBoneAnim = pChunk;
m_pChunkBoneAnimSize = nChunkSize;
m_pBoneAnimRawData = pChunk+1;
m_pBoneAnimRawDataEnd = ((const char*) pChunk) + nChunkSize;
if (nChunkSize < sizeof(*pChunk))
{
m_szLastError = "Couldn't read the data." ;
m_pBoneAnimRawData = pChunk;
return 0;
}
if (pChunk->nBones <= 0)
{
m_szLastError = "There must be at least one bone.";
m_pBoneAnimRawData = pChunk;
return 0;
}
const BONE_ENTITY* pBones = (const BONE_ENTITY*) (pChunk+1);
if (m_pBoneAnimRawDataEnd < (const byte*)(pBones + pChunk->nBones))
{
m_szLastError = "Premature end of data.";
return 0;
}
// check for one-and-only-one root rule
if (pBones[0].ParentID != -1)
{
m_szLastError = "The first bone in the hierarchy has a parent, but it is expected to be the root bone.";
return 0;
}
for (int i = 1; i < pChunk->nBones; ++i)
{
if (pBones[i].ParentID == -1)
{
m_szLastError = "The skeleton has multiple roots. Only single-rooted skeletons are supported in this version.";
return 0;
}
}
m_arrBones.resize (numBones());
m_arrIndexToId.resize (numBones(), -1);
m_arrIdToIndex.resize (numBones(), -1);
m_nNextBone = 0;
int nRootBoneIndex = (int)allocateBones (1);
if (nRootBoneIndex < 0 || !load(nRootBoneIndex, nRootBoneIndex))
m_pBoneAnimRawData = pChunk;
updateInvDefGlobalMatrices();
return (const byte*)m_pBoneAnimRawData - (const byte*)m_pChunkBoneAnim;
}
// updates the bone InvDefGlobal matrices, if the default pose matrices
// are available
void CryBoneHierarchyLoader::updateInvDefGlobalMatrices()
{
if (m_arrInitPose.empty() || m_arrBones.empty())
return;
if (m_arrInitPose.size() != m_arrBones.size())
{
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogError ("\003there are %d bones and %d initial pose matrices. ignoring matrices.", m_arrBones.size(), m_arrInitPose.size());
#endif
return;
}
for (unsigned nBone = 0; nBone < m_arrBones.size(); ++nBone)
{
unsigned nBoneID = mapIndexToId(nBone);
m_arrBones[nBone].setDefaultGlobal (m_arrInitPose[nBoneID]);
}
}
// loads the default positions of each bone; if the bone chunk is loaded,
// updates the bone inverse default pose matrices
// returns number of bytes read if successful, 0 if not
unsigned CryBoneHierarchyLoader::load (const BONEINITIALPOS_CHUNK_DESC_0001*pChunk, unsigned nChunkSize)
{
if (nChunkSize < sizeof(*pChunk) || pChunk->numBones < 0 || pChunk->numBones > 0x10000)
return 0;
const char* pChunkEnd = ((const char*)pChunk) + nChunkSize;
const SBoneInitPosMatrix* pDefMatrix = (const SBoneInitPosMatrix*)(pChunk+1);
// the end of utilized data in the chunk
const char* pUtilizedEnd = (const char*)(pDefMatrix + pChunk->numBones);
if (pUtilizedEnd > pChunkEnd)
return 0;
m_arrInitPose.resize (pChunk->numBones);
for (unsigned nBone = 0; nBone < pChunk->numBones; ++nBone)
{
Matrix44& matBone = m_arrInitPose[nBone];
copyMatrix (matBone,pDefMatrix[nBone]);
// for some reason Max supplies unnormalized matrices.
matBone.NoScale();
}
updateInvDefGlobalMatrices();
return pUtilizedEnd - (const char*)pChunk;
}
void CryBoneHierarchyLoader::scale (float fScale)
{
unsigned i;
for (i = 0; i < m_arrInitPose.size(); ++i)
m_arrInitPose[i].ScaleTranslationOLD (fScale);
updateInvDefGlobalMatrices();
}
// allocates the required number of bones in the plain hierarchy array, starting at the next available place
// -1 if couldn't allocate
int CryBoneHierarchyLoader::allocateBones(int numBones)
{
if (m_nNextBone + numBones > (int)this->numBones())
return -1; // the request is for too many bones
int nResult = m_nNextBone;
m_nNextBone += numBones;
assert (m_nNextBone <= (int)this->numBones());
return nResult;
}
// loads the whole hierarchy of bones, using the state machine
// when this funciton is called, the bone is already allocated
bool CryBoneHierarchyLoader::load (int nBoneParentIndex, int nBoneIndex)
{
const BONE_ENTITY* pEntity;
if (!EatRawDataPtr(pEntity, 1, m_pBoneAnimRawData, m_pBoneAnimRawDataEnd))
return false;
// initialize the next bone
CryBoneDesc& rBoneDesc = m_arrBones[nBoneIndex];
if (!rBoneDesc.LoadRaw (pEntity))
return false;
// set the mapping entries
m_arrIndexToId[nBoneIndex] = pEntity->BoneID;
m_arrIdToIndex[pEntity->BoneID] = nBoneIndex;
rBoneDesc.m_nOffsetParent = nBoneParentIndex - nBoneIndex;
// load children
if (pEntity->nChildren)
{
int nChildrenIndexBase = allocateBones(pEntity->nChildren);
if (nChildrenIndexBase < 0)
return false;
// load the children
rBoneDesc.m_numChildren = pEntity->nChildren;
rBoneDesc.m_nOffsetChildren = nChildrenIndexBase - nBoneIndex;
for (int nChild = 0; nChild < pEntity->nChildren; ++nChild)
{
if (!load(nBoneIndex, nChildrenIndexBase + nChild))
return false;
}
}
else
{
rBoneDesc.m_numChildren = 0;
rBoneDesc.m_nOffsetChildren = 0;
}
return true;
}
// compares the two bone structures. Returns true if they're equal
// (e.g. for validation of different lods)
bool CryBoneHierarchyLoader::isEqual (const CryBoneHierarchyLoader& right)const
{
if (m_arrBones.size() != right.m_arrBones.size())
return false;
for (unsigned nBone = 0; nBone < m_arrBones.size(); ++nBone)
{
if (!m_arrBones[nBone].isEqual(right.m_arrBones[nBone]))
return false;
}
return true;
}