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.

734 lines
19 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "hlfaceposer.h"
  8. #include <mxtk/mx.h>
  9. #include "expressions.h"
  10. #include "expclass.h"
  11. #include "hlfaceposer.h"
  12. #include "StudioModel.h"
  13. #include "FileSystem.h"
  14. #include "FlexPanel.h"
  15. #include "ControlPanel.h"
  16. #include "mxExpressionTray.h"
  17. #include "UtlBuffer.h"
  18. #include "FileSystem.h"
  19. #include "ExpressionTool.h"
  20. #include "faceposer_models.h"
  21. #include "mdlviewer.h"
  22. #include "phonemeconverter.h"
  23. #include "ProgressDialog.h"
  24. #include "tier1/fmtstr.h"
  25. #include "tier1/utlstring.h"
  26. #include "tier1/utlvector.h"
  27. #undef ALIGN16
  28. #undef ALIGN4
  29. #define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3)
  30. #define ALIGN16( a ) a = (byte *)((int)((byte *)a + 15) & ~ 15)
  31. char const *GetGlobalFlexControllerName( int index );
  32. int GetGlobalFlexControllerCount( void );
  33. //-----------------------------------------------------------------------------
  34. // Purpose:
  35. // Input : *classname -
  36. //-----------------------------------------------------------------------------
  37. CExpClass::CExpClass( const char *classname )
  38. {
  39. Q_strncpy( m_szClassName, classname, sizeof( m_szClassName ) );
  40. Q_FileBase( m_szClassName, m_szBaseName, sizeof( m_szBaseName ) );
  41. m_szFileName[ 0 ] = 0;
  42. m_bDirty = false;
  43. m_nSelectedExpression = -1;
  44. m_bIsPhonemeClass = Q_strstr( classname, "phonemes" ) ? true : false;
  45. }
  46. //-----------------------------------------------------------------------------
  47. // Purpose:
  48. //-----------------------------------------------------------------------------
  49. CExpClass::~CExpClass( void )
  50. {
  51. m_Expressions.Purge();
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose:
  55. // Input : *exp -
  56. //-----------------------------------------------------------------------------
  57. int CExpClass::FindExpressionIndex( CExpression *exp )
  58. {
  59. for ( int i = 0 ; i < GetNumExpressions(); i++ )
  60. {
  61. CExpression *e = GetExpression( i );
  62. if ( e == exp )
  63. return i;
  64. }
  65. return -1;
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose:
  69. //-----------------------------------------------------------------------------
  70. void CExpClass::Save( void )
  71. {
  72. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  73. if ( !hdr )
  74. {
  75. return;
  76. }
  77. const char *filename = GetFileName();
  78. if ( !filename || !filename[ 0 ] )
  79. return;
  80. Con_Printf( "Saving changes to %s to file %s\n", GetName(), GetFileName() );
  81. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  82. int i, j;
  83. int numflexmaps = 0;
  84. int flexmap[128]; // maps file local controlls into global controls
  85. CExpression *expr = NULL;
  86. // find all used controllers
  87. int fc = GetGlobalFlexControllerCount();
  88. for ( j = 0; j < fc; ++j )
  89. {
  90. for (i = 0; i < GetNumExpressions(); i++)
  91. {
  92. expr = GetExpression( i );
  93. Assert( expr );
  94. float *settings = expr->GetSettings();
  95. float *weights = expr->GetWeights();
  96. if ( settings[j] != 0 ||
  97. weights[j] != 0 )
  98. {
  99. flexmap[ numflexmaps++ ] = j;
  100. break;
  101. }
  102. }
  103. }
  104. buf.Printf( "$keys" );
  105. for (j = 0; j < numflexmaps; j++)
  106. {
  107. buf.Printf( " %s", GetGlobalFlexControllerName( flexmap[j] ) );
  108. }
  109. buf.Printf( "\n" );
  110. buf.Printf( "$hasweighting\n" );
  111. for (i = 0; i < GetNumExpressions(); i++)
  112. {
  113. expr = GetExpression( i );
  114. buf.Printf( "\"%s\" ", expr->name );
  115. // isalpha returns non zero for ents > 256
  116. if (expr->index <= 'z')
  117. {
  118. buf.Printf( "\"%c\" ", expr->index );
  119. }
  120. else
  121. {
  122. buf.Printf( "\"0x%04x\" ", expr->index );
  123. }
  124. float *settings = expr->GetSettings();
  125. float *weights = expr->GetWeights();
  126. Assert( settings );
  127. Assert( weights );
  128. for (j = 0; j < numflexmaps; j++)
  129. {
  130. buf.Printf( "%.3f %.3f ", settings[flexmap[j]], weights[flexmap[j]] );
  131. }
  132. if ( Q_strstr( expr->name, "Right Side Smile" ) )
  133. {
  134. Con_Printf( "wrote %s with checksum %s\n",
  135. expr->name, expr->GetBitmapCheckSum() );
  136. }
  137. buf.Printf( "\"%s\"\n", expr->description );
  138. }
  139. char relative[ 512 ];
  140. filesystem->FullPathToRelativePath( filename, relative, sizeof( relative ) );
  141. MakeFileWriteable( relative );
  142. FileHandle_t fh = filesystem->Open( relative, "wt" );
  143. if ( !fh )
  144. {
  145. Con_ErrorPrintf( "Unable to write to %s (read-only?)\n", relative );
  146. return;
  147. }
  148. else
  149. {
  150. filesystem->Write( buf.Base(), buf.TellPut(), fh );
  151. filesystem->Close(fh);
  152. }
  153. SetDirty( false );
  154. for (i = 0; i < GetNumExpressions(); i++)
  155. {
  156. expr = GetExpression( i );
  157. if ( expr )
  158. {
  159. expr->ResetUndo();
  160. expr->SetDirty( false );
  161. }
  162. }
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose:
  166. //-----------------------------------------------------------------------------
  167. void CExpClass::Export( void )
  168. {
  169. char vfefilename[ 512 ];
  170. Q_StripExtension( GetFileName(), vfefilename, sizeof( vfefilename ) );
  171. Q_DefaultExtension( vfefilename, ".vfe", sizeof( vfefilename ) );
  172. Con_Printf( "Exporting %s to %s\n", GetName(), vfefilename );
  173. int i, j;
  174. int numflexmaps = 0;
  175. int flexmap[128]; // maps file local controlls into global controls
  176. CExpression *expr = NULL;
  177. // find all used controllers
  178. int fc_count = GetGlobalFlexControllerCount();
  179. for (j = 0; j < fc_count; j++)
  180. {
  181. int k = j;
  182. for (i = 0; i < GetNumExpressions(); i++)
  183. {
  184. expr = GetExpression( i );
  185. Assert( expr );
  186. float *settings = expr->GetSettings();
  187. float *weights = expr->GetWeights();
  188. Assert( settings );
  189. Assert( weights );
  190. if ( settings[k] != 0 || weights[k] != 0 )
  191. {
  192. flexmap[numflexmaps++] = k;
  193. break;
  194. }
  195. }
  196. }
  197. byte *pData = (byte *)calloc( 1024 * 1024, 1 );
  198. byte *pDataStart = pData;
  199. flexsettinghdr_t *fhdr = (flexsettinghdr_t *)pData;
  200. fhdr->id = ('V' << 16) + ('F' << 8) + ('E');
  201. fhdr->version = 0;
  202. V_strncpy( fhdr->name, vfefilename, sizeof( fhdr->name ) );
  203. // allocate room for header
  204. pData += sizeof( flexsettinghdr_t );
  205. ALIGN4( pData );
  206. // store flex settings
  207. flexsetting_t *pSetting = (flexsetting_t *)pData;
  208. fhdr->numflexsettings = GetNumExpressions();
  209. fhdr->flexsettingindex = pData - pDataStart;
  210. pData += sizeof( flexsetting_t ) * fhdr->numflexsettings;
  211. ALIGN4( pData );
  212. for (i = 0; i < fhdr->numflexsettings; i++)
  213. {
  214. expr = GetExpression( i );
  215. Assert( expr );
  216. pSetting[i].index = expr->index;
  217. pSetting[i].settingindex = pData - (byte *)(&pSetting[i]);
  218. flexweight_t *pFlexWeights = (flexweight_t *)pData;
  219. float *settings = expr->GetSettings();
  220. float *weights = expr->GetWeights();
  221. Assert( settings );
  222. Assert( weights );
  223. for (j = 0; j < numflexmaps; j++)
  224. {
  225. if (settings[flexmap[j]] != 0 || weights[flexmap[j]] != 0)
  226. {
  227. pSetting[i].numsettings++;
  228. pFlexWeights->key = j;
  229. pFlexWeights->weight = settings[flexmap[j]];
  230. pFlexWeights->influence = weights[flexmap[j]];
  231. pFlexWeights++;
  232. }
  233. pData = (byte *)pFlexWeights;
  234. ALIGN4( pData );
  235. }
  236. }
  237. // store indexed table
  238. int numindexes = 1;
  239. for (i = 0; i < fhdr->numflexsettings; i++)
  240. {
  241. if (pSetting[i].index >= numindexes)
  242. numindexes = pSetting[i].index + 1;
  243. }
  244. int *pIndex = (int *)pData;
  245. fhdr->numindexes = numindexes;
  246. fhdr->indexindex = pData - pDataStart;
  247. pData += sizeof( int ) * numindexes;
  248. ALIGN4( pData );
  249. for (i = 0; i < numindexes; i++)
  250. {
  251. pIndex[i] = -1;
  252. }
  253. for (i = 0; i < fhdr->numflexsettings; i++)
  254. {
  255. pIndex[pSetting[i].index] = i;
  256. }
  257. // store flex setting names
  258. for (i = 0; i < fhdr->numflexsettings; i++)
  259. {
  260. expr = GetExpression( i );
  261. pSetting[i].nameindex = pData - (byte *)(&pSetting[i]);
  262. strcpy( (char *)pData, expr->name );
  263. pData += strlen( expr->name ) + 1;
  264. }
  265. ALIGN4( pData );
  266. // store key names
  267. char **pKeynames = (char **)pData;
  268. fhdr->numkeys = numflexmaps;
  269. fhdr->keynameindex = pData - pDataStart;
  270. pData += sizeof( char *) * numflexmaps;
  271. for (i = 0; i < numflexmaps; i++)
  272. {
  273. pKeynames[i] = (char *)(pData - pDataStart);
  274. strcpy( (char *)pData, GetGlobalFlexControllerName( flexmap[i] ) );
  275. pData += strlen( GetGlobalFlexControllerName( flexmap[i] ) ) + 1;
  276. }
  277. ALIGN4( pData );
  278. // allocate room for remapping
  279. int *keymapping = (int *)pData;
  280. fhdr->keymappingindex = pData - pDataStart;
  281. pData += sizeof( int ) * numflexmaps;
  282. for (i = 0; i < numflexmaps; i++)
  283. {
  284. keymapping[i] = -1;
  285. }
  286. ALIGN4( pData );
  287. fhdr->length = pData - pDataStart;
  288. char relative[ 512 ];
  289. filesystem->FullPathToRelativePath( vfefilename, relative, sizeof( relative ) );
  290. MakeFileWriteable( relative );
  291. FileHandle_t fh = filesystem->Open( relative, "wb" );
  292. if ( !fh )
  293. {
  294. Con_ErrorPrintf( "Unable to write to %s (read-only?)\n", relative );
  295. return;
  296. }
  297. else
  298. {
  299. filesystem->Write( pDataStart, fhdr->length, fh );
  300. filesystem->Close(fh);
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose:
  305. // Output : const char
  306. //-----------------------------------------------------------------------------
  307. const char *CExpClass::GetBaseName( void ) const
  308. {
  309. return m_szBaseName;
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose:
  313. // Output : const char
  314. //-----------------------------------------------------------------------------
  315. const char *CExpClass::GetName( void ) const
  316. {
  317. return m_szClassName;
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose:
  321. // Output : const char
  322. //-----------------------------------------------------------------------------
  323. const char *CExpClass::GetFileName( void ) const
  324. {
  325. return m_szFileName;
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Purpose:
  329. // Input : *filename -
  330. //-----------------------------------------------------------------------------
  331. void CExpClass::SetFileName( const char *filename )
  332. {
  333. strcpy( m_szFileName, filename );
  334. }
  335. bool IsUsingPerPlayerExpressions();
  336. void CExpClass::ReloadBitmaps( void )
  337. {
  338. bool bUsingPerPlayerOverrides = IsUsingPerPlayerExpressions();
  339. int c = models->Count();
  340. for ( int model = 0; model < MAX_FP_MODELS; model++ )
  341. {
  342. // Only reload bitmaps for current model index
  343. if ( bUsingPerPlayerOverrides && model != models->GetActiveModelIndex() )
  344. continue;
  345. models->ForceActiveModelIndex( model );
  346. for ( int i = 0 ; i < GetNumExpressions(); i++ )
  347. {
  348. CExpression *e = GetExpression( i );
  349. if ( !e )
  350. continue;
  351. if ( e->m_Bitmap[ model ].valid )
  352. {
  353. DeleteObject( e->m_Bitmap[ model ].image );
  354. e->m_Bitmap[ model ].valid = false;
  355. }
  356. if ( model >= c )
  357. continue;
  358. if ( !LoadBitmapFromFile( e->GetBitmapFilename( model ), e->m_Bitmap[ model ] ) )
  359. {
  360. e->CreateNewBitmap( model );
  361. }
  362. }
  363. }
  364. models->UnForceActiveModelIndex();
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose:
  368. // Input : *name -
  369. // *description -
  370. // *flexsettings -
  371. // selectnewitem -
  372. // Output : CExpression
  373. //-----------------------------------------------------------------------------
  374. CExpression *CExpClass::AddExpression( const char *name, const char *description, float *flexsettings, float *flexweights, bool selectnewitem, bool bDirtyClass )
  375. {
  376. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  377. if ( !hdr )
  378. return NULL;
  379. CExpression *exp = FindExpression( name );
  380. if ( exp )
  381. {
  382. Con_ErrorPrintf( "Can't create, an expression with the name '%s' already exists.\n", name );
  383. return NULL;
  384. }
  385. // Add to end of list
  386. int idx = m_Expressions.AddToTail();
  387. exp = &m_Expressions[ idx ];
  388. float *settings = exp->GetSettings();
  389. float *weights = exp->GetWeights();
  390. Assert( settings );
  391. Assert( weights );
  392. exp->SetExpressionClass( GetName() );
  393. strcpy( exp->name, name );
  394. strcpy( exp->description, description );
  395. memcpy( settings, flexsettings, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
  396. memcpy( weights, flexweights, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
  397. exp->index = '_';
  398. if ( IsPhonemeClass() )
  399. {
  400. exp->index = TextToPhoneme( name );
  401. }
  402. exp->m_Bitmap[ models->GetActiveModelIndex() ].valid = false;
  403. if ( !LoadBitmapFromFile( exp->GetBitmapFilename( models->GetActiveModelIndex() ), exp->m_Bitmap[ models->GetActiveModelIndex() ] ) )
  404. {
  405. exp->CreateNewBitmap( models->GetActiveModelIndex() );
  406. }
  407. if ( selectnewitem )
  408. {
  409. SelectExpression( idx );
  410. }
  411. if ( bDirtyClass )
  412. {
  413. SetDirty( true );
  414. }
  415. return exp;
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Purpose:
  419. // Input : *name -
  420. // Output : CExpression
  421. //-----------------------------------------------------------------------------
  422. CExpression *CExpClass::FindExpression( const char *name )
  423. {
  424. for ( int i = 0 ; i < m_Expressions.Count(); i++ )
  425. {
  426. CExpression *exp = &m_Expressions[ i ];
  427. if ( !stricmp( exp->name, name ) )
  428. {
  429. return exp;
  430. }
  431. }
  432. return NULL;
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Purpose:
  436. // Input : *name -
  437. //-----------------------------------------------------------------------------
  438. void CExpClass::DeleteExpression( const char *name )
  439. {
  440. for ( int i = 0 ; i < m_Expressions.Count(); i++ )
  441. {
  442. CExpression *exp = &m_Expressions[ i ];
  443. if ( !stricmp( exp->name, name ) )
  444. {
  445. SetDirty( true );
  446. m_Expressions.Remove( i );
  447. return;
  448. }
  449. }
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose:
  453. // Output : int
  454. //-----------------------------------------------------------------------------
  455. int CExpClass::GetNumExpressions( void )
  456. {
  457. return m_Expressions.Count();
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose:
  461. // Input : num -
  462. // Output : CExpression
  463. //-----------------------------------------------------------------------------
  464. CExpression *CExpClass::GetExpression( int num )
  465. {
  466. if ( num < 0 || num >= m_Expressions.Count() )
  467. {
  468. return NULL;
  469. }
  470. CExpression *exp = &m_Expressions[ num ];
  471. return exp;
  472. }
  473. //-----------------------------------------------------------------------------
  474. // Purpose:
  475. // Output : Returns true on success, false on failure.
  476. //-----------------------------------------------------------------------------
  477. bool CExpClass::GetDirty( void )
  478. {
  479. return m_bDirty;
  480. }
  481. //-----------------------------------------------------------------------------
  482. // Purpose:
  483. // Input : dirty -
  484. //-----------------------------------------------------------------------------
  485. void CExpClass::SetDirty( bool dirty )
  486. {
  487. m_bDirty = dirty;
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose:
  491. // Output : int
  492. //-----------------------------------------------------------------------------
  493. int CExpClass::GetIndex( void )
  494. {
  495. for ( int i = 0; i < expressions->GetNumClasses(); i++ )
  496. {
  497. CExpClass *cl = expressions->GetClass( i );
  498. if ( cl == this )
  499. return i;
  500. }
  501. return -1;
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose:
  505. // Input : num -
  506. //-----------------------------------------------------------------------------
  507. void CExpClass::SelectExpression( int num, bool deselect )
  508. {
  509. m_nSelectedExpression = num;
  510. g_pFlexPanel->setExpression( num );
  511. g_pExpressionTrayTool->Select( num, deselect );
  512. g_pExpressionTrayTool->redraw();
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose:
  516. // Output : int
  517. //-----------------------------------------------------------------------------
  518. int CExpClass::GetSelectedExpression( void )
  519. {
  520. return m_nSelectedExpression;
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. //-----------------------------------------------------------------------------
  525. void CExpClass::DeselectExpression( void )
  526. {
  527. m_nSelectedExpression = -1;
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose:
  531. // Input : exp1 -
  532. // exp2 -
  533. //-----------------------------------------------------------------------------
  534. void CExpClass::SwapExpressionOrder( int exp1, int exp2 )
  535. {
  536. CExpression temp1 = m_Expressions[ exp1 ];
  537. CExpression temp2 = m_Expressions[ exp2 ];
  538. m_Expressions.Remove( exp1 );
  539. m_Expressions.InsertBefore( exp1, temp2 );
  540. m_Expressions.Remove( exp2 );
  541. m_Expressions.InsertBefore( exp2, temp1 );
  542. }
  543. void CExpClass::BuildValidChecksums( CUtlRBTree< CRC32_t > &tree )
  544. {
  545. for ( int i = 0; i < m_Expressions.Count(); i++ )
  546. {
  547. CExpression *exp = &m_Expressions[ i ];
  548. if ( !exp )
  549. continue;
  550. CRC32_t crc = exp->GetBitmapCRC();
  551. tree.Insert( crc );
  552. }
  553. }
  554. //-----------------------------------------------------------------------------
  555. // Purpose: After a class is loaded, check the class directory and delete any bmp files that aren't
  556. // still referenced
  557. //-----------------------------------------------------------------------------
  558. void CExpClass::CheckBitmapConsistency( void )
  559. {
  560. char path[ 512 ];
  561. Q_snprintf( path, sizeof( path ), "expressions/%s/%s/*.bmp", models->GetActiveModelName(), GetBaseName() );
  562. Q_FixSlashes( path );
  563. Q_strlower( path );
  564. g_pProgressDialog->Start( CFmtStr( "%s / %s - Reconcile Expression Thumbnails", models->GetActiveModelName(), GetBaseName() ), "", true );
  565. CUtlVector< CUtlString > workList;
  566. FileFindHandle_t hFindFile;
  567. char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile );
  568. if ( fn )
  569. {
  570. while ( fn )
  571. {
  572. // Don't do anything with directories
  573. if ( !filesystem->FindIsDirectory( hFindFile ) )
  574. {
  575. CUtlString s = fn;
  576. workList.AddToTail( s );
  577. }
  578. fn = filesystem->FindNext( hFindFile );
  579. }
  580. filesystem->FindClose( hFindFile );
  581. }
  582. CUtlRBTree< CRC32_t > tree( 0, 0, DefLessFunc( CRC32_t ) );
  583. BuildValidChecksums( tree );
  584. for ( int i = 0 ; i < workList.Count(); ++i )
  585. {
  586. char testname[ 256 ];
  587. Q_StripExtension( workList[ i ].String(), testname, sizeof( testname ) );
  588. g_pProgressDialog->UpdateText( "%s", testname );
  589. g_pProgressDialog->Update( (float)i / (float)workList.Count() );
  590. CRC32_t check;
  591. Q_hextobinary( testname, Q_strlen( testname ), (byte *)&check, sizeof( check ) );
  592. if ( tree.Find( check ) == tree.InvalidIndex() )
  593. {
  594. char kill[ 512 ];
  595. Q_snprintf( kill, sizeof( kill ), "expressions/%s/%s/%s", models->GetActiveModelName(), GetBaseName(), fn );
  596. Q_FixSlashes( kill );
  597. Q_strlower( kill );
  598. // Delete it
  599. Con_ErrorPrintf( "Removing unused bitmap file '%s'\n", kill );
  600. filesystem->RemoveFile( kill, "MOD" );
  601. }
  602. if ( g_pProgressDialog->IsCancelled() )
  603. {
  604. Msg( "Cancelled\n" );
  605. break;
  606. }
  607. }
  608. g_pProgressDialog->Finish();
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose: Does this class have expression indices based on phoneme lookups
  612. // Output : Returns true on success, false on failure.
  613. //-----------------------------------------------------------------------------
  614. bool CExpClass::IsPhonemeClass( void ) const
  615. {
  616. return m_bIsPhonemeClass;
  617. }