1
This commit is contained in:
891
vtf/convert_x360.cpp
Normal file
891
vtf/convert_x360.cpp
Normal file
@ -0,0 +1,891 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
// Purpose: Force pc .VTF to preferred .VTF 360 format conversion
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#include "tier1/utlvector.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "cvtf.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier1/utlmemory.h"
|
||||
#include "bitmap/imageformat.h"
|
||||
|
||||
// if the entire vtf file is smaller than this threshold, add entirely to preload
|
||||
#define PRELOAD_VTF_THRESHOLD 2048
|
||||
|
||||
struct ResourceCopy_t
|
||||
{
|
||||
void *m_pData;
|
||||
int m_DataLength;
|
||||
ResourceEntryInfo m_EntryInfo;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Converts to an alternate format
|
||||
//-----------------------------------------------------------------------------
|
||||
ImageFormat PreferredFormat( IVTFTexture *pVTFTexture, ImageFormat fmt, int width, int height, int mipCount, int faceCount )
|
||||
{
|
||||
switch ( fmt )
|
||||
{
|
||||
case IMAGE_FORMAT_RGBA8888:
|
||||
case IMAGE_FORMAT_ABGR8888:
|
||||
case IMAGE_FORMAT_ARGB8888:
|
||||
case IMAGE_FORMAT_BGRA8888:
|
||||
return IMAGE_FORMAT_BGRA8888;
|
||||
|
||||
// 24bpp gpu formats don't exist, must convert
|
||||
case IMAGE_FORMAT_BGRX8888:
|
||||
case IMAGE_FORMAT_RGB888:
|
||||
case IMAGE_FORMAT_BGR888:
|
||||
case IMAGE_FORMAT_RGB888_BLUESCREEN:
|
||||
case IMAGE_FORMAT_BGR888_BLUESCREEN:
|
||||
return IMAGE_FORMAT_BGRX8888;
|
||||
|
||||
case IMAGE_FORMAT_BGRX5551:
|
||||
case IMAGE_FORMAT_RGB565:
|
||||
case IMAGE_FORMAT_BGR565:
|
||||
return IMAGE_FORMAT_BGR565;
|
||||
|
||||
// no change
|
||||
case IMAGE_FORMAT_I8:
|
||||
case IMAGE_FORMAT_IA88:
|
||||
case IMAGE_FORMAT_A8:
|
||||
case IMAGE_FORMAT_BGRA4444:
|
||||
case IMAGE_FORMAT_BGRA5551:
|
||||
case IMAGE_FORMAT_UV88:
|
||||
case IMAGE_FORMAT_UVWQ8888:
|
||||
case IMAGE_FORMAT_RGBA16161616:
|
||||
case IMAGE_FORMAT_UVLX8888:
|
||||
case IMAGE_FORMAT_DXT1_ONEBITALPHA:
|
||||
case IMAGE_FORMAT_DXT1:
|
||||
case IMAGE_FORMAT_DXT3:
|
||||
case IMAGE_FORMAT_DXT5:
|
||||
case IMAGE_FORMAT_ATI1N:
|
||||
case IMAGE_FORMAT_ATI2N:
|
||||
break;
|
||||
|
||||
case IMAGE_FORMAT_RGBA16161616F:
|
||||
return IMAGE_FORMAT_RGBA16161616;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Determines target dimensions
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ComputeTargetDimensions( const char *pDebugName, IVTFTexture *pVTFTexture, int picmip, int &width, int &height, int &mipCount, int &mipSkipCount, bool &bNoMip )
|
||||
{
|
||||
width = pVTFTexture->Width();
|
||||
height = pVTFTexture->Height();
|
||||
|
||||
// adhere to texture's internal lod setting
|
||||
int nClampX = 1<<30;
|
||||
int nClampY = 1<<30;
|
||||
TextureLODControlSettings_t const *pLODInfo = reinterpret_cast<TextureLODControlSettings_t const *> ( pVTFTexture->GetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, NULL ) );
|
||||
if ( pLODInfo )
|
||||
{
|
||||
if ( pLODInfo->m_ResolutionClampX )
|
||||
{
|
||||
nClampX = min( nClampX, 1 << pLODInfo->m_ResolutionClampX );
|
||||
}
|
||||
if ( pLODInfo->m_ResolutionClampX_360 )
|
||||
{
|
||||
nClampX = min( nClampX, 1 << pLODInfo->m_ResolutionClampX_360 );
|
||||
}
|
||||
if ( pLODInfo->m_ResolutionClampY )
|
||||
{
|
||||
nClampY = min( nClampY, 1 << pLODInfo->m_ResolutionClampY );
|
||||
}
|
||||
if ( pLODInfo->m_ResolutionClampY_360 )
|
||||
{
|
||||
nClampY = min( nClampY, 1 << pLODInfo->m_ResolutionClampY_360 );
|
||||
}
|
||||
}
|
||||
|
||||
// spin down to desired texture size
|
||||
mipSkipCount = 0;
|
||||
while ( mipSkipCount < picmip || width > nClampX || height > nClampY )
|
||||
{
|
||||
if ( width == 1 && height == 1 )
|
||||
break;
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
if ( width < 1 )
|
||||
width = 1;
|
||||
if ( height < 1 )
|
||||
height = 1;
|
||||
mipSkipCount++;
|
||||
}
|
||||
|
||||
bNoMip = false;
|
||||
if ( pVTFTexture->Flags() & TEXTUREFLAGS_NOMIP )
|
||||
{
|
||||
bNoMip = true;
|
||||
}
|
||||
|
||||
// determine mip quantity based on desired width/height
|
||||
if ( bNoMip )
|
||||
{
|
||||
// avoid serializing unused mips
|
||||
mipCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mipCount = ImageLoader::GetNumMipMapLevels( width, height );
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Align the buffer to specified boundary
|
||||
//-----------------------------------------------------------------------------
|
||||
int AlignBuffer( CUtlBuffer &buf, int alignment )
|
||||
{
|
||||
int curPosition;
|
||||
int newPosition;
|
||||
byte padByte = 0;
|
||||
|
||||
// advance to aligned position
|
||||
buf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
|
||||
curPosition = buf.TellPut();
|
||||
newPosition = AlignValue( curPosition, alignment );
|
||||
buf.EnsureCapacity( newPosition );
|
||||
|
||||
// write empty
|
||||
for ( int i=0; i<newPosition-curPosition; i++ )
|
||||
{
|
||||
buf.Put( &padByte, 1 );
|
||||
}
|
||||
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Convert the x86 image data to 360
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ConvertImageFormatEx(
|
||||
unsigned char *pSourceImage,
|
||||
int sourceImageSize,
|
||||
ImageFormat sourceFormat,
|
||||
unsigned char *pTargetImage,
|
||||
int targetImageSize,
|
||||
ImageFormat targetFormat,
|
||||
int width,
|
||||
int height,
|
||||
bool bSrgbGammaConvert )
|
||||
{
|
||||
// format conversion expects pc oriented data
|
||||
// but, formats that are >8 bits per channels need to be element pre-swapped
|
||||
ImageLoader::PreConvertSwapImageData( pSourceImage, sourceImageSize, sourceFormat );
|
||||
|
||||
bool bRetVal = ImageLoader::ConvertImageFormat(
|
||||
pSourceImage,
|
||||
sourceFormat,
|
||||
pTargetImage,
|
||||
targetFormat,
|
||||
width,
|
||||
height );
|
||||
if ( !bRetVal )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert to proper channel order for 360 d3dformats
|
||||
ImageLoader::PostConvertSwapImageData( pTargetImage, targetImageSize, targetFormat );
|
||||
|
||||
// Convert colors from sRGB gamma space into 360 piecewise linear gamma space
|
||||
if ( bSrgbGammaConvert == true )
|
||||
{
|
||||
if ( targetFormat == IMAGE_FORMAT_BGRA8888 || targetFormat == IMAGE_FORMAT_BGRX8888 )
|
||||
{
|
||||
//Msg( " Converting 8888 texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
|
||||
for ( int i = 0; i < ( targetImageSize / 4 ); i++ ) // targetImageSize is the raw data length in bytes
|
||||
{
|
||||
unsigned char *pRGB[3] = { NULL, NULL, NULL };
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
// pTargetImage is the raw image data
|
||||
pRGB[0] = &( pTargetImage[ ( i * 4 ) + 1 ] ); // Red
|
||||
pRGB[1] = &( pTargetImage[ ( i * 4 ) + 2 ] ); // Green
|
||||
pRGB[2] = &( pTargetImage[ ( i * 4 ) + 3 ] ); // Blue
|
||||
}
|
||||
else // 360
|
||||
{
|
||||
// pTargetImage is the raw image data
|
||||
pRGB[0] = &( pTargetImage[ ( i * 4 ) + 1 ] ); // Red
|
||||
pRGB[1] = &( pTargetImage[ ( i * 4 ) + 2 ] ); // Green
|
||||
pRGB[2] = &( pTargetImage[ ( i * 4 ) + 3 ] ); // Blue
|
||||
}
|
||||
|
||||
// Modify RGB data in place
|
||||
for ( int j = 0; j < 3; j++ ) // For red, green, blue
|
||||
{
|
||||
float flSrgbGamma = float( *( pRGB[j] ) ) / 255.0f;
|
||||
float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
|
||||
|
||||
fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
|
||||
*( pRGB[j] ) = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( ( targetFormat == IMAGE_FORMAT_DXT1_ONEBITALPHA ) || ( targetFormat == IMAGE_FORMAT_DXT1 ) || ( targetFormat == IMAGE_FORMAT_DXT3 ) || ( targetFormat == IMAGE_FORMAT_DXT5 ) )
|
||||
{
|
||||
//Msg( " Converting DXT texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
|
||||
int nStrideBytes = 8;
|
||||
int nOffsetBytes = 0;
|
||||
if ( ( targetFormat == IMAGE_FORMAT_DXT3 ) || ( targetFormat == IMAGE_FORMAT_DXT5 ) )
|
||||
{
|
||||
nOffsetBytes = 8;
|
||||
nStrideBytes = 16;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < ( targetImageSize / nStrideBytes ); i++ ) // For each color or color/alpha block
|
||||
{
|
||||
// Get 16bit 565 colors into an unsigned short
|
||||
unsigned short n565Color0 = 0;
|
||||
n565Color0 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 0 ] ) ) << 8;
|
||||
n565Color0 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 1 ] ) );
|
||||
|
||||
unsigned short n565Color1 = 0;
|
||||
n565Color1 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 2 ] ) ) << 8;
|
||||
n565Color1 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 3 ] ) );
|
||||
|
||||
// Convert to 888
|
||||
unsigned char v888Color0[3];
|
||||
v888Color0[0] = ( ( ( n565Color0 >> 11 ) & 0x1f ) << 3 );
|
||||
v888Color0[1] = ( ( ( n565Color0 >> 5 ) & 0x3f ) << 2 );
|
||||
v888Color0[2] = ( ( n565Color0 & 0x1f ) << 3 );
|
||||
|
||||
// Since we have one bit less of red and blue, add some of the error back in
|
||||
if ( v888Color0[0] != 0 ) // Don't mess with black pixels
|
||||
v888Color0[0] |= 0x04; // Add 0.5 of the error
|
||||
if ( v888Color0[2] != 0 ) // Don't mess with black pixels
|
||||
v888Color0[2] |= 0x04; // Add 0.5 of the error
|
||||
|
||||
unsigned char v888Color1[3];
|
||||
v888Color1[0] = ( ( ( n565Color1 >> 11 ) & 0x1f ) << 3 );
|
||||
v888Color1[1] = ( ( ( n565Color1 >> 5 ) & 0x3f ) << 2 );
|
||||
v888Color1[2] = ( ( n565Color1 & 0x1f ) << 3 );
|
||||
|
||||
// Since we have one bit less of red and blue, add some of the error back in
|
||||
if ( v888Color1[0] != 0 ) // Don't mess with black pixels
|
||||
v888Color1[0] |= 0x04; // Add 0.5 of the error
|
||||
if ( v888Color1[2] != 0 ) // Don't mess with black pixels
|
||||
v888Color1[2] |= 0x04; // Add 0.5 of the error
|
||||
|
||||
// Convert to float
|
||||
float vFlColor0[3];
|
||||
vFlColor0[0] = float( v888Color0[0] ) / 255.0f;
|
||||
vFlColor0[1] = float( v888Color0[1] ) / 255.0f;
|
||||
vFlColor0[2] = float( v888Color0[2] ) / 255.0f;
|
||||
|
||||
float vFlColor1[3];
|
||||
vFlColor1[0] = float( v888Color1[0] ) / 255.0f;
|
||||
vFlColor1[1] = float( v888Color1[1] ) / 255.0f;
|
||||
vFlColor1[2] = float( v888Color1[2] ) / 255.0f;
|
||||
|
||||
// Modify float RGB data and write to output 888 colors
|
||||
unsigned char v888Color0New[3];
|
||||
unsigned char v888Color1New[3];
|
||||
for ( int j = 0; j < 3; j++ ) // For red, green, blue
|
||||
{
|
||||
for ( int k = 0; k < 2; k++ ) // For color0 and color1
|
||||
{
|
||||
float *pFlValue = ( k == 0 ) ? &( vFlColor0[j] ) : &( vFlColor1[j] );
|
||||
unsigned char *p8BitValue = ( k == 0 ) ? &( v888Color0New[j] ) : &( v888Color1New[j] );
|
||||
|
||||
float flSrgbGamma = *pFlValue;
|
||||
float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
|
||||
|
||||
fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
|
||||
//*p8BitValue = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
|
||||
*p8BitValue = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) ), 0.0f, 255.0f ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to 565
|
||||
v888Color0New[0] &= 0xf8; // 5 bits
|
||||
v888Color0New[1] &= 0xfc; // 6 bits
|
||||
v888Color0New[2] &= 0xf8; // 5 bits
|
||||
unsigned short n565Color0New = ( ( unsigned short )v888Color0New[0] << 8 ) | ( ( unsigned short )v888Color0New[1] << 3 ) | ( ( unsigned short )v888Color0New[2] >> 3 );
|
||||
|
||||
v888Color1New[0] &= 0xf8; // 5 bits
|
||||
v888Color1New[1] &= 0xfc; // 6 bits
|
||||
v888Color1New[2] &= 0xf8; // 5 bits
|
||||
unsigned short n565Color1New = ( ( unsigned short )v888Color1New[0] << 8 ) | ( ( unsigned short )v888Color1New[1] << 3 ) | ( ( unsigned short )v888Color1New[2] >> 3 );
|
||||
|
||||
// If we're targeting DXT1, make sure we haven't made a non transparent color block transparent
|
||||
if ( ( targetFormat == IMAGE_FORMAT_DXT1 ) || ( targetFormat == IMAGE_FORMAT_DXT1_ONEBITALPHA ) )
|
||||
{
|
||||
// If new block is transparent but old block wasn't
|
||||
if ( ( n565Color0New <= n565Color1New ) && ( n565Color0 > n565Color1 ) )
|
||||
{
|
||||
if ( ( v888Color0New[0] == v888Color1New[0] ) && ( v888Color0[0] != v888Color1[0] ) )
|
||||
{
|
||||
if ( v888Color0New[0] == 0xf8 )
|
||||
v888Color1New[0] -= 0x08;
|
||||
else
|
||||
v888Color0New[0] += 0x08;
|
||||
}
|
||||
|
||||
if ( ( v888Color0New[1] == v888Color1New[1] ) && ( v888Color0[1] != v888Color1[1] ) )
|
||||
{
|
||||
if ( v888Color0New[1] == 0xfc )
|
||||
v888Color1New[1] -= 0x04;
|
||||
else
|
||||
v888Color0New[1] += 0x04;
|
||||
}
|
||||
|
||||
if ( ( v888Color0New[2] == v888Color1New[2] ) && ( v888Color0[2] != v888Color1[2] ) )
|
||||
{
|
||||
if ( v888Color0New[2] == 0xf8 )
|
||||
v888Color1New[2] -= 0x08;
|
||||
else
|
||||
v888Color0New[2] += 0x08;
|
||||
}
|
||||
|
||||
n565Color0New = ( ( unsigned short )v888Color0New[0] << 8 ) | ( ( unsigned short )v888Color0New[1] << 3 ) | ( ( unsigned short )v888Color0New[2] >> 3 );
|
||||
n565Color1New = ( ( unsigned short )v888Color1New[0] << 8 ) | ( ( unsigned short )v888Color1New[1] << 3 ) | ( ( unsigned short )v888Color1New[2] >> 3 );
|
||||
}
|
||||
}
|
||||
|
||||
// Copy new colors back to color block
|
||||
pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 0 ] = ( unsigned char )( ( n565Color0New >> 8 ) & 0x00ff );
|
||||
pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 1 ] = ( unsigned char )( n565Color0New & 0x00ff );
|
||||
|
||||
pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 2 ] = ( unsigned char )( ( n565Color1New >> 8 ) & 0x00ff );
|
||||
pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 3 ] = ( unsigned char )( n565Color1New & 0x00ff );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Write the source data as the desired format into a target buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SerializeImageData( IVTFTexture *pSourceVTF, int frame, int face, int mip, ImageFormat targetFormat, CUtlBuffer &targetBuf )
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int targetImageSize;
|
||||
byte *pSourceImage;
|
||||
int sourceImageSize;
|
||||
int targetSize;
|
||||
CUtlMemory<byte> targetImage;
|
||||
|
||||
width = pSourceVTF->Width() >> mip;
|
||||
height = pSourceVTF->Height() >> mip;
|
||||
if ( width < 1 )
|
||||
width = 1;
|
||||
if ( height < 1)
|
||||
height = 1;
|
||||
|
||||
sourceImageSize = ImageLoader::GetMemRequired( width, height, 1, pSourceVTF->Format(), false );
|
||||
pSourceImage = pSourceVTF->ImageData( frame, face, mip );
|
||||
|
||||
targetImageSize = ImageLoader::GetMemRequired( width, height, 1, targetFormat, false );
|
||||
targetImage.EnsureCapacity( targetImageSize );
|
||||
byte *pTargetImage = (byte*)targetImage.Base();
|
||||
|
||||
// conversion may skip bytes, ensure all bits initialized
|
||||
memset( pTargetImage, 0xFF, targetImageSize );
|
||||
|
||||
// format conversion expects pc oriented data
|
||||
bool bRetVal = ConvertImageFormatEx(
|
||||
pSourceImage,
|
||||
sourceImageSize,
|
||||
pSourceVTF->Format(),
|
||||
pTargetImage,
|
||||
targetImageSize,
|
||||
targetFormat,
|
||||
width,
|
||||
height,
|
||||
( pSourceVTF->Flags() & TEXTUREFLAGS_SRGB ) ? true : false );
|
||||
if ( !bRetVal )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//X360TBD: incorrect byte order
|
||||
// // fixup mip dependent data
|
||||
// if ( ( pSourceVTF->Flags() & TEXTUREFLAGS_ONEOVERMIPLEVELINALPHA ) && ( targetFormat == IMAGE_FORMAT_BGRA8888 ) )
|
||||
// {
|
||||
// unsigned char ooMipLevel = ( unsigned char )( 255.0f * ( 1.0f / ( float )( 1 << mip ) ) );
|
||||
// int i;
|
||||
//
|
||||
// for ( i=0; i<width*height; i++ )
|
||||
// {
|
||||
// pTargetImage[i*4+3] = ooMipLevel;
|
||||
// }
|
||||
// }
|
||||
|
||||
targetSize = targetBuf.Size() + targetImageSize;
|
||||
targetBuf.EnsureCapacity( targetSize );
|
||||
targetBuf.Put( pTargetImage, targetImageSize );
|
||||
if ( !targetBuf.IsValid() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Generate the 360 target into a buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ConvertVTFTo360Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc )
|
||||
{
|
||||
bool bRetVal;
|
||||
IVTFTexture *pSourceVTF;
|
||||
int targetWidth;
|
||||
int targetHeight;
|
||||
int targetMipCount;
|
||||
VTFFileHeaderX360_t targetHeader;
|
||||
int frame;
|
||||
int face;
|
||||
int mip;
|
||||
ImageFormat targetFormat;
|
||||
int targetLowResWidth;
|
||||
int targetLowResHeight;
|
||||
int targetFlags;
|
||||
int mipSkipCount;
|
||||
int targetFaceCount;
|
||||
int preloadDataSize;
|
||||
int targetImageDataOffset;
|
||||
int targetFrameCount;
|
||||
VTFFileHeaderV7_1_t *pVTFHeader71;
|
||||
bool bNoMip;
|
||||
CByteswap byteSwapWriter;
|
||||
CUtlVector< ResourceCopy_t > targetResources;
|
||||
bool bHasLowResData = false;
|
||||
unsigned int resourceTypes[MAX_RSRC_DICTIONARY_ENTRIES];
|
||||
unsigned char targetLowResSample[4];
|
||||
int numTypes;
|
||||
|
||||
// Only need to byte swap writes if we are running the coversion on the PC, and data will be read from 360
|
||||
byteSwapWriter.ActivateByteSwapping( !IsX360() );
|
||||
|
||||
// need mathlib
|
||||
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
|
||||
|
||||
// default failure
|
||||
bRetVal = false;
|
||||
|
||||
pSourceVTF = NULL;
|
||||
|
||||
// unserialize the vtf with just the header
|
||||
pSourceVTF = CreateVTFTexture();
|
||||
if ( !pSourceVTF->Unserialize( sourceBuf, true, 0 ) )
|
||||
goto cleanUp;
|
||||
|
||||
// volume textures not supported
|
||||
if ( pSourceVTF->Depth() != 1 )
|
||||
goto cleanUp;
|
||||
|
||||
if ( !ImageLoader::IsFormatValidForConversion( pSourceVTF->Format() ) )
|
||||
goto cleanUp;
|
||||
|
||||
if ( !ComputeTargetDimensions( pDebugName, pSourceVTF, 0, targetWidth, targetHeight, targetMipCount, mipSkipCount, bNoMip ) )
|
||||
goto cleanUp;
|
||||
|
||||
// must crack vtf file to determine if mip levels exist from header
|
||||
// vtf interface does not expose the true presence of this data
|
||||
pVTFHeader71 = (VTFFileHeaderV7_1_t*)sourceBuf.Base();
|
||||
if ( mipSkipCount >= pVTFHeader71->numMipLevels )
|
||||
{
|
||||
// can't skip mips that aren't there
|
||||
// ideally should just reconstruct them
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// unserialize the vtf with all the data configured with the desired starting mip
|
||||
sourceBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
if ( !pSourceVTF->Unserialize( sourceBuf, false, mipSkipCount ) )
|
||||
{
|
||||
Msg( "ConvertVTFTo360Format: Error reading in %s\n", pDebugName );
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// add the default resource image
|
||||
ResourceCopy_t resourceCopy;
|
||||
resourceCopy.m_EntryInfo.eType = VTF_LEGACY_RSRC_IMAGE;
|
||||
resourceCopy.m_EntryInfo.resData = 0;
|
||||
resourceCopy.m_pData = NULL;
|
||||
resourceCopy.m_DataLength = 0;
|
||||
targetResources.AddToTail( resourceCopy );
|
||||
|
||||
// get the resources
|
||||
numTypes = pSourceVTF->GetResourceTypes( resourceTypes, MAX_RSRC_DICTIONARY_ENTRIES );
|
||||
for ( int i=0; i<numTypes; i++ )
|
||||
{
|
||||
size_t resourceLength;
|
||||
void *pResourceData;
|
||||
|
||||
switch ( resourceTypes[i] & ~RSRCF_MASK )
|
||||
{
|
||||
case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
|
||||
case VTF_LEGACY_RSRC_IMAGE:
|
||||
case VTF_RSRC_TEXTURE_LOD_SETTINGS:
|
||||
case VTF_RSRC_TEXTURE_SETTINGS_EX:
|
||||
case VTF_RSRC_TEXTURE_CRC:
|
||||
// not needed, presence already folded into conversion
|
||||
continue;
|
||||
|
||||
default:
|
||||
pResourceData = pSourceVTF->GetResourceData( resourceTypes[i], &resourceLength );
|
||||
if ( pResourceData )
|
||||
{
|
||||
resourceCopy.m_EntryInfo.eType = resourceTypes[i] & ~RSRCF_MASK;
|
||||
resourceCopy.m_EntryInfo.resData = 0;
|
||||
resourceCopy.m_pData = new char[resourceLength];
|
||||
resourceCopy.m_DataLength = resourceLength;
|
||||
V_memcpy( resourceCopy.m_pData, pResourceData, resourceLength );
|
||||
targetResources.AddToTail( resourceCopy );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( targetResources.Count() > MAX_X360_RSRC_DICTIONARY_ENTRIES )
|
||||
{
|
||||
Msg( "ConvertVTFTo360Format: More resources than expected in %s\n", pDebugName );
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
targetFlags = pSourceVTF->Flags();
|
||||
targetFrameCount = pSourceVTF->FrameCount();
|
||||
|
||||
// skip over spheremap
|
||||
targetFaceCount = pSourceVTF->FaceCount();
|
||||
if ( targetFaceCount == CUBEMAP_FACE_COUNT )
|
||||
{
|
||||
targetFaceCount = CUBEMAP_FACE_COUNT-1;
|
||||
}
|
||||
|
||||
// determine target format
|
||||
targetFormat = PreferredFormat( pSourceVTF, pSourceVTF->Format(), targetWidth, targetHeight, targetMipCount, targetFaceCount );
|
||||
|
||||
// reset nomip flags
|
||||
if ( bNoMip )
|
||||
{
|
||||
targetFlags |= TEXTUREFLAGS_NOMIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetFlags &= ~TEXTUREFLAGS_NOMIP;
|
||||
}
|
||||
|
||||
// the lowres texture is used for coarse light sampling lookups
|
||||
bHasLowResData = ( pSourceVTF->LowResFormat() != -1 ) && pSourceVTF->LowResWidth() && pSourceVTF->LowResHeight();
|
||||
if ( bHasLowResData )
|
||||
{
|
||||
// ensure lowres data is serialized in preferred runtime expected format
|
||||
targetLowResWidth = pSourceVTF->LowResWidth();
|
||||
targetLowResHeight = pSourceVTF->LowResHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
// discarding low res data, ensure lowres data is culled
|
||||
targetLowResWidth = 0;
|
||||
targetLowResHeight = 0;
|
||||
}
|
||||
|
||||
// start serializing output data
|
||||
// skip past header
|
||||
// serialize in order, 0) Header 1) ResourceDictionary, 3) Resources, 4) image
|
||||
// preload may extend into image
|
||||
targetBuf.EnsureCapacity( sizeof( VTFFileHeaderX360_t ) + targetResources.Count() * sizeof( ResourceEntryInfo ) );
|
||||
targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, sizeof( VTFFileHeaderX360_t ) + targetResources.Count() * sizeof( ResourceEntryInfo ) );
|
||||
|
||||
// serialize low res
|
||||
if ( targetLowResWidth && targetLowResHeight )
|
||||
{
|
||||
CUtlMemory<byte> targetLowResImage;
|
||||
|
||||
int sourceLowResImageSize = ImageLoader::GetMemRequired( pSourceVTF->LowResWidth(), pSourceVTF->LowResHeight(), 1, pSourceVTF->LowResFormat(), false );
|
||||
int targetLowResImageSize = ImageLoader::GetMemRequired( targetLowResWidth, targetLowResHeight, 1, IMAGE_FORMAT_RGB888, false );
|
||||
|
||||
// conversion may skip bytes, ensure all bits initialized
|
||||
targetLowResImage.EnsureCapacity( targetLowResImageSize );
|
||||
byte* pTargetLowResImage = (byte*)targetLowResImage.Base();
|
||||
memset( pTargetLowResImage, 0xFF, targetLowResImageSize );
|
||||
|
||||
// convert and save lowres image in final format
|
||||
bRetVal = ConvertImageFormatEx(
|
||||
pSourceVTF->LowResImageData(),
|
||||
sourceLowResImageSize,
|
||||
pSourceVTF->LowResFormat(),
|
||||
pTargetLowResImage,
|
||||
targetLowResImageSize,
|
||||
IMAGE_FORMAT_RGB888,
|
||||
targetLowResWidth,
|
||||
targetLowResHeight,
|
||||
false );
|
||||
if ( !bRetVal )
|
||||
{
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// boil to a single linear color
|
||||
Vector linearColor;
|
||||
linearColor.x = linearColor.y = linearColor.z = 0;
|
||||
for ( int j = 0; j < targetLowResWidth * targetLowResHeight; j++ )
|
||||
{
|
||||
linearColor.x += SrgbGammaToLinear( pTargetLowResImage[j*3+0] * 1.0f/255.0f );
|
||||
linearColor.y += SrgbGammaToLinear( pTargetLowResImage[j*3+1] * 1.0f/255.0f );
|
||||
linearColor.z += SrgbGammaToLinear( pTargetLowResImage[j*3+2] * 1.0f/255.0f );
|
||||
}
|
||||
VectorScale( linearColor, 1.0f/(targetLowResWidth * targetLowResHeight), linearColor );
|
||||
|
||||
// serialize as a single texel
|
||||
targetLowResSample[0] = 255.0f * SrgbLinearToGamma( linearColor[0] );
|
||||
targetLowResSample[1] = 255.0f * SrgbLinearToGamma( linearColor[1] );
|
||||
targetLowResSample[2] = 255.0f * SrgbLinearToGamma( linearColor[2] );
|
||||
|
||||
// identifies color presence
|
||||
targetLowResSample[3] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetLowResSample[0] = 0;
|
||||
targetLowResSample[1] = 0;
|
||||
targetLowResSample[2] = 0;
|
||||
targetLowResSample[3] = 0;
|
||||
}
|
||||
|
||||
// serialize resource data
|
||||
for ( int i=0; i<targetResources.Count(); i++ )
|
||||
{
|
||||
int resourceDataLength = targetResources[i].m_DataLength;
|
||||
if ( resourceDataLength == 4 )
|
||||
{
|
||||
// data goes directly into structure, as is
|
||||
targetResources[i].m_EntryInfo.eType |= RSRCF_HAS_NO_DATA_CHUNK;
|
||||
V_memcpy( &targetResources[i].m_EntryInfo.resData, targetResources[i].m_pData, 4 );
|
||||
}
|
||||
else if ( resourceDataLength != 0 )
|
||||
{
|
||||
targetResources[i].m_EntryInfo.resData = targetBuf.TellPut();
|
||||
int swappedLength = 0;
|
||||
byteSwapWriter.SwapBufferToTargetEndian( &swappedLength, &resourceDataLength );
|
||||
targetBuf.PutInt( swappedLength );
|
||||
if ( !targetBuf.IsValid() )
|
||||
{
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// put the data
|
||||
targetBuf.Put( targetResources[i].m_pData, resourceDataLength );
|
||||
if ( !targetBuf.IsValid() )
|
||||
{
|
||||
goto cleanUp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark end of preload data
|
||||
// preload data might be updated and pushed to extend into the image data mip chain
|
||||
preloadDataSize = targetBuf.TellPut();
|
||||
|
||||
// image starts on an aligned boundary
|
||||
AlignBuffer( targetBuf, 4 );
|
||||
|
||||
// start of image data
|
||||
targetImageDataOffset = targetBuf.TellPut();
|
||||
if ( targetImageDataOffset >= 65536 )
|
||||
{
|
||||
// possible bug, or may have to offset to 32 bits
|
||||
Msg( "ConvertVTFTo360Format: non-image portion exceeds 16 bit boundary %s\n", pDebugName );
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// format conversion, data is stored by ascending mips, 1x1 up to NxN
|
||||
// data is stored ascending to allow picmipped loads
|
||||
for ( mip = targetMipCount - 1; mip >= 0; mip-- )
|
||||
{
|
||||
for ( frame = 0; frame < targetFrameCount; frame++ )
|
||||
{
|
||||
for ( face = 0; face < targetFaceCount; face++ )
|
||||
{
|
||||
if ( !SerializeImageData( pSourceVTF, frame, face, mip, targetFormat, targetBuf ) )
|
||||
{
|
||||
goto cleanUp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( preloadDataSize < VTFFileHeaderSize( VTF_X360_MAJOR_VERSION, VTF_X360_MINOR_VERSION ) )
|
||||
{
|
||||
// preload size must be at least what game attempts to initially read
|
||||
preloadDataSize = VTFFileHeaderSize( VTF_X360_MAJOR_VERSION, VTF_X360_MINOR_VERSION );
|
||||
}
|
||||
|
||||
if ( targetBuf.TellPut() <= PRELOAD_VTF_THRESHOLD )
|
||||
{
|
||||
// the entire file is too small, preload entirely
|
||||
preloadDataSize = targetBuf.TellPut();
|
||||
}
|
||||
|
||||
if ( preloadDataSize >= 65536 )
|
||||
{
|
||||
// possible overflow due to large frames, faces, and format, may have to offset to 32 bits
|
||||
Msg( "ConvertVTFTo360Format: preload portion exceeds 16 bit boundary %s\n", pDebugName );
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// finalize header
|
||||
V_memset( &targetHeader, 0, sizeof( VTFFileHeaderX360_t ) );
|
||||
|
||||
V_memcpy( targetHeader.fileTypeString, "VTFX", 4 );
|
||||
targetHeader.version[0] = VTF_X360_MAJOR_VERSION;
|
||||
targetHeader.version[1] = VTF_X360_MINOR_VERSION;
|
||||
targetHeader.headerSize = sizeof( VTFFileHeaderX360_t ) + targetResources.Count() * sizeof( ResourceEntryInfo );
|
||||
|
||||
targetHeader.flags = targetFlags;
|
||||
targetHeader.width = targetWidth;
|
||||
targetHeader.height = targetHeight;
|
||||
targetHeader.depth = 1;
|
||||
targetHeader.numFrames = targetFrameCount;
|
||||
targetHeader.preloadDataSize = preloadDataSize;
|
||||
targetHeader.mipSkipCount = mipSkipCount;
|
||||
targetHeader.numResources = targetResources.Count();
|
||||
VectorCopy( pSourceVTF->Reflectivity(), targetHeader.reflectivity );
|
||||
targetHeader.bumpScale = pSourceVTF->BumpScale();
|
||||
targetHeader.imageFormat = targetFormat;
|
||||
targetHeader.lowResImageSample[0] = targetLowResSample[0];
|
||||
targetHeader.lowResImageSample[1] = targetLowResSample[1];
|
||||
targetHeader.lowResImageSample[2] = targetLowResSample[2];
|
||||
targetHeader.lowResImageSample[3] = targetLowResSample[3];
|
||||
|
||||
if ( !IsX360() )
|
||||
{
|
||||
byteSwapWriter.SwapFieldsToTargetEndian( &targetHeader );
|
||||
}
|
||||
|
||||
// write out finalized header
|
||||
targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
targetBuf.Put( &targetHeader, sizeof( VTFFileHeaderX360_t ) );
|
||||
if ( !targetBuf.IsValid() )
|
||||
{
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
// fixup and write out finalized resource dictionary
|
||||
for ( int i=0; i<targetResources.Count(); i++ )
|
||||
{
|
||||
switch ( targetResources[i].m_EntryInfo.eType & ~RSRCF_MASK )
|
||||
{
|
||||
case VTF_LEGACY_RSRC_IMAGE:
|
||||
targetResources[i].m_EntryInfo.resData = targetImageDataOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !( targetResources[i].m_EntryInfo.eType & RSRCF_HAS_NO_DATA_CHUNK ) )
|
||||
{
|
||||
// swap the offset holders only
|
||||
byteSwapWriter.SwapBufferToTargetEndian( &targetResources[i].m_EntryInfo.resData );
|
||||
}
|
||||
|
||||
targetBuf.Put( &targetResources[i].m_EntryInfo, sizeof( ResourceEntryInfo ) );
|
||||
if ( !targetBuf.IsValid() )
|
||||
{
|
||||
goto cleanUp;
|
||||
}
|
||||
}
|
||||
|
||||
targetBuf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
|
||||
|
||||
if ( preloadDataSize < targetBuf.TellPut() && pCompressFunc )
|
||||
{
|
||||
// only compress files that are not entirely in preload
|
||||
CUtlBuffer compressedBuffer;
|
||||
targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, targetImageDataOffset );
|
||||
bool bCompressed = pCompressFunc( targetBuf, compressedBuffer );
|
||||
if ( bCompressed )
|
||||
{
|
||||
// copy all the header data off
|
||||
CUtlBuffer headerBuffer;
|
||||
headerBuffer.EnsureCapacity( targetImageDataOffset );
|
||||
headerBuffer.Put( targetBuf.Base(), targetImageDataOffset );
|
||||
|
||||
// reform the target with the header and then the compressed data
|
||||
targetBuf.Clear();
|
||||
targetBuf.Put( headerBuffer.Base(), targetImageDataOffset );
|
||||
targetBuf.Put( compressedBuffer.Base(), compressedBuffer.TellPut() );
|
||||
|
||||
VTFFileHeaderX360_t *pHeader = (VTFFileHeaderX360_t *)targetBuf.Base();
|
||||
if ( !IsX360() )
|
||||
{
|
||||
// swap it back into pc space
|
||||
byteSwapWriter.SwapFieldsToTargetEndian( pHeader );
|
||||
}
|
||||
|
||||
pHeader->compressedSize = compressedBuffer.TellPut();
|
||||
|
||||
if ( !IsX360() )
|
||||
{
|
||||
// swap it back into 360 space
|
||||
byteSwapWriter.SwapFieldsToTargetEndian( pHeader );
|
||||
}
|
||||
}
|
||||
|
||||
targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
}
|
||||
|
||||
// success
|
||||
bRetVal = true;
|
||||
|
||||
cleanUp:
|
||||
if ( pSourceVTF )
|
||||
{
|
||||
DestroyVTFTexture( pSourceVTF );
|
||||
}
|
||||
|
||||
for ( int i=0; i<targetResources.Count(); i++ )
|
||||
{
|
||||
delete [] (char *)targetResources[i].m_pData;
|
||||
targetResources[i].m_pData = NULL;
|
||||
}
|
||||
|
||||
return bRetVal;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copy the 360 preload data into a buffer. Used by tools to request the preload,
|
||||
// as part of the preload build process. Caller doesn't have to know cracking details.
|
||||
// Not to be used at gametime.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GetVTFPreload360Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
|
||||
{
|
||||
preloadBufferOut.Purge();
|
||||
|
||||
fileBufferIn.ActivateByteSwapping( IsPC() );
|
||||
|
||||
VTFFileHeaderX360_t header;
|
||||
fileBufferIn.GetObjects( &header );
|
||||
|
||||
if ( V_strnicmp( header.fileTypeString, "VTFX", 4 ) ||
|
||||
header.version[0] != VTF_X360_MAJOR_VERSION ||
|
||||
header.version[1] != VTF_X360_MINOR_VERSION )
|
||||
{
|
||||
// bad format
|
||||
return false;
|
||||
}
|
||||
|
||||
preloadBufferOut.EnsureCapacity( header.preloadDataSize );
|
||||
preloadBufferOut.Put( fileBufferIn.Base(), header.preloadDataSize );
|
||||
|
||||
return true;
|
||||
}
|
441
vtf/cvtf.h
Normal file
441
vtf/cvtf.h
Normal file
@ -0,0 +1,441 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Local header for CVTFTexture class declaration - allows platform-specific
|
||||
// implementation to be placed in separate cpp files.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef CVTF_H
|
||||
#define CVTF_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "s3tc_decode.h"
|
||||
#include "vtf/vtf.h"
|
||||
#include "byteswap.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
class CEdgePos
|
||||
{
|
||||
public:
|
||||
CEdgePos() {}
|
||||
CEdgePos( int ix, int iy )
|
||||
{
|
||||
x = ix;
|
||||
y = iy;
|
||||
}
|
||||
|
||||
void operator +=( const CEdgePos &other )
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
}
|
||||
|
||||
void operator /=( int val )
|
||||
{
|
||||
x /= val;
|
||||
y /= val;
|
||||
}
|
||||
|
||||
CEdgePos operator >>( int shift )
|
||||
{
|
||||
return CEdgePos( x >> shift, y >> shift );
|
||||
}
|
||||
|
||||
CEdgePos operator *( int shift )
|
||||
{
|
||||
return CEdgePos( x * shift, y * shift );
|
||||
}
|
||||
|
||||
CEdgePos operator -( const CEdgePos &other )
|
||||
{
|
||||
return CEdgePos( x - other.x, y - other.y );
|
||||
}
|
||||
|
||||
CEdgePos operator +( const CEdgePos &other )
|
||||
{
|
||||
return CEdgePos( x + other.x, y + other.y );
|
||||
}
|
||||
|
||||
bool operator!=( const CEdgePos &other )
|
||||
{
|
||||
return !( *this == other );
|
||||
}
|
||||
|
||||
bool operator==( const CEdgePos &other )
|
||||
{
|
||||
return x==other.x && y==other.y;
|
||||
}
|
||||
|
||||
int x, y;
|
||||
};
|
||||
|
||||
|
||||
class CEdgeIncrements
|
||||
{
|
||||
public:
|
||||
CEdgePos iFace1Start, iFace1End;
|
||||
CEdgePos iFace1Inc, iFace2Inc;
|
||||
CEdgePos iFace2Start, iFace2End;
|
||||
};
|
||||
|
||||
|
||||
class CEdgeMatch
|
||||
{
|
||||
public:
|
||||
int m_iFaces[2]; // Which faces are touching.
|
||||
int m_iEdges[2]; // Which edge on each face is touching.
|
||||
int m_iCubeVerts[2];// Which of the cube's verts comprise this edge?
|
||||
bool m_bFlipFace2Edge;
|
||||
};
|
||||
|
||||
|
||||
class CCornerMatch
|
||||
{
|
||||
public:
|
||||
// The info for the 3 edges that match at this corner.
|
||||
int m_iFaces[3];
|
||||
int m_iFaceEdges[3];
|
||||
};
|
||||
|
||||
|
||||
class CEdgeFaceIndex
|
||||
{
|
||||
public:
|
||||
int m_iEdge;
|
||||
int m_iFace;
|
||||
};
|
||||
|
||||
|
||||
#define NUM_EDGE_MATCHES 12
|
||||
#define NUM_CORNER_MATCHES 8
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implementation of the VTF Texture
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVTFTexture : public IVTFTexture
|
||||
{
|
||||
public:
|
||||
CVTFTexture();
|
||||
virtual ~CVTFTexture();
|
||||
|
||||
virtual bool Init( int nWidth, int nHeight, int nDepth, ImageFormat fmt, int iFlags, int iFrameCount, int nForceMipCount );
|
||||
|
||||
// Methods to initialize the low-res image
|
||||
virtual void InitLowResImage( int nWidth, int nHeight, ImageFormat fmt );
|
||||
|
||||
virtual void *SetResourceData( uint32 eType, void const *pData, size_t nDataSize );
|
||||
virtual void *GetResourceData( uint32 eType, size_t *pDataSize ) const;
|
||||
|
||||
// Locates the resource entry info if it's present, easier than crawling array types
|
||||
virtual bool HasResourceEntry( uint32 eType ) const;
|
||||
|
||||
// Retrieve available resource types of this IVTFTextures
|
||||
// arrTypesBuffer buffer to be filled with resource types available.
|
||||
// numTypesBufferElems how many resource types the buffer can accomodate.
|
||||
// Returns:
|
||||
// number of resource types available (can be greater than "numTypesBufferElems"
|
||||
// in which case only first "numTypesBufferElems" are copied to "arrTypesBuffer")
|
||||
virtual unsigned int GetResourceTypes( uint32 *arrTypesBuffer, int numTypesBufferElems ) const;
|
||||
|
||||
// Methods to set other texture fields
|
||||
virtual void SetBumpScale( float flScale );
|
||||
virtual void SetReflectivity( const Vector &vecReflectivity );
|
||||
|
||||
// These are methods to help with optimization of file access
|
||||
virtual void LowResFileInfo( int *pStartLocation, int *pSizeInBytes ) const;
|
||||
virtual void ImageFileInfo( int nFrame, int nFace, int nMip, int *pStartLocation, int *pSizeInBytes) const;
|
||||
virtual int FileSize( int nMipSkipCount = 0 ) const;
|
||||
|
||||
// When unserializing, we can skip a certain number of mip levels,
|
||||
// and we also can just load everything but the image data
|
||||
virtual bool Unserialize( CUtlBuffer &buf, bool bBufferHeaderOnly = false, int nSkipMipLevels = 0 );
|
||||
virtual bool UnserializeEx( CUtlBuffer &buf, bool bHeaderOnly = false, int nForceFlags = 0, int nSkipMipLevels = 0 );
|
||||
virtual bool Serialize( CUtlBuffer &buf );
|
||||
|
||||
virtual void GetMipmapRange( int* pOutFinest, int* pOutCoarsest );
|
||||
|
||||
// Attributes...
|
||||
virtual int Width() const;
|
||||
virtual int Height() const;
|
||||
virtual int Depth() const;
|
||||
virtual int MipCount() const;
|
||||
|
||||
virtual int RowSizeInBytes( int nMipLevel ) const;
|
||||
virtual int FaceSizeInBytes( int nMipLevel ) const;
|
||||
|
||||
virtual ImageFormat Format() const;
|
||||
virtual int FaceCount() const;
|
||||
virtual int FrameCount() const;
|
||||
virtual int Flags() const;
|
||||
|
||||
virtual float BumpScale() const;
|
||||
virtual const Vector &Reflectivity() const;
|
||||
|
||||
virtual bool IsCubeMap() const;
|
||||
virtual bool IsNormalMap() const;
|
||||
virtual bool IsVolumeTexture() const;
|
||||
|
||||
virtual int LowResWidth() const;
|
||||
virtual int LowResHeight() const;
|
||||
virtual ImageFormat LowResFormat() const;
|
||||
|
||||
// Computes the size (in bytes) of a single mipmap of a single face of a single frame
|
||||
virtual int ComputeMipSize( int iMipLevel ) const;
|
||||
|
||||
// Computes the size (in bytes) of a single face of a single frame
|
||||
// All mip levels starting at the specified mip level are included
|
||||
virtual int ComputeFaceSize( int iStartingMipLevel = 0 ) const;
|
||||
|
||||
// Computes the total size of all faces, all frames
|
||||
virtual int ComputeTotalSize( ) const;
|
||||
|
||||
// Computes the dimensions of a particular mip level
|
||||
virtual void ComputeMipLevelDimensions( int iMipLevel, int *pWidth, int *pHeight, int *pMipDepth ) const;
|
||||
|
||||
// Computes the size of a subrect (specified at the top mip level) at a particular lower mip level
|
||||
virtual void ComputeMipLevelSubRect( Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect ) const;
|
||||
|
||||
// Returns the base address of the image data
|
||||
virtual unsigned char *ImageData();
|
||||
|
||||
// Returns a pointer to the data associated with a particular frame, face, and mip level
|
||||
virtual unsigned char *ImageData( int iFrame, int iFace, int iMipLevel );
|
||||
|
||||
// Returns a pointer to the data associated with a particular frame, face, mip level, and offset
|
||||
virtual unsigned char *ImageData( int iFrame, int iFace, int iMipLevel, int x, int y, int z );
|
||||
|
||||
// Returns the base address of the low-res image data
|
||||
virtual unsigned char *LowResImageData();
|
||||
|
||||
// Converts the texture's image format. Use IMAGE_FORMAT_DEFAULT
|
||||
virtual void ConvertImageFormat( ImageFormat fmt, bool bNormalToDUDV );
|
||||
|
||||
// Generate spheremap based on the current cube faces (only works for cubemaps)
|
||||
// The look dir indicates the direction of the center of the sphere
|
||||
virtual void GenerateSpheremap( LookDir_t lookDir );
|
||||
|
||||
virtual void GenerateHemisphereMap( unsigned char *pSphereMapBitsRGBA, int targetWidth,
|
||||
int targetHeight, LookDir_t lookDir, int iFrame );
|
||||
|
||||
// Fixes the cubemap faces orientation from our standard to the
|
||||
// standard the material system needs.
|
||||
virtual void FixCubemapFaceOrientation( );
|
||||
|
||||
// Normalize the top mip level if necessary
|
||||
virtual void NormalizeTopMipLevel();
|
||||
|
||||
// Generates mipmaps from the base mip levels
|
||||
virtual void GenerateMipmaps();
|
||||
|
||||
// Put 1/miplevel (1..n) into alpha.
|
||||
virtual void PutOneOverMipLevelInAlpha();
|
||||
|
||||
// Computes the reflectivity
|
||||
virtual void ComputeReflectivity( );
|
||||
|
||||
// Computes the alpha flags
|
||||
virtual void ComputeAlphaFlags();
|
||||
|
||||
// Gets the texture all internally consistent assuming you've loaded
|
||||
// mip 0 of all faces of all frames
|
||||
virtual void PostProcess(bool bGenerateSpheremap, LookDir_t lookDir = LOOK_DOWN_Z, bool bAllowFixCubemapOrientation = true);
|
||||
virtual void SetPostProcessingSettings( VtfProcessingOptions const *pOptions );
|
||||
|
||||
// Generate the low-res image bits
|
||||
virtual bool ConstructLowResImage();
|
||||
|
||||
virtual void MatchCubeMapBorders( int iStage, ImageFormat finalFormat, bool bSkybox );
|
||||
|
||||
// Sets threshhold values for alphatest mipmapping
|
||||
virtual void SetAlphaTestThreshholds( float flBase, float flHighFreq );
|
||||
|
||||
#if defined( _X360 )
|
||||
virtual int UpdateOrCreate( const char *pFilename, const char *pPathID = NULL, bool bForce = false );
|
||||
virtual int FileSize( bool bPreloadOnly, int nMipSkipCount ) const;
|
||||
virtual bool UnserializeFromBuffer( CUtlBuffer &buf, bool bBufferIsVolatile, bool bHeaderOnly, bool bPreloadOnly, int nMipSkipCount );
|
||||
virtual bool IsPreTiled() const;
|
||||
virtual int MappingWidth() const;
|
||||
virtual int MappingHeight() const;
|
||||
virtual int MappingDepth() const;
|
||||
virtual int MipSkipCount() const;
|
||||
virtual unsigned char *LowResImageSample();
|
||||
virtual void ReleaseImageMemory();
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Unserialization
|
||||
bool ReadHeader( CUtlBuffer &buf, VTFFileHeader_t &header );
|
||||
|
||||
void BlendCubeMapEdgePalettes(
|
||||
int iFrame,
|
||||
int iMipLevel,
|
||||
const CEdgeMatch *pMatch );
|
||||
|
||||
void BlendCubeMapCornerPalettes(
|
||||
int iFrame,
|
||||
int iMipLevel,
|
||||
const CCornerMatch *pMatch );
|
||||
|
||||
void MatchCubeMapS3TCPalettes(
|
||||
CEdgeMatch edgeMatches[NUM_EDGE_MATCHES],
|
||||
CCornerMatch cornerMatches[NUM_CORNER_MATCHES]
|
||||
);
|
||||
|
||||
void SetupFaceVert( int iMipLevel, int iVert, CEdgePos &out );
|
||||
void SetupEdgeIncrement( CEdgePos &start, CEdgePos &end, CEdgePos &inc );
|
||||
|
||||
void SetupTextureEdgeIncrements(
|
||||
int iMipLevel,
|
||||
int iFace1Edge,
|
||||
int iFace2Edge,
|
||||
bool bFlipFace2Edge,
|
||||
CEdgeIncrements *incs );
|
||||
|
||||
void BlendCubeMapFaceEdges(
|
||||
int iFrame,
|
||||
int iMipLevel,
|
||||
const CEdgeMatch *pMatch );
|
||||
|
||||
void BlendCubeMapFaceCorners(
|
||||
int iFrame,
|
||||
int iMipLevel,
|
||||
const CCornerMatch *pMatch );
|
||||
|
||||
void BuildCubeMapMatchLists( CEdgeMatch edgeMatches[NUM_EDGE_MATCHES], CCornerMatch cornerMatches[NUM_CORNER_MATCHES], bool bSkybox );
|
||||
|
||||
// Allocate image data blocks with an eye toward re-using memory
|
||||
bool AllocateImageData( int nMemorySize );
|
||||
bool AllocateLowResImageData( int nMemorySize );
|
||||
|
||||
// Compute the mip count based on the size + flags
|
||||
int ComputeMipCount( ) const;
|
||||
|
||||
// Unserialization of low-res data
|
||||
bool LoadLowResData( CUtlBuffer &buf );
|
||||
|
||||
// Unserialization of new resource data
|
||||
bool LoadNewResources( CUtlBuffer &buf );
|
||||
|
||||
// Unserialization of image data
|
||||
bool LoadImageData( CUtlBuffer &buf, const VTFFileHeader_t &header, int nSkipMipLevels );
|
||||
|
||||
// Shutdown
|
||||
void Shutdown();
|
||||
void ReleaseResources();
|
||||
|
||||
// Makes a single frame of spheremap
|
||||
void ComputeSpheremapFrame( unsigned char **ppCubeFaces, unsigned char *pSpheremap, LookDir_t lookDir );
|
||||
|
||||
// Makes a single frame of spheremap
|
||||
void ComputeHemispheremapFrame( unsigned char **ppCubeFaces, unsigned char *pSpheremap, LookDir_t lookDir );
|
||||
|
||||
// Serialization of image data
|
||||
bool WriteImageData( CUtlBuffer &buf );
|
||||
|
||||
// Computes the size (in bytes) of a single mipmap of a single face of a single frame
|
||||
int ComputeMipSize( int iMipLevel, ImageFormat fmt ) const;
|
||||
|
||||
// Computes the size (in bytes) of a single face of a single frame
|
||||
// All mip levels starting at the specified mip level are included
|
||||
int ComputeFaceSize( int iStartingMipLevel, ImageFormat fmt ) const;
|
||||
|
||||
// Computes the total size of all faces, all frames
|
||||
int ComputeTotalSize( ImageFormat fmt ) const;
|
||||
|
||||
// Computes the location of a particular face, frame, and mip level
|
||||
int GetImageOffset( int iFrame, int iFace, int iMipLevel, ImageFormat fmt ) const;
|
||||
|
||||
// Determines if the vtf or vtfx file needs to be swapped to the current platform
|
||||
bool SetupByteSwap( CUtlBuffer &buf );
|
||||
|
||||
// Locates the resource entry info if it's present
|
||||
ResourceEntryInfo *FindResourceEntryInfo( unsigned int eType );
|
||||
ResourceEntryInfo const *FindResourceEntryInfo( unsigned int eType ) const;
|
||||
|
||||
// Inserts the resource entry info if it's not present
|
||||
ResourceEntryInfo *FindOrCreateResourceEntryInfo( unsigned int eType );
|
||||
|
||||
// Removes the resource entry info if it's present
|
||||
bool RemoveResourceEntryInfo( unsigned int eType );
|
||||
|
||||
#if defined( _X360 )
|
||||
bool ReadHeader( CUtlBuffer &buf, VTFFileHeaderX360_t &header );
|
||||
bool LoadImageData( CUtlBuffer &buf, bool bBufferIsVolatile, int nMipSkipCount );
|
||||
#endif
|
||||
|
||||
private:
|
||||
// This is to make sure old-format .vtf files are read properly
|
||||
int m_nVersion[2];
|
||||
|
||||
int m_nWidth;
|
||||
int m_nHeight;
|
||||
int m_nDepth;
|
||||
ImageFormat m_Format;
|
||||
|
||||
int m_nMipCount;
|
||||
int m_nFaceCount;
|
||||
int m_nFrameCount;
|
||||
|
||||
int m_nImageAllocSize;
|
||||
int m_nFlags;
|
||||
unsigned char *m_pImageData;
|
||||
|
||||
Vector m_vecReflectivity;
|
||||
float m_flBumpScale;
|
||||
|
||||
// FIXME: Do I need this?
|
||||
int m_iStartFrame;
|
||||
|
||||
// Low res data
|
||||
int m_nLowResImageAllocSize;
|
||||
ImageFormat m_LowResImageFormat;
|
||||
int m_nLowResImageWidth;
|
||||
int m_nLowResImageHeight;
|
||||
unsigned char *m_pLowResImageData;
|
||||
|
||||
// Used while fixing mipmap edges.
|
||||
CUtlVector<S3RGBA> m_OriginalData;
|
||||
|
||||
// Alpha threshholds
|
||||
float m_flAlphaThreshhold;
|
||||
float m_flAlphaHiFreqThreshhold;
|
||||
|
||||
CByteswap m_Swap;
|
||||
|
||||
int m_nFinestMipmapLevel;
|
||||
int m_nCoarsestMipmapLevel;
|
||||
|
||||
#if defined( _X360 )
|
||||
int m_iPreloadDataSize;
|
||||
int m_iCompressedSize;
|
||||
// resolves actual dimensions to/from mapping dimensions due to pre-picmipping
|
||||
int m_nMipSkipCount;
|
||||
unsigned char m_LowResImageSample[4];
|
||||
#endif
|
||||
|
||||
CUtlVector< ResourceEntryInfo > m_arrResourcesInfo;
|
||||
|
||||
struct ResourceMemorySection
|
||||
{
|
||||
ResourceMemorySection() { memset( this, 0, sizeof( *this ) ); }
|
||||
|
||||
int m_nDataAllocSize;
|
||||
int m_nDataLength;
|
||||
unsigned char *m_pData;
|
||||
|
||||
bool AllocateData( int nMemorySize );
|
||||
bool LoadData( CUtlBuffer &buf, CByteswap &byteSwap );
|
||||
bool WriteData( CUtlBuffer &buf ) const;
|
||||
};
|
||||
CUtlVector< ResourceMemorySection > m_arrResourcesData;
|
||||
CUtlVector< ResourceMemorySection > m_arrResourcesData_ForReuse; // Maintained to keep allocated memory blocks when unserializing from files
|
||||
|
||||
VtfProcessingOptions m_Options;
|
||||
};
|
||||
|
||||
#endif // CVTF_H
|
395
vtf/s3tc_decode.cpp
Normal file
395
vtf/s3tc_decode.cpp
Normal file
@ -0,0 +1,395 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include "bitmap/imageformat.h"
|
||||
#include "basetypes.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include "nvtc.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "mathlib/vector.h"
|
||||
#include "utlmemory.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "s3tc_decode.h"
|
||||
#include "utlvector.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// This is in s3tc.lib. Nvidia added it specially for us. It can be set to 4, 8, or 12.
|
||||
// When set to 8 or 12, it generates a palette given an 8x4 or 12x4 texture.
|
||||
extern int S3TC_BLOCK_WIDTH;
|
||||
|
||||
|
||||
class S3Palette
|
||||
{
|
||||
public:
|
||||
S3RGBA m_Colors[4];
|
||||
};
|
||||
|
||||
|
||||
class S3TCBlock_DXT1
|
||||
{
|
||||
public:
|
||||
unsigned short m_Ref1; // The two colors that this block blends betwixt.
|
||||
unsigned short m_Ref2;
|
||||
unsigned int m_PixelBits;
|
||||
};
|
||||
|
||||
|
||||
class S3TCBlock_DXT5
|
||||
{
|
||||
public:
|
||||
unsigned char m_AlphaRef[2];
|
||||
unsigned char m_AlphaBits[6];
|
||||
|
||||
unsigned short m_Ref1; // The two colors that this block blends betwixt.
|
||||
unsigned short m_Ref2;
|
||||
unsigned int m_PixelBits;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// S3TCBlock
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
|
||||
int ReadBitInt( const char *pBits, int iBaseBit, int nBits )
|
||||
{
|
||||
int ret = 0;
|
||||
for ( int i=0; i < nBits; i++ )
|
||||
{
|
||||
int iBit = iBaseBit + i;
|
||||
int val = ((pBits[iBit>>3] >> (iBit&7)) & 1) << i;
|
||||
ret |= val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WriteBitInt( char *pBits, int iBaseBit, int nBits, int val )
|
||||
{
|
||||
for ( int i=0; i < nBits; i++ )
|
||||
{
|
||||
int iBit = iBaseBit + i;
|
||||
pBits[iBit>>3] &= ~(1 << (iBit & 7));
|
||||
if ( (val >> i) & 1 )
|
||||
pBits[iBit>>3] |= (1 << (iBit & 7));
|
||||
}
|
||||
}
|
||||
|
||||
int S3TC_BytesPerBlock( ImageFormat format )
|
||||
{
|
||||
if ( format == IMAGE_FORMAT_DXT1 || format == IMAGE_FORMAT_ATI1N )
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( format == IMAGE_FORMAT_DXT5 || format == IMAGE_FORMAT_ATI2N );
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// We're not using this, but I'll keep it around for reference.
|
||||
void S3TC_BuildPalette( ImageFormat format, const char *pS3Block, S3RGBA palette[4] )
|
||||
{
|
||||
if ( format == IMAGE_FORMAT_DXT1 )
|
||||
{
|
||||
const S3TCBlock_DXT1 *pBlock = reinterpret_cast<const S3TCBlock_DXT1 *>( pS3Block );
|
||||
|
||||
palette[0] = S3TC_RGBAFrom565( pBlock->m_Ref1, 255 );
|
||||
|
||||
if ( pBlock->m_Ref1 <= pBlock->m_Ref2 )
|
||||
{
|
||||
// Opaque and transparent texels are defined. The lookup is 3 colors. 11 means
|
||||
// a black, transparent pixel.
|
||||
palette[1] = S3TC_RGBAFrom565( pBlock->m_Ref2, 255 );
|
||||
palette[2] = S3TC_RGBABlend( palette[0], palette[1], 1, 1, 2 );
|
||||
palette[3].r = palette[3].g = palette[3].b = palette[3].a = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only opaque texels are defined. The lookup is 4 colors.
|
||||
palette[1] = S3TC_RGBAFrom565( pBlock->m_Ref2, 255 );
|
||||
palette[2] = S3TC_RGBABlend( palette[0], palette[1], 2, 1, 3 );
|
||||
palette[3] = S3TC_RGBABlend( palette[0], palette[1], 1, 2, 3 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( format == IMAGE_FORMAT_DXT5 );
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
S3PaletteIndex S3TC_GetPixelPaletteIndex( ImageFormat format, const char *pS3Block, int x, int y )
|
||||
{
|
||||
Assert( x >= 0 && x < 4 );
|
||||
Assert( y >= 0 && y < 4 );
|
||||
int iQuadPixel = y*4 + x;
|
||||
S3PaletteIndex ret = { 0, 0 };
|
||||
|
||||
if ( format == IMAGE_FORMAT_DXT1 )
|
||||
{
|
||||
const S3TCBlock_DXT1 *pBlock = reinterpret_cast<const S3TCBlock_DXT1 *>( pS3Block );
|
||||
ret.m_ColorIndex = (pBlock->m_PixelBits >> (iQuadPixel << 1)) & 3;
|
||||
ret.m_AlphaIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( format == IMAGE_FORMAT_DXT5 );
|
||||
|
||||
const S3TCBlock_DXT5 *pBlock = reinterpret_cast<const S3TCBlock_DXT5 *>( pS3Block );
|
||||
|
||||
int64 &alphaBits = *((int64*)pBlock->m_AlphaBits);
|
||||
ret.m_ColorIndex = (unsigned char)((pBlock->m_PixelBits >> (iQuadPixel << 1)) & 3);
|
||||
ret.m_AlphaIndex = (unsigned char)((alphaBits >> (iQuadPixel * 3)) & 7);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void S3TC_SetPixelPaletteIndex( ImageFormat format, char *pS3Block, int x, int y, S3PaletteIndex iPaletteIndex )
|
||||
{
|
||||
Assert( x >= 0 && x < 4 );
|
||||
Assert( y >= 0 && y < 4 );
|
||||
Assert( iPaletteIndex.m_ColorIndex >= 0 && iPaletteIndex.m_ColorIndex < 4 );
|
||||
Assert( iPaletteIndex.m_AlphaIndex >= 0 && iPaletteIndex.m_AlphaIndex < 8 );
|
||||
|
||||
int iQuadPixel = y*4 + x;
|
||||
int iColorBit = iQuadPixel * 2;
|
||||
|
||||
if ( format == IMAGE_FORMAT_DXT1 )
|
||||
{
|
||||
S3TCBlock_DXT1 *pBlock = reinterpret_cast<S3TCBlock_DXT1 *>( pS3Block );
|
||||
|
||||
pBlock->m_PixelBits &= ~( 3 << iColorBit );
|
||||
pBlock->m_PixelBits |= (unsigned int)iPaletteIndex.m_ColorIndex << iColorBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( format == IMAGE_FORMAT_DXT5 );
|
||||
|
||||
S3TCBlock_DXT5 *pBlock = reinterpret_cast<S3TCBlock_DXT5 *>( pS3Block );
|
||||
|
||||
// Copy the color portion in.
|
||||
pBlock->m_PixelBits &= ~( 3 << iColorBit );
|
||||
pBlock->m_PixelBits |= (unsigned int)iPaletteIndex.m_ColorIndex << iColorBit;
|
||||
|
||||
// Copy the alpha portion in.
|
||||
WriteBitInt( (char*)pBlock->m_AlphaBits, iQuadPixel*3, 3, iPaletteIndex.m_AlphaIndex );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char* S3TC_GetBlock(
|
||||
const void *pCompressed,
|
||||
ImageFormat format,
|
||||
int nBlocksWidth,
|
||||
int xBlock,
|
||||
int yBlock )
|
||||
{
|
||||
int nBytesPerBlock = S3TC_BytesPerBlock( format );
|
||||
return &((const char*)pCompressed)[ ((yBlock * nBlocksWidth) + xBlock) * nBytesPerBlock ];
|
||||
}
|
||||
|
||||
|
||||
char* S3TC_GetBlock(
|
||||
void *pCompressed,
|
||||
ImageFormat format,
|
||||
int nBlocksWidth,
|
||||
int xBlock,
|
||||
int yBlock )
|
||||
{
|
||||
return (char*)S3TC_GetBlock( (const void *)pCompressed, format, nBlocksWidth, xBlock, yBlock );
|
||||
}
|
||||
|
||||
|
||||
void GenerateRepresentativePalette(
|
||||
ImageFormat format,
|
||||
S3RGBA **pOriginals, // Original RGBA colors in the texture. This allows it to avoid doubly compressing.
|
||||
int nBlocks,
|
||||
int lPitch, // (in BYTES)
|
||||
char mergedBlocks[16*MAX_S3TC_BLOCK_BYTES]
|
||||
)
|
||||
{
|
||||
Error( "GenerateRepresentativePalette: not implemented" );
|
||||
#if 0 // this code was ifdefed out. no idea under what circumstances it was meant to be called.
|
||||
|
||||
Assert( nBlocks == 2 || nBlocks == 3 );
|
||||
|
||||
S3RGBA values[12*4];
|
||||
memset( values, 0xFF, sizeof( values ) );
|
||||
int width = nBlocks * 4;
|
||||
for ( int i=0; i < nBlocks; i++ )
|
||||
{
|
||||
for ( int y=0; y < 4; y++ )
|
||||
{
|
||||
for ( int x=0; x < 4; x++ )
|
||||
{
|
||||
int outIndex = y*width+(i*4+x);
|
||||
values[outIndex] = pOriginals[i][y * (lPitch/4) + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DDSURFACEDESC descIn;
|
||||
DDSURFACEDESC descOut;
|
||||
memset( &descIn, 0, sizeof(descIn) );
|
||||
memset( &descOut, 0, sizeof(descOut) );
|
||||
|
||||
descIn.dwSize = sizeof(descIn);
|
||||
descIn.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_LPSURFACE | DDSD_PIXELFORMAT;
|
||||
descIn.dwWidth = width;
|
||||
descIn.dwHeight = 4;
|
||||
descIn.lPitch = width * 4;
|
||||
descIn.lpSurface = values;
|
||||
descIn.ddpfPixelFormat.dwSize = sizeof( DDPIXELFORMAT );
|
||||
|
||||
descIn.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
|
||||
descIn.ddpfPixelFormat.dwRGBBitCount = 32;
|
||||
descIn.ddpfPixelFormat.dwRBitMask = 0xff0000;
|
||||
descIn.ddpfPixelFormat.dwGBitMask = 0x00ff00;
|
||||
descIn.ddpfPixelFormat.dwBBitMask = 0x0000ff;
|
||||
descIn.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
|
||||
|
||||
descOut.dwSize = sizeof( descOut );
|
||||
|
||||
float weight[3] = {0.3086f, 0.6094f, 0.0820f};
|
||||
|
||||
S3TC_BLOCK_WIDTH = nBlocks * 4;
|
||||
|
||||
DWORD encodeFlags = S3TC_ENCODE_RGB_FULL;
|
||||
if ( format == IMAGE_FORMAT_DXT5 )
|
||||
encodeFlags |= S3TC_ENCODE_ALPHA_INTERPOLATED;
|
||||
|
||||
S3TCencode( &descIn, NULL, &descOut, mergedBlocks, encodeFlags, weight );
|
||||
|
||||
S3TC_BLOCK_WIDTH = 4;
|
||||
#endif
|
||||
}
|
||||
|
||||
void S3TC_MergeBlocks(
|
||||
char **blocks,
|
||||
S3RGBA **pOriginals, // Original RGBA colors in the texture. This allows it to avoid doubly compressing.
|
||||
int nBlocks,
|
||||
int lPitch, // (in BYTES)
|
||||
ImageFormat format
|
||||
)
|
||||
{
|
||||
// Figure out a good palette to represent all of these blocks.
|
||||
char mergedBlocks[16*MAX_S3TC_BLOCK_BYTES];
|
||||
GenerateRepresentativePalette( format, pOriginals, nBlocks, lPitch, mergedBlocks );
|
||||
|
||||
// Build a remap table to remap block 2's colors to block 1's colors.
|
||||
if ( format == IMAGE_FORMAT_DXT1 )
|
||||
{
|
||||
// Grab the palette indices that s3tc.lib made for us.
|
||||
const char *pBase = (const char*)mergedBlocks;
|
||||
pBase += 4;
|
||||
|
||||
for ( int iBlock=0; iBlock < nBlocks; iBlock++ )
|
||||
{
|
||||
S3TCBlock_DXT1 *pBlock = ((S3TCBlock_DXT1*)blocks[iBlock]);
|
||||
|
||||
// Remap all of the block's pixels.
|
||||
for ( int x=0; x < 4; x++ )
|
||||
{
|
||||
for ( int y=0; y < 4; y++ )
|
||||
{
|
||||
int iBaseBit = (y*nBlocks*4 + x + iBlock*4) * 2;
|
||||
|
||||
S3PaletteIndex index = {0, 0};
|
||||
index.m_ColorIndex = ReadBitInt( pBase, iBaseBit, 2 );
|
||||
|
||||
S3TC_SetPixelPaletteIndex( format, (char*)pBlock, x, y, index );
|
||||
}
|
||||
}
|
||||
|
||||
// Copy block 1's palette to block 2.
|
||||
pBlock->m_Ref1 = ((S3TCBlock_DXT1*)mergedBlocks)->m_Ref1;
|
||||
pBlock->m_Ref2 = ((S3TCBlock_DXT1*)mergedBlocks)->m_Ref2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( format == IMAGE_FORMAT_DXT5 );
|
||||
|
||||
// Skip past the alpha palette.
|
||||
const char *pAlphaPalette = mergedBlocks;
|
||||
const char *pAlphaBits = mergedBlocks + 2;
|
||||
|
||||
// Skip past the alpha pixel bits and past the color palette.
|
||||
const char *pColorPalette = pAlphaBits + 6*nBlocks;
|
||||
const char *pColorBits = pColorPalette + 4;
|
||||
|
||||
for ( int iBlock=0; iBlock < nBlocks; iBlock++ )
|
||||
{
|
||||
S3TCBlock_DXT5 *pBlock = ((S3TCBlock_DXT5*)blocks[iBlock]);
|
||||
|
||||
// Remap all of the block's pixels.
|
||||
for ( int x=0; x < 4; x++ )
|
||||
{
|
||||
for ( int y=0; y < 4; y++ )
|
||||
{
|
||||
int iBasePixel = (y*nBlocks*4 + x + iBlock*4);
|
||||
|
||||
S3PaletteIndex index;
|
||||
index.m_ColorIndex = ReadBitInt( pColorBits, iBasePixel * 2, 2 );
|
||||
index.m_AlphaIndex = ReadBitInt( pAlphaBits, iBasePixel * 3, 3 );
|
||||
|
||||
S3TC_SetPixelPaletteIndex( format, (char*)pBlock, x, y, index );
|
||||
}
|
||||
}
|
||||
|
||||
// Copy block 1's palette to block 2.
|
||||
pBlock->m_AlphaRef[0] = pAlphaPalette[0];
|
||||
pBlock->m_AlphaRef[1] = pAlphaPalette[1];
|
||||
pBlock->m_Ref1 = *((unsigned short*)pColorPalette);
|
||||
pBlock->m_Ref2 = *((unsigned short*)(pColorPalette + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
S3PaletteIndex S3TC_GetPaletteIndex(
|
||||
unsigned char *pFaceData,
|
||||
ImageFormat format,
|
||||
int imageWidth,
|
||||
int x,
|
||||
int y )
|
||||
{
|
||||
char *pBlock = S3TC_GetBlock( pFaceData, format, imageWidth>>2, x>>2, y>>2 );
|
||||
return S3TC_GetPixelPaletteIndex( format, pBlock, x&3, y&3 );
|
||||
}
|
||||
|
||||
|
||||
void S3TC_SetPaletteIndex(
|
||||
unsigned char *pFaceData,
|
||||
ImageFormat format,
|
||||
int imageWidth,
|
||||
int x,
|
||||
int y,
|
||||
S3PaletteIndex paletteIndex )
|
||||
{
|
||||
char *pBlock = S3TC_GetBlock( pFaceData, format, imageWidth>>2, x>>2, y>>2 );
|
||||
S3TC_SetPixelPaletteIndex( format, pBlock, x&3, y&3, paletteIndex );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
109
vtf/s3tc_decode.h
Normal file
109
vtf/s3tc_decode.h
Normal file
@ -0,0 +1,109 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef S3TC_DECODE_H
|
||||
#define S3TC_DECODE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "bitmap/imageformat.h"
|
||||
enum ImageFormat;
|
||||
|
||||
|
||||
class S3RGBA
|
||||
{
|
||||
public:
|
||||
unsigned char b, g, r, a;
|
||||
};
|
||||
|
||||
class S3PaletteIndex
|
||||
{
|
||||
public:
|
||||
unsigned char m_AlphaIndex;
|
||||
unsigned char m_ColorIndex;
|
||||
};
|
||||
|
||||
|
||||
#define MAX_S3TC_BLOCK_BYTES 16
|
||||
|
||||
|
||||
S3PaletteIndex S3TC_GetPixelPaletteIndex( ImageFormat format, const char *pS3Block, int x, int y );
|
||||
|
||||
void S3TC_SetPixelPaletteIndex( ImageFormat format, char *pS3Block, int x, int y, S3PaletteIndex iPaletteIndex );
|
||||
|
||||
|
||||
|
||||
// Note: width, x, and y are in texels, not S3 blocks.
|
||||
S3PaletteIndex S3TC_GetPaletteIndex(
|
||||
unsigned char *pFaceData,
|
||||
ImageFormat format,
|
||||
int imageWidth,
|
||||
int x,
|
||||
int y );
|
||||
|
||||
|
||||
// Note: width, x, and y are in texels, not S3 blocks.
|
||||
void S3TC_SetPaletteIndex(
|
||||
unsigned char *pFaceData,
|
||||
ImageFormat format,
|
||||
int imageWidth,
|
||||
int x,
|
||||
int y,
|
||||
S3PaletteIndex paletteIndex );
|
||||
|
||||
|
||||
const char* S3TC_GetBlock(
|
||||
const void *pCompressed,
|
||||
ImageFormat format,
|
||||
int nBlocksWide, // How many blocks wide is the image (pixels wide / 4).
|
||||
int xBlock,
|
||||
int yBlock );
|
||||
|
||||
|
||||
char* S3TC_GetBlock(
|
||||
void *pCompressed,
|
||||
ImageFormat format,
|
||||
int nBlocksWide, // How many blocks wide is the image (pixels wide / 4).
|
||||
int xBlock,
|
||||
int yBlock );
|
||||
|
||||
|
||||
// Merge the two palettes and copy the colors
|
||||
void S3TC_MergeBlocks(
|
||||
char **blocks,
|
||||
S3RGBA **pOriginals, // Original RGBA colors in the texture. This allows it to avoid doubly compressing.
|
||||
int nBlocks,
|
||||
int lPitch, // (in BYTES)
|
||||
ImageFormat format
|
||||
);
|
||||
|
||||
|
||||
// Convert an RGB565 color to RGBA8888.
|
||||
inline S3RGBA S3TC_RGBAFrom565( unsigned short color, unsigned char alphaValue=255 )
|
||||
{
|
||||
S3RGBA ret;
|
||||
ret.a = alphaValue;
|
||||
ret.r = (unsigned char)( (color >> 11) << 3 );
|
||||
ret.g = (unsigned char)( ((color >> 5) & 0x3F) << 2 );
|
||||
ret.b = (unsigned char)( (color & 0x1F) << 3 );
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Blend from one color to another..
|
||||
inline S3RGBA S3TC_RGBABlend( const S3RGBA &a, const S3RGBA &b, int aMul, int bMul, int div )
|
||||
{
|
||||
S3RGBA ret;
|
||||
ret.r = (unsigned char)(( (int)a.r * aMul + (int)b.r * bMul ) / div );
|
||||
ret.g = (unsigned char)(( (int)a.g * aMul + (int)b.g * bMul ) / div );
|
||||
ret.b = (unsigned char)(( (int)a.b * aMul + (int)b.b * bMul ) / div );
|
||||
ret.a = (unsigned char)(( (int)a.a * aMul + (int)b.a * bMul ) / div );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif // S3TC_DECODE_H
|
3493
vtf/vtf.cpp
Normal file
3493
vtf/vtf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
vtf/vtf.vpc
Normal file
46
vtf/vtf.vpc
Normal file
@ -0,0 +1,46 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// VTF.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$macro SRCDIR ".."
|
||||
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\dx9sdk\include" [$WINDOWS]
|
||||
$PreprocessorDefinitions "$BASE;fopen=dont_use_fopen" [$WINDOWS]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "vtf"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "convert_x360.cpp"
|
||||
$File "s3tc_decode.cpp" [$WINDOWS]
|
||||
$File "vtf.cpp"
|
||||
$File "vtf_x360.cpp" [$X360]
|
||||
}
|
||||
|
||||
$Folder "Public Header Files"
|
||||
{
|
||||
$File "s3tc_decode.h" [$WINDOWS]
|
||||
$File "$SRCDIR\public\vtf\vtf.h"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "cvtf.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries" [$X360]
|
||||
{
|
||||
$Lib bitmap
|
||||
$Lib mathlib
|
||||
$Lib tier2
|
||||
}
|
||||
}
|
347
vtf/vtf_x360.cpp
Normal file
347
vtf/vtf_x360.cpp
Normal file
@ -0,0 +1,347 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: The 360 VTF file format I/O class to help simplify access to 360 VTF files.
|
||||
// 360 Formatted VTF's are stored ascending 1x1 up to NxN. Disk format and unserialized
|
||||
// formats are expected to be the same.
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#include "bitmap/imageformat.h"
|
||||
#include "cvtf.h"
|
||||
#include "utlbuffer.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/mem.h"
|
||||
#include "tier2/fileutils.h"
|
||||
#include "byteswap.h"
|
||||
#include "filesystem.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "tier1/lzmaDecoder.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Callback for UpdateOrCreate utility function - swaps a vtf file.
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool VTFCreateCallback( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pExtraData )
|
||||
{
|
||||
// Generate the file
|
||||
CUtlBuffer sourceBuf;
|
||||
CUtlBuffer targetBuf;
|
||||
bool bOk = g_pFullFileSystem->ReadFile( pSourceName, pPathID, sourceBuf );
|
||||
if ( bOk )
|
||||
{
|
||||
bOk = ConvertVTFTo360Format( pSourceName, sourceBuf, targetBuf, NULL );
|
||||
if ( bOk )
|
||||
{
|
||||
bOk = g_pFullFileSystem->WriteFile( pTargetName, pPathID, targetBuf );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !bOk )
|
||||
{
|
||||
Warning( "Failed to create %s\n", pTargetName );
|
||||
}
|
||||
return bOk;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls utility function to create .360 version of a vtf file.
|
||||
//-----------------------------------------------------------------------------
|
||||
int CVTFTexture::UpdateOrCreate( const char *pFilename, const char *pPathID, bool bForce )
|
||||
{
|
||||
return ::UpdateOrCreate( pFilename, NULL, 0, pPathID, VTFCreateCallback, bForce, NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Determine size of file, possibly smaller if skipping top mip levels.
|
||||
//-----------------------------------------------------------------------------
|
||||
int CVTFTexture::FileSize( bool bPreloadOnly, int nMipSkipCount ) const
|
||||
{
|
||||
if ( bPreloadOnly )
|
||||
{
|
||||
// caller wants size of preload
|
||||
return m_iPreloadDataSize;
|
||||
}
|
||||
|
||||
const ResourceEntryInfo *pEntryInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
|
||||
if ( !pEntryInfo )
|
||||
{
|
||||
// has to exist
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
int iImageDataOffset = pEntryInfo->resData;
|
||||
|
||||
if ( m_iCompressedSize )
|
||||
{
|
||||
// file is compressed, mip skipping is non-applicable at this stage
|
||||
return iImageDataOffset + m_iCompressedSize;
|
||||
}
|
||||
|
||||
// caller gets file size, possibly truncated due to mip skipping
|
||||
int nFaceSize = ComputeFaceSize( nMipSkipCount );
|
||||
return iImageDataOffset + m_nFrameCount * m_nFaceCount * nFaceSize;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unserialization of image data from buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CVTFTexture::LoadImageData( CUtlBuffer &buf, bool bBufferIsVolatile, int nMipSkipCount )
|
||||
{
|
||||
ResourceEntryInfo *pEntryInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
|
||||
if ( !pEntryInfo )
|
||||
{
|
||||
// has to exist
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
int iImageDataOffset = pEntryInfo->resData;
|
||||
|
||||
// Fix up the mip count + size based on how many mip levels we skip...
|
||||
if ( nMipSkipCount > 0 )
|
||||
{
|
||||
if ( nMipSkipCount >= m_nMipCount )
|
||||
{
|
||||
nMipSkipCount = 0;
|
||||
}
|
||||
ComputeMipLevelDimensions( nMipSkipCount, &m_nWidth, &m_nHeight, &m_nDepth );
|
||||
m_nMipCount -= nMipSkipCount;
|
||||
m_nMipSkipCount += nMipSkipCount;
|
||||
}
|
||||
|
||||
int iImageSize = ComputeFaceSize();
|
||||
iImageSize = m_nFrameCount * m_nFaceCount * iImageSize;
|
||||
|
||||
// seek to start of image data
|
||||
// The mip levels are stored on disk ascending from smallest (1x1) to largest (NxN) to allow for picmip truncated reads
|
||||
buf.SeekGet( CUtlBuffer::SEEK_HEAD, iImageDataOffset );
|
||||
|
||||
CLZMA lzma;
|
||||
if ( m_iCompressedSize )
|
||||
{
|
||||
unsigned char *pCompressedData = (unsigned char *)buf.PeekGet();
|
||||
if ( !lzma.IsCompressed( pCompressedData ) )
|
||||
{
|
||||
// huh? header says it was compressed
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
// have to decode entire image
|
||||
unsigned int originalSize = lzma.GetActualSize( pCompressedData );
|
||||
AllocateImageData( originalSize );
|
||||
unsigned int outputLength = lzma.Uncompress( pCompressedData, m_pImageData );
|
||||
return ( outputLength == originalSize );
|
||||
}
|
||||
|
||||
bool bOK;
|
||||
if ( bBufferIsVolatile )
|
||||
{
|
||||
AllocateImageData( iImageSize );
|
||||
buf.Get( m_pImageData, iImageSize );
|
||||
bOK = buf.IsValid();
|
||||
}
|
||||
else
|
||||
{
|
||||
// safe to alias
|
||||
m_pImageData = (unsigned char *)buf.PeekGet( iImageSize, 0 );
|
||||
bOK = ( m_pImageData != NULL );
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unserialization
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CVTFTexture::ReadHeader( CUtlBuffer &buf, VTFFileHeaderX360_t &header )
|
||||
{
|
||||
memset( &header, 0, sizeof( VTFFileHeaderX360_t ) );
|
||||
buf.GetObjects( &header );
|
||||
if ( !buf.IsValid() )
|
||||
{
|
||||
Warning( "*** Error getting header from a X360 VTF file.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validity check
|
||||
if ( Q_strncmp( header.fileTypeString, "VTFX", 4 ) )
|
||||
{
|
||||
Warning( "*** Tried to load a PC VTF file as a X360 VTF file!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( header.version[0] != VTF_X360_MAJOR_VERSION && header.version[1] != VTF_X360_MINOR_VERSION )
|
||||
{
|
||||
Warning( "*** Encountered X360 VTF file with an invalid version!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ( header.flags & TEXTUREFLAGS_ENVMAP ) && ( header.width != header.height ) )
|
||||
{
|
||||
Warning( "*** Encountered X360 VTF non-square cubemap!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ( header.flags & TEXTUREFLAGS_ENVMAP ) && ( header.depth != 1 ) )
|
||||
{
|
||||
Warning( "*** Encountered X360 VTF volume texture cubemap!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( header.width <= 0 || header.height <= 0 || header.depth <= 0 )
|
||||
{
|
||||
Warning( "*** Encountered X360 VTF invalid texture size!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unserialization. Can optionally alias image components to a non-volatile buffer,
|
||||
// which prevents unecessary copies. Disk format and memory format of the image
|
||||
// components are explicitly the same.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CVTFTexture::UnserializeFromBuffer( CUtlBuffer &buf, bool bBufferIsVolatile, bool bHeaderOnly, bool bPreloadOnly, int nMipSkipCount )
|
||||
{
|
||||
VTFFileHeaderX360_t header;
|
||||
ResourceEntryInfo *pEntryInfo;
|
||||
|
||||
if ( !ReadHeader( buf, header ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// must first release any prior owned memory or reset aliases, otherwise corruption if types intermingled
|
||||
ReleaseImageMemory();
|
||||
ReleaseResources();
|
||||
|
||||
m_nVersion[0] = header.version[0];
|
||||
m_nVersion[1] = header.version[1];
|
||||
|
||||
m_nWidth = header.width;
|
||||
m_nHeight = header.height;
|
||||
m_nDepth = header.depth;
|
||||
m_Format = header.imageFormat;
|
||||
m_nFlags = header.flags;
|
||||
m_nFrameCount = header.numFrames;
|
||||
m_nFaceCount = ( m_nFlags & TEXTUREFLAGS_ENVMAP ) ? CUBEMAP_FACE_COUNT-1 : 1;
|
||||
m_nMipCount = ComputeMipCount();
|
||||
m_nMipSkipCount = header.mipSkipCount;
|
||||
m_vecReflectivity = header.reflectivity;
|
||||
m_flBumpScale = header.bumpScale;
|
||||
m_iPreloadDataSize = header.preloadDataSize;
|
||||
m_iCompressedSize = header.compressedSize;
|
||||
|
||||
m_LowResImageFormat = IMAGE_FORMAT_RGB888;
|
||||
if ( header.lowResImageSample[3] )
|
||||
{
|
||||
// nonzero denotes validity of color value
|
||||
m_nLowResImageWidth = 1;
|
||||
m_nLowResImageHeight = 1;
|
||||
*(unsigned int *)m_LowResImageSample = *(unsigned int *)header.lowResImageSample;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nLowResImageWidth = 0;
|
||||
m_nLowResImageHeight = 0;
|
||||
*(unsigned int *)m_LowResImageSample = 0;
|
||||
}
|
||||
|
||||
// 360 always has the image resource
|
||||
Assert( header.numResources >= 1 );
|
||||
m_arrResourcesInfo.SetCount( header.numResources );
|
||||
m_arrResourcesData.SetCount( header.numResources );
|
||||
|
||||
// Read the dictionary of resources info
|
||||
buf.Get( m_arrResourcesInfo.Base(), m_arrResourcesInfo.Count() * sizeof( ResourceEntryInfo ) );
|
||||
if ( !buf.IsValid() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pEntryInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
|
||||
if ( !pEntryInfo )
|
||||
{
|
||||
// not optional, has to be present
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( bHeaderOnly )
|
||||
{
|
||||
// caller wants header components only
|
||||
// resource data chunks are NOT unserialized!
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !LoadNewResources( buf ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( bPreloadOnly )
|
||||
{
|
||||
// caller wants preload portion only, everything up to the image
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !LoadImageData( buf, bBufferIsVolatile, nMipSkipCount ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Discard image data to free up memory.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CVTFTexture::ReleaseImageMemory()
|
||||
{
|
||||
// valid sizes identify locally owned memory
|
||||
if ( m_nImageAllocSize )
|
||||
{
|
||||
delete [] m_pImageData;
|
||||
m_nImageAllocSize = 0;
|
||||
}
|
||||
|
||||
// block pointers could be owned or aliased, always clear
|
||||
// ensures other caller's don't free an aliased pointer
|
||||
m_pImageData = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Attributes...
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CVTFTexture::IsPreTiled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int CVTFTexture::MappingWidth() const
|
||||
{
|
||||
return m_nWidth << m_nMipSkipCount;
|
||||
}
|
||||
|
||||
int CVTFTexture::MappingHeight() const
|
||||
{
|
||||
return m_nHeight << m_nMipSkipCount;
|
||||
}
|
||||
|
||||
int CVTFTexture::MappingDepth() const
|
||||
{
|
||||
return m_nDepth << m_nMipSkipCount;
|
||||
}
|
||||
|
||||
int CVTFTexture::MipSkipCount() const
|
||||
{
|
||||
return m_nMipSkipCount;
|
||||
}
|
||||
|
||||
unsigned char *CVTFTexture::LowResImageSample()
|
||||
{
|
||||
return &m_LowResImageSample[0];
|
||||
}
|
Reference in New Issue
Block a user