1 //------------------------------------------------------------------------------
2 // File : SkyTextureManager.cpp
3 //------------------------------------------------------------------------------
4 // SkyWorks : Copyright 2002 Mark J. Harris and
5 // The University of North Carolina at Chapel Hill
6 //------------------------------------------------------------------------------
7 // Permission to use, copy, modify, distribute and sell this software and its
8 // documentation for any purpose is hereby granted without fee, provided that
9 // the above copyright notice appear in all copies and that both that copyright
10 // notice and this permission notice appear in supporting documentation.
11 // Binaries may be compiled with this software without any royalties or
14 // The author(s) and The University of North Carolina at Chapel Hill make no
15 // representations about the suitability of this software for any purpose.
16 // It is provided "as is" without express or implied warranty.
18 * @file SkyTextureManager.cpp
20 * Implementation of a manager that keeps track of texture resource locations and sharing.
23 #pragma warning( disable : 4786)
26 # include <simgear_config.h>
35 #include "SkyTextureManager.hpp"
36 #include "SkyContext.hpp"
40 //#include "fileutils.hpp"
42 bool SkyTextureManager::s_bSlice3DTextures = false;
44 //------------------------------------------------------------------------------
45 // Function : SkyTextureManager::SkyTextureManager
47 //------------------------------------------------------------------------------
49 * @fn SkyTextureManager::SkyTextureManager(bool bSlice3DTextures)
53 SkyTextureManager::SkyTextureManager(bool bSlice3DTextures /* = false */)
55 s_bSlice3DTextures = bSlice3DTextures;
57 // this should be put somewhere more safe -- like done once in the functions that actually
58 // use these extensions.
59 /*GraphicsContext::InstancePtr()->InitializeExtensions("GL_ARB_texture_cube_map "
64 //------------------------------------------------------------------------------
65 // Function : SkyTextureManager::~SkyTextureManager
67 //------------------------------------------------------------------------------
69 * @fn SkyTextureManager::~SkyTextureManager()
72 * @todo <WRITE EXTENDED SkyTextureManager::~SkyTextureManager FUNCTION DOCUMENTATION>
74 SkyTextureManager::~SkyTextureManager()
76 _texturePaths.clear();
77 for ( TextureIterator iter = _textures.begin();
78 iter != _textures.end();
81 DestroyTextureObject(iter->second);
85 for ( TextureList::iterator uncachedIter = _uncachedTextures.begin();
86 uncachedIter != _uncachedTextures.end();
89 DestroyTextureObject(*uncachedIter);
91 _uncachedTextures.clear();
95 //------------------------------------------------------------------------------
96 // Function : SkyTextureManager::AddPath
98 //------------------------------------------------------------------------------
100 * @fn SkyTextureManager::AddPath(const string &path)
101 * @brief Adds a texture path to the list of active search paths.
104 void SkyTextureManager::AddPath(const string &path)
106 _texturePaths.push_back(path);
110 //------------------------------------------------------------------------------
111 // Function : SkyTextureManager::Get2DTexture
113 //------------------------------------------------------------------------------
115 * @fn SkyTextureManager::Get2DTexture(const string& filename, SkyTexture& texture, bool bMipmap)
116 * @brief Returns a 2D texture object from the texture set.
118 * If the texture is already loaded, it is returned. If it is not, the texture is loaded from
119 * file, added to the texture set, and returned.
121 * If the image cannot be loaded, returns an error. Otherwise returns success.
123 SKYRESULT SkyTextureManager::Get2DTexture(const string& filename,
125 bool bMipmap /* = false */)
127 TextureIterator iter = _textures.find(filename);
129 if (iter != _textures.end())
130 { // the texture is already loaded, just return it.
131 texture = iter->second;
134 { // the texture is being requested for the first time, load and return it
135 FAIL_RETURN(Clone2DTexture(filename, texture, bMipmap));
137 _textures.insert(make_pair(filename, texture));
144 //------------------------------------------------------------------------------
145 // Function : SkyTextureManager::Get3DTexture
147 //------------------------------------------------------------------------------
149 * @fn SkyTextureManager::Get3DTexture(const string& filename, SkyTexture& texture, unsigned int iDepth, bool bMipmap, bool bLoadFromSliceFiles)
150 * @brief Returns a 3D texture object from the texture set.
152 * If the texture is already loaded, it is returned. If it is not, the texture is loaded from
153 * file, added to the texture set, and returned. If the image cannot be loaded, returns an error.
154 * Otherwise returns success.
156 * For 3D textures, this simply loads a 2D image file, and duplicates it across each slice. The
157 * parameter iDepth must be set in order to use a 2D texture image for a 3D texture.
159 SKYRESULT SkyTextureManager::Get3DTexture(const string& filename,
162 bool bMipmap /* = false */,
163 bool bLoadFromSliceFiles /* = false */)
165 TextureIterator iter = _textures.find(filename);
167 if (iter != _textures.end())
168 { // the texture is already loaded, just return it.
169 texture = iter->second;
172 { // the texture is being requested for the first time, load and return it
173 FAIL_RETURN(Clone3DTexture(filename, texture, iDepth, bMipmap, bLoadFromSliceFiles));
175 _textures.insert(make_pair(filename, texture));
182 //------------------------------------------------------------------------------
183 // Function : roundPowerOf2
185 //------------------------------------------------------------------------------
186 static int roundPowerOf2(int n)
189 for (m = 1; m < n; m *= 2);
192 if (m - n <= n - m/2)
199 //------------------------------------------------------------------------------
200 // Function : SkyTextureManager::Clone2DTexture
202 //------------------------------------------------------------------------------
204 * @fn SkyTextureManager::Clone2DTexture( const string &filename, SkyTexture& texture, bool bMipmap)
205 * @brief Returns a 2D texture object.
207 * Ignores texture set. This always loads the file, if it exists, and creates and returns the texture.
209 * If the image cannot be loaded, returns an error. Otherwise returns success.
211 SKYRESULT SkyTextureManager::Clone2DTexture(const string &filename,
213 bool bMipmap /* = false */)
216 unsigned char *pImageData = NULL;
228 // first get the image type from its extension.
229 if (filename.find(".tga") != string.npos || filename.find(".TGA") != string.npos)
231 else if (filename.find(".ppm") != string.npos || filename.find(".PPM") != string.npos)
234 FAIL_RETURN_MSG(SKYRESULT_FAIL, "SkyTextureManager error: invalid image format");
236 // first try the filename sent in in case it includes a path.
237 //if (FileUtils::FileExists(filename.c_str()))
239 // printf("Filename is %s\n", filename.c_str() );
244 LoadPPM(filename.c_str(), pImageData, iWidth, iHeight);
248 LoadTGA(filename.c_str(), pImageData, iWidth, iHeight, iChannels);
256 if (!pImageData) // image not found in current directory. Check the paths...
259 for ( StringList::iterator iter = _texturePaths.begin();
260 iter != _texturePaths.end();
263 { // loop over all texture paths, looking for the filename
264 // get just the filename without path.
265 int iPos = filename.find_last_of("/");
266 if (iPos == filename.npos)
267 iPos = filename.find_last_of("/");
269 // tack on the paths from the texture path list.
270 if (iPos != filename.npos)
271 pathFilename = (*iter) + "/" + filename.substr(iPos+1);
273 pathFilename = (*iter) + "/" + filename;
275 //if (FileUtils::FileExists(pathFilename.c_str()))
280 LoadPPM(pathFilename.c_str(), pImageData, iWidth, iHeight);
283 LoadTGA(pathFilename.c_str(), pImageData, iWidth, iHeight, iChannels);
298 sprintf(buffer, "SkyTextureManager::Clone2DTexture(): Could not load image. %s.\n", filename.c_str());
299 FAIL_RETURN_MSG(SKYRESULT_OK, buffer);
302 // make sure it is power of 2 resolution.
303 int iNewWidth = roundPowerOf2(iWidth);
304 int iNewHeight = roundPowerOf2(iHeight);
306 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &iMaxsize );
307 if (iNewWidth > iMaxsize)
309 iNewWidth = iMaxsize;
311 if (iNewHeight> iMaxsize)
313 iNewHeight = iMaxsize;
316 GLenum eFormat = (4 == iChannels) ? GL_RGBA : GL_RGB;
318 if (iNewWidth != iWidth || iNewHeight != iHeight)
320 unsigned char *pScaledImageData = new unsigned char[iChannels * iNewWidth * iNewHeight];
321 gluScaleImage(eFormat, iWidth, iHeight, GL_UNSIGNED_BYTE, pImageData,
322 iNewWidth, iNewHeight, GL_UNSIGNED_BYTE, pScaledImageData);
323 SAFE_DELETE_ARRAY(pImageData);
324 pImageData = pScaledImageData;
327 _Create2DTextureObject( texture, iNewWidth, iNewHeight, eFormat, pImageData);
329 SAFE_DELETE_ARRAY(pImageData);
334 //------------------------------------------------------------------------------
335 // Function : SkyTextureManager::Clone3DTexture
337 //------------------------------------------------------------------------------
339 * @fn SkyTextureManager::Clone3DTexture( const string &filename, SkyTexture& texture, unsigned int iDepth, bool bMipmap, bool bLoadFromSliceFiles)
340 * @brief Returns a 3D texture object.
342 * Ignores texture set. This always loads the file, if it exists, and creates and returns the texture.
343 * If the image cannot be loaded, returns an error. Otherwise returns success.
345 SKYRESULT SkyTextureManager::Clone3DTexture(const string &filename,
348 bool bMipmap /* = false */,
349 bool bLoadFromSliceFiles /* = false */)
354 unsigned char *pBits = NULL;
356 if (!bLoadFromSliceFiles)
358 // first try the filename sent in in case it includes a path.
359 if (image.load(filename))
361 image = QGLWidget::convertToGLFormat(image);
367 for ( QStringList::Iterator iter = _texturePaths.begin();
368 iter != _texturePaths.end();
370 { // loop over all texture paths, looking for the filename
371 pathFilename = (*iter) + "\\" + filename;
373 if (image.load(pathFilename))
375 image = QGLWidget::convertToGLFormat(image);
385 qWarning("SkyTextureManager::GetTexture(): Could not load image "
390 // make sure it is power of 2 resolutions.
391 int iWidth = roundPowerOf2(image.width());
392 int iHeight = roundPowerOf2(image.height());
394 if (s_bSlice3DTextures)
395 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &iMaxsize );
397 glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &iMaxsize );
398 if (iWidth > iMaxsize)
402 if (iHeight> iMaxsize)
407 if (iWidth != image.width() || iHeight != image.height())
408 image = image.smoothScale(iWidth, iHeight);
410 // first build an array of repeated 2D textures...
411 QImage inverted(image.mirror());
412 pBits = new unsigned char[image.numBytes() * iDepth];
413 unsigned int iSliceSize = image.numBytes();
414 int bInverted = false;
415 int iInvertedCount = 8;
416 for (unsigned int iSlice = 0; iSlice < iDepth; ++iSlice)
418 memcpy(&(pBits[iSlice * iSliceSize]),
419 (bInverted) ? inverted.bits() : image.bits(),
421 if (--iInvertedCount <= 0)
424 bInverted = !bInverted;
428 else /// Load from a set of files matching the file pattern
430 QFileInfo fi(filename);
433 QString baseFilename = fi.baseName();
434 int truncPos = baseFilename.find(QRegExp("[0-9]"));
436 baseFilename.truncate(truncPos);
438 dir.setFilter(QDir::Files);
439 dir.setNameFilter(baseFilename + "*." + fi.extension());
440 dir.setSorting(QDir::Name);
441 QStringList files = dir.entryList();
444 if (files.count() < iDepth)
447 for ( QStringList::Iterator iter = _texturePaths.begin();
448 iter != _texturePaths.end();
451 dir.setCurrent(*iter);
452 files = dir.entryList();
453 if (files.count() >= iDepth)
462 qWarning("SkyTextureManager::Clone3DTexture: ERROR: could not find %d files matching "
463 "%s", iDepth, filename.latin1());
468 unsigned int iSlice = 0;
469 unsigned int iSliceSize = 0;
470 for ( QStringList::Iterator iter = files.begin();
471 iter != files.end() && iSlice < iDepth;
474 if (image.load(*iter))
476 image = QGLWidget::convertToGLFormat(image);
477 // make sure it is power of 2 resolution.
478 int iWidth = roundPowerOf2(image.width());
479 int iHeight = roundPowerOf2(image.height());
481 if (s_bSlice3DTextures)
482 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &iMaxsize );
484 glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &iMaxsize );
485 if (iWidth > iMaxsize)
489 if (iHeight> iMaxsize)
494 if (iWidth != image.width() || iHeight != image.height())
495 image = image.smoothScale(iWidth, iHeight);
498 pBits = new unsigned char[image.numBytes() * iDepth];
499 iSliceSize = image.numBytes();
501 memcpy(&(pBits[iSlice * iSliceSize]), image.bits(), image.numBytes());
506 qWarning("SkyTextureManager::Clone3DTexture: ERROR: could not find %d files matching "
507 "%s", iDepth, filename);
515 _Create3DTextureObject(texture,
522 return SKYRESULT_FAIL;
530 //------------------------------------------------------------------------------
531 // Function : SkyTextureManager::GetCubeMapTexture
533 //------------------------------------------------------------------------------
535 * @fn SkyTextureManager::GetCubeMapTexture(const string& filename, SkyTexture& texture, bool bMipmap)
536 * @brief Returns a cube map texture object from the texture set
538 * If the texture is already loaded, it is returned. If it is not, the texture is loaded from file,
539 * added to the texture set, and returned. If any of the 6 images cannot be loaded, returns an error.
540 * Otherwise returns success.
542 SKYRESULT SkyTextureManager::GetCubeMapTexture( const string& filename,
546 TextureIterator iter = _textures.find(filename);
548 if (iter != _textures.end())
549 { // the texture is already loaded, just return it.
550 texture = iter->second;
553 { // the texture is being requested for the first time, load and return it
554 if (!CloneCubeMapTexture(filename, texture, bMipmap))
557 _textures.insert(make_pair(filename, texture));
564 //------------------------------------------------------------------------------
565 // Function : SkyTextureManager::CloneCubeMapTexture
567 //------------------------------------------------------------------------------
569 * @fn SkyTextureManager::CloneCubeMapTexture(const string& filename, SkyTexture& texture, bool bMipmap)
570 * @brief Returns a cube map texture object.
572 * Ignores the texture set. This always loads the cube map texture, if all 6 face images exist,
573 * creates the texture, and returns it. If any of the 6 images cannot be loaded, returns an error.
574 * Otherwise returns success.
576 SKYRESULT SkyTextureManager::CloneCubeMapTexture( const string& filename,
583 GLenum faces [] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
584 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
585 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
586 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
587 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
588 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB };
589 char* faceNames[] = {"posx", "negx", "posy", "negy", "posz", "negz" };
591 for ( QStringList::Iterator iter = _texturePaths.begin();
592 iter != _texturePaths.end();
594 { // loop over all texture paths, looking for the filename
595 for (int i = 0; i < 6; i++)
597 char buffer[FILENAME_MAX];
598 sprintf(buffer, filename.ascii(), faceNames[i]);
599 pathFilename = (*iter) + "\\" + buffer;
601 if (images[i].load(pathFilename))
603 images[i] = QGLWidget::convertToGLFormat(images[i]);
610 for (int i = 0; i < 6; i++)
612 if (images[i].isNull())
614 char buffer[FILENAME_MAX];
615 sprintf(buffer, filename.ascii(), faceNames[i]);
616 qWarning("SkyTextureManager::GetTexture(): Could not load image "
622 glGenTextures(1, &(texture.iTextureID));
623 texture.iWidth = images[0].width();
624 texture.iHeight = images[0].height();
626 // create and bind a cubemap texture object
627 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texture.iTextureID);
629 // enable automipmap generation if needed.
630 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_GENERATE_MIPMAP_SGIS, bMipmap);
632 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
634 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
635 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
636 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
637 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
639 for (i = 0; i < 6; i++)
641 glTexImage2D(faces[i],
656 //------------------------------------------------------------------------------
657 // Function : SkyTextureManager::_Create2DTextureObject
659 //------------------------------------------------------------------------------
661 * @fn SkyTextureManager::_Create2DTextureObject(SkyTexture &texture, unsigned int iWidth, unsigned int iHeight, unsigned int iFormat, unsigned char *pData)
662 * @brief Creates a 2D texture.
664 * Creates an OpenGL texture object and returns its ID and dimensions in a SkyTexture structure.
666 SKYRESULT SkyTextureManager::_Create2DTextureObject(SkyTexture &texture,
668 unsigned int iHeight,
669 unsigned int iFormat,
670 unsigned char *pData)
673 unsigned int iTextureID;
674 if (!texture.GetID())
676 glGenTextures(1, &iTextureID);
677 texture.SetID(iTextureID);
680 texture.SetWidth(iWidth);
681 texture.SetHeight(iHeight);
683 glBindTexture(GL_TEXTURE_2D, texture.GetID());
687 GLenum iInternalFormat;
691 iInternalFormat = GL_LUMINANCE;
693 case GL_LUMINANCE_ALPHA:
694 iInternalFormat = GL_LUMINANCE_ALPHA;
697 iInternalFormat = GL_RGBA8;
701 glTexImage2D( GL_TEXTURE_2D,
712 glTexSubImage2D(GL_TEXTURE_2D,
719 // set default filtering.
720 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
721 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
722 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
723 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
729 //------------------------------------------------------------------------------
730 // Function : SkyTextureManager::_Create3DTextureObject
732 //------------------------------------------------------------------------------
734 * @fn SkyTextureManager::_Create3DTextureObject(SkyTexture &texture, unsigned int iWidth, unsigned int iHeight, unsigned int iDepth, unsigned int iFormat, unsigned char *pData)
735 * @brief Creates a 3D texture
737 * Creates an OpenGL 3D texture object (or a set of 2D slices) and returns its ID and dimensions
738 * in a SkyTexture structure.
740 SKYRESULT SkyTextureManager::_Create3DTextureObject(SkyTexture &texture,
742 unsigned int iHeight,
744 unsigned int iFormat,
745 unsigned char *pData)
747 /* bool bNew = false;
748 if (s_bSlice3DTextures) // create one 2D texture per slice!
750 if (!texture.pSliceIDs)
752 texture.pSliceIDs = new unsigned int[iDepth];
753 glGenTextures(iDepth, texture.pSliceIDs);
757 else if (!texture.iTextureID)
759 glGenTextures(1, &(texture.iTextureID));
763 texture.iWidth = iWidth;
764 texture.iHeight = iHeight;
765 texture.iDepth = iDepth;
766 texture.bSliced3D = s_bSlice3DTextures;
768 if (!s_bSlice3DTextures)
770 glBindTexture(GL_TEXTURE_3D, texture.iTextureID);
774 unsigned int iInternalFormat;
778 iInternalFormat = GL_LUMINANCE;
780 case GL_LUMINANCE_ALPHA:
781 iInternalFormat = GL_LUMINANCE_ALPHA;
784 iInternalFormat = GL_RGBA;
788 glTexImage3D( GL_TEXTURE_3D,
791 iWidth, iHeight, iDepth,
799 glTexSubImage3D(GL_TEXTURE_3D,
801 iWidth, iHeight, iDepth,
806 // set default filtering.
807 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
808 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
809 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
810 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
814 unsigned int iInternalFormat = 0;
815 unsigned int iBytesPerPixel = 0;
819 iInternalFormat = GL_LUMINANCE;
822 case GL_LUMINANCE_ALPHA:
823 iInternalFormat = GL_LUMINANCE_ALPHA;
828 iInternalFormat = GL_RGBA;
833 unsigned int iSliceSize = iWidth * iHeight * iBytesPerPixel;
835 // create iDepth 2D texture slices...
836 for (unsigned int iSlice = 0; iSlice < iDepth; ++iSlice)
838 glBindTexture(GL_TEXTURE_2D, texture.pSliceIDs[iSlice]);
842 glTexImage2D( GL_TEXTURE_2D,
849 (pData + iSlice * iSliceSize));
853 glTexSubImage2D(GL_TEXTURE_2D,
858 (pData + iSlice * iSliceSize));
860 // set default filtering.
861 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
862 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
863 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
864 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
867 GLVU::CheckForGLError("SkyTextureManager::_Create3DTextureObject()");
869 return SKYRESULT_OK;*/
870 return SKYRESULT_FAIL;
874 //------------------------------------------------------------------------------
875 // Function : SkyTextureManager::DestroyTextureObject
877 //------------------------------------------------------------------------------
879 * @fn SkyTextureManager::DestroyTextureObject(SkyTexture &texture)
880 * @brief destroys a SkyTexture object.
882 * Deletes the data as well as the OpenGL texture ID(s).
884 void SkyTextureManager::DestroyTextureObject(SkyTexture &texture)
887 glDeleteTextures(1, &(texture.iTextureID));
888 if (texture.bSliced3D && texture.pSliceIDs)
890 glDeleteTextures(texture.iDepth, texture.pSliceIDs);
891 delete [] texture.pSliceIDs;