source-engine/hammer/material.cpp

1420 lines
38 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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;
}