diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /materialsystem/checkmaterials.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'materialsystem/checkmaterials.cpp')
| -rw-r--r-- | materialsystem/checkmaterials.cpp | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/materialsystem/checkmaterials.cpp b/materialsystem/checkmaterials.cpp new file mode 100644 index 0000000..30432f8 --- /dev/null +++ b/materialsystem/checkmaterials.cpp @@ -0,0 +1,664 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "pch_materialsystem.h" + +// NOTE: currently this file is marked as "exclude from build" + +//#define _CHECK_MATERIALS_FOR_PROBLEMS 1 +#ifdef _CHECK_MATERIALS_FOR_PROBLEMS +#include "vtf/vtf.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlstring.h" +void CheckMateralsInDirectoryRecursive( const char *pRoot, const char *pDirectory ); +#endif + +#ifdef _CHECK_MATERIALS_FOR_PROBLEMS + +//----------------------------------------------------------------------------- +// Does a texture have alpha? +//----------------------------------------------------------------------------- +static bool DoesTextureUseAlpha( const char *pTextureName, const char *pMaterialName ) +{ + if ( IsX360() ) + { + // not supporting + return false; + } + + // Special textures start with '_'.. + if ( pTextureName[0] == '_' ) + return false; + + // The texture name doubles as the relative file name + // It's assumed to have already been set by this point + // Compute the cache name + char pCacheFileName[MATERIAL_MAX_PATH]; + Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s.vtf", pTextureName ); + + CUtlBuffer buf; + FileHandle_t fileHandle = g_pFullFileSystem->Open( pCacheFileName, "rb" ); + if ( fileHandle == FILESYSTEM_INVALID_HANDLE) + { + Warning( "Material \"%s\": can't open texture \"%s\"\n", pMaterialName, pCacheFileName ); + return false; + } + + // Check the .vtf for an alpha channel + IVTFTexture *pVTFTexture = CreateVTFTexture(); + + int nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION ); + buf.EnsureCapacity( nHeaderSize ); + + // read the header first.. it's faster!! + g_pFullFileSystem->Read( buf.Base(), nHeaderSize, fileHandle ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, nHeaderSize ); + + // Unserialize the header + bool bUsesAlpha = false; + + if (!pVTFTexture->Unserialize( buf, true )) + { + Warning( "Error reading material \"%s\"\n", pCacheFileName ); + g_pFullFileSystem->Close(fileHandle); + } + else + { + if ( pVTFTexture->Flags() & (TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA) ) + { + bUsesAlpha = true; + } + } + + DestroyVTFTexture( pVTFTexture ); + g_pFullFileSystem->Close( fileHandle ); + return bUsesAlpha; +} + + +//----------------------------------------------------------------------------- +// Does a texture have alpha? +//----------------------------------------------------------------------------- +static bool DoesTextureUseNormal( const char *pTextureName, const char *pMaterialName, bool &bUsesAlpha, bool &bIsCompressed, int &nSizeInBytes ) +{ + nSizeInBytes = 0; + bUsesAlpha = false; + + if ( IsX360() ) + { + // not supporting + return false; + } + + // Special textures start with '_'.. + if ( !pTextureName || ( pTextureName[0] == '_' ) || ( pTextureName[0] == 0 ) ) + return false; + + // The texture name doubles as the relative file name + // It's assumed to have already been set by this point + // Compute the cache name + char pCacheFileName[MATERIAL_MAX_PATH]; + Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s.vtf", pTextureName ); + + CUtlBuffer buf; + FileHandle_t fileHandle = g_pFullFileSystem->Open( pCacheFileName, "rb" ); + if ( fileHandle == FILESYSTEM_INVALID_HANDLE) + { +// Warning( "Material \"%s\": can't open texture \"%s\"\n", pMaterialName, pCacheFileName ); + return false; + } + + // Check the .vtf for an alpha channel + IVTFTexture *pVTFTexture = CreateVTFTexture(); + + int nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION ); + buf.EnsureCapacity( nHeaderSize ); + + // read the header first.. it's faster!! + g_pFullFileSystem->Read( buf.Base(), nHeaderSize, fileHandle ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, nHeaderSize ); + + // Unserialize the header + bool bUsesNormal = false; + if ( !pVTFTexture->Unserialize( buf, true ) ) + { + Warning( "Error reading material \"%s\"\n", pCacheFileName ); + } + else + { + if ( pVTFTexture->Flags() & TEXTUREFLAGS_NORMAL ) + { + bUsesAlpha = false; + bUsesNormal = true; + bIsCompressed = ImageLoader::IsCompressed( pVTFTexture->Format() ) || ( pVTFTexture->Format() == IMAGE_FORMAT_A8 ); + nSizeInBytes = pVTFTexture->ComputeTotalSize(); + if ( pVTFTexture->Flags() & (TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA) ) + { + bUsesAlpha = true; + } + } + } + + DestroyVTFTexture( pVTFTexture ); + g_pFullFileSystem->Close( fileHandle ); + return bUsesNormal; +} + + +//----------------------------------------------------------------------------- +// Is this a real texture +//----------------------------------------------------------------------------- +static bool IsTexture( const char *pTextureName ) +{ + // Special textures start with '_'.. + if ( pTextureName[0] == '_' ) + return false; + + // The texture name doubles as the relative file name + // It's assumed to have already been set by this point + // Compute the cache name + char pCacheFileName[MATERIAL_MAX_PATH]; + Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s.vtf", pTextureName ); + + FileHandle_t fileHandle = g_pFullFileSystem->Open( pCacheFileName, "rb" ); + if ( fileHandle == FILESYSTEM_INVALID_HANDLE) + return false; + + g_pFullFileSystem->Close( fileHandle ); + return true; +} + + +//----------------------------------------------------------------------------- +// Scan material + all subsections for key +//----------------------------------------------------------------------------- +static float MaterialFloatKeyValue( KeyValues *pKeyValues, const char *pKeyName, float flDefault ) +{ + float flValue = pKeyValues->GetFloat( pKeyName, flDefault ); + if ( flValue != flDefault ) + return flValue; + + for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() ) + { + float flValue = MaterialFloatKeyValue( pSubKey, pKeyName, flDefault ); + if ( flValue != flDefault ) + return flValue; + } + + return flDefault; +} + +int ParseVectorFromKeyValueString( KeyValues *pKeyValue, const char *pMaterialName, float vecVal[4] ); + +static bool AsVectorsEqual( int nDim1, float *pVector1, int nDim2, float *pVector2 ) +{ + if ( nDim1 != nDim2 ) + return false; + + for ( int i = 0; i < nDim1; ++i ) + { + if ( fabs( pVector1[i] - pVector2[i] ) > 1e-3 ) + return false; + } + + return true; +} + +static bool MaterialVectorKeyValue( KeyValues *pKeyValues, const char *pKeyName, int nDefaultDim, float *pDefault, int *pDim, float *pVector ) +{ + int nDim; + float retVal[4]; + + KeyValues *pValue = pKeyValues->FindKey( pKeyName ); + if ( pValue ) + { + switch( pValue->GetDataType() ) + { + case KeyValues::TYPE_INT: + { + int nInt = pValue->GetInt(); + for ( int i = 0; i < 4; ++i ) + { + retVal[i] = nInt; + } + if ( !AsVectorsEqual( nDefaultDim, pDefault, nDefaultDim, retVal ) ) + { + *pDim = nDefaultDim; + memcpy( pVector, retVal, nDefaultDim * sizeof(float) ); + return true; + } + } + break; + + case KeyValues::TYPE_FLOAT: + { + float flFloat = pValue->GetFloat(); + for ( int i = 0; i < 4; ++i ) + { + retVal[i] = flFloat; + } + if ( !AsVectorsEqual( nDefaultDim, pDefault, nDefaultDim, retVal ) ) + { + *pDim = nDefaultDim; + memcpy( pVector, retVal, nDefaultDim * sizeof(float) ); + return true; + } + } + break; + + case KeyValues::TYPE_STRING: + { + nDim = ParseVectorFromKeyValueString( pValue, "", retVal ); + if ( !AsVectorsEqual( nDefaultDim, pDefault, nDim, retVal ) ) + { + *pDim = nDim; + memcpy( pVector, retVal, nDim * sizeof(float) ); + return true; + } + } + break; + } + } + + for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() ) + { + if ( MaterialVectorKeyValue( pSubKey, pKeyName, nDefaultDim, pDefault, &nDim, retVal ) ) + { + *pDim = nDim; + memcpy( pVector, retVal, nDim * sizeof(float) ); + return true; + } + } + + *pDim = nDefaultDim; + memcpy( pVector, pDefault, nDefaultDim * sizeof(float) ); + return false; +} + + +//----------------------------------------------------------------------------- +// Scan material + all subsections for key +//----------------------------------------------------------------------------- +static bool DoesMaterialHaveKey( KeyValues *pKeyValues, const char *pKeyName ) +{ + if ( pKeyValues->GetString( pKeyName, NULL ) != NULL ) + return true; + + for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() ) + { + if ( DoesMaterialHaveKey( pSubKey, pKeyName ) ) + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Scan all materials for errors +//----------------------------------------------------------------------------- +static int s_nNormalBytes; +static int s_nNormalCompressedBytes; +static int s_nNormalPalettizedBytes; +static int s_nNormalWithAlphaBytes; +static int s_nNormalWithAlphaCompressedBytes; + +struct VTFInfo_t +{ + CUtlString m_VTFName; + bool m_bFoundInVMT; +}; + +void CheckKeyValues( KeyValues *pKeyValues, CUtlVector<VTFInfo_t> &vtf ) +{ + for ( KeyValues *pSubKey = pKeyValues->GetFirstValue(); pSubKey; pSubKey = pSubKey->GetNextValue() ) + { + if ( pSubKey->GetDataType() != KeyValues::TYPE_STRING ) + continue; + + if ( IsTexture( pSubKey->GetString() ) ) + { + int nLen = Q_strlen( pSubKey->GetString() ) + 1; + char *pTemp = (char*)_alloca( nLen ); + memcpy( pTemp, pSubKey->GetString(), nLen ); + Q_FixSlashes( pTemp ); + + int nCount = vtf.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( Q_stricmp( vtf[i].m_VTFName, pTemp ) ) + continue; + vtf[i].m_bFoundInVMT = true; + break; + } + } + } + + for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() ) + { + CheckKeyValues( pSubKey, vtf ); + } +} + +void CheckMaterial( KeyValues *pKeyValues, const char *pRoot, const char *pFileName, CUtlVector<VTFInfo_t> &vtf ) +{ + const char *pShaderName = pKeyValues->GetName(); +/* + if ( Q_stristr( pShaderName, "Water" ) || + Q_stristr( pShaderName, "Eyeball" ) || + Q_stristr( pShaderName, "Shadow" ) || + Q_stristr( pShaderName, "Refract" ) || + Q_stristr( pShaderName, "Predator" ) || + Q_stristr( pShaderName, "ParticleSphere" ) || + Q_stristr( pShaderName, "DebugLuxels" ) || + Q_stristr( pShaderName, "GooInGlass" ) || + Q_stristr( pShaderName, "Modulate" ) || + Q_stristr( pShaderName, "UnlitTwoTexture" ) || + Q_stristr( pShaderName, "Cloud" ) || + Q_stristr( pShaderName, "WorldVertexTransition" ) || + Q_stristr( pShaderName, "DecalModulate" ) || + Q_stristr( pShaderName, "DecalBaseTimesLightmapAlphaBlendSelfIllum" ) || + Q_stristr( pShaderName, "Sprite" ) ) + { + return; + } + + // Check for alpha channels + const char *pBaseTextureName = pKeyValues->GetString( "$basetexture", NULL ); + if ( pBaseTextureName != NULL ) + { + if ( DoesTextureUseAlpha( pBaseTextureName, pFileName ) ) + { + float flAlpha = MaterialFloatKeyValue( pKeyValues, "$alpha", 1.0f ); + bool bHasVertexAlpha = DoesMaterialHaveKey( pKeyValues, "$vertexalpha" ); // Modulation always happens here whether we want it to or not + bool bHasAlphaTest = DoesMaterialHaveKey( pKeyValues, "$alphatest" ); + bool bHasTranslucent = DoesMaterialHaveKey( pKeyValues, "$translucent" ); + bool bHasSelfIllum = DoesMaterialHaveKey( pKeyValues, "$selfillum" ); + bool bHasBaseAlphaEnvMapMask = DoesMaterialHaveKey( pKeyValues, "$basealphaenvmapmask" ); + if ( (flAlpha == 1.0f) && !bHasVertexAlpha && !bHasAlphaTest && !bHasTranslucent && !bHasSelfIllum && !bHasBaseAlphaEnvMapMask ) + { + Warning("Material \"%s\": BASETEXTURE \"%s\"\n", pFileName, pBaseTextureName ); + } + } + } +*/ + +/* +// Check for bump, spec, and no normalmapalphaenvmapmask + const char *pBumpmapName = pKeyValues->GetString( "$bumpmap", NULL ); + if ( pBumpmapName != NULL ) + { + if ( DoesTextureUseAlpha( pBumpmapName, pFileName ) ) + { + bool bHasEnvmap = DoesMaterialHaveKey( pKeyValues, "$envmap" ); + bool bHasNormalMapAlphaEnvMapMask = DoesMaterialHaveKey( pKeyValues, "$normalmapalphaenvmapmask" ); + if ( !bHasEnvmap || !bHasNormalMapAlphaEnvMapMask ) + { + Warning("Material \"%s\": BUMPMAP \"%s\"\n", pFileName, pBumpmapName ); + } + } + } +*/ + +/* + if ( !Q_stristr( pShaderName, "LightmappedGeneric" ) && + !Q_stristr( pShaderName, "VertexLitGeneric" ) ) + { + return; + } + + if ( DoesMaterialHaveKey( pKeyValues, "$envmap" ) && DoesMaterialHaveKey( pKeyValues, "$bumpmap" ) ) + { + int nDim; + float retVal[4]; + float defaultVal[4] = { 1, 1, 1, 1 }; + + if ( MaterialVectorKeyValue( pKeyValues, "$envmaptint", 3, defaultVal, &nDim, retVal ) ) + { + Warning("ENVMAP + ENVMAPTINT : Material \"%s\"\n", pFileName ); + } +// else +// { +// Warning("ENVMAP only: Material \"%s\"\n", pFileName ); +// } + } + */ + +/* + if ( !Q_stristr( pShaderName, "Refract" ) ) + { + return; + } + + if ( !DoesMaterialHaveKey( pKeyValues, "$envmap" ) ) + { + bool bUsesAlpha, bIsCompressed, bIsPalettized; + int nSizeInBytes; + if ( DoesTextureUseNormal( pKeyValues->GetString( "$normalmap" ), + pFileName, bUsesAlpha, bIsCompressed, bIsPalettized, nSizeInBytes ) ) + { + if ( bIsCompressed ) + { + Warning("Bad : Material compressed \"%s\"\n", pFileName ); + } + else + { + Warning("Bad : Material \"%s\"\n", pFileName ); + } + } + } +*/ + +/* + if ( !Q_stristr( pShaderName, "WorldTwoTextureBlend" ) ) + { + return; + } + + if ( DoesMaterialHaveKey( pKeyValues, "$envmap" ) || + DoesMaterialHaveKey( pKeyValues, "$parallaxmap" ) || + DoesMaterialHaveKey( pKeyValues, "$bumpmap" ) || + DoesMaterialHaveKey( pKeyValues, "$vertexcolor" ) || + DoesMaterialHaveKey( pKeyValues, "$basetexture2" ) + ) + { + Warning("Bad : Material \"%s\"\n", pFileName ); + } +*/ + + for ( KeyValues *pSubKey = pKeyValues->GetFirstValue(); pSubKey; pSubKey = pSubKey->GetNextValue() ) + { +// Msg( " Checking %s\n", pSubKey->GetString() ); + if ( pSubKey->GetDataType() != KeyValues::TYPE_STRING ) + continue; + + bool bUsesAlpha, bIsCompressed; + int nSizeInBytes; + if ( DoesTextureUseNormal( pSubKey->GetString(), pFileName, bUsesAlpha, bIsCompressed, nSizeInBytes ) ) + { + if ( bUsesAlpha ) + { + if ( bIsCompressed ) + { + s_nNormalWithAlphaCompressedBytes += nSizeInBytes; + } + else + { + s_nNormalWithAlphaBytes += nSizeInBytes; + Msg( "Normal texture w alpha uncompressed %s\n", pSubKey->GetString() ); + } + } + else + { + if ( bIsCompressed ) + { + s_nNormalCompressedBytes += nSizeInBytes; + } + else + { + s_nNormalBytes += nSizeInBytes; + } + } + } + } + +/* + if ( !Q_stristr( pShaderName, "VertexLitGeneric" ) ) + return; + + if ( !DoesMaterialHaveKey( pKeyValues, "$envmap" ) && DoesMaterialHaveKey( pKeyValues, "$bumpmap" ) ) + { + Warning("BUMPMAP + no ENVMAP : Material \"%s\"\n", pFileName ); + } +*/ + +// CheckKeyValues( pKeyValues, vtf ); +} + +//----------------------------------------------------------------------------- +// Build list of all VTFs +//----------------------------------------------------------------------------- +void CheckVTFInDirectoryRecursive( const char *pRoot, const char *pDirectory, CUtlVector< VTFInfo_t > &vtf ) +{ +#define BUF_SIZE 1024 + char buf[BUF_SIZE]; + WIN32_FIND_DATA wfd; + HANDLE findHandle; + + sprintf( buf, "%s/%s/*.vtf", pRoot, pDirectory ); + + findHandle = FindFirstFile( buf, &wfd ); + if ( findHandle != INVALID_HANDLE_VALUE ) + { + do + { + int i = vtf.AddToTail( ); + + char buf[MAX_PATH]; + char buf2[MAX_PATH]; + Q_snprintf( buf, MAX_PATH, "%s/%s", pDirectory, wfd.cFileName ); + Q_FixSlashes( buf ); + + Q_StripExtension( buf, buf2, sizeof(buf2) ); + Assert( !Q_strnicmp( buf2, "materials\\", 10 ) ); + + vtf[i].m_VTFName = &buf2[10]; + vtf[i].m_bFoundInVMT = false; + + } while ( FindNextFile ( findHandle, &wfd ) ); + + FindClose ( findHandle ); + } + + // do subdirectories + sprintf( buf, "%s/%s/*.*", pRoot, pDirectory ); + findHandle = FindFirstFile( buf, &wfd ); + if ( findHandle != INVALID_HANDLE_VALUE ) + { + do + { + if( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + if( ( strcmp( wfd.cFileName, ".." ) == 0 ) || + ( strcmp( wfd.cFileName, "." ) == 0 ) ) + { + continue; + } + + char buf[MAX_PATH]; + Q_snprintf( buf, MAX_PATH, "%s/%s", pDirectory, wfd.cFileName ); + CheckVTFInDirectoryRecursive( pRoot, buf, vtf ); + } + } while ( FindNextFile ( findHandle, &wfd ) ); + FindClose ( findHandle ); + } + +#undef BUF_SIZE +} + + +//----------------------------------------------------------------------------- +// Scan all materials for errors +//----------------------------------------------------------------------------- +void _CheckMateralsInDirectoryRecursive( const char *pRoot, const char *pDirectory, CUtlVector< VTFInfo_t > &vtf ) +{ +#define BUF_SIZE 1024 + char buf[BUF_SIZE]; + WIN32_FIND_DATA wfd; + HANDLE findHandle; + + sprintf( buf, "%s/%s/*.vmt", pRoot, pDirectory ); + findHandle = FindFirstFile( buf, &wfd ); + if ( findHandle != INVALID_HANDLE_VALUE ) + { + do + { + KeyValues * vmtKeyValues = new KeyValues("vmt"); + + char pFileName[MAX_PATH]; + Q_snprintf( pFileName, sizeof( pFileName ), "%s/%s", pDirectory, wfd.cFileName ); + if ( !vmtKeyValues->LoadFromFile( g_pFullFileSystem, pFileName, "GAME" ) ) + { + Warning( "CheckMateralsInDirectoryRecursive: can't open \"%s\"\n", pFileName ); + continue; + } + + CheckMaterial( vmtKeyValues, pRoot, pFileName, vtf ); + + vmtKeyValues->deleteThis(); + + } while ( FindNextFile ( findHandle, &wfd ) ); + + FindClose ( findHandle ); + } + + // do subdirectories + sprintf( buf, "%s/%s/*.*", pRoot, pDirectory ); + findHandle = FindFirstFile( buf, &wfd ); + if ( findHandle != INVALID_HANDLE_VALUE ) + { + do + { + if( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + if( ( strcmp( wfd.cFileName, ".." ) == 0 ) || + ( strcmp( wfd.cFileName, "." ) == 0 ) ) + { + continue; + } + + char buf[MAX_PATH]; + Q_snprintf( buf, MAX_PATH, "%s/%s", pDirectory, wfd.cFileName ); + _CheckMateralsInDirectoryRecursive( pRoot, buf, vtf ); + } + } while ( FindNextFile ( findHandle, &wfd ) ); + FindClose ( findHandle ); + } + +// Msg( "Normal only %d/%d/%d Normal w alpha %d/%d\n", s_nNormalBytes, s_nNormalPalettizedBytes, s_nNormalCompressedBytes, s_nNormalWithAlphaBytes, s_nNormalWithAlphaCompressedBytes ); +#undef BUF_SIZE +} + +void CheckMateralsInDirectoryRecursive( const char *pRoot, const char *pDirectory ) +{ + CUtlVector< VTFInfo_t > vtfNames; +// CheckVTFInDirectoryRecursive( pRoot, pDirectory, vtfNames ); + _CheckMateralsInDirectoryRecursive( pRoot, pDirectory, vtfNames ); + + /* + int nCount = vtfNames.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( !vtfNames[i].m_bFoundInVMT ) + { + Msg( "Unused VTF %s\n", vtfNames[i].m_VTFName ); + } + } + */ +} + +#endif // _CHECK_MATERIALS_FOR_PROBLEMS + |