From eb78802b5de8870a9c4a82e280fe024bb072db6c Mon Sep 17 00:00:00 2001 From: nillerusr Date: Thu, 28 Jul 2022 14:32:38 +0300 Subject: [PATCH] vtf: 7.5 version support ( fixes #83 ) --- bitmap/resample.cpp | 44 +++++++++++++ public/bitmap/imageformat.h | 1 + public/vtf/vtf.h | 2 +- vtf/vtf.cpp | 128 +++++++++++++++++------------------- 4 files changed, 105 insertions(+), 70 deletions(-) diff --git a/bitmap/resample.cpp b/bitmap/resample.cpp index 98998899..e27885c9 100644 --- a/bitmap/resample.cpp +++ b/bitmap/resample.cpp @@ -784,6 +784,50 @@ bool ResampleRGB323232F( const ResampleInfo_t& info ) return true; } +bool ResampleRGBA32323232F( const ResampleInfo_t& info ) +{ + // HDRFIXME: This is some lame shit right here. (We need to get NICE working, etc, etc.) + + // Make sure everything is power of two. + Assert( ( info.m_nSrcWidth & ( info.m_nSrcWidth - 1 ) ) == 0 ); + Assert( ( info.m_nSrcHeight & ( info.m_nSrcHeight - 1 ) ) == 0 ); + Assert( ( info.m_nDestWidth & ( info.m_nDestWidth - 1 ) ) == 0 ); + Assert( ( info.m_nDestHeight & ( info.m_nDestHeight - 1 ) ) == 0 ); + + // Make sure that we aren't upscaling the image. . .we don't support that very well. + Assert( info.m_nSrcWidth >= info.m_nDestWidth ); + Assert( info.m_nSrcHeight >= info.m_nDestHeight ); + + int nSampleWidth = info.m_nSrcWidth / info.m_nDestWidth; + int nSampleHeight = info.m_nSrcHeight / info.m_nDestHeight; + + float *pSrc = ( float * )info.m_pSrc; + float *pDst = ( float * )info.m_pDest; + for( int y = 0; y < info.m_nDestHeight; y++ ) + { + for( int x = 0; x < info.m_nDestWidth; x++ ) + { + float accum[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + for( int nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ ) + { + for( int nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ ) + { + accum[0] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+0]; + accum[1] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+1]; + accum[2] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+2]; + accum[3] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+3]; + } + } + for( int i = 0; i < 4; i++ ) + { + accum[i] /= ( nSampleWidth * nSampleHeight ); + pDst[(x+y*info.m_nDestWidth)*4+i] = accum[i]; + } + } + } + return true; +} + //----------------------------------------------------------------------------- // Generates mipmap levels //----------------------------------------------------------------------------- diff --git a/public/bitmap/imageformat.h b/public/bitmap/imageformat.h index f1e89823..1f78b078 100644 --- a/public/bitmap/imageformat.h +++ b/public/bitmap/imageformat.h @@ -459,6 +459,7 @@ namespace ImageLoader bool ResampleRGBA8888( const ResampleInfo_t &info ); bool ResampleRGBA16161616( const ResampleInfo_t &info ); bool ResampleRGB323232F( const ResampleInfo_t &info ); + bool ResampleRGBA32323232F( const ResampleInfo_t& info ); void ConvertNormalMapRGBA8888ToDUDVMapUVLX8888( const unsigned char *src, int width, int height, unsigned char *dst_ ); diff --git a/public/vtf/vtf.h b/public/vtf/vtf.h index 87e571e3..6cb2e79e 100644 --- a/public/vtf/vtf.h +++ b/public/vtf/vtf.h @@ -440,7 +440,7 @@ bool GetVTFPreload360Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUt // version number for the disk texture cache #define VTF_MAJOR_VERSION 7 -#define VTF_MINOR_VERSION 4 +#define VTF_MINOR_VERSION 5 //----------------------------------------------------------------------------- // !!!!CRITICAL!!!! BEFORE YOU CHANGE THE FORMAT diff --git a/vtf/vtf.cpp b/vtf/vtf.cpp index d014e12b..0bf308ea 100644 --- a/vtf/vtf.cpp +++ b/vtf/vtf.cpp @@ -190,7 +190,11 @@ IVTFTexture *CreateVTFTexture() void DestroyVTFTexture( IVTFTexture *pTexture ) { - delete pTexture; + CVTFTexture *pTex = static_cast(pTexture); + if ( pTex ) + { + delete pTex; + } } //----------------------------------------------------------------------------- @@ -220,6 +224,7 @@ int VTFFileHeaderSize( int nMajorVersion, int nMinorVersion ) return sizeof( VTFFileHeaderV7_2_t ); case 3: return sizeof( VTFFileHeaderV7_3_t ) + sizeof( ResourceEntryInfo ) * MAX_RSRC_DICTIONARY_ENTRIES; + case 4: case VTF_MINOR_VERSION: int size1 = sizeof( VTFFileHeader_t ); int size2 = sizeof( ResourceEntryInfo ) * MAX_RSRC_DICTIONARY_ENTRIES; @@ -388,10 +393,14 @@ bool CVTFTexture::Init( int nWidth, int nHeight, int nDepth, ImageFormat fmt, in } } - if ( !IsMultipleOf4( nWidth ) || !IsMultipleOf4( nHeight ) || !IsMultipleOf4( nDepth ) ) + if ( ( fmt == IMAGE_FORMAT_DXT1 ) || ( fmt == IMAGE_FORMAT_DXT3 ) || ( fmt == IMAGE_FORMAT_DXT5 ) || + ( fmt == IMAGE_FORMAT_DXT1_RUNTIME ) || ( fmt == IMAGE_FORMAT_DXT5_RUNTIME ) ) { - Warning( "Image dimensions must be multiple of 4!\n" ); - return false; + if ( !IsMultipleOf4( nWidth ) || !IsMultipleOf4( nHeight ) || !IsMultipleOf4( nDepth ) ) + { + Warning( "Image dimensions must be multiple of 4!\n" ); + return false; + } } if ( fmt == IMAGE_FORMAT_DEFAULT ) @@ -676,89 +685,65 @@ bool CVTFTexture::LoadImageData( CUtlBuffer &buf, const VTFFileHeader_t &header, int iImageSize = ComputeFaceSize(); iImageSize *= m_nFaceCount * m_nFrameCount; - // For backwards compatibility, we don't read in the spheremap fallback on - // older format .VTF files... - int nFacesToRead = m_nFaceCount; - if ( IsCubeMap() ) - { - if ((header.version[0] == 7) && (header.version[1] < 1)) - nFacesToRead = 6; - } - - // Even if we are preloading partial data, always do the full allocation here. We'll use LOD clamping to ensure we only - // reference data that is available. if ( !AllocateImageData( iImageSize ) ) return false; - // We may only have part of the data available--if so we will stream in the rest later. - // If we have the data available but we're ignoring it (for example during development), then we - // need to skip over the data we're ignoring below, otherwise we'll be sad pandas. - bool bMipDataPresent = true; - int nFirstAvailableMip = 0; - int nLastAvailableMip = m_nMipCount - 1; - TextureStreamSettings_t const *pStreamSettings = ( TextureStreamSettings_t const * ) GetResourceData( VTF_RSRC_TEXTURE_STREAM_SETTINGS, NULL ); - if ( pStreamSettings ) - { - nFirstAvailableMip = Max( 0, pStreamSettings->m_firstAvailableMip - nSkipMipLevels ); - nLastAvailableMip = Max( 0, pStreamSettings->m_lastAvailableMip - nSkipMipLevels ); - bMipDataPresent = false; - } - - // If we have coarse mips but not the fine mips (yet) - if ( ( header.flags & TEXTUREFLAGS_STREAMABLE ) == TEXTUREFLAGS_STREAMABLE_COARSE ) - { - nFirstAvailableMip = Max( 0, Max( nFirstAvailableMip, STREAMING_START_MIPMAP ) - nSkipMipLevels ); - } - - if ( header.flags & TEXTUREFLAGS_STREAMABLE_FINE ) - { - // Don't need to subtract nSkipMipLevels: m_nMipCount has subtracted that above--assuming this assert doesn't fire. - Assert( m_nMipCount == header.numMipLevels - nSkipMipLevels ); - nLastAvailableMip = Min( nLastAvailableMip, STREAMING_START_MIPMAP - 1 ); - } - - // Valid settings? - Assert( nFirstAvailableMip >= 0 && nFirstAvailableMip <= nLastAvailableMip && nLastAvailableMip < m_nMipCount ); - - // Store the clamp settings - m_nFinestMipmapLevel = nFirstAvailableMip; - m_nCoarsestMipmapLevel = nLastAvailableMip; - // NOTE: The mip levels are stored ascending from smallest (1x1) to largest (NxN) // in order to allow for truncated reads of the minimal required data + + // NOTE: I checked in a bad version 4 where it stripped out the spheremap. + // To make it all work, need to check for that bad case. + bool bNoSkip = false; + if ( IsCubeMap() && ( header.version[0] == 7 ) && ( header.version[1] == 4 ) ) + { + int nBytesRemaining = buf.TellMaxPut() - buf.TellGet(); + int nFileSize = ComputeFaceSize( nSkipMipLevels ) * m_nFaceCount * m_nFrameCount; + if ( nBytesRemaining == nFileSize ) + { + bNoSkip = true; + } + } + + int nGet = buf.TellGet(); + +retryCubemapLoad: for (int iMip = m_nMipCount; --iMip >= 0; ) { // NOTE: This is for older versions... - if (header.numMipLevels - nSkipMipLevels <= iMip) + if ( header.numMipLevels - nSkipMipLevels <= iMip ) continue; int iMipSize = ComputeMipSize( iMip ); - // Skip over any levels we don't have data for--we'll get them later. - if ( iMip > nLastAvailableMip || iMip < nFirstAvailableMip ) - { - // If the data is there but we're ignoring it, need to update the get pointer. - if ( bMipDataPresent ) - { - for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame ) - for ( int iFace = 0; iFace < nFacesToRead; ++iFace ) - buf.SeekGet( CUtlBuffer::SEEK_CURRENT, iMipSize ); - } - continue; - } - for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame) { - for (int iFace = 0; iFace < nFacesToRead; ++iFace) + for (int iFace = 0; iFace < m_nFaceCount; ++iFace) { // printf("\n tex %p mip %i frame %i face %i size %i buf offset %i", this, iMip, iFrame, iFace, iMipSize, buf.TellGet() ); unsigned char *pMipBits = ImageData( iFrame, iFace, iMip ); buf.Get( pMipBits, iMipSize ); } + + // Strip out the spheremap in older versions + if ( IsCubeMap() && !bNoSkip && ( header.version[0] == 7 ) && ( header.version[1] >= 1 ) && ( header.version[1] < 5 ) ) + { + buf.SeekGet( CUtlBuffer::SEEK_CURRENT, iMipSize ); + } } } - return buf.IsValid(); + bool bOk = buf.IsValid(); + if ( !bOk && IsCubeMap() && ( header.version[0] == 7 ) && ( header.version[1] <= 4 ) ) + { + if ( !bNoSkip ) + { + bNoSkip = true; + buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet ); + goto retryCubemapLoad; + } + Warning( "** Encountered stale cubemap! Please rebuild the following vtf:\n" ); + } + return bOk; } void *CVTFTexture::SetResourceData( uint32 eType, void const *pData, size_t nNumBytes ) @@ -912,7 +897,7 @@ bool CVTFTexture::SetupByteSwap( CUtlBuffer &buf ) static bool ReadHeaderFromBufferPastBaseHeader( CUtlBuffer &buf, VTFFileHeader_t &header ) { unsigned char *pBuf = (unsigned char*)(&header) + sizeof(VTFFileBaseHeader_t); - if ( header.version[1] == VTF_MINOR_VERSION ) + if ( header.version[1] <= VTF_MINOR_VERSION && header.version[1] >= 4 ) { buf.Get( pBuf, sizeof(VTFFileHeader_t) - sizeof(VTFFileBaseHeader_t) ); } @@ -974,7 +959,7 @@ bool CVTFTexture::ReadHeader( CUtlBuffer &buf, VTFFileHeader_t &header ) { m_Swap.SwapFieldsToTargetEndian( (VTFFileHeaderV7_3_t*)buf.PeekGet() ); } - else if ( baseHeader.version[1] == VTF_MINOR_VERSION ) + else if ( baseHeader.version[1] >= 4 && baseHeader.version[1] <= VTF_MINOR_VERSION ) { m_Swap.SwapFieldsToTargetEndian( (VTFFileHeader_t*)buf.PeekGet() ); } @@ -1021,6 +1006,7 @@ bool CVTFTexture::ReadHeader( CUtlBuffer &buf, VTFFileHeader_t &header ) case 3: header.flags &= VERSIONED_VTF_FLAGS_MASK_7_3; // fall-through + case 4: case VTF_MINOR_VERSION: break; } @@ -2528,7 +2514,7 @@ void CVTFTexture::GenerateMipmaps() // return; // } - Assert( m_Format == IMAGE_FORMAT_RGBA8888 || m_Format == IMAGE_FORMAT_RGB323232F ); + Assert( m_Format == IMAGE_FORMAT_RGBA8888 || m_Format == IMAGE_FORMAT_RGB323232F || m_Format == IMAGE_FORMAT_RGBA32323232F ); // FIXME: Should we be doing anything special for normalmaps other than a final normalization pass? ImageLoader::ResampleInfo_t info; @@ -2667,7 +2653,11 @@ void CVTFTexture::GenerateMipmaps() info.m_pSrc = pSrcLevel; info.m_pDest = pDstLevel; ComputeMipLevelDimensions( nSrcMipLevel, &info.m_nSrcWidth, &info.m_nSrcHeight, &info.m_nSrcDepth ); - if( m_Format == IMAGE_FORMAT_RGB323232F ) + if( m_Format == IMAGE_FORMAT_RGBA32323232F ) + { + ImageLoader::ResampleRGBA32323232F( info ); + } + else if( m_Format == IMAGE_FORMAT_RGB323232F ) { ImageLoader::ResampleRGB323232F( info ); }