From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/client/c_sceneentity.cpp | 1211 ++++++++++++++++++++++++++++++++++ 1 file changed, 1211 insertions(+) create mode 100644 mp/src/game/client/c_sceneentity.cpp (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 new file mode 100644 index 00000000..d56a67c8 --- /dev/null +++ b/mp/src/game/client/c_sceneentity.cpp @@ -0,0 +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 ); +} \ No newline at end of file -- cgit v1.2.3