From Per Fahlberg, "I have added support for PowerVR texture compression. osg::Texture and osg::Image have been modified to support the texture formats and I have added a plugin to load pvr files. All modified files are in the attached zip. "

This commit is contained in:
Robert Osfield 2010-11-04 11:02:37 +00:00
parent 4ced7dffc4
commit d2a9f48054
6 changed files with 318 additions and 0 deletions

View File

@ -64,6 +64,13 @@
#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#endif
#ifndef GL_IMG_texture_compression_pvrtc
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
#endif
#ifndef GL_ARB_INTERNAL_TEXTURE_FORMAT
#define GL_RGBA32F_ARB 0x8814
#define GL_RGB32F_ARB 0x8815
@ -642,6 +649,9 @@ class OSG_EXPORT Texture : public osg::StateAttribute
void setTextureCompressionRGTCSupported(bool flag) { _isTextureCompressionRGTCSupported=flag; }
bool isTextureCompressionRGTCSupported() const { return _isTextureCompressionRGTCSupported; }
void setTextureCompressionPVRTCSupported(bool flag) { _isTextureCompressionPVRTCSupported=flag; }
bool isTextureCompressionPVRTCSupported() const { return _isTextureCompressionPVRTCSupported; }
void setTextureMirroredRepeatSupported(bool flag) { _isTextureMirroredRepeatSupported=flag; }
bool isTextureMirroredRepeatSupported() const { return _isTextureMirroredRepeatSupported; }
@ -741,6 +751,7 @@ class OSG_EXPORT Texture : public osg::StateAttribute
bool _isTextureCompressionARBSupported;
bool _isTextureCompressionS3TCSupported;
bool _isTextureCompressionRGTCSupported;
bool _isTextureCompressionPVRTCSupported;
bool _isTextureMirroredRepeatSupported;
bool _isTextureEdgeClampSupported;
bool _isTextureBorderClampSupported;

View File

@ -307,6 +307,10 @@ unsigned int Image::computeNumComponents(GLenum pixelFormat)
case(GL_COMPRESSED_RED_RGTC1_EXT): return 1;
case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT): return 2;
case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT): return 2;
case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG): return 3;
case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG): return 3;
case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG): return 4;
case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG): return 4;
case(GL_COLOR_INDEX): return 1;
case(GL_STENCIL_INDEX): return 1;
case(GL_DEPTH_COMPONENT): return 1;
@ -417,6 +421,11 @@ unsigned int Image::computePixelSizeInBits(GLenum format,GLenum type)
case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT): return 8;
case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT): return 8;
case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG): return 4;
case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG): return 2;
case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG): return 4;
case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG): return 2;
default: break;
}
@ -551,6 +560,10 @@ bool Image::isCompressed() const
case(GL_COMPRESSED_RED_RGTC1_EXT):
case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT):
case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT):
case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG):
case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG):
case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG):
case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG):
return true;
default:
return false;

View File

