Team Fortress 2 Source Code as on 22/4/2020
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.

3493 lines
96 KiB

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