From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/client/c_sceneentity.cpp | 2420 +++++++++++++++++----------------- 1 file changed, 1210 insertions(+), 1210 deletions(-) (limited to 'mp/src/game/client/c_sceneentity.cpp') diff --git a/mp/src/game/client/c_sceneentity.cpp b/mp/src/game/client/c_sceneentity.cpp index d56a67c8..b44b59fd 100644 --- a/mp/src/game/client/c_sceneentity.cpp +++ b/mp/src/game/client/c_sceneentity.cpp @@ -1,1211 +1,1211 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "networkstringtable_clientdll.h" -#include "dt_utlvector_recv.h" -#include "choreoevent.h" -#include "choreoactor.h" -#include "choreochannel.h" -#include "filesystem.h" -#include "ichoreoeventcallback.h" -#include "scenefilecache/ISceneFileCache.h" -#include "materialsystem/imaterialsystemhardwareconfig.h" -#include "tier2/tier2.h" -#include "hud_closecaption.h" -#include "tier0/icommandline.h" - -#include "c_sceneentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Decodes animtime and notes when it changes -// Input : *pStruct - ( C_BaseEntity * ) used to flag animtime is changine -// *pVarData - -// *pIn - -// objectID - -//----------------------------------------------------------------------------- -void RecvProxy_ForcedClientTime( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - C_SceneEntity *pScene = reinterpret_cast< C_SceneEntity * >( pStruct ); - *(float *)pOut = pData->m_Value.m_Float; - pScene->OnResetClientTime(); -} - -#if defined( CSceneEntity ) -#undef CSceneEntity -#endif - -IMPLEMENT_CLIENTCLASS_DT(C_SceneEntity, DT_SceneEntity, CSceneEntity) - RecvPropInt(RECVINFO(m_nSceneStringIndex)), - RecvPropBool(RECVINFO(m_bIsPlayingBack)), - RecvPropBool(RECVINFO(m_bPaused)), - RecvPropBool(RECVINFO(m_bMultiplayer)), - RecvPropFloat(RECVINFO(m_flForceClientTime), 0, RecvProxy_ForcedClientTime ), - RecvPropUtlVector( - RECVINFO_UTLVECTOR( m_hActorList ), - MAX_ACTORS_IN_SCENE, - RecvPropEHandle(NULL, 0, 0)), -END_RECV_TABLE() - -C_SceneEntity::C_SceneEntity( void ) -{ - m_pScene = NULL; - m_bMultiplayer = false; - - m_hOwner = NULL; - m_bClientOnly = false; -} - -C_SceneEntity::~C_SceneEntity( void ) -{ - UnloadScene(); -} - -void C_SceneEntity::OnResetClientTime() -{ - // In TF2 we ignore this as the scene is played entirely client-side. -#ifndef TF_CLIENT_DLL - m_flCurrentTime = m_flForceClientTime; -#endif -} - -char const *C_SceneEntity::GetSceneFileName() -{ - return g_pStringTableClientSideChoreoScenes->GetString( m_nSceneStringIndex ); -} - -ConVar mp_usehwmvcds( "mp_usehwmvcds", "0", NULL, "Enable the use of the hw morph vcd(s). (-1 = never, 1 = always, 0 = based upon GPU)" ); // -1 = never, 0 = if hasfastvertextextures, 1 = always -bool UseHWMorphVCDs() -{ -// if ( mp_usehwmvcds.GetInt() == 0 ) -// return g_pMaterialSystemHardwareConfig->HasFastVertexTextures(); -// return mp_usehwmvcds.GetInt() > 0; - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool C_SceneEntity::GetHWMorphSceneFileName( const char *pFilename, char *pHWMFilename ) -{ - // Are we even using hardware morph? - if ( !UseHWMorphVCDs() ) - return false; - - // Multi-player only! - if ( !m_bMultiplayer ) - 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, "/high" ) != NULL ) || ( V_strstr( pFilename, "\\high" ) != NULL ) ) - { - V_strcpy( pHWMFilename, pFilename ); - return true; - } - - // Find the hardware morph scene name and pass that along as well. - char szScene[MAX_PATH]; - V_strcpy( szScene, pFilename ); - - char szSceneHWM[MAX_PATH]; - szSceneHWM[0] = '\0'; - - char *pszToken = strtok( szScene, "/\\" ); - while ( pszToken != NULL ) - { - if ( !V_stricmp( pszToken, "low" ) ) - { - V_strcat( szSceneHWM, "high", sizeof( szSceneHWM ) ); - } - else - { - V_strcat( szSceneHWM, pszToken, sizeof( szSceneHWM ) ); - } - - pszToken = strtok( NULL, "/\\" ); - if ( pszToken != NULL ) - { - V_strcat( szSceneHWM, "\\", sizeof( szSceneHWM ) ); - } - } - - V_strcpy( pHWMFilename, szSceneHWM ); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_SceneEntity::ResetActorFlexesForScene() -{ - int nActorCount = m_pScene->GetNumActors(); - for( int iActor = 0; iActor < nActorCount; ++iActor ) - { - CChoreoActor *pChoreoActor = m_pScene->GetActor( iActor ); - if ( !pChoreoActor ) - continue; - - C_BaseFlex *pFlexActor = FindNamedActor( pChoreoActor ); - if ( !pFlexActor ) - continue; - - CStudioHdr *pStudioHdr = pFlexActor->GetModelPtr(); - if ( !pStudioHdr ) - continue; - - if ( pStudioHdr->numflexdesc() == 0 ) - continue; - - // Reset the flex weights to their starting position. - LocalFlexController_t iController; - for ( iController = LocalFlexController_t(0); iController < pStudioHdr->numflexcontrollers(); ++iController ) - { - pFlexActor->SetFlexWeight( iController, 0.0f ); - } - - // Reset the prediction interpolation values. - pFlexActor->m_iv_flexWeight.Reset(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_SceneEntity::StopClientOnlyScene() -{ - if ( m_pScene ) - { - m_pScene->ResetSimulation(); - - if ( m_hOwner.Get() ) - { - m_hOwner->RemoveChoreoScene( m_pScene ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_SceneEntity::SetupClientOnlyScene( const char *pszFilename, C_BaseFlex *pOwner /* = NULL */, bool bMultiplayer /* = false */ ) -{ - m_bIsPlayingBack = true; - m_bMultiplayer = bMultiplayer; - m_hOwner = pOwner; - m_bClientOnly = true; - - char szFilename[128]; - Assert( V_strlen( pszFilename ) < 128 ); - V_strcpy( szFilename, pszFilename ); - - char szSceneHWM[128]; - if ( GetHWMorphSceneFileName( szFilename, szSceneHWM ) ) - { - V_strcpy( szFilename, szSceneHWM ); - } - - Assert( szFilename && szFilename[ 0 ] ); - if ( szFilename && szFilename[ 0 ] ) - { - LoadSceneFromFile( szFilename ); - - if (!CommandLine()->FindParm("-hushasserts")) - { - Assert( m_pScene ); - } - - // Should handle gestures and sequences client side. - if ( m_bMultiplayer ) - { - if ( m_pScene ) - { - int types[6]; - types[0] = CChoreoEvent::FLEXANIMATION; - types[1] = CChoreoEvent::EXPRESSION; - types[2] = CChoreoEvent::GESTURE; - types[3] = CChoreoEvent::SEQUENCE; - types[4] = CChoreoEvent::SPEAK; - types[5] = CChoreoEvent::LOOP; - m_pScene->RemoveEventsExceptTypes( types, 6 ); - } - - PrefetchAnimBlocks( m_pScene ); - } - else - { - if ( m_pScene ) - { - int types[ 2 ]; - types[ 0 ] = CChoreoEvent::FLEXANIMATION; - types[ 1 ] = CChoreoEvent::EXPRESSION; - m_pScene->RemoveEventsExceptTypes( types, 2 ); - } - } - - SetNextClientThink( CLIENT_THINK_ALWAYS ); - } - - if ( m_hOwner.Get() ) - { - if (!CommandLine()->FindParm("-hushasserts")) - { - Assert( m_pScene ); - } - - if ( m_pScene ) - { - ClearSceneEvents( m_pScene, false ); - - if ( m_bIsPlayingBack ) - { - m_pScene->ResetSimulation(); - m_hOwner->StartChoreoScene( m_pScene ); - } - else - { - m_pScene->ResetSimulation(); - m_hOwner->RemoveChoreoScene( m_pScene ); - } - - // Reset the flex weights when we start a new scene. This is normally done on the player model, but since - // we don't have a player here yet - we need to do this! - ResetActorFlexesForScene(); - } - } - else - { - for( int i = 0; i < m_hActorList.Count() ; ++i ) - { - C_BaseFlex *actor = m_hActorList[ i ].Get(); - if ( !actor ) - continue; - - Assert( m_pScene ); - - if ( m_pScene ) - { - ClearSceneEvents( m_pScene, false ); - - if ( m_bIsPlayingBack ) - { - m_pScene->ResetSimulation(); - actor->StartChoreoScene( m_pScene ); - } - else - { - m_pScene->ResetSimulation(); - actor->RemoveChoreoScene( m_pScene ); - } - } - } - } -} - -void C_SceneEntity::PostDataUpdate( DataUpdateType_t updateType ) -{ - BaseClass::PostDataUpdate( updateType ); - - char const *str = GetSceneFileName(); - char szFilename[MAX_PATH]; - if ( str ) - { - Assert( V_strlen( str ) < MAX_PATH ); - V_strcpy( szFilename, str ); - } - else - { - szFilename[0] = '\0'; - } - - char szSceneHWM[MAX_PATH]; - if ( GetHWMorphSceneFileName( szFilename, szSceneHWM ) ) - { - V_strcpy( szFilename, szSceneHWM ); - } - - if ( updateType == DATA_UPDATE_CREATED ) - { - Assert( szFilename && szFilename[ 0 ] ); - if ( szFilename && szFilename[ 0 ] ) - { - LoadSceneFromFile( szFilename ); - - // Kill everything except flex events - Assert( m_pScene ); - - // Should handle gestures and sequences clientside. - if ( m_bMultiplayer ) - { - if ( m_pScene ) - { - int types[6]; - types[0] = CChoreoEvent::FLEXANIMATION; - types[1] = CChoreoEvent::EXPRESSION; - types[2] = CChoreoEvent::GESTURE; - types[3] = CChoreoEvent::SEQUENCE; - types[4] = CChoreoEvent::SPEAK; - types[5] = CChoreoEvent::LOOP; - m_pScene->RemoveEventsExceptTypes( types, 6 ); - } - - PrefetchAnimBlocks( m_pScene ); - } - else - { - if ( m_pScene ) - { - int types[ 2 ]; - types[ 0 ] = CChoreoEvent::FLEXANIMATION; - types[ 1 ] = CChoreoEvent::EXPRESSION; - m_pScene->RemoveEventsExceptTypes( types, 2 ); - } - } - - SetNextClientThink( CLIENT_THINK_ALWAYS ); - } - } - - // Playback state changed... - if ( m_bWasPlaying != m_bIsPlayingBack ) - { - for(int i = 0; i < m_hActorList.Count() ; ++i ) - { - C_BaseFlex *actor = m_hActorList[ i ].Get(); - if ( !actor ) - continue; - - Assert( m_pScene ); - - if ( m_pScene ) - { - ClearSceneEvents( m_pScene, false ); - - if ( m_bIsPlayingBack ) - { - m_pScene->ResetSimulation(); - actor->StartChoreoScene( m_pScene ); - } - else - { - m_pScene->ResetSimulation(); - actor->RemoveChoreoScene( m_pScene ); - } - } - } - } -} - -void C_SceneEntity::PreDataUpdate( DataUpdateType_t updateType ) -{ - BaseClass::PreDataUpdate( updateType ); - - m_bWasPlaying = m_bIsPlayingBack; -} - -//----------------------------------------------------------------------------- -// Purpose: Called every frame that an event is active (Start/EndEvent as also -// called) -// Input : *event - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -void C_SceneEntity::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) -{ - // For now we only need to process events if we go back in time. - if ( currenttime < event->m_flPrevTime ) - { - //if ( !V_strstr( scene->GetFilename(), "idleloop" ) ) - //{ - // Msg( "ProcessEvent( %6.4f, %32s %6.4f ) %6.4f\n", currenttime, event->GetName(), event->m_flPrevTime, m_flCurrentTime ); - //} - - C_BaseFlex *pActor = NULL; - CChoreoActor *actor = event->GetActor(); - if ( actor ) - { - pActor = FindNamedActor( actor ); - if ( NULL == pActor ) - { - // TODO: QueueProcessEvent - // This can occur if we haven't been networked an actor yet... we need to queue it so that we can - // fire off the process event as soon as we have the actor resident on the client. - return; - } - } - - switch ( event->GetType() ) - { - case CChoreoEvent::GESTURE: - { - // Verify data. - Assert( m_bMultiplayer ); - Assert( scene != NULL ); - Assert( event != NULL ); - - if ( pActor ) - { - DispatchProcessGesture( scene, pActor, event ); - } - } - break; - case CChoreoEvent::SEQUENCE: - { - // Verify data. - Assert( m_bMultiplayer ); - Assert( scene != NULL ); - Assert( event != NULL ); - - if ( pActor ) - { - DispatchProcessSequence( scene, pActor, event ); - } - } - break; - } - } - - event->m_flPrevTime = currenttime; -} - -//----------------------------------------------------------------------------- -// Purpose: Called for events that are part of a pause condition -// Input : *event - -// Output : Returns true on event completed, false on non-completion. -//----------------------------------------------------------------------------- -bool C_SceneEntity::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) -{ - return true; -} - -C_BaseFlex *C_SceneEntity::FindNamedActor( CChoreoActor *pChoreoActor ) -{ - if ( !m_pScene ) - return NULL; - - if ( m_hOwner.Get() != NULL ) - { - return m_hOwner.Get(); - } - - int idx = m_pScene->FindActorIndex( pChoreoActor ); - if ( idx < 0 || idx >= m_hActorList.Count() ) - return NULL; - - return m_hActorList[ idx ].Get(); -} - -//----------------------------------------------------------------------------- -// Purpose: All events are leading edge triggered -// Input : currenttime - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) -{ - Assert( event ); - - if ( !Q_stricmp( event->GetName(), "NULL" ) ) - { - Scene_Printf( "%s : %8.2f: ignored %s\n", GetSceneFileName(), currenttime, event->GetDescription() ); - return; - } - - - C_BaseFlex *pActor = NULL; - CChoreoActor *actor = event->GetActor(); - if ( actor ) - { - pActor = FindNamedActor( actor ); - if ( NULL == pActor ) - { - // This can occur if we haven't been networked an actor yet... we need to queue it so that we can - // fire off the start event as soon as we have the actor resident on the client. - QueueStartEvent( currenttime, scene, event ); - return; - } - } - - Scene_Printf( "%s : %8.2f: start %s\n", GetSceneFileName(), currenttime, event->GetDescription() ); - - switch ( event->GetType() ) - { - case CChoreoEvent::FLEXANIMATION: - { - if ( pActor ) - { - DispatchStartFlexAnimation( scene, pActor, event ); - } - } - break; - case CChoreoEvent::EXPRESSION: - { - if ( pActor ) - { - DispatchStartExpression( scene, pActor, event ); - } - } - break; - case CChoreoEvent::GESTURE: - { - // Verify data. - Assert( m_bMultiplayer ); - Assert( scene != NULL ); - Assert( event != NULL ); - - if ( pActor ) - { - DispatchStartGesture( scene, pActor, event ); - } - } - break; - case CChoreoEvent::SEQUENCE: - { - // Verify data. - Assert( m_bMultiplayer ); - Assert( scene != NULL ); - Assert( event != NULL ); - - if ( pActor ) - { - DispatchStartSequence( scene, pActor, event ); - } - } - break; - case CChoreoEvent::LOOP: - { - // Verify data. - Assert( m_bMultiplayer ); - Assert( scene != NULL ); - Assert( event != NULL ); - - DispatchProcessLoop( scene, event ); - } - case CChoreoEvent::SPEAK: - { - if ( IsClientOnly() && pActor ) - { - // FIXME: dB hack. soundlevel needs to be moved into inside of wav? - soundlevel_t iSoundlevel = SNDLVL_TALKING; - if ( event->GetParameters2() ) - { - iSoundlevel = (soundlevel_t)atoi( event->GetParameters2() ); - if ( iSoundlevel == SNDLVL_NONE ) - { - iSoundlevel = SNDLVL_TALKING; - } - } - - DispatchStartSpeak( scene, pActor, event, iSoundlevel ); - } - } - break; - default: - break; - } - - event->m_flPrevTime = currenttime; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *scene - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event ) -{ - Assert( event->GetType() == CChoreoEvent::LOOP ); - - float backtime = (float)atof( event->GetParameters() ); - - bool process = true; - int counter = event->GetLoopCount(); - if ( counter != -1 ) - { - int remaining = event->GetNumLoopsRemaining(); - if ( remaining <= 0 ) - { - process = false; - } - else - { - event->SetNumLoopsRemaining( --remaining ); - } - } - - if ( !process ) - return; - - scene->LoopToTime( backtime ); - SetCurrentTime( backtime, true ); -} - -//----------------------------------------------------------------------------- -// Purpose: Playback sound file that contains phonemes -// Input : *actor - -// *parameters - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchStartSpeak( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel ) -{ - // Emit sound - if ( IsClientOnly() && actor ) - { - CSingleUserRecipientFilter filter( C_BasePlayer::GetLocalPlayer() ); - - float time_in_past = m_flCurrentTime - event->GetStartTime() ; - float soundtime = gpGlobals->curtime - time_in_past; - - EmitSound_t es; - es.m_nChannel = CHAN_VOICE; - es.m_flVolume = 1; - es.m_SoundLevel = iSoundlevel; - es.m_flSoundTime = soundtime; - - // No CC since we do it manually - // FIXME: This will change - es.m_bEmitCloseCaption = false; - es.m_pSoundName = event->GetParameters(); - - EmitSound( filter, actor->entindex(), es ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); - - // Close captioning only on master token no matter what... - if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) - { - char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; - bool validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ); - if ( validtoken ) - { - CRC32_t tokenCRC; - CRC32_Init( &tokenCRC ); - - char lowercase[ 256 ]; - Q_strncpy( lowercase, tok, sizeof( lowercase ) ); - Q_strlower( lowercase ); - - CRC32_ProcessBuffer( &tokenCRC, lowercase, Q_strlen( lowercase ) ); - CRC32_Final( &tokenCRC ); - - float endtime = event->GetLastSlaveEndTime(); - float durationShort = event->GetDuration(); - float durationLong = endtime - event->GetStartTime(); - float duration = MAX( durationShort, durationLong ); - - CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); - if ( hudCloseCaption ) - { - hudCloseCaption->ProcessCaption( lowercase, duration ); - } - } - - } - } -} - -void C_SceneEntity::DispatchEndSpeak( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - if ( IsClientOnly() ) - { - actor->RemoveSceneEvent( scene, event, false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : currenttime - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) -{ - Assert( event ); - - if ( !Q_stricmp( event->GetName(), "NULL" ) ) - { - return; - } - - C_BaseFlex *pActor = NULL; - CChoreoActor *actor = event->GetActor(); - if ( actor ) - { - pActor = FindNamedActor( actor ); - } - - Scene_Printf( "%s : %8.2f: finish %s\n", GetSceneFileName(), currenttime, event->GetDescription() ); - - switch ( event->GetType() ) - { - case CChoreoEvent::FLEXANIMATION: - { - if ( pActor ) - { - DispatchEndFlexAnimation( scene, pActor, event ); - } - } - break; - case CChoreoEvent::EXPRESSION: - { - if ( pActor ) - { - DispatchEndExpression( scene, pActor, event ); - } - } - break; - case CChoreoEvent::GESTURE: - { - if ( pActor ) - { - DispatchEndGesture( scene, pActor, event ); - } - } - break; - case CChoreoEvent::SEQUENCE: - { - if ( pActor ) - { - DispatchEndSequence( scene, pActor, event ); - } - } - break; - case CChoreoEvent::SPEAK: - { - if ( IsClientOnly() && pActor ) - { - DispatchEndSpeak( scene, pActor, event ); - } - } - break; - default: - break; - } -} - -bool CChoreoStringPool::GetString( short stringId, char *buff, int buffSize ) -{ - // fetch from compiled pool - const char *pString = scenefilecache->GetSceneString( stringId ); - if ( !pString ) - { - V_strncpy( buff, "", buffSize ); - return false; - } - V_strncpy( buff, pString, buffSize ); - return true; -} - -CChoreoStringPool g_ChoreoStringPool; - -CChoreoScene *C_SceneEntity::LoadScene( const char *filename ) -{ - char loadfile[ 512 ]; - Q_strncpy( loadfile, filename, sizeof( loadfile ) ); - Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) ); - Q_FixSlashes( loadfile ); - - char *pBuffer = NULL; - size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile ); - if ( bufsize <= 0 ) - return NULL; - - pBuffer = new char[ bufsize ]; - if ( !scenefilecache->GetSceneData( filename, (byte *)pBuffer, bufsize ) ) - { - delete[] pBuffer; - return NULL; - } - - CChoreoScene *pScene; - if ( IsBufferBinaryVCD( pBuffer, bufsize ) ) - { - pScene = new CChoreoScene( this ); - CUtlBuffer buf( pBuffer, bufsize, CUtlBuffer::READ_ONLY ); - if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) ) - { - Warning( "Unable to restore binary scene '%s'\n", loadfile ); - delete pScene; - pScene = NULL; - } - else - { - pScene->SetPrintFunc( Scene_Printf ); - pScene->SetEventCallbackInterface( this ); - } - } - else - { - g_TokenProcessor.SetBuffer( pBuffer ); - pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf ); - } - - delete[] pBuffer; - return pScene; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *filename - -//----------------------------------------------------------------------------- -void C_SceneEntity::LoadSceneFromFile( const char *filename ) -{ - UnloadScene(); - m_pScene = LoadScene( filename ); -} - -void C_SceneEntity::ClearSceneEvents( CChoreoScene *scene, bool canceled ) -{ - if ( !m_pScene ) - return; - - Scene_Printf( "%s : %8.2f: clearing events\n", GetSceneFileName(), m_flCurrentTime ); - - int i; - for ( i = 0 ; i < m_pScene->GetNumActors(); i++ ) - { - C_BaseFlex *pActor = FindNamedActor( m_pScene->GetActor( i ) ); - if ( !pActor ) - continue; - - // Clear any existing expressions - pActor->ClearSceneEvents( scene, canceled ); - } - - WipeQueuedEvents(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_SceneEntity::UnloadScene( void ) -{ - WipeQueuedEvents(); - - if ( m_pScene ) - { - ClearSceneEvents( m_pScene, false ); - for ( int i = 0 ; i < m_pScene->GetNumActors(); i++ ) - { - C_BaseFlex *pTestActor = FindNamedActor( m_pScene->GetActor( i ) ); - - if ( !pTestActor ) - continue; - - pTestActor->RemoveChoreoScene( m_pScene ); - } - } - delete m_pScene; - m_pScene = NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - actor->RemoveSceneEvent( scene, event, false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchStartExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *event - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchEndExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - actor->RemoveSceneEvent( scene, event, false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *parameters - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchStartGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - // Ingore null gestures - if ( !Q_stricmp( event->GetName(), "NULL" ) ) - return; - - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *parameters - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchProcessGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - // Ingore null gestures - if ( !Q_stricmp( event->GetName(), "NULL" ) ) - return; - - actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *parameters - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchEndGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) -{ - // Ingore null gestures - if ( !Q_stricmp( event->GetName(), "NULL" ) ) - return; - - actor->RemoveSceneEvent( scene, event, false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) -{ - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchProcessSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) -{ - actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -//----------------------------------------------------------------------------- -void C_SceneEntity::DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) -{ - actor->RemoveSceneEvent( scene, event, false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_SceneEntity::DoThink( float frametime ) -{ - if ( !m_pScene ) - return; - - if ( !m_bIsPlayingBack ) - { - WipeQueuedEvents(); - return; - } - - CheckQueuedEvents(); - - if ( m_bPaused ) - { - return; - } - - // Msg( "CL: %d, %f for %s\n", gpGlobals->tickcount, m_flCurrentTime, m_pScene->GetFilename() ); - - // Tell scene to go - m_pScene->Think( m_flCurrentTime ); - // Drive simulation time for scene - m_flCurrentTime += gpGlobals->frametime; -} - -void C_SceneEntity::ClientThink() -{ - DoThink( gpGlobals->frametime ); -} - -void C_SceneEntity::CheckQueuedEvents() -{ -// Check for duplicates - CUtlVector< QueuedEvents_t > events; - events = m_QueuedEvents; - m_QueuedEvents.RemoveAll(); - - int c = events.Count(); - for ( int i = 0; i < c; ++i ) - { - const QueuedEvents_t& check = events[ i ]; - - // Retry starting this event - StartEvent( check.starttime, check.scene, check.event ); - } -} - -void C_SceneEntity::WipeQueuedEvents() -{ - m_QueuedEvents.Purge(); -} - -void C_SceneEntity::QueueStartEvent( float starttime, CChoreoScene *scene, CChoreoEvent *event ) -{ - // Check for duplicates - int c = m_QueuedEvents.Count(); - for ( int i = 0; i < c; ++i ) - { - const QueuedEvents_t& check = m_QueuedEvents[ i ]; - if ( check.scene == scene && - check.event == event ) - return; - } - - QueuedEvents_t qe; - qe.scene = scene; - qe.event = event; - qe.starttime = starttime; - m_QueuedEvents.AddToTail( qe ); -} - -//----------------------------------------------------------------------------- -// Purpose: Resets time such that the client version of the .vcd is also updated, if appropriate -// Input : t - -// forceClientSync - unused for now, we may want to reenable this at some point -//----------------------------------------------------------------------------- -void C_SceneEntity::SetCurrentTime( float t, bool forceClientSync ) -{ - m_flCurrentTime = t; - m_flForceClientTime = t; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_SceneEntity::PrefetchAnimBlocks( CChoreoScene *pScene ) -{ - if (!CommandLine()->FindParm("-hushasserts")) - { - Assert( pScene && m_bMultiplayer ); - } - if ( !pScene || !m_bMultiplayer ) - return; - - // Build a fast lookup, too - CUtlMap actorMap( 0, 0, DefLessFunc( CChoreoActor* ) ); - - int nSpew = 0; - int nResident = 0; - int nChecked = 0; - - // Iterate events and precache necessary resources - for ( int i = 0; i < pScene->GetNumEvents(); i++ ) - { - CChoreoEvent *pEvent = pScene->GetEvent( i ); - if ( !pEvent ) - continue; - - // load any necessary data - switch ( pEvent->GetType() ) - { - default: - break; - case CChoreoEvent::SEQUENCE: - case CChoreoEvent::GESTURE: - { - CChoreoActor *pActor = pEvent->GetActor(); - if ( pActor ) - { - CBaseFlex *pFlex = NULL; - int idx = actorMap.Find( pActor ); - if ( idx == actorMap.InvalidIndex() ) - { - pFlex = FindNamedActor( pActor ); - idx = actorMap.Insert( pActor, pFlex ); - } - else - { - pFlex = actorMap[ idx ]; - } - - if ( pFlex ) - { - int iSequence = pFlex->LookupSequence( pEvent->GetParameters() ); - if ( iSequence >= 0 ) - { - CStudioHdr *pStudioHdr = pFlex->GetModelPtr(); - if ( pStudioHdr ) - { - // Now look up the animblock - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - for ( int i = 0 ; i < seqdesc.groupsize[ 0 ] ; ++i ) - { - for ( int j = 0; j < seqdesc.groupsize[ 1 ]; ++j ) - { - int iAnimation = seqdesc.anim( i, j ); - int iBaseAnimation = pStudioHdr->iRelativeAnim( iSequence, iAnimation ); - mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( iBaseAnimation ); - - ++nChecked; - - if ( nSpew != 0 ) - { - Msg( "%s checking block %d\n", pStudioHdr->pszName(), animdesc.animblock ); - } - - // Async load the animation - int iFrame = 0; - const mstudioanim_t *panim = animdesc.pAnim( &iFrame ); - if ( panim ) - { - ++nResident; - if ( nSpew > 1 ) - { - Msg( "%s:%s[%i:%i] was resident\n", pStudioHdr->pszName(), animdesc.pszName(), i, j ); - } - } - else - { - if ( nSpew != 0 ) - { - Msg( "%s:%s[%i:%i] async load\n", pStudioHdr->pszName(), animdesc.pszName(), i, j ); - } - } - } - } - } - } - } - } - break; - } - } - } - - if ( !nSpew || nChecked <= 0 ) - return; - - Msg( "%d of %d animations resident\n", nResident, nChecked ); +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "networkstringtable_clientdll.h" +#include "dt_utlvector_recv.h" +#include "choreoevent.h" +#include "choreoactor.h" +#include "choreochannel.h" +#include "filesystem.h" +#include "ichoreoeventcallback.h" +#include "scenefilecache/ISceneFileCache.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "tier2/tier2.h" +#include "hud_closecaption.h" +#include "tier0/icommandline.h" + +#include "c_sceneentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Decodes animtime and notes when it changes +// Input : *pStruct - ( C_BaseEntity * ) used to flag animtime is changine +// *pVarData - +// *pIn - +// objectID - +//----------------------------------------------------------------------------- +void RecvProxy_ForcedClientTime( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_SceneEntity *pScene = reinterpret_cast< C_SceneEntity * >( pStruct ); + *(float *)pOut = pData->m_Value.m_Float; + pScene->OnResetClientTime(); +} + +#if defined( CSceneEntity ) +#undef CSceneEntity +#endif + +IMPLEMENT_CLIENTCLASS_DT(C_SceneEntity, DT_SceneEntity, CSceneEntity) + RecvPropInt(RECVINFO(m_nSceneStringIndex)), + RecvPropBool(RECVINFO(m_bIsPlayingBack)), + RecvPropBool(RECVINFO(m_bPaused)), + RecvPropBool(RECVINFO(m_bMultiplayer)), + RecvPropFloat(RECVINFO(m_flForceClientTime), 0, RecvProxy_ForcedClientTime ), + RecvPropUtlVector( + RECVINFO_UTLVECTOR( m_hActorList ), + MAX_ACTORS_IN_SCENE, + RecvPropEHandle(NULL, 0, 0)), +END_RECV_TABLE() + +C_SceneEntity::C_SceneEntity( void ) +{ + m_pScene = NULL; + m_bMultiplayer = false; + + m_hOwner = NULL; + m_bClientOnly = false; +} + +C_SceneEntity::~C_SceneEntity( void ) +{ + UnloadScene(); +} + +void C_SceneEntity::OnResetClientTime() +{ + // In TF2 we ignore this as the scene is played entirely client-side. +#ifndef TF_CLIENT_DLL + m_flCurrentTime = m_flForceClientTime; +#endif +} + +char const *C_SceneEntity::GetSceneFileName() +{ + return g_pStringTableClientSideChoreoScenes->GetString( m_nSceneStringIndex ); +} + +ConVar mp_usehwmvcds( "mp_usehwmvcds", "0", NULL, "Enable the use of the hw morph vcd(s). (-1 = never, 1 = always, 0 = based upon GPU)" ); // -1 = never, 0 = if hasfastvertextextures, 1 = always +bool UseHWMorphVCDs() +{ +// if ( mp_usehwmvcds.GetInt() == 0 ) +// return g_pMaterialSystemHardwareConfig->HasFastVertexTextures(); +// return mp_usehwmvcds.GetInt() > 0; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_SceneEntity::GetHWMorphSceneFileName( const char *pFilename, char *pHWMFilename ) +{ + // Are we even using hardware morph? + if ( !UseHWMorphVCDs() ) + return false; + + // Multi-player only! + if ( !m_bMultiplayer ) + 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, "/high" ) != NULL ) || ( V_strstr( pFilename, "\\high" ) != NULL ) ) + { + V_strcpy( pHWMFilename, pFilename ); + return true; + } + + // Find the hardware morph scene name and pass that along as well. + char szScene[MAX_PATH]; + V_strcpy( szScene, pFilename ); + + char szSceneHWM[MAX_PATH]; + szSceneHWM[0] = '\0'; + + char *pszToken = strtok( szScene, "/\\" ); + while ( pszToken != NULL ) + { + if ( !V_stricmp( pszToken, "low" ) ) + { + V_strcat( szSceneHWM, "high", sizeof( szSceneHWM ) ); + } + else + { + V_strcat( szSceneHWM, pszToken, sizeof( szSceneHWM ) ); + } + + pszToken = strtok( NULL, "/\\" ); + if ( pszToken != NULL ) + { + V_strcat( szSceneHWM, "\\", sizeof( szSceneHWM ) ); + } + } + + V_strcpy( pHWMFilename, szSceneHWM ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_SceneEntity::ResetActorFlexesForScene() +{ + int nActorCount = m_pScene->GetNumActors(); + for( int iActor = 0; iActor < nActorCount; ++iActor ) + { + CChoreoActor *pChoreoActor = m_pScene->GetActor( iActor ); + if ( !pChoreoActor ) + continue; + + C_BaseFlex *pFlexActor = FindNamedActor( pChoreoActor ); + if ( !pFlexActor ) + continue; + + CStudioHdr *pStudioHdr = pFlexActor->GetModelPtr(); + if ( !pStudioHdr ) + continue; + + if ( pStudioHdr->numflexdesc() == 0 ) + continue; + + // Reset the flex weights to their starting position. + LocalFlexController_t iController; + for ( iController = LocalFlexController_t(0); iController < pStudioHdr->numflexcontrollers(); ++iController ) + { + pFlexActor->SetFlexWeight( iController, 0.0f ); + } + + // Reset the prediction interpolation values. + pFlexActor->m_iv_flexWeight.Reset(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_SceneEntity::StopClientOnlyScene() +{ + if ( m_pScene ) + { + m_pScene->ResetSimulation(); + + if ( m_hOwner.Get() ) + { + m_hOwner->RemoveChoreoScene( m_pScene ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_SceneEntity::SetupClientOnlyScene( const char *pszFilename, C_BaseFlex *pOwner /* = NULL */, bool bMultiplayer /* = false */ ) +{ + m_bIsPlayingBack = true; + m_bMultiplayer = bMultiplayer; + m_hOwner = pOwner; + m_bClientOnly = true; + + char szFilename[128]; + Assert( V_strlen( pszFilename ) < 128 ); + V_strcpy( szFilename, pszFilename ); + + char szSceneHWM[128]; + if ( GetHWMorphSceneFileName( szFilename, szSceneHWM ) ) + { + V_strcpy( szFilename, szSceneHWM ); + } + + Assert( szFilename && szFilename[ 0 ] ); + if ( szFilename && szFilename[ 0 ] ) + { + LoadSceneFromFile( szFilename ); + + if (!CommandLine()->FindParm("-hushasserts")) + { + Assert( m_pScene ); + } + + // Should handle gestures and sequences client side. + if ( m_bMultiplayer ) + { + if ( m_pScene ) + { + int types[6]; + types[0] = CChoreoEvent::FLEXANIMATION; + types[1] = CChoreoEvent::EXPRESSION; + types[2] = CChoreoEvent::GESTURE; + types[3] = CChoreoEvent::SEQUENCE; + types[4] = CChoreoEvent::SPEAK; + types[5] = CChoreoEvent::LOOP; + m_pScene->RemoveEventsExceptTypes( types, 6 ); + } + + PrefetchAnimBlocks( m_pScene ); + } + else + { + if ( m_pScene ) + { + int types[ 2 ]; + types[ 0 ] = CChoreoEvent::FLEXANIMATION; + types[ 1 ] = CChoreoEvent::EXPRESSION; + m_pScene->RemoveEventsExceptTypes( types, 2 ); + } + } + + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } + + if ( m_hOwner.Get() ) + { + if (!CommandLine()->FindParm("-hushasserts")) + { + Assert( m_pScene ); + } + + if ( m_pScene ) + { + ClearSceneEvents( m_pScene, false ); + + if ( m_bIsPlayingBack ) + { + m_pScene->ResetSimulation(); + m_hOwner->StartChoreoScene( m_pScene ); + } + else + { + m_pScene->ResetSimulation(); + m_hOwner->RemoveChoreoScene( m_pScene ); + } + + // Reset the flex weights when we start a new scene. This is normally done on the player model, but since + // we don't have a player here yet - we need to do this! + ResetActorFlexesForScene(); + } + } + else + { + for( int i = 0; i < m_hActorList.Count() ; ++i ) + { + C_BaseFlex *actor = m_hActorList[ i ].Get(); + if ( !actor ) + continue; + + Assert( m_pScene ); + + if ( m_pScene ) + { + ClearSceneEvents( m_pScene, false ); + + if ( m_bIsPlayingBack ) + { + m_pScene->ResetSimulation(); + actor->StartChoreoScene( m_pScene ); + } + else + { + m_pScene->ResetSimulation(); + actor->RemoveChoreoScene( m_pScene ); + } + } + } + } +} + +void C_SceneEntity::PostDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PostDataUpdate( updateType ); + + char const *str = GetSceneFileName(); + char szFilename[MAX_PATH]; + if ( str ) + { + Assert( V_strlen( str ) < MAX_PATH ); + V_strcpy( szFilename, str ); + } + else + { + szFilename[0] = '\0'; + } + + char szSceneHWM[MAX_PATH]; + if ( GetHWMorphSceneFileName( szFilename, szSceneHWM ) ) + { + V_strcpy( szFilename, szSceneHWM ); + } + + if ( updateType == DATA_UPDATE_CREATED ) + { + Assert( szFilename && szFilename[ 0 ] ); + if ( szFilename && szFilename[ 0 ] ) + { + LoadSceneFromFile( szFilename ); + + // Kill everything except flex events + Assert( m_pScene ); + + // Should handle gestures and sequences clientside. + if ( m_bMultiplayer ) + { + if ( m_pScene ) + { + int types[6]; + types[0] = CChoreoEvent::FLEXANIMATION; + types[1] = CChoreoEvent::EXPRESSION; + types[2] = CChoreoEvent::GESTURE; + types[3] = CChoreoEvent::SEQUENCE; + types[4] = CChoreoEvent::SPEAK; + types[5] = CChoreoEvent::LOOP; + m_pScene->RemoveEventsExceptTypes( types, 6 ); + } + + PrefetchAnimBlocks( m_pScene ); + } + else + { + if ( m_pScene ) + { + int types[ 2 ]; + types[ 0 ] = CChoreoEvent::FLEXANIMATION; + types[ 1 ] = CChoreoEvent::EXPRESSION; + m_pScene->RemoveEventsExceptTypes( types, 2 ); + } + } + + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } + } + + // Playback state changed... + if ( m_bWasPlaying != m_bIsPlayingBack ) + { + for(int i = 0; i < m_hActorList.Count() ; ++i ) + { + C_BaseFlex *actor = m_hActorList[ i ].Get(); + if ( !actor ) + continue; + + Assert( m_pScene ); + + if ( m_pScene ) + { + ClearSceneEvents( m_pScene, false ); + + if ( m_bIsPlayingBack ) + { + m_pScene->ResetSimulation(); + actor->StartChoreoScene( m_pScene ); + } + else + { + m_pScene->ResetSimulation(); + actor->RemoveChoreoScene( m_pScene ); + } + } + } + } +} + +void C_SceneEntity::PreDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PreDataUpdate( updateType ); + + m_bWasPlaying = m_bIsPlayingBack; +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame that an event is active (Start/EndEvent as also +// called) +// Input : *event - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +void C_SceneEntity::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + // For now we only need to process events if we go back in time. + if ( currenttime < event->m_flPrevTime ) + { + //if ( !V_strstr( scene->GetFilename(), "idleloop" ) ) + //{ + // Msg( "ProcessEvent( %6.4f, %32s %6.4f ) %6.4f\n", currenttime, event->GetName(), event->m_flPrevTime, m_flCurrentTime ); + //} + + C_BaseFlex *pActor = NULL; + CChoreoActor *actor = event->GetActor(); + if ( actor ) + { + pActor = FindNamedActor( actor ); + if ( NULL == pActor ) + { + // TODO: QueueProcessEvent + // This can occur if we haven't been networked an actor yet... we need to queue it so that we can + // fire off the process event as soon as we have the actor resident on the client. + return; + } + } + + switch ( event->GetType() ) + { + case CChoreoEvent::GESTURE: + { + // Verify data. + Assert( m_bMultiplayer ); + Assert( scene != NULL ); + Assert( event != NULL ); + + if ( pActor ) + { + DispatchProcessGesture( scene, pActor, event ); + } + } + break; + case CChoreoEvent::SEQUENCE: + { + // Verify data. + Assert( m_bMultiplayer ); + Assert( scene != NULL ); + Assert( event != NULL ); + + if ( pActor ) + { + DispatchProcessSequence( scene, pActor, event ); + } + } + break; + } + } + + event->m_flPrevTime = currenttime; +} + +//----------------------------------------------------------------------------- +// Purpose: Called for events that are part of a pause condition +// Input : *event - +// Output : Returns true on event completed, false on non-completion. +//----------------------------------------------------------------------------- +bool C_SceneEntity::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + return true; +} + +C_BaseFlex *C_SceneEntity::FindNamedActor( CChoreoActor *pChoreoActor ) +{ + if ( !m_pScene ) + return NULL; + + if ( m_hOwner.Get() != NULL ) + { + return m_hOwner.Get(); + } + + int idx = m_pScene->FindActorIndex( pChoreoActor ); + if ( idx < 0 || idx >= m_hActorList.Count() ) + return NULL; + + return m_hActorList[ idx ].Get(); +} + +//----------------------------------------------------------------------------- +// Purpose: All events are leading edge triggered +// Input : currenttime - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + Assert( event ); + + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + { + Scene_Printf( "%s : %8.2f: ignored %s\n", GetSceneFileName(), currenttime, event->GetDescription() ); + return; + } + + + C_BaseFlex *pActor = NULL; + CChoreoActor *actor = event->GetActor(); + if ( actor ) + { + pActor = FindNamedActor( actor ); + if ( NULL == pActor ) + { + // This can occur if we haven't been networked an actor yet... we need to queue it so that we can + // fire off the start event as soon as we have the actor resident on the client. + QueueStartEvent( currenttime, scene, event ); + return; + } + } + + Scene_Printf( "%s : %8.2f: start %s\n", GetSceneFileName(), currenttime, event->GetDescription() ); + + switch ( event->GetType() ) + { + case CChoreoEvent::FLEXANIMATION: + { + if ( pActor ) + { + DispatchStartFlexAnimation( scene, pActor, event ); + } + } + break; + case CChoreoEvent::EXPRESSION: + { + if ( pActor ) + { + DispatchStartExpression( scene, pActor, event ); + } + } + break; + case CChoreoEvent::GESTURE: + { + // Verify data. + Assert( m_bMultiplayer ); + Assert( scene != NULL ); + Assert( event != NULL ); + + if ( pActor ) + { + DispatchStartGesture( scene, pActor, event ); + } + } + break; + case CChoreoEvent::SEQUENCE: + { + // Verify data. + Assert( m_bMultiplayer ); + Assert( scene != NULL ); + Assert( event != NULL ); + + if ( pActor ) + { + DispatchStartSequence( scene, pActor, event ); + } + } + break; + case CChoreoEvent::LOOP: + { + // Verify data. + Assert( m_bMultiplayer ); + Assert( scene != NULL ); + Assert( event != NULL ); + + DispatchProcessLoop( scene, event ); + } + case CChoreoEvent::SPEAK: + { + if ( IsClientOnly() && pActor ) + { + // FIXME: dB hack. soundlevel needs to be moved into inside of wav? + soundlevel_t iSoundlevel = SNDLVL_TALKING; + if ( event->GetParameters2() ) + { + iSoundlevel = (soundlevel_t)atoi( event->GetParameters2() ); + if ( iSoundlevel == SNDLVL_NONE ) + { + iSoundlevel = SNDLVL_TALKING; + } + } + + DispatchStartSpeak( scene, pActor, event, iSoundlevel ); + } + } + break; + default: + break; + } + + event->m_flPrevTime = currenttime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *scene - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event ) +{ + Assert( event->GetType() == CChoreoEvent::LOOP ); + + float backtime = (float)atof( event->GetParameters() ); + + bool process = true; + int counter = event->GetLoopCount(); + if ( counter != -1 ) + { + int remaining = event->GetNumLoopsRemaining(); + if ( remaining <= 0 ) + { + process = false; + } + else + { + event->SetNumLoopsRemaining( --remaining ); + } + } + + if ( !process ) + return; + + scene->LoopToTime( backtime ); + SetCurrentTime( backtime, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Playback sound file that contains phonemes +// Input : *actor - +// *parameters - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchStartSpeak( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel ) +{ + // Emit sound + if ( IsClientOnly() && actor ) + { + CSingleUserRecipientFilter filter( C_BasePlayer::GetLocalPlayer() ); + + float time_in_past = m_flCurrentTime - event->GetStartTime() ; + float soundtime = gpGlobals->curtime - time_in_past; + + EmitSound_t es; + es.m_nChannel = CHAN_VOICE; + es.m_flVolume = 1; + es.m_SoundLevel = iSoundlevel; + es.m_flSoundTime = soundtime; + + // No CC since we do it manually + // FIXME: This will change + es.m_bEmitCloseCaption = false; + es.m_pSoundName = event->GetParameters(); + + EmitSound( filter, actor->entindex(), es ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + + // Close captioning only on master token no matter what... + if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) + { + char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; + bool validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ); + if ( validtoken ) + { + CRC32_t tokenCRC; + CRC32_Init( &tokenCRC ); + + char lowercase[ 256 ]; + Q_strncpy( lowercase, tok, sizeof( lowercase ) ); + Q_strlower( lowercase ); + + CRC32_ProcessBuffer( &tokenCRC, lowercase, Q_strlen( lowercase ) ); + CRC32_Final( &tokenCRC ); + + float endtime = event->GetLastSlaveEndTime(); + float durationShort = event->GetDuration(); + float durationLong = endtime - event->GetStartTime(); + float duration = MAX( durationShort, durationLong ); + + CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); + if ( hudCloseCaption ) + { + hudCloseCaption->ProcessCaption( lowercase, duration ); + } + } + + } + } +} + +void C_SceneEntity::DispatchEndSpeak( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + if ( IsClientOnly() ) + { + actor->RemoveSceneEvent( scene, event, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : currenttime - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + Assert( event ); + + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + { + return; + } + + C_BaseFlex *pActor = NULL; + CChoreoActor *actor = event->GetActor(); + if ( actor ) + { + pActor = FindNamedActor( actor ); + } + + Scene_Printf( "%s : %8.2f: finish %s\n", GetSceneFileName(), currenttime, event->GetDescription() ); + + switch ( event->GetType() ) + { + case CChoreoEvent::FLEXANIMATION: + { + if ( pActor ) + { + DispatchEndFlexAnimation( scene, pActor, event ); + } + } + break; + case CChoreoEvent::EXPRESSION: + { + if ( pActor ) + { + DispatchEndExpression( scene, pActor, event ); + } + } + break; + case CChoreoEvent::GESTURE: + { + if ( pActor ) + { + DispatchEndGesture( scene, pActor, event ); + } + } + break; + case CChoreoEvent::SEQUENCE: + { + if ( pActor ) + { + DispatchEndSequence( scene, pActor, event ); + } + } + break; + case CChoreoEvent::SPEAK: + { + if ( IsClientOnly() && pActor ) + { + DispatchEndSpeak( scene, pActor, event ); + } + } + break; + default: + break; + } +} + +bool CChoreoStringPool::GetString( short stringId, char *buff, int buffSize ) +{ + // fetch from compiled pool + const char *pString = scenefilecache->GetSceneString( stringId ); + if ( !pString ) + { + V_strncpy( buff, "", buffSize ); + return false; + } + V_strncpy( buff, pString, buffSize ); + return true; +} + +CChoreoStringPool g_ChoreoStringPool; + +CChoreoScene *C_SceneEntity::LoadScene( const char *filename ) +{ + char loadfile[ 512 ]; + Q_strncpy( loadfile, filename, sizeof( loadfile ) ); + Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) ); + Q_FixSlashes( loadfile ); + + char *pBuffer = NULL; + size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile ); + if ( bufsize <= 0 ) + return NULL; + + pBuffer = new char[ bufsize ]; + if ( !scenefilecache->GetSceneData( filename, (byte *)pBuffer, bufsize ) ) + { + delete[] pBuffer; + return NULL; + } + + CChoreoScene *pScene; + if ( IsBufferBinaryVCD( pBuffer, bufsize ) ) + { + pScene = new CChoreoScene( this ); + CUtlBuffer buf( pBuffer, bufsize, CUtlBuffer::READ_ONLY ); + if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) ) + { + Warning( "Unable to restore binary scene '%s'\n", loadfile ); + delete pScene; + pScene = NULL; + } + else + { + pScene->SetPrintFunc( Scene_Printf ); + pScene->SetEventCallbackInterface( this ); + } + } + else + { + g_TokenProcessor.SetBuffer( pBuffer ); + pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf ); + } + + delete[] pBuffer; + return pScene; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +//----------------------------------------------------------------------------- +void C_SceneEntity::LoadSceneFromFile( const char *filename ) +{ + UnloadScene(); + m_pScene = LoadScene( filename ); +} + +void C_SceneEntity::ClearSceneEvents( CChoreoScene *scene, bool canceled ) +{ + if ( !m_pScene ) + return; + + Scene_Printf( "%s : %8.2f: clearing events\n", GetSceneFileName(), m_flCurrentTime ); + + int i; + for ( i = 0 ; i < m_pScene->GetNumActors(); i++ ) + { + C_BaseFlex *pActor = FindNamedActor( m_pScene->GetActor( i ) ); + if ( !pActor ) + continue; + + // Clear any existing expressions + pActor->ClearSceneEvents( scene, canceled ); + } + + WipeQueuedEvents(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_SceneEntity::UnloadScene( void ) +{ + WipeQueuedEvents(); + + if ( m_pScene ) + { + ClearSceneEvents( m_pScene, false ); + for ( int i = 0 ; i < m_pScene->GetNumActors(); i++ ) + { + C_BaseFlex *pTestActor = FindNamedActor( m_pScene->GetActor( i ) ); + + if ( !pTestActor ) + continue; + + pTestActor->RemoveChoreoScene( m_pScene ); + } + } + delete m_pScene; + m_pScene = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + actor->RemoveSceneEvent( scene, event, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchStartExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *event - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchEndExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + actor->RemoveSceneEvent( scene, event, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *parameters - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchStartGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + // Ingore null gestures + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + return; + + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *parameters - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchProcessGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + // Ingore null gestures + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + return; + + actor->RemoveSceneEvent( scene, event, false ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *parameters - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchEndGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) +{ + // Ingore null gestures + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + return; + + actor->RemoveSceneEvent( scene, event, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) +{ + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchProcessSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) +{ + actor->RemoveSceneEvent( scene, event, false ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void C_SceneEntity::DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) +{ + actor->RemoveSceneEvent( scene, event, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_SceneEntity::DoThink( float frametime ) +{ + if ( !m_pScene ) + return; + + if ( !m_bIsPlayingBack ) + { + WipeQueuedEvents(); + return; + } + + CheckQueuedEvents(); + + if ( m_bPaused ) + { + return; + } + + // Msg( "CL: %d, %f for %s\n", gpGlobals->tickcount, m_flCurrentTime, m_pScene->GetFilename() ); + + // Tell scene to go + m_pScene->Think( m_flCurrentTime ); + // Drive simulation time for scene + m_flCurrentTime += gpGlobals->frametime; +} + +void C_SceneEntity::ClientThink() +{ + DoThink( gpGlobals->frametime ); +} + +void C_SceneEntity::CheckQueuedEvents() +{ +// Check for duplicates + CUtlVector< QueuedEvents_t > events; + events = m_QueuedEvents; + m_QueuedEvents.RemoveAll(); + + int c = events.Count(); + for ( int i = 0; i < c; ++i ) + { + const QueuedEvents_t& check = events[ i ]; + + // Retry starting this event + StartEvent( check.starttime, check.scene, check.event ); + } +} + +void C_SceneEntity::WipeQueuedEvents() +{ + m_QueuedEvents.Purge(); +} + +void C_SceneEntity::QueueStartEvent( float starttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + // Check for duplicates + int c = m_QueuedEvents.Count(); + for ( int i = 0; i < c; ++i ) + { + const QueuedEvents_t& check = m_QueuedEvents[ i ]; + if ( check.scene == scene && + check.event == event ) + return; + } + + QueuedEvents_t qe; + qe.scene = scene; + qe.event = event; + qe.starttime = starttime; + m_QueuedEvents.AddToTail( qe ); +} + +//----------------------------------------------------------------------------- +// Purpose: Resets time such that the client version of the .vcd is also updated, if appropriate +// Input : t - +// forceClientSync - unused for now, we may want to reenable this at some point +//----------------------------------------------------------------------------- +void C_SceneEntity::SetCurrentTime( float t, bool forceClientSync ) +{ + m_flCurrentTime = t; + m_flForceClientTime = t; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_SceneEntity::PrefetchAnimBlocks( CChoreoScene *pScene ) +{ + if (!CommandLine()->FindParm("-hushasserts")) + { + Assert( pScene && m_bMultiplayer ); + } + if ( !pScene || !m_bMultiplayer ) + return; + + // Build a fast lookup, too + CUtlMap actorMap( 0, 0, DefLessFunc( CChoreoActor* ) ); + + int nSpew = 0; + int nResident = 0; + int nChecked = 0; + + // Iterate events and precache necessary resources + for ( int i = 0; i < pScene->GetNumEvents(); i++ ) + { + CChoreoEvent *pEvent = pScene->GetEvent( i ); + if ( !pEvent ) + continue; + + // load any necessary data + switch ( pEvent->GetType() ) + { + default: + break; + case CChoreoEvent::SEQUENCE: + case CChoreoEvent::GESTURE: + { + CChoreoActor *pActor = pEvent->GetActor(); + if ( pActor ) + { + CBaseFlex *pFlex = NULL; + int idx = actorMap.Find( pActor ); + if ( idx == actorMap.InvalidIndex() ) + { + pFlex = FindNamedActor( pActor ); + idx = actorMap.Insert( pActor, pFlex ); + } + else + { + pFlex = actorMap[ idx ]; + } + + if ( pFlex ) + { + int iSequence = pFlex->LookupSequence( pEvent->GetParameters() ); + if ( iSequence >= 0 ) + { + CStudioHdr *pStudioHdr = pFlex->GetModelPtr(); + if ( pStudioHdr ) + { + // Now look up the animblock + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + for ( int i = 0 ; i < seqdesc.groupsize[ 0 ] ; ++i ) + { + for ( int j = 0; j < seqdesc.groupsize[ 1 ]; ++j ) + { + int iAnimation = seqdesc.anim( i, j ); + int iBaseAnimation = pStudioHdr->iRelativeAnim( iSequence, iAnimation ); + mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( iBaseAnimation ); + + ++nChecked; + + if ( nSpew != 0 ) + { + Msg( "%s checking block %d\n", pStudioHdr->pszName(), animdesc.animblock ); + } + + // Async load the animation + int iFrame = 0; + const mstudioanim_t *panim = animdesc.pAnim( &iFrame ); + if ( panim ) + { + ++nResident; + if ( nSpew > 1 ) + { + Msg( "%s:%s[%i:%i] was resident\n", pStudioHdr->pszName(), animdesc.pszName(), i, j ); + } + } + else + { + if ( nSpew != 0 ) + { + Msg( "%s:%s[%i:%i] async load\n", pStudioHdr->pszName(), animdesc.pszName(), i, j ); + } + } + } + } + } + } + } + } + break; + } + } + } + + if ( !nSpew || nChecked <= 0 ) + return; + + Msg( "%d of %d animations resident\n", nResident, nChecked ); } \ No newline at end of file -- cgit v1.2.3