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)
25 #include "SkyTextureManager.hpp"
26 #include "SkyContext.hpp"
30 //#include "fileutils.hpp"
32 bool SkyTextureManager::s_bSlice3DTextures = false;
34 //------------------------------------------------------------------------------
35 // Function : SkyTextureManager::SkyTextureManager
37 //------------------------------------------------------------------------------
39 * @fn SkyTextureManager::SkyTextureManager(bool bSlice3DTextures)
43 SkyTextureManager::SkyTextureManager(bool bSlice3DTextures /* = false */)
45 s_bSlice3DTextures = bSlice3DTextures;
47 // this should be put somewhere more safe -- like done once in the functions that actually
48 // use these extensions.
49 /*GraphicsContext::InstancePtr()->InitializeExtensions("GL_ARB_texture_cube_map "
54 //------------------------------------------------------------------------------
55 // Function : SkyTextureManager::~SkyTextureManager
57 //------------------------------------------------------------------------------
59 * @fn SkyTextureManager::~SkyTextureManager()
62 * @todo <WRITE EXTENDED SkyTextureManager::~SkyTextureManager FUNCTION DOCUMENTATION>
64 SkyTextureManager::~SkyTextureManager()
66 _texturePaths.clear();
67 for ( TextureIterator iter = _textures.begin();
68 iter != _textures.end();
71 DestroyTextureObject(iter->second);
75 for ( TextureList::iterator uncachedIter = _uncachedTextures.begin();
76 uncachedIter != _uncachedTextures.end();
79 DestroyTextureObject(*uncachedIter);
81 _uncachedTextures.clear();
85 //------------------------------------------------------------------------------
86 // Function : SkyTextureManager::AddPath
88 //------------------------------------------------------------------------------
90 * @fn SkyTextureManager::AddPath(const string &path)
91 * @brief Adds a texture path to the list of active search paths.
94 void SkyTextureManager::AddPath(const string &path)
96 _texturePaths.push_back(path);
100 //------------------------------------------------------------------------------
101 // Function : SkyTextureManager::Get2DTexture
103 //------------------------------------------------------------------------------
105 * @fn SkyTextureManager::Get2DTexture(const string& filename, SkyTexture& texture, bool bMipmap)
106 * @brief Returns a 2D texture object from the texture set.
108 * If the texture is already loaded, it is returned. If it is not, the texture is loaded from
109 * file, added to the texture set, and returned.
111 * If the image cannot be loaded, returns an error. Otherwise returns success.
113 SKYRESULT SkyTextureManager::Get2DTexture(const string& filename,
115 bool bMipmap /* = false */)
117 TextureIterator iter = _textures.find(filename);
119 if (iter != _textures.end())
120 { // the texture is already loaded, just return it.
121 texture = iter->second;
124 { // the texture is being requested for the first time, load and return it
125 FAIL_RETURN(Clone2DTexture(filename, texture, bMipmap));
127 _textures.insert(make_pair(filename, texture));
134 //------------------------------------------------------------------------------
135 // Function : SkyTextureManager::Get3DTexture
137 //------------------------------------------------------------------------------
139 * @fn SkyTextureManager::Get3DTexture(const string& filename, SkyTexture& texture, unsigned int iDepth, bool bMipmap, bool bLoadFromSliceFiles)
140 * @brief Returns a 3D texture object from the texture set.
142 * If the texture is already loaded, it is returned. If it is not, the texture is loaded from
143 * file, added to the texture set, and returned. If the image cannot be loaded, returns an error.
144 * Otherwise returns success.
146 * For 3D textures, this simply loads a 2D image file, and duplicates it across each slice. The
147 * parameter iDepth must be set in order to use a 2D texture image for a 3D texture.
149 SKYRESULT SkyTextureManager::Get3DTexture(const string& filename,
152 bool bMipmap /* = false */,
153 bool bLoadFromSliceFiles /* = false */)
155 TextureIterator iter = _textures.find(filename);
157 if (iter != _textures.end())
158 { // the texture is already loaded, just return it.
159 texture = iter->second;
162 { // the texture is being requested for the first time, load and return it
163 FAIL_RETURN(Clone3DTexture(filename, texture, iDepth, bMipmap, bLoadFromSliceFiles));
165 _textures.insert(make_pair(filename, texture));
172 //------------------------------------------------------------------------------
173 // Function : roundPowerOf2
175 //------------------------------------------------------------------------------
176 static int roundPowerOf2(int n)
179 for (m = 1; m < n; m *= 2);
182 if (m - n <= n - m/2)
189 //------------------------------------------------------------------------------
190 // Function : SkyTextureManager::Clone2DTexture
192 //------------------------------------------------------------------------------
194 * @fn SkyTextureManager::Clone2DTexture( const string &filename, SkyTexture& texture, bool bMipmap)
195 * @brief Returns a 2D texture object.
197 * Ignores texture set. This always loads the file, if it exists, and creates and returns the texture.
199 * If the image cannot be loaded, returns an error. Otherwise returns success.
201 SKYRESULT SkyTextureManager::Clone2DTexture(const string &filename,
203 bool bMipmap /* = false */)
206 unsigned char *pImageData = NULL;
218 // first get the image type from its extension.
219 if (filename.find(".tga") != string.npos || filename.find(".TGA") != string.npos)
221 else if (filename.find(".ppm") != string.npos || filename.find(".PPM") != string.npos)
224 FAIL_RETURN_MSG(SKYRESULT_FAIL, "SkyTextureManager error: invalid image format");
226 // first try the filename sent in in case it includes a path.
227 //if (FileUtils::FileExists(filename.c_str()))
229 // printf("Filename is %s\n", filename.c_str() );
234 LoadPPM(filename.c_str(), pImageData, iWidth, iHeight);
238 LoadTGA(filename.c_str(), pImageData, iWidth, iHeight, iChannels);
246 if (!pImageData) // image not found in current directory. Check the paths...
249 for ( StringList::iterator iter = _texturePaths.begin();
250 iter != _texturePaths.end();
253 { // loop over all texture paths, looking for the filename
254 // get just the filename without path.
255 int iPos = filename.find_last_of("/");
256 if (iPos == filename.npos)
257 iPos = filename.find_last_of("/");
259 // tack on the paths from the texture path list.
260 if (iPos != filename.npos)
261 pathFilename = (*iter) + "/" + filename.substr(iPos+1);
263 pathFilename = (*iter) + "/" + filename;
265 //if (FileUtils::FileExists(pathFilename.c_str()))
270 LoadPPM(pathFilename.c_str(), pImageData, iWidth, iHeight);
273 LoadTGA(pathFilename.c_str(), pImageData, iWidth, iHeight, iChannels);
288 sprintf(buffer, "SkyTextureManager::Clone2DTexture(): Could not load image. %s.\n", filename);
289 FAIL_RETURN_MSG(SKYRESULT_OK, buffer);
292 // make sure it is power of 2 resolution.
293 int iNewWidth = roundPowerOf2(iWidth);
294 int iNewHeight = roundPowerOf2(iHeight);
296 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &iMaxsize );
297 if (iNewWidth > iMaxsize)
299 iNewWidth = iMaxsize;
301 if (iNewHeight> iMaxsize)
303 iNewHeight = iMaxsize;
306 GLenum eFormat = (4 == iChannels) ? GL_RGBA : GL_RGB;
308 if (iNewWidth != iWidth || iNewHeight != iHeight)
310 unsigned char *pScaledImageData = new unsigned char[iChannels * iNewWidth * iNewHeight];
311 gluScaleImage(eFormat, iWidth, iHeight, GL_UNSIGNED_BYTE, pImageData,
312 iNewWidth, iNewHeight, GL_UNSIGNED_BYTE, pScaledImageData);
313 SAFE_DELETE_ARRAY(pImageData);
314 pImageData = pScaledImageData;
317 _Create2DTextureObject( texture, iNewWidth, iNewHeight, eFormat, pImageData);
319 SAFE_DELETE_ARRAY(pImageData);
324 //------------------------------------------------------------------------------
325 // Function : SkyTextureManager::Clone3DTexture
327 //------------------------------------------------------------------------------
329 * @fn SkyTextureManager::Clone3DTexture( const string &filename, SkyTexture& texture, unsigned int iDepth, bool bMipmap, bool bLoadFromSliceFiles)
330 * @brief Returns a 3D texture object.
332 * Ignores texture set. This always loads the file, if it exists, and creates and returns the texture.
333 * If the image cannot be loaded, returns an error. Otherwise returns success.
335 SKYRESULT SkyTextureManager::Clone3DTexture(const string &filename,
338 bool bMipmap /* = false */,
339 bool bLoadFromSliceFiles /* = false */)
344 unsigned char *pBits = NULL;
346 if (!bLoadFromSliceFiles)
348 // first try the filename sent in in case it includes a path.
349 if (image.load(filename))
351 image = QGLWidget::convertToGLFormat(image);
357 for ( QStringList::Iterator iter = _texturePaths.begin();
358 iter != _texturePaths.end();
360 { // loop over all texture paths, looking for the filename
361 pathFilename = (*iter) + "\\" + filename;
363 if (image.load(pathFilename))
365 image = QGLWidget::convertToGLFormat(image);
375 qWarning("SkyTextureManager::GetTexture(): Could not load image "
380 // make sure it is power of 2 resolutions.
381 int iWidth = roundPowerOf2(image.width());
382 int iHeight = roundPowerOf2(image.height());
384 if (s_bSlice3DTextures)
385 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &iMaxsize );
387 glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &iMaxsize );
388 if (iWidth > iMaxsize)
392 if (iHeight> iMaxsize)
397 if (iWidth != image.width() || iHeight != image.height())
398 image = image.smoothScale(iWidth, iHeight);
400 // first build an array of repeated 2D textures...
401 QImage inverted(image.mirror());
402 pBits = new unsigned char[image.numBytes() * iDepth];
403 unsigned int iSliceSize = image.numBytes();
404 int bInverted = false;
405 int iInvertedCount = 8;
406 for (unsigned int iSlice = 0; iSlice < iDepth; ++iSlice)
408 memcpy(&(pBits[iSlice * iSliceSize]),
409 (bInverted) ? inverted.bits() : image.bits(),
411 if (--iInvertedCount <= 0)
414 bInverted = !bInverted;
418 else /// Load from a set of files matching the file pattern
420 QFileInfo fi(filename);
423 QString baseFilename = fi.baseName();
424 int truncPos = baseFilename.find(QRegExp("[0-9]"));
426 baseFilename.truncate(truncPos);
428 dir.setFilter(QDir::Files);
429 dir.setNameFilter(baseFilename + "*." + fi.extension());
430 dir.setSorting(QDir::Name);
431 QStringList files = dir.entryList();
434 if (files.count() < iDepth)
437 for ( QStringList::Iterator iter = _texturePaths.begin();
438 iter != _texturePaths.end();
441 dir.setCurrent(*iter);
442 files = dir.entryList();
443 if (files.count() >= iDepth)
452 qWarning("SkyTextureManager::Clone3DTexture: ERROR: could not find %d files matching "
453 "%s", iDepth, filename.latin1());
458 unsigned int iSlice = 0;
459 unsigned int iSliceSize = 0;
460 for ( QStringList::Iterator iter = files.begin();
461 iter != files.end() && iSlice < iDepth;
464 if (image.load(*iter))
466 image = QGLWidget::convertToGLFormat(image);
467 // make sure it is power of 2 resolution.
468 int iWidth = roundPowerOf2(image.width());
469 int iHeight = roundPowerOf2(image.height());
471 if (s_bSlice3DTextures)
472 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &iMaxsize );
474 glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &iMaxsize );
475 if (iWidth > iMaxsize)
479 if (iHeight> iMaxsize)
484 if (iWidth != image.width() || iHeight != image.height())
485 image = image.smoothScale(iWidth, iHeight);
488 pBits = new unsigned char[image.numBytes() * iDepth];
489 iSliceSize = image.numBytes();
491 memcpy(&(pBits[iSlice * iSliceSize]), image.bits(), image.numBytes());
496 qWarning("SkyTextureManager::Clone3DTexture: ERROR: could not find %d files matching "
497 "%s", iDepth, filename);
505 _Create3DTextureObject(texture,
512 return SKYRESULT_FAIL;
520 //------------------------------------------------------------------------------
521 // Function : SkyTextureManager::GetCubeMapTexture
523 //------------------------------------------------------------------------------
525 * @fn SkyTextureManager::GetCubeMapTexture(const string& filename, SkyTexture& texture, bool bMipmap)
526 * @brief Returns a cube map texture object from the texture set
528 * If the texture is already loaded, it is returned. If it is not, the texture is loaded from file,
529 * added to the texture set, and returned. If any of the 6 images cannot be loaded, returns an error.
530 * Otherwise returns success.
532 SKYRESULT SkyTextureManager::GetCubeMapTexture( const string& filename,
536 TextureIterator iter = _textures.find(filename);
538 if (iter != _textures.end())
539 { // the texture is already loaded, just return it.
540 texture = iter->second;
543 { // the texture is being requested for the first time, load and return it
544 if (!CloneCubeMapTexture(filename, texture, bMipmap))
547 _textures.insert(make_pair(filename, texture));
554 //------------------------------------------------------------------------------
555 // Function : SkyTextureManager::CloneCubeMapTexture
557 //------------------------------------------------------------------------------
559 * @fn SkyTextureManager::CloneCubeMapTexture(const string& filename, SkyTexture& texture, bool bMipmap)
560 * @brief Returns a cube map texture object.
562 * Ignores the texture set. This always loads the cube map texture, if all 6 face images exist,
563 * creates the texture, and returns it. If any of the 6 images cannot be loaded, returns an error.
564 * Otherwise returns success.
566 SKYRESULT SkyTextureManager::CloneCubeMapTexture( const string& filename,
573 GLenum faces [] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
574 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
575 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
576 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
577 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
578 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB };
579 char* faceNames[] = {"posx", "negx", "posy", "negy", "posz", "negz" };
581 for ( QStringList::Iterator iter = _texturePaths.begin();
582 iter != _texturePaths.end();
584 { // loop over all texture paths, looking for the filename
585 for (int i = 0; i < 6; i++)
587 char buffer[FILENAME_MAX];
588 sprintf(buffer, filename.ascii(), faceNames[i]);
589 pathFilename = (*iter) + "\\" + buffer;
591 if (images[i].load(pathFilename))
593 images[i] = QGLWidget::convertToGLFormat(images[i]);
600 for (int i = 0; i < 6; i++)
602 if (images[i].isNull())
604 char buffer[FILENAME_MAX];
605 sprintf(buffer, filename.ascii(), faceNames[i]);
606 qWarning("SkyTextureManager::GetTexture(): Could not load image "
612 glGenTextures(1, &(texture.iTextureID));
613 texture.iWidth = images[0].width();
614 texture.iHeight = images[0].height();
616 // create and bind a cubemap texture object
617 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texture.iTextureID);
619 // enable automipmap generation if needed.
620 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_GENERATE_MIPMAP_SGIS, bMipmap);
622 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
624 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
625 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
626 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
627 glTexParameterf(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
629 for (i = 0; i < 6; i++)
631 glTexImage2D(faces[i],
646 //------------------------------------------------------------------------------
647 // Function : SkyTextureManager::_Create2DTextureObject
649 //------------------------------------------------------------------------------
651 * @fn SkyTextureManager::_Create2DTextureObject(SkyTexture &texture, unsigned int iWidth, unsigned int iHeight, unsigned int iFormat, unsigned char *pData)
652 * @brief Creates a 2D texture.
654 * Creates an OpenGL texture object and returns its ID and dimensions in a SkyTexture structure.
656 SKYRESULT SkyTextureManager::_Create2DTextureObject(SkyTexture &texture,
658 unsigned int iHeight,
659 unsigned int iFormat,
660 unsigned char *pData)
663 unsigned int iTextureID;
664 if (!texture.GetID())
666 glGenTextures(1, &iTextureID);
667 texture.SetID(iTextureID);
670 texture.SetWidth(iWidth);
671 texture.SetHeight(iHeight);
673 glBindTexture(GL_TEXTURE_2D, texture.GetID());
677 unsigned int iInternalFormat;
681 iInternalFormat = GL_LUMINANCE;
683 case GL_LUMINANCE_ALPHA:
684 iInternalFormat = GL_LUMINANCE_ALPHA;
687 iInternalFormat = GL_RGBA8;
691 glTexImage2D( GL_TEXTURE_2D,
702 glTexSubImage2D(GL_TEXTURE_2D,
709 // set default filtering.
710 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
711 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
712 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
713 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
719 //------------------------------------------------------------------------------
720 // Function : SkyTextureManager::_Create3DTextureObject
722 //------------------------------------------------------------------------------
724 * @fn SkyTextureManager::_Create3DTextureObject(SkyTexture &texture, unsigned int iWidth, unsigned int iHeight, unsigned int iDepth, unsigned int iFormat, unsigned char *pData)
725 * @brief Creates a 3D texture
727 * Creates an OpenGL 3D texture object (or a set of 2D slices) and returns its ID and dimensions
728 * in a SkyTexture structure.
730 SKYRESULT SkyTextureManager::_Create3DTextureObject(SkyTexture &texture,
732 unsigned int iHeight,
734 unsigned int iFormat,
735 unsigned char *pData)
737 /* bool bNew = false;
738 if (s_bSlice3DTextures) // create one 2D texture per slice!
740 if (!texture.pSliceIDs)
742 texture.pSliceIDs = new unsigned int[iDepth];
743 glGenTextures(iDepth, texture.pSliceIDs);
747 else if (!texture.iTextureID)
749 glGenTextures(1, &(texture.iTextureID));
753 texture.iWidth = iWidth;
754 texture.iHeight = iHeight;
755 texture.iDepth = iDepth;
756 texture.bSliced3D = s_bSlice3DTextures;
758 if (!s_bSlice3DTextures)
760 glBindTexture(GL_TEXTURE_3D, texture.iTextureID);
764 unsigned int iInternalFormat;
768 iInternalFormat = GL_LUMINANCE;
770 case GL_LUMINANCE_ALPHA:
771 iInternalFormat = GL_LUMINANCE_ALPHA;
774 iInternalFormat = GL_RGBA;
778 glTexImage3D( GL_TEXTURE_3D,
781 iWidth, iHeight, iDepth,
789 glTexSubImage3D(GL_TEXTURE_3D,
791 iWidth, iHeight, iDepth,
796 // set default filtering.
797 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
798 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
799 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
800 glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
804 unsigned int iInternalFormat = 0;
805 unsigned int iBytesPerPixel = 0;
809 iInternalFormat = GL_LUMINANCE;
812 case GL_LUMINANCE_ALPHA:
813 iInternalFormat = GL_LUMINANCE_ALPHA;
818 iInternalFormat = GL_RGBA;
823 unsigned int iSliceSize = iWidth * iHeight * iBytesPerPixel;
825 // create iDepth 2D texture slices...
826 for (unsigned int iSlice = 0; iSlice < iDepth; ++iSlice)
828 glBindTexture(GL_TEXTURE_2D, texture.pSliceIDs[iSlice]);
832 glTexImage2D( GL_TEXTURE_2D,
839 (pData + iSlice * iSliceSize));
843 glTexSubImage2D(GL_TEXTURE_2D,
848 (pData + iSlice * iSliceSize));
850 // set default filtering.
851 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
852 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
853 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
854 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
857 GLVU::CheckForGLError("SkyTextureManager::_Create3DTextureObject()");
859 return SKYRESULT_OK;*/
860 return SKYRESULT_FAIL;
864 //------------------------------------------------------------------------------
865 // Function : SkyTextureManager::DestroyTextureObject
867 //------------------------------------------------------------------------------
869 * @fn SkyTextureManager::DestroyTextureObject(SkyTexture &texture)
870 * @brief destroys a SkyTexture object.
872 * Deletes the data as well as the OpenGL texture ID(s).
874 void SkyTextureManager::DestroyTextureObject(SkyTexture &texture)
877 glDeleteTextures(1, &(texture.iTextureID));
878 if (texture.bSliced3D && texture.pSliceIDs)
880 glDeleteTextures(texture.iDepth, texture.pSliceIDs);
881 delete [] texture.pSliceIDs;