/******************************************************************************
@File OGLES3/PVRTTextureAPI.cpp
@Title OGLES3/PVRTTextureAPI
@Version
@Copyright Copyright (c) Imagination Technologies Limited.
@Platform ANSI compatible
@Description OGLES3 texture loading.
******************************************************************************/
#include <string.h>
#include <stdlib.h>
#include "PVRTContext.h"
#include "PVRTgles3Ext.h"
#include "PVRTTexture.h"
#include "PVRTTextureAPI.h"
#include "PVRTDecompress.h"
#include "PVRTFixedPoint.h"
#include "PVRTMap.h"
#include "PVRTMatrix.h"
#include "PVRTMisc.h"
#include "PVRTResourceFile.h"
/*****************************************************************************
** Functions
****************************************************************************/
/*!***********************************************************************
@Function: PVRTGetOGLES3TextureFormat
@Input: sTextureHeader
@Modified: glInternalFormat
@Modified: glFormat
@Modified: glType
@Description: Gets the OpenGLES equivalent values of internal format,
format and type for this texture header. This will return
any supported OpenGLES texture values, it is up to the user
to decide if these are valid for their current platform.
*************************************************************************/
static const void PVRTGetOGLES3TextureFormat(const PVRTextureHeaderV3& sTextureHeader, PVRTuint32& glInternalFormat, PVRTuint32& glFormat, PVRTuint32& glType)
{
PVRTuint64 PixelFormat = sTextureHeader.u64PixelFormat;
EPVRTVariableType ChannelType = (EPVRTVariableType)sTextureHeader.u32ChannelType;
EPVRTColourSpace ColourSpace = (EPVRTColourSpace)sTextureHeader.u32ColourSpace;
//Initialisation. Any invalid formats will return 0 always.
glFormat = 0;
glType = 0;
glInternalFormat=0;
//Get the last 32 bits of the pixel format.
PVRTuint64 PixelFormatPartHigh = PixelFormat&PVRTEX_PFHIGHMASK;
//Check for a compressed format (The first 8 bytes will be 0, so the whole thing will be equal to the last 32 bits).
if (PixelFormatPartHigh==0)
{
//Format and type == 0 for compressed textures.
switch (PixelFormat)
{
case ePVRTPF_PVRTCI_2bpp_RGB:
{
if (ColourSpace == ePVRTCSpacesRGB)
{
//glInternalFormat=GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT;
}
else
{
glInternalFormat=GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
}
return;
}
case ePVRTPF_PVRTCI_2bpp_RGBA:
{
if (ColourSpace == ePVRTCSpacesRGB)
{
//glInternalFormat=GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
}
else
{
glInternalFormat=GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
}
return;
}
case ePVRTPF_PVRTCI_4bpp_RGB:
{
if (ColourSpace == ePVRTCSpacesRGB)
{
//glInternalFormat=GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT;
}
else
{
glInternalFormat=GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
}
return;
}
case ePVRTPF_PVRTCI_4bpp_RGBA:
{
if (ColourSpace == ePVRTCSpacesRGB)
{
//glInternalFormat=GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
}
else
{
glInternalFormat=GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
}
return;
}
#ifndef TARGET_OS_IPHONE
case ePVRTPF_PVRTCII_2bpp:
{
if (ColourSpace == ePVRTCSpacesRGB)
{
//glInternalFormat=GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG;
}
else
{
glInternalFormat=GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG;
}
return;
}
case ePVRTPF_PVRTCII_4bpp:
{
if (ColourSpace == ePVRTCSpacesRGB)
{
//glInternalFormat=GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG;
}
else
{
glInternalFormat=GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG;
}
return;
}
case ePVRTPF_ETC1:
{
glInternalFormat=GL_ETC1_RGB8_OES;
return;
}
#endif
case ePVRTPF_ETC2_RGB:
{
if (ColourSpace==ePVRTCSpacesRGB)
glInternalFormat=GL_COMPRESSED_SRGB8_ETC2;
else
glInternalFormat=GL_COMPRESSED_RGB8_ETC2;
return;
}
case ePVRTPF_ETC2_RGBA:
{
if (ColourSpace==ePVRTCSpacesRGB)
glInternalFormat=GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
else
glInternalFormat=GL_COMPRESSED_RGBA8_ETC2_EAC;
return;
}
case ePVRTPF_ETC2_RGB_A1:
{
if (ColourSpace==ePVRTCSpacesRGB)
glInternalFormat=GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2;
else
glInternalFormat=GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
return;
}
case ePVRTPF_EAC_R11:
{
if (ChannelType==ePVRTVarTypeSignedInteger || ChannelType==ePVRTVarTypeSignedIntegerNorm ||
ChannelType==ePVRTVarTypeSignedShort || ChannelType==ePVRTVarTypeSignedShortNorm ||
ChannelType==ePVRTVarTypeSignedByte || ChannelType==ePVRTVarTypeSignedByteNorm ||
ChannelType==ePVRTVarTypeSignedFloat)
{
glInternalFormat=GL_COMPRESSED_SIGNED_R11_EAC;
}
else
{
glInternalFormat=GL_COMPRESSED_R11_EAC;
}
return;
}
case ePVRTPF_EAC_RG11:
{
if (ChannelType==ePVRTVarTypeSignedInteger || ChannelType==ePVRTVarTypeSignedIntegerNorm ||
ChannelType==ePVRTVarTypeSignedShort || ChannelType==ePVRTVarTypeSignedShortNorm ||
ChannelType==ePVRTVarTypeSignedByte || ChannelType==ePVRTVarTypeSignedByteNorm ||
ChannelType==ePVRTVarTypeSignedFloat)
{
glInternalFormat=GL_COMPRESSED_SIGNED_RG11_EAC;
}
else
{
glInternalFormat=GL_COMPRESSED_RG11_EAC;
}
return;
}
}
}
else
{
switch (ChannelType)
{
case ePVRTVarTypeUnsignedFloat:
if (PixelFormat==PVRTGENPIXELID3('r','g','b',11,11,10) )
{
glType=GL_UNSIGNED_INT_10F_11F_11F_REV;
glFormat = GL_RGB;
glInternalFormat=GL_R11F_G11F_B10F;
return;
}
break;
case ePVRTVarTypeSignedFloat:
{
switch (PixelFormat)
{
//HALF_FLOAT
case PVRTGENPIXELID4('r','g','b','a',16,16,16,16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_RGBA;
glInternalFormat=GL_RGBA;
return;
}
case PVRTGENPIXELID3('r','g','b',16,16,16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_RGB;
glInternalFormat=GL_RGB16F;
return;
}
case PVRTGENPIXELID2('r','g',16,16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_RG;
glInternalFormat=GL_RG16F;
return;
}
case PVRTGENPIXELID1('r',16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_RED;
glInternalFormat=GL_R16F;
return;
}
case PVRTGENPIXELID2('l','a',16,16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_LUMINANCE_ALPHA;
glInternalFormat=GL_LUMINANCE_ALPHA;
return;
}
case PVRTGENPIXELID1('l',16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_LUMINANCE;
glInternalFormat=GL_LUMINANCE;
return;
}
case PVRTGENPIXELID1('a',16):
{
glType=GL_HALF_FLOAT;
glFormat = GL_ALPHA;
glInternalFormat=GL_ALPHA;
return;
}
//FLOAT
case PVRTGENPIXELID4('r','g','b','a',32,32,32,32):
{
glType=GL_FLOAT;
glFormat = GL_RGBA;
glInternalFormat=GL_RGBA32F;
return;
}
case PVRTGENPIXELID3('r','g','b',32,32,32):
{
glType=GL_FLOAT;
glFormat = GL_RGB;
glInternalFormat=GL_RGB32F;
return;
}
case PVRTGENPIXELID2('r','g',32,32):
{
glType=GL_FLOAT;
glFormat = GL_RG;
glInternalFormat=GL_RG32F;
return;
}
case PVRTGENPIXELID1('r',32):
{
glType=GL_FLOAT;
glFormat = GL_RED;
glInternalFormat=GL_R32F;
return;
}
case PVRTGENPIXELID2('l','a',32,32):
{
glType=GL_FLOAT;
glFormat = GL_LUMINANCE_ALPHA;
glInternalFormat=GL_LUMINANCE_ALPHA;
return;
}
case PVRTGENPIXELID1('l',32):
{
glType=GL_FLOAT;
glFormat = GL_LUMINANCE;
glInternalFormat=GL_LUMINANCE;
return;
}
case PVRTGENPIXELID1('a',32):
{
glType=GL_FLOAT;
glFormat = GL_ALPHA;
glInternalFormat=GL_ALPHA;
return;
}
}
break;
}
case ePVRTVarTypeUnsignedByteNorm:
{
glType = GL_UNSIGNED_BYTE;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',8,8,8,8):
{
glFormat = GL_RGBA;
if (ColourSpace==ePVRTCSpacesRGB)
glInternalFormat=GL_SRGB8_ALPHA8;
else
glInternalFormat=GL_RGBA8;
return;
}
case PVRTGENPIXELID3('r','g','b',8,8,8):
{
glFormat = GL_RGB;
if (ColourSpace==ePVRTCSpacesRGB)
glInternalFormat=GL_SRGB8;
else
glInternalFormat=GL_RGB8;
return;
}
case PVRTGENPIXELID2('r','g',8,8):
{
glFormat = GL_RG;
glInternalFormat=GL_RG8;
return;
}
case PVRTGENPIXELID1('r',8):
{
glFormat = GL_RED;
glInternalFormat=GL_R8;
return;
}
case PVRTGENPIXELID2('l','a',8,8):
{
glFormat = GL_LUMINANCE_ALPHA;
glInternalFormat=GL_LUMINANCE_ALPHA;
return;
}
case PVRTGENPIXELID1('l',8):
{
glFormat = GL_LUMINANCE;
glInternalFormat=GL_LUMINANCE;
return;
}
case PVRTGENPIXELID1('a',8):
{
glFormat = GL_ALPHA;
glInternalFormat=GL_ALPHA;
return;
}
case PVRTGENPIXELID4('b','g','r','a',8,8,8,8):
{
glFormat = GL_BGRA_EXT;
glInternalFormat=GL_BGRA_EXT;
return;
}
}
break;
}
case ePVRTVarTypeSignedByteNorm:
{
glType = GL_BYTE;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',8,8,8,8):
{
glFormat = GL_RGBA;
glInternalFormat=GL_RGBA8_SNORM;
return;
}
case PVRTGENPIXELID3('r','g','b',8,8,8):
{
glFormat = GL_RGB;
glInternalFormat=GL_RGB8_SNORM;
return;
}
case PVRTGENPIXELID2('r','g',8,8):
{
glFormat = GL_RG;
glInternalFormat=GL_RGB8_SNORM;
return;
}
case PVRTGENPIXELID1('r',8):
{
glFormat = GL_RED;
glInternalFormat=GL_R8_SNORM;
return;
}
}
break;
}
case ePVRTVarTypeUnsignedByte:
{
glType = GL_UNSIGNED_BYTE;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',8,8,8,8):
{
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGBA8UI;
return;
}
case PVRTGENPIXELID3('r','g','b',8,8,8):
{
glFormat = GL_RGB_INTEGER;
glInternalFormat=GL_RGB8UI;
return;
}
case PVRTGENPIXELID2('r','g',8,8):
{
glFormat = GL_RG_INTEGER;
glInternalFormat=GL_RG8UI;
return;
}
case PVRTGENPIXELID1('r',8):
{
glFormat = GL_RED_INTEGER;
glInternalFormat=GL_R8UI;
return;
}
}
break;
}
case ePVRTVarTypeSignedByte:
{
glType = GL_BYTE;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',8,8,8,8):
{
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGBA8I;
return;
}
case PVRTGENPIXELID3('r','g','b',8,8,8):
{
glFormat = GL_RGB_INTEGER;
glInternalFormat=GL_RGB8I;
return;
}
case PVRTGENPIXELID2('r','g',8,8):
{
glFormat = GL_RG_INTEGER;
glInternalFormat=GL_RG8I;
return;
}
case PVRTGENPIXELID1('r',8):
{
glFormat = GL_RED_INTEGER;
glInternalFormat=GL_R8I;
return;
}
}
break;
}
case ePVRTVarTypeUnsignedShortNorm:
{
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',4,4,4,4):
{
glType = GL_UNSIGNED_SHORT_4_4_4_4;
glFormat = GL_RGBA;
glInternalFormat=GL_RGBA4;
return;
}
case PVRTGENPIXELID4('r','g','b','a',5,5,5,1):
{
glType = GL_UNSIGNED_SHORT_5_5_5_1;
glFormat = GL_RGBA;
glInternalFormat=GL_RGB5_A1;
return;
}
case PVRTGENPIXELID3('r','g','b',5,6,5):
{
glType = GL_UNSIGNED_SHORT_5_6_5;
glFormat = GL_RGB;
glInternalFormat=GL_RGB565;
return;
}
}
break;
}
case ePVRTVarTypeUnsignedShort:
{
glType = GL_UNSIGNED_SHORT;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',16,16,16,16):
{
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGBA16UI;
return;
}
case PVRTGENPIXELID3('r','g','b',16,16,16):
{
glFormat = GL_RGB_INTEGER;
glInternalFormat=GL_RGB16UI;
return;
}
case PVRTGENPIXELID2('r','g',16,16):
{
glFormat = GL_RG_INTEGER;
glInternalFormat=GL_RG16UI;
return;
}
case PVRTGENPIXELID1('r',16):
{
glFormat = GL_RED_INTEGER;
glInternalFormat=GL_R16UI;
return;
}
}
break;
}
case ePVRTVarTypeSignedShort:
{
glType = GL_SHORT;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',16,16,16,16):
{
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGBA16I;
return;
}
case PVRTGENPIXELID3('r','g','b',16,16,16):
{
glFormat = GL_RGB_INTEGER;
glInternalFormat=GL_RGB16I;
return;
}
case PVRTGENPIXELID2('r','g',16,16):
{
glFormat = GL_RG_INTEGER;
glInternalFormat=GL_RG16I;
return;
}
case PVRTGENPIXELID1('r',16):
{
glFormat = GL_RED_INTEGER;
glInternalFormat=GL_R16I;
return;
}
}
break;
}
case ePVRTVarTypeUnsignedIntegerNorm:
{
if (PixelFormat==PVRTGENPIXELID4('a','b','g','r',2,10,10,10))
{
glType = GL_UNSIGNED_INT_2_10_10_10_REV;
glFormat = GL_RGBA;
glInternalFormat=GL_RGB10_A2;
return;
}
break;
}
case ePVRTVarTypeUnsignedInteger:
{
glType = GL_UNSIGNED_INT;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',32,32,32,32):
{
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGBA32UI;
return;
}
case PVRTGENPIXELID3('r','g','b',32,32,32):
{
glFormat = GL_RGB_INTEGER;
glInternalFormat=GL_RGB32UI;
return;
}
case PVRTGENPIXELID2('r','g',32,32):
{
glFormat = GL_RG_INTEGER;
glInternalFormat=GL_RG32UI;
return;
}
case PVRTGENPIXELID1('r',32):
{
glFormat = GL_RED_INTEGER;
glInternalFormat=GL_R32UI;
return;
}
case PVRTGENPIXELID4('a','b','g','r',2,10,10,10):
{
glType = GL_UNSIGNED_INT_2_10_10_10_REV;
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGB10_A2UI;
return;
}
}
break;
}
case ePVRTVarTypeSignedInteger:
{
glType = GL_INT;
switch (PixelFormat)
{
case PVRTGENPIXELID4('r','g','b','a',32,32,32,32):
{
glFormat = GL_RGBA_INTEGER;
glInternalFormat=GL_RGBA32I;
return;
}
case PVRTGENPIXELID3('r','g','b',32,32,32):
{
glFormat = GL_RGB_INTEGER;
glInternalFormat=GL_RGB32I;
return;
}
case PVRTGENPIXELID2('r','g',32,32):
{
glFormat = GL_RG_INTEGER;
glInternalFormat=GL_RG32I;
return;
}
case PVRTGENPIXELID1('r',32):
{
glFormat = GL_RED_INTEGER;
glInternalFormat=GL_R32I;
return;
}
}
break;
}
default: { }
}
}
//Default (erroneous) return values.
glType = glFormat = glInternalFormat = 0;
}
/*!***************************************************************************
@Function PVRTTextureTile
@Modified pOut The tiled texture in system memory
@Input pIn The source texture
@Input nRepeatCnt Number of times to repeat the source texture
@Description Allocates and fills, in system memory, a texture large enough
to repeat the source texture specified number of times.
*****************************************************************************/
void PVRTTextureTile(
PVRTextureHeaderV3 **pOut,
const PVRTextureHeaderV3 * const pIn,
const int nRepeatCnt)
{
unsigned int nFormat = 0, nType = 0, nBPP, nSize, nElW = 0, nElH = 0, nElD = 0;
PVRTuint8 *pMmSrc, *pMmDst;
unsigned int nLevel;
PVRTextureHeaderV3 *psTexHeaderNew;
_ASSERT(pIn->u32Width);
_ASSERT(pIn->u32Width == pIn->u32Height);
_ASSERT(nRepeatCnt > 1);
PVRTGetOGLES3TextureFormat(*pIn,nFormat,nFormat,nType);
PVRTGetFormatMinDims(pIn->u64PixelFormat,nElW,nElH,nElD);
nBPP = PVRTGetBitsPerPixel(pIn->u64PixelFormat);
nSize = pIn->u32Width * nRepeatCnt;
psTexHeaderNew = PVRTTextureCreate(nSize, nSize, nElW, nElH, nBPP, true);
*psTexHeaderNew = *pIn;
pMmDst = (PVRTuint8*)psTexHeaderNew + sizeof(*psTexHeaderNew);
pMmSrc = (PVRTuint8*)pIn + sizeof(*pIn);
for(nLevel = 0; ((unsigned int)1 << nLevel) < nSize; ++nLevel)
{
int nBlocksDstW = PVRT_MAX((unsigned int)1, (nSize >> nLevel) / nElW);
int nBlocksDstH = PVRT_MAX((unsigned int)1, (nSize >> nLevel) / nElH);
int nBlocksSrcW = PVRT_MAX((unsigned int)1, (pIn->u32Width >> nLevel) / nElW);
int nBlocksSrcH = PVRT_MAX((unsigned int)1, (pIn->u32Height >> nLevel) / nElH);
int nBlocksS = nBPP * nElW * nElH / 8;
PVRTTextureLoadTiled(
pMmDst,
nBlocksDstW,
nBlocksDstH,
pMmSrc,
nBlocksSrcW,
nBlocksSrcH,
nBlocksS,
(pIn->u64PixelFormat>=ePVRTPF_PVRTCI_2bpp_RGB && pIn->u64PixelFormat<=ePVRTPF_PVRTCI_4bpp_RGBA) ? true : false);
pMmDst += nBlocksDstW * nBlocksDstH * nBlocksS;
pMmSrc += nBlocksSrcW * nBlocksSrcH * nBlocksS;
}
psTexHeaderNew->u32Width = nSize;
psTexHeaderNew->u32Height = nSize;
psTexHeaderNew->u32MIPMapCount = nLevel+1;
*pOut = psTexHeaderNew;
}
/*!***************************************************************************
@Function PVRTTextureLoadFromPointer
@Input pointer Pointer to header-texture's structure
@Modified texName the OpenGL ES texture name as returned by glBindTexture
@Modified psTextureHeader Pointer to a PVRTextureHeaderV3 struct. Modified to
contain the header data of the returned texture Ignored if NULL.
@Input bAllowDecompress Allow decompression if PVRTC is not supported in hardware.
@Input nLoadFromLevel Which mip map level to start loading from (0=all)
@Input texPtr If null, texture follows header, else texture is here.
@Modified pMetaData If a valid map is supplied, this will return any and all
MetaDataBlocks stored in the texture, organised by DevFourCC
then identifier. Supplying NULL will ignore all MetaData.
@Return PVR_SUCCESS on success
@Description Allows textures to be stored in C header files and loaded in. Can load parts of a
mip mapped texture (i.e. skipping the highest detailed levels). In OpenGL Cube Map, each
texture's up direction is defined as next (view direction, up direction),
(+x,-y)(-x,-y)(+y,+z)(-y,-z)(+z,-y)(-z,-y).
Sets the texture MIN/MAG filter to GL_LINEAR_MIPMAP_NEAREST/GL_LINEAR
if mip maps are present, GL_LINEAR/GL_LINEAR otherwise.
*****************************************************************************/
EPVRTError PVRTTextureLoadFromPointer( const void* pointer,
GLuint *const texName,
const void *psTextureHeader,
bool bAllowDecompress,
const unsigned int nLoadFromLevel,
const void * const texPtr,
CPVRTMap<unsigned int, CPVRTMap<unsigned int, MetaDataBlock> > *pMetaData)
{
//Compression bools
bool bIsCompressedFormatSupported=false;
bool bIsCompressedFormat=false;
bool bIsLegacyPVR=false;
bool bUsesTexImage3D=false;
//Texture setup
PVRTextureHeaderV3 sTextureHeader;
PVRTuint8* pTextureData=NULL;
//Just in case header and pointer for decompression.
PVRTextureHeaderV3 sTextureHeaderDecomp;
void* pDecompressedData=NULL;
//Check if it's an old header format
if((*(PVRTuint32*)pointer)!=PVRTEX3_IDENT)
{
//Convert the texture header to the new format.
PVRTConvertOldTextureHeaderToV3((PVR_Texture_Header*)pointer,sTextureHeader,pMetaData);
//Get the texture data.
pTextureData = texPtr? (PVRTuint8*)texPtr:(PVRTuint8*)pointer+*(PVRTuint32*)pointer;
bIsLegacyPVR=true;
}
else
{
//Get the header from the main pointer.
sTextureHeader=*(PVRTextureHeaderV3*)pointer;
//Get the texture data.
pTextureData = texPtr? (PVRTuint8*)texPtr:(PVRTuint8*)pointer+PVRTEX3_HEADERSIZE+sTextureHeader.u32MetaDataSize;
if (pMetaData)
{
//Read in all the meta data.
PVRTuint32 metaDataSize=0;
while (metaDataSize<sTextureHeader.u32MetaDataSize)
{
//Read the DevFourCC and advance the pointer offset.
PVRTuint32 DevFourCC=*(PVRTuint32*)((PVRTuint8*)pointer+PVRTEX3_HEADERSIZE+metaDataSize);
metaDataSize+=sizeof(DevFourCC);
//Read the Key and advance the pointer offset.
PVRTuint32 u32Key=*(PVRTuint32*)((PVRTuint8*)pointer+PVRTEX3_HEADERSIZE+metaDataSize);
metaDataSize+=sizeof(u32Key);
//Read the DataSize and advance the pointer offset.
PVRTuint32 u32DataSize = *(PVRTuint32*)((PVRTuint8*)pointer+PVRTEX3_HEADERSIZE+metaDataSize);
metaDataSize+=sizeof(u32DataSize);
//Get the current meta data.
MetaDataBlock& currentMetaData = (*pMetaData)[DevFourCC][u32Key];
//Assign the values to the meta data.
currentMetaData.DevFOURCC=DevFourCC;
currentMetaData.u32Key=u32Key;
currentMetaData.u32DataSize=u32DataSize;
//Check for data, if there is any, read it into the meta data.
if(u32DataSize > 0)
{
//Allocate memory.
currentMetaData.Data = new PVRTuint8[u32DataSize];
//Copy the data.
memcpy(currentMetaData.Data, ((PVRTuint8*)pointer+PVRTEX3_HEADERSIZE+metaDataSize), u32DataSize);
//Advance the meta data size.
metaDataSize+=u32DataSize;
}
}
}
}
//Return the PVRTextureHeader.
if (psTextureHeader)
{
*(PVRTextureHeaderV3*)psTextureHeader=sTextureHeader;
}
//Setup GL Texture format values.
GLenum eTextureFormat = 0;
GLenum eTextureInternalFormat = 0; // often this is the same as textureFormat, but not for BGRA8888 on iOS, for instance
GLenum eTextureType = 0;
//Get the OGLES format values.
PVRTGetOGLES3TextureFormat(sTextureHeader,eTextureInternalFormat,eTextureFormat,eTextureType);
bool bIsPVRTCSupported = CPVRTgles3Ext::IsGLExtensionSupported("GL_IMG_texture_compression_pvrtc");
#ifndef TARGET_OS_IPHONE
bool bIsBGRA8888Supported = CPVRTgles3Ext::IsGLExtensionSupported("GL_IMG_texture_format_BGRA8888");
#else
bool bIsBGRA8888Supported = CPVRTgles3Ext::IsGLExtensionSupported("GL_APPLE_texture_format_BGRA8888");
#endif
#ifndef TARGET_OS_IPHONE
bool bIsETCSupported = CPVRTgles3Ext::IsGLExtensionSupported("GL_OES_compressed_ETC1_RGB8_texture");
#endif
//Check for compressed formats
if (eTextureFormat==0 && eTextureType==0 && eTextureInternalFormat!=0)
{
if (eTextureInternalFormat>=GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG && eTextureInternalFormat<=GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG)
{
//Check for PVRTCI support.
if(bIsPVRTCSupported)
{
bIsCompressedFormatSupported = bIsCompressedFormat = true;
}
else
{
//Try to decompress the texture.
if(bAllowDecompress)
{
//Output a warning.
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer warning: PVRTC not supported. Converting to RGBA8888 instead.\n");
//Modify boolean values.
bIsCompressedFormatSupported = false;
bIsCompressedFormat = true;
//Check if it's 2bpp.
bool bIs2bppPVRTC = (eTextureInternalFormat==GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG || eTextureInternalFormat==GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG);
//Change texture format.
eTextureFormat = eTextureInternalFormat = GL_RGBA;
eTextureType = GL_UNSIGNED_BYTE;
//Create a near-identical texture header for the decompressed header.
sTextureHeaderDecomp = sTextureHeader;
sTextureHeaderDecomp.u32ChannelType=ePVRTVarTypeUnsignedByteNorm;
sTextureHeaderDecomp.u32ColourSpace=ePVRTCSpacelRGB;
sTextureHeaderDecomp.u64PixelFormat=PVRTGENPIXELID4('r','g','b','a',8,8,8,8);
//Allocate enough memory for the decompressed data. OGLES2, so only decompress one surface/face.
pDecompressedData = malloc(PVRTGetTextureDataSize(sTextureHeaderDecomp, PVRTEX_ALLMIPLEVELS, false, true) );
//Check the malloc.
if (!pDecompressedData)
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer error: Unable to allocate memory to decompress texture.\n");
return PVR_FAIL;
}
//Get the dimensions for the current MIP level.
PVRTuint32 uiMIPWidth = sTextureHeaderDecomp.u32Width>>nLoadFromLevel;
PVRTuint32 uiMIPHeight = sTextureHeaderDecomp.u32Height>>nLoadFromLevel;
//Setup temporary variables.
PVRTuint8* pTempDecompData = (PVRTuint8*)pDecompressedData;
PVRTuint8* pTempCompData = (PVRTuint8*)pTextureData;
if (bIsLegacyPVR)
{
//Decompress all the MIP levels.
for (PVRTuint32 uiFace=0;uiFace<sTextureHeader.u32NumFaces;++uiFace)
{
for (PVRTuint32 uiMIPMap=nLoadFromLevel;uiMIPMap<sTextureHeader.u32MIPMapCount;++uiMIPMap)
{
//Get the face offset. Varies per MIP level.
PVRTuint32 decompressedFaceOffset = PVRTGetTextureDataSize(sTextureHeaderDecomp, uiMIPMap, false, false);
PVRTuint32 compressedFaceOffset = PVRTGetTextureDataSize(sTextureHeader, uiMIPMap, false, false);
//Decompress the texture data.
PVRTDecompressPVRTC(pTempCompData,bIs2bppPVRTC?1:0,uiMIPWidth,uiMIPHeight,pTempDecompData);
//Move forward through the pointers.
pTempDecompData+=decompressedFaceOffset;
pTempCompData+=compressedFaceOffset;
//Work out the current MIP dimensions.
uiMIPWidth=PVRT_MAX(1,uiMIPWidth>>1);
uiMIPHeight=PVRT_MAX(1,uiMIPHeight>>1);
}
//Reset the dims.
uiMIPWidth=sTextureHeader.u32Width;
uiMIPHeight=sTextureHeader.u32Height;
}
}
else
{
//Decompress all the MIP levels.
for (PVRTuint32 uiMIPMap=nLoadFromLevel;uiMIPMap<sTextureHeader.u32MIPMapCount;++uiMIPMap)
{
//Get the face offset. Varies per MIP level.
PVRTuint32 decompressedFaceOffset = PVRTGetTextureDataSize(sTextureHeaderDecomp, uiMIPMap, false, false);
PVRTuint32 compressedFaceOffset = PVRTGetTextureDataSize(sTextureHeader, uiMIPMap, false, false);
for (PVRTuint32 uiFace=0;uiFace<sTextureHeader.u32NumFaces;++uiFace)
{
//Decompress the texture data.
PVRTDecompressPVRTC(pTempCompData,bIs2bppPVRTC?1:0,uiMIPWidth,uiMIPHeight,pTempDecompData);
//Move forward through the pointers.
pTempDecompData+=decompressedFaceOffset;
pTempCompData+=compressedFaceOffset;
}
//Work out the current MIP dimensions.
uiMIPWidth=PVRT_MAX(1,uiMIPWidth>>1);
uiMIPHeight=PVRT_MAX(1,uiMIPHeight>>1);
}
}
}
else
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer error: PVRTC not supported.\n");
return PVR_FAIL;
}
}
}
#ifndef TARGET_OS_IPHONE //TODO
else if (eTextureInternalFormat==GL_ETC1_RGB8_OES)
{
if(bIsETCSupported)
{
bIsCompressedFormatSupported = bIsCompressedFormat = true;
}
else
{
if(bAllowDecompress)
{
//Output a warning.
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer warning: ETC not supported. Converting to RGBA8888 instead.\n");
//Modify boolean values.
bIsCompressedFormatSupported = false;
bIsCompressedFormat = true;
//Change texture format.
eTextureFormat = eTextureInternalFormat = GL_RGBA;
eTextureType = GL_UNSIGNED_BYTE;
//Create a near-identical texture header for the decompressed header.
sTextureHeaderDecomp = sTextureHeader;
sTextureHeaderDecomp.u32ChannelType=ePVRTVarTypeUnsignedByteNorm;
sTextureHeaderDecomp.u32ColourSpace=ePVRTCSpacelRGB;
sTextureHeaderDecomp.u64PixelFormat=PVRTGENPIXELID4('r','g','b','a',8,8,8,8);
//Allocate enough memory for the decompressed data. OGLES1, so only decompress one surface/face.
pDecompressedData = malloc(PVRTGetTextureDataSize(sTextureHeaderDecomp, PVRTEX_ALLMIPLEVELS, false, true) );
//Check the malloc.
if (!pDecompressedData)
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer error: Unable to allocate memory to decompress texture.\n");
return PVR_FAIL;
}
//Get the dimensions for the current MIP level.
PVRTuint32 uiMIPWidth = sTextureHeaderDecomp.u32Width>>nLoadFromLevel;
PVRTuint32 uiMIPHeight = sTextureHeaderDecomp.u32Height>>nLoadFromLevel;
//Setup temporary variables.
PVRTuint8* pTempDecompData = (PVRTuint8*)pDecompressedData;
PVRTuint8* pTempCompData = (PVRTuint8*)pTextureData;
if (bIsLegacyPVR)
{
//Decompress all the MIP levels.
for (PVRTuint32 uiFace=0;uiFace<sTextureHeader.u32NumFaces;++uiFace)
{
for (PVRTuint32 uiMIPMap=nLoadFromLevel;uiMIPMap<sTextureHeader.u32MIPMapCount;++uiMIPMap)
{
//Get the face offset. Varies per MIP level.
PVRTuint32 decompressedFaceOffset = PVRTGetTextureDataSize(sTextureHeaderDecomp, uiMIPMap, false, false);
PVRTuint32 compressedFaceOffset = PVRTGetTextureDataSize(sTextureHeader, uiMIPMap, false, false);
//Decompress the texture data.
PVRTDecompressETC(pTempCompData,uiMIPWidth,uiMIPHeight,pTempDecompData,0);
//Move forward through the pointers.
pTempDecompData+=decompressedFaceOffset;
pTempCompData+=compressedFaceOffset;
//Work out the current MIP dimensions.
uiMIPWidth=PVRT_MAX(1,uiMIPWidth>>1);
uiMIPHeight=PVRT_MAX(1,uiMIPHeight>>1);
}
//Reset the dims.
uiMIPWidth=sTextureHeader.u32Width;
uiMIPHeight=sTextureHeader.u32Height;
}
}
else
{
//Decompress all the MIP levels.
for (PVRTuint32 uiMIPMap=nLoadFromLevel;uiMIPMap<sTextureHeader.u32MIPMapCount;++uiMIPMap)
{
//Get the face offset. Varies per MIP level.
PVRTuint32 decompressedFaceOffset = PVRTGetTextureDataSize(sTextureHeaderDecomp, uiMIPMap, false, false);
PVRTuint32 compressedFaceOffset = PVRTGetTextureDataSize(sTextureHeader, uiMIPMap, false, false);
for (PVRTuint32 uiFace=0;uiFace<sTextureHeader.u32NumFaces;++uiFace)
{
//Decompress the texture data.
PVRTDecompressETC(pTempCompData,uiMIPWidth,uiMIPHeight,pTempDecompData,0);
//Move forward through the pointers.
pTempDecompData+=decompressedFaceOffset;
pTempCompData+=compressedFaceOffset;
}
//Work out the current MIP dimensions.
uiMIPWidth=PVRT_MAX(1,uiMIPWidth>>1);
uiMIPHeight=PVRT_MAX(1,uiMIPHeight>>1);
}
}
}
else
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer error: ETC not supported.\n");
return PVR_FAIL;
}
}
}
#endif
}
//Check for BGRA support.
if(eTextureFormat==GL_BGRA_IMG)
{
#ifdef TARGET_OS_IPHONE
eTextureInternalFormat = GL_RGBA;
#endif
if(!bIsBGRA8888Supported)
{
#ifdef TARGET_OS_IPHONE
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: Unable to load GL_BGRA_IMG texture as extension GL_APPLE_texture_format_BGRA8888 is unsupported.\n");
#else
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: Unable to load GL_BGRA_IMG texture as extension GL_IMG_texture_format_BGRA8888 is unsupported.\n");
#endif
return PVR_FAIL;
}
}
//Deal with unsupported texture formats
if (eTextureInternalFormat==0)
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: pixel type not supported.\n");
return PVR_FAIL;
}
//PVR files are never row aligned.
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
//Generate a texture
glGenTextures(1, texName);
//Initialise a texture target.
GLint eTarget=GL_TEXTURE_2D;
//A mix of arrays/cubes/depths are not permitted in OpenGL ES. Check.
if (sTextureHeader.u32NumFaces>1 || sTextureHeader.u32NumSurfaces>1 || sTextureHeader.u32Depth>1)
{
if((sTextureHeader.u32NumFaces>1) && (sTextureHeader.u32NumSurfaces>1))
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: Arrays of cubemaps are not supported by OpenGL ES 3.0\n");
return PVR_FAIL;
}
else if ((sTextureHeader.u32NumFaces>1) && (sTextureHeader.u32Depth>1))
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: 3D Cubemap textures are not supported by OpenGL ES 3.0\n");
return PVR_FAIL;
}
else if ((sTextureHeader.u32NumSurfaces>1) && (sTextureHeader.u32Depth>1))
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: Arrays of 3D textures are not supported by OpenGL ES 3.0\n");
return PVR_FAIL;
}
if(sTextureHeader.u32NumSurfaces>1)
{
eTarget=GL_TEXTURE_2D_ARRAY;
bUsesTexImage3D=true;
}
else if(sTextureHeader.u32NumFaces>1)
{
eTarget=GL_TEXTURE_CUBE_MAP;
}
else if (sTextureHeader.u32Depth>1)
{
eTarget=GL_TEXTURE_3D;
bUsesTexImage3D=true;
}
}
//Bind the texture
glBindTexture(eTarget, *texName);
if(glGetError())
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: glBindTexture() failed.\n");
return PVR_FAIL;
}
//Temporary data to save on if statements within the load loops.
PVRTuint8* pTempData=NULL;
PVRTextureHeaderV3 *psTempHeader=NULL;
if (bIsCompressedFormat && !bIsCompressedFormatSupported)
{
pTempData=(PVRTuint8*)pDecompressedData;
psTempHeader=&sTextureHeaderDecomp;
}
else
{
pTempData=pTextureData;
psTempHeader=&sTextureHeader;
}
//Initialise the current MIP size.
PVRTuint32 uiCurrentMIPSize=0;
//Initialise the width/height
PVRTuint32 u32MIPWidth = sTextureHeader.u32Width;
PVRTuint32 u32MIPHeight = sTextureHeader.u32Height;
PVRTuint32 u32MIPDepth;
if (psTempHeader->u32Depth>1)
{
u32MIPDepth=psTempHeader->u32Depth; //3d texture.
}
else
{
u32MIPDepth=psTempHeader->u32NumSurfaces; //2d arrays.
}
//Loop through all MIP levels.
if (bIsLegacyPVR)
{
//Temporary texture target.
GLint eTextureTarget=eTarget;
//Cubemaps are special.
if (eTextureTarget==GL_TEXTURE_CUBE_MAP)
{
eTextureTarget=GL_TEXTURE_CUBE_MAP_POSITIVE_X;
}
//Loop through all the faces.
for (PVRTuint32 uiFace=0; uiFace<psTempHeader->u32NumFaces; ++uiFace)
{
//Loop through all the mip levels.
for (PVRTuint32 uiMIPLevel=0; uiMIPLevel<psTempHeader->u32MIPMapCount; ++uiMIPLevel)
{
//Get the current MIP size.
uiCurrentMIPSize=PVRTGetTextureDataSize(*psTempHeader,uiMIPLevel,false,false);
if (uiMIPLevel>=nLoadFromLevel)
{
//Upload the texture
if (bUsesTexImage3D)
{
if (bIsCompressedFormat && bIsCompressedFormatSupported)
{
glCompressedTexImage3D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, u32MIPDepth, 0, uiCurrentMIPSize, pTempData);
}
else
{
glTexImage3D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, u32MIPDepth, 0, eTextureFormat, eTextureType, pTempData);
}
}
else
{
if (bIsCompressedFormat && bIsCompressedFormatSupported)
{
glCompressedTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, 0, uiCurrentMIPSize, pTempData);
}
else
{
glTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, 0, eTextureFormat, eTextureType, pTempData);
}
}
}
pTempData+=uiCurrentMIPSize;
//Reduce the MIP Size.
u32MIPWidth=PVRT_MAX(1,u32MIPWidth>>1);
u32MIPHeight=PVRT_MAX(1,u32MIPHeight>>1);
if (psTempHeader->u32Depth>1)
{
u32MIPDepth=PVRT_MAX(1,u32MIPDepth>>1);
}
}
//Increase the texture target.
eTextureTarget++;
//Reset the current MIP dimensions.
u32MIPWidth=psTempHeader->u32Width;
u32MIPHeight=psTempHeader->u32Height;
if (psTempHeader->u32Depth>1)
{
u32MIPDepth=psTempHeader->u32Depth;
}
else
{
u32MIPDepth=psTempHeader->u32NumSurfaces; //2d arrays.
}
//Error check
if(glGetError())
{
FREE(pDecompressedData);
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: glTexImage2D() failed.\n");
return PVR_FAIL;
}
}
}
else
{
for (PVRTuint32 uiMIPLevel=0; uiMIPLevel<psTempHeader->u32MIPMapCount; ++uiMIPLevel)
{
//Get the current MIP size.
uiCurrentMIPSize=PVRTGetTextureDataSize(*psTempHeader,uiMIPLevel,false,false);
GLint eTextureTarget=eTarget;
//Cubemaps are special.
if (eTextureTarget==GL_TEXTURE_CUBE_MAP)
{
eTextureTarget=GL_TEXTURE_CUBE_MAP_POSITIVE_X;
}
for (PVRTuint32 uiFace=0; uiFace<psTempHeader->u32NumFaces; ++uiFace)
{
if (uiMIPLevel>=nLoadFromLevel)
{
//Upload the texture
if (bUsesTexImage3D)
{
//Upload the texture
if (bIsCompressedFormat && bIsCompressedFormatSupported)
{
glCompressedTexImage3D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, u32MIPDepth, 0, uiCurrentMIPSize, pTempData);
}
else
{
glTexImage3D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, u32MIPDepth, 0, eTextureFormat, eTextureType, pTempData);
}
}
else
{
//Upload the texture
if (bIsCompressedFormat && bIsCompressedFormatSupported)
{
glCompressedTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, 0, uiCurrentMIPSize, pTempData);
}
else
{
glTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, 0, eTextureFormat, eTextureType, pTempData);
}
}
}
pTempData+=uiCurrentMIPSize;
eTextureTarget++;
}
//Reduce the MIP Size.
u32MIPWidth=PVRT_MAX(1,u32MIPWidth>>1);
u32MIPHeight=PVRT_MAX(1,u32MIPHeight>>1);
if (psTempHeader->u32Depth>1)
{
u32MIPDepth=PVRT_MAX(1,u32MIPDepth>>1); //Only reduce depth for 3D textures, not texture arrays.
}
//Error check
if(glGetError())
{
FREE(pDecompressedData);
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: glTexImage2D() failed.\n");
return PVR_FAIL;
}
}
}
FREE(pDecompressedData);
//Error check
if(glGetError())
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: glTexImage2D() failed.\n");
return PVR_FAIL;
}
//Set Minification and Magnification filters according to whether MIP maps are present.
if(eTextureType==GL_FLOAT || eTextureType==GL_HALF_FLOAT)
{
if(sTextureHeader.u32MIPMapCount==1)
{ // Texture filter modes are limited to these for float textures
glTexParameteri(eTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(eTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else
{
glTexParameteri(eTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(eTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}
else
{
if(sTextureHeader.u32MIPMapCount==1)
{
glTexParameteri(eTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(eTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(eTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(eTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
if( (sTextureHeader.u32Width & (sTextureHeader.u32Width - 1)) | (sTextureHeader.u32Height & (sTextureHeader.u32Height - 1)))
{
/*
NPOT textures requires the wrap mode to be set explicitly to
GL_CLAMP_TO_EDGE or the texture will be inconsistent.
*/
glTexParameteri(eTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(eTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
else
{
glTexParameteri(eTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(eTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
//Error check
if(glGetError())
{
PVRTErrorOutputDebug("PVRTTextureLoadFromPointer failed: glTexParameter() failed.\n");
return PVR_FAIL;
}
return PVR_SUCCESS;
}
/*!***************************************************************************
@Function PVRTTextureLoadFromPVR
@Input filename Filename of the .PVR file to load the texture from
@Modified texName the OpenGL ES texture name as returned by glBindTexture
@Modified psTextureHeader Pointer to a PVR_Texture_Header struct. Modified to
contain the header data of the returned texture Ignored if NULL.
@Input bAllowDecompress Allow decompression if PVRTC is not supported in hardware.
@Input nLoadFromLevel Which mipmap level to start loading from (0=all)
@Modified pMetaData If a valid map is supplied, this will return any and all
MetaDataBlocks stored in the texture, organised by DevFourCC
then identifier. Supplying NULL will ignore all MetaData.
@Return PVR_SUCCESS on success
@Description Allows textures to be stored in binary PVR files and loaded in. Can load parts of a
mipmaped texture (ie skipping the highest detailed levels).
Sets the texture MIN/MAG filter to GL_LINEAR_MIPMAP_NEAREST/GL_LINEAR
if mipmaps are present, GL_LINEAR/GL_LINEAR otherwise.
*****************************************************************************/
EPVRTError PVRTTextureLoadFromPVR( const char * const filename,
GLuint * const texName,
const void *psTextureHeader,
bool bAllowDecompress,
const unsigned int nLoadFromLevel,
CPVRTMap<unsigned int, CPVRTMap<unsigned int, MetaDataBlock> > *pMetaData)
{
//Attempt to open file.
CPVRTResourceFile TexFile(filename);
//Check file opened successfully.
if (!TexFile.IsOpen())
{
return PVR_FAIL;
}
//Header size.
PVRTuint32 u32HeaderSize=0;
//Boolean whether to byte swap the texture data or not.
bool bSwapDataEndianness=false;
//Texture header to check against.
PVRTextureHeaderV3 sTextureHeader;
//The channel type for endian swapping.
EPVRTVariableType u32CurrentChannelType=ePVRTVarTypeUnsignedByte;
//Check the first word of the file and see if it's equal to the current identifier (or reverse identifier)
if(*(PVRTuint32*)TexFile.DataPtr()!=PVRTEX_CURR_IDENT && *(PVRTuint32*)TexFile.DataPtr()!=PVRTEX_CURR_IDENT_REV)
{
//Swap the header bytes if necessary.
if(!PVRTIsLittleEndian())
{
bSwapDataEndianness=true;
PVRTuint32 u32HeaderSize=PVRTByteSwap32(*(PVRTuint32*)TexFile.DataPtr());
for (PVRTuint32 i=0; i<u32HeaderSize; ++i)
{
PVRTByteSwap( (PVRTuint8*)( ( (PVRTuint32*)TexFile.DataPtr() )+i),sizeof(PVRTuint32) );
}
}
//Get a pointer to the header.
PVR_Texture_Header* sLegacyTextureHeader=(PVR_Texture_Header*)TexFile.DataPtr();
//Set the header size.
u32HeaderSize=sLegacyTextureHeader->dwHeaderSize;
//We only really need the channel type.
PVRTuint64 tempFormat;
EPVRTColourSpace tempColourSpace;
bool tempIsPreMult;
//Map the enum to get the channel type.
PVRTMapLegacyTextureEnumToNewFormat( (PVRTPixelType)( sLegacyTextureHeader->dwpfFlags&0xff),tempFormat,tempColourSpace, u32CurrentChannelType, tempIsPreMult);
}
// If the header file has a reverse identifier, then we need to swap endianness
else if(*(PVRTuint32*)TexFile.DataPtr()==PVRTEX_CURR_IDENT_REV)
{
//Setup the texture header
sTextureHeader=*(PVRTextureHeaderV3*)TexFile.DataPtr();
bSwapDataEndianness=true;
PVRTextureHeaderV3* pTextureHeader=(PVRTextureHeaderV3*)TexFile.DataPtr();
pTextureHeader->u32ChannelType=PVRTByteSwap32(pTextureHeader->u32ChannelType);
pTextureHeader->u32ColourSpace=PVRTByteSwap32(pTextureHeader->u32ColourSpace);
pTextureHeader->u32Depth=PVRTByteSwap32(pTextureHeader->u32Depth);
pTextureHeader->u32Flags=PVRTByteSwap32(pTextureHeader->u32Flags);
pTextureHeader->u32Height=PVRTByteSwap32(pTextureHeader->u32Height);
pTextureHeader->u32MetaDataSize=PVRTByteSwap32(pTextureHeader->u32MetaDataSize);
pTextureHeader->u32MIPMapCount=PVRTByteSwap32(pTextureHeader->u32MIPMapCount);
pTextureHeader->u32NumFaces=PVRTByteSwap32(pTextureHeader->u32NumFaces);
pTextureHeader->u32NumSurfaces=PVRTByteSwap32(pTextureHeader->u32NumSurfaces);
pTextureHeader->u32Version=PVRTByteSwap32(pTextureHeader->u32Version);
pTextureHeader->u32Width=PVRTByteSwap32(pTextureHeader->u32Width);
PVRTByteSwap((PVRTuint8*)&pTextureHeader->u64PixelFormat,sizeof(PVRTuint64));
//Channel type.
u32CurrentChannelType=(EPVRTVariableType)pTextureHeader->u32ChannelType;
//Header size.
u32HeaderSize=PVRTEX3_HEADERSIZE+sTextureHeader.u32MetaDataSize;
}
else
{
//Header size.
u32HeaderSize=PVRTEX3_HEADERSIZE+sTextureHeader.u32MetaDataSize;
}
// Convert the data if needed
if(bSwapDataEndianness)
{
//Get the size of the variables types.
PVRTuint32 ui32VariableSize=0;
switch(u32CurrentChannelType)
{
case ePVRTVarTypeFloat:
case ePVRTVarTypeUnsignedInteger:
case ePVRTVarTypeUnsignedIntegerNorm:
case ePVRTVarTypeSignedInteger:
case ePVRTVarTypeSignedIntegerNorm:
{
ui32VariableSize=4;
break;
}
case ePVRTVarTypeUnsignedShort:
case ePVRTVarTypeUnsignedShortNorm:
case ePVRTVarTypeSignedShort:
case ePVRTVarTypeSignedShortNorm:
{
ui32VariableSize=2;
break;
}
case ePVRTVarTypeUnsignedByte:
case ePVRTVarTypeUnsignedByteNorm:
case ePVRTVarTypeSignedByte:
case ePVRTVarTypeSignedByteNorm:
{
ui32VariableSize=1;
break;
}
default:
return PVR_FAIL;
}
//If the size of the variable type is greater than 1, then we need to byte swap.
if (ui32VariableSize>1)
{
//Get the texture data.
PVRTuint8* pu8OrigData = ( (PVRTuint8*)TexFile.DataPtr() + u32HeaderSize);
//Get the size of the texture data.
PVRTuint32 ui32TextureDataSize = PVRTGetTextureDataSize(sTextureHeader);
//Loop through and byte swap all the data. It's swapped in place so no need to do anything special.
for(PVRTuint32 i = 0; i < ui32TextureDataSize; i+=ui32VariableSize)
{
PVRTByteSwap(pu8OrigData+i,ui32VariableSize);
}
}
}
return PVRTTextureLoadFromPointer(TexFile.DataPtr(), texName, psTextureHeader, bAllowDecompress, nLoadFromLevel,NULL,pMetaData);
}
/*****************************************************************************
End of file (PVRTTextureAPI.cpp)
*****************************************************************************/