summaryrefslogtreecommitdiff
path: root/hammer/texturesystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hammer/texturesystem.cpp')
-rw-r--r--hammer/texturesystem.cpp1360
1 files changed, 1360 insertions, 0 deletions
diff --git a/hammer/texturesystem.cpp b/hammer/texturesystem.cpp
new file mode 100644
index 0000000..4c849e1
--- /dev/null
+++ b/hammer/texturesystem.cpp
@@ -0,0 +1,1360 @@
+//========= 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, int 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, int 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;
+ }
+}
+