|
|
#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; } }
|