Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5013 lines
164 KiB

//===== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======//
//
// Purpose: Loads mesh data from dmx files
//
// $NoKeywords: $
//
//===========================================================================//
// Valve includes
#include "bspflags.h"
#include "movieobjects/dmeattributereference.h"
#include "movieobjects/dmeconnectionoperator.h"
#include "movieobjects/dmemodel.h"
#include "movieobjects/dmemesh.h"
#include "movieobjects/dmefaceset.h"
#include "movieobjects/dmematerial.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmeattachment.h"
#include "movieobjects/dmeanimationlist.h"
#include "movieobjects/dmecombinationoperator.h"
#include "movieobjects/dmerigconstraintoperators.h"
#include "mdlobjects/dmebbox.h"
#include "mdlobjects/dmelod.h"
#include "mdlobjects/dmelodlist.h"
#include "mdlobjects/dmebodygroup.h"
#include "mdlobjects/dmebodygrouplist.h"
#include "mdlobjects/dmehitbox.h"
#include "mdlobjects/dmehitboxset.h"
#include "mdlobjects/dmehitboxsetlist.h"
#include "mdlobjects/dmesequence.h"
#include "mdlobjects/dmesequencelist.h"
#include "mdlobjects/dmecollisionmodel.h"
#include "mdlobjects/dmecollisionjoints.h"
#include "mdlobjects/dmeincludemodellist.h"
#include "mdlobjects/dmedefinebone.h"
#include "mdlobjects/dmedefinebonelist.h"
#include "mdlobjects/dmematerialgroup.h"
#include "mdlobjects/dmematerialgrouplist.h"
#include "mdlobjects/dmeeyeball.h"
#include "mdlobjects/dmeeyeballglobals.h"
#include "mdlobjects/dmeeyelid.h"
#include "mdlobjects/dmeboneweight.h"
#include "mdlobjects/dmebonemask.h"
#include "mdlobjects/dmebonemasklist.h"
#include "mdlobjects/dmeik.h"
#include "mdlobjects/dmeanimcmd.h"
#include "mdlobjects/dmemotioncontrol.h"
#include "mdlobjects/dmeposeparameter.h"
#include "mdlobjects/dmeposeparameterlist.h"
#include "mdlobjects/dmeanimblocksize.h"
#include "mdlobjects/dmeboneflexdriver.h"
#include "mdlobjects/dmejigglebone.h"
#include "mdlobjects/dmemouth.h"
#include "fbxutils/dmfbxserializer.h"
#include "mdlobjects/mpp_utils.h"
// Local includes
#include "studiomdl.h"
#include "collisionmodel.h"
#include "tier1/fmtstr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// The current model being loaded...
//-----------------------------------------------------------------------------
s_model_t *g_pCurrentModel = NULL;
void UnifyIndices( s_source_t *psource );
//-----------------------------------------------------------------------------
// Mapping of bone transforms
//-----------------------------------------------------------------------------
struct BoneTransformMap_t
{
// Number of bones
int m_nBoneCount;
// The order in which transforms appear in this list specifies their bone indices
CDmeTransform *m_ppTransforms[MAXSTUDIOSRCBONES];
// m_pnDmeModelToMdl[bone index in DmeModel] == bone index in studiomdl
int m_pnDmeModelToMdl[MAXSTUDIOSRCBONES];
// m_pnMdlToDmeModel[bone index in studiomdl] == bone index in DmeModel
int m_pnMdlToDmeModel[MAXSTUDIOSRCBONES];
};
//-----------------------------------------------------------------------------
// Index into an s_node_t array for the default root node
//-----------------------------------------------------------------------------
static int s_nDefaultRootNode;
//-----------------------------------------------------------------------------
// Balance/speed data
//-----------------------------------------------------------------------------
static CUtlVector<float> s_Balance;
static CUtlVector<float> s_Speed;
//-----------------------------------------------------------------------------
// List of unique vertices
//-----------------------------------------------------------------------------
struct VertIndices_t
{
int v;
int n;
int t[MAXSTUDIOTEXCOORDS];
int balance;
int speed;
};
static CUtlVector< VertIndices_t > s_UniqueVertices; // A list of the unique vertices in the mesh
// Given the non-unique vertex index, return the unique vertex index
// The indices are absolute indices into s_UniqueVertices
// But as both arrays contain information for all meshes in the DMX
// The proper offset for the desired mesh must be added to the lookup
// into the map but the value returned has the offset already built in
static CUtlVector< int > s_UniqueVerticesMap;
//-----------------------------------------------------------------------------
// Delta state intermediate data [used for positions, normals, etc.]
//-----------------------------------------------------------------------------
struct DeltaIndex_t
{
DeltaIndex_t() : m_nPositionIndex(-1), m_nNormalIndex(-1), m_nNextDelta(-1), m_nWrinkleIndex(-1), m_bInList(false) {}
int m_nPositionIndex; // Index into DeltaState_t::m_PositionDeltas
int m_nNormalIndex; // Index into DeltaState_t::m_NormalDeltas
int m_nWrinkleIndex; // Index into DeltaState_t::m_WrinkleDeltas
int m_nNextDelta; // Index into DeltaState_t::m_DeltaIndices;
bool m_bInList;
};
struct DeltaState_t
{
DeltaState_t() : m_nDeltaCount( 0 ), m_nFirstDelta( -1 ) {}
CUtlString m_Name;
CUtlVector< Vector > m_PositionDeltas;
CUtlVector< Vector > m_NormalDeltas;
CUtlVector< float > m_WrinkleDeltas;
CUtlVector< DeltaIndex_t > m_DeltaIndices;
int m_nDeltaCount;
int m_nFirstDelta;
};
// NOTE: This is a temporary which loses its state once Load_DMX is exited.
static CUtlVector<DeltaState_t> s_DeltaStates;
// Finds or adds delta states. These pointers are invalidated by calling FindOrAddDeltaState again
//-----------------------------------------------------------------------------
static DeltaState_t* FindOrAddDeltaState( const char *pDeltaStateName, int nBaseStateVertexCount )
{
int nCount = s_DeltaStates.Count();
for ( int i = 0; i < nCount; ++i )
{
if ( !Q_stricmp( s_DeltaStates[i].m_Name, pDeltaStateName ) )
{
MdlWarning( "Unsupported duplicate delta state named \"%s\" in DMX file\n", pDeltaStateName );
s_DeltaStates[i].m_DeltaIndices.EnsureCount( nBaseStateVertexCount );
return &s_DeltaStates[i];
}
}
int j = s_DeltaStates.AddToTail();
s_DeltaStates[j].m_Name = pDeltaStateName;
s_DeltaStates[j].m_DeltaIndices.SetCount( nBaseStateVertexCount );
return &s_DeltaStates[j];
}
struct VertexLookup_t
{
int v, n, t;
int index;
};
static bool VertexLookup_CompareFunc( VertexLookup_t const &a, VertexLookup_t const &b )
{
return ( ( a.v == b.v ) && ( a.n == b.n ) && ( a.t == b.t ) );
}
static unsigned int VertexLookup_KeyFunc( VertexLookup_t const &a )
{
return Hash12( &a );
}
//-----------------------------------------------------------------------------
// Loads the vertices from the model
//-----------------------------------------------------------------------------
static bool DefineUniqueVertices( CDmeVertexData *pBindState )
{
const CUtlVector<int> &positionIndices = pBindState->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
const CUtlVector<int> &normalIndices = pBindState->GetVertexIndexData( CDmeVertexData::FIELD_NORMAL );
const CUtlVector<int> &texcoordIndices = pBindState->GetVertexIndexData( CDmeVertexData::FIELD_TEXCOORD );
const CUtlVector<int> &balanceIndices = pBindState->GetVertexIndexData( CDmeVertexData::FIELD_BALANCE );
const CUtlVector<int> &speedIndices = pBindState->GetVertexIndexData( CDmeVertexData::FIELD_MORPH_SPEED );
int nPositionCount = positionIndices.Count();
int nNormalCount = normalIndices.Count();
int nTexcoordCount = texcoordIndices.Count();
int nBalanceCount = balanceIndices.Count();
int nSpeedCount = speedIndices.Count();
int nExtraTexcoordCount[MAXSTUDIOTEXCOORDS-1];
const CUtlVector<int> * extraTexcoordIndices[MAXSTUDIOTEXCOORDS - 1];
for (int i = 1; i < MAXSTUDIOTEXCOORDS; ++i)
{
FieldIndex_t nExtra = pBindState->FindFieldIndex(CFmtStr("texcoord$%d", i).Get());
if (nExtra != -1)
{
extraTexcoordIndices[i-1] = &pBindState->GetVertexIndexData(nExtra);
nExtraTexcoordCount[i-1] = extraTexcoordIndices[i-1]->Count();
if (nPositionCount != nExtraTexcoordCount[i-1])
{
MdlError("Encountered a mesh with invalid geometry (different number of indices for various data fields)\n");
return false;
}
}
else
{
extraTexcoordIndices[i-1] = NULL;
nExtraTexcoordCount[i-1] = 0;
}
}
if ( nNormalCount && nPositionCount != nNormalCount )
{
MdlError( "Encountered a mesh with invalid geometry (different number of indices for various data fields)\n" );
return false;
}
if ( nTexcoordCount && nPositionCount != nTexcoordCount )
{
MdlError( "Encountered a mesh with invalid geometry (different number of indices for various data fields)\n" );
return false;
}
if ( nBalanceCount && nPositionCount != nBalanceCount )
{
MdlError( "Encountered a mesh with invalid geometry (different number of indices for various data fields)\n" );
return false;
}
if ( nSpeedCount && nPositionCount != nSpeedCount )
{
MdlError( "Encountered a mesh with invalid geometry (different number of indices for various data fields)\n" );
return false;
}
// Make a hash table to speed up this de-duplication process:
CUtlHash< VertexLookup_t > vertexLookupHash( nPositionCount, 0, 0, VertexLookup_CompareFunc, VertexLookup_KeyFunc );
// Only add unique vertices to the list as in UnifyIndices
for ( int i = 0; i < nPositionCount; ++i )
{
VertIndices_t vert;
vert.v = g_numverts + positionIndices[i];
vert.n = ( nNormalCount > 0 ) ? g_numnormals + normalIndices[i] : -1;
vert.t[0] = ( nTexcoordCount > 0 ) ? g_numtexcoords[0] + texcoordIndices[i] : -1;
vert.balance = s_Balance.Count() + ( ( nBalanceCount > 0 ) ? balanceIndices[i] : 0 );
vert.speed = s_Speed.Count() + ( ( nSpeedCount > 0 ) ? speedIndices[i] : 0 );
for (int j = 1; j < MAXSTUDIOTEXCOORDS; ++j)
{
vert.t[j] = (nExtraTexcoordCount[j-1] > 0) ? g_numtexcoords[j] + extraTexcoordIndices[j-1]->Element(i) : -1;
}
VertexLookup_t vertexLookup = { vert.v, vert.n, vert.t[0], -1 };
UtlHashHandle_t vertexHandle = vertexLookupHash.Find( vertexLookup );
if ( vertexHandle == vertexLookupHash.InvalidHandle() )
{
// Unique
int k = s_UniqueVertices.AddToTail();
s_UniqueVertices[k] = vert;
s_UniqueVerticesMap.AddToTail( k );
vertexLookup.index = k;
vertexLookupHash.Insert( vertexLookup );
}
else
{
// Not unique
VertexLookup_t &equivalentVertex = vertexLookupHash.Element( vertexHandle );
s_UniqueVerticesMap.AddToTail( equivalentVertex.index );
}
}
return true;
}
//-----------------------------------------------------------------------------
// Loads the vertices from the model
//-----------------------------------------------------------------------------
static bool LoadVertices( CDmeDag *pDmeDag, CDmeVertexData *pBindState, const matrix3x4_t& mat, float flScale, int nBoneAssign, int *pBoneRemap, s_source_t *pSource )
{
// nBoneAssign is only used if the mesh has no skinning information
// It's the DMX bone index, but it might be < 0, in which case
// no bone has been defined yet. There are two options, use the
// default root bone which is always defined and at the origin
// or try and use the DmeDag's bone that was created when the mesh was
// loaded
if ( nBoneAssign < 0 )
{
nBoneAssign = s_nDefaultRootNode;
}
else
{
nBoneAssign = pBoneRemap[ nBoneAssign ];
if ( nBoneAssign < 0 )
{
nBoneAssign = s_nDefaultRootNode;
}
}
// Used by the morphing system to set up delta states
DefineUniqueVertices( pBindState );
matrix3x4_t normalMat;
MatrixInverseTranspose( mat, normalMat );
const CUtlVector<Vector> &positions = pBindState->GetPositionData( );
const CUtlVector<Vector> &normals = pBindState->GetNormalData( );
const CUtlVector<Vector2D> &texcoords = pBindState->GetTextureCoordData( );
const CUtlVector<float> &balances = pBindState->GetBalanceData( );
const CUtlVector<float> &speeds = pBindState->GetMorphSpeedData( );
int nCount = positions.Count();
int nJointCount = pBindState->HasSkinningData() ? pBindState->JointCount() : 0;
if ( nJointCount > MAXSTUDIOBONEWEIGHTS )
{
MdlError( "Too many bone influences per vertex!\n" );
return false;
}
if ( nJointCount <= 0 && nBoneAssign == s_nDefaultRootNode && pDmeDag )
{
// Use the bone created for the DmeDag node of the DmeMesh
for ( int i = 0; i < pSource->numbones; ++i )
{
if ( !Q_strcmp( pDmeDag->GetName(), pSource->localBone[ i ].name ) )
{
nBoneAssign = i;
break;
}
}
}
bool pbWarnmap[ MAXSTUDIOBONES ];
Q_memset( pbWarnmap, 0, MAXSTUDIOBONES * sizeof( bool ) );
float *pWeightBuf = (float *)malloc( nJointCount * sizeof( float ) );
int *pIndexBuf = (int *)malloc( nJointCount * sizeof( int ) );
// Copy positions + bone info
for ( int i = 0; i < nCount; ++i )
{
// NOTE: The transform transforms the positions into the bind space
VectorTransform( positions[i], mat, g_vertex[g_numverts] );
g_vertex[g_numverts] *= flScale;
if ( nJointCount == 0 )
{
g_bone[g_numverts].numbones = 1;
g_bone[g_numverts].bone[0] = nBoneAssign;
g_bone[g_numverts].weight[0] = 1.0;
}
else
{
const float *pJointWeights = pBindState->GetJointWeightData( i );
const int *pJointIndices = pBindState->GetJointIndexData( i );
memcpy( pWeightBuf, pJointWeights, nJointCount * sizeof(float) );
memcpy( pIndexBuf, pJointIndices, nJointCount * sizeof(int) );
int nBoneCount = SortAndBalanceBones( nJointCount, MAXSTUDIOBONEWEIGHTS, pIndexBuf, pWeightBuf );
int nBoneIndex = -1;
g_bone[g_numverts].numbones = nBoneCount;
for ( int j = 0; j < nBoneCount; ++j )
{
nBoneIndex = pBoneRemap[ pIndexBuf[j] ];
if ( nBoneIndex < 0 )
{
if ( pIndexBuf[j] < MAXSTUDIOBONES && !pbWarnmap[ pIndexBuf[j] ] )
{
MdlWarning( "DmeMesh[%s] Verts Assigned To DmeModel.jointList[%d] Which Isn't Mapped To The Dag Hierarchy\n",
pDmeDag->GetName(), pIndexBuf[j] );
pbWarnmap[ pIndexBuf[j] ] = true;
}
g_bone[g_numverts].bone[j] = nBoneAssign;
}
else
{
g_bone[g_numverts].bone[j] = nBoneIndex;
}
g_bone[g_numverts].weight[j] = pWeightBuf[j];
}
}
++g_numverts;
}
free( pWeightBuf );
free( pIndexBuf );
// Copy normals
Vector vNormal;
nCount = normals.Count();
for ( int i = 0; i < nCount; ++i )
{
VectorCopy( normals[i], vNormal );
VectorNormalize( vNormal );
if ( fabs( VectorLength( vNormal ) - 1.0f ) > 0.01 )
{
MdlWarning( "Non-Unit Length Normal [%d] < %8.6f %8.6f %8.6f >\n", i, vNormal.x, vNormal.y, vNormal.z );
}
VectorRotate( vNormal, normalMat, g_normal[g_numnormals] );
++g_numnormals;
}
// Copy texcoords
nCount = texcoords.Count();
bool bFlipVCoordinate = pBindState->IsVCoordinateFlipped();
for ( int i = 0; i < nCount; ++i )
{
g_texcoord[0][g_numtexcoords[0]].x = texcoords[i].x;
g_texcoord[0][g_numtexcoords[0]].y = bFlipVCoordinate ? 1.0f - texcoords[i].y : texcoords[i].y;
++g_numtexcoords[0];
}
// Check for additional texcoords
for (int i = 1; i < MAXSTUDIOTEXCOORDS; ++i)
{
g_numtexcoords[i] = 0;
FieldIndex_t nFieldIndex = pBindState->FindFieldIndex(CFmtStr("texcoord$%d", i).Get());
if (nFieldIndex > -1)
{
CDmrArrayConst<Vector2D> vertexData = pBindState->GetVertexData(nFieldIndex);
const CUtlVector<Vector2D> &extraTexcoords = vertexData.Get();
nCount = extraTexcoords.Count();
for (int j = 0; j < nCount; ++j)
{
g_texcoord[i][g_numtexcoords[i]].x = extraTexcoords[j].x;
g_texcoord[i][g_numtexcoords[i]].y = bFlipVCoordinate ? 1.0f - extraTexcoords[j].y : extraTexcoords[j].y;
++g_numtexcoords[i];
}
}
else
{
break;
}
}
// In the event of no speed or balance map, use the same value of 1 for all vertices
if ( balances.Count() )
{
s_Balance.AddMultipleToTail( balances.Count(), balances.Base() );
}
else
{
s_Balance.AddToTail( 1.0f );
}
if ( speeds.Count() )
{
s_Speed.AddMultipleToTail( speeds.Count(), speeds.Base() );
}
else
{
s_Speed.AddToTail( 1.0f );
}
return true;
}
//-----------------------------------------------------------------------------
// Hook delta into delta list
//-----------------------------------------------------------------------------
static void AddToDeltaList( DeltaState_t *pDeltaStateData, int nUniqueVertex )
{
DeltaIndex_t &index = pDeltaStateData->m_DeltaIndices[ nUniqueVertex ];
if ( !index.m_bInList )
{
index.m_nNextDelta = pDeltaStateData->m_nFirstDelta;
pDeltaStateData->m_nFirstDelta = nUniqueVertex;
pDeltaStateData->m_nDeltaCount++;
index.m_bInList = true;
}
}
//-----------------------------------------------------------------------------
// Loads the vertices from the delta state
//-----------------------------------------------------------------------------
static bool LoadDeltaState(
CDmeVertexDeltaData *pDeltaState,
CDmeVertexData *pBindState,
const matrix3x4_t& mat,
float flScale,
int nStartingUniqueVertex,
int nStartingUniqueVertexMap )
{
DeltaState_t *pDeltaStateData = FindOrAddDeltaState( pDeltaState->GetName(), nStartingUniqueVertex + pBindState->VertexCount() );
matrix3x4_t normalMat;
MatrixInverseTranspose( mat, normalMat );
const CUtlVector<Vector> &positions = pDeltaState->GetPositionData( );
const CUtlVector<int> &positionIndices = pDeltaState->GetVertexIndexData( CDmeVertexDataBase::FIELD_POSITION );
const CUtlVector<Vector> &normals = pDeltaState->GetNormalData( );
const CUtlVector<int> &normalIndices = pDeltaState->GetVertexIndexData( CDmeVertexDataBase::FIELD_NORMAL );
const CUtlVector<float> &wrinkle = pDeltaState->GetWrinkleData( );
const CUtlVector<int> &wrinkleIndices = pDeltaState->GetVertexIndexData( CDmeVertexDataBase::FIELD_WRINKLE );
if ( positions.Count() != positionIndices.Count() )
{
MdlError( "DeltaState %s contains a different number of positions + position indices!\n", pDeltaState->GetName() );
return false;
}
if ( normals.Count() != normalIndices.Count() )
{
MdlError( "DeltaState %s contains a different number of normals + normal indices!\n", pDeltaState->GetName() );
return false;
}
if ( wrinkle.Count() != wrinkleIndices.Count() )
{
MdlError( "DeltaState %s contains a different number of wrinkles + wrinkle indices!\n", pDeltaState->GetName() );
return false;
}
// Copy position delta
int nCount = positions.Count();
for ( int i = 0; i < nCount; ++i )
{
Vector vecDelta;
// NOTE NOTE!!: This is VectorRotate, *not* VectorTransform. This is because
// we're transforming a delta, which is basically a direction vector. To
// move it into the new space, we must rotate it
VectorRotate( positions[i], mat, vecDelta );
vecDelta *= flScale;
int nPositionIndex = pDeltaStateData->m_PositionDeltas.AddToTail( vecDelta );
// Indices
const CUtlVector< int > &baseVerts = pBindState->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, positionIndices[i] );
int nBaseVertCount = baseVerts.Count();
for ( int k = 0; k < nBaseVertCount; ++k )
{
int nUniqueVertexIndex = s_UniqueVerticesMap[ nStartingUniqueVertexMap + baseVerts[k] ];
AddToDeltaList( pDeltaStateData, nUniqueVertexIndex );
DeltaIndex_t &index = pDeltaStateData->m_DeltaIndices[ nUniqueVertexIndex ];
index.m_nPositionIndex = nPositionIndex;
}
}
// Copy normals
nCount = normals.Count();
for ( int i = 0; i < nCount; ++i )
{
Vector vecDelta;
VectorRotate( normals[i], normalMat, vecDelta );
int nNormalIndex = pDeltaStateData->m_NormalDeltas.AddToTail( vecDelta );
// Indices
const CUtlVector< int > &baseVerts = pBindState->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_NORMAL, normalIndices[i] );
int nBaseVertCount = baseVerts.Count();
for ( int k = 0; k < nBaseVertCount; ++k )
{
int nUniqueVertexIndex = s_UniqueVerticesMap[ nStartingUniqueVertexMap + baseVerts[k] ];
AddToDeltaList( pDeltaStateData, nUniqueVertexIndex );
DeltaIndex_t &index = pDeltaStateData->m_DeltaIndices[ nUniqueVertexIndex ];
index.m_nNormalIndex = nNormalIndex;
}
}
// Copy wrinkle
nCount = wrinkle.Count();
for ( int i = 0; i < nCount; ++i )
{
int nWrinkleIndex = pDeltaStateData->m_WrinkleDeltas.AddToTail( wrinkle[i] );
// Indices
const CUtlVector< int > &baseVerts = pBindState->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_WRINKLE, wrinkleIndices[i] );
int nBaseVertCount = baseVerts.Count();
for ( int k = 0; k < nBaseVertCount; ++k )
{
int nUniqueVertexIndex = s_UniqueVerticesMap[ nStartingUniqueVertexMap + baseVerts[k] ];
AddToDeltaList( pDeltaStateData, nUniqueVertexIndex );
DeltaIndex_t &index = pDeltaStateData->m_DeltaIndices[ nUniqueVertexIndex ];
index.m_nWrinkleIndex = nWrinkleIndex;
}
}
return true;
}
static int GetExtraTexcoordIndex(CDmeVertexData *pVertexData, int nVertexIndex, int nChannel)
{
FieldIndex_t nFieldIndex = pVertexData->FindFieldIndex(CFmtStr("texcoord$%d", nChannel).Get());
if (nFieldIndex < 0)
return -1;
CDmrArrayConst<int> indices(pVertexData->GetIndexData(nFieldIndex));
return indices[nVertexIndex];
}
// JasonM TODO: unify ParseQuadFaceData() and ParseFaceData()
//-----------------------------------------------------------------------------
// Reads the quad face data from the DMX data
//-----------------------------------------------------------------------------
static void ParseQuadFaceData( CDmeVertexData *pVertexData, int material, int *pIndices, int vi, int ni, int* ti )
{
s_tmpface_t f;
f.material = material;
int p, n, t;
p = pVertexData->GetPositionIndex(pIndices[0]); n = pVertexData->GetNormalIndex(pIndices[0]); t = pVertexData->GetTexCoordIndex(pIndices[0]);
f.a = (p >= 0) ? vi + p : 0; f.na = (n >= 0) ? ni + n : 0; f.ta[0] = (t >= 0) ? ti[0] + t : 0;
p = pVertexData->GetPositionIndex(pIndices[3]); n = pVertexData->GetNormalIndex(pIndices[3]); t = pVertexData->GetTexCoordIndex(pIndices[3]);
f.b = (p >= 0) ? vi + p : 0; f.nb = (n >= 0) ? ni + n : 0; f.tb[0] = (t >= 0) ? ti[0] + t : 0;
p = pVertexData->GetPositionIndex(pIndices[2]); n = pVertexData->GetNormalIndex(pIndices[2]); t = pVertexData->GetTexCoordIndex(pIndices[2]);
f.c = (p >= 0) ? vi + p : 0; f.nc = (n >= 0) ? ni + n : 0; f.tc[0] = (t >= 0) ? ti[0] + t : 0;
p = pVertexData->GetPositionIndex(pIndices[1]); n = pVertexData->GetNormalIndex(pIndices[1]); t = pVertexData->GetTexCoordIndex(pIndices[1]);
f.d = (p >= 0) ? vi + p : 0; f.nd = (n >= 0) ? ni + n : 0; f.td[0] = (t >= 0) ? ti[0] + t : 0;
Assert( f.a <= (unsigned long)g_numverts && f.b <= (unsigned long)g_numverts && f.c <= (unsigned long)g_numverts && f.d <= (unsigned long)g_numverts );
Assert( f.na <= (unsigned long)g_numnormals && f.nb <= (unsigned long)g_numnormals && f.nc <= (unsigned long)g_numnormals && f.nd <= (unsigned long)g_numnormals );
Assert(f.ta[0] <= (unsigned long)g_numtexcoords[0] && f.tb[0] <= (unsigned long)g_numtexcoords[0] && f.tc[0] <= (unsigned long)g_numtexcoords[0] && f.td[0] <= (unsigned long)g_numtexcoords[0]);
for (int i = 1; i < (MAXSTUDIOTEXCOORDS); ++i)
{
t = GetExtraTexcoordIndex(pVertexData, pIndices[0], i);
f.ta[i] = (t >= 0) ? ti[i] + t : 0;
t = GetExtraTexcoordIndex(pVertexData, pIndices[3], i);
f.tb[i] = (t >= 0) ? ti[i] + t : 0;
t = GetExtraTexcoordIndex(pVertexData, pIndices[2], i);
f.tc[i] = (t >= 0) ? ti[i] + t : 0;
t = GetExtraTexcoordIndex(pVertexData, pIndices[1], i);
f.td[i] = (t >= 0) ? ti[i] + t : 0;
Assert(f.ta[i] <= (unsigned long)g_numtexcoords[i] && f.tb[i] <= (unsigned long)g_numtexcoords[i] && f.tc[i] <= (unsigned long)g_numtexcoords[i] && f.td[i] <= (unsigned long)g_numtexcoords[i]);
}
int i = g_numfaces++;
g_face[i] = f;
}
//-----------------------------------------------------------------------------
// Reads the face data from the DMX data
//-----------------------------------------------------------------------------
static void ParseFaceData( CDmeVertexData *pVertexData, int material, int v1, int v2, int v3, int vi, int ni, int* ti )
{
s_tmpface_t f;
f.material = material;
int p, n, t;
p = pVertexData->GetPositionIndex(v1); n = pVertexData->GetNormalIndex(v1); t = pVertexData->GetTexCoordIndex(v1);
f.a = (p >= 0) ? vi + p : 0; f.na = (n >= 0) ? ni + n : 0; f.ta[0] = (t >= 0) ? ti[0] + t : 0;
p = pVertexData->GetPositionIndex(v2); n = pVertexData->GetNormalIndex(v2); t = pVertexData->GetTexCoordIndex(v2);
f.b = (p >= 0) ? vi + p : 0; f.nb = (n >= 0) ? ni + n : 0; f.tb[0] = (t >= 0) ? ti[0] + t : 0;
p = pVertexData->GetPositionIndex(v3); n = pVertexData->GetNormalIndex(v3); t = pVertexData->GetTexCoordIndex(v3);
f.c = (p >= 0) ? vi + p : 0; f.nc = (n >= 0) ? ni + n : 0; f.tc[0] = (t >= 0) ? ti[0] + t : 0;
Assert( f.a <= (unsigned long)g_numverts && f.b <= (unsigned long)g_numverts && f.c <= (unsigned long)g_numverts );
Assert( f.na <= (unsigned long)g_numnormals && f.nb <= (unsigned long)g_numnormals && f.nc <= (unsigned long)g_numnormals );
Assert(f.ta[0] <= (unsigned long)g_numtexcoords[0] && f.tb[0] <= (unsigned long)g_numtexcoords[0] && f.tc[0] <= (unsigned long)g_numtexcoords[0]);
for (int i = 1; i < (MAXSTUDIOTEXCOORDS); ++i)
{
t = GetExtraTexcoordIndex(pVertexData, v1, i);
f.ta[i] = (t >= 0) ? ti[i] + t : 0;
t = GetExtraTexcoordIndex(pVertexData, v2, i);
f.tb[i] = (t >= 0) ? ti[i] + t : 0;
t = GetExtraTexcoordIndex(pVertexData, v3, i);
f.tc[i] = (t >= 0) ? ti[i] + t : 0;
Assert(f.ta[i] <= (unsigned long)g_numtexcoords[i] && f.tb[i] <= (unsigned long)g_numtexcoords[i] && f.tc[i] <= (unsigned long)g_numtexcoords[i]);
}
int i = g_numfaces++;
g_face[i] = f;
}
//-----------------------------------------------------------------------------
// Reads the mesh data from the DMX data
//-----------------------------------------------------------------------------
static bool LoadMesh( CDmeDag *pDmeDag, CDmeMesh *pMesh, CDmeVertexData *pBindState, const matrix3x4_t& mat, float flScale,
int nBoneAssign, int *pBoneRemap, s_source_t *pSource )
{
pMesh->CollapseRedundantNormals( normal_blend );
// Load the vertices
int nStartingVertex = g_numverts;
int nStartingNormal = g_numnormals;
int nStartingUniqueCount = s_UniqueVertices.Count();
int nStartingUniqueMapCount = s_UniqueVerticesMap.Count();
int nStartingTexCoord[MAXSTUDIOTEXCOORDS];
for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
{
nStartingTexCoord[i] = g_numtexcoords[i];
}
// This defines s_UniqueVertices & s_UniqueVerticesMap
LoadVertices( pDmeDag, pBindState, mat, flScale, nBoneAssign, pBoneRemap, pSource );
// Load the deltas
int nDeltaStateCount = pMesh->DeltaStateCount();
for ( int i = 0; i < nDeltaStateCount; ++i )
{
CDmeVertexDeltaData *pDeltaState = pMesh->GetDeltaState( i );
if ( !LoadDeltaState( pDeltaState, pBindState, mat, flScale, nStartingUniqueCount, nStartingUniqueMapCount ) )
return false;
}
// load the base triangles
int texture;
int material;
char pTextureName[MAX_PATH];
int nFaceSetCount = pMesh->FaceSetCount();
for ( int i = 0; i < nFaceSetCount; ++i )
{
CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
CDmeMaterial *pMaterial = pFaceSet->GetMaterial();
// Get the material name
Q_strncpy( pTextureName, pMaterial->GetMaterialName(), sizeof(pTextureName) );
// funky texture overrides (specified with the -t command-line argument)
for ( int j = 0; j < numrep; j++ )
{
if ( sourcetexture[j][0] == '\0' )
{
Q_strncpy( pTextureName, defaulttexture[j], sizeof(pTextureName) );
break;
}
if ( Q_stricmp( pTextureName, sourcetexture[j]) == 0 )
{
Q_strncpy( pTextureName, defaulttexture[j], sizeof(pTextureName) );
break;
}
}
// skip all faces with the null texture on them.
char pPathNoExt[MAX_PATH];
Q_StripExtension( pTextureName, pPathNoExt, sizeof(pPathNoExt) );
if ( !Q_stricmp( pPathNoExt, "null" ) )
continue;
texture = LookupTexture( pTextureName, true );
pSource->texmap[texture] = texture; // hack, make it 1:1
material = UseTextureAsMaterial( texture );
// Is this a quad-only subd?
bool bQuadSubd = ( gflags & STUDIOHDR_FLAGS_SUBDIVISION_SURFACE ) != 0;
// prepare indices
int nFirstIndex = 0;
int nIndexCount = pFaceSet->NumIndices();
while ( nFirstIndex < nIndexCount )
{
int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
// Quad subd face?
if ( bQuadSubd && ( nVertexCount == 4 ) )
{
int quadIndices[4];
for ( int j = 0; j < 4; j++ )
{
quadIndices[j] = pFaceSet->GetIndex( nFirstIndex + j );
}
ParseQuadFaceData( pBindState, material, quadIndices, nStartingVertex, nStartingNormal, nStartingTexCoord );
nFirstIndex += 5; // -1 in list between face indices, so jump over 5 elements, not 4
}
else
{
if ( nVertexCount >= 3 )
{
int nOutCount = (nVertexCount-2) * 3;
int *pIndices = (int*)malloc( nOutCount * sizeof(int) );
pMesh->ComputeTriangulatedIndices( pBindState, pFaceSet, nFirstIndex, pIndices, nOutCount );
for ( int ii = 0; ii < nOutCount; ii +=3 )
{
ParseFaceData( pBindState, material, pIndices[ii], pIndices[ii+2], pIndices[ii+1], nStartingVertex, nStartingNormal, nStartingTexCoord );
}
free( pIndices );
}
nFirstIndex += nVertexCount + 1; // -1 in list between face indices, so jump over nVertexCount + 1 elements, not nVertexCount elements
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Method used to add mesh data
//-----------------------------------------------------------------------------
struct LoadMeshInfo_t
{
s_source_t *m_pSource;
CDmeModel *m_pModel;
float m_flScale;
int *m_pBoneRemap;
matrix3x4_t m_pBindPose[MAXSTUDIOSRCBONES];
};
static bool LoadMeshes( const LoadMeshInfo_t &info, CDmeDag *pDag, const matrix3x4_t &parentToBindPose, int nBoneAssign )
{
// We want to create an aggregate matrix transforming from this dag to its closest
// parent which actually is an animated joint. This is done so we can autoskin
// meshes to their closest parents if they have not been skinned.
matrix3x4_t dagToBindPose;
int nFoundIndex = info.m_pModel->GetJointIndex( pDag );
if ( nFoundIndex >= 0 /* && ( pDag == info.m_pModel || CastElement< CDmeJoint >( pDag ) ) */ )
{
nBoneAssign = nFoundIndex;
}
if ( nFoundIndex >= 0 )
{
ConcatTransforms( parentToBindPose, info.m_pBindPose[nFoundIndex], dagToBindPose );
}
else
{
// NOTE: This isn't particularly kosher; we're using the current pose instead of the bind pose
// because there's no transform in the bind pose
matrix3x4_t dagToParent;
pDag->GetTransform()->GetTransform( dagToParent );
ConcatTransforms( parentToBindPose, dagToParent, dagToBindPose );
}
CDmeMesh *pMesh = CastElement< CDmeMesh >( pDag->GetShape() );
if ( pMesh )
{
CDmeVertexData *pBindState = pMesh->FindBaseState( "bind" );
if ( !pBindState )
return false;
if ( !LoadMesh( pDag, pMesh, pBindState, dagToBindPose, info.m_flScale, nBoneAssign, info.m_pBoneRemap, info.m_pSource ) )
return false;
}
int nCount = pDag->GetChildCount();
for ( int i = 0; i < nCount; ++i )
{
CDmeDag *pChild = pDag->GetChild( i );
if ( !LoadMeshes( info, pChild, dagToBindPose, nBoneAssign ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Method used to add mesh data
//-----------------------------------------------------------------------------
static bool LoadMeshes( CDmeModel *pModel, float flScale, int *pBoneRemap, s_source_t *pSource )
{
matrix3x4_t mat;
SetIdentityMatrix( mat );
LoadMeshInfo_t info;
info.m_pModel = pModel;
info.m_flScale = flScale;
info.m_pBoneRemap = pBoneRemap;
info.m_pSource = pSource;
CDmeTransformList *pBindPose = pModel->FindBaseState( "bind" );
int nCount = pBindPose ? pBindPose->GetTransformCount() : pModel->GetJointCount();
for ( int i = 0; i < nCount; ++i )
{
CDmeTransform *pTransform = pBindPose ? pBindPose->GetTransform(i) : pModel->GetJointTransform(i);
matrix3x4_t jointTransform;
pTransform->GetTransform( info.m_pBindPose[i] );
}
int nChildCount = pModel->GetChildCount();
for ( int i = 0; i < nChildCount; ++i )
{
CDmeDag *pChild = pModel->GetChild( i );
if ( !LoadMeshes( info, pChild, mat, -1 ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Builds s_vertanim_ts
//-----------------------------------------------------------------------------
static void BuildVertexAnimations( s_source_t *pSource )
{
int nCount = s_DeltaStates.Count();
if ( nCount == 0 )
return;
Assert( s_Speed.Count() > 0 );
Assert( s_Balance.Count() > 0 );
Assert( s_UniqueVertices.Count() == g_numvlist );
s_vertanim_t *pVertAnim = (s_vertanim_t *)malloc( g_numvlist * sizeof( s_vertanim_t ) );
for ( int i = 0; i < nCount; ++i )
{
DeltaState_t &state = s_DeltaStates[i];
s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( pSource, state.m_Name );
pSourceAnim->numframes = 1;
pSourceAnim->startframe = 0;
pSourceAnim->endframe = 0;
pSourceAnim->newStyleVertexAnimations = true;
// Traverse the linked list of unique vertex indices j that has a delta
int nVertAnimCount = 0;
for ( int j = state.m_nFirstDelta; j >= 0; j = state.m_DeltaIndices[j].m_nNextDelta )
{
// The Delta Indices array is a parallel array to s_UniqueVertices
// j is used to index into both
DeltaIndex_t &delta = state.m_DeltaIndices[j];
Assert( delta.m_nPositionIndex >= 0 || delta.m_nNormalIndex >= 0 || delta.m_nWrinkleIndex >= 0 );
VertIndices_t &uniqueVert = s_UniqueVertices[j];
const v_unify_t *pList = v_list[uniqueVert.v];
for( ; pList; pList = pList->next )
{
if ( pList->n != uniqueVert.n || pList->t[0] != uniqueVert.t[0] )
continue;
s_vertanim_t& vertanim = pVertAnim[nVertAnimCount++];
vertanim.vertex = pList - v_listdata;
vertanim.speed = s_Speed[ s_UniqueVertices[j].speed ];
vertanim.side = s_Balance[ s_UniqueVertices[j].balance ];
if ( delta.m_nPositionIndex >= 0 )
{
vertanim.pos = state.m_PositionDeltas[ delta.m_nPositionIndex ];
}
else
{
vertanim.pos = vec3_origin;
}
if ( delta.m_nNormalIndex >= 0 )
{
vertanim.normal = state.m_NormalDeltas[ delta.m_nNormalIndex ];
}
else
{
vertanim.normal = vec3_origin;
}
if ( delta.m_nWrinkleIndex >= 0 )
{
vertanim.wrinkle = state.m_WrinkleDeltas[ delta.m_nWrinkleIndex ];
}
else
{
vertanim.wrinkle = 0.0f;
}
}
}
pSourceAnim->numvanims[0] = nVertAnimCount;
pSourceAnim->vanim[0] = (s_vertanim_t *)calloc( nVertAnimCount, sizeof( s_vertanim_t ) );
memcpy( pSourceAnim->vanim[0], pVertAnim, nVertAnimCount * sizeof( s_vertanim_t ) );
}
free( pVertAnim );
}
//-----------------------------------------------------------------------------
// Handles DmeJiggleBones
//-----------------------------------------------------------------------------
static void HandleDmeJiggleBone( const CDmeDag *pDmeDag )
{
const CDmeJiggleBone *pDmeJiggleBone = CastElementConst< CDmeJiggleBone >( pDmeDag );
if ( !pDmeJiggleBone )
return;
const char *pName = pDmeJiggleBone->GetName();
for ( int i = 0; i < g_numjigglebones; ++i )
{
if ( !Q_stricmp( pName, g_jigglebones[i].bonename ) )
{
MdlWarning( "2000: Jiggle Bone: %s already defined, ignoring additional declarations\n", pName );
return;
}
}
struct s_jigglebone_t *pJiggleBone = &g_jigglebones[ g_numjigglebones ];
++g_numjigglebones;
Q_strncpy( pJiggleBone->bonename, pName, sizeof( pJiggleBone->bonename ) );
// default values
memset( &pJiggleBone->data, 0, sizeof( mstudiojigglebone_t ) );
pJiggleBone->data.length = 10.0f;
pJiggleBone->data.yawStiffness = 100.0f;
pJiggleBone->data.pitchStiffness = 100.0f;
pJiggleBone->data.alongStiffness = 100.0f;
pJiggleBone->data.baseStiffness = 100.0f;
pJiggleBone->data.baseMinUp = -100.0f;
pJiggleBone->data.baseMaxUp = 100.0f;
pJiggleBone->data.baseMinLeft = -100.0f;
pJiggleBone->data.baseMaxLeft = 100.0f;
pJiggleBone->data.baseMinForward = -100.0f;
pJiggleBone->data.baseMaxForward = 100.0f;
// Common Parameters
pJiggleBone->data.length = pDmeJiggleBone->m_flLength;
pJiggleBone->data.tipMass = pDmeJiggleBone->m_flTipMass;
pJiggleBone->data.flags |= ( pDmeJiggleBone->m_bLengthConstrained ? JIGGLE_HAS_LENGTH_CONSTRAINT : 0 );
pJiggleBone->data.angleLimit = DEG2RAD( pDmeJiggleBone->m_flAngleLimit );
pJiggleBone->data.flags |= ( pDmeJiggleBone->m_bYawConstrained ? JIGGLE_HAS_YAW_CONSTRAINT : 0 );
pJiggleBone->data.minYaw = DEG2RAD( pDmeJiggleBone->m_flYawMin );
pJiggleBone->data.maxYaw = DEG2RAD( pDmeJiggleBone->m_flYawMax );
pJiggleBone->data.yawFriction = pDmeJiggleBone->m_flYawFriction;
pJiggleBone->data.yawBounce = pDmeJiggleBone->m_flYawBounce;
pJiggleBone->data.flags |= ( pDmeJiggleBone->m_bAngleConstrained ? JIGGLE_HAS_ANGLE_CONSTRAINT : 0 );
pJiggleBone->data.minPitch = DEG2RAD( pDmeJiggleBone->m_flPitchMin );
pJiggleBone->data.maxPitch = DEG2RAD( pDmeJiggleBone->m_flPitchMax );
pJiggleBone->data.pitchFriction = pDmeJiggleBone->m_flPitchFriction;
pJiggleBone->data.pitchBounce = pDmeJiggleBone->m_flPitchBounce;
if ( pDmeJiggleBone->m_bFlexible )
{
if ( pDmeJiggleBone->m_bRigid )
{
MdlWarning( "2001: Jiggle Bone %s: Both flexible and rigid set, ignoring rigid\n", pName );
}
pJiggleBone->data.flags |= ( JIGGLE_IS_FLEXIBLE );
pJiggleBone->data.flags |= ( pDmeJiggleBone->m_bPitchConstrained ? JIGGLE_HAS_PITCH_CONSTRAINT : 0 );
// flexible parameters - I think damping should be clamped [0, 10] but code
// in studiomdl looks incorrect and clamps 0, 1000.0f
pJiggleBone->data.yawStiffness = clamp( pDmeJiggleBone->m_flYawStiffness.Get(), 0.0f, 1000.0f );
pJiggleBone->data.yawDamping = clamp( pDmeJiggleBone->m_flYawDamping.Get(), 0.0f, 10.0f );
pJiggleBone->data.pitchStiffness = clamp( pDmeJiggleBone->m_flPitchStiffness.Get(), 0.0f, 1000.0f );
pJiggleBone->data.pitchDamping = clamp( pDmeJiggleBone->m_flPitchDamping.Get(), 0.0f, 10.0f );
pJiggleBone->data.alongStiffness = clamp( pDmeJiggleBone->m_flAlongStiffness.Get(), 0.0f, 1000.0f );
pJiggleBone->data.alongDamping = clamp( pDmeJiggleBone->m_flAlongDamping.Get(), 0.0f, 10.0f );
}
else if ( pDmeJiggleBone->m_bRigid )
{
pJiggleBone->data.flags |= ( JIGGLE_IS_FLEXIBLE | JIGGLE_HAS_LENGTH_CONSTRAINT );
}
else
{
// TODO: Is neither rigid or flexible an error?
}
if ( pDmeJiggleBone->m_bBaseSpring )
{
// flexible parameters - I think damping should be clamped [0, 10] but code
// in studiomdl looks incorrect and clamps 0, 1000.0f
pJiggleBone->data.baseMass = pDmeJiggleBone->m_flBaseMass;
pJiggleBone->data.baseStiffness = clamp( pDmeJiggleBone->m_flBaseStiffness.Get(), 0.0f, 1000.0f );
pJiggleBone->data.baseDamping = clamp( pDmeJiggleBone->m_flBaseStiffness.Get(), 0.0f, 10.0f );
pJiggleBone->data.baseMinLeft = pDmeJiggleBone->m_flBaseYawMin;
pJiggleBone->data.baseMaxLeft = pDmeJiggleBone->m_flBaseYawMax;
pJiggleBone->data.baseLeftFriction = pDmeJiggleBone->m_flBaseYawFriction;
pJiggleBone->data.baseMinUp = pDmeJiggleBone->m_flBasePitchMin;
pJiggleBone->data.baseMaxUp = pDmeJiggleBone->m_flBasePitchMax;
pJiggleBone->data.baseUpFriction = pDmeJiggleBone->m_flBasePitchFriction;
pJiggleBone->data.baseMinForward = pDmeJiggleBone->m_flBaseAlongMin;
pJiggleBone->data.baseMaxForward = pDmeJiggleBone->m_flBaseAlongMax;
pJiggleBone->data.baseForwardFriction = pDmeJiggleBone->m_flBaseAlongFriction;
}
}
//-----------------------------------------------------------------------------
// Loads the skeletal hierarchy from the game model, returns bone count
//-----------------------------------------------------------------------------
static bool AddDagJoint( CDmeModel *pModel, CDmeDag *pDag, s_node_t *pNodes, int nParentIndex, BoneTransformMap_t &boneMap )
{
CDmeTransform *pDmeTransform = pDag->GetTransform();
if ( !pDmeTransform )
return true;
// Need room for one implicit bone added
if ( boneMap.m_nBoneCount >= ( MAXSTUDIOSRCBONES - 1 ) )
{
MdlWarning( "Ignoring Bone %s and children, too many bones [max can be %d]!\n", pDag->GetName(), MAXSTUDIOSRCBONES - 1 );
return false;
}
const int nJointIndex = boneMap.m_nBoneCount++;
boneMap.m_ppTransforms[ nJointIndex ] = pDmeTransform;
if ( pModel )
{
const int nFoundIndex = pModel->GetJointIndex( pDag );
if ( nFoundIndex >= 0 )
{
boneMap.m_pnDmeModelToMdl[ nFoundIndex ] = nJointIndex;
boneMap.m_pnMdlToDmeModel[ nJointIndex ] = nFoundIndex;
}
else
{
MdlWarning( "Joint %s doesn't appear in DmeModel[%s].jointList\n", pDag->GetName(), pModel->GetName() );
}
}
HandleDmeJiggleBone( pDag );
Q_strncpy( pNodes[ nJointIndex ].name, pDag->GetName(), sizeof( pNodes[ nJointIndex ].name ) );
pNodes[ nJointIndex ].parent = nParentIndex;
// Now deal with children
for ( int i = 0; i < pDag->GetChildCount(); ++i )
{
CDmeDag *pChild = pDag->GetChild( i );
if ( !pChild )
continue;
if ( !AddDagJoint( pModel, pChild, pNodes, nJointIndex, boneMap ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Main entry point for loading the skeleton
//-----------------------------------------------------------------------------
static int LoadSkeleton( CDmeDag *pRoot, CDmeModel *pModel, s_node_t *pNodes, BoneTransformMap_t &boneMap )
{
// Initialize bone indices
boneMap.m_nBoneCount = 0;
for ( int i = 0; i < MAXSTUDIOSRCBONES; ++i )
{
pNodes[i].name[0] = 0;
pNodes[i].parent = -1;
boneMap.m_pnDmeModelToMdl[i] = -1;
boneMap.m_pnMdlToDmeModel[i] = -1;
boneMap.m_ppTransforms[i] = NULL;
}
// Don't create joints for the the root dag ever.. just deal with the children
for ( int i = 0; i < pRoot->GetChildCount(); ++i )
{
CDmeDag *pChild = pRoot->GetChild( i );
if ( !pChild )
continue;
if ( !AddDagJoint( pModel, pChild, pNodes, -1, boneMap ) )
return 0;
}
// Add a default identity bone used for autoskinning if no joints are specified
s_nDefaultRootNode = boneMap.m_nBoneCount;
Q_strncpy( pNodes[s_nDefaultRootNode].name, "defaultRoot", sizeof( pNodes[ s_nDefaultRootNode ].name ) );
pNodes[s_nDefaultRootNode].parent = -1;
// +1 for the default identity bone just added
return boneMap.m_nBoneCount + 1;
}
//-----------------------------------------------------------------------------
// Loads the attachments found in the file
//-----------------------------------------------------------------------------
static void LoadAttachments( CDmeDag *pRoot, CDmeDag *pDag, s_source_t *pSource, bool bStaticProp )
{
CDmeAttachment *pAttachment = CastElement< CDmeAttachment >( pDag->GetShape() );
if ( pAttachment && ( pDag != pRoot ) )
{
int i = pSource->m_Attachments.AddToTail();
s_attachment_t &attachment = pSource->m_Attachments[i];
memset( &attachment, 0, sizeof(s_attachment_t) );
Q_strncpy( attachment.name, pAttachment->GetName(), sizeof( attachment.name ) );
Q_strncpy( attachment.bonename, pDag->GetName(), sizeof( attachment.bonename ) );
SetIdentityMatrix( attachment.local );
if ( bStaticProp )
{
// Static prop will remove all bones so put the attachment transform
// on the attachment rather than the bone. Also ignore all attachment
// flags
pDag->GetAbsTransform( attachment.local );
}
else
{
if ( pAttachment->m_bIsRigid )
{
attachment.type |= IS_RIGID;
}
if ( pAttachment->m_bIsWorldAligned )
{
attachment.flags |= ATTACHMENT_FLAG_WORLD_ALIGN;
}
}
}
// Don't create joints for the the root dag ever.. just deal with the children
int nChildCount = pDag->GetChildCount();
for ( int i = 0; i < nChildCount; ++i )
{
CDmeDag *pChild = pDag->GetChild( i );
if ( !pChild )
continue;
LoadAttachments( pRoot, pChild, pSource, bStaticProp );
}
}
//-----------------------------------------------------------------------------
// Loads the bind pose
//-----------------------------------------------------------------------------
static void LoadBindPose( CDmeModel *pModel, float flScale, const BoneTransformMap_t &boneMap, s_source_t *pSource )
{
s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( pSource, "BindPose" );
pSourceAnim->startframe = 0;
pSourceAnim->endframe = 0;
pSourceAnim->numframes = 1;
// Default all transforms to identity
pSourceAnim->rawanim[0] = (s_bone_t *)calloc( pSource->numbones, sizeof(s_bone_t) );
for ( int i = 0; i < pSource->numbones; ++i )
{
pSourceAnim->rawanim[0][i].pos.Init();
pSourceAnim->rawanim[0][i].rot.Init();
}
{
matrix3x4_t jointTransform;
CDmeTransformList *pBindPose = pModel->FindBaseState( "bind" );
for ( int nMdlBoneIndex = 0; nMdlBoneIndex < boneMap.m_nBoneCount; ++nMdlBoneIndex )
{
CDmeTransform *pDmeTransform = NULL;
const int nDmeModelBoneIndex = boneMap.m_pnMdlToDmeModel[ nMdlBoneIndex ];
if ( nDmeModelBoneIndex < 0 )
{
// No bind pose stored for the specified joint, use the current
// position of the joint from the skeleton instead
pDmeTransform = boneMap.m_ppTransforms[ nMdlBoneIndex ];
}
else
{
pDmeTransform = pBindPose ? pBindPose->GetTransform( nDmeModelBoneIndex ) : pModel->GetJointTransform( nDmeModelBoneIndex );
}
if ( pDmeTransform )
{
pDmeTransform->GetTransform( jointTransform );
s_bone_t &mdlBone = pSourceAnim->rawanim[0][ nMdlBoneIndex ];
MatrixAngles( jointTransform, mdlBone.rot, mdlBone.pos );
mdlBone.pos *= flScale;
}
else
{
MdlWarning( "Cannot find DmeTransform for MDL Bone %d\n", nMdlBoneIndex );
}
}
}
Build_Reference( pSource, "BindPose" );
}
//-----------------------------------------------------------------------------
// Does a search through connection operators for dependent DmeOperators
//-----------------------------------------------------------------------------
static void GetDependentOperators( CUtlVector< IDmeOperator * > &operatorList, CDmeOperator *pDmeOperator )
{
if ( !pDmeOperator || !CastElement< CDmeOperator >( pDmeOperator ) )
return;
for ( int i = 0; i < operatorList.Count(); ++i )
{
CDmeOperator *pTmpDmeOperator = CastElement< CDmeOperator >( reinterpret_cast< CDmeOperator * >( operatorList[i] ) );
if ( pTmpDmeOperator && pTmpDmeOperator == pDmeOperator )
return;
}
operatorList.AddToTail( pDmeOperator );
CUtlVector< CDmAttribute * > outAttrList;
pDmeOperator->GetOutputAttributes( outAttrList );
for ( int i = 0; i < outAttrList.Count(); ++i )
{
CDmElement *pDmElement = outAttrList[i]->GetOwner();
if ( !pDmElement )
continue;
if ( pDmElement == pDmeOperator )
{
CUtlVector< CDmElement * > reList0;
FindReferringElements( reList0, pDmElement, g_pDataModel->GetSymbol( "element" ) );
for ( int j = 0; j < reList0.Count(); ++j )
{
CDmeAttributeReference *pRe0 = CastElement< CDmeAttributeReference >( reList0[j] );
if ( !pRe0 )
continue;
CUtlVector< CDmElement * > reList1;
FindReferringElements( reList1, pRe0, g_pDataModel->GetSymbol( "input" ) );
for ( int k = 0; k < reList1.Count(); ++k )
{
CDmeConnectionOperator *pRe1 = CastElement< CDmeConnectionOperator >( reList1[k] );
if ( !pRe1 )
continue;
GetDependentOperators( operatorList, pRe1 );
}
}
}
else
{
GetDependentOperators( operatorList, CastElement< CDmeOperator >( pDmElement ) );
}
}
}
//-----------------------------------------------------------------------------
// Main entry point for loading DMX files
//-----------------------------------------------------------------------------
static void PrepareChannels(
CUtlVector< IDmeOperator * > &operatorList,
CDmeChannelsClip *pAnimation )
{
int nChannelsCount = pAnimation->m_Channels.Count();
for ( int i = 0; i < nChannelsCount; ++i )
{
pAnimation->m_Channels[i]->SetMode( CM_PLAY );
GetDependentOperators( operatorList, pAnimation->m_Channels[i] );
}
}
//-----------------------------------------------------------------------------
// Update channels so they are in position for the next frame
//-----------------------------------------------------------------------------
static void UpdateChannels( CUtlVector< IDmeOperator * > &operators, CDmeChannelsClip *pAnimation, DmeTime_t clipTime )
{
int nChannelsCount = pAnimation->m_Channels.Count();
DmeTime_t channelTime = pAnimation->ToChildMediaTime( clipTime );
for ( int i = 0; i < nChannelsCount; ++i )
{
pAnimation->m_Channels[i]->SetCurrentTime( channelTime );
}
// Recompute the position of the joints
{
CDisableUndoScopeGuard guard;
g_pDmElementFramework->SetOperators( operators );
g_pDmElementFramework->Operate( true );
}
g_pDmElementFramework->BeginEdit();
}
//-----------------------------------------------------------------------------
// Initialize the pose for this frame
//-----------------------------------------------------------------------------
static void ComputeFramePose( s_sourceanim_t *pSourceAnim, int nFrame, float flScale, BoneTransformMap_t& boneMap )
{
pSourceAnim->rawanim[nFrame] = (s_bone_t *)calloc( boneMap.m_nBoneCount, sizeof( s_bone_t ) );
for ( int i = 0; i < boneMap.m_nBoneCount; ++i )
{
matrix3x4_t jointTransform;
boneMap.m_ppTransforms[i]->GetTransform( jointTransform );
MatrixAngles( jointTransform, pSourceAnim->rawanim[nFrame][i].rot, pSourceAnim->rawanim[nFrame][i].pos );
pSourceAnim->rawanim[nFrame][i].pos *= flScale;
}
}
//-----------------------------------------------------------------------------
// Main entry point for loading animations
//-----------------------------------------------------------------------------
static void LoadAnimations( s_source_t *pSource, CDmeAnimationList *pAnimationList, float flScale, BoneTransformMap_t &boneMap )
{
int nAnimationCount = pAnimationList->GetAnimationCount();
for ( int i = 0; i < nAnimationCount; ++i )
{
CDmeChannelsClip *pAnimation = pAnimationList->GetAnimation( i );
if ( !Q_stricmp( pAnimationList->GetName(), "BindPose" ) )
{
MdlError( "Error: Cannot use \"BindPose\" as an animation name!\n" );
break;
}
s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( pSource, pAnimation->GetName() );
DmeTime_t nStartTime = pAnimation->GetStartTime();
DmeTime_t nEndTime = pAnimation->GetEndTime();
int nFrameRateVal = pAnimation->GetValue<int>( "frameRate" );
if ( nFrameRateVal <= 0 )
{
nFrameRateVal = 30;
}
DmeFramerate_t nFrameRate = nFrameRateVal;
pSourceAnim->startframe = nStartTime.CurrentFrame( nFrameRate );
pSourceAnim->endframe = nEndTime.CurrentFrame( nFrameRate );
pSourceAnim->numframes = pSourceAnim->endframe - pSourceAnim->startframe + 1;
CUtlVector< IDmeOperator * > operatorList;
PrepareChannels( operatorList, pAnimation );
float flOOFrameRate = 1.0f / (float)nFrameRateVal;
int nFrame = 0;
while ( nFrame < pSourceAnim->numframes )
{
int nSecond = nFrame / nFrameRateVal;
int nFraction = nFrame - nSecond * nFrameRateVal;
DmeTime_t t = nStartTime + DmeTime_t( nSecond * 10000 ) + DmeTime_t( (float)nFraction * flOOFrameRate );
UpdateChannels( operatorList, pAnimation, t );
ComputeFramePose( pSourceAnim, nFrame, flScale, boneMap );
++nFrame;
}
}
}
//-----------------------------------------------------------------------------
// Loads the skeletal hierarchy from the game model, returns bone count
//-----------------------------------------------------------------------------
static void AddFlexKeys( CDmeDag *pRoot, CDmeDag *pDag, CDmeCombinationOperator *pComboOp, s_source_t *pSource )
{
CDmeMesh *pMesh = CastElement< CDmeMesh >( pDag->GetShape() );
if ( pMesh && ( pDag != pRoot ) )
{
int nDeltaStateCount = pMesh->DeltaStateCount();
for ( int i = 0; i < nDeltaStateCount; ++i )
{
CDmeVertexDeltaData *pDeltaState = pMesh->GetDeltaState( i );
AddFlexKey( pSource, pComboOp, pDeltaState->GetName() );
}
}
// Don't create joints for the the root dag ever.. just deal with the children
int nChildCount = pDag->GetChildCount();
for ( int i = 0; i < nChildCount; ++i )
{
CDmeDag *pChild = pDag->GetChild( i );
if ( !pChild )
continue;
AddFlexKeys( pRoot, pChild, pComboOp, pSource );
}
}
//-----------------------------------------------------------------------------
// Loads all auxilliary model info:
//
// * Determine original source files used to generate
// the current DMX file and schedule them for processing.
//-----------------------------------------------------------------------------
void LoadModelInfo( CDmElement *pRoot, char const *pFullPath )
{
// Determine original source files and schedule them for processing
if ( CDmElement *pMakeFile = pRoot->GetValueElement< CDmElement >( "makefile" ) )
{
if ( CDmAttribute *pSources = pMakeFile->GetAttribute( "sources" ) )
{
CDmrElementArray< CDmElement > arrSources( pSources );
for ( int kk = 0; kk < arrSources.Count(); ++ kk )
{
if ( CDmElement *pModelSource = arrSources.Element( kk ) )
{
if ( char const *szName = pModelSource->GetName() )
{
ProcessOriginalContentFile( pFullPath, szName );
}
}
}
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool LoadTwistConstraint( CDmElement *pDmElement )
{
CDmeRigTwistConstraintOperator *pDmeTwist = CastElement< CDmeRigTwistConstraintOperator >( pDmElement );
if ( !pDmeTwist )
return false;
if ( g_twistbones.Count() == MAXSTUDIOBONES )
return false;
CDmeDag *pDmeDagParent = pDmeTwist->GetParentTarget();
CDmeDag *pDmeDagChild = pDmeTwist->GetChildTarget();
if ( !pDmeDagParent || !pDmeDagChild )
return false;
CTwistBone &twistBone = g_twistbones[ g_twistbones.AddToTail() ];
twistBone.m_bInverse = pDmeTwist->GetInverse();
twistBone.m_vUpVector = pDmeTwist->GetUpAxis();
V_strncpy( twistBone.m_szParentBoneName, pDmeDagParent->GetName(), ARRAYSIZE( twistBone.m_szParentBoneName ) );
twistBone.m_qBaseRotation = twistBone.m_bInverse ? pDmeTwist->GetParentBindRotation() : pDmeTwist->GetChildBindRotation();
V_strncpy( twistBone.m_szChildBoneName, pDmeDagChild->GetName(), ARRAYSIZE( twistBone.m_szChildBoneName ) );
for ( int i = 0; i < pDmeTwist->SlaveCount(); ++i )
{
CDmeDag *pDmeTwistDag = pDmeTwist->GetSlaveDag( i );
if ( !pDmeTwistDag )
continue;
const char *pszTwistTargetName = pDmeTwistDag->GetName();
if ( !pszTwistTargetName || *pszTwistTargetName == '\0' )
continue;
bool bFound = false;
// Check to see if this target is already the target of a twist bone
for ( int j = 0; !bFound && j < g_twistbones.Count(); ++j )
{
const CTwistBone &tmpTwistBone = g_twistbones[j];
for ( int k = 0; k < tmpTwistBone.m_twistBoneTargets.Count(); ++k )
{
if ( !Q_stricmp( pszTwistTargetName, tmpTwistBone.m_twistBoneTargets[k].m_szBoneName ) )
{
bFound = true;
break;
}
}
}
// A Twist bone is already driving this bone, don't make another
if ( bFound )
continue;
s_constraintbonetarget_t &twistBoneTarget = twistBone.m_twistBoneTargets[ twistBone.m_twistBoneTargets.AddToTail() ];
V_strncpy( twistBoneTarget.m_szBoneName, pDmeTwistDag->GetName(), ARRAYSIZE( twistBoneTarget.m_szBoneName ) );
twistBoneTarget.m_nBone = -1;
twistBoneTarget.m_flWeight = pDmeTwist->GetSlaveWeight( i );
twistBoneTarget.m_vOffset = pDmeTwistDag->GetTransform()->GetPosition();
twistBoneTarget.m_qOffset = pDmeTwist->GetSlaveBindOrientation( i );
}
// No targets, twist constraint is useless
if ( twistBone.m_twistBoneTargets.Count() <= 0 )
{
g_twistbones.RemoveMultipleFromTail( 1 );
return true;
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool LoadBaseConstraintParams( CConstraintBoneBase *pConstraintBone, CDmeRigBaseConstraintOperator *pDmeBaseConstraint )
{
const CDmaElementArray< CDmeConstraintTarget > &constraintTargets = pDmeBaseConstraint->GetTargets();
if ( constraintTargets.Count() <= 0 )
return false;
const Quaternion qRot = Quaternion( g_defaultrotation );
for ( int i = 0; i < constraintTargets.Count(); ++i )
{
CDmeConstraintTarget *pDmeConstraintTarget = constraintTargets[i];
if ( !pDmeConstraintTarget )
continue;
CDmeDag *pDmeTargetDag = pDmeConstraintTarget->GetDag();
if ( !pDmeTargetDag )
continue;
// Load targets
s_constraintbonetarget_t &target = pConstraintBone->m_targets[ pConstraintBone->m_targets.AddToTail() ];
V_strncpy( target.m_szBoneName, pDmeTargetDag->GetName(), ARRAYSIZE( target.m_szBoneName ) );
target.m_nBone = -1;
target.m_flWeight = pDmeConstraintTarget->GetWeight();
target.m_qOffset = pDmeConstraintTarget->GetOrientationOffset();
if ( pDmeBaseConstraint->IsA( CDmeRigPointConstraintOperator::GetStaticTypeSymbol() ) )
{
// Target offsets are in world space for Point constraint
VectorRotate( pDmeConstraintTarget->GetPositionOfffset(), qRot, target.m_vOffset );
}
else
{
target.m_vOffset = pDmeConstraintTarget->GetPositionOfffset();
}
// QuaternionMult( pDmeConstraintTarget->GetOrientationOffset(), qRot, target.m_qOffset );
}
if ( pConstraintBone->m_targets.Count() <= 0 )
return false;
const CDmeConstraintSlave *pDmeConstraintSlave = pDmeBaseConstraint->GetConstraintSlave();
if ( !pDmeConstraintSlave )
return false;
const CDmeDag *pDmeSlaveDag = pDmeBaseConstraint->GetSlave();
if ( !pDmeSlaveDag )
return false;
s_constraintboneslave_t &slave = pConstraintBone->m_slave;
V_strncpy( slave.m_szBoneName, pDmeSlaveDag->GetName(), ARRAYSIZE( slave.m_szBoneName ) );
slave.m_nBone = -1;
slave.m_vBaseTranslate = pDmeConstraintSlave->GetBasePosition();
slave.m_qBaseRotation = pDmeConstraintSlave->GetBaseOrientation();
return true;
}
//-----------------------------------------------------------------------------
// Most constraints don't have any specialized constraint parameters
//-----------------------------------------------------------------------------
template < class S, class T >
static bool LoadSpecializedConstraintParams( S *pDmeConstraint, T *pConstraint )
{
return true;
}
//-----------------------------------------------------------------------------
// Load specialized Aim Constraint parameters
//-----------------------------------------------------------------------------
template <>
static bool LoadSpecializedConstraintParams( CDmeRigAimConstraintOperator *pDmeConstraint, CAimConstraint *pConstraint )
{
const Quaternion qRot = Quaternion( g_defaultrotation );
// Set Aim Constraint Parameters
pConstraint->m_qAimOffset = pDmeConstraint->GetValue< Quaternion >( "aimOffset" );
// Up vectors are specified in world space
VectorRotate( pDmeConstraint->GetValue< Vector >( "upVector" ), qRot, pConstraint->m_vUpVector );
CDmElement *pDmeUpSpaceTarget = pDmeConstraint->GetValueElement< CDmElement >( "upSpaceTarget" );
if ( pDmeUpSpaceTarget )
{
V_strncpy( pConstraint->m_szUpSpaceTargetBone, pDmeUpSpaceTarget->GetName(), ARRAYSIZE( pConstraint->m_szUpSpaceTargetBone ) );
}
pConstraint->m_nUpSpaceTargetBone = -1;
pConstraint->m_nUpType = pDmeConstraint->GetValue< int > ( "upType" );
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static bool FindDuplicateConstraint( const CConstraintBoneBase *pConstraintA )
{
for ( int i = 0; i < g_constraintBones.Count(); ++i )
{
const CConstraintBoneBase *pConstraintB = g_constraintBones[i];
if ( *pConstraintA == *pConstraintB )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
template < class S, class T >
static bool LoadConstraint( CDmElement *pDmElement )
{
if ( g_constraintBones.Count() == MAXSTUDIOBONES )
{
MdlError( "Too Many Constraint Bones, Max: %d\n", MAXSTUDIOBONES );
return false;
}
S *pDmeConstraint = CastElement< S >( pDmElement );
if ( !pDmeConstraint )
return false;
T *pConstraint = new T();
if ( !pConstraint )
return false;
if ( !LoadBaseConstraintParams( pConstraint, pDmeConstraint ) )
{
delete pConstraint;
return false;
}
if ( !LoadSpecializedConstraintParams( pDmeConstraint, pConstraint ) )
{
delete pConstraint;
return false;
}
if ( FindDuplicateConstraint( pConstraint ) )
{
// Delete this
delete pConstraint;
}
else
{
g_constraintBones.AddToTail( pConstraint );
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void LoadConstraints( CDmElement *pDmeRoot )
{
if ( !pDmeRoot )
return;
CDmAttribute *pDmeConstraintsAttr = pDmeRoot->GetAttribute( "constraints", AT_ELEMENT_ARRAY );
if ( !pDmeConstraintsAttr )
return;
CDmrElementArray< CDmElement > constraints( pDmeConstraintsAttr );
for ( int i = 0; i < constraints.Count(); ++i )
{
if ( LoadTwistConstraint( constraints[i] ) )
continue;
if ( LoadConstraint< CDmeRigPointConstraintOperator, CPointConstraint >( constraints[i] ) )
continue;
if ( LoadConstraint< CDmeRigOrientConstraintOperator, COrientConstraint >( constraints[i] ) )
continue;
if ( LoadConstraint< CDmeRigAimConstraintOperator, CAimConstraint >( constraints[i] ) )
continue;
if ( LoadConstraint< CDmeRigParentConstraintOperator, CParentConstraint >( constraints[i] ) )
continue;
Error( "TODO: Support Constraint: %s\n", constraints[i]->GetName() );
}
}
//-----------------------------------------------------------------------------
// Load model and skeleton
//-----------------------------------------------------------------------------
static bool LoadModelAndSkeleton( s_source_t *pSource, BoneTransformMap_t &boneMap, CDmeDag *pSkeleton, CDmeModel *pModel, CDmeCombinationOperator *pCombinationOperator, bool bStaticProp )
{
s_DeltaStates.RemoveAll();
s_Balance.RemoveAll();
s_Speed.RemoveAll();
s_UniqueVertices.RemoveAll();
s_UniqueVerticesMap.RemoveAll();
if ( !pSkeleton )
return false;
// BoneRemap[bone index in file] == bone index in studiomdl
pSource->numbones = LoadSkeleton( pSkeleton, pModel, pSource->localBone, boneMap );
if ( pSource->numbones == 0 )
return false;
g_numfaces = 0;
if ( pModel )
{
if ( pCombinationOperator )
{
pCombinationOperator->GenerateWrinkleDeltas( false );
}
LoadBindPose( pModel, g_currentscale, boneMap, pSource );
if ( !LoadMeshes( pModel, g_currentscale, boneMap.m_pnDmeModelToMdl, pSource ) )
return false;
UnifyIndices( pSource );
BuildVertexAnimations( pSource );
BuildIndividualMeshes( pSource );
}
if ( g_numfaces == 0 && pSource->numbones == 1 && !V_strcmp( pSource->localBone[0].name, "defaultRoot" ) )
{
MdlError( "Error - dmx has no contents: %s\n", pSource->filename );
}
if ( pCombinationOperator )
{
AddFlexKeys( pModel, pModel, pCombinationOperator, pSource );
AddCombination( pSource, pCombinationOperator );
}
LoadAttachments( pSkeleton, pSkeleton, pSource, bStaticProp );
return true;
}
//-----------------------------------------------------------------------------
// Given the s_model_t pointer, finds the index of it in g_model
// Returns -1 if it cannot be found
//-----------------------------------------------------------------------------
static int FindModelIndex( s_model_t *pModel )
{
if ( pModel )
{
for ( int i = 0; i < g_nummodels; ++i )
{
if ( g_model[ i ] == pModel )
return i;
}
MdlWarning( "Cannot Find s_model_t: \"%s\" in g_model\n", pModel->name );
}
return -1;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static bool AddFlexKey(
s_source_t *pSource, s_model_t *pModel,
const char *pszAnimationName,
int nFlexDesc,
int nFlexPair,
EyelidType_t nEyelidType,
float flLowererHeight,
float flNeutralHeight,
float flRaiserHeight,
float flSplit = 0.0f,
float flDecay = 1.0f,
int nFrame = 0 ) // DMX frame is always 0
{
if ( g_numflexkeys >= ARRAYSIZE( g_flexkey ) )
{
MdlError( "Too Many Flex Keys, Cannot Add %s\n", "TODO: FlexKeyName" );
return false;
}
const int nModelIndex = FindModelIndex( pModel );
s_flexkey_t *pFlexKey = &g_flexkey[ g_numflexkeys ];
pFlexKey->source = pSource;
V_strncpy( pFlexKey->animationname, pszAnimationName, sizeof( pFlexKey->animationname ) );
pFlexKey->imodel = nModelIndex;
pFlexKey->flexdesc = nFlexDesc;
pFlexKey->flexpair = nFlexPair;
pFlexKey->split = flSplit;
pFlexKey->decay = flDecay;
pFlexKey->frame = nFrame;
switch ( nEyelidType )
{
case kLowerer:
pFlexKey->target0 = -11;
pFlexKey->target1 = -10;
pFlexKey->target2 = flLowererHeight;
pFlexKey->target3 = flNeutralHeight;
break;
case kNeutral:
pFlexKey->target0 = flLowererHeight;
pFlexKey->target1 = flNeutralHeight;
pFlexKey->target2 = flNeutralHeight;
pFlexKey->target3 = flRaiserHeight;
break;
case kRaiser:
pFlexKey->target0 = flNeutralHeight;
pFlexKey->target1 = flRaiserHeight;
pFlexKey->target2 = 10;
pFlexKey->target3 = 11;
break;
}
// Check for a duplicate
for ( int i = 0; i < g_numflexkeys; ++i )
{
const s_flexkey_t *pTmpFlexKey = &( g_flexkey[ i ] );
if ( !V_stricmp( pFlexKey->animationname, pTmpFlexKey->animationname ) &&
pFlexKey->flexdesc == pTmpFlexKey->flexdesc &&
pFlexKey->flexpair == pTmpFlexKey->flexpair &&
pFlexKey->frame == pTmpFlexKey->frame &&
pFlexKey->target0 == pTmpFlexKey->target0 &&
pFlexKey->target1 == pTmpFlexKey->target1 &&
pFlexKey->target2 == pTmpFlexKey->target2 &&
pFlexKey->target3 == pTmpFlexKey->target3 )
{
// Duplicate, get rid of it
V_memset( pFlexKey, 0, sizeof( s_flexkey_t ) );
return false;
}
}
// Not a duplicate, keep it
++g_numflexkeys;
return true;
}
//-----------------------------------------------------------------------------
// In studiomdl.cpp
//-----------------------------------------------------------------------------
const s_sourceanim_t *GetNewStyleSourceVertexAnim( s_source_t *pSource, const char *pszVertexAnimName );
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static bool LoadEyelid( s_model_t *pModel, CDmeEyelid *pDmeEyelid )
{
if ( !pModel || !pDmeEyelid )
return false;
s_source_t *pSource = pModel->source;
if ( !pSource )
return false;
const bool bUpper = pDmeEyelid->m_bUpper.Get();
enum RightLeftType_t
{
kLeft = 0,
kRight = 1,
kRightLeftTypeCount = 2
};
struct EyelidData_t
{
int m_nFlexDesc[ kRightLeftTypeCount ];
const s_sourceanim_t *m_pSourceAnim;
float m_flTarget;
const char *m_pszSuffix;
};
EyelidData_t eyelidData[3] =
{
{ { -1, -1 }, NULL, 0.0f, "lowerer" },
{ { -1, -1 }, NULL, 0.0f, "neutral" },
{ { -1, -1 }, NULL, 0.0f, "raiser" }
};
eyelidData[kLowerer].m_pSourceAnim = GetNewStyleSourceVertexAnim( pSource, pDmeEyelid->m_sLowererFlex.Get() );
eyelidData[kLowerer].m_flTarget = pDmeEyelid->m_flLowererHeight.Get();
eyelidData[kNeutral].m_pSourceAnim = GetNewStyleSourceVertexAnim( pSource, pDmeEyelid->m_sNeutralFlex.Get() );
eyelidData[kNeutral].m_flTarget = pDmeEyelid->m_flNeutralHeight.Get();
eyelidData[kRaiser].m_pSourceAnim = GetNewStyleSourceVertexAnim( pSource, pDmeEyelid->m_sRaiserFlex.Get() );
eyelidData[kRaiser].m_flTarget = pDmeEyelid->m_flRaiserHeight.Get();
// Add a flexdesc for <type>_right & <type>_left
// Where <type> is "upper" or "lower"
int nRightLeftBaseDesc[kRightLeftTypeCount] = { -1, -1 };
const char *sRightBaseDesc = bUpper ? "upper_right" : "lower_right";
nRightLeftBaseDesc[kRight] = Add_Flexdesc( sRightBaseDesc );
for ( int i = 0; i < kEyelidTypeCount; ++i )
{
CUtlString sRightLocalDesc = sRightBaseDesc;
sRightLocalDesc += "_";
sRightLocalDesc += eyelidData[i].m_pszSuffix;
eyelidData[i].m_nFlexDesc[kRight] = Add_Flexdesc( sRightLocalDesc.Get() );
}
const char *sLeftBaseDesc = bUpper ? "upper_left" : "lower_left";
nRightLeftBaseDesc[kLeft] = Add_Flexdesc( sLeftBaseDesc );
for ( int i = 0; i < kEyelidTypeCount; ++i )
{
CUtlString sLeftLocalDesc = sLeftBaseDesc;
sLeftLocalDesc += "_";
sLeftLocalDesc += eyelidData[i].m_pszSuffix;
eyelidData[i].m_nFlexDesc[kLeft] = Add_Flexdesc( sLeftLocalDesc.Get() );
}
for ( int i = 0; i < kEyelidTypeCount; ++i )
{
if ( !AddFlexKey( pSource, pModel,
eyelidData[i].m_pSourceAnim->animationname,
nRightLeftBaseDesc[kLeft],
nRightLeftBaseDesc[kRight],
EyelidType_t( i ),
eyelidData[kLowerer].m_flTarget,
eyelidData[kNeutral].m_flTarget,
eyelidData[kRaiser].m_flTarget ) )
{
// Flex Keys Already Added - Eyelid is a duplicate
return false;
}
}
bool bRightOk = false;
bool bLeftOk = false;
for ( int i = 0; i < pModel->numeyeballs; ++i )
{
s_eyeball_t *pEyeball = &( pModel->eyeball[i] );
if ( !pEyeball )
continue;
RightLeftType_t nRightLeftIndex = kRight;
if ( !V_stricmp( pDmeEyelid->m_sRightEyeballName.Get(), pEyeball->name ) )
{
nRightLeftIndex = kRight;
bRightOk = true;
}
else if ( !Q_stricmp( pDmeEyelid->m_sLeftEyeballName, pEyeball->name ) )
{
nRightLeftIndex = kLeft;
bLeftOk = true;
}
else
{
MdlWarning( "Unknown Eyeball: %s\n", pEyeball->name );
continue;
}
for ( int j = 0; j < kEyelidTypeCount; ++j )
{
if ( fabs( eyelidData[j].m_flTarget ) > pEyeball->radius )
{
MdlError( "Eyelid \"%s\" %s %.1f out of range (+-%.1f)\n", bUpper ? "upper" : "lower", eyelidData[j].m_pszSuffix, eyelidData[j].m_flTarget, pEyeball->radius );
}
}
if ( bUpper )
{
pEyeball->upperlidflexdesc = nRightLeftBaseDesc[nRightLeftIndex];
for ( int j = 0; j < kEyelidTypeCount; ++j )
{
pEyeball->upperflexdesc[j] = eyelidData[j].m_nFlexDesc[nRightLeftIndex];
pEyeball->uppertarget[j] = eyelidData[j].m_flTarget;
}
}
else
{
pEyeball->lowerlidflexdesc = nRightLeftBaseDesc[nRightLeftIndex];
for ( int j = 0; j < kEyelidTypeCount; ++j )
{
pEyeball->lowerflexdesc[j] = eyelidData[j].m_nFlexDesc[nRightLeftIndex];
pEyeball->lowertarget[j] = eyelidData[j].m_flTarget;
}
}
}
if ( !bRightOk )
{
MdlError( "Could not find right eye \"%s\"\n", pDmeEyelid->m_sRightEyeballName.Get() );
return false;
}
if ( !bLeftOk )
{
MdlError( "Could not find left eye \"%s\"\n", pDmeEyelid->m_sLeftEyeballName.Get() );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static bool LoadMouth( CDmeMouth *pDmeMouth )
{
if ( !pDmeMouth )
return false;
const int nMouthIndex = pDmeMouth->m_nMouthNumber.Get();
// Check if mouth is already defined... not sure why people need to specify a mouth number
// in the QC though.
if ( g_nummouths > nMouthIndex )
return false;
g_nummouths = nMouthIndex + 1;
s_mouth_t *pMouth = &( g_mouth[ nMouthIndex ] );
pMouth->flexdesc = Add_Flexdesc( pDmeMouth->m_sFlexControllerName.Get() );
V_strncpy( pMouth->bonename, pDmeMouth->m_sBoneName, sizeof( pMouth->bonename ) );
pMouth->forward = pDmeMouth->m_vForward.Get(); // TODO: Adjust Y Up?
return false;
}
//-----------------------------------------------------------------------------
// Loads Eyeballs
//-----------------------------------------------------------------------------
static void LoadEyeballs( s_source_t *pSource, s_model_t *pModel, CDmrElementArray< CDmElement > &elementArray )
{
if ( !pSource || !pModel || elementArray.Count() <= 0 )
return;
Assert( pModel->source == NULL || pModel->source == pSource );
matrix3x4_t mDefRot;
AngleMatrix( g_defaultrotation, mDefRot );
Vector vTmp;
for ( int i = 0; i < elementArray.Count(); ++i )
{
CDmeEyeball *pDmeEyeball = CastElement< CDmeEyeball >( elementArray.Element( i ) );
if ( !pDmeEyeball )
continue;
if ( pModel->numeyeballs >= ARRAYSIZE( pModel->eyeball ) )
{
MdlWarning( "1100: Max number of eyeballs reached for model %s, ignoring eyeball %s\n", pModel->name, pDmeEyeball->GetName() );
continue;
}
int nFoundBoneIndex = -1;
for ( int nSearchBoneIndex = 0; nSearchBoneIndex < pSource->numbones; ++nSearchBoneIndex )
{
if ( !Q_stricmp( pSource->localBone[ nSearchBoneIndex ].name, pDmeEyeball->m_sParentBoneName.Get() ) )
{
nFoundBoneIndex = nSearchBoneIndex;
break;
}
}
if ( nFoundBoneIndex < 0 )
{
MdlWarning( "1101: Couldn't find bone %s on model %s, ignoring eyeball %s\n", pDmeEyeball->m_sParentBoneName.Get(), pModel->name, pDmeEyeball->GetName() );
continue;
}
const char *pszMaterialName = pDmeEyeball->m_sMaterialName.Get();
const bool bRelative = ( strchr( pszMaterialName, '/' ) || strchr( pszMaterialName, '\\' ) ) ? true : false;
const int nSearchMeshMatIndex = UseTextureAsMaterial( LookupTexture( pDmeEyeball->m_sMaterialName.Get(), bRelative ) );
int nFoundMeshMatIndex = -1;
for ( int i = 0; i < pSource->nummeshes; ++i )
{
const int nTmpMeshMatIndex = pSource->meshindex[ i ]; // meshes are internally stored by material index
if ( nTmpMeshMatIndex == nSearchMeshMatIndex )
{
nFoundMeshMatIndex = i;
break;
}
}
if ( nFoundMeshMatIndex < 0 )
{
MdlWarning( "1102: Couldn't find eyeball material %s on model %s, ignoring eyeball %s\n", pDmeEyeball->m_sMaterialName.Get(), pModel->name, pDmeEyeball->GetName() );
continue;
}
s_eyeball_t *ps_eyeball_t = &( pModel->eyeball[ pModel->numeyeballs ] );
Q_strncpy( ps_eyeball_t->name, pDmeEyeball->GetName(), sizeof( ps_eyeball_t->name ) );
ps_eyeball_t->bone = nFoundBoneIndex;
ps_eyeball_t->mesh = nFoundMeshMatIndex;
ps_eyeball_t->radius = pDmeEyeball->m_flRadius.Get();
ps_eyeball_t->zoffset = tan( DEG2RAD( pDmeEyeball->m_flYawAngle.Get() ) );
ps_eyeball_t->iris_scale = 1.0f / pDmeEyeball->m_flIrisScale;
// translate eyeball into bone space
VectorITransform( pDmeEyeball->m_vPosition.Get(), pSource->boneToPose[ ps_eyeball_t->bone ], ps_eyeball_t->org );
VectorIRotate( Vector( 0, 0, 1 ), mDefRot, vTmp );
VectorIRotate( vTmp, pSource->boneToPose[ ps_eyeball_t->bone ], ps_eyeball_t->up );
VectorIRotate( Vector( 1, 0, 0 ), mDefRot, vTmp );
VectorIRotate( vTmp, pSource->boneToPose[ ps_eyeball_t->bone ], ps_eyeball_t->forward );
// Not applicable
ps_eyeball_t->upperlidflexdesc = -1;
ps_eyeball_t->lowerlidflexdesc = -1;
bool bOk = true;
// Check for a duplicate eyeball
for ( int j = 0; j < pModel->numeyeballs; ++j )
{
s_eyeball_t *pTmp = &( pModel->eyeball[ j ] );
if ( !V_stricmp( ps_eyeball_t->name, pTmp->name ) )
{
// TODO: Check and warn about duplicate eyeballs with mis-matched parameters
bOk = false;
break;
}
}
if ( bOk )
{
// Keep eyeball
pModel->numeyeballs += 1;
}
else
{
// Clear data
V_memset( ps_eyeball_t, 0, sizeof( s_eyeball_t ) );
}
}
// Make the standard flex controllers for eyes if required
static const char *szEyesFlexControllers[] = {
"eyes_updown",
"eyes_rightleft"
};
for ( int nNewFlexIndex = 0; nNewFlexIndex < ARRAYSIZE( szEyesFlexControllers ); ++nNewFlexIndex )
{
bool bHasEyeFlexController = false;
for ( int nFlexIndex = 0; nFlexIndex < g_numflexcontrollers; ++nFlexIndex )
{
if ( !Q_strcmp( szEyesFlexControllers[ nNewFlexIndex ], g_flexcontroller[ nFlexIndex ].name ) )
{
bHasEyeFlexController = true;
break;
}
}
// The flex controller range for eyes_updown & eyes_rightleft is default [-45, 45] because it's clamped by eyesMaxDeflection
// and changing it based on max deflection would cause animatin changes since flex controller values are normalized [0, 1]
// [-45, 45 ] gives a maxium range that's useful
if ( !bHasEyeFlexController )
{
if ( g_numflexcontrollers >= MAXSTUDIOFLEXCTRL )
{
MdlWarning( "1103: Couldn't make eyes flexcontroller %s, too many flex controllers defined\n", szEyesFlexControllers[ nNewFlexIndex ] );
continue;
}
Q_strncpy( g_flexcontroller[g_numflexcontrollers].name, szEyesFlexControllers[ nNewFlexIndex ], sizeof( g_flexcontroller[ g_numflexcontrollers ].name ) );
Q_strncpy( g_flexcontroller[g_numflexcontrollers].type, "eyes", sizeof( g_flexcontroller[ g_numflexcontrollers ].name ) );
g_flexcontroller[g_numflexcontrollers].min = -45.0f;
g_flexcontroller[g_numflexcontrollers].max = 45.0f;
++g_numflexcontrollers;
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void LoadQcModelElements( s_source_t *pSource, s_model_t *pModel, CDmeModel *pDmeModel )
{
if ( !pModel || !pDmeModel )
return;
CDmAttribute *pQcModelElementsAttr = pDmeModel->GetAttribute( "qcModelElements", AT_ELEMENT_ARRAY );
if ( !pQcModelElementsAttr )
return;
CDmrElementArray< CDmElement > qcModelElements( pQcModelElementsAttr );
LoadEyeballs( pSource, pModel, qcModelElements );
for ( int i = 0; i < qcModelElements.Count(); ++i )
{
LoadEyelid( pModel, CastElement< CDmeEyelid >( qcModelElements.Element( i ) ) );
}
for ( int i = 0; i < qcModelElements.Count(); ++i )
{
LoadMouth( CastElement< CDmeMouth >( qcModelElements.Element( i ) ) );
}
}
#ifdef MDLCOMPILE
//-----------------------------------------------------------------------------
// Allocates a source
// Applies the .dmx extension because the source searching algorithm looks
// for .dmx
//-----------------------------------------------------------------------------
static s_source_t *AllocateDmxSource( const char *pSourceName )
{
// Allocate a new source
s_source_t *pSource = (s_source_t *)calloc( 1, sizeof( s_source_t ) );
g_source[g_numsources++] = pSource;
Q_strncpy( pSource->filename, pSourceName, sizeof( pSource->filename ) );
Q_SetExtension( pSource->filename, "dmx", sizeof( pSource->filename ) );
VectorCopy( g_defaultadjust, pSource->adjust );
pSource->scale = 1.0f;
pSource->rotation = g_defaultrotation;
return pSource;
}
//-----------------------------------------------------------------------------
// Purpose: Creates an animation from the Dme skeleton specified in the
// bone map. NOTE: It doesn't look for 'bindPose', it uses the
// current pose of the skeleton
//-----------------------------------------------------------------------------
static void CreateAnimFromSkeleton( s_source_t *pSource, const char *pSequenceName, const BoneTransformMap_t &boneMap )
{
s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( pSource, pSequenceName );
pSourceAnim->startframe = 0;
pSourceAnim->endframe = 0;
pSourceAnim->numframes = 1;
// Default all transforms to identity
pSourceAnim->rawanim[0] = (s_bone_t *)calloc( pSource->numbones, sizeof(s_bone_t) );
for ( int i = 0; i < pSource->numbones; ++i )
{
pSourceAnim->rawanim[0][i].pos.Init();
pSourceAnim->rawanim[0][i].rot.Init();
}
matrix3x4_t jointTransform;
for ( int nBoneIndex = 0; nBoneIndex < boneMap.m_nBoneCount; ++nBoneIndex )
{
const CDmeTransform *pDmeTransform = boneMap.m_ppTransforms[ nBoneIndex ];
s_bone_t &mdlBone = pSourceAnim->rawanim[0][ nBoneIndex ];
VectorCopy( pDmeTransform->GetPosition() * g_currentscale, mdlBone.pos );
QuaternionAngles( pDmeTransform->GetOrientation(), mdlBone.rot );
}
// This copies the named animation into the poseToBone array in the source
Build_Reference( pSource, pSequenceName );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadBoneMaskList( CDmeBoneMaskList *pDmeBoneMaskList )
{
if ( !pDmeBoneMaskList )
return;
CDmeBoneMask *pDmeDefaultBoneMask = pDmeBoneMaskList->m_eDefaultBoneMask.GetElement();
for ( int i = 0; i < pDmeBoneMaskList->m_BoneMaskList.Count(); ++i )
{
CDmeBoneMask *pDmeBoneMask = pDmeBoneMaskList->m_BoneMaskList.Element( i );
if ( !pDmeBoneMask )
return;
int nBoneMaskIndex = g_numweightlist;
if ( pDmeBoneMask == pDmeDefaultBoneMask )
{
nBoneMaskIndex = 0;
}
if ( nBoneMaskIndex >= MAXWEIGHTLISTS )
{
MdlWarning( "1300: Too many bone masks, max %d, ignoring %s\n", MAXWEIGHTLISTS, pDmeBoneMask->GetName() );
}
bool bDuplicate = false;
for ( int j = 0; j < g_numweightlist; ++j )
{
if ( g_weightlist[ j ].name && !Q_stricmp( g_weightlist[ j ].name, pDmeBoneMask->GetName() ) )
{
bDuplicate = true;
MdlWarning( "1301: Ignoring duplicate bone mask %s\n", pDmeBoneMask->GetName() );
break;
}
}
if ( bDuplicate )
continue;
s_weightlist_t *pWeightList = &( g_weightlist[ nBoneMaskIndex ] );
Q_strncpy( pWeightList->name, pDmeBoneMask->GetName(), sizeof( pWeightList->name ) );
pWeightList->numbones = 0;
for ( int j = 0; j < pDmeBoneMask->m_BoneWeights.Count(); ++j )
{
CDmeBoneWeight *pDmeBoneWeight = pDmeBoneMask->m_BoneWeights.Element( j );
if ( !pDmeBoneWeight )
continue;
int nBoneWeightIndex = pWeightList->numbones;
if ( nBoneWeightIndex >= MAXWEIGHTSPERLIST )
{
MdlWarning( "1302: Too many bones in weightlist %s, ignoring weight for %s (%f)\n", pWeightList->name, pDmeBoneWeight->GetName(), pDmeBoneWeight->m_flWeight.Get() );
continue;
}
pWeightList->bonename[ nBoneWeightIndex ] = strdup( pDmeBoneWeight->GetName() );
// 'weight' attribute is normally used for both rotation and position weight
pWeightList->boneweight[ nBoneWeightIndex ] = pDmeBoneWeight->m_flWeight.Get();
pWeightList->boneposweight[ nBoneWeightIndex ] = pWeightList->boneweight[ nBoneWeightIndex ];
// Look for optional 'positionWeight' attribute, use it separately for position if it exists
CDmAttribute *pDmePositionWeightAttr = pDmeBoneWeight->GetAttribute( "positionWeight", AT_FLOAT );
if ( pDmePositionWeightAttr )
{
pWeightList->boneposweight[ nBoneWeightIndex ] = pDmePositionWeightAttr->GetValue< float >();
if ( pWeightList->boneweight[ nBoneWeightIndex ] == 0 && pWeightList->boneposweight[ nBoneWeightIndex ] > 0 )
{
MdlWarning( "1303: Non-zero position weight with zero rotation weight not allowed for bone weight list %s:%s P: %f R: %f, ignoring position weight\n",
pWeightList->name, pWeightList->bonename[ nBoneWeightIndex ], pWeightList->boneposweight[ nBoneWeightIndex ], pWeightList->boneweight[ nBoneWeightIndex ] );
pWeightList->boneposweight[ nBoneWeightIndex ] = pWeightList->boneweight[ nBoneWeightIndex ];
}
}
++pWeightList->numbones;
}
if ( nBoneMaskIndex != 0 )
{
++g_numweightlist;
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadPoseParameterList( CDmePoseParameterList *pDmePoseParameterList )
{
if ( !pDmePoseParameterList )
return;
const int nPoseParameterCount = pDmePoseParameterList->m_ePoseParameterList.Count();
for ( int i = 0; i < nPoseParameterCount; ++i )
{
const CDmePoseParameter *pDmePoseParameter = pDmePoseParameterList->m_ePoseParameterList[ i ];
if ( g_numposeparameters >= MAXSTUDIOPOSEPARAM )
{
MdlWarning( "1900: Too many pose parameters, ignoring from %s\n", pDmePoseParameter->GetName() );
}
// This is like FindOrCreatePoseParamater, name is copied into g_pose[].name
const int nPoseParameterIndex = LookupPoseParameter( pDmePoseParameter->GetName() );
s_poseparameter_t *pPoseParameter = &g_pose[ nPoseParameterIndex ];
Q_strncpy( pPoseParameter->name, pDmePoseParameter->GetName(), sizeof( pPoseParameter->name ) );
pPoseParameter->min = pDmePoseParameter->m_flMin;
pPoseParameter->max = pDmePoseParameter->m_flMax;
if ( pDmePoseParameter->m_bWrap )
{
pPoseParameter->flags |= STUDIO_LOOPING;
pPoseParameter->loop = pPoseParameter->max - pPoseParameter->min;
}
else if ( pDmePoseParameter->m_bLoop )
{
pPoseParameter->flags |= STUDIO_LOOPING;
pPoseParameter->loop = pDmePoseParameter->m_flLoopRange;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Load dmeSequence.ikChainList into the sequence
//-----------------------------------------------------------------------------
static void LoadIkChainList( CDmeSequenceList *pDmeSequenceList )
{
if ( !pDmeSequenceList )
return;
const int nIkChainCount = pDmeSequenceList->m_eIkChainList.Count();
for ( int i = 0; i < nIkChainCount; ++i )
{
CDmeIkChain *pDmeIkChain = pDmeSequenceList->m_eIkChainList[ i ];
if ( !pDmeIkChain )
continue;
const char *pIkChainName = pDmeIkChain->GetName();
// Check for duplicates
int j;
for ( j = 0; j < g_numikchains; ++j )
{
if ( !Q_stricmp( pIkChainName, g_ikchain[ j ].name ) )
break;
}
if ( j < g_numikchains )
{
if ( !g_quiet )
{
MdlWarning( "1401: Duplicate IkChain: %s Ignored\n", pIkChainName );
}
}
// Set defaults
g_ikchain[g_numikchains].axis = STUDIO_Z; // Not actually used
g_ikchain[g_numikchains].value = 0.0; // Not actually used
g_ikchain[g_numikchains].height = 18.0;
g_ikchain[g_numikchains].floor = 0.0;
g_ikchain[g_numikchains].radius = 0.0;
Q_strncpy( g_ikchain[ g_numikchains ].name, pIkChainName, sizeof( g_ikchain[ g_numikchains ].name ) );
Q_strncpy( g_ikchain[ g_numikchains ].bonename, pDmeIkChain->m_sEndJoint, sizeof( g_ikchain[ g_numikchains ].bonename ) );
g_ikchain[ g_numikchains ].height = pDmeIkChain->m_flHeight;
g_ikchain[ g_numikchains ].floor = pDmeIkChain->m_flFloor;
g_ikchain[ g_numikchains ].radius = pDmeIkChain->m_flPad / 2.0f;
g_ikchain[ g_numikchains ].link[0].kneeDir = pDmeIkChain->m_vKnee;
g_ikchain[ g_numikchains ].center = pDmeIkChain->m_vCenter;
++g_numikchains;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void LoadAnimationOptions( CDmeSequence *pDmeSimpleSequence, s_animation_t *pAnimation )
{
if ( !pAnimation || !pDmeSimpleSequence )
return;
pAnimation->fps = pDmeSimpleSequence->m_flFPS;
pAnimation->adjust = pDmeSimpleSequence->m_vOrigin;
// Right now don't include rotation, but if added, likely combine it with the
// default rotation which is already in pAnimation->rotation
// QuaternionAngles( pDmeSequence->m_qRotation, pAnimation->rotation );
pAnimation->scale = pDmeSimpleSequence->m_flScale;
pAnimation->looprestart = pDmeSimpleSequence->m_nStartLoop;
if ( pDmeSimpleSequence->m_bLoop )
{
pAnimation->flags |= STUDIO_LOOPING;
}
// This should be removed and handled by assemble, i.e. STUDIO_NOFORCELOOP should always be set
if ( !pDmeSimpleSequence->m_bForceLoop )
{
pAnimation->flags |= STUDIO_NOFORCELOOP;
}
if ( pDmeSimpleSequence->m_bSnap )
{
pAnimation->flags |= STUDIO_SNAP;
}
if ( pDmeSimpleSequence->m_bPost )
{
pAnimation->flags |= STUDIO_POST;
}
if ( pDmeSimpleSequence->m_bAutoIk )
{
pAnimation->noAutoIK = false;
}
else
{
pAnimation->noAutoIK = true;
}
pAnimation->motionrollback = pDmeSimpleSequence->m_flMotionRollback;
if ( pDmeSimpleSequence->m_bAnimBlocks )
{
pAnimation->disableAnimblocks = false;
}
else
{
pAnimation->disableAnimblocks = true;
}
if ( pDmeSimpleSequence->m_bAnimBlockStall )
{
pAnimation->isFirstSectionLocal = false;
}
else
{
pAnimation->isFirstSectionLocal = true;
}
pAnimation->motiontype = pDmeSimpleSequence->m_eMotionControl.GetElement()->GetStudioMotionControl();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int FindWeightList( const char *pWeightListName )
{
for ( int i = 0; i < g_numweightlist; ++i )
{
if ( !Q_stricmp( pWeightListName, g_weightlist[ i ].name ) )
return i;
}
return -1;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool ValidateControlType( int nControlType, const char *pszTypeString, const char *pszNameString )
{
switch ( nControlType )
{
case STUDIO_X:
case STUDIO_Y:
case STUDIO_Z:
case STUDIO_XR:
case STUDIO_YR:
case STUDIO_ZR:
case STUDIO_LX:
case STUDIO_LY:
case STUDIO_LZ:
case STUDIO_LXR:
case STUDIO_LYR:
case STUDIO_LZR:
case STUDIO_LINEAR:
case STUDIO_QUADRATIC_MOTION:
return true;
// OK!
break;
default:
MdlWarning( "1605: Unknown controlType \"0x%04x\" specified for %s:%s\n",
nControlType,
pszTypeString,
pszNameString );
}
return false;
}
//-----------------------------------------------------------------------------
//
// Handles
//
// alignto
// align
// alignboneto
// alignbone
//
//-----------------------------------------------------------------------------
bool HandleDmeAnimCmdAlign( s_animcmd_t *pAnimCmd, CDmeAnimCmd *pDmeAnimCmd )
{
if ( !pDmeAnimCmd )
return false;
CDmeAnimCmdAlign *pDmeAnimCmdAlign = CastElement< CDmeAnimCmdAlign >( pDmeAnimCmd );
if ( !pDmeAnimCmdAlign )
return false;
CDmeSequenceBase *pDmeSequenceBase = pDmeAnimCmdAlign->m_eAnimation.GetElement();
if ( !pDmeSequenceBase )
{
MdlWarning( "1608: No DmeSequence specified for %s:%s\n",
pDmeAnimCmdAlign->GetTypeString(),
pDmeAnimCmd->GetName() );
return false;
}
s_animation_t *pExtAnim = LookupAnimation( pDmeSequenceBase->GetName() );
if ( !pExtAnim )
{
MdlWarning( "1604: Unknown animation \"%s\" specified for %s:%s\n",
pDmeSequenceBase->GetName(),
pDmeAnimCmdAlign->GetTypeString(),
pDmeAnimCmd->GetName() );
return false;
}
pAnimCmd->cmd = CMD_AO;
pAnimCmd->u.ao.ref = pExtAnim;
pAnimCmd->u.ao.pBonename = pDmeAnimCmdAlign->m_sBoneName.IsEmpty() ? NULL : strdup( pDmeAnimCmdAlign->m_sBoneName );
pAnimCmd->u.ao.srcframe = pDmeAnimCmdAlign->m_nSourceFrame;
pAnimCmd->u.ao.destframe = pDmeAnimCmdAlign->m_nDestinatonFrame;
pAnimCmd->u.ao.motiontype = pDmeAnimCmdAlign->m_eMotionControl.GetElement()->GetStudioMotionControl();
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void LoadAnimationCommands( CDmeSequence *pDmeSimpleSequence, s_animation_t *pAnimation )
{
if ( !pDmeSimpleSequence || !pAnimation )
return;
const int nAnimCmdCount = pDmeSimpleSequence->m_eAnimationCommandList.Count();
for ( int i = 0; i < nAnimCmdCount; ++i )
{
CDmeAnimCmd *pDmeAnimCmd = pDmeSimpleSequence->m_eAnimationCommandList[ i ];
if ( !pDmeAnimCmd )
continue;
if ( pAnimation->numcmds >= MAXSTUDIOCMDS )
{
MdlWarning( "1600: Too many animation commands for anim: %s, ignoring from %d:%s\n", pAnimation->name, i, pDmeAnimCmd->GetName() );
return;
}
s_animcmd_t *pAnimCmd = &pAnimation->cmds[ pAnimation->numcmds ];
if ( pDmeAnimCmd->IsA( CDmeAnimCmdFixupLoop::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdFixupLoop *pDmeAnimCmdFixupLoop = CastElement< CDmeAnimCmdFixupLoop >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_FIXUP;
pAnimCmd->u.fixuploop.start = pDmeAnimCmdFixupLoop->m_nStartFrame;
pAnimCmd->u.fixuploop.end = pDmeAnimCmdFixupLoop->m_nEndFrame;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdWeightList::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdWeightList *pDmeAnimCmdWeightList = CastElement< CDmeAnimCmdWeightList >( pDmeAnimCmd );
const int nWeightListIndex = FindWeightList( pDmeAnimCmdWeightList->m_sWeightListName );
if ( nWeightListIndex < 0 )
{
MdlWarning( "1602: Unknown weightList \"%s\" specified for %s animation command %s.%s[%d] %s\n",
pDmeAnimCmdWeightList->m_sWeightListName,
pDmeAnimCmdWeightList->GetTypeString(),
pDmeSimpleSequence->GetName(), pDmeSimpleSequence->m_eAnimationCommandList.GetAttribute()->GetName(), i,
pDmeAnimCmd->GetName() );
continue;
}
else
{
pAnimCmd->cmd = CMD_WEIGHTS;
pAnimCmd->u.weightlist.index = nWeightListIndex;
}
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdSubtract::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdSubtract *pDmeAnimCmdSubtract = CastElement< CDmeAnimCmdSubtract >( pDmeAnimCmd );
CDmeSequenceBase *pDmeSequenceBase = pDmeAnimCmdSubtract->m_eAnimation.GetElement();
if ( pDmeSequenceBase )
{
s_animation_t *pExtAnim = LookupAnimation( pDmeSequenceBase->GetName() );
if ( pExtAnim )
{
pAnimCmd->cmd = CMD_SUBTRACT;
pAnimCmd->u.subtract.ref = pExtAnim;
pAnimCmd->u.subtract.frame = pDmeAnimCmdSubtract->m_nFrame;
if ( !pDmeAnimCmd->IsA( CDmeAnimCmdPreSubtract::GetStaticTypeSymbol() ) )
{
pAnimCmd->u.subtract.flags |= STUDIO_POST;
}
}
else
{
MdlWarning( "1603: Unknown animation \"%s\" specified for %s %s.%s[%d] %s\n",
pDmeSequenceBase->GetName(),
pDmeAnimCmdSubtract->GetTypeString(),
pDmeSimpleSequence->GetName(), pDmeSimpleSequence->m_eAnimationCommandList.GetAttribute()->GetName(), i,
pDmeAnimCmd->GetName() );
continue;
}
}
else
{
MdlWarning( "1607: No DmeSequenceBase specified for %s %s.%s[%d] %s\n",
pDmeAnimCmdSubtract->GetTypeString(),
pDmeSimpleSequence->GetName(), pDmeSimpleSequence->m_eAnimationCommandList.GetAttribute()->GetName(), i,
pDmeAnimCmd->GetName() );
continue;
}
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdAlign::GetStaticTypeSymbol() ) )
{
if ( !HandleDmeAnimCmdAlign( pAnimCmd, pDmeAnimCmd ) )
continue;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdRotateTo::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdRotateTo *pDmeAnimCmdRotateTo = CastElement< CDmeAnimCmdRotateTo >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_ANGLE;
pAnimCmd->u.angle.angle = pDmeAnimCmdRotateTo->m_flAngle;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdWalkFrame::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdWalkFrame *pDmeAnimCmdWalkFrame = CastElement< CDmeAnimCmdWalkFrame >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_MOTION;
pAnimCmd->u.motion.iEndFrame = pDmeAnimCmdWalkFrame->m_nEndFrame;
pAnimCmd->u.motion.motiontype = pDmeAnimCmdWalkFrame->m_eMotionControl.GetElement()->GetStudioMotionControl();
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdDerivative::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdDerivative *pDmeAnimCmdDerivative = CastElement< CDmeAnimCmdDerivative >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_DERIVATIVE;
pAnimCmd->u.derivative.scale = pDmeAnimCmdDerivative->m_flScale;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdDerivative::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdDerivative *pDmeAnimCmdDerivative = CastElement< CDmeAnimCmdDerivative >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_DERIVATIVE;
pAnimCmd->u.derivative.scale = pDmeAnimCmdDerivative->m_flScale;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdLinearDelta::GetStaticTypeSymbol() ) )
{
pAnimCmd->cmd = CMD_LINEARDELTA;
pAnimCmd->u.linear.flags |= STUDIO_AL_POST;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdSplineDelta::GetStaticTypeSymbol() ) )
{
pAnimCmd->cmd = CMD_LINEARDELTA;
pAnimCmd->u.linear.flags |= STUDIO_AL_POST;
pAnimCmd->u.linear.flags |= STUDIO_AL_SPLINE;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdCompress::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdCompress *pDmeAnimCmdCompress = CastElement< CDmeAnimCmdCompress >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_COMPRESS;
pAnimCmd->u.compress.frames = pDmeAnimCmdCompress->m_nSkipFrames;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdNumFrames::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdNumFrames *pDmeAnimCmdNumFrames = CastElement< CDmeAnimCmdNumFrames >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_NUMFRAMES;
pAnimCmd->u.compress.frames = pDmeAnimCmdNumFrames->m_nFrames;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdLocalHierarchy::GetStaticTypeSymbol() ) )
{
CDmeAnimCmdLocalHierarchy *pDmeAnimCmdLocalHierarchy = CastElement< CDmeAnimCmdLocalHierarchy >( pDmeAnimCmd );
pAnimCmd->cmd = CMD_LOCALHIERARCHY;
pAnimCmd->u.localhierarchy.pBonename = strdup( pDmeAnimCmdLocalHierarchy->m_sBoneName.Get() );
pAnimCmd->u.localhierarchy.pParentname = strdup( pDmeAnimCmdLocalHierarchy->m_sParentBoneName.Get() );
pAnimCmd->u.localhierarchy.start = pDmeAnimCmdLocalHierarchy->m_flStartFrame;
pAnimCmd->u.localhierarchy.peak = pDmeAnimCmdLocalHierarchy->m_flPeakFrame;
pAnimCmd->u.localhierarchy.tail = pDmeAnimCmdLocalHierarchy->m_flTailFrame;
pAnimCmd->u.localhierarchy.end = pDmeAnimCmdLocalHierarchy->m_flEndFrame;
}
else if ( pDmeAnimCmd->IsA( CDmeAnimCmdNoAnimation::GetStaticTypeSymbol() ) )
{
pAnimCmd->cmd = CMD_NOANIMATION;
}
else
{
MdlWarning( "1601: Unhandled DmeAnimCmd %s (%s)\n", pDmeAnimCmd->GetTypeString(), pDmeAnimCmd->GetName() );
continue;
}
++pAnimation->numcmds;
}
}
//-----------------------------------------------------------------------------
// TODO: Rework to handle all animation related commands
//-----------------------------------------------------------------------------
void LoadIkRuleList( CDmeSequence *pDmeSimpleSequence, s_animation_t *pAnimation )
{
if ( !pDmeSimpleSequence )
return;
matrix3x4_t mDefRot;
AngleMatrix( g_defaultrotation, mDefRot );
// See if we have any IkRules to load
const int nIkRuleCount = pDmeSimpleSequence->m_eIkRuleList.Count();
if ( nIkRuleCount <= 0 )
return;
for ( int i = 0; i < nIkRuleCount; ++i )
{
CDmeIkRule *pDmeIkRule = pDmeSimpleSequence->m_eIkRuleList[ i ];
if ( !pDmeIkRule )
continue;
if ( pAnimation->numcmds >= MAXSTUDIOCMDS )
{
// TODO: Issue a warning
MdlWarning( "1503: Too many animation commands for anim: %s, ignoring from %s:%s\n",
pAnimation->name, pDmeIkRule->GetTypeString(), pDmeIkRule->GetName() );
return;
}
// Find the IkChain
CDmeIkChain *pDmeIkChain = pDmeIkRule->m_eIkChain.GetElement();
if ( !pDmeIkChain )
{
MdlError( "1501: No IkChain element assigned to %s:%s\n", pDmeIkRule->GetTypeString(), pDmeIkRule->GetName() );
continue;
}
int nIkChainIndex = -1;
for ( int j = 0; j < g_numikchains; ++j )
{
if ( !Q_stricmp( pDmeIkChain->GetName(), g_ikchain[ j ].name ) )
{
nIkChainIndex = j;
break;
}
}
if ( nIkChainIndex < 0 )
{
MdlWarning( "1504: Cannot find IkChain referenced by IkRule %s:%s, ignoring\n", pDmeIkRule->GetName(), pDmeIkRule->GetTypeString() );
continue;
}
// calloc sets memory to 0
s_ikrule_t *pIkRule = reinterpret_cast< s_ikrule_t * >( calloc( 1, sizeof( s_ikrule_t ) ) );
if ( !pIkRule )
{
MdlWarning( "1502: Cannot allocate memory for IkRule %s:%s, ignoring\n", pDmeIkRule->GetName(), pDmeIkRule->GetTypeString() );
return;
}
pIkRule->chain = nIkChainIndex;
pIkRule->slot = nIkChainIndex; // Default slot
CDmeIkTouchRule *pDmeIkTouchRule = CastElement< CDmeIkTouchRule >( pDmeIkRule );
CDmeIkFootstepRule *pDmeIkFootstepRule = CastElement< CDmeIkFootstepRule >( pDmeIkRule );
CDmeIkAttachmentRule *pDmeIkAttachmentRule = CastElement< CDmeIkAttachmentRule >( pDmeIkRule );
CDmeIkReleaseRule *pDmeIkReleaseRule = CastElement< CDmeIkReleaseRule >( pDmeIkRule );
if ( pDmeIkTouchRule )
{
pIkRule->type = IK_SELF;
Q_strncpy( pIkRule->bonename, pDmeIkTouchRule->m_sBoneName, sizeof( pIkRule->bonename ) );
}
else if ( pDmeIkFootstepRule )
{
pIkRule->type = IK_GROUND;
pIkRule->height = pDmeIkFootstepRule->GetValue< float >( "height", g_ikchain[ pIkRule->chain ].height );
pIkRule->floor = pDmeIkFootstepRule->GetValue< float >( "floor", g_ikchain[ pIkRule->chain ].floor );
pIkRule->radius = pDmeIkFootstepRule->GetValue< float >( "pad", g_ikchain[ pIkRule->chain ].radius * 2.0f ) / 2.0f;
if ( pDmeIkFootstepRule->HasAttribute( "contact", AT_INT ) )
{
pIkRule->contact = pDmeIkFootstepRule->GetValue< int >( "contact" );
}
}
else if ( pDmeIkAttachmentRule )
{
pIkRule->type = IK_ATTACHMENT;
Q_strncpy( pIkRule->attachment, pDmeIkAttachmentRule->m_sAttachmentName, sizeof( pIkRule->attachment ) );
if ( pDmeIkAttachmentRule->HasAttribute( "fallbackBone", AT_STRING ) )
{
Q_strncpy( pIkRule->bonename, pDmeIkAttachmentRule->GetValueString( "fallbackBone" ), sizeof( pIkRule->bonename ) );
}
if ( pDmeIkAttachmentRule->HasAttribute( "fallbackPosition", AT_VECTOR3 ) )
{
VectorIRotate( pDmeIkAttachmentRule->GetValue< Vector >( "fallbackPosition" ), mDefRot, pIkRule->pos );
pIkRule->bone = -1;
}
if ( pDmeIkAttachmentRule->HasAttribute( "fallbackRotation", AT_QUATERNION ) )
{
// TODO: Adjust rotation as above for position adjustment?
pIkRule->q = pDmeIkAttachmentRule->GetValue< Quaternion >( "fallbackRotation" );
pIkRule->bone = -1;
}
}
else if ( pDmeIkReleaseRule )
{
pIkRule->type = IK_RELEASE;
}
else
{
MdlWarning( "1500: Unknown IkRuleType %s:%s, ignoring\n", pDmeIkRule->GetName(), pDmeIkRule->GetTypeString() );
continue;
}
switch ( pDmeIkRule->m_nUseType )
{
case CDmeIkRule::USE_SEQUENCE:
pIkRule->usesequence = true;
pIkRule->usesource = false;
break;
case CDmeIkRule::USE_SOURCE:
pIkRule->usesequence = false;
pIkRule->usesource = true;
break;
case CDmeIkRule::USE_NONE:
default:
// Nothing
break;
}
CDmeIkRange *pDmeIkRange = pDmeIkRule->m_eRange.GetElement();
if ( pDmeIkRange )
{
pIkRule->start = pDmeIkRange->m_nStartFrame;
pIkRule->peak = pDmeIkRange->m_nMaxStartFrame;
pIkRule->tail = pDmeIkRange->m_nMaxEndFrame;
pIkRule->end = pDmeIkRange->m_nEndFrame;
}
s_animcmd_t *pAnimCmd = &( pAnimation->cmds[ pAnimation->numcmds ] );
pAnimCmd->cmd = CMD_IKRULE;
pAnimCmd->u.ikrule.pRule = pIkRule;
pAnimation->numcmds++;
}
}
//-----------------------------------------------------------------------------
// Purpose: Loads all of the AddLayer/BlendLayer for the specified sequence
//-----------------------------------------------------------------------------
void LoadLayerList( CDmeSequenceBase *pDmeSequenceBase, s_sequence_t *pSequence )
{
if ( !pDmeSequenceBase || !pSequence )
return;
const int nLayerCount = pDmeSequenceBase->m_eLayerList.Count();
for ( int i = 0; i < nLayerCount; ++i )
{
CDmeSequenceLayerBase *pDmeLayerBase = pDmeSequenceBase->m_eLayerList[ i ];
if ( !pDmeLayerBase )
continue;
CDmeSequenceBase *pDmeRefSeq = pDmeLayerBase->m_eAnimation.GetElement();
if ( !pDmeRefSeq )
{
// TODO: Warning message - No sequence specified
continue;
}
if ( pSequence->numautolayers >= ARRAYSIZE( pSequence->autolayer ) )
{
// TODO: Warning message - Too many layers
break;
}
Q_strncpy( pSequence->autolayer[ pSequence->numautolayers ].name, pDmeRefSeq->GetName(), sizeof( pSequence->autolayer[ pSequence->numautolayers ].name ) );
CDmeSequenceAddLayer *pDmeAddLayer = CastElement< CDmeSequenceAddLayer >( pDmeSequenceBase->m_eLayerList[ i ] );
if ( pDmeAddLayer )
{
// Nothing to do
}
CDmeSequenceBlendLayer *pDmeBlendLayer = CastElement< CDmeSequenceBlendLayer >( pDmeSequenceBase->m_eLayerList[ i ] );
if ( pDmeBlendLayer )
{
pSequence->autolayer[ pSequence->numautolayers ].start = pDmeBlendLayer->m_flStartFrame;
pSequence->autolayer[ pSequence->numautolayers ].peak = pDmeBlendLayer->m_flPeakFrame;
pSequence->autolayer[ pSequence->numautolayers ].tail = pDmeBlendLayer->m_flTailFrame;
pSequence->autolayer[ pSequence->numautolayers ].end = pDmeBlendLayer->m_flEndFrame;
pSequence->autolayer[ pSequence->numautolayers ].flags |= pDmeBlendLayer->m_bCrossfade ? STUDIO_AL_XFADE : 0;
pSequence->autolayer[ pSequence->numautolayers ].flags |= pDmeBlendLayer->m_bSpline ? STUDIO_AL_SPLINE : 0;
pSequence->autolayer[ pSequence->numautolayers ].flags |= pDmeBlendLayer->m_bNoBlend ? STUDIO_AL_NOBLEND : 0;
pSequence->autolayer[ pSequence->numautolayers ].flags |= pDmeBlendLayer->m_bLocal ? STUDIO_AL_LOCAL : 0;
pSequence->flags |= pDmeBlendLayer->m_bLocal ? STUDIO_AL_LOCAL : 0;
if ( !pDmeBlendLayer->m_sPoseParameterName.IsEmpty() )
{
pSequence->autolayer[ pSequence->numautolayers ].flags |= STUDIO_AL_POSE;
pSequence->autolayer[ pSequence->numautolayers ].pose = LookupPoseParameter( pDmeBlendLayer->m_sPoseParameterName.Get() );
}
}
pSequence->numautolayers++;
}
}
//-----------------------------------------------------------------------------
// Purpose: Loads all of the IkLocks for the specified Sequence
//-----------------------------------------------------------------------------
void LoadIkLockList( CDmeSequenceBase *pDmeSequenceBase, s_sequence_t *pSequence )
{
if ( !pDmeSequenceBase || !pSequence )
return;
const int nIkLockCount = pDmeSequenceBase->m_eIkLockList.Count();
for ( int i = 0; i < nIkLockCount; ++i )
{
CDmeIkLock *pDmeIkLock = pDmeSequenceBase->m_eIkLockList[ i ];
if ( !pDmeIkLock )
continue;
// TODO: Check for duplicates?
CDmeIkChain *pDmeIkChain = pDmeIkLock->m_eIkChain.GetElement();
if ( pDmeIkChain )
{
Q_strncpy( pSequence->iklock[ pSequence->numiklocks ].name, pDmeIkChain->GetName(), sizeof( pSequence->iklock[ pSequence->numiklocks ].name ) );
}
else
{
MdlError( "1700: No IkChain element assigned to %s:%s\n", pDmeIkLock->GetTypeString(), pDmeIkLock->GetName() );
}
pSequence->iklock[ pSequence->numiklocks ].flPosWeight = clamp( pDmeIkLock->m_flLockPosition.Get(), 0.0f, 1.0f );
pSequence->iklock[ pSequence->numiklocks ].flLocalQWeight = RemapValClamped( pDmeIkLock->m_flLockRotation, 0.0f, 1.0f, 1.0f, 0.0f ); // Invert it for historical reasons
pSequence->numiklocks++;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void LoadAnimationEventList( CDmeSequenceBase *pDmeSequenceBase, s_sequence_t *pSequence )
{
if ( !pDmeSequenceBase || !pSequence )
return;
CDmAttribute *pBlah = pDmeSequenceBase->GetAttribute( "animationEventList" );
int nFoo = 0;
if ( pBlah )
{
nFoo = CDmrGenericArrayConst( pBlah ).Count();
}
const int nAnimEventCount = pDmeSequenceBase->m_eAnimationEventList.Count();
for ( int i = 0; i < nAnimEventCount; ++i )
{
CDmeAnimationEvent *pDmeAnimationEvent = pDmeSequenceBase->m_eAnimationEventList[ i ];
if ( !pDmeAnimationEvent )
continue;
// Not sure why this is +1... always one special event??
if ( pSequence->numevents + 1 >= MAXSTUDIOEVENTS )
{
MdlError( "1800: Too many %s's on %s:%s, ignoring from %s\n",
pDmeAnimationEvent->GetTypeString(),
pDmeSequenceBase->GetTypeString(),
pDmeSequenceBase->GetName(),
pDmeAnimationEvent->GetName() );
return;
}
Q_strncpy( pSequence->event[ pSequence->numevents ].eventname, pDmeAnimationEvent->GetName(), sizeof( pSequence->event[ pSequence->numevents ].eventname ) );
pSequence->event[ pSequence->numevents ].frame = pDmeAnimationEvent->m_nFrame;
if ( pDmeAnimationEvent->m_sDataString.IsEmpty() )
{
pSequence->event[ pSequence->numevents ].options[0] = '\0';
}
else
{
Q_strncpy( pSequence->event[ pSequence->numevents ].options, pDmeAnimationEvent->m_sDataString, sizeof( pSequence->event[ pSequence->numevents ].options ) );
}
++pSequence->numevents;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadSequenceList( CDmeMultiSequence *pDmeMultiSequence, CUtlVector< s_animation_t * > &animations )
{
if ( !pDmeMultiSequence )
return;
const int nSequenceCount = pDmeMultiSequence->m_eSequenceList.Count();
for ( int i = 0; i < nSequenceCount; ++i )
{
CDmeSequence *pDmeSubSequence = pDmeMultiSequence->m_eSequenceList[ i ];
if ( !pDmeSubSequence )
continue;
s_animation_t *pAnim = LookupAnimation( pDmeSubSequence->GetName() );
if ( !pAnim )
{
MdlWarning( "1208: DmeSequence %s: Couldn't find referenced animation: %s\n", pDmeMultiSequence->GetName(), pDmeSubSequence->GetName() );
continue;
}
// TODO: Limit to 64?
animations.AddToTail( pAnim );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadSequenceBlends( CDmeMultiSequence *pDmeMultiSequence, s_sequence_t *pSequence )
{
if ( !pSequence || !pDmeMultiSequence )
return;
const int nBlendCount = pDmeMultiSequence->m_eBlendList.Count();
for ( int i = 0; i < nBlendCount; ++i )
{
CDmeSequenceBlendBase *pDmeSequenceBlendBase = pDmeMultiSequence->m_eBlendList[ i ];
if ( !pDmeSequenceBlendBase )
continue;
if ( pDmeSequenceBlendBase->IsA( CDmeSequenceBlend::GetStaticTypeSymbol() ) )
{
CDmeSequenceBlend *pDmeSequenceBlend = CastElement< CDmeSequenceBlend >( pDmeSequenceBlendBase );
const int i = ( pSequence->paramindex[0] != -1 ) ? 1 : 0;
const int j = LookupPoseParameter( pDmeSequenceBlend->m_sPoseParameterName.Get() );
pSequence->paramindex[i] = j;
pSequence->paramattachment[i] = -1;
pSequence->paramstart[i] = pDmeSequenceBlend->m_flParamStart;
pSequence->paramend[i] = pDmeSequenceBlend->m_flParamEnd;
g_pose[j].min = MIN( g_pose[j].min, pSequence->paramstart[i] );
g_pose[j].min = MIN( g_pose[j].min, pSequence->paramend[i] );
g_pose[j].max = MAX( g_pose[j].max, pSequence->paramstart[i] );
g_pose[j].max = MAX( g_pose[j].max, pSequence->paramend[i] );
}
else if ( pDmeSequenceBlendBase->IsA( CDmeSequenceCalcBlend::GetStaticTypeSymbol() ) )
{
// TODO: This really isn't an animation command but the distinction might not be important?
CDmeSequenceCalcBlend *pDmeSequenceCalcBlend = CastElement< CDmeSequenceCalcBlend >( pDmeSequenceBlendBase );
const int i = ( pSequence->paramindex[0] != -1 ) ? 1 : 0;
const int j = LookupPoseParameter( pDmeSequenceCalcBlend->m_sPoseParameterName.Get() );
pSequence->paramindex[i] = j;
pSequence->paramattachment[i] = LookupAttachment( pDmeSequenceCalcBlend->m_sAttachmentName.Get() );
pSequence->paramcontrol[i] = pDmeSequenceCalcBlend->m_eMotionControl.GetElement()->GetStudioMotionControl();
if ( pSequence->paramattachment[i] < 0 )
{
MdlWarning( "1606: Unknown Attachment For %s - %s.%s = %s\n",
pDmeSequenceCalcBlend->GetTypeString(),
pDmeSequenceCalcBlend->GetName(),
pDmeSequenceCalcBlend->m_sAttachmentName.GetAttribute()->GetName(),
pDmeSequenceCalcBlend->m_sAttachmentName.Get() );
}
continue; // Don't add it as an animation command
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void LoadBlendRefCompCenter( CDmeMultiSequence *pDmeMultiSequence, s_sequence_t *pSequence )
{
if ( !pDmeMultiSequence || !pSequence )
return;
CDmeSequence *pDmeBlendRefSequence = CastElement< CDmeSequence >( pDmeMultiSequence->m_eBlendRef.GetElement() );
if ( pDmeBlendRefSequence )
{
const char *pszBlendRef = pDmeBlendRefSequence->GetName();
if ( pszBlendRef && *pszBlendRef )
{
pSequence->paramanim = LookupAnimation( pszBlendRef );
if ( pSequence->paramanim == NULL )
{
MdlWarning( "1202: DmeSequence %s: Unknown .blendRef animation: %s\n", pDmeMultiSequence->GetName(), pszBlendRef );
}
}
}
CDmeSequence *pDmeBlendRefComp = CastElement< CDmeSequence >( pDmeMultiSequence->m_eBlendComp.GetElement() );
if ( pDmeBlendRefComp )
{
const char *pszBlendComp = pDmeBlendRefComp->GetName();
if ( pszBlendComp && *pszBlendComp )
{
pSequence->paramcompanim = LookupAnimation( pszBlendComp );
if ( pSequence->paramcompanim == NULL )
{
MdlWarning( "1203: DmeSequence %s: Unknown .blendComp animation: %s\n", pDmeMultiSequence->GetName(), pszBlendComp );
}
}
}
CDmeSequence *pDmeBlendRefCenter = CastElement< CDmeSequence >( pDmeMultiSequence->m_eBlendCenter.GetElement() );
if ( pDmeBlendRefCenter )
{
const char *pszBlendCenter = pDmeBlendRefCenter->GetName();
if ( pszBlendCenter && *pszBlendCenter )
{
pSequence->paramcenter = LookupAnimation( pszBlendCenter );
if ( pSequence->paramcenter == NULL )
{
MdlWarning( "1204: DmeSequence %s: Unknown .blendCenter animation: %s\n", pDmeMultiSequence->GetName(), pszBlendCenter );
}
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void CreateBindPoseSequence( s_source_t *pMainSource )
{
s_sourceanim_t *pSourceAnim = FindSourceAnim( pMainSource, "BindPose" );
if ( pSourceAnim )
{
// Create the first "BindPose" default sequence
s_sequence_t *pSeq = ProcessCmdSequence( "BindPose" );
s_animation_t *pAnim = ProcessImpliedAnimation( pSeq, pMainSource->filename );
pSeq->panim[0][0] = pAnim;
ProcessSequence( pSeq, 1, &pAnim, false );
}
}
//-----------------------------------------------------------------------------
// Purpose: Load the data from the CDmeAnimBlockSize element which is
// specified by the .animBlockSize attribute on the root node
// of the .mpp file. If it's NULL, do nothing
// Basically implements Cmd_AnimBlockSize
//-----------------------------------------------------------------------------
static void LoadAnimBlockSize( CDmeAnimBlockSize *pDmeAnimBlockSize )
{
if ( !pDmeAnimBlockSize )
return;
g_animblocksize = pDmeAnimBlockSize->m_nSize;
if ( g_animblocksize < 1024 )
{
g_animblocksize *= 1024;
}
g_bNoAnimblockStall = !pDmeAnimBlockSize->m_bStall;
switch ( pDmeAnimBlockSize->m_nStorageType )
{
case CDmeAnimBlockSize::ANIMBLOCKSTORAGETYPE_HIRES:
g_bAnimblockLowRes = false;
g_bAnimblockHighRes = true;
break;
case CDmeAnimBlockSize::ANIMBLOCKSTORAGETYPE_LOWRES:
// Fallthrough deliberate
default:
g_bAnimblockLowRes = true;
g_bAnimblockHighRes = false;
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Load all of the DmeSequence elements, if the passed
// CDmeSequenceList is NULL, empty or doesn't contain any valid
// sequences, a default "BindPose" sequence is created
// Returns: The number of sequences created
//-----------------------------------------------------------------------------
static int LoadAndCreateSequences( s_source_t *pMainSource, CDmeSequenceList *pSequenceList, bool &bSetUpAxis )
{
CreateBindPoseSequence( pMainSource );
LoadIkChainList( pSequenceList );
int nSequenceAddedCount = 0;
char szSeqName[ MAX_PATH ];
const int nSequenceCount = pSequenceList ? pSequenceList->m_Sequences.Count() : 0;
CUtlVector< CDmeSequenceBase * > sortedSequenceList;
// Add all CDmeSequence elements to the list first
for ( int nSequenceIndex = 0; nSequenceIndex < nSequenceCount; ++nSequenceIndex )
{
CDmeSequenceBase *pDmeSequenceBase = pSequenceList->m_Sequences[ nSequenceIndex ];
if ( !pDmeSequenceBase )
{
MdlWarning( "1201: Empty DmeSequence %s[ %d ]\n", pSequenceList->GetName(), nSequenceIndex );
continue;
}
const char *pszSeqName = pDmeSequenceBase->GetName();
if ( pszSeqName == NULL || *pszSeqName == '\0' )
{
MdlWarning( "1200: Ignoring Unnamed Sequence On %s[%d]\n", pSequenceList->GetName(), nSequenceIndex );
continue;
}
sortedSequenceList.AddToTail( pDmeSequenceBase );
}
qsort( sortedSequenceList.Base(), sortedSequenceList.Count(), sizeof( CDmeSequenceBase * ), CDmeSequenceBase::QSortFunction );
// Process the reordered DmeSequenceBase list
for ( int nSequenceIndex = 0; nSequenceIndex < sortedSequenceList.Count(); ++nSequenceIndex )
{
CUtlVector< s_animation_t * > animations;
CDmeSequenceBase *pDmeSequenceBase = sortedSequenceList[ nSequenceIndex ];
// Already checked for NULL & unnamed
const char *pszSeqName = pDmeSequenceBase->GetName();
Q_StripExtension( pszSeqName, szSeqName, sizeof( szSeqName ) );
s_sequence_t *pSeq = ProcessCmdSequence( szSeqName );
{
CDmeSequence *pDmeSimpleSequence = CastElement< CDmeSequence >( pDmeSequenceBase );
CDmeMultiSequence *pDmeMultiSequence = CastElement< CDmeMultiSequence >( pDmeSequenceBase );
if ( !pDmeSimpleSequence && !pDmeMultiSequence )
{
MdlWarning( "1209: Invalid DmeSequence %s[ %d ], not Simple or Multi\n", pSequenceList->GetName(), nSequenceIndex );
continue;
}
if ( pDmeSimpleSequence )
{
CDmeModel *pDmeModel = CastElement< CDmeModel >( pDmeSimpleSequence->m_eSkeleton.GetElement() );
if ( bSetUpAxis && pDmeModel )
{
const char *pUpAxis = pDmeModel->GetValueString( "upAxis" );
if ( pUpAxis )
{
if ( StringHasPrefix( pUpAxis, "Y" ) )
{
// rotate 90 degrees around x to move y into z
g_defaultrotation = RadianEuler( M_PI / 2.0f, 0.0f, M_PI / 2.0f );
}
}
bSetUpAxis = false;
}
CDmeDag *pDmeSkeleton = pDmeSimpleSequence->m_eSkeleton.GetElement();
if ( !pDmeSkeleton )
{
MdlWarning( "1205: Ignoring Sequence %s, No Skeleton Specified\n", pszSeqName );
continue;
}
s_source_t *pSource = AllocateDmxSource( szSeqName );
BoneTransformMap_t boneMap;
if ( !LoadModelAndSkeleton( pSource, boneMap, pDmeSkeleton, pDmeModel, NULL, false ) )
{
MdlWarning( "1206: Sequence %s: Ignoring Sequence, Couldn't Load Specified Skeleton: %s\n", pszSeqName, pDmeSkeleton->GetName() );
continue;
}
CDmeAnimationList *pDmeAnimationList = pDmeSimpleSequence->m_eAnimationList.GetElement();
s_animation_t *pAnim = NULL;
if ( pDmeAnimationList )
{
// Simple sequence created from animation data
LoadAnimations( pSource, pDmeAnimationList, g_currentscale, boneMap );
// Check for various CDmeAnimCmd's which may or may not be flagged with bDelta
bool bDmeAnimCmdDelta = false;
for ( int i = 0; i < pDmeSimpleSequence->m_eAnimationCommandList.Count(); ++i )
{
if ( pDmeSimpleSequence->m_eAnimationCommandList[i]->IsA( CDmeAnimCmdSubtract::GetStaticTypeSymbol() ) ||
pDmeSimpleSequence->m_eAnimationCommandList[i]->IsA( CDmeAnimCmdPreSubtract::GetStaticTypeSymbol() ) ||
pDmeSimpleSequence->m_eAnimationCommandList[i]->IsA( CDmeAnimCmdLinearDelta::GetStaticTypeSymbol() ) ||
pDmeSimpleSequence->m_eAnimationCommandList[i]->IsA( CDmeAnimCmdSplineDelta::GetStaticTypeSymbol() ) )
{
bDmeAnimCmdDelta = true;
pSeq->flags |= STUDIO_DELTA;
}
}
{
// allocate animation entry
g_panimation[g_numani] = (s_animation_t *)calloc( 1, sizeof( s_animation_t ) );
g_panimation[g_numani]->index = g_numani;
pAnim = g_panimation[g_numani];
g_numani++;
pAnim->isImplied = true;
pAnim->startframe = 0;
pAnim->endframe = MAXSTUDIOANIMFRAMES - 1;
strcpy( pAnim->name, "@" );
strcat( pAnim->name, pSeq->name );
strcpyn( pAnim->filename, pSource->filename );
VectorCopy( g_defaultadjust, pAnim->adjust );
pAnim->scale = 1.0f;
// Don't re-orient delta animations
if ( bDmeAnimCmdDelta || !( pDmeSimpleSequence->m_bDelta || pDmeSimpleSequence->m_bPreDelta ) )
{
pAnim->rotation = g_defaultrotation;
}
pAnim->fps = 30;
pAnim->motionrollback = g_flDefaultMotionRollback;
pAnim->source = pSource;
const int nSourceAnimCount = pAnim->source->m_Animations.Count();
if ( nSourceAnimCount > 0 )
{
s_sourceanim_t *pSourceAnim = &pAnim->source->m_Animations[nSourceAnimCount-1];
Q_strncpy( pAnim->animationname, pAnim->source->m_Animations[nSourceAnimCount-1].animationname, sizeof(pAnim->animationname) );
if ( pAnim->startframe < pSourceAnim->startframe )
{
pAnim->startframe = pSourceAnim->startframe;
}
if ( pAnim->endframe > pSourceAnim->endframe )
{
pAnim->endframe = pSourceAnim->endframe;
}
}
else
{
Q_strncpy( pAnim->animationname, "", sizeof( pAnim->animationname ) );
}
pAnim->numframes = pAnim->endframe - pAnim->startframe + 1;
}
}
else
{
// Simple sequence created from a single pose
CreateAnimFromSkeleton( pSource, szSeqName, boneMap );
pAnim = ProcessImpliedAnimation( pSeq, pSource->filename );
}
Assert( pAnim );
animations.AddToTail( pAnim );
// Hack to allow animations commands to refer to same sequence in a simple DmeSequence
pSeq->panim[0][0] = pAnim;
// Animation Options
LoadAnimationOptions( pDmeSimpleSequence, pAnim );
// Animation Commands
LoadAnimationCommands( pDmeSimpleSequence, pAnim );
// IkRules
LoadIkRuleList( pDmeSimpleSequence, pAnim );
AddBodyAttachments( pSource );
}
// Only apply to multi-sequences
if ( pDmeMultiSequence )
{
LoadSequenceList( pDmeMultiSequence, animations );
LoadSequenceBlends( pDmeMultiSequence, pSeq );
LoadBlendRefCompCenter( pDmeMultiSequence, pSeq );
pSeq->groupsize[ 0 ] = pDmeMultiSequence->m_nBlendWidth.Get();
}
}
LoadAnimationEventList( pDmeSequenceBase, pSeq );
// Same As ParseSequence
CDmeSequenceActivity *pDmeSequenceActivity = pDmeSequenceBase->m_eActivity.GetElement();
if ( pDmeSequenceActivity )
{
const char *pszActivityName = pDmeSequenceActivity->GetName();
if ( pszActivityName && Q_strlen( pszActivityName ) > 0 )
{
Q_strncpy( pSeq->activityname, pszActivityName, sizeof( pSeq->activityname ) );
pSeq->actweight = pDmeSequenceActivity->m_nWeight.Get();
for ( int nModifierIndex = 0; nModifierIndex < pDmeSequenceActivity->m_sModifierList.Count(); ++nModifierIndex )
{
strcpyn( pSeq->activitymodifier[ pSeq->numactivitymodifiers++ ].name, pDmeSequenceActivity->m_sModifierList.Get( nModifierIndex ) );
if ( pSeq->numactivitymodifiers == MAXSTUDIOACTIVITYMODIFIERS )
break;
}
if ( pDmeSequenceActivity->m_sModifierList.Count() > MAXSTUDIOACTIVITYMODIFIERS )
{
MdlWarning( "1210: Too many activity modifiers (%d) on DmeSequence %s, only using first %d\n",
pDmeSequenceActivity->m_sModifierList.Count(),
pSeq->name,
MAXSTUDIOACTIVITYMODIFIERS );
}
}
}
if ( pDmeSequenceBase->m_bLoop )
{
pSeq->flags |= STUDIO_LOOPING;
}
if ( pDmeSequenceBase->m_bSnap )
{
pSeq->flags |= STUDIO_SNAP;
}
if ( pDmeSequenceBase->m_bPost )
{
pSeq->flags |= STUDIO_POST;
}
if ( pDmeSequenceBase->m_bHidden )
{
pSeq->flags |= STUDIO_HIDDEN;
}
if ( pDmeSequenceBase->m_bDelta )
{
pSeq->flags |= STUDIO_DELTA;
pSeq->flags |= STUDIO_POST;
}
if ( pDmeSequenceBase->m_bWorldSpace )
{
pSeq->flags |= STUDIO_WORLD;
pSeq->flags |= STUDIO_POST;
}
if ( pDmeSequenceBase->m_bPreDelta )
{
pSeq->flags |= STUDIO_DELTA;
}
if ( pDmeSequenceBase->m_bAutoPlay )
{
pSeq->flags |= STUDIO_AUTOPLAY;
}
if ( pDmeSequenceBase->m_bRealtime )
{
pSeq->flags |= STUDIO_REALTIME;
}
// AddLayer/BlendLayer
LoadLayerList( pDmeSequenceBase, pSeq );
// IkLock
LoadIkLockList( pDmeSequenceBase, pSeq );
pSeq->fadeintime = pDmeSequenceBase->m_flFadeIn.Get();
pSeq->fadeouttime = pDmeSequenceBase->m_flFadeOut.Get();
const CUtlString &entryNode = pDmeSequenceBase->m_sEntryNode.Get();
const CUtlString &exitNode = pDmeSequenceBase->m_sExitNode.Get();
if ( !entryNode.IsEmpty() )
{
if ( !exitNode.IsEmpty() )
{
pSeq->entrynode = LookupXNode( entryNode.Get() );
pSeq->exitnode = LookupXNode( exitNode.Get() );
if ( pDmeSequenceBase->m_bReverseNodeTransition )
{
pSeq->nodeflags |= 1;
}
}
else
{
pSeq->entrynode = pSeq->exitnode = LookupXNode( entryNode.Get() );
}
}
if ( animations.Count() > 0 )
{
ProcessSequence( pSeq, animations.Count(), animations.Base(), false );
++nSequenceAddedCount;
}
else
{
MdlWarning( "1207: DmeSequence %s: No animations created or referenced, ignoring\n", pszSeqName );
}
const CUtlString &keyValues = pDmeSequenceBase->m_sKeyValues.Get();
const int nKeyValuesLength = keyValues.Length();
if ( nKeyValuesLength > 0 )
{
pSeq->KeyValue.AddMultipleToTail( nKeyValuesLength, keyValues.Get() );
}
}
return nSequenceAddedCount;
}
//-----------------------------------------------------------------------------
// Purpose: Load all of the strings from the passed
// CDmeIncludeModelList.includeModels array
//-----------------------------------------------------------------------------
static void LoadIncludeModelList( CDmeIncludeModelList *pIncludeModelList )
{
if ( !pIncludeModelList )
return;
const int nIncludeModelsCount = pIncludeModelList->m_IncludeModels.Count();
for ( int ni = 0; ni < nIncludeModelsCount; ++ni )
{
if ( g_numincludemodels >= ARRAYSIZE( g_includemodel ) )
{
MdlError( "Too Many Include Models while including: \"%s\", Max: %d\n", pIncludeModelList->m_IncludeModels[ ni ], ARRAYSIZE( g_includemodel ) );
}
Q_strncpy( g_includemodel[ g_numincludemodels ].name, pIncludeModelList->m_IncludeModels[ ni ], MAXSTUDIONAME );
Q_FixSlashes( g_includemodel[ g_numincludemodels ].name, '/' );
++g_numincludemodels;
}
}
// return true if this QAngle is (0,0,0) within tolerance
static bool IsZero( const QAngle &qa, float tolerance = 0.01f )
{
return (qa.x > -tolerance && qa.x < tolerance &&
qa.y > -tolerance && qa.y < tolerance &&
qa.z > -tolerance && qa.z < tolerance);
}
//-----------------------------------------------------------------------------
// Split the specified string into substrings separated by "_"'s and then
// sort the strings alphabetically
//-----------------------------------------------------------------------------
static void UnderscoreSplitAndSortStrings( const char *pszString, CUtlVector< CUtlString > &splitAndSortedString )
{
splitAndSortedString.RemoveAll();
const int nStrLen = Q_strlen( pszString );
if ( nStrLen <= 0 )
return;
char *pszTmpString = reinterpret_cast< char * >( stackalloc( ( nStrLen + 1 ) * sizeof( char ) ) );
if ( !pszTmpString )
return;
Q_memset( pszTmpString, 0, ( nStrLen + 1 ) * sizeof( char ) );
Q_strncpy( pszTmpString, pszString, nStrLen + 1 );
char *pszEnd = NULL;
for ( char *pszName = pszTmpString; pszName && *pszName; pszName = pszEnd )
{
pszEnd = strchr( pszName, '_' );
if ( !pszEnd )
{
splitAndSortedString.AddToTail( CUtlString( pszName ) );
}
else
{
*pszEnd = '\0';
++pszEnd;
splitAndSortedString.AddToTail( CUtlString( pszName ) );
}
}
// Insertion sort
for ( int i = 1; i < splitAndSortedString.Count(); ++i )
{
const CUtlString sValue = splitAndSortedString[i];
int j = i - 1;
while ( j >= 0 && Q_stricmp( splitAndSortedString[j].Get(), sValue.Get() ) > 0 )
{
splitAndSortedString[ j + 1 ] = splitAndSortedString[ j ];
--j;
}
splitAndSortedString[j + 1] = sValue;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static bool GetContentsDescription( int &nContentsDescription, CDmElement *pDmElement )
{
nContentsDescription = CONTENTS_SOLID;
if ( !pDmElement )
return false;
CDmAttribute *pDmeContentsDescriptionAttr = pDmElement->GetAttribute( "contentsDescription" );
if ( !pDmeContentsDescriptionAttr )
return false;
if ( pDmeContentsDescriptionAttr->GetType() != AT_STRING )
return false;
const char *pszContentsDesc = pDmeContentsDescriptionAttr->GetValueString();
if ( !pszContentsDesc || Q_strlen( pszContentsDesc ) <= 0 )
return false;
struct ContentDesc_t
{
const char *m_pszName;
int m_nContentDescriptionFlags;
};
ContentDesc_t contentDescs[] = {
{ "notsolid", CONTENTS_EMPTY },
{ "monster", CONTENTS_MONSTER },
{ "ladder", CONTENTS_LADDER },
{ "solid", CONTENTS_SOLID },
{ "solid_monster", CONTENTS_SOLID | CONTENTS_MONSTER },
{ "solid_ladder", CONTENTS_SOLID | CONTENTS_LADDER },
{ "solid_monster_ladder", CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_LADDER },
{ "grate", CONTENTS_GRATE },
{ "grate_monster", CONTENTS_GRATE | CONTENTS_MONSTER },
{ "grate_ladder", CONTENTS_GRATE | CONTENTS_LADDER },
{ "grate_monster_ladder", CONTENTS_GRATE | CONTENTS_MONSTER | CONTENTS_LADDER },
};
CUtlVector< CUtlString > userContentsDesc;
UnderscoreSplitAndSortStrings( pszContentsDesc, userContentsDesc );
CUtlVector< CUtlString > validContentsDesc;
for ( int i = 0; i < ARRAYSIZE( contentDescs ); ++i )
{
UnderscoreSplitAndSortStrings( contentDescs[i].m_pszName, validContentsDesc );
if ( userContentsDesc.Count() != validContentsDesc.Count() )
continue;
bool bFound = true;
for ( int j = 0; j < userContentsDesc.Count(); ++j )
{
if ( Q_stricmp( userContentsDesc[j].Get(), validContentsDesc[j].Get() ) )
{
bFound = false;
break;
}
}
if ( bFound )
{
nContentsDescription = contentDescs[i].m_nContentDescriptionFlags;
return true;
}
}
MdlWarning( "2100: Invalid \"contentsDescription\" \"%s\" on \"%s\"\n",
pszContentsDesc, pDmElement->GetName() );
static bool bWarned = false;
if ( !bWarned )
{
bWarned = true;
CUtlString sWarn;
const int nContentDescsCount = ARRAYSIZE( contentDescs );
if ( nContentDescsCount > 0 )
{
sWarn = contentDescs[0].m_pszName;
for ( int i = 1; i < nContentDescsCount; ++i )
{
sWarn += ", ";
sWarn += contentDescs[i].m_pszName;
}
}
MdlWarning( " Valid ones are: %s\n", sWarn.Get() );
}
return false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadDefineBoneList( CDmeDefineBoneList *pDmeDefineBoneList )
{
if ( !pDmeDefineBoneList )
return;
CDmeDefineBone *pDmeDefineBone = NULL;
s_importbone_t *pImportBone = NULL;
const int nDefineBoneCount = pDmeDefineBoneList->m_DefineBones.Count();
for ( int ni = 0; ni < nDefineBoneCount; ++ni )
{
pDmeDefineBone = pDmeDefineBoneList->m_DefineBones[ ni ];
if ( !pDmeDefineBone )
continue;
if ( g_numimportbones >= ARRAYSIZE( g_importbone ) )
{
MdlError( "Too Many Define Bones while processing: \"%s\", Max: %d\n", pDmeDefineBone->GetName(), ARRAYSIZE( g_importbone ) );
break;
}
pImportBone = &g_importbone[ g_numimportbones++ ];
Q_strncpy( pImportBone->name, pDmeDefineBone->GetName(), MAXSTUDIONAME );
Q_strncpy( pImportBone->parent, pDmeDefineBone->m_Parent.Get(), MAXSTUDIONAME );
AngleMatrix( pDmeDefineBone->m_Rotation.Get(), pDmeDefineBone->m_Translation.Get(), pImportBone->rawLocal );
const Vector &rt = pDmeDefineBone->m_RealignTranslation.Get();
const QAngle &rr = pDmeDefineBone->m_RealignRotation.Get();
if ( !rt.IsZero() || !IsZero( rr ) )
{
pImportBone->bPreAligned = true;
AngleMatrix( pDmeDefineBone->m_RealignRotation.Get(), pDmeDefineBone->m_RealignTranslation.Get(), pImportBone->srcRealign );
}
else
{
pImportBone->bPreAligned = false;
SetIdentityMatrix( pImportBone->srcRealign );
}
int nContentsDescription = CONTENTS_SOLID;
if ( GetContentsDescription( nContentsDescription, pDmeDefineBone ) )
{
ContentsName_t *pContentsName = NULL;
for ( int j = 0; j < s_JointContents.Count(); ++j )
{
if ( !Q_stricmp( s_JointContents[j].m_pJointName, pDmeDefineBone->GetName() ) )
{
pContentsName = &s_JointContents[j];
break;
}
}
if ( !pContentsName )
{
pContentsName = &s_JointContents[ s_JointContents.AddToTail() ];
Q_strncpy( pContentsName->m_pJointName, pDmeDefineBone->GetName(), ARRAYSIZE( pContentsName->m_pJointName ) );
}
pContentsName->m_nContents = nContentsDescription;
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadKeyValues( const char *pszKeyValues )
{
if ( !pszKeyValues )
return;
const int nKeyValueCount = Q_strlen( pszKeyValues );
if ( nKeyValueCount <= 0 )
return;
const char *pszHeader = "mdlkeyvalue\n{\n";
const char *pszFooter = "}\n";
g_KeyValueText.AddMultipleToTail( Q_strlen( pszHeader ), pszHeader );
g_KeyValueText.AddMultipleToTail( nKeyValueCount, pszKeyValues );
g_KeyValueText.AddMultipleToTail( Q_strlen( pszFooter ), pszFooter );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadGlobalFlags( CDmElement *pDmeRoot )
{
// Get default rotation as a matrix
matrix3x4_t mDefRot;
AngleMatrix( g_defaultrotation, mDefRot );
// QC $bbox
CDmeBBox *pDmeBBox = pDmeRoot->GetValueElement< CDmeBBox >( "bbox" );
if ( pDmeBBox )
{
ITransformAABB( mDefRot, pDmeBBox->m_vMinBounds, pDmeBBox->m_vMaxBounds, bbox[0], bbox[1] );
}
if ( pDmeRoot->HasAttribute( "opacity" ) )
{
switch ( pDmeRoot->GetValue< int >( "opacity" ) )
{
case 0: // Auto, do nothing
gflags &= ~STUDIOHDR_FLAGS_FORCE_OPAQUE;
gflags &= ~STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS;
break;
case 1: // opaque
// Opaque has precedence over Mostly Opaque (TRANSLUCENT_TWOPASS)
gflags |= STUDIOHDR_FLAGS_FORCE_OPAQUE;
gflags &= ~STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS;
break;
case 2: // mostly opaque
// Opaque has precedence over Mostly Opaque (TRANSLUCENT_TWOPASS)
if ( ( gflags & STUDIOHDR_FLAGS_FORCE_OPAQUE) == 0)
{
gflags |= STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS;
}
break;
}
}
if ( pDmeRoot->HasAttribute( "illuminationPosition", AT_VECTOR3 ) )
{
VectorIRotate( pDmeRoot->GetValue< Vector >( "illuminationPosition"), mDefRot, illumposition );
}
if ( pDmeRoot->GetValue< bool >( "ambientBoost", false ) )
{
gflags |= STUDIOHDR_FLAGS_AMBIENT_BOOST;
}
if ( pDmeRoot->GetValue< bool >( "subdivisionSurface", false ) )
{
gflags |= STUDIOHDR_FLAGS_SUBDIVISION_SURFACE;
}
if ( pDmeRoot->GetValue< bool >( "doNotCastShadows", false ) )
{
gflags |= STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS;
}
if ( pDmeRoot->GetValue< bool >( "castTextureShadows", false ) )
{
gflags |= STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS;
}
if ( pDmeRoot->GetValue< bool >( "noForcedFade", false ) )
{
gflags |= STUDIOHDR_FLAGS_NO_FORCED_FADE;
}
int nContentsDescription = CONTENTS_SOLID;
if ( GetContentsDescription( nContentsDescription, pDmeRoot ) )
{
s_nDefaultContents = nContentsDescription;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadEyeballGlobals( CDmeEyeballGlobals *pDmeEyeballGlobals )
{
if ( !pDmeEyeballGlobals )
return;
g_flMaxEyeDeflection = cosf( DEG2RAD( pDmeEyeballGlobals->m_flMaxEyeDeflection.Get() ) );
matrix3x4_t mDefRot;
AngleMatrix( g_defaultrotation, mDefRot );
VectorIRotate( pDmeEyeballGlobals->m_vEyePosition.Get(), mDefRot, eyeposition );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadMaterialGroups( const CDmeMaterialGroupList *pDmeMaterialGroupList )
{
if ( !pDmeMaterialGroupList )
return;
if ( g_numskinref == 0 )
g_numskinref = g_numtextures;
// Studiomdl will read in multiple $texturegroup calls but only the first one
// is ever used, so in mdlcompile, there will only ever be one
Assert( g_numtexturegroups == 0 );
CDmeMaterialGroup *pDmeMaterialGroup = NULL;
const int nMaterialGroupCount = pDmeMaterialGroupList->m_MaterialGroups.Count();
if ( nMaterialGroupCount >= ARRAYSIZE( g_texturegroup[ 0 ] ) )
{
MdlError( "Too Many Material Groups, Max: %d\n", ARRAYSIZE( g_texturegroup[ 0 ] ) );
return;
}
const int nTextureCount = g_numtextures;
for ( int nGroupIndex = 0; nGroupIndex < nMaterialGroupCount; ++nGroupIndex )
{
pDmeMaterialGroup = pDmeMaterialGroupList->m_MaterialGroups[ nGroupIndex ];
if ( !pDmeMaterialGroup )
continue;
const int nMaterialCount = pDmeMaterialGroup->m_MaterialList.Count();
if ( nGroupIndex > 0 && nMaterialCount < nTextureCount )
{
MdlWarning( "Only Setting %d of %d Textures via MaterialGroup %d, Skin %d will have bad materials\n",
nMaterialCount, nTextureCount, nGroupIndex, nGroupIndex );
}
for ( int nMaterialIndex = 0; nMaterialIndex < nMaterialCount; ++nMaterialIndex )
{
const int nMdlMaterialIndex = UseTextureAsMaterial( LookupTexture( pDmeMaterialGroup->m_MaterialList[ nMaterialIndex ], true ) );
g_texturegroup[ g_numtexturegroups ][ nGroupIndex ][ nMaterialIndex ] = nMdlMaterialIndex;
if ( nGroupIndex != 0 )
{
g_texture[ nMdlMaterialIndex ].parent = g_texturegroup[ g_numtexturegroups ][ 0 ][ nMaterialIndex ];
}
g_numtexturelayers[ g_numtexturegroups ] = nGroupIndex + 1;
g_numtexturereps[ g_numtexturegroups ] = nMaterialIndex + 1;
}
}
++g_numtexturegroups;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static s_hitboxset *AllocateHitboxSet()
{
s_hitboxset *pHitboxSet = &g_hitboxsets[ g_hitboxsets.AddToTail() ];
memset( pHitboxSet, 0, sizeof( s_hitboxset ) );
return pHitboxSet;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static s_bbox_t *AllocateHitbox( s_hitboxset *pHitboxSet )
{
if ( pHitboxSet->numhitboxes < ARRAYSIZE( pHitboxSet->hitbox ) )
return &pHitboxSet->hitbox[ pHitboxSet->numhitboxes++ ];
MdlWarning( "Too many hitboxes request for hitbox set \"%s\", max %d\n", pHitboxSet->hitboxsetname, ARRAYSIZE( pHitboxSet->hitbox ) );
return NULL;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadHitboxSetList( const CDmeHitboxSetList *pDmeHitboxSetList )
{
if ( !pDmeHitboxSetList )
return;
const int nHitboxSetCount = pDmeHitboxSetList->m_HitboxSetList.Count();
for ( int nHitBoxSetIndex = 0; nHitBoxSetIndex < nHitboxSetCount; ++nHitBoxSetIndex )
{
CDmeHitboxSet *pSrcHitboxSet = pDmeHitboxSetList->m_HitboxSetList[ nHitBoxSetIndex ];
// Add a new hitboxset
s_hitboxset *pHitboxSet = AllocateHitboxSet();
Q_strncpy( pHitboxSet->hitboxsetname, pSrcHitboxSet->GetName(), sizeof(pHitboxSet->hitboxsetname) );
int nHitboxCount = pSrcHitboxSet->m_HitboxList.Count();
for ( int nHitboxIndex = 0; nHitboxIndex < nHitboxCount; ++nHitboxIndex )
{
CDmeHitbox *pSrcHitbox = pSrcHitboxSet->m_HitboxList[ nHitboxIndex ];
// Find bone
s_bonetable_t *pStudioBone = NULL;
const int nBoneIndex = findGlobalBone( pSrcHitbox->m_sBoneName );
if ( nBoneIndex >= 0 && nBoneIndex < g_numbones )
{
pStudioBone = &g_bonetable[ nBoneIndex ];
}
// Find bone
for ( int nBoneIndex = 0; nBoneIndex < g_numbones; ++nBoneIndex )
{
}
s_bbox_t *pHitbox = AllocateHitbox( pHitboxSet );
Q_strncpy( pHitbox->name, pSrcHitbox->m_sBoneName, sizeof( pHitbox->name ) );
Q_strncpy( pHitbox->hitboxname, pSrcHitbox->GetName(), sizeof( pHitbox->hitboxname ) );
pHitbox->group = pSrcHitbox->m_nGroupId;
pHitbox->bmin = pSrcHitbox->m_vMinBounds;
pHitbox->bmax = pSrcHitbox->m_vMaxBounds;
if ( !pSrcHitbox->m_sSurfaceProperty.IsEmpty() )
{
const char *pSurfaceProp = FindSurfaceProp( pHitbox->name );
if ( pSurfaceProp && Q_stricmp( pSurfaceProp, pSrcHitbox->m_sSurfaceProperty ) )
{
MdlWarning( "Hitbox surface property \"%s\" for bone \"%s\" conflicts with existing \"%s\"",
pSrcHitbox->m_sSurfaceProperty.Get(), pSrcHitbox->m_sBoneName.Get(), pSurfaceProp );
}
else
{
AddSurfaceProp( pHitbox->name, pSrcHitbox->m_sSurfaceProperty );
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle the root.boneFlexDriverList
//-----------------------------------------------------------------------------
static void LoadBoneFlexDriverList( const CDmeBoneFlexDriverList *pDmeBoneFlexDriverList )
{
if ( !pDmeBoneFlexDriverList )
return;
const CDmeBoneFlexDriverList *pDmeTmp = GetElement< CDmeBoneFlexDriverList >( g_hDmeBoneFlexDriverList );
if ( pDmeTmp != NULL )
{
char szTmpBuf0[40];
UniqueIdToString( pDmeTmp->GetId(), szTmpBuf0, sizeof( szTmpBuf0 ) );
char szTmpBuf1[40];
UniqueIdToString( pDmeBoneFlexDriverList->GetId(), szTmpBuf1, sizeof( szTmpBuf1 ) );
MdlError( "DmeBoneFlexDriverList already defined (%s:%s), ignoring (%s:%s)\n",
pDmeTmp->GetName(), szTmpBuf0,
pDmeBoneFlexDriverList->GetName(), szTmpBuf1 );
return;
}
CDmeBoneFlexDriverList *pDmeCopy = pDmeBoneFlexDriverList->Copy();
pDmeCopy->SetFileId( DMFILEID_INVALID, TD_DEEP );
g_hDmeBoneFlexDriverList = pDmeCopy->GetHandle();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void LoadBoneMergeList( CDmAttribute *pDmeBoneMergeListAttr )
{
if ( !pDmeBoneMergeListAttr )
return;
CDmrStringArrayConst boneMergeList( pDmeBoneMergeListAttr );
if ( !boneMergeList.IsValid() )
return;
const int nBoneMergeCount = boneMergeList.Count();
for ( int nBoneMergeIndex = 0; nBoneMergeIndex < nBoneMergeCount; ++nBoneMergeIndex )
{
strcpyn( g_BoneMerge[ g_BoneMerge.AddToTail() ].bonename, boneMergeList[ nBoneMergeIndex ] );
}
}
//-----------------------------------------------------------------------------
// Loads LODs from the preprocessed file
//-----------------------------------------------------------------------------
static int LodDistanceCompare( const void *elem1, const void *elem2 )
{
const CDmeLOD *l1 = *(const CDmeLOD **)elem1;
const CDmeLOD *l2 = *(const CDmeLOD **)elem2;
if ( l1->m_bIsShadowLOD != l2->m_bIsShadowLOD )
return l1->m_bIsShadowLOD ? 1 : -1;
if ( l1->m_flSwitchMetric > l2->m_flSwitchMetric )
return 1;
return ( l1->m_flSwitchMetric == l2->m_flSwitchMetric ) ? 0 : -1;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static bool LoadLODs( s_source_t** ppRootLODSource, CDmeLODList *pLodList, bool &bSetUpAxis, bool bStaticProp )
{
// NOTE: This is a little bit of a hack; it generates new s_source_ts
// based on the LOD data, which is somewhat contrary to the existing
// architecture, but is the easiest method of achieving the goal
// of loading LODs from the mpp file without reloading the giant mpp
// file over and over again.
*ppRootLODSource = NULL;
int nCount = pLodList->m_LODs.Count();
if ( nCount == 0 )
return true;
// Sort LODs by switch distance
CDmeLOD **ppLODs = (CDmeLOD**)stackalloc( nCount * sizeof(CDmeLOD*) );
for ( int i = 0; i < nCount; ++i )
{
ppLODs[i] = pLodList->m_LODs[i];
}
qsort( ppLODs, nCount, sizeof( CDmeLOD* ), LodDistanceCompare );
CDmeLOD *pRootLOD = pLodList->GetRootLOD();
if ( pRootLOD && bSetUpAxis )
{
CDmeModel *pDmeModel = pRootLOD->GetValueElement< CDmeModel >( "model" );
if ( pDmeModel )
{
const char *pUpAxis = pDmeModel->GetValueString( "upAxis" );
if ( pUpAxis )
{
if ( StringHasPrefix( pUpAxis, "Y" ) )
{
// rotate 90 degrees around x to move y into z
g_defaultrotation = RadianEuler( M_PI / 2.0f, 0.0f, M_PI / 2.0f );
}
}
bSetUpAxis = false;
}
}
char pSrcLODName[MAX_PATH];
Q_StripExtension( pRootLOD->GetName(), pSrcLODName, sizeof(pSrcLODName) );
for ( int i = 0; i < nCount; ++i )
{
CDmeLOD *pLOD = ppLODs[i];
bool bIsShadowLOD = pLOD->m_bIsShadowLOD;
if ( bIsShadowLOD )
{
if ( ( gflags & STUDIOHDR_FLAGS_HASSHADOWLOD ) != 0 )
{
MdlError( "Invalid LOD: \"%s\": Multiple Shadow LODs Defined\n", pLOD->GetName() );
return false;
}
gflags |= STUDIOHDR_FLAGS_HASSHADOWLOD;
}
else if ( pLOD->m_flSwitchMetric.Get() < 0.0f )
{
MdlError( "Invalid LOD: \"%s\": Negative switch value\n", pLOD->GetName() );
return false;
}
// Skip lower LODs if we're stripping them
bool bIsRootLOD = ( pLOD == pRootLOD );
if ( g_bStripLods && !bIsShadowLOD && ( !bIsRootLOD ) )
{
if ( !g_quiet )
{
Msg( "Stripped lod \"%s\" @ %.1f\n", pLOD->m_Model.GetElement() ? pLOD->m_Model->GetName() : "<none>", pLOD->m_flSwitchMetric );
}
continue;
}
// Now, fill her up! No model means LOD is valid, but has no geometry
const bool bHasModel = pLOD->m_Model.GetHandle() != DMELEMENT_HANDLE_INVALID;
const bool bHasSkeleton = pLOD->m_Skeleton.GetHandle() != DMELEMENT_HANDLE_INVALID;
// No model & no skeleton means LOD is invalid
if ( !bHasModel && !bHasSkeleton )
{
MdlError( "Invalid LOD: \"%s\": No Model or Skeleton defined\n", pLOD->GetName() );
return false;
}
if ( g_ScriptLODs.Count() == MAX_NUM_LODS )
{
MdlError( "Too many LODs (MAX_NUM_LODS==%d) while loading LOD: \"%s\"\n", static_cast< int >( MAX_NUM_LODS ), pLOD->GetName() );
return false;
}
// Check for overflow
if ( g_numsources >= MAXSTUDIOSEQUENCES )
{
MdlError( "Too many source models/animations (MAXSTUDIOSEQUENCES==%d) while loading LOD: \"%s\"\n", static_cast< int >( MAXSTUDIOSEQUENCES ), pLOD->GetName() );
return false;
}
s_source_t *pLODSource = AllocateDmxSource( pLOD->m_Path.Get() );
if ( !pLODSource )
{
MdlError( "Couldn't allocate new source while loading LOD: \"%s\"\n", pLOD->GetName() );
return false;
}
if ( bIsRootLOD )
{
*ppRootLODSource = pLODSource;
pLODSource->isActiveModel = true;
}
if ( bHasModel )
{
BoneTransformMap_t boneMap;
if ( !LoadModelAndSkeleton( pLODSource, boneMap, pLOD->m_Skeleton, pLOD->m_Model, pLOD->m_CombinationOperator, bStaticProp ) )
{
MdlError( "Couldn't load skeleton and model while loading LOD: \"%s\"\n", pLOD->GetName() );
return false;
}
LoadQcModelElements( pLODSource, g_pCurrentModel, pLOD->m_Model );
}
if ( bIsRootLOD )
continue;
// Create LOD information in terms of how the old system does it
// Shadow lod reserves -1 as switch value which uniquely identifies a shadow lod
int j = g_ScriptLODs.AddToTail();
LodScriptData_t& lod = g_ScriptLODs[j];
lod.switchValue = bIsShadowLOD ? -1.0f : pLOD->m_flSwitchMetric;
lod.EnableFacialAnimation( bIsShadowLOD ? false : !pLOD->m_bNoFlex );
lod.StripFromModel( false );
// We only support simple model replacement here. Other processing
// is expected to have occurred in the preprocessing phase
j = lod.modelReplacements.AddToTail();
CLodScriptReplacement_t& replacement = lod.modelReplacements[j];
replacement.SetSrcName( pSrcLODName );
replacement.SetDstName( pLODSource->filename );
replacement.m_pSource = pLODSource;
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
extern int Option_Blank();
//-----------------------------------------------------------------------------
// Loads Eyeballs
//-----------------------------------------------------------------------------
static void LoadEyeballs( s_model_t *ps_model_t, CDmeLODList *pDmeLODList )
{
if ( !pDmeLODList )
return;
matrix3x4_t mDefRot;
AngleMatrix( g_defaultrotation, mDefRot );
Vector vTmp;
const int nEyeballCount = pDmeLODList->m_EyeballList.Count();
if ( nEyeballCount <= 0 )
return;
for ( int nEyeballIndex = 0; nEyeballIndex < nEyeballCount; ++nEyeballIndex )
{
CDmeEyeball *pDmeEyeball = pDmeLODList->m_EyeballList.Get( nEyeballIndex );
if ( !pDmeEyeball )
continue;
if ( ps_model_t->numeyeballs >= ARRAYSIZE( ps_model_t->eyeball ) )
{
MdlWarning( "1100: Max number of eyeballs reached for model %s, ignoring eyeball %s\n", ps_model_t->name, pDmeEyeball->GetName() );
continue;
}
int nFoundBoneIndex = -1;
for ( int nSearchBoneIndex = 0; nSearchBoneIndex < ps_model_t->source->numbones; ++nSearchBoneIndex )
{
if ( !Q_stricmp( ps_model_t->source->localBone[ nSearchBoneIndex ].name, pDmeEyeball->m_sParentBoneName.Get() ) )
{
nFoundBoneIndex = nSearchBoneIndex;
break;
}
}
if ( nFoundBoneIndex < 0 )
{
MdlWarning( "1101: Couldn't find bone %s on model %s, ignoring eyeball %s\n", pDmeEyeball->m_sParentBoneName.Get(), ps_model_t->name, pDmeEyeball->GetName() );
continue;
}
const char *pszMaterialName = pDmeEyeball->m_sMaterialName.Get();
const bool bRelative = ( strchr( pszMaterialName, '/' ) || strchr( pszMaterialName, '\\' ) ) ? true : false;
const int nSearchMeshMatIndex = UseTextureAsMaterial( LookupTexture( pDmeEyeball->m_sMaterialName.Get(), bRelative ) );
int nFoundMeshMatIndex = -1;
for ( int i = 0; i < ps_model_t->source->nummeshes; ++i )
{
const int nTmpMeshMatIndex = ps_model_t->source->meshindex[ i ]; // meshes are internally stored by material index
if ( nTmpMeshMatIndex == nSearchMeshMatIndex )
{
nFoundMeshMatIndex = i;
break;
}
}
if ( nFoundMeshMatIndex < 0 )
{
MdlWarning( "1102: Couldn't find eyeball material %s on model %s, ignoring eyeball %s\n", pDmeEyeball->m_sMaterialName.Get(), ps_model_t->name, pDmeEyeball->GetName() );
continue;
}
s_eyeball_t *ps_eyeball_t = &( ps_model_t->eyeball[ ps_model_t->numeyeballs++ ] );
Q_strncpy( ps_eyeball_t->name, pDmeEyeball->GetName(), sizeof( ps_eyeball_t->name ) );
ps_eyeball_t->bone = nFoundBoneIndex;
ps_eyeball_t->mesh = nFoundMeshMatIndex;
ps_eyeball_t->radius = pDmeEyeball->m_flRadius.Get();
ps_eyeball_t->zoffset = tan( DEG2RAD( pDmeEyeball->m_flYawAngle.Get() ) );
ps_eyeball_t->iris_scale = 1.0f / pDmeEyeball->m_flIrisScale;
// translate eyeball into bone space
VectorITransform( pDmeEyeball->m_vPosition.Get(), ps_model_t->source->boneToPose[ ps_eyeball_t->bone ], ps_eyeball_t->org );
VectorIRotate( Vector( 0, 0, 1 ), mDefRot, vTmp );
VectorIRotate( vTmp, ps_model_t->source->boneToPose[ ps_eyeball_t->bone ], ps_eyeball_t->up );
VectorIRotate( Vector( 1, 0, 0 ), mDefRot, vTmp );
VectorIRotate( vTmp, ps_model_t->source->boneToPose[ ps_eyeball_t->bone ], ps_eyeball_t->forward );
// Not applicable
ps_eyeball_t->upperlidflexdesc = -1;
ps_eyeball_t->lowerlidflexdesc = -1;
}
// Make the standard flex controllers for eyes if required
static const char *szEyesFlexControllers[] = {
"eyes_updown",
"eyes_rightleft"
};
for ( int nNewFlexIndex = 0; nNewFlexIndex < ARRAYSIZE( szEyesFlexControllers ); ++nNewFlexIndex )
{
bool bHasEyeFlexController = false;
for ( int nFlexIndex = 0; nFlexIndex < g_numflexcontrollers; ++nFlexIndex )
{
if ( !Q_strcmp( szEyesFlexControllers[ nNewFlexIndex ], g_flexcontroller[ nFlexIndex ].name ) )
{
bHasEyeFlexController = true;
break;
}
}
// The flex controller range for eyes_updown & eyes_rightleft is default [-45, 45] because it's clamped by eyesMaxDeflection
// and changing it based on max deflection would cause animatin changes since flex controller values are normalized [0, 1]
// [-45, 45 ] gives a maxium range that's useful
if ( !bHasEyeFlexController )
{
if ( g_numflexcontrollers >= MAXSTUDIOFLEXCTRL )
{
MdlWarning( "1103: Couldn't make eyes flexcontroller %s, too many flex controllers defined\n", szEyesFlexControllers[ nNewFlexIndex ] );
continue;
}
Q_strncpy( g_flexcontroller[g_numflexcontrollers].name, szEyesFlexControllers[ nNewFlexIndex ], sizeof( g_flexcontroller[ g_numflexcontrollers ].name ) );
Q_strncpy( g_flexcontroller[g_numflexcontrollers].type, "eyes", sizeof( g_flexcontroller[ g_numflexcontrollers ].name ) );
g_flexcontroller[g_numflexcontrollers].min = -45.0f;
g_flexcontroller[g_numflexcontrollers].max = 45.0f;
++g_numflexcontrollers;
}
}
}
//-----------------------------------------------------------------------------
// Loads Body Group Lists from the preprocessed file
//-----------------------------------------------------------------------------
static bool LoadBodyGroupList( s_source_t **ppMainSource, CDmeBodyGroupList *pBodyGroupList, CDmeEyeballGlobals *pDmeEyeballGlobals, bool bStaticProp, bool &bSetUpAxis )
{
*ppMainSource = NULL;
CDmaElementArray< CDmeBodyGroup > &dmeBodyGroups = pBodyGroupList->m_BodyGroups;
const int nDmeBodyGroupCount = dmeBodyGroups.Count();
if ( nDmeBodyGroupCount == 0 )
return true;
// Eyeball globals needs to be loaded after g_defaultrotation is set, which is after the first LOD is loaded
bool bLoadEyeballGlobals = true;
// Compute the 'main' body part
const CDmeLODList *pMainBodyPart = pBodyGroupList->GetMainBodyPart();
for ( int i = 0; i < nDmeBodyGroupCount; ++i )
{
const CDmeBodyGroup *pDmeBodyGroup = dmeBodyGroups[i];
s_bodypart_t *pBodyPart = &g_bodypart[ g_numbodyparts ];
pBodyPart->nummodels = 0;
if ( g_numbodyparts == 0 )
{
pBodyPart->base = 1;
}
else
{
pBodyPart->base = g_bodypart[g_numbodyparts-1].base * g_bodypart[g_numbodyparts-1].nummodels;
}
Q_strncpy( pBodyPart->name, pDmeBodyGroup->GetName(), sizeof( pBodyPart->name ) );
++g_numbodyparts;
// Load all body parts for this body group
const int nDmeBodyPartCount = pDmeBodyGroup->m_BodyParts.Count();
for ( int j = 0; j < nDmeBodyPartCount; ++j )
{
s_model_t *pModel = (s_model_t *)calloc( 1, sizeof( s_model_t ) );
const int nModel = g_nummodels++;
g_model[nModel] = pModel;
pBodyPart->pmodel[ pBodyPart->nummodels++ ] = pModel;
pModel->scale = 1.0f;
CDmeBodyPart *pDmeBodyPart = pDmeBodyGroup->m_BodyParts[j];
CDmeLODList *pDmeLODList = CastElement< CDmeLODList >( pDmeBodyPart );
if ( pDmeBodyPart->LODCount() == 0 || !pDmeLODList )
{
pModel->source = AllocateDmxSource( "blank" );
Q_strncpy( pModel->name, "blank", sizeof(pModel->name) );
}
else
{
Q_strncpy( pModel->name, pDmeLODList->GetName(), sizeof(pModel->name) );
g_pCurrentModel = pModel;
if ( !LoadLODs( &pModel->source, pDmeLODList, bSetUpAxis, bStaticProp ) )
{
MdlError( "Bad LOD On BodyGroup \"%s\".bodyPartList[%d]\n", pDmeBodyGroup->GetName(), j );
return false;
}
g_pCurrentModel = NULL;
// This needs to be done after g_defaultRotation is set, which should be set after the first LOD is loaded
if ( bLoadEyeballGlobals )
{
bLoadEyeballGlobals = false;
LoadEyeballGlobals( pDmeEyeballGlobals );
}
LoadEyeballs( pModel, pDmeLODList );
if ( pModel->source )
{
Q_strncpy( pModel->filename, pModel->source->filename, sizeof(pModel->filename) );
}
PostProcessSource( pModel->source, nModel );
if ( pMainBodyPart == pDmeLODList )
{
*ppMainSource = pModel->source;
}
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Loads the collision model from the preprocessed file
//-----------------------------------------------------------------------------
bool LoadCollisionModel( CDmeCollisionModel *pCollisionInfo, bool bStaticProp )
{
if ( !pCollisionInfo )
return false;
// Check for overflow - TODO: Move to AllocateSource()
if ( g_numsources >= MAXSTUDIOSEQUENCES )
{
MdlError( "Load_Source - overflowed g_numsources loading LODs." );
return false;
}
s_source_t *pCollisionSource = AllocateDmxSource( pCollisionInfo->GetName() );
if ( !pCollisionSource )
return false;
int nummaterials = g_nummaterials;
int numtextures = g_numtextures;
// BoneRemap[bone index in file] == bone index in studiomdl
CDmeDag *pSkeleton = pCollisionInfo->GetValueElement< CDmeDag >( "skeleton" );
if ( !pSkeleton )
{
MdlError( "%s(%s): No \"skeleton\" defined\n", pCollisionInfo->GetTypeString(), pCollisionInfo->GetName() );
return false;
}
CDmeModel *pModel = pCollisionInfo->GetValueElement< CDmeModel >( "model" );
if ( !pModel )
{
MdlError( "%s(%s): No \"model\" defined\n", pCollisionInfo->GetTypeString(), pCollisionInfo->GetName() );
return false;
}
BoneTransformMap_t boneMap;
if ( !LoadModelAndSkeleton( pCollisionSource, boneMap, pSkeleton, pModel, NULL, bStaticProp ) )
{
MdlError( "%s(%s): Couldn't Load Skeleton: %s(%s) & Model: %s(%s)\n",
pCollisionInfo->GetTypeString(), pCollisionInfo->GetName(),
pSkeleton->GetTypeString(), pSkeleton->GetName(),
pModel->GetTypeString(), pModel->GetName() );
return false;
}
// auto-remove any new materials/textures
if ( nummaterials && numtextures && ( numtextures != g_numtextures || nummaterials != g_nummaterials ) )
{
g_numtextures = numtextures;
g_nummaterials = nummaterials;
pCollisionSource->texmap[0] = 0;
}
if ( DoCollisionModel( pCollisionSource, pCollisionInfo, bStaticProp ) == 0 )
return false;
const char *pSurfaceProperty = pCollisionInfo->GetValueString( "surfaceProperty" );
if ( pSurfaceProperty && pSurfaceProperty[0] )
{
SetDefaultSurfaceProp( pSurfaceProperty );
}
return true;
}
#endif // MDLCOMPILE
//-----------------------------------------------------------------------------
// Sets up the DMX if it was a static prop
//-----------------------------------------------------------------------------
static void SetupStaticProp( s_source_t *pSource )
{
#ifdef MDLCOMPILE
ProcessStaticProp();
s_sequence_t *pSeq = ProcessCmdSequence( "BindPose" );
s_animation_t *pAnim = ProcessImpliedAnimation( pSeq, pSource->filename );
pSeq->panim[0][0] = pAnim;
ProcessSequence( pSeq, 1, &pAnim, false );
#endif
}
//-----------------------------------------------------------------------------
// Main entry point for loading DMX files
//-----------------------------------------------------------------------------
int Load_DMX( s_source_t *pSource )
{
DmFileId_t fileId;
// use the full search tree, including mod hierarchy to find the file
char pFullPath[MAX_PATH];
if ( !GetGlobalFilePath( pSource->filename, pFullPath, sizeof(pFullPath) ) )
return 0;
// When reading, keep the CRLF; this will make ReadFile read it in binary format
// and also append a couple 0s to the end of the buffer.
CDmElement *pRoot;
if ( g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot ) == DMFILEID_INVALID )
return 0;
if ( !g_quiet )
{
Msg( "DMX Model %s\n", pFullPath );
}
// Load model info
LoadModelInfo( pRoot, pFullPath );
// Load constraints
LoadConstraints( pRoot );
// Extract out the skeleton
// BoneRemap[bone index in file] == bone index in studiomdl
CDmeDag *pSkeleton = pRoot->GetValueElement< CDmeDag >( "skeleton" );
CDmeModel *pModel = pRoot->GetValueElement< CDmeModel >( "model" );
CDmeCombinationOperator *pCombinationOperator = pRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" );
BoneTransformMap_t boneMap;
if ( !LoadModelAndSkeleton( pSource, boneMap, pSkeleton, pModel, pCombinationOperator, false ) )
goto dmxError;
LoadQcModelElements( pSource, g_pCurrentModel, pModel );
CDmeAnimationList *pAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "animationList" );
if ( pAnimationList )
{
LoadAnimations( pSource, pAnimationList, g_currentscale, boneMap );
}
fileId = pRoot->GetFileId();
g_pDataModel->RemoveFileId( fileId );
return 1;
dmxError:
fileId = pRoot->GetFileId();
g_pDataModel->RemoveFileId( fileId );
return 0;
}
//-----------------------------------------------------------------------------
// Main entry point for loading FBX files
//-----------------------------------------------------------------------------
int Load_FBX( s_source_t *pSource )
{
// use the full search tree, including mod hierarchy to find the file
char pFullPath[ MAX_PATH ];
if ( !GetGlobalFilePath( pSource->filename, pFullPath, sizeof( pFullPath ) ) )
return 0;
CDmFbxSerializer dmFbxSerializer;
dmFbxSerializer.m_eOptUpAxis = CDmeAxisSystem::AS_AXIS_Y;
dmFbxSerializer.m_eOptForwardParity = CDmeAxisSystem::AS_PARITY_ODD;
CDmElement *pRoot = dmFbxSerializer.ReadFBX( pFullPath );
if ( !pRoot )
return 0;
if ( !g_quiet )
{
Msg( "FBX Model %s\n", pFullPath );
}
// Load model info
LoadModelInfo( pRoot, pFullPath );
// Load constraints
LoadConstraints( pRoot );
// Extract out the skeleton
// BoneRemap[bone index in file] == bone index in studiomdl
CDmeDag *pSkeleton = pRoot->GetValueElement< CDmeDag >( "skeleton" );
CDmeModel *pModel = pRoot->GetValueElement< CDmeModel >( "model" );
CDmeCombinationOperator *pCombinationOperator = pRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" );
BoneTransformMap_t boneMap;
int nReturn = 0;
if ( LoadModelAndSkeleton( pSource, boneMap, pSkeleton, pModel, pCombinationOperator, false ) )
{
LoadQcModelElements( pSource, g_pCurrentModel, pModel );
CDmeAnimationList *pAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "animationList" );
if ( pAnimationList )
{
LoadAnimations( pSource, pAnimationList, g_currentscale, boneMap );
}
if ( CommandLine()->FindParm( "-debugfbx2dmx" ) )
g_pDataModel->SaveToFile( CUtlString( pFullPath ).StripExtension() + ".fbx2dmx.dmx", NULL, "keyvalues2", "model", pRoot );
nReturn = 1; // loaded ok
}
g_pDataModel->UnloadFile/*RemoveFileId?*/( pRoot->GetFileId() );
return nReturn;
}
//-----------------------------------------------------------------------------
// Declare it so we can call it, defined in studiomdl.cpp
//-----------------------------------------------------------------------------
extern void ProcessModelName( const char *pMdlName );
//-----------------------------------------------------------------------------
// Main entry point for loading preprocessed files
//-----------------------------------------------------------------------------
bool LoadPreprocessedFile( const char *pFileName, float flScale )
{
#ifndef MDLCOMPILE
return false;
#else
DmFileId_t fileId;
// use the full search tree, including mod hierarchy to find the file
char pFullPath[MAX_PATH];
if ( !GetGlobalFilePath( pFileName, pFullPath, sizeof(pFullPath) ) )
{
MdlError( "Invalid MPP Filename: %s\n", pFileName );
return false;
}
// When reading, keep the CRLF; this will make ReadFile read it in binary format
// and also append a couple 0s to the end of the buffer.
CDmElement *pRoot;
if ( g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot ) == DMFILEID_INVALID )
{
MdlError( "0001: Couldn't Load MPP File: %s\n", pFullPath );
return false;
}
if ( !g_quiet )
{
Msg( "Loaded Preprocessed File %s\n", pFullPath );
}
const char *pMdlPath = pRoot->GetValueString( "mdlPath" );
if ( pMdlPath && *pMdlPath )
{
// This is a hack... really the model path should be able to be anywhere relative to the game
// directory but currently "models/" is prepended everywhere... would like to get rid of that
// pattern but would also like to limit changes required for mdlcompile
if ( !Q_strnicmp( pMdlPath, "models", 6 ) && pMdlPath[6] == '/' || pMdlPath[7] == '\\' )
{
ProcessModelName( pMdlPath + 7 );
}
else
{
ProcessModelName( pMdlPath );
}
}
// Get whether we're doing skinned LODs from the pre-process file
g_bSkinnedLODs = pRoot->GetValue< bool >( "skinnedLODs", false );
// Load model info
LoadModelInfo( pRoot, pFullPath );
// Find out if it's marked as a static prop
const bool bStaticProp = pRoot->GetValue< bool >( "staticProp" );
bool bSetUpAxis = true;
s_source_t *pMainSource = NULL;
CDmeBodyGroupList *pBodyGroupList = pRoot->GetValueElement< CDmeBodyGroupList >( "bodyGroupList" );
if ( pBodyGroupList )
{
// Loads all body groups
if ( !LoadBodyGroupList( &pMainSource, pBodyGroupList, pRoot->GetValueElement< CDmeEyeballGlobals >( "eyeballGlobals" ), bStaticProp, bSetUpAxis ) )
goto dmxError;
CDmeCollisionModel *pCollisionModel = pRoot->GetValueElement< CDmeCollisionModel >( "collisionModel" );
if ( pCollisionModel && !LoadCollisionModel( pCollisionModel, bStaticProp ) )
goto dmxError;
LoadCollisionText( pRoot->GetValueString( "collisionText" ) );
// Deal with material groups. Ok to pass NULL for CDmeMaterialGroupList
LoadMaterialGroups( pRoot->GetValueElement< CDmeMaterialGroupList >( "materialGroupList" ) );
// Deal with bone merge directives. Ok to pass NULL DmAttribute for string array
LoadBoneMergeList( pBodyGroupList->GetAttribute( "boneMergeList" ) );
// Deal with bone merge directives. Ok to pass NULL DmAttribute for string array
// TODO: Bone keep list is a little funny, right now just treat the same as bone
// merge list which will ensure the bone is always present. This will also
// increase the bone priority and make the bone available to the server
LoadBoneMergeList( pBodyGroupList->GetAttribute( "boneKeepList" ) );
}
else
{
if ( bStaticProp )
{
MdlError( "0002: Static prop specified but no body groups present\n" );
goto dmxError;
}
// This MPP has no body groups, so maybe it's an animation only MPP?
pMainSource = AllocateDmxSource( "anim" );
}
// Deal with static props
if ( bStaticProp && pBodyGroupList )
{
// FIXME: This source should come from the skeleton;
// need to figure out if static props can deal with multiple sources
SetupStaticProp( pMainSource );
goto dmxSuccess;
}
// Nothing after here is applied if this is a static prop
LoadBoneMaskList( pRoot->GetValueElement< CDmeBoneMaskList >( "boneMaskList" ) );
// Deal with pose parameters. Ok to pass NULL for CDmePoseParameterList
LoadPoseParameterList( pRoot->GetValueElement< CDmePoseParameterList >( "poseParameterList" ) );
// Deal with animBlockSize, Ok to pass NULL for CDmeAnimBlockSize
LoadAnimBlockSize( pRoot->GetValueElement< CDmeAnimBlockSize >( "animBlockSize" ) );
// Deal with sequences. Ok to pass NULL for CDmeSequenceList
const int nSequenceCount = LoadAndCreateSequences( pMainSource, pRoot->GetValueElement< CDmeSequenceList >( "sequenceList" ), bSetUpAxis );
if ( nSequenceCount == 0 && !pBodyGroupList )
{
MdlError( "0003: MPP has no body groups and no animations\n" );
goto dmxError;
}
// Deal with include models. Ok to pass NULL for CDmeIncludeModelList
LoadIncludeModelList( pRoot->GetValueElement< CDmeIncludeModelList >( "includeModelList" ) );
// Deal with define bones. Ok to pass NULL for CDmeDefineBoneList
LoadDefineBoneList( pRoot->GetValueElement< CDmeDefineBoneList >( "defineBoneList" ) );
// Deal with hitbox sets. Ok to pass NULL for CDmeHitboxSetList
LoadHitboxSetList( pRoot->GetValueElement< CDmeHitboxSetList >( "hitboxSetList" ) );
// Deal with boneFlexDriver
LoadBoneFlexDriverList( pRoot->GetValueElement< CDmeBoneFlexDriverList >( "boneFlexDriverList" ) );
// At this point, reorienting of skeleton defined stuff will likely be required
// Deal with KeyValues, Ok to pass NULL or empty string
LoadKeyValues( pRoot->GetValueString( "keyValues" ) );
LoadGlobalFlags( pRoot );
dmxSuccess:
fileId = pRoot->GetFileId();
g_pDataModel->RemoveFileId( fileId );
return true;
dmxError:
fileId = pRoot->GetFileId();
g_pDataModel->RemoveFileId( fileId );
return false;
#endif // MDLCOMPILE
}