Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Texture management functions. Exposes a list of available textures,
  4. // texture groups, and Most Recently Used textures.
  5. //
  6. // There is one texture context per game configuration in GameCfg.ini.
  7. //
  8. //=============================================================================//
  9. #include "stdafx.h"
  10. #include <process.h>
  11. #include <io.h>
  12. #include <sys\stat.h>
  13. #include <fcntl.h>
  14. #include "DummyTexture.h" // Specific IEditorTexture implementation
  15. #include "GlobalFunctions.h"
  16. #include "MainFrm.h"
  17. #include "MapDoc.h"
  18. #include "Material.h" // Specific IEditorTexture implementation
  19. #include "Options.h"
  20. #include "TextureSystem.h"
  21. #include "WADTexture.h" // Specific IEditorTexture implementation
  22. #include "WADTypes.h"
  23. #include "hammer.h"
  24. #include "filesystem.h"
  25. #include "materialsystem/itexture.h"
  26. #include "tier1/utldict.h"
  27. #include "FaceEditSheet.h"
  28. // memdbgon must be the last include file in a .cpp file!!!
  29. #include <tier0/memdbgon.h>
  30. #pragma warning(disable:4244)
  31. #define _GraphicCacheAllocate(n) malloc(n)
  32. #define IsSortChr(ch) ((ch == '-') || (ch == '+'))
  33. //-----------------------------------------------------------------------------
  34. // Stuff for loading WAD3 files.
  35. //-----------------------------------------------------------------------------
  36. typedef struct
  37. {
  38. int filepos;
  39. int disksize;
  40. int size; // uncompressed
  41. char type;
  42. char compression;
  43. char pad1, pad2;
  44. char name[16]; // must be null terminated
  45. } WAD3lumpinfo_t;
  46. //-----------------------------------------------------------------------------
  47. // List of global graphics
  48. //-----------------------------------------------------------------------------
  49. CTextureSystem g_Textures;
  50. //-----------------------------------------------------------------------------
  51. // CMaterialFileChangeWatcher implementation.
  52. //-----------------------------------------------------------------------------
  53. void CMaterialFileChangeWatcher::Init( CTextureSystem *pSystem, int context )
  54. {
  55. m_pTextureSystem = pSystem;
  56. m_Context = context;
  57. m_Watcher.Init( this );
  58. char searchPaths[1024 * 16];
  59. if ( g_pFullFileSystem->GetSearchPath( "GAME", false, searchPaths, sizeof( searchPaths ) ) > 0 )
  60. {
  61. CUtlVector<char*> searchPathList;
  62. V_SplitString( searchPaths, ";", searchPathList );
  63. for ( int i=0; i < searchPathList.Count(); i++ )
  64. {
  65. m_Watcher.AddDirectory( searchPathList[i], "materials", true );
  66. }
  67. searchPathList.PurgeAndDeleteElements();
  68. }
  69. else
  70. {
  71. Warning( "Error in GetSearchPath. Dynamic material list updating will not be available." );
  72. }
  73. }
  74. void CMaterialFileChangeWatcher::OnFileChange( const char *pRelativeFilename, const char *pFullFilename )
  75. {
  76. //Msg( "OnNewFile: %s\n", pRelativeFilename );
  77. CTextureSystem::EFileType eFileType;
  78. if ( CTextureSystem::GetFileTypeFromFilename( pRelativeFilename, &eFileType ) )
  79. m_pTextureSystem->OnFileChange( pRelativeFilename, m_Context, eFileType );
  80. }
  81. void CMaterialFileChangeWatcher::Update()
  82. {
  83. m_Watcher.Update();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Constructor. Creates the "All" group and sets it as the active group.
  87. //-----------------------------------------------------------------------------
  88. CTextureSystem::CTextureSystem(void)
  89. {
  90. m_pLastTex = NULL;
  91. m_nLastIndex = 0;
  92. m_pActiveContext = NULL;
  93. m_pActiveGroup = NULL;
  94. m_pCubemapTexture = NULL;
  95. m_pNoDrawTexture = NULL;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Destructor. Frees the list of groups and dummy textures.
  99. //-----------------------------------------------------------------------------
  100. CTextureSystem::~CTextureSystem(void)
  101. {
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose:
  105. //-----------------------------------------------------------------------------
  106. void CTextureSystem::FreeAllTextures()
  107. {
  108. if ( m_pCubemapTexture )
  109. {
  110. m_pCubemapTexture->DecrementReferenceCount();
  111. m_pCubemapTexture = NULL;
  112. }
  113. int nContextCount = m_TextureContexts.Count();
  114. for (int nContext = 0; nContext < nContextCount; nContext++)
  115. {
  116. TextureContext_t *pContext = &m_TextureContexts.Element(nContext);
  117. //
  118. // Delete all the texture groups for this context.
  119. //
  120. int nGroupCount = pContext->Groups.Count();
  121. for (int nGroup = 0; nGroup < nGroupCount; nGroup++)
  122. {
  123. delete pContext->Groups.Element(nGroup);
  124. }
  125. //
  126. // Delete dummy textures.
  127. //
  128. int nDummyCount = pContext->Dummies.Count();
  129. for (int nDummy = 0; nDummy < nDummyCount; nDummy++)
  130. {
  131. IEditorTexture *pTex = pContext->Dummies.Element(nDummy);
  132. delete pTex;
  133. }
  134. }
  135. //
  136. // Delete all the textures from the master list.
  137. //
  138. for (int i = 0; i < m_Textures.Count(); i++)
  139. {
  140. IEditorTexture *pTex = m_Textures[i];
  141. delete pTex;
  142. }
  143. m_Textures.RemoveAll();
  144. m_pLastTex = NULL;
  145. m_nLastIndex = -1;
  146. // Delete the keywords.
  147. m_Keywords.PurgeAndDeleteElements();
  148. m_ChangeWatchers.PurgeAndDeleteElements();
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Adds a texture to the master list of textures.
  152. // Input : pTexture - Pointer to texture to add.
  153. // Output : Returns the index of the texture in the master texture list.
  154. //-----------------------------------------------------------------------------
  155. int CTextureSystem::AddTexture(IEditorTexture *pTexture)
  156. {
  157. return m_Textures.AddToTail(pTexture);
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose: Begins iterating the list of texture/material keywords.
  161. //-----------------------------------------------------------------------------
  162. int CTextureSystem::GetNumKeywords(void)
  163. {
  164. return(m_Keywords.Count());
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Continues iterating the list of texture/material keywords.
  168. //-----------------------------------------------------------------------------
  169. const char *CTextureSystem::GetKeyword(int pos)
  170. {
  171. return(m_Keywords.Element(pos));
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose:
  175. // Input : *piIndex -
  176. // bUseMRU -
  177. // Output :
  178. //-----------------------------------------------------------------------------
  179. IEditorTexture *CTextureSystem::EnumActiveTextures(int *piIndex, TEXTUREFORMAT eDesiredFormat) const
  180. {
  181. Assert(piIndex != NULL);
  182. if (piIndex != NULL)
  183. {
  184. if (m_pActiveGroup != NULL)
  185. {
  186. IEditorTexture *pTex = NULL;
  187. do
  188. {
  189. pTex = m_pActiveGroup->GetTexture(*piIndex);
  190. if (pTex != NULL)
  191. {
  192. (*piIndex)++;
  193. if ((eDesiredFormat == tfNone) || (pTex->GetTextureFormat() == eDesiredFormat))
  194. {
  195. return(pTex);
  196. }
  197. }
  198. } while (pTex != NULL);
  199. }
  200. }
  201. return(NULL);
  202. }
  203. //-----------------------------------------------------------------------------
  204. // Purpose: Initializes the texture system.
  205. // Output : Returns true on success, false on failure.
  206. //-----------------------------------------------------------------------------
  207. bool CTextureSystem::Initialize(HWND hwnd)
  208. {
  209. bool bWAD = CWADTexture::Initialize();
  210. bool bMaterial = CMaterial::Initialize(hwnd);
  211. return(bWAD && bMaterial);
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose: Shuts down the texture system.
  215. //-----------------------------------------------------------------------------
  216. void CTextureSystem::ShutDown(void)
  217. {
  218. CWADTexture::ShutDown();
  219. CMaterial::ShutDown();
  220. FreeAllTextures();
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose:
  224. // Input : pszName -
  225. // piIndex -
  226. // bDummy -
  227. // Output :
  228. //-----------------------------------------------------------------------------
  229. IEditorTexture *CTextureSystem::FindActiveTexture(LPCSTR pszInputName, int *piIndex, BOOL bDummy)
  230. {
  231. // The .vmf file format gets confused if there are backslashes in material names,
  232. // so make sure they're all using forward slashes here.
  233. char szName[MAX_PATH];
  234. Q_StrSubst( pszInputName, "\\", "/", szName, sizeof( szName ) );
  235. const char *pszName = szName;
  236. IEditorTexture *pTex = NULL;
  237. //
  238. // Check the cache first.
  239. //
  240. if (m_pLastTex && !stricmp(pszName, m_pLastTex->GetName()))
  241. {
  242. if (piIndex)
  243. {
  244. *piIndex = m_nLastIndex;
  245. }
  246. return m_pLastTex;
  247. }
  248. int iIndex = 0;
  249. // We're finding by name, so we don't care what the format is as long as the name matches.
  250. if ( m_pActiveGroup )
  251. {
  252. pTex = m_pActiveGroup->FindTextureByName( pszName, &iIndex, tfNone );
  253. if ( pTex )
  254. {
  255. if ( piIndex )
  256. *piIndex = iIndex;
  257. m_pLastTex = pTex;
  258. m_nLastIndex = iIndex;
  259. return pTex;
  260. }
  261. }
  262. //
  263. // Let's try again, this time with \textures\ decoration
  264. // TODO: remove this?
  265. //
  266. {
  267. iIndex = 0;
  268. char szBuf[512];
  269. sprintf(szBuf, "textures\\%s", pszName);
  270. for (int i = strlen(szBuf) -1; i >= 0; i--)
  271. {
  272. if (szBuf[i] == '/')
  273. szBuf[i] = '\\';
  274. }
  275. strlwr(szBuf);
  276. if ( m_pActiveGroup )
  277. {
  278. pTex = m_pActiveGroup->FindTextureByName( szBuf, &iIndex, tfNone );
  279. if ( pTex )
  280. {
  281. if ( piIndex )
  282. *piIndex = iIndex;
  283. m_pLastTex = pTex;
  284. m_nLastIndex = iIndex;
  285. return pTex;
  286. }
  287. }
  288. }
  289. //
  290. // Caller doesn't want dummies.
  291. //
  292. if (!bDummy)
  293. {
  294. return(NULL);
  295. }
  296. Assert(!piIndex);
  297. //
  298. // Check the list of dummies for a texture with the same name and texture format.
  299. //
  300. if (m_pActiveContext)
  301. {
  302. int nDummyCount = m_pActiveContext->Dummies.Count();
  303. for (int nDummy = 0; nDummy < nDummyCount; nDummy++)
  304. {
  305. IEditorTexture *pTexDummy = m_pActiveContext->Dummies.Element(nDummy);
  306. if (!strcmpi(pszName, pTexDummy->GetName()))
  307. {
  308. m_pLastTex = pTexDummy;
  309. m_nLastIndex = -1;
  310. return(pTexDummy);
  311. }
  312. }
  313. //
  314. // Not found; add a dummy as a placeholder for the missing texture.
  315. //
  316. pTex = AddDummy(pszName, g_pGameConfig->GetTextureFormat());
  317. }
  318. if (pTex != NULL)
  319. {
  320. m_pLastTex = pTex;
  321. m_nLastIndex = -1;
  322. }
  323. return(pTex);
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. // Input : *pTex -
  328. //-----------------------------------------------------------------------------
  329. void CTextureSystem::AddMRU(IEditorTexture *pTex)
  330. {
  331. if (!m_pActiveContext)
  332. return;
  333. int nIndex = m_pActiveContext->MRU.Find(pTex);
  334. if (nIndex != -1)
  335. {
  336. m_pActiveContext->MRU.Remove(nIndex);
  337. }
  338. else if (m_pActiveContext->MRU.Count() == 8)
  339. {
  340. m_pActiveContext->MRU.Remove(7);
  341. }
  342. m_pActiveContext->MRU.AddToHead(pTex);
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose: Change palette on all textures.
  346. // Input :
  347. // dvs: need to handle a palette change for Quake support
  348. //-----------------------------------------------------------------------------
  349. void CTextureSystem::InformPaletteChanged()
  350. {
  351. // int nGraphics = GetCount();
  352. //
  353. // for (int i = 0; i < nGraphics; i++)
  354. // {
  355. // IEditorTexture *pTex = &GetAt(i);
  356. // }
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose: Returns the texture context that corresponds to the given game config.
  360. //-----------------------------------------------------------------------------
  361. TextureContext_t *CTextureSystem::FindTextureContextForConfig(CGameConfig *pConfig)
  362. {
  363. for (int i = 0; i < m_TextureContexts.Count(); i++)
  364. {
  365. if (m_TextureContexts.Element(i).pConfig == pConfig)
  366. {
  367. return &m_TextureContexts.Element(i);
  368. }
  369. }
  370. return NULL;
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. void CTextureSystem::SetActiveConfig(CGameConfig *pConfig)
  376. {
  377. TextureContext_t *pContext = FindTextureContextForConfig(pConfig);
  378. if (pContext)
  379. {
  380. m_pActiveContext = pContext;
  381. m_pActiveGroup = m_pActiveContext->pAllGroup;
  382. }
  383. else
  384. {
  385. m_pActiveContext = NULL;
  386. }
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Purpose:
  390. // Input : char *pcszName -
  391. //-----------------------------------------------------------------------------
  392. void CTextureSystem::SetActiveGroup(const char *pcszName)
  393. {
  394. if (!m_pActiveContext)
  395. return;
  396. char szBuf[MAX_PATH];
  397. sprintf(szBuf, "textures\\%s", pcszName);
  398. int iCount = m_pActiveContext->Groups.Count();
  399. for (int i = 0; i < iCount; i++)
  400. {
  401. CTextureGroup *pGroup = m_pActiveContext->Groups.Element(i);
  402. if (!strcmpi(pGroup->GetName(), pcszName))
  403. {
  404. m_pActiveGroup = pGroup;
  405. return;
  406. }
  407. if (strstr(pGroup->GetName(), pcszName))
  408. {
  409. m_pActiveGroup = pGroup;
  410. return;
  411. }
  412. }
  413. TRACE0("No Group Found!");
  414. }
  415. void HammerFileSystem_ReportSearchPath( const char *szPathID )
  416. {
  417. char szSearchPath[ 4096 ];
  418. g_pFullFileSystem->GetSearchPath( szPathID, true, szSearchPath, sizeof( szSearchPath ) );
  419. Msg( mwStatus, "------------------------------------------------------------------" );
  420. char *pszOnePath = strtok( szSearchPath, ";" );
  421. while ( pszOnePath )
  422. {
  423. Msg( mwStatus, "Search Path (%s): %s", szPathID, pszOnePath );
  424. pszOnePath = strtok( NULL, ";" );
  425. }
  426. }
  427. //-----------------------------------------------------------------------------
  428. // FIXME: Make this work correctly, using the version in filesystem_tools.cpp
  429. // (it doesn't work currently owing to filesystem setup issues)
  430. //-----------------------------------------------------------------------------
  431. void HammerFileSystem_SetGame( const char *pExeDir, const char *pModDir )
  432. {
  433. static bool s_bOnce = false;
  434. Assert( !s_bOnce );
  435. s_bOnce = true;
  436. char buf[MAX_PATH];
  437. Q_snprintf( buf, MAX_PATH, "%s\\hl2", pExeDir );
  438. g_pFullFileSystem->AddSearchPath( buf, "GAME", PATH_ADD_TO_HEAD );
  439. if ( pModDir && *pModDir != '\0' )
  440. {
  441. g_pFullFileSystem->AddSearchPath( pModDir, "GAME", PATH_ADD_TO_HEAD );
  442. }
  443. HammerFileSystem_ReportSearchPath( "GAME" );
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Loads textures from all texture files.
  447. //-----------------------------------------------------------------------------
  448. void CTextureSystem::LoadAllGraphicsFiles(void)
  449. {
  450. FreeAllTextures();
  451. // For each game config...
  452. // dvs: Disabled for single-config running.
  453. //for (int nConfig = 0; nConfig < Options.configs.GetGameConfigCount(); nConfig++)
  454. {
  455. //CGameConfig *pConfig = Options.configs.GetGameConfig(nConfig);
  456. CGameConfig *pConfig = g_pGameConfig;
  457. // Create a new texture context with the WADs and materials for that config.
  458. TextureContext_t *pContext = AddTextureContext();
  459. // Bind it to this config.
  460. pContext->pConfig = pConfig;
  461. // Create a group to hold all the textures for this context.
  462. pContext->pAllGroup = new CTextureGroup("All Textures");
  463. pContext->Groups.AddToTail(pContext->pAllGroup);
  464. HammerFileSystem_SetGame(pConfig->m_szGameExeDir, pConfig->m_szModDir);
  465. // Set the new context as the active context.
  466. m_pActiveContext = pContext;
  467. // Load the textures for all WAD files set in this config.
  468. // Only do this for configs that use WAD textures.
  469. if (pConfig->GetTextureFormat() == tfWAD3)
  470. {
  471. LoadWADFiles(pConfig);
  472. }
  473. // Load the materials for this config.
  474. // Do this unconditionally so that we get necessary editor materials.
  475. LoadMaterials(pConfig);
  476. m_pActiveContext->pAllGroup->Sort();
  477. }
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose: Loads all WAD files for the given game config.
  481. //-----------------------------------------------------------------------------
  482. void CTextureSystem::LoadWADFiles(CGameConfig *pConfig)
  483. {
  484. // dvs: FIXME: WADs are not currently per-config
  485. for (int i = 0; i < Options.textures.nTextureFiles; i++)
  486. {
  487. LoadGraphicsFile(Options.textures.TextureFiles[i]);
  488. }
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose: Loads all the materials for the given game config.
  492. //-----------------------------------------------------------------------------
  493. void CTextureSystem::LoadMaterials(CGameConfig *pConfig)
  494. {
  495. CTextureGroup *pGroup = new CTextureGroup("Materials");
  496. pGroup->SetTextureFormat(tfVMT);
  497. m_pActiveContext->Groups.AddToTail(pGroup);
  498. // Add all the materials to the group.
  499. CMaterial::EnumerateMaterials( this, "materials", (int)pGroup, INCLUDE_WORLD_MATERIALS );
  500. // Watch the materials directory recursively...
  501. CMaterialFileChangeWatcher *pWatcher = new CMaterialFileChangeWatcher;
  502. pWatcher->Init( this, (int)pGroup );
  503. m_ChangeWatchers.AddToTail( pWatcher );
  504. Assert( m_pCubemapTexture == NULL );
  505. m_pCubemapTexture = MaterialSystemInterface()->FindTexture( "editor/cubemap", NULL, true );
  506. if ( m_pCubemapTexture )
  507. {
  508. m_pCubemapTexture->IncrementReferenceCount();
  509. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  510. pRenderContext->BindLocalCubemap( m_pCubemapTexture );
  511. }
  512. // Get the nodraw texture.
  513. m_pNoDrawTexture = NULL;
  514. for ( int i=0; i < m_Textures.Count(); i++ )
  515. {
  516. if ( V_stricmp( m_Textures[i]->GetName(), "tools/toolsnodraw" ) == 0 || V_stricmp( m_Textures[i]->GetName(), "tools/toolsnodraw" ) == 0 )
  517. {
  518. m_pNoDrawTexture = m_Textures[i];
  519. break;
  520. }
  521. }
  522. if ( !m_pNoDrawTexture )
  523. m_pNoDrawTexture = CMaterial::CreateMaterial( "tools/toolsnodraw", true );
  524. }
  525. void CTextureSystem::RebindDefaultCubeMap()
  526. {
  527. // rebind with the default cubemap
  528. if ( m_pCubemapTexture )
  529. {
  530. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  531. pRenderContext->BindLocalCubemap( m_pCubemapTexture );
  532. }
  533. }
  534. void CTextureSystem::UpdateFileChangeWatchers()
  535. {
  536. for ( int i=0; i < m_ChangeWatchers.Count(); i++ )
  537. m_ChangeWatchers[i]->Update();
  538. }
  539. void CTextureSystem::OnFileChange( const char *pFilename, int context, CTextureSystem::EFileType eFileType )
  540. {
  541. // It requires the forward slashes later...
  542. char fixedSlashes[MAX_PATH];
  543. V_StrSubst( pFilename, "\\", "/", fixedSlashes, sizeof( fixedSlashes ) );
  544. // Get rid of the extension.
  545. if ( V_strlen( fixedSlashes ) < 5 )
  546. {
  547. Assert( false );
  548. return;
  549. }
  550. fixedSlashes[ V_strlen( fixedSlashes ) - 4 ] = 0;
  551. // Handle it based on what type of file we've got.
  552. if ( eFileType == k_eFileTypeVMT )
  553. {
  554. IEditorTexture *pTex = FindActiveTexture( fixedSlashes, NULL, FALSE );
  555. if ( pTex )
  556. {
  557. pTex->Reload( true );
  558. }
  559. else
  560. {
  561. EnumMaterial( fixedSlashes, context );
  562. IEditorTexture *pTexFixed = FindActiveTexture( fixedSlashes, NULL, FALSE );
  563. if ( pTexFixed )
  564. {
  565. GetMainWnd()->m_TextureBar.NotifyNewMaterial( pTexFixed );
  566. GetMainWnd()->GetFaceEditSheet()->NotifyNewMaterial( pTexFixed );
  567. }
  568. }
  569. }
  570. else if ( eFileType == k_eFileTypeVTF )
  571. {
  572. // Whether a VTF was added, removed, or modified, we do the same thing.. refresh it and any materials that reference it.
  573. ITexture *pTexture = materials->FindTexture( fixedSlashes, TEXTURE_GROUP_UNACCOUNTED, false );
  574. if ( pTexture )
  575. {
  576. pTexture->Download( NULL );
  577. ReloadMaterialsUsingTexture( pTexture );
  578. }
  579. }
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose: Load any materials that reference this texture. Used so we can refresh a
  583. // material's preview image if a relevant .vtf changes.
  584. //-----------------------------------------------------------------------------
  585. void CTextureSystem::ReloadMaterialsUsingTexture( ITexture *pTestTexture )
  586. {
  587. for ( int i=0; i < m_Textures.Count(); i++ )
  588. {
  589. IEditorTexture *pEditorTex = m_Textures[i];
  590. IMaterial *pMat = pEditorTex->GetMaterial( false );
  591. if ( !pMat )
  592. continue;
  593. IMaterialVar **pParams = pMat->GetShaderParams();
  594. int nParams = pMat->ShaderParamCount();
  595. for ( int iParam=0; iParam < nParams; iParam++ )
  596. {
  597. if ( pParams[iParam]->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  598. continue;
  599. ITexture *pTex = pParams[iParam]->GetTextureValue();
  600. if ( !pTex )
  601. continue;
  602. if ( pTex == pTestTexture )
  603. {
  604. pEditorTex->Reload( true );
  605. }
  606. }
  607. }
  608. }
  609. //-----------------------------------------------------------------------------
  610. // Purpose: Figure out the file type from its extension. Returns false if we don't have an enum for that extension.
  611. //-----------------------------------------------------------------------------
  612. bool CTextureSystem::GetFileTypeFromFilename( const char *pFilename, CTextureSystem::EFileType *pFileType )
  613. {
  614. char strRight[16];
  615. V_StrRight( pFilename, 4, strRight, sizeof( strRight ) );
  616. if ( V_stricmp( strRight, ".vmt" ) == 0 )
  617. {
  618. *pFileType = CTextureSystem::k_eFileTypeVMT;
  619. return true;
  620. }
  621. else if ( V_stricmp( strRight, ".vtf" ) == 0 )
  622. {
  623. *pFileType = CTextureSystem::k_eFileTypeVTF;
  624. return true;
  625. }
  626. return false;
  627. }
  628. //-----------------------------------------------------------------------------
  629. // Purpose: Loads textures from all texture files.
  630. //-----------------------------------------------------------------------------
  631. void CTextureSystem::ReloadTextures( const char *pFilterName )
  632. {
  633. MaterialSystemInterface()->ReloadMaterials( pFilterName );
  634. for ( int i = 0; i < m_Textures.Count(); i++ )
  635. {
  636. if ( !Q_stristr( pFilterName, m_Textures[i]->GetName() ) )
  637. continue;
  638. m_Textures[i]->Reload( false );
  639. }
  640. }
  641. //-----------------------------------------------------------------------------
  642. // Purpose: Adds a placeholder texture for a texture that exists in the map, but
  643. // was not found on disk.
  644. // Input : pszName - Name of missing texture.
  645. // Output : Returns a pointer to the new dummy texture.
  646. //-----------------------------------------------------------------------------
  647. IEditorTexture *CTextureSystem::AddDummy(LPCTSTR pszName, TEXTUREFORMAT eFormat)
  648. {
  649. if (!m_pActiveContext)
  650. return NULL;
  651. IEditorTexture *pTex = new CDummyTexture(pszName, eFormat);
  652. m_pActiveContext->Dummies.AddToTail(pTex);
  653. return(pTex);
  654. }
  655. //-----------------------------------------------------------------------------
  656. // Purpose:
  657. // Input : elem1 -
  658. // elem2 -
  659. // Output : static int __cdecl
  660. //-----------------------------------------------------------------------------
  661. static int __cdecl SortTexturesProc(IEditorTexture * const *elem1, IEditorTexture * const *elem2)
  662. {
  663. IEditorTexture *pElem1 = *((IEditorTexture **)elem1);
  664. IEditorTexture *pElem2 = *((IEditorTexture **)elem2);
  665. Assert((pElem1 != NULL) && (pElem2 != NULL));
  666. if ((pElem1 == NULL) || (pElem2 == NULL))
  667. {
  668. return(0);
  669. }
  670. const char *pszName1 = pElem1->GetName();
  671. const char *pszName2 = pElem2->GetName();
  672. char ch1 = pszName1[0];
  673. char ch2 = pszName2[0];
  674. if (IsSortChr(ch1) && !IsSortChr(ch2))
  675. {
  676. int iFamilyLen = strlen(pszName1+2);
  677. int iFamily = strnicmp(pszName1+2, pszName2, iFamilyLen);
  678. if (!iFamily)
  679. {
  680. return(-1); // same family - put elem1 before elem2
  681. }
  682. return(iFamily); // sort normally
  683. }
  684. else if (!IsSortChr(ch1) && IsSortChr(ch2))
  685. {
  686. int iFamilyLen = strlen(pszName2+2);
  687. int iFamily = strnicmp(pszName1, pszName2+2, iFamilyLen);
  688. if (!iFamily)
  689. {
  690. return(1); // same family - put elem2 before elem1
  691. }
  692. return(iFamily); // sort normally
  693. }
  694. else if (IsSortChr(ch1) && IsSortChr(ch2))
  695. {
  696. // do family name sorting
  697. int iFamily = strcmpi(pszName1+2, pszName2+2);
  698. if (!iFamily)
  699. {
  700. // same family - sort by number
  701. return pszName1[1] - pszName2[1];
  702. }
  703. // different family
  704. return(iFamily);
  705. }
  706. return(strcmpi(pszName1, pszName2));
  707. }
  708. //-----------------------------------------------------------------------------
  709. // Purpose:
  710. // Input : sizeSrc -
  711. // sizeDest -
  712. // *src -
  713. // *dest -
  714. //-----------------------------------------------------------------------------
  715. void ScaleBitmap(CSize sizeSrc, CSize sizeDest, char *src, char *dest)
  716. {
  717. int i;
  718. int e_y = (sizeSrc.cy << 1) - sizeDest.cy;
  719. int sizeDest2_y = (sizeDest.cy << 1);
  720. int sizeSrc2_y = sizeSrc.cy << 1;
  721. int srcline = 0, destline = 0;
  722. char *srclinep, *destlinep;
  723. int e_x = (sizeSrc.cx << 1) - sizeDest.cx;
  724. int sizeDest2_x = (sizeDest.cx << 1);
  725. int sizeSrc2_x = sizeSrc.cx << 1;
  726. for( i = 0; i < sizeDest.cy; i++ )
  727. {
  728. // scale by X
  729. {
  730. srclinep = src + (srcline * sizeSrc.cx);
  731. destlinep = dest + (destline * sizeDest.cx);
  732. for( int j = 0; j < sizeDest.cx; j++ )
  733. {
  734. *destlinep = *srclinep;
  735. while( e_x >= 0 )
  736. {
  737. ++srclinep;
  738. e_x -= sizeDest2_x;
  739. }
  740. ++destlinep;
  741. e_x += sizeSrc2_x;
  742. }
  743. }
  744. while( e_y >= 0 )
  745. {
  746. ++srcline;
  747. e_y -= sizeDest2_y;
  748. }
  749. ++destline;
  750. e_y += sizeSrc2_y;
  751. }
  752. }
  753. //-----------------------------------------------------------------------------
  754. // Purpose:
  755. // Input : id -
  756. // *piIndex -
  757. // Output : GRAPHICSFILESTRUCT *
  758. //-----------------------------------------------------------------------------
  759. bool CTextureSystem::FindGraphicsFile(GRAPHICSFILESTRUCT *pFileInfo, DWORD id, int *piIndex)
  760. {
  761. for (int i = 0; i < m_GraphicsFiles.Count(); i++)
  762. {
  763. if (m_GraphicsFiles[i].id == id)
  764. {
  765. if (piIndex)
  766. {
  767. piIndex[0] = i;
  768. }
  769. if (pFileInfo != NULL)
  770. {
  771. *pFileInfo = m_GraphicsFiles[i];
  772. }
  773. return(true);
  774. }
  775. }
  776. return(false);
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose:
  780. // Input : pFile -
  781. // fd -
  782. // pGroup -
  783. //-----------------------------------------------------------------------------
  784. void CTextureSystem::LoadGraphicsFileWAD3(GRAPHICSFILESTRUCT *pFile, int fd, CTextureGroup *pGroup)
  785. {
  786. // read wad header
  787. wadinfo_t hdr;
  788. _lseek(fd, 0, SEEK_SET);
  789. _read(fd, (char*)&hdr, sizeof hdr);
  790. _lseek(fd, hdr.infotableofs, SEEK_SET);
  791. // allocate directory memory.
  792. WAD3lumpinfo_t *dir = new WAD3lumpinfo_t[hdr.numlumps];
  793. // read entries.
  794. _read(fd, dir, sizeof(WAD3lumpinfo_t) * hdr.numlumps);
  795. // load graphics!
  796. for (int i = 0; i < hdr.numlumps; i++)
  797. {
  798. if (dir[i].type == TYP_MIPTEX)
  799. {
  800. _lseek(fd, dir[i].filepos, SEEK_SET);
  801. CWADTexture *pNew = new CWADTexture;
  802. if (pNew != NULL)
  803. {
  804. if (pNew->Init(fd, pFile->id, FALSE, dir[i].name))
  805. {
  806. pNew->SetTextureFormat(pFile->format);
  807. //
  808. // Add the texture to master list of textures.
  809. //
  810. AddTexture(pNew);
  811. //
  812. // Add the texture's index to the given group and to the "All" group.
  813. //
  814. pGroup->AddTexture(pNew);
  815. if (pGroup != m_pActiveContext->pAllGroup)
  816. {
  817. m_pActiveContext->pAllGroup->AddTexture(pNew);
  818. }
  819. }
  820. else
  821. {
  822. delete pNew;
  823. }
  824. }
  825. }
  826. }
  827. // free memory
  828. delete[] dir;
  829. }
  830. //-----------------------------------------------------------------------------
  831. // Purpose: Loads all textures in a given graphics file and returns an ID for
  832. // the file.
  833. // Input : filename - Full path of graphics file to load.
  834. // Output : Returns the file ID.
  835. //-----------------------------------------------------------------------------
  836. DWORD CTextureSystem::LoadGraphicsFile(const char *pFilename)
  837. {
  838. static DWORD __GraphFileID = 1; // must start at 1.
  839. //
  840. // Make sure it's not already there.
  841. //
  842. int i = m_GraphicsFiles.Count() - 1;
  843. while (i > -1)
  844. {
  845. if (!strcmp(m_GraphicsFiles[i].filename, pFilename))
  846. {
  847. return(m_GraphicsFiles[i].id);
  848. }
  849. i--;
  850. }
  851. //
  852. // Is this a WAD file?
  853. //
  854. DWORD dwAttrib = GetFileAttributes(pFilename);
  855. if (dwAttrib == 0xFFFFFFFF)
  856. {
  857. return(0);
  858. }
  859. GRAPHICSFILESTRUCT gf;
  860. if (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
  861. {
  862. // open the file, and add it to the GraphicFileList array
  863. gf.fd = _open(pFilename, _O_BINARY | _O_RDONLY);
  864. if (gf.fd == -1)
  865. {
  866. // todo: if errno is "out of handles", close some other
  867. // graphics files.
  868. // StatusMsg(IDS_ERROPENGRAPHFILE, errno);
  869. return 0; // could not open
  870. }
  871. char buf[4];
  872. _read(gf.fd, buf, 4);
  873. //
  874. // Make sure the file is in a format that we can read.
  875. //
  876. if (!memcmp(buf, "WAD3", 4))
  877. {
  878. gf.format = tfWAD3;
  879. }
  880. else
  881. {
  882. char str[MAX_PATH*2];
  883. Q_snprintf( str, sizeof(str), "The file \"%s\" is not a valid WAD3 file and will not be used.", pFilename);
  884. AfxMessageBox(str, MB_ICONEXCLAMATION | MB_OK);
  885. _close(gf.fd);
  886. return(0);
  887. }
  888. }
  889. // got it -- setup the rest of the gf structure
  890. gf.id = __GraphFileID++;
  891. Q_strncpy( gf.filename, pFilename, sizeof(gf.filename) );
  892. gf.bLoaded = FALSE;
  893. //
  894. // Add file to list of texture files.
  895. //
  896. m_GraphicsFiles.AddToTail(gf);
  897. //
  898. // Create a new texture group for the file.
  899. //
  900. CTextureGroup *pGroup = new CTextureGroup(pFilename);
  901. pGroup->SetTextureFormat(gf.format);
  902. m_pActiveContext->Groups.AddToTail(pGroup);
  903. //
  904. // Load the textures from the file and place them in the texture group.
  905. //
  906. LoadGraphicsFileWAD3(&gf, gf.fd, pGroup);
  907. gf.bLoaded = TRUE;
  908. //
  909. // Sort this group's list
  910. //
  911. pGroup->Sort();
  912. return(gf.id);
  913. }
  914. //-----------------------------------------------------------------------------
  915. // Purpose: Determines whether or not there is at least one available texture
  916. // group for a given texture format.
  917. // Input : format - Texture format to look for.
  918. // Output : Returns TRUE if textures of a given format are available, FALSE if not.
  919. //-----------------------------------------------------------------------------
  920. bool CTextureSystem::HasTexturesForConfig(CGameConfig *pConfig)
  921. {
  922. if (!pConfig)
  923. return false;
  924. TextureContext_t *pContext = FindTextureContextForConfig(pConfig);
  925. if (!pContext)
  926. return false;
  927. int nCount = pContext->Groups.Count();
  928. for (int i = 0; i < nCount; i++)
  929. {
  930. CTextureGroup *pGroup = pContext->Groups.Element(i);
  931. if (pGroup->GetTextureFormat() == pConfig->GetTextureFormat())
  932. {
  933. return true;
  934. }
  935. }
  936. return false;
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Used to add all the world materials into the material list
  940. //-----------------------------------------------------------------------------
  941. bool CTextureSystem::EnumMaterial( const char *pMaterialName, int nContext )
  942. {
  943. CTextureGroup *pGroup = (CTextureGroup *)nContext;
  944. CMaterial *pMaterial = CMaterial::CreateMaterial(pMaterialName, false);
  945. if (pMaterial != NULL)
  946. {
  947. // Add it to the master list of textures.
  948. AddTexture(pMaterial);
  949. // Add the texture's index to the given group and to the "All" group.
  950. pGroup->AddTexture(pMaterial);
  951. if (pGroup != m_pActiveContext->pAllGroup)
  952. {
  953. m_pActiveContext->pAllGroup->AddTexture(pMaterial);
  954. }
  955. }
  956. return true;
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Registers the keywords as existing in a particular material
  960. //-----------------------------------------------------------------------------
  961. void CTextureSystem::RegisterTextureKeywords( IEditorTexture *pTexture )
  962. {
  963. //
  964. // Add any new keywords from this material to the list of keywords.
  965. //
  966. char szKeywords[MAX_PATH];
  967. pTexture->GetKeywords(szKeywords);
  968. if (szKeywords[0] != '\0')
  969. {
  970. char *pch = strtok(szKeywords, " ,;");
  971. while (pch != NULL)
  972. {
  973. // dvs: hide in a Find function
  974. bool bFound = false;
  975. for( int pos=0; pos < m_Keywords.Count(); pos++ )
  976. {
  977. const char *pszTest = m_Keywords.Element(pos);
  978. if (!stricmp(pszTest, pch))
  979. {
  980. bFound = true;
  981. break;
  982. }
  983. }
  984. if (!bFound)
  985. {
  986. char *pszKeyword = new char[strlen(pch) + 1];
  987. strcpy(pszKeyword, pch);
  988. m_Keywords.AddToTail(pszKeyword);
  989. }
  990. pch = strtok(NULL, " ,;");
  991. }
  992. }
  993. }
  994. //-----------------------------------------------------------------------------
  995. // Used to lazily load in all the textures
  996. //-----------------------------------------------------------------------------
  997. void CTextureSystem::LazyLoadTextures()
  998. {
  999. if ( m_pActiveContext && m_pActiveContext->pAllGroup && !IsRunningInEngine() )
  1000. {
  1001. m_pActiveContext->pAllGroup->LazyLoadTextures();
  1002. }
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Purpose:
  1006. // Output : TextureContext_t
  1007. //-----------------------------------------------------------------------------
  1008. TextureContext_t *CTextureSystem::AddTextureContext()
  1009. {
  1010. // Allocate a new texture context.
  1011. int nIndex = m_TextureContexts.AddToTail();
  1012. // Add the group to this config's list of texture groups.
  1013. TextureContext_t *pContext = &m_TextureContexts.Element(nIndex);
  1014. return pContext;
  1015. }
  1016. //-----------------------------------------------------------------------------
  1017. // Opens the source file associated with a material
  1018. //-----------------------------------------------------------------------------
  1019. void CTextureSystem::OpenSource( const char *pMaterialName )
  1020. {
  1021. if ( !pMaterialName )
  1022. return;
  1023. char pRelativePath[MAX_PATH];
  1024. Q_snprintf( pRelativePath, MAX_PATH, "materials/%s.vmt", pMaterialName );
  1025. char pFullPath[MAX_PATH];
  1026. if ( g_pFullFileSystem->GetLocalPath( pRelativePath, pFullPath, MAX_PATH ) )
  1027. {
  1028. ShellExecute( NULL, "open", pFullPath, NULL, NULL, SW_SHOWNORMAL );
  1029. }
  1030. }
  1031. //-----------------------------------------------------------------------------
  1032. // Purpose: Constructor.
  1033. // Input : pszName - Name of group, ie "Materials" or "u:\hl\tfc\tfc.wad".
  1034. //-----------------------------------------------------------------------------
  1035. CTextureGroup::CTextureGroup(const char *pszName)
  1036. {
  1037. strcpy(m_szName, pszName);
  1038. m_eTextureFormat = tfNone;
  1039. m_nTextureToLoad = 0;
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. // Purpose: Adds a texture to this group.
  1043. // Input : pTexture - Texture to add.
  1044. //-----------------------------------------------------------------------------
  1045. void CTextureGroup::AddTexture(IEditorTexture *pTexture)
  1046. {
  1047. int index = m_Textures.AddToTail(pTexture);
  1048. m_TextureNameMap.Insert( pTexture->GetName(), index );
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. // Purpose: Sorts the group.
  1052. //-----------------------------------------------------------------------------
  1053. void CTextureGroup::Sort(void)
  1054. {
  1055. m_Textures.Sort(SortTexturesProc);
  1056. // Redo the name map.
  1057. m_TextureNameMap.RemoveAll();
  1058. for ( int i=0; i < m_Textures.Count(); i++ )
  1059. {
  1060. IEditorTexture *pTex = m_Textures[i];
  1061. m_TextureNameMap.Insert( pTex->GetName(), i );
  1062. }
  1063. // Changing the order means we don't know where we should be loading from
  1064. m_nTextureToLoad = 0;
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose: Retrieves a texture by index.
  1068. // Input : nIndex - Index of the texture in this group.
  1069. //-----------------------------------------------------------------------------
  1070. IEditorTexture *CTextureGroup::GetTexture(int nIndex)
  1071. {
  1072. if ((nIndex >= m_Textures.Count()) || (nIndex < 0))
  1073. {
  1074. return(NULL);
  1075. }
  1076. return(m_Textures[nIndex]);
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // finds a texture by name
  1080. //-----------------------------------------------------------------------------
  1081. IEditorTexture *CTextureGroup::GetTexture( char const* pName )
  1082. {
  1083. for (int i = 0; i < m_Textures.Count(); i++)
  1084. {
  1085. if (!strcmp(pName, m_Textures[i]->GetName()))
  1086. return m_Textures[i];
  1087. }
  1088. return NULL;
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Quickly find a texture by name.
  1092. //-----------------------------------------------------------------------------
  1093. IEditorTexture* CTextureGroup::FindTextureByName( const char *pName, int *piIndex, TEXTUREFORMAT eDesiredFormat )
  1094. {
  1095. int iMapEntry = m_TextureNameMap.Find( pName );
  1096. if ( iMapEntry == m_TextureNameMap.InvalidIndex() )
  1097. {
  1098. return NULL;
  1099. }
  1100. else
  1101. {
  1102. IEditorTexture *pTex = m_Textures[ m_TextureNameMap[iMapEntry] ];
  1103. if ((eDesiredFormat == tfNone) || (pTex->GetTextureFormat() == eDesiredFormat))
  1104. return pTex;
  1105. else
  1106. return NULL;
  1107. }
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. // Used to lazily load in all the textures
  1111. //-----------------------------------------------------------------------------
  1112. void CTextureGroup::LazyLoadTextures()
  1113. {
  1114. // Load at most once per call
  1115. while (m_nTextureToLoad < m_Textures.Count())
  1116. {
  1117. if (!m_Textures[m_nTextureToLoad]->IsLoaded())
  1118. {
  1119. m_Textures[m_nTextureToLoad]->Load();
  1120. ++m_nTextureToLoad;
  1121. return;
  1122. }
  1123. // This one was already loaded; skip it
  1124. ++m_nTextureToLoad;
  1125. }
  1126. }