#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 #include 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; } } } }