csgo-2018-source/datacache/vtfcombine.cpp
2021-07-24 21:11:47 -07:00

1213 lines
40 KiB
C++

#include "datacache/imdlcache.h"
#include "vtfcombine.h"
#include "strtools.h"
#include "keyvalues.h"
#include "filesystem.h"
#include "vtf/vtf.h"
#include "tier0/cache_hints.h"
#include "materialsystem/imaterialsystem.h"
//#define DEBUG_VMT_COMBINE 1
#ifdef DEBUG_VMT_COMBINE
#define DebugCombineMsg( ... ) Msg( __VA_ARGS__ )
#else
#define DebugCombineMsg( ... )
#endif
CTextureCombine& GetTextureCombiner()
{
// Allocate on demand to avoid consuming 50 MB of memory and address space
// everywhere when this object isn't even used.
// This is a memory leak but that is still better than having the memory
// allocated regardless of whether it is used.
static CTextureCombine* s_TextureCombiner = new CTextureCombine;
return *s_TextureCombiner;
}
class CSimpleTexturePacker
{
public:
CSimpleTexturePacker( );
void Init( int nWidth, int nHeight );
void AddTexture( int nID, int nWidth, int nHeight );
void Resolve( );
void GetTextureLocation( int nID, int &x, int &y );
void GetTextureSize( int nID, int &x, int &y );
private:
static const int m_nMaxSubdivisions = 32;
static const int m_nMaxTextures = 32;
typedef struct SSubDivision
{
int x, y, width, height;
} TSubDivisions;
TSubDivisions m_SubDivisions[ m_nMaxSubdivisions ];
int m_nNumSubDivisions;
typedef struct STextureInfo
{
int m_nID;
TSubDivisions m_Location;
} TTextureInfo;
TTextureInfo m_Textures[ m_nMaxTextures ];
TTextureInfo *m_TextureOrder[ m_nMaxTextures ];
int m_nNumTextures;
int m_nWidth;
int m_nHeight;
static int TextureSizeCompare( const void *elem1, const void *elem2 );
void Reset( );
bool FindOpenSpace( TTextureInfo *pTexture );
bool ResolveBrute( );
bool IterateTextures( );
bool BruteIterate( );
};
CSimpleTexturePacker::CSimpleTexturePacker( )
{
m_nNumSubDivisions = 0;
m_nNumTextures = 0;
}
void CSimpleTexturePacker::Init( int nWidth, int nHeight )
{
m_nWidth = nWidth;
m_nHeight = nHeight;
m_nNumTextures = 0;
}
void CSimpleTexturePacker::Reset( )
{
m_SubDivisions[ 0 ].x = 0;
m_SubDivisions[ 0 ].y = 0;
m_SubDivisions[ 0 ].width = m_nWidth;
m_SubDivisions[ 0 ].height = m_nHeight;
m_nNumSubDivisions = 1;
}
void CSimpleTexturePacker::AddTexture( int nID, int nWidth, int nHeight )
{
if ( m_nNumTextures >= m_nMaxTextures )
{
Assert( 0 );
V_sprintf_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorMessage, "too many textures added to packer" );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
m_Textures[ m_nNumTextures ].m_nID = nID;
m_Textures[ m_nNumTextures ].m_Location.width = nWidth;
m_Textures[ m_nNumTextures ].m_Location.height = nHeight;
m_TextureOrder[ m_nNumTextures ] = &m_Textures[ m_nNumTextures ];
m_nNumTextures++;
}
int CSimpleTexturePacker::TextureSizeCompare( const void *elem1, const void *elem2 )
{
TTextureInfo *pTexture1 = ( TTextureInfo * )elem1;
TTextureInfo *pTexture2 = ( TTextureInfo * )elem2;
if ( pTexture1->m_Location.height > pTexture2->m_Location.height )
{
return -1;
}
else if ( pTexture1->m_Location.height < pTexture2->m_Location.height )
{
return 1;
}
else if ( pTexture1->m_Location.width > pTexture2->m_Location.width )
{
return -1;
}
else if ( pTexture1->m_Location.width < pTexture2->m_Location.width )
{
return 1;
}
#if 0
int index1 = *(byte *)elem1;
int index2 = *(byte *)elem2;
if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.height > SimpleTexturePacker.m_Textures[ index2 ].m_Location.height )
{
return -1;
}
else if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.height < SimpleTexturePacker.m_Textures[ index2 ].m_Location.height )
{
return 1;
}
else if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.width > SimpleTexturePacker.m_Textures[ index2 ].m_Location.width )
{
return -1;
}
else if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.width < SimpleTexturePacker.m_Textures[ index2 ].m_Location.width )
{
return 1;
}
#endif
return 0;
}
bool CSimpleTexturePacker::FindOpenSpace( TTextureInfo *pTexture )
{
int nSubDivision;
const int nWidth = pTexture->m_Location.width;
const int nHeight = pTexture->m_Location.height;
for( nSubDivision = 0; nSubDivision < m_nNumSubDivisions; nSubDivision++ )
{
if ( m_SubDivisions[ nSubDivision ].width >= nWidth && m_SubDivisions[ nSubDivision ].height >= nHeight )
{
break;
}
}
if ( nSubDivision >= m_nNumSubDivisions )
{
return false;
}
pTexture->m_Location.x = m_SubDivisions[ nSubDivision ].x;
pTexture->m_Location.y = m_SubDivisions[ nSubDivision ].y;
if ( m_SubDivisions[ nSubDivision ].width == nWidth && m_SubDivisions[ nSubDivision ].height == nHeight )
{ // completely used up
m_nNumSubDivisions--;
if ( nSubDivision < m_nNumSubDivisions )
{
memmove( &m_SubDivisions[ nSubDivision ], &m_SubDivisions[ nSubDivision + 1 ], sizeof( m_SubDivisions[ nSubDivision ] ) * ( m_nNumSubDivisions - nSubDivision ) );
}
}
else
{
if ( m_SubDivisions[ nSubDivision ].width == nWidth )
{ // only one potential piece
m_SubDivisions[ nSubDivision ].y += nHeight;
m_SubDivisions[ nSubDivision ].height -= nHeight;
}
else if ( m_SubDivisions[ nSubDivision ].height == nHeight )
{ // only one potential piece
m_SubDivisions[ nSubDivision ].x += nWidth;
m_SubDivisions[ nSubDivision ].width -= nWidth;
}
else
{
if ( m_nNumSubDivisions >= m_nMaxSubdivisions )
{
Assert( 0 );
V_sprintf_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorMessage, "too many subdivision within texture packer" );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
m_SubDivisions[ m_nNumSubDivisions ].x = m_SubDivisions[ nSubDivision ].x + nWidth;
m_SubDivisions[ m_nNumSubDivisions ].y = m_SubDivisions[ nSubDivision ].y;
m_SubDivisions[ m_nNumSubDivisions ].width = m_SubDivisions[ nSubDivision ].width - nWidth;
m_SubDivisions[ m_nNumSubDivisions ].height = m_SubDivisions[ nSubDivision ].height;
m_nNumSubDivisions++;
m_SubDivisions[ nSubDivision ].y += nHeight;
m_SubDivisions[ nSubDivision ].width = nWidth;
m_SubDivisions[ nSubDivision ].height -= nHeight;
}
}
return true;
}
void CSimpleTexturePacker::Resolve( )
{
Reset();
qsort( m_TextureOrder, m_nNumTextures, sizeof( m_TextureOrder[ 0 ] ), CSimpleTexturePacker::TextureSizeCompare );
GetTextureCombiner().m_pCombinedStudioData->m_Results.m_nNumTexturePackIterations++;
for( int nTexture = 0; nTexture < m_nNumTextures; nTexture++ )
{
if ( FindOpenSpace( m_TextureOrder[ nTexture ] ) == false )
{
if ( ResolveBrute() )
{
#if 0
for( int nTexture = 0; nTexture < m_nNumTextures; nTexture++ )
{
Msg( "ID %d: x=%d y=%d width=%d height=%d\n", m_TextureOrder[ nTexture ]->m_nID, m_TextureOrder[ nTexture ]->m_Location.x, m_TextureOrder[ nTexture ]->m_Location.y,
m_TextureOrder[ nTexture ]->m_Location.width, m_TextureOrder[ nTexture ]->m_Location.height );
}
#endif
return;
}
// put them back in order
qsort( m_TextureOrder, m_nNumTextures, sizeof( m_TextureOrder[ 0 ] ), CSimpleTexturePacker::TextureSizeCompare );
AssertMsg( false, "Could not find open space for texture in packer" );
V_sprintf_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not find open space for texture in packer" );
GetTextureCombiner().m_pCombinedStudioData->m_Results.m_nDetailedError = COMBINED_DETAIL_ERROR_TEXTURE_PACKER_NO_SPACE;
char szTemp[ 256 ];
for( int nTextureList = 0; nTextureList < m_nNumTextures; nTextureList++ )
{
V_sprintf_safe( szTemp, "ID %d%c: x=%d y=%d width=%d height=%d\n", m_TextureOrder[ nTextureList ]->m_nID, ( nTextureList == nTexture ? '*' : ' ' ),
m_TextureOrder[ nTextureList ]->m_Location.x, m_TextureOrder[ nTextureList ]->m_Location.y, m_TextureOrder[ nTextureList ]->m_Location.width, m_TextureOrder[ nTextureList ]->m_Location.height );
V_strcat_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorDetails, szTemp );
}
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
DebugCombineMsg( "ID %d: x=%d y=%d width=%d height=%d\n", m_TextureOrder[ nTexture ]->m_nID, m_TextureOrder[ nTexture ]->m_Location.x, m_TextureOrder[ nTexture ]->m_Location.y,
m_TextureOrder[ nTexture ]->m_Location.width, m_TextureOrder[ nTexture ]->m_Location.height );
}
DebugCombineMsg( "Remaining Space:\n" );
for( int nSubDivision = 0; nSubDivision < m_nNumSubDivisions; nSubDivision++ )
{
DebugCombineMsg( " %d: x=%d y=%d width=%d height=%d\n", nSubDivision, m_SubDivisions[ nSubDivision ].x, m_SubDivisions[ nSubDivision ].y, m_SubDivisions[ nSubDivision ].width, m_SubDivisions[ nSubDivision ].height );
}
}
bool CSimpleTexturePacker::BruteIterate( )
{
Reset();
GetTextureCombiner().m_pCombinedStudioData->m_Results.m_nNumTexturePackIterations++;
DebugCombineMsg( "Trying: " );
for( int nTexture = 0; nTexture < m_nNumTextures; nTexture++ )
{
DebugCombineMsg( "%d ", nTexture );
if ( FindOpenSpace( m_TextureOrder[ nTexture ] ) == false )
{
DebugCombineMsg( "Failed\n" );
return false;
}
}
DebugCombineMsg( "Succeeded\n" );
return true;
}
bool CSimpleTexturePacker::IterateTextures( )
{
int nSwapIndex;
TTextureInfo *pSaveSwap;
int nCounters[ m_nMaxTextures ];
memset( nCounters, 0, m_nNumTextures * sizeof( int ) );
// Boothroyd method
if ( BruteIterate() )
{
return true;
}
for ( int nIndex = 0; ; nCounters[ nIndex ]++ )
{
while ( nIndex > 1 )
{
nCounters[ --nIndex ] = 0;
}
while ( nCounters[ nIndex ] >= nIndex )
{
if ( ++nIndex >= m_nNumTextures )
{
return false;
}
}
nSwapIndex = ( nIndex & 1 ) ? nCounters[ nIndex ] : 0;
pSaveSwap = m_TextureOrder[ nSwapIndex ];
m_TextureOrder[ nSwapIndex ] = m_TextureOrder[ nIndex ];
m_TextureOrder[ nIndex ] = pSaveSwap;
if ( BruteIterate() )
{
return true;
}
}
return false;
}
bool CSimpleTexturePacker::ResolveBrute( )
{
for ( int i = 0; i < m_nMaxTextures; i++ )
{
m_TextureOrder[ i ] = &m_Textures[ i ];
}
return IterateTextures();
}
void CSimpleTexturePacker::GetTextureSize( int nID, int &x, int &y )
{
x = m_Textures[ nID ].m_Location.width;
y = m_Textures[ nID ].m_Location.height;
}
void CSimpleTexturePacker::GetTextureLocation( int nID, int &x, int &y )
{
x = m_Textures[ nID ].m_Location.x;
y = m_Textures[ nID ].m_Location.y;
}
typedef struct STextureEntry
{
const char *m_pszTextureField;
const char *m_pszFlatReplacement;
} TTextureEntry;
static TTextureEntry szCustomHeroTextures[] =
{
{ "$basetexture", NULL },
{ "$normalmap", "models\\development\\flatnormal" },
// "$diffusewarp",
{ "$maskmap1", "models\\development\\blankmasks1" },
{ "$maskmap2", "models\\development\\blankmasks2" },
{ NULL, NULL }
};
static TTextureEntry szVertexLitGenericTextures[] =
{
{ "$basetexture", NULL },
{ "$phongexponenttexture", NULL },
{ NULL, NULL }
};
typedef struct SMaterialToTexture
{
const char *m_pszMaterialName;
TTextureEntry *m_pszTextureList;
} TMaterialToTexture;
static const TMaterialToTexture MaterialToTexture[] =
{
{
"customhero",
szCustomHeroTextures
},
{
"VertexLitGeneric",
szVertexLitGenericTextures
},
{
NULL,
NULL
}
};
static const char *pszFlatTextures[] =
{
"models\\development\\flatnormal",
"models\\development\\blankmasks1",
"models\\development\\blankmasks2",
NULL
};
CTextureCombine::CTextureCombine( )
{
Init( NULL );
}
void CTextureCombine::Init( TCombinedStudioData *pCombinedStudioData )
{
m_pCombinedStudioData = pCombinedStudioData;
m_nNumMaterials = 0;
memset( m_szMaterials, 0, sizeof( m_szMaterials ) );
memset( m_nMaterialAtlasInfo, 0, sizeof( m_nMaterialAtlasInfo ) );
m_nMaxAtlasGroup = 0;
memset( m_pMaterialKVs, 0, sizeof( m_pMaterialKVs ) );
for ( int nGroup = 0; nGroup < COMBINER_MAX_ATLAS_GROUPS; nGroup++ )
{
m_AtlasGroups[ nGroup ].m_nNumMaterials = 0;
memset( m_AtlasGroups[ nGroup ].m_nMaterialIndices, 0xFF, sizeof( m_AtlasGroups[ nGroup ].m_nMaterialIndices ) ); // all set to -1
memset( m_AtlasGroups[ nGroup ].m_pVTFData, 0, sizeof( m_AtlasGroups[ nGroup ].m_pVTFData ) );
memset( m_AtlasGroups[ nGroup ].m_pVTFFileHeader, 0, sizeof( m_AtlasGroups[ nGroup ].m_pVTFFileHeader ) );
memset( m_AtlasGroups[ nGroup ].m_pResources, 0, sizeof( m_AtlasGroups[ nGroup ].m_pResources ) );
memset( m_AtlasGroups[ nGroup ].m_bIsFlat, 0, sizeof( m_AtlasGroups[ nGroup ].m_bIsFlat ) );
m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs = NULL;
memset( m_AtlasGroups[ nGroup ].m_CombinedTextureMemory, 0, sizeof( m_AtlasGroups[ nGroup ].m_CombinedTextureMemory ) );
memset( m_AtlasGroups[ nGroup ].m_nCombinedTextureSize, 0, sizeof( m_AtlasGroups[ nGroup ].m_nCombinedTextureSize ) );
memset( m_AtlasGroups[ nGroup ].m_CombinedHeaders, 0, sizeof( m_AtlasGroups[ nGroup ].m_CombinedHeaders ) );
m_AtlasGroups[ nGroup ].m_pSimpleTexturePacker = NULL;
}
if ( pCombinedStudioData )
{
g_CombinerWriter.InitWriteArea( WRITE_AREA_VTF, g_CombinerWriter.GetWritePos() );
g_CombinerWriter.SetWriteArea( WRITE_AREA_VTF );
}
}
void CTextureCombine::Cleanup( )
{
for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
{
if ( m_pMaterialKVs[ nMaterial ] )
{
m_pMaterialKVs[ nMaterial ]->deleteThis();
m_pMaterialKVs[ nMaterial ] = NULL;
}
}
for ( int nGroup = 0; nGroup <= m_nMaxAtlasGroup; nGroup++ )
{
for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
{
delete m_AtlasGroups[ nGroup ].m_pVTFData[ nMaterial ];
m_AtlasGroups[ nGroup ].m_pVTFData[ nMaterial ] = NULL;
}
}
}
void CTextureCombine::FreeCombinedMaterials( )
{
for ( int nGroup = 0; nGroup <= COMBINER_MAX_ATLAS_GROUPS; nGroup++ )
{
if ( m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs != NULL )
{
m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs->deleteThis();
m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs = NULL;
}
delete m_AtlasGroups[ nGroup ].m_pSimpleTexturePacker;
m_AtlasGroups[ nGroup ].m_pSimpleTexturePacker = NULL;
}
}
int CTextureCombine::AddMaterial( const char *pszFileName )
{
for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
{
if ( strcmpi( m_szMaterials[ nMaterial ], pszFileName ) == 0 )
{
return nMaterial;
}
}
V_strcpy_safe( m_szMaterials[ m_nNumMaterials ], pszFileName );
// intending to return m_nNumMaterials, and then increment it
return m_nNumMaterials++;
}
void CTextureCombine::AddNonAtlasedMaterial( int nMaterial )
{
m_pCombinedStudioData->m_pNonAtlasedMaterialKVs[ m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames ] = ( m_pMaterialKVs[ nMaterial ] != NULL ) ? m_pMaterialKVs[ nMaterial ]->MakeCopy() : NULL;
V_FileBase( m_szMaterials[ nMaterial ], m_pCombinedStudioData->m_szNonAtlasedMaterialBaseName[ m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames ], MAX_PATH );
m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames++;
// de-dupe paths
char szPath[ MAX_PATH ];
V_strcpy( szPath, m_szMaterials[ nMaterial ] );
V_StripFilename( szPath );
bool bFound = false;
for (int i = 0; i < m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths; i++ )
{
if ( V_strcmp( m_pCombinedStudioData->m_szNonAtlasedMaterialPaths[ i ], szPath ) == 0 )
{
bFound = true;
break;
}
}
if ( !bFound )
{
V_strcpy( m_pCombinedStudioData->m_szNonAtlasedMaterialPaths[ m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths ], szPath );
m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths++;
}
}
void CTextureCombine::Resolve( )
{
if ( m_nNumMaterials <= 0 )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "no materials specified for texture combiner" );
throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
}
for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
{
m_pMaterialKVs[ nMaterial ] = new KeyValues( "vmt" );
if ( !materials->LoadKeyValuesFromVMTFile( *(m_pMaterialKVs[ nMaterial ]), m_szMaterials[ nMaterial ], true ) )
{
AddNonAtlasedMaterial( nMaterial );
// mark as not in an atlas
m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_GROUP_INDEX ] = -1;
m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_MATERIAL_INDEX ] = m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames - 1;
m_pMaterialKVs[ nMaterial ]->deleteThis();
m_pMaterialKVs[ nMaterial ] = NULL;
}
}
GatherAtlasInfo();
// assign a copy of the KVs from the first material in each atlas group to be the combined material KVs
for ( int nAtlasGroup = 0; nAtlasGroup <= m_nMaxAtlasGroup; nAtlasGroup++ )
{
int nMaterial = m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ 0 ];
if ( m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials > 1 )
{
m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterialKVs = m_pMaterialKVs[ nMaterial ]->MakeCopy();
}
else
{
AddNonAtlasedMaterial( nMaterial );
// mark as not in an atlas
m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_GROUP_INDEX ] = -1;
m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_MATERIAL_INDEX ] = m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames - 1;
}
}
m_pCombinedStudioData->m_nNumAtlasGroups = m_nMaxAtlasGroup + 1;
FindMaterialToTexture();
Cleanup();
}
void CTextureCombine::GatherAtlasInfo( )
{
for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
{
if ( m_pMaterialKVs[ nMaterial ] != NULL )
{
// get the atlas index for each material (default it to just use index 0, one atlas for all)
int nAtlasGroup = m_pMaterialKVs[ nMaterial ]->GetInt( "$atlas_group", 0 );
// track the max used atlas group
if ( nAtlasGroup > m_nMaxAtlasGroup )
{
m_nMaxAtlasGroup = nAtlasGroup;
}
// link the "global" material index to an atlas group
m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_GROUP_INDEX ] = nAtlasGroup;
// link the "global" material index to the atlas group material index
m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_MATERIAL_INDEX ] = m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials;
// link atlas group material index to the "global" material index
m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials ] = nMaterial;
m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials++;
}
}
}
void CTextureCombine::FindMaterialToTexture( )
{
for ( int nAtlasGroup = 0; nAtlasGroup <= m_nMaxAtlasGroup; nAtlasGroup++ )
{
// use the first material of the atlas group as the "master" for KV values and image format
const char *pszShaderName = m_pMaterialKVs[ m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ 0 ] ]->GetName();
for( m_nMaterialToTexture = 0; MaterialToTexture[ m_nMaterialToTexture ].m_pszMaterialName != NULL; m_nMaterialToTexture++ )
{
if ( strcmpi( pszShaderName, MaterialToTexture[ m_nMaterialToTexture ].m_pszMaterialName ) == 0 )
{
break;
}
}
if ( MaterialToTexture[ m_nMaterialToTexture ].m_pszMaterialName == NULL )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "unsupported shader for texture combiner: %s", pszShaderName );
throw( COMBINE_RESULT_FLAG_UNSUPPORTED_SHADER );
}
for( int nTexture = 0; MaterialToTexture[ m_nMaterialToTexture ].m_pszTextureList[ nTexture ].m_pszTextureField != NULL; nTexture++ )
{
const char *pszTextureField = MaterialToTexture[ m_nMaterialToTexture ].m_pszTextureList[ nTexture ].m_pszTextureField;
const char *pszFlatReplacement = MaterialToTexture[ m_nMaterialToTexture ].m_pszTextureList[ nTexture ].m_pszFlatReplacement;
char NewFieldValue[ 128 ];
bool bUsed = CombineTexture( nAtlasGroup, nTexture, pszTextureField, pszFlatReplacement );
if ( bUsed )
{
m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedTextures[ nTexture ] = ( unsigned char * )malloc( m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ] );
memcpy( m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedTextures[ nTexture ], m_AtlasGroups[ nAtlasGroup ].m_CombinedTextureMemory[ nTexture ], m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ] );
m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSizes[ nTexture ] = m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ];
V_sprintf_safe( NewFieldValue, "!%s|%d|%d|%hu|%d!", m_pCombinedStudioData->m_szCombinedModelName, nAtlasGroup, nTexture, m_pCombinedStudioData->m_FinalHandle, CModelCombine::GetNextAssetID() );
m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterialKVs->SetString( pszTextureField, NewFieldValue );
m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterial = m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterialKVs;
}
}
}
}
#define IGNORE_TEXTURE_FLAGS ( TEXTUREFLAGS_HINT_DXT5 | TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_SKIP_INITIAL_DOWNLOAD )
bool CTextureCombine::LoadVTFs( int nAtlasGroup, const char *pszTextureField, const char *pszFlatReplacement, char szTextureNames[ COMBINER_MAX_MATERIALS ][ MAX_PATH ] )
{
for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
{
const char *pszTexture = m_pMaterialKVs[ m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ nMaterial ] ]->GetString( pszTextureField, NULL );
if ( pszTexture == NULL )
{
if ( nMaterial == 0 )
{ // if not present on the primary material, then skip this texture option
return false;
}
if ( pszFlatReplacement != NULL )
{
pszTexture = pszFlatReplacement;
}
else
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not located required texture in material %s", pszTexture );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
}
V_strcpy_safe( szTextureNames[ nMaterial ], pszTexture );
char szFinalPath[ MAX_PATH ];
V_ComposeFileName( "materials/", pszTexture, szFinalPath, sizeof( szFinalPath ) );
V_DefaultExtension( szFinalPath, ".vtf", sizeof( szFinalPath ) );
m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ] = new CUtlBuffer();
if ( g_pFullFileSystem->ReadFile( szFinalPath, "GAME", *m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ], 0 ) == false )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not read texture %s", szFinalPath );
throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
}
VTFFileBaseHeader_t *pVTFFileBaseHeader = ( VTFFileBaseHeader_t * )m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ]->PeekGet();
if ( pVTFFileBaseHeader->version[ 0 ] != VTF_MAJOR_VERSION || pVTFFileBaseHeader->version[ 1 ] != VTF_MINOR_VERSION )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture is invalid version %s", szFinalPath );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] = ( VTFFileHeader_t * )pVTFFileBaseHeader;
if ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numFrames != 1 || m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->startFrame != 0 )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture has frame information %s", szFinalPath );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
m_AtlasGroups[ nAtlasGroup ].m_pResources[ nMaterial ] = ( ResourceEntryInfo * )(m_AtlasGroups[ nAtlasGroup ]. m_pVTFFileHeader[ nMaterial ] + 1 );
if ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels > MAX_COMBINED_MIP_LEVELS )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture %s has too many mip levels: %d > %d", szFinalPath, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels, MAX_COMBINED_MIP_LEVELS );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
}
return true;
}
bool CTextureCombine::CombineTexture( int nAtlasGroup, int nTexture, const char *pszTextureField, const char *pszFlatReplacement )
{
Assert( nAtlasGroup >= 0 && nAtlasGroup <= m_nMaxAtlasGroup );
Assert( nTexture >= 0 && nTexture < COMBINER_MAX_TEXTURES_PER_MATERIAL );
double flStartLoadTime = Plat_FloatTime();
char szTextureNames[ COMBINER_MAX_MATERIALS ][ MAX_PATH ];
V_strcpy_safe( szTextureNames[ 0 ], "Unknown Texture" );
if ( !LoadVTFs( nAtlasGroup, pszTextureField, pszFlatReplacement, szTextureNames ) )
{
return false;
}
double flStartCombineTime = Plat_FloatTime();
m_pCombinedStudioData->m_Results.m_flTextureLoadDuration += ( float )( flStartCombineTime - flStartLoadTime );
byte *pMipOffset[ COMBINER_MAX_MATERIALS ][ MAX_COMBINED_MIP_LEVELS ];
int nMipWidth[ COMBINER_MAX_MATERIALS ][ MAX_COMBINED_MIP_LEVELS ];
int nMipHeight[ COMBINER_MAX_MATERIALS ][ MAX_COMBINED_MIP_LEVELS ];
int nBlockSize;
memset( pMipOffset, 0, sizeof( pMipOffset ) );
switch ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat )
{
case IMAGE_FORMAT_DXT1:
case IMAGE_FORMAT_DXT1_RUNTIME:
case IMAGE_FORMAT_LINEAR_DXT1:
case IMAGE_FORMAT_ATI1N:
nBlockSize = 8;
break;
case IMAGE_FORMAT_DXT3:
case IMAGE_FORMAT_DXT5:
case IMAGE_FORMAT_DXT5_RUNTIME:
case IMAGE_FORMAT_LINEAR_DXT3:
case IMAGE_FORMAT_LINEAR_DXT5:
case IMAGE_FORMAT_ATI2N:
nBlockSize = 16;
break;
default:
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' has unsupported format: %d", szTextureNames[ 0 ], m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
break;
}
for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
{
//if ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->imageFormat != m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat )
//{
// Assert( 0 );
// V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' has different format ( number of channels, compression, etc. ) than base: %d != %d", szTextureNames[ nMaterial ],
// m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->imageFormat, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat );
// throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
//}
if ( ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->flags & ~( IGNORE_TEXTURE_FLAGS ) ) != ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->flags & ~( IGNORE_TEXTURE_FLAGS ) ) )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' has different flags than base: %d != %d", szTextureNames[ nMaterial ],
m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->flags, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->flags );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
ResourceEntryInfo *pImageResource = NULL;
for( unsigned int nResource = 0; nResource < m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numResources; nResource++ )
{
if ( m_AtlasGroups[ nAtlasGroup ].m_pResources[ nMaterial ][ nResource ].eType == VTF_LEGACY_RSRC_IMAGE )
{
pImageResource = &m_AtlasGroups[ nAtlasGroup ].m_pResources[ nMaterial ][ nResource ];
break;
}
}
if ( pImageResource == NULL )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not locate image resource for texture '%s'", szTextureNames[ nMaterial ] );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
byte *pPtr = ( ( byte * )m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] ) + ( pImageResource->resData );
DebugCombineMsg( "Material %d: Size = %d\n", nMaterial, m_pVTFData[ nMaterial ]->TellMaxPut() );
char szCheckFileName[ MAX_PATH ];
V_FixupPathName( szCheckFileName, sizeof( szCheckFileName ), szTextureNames[ nMaterial ] );
m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] = false;
for( int nTestIndex = 0; pszFlatTextures[ nTestIndex ] != NULL; nTestIndex++ )
{
if ( strcmpi( pszFlatTextures[ nTestIndex ], szCheckFileName ) == 0 )
{
m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] = true;
break;
}
}
if ( nTexture != 0 && !m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
{
int nWidth, nHeight;
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWidth, nHeight );
if ( nWidth != m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width || nHeight != m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height )
{
DebugCombineMsg( " '%s' has inconsistent texture size %d/%d: width %d->%d, height %d->%d\n", szTextureNames[ nMaterial ], nTexture, nMaterial, nWidth,
m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width, nHeight, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height );
}
}
for ( int nMip = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels - 1; nMip >= 0; nMip-- )
{
int nWidth = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width >> nMip;
int nHeight = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height >> nMip;
if ( nWidth < 4 )
{
nWidth = 4;
}
if ( nHeight < 4 )
{
nHeight = 4;
}
nWidth >>= 2;
nHeight >>= 2;
int nNumBlocks = ( nWidth * nHeight );
int nMipSize = nNumBlocks * nBlockSize;
pMipOffset[ nMaterial ][ nMip ] = pPtr;
nMipWidth[ nMaterial ][ nMip ] = nWidth;
nMipHeight[ nMaterial ][ nMip ] = nHeight;
DebugCombineMsg( " Mip %d: Width=%d, Height=%d, Offset = %d\n", nMip, nWidth, nHeight, pPtr - ( byte * ) m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] );
pPtr += nMipSize;
}
DebugCombineMsg( " END OF FILE = %d\n", pPtr - ( byte * ) m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] );
}
int nWidthShift = 0;
int nHeightShift = 0;
if ( nTexture == 0 )
{
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker = new CSimpleTexturePacker();
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->Init( MAX_COMBINED_WIDTH, MAX_COMBINED_HEIGHT );
for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
{
#ifdef DEBUG_VTF_COMBINE
const char *pszTexture = m_pMaterialKVs[ nMaterial ]->GetString( pszTextureField, NULL );
#endif
DebugCombineMsg( "Material: %d ( %s ) Width=%d, Height=%d\n", nMaterial, szTextureNames[ nMaterial ], m_pVTFFileHeader[ nMaterial ]->width, m_pVTFFileHeader[ nMaterial ]->height );
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->AddTexture( nMaterial, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height );
}
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->Resolve();
}
else
{
// need to validate that the all the "other" textures in the material are the same ratio of the base
// they don't have to be the same size as the base, just all 1/2 the base size or 1/4, or twice, or the same
bool bGotShifts = false;
for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
{
if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
{
continue;
}
int nWriteWidth, nWriteHeight;
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWriteWidth, nWriteHeight );
nWriteWidth >>= 2; // 2 accounts for the DDS block encoding
nWriteHeight >>= 2; // 2 accounts for the DDS block encoding
if ( !bGotShifts )
{
int nWidth = nWriteWidth;
int nHeight = nWriteHeight;
while ( nMipWidth[ nMaterial ][ 0 ] < nWidth )
{
nWidthShift++;
nWidth >>= 1;
}
while ( nMipWidth[ nMaterial ][ 0 ] > nWidth )
{
nWidthShift--;
nWidth <<= 1;
}
while ( nMipHeight[ nMaterial ][ 0 ] < nHeight )
{
nHeightShift++;
nHeight >>= 1;
}
while ( nMipHeight[ nMaterial ][ 0 ] > nHeight )
{
nHeightShift--;
nHeight <<= 1;
}
bGotShifts = true;
}
nWriteWidth = (nWidthShift > 0) ? nWriteWidth >> nWidthShift : nWriteWidth << (-nWidthShift);
nWriteHeight = (nHeightShift > 0) ? nWriteHeight >> nHeightShift : nWriteHeight << (-nHeightShift);
if ( nMipWidth[ nMaterial ][ 0 ] != nWriteWidth || nMipHeight[ nMaterial ][ 0 ] != nWriteHeight )
{
Assert( 0 );
V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' size ( %d, %d ) differs expected size ( %d, %d )",
szTextureNames[ nMaterial ], nMipWidth[ nMaterial ][ 0 ] << 2, nMipHeight[ nMaterial ][ 0 ] << 2, nWriteWidth << 2, nWriteHeight << 2 );
throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
}
}
}
byte *pPtr = m_AtlasGroups[ nAtlasGroup ].m_CombinedTextureMemory[ nTexture ];
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] = ( VTFFileHeader_t * )pPtr;
pPtr += sizeof( VTFFileHeader_t );
memset( m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ], 0, sizeof( *m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] ) );
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->numResources = 1;
ResourceEntryInfo *pResource = ( ResourceEntryInfo * )pPtr;
pPtr += sizeof( ResourceEntryInfo );
// align the dds data to a 16 byte boundary
pPtr = ( byte * )( ( ( uintp )( pPtr + 15 ) ) & ( ~16 ) );
pResource->eType = VTF_LEGACY_RSRC_IMAGE;
pResource->resData = pPtr - ( byte * )m_AtlasGroups[ nAtlasGroup ].m_CombinedTextureMemory[ nTexture ];
int nMaxSize = ( MAX_COMBINED_WIDTH > MAX_COMBINED_HEIGHT ? MAX_COMBINED_WIDTH : MAX_COMBINED_HEIGHT );
int nNumMips = 0;
while( nMaxSize > 0 )
{
nNumMips++;
nMaxSize >>= 1;
}
Q_strncpy( m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->fileTypeString, "VTF", 4 );
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->version[ 0 ] = VTF_MAJOR_VERSION;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->version[ 1 ] = VTF_MINOR_VERSION;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->headerSize = pPtr - ( ( byte * )m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] );
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->imageFormat = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->width = MAX_COMBINED_WIDTH;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->height = MAX_COMBINED_HEIGHT;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->numMipLevels = nNumMips;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->flags = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->flags | TEXTUREFLAGS_COMBINED;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->numFrames = 1;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->startFrame = 0;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->depth = 1;
// potential alignment issues
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->reflectivity.x = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->reflectivity.x;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->reflectivity.y = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->reflectivity.y;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->reflectivity.z = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->reflectivity.z;
m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->bumpScale = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->bumpScale;
byte *pCombinedMipOffset[ MAX_COMBINED_MIP_LEVELS ];
int nCombinedMipWidth[ MAX_COMBINED_MIP_LEVELS ];
int nCombinedMipHeight[ MAX_COMBINED_MIP_LEVELS ];
int nCombinedMipSize[ MAX_COMBINED_MIP_LEVELS ];
for( int nMip = nNumMips - 1; nMip >= 0; nMip-- )
{
pCombinedMipOffset[ nMip ] = pPtr;
int nWidth = m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->width >> nMip;
int nHeight = m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->height >> nMip;
if ( nWidth < 4 )
{
nWidth = 4;
}
if ( nHeight < 4 )
{
nHeight = 4;
}
nWidth >>= 2;
nHeight >>= 2;
int nNumBlocks = ( nWidth * nHeight );
nCombinedMipWidth[ nMip ] = nWidth;
nCombinedMipHeight[ nMip ] = nHeight;
nCombinedMipSize[ nMip ] = nNumBlocks * nBlockSize;
pPtr += nCombinedMipSize[ nMip ];
}
for( int nMip = 0; nMip < nNumMips; nMip++ )
{
int nCombinedLineSize = ( nCombinedMipWidth[ nMip ] ) * nBlockSize;
for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
{
if ( !m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] && nMip >= m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels )
{
continue;
}
int nNewX, nNewY, nWriteWidth, nWriteHeight;
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWriteWidth, nWriteHeight );
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureLocation( nMaterial, nNewX, nNewY );
// adjust nWriteWidth & nWriteHeight by the ratio for this texture
nWriteWidth = (nWidthShift > 0) ? nWriteWidth >> nWidthShift : nWriteWidth << (-nWidthShift);
nWriteHeight = (nHeightShift > 0) ? nWriteHeight >> nHeightShift : nWriteHeight << (-nHeightShift);
nWriteWidth >>= ( 2 + nMip ); // 2 accounts for the DDS block encoding
if ( nWriteWidth <= 0 )
{
if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
{ // we don't need to replicate down any further
continue;
}
nWriteWidth = 1;
}
nWriteHeight >>= ( 2 + nMip ); // 2 accounts for the DDS block encoding
if ( nWriteHeight <= 0 )
{
if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
{ // we don't need to replicate down any further
continue;
}
nWriteHeight = 1;
}
nNewX >>= nMip;
nNewY >>= nMip;
byte *pWriteOffset = pCombinedMipOffset[ nMip ];
pWriteOffset += ( nNewX >> 2 ) * nBlockSize;
pWriteOffset += ( nNewY >> 2 ) * nCombinedLineSize;
if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
{
/*
optimize below to use
_mm_stream_ps
*/
int nCombinedLineSizeDelta = nCombinedLineSize - ( nWriteWidth * nBlockSize );
byte *pReadOffset = pMipOffset[ nMaterial ][ 0 ];
for( int y = 0; y < nWriteHeight; y++ )
{
for( int x = 0; x < nWriteWidth; x++, pWriteOffset += nBlockSize )
{
memcpy( pWriteOffset, pReadOffset, nBlockSize );
}
pWriteOffset += nCombinedLineSizeDelta;
}
}
else
{
int nReadWidth = nMipWidth[ nMaterial ][ nMip ];
Assert( nWriteWidth == nReadWidth );
Assert( nWriteHeight == nMipHeight[ nMaterial ][ nMip ] );
int nReadSize = nReadWidth * nBlockSize;
byte *pReadOffset = pMipOffset[ nMaterial ][ nMip ];
for( int y = 0; y < nWriteHeight; y++ )
{
/* byte *pReadCache = pReadOffset;
int nReadCacheSize = nReadSize;
while ( nReadCacheSize >= 0 )
{
PREFETCH_128( pReadCache, nReadCacheSize );
nReadCacheSize -= CACHE_LINE_SIZE;
}
*/
memcpy( pWriteOffset, pReadOffset, nReadSize );
pReadOffset += nReadSize;
pWriteOffset += nCombinedLineSize;
}
}
}
}
m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ] = pPtr - ( ( byte * )m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] );
// FileHandle_t fh = g_pFullFileSystem->Open( "rjtest.vtf", "wb" );
// g_pFullFileSystem->Write( m_CombinedHeaders[ nTexture ], m_nCombinedTextureSize[ nTexture ], fh );
// g_pFullFileSystem->Close( fh );
for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
{
delete m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ];
m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ] = NULL;
m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] = NULL;
}
m_pCombinedStudioData->m_Results.m_flTextureCombineDuration += ( float )( Plat_FloatTime() - flStartCombineTime );
return true;
}
void CTextureCombine::GetTextureInfo( int nIndex, Vector2D &vStartST, Vector2D &vSizeST, Vector2D &vPixelSize )
{
int nAtlasGroup = m_nMaterialAtlasInfo[ nIndex ][ ATLAS_INFO_GROUP_INDEX ];
int nMaterial = m_nMaterialAtlasInfo[ nIndex ][ ATLAS_INFO_MATERIAL_INDEX ];
int nStartS, nStartT;
int nWidth, nHeight;
if ( m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker )
{
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWidth, nHeight );
m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureLocation( nMaterial, nStartS, nStartT );
vStartST.x = ( float )nStartS / ( float )MAX_COMBINED_WIDTH;
vStartST.y = ( float )nStartT / ( float )MAX_COMBINED_HEIGHT;
vSizeST.x = ( float )nWidth / ( float )MAX_COMBINED_WIDTH;
vSizeST.y = ( float )nHeight / ( float )MAX_COMBINED_HEIGHT;
vPixelSize.x = 1.0f / ( float )nWidth;
vPixelSize.y = 1.0f / ( float )nHeight;
}
else
{
vStartST.x = 0.0f;
vStartST.y = 0.0f;
vSizeST.x = 1.0f;
vSizeST.y = 1.0f;
vPixelSize.x = 0.0f;
vPixelSize.y = 0.0f;
}
}