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.

2096 lines
61 KiB

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