//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : TextureMgr.cpp Data : 27.09.15 Versione : 1.6i8 // Contenuto : Implementazione della classe gestione textures. // // // // Modifiche : 27.09.15 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "Scene.h" #include "TextureMgr.h" #include "/EgtDev/Include/EGnStringUtils.h" #include "/EgtDev/Include/EgtStringConverter.h" #include "/EgtDev/Extern/FreeImage/Include/FreeImage.h" using namespace std ; //---------------------------------------------------------------------------- TextureMgr::TextureMgr( void) { m_pScene = nullptr ; m_nMaxLinPix = 16384 ; } //---------------------------------------------------------------------------- TextureMgr::~TextureMgr( void) { Clear() ; } //---------------------------------------------------------------------------- bool TextureMgr::Clear( void) { // verifico il contesto OpenGL if ( m_pScene == nullptr || ! m_pScene->MakeCurrent()) return false ; // scarico le texture da OpenGL for ( auto iIter = m_umTextData.begin() ; iIter != m_umTextData.end() ; ++ iIter) { GLuint nTextId = iIter->second.nTexId ; glDeleteTextures( 1, &nTextId) ; } // pulisco i dati m_umTextData.clear() ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::SetTextureMaxLinPixels( int nMaxLinPix) { m_nMaxLinPix = max( nMaxLinPix, 128) ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::LoadTexture( const string& sName, const string& sFile, double dMMxPix, double dDimX, double dDimY, int nRepeat) { // verifico il contesto OpenGL if ( m_pScene == nullptr || ! m_pScene->MakeCurrent()) return false ; // verifico se immagine già caricata auto iIter = m_umTextData.find( sName) ; if ( iIter != m_umTextData.end()) return ( EqualNoCase( iIter->second.sFile, sFile) && iIter->second.nRepeat == nRepeat) ; // se texture calcolata if ( sFile.empty()) return LoadCalcTexture( sName, dMMxPix, dDimX, dDimY, nRepeat) ; // leggo l'immagine dal file insieme con le dimensioni in pixel originali dell'immagine FIBITMAP* pDib = nullptr ; int nOriWidth, nOriHeight ; if ( ! ReadImage( sFile, pDib, nOriWidth, nOriHeight)) return false ; // se necessario, converto l'immagine a 24 bit per pixel (BGR) int bitsPerPixel = FreeImage_GetBPP( pDib) ; if ( bitsPerPixel != 24) { FIBITMAP* pTmpDib = pDib ; pDib = FreeImage_ConvertTo24Bits( pTmpDib) ; FreeImage_Unload( pTmpDib) ; } // se necessario e possibile, la riduco alle dimensioni caricabili da OpenGL corrente if ( ! TestImageWithOpenGL( pDib)) return false ; // recupero le dimensioni in pixel dell'immagine int nWidth = FreeImage_GetWidth( pDib) ; int nHeight = FreeImage_GetHeight( pDib) ; // determino il flag di ripetizione GLint nFlag ; switch ( nRepeat) { default : // TXR_CLAMP nFlag = (( m_pScene->GetOpenGLver() < 13) ? GL_CLAMP : GL_CLAMP_TO_BORDER) ; break ; case TXR_REPEAT : nFlag = GL_REPEAT ; break ; case TXR_MIRROR : nFlag = (( m_pScene->GetOpenGLver() < 14) ? GL_REPEAT : GL_MIRRORED_REPEAT) ; break ; } // carico l'immagine in OpenGL come texture GLuint nTexName ; glPixelStorei( GL_UNPACK_ALIGNMENT, 4) ; glGenTextures( 1, &nTexName) ; glBindTexture( GL_TEXTURE_2D, nTexName) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, nFlag) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, nFlag) ; float color[] = { 0.9f, 0.9f, 0.9f, 1.0f} ; glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ; glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_BGR, GL_UNSIGNED_BYTE, FreeImage_GetBits( pDib)) ; // libero l'immagine originaria FreeImage_Unload( pDib) ; // verifico che la texture sia stata veramente caricata if ( ! glIsTexture( nTexName)) return false ; // rendo non corrente questa texture glBindTexture( GL_TEXTURE_2D, 0) ; // se dati mm/pixel determino le dimensioni fisiche della texture if ( dMMxPix > EPS_SMALL && ( dDimX < EPS_SMALL || dDimY < EPS_SMALL)) { dDimX = dMMxPix * nOriWidth ; dDimY = dMMxPix * nOriHeight ; } // salvo i dati TextureData textData( sFile, dDimX, dDimY, nWidth, nHeight, nOriWidth, nOriHeight, nTexName, nRepeat) ; m_umTextData.emplace( sName, textData) ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::LoadCalcTexture( const string& sName, double dMMxPix, double dDimX, double dDimY, int nRepeat) { // verifico il contesto OpenGL if ( m_pScene == nullptr || ! m_pScene->MakeCurrent()) return false ; // verifico se immagine già caricata auto iIter = m_umTextData.find( sName) ; if ( iIter != m_umTextData.end()) return ( iIter->second.sFile.empty() && iIter->second.nRepeat == nRepeat) ; // le texture calcolate sono : Chessboard e Lines if ( ! EqualNoCase( sName, "Chessboard") && ! EqualNoCase( sName, "Lines")) return false ; bool bChess = ( EqualNoCase( sName, "Chessboard")) ; // creo l'immagine const int IMG_WIDTH = 64 ; const int IMG_HEIGHT = 64 ; GLubyte Image[IMG_HEIGHT][IMG_WIDTH][4] ; for ( int i = 0 ; i < IMG_HEIGHT ; ++ i) { for ( int j = 0 ; j < IMG_WIDTH ; ++ j) { int c ; if ( bChess) c = (((i & 0x8) == 0) ^ ((j & 0x8) == 0)) * 255 ; else c = ((i & 0x8) == 0) * 255 ; Image[i][j][0] = (GLubyte) c ; Image[i][j][1] = (GLubyte) c ; Image[i][j][2] = (GLubyte) c ; Image[i][j][3] = (GLubyte) 255 ; } } // determino il flag di ripetizione GLint nFlag ; switch ( nRepeat) { default : // TXR_CLAMP nFlag = (( m_pScene->GetOpenGLver() < 13) ? GL_CLAMP : GL_CLAMP_TO_BORDER) ; break ; case TXR_REPEAT : nFlag = GL_REPEAT ; break ; case TXR_MIRROR : nFlag = (( m_pScene->GetOpenGLver() < 14) ? GL_REPEAT : GL_MIRRORED_REPEAT) ; break ; } // carico l'immagine in OpenGL come texture GLuint nTexName ; glPixelStorei( GL_UNPACK_ALIGNMENT, 4) ; glGenTextures( 1, &nTexName) ; glBindTexture( GL_TEXTURE_2D, nTexName) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, nFlag) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, nFlag) ; float color[] = { 0.9f, 0.9f, 0.9f, 1.0f} ; glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) ; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ; glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, IMG_WIDTH, IMG_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, &Image) ; // verifico che la texture sia stata veramente caricata if ( ! glIsTexture( nTexName)) return false ; // rendo non corrente questa texture glBindTexture( GL_TEXTURE_2D, 0) ; // se dati mm/pixel determino le dimensioni fisiche della texture if ( dMMxPix > EPS_SMALL && ( dDimX < EPS_SMALL || dDimY < EPS_SMALL)) { dDimX = dMMxPix * IMG_WIDTH ; dDimY = dMMxPix * IMG_HEIGHT ; } // salvo i dati TextureData textData( "", dDimX, dDimY, IMG_WIDTH, IMG_HEIGHT, 0, 0, nTexName, nRepeat) ; m_umTextData.emplace( sName, textData) ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::UnloadTexture( const string& sName) { // verifico il contesto OpenGL if ( m_pScene == nullptr || ! m_pScene->MakeCurrent()) return false ; // cerco la texture, se non la trovo già ok auto iIter = m_umTextData.find( sName) ; if ( iIter == m_umTextData.end()) return true ; // scarico la texture da OpenGL GLuint nTextId = iIter->second.nTexId ; glDeleteTextures( 1, &nTextId) ; // tolgo la texture dall'elenco m_umTextData.erase( iIter) ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::Exists( const string& sName) const { // ricerca della texture di nome dato auto iIter = m_umTextData.find( sName) ; return ( iIter != m_umTextData.end()) ; } //---------------------------------------------------------------------------- bool TextureMgr::GetPixels( const string& sName, int& nWidth, int& nHeight) const { // ricerca della texture di nome dato auto iIter = m_umTextData.find( sName) ; if ( iIter == m_umTextData.end()) return false ; // restituisco le dimensioni in pixel nWidth = iIter->second.nWidth ; nHeight = iIter->second.nHeight ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::GetImagePixels( const string& sName, int& nWidth, int& nHeight) const { // ricerca della texture di nome dato auto iIter = m_umTextData.find( sName) ; if ( iIter == m_umTextData.end()) return false ; // restituisco le dimensioni in pixel dell'immagine da cui è stata derivata la texture nWidth = iIter->second.nImgWidth ; nHeight = iIter->second.nImgHeight ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::GetDimensions( const string& sName, double& dDimX, double& dDimY) const { // ricerca della texture di nome dato auto iIter = m_umTextData.find( sName) ; if ( iIter == m_umTextData.end()) return false ; // restituisco le dimensioni fisiche dDimX = iIter->second.dDimX ; dDimY = iIter->second.dDimY ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::ChangeDimensions( const string& sName, double dDimX, double dDimY) { // ricerca della texture di nome dato auto iIter = m_umTextData.find( sName) ; if ( iIter == m_umTextData.end()) return false ; // assegno le nuove dimensioni iIter->second.dDimX = dDimX ; iIter->second.dDimY = dDimY ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::GetTextureData( const string& sName, unsigned int& nId, double& dDimX, double& dDimY) const { // ricerca della texture di nome dato auto iIter = m_umTextData.find( sName) ; if ( iIter == m_umTextData.end()) return false ; // restituzione dei parametri nId = iIter->second.nTexId ; dDimX = iIter->second.dDimX ; dDimY = iIter->second.dDimY ; return true ; } //---------------------------------------------------------------------------- bool TextureMgr::ReadImage( const string& sFile, FIBITMAP*& pDib, int& nFilePixX, int& nFilePixY) { // deduco il formato dell'immagine dalla sua segnatura o dalla estensione del file FREE_IMAGE_FORMAT nFif = FreeImage_GetFileTypeU( stringtoW( sFile), 0) ; if ( nFif == FIF_UNKNOWN) nFif = FreeImage_GetFIFFromFilenameU( stringtoW( sFile)) ; // se non determinata, esco con errore if ( nFif == FIF_UNKNOWN) return false ; // se formato Jpeg, impostazione flag per eventuale scalatura immagine già in lettura int nFlag = 0 ; if ( nFif == FIF_JPEG) nFlag = ( m_nMaxLinPix / 2) << 16 ; // carico l'immagine pDib = nullptr ; if ( FreeImage_FIFSupportsReading( nFif)) pDib = FreeImage_LoadU( nFif, stringtoW( sFile), nFlag) ; if ( pDib == nullptr) return false ; // recupero alcuni dati dell'immagine e li verifico if ( FreeImage_GetBits( pDib) == nullptr || FreeImage_GetWidth( pDib) == 0 || FreeImage_GetHeight( pDib) == 0) { FreeImage_Unload( pDib) ; pDib = nullptr ; return false ; } // recupero dimensioni dell'immagine nel file if ( nFif == FIF_JPEG) { FIBITMAP* pTmp = FreeImage_LoadU( nFif, stringtoW( sFile), FIF_LOAD_NOPIXELS) ; if ( pTmp == nullptr) return false ; nFilePixX = FreeImage_GetWidth( pTmp) ; nFilePixY = FreeImage_GetHeight( pTmp) ; FreeImage_Unload( pTmp) ; } else { nFilePixX = FreeImage_GetWidth( pDib) ; nFilePixY = FreeImage_GetHeight( pDib) ; } return true ; } //---------------------------------------------------------------------------- bool TextureMgr::TestImageWithOpenGL( FIBITMAP*& pDib) { // recupero le dimensioni in pixel dell'immagine int nWidth = FreeImage_GetWidth( pDib) ; int nHeight = FreeImage_GetHeight( pDib) ; // verifico di non superare la massima dimensione ammessa if ( nWidth > m_nMaxLinPix || nHeight > m_nMaxLinPix) { double dCoeff = max( nWidth / double( m_nMaxLinPix), nHeight / double( m_nMaxLinPix)) ; int nNewW = int( nWidth / dCoeff) ; int nNewH = int( nHeight / dCoeff) ; FIBITMAP* pTmpDib = pDib ; pDib = FreeImage_Rescale( pTmpDib, nNewW, nNewH, FILTER_BOX) ; FreeImage_Unload( pTmpDib) ; nWidth = nNewW ; nHeight = nNewH ; // verifico che la nuova immagine sia valida if ( pDib == nullptr) return false ; if ( FreeImage_GetBits( pDib) == nullptr || FreeImage_GetWidth( pDib) != nWidth || FreeImage_GetHeight( pDib) != nHeight) { FreeImage_Unload( pDib) ; pDib = nullptr ; return false ; } } // verifico sia caricabile da OpenGL corrente glTexImage2D( GL_PROXY_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_BGR, GL_UNSIGNED_BYTE, FreeImage_GetBits( pDib)) ; int nW ; glGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &nW) ; if ( nW == 0) { // recupero la massima dimensione di una texture per OpenGL corrente int nMaxTextDim ; glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextDim) ; // cerco le dimensioni POT (power of two) appena minori delle due dimensioni int nNewW = nMaxTextDim ; while ( nNewW > nWidth) nNewW /= 2 ; int nNewH = nMaxTextDim ; while ( nNewH > nHeight) nNewH /= 2 ; // deformo la texture per avere queste dimensioni FIBITMAP* pTmpDib = pDib ; pDib = FreeImage_Rescale( pTmpDib, nNewW, nNewH, FILTER_BILINEAR) ; FreeImage_Unload( pTmpDib) ; nWidth = nNewW ; nHeight = nNewH ; // verifico che la nuova immagine sia valida if ( pDib == nullptr) return false ; if ( FreeImage_GetBits( pDib) == nullptr || FreeImage_GetWidth( pDib) != nWidth || FreeImage_GetHeight( pDib) != nHeight) { FreeImage_Unload( pDib) ; pDib = nullptr ; return false ; } } return true ; }