You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1360 lines
35 KiB
1360 lines
35 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Texture management functions. Exposes a list of available textures, |
|
// texture groups, and Most Recently Used textures. |
|
// |
|
// There is one texture context per game configuration in GameCfg.ini. |
|
// |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include <process.h> |
|
#include <io.h> |
|
#include <sys\stat.h> |
|
#include <fcntl.h> |
|
#include "DummyTexture.h" // Specific IEditorTexture implementation |
|
#include "GlobalFunctions.h" |
|
#include "MainFrm.h" |
|
#include "MapDoc.h" |
|
#include "Material.h" // Specific IEditorTexture implementation |
|
#include "Options.h" |
|
#include "TextureSystem.h" |
|
#include "WADTexture.h" // Specific IEditorTexture implementation |
|
#include "WADTypes.h" |
|
#include "hammer.h" |
|
#include "filesystem.h" |
|
#include "materialsystem/itexture.h" |
|
#include "tier1/utldict.h" |
|
#include "FaceEditSheet.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) |
|
#define IsSortChr(ch) ((ch == '-') || (ch == '+')) |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Stuff for loading WAD3 files. |
|
//----------------------------------------------------------------------------- |
|
typedef struct |
|
{ |
|
int filepos; |
|
int disksize; |
|
int size; // uncompressed |
|
char type; |
|
char compression; |
|
char pad1, pad2; |
|
char name[16]; // must be null terminated |
|
} WAD3lumpinfo_t; |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// List of global graphics |
|
//----------------------------------------------------------------------------- |
|
CTextureSystem g_Textures; |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CMaterialFileChangeWatcher implementation. |
|
//----------------------------------------------------------------------------- |
|
void CMaterialFileChangeWatcher::Init( CTextureSystem *pSystem, intp context ) |
|
{ |
|
m_pTextureSystem = pSystem; |
|
m_Context = context; |
|
|
|
m_Watcher.Init( this ); |
|
|
|
char searchPaths[1024 * 16]; |
|
if ( g_pFullFileSystem->GetSearchPath( "GAME", false, searchPaths, sizeof( searchPaths ) ) > 0 ) |
|
{ |
|
CUtlVector<char*> searchPathList; |
|
V_SplitString( searchPaths, ";", searchPathList ); |
|
|
|
for ( int i=0; i < searchPathList.Count(); i++ ) |
|
{ |
|
m_Watcher.AddDirectory( searchPathList[i], "materials", true ); |
|
} |
|
|
|
searchPathList.PurgeAndDeleteElements(); |
|
} |
|
else |
|
{ |
|
Warning( "Error in GetSearchPath. Dynamic material list updating will not be available." ); |
|
} |
|
} |
|
|
|
void CMaterialFileChangeWatcher::OnFileChange( const char *pRelativeFilename, const char *pFullFilename ) |
|
{ |
|
//Msg( "OnNewFile: %s\n", pRelativeFilename ); |
|
|
|
CTextureSystem::EFileType eFileType; |
|
if ( CTextureSystem::GetFileTypeFromFilename( pRelativeFilename, &eFileType ) ) |
|
m_pTextureSystem->OnFileChange( pRelativeFilename, m_Context, eFileType ); |
|
} |
|
|
|
void CMaterialFileChangeWatcher::Update() |
|
{ |
|
m_Watcher.Update(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. Creates the "All" group and sets it as the active group. |
|
//----------------------------------------------------------------------------- |
|
CTextureSystem::CTextureSystem(void) |
|
{ |
|
m_pLastTex = NULL; |
|
m_nLastIndex = 0; |
|
m_pActiveContext = NULL; |
|
m_pActiveGroup = NULL; |
|
m_pCubemapTexture = NULL; |
|
m_pNoDrawTexture = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. Frees the list of groups and dummy textures. |
|
//----------------------------------------------------------------------------- |
|
CTextureSystem::~CTextureSystem(void) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::FreeAllTextures() |
|
{ |
|
if ( m_pCubemapTexture ) |
|
{ |
|
m_pCubemapTexture->DecrementReferenceCount(); |
|
m_pCubemapTexture = NULL; |
|
} |
|
|
|
int nContextCount = m_TextureContexts.Count(); |
|
for (int nContext = 0; nContext < nContextCount; nContext++) |
|
{ |
|
TextureContext_t *pContext = &m_TextureContexts.Element(nContext); |
|
|
|
// |
|
// Delete all the texture groups for this context. |
|
// |
|
int nGroupCount = pContext->Groups.Count(); |
|
for (int nGroup = 0; nGroup < nGroupCount; nGroup++) |
|
{ |
|
delete pContext->Groups.Element(nGroup); |
|
} |
|
|
|
// |
|
// Delete dummy textures. |
|
// |
|
int nDummyCount = pContext->Dummies.Count(); |
|
for (int nDummy = 0; nDummy < nDummyCount; nDummy++) |
|
{ |
|
IEditorTexture *pTex = pContext->Dummies.Element(nDummy); |
|
delete pTex; |
|
} |
|
} |
|
|
|
// |
|
// Delete all the textures from the master list. |
|
// |
|
for (int i = 0; i < m_Textures.Count(); i++) |
|
{ |
|
IEditorTexture *pTex = m_Textures[i]; |
|
delete pTex; |
|
} |
|
m_Textures.RemoveAll(); |
|
|
|
m_pLastTex = NULL; |
|
m_nLastIndex = -1; |
|
|
|
|
|
// Delete the keywords. |
|
m_Keywords.PurgeAndDeleteElements(); |
|
m_ChangeWatchers.PurgeAndDeleteElements(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a texture to the master list of textures. |
|
// Input : pTexture - Pointer to texture to add. |
|
// Output : Returns the index of the texture in the master texture list. |
|
//----------------------------------------------------------------------------- |
|
int CTextureSystem::AddTexture(IEditorTexture *pTexture) |
|
{ |
|
return m_Textures.AddToTail(pTexture); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Begins iterating the list of texture/material keywords. |
|
//----------------------------------------------------------------------------- |
|
int CTextureSystem::GetNumKeywords(void) |
|
{ |
|
return(m_Keywords.Count()); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Continues iterating the list of texture/material keywords. |
|
//----------------------------------------------------------------------------- |
|
const char *CTextureSystem::GetKeyword(int pos) |
|
{ |
|
return(m_Keywords.Element(pos)); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *piIndex - |
|
// bUseMRU - |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
IEditorTexture *CTextureSystem::EnumActiveTextures(int *piIndex, TEXTUREFORMAT eDesiredFormat) const |
|
{ |
|
Assert(piIndex != NULL); |
|
|
|
if (piIndex != NULL) |
|
{ |
|
if (m_pActiveGroup != NULL) |
|
{ |
|
IEditorTexture *pTex = NULL; |
|
|
|
do |
|
{ |
|
pTex = m_pActiveGroup->GetTexture(*piIndex); |
|
if (pTex != NULL) |
|
{ |
|
(*piIndex)++; |
|
|
|
if ((eDesiredFormat == tfNone) || (pTex->GetTextureFormat() == eDesiredFormat)) |
|
{ |
|
return(pTex); |
|
} |
|
} |
|
} while (pTex != NULL); |
|
} |
|
} |
|
|
|
return(NULL); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the texture system. |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CTextureSystem::Initialize(HWND hwnd) |
|
{ |
|
bool bWAD = CWADTexture::Initialize(); |
|
bool bMaterial = CMaterial::Initialize(hwnd); |
|
|
|
return(bWAD && bMaterial); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Shuts down the texture system. |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::ShutDown(void) |
|
{ |
|
CWADTexture::ShutDown(); |
|
CMaterial::ShutDown(); |
|
FreeAllTextures(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pszName - |
|
// piIndex - |
|
// bDummy - |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
IEditorTexture *CTextureSystem::FindActiveTexture(LPCSTR pszInputName, int *piIndex, BOOL bDummy) |
|
{ |
|
|
|
// The .vmf file format gets confused if there are backslashes in material names, |
|
// so make sure they're all using forward slashes here. |
|
char szName[MAX_PATH]; |
|
Q_StrSubst( pszInputName, "\\", "/", szName, sizeof( szName ) ); |
|
const char *pszName = szName; |
|
IEditorTexture *pTex = NULL; |
|
// |
|
// Check the cache first. |
|
// |
|
if (m_pLastTex && !stricmp(pszName, m_pLastTex->GetName())) |
|
{ |
|
if (piIndex) |
|
{ |
|
*piIndex = m_nLastIndex; |
|
} |
|
|
|
return m_pLastTex; |
|
} |
|
|
|
int iIndex = 0; |
|
|
|
// We're finding by name, so we don't care what the format is as long as the name matches. |
|
if ( m_pActiveGroup ) |
|
{ |
|
pTex = m_pActiveGroup->FindTextureByName( pszName, &iIndex, tfNone ); |
|
if ( pTex ) |
|
{ |
|
if ( piIndex ) |
|
*piIndex = iIndex; |
|
|
|
m_pLastTex = pTex; |
|
m_nLastIndex = iIndex; |
|
|
|
return pTex; |
|
} |
|
} |
|
|
|
// |
|
// Let's try again, this time with \textures\ decoration |
|
// TODO: remove this? |
|
// |
|
{ |
|
iIndex = 0; |
|
char szBuf[512]; |
|
|
|
sprintf(szBuf, "textures\\%s", pszName); |
|
|
|
for (int i = strlen(szBuf) -1; i >= 0; i--) |
|
{ |
|
if (szBuf[i] == '/') |
|
szBuf[i] = '\\'; |
|
} |
|
|
|
strlwr(szBuf); |
|
|
|
if ( m_pActiveGroup ) |
|
{ |
|
pTex = m_pActiveGroup->FindTextureByName( szBuf, &iIndex, tfNone ); |
|
if ( pTex ) |
|
{ |
|
if ( piIndex ) |
|
*piIndex = iIndex; |
|
|
|
m_pLastTex = pTex; |
|
m_nLastIndex = iIndex; |
|
|
|
return pTex; |
|
} |
|
} |
|
} |
|
// |
|
// Caller doesn't want dummies. |
|
// |
|
if (!bDummy) |
|
{ |
|
return(NULL); |
|
} |
|
|
|
Assert(!piIndex); |
|
|
|
// |
|
// Check the list of dummies for a texture with the same name and texture format. |
|
// |
|
if (m_pActiveContext) |
|
{ |
|
int nDummyCount = m_pActiveContext->Dummies.Count(); |
|
for (int nDummy = 0; nDummy < nDummyCount; nDummy++) |
|
{ |
|
IEditorTexture *pTexDummy = m_pActiveContext->Dummies.Element(nDummy); |
|
if (!strcmpi(pszName, pTexDummy->GetName())) |
|
{ |
|
m_pLastTex = pTexDummy; |
|
m_nLastIndex = -1; |
|
return(pTexDummy); |
|
} |
|
} |
|
|
|
// |
|
// Not found; add a dummy as a placeholder for the missing texture. |
|
// |
|
pTex = AddDummy(pszName, g_pGameConfig->GetTextureFormat()); |
|
} |
|
|
|
if (pTex != NULL) |
|
{ |
|
m_pLastTex = pTex; |
|
m_nLastIndex = -1; |
|
} |
|
|
|
return(pTex); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pTex - |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::AddMRU(IEditorTexture *pTex) |
|
{ |
|
if (!m_pActiveContext) |
|
return; |
|
|
|
int nIndex = m_pActiveContext->MRU.Find(pTex); |
|
if (nIndex != -1) |
|
{ |
|
m_pActiveContext->MRU.Remove(nIndex); |
|
} |
|
else if (m_pActiveContext->MRU.Count() == 8) |
|
{ |
|
m_pActiveContext->MRU.Remove(7); |
|
} |
|
|
|
m_pActiveContext->MRU.AddToHead(pTex); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Change palette on all textures. |
|
// Input : |
|
// dvs: need to handle a palette change for Quake support |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::InformPaletteChanged() |
|
{ |
|
// int nGraphics = GetCount(); |
|
// |
|
// for (int i = 0; i < nGraphics; i++) |
|
// { |
|
// IEditorTexture *pTex = &GetAt(i); |
|
// } |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the texture context that corresponds to the given game config. |
|
//----------------------------------------------------------------------------- |
|
TextureContext_t *CTextureSystem::FindTextureContextForConfig(CGameConfig *pConfig) |
|
{ |
|
for (int i = 0; i < m_TextureContexts.Count(); i++) |
|
{ |
|
if (m_TextureContexts.Element(i).pConfig == pConfig) |
|
{ |
|
return &m_TextureContexts.Element(i); |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::SetActiveConfig(CGameConfig *pConfig) |
|
{ |
|
TextureContext_t *pContext = FindTextureContextForConfig(pConfig); |
|
if (pContext) |
|
{ |
|
m_pActiveContext = pContext; |
|
m_pActiveGroup = m_pActiveContext->pAllGroup; |
|
} |
|
else |
|
{ |
|
m_pActiveContext = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : char *pcszName - |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::SetActiveGroup(const char *pcszName) |
|
{ |
|
if (!m_pActiveContext) |
|
return; |
|
|
|
char szBuf[MAX_PATH]; |
|
sprintf(szBuf, "textures\\%s", pcszName); |
|
|
|
int iCount = m_pActiveContext->Groups.Count(); |
|
for (int i = 0; i < iCount; i++) |
|
{ |
|
CTextureGroup *pGroup = m_pActiveContext->Groups.Element(i); |
|
if (!strcmpi(pGroup->GetName(), pcszName)) |
|
{ |
|
m_pActiveGroup = pGroup; |
|
return; |
|
} |
|
|
|
if (strstr(pGroup->GetName(), pcszName)) |
|
{ |
|
m_pActiveGroup = pGroup; |
|
return; |
|
} |
|
|
|
} |
|
|
|
TRACE0("No Group Found!"); |
|
} |
|
|
|
|
|
void HammerFileSystem_ReportSearchPath( const char *szPathID ) |
|
{ |
|
char szSearchPath[ 4096 ]; |
|
g_pFullFileSystem->GetSearchPath( szPathID, true, szSearchPath, sizeof( szSearchPath ) ); |
|
|
|
Msg( mwStatus, "------------------------------------------------------------------" ); |
|
|
|
char *pszOnePath = strtok( szSearchPath, ";" ); |
|
while ( pszOnePath ) |
|
{ |
|
Msg( mwStatus, "Search Path (%s): %s", szPathID, pszOnePath ); |
|
pszOnePath = strtok( NULL, ";" ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// FIXME: Make this work correctly, using the version in filesystem_tools.cpp |
|
// (it doesn't work currently owing to filesystem setup issues) |
|
//----------------------------------------------------------------------------- |
|
void HammerFileSystem_SetGame( const char *pExeDir, const char *pModDir ) |
|
{ |
|
static bool s_bOnce = false; |
|
Assert( !s_bOnce ); |
|
s_bOnce = true; |
|
|
|
char buf[MAX_PATH]; |
|
|
|
Q_snprintf( buf, MAX_PATH, "%s\\hl2", pExeDir ); |
|
g_pFullFileSystem->AddSearchPath( buf, "GAME", PATH_ADD_TO_HEAD ); |
|
|
|
if ( pModDir && *pModDir != '\0' ) |
|
{ |
|
g_pFullFileSystem->AddSearchPath( pModDir, "GAME", PATH_ADD_TO_HEAD ); |
|
} |
|
|
|
HammerFileSystem_ReportSearchPath( "GAME" ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads textures from all texture files. |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::LoadAllGraphicsFiles(void) |
|
{ |
|
FreeAllTextures(); |
|
|
|
// For each game config... |
|
// dvs: Disabled for single-config running. |
|
//for (int nConfig = 0; nConfig < Options.configs.GetGameConfigCount(); nConfig++) |
|
{ |
|
//CGameConfig *pConfig = Options.configs.GetGameConfig(nConfig); |
|
CGameConfig *pConfig = g_pGameConfig; |
|
|
|
// Create a new texture context with the WADs and materials for that config. |
|
TextureContext_t *pContext = AddTextureContext(); |
|
|
|
// Bind it to this config. |
|
pContext->pConfig = pConfig; |
|
|
|
// Create a group to hold all the textures for this context. |
|
pContext->pAllGroup = new CTextureGroup("All Textures"); |
|
pContext->Groups.AddToTail(pContext->pAllGroup); |
|
|
|
HammerFileSystem_SetGame(pConfig->m_szGameExeDir, pConfig->m_szModDir); |
|
|
|
// Set the new context as the active context. |
|
m_pActiveContext = pContext; |
|
|
|
// Load the textures for all WAD files set in this config. |
|
// Only do this for configs that use WAD textures. |
|
if (pConfig->GetTextureFormat() == tfWAD3) |
|
{ |
|
LoadWADFiles(pConfig); |
|
} |
|
|
|
// Load the materials for this config. |
|
// Do this unconditionally so that we get necessary editor materials. |
|
LoadMaterials(pConfig); |
|
|
|
m_pActiveContext->pAllGroup->Sort(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads all WAD files for the given game config. |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::LoadWADFiles(CGameConfig *pConfig) |
|
{ |
|
// dvs: FIXME: WADs are not currently per-config |
|
for (int i = 0; i < Options.textures.nTextureFiles; i++) |
|
{ |
|
LoadGraphicsFile(Options.textures.TextureFiles[i]); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads all the materials for the given game config. |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::LoadMaterials(CGameConfig *pConfig) |
|
{ |
|
CTextureGroup *pGroup = new CTextureGroup("Materials"); |
|
pGroup->SetTextureFormat(tfVMT); |
|
m_pActiveContext->Groups.AddToTail(pGroup); |
|
|
|
// Add all the materials to the group. |
|
CMaterial::EnumerateMaterials( this, "materials", (int)pGroup, INCLUDE_WORLD_MATERIALS ); |
|
|
|
// Watch the materials directory recursively... |
|
CMaterialFileChangeWatcher *pWatcher = new CMaterialFileChangeWatcher; |
|
pWatcher->Init( this, (int)pGroup ); |
|
m_ChangeWatchers.AddToTail( pWatcher ); |
|
|
|
Assert( m_pCubemapTexture == NULL ); |
|
|
|
m_pCubemapTexture = MaterialSystemInterface()->FindTexture( "editor/cubemap", NULL, true ); |
|
|
|
if ( m_pCubemapTexture ) |
|
{ |
|
m_pCubemapTexture->IncrementReferenceCount(); |
|
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); |
|
pRenderContext->BindLocalCubemap( m_pCubemapTexture ); |
|
} |
|
|
|
// Get the nodraw texture. |
|
m_pNoDrawTexture = NULL; |
|
for ( int i=0; i < m_Textures.Count(); i++ ) |
|
{ |
|
if ( V_stricmp( m_Textures[i]->GetName(), "tools/toolsnodraw" ) == 0 || V_stricmp( m_Textures[i]->GetName(), "tools/toolsnodraw" ) == 0 ) |
|
{ |
|
m_pNoDrawTexture = m_Textures[i]; |
|
break; |
|
} |
|
} |
|
if ( !m_pNoDrawTexture ) |
|
m_pNoDrawTexture = CMaterial::CreateMaterial( "tools/toolsnodraw", true ); |
|
} |
|
|
|
void CTextureSystem::RebindDefaultCubeMap() |
|
{ |
|
// rebind with the default cubemap |
|
|
|
if ( m_pCubemapTexture ) |
|
{ |
|
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); |
|
pRenderContext->BindLocalCubemap( m_pCubemapTexture ); |
|
} |
|
} |
|
|
|
|
|
void CTextureSystem::UpdateFileChangeWatchers() |
|
{ |
|
for ( int i=0; i < m_ChangeWatchers.Count(); i++ ) |
|
m_ChangeWatchers[i]->Update(); |
|
} |
|
|
|
|
|
void CTextureSystem::OnFileChange( const char *pFilename, intp context, CTextureSystem::EFileType eFileType ) |
|
{ |
|
// It requires the forward slashes later... |
|
char fixedSlashes[MAX_PATH]; |
|
V_StrSubst( pFilename, "\\", "/", fixedSlashes, sizeof( fixedSlashes ) ); |
|
|
|
// Get rid of the extension. |
|
if ( V_strlen( fixedSlashes ) < 5 ) |
|
{ |
|
Assert( false ); |
|
return; |
|
} |
|
fixedSlashes[ V_strlen( fixedSlashes ) - 4 ] = 0; |
|
|
|
|
|
// Handle it based on what type of file we've got. |
|
if ( eFileType == k_eFileTypeVMT ) |
|
{ |
|
IEditorTexture *pTex = FindActiveTexture( fixedSlashes, NULL, FALSE ); |
|
if ( pTex ) |
|
{ |
|
pTex->Reload( true ); |
|
} |
|
else |
|
{ |
|
EnumMaterial( fixedSlashes, context ); |
|
IEditorTexture *pTexFixed = FindActiveTexture( fixedSlashes, NULL, FALSE ); |
|
if ( pTexFixed ) |
|
{ |
|
GetMainWnd()->m_TextureBar.NotifyNewMaterial( pTexFixed ); |
|
GetMainWnd()->GetFaceEditSheet()->NotifyNewMaterial( pTexFixed ); |
|
} |
|
} |
|
} |
|
else if ( eFileType == k_eFileTypeVTF ) |
|
{ |
|
// Whether a VTF was added, removed, or modified, we do the same thing.. refresh it and any materials that reference it. |
|
ITexture *pTexture = materials->FindTexture( fixedSlashes, TEXTURE_GROUP_UNACCOUNTED, false ); |
|
if ( pTexture ) |
|
{ |
|
pTexture->Download( NULL ); |
|
ReloadMaterialsUsingTexture( pTexture ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Load any materials that reference this texture. Used so we can refresh a |
|
// material's preview image if a relevant .vtf changes. |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::ReloadMaterialsUsingTexture( ITexture *pTestTexture ) |
|
{ |
|
for ( int i=0; i < m_Textures.Count(); i++ ) |
|
{ |
|
IEditorTexture *pEditorTex = m_Textures[i]; |
|
IMaterial *pMat = pEditorTex->GetMaterial( false ); |
|
if ( !pMat ) |
|
continue; |
|
|
|
IMaterialVar **pParams = pMat->GetShaderParams(); |
|
int nParams = pMat->ShaderParamCount(); |
|
for ( int iParam=0; iParam < nParams; iParam++ ) |
|
{ |
|
if ( pParams[iParam]->GetType() != MATERIAL_VAR_TYPE_TEXTURE ) |
|
continue; |
|
|
|
ITexture *pTex = pParams[iParam]->GetTextureValue(); |
|
if ( !pTex ) |
|
continue; |
|
|
|
if ( pTex == pTestTexture ) |
|
{ |
|
pEditorTex->Reload( true ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Figure out the file type from its extension. Returns false if we don't have an enum for that extension. |
|
//----------------------------------------------------------------------------- |
|
bool CTextureSystem::GetFileTypeFromFilename( const char *pFilename, CTextureSystem::EFileType *pFileType ) |
|
{ |
|
char strRight[16]; |
|
V_StrRight( pFilename, 4, strRight, sizeof( strRight ) ); |
|
if ( V_stricmp( strRight, ".vmt" ) == 0 ) |
|
{ |
|
*pFileType = CTextureSystem::k_eFileTypeVMT; |
|
return true; |
|
} |
|
else if ( V_stricmp( strRight, ".vtf" ) == 0 ) |
|
{ |
|
*pFileType = CTextureSystem::k_eFileTypeVTF; |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads textures from all texture files. |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::ReloadTextures( const char *pFilterName ) |
|
{ |
|
MaterialSystemInterface()->ReloadMaterials( pFilterName ); |
|
|
|
for ( int i = 0; i < m_Textures.Count(); i++ ) |
|
{ |
|
if ( !Q_stristr( pFilterName, m_Textures[i]->GetName() ) ) |
|
continue; |
|
|
|
m_Textures[i]->Reload( false ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a placeholder texture for a texture that exists in the map, but |
|
// was not found on disk. |
|
// Input : pszName - Name of missing texture. |
|
// Output : Returns a pointer to the new dummy texture. |
|
//----------------------------------------------------------------------------- |
|
IEditorTexture *CTextureSystem::AddDummy(LPCTSTR pszName, TEXTUREFORMAT eFormat) |
|
{ |
|
if (!m_pActiveContext) |
|
return NULL; |
|
|
|
IEditorTexture *pTex = new CDummyTexture(pszName, eFormat); |
|
m_pActiveContext->Dummies.AddToTail(pTex); |
|
|
|
return(pTex); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : elem1 - |
|
// elem2 - |
|
// Output : static int __cdecl |
|
//----------------------------------------------------------------------------- |
|
static int __cdecl SortTexturesProc(IEditorTexture * const *elem1, IEditorTexture * const *elem2) |
|
{ |
|
IEditorTexture *pElem1 = *((IEditorTexture **)elem1); |
|
IEditorTexture *pElem2 = *((IEditorTexture **)elem2); |
|
|
|
Assert((pElem1 != NULL) && (pElem2 != NULL)); |
|
if ((pElem1 == NULL) || (pElem2 == NULL)) |
|
{ |
|
return(0); |
|
} |
|
|
|
const char *pszName1 = pElem1->GetName(); |
|
const char *pszName2 = pElem2->GetName(); |
|
|
|
char ch1 = pszName1[0]; |
|
char ch2 = pszName2[0]; |
|
|
|
if (IsSortChr(ch1) && !IsSortChr(ch2)) |
|
{ |
|
int iFamilyLen = strlen(pszName1+2); |
|
int iFamily = strnicmp(pszName1+2, pszName2, iFamilyLen); |
|
if (!iFamily) |
|
{ |
|
return(-1); // same family - put elem1 before elem2 |
|
} |
|
return(iFamily); // sort normally |
|
} |
|
else if (!IsSortChr(ch1) && IsSortChr(ch2)) |
|
{ |
|
int iFamilyLen = strlen(pszName2+2); |
|
int iFamily = strnicmp(pszName1, pszName2+2, iFamilyLen); |
|
if (!iFamily) |
|
{ |
|
return(1); // same family - put elem2 before elem1 |
|
} |
|
return(iFamily); // sort normally |
|
} |
|
else if (IsSortChr(ch1) && IsSortChr(ch2)) |
|
{ |
|
// do family name sorting |
|
int iFamily = strcmpi(pszName1+2, pszName2+2); |
|
|
|
if (!iFamily) |
|
{ |
|
// same family - sort by number |
|
return pszName1[1] - pszName2[1]; |
|
} |
|
|
|
// different family |
|
return(iFamily); |
|
} |
|
|
|
return(strcmpi(pszName1, pszName2)); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : sizeSrc - |
|
// sizeDest - |
|
// *src - |
|
// *dest - |
|
//----------------------------------------------------------------------------- |
|
void ScaleBitmap(CSize sizeSrc, CSize sizeDest, char *src, char *dest) |
|
{ |
|
int i; |
|
int e_y = (sizeSrc.cy << 1) - sizeDest.cy; |
|
int sizeDest2_y = (sizeDest.cy << 1); |
|
int sizeSrc2_y = sizeSrc.cy << 1; |
|
int srcline = 0, destline = 0; |
|
char *srclinep, *destlinep; |
|
int e_x = (sizeSrc.cx << 1) - sizeDest.cx; |
|
int sizeDest2_x = (sizeDest.cx << 1); |
|
int sizeSrc2_x = sizeSrc.cx << 1; |
|
|
|
for( i = 0; i < sizeDest.cy; i++ ) |
|
{ |
|
// scale by X |
|
{ |
|
srclinep = src + (srcline * sizeSrc.cx); |
|
destlinep = dest + (destline * sizeDest.cx); |
|
|
|
for( int j = 0; j < sizeDest.cx; j++ ) |
|
{ |
|
*destlinep = *srclinep; |
|
|
|
while( e_x >= 0 ) |
|
{ |
|
++srclinep; |
|
e_x -= sizeDest2_x; |
|
} |
|
|
|
++destlinep; |
|
e_x += sizeSrc2_x; |
|
} |
|
} |
|
|
|
while( e_y >= 0 ) |
|
{ |
|
++srcline; |
|
e_y -= sizeDest2_y; |
|
} |
|
|
|
++destline; |
|
e_y += sizeSrc2_y; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : id - |
|
// *piIndex - |
|
// Output : GRAPHICSFILESTRUCT * |
|
//----------------------------------------------------------------------------- |
|
bool CTextureSystem::FindGraphicsFile(GRAPHICSFILESTRUCT *pFileInfo, DWORD id, int *piIndex) |
|
{ |
|
for (int i = 0; i < m_GraphicsFiles.Count(); i++) |
|
{ |
|
if (m_GraphicsFiles[i].id == id) |
|
{ |
|
if (piIndex) |
|
{ |
|
piIndex[0] = i; |
|
} |
|
|
|
if (pFileInfo != NULL) |
|
{ |
|
*pFileInfo = m_GraphicsFiles[i]; |
|
} |
|
|
|
return(true); |
|
} |
|
} |
|
|
|
return(false); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pFile - |
|
// fd - |
|
// pGroup - |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::LoadGraphicsFileWAD3(GRAPHICSFILESTRUCT *pFile, int fd, CTextureGroup *pGroup) |
|
{ |
|
// read wad header |
|
wadinfo_t hdr; |
|
_lseek(fd, 0, SEEK_SET); |
|
_read(fd, (char*)&hdr, sizeof hdr); |
|
|
|
_lseek(fd, hdr.infotableofs, SEEK_SET); |
|
|
|
// allocate directory memory. |
|
WAD3lumpinfo_t *dir = new WAD3lumpinfo_t[hdr.numlumps]; |
|
|
|
// read entries. |
|
_read(fd, dir, sizeof(WAD3lumpinfo_t) * hdr.numlumps); |
|
|
|
// load graphics! |
|
for (int i = 0; i < hdr.numlumps; i++) |
|
{ |
|
if (dir[i].type == TYP_MIPTEX) |
|
{ |
|
_lseek(fd, dir[i].filepos, SEEK_SET); |
|
|
|
CWADTexture *pNew = new CWADTexture; |
|
if (pNew != NULL) |
|
{ |
|
if (pNew->Init(fd, pFile->id, FALSE, dir[i].name)) |
|
{ |
|
pNew->SetTextureFormat(pFile->format); |
|
|
|
// |
|
// Add the texture to master list of textures. |
|
// |
|
AddTexture(pNew); |
|
|
|
// |
|
// Add the texture's index to the given group and to the "All" group. |
|
// |
|
pGroup->AddTexture(pNew); |
|
if (pGroup != m_pActiveContext->pAllGroup) |
|
{ |
|
m_pActiveContext->pAllGroup->AddTexture(pNew); |
|
} |
|
} |
|
else |
|
{ |
|
delete pNew; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// free memory |
|
delete[] dir; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads all textures in a given graphics file and returns an ID for |
|
// the file. |
|
// Input : filename - Full path of graphics file to load. |
|
// Output : Returns the file ID. |
|
//----------------------------------------------------------------------------- |
|
DWORD CTextureSystem::LoadGraphicsFile(const char *pFilename) |
|
{ |
|
static DWORD __GraphFileID = 1; // must start at 1. |
|
|
|
// |
|
// Make sure it's not already there. |
|
// |
|
int i = m_GraphicsFiles.Count() - 1; |
|
while (i > -1) |
|
{ |
|
if (!strcmp(m_GraphicsFiles[i].filename, pFilename)) |
|
{ |
|
return(m_GraphicsFiles[i].id); |
|
} |
|
|
|
i--; |
|
} |
|
|
|
// |
|
// Is this a WAD file? |
|
// |
|
DWORD dwAttrib = GetFileAttributes(pFilename); |
|
if (dwAttrib == 0xFFFFFFFF) |
|
{ |
|
return(0); |
|
} |
|
|
|
GRAPHICSFILESTRUCT gf; |
|
|
|
if (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) |
|
{ |
|
// open the file, and add it to the GraphicFileList array |
|
gf.fd = _open(pFilename, _O_BINARY | _O_RDONLY); |
|
if (gf.fd == -1) |
|
{ |
|
// todo: if errno is "out of handles", close some other |
|
// graphics files. |
|
|
|
// StatusMsg(IDS_ERROPENGRAPHFILE, errno); |
|
return 0; // could not open |
|
} |
|
|
|
char buf[4]; |
|
_read(gf.fd, buf, 4); |
|
|
|
// |
|
// Make sure the file is in a format that we can read. |
|
// |
|
if (!memcmp(buf, "WAD3", 4)) |
|
{ |
|
gf.format = tfWAD3; |
|
} |
|
else |
|
{ |
|
char str[MAX_PATH*2]; |
|
Q_snprintf( str, sizeof(str), "The file \"%s\" is not a valid WAD3 file and will not be used.", pFilename); |
|
AfxMessageBox(str, MB_ICONEXCLAMATION | MB_OK); |
|
_close(gf.fd); |
|
return(0); |
|
} |
|
} |
|
|
|
// got it -- setup the rest of the gf structure |
|
gf.id = __GraphFileID++; |
|
Q_strncpy( gf.filename, pFilename, sizeof(gf.filename) ); |
|
gf.bLoaded = FALSE; |
|
|
|
// |
|
// Add file to list of texture files. |
|
// |
|
m_GraphicsFiles.AddToTail(gf); |
|
|
|
// |
|
// Create a new texture group for the file. |
|
// |
|
CTextureGroup *pGroup = new CTextureGroup(pFilename); |
|
pGroup->SetTextureFormat(gf.format); |
|
m_pActiveContext->Groups.AddToTail(pGroup); |
|
|
|
// |
|
// Load the textures from the file and place them in the texture group. |
|
// |
|
LoadGraphicsFileWAD3(&gf, gf.fd, pGroup); |
|
gf.bLoaded = TRUE; |
|
|
|
// |
|
// Sort this group's list |
|
// |
|
pGroup->Sort(); |
|
|
|
return(gf.id); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Determines whether or not there is at least one available texture |
|
// group for a given texture format. |
|
// Input : format - Texture format to look for. |
|
// Output : Returns TRUE if textures of a given format are available, FALSE if not. |
|
//----------------------------------------------------------------------------- |
|
bool CTextureSystem::HasTexturesForConfig(CGameConfig *pConfig) |
|
{ |
|
if (!pConfig) |
|
return false; |
|
|
|
TextureContext_t *pContext = FindTextureContextForConfig(pConfig); |
|
if (!pContext) |
|
return false; |
|
|
|
int nCount = pContext->Groups.Count(); |
|
for (int i = 0; i < nCount; i++) |
|
{ |
|
CTextureGroup *pGroup = pContext->Groups.Element(i); |
|
if (pGroup->GetTextureFormat() == pConfig->GetTextureFormat()) |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to add all the world materials into the material list |
|
//----------------------------------------------------------------------------- |
|
bool CTextureSystem::EnumMaterial( const char *pMaterialName, int nContext ) |
|
{ |
|
CTextureGroup *pGroup = (CTextureGroup *)nContext; |
|
CMaterial *pMaterial = CMaterial::CreateMaterial(pMaterialName, false); |
|
if (pMaterial != NULL) |
|
{ |
|
// Add it to the master list of textures. |
|
AddTexture(pMaterial); |
|
|
|
// Add the texture's index to the given group and to the "All" group. |
|
pGroup->AddTexture(pMaterial); |
|
if (pGroup != m_pActiveContext->pAllGroup) |
|
{ |
|
m_pActiveContext->pAllGroup->AddTexture(pMaterial); |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Registers the keywords as existing in a particular material |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::RegisterTextureKeywords( IEditorTexture *pTexture ) |
|
{ |
|
// |
|
// Add any new keywords from this material to the list of keywords. |
|
// |
|
char szKeywords[MAX_PATH]; |
|
pTexture->GetKeywords(szKeywords); |
|
if (szKeywords[0] != '\0') |
|
{ |
|
char *pch = strtok(szKeywords, " ,;"); |
|
while (pch != NULL) |
|
{ |
|
// dvs: hide in a Find function |
|
bool bFound = false; |
|
|
|
for( int pos=0; pos < m_Keywords.Count(); pos++ ) |
|
{ |
|
const char *pszTest = m_Keywords.Element(pos); |
|
if (!stricmp(pszTest, pch)) |
|
{ |
|
bFound = true; |
|
break; |
|
} |
|
} |
|
|
|
if (!bFound) |
|
{ |
|
char *pszKeyword = new char[strlen(pch) + 1]; |
|
strcpy(pszKeyword, pch); |
|
m_Keywords.AddToTail(pszKeyword); |
|
} |
|
|
|
pch = strtok(NULL, " ,;"); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to lazily load in all the textures |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::LazyLoadTextures() |
|
{ |
|
if ( m_pActiveContext && m_pActiveContext->pAllGroup && !IsRunningInEngine() ) |
|
{ |
|
m_pActiveContext->pAllGroup->LazyLoadTextures(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : TextureContext_t |
|
//----------------------------------------------------------------------------- |
|
TextureContext_t *CTextureSystem::AddTextureContext() |
|
{ |
|
// Allocate a new texture context. |
|
int nIndex = m_TextureContexts.AddToTail(); |
|
|
|
// Add the group to this config's list of texture groups. |
|
TextureContext_t *pContext = &m_TextureContexts.Element(nIndex); |
|
return pContext; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Opens the source file associated with a material |
|
//----------------------------------------------------------------------------- |
|
void CTextureSystem::OpenSource( const char *pMaterialName ) |
|
{ |
|
if ( !pMaterialName ) |
|
return; |
|
|
|
char pRelativePath[MAX_PATH]; |
|
Q_snprintf( pRelativePath, MAX_PATH, "materials/%s.vmt", pMaterialName ); |
|
|
|
char pFullPath[MAX_PATH]; |
|
if ( g_pFullFileSystem->GetLocalPath( pRelativePath, pFullPath, MAX_PATH ) ) |
|
{ |
|
ShellExecute( NULL, "open", pFullPath, NULL, NULL, SW_SHOWNORMAL ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. |
|
// Input : pszName - Name of group, ie "Materials" or "u:\hl\tfc\tfc.wad". |
|
//----------------------------------------------------------------------------- |
|
CTextureGroup::CTextureGroup(const char *pszName) |
|
{ |
|
strcpy(m_szName, pszName); |
|
m_eTextureFormat = tfNone; |
|
m_nTextureToLoad = 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a texture to this group. |
|
// Input : pTexture - Texture to add. |
|
//----------------------------------------------------------------------------- |
|
void CTextureGroup::AddTexture(IEditorTexture *pTexture) |
|
{ |
|
int index = m_Textures.AddToTail(pTexture); |
|
m_TextureNameMap.Insert( pTexture->GetName(), index ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sorts the group. |
|
//----------------------------------------------------------------------------- |
|
void CTextureGroup::Sort(void) |
|
{ |
|
m_Textures.Sort(SortTexturesProc); |
|
|
|
// Redo the name map. |
|
m_TextureNameMap.RemoveAll(); |
|
for ( int i=0; i < m_Textures.Count(); i++ ) |
|
{ |
|
IEditorTexture *pTex = m_Textures[i]; |
|
m_TextureNameMap.Insert( pTex->GetName(), i ); |
|
} |
|
|
|
// Changing the order means we don't know where we should be loading from |
|
m_nTextureToLoad = 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Retrieves a texture by index. |
|
// Input : nIndex - Index of the texture in this group. |
|
//----------------------------------------------------------------------------- |
|
IEditorTexture *CTextureGroup::GetTexture(int nIndex) |
|
{ |
|
if ((nIndex >= m_Textures.Count()) || (nIndex < 0)) |
|
{ |
|
return(NULL); |
|
} |
|
|
|
return(m_Textures[nIndex]); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// finds a texture by name |
|
//----------------------------------------------------------------------------- |
|
IEditorTexture *CTextureGroup::GetTexture( char const* pName ) |
|
{ |
|
for (int i = 0; i < m_Textures.Count(); i++) |
|
{ |
|
if (!strcmp(pName, m_Textures[i]->GetName())) |
|
return m_Textures[i]; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Quickly find a texture by name. |
|
//----------------------------------------------------------------------------- |
|
IEditorTexture* CTextureGroup::FindTextureByName( const char *pName, int *piIndex, TEXTUREFORMAT eDesiredFormat ) |
|
{ |
|
int iMapEntry = m_TextureNameMap.Find( pName ); |
|
if ( iMapEntry == m_TextureNameMap.InvalidIndex() ) |
|
{ |
|
return NULL; |
|
} |
|
else |
|
{ |
|
IEditorTexture *pTex = m_Textures[ m_TextureNameMap[iMapEntry] ]; |
|
if ((eDesiredFormat == tfNone) || (pTex->GetTextureFormat() == eDesiredFormat)) |
|
return pTex; |
|
else |
|
return NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to lazily load in all the textures |
|
//----------------------------------------------------------------------------- |
|
void CTextureGroup::LazyLoadTextures() |
|
{ |
|
// Load at most once per call |
|
while (m_nTextureToLoad < m_Textures.Count()) |
|
{ |
|
if (!m_Textures[m_nTextureToLoad]->IsLoaded()) |
|
{ |
|
m_Textures[m_nTextureToLoad]->Load(); |
|
++m_nTextureToLoad; |
|
return; |
|
} |
|
|
|
// This one was already loaded; skip it |
|
++m_nTextureToLoad; |
|
} |
|
} |
|
|
|
|