Team Fortress 2 Source Code as on 22/4/2020
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.

694 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Dme version of a game model (MDL)
  4. //
  5. //=============================================================================
  6. #include "movieobjects/dmegamemodel.h"
  7. #include "movieobjects_interfaces.h"
  8. #include "datamodel/dmelementfactoryhelper.h"
  9. #include "datacache/imdlcache.h"
  10. #include "studio.h"
  11. #include "tier3/tier3.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. // Expose this class to the scene database
  16. //-----------------------------------------------------------------------------
  17. IMPLEMENT_ELEMENT_FACTORY( DmeGlobalFlexControllerOperator, CDmeGlobalFlexControllerOperator );
  18. //-----------------------------------------------------------------------------
  19. // Purpose:
  20. //-----------------------------------------------------------------------------
  21. void CDmeGlobalFlexControllerOperator::OnConstruction()
  22. {
  23. m_flexWeight.Init( this, "flexWeight" );
  24. m_gameModel.Init( this, "gameModel", FATTRIB_HAS_CALLBACK );
  25. m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
  26. m_nFlexControllerIndex = -1;
  27. }
  28. void CDmeGlobalFlexControllerOperator::OnDestruction()
  29. {
  30. }
  31. void CDmeGlobalFlexControllerOperator::Resolve()
  32. {
  33. if ( m_nFlexControllerIndex < 0 )
  34. {
  35. m_nFlexControllerIndex = FindGlobalFlexControllerIndex();
  36. }
  37. }
  38. void CDmeGlobalFlexControllerOperator::OnAttributeChanged( CDmAttribute *pAttribute )
  39. {
  40. // Don't have the required interface...
  41. if ( !g_pGlobalFlexController )
  42. return;
  43. if ( pAttribute == m_gameModel.GetAttribute() && m_gameModel.GetElement() )
  44. {
  45. m_nFlexControllerIndex = FindGlobalFlexControllerIndex();
  46. SetupToAttribute();
  47. }
  48. }
  49. void CDmeGlobalFlexControllerOperator::Operate()
  50. {
  51. CDmAttribute *pToAttr = g_pDataModel->GetAttribute( m_ToAttributeHandle );
  52. if ( !pToAttr )
  53. return;
  54. DmAttributeType_t type = m_flexWeight.GetAttribute()->GetType();
  55. const void *pValue = m_flexWeight.GetAttribute()->GetValueUntyped();
  56. if ( IsArrayType( pToAttr->GetType() ) )
  57. {
  58. if ( m_nFlexControllerIndex == -1 )
  59. return;
  60. CDmrGenericArray array( pToAttr );
  61. array.Set( m_nFlexControllerIndex, type, pValue );
  62. }
  63. else
  64. {
  65. pToAttr->SetValue( type, pValue );
  66. }
  67. }
  68. void CDmeGlobalFlexControllerOperator::SetGameModel( CDmeGameModel *gameModel )
  69. {
  70. m_gameModel = gameModel;
  71. }
  72. void CDmeGlobalFlexControllerOperator::SetWeight( float flWeight )
  73. {
  74. m_flexWeight = flWeight;
  75. }
  76. void CDmeGlobalFlexControllerOperator::SetMapping( int globalIndex )
  77. {
  78. m_nFlexControllerIndex = globalIndex;
  79. if ( m_gameModel.GetElement() )
  80. {
  81. if ( (uint)globalIndex >= m_gameModel->NumFlexWeights() )
  82. {
  83. m_gameModel->SetNumFlexWeights( (uint)( globalIndex + 1 ) );
  84. }
  85. }
  86. }
  87. int CDmeGlobalFlexControllerOperator::GetGlobalIndex() const
  88. {
  89. return m_nFlexControllerIndex;
  90. }
  91. void CDmeGlobalFlexControllerOperator::GetInputAttributes ( CUtlVector< CDmAttribute * > &attrs )
  92. {
  93. attrs.AddToTail( m_flexWeight.GetAttribute() );
  94. }
  95. void CDmeGlobalFlexControllerOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
  96. {
  97. CDmAttribute *toAttribute = g_pDataModel->GetAttribute( m_ToAttributeHandle );
  98. if ( toAttribute )
  99. {
  100. attrs.AddToTail( toAttribute );
  101. }
  102. }
  103. void CDmeGlobalFlexControllerOperator::SetupToAttribute()
  104. {
  105. CDmElement *pObject = m_gameModel.GetElement();
  106. if ( pObject == NULL)
  107. return;
  108. CDmAttribute *pAttr = pObject->GetAttribute( "flexWeights" );
  109. Assert( pAttr );
  110. if ( !pAttr )
  111. return;
  112. m_ToAttributeHandle = pAttr->GetHandle();
  113. return;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Connect up stuff by index
  117. //-----------------------------------------------------------------------------
  118. int CDmeGlobalFlexControllerOperator::FindGlobalFlexControllerIndex() const
  119. {
  120. int nGlobalFlexControllerIndex = -1;
  121. const char *pModelName = m_gameModel->GetModelName();
  122. MDLHandle_t h = pModelName && pModelName[0] ? g_pMDLCache->FindMDL( pModelName ) : MDLHANDLE_INVALID;
  123. if ( h != MDLHANDLE_INVALID )
  124. {
  125. studiohdr_t *hdr = g_pMDLCache->GetStudioHdr( h );
  126. Assert( hdr );
  127. if ( hdr )
  128. {
  129. int fc = hdr->numflexcontrollers;
  130. for ( LocalFlexController_t i = LocalFlexController_t(0) ; i < fc; ++i )
  131. {
  132. mstudioflexcontroller_t *flex = hdr->pFlexcontroller( i );
  133. if ( flex->localToGlobal == -1 )
  134. {
  135. flex->localToGlobal = g_pGlobalFlexController->FindGlobalFlexController( flex->pszName() );
  136. }
  137. if ( !Q_stricmp( flex->pszName(), GetName() ) )
  138. {
  139. nGlobalFlexControllerIndex = flex->localToGlobal;
  140. // Grow the array
  141. if ( (uint)flex->localToGlobal >= m_gameModel->NumFlexWeights() )
  142. {
  143. m_gameModel->SetNumFlexWeights( (uint)( flex->localToGlobal + 1 ) );
  144. }
  145. break;
  146. }
  147. }
  148. }
  149. g_pMDLCache->Release( h );
  150. }
  151. return nGlobalFlexControllerIndex;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Expose this class to the scene database
  155. //-----------------------------------------------------------------------------
  156. IMPLEMENT_ELEMENT_FACTORY( DmeGameModel, CDmeGameModel );
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. //-----------------------------------------------------------------------------
  160. void CDmeGameModel::OnConstruction()
  161. {
  162. m_flexWeights.Init( this, "flexWeights" );
  163. m_viewTarget.Init( this, "viewTarget" );
  164. m_modelName.Init( this, "modelName", FATTRIB_HAS_CALLBACK );
  165. m_skin.Init( this, "skin" );
  166. m_body.Init( this, "body" );
  167. m_sequence.Init( this, "sequence" );
  168. m_flags.Init( this, "flags" );
  169. m_bones.Init( this, "bones" );
  170. m_globalFlexControllers.Init( this, "globalFlexControllers" );
  171. m_bComputeBounds.Init( this, "computeBounds" );
  172. }
  173. void CDmeGameModel::OnDestruction()
  174. {
  175. }
  176. CDmeGlobalFlexControllerOperator *CDmeGameModel::AddGlobalFlexController( const char *controllerName, int globalIndex )
  177. {
  178. int i, c;
  179. c = m_globalFlexControllers.Count();
  180. for ( i = 0; i < c; ++i )
  181. {
  182. CDmeGlobalFlexControllerOperator *op = m_globalFlexControllers.Get( i );
  183. Assert( op );
  184. if ( op && !Q_stricmp( op->GetName(), controllerName ) )
  185. break;
  186. }
  187. if ( i >= c )
  188. {
  189. CDmeGlobalFlexControllerOperator *newOperator = CreateElement< CDmeGlobalFlexControllerOperator >( controllerName, GetFileId() );
  190. Assert( newOperator );
  191. if ( !newOperator )
  192. return NULL;
  193. i = m_globalFlexControllers.AddToTail( newOperator );
  194. }
  195. Assert( m_globalFlexControllers.IsValidIndex( i ) );
  196. CDmeGlobalFlexControllerOperator *op = m_globalFlexControllers.Get( i );
  197. Assert( op );
  198. if ( op )
  199. {
  200. op->SetMapping( globalIndex );
  201. op->SetGameModel( this );
  202. }
  203. if ( (uint)globalIndex >= NumFlexWeights() )
  204. {
  205. SetNumFlexWeights( globalIndex + 1 );
  206. }
  207. return op;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Find a flex controller by its global index
  211. //-----------------------------------------------------------------------------
  212. CDmeGlobalFlexControllerOperator *CDmeGameModel::FindGlobalFlexController( int nGlobalIndex )
  213. {
  214. int i, c;
  215. c = m_globalFlexControllers.Count();
  216. for ( i = 0; i < c; ++i )
  217. {
  218. CDmeGlobalFlexControllerOperator *op = m_globalFlexControllers.Get( i );
  219. Assert( op );
  220. if ( op && op->GetGlobalIndex() == nGlobalIndex )
  221. return op;
  222. }
  223. return NULL;
  224. }
  225. studiohdr_t* CDmeGameModel::GetStudioHdr() const
  226. {
  227. const char *pModelName = GetModelName();
  228. MDLHandle_t h = pModelName && pModelName[0] ? g_pMDLCache->FindMDL( pModelName ) : MDLHANDLE_INVALID;
  229. return ( h != MDLHANDLE_INVALID ) ? g_pMDLCache->GetStudioHdr( h ) : NULL;
  230. }
  231. // A src bone transform transforms pre-compiled data (.dmx or .smd files, for example)
  232. // into post-compiled data (.mdl or .ani files)
  233. bool CDmeGameModel::GetSrcBoneTransforms( matrix3x4_t *pPreTransform, matrix3x4_t *pPostTransform, int nBoneIndex ) const
  234. {
  235. studiohdr_t *pStudioHdr = GetStudioHdr();
  236. if ( !pStudioHdr )
  237. return false;
  238. if ( pStudioHdr->numbones <= nBoneIndex )
  239. return false;
  240. const char *pBoneName = pStudioHdr->pBone( nBoneIndex )->pszName();
  241. int nCount = pStudioHdr->NumSrcBoneTransforms();
  242. for ( int i = 0; i < nCount; ++i )
  243. {
  244. const mstudiosrcbonetransform_t *pSrcTransform = pStudioHdr->SrcBoneTransform( i );
  245. if ( Q_stricmp( pSrcTransform->pszName(), pBoneName ) )
  246. continue;
  247. MatrixCopy( pSrcTransform->pretransform, *pPreTransform );
  248. MatrixCopy( pSrcTransform->posttransform, *pPostTransform );
  249. return true;
  250. }
  251. return false;
  252. }
  253. bool CDmeGameModel::IsRootTransform( int nBoneIndex ) const
  254. {
  255. studiohdr_t *pStudioHdr = GetStudioHdr();
  256. if ( !pStudioHdr )
  257. return true;
  258. if ( pStudioHdr->numbones <= nBoneIndex )
  259. return true;
  260. mstudiobone_t *pBone = pStudioHdr->pBone( nBoneIndex );
  261. return pBone->parent == -1;
  262. }
  263. int CDmeGameModel::NumGlobalFlexControllers() const
  264. {
  265. return m_globalFlexControllers.Count();
  266. }
  267. CDmeGlobalFlexControllerOperator *CDmeGameModel::GetGlobalFlexController( int localIndex )
  268. {
  269. return m_globalFlexControllers.Get( localIndex );
  270. }
  271. void CDmeGameModel::RemoveGlobalFlexController( CDmeGlobalFlexControllerOperator *controller )
  272. {
  273. int c = m_globalFlexControllers.Count();
  274. for ( int i = 0; i < c; ++i )
  275. {
  276. CDmeGlobalFlexControllerOperator *check = m_globalFlexControllers.Get( i );
  277. if ( check == controller )
  278. {
  279. m_globalFlexControllers.Remove( i );
  280. break;
  281. }
  282. }
  283. }
  284. void CDmeGameModel::AppendGlobalFlexControllerOperators( CUtlVector< IDmeOperator * >& list )
  285. {
  286. int c = m_globalFlexControllers.Count();
  287. for ( int i = 0 ; i < c; ++i )
  288. {
  289. CDmeOperator *op = m_globalFlexControllers.Get( i );
  290. if ( !op )
  291. continue;
  292. list.AddToTail( op );
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // accessors
  297. //-----------------------------------------------------------------------------
  298. void CDmeGameModel::AddBone( CDmeTransform* pTransform )
  299. {
  300. m_bones.AddToTail( pTransform );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Is this dag under the game model?
  304. //-----------------------------------------------------------------------------
  305. static bool IsDagUnderGameModel( CDmeDag *pDag, CDmeGameModel *pGameModel )
  306. {
  307. if ( pDag == pGameModel )
  308. return true;
  309. DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pDag->GetHandle() );
  310. while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
  311. {
  312. CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
  313. CDmElement *pDmeParent = pAttribute->GetOwner();
  314. const static UtlSymId_t symChildren = g_pDataModel->GetSymbol( "children" );
  315. if ( pDmeParent && pAttribute->GetNameSymbol() == symChildren )
  316. {
  317. CDmeDag *pParent = CastElement< CDmeDag >( pDmeParent );
  318. if ( pParent && ( pParent->GetFileId() == pDag->GetFileId() ) )
  319. {
  320. if ( IsDagUnderGameModel( pParent, pGameModel ) )
  321. return true;
  322. }
  323. }
  324. i = g_pDataModel->NextAttributeReferencingElement( i );
  325. }
  326. return false;
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Is this dag under the game model?
  330. //-----------------------------------------------------------------------------
  331. static CDmeDag* GetDagForTransform( CDmeTransform *pTransform, CDmeGameModel *pGameModel )
  332. {
  333. DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pTransform->GetHandle() );
  334. while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
  335. {
  336. CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
  337. CDmElement *pDmeParent = pAttribute->GetOwner();
  338. const static UtlSymId_t symTransform = g_pDataModel->GetSymbol( "transform" );
  339. if ( pDmeParent && pAttribute->GetNameSymbol() == symTransform )
  340. {
  341. CDmeDag *pParent = CastElement< CDmeDag >( pDmeParent );
  342. if ( pParent && ( pParent->GetFileId() == pTransform->GetFileId() ) )
  343. {
  344. if ( IsDagUnderGameModel( pParent, pGameModel ) )
  345. return pParent;
  346. }
  347. }
  348. i = g_pDataModel->NextAttributeReferencingElement( i );
  349. }
  350. return NULL;
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Finds existing dags
  354. //-----------------------------------------------------------------------------
  355. void CDmeGameModel::PopulateExistingDagList( CDmeDag** pDags, int nCount )
  356. {
  357. int nCurrentBoneCount = m_bones.Count();
  358. for ( int i = 0; i < nCount; ++i )
  359. {
  360. if ( i >= nCurrentBoneCount )
  361. {
  362. pDags[ i ] = NULL;
  363. continue;
  364. }
  365. CDmeTransform *pTransform = GetBone( i );
  366. Assert( pTransform );
  367. pDags[ i ] = pTransform ? GetDagForTransform( pTransform, this ) : NULL;
  368. }
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Adds bones to the game model
  372. //-----------------------------------------------------------------------------
  373. void CDmeGameModel::AddBones( studiohdr_t *pStudioHdr, const char *pBaseName, int nFirstBone, int nCount )
  374. {
  375. if ( nFirstBone + nCount > pStudioHdr->numbones )
  376. {
  377. nCount = pStudioHdr->numbones - nFirstBone;
  378. if ( nCount <= 0 )
  379. return;
  380. }
  381. // make room for bones
  382. CDmeDag** pDags = ( CDmeDag** )_alloca( pStudioHdr->numbones * sizeof(CDmeDag*) );
  383. int nDagCount = nFirstBone;
  384. PopulateExistingDagList( pDags, nFirstBone );
  385. char name[ 256 ];
  386. for ( int i = 0; i < nCount; ++i )
  387. {
  388. int bi = i + nFirstBone;
  389. // get parent
  390. mstudiobone_t *pBone = pStudioHdr->pBone( bi );
  391. int parentIndex = pBone->parent;
  392. Assert( parentIndex < nDagCount );
  393. // build dag hierarchy to match bone hierarchy
  394. CDmeDag *pParent = ( parentIndex < 0 ) ? this : pDags[ parentIndex ];
  395. Q_snprintf( name, sizeof( name ), "%s_bone %d (%s)", pBaseName, bi, pBone->pszName() );
  396. CDmeDag *pDag = CreateElement< CDmeDag >( name, GetFileId() );
  397. pDags[nDagCount++] = pDag;
  398. pParent->AddChild( pDag );
  399. CDmeTransform *pTransform = pDag->GetTransform();
  400. pTransform->SetName( name );
  401. // add different bone representations to dme model and input
  402. AddBone( pTransform );
  403. }
  404. }
  405. void CDmeGameModel::SetBone( uint index, const Vector& pos, const Quaternion& rot )
  406. {
  407. m_bones[ index ]->SetPosition( pos );
  408. m_bones[ index ]->SetOrientation( rot );
  409. }
  410. void CDmeGameModel::RemoveAllBones()
  411. {
  412. m_bones.RemoveAll();
  413. }
  414. uint CDmeGameModel::NumBones() const
  415. {
  416. return m_bones.Count();
  417. }
  418. CDmeTransform *CDmeGameModel::GetBone( uint index ) const
  419. {
  420. return m_bones[ index ];
  421. }
  422. int CDmeGameModel::FindBone( CDmeTransform *pTransform ) const
  423. {
  424. return m_bones.Find( pTransform );
  425. }
  426. uint CDmeGameModel::NumFlexWeights() const
  427. {
  428. return m_flexWeights.Count();
  429. }
  430. const CUtlVector< float >& CDmeGameModel::GetFlexWeights() const
  431. {
  432. return m_flexWeights.Get();
  433. }
  434. void CDmeGameModel::SetNumFlexWeights( uint nFlexWeights )
  435. {
  436. if ( nFlexWeights > (uint)m_flexWeights.Count() )
  437. {
  438. while ( (uint)m_flexWeights.Count() < nFlexWeights )
  439. {
  440. m_flexWeights.AddToTail( 0.0f );
  441. }
  442. }
  443. else
  444. {
  445. while ( (uint)m_flexWeights.Count() > nFlexWeights )
  446. {
  447. m_flexWeights.Remove( (uint)m_flexWeights.Count() - 1 );
  448. }
  449. }
  450. }
  451. void CDmeGameModel::SetFlexWeights( uint nFlexWeights, const float* flexWeights )
  452. {
  453. m_flexWeights.CopyArray( flexWeights, nFlexWeights );
  454. }
  455. const Vector& CDmeGameModel::GetViewTarget() const
  456. {
  457. return m_viewTarget.Get();
  458. }
  459. void CDmeGameModel::SetViewTarget( const Vector &viewTarget )
  460. {
  461. m_viewTarget = viewTarget;
  462. }
  463. void CDmeGameModel::SetFlags( int nFlags )
  464. {
  465. m_flags = nFlags;
  466. }
  467. void CDmeGameModel::SetSkin( int nSkin )
  468. {
  469. m_skin = nSkin;
  470. }
  471. void CDmeGameModel::SetBody( int nBody )
  472. {
  473. m_body = nBody;
  474. }
  475. void CDmeGameModel::SetSequence( int nSequence )
  476. {
  477. m_sequence = nSequence;
  478. }
  479. int CDmeGameModel::GetSkin() const
  480. {
  481. return m_skin;
  482. }
  483. int CDmeGameModel::GetBody() const
  484. {
  485. return m_body;
  486. }
  487. int CDmeGameModel::GetSequence() const
  488. {
  489. return m_sequence;
  490. }
  491. const char *CDmeGameModel::GetModelName() const
  492. {
  493. return m_modelName.Get();
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Expose this class to the scene database
  497. //-----------------------------------------------------------------------------
  498. IMPLEMENT_ELEMENT_FACTORY( DmeGameSprite, CDmeGameSprite );
  499. //-----------------------------------------------------------------------------
  500. // Purpose:
  501. //-----------------------------------------------------------------------------
  502. void CDmeGameSprite::OnConstruction()
  503. {
  504. m_modelName .Init( this, "modelName" );
  505. m_frame .Init( this, "frame" );
  506. m_rendermode.Init( this, "rendermode" );
  507. m_renderfx .Init( this, "renderfx" );
  508. m_renderscale.Init( this, "renderscale" );
  509. m_color .Init( this, "color" );
  510. m_proxyRadius.Init( this, "proxyRadius" );
  511. }
  512. void CDmeGameSprite::OnDestruction()
  513. {
  514. }
  515. //-----------------------------------------------------------------------------
  516. // accessors
  517. //-----------------------------------------------------------------------------
  518. const char *CDmeGameSprite::GetModelName() const
  519. {
  520. return m_modelName.Get();
  521. }
  522. float CDmeGameSprite::GetScale() const
  523. {
  524. return m_renderscale;
  525. }
  526. float CDmeGameSprite::GetFrame() const
  527. {
  528. return m_frame;
  529. }
  530. int CDmeGameSprite::GetRenderMode() const
  531. {
  532. return m_rendermode;
  533. }
  534. int CDmeGameSprite::GetRenderFX() const
  535. {
  536. return m_renderfx;
  537. }
  538. const Color &CDmeGameSprite::GetColor() const
  539. {
  540. return m_color;
  541. }
  542. float CDmeGameSprite::GetProxyRadius() const
  543. {
  544. return m_proxyRadius;
  545. }
  546. void CDmeGameSprite::SetState( bool bVisible, float nFrame, int nRenderMode, int nRenderFX, float flRenderScale, float flProxyRadius,
  547. const Vector &pos, const Quaternion &rot, const Color &color )
  548. {
  549. m_Visible = bVisible;
  550. m_frame = nFrame;
  551. m_rendermode = nRenderMode;
  552. m_renderfx = nRenderFX;
  553. m_renderscale = flRenderScale;
  554. m_proxyRadius = flProxyRadius;
  555. m_color = color;
  556. CDmeTransform *pTransform = GetTransform();
  557. pTransform->SetPosition( pos );
  558. pTransform->SetOrientation( rot );
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Expose this class to the scene database
  562. //-----------------------------------------------------------------------------
  563. IMPLEMENT_ELEMENT_FACTORY( DmeGamePortal, CDmeGamePortal );
  564. //-----------------------------------------------------------------------------
  565. // Purpose:
  566. //-----------------------------------------------------------------------------
  567. void CDmeGamePortal::OnConstruction()
  568. {
  569. m_flStaticAmount .Init( this, "staticAmount" );
  570. m_flSecondaryStaticAmount .Init( this, "secondaryStaticAmount" );
  571. m_flOpenAmount .Init( this, "openAmount" );
  572. m_nPortalId .Init( this, "portalId" );
  573. m_nLinkedPortalId .Init( this, "linkedPortalId" );
  574. m_bIsPortal2 .Init( this, "isPortal2" );
  575. }
  576. void CDmeGamePortal::OnDestruction()
  577. {
  578. }