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.

691 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "hlfaceposer.h"
  8. #include "expressions.h"
  9. #include <mxtk/mx.h>
  10. #include "ControlPanel.h"
  11. #include "StudioModel.h"
  12. #include "expclass.h"
  13. #include "mxExpressionTab.h"
  14. #include "mxExpressionTray.h"
  15. #include "filesystem.h"
  16. #include "faceposer_models.h"
  17. #include "utldict.h"
  18. #include "scriplib.h"
  19. #include "checksum_crc.h"
  20. bool Sys_Error(const char *pMsg, ...);
  21. extern char g_appTitle[];
  22. static CUtlVector< CUtlSymbol > g_GlobalFlexControllers;
  23. static CUtlDict< int, int > g_GlobalFlexControllerLookup;
  24. void ChecksumFlexControllers( bool bSpew, char const *name, CRC32_t &crc, const float *settings, const float *weights )
  25. {
  26. CRC32_Init( &crc );
  27. // Walk them alphabetically so that load order doesn't matter
  28. for ( int i = g_GlobalFlexControllerLookup.First() ;
  29. i != g_GlobalFlexControllerLookup.InvalidIndex();
  30. i = g_GlobalFlexControllerLookup.Next( i ) )
  31. {
  32. int controllerIndex = g_GlobalFlexControllerLookup[ i ];
  33. char const *pszName = g_GlobalFlexControllerLookup.GetElementName( i );
  34. // Only count active controllers in checksum
  35. float s = settings[ controllerIndex ];
  36. float w = weights[ controllerIndex ];
  37. if ( s == 0.0f && w == 0.0f )
  38. {
  39. continue;
  40. }
  41. CRC32_ProcessBuffer( &crc, (void *)pszName, Q_strlen( pszName ) );
  42. CRC32_ProcessBuffer( &crc, (void *)&s, sizeof( s ) );
  43. CRC32_ProcessBuffer( &crc, (void *)&w, sizeof( w ) );
  44. if ( bSpew )
  45. {
  46. Msg( "[%d] %s == %f %f\n", controllerIndex, pszName, s, w );
  47. }
  48. }
  49. CRC32_Final( &crc );
  50. if ( bSpew )
  51. {
  52. char hex[ 17 ];
  53. Q_binarytohex( (const byte *)&crc, sizeof( crc ), hex, sizeof( hex ) );
  54. Msg( "%s checksum = %sf\n", name, hex );
  55. }
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. // Input : index -
  60. // Output : char const
  61. //-----------------------------------------------------------------------------
  62. char const *GetGlobalFlexControllerName( int index )
  63. {
  64. return g_GlobalFlexControllers[ index ].String();
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. // Output : int
  69. //-----------------------------------------------------------------------------
  70. int GetGlobalFlexControllerCount( void )
  71. {
  72. return g_GlobalFlexControllers.Count();
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose: Accumulates throughout runtime session, oh well
  76. // Input : *szName -
  77. // Output : int
  78. //-----------------------------------------------------------------------------
  79. int AddGlobalFlexController( StudioModel *model, const char *szName )
  80. {
  81. int idx = g_GlobalFlexControllerLookup.Find( szName );
  82. if ( idx != g_GlobalFlexControllerLookup.InvalidIndex() )
  83. {
  84. return g_GlobalFlexControllerLookup[ idx ];
  85. }
  86. CUtlSymbol sym;
  87. sym = szName;
  88. idx = g_GlobalFlexControllers.AddToTail( sym );
  89. g_GlobalFlexControllerLookup.Insert( szName, idx );
  90. // Con_Printf( "Added global flex controller %i %s from %s\n", idx, szName, model->GetStudioHdr()->name );
  91. return idx;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. // Input : *model -
  96. //-----------------------------------------------------------------------------
  97. void SetupModelFlexcontrollerLinks( StudioModel *model )
  98. {
  99. if ( !model )
  100. return;
  101. CStudioHdr *hdr = model->GetStudioHdr();
  102. if ( !hdr )
  103. return;
  104. if ( hdr->numflexcontrollers() <= 0 )
  105. return;
  106. // Already set up!!!
  107. if ( hdr->pFlexcontroller( LocalFlexController_t(0) )->localToGlobal != -1 )
  108. return;
  109. for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  110. {
  111. int j = AddGlobalFlexController( model, hdr->pFlexcontroller( i )->pszName() );
  112. hdr->pFlexcontroller( i )->localToGlobal = j;
  113. model->SetFlexController( i, 0.0f );
  114. }
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose:
  118. //-----------------------------------------------------------------------------
  119. class CExpressionManager : public IExpressionManager
  120. {
  121. public:
  122. CExpressionManager( void );
  123. ~CExpressionManager( void );
  124. void Reset( void );
  125. void ActivateExpressionClass( CExpClass *cl );
  126. // File I/O
  127. void LoadClass( const char *filename );
  128. void CreateNewClass( const char *filename );
  129. bool CloseClass( CExpClass *cl );
  130. CExpClass *AddCExpClass( const char *classname, const char *filename );
  131. int GetNumClasses( void );
  132. CExpression *GetCopyBuffer( void );
  133. bool CanClose( void );
  134. CExpClass *GetActiveClass( void );
  135. CExpClass *GetClass( int num );
  136. CExpClass *FindClass( const char *classname, bool bMatchBaseNameOnly );
  137. private:
  138. // Methods
  139. const char *GetClassnameFromFilename( const char *filename );
  140. // UI
  141. void PopulateClassCB( CExpClass *cl );
  142. void RemoveCExpClass( CExpClass *cl );
  143. private:
  144. // Data
  145. CExpClass *m_pActiveClass;
  146. CUtlVector < CExpClass * > m_Classes;
  147. CExpression m_CopyBuffer;
  148. };
  149. // Expose interface
  150. static CExpressionManager g_ExpressionManager;
  151. IExpressionManager *expressions = &g_ExpressionManager;
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. CExpressionManager::CExpressionManager( void )
  156. {
  157. m_pActiveClass = NULL;
  158. Reset();
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose:
  162. //-----------------------------------------------------------------------------
  163. CExpressionManager::~CExpressionManager( void )
  164. {
  165. Reset();
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. //-----------------------------------------------------------------------------
  170. void CExpressionManager::Reset( void )
  171. {
  172. while ( m_Classes.Size() > 0 )
  173. {
  174. CExpClass *p = m_Classes[ 0 ];
  175. m_Classes.Remove( 0 );
  176. delete p;
  177. }
  178. m_pActiveClass = NULL;
  179. memset( &m_CopyBuffer, 0, sizeof( m_CopyBuffer ) );
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. CExpClass *CExpressionManager::GetActiveClass( void )
  185. {
  186. return m_pActiveClass;
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose:
  190. // Input : num -
  191. // Output : CExpClass
  192. //-----------------------------------------------------------------------------
  193. CExpClass *CExpressionManager::GetClass( int num )
  194. {
  195. return m_Classes[ num ];
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. // Input : *classname -
  200. // *filename -
  201. // Output : CExpClass *
  202. //-----------------------------------------------------------------------------
  203. CExpClass * CExpressionManager::AddCExpClass( const char *classname, const char *filename )
  204. {
  205. Assert( !FindClass( classname, false ) );
  206. CExpClass *pclass = new CExpClass( classname );
  207. if ( !pclass )
  208. return NULL;
  209. m_Classes.AddToTail( pclass );
  210. pclass->SetFileName( filename );
  211. return pclass;
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. // Input : *cl -
  216. //-----------------------------------------------------------------------------
  217. void CExpressionManager::RemoveCExpClass( CExpClass *cl )
  218. {
  219. for ( int i = 0; i < m_Classes.Size(); i++ )
  220. {
  221. CExpClass *p = m_Classes[ i ];
  222. if ( p == cl )
  223. {
  224. m_Classes.Remove( i );
  225. delete p;
  226. break;
  227. }
  228. }
  229. if ( m_Classes.Size() >= 1 )
  230. {
  231. ActivateExpressionClass( m_Classes[ 0 ] );
  232. }
  233. else
  234. {
  235. ActivateExpressionClass( NULL );
  236. }
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. // Input : *cl -
  241. //-----------------------------------------------------------------------------
  242. void CExpressionManager::ActivateExpressionClass( CExpClass *cl )
  243. {
  244. m_pActiveClass = cl;
  245. int select = 0;
  246. for ( int i = 0; i < GetNumClasses(); i++ )
  247. {
  248. CExpClass *c = GetClass( i );
  249. if ( cl == c )
  250. {
  251. select = i;
  252. break;
  253. }
  254. }
  255. g_pExpressionClass->select( select );
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose:
  259. // Output : int
  260. //-----------------------------------------------------------------------------
  261. int CExpressionManager::GetNumClasses( void )
  262. {
  263. return m_Classes.Size();
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose:
  267. // Input : *classname -
  268. // Output : CExpClass
  269. //-----------------------------------------------------------------------------
  270. CExpClass *CExpressionManager::FindClass( const char *classname, bool bMatchBaseNameOnly )
  271. {
  272. char search[ 256 ];
  273. if ( bMatchBaseNameOnly )
  274. {
  275. Q_FileBase( classname, search, sizeof( search ) );
  276. }
  277. else
  278. {
  279. Q_strncpy( search, classname, sizeof( search ) );
  280. }
  281. Q_FixSlashes( search );
  282. Q_strlower( search );
  283. for ( int i = 0; i < m_Classes.Size(); i++ )
  284. {
  285. CExpClass *cl = m_Classes[ i ];
  286. if ( !Q_stricmp( search, bMatchBaseNameOnly ? cl->GetBaseName() : cl->GetName() ) )
  287. {
  288. return cl;
  289. }
  290. }
  291. return NULL;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. // Input : *filename -
  296. // Output : const char
  297. //-----------------------------------------------------------------------------
  298. const char *CExpressionManager::GetClassnameFromFilename( const char *filename )
  299. {
  300. char cleanname[ 256 ];
  301. static char classname[ 256 ];
  302. classname[ 0 ] = 0;
  303. Assert( filename && filename[ 0 ] );
  304. // Strip the .txt
  305. Q_StripExtension( filename, cleanname, sizeof( cleanname ) );
  306. char *p = Q_stristr( cleanname, "expressions" );
  307. if ( p )
  308. {
  309. Q_strncpy( classname, p + Q_strlen( "expressions" ) + 1, sizeof( classname ) );
  310. }
  311. else
  312. {
  313. Assert( 0 );
  314. Q_strncpy( classname, cleanname, sizeof( classname ) );
  315. }
  316. Q_FixSlashes( classname );
  317. Q_strlower( classname );
  318. return classname;
  319. };
  320. //-----------------------------------------------------------------------------
  321. // Purpose:
  322. // Output : CExpression
  323. //-----------------------------------------------------------------------------
  324. CExpression *CExpressionManager::GetCopyBuffer( void )
  325. {
  326. return &m_CopyBuffer;
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose:
  330. // Output : Returns true on success, false on failure.
  331. //-----------------------------------------------------------------------------
  332. bool CExpressionManager::CanClose( void )
  333. {
  334. for ( int i = 0; i < m_Classes.Size(); i++ )
  335. {
  336. CExpClass *pclass = m_Classes[ i ];
  337. if ( pclass->GetDirty() )
  338. {
  339. return false;
  340. }
  341. }
  342. return true;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose:
  346. // Input : *filename -
  347. //-----------------------------------------------------------------------------
  348. void CExpressionManager::LoadClass( const char *inpath )
  349. {
  350. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  351. if ( inpath[ 0 ] == '/' || inpath[ 0 ] == '\\' )
  352. ++inpath;
  353. char filename[ 512 ];
  354. Q_strncpy( filename, inpath, sizeof( filename ) );
  355. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  356. if ( !hdr )
  357. {
  358. Con_ErrorPrintf( "Can't load expressions from %s, must load a .mdl file first!\n",
  359. filename );
  360. return;
  361. }
  362. Con_Printf( "Loading expressions from %s\n", filename );
  363. const char *classname = GetClassnameFromFilename( filename );
  364. // Already loaded, don't do anything
  365. if ( FindClass( classname, false ) )
  366. return;
  367. // Import actual data
  368. LoadScriptFile( filename, SCRIPT_USE_RELATIVE_PATH );
  369. CExpClass *active = AddCExpClass( classname, filename );
  370. if ( !active )
  371. return;
  372. ActivateExpressionClass( active );
  373. int numflexmaps = 0;
  374. int flexmap[128]; // maps file local controls into global controls
  375. LocalFlexController_t localflexmap[128]; // maps file local controls into local controls
  376. bool bHasWeighting = false;
  377. bool bNormalized = false;
  378. EnableStickySnapshotMode( );
  379. while (1)
  380. {
  381. GetToken (true);
  382. if (endofscript)
  383. break;
  384. if (stricmp( token, "$keys" ) == 0)
  385. {
  386. numflexmaps = 0;
  387. while (TokenAvailable())
  388. {
  389. flexmap[numflexmaps] = -1;
  390. localflexmap[numflexmaps] = LocalFlexController_t(-1);
  391. GetToken( false );
  392. bool bFound = false;
  393. for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  394. {
  395. if (stricmp( hdr->pFlexcontroller(i)->pszName(), token ) == 0)
  396. {
  397. localflexmap[numflexmaps] = i;
  398. flexmap[numflexmaps] = AddGlobalFlexController( models->GetActiveStudioModel(),
  399. hdr->pFlexcontroller(i)->pszName() );
  400. bFound = true;
  401. break;
  402. }
  403. }
  404. if ( !bFound )
  405. {
  406. flexmap[ numflexmaps ] = AddGlobalFlexController( models->GetActiveStudioModel(), token );
  407. }
  408. numflexmaps++;
  409. }
  410. }
  411. else if ( !stricmp( token, "$hasweighting" ) )
  412. {
  413. bHasWeighting = true;
  414. }
  415. else if ( !stricmp( token, "$normalized" ) )
  416. {
  417. bNormalized = true;
  418. }
  419. else
  420. {
  421. float setting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
  422. float weight[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
  423. char name[ 256 ];
  424. char desc[ 256 ];
  425. int index;
  426. memset( setting, 0, sizeof( setting ) );
  427. memset( weight, 0, sizeof( weight ) );
  428. strcpy( name, token );
  429. // phoneme index
  430. GetToken( false );
  431. if (token[1] == 'x')
  432. {
  433. sscanf( &token[2], "%x", &index );
  434. }
  435. else
  436. {
  437. index = (int)token[0];
  438. }
  439. // key values
  440. for (int i = 0; i < numflexmaps; i++)
  441. {
  442. if (flexmap[i] > -1)
  443. {
  444. GetToken( false );
  445. setting[flexmap[i]] = atof( token );
  446. if (bHasWeighting)
  447. {
  448. GetToken( false );
  449. weight[flexmap[i]] = atof( token );
  450. }
  451. else
  452. {
  453. weight[flexmap[i]] = 1.0;
  454. }
  455. if ( bNormalized && localflexmap[ i ] > -1 )
  456. {
  457. mstudioflexcontroller_t *pFlex = hdr->pFlexcontroller( localflexmap[i] );
  458. if ( pFlex->min != pFlex->max )
  459. {
  460. setting[flexmap[i]] = Lerp( setting[flexmap[i]], pFlex->min, pFlex->max );
  461. }
  462. }
  463. }
  464. else
  465. {
  466. GetToken( false );
  467. if (bHasWeighting)
  468. {
  469. GetToken( false );
  470. }
  471. }
  472. }
  473. // description
  474. GetToken( false );
  475. strcpy( desc, token );
  476. CExpression *exp = active->AddExpression( name, desc, setting, weight, false, false );
  477. if ( active->IsPhonemeClass() && exp )
  478. {
  479. if ( exp->index != index )
  480. {
  481. Con_Printf( "CExpressionManager::LoadClass (%s): phoneme index for %s in .txt file is wrong (expecting %i got %i), ignoring...\n",
  482. classname, name, exp->index, index );
  483. }
  484. }
  485. }
  486. }
  487. active->CheckBitmapConsistency();
  488. DisableStickySnapshotMode( );
  489. PopulateClassCB( active );
  490. active->DeselectExpression();
  491. Assert( !active->GetDirty() );
  492. active->SetDirty( false );
  493. }
  494. //-----------------------------------------------------------------------------
  495. // Purpose:
  496. // Input : *filename -
  497. //-----------------------------------------------------------------------------
  498. void CExpressionManager::CreateNewClass( const char *filename )
  499. {
  500. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  501. if ( !hdr )
  502. {
  503. Con_ErrorPrintf( "Can't create new expression file %s, must load a .mdl file first!\n", filename );
  504. return;
  505. }
  506. // Tell the use that the filename was loaded, expressions are empty for now
  507. const char *classname = GetClassnameFromFilename( filename );
  508. // Already loaded, don't do anything
  509. if ( FindClass( classname, false ) )
  510. return;
  511. Con_Printf( "Creating %s\n", filename );
  512. CExpClass *active = AddCExpClass( classname, filename );
  513. if ( !active )
  514. return;
  515. ActivateExpressionClass( active );
  516. // Select the newly created class
  517. PopulateClassCB( active );
  518. // Select first expression
  519. active->SelectExpression( 0 );
  520. // Nothing has changed so far
  521. active->SetDirty( false );
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose:
  525. // Input : *cl -
  526. // Output : Returns true on success, false on failure.
  527. //-----------------------------------------------------------------------------
  528. bool CExpressionManager::CloseClass( CExpClass *cl )
  529. {
  530. if ( !cl )
  531. return true;
  532. if ( cl->GetDirty() )
  533. {
  534. int retval = mxMessageBox( NULL, va( "Save changes to class '%s'?", cl->GetName() ), g_appTitle, MX_MB_YESNOCANCEL );
  535. if ( retval == 2 )
  536. {
  537. return false;
  538. }
  539. if ( retval == 0 )
  540. {
  541. Con_Printf( "Saving changes to %s : %s\n", cl->GetName(), cl->GetFileName() );
  542. cl->Save();
  543. }
  544. }
  545. // The memory can be freed here, so be more careful
  546. char temp[ 256 ];
  547. V_strcpy_safe( temp, cl->GetName() );
  548. RemoveCExpClass( cl );
  549. Con_Printf( "Closed expression class %s\n", temp );
  550. CExpClass *active = GetActiveClass();
  551. if ( !active )
  552. {
  553. PopulateClassCB( NULL );
  554. g_pExpressionTrayTool->redraw();
  555. return true;
  556. }
  557. // Select the first remaining class
  558. PopulateClassCB( active );
  559. // Select first expression
  560. active->DeselectExpression();
  561. return true;
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose:
  565. // Input : classnum -
  566. //-----------------------------------------------------------------------------
  567. void CExpressionManager::PopulateClassCB( CExpClass *current )
  568. {
  569. g_pExpressionClass->removeAll();
  570. int select = 0;
  571. for ( int i = 0; i < GetNumClasses(); i++ )
  572. {
  573. CExpClass *cl = GetClass( i );
  574. if ( !cl )
  575. continue;
  576. g_pExpressionClass->add( cl->GetName() );
  577. if ( cl == current )
  578. {
  579. select = i;
  580. }
  581. }
  582. g_pExpressionClass->select( select );
  583. }