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

2893 lines
106 KiB

  1. #include "datacache/imdlcache.h"
  2. #include "optimize.h"
  3. #include "mdlcombine.h"
  4. #include "filesystem.h"
  5. #include "studio.h"
  6. #include "mathlib/mathlib.h"
  7. #include <math.h>
  8. #include "tier1/characterset.h"
  9. #include "keyvalues.h"
  10. #include "vtfcombine.h"
  11. // beware: this does not do any type of byte swapping for handling of endian issues!
  12. CCombinerMemoryWriter g_CombinerWriter;
  13. CModelCombine g_ModelCombiner;
  14. static CModelCombine *s_pCurrentCombine = &g_ModelCombiner;
  15. static characterset_t s_BreakSet;
  16. unsigned int CModelCombine::m_nNextAssetID = 0;
  17. #ifdef DEBUG_COMBINE
  18. #define DebugCombineMsg( ... ) Msg( __VA_ARGS__ )
  19. //#define TEST_MDL_COMBINE 1
  20. //#define TEST_VTX_COMBINE 1
  21. #else
  22. #define DebugCombineMsg( ... )
  23. #endif
  24. CCombinerMemoryWriter::CCombinerMemoryWriter( )
  25. {
  26. m_pWorkBuffer = NULL;
  27. }
  28. CCombinerMemoryWriter::~CCombinerMemoryWriter( )
  29. {
  30. free( m_pWorkBuffer );
  31. }
  32. void CCombinerMemoryWriter::Init( )
  33. {
  34. if ( m_pWorkBuffer == NULL )
  35. {
  36. CharacterSetBuild( &s_BreakSet, "{}()':" );
  37. m_pWorkBuffer = ( char * )malloc( COMBINER_WORK_BUFFER_SIZE );
  38. m_pEndBuffer = m_pWorkBuffer + COMBINER_WORK_BUFFER_SIZE - 1;
  39. }
  40. memset( m_pWorkBuffer, 0, COMBINER_WORK_BUFFER_SIZE );
  41. m_pWriteArea = m_pWritePos = NULL;
  42. #ifdef DEBUG_COMBINE
  43. m_pErrorPos = NULL;
  44. #endif // DEBUG_COMBINE
  45. m_nWriteArea = -1;
  46. InitWriteArea( WRITE_AREA_VVD, m_pWorkBuffer );
  47. SetWriteArea( WRITE_AREA_VVD );
  48. }
  49. void CCombinerMemoryWriter::InitWriteArea( int nArea, char *pPosition )
  50. {
  51. pPosition = ( char * )( ( intp )( pPosition + 15 ) & ( ~15 ) );
  52. m_pSaveWriteArea[ nArea ] = m_pSaveWritePos[ nArea ] = pPosition;
  53. }
  54. void CCombinerMemoryWriter::SetWriteArea( int nArea )
  55. {
  56. if ( m_nWriteArea != -1 )
  57. {
  58. m_pSaveWriteArea[ m_nWriteArea ] = m_pWriteArea;
  59. m_pSaveWritePos[ m_nWriteArea ] = m_pWritePos;
  60. }
  61. m_nWriteArea = nArea;
  62. if ( m_nWriteArea != -1 )
  63. {
  64. m_pWriteArea = m_pSaveWriteArea[ m_nWriteArea ];
  65. m_pWritePos = m_pSaveWritePos[ m_nWriteArea ];
  66. }
  67. }
  68. char *CCombinerMemoryWriter::AllocWrite( int nSize )
  69. {
  70. char *pOrigPos = m_pWritePos;
  71. #ifdef DEBUG_COMBINE
  72. if ( m_pErrorPos >= m_pWritePos && m_pErrorPos < m_pWritePos + nSize )
  73. {
  74. Msg( "AllocFailure: %d offset %d\n", m_pErrorPos - m_pWriteArea, m_pErrorPos - m_pWritePos );
  75. Assert( 0 );
  76. }
  77. #endif // DEBUG_COMBINE
  78. m_pWritePos += nSize;
  79. if ( m_pWritePos > m_pEndBuffer )
  80. {
  81. AssertMsg( false, "Internal model combiner buffer not large enough" );
  82. V_sprintf_safe( g_ModelCombiner.GetResults()->m_szErrorMessage, "Internal model combiner buffer not large enough" );
  83. throw( COMBINE_RESULT_FLAG_OUT_OF_MEMORY );
  84. }
  85. return pOrigPos;
  86. }
  87. char *CCombinerMemoryWriter::WriteOffset( int &nOffsetIndex )
  88. {
  89. nOffsetIndex = ( m_pWritePos - m_pWriteArea );
  90. return m_pWritePos;
  91. }
  92. char *CCombinerMemoryWriter::WriteOffset( int &nOffsetIndex, void *pBasePtr )
  93. {
  94. nOffsetIndex = ( ( byte * )m_pWritePos - ( byte * )pBasePtr );
  95. return m_pWritePos;
  96. }
  97. char *CCombinerMemoryWriter::WriteOffset( short &nOffsetIndex, void *pBasePtr )
  98. {
  99. nOffsetIndex = ( ( byte * )m_pWritePos - ( byte * )pBasePtr );
  100. return m_pWritePos;
  101. }
  102. char *CCombinerMemoryWriter::WriteBuffer( const void *pData, int nSize )
  103. {
  104. char *pOrigPos = AllocWrite( nSize );
  105. memcpy( pOrigPos, pData, nSize );
  106. return pOrigPos;
  107. }
  108. char *CCombinerMemoryWriter::WriteBufferWithOffset( const void *pData, int nSize, int &nOffsetIndex )
  109. {
  110. WriteOffset( nOffsetIndex );
  111. return WriteBuffer( pData, nSize );
  112. }
  113. char *CCombinerMemoryWriter::WriteString( const char *pszString )
  114. {
  115. return WriteBuffer( pszString, strlen( pszString ) + 1 );
  116. }
  117. char *CCombinerMemoryWriter::WriteText( const char *pszString )
  118. {
  119. return WriteBuffer( pszString, strlen( pszString ) );
  120. }
  121. void CCombinerMemoryWriter::AlignWrite( int nAlignSize )
  122. {
  123. #ifdef DEBUG_COMBINE
  124. char *pPriorPos = m_pWritePos;
  125. #endif // DEBUG_COMBINE
  126. nAlignSize--;
  127. m_pWritePos = ( char * )( ( intp )( m_pWritePos + nAlignSize ) & ( ~nAlignSize ) );
  128. #ifdef DEBUG_COMBINE
  129. if ( m_pErrorPos >= pPriorPos && m_pErrorPos < m_pWritePos )
  130. {
  131. DebugCombineMsg( "AlignFailure: %d offset %d\n", m_pErrorPos - m_pWriteArea, m_pErrorPos - m_pWritePos );
  132. Assert( 0 );
  133. }
  134. #endif // DEBUG_COMBINE
  135. if ( m_pWritePos > m_pEndBuffer )
  136. {
  137. Assert( 0 );
  138. V_sprintf_safe( g_ModelCombiner.GetResults()->m_szErrorMessage, "Internal model combiner buffer not large enough" );
  139. throw( COMBINE_RESULT_FLAG_OUT_OF_MEMORY );
  140. }
  141. }
  142. CModelCombine::CModelCombine( )
  143. {
  144. }
  145. CModelCombine::~CModelCombine( )
  146. {
  147. }
  148. void CModelCombine::Init( TCombinedStudioData *pCombinedStudioData )
  149. {
  150. m_pCombinedStudioData = pCombinedStudioData;
  151. memset( &m_pCombinedStudioData->m_Results, 0, sizeof( m_pCombinedStudioData->m_Results ) );
  152. for ( int nGroup = 0; nGroup < COMBINER_MAX_ATLAS_GROUPS; nGroup++ )
  153. {
  154. memset( &m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures, 0, sizeof( m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures ) );
  155. memset( &m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_nCombinedTextureSizes, 0, sizeof( m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_nCombinedTextureSizes ) );
  156. m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedMaterial = NULL;
  157. memset( &m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_szCombinedMaterialName, 0, sizeof( m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_szCombinedMaterialName ) );
  158. }
  159. // we need to make sure we don't have any periods, as other routines may strip off those as an extension
  160. for( char *pszTestChar = m_pCombinedStudioData->m_szCombinedModelName; *pszTestChar; pszTestChar++ )
  161. {
  162. if ( *pszTestChar == '.' )
  163. {
  164. *pszTestChar = '_';
  165. }
  166. }
  167. g_CombinerWriter.Init();
  168. }
  169. void CModelCombine::BeginStringTable( )
  170. {
  171. strings[ 0 ].base = NULL;
  172. strings[ 0 ].ptr = NULL;
  173. strings[ 0 ].string = "";
  174. strings[ 0 ].dupindex = -1;
  175. numStrings = 1;
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: add a string to the file-global string table.
  179. // Keep track of fixup locations
  180. //-----------------------------------------------------------------------------
  181. void CModelCombine::AddToStringTable( void *base, int *ptr, const char *string )
  182. {
  183. if ( numStrings >= COMBINER_MAX_STRINGS )
  184. {
  185. Assert( 0 );
  186. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many strings for string table of size %d", COMBINER_MAX_STRINGS );
  187. throw( COMBINE_RESULT_FLAG_TOO_MANY_STRINGS );
  188. }
  189. for ( int i = 0; i < numStrings; i++ )
  190. {
  191. if ( !string || !strcmp( string, strings[ i ].string ) )
  192. {
  193. strings[ numStrings ].base = ( byte * )base;
  194. strings[ numStrings ].ptr = ptr;
  195. strings[ numStrings ].string = string;
  196. strings[ numStrings ].dupindex = i;
  197. numStrings++;
  198. return;
  199. }
  200. }
  201. strings[ numStrings ].base = ( byte * )base;
  202. strings[ numStrings ].ptr = ptr;
  203. strings[ numStrings ].string = string;
  204. strings[ numStrings ].dupindex = -1;
  205. numStrings++;
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: Write out stringtable
  209. // fixup local pointers
  210. //-----------------------------------------------------------------------------
  211. void CModelCombine::WriteStringTable( )
  212. {
  213. // force null at first address
  214. strings[ 0 ].addr = ( byte * )g_CombinerWriter.GetWritePos();
  215. g_CombinerWriter.WriteBuffer( "", 1 ); // null string
  216. // save all the rest
  217. for ( int i = 1; i < numStrings; i++ )
  218. {
  219. if ( strings[ i ].dupindex == -2 )
  220. {
  221. // initial filler to match string tables up
  222. strings[ i ].addr = ( byte * )g_CombinerWriter.GetWritePos();
  223. g_CombinerWriter.WriteBuffer( strings[ i ].string, strlen( strings[ i ].string ) + 1 );
  224. }
  225. else if ( strings[ i ].dupindex == -1 )
  226. {
  227. // not in table yet
  228. // calc offset relative to local base
  229. *strings[ i ].ptr = ( byte * )g_CombinerWriter.GetWritePos() - strings[ i ].base;
  230. // keep track of address in case of duplication
  231. strings[ i ].addr = ( byte * )g_CombinerWriter.GetWritePos();
  232. // copy string data, add a terminating \0
  233. g_CombinerWriter.WriteBuffer( strings[ i ].string, strlen( strings[ i ].string ) + 1 );
  234. }
  235. else
  236. {
  237. // already in table, calc offset of existing string relative to local base
  238. *strings[ i ].ptr = strings[ strings[ i ].dupindex ].addr - strings[ i ].base;
  239. }
  240. }
  241. g_CombinerWriter.AlignWrite( 4 );
  242. }
  243. void CModelCombine::VerifyField( int nField, const char *pszDescription )
  244. {
  245. #ifdef DEBUG_COMBINE
  246. int *pOriginal = ( int * )( ( ( byte * )m_pStudioHdr[ 0 ] ) + nField );
  247. int *pCombined = ( int * )( ( ( byte * )m_pCombinedStudioHdr ) + nField );
  248. DebugCombineMsg( "Verify Field %s: %d / %d\n", pszDescription, *pOriginal, *pCombined );
  249. #endif // DEBUG_COMBINE
  250. }
  251. void CModelCombine::VerifyField2( int nField, const char *pszDescription )
  252. {
  253. #ifdef DEBUG_COMBINE
  254. int *pOriginal = ( int * )( ( ( byte * )m_pStudioHdr2[ 0 ] ) + nField );
  255. int *pCombined = ( int * )( ( ( byte * )m_pCombinedStudioHdr2 ) + nField );
  256. DebugCombineMsg( "Verify Field2 %s: %d / %d\n", pszDescription, *pOriginal, *pCombined );
  257. #endif // DEBUG_COMBINE
  258. }
  259. void CModelCombine::VerifyOffset( void *pPtr, const char *pszDescription, void *pWritePos )
  260. {
  261. #ifdef DEBUG_COMBINE
  262. int pV1 = ( ( char * )pPtr ) - ( ( char * ) m_pStudioHdr[ 0 ] );
  263. if ( pWritePos == 0 )
  264. {
  265. pWritePos = m_pWritePos;
  266. }
  267. int pV2 = ( char * )pWritePos - m_pWriteArea;
  268. DebugCombineMsg( "Verify Offset %s: %d / %d ( %d )\n", pszDescription, pV1, pV2, pV2 - pV1 );
  269. #endif // DEBUG_COMBINE
  270. }
  271. bool CModelCombine::Resolve( )
  272. {
  273. char szFileName[ MAX_PATH ];
  274. m_pCombinedStudioData->m_Results.m_nCombinedResults = COMBINE_RESULT_FLAG_OK;
  275. m_pCombinedStudioData->m_Results.m_szErrorMessage[ 0 ] = 0;
  276. m_pCombinedStudioData->m_Results.m_szErrorDetails[ 0 ] = 0;
  277. m_pCombinedStudioData->m_Results.m_nDetailedError = COMBINED_DETAIL_ERROR_NOT_SPECIFIED;
  278. memset( MDL_Data, 0, sizeof( MDL_Data ) );
  279. memset( VTX_Data, 0, sizeof( VTX_Data ) );
  280. memset( VVD_Data, 0, sizeof( VVD_Data ) );
  281. try
  282. {
  283. if ( STRING(m_pCombinedStudioData->m_ModelInputData[ 0 ].m_iszModelName)[ 0 ] == 0 )
  284. {
  285. Assert( 0 );
  286. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "No primary model specified" );
  287. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  288. }
  289. double flStartLoadTime = Plat_FloatTime();
  290. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  291. {
  292. bool bResult;
  293. MDL_Data[ nModel ] = new CUtlBuffer();
  294. V_strcpy_safe( szFileName, STRING(m_pCombinedStudioData->m_ModelInputData[ nModel ].m_iszModelName) );
  295. Q_SetExtension( szFileName, ".mdl", sizeof( szFileName ) );
  296. bResult = g_pFullFileSystem->ReadFile( szFileName, "GAME", *MDL_Data[ nModel ], 0 );
  297. if ( !bResult )
  298. {
  299. Assert( 0 );
  300. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Missing asset file: %s", szFileName );
  301. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  302. }
  303. m_pStudioHdr[ nModel ] = ( studiohdr_t * )MDL_Data[ nModel ]->PeekGet();
  304. m_pStudioHdr2[ nModel ] = ( studiohdr2_t * )MDL_Data[ nModel ]->PeekGet( m_pStudioHdr[ nModel ]->studiohdr2index );
  305. VTX_Data[ nModel ] = new CUtlBuffer();
  306. V_strcpy_safe( szFileName, STRING(m_pCombinedStudioData->m_ModelInputData[ nModel ].m_iszModelName) );
  307. Q_SetExtension( szFileName, ".dx90.vtx", sizeof( szFileName ) ); // GetVTXExtension() private :(
  308. bResult = g_pFullFileSystem->ReadFile( szFileName, "GAME", *VTX_Data[ nModel ], 0 );
  309. if ( !bResult )
  310. {
  311. Assert( 0 );
  312. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Missing asset file: %s", szFileName );
  313. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  314. }
  315. VVD_Data[ nModel ] = new CUtlBuffer();
  316. V_strcpy_safe( szFileName, STRING(m_pCombinedStudioData->m_ModelInputData[ nModel ].m_iszModelName) );
  317. Q_SetExtension( szFileName, ".vvd", sizeof( szFileName ) );
  318. bResult = g_pFullFileSystem->ReadFile( szFileName, "GAME", *VVD_Data[ nModel ], 0 );
  319. if ( !bResult )
  320. {
  321. Assert( 0 );
  322. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Missing asset file: %s", szFileName );
  323. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  324. }
  325. m_pVertexFileHeader[ nModel ] = ( vertexFileHeader_t * )VVD_Data[ nModel ]->PeekGet();
  326. }
  327. m_pCombinedStudioData->m_Results.m_flModelLoadDuration += ( float )( Plat_FloatTime() - flStartLoadTime );
  328. CombineTextures();
  329. double flStartCombineTime = Plat_FloatTime();
  330. DetermineMasterBoneList();
  331. CombineVVD();
  332. CombineVTX();
  333. #ifdef TEST_MDL_COMBINE
  334. CombineMDL( true );
  335. TestCombineMDL();
  336. #else
  337. CombineMDL( false );
  338. #endif
  339. g_CombinerWriter.SetWriteArea( -1 );
  340. m_pCombinedStudioData->m_Results.m_flModelCombineDuration += ( float )( Plat_FloatTime() - flStartCombineTime );
  341. m_pCombinedStudioData->m_Results.m_nCombinedResults = COMBINE_RESULT_FLAG_OK;
  342. }
  343. catch( ECombinedResult nFlags )
  344. {
  345. m_pCombinedStudioData->m_Results.m_nCombinedResults = nFlags;
  346. }
  347. GetTextureCombiner().Cleanup();
  348. for( int i = 0; i < m_pCombinedStudioData->m_nNumModels; i++ )
  349. {
  350. delete MDL_Data[ i ];
  351. delete VTX_Data[ i ];
  352. delete VVD_Data[ i ];
  353. }
  354. return ( m_pCombinedStudioData->m_Results.m_nCombinedResults == COMBINE_RESULT_FLAG_OK );
  355. }
  356. void CModelCombine::DetermineMasterBoneList( )
  357. {
  358. studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
  359. if ( pPrimaryStudioHdr->numbones > COMBINER_MAX_BONES )
  360. {
  361. Assert( 0 );
  362. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many bones in primary model: %d > %d", pPrimaryStudioHdr->numbones, COMBINER_MAX_BONES );
  363. throw( COMBINE_RESULT_FLAG_TOO_MANY_BONES );
  364. }
  365. memset( m_nMasterToLocalBoneRemap, -1, sizeof( m_nMasterToLocalBoneRemap ) );
  366. m_nNumMasterBones = 0;
  367. for( int nBone = 0; nBone < pPrimaryStudioHdr->numbones; nBone++ )
  368. {
  369. const mstudiobone_t *pOrigBone = pPrimaryStudioHdr->pBone( nBone );
  370. m_pMasterBoneList[ m_nNumMasterBones ] = pOrigBone;
  371. m_nBoneModelOwner[ m_nNumMasterBones ] = 0;
  372. m_nBoneRemap[ 0 ][ nBone ] = m_nNumMasterBones;
  373. m_nMasterToLocalBoneRemap[ 0 ][ m_nNumMasterBones ] = nBone;
  374. m_nNumMasterBones++;
  375. }
  376. for( int nModel = 1; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  377. {
  378. studiohdr_t *pSecondaryStudioHdr = m_pStudioHdr[ nModel ];
  379. for( int nBone = 0; nBone < pSecondaryStudioHdr->numbones; nBone++ )
  380. {
  381. const mstudiobone_t *pOrigBone = pSecondaryStudioHdr->pBone( nBone );
  382. char *pszName = pOrigBone->pszName();
  383. bool bFound = false;
  384. for ( int nBoneSearch = 0 ; nBoneSearch < m_nNumMasterBones; nBoneSearch++ )
  385. {
  386. if ( strcmpi( m_pMasterBoneList[ nBoneSearch ]->pszName(), pszName ) == 0 )
  387. {
  388. m_nBoneRemap[ nModel ][ nBone ] = nBoneSearch;
  389. m_nMasterToLocalBoneRemap[ nModel ][ nBoneSearch ] = nBone;
  390. bFound = true;
  391. break;
  392. }
  393. }
  394. if ( bFound == false )
  395. {
  396. if ( m_nNumMasterBones >= COMBINER_MAX_BONES )
  397. {
  398. Assert( 0 );
  399. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many bones from secondary model %s", pSecondaryStudioHdr->pszName() );
  400. throw( COMBINE_RESULT_FLAG_TOO_MANY_BONES );
  401. }
  402. m_pMasterBoneList[ m_nNumMasterBones ] = pOrigBone;
  403. m_nBoneModelOwner[ m_nNumMasterBones ] = nModel;
  404. m_nBoneRemap[ nModel ][ nBone ] = m_nNumMasterBones;
  405. m_nMasterToLocalBoneRemap[ nModel ][ m_nNumMasterBones ] = nBone;
  406. m_nNumMasterBones++;
  407. }
  408. }
  409. }
  410. }
  411. void CModelCombine::CombineMDL_PreintStrings( )
  412. {
  413. char *pStringData = m_pStudioHdr2[ 0 ]->pszName(); // this is the first entry in the string table;
  414. char *pEndData = ( ( char * ) m_pStudioHdr[ 0 ] ) + m_pStudioHdr[ 0 ]->length;
  415. while( pStringData < pEndData )
  416. {
  417. if ( numStrings >= COMBINER_MAX_STRINGS )
  418. {
  419. Assert( 0 );
  420. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "Too many initial strings %d > %d", numStrings, COMBINER_MAX_STRINGS );
  421. throw( COMBINE_RESULT_FLAG_TOO_MANY_STRINGS );
  422. }
  423. strings[ numStrings ].base = NULL;
  424. strings[ numStrings ].ptr = NULL;
  425. strings[ numStrings ].string = pStringData;
  426. strings[ numStrings ].dupindex = -2;
  427. numStrings++;
  428. pStringData += strlen( pStringData ) + 1;
  429. }
  430. }
  431. void CModelCombine::WriteBoneProc( int nSize, int nType1, int nType2 )
  432. {
  433. for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ )
  434. {
  435. const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ];
  436. mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
  437. if ( pNewBone->proctype == nType1 || pNewBone->proctype == nType2 )
  438. {
  439. char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), nSize );
  440. pNewBone->procindex = (byte *)pPos - (byte *)pNewBone;
  441. }
  442. }
  443. g_CombinerWriter.AlignWrite( 4 );
  444. }
  445. void CModelCombine::WriteBoneQuatInterp( )
  446. {
  447. for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ )
  448. {
  449. const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ];
  450. mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
  451. if ( pNewBone->proctype == STUDIO_PROC_QUATINTERP )
  452. {
  453. mstudioquatinterpbone_t *pOrigProc = ( mstudioquatinterpbone_t * )pOrigBone->pProcedure();
  454. mstudioquatinterpbone_t *pNewProc = ( mstudioquatinterpbone_t * )pNewBone->pProcedure();
  455. char *pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTrigger( 0 ), pOrigProc->numtriggers * sizeof( mstudioquatinterpinfo_t ) );
  456. pNewProc->triggerindex = (byte *)pPos - (byte *)pNewProc;
  457. }
  458. }
  459. // AlignWrite( 4 );
  460. }
  461. void CModelCombine::WriteBoneTwist( )
  462. {
  463. for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ )
  464. {
  465. const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ];
  466. mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
  467. if ( pNewBone->proctype == STUDIO_PROC_TWIST_MASTER || pNewBone->proctype == STUDIO_PROC_TWIST_SLAVE )
  468. {
  469. mstudiotwistbone_t *pOrigProc = ( mstudiotwistbone_t * )pOrigBone->pProcedure();
  470. mstudiotwistbone_t *pNewProc = ( mstudiotwistbone_t * )pNewBone->pProcedure();
  471. char *pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudiotwistbonetarget_t ) );
  472. pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
  473. g_CombinerWriter.AlignWrite( 4 );
  474. }
  475. }
  476. // AlignWrite( 4 );
  477. }
  478. void CModelCombine::WriteBoneConstraints( )
  479. {
  480. for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ )
  481. {
  482. const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ];
  483. mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
  484. // make this not repeat the same code
  485. switch( pNewBone->proctype )
  486. {
  487. case STUDIO_PROC_POINT_CONSTRAINT:
  488. {
  489. char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudiopointconstraint_t ) );
  490. pNewBone->procindex = (byte *)pPos - (byte *)pNewBone;
  491. g_CombinerWriter.AlignWrite( 4 );
  492. mstudiopointconstraint_t *pOrigProc = ( mstudiopointconstraint_t * )pOrigBone->pProcedure();
  493. mstudiopointconstraint_t *pNewProc = ( mstudiopointconstraint_t * )pNewBone->pProcedure();
  494. pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) );
  495. pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
  496. g_CombinerWriter.AlignWrite( 4 );
  497. }
  498. break;
  499. case STUDIO_PROC_ORIENT_CONSTRAINT:
  500. {
  501. char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudioorientconstraint_t ) );
  502. pNewBone->procindex = (byte *)pPos - (byte *)pNewBone;
  503. g_CombinerWriter.AlignWrite( 4 );
  504. mstudioorientconstraint_t *pOrigProc = ( mstudioorientconstraint_t * )pOrigBone->pProcedure();
  505. mstudioorientconstraint_t *pNewProc = ( mstudioorientconstraint_t * )pNewBone->pProcedure();
  506. pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) );
  507. pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
  508. g_CombinerWriter.AlignWrite( 4 );
  509. }
  510. break;
  511. case STUDIO_PROC_AIM_CONSTRAINT:
  512. {
  513. char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudioaimconstraint_t ) );
  514. pNewBone->procindex = (byte *)pPos - (byte *)pNewBone;
  515. g_CombinerWriter.AlignWrite( 4 );
  516. mstudioaimconstraint_t *pOrigProc = ( mstudioaimconstraint_t * )pOrigBone->pProcedure();
  517. mstudioaimconstraint_t *pNewProc = ( mstudioaimconstraint_t * )pNewBone->pProcedure();
  518. pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) );
  519. pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
  520. g_CombinerWriter.AlignWrite( 4 );
  521. }
  522. break;
  523. case STUDIO_PROC_PARENT_CONSTRAINT:
  524. {
  525. char *pPos = g_CombinerWriter.WriteBuffer( pOrigBone->pProcedure(), sizeof( mstudioparentconstraint_t ) );
  526. pNewBone->procindex = (byte *)pPos - (byte *)pNewBone;
  527. g_CombinerWriter.AlignWrite( 4 );
  528. mstudioparentconstraint_t *pOrigProc = ( mstudioparentconstraint_t * )pOrigBone->pProcedure();
  529. mstudioparentconstraint_t *pNewProc = ( mstudioparentconstraint_t * )pNewBone->pProcedure();
  530. pPos = g_CombinerWriter.WriteBuffer( pOrigProc->pTarget( 0 ), pOrigProc->m_nTargetCount * sizeof( mstudioconstrainttarget_t ) );
  531. pNewProc->m_nTargetIndex = (byte *)pPos - (byte *)pNewProc;
  532. g_CombinerWriter.AlignWrite( 4 );
  533. }
  534. break;
  535. }
  536. }
  537. }
  538. void CModelCombine::WriteBoneAttachments( )
  539. {
  540. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localattachmentindex );
  541. m_pCombinedStudioHdr->numlocalattachments = 0;
  542. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  543. {
  544. studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
  545. int nNumAttachments = pStudioHdr->numlocalattachments;
  546. for( int nAttachment = 0; nAttachment < nNumAttachments; nAttachment++ )
  547. {
  548. m_pCombinedStudioHdr->numlocalattachments++;
  549. mstudioattachment_t *pOrigAttachment = pStudioHdr->pLocalAttachment( nAttachment );
  550. mstudioattachment_t *pNewAttachment = m_pCombinedStudioHdr->pLocalAttachment( m_pCombinedStudioHdr->numlocalattachments - 1 );
  551. g_CombinerWriter.WriteBuffer( pOrigAttachment, sizeof( *pOrigAttachment ) );
  552. AddToStringTable( pNewAttachment, &pNewAttachment->sznameindex, pOrigAttachment->pszName() );
  553. RemapBone( nModel, pNewAttachment->localbone );
  554. }
  555. }
  556. g_CombinerWriter.AlignWrite( 4 );
  557. VerifyField( offsetof( studiohdr_t, localattachmentindex ), "BoneAttachments" );
  558. }
  559. void CModelCombine::WriteHitBoxes( )
  560. {
  561. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->hitboxsetindex );
  562. m_pCombinedStudioHdr->numhitboxsets = 0;
  563. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  564. {
  565. if ( nModel > 0 )
  566. { // for now, we are only interested in the hitbox sets of the primary model
  567. break;
  568. }
  569. studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
  570. int nNumHitBoxSets = pStudioHdr->numhitboxsets;
  571. for( int nHitBoxSet = 0; nHitBoxSet < nNumHitBoxSets; nHitBoxSet++ )
  572. {
  573. m_pCombinedStudioHdr->numhitboxsets++;
  574. mstudiohitboxset_t *pOrigHitBoxSet = pStudioHdr->pHitboxSet( nHitBoxSet );
  575. mstudiohitboxset_t *pNewHitBoxSet = m_pCombinedStudioHdr->pHitboxSet( m_pCombinedStudioHdr->numhitboxsets - 1 );
  576. g_CombinerWriter.WriteBuffer( pOrigHitBoxSet, sizeof( *pOrigHitBoxSet ) );
  577. AddToStringTable( pNewHitBoxSet, &pNewHitBoxSet->sznameindex, pOrigHitBoxSet->pszName() );
  578. }
  579. }
  580. g_CombinerWriter.AlignWrite( 4 );
  581. for( int nHitBoxSet = 0; nHitBoxSet < m_pCombinedStudioHdr->numhitboxsets; nHitBoxSet++ )
  582. {
  583. mstudiohitboxset_t *pNewHitBoxSet = m_pCombinedStudioHdr->pHitboxSet( nHitBoxSet );
  584. pNewHitBoxSet->numhitboxes = 0;
  585. g_CombinerWriter.WriteOffset( pNewHitBoxSet->hitboxindex, pNewHitBoxSet );
  586. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  587. {
  588. studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
  589. if ( nModel > 0 )
  590. {
  591. break;
  592. }
  593. if ( nHitBoxSet < pStudioHdr->numhitboxsets )
  594. {
  595. mstudiohitboxset_t *pOrigHitBoxSet = pStudioHdr->pHitboxSet( nHitBoxSet );
  596. for( int nHitBox = 0; nHitBox < pOrigHitBoxSet->numhitboxes; nHitBox++ )
  597. {
  598. pNewHitBoxSet->numhitboxes++;
  599. mstudiobbox_t *pOrigHitBox = pOrigHitBoxSet->pHitbox( nHitBox );
  600. mstudiobbox_t *pNewHitBox = pNewHitBoxSet->pHitbox( pNewHitBoxSet->numhitboxes - 1 );
  601. g_CombinerWriter.WriteBuffer( pOrigHitBox, sizeof( *pOrigHitBox ) );
  602. AddToStringTable( pNewHitBox, &pNewHitBox->szhitboxnameindex, pOrigHitBox->pszHitboxName() );
  603. RemapBone( nModel, pNewHitBox->bone );
  604. }
  605. }
  606. }
  607. g_CombinerWriter.AlignWrite( 4 );
  608. }
  609. VerifyField( offsetof( studiohdr_t, hitboxsetindex ), "HitBoxes" );
  610. }
  611. // compare function for qsort below
  612. int CModelCombine::BoneNameCompare( const void *elem1, const void *elem2 )
  613. {
  614. int index1 = *(byte *)elem1;
  615. int index2 = *(byte *)elem2;
  616. // compare bones by name
  617. return strcmpi( s_pCurrentCombine->m_pMasterBoneList[ index1 ]->pszName(), s_pCurrentCombine->m_pMasterBoneList[ index2 ]->pszName() );
  618. }
  619. void CModelCombine::WriteBoneTable( )
  620. {
  621. byte *pBoneTable = ( byte * )g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->bonetablebynameindex );
  622. g_CombinerWriter.AllocWrite( sizeof( *pBoneTable ) * m_pCombinedStudioHdr->numbones );
  623. for ( int i = 0; i < m_pCombinedStudioHdr->numbones; i++ )
  624. {
  625. pBoneTable[ i ] = i;
  626. }
  627. qsort( pBoneTable, m_pCombinedStudioHdr->numbones, sizeof( byte ), BoneNameCompare );
  628. }
  629. void CModelCombine::CombineMDL_Bones( )
  630. {
  631. studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
  632. AddToStringTable( m_pCombinedStudioHdr, &m_pCombinedStudioHdr->surfacepropindex, pPrimaryStudioHdr->pszSurfaceProp() );
  633. int *nFlags = ( int * )stackalloc( sizeof( int ) * m_nNumMasterBones );
  634. memset( nFlags, 0, sizeof( int ) * m_nNumMasterBones );
  635. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  636. {
  637. OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet();
  638. int nBoneTest = 0;
  639. int nBoneProp = 0;
  640. if ( nModel > 0 )
  641. {
  642. nBoneTest = BONE_USED_BY_VERTEX_AT_LOD( pOrigHeader->numLODs - 1 );
  643. for( int nLOD = pOrigHeader->numLODs; nLOD < m_pCombinedHardwareHeader->numLODs; nLOD++ )
  644. {
  645. nBoneProp |= BONE_USED_BY_VERTEX_AT_LOD( nLOD );
  646. }
  647. }
  648. for( int nBone = 0; nBone < m_pStudioHdr[ nModel ]->numbones; nBone++ )
  649. {
  650. nFlags[ m_nBoneRemap[ nModel ][ nBone ] ] |= m_pStudioHdr[ nModel ]->pBone( nBone )->flags;
  651. if ( nModel > 0 && pOrigHeader->numLODs < m_pCombinedHardwareHeader->numLODs )
  652. {
  653. if ( ( m_pStudioHdr[ nModel ]->pBone( nBone )->flags & nBoneTest ) != 0 )
  654. {
  655. nFlags[ m_nBoneRemap[ nModel ][ nBone ] ] |= nBoneProp;
  656. }
  657. }
  658. }
  659. }
  660. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->boneindex );
  661. m_pCombinedStudioHdr->numbones = 0;
  662. for( int nBone = 0; nBone < pPrimaryStudioHdr->numbones; nBone++ )
  663. {
  664. const mstudiobone_t *pOrigBone = pPrimaryStudioHdr->pBone( nBone );
  665. mstudiobone_t *pNewBone = ( mstudiobone_t * )g_CombinerWriter.WriteBuffer( pOrigBone, sizeof( mstudiobone_t ) );
  666. AddToStringTable( pNewBone, &pNewBone->sznameindex, pOrigBone->pszName() );
  667. AddToStringTable( pNewBone, &pNewBone->surfacepropidx, pOrigBone->pszSurfaceProp() );
  668. pNewBone->flags = nFlags[ nBone ];
  669. m_pCombinedStudioHdr->numbones++;
  670. }
  671. for( int nBone = m_pCombinedStudioHdr->numbones; nBone < m_nNumMasterBones; nBone++ )
  672. {
  673. const mstudiobone_t *pOrigBone = m_pMasterBoneList[ nBone ];
  674. mstudiobone_t *pNewBone = ( mstudiobone_t * )g_CombinerWriter.WriteBuffer( pOrigBone, sizeof( mstudiobone_t ) );
  675. AddToStringTable( pNewBone, &pNewBone->sznameindex, pOrigBone->pszName() );
  676. AddToStringTable( pNewBone, &pNewBone->surfacepropidx, pOrigBone->pszSurfaceProp() );
  677. if ( pNewBone->parent >= 0 )
  678. {
  679. pNewBone->parent = m_nBoneRemap[ m_nBoneModelOwner[ nBone ] ][ pNewBone->parent ];
  680. }
  681. pNewBone->flags = nFlags[ nBone ];
  682. m_pCombinedStudioHdr->numbones++;
  683. }
  684. g_CombinerWriter.AlignWrite( 4 );
  685. WriteBoneProc( sizeof( mstudioaxisinterpbone_t ), STUDIO_PROC_AXISINTERP );
  686. WriteBoneProc( sizeof( mstudioquatinterpbone_t ), STUDIO_PROC_QUATINTERP );
  687. WriteBoneQuatInterp();
  688. WriteBoneProc( sizeof( mstudiojigglebone_t ), STUDIO_PROC_JIGGLE );
  689. WriteBoneProc( sizeof( mstudioaimatbone_t ), STUDIO_PROC_AIMATBONE, STUDIO_PROC_AIMATATTACH ); // aimAttach needs fixup
  690. WriteBoneProc( sizeof( mstudiotwistbone_t ), STUDIO_PROC_TWIST_MASTER, STUDIO_PROC_TWIST_SLAVE );
  691. WriteBoneTwist();
  692. WriteBoneConstraints();
  693. if ( pPrimaryStudioHdr->numbonecontrollers > 0 )
  694. {
  695. Assert( 0 );
  696. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has bone controllers: %s", pPrimaryStudioHdr->pszName() );
  697. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  698. }
  699. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->bonecontrollerindex );
  700. g_CombinerWriter.AlignWrite( 4 );
  701. WriteBoneAttachments();
  702. WriteHitBoxes();
  703. WriteBoneTable();
  704. g_CombinerWriter.AlignWrite( 4 );
  705. }
  706. void CModelCombine::WriteAnimation( mstudioanimdesc_t *pOrigAnim, void *pAnimData, int nFrameSize )
  707. {
  708. if ( ( pOrigAnim->flags & STUDIO_FRAMEANIM ) != 0 )
  709. {
  710. Assert( 0 );
  711. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has anim frames" );
  712. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  713. }
  714. else
  715. {
  716. mstudio_rle_anim_t *pOrigFrame = ( mstudio_rle_anim_t * )( pAnimData );
  717. while( 1 )
  718. {
  719. int nSize = sizeof( mstudio_rle_anim_t );
  720. if ( pOrigFrame->flags & STUDIO_ANIM_RAWROT2 )
  721. {
  722. nSize += sizeof( Quaternion64 );
  723. }
  724. if ( pOrigFrame->flags & STUDIO_ANIM_RAWPOS )
  725. {
  726. nSize += sizeof( Vector48 );
  727. }
  728. if ( pOrigFrame->flags & STUDIO_ANIM_ANIMROT )
  729. {
  730. nSize += sizeof( mstudioanim_valueptr_t );
  731. }
  732. if ( pOrigFrame->flags & STUDIO_ANIM_ANIMPOS )
  733. {
  734. nSize += sizeof( mstudioanim_valueptr_t );
  735. }
  736. if ( pOrigFrame->flags & STUDIO_ANIM_ANIMROT )
  737. {
  738. mstudioanim_valueptr_t *rotvptr = pOrigFrame->pRotV();
  739. for (int k = 0; k < 3; k++)
  740. {
  741. mstudioanimvalue_t *pAnimValue = rotvptr->pAnimvalue( k );
  742. if ( pAnimValue != NULL )
  743. {
  744. int nCount = nFrameSize;
  745. while( nCount > 0 )
  746. {
  747. nSize += sizeof( *pAnimValue ) * ( pAnimValue->num.valid + 1 );
  748. nCount -= pAnimValue->num.total;
  749. pAnimValue += pAnimValue->num.valid + 1;
  750. }
  751. }
  752. }
  753. }
  754. if ( pOrigFrame->flags & STUDIO_ANIM_ANIMPOS )
  755. {
  756. mstudioanim_valueptr_t *posvptr = pOrigFrame->pPosV();
  757. for (int k = 0; k < 3; k++)
  758. {
  759. mstudioanimvalue_t *pAnimValue = posvptr->pAnimvalue( k );
  760. if ( pAnimValue != NULL )
  761. {
  762. int nCount = nFrameSize;
  763. while( nCount > 0 )
  764. {
  765. nSize += sizeof( *pAnimValue ) * ( pAnimValue->num.valid + 1 );
  766. nCount -= pAnimValue->num.total;
  767. pAnimValue += pAnimValue->num.valid + 1;
  768. }
  769. }
  770. }
  771. }
  772. // all of the indexes are relative, so a direct copy should be safe
  773. g_CombinerWriter.WriteBuffer( pOrigFrame, nSize );
  774. // DebugCombineMsg( "%d: %d\n", pOrigFrame->bone, pOrigFrame->nextoffset - nSize );
  775. if ( pOrigFrame->nextoffset == 0 )
  776. {
  777. break;
  778. }
  779. else
  780. {
  781. pOrigFrame = pOrigFrame->pNext();
  782. VerifyOffset( pOrigFrame, "Start rle_anim" );
  783. }
  784. }
  785. g_CombinerWriter.AllocWrite( sizeof( mstudio_rle_anim_t ) ); // bug in studiomdl which adds an extra one of these
  786. }
  787. g_CombinerWriter.AlignWrite( 4 );
  788. }
  789. void CModelCombine::CombineMDL_Anims( )
  790. {
  791. studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
  792. VerifyOffset( pStudioHdr->pLocalAnimdesc( 0 ), "Start Animations" );
  793. g_CombinerWriter.WriteBufferWithOffset( pStudioHdr->pLocalAnimdesc( 0 ), sizeof( mstudioanimdesc_t ) * pStudioHdr->numlocalanim, m_pCombinedStudioHdr->localanimindex );
  794. g_CombinerWriter.AlignWrite( 4 );
  795. m_pCombinedStudioHdr->numlocalanim = pStudioHdr->numlocalanim;
  796. for( int nAnim = 0; nAnim < m_pCombinedStudioHdr->numlocalanim; nAnim++ )
  797. {
  798. mstudioanimdesc_t *pOrigAnim = pStudioHdr->pLocalAnimdesc( nAnim );
  799. mstudioanimdesc_t *pNewAnim = m_pCombinedStudioHdr->pLocalAnimdesc( nAnim );
  800. pNewAnim->baseptr = ( byte * )g_CombinerWriter.GetWriteArea() - ( byte * )pNewAnim;
  801. AddToStringTable( pNewAnim, &pNewAnim->sznameindex, pOrigAnim->pszName() );
  802. int nNumSections = 0;
  803. if ( pOrigAnim->sectionframes > 1 )
  804. {
  805. nNumSections = ( pOrigAnim->numframes / pOrigAnim->sectionframes ) + 2; // studio.cpp line 113
  806. pNewAnim->sectionindex = ( byte * )g_CombinerWriter.AllocWrite( nNumSections * sizeof( mstudioanimsections_t ) ) - (byte *)pNewAnim;
  807. }
  808. g_CombinerWriter.AlignWrite( 16 );
  809. pNewAnim->sectionframes = pOrigAnim->sectionframes;
  810. g_CombinerWriter.WriteOffset( pNewAnim->animindex, pNewAnim );
  811. DebugCombineMsg( "Anim %d: %d / %d ( %d )\n", nAnim, pOrigAnim->animindex, pNewAnim->animindex, pNewAnim->animindex - pOrigAnim->animindex );
  812. if ( nNumSections > 1 )
  813. {
  814. int nRemainingFrames = pOrigAnim->numframes;
  815. for( int nSection = 0; nSection < nNumSections; nSection++ )
  816. {
  817. mstudioanimsections_t *pOrigSection = pOrigAnim->pSection( nSection );
  818. mstudioanimsections_t *pNewSection = pNewAnim->pSection( nSection );
  819. VerifyOffset( pOrigSection, "Start mstudioanimsections_t", pNewSection );
  820. *pNewSection = *pOrigSection;
  821. g_CombinerWriter.WriteOffset( pNewSection->animindex, pNewAnim );
  822. int nFrames;
  823. if ( nRemainingFrames > pOrigAnim->sectionframes )
  824. {
  825. nRemainingFrames -= pOrigAnim->sectionframes;
  826. nFrames = pOrigAnim->sectionframes + 1;
  827. }
  828. else
  829. {
  830. nFrames = nRemainingFrames;
  831. nRemainingFrames = 0;
  832. }
  833. WriteAnimation( pOrigAnim, ( ( byte * )pOrigAnim ) + pOrigSection->animindex, nFrames );
  834. }
  835. }
  836. else
  837. {
  838. WriteAnimation( pOrigAnim, pOrigAnim->pAnimBlock( pOrigAnim->animblock, pOrigAnim->animindex ), pOrigAnim->numframes );
  839. }
  840. g_CombinerWriter.AlignWrite( 4 );
  841. if ( pOrigAnim->numikrules > 0 )
  842. {
  843. Assert( 0 );
  844. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has ik rules: %s", pStudioHdr->pszName() );
  845. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  846. }
  847. g_CombinerWriter.AlignWrite( 4 );
  848. if ( pOrigAnim->numlocalhierarchy > 0 )
  849. {
  850. g_CombinerWriter.WriteOffset( pNewAnim->localhierarchyindex, pNewAnim );
  851. g_CombinerWriter.WriteBuffer( pOrigAnim->pHierarchy( 0 ), pOrigAnim->numlocalhierarchy * sizeof( mstudiolocalhierarchy_t ) );
  852. for( int nLocalHierarchy = 0; nLocalHierarchy < pOrigAnim->numlocalhierarchy; nLocalHierarchy++ )
  853. {
  854. mstudiolocalhierarchy_t *pOrigLocalHierarchy = pOrigAnim->pHierarchy( nLocalHierarchy );
  855. mstudiolocalhierarchy_t *pNewLocalHierarchy = pNewAnim->pHierarchy( nLocalHierarchy );
  856. g_CombinerWriter.WriteOffset( pNewLocalHierarchy->localanimindex, pNewLocalHierarchy );
  857. mstudiocompressedikerror_t *pOrigCompressedIKError = pOrigLocalHierarchy->pLocalAnim();
  858. mstudiocompressedikerror_t *pNewCompressedIKError = pNewLocalHierarchy->pLocalAnim();
  859. g_CombinerWriter.WriteBuffer( pOrigCompressedIKError, sizeof( *pOrigCompressedIKError ) );
  860. for( int nAnim = 0; nAnim < 6; nAnim++ )
  861. {
  862. g_CombinerWriter.WriteOffset( pNewCompressedIKError->offset[ nAnim ], pNewCompressedIKError );
  863. int nSize = 0;
  864. mstudioanimvalue_t *pAnimValue = pOrigCompressedIKError->pAnimvalue( nAnim );
  865. if ( pAnimValue != NULL )
  866. {
  867. int nCount = pOrigAnim->numframes;
  868. while( nCount > 0 )
  869. {
  870. nSize += sizeof( *pAnimValue ) * ( pAnimValue->num.valid + 1 );
  871. nCount -= pAnimValue->num.total;
  872. pAnimValue += pAnimValue->num.valid + 1;
  873. }
  874. }
  875. g_CombinerWriter.WriteBuffer( pOrigCompressedIKError->pAnimvalue( nAnim ), nSize );
  876. }
  877. }
  878. }
  879. g_CombinerWriter.AlignWrite( 4 );
  880. }
  881. for( int nAnim = 0; nAnim < m_pCombinedStudioHdr->numlocalanim; nAnim++ )
  882. {
  883. mstudioanimdesc_t *pOrigAnim = pStudioHdr->pLocalAnimdesc( nAnim );
  884. mstudioanimdesc_t *pNewAnim = m_pCombinedStudioHdr->pLocalAnimdesc( nAnim );
  885. if ( pOrigAnim->nummovements > 0 )
  886. {
  887. g_CombinerWriter.WriteOffset( pNewAnim->movementindex, pNewAnim );
  888. VerifyOffset( pOrigAnim->pMovement( 0 ), "Start Movement" );
  889. g_CombinerWriter.WriteBuffer( pOrigAnim->pMovement( 0 ), sizeof( mstudiomovement_t ) * pOrigAnim->nummovements );
  890. g_CombinerWriter.AlignWrite( 4 );
  891. }
  892. }
  893. g_CombinerWriter.AlignWrite( 4 );
  894. }
  895. void CModelCombine::CombineMDL_SequenceInfo( )
  896. {
  897. studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
  898. VerifyField( offsetof( studiohdr_t, localseqindex ), "Sequence Info" );
  899. m_pCombinedStudioHdr->numlocalseq = pStudioHdr->numlocalseq;
  900. g_CombinerWriter.WriteBufferWithOffset( pStudioHdr->pLocalSeqdesc( 0 ), sizeof( mstudioseqdesc_t ) * pStudioHdr->numlocalseq, m_pCombinedStudioHdr->localseqindex );
  901. for( int nSequence = 0; nSequence < pStudioHdr->numlocalseq; nSequence++ )
  902. {
  903. mstudioseqdesc_t *pOrigSequence = pStudioHdr->pLocalSeqdesc( nSequence );
  904. mstudioseqdesc_t *pNewSequence = m_pCombinedStudioHdr->pLocalSeqdesc( nSequence );
  905. AddToStringTable( pNewSequence, &pNewSequence->szlabelindex, pOrigSequence->pszLabel() );
  906. AddToStringTable( pNewSequence, &pNewSequence->szactivitynameindex, pOrigSequence->pszActivityName() );
  907. pNewSequence->baseptr = ( byte * )g_CombinerWriter.GetWriteArea() - ( byte * )pNewSequence;
  908. if ( pOrigSequence->groupsize[ 0 ] > 1 || pOrigSequence->groupsize[ 1 ] > 1 )
  909. {
  910. g_CombinerWriter.WriteOffset( pNewSequence->posekeyindex, pNewSequence );
  911. int nSize = ( pOrigSequence->groupsize[ 0 ] + pOrigSequence->groupsize[ 1 ] ) * sizeof( float );
  912. g_CombinerWriter.WriteBuffer( pOrigSequence->pPoseKey( 0, 0 ), nSize );
  913. }
  914. g_CombinerWriter.WriteOffset( pNewSequence->eventindex, pNewSequence );
  915. if ( pOrigSequence->numevents > 0 )
  916. {
  917. VerifyOffset( pOrigSequence->pEvent( 0 ), "Sequence Event" );
  918. g_CombinerWriter.WriteBuffer( pOrigSequence->pEvent( 0 ), pOrigSequence->numevents * sizeof( mstudioevent_t ) );
  919. for ( int nEvent = 0; nEvent < pOrigSequence->numevents; nEvent++ )
  920. {
  921. mstudioevent_t *pOrigEvent = pOrigSequence->pEvent( nEvent );
  922. mstudioevent_t *pNewEvent = pNewSequence->pEvent( nEvent );
  923. if ( pOrigEvent->type == NEW_EVENT_STYLE )
  924. {
  925. AddToStringTable( pNewEvent, &pNewEvent->szeventindex, pOrigEvent->pszEventName() );
  926. }
  927. }
  928. }
  929. g_CombinerWriter.AlignWrite( 4 );
  930. g_CombinerWriter.WriteOffset( pNewSequence->autolayerindex, pNewSequence );
  931. if ( pOrigSequence->numautolayers > 0 )
  932. {
  933. VerifyOffset( pOrigSequence->pAutolayer( 0 ), "Auto Layer" );
  934. g_CombinerWriter.WriteBuffer( pOrigSequence->pAutolayer( 0 ), pOrigSequence->numautolayers * sizeof( mstudioautolayer_t ) );
  935. }
  936. if ( pOrigSequence->weightlistindex < pOrigSequence->eventindex )
  937. { // we are using an existing weight
  938. int nFound = -1;
  939. for( int nPreviousSequence = 0; nPreviousSequence < nSequence; nPreviousSequence++ )
  940. {
  941. mstudioseqdesc_t *pOrigPreviousSequence = pStudioHdr->pLocalSeqdesc( nPreviousSequence );
  942. if ( memcmp( pOrigPreviousSequence->pBoneweight( 0 ), pOrigSequence->pBoneweight( 0 ), pStudioHdr->numbones * sizeof( float ) ) == 0 )
  943. {
  944. nFound = nPreviousSequence;
  945. break;
  946. }
  947. }
  948. if ( nFound != -1 )
  949. {
  950. mstudioseqdesc_t *pNewPreviousSequence = m_pCombinedStudioHdr->pLocalSeqdesc( nFound );
  951. pNewSequence->weightlistindex = ( ( byte * )pNewPreviousSequence->pBoneweight( 0 ) - ( byte * )pNewSequence);
  952. }
  953. else
  954. {
  955. Assert( 0 );
  956. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "was not able to find existing weight in model: %s", pStudioHdr->pszName() );
  957. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  958. }
  959. }
  960. else
  961. {
  962. g_CombinerWriter.WriteOffset( pNewSequence->weightlistindex, pNewSequence );
  963. // Assert( m_nNumMasterBones == pStudioHdr->numbones ); // need to handle bone merge!
  964. g_CombinerWriter.WriteBuffer( pOrigSequence->pBoneweight( 0 ), pStudioHdr->numbones * sizeof( float ) );
  965. float flTempWeight = 1.0f;
  966. for( int nNewBones = pStudioHdr->numbones; nNewBones < m_nNumMasterBones; nNewBones++ )
  967. { // more efficient ways to do this, but for now, not knowing if I can just give them all 1.0f, I'll leave it
  968. g_CombinerWriter.WriteBuffer( &flTempWeight, sizeof( flTempWeight ) );
  969. }
  970. }
  971. g_CombinerWriter.WriteOffset( pNewSequence->iklockindex, pNewSequence );
  972. if ( pOrigSequence->numiklocks > 0 )
  973. {
  974. VerifyOffset( pOrigSequence->pIKLock( 0 ), "IK Lock" );
  975. g_CombinerWriter.WriteBuffer( pOrigSequence->pIKLock( 0 ), pOrigSequence->numiklocks * sizeof( mstudioiklock_t ) );
  976. }
  977. g_CombinerWriter.AlignWrite( 4 );
  978. g_CombinerWriter.WriteOffset( pNewSequence->animindexindex, pNewSequence );
  979. int nSize = ( pOrigSequence->groupsize[ 0 ] * pOrigSequence->groupsize[ 1 ] ) * sizeof( short );
  980. if ( nSize > 0 )
  981. {
  982. VerifyOffset( ( ( ( byte * )pOrigSequence ) + pOrigSequence->animindexindex ), "AnimIndexIndex" );
  983. g_CombinerWriter.WriteBuffer( ( ( ( byte * )pOrigSequence ) + pOrigSequence->animindexindex ), nSize );
  984. }
  985. g_CombinerWriter.AlignWrite( 4 );
  986. g_CombinerWriter.WriteOffset( pNewSequence->keyvalueindex, pNewSequence );
  987. if( pOrigSequence->keyvaluesize > 0 )
  988. {
  989. VerifyOffset( ( void * )pOrigSequence->KeyValueText(), "Sequence KV" );
  990. g_CombinerWriter.WriteBuffer( ( void * )pOrigSequence->KeyValueText(), pOrigSequence->keyvaluesize * sizeof( char ) );
  991. }
  992. g_CombinerWriter.AlignWrite( 4 );
  993. g_CombinerWriter.WriteOffset( pNewSequence->activitymodifierindex, pNewSequence );
  994. if ( pOrigSequence->numactivitymodifiers > 0 )
  995. {
  996. g_CombinerWriter.WriteBuffer( pOrigSequence->pActivityModifier( 0 ), pOrigSequence->numactivitymodifiers * sizeof( mstudioactivitymodifier_t ) );
  997. }
  998. g_CombinerWriter.AlignWrite( 4 );
  999. for ( int nActivityModifier = 0; nActivityModifier < pOrigSequence->numactivitymodifiers; nActivityModifier++ )
  1000. {
  1001. mstudioactivitymodifier_t *pOrigActivityModifier = pOrigSequence->pActivityModifier( nActivityModifier );
  1002. mstudioactivitymodifier_t *pNewActivityModifier = pNewSequence->pActivityModifier( nActivityModifier );
  1003. AddToStringTable( pNewActivityModifier, &pNewActivityModifier->sznameindex, pOrigActivityModifier->pszName() );
  1004. }
  1005. nSize = pOrigSequence->numactivitymodifiers * sizeof( mstudioactivitymodifier_t );
  1006. byte *pTestPtr = ( ( ( byte * )pOrigSequence ) + pOrigSequence->activitymodifierindex ) + nSize;
  1007. VerifyOffset( pTestPtr, "Final Sequence Write" );
  1008. }
  1009. int *pNodeName = ( int * )g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localnodenameindex );
  1010. g_CombinerWriter.AllocWrite( pStudioHdr->numlocalnodes * sizeof( *pNodeName ) );
  1011. g_CombinerWriter.AlignWrite( 4 );
  1012. for ( int nNodeIndex = 0; nNodeIndex < pStudioHdr->numlocalnodes; nNodeIndex++ )
  1013. {
  1014. AddToStringTable( m_pCombinedStudioHdr, pNodeName, pStudioHdr->pszLocalNodeName( nNodeIndex ) );
  1015. pNodeName++;
  1016. }
  1017. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localnodeindex );
  1018. if ( pStudioHdr->numlocalnodes > 0 )
  1019. {
  1020. g_CombinerWriter.WriteBuffer( pStudioHdr->pLocalTransition( 0 ), pStudioHdr->numlocalnodes * pStudioHdr->numlocalnodes * sizeof( byte ) );
  1021. }
  1022. g_CombinerWriter.AlignWrite( 4 );
  1023. }
  1024. void CModelCombine::WriteModel( int nModel )
  1025. {
  1026. mstudiomodel_t *pOrigModel = m_pMasterModels[ 0 ][ nModel ];
  1027. mstudiomodel_t *pNewModel = &m_pCombinedModels[ nModel ];
  1028. g_CombinerWriter.WriteOffset( pNewModel->meshindex, pNewModel );
  1029. g_CombinerWriter.WriteBuffer( pOrigModel->pMesh( 0 ), pOrigModel->nummeshes * sizeof( mstudiomesh_t ) );
  1030. g_CombinerWriter.AlignWrite( 4 );
  1031. for ( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ )
  1032. {
  1033. // mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
  1034. mstudiomesh_t *pNewMesh = pNewModel->pMesh( nMesh );
  1035. pNewMesh->numvertices = m_pCombinedVertex->numLODVertexes[ 0 ];
  1036. for( int nLOD = 0; nLOD < m_pCombinedVertex->numLODs; nLOD++ )
  1037. {
  1038. pNewMesh->vertexdata.numLODVertexes[ nLOD ] = m_pCombinedVertex->numLODVertexes[ nLOD ];
  1039. }
  1040. pNewMesh->modelindex = ( byte * )pNewModel - ( byte * )pNewMesh;
  1041. }
  1042. // pNewModel->vertexindex = pOrigModel->vertexindex;
  1043. // externalVertexIndex += pmodel[i].numvertices * sizeof(mstudiovertex_t);
  1044. // ALIGN4( externalTangentsIndex );
  1045. // pmodel[i].tangentsindex = (int)externalTangentsIndex;
  1046. // externalTangentsIndex += pmodel[i].numvertices * sizeof( Vector4D );
  1047. g_CombinerWriter.WriteOffset( pNewModel->eyeballindex, pNewModel );
  1048. if ( pOrigModel->numeyeballs > 0 )
  1049. {
  1050. g_CombinerWriter.WriteBuffer( pOrigModel->pEyeball( 0 ), pOrigModel->numeyeballs * sizeof( mstudioeyeball_t ) );
  1051. }
  1052. for ( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ )
  1053. {
  1054. // mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
  1055. mstudiomesh_t *pNewMesh = pNewModel->pMesh( nMesh );
  1056. pNewMesh->numflexes = 0;
  1057. g_CombinerWriter.WriteOffset( pNewMesh->flexindex, pNewMesh );
  1058. for( int nSubModel = 0; nSubModel < m_pCombinedStudioData->m_nNumModels; nSubModel++ )
  1059. {
  1060. if ( !m_pMasterModels[ nSubModel ][ nModel ] )
  1061. {
  1062. continue;
  1063. }
  1064. mstudiomesh_t *pOrigFlexMesh = m_pMasterModels[ nSubModel ][ nModel ]->pMesh( nMesh );
  1065. if ( pOrigFlexMesh->numflexes )
  1066. { // figure out the total number of flex entries for this mesh and reserve space
  1067. pNewMesh->numflexes += pOrigFlexMesh->numflexes;
  1068. g_CombinerWriter.WriteBuffer( pOrigFlexMesh->pFlex( 0 ), pOrigFlexMesh->numflexes * sizeof( mstudioflex_t ) );
  1069. }
  1070. }
  1071. int nTotalFlexes = 0;
  1072. if ( pNewMesh->numflexes > 0 )
  1073. {
  1074. g_CombinerWriter.AlignWrite( 4 );
  1075. for( int nSubModel = 0; nSubModel < m_pCombinedStudioData->m_nNumModels; nSubModel++ )
  1076. {
  1077. if ( !m_pMasterModels[ nSubModel ][ nModel ] )
  1078. {
  1079. continue;
  1080. }
  1081. mstudiomesh_t *pOrigFlexMesh = m_pMasterModels[ nSubModel ][ nModel ]->pMesh( nMesh );
  1082. if ( pOrigFlexMesh->numflexes )
  1083. {
  1084. for( int nFlex = 0; nFlex < pOrigFlexMesh->numflexes; nFlex++, nTotalFlexes++ )
  1085. {
  1086. mstudioflex_t *pOrigFlex = pOrigFlexMesh->pFlex( nFlex );
  1087. mstudioflex_t *pNewFlex = pNewMesh->pFlex( nTotalFlexes );
  1088. bool bWrinkleVAnim = ( pOrigFlex->vertanimtype == STUDIO_VERT_ANIM_WRINKLE );
  1089. int nVAnimDeltaSize = bWrinkleVAnim ? sizeof( mstudiovertanim_wrinkle_t ) : sizeof( mstudiovertanim_t );
  1090. g_CombinerWriter.WriteOffset( pNewFlex->vertindex, pNewFlex );
  1091. g_CombinerWriter.WriteBuffer( pOrigFlex->pBaseVertanim(), pOrigFlex->numverts * nVAnimDeltaSize );
  1092. if ( bWrinkleVAnim )
  1093. {
  1094. Assert( 0 );
  1095. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has anim wrinkle" );
  1096. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  1097. #if 0
  1098. for( int nVert = 0; nVert < pOrigFlex->numverts; nVert++ )
  1099. {
  1100. mstudiovertanim_wrinkle_t *pNewVertAnimWrinkle = pNewFlex->pVertanimWrinkle( nVert );
  1101. }
  1102. #endif
  1103. }
  1104. else
  1105. {
  1106. float flDestAnimFixPointScale = m_pCombinedStudioHdr->VertAnimFixedPointScale();
  1107. float flSourceAnimFixPointScale = m_pStudioHdr[ nSubModel ]->VertAnimFixedPointScale();
  1108. Vector vUpdate;
  1109. for( int nVert = 0; nVert < pOrigFlex->numverts; nVert++ )
  1110. {
  1111. mstudiovertanim_t *pOrigVertAnim = pOrigFlex->pVertanim( nVert );
  1112. mstudiovertanim_t *pNewVertAnim = pNewFlex->pVertanim( nVert );
  1113. int nVertIndex = pOrigVertAnim->index;
  1114. nVertIndex += pOrigFlexMesh->vertexoffset;
  1115. vUpdate = pNewVertAnim->GetDeltaFloat();
  1116. vUpdate /= flSourceAnimFixPointScale;
  1117. vUpdate *= flDestAnimFixPointScale;
  1118. pNewVertAnim->SetDeltaFloat( vUpdate );
  1119. vUpdate = pNewVertAnim->GetNDeltaFloat();
  1120. vUpdate /= flSourceAnimFixPointScale;
  1121. vUpdate *= flDestAnimFixPointScale;
  1122. pNewVertAnim->SetNDeltaFloat( vUpdate );
  1123. pNewVertAnim->index = m_nVertexRemap[ nSubModel ][ nVertIndex ] - pNewMesh->vertexoffset;
  1124. }
  1125. }
  1126. }
  1127. }
  1128. }
  1129. }
  1130. }
  1131. }
  1132. void CModelCombine::CombineMDL_Model( )
  1133. {
  1134. studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
  1135. studiohdr_t *pFlexStudioHdr = NULL;
  1136. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  1137. {
  1138. studiohdr_t *pStudioHdr = m_pStudioHdr[ nModel ];
  1139. if ( pStudioHdr->numflexdesc )
  1140. {
  1141. #if 0
  1142. if ( pFlexStudioHdr )
  1143. {
  1144. Assert( 0 );
  1145. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model %s and %s both have flex specifications", pFlexStudioHdr->pszName(), pStudioHdr->pszName() );
  1146. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  1147. }
  1148. #endif
  1149. pFlexStudioHdr = pStudioHdr;
  1150. m_nFlexModelSource = nModel;
  1151. break;
  1152. }
  1153. }
  1154. if ( !pFlexStudioHdr )
  1155. {
  1156. pFlexStudioHdr = pPrimaryStudioHdr;
  1157. m_nFlexModelSource = 0;
  1158. }
  1159. VerifyField( offsetof( studiohdr_t, bodypartindex ), "Body Part Sequence" );
  1160. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->bodypartindex );
  1161. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pBodypart( 0 ), pPrimaryStudioHdr->numbodyparts * sizeof( mstudiobodyparts_t ) );
  1162. m_pCombinedModels = ( mstudiomodel_t * )g_CombinerWriter.GetWritePos();
  1163. int nTotalModels = 0;
  1164. for ( int nBodyPart = 0; nBodyPart < pPrimaryStudioHdr->numbodyparts; nBodyPart++ )
  1165. {
  1166. mstudiobodyparts_t *pOrigBodyPart = pPrimaryStudioHdr->pBodypart( nBodyPart );
  1167. mstudiobodyparts_t *pNewBodyPart = m_pCombinedStudioHdr->pBodypart( nBodyPart );
  1168. AddToStringTable( pNewBodyPart, &pNewBodyPart->sznameindex, pOrigBodyPart->pszName() );
  1169. g_CombinerWriter.WriteOffset( pNewBodyPart->modelindex, pNewBodyPart );
  1170. for( int nModel = 0; nModel < pOrigBodyPart->nummodels; nModel++ )
  1171. { // build our list of master models
  1172. m_pMasterModels[ 0 ][ nTotalModels ] = m_pMasterFlexModels[ nTotalModels ] = pOrigBodyPart->pModel( nModel );
  1173. g_CombinerWriter.WriteBuffer( m_pMasterModels[ 0 ][ nTotalModels ], sizeof( mstudiomodel_t ) );
  1174. for( int nSubModels = 1; nSubModels < m_pCombinedStudioData->m_nNumModels; nSubModels++ )
  1175. {
  1176. m_pMasterModels[ nSubModels ][ nTotalModels ] = NULL;
  1177. studiohdr_t *pSubStudioHdr = m_pStudioHdr[ nSubModels ];
  1178. if ( nBodyPart < pSubStudioHdr->numbodyparts )
  1179. {
  1180. mstudiobodyparts_t *pSubBodyPart = pSubStudioHdr->pBodypart( nBodyPart );
  1181. if ( nModel < pSubBodyPart->nummodels )
  1182. { // if a sub model matches up to the body part
  1183. m_pCombinedModels[ nTotalModels ].numvertices += pSubBodyPart->pModel( nModel )->numvertices;
  1184. if ( nSubModels == m_nFlexModelSource )
  1185. { // if it is part of the primary flex specification
  1186. m_pMasterFlexModels[ nTotalModels ] = pSubBodyPart->pModel( nModel );
  1187. }
  1188. m_pMasterModels[ nSubModels ][ nTotalModels ] = pSubBodyPart->pModel( nModel );
  1189. }
  1190. }
  1191. }
  1192. nTotalModels++;
  1193. }
  1194. }
  1195. g_CombinerWriter.AlignWrite( 4 );
  1196. m_pCombinedStudioHdr->numflexdesc = pFlexStudioHdr->numflexdesc;
  1197. m_pCombinedStudioHdr->numflexcontrollers = pFlexStudioHdr->numflexcontrollers;
  1198. m_pCombinedStudioHdr->numflexrules = pFlexStudioHdr->numflexrules;
  1199. m_pCombinedStudioHdr->numflexcontrollerui = pFlexStudioHdr->numflexcontrollerui;
  1200. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexdescindex );
  1201. VerifyField( offsetof( studiohdr_t, flexdescindex ), "Flex Desc" );
  1202. if ( pFlexStudioHdr->numflexdesc > 0 )
  1203. {
  1204. g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexdesc( 0 ), pFlexStudioHdr->numflexdesc * sizeof( mstudioflexdesc_t ) );
  1205. }
  1206. g_CombinerWriter.AlignWrite( 4 );
  1207. for ( int nFlexDesc = 0; nFlexDesc < pFlexStudioHdr->numflexdesc; nFlexDesc++ )
  1208. {
  1209. mstudioflexdesc_t *pOrigFlexDesc = pFlexStudioHdr->pFlexdesc( nFlexDesc );
  1210. mstudioflexdesc_t *pNewFlexDesc = m_pCombinedStudioHdr->pFlexdesc( nFlexDesc );
  1211. AddToStringTable( pNewFlexDesc, &pNewFlexDesc->szFACSindex, pOrigFlexDesc->pszFACS() );
  1212. }
  1213. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexcontrollerindex );
  1214. VerifyField( offsetof( studiohdr_t, flexdescindex ), "Flex Controller" );
  1215. if ( pFlexStudioHdr->numflexcontrollers > 0 )
  1216. {
  1217. g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexcontroller( ( LocalFlexController_t )0 ), pFlexStudioHdr->numflexcontrollers * sizeof( mstudioflexcontroller_t ) );
  1218. for( int nFlexController = 0; nFlexController < pFlexStudioHdr->numflexcontrollers; nFlexController++ )
  1219. {
  1220. mstudioflexcontroller_t *pOrigFlexController = pFlexStudioHdr->pFlexcontroller( ( LocalFlexController_t )nFlexController );
  1221. mstudioflexcontroller_t *pNewFlexController = m_pCombinedStudioHdr->pFlexcontroller( ( LocalFlexController_t )nFlexController );
  1222. AddToStringTable( pNewFlexController, &pNewFlexController->sznameindex, pOrigFlexController->pszName() );
  1223. AddToStringTable( pNewFlexController, &pNewFlexController->sztypeindex, pOrigFlexController->pszType() );
  1224. }
  1225. }
  1226. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexruleindex );
  1227. VerifyField( offsetof( studiohdr_t, flexruleindex ), "Flex Rules" );
  1228. if ( pFlexStudioHdr->numflexrules > 0 )
  1229. {
  1230. g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexRule( 0 ), pFlexStudioHdr->numflexrules * sizeof( mstudioflexrule_t ) );
  1231. g_CombinerWriter.AlignWrite( 4 );
  1232. for( int nFlexRule = 0; nFlexRule < pFlexStudioHdr->numflexrules; nFlexRule++ )
  1233. {
  1234. mstudioflexrule_t *pOrigFlexRule = pFlexStudioHdr->pFlexRule( nFlexRule );
  1235. mstudioflexrule_t *pNewFlexRule = m_pCombinedStudioHdr->pFlexRule( nFlexRule );
  1236. g_CombinerWriter.WriteOffset( pNewFlexRule->opindex, pNewFlexRule );
  1237. g_CombinerWriter.WriteBuffer( pOrigFlexRule->iFlexOp( 0 ), pOrigFlexRule->numops * sizeof( mstudioflexop_t ) );
  1238. g_CombinerWriter.AlignWrite( 4 );
  1239. }
  1240. }
  1241. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->flexcontrolleruiindex );
  1242. VerifyField( offsetof( studiohdr_t, flexcontrolleruiindex ), "Flex Controller UI" );
  1243. if ( pFlexStudioHdr->numflexcontrollerui > 0 )
  1244. {
  1245. g_CombinerWriter.WriteBuffer( pFlexStudioHdr->pFlexControllerUI( 0 ), pFlexStudioHdr->numflexcontrollerui * sizeof( mstudioflexcontrollerui_t ) );
  1246. for( int nFlexControllerUI = 0; nFlexControllerUI < pFlexStudioHdr->numflexcontrollerui; nFlexControllerUI++ )
  1247. {
  1248. mstudioflexcontrollerui_t *pOrigFlexControllerUI = pFlexStudioHdr->pFlexControllerUI( nFlexControllerUI );
  1249. mstudioflexcontrollerui_t *pNewFlexControllerUI = m_pCombinedStudioHdr->pFlexControllerUI( nFlexControllerUI );
  1250. AddToStringTable( pNewFlexControllerUI, &pNewFlexControllerUI->sznameindex, pOrigFlexControllerUI->pszName() );
  1251. // not worrying about remapping the controller indexes here yet
  1252. }
  1253. g_CombinerWriter.AlignWrite( 4 );
  1254. }
  1255. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->ikchainindex );
  1256. VerifyField( offsetof( studiohdr_t, ikchainindex ), "IK Chain" );
  1257. if ( pPrimaryStudioHdr->numikchains > 0 )
  1258. {
  1259. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pIKChain( 0 ), pPrimaryStudioHdr->numikchains * sizeof( mstudioikchain_t ) );
  1260. g_CombinerWriter.AlignWrite( 4 );
  1261. for( int nIKChain = 0; nIKChain < pPrimaryStudioHdr->numikchains; nIKChain++ )
  1262. {
  1263. mstudioikchain_t *pOrigIKChain = pPrimaryStudioHdr->pIKChain( nIKChain );
  1264. mstudioikchain_t *pNewIKChain = m_pCombinedStudioHdr->pIKChain( nIKChain );
  1265. AddToStringTable( pNewIKChain, &pNewIKChain->sznameindex, pNewIKChain->pszName() );
  1266. g_CombinerWriter.WriteOffset( pNewIKChain->linkindex, pNewIKChain );
  1267. g_CombinerWriter.WriteBuffer( pOrigIKChain->pLink( 0 ), pOrigIKChain->numlinks * sizeof( mstudioiklink_t ) );
  1268. }
  1269. }
  1270. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localikautoplaylockindex );
  1271. VerifyField( offsetof( studiohdr_t, localikautoplaylockindex ), "IK Autoplay Lock" );
  1272. if ( pPrimaryStudioHdr->numlocalikautoplaylocks > 0 )
  1273. {
  1274. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pLocalIKAutoplayLock( 0 ), pPrimaryStudioHdr->numlocalikautoplaylocks * sizeof( mstudioiklock_t ) );
  1275. g_CombinerWriter.AlignWrite( 4 );
  1276. }
  1277. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->mouthindex );
  1278. VerifyField( offsetof( studiohdr_t, mouthindex ), "Mouth" );
  1279. if ( pPrimaryStudioHdr->nummouths )
  1280. {
  1281. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pMouth( 0 ), pPrimaryStudioHdr->nummouths * sizeof( mstudiomouth_t ) );
  1282. g_CombinerWriter.AlignWrite( 4 );
  1283. }
  1284. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->localposeparamindex );
  1285. VerifyField( offsetof( studiohdr_t, localposeparamindex ), "Pose Param" );
  1286. if ( pPrimaryStudioHdr->numlocalposeparameters > 0 )
  1287. {
  1288. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pLocalPoseParameter( 0 ), pPrimaryStudioHdr->numlocalposeparameters * sizeof( mstudioposeparamdesc_t ) );
  1289. g_CombinerWriter.AlignWrite( 4 );
  1290. for( int nPoseParam = 0; nPoseParam < pPrimaryStudioHdr->numlocalposeparameters; nPoseParam++ )
  1291. {
  1292. mstudioposeparamdesc_t *pOrigPoseParam = pPrimaryStudioHdr->pLocalPoseParameter( nPoseParam );
  1293. mstudioposeparamdesc_t *pNewPoseParam = m_pCombinedStudioHdr->pLocalPoseParameter( nPoseParam );
  1294. AddToStringTable( pNewPoseParam, &pNewPoseParam->sznameindex, pOrigPoseParam->pszName() );
  1295. }
  1296. }
  1297. for( int nModel = 0; nModel < nTotalModels; nModel++ )
  1298. {
  1299. WriteModel( nModel );
  1300. }
  1301. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->includemodelindex );
  1302. VerifyField( offsetof( studiohdr_t, includemodelindex ), "Model Groups" );
  1303. if ( pPrimaryStudioHdr->numincludemodels > 0 )
  1304. {
  1305. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pModelGroup( 0 ), pPrimaryStudioHdr->numincludemodels * sizeof( mstudiomodelgroup_t ) );
  1306. for( int nModelGroup = 0; nModelGroup < pPrimaryStudioHdr->numincludemodels; nModelGroup++ )
  1307. {
  1308. mstudiomodelgroup_t *pOrigModelGroup = pPrimaryStudioHdr->pModelGroup( nModelGroup );
  1309. mstudiomodelgroup_t *pNewModelGroup = m_pCombinedStudioHdr->pModelGroup( nModelGroup );
  1310. AddToStringTable( pNewModelGroup, &pNewModelGroup->sznameindex, pOrigModelGroup->pszName() );
  1311. }
  1312. }
  1313. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->animblockindex );
  1314. VerifyField( offsetof( studiohdr_t, animblockindex ), "Anim Blocks" );
  1315. if ( pPrimaryStudioHdr->numanimblocks > 0 )
  1316. {
  1317. g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr->pAnimBlock( 0 ), pPrimaryStudioHdr->numanimblocks * sizeof( mstudioanimblock_t ) );
  1318. Assert( 0 );
  1319. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model has anim blocks: %s", pPrimaryStudioHdr->pszName() );
  1320. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  1321. }
  1322. g_CombinerWriter.AlignWrite( 4 );
  1323. AddToStringTable( m_pCombinedStudioHdr, &m_pCombinedStudioHdr->szanimblocknameindex, pPrimaryStudioHdr->pszAnimBlockName() );
  1324. }
  1325. void CModelCombine::CombineMDL_Textures( )
  1326. {
  1327. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->textureindex );
  1328. m_pCombinedStudioHdr->numtextures = m_pCombinedStudioData->m_nNumAtlasGroups + m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames;
  1329. // first allocate/write out "blank" texture entries (materials)
  1330. mstudiotexture_t NewTexture;
  1331. memset( &NewTexture, 0, sizeof( NewTexture ) );
  1332. NewTexture.sznameindex = 0;
  1333. NewTexture.used = 1;
  1334. for ( int nAtlasGroup = 0; nAtlasGroup < m_pCombinedStudioHdr->numtextures; nAtlasGroup++ )
  1335. {
  1336. g_CombinerWriter.WriteBuffer( &NewTexture, sizeof( mstudiotexture_t ) );
  1337. }
  1338. g_CombinerWriter.AlignWrite( 4 );
  1339. // then fix up those written entries with proper name index values from the string table
  1340. for ( int nAtlasGroup = 0; nAtlasGroup < m_pCombinedStudioData->m_nNumAtlasGroups; nAtlasGroup++ )
  1341. {
  1342. mstudiotexture_t *pNewTexture = m_pCombinedStudioHdr->pTexture( nAtlasGroup );
  1343. // we use a specialized name here that other systems won't understand
  1344. V_sprintf_safe( m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_szCombinedMaterialName, "!%s|%d|%hu|%d!", m_pCombinedStudioData->m_szCombinedModelName, nAtlasGroup, m_pCombinedStudioData->m_FinalHandle, GetNextAssetID() );
  1345. AddToStringTable( pNewTexture, &pNewTexture->sznameindex, m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_szCombinedMaterialName );
  1346. }
  1347. for ( int nNonAtlasedMaterial = 0; nNonAtlasedMaterial < m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames; nNonAtlasedMaterial++ )
  1348. {
  1349. int nMaterial = m_pCombinedStudioData->m_nNumAtlasGroups + nNonAtlasedMaterial;
  1350. mstudiotexture_t *pNewTexture = m_pCombinedStudioHdr->pTexture( nMaterial );
  1351. AddToStringTable( pNewTexture, &pNewTexture->sznameindex, m_pCombinedStudioData->m_szNonAtlasedMaterialBaseName[ nNonAtlasedMaterial ] );
  1352. }
  1353. // write out material paths (used for non atlased materials)
  1354. int *pNewCDTexture = ( int * )g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->cdtextureindex );
  1355. m_pCombinedStudioHdr->numcdtextures = m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths;
  1356. g_CombinerWriter.AllocWrite( m_pCombinedStudioHdr->numcdtextures * sizeof( int ) );
  1357. g_CombinerWriter.AlignWrite( 4 );
  1358. for ( int nMaterialPaths = 0; nMaterialPaths < m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths; nMaterialPaths++ )
  1359. {
  1360. AddToStringTable( m_pCombinedStudioHdr, &pNewCDTexture[ nMaterialPaths ], m_pCombinedStudioData->m_szNonAtlasedMaterialPaths[ nMaterialPaths ] );
  1361. }
  1362. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->skinindex );
  1363. m_pCombinedStudioHdr->numskinref = m_pCombinedStudioHdr->numtextures;
  1364. m_pCombinedStudioHdr->numskinfamilies = 1;
  1365. for ( short nTexture = 0; nTexture < static_cast< short >( m_pCombinedStudioHdr->numtextures ); nTexture++ )
  1366. {
  1367. g_CombinerWriter.WriteBuffer( &nTexture, sizeof( short ) );
  1368. }
  1369. g_CombinerWriter.AlignWrite( 4 );
  1370. }
  1371. void CModelCombine::CombineMDL_KeyValues( )
  1372. {
  1373. studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
  1374. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr->keyvalueindex );
  1375. VerifyField( offsetof( studiohdr_t, keyvalueindex ), "KeyValues" );
  1376. if ( pStudioHdr->keyvaluesize > 0 )
  1377. {
  1378. g_CombinerWriter.WriteBuffer( pStudioHdr->KeyValueText(), pStudioHdr->keyvaluesize );
  1379. }
  1380. g_CombinerWriter.AlignWrite( 4 );
  1381. }
  1382. #define WRITE_BONE_BLOCK( type, srcfield, dest, destindex ) \
  1383. g_CombinerWriter.WriteOffset( pNewLinearBone->destindex, pNewLinearBone ); \
  1384. type *dest = (type *)g_CombinerWriter.AllocWrite( m_nNumMasterBones * sizeof( type ) ); \
  1385. g_CombinerWriter.AlignWrite( 4 ); \
  1386. for ( int i = 0; i < m_nNumMasterBones; i++) \
  1387. { \
  1388. dest[ i ] = m_pMasterBoneList[ i ]->srcfield; \
  1389. }
  1390. void CModelCombine::CombineMDL_BoneTransforms( )
  1391. {
  1392. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr2->srcbonetransformindex );
  1393. VerifyField2( offsetof( studiohdr2_t, srcbonetransformindex ), "Bone Transforms" );
  1394. // need to merge bone transforms from the sub models?
  1395. if ( m_pStudioHdr2[ 0 ]->numsrcbonetransform > 0 )
  1396. {
  1397. g_CombinerWriter.WriteBuffer( m_pStudioHdr[ 0 ]->SrcBoneTransform( 0 ), m_pStudioHdr2[ 0 ]->numsrcbonetransform * sizeof( mstudiosrcbonetransform_t ) );
  1398. for( int nBoneTransform = 0; nBoneTransform < m_pStudioHdr2[ 0 ]->numsrcbonetransform; nBoneTransform++ )
  1399. {
  1400. mstudiosrcbonetransform_t *pOrigBoneTransform = ( mstudiosrcbonetransform_t * )m_pStudioHdr[ 0 ]->SrcBoneTransform( nBoneTransform );
  1401. mstudiosrcbonetransform_t *pNewBoneTransform = ( mstudiosrcbonetransform_t * )m_pCombinedStudioHdr->SrcBoneTransform( nBoneTransform );
  1402. AddToStringTable( pNewBoneTransform, &pNewBoneTransform->sznameindex, pOrigBoneTransform->pszName() );
  1403. }
  1404. }
  1405. g_CombinerWriter.AlignWrite( 4 );
  1406. if ( m_pStudioHdr2[ 0 ]->pLinearBones() != NULL )
  1407. {
  1408. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr2->linearboneindex, m_pCombinedStudioHdr2 );
  1409. VerifyField2( offsetof( studiohdr2_t, linearboneindex ), "Linear Bone Index" );
  1410. mstudiolinearbone_t *pNewLinearBone = ( mstudiolinearbone_t * )g_CombinerWriter.WriteBuffer( m_pStudioHdr2[ 0 ]->pLinearBones(), sizeof( mstudiolinearbone_t ) );
  1411. pNewLinearBone->numbones = m_nNumMasterBones;
  1412. WRITE_BONE_BLOCK( int, flags, pFlags, flagsindex );
  1413. WRITE_BONE_BLOCK( int, parent, pParent, parentindex );
  1414. WRITE_BONE_BLOCK( Vector, pos, pPos, posindex );
  1415. WRITE_BONE_BLOCK( Quaternion, quat, pQuat, quatindex );
  1416. WRITE_BONE_BLOCK( RadianEuler, rot, pRot, rotindex );
  1417. WRITE_BONE_BLOCK( matrix3x4_t, poseToBone, pPoseToBone, posetoboneindex );
  1418. WRITE_BONE_BLOCK( Vector, posscale, pPoseScale, posscaleindex );
  1419. WRITE_BONE_BLOCK( Vector, rotscale, pRotScale, rotscaleindex );
  1420. WRITE_BONE_BLOCK( Quaternion, qAlignment, pQAlignment, qalignmentindex );
  1421. for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ )
  1422. {
  1423. mstudiobone_t *pNewBone = (mstudiobone_t *)m_pCombinedStudioHdr->pBone( nBone );
  1424. *pNewLinearBone->pflags( nBone ) = pNewBone->flags;
  1425. }
  1426. }
  1427. }
  1428. void CModelCombine::CombineMDL_BoneFlexDrivers( )
  1429. {
  1430. m_pCombinedStudioHdr2->m_nBoneFlexDriverCount = m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount;
  1431. if ( m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount > 0 )
  1432. {
  1433. g_CombinerWriter.WriteOffset( m_pCombinedStudioHdr2->m_nBoneFlexDriverIndex, m_pCombinedStudioHdr2 );
  1434. VerifyField2( offsetof( studiohdr2_t, m_nBoneFlexDriverIndex ), "Bone Flex Drivers" );
  1435. g_CombinerWriter.WriteBuffer( m_pStudioHdr2[ m_nFlexModelSource ]->pBoneFlexDriver( 0 ), m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount * sizeof( mstudioboneflexdriver_t ) );
  1436. g_CombinerWriter.AlignWrite( 4 );
  1437. for( int nBoneFlexDriver = 0; nBoneFlexDriver < m_pStudioHdr2[ m_nFlexModelSource ]->m_nBoneFlexDriverCount; nBoneFlexDriver++ )
  1438. {
  1439. mstudioboneflexdriver_t *pOrigBoneFlexDriver = m_pStudioHdr2[ m_nFlexModelSource ]->pBoneFlexDriver( nBoneFlexDriver );
  1440. mstudioboneflexdriver_t *pNewBoneFlexDriver = m_pCombinedStudioHdr2->pBoneFlexDriver( nBoneFlexDriver );
  1441. g_CombinerWriter.WriteOffset( pNewBoneFlexDriver->m_nControlIndex, pNewBoneFlexDriver );
  1442. g_CombinerWriter.WriteBuffer( pOrigBoneFlexDriver->pBoneFlexDriverControl( 0 ), pOrigBoneFlexDriver->m_nControlCount * sizeof( mstudioboneflexdrivercontrol_t ) );
  1443. /* for( int nBoneFlexDriverControl = 0; nBoneFlexDriverControl < pOrigBoneFlexDriver->m_nControlCount; nBoneFlexDriverControl++ )
  1444. {
  1445. mstudioboneflexdrivercontrol_t *pOrigBoneFlexDriverControl = pOrigBoneFlexDriver->pBoneFlexDriverControl( nBoneFlexDriverControl );
  1446. mstudioboneflexdrivercontrol_t *pNewBoneFlexDriverControl = pNewBoneFlexDriver->pBoneFlexDriverControl( nBoneFlexDriver );
  1447. }*/
  1448. g_CombinerWriter.AlignWrite( 4 );
  1449. }
  1450. }
  1451. }
  1452. void CModelCombine::CombineMDL_AssignMeshIDs( )
  1453. {
  1454. int i;
  1455. int j;
  1456. int m;
  1457. int numMeshes;
  1458. mstudiobodyparts_t *pStudioBodyPart;
  1459. mstudiomodel_t *pStudioModel;
  1460. mstudiomesh_t *pStudioMesh;
  1461. numMeshes = 0;
  1462. for (i=0; i<m_pCombinedStudioHdr->numbodyparts; i++)
  1463. {
  1464. pStudioBodyPart = m_pCombinedStudioHdr->pBodypart(i);
  1465. for (j=0; j<pStudioBodyPart->nummodels; j++)
  1466. {
  1467. pStudioModel = pStudioBodyPart->pModel(j);
  1468. for (m=0; m<pStudioModel->nummeshes; m++)
  1469. {
  1470. // get each mesh
  1471. pStudioMesh = pStudioModel->pMesh(m);
  1472. pStudioMesh->meshid = numMeshes + m;
  1473. }
  1474. numMeshes += pStudioModel->nummeshes;
  1475. }
  1476. }
  1477. }
  1478. void CModelCombine::CombineMDL( bool bNoStringTable )
  1479. {
  1480. g_CombinerWriter.AllocWrite( 256 );
  1481. g_CombinerWriter.AlignWrite( 16 );
  1482. g_CombinerWriter.InitWriteArea( WRITE_AREA_MDL, g_CombinerWriter.GetWritePos() );
  1483. g_CombinerWriter.SetWriteArea( WRITE_AREA_MDL );
  1484. m_nFlexModelSource = -1;
  1485. studiohdr_t *pPrimaryStudioHdr = m_pStudioHdr[ 0 ];
  1486. m_pCombinedStudioHdr = ( studiohdr_t * )g_CombinerWriter.WriteBuffer( pPrimaryStudioHdr, sizeof( *pPrimaryStudioHdr ) );
  1487. m_pCombinedStudioHdr2 = ( studiohdr2_t * )g_CombinerWriter.WriteBufferWithOffset( pPrimaryStudioHdr->pStudioHdr2(), sizeof( studiohdr2_t ), m_pCombinedStudioHdr->studiohdr2index );
  1488. m_pCombinedStudioHdr->flags |= STUDIOHDR_FLAGS_COMBINED;
  1489. BeginStringTable();
  1490. CombineMDL_PreintStrings();
  1491. AddToStringTable( m_pCombinedStudioHdr2, &m_pCombinedStudioHdr2->sznameindex, m_pStudioHdr2[ 0 ]->pszName() );
  1492. CombineMDL_Bones();
  1493. CombineMDL_Anims();
  1494. CombineMDL_SequenceInfo();
  1495. CombineMDL_Model();
  1496. CombineMDL_Textures();
  1497. CombineMDL_KeyValues();
  1498. CombineMDL_BoneTransforms();
  1499. CombineMDL_BoneFlexDrivers();
  1500. // int nStringOffset = m_pWritePos - m_pWriteArea;
  1501. // const char *pszOrigStrings = ( ( char * ) m_pStudioHdr[ 0 ] ) + nStringOffset;
  1502. // const char *pszNewStrings = ( char * )m_pWritePos;
  1503. if ( !bNoStringTable )
  1504. {
  1505. WriteStringTable();
  1506. }
  1507. int nTotal = g_CombinerWriter.GetWritePos() - g_CombinerWriter.GetWriteArea();
  1508. m_pCombinedStudioHdr->length = nTotal;
  1509. m_pCombinedStudioHdr->checksum = 0;
  1510. for ( int i = 0; i < nTotal; i += 4 )
  1511. {
  1512. // TODO: does this need something more than a simple shift left and add checksum?
  1513. m_pCombinedStudioHdr->checksum = ( m_pCombinedStudioHdr->checksum << 1 ) +
  1514. ( ( m_pCombinedStudioHdr->checksum & 0x8000000 ) ? 1 : 0 ) + *( ( long * )( g_CombinerWriter.GetWriteArea() + i ) );
  1515. }
  1516. m_pCombinedHardwareHeader->checkSum = m_pCombinedStudioHdr->checksum;
  1517. m_pCombinedVertex->checksum = m_pCombinedStudioHdr->checksum;
  1518. CombineMDL_AssignMeshIDs();
  1519. }
  1520. #ifdef DEBUG_COMBINE
  1521. void CModelCombine::TestCombineMDL( )
  1522. {
  1523. int nLength = m_pCombinedStudioHdr->length;
  1524. char *pOrig = ( char * )m_pStudioHdr[ 0 ];
  1525. char *pNew = ( char * )m_pCombinedStudioHdr;
  1526. char *pStart = pNew;
  1527. if ( nLength != m_pStudioHdr[ 0 ]->length )
  1528. {
  1529. Msg( "Test Failure: Original Length: %d, New Length: %d\n", m_pStudioHdr[ 0 ]->length, nLength );
  1530. if ( nLength > m_pStudioHdr[ 0 ]->length )
  1531. {
  1532. nLength = m_pStudioHdr[ 0 ]->length;
  1533. }
  1534. }
  1535. int nOffset = sizeof( studiohdr_t ) + sizeof( studiohdr2_t );
  1536. nLength -= nOffset;
  1537. pOrig += nOffset;
  1538. pNew += nOffset;
  1539. char *pErrorPos = NULL;
  1540. for( ; nLength; pOrig++, pNew++, nLength-- )
  1541. {
  1542. if ( ( *pOrig ) != ( *pNew ) )
  1543. {
  1544. pErrorPos = pNew;
  1545. break;
  1546. }
  1547. }
  1548. if ( pErrorPos != NULL )
  1549. {
  1550. Msg( "Test Failure: Offset %d\n", pNew - pStart );
  1551. Init( m_pCombinedStudioData );
  1552. m_pErrorPos = pErrorPos;
  1553. // need to do VVD and VTX to recreate memory
  1554. CombineVVD();
  1555. CombineVTX();
  1556. CombineMDL( true );
  1557. }
  1558. }
  1559. #endif // DEBUG_COMBINE
  1560. void CModelCombine::CalcVTXInfo()
  1561. {
  1562. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  1563. {
  1564. OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet();
  1565. if ( pOrigHeader->numBodyParts > m_MaxHardwareData.m_nMaxBodyParts )
  1566. {
  1567. m_MaxHardwareData.m_nMaxBodyParts = pOrigHeader->numBodyParts;
  1568. }
  1569. for( int nBodyPart = 0; nBodyPart < pOrigHeader->numBodyParts; nBodyPart++ )
  1570. {
  1571. OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pOrigHeader->pBodyPart( nBodyPart );
  1572. int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
  1573. for( int nBodyModel = 0; nBodyModel < pOrigBodyPart->numModels; nBodyModel++ )
  1574. {
  1575. // skip over sub models that are not the selected one (-1 no selection, so no skipping)
  1576. if ( nSubModelToUse != -1 && nBodyModel != nSubModelToUse )
  1577. {
  1578. continue;
  1579. }
  1580. OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nBodyModel );
  1581. for ( int nLOD = 0; nLOD < pOrigModel->numLODs; nLOD++ )
  1582. {
  1583. OptimizedModel::ModelLODHeader_t *pOrigLOD = pOrigModel->pLOD( nLOD );
  1584. unsigned char nStripGroupFlags = OptimizedModel::STRIPGROUP_IS_HWSKINNED;
  1585. for (int i = 0; i < 2; i++ )
  1586. {
  1587. if ( i > 0 )
  1588. {
  1589. nStripGroupFlags |= OptimizedModel::STRIPGROUP_IS_DELTA_FLEXED;
  1590. }
  1591. for( int nMesh = 0; nMesh < pOrigLOD->numMeshes; nMesh++ )
  1592. {
  1593. OptimizedModel::MeshHeader_t *pOrigMesh = pOrigLOD->pMesh( nMesh );
  1594. bool bUsed = false;
  1595. for( int nStripGroup = 0; nStripGroup < pOrigMesh->numStripGroups; nStripGroup++ )
  1596. {
  1597. OptimizedModel::StripGroupHeader_t *pOrigStripGroup = pOrigMesh->pStripGroup( nStripGroup );
  1598. if ( pOrigStripGroup->flags == nStripGroupFlags )
  1599. { // we are compatible
  1600. for( int nStrip = 0; nStrip < pOrigStripGroup->numStrips; nStrip++ )
  1601. {
  1602. OptimizedModel::StripHeader_t *pOrigStrip = pOrigStripGroup->pStrip( nStrip );
  1603. if ( pOrigStrip->numTopologyIndices > 0 )
  1604. {
  1605. Assert( 0 );
  1606. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "strip has topology indices" );
  1607. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  1608. }
  1609. m_MaxHardwareData.m_nBoneStateChanges += pOrigStrip->numBoneStateChanges;
  1610. m_MaxHardwareData.m_nIndices += pOrigStrip->numIndices;
  1611. m_MaxHardwareData.m_nVerts += pOrigStrip->numVerts;
  1612. m_MaxHardwareData.m_nStrips++;
  1613. }
  1614. bUsed = true;
  1615. }
  1616. }
  1617. if ( bUsed )
  1618. { // we put things into this strip group
  1619. m_MaxHardwareData.m_nStripGroups++;
  1620. }
  1621. }
  1622. }
  1623. m_MaxHardwareData.m_nModelLODs++;
  1624. }
  1625. }
  1626. m_MaxHardwareData.m_nBodyParts++;
  1627. }
  1628. }
  1629. }
  1630. void CModelCombine::WriteStrip( OptimizedModel::StripGroupHeader_t *pNewStripGroup,
  1631. int nModel, OptimizedModel::StripGroupHeader_t *pOrigStripGroup, OptimizedModel::StripHeader_t *pOrigStrip )
  1632. {
  1633. OptimizedModel::Vertex_t *pNewVert = pNewStripGroup->pVertex( pNewStripGroup->numVerts );
  1634. // OptimizedModel::Vertex_t *pOrigVert = pOrigStripGroup->pVertex( pOrigStrip->vertOffset );
  1635. unsigned short *pNewIndex = pNewStripGroup->pIndex( pNewStripGroup->numIndices );
  1636. unsigned short *pOrigIndex = pOrigStripGroup->pIndex( pOrigStrip->indexOffset );
  1637. OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( pNewStripGroup->numStrips );
  1638. if ( pOrigStrip->numTopologyIndices > 0 )
  1639. {
  1640. Assert( 0 );
  1641. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "strip has topology indices" );
  1642. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  1643. }
  1644. Assert( m_CurrentHardwareData.m_nStrips < m_MaxHardwareData.m_nStrips );
  1645. pNewStrip->numIndices = pOrigStrip->numIndices;
  1646. pNewStrip->numTopologyIndices = pOrigStrip->numTopologyIndices;
  1647. pNewStrip->indexOffset = pNewStripGroup->numIndices;
  1648. pNewStrip->topologyOffset = pNewStripGroup->numTopologyIndices;
  1649. pNewStrip->numVerts = pOrigStrip->numVerts;
  1650. pNewStrip->vertOffset = pNewStripGroup->numVerts;
  1651. pNewStrip->numBoneStateChanges = pOrigStrip->numBoneStateChanges;
  1652. pNewStrip->numBones = pOrigStrip->numBones;
  1653. pNewStrip->flags = pOrigStrip->flags;
  1654. int boneFileOffset = m_HardwareOffsets.m_nBoneStateChanges + m_CurrentHardwareData.m_nBoneStateChanges * sizeof( OptimizedModel::BoneStateChangeHeader_t );
  1655. int stripFileOffset = m_HardwareOffsets.m_nStrips + m_CurrentHardwareData.m_nStrips * sizeof( OptimizedModel::StripHeader_t );
  1656. pNewStrip->boneStateChangeOffset = boneFileOffset - stripFileOffset;
  1657. int nOffset = pNewStripGroup->numVerts - pOrigStrip->vertOffset;
  1658. for( int nIndex = 0; nIndex < pOrigStrip->numIndices; nIndex++ )
  1659. {
  1660. *pNewIndex = ( *pOrigIndex ) + nOffset;
  1661. Assert( ( *pNewIndex ) >= pNewStrip->vertOffset );
  1662. Assert( ( *pNewIndex ) < pNewStrip->vertOffset + pNewStrip->numVerts );
  1663. pOrigIndex++;
  1664. pNewIndex++;
  1665. }
  1666. // OptimizedModel::Vertex_t *pTestVert = pOrigStripGroup->pVertex( 0 );
  1667. // OptimizedModel::FileHeader_t *pPrimaryHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ 0 ]->PeekGet();
  1668. // Msg( "%d / %d\n", ( char * )pNewVert - ( char * )m_pCombinedHardwareHeader, ( char * )pTestVert - ( char * )pPrimaryHeader );
  1669. for( int nVert = 0; nVert < pOrigStrip->numVerts; nVert++ )
  1670. {
  1671. OptimizedModel::Vertex_t *pOrigVert = pOrigStripGroup->pVertex( nVert + pOrigStrip->vertOffset );
  1672. *pNewVert = *pOrigVert;
  1673. pNewVert->origMeshVertID = m_nVertexRemap[ nModel ][ pOrigVert->origMeshVertID ];
  1674. pNewVert++;
  1675. }
  1676. // pTestVert = pOrigStripGroup->pVertex( pOrigStrip->numVerts );
  1677. // Msg( "%d / %d\n", ( char * )pNewVert - ( char * )m_pCombinedHardwareHeader, ( char * )pTestVert - ( char * )pPrimaryHeader );
  1678. for( int nBone = 0; nBone < pOrigStrip->numBoneStateChanges; nBone++ )
  1679. {
  1680. OptimizedModel::BoneStateChangeHeader_t *pNewBone = pNewStrip->pBoneStateChange( nBone );
  1681. OptimizedModel::BoneStateChangeHeader_t *pOrigBone = pOrigStrip->pBoneStateChange( nBone );
  1682. pNewBone->hardwareID = pOrigBone->hardwareID;
  1683. pNewBone->newBoneID = m_nBoneRemap[ nModel ][ pOrigBone->newBoneID ];
  1684. }
  1685. m_CurrentHardwareData.m_nBoneStateChanges += pNewStrip->numBoneStateChanges;
  1686. pNewStripGroup->numIndices += pOrigStrip->numIndices;
  1687. m_CurrentHardwareData.m_nIndices += pOrigStrip->numIndices;
  1688. pNewStripGroup->numVerts += pOrigStrip->numVerts;
  1689. m_CurrentHardwareData.m_nVerts += pOrigStrip->numVerts;
  1690. pNewStripGroup->numStrips++;
  1691. m_CurrentHardwareData.m_nStrips++;
  1692. }
  1693. void CModelCombine::MergeStripGroup( int nLOD, OptimizedModel::StripGroupHeader_t *pNewStripGroup, int nModel, OptimizedModel::StripGroupHeader_t *pOrigStripGroup )
  1694. {
  1695. // OptimizedModel::Vertex_t *pNewVert = pNewStripGroup->pVertex( pNewStripGroup->numVerts );
  1696. // unsigned short *pNewIndex = pNewStripGroup->pIndex( pNewStripGroup->numIndices );
  1697. // OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( pNewStripGroup->numStrips );
  1698. for( int nStrip = 0; nStrip < pOrigStripGroup->numStrips; nStrip++ )
  1699. {
  1700. OptimizedModel::StripHeader_t *pOrigStrip = pOrigStripGroup->pStrip( nStrip );
  1701. WriteStrip( pNewStripGroup, nModel, pOrigStripGroup, pOrigStrip );
  1702. m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD ] += pOrigStrip->numIndices;
  1703. }
  1704. m_pCombinedStudioData->m_Results.m_nBatches[ nModel ][ nLOD ] += pOrigStripGroup->numStrips;
  1705. m_pCombinedStudioData->m_Results.m_nCombinedBatches[ nLOD ] += pOrigStripGroup->numStrips;
  1706. }
  1707. void CModelCombine::WriteStripGroup( int nLOD, int nMaterialIndex, unsigned char nStripGroupFlags, OptimizedModel::MeshHeader_t *pNewMesh )
  1708. {
  1709. OptimizedModel::StripGroupHeader_t *pNewStripGroup = pNewMesh->pStripGroup( pNewMesh->numStripGroups );
  1710. pNewStripGroup->numVerts = 0;
  1711. pNewStripGroup->numIndices = 0;
  1712. pNewStripGroup->numTopologyIndices = 0;
  1713. pNewStripGroup->numStrips = 0;
  1714. pNewStripGroup->flags = nStripGroupFlags;
  1715. int stripGroupFileOffset = m_HardwareOffsets.m_nStripGroups + m_CurrentHardwareData.m_nStripGroups * sizeof( OptimizedModel::StripGroupHeader_t );
  1716. int vertsFileOffset = m_HardwareOffsets.m_nVerts + m_CurrentHardwareData.m_nVerts * sizeof( OptimizedModel::Vertex_t );
  1717. int indicesFileOffset = m_HardwareOffsets.m_nIndices + m_CurrentHardwareData.m_nIndices * sizeof( unsigned short );
  1718. int topologyFileOffset = m_HardwareOffsets.m_nTopology + m_CurrentHardwareData.m_nTopology * sizeof( unsigned short );
  1719. int stripsFileOffset = m_HardwareOffsets.m_nStrips + m_CurrentHardwareData.m_nStrips * sizeof( OptimizedModel::StripHeader_t );
  1720. pNewStripGroup->vertOffset = vertsFileOffset - stripGroupFileOffset;
  1721. pNewStripGroup->indexOffset = indicesFileOffset - stripGroupFileOffset;
  1722. pNewStripGroup->topologyOffset = topologyFileOffset - stripGroupFileOffset;
  1723. pNewStripGroup->stripOffset = stripsFileOffset - stripGroupFileOffset;
  1724. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  1725. {
  1726. OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet();
  1727. for( int nBodyPart = 0; nBodyPart < pOrigHeader->numBodyParts; nBodyPart++ )
  1728. {
  1729. OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pOrigHeader->pBodyPart( nBodyPart );
  1730. int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
  1731. for( int nBodyModel = 0; nBodyModel < pOrigBodyPart->numModels; nBodyModel++ )
  1732. {
  1733. // skip over sub models that are not the selected one (-1 no selection, so no skipping)
  1734. if ( nSubModelToUse != -1 && nBodyModel != nSubModelToUse )
  1735. {
  1736. continue;
  1737. }
  1738. OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nBodyModel );
  1739. int nPickLOD = nLOD;
  1740. if ( nLOD >= pOrigModel->numLODs )
  1741. {
  1742. nPickLOD = pOrigModel->numLODs - 1;
  1743. }
  1744. OptimizedModel::ModelLODHeader_t *pOrigLOD = pOrigModel->pLOD( nPickLOD );
  1745. for( int nMesh = 0; nMesh < pOrigLOD->numMeshes; nMesh++ )
  1746. {
  1747. OptimizedModel::MeshHeader_t *pOrigMesh = pOrigLOD->pMesh( nMesh );
  1748. // determine the new material index
  1749. int nOriginalMaterialIndex = m_pCombinedStudioData->m_MeshToMaterialMap[ nModel ][ nBodyPart ][ nMesh ];
  1750. int nNewMaterialIndex = GetTextureCombiner().GetAtlasGroupIndex( nOriginalMaterialIndex );
  1751. if ( nNewMaterialIndex == -1 )
  1752. {
  1753. nNewMaterialIndex = m_pCombinedStudioData->m_nNumAtlasGroups + GetTextureCombiner().GetAtlasGroupMaterialIndex( nOriginalMaterialIndex );
  1754. }
  1755. if ( nMaterialIndex == nNewMaterialIndex )
  1756. {
  1757. for( int nStripGroup = 0; nStripGroup < pOrigMesh->numStripGroups; nStripGroup++ )
  1758. {
  1759. OptimizedModel::StripGroupHeader_t *pOrigStripGroup = pOrigMesh->pStripGroup( nStripGroup );
  1760. if ( pOrigStripGroup->flags == pNewStripGroup->flags )
  1761. { // we are compatible
  1762. MergeStripGroup( nLOD, pNewStripGroup, nModel, pOrigStripGroup );
  1763. }
  1764. }
  1765. }
  1766. }
  1767. }
  1768. }
  1769. }
  1770. m_pCombinedStudioData->m_Results.m_nCombinedNumIndexes[ nLOD ] += pNewStripGroup->numIndices;
  1771. if ( pNewStripGroup->numIndices )
  1772. { // we put things into this strip group
  1773. pNewMesh->numStripGroups++;
  1774. m_CurrentHardwareData.m_nStripGroups++;
  1775. }
  1776. }
  1777. void CModelCombine::WriteMeshes( int nLOD, int nMaterialIndex, OptimizedModel::ModelLODHeader_t *pNewModelLOD, OptimizedModel::ModelLODHeader_t *pOrigModelLOD )
  1778. {
  1779. OptimizedModel::MeshHeader_t *pNewMesh = pNewModelLOD->pMesh( pNewModelLOD->numMeshes );
  1780. OptimizedModel::MeshHeader_t *pOrigMesh = pOrigModelLOD->pMesh( 0 );
  1781. int meshFileOffset = m_HardwareOffsets.m_nMeshes + m_CurrentHardwareData.m_nMeshes * sizeof( OptimizedModel::MeshHeader_t );
  1782. int stripGroupFileOffset = m_HardwareOffsets.m_nStripGroups + m_CurrentHardwareData.m_nStripGroups * sizeof( OptimizedModel::StripGroupHeader_t );
  1783. pNewMesh->stripGroupHeaderOffset = stripGroupFileOffset - meshFileOffset;
  1784. pNewMesh->flags = pOrigMesh->flags;
  1785. WriteStripGroup( nLOD, nMaterialIndex, OptimizedModel::STRIPGROUP_IS_HWSKINNED, pNewMesh );
  1786. WriteStripGroup( nLOD, nMaterialIndex, OptimizedModel::STRIPGROUP_IS_HWSKINNED | OptimizedModel::STRIPGROUP_IS_DELTA_FLEXED, pNewMesh );
  1787. pNewModelLOD->numMeshes++;
  1788. m_CurrentHardwareData.m_nMeshes++;
  1789. }
  1790. void CModelCombine::WriteModelLOD( int nLOD, OptimizedModel::ModelHeader_t *pNewModel, OptimizedModel::ModelHeader_t *pOrigModel )
  1791. {
  1792. OptimizedModel::ModelLODHeader_t *pNewModelLOD = pNewModel->pLOD( m_CurrentHardwareData.m_nModelLODs );
  1793. OptimizedModel::ModelLODHeader_t *pOrigModelLOD = pOrigModel->pLOD( nLOD );
  1794. int lodFileOffset = m_HardwareOffsets.m_nModelLODs + m_CurrentHardwareData.m_nModelLODs * sizeof( OptimizedModel::ModelLODHeader_t );
  1795. int meshFileOffset = m_HardwareOffsets.m_nMeshes + m_CurrentHardwareData.m_nMeshes * sizeof( OptimizedModel::MeshHeader_t );
  1796. pNewModelLOD->meshOffset = meshFileOffset - lodFileOffset;
  1797. int nTotalMaterials = m_pCombinedStudioData->m_nNumAtlasGroups + m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames;
  1798. for ( int nMaterialIndex = 0; nMaterialIndex < nTotalMaterials; nMaterialIndex++ )
  1799. {
  1800. WriteMeshes( nLOD, nMaterialIndex, pNewModelLOD, pOrigModelLOD );
  1801. }
  1802. m_CurrentHardwareData.m_nModelLODs++;
  1803. }
  1804. void CModelCombine::WriteModel( int nBodyModel, OptimizedModel::BodyPartHeader_t *pNewBodyPart, OptimizedModel::BodyPartHeader_t *pOrigBodyPart )
  1805. {
  1806. OptimizedModel::ModelHeader_t *pNewModel = pNewBodyPart->pModel( m_CurrentHardwareData.m_nModels );
  1807. OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nBodyModel );
  1808. pNewModel->numLODs = pOrigModel->numLODs;
  1809. int modelFileOffset = m_HardwareOffsets.m_nModels + m_CurrentHardwareData.m_nModels * sizeof( OptimizedModel::ModelHeader_t );
  1810. int lodFileOffset = m_HardwareOffsets.m_nModelLODs + m_CurrentHardwareData.m_nModelLODs * sizeof( OptimizedModel::ModelLODHeader_t );
  1811. pNewModel->lodOffset = lodFileOffset - modelFileOffset;
  1812. for( int nLOD = 0; nLOD < pOrigModel->numLODs; nLOD++ )
  1813. {
  1814. WriteModelLOD( nLOD, pNewModel, pOrigModel );
  1815. }
  1816. m_CurrentHardwareData.m_nModels++;
  1817. pNewBodyPart->numModels++;
  1818. }
  1819. void CModelCombine::WriteBodyPart( int nBodyPart )
  1820. {
  1821. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  1822. {
  1823. OptimizedModel::FileHeader_t *pOrigHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ nModel ]->PeekGet();
  1824. if ( pOrigHeader->numBodyParts > nBodyPart )
  1825. {
  1826. OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pOrigHeader->pBodyPart( nBodyPart );
  1827. OptimizedModel::BodyPartHeader_t *pNewBodyPart = m_pCombinedHardwareHeader->pBodyPart( nBodyPart );
  1828. pNewBodyPart->numModels = 0;
  1829. int bodyPartOffset = m_HardwareOffsets.m_nBodyParts + m_CurrentHardwareData.m_nBodyParts * sizeof( OptimizedModel::BodyPartHeader_t );
  1830. int modelFileOffset = m_HardwareOffsets.m_nModels + m_CurrentHardwareData.m_nModels * sizeof( OptimizedModel::ModelHeader_t );
  1831. pNewBodyPart->modelOffset = modelFileOffset - bodyPartOffset;
  1832. int nBodyModel = ( m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection != -1 ) ? m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection : 0;
  1833. WriteModel( nBodyModel, pNewBodyPart, pOrigBodyPart );
  1834. break;
  1835. }
  1836. }
  1837. m_CurrentHardwareData.m_nBodyParts++;
  1838. }
  1839. void CModelCombine::CombineVTX( )
  1840. {
  1841. int nMaxSize = 0;
  1842. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  1843. {
  1844. nMaxSize += VTX_Data[ nModel ]->TellMaxPut();
  1845. }
  1846. g_CombinerWriter.AllocWrite( 256 );
  1847. g_CombinerWriter.AlignWrite( 16 );
  1848. g_CombinerWriter.InitWriteArea( WRITE_AREA_VTX, g_CombinerWriter.GetWritePos() );
  1849. g_CombinerWriter.SetWriteArea( WRITE_AREA_VTX );
  1850. OptimizedModel::FileHeader_t *pPrimaryHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ 0 ]->PeekGet();
  1851. // m_pCombinedHardwareHeader = ( OptimizedModel::FileHeader_t * )g_CombinerWriter.WriteBuffer( pPrimaryHeader, VTX_Data[ 0 ]->TellMaxPut() );
  1852. // return;
  1853. m_pCombinedHardwareHeader = ( OptimizedModel::FileHeader_t * )g_CombinerWriter.AllocWrite( sizeof( *m_pCombinedHardwareHeader ) );
  1854. m_pCombinedHardwareHeader->version = pPrimaryHeader->version;
  1855. m_pCombinedHardwareHeader->vertCacheSize = pPrimaryHeader->vertCacheSize;
  1856. m_pCombinedHardwareHeader->maxBonesPerStrip = pPrimaryHeader->maxBonesPerStrip;
  1857. m_pCombinedHardwareHeader->maxBonesPerFace = pPrimaryHeader->maxBonesPerFace;
  1858. m_pCombinedHardwareHeader->maxBonesPerVert = pPrimaryHeader->maxBonesPerVert;
  1859. m_pCombinedHardwareHeader->numLODs = pPrimaryHeader->numLODs;
  1860. // figure out the worst case scenario - we'll have some blank spots in the file, but should be minimal
  1861. memset( &m_MaxHardwareData, 0, sizeof( m_MaxHardwareData ) );
  1862. memset( &m_CurrentHardwareData, 0, sizeof( m_CurrentHardwareData ) );
  1863. memset( &m_HardwareOffsets, 0, sizeof( m_HardwareOffsets ) );
  1864. CalcVTXInfo();
  1865. m_pCombinedHardwareHeader->numBodyParts = m_MaxHardwareData.m_nMaxBodyParts;
  1866. m_MaxHardwareData.m_nStripGroups++; // we need to reserve space for temp writing
  1867. m_HardwareOffsets.m_nBodyParts = sizeof( OptimizedModel::FileHeader_t );
  1868. m_HardwareOffsets.m_nModels = m_HardwareOffsets.m_nBodyParts + sizeof( OptimizedModel::BodyPartHeader_t ) * m_MaxHardwareData.m_nBodyParts;
  1869. m_HardwareOffsets.m_nModelLODs = m_HardwareOffsets.m_nModels + sizeof( OptimizedModel::ModelHeader_t ) * m_MaxHardwareData.m_nModels;
  1870. m_HardwareOffsets.m_nMeshes = m_HardwareOffsets.m_nModelLODs + sizeof( OptimizedModel::ModelLODHeader_t ) * m_MaxHardwareData.m_nModelLODs;
  1871. m_HardwareOffsets.m_nStripGroups = m_HardwareOffsets.m_nMeshes + sizeof( OptimizedModel::MeshHeader_t ) * m_MaxHardwareData.m_nMeshes;
  1872. m_HardwareOffsets.m_nStrips = m_HardwareOffsets.m_nStripGroups + sizeof( OptimizedModel::StripGroupHeader_t ) * m_MaxHardwareData.m_nStripGroups;
  1873. m_HardwareOffsets.m_nVerts = m_HardwareOffsets.m_nStrips + sizeof( OptimizedModel::StripHeader_t ) * m_MaxHardwareData.m_nStrips;
  1874. m_HardwareOffsets.m_nIndices = m_HardwareOffsets.m_nVerts + sizeof( OptimizedModel::Vertex_t ) * m_MaxHardwareData.m_nVerts;
  1875. m_HardwareOffsets.m_nBoneStateChanges = m_HardwareOffsets.m_nIndices + sizeof( unsigned short ) * m_MaxHardwareData.m_nIndices;
  1876. m_HardwareOffsets.m_nStringTable = m_HardwareOffsets.m_nBoneStateChanges + sizeof( OptimizedModel::BoneStateChangeHeader_t ) * m_MaxHardwareData.m_nBoneStateChanges;
  1877. m_HardwareOffsets.m_nMaterialReplacements = m_HardwareOffsets.m_nStringTable + 0;
  1878. g_CombinerWriter.AllocWrite( m_HardwareOffsets.m_nMaterialReplacements - m_HardwareOffsets.m_nModels );
  1879. m_pCombinedHardwareHeader->bodyPartOffset = m_HardwareOffsets.m_nBodyParts;
  1880. for( int nBodyPart = 0; nBodyPart < m_MaxHardwareData.m_nMaxBodyParts; nBodyPart++ )
  1881. {
  1882. WriteBodyPart( nBodyPart );
  1883. }
  1884. for( int nLOD = 1; nLOD < m_pCombinedVertex->numLODs; nLOD++ )
  1885. {
  1886. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  1887. {
  1888. if ( m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD ] > m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD - 1 ] )
  1889. {
  1890. Assert( 0 );
  1891. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "%s has lower LOD which has higher triangle count\n", m_pStudioHdr[ nModel ]->pszName() );
  1892. m_pCombinedStudioData->m_Results.m_nDetailedError = COMBINED_DETAIL_ERROR_MODEL_LOWER_LOD_HIGHER_TRI_COUNT;
  1893. 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,
  1894. nLOD, m_pCombinedStudioData->m_Results.m_nNumIndexes[ nModel ][ nLOD ] / 3 );
  1895. throw( COMBINE_RESULT_FLAG_FAILED_GOOD_PRACTICE );
  1896. }
  1897. }
  1898. }
  1899. m_pCombinedHardwareHeader->materialReplacementListOffset = m_HardwareOffsets.m_nMaterialReplacements;
  1900. g_CombinerWriter.AllocWrite( pPrimaryHeader->numLODs * sizeof( OptimizedModel::MaterialReplacementListHeader_t ) );
  1901. for( int nLOD = 0; nLOD < pPrimaryHeader->numLODs; nLOD++ )
  1902. {
  1903. OptimizedModel::MaterialReplacementListHeader_t *pNewMaterialReplacementList = m_pCombinedHardwareHeader->pMaterialReplacementList( nLOD );
  1904. OptimizedModel::MaterialReplacementListHeader_t *pOrigMaterialReplacementList = pPrimaryHeader->pMaterialReplacementList( nLOD );
  1905. pNewMaterialReplacementList->numReplacements = pOrigMaterialReplacementList->numReplacements;
  1906. g_CombinerWriter.WriteOffset( pNewMaterialReplacementList->replacementOffset, pNewMaterialReplacementList );
  1907. if ( pOrigMaterialReplacementList->numReplacements > 0 )
  1908. {
  1909. Assert( 0 );
  1910. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "material has replacements" );
  1911. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_FEATURE );
  1912. }
  1913. }
  1914. #ifdef TEST_VTX_COMBINE
  1915. TestCombineVTX();
  1916. #endif // TEST_VTX_COMBINE
  1917. // int nSize = Min( 48916, VTX_Data[ 0 ]->TellMaxPut() );
  1918. // memcpy( m_pCombinedHardwareHeader, VTX_Data[ 0 ]->PeekGet(), nSize );
  1919. // memcpy( ( char * )m_pCombinedHardwareHeader + 48916, ( char * )VTX_Data[ 0 ]->PeekGet() + 48916, 688 );
  1920. // g_CombinerWriter.AlignWrite( 128 );
  1921. // m_pCombinedHardwareHeader = ( OptimizedModel::FileHeader_t * )g_CombinerWriter.WriteBuffer( VTX_Data[ 0 ]->PeekGet(), VTX_Data[ 0 ]->TellMaxPut() );
  1922. }
  1923. #ifdef DEBUG_COMBINE
  1924. void CModelCombine::TestCombineVTX( )
  1925. {
  1926. OptimizedModel::FileHeader_t *pPrimaryHeader = ( OptimizedModel::FileHeader_t * )VTX_Data[ 0 ]->PeekGet();
  1927. Assert( pPrimaryHeader->version == m_pCombinedHardwareHeader->version );
  1928. Assert( pPrimaryHeader->vertCacheSize == m_pCombinedHardwareHeader->vertCacheSize );
  1929. Assert( pPrimaryHeader->maxBonesPerStrip == m_pCombinedHardwareHeader->maxBonesPerStrip );
  1930. Assert( pPrimaryHeader->maxBonesPerFace == m_pCombinedHardwareHeader->maxBonesPerFace );
  1931. Assert( pPrimaryHeader->maxBonesPerVert == m_pCombinedHardwareHeader->maxBonesPerVert );
  1932. Assert( pPrimaryHeader->numLODs == m_pCombinedHardwareHeader->numLODs );
  1933. Assert( pPrimaryHeader->numBodyParts == m_pCombinedHardwareHeader->numBodyParts );
  1934. for( int nBodyPart = 0; nBodyPart < pPrimaryHeader->numBodyParts; nBodyPart++ )
  1935. {
  1936. OptimizedModel::BodyPartHeader_t *pOrigBodyPart = pPrimaryHeader->pBodyPart( nBodyPart );
  1937. OptimizedModel::BodyPartHeader_t *pNewBodyPart = m_pCombinedHardwareHeader->pBodyPart( nBodyPart );
  1938. Assert( pOrigBodyPart->numModels == pNewBodyPart->numModels );
  1939. for( int nModel = 0; nModel < pOrigBodyPart->numModels; nModel++ )
  1940. {
  1941. OptimizedModel::ModelHeader_t *pOrigModel = pOrigBodyPart->pModel( nModel );
  1942. OptimizedModel::ModelHeader_t *pNewModel = pNewBodyPart->pModel( nModel );
  1943. Assert( pOrigModel->numLODs == pNewModel->numLODs );
  1944. for( int nLOD = 0; nLOD < pOrigModel->numLODs; nLOD++ )
  1945. {
  1946. OptimizedModel::ModelLODHeader_t *pOrigLOD = pOrigModel->pLOD( nLOD );
  1947. OptimizedModel::ModelLODHeader_t *pNewLOD = pNewModel->pLOD( nLOD );
  1948. Assert( pOrigLOD->numMeshes == pNewLOD->numMeshes );
  1949. Assert( pOrigLOD->switchPoint == pNewLOD->switchPoint );
  1950. for( int nMesh = 0; nMesh < pOrigLOD->numMeshes; nMesh++ )
  1951. {
  1952. OptimizedModel::MeshHeader_t *pOrigMesh = pOrigLOD->pMesh( nMesh );
  1953. OptimizedModel::MeshHeader_t *pNewMesh = pNewLOD->pMesh( nMesh );
  1954. Assert( pOrigMesh->numStripGroups == pNewMesh->numStripGroups );
  1955. Assert( pOrigMesh->flags == pNewMesh->flags );
  1956. for( int nStripGroup = 0; nStripGroup < pOrigMesh->numStripGroups; nStripGroup++ )
  1957. {
  1958. OptimizedModel::StripGroupHeader_t *pOrigStripGroup = pOrigMesh->pStripGroup( nStripGroup );
  1959. OptimizedModel::StripGroupHeader_t *pNewStripGroup = pNewMesh->pStripGroup( nStripGroup );
  1960. Assert( pOrigStripGroup->numVerts == pNewStripGroup->numVerts );
  1961. Assert( pOrigStripGroup->numIndices == pNewStripGroup->numIndices );
  1962. Assert( pOrigStripGroup->numStrips == pNewStripGroup->numStrips );
  1963. Assert( pOrigStripGroup->flags == pNewStripGroup->flags );
  1964. Assert( pOrigStripGroup->numTopologyIndices == pNewStripGroup->numTopologyIndices );
  1965. for( int nNumVerts = 0; nNumVerts < pOrigStripGroup->numVerts; nNumVerts++ )
  1966. {
  1967. OptimizedModel::Vertex_t *pOrigVertex = pOrigStripGroup->pVertex( nNumVerts );
  1968. OptimizedModel::Vertex_t *pNewVertex = pNewStripGroup->pVertex( nNumVerts );
  1969. Assert( memcmp( pOrigVertex, pNewVertex, sizeof( *pOrigVertex ) ) == 0 );
  1970. }
  1971. for( int nStrip = 0; nStrip < pOrigStripGroup->numStrips; nStrip++ )
  1972. {
  1973. OptimizedModel::StripHeader_t *pOrigStrip = pOrigStripGroup->pStrip( nStrip );
  1974. OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( nStrip );
  1975. Assert( pOrigStrip->numIndices == pNewStrip->numIndices );
  1976. Assert( pOrigStrip->numVerts == pNewStrip->numVerts );
  1977. Assert( pOrigStrip->numBones == pNewStrip->numBones );
  1978. Assert( pOrigStrip->flags == pNewStrip->flags );
  1979. Assert( pOrigStrip->numBoneStateChanges == pNewStrip->numBoneStateChanges );
  1980. Assert( pOrigStrip->numTopologyIndices == pNewStrip->numTopologyIndices );
  1981. for( int nBoneStateChange = 0; nBoneStateChange < pOrigStrip->numBoneStateChanges; nBoneStateChange++ )
  1982. {
  1983. OptimizedModel::BoneStateChangeHeader_t *pOrigBoneStateChange = pOrigStrip->pBoneStateChange( nBoneStateChange );
  1984. OptimizedModel::BoneStateChangeHeader_t *pNewBoneStateChange = pNewStrip->pBoneStateChange( nBoneStateChange );
  1985. Assert( pOrigBoneStateChange->hardwareID == pNewBoneStateChange->hardwareID );
  1986. Assert( pOrigBoneStateChange->newBoneID == pNewBoneStateChange->newBoneID );
  1987. }
  1988. }
  1989. }
  1990. }
  1991. }
  1992. }
  1993. }
  1994. }
  1995. #endif // DEBUG_COMBINE
  1996. // this will move a vert belonging to a secondary model from pose space of that secondary model
  1997. // into that secondary model's bone space, then transform the vert into pose space of the primary model.
  1998. void CModelCombine::CombineVVD_OffsetVerts( )
  1999. {
  2000. matrix3x4_t VertToPrimaryPose[ COMBINER_MAX_BONES ];
  2001. for( int nBone = 0; nBone < m_nNumMasterBones; nBone++ )
  2002. {
  2003. matrix3x4_t VertToPrimaryBone;
  2004. VertToPrimaryBone = m_pMasterBoneList[ nBone ]->poseToBone;
  2005. MatrixInvert( VertToPrimaryBone, VertToPrimaryPose[ nBone ] );
  2006. }
  2007. for( int nModel = 1; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  2008. {
  2009. int nNumVerts = m_pVertexFileHeader[ nModel ]->numLODVertexes[ 0 ];
  2010. for( int nVert = 0; nVert < nNumVerts; nVert++ )
  2011. {
  2012. int nRealVert = m_nVertexRemap[ nModel ][ nVert ];
  2013. mstudiovertex_t *pVert = ( mstudiovertex_t * )m_pCombinedVertex->GetVertexData() + nRealVert;
  2014. Vector vPosition, vPos, vFinalVert, vFinalNormal, vFinalTangent;
  2015. bool bHasTangent = m_pVertexFileHeader[ nModel ]->tangentDataStart != NULL;
  2016. vFinalVert.Init();
  2017. vFinalNormal.Init();
  2018. vFinalTangent.Init();
  2019. for( int nBone = 0; nBone < pVert->m_BoneWeights.numbones; nBone++ )
  2020. {
  2021. int nBoneIndex = pVert->m_BoneWeights.bone[ nBone ];
  2022. VectorTransform( pVert->m_vecPosition, m_pStudioHdr[ nModel ]->pBone( m_nMasterToLocalBoneRemap[ nModel ][ nBoneIndex ] )->poseToBone, vPosition );
  2023. VectorTransform( vPosition, VertToPrimaryPose[ nBoneIndex ], vPos );
  2024. vFinalVert += vPos * pVert->m_BoneWeights.weight[ nBone ];
  2025. VectorRotate( pVert->m_vecNormal, m_pStudioHdr[ nModel ]->pBone( m_nMasterToLocalBoneRemap[ nModel ][ nBoneIndex ] )->poseToBone, vPosition );
  2026. VectorRotate( vPosition, VertToPrimaryPose[ nBoneIndex ], vPos );
  2027. vFinalNormal += vPos * pVert->m_BoneWeights.weight[ nBone ];
  2028. if ( bHasTangent )
  2029. {
  2030. Vector4D vTangent = *( m_pCombinedVertex->GetTangentData() + nRealVert );
  2031. VectorRotate( vTangent.AsVector3D(), m_pStudioHdr[ nModel ]->pBone( m_nMasterToLocalBoneRemap[ nModel ][ nBoneIndex ] )->poseToBone, vPosition );
  2032. VectorRotate( vPosition, VertToPrimaryPose[ nBoneIndex ], vPos );
  2033. vFinalTangent += vPos * pVert->m_BoneWeights.weight[ nBone ];
  2034. }
  2035. }
  2036. pVert->m_vecPosition = vFinalVert;
  2037. pVert->m_vecNormal = vFinalNormal;
  2038. if ( bHasTangent )
  2039. {
  2040. Vector *pTangent = ( Vector * )( m_pCombinedVertex->GetTangentData() + nRealVert );
  2041. *pTangent = vFinalTangent;
  2042. }
  2043. }
  2044. }
  2045. }
  2046. void CModelCombine::CombineVVD( )
  2047. {
  2048. int nVertsWritten[ COMBINER_MAX_MODELS ];
  2049. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  2050. {
  2051. int nNumVerts = m_pVertexFileHeader[ nModel ]->numLODVertexes[ 0 ];
  2052. m_nVertexRemap[ nModel ] = ( int * )g_CombinerWriter.AllocWrite( sizeof( int ) * nNumVerts );
  2053. nVertsWritten[ nModel ] = 0;
  2054. }
  2055. g_CombinerWriter.AlignWrite( 16 );
  2056. g_CombinerWriter.InitWriteArea( WRITE_AREA_VVD, g_CombinerWriter.GetWritePos() );
  2057. g_CombinerWriter.SetWriteArea( WRITE_AREA_VVD );
  2058. m_pCombinedVertex = ( vertexFileHeader_t * )g_CombinerWriter.AllocWrite( sizeof( *m_pCombinedVertex ) );
  2059. m_pCombinedVertex->id = MODEL_VERTEX_FILE_ID;
  2060. m_pCombinedVertex->version = MODEL_VERTEX_FILE_VERSION;
  2061. m_pCombinedVertex->numLODs = m_pVertexFileHeader[ 0 ]->numLODs;
  2062. g_CombinerWriter.WriteOffset( m_pCombinedVertex->vertexDataStart, m_pCombinedVertex );
  2063. int nVertOffset = 0;
  2064. for( int nLOD = m_pCombinedVertex->numLODs - 1; nLOD >= 0; nLOD-- )
  2065. {
  2066. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  2067. {
  2068. int nNumVerts = m_pVertexFileHeader[ nModel ]->numLODVertexes[ nLOD ];
  2069. m_pCombinedVertex->numLODVertexes[ nLOD ] += m_pVertexFileHeader[ nModel ]->numLODVertexes[ nLOD ];
  2070. nNumVerts -= nVertsWritten[ nModel ];
  2071. mstudiovertex_t *pNewVertex = ( mstudiovertex_t * )g_CombinerWriter.GetWritePos();
  2072. if ( m_pVertexFileHeader[ nModel ]->numFixups > 0 )
  2073. {
  2074. int nCount = nNumVerts;
  2075. int nOffset = nVertsWritten[ nModel ];
  2076. int nNumWritten = 0;
  2077. const mstudiovertex_t *pOrigVertex = m_pVertexFileHeader[ nModel ]->GetVertexData();
  2078. for( int nFixup = 0; nFixup < m_pVertexFileHeader[ nModel ]->numFixups; nFixup++ )
  2079. {
  2080. vertexFileFixup_t *pOrigFixup = ( vertexFileFixup_t * )( ( byte * )m_pVertexFileHeader[ nModel ] + m_pVertexFileHeader[ nModel ]->fixupTableStart) + nFixup;
  2081. if ( pOrigFixup->numVertexes < nOffset )
  2082. {
  2083. nOffset -= pOrigFixup->numVertexes;
  2084. }
  2085. else
  2086. {
  2087. int nSize = pOrigFixup->numVertexes - nOffset;
  2088. if ( nSize > nCount )
  2089. {
  2090. nSize = nCount;
  2091. }
  2092. g_CombinerWriter.WriteBuffer( pOrigVertex + nOffset, nSize * sizeof( mstudiovertex_t ) );
  2093. nOffset = 0;
  2094. nCount -= nSize;
  2095. nNumWritten += nSize;
  2096. if ( nCount == 0 )
  2097. {
  2098. break;
  2099. }
  2100. }
  2101. }
  2102. Assert( nNumWritten == nNumVerts );
  2103. }
  2104. else
  2105. {
  2106. g_CombinerWriter.WriteBuffer( m_pVertexFileHeader[ nModel ]->GetVertexData() + nVertsWritten[ nModel ], nNumVerts * sizeof( mstudiovertex_t ) );
  2107. }
  2108. if ( nModel > 0 )
  2109. { // primary one doesn't need bone remap
  2110. mstudiovertex_t *pBoneVertex = pNewVertex;
  2111. for( int nVert = 0, nVertIndex = nVertsWritten[ nModel ]; nVert < nNumVerts; nVert++, pBoneVertex++, nVertIndex++ )
  2112. {
  2113. for( int nBone = 0; nBone < pBoneVertex->m_BoneWeights.numbones; nBone++ )
  2114. {
  2115. pBoneVertex->m_BoneWeights.bone[ nBone ] = m_nBoneRemap[ nModel ][ pBoneVertex->m_BoneWeights.bone[ nBone ] ];
  2116. }
  2117. }
  2118. }
  2119. for( int nBodyBart = 0; nBodyBart < m_pStudioHdr[ nModel ]->numbodyparts; nBodyBart++ )
  2120. {
  2121. mstudiobodyparts_t *pOrigBodyPart = m_pStudioHdr[ nModel ]->pBodypart( nBodyBart );
  2122. int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
  2123. for( int nBodyPartModel = 0; nBodyPartModel < pOrigBodyPart->nummodels; nBodyPartModel++ )
  2124. {
  2125. // skip over sub models that are not the selected one (-1 no selection, so no skipping)
  2126. if ( nSubModelToUse != -1 && nBodyPartModel != nSubModelToUse )
  2127. {
  2128. continue;
  2129. }
  2130. mstudiomodel_t *pOrigModel = pOrigBodyPart->pModel( nBodyPartModel );
  2131. for( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ )
  2132. {
  2133. mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
  2134. Vector2D vStartST, vSizeST, vPixelSize;
  2135. int nTextureIndex = m_pCombinedStudioData->m_nModelMaterialIndices[ nModel ][ nMesh ];
  2136. GetTextureCombiner().GetTextureInfo( nTextureIndex, vStartST, vSizeST, vPixelSize );
  2137. for( int nVert = 0, nVertIndex = nVertsWritten[ nModel ] + pOrigMesh->vertexoffset; nVert < pOrigMesh->numvertices; nVert++, pNewVertex++, nVertIndex++ )
  2138. {
  2139. double flInteger;
  2140. pNewVertex->m_vecTexCoord.x = ( float )modf( ( double )pNewVertex->m_vecTexCoord.x, &flInteger );
  2141. if ( pNewVertex->m_vecTexCoord.x < 0.0f )
  2142. {
  2143. pNewVertex->m_vecTexCoord.x += 1.0f;
  2144. }
  2145. pNewVertex->m_vecTexCoord.y = ( float )modf( ( double )pNewVertex->m_vecTexCoord.y, &flInteger );
  2146. if ( pNewVertex->m_vecTexCoord.y < 0.0f )
  2147. {
  2148. pNewVertex->m_vecTexCoord.y += 1.0f;
  2149. }
  2150. pNewVertex->m_vecTexCoord = ( pNewVertex->m_vecTexCoord * vSizeST );
  2151. pNewVertex->m_vecTexCoord += vStartST;
  2152. m_nVertexRemap[ nModel ][ nVertIndex ] = nVertOffset + nVert;
  2153. }
  2154. }
  2155. }
  2156. }
  2157. nVertsWritten[ nModel ] += nNumVerts;
  2158. nVertOffset += nNumVerts;
  2159. m_pCombinedStudioData->m_Results.m_nNumLODs[ nModel ] = m_pVertexFileHeader[ nModel ]->numLODs;
  2160. if ( nLOD < m_pCombinedVertex->numLODs - 1 )
  2161. {
  2162. m_pCombinedStudioData->m_Results.m_nNumVerts[ nModel ][ nLOD ] = m_pCombinedStudioData->m_Results.m_nNumVerts[ nModel ][ nLOD + 1 ] + nNumVerts;
  2163. }
  2164. else
  2165. {
  2166. m_pCombinedStudioData->m_Results.m_nNumVerts[ nModel ][ nLOD ] = nNumVerts;
  2167. }
  2168. m_pCombinedStudioData->m_Results.m_nCombinedNumVerts[ nLOD ] += nNumVerts;
  2169. }
  2170. }
  2171. m_pCombinedStudioData->m_Results.m_nCombinedNumLODs = m_pCombinedVertex->numLODs;
  2172. int nLastLODSize = m_pCombinedVertex->numLODVertexes[ m_pCombinedVertex->numLODs - 1 ];
  2173. for( int nLOD = m_pCombinedVertex->numLODs; nLOD < MAX_NUM_LODS; nLOD++ )
  2174. {
  2175. m_pCombinedVertex->numLODVertexes[ nLOD ] = nLastLODSize;
  2176. }
  2177. Vector4D *pData = ( Vector4D * )g_CombinerWriter.WriteOffset( m_pCombinedVertex->tangentDataStart, m_pCombinedVertex );
  2178. g_CombinerWriter.AllocWrite( m_pCombinedVertex->numLODVertexes[ 0 ] * sizeof( Vector4D ) );
  2179. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  2180. {
  2181. if ( m_pVertexFileHeader[ nModel ]->tangentDataStart == 0 )
  2182. {
  2183. Assert( 0 );
  2184. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "vertex file has tangent data" );
  2185. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  2186. }
  2187. const Vector4D *vData = m_pVertexFileHeader[ nModel ]->GetTangentData();
  2188. for( int nIndex = 0; nIndex < m_pVertexFileHeader[ nModel ]->numLODVertexes[ 0 ]; nIndex++ )
  2189. {
  2190. pData[ m_nVertexRemap[ nModel ][ nIndex ] ] = *vData;
  2191. vData++;
  2192. }
  2193. }
  2194. CombineVVD_OffsetVerts();
  2195. }
  2196. #if 0
  2197. void CModelCombine::Test( )
  2198. {
  2199. studiohdr_t *pStudioHdr = m_pStudioHdr[ 0 ];
  2200. vertexFileHeader_t *pVertexHeader = ( vertexFileHeader_t * )VVD_Data[ 0 ]->PeekGet();
  2201. const mstudiovertex_t *pVertex = pVertexHeader->GetVertexData();
  2202. int nTotalModels = 0;
  2203. for ( int nBodyPart = 0; nBodyPart < pStudioHdr->numbodyparts; nBodyPart++ )
  2204. {
  2205. mstudiobodyparts_t *pOrigBodyPart = pStudioHdr->pBodypart( nBodyPart );
  2206. for( int nModel = 0; nModel < pOrigBodyPart->nummodels; nModel++ )
  2207. {
  2208. mstudiomodel_t *pOrigModel = pOrigBodyPart->pModel( nModel );
  2209. for( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ )
  2210. {
  2211. mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
  2212. for( int nVert = 0; nVert < pOrigMesh->numvertices; nVert++ )
  2213. {
  2214. const mstudiovertex_t *pOrigVert = &pVertex[ nVert + pOrigMesh->vertexoffset ];
  2215. Msg("");
  2216. }
  2217. }
  2218. }
  2219. }
  2220. }
  2221. #endif
  2222. void CModelCombine::CombineTextures( )
  2223. {
  2224. GetTextureCombiner().Init( m_pCombinedStudioData );
  2225. for( int nModel = 0; nModel < m_pCombinedStudioData->m_nNumModels; nModel++ )
  2226. {
  2227. #if 0
  2228. for( int nSkin = 0; nSkin < m_pStudioHdr[ nModel ]->numskinfamilies; nSkin++ )
  2229. {
  2230. short *pSkinRef = m_pStudioHdr[ nModel ]->pSkinref( 0 );
  2231. pSkinRef += ( nSkin * m_pStudioHdr[ nModel ]->numskinref );
  2232. for( int nReference = 0; nReference < m_pStudioHdr[ nModel ]->numskinref; nReference++, pSkinRef++ )
  2233. {
  2234. int nTextureIndex = *pSkinRef;
  2235. const char *pszTextureName = m_pStudioHdr[ nModel ]->pTexture( nTextureIndex )->pszName();
  2236. Msg( "SKin %d, Reference %d: %s\n", nSkin, nReference, pszTextureName );
  2237. }
  2238. }
  2239. #endif
  2240. int nSkin = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nSkinFamily;
  2241. short *pSkinRef = m_pStudioHdr[ nModel ]->pSkinref( 0 );
  2242. if ( nSkin > 0 && nSkin < m_pStudioHdr[ nModel ]->numskinfamilies )
  2243. {
  2244. pSkinRef += ( nSkin * m_pStudioHdr[ nModel ]->numskinref );
  2245. }
  2246. for( int nBodyPart = 0; nBodyPart < m_pStudioHdr[ nModel ]->numbodyparts; nBodyPart++ )
  2247. {
  2248. mstudiobodyparts_t *pOrigBodyPart = m_pStudioHdr[ nModel ]->pBodypart( nBodyPart );
  2249. int nSubModelToUse = m_pCombinedStudioData->m_ModelInputData[ nModel ].m_nBodyGroupSubModelSelection;
  2250. for( int nBodyPartModel = 0; nBodyPartModel < pOrigBodyPart->nummodels; nBodyPartModel++ )
  2251. {
  2252. // skip over sub models that are not the selected one (-1 no selection, so no skipping)
  2253. if ( nSubModelToUse != -1 && nBodyPartModel != nSubModelToUse )
  2254. {
  2255. continue;
  2256. }
  2257. mstudiomodel_t *pOrigModel = pOrigBodyPart->pModel( nBodyPartModel );
  2258. m_pCombinedStudioData->m_nModelMaterialCounts[ nModel ] = pOrigModel->nummeshes;
  2259. for( int nMesh = 0; nMesh < pOrigModel->nummeshes; nMesh++ )
  2260. {
  2261. mstudiomesh_t *pOrigMesh = pOrigModel->pMesh( nMesh );
  2262. int nTextureIndex = pSkinRef[ pOrigMesh->material ];
  2263. m_pCombinedStudioData->m_MeshToMaterialMap[ nModel ][ nBodyPart ][ nMesh ] = nTextureIndex;
  2264. m_pCombinedStudioData->m_nModelMaterialIndices[ nModel ][ nMesh ] = AddMaterialToTextureCombiner( nTextureIndex, nModel, pOrigMesh->material );
  2265. }
  2266. }
  2267. }
  2268. }
  2269. GetTextureCombiner().Resolve();
  2270. }
  2271. int CModelCombine::AddMaterialToTextureCombiner( int nTextureIndex, int nModel, int nModelMaterialIndex )
  2272. {
  2273. char szPath[ MAX_PATH ];
  2274. char szFinalPath[ MAX_PATH ];
  2275. bool bFound = false;
  2276. // If we don't do this, we get filenames like "materials\\blah.vmt".
  2277. const char *pszTextureName = m_pStudioHdr[ nModel ]->pTexture( nTextureIndex )->pszName();
  2278. if ( pszTextureName[ 0 ] == CORRECT_PATH_SEPARATOR || pszTextureName[ 0 ] == INCORRECT_PATH_SEPARATOR )
  2279. {
  2280. pszTextureName++;
  2281. }
  2282. // search through all specified directories until a valid material is found
  2283. for ( int nSearch = 0; nSearch < m_pStudioHdr[ nModel ]->numcdtextures; nSearch++ )
  2284. {
  2285. // This prevents filenames like /models/blah.vmt.
  2286. const char *pszCDTexture = m_pStudioHdr[ nModel ]->pCdtexture( nSearch );
  2287. if ( pszCDTexture[ 0 ] == CORRECT_PATH_SEPARATOR || pszCDTexture[ 0 ] == INCORRECT_PATH_SEPARATOR )
  2288. {
  2289. pszCDTexture++;
  2290. }
  2291. V_ComposeFileName( pszCDTexture, pszTextureName, szPath, sizeof( szPath ) );
  2292. V_ComposeFileName( "materials/", szPath, szFinalPath, sizeof( szFinalPath ) );
  2293. char szCheckPath[ MAX_PATH ];
  2294. V_strcpy_safe( szCheckPath, szFinalPath );
  2295. V_strcat_safe( szCheckPath, ".vmt" );
  2296. if ( g_pFullFileSystem->FileExists( szCheckPath ) )
  2297. {
  2298. bFound = true;
  2299. break;
  2300. }
  2301. }
  2302. if ( bFound == false )
  2303. {
  2304. Assert( 0 );
  2305. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "model %s has missing material %s", m_pStudioHdr[ nModel ]->pszName(), pszTextureName );
  2306. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  2307. }
  2308. return GetTextureCombiner().AddMaterial( szFinalPath );
  2309. }