fb44cd7e95
- corretto caricamento texture da jpeg con ridimensionamento immediato.
449 lines
15 KiB
C++
449 lines
15 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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 ;
|
|
}
|