Show texture.cpp syntax highlighted
/***************************************************************************
texture.cpp - OpenGL texture implementation
-------------------
begin : Sun Mar 30 2003
copyright : (C) 2003 by Reality Rift Studios
email : mattias@realityrift.com
***************************************************************************
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the NeoEngine, NeoDevOpenGL, texture.cpp
The Initial Developer of the Original Code is Mattias Jansson.
Portions created by Mattias Jansson are Copyright (C) 2003
Reality Rift Studios. All Rights Reserved.
***************************************************************************/
#include "texture.h"
#include "device.h"
#include "textureunit.h"
#include <neoengine/file.h>
#include <neoengine/util.h>
#include <neoengine/logstream.h>
using namespace std;
using namespace NeoEngine;
namespace NeoOGL
{
GLenum BuildMinFilter( unsigned int uiMinFilter, unsigned int uiMipFilter )
{
if( uiMinFilter == Texture::LINEAR )
{
if( uiMipFilter == Texture::LINEAR )
return GL_LINEAR_MIPMAP_LINEAR;
else if( uiMipFilter == Texture::NEAREST )
return GL_LINEAR_MIPMAP_NEAREST;
return GL_LINEAR;
}
else //if( m_uiMinFilter == Texture::NEAREST )
{
if( uiMipFilter == Texture::LINEAR )
return GL_NEAREST_MIPMAP_LINEAR;
else if( uiMipFilter == Texture::NEAREST )
return GL_NEAREST_MIPMAP_NEAREST;
return GL_NEAREST;
}
}
GLenum BuildMagFilter( unsigned int uiMagFilter )
{
if( uiMagFilter == Texture::LINEAR )
return GL_LINEAR;
return GL_NEAREST;
}
inline int lowpow( int n )
{
int m;
for( m = 1; m <= n; m <<= 1 );
m >>= 1;
return m;
}
TexturePtr Device::GetTexture( const std::string &rstrName )
{
const vector< NeoEngine::Texture* > *pvpkTextures = m_pkTexturePool->Find( rstrName );
if( pvpkTextures && pvpkTextures->size() )
return *(pvpkTextures->begin());
return 0;
}
TexturePtr Device::LoadTexture( const std::string &rstrFilename, Texture::TEXTURETYPE eTextureType, Texture::TEXTUREFORMAT eTextureFormat, unsigned int uiFlags, unsigned int uiFiltering, unsigned int uiMaxAnisotropy )
{
if (rstrFilename == "null" ) return 0;
//try to find texture
{
const vector< NeoEngine::Texture* > *pvpkTextures = m_pkTexturePool->Find( rstrFilename );
if( pvpkTextures && pvpkTextures->size() )
{
vector< NeoEngine::Texture* >::const_iterator ppkTexture = pvpkTextures->begin();
vector< NeoEngine::Texture* >::const_iterator ppkEnd = pvpkTextures->end();
for( ; ppkTexture != ppkEnd; ++ppkTexture )
if( ( (*ppkTexture)->GetType() == eTextureType ) && ( ( eTextureFormat == Texture::DEFAULT ) || ( (*ppkTexture)->GetFormat() == eTextureFormat ) ) )
return *ppkTexture;
}
}
Texture *pkTexture = new NeoOGL::Texture( this, rstrFilename, eTextureType, eTextureFormat, uiFiltering, uiMaxAnisotropy, m_pkTexturePool, m_pkFileManager );
vector< ImageCodec* >::iterator ppkCodec = m_vpkCodecs.begin();
vector< ImageCodec* >::iterator ppkCodecEnd = m_vpkCodecs.end();
for( ; ppkCodec != ppkCodecEnd; ++ppkCodec )
{
string strFilename = rstrFilename + ".";
const vector<string> &rvstrExtensions = (*ppkCodec)->GetExtensions();
vector<string>::const_iterator pstrExt = rvstrExtensions.begin();
vector<string>::const_iterator pstrExtEnd = rvstrExtensions.end();
for( ; pstrExt != pstrExtEnd; ++pstrExt )
{
string strThisFile = strFilename + (*pstrExt);
if( pkTexture->Load( strThisFile, uiFlags ) )
return pkTexture;
}
}
neolog << LogLevel( WARNING ) << "*** unable to load texture [" << rstrFilename << "]" << endl;
delete pkTexture;
return 0;
}
TexturePtr Device::CreateTexture( const std::string &rstrName, Texture::TEXTURETYPE eTextureType, Texture::TEXTUREFORMAT eTextureFormat )
{
return new NeoOGL::Texture( this, rstrName, eTextureType, eTextureFormat, m_uiDefaultTextureFilter, 1, m_pkTexturePool, m_pkFileManager );
}
const GLenum Texture::s_aeOGLCubeFaces[6] = {
GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
};
unsigned int GetTargetIndex( Texture::TEXTURETYPE eTextureType )
{
switch( eTextureType )
{
/* case Texture::TEX1D:
return 0;
*/
case Texture::TEX2D:
return 1;
/* case Texture::TEX3D:
return 2;
*/
case Texture::CUBEMAP:
return 3;
case Texture::CUBEMAPFBO:
return 3;
default:
break;
}
return 1;
}
Texture::Texture( Device *pkDevice, const std::string &rstrName, TEXTURETYPE eTextureType, TEXTUREFORMAT eTextureFormat, unsigned int uiFiltering, unsigned int uiMaxAnisotropy, TexturePool *pkTexturePool, FileManager *pkFileManager, int iWidth, int iHeight ) :
NeoEngine::Texture( rstrName, pkTexturePool ),
LoadableEntity( pkFileManager ),
m_pkDevice( pkDevice ),
m_uiUVAddress( 0 )
{
m_iID = 0;
m_eFormat = eTextureFormat;
m_eType = eTextureType;
m_uiFiltering = uiFiltering;
m_uiTargetIndex = GetTargetIndex( eTextureType );
m_iWidth = iWidth;
m_iHeight = iHeight;
m_fWidth = (float)m_iWidth;
m_fHeight = (float)m_iHeight;
m_fRatio = ( m_iWidth || m_iHeight ) ? ( ( m_iWidth > m_iHeight ) ? (float)m_iWidth / (float)m_iHeight : (float)m_iHeight / (float)m_iWidth ) : 0.0f;
//Generate texture
glGenTextures( 1, (GLuint*)&m_iID );
//get default anisotropy from the device
m_uiMaxAnisotropy=pkDevice->m_uiDefaultMaxAnisotropy;
}
Texture::~Texture()
{
if( m_iID > 0 )
{
for( unsigned int uiTMU = 0; uiTMU < m_pkDevice->m_uiNumTMUs; ++uiTMU )
{
if( m_pkDevice->m_ppkTMU[ uiTMU ]->m_akTarget[ m_uiTargetIndex ].m_pkTexture == this )
{
if( m_pkDevice->m_ppkTMU[ uiTMU ]->m_akTarget[ m_uiTargetIndex ].m_bEnabled )
{
assert( m_pkDevice->m_ppkTMU[ uiTMU ]->m_pkTexture == this );
glDisable( m_pkDevice->m_ppkTMU[ uiTMU ]->m_akTarget[ m_uiTargetIndex ].m_eTarget );
}
//Make sure unbinding is not done on us, we are destroyed
if( m_pkDevice->m_ppkTMU[ uiTMU ]->m_pkTexture == this )
m_pkDevice->m_ppkTMU[ uiTMU ]->m_pkTexture = 0;
m_pkDevice->m_ppkTMU[ uiTMU ]->m_akTarget[ m_uiTargetIndex ].m_pkTexture = 0;
m_pkDevice->m_ppkTMU[ uiTMU ]->m_akTarget[ m_uiTargetIndex ].m_bEnabled = false;
}
}
glDeleteTextures( 1, (GLuint*)&m_iID );
}
neolog << LogLevel( DEBUG ) << "Deallocated texture [" << GetName() << "]" << endl;
}
bool Texture::LoadNode( unsigned int uiFlags )
{
bool bRet = true;
string strName = m_pkFile->GetName();
strName = strName.substr( 0, strName.find_last_of( '.' ) );
if( GetName() != strName )
SetName( strName );
//Read and create new texture from image data
ImageData *pkImageData = m_pkDevice->LoadImageData( m_pkFile );
if( !pkImageData )
{
neolog << LogLevel( ERROR ) << "*** Unable to load texture: Failed to load image data from file [" << m_pkFile->GetName() << "]" << endl;
return false;
}
if( !UploadImage( pkImageData, m_eType, m_eFormat, uiFlags, m_uiFiltering, m_uiMaxAnisotropy ) )
bRet = false;
m_pkDevice->FreeImageData( pkImageData );
return bRet;
}
bool Texture::UploadImage( ImageData *pkImageData, TEXTURETYPE eTextureType, TEXTUREFORMAT eTextureFormat, unsigned int uiFlags, unsigned int uiFiltering, unsigned int uiMaxAnisotropy )
{
int iSize = 0;
int iComp = 0;
GLenum iInternalFormat = 0;
GLenum iDataFormat = 0;
GLenum iDataType = GL_UNSIGNED_BYTE;
if( m_eFormat == Texture::GRAY || m_eFormat == Texture::GRAYALPHA )
{
neolog << LogLevel( ERROR ) << "*** Unable to upload image: Cannot use GRAY or GRAYALPHA image formats in textures" << endl
<< " You must convert these images to RGB or RGBA before using as textures" << endl;
return false;
}
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::EXT_TEXTURE_FILTER_ANISOTROPIC ) ){
glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint*) &m_uiMaxAnisotropy );
}
if( uiMaxAnisotropy > m_uiMaxAnisotropy )
uiMaxAnisotropy = m_uiMaxAnisotropy;
m_iWidth = pkImageData->m_iWidth;
m_iHeight = ( eTextureType == Texture::CUBEMAP ) ? pkImageData->m_iHeight / 6 : pkImageData->m_iHeight;
m_eFormat = pkImageData->m_eFormat;
m_eType = eTextureType;
m_uiFiltering = uiFiltering ? uiFiltering : m_pkDevice->m_uiDefaultTextureFilter;
m_uiTargetIndex = GetTargetIndex( eTextureType );
if( ( eTextureType == Texture::CUBEMAP ) && ( m_iWidth != m_iHeight ) )
{
neolog << LogLevel( ERROR ) << "*** Unable to upload image: invalid cubemap, width != height" << endl;
return false;
}
switch( m_eFormat )
{
case Texture::ALPHA:
{
iInternalFormat = GL_ALPHA;
iDataFormat = GL_ALPHA;
iComp = 1;
m_iInternalFormat = Texture::INTERNALFORMAT_ALPHA8;
break;
}
case Texture::RGB:
{
iInternalFormat = GL_RGB;
iDataFormat = GL_RGB;
iComp = 3;
m_iInternalFormat = Texture::INTERNALFORMAT_RGB888;
break;
}
case Texture::RGBA:
{
iInternalFormat = GL_RGBA;
iDataFormat = GL_RGBA;
iComp = 4;
m_iInternalFormat = Texture::INTERNALFORMAT_RGBA8888;
break;
}
case Texture::DEPTH:
{
iInternalFormat = GL_DEPTH_COMPONENT;
iDataFormat = GL_DEPTH_COMPONENT;
iComp = 1;
m_iInternalFormat = Texture::INTERNALFORMAT_DEPTH;
break;
}
case Texture::RGBA16F:
{
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_FLOAT_TEXTURE ) ){
iInternalFormat = GL_RGBA16F_ARB;
iDataFormat = GL_RGBA;
iComp = 4;
iDataType = GL_FLOAT;
m_iInternalFormat = Texture::INTERNALFORMAT_RGBA16F;
}else{
neolog << LogLevel( ERROR ) << "*** Invalid texture format [" << (int)m_eFormat << "]" << endl;
}
break;
}
case Texture::RGB16F:
{
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_FLOAT_TEXTURE ) ){
iInternalFormat = GL_RGB16F_ARB;
iDataFormat = GL_RGB;
iComp = 3;
iDataType = GL_FLOAT;
m_iInternalFormat = Texture::INTERNALFORMAT_RGB16F;
}else{
neolog << LogLevel( ERROR ) << "*** Invalid texture format [" << (int)m_eFormat << "]" << endl;
}
break;
}
case Texture::RGBA32F:
{
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_FLOAT_TEXTURE ) ){
iInternalFormat = GL_RGBA32F_ARB;
iDataFormat = GL_RGBA;
iComp = 4;
iDataType = GL_FLOAT;
m_iInternalFormat = Texture::INTERNALFORMAT_RGBA32F;
}else{
neolog << LogLevel( ERROR ) << "*** Invalid texture format [" << (int)m_eFormat << "]" << endl;
}
break;
}
case Texture::RGB32F:
{
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_FLOAT_TEXTURE ) ){
iInternalFormat = GL_RGB32F_ARB;
iDataFormat = GL_RGB;
iComp = 3;
iDataType = GL_FLOAT;
m_iInternalFormat = Texture::INTERNALFORMAT_RGB32F;
}else{
neolog << LogLevel( ERROR ) << "*** Invalid texture format [" << (int)m_eFormat << "]" << endl;
}
break;
}
default:
{
neolog << LogLevel( ERROR ) << "*** Invalid texture format [" << (int)m_eFormat << "]" << endl;
break;
}
}
if( eTextureFormat != Texture::DEFAULT )
{
switch( eTextureFormat )
{
case Texture::RGB:
{
m_iInternalFormat = Texture::INTERNALFORMAT_RGB888;
iInternalFormat = GL_RGB;
break;
}
case Texture::RGBA:
{
m_iInternalFormat = Texture::INTERNALFORMAT_RGBA8888;
iInternalFormat = GL_RGBA;
break;
}
case Texture::RGB16F :
{
m_iInternalFormat = Texture::INTERNALFORMAT_RGB16F;
iInternalFormat = GL_RGB16F_ARB;
iDataType = GL_FLOAT;
break;
}
case Texture::RGBA16F :
{
m_iInternalFormat = Texture::INTERNALFORMAT_RGBA16F;
iInternalFormat = GL_RGBA16F_ARB;
iDataType = GL_FLOAT;
break;
}
case Texture::RGB32F :
{
m_iInternalFormat = Texture::INTERNALFORMAT_RGB32F;
iInternalFormat = GL_RGB32F_ARB;
iDataType = GL_FLOAT;
break;
}
case Texture::RGBA32F :
{
m_iInternalFormat = Texture::INTERNALFORMAT_RGBA32F;
iInternalFormat = GL_RGBA32F_ARB;
iDataType = GL_FLOAT;
break;
}
case Texture::ALPHA:
{
m_iInternalFormat = Texture::INTERNALFORMAT_ALPHA8;
iInternalFormat = GL_ALPHA;
break;
}
case Texture::DEPTH:
{
m_iInternalFormat = Texture::INTERNALFORMAT_DEPTH;
iInternalFormat = GL_DEPTH_COMPONENT32_ARB; //DEPTH_COMPONENT32_ARB
break;
}
default:
{
if( eTextureFormat != Texture::DEFAULT )
neolog << LogLevel( WARNING ) << "*** Invalid requested format [" << (int)eTextureFormat << "], ignored" << endl;
break;
}
}
}
//Dario
//uiFlags = Texture::NOMIPMAPS;
//m_uiMaxAnisotropy =0;
if( uiFlags & Texture::NOMIPMAPS )
m_uiFiltering &= 0xFF00FFFF; // Mask away mipmap filter to none
int iNewWidth = m_iWidth;
int iNewHeight = m_iHeight;
// test for non power of two texture support
if ( !m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_NP2_TEXTURE) )
{
iNewWidth = lowpow( m_iWidth );
iNewHeight = lowpow( m_iHeight );
}
if( iNewWidth > (int)m_pkDevice->m_uiMaxTextureSize )
iNewWidth = m_pkDevice->m_uiMaxTextureSize;
if( iNewHeight > (int)m_pkDevice->m_uiMaxTextureSize )
iNewHeight = m_pkDevice->m_uiMaxTextureSize;
unsigned char *pucSavedImage = 0;
// if( ( m_iWidth != iNewWidth ) || ( m_iHeight != iNewHeight ) )
if ( !m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_RECT_TEXTURE) && ( ( m_iWidth != iNewWidth ) || ( m_iHeight != iNewHeight ) ) )
{
//Resize image
unsigned char *pucImage = new unsigned char[ iComp * iNewWidth * iNewHeight * ( ( eTextureType == Texture::CUBEMAP ) ? 6 : 1 ) ];
ImageData::ScaleImage( iComp, pkImageData->m_iWidth, pkImageData->m_iHeight, pkImageData->m_pucData, iNewWidth, iNewHeight, pucImage );
pucSavedImage = pkImageData->m_pucData;
pkImageData->m_pucData = pucImage;
}
iSize = iNewWidth * iNewHeight * iComp;
m_pkDevice->m_ppkTMU[0]->SetTexture( this );
if( eTextureType == Texture::TEX2D )
{
if( !( uiFlags & Texture::NOMIPMAPS ) )
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, BuildMinFilter( m_uiFiltering & 0xFF, ( m_uiFiltering >> 16 ) & 0xFF ) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, BuildMagFilter( ( m_uiFiltering >> 8 ) & 0xFF ) );
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::EXT_TEXTURE_FILTER_ANISOTROPIC ) && m_uiMaxAnisotropy > 0 )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (GLint) m_uiMaxAnisotropy );
//Create mipmaps
#ifdef GL_GENERATE_MIPMAP_SGIS
if( m_pkDevice->m_kExtensions.IsSupported( Extensions::SGIS_GENERATE_MIPMAP ) )
{
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE ); //Generate mipmaps automatically
glTexImage2D( GL_TEXTURE_2D, 0, iInternalFormat, iNewWidth, iNewHeight, 0, iDataFormat, iDataType, pkImageData->m_pucData );
}
else
#endif
{
//software mipmap generation
pkImageData->CreateMipMaps();
glTexImage2D( GL_TEXTURE_2D, 0, iInternalFormat, pkImageData->m_iWidth, pkImageData->m_iHeight, 0, iDataFormat, iDataType, pkImageData->m_pucData );
for( int iMap = 0; iMap < pkImageData->m_iMipMaps; ++iMap )
glTexImage2D( GL_TEXTURE_2D, iMap + 1, iInternalFormat, pkImageData->m_pkMipMaps[iMap].m_iWidth, pkImageData->m_pkMipMaps[iMap].m_iHeight, 0, iDataFormat, iDataType, pkImageData->m_pkMipMaps[iMap].m_pucData );
}
}
else
{
//Dario
// glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
// glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, BuildMinFilter( m_uiFiltering & 0xFF, 0 ) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, BuildMagFilter( ( m_uiFiltering >> 8 ) & 0xFF ) );
glTexImage2D( GL_TEXTURE_2D, 0, iInternalFormat, iNewWidth, iNewHeight, 0, iDataFormat, iDataType, pkImageData->m_pucData );
}
}
else if( eTextureType == Texture::CUBEMAP )
{
if( !m_pkDevice->m_kExtensions.IsSupported( Extensions::ARB_TEXTURE_CUBE_MAP ) )
{
neolog << LogLevel( ERROR ) << "*** Unable to upload image: no cubemap support" << endl;
return false;
}
//No mipmaps
glTexParameteri( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, BuildMinFilter( m_uiFiltering & 0xFF, 0 ) );
glTexParameteri( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, BuildMagFilter( ( m_uiFiltering >> 8 ) & 0xFF ) );
for( int iFace = 0; iFace < 6; ++iFace )
glTexImage2D( s_aeOGLCubeFaces[iFace], 0, iInternalFormat, iNewWidth, iNewHeight, 0, iDataFormat, GL_UNSIGNED_BYTE, pkImageData->m_pucData + iSize * iFace );
}
else
{
neolog << LogLevel( ERROR ) << " *** Unable to upload image: unsupported texture type [" << (int)eTextureType << "]" << endl;
return false;
}
GLint iRealWidth = 0;
GLint iRealHeight = 0;
GLint iA = 0;
GLint iR = 0;
GLint iG = 0;
GLint iB = 0;
GLenum eTextureID = ( eTextureType != Texture::CUBEMAP ) ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB;
glGetTexLevelParameteriv( eTextureID, 0, GL_TEXTURE_WIDTH, &iRealWidth );
glGetTexLevelParameteriv( eTextureID, 0, GL_TEXTURE_HEIGHT, &iRealHeight );
glGetTexLevelParameteriv( eTextureID, 0, GL_TEXTURE_ALPHA_SIZE, &iA );
glGetTexLevelParameteriv( eTextureID, 0, GL_TEXTURE_RED_SIZE, &iR );
glGetTexLevelParameteriv( eTextureID, 0, GL_TEXTURE_GREEN_SIZE, &iG );
glGetTexLevelParameteriv( eTextureID, 0, GL_TEXTURE_BLUE_SIZE, &iB );
neolog << LogLevel( DEBUG ) << "Texture [" << GetName() << "] " << m_iWidth << "x" << m_iHeight << " ID " << m_iID << " -> [" << iRealWidth << "x" << iRealHeight << "] ARGB" << iA << iR << iG << iB << ( ( eTextureType != Texture::CUBEMAP ) ? "" : " x 6" ) << endl;
m_iWidth = iRealWidth;
m_iHeight = iRealHeight;
m_fWidth = (float)m_iWidth;
m_fHeight = (float)m_iHeight;
m_fRatio = ( m_iWidth > m_iHeight ) ? (float)m_iWidth / (float)m_iHeight : (float)m_iHeight / (float)m_iWidth;
if( pucSavedImage )
{
delete [] pkImageData->m_pucData;
pkImageData->m_pucData = pucSavedImage;
}
return true;
}
}; // namespace NeoOGL
See more files for this project here