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.

3649 lines
102 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The VTF file format I/O class to help simplify access to VTF files
  4. //
  5. //=====================================================================================//
  6. #undef fopen
  7. #include "bitmap/imageformat.h"
  8. #include "cvtf.h"
  9. #include "utlbuffer.h"
  10. #include "tier0/dbg.h"
  11. #include "mathlib/vector.h"
  12. #include "mathlib/mathlib.h"
  13. #include "tier1/strtools.h"
  14. #include "tier0/mem.h"
  15. #include "s3tc_decode.h"
  16. #include "utlvector.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. // byteswap data descriptions
  20. BEGIN_BYTESWAP_DATADESC( VTFFileBaseHeader_t )
  21. DEFINE_ARRAY( fileTypeString, FIELD_CHARACTER, 4 ),
  22. DEFINE_ARRAY( version, FIELD_INTEGER, 2 ),
  23. DEFINE_FIELD( headerSize, FIELD_INTEGER ),
  24. END_DATADESC()
  25. BEGIN_BYTESWAP_DATADESC_( VTFFileHeaderV7_1_t, VTFFileBaseHeader_t )
  26. DEFINE_FIELD( width, FIELD_SHORT ),
  27. DEFINE_FIELD( height, FIELD_SHORT ),
  28. DEFINE_FIELD( flags, FIELD_INTEGER ),
  29. DEFINE_FIELD( numFrames, FIELD_SHORT ),
  30. DEFINE_FIELD( startFrame, FIELD_SHORT ),
  31. DEFINE_FIELD( reflectivity, FIELD_VECTOR ),
  32. DEFINE_FIELD( bumpScale, FIELD_FLOAT ),
  33. DEFINE_FIELD( imageFormat, FIELD_INTEGER ),
  34. DEFINE_FIELD( numMipLevels, FIELD_CHARACTER ),
  35. DEFINE_FIELD( lowResImageFormat, FIELD_INTEGER ),
  36. DEFINE_FIELD( lowResImageWidth, FIELD_CHARACTER ),
  37. DEFINE_FIELD( lowResImageHeight, FIELD_CHARACTER ),
  38. END_DATADESC()
  39. BEGIN_BYTESWAP_DATADESC_( VTFFileHeaderV7_2_t, VTFFileHeaderV7_1_t )
  40. DEFINE_FIELD( depth, FIELD_SHORT ),
  41. END_DATADESC()
  42. BEGIN_BYTESWAP_DATADESC_( VTFFileHeaderV7_3_t, VTFFileHeaderV7_2_t )
  43. DEFINE_FIELD( numResources, FIELD_INTEGER ),
  44. END_DATADESC()
  45. BEGIN_BYTESWAP_DATADESC_( VTFFileHeader_t, VTFFileHeaderV7_2_t )
  46. END_DATADESC()
  47. BEGIN_BYTESWAP_DATADESC_( VTFFileHeaderX360_t, VTFFileBaseHeader_t )
  48. DEFINE_FIELD( flags, FIELD_INTEGER ),
  49. DEFINE_FIELD( width, FIELD_SHORT ),
  50. DEFINE_FIELD( height, FIELD_SHORT ),
  51. DEFINE_FIELD( depth, FIELD_SHORT ),
  52. DEFINE_FIELD( numFrames, FIELD_SHORT ),
  53. DEFINE_FIELD( preloadDataSize, FIELD_SHORT ),
  54. DEFINE_FIELD( mipSkipCount, FIELD_CHARACTER ),
  55. DEFINE_FIELD( numResources, FIELD_CHARACTER ),
  56. DEFINE_FIELD( reflectivity, FIELD_VECTOR ),
  57. DEFINE_FIELD( bumpScale, FIELD_FLOAT ),
  58. DEFINE_FIELD( imageFormat, FIELD_INTEGER ),
  59. DEFINE_ARRAY( lowResImageSample, FIELD_CHARACTER, 4 ),
  60. DEFINE_FIELD( compressedSize, FIELD_INTEGER ),
  61. END_DATADESC()
  62. BEGIN_BYTESWAP_DATADESC_( VTFFileHeaderPS3_t, VTFFileBaseHeader_t )
  63. DEFINE_FIELD( flags, FIELD_INTEGER ),
  64. DEFINE_FIELD( width, FIELD_SHORT ),
  65. DEFINE_FIELD( height, FIELD_SHORT ),
  66. DEFINE_FIELD( depth, FIELD_SHORT ),
  67. DEFINE_FIELD( numFrames, FIELD_SHORT ),
  68. DEFINE_FIELD( preloadDataSize, FIELD_SHORT ),
  69. DEFINE_FIELD( mipSkipCount, FIELD_CHARACTER ),
  70. DEFINE_FIELD( numResources, FIELD_CHARACTER ),
  71. DEFINE_FIELD( reflectivity, FIELD_VECTOR ),
  72. DEFINE_FIELD( bumpScale, FIELD_FLOAT ),
  73. DEFINE_FIELD( imageFormat, FIELD_INTEGER ),
  74. DEFINE_ARRAY( lowResImageSample, FIELD_CHARACTER, 4 ),
  75. DEFINE_FIELD( compressedSize, FIELD_INTEGER ),
  76. END_DATADESC()
  77. #if defined( POSIX ) || defined( _X360 )
  78. // stub functions
  79. const char* S3TC_GetBlock(
  80. const void *pCompressed,
  81. ImageFormat format,
  82. int nBlocksWide, // How many blocks wide is the image (pixels wide / 4).
  83. int xBlock,
  84. int yBlock )
  85. {
  86. return NULL;
  87. }
  88. char* S3TC_GetBlock(
  89. void *pCompressed,
  90. ImageFormat format,
  91. int nBlocksWide, // How many blocks wide is the image (pixels wide / 4).
  92. int xBlock,
  93. int yBlock )
  94. {
  95. return NULL;
  96. }
  97. S3PaletteIndex S3TC_GetPaletteIndex(
  98. unsigned char *pFaceData,
  99. ImageFormat format,
  100. int imageWidth,
  101. int x,
  102. int y )
  103. {
  104. S3PaletteIndex nullPalette;
  105. memset(&nullPalette, 0x0, sizeof(nullPalette));
  106. return nullPalette;
  107. }
  108. // Merge the two palettes and copy the colors
  109. void S3TC_MergeBlocks(
  110. char **blocks,
  111. S3RGBA **pOriginals,
  112. int nBlocks,
  113. int lPitch, // (in BYTES)
  114. ImageFormat format
  115. )
  116. {
  117. }
  118. // Note: width, x, and y are in texels, not S3 blocks.
  119. void S3TC_SetPaletteIndex(
  120. unsigned char *pFaceData,
  121. ImageFormat format,
  122. int imageWidth,
  123. int x,
  124. int y,
  125. S3PaletteIndex paletteIndex )
  126. {
  127. }
  128. #endif
  129. // This gives a vertex number to each of the 4 verts on each face.
  130. // We use this to match the verts and determine which edges need to be blended together.
  131. // The vert ordering is lower-left, top-left, top-right, bottom-right.
  132. int g_leftFaceVerts[4] = { 2, 6, 7, 3 };
  133. int g_frontFaceVerts[4] = { 2, 3, 5, 4 };
  134. int g_downFaceVerts[4] = { 4, 0, 6, 2 };
  135. int g_rightFaceVerts[4] = { 5, 1, 0, 4 };
  136. int g_backFaceVerts[4] = { 7, 6, 0, 1 };
  137. int g_upFaceVerts[4] = { 3, 7, 1, 5 };
  138. int *g_FaceVerts[6] =
  139. {
  140. g_rightFaceVerts,
  141. g_leftFaceVerts,
  142. g_backFaceVerts,
  143. g_frontFaceVerts,
  144. g_upFaceVerts,
  145. g_downFaceVerts
  146. };
  147. // For skyboxes..
  148. // These were constructed for the engine skybox, which looks like this
  149. // (assuming X goes forward, Y goes left, and Z goes up).
  150. //
  151. // 6 ------------- 5
  152. // / /
  153. // / | / |
  154. // / | / |
  155. // 2 ------------- 1 |
  156. // | |
  157. // | |
  158. // | 7 ------|------ 4
  159. // | / | /
  160. // | / | /
  161. // / /
  162. // 3 ------------- 0
  163. //
  164. int g_skybox_rightFaceVerts[4] = { 7, 6, 5, 4 };
  165. int g_skybox_leftFaceVerts[4] = { 0, 1, 2, 3 };
  166. int g_skybox_backFaceVerts[4] = { 3, 2, 6, 7 };
  167. int g_skybox_frontFaceVerts[4] = { 4, 5, 1, 0 };
  168. int g_skybox_upFaceVerts[4] = { 6, 2, 1, 5 };
  169. int g_skybox_downFaceVerts[4] = { 3, 7, 4, 0 };
  170. int *g_skybox_FaceVerts[6] =
  171. {
  172. g_skybox_rightFaceVerts,
  173. g_skybox_leftFaceVerts,
  174. g_skybox_backFaceVerts,
  175. g_skybox_frontFaceVerts,
  176. g_skybox_upFaceVerts,
  177. g_skybox_downFaceVerts
  178. };
  179. //-----------------------------------------------------------------------------
  180. // Class factory
  181. //-----------------------------------------------------------------------------
  182. IVTFTexture *CreateVTFTexture()
  183. {
  184. return new CVTFTexture;
  185. }
  186. void DestroyVTFTexture( IVTFTexture *pTexture )
  187. {
  188. CVTFTexture *pTex = static_cast<CVTFTexture*>(pTexture);
  189. if ( pTex )
  190. {
  191. delete pTex;
  192. }
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Allows us to only load in the first little bit of the VTF file to get info
  196. //-----------------------------------------------------------------------------
  197. int VTFFileHeaderSize( int nMajorVersion, int nMinorVersion )
  198. {
  199. if ( nMajorVersion == -1 )
  200. {
  201. nMajorVersion = VTF_MAJOR_VERSION;
  202. }
  203. if ( nMinorVersion == -1 )
  204. {
  205. nMinorVersion = VTF_MINOR_VERSION;
  206. }
  207. switch ( nMajorVersion )
  208. {
  209. case VTF_MAJOR_VERSION:
  210. switch ( nMinorVersion )
  211. {
  212. case 0: // fall through
  213. case 1:
  214. return sizeof( VTFFileHeaderV7_1_t );
  215. case 2:
  216. return sizeof( VTFFileHeaderV7_2_t );
  217. case 3:
  218. return sizeof( VTFFileHeaderV7_3_t ) + sizeof( ResourceEntryInfo ) * MAX_RSRC_DICTIONARY_ENTRIES;
  219. case 4:
  220. case VTF_MINOR_VERSION:
  221. int size1 = sizeof( VTFFileHeader_t );
  222. int size2 = sizeof( ResourceEntryInfo ) * MAX_RSRC_DICTIONARY_ENTRIES;
  223. int result = size1 + size2;
  224. //printf("\n VTFFileHeaderSize (%i %i) is %i + %i -> %i",nMajorVersion,nMinorVersion, size1, size2, result );
  225. return result;
  226. }
  227. break;
  228. case VTF_X360_MAJOR_VERSION:
  229. return sizeof( VTFFileHeaderX360_t ) + sizeof( ResourceEntryInfo ) * MAX_X360_RSRC_DICTIONARY_ENTRIES;
  230. case VTF_PS3_MAJOR_VERSION:
  231. return sizeof( VTFFileHeaderPS3_t ) + sizeof( ResourceEntryInfo ) * MAX_X360_RSRC_DICTIONARY_ENTRIES;
  232. }
  233. return 0;
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Constructor, destructor
  237. //-----------------------------------------------------------------------------
  238. CVTFTexture::CVTFTexture()
  239. {
  240. m_nVersion[0] = 0;
  241. m_nVersion[1] = 0;
  242. m_nWidth = 0;
  243. m_nHeight = 0;
  244. m_nDepth = 1;
  245. m_Format = IMAGE_FORMAT_UNKNOWN;
  246. m_nMipCount = 0;
  247. m_nFaceCount = 0;
  248. m_nFrameCount = 0;
  249. // FIXME: Is the start frame needed?
  250. m_iStartFrame = 0;
  251. m_flAlphaThreshhold = -1.0f;
  252. m_flAlphaHiFreqThreshhold = -1.0f;
  253. m_flBumpScale = 1.0f;
  254. m_vecReflectivity.Init( 1.0, 1.0, 1.0f );
  255. m_nFlags = 0;
  256. m_pImageData = NULL;
  257. m_nImageAllocSize = 0;
  258. // LowRes data
  259. m_LowResImageFormat = IMAGE_FORMAT_UNKNOWN;
  260. m_nLowResImageWidth = 0;
  261. m_nLowResImageHeight = 0;
  262. m_pLowResImageData = NULL;
  263. m_nLowResImageAllocSize = 0;
  264. #if defined( _X360 ) || defined ( _PS3 )
  265. m_nMipSkipCount = 0;
  266. *(unsigned int *)m_LowResImageSample = 0;
  267. #endif
  268. Assert( m_arrResourcesInfo.Count() == 0 );
  269. Assert( m_arrResourcesData.Count() == 0 );
  270. Assert( m_arrResourcesData_ForReuse.Count() == 0 );
  271. memset( &m_Options, 0, sizeof( m_Options ) );
  272. m_Options.cbSize = sizeof( m_Options );
  273. }
  274. CVTFTexture::~CVTFTexture()
  275. {
  276. Shutdown();
  277. }
  278. #ifndef PLATFORM_X360
  279. bool CVTFTexture::IsPreTiled() const
  280. {
  281. return false;
  282. }
  283. #endif
  284. //-----------------------------------------------------------------------------
  285. // Compute the mip count based on the size + flags
  286. //-----------------------------------------------------------------------------
  287. int CVTFTexture::ComputeMipCount() const
  288. {
  289. if ( IsX360() && ( m_nVersion[0] == VTF_X360_MAJOR_VERSION ) && ( m_nFlags & TEXTUREFLAGS_NOMIP ) )
  290. {
  291. // 360 vtf format culled unused mips at conversion time
  292. return 1;
  293. }
  294. if ( IsPS3() && ( m_nVersion[0] == VTF_PS3_MAJOR_VERSION ) && ( m_nFlags & TEXTUREFLAGS_NOMIP ) )
  295. {
  296. // PS3 vtf format culled unused mips at conversion time
  297. return 1;
  298. }
  299. // NOTE: No matter what, all mip levels should be created because
  300. // we have to worry about various fallbacks
  301. return ImageLoader::GetNumMipMapLevels( m_nWidth, m_nHeight, m_nDepth );
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Allocate data blocks with an eye toward re-using memory
  305. //-----------------------------------------------------------------------------
  306. static bool GenericAllocateReusableData( unsigned char **ppData, int *pNumAllocated, int numRequested )
  307. {
  308. // If we're asking for memory and we have way more than we expect, free some.
  309. if ( *pNumAllocated < numRequested || ( numRequested > 0 && *pNumAllocated > 16 * numRequested ) )
  310. {
  311. delete [] *ppData;
  312. *ppData = new unsigned char[ numRequested ];
  313. if ( *ppData )
  314. {
  315. *pNumAllocated = numRequested;
  316. return true;
  317. }
  318. *pNumAllocated = 0;
  319. return false;
  320. }
  321. return true;
  322. }
  323. bool CVTFTexture::AllocateImageData( int nMemorySize )
  324. {
  325. return GenericAllocateReusableData( &m_pImageData, &m_nImageAllocSize, nMemorySize );
  326. }
  327. bool CVTFTexture::ResourceMemorySection::AllocateData( int nMemorySize )
  328. {
  329. if ( GenericAllocateReusableData( &m_pData, &m_nDataAllocSize, nMemorySize ) )
  330. {
  331. m_nDataLength = nMemorySize;
  332. return true;
  333. }
  334. return false;
  335. }
  336. bool CVTFTexture::AllocateLowResImageData( int nMemorySize )
  337. {
  338. return GenericAllocateReusableData( &m_pLowResImageData, &m_nLowResImageAllocSize, nMemorySize );
  339. }
  340. inline bool IsMultipleOf4( int value )
  341. {
  342. // NOTE: This catches powers of 2 less than 4 also
  343. return ( value <= 2 ) || ( (value & 0x3) == 0 );
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Initialization
  347. //-----------------------------------------------------------------------------
  348. bool CVTFTexture::Init( int nWidth, int nHeight, int nDepth, ImageFormat fmt, int iFlags, int iFrameCount, int nForceMipCount )
  349. {
  350. if ( nDepth == 0 )
  351. {
  352. nDepth = 1;
  353. }
  354. if (iFlags & TEXTUREFLAGS_ENVMAP)
  355. {
  356. if (nWidth != nHeight)
  357. {
  358. Warning( "Height and width must be equal for cubemaps!\n" );
  359. return false;
  360. }
  361. if (nDepth != 1)
  362. {
  363. Warning( "Depth must be 1 for cubemaps!\n" );
  364. return false;
  365. }
  366. }
  367. if ( ( fmt == IMAGE_FORMAT_DXT1 ) || ( fmt == IMAGE_FORMAT_DXT3 ) || ( fmt == IMAGE_FORMAT_DXT5 ) ||
  368. ( fmt == IMAGE_FORMAT_DXT1_RUNTIME ) || ( fmt == IMAGE_FORMAT_DXT5_RUNTIME ) )
  369. {
  370. if ( !IsMultipleOf4( nWidth ) || !IsMultipleOf4( nHeight ) || !IsMultipleOf4( nDepth ) )
  371. {
  372. Warning( "Image dimensions must be multiple of 4!\n" );
  373. return false;
  374. }
  375. }
  376. if ( fmt == IMAGE_FORMAT_DEFAULT )
  377. {
  378. fmt = IMAGE_FORMAT_RGBA8888;
  379. }
  380. m_nWidth = nWidth;
  381. m_nHeight = nHeight;
  382. m_nDepth = nDepth;
  383. m_Format = fmt;
  384. m_nFlags = iFlags;
  385. // THIS CAUSED A BUG!!! We want all of the mip levels in the vtf file even with nomip in case we have lod.
  386. // NOTE: But we don't want more than 1 mip level for procedural textures
  387. if ( (iFlags & (TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_PROCEDURAL)) == (TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_PROCEDURAL) )
  388. {
  389. nForceMipCount = 1;
  390. }
  391. if ( nForceMipCount == -1 )
  392. {
  393. m_nMipCount = ComputeMipCount();
  394. }
  395. else
  396. {
  397. m_nMipCount = nForceMipCount;
  398. }
  399. m_nFrameCount = iFrameCount;
  400. m_nFaceCount = (iFlags & TEXTUREFLAGS_ENVMAP) ? CUBEMAP_FACE_COUNT : 1;
  401. #if defined( _X360 ) || defined ( _PS3 )
  402. m_nMipSkipCount = 0;
  403. #endif
  404. // Need to do this because Shutdown deallocates the low-res image
  405. m_nLowResImageWidth = m_nLowResImageHeight = 0;
  406. // Allocate me some bits!
  407. int iMemorySize = ComputeTotalSize();
  408. if ( !AllocateImageData( iMemorySize ) )
  409. return false;
  410. // As soon as we have image indicate so in the resources
  411. if ( iMemorySize )
  412. FindOrCreateResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  413. else
  414. RemoveResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  415. return true;
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Methods to initialize the low-res image
  419. //-----------------------------------------------------------------------------
  420. void CVTFTexture::InitLowResImage( int nWidth, int nHeight, ImageFormat fmt )
  421. {
  422. m_nLowResImageWidth = nWidth;
  423. m_nLowResImageHeight = nHeight;
  424. m_LowResImageFormat = fmt;
  425. // Allocate low-res bits
  426. int iLowResImageSize = ImageLoader::GetMemRequired( m_nLowResImageWidth,
  427. m_nLowResImageHeight, 1, m_LowResImageFormat, false );
  428. if ( !AllocateLowResImageData( iLowResImageSize ) )
  429. return;
  430. // As soon as we have low-res image indicate so in the resources
  431. if ( iLowResImageSize )
  432. FindOrCreateResourceEntryInfo( VTF_LEGACY_RSRC_LOW_RES_IMAGE );
  433. else
  434. RemoveResourceEntryInfo( VTF_LEGACY_RSRC_LOW_RES_IMAGE );
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Methods to set other texture fields
  438. //-----------------------------------------------------------------------------
  439. void CVTFTexture::SetBumpScale( float flScale )
  440. {
  441. m_flBumpScale = flScale;
  442. }
  443. void CVTFTexture::SetReflectivity( const Vector &vecReflectivity )
  444. {
  445. VectorCopy( vecReflectivity, m_vecReflectivity );
  446. }
  447. // Sets threshhold values for alphatest mipmapping
  448. void CVTFTexture::SetAlphaTestThreshholds( float flBase, float flHighFreq )
  449. {
  450. m_flAlphaThreshhold = flBase;
  451. m_flAlphaHiFreqThreshhold = flHighFreq;
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Release and reset the resources.
  455. //-----------------------------------------------------------------------------
  456. void CVTFTexture::ReleaseResources()
  457. {
  458. m_arrResourcesInfo.RemoveAll();
  459. for ( ResourceMemorySection *pRms = m_arrResourcesData.Base(),
  460. *pRmsEnd = pRms + m_arrResourcesData.Count(); pRms < pRmsEnd; ++pRms )
  461. {
  462. delete [] pRms->m_pData;
  463. }
  464. m_arrResourcesData.RemoveAll();
  465. for ( ResourceMemorySection *pRms = m_arrResourcesData_ForReuse.Base(),
  466. *pRmsEnd = pRms + m_arrResourcesData_ForReuse.Count(); pRms < pRmsEnd; ++pRms )
  467. {
  468. delete [] pRms->m_pData;
  469. }
  470. m_arrResourcesData_ForReuse.RemoveAll();
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Shutdown
  474. //-----------------------------------------------------------------------------
  475. void CVTFTexture::Shutdown()
  476. {
  477. #if defined( _GAMECONSOLE )
  478. // must be first to ensure X360/PS3 aliased pointers are unhooked, otherwise memory corruption
  479. ReleaseImageMemory();
  480. #endif
  481. delete[] m_pImageData;
  482. m_pImageData = NULL;
  483. m_nImageAllocSize = 0;
  484. delete[] m_pLowResImageData;
  485. m_pLowResImageData = NULL;
  486. m_nLowResImageAllocSize = 0;
  487. ReleaseResources();
  488. }
  489. //-----------------------------------------------------------------------------
  490. // These are methods to help with optimization of file access
  491. //-----------------------------------------------------------------------------
  492. void CVTFTexture::LowResFileInfo( int *pStartLocation, int *pSizeInBytes ) const
  493. {
  494. // Once the header is read in, they indicate where to start reading
  495. // other data, and how many bytes to read....
  496. if ( ResourceEntryInfo const *pLowResData = FindResourceEntryInfo( VTF_LEGACY_RSRC_LOW_RES_IMAGE ) )
  497. {
  498. *pStartLocation = pLowResData->resData;
  499. *pSizeInBytes = ImageLoader::GetMemRequired( m_nLowResImageWidth,
  500. m_nLowResImageHeight, 1, m_LowResImageFormat, false );
  501. }
  502. else
  503. {
  504. *pStartLocation = 0;
  505. *pSizeInBytes = 0;
  506. }
  507. }
  508. void CVTFTexture::ImageFileInfo( int nFrame, int nFace, int nMipLevel, int *pStartLocation, int *pSizeInBytes) const
  509. {
  510. int i;
  511. int iMipWidth;
  512. int iMipHeight;
  513. int iMipDepth;
  514. ResourceEntryInfo const *pImageDataInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  515. if ( pImageDataInfo == NULL )
  516. {
  517. // This should never happen for real, but can happen if someone intentionally fed us a bad VTF.
  518. Assert( pImageDataInfo );
  519. ( *pStartLocation ) = 0;
  520. ( *pSizeInBytes ) = 0;
  521. return;
  522. }
  523. // The image data start offset
  524. int nOffset = pImageDataInfo->resData;
  525. // get to the right miplevel
  526. for( i = m_nMipCount - 1; i > nMipLevel; --i )
  527. {
  528. ComputeMipLevelDimensions( i, &iMipWidth, &iMipHeight, &iMipDepth );
  529. int iMipLevelSize = ImageLoader::GetMemRequired( iMipWidth, iMipHeight, iMipDepth, m_Format, false );
  530. nOffset += iMipLevelSize * m_nFrameCount * m_nFaceCount;
  531. }
  532. // get to the right frame
  533. ComputeMipLevelDimensions( nMipLevel, &iMipWidth, &iMipHeight, &iMipDepth );
  534. int nFaceSize = ImageLoader::GetMemRequired( iMipWidth, iMipHeight, iMipDepth, m_Format, false );
  535. // For backwards compatibility, we don't read in the spheremap fallback on
  536. // older format .VTF files...
  537. int nFacesToRead = m_nFaceCount;
  538. if ( IsCubeMap() )
  539. {
  540. if ((m_nVersion[0] == 7) && (m_nVersion[1] < 1))
  541. {
  542. nFacesToRead = 6;
  543. if (nFace == CUBEMAP_FACE_SPHEREMAP)
  544. {
  545. --nFace;
  546. }
  547. }
  548. }
  549. int nFrameSize = nFacesToRead * nFaceSize;
  550. nOffset += nFrameSize * nFrame;
  551. // get to the right face
  552. nOffset += nFace * nFaceSize;
  553. *pStartLocation = nOffset;
  554. *pSizeInBytes = nFaceSize;
  555. }
  556. int CVTFTexture::FileSize( int nMipSkipCount ) const
  557. {
  558. ResourceEntryInfo const *pImageDataInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  559. // Can be null when someone gives us an intentionally malformed VTF.
  560. if ( pImageDataInfo == NULL )
  561. {
  562. // Still do the assert so we can catch this in debug--we don't expect this for well formed files.
  563. Assert( pImageDataInfo != NULL );
  564. return 0;
  565. }
  566. int nOffset = pImageDataInfo->resData;
  567. int nFaceSize = ComputeFaceSize( nMipSkipCount );
  568. int nImageSize = nFaceSize * m_nFaceCount * m_nFrameCount;
  569. return nOffset + nImageSize;
  570. }
  571. //-----------------------------------------------------------------------------
  572. // Unserialization of low-res data
  573. //-----------------------------------------------------------------------------
  574. bool CVTFTexture::LoadLowResData( CUtlBuffer &buf )
  575. {
  576. // Allocate low-res bits
  577. InitLowResImage( m_nLowResImageWidth, m_nLowResImageHeight, m_LowResImageFormat );
  578. int nLowResImageSize = ImageLoader::GetMemRequired( m_nLowResImageWidth,
  579. m_nLowResImageHeight, 1, m_LowResImageFormat, false );
  580. buf.Get( m_pLowResImageData, nLowResImageSize );
  581. bool bValid = buf.IsValid();
  582. return bValid;
  583. }
  584. //-----------------------------------------------------------------------------
  585. // Unserialization of image data
  586. //-----------------------------------------------------------------------------
  587. bool CVTFTexture::LoadImageData( CUtlBuffer &buf, const VTFFileHeader_t &header, int nSkipMipLevels )
  588. {
  589. // Fix up the mip count + size based on how many mip levels we skip...
  590. if (nSkipMipLevels > 0)
  591. {
  592. Assert( m_nMipCount > nSkipMipLevels );
  593. if (header.numMipLevels < nSkipMipLevels)
  594. {
  595. // NOTE: This can only happen with older format .vtf files
  596. Warning("Warning! Encountered old format VTF file; please rebuild it!\n");
  597. return false;
  598. }
  599. ComputeMipLevelDimensions( nSkipMipLevels, &m_nWidth, &m_nHeight, &m_nDepth );
  600. m_nMipCount -= nSkipMipLevels;
  601. }
  602. // read the texture image (including mipmaps if they are there and needed.)
  603. int iImageSize = ComputeFaceSize();
  604. iImageSize *= m_nFaceCount * m_nFrameCount;
  605. // For backwards compatibility, we don't read in the spheremap fallback on
  606. // older format .VTF files...
  607. if ( !AllocateImageData( iImageSize ) )
  608. return false;
  609. // NOTE: The mip levels are stored ascending from smallest (1x1) to largest (NxN)
  610. // in order to allow for truncated reads of the minimal required data
  611. // NOTE: I checked in a bad version 4 where it stripped out the spheremap.
  612. // To make it all work, need to check for that bad case.
  613. bool bNoSkip = false;
  614. if ( IsCubeMap() && ( header.version[0] == 7 ) && ( header.version[1] == 4 ) )
  615. {
  616. int nBytesRemaining = buf.TellMaxPut() - buf.TellGet();
  617. int nFileSize = ComputeFaceSize( nSkipMipLevels ) * m_nFaceCount * m_nFrameCount;
  618. if ( nBytesRemaining == nFileSize )
  619. {
  620. bNoSkip = true;
  621. }
  622. }
  623. int nGet = buf.TellGet();
  624. retryCubemapLoad:
  625. for (int iMip = m_nMipCount; --iMip >= 0; )
  626. {
  627. // NOTE: This is for older versions...
  628. if ( header.numMipLevels - nSkipMipLevels <= iMip )
  629. continue;
  630. int iMipSize = ComputeMipSize( iMip );
  631. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  632. {
  633. for (int iFace = 0; iFace < m_nFaceCount; ++iFace)
  634. {
  635. // printf("\n tex %p mip %i frame %i face %i size %i buf offset %i", this, iMip, iFrame, iFace, iMipSize, buf.TellGet() );
  636. unsigned char *pMipBits = ImageData( iFrame, iFace, iMip );
  637. buf.Get( pMipBits, iMipSize );
  638. }
  639. // Strip out the spheremap in older versions
  640. if ( IsCubeMap() && !bNoSkip && ( header.version[0] == 7 ) && ( header.version[1] >= 1 ) && ( header.version[1] < 5 ) )
  641. {
  642. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, iMipSize );
  643. }
  644. }
  645. }
  646. bool bOk = buf.IsValid();
  647. if ( !bOk && IsCubeMap() && ( header.version[0] == 7 ) && ( header.version[1] <= 4 ) )
  648. {
  649. if ( !bNoSkip )
  650. {
  651. bNoSkip = true;
  652. buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
  653. goto retryCubemapLoad;
  654. }
  655. Warning( "** Encountered stale cubemap! Please rebuild the following vtf:\n" );
  656. }
  657. return bOk;
  658. }
  659. void *CVTFTexture::SetResourceData( uint32 eType, void const *pData, size_t nNumBytes )
  660. {
  661. Assert( ( eType & RSRCF_MASK ) == 0 );
  662. eType &= ~RSRCF_MASK;
  663. // Very inefficient to set less than 4 bytes of data
  664. Assert( !nNumBytes || ( nNumBytes >= sizeof( uint32 ) ) );
  665. if ( nNumBytes )
  666. {
  667. ResourceEntryInfo *pInfo = FindOrCreateResourceEntryInfo( eType );
  668. int idx = pInfo - m_arrResourcesInfo.Base();
  669. ResourceMemorySection &rms = m_arrResourcesData[ idx ];
  670. if ( nNumBytes == sizeof( pInfo->resData ) )
  671. {
  672. // store 4 bytes directly
  673. pInfo->eType |= RSRCF_HAS_NO_DATA_CHUNK;
  674. if ( pData )
  675. pInfo->resData = reinterpret_cast< const int * >( pData )[0];
  676. return &pInfo->resData;
  677. }
  678. else
  679. {
  680. if ( !rms.AllocateData( nNumBytes ) )
  681. {
  682. RemoveResourceEntryInfo( eType );
  683. return NULL;
  684. }
  685. if ( pData )
  686. memcpy( rms.m_pData, pData, nNumBytes );
  687. return rms.m_pData;
  688. }
  689. }
  690. else
  691. {
  692. RemoveResourceEntryInfo( eType );
  693. return NULL;
  694. }
  695. }
  696. void *CVTFTexture::GetResourceData( uint32 eType, size_t *pDataSize ) const
  697. {
  698. Assert( ( eType & RSRCF_MASK ) == 0 );
  699. eType &= ~RSRCF_MASK;
  700. ResourceEntryInfo const *pInfo = FindResourceEntryInfo( eType );
  701. if ( pInfo )
  702. {
  703. if ( ( pInfo->eType & RSRCF_HAS_NO_DATA_CHUNK ) == 0 )
  704. {
  705. int idx = pInfo - m_arrResourcesInfo.Base();
  706. ResourceMemorySection const &rms = m_arrResourcesData[ idx ];
  707. if ( pDataSize )
  708. {
  709. *pDataSize = rms.m_nDataLength;
  710. }
  711. return rms.m_pData;
  712. }
  713. else
  714. {
  715. if ( pDataSize )
  716. {
  717. *pDataSize = sizeof( pInfo->resData );
  718. }
  719. return (void *)&pInfo->resData;
  720. }
  721. }
  722. else
  723. {
  724. if ( pDataSize )
  725. *pDataSize = 0;
  726. }
  727. return NULL;
  728. }
  729. bool CVTFTexture::HasResourceEntry( uint32 eType ) const
  730. {
  731. return ( FindResourceEntryInfo( eType ) != NULL );
  732. }
  733. unsigned int CVTFTexture::GetResourceTypes( unsigned int *arrTypesBuffer, int numTypesBufferElems ) const
  734. {
  735. for ( ResourceEntryInfo const *pInfo = m_arrResourcesInfo.Base(),
  736. *pInfoEnd = pInfo + m_arrResourcesInfo.Count();
  737. numTypesBufferElems-- > 0 && pInfo < pInfoEnd; )
  738. {
  739. *( arrTypesBuffer++ ) = ( ( pInfo++ )->eType & ~RSRCF_MASK );
  740. }
  741. return m_arrResourcesInfo.Count();
  742. }
  743. //-----------------------------------------------------------------------------
  744. // Serialization/Unserialization of resource data
  745. //-----------------------------------------------------------------------------
  746. bool CVTFTexture::ResourceMemorySection::LoadData( CUtlBuffer &buf, CByteswap &byteSwap )
  747. {
  748. // Read the size
  749. int iDataSize = 0;
  750. buf.Get( &iDataSize, sizeof( iDataSize ) );
  751. byteSwap.SwapBufferToTargetEndian( &iDataSize );
  752. // Read the actual data
  753. if ( !AllocateData( iDataSize ) )
  754. return false;
  755. buf.Get( m_pData, iDataSize );
  756. // Test valid
  757. bool bValid = buf.IsValid();
  758. return bValid;
  759. }
  760. bool CVTFTexture::ResourceMemorySection::WriteData( CUtlBuffer &buf ) const
  761. {
  762. Assert( m_nDataLength && m_pData );
  763. int iBufSize = m_nDataLength;
  764. buf.Put( &iBufSize, sizeof( iBufSize ) );
  765. buf.Put( m_pData, m_nDataLength );
  766. return buf.IsValid();
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Checks if the file data needs to be swapped
  770. //-----------------------------------------------------------------------------
  771. bool CVTFTexture::SetupByteSwap( CUtlBuffer &buf )
  772. {
  773. VTFFileBaseHeader_t *header = (VTFFileBaseHeader_t*)buf.PeekGet();
  774. if ( header->version[0] == SwapLong( VTF_MAJOR_VERSION ) )
  775. {
  776. m_Swap.ActivateByteSwapping( true );
  777. return true;
  778. }
  779. return false;
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Unserialization
  783. //-----------------------------------------------------------------------------
  784. static bool ReadHeaderFromBufferPastBaseHeader( CUtlBuffer &buf, VTFFileHeader_t &header )
  785. {
  786. unsigned char *pBuf = (unsigned char*)(&header) + sizeof(VTFFileBaseHeader_t);
  787. if ( header.version[1] <= VTF_MINOR_VERSION && header.version[1] >= 4 )
  788. {
  789. buf.Get( pBuf, sizeof(VTFFileHeader_t) - sizeof(VTFFileBaseHeader_t) );
  790. }
  791. else if ( header.version[1] == 3 )
  792. {
  793. buf.Get( pBuf, sizeof(VTFFileHeaderV7_3_t) - sizeof(VTFFileBaseHeader_t) );
  794. }
  795. else if ( header.version[1] == 2 )
  796. {
  797. buf.Get( pBuf, sizeof(VTFFileHeaderV7_2_t) - sizeof(VTFFileBaseHeader_t) );
  798. #if defined( _X360 ) || defined (POSIX)
  799. // read 15 dummy bytes to be properly positioned with 7.2 PC data
  800. byte dummy[15];
  801. buf.Get( dummy, 15 );
  802. #endif
  803. }
  804. else if ( header.version[1] == 1 || header.version[1] == 0 )
  805. {
  806. // previous version 7.0 or 7.1
  807. buf.Get( pBuf, sizeof(VTFFileHeaderV7_1_t) - sizeof(VTFFileBaseHeader_t) );
  808. #if defined( _X360 ) || defined (POSIX)
  809. // read a dummy byte to be properly positioned with 7.0/1 PC data
  810. byte dummy;
  811. buf.Get( &dummy, 1 );
  812. #endif
  813. }
  814. else
  815. {
  816. Warning( "*** Encountered VTF file with an invalid minor version!\n" );
  817. return false;
  818. }
  819. return buf.IsValid();
  820. }
  821. bool CVTFTexture::ReadHeader( CUtlBuffer &buf, VTFFileHeader_t &header )
  822. {
  823. if ( (IsX360() || IsPS3()) && SetupByteSwap( buf ) )
  824. {
  825. VTFFileBaseHeader_t baseHeader;
  826. m_Swap.SwapFieldsToTargetEndian( &baseHeader, (VTFFileBaseHeader_t*)buf.PeekGet() );
  827. // Swap the header inside the UtlBuffer
  828. if ( baseHeader.version[0] == VTF_MAJOR_VERSION )
  829. {
  830. if ( baseHeader.version[1] == 0 || baseHeader.version[1] == 1 )
  831. {
  832. // version 7.0 or 7.1
  833. m_Swap.SwapFieldsToTargetEndian( (VTFFileHeaderV7_1_t*)buf.PeekGet() );
  834. }
  835. else if ( baseHeader.version[1] == 2 )
  836. {
  837. // version 7.2
  838. m_Swap.SwapFieldsToTargetEndian( (VTFFileHeaderV7_2_t*)buf.PeekGet() );
  839. }
  840. else if ( baseHeader.version[1] == 3 )
  841. {
  842. m_Swap.SwapFieldsToTargetEndian( (VTFFileHeaderV7_3_t*)buf.PeekGet() );
  843. }
  844. else if ( baseHeader.version[1] >= 4 && baseHeader.version[1] <= VTF_MINOR_VERSION )
  845. {
  846. m_Swap.SwapFieldsToTargetEndian( (VTFFileHeader_t*)buf.PeekGet() );
  847. }
  848. }
  849. }
  850. memset( &header, 0, sizeof(VTFFileHeader_t) );
  851. buf.Get( &header, sizeof(VTFFileBaseHeader_t) );
  852. if ( !buf.IsValid() )
  853. {
  854. Warning( "*** Error unserializing VTF file... is the file empty?\n" );
  855. return false;
  856. }
  857. // Validity check
  858. if ( Q_strncmp( header.fileTypeString, "VTF", 4 ) )
  859. {
  860. Warning( "*** Tried to load a non-VTF file as a VTF file!\n" );
  861. return false;
  862. }
  863. if ( header.version[0] != VTF_MAJOR_VERSION )
  864. {
  865. Warning( "*** Encountered VTF file with an invalid version!\n" );
  866. return false;
  867. }
  868. if ( !ReadHeaderFromBufferPastBaseHeader( buf, header ) )
  869. {
  870. Warning( "*** Encountered VTF file with an invalid full header!\n" );
  871. return false;
  872. }
  873. // version fixups
  874. switch ( header.version[1] )
  875. {
  876. case 0:
  877. case 1:
  878. header.depth = 1;
  879. // fall-through
  880. case 2:
  881. header.numResources = 0;
  882. // fall-through
  883. case 3:
  884. header.flags &= VERSIONED_VTF_FLAGS_MASK_7_3;
  885. // fall-through
  886. case 4:
  887. case VTF_MINOR_VERSION:
  888. break;
  889. }
  890. return true;
  891. }
  892. //-----------------------------------------------------------------------------
  893. // Unserialization
  894. //-----------------------------------------------------------------------------
  895. bool CVTFTexture::Unserialize( CUtlBuffer &buf, bool bHeaderOnly, int nSkipMipLevels )
  896. {
  897. // When unserializing, we can skip a certain number of mip levels,
  898. // and we also can just load everything but the image data
  899. VTFFileHeader_t header;
  900. if ( !ReadHeader( buf, header ) )
  901. return false;
  902. if ( (header.flags & TEXTUREFLAGS_ENVMAP) && (header.width != header.height) )
  903. {
  904. Warning( "*** Encountered VTF non-square cubemap!\n" );
  905. return false;
  906. }
  907. if ( (header.flags & TEXTUREFLAGS_ENVMAP) && (header.depth != 1) )
  908. {
  909. Warning("*** Encountered VTF volume texture cubemap!\n");
  910. return false;
  911. }
  912. if ( header.width <= 0 || header.height <= 0 || header.depth <= 0 )
  913. {
  914. Warning( "*** Encountered VTF invalid texture size!\n" );
  915. return false;
  916. }
  917. m_nWidth = header.width;
  918. m_nHeight = header.height;
  919. m_nDepth = header.depth;
  920. m_Format = header.imageFormat;
  921. m_nFlags = header.flags;
  922. m_nFrameCount = header.numFrames;
  923. m_nFaceCount = (m_nFlags & TEXTUREFLAGS_ENVMAP) ? CUBEMAP_FACE_COUNT : 1;
  924. // NOTE: We're going to store space for all mip levels, even if we don't
  925. // have data on disk for them. This is for backward compatibility
  926. m_nMipCount = ComputeMipCount();
  927. m_vecReflectivity = header.reflectivity;
  928. m_flBumpScale = header.bumpScale;
  929. // FIXME: Why is this needed?
  930. m_iStartFrame = header.startFrame;
  931. // This is to make sure old-format .vtf files are read properly
  932. m_nVersion[0] = header.version[0];
  933. m_nVersion[1] = header.version[1];
  934. if ( header.lowResImageWidth == 0 || header.lowResImageHeight == 0 )
  935. {
  936. m_nLowResImageWidth = 0;
  937. m_nLowResImageHeight = 0;
  938. }
  939. else
  940. {
  941. m_nLowResImageWidth = header.lowResImageWidth;
  942. m_nLowResImageHeight = header.lowResImageHeight;
  943. }
  944. m_LowResImageFormat = header.lowResImageFormat;
  945. // Keep the allocated memory chunks of data
  946. if ( int( header.numResources ) < m_arrResourcesData.Count() )
  947. {
  948. m_arrResourcesData_ForReuse.EnsureCapacity( m_arrResourcesData_ForReuse.Count() + m_arrResourcesData.Count() - header.numResources );
  949. for ( ResourceMemorySection const *pRms = &m_arrResourcesData[ header.numResources ],
  950. *pRmsEnd = m_arrResourcesData.Base() + m_arrResourcesData.Count(); pRms < pRmsEnd; ++ pRms )
  951. {
  952. if ( pRms->m_pData )
  953. {
  954. int idxReuse = m_arrResourcesData_ForReuse.AddToTail( *pRms );
  955. m_arrResourcesData_ForReuse[ idxReuse ].m_nDataLength = 0; // Data for reuse shouldn't have length set
  956. }
  957. }
  958. }
  959. m_arrResourcesData.SetCount( header.numResources );
  960. // Read the dictionary of resources info
  961. if ( header.numResources > 0 )
  962. {
  963. m_arrResourcesInfo.RemoveAll();
  964. m_arrResourcesInfo.SetCount( header.numResources );
  965. buf.Get( m_arrResourcesInfo.Base(), m_arrResourcesInfo.Count() * sizeof( ResourceEntryInfo ) );
  966. if ( !buf.IsValid() )
  967. return false;
  968. if ( IsX360() || IsPS3() )
  969. {
  970. // Byte-swap the dictionary data offsets
  971. for ( int k = 0; k < m_arrResourcesInfo.Count(); ++ k )
  972. {
  973. ResourceEntryInfo &rei = m_arrResourcesInfo[k];
  974. if ( ( rei.eType & RSRCF_HAS_NO_DATA_CHUNK ) == 0 )
  975. {
  976. m_Swap.SwapBufferToTargetEndian( &rei.resData );
  977. }
  978. }
  979. }
  980. }
  981. else
  982. {
  983. // Older version (7.0 - 7.2):
  984. // - low-res image data first (optional)
  985. // - then image data
  986. m_arrResourcesInfo.RemoveAll();
  987. // Low-res image data
  988. int nLowResImageSize = ImageLoader::GetMemRequired( m_nLowResImageWidth,
  989. m_nLowResImageHeight, 1, m_LowResImageFormat, false );
  990. if ( nLowResImageSize )
  991. {
  992. ResourceEntryInfo &rei = *FindOrCreateResourceEntryInfo( VTF_LEGACY_RSRC_LOW_RES_IMAGE );
  993. rei.resData = buf.TellGet();
  994. }
  995. // Image data
  996. ResourceEntryInfo &rei = *FindOrCreateResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  997. rei.resData = buf.TellGet() + nLowResImageSize;
  998. }
  999. // Caller wants the header component only, avoids reading large image data sets
  1000. if ( bHeaderOnly )
  1001. return true;
  1002. // Load the low res image
  1003. if ( ResourceEntryInfo const *pLowResDataInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_LOW_RES_IMAGE ) )
  1004. {
  1005. buf.SeekGet( CUtlBuffer::SEEK_HEAD, pLowResDataInfo->resData );
  1006. if ( !LoadLowResData( buf ) )
  1007. return false;
  1008. }
  1009. // Load any new resources
  1010. if ( !LoadNewResources( buf ) )
  1011. {
  1012. return false;
  1013. }
  1014. // Load the image data
  1015. if ( ResourceEntryInfo const *pImageDataInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE ) )
  1016. {
  1017. buf.SeekGet( CUtlBuffer::SEEK_HEAD, pImageDataInfo->resData );
  1018. if ( !LoadImageData( buf, header, nSkipMipLevels ) )
  1019. return false;
  1020. }
  1021. else
  1022. {
  1023. // No image data
  1024. return false;
  1025. }
  1026. return true;
  1027. }
  1028. bool CVTFTexture::LoadNewResources( CUtlBuffer &buf )
  1029. {
  1030. // Load the new resources
  1031. for ( int idxRsrc = 0; idxRsrc < m_arrResourcesInfo.Count(); ++idxRsrc )
  1032. {
  1033. ResourceEntryInfo &rei = m_arrResourcesInfo[ idxRsrc ];
  1034. ResourceMemorySection &rms = m_arrResourcesData[ idxRsrc ];
  1035. if ( ( rei.eType & RSRCF_HAS_NO_DATA_CHUNK ) == 0 )
  1036. {
  1037. switch( rei.eType )
  1038. {
  1039. case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
  1040. case VTF_LEGACY_RSRC_IMAGE:
  1041. // these legacy resources are loaded differently
  1042. continue;
  1043. default:
  1044. buf.SeekGet( CUtlBuffer::SEEK_HEAD, rei.resData );
  1045. if ( !rms.LoadData( buf, m_Swap ) )
  1046. return false;
  1047. }
  1048. }
  1049. }
  1050. return true;
  1051. }
  1052. ResourceEntryInfo const *CVTFTexture::FindResourceEntryInfo( uint32 eType ) const
  1053. {
  1054. Assert( ( eType & RSRCF_MASK ) == 0 );
  1055. ResourceEntryInfo const *pRange[2];
  1056. pRange[0] = m_arrResourcesInfo.Base();
  1057. pRange[1] = pRange[0] + m_arrResourcesInfo.Count();
  1058. if ( IsPC() )
  1059. {
  1060. // Quick-search in a sorted array
  1061. ResourceEntryInfo const *pMid;
  1062. find_routine:
  1063. if ( pRange[0] != pRange[1] )
  1064. {
  1065. pMid = pRange[0] + ( pRange[1] - pRange[0] ) / 2;
  1066. if ( int diff = int( pMid->eType & ~RSRCF_MASK ) - int( eType ) )
  1067. {
  1068. int off = !( diff > 0 );
  1069. pRange[ !off ] = pMid + off;
  1070. goto find_routine;
  1071. }
  1072. else
  1073. return pMid;
  1074. }
  1075. else
  1076. return NULL;
  1077. }
  1078. else
  1079. {
  1080. // 360 eschews a sorted format due to endian issues
  1081. // use a linear search for compatibility with reading pc formats
  1082. for ( ; pRange[0] < pRange[1]; ++pRange[0] )
  1083. {
  1084. if ( ( pRange[0]->eType & ~RSRCF_MASK ) == eType )
  1085. return pRange[0];
  1086. }
  1087. }
  1088. return NULL;
  1089. }
  1090. ResourceEntryInfo * CVTFTexture::FindResourceEntryInfo( uint32 eType )
  1091. {
  1092. return const_cast< ResourceEntryInfo * >(
  1093. ( ( CVTFTexture const * ) this )->FindResourceEntryInfo( eType ) );
  1094. }
  1095. ResourceEntryInfo * CVTFTexture::FindOrCreateResourceEntryInfo( uint32 eType )
  1096. {
  1097. Assert( ( eType & RSRCF_MASK ) == 0 );
  1098. int k = 0;
  1099. for ( ; k < m_arrResourcesInfo.Count(); ++ k )
  1100. {
  1101. uint32 rsrcType = ( m_arrResourcesInfo[ k ].eType & ~RSRCF_MASK );
  1102. if ( rsrcType == eType )
  1103. {
  1104. // found
  1105. return &m_arrResourcesInfo[ k ];
  1106. }
  1107. // sort for PC only, 360 uses linear sort for compatibility with PC endian
  1108. if ( IsPC() )
  1109. {
  1110. if ( rsrcType > eType )
  1111. break;
  1112. }
  1113. }
  1114. ResourceEntryInfo rei;
  1115. memset( &rei, 0, sizeof( rei ) );
  1116. rei.eType = eType;
  1117. // Inserting before "k"
  1118. if ( m_arrResourcesData_ForReuse.Count() )
  1119. {
  1120. m_arrResourcesData.InsertBefore( k, m_arrResourcesData_ForReuse[ m_arrResourcesData_ForReuse.Count() - 1 ] );
  1121. m_arrResourcesData_ForReuse.FastRemove( m_arrResourcesData_ForReuse.Count() - 1 );
  1122. }
  1123. else
  1124. {
  1125. m_arrResourcesData.InsertBefore( k );
  1126. }
  1127. m_arrResourcesInfo.InsertBefore( k, rei );
  1128. return &m_arrResourcesInfo[k];
  1129. }
  1130. bool CVTFTexture::RemoveResourceEntryInfo( uint32 eType )
  1131. {
  1132. Assert( ( eType & RSRCF_MASK ) == 0 );
  1133. for ( int k = 0; k < m_arrResourcesInfo.Count(); ++ k )
  1134. {
  1135. if ( ( m_arrResourcesInfo[ k ].eType & ~RSRCF_MASK ) == eType )
  1136. {
  1137. m_arrResourcesInfo.Remove( k );
  1138. if ( m_arrResourcesData[k].m_pData )
  1139. {
  1140. int idxReuse = m_arrResourcesData_ForReuse.AddToTail( m_arrResourcesData[k] );
  1141. m_arrResourcesData_ForReuse[ idxReuse ].m_nDataLength = 0; // Data for reuse shouldn't have length set
  1142. }
  1143. m_arrResourcesData.Remove( k );
  1144. return true;
  1145. }
  1146. }
  1147. return false;
  1148. }
  1149. //-----------------------------------------------------------------------------
  1150. // Serialization of image data
  1151. //-----------------------------------------------------------------------------
  1152. bool CVTFTexture::WriteImageData( CUtlBuffer &buf )
  1153. {
  1154. // NOTE: We load the bits this way because we store the bits in memory
  1155. // differently that the way they are stored on disk; we store on disk
  1156. // differently so we can only load up
  1157. // NOTE: The smallest mip levels are stored first!!
  1158. for (int iMip = m_nMipCount; --iMip >= 0; )
  1159. {
  1160. int iMipSize = ComputeMipSize( iMip );
  1161. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  1162. {
  1163. for (int iFace = 0; iFace < m_nFaceCount; ++iFace)
  1164. {
  1165. unsigned char *pMipBits = ImageData( iFrame, iFace, iMip );
  1166. buf.Put( pMipBits, iMipSize );
  1167. }
  1168. }
  1169. }
  1170. return buf.IsValid();
  1171. }
  1172. // Inserts padding to have a multiple of "iAlignment" bytes in the buffer
  1173. // Returns number of pad bytes written
  1174. static int PadBuffer( CUtlBuffer &buf, int iAlignment )
  1175. {
  1176. unsigned int uiCurrentBytes = buf.TellPut();
  1177. int iPadBytes = AlignValue( uiCurrentBytes, iAlignment ) - uiCurrentBytes;
  1178. // Fill data
  1179. for ( int i=0; i<iPadBytes; i++ )
  1180. {
  1181. buf.PutChar( '\0' );
  1182. }
  1183. buf.SeekPut( CUtlBuffer::SEEK_HEAD, uiCurrentBytes + iPadBytes );
  1184. return iPadBytes;
  1185. }
  1186. //-----------------------------------------------------------------------------
  1187. // Serialization
  1188. //-----------------------------------------------------------------------------
  1189. bool CVTFTexture::Serialize( CUtlBuffer &buf )
  1190. {
  1191. if ( IsGameConsole() )
  1192. {
  1193. // Unsupported path, console has no reason and cannot serialize
  1194. Assert( 0 );
  1195. return false;
  1196. }
  1197. if ( !m_pImageData )
  1198. {
  1199. Warning("*** Unable to serialize... have no image data!\n");
  1200. return false;
  1201. }
  1202. VTFFileHeader_t header;
  1203. memset( &header, 0, sizeof( header ) );
  1204. Q_strncpy( header.fileTypeString, "VTF", 4 );
  1205. header.version[0] = VTF_MAJOR_VERSION;
  1206. header.version[1] = VTF_MINOR_VERSION;
  1207. header.headerSize = sizeof(VTFFileHeader_t) + m_arrResourcesInfo.Count() * sizeof( ResourceEntryInfo );
  1208. header.width = m_nWidth;
  1209. header.height = m_nHeight;
  1210. header.depth = m_nDepth;
  1211. header.flags = m_nFlags;
  1212. header.numFrames = m_nFrameCount;
  1213. header.numMipLevels = m_nMipCount;
  1214. header.imageFormat = m_Format;
  1215. // fixup runtime image formats to be their non-runtime equivolents.
  1216. if ( m_Format == IMAGE_FORMAT_DXT1_RUNTIME )
  1217. {
  1218. header.imageFormat = IMAGE_FORMAT_DXT1;
  1219. }
  1220. else if ( m_Format == IMAGE_FORMAT_DXT5_RUNTIME )
  1221. {
  1222. header.imageFormat = IMAGE_FORMAT_DXT5;
  1223. }
  1224. VectorCopy( m_vecReflectivity, header.reflectivity );
  1225. header.bumpScale = m_flBumpScale;
  1226. // FIXME: Why is this needed?
  1227. header.startFrame = m_iStartFrame;
  1228. header.lowResImageWidth = m_nLowResImageWidth;
  1229. header.lowResImageHeight = m_nLowResImageHeight;
  1230. header.lowResImageFormat = m_LowResImageFormat;
  1231. header.numResources = m_arrResourcesInfo.Count();
  1232. buf.Put( &header, sizeof(VTFFileHeader_t) );
  1233. if ( !buf.IsValid() )
  1234. return false;
  1235. // Write the dictionary of resource entry infos
  1236. int iSeekOffsetResInfoFixup = buf.TellPut();
  1237. buf.Put( m_arrResourcesInfo.Base(), m_arrResourcesInfo.Count() * sizeof( ResourceEntryInfo ) );
  1238. if ( !buf.IsValid() )
  1239. return false;
  1240. // Write the low res image first
  1241. if ( ResourceEntryInfo *pRei = FindResourceEntryInfo( VTF_LEGACY_RSRC_LOW_RES_IMAGE ) )
  1242. {
  1243. pRei->resData = buf.TellPut();
  1244. Assert( m_pLowResImageData );
  1245. int iLowResImageSize = ImageLoader::GetMemRequired( m_nLowResImageWidth,
  1246. m_nLowResImageHeight, 1, m_LowResImageFormat, false );
  1247. buf.Put( m_pLowResImageData, iLowResImageSize );
  1248. if ( !buf.IsValid() )
  1249. return false;
  1250. }
  1251. // Serialize the new resources
  1252. for ( int iRsrc = 0; iRsrc < m_arrResourcesInfo.Count(); ++ iRsrc )
  1253. {
  1254. ResourceEntryInfo &rei = m_arrResourcesInfo[ iRsrc ];
  1255. switch ( rei.eType )
  1256. {
  1257. case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
  1258. case VTF_LEGACY_RSRC_IMAGE:
  1259. // written differently
  1260. continue;
  1261. default:
  1262. {
  1263. if ( rei.eType & RSRCF_HAS_NO_DATA_CHUNK )
  1264. continue;
  1265. rei.resData = buf.TellPut();
  1266. ResourceMemorySection &rms = m_arrResourcesData[ iRsrc ];
  1267. if ( !rms.WriteData( buf ) )
  1268. return false;
  1269. }
  1270. }
  1271. }
  1272. // Write image data last
  1273. if ( ResourceEntryInfo *pRei = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE ) )
  1274. {
  1275. pRei->resData = buf.TellPut();
  1276. WriteImageData( buf );
  1277. }
  1278. else
  1279. return false;
  1280. // Now fixup the resources dictionary
  1281. int iTotalBytesPut = buf.TellPut();
  1282. buf.SeekPut( CUtlBuffer::SEEK_HEAD, iSeekOffsetResInfoFixup );
  1283. buf.Put( m_arrResourcesInfo.Base(), m_arrResourcesInfo.Count() * sizeof( ResourceEntryInfo ) );
  1284. buf.SeekPut( CUtlBuffer::SEEK_HEAD, iTotalBytesPut );
  1285. // Return if the buffer is valid
  1286. return buf.IsValid();
  1287. }
  1288. //-----------------------------------------------------------------------------
  1289. // Attributes...
  1290. //-----------------------------------------------------------------------------
  1291. int CVTFTexture::Width() const
  1292. {
  1293. return m_nWidth;
  1294. }
  1295. int CVTFTexture::Height() const
  1296. {
  1297. return m_nHeight;
  1298. }
  1299. int CVTFTexture::Depth() const
  1300. {
  1301. return m_nDepth;
  1302. }
  1303. int CVTFTexture::MipCount() const
  1304. {
  1305. return m_nMipCount;
  1306. }
  1307. ImageFormat CVTFTexture::Format() const
  1308. {
  1309. return m_Format;
  1310. }
  1311. int CVTFTexture::FaceCount() const
  1312. {
  1313. return m_nFaceCount;
  1314. }
  1315. int CVTFTexture::FrameCount() const
  1316. {
  1317. return m_nFrameCount;
  1318. }
  1319. int CVTFTexture::Flags() const
  1320. {
  1321. return m_nFlags;
  1322. }
  1323. bool CVTFTexture::IsCubeMap() const
  1324. {
  1325. return (m_nFlags & TEXTUREFLAGS_ENVMAP) != 0;
  1326. }
  1327. bool CVTFTexture::IsNormalMap() const
  1328. {
  1329. return (m_nFlags & TEXTUREFLAGS_NORMAL) != 0;
  1330. }
  1331. bool CVTFTexture::IsVolumeTexture() const
  1332. {
  1333. return (m_nDepth > 1);
  1334. }
  1335. float CVTFTexture::BumpScale() const
  1336. {
  1337. return m_flBumpScale;
  1338. }
  1339. const Vector &CVTFTexture::Reflectivity() const
  1340. {
  1341. return m_vecReflectivity;
  1342. }
  1343. unsigned char *CVTFTexture::ImageData()
  1344. {
  1345. return m_pImageData;
  1346. }
  1347. int CVTFTexture::LowResWidth() const
  1348. {
  1349. return m_nLowResImageWidth;
  1350. }
  1351. int CVTFTexture::LowResHeight() const
  1352. {
  1353. return m_nLowResImageHeight;
  1354. }
  1355. ImageFormat CVTFTexture::LowResFormat() const
  1356. {
  1357. return m_LowResImageFormat;
  1358. }
  1359. unsigned char *CVTFTexture::LowResImageData()
  1360. {
  1361. return m_pLowResImageData;
  1362. }
  1363. int CVTFTexture::RowSizeInBytes( int nMipLevel ) const
  1364. {
  1365. int nWidth = (m_nWidth >> nMipLevel);
  1366. if (nWidth < 1)
  1367. {
  1368. nWidth = 1;
  1369. }
  1370. return ImageLoader::SizeInBytes( m_Format ) * nWidth;
  1371. }
  1372. //-----------------------------------------------------------------------------
  1373. // returns the size of one face of a particular mip level
  1374. //-----------------------------------------------------------------------------
  1375. int CVTFTexture::FaceSizeInBytes( int nMipLevel ) const
  1376. {
  1377. int nWidth = (m_nWidth >> nMipLevel);
  1378. if (nWidth < 1)
  1379. {
  1380. nWidth = 1;
  1381. }
  1382. int nHeight = (m_nHeight >> nMipLevel);
  1383. if (nHeight < 1)
  1384. {
  1385. nHeight = 1;
  1386. }
  1387. return ImageLoader::SizeInBytes( m_Format, nWidth, nHeight );
  1388. }
  1389. //-----------------------------------------------------------------------------
  1390. // Returns a pointer to the data associated with a particular frame, face, and mip level
  1391. //-----------------------------------------------------------------------------
  1392. unsigned char *CVTFTexture::ImageData( int iFrame, int iFace, int iMipLevel )
  1393. {
  1394. Assert( m_pImageData );
  1395. int iOffset = GetImageOffset( iFrame, iFace, iMipLevel, m_Format );
  1396. return &m_pImageData[iOffset];
  1397. }
  1398. //-----------------------------------------------------------------------------
  1399. // Returns a pointer to the data associated with a particular frame, face, mip level, and offset
  1400. //-----------------------------------------------------------------------------
  1401. unsigned char *CVTFTexture::ImageData( int iFrame, int iFace, int iMipLevel, int x, int y, int z )
  1402. {
  1403. #ifdef _DEBUG
  1404. int nWidth, nHeight, nDepth;
  1405. ComputeMipLevelDimensions( iMipLevel, &nWidth, &nHeight, &nDepth );
  1406. Assert( (x >= 0) && (x <= nWidth) && (y >= 0) && (y <= nHeight) && (z >= 0) && (z <= nDepth) );
  1407. #endif
  1408. int nFaceBytes = FaceSizeInBytes( iMipLevel );
  1409. int nRowBytes = RowSizeInBytes( iMipLevel );
  1410. int nTexelBytes = ImageLoader::SizeInBytes( m_Format );
  1411. unsigned char *pMipBits = ImageData( iFrame, iFace, iMipLevel );
  1412. pMipBits += z * nFaceBytes + y * nRowBytes + x * nTexelBytes;
  1413. return pMipBits;
  1414. }
  1415. //-----------------------------------------------------------------------------
  1416. // Computes the size (in bytes) of a single mipmap of a single face of a single frame
  1417. //-----------------------------------------------------------------------------
  1418. inline int CVTFTexture::ComputeMipSize( int iMipLevel, ImageFormat fmt ) const
  1419. {
  1420. Assert( iMipLevel < m_nMipCount );
  1421. int w, h, d;
  1422. ComputeMipLevelDimensions( iMipLevel, &w, &h, &d );
  1423. return ImageLoader::GetMemRequired( w, h, d, fmt, false );
  1424. }
  1425. int CVTFTexture::ComputeMipSize( int iMipLevel ) const
  1426. {
  1427. // Version for the public interface; don't want to expose the fmt parameter
  1428. return ComputeMipSize( iMipLevel, m_Format );
  1429. }
  1430. //-----------------------------------------------------------------------------
  1431. // Computes the size of a single face of a single frame
  1432. // All mip levels starting at the specified mip level are included
  1433. //-----------------------------------------------------------------------------
  1434. inline int CVTFTexture::ComputeFaceSize( int iStartingMipLevel, ImageFormat fmt ) const
  1435. {
  1436. int iSize = 0;
  1437. int w = m_nWidth;
  1438. int h = m_nHeight;
  1439. int d = m_nDepth;
  1440. for( int i = 0; i < m_nMipCount; ++i )
  1441. {
  1442. if (i >= iStartingMipLevel)
  1443. {
  1444. iSize += ImageLoader::GetMemRequired( w, h, d, fmt, false );
  1445. }
  1446. w >>= 1;
  1447. h >>= 1;
  1448. d >>= 1;
  1449. if ( w < 1 )
  1450. {
  1451. w = 1;
  1452. }
  1453. if ( h < 1 )
  1454. {
  1455. h = 1;
  1456. }
  1457. if ( d < 1 )
  1458. {
  1459. d = 1;
  1460. }
  1461. }
  1462. return iSize;
  1463. }
  1464. int CVTFTexture::ComputeFaceSize( int iStartingMipLevel ) const
  1465. {
  1466. // Version for the public interface; don't want to expose the fmt parameter
  1467. return ComputeFaceSize( iStartingMipLevel, m_Format );
  1468. }
  1469. //-----------------------------------------------------------------------------
  1470. // Computes the total size of all faces, all frames
  1471. //-----------------------------------------------------------------------------
  1472. inline int CVTFTexture::ComputeTotalSize( ImageFormat fmt ) const
  1473. {
  1474. // Compute the number of bytes required to store a single face/frame
  1475. int iMemRequired = ComputeFaceSize( 0, fmt );
  1476. // Now compute the total image size
  1477. return m_nFaceCount * m_nFrameCount * iMemRequired;
  1478. }
  1479. int CVTFTexture::ComputeTotalSize( ) const
  1480. {
  1481. // Version for the public interface; don't want to expose the fmt parameter
  1482. return ComputeTotalSize( m_Format );
  1483. }
  1484. //-----------------------------------------------------------------------------
  1485. // Computes the location of a particular frame, face, and mip level
  1486. //-----------------------------------------------------------------------------
  1487. int CVTFTexture::GetImageOffset( int iFrame, int iFace, int iMipLevel, ImageFormat fmt ) const
  1488. {
  1489. Assert( iFrame < m_nFrameCount );
  1490. Assert( iFace < m_nFaceCount );
  1491. Assert( iMipLevel < m_nMipCount );
  1492. int i;
  1493. int iOffset = 0;
  1494. if ( ( IsX360() && ( m_nVersion[0] == VTF_X360_MAJOR_VERSION ) ) || ( IsPS3() && ( m_nVersion[0] == VTF_PS3_MAJOR_VERSION ) ) )
  1495. {
  1496. // 360 data is stored same as disk, 1x1 up to NxN
  1497. // get to the right miplevel
  1498. int iMipWidth, iMipHeight, iMipDepth;
  1499. for ( i = m_nMipCount - 1; i > iMipLevel; --i )
  1500. {
  1501. ComputeMipLevelDimensions( i, &iMipWidth, &iMipHeight, &iMipDepth );
  1502. int iMipLevelSize = ImageLoader::GetMemRequired( iMipWidth, iMipHeight, iMipDepth, fmt, false );
  1503. iOffset += m_nFrameCount * m_nFaceCount * iMipLevelSize;
  1504. }
  1505. // get to the right frame
  1506. ComputeMipLevelDimensions( iMipLevel, &iMipWidth, &iMipHeight, &iMipDepth );
  1507. int nFaceSize = ImageLoader::GetMemRequired( iMipWidth, iMipHeight, iMipDepth, fmt, false );
  1508. iOffset += iFrame * m_nFaceCount * nFaceSize;
  1509. // get to the right face
  1510. iOffset += iFace * nFaceSize;
  1511. return iOffset;
  1512. }
  1513. // get to the right frame
  1514. int iFaceSize = ComputeFaceSize( 0, fmt );
  1515. iOffset = iFrame * m_nFaceCount * iFaceSize;
  1516. // Get to the right face
  1517. iOffset += iFace * iFaceSize;
  1518. // Get to the right mip level
  1519. for (i = 0; i < iMipLevel; ++i)
  1520. {
  1521. iOffset += ComputeMipSize( i, fmt );
  1522. }
  1523. return iOffset;
  1524. }
  1525. //-----------------------------------------------------------------------------
  1526. // Computes the dimensions of a particular mip level
  1527. //-----------------------------------------------------------------------------
  1528. void CVTFTexture::ComputeMipLevelDimensions( int iMipLevel, int *pMipWidth, int *pMipHeight, int *pMipDepth ) const
  1529. {
  1530. Assert( iMipLevel < m_nMipCount );
  1531. *pMipWidth = m_nWidth >> iMipLevel;
  1532. *pMipHeight = m_nHeight >> iMipLevel;
  1533. *pMipDepth = m_nDepth >> iMipLevel;
  1534. if ( *pMipWidth < 1 )
  1535. {
  1536. *pMipWidth = 1;
  1537. }
  1538. if ( *pMipHeight < 1 )
  1539. {
  1540. *pMipHeight = 1;
  1541. }
  1542. if ( *pMipDepth < 1 )
  1543. {
  1544. *pMipDepth = 1;
  1545. }
  1546. }
  1547. //-----------------------------------------------------------------------------
  1548. // Computes the size of a subrect at a particular mip level
  1549. //-----------------------------------------------------------------------------
  1550. void CVTFTexture::ComputeMipLevelSubRect( Rect_t *pSrcRect, int nMipLevel, Rect_t *pSubRect ) const
  1551. {
  1552. Assert( pSrcRect->x >= 0 && pSrcRect->y >= 0 &&
  1553. (pSrcRect->x + pSrcRect->width <= m_nWidth) &&
  1554. (pSrcRect->y + pSrcRect->height <= m_nHeight) );
  1555. if (nMipLevel == 0)
  1556. {
  1557. *pSubRect = *pSrcRect;
  1558. return;
  1559. }
  1560. float flInvShrink = 1.0f / (float)(1 << nMipLevel);
  1561. pSubRect->x = ( int )( pSrcRect->x * flInvShrink );
  1562. pSubRect->y = ( int )( pSrcRect->y * flInvShrink );
  1563. pSubRect->width = (int)ceil( (pSrcRect->x + pSrcRect->width) * flInvShrink ) - pSubRect->x;
  1564. pSubRect->height = (int)ceil( (pSrcRect->y + pSrcRect->height) * flInvShrink ) - pSubRect->y;
  1565. }
  1566. //-----------------------------------------------------------------------------
  1567. // Converts the texture's image format. Use IMAGE_FORMAT_DEFAULT
  1568. // if you want to be able to use various tool functions below
  1569. //-----------------------------------------------------------------------------
  1570. void CVTFTexture::ConvertImageFormat( ImageFormat fmt, bool bNormalToDUDV, bool bNormalToDXT5GA )
  1571. {
  1572. if ( !m_pImageData )
  1573. {
  1574. return;
  1575. }
  1576. if ( fmt == IMAGE_FORMAT_DEFAULT )
  1577. {
  1578. fmt = IMAGE_FORMAT_RGBA8888;
  1579. }
  1580. if ( bNormalToDUDV && !( fmt == IMAGE_FORMAT_UV88 || fmt == IMAGE_FORMAT_UVWQ8888 || fmt == IMAGE_FORMAT_UVLX8888 ) )
  1581. {
  1582. Assert( 0 );
  1583. return;
  1584. }
  1585. if ( m_Format == fmt )
  1586. {
  1587. return;
  1588. }
  1589. if ( ( IsX360() && ( m_nVersion[0] == VTF_X360_MAJOR_VERSION ) ) || ( IsPS3() && ( m_nVersion[0] == VTF_PS3_MAJOR_VERSION ) ) )
  1590. {
  1591. // 360 textures should be baked in final format
  1592. Assert( 0 );
  1593. return;
  1594. }
  1595. // FIXME: Should this be re-written to not do an allocation?
  1596. int iConvertedSize = ComputeTotalSize( fmt );
  1597. unsigned char *pConvertedImage = (unsigned char*)MemAllocScratch(iConvertedSize);
  1598. for (int iMip = 0; iMip < m_nMipCount; ++iMip)
  1599. {
  1600. int nMipWidth, nMipHeight, nMipDepth;
  1601. ComputeMipLevelDimensions( iMip, &nMipWidth, &nMipHeight, &nMipDepth );
  1602. int nSrcFaceStride = ImageLoader::GetMemRequired( nMipWidth, nMipHeight, 1, m_Format, false );
  1603. int nDstFaceStride = ImageLoader::GetMemRequired( nMipWidth, nMipHeight, 1, fmt, false );
  1604. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  1605. {
  1606. for (int iFace = 0; iFace < m_nFaceCount; ++iFace)
  1607. {
  1608. unsigned char *pSrcData = ImageData( iFrame, iFace, iMip );
  1609. unsigned char *pDstData = pConvertedImage + GetImageOffset( iFrame, iFace, iMip, fmt );
  1610. for ( int z = 0; z < nMipDepth; ++z, pSrcData += nSrcFaceStride, pDstData += nDstFaceStride )
  1611. {
  1612. if( bNormalToDUDV )
  1613. {
  1614. if( fmt == IMAGE_FORMAT_UV88 )
  1615. {
  1616. ImageLoader::ConvertNormalMapRGBA8888ToDUDVMapUV88( pSrcData,
  1617. nMipWidth, nMipHeight, pDstData );
  1618. }
  1619. else if( fmt == IMAGE_FORMAT_UVWQ8888 )
  1620. {
  1621. ImageLoader::ConvertNormalMapRGBA8888ToDUDVMapUVWQ8888( pSrcData,
  1622. nMipWidth, nMipHeight, pDstData );
  1623. }
  1624. else if ( fmt == IMAGE_FORMAT_UVLX8888 )
  1625. {
  1626. ImageLoader::ConvertNormalMapRGBA8888ToDUDVMapUVLX8888( pSrcData,
  1627. nMipWidth, nMipHeight, pDstData );
  1628. }
  1629. else
  1630. {
  1631. Assert( 0 );
  1632. return;
  1633. }
  1634. }
  1635. else if( bNormalToDXT5GA )
  1636. {
  1637. ImageLoader::ConvertNormalMapARGB8888ToDXT5GA( pSrcData, pDstData, nMipWidth, nMipHeight );
  1638. }
  1639. else
  1640. {
  1641. ImageLoader::ConvertImageFormat( pSrcData, m_Format,
  1642. pDstData, fmt, nMipWidth, nMipHeight );
  1643. }
  1644. }
  1645. }
  1646. }
  1647. }
  1648. if ( !AllocateImageData(iConvertedSize) )
  1649. return;
  1650. Assert(iConvertedSize<=m_nImageAllocSize);
  1651. memcpy( m_pImageData, pConvertedImage, iConvertedSize );
  1652. m_Format = fmt;
  1653. if ( !ImageLoader::IsCompressed( fmt ) )
  1654. {
  1655. int nAlphaBits = ImageLoader::ImageFormatInfo( fmt ).m_nNumAlphaBits;
  1656. if ( nAlphaBits > 1 )
  1657. {
  1658. m_nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  1659. m_nFlags &= ~TEXTUREFLAGS_ONEBITALPHA;
  1660. }
  1661. if ( nAlphaBits <= 1 )
  1662. {
  1663. m_nFlags &= ~TEXTUREFLAGS_EIGHTBITALPHA;
  1664. if ( nAlphaBits == 0 )
  1665. {
  1666. m_nFlags &= ~TEXTUREFLAGS_ONEBITALPHA;
  1667. }
  1668. }
  1669. }
  1670. else
  1671. {
  1672. // Only DXT5 has alpha bits
  1673. if ( ( fmt == IMAGE_FORMAT_DXT1 ) || ( fmt == IMAGE_FORMAT_ATI2N ) || ( fmt == IMAGE_FORMAT_ATI1N ) )
  1674. {
  1675. m_nFlags &= ~(TEXTUREFLAGS_ONEBITALPHA|TEXTUREFLAGS_EIGHTBITALPHA);
  1676. }
  1677. }
  1678. MemFreeScratch();
  1679. }
  1680. //-----------------------------------------------------------------------------
  1681. // Enums + structures related to conversion from cube to spheremap
  1682. //-----------------------------------------------------------------------------
  1683. struct SphereCalc_t
  1684. {
  1685. Vector dir;
  1686. float m_flRadius;
  1687. float m_flOORadius;
  1688. float m_flRadiusSq;
  1689. LookDir_t m_LookDir;
  1690. Vector m_vecLookDir;
  1691. unsigned char m_pColor[4];
  1692. unsigned char **m_ppCubeFaces;
  1693. int m_iSize;
  1694. };
  1695. //-----------------------------------------------------------------------------
  1696. //
  1697. // Methods associated with computing a spheremap from a cubemap
  1698. //
  1699. //-----------------------------------------------------------------------------
  1700. static void CalcInit( SphereCalc_t *pCalc, int iSize, unsigned char **ppCubeFaces, LookDir_t lookDir = LOOK_DOWN_Z )
  1701. {
  1702. // NOTE: Width + height should be the same
  1703. pCalc->m_flRadius = iSize * 0.5f;
  1704. pCalc->m_flRadiusSq = pCalc->m_flRadius * pCalc->m_flRadius;
  1705. pCalc->m_flOORadius = 1.0f / pCalc->m_flRadius;
  1706. pCalc->m_LookDir = lookDir;
  1707. pCalc->m_ppCubeFaces = ppCubeFaces;
  1708. pCalc->m_iSize = iSize;
  1709. switch( lookDir)
  1710. {
  1711. case LOOK_DOWN_X:
  1712. pCalc->m_vecLookDir.Init( 1, 0, 0 );
  1713. break;
  1714. case LOOK_DOWN_NEGX:
  1715. pCalc->m_vecLookDir.Init( -1, 0, 0 );
  1716. break;
  1717. case LOOK_DOWN_Y:
  1718. pCalc->m_vecLookDir.Init( 0, 1, 0 );
  1719. break;
  1720. case LOOK_DOWN_NEGY:
  1721. pCalc->m_vecLookDir.Init( 0, -1, 0 );
  1722. break;
  1723. case LOOK_DOWN_Z:
  1724. pCalc->m_vecLookDir.Init( 0, 0, 1 );
  1725. break;
  1726. case LOOK_DOWN_NEGZ:
  1727. pCalc->m_vecLookDir.Init( 0, 0, -1 );
  1728. break;
  1729. }
  1730. }
  1731. static void TransformNormal( SphereCalc_t *pCalc, Vector& normal )
  1732. {
  1733. Vector vecTemp = normal;
  1734. switch( pCalc->m_LookDir)
  1735. {
  1736. // Look down +x
  1737. case LOOK_DOWN_X:
  1738. normal[0] = vecTemp[2];
  1739. normal[2] = -vecTemp[0];
  1740. break;
  1741. // Look down -x
  1742. case LOOK_DOWN_NEGX:
  1743. normal[0] = -vecTemp[2];
  1744. normal[2] = vecTemp[0];
  1745. break;
  1746. // Look down +y
  1747. case LOOK_DOWN_Y:
  1748. normal[0] = -vecTemp[0];
  1749. normal[1] = vecTemp[2];
  1750. normal[2] = vecTemp[1];
  1751. break;
  1752. // Look down -y
  1753. case LOOK_DOWN_NEGY:
  1754. normal[0] = vecTemp[0];
  1755. normal[1] = -vecTemp[2];
  1756. normal[2] = vecTemp[1];
  1757. break;
  1758. // Look down +z
  1759. case LOOK_DOWN_Z:
  1760. return;
  1761. // Look down -z
  1762. case LOOK_DOWN_NEGZ:
  1763. normal[0] = -vecTemp[0];
  1764. normal[2] = -vecTemp[2];
  1765. break;
  1766. }
  1767. }
  1768. //-----------------------------------------------------------------------------
  1769. // Given a iFace normal, determine which cube iFace to sample
  1770. //-----------------------------------------------------------------------------
  1771. static int CalcFaceIndex( const Vector& normal )
  1772. {
  1773. float absx, absy, absz;
  1774. absx = normal[0] >= 0 ? normal[0] : -normal[0];
  1775. absy = normal[1] >= 0 ? normal[1] : -normal[1];
  1776. absz = normal[2] >= 0 ? normal[2] : -normal[2];
  1777. if ( absx > absy )
  1778. {
  1779. if ( absx > absz )
  1780. {
  1781. // left/right
  1782. if ( normal[0] >= 0 )
  1783. return CUBEMAP_FACE_RIGHT;
  1784. return CUBEMAP_FACE_LEFT;
  1785. }
  1786. }
  1787. else
  1788. {
  1789. if ( absy > absz )
  1790. {
  1791. // front / back
  1792. if ( normal[1] >= 0 )
  1793. return CUBEMAP_FACE_BACK;
  1794. return CUBEMAP_FACE_FRONT;
  1795. }
  1796. }
  1797. // top / bottom
  1798. if ( normal[2] >= 0 )
  1799. return CUBEMAP_FACE_UP;
  1800. return CUBEMAP_FACE_DOWN;
  1801. }
  1802. static void CalcColor( SphereCalc_t *pCalc, int iFace, const Vector &normal, unsigned char *color )
  1803. {
  1804. float x, y, w;
  1805. int size = pCalc->m_iSize;
  1806. float hw = 0.5 * size;
  1807. if ( (iFace == CUBEMAP_FACE_LEFT) || (iFace == CUBEMAP_FACE_RIGHT) )
  1808. {
  1809. w = hw / normal[0];
  1810. x = -normal[2];
  1811. y = -normal[1];
  1812. if ( iFace == CUBEMAP_FACE_LEFT )
  1813. y = -y;
  1814. }
  1815. else if ( (iFace == CUBEMAP_FACE_FRONT) || (iFace == CUBEMAP_FACE_BACK) )
  1816. {
  1817. w = hw / normal[1];
  1818. x = normal[0];
  1819. y = normal[2];
  1820. if ( iFace == CUBEMAP_FACE_FRONT )
  1821. x = -x;
  1822. }
  1823. else
  1824. {
  1825. w = hw / normal[2];
  1826. x = -normal[0];
  1827. y = -normal[1];
  1828. if ( iFace == CUBEMAP_FACE_UP )
  1829. x = -x;
  1830. }
  1831. x = (x * w) + hw - 0.5;
  1832. y = (y * w) + hw - 0.5;
  1833. int u = (int)(x+0.5);
  1834. int v = (int)(y+0.5);
  1835. if ( u < 0 ) u = 0;
  1836. else if ( u > (size-1) ) u = (size-1);
  1837. if ( v < 0 ) v = 0;
  1838. else if ( v > (size-1) ) v = (size-1);
  1839. int offset = (v * size + u) * 4;
  1840. unsigned char *pPix = pCalc->m_ppCubeFaces[iFace] + offset;
  1841. color[0] = pPix[0];
  1842. color[1] = pPix[1];
  1843. color[2] = pPix[2];
  1844. color[3] = pPix[3];
  1845. }
  1846. //-----------------------------------------------------------------------------
  1847. // Computes the spheremap color at a particular (x,y) texcoord
  1848. //-----------------------------------------------------------------------------
  1849. static void CalcSphereColor( SphereCalc_t *pCalc, float x, float y )
  1850. {
  1851. Vector normal;
  1852. float flRadiusSq = x*x + y*y;
  1853. if (flRadiusSq > pCalc->m_flRadiusSq)
  1854. {
  1855. // Force a glancing reflection
  1856. normal.Init( 0, 1, 0 );
  1857. }
  1858. else
  1859. {
  1860. // Compute the z distance based on x*x + y*y + z*z = r*r
  1861. float z = sqrt( pCalc->m_flRadiusSq - flRadiusSq );
  1862. // Here's the untransformed surface normal
  1863. normal.Init( x, y, z );
  1864. normal *= pCalc->m_flOORadius;
  1865. }
  1866. // Transform the normal based on the actual view direction
  1867. TransformNormal( pCalc, normal );
  1868. // Compute the reflection vector (full spheremap solution)
  1869. // R = 2 * (N dot L)N - L
  1870. Vector vecReflect;
  1871. float nDotL = DotProduct( normal, pCalc->m_vecLookDir );
  1872. VectorMA( pCalc->m_vecLookDir, -2.0f * nDotL, normal, vecReflect );
  1873. vecReflect *= -1.0f;
  1874. int iFace = CalcFaceIndex( vecReflect );
  1875. CalcColor( pCalc, iFace, vecReflect, pCalc->m_pColor );
  1876. }
  1877. //-----------------------------------------------------------------------------
  1878. // Computes the spheremap color at a particular (x,y) texcoord
  1879. //-----------------------------------------------------------------------------
  1880. static void CalcHemisphereColor( SphereCalc_t *pCalc, float x, float y )
  1881. {
  1882. Vector normal;
  1883. float flRadiusSq = x*x + y*y;
  1884. if (flRadiusSq > pCalc->m_flRadiusSq)
  1885. {
  1886. normal.Init( x, y, 0.0f );
  1887. VectorNormalize( normal );
  1888. normal *= pCalc->m_flRadiusSq;
  1889. flRadiusSq = pCalc->m_flRadiusSq;
  1890. }
  1891. // Compute the z distance based on x*x + y*y + z*z = r*r
  1892. float z = sqrt( pCalc->m_flRadiusSq - flRadiusSq );
  1893. // Here's the untransformed surface normal
  1894. normal.Init( x, y, z );
  1895. normal *= pCalc->m_flOORadius;
  1896. // Transform the normal based on the actual view direction
  1897. TransformNormal( pCalc, normal );
  1898. // printf( "x: %f y: %f normal: %f %f %f\n", x, y, normal.x, normal.y, normal.z );
  1899. /*
  1900. // Compute the reflection vector (full spheremap solution)
  1901. // R = 2 * (N dot L)N - L
  1902. Vector vecReflect;
  1903. float nDotL = DotProduct( normal, pCalc->m_vecLookDir );
  1904. VectorMA( pCalc->m_vecLookDir, -2.0f * nDotL, normal, vecReflect );
  1905. vecReflect *= -1.0f;
  1906. */
  1907. int iFace = CalcFaceIndex( normal );
  1908. CalcColor( pCalc, iFace, normal, pCalc->m_pColor );
  1909. #if 0
  1910. pCalc->m_pColor[0] = normal[0] * 127 + 127;
  1911. pCalc->m_pColor[1] = normal[1] * 127 + 127;
  1912. pCalc->m_pColor[2] = normal[2] * 127 + 127;
  1913. #endif
  1914. }
  1915. //-----------------------------------------------------------------------------
  1916. // Makes a single frame of spheremap
  1917. //-----------------------------------------------------------------------------
  1918. void CVTFTexture::ComputeSpheremapFrame( unsigned char **ppCubeFaces, unsigned char *pSpheremap, LookDir_t lookDir )
  1919. {
  1920. SphereCalc_t sphere;
  1921. CalcInit( &sphere, m_nWidth, ppCubeFaces, lookDir );
  1922. int offset = 0;
  1923. for ( int y = 0; y < m_nHeight; y++ )
  1924. {
  1925. for ( int x = 0; x < m_nWidth; x++ )
  1926. {
  1927. int r = 0, g = 0, b = 0, a = 0;
  1928. float u = (float)x - m_nWidth * 0.5f;
  1929. float v = m_nHeight * 0.5f - (float)y;
  1930. CalcSphereColor( &sphere, u, v );
  1931. r += sphere.m_pColor[0];
  1932. g += sphere.m_pColor[1];
  1933. b += sphere.m_pColor[2];
  1934. a += sphere.m_pColor[3];
  1935. CalcSphereColor( &sphere, u + 0.25, v );
  1936. r += sphere.m_pColor[0];
  1937. g += sphere.m_pColor[1];
  1938. b += sphere.m_pColor[2];
  1939. a += sphere.m_pColor[3];
  1940. v += 0.25;
  1941. CalcSphereColor( &sphere, u + 0.25, v );
  1942. r += sphere.m_pColor[0];
  1943. g += sphere.m_pColor[1];
  1944. b += sphere.m_pColor[2];
  1945. a += sphere.m_pColor[3];
  1946. CalcSphereColor( &sphere, u, v );
  1947. r += sphere.m_pColor[0];
  1948. g += sphere.m_pColor[1];
  1949. b += sphere.m_pColor[2];
  1950. a += sphere.m_pColor[3];
  1951. pSpheremap[ offset + 0 ] = r >> 2;
  1952. pSpheremap[ offset + 1 ] = g >> 2;
  1953. pSpheremap[ offset + 2 ] = b >> 2;
  1954. pSpheremap[ offset + 3 ] = a >> 2;
  1955. offset += 4;
  1956. }
  1957. }
  1958. }
  1959. void CVTFTexture::ComputeHemispheremapFrame( unsigned char **ppCubeFaces, unsigned char *pSpheremap, LookDir_t lookDir )
  1960. {
  1961. SphereCalc_t sphere;
  1962. CalcInit( &sphere, m_nWidth, ppCubeFaces, lookDir );
  1963. int offset = 0;
  1964. for ( int y = 0; y < m_nHeight; y++ )
  1965. {
  1966. for ( int x = 0; x < m_nWidth; x++ )
  1967. {
  1968. int r = 0, g = 0, b = 0, a = 0;
  1969. float u = (float)x - m_nWidth * 0.5f;
  1970. float v = m_nHeight * 0.5f - (float)y;
  1971. CalcHemisphereColor( &sphere, u, v );
  1972. r += sphere.m_pColor[0];
  1973. g += sphere.m_pColor[1];
  1974. b += sphere.m_pColor[2];
  1975. a += sphere.m_pColor[3];
  1976. CalcHemisphereColor( &sphere, u + 0.25, v );
  1977. r += sphere.m_pColor[0];
  1978. g += sphere.m_pColor[1];
  1979. b += sphere.m_pColor[2];
  1980. a += sphere.m_pColor[3];
  1981. v += 0.25;
  1982. CalcHemisphereColor( &sphere, u + 0.25, v );
  1983. r += sphere.m_pColor[0];
  1984. g += sphere.m_pColor[1];
  1985. b += sphere.m_pColor[2];
  1986. a += sphere.m_pColor[3];
  1987. CalcHemisphereColor( &sphere, u, v );
  1988. r += sphere.m_pColor[0];
  1989. g += sphere.m_pColor[1];
  1990. b += sphere.m_pColor[2];
  1991. a += sphere.m_pColor[3];
  1992. pSpheremap[ offset + 0 ] = r >> 2;
  1993. pSpheremap[ offset + 1 ] = g >> 2;
  1994. pSpheremap[ offset + 2 ] = b >> 2;
  1995. pSpheremap[ offset + 3 ] = a >> 2;
  1996. offset += 4;
  1997. }
  1998. }
  1999. }
  2000. //-----------------------------------------------------------------------------
  2001. // Generate spheremap based on the current images (only works for cubemaps)
  2002. // The look dir indicates the direction of the center of the sphere
  2003. //-----------------------------------------------------------------------------
  2004. void CVTFTexture::GenerateSpheremap( LookDir_t lookDir )
  2005. {
  2006. if (!IsCubeMap())
  2007. return;
  2008. /*
  2009. // HDRFIXME: Need to re-enable this.
  2010. // Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2011. // We'll be doing our work in IMAGE_FORMAT_RGBA8888 mode 'cause it's easier
  2012. unsigned char *pCubeMaps[6];
  2013. // Allocate the bits for the spheremap
  2014. Assert( m_nDepth == 1 );
  2015. int iMemRequired = ComputeFaceSize( 0, IMAGE_FORMAT_RGBA8888 );
  2016. unsigned char *pSphereMapBits = (unsigned char *)MemAllocScratch(iMemRequired);
  2017. // Generate a spheremap for each frame of the cubemap
  2018. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  2019. {
  2020. // Point to our own textures (highest mip level)
  2021. for (int iFace = 0; iFace < 6; ++iFace)
  2022. {
  2023. pCubeMaps[iFace] = ImageData( iFrame, iFace, 0 );
  2024. }
  2025. // Compute the spheremap of the top LOD
  2026. // HDRFIXME: Make this work?
  2027. if( m_Format == IMAGE_FORMAT_RGBA8888 )
  2028. {
  2029. ComputeSpheremapFrame( pCubeMaps, pSphereMapBits, lookDir );
  2030. }
  2031. // Compute the mip levels of the spheremap, converting from RGBA8888 to our format
  2032. unsigned char *pFinalSphereMapBits = ImageData( iFrame, CUBEMAP_FACE_SPHEREMAP, 0 );
  2033. ImageLoader::GenerateMipmapLevels( pSphereMapBits, pFinalSphereMapBits,
  2034. m_nWidth, m_nHeight, m_nDepth, m_Format, 2.2, 2.2, m_nMipCount );
  2035. }
  2036. // Free memory
  2037. MemFreeScratch();
  2038. */
  2039. }
  2040. void CVTFTexture::GenerateHemisphereMap( unsigned char *pSphereMapBitsRGBA, int targetWidth,
  2041. int targetHeight, LookDir_t lookDir, int iFrame )
  2042. {
  2043. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2044. unsigned char *pCubeMaps[6];
  2045. // Point to our own textures (highest mip level)
  2046. for (int iFace = 0; iFace < 6; ++iFace)
  2047. {
  2048. pCubeMaps[iFace] = ImageData( iFrame, iFace, 0 );
  2049. }
  2050. // Compute the spheremap of the top LOD
  2051. ComputeHemispheremapFrame( pCubeMaps, pSphereMapBitsRGBA, lookDir );
  2052. }
  2053. //-----------------------------------------------------------------------------
  2054. // Rotate the image depending on what iFace we've got...
  2055. // We need to do this because we define the cube textures in a different
  2056. // format from DX8.
  2057. //-----------------------------------------------------------------------------
  2058. static void FixCubeMapFacing( unsigned char* pImage, int cubeFaceID, int size, ImageFormat fmt )
  2059. {
  2060. int retVal;
  2061. switch( cubeFaceID )
  2062. {
  2063. case CUBEMAP_FACE_RIGHT: // +x
  2064. retVal = ImageLoader::RotateImageLeft( pImage, pImage, size, fmt );
  2065. Assert( retVal );
  2066. retVal = ImageLoader::FlipImageVertically( pImage, pImage, size, size, fmt );
  2067. Assert( retVal );
  2068. break;
  2069. case CUBEMAP_FACE_LEFT: // -x
  2070. retVal = ImageLoader::RotateImageLeft( pImage, pImage, size, fmt );
  2071. Assert( retVal );
  2072. retVal = ImageLoader::FlipImageHorizontally( pImage, pImage, size, size, fmt );
  2073. Assert( retVal );
  2074. break;
  2075. case CUBEMAP_FACE_BACK: // +y
  2076. retVal = ImageLoader::RotateImage180( pImage, pImage, size, fmt );
  2077. Assert( retVal );
  2078. retVal = ImageLoader::FlipImageHorizontally( pImage, pImage, size, size, fmt );
  2079. Assert( retVal );
  2080. break;
  2081. case CUBEMAP_FACE_FRONT: // -y
  2082. retVal = ImageLoader::FlipImageHorizontally( pImage, pImage, size, size, fmt );
  2083. Assert( retVal );
  2084. break;
  2085. case CUBEMAP_FACE_UP: // +z
  2086. retVal = ImageLoader::RotateImageLeft( pImage, pImage, size, fmt );
  2087. Assert( retVal );
  2088. retVal = ImageLoader::FlipImageVertically( pImage, pImage, size, size, fmt );
  2089. Assert( retVal );
  2090. break;
  2091. case CUBEMAP_FACE_DOWN: // -z
  2092. retVal = ImageLoader::FlipImageHorizontally( pImage, pImage, size, size, fmt );
  2093. Assert( retVal );
  2094. retVal = ImageLoader::RotateImageLeft( pImage, pImage, size, fmt );
  2095. Assert( retVal );
  2096. break;
  2097. }
  2098. }
  2099. //-----------------------------------------------------------------------------
  2100. // Fixes the cubemap faces orientation from our standard to what the material system needs
  2101. //-----------------------------------------------------------------------------
  2102. void CVTFTexture::FixCubemapFaceOrientation( )
  2103. {
  2104. if (!IsCubeMap())
  2105. return;
  2106. Assert( !ImageLoader::IsCompressed( m_Format ) );
  2107. for (int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel)
  2108. {
  2109. int iMipSize, iTemp, nDepth;
  2110. ComputeMipLevelDimensions( iMipLevel, &iMipSize, &iTemp, &nDepth );
  2111. Assert( (iMipSize == iTemp) && (nDepth == 1) );
  2112. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  2113. {
  2114. for (int iFace = 0; iFace < 6; ++iFace)
  2115. {
  2116. FixCubeMapFacing( ImageData( iFrame, iFace, iMipLevel ), iFace, iMipSize, m_Format );
  2117. }
  2118. }
  2119. }
  2120. }
  2121. void CVTFTexture::NormalizeTopMipLevel()
  2122. {
  2123. if( !( m_nFlags & TEXTUREFLAGS_NORMAL ) )
  2124. return;
  2125. int nSrcWidth, nSrcHeight, nSrcDepth;
  2126. int srcMipLevel = 0;
  2127. ComputeMipLevelDimensions( srcMipLevel, &nSrcWidth, &nSrcHeight, &nSrcDepth );
  2128. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  2129. {
  2130. for (int iFace = 0; iFace < m_nFaceCount; ++iFace)
  2131. {
  2132. unsigned char *pSrcLevel = ImageData( iFrame, iFace, srcMipLevel );
  2133. ImageLoader::NormalizeNormalMapRGBA8888( pSrcLevel, nSrcWidth * nSrcHeight * nSrcDepth );
  2134. }
  2135. }
  2136. }
  2137. static inline int PixelOffset( int x, int y, int w, int h )
  2138. {
  2139. x = ( x + w ) % w;
  2140. y = ( y + h ) % h;
  2141. return y * w + x;
  2142. }
  2143. void CVTFTexture::Compute2DGradient()
  2144. {
  2145. int nSrcWidth, nSrcHeight, nSrcDepth;
  2146. int srcMipLevel = 0;
  2147. ComputeMipLevelDimensions( srcMipLevel, &nSrcWidth, &nSrcHeight, &nSrcDepth );
  2148. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2149. {
  2150. for ( int iFace = 0; iFace < m_nFaceCount; ++iFace )
  2151. {
  2152. unsigned char *pSrcLevel = ImageData( iFrame, iFace, srcMipLevel );
  2153. for ( int iSlice = 0; iSlice < nSrcDepth; iSlice++ )
  2154. {
  2155. uint8 *pSliceData = pSrcLevel + iSlice * nSrcWidth * nSrcHeight * 4;
  2156. // Copy G to A channel
  2157. for ( int iY = 0; iY < nSrcHeight; iY++ )
  2158. {
  2159. for ( int iX = 0; iX < nSrcWidth; iX++ )
  2160. {
  2161. // Copy green channel into alpha
  2162. pSliceData[ PixelOffset( iX, iY, nSrcWidth, nSrcHeight ) * 4 + 3 ] = pSliceData[ PixelOffset( iX, iY, nSrcWidth, nSrcHeight ) * 4 + 1 ];
  2163. }
  2164. }
  2165. for ( int iY = 0; iY < nSrcHeight; iY++ )
  2166. {
  2167. for ( int iX = 0; iX < nSrcWidth; iX++ )
  2168. {
  2169. float flSobelX, flSobelY;
  2170. flSobelX = pSliceData[ PixelOffset( iX - 1, iY - 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * -1.0f; // Apply Sobel filter in X, with wrapping texel accesses
  2171. flSobelX += pSliceData[ PixelOffset( iX - 1, iY + 0, nSrcWidth, nSrcHeight ) * 4 + 3 ] * -2.0f; //
  2172. flSobelX += pSliceData[ PixelOffset( iX - 1, iY + 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * -1.0f; // [ -1 0 1 ]
  2173. flSobelX += pSliceData[ PixelOffset( iX + 1, iY - 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * 1.0f; // [ -2 0 2 ]
  2174. flSobelX += pSliceData[ PixelOffset( iX + 1, iY + 0, nSrcWidth, nSrcHeight ) * 4 + 3 ] * 2.0f; // [ -1 0 1 ]
  2175. flSobelX += pSliceData[ PixelOffset( iX + 1, iY + 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * 1.0f; //
  2176. flSobelX /= 255.0f;
  2177. flSobelY = pSliceData[ PixelOffset( iX - 1, iY - 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * -1.0f; // Apply Sobel filter in Y, with wrapping texel accesses
  2178. flSobelY += pSliceData[ PixelOffset( iX + 0, iY - 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * -2.0f; //
  2179. flSobelY += pSliceData[ PixelOffset( iX + 1, iY - 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * -1.0f; // [ -1 -2 -1 ]
  2180. flSobelY += pSliceData[ PixelOffset( iX - 1, iY + 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * 1.0f; // [ 0 0 0 ]
  2181. flSobelY += pSliceData[ PixelOffset( iX + 0, iY + 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * 2.0f; // [ 1 2 1 ]
  2182. flSobelY += pSliceData[ PixelOffset( iX + 1, iY + 1, nSrcWidth, nSrcHeight ) * 4 + 3 ] * 1.0f; //
  2183. flSobelY /= 255.0f;
  2184. float flLength = sqrtf( ( flSobelX * flSobelX ) + ( flSobelY * flSobelY ) );
  2185. float flX = ( flLength == 0.0f ) ? 0.5f : ( ( flSobelX / flLength ) + 1.0f ) / 2.0f; // Normalized, since we care about direction, but not magnitude
  2186. float flY = ( flLength == 0.0f ) ? 0.5f : ( ( -flSobelY / flLength ) + 1.0f ) / 2.0f; // Inverting Y to match source1-style normals
  2187. // 2D gradient story in R and G
  2188. pSliceData[ PixelOffset( iX, iY, nSrcWidth, nSrcHeight ) * 4 + 0 ] = uint8( flX*255.0f );
  2189. pSliceData[ PixelOffset( iX, iY, nSrcWidth, nSrcHeight ) * 4 + 1 ] = uint8( flY*255.0f );
  2190. pSliceData[ PixelOffset( iX, iY, nSrcWidth, nSrcHeight ) * 4 + 2 ] = 0;
  2191. }
  2192. }
  2193. }
  2194. }
  2195. }
  2196. }
  2197. //-----------------------------------------------------------------------------
  2198. // Converts PC Gamma to 360 Gamma
  2199. //-----------------------------------------------------------------------------
  2200. static void ConvertPcTo360SrgbRGBA8888( unsigned char *pImageData, int iWidth, int iHeight )
  2201. {
  2202. int nBytesPerPixel = 4;
  2203. for ( int32 h = 0; h < iHeight; h++ ) // For each row
  2204. {
  2205. for ( int32 w = 0; w < iWidth; w++ ) // For each texel in this row
  2206. {
  2207. unsigned char *pRGB[3] = { NULL, NULL, NULL };
  2208. pRGB[0] = &( pImageData[ ( h * iWidth * nBytesPerPixel ) + ( w * nBytesPerPixel ) + 0 ] ); // Red
  2209. pRGB[1] = &( pImageData[ ( h * iWidth * nBytesPerPixel ) + ( w * nBytesPerPixel ) + 1 ] ); // Green
  2210. pRGB[2] = &( pImageData[ ( h * iWidth * nBytesPerPixel ) + ( w * nBytesPerPixel ) + 2 ] ); // Blue
  2211. // Modify RGB data in place
  2212. for ( int j = 0; j < 3; j++ ) // For red, green, blue
  2213. {
  2214. float flSrgbGamma = float( *( pRGB[j] ) ) / 255.0f;
  2215. float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
  2216. fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
  2217. *( pRGB[j] ) = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
  2218. }
  2219. }
  2220. }
  2221. }
  2222. //-----------------------------------------------------------------------------
  2223. // Generates mipmaps from the base mip levels
  2224. //-----------------------------------------------------------------------------
  2225. void CVTFTexture::GenerateMipmaps()
  2226. {
  2227. // Go ahead and generate mipmaps even if we don't want 'em in the vtf.
  2228. // if( ( Flags() & ( TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD ) ) == ( TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD ) )
  2229. // {
  2230. // return;
  2231. // }
  2232. Assert( m_Format == IMAGE_FORMAT_RGBA8888 || m_Format == IMAGE_FORMAT_RGB323232F || m_Format == IMAGE_FORMAT_RGBA32323232F );
  2233. // FIXME: Should we be doing anything special for normalmaps other than a final normalization pass?
  2234. ImageLoader::ResampleInfo_t info;
  2235. info.m_nSrcWidth = m_nWidth;
  2236. info.m_nSrcHeight = m_nHeight;
  2237. info.m_nSrcDepth = m_nDepth;
  2238. info.m_flSrcGamma = 2.2f;
  2239. info.m_flDestGamma = 2.2f;
  2240. info.m_nFlags = 0;
  2241. bool bNormalMap = ( Flags() & TEXTUREFLAGS_NORMAL ) || ( m_Options.flags0 & VtfProcessingOptions::OPT_NORMAL_DUDV );
  2242. bool bAlphaTest = ( ( m_Options.flags0 & VtfProcessingOptions::OPT_MIP_ALPHATEST ) != 0 );
  2243. if ( bAlphaTest )
  2244. {
  2245. info.m_nFlags |= ImageLoader::RESAMPLE_ALPHATEST;
  2246. if ( m_flAlphaThreshhold >= 0 )
  2247. {
  2248. info.m_flAlphaThreshhold = m_flAlphaThreshhold;
  2249. }
  2250. if ( m_flAlphaHiFreqThreshhold >= 0 )
  2251. {
  2252. info.m_flAlphaHiFreqThreshhold = m_flAlphaHiFreqThreshhold;
  2253. }
  2254. }
  2255. if ( m_Options.flags0 & VtfProcessingOptions::OPT_FILTER_NICE )
  2256. {
  2257. info.m_nFlags |= ImageLoader::RESAMPLE_NICE_FILTER;
  2258. }
  2259. if ( Flags() & TEXTUREFLAGS_CLAMPS )
  2260. {
  2261. info.m_nFlags |= ImageLoader::RESAMPLE_CLAMPS;
  2262. }
  2263. if ( Flags() & TEXTUREFLAGS_CLAMPT )
  2264. {
  2265. info.m_nFlags |= ImageLoader::RESAMPLE_CLAMPT;
  2266. }
  2267. if ( Flags() & TEXTUREFLAGS_CLAMPU )
  2268. {
  2269. info.m_nFlags |= ImageLoader::RESAMPLE_CLAMPU;
  2270. }
  2271. // Compute how many mips are above "visible mip0"
  2272. int numMipsClampedLod = 0;
  2273. if ( TextureLODControlSettings_t const *pLodSettings = ( TextureLODControlSettings_t const * ) GetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, NULL ) )
  2274. {
  2275. int iClampX = 1 << MIN( pLodSettings->m_ResolutionClampX, pLodSettings->m_ResolutionClampX_360 );
  2276. int iClampY = 1 << MIN( pLodSettings->m_ResolutionClampX, pLodSettings->m_ResolutionClampX_360 );
  2277. while ( iClampX < m_nWidth || iClampY < m_nHeight )
  2278. {
  2279. ++ numMipsClampedLod;
  2280. iClampX <<= 1;
  2281. iClampY <<= 1;
  2282. }
  2283. }
  2284. if ( m_Options.flags0 & VtfProcessingOptions::OPT_SRGB_PC_TO_360 )
  2285. {
  2286. int iMipLevel = 0; // Perform srgb-correction on mip0, it will be propagated down the mip chain
  2287. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2288. {
  2289. for ( int iFace = 0; iFace < m_nFaceCount; ++iFace )
  2290. {
  2291. unsigned char *pImageData = ImageData( iFrame, iFace, iMipLevel );
  2292. int w, h, d;
  2293. ComputeMipLevelDimensions( iMipLevel, &w, &h, &d );
  2294. if ( m_Format == IMAGE_FORMAT_RGB323232F )
  2295. {
  2296. // We only convert 8-bit art, so do nothing for 32f?
  2297. }
  2298. else
  2299. {
  2300. ConvertPcTo360SrgbRGBA8888( pImageData, w, h );
  2301. }
  2302. }
  2303. }
  2304. // Mark as pwl corrected, used as a debugging aid in VXConsole
  2305. m_nFlags |= TEXTUREFLAGS_PWL_CORRECTED;
  2306. }
  2307. else
  2308. {
  2309. // Mark not pwl
  2310. m_nFlags &= ~TEXTUREFLAGS_PWL_CORRECTED;
  2311. }
  2312. for ( int iMipLevel = 1; iMipLevel < m_nMipCount; ++iMipLevel )
  2313. {
  2314. ComputeMipLevelDimensions( iMipLevel, &info.m_nDestWidth, &info.m_nDestHeight, &info.m_nDestDepth );
  2315. if ( m_Options.flags0 & VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP )
  2316. {
  2317. for ( int ch = 0; ch < 3; ++ ch )
  2318. info.m_flColorScale[ch] = 1.0f / ( float )( 1 << iMipLevel );
  2319. }
  2320. // don't use the 0th mip level since NICE filtering blows up!
  2321. int nSrcMipLevel = iMipLevel - 4;
  2322. if ( nSrcMipLevel < 0 )
  2323. nSrcMipLevel = 0;
  2324. // Decay options
  2325. bool bMipBlendActive = false;
  2326. char chChannels[4] = { 'R', 'G', 'B', 'A' };
  2327. for ( int ch = 0; ch < 4; ++ ch )
  2328. {
  2329. int iLastNonDecayMip = numMipsClampedLod + int( m_Options.numNotDecayMips[ch] );
  2330. if ( iLastNonDecayMip > m_nMipCount )
  2331. iLastNonDecayMip = m_nMipCount - 1;
  2332. int numDecayMips = m_nMipCount - iLastNonDecayMip - 1;
  2333. if ( numDecayMips < 1 )
  2334. numDecayMips = 1;
  2335. // Decay is only active starting from numDecayMips
  2336. if ( !( ( ( iMipLevel == m_nMipCount - 1 ) || ( iMipLevel > iLastNonDecayMip ) ) && // last 1x1 mip or past clamped and skipped
  2337. ( m_Options.flags0 & ( VtfProcessingOptions::OPT_DECAY_R << ch ) ) ) ) // the channel has decay
  2338. continue;
  2339. // Color goal
  2340. info.m_flColorGoal[ch] = m_Options.clrDecayGoal[ch];
  2341. // Color scale
  2342. if ( iMipLevel == m_nMipCount - 1 )
  2343. {
  2344. info.m_flColorScale[ch] = 0.0f;
  2345. }
  2346. else if ( m_Options.flags0 & ( VtfProcessingOptions::OPT_DECAY_EXP_R << ch ) )
  2347. {
  2348. info.m_flColorScale[ch] = pow( m_Options.fDecayExponentBase[ch], iMipLevel - iLastNonDecayMip );
  2349. }
  2350. else
  2351. {
  2352. info.m_flColorScale[ch] = 1.0f - float( iMipLevel - iLastNonDecayMip ) / float( numDecayMips );
  2353. }
  2354. if ( !bMipBlendActive )
  2355. {
  2356. bMipBlendActive = true;
  2357. printf( "Blending mip%d %dx%d to", iMipLevel, info.m_nDestWidth, info.m_nDestHeight );
  2358. }
  2359. printf( " %c=%d ~%d%%", chChannels[ch], m_Options.clrDecayGoal[ch], int( (1.f - info.m_flColorScale[ch]) * 100.0f + 0.5f ) );
  2360. }
  2361. if ( bMipBlendActive )
  2362. printf( "\n" );
  2363. if ( bNormalMap )
  2364. {
  2365. info.m_nFlags |= ImageLoader::RESAMPLE_NORMALMAP;
  2366. // Normal maps xyz decays to 127.f
  2367. for ( int ch = 0; ch < 3; ++ ch )
  2368. info.m_flColorGoal[ch] = 127.0f;
  2369. }
  2370. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2371. {
  2372. for ( int iFace = 0; iFace < m_nFaceCount; ++iFace )
  2373. {
  2374. unsigned char *pSrcLevel = ImageData( iFrame, iFace, nSrcMipLevel );
  2375. unsigned char *pDstLevel = ImageData( iFrame, iFace, iMipLevel );
  2376. info.m_pSrc = pSrcLevel;
  2377. info.m_pDest = pDstLevel;
  2378. ComputeMipLevelDimensions( nSrcMipLevel, &info.m_nSrcWidth, &info.m_nSrcHeight, &info.m_nSrcDepth );
  2379. if( m_Format == IMAGE_FORMAT_RGBA32323232F )
  2380. {
  2381. ImageLoader::ResampleRGBA32323232F( info );
  2382. }
  2383. else if( m_Format == IMAGE_FORMAT_RGB323232F )
  2384. {
  2385. ImageLoader::ResampleRGB323232F( info );
  2386. }
  2387. else
  2388. {
  2389. ImageLoader::ResampleRGBA8888( info );
  2390. }
  2391. if ( Flags() & TEXTUREFLAGS_NORMAL )
  2392. {
  2393. ImageLoader::NormalizeNormalMapRGBA8888( pDstLevel, info.m_nDestWidth * info.m_nDestHeight * info.m_nDestDepth );
  2394. }
  2395. }
  2396. }
  2397. }
  2398. }
  2399. void CVTFTexture::PutOneOverMipLevelInAlpha()
  2400. {
  2401. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2402. for (int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel)
  2403. {
  2404. int nMipWidth, nMipHeight, nMipDepth;
  2405. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2406. int size = nMipWidth * nMipHeight * nMipDepth;
  2407. unsigned char ooMipLevel = ( unsigned char )( 255.0f * ( 1.0f / ( float )( 1 << iMipLevel ) ) );
  2408. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  2409. {
  2410. for (int iFace = 0; iFace < m_nFaceCount; ++iFace)
  2411. {
  2412. unsigned char *pDstLevel = ImageData( iFrame, iFace, iMipLevel );
  2413. unsigned char *pDst;
  2414. for( pDst = pDstLevel; pDst < pDstLevel + size * 4; pDst += 4 )
  2415. {
  2416. pDst[3] = ooMipLevel;
  2417. }
  2418. }
  2419. }
  2420. }
  2421. }
  2422. void CVTFTexture::PremultAlphaWithMipFraction()
  2423. {
  2424. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2425. for (int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel)
  2426. {
  2427. int nMipWidth, nMipHeight, nMipDepth;
  2428. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2429. int size = nMipWidth * nMipHeight * nMipDepth;
  2430. int maxAlphaAtMip = clamp ( m_Options.fullAlphaAtMipLevel, 1, m_nMipCount );
  2431. float flMipScale = ( float )( iMipLevel ) / ( float )( maxAlphaAtMip );
  2432. flMipScale = clamp ( flMipScale, 0.0f, 1.0f );
  2433. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  2434. {
  2435. for (int iFace = 0; iFace < m_nFaceCount; ++iFace)
  2436. {
  2437. unsigned char *pDstLevel = ImageData( iFrame, iFace, iMipLevel );
  2438. unsigned char *pDst;
  2439. for( pDst = pDstLevel; pDst < pDstLevel + size * 4; pDst += 4 )
  2440. {
  2441. pDst[3] = ( unsigned char )( ( float )( pDst[3] ) * flMipScale );
  2442. pDst[3] = ( unsigned char )clamp( pDst[3], m_Options.minAlpha, 255 );
  2443. }
  2444. }
  2445. }
  2446. }
  2447. }
  2448. //-----------------------------------------------------------------------------
  2449. // Computes the reflectivity
  2450. //-----------------------------------------------------------------------------
  2451. void CVTFTexture::ComputeReflectivity( )
  2452. {
  2453. // HDRFIXME: fix this when we ahve a new intermediate format
  2454. if( m_Format != IMAGE_FORMAT_RGBA8888 )
  2455. {
  2456. m_vecReflectivity.Init( 0.2f, 0.2f, 0.2f );
  2457. return;
  2458. }
  2459. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2460. int divisor = 0;
  2461. m_vecReflectivity.Init( 0.0f, 0.0f, 0.0f );
  2462. for( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2463. {
  2464. for( int iFace = 0; iFace < m_nFaceCount; ++iFace )
  2465. {
  2466. Vector vecFaceReflect;
  2467. unsigned char* pSrc = ImageData( iFrame, iFace, 0 );
  2468. int nNumPixels = m_nWidth * m_nHeight * m_nDepth;
  2469. VectorClear( vecFaceReflect );
  2470. for (int i = 0; i < nNumPixels; ++i, pSrc += 4 )
  2471. {
  2472. vecFaceReflect[0] += TextureToLinear( pSrc[0] );
  2473. vecFaceReflect[1] += TextureToLinear( pSrc[1] );
  2474. vecFaceReflect[2] += TextureToLinear( pSrc[2] );
  2475. }
  2476. vecFaceReflect /= nNumPixels;
  2477. m_vecReflectivity += vecFaceReflect;
  2478. ++divisor;
  2479. }
  2480. }
  2481. m_vecReflectivity /= divisor;
  2482. }
  2483. //-----------------------------------------------------------------------------
  2484. // Computes the alpha flags
  2485. //-----------------------------------------------------------------------------
  2486. void CVTFTexture::ComputeAlphaFlags()
  2487. {
  2488. // HDRFIXME: hack hack hack
  2489. if( m_Format != IMAGE_FORMAT_RGBA8888 )
  2490. {
  2491. m_nFlags &= ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
  2492. m_Options.flags0 &= ~( VtfProcessingOptions::OPT_MIP_ALPHATEST );
  2493. return;
  2494. }
  2495. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2496. m_nFlags &= ~(TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA);
  2497. if( m_Options.flags0 & VtfProcessingOptions::OPT_SET_ALPHA_ONEOVERMIP )
  2498. {
  2499. m_nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  2500. return;
  2501. }
  2502. for( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2503. {
  2504. for( int iFace = 0; iFace < m_nFaceCount; ++iFace )
  2505. {
  2506. for( int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel )
  2507. {
  2508. // If we're all 0 or all 255, assume it's opaque
  2509. bool bHasZero = false;
  2510. bool bHas255 = false;
  2511. unsigned char* pSrcBits = ImageData( iFrame, iFace, iMipLevel );
  2512. int nMipWidth, nMipHeight, nMipDepth;
  2513. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2514. int nNumPixels = nMipWidth * nMipHeight * nMipDepth;
  2515. while ( --nNumPixels >= 0 )
  2516. {
  2517. if ( pSrcBits[3] == 0 )
  2518. {
  2519. bHasZero = true;
  2520. }
  2521. else if ( pSrcBits[3] == 255 )
  2522. {
  2523. bHas255 = true;
  2524. }
  2525. else
  2526. {
  2527. // Have grey at all? 8 bit alpha baby
  2528. m_nFlags &= ~TEXTUREFLAGS_ONEBITALPHA;
  2529. m_nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  2530. return;
  2531. }
  2532. pSrcBits += 4;
  2533. }
  2534. // If we have both 0 at 255, we're at least one-bit alpha
  2535. if ( bHasZero && bHas255 )
  2536. {
  2537. m_nFlags |= TEXTUREFLAGS_ONEBITALPHA;
  2538. }
  2539. }
  2540. }
  2541. }
  2542. }
  2543. //-----------------------------------------------------------------------------
  2544. // Gets the texture all internally consistent assuming you've loaded
  2545. // mip 0 of all faces of all frames
  2546. //-----------------------------------------------------------------------------
  2547. void CVTFTexture::PostProcess( bool bGenerateSpheremap, LookDir_t lookDir, bool bAllowFixCubemapOrientation, bool bLoadedMiplevels )
  2548. {
  2549. // HDRFIXME: Make sure that all of the below functions check for the proper formats if we get rid of this assert.
  2550. // Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2551. // Set up the cube map faces
  2552. if (IsCubeMap())
  2553. {
  2554. // Rotate the cubemaps so they're appropriate for the material system
  2555. if ( bAllowFixCubemapOrientation )
  2556. FixCubemapFaceOrientation();
  2557. // FIXME: We could theoretically not compute spheremap mip levels
  2558. // in generate spheremaps; should we? The trick is when external
  2559. // clients can be expected to call it
  2560. // Compute the spheremap fallback for cubemaps if we weren't able to load up one...
  2561. if (bGenerateSpheremap)
  2562. GenerateSpheremap(lookDir);
  2563. }
  2564. if ( m_Options.flags0 & VtfProcessingOptions::OPT_COMPUTE_GRADIENT )
  2565. {
  2566. Compute2DGradient();
  2567. }
  2568. // Normalize the top mip level if necessary.
  2569. NormalizeTopMipLevel();
  2570. // Generate mipmap levels
  2571. if ( !bLoadedMiplevels )
  2572. {
  2573. GenerateMipmaps();
  2574. }
  2575. if( m_Options.flags0 & VtfProcessingOptions::OPT_SET_ALPHA_ONEOVERMIP )
  2576. {
  2577. PutOneOverMipLevelInAlpha();
  2578. }
  2579. if( m_Options.flags0 & VtfProcessingOptions::OPT_PREMULT_ALPHA_MIPFRACTION )
  2580. {
  2581. PremultAlphaWithMipFraction();
  2582. }
  2583. // Compute reflectivity
  2584. ComputeReflectivity();
  2585. // Are we 8-bit or 1-bit alpha?
  2586. // NOTE: We have to do this *after* computing the spheremap fallback for
  2587. // cubemaps or it'll throw the flags off
  2588. ComputeAlphaFlags();
  2589. }
  2590. void CVTFTexture::SetPostProcessingSettings( VtfProcessingOptions const *pOptions )
  2591. {
  2592. memset( &m_Options, 0, sizeof( m_Options ) );
  2593. memcpy( &m_Options, pOptions, MIN( sizeof( m_Options ), pOptions->cbSize ) );
  2594. m_Options.cbSize = sizeof( m_Options );
  2595. // Optionally perform the fixups
  2596. }
  2597. //-----------------------------------------------------------------------------
  2598. // Generate the low-res image bits
  2599. //-----------------------------------------------------------------------------
  2600. bool CVTFTexture::ConstructLowResImage()
  2601. {
  2602. // HDRFIXME: hack hack hack
  2603. if( m_Format != IMAGE_FORMAT_RGBA8888 )
  2604. {
  2605. return true;
  2606. }
  2607. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2608. Assert( m_pLowResImageData );
  2609. CUtlMemory<unsigned char> lowResSizeImage;
  2610. lowResSizeImage.EnsureCapacity( m_nLowResImageWidth * m_nLowResImageHeight * 4 );
  2611. ImageLoader::ResampleInfo_t info;
  2612. info.m_pSrc = ImageData(0, 0, 0);
  2613. info.m_pDest = lowResSizeImage.Base();
  2614. info.m_nSrcWidth = m_nWidth;
  2615. info.m_nSrcHeight = m_nHeight;
  2616. info.m_nDestWidth = m_nLowResImageWidth;
  2617. info.m_nDestHeight = m_nLowResImageHeight;
  2618. info.m_flSrcGamma = 2.2f;
  2619. info.m_flDestGamma = 2.2f;
  2620. info.m_nFlags = ImageLoader::RESAMPLE_NICE_FILTER;
  2621. if( !ImageLoader::ResampleRGBA8888( info ) )
  2622. return false;
  2623. // convert to the low-res size version with the correct image format
  2624. unsigned char *tmpImage = lowResSizeImage.Base();
  2625. return ImageLoader::ConvertImageFormat( tmpImage, IMAGE_FORMAT_RGBA8888,
  2626. m_pLowResImageData, m_LowResImageFormat, m_nLowResImageWidth, m_nLowResImageHeight );
  2627. }
  2628. // -----------------------------------------------------------------------------
  2629. // Cubemap edge-filtering functions.
  2630. // -----------------------------------------------------------------------------
  2631. void CVTFTexture::SetupFaceVert( int iMipLevel, int iVert, CEdgePos &out )
  2632. {
  2633. int nMipWidth, nMipHeight, nMipDepth;
  2634. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2635. out.x = out.y = 0;
  2636. if ( iVert == 0 || iVert == 3 )
  2637. {
  2638. out.y = nMipHeight - 1;
  2639. }
  2640. if ( iVert == 2 || iVert == 3 )
  2641. {
  2642. out.x = nMipWidth - 1;
  2643. }
  2644. }
  2645. void CVTFTexture::SetupEdgeIncrement( CEdgePos &start, CEdgePos &end, CEdgePos &inc )
  2646. {
  2647. inc.x = inc.y = 0;
  2648. if ( start.x != end.x )
  2649. {
  2650. Assert( start.y == end.y );
  2651. inc.x = (start.x < end.x) ? 1 : -1;
  2652. }
  2653. else if ( start.y != end.y )
  2654. {
  2655. Assert( start.x == end.x );
  2656. inc.y = (start.y < end.y) ? 1 : -1;
  2657. }
  2658. else
  2659. {
  2660. Assert( false );
  2661. }
  2662. }
  2663. void CVTFTexture::SetupTextureEdgeIncrements(
  2664. int iMipLevel,
  2665. int iFace1Edge,
  2666. int iFace2Edge,
  2667. bool bFlipFace2Edge,
  2668. CEdgeIncrements *incs )
  2669. {
  2670. // Figure out the coordinates of the verts we're blending.
  2671. SetupFaceVert( iMipLevel, iFace1Edge, incs->iFace1Start );
  2672. SetupFaceVert( iMipLevel, (iFace1Edge+1)%4, incs->iFace1End );
  2673. if ( bFlipFace2Edge )
  2674. {
  2675. SetupFaceVert( iMipLevel, (iFace2Edge+1)%4, incs->iFace2Start );
  2676. SetupFaceVert( iMipLevel, iFace2Edge, incs->iFace2End );
  2677. }
  2678. else
  2679. {
  2680. SetupFaceVert( iMipLevel, iFace2Edge, incs->iFace2Start );
  2681. SetupFaceVert( iMipLevel, (iFace2Edge+1)%4, incs->iFace2End );
  2682. }
  2683. // Figure out the increments from start to end.
  2684. SetupEdgeIncrement( incs->iFace1Start, incs->iFace1End, incs->iFace1Inc );
  2685. SetupEdgeIncrement( incs->iFace2Start, incs->iFace2End, incs->iFace2Inc );
  2686. }
  2687. void BlendTexels( unsigned char **texels, int nTexels )
  2688. {
  2689. int sum[4] = { 0, 0, 0, 0 };
  2690. int i;
  2691. for ( i=0; i < nTexels; i++ )
  2692. {
  2693. sum[0] += texels[i][0];
  2694. sum[1] += texels[i][1];
  2695. sum[2] += texels[i][2];
  2696. sum[3] += texels[i][3];
  2697. }
  2698. for ( i=0; i < nTexels; i++ )
  2699. {
  2700. texels[i][0] = (unsigned char)( sum[0] / nTexels );
  2701. texels[i][1] = (unsigned char)( sum[1] / nTexels );
  2702. texels[i][2] = (unsigned char)( sum[2] / nTexels );
  2703. texels[i][3] = (unsigned char)( sum[3] / nTexels );
  2704. }
  2705. }
  2706. void CVTFTexture::BlendCubeMapFaceEdges(
  2707. int iFrame,
  2708. int iMipLevel,
  2709. const CEdgeMatch *pMatch )
  2710. {
  2711. int nMipWidth, nMipHeight, nMipDepth;
  2712. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2713. Assert( nMipDepth == 1 );
  2714. if ( nMipWidth <= 1 || nMipHeight <= 1 )
  2715. return;
  2716. unsigned char *pFace1Data = ImageData( iFrame, pMatch->m_iFaces[0], iMipLevel );
  2717. unsigned char *pFace2Data = ImageData( iFrame, pMatch->m_iFaces[1], iMipLevel );
  2718. CEdgeIncrements incs;
  2719. SetupTextureEdgeIncrements( iMipLevel, pMatch->m_iEdges[0], pMatch->m_iEdges[1], pMatch->m_bFlipFace2Edge, &incs );
  2720. // Do all pixels but the first and the last one (those will be handled when blending corners).
  2721. CEdgePos iFace1Cur = incs.iFace1Start + incs.iFace1Inc;
  2722. CEdgePos iFace2Cur = incs.iFace2Start + incs.iFace2Inc;
  2723. if ( m_Format == IMAGE_FORMAT_DXT1 || m_Format == IMAGE_FORMAT_DXT5 )
  2724. {
  2725. if ( iFace1Cur != incs.iFace1End )
  2726. {
  2727. while ( iFace1Cur != incs.iFace1End )
  2728. {
  2729. // Copy the palette index from image 1 to image 2.
  2730. S3PaletteIndex paletteIndex = S3TC_GetPaletteIndex( pFace1Data, m_Format, nMipWidth, iFace1Cur.x, iFace1Cur.y );
  2731. S3TC_SetPaletteIndex( pFace2Data, m_Format, nMipWidth, iFace2Cur.x, iFace2Cur.y, paletteIndex );
  2732. iFace1Cur += incs.iFace1Inc;
  2733. iFace2Cur += incs.iFace2Inc;
  2734. }
  2735. }
  2736. }
  2737. else if ( m_Format == IMAGE_FORMAT_RGBA8888 )
  2738. {
  2739. if ( iFace1Cur != incs.iFace1End )
  2740. {
  2741. while ( iFace1Cur != incs.iFace1End )
  2742. {
  2743. // Now we know the 2 pixels. Average them and copy the averaged value to both pixels.
  2744. unsigned char *texels[2] =
  2745. {
  2746. pFace1Data + ((iFace1Cur.y * nMipWidth) + iFace1Cur.x) * 4,
  2747. pFace2Data + ((iFace2Cur.y * nMipWidth) + iFace2Cur.x) * 4
  2748. };
  2749. BlendTexels( texels, 2 );
  2750. iFace1Cur += incs.iFace1Inc;
  2751. iFace2Cur += incs.iFace2Inc;
  2752. }
  2753. }
  2754. }
  2755. else
  2756. {
  2757. Error( "BlendCubeMapFaceEdges: unsupported image format (%d)", (int)m_Format );
  2758. }
  2759. }
  2760. void CVTFTexture::BlendCubeMapFaceCorners(
  2761. int iFrame,
  2762. int iMipLevel,
  2763. const CCornerMatch *pMatch )
  2764. {
  2765. int nMipWidth, nMipHeight, nMipDepth;
  2766. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2767. Assert( nMipDepth == 1 );
  2768. // Setup the coordinates of each texel.
  2769. CEdgePos texelPos[3];
  2770. unsigned char *pImageData[3];
  2771. int iEdge;
  2772. for ( iEdge=0; iEdge < 3; iEdge++ )
  2773. {
  2774. SetupFaceVert( iMipLevel, pMatch->m_iFaceEdges[iEdge], texelPos[iEdge] );
  2775. pImageData[iEdge] = ImageData( iFrame, pMatch->m_iFaces[iEdge], iMipLevel );
  2776. }
  2777. if ( m_Format == IMAGE_FORMAT_DXT1 || m_Format == IMAGE_FORMAT_DXT5 )
  2778. {
  2779. if ( nMipWidth < 4 || nMipHeight < 4 )
  2780. return;
  2781. // Copy the first palette index to the other blocks.
  2782. S3PaletteIndex paletteIndex = S3TC_GetPaletteIndex( pImageData[0], m_Format, nMipWidth, texelPos[0].x, texelPos[0].y );
  2783. S3TC_SetPaletteIndex( pImageData[1], m_Format, nMipWidth, texelPos[1].x, texelPos[1].y, paletteIndex );
  2784. S3TC_SetPaletteIndex( pImageData[2], m_Format, nMipWidth, texelPos[2].x, texelPos[2].y, paletteIndex );
  2785. }
  2786. else if ( m_Format == IMAGE_FORMAT_RGBA8888 )
  2787. {
  2788. // Setup pointers to the 3 corner texels.
  2789. unsigned char *texels[3];
  2790. for ( iEdge=0; iEdge < 3; iEdge++ )
  2791. {
  2792. CEdgePos facePos;
  2793. SetupFaceVert( iMipLevel, pMatch->m_iFaceEdges[iEdge], facePos );
  2794. texels[iEdge] = pImageData[iEdge];
  2795. texels[iEdge] += (facePos.y * nMipWidth + facePos.x) * 4;
  2796. }
  2797. // Now blend the texels.
  2798. BlendTexels( texels, 3 );
  2799. }
  2800. else
  2801. {
  2802. Assert( false );
  2803. }
  2804. }
  2805. void CVTFTexture::BuildCubeMapMatchLists(
  2806. CEdgeMatch edgeMatches[NUM_EDGE_MATCHES],
  2807. CCornerMatch cornerMatches[NUM_CORNER_MATCHES],
  2808. bool bSkybox )
  2809. {
  2810. int **faceVertsList = bSkybox ? g_skybox_FaceVerts : g_FaceVerts;
  2811. // For each face, look for matching edges on other faces.
  2812. int nTotalEdgesMatched = 0;
  2813. for ( int iFace = 0; iFace < 6; iFace++ )
  2814. {
  2815. for ( int iEdge=0; iEdge < 4; iEdge++ )
  2816. {
  2817. int i1 = faceVertsList[iFace][iEdge];
  2818. int i2 = faceVertsList[iFace][(iEdge+1)%4];
  2819. // Only look for faces with indices < what we have so we don't do each edge twice.
  2820. for ( int iOtherFace=0; iOtherFace < iFace; iOtherFace++ )
  2821. {
  2822. for ( int iOtherEdge=0; iOtherEdge < 4; iOtherEdge++ )
  2823. {
  2824. int o1 = faceVertsList[iOtherFace][iOtherEdge];
  2825. int o2 = faceVertsList[iOtherFace][(iOtherEdge+1)%4];
  2826. if ( (i1 == o1 && i2 == o2) || (i2 == o1 && i1 == o2) )
  2827. {
  2828. CEdgeMatch *pMatch = &edgeMatches[nTotalEdgesMatched];
  2829. pMatch->m_iFaces[0] = iFace;
  2830. pMatch->m_iEdges[0] = iEdge;
  2831. pMatch->m_iFaces[1] = iOtherFace;
  2832. pMatch->m_iEdges[1] = iOtherEdge;
  2833. pMatch->m_iCubeVerts[0] = o1;
  2834. pMatch->m_iCubeVerts[1] = o2;
  2835. pMatch->m_bFlipFace2Edge = i1 != o1;
  2836. ++nTotalEdgesMatched;
  2837. }
  2838. }
  2839. }
  2840. }
  2841. }
  2842. Assert( nTotalEdgesMatched == 12 );
  2843. // For each corner vert, find the 3 edges touching it.
  2844. for ( int iVert=0; iVert < NUM_CORNER_MATCHES; iVert++ )
  2845. {
  2846. int iTouchingFace = 0;
  2847. for ( int iFace=0; iFace < 6; iFace++ )
  2848. {
  2849. for ( int iFaceVert=0; iFaceVert < 4; iFaceVert++ )
  2850. {
  2851. if ( faceVertsList[iFace][iFaceVert] == iVert )
  2852. {
  2853. cornerMatches[iVert].m_iFaces[iTouchingFace] = iFace;
  2854. cornerMatches[iVert].m_iFaceEdges[iTouchingFace] = iFaceVert;
  2855. ++iTouchingFace;
  2856. }
  2857. }
  2858. }
  2859. Assert( iTouchingFace == 3 );
  2860. }
  2861. }
  2862. void CVTFTexture::BlendCubeMapEdgePalettes(
  2863. int iFrame,
  2864. int iMipLevel,
  2865. const CEdgeMatch *pMatch )
  2866. {
  2867. Assert( m_Format == IMAGE_FORMAT_DXT1 || m_Format == IMAGE_FORMAT_DXT5 );
  2868. int nMipWidth, nMipHeight, nMipDepth;
  2869. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2870. Assert( nMipDepth == 1 );
  2871. if ( nMipWidth <= 8 || nMipHeight <= 8 )
  2872. return;
  2873. unsigned char *pFace1Data = ImageData( iFrame, pMatch->m_iFaces[0], iMipLevel );
  2874. unsigned char *pFace2Data = ImageData( iFrame, pMatch->m_iFaces[1], iMipLevel );
  2875. S3RGBA *pFace1Original = &m_OriginalData[ GetImageOffset( iFrame, pMatch->m_iFaces[0], iMipLevel, IMAGE_FORMAT_RGBA8888 ) / 4 ];
  2876. S3RGBA *pFace2Original = &m_OriginalData[ GetImageOffset( iFrame, pMatch->m_iFaces[1], iMipLevel, IMAGE_FORMAT_RGBA8888 ) / 4 ];
  2877. CEdgeIncrements incs;
  2878. SetupTextureEdgeIncrements( iMipLevel, pMatch->m_iEdges[0], pMatch->m_iEdges[1], pMatch->m_bFlipFace2Edge, &incs );
  2879. // Divide the coordinates by 4 since we're dealing with S3 blocks here.
  2880. incs.iFace1Start /= 4; incs.iFace1End /= 4; incs.iFace2Start /= 4; incs.iFace2End /= 4;
  2881. // Now walk along the edges, blending the edge pixels.
  2882. CEdgePos iFace1Cur = incs.iFace1Start + incs.iFace1Inc;
  2883. CEdgePos iFace2Cur = incs.iFace2Start + incs.iFace2Inc;
  2884. while ( iFace1Cur != incs.iFace1End ) // We intentionally want to not process the last block here..
  2885. {
  2886. // Merge the palette of these two blocks.
  2887. char *blocks[2] =
  2888. {
  2889. S3TC_GetBlock( pFace1Data, m_Format, nMipWidth>>2, iFace1Cur.x, iFace1Cur.y ),
  2890. S3TC_GetBlock( pFace2Data, m_Format, nMipWidth>>2, iFace2Cur.x, iFace2Cur.y )
  2891. };
  2892. S3RGBA *originals[2] =
  2893. {
  2894. &pFace1Original[(iFace1Cur.y * 4 * nMipWidth) + iFace1Cur.x * 4],
  2895. &pFace2Original[(iFace2Cur.y * 4 * nMipWidth) + iFace2Cur.x * 4]
  2896. };
  2897. S3TC_MergeBlocks(
  2898. blocks,
  2899. originals,
  2900. 2,
  2901. nMipWidth*4,
  2902. m_Format );
  2903. iFace1Cur += incs.iFace1Inc;
  2904. iFace2Cur += incs.iFace2Inc;
  2905. }
  2906. }
  2907. void CVTFTexture::BlendCubeMapCornerPalettes(
  2908. int iFrame,
  2909. int iMipLevel,
  2910. const CCornerMatch *pMatch )
  2911. {
  2912. int nMipWidth, nMipHeight, nMipDepth;
  2913. ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  2914. Assert( nMipDepth == 1 );
  2915. if ( nMipWidth < 4 || nMipHeight < 4 )
  2916. return;
  2917. // Now setup an S3TC block pointer for each of the corner blocks on each face.
  2918. char *blocks[3];
  2919. S3RGBA *originals[3];
  2920. for ( int iEdge=0; iEdge < 3; iEdge++ )
  2921. {
  2922. CEdgePos facePos;
  2923. SetupFaceVert( iMipLevel, pMatch->m_iFaceEdges[iEdge], facePos );
  2924. facePos /= 4; // To get the S3 block index.
  2925. int iFaceIndex = pMatch->m_iFaces[iEdge];
  2926. unsigned char *pFaceData = ImageData( iFrame, iFaceIndex, iMipLevel );
  2927. S3RGBA *pFaceOriginal = &m_OriginalData[ GetImageOffset( iFrame, iFaceIndex, iMipLevel, IMAGE_FORMAT_RGBA8888 ) / 4 ];
  2928. blocks[iEdge] = S3TC_GetBlock( pFaceData, m_Format, nMipWidth>>2, facePos.x, facePos.y );
  2929. originals[iEdge] = &pFaceOriginal[ (facePos.y * 4 * nMipWidth) + facePos.x * 4 ];
  2930. }
  2931. S3TC_MergeBlocks(
  2932. blocks,
  2933. originals,
  2934. 3,
  2935. nMipWidth*4,
  2936. m_Format );
  2937. }
  2938. void CVTFTexture::MatchCubeMapS3TCPalettes(
  2939. CEdgeMatch edgeMatches[NUM_EDGE_MATCHES],
  2940. CCornerMatch cornerMatches[NUM_CORNER_MATCHES]
  2941. )
  2942. {
  2943. for (int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel)
  2944. {
  2945. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  2946. {
  2947. // First, match all the edge palettes (this part skips the first and last 4 texels
  2948. // along the edge since those S3 blocks are handled in the corner case).
  2949. for ( int iEdgeMatch=0; iEdgeMatch < NUM_EDGE_MATCHES; iEdgeMatch++ )
  2950. {
  2951. BlendCubeMapEdgePalettes(
  2952. iFrame,
  2953. iMipLevel,
  2954. &edgeMatches[iEdgeMatch] );
  2955. }
  2956. for ( int iCornerMatch=0; iCornerMatch < NUM_CORNER_MATCHES; iCornerMatch++ )
  2957. {
  2958. BlendCubeMapCornerPalettes(
  2959. iFrame,
  2960. iMipLevel,
  2961. &cornerMatches[iCornerMatch] );
  2962. }
  2963. }
  2964. }
  2965. }
  2966. void CVTFTexture::MatchCubeMapBorders( int iStage, ImageFormat finalFormat, bool bSkybox )
  2967. {
  2968. // HDRFIXME: hack hack hack
  2969. if( m_Format != IMAGE_FORMAT_RGBA8888 )
  2970. {
  2971. //Assert( 0 );
  2972. return;
  2973. }
  2974. if ( !IsCubeMap() )
  2975. return;
  2976. Assert( IsCubeMap() );
  2977. Assert( m_nFaceCount >= 6 );
  2978. if ( iStage == 1 )
  2979. {
  2980. // Stage 1 is while the image is still RGBA8888. If we're not going to S3 compress the image,
  2981. // then it is easiest to match the borders now.
  2982. Assert( m_Format == IMAGE_FORMAT_RGBA8888 );
  2983. if ( finalFormat == IMAGE_FORMAT_DXT1 || finalFormat == IMAGE_FORMAT_DXT5 )
  2984. {
  2985. // If we're going to S3 compress the image eventually, then store off the original version
  2986. // because we can use that while matching the S3 compressed edges (we have to do some tricky
  2987. // repalettizing).
  2988. int nTotalBytes = ComputeTotalSize();
  2989. m_OriginalData.SetSize( nTotalBytes / 4 );
  2990. memcpy( m_OriginalData.Base(), ImageData(), nTotalBytes );
  2991. // Swap R and B in these because IMAGE_FORMAT_RGBA8888 is swapped from the way S3RGBAs are.
  2992. for ( int i=0; i < nTotalBytes/4; i++ )
  2993. V_swap( m_OriginalData[i].r, m_OriginalData[i].b );
  2994. return;
  2995. }
  2996. else
  2997. {
  2998. // Drop down below and do the edge matching.
  2999. }
  3000. }
  3001. else
  3002. {
  3003. if ( finalFormat == IMAGE_FORMAT_DXT1 || finalFormat == IMAGE_FORMAT_DXT5 )
  3004. {
  3005. Assert( m_Format == finalFormat );
  3006. }
  3007. else
  3008. {
  3009. // If we're not winding up S3 compressed, then we already fixed the cubemap borders.
  3010. return;
  3011. }
  3012. }
  3013. // Figure out
  3014. CEdgeMatch edgeMatches[NUM_EDGE_MATCHES];
  3015. CCornerMatch cornerMatches[NUM_CORNER_MATCHES];
  3016. BuildCubeMapMatchLists( edgeMatches, cornerMatches, bSkybox );
  3017. // If we're S3 compressed, then during the first pass, we need to match the palettes of all
  3018. // bordering S3 blocks.
  3019. if ( m_Format == IMAGE_FORMAT_DXT1 || m_Format == IMAGE_FORMAT_DXT5 )
  3020. {
  3021. MatchCubeMapS3TCPalettes( edgeMatches, cornerMatches );
  3022. }
  3023. for (int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel)
  3024. {
  3025. for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame)
  3026. {
  3027. for ( int iEdgeMatch=0; iEdgeMatch < NUM_EDGE_MATCHES; iEdgeMatch++ )
  3028. {
  3029. BlendCubeMapFaceEdges(
  3030. iFrame,
  3031. iMipLevel,
  3032. &edgeMatches[iEdgeMatch] );
  3033. }
  3034. for ( int iCornerMatch=0; iCornerMatch < NUM_CORNER_MATCHES; iCornerMatch++ )
  3035. {
  3036. BlendCubeMapFaceCorners(
  3037. iFrame,
  3038. iMipLevel,
  3039. &cornerMatches[iCornerMatch] );
  3040. }
  3041. }
  3042. }
  3043. }
  3044. /*
  3045. Test code used to draw the cubemap into a scratchpad file. Useful for debugging, or at least
  3046. it was once.
  3047. IScratchPad3D *pPad = ScratchPad3D_Create();
  3048. int nMipWidth, nMipHeight;
  3049. ComputeMipLevelDimensions( 0, &nMipWidth, &nMipHeight );
  3050. CUtlVector<unsigned char> data;
  3051. data.SetSize( nMipWidth*nMipHeight );
  3052. float cubeSize = 200;
  3053. Vector vertPositions[8] =
  3054. {
  3055. Vector( 0, cubeSize, 0 ),
  3056. Vector( 0, cubeSize, cubeSize ),
  3057. Vector( cubeSize, 0, 0 ),
  3058. Vector( cubeSize, 0, cubeSize ),
  3059. Vector( 0, 0, 0 ),
  3060. Vector( 0, 0, cubeSize ),
  3061. Vector( cubeSize, cubeSize, 0 ),
  3062. Vector( cubeSize, cubeSize, cubeSize )
  3063. };
  3064. char *faceNames[6] = { "right","left","back","front","up","down" };
  3065. for ( int iVert=0; iVert < 8; iVert++ )
  3066. {
  3067. char str[512];
  3068. Q_snprintf( str, sizeof( str ), "%d", iVert );
  3069. CTextParams params;
  3070. params.m_flLetterWidth = 20;
  3071. params.m_vPos = vertPositions[iVert];
  3072. pPad->DrawText( str, params );
  3073. }
  3074. for ( int iFace=0; iFace < 6; iFace++ )
  3075. {
  3076. unsigned char *pFace1Data = ImageData( 0, iFace, 0 );
  3077. for ( int y=0; y < nMipHeight; y++ )
  3078. {
  3079. for( int x=0; x < nMipWidth; x++ )
  3080. {
  3081. S3PaletteIndex index = S3TC_GetPaletteIndex(
  3082. pFace1Data,
  3083. m_Format,
  3084. nMipWidth,
  3085. x, y );
  3086. const char *pBlock = S3TC_GetBlock( pFace1Data, m_Format, nMipWidth/4, x/4, y/4 );
  3087. unsigned char a0 = pBlock[0];
  3088. unsigned char a1 = pBlock[1];
  3089. if ( index.m_AlphaIndex == 0 )
  3090. {
  3091. data[y*nMipWidth+x] = a0;
  3092. }
  3093. else if ( index.m_AlphaIndex == 1 )
  3094. {
  3095. data[y*nMipWidth+x] = a1;
  3096. }
  3097. else if ( a0 > a1 )
  3098. {
  3099. data[y*nMipWidth+x] = ((8-(int)index.m_AlphaIndex)*a0 + ((int)index.m_AlphaIndex-1)*a1) / 7;
  3100. }
  3101. else
  3102. {
  3103. if ( index.m_AlphaIndex == 6 )
  3104. data[y*nMipWidth+x] = 0;
  3105. else if ( index.m_AlphaIndex == 7 )
  3106. data[y*nMipWidth+x] = 255;
  3107. else
  3108. data[y*nMipWidth+x] = ((6-(int)index.m_AlphaIndex)*a0 + ((int)index.m_AlphaIndex-1)*a1) / 5;
  3109. }
  3110. }
  3111. }
  3112. Vector vCorners[4];
  3113. for ( int iCorner=0; iCorner < 4; iCorner++ )
  3114. vCorners[iCorner] = vertPositions[g_FaceVerts[iFace][iCorner]];
  3115. pPad->DrawImageBW( data.Base(), nMipWidth, nMipHeight, nMipWidth, false, true, vCorners );
  3116. CTextParams params;
  3117. params.m_vPos = (vCorners[0] + vCorners[1] + vCorners[2] + vCorners[3]) / 4;
  3118. params.m_bCentered = true;
  3119. params.m_vColor.Init( 1, 0, 0 );
  3120. params.m_bTwoSided = true;
  3121. params.m_flLetterWidth = 10;
  3122. Vector vNormal = (vCorners[1] - vCorners[0]).Cross( vCorners[2] - vCorners[1] );
  3123. VectorNormalize( vNormal );
  3124. params.m_vPos += vNormal*5;
  3125. VectorAngles( vNormal, params.m_vAngles );
  3126. pPad->DrawText( faceNames[iFace], params );
  3127. pPad->Flush();
  3128. }
  3129. */