2015-07-26 21:39:49 +00:00
# include "CTexture.h"
CTexture : : CTexture ( ) : CResource ( )
{
mTexelFormat = eRGBA8 ;
mSourceTexelFormat = eRGBA8 ;
mWidth = 0 ;
mHeight = 0 ;
mNumMipMaps = 0 ;
mLinearSize = 0 ;
mBufferExists = false ;
mImgDataBuffer = nullptr ;
mImgDataSize = 0 ;
mGLBufferExists = false ;
}
CTexture : : CTexture ( const CTexture & Source )
{
mTexelFormat = Source . mTexelFormat ;
mSourceTexelFormat = Source . mSourceTexelFormat ;
mWidth = Source . mWidth ;
mHeight = Source . mHeight ;
mLinearSize = Source . mLinearSize ;
mBufferExists = Source . mBufferExists ;
mImgDataSize = Source . mImgDataSize ;
mImgDataBuffer = new u8 [ mImgDataSize ] ;
memcpy ( mImgDataBuffer , Source . mImgDataBuffer , mImgDataSize ) ;
mGLBufferExists = false ;
}
CTexture : : CTexture ( u32 Width , u32 Height )
{
mTexelFormat = eRGBA8 ;
mSourceTexelFormat = eRGBA8 ;
mWidth = ( u16 ) Width ;
mHeight = ( u16 ) Height ;
mNumMipMaps = 1 ;
mLinearSize = Width * Height * 4 ;
mBufferExists = false ;
mImgDataBuffer = nullptr ;
mImgDataSize = 0 ;
mGLBufferExists = false ;
}
CTexture : : ~ CTexture ( )
{
DeleteBuffers ( ) ;
}
EResType CTexture : : Type ( )
{
return eTexture ;
}
bool CTexture : : BufferGL ( )
{
glGenTextures ( 1 , & mTextureID ) ;
glBindTexture ( GL_TEXTURE_2D , mTextureID ) ;
GLenum GLFormat , GLType ;
bool IsCompressed = false ;
switch ( mTexelFormat ) {
case eLuminance :
GLFormat = GL_LUMINANCE ;
GLType = GL_UNSIGNED_BYTE ;
break ;
case eLuminanceAlpha :
GLFormat = GL_LUMINANCE_ALPHA ;
GLType = GL_UNSIGNED_BYTE ;
break ;
case eRGB565 :
GLFormat = GL_RGB ;
GLType = GL_UNSIGNED_SHORT_5_6_5 ;
break ;
case eRGBA4 :
GLFormat = GL_RGBA ;
GLType = GL_UNSIGNED_SHORT_4_4_4_4 ;
break ;
case eRGBA8 :
GLFormat = GL_RGBA ;
GLType = GL_UNSIGNED_BYTE ;
break ;
case eDXT1 :
GLFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ;
IsCompressed = true ;
break ;
}
// The smallest mipmaps are probably not being loaded correctly, because mipmaps in GX textures have a minimum size depending on the format, and these don't.
// Not sure specifically what accomodations should be made to fix that though so whatever.
u32 MipSize = mLinearSize ;
u32 MipOffset = 0 ;
u16 MipW = mWidth , MipH = mHeight ;
for ( u32 iMip = 0 ; iMip < mNumMipMaps ; iMip + + )
{
GLvoid * pData = ( mBufferExists ) ? ( mImgDataBuffer + MipOffset ) : NULL ;
if ( ! IsCompressed )
glTexImage2D ( GL_TEXTURE_2D , iMip , GLFormat , MipW , MipH , 0 , GLFormat , GLType , pData ) ;
else
glCompressedTexImage2D ( GL_TEXTURE_2D , iMip , GLFormat , MipW , MipH , 0 , MipSize , pData ) ;
MipW / = 2 ;
MipH / = 2 ;
MipOffset + = MipSize ;
MipSize / = 4 ;
}
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_BASE_LEVEL , 0 ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , mNumMipMaps - 1 ) ;
// Linear filtering on mipmaps:
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_LINEAR ) ;
// Anisotropic filtering:
float MaxAnisotropy ;
glGetFloatv ( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , & MaxAnisotropy ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , MaxAnisotropy ) ;
mGLBufferExists = true ;
return true ;
}
void CTexture : : Bind ( u32 GLTextureUnit )
{
glActiveTexture ( GL_TEXTURE0 + GLTextureUnit ) ;
if ( ! mGLBufferExists )
BufferGL ( ) ;
glBindTexture ( GL_TEXTURE_2D , mTextureID ) ;
}
void CTexture : : Resize ( u32 Width , u32 Height )
{
if ( ( mWidth ! = Width ) | | ( mHeight ! = Height ) )
{
DeleteBuffers ( ) ;
mWidth = ( u16 ) Width ;
mHeight = ( u16 ) Height ;
mNumMipMaps = 1 ;
}
}
2015-11-25 21:37:34 +00:00
float CTexture : : ReadTexelAlpha ( const CVector2f & TexCoord )
{
// todo: support texel formats other than DXT1
// DXT1 is definitely the most complicated one anyway; try reusing CTextureDecoder functions for other formats
u32 TexelX = ( u32 ) ( ( mWidth - 1 ) * TexCoord . x ) ;
u32 TexelY = ( u32 ) ( ( mHeight - 1 ) * ( 1.f - fmodf ( TexCoord . y , 1.f ) ) ) ;
if ( mTexelFormat = = eDXT1 & & mBufferExists )
{
CMemoryInStream Buffer ( mImgDataBuffer , mImgDataSize , IOUtil : : SystemEndianness ) ;
// 8 bytes per 4x4 16-pixel block, left-to-right top-to-bottom
u32 BlockIdxX = TexelX / 4 ;
u32 BlockIdxY = TexelY / 4 ;
u32 BlocksPerRow = mWidth / 4 ;
u32 BufferPos = ( 8 * BlockIdxX ) + ( 8 * BlockIdxY * BlocksPerRow ) ;
Buffer . Seek ( BufferPos , SEEK_SET ) ;
u16 PaletteA = Buffer . ReadShort ( ) ;
u16 PaletteB = Buffer . ReadShort ( ) ;
if ( PaletteA > PaletteB )
{
// No palette colors have alpha
return 1.f ;
}
// We only care about alpha, which is only present on palette index 3.
// We don't need to calculate/decode the actual palette colors.
u32 BlockCol = ( TexelX & 0xF ) / 4 ;
u32 BlockRow = ( TexelY & 0xF ) / 4 ;
Buffer . Seek ( BlockRow , SEEK_CUR ) ;
u8 Row = Buffer . ReadByte ( ) ;
u8 Shift = ( u8 ) ( 6 - ( BlockCol * 2 ) ) ;
u8 PaletteIndex = ( Row > > Shift ) & 0x3 ;
return ( PaletteIndex = = 3 ? 0.f : 1.f ) ;
}
return 1.f ;
}
2015-07-26 21:39:49 +00:00
bool CTexture : : WriteDDS ( COutputStream & out )
{
if ( ! out . IsValid ( ) ) return false ;
CopyGLBuffer ( ) ;
out . WriteString ( " DDS " , 4 ) ; // "DDS " fourCC
out . WriteLong ( 0x7C ) ; // dwSize
out . WriteLong ( 0x21007 ) ; // dwFlags
out . WriteLong ( mHeight ) ; // dwHeight
out . WriteLong ( mWidth ) ; // dwWidth
out . WriteLong ( mLinearSize ) ; // dwPitchOrLinearSize
out . WriteLong ( 0 ) ; // dwDepth
out . WriteLong ( mNumMipMaps - 1 ) ; // dwMipMapCount
for ( u32 i = 0 ; i < 11 ; i + + )
out . WriteLong ( 0 ) ; // dwReserved1[11]
// DDS_PIXELFORMAT
out . WriteLong ( 32 ) ; // DDS_PIXELFORMAT.dwSize
u32 pfFlags = 0 , pfBpp = 0 , pfRBitMask = 0 , pfGBitMask = 0 , pfBBitMask = 0 , pfABitMask = 0 ;
switch ( mTexelFormat ) {
case eLuminance :
pfFlags = 0x20000 ;
pfBpp = 0x8 ;
pfRBitMask = 0xFF ;
break ;
case eLuminanceAlpha :
pfFlags = 0x20001 ;
pfBpp = 0x10 ;
pfRBitMask = 0x00FF ;
pfABitMask = 0xFF00 ;
break ;
case eRGBA4 :
pfFlags = 0x41 ;
pfBpp = 0x10 ;
pfRBitMask = 0x0F00 ;
pfGBitMask = 0x00F0 ;
pfBBitMask = 0x000F ;
pfABitMask = 0xF000 ;
break ;
case eRGB565 :
pfFlags = 0x40 ;
pfBpp = 0x10 ;
pfRBitMask = 0xF800 ;
pfGBitMask = 0x7E0 ;
pfBBitMask = 0x1F ;
break ;
case eRGBA8 :
pfFlags = 0x41 ;
pfBpp = 0x20 ;
pfRBitMask = 0x00FF0000 ;
pfGBitMask = 0x0000FF00 ;
pfBBitMask = 0x000000FF ;
pfABitMask = 0xFF000000 ;
break ;
case eDXT1 :
pfFlags = 0x4 ;
break ;
}
out . WriteLong ( pfFlags ) ; // DDS_PIXELFORMAT.dwFlags
( mTexelFormat = = eDXT1 ) ? out . WriteString ( " DXT1 " , 4 ) : out . WriteLong ( 0 ) ; // DDS_PIXELFORMAT.dwFourCC
out . WriteLong ( pfBpp ) ; // DDS_PIXELFORMAT.dwRGBBitCount
out . WriteLong ( pfRBitMask ) ; // DDS_PIXELFORMAT.dwRBitMask
out . WriteLong ( pfGBitMask ) ; // DDS_PIXELFORMAT.dwGBitMask
out . WriteLong ( pfBBitMask ) ; // DDS_PIXELFORMAT.dwBBitMask
out . WriteLong ( pfABitMask ) ; // DDS_PIXELFORMAT.dwABitMask
out . WriteLong ( 0x401000 ) ; // dwCaps
out . WriteLong ( 0 ) ; // dwCaps2
out . WriteLong ( 0 ) ; // dwCaps3
out . WriteLong ( 0 ) ; // dwCaps4
out . WriteLong ( 0 ) ; // dwReserved2
out . WriteBytes ( mImgDataBuffer , mImgDataSize ) ; // Image data
return true ;
}
// ************ STATIC ************
u32 CTexture : : FormatBPP ( ETexelFormat Format )
{
switch ( Format )
{
case eGX_I4 : return 4 ;
case eGX_I8 : return 8 ;
case eGX_IA4 : return 8 ;
case eGX_IA8 : return 16 ;
case eGX_C4 : return 4 ;
case eGX_C8 : return 8 ;
case eGX_RGB565 : return 16 ;
case eGX_RGB5A3 : return 16 ;
case eGX_RGBA8 : return 32 ;
case eGX_CMPR : return 4 ;
case eLuminance : return 8 ;
case eLuminanceAlpha : return 16 ;
case eRGBA4 : return 16 ;
case eRGB565 : return 16 ;
case eRGBA8 : return 32 ;
case eDXT1 : return 4 ;
default : return 0 ;
}
}
// ************ PRIVATE ************
void CTexture : : CalcLinearSize ( )
{
float BytesPerPixel = FormatBPP ( mTexelFormat ) / 8.f ;
2015-11-24 10:22:37 +00:00
mLinearSize = ( u32 ) ( mWidth * mHeight * BytesPerPixel ) ;
2015-07-26 21:39:49 +00:00
}
u32 CTexture : : CalcTotalSize ( )
{
float BytesPerPixel = FormatBPP ( mTexelFormat ) / 8.f ;
u32 MipW = mWidth , MipH = mHeight ;
u32 Size = 0 ;
for ( u32 iMip = 0 ; iMip < mNumMipMaps ; iMip + + )
{
2015-11-24 10:22:37 +00:00
Size + = ( u32 ) ( MipW * MipH * BytesPerPixel ) ;
2015-07-26 21:39:49 +00:00
MipW / = 2 ;
MipH / = 2 ;
}
return Size ;
}
void CTexture : : CopyGLBuffer ( )
{
if ( ! mGLBufferExists ) return ;
// Clear existing buffer
if ( mBufferExists )
{
delete [ ] mImgDataBuffer ;
mBufferExists = false ;
mImgDataBuffer = nullptr ;
mImgDataSize = 0 ;
}
// Calculate buffer size
mImgDataSize = CalcTotalSize ( ) ;
mImgDataBuffer = new u8 [ mImgDataSize ] ;
mBufferExists = true ;
// Get texture
u32 MipW = mWidth , MipH = mHeight , MipOffset = 0 ;
float BytesPerPixel = FormatBPP ( mTexelFormat ) / 8.f ;
glBindTexture ( GL_TEXTURE_2D , mTextureID ) ;
for ( u32 iMip = 0 ; iMip < mNumMipMaps ; iMip + + )
{
void * pData = mImgDataBuffer + MipOffset ;
glGetTexImage ( GL_TEXTURE_2D , iMip , GL_RGBA , GL_UNSIGNED_BYTE , pData ) ;
2015-11-24 10:22:37 +00:00
MipOffset + = ( u32 ) ( MipW * MipH * BytesPerPixel ) ;
2015-07-26 21:39:49 +00:00
MipW / = 2 ;
MipH / = 2 ;
}
mTexelFormat = eRGBA8 ;
mLinearSize = mWidth * mHeight * 4 ;
}
void CTexture : : DeleteBuffers ( )
{
if ( mBufferExists )
{
delete [ ] mImgDataBuffer ;
mBufferExists = false ;
mImgDataBuffer = nullptr ;
mImgDataSize = 0 ;
}
if ( mGLBufferExists )
{
glDeleteTextures ( 1 , & mTextureID ) ;
mGLBufferExists = false ;
}
}