bluecore/engine/TextureManager.cpp

445 lines
11 KiB
C++

#include "TextureManager.h"
#include "Utilities/Log.h"
#include "Utilities/format.h"
// library includes
//#include "png.h"
#include "physfs.h"
#include "corona.h"
#include "GL/glew.h"
// system includes
#include <fstream>
#include <limits.h>
using namespace std;
namespace BlueCore
{
//----------------------------------------------------------------------------------
TextureManager::TextureManager()
{
_maxAnisotopy = 128.0;
_maxTextureSize = 1024 * 16;
_lodDrop = 0;
_textureCompression = true;
glewInit();
if (GLEW_ARB_texture_compression == false)
_textureCompression = false;
clog << ">>> TextureManager constructed ..."<< endline;
if (_textureCompression)
clog << " using texture compression"<< endline;
else
clog << " texture compression not supported!"<< endline;
}
//------------------------------------------------------------------------------
TextureManager::~TextureManager()
{
clog << ">>> TextureManager destructed ..."<< endline;
}
Texture::~Texture()
{
glDeleteTextures( 1, &_Id);
clog << ">>> Texture destructed ..."<< endline;
}
/*
//------------------------------------------------------------------------------
void TextureManager::initializeSingleton()
{
XmlConfig config("config.xml");
_lodDrop = 0;
config.getBool("TextureManager", "TextureCompression", _textureCompression,
true);
if (GLEW_ARB_texture_compression == false)
_textureCompression = false;
if (_textureCompression )
clog << " using texture compression"<< endline;
else
clog << " texture compression not supported!"<< endline;
config.getUInt("TextureManager", "MaxTextureSize", _maxTextureSize, 8192);
GLint maxTextureSize;
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &maxTextureSize );
if (_maxTextureSize > (unsigned int)maxTextureSize )
_maxTextureSize = maxTextureSize;
clog << " maximum texture size: "<< _maxTextureSize << endline;
config.getFloat("TextureManager", "MaxAnisotopy", _maxAnisotopy, 128.0);
float maxAnisotopy = 0.0;
if (GLEW_EXT_texture_filter_anisotropic )
glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotopy );
if (_maxAnisotopy > maxAnisotopy )
_maxAnisotopy = maxAnisotopy;
if (_maxAnisotopy > 1.0)
clog << " using anisotopic texture filtering, level: "<< _maxAnisotopy
<< endlog;
else
clog << " not using anisotopic texture filtering."<< endlog;
}
//------------------------------------------------------------------------------
void TextureManager::shutdownSingleton()
{
clog << ">>> shutdown TextureManager..."<< endline;
clearTextures();
}
*/
//------------------------------------------------------------------------------
bool TextureManager::saveToCache(const std::string &filename, int levels)
{
unsigned char *data = 0;
GLint internalformat, compressed_size = 0, width = 0, height = 0, level = 0;
PHYSFS_file *file = PHYSFS_openWrite(filename.c_str() );
if ( !file)
{
return false;
}
level = 0;
while ( !PHYSFS_eof(file) )
{
glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size);
if (compressed_size == 0)
break;
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width);
if (width == 0)
break;
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT,
&height);
if (height == 0)
break;
glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size);
data = new unsigned char[ compressed_size ];
glGetCompressedTexImageARB(GL_TEXTURE_2D, level, data);
PHYSFS_write(file, &internalformat, sizeof (internalformat ), 1);
PHYSFS_write(file, &level, sizeof (level ), 1);
PHYSFS_write(file, &width, sizeof (width ), 1);
PHYSFS_write(file, &height, sizeof (height ), 1);
PHYSFS_write(file, &compressed_size, sizeof (compressed_size ), 1);
PHYSFS_write(file, data, compressed_size, 1);
delete [] data;
level += 1;
if ( (width == 1 ) && (height == 1 ))
break;
if (level >= levels)
break;
}
PHYSFS_close(file);
return true;
}
//------------------------------------------------------------------------------
bool TextureManager::loadFromCache(const std::string &filename,
unsigned int &max_width, unsigned int &max_height)
{
unsigned char *data = 0;
GLuint internalformat, compressed_size, width, height, level;
max_width = 0;
max_height = 0;
PHYSFS_file *file = PHYSFS_openRead(filename.c_str() );
if ( !file)
{
return false;
}
while ( !PHYSFS_eof(file) )
{
PHYSFS_read(file, &internalformat, sizeof (internalformat ), 1);
PHYSFS_read(file, &level, sizeof (level ), 1);
PHYSFS_read(file, &width, sizeof (width ), 1);
PHYSFS_read(file, &height, sizeof (height ), 1);
PHYSFS_read(file, &compressed_size, sizeof (compressed_size ), 1);
if ( !data)
data = new unsigned char[ compressed_size ];
PHYSFS_read(file, data, compressed_size, 1);
glCompressedTexImage2DARB(GL_TEXTURE_2D, level, internalformat, width,
height, 0, compressed_size, data);
max_width = max(width, max_width);
max_height = max(height, max_height);
if (width == 1 && height == 1)
break;
}
delete [] data;
PHYSFS_close(file);
return true;
}
//------------------------------------------------------------------------------
Texture *TextureManager::loadTexture(const std::string &filename,
int mipmapLevel, int compressionLevel)
{
if (filename.empty() )
return 0;
std::string cachename = filename;
cachename += "_";
cachename += format("%d", mipmapLevel);
cachename += "_";
cachename += format("%d", compressionLevel);
cachename += ".cache";
TextureMap::const_iterator result;
result = textures.find(filename);
if (result != textures.end() )
{
return result->second;
}
GLint UnpackAlignment;
// Set unpack alignment to one byte
glGetIntegerv(GL_UNPACK_ALIGNMENT, &UnpackAlignment);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
unsigned int id = 0;
glGenTextures( 1, &id);
glBindTexture(GL_TEXTURE_2D, id);
if (mipmapLevel == 0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (_maxAnisotopy > 0.0)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
( float ) _maxAnisotopy );
}
Texture *t = 0;
unsigned int width = 0, height = 0;
// check for cached texture
if (_textureCompression && (compressionLevel > 0))
{
PHYSFS_sint64 mod_file = PHYSFS_getLastModTime(filename.c_str() );
PHYSFS_sint64 mod_config = PHYSFS_getLastModTime("config.xml");
PHYSFS_sint64 mod_cache = PHYSFS_getLastModTime(cachename.c_str() );
if ( (mod_cache > mod_file) && (mod_cache > mod_config))
{
if (loadFromCache(cachename, width, height) )
{
glPixelStorei(GL_UNPACK_ALIGNMENT, UnpackAlignment);
clog << ">>> Texture '" << filename << "' loaded from cache ("
<< width << ", " << height << ")." << endline;
t = new Texture( id, width, height );
textures[filename] = t;
return t;
}
}
}
//clog << ">>> Texture '" << filename << "': loading from file..." << endline;
corona::Image *image = corona::OpenImage(filename.c_str(),
corona::PF_R8G8B8A8);
if (image)
{
int level = 0;
unsigned int width_scaled = image->getWidth();
unsigned int height_scaled = image->getHeight();
corona::FlipImage(image, corona::CA_X);
// create mipmaps
while ( 1)
{
if ( (width_scaled <= _maxTextureSize ) && (height_scaled
<= _maxTextureSize ))
{
width = max(width_scaled, width);
height = max(height_scaled, height);
if (_textureCompression && (compressionLevel > 0))
{
glHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
GLuint compression = GL_COMPRESSED_RGBA_ARB;
if (GLEW_EXT_texture_compression_s3tc)
{
if (compressionLevel == 1)
compression = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
else if (compressionLevel == 2)
compression = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
else
compression = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
}
glTexImage2D(GL_TEXTURE_2D, level, compression,
width_scaled, height_scaled, 0, GL_RGBA,
GL_UNSIGNED_BYTE, ( void* ) image->getPixels() );
}
else
{
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width_scaled,
height_scaled, 0, GL_RGBA, GL_UNSIGNED_BYTE,
( void* ) image->getPixels() );
}
level += 1;
}
if ( (width_scaled == 1 ) && (height_scaled == 1 ))
break;
if ( (mipmapLevel >= 0 ) && (level >= mipmapLevel ))
break;
// rescale image
int halfwidth = width_scaled > 1 ? width_scaled / 2 : 1;
int halfheight = height_scaled > 1 ? height_scaled / 2 : 1;
int idx1 = width_scaled * 4;
int idx2 = (width_scaled + 1 ) * 4;
unsigned char *dst = (unsigned char *)image->getPixels();
unsigned char *src = (unsigned char *)image->getPixels();
for (int y = 0; y < halfheight; y++)
{
for (int x = 0; x < halfwidth; x++)
{
for (int k = 0; k < 4; k ++)
{
*dst ++ = ( GLubyte ) ( ( ( int ) *src + ( int ) src[4]
+ ( int ) src[idx1] + ( int ) src[idx2] + 2 )
>> 2 );
src ++;
}
src += 4;
}
src += 4 * width_scaled;
}
width_scaled = halfheight;
height_scaled = halfwidth;
}
delete image;
if (_textureCompression && (compressionLevel > 0))
saveToCache(cachename, level);
t = new Texture( id, width, height );
clog << ">>> Texture '" << filename << "': loaded from file" << endline;
}
else
{
glDeleteTextures( 1, &id);
t = new Texture( 0, 0, 0 );
clog << "!!! Texture '" << filename << "' not found!" << endline;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, UnpackAlignment);
textures[filename] = t;
return t;
}
/*
//------------------------------------------------------------------------------
void TextureManager::releaseTexture ( const Texture *texture )
{
Texture *t = (Texture *)texture;
if( t->_RefCount > 1 )
t->_RefCount--;
else if( t->_RefCount == 1 )
{
glDeleteTextures ( 1, &t->_Id );
delete t;
TextureMap::iterator i;
for( i = textures.begin(); i != textures.end(); i++ )
{
if( (*i).second == t )
{
textures.erase( i );
break;
}
}
}
}
*/
//------------------------------------------------------------------------------
void TextureManager::TextureDestroySlot(Referenced *referenced)
{
TextureMap::iterator i;
for (i = textures.begin(); i != textures.end(); i++)
{
Texture *t = (*i).second;
if (t == (Texture*)referenced)
{
textures.erase(i);
break;
}
}
}
}