2008-09-15 01:00:17 -05:00
//===== Copyright © 1996-2005, 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"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
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 ] ) ) ,
# 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 ( )
C_BaseFlex : : C_BaseFlex ( ) : m_iv_viewtarget ( " C_BaseFlex::m_iv_viewtarget " ) , m_iv_flexWeight ( " C_BaseFlex:m_iv_flexWeight " ) ,
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
memset ( m_PhonemeClasses , 0 , sizeof ( m_PhonemeClasses ) ) ;
Emphasized_Phoneme * weak = & m_PhonemeClasses [ PHONEME_CLASS_WEAK ] ;
Q_strncpy ( weak - > classname , " phonemes_weak " , sizeof ( weak - > classname ) ) ;
weak - > required = false ;
Emphasized_Phoneme * normal = & m_PhonemeClasses [ PHONEME_CLASS_NORMAL ] ;
Q_strncpy ( normal - > classname , " phonemes " , sizeof ( normal - > classname ) ) ;
normal - > required = true ;
Emphasized_Phoneme * strong = & m_PhonemeClasses [ PHONEME_CLASS_STRONG ] ;
Q_strncpy ( strong - > classname , " phonemes_strong " , sizeof ( strong - > classname ) ) ;
strong - > required = false ;
m_flFlexDelayedWeight = NULL ;
/// Make sure size is correct
Assert ( PHONEME_CLASS_STRONG + 1 = = NUM_PHONEME_CLASSES ) ;
}
C_BaseFlex : : ~ C_BaseFlex ( )
{
delete [ ] m_flFlexDelayedWeight ;
m_SceneEvents . RemoveAll ( ) ;
m_LocalToGlobal . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: initialize fast lookups when model changes
//-----------------------------------------------------------------------------
CStudioHdr * C_BaseFlex : : OnNewModel ( )
{
CStudioHdr * hdr = BaseClass : : OnNewModel ( ) ;
// init to invalid setting
m_iBlink = - 1 ;
m_iEyeUpdown = - 1 ;
m_iEyeRightleft = - 1 ;
m_iMouthAttachment = 0 ;
delete [ ] m_flFlexDelayedWeight ;
m_flFlexDelayedWeight = NULL ;
if ( hdr )
{
if ( hdr - > numflexdesc ( ) )
{
m_flFlexDelayedWeight = new float [ hdr - > numflexdesc ( ) ] ;
for ( int i = 0 ; i < hdr - > numflexdesc ( ) ; i + + )
{
m_flFlexDelayedWeight [ i ] = 0.0 ;
}
}
m_iv_flexWeight . SetMaxCount ( hdr - > numflexcontrollers ( ) ) ;
m_iMouthAttachment = LookupAttachment ( " mouth " ) ;
}
return hdr ;
}
//-----------------------------------------------------------------------------
// 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 & & m_iMouthAttachment > 0 )
{
Vector origin ;
QAngle angles ;
C_BaseAnimating : : PushAllowBoneAccess ( true , false ) ;
if ( GetAttachment ( m_iMouthAttachment , origin , angles ) )
{
if ( info . pOrigin )
{
* info . pOrigin = origin ;
}
if ( info . pAngles )
{
* info . pAngles = angles ;
}
}
C_BaseAnimating : : PopBoneAccess ( ) ;
}
}
return bret ;
}
//-----------------------------------------------------------------------------
// Purpose: run the interpreted FAC's expressions, converting 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 ) ;
}
class CFlexSceneFileManager : CAutoGameSystem
{
public :
CFlexSceneFileManager ( ) : CAutoGameSystem ( " CFlexSceneFileManager " )
{
}
virtual bool 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
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
virtual void Shutdown ( )
{
DeleteSceneFiles ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets up translations
// Input : *instance -
// *pSettinghdr -
// Output : void
//-----------------------------------------------------------------------------
void EnsureTranslations ( C_BaseFlex * 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 ) ;
}
}
void * FindSceneFile ( C_BaseFlex * instance , const char * filename , bool allowBlockingIO )
{
// See if it's already loaded
int i ;
for ( i = 0 ; i < m_FileList . Count ( ) ; i + + )
{
CFlexSceneFile * file = m_FileList [ i ] ;
if ( file & & ! stricmp ( file - > filename , filename ) )
{
// 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 " , filename ) , " GAME " , & buffer ) ;
if ( ! len )
return NULL ;
// Create scene entry
CFlexSceneFile * pfile = new CFlexSceneFile ;
// Remember filename
Q_strncpy ( pfile - > filename , filename , sizeof ( pfile - > filename ) ) ;
// Remember data pointer
pfile - > buffer = buffer ;
// Add to list
m_FileList . AddToTail ( pfile ) ;
// Fill in translation table
EnsureTranslations ( instance , ( const flexsettinghdr_t * ) pfile - > buffer ) ;
// Return data
return pfile - > buffer ;
}
private :
void DeleteSceneFiles ( )
{
while ( m_FileList . Count ( ) > 0 )
{
CFlexSceneFile * file = m_FileList [ 0 ] ;
m_FileList . Remove ( 0 ) ;
delete [ ] file - > buffer ;
delete file ;
}
}
CUtlVector < CFlexSceneFile * > m_FileList ;
} ;
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_iEyeUpdown = = - 1 )
m_iEyeUpdown = AddGlobalFlexController ( " eyes_updown " ) ;
if ( m_iEyeRightleft = = - 1 )
m_iEyeRightleft = 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 ) ;
if ( m_iEyeUpdown ! = - 1 )
{
eyeAng . x = g_flexweight [ m_iEyeUpdown ] ;
}
if ( m_iEyeRightleft ! = - 1 )
{
eyeAng . y = g_flexweight [ m_iEyeRightleft ] ;
}
// 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 a 30 degree cone
if ( local . x < 0.866 ) // cos(30)
{
// 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.0 )
{
d = sqrtf ( ( 1.0 - 0.866 * 0.866 ) / ( local . y * local . y + local . z * local . z ) ) ;
local . x = 0.866 ;
local . y = local . y * d ;
local . z = local . z * d ;
}
else
{
local . x = 1.0 ;
}
}
local = local * flDist ;
VectorTransform ( local , attToWorld , tmp ) ;
}
modelrender - > SetViewTarget ( 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 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static void NewMarkovIndex ( flexsetting_t * pSetting )
{
if ( pSetting - > type ! = FS_MARKOV )
return ;
int weighttotal = 0 ;
int member = 0 ;
for ( int i = 0 ; i < pSetting - > numsettings ; i + + )
{
flexmarkovgroup_t * group = pSetting - > pMarkovGroup ( i ) ;
if ( ! group )
continue ;
weighttotal + = group - > weight ;
if ( ! weighttotal | | random - > RandomInt ( 0 , weighttotal - 1 ) < group - > weight )
{
member = i ;
}
}
pSetting - > currentindex = member ;
}
# 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 overrides 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
{
2011-04-28 01:28:41 -05:00
emphasis_intensity = MIN ( emphasis_intensity , STRONG_CROSSFADE_START ) ;
2008-09-15 01:00:17 -05:00
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
{
2011-04-28 01:28:41 -05:00
emphasis_intensity = MAX ( emphasis_intensity , WEAK_CROSSFADE_START ) ;
2008-09-15 01:00:17 -05:00
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 ;
// Assume that we're not using overrieds
const flexsettinghdr_t * actual_flexsetting_header = info - > base ;
const flexsetting_t * pSetting = actual_flexsetting_header - > pIndexedSetting ( phoneme ) ;
if ( ! pSetting )
{
continue ;
}
if ( newexpression )
{
if ( pSetting - > type = = FS_MARKOV )
{
NewMarkovIndex ( ( flexsetting_t * ) pSetting ) ;
}
}
// Determine its index
int i = pSetting - actual_flexsetting_header - > pSetting ( 0 ) ;
Assert ( i > = 0 ) ;
Assert ( i < actual_flexsetting_header - > numflexsettings ) ;
// Resolve markov chain for the returned setting, probably not an issue for visemes
pSetting = actual_flexsetting_header - > pTranslatedSetting ( i ) ;
# if !defined( NO_ENTITY_PREDICTION )
// Check for overrides
if ( info - > override )
{
// Get name from setting
const char * resolvedName = pSetting - > pszName ( ) ;
if ( resolvedName )
{
// See if resolvedName exists in the override file
const flexsetting_t * override = FindNamedSetting ( info - > override , resolvedName ) ;
if ( override )
{
// If so, point at the override file instead
actual_flexsetting_header = info - > override ;
pSetting = override ;
}
}
}
# endif
flexweight_t * pWeights = NULL ;
int truecount = pSetting - > psetting ( ( byte * ) actual_flexsetting_header , 0 , & pWeights ) ;
if ( pWeights )
{
for ( 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 ) ;
}
# if !defined( NO_ENTITY_PREDICTION )
info - > override = NULL ;
# endif
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 ;
}
// NOTE: We never actually used any overrides in HL2/Aftermath, so doing this disk check could lead to hitches due to calling filesystem->Open on each
// possibility. If we ever need to use these overrides, I would suggest adding a flag on the server to the keyvalues for NPCs specifying "use overrides",
// networking the flag down, and then only checking for overrides if the flag is set on the client. ALternateley, we could crawl the expressions folders
// with findfirst/next and find all override files and create a database, we'd do that at startup if we did it. However, that would add a bit of time to startup
// due to recursively crawling the directories (though we could just enumerate dirs off of the expressions dir...).
// ywb 2/8/06
#if 0
# if !defined( NO_ENTITY_PREDICTION )
// Find overrides, if any exist
// Also a one-time setup
if ( ! info - > overridechecked )
{
char overridefile [ 512 ] ;
char shortname [ 128 ] ;
char modelname [ 128 ] ;
Q_strncpy ( modelname , modelinfo - > GetModelName ( GetModel ( ) ) , sizeof ( modelname ) ) ;
// Fix up the name
Q_FileBase ( modelname , shortname , sizeof ( shortname ) ) ;
Q_snprintf ( overridefile , sizeof ( overridefile ) , " %s/%s " , shortname , info - > classname ) ;
info - > overridechecked = true ;
info - > override = ( flexsettinghdr_t * ) FindSceneFile ( overridefile ) ;
}
# else
info - > overridechecked = true ;
info - > override = 0 ;
# endif
# endif
}
return skip ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *classes -
// *sentence -
// t -
// dt -
// juststarted -
//-----------------------------------------------------------------------------
ConVar g_CV_PhonemeSnap ( " phonemesnap " , " 1 " , 0 , " Don't force visemes to always consider 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 ( ( ! g_CV_PhonemeSnap . GetBool ( ) | | ( hdr - > flags ( ) & STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE ) ) & & t > phoneme - > GetStartTime ( ) & & t < phoneme - > GetEndTime ( ) )
{
if ( k < pcount - 1 )
{
const CBasePhonemeTag * next = sentence - > GetRuntimePhoneme ( k + 1 ) ;
if ( next )
{
2011-04-28 01:28:41 -05:00
dt = MAX ( dt , MIN ( next - > GetEndTime ( ) - t , phoneme - > GetEndTime ( ) - phoneme - > GetStartTime ( ) ) ) ;
2008-09-15 01:00:17 -05:00
}
}
}
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 )
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 ;
/*
// FIXME: Do we really want to support markov chains for the phonemes?
// If so, we'll need to uncomment out these lines.
if ( timesincestart < 0.001 )
{
juststarted = true ;
}
*/
// 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 ;
int i , j ;
ProcessSceneEvents ( true ) ;
// FIXME: shouldn't this happen at runtime?
// initialize the models local to global flex controller mappings
if ( hdr - > pFlexcontroller ( 0 ) - > link = = - 1 )
{
for ( i = 0 ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
j = AddGlobalFlexController ( hdr - > pFlexcontroller ( i ) - > pszName ( ) ) ;
hdr - > pFlexcontroller ( i ) - > link = j ;
}
}
// blend weights from server
for ( i = 0 ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
mstudioflexcontroller_t * pflex = hdr - > pFlexcontroller ( i ) ;
g_flexweight [ pflex - > link ] = m_flexWeight [ i ] ;
// rescale
g_flexweight [ pflex - > link ] = g_flexweight [ pflex - > link ] * ( 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 ) ;
Vector viewtarget = SetViewTarget ( hdr ) ;
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 : : SetupWeights ( )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
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 )
return ;
int i , j ;
ProcessSceneEvents ( true ) ;
// FIXME: shouldn't this happen at runtime?
// initialize the models local to global flex controller mappings
if ( hdr - > pFlexcontroller ( 0 ) - > link = = - 1 )
{
for ( i = 0 ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
j = AddGlobalFlexController ( hdr - > pFlexcontroller ( i ) - > pszName ( ) ) ;
hdr - > pFlexcontroller ( i ) - > link = j ;
}
}
// get the networked flexweights and convert them from 0..1 to real dynamic range
for ( i = 0 ; i < hdr - > numflexcontrollers ( ) ; i + + )
{
mstudioflexcontroller_t * pflex = hdr - > pFlexcontroller ( i ) ;
g_flexweight [ pflex - > link ] = m_flexWeight [ i ] ;
// rescale
g_flexweight [ pflex - > link ] = g_flexweight [ pflex - > link ] * ( 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 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.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.0 , 1.0 ) ;
}
}
// Drive the mouth from .wav file playback...
ProcessVisemes ( m_PhonemeClasses ) ;
// convert the flex controllers into actual flex values
float destweight [ MAXSTUDIOFLEXDESC ] ;
RunFlexRules ( hdr , destweight ) ;
// aim the eyes
SetViewTarget ( hdr ) ;
if ( m_flFlexDelayedWeight & & g_CV_FlexSmooth . GetBool ( ) )
{
// process the delayed version of the flexweights
float d = 1.0 ;
if ( gpGlobals - > frametime ! = 0 )
{
d = ExponentialDecay ( 0.8 , 0.033 , gpGlobals - > frametime ) ;
}
for ( i = 0 ; i < hdr - > numflexdesc ( ) ; i + + )
{
m_flFlexDelayedWeight [ i ] = m_flFlexDelayedWeight [ i ] * d + destweight [ i ] * ( 1 - d ) ;
}
// debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), i-hdr->numflexcontrollers, 0, "%.3f", d );
// send the flex values to the renderer
modelrender - > SetFlexWeights ( hdr - > numflexdesc ( ) , destweight , m_flFlexDelayedWeight ) ;
}
else
{
// send the flex values to the renderer
modelrender - > SetFlexWeights ( hdr - > numflexdesc ( ) , destweight ) ;
}
/*
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 ( ) , destweight [ 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 ] ) ;
}
*/
}
int C_BaseFlex : : g_numflexcontrollers ;
char * C_BaseFlex : : g_flexcontroller [ MAXSTUDIOFLEXCTRL * 4 ] ;
float C_BaseFlex : : g_flexweight [ MAXSTUDIOFLEXDESC ] ;
int C_BaseFlex : : AddGlobalFlexController ( 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 )
{
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 ;
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 ;
}
return false ;
}
//-----------------------------------------------------------------------------
// 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 ) ;
return ;
}
}
// 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 ;
}
void C_BaseFlex : : SetFlexWeight ( int index , float value )
{
if ( index > = 0 & & index < GetNumFlexControllers ( ) )
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return ;
mstudioflexcontroller_t * pflexcontroller = pstudiohdr - > pFlexcontroller ( index ) ;
if ( pflexcontroller - > max ! = pflexcontroller - > min )
{
value = ( value - pflexcontroller - > min ) / ( pflexcontroller - > max - pflexcontroller - > min ) ;
value = clamp ( value , 0.0 , 1.0 ) ;
}
m_flexWeight [ index ] = value ;
}
}
float C_BaseFlex : : GetFlexWeight ( int index )
{
if ( index > = 0 & & index < GetNumFlexControllers ( ) )
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return 0 ;
mstudioflexcontroller_t * pflexcontroller = pstudiohdr - > pFlexcontroller ( index ) ;
if ( pflexcontroller - > max ! = pflexcontroller - > min )
{
return m_flexWeight [ index ] * ( pflexcontroller - > max - pflexcontroller - > min ) + pflexcontroller - > min ;
}
return m_flexWeight [ index ] ;
}
return 0.0 ;
}
int C_BaseFlex : : FindFlexController ( const char * szName )
{
for ( int i = 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 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Default implementation
//-----------------------------------------------------------------------------
void C_BaseFlex : : ProcessSceneEvents ( bool bFlexEvents )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
// slowly decay to netural expression
int i ;
if ( bFlexEvents )
{
for ( i = 0 ; i < GetNumFlexControllers ( ) ; i + + )
{
SetFlexWeight ( i , GetFlexWeight ( i ) * 0.95 ) ;
}
}
// Iterate SceneEvents and look for active slots
for ( 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 ;
}
# define AllowSceneOverrides() 0
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 )
{
const flexsettinghdr_t * pOverrideHdr = NULL ;
// Find overrides, if any exist
CStudioHdr * hdr ;
if ( AllowSceneOverrides ( ) & & ( hdr = GetModelPtr ( ) ) ! = NULL )
{
char overridefile [ 512 ] ;
char shortname [ 128 ] ;
char modelname [ 128 ] ;
//Q_strncpy( modelname, modelinfo->GetModelName( model ) ,sizeof(modelname));
Q_strncpy ( modelname , hdr - > pszName ( ) , sizeof ( modelname ) ) ;
// Fix up the name
Q_FileBase ( modelname , shortname , sizeof ( shortname ) ) ;
Q_snprintf ( overridefile , sizeof ( overridefile ) , " %s/%s " , shortname , scenefile ) ;
pOverrideHdr = ( const flexsettinghdr_t * ) g_FlexSceneFileManager . FindSceneFile ( this , overridefile , true ) ;
}
float scenetime = scene - > GetTime ( ) ;
float scale = event - > GetIntensity ( event , scenetime ) ;
// Add the named expression
AddFlexSetting ( name , scale , pExpHdr , pOverrideHdr , ! 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 C_BaseFlex : : 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 ) ;
int index = result . m_Mapping [ key ] ;
return index ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *expr -
// scale -
// *pSettinghdr -
// *pOverrideHdr -
// newexpression -
//-----------------------------------------------------------------------------
void C_BaseFlex : : AddFlexSetting ( const char * expr , float scale ,
const flexsettinghdr_t * pSettinghdr , const flexsettinghdr_t * pOverrideHdr , 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 ( ! stricmp ( name , expr ) )
break ;
}
if ( i > = pSettinghdr - > numflexsettings )
{
return ;
}
// Update markov chain if needed
if ( newexpression )
{
if ( pSetting - > type = = FS_MARKOV )
{
NewMarkovIndex ( ( flexsetting_t * ) pSetting ) ;
}
}
// Resolve markov chain for the returned setting
pSetting = pSettinghdr - > pTranslatedSetting ( i ) ;
// Check for overrides
if ( AllowSceneOverrides ( ) & & pOverrideHdr )
{
// Get name from setting
const char * resolvedName = pSetting - > pszName ( ) ;
if ( resolvedName )
{
// See if resolvedName exists in the override file
const flexsetting_t * override = FindNamedSetting ( pOverrideHdr , resolvedName ) ;
if ( override )
{
// If so, point at the override file instead
pSettinghdr = pOverrideHdr ;
pSetting = override ;
}
}
}
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
int index = FlexControllerLocalToGlobal ( pSettinghdr , pWeights - > key ) ;
// Add scaled weighting in to total (post networking g_flexweight!!!!)
float value = g_flexweight [ index ] + scale * pWeights - > weight ;
g_flexweight [ index ] = value ;
}
}
//-----------------------------------------------------------------------------
// 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 ;
}
return false ;
}
//-----------------------------------------------------------------------------
// 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 ( 0 , FindFlexController ( name ) , 0 ) ;
Q_strncpy ( name , " left_ " , sizeof ( name ) ) ;
Q_strncat ( name , track - > GetFlexControllerName ( ) , sizeof ( name ) , COPY_ALL_CHARACTERS ) ;
track - > SetFlexControllerIndex ( 0 , FindFlexController ( name ) , 1 ) ;
}
else
{
track - > SetFlexControllerIndex ( 0 , FindFlexController ( ( char * ) track - > GetFlexControllerName ( ) ) ) ;
}
}
event - > SetTrackLookupSet ( true ) ;
}
if ( ! scene_clientflex . GetBool ( ) )
return ;
float scenetime = scene - > GetTime ( ) ;
float weight = event - > GetIntensity ( event , 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 + + )
{
int controller = track - > GetFlexControllerIndex ( side ) ;
// Get spline intensity for controller
float flIntensity = track - > GetIntensity ( scenetime , side ) ;
if ( controller > = 0 )
{
float orig = GetFlexWeight ( controller ) ;
float value = orig * ( 1 - weight ) + flIntensity * weight ;
SetFlexWeight ( controller , value ) ;
}
}
}
else
{
int controller = track - > GetFlexControllerIndex ( 0 ) ;
// Get spline intensity for controller
float flIntensity = track - > GetIntensity ( scenetime , 0 ) ;
if ( controller > = 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 )
{
2011-04-28 01:28:41 -05:00
m_flWeight = MIN ( m_flWeight + 0.1 , 1.0 ) ;
2008-09-15 01:00:17 -05:00
return m_flWeight ;
}