/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "stdh.h" #include #include #include #include #include #include #include #include #include #include #include #include #include extern INDEX tex_iNormalQuality; extern INDEX tex_iAnimationQuality; extern INDEX tex_iNormalSize; extern INDEX tex_iAnimationSize; extern INDEX tex_iEffectSize; extern INDEX tex_iDithering; extern INDEX tex_iFiltering; extern INDEX gap_bAllowSingleMipmap; extern FLOAT gfx_tmProbeDecay; // special mode flag when loading texture for exporting static BOOL _bExport = FALSE; // singleton object for texture settings struct TextureSettings TS = {0}; #define TEXFMT_NONE 0 // determine (or assume) support for OpenGL and Direct3D texture internal formats extern void DetermineSupportedTextureFormats( GfxAPIType eAPI) { if( eAPI==GAT_OGL) { TS.ts_tfRGB8 = GL_RGB8; TS.ts_tfRGBA8 = GL_RGBA8; TS.ts_tfRGB5 = GL_RGB5; TS.ts_tfRGBA4 = GL_RGBA4; TS.ts_tfRGB5A1 = GL_RGB5_A1; TS.ts_tfLA8 = GL_LUMINANCE8_ALPHA8; TS.ts_tfL8 = GL_LUMINANCE8; } #ifdef SE1_D3D if( eAPI==GAT_D3D) { extern D3DFORMAT FindClosestFormat_D3D(D3DFORMAT d3df); TS.ts_tfRGBA8 = FindClosestFormat_D3D(D3DFMT_A8R8G8B8); TS.ts_tfRGB8 = FindClosestFormat_D3D(D3DFMT_X8R8G8B8); TS.ts_tfRGB5 = FindClosestFormat_D3D(D3DFMT_R5G6B5); TS.ts_tfRGB5A1 = FindClosestFormat_D3D(D3DFMT_A1R5G5B5); TS.ts_tfRGBA4 = FindClosestFormat_D3D(D3DFMT_A4R4G4B4); TS.ts_tfLA8 = FindClosestFormat_D3D(D3DFMT_A8L8); TS.ts_tfL8 = FindClosestFormat_D3D(D3DFMT_L8); } #endif // SE1_D3D } // update all relevant texture parameters extern void UpdateTextureSettings(void) { // determine API const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI; #ifdef SE1_D3D ASSERT( eAPI==GAT_OGL || eAPI==GAT_D3D || eAPI==GAT_NONE); #else ASSERT( eAPI==GAT_OGL || eAPI==GAT_NONE); #endif // SE1_D3D // set texture formats and compression TS.ts_tfRGB8 = TS.ts_tfRGBA8 = NONE; TS.ts_tfRGB5 = TS.ts_tfRGBA4 = TS.ts_tfRGB5A1 = NONE; TS.ts_tfLA8 = TS.ts_tfL8 = NONE; DetermineSupportedTextureFormats(eAPI); // clamp and adjust texture compression type INDEX iTCType = 0; const ULONG ulGfxFlags = _pGfx->gl_ulFlags; const BOOL bHasTC = (ulGfxFlags&GLF_TEXTURECOMPRESSION); if( eAPI==GAT_OGL && bHasTC) { // OpenGL extern INDEX ogl_iTextureCompressionType; // 0=none, 1=default (ARB), 2=S3TC, 3=FXT1, 4=legacy S3TC INDEX &iTC = ogl_iTextureCompressionType; iTC = Clamp( iTC, 0L, 4L); if( iTC==3 && !(ulGfxFlags&GLF_EXTC_FXT1)) iTC = 2; if( iTC==2 && !(ulGfxFlags&GLF_EXTC_S3TC)) iTC = 3; if((iTC==2 || iTC==3) && !((ulGfxFlags&GLF_EXTC_FXT1) || (ulGfxFlags&GLF_EXTC_S3TC))) iTC = 1; if( iTC==1 && !(ulGfxFlags&GLF_EXTC_ARB)) iTC = 4; if( iTC==4 && !(ulGfxFlags&GLF_EXTC_LEGACY)) iTC = 0; // khm ... iTCType = iTC; // set it } // Direct3D (just force DXTC - it's the only one) #ifdef SE1_D3D if( eAPI==GAT_D3D && bHasTC) iTCType = 5; #endif // SE1_D3D // clamp and cache cvar extern INDEX tex_bCompressAlphaChannel; if( tex_bCompressAlphaChannel) tex_bCompressAlphaChannel = 1; const BOOL bCAC = tex_bCompressAlphaChannel; // set members switch( iTCType) { case 1: // ARB TS.ts_tfCRGBA = GL_COMPRESSED_RGBA_ARB; TS.ts_tfCRGB = GL_COMPRESSED_RGB_ARB; break; case 2: // S3TC TS.ts_tfCRGBA = bCAC ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; TS.ts_tfCRGB = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case 3: // FXT1 TS.ts_tfCRGBA = GL_COMPRESSED_RGBA_FXT1_3DFX; TS.ts_tfCRGB = GL_COMPRESSED_RGB_FXT1_3DFX; break; case 4: // LEGACY TS.ts_tfCRGBA = bCAC ? GL_COMPRESSED_RGB4_COMPRESSED_ALPHA4_S3TC : GL_COMPRESSED_RGBA_S3TC; TS.ts_tfCRGB = GL_COMPRESSED_RGB_S3TC; break; #ifdef SE1_D3D case 5: // DXTC extern D3DFORMAT FindClosestFormat_D3D(D3DFORMAT d3df); TS.ts_tfCRGBA = bCAC ? FindClosestFormat_D3D(D3DFMT_DXT5) : FindClosestFormat_D3D(D3DFMT_DXT3); TS.ts_tfCRGB = D3DFMT_DXT1; break; #endif // SE1_D3D default: // none TS.ts_tfCRGBA = NONE; TS.ts_tfCRGB = NONE; break; } // adjust if need to compress opaque textures as transparent extern INDEX tex_bAlternateCompression; if( tex_bAlternateCompression) { tex_bAlternateCompression = 1; TS.ts_tfCRGB = TS.ts_tfCRGBA; } // clamp texture quality INDEX iMinQuality = 0; INDEX iMaxQuality = iTCType>0 ? 3 : 2; if( !(_pGfx->gl_ulFlags&GLF_32BITTEXTURES)) iMinQuality = iMaxQuality = 1; TS.ts_iNormQualityO = Clamp( (INDEX)(tex_iNormalQuality /10), iMinQuality, iMaxQuality); TS.ts_iNormQualityA = Clamp( (INDEX)(tex_iNormalQuality %10), iMinQuality, iMaxQuality); TS.ts_iAnimQualityO = Clamp( (INDEX)(tex_iAnimationQuality/10), iMinQuality, iMaxQuality); TS.ts_iAnimQualityA = Clamp( (INDEX)(tex_iAnimationQuality%10), iMinQuality, iMaxQuality); tex_iNormalQuality = TS.ts_iNormQualityO*10 + TS.ts_iNormQualityA; tex_iAnimationQuality = TS.ts_iAnimQualityO*10 + TS.ts_iAnimQualityA; // clamp texture size tex_iNormalSize = Clamp( tex_iNormalSize, 5L, 11L); tex_iAnimationSize = Clamp( tex_iAnimationSize, 5L, 9L); TS.ts_pixNormSize = 1L<<(tex_iNormalSize *2); TS.ts_pixAnimSize = 1L<<(tex_iAnimationSize*2); // determine maximum texel-byte ratio INDEX iOQ = tex_iNormalQuality /10; if( iOQ==0) iOQ=2; else if( iOQ==3) iOQ=0; INDEX iAQ = tex_iNormalQuality %10; if( iAQ==0) iAQ=2; else if( iAQ==3) iAQ=0; INDEX iTexMul = 2 * Max(iOQ,iAQ); if( iTexMul==0) iTexMul=1; if( tex_iNormalSize<=6 && iTexMul==1) iTexMul = 2; if( tex_iNormalSize<=5 && iTexMul==2) iTexMul = 4; TS.ts_iMaxBytesPerTexel = iTexMul; } /***************************************** * Implementation of CTextureData routines */ CTextureData::CTextureData() { td_ulFlags = NONE; td_mexWidth = 0; td_mexHeight = 0; td_tvLastDrawn = 0I64; td_iFirstMipLevel = 0; td_ctFineMipLevels = 0; td_ctFrames = 0; td_slFrameSize = 0; td_ulInternalFormat = TEXFMT_NONE; td_ulProbeObject = NONE; td_ulObject = NONE; td_pulFrames = NULL; td_pubBuffer1 = NULL; // reset effect buffers td_pubBuffer2 = NULL; td_pixBufferWidth = 0; td_pixBufferHeight = 0; td_ptdBaseTexture = NULL; // no base texture by default td_ptegEffect = NULL; // no effect data td_iRenderFrame = -1; CAnimData::DefaultAnimation(); _bExport = FALSE; } CTextureData::~CTextureData() { Clear(); } // converts mip level to the one of allowed by texture INDEX CTextureData::ClampMipLevel( FLOAT fMipFactor) const { INDEX res = (INDEX)fMipFactor; INDEX iLastMipLevel = GetNoOfMipmaps( GetPixWidth(), GetPixHeight()) -1 +td_iFirstMipLevel; res = Clamp( res, td_iFirstMipLevel, iLastMipLevel); return( res); } // routine that adds one-frame to texture from one picture void CTextureData::AddFrame_t( const CImageInfo *pII) { // check for supported image format ASSERT( pII->ii_BitsPerPixel==24 || pII->ii_BitsPerPixel==32); if( pII->ii_BitsPerPixel!=24 && pII->ii_BitsPerPixel!=32) { throw( TRANS("Only 24-bit and 32-bit pictures can be processed.")); } PIX pixWidth = pII->ii_Width; PIX pixHeight = pII->ii_Height; // frame that is about to be added must have the same dimensions as the texture ASSERT( pixWidth == GetPixWidth() ); ASSERT( pixHeight == GetPixHeight() ); if( pixWidth != GetPixWidth() ) throw( TRANS("Incompatible frame width.")); if( pixHeight != GetPixHeight() ) throw( TRANS("Incompatible frame height.")); // add memory for new frame SLONG slFramesSize = td_slFrameSize * td_ctFrames; GrowMemory( (void**)&td_pulFrames, slFramesSize + td_slFrameSize); // add new frame to the end of the previous texture frames PIX pixMipmapSize = pixWidth*pixHeight; ULONG *pulCurrentFrame = td_pulFrames + slFramesSize/BYTES_PER_TEXEL; if( td_ulFlags&TEX_ALPHACHANNEL) { // has alpha channel - do simple copying memcpy( pulCurrentFrame, pII->ii_Picture, pixMipmapSize*4); } else { // hasn't got alpha channel - do conversion from 24-bit bitmap to 32-bit format memcpy( pulCurrentFrame, pII->ii_Picture, pixMipmapSize*3); AddAlphaChannel( (UBYTE*)pulCurrentFrame, (ULONG*)pulCurrentFrame, pixMipmapSize); } // make mipmaps (in place!) MakeMipmaps( td_ctFineMipLevels, pulCurrentFrame, pixWidth,pixHeight); // increase number of frames td_ctFrames++; } // routine that creates one-frame-texture from loaded picture thru image info structure void CTextureData::Create_t( const CImageInfo *pII, MEX mexWanted, INDEX ctFineMips, BOOL bForce32bit) { // check for supported image format _bExport = FALSE; ASSERT( pII->ii_BitsPerPixel==24 || pII->ii_BitsPerPixel==32); if( pII->ii_BitsPerPixel!=24 && pII->ii_BitsPerPixel!=32) { throw( TRANS("Only 24-bit and 32-bit pictures can be processed.")); } // get picture data PIX pixSizeU = pII->ii_Width; PIX pixSizeV = pII->ii_Height; // check maximum supported texture dimension if( pixSizeU>MAX_MEX || pixSizeV>MAX_MEX) throw( TRANS("At least one of texture dimensions is too large.")); // determine physical (maximum) number of mip-levels INDEX iSizeULog2 = FastLog2( pixSizeU); INDEX iSizeVLog2 = FastLog2( pixSizeV); ASSERT( (1UL<ii_BitsPerPixel==32) td_ulFlags |= TEX_ALPHACHANNEL; if( bForce32bit) td_ulFlags |= TEX_32BIT; // initialize general TextureData members td_ctFrames = 0; td_mexWidth = pixSizeU<td_ptegEffect!=NULL); PIX pixWidth = pTD->td_pixBufferWidth; PIX pixHeight = pTD->td_pixBufferHeight; ULONG ulSize = pixWidth*pixHeight *sizeof(UBYTE); // eventual adjustment for water effect type if( pTD->td_ptegEffect->IsWater()) ulSize = pixWidth*(pixHeight+2) *sizeof(SWORD); return ulSize; } // initializes td_pixBufferWidth & td_pixBufferHeight static void InitEffectBufferDimensions( CTextureData *pTD) { // initialize as default PIX pixWidth = pTD->GetPixWidth(); PIX pixHeight = pTD->GetPixHeight(); // if water effect type if( pTD->td_ptegEffect->IsWater()) { // adjust size for water type effect (width or height must be 64) if( pixWidth > pixHeight) { pixHeight = (PIX)((FLOAT)pixHeight/pixWidth *64.0f); pixWidth = 64; } else { pixWidth = (PIX)((FLOAT)pixWidth /pixHeight *64.0f); pixHeight = 64; } } // set pTD->td_pixBufferWidth = pixWidth; pTD->td_pixBufferHeight = pixHeight; } // free effect buffers' memory static void FreeEffectBuffers( CTextureData *pTD) { if( pTD->td_pubBuffer1 != NULL) { FreeMemory( pTD->td_pubBuffer1); pTD->td_pubBuffer1 = NULL; } if( pTD->td_pubBuffer2 != NULL) { FreeMemory( pTD->td_pubBuffer2); pTD->td_pubBuffer2 = NULL; } } // allocates and resets effect buffers static ULONG AllocEffectBuffers( CTextureData *pTD) { // free if already allocated FreeEffectBuffers( pTD); // determine size of effect buffers ULONG ulSize = GetEffectBufferSize( pTD); // allocate and reset buffers (memory walling!) pTD->td_pubBuffer1 = (UBYTE*)AllocMemory( ulSize+8); pTD->td_pubBuffer2 = (UBYTE*)AllocMemory( ulSize+8); memset( pTD->td_pubBuffer1, 0, ulSize); memset( pTD->td_pubBuffer2, 0, ulSize); return ulSize; } // creates new effect texture with one frame void CTextureData::CreateEffectTexture( PIX pixWidth, PIX pixHeight, MEX mexWidth, CTextureData *ptdBaseTexture, ULONG ulGlobalEffect) { ptdBaseTexture->MarkUsed(); Clear(); CAnimData::DefaultAnimation(); // determine mip index from mex size td_iFirstMipLevel = FastLog2( mexWidth/pixWidth); // fill some of TextureData members td_ulFlags = TEX_STATIC; td_mexWidth = pixWidth <td_ulFlags&TEX_GRAY); BOOL bAlphaChannel = pTD->td_ulFlags & TEX_ALPHACHANNEL; PIX pixTexSize = pTD->GetPixWidth() * pTD->GetPixHeight(); // choose internal texture format for alpha textures INDEX iQuality; ULONG ulInternalFormat; if( bAlphaChannel) { iQuality = pTD->td_ctFrames>1 ? TS.ts_iAnimQualityA : TS.ts_iNormQualityA; ulInternalFormat = TS.ts_tfRGBA4; switch( iQuality) { case 3: case 2: ulInternalFormat = TS.ts_tfRGBA8; break; // uploaded as 32 bit or compressed case 1: break; // uploaded as 16 bit (default) case 0: if( pTD->td_ulFlags&TEX_32BIT) ulInternalFormat = TS.ts_tfRGBA8; break; // uploaded optimally default: ASSERTALWAYS( "Unexpected texture type found."); break; } // adjust quality by size if( pixTexSize<=32*32 && ulInternalFormat==TS.ts_tfRGBA4) ulInternalFormat = TS.ts_tfRGBA8; // do eventual adjustment of internal format for grayscale textures if( bGrayTexture) ulInternalFormat = TS.ts_tfLA8; // handle case of forced internal format (for texture cration process only!) if( _iTexForcedQuality==16) ulInternalFormat = TS.ts_tfRGBA4; if( _iTexForcedQuality==32) ulInternalFormat = TS.ts_tfRGBA8; // do eventual adjustment of transparent textures if( (pTD->td_ulFlags&TEX_TRANSPARENT) && ulInternalFormat==TS.ts_tfRGBA4) ulInternalFormat = TS.ts_tfRGB5A1; } // choose internal texture format for opaque textures else { iQuality = pTD->td_ctFrames>1 ? TS.ts_iAnimQualityO : TS.ts_iNormQualityO; ulInternalFormat = TS.ts_tfRGB5; switch( iQuality) { case 3: case 2: ulInternalFormat = TS.ts_tfRGB8; break; // uploaded as 32 bit or compressed case 1: break; // uploaded as 16 bit (default) case 0: if( pTD->td_ulFlags&TEX_32BIT) ulInternalFormat = TS.ts_tfRGB8; break; // uploaded optimally default: ASSERTALWAYS( "Unexpected texture type found."); break; } // adjust quality by size if( pixTexSize<=32*32 && ulInternalFormat==TS.ts_tfRGB5) ulInternalFormat = TS.ts_tfRGB8; // do eventual adjustment of internal format for grayscale textures if( bGrayTexture) ulInternalFormat = TS.ts_tfL8; // handle case of forced internal format (for texture cration process only!) if( _iTexForcedQuality==16) ulInternalFormat = TS.ts_tfRGB5; if( _iTexForcedQuality==32) ulInternalFormat = TS.ts_tfRGB8; } // adjust format to compressed if needed and allowed if( iQuality==3 && pixTexSize>=64*64) { if( ulInternalFormat==TS.ts_tfRGB8 || ulInternalFormat==TS.ts_tfRGB5 || ulInternalFormat==TS.ts_tfRGB5A1) ulInternalFormat = TS.ts_tfCRGB; if( ulInternalFormat==TS.ts_tfRGBA8 || ulInternalFormat==TS.ts_tfRGBA4) ulInternalFormat = TS.ts_tfCRGBA; } // all done return ulInternalFormat; } // routine that performs texture conversion to current texture format (version 4) static void Convert( CTextureData *pTD) { // skip effect textures if( pTD->td_ptegEffect != NULL) return; // determine dimensions PIX pixWidth = pTD->GetPixWidth(); PIX pixHeight = pTD->GetPixHeight(); PIX pixMipSize = pixWidth * pixHeight; PIX pixFrameSize = GetMipmapOffset( 15, pixWidth, pixHeight); // allocate memory for new texture ULONG *pulFramesNew = (ULONG*)AllocMemory( pixFrameSize*pTD->td_ctFrames *BYTES_PER_TEXEL); UWORD *puwFramesOld = (UWORD*)pTD->td_pulFrames; ASSERT( puwFramesOld!=NULL); // determine alpha channel presence BOOL bHasAlphaChannel = pTD->td_ulFlags & TEX_ALPHACHANNEL; // unpack texture from 16-bit RGBA4444 or RGBA5551 format to RGBA8888 32-bit format UBYTE r,g,b,a; // for each frame for( INDEX iFr=0; iFrtd_ctFrames; iFr++) { // get addresses of current frames (new and old) PIX pixFrameOffset = iFr * pixFrameSize; // for each pixel for( INDEX iPix=0; iPix>8; g = (uwPix & 0x0F00) >>4; b = (uwPix & 0x00F0) >>0; a = (uwPix & 0x000F) <<4; // adjust strength r |= r>>4; g |= g>>4; b |= b>>4; a |= a>>4; } else { // without alpha channel r = (uwPix & 0xF800) >>8; g = (uwPix & 0x07C0) >>3; b = (uwPix & 0x003E) <<2; a = 0xFF; // adjust strength r |= r>>5; g |= g>>5; b |= b>>5; } // pack it back to 32-bit ULONG ulPix = RGBAToColor(r,g,b,a); // store 32-bit pixel pulFramesNew[pixFrameOffset+iPix] = ByteSwap(ulPix); } } // free old memory FreeMemory( pTD->td_pulFrames); // remember new texture parameters pTD->td_pulFrames = pulFramesNew; pTD->td_slFrameSize = pixFrameSize *BYTES_PER_TEXEL; } // remove mipmaps from texture that are not needed (exceeds maximum supported dimension) static void RemoveOversizedMipmaps( CTextureData *pTD) { // if this is an effect texture, leave as it is if( pTD->td_ptegEffect != NULL) return; pTD->td_ulFlags &= ~TEX_DISPOSED; // determine and clamp to max allowed texture dimension and size PIX pixClampAreaSize = (pTD->td_ctFrames>1) ? TS.ts_pixAnimSize : TS.ts_pixNormSize; // constant textures doesn't need clamping to area, but still must be clamped to max HW dimension! if( pTD->td_ulFlags & TEX_CONSTANT) pixClampAreaSize = 4096*4096; // determine dimensions of finest mip-map PIX pixSizeU = pTD->GetPixWidth(); PIX pixSizeV = pTD->GetPixHeight(); // determine number of mip-maps to skip INDEX ctSkipMips = ClampTextureSize( pixClampAreaSize, _pGfx->gl_pixMaxTextureDimension, pixSizeU, pixSizeV); // return if no need to remove mip-maps if( ctSkipMips==0) return; // check for mip overhead INDEX ctMips = GetNoOfMipmaps( pixSizeU, pixSizeV); while( ctMips<=ctSkipMips) ctSkipMips--; // determine memory size and allocate memory for rest mip-maps SLONG slRemovedMipsSize = GetMipmapOffset( ctSkipMips, pixSizeU, pixSizeV) *BYTES_PER_TEXEL; SLONG slNewFrameSize = pTD->td_slFrameSize-slRemovedMipsSize; ULONG *pulNewFrames = (ULONG*)AllocMemory( slNewFrameSize * pTD->td_ctFrames); ULONG *pulNewFrame = pulNewFrames; ULONG *pulOldFrame = pTD->td_pulFrames + (slRemovedMipsSize/BYTES_PER_TEXEL); // copy only needed mip-maps from each frame for( INDEX iFr=0; iFrtd_ctFrames; iFr++) { memcpy( pulNewFrame, pulOldFrame, slNewFrameSize); pulNewFrame += slNewFrameSize/BYTES_PER_TEXEL; pulOldFrame += pTD->td_slFrameSize/BYTES_PER_TEXEL; } // free old frames memory FreeMemory( pTD->td_pulFrames); // adjust texture parameters pTD->td_pulFrames = pulNewFrames; pTD->td_slFrameSize = slNewFrameSize; pTD->td_iFirstMipLevel += ctSkipMips; pTD->td_ctFineMipLevels = ClampDn( (INDEX)(pTD->td_ctFineMipLevels-ctSkipMips), (INDEX)1); // mark that this texture had some mip maps disposed pTD->td_ulFlags |= TEX_DISPOSED; } // internal routines for texture::read routine // test mipmap if it can be equilized #define EQUALIZER_TRESHOLD 3 static BOOL IsEqualized( ULONG *pulMipmap, INDEX pixMipSize) { // determine components and calc averages COLOR col; ULONG ulR=0, ulG=0, ulB=0; for( INDEX iPix=0; iPix>CT_RSHIFT; ulG += (col&CT_GMASK)>>CT_GSHIFT; ulB += (col&CT_BMASK)>>CT_BSHIFT; } ulR /= pixMipSize; ulG /= pixMipSize; ulB /= pixMipSize; const ULONG ulLoEdge = 127-EQUALIZER_TRESHOLD; const ULONG ulHiEdge = 128+EQUALIZER_TRESHOLD; BOOL bEqulized = FALSE; if( ulR>ulLoEdge && ulRulLoEdge && ulGulLoEdge && ulB>CT_ASHIFT; if( ulA>TRANS_TRESHOLD && ulA<(255-TRANS_TRESHOLD)) return FALSE; } // transparent! return TRUE; } // test mipmap whether it is grayscaled static BOOL IsGray( ULONG *pulMipmap, INDEX pixMipSize) { // loop thru texels for( INDEX iPix=0; iPixGetDescription() != "Textures\\Test\\BetterQuality\\FloorWS08.tex"); // reset texture (blank all except some flags) Clear(); // determine API const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI; #ifdef SE1_D3D ASSERT( eAPI==GAT_OGL || eAPI==GAT_D3D || eAPI==GAT_NONE); #else // SE1_D3D ASSERT( eAPI==GAT_OGL || eAPI==GAT_NONE); #endif // SE1_D3D // determine driver context presence (must have at least 1 texture unit!) const BOOL bHasContext = (_pGfx->gl_ctRealTextureUnits>0); // read version INDEX iVersion; inFile->ExpectID_t( "TVER"); *inFile >> iVersion; // check currently supported versions if( iVersion!=4 && iVersion!=3) throw( TRANS("Invalid texture format version.")); // mark if this texture was loaded form the old format if( iVersion==3) td_ulFlags |= TEX_WASOLD; BOOL bResetEffectBuffers = FALSE; BOOL bFramesLoaded = FALSE; BOOL bAlphaChannel = FALSE; // loop trough file and react according to chunk ID do { // obtain chunk id CChunkID idChunk = inFile->GetID_t(); if( idChunk == CChunkID(" ")) { // we should stop reading when an invalid chunk has been encountered break; } // if this is chunk containing texture data if( idChunk == CChunkID("TDAT")) { // read data describing texture ULONG ulFlags=0; INDEX ctMipLevels; *inFile >> ulFlags; *inFile >> td_mexWidth; *inFile >> td_mexHeight; *inFile >> td_ctFineMipLevels; if( iVersion!=4) *inFile >> ctMipLevels; *inFile >> td_iFirstMipLevel; if( iVersion!=4) *inFile >> td_slFrameSize; *inFile >> td_ctFrames; // combine flags td_ulFlags |= ulFlags; bAlphaChannel = td_ulFlags&TEX_ALPHACHANNEL; // determine frame size if( iVersion==4) td_slFrameSize = GetMipmapOffset( 15, GetPixWidth(), GetPixHeight()) * BYTES_PER_TEXEL; } // if this is chunk containing raw frames else if( idChunk == CChunkID("FRMS")) { // if no driver is present and texture is not static if( !(bHasContext || td_ulFlags&TEX_STATIC)) { // determine frames' size SLONG slSkipSize = td_slFrameSize; if( iVersion==4) { slSkipSize = GetPixWidth()*GetPixHeight(); if( bAlphaChannel) slSkipSize *=4; else slSkipSize *=3; } // just seek over frames (skip it) inFile->Seek_t( slSkipSize*td_ctFrames, CTStream::SD_CUR); continue; } // calculate texture size for corresponding texture format and allocate memory SLONG slTexSize = td_slFrameSize * td_ctFrames; td_pulFrames = (ULONG*)AllocMemory( slTexSize); // if older version if( iVersion==3) { // alloc memory block and read mip-maps inFile->Read_t( td_pulFrames, slTexSize); } // if current version else { PIX pixFrameSizeOnDisk = GetPixWidth()*GetPixHeight(); for( INDEX iFr=0; iFrRead_t( pulCurrentFrame, pixFrameSizeOnDisk *4); } else { // read texture without alpha channel from file inFile->Read_t( pulCurrentFrame, pixFrameSizeOnDisk *3); // add opaque alpha channel AddAlphaChannel( (UBYTE*)pulCurrentFrame, pulCurrentFrame, pixFrameSizeOnDisk); } } } bFramesLoaded = TRUE; } // if this is chunk containing texture animation data else if( idChunk == CChunkID("ANIM")) { // read corresponding animation(s) CAnimData::Read_t( inFile); } // if this is chunk containing base texture name else if( idChunk == CChunkID("BAST")) { CTFileName fnBaseTexture; // read file name of base texture *inFile >> fnBaseTexture; // if there is base texture, obtain it if( fnBaseTexture != "") { // must not be the same as base texture CTFileName fnTex = inFile->GetDescription(); if( fnTex == fnBaseTexture) { // generate exception ThrowF_t( TRANS("Texture \"%s\" has same name as its base texture."), (CTString&)fnTex); } else { // obtain base texture td_ptdBaseTexture = _pTextureStock->Obtain_t( fnBaseTexture); } } // force base to be static by default td_ptdBaseTexture->Force(TEX_STATIC); } // if this is chunk containing saved effect buffers else if( idChunk == CChunkID("FXBF")) { // skip chunk in old versions bResetEffectBuffers = TRUE; if( iVersion!=4) { inFile->Seek_t( 2* GetPixWidth()*GetPixHeight() *sizeof(SWORD), CTStream::SD_CUR); } else { ASSERT( td_pixBufferWidth>0 && td_pixBufferHeight>0); ULONG ulSize = AllocEffectBuffers(this); if( td_ptegEffect->IsWater()) ulSize*=2; inFile->Seek_t( 2*ulSize, CTStream::SD_CUR); } } else if( idChunk == CChunkID("FXB2")) { // read effect buffers ASSERT( td_pixBufferWidth>0 && td_pixBufferHeight>0); ULONG ulSize = AllocEffectBuffers(this); inFile->Read_t( td_pubBuffer1, ulSize); inFile->Read_t( td_pubBuffer2, ulSize); } // if this is chunk containing effect data else if( idChunk == CChunkID("FXDT")) { // read effect class ULONG ulGlobalEffect; *inFile >> ulGlobalEffect; // read effect buffer dimensions if( iVersion==4) *inFile >> td_pixBufferWidth; if( iVersion==4) *inFile >> td_pixBufferHeight; // allocate memory for texture effect struct td_ptegEffect = new CTextureEffectGlobal(this, ulGlobalEffect); // skip global properties for old format effect texture if( iVersion!=4) inFile->Seek_t( 64*sizeof(char), CTStream::SD_CUR); // read count of effect sources INDEX ctEffectSources; *inFile >> ctEffectSources; // add requested number of members to effect source array CTextureEffectSource *pEffectSources = td_ptegEffect->teg_atesEffectSources.New( ctEffectSources); // read whole dynamic array of effect sources FOREACHINDYNAMICARRAY( td_ptegEffect->teg_atesEffectSources, CTextureEffectSource, itEffectSource) { // read type of effect source *inFile >> (ULONG) itEffectSource->tes_ulEffectSourceType; // read structure holding effect source properties inFile->Read_t( &itEffectSource->tes_tespEffectSourceProperties, sizeof(struct TextureEffectSourceProperties)); // remember pointer to global effect itEffectSource->tes_ptegGlobalEffect = td_ptegEffect; // read count of effect pixels INDEX ctEffectSourcePixels; *inFile >> ctEffectSourcePixels; // if there are any effect pixels if (ctEffectSourcePixels>0) { // alocate needed ammount of members itEffectSource->tes_atepPixels.New( ctEffectSourcePixels); // read all effect pixels in one block inFile->Read_t( &itEffectSource->tes_atepPixels[0], sizeof(struct TextureEffectPixel)*ctEffectSourcePixels); } } // allocate memory for effect frame buffer SLONG slFrameSize = GetMipmapOffset( 15, GetPixWidth(), GetPixHeight()) *BYTES_PER_TEXEL; td_pulFrames = (ULONG*)AllocMemory( slFrameSize); // remember once again new frame size just for the sake of old effect textures td_slFrameSize = slFrameSize; // mark that effect texture needs to be static td_ulFlags |= TEX_STATIC; } // if this is chunk containing data about detail texture else if( idChunk == CChunkID("DTLT")) { // skip chunk (this is here only for compatibility reasons) CTFileName fnTmp; *inFile >> fnTmp; } else { ThrowF_t( TRANS("Unrecognisable chunk ID (\"%s\") found while reading texture \"%s\"."), (char*)idChunk, (CTString&)inFile->GetDescription() ); } } // until we didn't reach end of file while( !inFile->AtEOF()); // reset effect buffers if needed if( bResetEffectBuffers) { InitEffectBufferDimensions(this); AllocEffectBuffers(this); } // were done if frames weren't loaded or effect texture has been read if( !bFramesLoaded || td_ptegEffect!=NULL) return; // if texture is in old format, convert it to current format if( iVersion==3) Convert(this); PIX pixWidth = GetPixWidth(); PIX pixHeight = GetPixHeight(); PIX pixTexSize = pixWidth*pixHeight; PIX pixFrameSize = td_slFrameSize>>2; // /BYTES_PER_TEXEL; INDEX iFrame; // test first mipmap for transparency (i.e. is one bit of alpha channel enough?) // (must test it before filtering and/or mipmap reduction gets to this texture) if( bAlphaChannel) { td_ulFlags |= TEX_TRANSPARENT; for( iFrame=0; iFrame>2; // /BYTES_PER_TEXEL; // eventually colorize mipmaps extern INDEX tex_bColorizeMipmaps; if( !_bExport && tex_bColorizeMipmaps && !(td_ulFlags&TEX_CONSTANT)) { td_ulFlags |= TEX_COLORIZED; for( iFrame=0; iFrame1)) { // get last mipmap pointer INDEX ctLastPixels = Max(pixWidth,pixHeight) / Min(pixWidth,pixHeight); ULONG *pulLastMipMap = td_pulFrames + td_slFrameSize/BYTES_PER_TEXEL - ctLastPixels; if( IsEqualized( pulLastMipMap, ctLastPixels)) td_ulFlags |= TEX_EQUALIZED; } // prepare dithering type td_ulInternalFormat = DetermineInternalFormat(this); tex_iDithering = Clamp( tex_iDithering, 0L, 10L); INDEX iDitherType = 0; if( !(td_ulFlags&TEX_STATIC) || !(td_ulFlags&TEX_CONSTANT)) { // only non-static-constant textures can be dithered extern INDEX AdjustDitheringType_OGL( GLenum eFormat, INDEX iDitheringType); if( eAPI==GAT_OGL) iDitherType = AdjustDitheringType_OGL( (GLenum)td_ulInternalFormat, tex_iDithering); #ifdef SE1_D3D extern INDEX AdjustDitheringType_D3D( D3DFORMAT eFormat, INDEX iDitheringType); if( eAPI==GAT_D3D) iDitherType = AdjustDitheringType_D3D( (D3DFORMAT)td_ulInternalFormat, tex_iDithering); #endif // SE1_D3D } // eventually dither texture if( !_bExport && iDitherType!=0) { td_ulFlags |= TEX_DITHERED; for( iFrame=0; iFrameGetDescription(); if( fnTex == td_ptdBaseTexture->GetName()) { ThrowF_t( TRANS("Texture \"%s\" has same name as its base texture."), (CTString&)fnTex); } } // write version INDEX iVersion = 4; outFile->WriteID_t("TVER"); *outFile << iVersion; // isolate required flags ULONG ulFlags = td_ulFlags & (TEX_ALPHACHANNEL|TEX_32BIT); BOOL bAlphaChannel = td_ulFlags&TEX_ALPHACHANNEL; // write chunk containing texture data outFile->WriteID_t( CChunkID("TDAT")); // write data describing texture *outFile << ulFlags; *outFile << td_mexWidth; *outFile << td_mexHeight; *outFile << td_ctFineMipLevels; *outFile << td_iFirstMipLevel; *outFile << td_ctFrames; // if global effect struct exists in texture, don't save frames if( td_ptegEffect==NULL) { // write chunk containing raw frames ASSERT( td_ctFrames>0); ASSERT( td_pulFrames!=NULL); outFile->WriteID_t( CChunkID("FRMS")); PIX pixFrSize = GetPixWidth()*GetPixHeight(); // eventually prepare temp buffer in case of frames without alpha channel UBYTE *pubTmp = NULL; if( !bAlphaChannel) pubTmp = (UBYTE*)AllocMemory( pixFrSize*3); // write frames without mip-maps (just write the largest one) for( INDEX iFr=0; iFrWrite_t( pulCurrentFrame, pixFrSize *4); } else { // write frame without alpha channel RemoveAlphaChannel( pulCurrentFrame, pubTmp, pixFrSize); outFile->Write_t( pubTmp, pixFrSize *3); } } // no need for temp buffer anymore if( pubTmp!=NULL) FreeMemory(pubTmp); } // if exists global effect struct in texture else { // write chunk containing effect data outFile->WriteID_t( CChunkID("FXDT")); // write effect class *outFile << td_ptegEffect->teg_ulEffectType; // write effect buffer dimensions *outFile << td_pixBufferWidth; *outFile << td_pixBufferHeight; // write count of effect sources *outFile << td_ptegEffect->teg_atesEffectSources.Count(); // write whole dynamic array of effect sources FOREACHINDYNAMICARRAY(td_ptegEffect->teg_atesEffectSources, CTextureEffectSource, itEffectSource) { // write type of effect source *outFile << itEffectSource->tes_ulEffectSourceType; // write structure holding effect source properties outFile->Write_t( &itEffectSource->tes_tespEffectSourceProperties, sizeof( struct TextureEffectSourceProperties)); INDEX ctEffectSourcePixels = itEffectSource->tes_atepPixels.Count(); // write count of effect pixels *outFile << ctEffectSourcePixels; // if there are any effect pixels if( ctEffectSourcePixels>0) { // write all effect pixels in one block outFile->Write_t( &itEffectSource->tes_atepPixels[0], sizeof(struct TextureEffectPixel)*ctEffectSourcePixels); } } // if effect buffers are valid if( td_pubBuffer1!=NULL && td_pubBuffer2!=NULL) { // write chunk containing effect buffers outFile->WriteID_t( CChunkID("FXB2")); ULONG ulSize = GetEffectBufferSize(this); // write effect buffers outFile->Write_t( td_pubBuffer1, ulSize); outFile->Write_t( td_pubBuffer2, ulSize); } } // write chunk containing texture animation data outFile->WriteID_t( CChunkID("ANIM")); // write corresponding animation(s) CAnimData::Write_t( outFile); // if this texture has base texture if( td_ptdBaseTexture != NULL) { // write chunk containing base texture file name outFile->WriteID_t( CChunkID("BAST")); // write file name of base texture *outFile << td_ptdBaseTexture->GetName(); } } // export finest mipmap of one texture's frame to imageinfo void CTextureData::Export_t( class CImageInfo &iiExportedImage, INDEX iFrame) { // check for right frame number and non-effect texture type ASSERT( iFrame=td_ctFrames) throw( TRANS("Texture frame that is to be exported doesn't exist.")); // reload without modifications _bExport = TRUE; Reload(); ASSERT( td_pulFrames!=NULL); // prepare miplevel and mipmap offset PIX pixWidth = GetPixWidth(); PIX pixHeight = GetPixHeight(); // export header to image info structure iiExportedImage.Clear(); iiExportedImage.ii_Width = pixWidth; iiExportedImage.ii_Height = pixHeight; iiExportedImage.ii_BitsPerPixel = (td_ulFlags&TEX_ALPHACHANNEL) ? 32 : 24; // prepare the texture for exporting (with or without alpha channel) ULONG *pulFrame = td_pulFrames + td_slFrameSize*iFrame/BYTES_PER_TEXEL; PIX pixMipSize = pixWidth*pixHeight; SLONG slMipSize = pixMipSize * iiExportedImage.ii_BitsPerPixel/8; iiExportedImage.ii_Picture = (UBYTE*)AllocMemory( slMipSize); // export frame if( td_ulFlags&TEX_ALPHACHANNEL) { memcpy( iiExportedImage.ii_Picture, pulFrame, slMipSize); } else { RemoveAlphaChannel( pulFrame, iiExportedImage.ii_Picture, pixMipSize); } // reload as it was _bExport = FALSE; Reload(); } // force texture to be re-loaded (if needed) in corresponding manner void CTextureData::Force( ULONG ulTexFlags) { ASSERT( td_ctFrames>0); const BOOL bReload = (td_pulFrames==NULL && (ulTexFlags&TEX_STATIC)) || ((td_ulFlags&TEX_DISPOSED) && (ulTexFlags&TEX_CONSTANT)) || ((td_ulFlags&TEX_SATURATED) && (ulTexFlags&TEX_KEEPCOLOR)); td_ulFlags |= ulTexFlags & (TEX_CONSTANT|TEX_STATIC|TEX_KEEPCOLOR); if( bReload) Reload(); } // set texture to be as current for accelerator and eventually upload it to accelerator's memory void CTextureData::SetAsCurrent( INDEX iFrameNo/*=0*/, BOOL bForceUpload/*=FALSE*/) { // check API const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI; #ifdef SE1_D3D ASSERT( eAPI==GAT_OGL || eAPI==GAT_D3D || eAPI==GAT_NONE); #else // SE1_D3D ASSERT( eAPI==GAT_OGL || eAPI==GAT_NONE); #endif // SE1_D3D ASSERT( iFrameNogl_fTextureLODBias; extern void UpdateLODBias( const FLOAT fLODBias); if( td_ulFlags&TEX_CONSTANT) { // non-adjustable textures don't tolerate positive LOD bias if( _fCurrentLODBias>0) UpdateLODBias(0); else if( _fCurrentLODBias>fWantedLODBias) UpdateLODBias(fWantedLODBias); } else if( td_ulFlags&TEX_EQUALIZED) { // equilized textures don't tolerate negative LOD bias if( _fCurrentLODBias<0) UpdateLODBias(0); else if( _fCurrentLODBiasgl_pixMaxTextureDimension, pixWidth, pixHeight); // check whether wanted mip level is beyond last mip-level iWantedMipLevel = ClampMipLevel( iWantedMipLevel); // default adjustment for mapping pixWidth >>= iWantedMipLevel-td_iFirstMipLevel; pixHeight >>= iWantedMipLevel-td_iFirstMipLevel; ASSERT( pixWidth>0 && pixHeight>0); // eventually adjust water effect texture size (if larger than base) if( td_ptegEffect->IsWater()) { INDEX iMipDiff = Min( FastLog2(td_ptdBaseTexture->GetPixWidth()) - FastLog2(pixWidth), FastLog2(td_ptdBaseTexture->GetPixHeight()) - FastLog2(pixHeight)); iWantedMipLevel = iMipDiff; if( iMipDiff<0) { pixWidth >>= (-iMipDiff); pixHeight >>= (-iMipDiff); iWantedMipLevel = 0; ASSERT( pixWidth>0 && pixHeight>0); } } // if current frame size differs from the previous one SLONG slFrameSize = GetMipmapOffset( 15, pixWidth, pixHeight) *BYTES_PER_TEXEL; if( td_pulFrames==NULL || td_slFrameSize!=slFrameSize) { // (re)allocate the frame buffer if( td_pulFrames!=NULL) FreeMemory( td_pulFrames); td_pulFrames = (ULONG*)AllocMemory( slFrameSize); td_slFrameSize = slFrameSize; bNoDiscard = FALSE; } // if not calculated for this tick (must be != to test for time rewinding) if( td_ptegEffect->teg_updTexture.LastUpdateTime() != _pTimer->CurrentTick()) { // discard eventual cached frame and calculate new frame MarkChanged(); td_ptegEffect->Animate(); bNeedUpload = TRUE; // make sure that effect and base textures are static Force(TEX_STATIC); td_ptdBaseTexture->Force(TEX_STATIC); // copy some flags from base texture to effect texture td_ulFlags |= td_ptdBaseTexture->td_ulFlags & (TEX_ALPHACHANNEL|TEX_TRANSPARENT|TEX_GRAY); // render effect texture td_ptegEffect->Render( iWantedMipLevel, pixWidth, pixHeight); // determine internal format ULONG ulNewFormat; if( td_ulFlags&TEX_GRAY) { if( td_ulFlags&TEX_ALPHACHANNEL) ulNewFormat = TS.ts_tfLA8; else ulNewFormat = TS.ts_tfL8; } else { if( td_ulFlags&TEX_TRANSPARENT) ulNewFormat = TS.ts_tfRGB5A1; else if( td_ulFlags&TEX_ALPHACHANNEL) ulNewFormat = TS.ts_tfRGBA4; else ulNewFormat = TS.ts_tfRGB5; } // effect texture can be in 32-bit quality only if base texture hasn't been dithered extern INDEX tex_bFineEffect; if( tex_bFineEffect && (td_ptdBaseTexture->td_ulFlags&TEX_DITHERED)) { ulNewFormat = PromoteTo32bitFormat(ulNewFormat); } // internal format changed? - must discard! if( td_ulInternalFormat!=ulNewFormat) { td_ulInternalFormat = ulNewFormat; bNoDiscard = FALSE; } } // effect texture cannot have probing bUseProbe = FALSE; } // prepare effect cvars extern INDEX tex_bDynamicMipmaps; extern INDEX tex_iEffectFiltering; if( tex_bDynamicMipmaps) tex_bDynamicMipmaps = 1; tex_iEffectFiltering = Clamp( tex_iEffectFiltering, -6L, +6L); // determine whether texture has single mipmap if( gap_bAllowSingleMipmap) { // effect textures are treated differently if( td_ptegEffect!=NULL) td_tpLocal.tp_bSingleMipmap = !tex_bDynamicMipmaps; else td_tpLocal.tp_bSingleMipmap = (td_ctFineMipLevels<2); } else { // single mipmap is not allowed td_tpLocal.tp_bSingleMipmap = FALSE; } // effect texture might need dynamic mipmaps creation if( bNeedUpload && td_ptegEffect!=NULL) { _sfStats.StartTimer(CStatForm::STI_EFFECTRENDER); const INDEX iTexFilter = td_ptegEffect->IsWater() ? NONE : tex_iEffectFiltering; // don't filter water textures if( td_tpLocal.tp_bSingleMipmap) { // no mipmaps? if( iTexFilter!=NONE) FilterBitmap( iTexFilter, td_pulFrames, td_pulFrames, pixWidth, pixHeight); } else { // mipmaps! const INDEX ctFine = tex_bDynamicMipmaps ? 15 : 0; // whether they're fine or coarse still depends on cvar MakeMipmaps( ctFine, td_pulFrames, pixWidth,pixHeight, iTexFilter); } // done with effect _sfStats.StopTimer(CStatForm::STI_EFFECTRENDER); } // if not already generated, generate bind number(s) and force upload const PIX pixTextureSize = pixWidth*pixHeight; if( td_ulObject==NONE) { // check whether frames are present ASSERT( td_pulFrames!=NULL && td_pulFrames[0]!=0xDEADBEEF); if( td_ctFrames>1) { // animation textures td_pulObjects = (ULONG*)AllocMemory( td_ctFrames *sizeof(td_ulProbeObject)); for( INDEX i=0; i16*16) gfxGenerateTexture( td_ulProbeObject); // must do initial uploading bNeedUpload = TRUE; bNoDiscard = FALSE; } // constant textures cannot be probed either if( td_ulFlags&TEX_CONSTANT) gfxDeleteTexture(td_ulProbeObject); if( td_ulProbeObject==NONE) bUseProbe = FALSE; // update statistics if not updated already for this frame if( td_iRenderFrame != _pGfx->gl_iFrameNumber) { td_iRenderFrame = _pGfx->gl_iFrameNumber; // determine size and update SLONG slBytes = pixWidth*pixHeight * gfxGetFormatPixRatio(td_ulInternalFormat); if( !td_tpLocal.tp_bSingleMipmap) slBytes = slBytes *4/3; _sfStats.IncrementCounter( CStatForm::SCI_TEXTUREBINDS, 1); _sfStats.IncrementCounter( CStatForm::SCI_TEXTUREBINDBYTES, slBytes); } // if needs to be uploaded if( bNeedUpload) { // check whether frames are present ASSERT( td_pulFrames!=NULL && td_pulFrames[0]!=0xDEADBEEF); // must discard uploaded texture if single mipmap flag has been changed const BOOL bLastSingleMipmap = td_ulFlags & TEX_SINGLEMIPMAP; bNoDiscard = (bNoDiscard && bLastSingleMipmap==td_tpLocal.tp_bSingleMipmap); // update flag if( td_tpLocal.tp_bSingleMipmap) td_ulFlags |= TEX_SINGLEMIPMAP; else td_ulFlags &= ~TEX_SINGLEMIPMAP; // upload all texture frames ASSERT( td_ulInternalFormat!=TEXFMT_NONE); if( td_ctFrames>1) { // animation textures for( INDEX iFr=0; iFr1 && !td_tpLocal.IsEqual(_tpGlobal[0])) { // must reset local texture parameters for each frame of animated texture for( INDEX iFr=0; iFr1) ulTexObject = ((ULONG*)td_ulObject)[iFrameNo]; // animation if( bUseProbe) { // set probe if burst value doesn't allow real texture if( _pGfx->gl_slAllowedUploadBurst<0) { CTexParams tpTmp = td_tpLocal; ASSERT( td_ulProbeObject!=NONE); gfxSetTexture( td_ulProbeObject, tpTmp); //extern INDEX _ctProbeTexs; //_ctProbeTexs++; //CPrintF( "Probed!\n"); return; } // reduce allowed burst value _pGfx->gl_slAllowedUploadBurst -= pixWidth*pixHeight *4; // assume 32-bit textures (don't ask driver!) } // set real texture and mark that this texture has been drawn gfxSetTexture( ulTexObject, td_tpLocal); MarkDrawn(); // debug check ASSERT( td_ulObject!=NONE); } // unbind texture from accelerator's memory void CTextureData::Unbind(void) { // reset mark td_tvLastDrawn = 0I64; // only if bound if( td_ulObject==NONE) { ASSERT( td_ulProbeObject==NONE); return; } // free frame number(s) if( td_ctFrames>1) { // animation for( INDEX iFrame=0; iFrameRelease( td_ptdBaseTexture); td_ptdBaseTexture = NULL; } // free global effect data if it exists if( td_ptegEffect != NULL) { delete td_ptegEffect; td_ptegEffect = NULL; } // reset texture parameters td_tpLocal.Clear(); // clear animation CAnimData::Clear(); // reset variables (but keep some flags) td_ctFrames = 0; td_mexWidth = 0; td_mexHeight = 0; td_tvLastDrawn = 0I64; td_iFirstMipLevel = 0; td_ctFineMipLevels = 0; td_pixBufferWidth = 0; td_pixBufferHeight = 0; td_ulInternalFormat = TEXFMT_NONE; td_iRenderFrame = -1; td_ulFlags &= TEX_CONSTANT|TEX_STATIC|TEX_KEEPCOLOR; } /******************************************* * Implementation of CTextureObject routines */ CTextureObject::CTextureObject(void) { } // copy from another object of same class void CTextureObject::Copy(CTextureObject &toOther) { CAnimObject::Copy(toOther); } void CTextureObject::Read_t( CTStream *istrFile) // throw char * { CAnimObject::Read_t( istrFile); } void CTextureObject::Write_t( CTStream *ostrFile) // throw char * { CAnimObject::Write_t( ostrFile); } MEX CTextureObject::GetWidth(void) const { return ((CTextureData*)ao_AnimData)->GetWidth(); }; MEX CTextureObject::GetHeight(void) const { return ((CTextureData*)ao_AnimData)->GetHeight(); }; ULONG CTextureObject::GetFlags(void) const { return ((CTextureData*)ao_AnimData)->GetFlags(); }; /**************************************** * Implementation of independent routines */ #define EQUAL_SUB_STR( str) (strnicmp( ld_line, str, strlen(str))==0) void ProcessScript_t( const CTFileName &inFileName) // throw char * { CTFileStream File; char ld_line[128]; char err_str[256]; FLOAT fTextureWidthMeters = 2.0f; INDEX TexMipmaps = MAX_MEX_LOG2; CTextureData tex; CListHead FrameNamesList; INDEX NoOfDataFound = 0; BOOL bForce32bit = FALSE; File.Open_t( inFileName, CTStream::OM_READ); // open script file for text reading FOREVER { do { File.GetLine_t( ld_line, 128); } while( (strlen( ld_line)==0) || (ld_line[0]==';')); _strupr( ld_line); // specified width of texture if( EQUAL_SUB_STR( "TEXTURE_WIDTH")) { sscanf( ld_line, "TEXTURE_WIDTH %g", &fTextureWidthMeters); NoOfDataFound ++; } // how many mip-map levels will texture have else if( EQUAL_SUB_STR( "TEXTURE_MIPMAPS")) { sscanf( ld_line, "TEXTURE_MIPMAPS %d", &TexMipmaps); } // should texture be forced to keep 32-bit quality even that 16-bit textures are set else if( EQUAL_SUB_STR( "TEXTURE_32BIT")) { bForce32bit = TRUE; } // Key-word "ANIM_START" starts loading of Animation Data object else if( EQUAL_SUB_STR( "ANIM_START")) { tex.LoadFromScript_t( &File, &FrameNamesList); NoOfDataFound ++; } // Key-word "END" ends infinite loop and script loading is over else if( EQUAL_SUB_STR( "END")) break; // if none of known key-words isn't recognised, throw error else { sprintf( err_str, TRANS("Unidentified key-word found (line: \"%s\") or unexpected end of file reached."), ld_line); throw( err_str); } } if( NoOfDataFound != 2) throw( TRANS("Required key-word(s) has not been specified in script file:\nTEXTURE_WIDTH and/or ANIM_START")); // Now we will create texture file form read script data CImageInfo inPic; CTFileName outFileName; CTFileStream outFile; // load first picture CFileNameNode *pFirstFNN = LIST_HEAD( FrameNamesList, CFileNameNode, cfnn_Node); inPic.LoadAnyGfxFormat_t( CTString(pFirstFNN->cfnn_FileName)); // create texture with one frame tex.Create_t( &inPic, MEX_METERS(fTextureWidthMeters), TexMipmaps, bForce32bit); inPic.Clear(); // process rest of the frames in animation (if any) INDEX i=0; FOREACHINLIST( CFileNameNode, cfnn_Node, FrameNamesList, it1) { if( i != 0) { // we have to skip first picture since it has already been done inPic.LoadAnyGfxFormat_t( CTString(it1->cfnn_FileName)); // add picture as next frame in texture tex.AddFrame_t( &inPic); inPic.Clear(); } i++; } // save texture outFileName = inFileName.FileDir() + inFileName.FileName() + ".TEX"; tex.Save_t( outFileName); // clear list FORDELETELIST( CFileNameNode, cfnn_Node, FrameNamesList, itDel) delete &itDel.Current(); } void CreateTexture_t( const CTFileName &inFileName, const CTFileName &outFileName, MEX inMex, INDEX inMipmaps, BOOL bForce32bit) { if( inFileName.FileExt() == ".SCR") { // input is a script file ProcessScript_t( inFileName); } else { // input is a picture file (PCX or TGA) CAnimData anim; CTextureData tex; CImageInfo inPic; // mex must be specified and valid if( (inMex <= 0)) throw( TRANS("Invalid or unspecified mexel units.")); // load picture inPic.LoadAnyGfxFormat_t( inFileName); // create texture tex.Create_t( &inPic, inMex, inMipmaps, bForce32bit); // no more need for picture - get out! inPic.Clear(); // save texture to file tex.Save_t( outFileName); } } void CreateTexture_t( const CTFileName &inFileName, MEX inMex, INDEX inMipmaps, BOOL bForce32bit) { CTFileName outFileName = inFileName.FileDir() + inFileName.FileName() + ".TEX"; CreateTexture_t( inFileName, outFileName, inMex, inMipmaps, bForce32bit); } // reference counting (override from CAnimData) void CTextureData::RemReference_internal(void) { _pTextureStock->Release(this); } // obtain texture and set it for this object void CTextureObject::SetData_t(const CTFileName &fnmTexture) // throw char * { // if the filename is empty if (fnmTexture=="") { // release current texture SetData(NULL); // if the filename is not empty } else { // obtain it (adds one reference) CTextureData *ptd = _pTextureStock->Obtain_t(fnmTexture); // set it as data (adds one more reference, and remove old reference) SetData(ptd); // release it (removes one reference) _pTextureStock->Release(ptd); // total reference count +1+1-1 = +1 for new data -1 for old data } } // get filename of texture or empty string if no texture const CTFileName &CTextureObject::GetName(void) { static const CTFileName strDummy(CTString("")); // if there is some texture if (ao_AnimData!=NULL) { // get texture filename return ao_AnimData->GetName(); // if there is no texture } else { // get empty string return strDummy; } } // check if this kind of objects is auto-freed BOOL CTextureData::IsAutoFreed(void) { // cannot be return FALSE; } // get amount of memory used by this object SLONG CTextureData::GetUsedMemory(void) { // readout texture object ULONG ulTexObject = td_ulObject; if( td_ctFrames>1) ulTexObject = td_pulObjects[0]; // add structure size and anim block size SLONG slUsed = sizeof(*this) + CAnimData::GetUsedMemory()-sizeof(CAnimData); // add effect buffers and static memory if exist if( td_pubBuffer1!=NULL) slUsed += 2* GetEffectBufferSize(this); // two buffers if( (td_ulFlags&TEX_STATIC) && td_pulFrames!=NULL) { slUsed += td_ctFrames*td_slFrameSize; } // add eventual uploaded size and finito const SLONG slUploadSize = gfxGetTextureSize( ulTexObject, !td_tpLocal.tp_bSingleMipmap); return slUsed + td_ctFrames*slUploadSize; } // get texel from texture's largest mip-map COLOR CTextureData::GetTexel( MEX mexU, MEX mexV) { // if the texture is not static if (!(td_ulFlags&TEX_STATIC) && !(td_ulFlags&TEX_CONSTANT)) { // print warning ASSERTALWAYS("GetTexel: Texture needs to be static and constant."); CPrintF("GetTexel: '%s' was not static and/or constant!\n", (const char*)GetName()); } // make sure that the texture is static Force( TEX_STATIC|TEX_CONSTANT); // convert dimensions to pixels PIX pixU = mexU >>td_iFirstMipLevel; PIX pixV = mexV >>td_iFirstMipLevel; pixU &= GetPixWidth()-1; pixV &= GetPixHeight()-1; ASSERT(pixU>=0 && pixU=0 && pixV1 || aiInfo.ai_NumberOfFrames>1) { strAnims.PrintF(" %d(%d)anim", ad_NumberOfAnims, aiInfo.ai_NumberOfFrames); } // return combined string return str+strFlags+strAnims; }