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

543 lines
13 KiB
C++

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// History:
// Created by Sergiy Migdalskiy
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "StringUtils.h"
#include "CgfUtils.h"
using namespace CryStringUtils;
//////////////////////////////////////////////////////////////////////////
// given the chunk, parses it and returns the array of pointers to the strings with the bone names
// in the supplied array. Returns true when successful
// PARAMETERS:
// chunkHeader - the header of the chunk in the chunk table
// pChunk - the raw chunk data in the file
// nChunkSize - the size of the raw data piece in the file
// arrNames - the array that will be resized according to the number of entries, and each element of this array
// will be the pointer to the corresponding 0-terminated name string.
bool LoadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize, std::vector<const char*>& arrNames)
{
switch (chunkHeader.ChunkVersion)
{
case BONENAMELIST_CHUNK_DESC_0744::VERSION:
{
// the old chunk version - fixed-size name list
const BONENAMELIST_CHUNK_DESC_0744* pNameChunk = (const BONENAMELIST_CHUNK_DESC_0744*)(pChunk);
int nGeomBones = pNameChunk->nEntities;
// just fill in the pointers to the fixed-size string buffers
const NAME_ENTITY* pGeomBoneNameTable = (const NAME_ENTITY*)(pNameChunk+1);
if(nGeomBones < 0 || nGeomBones > 0x800)
return false;
arrNames.resize(nGeomBones);
for (int i = 0; i < nGeomBones; ++i)
arrNames[i] = pGeomBoneNameTable[i].name;
}
break;
case BONENAMELIST_CHUNK_DESC_0745::VERSION:
{
// the new memory-economizing chunk with variable-length packed strings following tightly each other
const BONENAMELIST_CHUNK_DESC_0745* pNameChunk = (const BONENAMELIST_CHUNK_DESC_0745*)(pChunk);
int nGeomBones = pNameChunk->numEntities;
// we know how many strings there are there; the whole bunch of strings may
// be followed by pad zeros, to enable alignment
arrNames.resize(nGeomBones, "");
// scan through all the strings, each string following immediately the 0 terminator of the previous one
const char* pNameListEnd = ((const char*)pNameChunk) + nChunkSize;
const char* pName = (const char*)(pNameChunk+1);
int nName = 0;
while (*pName && pName < pNameListEnd && nName < nGeomBones)
{
arrNames[nName] = pName;
pName += strnlen(pName, pNameListEnd) + 1;
++nName;
}
if (nName < nGeomBones)
{
// the chunk is truncated
#ifdef _CRY_ANIMATION_BASE_HEADER_
// if this is in the engine, log the error
g_GetLog()->LogWarning ("\003inconsistent bone name list chunk: only %d out of %d bone names have been read.", nName, nGeomBones);
#endif
return false;
}
}
break;
}
return true;
}
// Attempts to load the material from the given material chunk to the "me" structure
MatChunkLoadErrorEnum LoadMatEntity (const CHUNK_HEADER& chunkHeader, const void* pChunkData, unsigned nChunkSize, MAT_ENTITY& me)
{
memset(&me, 0, sizeof(MAT_ENTITY));
switch (chunkHeader.ChunkVersion)
{
case MTL_CHUNK_DESC_0746::VERSION:
{
const MTL_CHUNK_DESC_0746* pMatChunk = (const MTL_CHUNK_DESC_0746*)pChunkData;
me.m_New = 2;
strcpy(me.name, pMatChunk->name);
switch (pMatChunk->MtlType)
{
case MTL_STANDARD:
{
me.IsStdMat = true;
me.col_d = pMatChunk->col_d;
me.col_a = pMatChunk->col_a;
//me.col_a.g=0;
//me.col_a.b=0;
me.col_s = pMatChunk->col_s;
me.specLevel = pMatChunk->specLevel;
me.specShininess = pMatChunk->specShininess*100;
me.opacity = pMatChunk->opacity;
me.selfIllum = pMatChunk->selfIllum;
me.flags = pMatChunk->flags;
me.Dyn_Bounce = pMatChunk->Dyn_Bounce;
me.Dyn_StaticFriction = pMatChunk->Dyn_StaticFriction;
me.Dyn_SlidingFriction = pMatChunk->Dyn_SlidingFriction;
/* //Timur[10/24/2001]
strcpy(me.map_a, pMatChunk->tex_a.name);
strcpy(me.map_d, pMatChunk->tex_d.name);
strcpy(me.map_o, pMatChunk->tex_o.name);
strcpy(me.map_b, pMatChunk->tex_b.name);
strcpy(me.map_s, pMatChunk->tex_s.name);
strcpy(me.map_g, pMatChunk->tex_g.name);
strcpy(me.map_c, pMatChunk->tex_c.name);
strcpy(me.map_e, pMatChunk->tex_rl.name);
strcpy(me.map_rr, pMatChunk->tex_rr.name);
strcpy(me.map_det, pMatChunk->tex_det.name);
*/
me.map_a = pMatChunk->tex_a;
me.map_d = pMatChunk->tex_d;
me.map_o = pMatChunk->tex_o;
me.map_b = pMatChunk->tex_b;
me.map_s = pMatChunk->tex_s;
me.map_g = pMatChunk->tex_g;
me.map_detail = pMatChunk->tex_fl;
me.map_e = pMatChunk->tex_rl;
me.map_subsurf = pMatChunk->tex_subsurf;
me.map_displ = pMatChunk->tex_det;
me.nChildren = pMatChunk->nChildren;
me.alpharef = pMatChunk->alphaTest;
}
return MCLE_Success;
default:
return MCLE_IgnoredType;
}
}
break;
case MTL_CHUNK_DESC_0745::VERSION:
{
const MTL_CHUNK_DESC_0745* pMatChunk = (const MTL_CHUNK_DESC_0745*)pChunkData;
me.m_New = 1;
strcpy(me.name, pMatChunk->name);
switch (pMatChunk->MtlType)
{
case MTL_STANDARD:
me.IsStdMat = true;
me.col_d = pMatChunk->col_d;
me.col_a = pMatChunk->col_a;
//me.col_a.g=0;
//me.col_a.b=0;
me.col_s = pMatChunk->col_s;
me.specLevel = pMatChunk->specLevel;
me.specShininess = pMatChunk->specShininess*100;
me.opacity = pMatChunk->opacity;
me.selfIllum = pMatChunk->selfIllum;
me.flags = pMatChunk->flags;
me.Dyn_Bounce = pMatChunk->Dyn_Bounce;
me.Dyn_StaticFriction = pMatChunk->Dyn_StaticFriction;
me.Dyn_SlidingFriction = pMatChunk->Dyn_SlidingFriction;
me.map_a = pMatChunk->tex_a;
me.map_d = pMatChunk->tex_d;
me.map_o = pMatChunk->tex_o;
me.map_b = pMatChunk->tex_b;
me.map_s = pMatChunk->tex_s;
me.map_g = pMatChunk->tex_g;
me.map_detail = pMatChunk->tex_c;
me.map_e = pMatChunk->tex_rl;
me.map_subsurf = pMatChunk->tex_subsurf;
me.map_displ = pMatChunk->tex_det;
me.nChildren = pMatChunk->nChildren;
return MCLE_Success;
default:
return MCLE_IgnoredType;
}
}
break;
default:
return MCLE_UnknownVersion;
}
}
// if the given string is null, assigns the "" to the pointer
void chkNullString (const char*&pszString)
{
if (!pszString)
pszString = "";
}
static void rtrim (char* szString)
{
for (char* p = szString + strlen (szString) - 1; p >= szString && isspace(*p); --p)
*p = '\0';
}
static void ltrim (const char*& szString)
{
while (*szString && isspace(*szString))
++szString;
}
CMatEntityNameTokenizer::CMatEntityNameTokenizer ():
m_szMtlName (NULL),
szName (""),
szTemplate (""),
szPhysMtl (""),
nSortValue (0),
bInvert (false)
{
}
void CMatEntityNameTokenizer::tokenize (const char* szMtlFullName)
{
if (m_szMtlName)
{
free (m_szMtlName);
m_szMtlName = NULL;
}
if (!szMtlFullName)
return;
int nLen = (int)strlen(szMtlFullName);
m_szMtlName = (char*)malloc (nLen+1);
memcpy (m_szMtlName, szMtlFullName, nLen + 1);
szName = NULL;
szTemplate = NULL;
szPhysMtl = NULL;
nSortValue = 0;
bInvert = false;
// the state machine will parse the whole string
enum StateEnum
{
kUnknown,
kName,
kTemplate,
kPhysMtl,
kIndex
};
StateEnum nState = kName; // by default, the string begins with name
this->szName = m_szMtlName;
for (char* p = m_szMtlName; *p; ++p)
{
switch (*p)
{
case '(': // template name begins
this->szTemplate = p+1;
nState = kTemplate;
*p = '\0';
break;
case '[': // render priority number begins
*p = '\0';
nState = kIndex;
break;
case '/':
this->szPhysMtl = p+1;
*p = '\0';
nState = kPhysMtl;
break;
default:
switch (nState)
{
case kName:
switch (*p)
{
/*case ' ': // there are no spaces in the name
*p = '\0';
break;*/
case ')':
case ']':
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogError ("Invalid material name (unexpected closing bracket) \"%s\"", szMtlFullName);
#endif
break;
};
break;
case kTemplate:
switch (*p)
{
case ')':
nState = kUnknown;
*p = '\0';
break;
}
break;
case kIndex:
{
switch (*p)
{
case ']':
nState = kUnknown;
break;
case ' ':
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
this->nSortValue *= 10;
this->nSortValue += *p - '0';
break;
default:
nState = kUnknown;
#ifdef _CRY_ANIMATION_BASE_HEADER_
g_GetLog()->LogError ("Invalid material name (unexpected symbol in index field) \"%s\"", szMtlFullName);
#endif
break;
}
}
break;
};
break;
}
}
// take into account hte old form $s_... of setting the template name
// if there was no () template, then use this one (after _)
if ((!this->szTemplate || !this->szTemplate[0]) &&
this->szName[0] == '$' && tolower(this->szName[1]) == 's' && this->szName[2] == '_')
{
this->szTemplate = this->szName + 3;
}
// make sure all the strings get their pointers - if there's no name, then it will be an empty name
chkNullString(this->szName);
chkNullString(this->szTemplate);
chkNullString(this->szPhysMtl);
// a special case (one more) - template name preceded by # means (or meant) the inverted template
if (this->szTemplate[0] == '#')
{
this->szTemplate++;
this->bInvert = true;
}
// trim unneeded left and right leading spaces
rtrim ((char*)this->szName);
rtrim ((char*)this->szTemplate);
rtrim ((char*)this->szPhysMtl);
ltrim (this->szName);
ltrim (this->szTemplate);
ltrim (this->szPhysMtl);
}
CMatEntityNameTokenizer::~CMatEntityNameTokenizer ()
{
if (m_szMtlName)
free (m_szMtlName);
}
// operator that sorts the materials for rendering
bool CMatEntityNameTokenizer::operator < (const CMatEntityNameTokenizer& right)const
{
if (this->nSortValue < right.nSortValue)
return true;
if (this->nSortValue > right.nSortValue)
return false;
int nComp = stricmp (this->szTemplate, right.szTemplate);
if (nComp < 0)
return true;
if (nComp > 0)
return false;
nComp = stricmp (this->szName, right.szName);
if (nComp < 0)
return true;
if (nComp > 0)
return false;
return false; // they're equal
}
// given the in-permutation, constructs the inverse out-permutation, so that:
// pOut[pIn[i]] == i
// pIn[pOut[i]] == i
void ConstructReversePermutation (const unsigned* pIn, unsigned* pOut, unsigned num)
{
unsigned i;
#ifdef _DEBUG
// we'll check the correctness of permutation by checking if there are duplicate entries in pIn
// if there are no duplicate entries, according to Dirichle principle, there are no missed entries in pOut
for (i = 0; i < num; ++i)
pOut[i] = (unsigned)-1;
#endif
for (i = 0; i < num; ++i)
{
assert (pIn[i] < num);
assert ((int)pOut[pIn[i]] ==(unsigned)-1);
pOut[pIn[i]] = i;
}
}
// Remaps the materials according to the given permutation
// the permutation is perm[new index] == old index
void RemapMatEntities (MAT_ENTITY* pMtls, unsigned numMtls, unsigned* pPerm)
{
MAT_ENTITY* pOldMtls = new MAT_ENTITY[numMtls];
memcpy (pOldMtls, pMtls, sizeof(MAT_ENTITY)*numMtls);
for (unsigned nNewMtl = 0; nNewMtl < numMtls; ++nNewMtl)
memcpy (pMtls + nNewMtl, pOldMtls + pPerm[nNewMtl], sizeof(MAT_ENTITY));
delete[]pOldMtls;
}
// copies the matrix from SBoneInitPosMatrix to Matrix
void copyMatrix (Matrix44& matDst, const SBoneInitPosMatrix& matSrc)
{
for (unsigned i = 0; i < 4; ++i)
{
matDst(i,0) = matSrc[i][0];
matDst(i,1) = matSrc[i][1];
matDst(i,2) = matSrc[i][2];
}
matDst(0,3) = 0;
matDst(1,3) = 0;
matDst(2,3) = 0;
matDst(3,3) = 1;
}
const char* getMtlType (unsigned nMtlType)
{
switch (nMtlType)
{
case MTL_UNKNOWN:
return "UNKNOWN";
case MTL_STANDARD:
return "STANDARD";
case MTL_MULTI:
return "MULTI";
case MTL_2SIDED:
return "2SIDED";
default:
return "#Unknown#";
}
}
const char* getTexType (unsigned char nTexType)
{
switch (nTexType)
{
case TEXMAP_AUTOCUBIC:
return "TEXMAP_AUTOCUBIC";
case TEXMAP_CUBIC:
return "TEXMAP_CUBIC";
case TEXMAP_ENVIRONMENT:
return "TEXMAP_ENVIRONMENT";
case TEXMAP_SCREENENVIRONMENT:
return "!TEXMAP_SCREENENVIRONMENT(unsupported)!";
default:
return "#Unknown#";
}
}
string getLightType (LightTypes nType)
{
switch (nType)
{
case LT_OMNI:
return "Omni";
case LT_SPOT:
return "Spot";
case LT_DIRECT:
return "Direct";
case LT_AMBIENT:
return "Ambient";
default:
{
char szBuffer[32];
printf (szBuffer, "Unknown(%d)", nType);
return szBuffer;
}
}
}
string getMtlFlags (int nFlags)
{
string strResult;
if (nFlags & MTLFLAG_WIRE)
strResult += "MTLFLAG_WIRE|";
if (nFlags & MTLFLAG_2SIDED)
strResult += "MTLFLAG_2SIDED|";
if (nFlags & MTLFLAG_FACEMAP)
strResult += "MTLFLAG_FACEMAP|";
if (nFlags & MTLFLAG_FACETED)
strResult += "MTLFLAG_FACETED|";
if (nFlags & MTLFLAG_ADDITIVE)
strResult += "MTLFLAG_ADDITIVE|";
if (nFlags & MTLFLAG_SUBTRACTIVE)
strResult += "MTLFLAG_SUBTRACTIVE|";
if (!strResult.empty ())
strResult.resize (strResult.length ()-1);
return strResult;
}