@ -135,6 +135,11 @@ void Texture::TextureProfile::computeSize()
case(GL_COMPRESSED_RED_RGTC1_EXT): numBitsPerTexel = 4; break;
case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT): numBitsPerTexel = 8; break;
case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT): numBitsPerTexel = 8; break;
case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG): numBitsPerTexel = 2; break;
case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG): numBitsPerTexel = 2; break;
case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG): numBitsPerTexel = 4; break;
case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG): numBitsPerTexel = 4; break;
}
_size = (unsigned int)(ceil(double(_width * _height * _depth * numBitsPerTexel)/8.0));
@ -1351,6 +1356,10 @@ bool Texture::isCompressedInternalFormat(GLint internalFormat)
case(GL_COMPRESSED_RED_RGTC1_EXT):
case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT):
case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT):
case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG):
case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG):
case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG):
case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG):
return true;
default:
return false;
@ -1367,6 +1376,38 @@ void Texture::getCompressedSize(GLenum internalFormat, GLint width, GLint height
blockSize = 8;
else if (internalFormat == GL_COMPRESSED_RED_GREEN_RGTC2_EXT || internalFormat == GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT)
blockSize = 16;
else if (internalFormat == GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG || internalFormat == GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG)
{
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
GLint widthBlocks = width / 8;
GLint heightBlocks = height / 4;
GLint bpp = 2;
// Clamp to minimum number of blocks
if(widthBlocks < 2)
widthBlocks = 2;
if(heightBlocks < 2)
heightBlocks = 2;
size = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG || internalFormat == GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG)
{
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
GLint widthBlocks = width / 4;
GLint heightBlocks = height / 4;
GLint bpp = 4;
// Clamp to minimum number of blocks
if(widthBlocks < 2)
widthBlocks = 2;
if(heightBlocks < 2)
heightBlocks = 2;
size = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
return;
}
else
{
OSG_WARN<<"Texture::getCompressedSize(...) : cannot compute correct size of compressed format ("<<internalFormat<<") returning 0."<<std::endl;
@ -2218,6 +2259,8 @@ Texture::Extensions::Extensions(unsigned int contextID)
_isTextureCompressionRGTCSupported = isGLExtensionSupported(contextID,"GL_EXT_texture_compression_rgtc");
_isTextureCompressionPVRTCSupported = isGLExtensionSupported(contextID,"GL_IMG_texture_compression_pvrtc");
_isTextureMirroredRepeatSupported = builtInSupport ||
isGLExtensionOrVersionSupported(contextID,"GL_IBM_texture_mirrored_repeat", 1.4f) ||
isGLExtensionOrVersionSupported(contextID,"GL_ARB_texture_mirrored_repeat", 1.4f);

View File

@ -246,6 +246,8 @@ IF(LIBVNCSERVER_FOUND)
ADD_SUBDIRECTORY(vnc)
ENDIF()
ADD_SUBDIRECTORY(pvr)
##########to get all the variables of Cmake
#GET_CMAKE_PROPERTY(MYVARS VARIABLES)
#FOREACH(myvar ${MYVARS})

View File

@ -0,0 +1,3 @@
SET(TARGET_SRC ReaderWriterPVR.cpp )
#### end var setup ###
SETUP_PLUGIN(pvr)

View File

@ -0,0 +1,246 @@
// ReaderWriter for pvr images
#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/GL>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
using namespace osg;
#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
static char gPVRTexIdentifier[5] = "PVR!";
enum
{
kPVRTextureFlagTypePVRTC_2 = 12,
kPVRTextureFlagTypePVRTC_4,
kPVRTextureFlagTypeOGLPVRTC_2 = 24,
kPVRTextureFlagTypeOGLPVRTC_4
};
typedef struct _PVRTexHeader
{
uint32_t headerLength;
uint32_t height;
uint32_t width;
uint32_t numMipmaps;
uint32_t flags;
uint32_t dataLength;
uint32_t bpp;
uint32_t bitmaskRed;
uint32_t bitmaskGreen;
uint32_t bitmaskBlue;
uint32_t bitmaskAlpha;
uint32_t pvrTag;
uint32_t numSurfs;
typedef unsigned char * BytePtr;
bool needsBytesSwapped()
{
union {
int testWord;
char testByte[sizeof(int)];
}endianTest;
endianTest.testWord = 1;
if( endianTest.testByte[0] == 1 )
return false;
else
return true;
}
template <class T>
inline void swapBytes( T &s )
{
if( sizeof( T ) == 1 )
return;
T d = s;
BytePtr sptr = (BytePtr)&s;
BytePtr dptr = &(((BytePtr)&d)[sizeof(T)-1]);
for( unsigned int i = 0; i < sizeof(T); i++ )
*(sptr++) = *(dptr--);
}
void swapBytes()
{
swapBytes(headerLength);
swapBytes(height);
swapBytes(width);
swapBytes(numMipmaps);
swapBytes(flags);
swapBytes(dataLength);
swapBytes(bpp);
swapBytes(bitmaskRed);
swapBytes(bitmaskGreen);
swapBytes(bitmaskBlue);
swapBytes(bitmaskAlpha);
swapBytes(pvrTag);
swapBytes(numSurfs);
}
} PVRTexHeader;
class ReaderWriterPVR : public osgDB::ReaderWriter
{
public:
ReaderWriterPVR()
{
supportsExtension("pvr","PVR image format");
}
virtual const char* className() const { return "PVR Image Reader/Writer"; }
ReadResult readPVRStream(std::istream& fin) const
{
PVRTexHeader header;
fin.read((char*)&header, sizeof(PVRTexHeader));
if(!fin.good()){
osg::notify(osg::WARN) << "Failed to read pvr header." << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
if(header.needsBytesSwapped())
header.swapBytes();
if(gPVRTexIdentifier[0] != static_cast<char>((header.pvrTag >> 0) & 0xff) ||
gPVRTexIdentifier[1] != static_cast<char>((header.pvrTag >> 8) & 0xff) ||
gPVRTexIdentifier[2] != static_cast<char>((header.pvrTag >> 16) & 0xff) ||
gPVRTexIdentifier[3] != static_cast<char>((header.pvrTag >> 24) & 0xff))
{
osg::notify(osg::WARN) << "Failed to verify pvr header: " << ((header.pvrTag >> 0) & 0xff) << ", " << ((header.pvrTag >> 8) & 0xff) << ", " << ((header.pvrTag >> 16) & 0xff) << ", " << ((header.pvrTag >> 24) & 0xff) << std::endl;
return ReadResult::FILE_NOT_HANDLED;
}
uint32_t formatFlags = header.flags & PVR_TEXTURE_FLAG_TYPE_MASK;
GLenum internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
uint32_t width, height;
bool hasAlpha;
if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2 ||
formatFlags == kPVRTextureFlagTypeOGLPVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_2){
if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4)
internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
else if(formatFlags == kPVRTextureFlagTypePVRTC_2 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_2)
internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
width = header.width;
height = header.height;
if(header.bitmaskAlpha)
hasAlpha = true;
else
hasAlpha = false;
osg::Image *image = new osg::Image;
unsigned char *imageData = new unsigned char[header.dataLength];
fin.read((char*)imageData, header.dataLength);
if(!fin.good())
return ReadResult::ERROR_IN_READING_FILE;
image->setImage(header.width, header.height, 1,
internalFormat, internalFormat,
GL_UNSIGNED_BYTE,
imageData,
osg::Image::USE_NEW_DELETE);
uint32_t dataOffset = 0;
uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
uint32_t bpp = 4;
osg::Image::MipmapDataType mipmapdata;
// Calculate the data size for each texture level and respect the minimum number of blocks
while(dataOffset < header.dataLength){
if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4){
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
bpp = 4;
}else{
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
bpp = 2;
}
// Clamp to minimum number of blocks
if(widthBlocks < 2)
widthBlocks = 2;
if(heightBlocks < 2)
heightBlocks = 2;
if(dataOffset > 0)
mipmapdata.push_back(dataOffset);
dataOffset += widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
width = std::max(width >> 1, (uint32_t)1);
height = std::max(height >> 1, (uint32_t)1);
}
if(!mipmapdata.empty())
image->setMipmapLevels(mipmapdata);
return image;
}
osg::notify(osg::WARN) << "Failed to read pvr data." << std::endl;
return ReadResult::FILE_NOT_HANDLED;
}
virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
{
return readImage(fin, options);
}
virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
{
return readImage(file, options);
}
virtual ReadResult readImage(std::istream& fin,const osgDB::ReaderWriter::Options* =NULL) const
{
return readPVRStream(fin);
}
virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if(!acceptsExtension(ext))
return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile(file, options);
if(fileName.empty())
return ReadResult::FILE_NOT_FOUND;
std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
if(!istream) return ReadResult::FILE_NOT_HANDLED;
ReadResult rr = readPVRStream(istream);
if(rr.validImage()) rr.getImage()->setFileName(file);
return rr;
}
};
// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(pvr, ReaderWriterPVR)