445 lines
11 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|