diff --git a/sp/src/game/client/ShaderEditor/ISEdit_ModelRender.h b/sp/src/game/client/ShaderEditor/ISEdit_ModelRender.h new file mode 100644 index 00000000..6db545e9 --- /dev/null +++ b/sp/src/game/client/ShaderEditor/ISEdit_ModelRender.h @@ -0,0 +1,41 @@ +#ifndef IV_SHADEREDITOR_MRENDER +#define IV_SHADEREDITOR_MRENDER + +#ifdef _WIN32 +#pragma once +#endif // _WIN32 + +#ifdef SHADER_EDITOR_DLL +#include "../public/tier1/interface.h" +#else +#include "interface.h" +#include "ShaderEditor/ShaderEditorSystem.h" +#endif // NOT SHADER_EDITOR_DLL + + +class ISEditModelRender +{ +public: + virtual bool LoadModel( const char *localPath ) = 0; + virtual void DestroyModel() = 0; + virtual void GetModelCenter( float *pFl3_ViewOffset ) = 0; + + virtual int QuerySequences( char ***list ) = 0; + virtual void SetSequence( const char *name ) = 0; + + virtual void ExecRender() = 0; + virtual void DoPostProc( int x, int y, int w, int h ) = 0; + virtual int MaterialPicker( char ***szMat ) = 0; + + virtual void DestroyCharPtrList( char ***szList ) = 0; +}; + + +#ifdef SHADER_EDITOR_DLL +extern ISEditModelRender *sEditMRender; +#else +class SEditModelRender; +extern SEditModelRender *sEditMRender; +#endif + +#endif \ No newline at end of file diff --git a/sp/src/game/client/ShaderEditor/IVShaderEditor.h b/sp/src/game/client/ShaderEditor/IVShaderEditor.h new file mode 100644 index 00000000..d1b8e443 --- /dev/null +++ b/sp/src/game/client/ShaderEditor/IVShaderEditor.h @@ -0,0 +1,128 @@ +#ifndef IV_SHADEREDITOR +#define IV_SHADEREDITOR + +#ifdef _WIN32 +#pragma once +#endif // _WIN32 + +#define pFnClCallback( x ) void(* x )( float *pfl4 ) +#define pFnClCallback_Declare( x ) void x( float *pfl4 ) + +#define pFnVrCallback( x ) void(* x )( bool * const pbOptions, int * const piOptions,\ + float * const pflOptions, char ** const pszOptions ) +#define pFnVrCallback_Declare( x ) void x( bool * const pbOptions, int * const piOptions,\ + float * const pflOptions, char ** const pszOptions ) + +#ifndef PROCSHADER_DLL + +#ifdef SHADER_EDITOR_DLL +#include "../public/tier1/interface.h" +#include "view_shared.h" +#else +#include "interface.h" +#include "ShaderEditor/ShaderEditorSystem.h" +#endif // NOT SHADER_EDITOR_DLL + +enum SEDIT_SKYMASK_MODE +{ + SKYMASK_OFF = 0, + SKYMASK_QUARTER, // render at 1/4 fb size where possible + SKYMASK_FULL, // render at full fb size +}; + +class CViewSetup_SEdit_Shared +{ +public: + CViewSetup_SEdit_Shared() + { + Q_memset( this, 0, sizeof( CViewSetup_SEdit_Shared ) ); + }; + CViewSetup_SEdit_Shared( const CViewSetup &o ) + { + x = o.x; + y = o.y; + width = o.width; + height = o.height; + fov = o.fov; + fovViewmodel = o.fovViewmodel; + origin = o.origin; + angles = o.angles; + zNear = o.zNear; + zFar = o.zFar; + zNearViewmodel = o.zNearViewmodel; + zFarViewmodel = o.zFarViewmodel; + m_flAspectRatio = o.m_flAspectRatio; + }; + int x,y,width,height; + float fov,fovViewmodel; + Vector origin; + QAngle angles; + float zNear,zFar,zNearViewmodel,zFarViewmodel; + float m_flAspectRatio; +}; + + +class IVShaderEditor : public IBaseInterface +{ +public: + virtual bool Init( CreateInterfaceFn appSystemFactory, CGlobalVarsBase *pGlobals, + void *pSEditMRender, + bool bCreateEditor, bool bEnablePrimaryDebug, int iSkymaskMode ) = 0; + virtual void Shutdown() = 0; + virtual void PrecacheData() = 0; + + // call before Init() to overwrite any paths, pass in NULL for the ones that shouldn't be overwritten + virtual void OverridePaths( const char *pszWorkingDirectory, + const char *pszCompilePath = NULL, // abs path to compiler binaries + const char *pszLocalCompilePath = NULL, // local path to compiler binaries, relative to shader source directory + const char *pszGamePath = NULL, + const char *pszCanvas = NULL, // path to canvas files + const char *pszShaderSource = NULL, // path to shader source files + const char *pszDumps = NULL, // path to shader configuration files + const char *pszUserFunctions = NULL, // path to custom function bodies + const char *pszEditorRoot = NULL ) = 0; // path to 'shadereditorui' home directory + + // update the lib + virtual void OnFrame( float frametime ) = 0; + virtual void OnPreRender( void *viewsetup ) = 0; + virtual void OnSceneRender() = 0; + virtual void OnUpdateSkymask(bool bCombineMode, int x, int y, int w, int h) = 0; + virtual void OnPostRender( bool bUpdateFB ) = 0; + + // data callbacks for hlsl constants + virtual void RegisterClientCallback( const char *name, pFnClCallback(callback), int numComponents ) = 0; + virtual void LockClientCallbacks() = 0; + + // view render callbacks for post processing graphs + virtual void RegisterViewRenderCallback( const char *pszVrCName, pFnVrCallback(callback), + const char **pszBoolNames = NULL, const bool *pBoolDefaults = NULL, const int numBoolParams = 0, + const char **pszIntNames = NULL, const int *pIntDefaults = NULL, const int numIntParams = 0, + const char **pszFloatNames = NULL, const float *pFloatDefaults = NULL, const int numFloatParams = 0, + const char **pszStringNames = NULL, const char **pStringDefaults = NULL, const int numStringParams = 0 ) = 0; + virtual void LockViewRenderCallbacks() = 0; + + // post processing effect manipulation (precached effects accessible) + // the index becomes invalid when editing the precache list + virtual int GetPPEIndex( const char *pszName ) = 0; // returns -1 when not found, case insensitive + virtual bool IsPPEEnabled( const int &index ) = 0; + virtual void SetPPEEnabled( const int &index, const bool &bEnabled ) = 0; + virtual IMaterial *GetPPEMaterial( const int &index, const char *pszNodeName ) = 0; + + // Draws a PPE graph right now or adds it to the render queue (r_queued_post_processing!) + // Does not push a new RT but uses the current one + // If you have 'during scene' nodes, make sure to call it twice in the appropriate places + virtual void DrawPPEOnDemand( const int &index, const bool bInScene = false ) = 0; +}; + +#define SHADEREDIT_INTERFACE_VERSION "ShaderEditor005" + +#ifdef SHADER_EDITOR_DLL +class ShaderEditorInterface; +extern ShaderEditorInterface *shaderEdit; +#else +extern IVShaderEditor *shaderEdit; +#endif // NOT SHADER_EDITOR_DLL + +#endif // NOT PROCSHADER_DLL + +#endif // NOT IV_SHADEREDITOR \ No newline at end of file diff --git a/sp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp b/sp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp new file mode 100644 index 00000000..9ab839c2 --- /dev/null +++ b/sp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp @@ -0,0 +1,424 @@ +// ****************************************************** +// +// Purpose: +// - Handles model rendering requests from the +// shader editor library +// +// ****************************************************** + +#include "cbase.h" + +#include "vgui/iinput.h" +#include "vgui_controls/controls.h" + +#include "ShaderEditor/SEdit_ModelRender.h" +#include "model_types.h" + +#ifndef SOURCE_2006 +#include "viewpostprocess.h" +#endif + +#include "view.h" +#include "input.h" + +#include "beamdraw.h" + +#ifdef SOURCE_2006 +void ScreenToWorld( int mousex, int mousey, float fov, + const Vector& vecRenderOrigin, + const QAngle& vecRenderAngles, + Vector& vecPickingRay ) +{ + float dx, dy; + float c_x, c_y; + float dist; + Vector vpn, vup, vright; + + float scaled_fov = ScaleFOVByWidthRatio( fov, engine->GetScreenAspectRatio() * 0.75f ); + + c_x = ScreenWidth() / 2; + c_y = ScreenHeight() / 2; + + dx = (float)mousex - c_x; + dy = c_y - (float)mousey; + + float dist_denom = tan(M_PI * scaled_fov / 360.0f); + dist = c_x / dist_denom; + AngleVectors( vecRenderAngles, &vpn, &vright, &vup ); + vecPickingRay = vpn * dist + vright * ( dx ) + vup * ( dy ); + VectorNormalize( vecPickingRay ); +} +#else +extern void ScreenToWorld( int mousex, int mousey, float fov, + const Vector& vecRenderOrigin, + const QAngle& vecRenderAngles, + Vector& vecPickingRay ); +#endif + +SEditModelRender __g_ShaderEditorMReder( "ShEditMRender" ); +SEditModelRender *sEditMRender = &__g_ShaderEditorMReder; + +SEditModelRender::SEditModelRender( char const *name ) : CAutoGameSystemPerFrame( name ) +{ + pModelInstance = NULL; + m_iNumPoseParams = 0; + DestroyModel(); +} +SEditModelRender::~SEditModelRender() +{ + DestroyModel(); +} + +bool SEditModelRender::Init() +{ + return true; +} +void SEditModelRender::Shutdown() +{ +} +void SEditModelRender::Update( float frametime ) +{ + if ( !IsModelReady() ) + return; + + pModelInstance->StudioFrameAdvance(); + if ( pModelInstance->GetCycle() >= 1.0f ) + pModelInstance->SetCycle( pModelInstance->GetCycle() - 1.0f ); +} +void SEditModelRender::LevelInitPostEntity() +{ + ResetModel(); +} +void SEditModelRender::LevelShutdownPostEntity() +{ + ResetModel(); +} +void SEditModelRender::ResetModel() +{ + if ( !IsModelReady() ) + return; + pModelInstance->m_flAnimTime = gpGlobals->curtime; + pModelInstance->m_flOldAnimTime = gpGlobals->curtime; +} +bool SEditModelRender::IsModelReady() +{ + if ( !pModelInstance ) + return false; + + bool bValid = !!pModelInstance->GetModel(); + + if ( bValid && Q_strlen( m_szModelPath ) ) + { + const model_t *pMdl = modelinfo ? modelinfo->FindOrLoadModel( m_szModelPath ) : NULL; + if ( pMdl ) + pModelInstance->SetModelPointer( pMdl ); + bValid = !!pMdl; + } + + if ( !bValid ) + DestroyModel(); + + return bValid; +} +bool SEditModelRender::LoadModel( const char *localPath ) +{ + DestroyModel(); + + const model_t *mdl = modelinfo->FindOrLoadModel( localPath ); + if ( !mdl ) + return false; + + Q_strcpy( m_szModelPath, localPath ); + + C_BaseFlex *pEnt = new C_BaseFlex(); + pEnt->InitializeAsClientEntity( NULL, +#if SWARM_DLL + false +#else + RENDER_GROUP_OPAQUE_ENTITY +#endif + ); + MDLCACHE_CRITICAL_SECTION(); + pEnt->SetModelPointer( mdl ); + pEnt->Spawn(); + + pEnt->SetAbsAngles( vec3_angle ); + pEnt->SetAbsOrigin( vec3_origin ); + + pEnt->AddEffects( EF_NODRAW | EF_NOINTERP ); + pEnt->m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK; + + // leave it alone. + pEnt->RemoveFromLeafSystem(); + cl_entitylist->RemoveEntity( pEnt->GetRefEHandle() ); + pEnt->CollisionProp()->DestroyPartitionHandle(); + + CStudioHdr *pHdr = pEnt->GetModelPtr(); + m_iNumPoseParams = pHdr ? pHdr->GetNumPoseParameters() : 0; + + pModelInstance = pEnt; + return true; +} +void SEditModelRender::DestroyModel() +{ + if ( pModelInstance ) + pModelInstance->Remove(); + + pModelInstance = NULL; + m_szModelPath[0] = '\0'; + m_iNumPoseParams = 0; +} +void SEditModelRender::GetModelCenter( float *pFl3_ViewOffset ) +{ + Q_memset( pFl3_ViewOffset, 0, sizeof(float) * 3 ); + if ( IsModelReady() ) + { + MDLCACHE_CRITICAL_SECTION(); + if ( pModelInstance->GetModelPtr() ) + { + const Vector &vecMin = pModelInstance->GetModelPtr()->hull_min(); + const Vector &vecMax = pModelInstance->GetModelPtr()->hull_max(); + Vector vecPos = ( vecMin + ( vecMax - vecMin ) * 0.5f ); + if ( pFl3_ViewOffset ) + Q_memcpy( pFl3_ViewOffset, vecPos.Base(), sizeof(float) * 3 ); + } + } +} +void SEditModelRender::DestroyCharPtrList( char ***szList ) +{ + Assert( szList ); + if ( *szList ) + { + delete [] (**szList); + delete [] (*szList); + *szList = NULL; + } +} + +int SequenceSort( mstudioseqdesc_t *const *seq1, mstudioseqdesc_t *const *seq2 ) +{ + return Q_stricmp( ( *seq1 )->pszLabel(), ( *seq2 )->pszLabel() ); +} +int SEditModelRender::QuerySequences( char ***list ) +{ + if ( !IsModelReady() ) + return 0; + + MDLCACHE_CRITICAL_SECTION(); + CStudioHdr *pHdr = pModelInstance->GetModelPtr(); + if ( !pHdr ) + return 0; + + CUtlVector< mstudioseqdesc_t* >hSeqs; + for ( int i = 0; i < pHdr->GetNumSeq(); i++ ) + if ( !( pHdr->pSeqdesc( i ).flags & STUDIO_HIDDEN ) ) + hSeqs.AddToTail( &pHdr->pSeqdesc( i ) ); + + int numSequences = hSeqs.Count(); + + if ( !numSequences ) + return 0; + + hSeqs.Sort( SequenceSort ); + + CUtlVector< const char* >hNameList; + for ( int i = 0; i < numSequences; i++ ) + { + const char *seqName = NULL; + const mstudioseqdesc_t &seqPtr = *hSeqs[ i ]; + if ( seqPtr.pszLabel() ) + seqName = seqPtr.pszLabel(); + else + seqName = "Unknown Sequence"; + + hNameList.AddToTail( seqName ); + } + + *list = new char*[numSequences]; + + int iTotalLength = 0; + for ( int i = 0; i < numSequences; i++ ) + iTotalLength += Q_strlen( hNameList[i] ) + 1; + + **list = new char[ iTotalLength ]; + + int curpos = 0; + for ( int i = 0; i < numSequences; i++ ) + { + int curLength = Q_strlen( hNameList[i] ) + 1; + (*list)[ i ] = **list + curpos; + Q_strcpy( (*list)[ i ], hNameList[i] ); + curpos += curLength; + } + + hNameList.Purge(); + hSeqs.Purge(); + return numSequences; +} +void SEditModelRender::SetSequence( const char *name ) +{ + if ( !IsModelReady() ) + return; + + MDLCACHE_CRITICAL_SECTION(); + pModelInstance->ResetSequence( pModelInstance->LookupSequence( name ) ); +} +void SEditModelRender::ExecRender() +{ + if ( !IsModelReady() ) + return; + + MDLCACHE_CRITICAL_SECTION(); + for ( int i = 0; i < m_iNumPoseParams; i++ ) + pModelInstance->SetPoseParameter( i, 0 ); + +#if SWARM_DLL + RenderableInstance_t instance; + instance.m_nAlpha = 255; +#endif + pModelInstance->DrawModel( STUDIO_RENDER +#if SWARM_DLL + , instance +#endif + ); +} +void SEditModelRender::DoPostProc( int x, int y, int w, int h ) +{ +#ifndef SOURCE_2006 + if ( view && view->GetPlayerViewSetup()->m_bDoBloomAndToneMapping ) + DoEnginePostProcessing( x, y, w, h, false, false ); +#endif +} +int SEditModelRender::MaterialPicker( char ***szMat ) +{ + int mx, my; +#ifdef SOURCE_2006 + vgui::input()->GetCursorPos( mx, my ); +#else + vgui::input()->GetCursorPosition( mx, my ); +#endif + + Vector ray; + const CViewSetup *pViewSetup = view->GetPlayerViewSetup(); + float ratio =engine->GetScreenAspectRatio( +#if SWARM_DLL + pViewSetup->width, pViewSetup->height +#endif + ); + + ratio = ( 1.0f / ratio ) * (4.0f/3.0f); + float flFov = ScaleFOVByWidthRatio( pViewSetup->fov, ratio ); + ScreenToWorld( mx, my, flFov, pViewSetup->origin, pViewSetup->angles, ray ); + + Vector start = pViewSetup->origin; + Vector end = start + ray * MAX_TRACE_LENGTH; + trace_t tr; + C_BaseEntity *pIgnore = input->CAM_IsThirdPerson() ? NULL : C_BasePlayer::GetLocalPlayer(); + UTIL_TraceLine( start, end, MASK_SOLID, pIgnore, COLLISION_GROUP_NONE, &tr ); + + if ( !tr.DidHit() ) + return 0; + + int numMaterials = 0; + IMaterial **MatList = NULL; + studiohdr_t *pSHdr = NULL; + + if ( tr.DidHitWorld() ) + { + if ( tr.hitbox == 0 ) + { + Vector dummy; + IMaterial *pMat = engine->TraceLineMaterialAndLighting( start, end, dummy, dummy ); + if ( pMat ) + { + numMaterials = 1; + MatList = new IMaterial*[1]; + MatList[0] = pMat; + } + } + else + { + ICollideable *prop = staticpropmgr->GetStaticPropByIndex( tr.hitbox - 1 ); + if ( prop ) + { + IClientRenderable *pRenderProp = prop->GetIClientUnknown()->GetClientRenderable(); + if ( pRenderProp ) + { + const model_t *pModel = pRenderProp->GetModel(); + if ( pModel ) + pSHdr = modelinfo->GetStudiomodel( pModel ); + } + } + } + } + else if ( tr.m_pEnt ) + { + const model_t *pModel = tr.m_pEnt->GetModel(); + if ( pModel ) + pSHdr = modelinfo->GetStudiomodel( pModel ); + } + + if ( pSHdr ) + { + Assert( !numMaterials && !MatList ); + numMaterials = pSHdr->numtextures; + const int numPaths = pSHdr->numcdtextures; + + if ( numMaterials ) + { + CUtlVector< IMaterial* >hValidMaterials; + for ( int i = 0; i < numMaterials; i++ ) + { + mstudiotexture_t *pStudioTex = pSHdr->pTexture( i ); + const char *matName = pStudioTex->pszName(); + + for ( int p = 0; p < numPaths; p++ ) + { + char tmpPath[MAX_PATH]; + Q_snprintf( tmpPath, MAX_PATH, "%s%s\0", pSHdr->pCdtexture( p ), matName ); + Q_FixSlashes( tmpPath ); + IMaterial *pTempMat = materials->FindMaterial( tmpPath, TEXTURE_GROUP_MODEL ); + if ( !IsErrorMaterial( pTempMat ) ) + { + hValidMaterials.AddToTail( pTempMat ); + break; + } + } + } + + numMaterials = hValidMaterials.Count(); + if ( numMaterials ) + { + MatList = new IMaterial*[ numMaterials ]; + for ( int i = 0; i < numMaterials; i++ ) + MatList[i] = hValidMaterials[i]; + } + + hValidMaterials.Purge(); + } + } + + *szMat = new char*[ numMaterials ]; + + int iTotalLength = 0; + for ( int i = 0; i < numMaterials; i++ ) + iTotalLength += Q_strlen( MatList[i]->GetName() ) + 1; + + **szMat = new char[ iTotalLength ]; + + int curpos = 0; + for ( int i = 0; i < numMaterials; i++ ) + { + const char *pszName = MatList[i]->GetName(); + + int curLength = Q_strlen( pszName ) + 1; + (*szMat)[ i ] = **szMat + curpos; + Q_strcpy( (*szMat)[ i ], pszName ); + curpos += curLength; + } + + if ( MatList ) + delete [] MatList; + + return numMaterials; +} \ No newline at end of file diff --git a/sp/src/game/client/ShaderEditor/SEdit_ModelRender.h b/sp/src/game/client/ShaderEditor/SEdit_ModelRender.h new file mode 100644 index 00000000..34659896 --- /dev/null +++ b/sp/src/game/client/ShaderEditor/SEdit_ModelRender.h @@ -0,0 +1,48 @@ +#ifndef SHEDITMRENDER_H +#define SHEDITMRENDER_H + +#include "cbase.h" +#include "ShaderEditor/ISEdit_ModelRender.h" + +class C_BaseFlex_OverrideLod; + +class SEditModelRender : public ISEditModelRender, public CAutoGameSystemPerFrame +{ +public: + SEditModelRender( char const *name ); + ~SEditModelRender(); + +// autogamesystem + virtual bool Init(); + virtual void Shutdown(); + virtual void Update( float frametime ); + + virtual void LevelInitPostEntity(); + virtual void LevelShutdownPostEntity(); + +// interface + virtual bool LoadModel( const char *localPath ); + virtual void DestroyModel(); + virtual void GetModelCenter( float *pFl3_ViewOffset ); + + virtual int QuerySequences( char ***list ); + virtual void SetSequence( const char *name ); + + virtual void ExecRender(); + virtual void DoPostProc( int x, int y, int w, int h ); + + virtual int MaterialPicker( char ***szMat ); + + virtual void DestroyCharPtrList( char ***szList ); + +private: + + bool IsModelReady(); + void ResetModel(); + + C_BaseFlex *pModelInstance; + char m_szModelPath[MAX_PATH]; + int m_iNumPoseParams; +}; + +#endif \ No newline at end of file diff --git a/sp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp b/sp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp new file mode 100644 index 00000000..218517b5 --- /dev/null +++ b/sp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp @@ -0,0 +1,1625 @@ +// ****************************************************** +// +// Purpose: +// - Connects the shader editor +// - Sends data from the main viewsetup +// - exposes client callbacks to shaders +// +// ****************************************************** + +#include "cbase.h" +#include "client_factorylist.h" +#include "ShaderEditor/IVShaderEditor.h" +#include "ShaderEditor/SEdit_ModelRender.h" +#include "ivrenderview.h" +#include "iviewrender.h" +#include "viewrender.h" +#include "view.h" +#include "view_scene.h" +#include "view_shared.h" +#include "beamdraw.h" +#include "c_sun.h" +#include "tier0/icommandline.h" +#include "rendertexture.h" +#include "c_rope.h" +#include "model_types.h" +#ifdef SWARM_DLL +#include "modelrendersystem.h" +#endif + + +#if SWARM_DLL +#define Editor_MainViewOrigin MainViewOrigin( 0 ) +#define Editor_MainViewForward MainViewForward( 0 ) +#else +#define Editor_MainViewOrigin MainViewOrigin() +#define Editor_MainViewForward MainViewForward() +#endif + + +ShaderEditorHandler __g_ShaderEditorSystem( "ShEditUpdate" ); +ShaderEditorHandler *g_ShaderEditorSystem = &__g_ShaderEditorSystem; + +CSysModule *shaderEditorModule = NULL; +IVShaderEditor *shaderEdit = NULL; + +ShaderEditorHandler::ShaderEditorHandler( char const *name ) : CAutoGameSystemPerFrame( name ) +{ + m_bReady = false; + m_piCurrentViewId = NULL; +} + +ShaderEditorHandler::~ShaderEditorHandler() +{ +} + +const bool ShaderEditorHandler::IsReady() +{ + return m_bReady; +} + +bool ShaderEditorHandler::Init() +{ + factorylist_t factories; + FactoryList_Retrieve( factories ); + +#ifdef SOURCE_2006 + ConVar *pCVarDev = cvar->FindVar( "developer" ); + bool bShowPrimDebug = pCVarDev != NULL && pCVarDev->GetInt() != 0; +#else + ConVarRef devEnabled( "developer", true ); + bool bShowPrimDebug = devEnabled.GetInt() != 0; +#endif + + bool bCreateEditor = ( CommandLine() != NULL ) && ( CommandLine()->FindParm( "-shaderedit" ) != 0 ); + SEDIT_SKYMASK_MODE iEnableSkymask = SKYMASK_OFF; + +#ifdef SHADEREDITOR_FORCE_ENABLED + bCreateEditor = true; + iEnableSkymask = SKYMASK_QUARTER; +#endif + + char modulePath[MAX_PATH*4]; + + Q_snprintf( modulePath, sizeof( modulePath ), "%s/bin/shadereditor_2013.dll\0", engine->GetGameDirectory() ); + + + + shaderEditorModule = Sys_LoadModule( modulePath ); + if ( shaderEditorModule ) + { + CreateInterfaceFn shaderEditorDLLFactory = Sys_GetFactory( shaderEditorModule ); + shaderEdit = shaderEditorDLLFactory ? ((IVShaderEditor *) shaderEditorDLLFactory( SHADEREDIT_INTERFACE_VERSION, NULL )) : NULL; + + if ( !shaderEdit ) + { + Warning( "Unable to pull IVShaderEditor interface.\n" ); + } + else if ( !shaderEdit->Init( factories.appSystemFactory, gpGlobals, sEditMRender, + bCreateEditor, bShowPrimDebug, iEnableSkymask ) ) + { + Warning( "Cannot initialize IVShaderEditor.\n" ); + shaderEdit = NULL; + } + } + else + { + Warning( "Cannot load shadereditor.dll from %s!\n", modulePath ); + } + + m_bReady = shaderEdit != NULL; + + RegisterCallbacks(); + RegisterViewRenderCallbacks(); + + if ( IsReady() ) + { + shaderEdit->PrecacheData(); + } + + return true; +} + +#ifdef SHADEREDITOR_FORCE_ENABLED +CON_COMMAND( sedit_debug_toggle_ppe, "" ) +{ + if ( !g_ShaderEditorSystem->IsReady() ) + return Warning( "lib not ready.\n" ); + + if ( args.ArgC() < 2 ) + return; + + const int idx = shaderEdit->GetPPEIndex( args[1] ); + if ( idx < 0 ) + return Warning( "can't find ppe named: %s\n", args[1] ); + + shaderEdit->SetPPEEnabled( idx, !shaderEdit->IsPPEEnabled( idx ) ); +} +#endif + +void ShaderEditorHandler::Shutdown() +{ + if ( shaderEdit ) + shaderEdit->Shutdown(); + if ( shaderEditorModule ) + Sys_UnloadModule( shaderEditorModule ); +} + +void ShaderEditorHandler::Update( float frametime ) +{ + if ( IsReady() ) + shaderEdit->OnFrame( frametime ); +} + +CThreadMutex m_Lock; + +void ShaderEditorHandler::PreRender() +{ + if ( IsReady() && view ) + { + // make sure the class matches + const CViewSetup *v = view->GetPlayerViewSetup(); + CViewSetup_SEdit_Shared stableVSetup( *v ); + shaderEdit->OnPreRender( &stableVSetup ); + + m_Lock.Lock(); + PrepareCallbackData(); + m_Lock.Unlock(); + } +} +void ShaderEditorHandler::PostRender() +{ +} +#ifdef SOURCE_2006 +void ShaderEditorHandler::CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo ) +#else +void ShaderEditorHandler::CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo, const WaterRenderInfo_t &waterRenderInfo ) +#endif +{ + m_piCurrentViewId = viewId; + m_tFogVolumeInfo = fogVolumeInfo; + +#ifndef SOURCE_2006 + m_tWaterRenderInfo = waterRenderInfo; +#endif + + if ( IsReady() ) + shaderEdit->OnSceneRender(); +} +void ShaderEditorHandler::UpdateSkymask(bool bCombineMode, int x, int y, int w, int h) +{ + if ( IsReady() ) + shaderEdit->OnUpdateSkymask(bCombineMode, x, y, w, h); +} +void ShaderEditorHandler::CustomPostRender() +{ + if ( IsReady() ) + shaderEdit->OnPostRender( true ); +} + +struct CallbackData_t +{ + void Reset() + { + sun_data.Init(); + sun_dir.Init(); + + player_speed.Init(); + player_pos.Init(); + }; + Vector4D sun_data; + Vector sun_dir; + + Vector4D player_speed; + Vector player_pos; +}; + +static CallbackData_t clCallback_data; + +void ShaderEditorHandler::PrepareCallbackData() +{ + clCallback_data.Reset(); + + float flSunAmt_Goal = 0; + static float s_flSunAmt_Last = 0; + + C_BaseEntity *pEnt = ClientEntityList().FirstBaseEntity(); + while ( pEnt ) + { + if ( !Q_stricmp( pEnt->GetClassname(), "class C_Sun" ) ) + { + C_Sun *pSun = ( C_Sun* )pEnt; + Vector dir = pSun->m_vDirection; + dir.NormalizeInPlace(); + + Vector screen; + + if ( ScreenTransform( Editor_MainViewOrigin + dir * 512, screen ) ) + ScreenTransform( (Editor_MainViewOrigin - dir * 512), screen ); + + screen = screen * Vector( 0.5f, -0.5f, 0 ) + Vector( 0.5f, 0.5f, 0 ); + + Q_memcpy( clCallback_data.sun_data.Base(), screen.Base(), sizeof(float) * 2 ); + clCallback_data.sun_data[ 2 ] = DotProduct( dir, Editor_MainViewForward ); + clCallback_data.sun_dir = dir; + + trace_t tr; + UTIL_TraceLine( Editor_MainViewOrigin, Editor_MainViewOrigin + dir * MAX_TRACE_LENGTH, MASK_SOLID, NULL, COLLISION_GROUP_DEBRIS, &tr ); + if ( !tr.DidHitWorld() ) + break; + + if ( tr.surface.flags & SURF_SKY ) + flSunAmt_Goal = 1; + + break; + } + pEnt = ClientEntityList().NextBaseEntity( pEnt ); + } + + if ( s_flSunAmt_Last != flSunAmt_Goal ) + s_flSunAmt_Last = Approach( flSunAmt_Goal, s_flSunAmt_Last, gpGlobals->frametime * ( (!!flSunAmt_Goal) ? 4.0f : 0.75f ) ); + + clCallback_data.sun_data[ 3 ] = s_flSunAmt_Last; + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pPlayer ) + { + Vector velo = pPlayer->GetLocalVelocity(); + clCallback_data.player_speed[ 3 ] = velo.NormalizeInPlace(); + Q_memcpy( clCallback_data.player_speed.Base(), velo.Base(), sizeof(float) * 3 ); + + clCallback_data.player_pos = pPlayer->GetLocalOrigin(); + } +} + +pFnClCallback_Declare( ClCallback_SunData ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.sun_data.Base(), sizeof(float) * 4 ); + m_Lock.Unlock(); +} + +pFnClCallback_Declare( ClCallback_SunDirection ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.sun_dir.Base(), sizeof(float) * 3 ); + m_Lock.Unlock(); +} + +pFnClCallback_Declare( ClCallback_PlayerVelocity ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.player_speed.Base(), sizeof(float) * 4 ); + m_Lock.Unlock(); +} + +pFnClCallback_Declare( ClCallback_PlayerPos ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.player_pos.Base(), sizeof(float) * 3 ); + m_Lock.Unlock(); +} + +void ShaderEditorHandler::RegisterCallbacks() +{ + if ( !IsReady() ) + return; + + // 4 components max + shaderEdit->RegisterClientCallback( "sun data", ClCallback_SunData, 4 ); + shaderEdit->RegisterClientCallback( "sun dir", ClCallback_SunDirection, 3 ); + shaderEdit->RegisterClientCallback( "local player velocity", ClCallback_PlayerVelocity, 4 ); + shaderEdit->RegisterClientCallback( "local player position", ClCallback_PlayerPos, 3 ); + + shaderEdit->LockClientCallbacks(); +} + +#ifdef SOURCE_2006 + +void ShaderEditorHandler::RegisterViewRenderCallbacks(){} + +#else + +extern bool DoesViewPlaneIntersectWater( float waterZ, int leafWaterDataID ); + +// copy pasta from baseworldview +class CBaseVCallbackView : public CRendering3dView +{ + DECLARE_CLASS( CBaseVCallbackView, CRendering3dView ); +protected: + + CBaseVCallbackView( CViewRender *pMainView ) : CRendering3dView( pMainView ) + { + }; + + virtual bool AdjustView( float waterHeight ){ return false; }; + + virtual void CallbackInitRenderList( int viewId ) + { + BuildRenderableRenderLists( viewId ); + }; + + virtual bool ShouldDrawParticles() + { + return true; + }; + + virtual bool ShouldDrawRopes() + { + return true; + }; + + virtual bool ShouldDrawWorld() + { + return true; + }; + + virtual bool ShouldDrawTranslucents() + { + return true; + }; + + virtual bool ShouldDrawTranslucentWorld() + { + return true; + }; + + void DrawSetup( float waterHeight, int nSetupFlags, float waterZAdjust, int iForceViewLeaf = -1 ) + { + int savedViewID = g_ShaderEditorSystem->GetViewIdForModify(); + + g_ShaderEditorSystem->GetViewIdForModify() = VIEW_ILLEGAL; + + render->BeginUpdateLightmaps(); + + bool bDrawEntities = ( nSetupFlags & DF_DRAW_ENTITITES ) != 0; + BuildWorldRenderLists( bDrawEntities, iForceViewLeaf, true, false, NULL ); + PruneWorldListInfo(); + + if ( bDrawEntities ) + CallbackInitRenderList( savedViewID ); + + render->EndUpdateLightmaps(); + + g_ShaderEditorSystem->GetViewIdForModify() = savedViewID; + }; + + void DrawExecute( float waterHeight, view_id_t viewID, float waterZAdjust ) + { + // ClientWorldListInfo_t is defined in viewrender.cpp... + //g_pClientShadowMgr->ComputeShadowTextures( *this, m_pWorldListInfo->m_LeafCount, m_pWorldListInfo->m_pLeafList ); + + engine->Sound_ExtraUpdate(); + + int savedViewID = g_ShaderEditorSystem->GetViewIdForModify(); + g_ShaderEditorSystem->GetViewIdForModify() = viewID; + + int iDrawFlagsBackup = m_DrawFlags; + m_DrawFlags |= m_pMainView->GetBaseDrawFlags(); + + PushView( waterHeight ); + + CMatRenderContextPtr pRenderContext( materials ); + + ITexture *pSaveFrameBufferCopyTexture = pRenderContext->GetFrameBufferCopyTexture( 0 ); + if ( engine->GetDXSupportLevel() >= 80 ) + { + pRenderContext->SetFrameBufferCopyTexture( GetPowerOfTwoFrameBufferTexture() ); + } + + pRenderContext.SafeRelease(); + + static ConVarRef translucentNoWorld( "r_drawtranslucentworld" ); + const int tnoWorldSaved = translucentNoWorld.GetInt(); + translucentNoWorld.SetValue( ShouldDrawWorld() ? 0 : 1 ); + + if ( m_DrawFlags & DF_DRAW_ENTITITES ) + { + if ( ShouldDrawWorld() ) + DrawWorld( waterZAdjust ); + + DrawOpaqueRenderables_Custom( false ); + + if ( ShouldDrawTranslucents() && ShouldDrawTranslucentWorld() ) + DrawTranslucentRenderables( false, false ); + else if ( ShouldDrawTranslucents() ) + DrawTranslucentRenderablesNoWorld( false ); + else if ( ShouldDrawTranslucentWorld() ) + DrawTranslucentWorldInLeaves( false ); + } + else if ( ShouldDrawWorld() ) + { + DrawWorld( waterZAdjust ); + + if ( ShouldDrawTranslucentWorld() ) + DrawTranslucentWorldInLeaves( false ); + } + + translucentNoWorld.SetValue( tnoWorldSaved ); + + if ( CurrentViewID() != VIEW_MAIN && CurrentViewID() != VIEW_INTRO_CAMERA ) + PixelVisibility_EndCurrentView(); + + pRenderContext.GetFrom( materials ); + pRenderContext->SetFrameBufferCopyTexture( pSaveFrameBufferCopyTexture ); + PopView(); + + m_DrawFlags = iDrawFlagsBackup; + + g_ShaderEditorSystem->GetViewIdForModify() = savedViewID; + }; + + virtual void PushView( float waterHeight ) + { + float spread = 2.0f; + if( m_DrawFlags & DF_FUDGE_UP ) + { + waterHeight += spread; + } + else + { + waterHeight -= spread; + } + + MaterialHeightClipMode_t clipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE; + + if ( ( m_DrawFlags & DF_CLIP_Z ) ) + { + if( m_DrawFlags & DF_CLIP_BELOW ) + { + clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT; + } + else + { + clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT; + } + } + + CMatRenderContextPtr pRenderContext( materials ); + + if ( m_ClearFlags & ( VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_STENCIL ) ) + { + if ( m_ClearFlags & VIEW_CLEAR_OBEY_STENCIL ) + { + pRenderContext->ClearBuffersObeyStencil( (m_ClearFlags & VIEW_CLEAR_COLOR) != 0, (m_ClearFlags & VIEW_CLEAR_DEPTH) != 0 ); + } + else + { + pRenderContext->ClearBuffers( (m_ClearFlags & VIEW_CLEAR_COLOR) != 0, (m_ClearFlags & VIEW_CLEAR_DEPTH) != 0, (m_ClearFlags & VIEW_CLEAR_STENCIL) != 0 ); + } + } + + pRenderContext->SetHeightClipMode( clipMode ); + if ( clipMode != MATERIAL_HEIGHTCLIPMODE_DISABLE ) + { + pRenderContext->SetHeightClipZ( waterHeight ); + } + }; + + virtual void PopView() + { + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); + }; + + void DrawOpaqueRenderables_Custom( bool bShadowDepth ) + { + //if( !r_drawopaquerenderables.GetBool() ) + // return; + + if( !m_pMainView->ShouldDrawEntities() ) + return; + + render->SetBlend( 1 ); + + const bool bRopes = ShouldDrawRopes(); + const bool bParticles = ShouldDrawParticles(); + + // + // Prepare to iterate over all leaves that were visible, and draw opaque things in them. + // + if ( bRopes ) + RopeManager()->ResetRenderCache(); + if ( bParticles ) + g_pParticleSystemMgr->ResetRenderCache(); + +#ifdef SWARM_DLL + + extern ConVar cl_modelfastpath; + extern ConVar r_drawothermodels; + + // Categorize models by type + int nOpaqueRenderableCount = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE]; + CUtlVector< CClientRenderablesList::CEntry* > brushModels( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount ); + CUtlVector< CClientRenderablesList::CEntry* > staticProps( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount ); + CUtlVector< CClientRenderablesList::CEntry* > otherRenderables( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount ); + CClientRenderablesList::CEntry *pOpaqueList = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE]; + for ( int i = 0; i < nOpaqueRenderableCount; ++i ) + { + switch( pOpaqueList[i].m_nModelType ) + { + case RENDERABLE_MODEL_BRUSH: brushModels.AddToTail( &pOpaqueList[i] ); break; + case RENDERABLE_MODEL_STATIC_PROP: staticProps.AddToTail( &pOpaqueList[i] ); break; + default: otherRenderables.AddToTail( &pOpaqueList[i] ); break; + } + } + + // + // First do the brush models + // + DrawOpaqueRenderables_DrawBrushModels( brushModels.Count(), brushModels.Base(), bShadowDepth ); + + // Move all static props to modelrendersystem + bool bUseFastPath = ( cl_modelfastpath.GetInt() != 0 ); + + // + // Sort everything that's not a static prop + // + int nStaticPropCount = staticProps.Count(); + int numOpaqueEnts = otherRenderables.Count(); + CUtlVector< CClientRenderablesList::CEntry* > arrRenderEntsNpcsFirst( (CClientRenderablesList::CEntry **)stackalloc( numOpaqueEnts * sizeof( CClientRenderablesList::CEntry ) ), numOpaqueEnts ); + CUtlVector< ModelRenderSystemData_t > arrModelRenderables( (ModelRenderSystemData_t *)stackalloc( ( numOpaqueEnts + nStaticPropCount ) * sizeof( ModelRenderSystemData_t ) ), numOpaqueEnts + nStaticPropCount ); + + // Queue up RENDER_GROUP_OPAQUE_ENTITY entities to be rendered later. + CClientRenderablesList::CEntry *itEntity; + if( r_drawothermodels.GetBool() ) + { + for ( int i = 0; i < numOpaqueEnts; ++i ) + { + itEntity = otherRenderables[i]; + if ( !itEntity->m_pRenderable ) + continue; + + IClientUnknown *pUnknown = itEntity->m_pRenderable->GetIClientUnknown(); + IClientModelRenderable *pModelRenderable = pUnknown->GetClientModelRenderable(); + C_BaseEntity *pEntity = pUnknown->GetBaseEntity(); + + // FIXME: Strangely, some static props are in the non-static prop bucket + // which is what the last case in this if statement is for + if ( bUseFastPath && pModelRenderable ) + { + ModelRenderSystemData_t data; + data.m_pRenderable = itEntity->m_pRenderable; + data.m_pModelRenderable = pModelRenderable; + data.m_InstanceData = itEntity->m_InstanceData; + arrModelRenderables.AddToTail( data ); + otherRenderables.FastRemove( i ); + --i; --numOpaqueEnts; + continue; + } + + if ( !pEntity ) + continue; + + if ( pEntity->IsNPC() ) + { + arrRenderEntsNpcsFirst.AddToTail( itEntity ); + otherRenderables.FastRemove( i ); + --i; --numOpaqueEnts; + continue; + } + } + } + + // Queue up the static props to be rendered later. + for ( int i = 0; i < nStaticPropCount; ++i ) + { + itEntity = staticProps[i]; + if ( !itEntity->m_pRenderable ) + continue; + + IClientUnknown *pUnknown = itEntity->m_pRenderable->GetIClientUnknown(); + IClientModelRenderable *pModelRenderable = pUnknown->GetClientModelRenderable(); + if ( !bUseFastPath || !pModelRenderable ) + continue; + + ModelRenderSystemData_t data; + data.m_pRenderable = itEntity->m_pRenderable; + data.m_pModelRenderable = pModelRenderable; + data.m_InstanceData = itEntity->m_InstanceData; + arrModelRenderables.AddToTail( data ); + + staticProps.FastRemove( i ); + --i; --nStaticPropCount; + } + + // + // Draw model renderables now (ie. models that use the fast path) + // + DrawOpaqueRenderables_ModelRenderables( arrModelRenderables.Count(), arrModelRenderables.Base(), bShadowDepth ); + + // Turn off z pass here. Don't want non-fastpath models with potentially large dynamic VB requirements overwrite + // stuff in the dynamic VB ringbuffer. We're calling End360ZPass again in DrawExecute, but that's not a problem. + // Begin360ZPass/End360ZPass don't have to be matched exactly. + End360ZPass(); + + // + // Draw static props + opaque entities that aren't using the fast path. + // + DrawOpaqueRenderables_Range( otherRenderables.Count(), otherRenderables.Base(), bShadowDepth ); + DrawOpaqueRenderables_DrawStaticProps( staticProps.Count(), staticProps.Base(), bShadowDepth ); + + // + // Draw NPCs now + // + DrawOpaqueRenderables_NPCs( arrRenderEntsNpcsFirst.Count(), arrRenderEntsNpcsFirst.Base(), bShadowDepth ); +#else + + bool const bDrawopaquestaticpropslast = false; //r_drawopaquestaticpropslast.GetBool(); + + + // + // First do the brush models + // + { + CClientRenderablesList::CEntry *pEntitiesBegin, *pEntitiesEnd; + pEntitiesBegin = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE_BRUSH]; + pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE_BRUSH]; + DrawOpaqueRenderables_DrawBrushModels( pEntitiesBegin, pEntitiesEnd, bShadowDepth ); + } + + + // + // Sort everything that's not a static prop + // + int numOpaqueEnts = 0; + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + numOpaqueEnts += m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ]; + + CUtlVector< C_BaseAnimating * > arrBoneSetupNpcsLast( (C_BaseAnimating **)_alloca( numOpaqueEnts * sizeof( C_BaseAnimating * ) ), numOpaqueEnts, numOpaqueEnts ); + CUtlVector< CClientRenderablesList::CEntry > arrRenderEntsNpcsFirst( (CClientRenderablesList::CEntry *)_alloca( numOpaqueEnts * sizeof( CClientRenderablesList::CEntry ) ), numOpaqueEnts, numOpaqueEnts ); + int numNpcs = 0, numNonNpcsAnimating = 0; + + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + { + for( CClientRenderablesList::CEntry + * const pEntitiesBegin = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ], + * const pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ], + *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + { + C_BaseEntity *pEntity = itEntity->m_pRenderable ? itEntity->m_pRenderable->GetIClientUnknown()->GetBaseEntity() : NULL; + if ( pEntity ) + { + if ( pEntity->IsNPC() ) + { + C_BaseAnimating *pba = assert_cast( pEntity ); + arrRenderEntsNpcsFirst[ numNpcs ++ ] = *itEntity; + arrBoneSetupNpcsLast[ numOpaqueEnts - numNpcs ] = pba; + + itEntity->m_pRenderable = NULL; // We will render NPCs separately + itEntity->m_RenderHandle = NULL; + + continue; + } + else if ( pEntity->GetBaseAnimating() ) + { + C_BaseAnimating *pba = assert_cast( pEntity ); + arrBoneSetupNpcsLast[ numNonNpcsAnimating ++ ] = pba; + // fall through + } + } + } + } + + // + // Draw static props + opaque entities from the biggest bucket to the smallest + // + { + CClientRenderablesList::CEntry * pEnts[ RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS ][2]; + CClientRenderablesList::CEntry * pProps[ RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS ][2]; + + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + { + pEnts[bucket][0] = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ]; + pEnts[bucket][1] = pEnts[bucket][0] + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ]; + + pProps[bucket][0] = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ]; + pProps[bucket][1] = pProps[bucket][0] + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ]; + } + + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + { + if ( bDrawopaquestaticpropslast ) + { + DrawOpaqueRenderables_Range( pEnts[bucket][0], pEnts[bucket][1], bShadowDepth ); + DrawOpaqueRenderables_DrawStaticProps( pProps[bucket][0], pProps[bucket][1], bShadowDepth ); + } + else + { + DrawOpaqueRenderables_Range( pEnts[bucket][0], pEnts[bucket][1], bShadowDepth ); + DrawOpaqueRenderables_DrawStaticProps( pProps[bucket][0], pProps[bucket][1], bShadowDepth ); + } + } + } + + // + // Draw NPCs now + // + DrawOpaqueRenderables_Range( arrRenderEntsNpcsFirst.Base(), arrRenderEntsNpcsFirst.Base() + numNpcs, bShadowDepth ); +#endif + // + // Ropes and particles + // + if ( bRopes ) + RopeManager()->DrawRenderCache( bShadowDepth ); + if ( bParticles ) + g_pParticleSystemMgr->DrawRenderCache( bShadowDepth ); + }; + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_ModelRenderables( int nCount, ModelRenderSystemData_t* pModelRenderables, bool bShadowDepth ) + { + g_pModelRenderSystem->DrawModels( pModelRenderables, nCount, bShadowDepth ? MODEL_RENDER_MODE_SHADOW_DEPTH : MODEL_RENDER_MODE_NORMAL ); + } + void DrawOpaqueRenderables_NPCs( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + DrawOpaqueRenderables_Range( nCount, ppEntities, bShadowDepth ); + } + void DrawRenderable( IClientRenderable *pEnt, int flags, const RenderableInstance_t &instance ) + { + extern ConVar r_entityclips; + float *pRenderClipPlane = NULL; + if( r_entityclips.GetBool() ) + pRenderClipPlane = pEnt->GetRenderClipPlane(); + + if( pRenderClipPlane ) + { + CMatRenderContextPtr pRenderContext( materials ); + if( !materials->UsingFastClipping() ) //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though + pRenderContext->PushCustomClipPlane( pRenderClipPlane ); +#if DEBUG + else + AssertMsg( 0, "can't link DrawClippedDepthBox externally so you either have to cope with even more redundancy or move all this crap to viewrender" ); +#endif + // DrawClippedDepthBox( pEnt, pRenderClipPlane ); + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + bool bBlockNormalDraw = false; //BlurTest( pEnt, flags, true, instance ); + if( !bBlockNormalDraw ) + pEnt->DrawModel( flags, instance ); + //BlurTest( pEnt, flags, false, instance ); + view->SetCurrentlyDrawingEntity( NULL ); + + if( !materials->UsingFastClipping() ) + pRenderContext->PopCustomClipPlane(); + } + else + { + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + bool bBlockNormalDraw = false; //BlurTest( pEnt, flags, true, instance ); + if( !bBlockNormalDraw ) + pEnt->DrawModel( flags, instance ); + //BlurTest( pEnt, flags, false, instance ); + view->SetCurrentlyDrawingEntity( NULL ); + } + }; + void DrawOpaqueRenderable( IClientRenderable *pEnt, bool bTwoPass, bool bShadowDepth ) + { + ASSERT_LOCAL_PLAYER_RESOLVABLE(); + float color[3]; + + Assert( !IsSplitScreenSupported() || pEnt->ShouldDrawForSplitScreenUser( GET_ACTIVE_SPLITSCREEN_SLOT() ) ); + Assert( (pEnt->GetIClientUnknown() == NULL) || (pEnt->GetIClientUnknown()->GetIClientEntity() == NULL) || (pEnt->GetIClientUnknown()->GetIClientEntity()->IsBlurred() == false) ); + pEnt->GetColorModulation( color ); + render->SetColorModulation( color ); + + int flags = STUDIO_RENDER; + if ( bTwoPass ) + { + flags |= STUDIO_TWOPASS; + } + + if ( bShadowDepth ) + { + flags |= STUDIO_SHADOWDEPTHTEXTURE; + } + + RenderableInstance_t instance; + instance.m_nAlpha = 255; + DrawRenderable( pEnt, flags, instance ); + }; +#else + void DrawOpaqueRenderable( IClientRenderable *pEnt, bool bTwoPass, bool bShadowDepth ) + { + float color[3]; + + pEnt->GetColorModulation( color ); + render->SetColorModulation( color ); + + int flags = STUDIO_RENDER; + if ( bTwoPass ) + { + flags |= STUDIO_TWOPASS; + } + + if ( bShadowDepth ) + { + flags |= STUDIO_SHADOWDEPTHTEXTURE; + } + + float *pRenderClipPlane = NULL; + if( true ) //r_entityclips.GetBool() ) + pRenderClipPlane = pEnt->GetRenderClipPlane(); + + if( pRenderClipPlane ) + { + CMatRenderContextPtr pRenderContext( materials ); + if( !materials->UsingFastClipping() ) //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though + pRenderContext->PushCustomClipPlane( pRenderClipPlane ); +#if DEBUG + else + AssertMsg( 0, "can't link DrawClippedDepthBox externally so you either have to cope with even more redundancy or move all this crap to viewrender" ); +#endif + // DrawClippedDepthBox( pEnt, pRenderClipPlane ); + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + pEnt->DrawModel( flags ); + view->SetCurrentlyDrawingEntity( NULL ); + if( pRenderClipPlane && !materials->UsingFastClipping() ) + pRenderContext->PopCustomClipPlane(); + } + else + { + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + pEnt->DrawModel( flags ); + view->SetCurrentlyDrawingEntity( NULL ); + } + }; +#endif + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_DrawBrushModels( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + for( int i = 0; i < nCount; ++i ) + DrawOpaqueRenderable( ppEntities[i]->m_pRenderable, false, bShadowDepth ); + }; +#else + void DrawOpaqueRenderables_DrawBrushModels( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, bool bShadowDepth ) + { + for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + DrawOpaqueRenderable( itEntity->m_pRenderable, false, bShadowDepth ); + }; +#endif + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_DrawStaticProps( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + if ( nCount == 0 ) + return; + + float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( one ); + render->SetBlend( 1.0f ); + + const int MAX_STATICS_PER_BATCH = 512; + IClientRenderable *pStatics[ MAX_STATICS_PER_BATCH ]; + RenderableInstance_t pInstances[ MAX_STATICS_PER_BATCH ]; + + int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH; + + for( int i = 0; i < nCount; ++i ) + { + CClientRenderablesList::CEntry *itEntity = ppEntities[i]; + if ( itEntity->m_pRenderable ) + NULL; + else + continue; + + pInstances[ numScheduled ] = itEntity->m_InstanceData; + pStatics[ numScheduled ++ ] = itEntity->m_pRenderable; + if ( -- numAvailable > 0 ) + continue; // place a hint for compiler to predict more common case in the loop + + staticpropmgr->DrawStaticProps( pStatics, pInstances, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + numScheduled = 0; + numAvailable = MAX_STATICS_PER_BATCH; + } + + if ( numScheduled ) + staticpropmgr->DrawStaticProps( pStatics, pInstances, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + } +#else + void DrawOpaqueRenderables_DrawStaticProps( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, bool bShadowDepth ) + { + if ( pEntitiesEnd == pEntitiesBegin ) + return; + + float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( one ); + render->SetBlend( 1.0f ); + + const int MAX_STATICS_PER_BATCH = 512; + IClientRenderable *pStatics[ MAX_STATICS_PER_BATCH ]; + + int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH; + + for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + { + if ( itEntity->m_pRenderable ) + NULL; + else + continue; + + pStatics[ numScheduled ++ ] = itEntity->m_pRenderable; + if ( -- numAvailable > 0 ) + continue; // place a hint for compiler to predict more common case in the loop + + staticpropmgr->DrawStaticProps( pStatics, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + numScheduled = 0; + numAvailable = MAX_STATICS_PER_BATCH; + } + + if ( numScheduled ) + staticpropmgr->DrawStaticProps( pStatics, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + }; +#endif + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_Range( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + for ( int i = 0; i < nCount; ++i ) + { + CClientRenderablesList::CEntry *itEntity = ppEntities[i]; + if ( itEntity->m_pRenderable ) + DrawOpaqueRenderable( itEntity->m_pRenderable, ( itEntity->m_TwoPass != 0 ), bShadowDepth ); + } + }; +#else + void DrawOpaqueRenderables_Range( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, bool bShadowDepth ) + { + for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + if ( itEntity->m_pRenderable ) + DrawOpaqueRenderable( itEntity->m_pRenderable, ( itEntity->m_TwoPass != 0 ), bShadowDepth ); + }; +#endif +}; + + +class CSimpleVCallbackView : public CBaseVCallbackView +{ + DECLARE_CLASS( CSimpleVCallbackView, CBaseVCallbackView ); +public: + CSimpleVCallbackView(CViewRender *pMainView) : CBaseVCallbackView( pMainView ) {} + + struct EditorViewSettings + { + public: + bool bDrawPlayers; + bool bDrawWeapons; + bool bDrawStaticProps; + bool bDrawMisc; + bool bDrawTranslucents; + bool bDrawWater; + bool bDrawWorld; + bool bDrawParticles; + bool bDrawRopes; + bool bDrawSkybox; + bool bClipSkybox; + bool bClearColor; + bool bClearDepth; + bool bClearStencil; + bool bClearObeyStencil; + bool bFogOverride; + bool bFogEnabled; + + int iClearColorR; + int iClearColorG; + int iClearColorB; + int iClearColorA; + int iFogColorR; + int iFogColorG; + int iFogColorB; + + float flFogStart; + float flFogEnd; + float flFogDensity; + }; + + EditorViewSettings settings; + + void Setup( const CViewSetup &view, CSimpleVCallbackView::EditorViewSettings settings, + const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& info ) + { + this->settings = settings; + + BaseClass::Setup( view ); + + m_ClearFlags = (settings.bClearColor ? VIEW_CLEAR_COLOR : 0) | + (settings.bClearDepth ? VIEW_CLEAR_DEPTH : 0) | + (settings.bClearStencil ? VIEW_CLEAR_STENCIL : 0) | + (settings.bClearObeyStencil ? VIEW_CLEAR_OBEY_STENCIL : 0); + + m_DrawFlags = (settings.bDrawPlayers || settings.bDrawStaticProps || + settings.bDrawTranslucents || settings.bDrawWeapons || + settings.bDrawMisc) ? DF_DRAW_ENTITITES : 0; + + //if ( settings.bDrawWorld ) + { + if ( !info.m_bOpaqueWater ) + { + m_DrawFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER; + } + else + { + bool bViewIntersectsWater = DoesViewPlaneIntersectWater( fogInfo.m_flWaterHeight, fogInfo.m_nVisibleFogVolume ); + if( bViewIntersectsWater ) + { + // have to draw both sides if we can see both. + m_DrawFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER; + } + else if ( fogInfo.m_bEyeInFogVolume ) + { + m_DrawFlags |= DF_RENDER_UNDERWATER; + } + else + { + m_DrawFlags |= DF_RENDER_ABOVEWATER; + } + } + } + + if ( info.m_bDrawWaterSurface && settings.bDrawWater ) + { + m_DrawFlags |= DF_RENDER_WATER; + } + + if ( !fogInfo.m_bEyeInFogVolume && settings.bDrawSkybox ) + { + m_DrawFlags |= DF_DRAWSKYBOX; + } + + if ( settings.bClipSkybox ) + m_DrawFlags |= DF_CLIP_SKYBOX; + + m_pCustomVisibility = NULL; + m_fogInfo = fogInfo; + }; + + void Draw() + { + DrawSetup( 0, m_DrawFlags, 0 ); + + CMatRenderContextPtr pRenderContext( materials ); + + pRenderContext->ClearColor4ub( (unsigned char)settings.iClearColorR, + (unsigned char)settings.iClearColorG, + (unsigned char)settings.iClearColorB, + (unsigned char)settings.iClearColorA ); + + if ( settings.bFogOverride ) + { + if ( !settings.bFogEnabled ) + pRenderContext->FogMode( MATERIAL_FOG_NONE ); + else + { + pRenderContext->FogMode( MATERIAL_FOG_LINEAR ); + pRenderContext->FogColor3ub( (unsigned char)settings.iFogColorR, + (unsigned char)settings.iFogColorG, + (unsigned char)settings.iFogColorB ); + pRenderContext->FogStart( settings.flFogStart ); + pRenderContext->FogEnd( settings.flFogEnd ); + pRenderContext->FogMaxDensity( settings.flFogDensity ); + } + } + else if ( !m_fogInfo.m_bEyeInFogVolume ) + { + EnableWorldFog(); + } + else + { + m_ClearFlags |= VIEW_CLEAR_COLOR; + + SetFogVolumeState( m_fogInfo, false ); + + pRenderContext.GetFrom( materials ); + + unsigned char ucFogColor[3]; + pRenderContext->GetFogColor( ucFogColor ); + pRenderContext->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); + } + + pRenderContext.SafeRelease(); + + DrawExecute( 0, CurrentViewID(), 0 ); + + pRenderContext.GetFrom( materials ); + pRenderContext->ClearColor4ub( 0, 0, 0, 255 ); + + m_pMainView->DisableFog(); + }; + + virtual void CallbackInitRenderList( int viewId ) + { + BaseClass::CallbackInitRenderList( viewId ); + + if ( settings.bDrawPlayers && settings.bDrawStaticProps && + settings.bDrawTranslucents && settings.bDrawWeapons && + settings.bDrawMisc ) + return; + + for ( int i = 0; i < RENDER_GROUP_COUNT; i++ ) + { +#ifndef SWARM_DLL + const bool bStaticProp = i == 0 || i == 2 || i == 4 || i == 6; +#endif + + for ( int e = 0; e < m_pRenderablesList->m_RenderGroupCounts[i]; e++ ) + { + CClientRenderablesList::CEntry *pEntry = m_pRenderablesList->m_RenderGroups[i] + e; + + if ( !pEntry || !pEntry->m_pRenderable ) + continue; + +#ifdef SWARM_DLL + const bool bStaticProp = pEntry->m_nModelType == RENDERABLE_MODEL_STATIC_PROP; +#endif + + bool bRemove = false; + if ( bStaticProp ) + bRemove = !settings.bDrawStaticProps; + else + { + IClientUnknown *pUnknown = pEntry->m_pRenderable->GetIClientUnknown(); + + if ( !pUnknown || !pUnknown->GetBaseEntity() ) + continue; + + C_BaseEntity *pEntity = pUnknown->GetBaseEntity(); + + if ( pEntity->IsPlayer() ) + bRemove = !settings.bDrawPlayers; + else if ( dynamic_cast< CBaseCombatWeapon* >( pEntity ) != NULL ) + bRemove = !settings.bDrawWeapons; +#ifdef SWARM_DLL + else if ( pEntity->ComputeTranslucencyType() != RENDERABLE_IS_OPAQUE ) +#else + else if ( pEntry->m_pRenderable->IsTransparent() ) +#endif + bRemove = !settings.bDrawTranslucents; + else + bRemove = !settings.bDrawMisc; + } + + if ( bRemove ) + { + pEntry->m_pRenderable = NULL; +#ifndef SWARM_DLL + pEntry->m_RenderHandle = NULL; +#endif + } + } + + int eLast = -1; + for ( int e = 0; e < m_pRenderablesList->m_RenderGroupCounts[i]; e++ ) + { + CClientRenderablesList::CEntry *pEntry = m_pRenderablesList->m_RenderGroups[i] + e; + + if ( !pEntry || !pEntry->m_pRenderable +#ifndef SWARM_DLL + || !pEntry->m_RenderHandle +#endif + ) + { + for ( int e2 = e + 1; e2 < m_pRenderablesList->m_RenderGroupCounts[i]; e2++ ) + { + CClientRenderablesList::CEntry *pEntry2 = m_pRenderablesList->m_RenderGroups[i] + e2; + if ( pEntry2 && pEntry2->m_pRenderable +#ifndef SWARM_DLL + && pEntry2->m_RenderHandle +#endif + ) + { + CClientRenderablesList::CEntry tmp = *pEntry; + *pEntry = *pEntry2; + *pEntry2 = tmp; + break; + } + } + } + + if ( pEntry && pEntry->m_pRenderable +#ifndef SWARM_DLL + && pEntry->m_RenderHandle +#endif + ) + eLast = e; + } + + m_pRenderablesList->m_RenderGroupCounts[i] = eLast + 1; + } + }; + + virtual bool ShouldDrawParticles() + { + return settings.bDrawParticles; + }; + + virtual bool ShouldDrawRopes() + { + return settings.bDrawRopes; + }; + + virtual bool ShouldDrawWorld() + { + return settings.bDrawWorld; + }; + + virtual bool ShouldDrawTranslucents() + { + return settings.bDrawTranslucents; + }; + + virtual bool ShouldDrawTranslucentWorld() + { + return settings.bDrawWorld && settings.bDrawTranslucents; + }; + +private: + VisibleFogVolumeInfo_t m_fogInfo; + +}; + +#ifdef SWARM_DLL +bool UpdateRefractIfNeededByList( CViewModelRenderablesList::RenderGroups_t &list ) +{ + int nCount = list.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientRenderable *pRenderable = list[i].m_pRenderable; + Assert( pRenderable ); + if ( pRenderable->GetRenderFlags() & ERENDERFLAGS_NEEDS_POWER_OF_TWO_FB ) + { + UpdateRefractTexture(); + return true; + } + } + return false; +} +void DrawRenderablesInList( CViewModelRenderablesList::RenderGroups_t &renderGroups, int flags = 0 ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetCurrentlyDrawingEntity() == NULL ); + + ASSERT_LOCAL_PLAYER_RESOLVABLE(); +#if defined( DBGFLAG_ASSERT ) + int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); +#endif + Assert( pCView->GetCurrentlyDrawingEntity() == NULL ); + int nCount = renderGroups.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientRenderable *pRenderable = renderGroups[i].m_pRenderable; + Assert( pRenderable ); + + // Non-view models wanting to render in view model list... + if ( pRenderable->ShouldDraw() ) + { + Assert( !IsSplitScreenSupported() || pRenderable->ShouldDrawForSplitScreenUser( nSlot ) ); + pCView->SetCurrentlyDrawingEntity( pRenderable->GetIClientUnknown()->GetBaseEntity() ); + pRenderable->DrawModel( STUDIO_RENDER | flags, renderGroups[i].m_InstanceData ); + } + } + pCView->SetCurrentlyDrawingEntity( NULL ); +} +#else +static inline bool UpdateRefractIfNeededByList( CUtlVector< IClientRenderable * > &list ) +{ + int nCount = list.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientUnknown *pUnk = list[i]->GetIClientUnknown(); + Assert( pUnk ); + + IClientRenderable *pRenderable = pUnk->GetClientRenderable(); + Assert( pRenderable ); + + if ( pRenderable->UsesPowerOfTwoFrameBufferTexture() ) + { + UpdateRefractTexture(); + return true; + } + } + + return false; +} +static inline void DrawRenderablesInList( CUtlVector< IClientRenderable * > &list, int flags = 0 ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetCurrentlyDrawingEntity() == NULL ); + + int nCount = list.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientUnknown *pUnk = list[i]->GetIClientUnknown(); + Assert( pUnk ); + + IClientRenderable *pRenderable = pUnk->GetClientRenderable(); + Assert( pRenderable ); + + // Non-view models wanting to render in view model list... + if ( pRenderable->ShouldDraw() ) + { + pCView->SetCurrentlyDrawingEntity( pUnk->GetBaseEntity() ); + pRenderable->DrawModel( STUDIO_RENDER | flags ); + } + } + pCView->SetCurrentlyDrawingEntity( NULL ); +} +#endif + + +int &ShaderEditorHandler::GetViewIdForModify() +{ + Assert( m_piCurrentViewId != NULL ); + + return *m_piCurrentViewId; +} +const VisibleFogVolumeInfo_t &ShaderEditorHandler::GetFogVolumeInfo() +{ + return m_tFogVolumeInfo; +} +const WaterRenderInfo_t &ShaderEditorHandler::GetWaterRenderInfo() +{ + return m_tWaterRenderInfo; +} + +pFnVrCallback_Declare( VrCallback_General ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetViewSetup() != NULL ); + + const CViewSetup *setup = pCView->GetViewSetup(); + + CSimpleVCallbackView::EditorViewSettings settings; + + settings.bDrawPlayers = pbOptions[0]; + settings.bDrawWeapons = pbOptions[1]; + settings.bDrawStaticProps = pbOptions[2]; + settings.bDrawMisc = pbOptions[3]; + settings.bDrawTranslucents = pbOptions[4]; + settings.bDrawWater = pbOptions[5]; + settings.bDrawWorld = pbOptions[6]; + settings.bDrawParticles = pbOptions[7]; + settings.bDrawRopes = pbOptions[8]; + settings.bDrawSkybox = pbOptions[9]; + settings.bClipSkybox = pbOptions[10]; + settings.bClearColor = pbOptions[11]; + settings.bClearDepth = pbOptions[12]; + settings.bClearStencil = pbOptions[13]; + settings.bClearObeyStencil = pbOptions[14]; + settings.bFogOverride = pbOptions[15]; + settings.bFogEnabled = pbOptions[16]; + + settings.iClearColorR = piOptions[0]; + settings.iClearColorG = piOptions[1]; + settings.iClearColorB = piOptions[2]; + settings.iClearColorA = piOptions[3]; + settings.iFogColorR = piOptions[4]; + settings.iFogColorG = piOptions[5]; + settings.iFogColorB = piOptions[6]; + + settings.flFogStart = pflOptions[0]; + settings.flFogEnd = pflOptions[1]; + settings.flFogDensity = pflOptions[2]; + + if ( settings.flFogEnd < 0 ) + settings.flFogEnd = setup->zFar; + + CRefPtr pGeneralCallbackView = new CSimpleVCallbackView( pCView ); + pGeneralCallbackView->Setup( *setup, settings, + g_ShaderEditorSystem->GetFogVolumeInfo(), g_ShaderEditorSystem->GetWaterRenderInfo() ); + pCView->AddViewToScene( pGeneralCallbackView ); +} + +pFnVrCallback_Declare( VrCallback_ViewModel ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetViewSetup() != NULL ); + + CMatRenderContextPtr pRenderContext( materials ); + + static ConVarRef drawVM( "r_drawviewmodel" ); + + const bool bHideVM = pbOptions[0]; + const bool bFogOverride = pbOptions[5]; + const int iClearFlags = (pbOptions[1] ? VIEW_CLEAR_COLOR : 0) | + (pbOptions[2] ? VIEW_CLEAR_DEPTH : 0) | + (pbOptions[3] ? VIEW_CLEAR_STENCIL : 0) | + (pbOptions[4] ? VIEW_CLEAR_OBEY_STENCIL : 0); + + drawVM.SetValue( !bHideVM ); + + if ( bFogOverride ) + { + if ( !pbOptions[6] ) + pCView->DisableFog(); + else + { + pRenderContext->FogMode( MATERIAL_FOG_LINEAR ); + pRenderContext->FogColor3ub( (unsigned char)piOptions[4], + (unsigned char)piOptions[5], + (unsigned char)piOptions[6] ); + pRenderContext->FogStart( pflOptions[0] ); + pRenderContext->FogEnd( pflOptions[1] ); + pRenderContext->FogMaxDensity( pflOptions[2] ); + } + } + + int bbx, bby; + materials->GetBackBufferDimensions( bbx, bby ); + + // Restore the matrices + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + + ITexture *pTex = pRenderContext->GetRenderTarget(); + const CViewSetup &view = *pCView->GetViewSetup(); + CViewSetup viewModelSetup( view ); + viewModelSetup.zNear = view.zNearViewmodel; + viewModelSetup.zFar = view.zFarViewmodel; + viewModelSetup.fov = view.fovViewmodel; +#ifdef SWARM_DLL + viewModelSetup.m_flAspectRatio = engine->GetScreenAspectRatio( view.width, view.height ); +#else + viewModelSetup.m_flAspectRatio = engine->GetScreenAspectRatio(); +#endif + viewModelSetup.width = pTex ? pTex->GetActualWidth() : bbx; + viewModelSetup.height = pTex ? pTex->GetActualHeight() : bby; + + if ( iClearFlags & VIEW_CLEAR_COLOR ) + { + pRenderContext->ClearColor4ub( (unsigned char)piOptions[0], + (unsigned char)piOptions[1], + (unsigned char)piOptions[2], + (unsigned char)piOptions[3] ); + } + + render->Push3DView( viewModelSetup, iClearFlags, pTex, pCView->GetFrustum() ); + const bool bUseDepthHack = true; + + float depthmin = 0.0f; + float depthmax = 1.0f; + + // HACK HACK: Munge the depth range to prevent view model from poking into walls, etc. + // Force clipped down range + if( bUseDepthHack ) + pRenderContext->DepthRange( 0.0f, 0.1f ); + +#ifdef SWARM_DLL + CViewModelRenderablesList list; + ClientLeafSystem()->CollateViewModelRenderables( &list ); + CViewModelRenderablesList::RenderGroups_t &opaqueViewModelList = list.m_RenderGroups[ CViewModelRenderablesList::VM_GROUP_OPAQUE ]; + CViewModelRenderablesList::RenderGroups_t &translucentViewModelList = list.m_RenderGroups[ CViewModelRenderablesList::VM_GROUP_TRANSLUCENT ]; +#else + CUtlVector< IClientRenderable * > opaqueViewModelList( 32 ); + CUtlVector< IClientRenderable * > translucentViewModelList( 32 ); + ClientLeafSystem()->CollateViewModelRenderables( opaqueViewModelList, translucentViewModelList ); +#endif + + const bool bUpdateRefractForOpaque = UpdateRefractIfNeededByList( opaqueViewModelList ); + DrawRenderablesInList( opaqueViewModelList ); + + if ( !bUpdateRefractForOpaque ) + UpdateRefractIfNeededByList( translucentViewModelList ); + + DrawRenderablesInList( translucentViewModelList, STUDIO_TRANSPARENCY ); + + // Reset the depth range to the original values + if( bUseDepthHack ) + pRenderContext->DepthRange( depthmin, depthmax ); + + render->PopView( pCView->GetFrustum() ); + + // Restore the matrices + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); + + if ( bFogOverride ) + pCView->DisableFog(); +} + + +void ShaderEditorHandler::RegisterViewRenderCallbacks() +{ + if ( !IsReady() ) + return; + + const char *boolNames_generalVrc[] = { + "Draw players", + "Draw weapons", + "Draw static props", + "Draw misc", + "Draw translucents", + "Draw water", + "Draw world", + "Draw particles", + "Draw ropes", + "Draw skybox (2D)", + "Clip skybox", + "Clear color", + "Clear depth", + "Clear stencil", + "Clear obey stencil", + "Fog override", + "Fog force enabled", + }; + const bool boolDefaults_generalVrc[] = { + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + true, + true, + false, + false, + false, + true, + }; + const char *intNames_generalVrc[] = { + "Clear color R (0-255)", + "Clear color G (0-255)", + "Clear color B (0-255)", + "Clear color A (0-255)", + "Fog color R (0-255)", + "Fog color G (0-255)", + "Fog color B (0-255)", + }; + + const char *floatNames_generalVrc[] = { + "Fog start (units)", + "Fog end (units)", + "Fog density (0-1)", + }; + const float floatDefaults_generalVrc[] = { + 0, + 2000, + 1, + }; + + + const char *boolNames_vmVrc[] = { + "Hide default viewmodel", + "Clear color", + "Clear depth", + "Clear stencil", + "Clear obey stencil", + "Fog override", + "Fog force enabled", + }; + const bool boolDefaults_vmVrc[] = { + false, + true, + true, + false, + false, + false, + true, + }; + + Assert( ARRAYSIZE(boolNames_generalVrc) == ARRAYSIZE(boolDefaults_generalVrc) ); + Assert( ARRAYSIZE(floatNames_generalVrc) == ARRAYSIZE(floatDefaults_generalVrc) ); + Assert( ARRAYSIZE(boolNames_vmVrc) == ARRAYSIZE(boolDefaults_vmVrc) ); + + shaderEdit->RegisterViewRenderCallback( "General view", VrCallback_General, + boolNames_generalVrc, boolDefaults_generalVrc, ARRAYSIZE(boolNames_generalVrc), + intNames_generalVrc, NULL, ARRAYSIZE(intNames_generalVrc), + floatNames_generalVrc, floatDefaults_generalVrc, ARRAYSIZE(floatNames_generalVrc) ); + + shaderEdit->RegisterViewRenderCallback( "Viewmodel view", VrCallback_ViewModel, + boolNames_vmVrc, boolDefaults_vmVrc, ARRAYSIZE(boolNames_vmVrc), + intNames_generalVrc, NULL, ARRAYSIZE(intNames_generalVrc), + floatNames_generalVrc, floatDefaults_generalVrc, ARRAYSIZE(floatNames_generalVrc) ); + + shaderEdit->LockViewRenderCallbacks(); +} + +#endif \ No newline at end of file diff --git a/sp/src/game/client/ShaderEditor/ShaderEditorSystem.h b/sp/src/game/client/ShaderEditor/ShaderEditorSystem.h new file mode 100644 index 00000000..1592e780 --- /dev/null +++ b/sp/src/game/client/ShaderEditor/ShaderEditorSystem.h @@ -0,0 +1,59 @@ +#ifndef SHEDITSYSTEM_H +#define SHEDITSYSTEM_H + +#include "cbase.h" + +#include "datacache/imdlcache.h" + +#include "iviewrender.h" +#include "view_shared.h" +#include "viewrender.h" + + +class ShaderEditorHandler : public CAutoGameSystemPerFrame +{ +public: + ShaderEditorHandler( char const *name ); + ~ShaderEditorHandler(); + + virtual bool Init(); + virtual void Shutdown(); + + virtual void Update( float frametime ); + virtual void PreRender(); + virtual void PostRender(); + +#ifdef SOURCE_2006 + void CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo ); +#else + void CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo, const WaterRenderInfo_t &waterRenderInfo ); +#endif + void CustomPostRender(); + void UpdateSkymask(bool bCombineMode, int x, int y, int w, int h); + + const bool IsReady(); + int &GetViewIdForModify(); + const VisibleFogVolumeInfo_t &GetFogVolumeInfo(); +#ifndef SOURCE_2006 + const WaterRenderInfo_t &GetWaterRenderInfo(); +#endif + +private: + bool m_bReady; + + void RegisterCallbacks(); + void PrepareCallbackData(); + + void RegisterViewRenderCallbacks(); + + int *m_piCurrentViewId; + VisibleFogVolumeInfo_t m_tFogVolumeInfo; +#ifndef SOURCE_2006 + WaterRenderInfo_t m_tWaterRenderInfo; +#endif +}; + +extern ShaderEditorHandler *g_ShaderEditorSystem; + + +#endif \ No newline at end of file diff --git a/sp/src/game/client/episodic/c_npc_advisor.cpp b/sp/src/game/client/episodic/c_npc_advisor.cpp new file mode 100644 index 00000000..4aba115d --- /dev/null +++ b/sp/src/game/client/episodic/c_npc_advisor.cpp @@ -0,0 +1,251 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Definition for client-side advisor. +// +//=====================================================================================// + + + +#include "cbase.h" +// this file contains the definitions for the message ID constants (eg ADVISOR_MSG_START_BEAM etc) +#include "npc_advisor_shared.h" + +#if NPC_ADVISOR_HAS_BEHAVIOR + +#include "particles_simple.h" +#include "citadel_effects_shared.h" +#include "particles_attractor.h" +#include "clienteffectprecachesystem.h" +#include "c_te_effect_dispatch.h" + +#include "c_ai_basenpc.h" +#include "dlight.h" +#include "iefx.h" + + +//----------------------------------------------------------------------------- +// Purpose: unpack a networked entity index into a basehandle. +//----------------------------------------------------------------------------- +inline C_BaseEntity *IndexToEntity( int eindex ) +{ + return ClientEntityList().GetBaseEntityFromHandle(ClientEntityList().EntIndexToHandle(eindex)); +} + + + +#define ADVISOR_ELIGHT_CVARS 1 // enable/disable tuning advisor elight with console variables + +#if ADVISOR_ELIGHT_CVARS +ConVar advisor_elight_e("advisor_elight_e","3"); +ConVar advisor_elight_rfeet("advisor_elight_rfeet","52"); +#endif + + +/*! Client-side reflection of the advisor class. + */ +class C_NPC_Advisor : public C_AI_BaseNPC +{ + DECLARE_CLASS( C_NPC_Advisor, C_AI_BaseNPC ); + DECLARE_CLIENTCLASS(); + +public: + // Server to client message received + virtual void ReceiveMessage( int classID, bf_read &msg ); + virtual void ClientThink( void ); + +private: + /* + // broken into its own function so I can move it if necesasry + void Initialize(); + */ + + // start/stop beam particle effect from me to a pelting object + void StartBeamFX( C_BaseEntity *pOnEntity ); + void StopBeamFX( C_BaseEntity *pOnEntity ); + + void StartElight(); + void StopElight(); + + int m_ElightKey; // test using an elight to make the escape sequence more visible. 0 is invalid. + +}; + +IMPLEMENT_CLIENTCLASS_DT( C_NPC_Advisor, DT_NPC_Advisor, CNPC_Advisor ) + +END_RECV_TABLE() + +// Server to client message received +void C_NPC_Advisor::ReceiveMessage( int classID, bf_read &msg ) +{ + if ( classID != GetClientClass()->m_ClassID ) + { + // message is for subclass + BaseClass::ReceiveMessage( classID, msg ); + return; + } + + int messageType = msg.ReadByte(); + switch( messageType ) + { + case ADVISOR_MSG_START_BEAM: + { + int eindex = msg.ReadLong(); + StartBeamFX(IndexToEntity(eindex)); + } + break; + + case ADVISOR_MSG_STOP_BEAM: + { + int eindex = msg.ReadLong(); + StopBeamFX(IndexToEntity(eindex)); + + } + break; + + case ADVISOR_MSG_STOP_ALL_BEAMS: + { + ParticleProp()->StopEmission(); + } + break; + case ADVISOR_MSG_START_ELIGHT: + { + StartElight(); + } + break; + case ADVISOR_MSG_STOP_ELIGHT: + { + StopElight(); + } + break; + + default: + AssertMsg1( false, "Received unknown message %d", messageType); + } +} + +/// only use of the clientthink on the advisor is to update the elight +void C_NPC_Advisor::ClientThink( void ) +{ + // if the elight has gone away, bail out + if (m_ElightKey == 0) + { + SetNextClientThink( CLIENT_THINK_NEVER ); + return; + } + + // get the elight + dlight_t * el = effects->GetElightByKey(m_ElightKey); + if (!el) + { + // the elight has been invalidated. bail out. + m_ElightKey = 0; + + SetNextClientThink( CLIENT_THINK_NEVER ); + return; + } + else + { + el->origin = WorldSpaceCenter(); + +#if ADVISOR_ELIGHT_CVARS + el->color.exponent = advisor_elight_e.GetFloat(); + el->radius = advisor_elight_rfeet.GetFloat() * 12.0f; +#endif + } +} + +//----------------------------------------------------------------------------- +// Create a telekinetic beam effect from my head to an object +// TODO: use a point attachment. +//----------------------------------------------------------------------------- +void C_NPC_Advisor::StartBeamFX( C_BaseEntity *pOnEntity ) +{ + Assert(pOnEntity); + if (!pOnEntity) + return; + + CNewParticleEffect *pEffect = ParticleProp()->Create( "Advisor_Psychic_Beam", PATTACH_ABSORIGIN_FOLLOW ); + + Assert(pEffect); + if (!pEffect) return; + + ParticleProp()->AddControlPoint( pEffect, 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW ); +} + + +//----------------------------------------------------------------------------- +// terminate a telekinetic beam effect from my head to an object +//----------------------------------------------------------------------------- +void C_NPC_Advisor::StopBeamFX( C_BaseEntity *pOnEntity ) +{ + Assert(pOnEntity); + if (!pOnEntity) + return; + + ParticleProp()->StopParticlesInvolving( pOnEntity ); +} + + + + + + +void C_NPC_Advisor::StartElight() +{ + AssertMsg(m_ElightKey == 0 , "Advisor trying to create new elight on top of old one!"); + if ( m_ElightKey != 0 ) + { + Warning("Advisor tried to start his elight when it was already one.\n"); + } + else + { + m_ElightKey = LIGHT_INDEX_TE_DYNAMIC + this->entindex(); + dlight_t * el = effects->CL_AllocElight( m_ElightKey ); + + if ( el ) + { + // create an elight on top of me + el->origin = this->WorldSpaceCenter(); + + el->color.r = 235; + el->color.g = 255; + el->color.b = 255; + el->color.exponent = 3; + + el->radius = 52*12; + el->decay = 0.0f; + el->die = gpGlobals->curtime + 2000.0f; // 1000 just means " a long time " + + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } + else + { // null out the light value + m_ElightKey = 0; + } + } +} + +void C_NPC_Advisor::StopElight() +{ + AssertMsg( m_ElightKey != 0, "Advisor tried to stop elight when none existed!"); + dlight_t * el; + // note: the following conditional sets el if not short-circuited + if ( m_ElightKey == 0 || (el = effects->GetElightByKey(m_ElightKey)) == NULL ) + { + Warning("Advisor tried to stop its elight when it had none.\n"); + } + else + { + // kill the elight by setting the die value to now + el->die = gpGlobals->curtime; + } +} + + +#endif + +/****************************************************** + * Tenser, said the Tensor. * + * Tenser, said the Tensor. * + * Tension, apprehension and dissension have begun. * + ******************************************************/ diff --git a/sp/src/game/client/episodic/c_npc_puppet.cpp b/sp/src/game/client/episodic/c_npc_puppet.cpp new file mode 100644 index 00000000..193b1da2 --- /dev/null +++ b/sp/src/game/client/episodic/c_npc_puppet.cpp @@ -0,0 +1,167 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "c_ai_basenpc.h" +#include "bone_setup.h" + +// Must be the last file included +#include "memdbgon.h" + + +extern ConVar r_sequence_debug; + +class C_NPC_Puppet : public C_AI_BaseNPC +{ + DECLARE_CLASS( C_NPC_Puppet, C_AI_BaseNPC ); +public: + + virtual void ClientThink( void ); + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ); + virtual void AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime ); + + EHANDLE m_hAnimationTarget; + int m_nTargetAttachment; + + DECLARE_CLIENTCLASS(); +}; + +IMPLEMENT_CLIENTCLASS_DT( C_NPC_Puppet, DT_NPC_Puppet, CNPC_Puppet ) + RecvPropEHandle( RECVINFO(m_hAnimationTarget) ), + RecvPropInt( RECVINFO(m_nTargetAttachment) ), +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_NPC_Puppet::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + if ( updateType == DATA_UPDATE_CREATED ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: We need to slam our position! +//----------------------------------------------------------------------------- +void C_NPC_Puppet::BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ) +{ + if ( m_hAnimationTarget && m_nTargetAttachment != -1 ) + { + C_BaseAnimating *pTarget = m_hAnimationTarget->GetBaseAnimating(); + if ( pTarget ) + { + matrix3x4_t matTarget; + pTarget->GetAttachment( m_nTargetAttachment, matTarget ); + + MatrixCopy( matTarget, GetBoneForWrite( 0 ) ); + boneComputed.ClearAll(); // FIXME: Why is this calculated already? + boneComputed.MarkBone( 0 ); + } + } + + // Call the baseclass + BaseClass::BuildTransformations( pStudioHdr, pos, q, cameraTransform, boneMask, boneComputed ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_NPC_Puppet::ClientThink( void ) +{ + if ( m_hAnimationTarget == NULL ) + return; + + C_BaseAnimating *pTarget = m_hAnimationTarget->GetBaseAnimating(); + if ( pTarget == NULL ) + return; + + int nTargetSequence = pTarget->GetSequence(); + const char *pSequenceName = pTarget->GetSequenceName( nTargetSequence ); + + int nSequence = LookupSequence( pSequenceName ); + if ( nSequence >= 0 ) + { + if ( nSequence != GetSequence() ) + { + SetSequence( nSequence ); + UpdateVisibility(); + } + + SetCycle( pTarget->GetCycle() ); + SetPlaybackRate( pTarget->GetPlaybackRate() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_NPC_Puppet::AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime ) +{ + if ( m_hAnimationTarget == NULL ) + return; + + C_BaseAnimatingOverlay *pTarget = dynamic_cast( m_hAnimationTarget->GetBaseAnimating() ); + if ( pTarget == NULL ) + return; + + // resort the layers + int layer[MAX_OVERLAYS]; + int i; + for (i = 0; i < MAX_OVERLAYS; i++) + { + layer[i] = MAX_OVERLAYS; + } + for (i = 0; i < pTarget->m_AnimOverlay.Count(); i++) + { + if (pTarget->m_AnimOverlay[i].m_nOrder < MAX_OVERLAYS) + { + layer[pTarget->m_AnimOverlay[i].m_nOrder] = i; + } + } + + int j; + for (j = 0; j < MAX_OVERLAYS; j++) + { + i = layer[ j ]; + if (i < pTarget->m_AnimOverlay.Count()) + { + float fWeight = pTarget->m_AnimOverlay[i].m_flWeight; + + if (fWeight > 0) + { + const char *pSequenceName = pTarget->GetSequenceName( pTarget->m_AnimOverlay[i].m_nSequence ); + + int nSequence = LookupSequence( pSequenceName ); + if ( nSequence >= 0 ) + { + float fCycle = pTarget->m_AnimOverlay[ i ].m_flCycle; + + fCycle = ClampCycle( fCycle, IsSequenceLooping( nSequence ) ); + + if (fWeight > 1) + fWeight = 1; + + boneSetup.AccumulatePose( pos, q, nSequence, fCycle, fWeight, currentTime, NULL ); + +#if _DEBUG + if (Q_stristr( boneSetup.GetStudioHdr()->pszName(), r_sequence_debug.GetString()) != NULL) + { + DevMsgRT( "%6.2f : %30s : %5.3f : %4.2f : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( nSequence ).pszLabel(), fCycle, fWeight, i ); + } +#endif + + } + } + } + } +} + diff --git a/sp/src/game/client/episodic/c_prop_coreball.cpp b/sp/src/game/client/episodic/c_prop_coreball.cpp new file mode 100644 index 00000000..a8ec7dc9 --- /dev/null +++ b/sp/src/game/client/episodic/c_prop_coreball.cpp @@ -0,0 +1,142 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +class C_PropCoreBall : public C_BaseAnimating +{ + DECLARE_CLASS( C_PropCoreBall, C_BaseAnimating ); + DECLARE_CLIENTCLASS(); + DECLARE_DATADESC(); + +public: + + C_PropCoreBall(); + + void ApplyBoneMatrixTransform( matrix3x4_t& transform ); + + float m_flScaleX; + float m_flScaleY; + float m_flScaleZ; + + float m_flLerpTimeX; + float m_flLerpTimeY; + float m_flLerpTimeZ; + + float m_flGoalTimeX; + float m_flGoalTimeY; + float m_flGoalTimeZ; + + float m_flCurrentScale[3]; + bool m_bRunningScale[3]; + float m_flTargetScale[3]; + +private: + +}; + +void RecvProxy_ScaleX( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_PropCoreBall *pCoreData = (C_PropCoreBall *) pStruct; + + pCoreData->m_flScaleX = pData->m_Value.m_Float; + + if ( pCoreData->m_bRunningScale[0] == true ) + { + pCoreData->m_flTargetScale[0] = pCoreData->m_flCurrentScale[0]; + } +} + +void RecvProxy_ScaleY( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_PropCoreBall *pCoreData = (C_PropCoreBall *) pStruct; + + pCoreData->m_flScaleY = pData->m_Value.m_Float; + + if ( pCoreData->m_bRunningScale[1] == true ) + { + pCoreData->m_flTargetScale[1] = pCoreData->m_flCurrentScale[1]; + } +} + +void RecvProxy_ScaleZ( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_PropCoreBall *pCoreData = (C_PropCoreBall *) pStruct; + + pCoreData->m_flScaleZ = pData->m_Value.m_Float; + + if ( pCoreData->m_bRunningScale[2] == true ) + { + pCoreData->m_flTargetScale[2] = pCoreData->m_flCurrentScale[2]; + } +} + +IMPLEMENT_CLIENTCLASS_DT( C_PropCoreBall, DT_PropCoreBall, CPropCoreBall ) + RecvPropFloat( RECVINFO( m_flScaleX ), 0, RecvProxy_ScaleX ), + RecvPropFloat( RECVINFO( m_flScaleY ), 0, RecvProxy_ScaleY ), + RecvPropFloat( RECVINFO( m_flScaleZ ), 0, RecvProxy_ScaleZ ), + + RecvPropFloat( RECVINFO( m_flLerpTimeX ) ), + RecvPropFloat( RECVINFO( m_flLerpTimeY ) ), + RecvPropFloat( RECVINFO( m_flLerpTimeZ ) ), + + RecvPropFloat( RECVINFO( m_flGoalTimeX ) ), + RecvPropFloat( RECVINFO( m_flGoalTimeY ) ), + RecvPropFloat( RECVINFO( m_flGoalTimeZ ) ), +END_RECV_TABLE() + + +BEGIN_DATADESC( C_PropCoreBall ) + DEFINE_AUTO_ARRAY( m_flTargetScale, FIELD_FLOAT ), + DEFINE_AUTO_ARRAY( m_bRunningScale, FIELD_BOOLEAN ), +END_DATADESC() + +C_PropCoreBall::C_PropCoreBall( void ) +{ + m_flTargetScale[0] = 1.0f; + m_flTargetScale[1] = 1.0f; + m_flTargetScale[2] = 1.0f; + + m_bRunningScale[0] = false; + m_bRunningScale[1] = false; + m_bRunningScale[2] = false; +} + +void C_PropCoreBall::ApplyBoneMatrixTransform( matrix3x4_t& transform ) +{ + BaseClass::ApplyBoneMatrixTransform( transform ); + + float flVal[3] = { m_flTargetScale[0], m_flTargetScale[1], m_flTargetScale[2] }; + float *flTargetScale[3] = { &m_flTargetScale[0], &m_flTargetScale[1], &m_flTargetScale[2] }; + float flScale[3] = { m_flScaleX, m_flScaleY, m_flScaleZ }; + float flLerpTime[3] = { m_flLerpTimeX, m_flLerpTimeY, m_flLerpTimeZ }; + float flGoalTime[3] = { m_flGoalTimeX, m_flGoalTimeY, m_flGoalTimeZ }; + bool *bRunning[3] = { &m_bRunningScale[0], &m_bRunningScale[1], &m_bRunningScale[2] }; + + for ( int i = 0; i < 3; i++ ) + { + if ( *flTargetScale[i] != flScale[i] ) + { + float deltaTime = (float)( gpGlobals->curtime - flGoalTime[i]) / flLerpTime[i]; + float flRemapVal = SimpleSplineRemapVal( deltaTime, 0.0f, 1.0f, *flTargetScale[i], flScale[i] ); + + *bRunning[i] = true; + + if ( deltaTime >= 1.0f ) + { + *flTargetScale[i] = flScale[i]; + *bRunning[i] = false; + } + + flVal[i] = flRemapVal; + m_flCurrentScale[i] = flVal[i]; + } + } + + VectorScale( transform[0], flVal[0], transform[0] ); + VectorScale( transform[1], flVal[1], transform[1] ); + VectorScale( transform[2], flVal[2], transform[2] ); +} \ No newline at end of file diff --git a/sp/src/game/client/episodic/c_prop_scalable.cpp b/sp/src/game/client/episodic/c_prop_scalable.cpp new file mode 100644 index 00000000..d3902db3 --- /dev/null +++ b/sp/src/game/client/episodic/c_prop_scalable.cpp @@ -0,0 +1,196 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +class C_PropScalable : public C_BaseAnimating +{ + DECLARE_CLASS( C_PropScalable, C_BaseAnimating ); + DECLARE_CLIENTCLASS(); + DECLARE_DATADESC(); + +public: + + C_PropScalable(); + + virtual void ApplyBoneMatrixTransform( matrix3x4_t& transform ); + virtual void GetRenderBounds( Vector &theMins, Vector &theMaxs ); + + // Must be available to proxy functions + float m_flScaleX; + float m_flScaleY; + float m_flScaleZ; + + float m_flLerpTimeX; + float m_flLerpTimeY; + float m_flLerpTimeZ; + + float m_flGoalTimeX; + float m_flGoalTimeY; + float m_flGoalTimeZ; + + float m_flCurrentScale[3]; + bool m_bRunningScale[3]; + float m_flTargetScale[3]; + +private: + + void CalculateScale( void ); + float m_nCalcFrame; // Frame the last calculation was made at +}; + +void RecvProxy_ScaleX( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_PropScalable *pCoreData = (C_PropScalable *) pStruct; + + pCoreData->m_flScaleX = pData->m_Value.m_Float; + + if ( pCoreData->m_bRunningScale[0] == true ) + { + pCoreData->m_flTargetScale[0] = pCoreData->m_flCurrentScale[0]; + } +} + +void RecvProxy_ScaleY( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_PropScalable *pCoreData = (C_PropScalable *) pStruct; + + pCoreData->m_flScaleY = pData->m_Value.m_Float; + + if ( pCoreData->m_bRunningScale[1] == true ) + { + pCoreData->m_flTargetScale[1] = pCoreData->m_flCurrentScale[1]; + } +} + +void RecvProxy_ScaleZ( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_PropScalable *pCoreData = (C_PropScalable *) pStruct; + + pCoreData->m_flScaleZ = pData->m_Value.m_Float; + + if ( pCoreData->m_bRunningScale[2] == true ) + { + pCoreData->m_flTargetScale[2] = pCoreData->m_flCurrentScale[2]; + } +} + +IMPLEMENT_CLIENTCLASS_DT( C_PropScalable, DT_PropScalable, CPropScalable ) + RecvPropFloat( RECVINFO( m_flScaleX ), 0, RecvProxy_ScaleX ), + RecvPropFloat( RECVINFO( m_flScaleY ), 0, RecvProxy_ScaleY ), + RecvPropFloat( RECVINFO( m_flScaleZ ), 0, RecvProxy_ScaleZ ), + + RecvPropFloat( RECVINFO( m_flLerpTimeX ) ), + RecvPropFloat( RECVINFO( m_flLerpTimeY ) ), + RecvPropFloat( RECVINFO( m_flLerpTimeZ ) ), + + RecvPropFloat( RECVINFO( m_flGoalTimeX ) ), + RecvPropFloat( RECVINFO( m_flGoalTimeY ) ), + RecvPropFloat( RECVINFO( m_flGoalTimeZ ) ), +END_RECV_TABLE() + + +BEGIN_DATADESC( C_PropScalable ) + DEFINE_AUTO_ARRAY( m_flTargetScale, FIELD_FLOAT ), + DEFINE_AUTO_ARRAY( m_bRunningScale, FIELD_BOOLEAN ), +END_DATADESC() + +C_PropScalable::C_PropScalable( void ) +{ + m_flTargetScale[0] = 1.0f; + m_flTargetScale[1] = 1.0f; + m_flTargetScale[2] = 1.0f; + + m_bRunningScale[0] = false; + m_bRunningScale[1] = false; + m_bRunningScale[2] = false; + + m_nCalcFrame = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates the scake of the object once per frame +//----------------------------------------------------------------------------- +void C_PropScalable::CalculateScale( void ) +{ + // Don't bother to calculate this for a second time in the same frame + if ( m_nCalcFrame == gpGlobals->framecount ) + return; + + // Mark that we cached this value for the frame + m_nCalcFrame = gpGlobals->framecount; + + float flVal[3] = { m_flTargetScale[0], m_flTargetScale[1], m_flTargetScale[2] }; + float *flTargetScale[3] = { &m_flTargetScale[0], &m_flTargetScale[1], &m_flTargetScale[2] }; + float flScale[3] = { m_flScaleX, m_flScaleY, m_flScaleZ }; + float flLerpTime[3] = { m_flLerpTimeX, m_flLerpTimeY, m_flLerpTimeZ }; + float flGoalTime[3] = { m_flGoalTimeX, m_flGoalTimeY, m_flGoalTimeZ }; + bool *bRunning[3] = { &m_bRunningScale[0], &m_bRunningScale[1], &m_bRunningScale[2] }; + + for ( int i = 0; i < 3; i++ ) + { + if ( *flTargetScale[i] != flScale[i] ) + { + float deltaTime = (float)( gpGlobals->curtime - flGoalTime[i]) / flLerpTime[i]; + float flRemapVal = SimpleSplineRemapValClamped( deltaTime, 0.0f, 1.0f, *flTargetScale[i], flScale[i] ); + + *bRunning[i] = true; + + if ( deltaTime >= 1.0f ) + { + *flTargetScale[i] = flScale[i]; + *bRunning[i] = false; + } + + flVal[i] = flRemapVal; + m_flCurrentScale[i] = flVal[i]; + } + else + { + m_flCurrentScale[i] = m_flTargetScale[i]; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Scales the bones based on the current scales +//----------------------------------------------------------------------------- +void C_PropScalable::ApplyBoneMatrixTransform( matrix3x4_t& transform ) +{ + BaseClass::ApplyBoneMatrixTransform( transform ); + + // Find the scale for this frame + CalculateScale(); + + VectorScale( transform[0], m_flCurrentScale[0], transform[0] ); + VectorScale( transform[1], m_flCurrentScale[1], transform[1] ); + VectorScale( transform[2], m_flCurrentScale[2], transform[2] ); + + UpdateVisibility(); +} + +//----------------------------------------------------------------------------- +// Purpose: Ensures the render bounds match the scales +//----------------------------------------------------------------------------- +void C_PropScalable::GetRenderBounds( Vector &theMins, Vector &theMaxs ) +{ + BaseClass::GetRenderBounds( theMins, theMaxs ); + + // Find the scale for this frame + CalculateScale(); + + // Extend our render bounds to encompass the scaled object + theMins.x *= m_flCurrentScale[0]; + theMins.y *= m_flCurrentScale[1]; + theMins.z *= m_flCurrentScale[2]; + + theMaxs.x *= m_flCurrentScale[0]; + theMaxs.y *= m_flCurrentScale[1]; + theMaxs.z *= m_flCurrentScale[2]; + + Assert( theMins.IsValid() && theMaxs.IsValid() ); + +} diff --git a/sp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp b/sp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp new file mode 100644 index 00000000..66beface --- /dev/null +++ b/sp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp @@ -0,0 +1,133 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "c_prop_vehicle.h" +#include "c_vehicle_jeep.h" +#include "movevars_shared.h" +#include "view.h" +#include "flashlighteffect.h" +#include "c_baseplayer.h" +#include "c_te_effect_dispatch.h" +#include "hl2_vehicle_radar.h" +#include "usermessages.h" +#include "hud_radar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//============================================================================= +// +// Client-side Episodic Jeep (Jalopy) Class +// +class C_PropJeepEpisodic : public C_PropJeep +{ + + DECLARE_CLASS( C_PropJeepEpisodic, C_PropJeep ); + +public: + DECLARE_CLIENTCLASS(); + +public: + C_PropJeepEpisodic(); + + void OnEnteredVehicle( C_BasePlayer *pPlayer ); + void Simulate( void ); + +public: + int m_iNumRadarContacts; + Vector m_vecRadarContactPos[ RADAR_MAX_CONTACTS ]; + int m_iRadarContactType[ RADAR_MAX_CONTACTS ]; +}; +C_PropJeepEpisodic *g_pJalopy = NULL; + +IMPLEMENT_CLIENTCLASS_DT( C_PropJeepEpisodic, DT_CPropJeepEpisodic, CPropJeepEpisodic ) + //CNetworkVar( int, m_iNumRadarContacts ); + RecvPropInt( RECVINFO(m_iNumRadarContacts) ), + + //CNetworkArray( Vector, m_vecRadarContactPos, RADAR_MAX_CONTACTS ); + RecvPropArray( RecvPropVector(RECVINFO(m_vecRadarContactPos[0])), m_vecRadarContactPos ), + + //CNetworkArray( int, m_iRadarContactType, RADAR_MAX_CONTACTS ); + RecvPropArray( RecvPropInt( RECVINFO(m_iRadarContactType[0] ) ), m_iRadarContactType ), + +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void __MsgFunc_UpdateJalopyRadar(bf_read &msg) +{ + // Radar code here! + if( !GetHudRadar() ) + return; + + // Sometimes we update more quickly when we need to track something in high resolution. + // Usually we do not, so default to false. + GetHudRadar()->m_bUseFastUpdate = false; + + for( int i = 0 ; i < g_pJalopy->m_iNumRadarContacts ; i++ ) + { + if( g_pJalopy->m_iRadarContactType[i] == RADAR_CONTACT_DOG ) + { + GetHudRadar()->m_bUseFastUpdate = true; + break; + } + } + + float flContactTimeToLive; + + if( GetHudRadar()->m_bUseFastUpdate ) + { + flContactTimeToLive = RADAR_UPDATE_FREQUENCY_FAST; + } + else + { + flContactTimeToLive = RADAR_UPDATE_FREQUENCY; + } + + for( int i = 0 ; i < g_pJalopy->m_iNumRadarContacts ; i++ ) + { + GetHudRadar()->AddRadarContact( g_pJalopy->m_vecRadarContactPos[i], g_pJalopy->m_iRadarContactType[i], flContactTimeToLive ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +C_PropJeepEpisodic::C_PropJeepEpisodic() +{ + if( g_pJalopy == NULL ) + { + usermessages->HookMessage( "UpdateJalopyRadar", __MsgFunc_UpdateJalopyRadar ); + } + + g_pJalopy = this; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PropJeepEpisodic::Simulate( void ) +{ + // Keep trying to hook to the radar. + if( GetHudRadar() != NULL ) + { + // This is not our ideal long-term solution. This will only work if you only have + // one jalopy in a given level. The Jalopy and the Radar Screen are currently both + // assumed to be singletons. This is appropriate for EP2, however. (sjb) + GetHudRadar()->SetVehicle( this ); + } + + BaseClass::Simulate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PropJeepEpisodic::OnEnteredVehicle( C_BasePlayer *pPlayer ) +{ + BaseClass::OnEnteredVehicle( pPlayer ); +} diff --git a/sp/src/game/client/episodic/c_vort_charge_token.cpp b/sp/src/game/client/episodic/c_vort_charge_token.cpp new file mode 100644 index 00000000..fa769fa0 --- /dev/null +++ b/sp/src/game/client/episodic/c_vort_charge_token.cpp @@ -0,0 +1,600 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "cbase.h" +#include "particles_simple.h" +#include "citadel_effects_shared.h" +#include "particles_attractor.h" +#include "iefx.h" +#include "dlight.h" +#include "clienteffectprecachesystem.h" +#include "c_te_effect_dispatch.h" +#include "fx_quad.h" + +#include "c_ai_basenpc.h" + +// For material proxy +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" + +#define NUM_INTERIOR_PARTICLES 8 + +#define DLIGHT_RADIUS (150.0f) +#define DLIGHT_MINLIGHT (40.0f/255.0f) + +class C_NPC_Vortigaunt : public C_AI_BaseNPC +{ + DECLARE_CLASS( C_NPC_Vortigaunt, C_AI_BaseNPC ); + DECLARE_CLIENTCLASS(); + +public: + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void ClientThink( void ); + virtual void ReceiveMessage( int classID, bf_read &msg ); + +public: + bool m_bIsBlue; ///< wants to fade to blue + float m_flBlueEndFadeTime; ///< when to end fading from one skin to another + + bool m_bIsBlack; ///< wants to fade to black (networked) + float m_flBlackFade; ///< [0.00 .. 1.00] where 1.00 is all black. Locally interpolated. +}; + +IMPLEMENT_CLIENTCLASS_DT( C_NPC_Vortigaunt, DT_NPC_Vortigaunt, CNPC_Vortigaunt ) + RecvPropTime( RECVINFO(m_flBlueEndFadeTime ) ), + RecvPropBool( RECVINFO(m_bIsBlue) ), + RecvPropBool( RECVINFO(m_bIsBlack) ), +END_RECV_TABLE() + + +#define VORTIGAUNT_BLUE_FADE_TIME 2.25f // takes this long to fade from green to blue or back +#define VORT_BLACK_FADE_TIME 2.2f // time to interpolate up or down in fading to black + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : updateType - +//----------------------------------------------------------------------------- +void C_NPC_Vortigaunt::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + // start thinking if we need to fade. + if ( m_flBlackFade != (m_bIsBlack ? 1.0f : 0.0f) ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_NPC_Vortigaunt::ClientThink( void ) +{ + // Don't update if our frame hasn't moved forward (paused) + if ( gpGlobals->frametime <= 0.0f ) + return; + + if ( m_bIsBlack ) + { + // are we done? + if ( m_flBlackFade >= 1.0f ) + { + m_flBlackFade = 1.0f; + SetNextClientThink( CLIENT_THINK_NEVER ); + } + else // interpolate there + { + float lerpQuant = gpGlobals->frametime / VORT_BLACK_FADE_TIME; + m_flBlackFade += lerpQuant; + if ( m_flBlackFade > 1.0f ) + { + m_flBlackFade = 1.0f; + } + } + } + else + { + // are we done? + if ( m_flBlackFade <= 0.0f ) + { + m_flBlackFade = 0.0f; + SetNextClientThink( CLIENT_THINK_NEVER ); + } + else // interpolate there + { + float lerpQuant = gpGlobals->frametime / VORT_BLACK_FADE_TIME; + m_flBlackFade -= lerpQuant; + if ( m_flBlackFade < 0.0f ) + { + m_flBlackFade = 0.0f; + } + } + } +} + +// FIXME: Move to shared code! +#define VORTFX_ZAPBEAM 0 +#define VORTFX_ARMBEAM 1 + +//----------------------------------------------------------------------------- +// Purpose: Receive messages from the server +//----------------------------------------------------------------------------- +void C_NPC_Vortigaunt::ReceiveMessage( int classID, bf_read &msg ) +{ + // Is the message for a sub-class? + if ( classID != GetClientClass()->m_ClassID ) + { + BaseClass::ReceiveMessage( classID, msg ); + return; + } + + int messageType = msg.ReadByte(); + switch( messageType ) + { + case VORTFX_ZAPBEAM: + { + // Find our attachment point + unsigned char nAttachment = msg.ReadByte(); + + // Get our attachment position + Vector vecStart; + QAngle vecAngles; + GetAttachment( nAttachment, vecStart, vecAngles ); + + // Get the final position we'll strike + Vector vecEndPos; + msg.ReadBitVec3Coord( vecEndPos ); + + // Place a beam between the two points + CNewParticleEffect *pEffect = ParticleProp()->Create( "vortigaunt_beam", PATTACH_POINT_FOLLOW, nAttachment ); + if ( pEffect ) + { + pEffect->SetControlPoint( 0, vecStart ); + pEffect->SetControlPoint( 1, vecEndPos ); + } + } + break; + + case VORTFX_ARMBEAM: + { + int nIndex = msg.ReadLong(); + C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( ClientEntityList().EntIndexToHandle( nIndex ) ); + + if ( pEnt ) + { + unsigned char nAttachment = msg.ReadByte(); + Vector vecEndPos; + msg.ReadBitVec3Coord( vecEndPos ); + + Vector vecNormal; + msg.ReadBitVec3Normal( vecNormal ); + + CNewParticleEffect *pEffect = pEnt->ParticleProp()->Create( "vortigaunt_beam_charge", PATTACH_POINT_FOLLOW, nAttachment ); + if ( pEffect ) + { + // Set the control point's angles to be the surface normal we struct + Vector vecRight, vecUp; + VectorVectors( vecNormal, vecRight, vecUp ); + pEffect->SetControlPointOrientation( 1, vecNormal, vecRight, vecUp ); + pEffect->SetControlPoint( 1, vecEndPos ); + } + } + } + break; + default: + AssertMsg1( false, "Received unknown message %d", messageType); + } +} + +class C_VortigauntChargeToken : public C_BaseEntity +{ + DECLARE_CLASS( C_VortigauntChargeToken, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + +public: + virtual void UpdateOnRemove( void ); + virtual void ClientThink( void ); + virtual void NotifyShouldTransmit( ShouldTransmitState_t state ); + virtual void OnDataChanged( DataUpdateType_t type ); + + // For RecvProxy handlers + float m_flFadeOutTime; + float m_flFadeOutStart; + +private: + bool SetupEmitters( void ); + + bool m_bFadeOut; + CNewParticleEffect *m_hEffect; + dlight_t *m_pDLight; +}; + +void RecvProxy_FadeOutDuration( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_VortigauntChargeToken *pVortToken = (C_VortigauntChargeToken *) pStruct; + Assert( pOut == &pVortToken->m_flFadeOutTime ); + + pVortToken->m_flFadeOutStart = gpGlobals->curtime; + pVortToken->m_flFadeOutTime = ( pData->m_Value.m_Float - gpGlobals->curtime ); +} + +IMPLEMENT_CLIENTCLASS_DT( C_VortigauntChargeToken, DT_VortigauntChargeToken, CVortigauntChargeToken ) + RecvPropBool( RECVINFO( m_bFadeOut ) ), +END_RECV_TABLE() + +void C_VortigauntChargeToken::UpdateOnRemove( void ) +{ + if ( m_hEffect ) + { + m_hEffect->StopEmission(); + m_hEffect = NULL; + } + + if ( m_pDLight != NULL ) + { + m_pDLight->die = gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Change our transmission state +//----------------------------------------------------------------------------- +void C_VortigauntChargeToken::NotifyShouldTransmit( ShouldTransmitState_t state ) +{ + BaseClass::NotifyShouldTransmit( state ); + + // Turn off + if ( state == SHOULDTRANSMIT_END ) + { + if ( m_hEffect ) + { + m_hEffect->StopEmission(); + m_hEffect = NULL; + } + } + + // Turn on + if ( state == SHOULDTRANSMIT_START ) + { + m_hEffect = ParticleProp()->Create( "vortigaunt_charge_token", PATTACH_ABSORIGIN_FOLLOW ); + m_hEffect->SetControlPointEntity( 0, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_VortigauntChargeToken::OnDataChanged( DataUpdateType_t type ) +{ + if ( m_bFadeOut ) + { + if ( m_hEffect ) + { + m_hEffect->StopEmission(); + m_hEffect = NULL; + } + } + + BaseClass::OnDataChanged( type ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_VortigauntChargeToken::ClientThink( void ) +{ + // + // -- DLight + // + + if ( m_pDLight != NULL ) + { + m_pDLight->origin = GetAbsOrigin(); + m_pDLight->radius = DLIGHT_RADIUS; + } +} + +//============================================================================= +// +// Dispel Effect +// +//============================================================================= + +class C_VortigauntEffectDispel : public C_BaseEntity +{ + DECLARE_CLASS( C_VortigauntEffectDispel, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + +public: + virtual void UpdateOnRemove( void ); + virtual void ClientThink( void ); + virtual void NotifyShouldTransmit( ShouldTransmitState_t state ); + virtual void OnDataChanged( DataUpdateType_t type ); + + // For RecvProxy handlers + float m_flFadeOutTime; + float m_flFadeOutStart; + +private: + bool SetupEmitters( void ); + + CNewParticleEffect *m_hEffect; + bool m_bFadeOut; + dlight_t *m_pDLight; +}; + +void RecvProxy_DispelFadeOutDuration( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_VortigauntEffectDispel *pVortToken = (C_VortigauntEffectDispel *) pStruct; + Assert( pOut == &pVortToken->m_flFadeOutTime ); + + pVortToken->m_flFadeOutStart = gpGlobals->curtime; + pVortToken->m_flFadeOutTime = ( pData->m_Value.m_Float - gpGlobals->curtime ); +} + +IMPLEMENT_CLIENTCLASS_DT( C_VortigauntEffectDispel, DT_VortigauntEffectDispel, CVortigauntEffectDispel ) + RecvPropBool( RECVINFO( m_bFadeOut ) ), +END_RECV_TABLE() + +void C_VortigauntEffectDispel::UpdateOnRemove( void ) +{ + if ( m_hEffect ) + { + m_hEffect->StopEmission(); + m_hEffect = NULL; + } + + if ( m_pDLight != NULL ) + { + m_pDLight->die = gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_VortigauntEffectDispel::OnDataChanged( DataUpdateType_t type ) +{ + if ( m_bFadeOut ) + { + if ( m_hEffect ) + { + m_hEffect->StopEmission(); + m_hEffect = NULL; + } + } + + BaseClass::OnDataChanged( type ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_VortigauntEffectDispel::NotifyShouldTransmit( ShouldTransmitState_t state ) +{ + BaseClass::NotifyShouldTransmit( state ); + + // Turn off + if ( state == SHOULDTRANSMIT_END ) + { + if ( m_hEffect ) + { + m_hEffect->StopEmission(); + m_hEffect = NULL; + } + } + + // Turn on + if ( state == SHOULDTRANSMIT_START ) + { + m_hEffect = ParticleProp()->Create( "vortigaunt_hand_glow", PATTACH_ABSORIGIN_FOLLOW ); + m_hEffect->SetControlPointEntity( 0, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Create our emitter +//----------------------------------------------------------------------------- +bool C_VortigauntEffectDispel::SetupEmitters( void ) +{ + m_pDLight = NULL; + +#ifndef _X360 + m_pDLight = effects->CL_AllocDlight ( index ); + m_pDLight->origin = GetAbsOrigin(); + m_pDLight->color.r = 64; + m_pDLight->color.g = 255; + m_pDLight->color.b = 64; + m_pDLight->radius = 0; + m_pDLight->minlight = DLIGHT_MINLIGHT; + m_pDLight->die = FLT_MAX; +#endif // _X360 + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_VortigauntEffectDispel::ClientThink( void ) +{ + if ( m_pDLight != NULL ) + { + m_pDLight->origin = GetAbsOrigin(); + m_pDLight->radius = DLIGHT_RADIUS; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void DispelCallback( const CEffectData &data ) +{ + // Kaboom! + Vector startPos = data.m_vOrigin + Vector(0,0,16); + Vector endPos = data.m_vOrigin + Vector(0,0,-64); + + trace_t tr; + UTIL_TraceLine( startPos, endPos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + if ( tr.fraction < 1.0f ) + { + //Add a ripple quad to the surface + FX_AddQuad( tr.endpos + ( tr.plane.normal * 8.0f ), + Vector( 0, 0, 1 ), + 64.0f, + 600.0f, + 0.8f, + 1.0f, // start alpha + 0.0f, // end alpha + 0.3f, + random->RandomFloat( 0, 360 ), + 0.0f, + Vector( 0.5f, 1.0f, 0.5f ), + 0.75f, + "effects/ar2_altfire1b", + (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA|FXQUAD_COLOR_FADE) ); + + //Add a ripple quad to the surface + FX_AddQuad( tr.endpos + ( tr.plane.normal * 8.0f ), + Vector( 0, 0, 1 ), + 16.0f, + 300.0f, + 0.9f, + 1.0f, // start alpha + 0.0f, // end alpha + 0.9f, + random->RandomFloat( 0, 360 ), + 0.0f, + Vector( 0.5f, 1.0f, 0.5f ), + 1.25f, + "effects/rollerglow", + (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) ); + } +} + +DECLARE_CLIENT_EFFECT( "VortDispel", DispelCallback ); + +//----------------------------------------------------------------------------- +// Purpose: Used for emissive lightning layer on vort +//----------------------------------------------------------------------------- +class CVortEmissiveProxy : public CEntityMaterialProxy +{ +public: + CVortEmissiveProxy( void ); + virtual ~CVortEmissiveProxy( void ); + virtual bool Init( IMaterial *pMaterial, KeyValues* pKeyValues ); + virtual void OnBind( C_BaseEntity *pC_BaseEntity ); + virtual IMaterial * GetMaterial(); + +private: + + IMaterialVar *m_pMatEmissiveStrength; + IMaterialVar *m_pMatDetailBlendStrength; +}; + +//----------------------------------------------------------------------------- +CVortEmissiveProxy::CVortEmissiveProxy( void ) +{ + m_pMatEmissiveStrength = NULL; + m_pMatDetailBlendStrength = NULL; +} + +CVortEmissiveProxy::~CVortEmissiveProxy( void ) +{ + // Do nothing +} + +//----------------------------------------------------------------------------- +bool CVortEmissiveProxy::Init( IMaterial *pMaterial, KeyValues* pKeyValues ) +{ + Assert( pMaterial ); + + // Need to get the material var + bool bFound; + m_pMatEmissiveStrength = pMaterial->FindVar( "$emissiveblendstrength", &bFound ); + + if ( bFound ) + { + // Optional + bool bFound2; + m_pMatDetailBlendStrength = pMaterial->FindVar( "$detailblendfactor", &bFound2 ); + } + + return bFound; +} + +//----------------------------------------------------------------------------- +void CVortEmissiveProxy::OnBind( C_BaseEntity *pEnt ) +{ + C_NPC_Vortigaunt *pVort = dynamic_cast(pEnt); + + float flBlendValue; + + if (pVort) + { + // do we need to crossfade? + if (gpGlobals->curtime < pVort->m_flBlueEndFadeTime) + { + // will be 0 when fully faded and 1 when not faded at all: + float fadeRatio = (pVort->m_flBlueEndFadeTime - gpGlobals->curtime) / VORTIGAUNT_BLUE_FADE_TIME; + if (pVort->m_bIsBlue) + { + fadeRatio = 1.0f - fadeRatio; + } + flBlendValue = clamp( fadeRatio, 0.0f, 1.0f ); + } + else // no crossfade + { + flBlendValue = pVort->m_bIsBlue ? 1.0f : 0.0f; + } + + // ALEX VLACHOS: + // The following variable varies on [0 .. 1]. 0.0 means the vort wants to be his normal + // color. 1.0 means he wants to be all black. It is interpolated in the + // C_NPC_Vortigaunt::ClientThink() function. + // + // pVort->m_flBlackFade + } + else + { // if you bind this proxy to anything non-vort (eg a ragdoll) it's always green + flBlendValue = 0.0f; + } + + + /* + // !!! Change me !!! I'm using a clamped sin wave for debugging + float flBlendValue = sinf( gpGlobals->curtime * 4.0f ) * 0.75f + 0.25f; + + // Clamp 0-1 + flBlendValue = ( flBlendValue < 0.0f ) ? 0.0f : ( flBlendValue > 1.0f ) ? 1.0f : flBlendValue; + */ + + if( m_pMatEmissiveStrength != NULL ) + { + m_pMatEmissiveStrength->SetFloatValue( flBlendValue ); + } + + if( m_pMatDetailBlendStrength != NULL ) + { + m_pMatDetailBlendStrength->SetFloatValue( flBlendValue ); + } +} + +//----------------------------------------------------------------------------- +IMaterial *CVortEmissiveProxy::GetMaterial() +{ + if ( m_pMatEmissiveStrength != NULL ) + return m_pMatEmissiveStrength->GetOwningMaterial(); + else if ( m_pMatDetailBlendStrength != NULL ) + return m_pMatDetailBlendStrength->GetOwningMaterial(); + else + return NULL; +} + +EXPOSE_INTERFACE( CVortEmissiveProxy, IMaterialProxy, "VortEmissive" IMATERIAL_PROXY_INTERFACE_VERSION ); diff --git a/sp/src/game/client/episodic/c_weapon_hopwire.cpp b/sp/src/game/client/episodic/c_weapon_hopwire.cpp new file mode 100644 index 00000000..942ef0c0 --- /dev/null +++ b/sp/src/game/client/episodic/c_weapon_hopwire.cpp @@ -0,0 +1,421 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "basegrenade_shared.h" +#include "fx_interpvalue.h" +#include "fx_envelope.h" +#include "materialsystem/imaterialvar.h" +#include "particles_simple.h" +#include "particles_attractor.h" + +// FIXME: Move out +extern void DrawSpriteTangentSpace( const Vector &vecOrigin, float flWidth, float flHeight, color32 color ); + +#define EXPLOSION_DURATION 3.0f + +//----------------------------------------------------------------------------- +// Explosion effect for hopwire +//----------------------------------------------------------------------------- + +class C_HopwireExplosion : public C_EnvelopeFX +{ + typedef C_EnvelopeFX BaseClass; + +public: + C_HopwireExplosion( void ) : + m_hOwner( NULL ) + { + m_FXCoreScale.SetAbsolute( 0.0f ); + m_FXCoreAlpha.SetAbsolute( 0.0f ); + } + + virtual void Update( void ); + virtual int DrawModel( int flags ); + virtual void GetRenderBounds( Vector& mins, Vector& maxs ); + + bool SetupEmitters( void ); + void AddParticles( void ); + void SetOwner( C_BaseEntity *pOwner ); + void StartExplosion( void ); + void StopExplosion( void ); + void StartPreExplosion( void ); + +private: + CInterpolatedValue m_FXCoreScale; + CInterpolatedValue m_FXCoreAlpha; + + CSmartPtr m_pSimpleEmitter; + CSmartPtr m_pAttractorEmitter; + + TimedEvent m_ParticleTimer; + + CHandle m_hOwner; +}; + +//----------------------------------------------------------------------------- +// Purpose: Setup the emitters we'll be using +//----------------------------------------------------------------------------- +bool C_HopwireExplosion::SetupEmitters( void ) +{ + // Setup the basic core emitter + if ( m_pSimpleEmitter.IsValid() == false ) + { + m_pSimpleEmitter = CSimpleEmitter::Create( "hopwirecore" ); + + if ( m_pSimpleEmitter.IsValid() == false ) + return false; + } + + // Setup the attractor emitter + if ( m_pAttractorEmitter.IsValid() == false ) + { + m_pAttractorEmitter = CParticleAttractor::Create( GetRenderOrigin(), "hopwireattractor" ); + + if ( m_pAttractorEmitter.IsValid() == false ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::AddParticles( void ) +{ + // Make sure the emitters are setup properly + if ( SetupEmitters() == false ) + return; + + float tempDelta = gpGlobals->frametime; + while( m_ParticleTimer.NextEvent( tempDelta ) ) + { + // ======================== + // Attracted dust particles + // ======================== + + // Update our attractor point + m_pAttractorEmitter->SetAttractorOrigin( GetRenderOrigin() ); + + Vector offset; + SimpleParticle *sParticle; + + offset = GetRenderOrigin() + RandomVector( -256.0f, 256.0f ); + + sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_Fleck_Cement[0], offset ); + + if ( sParticle == NULL ) + return; + + sParticle->m_vecVelocity = Vector(0,0,8); + sParticle->m_flDieTime = 0.5f; + sParticle->m_flLifetime = 0.0f; + + sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); + sParticle->m_flRollDelta = 1.0f; + + float alpha = random->RandomFloat( 128.0f, 200.0f ); + + sParticle->m_uchColor[0] = alpha; + sParticle->m_uchColor[1] = alpha; + sParticle->m_uchColor[2] = alpha; + sParticle->m_uchStartAlpha = alpha; + sParticle->m_uchEndAlpha = alpha; + + sParticle->m_uchStartSize = random->RandomInt( 1, 4 ); + sParticle->m_uchEndSize = 0; + + // ======================== + // Core effects + // ======================== + + // Reset our sort origin + m_pSimpleEmitter->SetSortOrigin( GetRenderOrigin() ); + + // Base of the core effect + sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/strider_muzzle" ), GetRenderOrigin() ); + + if ( sParticle == NULL ) + return; + + sParticle->m_vecVelocity = vec3_origin; + sParticle->m_flDieTime = 0.2f; + sParticle->m_flLifetime = 0.0f; + + sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); + sParticle->m_flRollDelta = 4.0f; + + alpha = random->RandomInt( 32, 200 ); + + sParticle->m_uchColor[0] = alpha; + sParticle->m_uchColor[1] = alpha; + sParticle->m_uchColor[2] = alpha; + sParticle->m_uchStartAlpha = 0; + sParticle->m_uchEndAlpha = alpha; + + sParticle->m_uchStartSize = 255; + sParticle->m_uchEndSize = 0; + + // Make sure we encompass the complete particle here! + m_pSimpleEmitter->SetParticleCullRadius( sParticle->m_uchEndSize ); + + // ========================= + // Dust ring effect + // ========================= + + if ( random->RandomInt( 0, 5 ) != 1 ) + return; + + Vector vecDustColor; + vecDustColor.x = 0.35f; + vecDustColor.y = 0.3f; + vecDustColor.z = 0.25f; + + Vector color; + + int numRingSprites = 8; + float yaw; + Vector forward, vRight, vForward; + + vForward = Vector( 0, 1, 0 ); + vRight = Vector( 1, 0, 0 ); + + float yawOfs = random->RandomFloat( 0, 359 ); + + for ( int i = 0; i < numRingSprites; i++ ) + { + yaw = ( (float) i / (float) numRingSprites ) * 360.0f; + yaw += yawOfs; + + forward = ( vRight * sin( DEG2RAD( yaw) ) ) + ( vForward * cos( DEG2RAD( yaw ) ) ); + VectorNormalize( forward ); + + trace_t tr; + + UTIL_TraceLine( GetRenderOrigin(), GetRenderOrigin()+(Vector(0, 0, -1024)), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + offset = ( RandomVector( -4.0f, 4.0f ) + tr.endpos ) + ( forward * 512.0f ); + + sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[random->RandomInt(0,1)], offset ); + + if ( sParticle != NULL ) + { + sParticle->m_flLifetime = 0.0f; + sParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); + + sParticle->m_vecVelocity = forward * -random->RandomFloat( 1000, 1500 ); + sParticle->m_vecVelocity[2] += 128.0f; + + #if __EXPLOSION_DEBUG + debugoverlay->AddLineOverlay( m_vecOrigin, m_vecOrigin + sParticle->m_vecVelocity, 255, 0, 0, false, 3 ); + #endif + + sParticle->m_uchColor[0] = vecDustColor.x * 255.0f; + sParticle->m_uchColor[1] = vecDustColor.y * 255.0f; + sParticle->m_uchColor[2] = vecDustColor.z * 255.0f; + + sParticle->m_uchStartSize = random->RandomInt( 32, 128 ); + sParticle->m_uchEndSize = 200; + + sParticle->m_uchStartAlpha = random->RandomFloat( 16, 64 ); + sParticle->m_uchEndAlpha = 0; + + sParticle->m_flRoll = random->RandomInt( 0, 360 ); + sParticle->m_flRollDelta = random->RandomFloat( -16.0f, 16.0f ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOwner - +//----------------------------------------------------------------------------- +void C_HopwireExplosion::SetOwner( C_BaseEntity *pOwner ) +{ + m_hOwner = pOwner; +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the internal values for the effect +//----------------------------------------------------------------------------- +void C_HopwireExplosion::Update( void ) +{ + if ( m_hOwner ) + { + SetRenderOrigin( m_hOwner->GetRenderOrigin() ); + } + + BaseClass::Update(); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates and renders all effects +//----------------------------------------------------------------------------- +int C_HopwireExplosion::DrawModel( int flags ) +{ + AddParticles(); + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->Flush(); + UpdateRefractTexture(); + + IMaterial *pMat = materials->FindMaterial( "effects/strider_pinch_dudv", TEXTURE_GROUP_CLIENT_EFFECTS ); + + float refract = m_FXCoreAlpha.Interp( gpGlobals->curtime ); + float scale = m_FXCoreScale.Interp( gpGlobals->curtime ); + + IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL ); + pVar->SetFloatValue( refract ); + + pRenderContext->Bind( pMat, (IClientRenderable*)this ); + + float sin1 = sinf( gpGlobals->curtime * 10 ); + float sin2 = sinf( gpGlobals->curtime ); + + float scaleY = ( sin1 * sin2 ) * 32.0f; + float scaleX = (sin2 * sin2) * 32.0f; + + // FIXME: The ball needs to sort properly at all times + static color32 white = {255,255,255,255}; + DrawSpriteTangentSpace( GetRenderOrigin() + ( CurrentViewForward() * 128.0f ), scale+scaleX, scale+scaleY, white ); + + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the bounds relative to the origin (render bounds) +//----------------------------------------------------------------------------- +void C_HopwireExplosion::GetRenderBounds( Vector& mins, Vector& maxs ) +{ + float scale = m_FXCoreScale.Interp( gpGlobals->curtime ); + + mins.Init( -scale, -scale, -scale ); + maxs.Init( scale, scale, scale ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::StartExplosion( void ) +{ + m_FXCoreScale.Init( 300.0f, 500.0f, 2.0f, INTERP_SPLINE ); + m_FXCoreAlpha.Init( 0.0f, 0.1f, 1.5f, INTERP_SPLINE ); + + // Particle timer + m_ParticleTimer.Init( 60 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::StopExplosion( void ) +{ + m_FXCoreAlpha.InitFromCurrent( 0.0f, 1.0f, INTERP_SPLINE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::StartPreExplosion( void ) +{ +} + +//----------------------------------------------------------------------------- +// Hopwire client class +//----------------------------------------------------------------------------- + +class C_GrenadeHopwire : public C_BaseGrenade +{ + DECLARE_CLASS( C_GrenadeHopwire, C_BaseGrenade ); + DECLARE_CLIENTCLASS(); + +public: + C_GrenadeHopwire( void ); + + virtual int DrawModel( int flags ); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void ReceiveMessage( int classID, bf_read &msg ); + +private: + + C_HopwireExplosion m_ExplosionEffect; // Explosion effect information and drawing +}; + +IMPLEMENT_CLIENTCLASS_DT( C_GrenadeHopwire, DT_GrenadeHopwire, CGrenadeHopwire ) +END_RECV_TABLE() + +#define HOPWIRE_START_EXPLOSION 0 +#define HOPWIRE_STOP_EXPLOSION 1 +#define HOPWIRE_START_PRE_EXPLOSION 2 + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +C_GrenadeHopwire::C_GrenadeHopwire( void ) +{ + m_ExplosionEffect.SetActive( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Receive messages from the server +// Input : classID - class to receive the message +// &msg - message in question +//----------------------------------------------------------------------------- +void C_GrenadeHopwire::ReceiveMessage( int classID, bf_read &msg ) +{ + if ( classID != GetClientClass()->m_ClassID ) + { + // Message is for subclass + BaseClass::ReceiveMessage( classID, msg ); + return; + } + + int messageType = msg.ReadByte(); + switch( messageType ) + { + case HOPWIRE_START_EXPLOSION: + { + m_ExplosionEffect.SetActive(); + m_ExplosionEffect.SetOwner( this ); + m_ExplosionEffect.StartExplosion(); + } + break; + case HOPWIRE_STOP_EXPLOSION: + { + m_ExplosionEffect.StopExplosion(); + } + break; + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : updateType - +//----------------------------------------------------------------------------- +void C_GrenadeHopwire::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + m_ExplosionEffect.Update(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flags - +//----------------------------------------------------------------------------- +int C_GrenadeHopwire::DrawModel( int flags ) +{ + if ( m_ExplosionEffect.IsActive() ) + return 1; + + return BaseClass::DrawModel( flags ); +} + diff --git a/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp b/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp new file mode 100644 index 00000000..a2b59bbc --- /dev/null +++ b/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp @@ -0,0 +1,464 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Episodic screen-space effects +// + +#include "cbase.h" +#include "ScreenSpaceEffects.h" +#include "rendertexture.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imaterialvar.h" +#include "cdll_client_int.h" +#include "materialsystem/itexture.h" +#include "KeyValues.h" +#include "clienteffectprecachesystem.h" + +#include "episodic_screenspaceeffects.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +#define STUN_TEXTURE "_rt_FullFrameFB2" +#else +#define STUN_TEXTURE "_rt_WaterRefraction" +#endif + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CStunEffect::Init( void ) +{ + m_flDuration = 0.0f; + m_flFinishTime = 0.0f; + m_bUpdateView = true; + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE ); + m_EffectMaterial.Init( "__stuneffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues ); + m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS ); +} + +void CStunEffect::Shutdown( void ) +{ + m_EffectMaterial.Shutdown(); + m_StunTexture.Shutdown(); +} + +//------------------------------------------------------------------------------ +// Purpose: Pick up changes in our parameters +//------------------------------------------------------------------------------ +void CStunEffect::SetParameters( KeyValues *params ) +{ + if( params->FindKey( "duration" ) ) + { + m_flDuration = params->GetFloat( "duration" ); + m_flFinishTime = gpGlobals->curtime + m_flDuration; + m_bUpdateView = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CStunEffect::Render( int x, int y, int w, int h ) +{ + // Make sure we're ready to play this effect + if ( m_flFinishTime < gpGlobals->curtime ) + return; + + CMatRenderContextPtr pRenderContext( materials ); + + // Set ourselves to the proper rendermode + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + + // Draw the texture if we're using it + if ( m_bUpdateView ) + { + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + m_bUpdateView = false; + } + + float flEffectPerc = ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration; + + float viewOffs = ( flEffectPerc * 32.0f ) * ( cos( gpGlobals->curtime * 40.0f ) * sin( gpGlobals->curtime * 17.0f ) ); + float vX = x + viewOffs; + + if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 ) + { + if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) + { + m_EffectMaterial->ColorModulate( 1.0f, 1.0f, 1.0f ); + } + else + { + // This is a stupid fix, but I don't have time to do a cleaner implementation. Since + // the introblur.vmt material uses unlit generic, it will tone map, so I need to undo the tone mapping + // using color modulate. The proper fix would be to use a different material type that + // supports alpha blending but not tone mapping, which I don't think exists. Whatever. This works when + // the tone mapping scalar is less than 1.0, which it is in the cases it's used in game. + float flUnTonemap = pow( 1.0f / pRenderContext->GetToneMappingScaleLinear().x, 1.0f / 2.2f ); + m_EffectMaterial->ColorModulate( flUnTonemap, flUnTonemap, flUnTonemap ); + } + + // Set alpha blend value + float flOverlayAlpha = clamp( ( 150.0f / 255.0f ) * flEffectPerc, 0.0f, 1.0f ); + m_EffectMaterial->AlphaModulate( flOverlayAlpha ); + + // Draw full screen alpha-blended quad + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, 0, 0, w, h, + vX, 0, (m_StunTexture->GetActualWidth()-1)+vX, (m_StunTexture->GetActualHeight()-1), + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + } + + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + + // Restore our state + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PopMatrix(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); +} + +// ================================================================================================================ +// +// Ep 1. Intro blur +// +// ================================================================================================================ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEP1IntroEffect::Init( void ) +{ + m_flDuration = 0.0f; + m_flFinishTime = 0.0f; + m_bUpdateView = true; + m_bFadeOut = false; + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE ); + m_EffectMaterial.Init( "__ep1introeffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues ); + m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS ); +} + +void CEP1IntroEffect::Shutdown( void ) +{ + m_EffectMaterial.Shutdown(); + m_StunTexture.Shutdown(); +} + + +//------------------------------------------------------------------------------ +// Purpose: Pick up changes in our parameters +//------------------------------------------------------------------------------ +void CEP1IntroEffect::SetParameters( KeyValues *params ) +{ + if( params->FindKey( "duration" ) ) + { + m_flDuration = params->GetFloat( "duration" ); + m_flFinishTime = gpGlobals->curtime + m_flDuration; + } + + if( params->FindKey( "fadeout" ) ) + { + m_bFadeOut = ( params->GetInt( "fadeout" ) == 1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the alpha value depending on various factors and time +//----------------------------------------------------------------------------- +inline unsigned char CEP1IntroEffect::GetFadeAlpha( void ) +{ + // Find our percentage between fully "on" and "off" in the pulse range + float flEffectPerc = ( m_flDuration == 0.0f ) ? 0.0f : ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration; + flEffectPerc = clamp( flEffectPerc, 0.0f, 1.0f ); + + if ( m_bFadeOut ) + { + // HDR requires us to be more subtle, or we get uber-brightening + if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + return (unsigned char) clamp( 50.0f * flEffectPerc, 0.0f, 50.0f ); + + // Non-HDR + return (unsigned char) clamp( 64.0f * flEffectPerc, 0.0f, 64.0f ); + } + else + { + // HDR requires us to be more subtle, or we get uber-brightening + if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + return (unsigned char) clamp( 64.0f * flEffectPerc, 50.0f, 64.0f ); + + // Non-HDR + return (unsigned char) clamp( 128.0f * flEffectPerc, 64.0f, 128.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CEP1IntroEffect::Render( int x, int y, int w, int h ) +{ + if ( ( m_flFinishTime == 0 ) || ( IsEnabled() == false ) ) + return; + + CMatRenderContextPtr pRenderContext( materials ); + + // Set ourselves to the proper rendermode + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + + // Draw the texture if we're using it + if ( m_bUpdateView ) + { + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + m_bUpdateView = false; + } + + byte overlaycolor[4] = { 255, 255, 255, 0 }; + + // Get our fade value depending on our fade duration + overlaycolor[3] = GetFadeAlpha(); + if ( g_pMaterialSystemHardwareConfig->UsesSRGBCorrectBlending() ) + { + // For DX10 cards, alpha blending happens in linear space, so try to adjust by hacking alpha to 50% + overlaycolor[3] *= 0.7f; + } + + // Disable overself if we're done fading out + if ( m_bFadeOut && overlaycolor[3] == 0 ) + { + // Takes effect next frame (we don't want to hose our matrix stacks here) + g_pScreenSpaceEffects->DisableScreenSpaceEffect( "episodic_intro" ); + m_bUpdateView = true; + } + + // Calculate some wavey noise to jitter the view by + float vX = 2.0f * -fabs( cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 6.0 ) ); + float vY = 2.0f * cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 5.0 ); + + // Scale percentage + float flScalePerc = 0.02f + ( 0.01f * cosf( gpGlobals->curtime * 2.0f ) * cosf( gpGlobals->curtime * 0.5f ) ); + + // Scaled offsets for the UVs (as texels) + float flUOffset = ( m_StunTexture->GetActualWidth() - 1 ) * flScalePerc * 0.5f; + float flVOffset = ( m_StunTexture->GetActualHeight() - 1 ) * flScalePerc * 0.5f; + + // New UVs with scaling offsets + float flU1 = flUOffset; + float flU2 = ( m_StunTexture->GetActualWidth() - 1 ) - flUOffset; + float flV1 = flVOffset; + float flV2 = ( m_StunTexture->GetActualHeight() - 1 ) - flVOffset; + + // Draw the "zoomed" overlay + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, vX, vY, w, h, + flU1, flV1, + flU2, flV2, + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + + render->ViewDrawFade( overlaycolor, m_EffectMaterial ); + + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + + // Restore our state + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PopMatrix(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); +} + +// ================================================================================================================ +// +// Ep 2. Groggy-player view +// +// ================================================================================================================ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEP2StunEffect::Init( void ) +{ + m_flDuration = 0.0f; + m_flFinishTime = 0.0f; + m_bUpdateView = true; + m_bFadeOut = false; + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE ); + m_EffectMaterial.Init( "__ep2stuneffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues ); + m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS ); +} + +void CEP2StunEffect::Shutdown( void ) +{ + m_EffectMaterial.Shutdown(); + m_StunTexture.Shutdown(); +} + +//------------------------------------------------------------------------------ +// Purpose: Pick up changes in our parameters +//------------------------------------------------------------------------------ +void CEP2StunEffect::SetParameters( KeyValues *params ) +{ + if( params->FindKey( "duration" ) ) + { + m_flDuration = params->GetFloat( "duration" ); + m_flFinishTime = gpGlobals->curtime + m_flDuration; + } + + if( params->FindKey( "fadeout" ) ) + { + m_bFadeOut = ( params->GetInt( "fadeout" ) == 1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the alpha value depending on various factors and time +//----------------------------------------------------------------------------- +inline unsigned char CEP2StunEffect::GetFadeAlpha( void ) +{ + // Find our percentage between fully "on" and "off" in the pulse range + float flEffectPerc = ( m_flDuration == 0.0f ) ? 0.0f : ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration; + flEffectPerc = clamp( flEffectPerc, 0.0f, 1.0f ); + + if ( m_bFadeOut ) + { + // HDR requires us to be more subtle, or we get uber-brightening + if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + return (unsigned char) clamp( 50.0f * flEffectPerc, 0.0f, 50.0f ); + + // Non-HDR + return (unsigned char) clamp( 64.0f * flEffectPerc, 0.0f, 64.0f ); + } + else + { + // HDR requires us to be more subtle, or we get uber-brightening + if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + return (unsigned char) clamp( 164.0f * flEffectPerc, 128.0f, 164.0f ); + + // Non-HDR + return (unsigned char) clamp( 164.0f * flEffectPerc, 128.0f, 164.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CEP2StunEffect::Render( int x, int y, int w, int h ) +{ + if ( ( m_flFinishTime == 0 ) || ( IsEnabled() == false ) ) + return; + + CMatRenderContextPtr pRenderContext( materials ); + + // Set ourselves to the proper rendermode + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + + if ( m_bUpdateView ) + { + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + m_bUpdateView = false; + } + + byte overlaycolor[4] = { 255, 255, 255, 0 }; + + // Get our fade value depending on our fade duration + overlaycolor[3] = GetFadeAlpha(); + + // Disable overself if we're done fading out + if ( m_bFadeOut && overlaycolor[3] == 0 ) + { + // Takes effect next frame (we don't want to hose our matrix stacks here) + g_pScreenSpaceEffects->DisableScreenSpaceEffect( "ep2_groggy" ); + m_bUpdateView = true; + } + + // Calculate some wavey noise to jitter the view by + float vX = 4.0f * cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 6.0 ); + float vY = 2.0f * cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 5.0 ); + + float flBaseScale = 0.2f + 0.005f * sinf( gpGlobals->curtime * 4.0f ); + + // Scale percentage + float flScalePerc = flBaseScale + ( 0.01f * cosf( gpGlobals->curtime * 2.0f ) * cosf( gpGlobals->curtime * 0.5f ) ); + + // Scaled offsets for the UVs (as texels) + float flUOffset = ( m_StunTexture->GetActualWidth() - 1 ) * flScalePerc * 0.5f; + float flVOffset = ( m_StunTexture->GetActualHeight() - 1 ) * flScalePerc * 0.5f; + + // New UVs with scaling offsets + float flU1 = flUOffset; + float flU2 = ( m_StunTexture->GetActualWidth() - 1 ) - flUOffset; + float flV1 = flVOffset; + float flV2 = ( m_StunTexture->GetActualHeight() - 1 ) - flVOffset; + + // Draw the "zoomed" overlay + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, vX, vY, w, h, + flU1, flV1, + flU2, flV2, + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + + render->ViewDrawFade( overlaycolor, m_EffectMaterial ); + + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + + // Restore our state + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PopMatrix(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); +} diff --git a/sp/src/game/client/episodic/episodic_screenspaceeffects.h b/sp/src/game/client/episodic/episodic_screenspaceeffects.h new file mode 100644 index 00000000..ff5bc716 --- /dev/null +++ b/sp/src/game/client/episodic/episodic_screenspaceeffects.h @@ -0,0 +1,119 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef EPISODIC_SCREENSPACEEFFECTS_H +#define EPISODIC_SCREENSPACEEFFECTS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "ScreenSpaceEffects.h" + +class CStunEffect : public IScreenSpaceEffect +{ +public: + CStunEffect( void ) : + m_flDuration( 0.0f ), + m_flFinishTime( 0.0f ), + m_bUpdateView( true ) {} + + virtual void Init( void ); + virtual void Shutdown( void ); + virtual void SetParameters( KeyValues *params ); + virtual void Enable( bool bEnable ) {}; + virtual bool IsEnabled( ) { return true; } + + virtual void Render( int x, int y, int w, int h ); + +private: + CTextureReference m_StunTexture; + CMaterialReference m_EffectMaterial; + float m_flDuration; + float m_flFinishTime; + bool m_bUpdateView; +}; + +ADD_SCREENSPACE_EFFECT( CStunEffect, episodic_stun ); + +// +// EP1 Intro Blur +// + +class CEP1IntroEffect : public IScreenSpaceEffect +{ +public: + CEP1IntroEffect( void ) : + m_flDuration( 0.0f ), + m_flFinishTime( 0.0f ), + m_bUpdateView( true ), + m_bEnabled( false ), + m_bFadeOut( false ) {} + + virtual void Init( void ); + virtual void Shutdown( void ); + virtual void SetParameters( KeyValues *params ); + virtual void Enable( bool bEnable ) { m_bEnabled = bEnable; } + virtual bool IsEnabled( ) { return m_bEnabled; } + + virtual void Render( int x, int y, int w, int h ); + +private: + + inline unsigned char GetFadeAlpha( void ); + + CTextureReference m_StunTexture; + CMaterialReference m_EffectMaterial; + float m_flDuration; + float m_flFinishTime; + bool m_bUpdateView; + bool m_bEnabled; + bool m_bFadeOut; +}; + +ADD_SCREENSPACE_EFFECT( CEP1IntroEffect, episodic_intro ); + +// +// EP2 Player Stunned Effect +// + +// +// EP1 Intro Blur +// + +class CEP2StunEffect : public IScreenSpaceEffect +{ +public: + CEP2StunEffect( void ) : + m_flDuration( 0.0f ), + m_flFinishTime( 0.0f ), + m_bUpdateView( true ), + m_bEnabled( false ), + m_bFadeOut( false ) {} + + virtual void Init( void ); + virtual void Shutdown( void ); + virtual void SetParameters( KeyValues *params ); + virtual void Enable( bool bEnable ) { m_bEnabled = bEnable; } + virtual bool IsEnabled( ) { return m_bEnabled; } + + virtual void Render( int x, int y, int w, int h ); + +private: + + inline unsigned char GetFadeAlpha( void ); + + CTextureReference m_StunTexture; + CMaterialReference m_EffectMaterial; + float m_flDuration; + float m_flFinishTime; + bool m_bUpdateView; + bool m_bEnabled; + bool m_bFadeOut; +}; + +ADD_SCREENSPACE_EFFECT( CEP2StunEffect, ep2_groggy ); + +#endif // EPISODIC_SCREENSPACEEFFECTS_H diff --git a/sp/src/game/client/episodic/flesh_internal_material_proxy.cpp b/sp/src/game/client/episodic/flesh_internal_material_proxy.cpp new file mode 100644 index 00000000..6d182ea1 --- /dev/null +++ b/sp/src/game/client/episodic/flesh_internal_material_proxy.cpp @@ -0,0 +1,225 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// Purpose: +// +// $NoKeywords: $ +//=====================================================================================// +#include "cbase.h" +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#include "debugoverlay_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class C_FleshEffectTarget; +void AddFleshProxyTarget( C_FleshEffectTarget *pTarget ); +void RemoveFleshProxy( C_FleshEffectTarget *pTarget ); + +//============================================================================= +// +// Flesh effect target (used for orchestrating the "Invisible Alyx" moment +// +//============================================================================= + +class C_FleshEffectTarget : public C_BaseEntity +{ + DECLARE_CLASS( C_FleshEffectTarget, C_BaseEntity ); + +public: + float GetRadius( void ) + { + if ( m_flScaleTime <= 0.0f ) + return m_flRadius; + + float dt = ( gpGlobals->curtime - m_flScaleStartTime ); + if ( dt >= m_flScaleTime ) + return m_flRadius; + + return SimpleSplineRemapVal( ( dt / m_flScaleTime ), 0.0f, 1.0f, m_flStartRadius, m_flRadius ); + } + + virtual void Release( void ) + { + // Remove us from the list of targets + RemoveFleshProxy( this ); + } + + virtual void OnDataChanged( DataUpdateType_t updateType ) + { + BaseClass::OnDataChanged( updateType ); + + if ( updateType == DATA_UPDATE_CREATED ) + { + // Add us to the list of flesh proxy targets + AddFleshProxyTarget( this ); + } + } + + float m_flRadius; + float m_flStartRadius; + float m_flScaleStartTime; + float m_flScaleTime; + + DECLARE_CLIENTCLASS(); +}; + +void RecvProxy_FleshEffect_Radius( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_FleshEffectTarget *pTarget = (C_FleshEffectTarget *) pStruct; + float flRadius = pData->m_Value.m_Float; + + //If changed, update our internal information + if ( pTarget->m_flRadius != flRadius ) + { + pTarget->m_flStartRadius = pTarget->m_flRadius; + pTarget->m_flScaleStartTime = gpGlobals->curtime; + } + + pTarget->m_flRadius = flRadius; +} + +IMPLEMENT_CLIENTCLASS_DT( C_FleshEffectTarget, DT_FleshEffectTarget, CFleshEffectTarget ) + RecvPropFloat( RECVINFO(m_flRadius), 0, RecvProxy_FleshEffect_Radius ), + RecvPropFloat( RECVINFO(m_flScaleTime) ), +END_RECV_TABLE() + +CUtlVector< C_FleshEffectTarget * > g_FleshProxyTargets; + +void AddFleshProxyTarget( C_FleshEffectTarget *pTarget ) +{ + // Take it! + g_FleshProxyTargets.AddToTail( pTarget ); +} + +void RemoveFleshProxy( C_FleshEffectTarget *pTarget ) +{ + int nIndex = g_FleshProxyTargets.Find( pTarget ); + if ( nIndex != g_FleshProxyTargets.InvalidIndex() ) + { + g_FleshProxyTargets.Remove( nIndex ); + } +} + +// $sineVar : name of variable that controls the FleshInterior level (float) +class CFleshInteriorMaterialProxy : public CEntityMaterialProxy +{ +public: + CFleshInteriorMaterialProxy(); + virtual ~CFleshInteriorMaterialProxy(); + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + virtual void OnBind( C_BaseEntity *pEntity ); + virtual IMaterial *GetMaterial(); + +private: + IMaterialVar *m_pMaterialParamFleshEffectCenterRadius1; + IMaterialVar *m_pMaterialParamFleshEffectCenterRadius2; + IMaterialVar *m_pMaterialParamFleshEffectCenterRadius3; + IMaterialVar *m_pMaterialParamFleshEffectCenterRadius4; + IMaterialVar *m_pMaterialParamFleshGlobalOpacity; + IMaterialVar *m_pMaterialParamFleshSubsurfaceTint; +}; + +CFleshInteriorMaterialProxy::CFleshInteriorMaterialProxy() +{ + m_pMaterialParamFleshEffectCenterRadius1 = NULL; + m_pMaterialParamFleshEffectCenterRadius2 = NULL; + m_pMaterialParamFleshEffectCenterRadius3 = NULL; + m_pMaterialParamFleshEffectCenterRadius4 = NULL; + m_pMaterialParamFleshGlobalOpacity = NULL; + m_pMaterialParamFleshSubsurfaceTint = NULL; +} + +CFleshInteriorMaterialProxy::~CFleshInteriorMaterialProxy() +{ + // Do nothing +} + +bool CFleshInteriorMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + bool bFoundVar = false; + + m_pMaterialParamFleshEffectCenterRadius1 = pMaterial->FindVar( "$FleshEffectCenterRadius1", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + + m_pMaterialParamFleshEffectCenterRadius2 = pMaterial->FindVar( "$FleshEffectCenterRadius2", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + + m_pMaterialParamFleshEffectCenterRadius3 = pMaterial->FindVar( "$FleshEffectCenterRadius3", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + + m_pMaterialParamFleshEffectCenterRadius4 = pMaterial->FindVar( "$FleshEffectCenterRadius4", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + + m_pMaterialParamFleshGlobalOpacity = pMaterial->FindVar( "$FleshGlobalOpacity", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + + m_pMaterialParamFleshSubsurfaceTint = pMaterial->FindVar( "$FleshSubsurfaceTint", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + + return true; +} + +void CFleshInteriorMaterialProxy::OnBind( C_BaseEntity *pEnt ) +{ + IMaterialVar *pParams[] = + { + m_pMaterialParamFleshEffectCenterRadius1, + m_pMaterialParamFleshEffectCenterRadius2, + m_pMaterialParamFleshEffectCenterRadius3, + m_pMaterialParamFleshEffectCenterRadius4 + }; + + float vEffectCenterRadius[4]; + for ( int i = 0; i < ARRAYSIZE( pParams ); i++ ) + { + if ( i < g_FleshProxyTargets.Count() ) + { + // Setup the target + if ( g_FleshProxyTargets[i]->IsAbsQueriesValid() == false ) + continue; + + Vector vecAbsOrigin = g_FleshProxyTargets[i]->GetAbsOrigin(); + vEffectCenterRadius[0] = vecAbsOrigin.x; + vEffectCenterRadius[1] = vecAbsOrigin.y; + vEffectCenterRadius[2] = vecAbsOrigin.z; + vEffectCenterRadius[3] = g_FleshProxyTargets[i]->GetRadius(); + } + else + { + // Clear the target + vEffectCenterRadius[0] = vEffectCenterRadius[1] = vEffectCenterRadius[2] = vEffectCenterRadius[3] = 0.0f; + } + + // Set the value either way + pParams[i]->SetVecValue( vEffectCenterRadius, 4 ); + } + + // Subsurface texture. NOTE: This texture bleeds through the color of the flesh texture so expect + // to have to set this brighter than white to really see the subsurface texture glow through. + if ( m_pMaterialParamFleshSubsurfaceTint != NULL ) + { + float vSubsurfaceTintColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + // !!! Test code. REPLACE ME! + // vSubsurfaceTintColor[0] = vSubsurfaceTintColor[1] = vSubsurfaceTintColor[2] = sinf( gpGlobals->curtime * 3.0f ) + 1.0f; // * 0.5f + 0.5f; + + m_pMaterialParamFleshSubsurfaceTint->SetVecValue( vSubsurfaceTintColor, 4 ); + } +} + +IMaterial *CFleshInteriorMaterialProxy::GetMaterial() +{ + if ( m_pMaterialParamFleshEffectCenterRadius1 == NULL) + return NULL; + + return m_pMaterialParamFleshEffectCenterRadius1->GetOwningMaterial(); +} + +EXPOSE_INTERFACE( CFleshInteriorMaterialProxy, IMaterialProxy, "FleshInterior" IMATERIAL_PROXY_INTERFACE_VERSION ); diff --git a/sp/src/game/client/game_controls/ClientScoreBoardDialog.cpp b/sp/src/game/client/game_controls/ClientScoreBoardDialog.cpp new file mode 100644 index 00000000..72776206 --- /dev/null +++ b/sp/src/game/client/game_controls/ClientScoreBoardDialog.cpp @@ -0,0 +1,580 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "cbase.h" +#include + +#include +#include +#include +#include +#include "IGameUIFuncs.h" // for key bindings +#include "inputsystem/iinputsystem.h" +#include "clientscoreboarddialog.h" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "vgui_avatarimage.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +bool AvatarIndexLessFunc( const int &lhs, const int &rhs ) +{ + return lhs < rhs; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CClientScoreBoardDialog::CClientScoreBoardDialog(IViewPort *pViewPort) : EditablePanel( NULL, PANEL_SCOREBOARD ) +{ + m_iPlayerIndexSymbol = KeyValuesSystem()->GetSymbolForString("playerIndex"); + m_nCloseKey = BUTTON_CODE_INVALID; + + //memset(s_VoiceImage, 0x0, sizeof( s_VoiceImage )); + TrackerImage = 0; + m_pViewPort = pViewPort; + + // initialize dialog + SetProportional(true); + SetKeyBoardInputEnabled(false); + SetMouseInputEnabled(false); + + // set the scheme before any child control is created + SetScheme("ClientScheme"); + + m_pPlayerList = new SectionedListPanel(this, "PlayerList"); + m_pPlayerList->SetVerticalScrollbar(false); + + LoadControlSettings("Resource/UI/ScoreBoard.res"); + m_iDesiredHeight = GetTall(); + m_pPlayerList->SetVisible( false ); // hide this until we load the images in applyschemesettings + + m_HLTVSpectators = 0; + m_ReplaySpectators = 0; + + // update scoreboard instantly if on of these events occure + ListenForGameEvent( "hltv_status" ); + ListenForGameEvent( "server_spawn" ); + + m_pImageList = NULL; + + m_mapAvatarsToImageList.SetLessFunc( DefLessFunc( CSteamID ) ); + m_mapAvatarsToImageList.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CClientScoreBoardDialog::~CClientScoreBoardDialog() +{ + if ( NULL != m_pImageList ) + { + delete m_pImageList; + m_pImageList = NULL; + } +} + +//----------------------------------------------------------------------------- +// Call every frame +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::OnThink() +{ + BaseClass::OnThink(); + + // NOTE: this is necessary because of the way input works. + // If a key down message is sent to vgui, then it will get the key up message + // Sometimes the scoreboard is activated by other vgui menus, + // sometimes by console commands. In the case where it's activated by + // other vgui menus, we lose the key up message because this panel + // doesn't accept keyboard input. It *can't* accept keyboard input + // because another feature of the dialog is that if it's triggered + // from within the game, you should be able to still run around while + // the scoreboard is up. That feature is impossible if this panel accepts input. + // because if a vgui panel is up that accepts input, it prevents the engine from + // receiving that input. So, I'm stuck with a polling solution. + // + // Close key is set to non-invalid when something other than a keybind + // brings the scoreboard up, and it's set to invalid as soon as the + // dialog becomes hidden. + if ( m_nCloseKey != BUTTON_CODE_INVALID ) + { + if ( !g_pInputSystem->IsButtonDown( m_nCloseKey ) ) + { + m_nCloseKey = BUTTON_CODE_INVALID; + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, false ); + GetClientVoiceMgr()->StopSquelchMode(); + } + } +} + +//----------------------------------------------------------------------------- +// Called by vgui panels that activate the client scoreboard +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::OnPollHideCode( int code ) +{ + m_nCloseKey = (ButtonCode_t)code; +} + +//----------------------------------------------------------------------------- +// Purpose: clears everything in the scoreboard and all it's state +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::Reset() +{ + // clear + m_pPlayerList->DeleteAllItems(); + m_pPlayerList->RemoveAllSections(); + + m_iSectionId = 0; + m_fNextUpdateTime = 0; + // add all the sections + InitScoreboardSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: adds all the team sections to the scoreboard +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::InitScoreboardSections() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: sets up screen +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + if ( m_pImageList ) + delete m_pImageList; + m_pImageList = new ImageList( false ); + + m_mapAvatarsToImageList.RemoveAll(); + + PostApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: Does dialog-specific customization after applying scheme settings. +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::PostApplySchemeSettings( vgui::IScheme *pScheme ) +{ + // resize the images to our resolution + for (int i = 0; i < m_pImageList->GetImageCount(); i++ ) + { + int wide, tall; + m_pImageList->GetImage(i)->GetSize(wide, tall); + m_pImageList->GetImage(i)->SetSize(scheme()->GetProportionalScaledValueEx( GetScheme(),wide), scheme()->GetProportionalScaledValueEx( GetScheme(),tall)); + } + + m_pPlayerList->SetImageList( m_pImageList, false ); + m_pPlayerList->SetVisible( true ); + + // light up scoreboard a bit + SetBgColor( Color( 0,0,0,0) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::ShowPanel(bool bShow) +{ + // Catch the case where we call ShowPanel before ApplySchemeSettings, eg when + // going from windowed <-> fullscreen + if ( m_pImageList == NULL ) + { + InvalidateLayout( true, true ); + } + + if ( !bShow ) + { + m_nCloseKey = BUTTON_CODE_INVALID; + } + + if ( BaseClass::IsVisible() == bShow ) + return; + + if ( bShow ) + { + Reset(); + Update(); + SetVisible( true ); + MoveToFront(); + } + else + { + BaseClass::SetVisible( false ); + SetMouseInputEnabled( false ); + SetKeyBoardInputEnabled( false ); + } +} + +void CClientScoreBoardDialog::FireGameEvent( IGameEvent *event ) +{ + const char * type = event->GetName(); + + if ( Q_strcmp(type, "hltv_status") == 0 ) + { + // spectators = clients - proxies + m_HLTVSpectators = event->GetInt( "clients" ); + m_HLTVSpectators -= event->GetInt( "proxies" ); + } + else if ( Q_strcmp(type, "server_spawn") == 0 ) + { + // We'll post the message ourselves instead of using SetControlString() + // so we don't try to translate the hostname. + const char *hostname = event->GetString( "hostname" ); + Panel *control = FindChildByName( "ServerName" ); + if ( control ) + { + PostMessage( control, new KeyValues( "SetText", "text", hostname ) ); + control->MoveToFront(); + } + } + + if( IsVisible() ) + Update(); + +} + +bool CClientScoreBoardDialog::NeedsUpdate( void ) +{ + return (m_fNextUpdateTime < gpGlobals->curtime); +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculate the internal scoreboard data +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::Update( void ) +{ + // Set the title + + // Reset(); + m_pPlayerList->DeleteAllItems(); + + FillScoreBoard(); + + // grow the scoreboard to fit all the players + int wide, tall; + m_pPlayerList->GetContentSize(wide, tall); + tall += GetAdditionalHeight(); + wide = GetWide(); + if (m_iDesiredHeight < tall) + { + SetSize(wide, tall); + m_pPlayerList->SetSize(wide, tall); + } + else + { + SetSize(wide, m_iDesiredHeight); + m_pPlayerList->SetSize(wide, m_iDesiredHeight); + } + + MoveToCenterOfScreen(); + + // update every second + m_fNextUpdateTime = gpGlobals->curtime + 1.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Sort all the teams +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::UpdateTeamInfo() +{ +// TODO: work out a sorting algorithm for team display for TF2 +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::UpdatePlayerInfo() +{ + m_iSectionId = 0; // 0'th row is a header + int selectedRow = -1; + + // walk all the players and make sure they're in the scoreboard + for ( int i = 1; i <= gpGlobals->maxClients; ++i ) + { + IGameResources *gr = GameResources(); + + if ( gr && gr->IsConnected( i ) ) + { + // add the player to the list + KeyValues *playerData = new KeyValues("data"); + GetPlayerScoreInfo( i, playerData ); + UpdatePlayerAvatar( i, playerData ); + + const char *oldName = playerData->GetString("name",""); + char newName[MAX_PLAYER_NAME_LENGTH]; + + UTIL_MakeSafeName( oldName, newName, MAX_PLAYER_NAME_LENGTH ); + + playerData->SetString("name", newName); + + int itemID = FindItemIDForPlayerIndex( i ); + int sectionID = gr->GetTeam( i ); + + if ( gr->IsLocalPlayer( i ) ) + { + selectedRow = itemID; + } + if (itemID == -1) + { + // add a new row + itemID = m_pPlayerList->AddItem( sectionID, playerData ); + } + else + { + // modify the current row + m_pPlayerList->ModifyItem( itemID, sectionID, playerData ); + } + + // set the row color based on the players team + m_pPlayerList->SetItemFgColor( itemID, gr->GetTeamColor( sectionID ) ); + + playerData->deleteThis(); + } + else + { + // remove the player + int itemID = FindItemIDForPlayerIndex( i ); + if (itemID != -1) + { + m_pPlayerList->RemoveItem(itemID); + } + } + } + + if ( selectedRow != -1 ) + { + m_pPlayerList->SetSelectedItem(selectedRow); + } +} + +//----------------------------------------------------------------------------- +// Purpose: adds the top header of the scoreboars +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::AddHeader() +{ + // add the top header + m_pPlayerList->AddSection(m_iSectionId, ""); + m_pPlayerList->SetSectionAlwaysVisible(m_iSectionId); + m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#PlayerName", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "#PlayerScore", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "deaths", "#PlayerDeath", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),DEATH_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "#PlayerPing", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),PING_WIDTH) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new section to the scoreboard (i.e the team header) +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::AddSection(int teamType, int teamNumber) +{ + if ( teamType == TYPE_TEAM ) + { + IGameResources *gr = GameResources(); + + if ( !gr ) + return; + + // setup the team name + wchar_t *teamName = g_pVGuiLocalize->Find( gr->GetTeamName(teamNumber) ); + wchar_t name[64]; + wchar_t string1[1024]; + + if (!teamName) + { + g_pVGuiLocalize->ConvertANSIToUnicode(gr->GetTeamName(teamNumber), name, sizeof(name)); + teamName = name; + } + + g_pVGuiLocalize->ConstructString( string1, sizeof( string1 ), g_pVGuiLocalize->Find("#Player"), 2, teamName ); + + m_pPlayerList->AddSection(m_iSectionId, "", StaticPlayerSortFunc); + + // Avatars are always displayed at 32x32 regardless of resolution + if ( ShowAvatars() ) + { + m_pPlayerList->AddColumnToSection( m_iSectionId, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth ); + } + + m_pPlayerList->AddColumnToSection(m_iSectionId, "name", string1, 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) - m_iAvatarWidth ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "deaths", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),DEATH_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),PING_WIDTH) ); + } + else if ( teamType == TYPE_SPECTATORS ) + { + m_pPlayerList->AddSection(m_iSectionId, ""); + + // Avatars are always displayed at 32x32 regardless of resolution + if ( ShowAvatars() ) + { + m_pPlayerList->AddColumnToSection( m_iSectionId, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth ); + } + m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#Spectators", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) - m_iAvatarWidth ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Used for sorting players +//----------------------------------------------------------------------------- +bool CClientScoreBoardDialog::StaticPlayerSortFunc(vgui::SectionedListPanel *list, int itemID1, int itemID2) +{ + KeyValues *it1 = list->GetItemData(itemID1); + KeyValues *it2 = list->GetItemData(itemID2); + Assert(it1 && it2); + + // first compare frags + int v1 = it1->GetInt("frags"); + int v2 = it2->GetInt("frags"); + if (v1 > v2) + return true; + else if (v1 < v2) + return false; + + // next compare deaths + v1 = it1->GetInt("deaths"); + v2 = it2->GetInt("deaths"); + if (v1 > v2) + return false; + else if (v1 < v2) + return true; + + // the same, so compare itemID's (as a sentinel value to get deterministic sorts) + return itemID1 < itemID2; +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new row to the scoreboard, from the playerinfo structure +//----------------------------------------------------------------------------- +bool CClientScoreBoardDialog::GetPlayerScoreInfo(int playerIndex, KeyValues *kv) +{ + IGameResources *gr = GameResources(); + + if (!gr ) + return false; + + kv->SetInt("deaths", gr->GetDeaths( playerIndex ) ); + kv->SetInt("frags", gr->GetFrags( playerIndex ) ); + kv->SetInt("ping", gr->GetPing( playerIndex ) ) ; + kv->SetString("name", gr->GetPlayerName( playerIndex ) ); + kv->SetInt("playerIndex", playerIndex); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::UpdatePlayerAvatar( int playerIndex, KeyValues *kv ) +{ + // Update their avatar + if ( kv && ShowAvatars() && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() ) + { + player_info_t pi; + if ( engine->GetPlayerInfo( playerIndex, &pi ) ) + { + if ( pi.friendsID ) + { + CSteamID steamIDForPlayer( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual ); + + // See if we already have that avatar in our list + int iMapIndex = m_mapAvatarsToImageList.Find( steamIDForPlayer ); + int iImageIndex; + if ( iMapIndex == m_mapAvatarsToImageList.InvalidIndex() ) + { + CAvatarImage *pImage = new CAvatarImage(); + pImage->SetAvatarSteamID( steamIDForPlayer ); + pImage->SetAvatarSize( 32, 32 ); // Deliberately non scaling + iImageIndex = m_pImageList->AddImage( pImage ); + + m_mapAvatarsToImageList.Insert( steamIDForPlayer, iImageIndex ); + } + else + { + iImageIndex = m_mapAvatarsToImageList[ iMapIndex ]; + } + + kv->SetInt( "avatar", iImageIndex ); + + CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( iImageIndex ); + pAvIm->UpdateFriendStatus(); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: reload the player list on the scoreboard +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::FillScoreBoard() +{ + // update totals information + UpdateTeamInfo(); + + // update player info + UpdatePlayerInfo(); +} + +//----------------------------------------------------------------------------- +// Purpose: searches for the player in the scoreboard +//----------------------------------------------------------------------------- +int CClientScoreBoardDialog::FindItemIDForPlayerIndex(int playerIndex) +{ + for (int i = 0; i <= m_pPlayerList->GetHighestItemID(); i++) + { + if (m_pPlayerList->IsItemIDValid(i)) + { + KeyValues *kv = m_pPlayerList->GetItemData(i); + kv = kv->FindKey(m_iPlayerIndexSymbol); + if (kv && kv->GetInt() == playerIndex) + return i; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::MoveLabelToFront(const char *textEntryName) +{ + Label *entry = dynamic_cast