2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
# include "cbase.h"
# include "filesystem.h"
# include "sentence.h"
# include "hud_closecaption.h"
# include "engine/ivmodelinfo.h"
# include "engine/ivdebugoverlay.h"
# include "bone_setup.h"
# include "soundinfo.h"
# include "tools/bonelist.h"
# include "KeyValues.h"
# include "tier0/vprof.h"
# include "toolframework/itoolframework.h"
# include "choreoevent.h"
# include "choreoscene.h"
# include "choreoactor.h"
# include "toolframework_client.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
bool UseHWMorphVCDs ( ) ;
ConVar g_CV_PhonemeDelay ( " phonemedelay " , " 0 " , 0 , " Phoneme delay to account for sound system latency. " ) ;
ConVar g_CV_PhonemeFilter ( " phonemefilter " , " 0.08 " , 0 , " Time duration of box filter to pass over phonemes. " ) ;
ConVar g_CV_FlexRules ( " flex_rules " , " 1 " , 0 , " Allow flex animation rules to run. " ) ;
ConVar g_CV_BlinkDuration ( " blink_duration " , " 0.2 " , 0 , " How many seconds an eye blink will last. " ) ;
ConVar g_CV_FlexSmooth ( " flex_smooth " , " 1 " , 0 , " Applies smoothing/decay curve to flex animation controller changes. " ) ;
# if defined( CBaseFlex )
# undef CBaseFlex
# endif
IMPLEMENT_CLIENTCLASS_DT ( C_BaseFlex , DT_BaseFlex , CBaseFlex )
RecvPropArray3 ( RECVINFO_ARRAY ( m_flexWeight ) , RecvPropFloat ( RECVINFO ( m_flexWeight [ 0 ] ) ) ) ,
RecvPropInt ( RECVINFO ( m_blinktoggle ) ) ,
RecvPropVector ( RECVINFO ( m_viewtarget ) ) ,
# ifdef HL2_CLIENT_DLL
RecvPropFloat ( RECVINFO ( m_vecViewOffset [ 0 ] ) ) ,
RecvPropFloat ( RECVINFO ( m_vecViewOffset [ 1 ] ) ) ,
RecvPropFloat ( RECVINFO ( m_vecViewOffset [ 2 ] ) ) ,
RecvPropVector ( RECVINFO ( m_vecLean ) ) ,
RecvPropVector ( RECVINFO ( m_vecShift ) ) ,
# endif
END_RECV_TABLE ( )
BEGIN_PREDICTION_DATA ( C_BaseFlex )
/*
// DEFINE_FIELD( C_BaseFlex, m_viewtarget, FIELD_VECTOR ),
// DEFINE_ARRAY( C_BaseFlex, m_flexWeight, FIELD_FLOAT, 64 ),
// DEFINE_FIELD( C_BaseFlex, m_blinktoggle, FIELD_INTEGER ),
// DEFINE_FIELD( C_BaseFlex, m_blinktime, FIELD_FLOAT ),
// DEFINE_FIELD( C_BaseFlex, m_prevviewtarget, FIELD_VECTOR ),
// DEFINE_ARRAY( C_BaseFlex, m_prevflexWeight, FIELD_FLOAT, 64 ),
// DEFINE_FIELD( C_BaseFlex, m_prevblinktoggle, FIELD_INTEGER ),
// DEFINE_FIELD( C_BaseFlex, m_iBlink, FIELD_INTEGER ),
// DEFINE_FIELD( C_BaseFlex, m_iEyeUpdown, FIELD_INTEGER ),
// DEFINE_FIELD( C_BaseFlex, m_iEyeRightleft, FIELD_INTEGER ),
// DEFINE_FIELD( C_BaseFlex, m_FileList, CUtlVector < CFlexSceneFile * > ),
*/
END_PREDICTION_DATA ( )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool GetHWMExpressionFileName ( const char * pFilename , char * pHWMFilename )
{
// Are we even using hardware morph?
if ( ! UseHWMorphVCDs ( ) )
return false ;
// Do we have a valid filename?
if ( ! ( pFilename & & pFilename [ 0 ] ) )
return false ;
// Check to see if we already have an player/hwm/* filename.
if ( ( V_strstr ( pFilename , " player/hwm " ) ! = NULL ) | | ( V_strstr ( pFilename , " player \\ hwm " ) ! = NULL ) )
{
V_strcpy ( pHWMFilename , pFilename ) ;
return true ;
}
// Find the hardware morph scene name and pass that along as well.
char szExpression [ MAX_PATH ] ;
2022-03-01 20:00:42 +00:00
V_strcpy ( szExpression , pFilename ) ;
2020-04-22 16:56:21 +00:00
char szExpressionHWM [ MAX_PATH ] ;
szExpressionHWM [ 0 ] = ' \0 ' ;
char * pszToken = strtok ( szExpression , " / \\ " ) ;
while ( pszToken ! = NULL )
{
V_strcat ( szExpressionHWM , pszToken , sizeof ( szExpressionHWM ) ) ;
if ( ! V_stricmp ( pszToken , " player " ) )
{
V_strcat ( szExpressionHWM , " \\ hwm " , sizeof ( szExpressionHWM ) ) ;
}
pszToken = strtok ( NULL , " / \\ " ) ;
if ( pszToken ! = NULL )
{
V_strcat ( szExpressionHWM , " \\ " , sizeof ( szExpressionHWM ) ) ;
}
}
V_strcpy ( pHWMFilename , szExpressionHWM ) ;
return true ;
}
C_BaseFlex : : C_BaseFlex ( ) :
m_iv_viewtarget ( " C_BaseFlex::m_iv_viewtarget " ) ,
m_iv_flexWeight ( " C_BaseFlex:m_iv_flexWeight " ) ,
# ifdef HL2_CLIENT_DLL
m_iv_vecLean ( " C_BaseFlex:m_iv_vecLean " ) ,
m_iv_vecShift ( " C_BaseFlex:m_iv_vecShift " ) ,
# endif
m_LocalToGlobal ( 0 , 0 , FlexSettingLessFunc )
{
# ifdef _DEBUG
( ( Vector & ) m_viewtarget ) . Init ( ) ;
# endif
AddVar ( & m_viewtarget , & m_iv_viewtarget , LATCH_ANIMATION_VAR | INTERPOLATE_LINEAR_ONLY ) ;
AddVar ( m_flexWeight , & m_iv_flexWeight , LATCH_ANIMATION_VAR ) ;
// Fill in phoneme class lookup
SetupMappings ( " phonemes " ) ;
m_flFlexDelayedWeight = NULL ;
m_cFlexDelayedWeight = 0 ;
/// Make sure size is correct
Assert ( PHONEME_CLASS_STRONG + 1 = = NUM_PHONEME_CLASSES ) ;
# ifdef HL2_CLIENT_DLL
// Get general lean vector
AddVar ( & m_vecLean , & m_iv_vecLean , LATCH_ANIMATION_VAR ) ;
AddVar ( & m_vecShift , & m_iv_vecShift , LATCH_ANIMATION_VAR ) ;
# endif
}
C_BaseFlex : : ~ C_BaseFlex ( )
{
delete [ ] m_flFlexDelayedWeight ;
m_SceneEvents . RemoveAll ( ) ;
m_LocalToGlobal . RemoveAll ( ) ;
}
void C_BaseFlex : : Spawn ( )
{
BaseClass : : Spawn ( ) ;
InitPhonemeMappings ( ) ;
}
// TF Player overrides all of these with class specific files
void C_BaseFlex : : InitPhonemeMappings ( )
{
SetupMappings ( " phonemes " ) ;
}
void C_BaseFlex : : SetupMappings ( char const * pchFileRoot )
{
// Fill in phoneme class lookup
memset ( m_PhonemeClasses , 0 , sizeof ( m_PhonemeClasses ) ) ;
Emphasized_Phoneme * normal = & m_PhonemeClasses [ PHONEME_CLASS_NORMAL ] ;
Q_snprintf ( normal - > classname , sizeof ( normal - > classname ) , " %s " , pchFileRoot ) ;
normal - > required = true ;
Emphasized_Phoneme * weak = & m_PhonemeClasses [ PHONEME_CLASS_WEAK ] ;
Q_snprintf ( weak - > classname , sizeof ( weak - > classname ) , " %s_weak " , pchFileRoot ) ;
Emphasized_Phoneme * strong = & m_PhonemeClasses [ PHONEME_CLASS_STRONG ] ;
Q_snprintf ( strong - > classname , sizeof ( strong - > classname ) , " %s_strong " , pchFileRoot ) ;
}
//-----------------------------------------------------------------------------
// Purpose: initialize fast lookups when model changes
//-----------------------------------------------------------------------------
CStudioHdr * C_BaseFlex : : OnNewModel ( )
{
CStudioHdr * hdr = BaseClass : : OnNewModel ( ) ;
// init to invalid setting
m_iBlink = - 1 ;
m_iEyeUpdown = LocalFlexController_t ( - 1 ) ;
m_iEyeRightleft = LocalFlexController_t ( - 1 ) ;
m_bSearchedForEyeFlexes = false ;
m_iMouthAttachment = 0 ;
delete [ ] m_flFlexDelayedWeight ;
m_flFlexDelayedWeight = NULL ;
m_cFlexDelayedWeight = 0 ;
if ( hdr )
{
if ( hdr - > numflexdesc ( ) )
{
m_cFlexDelayedWeight = hdr - > numflexdesc ( ) ;
m_flFlexDelayedWeight = new float [ m_cFlexDelayedWeight ] ;
memset ( m_flFlexDelayedWeight , 0 , sizeof ( float ) * m_cFlexDelayedWeight ) ;
}
m_iv_flexWeight . SetMaxCount ( hdr - > numflexcontrollers ( ) ) ;
m_iMouthAttachment = LookupAttachment ( " mouth " ) ;
LinkToGlobalFlexControllers ( hdr ) ;
}
return hdr ;
}
void C_BaseFlex : : StandardBlendingRules ( CStudioHdr * hdr , Vector pos [ ] , Quaternion q [ ] , float currentTime , int boneMask )
{
BaseClass : : StandardBlendingRules ( hdr , pos , q , currentTime , boneMask ) ;
# ifdef HL2_CLIENT_DLL
// shift pelvis, rotate body
if ( hdr - > GetNumIKChains ( ) ! = 0 & & ( m_vecShift . x ! = 0.0 | | m_vecShift . y ! = 0.0 ) )
{
//CIKContext auto_ik;
//auto_ik.Init( hdr, GetRenderAngles(), GetRenderOrigin(), currentTime, gpGlobals->framecount, boneMask );
//auto_ik.AddAllLocks( pos, q );
matrix3x4_t rootxform ;
AngleMatrix ( GetRenderAngles ( ) , GetRenderOrigin ( ) , rootxform ) ;
Vector localShift ;
VectorIRotate ( m_vecShift , rootxform , localShift ) ;
Vector localLean ;
VectorIRotate ( m_vecLean , rootxform , localLean ) ;
Vector p0 = pos [ 0 ] ;
float length = VectorNormalize ( p0 ) ;
// shift the root bone, but keep the height off the origin the same
Vector shiftPos = pos [ 0 ] + localShift ;
VectorNormalize ( shiftPos ) ;
Vector leanPos = pos [ 0 ] + localLean ;
VectorNormalize ( leanPos ) ;
pos [ 0 ] = shiftPos * length ;
// rotate the root bone based on how much it was "leaned"
Vector p1 ;
CrossProduct ( p0 , leanPos , p1 ) ;
float sinAngle = VectorNormalize ( p1 ) ;
float cosAngle = DotProduct ( p0 , leanPos ) ;
float angle = atan2 ( sinAngle , cosAngle ) * 180 / M_PI ;
Quaternion q1 ;
angle = clamp ( angle , - 45 , 45 ) ;
AxisAngleQuaternion ( p1 , angle , q1 ) ;
QuaternionMult ( q1 , q [ 0 ] , q [ 0 ] ) ;
QuaternionNormalize ( q [ 0 ] ) ;
// DevMsgRT( " (%.2f) %.2f %.2f %.2f\n", angle, p1.x, p1.y, p1.z );
// auto_ik.SolveAllLocks( pos, q );
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: place "voice" sounds on mouth
//-----------------------------------------------------------------------------
bool C_BaseFlex : : GetSoundSpatialization ( SpatializationInfo_t & info )
{
bool bret = BaseClass : : GetSoundSpatialization ( info ) ;
// Default things it's audible, put it at a better spot?
if ( bret )
{
if ( ( info . info . nChannel = = CHAN_VOICE | | info . info . nChannel = = CHAN_VOICE2 ) & & m_iMouthAttachment > 0 )
{
Vector origin ;
QAngle angles ;
C_BaseAnimating : : AutoAllowBoneAccess boneaccess ( true , false ) ;
if ( GetAttachment ( m_iMouthAttachment , origin , angles ) )
{
if ( info . pOrigin )
{
* info . pOrigin = origin ;
}
if ( info . pAngles )
{
* info . pAngles = angles ;
}
}
}
}
return bret ;
}
//-----------------------------------------------------------------------------
// Purpose: run the interpreted FAC's expressions, converting global flex_controller
// values into FAC weights
//-----------------------------------------------------------------------------
void C_BaseFlex : : RunFlexRules ( CStudioHdr * hdr , float * dest )
{
if ( ! g_CV_FlexRules . GetInt ( ) )
return ;
if ( ! hdr )
return ;
/*
// 0 means run them all
int nFlexRulesToRun = 0 ;
const char * pszExpression = flex_expression . GetString ( ) ;
if ( pszExpression )
{
nFlexRulesToRun = atoi ( pszExpression ) ; // 0 will be returned if not a numeric string
}
//*/
hdr - > RunFlexRules ( g_flexweight , dest ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CFlexSceneFileManager : : Init ( )
{
// Trakcer 16692: Preload these at startup to avoid hitch first time we try to load them during actual gameplay
FindSceneFile ( NULL , " phonemes " , true ) ;
FindSceneFile ( NULL , " phonemes_weak " , true ) ;
FindSceneFile ( NULL , " phonemes_strong " , true ) ;
# if defined( HL2_CLIENT_DLL )
FindSceneFile ( NULL , " random " , true ) ;
FindSceneFile ( NULL , " randomAlert " , true ) ;
# endif
# if defined( TF_CLIENT_DLL )
// HACK TO ALL TF TO HAVE PER CLASS OVERRIDES
char const * pTFClasses [ ] =
{
" scout " ,
" sniper " ,
" soldier " ,
" demo " ,
" medic " ,
" heavy " ,
" pyro " ,
" spy " ,
" engineer " ,
} ;
char fn [ MAX_PATH ] ;
for ( int i = 0 ; i < ARRAYSIZE ( pTFClasses ) ; + + i )
{
Q_snprintf ( fn , sizeof ( fn ) , " player/%s/phonemes/phonemes " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
Q_snprintf ( fn , sizeof ( fn ) , " player/%s/phonemes/phonemes_weak " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
Q_snprintf ( fn , sizeof ( fn ) , " player/%s/phonemes/phonemes_strong " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
if ( ! IsX360 ( ) )
{
Q_snprintf ( fn , sizeof ( fn ) , " player/hwm/%s/phonemes/phonemes " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
Q_snprintf ( fn , sizeof ( fn ) , " player/hwm/%s/phonemes/phonemes_weak " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
Q_snprintf ( fn , sizeof ( fn ) , " player/hwm/%s/phonemes/phonemes_strong " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
}
Q_snprintf ( fn , sizeof ( fn ) , " player/%s/emotion/emotion " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
if ( ! IsX360 ( ) )
{
Q_snprintf ( fn , sizeof ( fn ) , " player/hwm/%s/emotion/emotion " , pTFClasses [ i ] ) ;
FindSceneFile ( NULL , fn , true ) ;
}
}
# endif
return true ;
}
//-----------------------------------------------------------------------------
// Tracker 14992: We used to load 18K of .vfes for every C_BaseFlex who lipsynced, but now we only load those files once globally.
// 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
// so I'll just leave them loaded forever for now
//-----------------------------------------------------------------------------
void CFlexSceneFileManager : : Shutdown ( )
{
DeleteSceneFiles ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets up translations
// Input : *instance -
// *pSettinghdr -
// Output : void
//-----------------------------------------------------------------------------
void CFlexSceneFileManager : : EnsureTranslations ( IHasLocalToGlobalFlexSettings * instance , const flexsettinghdr_t * pSettinghdr )
{
// The only time instance is NULL is in Init() above, where we're just loading the .vfe files off of the hard disk.
if ( instance )
{
instance - > EnsureTranslations ( pSettinghdr ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void * CFlexSceneFileManager : : FindSceneFile ( IHasLocalToGlobalFlexSettings * instance , const char * filename , bool allowBlockingIO )
{
char szFilename [ MAX_PATH ] ;
Assert ( V_strlen ( filename ) < MAX_PATH ) ;
2022-03-01 20:00:42 +00:00
V_strcpy ( szFilename , filename ) ;
2020-04-22 16:56:21 +00:00
# if defined( TF_CLIENT_DLL )
char szHWMFilename [ MAX_PATH ] ;
if ( GetHWMExpressionFileName ( szFilename , szHWMFilename ) )
{
2022-03-01 20:00:42 +00:00
V_strcpy ( szFilename , szHWMFilename ) ;
2020-04-22 16:56:21 +00:00
}
# endif
Q_FixSlashes ( szFilename ) ;
// See if it's already loaded
2022-03-01 20:00:42 +00:00
int i ;
for ( i = 0 ; i < m_FileList . Count ( ) ; i + + )
2020-04-22 16:56:21 +00:00
{
CFlexSceneFile * file = m_FileList [ i ] ;
if ( file & & ! Q_stricmp ( file - > filename , szFilename ) )
{
// Make sure translations (local to global flex controller) are set up for this instance
EnsureTranslations ( instance , ( const flexsettinghdr_t * ) file - > buffer ) ;
return file - > buffer ;
}
}
if ( ! allowBlockingIO )
{
return NULL ;
}
// Load file into memory
void * buffer = NULL ;
int len = filesystem - > ReadFileEx ( VarArgs ( " expressions/%s.vfe " , szFilename ) , " GAME " , & buffer ) ;
if ( ! len )
return NULL ;
// Create scene entry
CFlexSceneFile * pfile = new CFlexSceneFile ;
// Remember filename
Q_strncpy ( pfile - > filename , szFilename , sizeof ( pfile - > filename ) ) ;
// Remember data pointer
pfile - > buffer = buffer ;
// Add to list
m_FileList . AddToTail ( pfile ) ;
// Swap the entire file
if ( IsX360 ( ) )
{
CByteswap swap ;
swap . ActivateByteSwapping ( true ) ;
byte * pData = ( byte * ) buffer ;
flexsettinghdr_t * pHdr = ( flexsettinghdr_t * ) pData ;
swap . SwapFieldsToTargetEndian ( pHdr ) ;
// Flex Settings
flexsetting_t * pFlexSetting = ( flexsetting_t * ) ( ( byte * ) pHdr + pHdr - > flexsettingindex ) ;
for ( int i = 0 ; i < pHdr - > numflexsettings ; + + i , + + pFlexSetting )
{
swap . SwapFieldsToTargetEndian ( pFlexSetting ) ;
flexweight_t * pWeight = ( flexweight_t * ) ( ( ( byte * ) pFlexSetting ) + pFlexSetting - > settingindex ) ;
for ( int j = 0 ; j < pFlexSetting - > numsettings ; + + j , + + pWeight )
{
swap . SwapFieldsToTargetEndian ( pWeight ) ;
}
}
// indexes
pData = ( byte * ) pHdr + pHdr - > indexindex ;
swap . SwapBufferToTargetEndian ( ( int * ) pData , ( int * ) pData , pHdr - > numindexes ) ;
// keymappings
pData = ( byte * ) pHdr + pHdr - > keymappingindex ;
swap . SwapBufferToTargetEndian ( ( int * ) pData , ( int * ) pData , pHdr - > numkeys ) ;
// keyname indices
pData = ( byte * ) pHdr + pHdr - > keynameindex ;
swap . SwapBufferToTargetEndian ( ( int * ) pData , ( int * ) pData , pHdr - > numkeys ) ;
}
// Fill in translation table
EnsureTranslations ( instance , ( const flexsettinghdr_t * ) pfile - > buffer ) ;
// Return data
return pfile - > buffer ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlexSceneFileManager : : DeleteSceneFiles ( )
{
while ( m_FileList . Count ( ) > 0 )
{
CFlexSceneFile * file = m_FileList [ 0 ] ;
m_FileList . Remove ( 0 ) ;
free ( file - > buffer ) ;
delete file ;
}
}
CFlexSceneFileManager g_FlexSceneFileManager ;
//-----------------------------------------------------------------------------
// Purpose:
// Input : *filename -
//-----------------------------------------------------------------------------
void * C_BaseFlex : : FindSceneFile ( const char * filename )
{
return g_FlexSceneFileManager . FindSceneFile ( this , filename , false ) ;
}
//-----------------------------------------------------------------------------
// Purpose: make sure the eyes are within 30 degrees of forward
//-----------------------------------------------------------------------------
Vector C_BaseFlex : : SetViewTarget ( CStudioHdr * pStudioHdr )
{
if ( ! pStudioHdr )
return Vector ( 0 , 0 , 0 ) ;
// aim the eyes
Vector tmp = m_viewtarget ;
if ( ! m_bSearchedForEyeFlexes )
{
m_bSearchedForEyeFlexes = true ;
m_iEyeUpdown = FindFlexController ( " eyes_updown " ) ;
m_iEyeRightleft = FindFlexController ( " eyes_rightleft " ) ;
2022-03-01 20:00:42 +00:00
if ( m_iEyeUpdown ! = - 1 )
2020-04-22 16:56:21 +00:00
{
pStudioHdr - > pFlexcontroller ( m_iEyeUpdown ) - > localToGlobal = AddGlobalFlexController ( " eyes_updown " ) ;
}
2022-03-01 20:00:42 +00:00
if ( m_iEyeRightleft ! = - 1 )
2020-04-22 16:56:21 +00:00
{
pStudioHdr - > pFlexcontroller ( m_iEyeRightleft ) - > localToGlobal = AddGlobalFlexController ( " eyes_rightleft " ) ;
}
}
if ( m_iEyeAttachment > 0 )
{
matrix3x4_t attToWorld ;
if ( ! GetAttachment ( m_iEyeAttachment , attToWorld ) )
{
return Vector ( 0 , 0 , 0 ) ;
}
Vector local ;
VectorITransform ( tmp , attToWorld , local ) ;
// FIXME: clamp distance to something based on eyeball distance
if ( local . x < 6 )
{
local . x = 6 ;
}
float flDist = local . Length ( ) ;
VectorNormalize ( local ) ;
// calculate animated eye deflection
Vector eyeDeflect ;
QAngle eyeAng ( 0 , 0 , 0 ) ;
2022-03-01 20:00:42 +00:00
if ( m_iEyeUpdown ! = - 1 )
2020-04-22 16:56:21 +00:00
{
mstudioflexcontroller_t * pflex = pStudioHdr - > pFlexcontroller ( m_iEyeUpdown ) ;
eyeAng . x = g_flexweight [ pflex - > localToGlobal ] ;
}
2022-03-01 20:00:42 +00:00
if ( m_iEyeRightleft ! = - 1 )
2020-04-22 16:56:21 +00:00
{
mstudioflexcontroller_t * pflex = pStudioHdr - > pFlexcontroller ( m_iEyeRightleft ) ;
eyeAng . y = g_flexweight [ pflex - > localToGlobal ] ;
}
// debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%5.3f %5.3f", eyeAng.x, eyeAng.y );
AngleVectors ( eyeAng , & eyeDeflect ) ;
eyeDeflect . x = 0 ;
// reduce deflection the more the eye is off center
// FIXME: this angles make no damn sense
eyeDeflect = eyeDeflect * ( local . x * local . x ) ;
local = local + eyeDeflect ;
VectorNormalize ( local ) ;
// check to see if the eye is aiming outside the max eye deflection
float flMaxEyeDeflection = pStudioHdr - > MaxEyeDeflection ( ) ;
if ( local . x < flMaxEyeDeflection )
{
// if so, clamp it to 30 degrees offset
// debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 1, 0, "%5.3f %5.3f %5.3f", local.x, local.y, local.z );
local . x = 0 ;
float d = local . LengthSqr ( ) ;
if ( d > 0.0f )
{
d = sqrtf ( ( 1.0f - flMaxEyeDeflection * flMaxEyeDeflection ) / ( local . y * local . y + local . z * local . z ) ) ;
local . x = flMaxEyeDeflection ;
local . y = local . y * d ;
local . z = local . z * d ;
}
else
{
local . x = 1.0 ;
}
}
local = local * flDist ;
VectorTransform ( local , attToWorld , tmp ) ;
}
modelrender - > SetViewTarget ( GetModelPtr ( ) , GetBody ( ) , tmp ) ;
/*
debugoverlay - > AddTextOverlay ( GetAbsOrigin ( ) + Vector ( 0 , 0 , 64 ) , 0 , 0 , " %.2f %.2f %.2f : %.2f %.2f %.2f " ,
m_viewtarget . x , m_viewtarget . y , m_viewtarget . z ,
m_prevviewtarget . x , m_prevviewtarget . y , m_prevviewtarget . z ) ;
*/
return tmp ;
}
# define STRONG_CROSSFADE_START 0.60f
# define WEAK_CROSSFADE_START 0.40f
//-----------------------------------------------------------------------------
// Purpose:
// Here's the formula
// 0.5 is neutral 100 % of the default setting
// Crossfade starts at STRONG_CROSSFADE_START and is full at STRONG_CROSSFADE_END
// If there isn't a strong then the intensity of the underlying phoneme is fixed at 2 x STRONG_CROSSFADE_START
// so we don't get huge numbers
// Input : *classes -
// emphasis_intensity -
//-----------------------------------------------------------------------------
void C_BaseFlex : : ComputeBlendedSetting ( Emphasized_Phoneme * classes , float emphasis_intensity )
{
// See which blends are available for the current phoneme
bool has_weak = classes [ PHONEME_CLASS_WEAK ] . valid ;
bool has_strong = classes [ PHONEME_CLASS_STRONG ] . valid ;
// Better have phonemes in general
Assert ( classes [ PHONEME_CLASS_NORMAL ] . valid ) ;
if ( emphasis_intensity > STRONG_CROSSFADE_START )
{
if ( has_strong )
{
// Blend in some of strong
float dist_remaining = 1.0f - emphasis_intensity ;
float frac = dist_remaining / ( 1.0f - STRONG_CROSSFADE_START ) ;
classes [ PHONEME_CLASS_NORMAL ] . amount = ( frac ) * 2.0f * STRONG_CROSSFADE_START ;
classes [ PHONEME_CLASS_STRONG ] . amount = 1.0f - frac ;
}
else
{
emphasis_intensity = MIN ( emphasis_intensity , STRONG_CROSSFADE_START ) ;
classes [ PHONEME_CLASS_NORMAL ] . amount = 2.0f * emphasis_intensity ;
}
}
else if ( emphasis_intensity < WEAK_CROSSFADE_START )
{
if ( has_weak )
{
// Blend in some weak
float dist_remaining = WEAK_CROSSFADE_START - emphasis_intensity ;
float frac = dist_remaining / ( WEAK_CROSSFADE_START ) ;
classes [ PHONEME_CLASS_NORMAL ] . amount = ( 1.0f - frac ) * 2.0f * WEAK_CROSSFADE_START ;
classes [ PHONEME_CLASS_WEAK ] . amount = frac ;
}
else
{
emphasis_intensity = MAX ( emphasis_intensity , WEAK_CROSSFADE_START ) ;
classes [ PHONEME_CLASS_NORMAL ] . amount = 2.0f * emphasis_intensity ;
}
}
else
{
// Assume 0.5 (neutral) becomes a scaling of 1.0f
classes [ PHONEME_CLASS_NORMAL ] . amount = 2.0f * emphasis_intensity ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *classes -
// phoneme -
// scale -
// newexpression -
//-----------------------------------------------------------------------------
void C_BaseFlex : : AddViseme ( Emphasized_Phoneme * classes , float emphasis_intensity , int phoneme , float scale , bool newexpression )
{
int type ;
// Setup weights for any emphasis blends
bool skip = SetupEmphasisBlend ( classes , phoneme ) ;
// Uh-oh, missing or unknown phoneme???
if ( skip )
{
return ;
}
// Compute blend weights
ComputeBlendedSetting ( classes , emphasis_intensity ) ;
for ( type = 0 ; type < NUM_PHONEME_CLASSES ; type + + )
{
Emphasized_Phoneme * info = & classes [ type ] ;
if ( ! info - > valid | | info - > amount = = 0.0f )
continue ;
const flexsettinghdr_t * actual_flexsetting_header = info - > base ;
const flexsetting_t * pSetting = actual_flexsetting_header - > pIndexedSetting ( phoneme ) ;
if ( ! pSetting )
{
continue ;
}
flexweight_t * pWeights = NULL ;
int truecount = pSetting - > psetting ( ( byte * ) actual_flexsetting_header , 0 , & pWeights ) ;
if ( pWeights )
{
for ( int i = 0 ; i < truecount ; i + + )
{
// Translate to global controller number
int j = FlexControllerLocalToGlobal ( actual_flexsetting_header , pWeights - > key ) ;
// Add scaled weighting in
g_flexweight [ j ] + = info - > amount * scale * pWeights - > weight ;
// Go to next setting
pWeights + + ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: A lot of the one time setup and also resets amount to 0.0f default
// for strong/weak/normal tracks
// Returning true == skip this phoneme
// Input : *classes -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_BaseFlex : : SetupEmphasisBlend ( Emphasized_Phoneme * classes , int phoneme )
{
int i ;
bool skip = false ;
for ( i = 0 ; i < NUM_PHONEME_CLASSES ; i + + )
{
Emphasized_Phoneme * info = & classes [ i ] ;
// Assume it's bogus
info - > valid = false ;
info - > amount = 0.0f ;
// One time setup
if ( ! info - > basechecked )
{
info - > basechecked = true ;
info - > base = ( flexsettinghdr_t * ) FindSceneFile ( info - > classname ) ;
}
info - > exp = NULL ;
if ( info - > base )
{
Assert ( info - > base - > id = = ( ' V ' < < 16 ) + ( ' F ' < < 8 ) + ( ' E ' ) ) ;
info - > exp = info - > base - > pIndexedSetting ( phoneme ) ;
}
if ( info - > required & & ( ! info - > base | | ! info - > exp ) )
{
skip = true ;
break ;
}
if ( info - > exp )
{
info - > valid = true ;
}
}
return skip ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *classes -
// *sentence -
// t -
// dt -
// juststarted -
//-----------------------------------------------------------------------------
ConVar g_CV_PhonemeSnap ( " phonemesnap " , " 2 " , 0 , " Lod at level at which visemes stops always considering two phonemes, regardless of duration. " ) ;
void C_BaseFlex : : AddVisemesForSentence ( Emphasized_Phoneme * classes , float emphasis_intensity , CSentence * sentence , float t , float dt , bool juststarted )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
int pcount = sentence - > GetRuntimePhonemeCount ( ) ;
for ( int k = 0 ; k < pcount ; k + + )
{
const CBasePhonemeTag * phoneme = sentence - > GetRuntimePhoneme ( k ) ;
if ( t > phoneme - > GetStartTime ( ) & & t < phoneme - > GetEndTime ( ) )
{
bool bCrossfade = true ;
if ( ( hdr - > flags ( ) & STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE ) = = 0 )
{
if ( m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD0 )
{
bCrossfade = ( g_CV_PhonemeSnap . GetInt ( ) > 0 ) ;
}
else if ( m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD1 )
{
bCrossfade = ( g_CV_PhonemeSnap . GetInt ( ) > 1 ) ;
}
else if ( m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD2 )
{
bCrossfade = ( g_CV_PhonemeSnap . GetInt ( ) > 2 ) ;
}
else if ( m_iAccumulatedBoneMask & BONE_USED_BY_VERTEX_LOD3 )
{
bCrossfade = ( g_CV_PhonemeSnap . GetInt ( ) > 3 ) ;
}
else
{
bCrossfade = false ;
}
}
if ( bCrossfade )
{
if ( k < pcount - 1 )
{
const CBasePhonemeTag * next = sentence - > GetRuntimePhoneme ( k + 1 ) ;
// if I have a neighbor
if ( next )
{
// and they're touching
if ( next - > GetStartTime ( ) = = phoneme - > GetEndTime ( ) )
{
// 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
dt = MAX ( dt , MIN ( next - > GetEndTime ( ) - t , phoneme - > GetEndTime ( ) - phoneme - > GetStartTime ( ) ) ) ;
}
else
{
// 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
dt = MAX ( dt , MIN ( next - > GetStartTime ( ) - t , phoneme - > GetEndTime ( ) - phoneme - > GetStartTime ( ) ) ) ;
}
}
else
{
// last phoneme in list, increase the blend length to the length of the current phoneme
dt = MAX ( dt , phoneme - > GetEndTime ( ) - phoneme - > GetStartTime ( ) ) ;
}
}
}
}
float t1 = ( phoneme - > GetStartTime ( ) - t ) / dt ;
float t2 = ( phoneme - > GetEndTime ( ) - t ) / dt ;
if ( t1 < 1.0 & & t2 > 0 )
{
float scale ;
// clamp
if ( t2 > 1 )
t2 = 1 ;
if ( t1 < 0 )
t1 = 0 ;
// FIXME: simple box filter. Should use something fancier
scale = ( t2 - t1 ) ;
AddViseme ( classes , emphasis_intensity , phoneme - > GetPhonemeCode ( ) , scale , juststarted ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *classes -
//-----------------------------------------------------------------------------
void C_BaseFlex : : ProcessVisemes ( Emphasized_Phoneme * classes )
{
// Any sounds being played?
if ( ! MouthInfo ( ) . IsActive ( ) )
return ;
// Multiple phoneme tracks can overlap, look across all such tracks.
for ( int source = 0 ; source < MouthInfo ( ) . GetNumVoiceSources ( ) ; source + + )
{
CVoiceData * vd = MouthInfo ( ) . GetVoiceSource ( source ) ;
if ( ! vd | | vd - > ShouldIgnorePhonemes ( ) )
continue ;
CSentence * sentence = engine - > GetSentence ( vd - > GetSource ( ) ) ;
if ( ! sentence )
continue ;
float sentence_length = engine - > GetSentenceLength ( vd - > GetSource ( ) ) ;
float timesincestart = vd - > GetElapsedTime ( ) ;
// This sound should be done...why hasn't it been removed yet???
if ( timesincestart > = ( sentence_length + 2.0f ) )
continue ;
// Adjust actual time
float t = timesincestart - g_CV_PhonemeDelay . GetFloat ( ) ;
// Get box filter duration
float dt = g_CV_PhonemeFilter . GetFloat ( ) ;
// Streaming sounds get an additional delay...
/*
// Tracker 20534: Probably not needed any more with the async sound stuff that
// we now have (we don't have a disk i/o hitch on startup which might have been
// messing up the startup timing a bit )
bool streaming = engine - > IsStreaming ( vd - > m_pAudioSource ) ;
if ( streaming )
{
t - = g_CV_PhonemeDelayStreaming . GetFloat ( ) ;
}
*/
// Assume sound has been playing for a while...
bool juststarted = false ;
// Get intensity setting for this time (from spline)
float emphasis_intensity = sentence - > GetIntensity ( t , sentence_length ) ;
// Blend and add visemes together
AddVisemesForSentence ( classes , emphasis_intensity , sentence , t , dt , juststarted ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: fill keyvalues message with flex state
// Input :
//-----------------------------------------------------------------------------
void C_BaseFlex : : GetToolRecordingState ( KeyValues * msg )
{
if ( ! ToolsEnabled ( ) )
return ;
VPROF_BUDGET ( " C_BaseFlex::GetToolRecordingState " , VPROF_BUDGETGROUP_TOOLS ) ;
BaseClass : : GetToolRecordingState ( msg ) ;
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
return ;
memset ( g_flexweight , 0 , sizeof ( g_flexweight ) ) ;
if ( hdr - > numflexcontrollers ( ) = = 0 )
return ;
LocalFlexController_t i ;
ProcessSceneEvents ( true ) ;
// FIXME: shouldn't this happen at runtime?
// initialize the models local to global flex controller mappings
if ( hdr - > pFlexcontroller ( LocalFlexController_t ( 0 ) ) - > localToGlobal = = - 1 )
{
for ( i = LocalFlexController_t ( 0 ) ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
int j = AddGlobalFlexController ( hdr - > pFlexcontroller ( i ) - > pszName ( ) ) ;
hdr - > pFlexcontroller ( i ) - > localToGlobal = j ;
}
}
// blend weights from server
for ( i = LocalFlexController_t ( 0 ) ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
mstudioflexcontroller_t * pflex = hdr - > pFlexcontroller ( i ) ;
g_flexweight [ pflex - > localToGlobal ] = m_flexWeight [ i ] ;
// rescale
g_flexweight [ pflex - > localToGlobal ] = g_flexweight [ pflex - > localToGlobal ] * ( pflex - > max - pflex - > min ) + pflex - > min ;
}
ProcessSceneEvents ( false ) ;
// check for blinking
if ( m_blinktoggle ! = m_prevblinktoggle )
{
m_prevblinktoggle = m_blinktoggle ;
m_blinktime = gpGlobals - > curtime + g_CV_BlinkDuration . GetFloat ( ) ;
}
if ( m_iBlink = = - 1 )
m_iBlink = AddGlobalFlexController ( " blink " ) ;
g_flexweight [ m_iBlink ] = 0 ;
// FIXME: this needs a better algorithm
// blink the eyes
float t = ( m_blinktime - gpGlobals - > curtime ) * M_PI * 0.5 * ( 1.0 / g_CV_BlinkDuration . GetFloat ( ) ) ;
if ( t > 0 )
{
// do eyeblink falloff curve
t = cos ( t ) ;
if ( t > 0 )
{
g_flexweight [ m_iBlink ] = sqrtf ( t ) * 2 ;
if ( g_flexweight [ m_iBlink ] > 1 )
g_flexweight [ m_iBlink ] = 2.0 - g_flexweight [ m_iBlink ] ;
}
}
// Drive the mouth from .wav file playback...
ProcessVisemes ( m_PhonemeClasses ) ;
// Necessary???
SetViewTarget ( hdr ) ;
Vector viewtarget = m_viewtarget ; // Use the unfiltered value
// HACK HACK: Unmap eyes right/left amounts
2022-03-01 20:00:42 +00:00
if ( m_iEyeUpdown ! = - 1 & & m_iEyeRightleft ! = - 1 )
2020-04-22 16:56:21 +00:00
{
mstudioflexcontroller_t * flexupdown = hdr - > pFlexcontroller ( m_iEyeUpdown ) ;
mstudioflexcontroller_t * flexrightleft = hdr - > pFlexcontroller ( m_iEyeRightleft ) ;
if ( flexupdown - > localToGlobal ! = - 1 & & flexrightleft - > localToGlobal ! = - 1 )
{
float updown = g_flexweight [ flexupdown - > localToGlobal ] ;
float rightleft = g_flexweight [ flexrightleft - > localToGlobal ] ;
if ( flexupdown - > min ! = flexupdown - > max )
{
updown = RemapVal ( updown , flexupdown - > min , flexupdown - > max , 0.0f , 1.0f ) ;
}
if ( flexrightleft - > min ! = flexrightleft - > max )
{
rightleft = RemapVal ( rightleft , flexrightleft - > min , flexrightleft - > max , 0.0f , 1.0f ) ;
}
g_flexweight [ flexupdown - > localToGlobal ] = updown ;
g_flexweight [ flexrightleft - > localToGlobal ] = rightleft ;
}
}
// Convert back to normalized weights
for ( i = LocalFlexController_t ( 0 ) ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
mstudioflexcontroller_t * pflex = hdr - > pFlexcontroller ( i ) ;
// rescale
if ( pflex - > max ! = pflex - > min )
{
g_flexweight [ pflex - > localToGlobal ] = ( g_flexweight [ pflex - > localToGlobal ] - pflex - > min ) / ( pflex - > max - pflex - > min ) ;
}
}
static BaseFlexRecordingState_t state ;
state . m_nFlexCount = MAXSTUDIOFLEXCTRL ;
state . m_pDestWeight = g_flexweight ;
state . m_vecViewTarget = viewtarget ;
msg - > SetPtr ( " baseflex " , & state ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseFlex : : OnThreadedDrawSetup ( )
{
if ( m_iEyeAttachment < 0 )
return ;
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
CalcAttachments ( ) ;
}
//-----------------------------------------------------------------------------
// Should we use delayed flex weights?
//-----------------------------------------------------------------------------
bool C_BaseFlex : : UsesFlexDelayedWeights ( )
{
return ( m_flFlexDelayedWeight & & g_CV_FlexSmooth . GetBool ( ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseFlex : : LinkToGlobalFlexControllers ( CStudioHdr * hdr )
{
if ( hdr & & hdr - > pFlexcontroller ( LocalFlexController_t ( 0 ) ) - > localToGlobal = = - 1 )
{
for ( LocalFlexController_t i = LocalFlexController_t ( 0 ) ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
int j = AddGlobalFlexController ( hdr - > pFlexcontroller ( i ) - > pszName ( ) ) ;
hdr - > pFlexcontroller ( i ) - > localToGlobal = j ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Rendering callback to allow the client to set up all the model specific flex weights
//-----------------------------------------------------------------------------
void C_BaseFlex : : SetupWeights ( const matrix3x4_t * pBoneToWorld , int nFlexWeightCount , float * pFlexWeights , float * pFlexDelayedWeights )
{
// hack in an initialization
LinkToGlobalFlexControllers ( GetModelPtr ( ) ) ;
m_iBlink = AddGlobalFlexController ( " UH " ) ;
if ( SetupGlobalWeights ( pBoneToWorld , nFlexWeightCount , pFlexWeights , pFlexDelayedWeights ) )
{
SetupLocalWeights ( pBoneToWorld , nFlexWeightCount , pFlexWeights , pFlexDelayedWeights ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Use the local bone positions to set flex control weights
// via boneflexdrivers specified in the model
//-----------------------------------------------------------------------------
void C_BaseFlex : : BuildTransformations ( CStudioHdr * pStudioHdr , Vector * pos , Quaternion q [ ] , const matrix3x4_t & cameraTransform , int boneMask , CBoneBitList & boneComputed )
{
const int nBoneFlexDriverCount = pStudioHdr - > BoneFlexDriverCount ( ) ;
for ( int i = 0 ; i < nBoneFlexDriverCount ; + + i )
{
const mstudioboneflexdriver_t * pBoneFlexDriver = pStudioHdr - > BoneFlexDriver ( i ) ;
const Vector & position = pos [ pBoneFlexDriver - > m_nBoneIndex ] ;
const int nControllerCount = pBoneFlexDriver - > m_nControlCount ;
for ( int j = 0 ; j < nControllerCount ; + + j )
{
const mstudioboneflexdrivercontrol_t * pController = pBoneFlexDriver - > pBoneFlexDriverControl ( j ) ;
Assert ( pController - > m_nFlexControllerIndex > = 0 & & pController - > m_nFlexControllerIndex < pStudioHdr - > numflexcontrollers ( ) ) ;
Assert ( pController - > m_nBoneComponent > = 0 & & pController - > m_nBoneComponent < = 2 ) ;
SetFlexWeight ( static_cast < LocalFlexController_t > ( pController - > m_nFlexControllerIndex ) , RemapValClamped ( position [ pController - > m_nBoneComponent ] , pController - > m_flMin , pController - > m_flMax , 0.0f , 1.0f ) ) ;
}
}
BaseClass : : BuildTransformations ( pStudioHdr , pos , q , cameraTransform , boneMask , boneComputed ) ;
}
//-----------------------------------------------------------------------------
// Purpose: process the entities networked state, vcd playback, wav file visemes, and blinks into a global shared flex controller array
//-----------------------------------------------------------------------------
bool C_BaseFlex : : SetupGlobalWeights ( const matrix3x4_t * pBoneToWorld , int nFlexWeightCount , float * pFlexWeights , float * pFlexDelayedWeights )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
return false ;
memset ( g_flexweight , 0 , sizeof ( g_flexweight ) ) ;
// FIXME: this should assert then, it's too complex a class for the model
if ( hdr - > numflexcontrollers ( ) = = 0 )
{
int nSizeInBytes = nFlexWeightCount * sizeof ( float ) ;
memset ( pFlexWeights , 0 , nSizeInBytes ) ;
if ( pFlexDelayedWeights )
{
memset ( pFlexDelayedWeights , 0 , nSizeInBytes ) ;
}
return false ;
}
LocalFlexController_t i ;
ProcessSceneEvents ( true ) ;
Assert ( hdr - > pFlexcontroller ( LocalFlexController_t ( 0 ) ) - > localToGlobal ! = - 1 ) ;
// get the networked flexweights and convert them from 0..1 to real dynamic range
for ( i = LocalFlexController_t ( 0 ) ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
mstudioflexcontroller_t * pflex = hdr - > pFlexcontroller ( i ) ;
g_flexweight [ pflex - > localToGlobal ] = m_flexWeight [ i ] ;
// rescale
g_flexweight [ pflex - > localToGlobal ] = g_flexweight [ pflex - > localToGlobal ] * ( pflex - > max - pflex - > min ) + pflex - > min ;
}
ProcessSceneEvents ( false ) ;
// check for blinking
if ( m_blinktoggle ! = m_prevblinktoggle )
{
m_prevblinktoggle = m_blinktoggle ;
m_blinktime = gpGlobals - > curtime + g_CV_BlinkDuration . GetFloat ( ) ;
}
if ( m_iBlink = = - 1 )
{
m_iBlink = AddGlobalFlexController ( " blink " ) ;
}
// FIXME: this needs a better algorithm
// blink the eyes
float flBlinkDuration = g_CV_BlinkDuration . GetFloat ( ) ;
float flOOBlinkDuration = ( flBlinkDuration > 0 ) ? 1.0f / flBlinkDuration : 0.0f ;
float t = ( m_blinktime - gpGlobals - > curtime ) * M_PI * 0.5 * flOOBlinkDuration ;
if ( t > 0 )
{
// do eyeblink falloff curve
t = cos ( t ) ;
if ( t > 0.0f & & t < 1.0f )
{
t = sqrtf ( t ) * 2.0f ;
if ( t > 1.0f )
t = 2.0f - t ;
t = clamp ( t , 0.0f , 1.0f ) ;
// add it to whatever the blink track is doing
g_flexweight [ m_iBlink ] = clamp ( g_flexweight [ m_iBlink ] + t , 0.0f , 1.0f ) ;
}
}
// Drive the mouth from .wav file playback...
ProcessVisemes ( m_PhonemeClasses ) ;
return true ;
}
void C_BaseFlex : : RunFlexDelay ( int nFlexWeightCount , float * pFlexWeights , float * pFlexDelayedWeights , float & flFlexDelayTime )
{
// process the delayed version of the flexweights
if ( flFlexDelayTime > 0.0f & & flFlexDelayTime < gpGlobals - > curtime )
{
float d = clamp ( gpGlobals - > curtime - flFlexDelayTime , 0.0 , gpGlobals - > frametime ) ;
d = ExponentialDecay ( 0.8 , 0.033 , d ) ;
for ( int i = 0 ; i < nFlexWeightCount ; i + + )
{
pFlexDelayedWeights [ i ] = pFlexDelayedWeights [ i ] * d + pFlexWeights [ i ] * ( 1.0f - d ) ;
}
}
flFlexDelayTime = gpGlobals - > curtime ;
}
//-----------------------------------------------------------------------------
// Purpose: convert the global flex controllers into model specific flex weights
//-----------------------------------------------------------------------------
void C_BaseFlex : : SetupLocalWeights ( const matrix3x4_t * pBoneToWorld , int nFlexWeightCount , float * pFlexWeights , float * pFlexDelayedWeights )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
return ;
// 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
// 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.
// Note that this check is duplicated in CEconEntity::SetupWeights.
AssertMsg ( nFlexWeightCount = = m_cFlexDelayedWeight , " Disagreement between the number of flex weights. Do the studio headers match? " ) ;
if ( nFlexWeightCount ! = m_cFlexDelayedWeight )
{
return ;
}
// convert the flex controllers into actual flex values
RunFlexRules ( hdr , pFlexWeights ) ;
// aim the eyes
SetViewTarget ( hdr ) ;
AssertOnce ( hdr - > pFlexcontroller ( LocalFlexController_t ( 0 ) ) - > localToGlobal ! = - 1 ) ;
if ( pFlexDelayedWeights )
{
RunFlexDelay ( nFlexWeightCount , pFlexWeights , m_flFlexDelayedWeight , m_flFlexDelayTime ) ;
memcpy ( pFlexDelayedWeights , m_flFlexDelayedWeight , sizeof ( float ) * nFlexWeightCount ) ;
}
/*
LocalFlexController_t i ;
for ( i = 0 ; i < hdr - > numflexdesc ; i + + )
{
debugoverlay - > AddTextOverlay ( GetAbsOrigin ( ) + Vector ( 0 , 0 , 64 ) , i - hdr - > numflexcontrollers , 0 , " %2d:%s : %3.2f " , i , hdr - > pFlexdesc ( i ) - > pszFACS ( ) , pFlexWeights [ i ] ) ;
}
*/
/*
for ( i = 0 ; i < g_numflexcontrollers ; i + + )
{
int j = hdr - > pFlexcontroller ( i ) - > link ;
debugoverlay - > AddTextOverlay ( GetAbsOrigin ( ) + Vector ( 0 , 0 , 64 ) , - i , 0 , " %s %3.2f " , g_flexcontroller [ i ] , g_flexweight [ j ] ) ;
}
*/
}
//-----------------------------------------------------------------------------
// Purpose: Unified set of flex controller entries that all systems can talk to
//-----------------------------------------------------------------------------
int C_BaseFlex : : g_numflexcontrollers ;
char * C_BaseFlex : : g_flexcontroller [ MAXSTUDIOFLEXCTRL * 4 ] ;
float C_BaseFlex : : g_flexweight [ MAXSTUDIOFLEXDESC ] ;
int C_BaseFlex : : AddGlobalFlexController ( const char * szName )
{
int i ;
for ( i = 0 ; i < g_numflexcontrollers ; i + + )
{
if ( Q_stricmp ( g_flexcontroller [ i ] , szName ) = = 0 )
{
return i ;
}
}
if ( g_numflexcontrollers < MAXSTUDIOFLEXCTRL * 4 )
{
g_flexcontroller [ g_numflexcontrollers + + ] = strdup ( szName ) ;
}
else
{
// FIXME: missing runtime error condition
}
return i ;
}
char const * C_BaseFlex : : GetGlobalFlexControllerName ( int idx )
{
if ( idx < 0 | | idx > = g_numflexcontrollers )
{
return " " ;
}
return g_flexcontroller [ idx ] ;
}
const flexsetting_t * C_BaseFlex : : FindNamedSetting ( const flexsettinghdr_t * pSettinghdr , const char * expr )
{
int i ;
const flexsetting_t * pSetting = NULL ;
for ( i = 0 ; i < pSettinghdr - > numflexsettings ; i + + )
{
pSetting = pSettinghdr - > pSetting ( i ) ;
if ( ! pSetting )
continue ;
const char * name = pSetting - > pszName ( ) ;
if ( ! stricmp ( name , expr ) )
break ;
}
if ( i > = pSettinghdr - > numflexsettings )
{
return NULL ;
}
return pSetting ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseFlex : : StartChoreoScene ( CChoreoScene * scene )
{
if ( m_ActiveChoreoScenes . Find ( scene ) ! = m_ActiveChoreoScenes . InvalidIndex ( ) )
{
return ;
}
m_ActiveChoreoScenes . AddToTail ( scene ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseFlex : : RemoveChoreoScene ( CChoreoScene * scene )
{
// Assert( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() );
m_ActiveChoreoScenes . FindAndRemove ( scene ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Remove all active SceneEvents
//-----------------------------------------------------------------------------
void C_BaseFlex : : ClearSceneEvents ( CChoreoScene * scene , bool canceled )
{
if ( ! scene )
{
m_SceneEvents . RemoveAll ( ) ;
return ;
}
for ( int i = m_SceneEvents . Count ( ) - 1 ; i > = 0 ; i - - )
{
CSceneEventInfo * info = & m_SceneEvents [ i ] ;
Assert ( info ) ;
Assert ( info - > m_pScene ) ;
Assert ( info - > m_pEvent ) ;
if ( info - > m_pScene ! = scene )
continue ;
if ( ! ClearSceneEvent ( info , false , canceled ) )
{
// unknown expression to clear!!
Assert ( 0 ) ;
}
// Free this slot
info - > m_pEvent = NULL ;
info - > m_pScene = NULL ;
info - > m_bStarted = false ;
m_SceneEvents . Remove ( i ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Stop specifics of expression
//-----------------------------------------------------------------------------
bool C_BaseFlex : : ClearSceneEvent ( CSceneEventInfo * info , bool fastKill , bool canceled )
{
Assert ( info ) ;
Assert ( info - > m_pScene ) ;
Assert ( info - > m_pEvent ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Add string indexed scene/expression/duration to list of active SceneEvents
// Input : scenefile -
// expression -
// duration -
//-----------------------------------------------------------------------------
void C_BaseFlex : : AddSceneEvent ( CChoreoScene * scene , CChoreoEvent * event , CBaseEntity * pTarget , bool bClientSide )
{
if ( ! scene | | ! event )
{
Msg ( " C_BaseFlex::AddSceneEvent: scene or event was NULL!!! \n " ) ;
return ;
}
CChoreoActor * actor = event - > GetActor ( ) ;
if ( ! actor )
{
Msg ( " C_BaseFlex::AddSceneEvent: event->GetActor() was NULL!!! \n " ) ;
return ;
}
CSceneEventInfo info ;
memset ( ( void * ) & info , 0 , sizeof ( info ) ) ;
info . m_pEvent = event ;
info . m_pScene = scene ;
info . m_hTarget = pTarget ;
info . m_bStarted = false ;
info . m_bClientSide = bClientSide ;
if ( StartSceneEvent ( & info , scene , event , actor , pTarget ) )
{
m_SceneEvents . AddToTail ( info ) ;
}
else
{
scene - > SceneMsg ( " C_BaseFlex::AddSceneEvent: event failed \n " ) ;
// Assert( 0 ); // expression failed to start
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseFlex : : StartSceneEvent ( CSceneEventInfo * info , CChoreoScene * scene , CChoreoEvent * event , CChoreoActor * actor , CBaseEntity * pTarget )
{
switch ( event - > GetType ( ) )
{
default :
break ;
case CChoreoEvent : : FLEXANIMATION :
info - > InitWeight ( this ) ;
return true ;
case CChoreoEvent : : EXPRESSION :
return true ;
case CChoreoEvent : : SEQUENCE :
if ( info - > m_bClientSide )
{
return RequestStartSequenceSceneEvent ( info , scene , event , actor , pTarget ) ;
}
break ;
case CChoreoEvent : : SPEAK :
if ( info - > m_bClientSide )
{
return true ;
}
break ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseFlex : : RequestStartSequenceSceneEvent ( CSceneEventInfo * info , CChoreoScene * scene , CChoreoEvent * event , CChoreoActor * actor , CBaseEntity * pTarget )
{
info - > m_nSequence = LookupSequence ( event - > GetParameters ( ) ) ;
// make sure sequence exists
if ( info - > m_nSequence < 0 )
return false ;
info - > m_pActor = actor ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Remove expression
// Input : scenefile -
// expression -
//-----------------------------------------------------------------------------
void C_BaseFlex : : RemoveSceneEvent ( CChoreoScene * scene , CChoreoEvent * event , bool fastKill )
{
Assert ( event ) ;
for ( int i = 0 ; i < m_SceneEvents . Count ( ) ; i + + )
{
CSceneEventInfo * info = & m_SceneEvents [ i ] ;
Assert ( info ) ;
Assert ( info - > m_pEvent ) ;
if ( info - > m_pScene ! = scene )
continue ;
if ( info - > m_pEvent ! = event )
continue ;
if ( ClearSceneEvent ( info , fastKill , false ) )
{
// Free this slot
info - > m_pEvent = NULL ;
info - > m_pScene = NULL ;
info - > m_bStarted = false ;
m_SceneEvents . Remove ( i ) ;
2022-03-01 20:00:42 +00:00
return ;
2020-04-22 16:56:21 +00:00
}
}
// many events refuse to start due to bogus parameters
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if the event should be considered "completed"
//-----------------------------------------------------------------------------
bool C_BaseFlex : : CheckSceneEvent ( float currenttime , CChoreoScene * scene , CChoreoEvent * event )
{
for ( int i = 0 ; i < m_SceneEvents . Count ( ) ; i + + )
{
CSceneEventInfo * info = & m_SceneEvents [ i ] ;
Assert ( info ) ;
Assert ( info - > m_pEvent ) ;
if ( info - > m_pScene ! = scene )
continue ;
if ( info - > m_pEvent ! = event )
continue ;
return CheckSceneEventCompletion ( info , currenttime , scene , event ) ;
}
return true ;
}
bool C_BaseFlex : : CheckSceneEventCompletion ( CSceneEventInfo * info , float currenttime , CChoreoScene * scene , CChoreoEvent * event )
{
return true ;
}
2022-03-01 20:00:42 +00:00
void C_BaseFlex : : SetFlexWeight ( LocalFlexController_t index , float value )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
if ( index > = 0 & & index < GetNumFlexControllers ( ) )
2020-04-22 16:56:21 +00:00
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return ;
2022-03-01 20:00:42 +00:00
mstudioflexcontroller_t * pflexcontroller = pstudiohdr - > pFlexcontroller ( index ) ;
2020-04-22 16:56:21 +00:00
if ( pflexcontroller - > max ! = pflexcontroller - > min )
{
value = ( value - pflexcontroller - > min ) / ( pflexcontroller - > max - pflexcontroller - > min ) ;
value = clamp ( value , 0.0f , 1.0f ) ;
}
2022-03-01 20:00:42 +00:00
m_flexWeight [ index ] = value ;
2020-04-22 16:56:21 +00:00
}
}
2022-03-01 20:00:42 +00:00
float C_BaseFlex : : GetFlexWeight ( LocalFlexController_t index )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
if ( index > = 0 & & index < GetNumFlexControllers ( ) )
2020-04-22 16:56:21 +00:00
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return 0 ;
2022-03-01 20:00:42 +00:00
mstudioflexcontroller_t * pflexcontroller = pstudiohdr - > pFlexcontroller ( index ) ;
2020-04-22 16:56:21 +00:00
if ( pflexcontroller - > max ! = pflexcontroller - > min )
{
2022-03-01 20:00:42 +00:00
return m_flexWeight [ index ] * ( pflexcontroller - > max - pflexcontroller - > min ) + pflexcontroller - > min ;
2020-04-22 16:56:21 +00:00
}
2022-03-01 20:00:42 +00:00
return m_flexWeight [ index ] ;
2020-04-22 16:56:21 +00:00
}
return 0.0 ;
}
LocalFlexController_t C_BaseFlex : : FindFlexController ( const char * szName )
{
for ( LocalFlexController_t i = LocalFlexController_t ( 0 ) ; i < GetNumFlexControllers ( ) ; i + + )
{
if ( stricmp ( GetFlexControllerName ( i ) , szName ) = = 0 )
{
return i ;
}
}
// AssertMsg( 0, UTIL_VarArgs( "flexcontroller %s couldn't be mapped!!!\n", szName ) );
return LocalFlexController_t ( - 1 ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Default implementation
//-----------------------------------------------------------------------------
void C_BaseFlex : : ProcessSceneEvents ( bool bFlexEvents )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
// slowly decay to netural expression
if ( bFlexEvents )
{
for ( LocalFlexController_t i = LocalFlexController_t ( 0 ) ; i < GetNumFlexControllers ( ) ; i + + )
{
SetFlexWeight ( i , GetFlexWeight ( i ) * 0.95 ) ;
}
}
// Iterate SceneEvents and look for active slots
for ( int i = 0 ; i < m_SceneEvents . Count ( ) ; i + + )
{
CSceneEventInfo * info = & m_SceneEvents [ i ] ;
Assert ( info ) ;
// FIXME: Need a safe handle to m_pEvent in case of memory deletion?
CChoreoEvent * event = info - > m_pEvent ;
Assert ( event ) ;
CChoreoScene * scene = info - > m_pScene ;
Assert ( scene ) ;
if ( ProcessSceneEvent ( bFlexEvents , info , scene , event ) )
{
info - > m_bStarted = true ;
}
}
}
//-----------------------------------------------------------------------------
// Various methods to process facial SceneEvents:
//-----------------------------------------------------------------------------
bool C_BaseFlex : : ProcessFlexAnimationSceneEvent ( CSceneEventInfo * info , CChoreoScene * scene , CChoreoEvent * event )
{
Assert ( event - > HasEndTime ( ) ) ;
if ( event - > HasEndTime ( ) )
{
AddFlexAnimation ( info ) ;
}
return true ;
}
bool C_BaseFlex : : ProcessFlexSettingSceneEvent ( CSceneEventInfo * info , CChoreoScene * scene , CChoreoEvent * event )
{
// Flexanimations have to have an end time!!!
if ( ! event - > HasEndTime ( ) )
return true ;
VPROF ( " C_BaseFlex::ProcessFlexSettingSceneEvent " ) ;
// Look up the actual strings
const char * scenefile = event - > GetParameters ( ) ;
const char * name = event - > GetParameters2 ( ) ;
// Have to find both strings
if ( scenefile & & name )
{
// Find the scene file
const flexsettinghdr_t * pExpHdr = ( const flexsettinghdr_t * ) g_FlexSceneFileManager . FindSceneFile ( this , scenefile , true ) ;
if ( pExpHdr )
{
float scenetime = scene - > GetTime ( ) ;
float scale = event - > GetIntensity ( scenetime ) ;
// Add the named expression
AddFlexSetting ( name , scale , pExpHdr , ! info - > m_bStarted ) ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Each CBaseFlex maintains a UtlRBTree of mappings, one for each loaded flex scene file it uses. This is used to
// sort the entries in the RBTree
// Input : lhs -
// rhs -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool FlexSettingLessFunc ( const FS_LocalToGlobal_t & lhs , const FS_LocalToGlobal_t & rhs )
{
return lhs . m_Key < rhs . m_Key ;
}
//-----------------------------------------------------------------------------
// Purpose: Since everyone shared a pSettinghdr now, we need to set up the localtoglobal mapping per entity, but
// we just do this in memory with an array of integers (could be shorts, I suppose)
// Input : *pSettinghdr -
//-----------------------------------------------------------------------------
void C_BaseFlex : : EnsureTranslations ( const flexsettinghdr_t * pSettinghdr )
{
Assert ( pSettinghdr ) ;
FS_LocalToGlobal_t entry ( pSettinghdr ) ;
unsigned short idx = m_LocalToGlobal . Find ( entry ) ;
if ( idx ! = m_LocalToGlobal . InvalidIndex ( ) )
return ;
entry . SetCount ( pSettinghdr - > numkeys ) ;
for ( int i = 0 ; i < pSettinghdr - > numkeys ; + + i )
{
entry . m_Mapping [ i ] = AddGlobalFlexController ( pSettinghdr - > pLocalName ( i ) ) ;
}
m_LocalToGlobal . Insert ( entry ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Look up instance specific mapping
// Input : *pSettinghdr -
// key -
// Output : int
//-----------------------------------------------------------------------------
int C_BaseFlex : : FlexControllerLocalToGlobal ( const flexsettinghdr_t * pSettinghdr , int key )
{
FS_LocalToGlobal_t entry ( pSettinghdr ) ;
int idx = m_LocalToGlobal . Find ( entry ) ;
if ( idx = = m_LocalToGlobal . InvalidIndex ( ) )
{
// This should never happen!!!
Assert ( 0 ) ;
Warning ( " Unable to find mapping for flexcontroller %i, settings %p on %i/%s \n " , key , pSettinghdr , entindex ( ) , GetClassname ( ) ) ;
EnsureTranslations ( pSettinghdr ) ;
idx = m_LocalToGlobal . Find ( entry ) ;
if ( idx = = m_LocalToGlobal . InvalidIndex ( ) )
{
Error ( " CBaseFlex::FlexControllerLocalToGlobal failed! \n " ) ;
}
}
FS_LocalToGlobal_t & result = m_LocalToGlobal [ idx ] ;
// Validate lookup
Assert ( result . m_nCount ! = 0 & & key < result . m_nCount ) ;
2022-03-01 20:00:42 +00:00
int index = result . m_Mapping [ key ] ;
return index ;
2020-04-22 16:56:21 +00:00
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *expr -
// scale -
// *pSettinghdr -
// newexpression -
//-----------------------------------------------------------------------------
void C_BaseFlex : : AddFlexSetting ( const char * expr , float scale ,
const flexsettinghdr_t * pSettinghdr , bool newexpression )
{
int i ;
const flexsetting_t * pSetting = NULL ;
// Find the named setting in the base
for ( i = 0 ; i < pSettinghdr - > numflexsettings ; i + + )
{
pSetting = pSettinghdr - > pSetting ( i ) ;
if ( ! pSetting )
continue ;
const char * name = pSetting - > pszName ( ) ;
if ( ! V_stricmp ( name , expr ) )
break ;
}
if ( i > = pSettinghdr - > numflexsettings )
{
return ;
}
flexweight_t * pWeights = NULL ;
int truecount = pSetting - > psetting ( ( byte * ) pSettinghdr , 0 , & pWeights ) ;
if ( ! pWeights )
return ;
for ( i = 0 ; i < truecount ; i + + , pWeights + + )
{
// Translate to local flex controller
// this is translating from the settings's local index to the models local index
2022-03-01 20:00:42 +00:00
int index = FlexControllerLocalToGlobal ( pSettinghdr , pWeights - > key ) ;
2020-04-22 16:56:21 +00:00
// blend scaled weighting in to total (post networking g_flexweight!!!!)
float s = clamp ( scale * pWeights - > influence , 0.0f , 1.0f ) ;
2022-03-01 20:00:42 +00:00
g_flexweight [ index ] = g_flexweight [ index ] * ( 1.0f - s ) + pWeights - > weight * s ;
2020-04-22 16:56:21 +00:00
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseFlex : : ProcessSceneEvent ( bool bFlexEvents , CSceneEventInfo * info , CChoreoScene * scene , CChoreoEvent * event )
{
switch ( event - > GetType ( ) )
{
default :
break ;
case CChoreoEvent : : FLEXANIMATION :
if ( bFlexEvents )
{
return ProcessFlexAnimationSceneEvent ( info , scene , event ) ;
}
return true ;
case CChoreoEvent : : EXPRESSION :
if ( ! bFlexEvents )
{
return ProcessFlexSettingSceneEvent ( info , scene , event ) ;
}
return true ;
case CChoreoEvent : : SEQUENCE :
if ( info - > m_bClientSide )
{
if ( ! bFlexEvents )
{
return ProcessSequenceSceneEvent ( info , scene , event ) ;
}
return true ;
}
break ;
case CChoreoEvent : : SPEAK :
if ( info - > m_bClientSide )
{
return true ;
}
break ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
// *parameters -
//-----------------------------------------------------------------------------
bool C_BaseFlex : : ProcessSequenceSceneEvent ( CSceneEventInfo * info , CChoreoScene * scene , CChoreoEvent * event )
{
if ( ! info | | ! event | | ! scene )
return false ;
SetSequence ( info - > m_nSequence ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
//-----------------------------------------------------------------------------
void C_BaseFlex : : AddFlexAnimation ( CSceneEventInfo * info )
{
if ( ! info )
return ;
CChoreoEvent * event = info - > m_pEvent ;
if ( ! event )
return ;
CChoreoScene * scene = info - > m_pScene ;
if ( ! scene )
return ;
if ( ! event - > GetTrackLookupSet ( ) )
{
// Create lookup data
for ( int i = 0 ; i < event - > GetNumFlexAnimationTracks ( ) ; i + + )
{
CFlexAnimationTrack * track = event - > GetFlexAnimationTrack ( i ) ;
if ( ! track )
continue ;
if ( track - > IsComboType ( ) )
{
char name [ 512 ] ;
Q_strncpy ( name , " right_ " , sizeof ( name ) ) ;
Q_strncat ( name , track - > GetFlexControllerName ( ) , sizeof ( name ) , COPY_ALL_CHARACTERS ) ;
track - > SetFlexControllerIndex ( MAX ( FindFlexController ( name ) , LocalFlexController_t ( 0 ) ) , 0 , 0 ) ;
Q_strncpy ( name , " left_ " , sizeof ( name ) ) ;
Q_strncat ( name , track - > GetFlexControllerName ( ) , sizeof ( name ) , COPY_ALL_CHARACTERS ) ;
track - > SetFlexControllerIndex ( MAX ( FindFlexController ( name ) , LocalFlexController_t ( 0 ) ) , 0 , 1 ) ;
}
else
{
track - > SetFlexControllerIndex ( MAX ( FindFlexController ( ( char * ) track - > GetFlexControllerName ( ) ) , LocalFlexController_t ( 0 ) ) , 0 ) ;
}
}
event - > SetTrackLookupSet ( true ) ;
}
if ( ! scene_clientflex . GetBool ( ) )
return ;
float scenetime = scene - > GetTime ( ) ;
float weight = event - > GetIntensity ( scenetime ) ;
// decay if this is a background scene and there's other flex animations playing
weight = weight * info - > UpdateWeight ( this ) ;
// Compute intensity for each track in animation and apply
// Iterate animation tracks
for ( int i = 0 ; i < event - > GetNumFlexAnimationTracks ( ) ; i + + )
{
CFlexAnimationTrack * track = event - > GetFlexAnimationTrack ( i ) ;
if ( ! track )
continue ;
// Disabled
if ( ! track - > IsTrackActive ( ) )
continue ;
// Map track flex controller to global name
if ( track - > IsComboType ( ) )
{
for ( int side = 0 ; side < 2 ; side + + )
{
LocalFlexController_t controller = track - > GetRawFlexControllerIndex ( side ) ;
// Get spline intensity for controller
float flIntensity = track - > GetIntensity ( scenetime , side ) ;
if ( controller > = LocalFlexController_t ( 0 ) )
{
float orig = GetFlexWeight ( controller ) ;
float value = orig * ( 1 - weight ) + flIntensity * weight ;
SetFlexWeight ( controller , value ) ;
}
}
}
else
{
LocalFlexController_t controller = track - > GetRawFlexControllerIndex ( 0 ) ;
// Get spline intensity for controller
float flIntensity = track - > GetIntensity ( scenetime , 0 ) ;
if ( controller > = LocalFlexController_t ( 0 ) )
{
float orig = GetFlexWeight ( controller ) ;
float value = orig * ( 1 - weight ) + flIntensity * weight ;
SetFlexWeight ( controller , value ) ;
}
}
}
info - > m_bStarted = true ;
}
void CSceneEventInfo : : InitWeight ( C_BaseFlex * pActor )
{
m_flWeight = 1.0 ;
}
//-----------------------------------------------------------------------------
// Purpose: update weight for background events. Only call once per think
//-----------------------------------------------------------------------------
float CSceneEventInfo : : UpdateWeight ( C_BaseFlex * pActor )
{
m_flWeight = MIN ( m_flWeight + 0.1 , 1.0 ) ;
return m_flWeight ;
}
BEGIN_BYTESWAP_DATADESC ( flexsettinghdr_t )
DEFINE_FIELD ( id , FIELD_INTEGER ) ,
DEFINE_FIELD ( version , FIELD_INTEGER ) ,
DEFINE_ARRAY ( name , FIELD_CHARACTER , 64 ) ,
DEFINE_FIELD ( length , FIELD_INTEGER ) ,
DEFINE_FIELD ( numflexsettings , FIELD_INTEGER ) ,
DEFINE_FIELD ( flexsettingindex , FIELD_INTEGER ) ,
DEFINE_FIELD ( nameindex , FIELD_INTEGER ) ,
DEFINE_FIELD ( numindexes , FIELD_INTEGER ) ,
DEFINE_FIELD ( indexindex , FIELD_INTEGER ) ,
DEFINE_FIELD ( numkeys , FIELD_INTEGER ) ,
DEFINE_FIELD ( keynameindex , FIELD_INTEGER ) ,
DEFINE_FIELD ( keymappingindex , FIELD_INTEGER ) ,
END_BYTESWAP_DATADESC ( )
BEGIN_BYTESWAP_DATADESC ( flexsetting_t )
DEFINE_FIELD ( nameindex , FIELD_INTEGER ) ,
DEFINE_FIELD ( obsolete1 , FIELD_INTEGER ) ,
DEFINE_FIELD ( numsettings , FIELD_INTEGER ) ,
DEFINE_FIELD ( index , FIELD_INTEGER ) ,
DEFINE_FIELD ( obsolete2 , FIELD_INTEGER ) ,
DEFINE_FIELD ( settingindex , FIELD_INTEGER ) ,
END_BYTESWAP_DATADESC ( )
BEGIN_BYTESWAP_DATADESC ( flexweight_t )
DEFINE_FIELD ( key , FIELD_INTEGER ) ,
DEFINE_FIELD ( weight , FIELD_FLOAT ) ,
DEFINE_FIELD ( influence , FIELD_FLOAT ) ,
END_BYTESWAP_DATADESC ( )