From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- materialsystem/shadersystem.cpp | 2057 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 2057 insertions(+) create mode 100644 materialsystem/shadersystem.cpp (limited to 'materialsystem/shadersystem.cpp') diff --git a/materialsystem/shadersystem.cpp b/materialsystem/shadersystem.cpp new file mode 100644 index 0000000..7d53060 --- /dev/null +++ b/materialsystem/shadersystem.cpp @@ -0,0 +1,2057 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "shadersystem.h" +#include +#include "materialsystem_global.h" +#include "filesystem.h" +#include "tier1/utldict.h" +#include "shaderlib/ShaderDLL.h" +#include "texturemanager.h" +#include "itextureinternal.h" +#include "IHardwareConfigInternal.h" +#include "tier1/utlstack.h" +#include "tier1/utlbuffer.h" +#include "mathlib/vmatrix.h" +#include "imaterialinternal.h" +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "shaderlib/cshader.h" +#include "tier1/convar.h" +#include "tier1/KeyValues.h" +#include "shader_dll_verify.h" +#include "tier0/vprof.h" + +// NOTE: This must be the last file included! +#include "tier0/memdbgon.h" + + +//#define DEBUG_DEPTH 1 + +//----------------------------------------------------------------------------- +// Lovely convars +//----------------------------------------------------------------------------- +static ConVar mat_showenvmapmask( "mat_showenvmapmask", "0" ); +static ConVar mat_debugdepth( "mat_debugdepth", "0" ); +extern ConVar mat_supportflashlight; + + +//----------------------------------------------------------------------------- +// Implementation of the shader system +//----------------------------------------------------------------------------- +class CShaderSystem : public IShaderSystemInternal +{ +public: + CShaderSystem(); + + // Methods of IShaderSystem + virtual ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrameVar, int nTextureChannel = 0 ); + + virtual void BindTexture( Sampler_t sampler1, ITexture *pTexture, int nFrame = 0 ); + virtual void BindTexture( Sampler_t sampler1, Sampler_t sampler2, ITexture *pTexture, int nFrame = 0 ); + + virtual void TakeSnapshot( ); + virtual void DrawSnapshot( bool bMakeActualDrawCall = true ); + virtual bool IsUsingGraphics() const; + virtual bool CanUseEditorMaterials() const; + + // Methods of IShaderSystemInternal + virtual void Init(); + virtual void Shutdown(); + virtual void ModInit(); + virtual void ModShutdown(); + + virtual bool LoadShaderDLL( const char *pFullPath ); + virtual bool LoadShaderDLL( const char *pFullPath, const char *pPathID, bool bModShaderDLL ); + virtual void UnloadShaderDLL( const char *pFullPath ); + + virtual IShader* FindShader( char const* pShaderName ); + virtual void CreateDebugMaterials(); + virtual void CleanUpDebugMaterials(); + virtual char const* ShaderStateString( int i ) const; + virtual int ShaderStateCount( ) const; + + virtual void InitShaderParameters( IShader *pShader, IMaterialVar **params, const char *pMaterialName ); + virtual void InitShaderInstance( IShader *pShader, IMaterialVar **params, const char *pMaterialName, const char *pTextureGroupName ); + virtual bool InitRenderState( IShader *pShader, int numParams, IMaterialVar **params, ShaderRenderState_t* pRenderState, char const* pMaterialName ); + virtual void CleanupRenderState( ShaderRenderState_t* pRenderState ); + virtual void DrawElements( IShader *pShader, IMaterialVar **params, ShaderRenderState_t* pShaderState, VertexCompressionType_t vertexCompression, uint32 nVarChangeID ); + + // Used to iterate over all shaders for editing purposes + virtual int ShaderCount() const; + virtual int GetShaders( int nFirstShader, int nMaxCount, IShader **ppShaderList ) const; + + // Methods of IShaderInit + virtual void LoadTexture( IMaterialVar *pTextureVar, const char *pTextureGroupName, int nAdditionalCreationFlags = 0 ); + virtual void LoadBumpMap( IMaterialVar *pTextureVar, const char *pTextureGroupName ); + virtual void LoadCubeMap( IMaterialVar **ppParams, IMaterialVar *pTextureVar, int nAdditionalCreationFlags = 0 ); + + // Used to prevent re-entrant rendering from warning messages + void BufferSpew( SpewType_t spewType, const Color &c, const char *pMsg ); + +private: + struct ShaderDLLInfo_t + { + char *m_pFileName; + CSysModule *m_hInstance; + IShaderDLLInternal *m_pShaderDLL; + ShaderDLL_t m_hShaderDLL; + + // True if this is a mod's shader DLL, in which case it's not allowed to + // override any existing shader names. + bool m_bModShaderDLL; + CUtlDict< IShader *, unsigned short > m_ShaderDict; + }; + +private: + // hackhack: remove this when VAC2 is online. + void VerifyBaseShaderDLL( CSysModule *pModule ); + + // Load up the shader DLLs... + void LoadAllShaderDLLs(); + + // Load the "mshader_" DLLs. + void LoadModShaderDLLs( int dxSupportLevel ); + + // Unload all the shader DLLs... + void UnloadAllShaderDLLs(); + + // Sets up the shader dictionary. + void SetupShaderDictionary( int nShaderDLLIndex ); + + // Cleans up the shader dictionary. + void CleanupShaderDictionary( int nShaderDLLIndex ); + + // Finds an already loaded shader DLL + int FindShaderDLL( const char *pFullPath ); + + // Unloads a particular shader DLL + void UnloadShaderDLL( int nShaderDLLIndex ); + + // Sets up the current ShaderState_t for rendering + void PrepForShaderDraw( IShader *pShader, IMaterialVar** ppParams, + ShaderRenderState_t* pRenderState, int modulation ); + void DoneWithShaderDraw(); + + // Initializes state snapshots + void InitStateSnapshots( IShader *pShader, IMaterialVar **params, ShaderRenderState_t* pRenderState ); + + // Compute snapshots for all combinations of alpha + color modulation + void InitRenderStateFlags( ShaderRenderState_t* pRenderState, int numParams, IMaterialVar **params ); + + // Computes flags from a particular snapshot + void ComputeRenderStateFlagsFromSnapshot( ShaderRenderState_t* pRenderState ); + + // Computes vertex format + usage from a particular snapshot + bool ComputeVertexFormatFromSnapshot( IMaterialVar **params, ShaderRenderState_t* pRenderState ); + + // Used to prevent re-entrant rendering from warning messages + void PrintBufferedSpew( void ); + + // Gets at the current snapshot + StateSnapshot_t CurrentStateSnapshot(); + + // Draws using a particular material.. + void DrawUsingMaterial( IMaterialInternal *pMaterial, VertexCompressionType_t vertexCompression ); + + // Copies material vars + void CopyMaterialVarToDebugShader( IMaterialInternal *pDebugMaterial, IShader *pShader, IMaterialVar **ppParams, const char *pSrcVarName, const char *pDstVarName = NULL ); + + // Debugging draw methods... + void DrawMeasureFillRate( ShaderRenderState_t* pRenderState, int mod, VertexCompressionType_t vertexCompression ); + void DrawNormalMap( IShader *pShader, IMaterialVar **ppParams, VertexCompressionType_t vertexCompression ); + bool DrawEnvmapMask( IShader *pShader, IMaterialVar **ppParams, ShaderRenderState_t* pRenderState, VertexCompressionType_t vertexCompression ); + + int GetModulationSnapshotCount( IMaterialVar **params ); + +private: + // List of all DLLs containing shaders + CUtlVector< ShaderDLLInfo_t > m_ShaderDLLs; + + // Used to prevent re-entrant rendering from warning messages + SpewOutputFunc_t m_SaveSpewOutput; + + CUtlBuffer m_StoredSpew; + + // Render state we're drawing with + ShaderRenderState_t* m_pRenderState; + unsigned short m_hShaderDLL; + unsigned char m_nModulation; + unsigned char m_nRenderPass; + + // Debugging materials + // If you add to this, add to the list of debug shader names (s_pDebugShaderName) below + enum + { + MATERIAL_FILL_RATE = 0, + MATERIAL_DEBUG_NORMALMAP, + MATERIAL_DEBUG_ENVMAPMASK, + MATERIAL_DEBUG_DEPTH, + MATERIAL_DEBUG_DEPTH_DECAL, + MATERIAL_DEBUG_WIREFRAME, + + MATERIAL_DEBUG_COUNT, + }; + + IMaterialInternal* m_pDebugMaterials[MATERIAL_DEBUG_COUNT]; + static const char *s_pDebugShaderName[MATERIAL_DEBUG_COUNT]; + + bool m_bForceUsingGraphicsReturnTrue; +}; + + +//----------------------------------------------------------------------------- +// Singleton +//----------------------------------------------------------------------------- +static CShaderSystem s_ShaderSystem; +IShaderSystemInternal *g_pShaderSystem = &s_ShaderSystem; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderSystem, IShaderSystem, + SHADERSYSTEM_INTERFACE_VERSION, s_ShaderSystem ); + + +//----------------------------------------------------------------------------- +// Debugging shader names +//----------------------------------------------------------------------------- +const char *CShaderSystem::s_pDebugShaderName[MATERIAL_DEBUG_COUNT] = +{ + "FillRate", + "DebugNormalMap", + "DebugDrawEnvmapMask", + "DebugDepth", + "DebugDepth", + "Wireframe_DX9" +}; + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CShaderSystem::CShaderSystem() : m_StoredSpew( 0, 512, 0 ), m_bForceUsingGraphicsReturnTrue( false ) +{ +} + + +//----------------------------------------------------------------------------- +// Initialization, shutdown +//----------------------------------------------------------------------------- +void CShaderSystem::Init() +{ + m_SaveSpewOutput = NULL; + + m_bForceUsingGraphicsReturnTrue = false; + if ( CommandLine()->FindParm( "-noshaderapi" ) || + CommandLine()->FindParm( "-makereslists" ) ) + { + m_bForceUsingGraphicsReturnTrue = true; + } + + for ( int i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) + { + m_pDebugMaterials[i] = NULL; + } + + LoadAllShaderDLLs(); +} + +void CShaderSystem::Shutdown() +{ + UnloadAllShaderDLLs(); +} + + +//----------------------------------------------------------------------------- +// Load/unload mod-specific shader DLLs +//----------------------------------------------------------------------------- +void CShaderSystem::ModInit() +{ + // Load up standard shader DLLs... + int dxSupportLevel = HardwareConfig()->GetMaxDXSupportLevel(); + Assert( dxSupportLevel >= 60 ); + dxSupportLevel /= 10; + + LoadModShaderDLLs( dxSupportLevel ); +} + + +void CShaderSystem::ModShutdown() +{ + // Unload only MOD dlls + for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) + { + if ( m_ShaderDLLs[i].m_bModShaderDLL ) + { + UnloadShaderDLL(i); + delete[] m_ShaderDLLs[i].m_pFileName; + m_ShaderDLLs.Remove( i ); + } + } +} + + +//----------------------------------------------------------------------------- +// Load up the shader DLLs... +//----------------------------------------------------------------------------- +void CShaderSystem::LoadAllShaderDLLs( ) +{ + UnloadAllShaderDLLs(); + + GetShaderDLLInternal()->Connect( Sys_GetFactoryThis(), true ); + + // Loads local defined or statically linked shaders + int i = m_ShaderDLLs.AddToHead(); + + m_ShaderDLLs[i].m_pFileName = new char[1]; + m_ShaderDLLs[i].m_pFileName[0] = 0; + m_ShaderDLLs[i].m_hInstance = NULL; + m_ShaderDLLs[i].m_pShaderDLL = GetShaderDLLInternal(); + m_ShaderDLLs[i].m_bModShaderDLL = false; + + // Add the shaders to the dictionary of shaders... + SetupShaderDictionary( i ); + + // 360 has the the debug shaders in its dx9 dll + if ( IsPC() || !IsX360() ) + { + // Always need the debug shaders + LoadShaderDLL( "stdshader_dbg" DLL_EXT_STRING ); + } + + // Load up standard shader DLLs... + int dxSupportLevel = HardwareConfig()->GetMaxDXSupportLevel(); + Assert( dxSupportLevel >= 60 ); + dxSupportLevel /= 10; + + // 360 only supports its dx9 dll + int dxStart = IsX360() ? 9 : 6; + char buf[32]; + for ( i = dxStart; i <= dxSupportLevel; ++i ) + { + Q_snprintf( buf, sizeof( buf ), "stdshader_dx%d%s", i, DLL_EXT_STRING ); + LoadShaderDLL( buf ); + } + + const char *pShaderName = NULL; +#ifdef _DEBUG + pShaderName = CommandLine()->ParmValue( "-shader" ); +#endif + if ( !pShaderName ) + { + pShaderName = HardwareConfig()->GetHWSpecificShaderDLLName(); + } + if ( pShaderName ) + { + LoadShaderDLL( pShaderName ); + } + +#ifdef _DEBUG + // For fast-iteration debugging + if ( CommandLine()->FindParm( "-testshaders" ) ) + { + LoadShaderDLL( "shader_test" DLL_EXT_STRING ); + } +#endif +} + +const char *COM_GetModDirectory() +{ + static char modDir[MAX_PATH]; + if ( Q_strlen( modDir ) == 0 ) + { + const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) ); + Q_strncpy( modDir, gamedir, sizeof(modDir) ); + if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) ) + { + Q_StripLastDir( modDir, sizeof(modDir) ); + int dirlen = Q_strlen( modDir ); + Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen ); + } + } + + return modDir; +} + +void CShaderSystem::LoadModShaderDLLs( int dxSupportLevel ) +{ + if ( IsX360() ) + return; + + // Don't do this for Valve mods. They don't need them, and attempting to load them is an opportunity for cheaters to get their code into the process + const char *pGameDir = COM_GetModDirectory(); + if ( !Q_stricmp( pGameDir, "hl2" ) || !Q_stricmp( pGameDir, "cstrike" ) || !Q_stricmp( pGameDir, "cstrike_beta" ) || + !Q_stricmp( pGameDir, "hl2mp" ) || !Q_stricmp( pGameDir, "lostcoast" ) || !Q_stricmp( pGameDir, "episodic" ) || + !Q_stricmp( pGameDir, "portal" ) || !Q_stricmp( pGameDir, "ep2" ) || !Q_stricmp( pGameDir, "dod" ) || + !Q_stricmp( pGameDir, "tf" ) || !Q_stricmp( pGameDir, "tf_beta" ) || !Q_stricmp( pGameDir, "hl1" ) ) + { + return; + } + + const char *pModShaderPathID = "GAMEBIN"; + + // First load the ones with dx_ prefix. + char buf[256]; + + int dxStart = 6; + for ( int i = dxStart; i <= dxSupportLevel; ++i ) + { + Q_snprintf( buf, sizeof( buf ), "game_shader_dx%d%s", i, DLL_EXT_STRING ); + LoadShaderDLL( buf, pModShaderPathID, true ); + } + + // Now load the ones with any dx_ prefix. + FileFindHandle_t findHandle; + const char *pFilename = g_pFullFileSystem->FindFirstEx( "game_shader_generic*", pModShaderPathID, &findHandle ); + while ( pFilename ) + { + Q_snprintf( buf, sizeof( buf ), "%s%s", pFilename, DLL_EXT_STRING ); + LoadShaderDLL( buf, pModShaderPathID, true ); + + pFilename = g_pFullFileSystem->FindNext( findHandle ); + } +} + + +//----------------------------------------------------------------------------- +// Unload all the shader DLLs... +//----------------------------------------------------------------------------- +void CShaderSystem::UnloadAllShaderDLLs() +{ + if ( m_ShaderDLLs.Count() == 0 ) + return; + + for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) + { + UnloadShaderDLL(i); + delete[] m_ShaderDLLs[i].m_pFileName; + } + + m_ShaderDLLs.RemoveAll(); +} + +bool CShaderSystem::LoadShaderDLL( const char *pFullPath ) +{ + return LoadShaderDLL( pFullPath, NULL, false ); +} + +// HACKHACK: remove me when VAC2 is online. +#if defined( _WIN32 ) && !defined( _X360 ) +// Instead of including windows.h +extern "C" +{ + extern void * __stdcall GetProcAddress( void *hModule, const char *pszProcName ); +}; +#endif + +void CShaderSystem::VerifyBaseShaderDLL( CSysModule *pModule ) +{ +#if defined( _WIN32 ) && !defined( _X360 ) + const char *pErrorStr = "Corrupt save data settings."; + + unsigned char *testData1 = new unsigned char[SHADER_DLL_VERIFY_DATA_LEN1]; + + ShaderDLLVerifyFn fn = (ShaderDLLVerifyFn)GetProcAddress( (void *)pModule, SHADER_DLL_FNNAME_1 ); + if ( !fn ) + Error( pErrorStr ); + + IShaderDLLVerification *pVerify; + char *pPtr = (char*)(void*)&pVerify; + pPtr -= SHADER_DLL_VERIFY_DATA_PTR_OFFSET; + fn( pPtr ); + + // Test the first CRC. + CRC32_t testCRC; + CRC32_Init( &testCRC ); + CRC32_ProcessBuffer( &testCRC, testData1, SHADER_DLL_VERIFY_DATA_LEN1 ); + CRC32_ProcessBuffer( &testCRC, &pModule, 4 ); + CRC32_ProcessBuffer( &testCRC, &pVerify, 4 ); + CRC32_Final( &testCRC ); + if ( testCRC != pVerify->Function1( testData1 - SHADER_DLL_VERIFY_DATA_PTR_OFFSET ) ) + Error( pErrorStr ); + + // Test the next one. + unsigned char digest[MD5_DIGEST_LENGTH]; + MD5Context_t md5Context; + MD5Init( &md5Context ); + MD5Update( &md5Context, testData1 + SHADER_DLL_VERIFY_DATA_PTR_OFFSET, SHADER_DLL_VERIFY_DATA_LEN1 - SHADER_DLL_VERIFY_DATA_PTR_OFFSET ); + MD5Final( digest, &md5Context ); + pVerify->Function2( 2, 3, 3 ); // fn2 is supposed to place the result in testData1. + if ( memcmp( digest, testData1, MD5_DIGEST_LENGTH ) != 0 ) + Error( pErrorStr ); + + pVerify->Function5(); + + delete [] testData1; +#endif +} + +//----------------------------------------------------------------------------- +// Methods related to reading in shader DLLs +//----------------------------------------------------------------------------- +bool CShaderSystem::LoadShaderDLL( const char *pFullPath, const char *pPathID, bool bModShaderDLL ) +{ + if ( !pFullPath && !pFullPath[0] ) + return true; + + // Load the new shader + bool bValidatedDllOnly = true; + if ( bModShaderDLL ) + bValidatedDllOnly = false; + + CSysModule *hInstance = g_pFullFileSystem->LoadModule( pFullPath, pPathID, bValidatedDllOnly ); + if ( !hInstance ) + return false; + + // Get at the shader DLL interface + CreateInterfaceFn factory = Sys_GetFactory( hInstance ); + if (!factory) + { + g_pFullFileSystem->UnloadModule( hInstance ); + return false; + } + + IShaderDLLInternal *pShaderDLL = (IShaderDLLInternal*)factory( SHADER_DLL_INTERFACE_VERSION, NULL ); + if ( !pShaderDLL ) + { + g_pFullFileSystem->UnloadModule( hInstance ); + return false; + } + + // Make sure it's a valid base shader DLL if necessary. + //HACKHACK get rid of this when VAC2 comes online. + if ( !bModShaderDLL ) + { + VerifyBaseShaderDLL( hInstance ); + } + + // Allow the DLL to try to connect to interfaces it needs + if ( !pShaderDLL->Connect( Sys_GetFactoryThis(), false ) ) + { + g_pFullFileSystem->UnloadModule( hInstance ); + return false; + } + + // FIXME: We need to do some sort of shader validation here for anticheat. + + // Now replace any existing shader + int nShaderDLLIndex = FindShaderDLL( pFullPath ); + if ( nShaderDLLIndex >= 0 ) + { + UnloadShaderDLL( nShaderDLLIndex ); + } + else + { + nShaderDLLIndex = m_ShaderDLLs.AddToTail(); + int nLen = Q_strlen(pFullPath) + 1; + m_ShaderDLLs[nShaderDLLIndex].m_pFileName = new char[ nLen ]; + Q_strncpy( m_ShaderDLLs[nShaderDLLIndex].m_pFileName, pFullPath, nLen ); + } + + // Ok, the shader DLL's good! + m_ShaderDLLs[nShaderDLLIndex].m_hInstance = hInstance; + m_ShaderDLLs[nShaderDLLIndex].m_pShaderDLL = pShaderDLL; + m_ShaderDLLs[nShaderDLLIndex].m_bModShaderDLL = bModShaderDLL; + + // Add the shaders to the dictionary of shaders... + SetupShaderDictionary( nShaderDLLIndex ); + + // FIXME: Fix up existing materials that were using shaders that have + // been reloaded? + + return true; +} + +//----------------------------------------------------------------------------- +// Finds an already loaded shader DLL +//----------------------------------------------------------------------------- +int CShaderSystem::FindShaderDLL( const char *pFullPath ) +{ + for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) + { + if ( !Q_stricmp( pFullPath, m_ShaderDLLs[i].m_pFileName ) ) + return i; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Unloads a particular shader DLL +//----------------------------------------------------------------------------- +void CShaderSystem::UnloadShaderDLL( int nShaderDLLIndex ) +{ + if ( nShaderDLLIndex < 0 ) + return; + + // FIXME: Do some sort of fixup of materials to determine which + // materials are referencing shaders in this DLL? + CleanupShaderDictionary( nShaderDLLIndex ); + IShaderDLLInternal *pShaderDLL = m_ShaderDLLs[nShaderDLLIndex].m_pShaderDLL; + pShaderDLL->Disconnect( pShaderDLL == GetShaderDLLInternal() ); + if ( m_ShaderDLLs[nShaderDLLIndex].m_hInstance ) + { + g_pFullFileSystem->UnloadModule( m_ShaderDLLs[nShaderDLLIndex].m_hInstance ); + } +} + +//----------------------------------------------------------------------------- +// Unloads a particular shader DLL +//----------------------------------------------------------------------------- +void CShaderSystem::UnloadShaderDLL( const char *pFullPath ) +{ + int nShaderDLLIndex = FindShaderDLL( pFullPath ); + if ( nShaderDLLIndex >= 0 ) + { + UnloadShaderDLL( nShaderDLLIndex ); + delete[] m_ShaderDLLs[nShaderDLLIndex].m_pFileName; + m_ShaderDLLs.Remove( nShaderDLLIndex ); + } +} + + +//----------------------------------------------------------------------------- +// Make sure these match the bits in imaterial.h +//----------------------------------------------------------------------------- +static const char* s_pShaderStateString[] = +{ + "$debug", + "$no_fullbright", + "$no_draw", + "$use_in_fillrate_mode", + + "$vertexcolor", + "$vertexalpha", + "$selfillum", + "$additive", + "$alphatest", + "$multipass", + "$znearer", + "$model", + "$flat", + "$nocull", + "$nofog", + "$ignorez", + "$decal", + "$envmapsphere", + "$noalphamod", + "$envmapcameraspace", + "$basealphaenvmapmask", + "$translucent", + "$normalmapalphaenvmapmask", + "$softwareskin", + "$opaquetexture", + "$envmapmode", + "$nodecal", + "$halflambert", + "$wireframe", + "$allowalphatocoverage", + + "" // last one must be null +}; + + +//----------------------------------------------------------------------------- +// returns strings associated with the shader state flags... +// If you modify this, make sure and modify MaterialVarFlags_t in imaterial.h +//----------------------------------------------------------------------------- +int CShaderSystem::ShaderStateCount( ) const +{ + return sizeof( s_pShaderStateString ) / sizeof( char* ) - 1; +} + + +//----------------------------------------------------------------------------- +// returns strings associated with the shader state flags... +// If you modify this, make sure and modify MaterialVarFlags_t in imaterial.h +//----------------------------------------------------------------------------- +char const* CShaderSystem::ShaderStateString( int i ) const +{ + return s_pShaderStateString[i]; +} + + +//----------------------------------------------------------------------------- +// Sets up the shader dictionary. +//----------------------------------------------------------------------------- +void CShaderSystem::SetupShaderDictionary( int nShaderDLLIndex ) +{ + // We could have put the shader dictionary into each shader DLL + // I'm not sure if that makes this system any less secure than it already is + int i; + ShaderDLLInfo_t &info = m_ShaderDLLs[nShaderDLLIndex]; + int nCount = info.m_pShaderDLL->ShaderCount(); + for ( i = 0; i < nCount; ++i ) + { + IShader *pShader = info.m_pShaderDLL->GetShader( i ); + const char *pShaderName = pShader->GetName(); + +#ifdef POSIX + if (CommandLine()->FindParm("-glmspew")) + printf("CShaderSystem::SetupShaderDictionary: %s", pShaderName ); +#endif + + // Make sure it doesn't try to override another shader DLL's names. + if ( info.m_bModShaderDLL ) + { + for ( int iTestDLL=0; iTestDLL < m_ShaderDLLs.Count(); iTestDLL++ ) + { + ShaderDLLInfo_t *pTestDLL = &m_ShaderDLLs[iTestDLL]; + if ( !pTestDLL->m_bModShaderDLL ) + { + if ( pTestDLL->m_ShaderDict.Find( pShaderName ) != pTestDLL->m_ShaderDict.InvalidIndex() ) + { + Error( "Game shader '%s' trying to override a base shader '%s'.", info.m_pFileName, pShaderName ); + } + } + } + } + + info.m_ShaderDict.Insert( pShaderName, pShader ); + } +} + +//----------------------------------------------------------------------------- +// Cleans up the shader dictionary. +//----------------------------------------------------------------------------- +void CShaderSystem::CleanupShaderDictionary( int nShaderDLLIndex ) +{ +} + +//----------------------------------------------------------------------------- +// Finds a shader in the shader dictionary +//----------------------------------------------------------------------------- +IShader* CShaderSystem::FindShader( char const* pShaderName ) +{ + // FIXME: What kind of search order should we use here? + // I'm currently assuming last added, first searched. + for (int i = m_ShaderDLLs.Count(); --i >= 0; ) + { + ShaderDLLInfo_t &info = m_ShaderDLLs[i]; + unsigned short idx = info.m_ShaderDict.Find( pShaderName ); + if ( idx != info.m_ShaderDict.InvalidIndex() ) + { + return info.m_ShaderDict[idx]; + } + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Used to iterate over all shaders for editing purposes +//----------------------------------------------------------------------------- +int CShaderSystem::ShaderCount() const +{ + return GetShaders( 0, 65536, NULL ); +} + +int CShaderSystem::GetShaders( int nFirstShader, int nMaxCount, IShader **ppShaderList ) const +{ + CUtlSymbolTable uniqueNames( 0, 512, true ); + + int nCount = 0; + int nActualCount = 0; + for ( int i = m_ShaderDLLs.Count(); --i >= 0; ) + { + const ShaderDLLInfo_t &info = m_ShaderDLLs[i]; + for ( unsigned short j = info.m_ShaderDict.First(); + j != info.m_ShaderDict.InvalidIndex(); + j = info.m_ShaderDict.Next( j ) ) + { + // Don't add shaders twice + const char *pShaderName = info.m_ShaderDict.GetElementName( j ); + if ( uniqueNames.Find( pShaderName ) != UTL_INVAL_SYMBOL ) + continue; + + // Indicate we've seen this shader + uniqueNames.AddString( pShaderName ); + + ++nActualCount; + if ( nActualCount > nFirstShader ) + { + if ( ppShaderList ) + { + ppShaderList[ nCount ] = info.m_ShaderDict[j]; + } + ++nCount; + if ( nCount >= nMaxCount ) + return nCount; + } + } + } + + return nCount; +} + + +//----------------------------------------------------------------------------- +// +// Methods of IShaderInit lie below +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Gets at the render pass info for this pass... +//----------------------------------------------------------------------------- +inline StateSnapshot_t CShaderSystem::CurrentStateSnapshot() +{ + Assert( m_pRenderState ); + Assert( m_nRenderPass < MAX_RENDER_PASSES ); + Assert( m_nRenderPass < m_pRenderState->m_pSnapshots[m_nModulation].m_nPassCount ); + return m_pRenderState->m_pSnapshots[m_nModulation].m_Snapshot[m_nRenderPass]; +} + + +//----------------------------------------------------------------------------- +// Create debugging materials +//----------------------------------------------------------------------------- +void CShaderSystem::CreateDebugMaterials() +{ + if (m_pDebugMaterials[0]) + return; + + KeyValues *pVMTKeyValues[MATERIAL_DEBUG_COUNT]; + + int i; + for ( i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) + { + pVMTKeyValues[i] = new KeyValues( s_pDebugShaderName[i] ); + } + + pVMTKeyValues[MATERIAL_DEBUG_DEPTH_DECAL]->SetInt( "$decal", 1 ); + + for ( i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) + { + char shaderName[64]; + Q_snprintf( shaderName, sizeof( shaderName ), "___%s_%d.vmt", s_pDebugShaderName[i], i ); + m_pDebugMaterials[i] = static_cast(MaterialSystem()->CreateMaterial( shaderName, pVMTKeyValues[i] )); + if( m_pDebugMaterials[i] ) + m_pDebugMaterials[i] = m_pDebugMaterials[i]->GetRealTimeVersion(); + } +} + + +//----------------------------------------------------------------------------- +// Cleans up the debugging materials +//----------------------------------------------------------------------------- +void CShaderSystem::CleanUpDebugMaterials() +{ + if (m_pDebugMaterials[0]) + { + for ( int i = 0; i < MATERIAL_DEBUG_COUNT; ++i ) + { + m_pDebugMaterials[i]->DecrementReferenceCount(); + if ( m_pDebugMaterials[i]->InMaterialPage() ) + { + MaterialSystem()->RemoveMaterialSubRect( m_pDebugMaterials[i] ); + } + else + { + MaterialSystem()->RemoveMaterial( m_pDebugMaterials[i] ); + } + m_pDebugMaterials[i] = NULL; + } + } +} + + +//----------------------------------------------------------------------------- +// Deal with buffering of spew while doing shader draw so that we don't get +// recursive spew during precache due to fonts not being loaded, etc. +//----------------------------------------------------------------------------- +CThreadFastMutex g_StgoredSpewMutex; +void CShaderSystem::BufferSpew( SpewType_t spewType, const Color &c, const char *pMsg ) +{ + AUTO_LOCK( g_StgoredSpewMutex ); + m_StoredSpew.PutInt( spewType ); + m_StoredSpew.PutChar( c.r() ); + m_StoredSpew.PutChar( c.g() ); + m_StoredSpew.PutChar( c.b() ); + m_StoredSpew.PutChar( c.a() ); + m_StoredSpew.PutString( pMsg ); +} + +void CShaderSystem::PrintBufferedSpew( void ) +{ + AUTO_LOCK( g_StgoredSpewMutex ); + while ( m_StoredSpew.GetBytesRemaining() > 0 ) + { + SpewType_t spewType = (SpewType_t)m_StoredSpew.GetInt(); + + unsigned char r, g, b, a; + r = m_StoredSpew.GetChar(); + g = m_StoredSpew.GetChar(); + b = m_StoredSpew.GetChar(); + a = m_StoredSpew.GetChar(); + + Color c( r, g, b, a ); + + int nLen = m_StoredSpew.PeekStringLength(); + if ( nLen ) + { + char *pBuf = (char*)_alloca( nLen ); + m_StoredSpew.GetStringManualCharCount( pBuf, nLen ); + ColorSpewMessage( spewType, &c, "%s", pBuf ); + } + else + { + break; + } + } + + m_StoredSpew.Clear(); +} + +static SpewRetval_t MySpewOutputFunc( SpewType_t spewType, char const *pMsg ) +{ + AUTO_LOCK( g_StgoredSpewMutex ); + Color c = *GetSpewOutputColor(); + s_ShaderSystem.BufferSpew( spewType, c, pMsg ); + + switch( spewType ) + { + case SPEW_MESSAGE: + case SPEW_WARNING: + case SPEW_LOG: + return SPEW_CONTINUE; + + case SPEW_ASSERT: + case SPEW_ERROR: + default: + return SPEW_DEBUGGER; + } +} + + +//----------------------------------------------------------------------------- +// Deals with shader draw +//----------------------------------------------------------------------------- +void CShaderSystem::PrepForShaderDraw( IShader *pShader, + IMaterialVar** ppParams, ShaderRenderState_t* pRenderState, int nModulation ) +{ + Assert( !m_pRenderState ); + + // 360 runs the console remotely, spew cannot cause the matsys to be reentrant + // 360 sidesteps the other negative affect that *all* buffered spew redirects as warning text + if ( IsPC() || !IsX360() ) + { + Assert( !m_SaveSpewOutput ); + m_SaveSpewOutput = GetSpewOutputFunc(); + SpewOutputFunc( MySpewOutputFunc ); + } + + m_pRenderState = pRenderState; + m_nModulation = nModulation; + m_nRenderPass = 0; +} + +void CShaderSystem::DoneWithShaderDraw() +{ + if ( IsPC() || !IsX360() ) + { + SpewOutputFunc( m_SaveSpewOutput ); + PrintBufferedSpew(); + m_SaveSpewOutput = NULL; + } + + m_pRenderState = NULL; +} + + +//----------------------------------------------------------------------------- +// Call the SHADER_PARAM_INIT block of the shaders +//----------------------------------------------------------------------------- +void CShaderSystem::InitShaderParameters( IShader *pShader, IMaterialVar **params, const char *pMaterialName ) +{ + // Let the derived class do its thing + PrepForShaderDraw( pShader, params, 0, 0 ); + pShader->InitShaderParams( params, pMaterialName ); + DoneWithShaderDraw(); + + // Set up color + alpha defaults + if (!params[COLOR]->IsDefined()) + { + params[COLOR]->SetVecValue( 1.0f, 1.0f, 1.0f ); + } + + if (!params[ALPHA]->IsDefined()) + { + params[ALPHA]->SetFloatValue( 1.0f ); + } + + // Initialize all shader params based on their type... + int i; + for ( i = pShader->GetNumParams(); --i >= 0; ) + { + // Don't initialize parameters that are already set up + if (params[i]->IsDefined()) + continue; + + int type = pShader->GetParamType( i ); + switch( type ) + { + case SHADER_PARAM_TYPE_TEXTURE: + // Do nothing; we'll be loading in a string later + break; + case SHADER_PARAM_TYPE_STRING: + // Do nothing; we'll be loading in a string later + break; + case SHADER_PARAM_TYPE_MATERIAL: + params[i]->SetMaterialValue( NULL ); + break; + case SHADER_PARAM_TYPE_BOOL: + case SHADER_PARAM_TYPE_INTEGER: + params[i]->SetIntValue( 0 ); + break; + case SHADER_PARAM_TYPE_COLOR: + params[i]->SetVecValue( 1.0f, 1.0f, 1.0f ); + break; + case SHADER_PARAM_TYPE_VEC2: + params[i]->SetVecValue( 0.0f, 0.0f ); + break; + case SHADER_PARAM_TYPE_VEC3: + params[i]->SetVecValue( 0.0f, 0.0f, 0.0f ); + break; + case SHADER_PARAM_TYPE_VEC4: + params[i]->SetVecValue( 0.0f, 0.0f, 0.0f, 0.0f ); + break; + case SHADER_PARAM_TYPE_FLOAT: + params[i]->SetFloatValue( 0 ); + break; + case SHADER_PARAM_TYPE_FOURCC: + params[i]->SetFourCCValue( 0, 0 ); + break; + case SHADER_PARAM_TYPE_MATRIX: + { + VMatrix identity; + MatrixSetIdentity( identity ); + params[i]->SetMatrixValue( identity ); + } + break; + case SHADER_PARAM_TYPE_MATRIX4X2: + { + VMatrix identity; + MatrixSetIdentity( identity ); + params[i]->SetMatrixValue( identity ); + } + break; + + + default: + Assert(0); + } + } +} + + +//----------------------------------------------------------------------------- +// Call the SHADER_INIT block of the shaders +//----------------------------------------------------------------------------- +void CShaderSystem::InitShaderInstance( IShader *pShader, IMaterialVar **params, const char *pMaterialName, const char *pTextureGroupName ) +{ + // Let the derived class do its thing + PrepForShaderDraw( pShader, params, 0, 0 ); + pShader->InitShaderInstance( params, ShaderSystem(), pMaterialName, pTextureGroupName ); + DoneWithShaderDraw(); +} + + +//----------------------------------------------------------------------------- +// Compute snapshots for all combinations of alpha + color modulation +//----------------------------------------------------------------------------- +void CShaderSystem::InitRenderStateFlags( ShaderRenderState_t* pRenderState, int numParams, IMaterialVar **params ) +{ + // Compute vertex format and flags + pRenderState->m_Flags = 0; + + // Make sure the shader don't force these flags. . they are automatically computed. + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) ); + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) ); + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) ); + + // If we are in release mode, just go ahead and clear in case the above is screwed up. + pRenderState->m_Flags &= ~SHADER_OPACITY_MASK; + +/* + // HACK: Also kind of gross; turn off bump lightmapping for low-end + if (g_config.bUseGraphics && !HardwareConfig()->SupportsVertexAndPixelShaders()) + { + pRenderState->m_Flags &= ~SHADER_NEEDS_BUMPED_LIGHTMAPS; + } +*/ +/* + // HACK: more grossness!!! turn off bump lightmapping if we don't have a bumpmap + // Shaders should specify SHADER_NEEDS_BUMPED_LIGHTMAPS if they might need a bumpmap, + // and this'll take care of getting rid of it if it isn't there. + if( pRenderState->m_Flags & SHADER_NEEDS_BUMPED_LIGHTMAPS ) + { + pRenderState->m_Flags &= ~SHADER_NEEDS_BUMPED_LIGHTMAPS; + for( int i = 0; i < numParams; i++ ) + { + if( stricmp( params[i]->GetName(), "$bumpmap" ) == 0 ) + { + if( params[i]->IsDefined() ) + { + const char *blah = params[i]->GetStringValue(); + pRenderState->m_Flags |= SHADER_NEEDS_BUMPED_LIGHTMAPS; + break; + } + } + } + } +*/ +} + + +//----------------------------------------------------------------------------- +// Computes flags from a particular snapshot +//----------------------------------------------------------------------------- +void CShaderSystem::ComputeRenderStateFlagsFromSnapshot( ShaderRenderState_t* pRenderState ) +{ + // When computing the flags, use the snapshot that has no alpha or color + // modulation. When asking for translucency, we'll have to check for + // alpha modulation in addition to checking the TRANSLUCENT flag. + + // I have to do it this way because I'm really wanting to treat alpha + // modulation as a dynamic state, even though it's being used to compute + // shadow state. I still want to use it to compute shadow state though + // because it's somewhat complicated code that I'd rather precache. + + StateSnapshot_t snapshot = pRenderState->m_pSnapshots[0].m_Snapshot[0]; + + // Automatically compute if the snapshot is transparent or not + if ( g_pShaderAPI->IsTranslucent( snapshot ) ) + { + pRenderState->m_Flags |= SHADER_OPACITY_TRANSLUCENT; + } + else + { + if ( g_pShaderAPI->IsAlphaTested( snapshot ) ) + { + pRenderState->m_Flags |= SHADER_OPACITY_ALPHATEST; + } + else + { + pRenderState->m_Flags |= SHADER_OPACITY_OPAQUE; + } + } + +#ifdef _DEBUG + if( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) + { + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) ); + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) ); + } + if( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) + { + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) ); + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) ); + } + if( pRenderState->m_Flags & SHADER_OPACITY_OPAQUE ) + { + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_ALPHATEST ) ); + Assert( !( pRenderState->m_Flags & SHADER_OPACITY_TRANSLUCENT ) ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Initializes state snapshots +//----------------------------------------------------------------------------- +#ifdef _DEBUG +#pragma warning (disable:4189) +#endif + +int CShaderSystem::GetModulationSnapshotCount( IMaterialVar **params ) +{ + int nSnapshotCount = SnapshotTypeCount(); + if ( !MaterialSystem()->CanUseEditorMaterials() ) + { + if( !IsFlag2Set( params, MATERIAL_VAR2_NEEDS_BAKED_LIGHTING_SNAPSHOTS ) ) + { + nSnapshotCount /= 2; + } + } + + return nSnapshotCount; +} + +void CShaderSystem::InitStateSnapshots( IShader *pShader, IMaterialVar **params, ShaderRenderState_t* pRenderState ) +{ +#ifdef _DEBUG + if ( IsFlagSet( params, MATERIAL_VAR_DEBUG ) ) + { + // Putcher breakpoint here to catch the rendering of a material + // marked for debugging ($debug = 1 in a .vmt file) shadow state version + int x = 0; + } +#endif + + // Store off the current alpha + color modulations + float alpha; + float color[3]; + params[COLOR]->GetVecValue( color, 3 ); + alpha = params[ALPHA]->GetFloatValue( ); + bool bBakedLighting = IsFlag2Set( params, MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); + bool bFlashlight = IsFlag2Set( params, MATERIAL_VAR2_USE_FLASHLIGHT ); + bool bEditor = IsFlag2Set( params, MATERIAL_VAR2_USE_EDITOR ); +// bool bSupportsFlashlight = IsFlag2Set( params, MATERIAL_VAR2_SUPPORTS_FLASHLIGHT ); + float white[3] = { 1, 1, 1 }; + float grey[3] = { .5, .5, .5 }; + + int nSnapshotCount = GetModulationSnapshotCount( params ); + + // If the current mod does not use the flashlight, skip all flashlight snapshots (saves a ton of memory) + bool bModUsesFlashlight = ( mat_supportflashlight.GetInt() != 0 ); + + for (int i = 0; i < nSnapshotCount; ++i) + { + if ( ( i & SHADER_USING_FLASHLIGHT ) && + !bModUsesFlashlight ) + { + pRenderState->m_pSnapshots[i].m_nPassCount = 0; + continue; + } + + // Set modulation to force particular code paths + if (i & SHADER_USING_COLOR_MODULATION) + { + params[COLOR]->SetVecValue( grey, 3 ); + } + else + { + params[COLOR]->SetVecValue( white, 3 ); + } + + if (i & SHADER_USING_ALPHA_MODULATION) + { + params[ALPHA]->SetFloatValue( grey[0] ); + } + else + { + params[ALPHA]->SetFloatValue( white[0] ); + } + + if ( i & SHADER_USING_FLASHLIGHT ) + { +// if ( !bSupportsFlashlight ) +// { +// pRenderState->m_pSnapshots[i].m_nPassCount = 0; +// continue; +// } + SET_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); + } + else + { + CLEAR_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); + } + + if ( i & SHADER_USING_EDITOR ) + { + SET_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); + } + else + { + CLEAR_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); + } + + if ( i & SHADER_USING_FIXED_FUNCTION_BAKED_LIGHTING ) + { + SET_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); + } + else + { + CLEAR_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); + } + + PrepForShaderDraw( pShader, params, pRenderState, i ); + + // Now snapshot how we're going to draw + pRenderState->m_pSnapshots[i].m_nPassCount = 0; + pShader->DrawElements( params, i, g_pShaderShadow, 0, VERTEX_COMPRESSION_NONE, &(pRenderState->m_pSnapshots[i].m_pContextData[0] ) ); + DoneWithShaderDraw(); + } + + // Restore alpha + color modulation + params[COLOR]->SetVecValue( color, 3 ); + params[ALPHA]->SetFloatValue( alpha ); + if( bBakedLighting ) + { + SET_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); + } + else + { + CLEAR_FLAGS2( MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING ); + } + + if( bEditor ) + { + SET_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); + } + else + { + CLEAR_FLAGS2( MATERIAL_VAR2_USE_EDITOR ); + } + + if( bFlashlight ) + { + SET_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); + } + else + { + CLEAR_FLAGS2( MATERIAL_VAR2_USE_FLASHLIGHT ); + } +} + +#ifdef _DEBUG +#pragma warning (default:4189) +#endif + +//----------------------------------------------------------------------------- +// Helper to count texture coordinates +//----------------------------------------------------------------------------- +static int NumTextureCoordinates( VertexFormat_t vertexFormat ) +{ + // FIXME: this is a duplicate of the function in meshdx8.cpp + int nTexCoordCount = 0; + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + if ( TexCoordSize( i, vertexFormat ) == 0 ) + continue; + ++nTexCoordCount; + } + return nTexCoordCount; +} + +//----------------------------------------------------------------------------- +// Displays the vertex format +//----------------------------------------------------------------------------- +static void OutputVertexFormat( VertexFormat_t format ) +{ + // FIXME: this is a duplicate of the function in meshdx8.cpp + VertexCompressionType_t compressionType = CompressionType( format ); + + if( format & VERTEX_POSITION ) + { + Warning( "VERTEX_POSITION|" ); + } + if( format & VERTEX_NORMAL ) + { + if ( compressionType == VERTEX_COMPRESSION_ON ) + Warning( "VERTEX_NORMAL[COMPRESSED]|" ); + else + Warning( "VERTEX_NORMAL|" ); + } + if( format & VERTEX_COLOR ) + { + Warning( "VERTEX_COLOR|" ); + } + if( format & VERTEX_SPECULAR ) + { + Warning( "VERTEX_SPECULAR|" ); + } + if( format & VERTEX_TANGENT_S ) + { + Warning( "VERTEX_TANGENT_S|" ); + } + if( format & VERTEX_TANGENT_T ) + { + Warning( "VERTEX_TANGENT_T|" ); + } + if( format & VERTEX_BONE_INDEX ) + { + Warning( "VERTEX_BONE_INDEX|" ); + } + if( format & VERTEX_FORMAT_VERTEX_SHADER ) + { + Warning( "VERTEX_FORMAT_VERTEX_SHADER|" ); + } + Warning( "\nBone weights: %d\n", NumBoneWeights( format ) ); + Warning( "user data size: %d (%s)\n", UserDataSize( format ), + ( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) ); + Warning( "num tex coords: %d\n", NumTextureCoordinates( format ) ); + // NOTE: This doesn't print texcoord sizes. +} + + +#ifdef _DEBUG +static bool IsVertexFormatSubsetOfVertexformat( VertexFormat_t subset, VertexFormat_t superset ) +{ + subset &= ~VERTEX_FORMAT_USE_EXACT_FORMAT; + superset &= ~VERTEX_FORMAT_USE_EXACT_FORMAT; + + // Test the flags + if( VertexFlags( subset ) & VertexFlags( ~superset ) ) + return false; + + // Test bone weights + if( NumBoneWeights( subset ) > NumBoneWeights( superset ) ) + return false; + + // Test user data size + if( UserDataSize( subset ) > UserDataSize( superset ) ) + return false; + + // Test the texcoord dimensions + for( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) + { + if( TexCoordSize( i, subset ) > TexCoordSize( i, superset ) ) + return false; + } + + return true; +} +#endif + + +//----------------------------------------------------------------------------- +// Adds state snapshots to the render list +//----------------------------------------------------------------------------- +static void AddSnapshotsToList( RenderPassList_t *pPassList, int &nSnapshotID, StateSnapshot_t *pSnapshots ) +{ + int nNumPassSnapshots = pPassList->m_nPassCount; + for( int i = 0; i < nNumPassSnapshots; ++i ) + { + pSnapshots[nSnapshotID] = pPassList->m_Snapshot[i]; + nSnapshotID++; + } +} + + +//----------------------------------------------------------------------------- +// Computes vertex format + usage from a particular snapshot +//----------------------------------------------------------------------------- +bool CShaderSystem::ComputeVertexFormatFromSnapshot( IMaterialVar **params, ShaderRenderState_t* pRenderState ) +{ + // When computing the usage, use the snapshot that has no alpha or color + // modulation. We need the usage + format to be the same for all + // combinations of alpha + color modulation, though, or we are asking for + // trouble. + int nModulationSnapshotCount = GetModulationSnapshotCount( params ); + int numSnapshots = pRenderState->m_pSnapshots[0].m_nPassCount; + if (nModulationSnapshotCount >= SHADER_USING_FLASHLIGHT) + { + numSnapshots += pRenderState->m_pSnapshots[SHADER_USING_FLASHLIGHT].m_nPassCount; + } + if ( MaterialSystem()->CanUseEditorMaterials() ) + { + numSnapshots += pRenderState->m_pSnapshots[SHADER_USING_EDITOR].m_nPassCount; + } + + StateSnapshot_t* pSnapshots = (StateSnapshot_t*)stackalloc( + numSnapshots * sizeof(StateSnapshot_t) ); + + int snapshotID = 0; + AddSnapshotsToList( &pRenderState->m_pSnapshots[0], snapshotID, pSnapshots ); + if (nModulationSnapshotCount >= SHADER_USING_FLASHLIGHT) + { + AddSnapshotsToList( &pRenderState->m_pSnapshots[SHADER_USING_FLASHLIGHT], snapshotID, pSnapshots ); + } + if ( MaterialSystem()->CanUseEditorMaterials() ) + { + AddSnapshotsToList( &pRenderState->m_pSnapshots[SHADER_USING_EDITOR], snapshotID, pSnapshots ); + } + + Assert( snapshotID == numSnapshots ); + + pRenderState->m_VertexUsage = g_pShaderAPI->ComputeVertexUsage( numSnapshots, pSnapshots ); + pRenderState->m_MorphFormat = g_pShaderAPI->ComputeMorphFormat( numSnapshots, pSnapshots ); + +#ifdef _DEBUG + // Make sure all modulation combinations match vertex usage + for ( int mod = 1; mod < nModulationSnapshotCount; ++mod ) + { + int numSnapshotsTest = pRenderState->m_pSnapshots[mod].m_nPassCount; + StateSnapshot_t* pSnapshotsTest = (StateSnapshot_t*)_alloca( + numSnapshotsTest * sizeof(StateSnapshot_t) ); + + for (int i = 0; i < numSnapshotsTest; ++i) + { + pSnapshotsTest[i] = pRenderState->m_pSnapshots[mod].m_Snapshot[i]; + } + + VertexFormat_t usageTest = g_pShaderAPI->ComputeVertexUsage( numSnapshotsTest, pSnapshotsTest ); + Assert( IsVertexFormatSubsetOfVertexformat( usageTest, pRenderState->m_VertexUsage ) ); + } +#endif + + if ( IsPC() ) + { + pRenderState->m_VertexFormat = g_pShaderAPI->ComputeVertexFormat( numSnapshots, pSnapshots ); + } + else + { + pRenderState->m_VertexFormat = pRenderState->m_VertexUsage; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// go through each param and make sure it is the right type, load textures, +// compute state snapshots and vertex types, etc. +//----------------------------------------------------------------------------- +bool CShaderSystem::InitRenderState( IShader *pShader, int numParams, IMaterialVar **params, ShaderRenderState_t* pRenderState, char const* pMaterialName ) +{ + Assert( !m_pRenderState ); + + // Initialize render state flags + InitRenderStateFlags( pRenderState, numParams, params ); + + // Compute state snapshots for each combination of alpha + color + InitStateSnapshots( pShader, params, pRenderState ); + + // Compute other infomation for the render state based on snapshots + if (pRenderState->m_pSnapshots[0].m_nPassCount == 0) + { + Warning( "Material \"%s\":\n No render states in shader \"%s\"\n", pMaterialName, pShader->GetName() ); + return false; + } + + // Set a couple additional flags based on the render state + ComputeRenderStateFlagsFromSnapshot( pRenderState ); + + // Compute the vertex format + usage from the snapshot + if ( !ComputeVertexFormatFromSnapshot( params, pRenderState ) ) + { + // warn.. return a null render state... + Warning("Material \"%s\":\n Shader \"%s\" can't be used with models!\n", pMaterialName, pShader->GetName() ); + CleanupRenderState( pRenderState ); + return false; + } + return true; +} + +// When you're done with the shader, be sure to call this to clean up +void CShaderSystem::CleanupRenderState( ShaderRenderState_t* pRenderState ) +{ + if (pRenderState) + { + int nSnapshotCount = SnapshotTypeCount(); + // kill context data + // Indicate no passes for any of the snapshot lists + RenderPassList_t *pTemp = pRenderState->m_pSnapshots; + for(int i = 0; i < nSnapshotCount; i++ ) + { + for(int j = 0 ; j < pRenderState->m_pSnapshots[i].m_nPassCount; j++ ) + if ( pTemp[i].m_pContextData[j] ) + { + delete pTemp[i].m_pContextData[j]; + pTemp[i].m_pContextData[j] = NULL; + } + pRenderState->m_pSnapshots[i].m_nPassCount = 0; + } + } +} + + +//----------------------------------------------------------------------------- +// Does the rendering! +//----------------------------------------------------------------------------- +void CShaderSystem::DrawElements( IShader *pShader, IMaterialVar **params, + ShaderRenderState_t* pRenderState, + VertexCompressionType_t vertexCompression, + uint32 nMaterialVarChangeTimeStamp ) +{ + VPROF("CShaderSystem::DrawElements"); + + g_pShaderAPI->InvalidateDelayedShaderConstants(); + // Compute modulation... + int mod = pShader->ComputeModulationFlags( params, g_pShaderAPI ); + + // No snapshots? do nothing. + if ( pRenderState->m_pSnapshots[mod].m_nPassCount == 0 ) + return; + + // If we're rendering a model, gotta have skinning matrices + int materialVarFlags = params[FLAGS]->GetIntValue(); + if ( (( materialVarFlags & MATERIAL_VAR_MODEL ) != 0) || + ( IsFlag2Set( params, MATERIAL_VAR2_SUPPORTS_HW_SKINNING ) && ( g_pShaderAPI->GetCurrentNumBones() > 0 )) ) + { + g_pShaderAPI->SetSkinningMatrices( ); + } + + // FIXME: need one conditional that we calculate once a frame for debug or not with everything debug under that. +#ifndef DX_TO_GL_ABSTRACTION + if ( ( ( g_config.bMeasureFillRate || g_config.bVisualizeFillRate ) && + ( ( materialVarFlags & MATERIAL_VAR_USE_IN_FILLRATE_MODE ) == 0 ) ) ) + { + DrawMeasureFillRate( pRenderState, mod, vertexCompression ); + } + else +#endif + if( ( g_config.bShowNormalMap || g_config.nShowMipLevels == 2 ) && + ( IsFlag2Set( params, MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ) || + IsFlag2Set( params, MATERIAL_VAR2_DIFFUSE_BUMPMAPPED_MODEL ) ) ) + { + DrawNormalMap( pShader, params, vertexCompression ); + } +#if defined(DEBUG_DEPTH) + else if ( mat_debugdepth.GetInt() && ((materialVarFlags & MATERIAL_VAR_NO_DEBUG_OVERRIDE) == 0) ) + { + int nIndex = 0; + if ( IsFlagSet( params, MATERIAL_VAR_DECAL ) ) + { + nIndex |= 0x1; + } + IMaterialInternal *pDebugMaterial = m_pDebugMaterials[ MATERIAL_DEBUG_DEPTH + nIndex ]; + if ( !g_pShaderAPI->IsDepthWriteEnabled( pRenderState->m_Snapshots[mod].m_Snapshot[0] ) ) + { + pDebugMaterial = m_pDebugMaterials[MATERIAL_DEBUG_WIREFRAME]; + } + + DrawUsingMaterial( pDebugMaterial, vertexCompression ); + } +#endif + else + { + g_pShaderAPI->SetDefaultState(); + + // If we're rendering flat, turn on flat mode... + if (materialVarFlags & MATERIAL_VAR_FLAT) + { + g_pShaderAPI->ShadeMode( SHADER_FLAT ); + } + + PrepForShaderDraw( pShader, params, pRenderState, mod ); + g_pShaderAPI->BeginPass( CurrentStateSnapshot() ); + + CBasePerMaterialContextData ** pContextDataPtr = + &( m_pRenderState->m_pSnapshots[m_nModulation].m_pContextData[m_nRenderPass] ); + + if ( *pContextDataPtr && ( (*pContextDataPtr)->m_nVarChangeID != nMaterialVarChangeTimeStamp ) ) + { + (*pContextDataPtr)->m_bMaterialVarsChanged = true; + (*pContextDataPtr)->m_nVarChangeID = nMaterialVarChangeTimeStamp; + } + + pShader->DrawElements( + params, mod, 0, g_pShaderAPI, vertexCompression, + &( m_pRenderState->m_pSnapshots[m_nModulation].m_pContextData[m_nRenderPass] ) ); + DoneWithShaderDraw(); + } + + MaterialSystem()->ForceDepthFuncEquals( false ); +} + + +//----------------------------------------------------------------------------- +// Are we using graphics? +//----------------------------------------------------------------------------- +bool CShaderSystem::IsUsingGraphics() const +{ + // YWB Hack if running with -noshaderapi/-makereslists this forces materials to "precache" which means they will resolve their .vtf files for + // things like normal/height/dudv maps... + if ( m_bForceUsingGraphicsReturnTrue ) + return true; + + return g_pShaderDevice->IsUsingGraphics(); +} + + +//----------------------------------------------------------------------------- +// Are we using the editor materials? +//----------------------------------------------------------------------------- +bool CShaderSystem::CanUseEditorMaterials() const +{ + return MaterialSystem()->CanUseEditorMaterials(); +} + + +//----------------------------------------------------------------------------- +// Takes a snapshot +//----------------------------------------------------------------------------- +void CShaderSystem::TakeSnapshot( ) +{ + Assert( m_pRenderState ); + Assert( m_nModulation < SnapshotTypeCount() ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + //enable linear->gamma srgb conversion lookup texture + g_pShaderShadow->EnableTexture( SHADER_SAMPLER15, true ); + g_pShaderShadow->EnableSRGBRead( SHADER_SAMPLER15, true ); + } + + RenderPassList_t& snapshotList = m_pRenderState->m_pSnapshots[m_nModulation]; + + // Take a snapshot... + snapshotList.m_Snapshot[snapshotList.m_nPassCount] = g_pShaderAPI->TakeSnapshot(); + ++snapshotList.m_nPassCount; +} + + +//----------------------------------------------------------------------------- +// Draws a snapshot +//----------------------------------------------------------------------------- +void CShaderSystem::DrawSnapshot( bool bMakeActualDrawCall ) +{ + Assert( m_pRenderState ); + RenderPassList_t& snapshotList = m_pRenderState->m_pSnapshots[m_nModulation]; + + int nPassCount = snapshotList.m_nPassCount; + Assert( m_nRenderPass < nPassCount ); + + if ( bMakeActualDrawCall ) + { + g_pShaderAPI->RenderPass( m_nRenderPass, nPassCount ); + } + + g_pShaderAPI->InvalidateDelayedShaderConstants(); + if (++m_nRenderPass < nPassCount) + { + g_pShaderAPI->BeginPass( CurrentStateSnapshot() ); + } +} + + + +//----------------------------------------------------------------------------- +// +// Debugging material methods below +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Draws a using a particular material.. +//----------------------------------------------------------------------------- +void CShaderSystem::DrawUsingMaterial( IMaterialInternal *pMaterial, VertexCompressionType_t vertexCompression ) +{ + ShaderRenderState_t *pRenderState = pMaterial->GetRenderState(); + g_pShaderAPI->SetDefaultState( ); + + IShader *pShader = pMaterial->GetShader(); + int nMod = pShader->ComputeModulationFlags( pMaterial->GetShaderParams(), g_pShaderAPI ); + PrepForShaderDraw( pShader, pMaterial->GetShaderParams(), pRenderState, nMod ); + g_pShaderAPI->BeginPass( pRenderState->m_pSnapshots[nMod].m_Snapshot[0] ); + pShader->DrawElements( pMaterial->GetShaderParams(), nMod, 0, g_pShaderAPI, vertexCompression, + &( pRenderState->m_pSnapshots[nMod].m_pContextData[0] ) ); + DoneWithShaderDraw( ); +} + + +//----------------------------------------------------------------------------- +// Copies material vars +//----------------------------------------------------------------------------- +void CShaderSystem::CopyMaterialVarToDebugShader( IMaterialInternal *pDebugMaterial, IShader *pShader, IMaterialVar **ppParams, const char *pSrcVarName, const char *pDstVarName ) +{ + bool bFound; + IMaterialVar *pMaterialVar = pDebugMaterial->FindVar( pDstVarName ? pDstVarName : pSrcVarName, &bFound ); + Assert( bFound ); + + for( int i = pShader->GetNumParams(); --i >= 0; ) + { + if( !Q_stricmp( ppParams[i]->GetName( ), pSrcVarName ) ) + { + pMaterialVar->CopyFrom( ppParams[i] ); + return; + } + } + + pMaterialVar->SetUndefined(); +} + + +//----------------------------------------------------------------------------- +// Draws the puppy in fill rate mode... +//----------------------------------------------------------------------------- +void CShaderSystem::DrawMeasureFillRate( ShaderRenderState_t* pRenderState, int mod, VertexCompressionType_t vertexCompression ) +{ + int nPassCount = pRenderState->m_pSnapshots[mod].m_nPassCount; + + // We require the use of a vertex shader rather than fixed function transforms + Assert( (VertexFlags(pRenderState->m_VertexFormat) & VERTEX_FORMAT_VERTEX_SHADER) != 0 ); + + IMaterialInternal *pMaterial = m_pDebugMaterials[ MATERIAL_FILL_RATE ]; + + bool bFound; + IMaterialVar *pMaterialVar = pMaterial->FindVar( "$passcount", &bFound ); + pMaterialVar->SetIntValue( nPassCount ); + DrawUsingMaterial( pMaterial, vertexCompression ); +} + + +//----------------------------------------------------------------------------- +// Draws normalmaps +//----------------------------------------------------------------------------- +void CShaderSystem::DrawNormalMap( IShader *pShader, IMaterialVar **ppParams, VertexCompressionType_t vertexCompression ) +{ + IMaterialInternal *pDebugMaterial = m_pDebugMaterials[MATERIAL_DEBUG_NORMALMAP]; + + if( !g_config.m_bFastNoBump ) + { + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpmap" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpframe" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumptransform" ); + } + else + { + bool bFound; + IMaterialVar *pMaterialVar = pDebugMaterial->FindVar( "$bumpmap", &bFound ); + Assert( bFound ); + + pMaterialVar->SetUndefined(); + } + + DrawUsingMaterial( pDebugMaterial, vertexCompression ); +} + +//----------------------------------------------------------------------------- +// Draws envmapmask +//----------------------------------------------------------------------------- +bool CShaderSystem::DrawEnvmapMask( IShader *pShader, IMaterialVar **ppParams, + ShaderRenderState_t* pRenderState, VertexCompressionType_t vertexCompression ) +{ + // FIXME! Make this work with fixed function. + int vertexFormat = pRenderState->m_VertexFormat; + bool bUsesVertexShader = (VertexFlags(vertexFormat) & VERTEX_FORMAT_VERTEX_SHADER) != 0; + if( !bUsesVertexShader ) + { + Assert( 0 ); + return false; + } + IMaterialInternal *pDebugMaterial = m_pDebugMaterials[ MATERIAL_DEBUG_ENVMAPMASK ]; + + bool bFound; + IMaterialVar *pMaterialVar = pDebugMaterial->FindVar( "$showalpha", &bFound ); + Assert( bFound ); + + if( IsFlagSet( ppParams, MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ) ) + { + // $bumpmap + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpmap", "$basetexture" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumpframe", "$frame" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$bumptransform", "$basetexturetransform" ); + pMaterialVar->SetIntValue( 1 ); + } + else if( IsFlagSet( ppParams, MATERIAL_VAR_BASEALPHAENVMAPMASK ) ) + { + // $basealphaenvmapmask + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$basetexture" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$frame" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$basetexturetransform" ); + pMaterialVar->SetIntValue( 1 ); + } + else + { + // $envmapmask + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$envmapmask", "$basetexture" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$envmapmaskframe", "$frame" ); + CopyMaterialVarToDebugShader( pDebugMaterial, pShader, ppParams, "$envmapmasktransform", "$basetexturetransform" ); + pMaterialVar->SetIntValue( 0 ); + } + + if( pDebugMaterial->FindVar( "$basetexture", NULL )->IsTexture() ) + { + DrawUsingMaterial( pDebugMaterial, vertexCompression ); + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +// +// Methods of IShaderSystem lie below +// +//----------------------------------------------------------------------------- + + +ShaderAPITextureHandle_t CShaderSystem::GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel ) +{ + Assert( !IsTextureInternalEnvCubemap( static_cast(pTexture) ) ); + + // Bind away baby + if( pTexture ) + { + // This is ugly. Basically, this is yet another way that textures can be bound. They don't get bound here, + // but the return is only used to bind them for semistatic command buffer building, which doesn't go through + // CTexture::Bind for whatever reason. So let's request the mipmaps here. If you run into this, in a situation + // where we shouldn't be doing the request, we could relocate this code to the appropriate callsites instead. + ITextureInternal* pTex = assert_cast< ITextureInternal* >( pTexture ); + TextureManager()->RequestAllMipmaps( pTex ); + + return pTex->GetTextureHandle( nFrame, nTextureChannel ); + } + else + return INVALID_SHADERAPI_TEXTURE_HANDLE; +} + +//----------------------------------------------------------------------------- +// Binds a texture +//----------------------------------------------------------------------------- +void CShaderSystem::BindTexture( Sampler_t sampler1, ITexture *pTexture, int nFrame /* = 0 */ ) +{ + // The call to IMaterialVar::GetTextureValue should have converted this to a real thing + Assert( !IsTextureInternalEnvCubemap( static_cast(pTexture) ) ); + + // Bind away baby + if( pTexture ) + { + static_cast(pTexture)->Bind( sampler1, nFrame ); + } +} + + +void CShaderSystem::BindTexture( Sampler_t sampler1, Sampler_t sampler2, ITexture *pTexture, int nFrame /* = 0 */ ) +{ + // The call to IMaterialVar::GetTextureValue should have converted this to a real thing + Assert( !IsTextureInternalEnvCubemap( static_cast(pTexture) ) ); + + // Bind away baby + if( pTexture ) + { + if ( sampler2 == Sampler_t(-1) ) + { + static_cast(pTexture)->Bind( sampler1, nFrame ); + } + else + { + static_cast(pTexture)->Bind( sampler1, nFrame, sampler2 ); + } + } +} + + +//----------------------------------------------------------------------------- +// +// Methods of IShaderInit lie below +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Loads a texture +//----------------------------------------------------------------------------- +void CShaderSystem::LoadTexture( IMaterialVar *pTextureVar, const char *pTextureGroupName, int nAdditionalCreationFlags /* = 0 */ ) +{ + if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_STRING) + { + // This here will cause 'UNDEFINED' material vars + if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) + { + pTextureVar->SetTextureValue( TextureManager()->ErrorTexture() ); + } + return; + } + + // In this case, we have to convert the string into a texture value + const char *pName = pTextureVar->GetStringValue(); + + // Fix cases where people stupidly put a slash at the front of the vtf filename in a vmt. Causes trouble elsewhere. + if ( pName[0] == CORRECT_PATH_SEPARATOR || pName[1] == CORRECT_PATH_SEPARATOR ) + ++pName; + + ITextureInternal *pTexture; + + // Force local cubemaps when using the editor + if ( MaterialSystem()->CanUseEditorMaterials() && ( stricmp( pName, "env_cubemap" ) == 0 ) ) + { + pTexture = (ITextureInternal*)-1; + } + else + { + pTexture = static_cast< ITextureInternal * >( MaterialSystem()->FindTexture( pName, pTextureGroupName, false, nAdditionalCreationFlags ) ); + } + + if( !pTexture ) + { + if( !g_pShaderDevice->IsUsingGraphics() && ( stricmp( pName, "env_cubemap" ) != 0 ) ) + { + Warning( "Shader_t::LoadTexture: texture \"%s.vtf\" doesn't exist\n", pName ); + } + pTexture = TextureManager()->ErrorTexture(); + } + + pTextureVar->SetTextureValue( pTexture ); +} + + +//----------------------------------------------------------------------------- +// Loads a bumpmap +//----------------------------------------------------------------------------- +void CShaderSystem::LoadBumpMap( IMaterialVar *pTextureVar, const char *pTextureGroupName ) +{ + Assert( pTextureVar ); + + if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_STRING) + { + // This here will cause 'UNDEFINED' material vars + if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) + { + pTextureVar->SetTextureValue( TextureManager()->ErrorTexture() ); + } + return; + } + + // Convert a string to the actual texture + ITexture *pTexture; + pTexture = MaterialSystem()->FindTexture( pTextureVar->GetStringValue(), pTextureGroupName, false, 0 ); + + // FIXME: Make a bumpmap error texture + if (!pTexture) + { + pTexture = TextureManager()->ErrorTexture(); + } + + pTextureVar->SetTextureValue( pTexture ); +} + + +//----------------------------------------------------------------------------- +// Loads a cubemap +//----------------------------------------------------------------------------- +void CShaderSystem::LoadCubeMap( IMaterialVar **ppParams, IMaterialVar *pTextureVar, int nAdditionalCreationFlags /* = 0 */ ) +{ + if ( !HardwareConfig()->SupportsCubeMaps() ) + return; + + if ( pTextureVar->GetType() != MATERIAL_VAR_TYPE_STRING ) + { + // This here will cause 'UNDEFINED' material vars + if (pTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) + { + pTextureVar->SetTextureValue( TextureManager()->ErrorTexture() ); + } + return; + } + + if ( stricmp( pTextureVar->GetStringValue(), "env_cubemap" ) == 0 ) + { + // garymcthack + // don't have to load anything here. . just set the texture value to something + // special that says to use the cubemap entity. + pTextureVar->SetTextureValue( ( ITexture * )-1 ); + SetFlags2( ppParams, MATERIAL_VAR2_USES_ENV_CUBEMAP ); + } + else + { + ITexture *pTexture; + char textureName[MAX_PATH]; + Q_strncpy( textureName, pTextureVar->GetStringValue(), MAX_PATH ); + if ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE ) + { + // Overload the texture name to ".hdr.vtf" (instead of .vtf) if we are running with + // HDR enabled. + Q_strncat( textureName, ".hdr", MAX_PATH, COPY_ALL_CHARACTERS ); + } + pTexture = MaterialSystem()->FindTexture( textureName, TEXTURE_GROUP_CUBE_MAP, false, nAdditionalCreationFlags ); + + // FIXME: Make a cubemap error texture + if ( !pTexture ) + { + pTexture = TextureManager()->ErrorTexture(); + } + + pTextureVar->SetTextureValue( pTexture ); + } +} + + + -- cgit v1.2.3