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.

393 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "model_combiner.h"
  9. struct model_combine_t;
  10. enum combine_result_t
  11. {
  12. COMBINE_NOT_STARTED,
  13. COMBINE_IN_PROCESS,
  14. COMBINE_SUCCEEDED,
  15. COMBINE_FAILED,
  16. };
  17. static const char *g_pszCombineResults[] =
  18. {
  19. "not started", // COMBINE_NOT_STARTED,
  20. "in process", // COMBINE_IN_PROCESS,
  21. "succeeded", // COMBINE_SUCCEEDED,
  22. "failed" // COMBINE_FAILED,
  23. };
  24. //-----------------------------------------------------------------------------
  25. // Purpose:
  26. //-----------------------------------------------------------------------------
  27. // Class that manages combining multiple models into single models
  28. class CModelCombiner : public CAutoGameSystem
  29. {
  30. public:
  31. CModelCombiner();
  32. // Request a combined model to be created for the base model and the attached additional models.
  33. // The requester it must derive from IModelCombinerRequesterInterface. The ModelCombineFinished
  34. // function will be called with the result of the combine request.
  35. bool CombineModel( IModelCombinerRequesterInterface *pRequester, const char *pszBaseModel, CUtlVector<const char *> *vecAdditionalModels );
  36. bool CombineModel( IModelCombinerRequesterInterface *pRequester, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine );
  37. // Call this when you're being destroyed. It'll remove you from any pending callback lists.
  38. void AbortCombineModelFor( IModelCombinerRequesterInterface *pRequester );
  39. public:
  40. model_combine_t *FindCombineRequestForIndex( int iCombineIndex );
  41. virtual void LevelShutdownPostEntity( void );
  42. void DumpStats( void );
  43. private:
  44. void FillOutCombineRequest( model_combine_t *pRequest, IModelCombinerRequesterInterface *pRequester, const char *pszBaseModel, CUtlVector<const char *> *vecAdditionalModels );
  45. void FillOutCombineRequest( model_combine_t *pRequest, IModelCombinerRequesterInterface *pRequester, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine );
  46. bool CombineRequestExists( model_combine_t *pRequest, combine_result_t *iResult, int *iIndex );
  47. void AddRequesterToCallbackList( int iIndex, IModelCombinerRequesterInterface *pRequester );
  48. private:
  49. CUtlVector<model_combine_t*> m_vecCombines;
  50. int m_nNextCombineIndex;
  51. };
  52. CModelCombiner *ModelCombiner( void );
  53. // Struct that contains the state of a single model combine request
  54. struct model_combine_t
  55. {
  56. model_combine_t& operator=( const model_combine_t& src )
  57. {
  58. if ( this == &src )
  59. {
  60. return *this;
  61. }
  62. nCombinedModelIndex = src.nCombinedModelIndex;
  63. vecModelsToCombine = src.vecModelsToCombine;
  64. pRequesterCallbackList = src.pRequesterCallbackList;
  65. iCombineRequestIndex = src.iCombineRequestIndex;
  66. iCombineResult = src.iCombineResult;
  67. return *this;
  68. }
  69. bool operator==( const model_combine_t &val ) const
  70. {
  71. if ( vecModelsToCombine.Count() != val.vecModelsToCombine.Count() )
  72. {
  73. return false;
  74. }
  75. FOR_EACH_VEC( vecModelsToCombine, i )
  76. {
  77. if ( ( vecModelsToCombine[ i ].m_iszModelName != val.vecModelsToCombine[i].m_iszModelName ) ||
  78. ( vecModelsToCombine[ i ].m_nBodyGroupSubModelSelection != val.vecModelsToCombine[i].m_nBodyGroupSubModelSelection ) )
  79. {
  80. return false;
  81. }
  82. for ( int j = 0; j < COMBINER_MAX_MATERIALS_PER_INPUT_MODEL; j++ )
  83. {
  84. for ( int k = 0; k < COMBINER_MAX_TEXTURES_PER_MATERIAL; k++ )
  85. {
  86. if ( ( vecModelsToCombine[ i ].m_textureSubstitutes[ j ][ k ].m_iszMaterialParam != val.vecModelsToCombine[ i ].m_textureSubstitutes[ j ][ k ].m_iszMaterialParam ) ||
  87. ( vecModelsToCombine[ i ].m_textureSubstitutes[ j ][ k ].m_pVTFTexture != val.vecModelsToCombine[ i ].m_textureSubstitutes[ j ][ k ].m_pVTFTexture ) )
  88. {
  89. return false;
  90. }
  91. }
  92. }
  93. }
  94. return true;
  95. }
  96. int nCombinedModelIndex;
  97. CCopyableUtlVector<SCombinerModelInput_t> vecModelsToCombine;
  98. CCopyableUtlVector<IModelCombinerRequesterInterface*> pRequesterCallbackList;
  99. int iCombineRequestIndex;
  100. combine_result_t iCombineResult;
  101. };
  102. CModelCombiner g_ModelCombiner;
  103. CModelCombiner* ModelCombiner() { return &g_ModelCombiner; }
  104. // Request a combined model to be created for the base model and the attached additional models.
  105. // The requester it must derive from IModelCombinerRequesterInterface. The ModelCombineFinished
  106. // function will be called with the result of the combine request.
  107. bool ModelCombiner_CombineModel( IModelCombinerRequesterInterface *pRequester, const char *pszBaseModel, CUtlVector<const char *> *vecAdditionalModels )
  108. {
  109. CUtlVector< SCombinerModelInput_t > vecModelsToCombine;
  110. string_t iszBaseModel = AllocPooledString( pszBaseModel );
  111. vecModelsToCombine.AddToTail( SCombinerModelInput_t( iszBaseModel ) );
  112. FOR_EACH_VEC( *vecAdditionalModels, i )
  113. {
  114. string_t iszAdditionalModel = AllocPooledString( vecAdditionalModels->Element( i ) );
  115. vecModelsToCombine.AddToTail( SCombinerModelInput_t( iszAdditionalModel ) );
  116. }
  117. return ModelCombiner()->CombineModel( pRequester, vecModelsToCombine );
  118. }
  119. bool ModelCombiner_CombineModel( IModelCombinerRequesterInterface *pRequester, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine )
  120. {
  121. return ModelCombiner()->CombineModel( pRequester, vecModelsToCombine );
  122. }
  123. // Call this when you're being destroyed. It'll remove you from any pending callback lists.
  124. void ModelCombiner_AbortCombineModelFor( IModelCombinerRequesterInterface *pRequester )
  125. {
  126. ModelCombiner()->AbortCombineModelFor( pRequester );
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. CModelCombiner::CModelCombiner()
  132. {
  133. m_nNextCombineIndex = 0;
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. //-----------------------------------------------------------------------------
  138. void CModelCombiner::FillOutCombineRequest( model_combine_t *pRequest, IModelCombinerRequesterInterface *pRequester, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine )
  139. {
  140. FOR_EACH_VEC( vecModelsToCombine, i )
  141. {
  142. pRequest->vecModelsToCombine.AddToTail( vecModelsToCombine.Element( i ) );
  143. }
  144. pRequest->pRequesterCallbackList.AddToTail( pRequester );
  145. pRequest->iCombineRequestIndex = -1;
  146. pRequest->nCombinedModelIndex = 0;
  147. pRequest->iCombineResult = COMBINE_NOT_STARTED;
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose:
  151. //-----------------------------------------------------------------------------
  152. bool CModelCombiner::CombineRequestExists( model_combine_t *pRequest, combine_result_t *iResult, int *iIndex )
  153. {
  154. *iResult = COMBINE_NOT_STARTED;
  155. *iIndex = -1;
  156. FOR_EACH_VEC( m_vecCombines, i )
  157. {
  158. if ( *m_vecCombines[i] == *pRequest )
  159. {
  160. *iResult = m_vecCombines[i]->iCombineResult;
  161. *iIndex = i;
  162. break;
  163. }
  164. }
  165. return (*iIndex != -1);
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. //-----------------------------------------------------------------------------
  170. void CModelCombiner::AddRequesterToCallbackList( int iIndex, IModelCombinerRequesterInterface *pRequester )
  171. {
  172. FOR_EACH_VEC( m_vecCombines[iIndex]->pRequesterCallbackList, i )
  173. {
  174. if ( m_vecCombines[iIndex]->pRequesterCallbackList[i] == pRequester )
  175. return;
  176. }
  177. m_vecCombines[iIndex]->pRequesterCallbackList.AddToTail( pRequester );
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. //-----------------------------------------------------------------------------
  182. model_combine_t *CModelCombiner::FindCombineRequestForIndex( int iCombineIndex )
  183. {
  184. FOR_EACH_VEC( m_vecCombines, i )
  185. {
  186. if ( m_vecCombines[i]->iCombineRequestIndex == iCombineIndex )
  187. return m_vecCombines[i];
  188. }
  189. return NULL;
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose:
  193. //-----------------------------------------------------------------------------
  194. void CModelCombiner::LevelShutdownPostEntity( void )
  195. {
  196. // Release all the combined models.
  197. FOR_EACH_VEC( m_vecCombines, i )
  198. {
  199. modelinfo->ReleaseDynamicModel( m_vecCombines[i]->nCombinedModelIndex );
  200. }
  201. m_vecCombines.PurgeAndDeleteElements();
  202. }
  203. //-----------------------------------------------------------------------------
  204. // Purpose:
  205. //-----------------------------------------------------------------------------
  206. void ModelCombineFinished( void *pUserData, MDLHandle_t OldHandle, MDLHandle_t NewHandle, TCombinedResults &CombinedResults )
  207. {
  208. int *pCombineIndex = (int *)pUserData;
  209. if ( !pCombineIndex )
  210. return;
  211. model_combine_t *pCombine = ModelCombiner()->FindCombineRequestForIndex( *pCombineIndex );
  212. if ( !pCombine )
  213. {
  214. Warning("MODELCOMBINER: Received a ModelCombineFinished call with no matching combine request (%d)\n", *pCombineIndex );
  215. return;
  216. }
  217. bool bSucceeded = ( CombinedResults.m_nCombinedResults == COMBINE_RESULT_FLAG_OK );
  218. if ( bSucceeded )
  219. {
  220. DevMsg("MODELCOMBINER: Finishing building combined model for %s.\n", STRING( pCombine->vecModelsToCombine.Element( 0 ).m_iszModelName ) );
  221. pCombine->iCombineResult = COMBINE_SUCCEEDED;
  222. modelinfo->UpdateCombinedDynamicModel( pCombine->nCombinedModelIndex, NewHandle );
  223. }
  224. else
  225. {
  226. DevMsg("MODELCOMBINER: Failed to build combined model for %s: Error %s (%d)\n", STRING( pCombine->vecModelsToCombine.Element( 0 ).m_iszModelName ), CombinedResults.m_szErrorMessage, CombinedResults.m_nCombinedResults );
  227. pCombine->iCombineResult = COMBINE_FAILED;
  228. modelinfo->ReleaseDynamicModel( pCombine->nCombinedModelIndex );
  229. }
  230. // Fire off the callbacks for every requester that wanted to use their combined model
  231. FOR_EACH_VEC( pCombine->pRequesterCallbackList, i )
  232. {
  233. pCombine->pRequesterCallbackList[i]->ModelCombineFinished( bSucceeded, pCombine->nCombinedModelIndex );
  234. }
  235. pCombine->pRequesterCallbackList.Purge();
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. //-----------------------------------------------------------------------------
  240. bool CModelCombiner::CombineModel( IModelCombinerRequesterInterface *pRequester, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine )
  241. {
  242. Assert( vecModelsToCombine.Count() > 1 );
  243. model_combine_t *pCombine = new model_combine_t();
  244. FillOutCombineRequest( pCombine, pRequester, vecModelsToCombine );
  245. DevMsg("MODELCOMBINER: Received combine request for:\n");
  246. FOR_EACH_VEC( vecModelsToCombine, i )
  247. {
  248. DevMsg(" -> %s\n", STRING( vecModelsToCombine.Element( i ).m_iszModelName ) );
  249. }
  250. DevMsg(" %d models.\n", vecModelsToCombine.Count() );
  251. // Have we already tried combining this set of models?
  252. int iExistingIndex;
  253. combine_result_t iExistingState;
  254. if ( CombineRequestExists( pCombine, &iExistingState, &iExistingIndex ) )
  255. {
  256. DevMsg(" FOUND existing combine in state: %d\n", iExistingState );
  257. if ( iExistingState == COMBINE_IN_PROCESS )
  258. {
  259. // We're still combining this set. Add ourselves to the callback list.
  260. AddRequesterToCallbackList( iExistingIndex, pRequester );
  261. DevMsg(" ADDING to the in-progress callback list.\n" );
  262. }
  263. else if ( iExistingState == COMBINE_SUCCEEDED )
  264. {
  265. // Tell the requester the combined model is already available.
  266. pRequester->ModelCombineFinished( true, m_vecCombines[iExistingIndex]->nCombinedModelIndex );
  267. DevMsg(" RE-USING existing combined model (%d).\n", m_vecCombines[iExistingIndex]->nCombinedModelIndex );
  268. }
  269. delete pCombine;
  270. return ( ( iExistingState == COMBINE_SUCCEEDED ) || ( iExistingState == COMBINE_IN_PROCESS ) );
  271. }
  272. // New combine. Add it to our list.
  273. m_vecCombines.AddToTail( pCombine );
  274. pCombine->iCombineRequestIndex = m_nNextCombineIndex++;
  275. pCombine->iCombineResult = COMBINE_FAILED;
  276. // Build our unique combined model name.
  277. char szCombinedModelName[ 256 ];
  278. V_sprintf_safe( szCombinedModelName, "%s_c_%d", STRING( vecModelsToCombine.Element( 0 ).m_iszModelName ), pCombine->iCombineRequestIndex );
  279. pCombine->nCombinedModelIndex = modelinfo->BeginCombinedModel( szCombinedModelName, true );
  280. if ( pCombine->nCombinedModelIndex == -1 )
  281. {
  282. AssertMsg1( false, "Failed to combine model %s", STRING( vecModelsToCombine.Element( 0 ).m_iszModelName ) );
  283. return false;
  284. }
  285. if ( !modelinfo->SetCombineModels( pCombine->nCombinedModelIndex, vecModelsToCombine ) )
  286. {
  287. // we failed - invalid handle?
  288. AssertMsg( "Failed to set combined models for %s!", STRING( vecModelsToCombine.Element( 0 ).m_iszModelName ) );
  289. return false;
  290. }
  291. DevMsg(" STARTED Combining: final model index will be %d\n", pCombine->nCombinedModelIndex );
  292. pCombine->iCombineResult = COMBINE_IN_PROCESS;
  293. modelinfo->FinishCombinedModel( pCombine->nCombinedModelIndex, &ModelCombineFinished, &pCombine->iCombineRequestIndex );
  294. return true;
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose:
  298. //-----------------------------------------------------------------------------
  299. void CModelCombiner::AbortCombineModelFor( IModelCombinerRequesterInterface *pRequester )
  300. {
  301. FOR_EACH_VEC( m_vecCombines, i )
  302. {
  303. FOR_EACH_VEC_BACK( m_vecCombines[ i ]->pRequesterCallbackList, j )
  304. {
  305. if ( m_vecCombines[ i ]->pRequesterCallbackList[ j ] == pRequester )
  306. {
  307. m_vecCombines[ i ]->pRequesterCallbackList.Remove( j );
  308. DevMsg("MODELCOMBINER: Removing requester from callback for %s\n", STRING( m_vecCombines[ i ]->vecModelsToCombine.Element( 0 ).m_iszModelName ) );
  309. }
  310. }
  311. }
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose:
  315. //-----------------------------------------------------------------------------
  316. void CModelCombiner::DumpStats( void )
  317. {
  318. Msg("Model Combiner State\n %d combined models.\n", m_vecCombines.Count());
  319. FOR_EACH_VEC( m_vecCombines, i )
  320. {
  321. Msg(" %d: Base model %s (%d attached models)\n", i, STRING( m_vecCombines[ i ]->vecModelsToCombine.Element( 0 ).m_iszModelName ), m_vecCombines[ i ]->vecModelsToCombine.Count() - 1 );
  322. FOR_EACH_VEC( m_vecCombines[ i ]->vecModelsToCombine, j )
  323. {
  324. if ( j > 0 )
  325. {
  326. Msg(" -> %s\n", STRING( m_vecCombines[ i ]->vecModelsToCombine.Element( j ).m_iszModelName ) );
  327. }
  328. }
  329. Msg(" Combine result: %s\n", g_pszCombineResults[ m_vecCombines[ i ]->iCombineResult ] );
  330. Msg(" Combine model index: %d\n", m_vecCombines[ i ]->nCombinedModelIndex );
  331. Msg(" Combine request index: %d\n", m_vecCombines[ i ]->iCombineRequestIndex );
  332. Msg(" Number of requesters: %d\n", m_vecCombines[ i ]->pRequesterCallbackList.Count() );
  333. }
  334. }