Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1212 lines
40 KiB

  1. #include "datacache/imdlcache.h"
  2. #include "vtfcombine.h"
  3. #include "strtools.h"
  4. #include "keyvalues.h"
  5. #include "filesystem.h"
  6. #include "vtf/vtf.h"
  7. #include "tier0/cache_hints.h"
  8. #include "materialsystem/imaterialsystem.h"
  9. //#define DEBUG_VMT_COMBINE 1
  10. #ifdef DEBUG_VMT_COMBINE
  11. #define DebugCombineMsg( ... ) Msg( __VA_ARGS__ )
  12. #else
  13. #define DebugCombineMsg( ... )
  14. #endif
  15. CTextureCombine& GetTextureCombiner()
  16. {
  17. // Allocate on demand to avoid consuming 50 MB of memory and address space
  18. // everywhere when this object isn't even used.
  19. // This is a memory leak but that is still better than having the memory
  20. // allocated regardless of whether it is used.
  21. static CTextureCombine* s_TextureCombiner = new CTextureCombine;
  22. return *s_TextureCombiner;
  23. }
  24. class CSimpleTexturePacker
  25. {
  26. public:
  27. CSimpleTexturePacker( );
  28. void Init( int nWidth, int nHeight );
  29. void AddTexture( int nID, int nWidth, int nHeight );
  30. void Resolve( );
  31. void GetTextureLocation( int nID, int &x, int &y );
  32. void GetTextureSize( int nID, int &x, int &y );
  33. private:
  34. static const int m_nMaxSubdivisions = 32;
  35. static const int m_nMaxTextures = 32;
  36. typedef struct SSubDivision
  37. {
  38. int x, y, width, height;
  39. } TSubDivisions;
  40. TSubDivisions m_SubDivisions[ m_nMaxSubdivisions ];
  41. int m_nNumSubDivisions;
  42. typedef struct STextureInfo
  43. {
  44. int m_nID;
  45. TSubDivisions m_Location;
  46. } TTextureInfo;
  47. TTextureInfo m_Textures[ m_nMaxTextures ];
  48. TTextureInfo *m_TextureOrder[ m_nMaxTextures ];
  49. int m_nNumTextures;
  50. int m_nWidth;
  51. int m_nHeight;
  52. static int TextureSizeCompare( const void *elem1, const void *elem2 );
  53. void Reset( );
  54. bool FindOpenSpace( TTextureInfo *pTexture );
  55. bool ResolveBrute( );
  56. bool IterateTextures( );
  57. bool BruteIterate( );
  58. };
  59. CSimpleTexturePacker::CSimpleTexturePacker( )
  60. {
  61. m_nNumSubDivisions = 0;
  62. m_nNumTextures = 0;
  63. }
  64. void CSimpleTexturePacker::Init( int nWidth, int nHeight )
  65. {
  66. m_nWidth = nWidth;
  67. m_nHeight = nHeight;
  68. m_nNumTextures = 0;
  69. }
  70. void CSimpleTexturePacker::Reset( )
  71. {
  72. m_SubDivisions[ 0 ].x = 0;
  73. m_SubDivisions[ 0 ].y = 0;
  74. m_SubDivisions[ 0 ].width = m_nWidth;
  75. m_SubDivisions[ 0 ].height = m_nHeight;
  76. m_nNumSubDivisions = 1;
  77. }
  78. void CSimpleTexturePacker::AddTexture( int nID, int nWidth, int nHeight )
  79. {
  80. if ( m_nNumTextures >= m_nMaxTextures )
  81. {
  82. Assert( 0 );
  83. V_sprintf_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorMessage, "too many textures added to packer" );
  84. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  85. }
  86. m_Textures[ m_nNumTextures ].m_nID = nID;
  87. m_Textures[ m_nNumTextures ].m_Location.width = nWidth;
  88. m_Textures[ m_nNumTextures ].m_Location.height = nHeight;
  89. m_TextureOrder[ m_nNumTextures ] = &m_Textures[ m_nNumTextures ];
  90. m_nNumTextures++;
  91. }
  92. int CSimpleTexturePacker::TextureSizeCompare( const void *elem1, const void *elem2 )
  93. {
  94. TTextureInfo *pTexture1 = ( TTextureInfo * )elem1;
  95. TTextureInfo *pTexture2 = ( TTextureInfo * )elem2;
  96. if ( pTexture1->m_Location.height > pTexture2->m_Location.height )
  97. {
  98. return -1;
  99. }
  100. else if ( pTexture1->m_Location.height < pTexture2->m_Location.height )
  101. {
  102. return 1;
  103. }
  104. else if ( pTexture1->m_Location.width > pTexture2->m_Location.width )
  105. {
  106. return -1;
  107. }
  108. else if ( pTexture1->m_Location.width < pTexture2->m_Location.width )
  109. {
  110. return 1;
  111. }
  112. #if 0
  113. int index1 = *(byte *)elem1;
  114. int index2 = *(byte *)elem2;
  115. if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.height > SimpleTexturePacker.m_Textures[ index2 ].m_Location.height )
  116. {
  117. return -1;
  118. }
  119. else if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.height < SimpleTexturePacker.m_Textures[ index2 ].m_Location.height )
  120. {
  121. return 1;
  122. }
  123. else if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.width > SimpleTexturePacker.m_Textures[ index2 ].m_Location.width )
  124. {
  125. return -1;
  126. }
  127. else if ( SimpleTexturePacker.m_Textures[ index1 ].m_Location.width < SimpleTexturePacker.m_Textures[ index2 ].m_Location.width )
  128. {
  129. return 1;
  130. }
  131. #endif
  132. return 0;
  133. }
  134. bool CSimpleTexturePacker::FindOpenSpace( TTextureInfo *pTexture )
  135. {
  136. int nSubDivision;
  137. const int nWidth = pTexture->m_Location.width;
  138. const int nHeight = pTexture->m_Location.height;
  139. for( nSubDivision = 0; nSubDivision < m_nNumSubDivisions; nSubDivision++ )
  140. {
  141. if ( m_SubDivisions[ nSubDivision ].width >= nWidth && m_SubDivisions[ nSubDivision ].height >= nHeight )
  142. {
  143. break;
  144. }
  145. }
  146. if ( nSubDivision >= m_nNumSubDivisions )
  147. {
  148. return false;
  149. }
  150. pTexture->m_Location.x = m_SubDivisions[ nSubDivision ].x;
  151. pTexture->m_Location.y = m_SubDivisions[ nSubDivision ].y;
  152. if ( m_SubDivisions[ nSubDivision ].width == nWidth && m_SubDivisions[ nSubDivision ].height == nHeight )
  153. { // completely used up
  154. m_nNumSubDivisions--;
  155. if ( nSubDivision < m_nNumSubDivisions )
  156. {
  157. memmove( &m_SubDivisions[ nSubDivision ], &m_SubDivisions[ nSubDivision + 1 ], sizeof( m_SubDivisions[ nSubDivision ] ) * ( m_nNumSubDivisions - nSubDivision ) );
  158. }
  159. }
  160. else
  161. {
  162. if ( m_SubDivisions[ nSubDivision ].width == nWidth )
  163. { // only one potential piece
  164. m_SubDivisions[ nSubDivision ].y += nHeight;
  165. m_SubDivisions[ nSubDivision ].height -= nHeight;
  166. }
  167. else if ( m_SubDivisions[ nSubDivision ].height == nHeight )
  168. { // only one potential piece
  169. m_SubDivisions[ nSubDivision ].x += nWidth;
  170. m_SubDivisions[ nSubDivision ].width -= nWidth;
  171. }
  172. else
  173. {
  174. if ( m_nNumSubDivisions >= m_nMaxSubdivisions )
  175. {
  176. Assert( 0 );
  177. V_sprintf_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorMessage, "too many subdivision within texture packer" );
  178. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  179. }
  180. m_SubDivisions[ m_nNumSubDivisions ].x = m_SubDivisions[ nSubDivision ].x + nWidth;
  181. m_SubDivisions[ m_nNumSubDivisions ].y = m_SubDivisions[ nSubDivision ].y;
  182. m_SubDivisions[ m_nNumSubDivisions ].width = m_SubDivisions[ nSubDivision ].width - nWidth;
  183. m_SubDivisions[ m_nNumSubDivisions ].height = m_SubDivisions[ nSubDivision ].height;
  184. m_nNumSubDivisions++;
  185. m_SubDivisions[ nSubDivision ].y += nHeight;
  186. m_SubDivisions[ nSubDivision ].width = nWidth;
  187. m_SubDivisions[ nSubDivision ].height -= nHeight;
  188. }
  189. }
  190. return true;
  191. }
  192. void CSimpleTexturePacker::Resolve( )
  193. {
  194. Reset();
  195. qsort( m_TextureOrder, m_nNumTextures, sizeof( m_TextureOrder[ 0 ] ), CSimpleTexturePacker::TextureSizeCompare );
  196. GetTextureCombiner().m_pCombinedStudioData->m_Results.m_nNumTexturePackIterations++;
  197. for( int nTexture = 0; nTexture < m_nNumTextures; nTexture++ )
  198. {
  199. if ( FindOpenSpace( m_TextureOrder[ nTexture ] ) == false )
  200. {
  201. if ( ResolveBrute() )
  202. {
  203. #if 0
  204. for( int nTexture = 0; nTexture < m_nNumTextures; nTexture++ )
  205. {
  206. 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,
  207. m_TextureOrder[ nTexture ]->m_Location.width, m_TextureOrder[ nTexture ]->m_Location.height );
  208. }
  209. #endif
  210. return;
  211. }
  212. // put them back in order
  213. qsort( m_TextureOrder, m_nNumTextures, sizeof( m_TextureOrder[ 0 ] ), CSimpleTexturePacker::TextureSizeCompare );
  214. AssertMsg( false, "Could not find open space for texture in packer" );
  215. V_sprintf_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not find open space for texture in packer" );
  216. GetTextureCombiner().m_pCombinedStudioData->m_Results.m_nDetailedError = COMBINED_DETAIL_ERROR_TEXTURE_PACKER_NO_SPACE;
  217. char szTemp[ 256 ];
  218. for( int nTextureList = 0; nTextureList < m_nNumTextures; nTextureList++ )
  219. {
  220. V_sprintf_safe( szTemp, "ID %d%c: x=%d y=%d width=%d height=%d\n", m_TextureOrder[ nTextureList ]->m_nID, ( nTextureList == nTexture ? '*' : ' ' ),
  221. m_TextureOrder[ nTextureList ]->m_Location.x, m_TextureOrder[ nTextureList ]->m_Location.y, m_TextureOrder[ nTextureList ]->m_Location.width, m_TextureOrder[ nTextureList ]->m_Location.height );
  222. V_strcat_safe( GetTextureCombiner().m_pCombinedStudioData->m_Results.m_szErrorDetails, szTemp );
  223. }
  224. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  225. }
  226. 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,
  227. m_TextureOrder[ nTexture ]->m_Location.width, m_TextureOrder[ nTexture ]->m_Location.height );
  228. }
  229. DebugCombineMsg( "Remaining Space:\n" );
  230. for( int nSubDivision = 0; nSubDivision < m_nNumSubDivisions; nSubDivision++ )
  231. {
  232. 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 );
  233. }
  234. }
  235. bool CSimpleTexturePacker::BruteIterate( )
  236. {
  237. Reset();
  238. GetTextureCombiner().m_pCombinedStudioData->m_Results.m_nNumTexturePackIterations++;
  239. DebugCombineMsg( "Trying: " );
  240. for( int nTexture = 0; nTexture < m_nNumTextures; nTexture++ )
  241. {
  242. DebugCombineMsg( "%d ", nTexture );
  243. if ( FindOpenSpace( m_TextureOrder[ nTexture ] ) == false )
  244. {
  245. DebugCombineMsg( "Failed\n" );
  246. return false;
  247. }
  248. }
  249. DebugCombineMsg( "Succeeded\n" );
  250. return true;
  251. }
  252. bool CSimpleTexturePacker::IterateTextures( )
  253. {
  254. int nSwapIndex;
  255. TTextureInfo *pSaveSwap;
  256. int nCounters[ m_nMaxTextures ];
  257. memset( nCounters, 0, m_nNumTextures * sizeof( int ) );
  258. // Boothroyd method
  259. if ( BruteIterate() )
  260. {
  261. return true;
  262. }
  263. for ( int nIndex = 0; ; nCounters[ nIndex ]++ )
  264. {
  265. while ( nIndex > 1 )
  266. {
  267. nCounters[ --nIndex ] = 0;
  268. }
  269. while ( nCounters[ nIndex ] >= nIndex )
  270. {
  271. if ( ++nIndex >= m_nNumTextures )
  272. {
  273. return false;
  274. }
  275. }
  276. nSwapIndex = ( nIndex & 1 ) ? nCounters[ nIndex ] : 0;
  277. pSaveSwap = m_TextureOrder[ nSwapIndex ];
  278. m_TextureOrder[ nSwapIndex ] = m_TextureOrder[ nIndex ];
  279. m_TextureOrder[ nIndex ] = pSaveSwap;
  280. if ( BruteIterate() )
  281. {
  282. return true;
  283. }
  284. }
  285. return false;
  286. }
  287. bool CSimpleTexturePacker::ResolveBrute( )
  288. {
  289. for ( int i = 0; i < m_nMaxTextures; i++ )
  290. {
  291. m_TextureOrder[ i ] = &m_Textures[ i ];
  292. }
  293. return IterateTextures();
  294. }
  295. void CSimpleTexturePacker::GetTextureSize( int nID, int &x, int &y )
  296. {
  297. x = m_Textures[ nID ].m_Location.width;
  298. y = m_Textures[ nID ].m_Location.height;
  299. }
  300. void CSimpleTexturePacker::GetTextureLocation( int nID, int &x, int &y )
  301. {
  302. x = m_Textures[ nID ].m_Location.x;
  303. y = m_Textures[ nID ].m_Location.y;
  304. }
  305. typedef struct STextureEntry
  306. {
  307. const char *m_pszTextureField;
  308. const char *m_pszFlatReplacement;
  309. } TTextureEntry;
  310. static TTextureEntry szCustomHeroTextures[] =
  311. {
  312. { "$basetexture", NULL },
  313. { "$normalmap", "models\\development\\flatnormal" },
  314. // "$diffusewarp",
  315. { "$maskmap1", "models\\development\\blankmasks1" },
  316. { "$maskmap2", "models\\development\\blankmasks2" },
  317. { NULL, NULL }
  318. };
  319. static TTextureEntry szVertexLitGenericTextures[] =
  320. {
  321. { "$basetexture", NULL },
  322. { "$phongexponenttexture", NULL },
  323. { NULL, NULL }
  324. };
  325. typedef struct SMaterialToTexture
  326. {
  327. const char *m_pszMaterialName;
  328. TTextureEntry *m_pszTextureList;
  329. } TMaterialToTexture;
  330. static const TMaterialToTexture MaterialToTexture[] =
  331. {
  332. {
  333. "customhero",
  334. szCustomHeroTextures
  335. },
  336. {
  337. "VertexLitGeneric",
  338. szVertexLitGenericTextures
  339. },
  340. {
  341. NULL,
  342. NULL
  343. }
  344. };
  345. static const char *pszFlatTextures[] =
  346. {
  347. "models\\development\\flatnormal",
  348. "models\\development\\blankmasks1",
  349. "models\\development\\blankmasks2",
  350. NULL
  351. };
  352. CTextureCombine::CTextureCombine( )
  353. {
  354. Init( NULL );
  355. }
  356. void CTextureCombine::Init( TCombinedStudioData *pCombinedStudioData )
  357. {
  358. m_pCombinedStudioData = pCombinedStudioData;
  359. m_nNumMaterials = 0;
  360. memset( m_szMaterials, 0, sizeof( m_szMaterials ) );
  361. memset( m_nMaterialAtlasInfo, 0, sizeof( m_nMaterialAtlasInfo ) );
  362. m_nMaxAtlasGroup = 0;
  363. memset( m_pMaterialKVs, 0, sizeof( m_pMaterialKVs ) );
  364. for ( int nGroup = 0; nGroup < COMBINER_MAX_ATLAS_GROUPS; nGroup++ )
  365. {
  366. m_AtlasGroups[ nGroup ].m_nNumMaterials = 0;
  367. memset( m_AtlasGroups[ nGroup ].m_nMaterialIndices, 0xFF, sizeof( m_AtlasGroups[ nGroup ].m_nMaterialIndices ) ); // all set to -1
  368. memset( m_AtlasGroups[ nGroup ].m_pVTFData, 0, sizeof( m_AtlasGroups[ nGroup ].m_pVTFData ) );
  369. memset( m_AtlasGroups[ nGroup ].m_pVTFFileHeader, 0, sizeof( m_AtlasGroups[ nGroup ].m_pVTFFileHeader ) );
  370. memset( m_AtlasGroups[ nGroup ].m_pResources, 0, sizeof( m_AtlasGroups[ nGroup ].m_pResources ) );
  371. memset( m_AtlasGroups[ nGroup ].m_bIsFlat, 0, sizeof( m_AtlasGroups[ nGroup ].m_bIsFlat ) );
  372. m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs = NULL;
  373. memset( m_AtlasGroups[ nGroup ].m_CombinedTextureMemory, 0, sizeof( m_AtlasGroups[ nGroup ].m_CombinedTextureMemory ) );
  374. memset( m_AtlasGroups[ nGroup ].m_nCombinedTextureSize, 0, sizeof( m_AtlasGroups[ nGroup ].m_nCombinedTextureSize ) );
  375. memset( m_AtlasGroups[ nGroup ].m_CombinedHeaders, 0, sizeof( m_AtlasGroups[ nGroup ].m_CombinedHeaders ) );
  376. m_AtlasGroups[ nGroup ].m_pSimpleTexturePacker = NULL;
  377. }
  378. if ( pCombinedStudioData )
  379. {
  380. g_CombinerWriter.InitWriteArea( WRITE_AREA_VTF, g_CombinerWriter.GetWritePos() );
  381. g_CombinerWriter.SetWriteArea( WRITE_AREA_VTF );
  382. }
  383. }
  384. void CTextureCombine::Cleanup( )
  385. {
  386. for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
  387. {
  388. if ( m_pMaterialKVs[ nMaterial ] )
  389. {
  390. m_pMaterialKVs[ nMaterial ]->deleteThis();
  391. m_pMaterialKVs[ nMaterial ] = NULL;
  392. }
  393. }
  394. for ( int nGroup = 0; nGroup <= m_nMaxAtlasGroup; nGroup++ )
  395. {
  396. for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
  397. {
  398. delete m_AtlasGroups[ nGroup ].m_pVTFData[ nMaterial ];
  399. m_AtlasGroups[ nGroup ].m_pVTFData[ nMaterial ] = NULL;
  400. }
  401. }
  402. }
  403. void CTextureCombine::FreeCombinedMaterials( )
  404. {
  405. for ( int nGroup = 0; nGroup <= COMBINER_MAX_ATLAS_GROUPS; nGroup++ )
  406. {
  407. if ( m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs != NULL )
  408. {
  409. m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs->deleteThis();
  410. m_AtlasGroups[ nGroup ].m_pCombinedMaterialKVs = NULL;
  411. }
  412. delete m_AtlasGroups[ nGroup ].m_pSimpleTexturePacker;
  413. m_AtlasGroups[ nGroup ].m_pSimpleTexturePacker = NULL;
  414. }
  415. }
  416. int CTextureCombine::AddMaterial( const char *pszFileName )
  417. {
  418. for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
  419. {
  420. if ( strcmpi( m_szMaterials[ nMaterial ], pszFileName ) == 0 )
  421. {
  422. return nMaterial;
  423. }
  424. }
  425. V_strcpy_safe( m_szMaterials[ m_nNumMaterials ], pszFileName );
  426. // intending to return m_nNumMaterials, and then increment it
  427. return m_nNumMaterials++;
  428. }
  429. void CTextureCombine::AddNonAtlasedMaterial( int nMaterial )
  430. {
  431. m_pCombinedStudioData->m_pNonAtlasedMaterialKVs[ m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames ] = ( m_pMaterialKVs[ nMaterial ] != NULL ) ? m_pMaterialKVs[ nMaterial ]->MakeCopy() : NULL;
  432. V_FileBase( m_szMaterials[ nMaterial ], m_pCombinedStudioData->m_szNonAtlasedMaterialBaseName[ m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames ], MAX_PATH );
  433. m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames++;
  434. // de-dupe paths
  435. char szPath[ MAX_PATH ];
  436. V_strcpy( szPath, m_szMaterials[ nMaterial ] );
  437. V_StripFilename( szPath );
  438. bool bFound = false;
  439. for (int i = 0; i < m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths; i++ )
  440. {
  441. if ( V_strcmp( m_pCombinedStudioData->m_szNonAtlasedMaterialPaths[ i ], szPath ) == 0 )
  442. {
  443. bFound = true;
  444. break;
  445. }
  446. }
  447. if ( !bFound )
  448. {
  449. V_strcpy( m_pCombinedStudioData->m_szNonAtlasedMaterialPaths[ m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths ], szPath );
  450. m_pCombinedStudioData->m_nNumNonAtlasedMaterialPaths++;
  451. }
  452. }
  453. void CTextureCombine::Resolve( )
  454. {
  455. if ( m_nNumMaterials <= 0 )
  456. {
  457. Assert( 0 );
  458. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "no materials specified for texture combiner" );
  459. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  460. }
  461. for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
  462. {
  463. m_pMaterialKVs[ nMaterial ] = new KeyValues( "vmt" );
  464. if ( !materials->LoadKeyValuesFromVMTFile( *(m_pMaterialKVs[ nMaterial ]), m_szMaterials[ nMaterial ], true ) )
  465. {
  466. AddNonAtlasedMaterial( nMaterial );
  467. // mark as not in an atlas
  468. m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_GROUP_INDEX ] = -1;
  469. m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_MATERIAL_INDEX ] = m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames - 1;
  470. m_pMaterialKVs[ nMaterial ]->deleteThis();
  471. m_pMaterialKVs[ nMaterial ] = NULL;
  472. }
  473. }
  474. GatherAtlasInfo();
  475. // assign a copy of the KVs from the first material in each atlas group to be the combined material KVs
  476. for ( int nAtlasGroup = 0; nAtlasGroup <= m_nMaxAtlasGroup; nAtlasGroup++ )
  477. {
  478. int nMaterial = m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ 0 ];
  479. if ( m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials > 1 )
  480. {
  481. m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterialKVs = m_pMaterialKVs[ nMaterial ]->MakeCopy();
  482. }
  483. else
  484. {
  485. AddNonAtlasedMaterial( nMaterial );
  486. // mark as not in an atlas
  487. m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_GROUP_INDEX ] = -1;
  488. m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_MATERIAL_INDEX ] = m_pCombinedStudioData->m_nNumNonAtlasedMaterialBaseNames - 1;
  489. }
  490. }
  491. m_pCombinedStudioData->m_nNumAtlasGroups = m_nMaxAtlasGroup + 1;
  492. FindMaterialToTexture();
  493. Cleanup();
  494. }
  495. void CTextureCombine::GatherAtlasInfo( )
  496. {
  497. for( int nMaterial = 0; nMaterial < m_nNumMaterials; nMaterial++ )
  498. {
  499. if ( m_pMaterialKVs[ nMaterial ] != NULL )
  500. {
  501. // get the atlas index for each material (default it to just use index 0, one atlas for all)
  502. int nAtlasGroup = m_pMaterialKVs[ nMaterial ]->GetInt( "$atlas_group", 0 );
  503. // track the max used atlas group
  504. if ( nAtlasGroup > m_nMaxAtlasGroup )
  505. {
  506. m_nMaxAtlasGroup = nAtlasGroup;
  507. }
  508. // link the "global" material index to an atlas group
  509. m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_GROUP_INDEX ] = nAtlasGroup;
  510. // link the "global" material index to the atlas group material index
  511. m_nMaterialAtlasInfo[ nMaterial ][ ATLAS_INFO_MATERIAL_INDEX ] = m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials;
  512. // link atlas group material index to the "global" material index
  513. m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials ] = nMaterial;
  514. m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials++;
  515. }
  516. }
  517. }
  518. void CTextureCombine::FindMaterialToTexture( )
  519. {
  520. for ( int nAtlasGroup = 0; nAtlasGroup <= m_nMaxAtlasGroup; nAtlasGroup++ )
  521. {
  522. // use the first material of the atlas group as the "master" for KV values and image format
  523. const char *pszShaderName = m_pMaterialKVs[ m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ 0 ] ]->GetName();
  524. for( m_nMaterialToTexture = 0; MaterialToTexture[ m_nMaterialToTexture ].m_pszMaterialName != NULL; m_nMaterialToTexture++ )
  525. {
  526. if ( strcmpi( pszShaderName, MaterialToTexture[ m_nMaterialToTexture ].m_pszMaterialName ) == 0 )
  527. {
  528. break;
  529. }
  530. }
  531. if ( MaterialToTexture[ m_nMaterialToTexture ].m_pszMaterialName == NULL )
  532. {
  533. Assert( 0 );
  534. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "unsupported shader for texture combiner: %s", pszShaderName );
  535. throw( COMBINE_RESULT_FLAG_UNSUPPORTED_SHADER );
  536. }
  537. for( int nTexture = 0; MaterialToTexture[ m_nMaterialToTexture ].m_pszTextureList[ nTexture ].m_pszTextureField != NULL; nTexture++ )
  538. {
  539. const char *pszTextureField = MaterialToTexture[ m_nMaterialToTexture ].m_pszTextureList[ nTexture ].m_pszTextureField;
  540. const char *pszFlatReplacement = MaterialToTexture[ m_nMaterialToTexture ].m_pszTextureList[ nTexture ].m_pszFlatReplacement;
  541. char NewFieldValue[ 128 ];
  542. bool bUsed = CombineTexture( nAtlasGroup, nTexture, pszTextureField, pszFlatReplacement );
  543. if ( bUsed )
  544. {
  545. m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedTextures[ nTexture ] = ( unsigned char * )malloc( m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ] );
  546. memcpy( m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedTextures[ nTexture ], m_AtlasGroups[ nAtlasGroup ].m_CombinedTextureMemory[ nTexture ], m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ] );
  547. m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSizes[ nTexture ] = m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ];
  548. V_sprintf_safe( NewFieldValue, "!%s|%d|%d|%hu|%d!", m_pCombinedStudioData->m_szCombinedModelName, nAtlasGroup, nTexture, m_pCombinedStudioData->m_FinalHandle, CModelCombine::GetNextAssetID() );
  549. m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterialKVs->SetString( pszTextureField, NewFieldValue );
  550. m_pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterial = m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterialKVs;
  551. }
  552. }
  553. }
  554. }
  555. #define IGNORE_TEXTURE_FLAGS ( TEXTUREFLAGS_HINT_DXT5 | TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_SKIP_INITIAL_DOWNLOAD )
  556. bool CTextureCombine::LoadVTFs( int nAtlasGroup, const char *pszTextureField, const char *pszFlatReplacement, char szTextureNames[ COMBINER_MAX_MATERIALS ][ MAX_PATH ] )
  557. {
  558. for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
  559. {
  560. const char *pszTexture = m_pMaterialKVs[ m_AtlasGroups[ nAtlasGroup ].m_nMaterialIndices[ nMaterial ] ]->GetString( pszTextureField, NULL );
  561. if ( pszTexture == NULL )
  562. {
  563. if ( nMaterial == 0 )
  564. { // if not present on the primary material, then skip this texture option
  565. return false;
  566. }
  567. if ( pszFlatReplacement != NULL )
  568. {
  569. pszTexture = pszFlatReplacement;
  570. }
  571. else
  572. {
  573. Assert( 0 );
  574. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not located required texture in material %s", pszTexture );
  575. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  576. }
  577. }
  578. V_strcpy_safe( szTextureNames[ nMaterial ], pszTexture );
  579. char szFinalPath[ MAX_PATH ];
  580. V_ComposeFileName( "materials/", pszTexture, szFinalPath, sizeof( szFinalPath ) );
  581. V_DefaultExtension( szFinalPath, ".vtf", sizeof( szFinalPath ) );
  582. m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ] = new CUtlBuffer();
  583. if ( g_pFullFileSystem->ReadFile( szFinalPath, "GAME", *m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ], 0 ) == false )
  584. {
  585. Assert( 0 );
  586. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not read texture %s", szFinalPath );
  587. throw( COMBINE_RESULT_FLAG_MISSING_ASSET_FILE );
  588. }
  589. VTFFileBaseHeader_t *pVTFFileBaseHeader = ( VTFFileBaseHeader_t * )m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ]->PeekGet();
  590. if ( pVTFFileBaseHeader->version[ 0 ] != VTF_MAJOR_VERSION || pVTFFileBaseHeader->version[ 1 ] != VTF_MINOR_VERSION )
  591. {
  592. Assert( 0 );
  593. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture is invalid version %s", szFinalPath );
  594. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  595. }
  596. m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] = ( VTFFileHeader_t * )pVTFFileBaseHeader;
  597. if ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numFrames != 1 || m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->startFrame != 0 )
  598. {
  599. Assert( 0 );
  600. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture has frame information %s", szFinalPath );
  601. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  602. }
  603. m_AtlasGroups[ nAtlasGroup ].m_pResources[ nMaterial ] = ( ResourceEntryInfo * )(m_AtlasGroups[ nAtlasGroup ]. m_pVTFFileHeader[ nMaterial ] + 1 );
  604. if ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels > MAX_COMBINED_MIP_LEVELS )
  605. {
  606. Assert( 0 );
  607. 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 );
  608. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  609. }
  610. }
  611. return true;
  612. }
  613. bool CTextureCombine::CombineTexture( int nAtlasGroup, int nTexture, const char *pszTextureField, const char *pszFlatReplacement )
  614. {
  615. Assert( nAtlasGroup >= 0 && nAtlasGroup <= m_nMaxAtlasGroup );
  616. Assert( nTexture >= 0 && nTexture < COMBINER_MAX_TEXTURES_PER_MATERIAL );
  617. double flStartLoadTime = Plat_FloatTime();
  618. char szTextureNames[ COMBINER_MAX_MATERIALS ][ MAX_PATH ];
  619. V_strcpy_safe( szTextureNames[ 0 ], "Unknown Texture" );
  620. if ( !LoadVTFs( nAtlasGroup, pszTextureField, pszFlatReplacement, szTextureNames ) )
  621. {
  622. return false;
  623. }
  624. double flStartCombineTime = Plat_FloatTime();
  625. m_pCombinedStudioData->m_Results.m_flTextureLoadDuration += ( float )( flStartCombineTime - flStartLoadTime );
  626. byte *pMipOffset[ COMBINER_MAX_MATERIALS ][ MAX_COMBINED_MIP_LEVELS ];
  627. int nMipWidth[ COMBINER_MAX_MATERIALS ][ MAX_COMBINED_MIP_LEVELS ];
  628. int nMipHeight[ COMBINER_MAX_MATERIALS ][ MAX_COMBINED_MIP_LEVELS ];
  629. int nBlockSize;
  630. memset( pMipOffset, 0, sizeof( pMipOffset ) );
  631. switch ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat )
  632. {
  633. case IMAGE_FORMAT_DXT1:
  634. case IMAGE_FORMAT_DXT1_RUNTIME:
  635. case IMAGE_FORMAT_LINEAR_DXT1:
  636. case IMAGE_FORMAT_ATI1N:
  637. nBlockSize = 8;
  638. break;
  639. case IMAGE_FORMAT_DXT3:
  640. case IMAGE_FORMAT_DXT5:
  641. case IMAGE_FORMAT_DXT5_RUNTIME:
  642. case IMAGE_FORMAT_LINEAR_DXT3:
  643. case IMAGE_FORMAT_LINEAR_DXT5:
  644. case IMAGE_FORMAT_ATI2N:
  645. nBlockSize = 16;
  646. break;
  647. default:
  648. Assert( 0 );
  649. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' has unsupported format: %d", szTextureNames[ 0 ], m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat );
  650. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  651. break;
  652. }
  653. for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
  654. {
  655. //if ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->imageFormat != m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat )
  656. //{
  657. // Assert( 0 );
  658. // 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 ],
  659. // m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->imageFormat, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat );
  660. // throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  661. //}
  662. if ( ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->flags & ~( IGNORE_TEXTURE_FLAGS ) ) != ( m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->flags & ~( IGNORE_TEXTURE_FLAGS ) ) )
  663. {
  664. Assert( 0 );
  665. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' has different flags than base: %d != %d", szTextureNames[ nMaterial ],
  666. m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->flags, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->flags );
  667. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  668. }
  669. ResourceEntryInfo *pImageResource = NULL;
  670. for( unsigned int nResource = 0; nResource < m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numResources; nResource++ )
  671. {
  672. if ( m_AtlasGroups[ nAtlasGroup ].m_pResources[ nMaterial ][ nResource ].eType == VTF_LEGACY_RSRC_IMAGE )
  673. {
  674. pImageResource = &m_AtlasGroups[ nAtlasGroup ].m_pResources[ nMaterial ][ nResource ];
  675. break;
  676. }
  677. }
  678. if ( pImageResource == NULL )
  679. {
  680. Assert( 0 );
  681. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "could not locate image resource for texture '%s'", szTextureNames[ nMaterial ] );
  682. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  683. }
  684. byte *pPtr = ( ( byte * )m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] ) + ( pImageResource->resData );
  685. DebugCombineMsg( "Material %d: Size = %d\n", nMaterial, m_pVTFData[ nMaterial ]->TellMaxPut() );
  686. char szCheckFileName[ MAX_PATH ];
  687. V_FixupPathName( szCheckFileName, sizeof( szCheckFileName ), szTextureNames[ nMaterial ] );
  688. m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] = false;
  689. for( int nTestIndex = 0; pszFlatTextures[ nTestIndex ] != NULL; nTestIndex++ )
  690. {
  691. if ( strcmpi( pszFlatTextures[ nTestIndex ], szCheckFileName ) == 0 )
  692. {
  693. m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] = true;
  694. break;
  695. }
  696. }
  697. if ( nTexture != 0 && !m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
  698. {
  699. int nWidth, nHeight;
  700. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWidth, nHeight );
  701. if ( nWidth != m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width || nHeight != m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height )
  702. {
  703. DebugCombineMsg( " '%s' has inconsistent texture size %d/%d: width %d->%d, height %d->%d\n", szTextureNames[ nMaterial ], nTexture, nMaterial, nWidth,
  704. m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width, nHeight, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height );
  705. }
  706. }
  707. for ( int nMip = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels - 1; nMip >= 0; nMip-- )
  708. {
  709. int nWidth = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width >> nMip;
  710. int nHeight = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height >> nMip;
  711. if ( nWidth < 4 )
  712. {
  713. nWidth = 4;
  714. }
  715. if ( nHeight < 4 )
  716. {
  717. nHeight = 4;
  718. }
  719. nWidth >>= 2;
  720. nHeight >>= 2;
  721. int nNumBlocks = ( nWidth * nHeight );
  722. int nMipSize = nNumBlocks * nBlockSize;
  723. pMipOffset[ nMaterial ][ nMip ] = pPtr;
  724. nMipWidth[ nMaterial ][ nMip ] = nWidth;
  725. nMipHeight[ nMaterial ][ nMip ] = nHeight;
  726. DebugCombineMsg( " Mip %d: Width=%d, Height=%d, Offset = %d\n", nMip, nWidth, nHeight, pPtr - ( byte * ) m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] );
  727. pPtr += nMipSize;
  728. }
  729. DebugCombineMsg( " END OF FILE = %d\n", pPtr - ( byte * ) m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] );
  730. }
  731. int nWidthShift = 0;
  732. int nHeightShift = 0;
  733. if ( nTexture == 0 )
  734. {
  735. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker = new CSimpleTexturePacker();
  736. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->Init( MAX_COMBINED_WIDTH, MAX_COMBINED_HEIGHT );
  737. for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
  738. {
  739. #ifdef DEBUG_VTF_COMBINE
  740. const char *pszTexture = m_pMaterialKVs[ nMaterial ]->GetString( pszTextureField, NULL );
  741. #endif
  742. DebugCombineMsg( "Material: %d ( %s ) Width=%d, Height=%d\n", nMaterial, szTextureNames[ nMaterial ], m_pVTFFileHeader[ nMaterial ]->width, m_pVTFFileHeader[ nMaterial ]->height );
  743. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->AddTexture( nMaterial, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->width, m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->height );
  744. }
  745. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->Resolve();
  746. }
  747. else
  748. {
  749. // need to validate that the all the "other" textures in the material are the same ratio of the base
  750. // 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
  751. bool bGotShifts = false;
  752. for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
  753. {
  754. if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
  755. {
  756. continue;
  757. }
  758. int nWriteWidth, nWriteHeight;
  759. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWriteWidth, nWriteHeight );
  760. nWriteWidth >>= 2; // 2 accounts for the DDS block encoding
  761. nWriteHeight >>= 2; // 2 accounts for the DDS block encoding
  762. if ( !bGotShifts )
  763. {
  764. int nWidth = nWriteWidth;
  765. int nHeight = nWriteHeight;
  766. while ( nMipWidth[ nMaterial ][ 0 ] < nWidth )
  767. {
  768. nWidthShift++;
  769. nWidth >>= 1;
  770. }
  771. while ( nMipWidth[ nMaterial ][ 0 ] > nWidth )
  772. {
  773. nWidthShift--;
  774. nWidth <<= 1;
  775. }
  776. while ( nMipHeight[ nMaterial ][ 0 ] < nHeight )
  777. {
  778. nHeightShift++;
  779. nHeight >>= 1;
  780. }
  781. while ( nMipHeight[ nMaterial ][ 0 ] > nHeight )
  782. {
  783. nHeightShift--;
  784. nHeight <<= 1;
  785. }
  786. bGotShifts = true;
  787. }
  788. nWriteWidth = (nWidthShift > 0) ? nWriteWidth >> nWidthShift : nWriteWidth << (-nWidthShift);
  789. nWriteHeight = (nHeightShift > 0) ? nWriteHeight >> nHeightShift : nWriteHeight << (-nHeightShift);
  790. if ( nMipWidth[ nMaterial ][ 0 ] != nWriteWidth || nMipHeight[ nMaterial ][ 0 ] != nWriteHeight )
  791. {
  792. Assert( 0 );
  793. V_sprintf_safe( m_pCombinedStudioData->m_Results.m_szErrorMessage, "texture '%s' size ( %d, %d ) differs expected size ( %d, %d )",
  794. szTextureNames[ nMaterial ], nMipWidth[ nMaterial ][ 0 ] << 2, nMipHeight[ nMaterial ][ 0 ] << 2, nWriteWidth << 2, nWriteHeight << 2 );
  795. throw( COMBINE_RESULT_FLAG_UNHANDLED_ISSUE );
  796. }
  797. }
  798. }
  799. byte *pPtr = m_AtlasGroups[ nAtlasGroup ].m_CombinedTextureMemory[ nTexture ];
  800. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] = ( VTFFileHeader_t * )pPtr;
  801. pPtr += sizeof( VTFFileHeader_t );
  802. memset( m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ], 0, sizeof( *m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] ) );
  803. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->numResources = 1;
  804. ResourceEntryInfo *pResource = ( ResourceEntryInfo * )pPtr;
  805. pPtr += sizeof( ResourceEntryInfo );
  806. // align the dds data to a 16 byte boundary
  807. pPtr = ( byte * )( ( ( uintp )( pPtr + 15 ) ) & ( ~16 ) );
  808. pResource->eType = VTF_LEGACY_RSRC_IMAGE;
  809. pResource->resData = pPtr - ( byte * )m_AtlasGroups[ nAtlasGroup ].m_CombinedTextureMemory[ nTexture ];
  810. int nMaxSize = ( MAX_COMBINED_WIDTH > MAX_COMBINED_HEIGHT ? MAX_COMBINED_WIDTH : MAX_COMBINED_HEIGHT );
  811. int nNumMips = 0;
  812. while( nMaxSize > 0 )
  813. {
  814. nNumMips++;
  815. nMaxSize >>= 1;
  816. }
  817. Q_strncpy( m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->fileTypeString, "VTF", 4 );
  818. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->version[ 0 ] = VTF_MAJOR_VERSION;
  819. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->version[ 1 ] = VTF_MINOR_VERSION;
  820. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->headerSize = pPtr - ( ( byte * )m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] );
  821. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->imageFormat = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->imageFormat;
  822. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->width = MAX_COMBINED_WIDTH;
  823. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->height = MAX_COMBINED_HEIGHT;
  824. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->numMipLevels = nNumMips;
  825. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->flags = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->flags | TEXTUREFLAGS_COMBINED;
  826. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->numFrames = 1;
  827. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->startFrame = 0;
  828. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->depth = 1;
  829. // potential alignment issues
  830. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->reflectivity.x = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->reflectivity.x;
  831. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->reflectivity.y = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->reflectivity.y;
  832. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->reflectivity.z = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->reflectivity.z;
  833. m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->bumpScale = m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ 0 ]->bumpScale;
  834. byte *pCombinedMipOffset[ MAX_COMBINED_MIP_LEVELS ];
  835. int nCombinedMipWidth[ MAX_COMBINED_MIP_LEVELS ];
  836. int nCombinedMipHeight[ MAX_COMBINED_MIP_LEVELS ];
  837. int nCombinedMipSize[ MAX_COMBINED_MIP_LEVELS ];
  838. for( int nMip = nNumMips - 1; nMip >= 0; nMip-- )
  839. {
  840. pCombinedMipOffset[ nMip ] = pPtr;
  841. int nWidth = m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->width >> nMip;
  842. int nHeight = m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ]->height >> nMip;
  843. if ( nWidth < 4 )
  844. {
  845. nWidth = 4;
  846. }
  847. if ( nHeight < 4 )
  848. {
  849. nHeight = 4;
  850. }
  851. nWidth >>= 2;
  852. nHeight >>= 2;
  853. int nNumBlocks = ( nWidth * nHeight );
  854. nCombinedMipWidth[ nMip ] = nWidth;
  855. nCombinedMipHeight[ nMip ] = nHeight;
  856. nCombinedMipSize[ nMip ] = nNumBlocks * nBlockSize;
  857. pPtr += nCombinedMipSize[ nMip ];
  858. }
  859. for( int nMip = 0; nMip < nNumMips; nMip++ )
  860. {
  861. int nCombinedLineSize = ( nCombinedMipWidth[ nMip ] ) * nBlockSize;
  862. for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
  863. {
  864. if ( !m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] && nMip >= m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ]->numMipLevels )
  865. {
  866. continue;
  867. }
  868. int nNewX, nNewY, nWriteWidth, nWriteHeight;
  869. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWriteWidth, nWriteHeight );
  870. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureLocation( nMaterial, nNewX, nNewY );
  871. // adjust nWriteWidth & nWriteHeight by the ratio for this texture
  872. nWriteWidth = (nWidthShift > 0) ? nWriteWidth >> nWidthShift : nWriteWidth << (-nWidthShift);
  873. nWriteHeight = (nHeightShift > 0) ? nWriteHeight >> nHeightShift : nWriteHeight << (-nHeightShift);
  874. nWriteWidth >>= ( 2 + nMip ); // 2 accounts for the DDS block encoding
  875. if ( nWriteWidth <= 0 )
  876. {
  877. if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
  878. { // we don't need to replicate down any further
  879. continue;
  880. }
  881. nWriteWidth = 1;
  882. }
  883. nWriteHeight >>= ( 2 + nMip ); // 2 accounts for the DDS block encoding
  884. if ( nWriteHeight <= 0 )
  885. {
  886. if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
  887. { // we don't need to replicate down any further
  888. continue;
  889. }
  890. nWriteHeight = 1;
  891. }
  892. nNewX >>= nMip;
  893. nNewY >>= nMip;
  894. byte *pWriteOffset = pCombinedMipOffset[ nMip ];
  895. pWriteOffset += ( nNewX >> 2 ) * nBlockSize;
  896. pWriteOffset += ( nNewY >> 2 ) * nCombinedLineSize;
  897. if ( m_AtlasGroups[ nAtlasGroup ].m_bIsFlat[ nMaterial ] )
  898. {
  899. /*
  900. optimize below to use
  901. _mm_stream_ps
  902. */
  903. int nCombinedLineSizeDelta = nCombinedLineSize - ( nWriteWidth * nBlockSize );
  904. byte *pReadOffset = pMipOffset[ nMaterial ][ 0 ];
  905. for( int y = 0; y < nWriteHeight; y++ )
  906. {
  907. for( int x = 0; x < nWriteWidth; x++, pWriteOffset += nBlockSize )
  908. {
  909. memcpy( pWriteOffset, pReadOffset, nBlockSize );
  910. }
  911. pWriteOffset += nCombinedLineSizeDelta;
  912. }
  913. }
  914. else
  915. {
  916. int nReadWidth = nMipWidth[ nMaterial ][ nMip ];
  917. Assert( nWriteWidth == nReadWidth );
  918. Assert( nWriteHeight == nMipHeight[ nMaterial ][ nMip ] );
  919. int nReadSize = nReadWidth * nBlockSize;
  920. byte *pReadOffset = pMipOffset[ nMaterial ][ nMip ];
  921. for( int y = 0; y < nWriteHeight; y++ )
  922. {
  923. /* byte *pReadCache = pReadOffset;
  924. int nReadCacheSize = nReadSize;
  925. while ( nReadCacheSize >= 0 )
  926. {
  927. PREFETCH_128( pReadCache, nReadCacheSize );
  928. nReadCacheSize -= CACHE_LINE_SIZE;
  929. }
  930. */
  931. memcpy( pWriteOffset, pReadOffset, nReadSize );
  932. pReadOffset += nReadSize;
  933. pWriteOffset += nCombinedLineSize;
  934. }
  935. }
  936. }
  937. }
  938. m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSize[ nTexture ] = pPtr - ( ( byte * )m_AtlasGroups[ nAtlasGroup ].m_CombinedHeaders[ nTexture ] );
  939. // FileHandle_t fh = g_pFullFileSystem->Open( "rjtest.vtf", "wb" );
  940. // g_pFullFileSystem->Write( m_CombinedHeaders[ nTexture ], m_nCombinedTextureSize[ nTexture ], fh );
  941. // g_pFullFileSystem->Close( fh );
  942. for( int nMaterial = 0; nMaterial < m_AtlasGroups[ nAtlasGroup ].m_nNumMaterials; nMaterial++ )
  943. {
  944. delete m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ];
  945. m_AtlasGroups[ nAtlasGroup ].m_pVTFData[ nMaterial ] = NULL;
  946. m_AtlasGroups[ nAtlasGroup ].m_pVTFFileHeader[ nMaterial ] = NULL;
  947. }
  948. m_pCombinedStudioData->m_Results.m_flTextureCombineDuration += ( float )( Plat_FloatTime() - flStartCombineTime );
  949. return true;
  950. }
  951. void CTextureCombine::GetTextureInfo( int nIndex, Vector2D &vStartST, Vector2D &vSizeST, Vector2D &vPixelSize )
  952. {
  953. int nAtlasGroup = m_nMaterialAtlasInfo[ nIndex ][ ATLAS_INFO_GROUP_INDEX ];
  954. int nMaterial = m_nMaterialAtlasInfo[ nIndex ][ ATLAS_INFO_MATERIAL_INDEX ];
  955. int nStartS, nStartT;
  956. int nWidth, nHeight;
  957. if ( m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker )
  958. {
  959. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureSize( nMaterial, nWidth, nHeight );
  960. m_AtlasGroups[ nAtlasGroup ].m_pSimpleTexturePacker->GetTextureLocation( nMaterial, nStartS, nStartT );
  961. vStartST.x = ( float )nStartS / ( float )MAX_COMBINED_WIDTH;
  962. vStartST.y = ( float )nStartT / ( float )MAX_COMBINED_HEIGHT;
  963. vSizeST.x = ( float )nWidth / ( float )MAX_COMBINED_WIDTH;
  964. vSizeST.y = ( float )nHeight / ( float )MAX_COMBINED_HEIGHT;
  965. vPixelSize.x = 1.0f / ( float )nWidth;
  966. vPixelSize.y = 1.0f / ( float )nHeight;
  967. }
  968. else
  969. {
  970. vStartST.x = 0.0f;
  971. vStartST.y = 0.0f;
  972. vSizeST.x = 1.0f;
  973. vSizeST.y = 1.0f;
  974. vPixelSize.x = 0.0f;
  975. vPixelSize.y = 0.0f;
  976. }
  977. }