summaryrefslogtreecommitdiff
path: root/hammer/material.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/material.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/material.cpp')
-rw-r--r--hammer/material.cpp1419
1 files changed, 1419 insertions, 0 deletions
diff --git a/hammer/material.cpp b/hammer/material.cpp
new file mode 100644
index 0000000..f5543d0
--- /dev/null
+++ b/hammer/material.cpp
@@ -0,0 +1,1419 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implementation of IEditorTexture interface for materials.
+//
+// Materials are kept in a directory tree containing pairs of VMT
+// and VTF files. Each pair of files represents a material.
+//
+//=============================================================================//
+
+#include "stdafx.h"
+#include <process.h>
+#include <afxtempl.h>
+#include <io.h>
+#include <sys\stat.h>
+#include <fcntl.h>
+#include "hammer.h"
+#include "MapDoc.h"
+#include "Material.h"
+#include "Options.h"
+#include "MainFrm.h"
+#include "GlobalFunctions.h"
+#include "WADTypes.h"
+#include "BSPFile.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/IMaterialSystemHardwareConfig.h"
+#include "materialsystem/MaterialSystem_Config.h"
+#include "materialsystem/MaterialSystemUtil.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/imaterial.h"
+#include "bitmap/imageformat.h" // hack : don't want to include this just for ImageFormat
+#include "filesystem.h"
+#include "StudioModel.h"
+#include "tier1/strtools.h"
+#include "tier0/dbg.h"
+#include "TextureSystem.h"
+#include "materialproxyfactory_wc.h"
+#include "vstdlib/cvar.h"
+#include "interface.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+#pragma warning(disable:4244)
+
+#define _GraphicCacheAllocate(n) malloc(n)
+
+
+MaterialSystem_Config_t g_materialSystemConfig;
+static MaterialHandle_t g_CurrMaterial;
+
+extern void ScaleBitmap(CSize sizeSrc, CSize sizeDest, char *src, char *dest);
+
+
+struct MaterialCacheEntry_t
+{
+ char szName[MAX_PATH]; //
+ CMaterial *pMaterial; //
+ int nRefCount; //
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// This class speeds up the call to IMaterial::GetPreviewImageProperties because
+// we call it thousands of times per level load when there are detail props.
+//-----------------------------------------------------------------------------
+class CPreviewImagePropertiesCache
+{
+public:
+ //-----------------------------------------------------------------------------
+ // Purpose: Anyone can call this instead of IMaterial::GetPreviewImageProperties
+ // and it'll be a lot faster if there are redundant calls to it.
+ //-----------------------------------------------------------------------------
+ static PreviewImageRetVal_t GetPreviewImageProperties( IMaterial *pMaterial, int *width, int *height, ImageFormat *imageFormat, bool* isTranslucent )
+ {
+ int i = s_PreviewImagePropertiesCache.Find( pMaterial );
+ if ( i == s_PreviewImagePropertiesCache.InvalidIndex() )
+ {
+ // Add an entry to the cache.
+ CPreviewImagePropertiesCache::CEntry entry;
+ entry.m_RetVal = pMaterial->GetPreviewImageProperties( &entry.m_Width, &entry.m_Height, &entry.m_ImageFormat, &entry.m_bIsTranslucent );
+ i = s_PreviewImagePropertiesCache.Insert( pMaterial, entry );
+ }
+
+ CPreviewImagePropertiesCache::CEntry &entry = s_PreviewImagePropertiesCache[i];
+ *width = entry.m_Width;
+ *height = entry.m_Height;
+ *imageFormat = entry.m_ImageFormat;
+ *isTranslucent = entry.m_bIsTranslucent;
+
+ return entry.m_RetVal;
+ }
+
+private:
+
+ class CEntry
+ {
+ public:
+ int m_Width;
+ int m_Height;
+ ImageFormat m_ImageFormat;
+ bool m_bIsTranslucent;
+ PreviewImageRetVal_t m_RetVal;
+ };
+
+ static bool PreviewImageLessFunc( IMaterial* const &a, IMaterial* const &b )
+ {
+ return a < b;
+ }
+
+ static CUtlMap<IMaterial*, CPreviewImagePropertiesCache::CEntry> s_PreviewImagePropertiesCache;
+};
+CUtlMap<IMaterial*, CPreviewImagePropertiesCache::CEntry> CPreviewImagePropertiesCache::s_PreviewImagePropertiesCache( 64, 64, &CPreviewImagePropertiesCache::PreviewImageLessFunc );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: stuff for caching textures in memory.
+//-----------------------------------------------------------------------------
+class CMaterialImageCache
+{
+public:
+
+ CMaterialImageCache(int maxNumGraphicsLoaded);
+ ~CMaterialImageCache(void);
+ void EnCache( CMaterial *pMaterial );
+
+protected:
+
+ CMaterial **pool;
+ int cacheSize;
+ int currentID; // next one to get killed.
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor. Allocates a pool of material pointers.
+// Input : maxNumGraphicsLoaded -
+//-----------------------------------------------------------------------------
+CMaterialImageCache::CMaterialImageCache(int maxNumGraphicsLoaded)
+{
+ cacheSize = maxNumGraphicsLoaded;
+ pool = new CMaterialPtr[cacheSize];
+ if (pool != NULL)
+ {
+ memset(pool, 0, sizeof(CMaterialPtr) * cacheSize);
+ }
+ currentID = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor. Frees the pool memory.
+//-----------------------------------------------------------------------------
+CMaterialImageCache::~CMaterialImageCache(void)
+{
+ if (pool != NULL)
+ {
+ delete [] pool;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pMaterial -
+//-----------------------------------------------------------------------------
+void CMaterialImageCache::EnCache( CMaterial *pMaterial )
+{
+ if (pMaterial->m_pData != NULL)
+ {
+ // Already cached.
+ return;
+ }
+
+ // kill currentID
+ if ((pool[currentID]) && (pool[currentID]->HasData()))
+ {
+ pool[currentID]->FreeData();
+ }
+
+ pool[currentID] = pMaterial;
+ pMaterial->LoadMaterialImage();
+ currentID = ( currentID + 1 ) % cacheSize;
+
+#if 0
+ OutputDebugString( "CMaterialCache::Encache: " );
+ OutputDebugString( pMaterial->m_szName );
+ OutputDebugString( "\n" );
+#endif
+}
+
+
+static CMaterialImageCache *g_pMaterialImageCache = NULL;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CMaterialCache::CMaterialCache(void)
+{
+ m_pCache = NULL;
+ m_nMaxEntries = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CMaterialCache::~CMaterialCache(void)
+{
+ if (m_pCache != NULL)
+ {
+ delete m_pCache;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Allocates cache memory for a given number of materials.
+// Input : nMaxEntries - Maximum number of materials in the cache.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMaterialCache::Create(int nMaxEntries)
+{
+ Assert((m_pCache == NULL) && (m_nMaxEntries == 0));
+
+ if (m_pCache != NULL)
+ {
+ delete m_pCache;
+ m_pCache = NULL;
+ m_nMaxEntries = 0;
+ }
+
+ if (nMaxEntries <= 0)
+ {
+ nMaxEntries = 500;
+ }
+
+ m_pCache = new MaterialCacheEntry_t[nMaxEntries];
+
+ if (m_pCache != NULL)
+ {
+ memset(m_pCache, 0, sizeof(m_pCache[0]) * nMaxEntries);
+ m_nMaxEntries = nMaxEntries;
+ }
+
+ return(m_pCache != NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Factory. Creates a material by name, first looking in the cache.
+// Input : pszMaterialName - Name of material, ie "brick/brickfloor01".
+// Output : Returns a pointer to the new material object, NULL if the given
+// material did not exist.
+//-----------------------------------------------------------------------------
+CMaterial *CMaterialCache::CreateMaterial(const char *pszMaterialName)
+{
+ CMaterial *pMaterial = NULL;
+
+ if (pszMaterialName != NULL)
+ {
+ //
+ // Find this material in the cache. If it is here, return it.
+ //
+ pMaterial = FindMaterial(pszMaterialName);
+ if (pMaterial == NULL)
+ {
+ //
+ // Not found in the cache, try to create it.
+ //
+ pMaterial = CMaterial::CreateMaterial(pszMaterialName, true);
+ if (pMaterial != NULL)
+ {
+ //
+ // Success. Add the newly created material to the cache.
+ //
+ AddMaterial(pMaterial);
+ return(pMaterial);
+ }
+ }
+ else
+ {
+ //
+ // Found in the cache, bump the reference count.
+ //
+ AddRef(pMaterial);
+ }
+ }
+
+ return(pMaterial);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds a material in the cache.
+// Input : char *pszMaterialName -
+// Output : CMaterial
+//-----------------------------------------------------------------------------
+void CMaterialCache::AddMaterial(CMaterial *pMaterial)
+{
+ if (pMaterial != NULL)
+ {
+ Assert(m_nEntries < m_nMaxEntries);
+
+ if (m_nEntries < m_nMaxEntries)
+ {
+ m_pCache[m_nEntries].pMaterial = pMaterial;
+ m_pCache[m_nEntries].nRefCount = 1;
+ m_nEntries++;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Increments the reference count on a material in the cache. Called by
+// client code when a pointer to the model is copied, making that
+// reference independent.
+// Input : pModel - Model for which to increment the reference count.
+//-----------------------------------------------------------------------------
+void CMaterialCache::AddRef(CMaterial *pMaterial)
+{
+ for (int i = 0; i < m_nEntries; i++)
+ {
+ if (m_pCache[i].pMaterial == pMaterial)
+ {
+ m_pCache[i].nRefCount++;
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds a material in the cache by name.
+// Input : char *pszMaterialName -
+// Output : CMaterial
+//-----------------------------------------------------------------------------
+CMaterial *CMaterialCache::FindMaterial(const char *pszMaterialName)
+{
+ if (pszMaterialName != NULL)
+ {
+ for (int i = 0; i < m_nEntries; i++)
+ {
+ if (!stricmp(m_pCache[i].pMaterial->GetName(), pszMaterialName))
+ {
+ return(m_pCache[i].pMaterial);
+ }
+ }
+ }
+
+ return(NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decrements the reference count of a material, deleting it and
+// removing it from the cache if its reference count becomes zero.
+// Input : pMaterial - Material to release.
+//-----------------------------------------------------------------------------
+void CMaterialCache::Release(CMaterial *pMaterial)
+{
+ if (pMaterial != NULL)
+ {
+ for (int i = 0; i < m_nEntries; i++)
+ {
+ if (m_pCache[i].pMaterial == pMaterial)
+ {
+ m_pCache[i].nRefCount--;
+ if (m_pCache[i].nRefCount == 0)
+ {
+ delete m_pCache[i].pMaterial;
+
+ m_nEntries--;
+ m_pCache[i] = m_pCache[m_nEntries];
+
+ memset(&m_pCache[m_nEntries], 0, sizeof(m_pCache[0]));
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor. Initializes data members.
+//-----------------------------------------------------------------------------
+CMaterial::CMaterial(void)
+{
+ memset(m_szName, 0, sizeof(m_szName));
+ memset(m_szFileName, 0, sizeof(m_szFileName));
+ memset(m_szKeywords, 0, sizeof(m_szKeywords));
+
+ m_nWidth = 0;
+ m_nHeight = 0;
+ m_nTextureID = 0;
+ m_pData = NULL;
+ m_bLoaded = false;
+ m_pMaterial = NULL;
+ m_TranslucentBaseTexture = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor. Frees texture image data and palette.
+//-----------------------------------------------------------------------------
+CMaterial::~CMaterial(void)
+{
+ //
+ // Free image data.
+ //
+ if (m_pData != NULL)
+ {
+ free(m_pData);
+ m_pData = NULL;
+ }
+
+ /* FIXME: Texture manager shuts down after the material system
+ if (m_pMaterial)
+ {
+ m_pMaterial->DecrementReferenceCount();
+ m_pMaterial = NULL;
+ }
+ */
+}
+
+
+#define MATERIAL_PREFIX_LEN 10
+//-----------------------------------------------------------------------------
+// Finds all .VMT files in a particular directory
+//-----------------------------------------------------------------------------
+bool CMaterial::LoadMaterialsInDirectory( char const* pDirectoryName, int nDirectoryNameLen,
+ IMaterialEnumerator *pEnum, int nContext, int nFlags )
+{
+ //Assert( Q_strnicmp( pDirectoryName, "materials", 9 ) == 0 );
+
+ char *pWildCard;
+ pWildCard = ( char * )stackalloc( nDirectoryNameLen + 7 );
+ Q_snprintf( pWildCard, nDirectoryNameLen + 7, "%s/*.vmt", pDirectoryName );
+
+ if ( !g_pFileSystem )
+ {
+ return false;
+ }
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = g_pFullFileSystem->FindFirstEx( pWildCard, "GAME", &findHandle );
+ while( pFileName )
+ {
+ if (IsIgnoredMaterial(pFileName))
+ {
+ pFileName = g_pFullFileSystem->FindNext( findHandle );
+ continue;
+ }
+
+ if( !g_pFullFileSystem->FindIsDirectory( findHandle ) )
+ {
+ // Strip off the 'materials/' part of the material name.
+ char *pFileNameWithPath;
+ int nAllocSize = nDirectoryNameLen + Q_strlen(pFileName) + 2;
+ pFileNameWithPath = (char *)stackalloc( nAllocSize );
+ Q_snprintf( pFileNameWithPath, nAllocSize, "%s/%s", &pDirectoryName[MATERIAL_PREFIX_LEN], pFileName );
+ Q_strnlwr( pFileNameWithPath, nAllocSize );
+
+ // Strip off the extension...
+ char *pExt = Q_strrchr( pFileNameWithPath, '.');
+ if (pExt)
+ *pExt = 0;
+
+ if (!pEnum->EnumMaterial( pFileNameWithPath, nContext ))
+ {
+ return false;
+ }
+ }
+ pFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+ g_pFullFileSystem->FindClose( findHandle );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Discovers all .VMT files lying under a particular directory
+// It only finds their names so we can generate shell materials for them
+// that we can load up at a later time
+//-----------------------------------------------------------------------------
+bool CMaterial::InitDirectoryRecursive( char const* pDirectoryName,
+ IMaterialEnumerator *pEnum, int nContext, int nFlags )
+{
+ // Make sure this is an ok directory, otherwise don't bother
+ if (ShouldSkipMaterial( pDirectoryName + MATERIAL_PREFIX_LEN, nFlags ))
+ return true;
+
+ // Compute directory name length
+ int nDirectoryNameLen = Q_strlen( pDirectoryName );
+
+ if (!LoadMaterialsInDirectory( pDirectoryName, nDirectoryNameLen, pEnum, nContext, nFlags ))
+ return false;
+
+ char *pWildCard = ( char * )stackalloc( nDirectoryNameLen + 5 );
+ strcpy(pWildCard, pDirectoryName);
+ strcat(pWildCard, "/*.*");
+ int nPathStrLen = nDirectoryNameLen + 1;
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = g_pFullFileSystem->FindFirstEx( pWildCard, "GAME", &findHandle );
+ while( pFileName )
+ {
+ if (!IsIgnoredMaterial(pFileName))
+ {
+ if ((pFileName[0] != '.') || (pFileName[1] != '.' && pFileName[1] != 0))
+ {
+ if( g_pFullFileSystem->FindIsDirectory( findHandle ) )
+ {
+ int fileNameStrLen = Q_strlen( pFileName );
+ char *pFileNameWithPath = ( char * )stackalloc( nPathStrLen + fileNameStrLen + 1 );
+ memcpy( pFileNameWithPath, pWildCard, nPathStrLen );
+ pFileNameWithPath[nPathStrLen] = '\0';
+ Q_strncat( pFileNameWithPath, pFileName, nPathStrLen + fileNameStrLen + 1 );
+
+ if (!InitDirectoryRecursive( pFileNameWithPath, pEnum, nContext, nFlags ))
+ return false;
+ }
+ }
+ }
+ pFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Discovers all .VMT files lying under a particular directory
+// It only finds their names so we can generate shell materials for them
+// that we can load up at a later time
+//-----------------------------------------------------------------------------
+void CMaterial::EnumerateMaterials( IMaterialEnumerator *pEnum, const char *szRoot, int nContext, int nFlags )
+{
+ InitDirectoryRecursive( szRoot, pEnum, nContext, nFlags );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called from GetFirst/GetNextMaterialName to skip unwanted materials.
+// Input : pszName - Name of material to evaluate.
+// nFlags - One or more of the following:
+// INCLUDE_ALL_MATERIALS
+// INCLUDE_WORLD_MATERIALS
+// INCLUDE_MODEL_MATERIALS
+// Output : Returns true to skip, false to not skip this material.
+//-----------------------------------------------------------------------------
+bool CMaterial::ShouldSkipMaterial(const char *pszName, int nFlags)
+{
+ static char szStrippedName[MAX_PATH];
+
+ // if NULL skip it
+ if( !pszName )
+ return true;
+
+ //
+ // check against the list of user-defined exclusion directories
+ //
+ for( int i = 0; i < g_pGameConfig->m_MaterialExcludeCount; i++ )
+ {
+ // This will guarantee the match is at the start of the string
+ const char *pMatchFound = Q_stristr( pszName, g_pGameConfig->m_MaterialExclusions[i].szDirectory );
+ if( pMatchFound == pszName )
+ return true;
+ }
+ // also check against any FGD-defined exclusions
+ if (pGD != NULL)
+ {
+ for( int i = 0; i < pGD->m_FGDMaterialExclusions.Count(); i++ )
+ {
+ const char *pMatchFound = Q_stristr( pszName, pGD->m_FGDMaterialExclusions[i].szDirectory );
+ if( pMatchFound == pszName )
+ return true;
+ }
+ }
+
+ return false;
+
+#if 0
+ bool bSkip = false;
+
+ if (pszName != NULL)
+ {
+ if (!(nFlags & INCLUDE_MODEL_MATERIALS))
+ {
+ if (_strnicmp(pszName, "models/", 7) == 0)
+ {
+ bSkip = true;
+ }
+ }
+
+ if (!(nFlags & INCLUDE_WORLD_MATERIALS))
+ {
+ if (_strnicmp(pszName, "models/", 7) != 0)
+ {
+ bSkip = true;
+ }
+ }
+ }
+ else
+ {
+ bSkip = true;
+ }
+
+ return(bSkip);
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Factory. Creates a material by name.
+// Input : pszMaterialName - Name of material, ie "brick/brickfloor01".
+// Output : Returns a pointer to the new material object, NULL if the given
+// material did not exist.
+//-----------------------------------------------------------------------------
+CMaterial *CMaterial::CreateMaterial(const char *pszMaterialName, bool bLoadImmediately, bool* pFound)
+{
+ Assert (pszMaterialName);
+
+ CMaterial *pMaterial = new CMaterial;
+ Assert( pMaterial );
+
+ // Store off the material name so we can load it later if we need to
+ Q_snprintf( pMaterial->m_szFileName, MAX_PATH, pszMaterialName );
+ Q_snprintf( pMaterial->m_szName, MAX_PATH, pszMaterialName );
+
+ //
+ // Find the material by name and load it.
+ //
+ if (bLoadImmediately)
+ {
+ bool bFound = pMaterial->LoadMaterial();
+
+ // Returns if the material was found or not
+ if (pFound)
+ *pFound = bFound;
+ }
+
+ return pMaterial;
+}
+
+bool CMaterial::IsIgnoredMaterial( const char *pName )
+{
+ //TODO: make this a customizable user option?
+ if ( !Q_strnicmp(pName, ".svn", 4) || strstr (pName, ".svn") ||
+ !Q_strnicmp(pName, "models", 6) || strstr (pName, "models") )
+ return true;
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+// Will actually load the material bits
+// We don't want to load them all at once because it takes way too long
+//-----------------------------------------------------------------------------
+bool CMaterial::LoadMaterial()
+{
+ bool bFound = true;
+ if (!m_bLoaded)
+ {
+ if (IsIgnoredMaterial(m_szFileName))
+ {
+ return false;
+ }
+
+ m_bLoaded = true;
+
+ IMaterial *pMat = materials->FindMaterial(m_szFileName, TEXTURE_GROUP_OTHER);
+ if ( IsErrorMaterial( pMat ) )
+ bFound = false;
+
+ Assert( pMat );
+
+ if (!pMat)
+ {
+ return false;
+ }
+
+ if (!LoadMaterialHeader(pMat))
+ {
+ // dvs: yeaaaaaaaaah, we're gonna disable this until the spew can be reduced
+ //Msg( mwError,"Load material header failed: %s", m_szFileName );
+
+ bFound = false;
+ pMat = materials->FindMaterial("debug/debugempty", TEXTURE_GROUP_OTHER);
+
+ if (pMat)
+ {
+ LoadMaterialHeader(pMat);
+ }
+ }
+ }
+
+ return bFound;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reloads owing to a material change
+//-----------------------------------------------------------------------------
+void CMaterial::Reload( bool bFullReload )
+{
+ // Don't bother if we're not loaded yet
+ if (!m_bLoaded)
+ return;
+
+ FreeData();
+
+ if ( m_pMaterial )
+ {
+ m_pMaterial->DecrementReferenceCount();
+ }
+ m_pMaterial = materials->FindMaterial(m_szFileName, TEXTURE_GROUP_OTHER);
+ Assert( m_pMaterial );
+
+ if ( bFullReload )
+ m_pMaterial->Refresh();
+
+ PreviewImageRetVal_t retVal;
+ bool translucentBaseTexture;
+ ImageFormat eImageFormat;
+ int width, height;
+ retVal = m_pMaterial->GetPreviewImageProperties(&width, &height, &eImageFormat, &translucentBaseTexture);
+ if (retVal == MATERIAL_PREVIEW_IMAGE_BAD)
+ return;
+
+ m_nWidth = width;
+ m_nHeight = height;
+ m_TranslucentBaseTexture = translucentBaseTexture;
+
+ // Find the keywords for this material from the vmt file.
+ bool bFound;
+ IMaterialVar *pVar = m_pMaterial->FindVar("%keywords", &bFound, false);
+ if (bFound)
+ {
+ V_strcpy_safe( m_szKeywords, pVar->GetStringValue() );
+
+ // Register the keywords
+ g_Textures.RegisterTextureKeywords( this );
+ }
+
+ // Make sure to bump the refcount again. Not sure why this wasn't always done (check for leaks).
+ if (m_pMaterial)
+ {
+ m_pMaterial->IncrementReferenceCount();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the material
+//-----------------------------------------------------------------------------
+IMaterial* CMaterial::GetMaterial( bool bForceLoad )
+{
+ if ( bForceLoad )
+ LoadMaterial();
+
+ return m_pMaterial;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMaterial::DrawIcon( CDC *pDC, CMaterial* pIcon, RECT& dstRect )
+{
+ if (!pIcon)
+ return;
+
+ g_pMaterialImageCache->EnCache(pIcon);
+
+ RECT rect, dst;
+ rect.left = 0; rect.right = pIcon->GetWidth();
+
+ // FIXME: Workaround the fact that materials must be power of 2, I want 12 bite
+ rect.top = 2; rect.bottom = pIcon->GetHeight() - 2;
+
+ dst = dstRect;
+ float dstHeight = dstRect.bottom - dstRect.top;
+ float srcAspect = (float)(rect.right - rect.left) / (float)(rect.bottom - rect.top);
+ dst.right = dst.left + (dstHeight * srcAspect);
+ pIcon->DrawBitmap( pDC, rect, dst );
+
+ dstRect.left += dst.right - dst.left;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pDC -
+// dstRect -
+// detectErrors -
+//-----------------------------------------------------------------------------
+void CMaterial::DrawBrowserIcons( CDC *pDC, RECT& dstRect, bool detectErrors )
+{
+ static CMaterial* pTranslucentIcon = 0;
+ static CMaterial* pOpaqueIcon = 0;
+ static CMaterial* pSelfIllumIcon = 0;
+ static CMaterial* pBaseAlphaEnvMapMaskIcon = 0;
+ static CMaterial* pErrorIcon = 0;
+
+ if (!pTranslucentIcon)
+ {
+ pTranslucentIcon = CreateMaterial("editor/translucenticon", true);
+ pOpaqueIcon = CreateMaterial("editor/opaqueicon", true);
+ pSelfIllumIcon = CreateMaterial("editor/selfillumicon", true);
+ pBaseAlphaEnvMapMaskIcon = CreateMaterial("editor/basealphaenvmapmaskicon", true);
+ pErrorIcon = CreateMaterial("editor/erroricon", true);
+
+ Assert( pTranslucentIcon && pOpaqueIcon && pSelfIllumIcon && pBaseAlphaEnvMapMaskIcon && pErrorIcon );
+ }
+
+ bool error = false;
+
+ IMaterial* pMaterial = GetMaterial();
+ if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_TRANSLUCENT ) )
+ {
+ DrawIcon( pDC, pTranslucentIcon, dstRect );
+ if (detectErrors)
+ {
+ error = error || !m_TranslucentBaseTexture;
+ }
+ }
+ else
+ {
+ DrawIcon( pDC, pOpaqueIcon, dstRect );
+ }
+
+ if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SELFILLUM ))
+ {
+ DrawIcon( pDC, pSelfIllumIcon, dstRect );
+ if (detectErrors)
+ {
+ error = error || !m_TranslucentBaseTexture;
+ }
+ }
+
+ if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_BASEALPHAENVMAPMASK ))
+ {
+ DrawIcon( pDC, pBaseAlphaEnvMapMaskIcon, dstRect );
+ if (detectErrors)
+ {
+ error = error || !m_TranslucentBaseTexture;
+ }
+ }
+
+ if (error)
+ {
+ DrawIcon( pDC, pErrorIcon, dstRect );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pDC -
+// srcRect -
+// dstRect -
+//-----------------------------------------------------------------------------
+void CMaterial::DrawBitmap( CDC *pDC, RECT& srcRect, RECT& dstRect )
+{
+ static struct
+ {
+ BITMAPINFOHEADER bmih;
+ unsigned short colorindex[256];
+ } bmi;
+
+ int srcWidth = srcRect.right - srcRect.left;
+ int srcHeight = srcRect.bottom - srcRect.top;
+
+ BITMAPINFOHEADER &bmih = bmi.bmih;
+ memset(&bmih, 0, sizeof(bmih));
+ bmih.biSize = sizeof(bmih);
+ bmih.biWidth = srcWidth;
+ bmih.biHeight = -srcHeight;
+ bmih.biCompression = BI_RGB;
+
+ bmih.biBitCount = 24;
+ bmih.biPlanes = 1;
+
+ static BOOL bInit = false;
+ if (!bInit)
+ {
+ bInit = true;
+ for (int i = 0; i < 256; i++)
+ {
+ bmi.colorindex[i] = i;
+ }
+ }
+
+ int dest_width = dstRect.right - dstRect.left;
+ int dest_height = dstRect.bottom - dstRect.top;
+
+ // ** bits **
+ SetStretchBltMode(pDC->m_hDC, COLORONCOLOR);
+ if (StretchDIBits(pDC->m_hDC, dstRect.left, dstRect.top, dest_width, dest_height,
+ srcRect.left, -srcRect.top, srcWidth, srcHeight, m_pData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, SRCCOPY) == GDI_ERROR)
+ {
+ Msg(mwError, "CMaterial::Draw(): StretchDIBits failed.");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pDC -
+// rect -
+// iFontHeight -
+// dwFlags -
+//-----------------------------------------------------------------------------
+void CMaterial::Draw(CDC *pDC, RECT& rect, int iFontHeight, int iIconHeight, DrawTexData_t &DrawTexData)//, BrowserData_t *pBrowserData)
+{
+ g_pMaterialImageCache->EnCache(this);
+ if (!this->HasData())
+ {
+ return;
+ }
+
+ if (m_nWidth <= 0)
+ {
+NoData:
+ // draw "no data"
+ CFont *pOldFont = (CFont*) pDC->SelectStockObject(ANSI_VAR_FONT);
+ COLORREF cr = pDC->SetTextColor(RGB(0xff, 0xff, 0xff));
+ COLORREF cr2 = pDC->SetBkColor(RGB(0, 0, 0));
+
+ // draw black rect first
+ pDC->FillRect(&rect, CBrush::FromHandle(HBRUSH(GetStockObject(BLACK_BRUSH))));
+
+ // then text
+ pDC->TextOut(rect.left+2, rect.top+2, "No Image", 8);
+ pDC->SelectObject(pOldFont);
+ pDC->SetTextColor(cr);
+ pDC->SetBkColor(cr2);
+ return;
+ }
+
+ // no data -
+ if (!m_pData)
+ {
+ // try to load -
+ if (!Load())
+ {
+ // can't load -
+ goto NoData;
+ }
+ }
+
+ // Draw the material image
+ RECT srcRect, dstRect;
+ srcRect.left = 0;
+ srcRect.top = 0;
+ srcRect.right = m_nWidth;
+ srcRect.bottom = m_nHeight;
+ dstRect = rect;
+
+ if (DrawTexData.nFlags & drawCaption)
+ {
+ dstRect.bottom -= iFontHeight + 4;
+ }
+ if (DrawTexData.nFlags & drawIcons)
+ {
+ dstRect.bottom -= iIconHeight;
+ }
+
+ if (!(DrawTexData.nFlags & drawResizeAlways))
+ {
+ if (m_nWidth < dstRect.right - dstRect.left )
+ {
+ dstRect.right = dstRect.left + m_nWidth;
+ }
+
+ if (m_nHeight < dstRect.bottom - dstRect.top )
+ {
+ dstRect.bottom = dstRect.top + m_nHeight;
+ }
+ }
+ DrawBitmap( pDC, srcRect, dstRect );
+
+ // Draw the icons
+ if (DrawTexData.nFlags & drawIcons)
+ {
+ dstRect = rect;
+ if (DrawTexData.nFlags & drawCaption)
+ {
+ dstRect.bottom -= iFontHeight + 5;
+ }
+ dstRect.top = dstRect.bottom - iIconHeight;
+ DrawBrowserIcons(pDC, dstRect, (DrawTexData.nFlags & drawErrors) != 0 );
+ }
+
+ // ** caption **
+ if (DrawTexData.nFlags & drawCaption)
+ {
+ // draw background for name
+ CBrush brCaption(RGB(0, 0, 255));
+ CRect rcCaption(rect);
+
+ rcCaption.top = rcCaption.bottom - (iFontHeight + 5);
+ pDC->FillRect(rcCaption, &brCaption);
+
+ // draw name
+ char szShortName[MAX_PATH];
+ int iLen = GetShortName(szShortName);
+ pDC->TextOut(rect.left, rect.bottom - (iFontHeight + 4), szShortName, iLen);
+
+ // draw usage count
+ if (DrawTexData.nFlags & drawUsageCount)
+ {
+ CString str;
+ str.Format("%d", DrawTexData.nUsageCount);
+ CSize size = pDC->GetTextExtent(str);
+ pDC->TextOut(rect.right - size.cx, rect.bottom - (iFontHeight + 4), str);
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMaterial::FreeData( void )
+{
+ free( m_pData );
+ m_pData = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a string of comma delimited keywords associated with this
+// material.
+// Input : pszKeywords - Buffer to receive keywords, NULL to query string length.
+// Output : Returns the number of characters in the keyword string.
+//-----------------------------------------------------------------------------
+int CMaterial::GetKeywords(char *pszKeywords) const
+{
+ // To access keywords, we have to have the header loaded
+ const_cast<CMaterial*>(this)->Load();
+ if (pszKeywords != NULL)
+ {
+ strcpy(pszKeywords, m_szKeywords);
+ }
+
+ return(strlen(m_szKeywords));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pszName -
+// Output : int
+//-----------------------------------------------------------------------------
+int CMaterial::GetShortName(char *pszName) const
+{
+ if (pszName != NULL)
+ {
+ strcpy(pszName, m_szName);
+ }
+
+ return(strlen(m_szName));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : material -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMaterial::LoadMaterialHeader( IMaterial *pMat )
+{
+
+ PreviewImageRetVal_t retVal;
+ bool translucentBaseTexture;
+ ImageFormat eImageFormat;
+ int width, height;
+ retVal = CPreviewImagePropertiesCache::GetPreviewImageProperties( pMat, &width, &height, &eImageFormat, &translucentBaseTexture);
+ if (retVal == MATERIAL_PREVIEW_IMAGE_BAD)
+ return false;
+
+ m_pMaterial = pMat;
+ m_pMaterial->IncrementReferenceCount();
+
+ m_nWidth = width;
+ m_nHeight = height;
+ m_TranslucentBaseTexture = translucentBaseTexture;
+
+ // Find the keywords for this material from the vmt file.
+ bool bFound;
+ IMaterialVar *pVar = pMat->FindVar("%keywords", &bFound, false);
+ if (bFound)
+ {
+ V_strcpy_safe( m_szKeywords, pVar->GetStringValue() );
+
+ // Register the keywords
+ g_Textures.RegisterTextureKeywords( this );
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the full path of the file from which this material was loaded.
+//-----------------------------------------------------------------------------
+const char *CMaterial::GetFileName( void ) const
+{
+ return(m_szFileName);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CMaterial::IsWater( void ) const
+{
+ bool bFound;
+ IMaterialVar *pVar = m_pMaterial->FindVar( "$surfaceprop", &bFound, false );
+ if ( bFound )
+ {
+ if ( !strcmp( "water", pVar->GetStringValue() ) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If the buffer pointer passed in is not NULL, copies the image data
+// in RGB format to the buffer
+// Input : pImageRGB - Pointer to buffer that receives the image data. If the
+// pointer is NULL, no data is copied, only the data size is returned.
+// Output : Returns a the size of the RGB image in bytes.
+//-----------------------------------------------------------------------------
+int CMaterial::GetImageDataRGB( void *pImageRGB )
+{
+ Assert( m_nWidth > 0 );
+
+ if ( pImageRGB != NULL )
+ {
+ Load();
+
+ g_pMaterialImageCache->EnCache( this );
+ if (!this->HasData())
+ {
+ return(NULL);
+ }
+
+ unsigned char *src, *dst;
+ src = ( unsigned char * )m_pData;
+ dst = (unsigned char *)pImageRGB;
+ for( ; src < ( unsigned char * )m_pData + m_nWidth * m_nHeight * 3; src += 3, dst += 3 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ }
+
+ return( m_nWidth * m_nHeight * 3 );
+ }
+
+ return( m_nWidth * m_nHeight * 3 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: If the buffer pointer passed in is not NULL, copies the image data
+// in RGBA format to the buffer
+// Input : pImageRGBA - Pointer to buffer that receives the image data. If the
+// pointer is NULL, no data is copied, only the data size is returned.
+// Output : Returns a the size of the RGBA image in bytes.
+//-----------------------------------------------------------------------------
+int CMaterial::GetImageDataRGBA(void *pImageRGBA)
+{
+ Assert( m_nWidth > 0 );
+
+ if (pImageRGBA != NULL)
+ {
+ Load();
+
+ g_pMaterialImageCache->EnCache(this);
+ if (!this->HasData())
+ {
+ return(NULL);
+ }
+
+ unsigned char *src, *dst;
+ src = (unsigned char *)m_pData;
+ dst = (unsigned char *)pImageRGBA;
+
+ while (src < (unsigned char *)m_pData + m_nWidth * m_nHeight * 4);
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = 0;
+
+ src += 4;
+ dst += 4;
+ }
+ }
+
+ return(m_nWidth * m_nHeight * 4);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : size -
+//-----------------------------------------------------------------------------
+void CMaterial::GetSize(SIZE &size) const
+{
+ const_cast<CMaterial*>(this)->Load();
+ Assert( m_nWidth >= 0 );
+
+ size.cx = m_nWidth;
+ size.cy = m_nHeight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads this material's image from disk if it is not already loaded.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMaterial::Load( void )
+{
+ LoadMaterial();
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// cache in the image size only when we need to
+//-----------------------------------------------------------------------------
+int CMaterial::GetImageWidth(void) const
+{
+ const_cast<CMaterial*>(this)->Load();
+ return(m_nWidth);
+}
+
+int CMaterial::GetImageHeight(void) const
+{
+ const_cast<CMaterial*>(this)->Load();
+ return(m_nHeight);
+}
+
+int CMaterial::GetWidth(void) const
+{
+ const_cast<CMaterial*>(this)->Load();
+ return(m_nWidth);
+}
+
+int CMaterial::GetHeight(void) const
+{
+ const_cast<CMaterial*>(this)->Load();
+ return(m_nHeight);
+}
+
+float CMaterial::GetDecalScale(void) const
+{
+ const_cast<CMaterial*>(this)->Load();
+
+ IMaterialVar *decalScaleVar;
+ bool found;
+
+ decalScaleVar = m_pMaterial->FindVar( "$decalScale", &found, false );
+ if( !found )
+ {
+ return 1.0f;
+ }
+ else
+ {
+ return decalScaleVar->GetFloatValue();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMaterial::LoadMaterialImage( void )
+{
+ Load();
+
+ if ((!m_nWidth) || (!m_nHeight))
+ return(false);
+
+ m_pData = malloc(m_nWidth * m_nHeight * 3);
+ Assert(m_pData);
+
+ ImageFormat imageFormat;
+
+// if( _strnicmp( m_pMaterial->GetName(), "decals", 6 ) == 0 )
+// {
+// imageFormat = IMAGE_FORMAT_BGR888_BLUESCREEN;
+// }
+// else
+ {
+ imageFormat = IMAGE_FORMAT_BGR888;
+ }
+
+ PreviewImageRetVal_t retVal;
+ retVal = m_pMaterial->GetPreviewImage( (unsigned char *)m_pData, m_nWidth, m_nHeight, imageFormat );
+ return (retVal != MATERIAL_PREVIEW_IMAGE_BAD);
+}
+
+
+static void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig)
+{
+ pConfig->bEditMode = true;
+ pConfig->m_nAASamples = 0;
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP, true);
+ // When I do this the model browser layout is horked...
+ // pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_USING_MULTIPLE_WINDOWS, true );
+}
+
+
+static char const *s_rt_names[]={"_rt_albedo","_rt_normal","_rt_position","_rt_flags",
+ "_rt_accbuf_0","_rt_accbuf_1"};
+ImageFormat s_rt_formats[]={ IMAGE_FORMAT_RGBA32323232F, IMAGE_FORMAT_RGBA32323232F,
+ IMAGE_FORMAT_RGBA32323232F, IMAGE_FORMAT_RGBA32323232F,
+ IMAGE_FORMAT_RGBA16161616F, IMAGE_FORMAT_RGBA16161616F };
+
+// ImageFormat s_rt_formats[]={
+// IMAGE_FORMAT_RGBA16161616F, IMAGE_FORMAT_RGBA16161616F,
+// IMAGE_FORMAT_RGBA16161616F, IMAGE_FORMAT_RGBA16161616F,
+// IMAGE_FORMAT_RGBA16161616F, IMAGE_FORMAT_RGBA16161616F,
+// IMAGE_FORMAT_RGBA16161616F, IMAGE_FORMAT_RGBA16161616F };
+
+static CTextureReference sg_ExtraFP16Targets[NELEMS(s_rt_names)];
+
+
+void AllocateLightingPreviewtextures(void)
+{
+ static bool bHaveAllocated=false;
+ if (! bHaveAllocated )
+ {
+ bHaveAllocated = true;
+ MaterialSystemInterface()->BeginRenderTargetAllocation();
+ for(int idx=0;idx<NELEMS(sg_ExtraFP16Targets);idx++)
+ sg_ExtraFP16Targets[idx].Init(
+ materials->CreateNamedRenderTargetTextureEx2(
+ s_rt_names[idx],
+ 512, 512, RT_SIZE_DEFAULT, s_rt_formats[idx],
+ MATERIAL_RT_DEPTH_SHARED,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT,
+ CREATERENDERTARGETFLAGS_HDR )
+ );
+
+ // End block in which all render targets should be allocated (kicking off an Alt-Tab type
+ // behavior)
+ MaterialSystemInterface()->EndRenderTargetAllocation();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMaterial::Initialize( HWND hwnd )
+{
+ // NOTE: This gets set to true later upon creating a 3d view.
+ g_materialSystemConfig = materials->GetCurrentConfigForVideoCard();
+ InitMaterialSystemConfig( &g_materialSystemConfig );
+
+ // Create a cache for material images (for browsing and uploading to the driver).
+ if (g_pMaterialImageCache == NULL)
+ {
+ g_pMaterialImageCache = new CMaterialImageCache(500);
+ if (g_pMaterialImageCache == NULL)
+ return false ;
+ }
+
+ materials->OverrideConfig( g_materialSystemConfig, false );
+
+ // Set the mode
+ // When setting the mode, we need to grab the parent window
+ // since that's going to enclose all our little render windows
+ g_materialSystemConfig.m_VideoMode.m_Width = g_materialSystemConfig.m_VideoMode.m_Height = 0;
+ g_materialSystemConfig.m_VideoMode.m_Format = IMAGE_FORMAT_BGRA8888;
+ g_materialSystemConfig.m_VideoMode.m_RefreshRate = 0;
+ g_materialSystemConfig.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ g_materialSystemConfig.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true );
+
+
+ if (!MaterialSystemInterface()->SetMode( hwnd, g_materialSystemConfig ) )
+ return false;
+
+ return true;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Restores the material system to an uninitialized state.
+//-----------------------------------------------------------------------------
+void CMaterial::ShutDown(void)
+{
+ for ( int i = 0; i < NELEMS(sg_ExtraFP16Targets); ++i )
+ {
+ sg_ExtraFP16Targets[i].Shutdown();
+ }
+
+ if (materials != NULL)
+ {
+ materials->UncacheAllMaterials();
+ }
+
+ delete g_pMaterialImageCache;
+ g_pMaterialImageCache = NULL;
+}
+
+