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 /hammer/material.cpp | |
| download | archived-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.cpp | 1419 |
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; +} + + |