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.

2200 lines
62 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "filesystem.h"
  9. #include "sentence.h"
  10. #include "hud_closecaption.h"
  11. #include "engine/ivmodelinfo.h"
  12. #include "engine/ivdebugoverlay.h"
  13. #include "bone_setup.h"
  14. #include "soundinfo.h"
  15. #include "tools/bonelist.h"
  16. #include "keyvalues.h"
  17. #include "tier0/vprof.h"
  18. #include "toolframework/itoolframework.h"
  19. #include "choreoevent.h"
  20. #include "choreoscene.h"
  21. #include "choreoactor.h"
  22. #include "toolframework_client.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. bool UseHWMorphVCDs();
  26. ConVar g_CV_PhonemeDelay("phonemedelay", "0", 0, "Phoneme delay to account for sound system latency." );
  27. ConVar g_CV_PhonemeFilter("phonemefilter", "0.08", 0, "Time duration of box filter to pass over phonemes." );
  28. ConVar g_CV_FlexRules("flex_rules", "1", 0, "Allow flex animation rules to run." );
  29. ConVar g_CV_BlinkDuration("blink_duration", "0.2", 0, "How many seconds an eye blink will last." );
  30. ConVar g_CV_FlexSmooth("flex_smooth", "1", 0, "Applies smoothing/decay curve to flex animation controller changes." );
  31. #if defined( CBaseFlex )
  32. #undef CBaseFlex
  33. #endif
  34. static int g_iFlexCounter = 0;
  35. IMPLEMENT_CLIENTCLASS_DT(C_BaseFlex, DT_BaseFlex, CBaseFlex)
  36. RecvPropArray3( RECVINFO_ARRAY(m_flexWeight), RecvPropFloat(RECVINFO(m_flexWeight[0]))),
  37. RecvPropInt(RECVINFO(m_blinktoggle)),
  38. RecvPropVector(RECVINFO(m_viewtarget)),
  39. #ifdef HL2_CLIENT_DLL
  40. RecvPropFloat( RECVINFO(m_vecViewOffset[0]) ),
  41. RecvPropFloat( RECVINFO(m_vecViewOffset[1]) ),
  42. RecvPropFloat( RECVINFO(m_vecViewOffset[2]) ),
  43. RecvPropVector(RECVINFO(m_vecLean)),
  44. RecvPropVector(RECVINFO(m_vecShift)),
  45. #endif
  46. END_RECV_TABLE()
  47. BEGIN_PREDICTION_DATA( C_BaseFlex )
  48. /*
  49. // DEFINE_FIELD( C_BaseFlex, m_viewtarget, FIELD_VECTOR ),
  50. // DEFINE_ARRAY( C_BaseFlex, m_flexWeight, FIELD_FLOAT, 64 ),
  51. // DEFINE_FIELD( C_BaseFlex, m_blinktoggle, FIELD_INTEGER ),
  52. // DEFINE_FIELD( C_BaseFlex, m_blinktime, FIELD_FLOAT ),
  53. // DEFINE_FIELD( C_BaseFlex, m_prevviewtarget, FIELD_VECTOR ),
  54. // DEFINE_ARRAY( C_BaseFlex, m_prevflexWeight, FIELD_FLOAT, 64 ),
  55. // DEFINE_FIELD( C_BaseFlex, m_prevblinktoggle, FIELD_INTEGER ),
  56. // DEFINE_FIELD( C_BaseFlex, m_iBlink, FIELD_INTEGER ),
  57. // DEFINE_FIELD( C_BaseFlex, m_iEyeUpdown, FIELD_INTEGER ),
  58. // DEFINE_FIELD( C_BaseFlex, m_iEyeRightleft, FIELD_INTEGER ),
  59. // DEFINE_FIELD( C_BaseFlex, m_FileList, CUtlVector < CFlexSceneFile * > ),
  60. */
  61. END_PREDICTION_DATA()
  62. //-----------------------------------------------------------------------------
  63. // Purpose:
  64. //-----------------------------------------------------------------------------
  65. bool GetHWMExpressionFileName( const char *pFilename, char *pHWMFilename )
  66. {
  67. // Are we even using hardware morph?
  68. if ( !UseHWMorphVCDs() )
  69. return false;
  70. // Do we have a valid filename?
  71. if ( !( pFilename && pFilename[0] ) )
  72. return false;
  73. // Check to see if we already have an player/hwm/* filename.
  74. if ( ( V_strstr( pFilename, "player/hwm" ) != NULL ) || ( V_strstr( pFilename, "player\\hwm" ) != NULL ) )
  75. {
  76. V_strcpy( pHWMFilename, pFilename );
  77. return true;
  78. }
  79. // Find the hardware morph scene name and pass that along as well.
  80. char szExpression[MAX_PATH];
  81. V_strcpy( szExpression, pFilename );
  82. char szExpressionHWM[MAX_PATH];
  83. szExpressionHWM[0] = '\0';
  84. char *pszToken = strtok( szExpression, "/\\" );
  85. while ( pszToken != NULL )
  86. {
  87. V_strcat( szExpressionHWM, pszToken, sizeof( szExpressionHWM ) );
  88. if ( !V_stricmp( pszToken, "player" ) )
  89. {
  90. V_strcat( szExpressionHWM, "\\hwm", sizeof( szExpressionHWM ) );
  91. }
  92. pszToken = strtok( NULL, "/\\" );
  93. if ( pszToken != NULL )
  94. {
  95. V_strcat( szExpressionHWM, "\\", sizeof( szExpressionHWM ) );
  96. }
  97. }
  98. V_strcpy( pHWMFilename, szExpressionHWM );
  99. return true;
  100. }
  101. C_BaseFlex::C_BaseFlex() :
  102. m_iv_viewtarget( "C_BaseFlex::m_iv_viewtarget" ),
  103. m_iv_flexWeight("C_BaseFlex:m_iv_flexWeight" ),
  104. #ifdef HL2_CLIENT_DLL
  105. m_iv_vecLean("C_BaseFlex:m_iv_vecLean" ),
  106. m_iv_vecShift("C_BaseFlex:m_iv_vecShift" ),
  107. #endif
  108. m_LocalToGlobal( 0, 0, FlexSettingLessFunc )
  109. {
  110. #ifdef _DEBUG
  111. ((Vector&)m_viewtarget).Init();
  112. #endif
  113. AddVar( &m_viewtarget, &m_iv_viewtarget, LATCH_ANIMATION_VAR | INTERPOLATE_LINEAR_ONLY );
  114. AddVar( m_flexWeight, &m_iv_flexWeight, LATCH_ANIMATION_VAR );
  115. // Fill in phoneme class lookup
  116. SetupMappings( "phonemes" );
  117. m_flFlexDelayedWeight = NULL;
  118. m_iMostRecentFlexCounter = 0xFFFFFFFF;
  119. /// Make sure size is correct
  120. Assert( PHONEME_CLASS_STRONG + 1 == NUM_PHONEME_CLASSES );
  121. #ifdef HL2_CLIENT_DLL
  122. // Get general lean vector
  123. AddVar( &m_vecLean, &m_iv_vecLean, LATCH_ANIMATION_VAR );
  124. AddVar( &m_vecShift, &m_iv_vecShift, LATCH_ANIMATION_VAR );
  125. #endif
  126. }
  127. C_BaseFlex::~C_BaseFlex()
  128. {
  129. if ( m_flFlexDelayedWeight )
  130. {
  131. delete[] m_flFlexDelayedWeight;
  132. m_flFlexDelayedWeight = NULL;
  133. }
  134. m_SceneEvents.RemoveAll();
  135. m_LocalToGlobal.RemoveAll();
  136. }
  137. void C_BaseFlex::Spawn()
  138. {
  139. BaseClass::Spawn();
  140. InitPhonemeMappings();
  141. }
  142. // TF Player overrides all of these with class specific files
  143. void C_BaseFlex::InitPhonemeMappings()
  144. {
  145. SetupMappings( "phonemes" );
  146. }
  147. void C_BaseFlex::SetupMappings( char const *pchFileRoot )
  148. {
  149. // Fill in phoneme class lookup
  150. memset( m_PhonemeClasses, 0, sizeof( m_PhonemeClasses ) );
  151. Emphasized_Phoneme *normal = &m_PhonemeClasses[ PHONEME_CLASS_NORMAL ];
  152. Q_snprintf( normal->classname, sizeof( normal->classname ), "%s", pchFileRoot );
  153. normal->required = true;
  154. Emphasized_Phoneme *weak = &m_PhonemeClasses[ PHONEME_CLASS_WEAK ];
  155. Q_snprintf( weak->classname, sizeof( weak->classname ), "%s_weak", pchFileRoot );
  156. Emphasized_Phoneme *strong = &m_PhonemeClasses[ PHONEME_CLASS_STRONG ];
  157. Q_snprintf( strong->classname, sizeof( strong->classname ), "%s_strong", pchFileRoot );
  158. }
  159. //----------------------------------------------------------------------------
  160. // Hooks into the fast path render system
  161. //----------------------------------------------------------------------------
  162. IClientModelRenderable* C_BaseFlex::GetClientModelRenderable()
  163. {
  164. // Cannot participate if it has a render clip plane
  165. if ( !BaseClass::GetClientModelRenderable() )
  166. return NULL;
  167. // No flexes allowed for fast path atm
  168. CStudioHdr *hdr = GetModelPtr();
  169. if ( !hdr || ( hdr->numflexcontrollers() != 0 ) )
  170. return NULL;
  171. return this;
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose: initialize fast lookups when model changes
  175. //-----------------------------------------------------------------------------
  176. CStudioHdr *C_BaseFlex::OnNewModel()
  177. {
  178. CStudioHdr *hdr = BaseClass::OnNewModel();
  179. // init to invalid setting
  180. m_iBlink = -1;
  181. m_iEyeUpdown = LocalFlexController_t(-1);
  182. m_iEyeRightleft = LocalFlexController_t(-1);
  183. m_iMouthAttachment = 0;
  184. if ( m_flFlexDelayedWeight )
  185. {
  186. delete[] m_flFlexDelayedWeight;
  187. m_flFlexDelayedWeight = NULL;
  188. }
  189. if (hdr)
  190. {
  191. int nFlexDescCount = hdr->numflexdesc();
  192. m_CachedFlexWeights.SetCount( nFlexDescCount );
  193. m_CachedDelayedFlexWeights.SetCount( nFlexDescCount );
  194. if ( nFlexDescCount )
  195. {
  196. m_flFlexDelayedWeight = new float[ nFlexDescCount ];
  197. memset( m_flFlexDelayedWeight, 0, nFlexDescCount * sizeof(float) );
  198. memset( m_CachedFlexWeights.Base(), 0, nFlexDescCount * sizeof(float) );
  199. memset( m_CachedDelayedFlexWeights.Base(), 0, nFlexDescCount * sizeof(float) );
  200. }
  201. m_iv_flexWeight.SetMaxCount( gpGlobals->curtime, hdr->numflexcontrollers() );
  202. m_iMouthAttachment = LookupAttachment( "mouth" );
  203. // NOTE: Eye updown/right left controllers are *local*
  204. // blink is global
  205. m_iEyeUpdown = FindFlexController( "eyes_updown" );
  206. m_iEyeRightleft = FindFlexController( "eyes_rightleft" );
  207. m_iBlink = AddGlobalFlexController( "blink" );
  208. }
  209. return hdr;
  210. }
  211. void C_BaseFlex::StandardBlendingRules( CStudioHdr *hdr, BoneVector pos[], BoneQuaternionAligned q[], float currentTime, int boneMask )
  212. {
  213. BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
  214. #ifdef HL2_CLIENT_DLL
  215. // shift pelvis, rotate body
  216. if (hdr->GetNumIKChains() != 0 && (m_vecShift.x != 0.0 || m_vecShift.y != 0.0))
  217. {
  218. //CIKContext auto_ik;
  219. //auto_ik.Init( hdr, GetRenderAngles(), GetRenderOrigin(), currentTime, gpGlobals->framecount, boneMask );
  220. //auto_ik.AddAllLocks( pos, q );
  221. matrix3x4_t rootxform;
  222. AngleMatrix( GetRenderAngles(), GetRenderOrigin(), rootxform );
  223. Vector localShift;
  224. VectorIRotate( m_vecShift, rootxform, localShift );
  225. Vector localLean;
  226. VectorIRotate( m_vecLean, rootxform, localLean );
  227. Vector p0 = pos[0];
  228. float length = VectorNormalize( p0 );
  229. // shift the root bone, but keep the height off the origin the same
  230. Vector shiftPos = pos[0] + localShift;
  231. VectorNormalize( shiftPos );
  232. Vector leanPos = pos[0] + localLean;
  233. VectorNormalize( leanPos );
  234. pos[0] = shiftPos * length;
  235. // rotate the root bone based on how much it was "leaned"
  236. Vector p1;
  237. CrossProduct( p0, leanPos, p1 );
  238. float sinAngle = VectorNormalize( p1 );
  239. float cosAngle = DotProduct( p0, leanPos );
  240. float angle = atan2( sinAngle, cosAngle ) * 180 / M_PI;
  241. Quaternion q1;
  242. angle = clamp( angle, -45, 45 );
  243. AxisAngleQuaternion( p1, angle, q1 );
  244. QuaternionMult( q1, q[0], q[0] );
  245. QuaternionNormalize( q[0] );
  246. // DevMsgRT( " (%.2f) %.2f %.2f %.2f\n", angle, p1.x, p1.y, p1.z );
  247. // auto_ik.SolveAllLocks( pos, q );
  248. }
  249. #endif
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose: place "voice" sounds on mouth
  253. //-----------------------------------------------------------------------------
  254. bool C_BaseFlex::GetSoundSpatialization( SpatializationInfo_t& info )
  255. {
  256. bool bret = BaseClass::GetSoundSpatialization( info );
  257. // Default things it's audible, put it at a better spot?
  258. if ( bret )
  259. {
  260. if (info.info.nChannel == CHAN_VOICE && m_iMouthAttachment > 0)
  261. {
  262. Vector origin;
  263. QAngle angles;
  264. C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
  265. if (GetAttachment( m_iMouthAttachment, origin, angles ))
  266. {
  267. if (info.pOrigin)
  268. {
  269. *info.pOrigin = origin;
  270. }
  271. if (info.pAngles)
  272. {
  273. *info.pAngles = angles;
  274. }
  275. }
  276. }
  277. }
  278. return bret;
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Purpose: run the interpreted FAC's expressions, converting flex_controller
  282. // values into FAC weights
  283. //-----------------------------------------------------------------------------
  284. void C_BaseFlex::RunFlexRules( CStudioHdr *hdr, const float *pGlobalFlexWeight, float *dest )
  285. {
  286. if ( !g_CV_FlexRules.GetInt() )
  287. return;
  288. if ( !hdr )
  289. return;
  290. /*
  291. // 0 means run them all
  292. int nFlexRulesToRun = 0;
  293. const char *pszExpression = flex_expression.GetString();
  294. if ( pszExpression )
  295. {
  296. nFlexRulesToRun = atoi(pszExpression); // 0 will be returned if not a numeric string
  297. }
  298. */
  299. hdr->RunFlexRules( pGlobalFlexWeight, dest );
  300. }
  301. class CFlexSceneFileManager : CAutoGameSystem
  302. {
  303. public:
  304. CFlexSceneFileManager() : CAutoGameSystem( "CFlexSceneFileManager" )
  305. {
  306. }
  307. virtual bool InitRecursive( const char *pFolder )
  308. {
  309. if ( pFolder == NULL )
  310. {
  311. pFolder = "expressions";
  312. #if defined( PORTAL2 )
  313. if ( IsGameConsole() )
  314. {
  315. return false;
  316. }
  317. #endif
  318. }
  319. char directory[ MAX_PATH ];
  320. Q_snprintf( directory, sizeof( directory ), "%s/*.*", pFolder );
  321. FileFindHandle_t fh;
  322. const char *fn;
  323. for (fn = g_pFullFileSystem->FindFirst( directory, &fh ); fn; fn = g_pFullFileSystem->FindNext( fh ) )
  324. {
  325. if ( !stricmp( fn, ".") || !stricmp( fn, "..") )
  326. {
  327. continue;
  328. }
  329. if ( g_pFullFileSystem->FindIsDirectory( fh ) )
  330. {
  331. char folderpath[MAX_PATH];
  332. Q_snprintf( folderpath, sizeof( folderpath ), "%s/%s", pFolder, fn );
  333. InitRecursive( folderpath );
  334. continue;
  335. }
  336. const char *pExt = Q_GetFileExtension( fn );
  337. if ( pExt && Q_stricmp( pExt, "vfe" ) )
  338. {
  339. continue;
  340. }
  341. char fullFileName[MAX_PATH];
  342. Q_snprintf( fullFileName, sizeof(fullFileName), "%s/%s", pFolder, fn );
  343. // strip default folder and extension
  344. int index = V_strlen( "expressions/" );
  345. char vfeName[ MAX_PATH ];
  346. V_StripExtension( &fullFileName[index], vfeName, sizeof( vfeName ) );
  347. V_FixSlashes( vfeName );
  348. FindSceneFile( NULL, vfeName, true );
  349. }
  350. return true;
  351. }
  352. virtual bool Init()
  353. {
  354. // Trakcer 16692: Preload these at startup to avoid hitch first time we try to load them during actual gameplay
  355. FindSceneFile( NULL, "phonemes", true );
  356. FindSceneFile( NULL, "phonemes_weak", true );
  357. FindSceneFile(NULL, "phonemes_strong", true );
  358. #if defined( HL2_CLIENT_DLL )
  359. FindSceneFile( NULL, "random", true );
  360. FindSceneFile( NULL, "randomAlert", true );
  361. #endif
  362. #if defined( TF_CLIENT_DLL )
  363. // HACK TO ALL TF TO HAVE PER CLASS OVERRIDES
  364. char const *pTFClasses[] =
  365. {
  366. "scout",
  367. "sniper",
  368. "soldier",
  369. "demo",
  370. "medic",
  371. "heavy",
  372. "pyro",
  373. "spy",
  374. "engineer",
  375. };
  376. char fn[ MAX_PATH ];
  377. for ( int i = 0; i < ARRAYSIZE( pTFClasses ); ++i )
  378. {
  379. Q_snprintf( fn, sizeof( fn ), "player/%s/phonemes/phonemes", pTFClasses[i] );
  380. FindSceneFile( NULL, fn, true );
  381. Q_snprintf( fn, sizeof( fn ), "player/%s/phonemes/phonemes_weak", pTFClasses[i] );
  382. FindSceneFile( NULL, fn, true );
  383. Q_snprintf( fn, sizeof( fn ), "player/%s/phonemes/phonemes_strong", pTFClasses[i] );
  384. FindSceneFile( NULL, fn, true );
  385. if ( !IsGameConsole() )
  386. {
  387. Q_snprintf( fn, sizeof( fn ), "player/hwm/%s/phonemes/phonemes", pTFClasses[i] );
  388. FindSceneFile( NULL, fn, true );
  389. Q_snprintf( fn, sizeof( fn ), "player/hwm/%s/phonemes/phonemes_weak", pTFClasses[i] );
  390. FindSceneFile( NULL, fn, true );
  391. Q_snprintf( fn, sizeof( fn ), "player/hwm/%s/phonemes/phonemes_strong", pTFClasses[i] );
  392. FindSceneFile( NULL, fn, true );
  393. }
  394. Q_snprintf( fn, sizeof( fn ), "player/%s/emotion/emotion", pTFClasses[i] );
  395. FindSceneFile( NULL, fn, true );
  396. if ( !IsGameConsole() )
  397. {
  398. Q_snprintf( fn, sizeof( fn ), "player/hwm/%s/emotion/emotion", pTFClasses[i] );
  399. FindSceneFile( NULL, fn, true );
  400. }
  401. }
  402. #endif
  403. InitRecursive( NULL );
  404. return true;
  405. }
  406. // Tracker 14992: We used to load 18K of .vfes for every C_BaseFlex who lipsynced, but now we only load those files once globally.
  407. // Note, we could wipe these between levels, but they don't ever load more than the weak/normal/strong phoneme classes that I can tell
  408. // so I'll just leave them loaded forever for now
  409. virtual void Shutdown()
  410. {
  411. DeleteSceneFiles();
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose: Sets up translations
  415. // Input : *instance -
  416. // *pSettinghdr -
  417. // Output : void
  418. //-----------------------------------------------------------------------------
  419. void EnsureTranslations( C_BaseFlex *instance, const flexsettinghdr_t *pSettinghdr )
  420. {
  421. // The only time instance is NULL is in Init() above, where we're just loading the .vfe files off of the hard disk.
  422. if ( instance )
  423. {
  424. instance->EnsureTranslations( pSettinghdr );
  425. }
  426. }
  427. const void *FindSceneFile( C_BaseFlex *instance, const char *filename, bool allowBlockingIO )
  428. {
  429. char szFilename[MAX_PATH];
  430. Assert( V_strlen( filename ) < MAX_PATH );
  431. V_strcpy( szFilename, filename );
  432. #if defined( TF_CLIENT_DLL )
  433. char szHWMFilename[MAX_PATH];
  434. if ( GetHWMExpressionFileName( szFilename, szHWMFilename ) )
  435. {
  436. V_strcpy( szFilename, szHWMFilename );
  437. }
  438. #endif
  439. Q_FixSlashes( szFilename );
  440. // See if it's already loaded
  441. int i;
  442. for ( i = 0; i < m_FileList.Count(); i++ )
  443. {
  444. CFlexSceneFile *file = m_FileList[ i ];
  445. if ( file && !V_stricmp( file->filename, szFilename ) )
  446. {
  447. // Make sure translations (local to global flex controller) are set up for this instance
  448. EnsureTranslations( instance, ( const flexsettinghdr_t * )file->buffer );
  449. return file->buffer;
  450. }
  451. }
  452. if ( !allowBlockingIO )
  453. {
  454. return NULL;
  455. }
  456. // Load file into memory
  457. void *buffer = NULL;
  458. int len = filesystem->ReadFileEx( VarArgs( "expressions/%s.vfe", szFilename ), "GAME", &buffer );
  459. if ( !len )
  460. return NULL;
  461. // Create scene entry
  462. CFlexSceneFile *pfile = new CFlexSceneFile;
  463. // Remember filename
  464. Q_strncpy( pfile->filename, szFilename, sizeof( pfile->filename ) );
  465. // Remember data pointer
  466. pfile->buffer = buffer;
  467. // Add to list
  468. m_FileList.AddToTail( pfile );
  469. // Swap the entire file
  470. if ( IsGameConsole() )
  471. {
  472. CByteswap swap;
  473. swap.ActivateByteSwapping( true );
  474. byte *pData = (byte*)buffer;
  475. flexsettinghdr_t *pHdr = (flexsettinghdr_t*)pData;
  476. swap.SwapFieldsToTargetEndian( pHdr );
  477. // Flex Settings
  478. flexsetting_t *pFlexSetting = (flexsetting_t*)((byte*)pHdr + pHdr->flexsettingindex);
  479. for ( int i = 0; i < pHdr->numflexsettings; ++i, ++pFlexSetting )
  480. {
  481. swap.SwapFieldsToTargetEndian( pFlexSetting );
  482. flexweight_t *pWeight = (flexweight_t*)(((byte*)pFlexSetting) + pFlexSetting->settingindex );
  483. for ( int j = 0; j < pFlexSetting->numsettings; ++j, ++pWeight )
  484. {
  485. swap.SwapFieldsToTargetEndian( pWeight );
  486. }
  487. }
  488. // indexes
  489. pData = (byte*)pHdr + pHdr->indexindex;
  490. swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numindexes );
  491. // keymappings
  492. pData = (byte*)pHdr + pHdr->keymappingindex;
  493. swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numkeys );
  494. // keyname indices
  495. pData = (byte*)pHdr + pHdr->keynameindex;
  496. swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numkeys );
  497. }
  498. // Fill in translation table
  499. EnsureTranslations( instance, ( const flexsettinghdr_t * )pfile->buffer );
  500. // Return data
  501. return pfile->buffer;
  502. }
  503. private:
  504. void DeleteSceneFiles()
  505. {
  506. while ( m_FileList.Count() > 0 )
  507. {
  508. CFlexSceneFile *file = m_FileList[ 0 ];
  509. m_FileList.Remove( 0 );
  510. free( file->buffer );
  511. delete file;
  512. }
  513. }
  514. CUtlVector< CFlexSceneFile * > m_FileList;
  515. };
  516. CFlexSceneFileManager g_FlexSceneFileManager;
  517. //-----------------------------------------------------------------------------
  518. // Purpose:
  519. // Input : *filename -
  520. //-----------------------------------------------------------------------------
  521. const void *C_BaseFlex::FindSceneFile( const char *filename )
  522. {
  523. // Ask manager to get the globally cached scene instead.
  524. // hunt up the tree for the filename starting with our model name prepended as the path
  525. static char szExtendedPath[MAX_PATH];
  526. const char *pModelName = NULL;
  527. CStudioHdr *pStudioHdr = GetModelPtr();
  528. if ( pStudioHdr )
  529. {
  530. pModelName = pStudioHdr->pszName();
  531. }
  532. if ( !pModelName )
  533. {
  534. return g_FlexSceneFileManager.FindSceneFile( this, filename, false );
  535. }
  536. if ( StringHasPrefix( pModelName, "models" ) )
  537. {
  538. strcpy( szExtendedPath, StringAfterPrefix( pModelName, "models" ) + 1 );
  539. }
  540. else
  541. {
  542. strcpy( szExtendedPath, pModelName );
  543. }
  544. V_StripExtension( szExtendedPath, szExtendedPath, sizeof( szExtendedPath ) );
  545. V_FixupPathName( szExtendedPath, sizeof( szExtendedPath ), szExtendedPath );
  546. const void *pSceneFile = NULL;
  547. // FIXME: V_StripLastDir returns "./" path when it strips out the last one. That don't resolve on FindSceneFile
  548. while ( V_strlen( szExtendedPath ) > 2 )
  549. {
  550. static char szExtendedName[MAX_PATH];
  551. V_ComposeFileName( szExtendedPath, filename, szExtendedName, sizeof( szExtendedName ) );
  552. pSceneFile = g_FlexSceneFileManager.FindSceneFile( this, szExtendedName, false );
  553. if ( pSceneFile )
  554. break;
  555. if ( !V_StripLastDir( szExtendedPath, sizeof( szExtendedPath ) ) )
  556. break;
  557. }
  558. if ( !pSceneFile )
  559. {
  560. // just ask for it by name
  561. pSceneFile = g_FlexSceneFileManager.FindSceneFile( this, filename, false );
  562. }
  563. return pSceneFile;
  564. }
  565. //-----------------------------------------------------------------------------
  566. // Purpose: make sure the eyes are within 30 degrees of forward
  567. //-----------------------------------------------------------------------------
  568. Vector C_BaseFlex::SetViewTarget( CStudioHdr *pStudioHdr, const float *pGlobalFlexWeight )
  569. {
  570. if ( !pStudioHdr )
  571. return Vector( 0, 0, 0);
  572. // aim the eyes
  573. Vector tmp = m_viewtarget;
  574. if (m_iEyeAttachment > 0)
  575. {
  576. matrix3x4_t attToWorld;
  577. if (!GetAttachment( m_iEyeAttachment, attToWorld ))
  578. {
  579. return Vector( 0, 0, 0);
  580. }
  581. Vector local;
  582. VectorITransform( tmp, attToWorld, local );
  583. // FIXME: clamp distance to something based on eyeball distance
  584. if (local.x < 6)
  585. {
  586. local.x = 6;
  587. }
  588. float flDist = local.Length();
  589. VectorNormalize( local );
  590. // calculate animated eye deflection
  591. Vector eyeDeflect;
  592. QAngle eyeAng( 0, 0, 0 );
  593. if ( m_iEyeUpdown != -1 )
  594. {
  595. mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller( m_iEyeUpdown );
  596. eyeAng.x = pGlobalFlexWeight[ pflex->localToGlobal ];
  597. }
  598. if ( m_iEyeRightleft != -1 )
  599. {
  600. mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller( m_iEyeRightleft );
  601. eyeAng.y = pGlobalFlexWeight[ pflex->localToGlobal ];
  602. }
  603. // debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%5.3f %5.3f", eyeAng.x, eyeAng.y );
  604. AngleVectors( eyeAng, &eyeDeflect );
  605. eyeDeflect.x = 0;
  606. // reduce deflection the more the eye is off center
  607. // FIXME: this angles make no damn sense
  608. eyeDeflect = eyeDeflect * (local.x * local.x);
  609. local = local + eyeDeflect;
  610. VectorNormalize( local );
  611. // check to see if the eye is aiming outside the max eye deflection
  612. float flMaxEyeDeflection = pStudioHdr->MaxEyeDeflection();
  613. if ( local.x < flMaxEyeDeflection )
  614. {
  615. // if so, clamp it to 30 degrees offset
  616. // debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 1, 0, "%5.3f %5.3f %5.3f", local.x, local.y, local.z );
  617. local.x = 0;
  618. float d = local.LengthSqr();
  619. if ( d > 0.0f )
  620. {
  621. d = sqrtf( ( 1.0f - flMaxEyeDeflection * flMaxEyeDeflection ) / ( local.y*local.y + local.z*local.z ) );
  622. local.x = flMaxEyeDeflection;
  623. local.y = local.y * d;
  624. local.z = local.z * d;
  625. }
  626. else
  627. {
  628. local.x = 1.0;
  629. }
  630. }
  631. local = local * flDist;
  632. VectorTransform( local, attToWorld, tmp );
  633. }
  634. modelrender->SetViewTarget( GetModelPtr(), GetBody(), tmp );
  635. /*
  636. debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%.2f %.2f %.2f : %.2f %.2f %.2f",
  637. m_viewtarget.x, m_viewtarget.y, m_viewtarget.z,
  638. m_prevviewtarget.x, m_prevviewtarget.y, m_prevviewtarget.z );
  639. */
  640. return tmp;
  641. }
  642. #define STRONG_CROSSFADE_START 0.60f
  643. #define WEAK_CROSSFADE_START 0.40f
  644. //-----------------------------------------------------------------------------
  645. // Purpose:
  646. // Here's the formula
  647. // 0.5 is neutral 100 % of the default setting
  648. // Crossfade starts at STRONG_CROSSFADE_START and is full at STRONG_CROSSFADE_END
  649. // If there isn't a strong then the intensity of the underlying phoneme is fixed at 2 x STRONG_CROSSFADE_START
  650. // so we don't get huge numbers
  651. // Input : *classes -
  652. // emphasis_intensity -
  653. //-----------------------------------------------------------------------------
  654. void C_BaseFlex::ComputeBlendedSetting( Emphasized_Phoneme *classes, float emphasis_intensity )
  655. {
  656. // See which blends are available for the current phoneme
  657. bool has_weak = classes[ PHONEME_CLASS_WEAK ].valid;
  658. bool has_strong = classes[ PHONEME_CLASS_STRONG ].valid;
  659. // Better have phonemes in general
  660. Assert( classes[ PHONEME_CLASS_NORMAL ].valid );
  661. if ( emphasis_intensity > STRONG_CROSSFADE_START )
  662. {
  663. if ( has_strong )
  664. {
  665. // Blend in some of strong
  666. float dist_remaining = 1.0f - emphasis_intensity;
  667. float frac = dist_remaining / ( 1.0f - STRONG_CROSSFADE_START );
  668. classes[ PHONEME_CLASS_NORMAL ].amount = (frac) * 2.0f * STRONG_CROSSFADE_START;
  669. classes[ PHONEME_CLASS_STRONG ].amount = 1.0f - frac;
  670. }
  671. else
  672. {
  673. emphasis_intensity = MIN( emphasis_intensity, STRONG_CROSSFADE_START );
  674. classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
  675. }
  676. }
  677. else if ( emphasis_intensity < WEAK_CROSSFADE_START )
  678. {
  679. if ( has_weak )
  680. {
  681. // Blend in some weak
  682. float dist_remaining = WEAK_CROSSFADE_START - emphasis_intensity;
  683. float frac = dist_remaining / ( WEAK_CROSSFADE_START );
  684. classes[ PHONEME_CLASS_NORMAL ].amount = (1.0f - frac) * 2.0f * WEAK_CROSSFADE_START;
  685. classes[ PHONEME_CLASS_WEAK ].amount = frac;
  686. }
  687. else
  688. {
  689. emphasis_intensity = MAX( emphasis_intensity, WEAK_CROSSFADE_START );
  690. classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
  691. }
  692. }
  693. else
  694. {
  695. // Assume 0.5 (neutral) becomes a scaling of 1.0f
  696. classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
  697. }
  698. }
  699. //-----------------------------------------------------------------------------
  700. // Purpose:
  701. // Input : *classes -
  702. // phoneme -
  703. // scale -
  704. // newexpression -
  705. //-----------------------------------------------------------------------------
  706. void C_BaseFlex::AddViseme( float *pGlobalFlexWeight, Emphasized_Phoneme *classes, float emphasis_intensity, int phoneme, float scale, bool newexpression )
  707. {
  708. int type;
  709. // Setup weights for any emphasis blends
  710. bool skip = SetupEmphasisBlend( classes, phoneme );
  711. // Uh-oh, missing or unknown phoneme???
  712. if ( skip )
  713. {
  714. return;
  715. }
  716. // Compute blend weights
  717. ComputeBlendedSetting( classes, emphasis_intensity );
  718. for ( type = 0; type < NUM_PHONEME_CLASSES; type++ )
  719. {
  720. Emphasized_Phoneme *info = &classes[ type ];
  721. if ( !info->valid || info->amount == 0.0f )
  722. continue;
  723. const flexsettinghdr_t *actual_flexsetting_header = info->base;
  724. const flexsetting_t *pSetting = actual_flexsetting_header->pIndexedSetting( phoneme );
  725. if (!pSetting)
  726. {
  727. continue;
  728. }
  729. flexweight_t *pWeights = NULL;
  730. int truecount = pSetting->psetting( (byte *)actual_flexsetting_header, 0, &pWeights );
  731. if ( pWeights )
  732. {
  733. for ( int i = 0; i < truecount; i++)
  734. {
  735. // Translate to global controller number
  736. int j = FlexControllerLocalToGlobal( actual_flexsetting_header, pWeights->key );
  737. // Add scaled weighting in
  738. pGlobalFlexWeight[j] += info->amount * scale * pWeights->weight;
  739. // Go to next setting
  740. pWeights++;
  741. }
  742. }
  743. }
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Purpose: A lot of the one time setup and also resets amount to 0.0f default
  747. // for strong/weak/normal tracks
  748. // Returning true == skip this phoneme
  749. // Input : *classes -
  750. // Output : Returns true on success, false on failure.
  751. //-----------------------------------------------------------------------------
  752. bool C_BaseFlex::SetupEmphasisBlend( Emphasized_Phoneme *classes, int phoneme )
  753. {
  754. int i;
  755. bool skip = false;
  756. for ( i = 0; i < NUM_PHONEME_CLASSES; i++ )
  757. {
  758. Emphasized_Phoneme *info = &classes[ i ];
  759. // Assume it's bogus
  760. info->valid = false;
  761. info->amount = 0.0f;
  762. // One time setup
  763. if ( !info->basechecked )
  764. {
  765. info->basechecked = true;
  766. info->base = (flexsettinghdr_t *)FindSceneFile( info->classname );
  767. }
  768. info->exp = NULL;
  769. if ( info->base )
  770. {
  771. Assert( info->base->id == ('V' << 16) + ('F' << 8) + ('E') );
  772. info->exp = info->base->pIndexedSetting( phoneme );
  773. }
  774. if ( info->required && ( !info->base || !info->exp ) )
  775. {
  776. skip = true;
  777. break;
  778. }
  779. if ( info->exp )
  780. {
  781. info->valid = true;
  782. }
  783. }
  784. return skip;
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose:
  788. // Input : *classes -
  789. // *sentence -
  790. // t -
  791. // dt -
  792. // juststarted -
  793. //-----------------------------------------------------------------------------
  794. ConVar g_CV_PhonemeSnap("phonemesnap", "2", 0, "Lod at level at which visemes stops always considering two phonemes, regardless of duration." );
  795. void C_BaseFlex::AddVisemesForSentence( float *pGlobalFlexWeight, Emphasized_Phoneme *classes, float emphasis_intensity, CSentence *sentence, float t, float dt, bool juststarted )
  796. {
  797. CStudioHdr *hdr = GetModelPtr();
  798. if ( !hdr )
  799. {
  800. return;
  801. }
  802. int pcount = sentence->GetRuntimePhonemeCount();
  803. for ( int k = 0; k < pcount; k++ )
  804. {
  805. const CBasePhonemeTag *phoneme = sentence->GetRuntimePhoneme( k );
  806. if (t > phoneme->GetStartTime() && t < phoneme->GetEndTime())
  807. {
  808. bool bCrossfade = true;
  809. if ((hdr->flags() & STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE) == 0)
  810. {
  811. if (m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD0)
  812. {
  813. bCrossfade = (g_CV_PhonemeSnap.GetInt() > 0);
  814. }
  815. else if (m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD1)
  816. {
  817. bCrossfade = (g_CV_PhonemeSnap.GetInt() > 1);
  818. }
  819. else if (m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD2)
  820. {
  821. bCrossfade = (g_CV_PhonemeSnap.GetInt() > 2);
  822. }
  823. else if (m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD3)
  824. {
  825. bCrossfade = (g_CV_PhonemeSnap.GetInt() > 3);
  826. }
  827. else
  828. {
  829. bCrossfade = false;
  830. }
  831. }
  832. if (bCrossfade)
  833. {
  834. if (k < pcount-1)
  835. {
  836. const CBasePhonemeTag *next = sentence->GetRuntimePhoneme( k + 1 );
  837. // if I have a neighbor
  838. if ( next )
  839. {
  840. // and they're touching
  841. if (next->GetStartTime() == phoneme->GetEndTime() )
  842. {
  843. // no gap, so increase the blend length to the end of the next phoneme, as long as it's not longer than the current phoneme
  844. dt = MAX( dt, MIN( next->GetEndTime() - t, phoneme->GetEndTime() - phoneme->GetStartTime() ) );
  845. }
  846. else
  847. {
  848. // dead space, so increase the blend length to the start of the next phoneme, as long as it's not longer than the current phoneme
  849. dt = MAX( dt, MIN( next->GetStartTime() - t, phoneme->GetEndTime() - phoneme->GetStartTime() ) );
  850. }
  851. }
  852. else
  853. {
  854. // last phoneme in list, increase the blend length to the length of the current phoneme
  855. dt = MAX( dt, phoneme->GetEndTime() - phoneme->GetStartTime() );
  856. }
  857. }
  858. }
  859. }
  860. float t1 = ( phoneme->GetStartTime() - t) / dt;
  861. float t2 = ( phoneme->GetEndTime() - t) / dt;
  862. if (t1 < 1.0 && t2 > 0)
  863. {
  864. float scale;
  865. // clamp
  866. if (t2 > 1)
  867. t2 = 1;
  868. if (t1 < 0)
  869. t1 = 0;
  870. // FIXME: simple box filter. Should use something fancier
  871. scale = (t2 - t1);
  872. AddViseme( pGlobalFlexWeight, classes, emphasis_intensity, phoneme->GetPhonemeCode(), scale, juststarted );
  873. }
  874. }
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose:
  878. // Input : *classes -
  879. //-----------------------------------------------------------------------------
  880. void C_BaseFlex::ProcessVisemes( Emphasized_Phoneme *classes, float *pGlobalFlexWeight )
  881. {
  882. // Any sounds being played?
  883. if ( !MouthInfo().IsActive() )
  884. return;
  885. // Multiple phoneme tracks can overlap, look across all such tracks.
  886. for ( int source = 0 ; source < MouthInfo().GetNumVoiceSources(); source++ )
  887. {
  888. CVoiceData *vd = MouthInfo().GetVoiceSource( source );
  889. if ( !vd || vd->ShouldIgnorePhonemes() )
  890. continue;
  891. CSentence *sentence = engine->GetSentence( vd->GetSource() );
  892. if ( !sentence )
  893. continue;
  894. float sentence_length = engine->GetSentenceLength( vd->GetSource() );
  895. float timesincestart = vd->GetElapsedTime();
  896. // This sound should be done...why hasn't it been removed yet???
  897. if ( timesincestart >= ( sentence_length + 2.0f ) )
  898. continue;
  899. // Adjust actual time
  900. float t = timesincestart - g_CV_PhonemeDelay.GetFloat();
  901. // Get box filter duration
  902. float dt = g_CV_PhonemeFilter.GetFloat();
  903. // Streaming sounds get an additional delay...
  904. /*
  905. // Tracker 20534: Probably not needed any more with the async sound stuff that
  906. // we now have (we don't have a disk i/o hitch on startup which might have been
  907. // messing up the startup timing a bit )
  908. bool streaming = engine->IsStreaming( vd->m_pAudioSource );
  909. if ( streaming )
  910. {
  911. t -= g_CV_PhonemeDelayStreaming.GetFloat();
  912. }
  913. */
  914. // Assume sound has been playing for a while...
  915. bool juststarted = false;
  916. // Get intensity setting for this time (from spline)
  917. float emphasis_intensity = sentence->GetIntensity( t, sentence_length );
  918. // Blend and add visemes together
  919. AddVisemesForSentence( pGlobalFlexWeight, classes, emphasis_intensity, sentence, t, dt, juststarted );
  920. }
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Purpose: fill keyvalues message with flex state
  924. // Input :
  925. //-----------------------------------------------------------------------------
  926. void C_BaseFlex::GetToolRecordingState( KeyValues *msg )
  927. {
  928. if ( !ToolsEnabled() )
  929. return;
  930. VPROF_BUDGET( "C_BaseFlex::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
  931. BaseClass::GetToolRecordingState( msg );
  932. CStudioHdr *hdr = GetModelPtr();
  933. if ( !hdr )
  934. return;
  935. if ( hdr->numflexcontrollers() == 0 )
  936. return;
  937. LocalFlexController_t i;
  938. ProcessSceneEvents( true, NULL );
  939. // FIXME: shouldn't this happen at runtime?
  940. // initialize the models local to global flex controller mappings
  941. if (hdr->pFlexcontroller( LocalFlexController_t(0) )->localToGlobal == -1)
  942. {
  943. for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  944. {
  945. int j = AddGlobalFlexController( hdr->pFlexcontroller( i )->pszName() );
  946. hdr->pFlexcontroller( i )->localToGlobal = j;
  947. }
  948. }
  949. memset( s_pGlobalFlexWeight, 0, g_numflexcontrollers * sizeof( float ) );
  950. // blend weights from server
  951. for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  952. {
  953. mstudioflexcontroller_t *pflex = hdr->pFlexcontroller( i );
  954. // rescale
  955. s_pGlobalFlexWeight[pflex->localToGlobal] = m_flexWeight[i] * (pflex->max - pflex->min) + pflex->min;
  956. }
  957. ProcessSceneEvents( false, s_pGlobalFlexWeight );
  958. // check for blinking
  959. if (m_blinktoggle != m_prevblinktoggle)
  960. {
  961. m_prevblinktoggle = m_blinktoggle;
  962. m_blinktime = gpGlobals->curtime + g_CV_BlinkDuration.GetFloat();
  963. }
  964. if (m_iBlink == -1)
  965. {
  966. m_iBlink = AddGlobalFlexController( "blink" );
  967. }
  968. s_pGlobalFlexWeight[m_iBlink] = 0;
  969. // FIXME: this needs a better algorithm
  970. // blink the eyes
  971. float t = (m_blinktime - gpGlobals->curtime) * M_PI * 0.5 * (1.0/g_CV_BlinkDuration.GetFloat());
  972. if (t > 0)
  973. {
  974. // do eyeblink falloff curve
  975. t = cos(t);
  976. if (t > 0)
  977. {
  978. s_pGlobalFlexWeight[m_iBlink] = sqrtf( t ) * 2;
  979. if (s_pGlobalFlexWeight[m_iBlink] > 1)
  980. {
  981. s_pGlobalFlexWeight[m_iBlink] = 2.0 - s_pGlobalFlexWeight[m_iBlink];
  982. }
  983. }
  984. }
  985. // Drive the mouth from .wav file playback...
  986. ProcessVisemes( m_PhonemeClasses, s_pGlobalFlexWeight );
  987. // Necessary???
  988. SetViewTarget( hdr, s_pGlobalFlexWeight );
  989. Vector viewtarget = m_viewtarget; // Use the unfiltered value
  990. // HACK HACK: Unmap eyes right/left amounts
  991. if (m_iEyeUpdown != -1 && m_iEyeRightleft != -1)
  992. {
  993. mstudioflexcontroller_t *flexupdown = hdr->pFlexcontroller( m_iEyeUpdown );
  994. mstudioflexcontroller_t *flexrightleft = hdr->pFlexcontroller( m_iEyeRightleft );
  995. if ( flexupdown->localToGlobal != -1 && flexrightleft->localToGlobal != -1 )
  996. {
  997. float updown = s_pGlobalFlexWeight[ flexupdown->localToGlobal ];
  998. float rightleft = s_pGlobalFlexWeight[ flexrightleft->localToGlobal ];
  999. if ( flexupdown->min != flexupdown->max )
  1000. {
  1001. updown = RemapVal( updown, flexupdown->min, flexupdown->max, 0.0f, 1.0f );
  1002. }
  1003. if ( flexrightleft->min != flexrightleft->max )
  1004. {
  1005. rightleft = RemapVal( rightleft, flexrightleft->min, flexrightleft->max, 0.0f, 1.0f );
  1006. }
  1007. s_pGlobalFlexWeight[ flexupdown->localToGlobal ] = updown;
  1008. s_pGlobalFlexWeight[ flexrightleft->localToGlobal ] = rightleft;
  1009. }
  1010. }
  1011. // Convert back to normalized weights
  1012. for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  1013. {
  1014. mstudioflexcontroller_t *pflex = hdr->pFlexcontroller( i );
  1015. // rescale
  1016. if ( pflex->max != pflex->min )
  1017. {
  1018. s_pGlobalFlexWeight[pflex->localToGlobal] = ( s_pGlobalFlexWeight[pflex->localToGlobal] - pflex->min ) / ( pflex->max - pflex->min );
  1019. }
  1020. }
  1021. static BaseFlexRecordingState_t state;
  1022. state.m_nFlexCount = g_numflexcontrollers;
  1023. state.m_pDestWeight = s_pGlobalFlexWeight;
  1024. state.m_vecViewTarget = viewtarget;
  1025. msg->SetPtr( "baseflex", &state );
  1026. }
  1027. //-----------------------------------------------------------------------------
  1028. // Purpose:
  1029. //-----------------------------------------------------------------------------
  1030. void C_BaseFlex::OnThreadedDrawSetup()
  1031. {
  1032. if (m_iEyeAttachment < 0)
  1033. return;
  1034. CStudioHdr *hdr = GetModelPtr();
  1035. if ( !hdr )
  1036. {
  1037. return;
  1038. }
  1039. CalcAttachments();
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. // Should we use delayed flex weights?
  1043. //-----------------------------------------------------------------------------
  1044. bool C_BaseFlex::UsesFlexDelayedWeights()
  1045. {
  1046. return ( m_flFlexDelayedWeight && g_CV_FlexSmooth.GetBool() );
  1047. }
  1048. void C_BaseFlex::InvalidateFlexCaches()
  1049. {
  1050. g_iFlexCounter++;
  1051. }
  1052. bool C_BaseFlex::IsFlexCacheValid() const
  1053. {
  1054. return m_iMostRecentFlexCounter == g_iFlexCounter;
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Purpose: Use the local bone positions to set flex control weights
  1058. // via boneflexdrivers specified in the model
  1059. //-----------------------------------------------------------------------------
  1060. void C_BaseFlex::BuildTransformations( CStudioHdr *pStudioHdr, BoneVector *pos, BoneQuaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
  1061. {
  1062. const int nBoneFlexDriverCount = pStudioHdr->BoneFlexDriverCount();
  1063. for ( int i = 0; i < nBoneFlexDriverCount; ++i )
  1064. {
  1065. const mstudioboneflexdriver_t *pBoneFlexDriver = pStudioHdr->BoneFlexDriver( i );
  1066. const Vector &position = pos[ pBoneFlexDriver->m_nBoneIndex ];
  1067. const int nControllerCount = pBoneFlexDriver->m_nControlCount;
  1068. for ( int j = 0; j < nControllerCount; ++j )
  1069. {
  1070. const mstudioboneflexdrivercontrol_t *pController = pBoneFlexDriver->pBoneFlexDriverControl( j );
  1071. Assert( pController->m_nFlexControllerIndex >= 0 && pController->m_nFlexControllerIndex < pStudioHdr->numflexcontrollers() );
  1072. Assert( pController->m_nBoneComponent >= 0 && pController->m_nBoneComponent <= 2 );
  1073. SetFlexWeight( static_cast< LocalFlexController_t >( pController->m_nFlexControllerIndex ), RemapValClamped( position[pController->m_nBoneComponent], pController->m_flMin, pController->m_flMax, 0.0f, 1.0f ) );
  1074. }
  1075. }
  1076. BaseClass::BuildTransformations( pStudioHdr, pos, q, cameraTransform, boneMask, boneComputed );
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // Purpose:
  1080. //-----------------------------------------------------------------------------
  1081. void C_BaseFlex::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
  1082. {
  1083. CStudioHdr *hdr = GetModelPtr();
  1084. if ( !hdr )
  1085. return;
  1086. // FIXME: this should assert then, it's too complex a class for the model
  1087. if ( hdr->numflexcontrollers() == 0 )
  1088. {
  1089. int nSizeInBytes = nFlexWeightCount * sizeof( float );
  1090. memset( pFlexWeights, 0, nSizeInBytes );
  1091. if ( pFlexDelayedWeights )
  1092. {
  1093. memset( pFlexDelayedWeights, 0, nSizeInBytes );
  1094. }
  1095. return;
  1096. }
  1097. int nFlexDescCount = hdr->numflexdesc();
  1098. Assert( nFlexDescCount == m_CachedFlexWeights.Count() );
  1099. if ( IsFlexCacheValid() )
  1100. {
  1101. int nCount = MIN( nFlexWeightCount, nFlexDescCount );
  1102. memcpy( pFlexWeights, m_CachedFlexWeights.Base(), nCount * sizeof(float) );
  1103. if ( pFlexDelayedWeights )
  1104. {
  1105. memcpy( pFlexDelayedWeights, m_CachedDelayedFlexWeights.Base(), nCount * sizeof(float) );
  1106. }
  1107. modelrender->SetViewTarget( GetModelPtr(), GetBody(), m_CachedViewTarget );
  1108. return;
  1109. }
  1110. Assert( nFlexWeightCount >= nFlexDescCount );
  1111. LocalFlexController_t i;
  1112. ProcessSceneEvents( true, NULL );
  1113. // FIXME: shouldn't this happen at runtime?
  1114. // initialize the models local to global flex controller mappings
  1115. if ( hdr->pFlexcontroller( LocalFlexController_t(0) )->localToGlobal == -1 )
  1116. {
  1117. for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  1118. {
  1119. int j = AddGlobalFlexController( hdr->pFlexcontroller( i )->pszName() );
  1120. hdr->pFlexcontroller( i )->localToGlobal = j;
  1121. }
  1122. }
  1123. memset( s_pGlobalFlexWeight, 0, g_numflexcontrollers * sizeof(float) );
  1124. // get the networked flexweights and convert them from 0..1 to real dynamic range
  1125. for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  1126. {
  1127. mstudioflexcontroller_t *pflex = hdr->pFlexcontroller( i );
  1128. // rescale
  1129. s_pGlobalFlexWeight[pflex->localToGlobal] = m_flexWeight[i] * (pflex->max - pflex->min) + pflex->min;
  1130. }
  1131. ProcessSceneEvents( false, s_pGlobalFlexWeight );
  1132. // check for blinking
  1133. if (m_blinktoggle != m_prevblinktoggle)
  1134. {
  1135. m_prevblinktoggle = m_blinktoggle;
  1136. m_blinktime = gpGlobals->curtime + g_CV_BlinkDuration.GetFloat();
  1137. }
  1138. // FIXME: this needs a better algorithm
  1139. // blink the eyes
  1140. float flBlinkDuration = g_CV_BlinkDuration.GetFloat();
  1141. float flOOBlinkDuration = ( flBlinkDuration > 0 ) ? 1.0f / flBlinkDuration : 0.0f;
  1142. float t = ( m_blinktime - gpGlobals->curtime ) * M_PI * 0.5 * flOOBlinkDuration;
  1143. if (t > 0)
  1144. {
  1145. // do eyeblink falloff curve
  1146. t = cos(t);
  1147. if (t > 0.0f && t < 1.0f)
  1148. {
  1149. t = sqrtf( t ) * 2.0f;
  1150. if (t > 1.0f)
  1151. t = 2.0f - t;
  1152. t = clamp( t, 0.0f, 1.0f );
  1153. // add it to whatever the blink track is doing
  1154. s_pGlobalFlexWeight[m_iBlink] = clamp( s_pGlobalFlexWeight[m_iBlink] + t, 0.0, 1.0 );
  1155. }
  1156. }
  1157. // Drive the mouth from .wav file playback...
  1158. ProcessVisemes( m_PhonemeClasses, s_pGlobalFlexWeight );
  1159. // convert the flex controllers into actual flex values
  1160. RunFlexRules( hdr, s_pGlobalFlexWeight, pFlexWeights );
  1161. // aim the eyes
  1162. m_CachedViewTarget = SetViewTarget( hdr, s_pGlobalFlexWeight );
  1163. // process the delayed version of the flexweights
  1164. float d = 1.0f;
  1165. if ( gpGlobals->frametime != 0 )
  1166. {
  1167. d = ExponentialDecay( 0.8f, 0.033f, gpGlobals->frametime );
  1168. }
  1169. for ( i = LocalFlexController_t(0); i < nFlexDescCount; i++ )
  1170. {
  1171. m_flFlexDelayedWeight[i] = m_flFlexDelayedWeight[i] * d + pFlexWeights[i] * (1 - d);
  1172. }
  1173. if ( pFlexDelayedWeights )
  1174. {
  1175. memcpy( pFlexDelayedWeights, m_flFlexDelayedWeight, nFlexDescCount * sizeof(float) );
  1176. }
  1177. // Cache off results
  1178. m_iMostRecentFlexCounter = g_iFlexCounter;
  1179. memcpy( m_CachedFlexWeights.Base(), pFlexWeights, nFlexDescCount * sizeof(float) );
  1180. memcpy( m_CachedDelayedFlexWeights.Base(), m_flFlexDelayedWeight, nFlexDescCount * sizeof(float) );
  1181. /*
  1182. for (i = 0; i < hdr->numflexdesc; i++)
  1183. {
  1184. debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), i-hdr->numflexcontrollers, 0, "%2d:%s : %3.2f", i, hdr->pFlexdesc( i )->pszFACS(), pFlexWeights[i] );
  1185. }
  1186. */
  1187. /*
  1188. for (i = 0; i < g_numflexcontrollers; i++)
  1189. {
  1190. int j = hdr->pFlexcontroller( i )->link;
  1191. debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), -i, 0, "%s %3.2f", g_flexcontroller[i], s_pGlobalFlexWeight[j] );
  1192. }
  1193. */
  1194. }
  1195. int C_BaseFlex::g_numflexcontrollers;
  1196. char * C_BaseFlex::g_flexcontroller[MAXSTUDIOFLEXCTRL*4];
  1197. float C_BaseFlex::s_pGlobalFlexWeight[MAXSTUDIOFLEXCTRL*4];
  1198. int C_BaseFlex::AddGlobalFlexController( char *szName )
  1199. {
  1200. int i;
  1201. for (i = 0; i < g_numflexcontrollers; i++)
  1202. {
  1203. if (Q_stricmp( g_flexcontroller[i], szName ) == 0)
  1204. {
  1205. return i;
  1206. }
  1207. }
  1208. if ( g_numflexcontrollers < MAXSTUDIOFLEXCTRL * 4 )
  1209. {
  1210. g_flexcontroller[g_numflexcontrollers++] = strdup( szName );
  1211. }
  1212. else
  1213. {
  1214. // FIXME: missing runtime error condition
  1215. }
  1216. return i;
  1217. }
  1218. char const *C_BaseFlex::GetGlobalFlexControllerName( int idx )
  1219. {
  1220. if ( idx < 0 || idx >= g_numflexcontrollers )
  1221. {
  1222. return "";
  1223. }
  1224. return g_flexcontroller[ idx ];
  1225. }
  1226. const flexsetting_t *C_BaseFlex::FindNamedSetting( const flexsettinghdr_t *pSettinghdr, const char *expr )
  1227. {
  1228. int i;
  1229. const flexsetting_t *pSetting = NULL;
  1230. for ( i = 0; i < pSettinghdr->numflexsettings; i++ )
  1231. {
  1232. pSetting = pSettinghdr->pSetting( i );
  1233. if ( !pSetting )
  1234. continue;
  1235. const char *name = pSetting->pszName();
  1236. if ( !stricmp( name, expr ) )
  1237. break;
  1238. }
  1239. if ( i>=pSettinghdr->numflexsettings )
  1240. {
  1241. return NULL;
  1242. }
  1243. return pSetting;
  1244. }
  1245. //-----------------------------------------------------------------------------
  1246. // Purpose:
  1247. //-----------------------------------------------------------------------------
  1248. void C_BaseFlex::StartChoreoScene( CChoreoScene *scene )
  1249. {
  1250. if ( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() )
  1251. {
  1252. return;
  1253. }
  1254. m_ActiveChoreoScenes.AddToTail( scene );
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. // Purpose:
  1258. //-----------------------------------------------------------------------------
  1259. void C_BaseFlex::RemoveChoreoScene( CChoreoScene *scene )
  1260. {
  1261. // Assert( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() );
  1262. m_ActiveChoreoScenes.FindAndRemove( scene );
  1263. }
  1264. //-----------------------------------------------------------------------------
  1265. // Purpose: Remove all active SceneEvents
  1266. //-----------------------------------------------------------------------------
  1267. void C_BaseFlex::ClearSceneEvents( CChoreoScene *scene, bool canceled )
  1268. {
  1269. if ( !scene )
  1270. {
  1271. m_SceneEvents.RemoveAll();
  1272. return;
  1273. }
  1274. for ( int i = m_SceneEvents.Count() - 1; i >= 0; i-- )
  1275. {
  1276. CSceneEventInfo *info = &m_SceneEvents[ i ];
  1277. Assert( info );
  1278. Assert( info->m_pScene );
  1279. Assert( info->m_pEvent );
  1280. if ( info->m_pScene != scene )
  1281. continue;
  1282. if ( !ClearSceneEvent( info, false, canceled ))
  1283. {
  1284. // unknown expression to clear!!
  1285. Assert( 0 );
  1286. }
  1287. // Free this slot
  1288. info->m_pEvent = NULL;
  1289. info->m_pScene = NULL;
  1290. info->m_bStarted = false;
  1291. m_SceneEvents.Remove( i );
  1292. }
  1293. }
  1294. //-----------------------------------------------------------------------------
  1295. // Purpose: Stop specifics of expression
  1296. //-----------------------------------------------------------------------------
  1297. bool C_BaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled )
  1298. {
  1299. Assert( info );
  1300. Assert( info->m_pScene );
  1301. Assert( info->m_pEvent );
  1302. return true;
  1303. }
  1304. //-----------------------------------------------------------------------------
  1305. // Purpose: Add string indexed scene/expression/duration to list of active SceneEvents
  1306. // Input : scenefile -
  1307. // expression -
  1308. // duration -
  1309. //-----------------------------------------------------------------------------
  1310. void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide, C_SceneEntity *pSceneEntity )
  1311. {
  1312. if ( !scene || !event )
  1313. {
  1314. Msg( "C_BaseFlex::AddSceneEvent: scene or event was NULL!!!\n" );
  1315. return;
  1316. }
  1317. CChoreoActor *actor = event->GetActor();
  1318. if ( !actor )
  1319. {
  1320. Msg( "C_BaseFlex::AddSceneEvent: event->GetActor() was NULL!!!\n" );
  1321. return;
  1322. }
  1323. CSceneEventInfo info;
  1324. memset( (void *)&info, 0, sizeof( info ) );
  1325. info.m_pEvent = event;
  1326. info.m_pScene = scene;
  1327. info.m_hTarget = pTarget;
  1328. info.m_bStarted = false;
  1329. info.m_bClientSide = bClientSide;
  1330. info.m_hSceneEntity = pSceneEntity;
  1331. if (StartSceneEvent( &info, scene, event, actor, pTarget ))
  1332. {
  1333. m_SceneEvents.AddToTail( info );
  1334. }
  1335. else
  1336. {
  1337. scene->SceneMsg( "C_BaseFlex::AddSceneEvent: event failed\n" );
  1338. // Assert( 0 ); // expression failed to start
  1339. }
  1340. }
  1341. //-----------------------------------------------------------------------------
  1342. // Purpose:
  1343. //-----------------------------------------------------------------------------
  1344. bool C_BaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  1345. {
  1346. switch ( event->GetType() )
  1347. {
  1348. default:
  1349. break;
  1350. case CChoreoEvent::FLEXANIMATION:
  1351. info->InitWeight( this );
  1352. return true;
  1353. case CChoreoEvent::EXPRESSION:
  1354. return true;
  1355. case CChoreoEvent::SEQUENCE:
  1356. if ( info->m_bClientSide )
  1357. {
  1358. return RequestStartSequenceSceneEvent( info, scene, event, actor, pTarget );
  1359. }
  1360. break;
  1361. case CChoreoEvent::SPEAK:
  1362. if ( info->m_bClientSide )
  1363. {
  1364. return true;
  1365. }
  1366. break;
  1367. }
  1368. return false;
  1369. }
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose:
  1372. //-----------------------------------------------------------------------------
  1373. bool C_BaseFlex::RequestStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  1374. {
  1375. info->m_nSequence = LookupSequence( event->GetParameters() );
  1376. // make sure sequence exists
  1377. if ( info->m_nSequence < 0 )
  1378. return false;
  1379. info->m_pActor = actor;
  1380. return true;
  1381. }
  1382. //-----------------------------------------------------------------------------
  1383. // Purpose: Remove expression
  1384. // Input : scenefile -
  1385. // expression -
  1386. //-----------------------------------------------------------------------------
  1387. void C_BaseFlex::RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill )
  1388. {
  1389. Assert( event );
  1390. for ( int i = 0 ; i < m_SceneEvents.Count(); i++ )
  1391. {
  1392. CSceneEventInfo *info = &m_SceneEvents[ i ];
  1393. Assert( info );
  1394. Assert( info->m_pEvent );
  1395. if ( info->m_pScene != scene )
  1396. continue;
  1397. if ( info->m_pEvent != event)
  1398. continue;
  1399. if (ClearSceneEvent( info, fastKill, false ))
  1400. {
  1401. // Free this slot
  1402. info->m_pEvent = NULL;
  1403. info->m_pScene = NULL;
  1404. info->m_bStarted = false;
  1405. m_SceneEvents.Remove( i );
  1406. return;
  1407. }
  1408. }
  1409. // many events refuse to start due to bogus parameters
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Purpose: Checks to see if the event should be considered "completed"
  1413. //-----------------------------------------------------------------------------
  1414. bool C_BaseFlex::CheckSceneEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
  1415. {
  1416. for ( int i = 0 ; i < m_SceneEvents.Count(); i++ )
  1417. {
  1418. CSceneEventInfo *info = &m_SceneEvents[ i ];
  1419. Assert( info );
  1420. Assert( info->m_pEvent );
  1421. if ( info->m_pScene != scene )
  1422. continue;
  1423. if ( info->m_pEvent != event)
  1424. continue;
  1425. return CheckSceneEventCompletion( info, currenttime, scene, event );
  1426. }
  1427. return true;
  1428. }
  1429. bool C_BaseFlex::CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event )
  1430. {
  1431. return true;
  1432. }
  1433. void C_BaseFlex::SetFlexWeight( LocalFlexController_t index, float value )
  1434. {
  1435. if (index >= 0 && index < GetNumFlexControllers())
  1436. {
  1437. CStudioHdr *pstudiohdr = GetModelPtr( );
  1438. if (! pstudiohdr)
  1439. return;
  1440. mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( index );
  1441. if (pflexcontroller->max != pflexcontroller->min)
  1442. {
  1443. value = (value - pflexcontroller->min) / (pflexcontroller->max - pflexcontroller->min);
  1444. value = clamp( value, 0.0, 1.0 );
  1445. }
  1446. Assert( IsFinite( value ) );
  1447. m_flexWeight[ index ] = value;
  1448. }
  1449. }
  1450. float C_BaseFlex::GetFlexWeight( LocalFlexController_t index )
  1451. {
  1452. if (index >= 0 && index < GetNumFlexControllers())
  1453. {
  1454. CStudioHdr *pstudiohdr = GetModelPtr( );
  1455. if (! pstudiohdr)
  1456. return 0;
  1457. mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( index );
  1458. if (pflexcontroller->max != pflexcontroller->min)
  1459. {
  1460. return m_flexWeight[index] * (pflexcontroller->max - pflexcontroller->min) + pflexcontroller->min;
  1461. }
  1462. return m_flexWeight[index];
  1463. }
  1464. return 0.0;
  1465. }
  1466. LocalFlexController_t C_BaseFlex::FindFlexController( const char *szName )
  1467. {
  1468. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  1469. {
  1470. if ( !Q_stricmp( GetFlexControllerName( i ), szName ) )
  1471. return i;
  1472. }
  1473. // AssertMsg( 0, UTIL_VarArgs( "flexcontroller %s couldn't be mapped!!!\n", szName ) );
  1474. return LocalFlexController_t(-1);
  1475. }
  1476. //-----------------------------------------------------------------------------
  1477. // Purpose: Default implementation
  1478. //-----------------------------------------------------------------------------
  1479. void C_BaseFlex::ProcessSceneEvents( bool bFlexEvents, float *pGlobalFlexWeight )
  1480. {
  1481. CStudioHdr *hdr = GetModelPtr();
  1482. if ( !hdr )
  1483. {
  1484. return;
  1485. }
  1486. // slowly decay to netural expression
  1487. if ( bFlexEvents )
  1488. {
  1489. for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  1490. {
  1491. SetFlexWeight( i, GetFlexWeight( i ) * 0.95 );
  1492. }
  1493. }
  1494. // Iterate SceneEvents and look for active slots
  1495. for ( int i = 0; i < m_SceneEvents.Count(); i++ )
  1496. {
  1497. CSceneEventInfo *info = &m_SceneEvents[ i ];
  1498. Assert( info );
  1499. // FIXME: Need a safe handle to m_pEvent in case of memory deletion?
  1500. CChoreoEvent *event = info->m_pEvent;
  1501. Assert( event );
  1502. CChoreoScene *scene = info->m_pScene;
  1503. Assert( scene );
  1504. if ( ProcessSceneEvent( pGlobalFlexWeight, bFlexEvents, info, scene, event ) )
  1505. {
  1506. info->m_bStarted = true;
  1507. }
  1508. }
  1509. }
  1510. //-----------------------------------------------------------------------------
  1511. // Various methods to process facial SceneEvents:
  1512. //-----------------------------------------------------------------------------
  1513. bool C_BaseFlex::ProcessFlexAnimationSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1514. {
  1515. Assert( event->HasEndTime() );
  1516. if ( event->HasEndTime() )
  1517. {
  1518. AddFlexAnimation( info );
  1519. }
  1520. return true;
  1521. }
  1522. bool C_BaseFlex::ProcessFlexSettingSceneEvent( float *pGlobalFlexWeight, CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1523. {
  1524. // Flexanimations have to have an end time!!!
  1525. if ( !event->HasEndTime() )
  1526. return true;
  1527. VPROF( "C_BaseFlex::ProcessFlexSettingSceneEvent" );
  1528. // Look up the actual strings
  1529. const char *scenefile = event->GetParameters();
  1530. const char *name = event->GetParameters2();
  1531. // Have to find both strings
  1532. if ( scenefile && name )
  1533. {
  1534. if ( info->m_pExpHdr == NULL)
  1535. {
  1536. info->m_pExpHdr = ( const flexsettinghdr_t * )FindSceneFile( scenefile );
  1537. }
  1538. if ( info->m_pExpHdr )
  1539. {
  1540. float scenetime = scene->GetTime();
  1541. float scale = event->GetIntensity( scenetime );
  1542. // Add the named expression
  1543. AddFlexSetting( pGlobalFlexWeight, name, scale, info->m_pExpHdr, !info->m_bStarted );
  1544. }
  1545. }
  1546. return true;
  1547. }
  1548. //-----------------------------------------------------------------------------
  1549. // Purpose: Each CBaseFlex maintains a UtlRBTree of mappings, one for each loaded flex scene file it uses. This is used to
  1550. // sort the entries in the RBTree
  1551. // Input : lhs -
  1552. // rhs -
  1553. // Output : Returns true on success, false on failure.
  1554. //-----------------------------------------------------------------------------
  1555. bool C_BaseFlex::FlexSettingLessFunc( const FS_LocalToGlobal_t& lhs, const FS_LocalToGlobal_t& rhs )
  1556. {
  1557. return lhs.m_Key < rhs.m_Key;
  1558. }
  1559. //-----------------------------------------------------------------------------
  1560. // Purpose: Since everyone shared a pSettinghdr now, we need to set up the localtoglobal mapping per entity, but
  1561. // we just do this in memory with an array of integers (could be shorts, I suppose)
  1562. // Input : *pSettinghdr -
  1563. //-----------------------------------------------------------------------------
  1564. void C_BaseFlex::EnsureTranslations( const flexsettinghdr_t *pSettinghdr )
  1565. {
  1566. Assert( pSettinghdr );
  1567. FS_LocalToGlobal_t entry( pSettinghdr );
  1568. unsigned short idx = m_LocalToGlobal.Find( entry );
  1569. if ( idx != m_LocalToGlobal.InvalidIndex() )
  1570. return;
  1571. entry.SetCount( pSettinghdr->numkeys );
  1572. for ( int i = 0; i < pSettinghdr->numkeys; ++i )
  1573. {
  1574. entry.m_Mapping[ i ] = AddGlobalFlexController( pSettinghdr->pLocalName( i ) );
  1575. }
  1576. m_LocalToGlobal.Insert( entry );
  1577. }
  1578. //-----------------------------------------------------------------------------
  1579. // Purpose: Look up instance specific mapping
  1580. // Input : *pSettinghdr -
  1581. // key -
  1582. // Output : int
  1583. //-----------------------------------------------------------------------------
  1584. int C_BaseFlex::FlexControllerLocalToGlobal( const flexsettinghdr_t *pSettinghdr, int key )
  1585. {
  1586. FS_LocalToGlobal_t entry( pSettinghdr );
  1587. int idx = m_LocalToGlobal.Find( entry );
  1588. if ( idx == m_LocalToGlobal.InvalidIndex() )
  1589. {
  1590. // This should never happen!!!
  1591. Assert( 0 );
  1592. Warning( "Unable to find mapping for flexcontroller %i, settings %p on %i/%s\n", key, pSettinghdr, entindex(), GetClassname() );
  1593. EnsureTranslations( pSettinghdr );
  1594. idx = m_LocalToGlobal.Find( entry );
  1595. if ( idx == m_LocalToGlobal.InvalidIndex() )
  1596. {
  1597. Error( "CBaseFlex::FlexControllerLocalToGlobal failed!\n" );
  1598. }
  1599. }
  1600. FS_LocalToGlobal_t& result = m_LocalToGlobal[ idx ];
  1601. // Validate lookup
  1602. Assert( result.m_nCount != 0 && key < result.m_nCount );
  1603. int index = result.m_Mapping[ key ];
  1604. return index;
  1605. }
  1606. //-----------------------------------------------------------------------------
  1607. // Purpose:
  1608. // Input : *expr -
  1609. // scale -
  1610. // *pSettinghdr -
  1611. // newexpression -
  1612. //-----------------------------------------------------------------------------
  1613. void C_BaseFlex::AddFlexSetting( float *pGlobalFlexWeight, const char *expr, float scale,
  1614. const flexsettinghdr_t *pSettinghdr, bool newexpression )
  1615. {
  1616. int i;
  1617. const flexsetting_t *pSetting = NULL;
  1618. // Find the named setting in the base
  1619. for ( i = 0; i < pSettinghdr->numflexsettings; i++ )
  1620. {
  1621. pSetting = pSettinghdr->pSetting( i );
  1622. if ( !pSetting )
  1623. continue;
  1624. const char *name = pSetting->pszName();
  1625. if ( !stricmp( name, expr ) )
  1626. break;
  1627. }
  1628. if ( i >= pSettinghdr->numflexsettings )
  1629. return;
  1630. flexweight_t *pWeights = NULL;
  1631. int truecount = pSetting->psetting( (byte *)pSettinghdr, 0, &pWeights );
  1632. if ( !pWeights )
  1633. return;
  1634. for (i = 0; i < truecount; i++, pWeights++)
  1635. {
  1636. // Translate to local flex controller
  1637. // this is translating from the settings's local index to the models local index
  1638. int index = FlexControllerLocalToGlobal( pSettinghdr, pWeights->key );
  1639. // blend scaled weighting in to total
  1640. float s = clamp( scale * pWeights->influence, 0.0f, 1.0f );
  1641. pGlobalFlexWeight[index] = pGlobalFlexWeight[index] * (1.0f - s) + pWeights->weight * s;
  1642. }
  1643. }
  1644. //-----------------------------------------------------------------------------
  1645. // Purpose:
  1646. //-----------------------------------------------------------------------------
  1647. bool C_BaseFlex::ProcessSceneEvent( float *pGlobalFlexWeight, bool bFlexEvents, CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1648. {
  1649. switch ( event->GetType() )
  1650. {
  1651. default:
  1652. break;
  1653. case CChoreoEvent::FLEXANIMATION:
  1654. if ( bFlexEvents )
  1655. {
  1656. return ProcessFlexAnimationSceneEvent( info, scene, event );
  1657. }
  1658. return true;
  1659. case CChoreoEvent::EXPRESSION:
  1660. if ( !bFlexEvents )
  1661. {
  1662. return ProcessFlexSettingSceneEvent( pGlobalFlexWeight, info, scene, event );
  1663. }
  1664. return true;
  1665. case CChoreoEvent::SEQUENCE:
  1666. if ( info->m_bClientSide )
  1667. {
  1668. if ( !bFlexEvents )
  1669. {
  1670. return ProcessSequenceSceneEvent( info, scene, event );
  1671. }
  1672. return true;
  1673. }
  1674. break;
  1675. case CChoreoEvent::SPEAK:
  1676. if ( info->m_bClientSide )
  1677. {
  1678. return true;
  1679. }
  1680. break;
  1681. }
  1682. return false;
  1683. }
  1684. //-----------------------------------------------------------------------------
  1685. // Purpose:
  1686. // Input : *actor -
  1687. // *parameters -
  1688. //-----------------------------------------------------------------------------
  1689. bool C_BaseFlex::ProcessSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1690. {
  1691. if ( !info || !event || !scene )
  1692. return false;
  1693. SetSequence( info->m_nSequence );
  1694. return true;
  1695. }
  1696. //-----------------------------------------------------------------------------
  1697. // Purpose:
  1698. // Input : *event -
  1699. //-----------------------------------------------------------------------------
  1700. void C_BaseFlex::AddFlexAnimation( CSceneEventInfo *info )
  1701. {
  1702. if ( !info )
  1703. return;
  1704. CChoreoEvent *event = info->m_pEvent;
  1705. if ( !event )
  1706. return;
  1707. CChoreoScene *scene = info->m_pScene;
  1708. if ( !scene )
  1709. return;
  1710. if ( !event->GetTrackLookupSet() )
  1711. {
  1712. // Create lookup data
  1713. for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
  1714. {
  1715. CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
  1716. if ( !track )
  1717. continue;
  1718. if ( track->IsComboType() )
  1719. {
  1720. char name[ 512 ];
  1721. Q_strncpy( name, "right_" ,sizeof(name));
  1722. Q_strncat( name, track->GetFlexControllerName(),sizeof(name), COPY_ALL_CHARACTERS );
  1723. track->SetFlexControllerIndex( MAX( FindFlexController( name ), LocalFlexController_t(0) ), 0, 0 );
  1724. Q_strncpy( name, "left_" ,sizeof(name));
  1725. Q_strncat( name, track->GetFlexControllerName(),sizeof(name), COPY_ALL_CHARACTERS );
  1726. track->SetFlexControllerIndex( MAX( FindFlexController( name ), LocalFlexController_t(0) ), 0, 1 );
  1727. }
  1728. else
  1729. {
  1730. track->SetFlexControllerIndex( MAX( FindFlexController( (char *)track->GetFlexControllerName() ), LocalFlexController_t(0)), 0 );
  1731. }
  1732. }
  1733. event->SetTrackLookupSet( true );
  1734. }
  1735. if ( !scene_clientflex.GetBool() )
  1736. return;
  1737. float scenetime = scene->GetTime();
  1738. float weight = event->GetIntensity( scenetime );
  1739. // decay if this is a background scene and there's other flex animations playing
  1740. weight = weight * info->UpdateWeight( this );
  1741. // Compute intensity for each track in animation and apply
  1742. // Iterate animation tracks
  1743. for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
  1744. {
  1745. CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
  1746. if ( !track )
  1747. continue;
  1748. // Disabled
  1749. if ( !track->IsTrackActive() )
  1750. continue;
  1751. // Map track flex controller to global name
  1752. if ( track->IsComboType() )
  1753. {
  1754. for ( int side = 0; side < 2; side++ )
  1755. {
  1756. LocalFlexController_t controller = track->GetRawFlexControllerIndex( side );
  1757. // Get spline intensity for controller
  1758. float flIntensity = track->GetIntensity( scenetime, side );
  1759. if ( controller >= LocalFlexController_t(0) )
  1760. {
  1761. float orig = GetFlexWeight( controller );
  1762. float value = orig * (1 - weight) + flIntensity * weight;
  1763. SetFlexWeight( controller, value );
  1764. }
  1765. }
  1766. }
  1767. else
  1768. {
  1769. LocalFlexController_t controller = track->GetRawFlexControllerIndex( 0 );
  1770. // Get spline intensity for controller
  1771. float flIntensity = track->GetIntensity( scenetime, 0 );
  1772. if ( controller >= LocalFlexController_t(0) )
  1773. {
  1774. float orig = GetFlexWeight( controller );
  1775. float value = orig * (1 - weight) + flIntensity * weight;
  1776. SetFlexWeight( controller, value );
  1777. }
  1778. }
  1779. }
  1780. info->m_bStarted = true;
  1781. }
  1782. void CSceneEventInfo::InitWeight( C_BaseFlex *pActor )
  1783. {
  1784. m_flWeight = 1.0;
  1785. }
  1786. //-----------------------------------------------------------------------------
  1787. // Purpose: update weight for background events. Only call once per think
  1788. //-----------------------------------------------------------------------------
  1789. float CSceneEventInfo::UpdateWeight( C_BaseFlex *pActor )
  1790. {
  1791. m_flWeight = MIN( m_flWeight + 0.1, 1.0 );
  1792. return m_flWeight;
  1793. }
  1794. BEGIN_BYTESWAP_DATADESC( flexsettinghdr_t )
  1795. DEFINE_FIELD( id, FIELD_INTEGER ),
  1796. DEFINE_FIELD( version, FIELD_INTEGER ),
  1797. DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ),
  1798. DEFINE_FIELD( length, FIELD_INTEGER ),
  1799. DEFINE_FIELD( numflexsettings, FIELD_INTEGER ),
  1800. DEFINE_FIELD( flexsettingindex, FIELD_INTEGER ),
  1801. DEFINE_FIELD( nameindex, FIELD_INTEGER ),
  1802. DEFINE_FIELD( numindexes, FIELD_INTEGER ),
  1803. DEFINE_FIELD( indexindex, FIELD_INTEGER ),
  1804. DEFINE_FIELD( numkeys, FIELD_INTEGER ),
  1805. DEFINE_FIELD( keynameindex, FIELD_INTEGER ),
  1806. DEFINE_FIELD( keymappingindex, FIELD_INTEGER ),
  1807. END_BYTESWAP_DATADESC()
  1808. BEGIN_BYTESWAP_DATADESC( flexsetting_t )
  1809. DEFINE_FIELD( nameindex, FIELD_INTEGER ),
  1810. DEFINE_FIELD( obsolete1, FIELD_INTEGER ),
  1811. DEFINE_FIELD( numsettings, FIELD_INTEGER ),
  1812. DEFINE_FIELD( index, FIELD_INTEGER ),
  1813. DEFINE_FIELD( obsolete2, FIELD_INTEGER ),
  1814. DEFINE_FIELD( settingindex, FIELD_INTEGER ),
  1815. END_BYTESWAP_DATADESC()
  1816. BEGIN_BYTESWAP_DATADESC( flexweight_t )
  1817. DEFINE_FIELD( key, FIELD_INTEGER ),
  1818. DEFINE_FIELD( weight, FIELD_FLOAT ),
  1819. DEFINE_FIELD( influence, FIELD_FLOAT ),
  1820. END_BYTESWAP_DATADESC()