|
|
#include "datacache/imdlcache.h"
#include "optimize.h"
#include "mdlcombine.h"
#include "filesystem.h"
#include "studio.h"
#include "mathlib/mathlib.h"
#include <math.h>
#include "tier1/characterset.h"
#include "keyvalues.h"
#include "vtfcombine.h"
// beware: this does not do any type of byte swapping for handling of endian issues!
CCombinerMemoryWriter g_CombinerWriter; CModelCombine g_ModelCombiner; static CModelCombine *s_pCurrentCombine = &g_ModelCombiner; static characterset_t s_BreakSet; unsigned int CModelCombine::m_nNextAssetID = 0;
#ifdef DEBUG_COMBINE
#define DebugCombineMsg( ... ) Msg( __VA_ARGS__ )
//#define TEST_MDL_COMBINE 1
//#define TEST_VTX_COMBINE 1
#else
#define DebugCombineMsg( ... )
#endif
CCombinerMemoryWriter::CCombinerMemoryWriter( ) { m_pWorkBuffer = NULL; }
CCombinerMemoryWriter::~CCombinerMemoryWriter( ) { free( m_pWorkBuffer ); }
void CCombinerMemoryWriter::Init( ) { if ( m_pWorkBuffer == NULL ) { CharacterSetBuild( &s_BreakSet, "{}()':" );
m_pWorkBuffer = ( char * )malloc( COMBINER_WORK_BUFFER_SIZE ); m_pEndBuffer = m_pWorkBuffer + COMBINER_WORK_BUFFER_SIZE - 1; }
memset( m_pWorkBuffer, 0, COMBINER_WORK_BUFFER_SIZE );
m_pWriteArea = m_pWritePos = NULL; #ifdef DEBUG_COMBINE
m_pErrorPos = NULL; #endif // DEBUG_COMBINE
m_nWriteArea = -1;
InitWriteArea( WRITE_AREA_VVD, m_pWorkBuffer ); SetWriteArea( WRITE_AREA_VVD ); }
void CCombinerMemoryWriter::InitWriteArea( int nArea, char *pPosition ) { pPosition = ( char * )( ( intp )( pPosition + 15 ) & ( ~15 ) ); m_pSaveWriteArea[ nArea ] = m_pSaveWritePos[ nArea ] = pPosition; }
void CCombinerMemoryWriter::SetWriteArea( int nArea ) { if ( m_nWriteArea != -1 ) { m_pSaveWriteArea[ m_nWriteArea ] = m_pWriteArea; m_pSaveWritePos[ m_nWriteArea ] = m_pWritePos; }
m_nWriteArea = nArea; if ( m_nWriteArea != -1 ) { m_pWriteArea = m_pSaveWriteArea[ m_nWriteArea ]; m_pWritePos = m_pSaveWritePos[ m_nWriteArea ]; } }
char *CCombinerMemoryWriter::AllocWrite( int nSize ) { char *pOrigPos = m_pWritePos;
#ifdef DEBUG_COMBINE
if ( m_pErrorPos >= m_pWritePos && m_pErrorPos < m_pWritePos + nSize ) { Msg( "AllocFailure: %d offset %d\n", m_pErrorPos - m_pWriteArea, m_pErrorPos - m_pWritePos ); Assert( 0 ); } #endif // DEBUG_COMBINE
m_pWritePos += nSize;
if ( m_pWritePos > m_pEndBuffer ) { AssertMsg( false, "Internal model combiner buffer not large enough" ); V_sprintf_safe( g_ModelCombiner.GetResults()->m_szErrorMessage, "Internal model combiner buffer not large enough" ); throw( COMBINE_RESULT_FLAG_OUT_OF_MEMORY ); }
return pOrigPos; }
char *CCombinerMemoryWriter::WriteOffset( int &nOffsetIndex ) { nOffsetIndex = ( m_pWritePos - m_pWriteArea );
return m_pWritePos; }
char *CCombinerMemoryWriter::WriteOffset( int &nOffsetIndex, void *pBasePtr ) { nOffsetIndex = ( ( byte * )m_pWritePos - ( byte * )pBasePtr );
return m_pWritePos; }
char *CCombinerMemoryWriter::WriteOffset( short &nOffsetIndex, void *pBasePtr ) { nOffsetIndex = ( ( byte * )m_pWritePos - ( byte * )pBasePtr );
return m_pWritePos; }
char *CCombinerMemoryWriter::WriteBuffer( const void *pData, int nSize ) { char *pOrigPos = AllocWrite( nSize );
memcpy( pOrigPos, pData, nSize );
return pOrigPos; }
char *CCombinerMemoryWriter::WriteBufferWithOffset( const void *pData, int nSize, int &nOffsetIndex ) { WriteOffset( nOffsetIndex );
return WriteBuffer( pData, nSize ); }
char *CCombinerMemoryWriter::WriteString( const char *pszString ) { return WriteBuffer( pszString, strlen( pszString ) + 1 ); }
char *CCombinerMemoryWriter::WriteText( const char *pszString ) { return WriteBuffer( pszString, strlen( pszString ) ); }
void CCombinerMemoryWriter::AlignWrite( int nAlignSize ) { #ifdef DEBUG_COMBINE
char *pPriorPos = m_pWritePos; #endif // DEBUG_COMBINE
nAlignSize--; m_pWritePos = ( char * )( ( intp )( m_pWritePos + nAlignSize ) & ( ~nAlignSize ) );
#ifdef DEBUG_COMBINE
if ( m_pErrorPos >= pPriorPos && m_pErrorPos < m_pWritePos ) { DebugCombineMsg( "AlignFailure: %d offset %d\n", m_pErrorPos - m_pWriteArea, m_pErrorPos - m_pWritePos ); Assert( 0 ); } #endif // DEBUG_COMBINE
if ( m_pWritePos > m_pEndBuffer ) { Assert( 0 ); V_sprintf_safe( g_ModelCombiner.GetResults()->m_szErrorMessage, "Internal model combiner buffer not large enough" ); throw( COMBINE_RESULT_FLAG_OUT_OF_MEMORY ); } }
CModelCombine::CModelCombine( ) { }
CModelCombine::~CModelCombine( ) { }
void CModelCombine::Init( TCombinedStudioData *pCombinedStudioData ) { m_pCombinedStudioData = pCombinedStudioData;
memset( &m_pCombinedStudioData->m_Results, 0, sizeof( m_pCombinedStudioData->m_Results ) ); for ( int nGroup = 0; nGroup < COMBINER_MAX_ATLAS_GROUPS; nGroup++ ) { memset( &m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures, 0, sizeof( m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures ) ); memset( &m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_nCombinedTextureSizes, 0, sizeof( m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_nCombinedTextureSizes ) ); m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedMaterial = NULL; memset( &m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_szCombinedMaterialName, 0, sizeof( m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_szCombinedMaterialName ) ); }
// we need to make sure we don't have any periods, as other routines may strip off those as an extension
for( char *pszTestChar = m_pCombinedStudioData->m_szCombinedModelName; *pszTestChar; pszTestChar++ ) { if ( *pszTestChar == '.' ) { *pszTestChar = '_'; } }
g_CombinerWriter.Init(); }
void CModelCombine::BeginStringTable( ) { strings[ 0 ].base = NULL; strings[ 0 ].ptr = NULL; strings[ 0 ].string = ""; strings[ 0 ].dupindex = -1; numStrings = 1; }
//-----------------------------------------------------------------------------
// Purpose: add a string to the file-global string table.
// Keep track of fixup locations
//-----------------------------------------------------------------------------
void CModelCombine::AddToStringTable( void *base, int *ptr, const char *string ) { if ( numStrings >= COMBINER_MAX_STRINGS ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many strings for string table of size %d", COMBINER_MAX_STRINGS ); throw( COMBINE_RESULT_FLAG_TOO_MANY_STRINGS ); }
for ( int i = 0; i < numStrings; i++ ) { if ( !string || !strcmp( string, strings[ i ].string ) ) { strings[ numStrings ].base = ( byte * )base; strings[ numStrings ].ptr = ptr; strings[ numStrings ].string = string; strings[ numStrings ].dupindex = i; numStrings++; return; } }
strings[ numStrings ].base = ( byte * )base; strings[ numStrings ].ptr = ptr; strings[ numStrings ].string = string; strings[ numStrings ].dupindex = -1; numStrings++; }
//-----------------------------------------------------------------------------
// Purpose: Write out stringtable
// fixup local pointers
//-----------------------------------------------------------------------------
void CModelCombine::WriteStringTable( ) { // force null at first address
strings[ 0 ].addr = ( byte * )g_CombinerWriter.GetWritePos(); g_CombinerWriter.WriteBuffer( "", 1 ); // null string
// save all the rest
for ( int i = 1; i < numStrings; i++ ) { if ( strings[ i ].dupindex == -2 ) { // initial filler to match string tables up
strings[ i ].addr = ( byte * )g_CombinerWriter.GetWritePos(); g_CombinerWriter.WriteBuffer( strings[ i ].string, strlen( strings[ i ].string ) + 1 ); } else if ( strings[ i ].dupindex == -1 ) { // not in table yet
// calc offset relative to local base
*strings[ i ].ptr = ( byte * )g_CombinerWriter.GetWritePos() - strings[ i ].base; // keep track of address in case of duplication
strings[ i ].addr = ( byte * )g_CombinerWriter.GetWritePos(); // copy string data, add a terminating \0
g_CombinerWriter.WriteBuffer( strings[ i ].string, strlen( strings[ i ].string ) + 1 ); } else { // already in table, calc offset of existing string relative to local base
*strings[ i ].ptr = strings[ strings[ i ].dupindex ].addr - strings[ i ].base; } }
g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::VerifyField( int nField, const char *pszDescription ) { #ifdef DEBUG_COMBINE
int *pOriginal = ( int * )( ( ( byte * )m_pStudioHdr[ 0 ] ) + nField ); int *pCombined = ( int * )( ( ( byte * )m_pCombinedStudioHdr ) + nField );
DebugCombineMsg( "Verify Field %s: %d / %d\n", pszDescription, *pOriginal, *pCombined ); #endif // DEBUG_COMBINE
}
void CModelCombine::VerifyField2( int nField, const char *pszDescription ) { #ifdef DEBUG_COMBINE
int *pOriginal = ( int * )( ( ( byte * )m_pStudioHdr2[ 0 ] ) + nField ); int *pCombined = ( int * )( ( ( byte * )m_pCombinedStudioHdr2 ) + nField );
DebugCombineMsg( "Verify Field2 %s: %d / %d\n", pszDescription, *pOriginal, *pCombined ); #endif // DEBUG_COMBINE
}
void CModelCombine::VerifyOffset( void *pPtr, const char *pszDescription, void *pWritePos ) { #ifdef DEBUG_COMBINE
int pV1 = ( ( char * )pPtr ) - ( ( char * ) m_pStudioHdr[ 0 ] );
if ( pWritePos == 0 ) { pWritePos = m_pWritePos; } int pV2 = ( char * )pWritePos - m_pWriteArea;
DebugCombineMsg( "Verify Offset %s: %d / %d ( %d )\n", pszDescription, pV1, pV2, pV2 - pV1 ); #endif // DEBUG_COMBINE
}
bool CModelCombine::Resolve( ) { char szFileName[ MAX_PATH ];
m_pCombinedStudioData->m_Results.m_nCombinedResults = COMBINE_RESULT_FLAG_OK; m_pCombinedStudioData->m_Results.m_szErrorMessage[ 0 ] = 0; m_pCombinedStudioData->m_Results.m_szErrorDetails[ 0 ] = 0; m_pCombinedStudioData->m_Results.m_nDetailedError = COMBINED_DETAIL_ERROR_NOT_SPECIFIED;
memset( MDL_Data, 0, sizeof( MDL_Data ) ); memset( VTX_Data, 0, sizeof( VTX_Data ) ); memset( VVD_Data, 0, sizeof( VVD_Data ) );
try { if ( STRING(m_pCombinedStudioData->m_ModelInputData[ 0 ].m_iszModelName)[ 0 ] == 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "No primary model specified" ); throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE ); }
double flStartLoadTime = Plat_FloatTime();
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { bool bResult;
MDL_Data[ nModel ] = new CUtlBuffer(); V_strcpy_safe( szFileName, STRING(m_pCombinedStudioData->m_ModelInputData[ nModel ].m_iszModelName) ); Q_SetExtension( szFileName, ".mdl", sizeof( szFileName ) ); bResult = g_pFullFileSystem->ReadFile( szFileName, "GAME", *MDL_Data[ nModel ], 0 ); if ( !bResult ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Missing asset file: %s", szFileName ); throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE ); } m_pStudioHdr[ nModel ] = ( studiohdr_t * )MDL_Data[ nModel ]->PeekGet(); m_pStudioHdr2[ nModel ] = ( studiohdr2_t * )MDL_Data[ nModel ]->PeekGet( m_pStudioHdr[ nModel ]->studiohdr2index );
VTX_Data[ nModel ] = new CUtlBuffer(); V_strcpy_safe( szFileName, STRING(m_pCombinedStudioData->m_ModelInputData[ nModel ].m_iszModelName) ); Q_SetExtension( szFileName, ".dx90.vtx", sizeof( szFileName ) ); // GetVTXExtension() private :(
bResult = g_pFullFileSystem->ReadFile( szFileName, "GAME", *VTX_Data[ nModel ], 0 ); if ( !bResult ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Missing asset file: %s", szFileName ); throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE ); }
VVD_Data[ nModel ] = new CUtlBuffer(); V_strcpy_safe( szFileName, STRING(m_pCombinedStudioData->m_ModelInputData[ nModel ].m_iszModelName) ); Q_SetExtension( szFileName, ".vvd", sizeof( szFileName ) ); bResult = g_pFullFileSystem->ReadFile( szFileName, "GAME", *VVD_Data[ nModel ], 0 ); if ( !bResult ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Missing asset file: %s", szFileName ); throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE ); } m_pVertexFileHeader[ nModel ] = ( vertexFileHeader_t * )VVD_Data[ nModel ]->PeekGet(); }
m_pCombinedStudioData->m_Results.m_flModelLoadDuration += ( float )( Plat_FloatTime() - flStartLoadTime );
CombineTextures();
double flStartCombineTime = Plat_FloatTime();
DetermineMasterBoneList();
CombineVVD();
CombineVTX();
#ifdef TEST_MDL_COMBINE
CombineMDL( true ); TestCombineMDL(); #else
CombineMDL( false ); #endif
g_CombinerWriter.SetWriteArea( -1 ); m_pCombinedStudioData->m_Results.m_flModelCombineDuration += ( float )( Plat_FloatTime() - flStartCombineTime ); m_pCombinedStudioData->m_Results.m_nCombinedResults = COMBINE_RESULT_FLAG_OK; } catch( ECombinedResult nFlags ) { m_pCombinedStudioData->m_Results.m_nCombinedResults = nFlags; }
GetTextureCombiner().Cleanup();
for( int i = 0; i < m_pCombinedStudioData->m_nNumModels; i++ ) { delete MDL_Data[ i ]; delete VTX_Data[ i ]; delete VVD_Data[ i ]; }
return ( m_pCombinedStudioData->m_Results.m_nCombinedResults == COMBINE_RESULT_FLAG_OK ); }
void CModelCombine::DetermineMasterBoneList( ) { studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
if ( pPrimaryStudioHdr->numbones > COMBINER_MAX_BONES ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many bones in primary model: %d > %d", pPrimaryStudioHdr->numbones, COMBINER_MAX_BONES ); throw( COMBINE_RESULT_FLAG_TOO_MANY_BONES ); }
memset( m_nMasterToLocalBoneRemap, -1, sizeof( m_nMasterToLocalBoneRemap ) );
m_nNumMasterBones = 0; for( int nBone = 0; nBone < pPrimaryStudioHdr->numbones; nBone++ ) { const mstudiobone_t *pOrigBone = pPrimaryStudioHdr->pBone( nBone );
m_pMasterBoneList[ m_nNumMasterBones ] = pOrigBone; m_nBoneModelOwner[ m_nNumMasterBones ] = 0; m_nBoneRemap[ 0 ][ nBone ] = m_nNumMasterBones; m_nMasterToLocalBoneRemap[ 0 ][ m_nNumMasterBones ] = nBone;
m_nNumMasterBones++; }
for( int nModel = 1; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { studiohdr_t *pSecondaryStudioHdr = m_pStudioHdr[ nModel ];
for( int nBone = 0; nBone < pSecondaryStudioHdr->numbones; nBone++ ) { const mstudiobone_t *pOrigBone = pSecondaryStudioHdr->pBone( nBone ); char *pszName = pOrigBone->pszName(); bool bFound = false;
for ( int nBoneSearch = 0 ; nBoneSearch < m_nNumMasterBones; nBoneSearch++ ) { if ( strcmpi( m_pMasterBoneList[ nBoneSearch ]->pszName(), pszName ) == 0 ) { m_nBoneRemap[ nModel ][ nBone ] = nBoneSearch; m_nMasterToLocalBoneRemap[ nModel ][ nBoneSearch ] = nBone; bFound = true; break; } }
if ( bFound == false ) { if ( m_nNumMasterBones >= COMBINER_MAX_BONES ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many bones from secondary model %s", pSecondaryStudioHdr->pszName() ); throw( COMBINE_RESULT_FLAG_TOO_MANY_BONES ); }
m_pMasterBoneList[ m_nNumMasterBones ] = pOrigBone; m_nBoneModelOwner[ m_nNumMasterBones ] = nModel; m_nBoneRemap[ nModel ][ nBone ] = m_nNumMasterBones; m_nMasterToLocalBoneRemap[ nModel ][ m_nNumMasterBones ] = nBone;
m_nNumMasterBones++; } } } }
void CModelCombine::CombineMDL_PreintStrings( ) { char *pStringData = m_pStudioHdr2[ 0 ]->pszName(); // this is the first entry in the string table;
char *pEndData = ( ( char * ) m_pStudioHdr[ 0 ] ) + m_pStudioHdr[ 0 ]->length;
while( pStringData < pEndData ) { if ( numStrings >= COMBINER_MAX_STRINGS ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many initial strings %d > %d", numStrings, COMBINER_MAX_STRINGS ); throw( COMBINE_RESULT_FLAG_TOO_MANY_STRINGS ); }
strings[ numStrings ].base = NULL; strings[ numStrings ].ptr = NULL; strings[ numStrings ].string = pStringData; strings[ numStrings ].dupindex = -2; numStrings++;
pStringData += strlen( pStringData ) + 1; } }
void CModelCombine::WriteBoneProc( int nSize, int nType1, int nType2 ) { for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ ) { const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ]; mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
if ( pNewBone->proctype == nType1 || pNewBone->proctype == nType2 ) { char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), nSize ); pNewBone->procindex = (byte *)pPos - (byte *)pNewBone; } }
g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::WriteBoneQuatInterp( ) { for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ ) { const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ]; mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
if ( pNewBone->proctype == STUDIO_PROC_QUATINTERP ) { mstudioquatinterpbone_t *pOrigProc = ( mstudioquatinterpbone_t * )pOrigBone->pProcedure(); mstudioquatinterpbone_t *pNewProc = ( mstudioquatinterpbone_t * )pNewBone->pProcedure();
char *pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTrigger( 0 ), pOrigProc->numtriggers * sizeof( mstudioquatinterpinfo_t ) ); pNewProc->triggerindex = (byte *)pPos - (byte *)pNewProc; } }
// AlignWrite( 4 );
}
void CModelCombine::WriteBoneTwist( ) { for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ ) { const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ]; mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
if ( pNewBone->proctype == STUDIO_PROC_TWIST_MASTER || pNewBone->proctype == STUDIO_PROC_TWIST_SLAVE ) { mstudiotwistbone_t *pOrigProc = ( mstudiotwistbone_t * )pOrigBone->pProcedure(); mstudiotwistbone_t *pNewProc = ( mstudiotwistbone_t * )pNewBone->pProcedure();
char *pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudiotwistbonetarget_t ) ); pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
g_CombinerWriter.AlignWrite( 4 ); } }
// AlignWrite( 4 );
}
void CModelCombine::WriteBoneConstraints( ) { for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ ) { const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ]; mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
// make this not repeat the same code
switch( pNewBone->proctype ) { case STUDIO_PROC_POINT_CONSTRAINT: { char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudiopointconstraint_t ) ); pNewBone->procindex = (byte *)pPos - (byte *)pNewBone; g_CombinerWriter.AlignWrite( 4 );
mstudiopointconstraint_t *pOrigProc = ( mstudiopointconstraint_t * )pOrigBone->pProcedure(); mstudiopointconstraint_t *pNewProc = ( mstudiopointconstraint_t * )pNewBone->pProcedure();
pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) ); pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
g_CombinerWriter.AlignWrite( 4 ); } break;
case STUDIO_PROC_ORIENT_CONSTRAINT: { char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudioorientconstraint_t ) ); pNewBone->procindex = (byte *)pPos - (byte *)pNewBone; g_CombinerWriter.AlignWrite( 4 );
mstudioorientconstraint_t *pOrigProc = ( mstudioorientconstraint_t * )pOrigBone->pProcedure(); mstudioorientconstraint_t *pNewProc = ( mstudioorientconstraint_t * )pNewBone->pProcedure();
pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) ); pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
g_CombinerWriter.AlignWrite( 4 ); } break;
case STUDIO_PROC_AIM_CONSTRAINT: { char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudioaimconstraint_t ) ); pNewBone->procindex = (byte *)pPos - (byte *)pNewBone; g_CombinerWriter.AlignWrite( 4 );
mstudioaimconstraint_t *pOrigProc = ( mstudioaimconstraint_t * )pOrigBone->pProcedure(); mstudioaimconstraint_t *pNewProc = ( mstudioaimconstraint_t * )pNewBone->pProcedure();
pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) ); pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
g_CombinerWriter.AlignWrite( 4 ); } break;
case STUDIO_PROC_PARENT_CONSTRAINT: { char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudioparentconstraint_t ) ); pNewBone->procindex = (byte *)pPos - (byte *)pNewBone; g_CombinerWriter.AlignWrite( 4 );
mstudioparentconstraint_t *pOrigProc = ( mstudioparentconstraint_t * )pOrigBone->pProcedure(); mstudioparentconstraint_t *pNewProc = ( mstudioparentconstraint_t * )pNewBone->pProcedure();
pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) ); pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
g_CombinerWriter.AlignWrite( 4 ); } break; } } }
void CModelCombine::WriteBoneAttachments( ) { g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localattachmentindex ); m_pCombinedStudioHdr->numlocalattachments = 0;
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
int nNumAttachments = pStudioHdr->numlocalattachments;
for( int nAttachment = 0; nAttachment < nNumAttachments; nAttachment++ ) { m_pCombinedStudioHdr->numlocalattachments++;
mstudioattachment_t *pOrigAttachment = pStudioHdr->pLocalAttachment( nAttachment ); mstudioattachment_t *pNewAttachment = m_pCombinedStudioHdr->pLocalAttachment( m_pCombinedStudioHdr->numlocalattachments - 1 );
g_CombinerWriter.WriteBuffer( pOrigAttachment, sizeof( *pOrigAttachment ) ); AddToStringTable( pNewAttachment, &pNewAttachment->sznameindex, pOrigAttachment->pszName() ); RemapBone( nModel, pNewAttachment->localbone );
} }
g_CombinerWriter.AlignWrite( 4 );
VerifyField( offsetof( studiohdr_t, localattachmentindex ), "BoneAttachments" ); }
void CModelCombine::WriteHitBoxes( ) { g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->hitboxsetindex ); m_pCombinedStudioHdr->numhitboxsets = 0;
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { if ( nModel > 0 ) { // for now, we are only interested in the hitbox sets of the primary model
break; }
studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
int nNumHitBoxSets = pStudioHdr->numhitboxsets;
for( int nHitBoxSet = 0; nHitBoxSet < nNumHitBoxSets; nHitBoxSet++ ) { m_pCombinedStudioHdr->numhitboxsets++; mstudiohitboxset_t *pOrigHitBoxSet = pStudioHdr->pHitboxSet( nHitBoxSet ); mstudiohitboxset_t *pNewHitBoxSet = m_pCombinedStudioHdr->pHitboxSet( m_pCombinedStudioHdr->numhitboxsets - 1 );
g_CombinerWriter.WriteBuffer( pOrigHitBoxSet, sizeof( *pOrigHitBoxSet ) ); AddToStringTable( pNewHitBoxSet, &pNewHitBoxSet->sznameindex, pOrigHitBoxSet->pszName() ); } }
g_CombinerWriter.AlignWrite( 4 );
for( int nHitBoxSet = 0; nHitBoxSet < m_pCombinedStudioHdr->numhitboxsets; nHitBoxSet++ ) { mstudiohitboxset_t *pNewHitBoxSet = m_pCombinedStudioHdr->pHitboxSet( nHitBoxSet );
pNewHitBoxSet->numhitboxes = 0; g_CombinerWriter.WriteOffset( pNewHitBoxSet->hitboxindex, pNewHitBoxSet );
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
if ( nModel > 0 ) { break; } if ( nHitBoxSet < pStudioHdr->numhitboxsets ) { mstudiohitboxset_t *pOrigHitBoxSet = pStudioHdr->pHitboxSet( nHitBoxSet );
for( int nHitBox = 0; nHitBox < pOrigHitBoxSet->numhitboxes; nHitBox++ ) { pNewHitBoxSet->numhitboxes++; mstudiobbox_t *pOrigHitBox = pOrigHitBoxSet->pHitbox( nHitBox ); mstudiobbox_t *pNewHitBox = pNewHitBoxSet->pHitbox( pNewHitBoxSet->numhitboxes - 1 );
g_CombinerWriter.WriteBuffer( pOrigHitBox, sizeof( *pOrigHitBox ) ); AddToStringTable( pNewHitBox, &pNewHitBox->szhitboxnameindex, pOrigHitBox->pszHitboxName() ); RemapBone( nModel, pNewHitBox->bone ); } } }
g_CombinerWriter.AlignWrite( 4 ); }
VerifyField( offsetof( studiohdr_t, hitboxsetindex ), "HitBoxes" ); }
// compare function for qsort below
int CModelCombine::BoneNameCompare( const void *elem1, const void *elem2 ) { int index1 = *(byte *)elem1; int index2 = *(byte *)elem2;
// compare bones by name
return strcmpi( s_pCurrentCombine->m_pMasterBoneList[ index1 ]->pszName(), s_pCurrentCombine->m_pMasterBoneList[ index2 ]->pszName() ); }
void CModelCombine::WriteBoneTable( ) { byte *pBoneTable = ( byte * )g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->bonetablebynameindex ); g_CombinerWriter.AllocWrite( sizeof( *pBoneTable ) * m_pCombinedStudioHdr->numbones );
for ( int i = 0; i < m_pCombinedStudioHdr->numbones; i++ ) { pBoneTable[ i ] = i; }
qsort( pBoneTable, m_pCombinedStudioHdr->numbones, sizeof( byte ), BoneNameCompare ); }
void CModelCombine::CombineMDL_Bones( ) { studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
AddToStringTable( m_pCombinedStudioHdr, &m_pCombinedStudioHdr->surfacepropindex, pPrimaryStudioHdr->pszSurfaceProp() );
int *nFlags = ( int * )stackalloc( sizeof( int ) * m_nNumMasterBones ); memset( nFlags, 0, sizeof( int ) * m_nNumMasterBones );
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet(); int nBoneTest = 0; int nBoneProp = 0;
if ( nModel > 0 ) { nBoneTest = BONE_USED_BY_VERTEX_AT_LOD( pOrigHeader->numLODs - 1 );
for( int nLOD = pOrigHeader->numLODs; nLOD < m_pCombinedHardwareHeader->numLODs; nLOD++ ) { nBoneProp |= BONE_USED_BY_VERTEX_AT_LOD( nLOD ); } }
for( int nBone = 0; nBone < m_pStudioHdr[ nModel ]->numbones; nBone++ ) { nFlags[ m_nBoneRemap[ nModel ][ nBone ] ] |= m_pStudioHdr[ nModel ]->pBone( nBone )->flags;
if ( nModel > 0 && pOrigHeader->numLODs < m_pCombinedHardwareHeader->numLODs ) { if ( ( m_pStudioHdr[ nModel ]->pBone( nBone )->flags & nBoneTest ) != 0 ) { nFlags[ m_nBoneRemap[ nModel ][ nBone ] ] |= nBoneProp; } } } }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->boneindex ); m_pCombinedStudioHdr->numbones = 0; for( int nBone = 0; nBone < pPrimaryStudioHdr->numbones; nBone++ ) { const mstudiobone_t *pOrigBone = pPrimaryStudioHdr->pBone( nBone ); mstudiobone_t *pNewBone = ( mstudiobone_t * )g_CombinerWriter.WriteBuffer( pOrigBone, sizeof( mstudiobone_t ) );
AddToStringTable( pNewBone, &pNewBone->sznameindex, pOrigBone->pszName() ); AddToStringTable( pNewBone, &pNewBone->surfacepropidx, pOrigBone->pszSurfaceProp() );
pNewBone->flags = nFlags[ nBone ]; m_pCombinedStudioHdr->numbones++; }
for( int nBone = m_pCombinedStudioHdr->numbones; nBone < m_nNumMasterBones; nBone++ ) { const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ]; mstudiobone_t *pNewBone = ( mstudiobone_t * )g_CombinerWriter.WriteBuffer( pOrigBone, sizeof( mstudiobone_t ) );
AddToStringTable( pNewBone, &pNewBone->sznameindex, pOrigBone->pszName() ); AddToStringTable( pNewBone, &pNewBone->surfacepropidx, pOrigBone->pszSurfaceProp() );
if ( pNewBone->parent >= 0 ) { pNewBone->parent = m_nBoneRemap[ m_nBoneModelOwner[ nBone ] ][ pNewBone->parent ]; }
pNewBone->flags = nFlags[ nBone ]; m_pCombinedStudioHdr->numbones++; }
g_CombinerWriter.AlignWrite( 4 );
WriteBoneProc( sizeof( mstudioaxisinterpbone_t ), STUDIO_PROC_AXISINTERP ); WriteBoneProc( sizeof( mstudioquatinterpbone_t ), STUDIO_PROC_QUATINTERP ); WriteBoneQuatInterp(); WriteBoneProc( sizeof( mstudiojigglebone_t ), STUDIO_PROC_JIGGLE ); WriteBoneProc( sizeof( mstudioaimatbone_t ), STUDIO_PROC_AIMATBONE, STUDIO_PROC_AIMATATTACH ); // aimAttach needs fixup
WriteBoneProc( sizeof( mstudiotwistbone_t ), STUDIO_PROC_TWIST_MASTER, STUDIO_PROC_TWIST_SLAVE ); WriteBoneTwist(); WriteBoneConstraints();
if ( pPrimaryStudioHdr->numbonecontrollers > 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has bone controllers: %s", pPrimaryStudioHdr->pszName() ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); } g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->bonecontrollerindex ); g_CombinerWriter.AlignWrite( 4 );
WriteBoneAttachments();
WriteHitBoxes();
WriteBoneTable();
g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::WriteAnimation( mstudioanimdesc_t *pOrigAnim, void *pAnimData, int nFrameSize ) { if ( ( pOrigAnim->flags & STUDIO_FRAMEANIM ) != 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has anim frames" ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); } else { mstudio_rle_anim_t *pOrigFrame = ( mstudio_rle_anim_t * )( pAnimData );
while( 1 ) { int nSize = sizeof( mstudio_rle_anim_t ); if ( pOrigFrame->flags & STUDIO_ANIM_RAWROT2 ) { nSize += sizeof( Quaternion64 ); } if ( pOrigFrame->flags & STUDIO_ANIM_RAWPOS ) { nSize += sizeof( Vector48 ); } if ( pOrigFrame->flags & STUDIO_ANIM_ANIMROT ) { nSize += sizeof( mstudioanim_valueptr_t ); } if ( pOrigFrame->flags & STUDIO_ANIM_ANIMPOS ) { nSize += sizeof( mstudioanim_valueptr_t ); }
if ( pOrigFrame->flags & STUDIO_ANIM_ANIMROT ) { mstudioanim_valueptr_t *rotvptr = pOrigFrame->pRotV(); for (int k = 0; k < 3; k++) { mstudioanimvalue_t *pAnimValue = rotvptr->pAnimvalue( k ); if ( pAnimValue != NULL ) { int nCount = nFrameSize; while( nCount > 0 ) { nSize += sizeof( *pAnimValue ) * ( pAnimValue->num.valid + 1 ); nCount -= pAnimValue->num.total; pAnimValue += pAnimValue->num.valid + 1; } } }
} if ( pOrigFrame->flags & STUDIO_ANIM_ANIMPOS ) { mstudioanim_valueptr_t *posvptr = pOrigFrame->pPosV(); for (int k = 0; k < 3; k++) { mstudioanimvalue_t *pAnimValue = posvptr->pAnimvalue( k ); if ( pAnimValue != NULL ) { int nCount = nFrameSize; while( nCount > 0 ) { nSize += sizeof( *pAnimValue ) * ( pAnimValue->num.valid + 1 ); nCount -= pAnimValue->num.total; pAnimValue += pAnimValue->num.valid + 1; } } } }
// all of the indexes are relative, so a direct copy should be safe
g_CombinerWriter.WriteBuffer( pOrigFrame, nSize ); // DebugCombineMsg( "%d: %d\n", pOrigFrame->bone, pOrigFrame->nextoffset - nSize );
if ( pOrigFrame->nextoffset == 0 ) { break; } else { pOrigFrame = pOrigFrame->pNext(); VerifyOffset( pOrigFrame, "Start rle_anim" ); } }
g_CombinerWriter.AllocWrite( sizeof( mstudio_rle_anim_t ) ); // bug in studiomdl which adds an extra one of these
}
g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::CombineMDL_Anims( ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
VerifyOffset( pStudioHdr->pLocalAnimdesc( 0 ), "Start Animations" );
g_CombinerWriter.WriteBufferWithOffset( pStudioHdr->pLocalAnimdesc( 0 ), sizeof( mstudioanimdesc_t ) * pStudioHdr->numlocalanim, m_pCombinedStudioHdr->localanimindex ); g_CombinerWriter.AlignWrite( 4 ); m_pCombinedStudioHdr->numlocalanim = pStudioHdr->numlocalanim;
for( int nAnim = 0; nAnim < m_pCombinedStudioHdr->numlocalanim; nAnim++ ) { mstudioanimdesc_t *pOrigAnim = pStudioHdr->pLocalAnimdesc( nAnim ); mstudioanimdesc_t *pNewAnim = m_pCombinedStudioHdr->pLocalAnimdesc( nAnim );
pNewAnim->baseptr = ( byte * )g_CombinerWriter.GetWriteArea() - ( byte * )pNewAnim; AddToStringTable( pNewAnim, &pNewAnim->sznameindex, pOrigAnim->pszName() );
int nNumSections = 0;
if ( pOrigAnim->sectionframes > 1 ) { nNumSections = ( pOrigAnim->numframes / pOrigAnim->sectionframes ) + 2; // studio.cpp line 113
pNewAnim->sectionindex = ( byte * )g_CombinerWriter.AllocWrite( nNumSections * sizeof( mstudioanimsections_t ) ) - (byte *)pNewAnim; }
g_CombinerWriter.AlignWrite( 16 ); pNewAnim->sectionframes = pOrigAnim->sectionframes; g_CombinerWriter.WriteOffset( pNewAnim->animindex, pNewAnim );
DebugCombineMsg( "Anim %d: %d / %d ( %d )\n", nAnim, pOrigAnim->animindex, pNewAnim->animindex, pNewAnim->animindex - pOrigAnim->animindex );
if ( nNumSections > 1 ) { int nRemainingFrames = pOrigAnim->numframes;
for( int nSection = 0; nSection < nNumSections; nSection++ ) { mstudioanimsections_t *pOrigSection = pOrigAnim->pSection( nSection ); mstudioanimsections_t *pNewSection = pNewAnim->pSection( nSection );
VerifyOffset( pOrigSection, "Start mstudioanimsections_t", pNewSection );
*pNewSection = *pOrigSection; g_CombinerWriter.WriteOffset( pNewSection->animindex, pNewAnim );
int nFrames;
if ( nRemainingFrames > pOrigAnim->sectionframes ) { nRemainingFrames -= pOrigAnim->sectionframes; nFrames = pOrigAnim->sectionframes + 1; } else { nFrames = nRemainingFrames; nRemainingFrames = 0; }
WriteAnimation( pOrigAnim, ( ( byte * )pOrigAnim ) + pOrigSection->animindex, nFrames ); } } else { WriteAnimation( pOrigAnim, pOrigAnim->pAnimBlock( pOrigAnim->animblock, pOrigAnim->animindex ), pOrigAnim->numframes ); }
g_CombinerWriter.AlignWrite( 4 ); if ( pOrigAnim->numikrules > 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has ik rules: %s", pStudioHdr->pszName() ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); }
g_CombinerWriter.AlignWrite( 4 ); if ( pOrigAnim->numlocalhierarchy > 0 ) { g_CombinerWriter.WriteOffset( pNewAnim->localhierarchyindex, pNewAnim ); g_CombinerWriter.WriteBuffer( pOrigAnim->pHierarchy( 0 ), pOrigAnim->numlocalhierarchy * sizeof( mstudiolocalhierarchy_t ) ); for( int nLocalHierarchy = 0; nLocalHierarchy < pOrigAnim->numlocalhierarchy; nLocalHierarchy++ ) { mstudiolocalhierarchy_t *pOrigLocalHierarchy = pOrigAnim->pHierarchy( nLocalHierarchy ); mstudiolocalhierarchy_t *pNewLocalHierarchy = pNewAnim->pHierarchy( nLocalHierarchy );
g_CombinerWriter.WriteOffset( pNewLocalHierarchy->localanimindex, pNewLocalHierarchy );
mstudiocompressedikerror_t *pOrigCompressedIKError = pOrigLocalHierarchy->pLocalAnim(); mstudiocompressedikerror_t *pNewCompressedIKError = pNewLocalHierarchy->pLocalAnim(); g_CombinerWriter.WriteBuffer( pOrigCompressedIKError, sizeof( *pOrigCompressedIKError ) );
for( int nAnim = 0; nAnim < 6; nAnim++ ) { g_CombinerWriter.WriteOffset( pNewCompressedIKError->offset[ nAnim ], pNewCompressedIKError );
int nSize = 0; mstudioanimvalue_t *pAnimValue = pOrigCompressedIKError->pAnimvalue( nAnim ); if ( pAnimValue != NULL ) { int nCount = pOrigAnim->numframes; while( nCount > 0 ) { nSize += sizeof( *pAnimValue ) * ( pAnimValue->num.valid + 1 ); nCount -= pAnimValue->num.total; pAnimValue += pAnimValue->num.valid + 1; } }
g_CombinerWriter.WriteBuffer( pOrigCompressedIKError->pAnimvalue( nAnim ), nSize ); } } }
g_CombinerWriter.AlignWrite( 4 ); }
for( int nAnim = 0; nAnim < m_pCombinedStudioHdr->numlocalanim; nAnim++ ) { mstudioanimdesc_t *pOrigAnim = pStudioHdr->pLocalAnimdesc( nAnim ); mstudioanimdesc_t *pNewAnim = m_pCombinedStudioHdr->pLocalAnimdesc( nAnim );
if ( pOrigAnim->nummovements > 0 ) { g_CombinerWriter.WriteOffset( pNewAnim->movementindex, pNewAnim ); VerifyOffset( pOrigAnim->pMovement( 0 ), "Start Movement" ); g_CombinerWriter.WriteBuffer( pOrigAnim->pMovement( 0 ), sizeof( mstudiomovement_t ) * pOrigAnim->nummovements ); g_CombinerWriter.AlignWrite( 4 ); } }
g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::CombineMDL_SequenceInfo( ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
VerifyField( offsetof( studiohdr_t, localseqindex ), "Sequence Info" ); m_pCombinedStudioHdr->numlocalseq = pStudioHdr->numlocalseq; g_CombinerWriter.WriteBufferWithOffset( pStudioHdr->pLocalSeqdesc( 0 ), sizeof( mstudioseqdesc_t ) * pStudioHdr->numlocalseq, m_pCombinedStudioHdr->localseqindex );
for( int nSequence = 0; nSequence < pStudioHdr->numlocalseq; nSequence++ ) { mstudioseqdesc_t *pOrigSequence = pStudioHdr->pLocalSeqdesc( nSequence ); mstudioseqdesc_t *pNewSequence = m_pCombinedStudioHdr->pLocalSeqdesc( nSequence );
AddToStringTable( pNewSequence, &pNewSequence->szlabelindex, pOrigSequence->pszLabel() ); AddToStringTable( pNewSequence, &pNewSequence->szactivitynameindex, pOrigSequence->pszActivityName() );
pNewSequence->baseptr = ( byte * )g_CombinerWriter.GetWriteArea() - ( byte * )pNewSequence;
if ( pOrigSequence->groupsize[ 0 ] > 1 || pOrigSequence->groupsize[ 1 ] > 1 ) { g_CombinerWriter.WriteOffset( pNewSequence->posekeyindex, pNewSequence ); int nSize = ( pOrigSequence->groupsize[ 0 ] + pOrigSequence->groupsize[ 1 ] ) * sizeof( float ); g_CombinerWriter.WriteBuffer( pOrigSequence->pPoseKey( 0, 0 ), nSize ); }
g_CombinerWriter.WriteOffset( pNewSequence->eventindex, pNewSequence ); if ( pOrigSequence->numevents > 0 ) { VerifyOffset( pOrigSequence->pEvent( 0 ), "Sequence Event" ); g_CombinerWriter.WriteBuffer( pOrigSequence->pEvent( 0 ), pOrigSequence->numevents * sizeof( mstudioevent_t ) ); for ( int nEvent = 0; nEvent < pOrigSequence->numevents; nEvent++ ) { mstudioevent_t *pOrigEvent = pOrigSequence->pEvent( nEvent ); mstudioevent_t *pNewEvent = pNewSequence->pEvent( nEvent );
if ( pOrigEvent->type == NEW_EVENT_STYLE ) { AddToStringTable( pNewEvent, &pNewEvent->szeventindex, pOrigEvent->pszEventName() ); } } } g_CombinerWriter.AlignWrite( 4 );
g_CombinerWriter.WriteOffset( pNewSequence->autolayerindex, pNewSequence ); if ( pOrigSequence->numautolayers > 0 ) { VerifyOffset( pOrigSequence->pAutolayer( 0 ), "Auto Layer" ); g_CombinerWriter.WriteBuffer( pOrigSequence->pAutolayer( 0 ), pOrigSequence->numautolayers * sizeof( mstudioautolayer_t ) ); }
if ( pOrigSequence->weightlistindex < pOrigSequence->eventindex ) { // we are using an existing weight
int nFound = -1;
for( int nPreviousSequence = 0; nPreviousSequence < nSequence; nPreviousSequence++ ) { mstudioseqdesc_t *pOrigPreviousSequence = pStudioHdr->pLocalSeqdesc( nPreviousSequence );
if ( memcmp( pOrigPreviousSequence->pBoneweight( 0 ), pOrigSequence->pBoneweight( 0 ), pStudioHdr->numbones * sizeof( float ) ) == 0 ) { nFound = nPreviousSequence; break; } }
if ( nFound != -1 ) { mstudioseqdesc_t *pNewPreviousSequence = m_pCombinedStudioHdr->pLocalSeqdesc( nFound ); pNewSequence->weightlistindex = ( ( byte * )pNewPreviousSequence->pBoneweight( 0 ) - ( byte * )pNewSequence); } else { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "was not able to find existing weight in model: %s", pStudioHdr->pszName() ); throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE ); } } else { g_CombinerWriter.WriteOffset( pNewSequence->weightlistindex, pNewSequence ); // Assert( m_nNumMasterBones == pStudioHdr->numbones ); // need to handle bone merge!
g_CombinerWriter.WriteBuffer( pOrigSequence->pBoneweight( 0 ), pStudioHdr->numbones * sizeof( float ) ); float flTempWeight = 1.0f; for( int nNewBones = pStudioHdr->numbones; nNewBones < m_nNumMasterBones; nNewBones++ ) { // more efficient ways to do this, but for now, not knowing if I can just give them all 1.0f, I'll leave it
g_CombinerWriter.WriteBuffer( &flTempWeight, sizeof( flTempWeight ) ); } }
g_CombinerWriter.WriteOffset( pNewSequence->iklockindex, pNewSequence ); if ( pOrigSequence->numiklocks > 0 ) { VerifyOffset( pOrigSequence->pIKLock( 0 ), "IK Lock" ); g_CombinerWriter.WriteBuffer( pOrigSequence->pIKLock( 0 ), pOrigSequence->numiklocks * sizeof( mstudioiklock_t ) ); } g_CombinerWriter.AlignWrite( 4 );
g_CombinerWriter.WriteOffset( pNewSequence->animindexindex, pNewSequence ); int nSize = ( pOrigSequence->groupsize[ 0 ] * pOrigSequence->groupsize[ 1 ] ) * sizeof( short ); if ( nSize > 0 ) { VerifyOffset( ( ( ( byte * )pOrigSequence ) + pOrigSequence->animindexindex ), "AnimIndexIndex" ); g_CombinerWriter.WriteBuffer( ( ( ( byte * )pOrigSequence ) + pOrigSequence->animindexindex ), nSize ); } g_CombinerWriter.AlignWrite( 4 );
g_CombinerWriter.WriteOffset( pNewSequence->keyvalueindex, pNewSequence ); if( pOrigSequence->keyvaluesize > 0 ) { VerifyOffset( ( void * )pOrigSequence->KeyValueText(), "Sequence KV" ); g_CombinerWriter.WriteBuffer( ( void * )pOrigSequence->KeyValueText(), pOrigSequence->keyvaluesize * sizeof( char ) ); } g_CombinerWriter.AlignWrite( 4 );
g_CombinerWriter.WriteOffset( pNewSequence->activitymodifierindex, pNewSequence ); if ( pOrigSequence->numactivitymodifiers > 0 ) { g_CombinerWriter.WriteBuffer( pOrigSequence->pActivityModifier( 0 ), pOrigSequence->numactivitymodifiers * sizeof( mstudioactivitymodifier_t ) ); } g_CombinerWriter.AlignWrite( 4 ); for ( int nActivityModifier = 0; nActivityModifier < pOrigSequence->numactivitymodifiers; nActivityModifier++ ) { mstudioactivitymodifier_t *pOrigActivityModifier = pOrigSequence->pActivityModifier( nActivityModifier ); mstudioactivitymodifier_t *pNewActivityModifier = pNewSequence->pActivityModifier( nActivityModifier );
AddToStringTable( pNewActivityModifier, &pNewActivityModifier->sznameindex, pOrigActivityModifier->pszName() ); }
nSize = pOrigSequence->numactivitymodifiers * sizeof( mstudioactivitymodifier_t ); byte *pTestPtr = ( ( ( byte * )pOrigSequence ) + pOrigSequence->activitymodifierindex ) + nSize; VerifyOffset( pTestPtr, "Final Sequence Write" ); }
int *pNodeName = ( int * )g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localnodenameindex ); g_CombinerWriter.AllocWrite( pStudioHdr->numlocalnodes * sizeof( *pNodeName ) ); g_CombinerWriter.AlignWrite( 4 ); for ( int nNodeIndex = 0; nNodeIndex < pStudioHdr->numlocalnodes; nNodeIndex++ ) { AddToStringTable( m_pCombinedStudioHdr, pNodeName, pStudioHdr->pszLocalNodeName( nNodeIndex ) ); pNodeName++; }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localnodeindex ); if ( pStudioHdr->numlocalnodes > 0 ) { g_CombinerWriter.WriteBuffer( pStudioHdr->pLocalTransition( 0 ), pStudioHdr->numlocalnodes * pStudioHdr->numlocalnodes * sizeof( byte ) ); }
g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::WriteModel( int nModel ) { mstudiomodel_t *pOrigModel = m_pMasterModels[ 0 ][ nModel ]; mstudiomodel_t *pNewModel = &m_pCombinedModels[ nModel ];
g_CombinerWriter.WriteOffset( pNewModel->meshindex, pNewModel ); g_CombinerWriter.WriteBuffer( pOrigModel->pMesh( 0 ), pOrigModel->nummeshes * sizeof( mstudiomesh_t ) ); g_CombinerWriter.AlignWrite( 4 ); for ( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ ) { // mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
mstudiomesh_t *pNewMesh = pNewModel->pMesh( nMesh );
pNewMesh->numvertices = m_pCombinedVertex->numLODVertexes[ 0 ]; for( int nLOD = 0; nLOD < m_pCombinedVertex->numLODs; nLOD++ ) { pNewMesh->vertexdata.numLODVertexes[ nLOD ] = m_pCombinedVertex->numLODVertexes[ nLOD ]; }
pNewMesh->modelindex = ( byte * )pNewModel - ( byte * )pNewMesh; }
// pNewModel->vertexindex = pOrigModel->vertexindex;
// externalVertexIndex += pmodel[i].numvertices * sizeof(mstudiovertex_t);
// ALIGN4( externalTangentsIndex );
// pmodel[i].tangentsindex = (int)externalTangentsIndex;
// externalTangentsIndex += pmodel[i].numvertices * sizeof( Vector4D );
g_CombinerWriter.WriteOffset( pNewModel->eyeballindex, pNewModel ); if ( pOrigModel->numeyeballs > 0 ) { g_CombinerWriter.WriteBuffer( pOrigModel->pEyeball( 0 ), pOrigModel->numeyeballs * sizeof( mstudioeyeball_t ) ); }
for ( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ ) { // mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
mstudiomesh_t *pNewMesh = pNewModel->pMesh( nMesh );
pNewMesh->numflexes = 0; g_CombinerWriter.WriteOffset( pNewMesh->flexindex, pNewMesh ); for( int nSubModel = 0; nSubModel < m_pCombinedStudioData->m_nNumModels; nSubModel++ ) { if ( !m_pMasterModels[ nSubModel ][ nModel ] ) { continue; } mstudiomesh_t *pOrigFlexMesh = m_pMasterModels[ nSubModel ][ nModel ]->pMesh( nMesh );
if ( pOrigFlexMesh->numflexes ) { // figure out the total number of flex entries for this mesh and reserve space
pNewMesh->numflexes += pOrigFlexMesh->numflexes; g_CombinerWriter.WriteBuffer( pOrigFlexMesh->pFlex( 0 ), pOrigFlexMesh->numflexes * sizeof( mstudioflex_t ) ); } }
int nTotalFlexes = 0; if ( pNewMesh->numflexes > 0 ) { g_CombinerWriter.AlignWrite( 4 );
for( int nSubModel = 0; nSubModel < m_pCombinedStudioData->m_nNumModels; nSubModel++ ) { if ( !m_pMasterModels[ nSubModel ][ nModel ] ) { continue; } mstudiomesh_t *pOrigFlexMesh = m_pMasterModels[ nSubModel ][ nModel ]->pMesh( nMesh );
if ( pOrigFlexMesh->numflexes ) { for( int nFlex = 0; nFlex < pOrigFlexMesh->numflexes; nFlex++, nTotalFlexes++ ) { mstudioflex_t *pOrigFlex = pOrigFlexMesh->pFlex( nFlex ); mstudioflex_t *pNewFlex = pNewMesh->pFlex( nTotalFlexes );
bool bWrinkleVAnim = ( pOrigFlex->vertanimtype == STUDIO_VERT_ANIM_WRINKLE ); int nVAnimDeltaSize = bWrinkleVAnim ? sizeof( mstudiovertanim_wrinkle_t ) : sizeof( mstudiovertanim_t );
g_CombinerWriter.WriteOffset( pNewFlex->vertindex, pNewFlex ); g_CombinerWriter.WriteBuffer( pOrigFlex->pBaseVertanim(), pOrigFlex->numverts * nVAnimDeltaSize );
if ( bWrinkleVAnim ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has anim wrinkle" ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
#if 0
for( int nVert = 0; nVert < pOrigFlex->numverts; nVert++ ) { mstudiovertanim_wrinkle_t *pNewVertAnimWrinkle = pNewFlex->pVertanimWrinkle( nVert ); } #endif
} else { float flDestAnimFixPointScale = m_pCombinedStudioHdr->VertAnimFixedPointScale(); float flSourceAnimFixPointScale = m_pStudioHdr[ nSubModel ]->VertAnimFixedPointScale(); Vector vUpdate;
for( int nVert = 0; nVert < pOrigFlex->numverts; nVert++ ) { mstudiovertanim_t *pOrigVertAnim = pOrigFlex->pVertanim( nVert ); mstudiovertanim_t *pNewVertAnim = pNewFlex->pVertanim( nVert );
int nVertIndex = pOrigVertAnim->index; nVertIndex += pOrigFlexMesh->vertexoffset;
vUpdate = pNewVertAnim->GetDeltaFloat(); vUpdate /= flSourceAnimFixPointScale; vUpdate *= flDestAnimFixPointScale; pNewVertAnim->SetDeltaFloat( vUpdate );
vUpdate = pNewVertAnim->GetNDeltaFloat(); vUpdate /= flSourceAnimFixPointScale; vUpdate *= flDestAnimFixPointScale; pNewVertAnim->SetNDeltaFloat( vUpdate );
pNewVertAnim->index = m_nVertexRemap[ nSubModel ][ nVertIndex ] - pNewMesh->vertexoffset; } } } } } } } }
void CModelCombine::CombineMDL_Model( ) { studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
studiohdr_t *pFlexStudioHdr = NULL;
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ]; if ( pStudioHdr->numflexdesc ) { #if 0
if ( pFlexStudioHdr ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model %s and %s both have flex specifications", pFlexStudioHdr->pszName(), pStudioHdr->pszName() ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); } #endif
pFlexStudioHdr = pStudioHdr; m_nFlexModelSource = nModel; break; } }
if ( !pFlexStudioHdr ) { pFlexStudioHdr = pPrimaryStudioHdr; m_nFlexModelSource = 0; }
VerifyField( offsetof( studiohdr_t, bodypartindex ), "Body Part Sequence" );
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->bodypartindex ); g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pBodypart( 0 ), pPrimaryStudioHdr->numbodyparts * sizeof( mstudiobodyparts_t ) );
m_pCombinedModels = ( mstudiomodel_t * )g_CombinerWriter.GetWritePos();
int nTotalModels = 0; for ( int nBodyPart = 0; nBodyPart < pPrimaryStudioHdr->numbodyparts; nBodyPart++ ) { mstudiobodyparts_t *pOrigBodyPart = pPrimaryStudioHdr->pBodypart( nBodyPart ); mstudiobodyparts_t *pNewBodyPart = m_pCombinedStudioHdr->pBodypart( nBodyPart );
AddToStringTable( pNewBodyPart, &pNewBodyPart->sznameindex, pOrigBodyPart->pszName() ); g_CombinerWriter.WriteOffset( pNewBodyPart->modelindex, pNewBodyPart );
for( int nModel = 0; nModel < pOrigBodyPart->nummodels; nModel++ ) { // build our list of master models
m_pMasterModels[ 0 ][ nTotalModels ] = m_pMasterFlexModels[ nTotalModels ] = pOrigBodyPart->pModel( nModel ); g_CombinerWriter.WriteBuffer( m_pMasterModels[ 0 ][ nTotalModels ], sizeof( mstudiomodel_t ) );
for( int nSubModels = 1; nSubModels < m_pCombinedStudioData->m_nNumModels; nSubModels++ ) { m_pMasterModels[ nSubModels ][ nTotalModels ] = NULL;
studiohdr_t *pSubStudioHdr = m_pStudioHdr[ nSubModels ]; if ( nBodyPart < pSubStudioHdr->numbodyparts ) { mstudiobodyparts_t *pSubBodyPart = pSubStudioHdr->pBodypart( nBodyPart ); if ( nModel < pSubBodyPart->nummodels ) { // if a sub model matches up to the body part
m_pCombinedModels[ nTotalModels ].numvertices += pSubBodyPart->pModel( nModel )->numvertices; if ( nSubModels == m_nFlexModelSource ) { // if it is part of the primary flex specification
m_pMasterFlexModels[ nTotalModels ] = pSubBodyPart->pModel( nModel ); } m_pMasterModels[ nSubModels ][ nTotalModels ] = pSubBodyPart->pModel( nModel ); } } } nTotalModels++; } } g_CombinerWriter.AlignWrite( 4 );
m_pCombinedStudioHdr->numflexdesc = pFlexStudioHdr->numflexdesc; m_pCombinedStudioHdr->numflexcontrollers = pFlexStudioHdr->numflexcontrollers; m_pCombinedStudioHdr->numflexrules = pFlexStudioHdr->numflexrules; m_pCombinedStudioHdr->numflexcontrollerui = pFlexStudioHdr->numflexcontrollerui;
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexdescindex ); VerifyField( offsetof( studiohdr_t, flexdescindex ), "Flex Desc" ); if ( pFlexStudioHdr->numflexdesc > 0 ) { g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexdesc( 0 ), pFlexStudioHdr->numflexdesc * sizeof( mstudioflexdesc_t ) ); } g_CombinerWriter.AlignWrite( 4 ); for ( int nFlexDesc = 0; nFlexDesc < pFlexStudioHdr->numflexdesc; nFlexDesc++ ) { mstudioflexdesc_t *pOrigFlexDesc = pFlexStudioHdr->pFlexdesc( nFlexDesc ); mstudioflexdesc_t *pNewFlexDesc = m_pCombinedStudioHdr->pFlexdesc( nFlexDesc ); AddToStringTable( pNewFlexDesc, &pNewFlexDesc->szFACSindex, pOrigFlexDesc->pszFACS() ); }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexcontrollerindex ); VerifyField( offsetof( studiohdr_t, flexdescindex ), "Flex Controller" ); if ( pFlexStudioHdr->numflexcontrollers > 0 ) { g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexcontroller( ( LocalFlexController_t )0 ), pFlexStudioHdr->numflexcontrollers * sizeof( mstudioflexcontroller_t ) ); for( int nFlexController = 0; nFlexController < pFlexStudioHdr->numflexcontrollers; nFlexController++ ) { mstudioflexcontroller_t *pOrigFlexController = pFlexStudioHdr->pFlexcontroller( ( LocalFlexController_t )nFlexController ); mstudioflexcontroller_t *pNewFlexController = m_pCombinedStudioHdr->pFlexcontroller( ( LocalFlexController_t )nFlexController );
AddToStringTable( pNewFlexController, &pNewFlexController->sznameindex, pOrigFlexController->pszName() ); AddToStringTable( pNewFlexController, &pNewFlexController->sztypeindex, pOrigFlexController->pszType() ); } }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexruleindex ); VerifyField( offsetof( studiohdr_t, flexruleindex ), "Flex Rules" ); if ( pFlexStudioHdr->numflexrules > 0 ) { g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexRule( 0 ), pFlexStudioHdr->numflexrules * sizeof( mstudioflexrule_t ) ); g_CombinerWriter.AlignWrite( 4 ); for( int nFlexRule = 0; nFlexRule < pFlexStudioHdr->numflexrules; nFlexRule++ ) { mstudioflexrule_t *pOrigFlexRule = pFlexStudioHdr->pFlexRule( nFlexRule ); mstudioflexrule_t *pNewFlexRule = m_pCombinedStudioHdr->pFlexRule( nFlexRule );
g_CombinerWriter.WriteOffset( pNewFlexRule->opindex, pNewFlexRule ); g_CombinerWriter.WriteBuffer( pOrigFlexRule->iFlexOp( 0 ), pOrigFlexRule->numops * sizeof( mstudioflexop_t ) );
g_CombinerWriter.AlignWrite( 4 ); } }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexcontrolleruiindex ); VerifyField( offsetof( studiohdr_t, flexcontrolleruiindex ), "Flex Controller UI" ); if ( pFlexStudioHdr->numflexcontrollerui > 0 ) { g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexControllerUI( 0 ), pFlexStudioHdr->numflexcontrollerui * sizeof( mstudioflexcontrollerui_t ) ); for( int nFlexControllerUI = 0; nFlexControllerUI < pFlexStudioHdr->numflexcontrollerui; nFlexControllerUI++ ) { mstudioflexcontrollerui_t *pOrigFlexControllerUI = pFlexStudioHdr->pFlexControllerUI( nFlexControllerUI ); mstudioflexcontrollerui_t *pNewFlexControllerUI = m_pCombinedStudioHdr->pFlexControllerUI( nFlexControllerUI );
AddToStringTable( pNewFlexControllerUI, &pNewFlexControllerUI->sznameindex, pOrigFlexControllerUI->pszName() );
// not worrying about remapping the controller indexes here yet
} g_CombinerWriter.AlignWrite( 4 ); }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->ikchainindex ); VerifyField( offsetof( studiohdr_t, ikchainindex ), "IK Chain" ); if ( pPrimaryStudioHdr->numikchains > 0 ) { g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pIKChain( 0 ), pPrimaryStudioHdr->numikchains * sizeof( mstudioikchain_t ) ); g_CombinerWriter.AlignWrite( 4 );
for( int nIKChain = 0; nIKChain < pPrimaryStudioHdr->numikchains; nIKChain++ ) { mstudioikchain_t *pOrigIKChain = pPrimaryStudioHdr->pIKChain( nIKChain ); mstudioikchain_t *pNewIKChain = m_pCombinedStudioHdr->pIKChain( nIKChain );
AddToStringTable( pNewIKChain, &pNewIKChain->sznameindex, pNewIKChain->pszName() );
g_CombinerWriter.WriteOffset( pNewIKChain->linkindex, pNewIKChain ); g_CombinerWriter.WriteBuffer( pOrigIKChain->pLink( 0 ), pOrigIKChain->numlinks * sizeof( mstudioiklink_t ) ); } }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localikautoplaylockindex ); VerifyField( offsetof( studiohdr_t, localikautoplaylockindex ), "IK Autoplay Lock" ); if ( pPrimaryStudioHdr->numlocalikautoplaylocks > 0 ) { g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pLocalIKAutoplayLock( 0 ), pPrimaryStudioHdr->numlocalikautoplaylocks * sizeof( mstudioiklock_t ) ); g_CombinerWriter.AlignWrite( 4 ); }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->mouthindex ); VerifyField( offsetof( studiohdr_t, mouthindex ), "Mouth" ); if ( pPrimaryStudioHdr->nummouths ) { g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pMouth( 0 ), pPrimaryStudioHdr->nummouths * sizeof( mstudiomouth_t ) ); g_CombinerWriter.AlignWrite( 4 ); }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localposeparamindex ); VerifyField( offsetof( studiohdr_t, localposeparamindex ), "Pose Param" ); if ( pPrimaryStudioHdr->numlocalposeparameters > 0 ) { g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pLocalPoseParameter( 0 ), pPrimaryStudioHdr->numlocalposeparameters * sizeof( mstudioposeparamdesc_t ) ); g_CombinerWriter.AlignWrite( 4 ); for( int nPoseParam = 0; nPoseParam < pPrimaryStudioHdr->numlocalposeparameters; nPoseParam++ ) { mstudioposeparamdesc_t *pOrigPoseParam = pPrimaryStudioHdr->pLocalPoseParameter( nPoseParam ); mstudioposeparamdesc_t *pNewPoseParam = m_pCombinedStudioHdr->pLocalPoseParameter( nPoseParam );
AddToStringTable( pNewPoseParam, &pNewPoseParam->sznameindex, pOrigPoseParam->pszName() ); } }
for( int nModel = 0; nModel < nTotalModels; nModel++ ) { WriteModel( nModel ); }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->includemodelindex ); VerifyField( offsetof( studiohdr_t, includemodelindex ), "Model Groups" ); if ( pPrimaryStudioHdr->numincludemodels > 0 ) { g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pModelGroup( 0 ), pPrimaryStudioHdr->numincludemodels * sizeof( mstudiomodelgroup_t ) ); for( int nModelGroup = 0; nModelGroup < pPrimaryStudioHdr->numincludemodels; nModelGroup++ ) { mstudiomodelgroup_t *pOrigModelGroup = pPrimaryStudioHdr->pModelGroup( nModelGroup ); mstudiomodelgroup_t *pNewModelGroup = m_pCombinedStudioHdr->pModelGroup( nModelGroup );
AddToStringTable( pNewModelGroup, &pNewModelGroup->sznameindex, pOrigModelGroup->pszName() ); } }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->animblockindex ); VerifyField( offsetof( studiohdr_t, animblockindex ), "Anim Blocks" ); if ( pPrimaryStudioHdr->numanimblocks > 0 ) { g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pAnimBlock( 0 ), pPrimaryStudioHdr->numanimblocks * sizeof( mstudioanimblock_t ) );
Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has anim blocks: %s", pPrimaryStudioHdr->pszName() ); throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE ); }
g_CombinerWriter.AlignWrite( 4 ); AddToStringTable( m_pCombinedStudioHdr, &m_pCombinedStudioHdr->szanimblocknameindex, pPrimaryStudioHdr->pszAnimBlockName() ); }
void CModelCombine::CombineMDL_Textures( ) { g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->textureindex );
m_pCombinedStudioHdr->numtextures = m_pCombinedStudioData->m_nNumAtlasGroups + m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames; // first allocate/write out "blank" texture entries (materials)
mstudiotexture_t NewTexture; memset( &NewTexture, 0, sizeof( NewTexture ) ); NewTexture.sznameindex = 0; NewTexture.used = 1; for ( int nAtlasGroup = 0; nAtlasGroup < m_pCombinedStudioHdr->numtextures; nAtlasGroup++ ) { g_CombinerWriter.WriteBuffer( &NewTexture, sizeof( mstudiotexture_t ) ); } g_CombinerWriter.AlignWrite( 4 );
// then fix up those written entries with proper name index values from the string table
for ( int nAtlasGroup = 0; nAtlasGroup < m_pCombinedStudioData->m_nNumAtlasGroups; nAtlasGroup++ ) { mstudiotexture_t *pNewTexture = m_pCombinedStudioHdr->pTexture( nAtlasGroup ); // we use a specialized name here that other systems won't understand
V_sprintf_safe( m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_szCombinedMaterialName, "!%s|%d|%hu|%d!", m_pCombinedStudioData->m_szCombinedModelName, nAtlasGroup, m_pCombinedStudioData->m_FinalHandle, GetNextAssetID() ); AddToStringTable( pNewTexture, &pNewTexture->sznameindex, m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_szCombinedMaterialName ); } for ( int nNonAtlasedMaterial = 0; nNonAtlasedMaterial < m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames; nNonAtlasedMaterial++ ) { int nMaterial = m_pCombinedStudioData->m_nNumAtlasGroups + nNonAtlasedMaterial; mstudiotexture_t *pNewTexture = m_pCombinedStudioHdr->pTexture( nMaterial ); AddToStringTable( pNewTexture, &pNewTexture->sznameindex, m_pCombinedStudioData->m_szNonAtlasedMaterialBaseName[ nNonAtlasedMaterial ] ); } // write out material paths (used for non atlased materials)
int *pNewCDTexture = ( int * )g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->cdtextureindex ); m_pCombinedStudioHdr->numcdtextures = m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths; g_CombinerWriter.AllocWrite( m_pCombinedStudioHdr->numcdtextures * sizeof( int ) ); g_CombinerWriter.AlignWrite( 4 );
for ( int nMaterialPaths = 0; nMaterialPaths < m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths; nMaterialPaths++ ) { AddToStringTable( m_pCombinedStudioHdr, &pNewCDTexture[ nMaterialPaths ], m_pCombinedStudioData->m_szNonAtlasedMaterialPaths[ nMaterialPaths ] ); }
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->skinindex ); m_pCombinedStudioHdr->numskinref = m_pCombinedStudioHdr->numtextures; m_pCombinedStudioHdr->numskinfamilies = 1; for ( short nTexture = 0; nTexture < static_cast< short >( m_pCombinedStudioHdr->numtextures ); nTexture++ ) { g_CombinerWriter.WriteBuffer( &nTexture, sizeof( short ) ); } g_CombinerWriter.AlignWrite( 4 ); }
void CModelCombine::CombineMDL_KeyValues( ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->keyvalueindex ); VerifyField( offsetof( studiohdr_t, keyvalueindex ), "KeyValues" );
if ( pStudioHdr->keyvaluesize > 0 ) { g_CombinerWriter.WriteBuffer( pStudioHdr->KeyValueText(), pStudioHdr->keyvaluesize ); }
g_CombinerWriter.AlignWrite( 4 ); }
#define WRITE_BONE_BLOCK( type, srcfield, dest, destindex ) \
g_CombinerWriter.WriteOffset( pNewLinearBone->destindex, pNewLinearBone ); \ type *dest = (type *)g_CombinerWriter.AllocWrite( m_nNumMasterBones * sizeof( type ) ); \ g_CombinerWriter.AlignWrite( 4 ); \ for ( int i = 0; i < m_nNumMasterBones; i++) \ { \ dest[ i ] = m_pMasterBoneList[ i ]->srcfield; \ }
void CModelCombine::CombineMDL_BoneTransforms( ) { g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr2->srcbonetransformindex ); VerifyField2( offsetof( studiohdr2_t, srcbonetransformindex ), "Bone Transforms" ); // need to merge bone transforms from the sub models?
if ( m_pStudioHdr2[ 0 ]->numsrcbonetransform > 0 ) { g_CombinerWriter.WriteBuffer( m_pStudioHdr[ 0 ]->SrcBoneTransform( 0 ), m_pStudioHdr2[ 0 ]->numsrcbonetransform * sizeof( mstudiosrcbonetransform_t ) ); for( int nBoneTransform = 0; nBoneTransform < m_pStudioHdr2[ 0 ]->numsrcbonetransform; nBoneTransform++ ) { mstudiosrcbonetransform_t *pOrigBoneTransform = ( mstudiosrcbonetransform_t * )m_pStudioHdr[ 0 ]->SrcBoneTransform( nBoneTransform ); mstudiosrcbonetransform_t *pNewBoneTransform = ( mstudiosrcbonetransform_t * )m_pCombinedStudioHdr->SrcBoneTransform( nBoneTransform );
AddToStringTable( pNewBoneTransform, &pNewBoneTransform->sznameindex, pOrigBoneTransform->pszName() ); } } g_CombinerWriter.AlignWrite( 4 );
if ( m_pStudioHdr2[ 0 ]->pLinearBones() != NULL ) { g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr2->linearboneindex, m_pCombinedStudioHdr2 ); VerifyField2( offsetof( studiohdr2_t, linearboneindex ), "Linear Bone Index" ); mstudiolinearbone_t *pNewLinearBone = ( mstudiolinearbone_t * )g_CombinerWriter.WriteBuffer( m_pStudioHdr2[ 0 ]->pLinearBones(), sizeof( mstudiolinearbone_t ) ); pNewLinearBone->numbones = m_nNumMasterBones;
WRITE_BONE_BLOCK( int, flags, pFlags, flagsindex ); WRITE_BONE_BLOCK( int, parent, pParent, parentindex ); WRITE_BONE_BLOCK( Vector, pos, pPos, posindex ); WRITE_BONE_BLOCK( Quaternion, quat, pQuat, quatindex ); WRITE_BONE_BLOCK( RadianEuler, rot, pRot, rotindex ); WRITE_BONE_BLOCK( matrix3x4_t, poseToBone, pPoseToBone, posetoboneindex ); WRITE_BONE_BLOCK( Vector, posscale, pPoseScale, posscaleindex ); WRITE_BONE_BLOCK( Vector, rotscale, pRotScale, rotscaleindex ); WRITE_BONE_BLOCK( Quaternion, qAlignment, pQAlignment, qalignmentindex );
for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ ) { mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
*pNewLinearBone->pflags( nBone ) = pNewBone->flags; } } }
void CModelCombine::CombineMDL_BoneFlexDrivers( ) { m_pCombinedStudioHdr2->m_nBoneFlexDriverCount = m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount;
if ( m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount > 0 ) { g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr2->m_nBoneFlexDriverIndex, m_pCombinedStudioHdr2 ); VerifyField2( offsetof( studiohdr2_t, m_nBoneFlexDriverIndex ), "Bone Flex Drivers" ); g_CombinerWriter.WriteBuffer( m_pStudioHdr2[ m_nFlexModelSource ]->pBoneFlexDriver( 0 ), m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount * sizeof( mstudioboneflexdriver_t ) ); g_CombinerWriter.AlignWrite( 4 );
for( int nBoneFlexDriver = 0; nBoneFlexDriver < m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount; nBoneFlexDriver++ ) { mstudioboneflexdriver_t *pOrigBoneFlexDriver = m_pStudioHdr2[ m_nFlexModelSource ]->pBoneFlexDriver( nBoneFlexDriver ); mstudioboneflexdriver_t *pNewBoneFlexDriver = m_pCombinedStudioHdr2->pBoneFlexDriver( nBoneFlexDriver );
g_CombinerWriter.WriteOffset( pNewBoneFlexDriver->m_nControlIndex, pNewBoneFlexDriver ); g_CombinerWriter.WriteBuffer( pOrigBoneFlexDriver->pBoneFlexDriverControl( 0 ), pOrigBoneFlexDriver->m_nControlCount * sizeof( mstudioboneflexdrivercontrol_t ) ); /* for( int nBoneFlexDriverControl = 0; nBoneFlexDriverControl < pOrigBoneFlexDriver->m_nControlCount; nBoneFlexDriverControl++ )
{ mstudioboneflexdrivercontrol_t *pOrigBoneFlexDriverControl = pOrigBoneFlexDriver->pBoneFlexDriverControl( nBoneFlexDriverControl ); mstudioboneflexdrivercontrol_t *pNewBoneFlexDriverControl = pNewBoneFlexDriver->pBoneFlexDriverControl( nBoneFlexDriver ); }*/ g_CombinerWriter.AlignWrite( 4 ); } } }
void CModelCombine::CombineMDL_AssignMeshIDs( ) { int i; int j; int m; int numMeshes; mstudiobodyparts_t *pStudioBodyPart; mstudiomodel_t *pStudioModel; mstudiomesh_t *pStudioMesh;
numMeshes = 0; for (i=0; i<m_pCombinedStudioHdr->numbodyparts; i++) { pStudioBodyPart = m_pCombinedStudioHdr->pBodypart(i); for (j=0; j<pStudioBodyPart->nummodels; j++) { pStudioModel = pStudioBodyPart->pModel(j); for (m=0; m<pStudioModel->nummeshes; m++) { // get each mesh
pStudioMesh = pStudioModel->pMesh(m); pStudioMesh->meshid = numMeshes + m; } numMeshes += pStudioModel->nummeshes; } } }
void CModelCombine::CombineMDL( bool bNoStringTable ) { g_CombinerWriter.AllocWrite( 256 ); g_CombinerWriter.AlignWrite( 16 ); g_CombinerWriter.InitWriteArea( WRITE_AREA_MDL, g_CombinerWriter.GetWritePos() ); g_CombinerWriter.SetWriteArea( WRITE_AREA_MDL );
m_nFlexModelSource = -1;
studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
m_pCombinedStudioHdr = ( studiohdr_t * )g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr, sizeof( *pPrimaryStudioHdr ) ); m_pCombinedStudioHdr2 = ( studiohdr2_t * )g_CombinerWriter.WriteBufferWithOffset( pPrimaryStudioHdr->pStudioHdr2(), sizeof( studiohdr2_t ), m_pCombinedStudioHdr->studiohdr2index );
m_pCombinedStudioHdr->flags |= STUDIOHDR_FLAGS_COMBINED;
BeginStringTable(); CombineMDL_PreintStrings();
AddToStringTable( m_pCombinedStudioHdr2, &m_pCombinedStudioHdr2->sznameindex, m_pStudioHdr2[ 0 ]->pszName() );
CombineMDL_Bones(); CombineMDL_Anims(); CombineMDL_SequenceInfo(); CombineMDL_Model(); CombineMDL_Textures(); CombineMDL_KeyValues(); CombineMDL_BoneTransforms(); CombineMDL_BoneFlexDrivers();
// int nStringOffset = m_pWritePos - m_pWriteArea;
// const char *pszOrigStrings = ( ( char * ) m_pStudioHdr[ 0 ] ) + nStringOffset;
// const char *pszNewStrings = ( char * )m_pWritePos;
if ( !bNoStringTable ) { WriteStringTable(); }
int nTotal = g_CombinerWriter.GetWritePos() - g_CombinerWriter.GetWriteArea(); m_pCombinedStudioHdr->length = nTotal;
m_pCombinedStudioHdr->checksum = 0; for ( int i = 0; i < nTotal; i += 4 ) { // TODO: does this need something more than a simple shift left and add checksum?
m_pCombinedStudioHdr->checksum = ( m_pCombinedStudioHdr->checksum << 1 ) + ( ( m_pCombinedStudioHdr->checksum & 0x8000000 ) ? 1 : 0 ) + *( ( long * )( g_CombinerWriter.GetWriteArea() + i ) ); }
m_pCombinedHardwareHeader->checkSum = m_pCombinedStudioHdr->checksum; m_pCombinedVertex->checksum = m_pCombinedStudioHdr->checksum;
CombineMDL_AssignMeshIDs(); }
#ifdef DEBUG_COMBINE
void CModelCombine::TestCombineMDL( ) { int nLength = m_pCombinedStudioHdr->length; char *pOrig = ( char * )m_pStudioHdr[ 0 ]; char *pNew = ( char * )m_pCombinedStudioHdr; char *pStart = pNew;
if ( nLength != m_pStudioHdr[ 0 ]->length ) { Msg( "Test Failure: Original Length: %d, New Length: %d\n", m_pStudioHdr[ 0 ]->length, nLength ); if ( nLength > m_pStudioHdr[ 0 ]->length ) { nLength = m_pStudioHdr[ 0 ]->length; } }
int nOffset = sizeof( studiohdr_t ) + sizeof( studiohdr2_t ); nLength -= nOffset; pOrig += nOffset; pNew += nOffset;
char *pErrorPos = NULL;
for( ; nLength; pOrig++, pNew++, nLength-- ) { if ( ( *pOrig ) != ( *pNew ) ) { pErrorPos = pNew; break; } }
if ( pErrorPos != NULL ) { Msg( "Test Failure: Offset %d\n", pNew - pStart ); Init( m_pCombinedStudioData ); m_pErrorPos = pErrorPos;
// need to do VVD and VTX to recreate memory
CombineVVD(); CombineVTX();
CombineMDL( true ); } }
#endif // DEBUG_COMBINE
void CModelCombine::CalcVTXInfo() { for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet();
if ( pOrigHeader->numBodyParts > m_MaxHardwareData.m_nMaxBodyParts ) { m_MaxHardwareData.m_nMaxBodyParts = pOrigHeader->numBodyParts; }
for( int nBodyPart = 0; nBodyPart < pOrigHeader->numBodyParts; nBodyPart++ ) { OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pOrigHeader->pBodyPart( nBodyPart ); int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection; for( int nBodyModel = 0; nBodyModel < pOrigBodyPart->numModels; nBodyModel++ ) { // skip over sub models that are not the selected one (-1 no selection, so no skipping)
if ( nSubModelToUse != -1 && nBodyModel != nSubModelToUse ) { continue; } OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nBodyModel ); for ( int nLOD = 0; nLOD < pOrigModel->numLODs; nLOD++ ) { OptimizedModel::ModelLODHeader_t *pOrigLOD = pOrigModel->pLOD( nLOD ); unsigned char nStripGroupFlags = OptimizedModel::STRIPGROUP_IS_HWSKINNED; for (int i = 0; i < 2; i++ ) { if ( i > 0 ) { nStripGroupFlags |= OptimizedModel::STRIPGROUP_IS_DELTA_FLEXED; } for( int nMesh = 0; nMesh < pOrigLOD->numMeshes; nMesh++ ) { OptimizedModel::MeshHeader_t *pOrigMesh = pOrigLOD->pMesh( nMesh ); bool bUsed = false; for( int nStripGroup = 0; nStripGroup < pOrigMesh->numStripGroups; nStripGroup++ ) { OptimizedModel::StripGroupHeader_t *pOrigStripGroup = pOrigMesh->pStripGroup( nStripGroup ); if ( pOrigStripGroup->flags == nStripGroupFlags ) { // we are compatible
for( int nStrip = 0; nStrip < pOrigStripGroup->numStrips; nStrip++ ) { OptimizedModel::StripHeader_t *pOrigStrip = pOrigStripGroup->pStrip( nStrip ); if ( pOrigStrip->numTopologyIndices > 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "strip has topology indices" ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); } m_MaxHardwareData.m_nBoneStateChanges += pOrigStrip->numBoneStateChanges; m_MaxHardwareData.m_nIndices += pOrigStrip->numIndices; m_MaxHardwareData.m_nVerts += pOrigStrip->numVerts; m_MaxHardwareData.m_nStrips++; } bUsed = true; } } if ( bUsed ) { // we put things into this strip group
m_MaxHardwareData.m_nStripGroups++; } } } m_MaxHardwareData.m_nModelLODs++; } } m_MaxHardwareData.m_nBodyParts++; } } }
void CModelCombine::WriteStrip( OptimizedModel::StripGroupHeader_t *pNewStripGroup, int nModel, OptimizedModel::StripGroupHeader_t *pOrigStripGroup, OptimizedModel::StripHeader_t *pOrigStrip ) { OptimizedModel::Vertex_t *pNewVert = pNewStripGroup->pVertex( pNewStripGroup->numVerts ); // OptimizedModel::Vertex_t *pOrigVert = pOrigStripGroup->pVertex( pOrigStrip->vertOffset );
unsigned short *pNewIndex = pNewStripGroup->pIndex( pNewStripGroup->numIndices ); unsigned short *pOrigIndex = pOrigStripGroup->pIndex( pOrigStrip->indexOffset ); OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( pNewStripGroup->numStrips );
if ( pOrigStrip->numTopologyIndices > 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "strip has topology indices" ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); }
Assert( m_CurrentHardwareData.m_nStrips < m_MaxHardwareData.m_nStrips ); pNewStrip->numIndices = pOrigStrip->numIndices; pNewStrip->numTopologyIndices = pOrigStrip->numTopologyIndices; pNewStrip->indexOffset = pNewStripGroup->numIndices; pNewStrip->topologyOffset = pNewStripGroup->numTopologyIndices; pNewStrip->numVerts = pOrigStrip->numVerts; pNewStrip->vertOffset = pNewStripGroup->numVerts; pNewStrip->numBoneStateChanges = pOrigStrip->numBoneStateChanges; pNewStrip->numBones = pOrigStrip->numBones; pNewStrip->flags = pOrigStrip->flags; int boneFileOffset = m_HardwareOffsets.m_nBoneStateChanges + m_CurrentHardwareData.m_nBoneStateChanges * sizeof( OptimizedModel::BoneStateChangeHeader_t ); int stripFileOffset = m_HardwareOffsets.m_nStrips + m_CurrentHardwareData.m_nStrips * sizeof( OptimizedModel::StripHeader_t ); pNewStrip->boneStateChangeOffset = boneFileOffset - stripFileOffset;
int nOffset = pNewStripGroup->numVerts - pOrigStrip->vertOffset; for( int nIndex = 0; nIndex < pOrigStrip->numIndices; nIndex++ ) { *pNewIndex = ( *pOrigIndex ) + nOffset; Assert( ( *pNewIndex ) >= pNewStrip->vertOffset ); Assert( ( *pNewIndex ) < pNewStrip->vertOffset + pNewStrip->numVerts );
pOrigIndex++; pNewIndex++; }
// OptimizedModel::Vertex_t *pTestVert = pOrigStripGroup->pVertex( 0 );
// OptimizedModel::FileHeader_t *pPrimaryHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ 0 ]->PeekGet();
// Msg( "%d / %d\n", ( char * )pNewVert - ( char * )m_pCombinedHardwareHeader, ( char * )pTestVert - ( char * )pPrimaryHeader );
for( int nVert = 0; nVert < pOrigStrip->numVerts; nVert++ ) { OptimizedModel::Vertex_t *pOrigVert = pOrigStripGroup->pVertex( nVert + pOrigStrip->vertOffset );
*pNewVert = *pOrigVert; pNewVert->origMeshVertID = m_nVertexRemap[ nModel ][ pOrigVert->origMeshVertID ];
pNewVert++; }
// pTestVert = pOrigStripGroup->pVertex( pOrigStrip->numVerts );
// Msg( "%d / %d\n", ( char * )pNewVert - ( char * )m_pCombinedHardwareHeader, ( char * )pTestVert - ( char * )pPrimaryHeader );
for( int nBone = 0; nBone < pOrigStrip->numBoneStateChanges; nBone++ ) { OptimizedModel::BoneStateChangeHeader_t *pNewBone = pNewStrip->pBoneStateChange( nBone ); OptimizedModel::BoneStateChangeHeader_t *pOrigBone = pOrigStrip->pBoneStateChange( nBone );
pNewBone->hardwareID = pOrigBone->hardwareID; pNewBone->newBoneID = m_nBoneRemap[ nModel ][ pOrigBone->newBoneID ]; }
m_CurrentHardwareData.m_nBoneStateChanges += pNewStrip->numBoneStateChanges;
pNewStripGroup->numIndices += pOrigStrip->numIndices; m_CurrentHardwareData.m_nIndices += pOrigStrip->numIndices;
pNewStripGroup->numVerts += pOrigStrip->numVerts; m_CurrentHardwareData.m_nVerts += pOrigStrip->numVerts;
pNewStripGroup->numStrips++; m_CurrentHardwareData.m_nStrips++; }
void CModelCombine::MergeStripGroup( int nLOD, OptimizedModel::StripGroupHeader_t *pNewStripGroup, int nModel, OptimizedModel::StripGroupHeader_t *pOrigStripGroup ) { // OptimizedModel::Vertex_t *pNewVert = pNewStripGroup->pVertex( pNewStripGroup->numVerts );
// unsigned short *pNewIndex = pNewStripGroup->pIndex( pNewStripGroup->numIndices );
// OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( pNewStripGroup->numStrips );
for( int nStrip = 0; nStrip < pOrigStripGroup->numStrips; nStrip++ ) { OptimizedModel::StripHeader_t *pOrigStrip = pOrigStripGroup->pStrip( nStrip ); WriteStrip( pNewStripGroup, nModel, pOrigStripGroup, pOrigStrip );
m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD ] += pOrigStrip->numIndices; }
m_pCombinedStudioData->m_Results.m_nBatches[ nModel ][ nLOD ] += pOrigStripGroup->numStrips; m_pCombinedStudioData->m_Results.m_nCombinedBatches[ nLOD ] += pOrigStripGroup->numStrips; }
void CModelCombine::WriteStripGroup( int nLOD, int nMaterialIndex, unsigned char nStripGroupFlags, OptimizedModel::MeshHeader_t *pNewMesh ) { OptimizedModel::StripGroupHeader_t *pNewStripGroup = pNewMesh->pStripGroup( pNewMesh->numStripGroups );
pNewStripGroup->numVerts = 0; pNewStripGroup->numIndices = 0; pNewStripGroup->numTopologyIndices = 0; pNewStripGroup->numStrips = 0; pNewStripGroup->flags = nStripGroupFlags;
int stripGroupFileOffset = m_HardwareOffsets.m_nStripGroups + m_CurrentHardwareData.m_nStripGroups * sizeof( OptimizedModel::StripGroupHeader_t ); int vertsFileOffset = m_HardwareOffsets.m_nVerts + m_CurrentHardwareData.m_nVerts * sizeof( OptimizedModel::Vertex_t ); int indicesFileOffset = m_HardwareOffsets.m_nIndices + m_CurrentHardwareData.m_nIndices * sizeof( unsigned short ); int topologyFileOffset = m_HardwareOffsets.m_nTopology + m_CurrentHardwareData.m_nTopology * sizeof( unsigned short ); int stripsFileOffset = m_HardwareOffsets.m_nStrips + m_CurrentHardwareData.m_nStrips * sizeof( OptimizedModel::StripHeader_t ); pNewStripGroup->vertOffset = vertsFileOffset - stripGroupFileOffset; pNewStripGroup->indexOffset = indicesFileOffset - stripGroupFileOffset; pNewStripGroup->topologyOffset = topologyFileOffset - stripGroupFileOffset; pNewStripGroup->stripOffset = stripsFileOffset - stripGroupFileOffset;
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet();
for( int nBodyPart = 0; nBodyPart < pOrigHeader->numBodyParts; nBodyPart++ ) { OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pOrigHeader->pBodyPart( nBodyPart );
int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
for( int nBodyModel = 0; nBodyModel < pOrigBodyPart->numModels; nBodyModel++ ) { // skip over sub models that are not the selected one (-1 no selection, so no skipping)
if ( nSubModelToUse != -1 && nBodyModel != nSubModelToUse ) { continue; }
OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nBodyModel );
int nPickLOD = nLOD; if ( nLOD >= pOrigModel->numLODs ) { nPickLOD = pOrigModel->numLODs - 1; }
OptimizedModel::ModelLODHeader_t *pOrigLOD = pOrigModel->pLOD( nPickLOD );
for( int nMesh = 0; nMesh < pOrigLOD->numMeshes; nMesh++ ) { OptimizedModel::MeshHeader_t *pOrigMesh = pOrigLOD->pMesh( nMesh );
// determine the new material index
int nOriginalMaterialIndex = m_pCombinedStudioData->m_MeshToMaterialMap[ nModel ][ nBodyPart ][ nMesh ]; int nNewMaterialIndex = GetTextureCombiner().GetAtlasGroupIndex( nOriginalMaterialIndex ); if ( nNewMaterialIndex == -1 ) { nNewMaterialIndex = m_pCombinedStudioData->m_nNumAtlasGroups + GetTextureCombiner().GetAtlasGroupMaterialIndex( nOriginalMaterialIndex ); }
if ( nMaterialIndex == nNewMaterialIndex ) { for( int nStripGroup = 0; nStripGroup < pOrigMesh->numStripGroups; nStripGroup++ ) { OptimizedModel::StripGroupHeader_t *pOrigStripGroup = pOrigMesh->pStripGroup( nStripGroup ); if ( pOrigStripGroup->flags == pNewStripGroup->flags ) { // we are compatible
MergeStripGroup( nLOD, pNewStripGroup, nModel, pOrigStripGroup ); } } } } } } }
m_pCombinedStudioData->m_Results.m_nCombinedNumIndexes[ nLOD ] += pNewStripGroup->numIndices;
if ( pNewStripGroup->numIndices ) { // we put things into this strip group
pNewMesh->numStripGroups++; m_CurrentHardwareData.m_nStripGroups++; } }
void CModelCombine::WriteMeshes( int nLOD, int nMaterialIndex, OptimizedModel::ModelLODHeader_t *pNewModelLOD, OptimizedModel::ModelLODHeader_t *pOrigModelLOD ) { OptimizedModel::MeshHeader_t *pNewMesh = pNewModelLOD->pMesh( pNewModelLOD->numMeshes ); OptimizedModel::MeshHeader_t *pOrigMesh = pOrigModelLOD->pMesh( 0 );
int meshFileOffset = m_HardwareOffsets.m_nMeshes + m_CurrentHardwareData.m_nMeshes * sizeof( OptimizedModel::MeshHeader_t ); int stripGroupFileOffset = m_HardwareOffsets.m_nStripGroups + m_CurrentHardwareData.m_nStripGroups * sizeof( OptimizedModel::StripGroupHeader_t ); pNewMesh->stripGroupHeaderOffset = stripGroupFileOffset - meshFileOffset;
pNewMesh->flags = pOrigMesh->flags;
WriteStripGroup( nLOD, nMaterialIndex, OptimizedModel::STRIPGROUP_IS_HWSKINNED, pNewMesh ); WriteStripGroup( nLOD, nMaterialIndex, OptimizedModel::STRIPGROUP_IS_HWSKINNED | OptimizedModel::STRIPGROUP_IS_DELTA_FLEXED, pNewMesh );
pNewModelLOD->numMeshes++; m_CurrentHardwareData.m_nMeshes++; }
void CModelCombine::WriteModelLOD( int nLOD, OptimizedModel::ModelHeader_t *pNewModel, OptimizedModel::ModelHeader_t *pOrigModel ) { OptimizedModel::ModelLODHeader_t *pNewModelLOD = pNewModel->pLOD( m_CurrentHardwareData.m_nModelLODs ); OptimizedModel::ModelLODHeader_t *pOrigModelLOD = pOrigModel->pLOD( nLOD );
int lodFileOffset = m_HardwareOffsets.m_nModelLODs + m_CurrentHardwareData.m_nModelLODs * sizeof( OptimizedModel::ModelLODHeader_t ); int meshFileOffset = m_HardwareOffsets.m_nMeshes + m_CurrentHardwareData.m_nMeshes * sizeof( OptimizedModel::MeshHeader_t ); pNewModelLOD->meshOffset = meshFileOffset - lodFileOffset;
int nTotalMaterials = m_pCombinedStudioData->m_nNumAtlasGroups + m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames; for ( int nMaterialIndex = 0; nMaterialIndex < nTotalMaterials; nMaterialIndex++ ) { WriteMeshes( nLOD, nMaterialIndex, pNewModelLOD, pOrigModelLOD ); }
m_CurrentHardwareData.m_nModelLODs++; }
void CModelCombine::WriteModel( int nBodyModel, OptimizedModel::BodyPartHeader_t *pNewBodyPart, OptimizedModel::BodyPartHeader_t *pOrigBodyPart ) { OptimizedModel::ModelHeader_t *pNewModel = pNewBodyPart->pModel( m_CurrentHardwareData.m_nModels ); OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nBodyModel );
pNewModel->numLODs = pOrigModel->numLODs; int modelFileOffset = m_HardwareOffsets.m_nModels + m_CurrentHardwareData.m_nModels * sizeof( OptimizedModel::ModelHeader_t ); int lodFileOffset = m_HardwareOffsets.m_nModelLODs + m_CurrentHardwareData.m_nModelLODs * sizeof( OptimizedModel::ModelLODHeader_t ); pNewModel->lodOffset = lodFileOffset - modelFileOffset;
for( int nLOD = 0; nLOD < pOrigModel->numLODs; nLOD++ ) { WriteModelLOD( nLOD, pNewModel, pOrigModel ); }
m_CurrentHardwareData.m_nModels++; pNewBodyPart->numModels++; }
void CModelCombine::WriteBodyPart( int nBodyPart ) { for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet(); if ( pOrigHeader->numBodyParts > nBodyPart ) { OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pOrigHeader->pBodyPart( nBodyPart ); OptimizedModel::BodyPartHeader_t *pNewBodyPart = m_pCombinedHardwareHeader->pBodyPart( nBodyPart ); pNewBodyPart->numModels = 0; int bodyPartOffset = m_HardwareOffsets.m_nBodyParts + m_CurrentHardwareData.m_nBodyParts * sizeof( OptimizedModel::BodyPartHeader_t ); int modelFileOffset = m_HardwareOffsets.m_nModels + m_CurrentHardwareData.m_nModels * sizeof( OptimizedModel::ModelHeader_t ); pNewBodyPart->modelOffset = modelFileOffset - bodyPartOffset;
int nBodyModel = ( m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection != -1 ) ? m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection : 0; WriteModel( nBodyModel, pNewBodyPart, pOrigBodyPart ); break; } }
m_CurrentHardwareData.m_nBodyParts++;
}
void CModelCombine::CombineVTX( ) { int nMaxSize = 0;
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { nMaxSize += VTX_Data[ nModel ]->TellMaxPut(); }
g_CombinerWriter.AllocWrite( 256 ); g_CombinerWriter.AlignWrite( 16 ); g_CombinerWriter.InitWriteArea( WRITE_AREA_VTX, g_CombinerWriter.GetWritePos() ); g_CombinerWriter.SetWriteArea( WRITE_AREA_VTX );
OptimizedModel::FileHeader_t *pPrimaryHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ 0 ]->PeekGet();
// m_pCombinedHardwareHeader = ( OptimizedModel::FileHeader_t * )g_CombinerWriter.WriteBuffer( pPrimaryHeader, VTX_Data[ 0 ]->TellMaxPut() );
// return;
m_pCombinedHardwareHeader = ( OptimizedModel::FileHeader_t * )g_CombinerWriter.AllocWrite( sizeof( *m_pCombinedHardwareHeader ) ); m_pCombinedHardwareHeader->version = pPrimaryHeader->version; m_pCombinedHardwareHeader->vertCacheSize = pPrimaryHeader->vertCacheSize; m_pCombinedHardwareHeader->maxBonesPerStrip = pPrimaryHeader->maxBonesPerStrip; m_pCombinedHardwareHeader->maxBonesPerFace = pPrimaryHeader->maxBonesPerFace; m_pCombinedHardwareHeader->maxBonesPerVert = pPrimaryHeader->maxBonesPerVert; m_pCombinedHardwareHeader->numLODs = pPrimaryHeader->numLODs;
// figure out the worst case scenario - we'll have some blank spots in the file, but should be minimal
memset( &m_MaxHardwareData, 0, sizeof( m_MaxHardwareData ) ); memset( &m_CurrentHardwareData, 0, sizeof( m_CurrentHardwareData ) ); memset( &m_HardwareOffsets, 0, sizeof( m_HardwareOffsets ) );
CalcVTXInfo();
m_pCombinedHardwareHeader->numBodyParts = m_MaxHardwareData.m_nMaxBodyParts;
m_MaxHardwareData.m_nStripGroups++; // we need to reserve space for temp writing
m_HardwareOffsets.m_nBodyParts = sizeof( OptimizedModel::FileHeader_t ); m_HardwareOffsets.m_nModels = m_HardwareOffsets.m_nBodyParts + sizeof( OptimizedModel::BodyPartHeader_t ) * m_MaxHardwareData.m_nBodyParts; m_HardwareOffsets.m_nModelLODs = m_HardwareOffsets.m_nModels + sizeof( OptimizedModel::ModelHeader_t ) * m_MaxHardwareData.m_nModels; m_HardwareOffsets.m_nMeshes = m_HardwareOffsets.m_nModelLODs + sizeof( OptimizedModel::ModelLODHeader_t ) * m_MaxHardwareData.m_nModelLODs; m_HardwareOffsets.m_nStripGroups = m_HardwareOffsets.m_nMeshes + sizeof( OptimizedModel::MeshHeader_t ) * m_MaxHardwareData.m_nMeshes; m_HardwareOffsets.m_nStrips = m_HardwareOffsets.m_nStripGroups + sizeof( OptimizedModel::StripGroupHeader_t ) * m_MaxHardwareData.m_nStripGroups; m_HardwareOffsets.m_nVerts = m_HardwareOffsets.m_nStrips + sizeof( OptimizedModel::StripHeader_t ) * m_MaxHardwareData.m_nStrips; m_HardwareOffsets.m_nIndices = m_HardwareOffsets.m_nVerts + sizeof( OptimizedModel::Vertex_t ) * m_MaxHardwareData.m_nVerts; m_HardwareOffsets.m_nBoneStateChanges = m_HardwareOffsets.m_nIndices + sizeof( unsigned short ) * m_MaxHardwareData.m_nIndices; m_HardwareOffsets.m_nStringTable = m_HardwareOffsets.m_nBoneStateChanges + sizeof( OptimizedModel::BoneStateChangeHeader_t ) * m_MaxHardwareData.m_nBoneStateChanges; m_HardwareOffsets.m_nMaterialReplacements = m_HardwareOffsets.m_nStringTable + 0;
g_CombinerWriter.AllocWrite( m_HardwareOffsets.m_nMaterialReplacements - m_HardwareOffsets.m_nModels );
m_pCombinedHardwareHeader->bodyPartOffset = m_HardwareOffsets.m_nBodyParts;
for( int nBodyPart = 0; nBodyPart < m_MaxHardwareData.m_nMaxBodyParts; nBodyPart++ ) { WriteBodyPart( nBodyPart ); }
for( int nLOD = 1; nLOD < m_pCombinedVertex->numLODs; nLOD++ ) { for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { if ( m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD ] > m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD - 1 ] ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "%s has lower LOD which has higher triangle count\n", m_pStudioHdr[ nModel ]->pszName() ); m_pCombinedStudioData->m_Results.m_nDetailedError = COMBINED_DETAIL_ERROR_MODEL_LOWER_LOD_HIGHER_TRI_COUNT; V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorDetails, "Model %s\nLOD %d Tris %d\nLOD %d Tris %d\n", m_pStudioHdr[ nModel ]->pszName(), nLOD - 1, m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD - 1 ] / 3, nLOD, m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD ] / 3 );
throw( COMBINE_RESULT_FLAG_FAILED_GOOD_PRACTICE ); } } }
m_pCombinedHardwareHeader->materialReplacementListOffset = m_HardwareOffsets.m_nMaterialReplacements; g_CombinerWriter.AllocWrite( pPrimaryHeader->numLODs * sizeof( OptimizedModel::MaterialReplacementListHeader_t ) ); for( int nLOD = 0; nLOD < pPrimaryHeader->numLODs; nLOD++ ) { OptimizedModel::MaterialReplacementListHeader_t *pNewMaterialReplacementList = m_pCombinedHardwareHeader->pMaterialReplacementList( nLOD ); OptimizedModel::MaterialReplacementListHeader_t *pOrigMaterialReplacementList = pPrimaryHeader->pMaterialReplacementList( nLOD );
pNewMaterialReplacementList->numReplacements = pOrigMaterialReplacementList->numReplacements; g_CombinerWriter.WriteOffset( pNewMaterialReplacementList->replacementOffset, pNewMaterialReplacementList );
if ( pOrigMaterialReplacementList->numReplacements > 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "material has replacements" ); throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE ); } }
#ifdef TEST_VTX_COMBINE
TestCombineVTX();
#endif // TEST_VTX_COMBINE
// int nSize = Min( 48916, VTX_Data[ 0 ]->TellMaxPut() );
// memcpy( m_pCombinedHardwareHeader, VTX_Data[ 0 ]->PeekGet(), nSize );
// memcpy( ( char * )m_pCombinedHardwareHeader + 48916, ( char * )VTX_Data[ 0 ]->PeekGet() + 48916, 688 );
// g_CombinerWriter.AlignWrite( 128 );
// m_pCombinedHardwareHeader = ( OptimizedModel::FileHeader_t * )g_CombinerWriter.WriteBuffer( VTX_Data[ 0 ]->PeekGet(), VTX_Data[ 0 ]->TellMaxPut() );
}
#ifdef DEBUG_COMBINE
void CModelCombine::TestCombineVTX( ) { OptimizedModel::FileHeader_t *pPrimaryHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ 0 ]->PeekGet();
Assert( pPrimaryHeader->version == m_pCombinedHardwareHeader->version ); Assert( pPrimaryHeader->vertCacheSize == m_pCombinedHardwareHeader->vertCacheSize ); Assert( pPrimaryHeader->maxBonesPerStrip == m_pCombinedHardwareHeader->maxBonesPerStrip ); Assert( pPrimaryHeader->maxBonesPerFace == m_pCombinedHardwareHeader->maxBonesPerFace ); Assert( pPrimaryHeader->maxBonesPerVert == m_pCombinedHardwareHeader->maxBonesPerVert ); Assert( pPrimaryHeader->numLODs == m_pCombinedHardwareHeader->numLODs ); Assert( pPrimaryHeader->numBodyParts == m_pCombinedHardwareHeader->numBodyParts );
for( int nBodyPart = 0; nBodyPart < pPrimaryHeader->numBodyParts; nBodyPart++ ) { OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pPrimaryHeader->pBodyPart( nBodyPart ); OptimizedModel::BodyPartHeader_t *pNewBodyPart = m_pCombinedHardwareHeader->pBodyPart( nBodyPart );
Assert( pOrigBodyPart->numModels == pNewBodyPart->numModels );
for( int nModel = 0; nModel < pOrigBodyPart->numModels; nModel++ ) { OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nModel ); OptimizedModel::ModelHeader_t *pNewModel = pNewBodyPart->pModel( nModel );
Assert( pOrigModel->numLODs == pNewModel->numLODs );
for( int nLOD = 0; nLOD < pOrigModel->numLODs; nLOD++ ) { OptimizedModel::ModelLODHeader_t *pOrigLOD = pOrigModel->pLOD( nLOD ); OptimizedModel::ModelLODHeader_t *pNewLOD = pNewModel->pLOD( nLOD );
Assert( pOrigLOD->numMeshes == pNewLOD->numMeshes ); Assert( pOrigLOD->switchPoint == pNewLOD->switchPoint );
for( int nMesh = 0; nMesh < pOrigLOD->numMeshes; nMesh++ ) { OptimizedModel::MeshHeader_t *pOrigMesh = pOrigLOD->pMesh( nMesh ); OptimizedModel::MeshHeader_t *pNewMesh = pNewLOD->pMesh( nMesh );
Assert( pOrigMesh->numStripGroups == pNewMesh->numStripGroups ); Assert( pOrigMesh->flags == pNewMesh->flags );
for( int nStripGroup = 0; nStripGroup < pOrigMesh->numStripGroups; nStripGroup++ ) { OptimizedModel::StripGroupHeader_t *pOrigStripGroup = pOrigMesh->pStripGroup( nStripGroup ); OptimizedModel::StripGroupHeader_t *pNewStripGroup = pNewMesh->pStripGroup( nStripGroup );
Assert( pOrigStripGroup->numVerts == pNewStripGroup->numVerts ); Assert( pOrigStripGroup->numIndices == pNewStripGroup->numIndices ); Assert( pOrigStripGroup->numStrips == pNewStripGroup->numStrips ); Assert( pOrigStripGroup->flags == pNewStripGroup->flags ); Assert( pOrigStripGroup->numTopologyIndices == pNewStripGroup->numTopologyIndices );
for( int nNumVerts = 0; nNumVerts < pOrigStripGroup->numVerts; nNumVerts++ ) { OptimizedModel::Vertex_t *pOrigVertex = pOrigStripGroup->pVertex( nNumVerts ); OptimizedModel::Vertex_t *pNewVertex = pNewStripGroup->pVertex( nNumVerts );
Assert( memcmp( pOrigVertex, pNewVertex, sizeof( *pOrigVertex ) ) == 0 ); }
for( int nStrip = 0; nStrip < pOrigStripGroup->numStrips; nStrip++ ) { OptimizedModel::StripHeader_t *pOrigStrip = pOrigStripGroup->pStrip( nStrip ); OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( nStrip );
Assert( pOrigStrip->numIndices == pNewStrip->numIndices ); Assert( pOrigStrip->numVerts == pNewStrip->numVerts ); Assert( pOrigStrip->numBones == pNewStrip->numBones ); Assert( pOrigStrip->flags == pNewStrip->flags ); Assert( pOrigStrip->numBoneStateChanges == pNewStrip->numBoneStateChanges ); Assert( pOrigStrip->numTopologyIndices == pNewStrip->numTopologyIndices );
for( int nBoneStateChange = 0; nBoneStateChange < pOrigStrip->numBoneStateChanges; nBoneStateChange++ ) { OptimizedModel::BoneStateChangeHeader_t *pOrigBoneStateChange = pOrigStrip->pBoneStateChange( nBoneStateChange ); OptimizedModel::BoneStateChangeHeader_t *pNewBoneStateChange = pNewStrip->pBoneStateChange( nBoneStateChange );
Assert( pOrigBoneStateChange->hardwareID == pNewBoneStateChange->hardwareID ); Assert( pOrigBoneStateChange->newBoneID == pNewBoneStateChange->newBoneID ); } } } } } } } }
#endif // DEBUG_COMBINE
// this will move a vert belonging to a secondary model from pose space of that secondary model
// into that secondary model's bone space, then transform the vert into pose space of the primary model.
void CModelCombine::CombineVVD_OffsetVerts( ) { matrix3x4_t VertToPrimaryPose[ COMBINER_MAX_BONES ];
for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ ) { matrix3x4_t VertToPrimaryBone; VertToPrimaryBone = m_pMasterBoneList[ nBone ]->poseToBone;
MatrixInvert( VertToPrimaryBone, VertToPrimaryPose[ nBone ] ); }
for( int nModel = 1; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { int nNumVerts = m_pVertexFileHeader[ nModel ]->numLODVertexes[ 0 ];
for( int nVert = 0; nVert < nNumVerts; nVert++ ) { int nRealVert = m_nVertexRemap[ nModel ][ nVert ]; mstudiovertex_t *pVert = ( mstudiovertex_t * )m_pCombinedVertex->GetVertexData() + nRealVert; Vector vPosition, vPos, vFinalVert, vFinalNormal, vFinalTangent; bool bHasTangent = m_pVertexFileHeader[ nModel ]->tangentDataStart != NULL;
vFinalVert.Init(); vFinalNormal.Init(); vFinalTangent.Init(); for( int nBone = 0; nBone < pVert->m_BoneWeights.numbones; nBone++ ) { int nBoneIndex = pVert->m_BoneWeights.bone[ nBone ];
VectorTransform( pVert->m_vecPosition, m_pStudioHdr[ nModel ]->pBone( m_nMasterToLocalBoneRemap[ nModel ][ nBoneIndex ] )->poseToBone, vPosition ); VectorTransform( vPosition, VertToPrimaryPose[ nBoneIndex ], vPos ); vFinalVert += vPos * pVert->m_BoneWeights.weight[ nBone ];
VectorRotate( pVert->m_vecNormal, m_pStudioHdr[ nModel ]->pBone( m_nMasterToLocalBoneRemap[ nModel ][ nBoneIndex ] )->poseToBone, vPosition ); VectorRotate( vPosition, VertToPrimaryPose[ nBoneIndex ], vPos ); vFinalNormal += vPos * pVert->m_BoneWeights.weight[ nBone ];
if ( bHasTangent ) { Vector4D vTangent = *( m_pCombinedVertex->GetTangentData() + nRealVert ); VectorRotate( vTangent.AsVector3D(), m_pStudioHdr[ nModel ]->pBone( m_nMasterToLocalBoneRemap[ nModel ][ nBoneIndex ] )->poseToBone, vPosition ); VectorRotate( vPosition, VertToPrimaryPose[ nBoneIndex ], vPos ); vFinalTangent += vPos * pVert->m_BoneWeights.weight[ nBone ]; } } pVert->m_vecPosition = vFinalVert; pVert->m_vecNormal = vFinalNormal; if ( bHasTangent ) { Vector *pTangent = ( Vector * )( m_pCombinedVertex->GetTangentData() + nRealVert ); *pTangent = vFinalTangent; } } } }
void CModelCombine::CombineVVD( ) { int nVertsWritten[ COMBINER_MAX_MODELS ]; for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { int nNumVerts = m_pVertexFileHeader[ nModel ]->numLODVertexes[ 0 ];
m_nVertexRemap[ nModel ] = ( int * )g_CombinerWriter.AllocWrite( sizeof( int ) * nNumVerts ); nVertsWritten[ nModel ] = 0; }
g_CombinerWriter.AlignWrite( 16 );
g_CombinerWriter.InitWriteArea( WRITE_AREA_VVD, g_CombinerWriter.GetWritePos() ); g_CombinerWriter.SetWriteArea( WRITE_AREA_VVD );
m_pCombinedVertex = ( vertexFileHeader_t * )g_CombinerWriter.AllocWrite( sizeof( *m_pCombinedVertex ) );
m_pCombinedVertex->id = MODEL_VERTEX_FILE_ID; m_pCombinedVertex->version = MODEL_VERTEX_FILE_VERSION; m_pCombinedVertex->numLODs = m_pVertexFileHeader[ 0 ]->numLODs;
g_CombinerWriter.WriteOffset( m_pCombinedVertex->vertexDataStart, m_pCombinedVertex ); int nVertOffset = 0; for( int nLOD = m_pCombinedVertex->numLODs - 1; nLOD >= 0; nLOD-- ) { for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { int nNumVerts = m_pVertexFileHeader[ nModel ]->numLODVertexes[ nLOD ];
m_pCombinedVertex->numLODVertexes[ nLOD ] += m_pVertexFileHeader[ nModel ]->numLODVertexes[ nLOD ];
nNumVerts -= nVertsWritten[ nModel ];
mstudiovertex_t *pNewVertex = ( mstudiovertex_t * )g_CombinerWriter.GetWritePos(); if ( m_pVertexFileHeader[ nModel ]->numFixups > 0 ) { int nCount = nNumVerts; int nOffset = nVertsWritten[ nModel ]; int nNumWritten = 0; const mstudiovertex_t *pOrigVertex = m_pVertexFileHeader[ nModel ]->GetVertexData();
for( int nFixup = 0; nFixup < m_pVertexFileHeader[ nModel ]->numFixups; nFixup++ ) { vertexFileFixup_t *pOrigFixup = ( vertexFileFixup_t * )( ( byte * )m_pVertexFileHeader[ nModel ] + m_pVertexFileHeader[ nModel ]->fixupTableStart) + nFixup;
if ( pOrigFixup->numVertexes < nOffset ) { nOffset -= pOrigFixup->numVertexes; } else { int nSize = pOrigFixup->numVertexes - nOffset; if ( nSize > nCount ) { nSize = nCount; }
g_CombinerWriter.WriteBuffer( pOrigVertex + nOffset, nSize * sizeof( mstudiovertex_t ) );
nOffset = 0; nCount -= nSize; nNumWritten += nSize;
if ( nCount == 0 ) { break; } } }
Assert( nNumWritten == nNumVerts ); } else { g_CombinerWriter.WriteBuffer( m_pVertexFileHeader[ nModel ]->GetVertexData() + nVertsWritten[ nModel ], nNumVerts * sizeof( mstudiovertex_t ) ); }
if ( nModel > 0 ) { // primary one doesn't need bone remap
mstudiovertex_t *pBoneVertex = pNewVertex;
for( int nVert = 0, nVertIndex = nVertsWritten[ nModel ]; nVert < nNumVerts; nVert++, pBoneVertex++, nVertIndex++ ) { for( int nBone = 0; nBone < pBoneVertex->m_BoneWeights.numbones; nBone++ ) { pBoneVertex->m_BoneWeights.bone[ nBone ] = m_nBoneRemap[ nModel ][ pBoneVertex->m_BoneWeights.bone[ nBone ] ]; } } }
for( int nBodyBart = 0; nBodyBart < m_pStudioHdr[ nModel ]->numbodyparts; nBodyBart++ ) { mstudiobodyparts_t *pOrigBodyPart = m_pStudioHdr[ nModel ]->pBodypart( nBodyBart );
int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
for( int nBodyPartModel = 0; nBodyPartModel < pOrigBodyPart->nummodels; nBodyPartModel++ ) { // skip over sub models that are not the selected one (-1 no selection, so no skipping)
if ( nSubModelToUse != -1 && nBodyPartModel != nSubModelToUse ) { continue; }
mstudiomodel_t *pOrigModel = pOrigBodyPart->pModel( nBodyPartModel ); for( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ ) { mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
Vector2D vStartST, vSizeST, vPixelSize; int nTextureIndex = m_pCombinedStudioData->m_nModelMaterialIndices[ nModel ][ nMesh ];
GetTextureCombiner().GetTextureInfo( nTextureIndex, vStartST, vSizeST, vPixelSize );
for( int nVert = 0, nVertIndex = nVertsWritten[ nModel ] + pOrigMesh->vertexoffset; nVert < pOrigMesh->numvertices; nVert++, pNewVertex++, nVertIndex++ ) { double flInteger;
pNewVertex->m_vecTexCoord.x = ( float )modf( ( double )pNewVertex->m_vecTexCoord.x, &flInteger ); if ( pNewVertex->m_vecTexCoord.x < 0.0f ) { pNewVertex->m_vecTexCoord.x += 1.0f; }
pNewVertex->m_vecTexCoord.y = ( float )modf( ( double )pNewVertex->m_vecTexCoord.y, &flInteger ); if ( pNewVertex->m_vecTexCoord.y < 0.0f ) { pNewVertex->m_vecTexCoord.y += 1.0f; }
pNewVertex->m_vecTexCoord = ( pNewVertex->m_vecTexCoord * vSizeST ); pNewVertex->m_vecTexCoord += vStartST;
m_nVertexRemap[ nModel ][ nVertIndex ] = nVertOffset + nVert; } } } }
nVertsWritten[ nModel ] += nNumVerts; nVertOffset += nNumVerts;
m_pCombinedStudioData->m_Results.m_nNumLODs[ nModel ] = m_pVertexFileHeader[ nModel ]->numLODs; if ( nLOD < m_pCombinedVertex->numLODs - 1 ) { m_pCombinedStudioData->m_Results.m_nNumVerts[ nModel ][ nLOD ] = m_pCombinedStudioData->m_Results.m_nNumVerts[ nModel ][ nLOD + 1 ] + nNumVerts; } else { m_pCombinedStudioData->m_Results.m_nNumVerts[ nModel ][ nLOD ] = nNumVerts; } m_pCombinedStudioData->m_Results.m_nCombinedNumVerts[ nLOD ] += nNumVerts; } }
m_pCombinedStudioData->m_Results.m_nCombinedNumLODs = m_pCombinedVertex->numLODs;
int nLastLODSize = m_pCombinedVertex->numLODVertexes[ m_pCombinedVertex->numLODs - 1 ]; for( int nLOD = m_pCombinedVertex->numLODs; nLOD < MAX_NUM_LODS; nLOD++ ) { m_pCombinedVertex->numLODVertexes[ nLOD ] = nLastLODSize; }
Vector4D *pData = ( Vector4D * )g_CombinerWriter.WriteOffset( m_pCombinedVertex->tangentDataStart, m_pCombinedVertex ); g_CombinerWriter.AllocWrite( m_pCombinedVertex->numLODVertexes[ 0 ] * sizeof( Vector4D ) ); for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { if ( m_pVertexFileHeader[ nModel ]->tangentDataStart == 0 ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "vertex file has tangent data" ); throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE ); }
const Vector4D *vData = m_pVertexFileHeader[ nModel ]->GetTangentData(); for( int nIndex = 0; nIndex < m_pVertexFileHeader[ nModel ]->numLODVertexes[ 0 ]; nIndex++ ) { pData[ m_nVertexRemap[ nModel ][ nIndex ] ] = *vData; vData++; } }
CombineVVD_OffsetVerts(); }
#if 0
void CModelCombine::Test( ) { studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ]; vertexFileHeader_t *pVertexHeader = ( vertexFileHeader_t * )VVD_Data[ 0 ]->PeekGet(); const mstudiovertex_t *pVertex = pVertexHeader->GetVertexData();
int nTotalModels = 0; for ( int nBodyPart = 0; nBodyPart < pStudioHdr->numbodyparts; nBodyPart++ ) { mstudiobodyparts_t *pOrigBodyPart = pStudioHdr->pBodypart( nBodyPart );
for( int nModel = 0; nModel < pOrigBodyPart->nummodels; nModel++ ) { mstudiomodel_t *pOrigModel = pOrigBodyPart->pModel( nModel );
for( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ ) { mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
for( int nVert = 0; nVert < pOrigMesh->numvertices; nVert++ ) { const mstudiovertex_t *pOrigVert = &pVertex[ nVert + pOrigMesh->vertexoffset ];
Msg(""); } } } } } #endif
void CModelCombine::CombineTextures( ) { GetTextureCombiner().Init( m_pCombinedStudioData );
for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ ) { #if 0
for( int nSkin = 0; nSkin < m_pStudioHdr[ nModel ]->numskinfamilies; nSkin++ ) { short *pSkinRef = m_pStudioHdr[ nModel ]->pSkinref( 0 ); pSkinRef += ( nSkin * m_pStudioHdr[ nModel ]->numskinref );
for( int nReference = 0; nReference < m_pStudioHdr[ nModel ]->numskinref; nReference++, pSkinRef++ ) { int nTextureIndex = *pSkinRef; const char *pszTextureName = m_pStudioHdr[ nModel ]->pTexture( nTextureIndex )->pszName();
Msg( "SKin %d, Reference %d: %s\n", nSkin, nReference, pszTextureName ); } } #endif
int nSkin = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nSkinFamily;
short *pSkinRef = m_pStudioHdr[ nModel ]->pSkinref( 0 ); if ( nSkin > 0 && nSkin < m_pStudioHdr[ nModel ]->numskinfamilies ) { pSkinRef += ( nSkin * m_pStudioHdr[ nModel ]->numskinref ); }
for( int nBodyPart = 0; nBodyPart < m_pStudioHdr[ nModel ]->numbodyparts; nBodyPart++ ) { mstudiobodyparts_t *pOrigBodyPart = m_pStudioHdr[ nModel ]->pBodypart( nBodyPart );
int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
for( int nBodyPartModel = 0; nBodyPartModel < pOrigBodyPart->nummodels; nBodyPartModel++ ) { // skip over sub models that are not the selected one (-1 no selection, so no skipping)
if ( nSubModelToUse != -1 && nBodyPartModel != nSubModelToUse ) { continue; }
mstudiomodel_t *pOrigModel = pOrigBodyPart->pModel( nBodyPartModel ); m_pCombinedStudioData->m_nModelMaterialCounts[ nModel ] = pOrigModel->nummeshes; for( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ ) { mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
int nTextureIndex = pSkinRef[ pOrigMesh->material ];
m_pCombinedStudioData->m_MeshToMaterialMap[ nModel ][ nBodyPart ][ nMesh ] = nTextureIndex;
m_pCombinedStudioData->m_nModelMaterialIndices[ nModel ][ nMesh ] = AddMaterialToTextureCombiner( nTextureIndex, nModel, pOrigMesh->material ); } } } }
GetTextureCombiner().Resolve(); }
int CModelCombine::AddMaterialToTextureCombiner( int nTextureIndex, int nModel, int nModelMaterialIndex ) { char szPath[ MAX_PATH ]; char szFinalPath[ MAX_PATH ]; bool bFound = false;
// If we don't do this, we get filenames like "materials\\blah.vmt".
const char *pszTextureName = m_pStudioHdr[ nModel ]->pTexture( nTextureIndex )->pszName(); if ( pszTextureName[ 0 ] == CORRECT_PATH_SEPARATOR || pszTextureName[ 0 ] == INCORRECT_PATH_SEPARATOR ) { pszTextureName++; }
// search through all specified directories until a valid material is found
for ( int nSearch = 0; nSearch < m_pStudioHdr[ nModel ]->numcdtextures; nSearch++ ) { // This prevents filenames like /models/blah.vmt.
const char *pszCDTexture = m_pStudioHdr[ nModel ]->pCdtexture( nSearch ); if ( pszCDTexture[ 0 ] == CORRECT_PATH_SEPARATOR || pszCDTexture[ 0 ] == INCORRECT_PATH_SEPARATOR ) { pszCDTexture++; }
V_ComposeFileName( pszCDTexture, pszTextureName, szPath, sizeof( szPath ) ); V_ComposeFileName( "materials/", szPath, szFinalPath, sizeof( szFinalPath ) );
char szCheckPath[ MAX_PATH ]; V_strcpy_safe( szCheckPath, szFinalPath ); V_strcat_safe( szCheckPath, ".vmt" );
if ( g_pFullFileSystem->FileExists( szCheckPath ) ) { bFound = true; break; } }
if ( bFound == false ) { Assert( 0 ); V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model %s has missing material %s", m_pStudioHdr[ nModel ]->pszName(), pszTextureName ); throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE ); }
return GetTextureCombiner().AddMaterial( szFinalPath ); }
|