summaryrefslogtreecommitdiff
path: root/hammer/textureconverter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hammer/textureconverter.cpp')
-rw-r--r--hammer/textureconverter.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/hammer/textureconverter.cpp b/hammer/textureconverter.cpp
new file mode 100644
index 0000000..205393e
--- /dev/null
+++ b/hammer/textureconverter.cpp
@@ -0,0 +1,639 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdafx.h>
+#include "MapWorld.h"
+#include "MessageWnd.h"
+#include "IEditorTexture.h"
+#include "GlobalFunctions.h"
+#include "TextureSystem.h"
+#include "TextureConverter.h"
+#include "filesystem.h"
+#include "Hammer.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+CProgressDlg * CTextureConverter::m_pProgDlg;
+int CTextureConverter::m_nSolidCount;
+int CTextureConverter::m_nFaceCount;
+int CTextureConverter::m_nDecalCount;
+int CTextureConverter::m_nCurrentSolid;
+int CTextureConverter::m_nCurrentDecal;
+int CTextureConverter::m_nSuccesses;
+int CTextureConverter::m_nErrors;
+int CTextureConverter::m_nSkipped;
+int CTextureConverter::m_nWarnings;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset counters.
+// Input :
+// Output : Counters all reset to 0.
+//-----------------------------------------------------------------------------
+void CTextureConverter::Initialize( void )
+{
+ m_nSolidCount = 0;
+ m_nCurrentSolid = 0;
+ m_nFaceCount = 0;
+ m_nDecalCount = 0;
+ m_nCurrentDecal = 0;
+
+ m_nSuccesses = 0;
+ m_nErrors = 0;
+ m_nSkipped = 0;
+ m_nWarnings = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recurse through the contents of the map, passing solid objects on
+// to be converted.
+// Input : pWorld - pointer to the map to have textures converted.
+// Output : All solid faces and decals in the world have WAD3 textures
+// converted to VMT.
+//-----------------------------------------------------------------------------
+void CTextureConverter::ConvertWorldTextures( CMapWorld * pWorld )
+{
+ Initialize();
+
+ // Bring the message window to the front, to display conversion info
+ g_pwndMessage->Activate();
+
+ Msg( mwStatus, "Converting textures from WAD to VMT format..." );
+
+ // Set up a progress meter dialogue
+ m_pProgDlg = new CProgressDlg;
+ m_pProgDlg->Create();
+ m_pProgDlg->SetStep( 1 );
+ m_pProgDlg->SetWindowText( "Preparing to convert textures..." );
+
+ // Run the converter
+ ConvertSolids( pWorld );
+ ConvertDecals( pWorld );
+ DisplayStatistics();
+
+ // Destroy the progress meter
+ if ( m_pProgDlg )
+ {
+ m_pProgDlg->DestroyWindow();
+ delete m_pProgDlg;
+ m_pProgDlg = NULL;
+ }
+
+ AfxMessageBox( "Conversion complete. Check the Hammer \"Messages\" window for complete details." );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recurse through the contents of the map, passing solid objects on
+// to be converted.
+// Input : pWorld - pointer to the map to have textures converted.
+// Output : All solid faces in the world have WAD3 textures converted to VMT.
+//-----------------------------------------------------------------------------
+void CTextureConverter::ConvertSolids( CMapWorld * pWorld )
+{
+ // Count total map solids so we know how many we have to do (for progress meter).
+ pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CountMapSolids ), 0, MAPCLASS_TYPE( CMapSolid ) );
+
+ m_pProgDlg->SetRange( 0, m_nSolidCount );
+ m_pProgDlg->SetStep( 2 );
+ m_pProgDlg->SetWindowText( "Converting solids..." );
+
+ // Cycle through the solids again and convert as necessary.
+ pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CheckSolidTextures ), 0, MAPCLASS_TYPE( CMapSolid ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Enumeration function, increment the solids counter.
+// Input :
+// Output : Always return true to continue enumerating.
+//-----------------------------------------------------------------------------
+bool CTextureConverter::CountMapSolids( CMapSolid *, DWORD )
+{
+ m_nSolidCount++;
+
+ return true; // return true to continue enumerating
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Enumeration function, check all the faces of a solid for texture conversion
+// Input : pSolid - map solid to be checked.
+// Output : Always return true to continue enumerating.
+//-----------------------------------------------------------------------------
+bool CTextureConverter::CheckSolidTextures( CMapSolid * pSolid, DWORD )
+{
+ int nFaceCount;
+
+ m_nCurrentSolid++;
+
+ if ( m_nCurrentSolid % 100 == 0 )
+ m_pProgDlg->SetPos( m_nCurrentSolid );
+
+ // check each face of the solid
+ nFaceCount = pSolid->GetFaceCount();
+ while( nFaceCount-- )
+ {
+ CheckFaceTexture( pSolid->GetFace( nFaceCount ) );
+ }
+
+ return true; // return true to continue enumerating
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the texture of a face to determine if conversion is necessary.
+// Input : pFace - a map face.
+// Output :
+//-----------------------------------------------------------------------------
+void CTextureConverter::CheckFaceTexture( CMapFace * pFace )
+{
+ m_nFaceCount++;
+
+ // Criteria for needing conversion is a) being a dummy texture AND b) having no slashes
+ // in the texture name.
+
+ if ( !pFace->GetTexture()->IsDummy() )
+ {
+ m_nSkipped++;
+ return;
+ }
+
+ if ( strchr( pFace->GetTexture()->GetName(), '/') != NULL )
+ {
+ m_nSkipped++;
+ return;
+ }
+
+ ConvertFaceTexture( pFace );
+}
+
+
+bool TextureEndsIn( const char *pTextureName, const char *pEnd )
+{
+ const char *pLast = strrchr( pTextureName, '\\' );
+ if ( strrchr( pTextureName, '/' ) > pLast )
+ pLast = strrchr( pTextureName, '/' );
+
+ if ( pLast )
+ return stricmp( pLast+1, pEnd ) == 0;
+ else
+ return stricmp( pTextureName, pEnd ) == 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determine if any materials match the old texture of a face and replace
+// appropriately.
+// Input : pFace - a map face known to need conversion.
+// Output :
+//-----------------------------------------------------------------------------
+void CTextureConverter::ConvertFaceTexture( CMapFace * pFace )
+{
+ EditorTextureList_t tlMatches;
+ IEditorTexture *pNewTexture;
+
+ const char *pTextureName = pFace->GetTexture()->GetName();
+
+ // Check for SKY and SKIP brushes.
+ char *replacements[][2] =
+ {
+ { "sky", "tools/toolsskybox" },
+ { "skip", "tools/toolsskip" },
+ { "aaatrigger", "tools/toolstrigger" },
+ { "hint", "tools/toolshint" },
+ { "clip", "tools/toolsclip" },
+ { "null", "tools/toolsnodraw" }
+ };
+ for ( int i=0; i < sizeof( replacements ) / sizeof( replacements[0] ); i++ )
+ {
+ if ( TextureEndsIn( pTextureName, replacements[i][0] ) )
+ {
+ pNewTexture = g_Textures.FindActiveTexture( replacements[i][1] );
+ if ( pNewTexture )
+ {
+ ReplaceFaceTexture( pFace, pNewTexture );
+ return;
+ }
+ }
+ }
+
+ GetNewTextureMatches( pTextureName, tlMatches );
+
+ switch( tlMatches.Count() )
+ {
+ case 0:
+ MsgConvertFace( pFace, "ERROR: No matching material. Cannot convert." );
+ m_nErrors++;
+
+ break;
+ case 1:
+ pNewTexture = tlMatches.Element(0);
+ ReplaceFaceTexture( pFace, pNewTexture );
+
+ break;
+ default:
+ // Multiple matches. For now, just use the first.
+ pNewTexture = tlMatches.Element(0);
+ ReplaceFaceTexture( pFace, pNewTexture );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recurse through the contents of the map, passing decal objects on
+// to be converted.
+// Input : pWorld - pointer to the map to have textures converted.
+// Output : All decals in the world have WAD3 textures converted to VMT.
+//-----------------------------------------------------------------------------
+void CTextureConverter::ConvertDecals( CMapWorld * pWorld )
+{
+ // Count total map decals so we know how many we have to do (for progress meter).
+ pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CountMapDecals ), 0, MAPCLASS_TYPE( CMapEntity ) );
+
+ m_pProgDlg->SetRange( 0, m_nDecalCount );
+ m_pProgDlg->SetStep( 3 );
+ m_pProgDlg->SetWindowText( "Converting decals..." );
+
+ // Cycle through the solids again and convert as necessary.
+ pWorld->EnumChildren( ENUMMAPCHILDRENPROC( CheckDecalTextures ), 0, MAPCLASS_TYPE( CMapEntity ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Enumeration function, increment the decals counter if entity is a decal.
+// Input :
+// Output : Always return true to continue enumerating.
+//-----------------------------------------------------------------------------
+bool CTextureConverter::CountMapDecals( CMapEntity * pEnt, DWORD )
+{
+ if ( !strcmp( pEnt->GetClassName(), "infodecal" ) )
+ m_nDecalCount++;
+
+ return true; // return true to continue enumerating
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Enumeration function, check a decal's texture to determine if
+// conversion is necessary.
+// Input : pEnt - map decal to be checked.
+// Output : Always return true to continue enumerating.
+//-----------------------------------------------------------------------------
+bool CTextureConverter::CheckDecalTextures( CMapEntity * pEnt, DWORD )
+{
+ if ( strcmp( pEnt->GetClassName(), "infodecal" ) )
+ return true; // not a decal, return true to continue enumerating
+
+ m_nCurrentDecal++;
+
+ m_pProgDlg->SetPos( m_nCurrentDecal );
+
+ if ( strchr( pEnt->GetKeyValue( "texture" ), '/') != NULL )
+ {
+ m_nSkipped++;
+ }
+ else
+ {
+ ConvertDecalTexture( pEnt );
+ }
+
+ return true; // return true to continue enumerating
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determine if any materials match the old texture of a decal and replace
+// appropriately.
+// Input : pEnt - a map decal known to need conversion.
+// Output :
+//-----------------------------------------------------------------------------
+void CTextureConverter::ConvertDecalTexture( CMapEntity * pEnt )
+{
+ EditorTextureList_t tlMatches;
+ IEditorTexture *pNewTexture;
+
+ GetNewTextureMatches( pEnt->GetKeyValue( "texture" ), tlMatches );
+
+ switch( tlMatches.Count() )
+ {
+ case 0:
+ MsgConvertDecal( pEnt, "ERROR: No matching material. Cannot convert." );
+ m_nErrors++;
+
+ break;
+ case 1:
+ pNewTexture = tlMatches.Element(0);
+ ReplaceDecalTexture( pEnt, pNewTexture );
+
+ break;
+ default:
+ // Multiple matches. For now, just use the first.
+ pNewTexture = tlMatches.Element(0);
+
+ MsgConvertDecal( pEnt, "WARNING: Multiple matches found. Using first match (%s).", pNewTexture->GetName() );
+ m_nWarnings++;
+
+ ReplaceDecalTexture( pEnt, pNewTexture );
+
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Look for material matches for an old texture and add them to a list.
+// Input : pszOldName - old texture name.
+// pMatchList - empty texture list.
+// Output : pMatchList - texture list is filled in with matching material textures.
+//-----------------------------------------------------------------------------
+void CTextureConverter::GetNewTextureMatches( const char * pszOldName, EditorTextureList_t &tlMatchList )
+{
+ IEditorTexture * pTexture;
+ int nIndex;
+
+ nIndex = 0;
+ pTexture = g_Textures.EnumActiveTextures( &nIndex, tfVMT );
+
+ // loop through all VMT textures
+ while ( pTexture != NULL )
+ {
+ if ( TextureNameMatchesMaterialName( pszOldName, pTexture->GetName() ) ) // check for a match
+ {
+ tlMatchList.AddToTail( pTexture );
+ }
+
+ pTexture = g_Textures.EnumActiveTextures( &nIndex, tfVMT );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Compare an old texture name to a new material name.
+// Input : pszTextureName - Old texture name.
+// pszMaterialName - New material name.
+// Output : Return true if the old texture name is the same (case insensitive)
+// as the last token (delimiter '/') of the new material name, otherwise
+// return false.
+//-----------------------------------------------------------------------------
+bool CTextureConverter::TextureNameMatchesMaterialName( const char * pszTextureName, const char * pszMaterialName )
+{
+ const char * pszPartialMaterialName; // sublocation of the material name
+
+ pszPartialMaterialName = strrchr( pszMaterialName, '/' ); // Find the last '/'
+
+ if ( pszPartialMaterialName != NULL)
+ {
+ pszPartialMaterialName++; // Point to the character after the '/'
+ }
+ else
+ {
+ pszPartialMaterialName = pszMaterialName; // No slashes found, just point to the name
+ }
+
+ // No '/' found in the VMT name, or the name ended in a '/'. This shouldn't happen.
+ if ( ( pszPartialMaterialName == NULL ) || strlen( pszPartialMaterialName ) == 0 )
+ return false;
+
+ if ( stricmp( pszTextureName, pszPartialMaterialName ) == 0 )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Change the texture on a face, re-scaling if possible.
+// Input : pFace - a map face
+// pNewTexture - texture to place on the map face.
+// Output : pFace has a new texture pointer set.
+//-----------------------------------------------------------------------------
+void CTextureConverter::ReplaceFaceTexture( CMapFace * pFace, IEditorTexture * pNewTexture )
+{
+ if ( !pNewTexture->Load() ) // make sure new texture is loaded
+ {
+ MsgConvertFace( pFace, "WARNING: Couldn't load new material. Texture converted but not re-scaled." );
+ m_nWarnings++;
+ }
+
+ RescaleFaceTexture( pFace, pNewTexture );
+
+ pFace->SetTexture( pNewTexture );
+
+ m_nSuccesses++;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Change the texture on a decal
+// Input : pEntity - a map decal
+// pNewTexture - texture to place on the map face.
+// Output : pEnt has a new texture set
+//-----------------------------------------------------------------------------
+void CTextureConverter::ReplaceDecalTexture( CMapEntity * pEnt, IEditorTexture * pNewTexture )
+{
+ pEnt->SetKeyValue( "texture", pNewTexture->GetName() );
+ m_nSuccesses++;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Find a WAD3 texture based on a name search.
+// Input : pszName - name of the texture to search for.
+// Output : return a texture if found, otherwise NULL.
+//-----------------------------------------------------------------------------
+IEditorTexture * CTextureConverter::FindWAD3Texture( const char * pszName )
+{
+ IEditorTexture * pTexture;
+ int nIndex;
+
+ nIndex = 0;
+ pTexture = g_Textures.EnumActiveTextures( &nIndex, tfWAD3 );
+
+ // loop through all the WAD3 textures
+ while ( pTexture != NULL )
+ {
+ if ( !strcmp( pTexture->GetName(), pszName ) ) //check for exact match
+ return pTexture;
+
+ pTexture = g_Textures.EnumActiveTextures( &nIndex, tfWAD3 );
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Change the scale and shift values of a texture, based on old texture
+// image dimensions compared to new texture image dimensions.
+// Input : pFace - map face that the texture to be scaled is on.
+// pOldTexture - the old texture.
+// pNewTexture - the new texture.
+// Output : pFace->scale and pface->texture are modified if either the height or
+// width (or both) of the texture has changed.
+//-----------------------------------------------------------------------------
+void CTextureConverter::RescaleFaceTexture( CMapFace * pFace, IEditorTexture * pNewTexture )
+{
+ int nNewWidth;
+ int nNewHeight;
+
+ int nOldWidth = -1;
+ int nOldHeight = -1;
+
+ // First look for the .resizeinfo in the mod dir (hl2\dod), then the game dir (hl2\hl2).
+ char resizeInfoFilename[512];
+ Q_snprintf( resizeInfoFilename, sizeof( resizeInfoFilename ), "materials\\%s.resizeinfo", pNewTexture->GetName() );
+ FileHandle_t fp = g_pFileSystem->Open( resizeInfoFilename, "rt" );
+ if ( !fp )
+ {
+ return;
+ }
+
+ char line[512];
+ int nScanned = 0;
+ if ( g_pFullFileSystem->ReadLine( line, sizeof( line ), fp ) )
+ {
+ nScanned = sscanf( line, "%d %d", &nOldWidth, &nOldHeight );
+ }
+ g_pFileSystem->Close( fp );
+ if ( nScanned != 2 || nOldWidth < 0 || nOldHeight < 0 || nOldWidth > 5000 || nOldHeight > 5000 )
+ return;
+
+ nNewWidth = pNewTexture->GetWidth();
+ nNewHeight = pNewTexture->GetHeight();
+
+ // Divide by 0 checks
+ if ( ( nOldWidth == 0 ) || ( nOldHeight == 0 ) )
+ {
+ MsgConvertFace( pFace,
+ "WARNING: Invalid old texture dimensions (%dx%d). Texture converted but not re-scaled.",
+ nOldWidth,
+ nOldHeight
+ );
+ m_nWarnings++;
+ return;
+ }
+
+ // Divide by 0 checks
+ if ( ( nNewWidth == 0 ) || ( nNewHeight == 0 ) )
+ {
+ MsgConvertFace( pFace,
+ "WARNING: Invalid new material dimensions (%dx%d). Texture converted but not re-scaled.",
+ nNewWidth,
+ nNewHeight
+ );
+ m_nWarnings++;
+ return;
+ }
+
+
+
+ if ( nOldWidth != nNewWidth )
+ {
+ // Adjust the width scale by an old to new ratio
+ pFace->texture.scale[ 0 ] = pFace->texture.scale[ 0 ] * nOldWidth / nNewWidth;
+
+ // Adjust the height shift by a new to old ratio
+ pFace->texture.UAxis[ 3 ] = pFace->texture.UAxis[ 3 ] * nNewWidth / nOldWidth;
+ }
+
+ if ( nOldHeight != nNewHeight )
+ {
+ // Adjust the height scale by an old to new ratio
+ pFace->texture.scale[ 1 ] = pFace->texture.scale[ 1 ] * nOldHeight / nNewHeight;
+
+ // Adjust the height shift by a new to old ratio
+ pFace->texture.VAxis[ 3 ] = pFace->texture.VAxis[ 3 ] * nNewHeight / nOldHeight;
+ }
+
+ pFace->CalcTextureCoords(); // recompute internals base on the new scaling and shifting.
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message to WC's message window about the specified face.
+// Input : pFace - The map face the message relates to
+// format - The message format string, *printf style
+// ... - The remaining arguments of the *printf style message
+// Output : A status message is sent to the WC message window.
+//-----------------------------------------------------------------------------
+void CTextureConverter::MsgConvertFace( CMapFace * pFace, const char * format, ... )
+{
+ va_list ptr;
+ char message[ 1024 ];
+ Vector vecFaceCenter;
+
+ pFace->GetCenter( vecFaceCenter );
+
+ va_start( ptr, format );
+ _vsnprintf( message, 1024, format, ptr );
+ va_end( ptr );
+
+ Msg( mwStatus,
+ "[face] %s at (%d,%d,%d): %s",
+ pFace->GetTexture()->GetName(),
+ (int)vecFaceCenter[ 0 ],
+ (int)vecFaceCenter[ 1 ],
+ (int)vecFaceCenter[ 2 ],
+ message
+ );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message to WC's message window about the specified decal.
+// Input : pEnt - The map decal the message relates to
+// format - The message format string, *printf style
+// ... - The remaining arguments of the *printf style message
+// Output : A status message is sent to the WC message window.
+//-----------------------------------------------------------------------------
+void CTextureConverter::MsgConvertDecal( CMapEntity * pEnt, const char * format, ... )
+{
+ va_list ptr;
+ char message[ 1024 ];
+ Vector vecOrigin;
+
+ pEnt->GetOrigin( vecOrigin );
+
+ va_start( ptr, format );
+ _vsnprintf( message, 1024, format, ptr );
+ va_end( ptr );
+
+ Msg( mwStatus,
+ "[decal] %s at (%d,%d,%d): %s",
+ pEnt->GetKeyValue("texture"),
+ (int) vecOrigin.x,
+ (int) vecOrigin.y,
+ (int) vecOrigin.z,
+ message
+ );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Display information about the full conversion process.
+// Input :
+// Output : Values of the counters are logged.
+//-----------------------------------------------------------------------------
+void CTextureConverter::DisplayStatistics( void )
+{
+ Msg( mwStatus, "==================" );
+ Msg( mwStatus, "Conversion summary:" );
+ Msg( mwStatus, "==================" );
+ Msg( mwStatus, "Total solids: %10d", m_nSolidCount );
+ Msg( mwStatus, "Total faces: %10d", m_nFaceCount );
+ Msg( mwStatus, "Total decals: %10d", m_nDecalCount );
+ Msg( mwStatus, "Total conversions: %10d", m_nFaceCount + m_nDecalCount );
+ Msg( mwStatus, "Successful conversions: %10d", m_nSuccesses );
+ Msg( mwStatus, "Skipped conversions %10d", m_nSkipped );
+ Msg( mwStatus, "Conversion errors: %10d", m_nErrors );
+ Msg( mwStatus, "Conversion warnings: %10d", m_nWarnings );
+}