Serious-Engine/Sources/Engine/Graphics/Texture.cpp

1845 lines
64 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* 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. */
2016-03-11 14:57:17 +01:00
#include "stdh.h"
#include <Engine/Graphics/Texture.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/Timer.h>
#include <Engine/Base/Console.h>
#include <Engine/Math/Functions.h>
#include <Engine/Graphics/GfxLibrary.h>
#include <Engine/Graphics/ImageInfo.h>
#include <Engine/Graphics/TextureEffects.h>
#include <Engine/Templates/DynamicArray.h>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Templates/Stock_CtextureData.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Base/Statistics_internal.h>
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<<iSizeULog2)==pixSizeU && (1UL<<iSizeVLog2)==pixSizeV);
// dimension in mexels must not be smaller than the one in pixels
ASSERT( pixSizeU<=mexWanted);
// determine mip index from mex size
td_iFirstMipLevel = FastLog2( mexWanted/pixSizeU);
// initiate proper flags
td_ulFlags = NONE;
if( pII->ii_BitsPerPixel==32) td_ulFlags |= TEX_ALPHACHANNEL;
if( bForce32bit) td_ulFlags |= TEX_32BIT;
// initialize general TextureData members
td_ctFrames = 0;
td_mexWidth = pixSizeU<<td_iFirstMipLevel;
td_mexHeight = pixSizeV<<td_iFirstMipLevel;
// create all mip levels (either bilinear or downsampled)
INDEX ctMipLevels = GetNoOfMipmaps( pixSizeU, pixSizeV);
td_ctFineMipLevels = Min( ctFineMips, ctMipLevels);
// get frame size (includes only one mip-map)
td_slFrameSize = GetMipmapOffset( 15, pixSizeU, pixSizeV) *BYTES_PER_TEXEL;
// allocate small ammount of memory just for Realloc sake
td_pulFrames = (ULONG*)AllocMemory(16);
AddFrame_t( pII);
}
// returns dimension of effect buffers and size in bytes (of one, not both)
static ULONG GetEffectBufferSize( CTextureData *pTD)
{
ASSERT( pTD->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_iFirstMipLevel;
td_mexHeight = pixHeight<<td_iFirstMipLevel;
td_ctFrames = 1;
td_pulFrames = NULL;
// remember base texture
td_ptdBaseTexture = ptdBaseTexture;
// allocate texture effect global
td_ptegEffect = new CTextureEffectGlobal( this, ulGlobalEffect);
// allocate and reset effect buffers
InitEffectBufferDimensions(this);
AllocEffectBuffers(this);
}
// this promotes 16bit internal format to corresponding 32bit
static ULONG PromoteTo32bitFormat( ULONG ulFormat)
{
if( ulFormat==TS.ts_tfRGB5) return TS.ts_tfRGB8;
if( ulFormat==TS.ts_tfRGBA4 || ulFormat==TS.ts_tfRGB5A1) return TS.ts_tfRGBA8;
return ulFormat;
}
// returns format in what texture will be uploaded (regarding console vars)
static ULONG DetermineInternalFormat( CTextureData *pTD)
{
// cache some vars
extern INDEX gap_bAllowGrayTextures;
BOOL bGrayTexture = gap_bAllowGrayTextures && (pTD->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; iFr<pTD->td_ctFrames; iFr++)
{ // get addresses of current frames (new and old)
PIX pixFrameOffset = iFr * pixFrameSize;
// for each pixel
for( INDEX iPix=0; iPix<pixMipSize; iPix++)
{ // read 16-bit pixel
UWORD uwPix = puwFramesOld[pixFrameOffset+iPix];
// unpack it
if( bHasAlphaChannel) {
// with alpha channel
r = (uwPix & 0xF000) >>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; iFr<pTD->td_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<pixMipSize; iPix++) {
col = ByteSwap(pulMipmap[iPix]);
ulR += (col&CT_RMASK)>>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 && ulR<ulHiEdge &&
ulG>ulLoEdge && ulG<ulHiEdge &&
ulB>ulLoEdge && ulB<ulHiEdge) bEqulized = TRUE;
return bEqulized;
}
// test mipmap if it can be transparent
#define TRANS_TRESHOLD 7
static BOOL IsTransparent( ULONG *pulMipmap, INDEX pixMipSize)
{
COLOR col;
ULONG ulA;
// determine transparency
for( INDEX iPix=0; iPix<pixMipSize; iPix++) {
col = ByteSwap(pulMipmap[iPix]);
ulA = (col&CT_AMASK)>>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; iPix<pixMipSize; iPix++) {
COLOR col = ByteSwap(pulMipmap[iPix]);
if( !IsGray(col)) return FALSE; // colored
} // grayscaled
return TRUE;
}
// reads 32/24-bit texture from file and eventually converts it to 8-bit pixel format
void CTextureData::Read_t( CTStream *inFile)
{
//ASSERT( inFile->GetDescription() != "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; iFr<td_ctFrames; iFr++)
{ // loop thru frames
ULONG *pulCurrentFrame = td_pulFrames + (iFr * td_slFrameSize/BYTES_PER_TEXEL);
if( bAlphaChannel) {
// read texture with alpha channel from file
inFile->Read_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<td_ctFrames; iFrame++) {
ULONG *pulCurrentFrame = td_pulFrames + iFrame*pixFrameSize;
if( !IsTransparent( pulCurrentFrame, pixTexSize)) {
// no need to test other frames if one found that isn't gray
td_ulFlags &= ~TEX_TRANSPARENT;
break;
}
}
}
// generate texture mip-maps for each frame (in version 4 they're no longer kept in file)
// and eventually adjust texture saturation, do filtering and/or dithering
tex_iFiltering = Clamp( tex_iFiltering, -6L, +6L);
INDEX iTexFilter = tex_iFiltering;
if( _bExport || (td_ulFlags&TEX_CONSTANT)) iTexFilter = 0; // don't filter constants and textures for exporting
if( iTexFilter) td_ulFlags |= TEX_FILTERED;
// eventually saturate texture
if( !_bExport && !(td_ulFlags&TEX_KEEPCOLOR) && (_slTexSaturation!=256 || _slTexHueShift!=0)) {
td_ulFlags |= TEX_SATURATED;
for( iFrame=0; iFrame<td_ctFrames; iFrame++) {
ULONG *pulCurrentFrame = td_pulFrames + iFrame*pixFrameSize;
AdjustBitmapColor( pulCurrentFrame, pulCurrentFrame, pixWidth, pixHeight, _slTexHueShift, _slTexSaturation);
}
}
// make mipmaps
for( iFrame=0; iFrame<td_ctFrames; iFrame++) {
ULONG *pulCurrentFrame = td_pulFrames + iFrame*pixFrameSize;
MakeMipmaps( td_ctFineMipLevels, pulCurrentFrame, pixWidth,pixHeight, iTexFilter);
}
// remove mipmaps from texture that are not needed and update texture size
if( !_bExport) RemoveOversizedMipmaps(this);
// do some additional mipmap adjustments if needed
pixWidth = GetPixWidth();
pixHeight = GetPixHeight();
pixTexSize = pixWidth*pixHeight;
pixFrameSize = td_slFrameSize>>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; iFrame<td_ctFrames; iFrame++) {
ULONG *pulCurrentFrame = td_pulFrames + iFrame*pixFrameSize;
ColorizeMipmaps( 1, pulCurrentFrame, pixWidth, pixHeight);
}
} // if not colorized, test if texture is gray
else {
td_ulFlags |= TEX_GRAY;
for( iFrame=0; iFrame<td_ctFrames; iFrame++) {
ULONG *pulCurrentFrame = td_pulFrames + iFrame*pixFrameSize;
if( !IsGray( pulCurrentFrame, pixTexSize)) {
// no need to test other frames if one found that isn't gray
td_ulFlags &= ~TEX_GRAY;
break;
}
}
} // test texture for equality (i.e. determine if texture could be discardable in shade mode when in lowest mipmap)
if( td_ctFrames<2 && (!gap_bAllowSingleMipmap || td_ctFineMipLevels>1))
{ // 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; iFrame<td_ctFrames; iFrame++) {
ULONG *pulCurrentFrame = td_pulFrames + iFrame*pixFrameSize;
DitherMipmaps( iDitherType, pulCurrentFrame, pulCurrentFrame, pixWidth, pixHeight);
}
}
// upload texture if not static and API is active
// (or, in the other hand, better not - this could cause reloading due to force() after obtain())
if( !_bExport && bHasContext && !(td_ulFlags&TEX_STATIC)) SetAsCurrent();
}
// writes texutre to file
void CTextureData::Write_t( CTStream *outFile) // throw char *
{
// cannot write textures that have been mangled somehow
_bExport = FALSE;
if( td_ptegEffect==NULL && IsModified()) throw( TRANS("Cannot write texture that has modified frames."));
// must not have base texture with same name
if( td_ptdBaseTexture != NULL) {
CTFileName fnTex = outFile->GetDescription();
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; iFr<td_ctFrames; iFr++ )
{ // determine write params
ULONG *pulCurrentFrame = td_pulFrames + (iFr * td_slFrameSize/BYTES_PER_TEXEL);
if( bAlphaChannel) { // write frame with alpha channel
outFile->Write_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 && td_ptegEffect==NULL);
if( 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( iFrameNo<td_ctFrames);
BOOL bNeedUpload = bForceUpload;
BOOL bNoDiscard = TRUE;
PIX pixWidth = GetPixWidth();
PIX pixHeight = GetPixHeight();
// eventually re-adjust LOD bias
extern FLOAT _fCurrentLODBias;
const FLOAT fWantedLODBias = _pGfx->gl_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( _fCurrentLODBias<fWantedLODBias) UpdateLODBias(fWantedLODBias);
}
else if( _fCurrentLODBias != fWantedLODBias) {
// all other textures must take LOD bias into account
UpdateLODBias( fWantedLODBias);
}
// determine probing
extern BOOL ProbeMode( CTimerValue tvLast);
BOOL bUseProbe = ProbeMode(td_tvLastDrawn);
// if we have an effect texture
if( td_ptegEffect!=NULL)
{
ASSERT( iFrameNo==0); // effect texture must have only one frame
// get max allowed effect texture dimension
PIX pixClampAreaSize = 1L<<16L;
tex_iEffectSize = Clamp( tex_iEffectSize, 4L, 8L);
if( !(td_ulFlags&TEX_CONSTANT)) pixClampAreaSize = 1L<<(tex_iEffectSize*2);
INDEX iWantedMipLevel = td_iFirstMipLevel
+ ClampTextureSize( pixClampAreaSize, _pGfx->gl_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; i<td_ctFrames; i++) gfxGenerateTexture( td_pulObjects[i]);
} else {
// single-frame textures
gfxGenerateTexture( td_ulObject);
}
// generate probe texture (if needed)
ASSERT( td_ulProbeObject==NONE);
if( td_ptegEffect==NULL && pixTextureSize>16*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; iFr<td_ctFrames; iFr++)
{ // determine frame offset and upload texture frame
ULONG *pulCurrentFrame = td_pulFrames + (iFr * td_slFrameSize/BYTES_PER_TEXEL);
gfxSetTexture( ((ULONG*)td_ulObject)[iFr], td_tpLocal);
gfxUploadTexture( pulCurrentFrame, pixWidth, pixHeight, td_ulInternalFormat, bNoDiscard);
}
} else {
// single-frame textures
gfxSetTexture( td_ulObject, td_tpLocal);
gfxUploadTexture( td_pulFrames, pixWidth, pixHeight, td_ulInternalFormat, bNoDiscard);
}
// upload probe texture if exist
if( td_ulProbeObject!=NONE) {
PIX pixProbeWidth = pixWidth;
PIX pixProbeHeight = pixHeight;
ULONG *pulProbeFrame = td_pulFrames;
GetMipmapOfSize( 16*16, pulProbeFrame, pixProbeWidth, pixProbeHeight);
gfxSetTexture( td_ulProbeObject, td_tpLocal);
gfxUploadTexture( pulProbeFrame, pixProbeWidth, pixProbeHeight, TS.ts_tfRGBA4, FALSE);
}
// clear local texture parameters because we need to correct later texture setting
td_tpLocal.Clear();
// free frames' memory if allowed
if( !(td_ulFlags&TEX_STATIC)) {
FreeMemory( td_pulFrames);
td_pulFrames = NULL;
}
// done uploading
ASSERT( td_ulObject!=NONE);
return;
}
// do special case for animated textures when parameters re-initialization is required
if( td_ctFrames>1 && !td_tpLocal.IsEqual(_tpGlobal[0])) {
// must reset local texture parameters for each frame of animated texture
for( INDEX iFr=0; iFr<td_ctFrames; iFr++) {
td_tpLocal.Clear();
gfxSetTexture( ((ULONG*)td_ulObject)[iFr], td_tpLocal);
}
}
// set corresponding probe or texture frame as current
ULONG ulTexObject = td_ulObject; // single-frame
if( td_ctFrames>1) 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; iFrame<td_ctFrames; iFrame++) gfxDeleteTexture( td_pulObjects[iFrame]);
FreeMemory( td_pulObjects);
td_pulObjects = NULL;
} else { // single-frame
gfxDeleteTexture(td_ulObject);
}
// delete probe texture, too
gfxDeleteTexture(td_ulProbeObject);
}
// free memory allocated for texture (if any)
void CTextureData::Clear(void)
{
// unbind texture from OpenGL or Direct3D memory
Unbind();
// free allocated memory and reset pointer
if( td_pulFrames!=NULL && td_slFrameSize!=0) {
FreeMemory( td_pulFrames);
td_pulFrames = NULL;
td_slFrameSize = 0;
}
// free memory allocated for texture effect buffers
FreeEffectBuffers(this);
// release base texture if it exists
if( td_ptdBaseTexture != NULL) {
_pTextureStock->Release( 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<GetPixWidth());
ASSERT(pixV>=0 && pixV<GetPixHeight());
// read texel from texture
return ByteSwap( *(ULONG*)(td_pulFrames + pixV*GetPixWidth() + pixU));
}
// copy (and eventually convert to floats) one row from texture to an array (iChannel is 1=R,2=G,3=B,4=A)
void CTextureData::FetchRow( PIX pixRow, void *pvDst, INDEX iChannel/*=4*/, BOOL bConvertToFloat/*=TRUE*/)
{
// if the texture is not static
if (!(td_ulFlags&TEX_STATIC) && !(td_ulFlags&TEX_CONSTANT)) {
// print warning
ASSERTALWAYS("FetchRow: Texture needs to be static and constant.");
CPrintF("FetchRow: '%s' was not static and/or constant!\n", (const char*)GetName());
}
// workaround: make sure that the texture is static
Force( TEX_STATIC|TEX_CONSTANT);
// determine row offset and loop thru row pixels
ULONG *pulSrc = td_pulFrames + pixRow*GetPixWidth();
for( INDEX iCol=0; iCol<GetPixWidth(); iCol++) {
const UBYTE ubPix = ((UBYTE*)pulSrc)[iCol*4 +iChannel-1];
if( bConvertToFloat) ((FLOAT*)pvDst)[iCol] = NormByteToFloat(ubPix);
else ((UBYTE*)pvDst)[iCol] = ubPix;
}
}
// get pointer to one row of texture
ULONG *CTextureData::GetRowPointer( PIX pixRow)
{
// if the texture is not static
if (!(td_ulFlags&TEX_STATIC) && !(td_ulFlags&TEX_CONSTANT)) {
// print warning
ASSERTALWAYS("GetRowPointer: Texture needs to be static and constant.");
CPrintF("GetRowPointer: '%s' was not static and/or constant!\n", (const char*)GetName());
}
// workaround: make sure that the texture is static
Force( TEX_STATIC|TEX_CONSTANT);
return (td_pulFrames + pixRow*GetPixWidth());
}
// get string description of texture size, mips and parameters
CTString CTextureData::GetDescription(void)
{
// get all parameters
MEX mexSizeU = GetWidth();
MEX mexSizeV = GetHeight();
PIX pixSizeU = GetPixWidth();
MEX pixSizeV = GetPixHeight();
FLOAT fSizeU = METERS_MEX( mexSizeU);
FLOAT fSizeV = METERS_MEX( mexSizeV);
INDEX ctFineMips = GetNoOfFineMips();
INDEX ctTotalMips = GetNoOfMips();
// print size and mips
CTString strSizeM;
if (fSizeU==int(fSizeU) && fSizeV==int(fSizeV)) {
strSizeM.PrintF("%dx%dm", int(fSizeU), int(fSizeV));
} else {
strSizeM.PrintF("%.2fx%.2fm", fSizeU, fSizeV);
}
CTString str;
str.PrintF( "%s(%dx%d) %d/%d", strSizeM, pixSizeU, pixSizeV, ctFineMips, ctTotalMips);
// print flags
CTString strFlags = "";
if (td_ulFlags&TEX_ALPHACHANNEL) strFlags+="A";
if (td_ulFlags&TEX_EQUALIZED) strFlags+="E";
if (td_ulFlags&TEX_32BIT) strFlags+="H";
if (td_ulFlags&TEX_WASOLD) strFlags+="!";
// if there are any flags, add blank before flags
if( strFlags!="") str=CTString(" ")+str;
CAnimInfo aiInfo;
GetAnimInfo( 0, aiInfo);
CTString strAnims = "";
if (ad_NumberOfAnims>1 || aiInfo.ai_NumberOfFrames>1) {
strAnims.PrintF(" %d(%d)anim", ad_NumberOfAnims, aiInfo.ai_NumberOfFrames);
}
// return combined string
return str+strFlags+strAnims;
}