Counter Strike : Global Offensive Source Code
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.

639 lines
20 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdafx.h>
  8. #include "MapWorld.h"
  9. #include "MessageWnd.h"
  10. #include "IEditorTexture.h"
  11. #include "GlobalFunctions.h"
  12. #include "TextureSystem.h"
  13. #include "TextureConverter.h"
  14. #include "FileSystem.h"
  15. #include "Hammer.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. CProgressDlg * CTextureConverter::m_pProgDlg;
  19. int CTextureConverter::m_nSolidCount;
  20. int CTextureConverter::m_nFaceCount;
  21. int CTextureConverter::m_nDecalCount;
  22. int CTextureConverter::m_nCurrentSolid;
  23. int CTextureConverter::m_nCurrentDecal;
  24. int CTextureConverter::m_nSuccesses;
  25. int CTextureConverter::m_nErrors;
  26. int CTextureConverter::m_nSkipped;
  27. int CTextureConverter::m_nWarnings;
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Reset counters.
  30. // Input :
  31. // Output : Counters all reset to 0.
  32. //-----------------------------------------------------------------------------
  33. void CTextureConverter::Initialize( void )
  34. {
  35. m_nSolidCount = 0;
  36. m_nCurrentSolid = 0;
  37. m_nFaceCount = 0;
  38. m_nDecalCount = 0;
  39. m_nCurrentDecal = 0;
  40. m_nSuccesses = 0;
  41. m_nErrors = 0;
  42. m_nSkipped = 0;
  43. m_nWarnings = 0;
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose: Recurse through the contents of the map, passing solid objects on
  47. // to be converted.
  48. // Input : pWorld - pointer to the map to have textures converted.
  49. // Output : All solid faces and decals in the world have WAD3 textures
  50. // converted to VMT.
  51. //-----------------------------------------------------------------------------
  52. void CTextureConverter::ConvertWorldTextures( CMapWorld * pWorld )
  53. {
  54. Initialize();
  55. // Bring the message window to the front, to display conversion info
  56. g_pwndMessage->Activate();
  57. Msg( mwStatus, "Converting textures from WAD to VMT format..." );
  58. // Set up a progress meter dialogue
  59. m_pProgDlg = new CProgressDlg;
  60. m_pProgDlg->Create();
  61. m_pProgDlg->SetStep( 1 );
  62. m_pProgDlg->SetWindowText( "Preparing to convert textures..." );
  63. // Run the converter
  64. ConvertSolids( pWorld );
  65. ConvertDecals( pWorld );
  66. DisplayStatistics();
  67. // Destroy the progress meter
  68. if ( m_pProgDlg )
  69. {
  70. m_pProgDlg->DestroyWindow();
  71. delete m_pProgDlg;
  72. m_pProgDlg = NULL;
  73. }
  74. AfxMessageBox( "Conversion complete. Check the Hammer \"Messages\" window for complete details." );
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose: Recurse through the contents of the map, passing solid objects on
  78. // to be converted.
  79. // Input : pWorld - pointer to the map to have textures converted.
  80. // Output : All solid faces in the world have WAD3 textures converted to VMT.
  81. //-----------------------------------------------------------------------------
  82. void CTextureConverter::ConvertSolids( CMapWorld * pWorld )
  83. {
  84. // Count total map solids so we know how many we have to do (for progress meter).
  85. pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CountMapSolids ), 0, MAPCLASS_TYPE( CMapSolid ) );
  86. m_pProgDlg->SetRange( 0, m_nSolidCount );
  87. m_pProgDlg->SetStep( 2 );
  88. m_pProgDlg->SetWindowText( "Converting solids..." );
  89. // Cycle through the solids again and convert as necessary.
  90. pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CheckSolidTextures ), 0, MAPCLASS_TYPE( CMapSolid ) );
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose: Enumeration function, increment the solids counter.
  94. // Input :
  95. // Output : Always return true to continue enumerating.
  96. //-----------------------------------------------------------------------------
  97. bool CTextureConverter::CountMapSolids( CMapSolid *, DWORD )
  98. {
  99. m_nSolidCount++;
  100. return true; // return true to continue enumerating
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Enumeration function, check all the faces of a solid for texture conversion
  104. // Input : pSolid - map solid to be checked.
  105. // Output : Always return true to continue enumerating.
  106. //-----------------------------------------------------------------------------
  107. bool CTextureConverter::CheckSolidTextures( CMapSolid * pSolid, DWORD )
  108. {
  109. int nFaceCount;
  110. m_nCurrentSolid++;
  111. if ( m_nCurrentSolid % 100 == 0 )
  112. m_pProgDlg->SetPos( m_nCurrentSolid );
  113. // check each face of the solid
  114. nFaceCount = pSolid->GetFaceCount();
  115. while( nFaceCount-- )
  116. {
  117. CheckFaceTexture( pSolid->GetFace( nFaceCount ) );
  118. }
  119. return true; // return true to continue enumerating
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Check the texture of a face to determine if conversion is necessary.
  123. // Input : pFace - a map face.
  124. // Output :
  125. //-----------------------------------------------------------------------------
  126. void CTextureConverter::CheckFaceTexture( CMapFace * pFace )
  127. {
  128. m_nFaceCount++;
  129. // Criteria for needing conversion is a) being a dummy texture AND b) having no slashes
  130. // in the texture name.
  131. if ( !pFace->GetTexture()->IsDummy() )
  132. {
  133. m_nSkipped++;
  134. return;
  135. }
  136. if ( strchr( pFace->GetTexture()->GetName(), '/') != NULL )
  137. {
  138. m_nSkipped++;
  139. return;
  140. }
  141. ConvertFaceTexture( pFace );
  142. }
  143. bool TextureEndsIn( const char *pTextureName, const char *pEnd )
  144. {
  145. const char *pLast = strrchr( pTextureName, '\\' );
  146. if ( strrchr( pTextureName, '/' ) > pLast )
  147. pLast = strrchr( pTextureName, '/' );
  148. if ( pLast )
  149. return stricmp( pLast+1, pEnd ) == 0;
  150. else
  151. return stricmp( pTextureName, pEnd ) == 0;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose: Determine if any materials match the old texture of a face and replace
  155. // appropriately.
  156. // Input : pFace - a map face known to need conversion.
  157. // Output :
  158. //-----------------------------------------------------------------------------
  159. void CTextureConverter::ConvertFaceTexture( CMapFace * pFace )
  160. {
  161. EditorTextureList_t tlMatches;
  162. IEditorTexture *pNewTexture;
  163. const char *pTextureName = pFace->GetTexture()->GetName();
  164. // Check for SKY and SKIP brushes.
  165. char *replacements[][2] =
  166. {
  167. { "sky", "tools/toolsskybox" },
  168. { "skip", "tools/toolsskip" },
  169. { "aaatrigger", "tools/toolstrigger" },
  170. { "hint", "tools/toolshint" },
  171. { "clip", "tools/toolsclip" },
  172. { "null", "tools/toolsnodraw" }
  173. };
  174. for ( int i=0; i < sizeof( replacements ) / sizeof( replacements[0] ); i++ )
  175. {
  176. if ( TextureEndsIn( pTextureName, replacements[i][0] ) )
  177. {
  178. pNewTexture = g_Textures.FindActiveTexture( replacements[i][1] );
  179. if ( pNewTexture )
  180. {
  181. ReplaceFaceTexture( pFace, pNewTexture );
  182. return;
  183. }
  184. }
  185. }
  186. GetNewTextureMatches( pTextureName, tlMatches );
  187. switch( tlMatches.Count() )
  188. {
  189. case 0:
  190. MsgConvertFace( pFace, "ERROR: No matching material. Cannot convert." );
  191. m_nErrors++;
  192. break;
  193. case 1:
  194. pNewTexture = tlMatches.Element(0);
  195. ReplaceFaceTexture( pFace, pNewTexture );
  196. break;
  197. default:
  198. // Multiple matches. For now, just use the first.
  199. pNewTexture = tlMatches.Element(0);
  200. ReplaceFaceTexture( pFace, pNewTexture );
  201. break;
  202. }
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose: Recurse through the contents of the map, passing decal objects on
  206. // to be converted.
  207. // Input : pWorld - pointer to the map to have textures converted.
  208. // Output : All decals in the world have WAD3 textures converted to VMT.
  209. //-----------------------------------------------------------------------------
  210. void CTextureConverter::ConvertDecals( CMapWorld * pWorld )
  211. {
  212. // Count total map decals so we know how many we have to do (for progress meter).
  213. pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CountMapDecals ), 0, MAPCLASS_TYPE( CMapEntity ) );
  214. m_pProgDlg->SetRange( 0, m_nDecalCount );
  215. m_pProgDlg->SetStep( 3 );
  216. m_pProgDlg->SetWindowText( "Converting decals..." );
  217. // Cycle through the solids again and convert as necessary.
  218. pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CheckDecalTextures ), 0, MAPCLASS_TYPE( CMapEntity ) );
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Enumeration function, increment the decals counter if entity is a decal.
  222. // Input :
  223. // Output : Always return true to continue enumerating.
  224. //-----------------------------------------------------------------------------
  225. bool CTextureConverter::CountMapDecals( CMapEntity * pEnt, DWORD )
  226. {
  227. if ( !strcmp( pEnt->GetClassName(), "infodecal" ) )
  228. m_nDecalCount++;
  229. return true; // return true to continue enumerating
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: Enumeration function, check a decal's texture to determine if
  233. // conversion is necessary.
  234. // Input : pEnt - map decal to be checked.
  235. // Output : Always return true to continue enumerating.
  236. //-----------------------------------------------------------------------------
  237. bool CTextureConverter::CheckDecalTextures( CMapEntity * pEnt, DWORD )
  238. {
  239. if ( strcmp( pEnt->GetClassName(), "infodecal" ) )
  240. return true; // not a decal, return true to continue enumerating
  241. m_nCurrentDecal++;
  242. m_pProgDlg->SetPos( m_nCurrentDecal );
  243. if ( strchr( pEnt->GetKeyValue( "texture" ), '/') != NULL )
  244. {
  245. m_nSkipped++;
  246. }
  247. else
  248. {
  249. ConvertDecalTexture( pEnt );
  250. }
  251. return true; // return true to continue enumerating
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Determine if any materials match the old texture of a decal and replace
  255. // appropriately.
  256. // Input : pEnt - a map decal known to need conversion.
  257. // Output :
  258. //-----------------------------------------------------------------------------
  259. void CTextureConverter::ConvertDecalTexture( CMapEntity * pEnt )
  260. {
  261. EditorTextureList_t tlMatches;
  262. IEditorTexture *pNewTexture;
  263. GetNewTextureMatches( pEnt->GetKeyValue( "texture" ), tlMatches );
  264. switch( tlMatches.Count() )
  265. {
  266. case 0:
  267. MsgConvertDecal( pEnt, "ERROR: No matching material. Cannot convert." );
  268. m_nErrors++;
  269. break;
  270. case 1:
  271. pNewTexture = tlMatches.Element(0);
  272. ReplaceDecalTexture( pEnt, pNewTexture );
  273. break;
  274. default:
  275. // Multiple matches. For now, just use the first.
  276. pNewTexture = tlMatches.Element(0);
  277. MsgConvertDecal( pEnt, "WARNING: Multiple matches found. Using first match (%s).", pNewTexture->GetName() );
  278. m_nWarnings++;
  279. ReplaceDecalTexture( pEnt, pNewTexture );
  280. break;
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: Look for material matches for an old texture and add them to a list.
  285. // Input : pszOldName - old texture name.
  286. // pMatchList - empty texture list.
  287. // Output : pMatchList - texture list is filled in with matching material textures.
  288. //-----------------------------------------------------------------------------
  289. void CTextureConverter::GetNewTextureMatches( const char * pszOldName, EditorTextureList_t &tlMatchList )
  290. {
  291. IEditorTexture * pTexture;
  292. int nIndex;
  293. nIndex = 0;
  294. pTexture = g_Textures.EnumActiveTextures( &nIndex, tfVMT );
  295. // loop through all VMT textures
  296. while ( pTexture != NULL )
  297. {
  298. if ( TextureNameMatchesMaterialName( pszOldName, pTexture->GetName() ) ) // check for a match
  299. {
  300. tlMatchList.AddToTail( pTexture );
  301. }
  302. pTexture = g_Textures.EnumActiveTextures( &nIndex, tfVMT );
  303. }
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Purpose: Compare an old texture name to a new material name.
  307. // Input : pszTextureName - Old texture name.
  308. // pszMaterialName - New material name.
  309. // Output : Return true if the old texture name is the same (case insensitive)
  310. // as the last token (delimiter '/') of the new material name, otherwise
  311. // return false.
  312. //-----------------------------------------------------------------------------
  313. bool CTextureConverter::TextureNameMatchesMaterialName( const char * pszTextureName, const char * pszMaterialName )
  314. {
  315. const char * pszPartialMaterialName; // sublocation of the material name
  316. pszPartialMaterialName = strrchr( pszMaterialName, '/' ); // Find the last '/'
  317. if ( pszPartialMaterialName != NULL)
  318. {
  319. pszPartialMaterialName++; // Point to the character after the '/'
  320. }
  321. else
  322. {
  323. pszPartialMaterialName = pszMaterialName; // No slashes found, just point to the name
  324. }
  325. // No '/' found in the VMT name, or the name ended in a '/'. This shouldn't happen.
  326. if ( ( pszPartialMaterialName == NULL ) || strlen( pszPartialMaterialName ) == 0 )
  327. return false;
  328. if ( stricmp( pszTextureName, pszPartialMaterialName ) == 0 )
  329. return true;
  330. return false;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: Change the texture on a face, re-scaling if possible.
  334. // Input : pFace - a map face
  335. // pNewTexture - texture to place on the map face.
  336. // Output : pFace has a new texture pointer set.
  337. //-----------------------------------------------------------------------------
  338. void CTextureConverter::ReplaceFaceTexture( CMapFace * pFace, IEditorTexture * pNewTexture )
  339. {
  340. if ( !pNewTexture->Load() ) // make sure new texture is loaded
  341. {
  342. MsgConvertFace( pFace, "WARNING: Couldn't load new material. Texture converted but not re-scaled." );
  343. m_nWarnings++;
  344. }
  345. RescaleFaceTexture( pFace, pNewTexture );
  346. pFace->SetTexture( pNewTexture );
  347. m_nSuccesses++;
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose: Change the texture on a decal
  351. // Input : pEntity - a map decal
  352. // pNewTexture - texture to place on the map face.
  353. // Output : pEnt has a new texture set
  354. //-----------------------------------------------------------------------------
  355. void CTextureConverter::ReplaceDecalTexture( CMapEntity * pEnt, IEditorTexture * pNewTexture )
  356. {
  357. pEnt->SetKeyValue( "texture", pNewTexture->GetName() );
  358. m_nSuccesses++;
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Purpose: Find a WAD3 texture based on a name search.
  362. // Input : pszName - name of the texture to search for.
  363. // Output : return a texture if found, otherwise NULL.
  364. //-----------------------------------------------------------------------------
  365. IEditorTexture * CTextureConverter::FindWAD3Texture( const char * pszName )
  366. {
  367. IEditorTexture * pTexture;
  368. int nIndex;
  369. nIndex = 0;
  370. pTexture = g_Textures.EnumActiveTextures( &nIndex, tfWAD3 );
  371. // loop through all the WAD3 textures
  372. while ( pTexture != NULL )
  373. {
  374. if ( !strcmp( pTexture->GetName(), pszName ) ) //check for exact match
  375. return pTexture;
  376. pTexture = g_Textures.EnumActiveTextures( &nIndex, tfWAD3 );
  377. }
  378. return NULL;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose: Change the scale and shift values of a texture, based on old texture
  382. // image dimensions compared to new texture image dimensions.
  383. // Input : pFace - map face that the texture to be scaled is on.
  384. // pOldTexture - the old texture.
  385. // pNewTexture - the new texture.
  386. // Output : pFace->scale and pface->texture are modified if either the height or
  387. // width (or both) of the texture has changed.
  388. //-----------------------------------------------------------------------------
  389. void CTextureConverter::RescaleFaceTexture( CMapFace * pFace, IEditorTexture * pNewTexture )
  390. {
  391. int nNewWidth;
  392. int nNewHeight;
  393. int nOldWidth = -1;
  394. int nOldHeight = -1;
  395. // First look for the .resizeinfo in the mod dir (hl2\dod), then the game dir (hl2\hl2).
  396. char resizeInfoFilename[512];
  397. Q_snprintf( resizeInfoFilename, sizeof( resizeInfoFilename ), "materials\\%s.resizeinfo", pNewTexture->GetName() );
  398. FileHandle_t fp = g_pFileSystem->Open( resizeInfoFilename, "rt" );
  399. if ( !fp )
  400. {
  401. return;
  402. }
  403. char line[512];
  404. int nScanned = 0;
  405. if ( g_pFullFileSystem->ReadLine( line, sizeof( line ), fp ) )
  406. {
  407. nScanned = sscanf( line, "%d %d", &nOldWidth, &nOldHeight );
  408. }
  409. g_pFileSystem->Close( fp );
  410. if ( nScanned != 2 || nOldWidth < 0 || nOldHeight < 0 || nOldWidth > 5000 || nOldHeight > 5000 )
  411. return;
  412. nNewWidth = pNewTexture->GetWidth();
  413. nNewHeight = pNewTexture->GetHeight();
  414. // Divide by 0 checks
  415. if ( ( nOldWidth == 0 ) || ( nOldHeight == 0 ) )
  416. {
  417. MsgConvertFace( pFace,
  418. "WARNING: Invalid old texture dimensions (%dx%d). Texture converted but not re-scaled.",
  419. nOldWidth,
  420. nOldHeight
  421. );
  422. m_nWarnings++;
  423. return;
  424. }
  425. // Divide by 0 checks
  426. if ( ( nNewWidth == 0 ) || ( nNewHeight == 0 ) )
  427. {
  428. MsgConvertFace( pFace,
  429. "WARNING: Invalid new material dimensions (%dx%d). Texture converted but not re-scaled.",
  430. nNewWidth,
  431. nNewHeight
  432. );
  433. m_nWarnings++;
  434. return;
  435. }
  436. if ( nOldWidth != nNewWidth )
  437. {
  438. // Adjust the width scale by an old to new ratio
  439. pFace->texture.scale[ 0 ] = pFace->texture.scale[ 0 ] * nOldWidth / nNewWidth;
  440. // Adjust the height shift by a new to old ratio
  441. pFace->texture.UAxis[ 3 ] = pFace->texture.UAxis[ 3 ] * nNewWidth / nOldWidth;
  442. }
  443. if ( nOldHeight != nNewHeight )
  444. {
  445. // Adjust the height scale by an old to new ratio
  446. pFace->texture.scale[ 1 ] = pFace->texture.scale[ 1 ] * nOldHeight / nNewHeight;
  447. // Adjust the height shift by a new to old ratio
  448. pFace->texture.VAxis[ 3 ] = pFace->texture.VAxis[ 3 ] * nNewHeight / nOldHeight;
  449. }
  450. pFace->CalcTextureCoords(); // recompute internals base on the new scaling and shifting.
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose: Send a message to WC's message window about the specified face.
  454. // Input : pFace - The map face the message relates to
  455. // format - The message format string, *printf style
  456. // ... - The remaining arguments of the *printf style message
  457. // Output : A status message is sent to the WC message window.
  458. //-----------------------------------------------------------------------------
  459. void CTextureConverter::MsgConvertFace( CMapFace * pFace, const char * format, ... )
  460. {
  461. va_list ptr;
  462. char message[ 1024 ];
  463. Vector vecFaceCenter;
  464. pFace->GetCenter( vecFaceCenter );
  465. va_start( ptr, format );
  466. _vsnprintf( message, 1024, format, ptr );
  467. va_end( ptr );
  468. Msg( mwStatus,
  469. "[face] %s at (%d,%d,%d): %s",
  470. pFace->GetTexture()->GetName(),
  471. (int)vecFaceCenter[ 0 ],
  472. (int)vecFaceCenter[ 1 ],
  473. (int)vecFaceCenter[ 2 ],
  474. message
  475. );
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Send a message to WC's message window about the specified decal.
  479. // Input : pEnt - The map decal the message relates to
  480. // format - The message format string, *printf style
  481. // ... - The remaining arguments of the *printf style message
  482. // Output : A status message is sent to the WC message window.
  483. //-----------------------------------------------------------------------------
  484. void CTextureConverter::MsgConvertDecal( CMapEntity * pEnt, const char * format, ... )
  485. {
  486. va_list ptr;
  487. char message[ 1024 ];
  488. Vector vecOrigin;
  489. pEnt->GetOrigin( vecOrigin );
  490. va_start( ptr, format );
  491. _vsnprintf( message, 1024, format, ptr );
  492. va_end( ptr );
  493. Msg( mwStatus,
  494. "[decal] %s at (%d,%d,%d): %s",
  495. pEnt->GetKeyValue("texture"),
  496. (int) vecOrigin.x,
  497. (int) vecOrigin.y,
  498. (int) vecOrigin.z,
  499. message
  500. );
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Display information about the full conversion process.
  504. // Input :
  505. // Output : Values of the counters are logged.
  506. //-----------------------------------------------------------------------------
  507. void CTextureConverter::DisplayStatistics( void )
  508. {
  509. Msg( mwStatus, "==================" );
  510. Msg( mwStatus, "Conversion summary:" );
  511. Msg( mwStatus, "==================" );
  512. Msg( mwStatus, "Total solids: %10d", m_nSolidCount );
  513. Msg( mwStatus, "Total faces: %10d", m_nFaceCount );
  514. Msg( mwStatus, "Total decals: %10d", m_nDecalCount );
  515. Msg( mwStatus, "Total conversions: %10d", m_nFaceCount + m_nDecalCount );
  516. Msg( mwStatus, "Successful conversions: %10d", m_nSuccesses );
  517. Msg( mwStatus, "Skipped conversions %10d", m_nSkipped );
  518. Msg( mwStatus, "Conversion errors: %10d", m_nErrors );
  519. Msg( mwStatus, "Conversion warnings: %10d", m_nWarnings );
  520. }