Blame | Last modification | View Log | RSS feed
// TextureFile.cpp: implementation of the CTextureFile class.
//
//////////////////////////////////////////////////////////////////////
#include "TextureFile.h"
#include <stdlib.h>
#include <stdio.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CTextureFile::CTextureFile( bool editor )
{
m_data = NULL;
m_numData = 0;
m_height = 0;
m_width = 0;
m_frames.x = 0;
m_frames.y = 0;
m_frames.rows = 1;
m_frames.cols = 1;
m_alpha = false;
m_alphamap[0] = 0;
m_alphamap[1] = 0;
m_alphamap[2] = 0;
m_animNum = 0;
m_anim = NULL;
m_collisionMap = false;
m_collisionData = NULL;
m_numframes = 0;
m_framebounds = NULL;
m_editor = editor;
}
CTextureFile::~CTextureFile()
{
if ( m_data )
delete[] m_data;
if ( m_collisionData )
delete[] m_collisionData;
if ( m_anim )
{
for ( int i = 0; i < m_animNum; i++ )
{
if ( m_anim[i].frames )
delete[] m_anim[i].frames;
if ( m_anim[i].name )
delete[] m_anim[i].name;
}
if ( m_editor )
free ( m_anim );
else
delete[] m_anim;
}
//if ( m_framebounds )
// delete [] m_framebounds;
}
STextureData *CTextureFile::CreateData(int width, int height, STextureSize *size )
{
if ( (!height) || (!width) )
return NULL;
size->height = height;
size->width = width;
size->num = (height * width);
STextureData *data = new STextureData[size->num];
for ( int i = 0; i < size->num; i++ )
{
data[i].r = 0;
data[i].g = 0;
data[i].b = 0;
data[i].alpha = 255;
}
return data;
}
int CTextureFile::LoadTextureTGA(const char *filename )
{
STextureSize *size = new STextureSize;
int error;
m_data = LoadTGA ( filename, size, &error );
if ( error == TEXTURELOAD_OK )
{
m_height = size->height;
m_width = size->width;
m_numData = size->num;
}
else
{
delete [] m_data;
m_data = NULL;
}
if ( m_collisionData )
delete [] m_collisionData;
m_collisionMap = false;
delete [] size;
return error;
}
int CTextureFile::LoadCollisionTGA(const char *filename )
{
STextureSize *size = new STextureSize;
int error;
STextureData *data = LoadTGA ( filename, size, &error );
if ( error == TEXTURELOAD_OK )
{
if ( (size->height != m_height) || (size->width != m_width) )
error = TEXTURELOAD_SIZEMISMATCH;
else
{
if ( m_collisionData )
delete [] m_collisionData;
m_collisionData = new bool[size->num];
for ( int i = 0; i < size->num; i++ )
m_collisionData[i] = (data[i].alpha) ? false : true;
m_collisionMap = true;
}
}
delete [] data;
delete [] size;
return error;
}
bool CTextureFile::SaveTGA ( const char *filename )
{
FILE *fileID = NULL;
unsigned char header[18];
unsigned char *imagedata;
int i;
int num;
#ifdef CY_USESECURE
if ( fopen_s ( &fileID, filename, "wb" ) )
return false;
#else
fileID = fopen ( filename, "wb" );
if ( !fileID )
return false;
#endif
for ( i = 0; i < 18; i++ ) //initilise header to all 0's
header[i] = 0;
// setup header
header[2] = 2;
header[12] = m_width % 256;
header[13] = (int) (m_width / 256);
header[14] = m_height % 256;
header[15] = (int) (m_height / 256);
header[16] = 32; // 32 bit
int bitSize = header[16] / 8;
fwrite(header, sizeof (unsigned char), 22, fileID); // write header to the file
imagedata = (unsigned char *)malloc(sizeof(unsigned char) * (m_width * m_height * bitSize));
num = 0;
// write colour data
for ( i = 0; i < (m_width * m_height); i++)
{
imagedata[num] = m_data[i].b;
imagedata[num + 1] = m_data[i].g;
imagedata[num + 2] = m_data[i].r;
if ( bitSize == 4 )
imagedata[num + 3] = (unsigned char) m_data[i].alpha;
num += bitSize;
}
fwrite ( imagedata, sizeof (unsigned char), m_width * m_height * bitSize, fileID );
fclose ( fileID );
return true;
}
STextureData *CTextureFile::LoadTGA(const char *filename, STextureSize *tsize, int *error )
{
unsigned char type[4];
unsigned char info[7];
unsigned char *imageData = NULL;
int imageWidth, imageHeight;
int imageBits, size;
FILE *fileID;
// open the filename
#ifdef CY_USESECURE
if ( fopen_s ( &fileID, filename, "r+bt") )
{
*error = TEXTURELOAD_NOFILE;
return NULL;
}
#else
if (!(fileID = fopen (filename, "r+bt")))
{
*error = TEXTURELOAD_NOFILE;
return NULL;
}
#endif
// get the heading data
fread (&type, sizeof (char), 3, fileID); // read in colormap info and image type
fseek (fileID, 12, SEEK_SET); // seek past the header
fread (&info, sizeof (char), 6, fileID);
/*
if (type[1] != 0 || (type[2] != 2 && type[2] != 3))
{
fclose ( fileID );
return false;
}*/
// convert heading to data
imageWidth = info[0] + info[1] * 256; // get image size from the header
imageHeight = info[2] + info[3] * 256;
imageBits = info[4];
size = imageWidth * imageHeight; // number of bits in image file
// check for right format to load
if ( (imageBits != 32) && (imageBits != 24) ) // invalid format
{
*error = TEXTURELOAD_INVALIDFORMAT;
fclose ( fileID );
return false;
}
/* if ( imageBits != 32 )
{
fclose ( fileID );
*error = TEXTURELOAD_INVALIDFORMAT;
return NULL;
}*/
int bitSize = (imageBits / 8);
// read the data
imageData = (unsigned char *)malloc (size * bitSize);
if (imageData == NULL)
{
fclose ( fileID );
*error = TEXTURELOAD_INVALIDFORMAT;
return NULL;
}
if ( (int)fread (imageData, sizeof (unsigned char), size * bitSize, fileID) != (int)(size * bitSize) )
{
free (imageData);
*error = TEXTURELOAD_INVALIDFORMAT;
return NULL;
}
// dont use greyscale ?
// create the data storage
STextureData *data = CreateData ( imageWidth, imageHeight, tsize );
if ( !data )
{
free (imageData);
*error = TEXTURELOAD_INVALIDFORMAT;
return NULL;
}
// copy to data storage
int tempI;
for ( int i = 0; i < size; i++ )
{
tempI = i * bitSize;
data[i].b = imageData[tempI];
data[i].g = imageData[tempI + 1];
data[i].r = imageData[tempI + 2];
if ( imageBits == 32 )
data[i].alpha = imageData[tempI + 3];
}
free ( imageData );
fclose (fileID);
*error = TEXTURELOAD_OK;
return data;
}
#ifdef DIRECT3D
LPDIRECT3DTEXTURE9 CTextureFile::CreateTexture(LPDIRECT3DDEVICE9 device)
{
if ( m_numData < 1 )
return NULL;
LPDIRECT3DTEXTURE9 texture;
if ( FAILED (D3DXCreateTexture ( device, m_width, m_height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture )) )
return NULL;
D3DSURFACE_DESC surfDesc;
texture->GetLevelDesc( 0, &surfDesc );
int h = surfDesc.Height;
D3DLOCKED_RECT LockedRect;
texture->LockRect( 0, &LockedRect, NULL, 0 );
DWORD* pBits;
for( DWORD iTexelZ=0; iTexelZ < surfDesc.Height; iTexelZ++ )
{
pBits = (DWORD*) ((BYTE*)LockedRect.pBits + (LockedRect.Pitch * iTexelZ));
for( DWORD iTexelX = 0; iTexelX < surfDesc.Width; iTexelX++ )
{
int num = m_numData - ((iTexelZ * surfDesc.Width) + (surfDesc.Width - iTexelX));
DWORD nRed = (DWORD) m_data[num].r;
DWORD nGreen = (DWORD) m_data[num].g;
DWORD nBlue = (DWORD) m_data[num].b;
DWORD nAlpha = (DWORD) m_data[num].alpha;
if ( m_alpha )
{
if ( (m_data[num].r == m_alphamap[0]) && (m_data[num].g == m_alphamap[1]) && (m_data[num].b == m_alphamap[2]) )
nAlpha = 0;
}
if( nRed > 0xFF )
nRed = 0xFF;
if( nGreen > 0xFF )
nGreen = 0xFF;
if( nBlue > 0xFF )
nBlue = 0xFF;
if( nAlpha > 0xFF )
nAlpha = 0xFF;
*pBits = (nAlpha << 24) + (nRed << 16) + (nGreen << 8) + (nBlue << 0);
pBits++;
}
}
texture->UnlockRect ( 0 );
return texture;
}
LPDIRECT3DTEXTURE9 CTextureFile::CreateCollisionTexture(LPDIRECT3DDEVICE9 device, DWORD nRed, DWORD nGreen, DWORD nBlue)
{
if ( (m_numData < 1) || (!m_collisionMap) || (!m_collisionData) )
return NULL;
LPDIRECT3DTEXTURE9 texture;
if ( FAILED (D3DXCreateTexture ( device, m_width, m_height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture )) )
return NULL;
D3DSURFACE_DESC surfDesc;
texture->GetLevelDesc( 0, &surfDesc );
int h = surfDesc.Height;
D3DLOCKED_RECT LockedRect;
texture->LockRect( 0, &LockedRect, NULL, 0 );
DWORD* pBits;
for( DWORD iTexelZ=0; iTexelZ < surfDesc.Height; iTexelZ++ )
{
pBits = (DWORD*) ((BYTE*)LockedRect.pBits + (LockedRect.Pitch * iTexelZ));
for( DWORD iTexelX = 0; iTexelX < surfDesc.Width; iTexelX++ )
{
int num = m_numData - ((iTexelZ * surfDesc.Width) + (surfDesc.Width - iTexelX));
DWORD nAlpha = 0xFF;
if ( m_collisionData[num] )
nAlpha = 0x00;
if( nRed > 0xFF )
nRed = 0xFF;
if( nGreen > 0xFF )
nGreen = 0xFF;
if( nBlue > 0xFF )
nBlue = 0xFF;
if( nAlpha > 0xFF )
nAlpha = 0xFF;
*pBits = (nAlpha << 24) + (nRed << 16) + (nGreen << 8) + (nBlue << 0);
pBits++;
}
}
texture->UnlockRect ( 0 );
return texture;
}
#endif
bool CTextureFile::Save(const char *filename)
{
// create header
SFileHeader header;
ClearHeader ( &header );
// open file
FILE *id;
#ifdef CY_USESECURE
if ( fopen_s ( &id, filename, "wb" ) )
return false;
#else
if ( (id = fopen ( filename, "wb" )) == NULL )
return false;
#endif
// write version numbers (used incase header needs to change in later versions)
float version = (float) TEXTUREFILE_VERSION;
fwrite ( &version, sizeof ( float ), 1, id );
// write header to file
header.width = m_width;
header.height = m_height;
header.frames = false;
header.alpha = m_alpha;
header.alphamap[0] = m_alphamap[0];
header.alphamap[1] = m_alphamap[1];
header.alphamap[2] = m_alphamap[2];
if ( m_animNum )
header.frames = true;
fwrite ( &header, sizeof ( SFileHeader ), 1, id );
// * Added since verison 1.20
fwrite ( &m_collisionMap, sizeof ( bool ), 1, id );
// write all the data
fwrite ( m_data, sizeof ( STextureData ), m_numData, id );
// write frames data
fwrite ( &m_frames, sizeof ( STextureFrames ), 1, id );
int animNum = 0;
int i;
for ( i = 0; i < m_animNum; i++ )
{
if ( !m_anim[i].remove )
animNum++;
}
fwrite ( &animNum, sizeof ( int ), 1, id );
SAnimation *anim;
for ( i = 0; i < m_animNum; i++ )
{
anim = &m_anim[i];
if ( !anim->remove )
{
int size = (int)strlen ( anim->name ) + 1;
fwrite ( &size, sizeof ( int ), 1, id );
fwrite ( anim->name, sizeof ( char ), size, id );
fwrite ( &anim->amt, sizeof ( int ), 1, id );
fwrite ( &anim->delay, sizeof ( int ), 1, id );
fwrite ( &anim->cont, sizeof ( bool ), 1, id );
fwrite ( anim->frames, sizeof ( int ), anim->amt, id );
}
}
if ( m_collisionMap )
fwrite ( m_collisionData, sizeof ( bool ), m_numData, id );
fclose ( id );
return true;
}
void CTextureFile::ClearHeader(SFileHeader *header)
{
header->alpha = false;
header->alphamap[0] = 0;
header->alphamap[1] = 0;
header->alphamap[2] = 0;
header->frames = false;
header->height = 0;
header->width = 0;
}
bool CTextureFile::Load(const char *filename)
{
// create header
SFileHeader header;
ClearHeader ( &header );
// open file
FILE *id;
#ifdef CY_USESECURE
if ( fopen_s ( &id, filename, "rb" ) )
return false;
#else
if ( (id = fopen ( filename, "rb" )) == NULL )
return false;
#endif
// read version numbers (used incase header needs to change in later versions)
float version;
fread ( &version, sizeof ( float ), 1, id );
if ( version < 1.00 )
return false;
// read header from file
fread ( &header, sizeof ( SFileHeader ), 1, id );
m_width = header.width;
m_height = header.height;
m_numData = m_width * m_height;
m_alpha = header.alpha;
m_alphamap[0] = header.alphamap[0];
m_alphamap[1] = header.alphamap[1];
m_alphamap[2] = header.alphamap[2];
m_alpha = header.alpha;
// * Added since version 1.20
if ( version > 1.20 )
fread ( &m_collisionMap, sizeof ( bool ), 1, id );
// read all the data
m_data = new STextureData[m_numData];
if ( fread ( m_data, sizeof ( STextureData ), m_numData, id ) != m_numData )
return false;
fread ( &m_frames, sizeof ( STextureFrames ), 1, id );
if ( (version >= 1.1) && (header.frames) )
{
// read number of animations
int fnum, size;
fread ( &fnum, sizeof ( int ), 1, id );
if ( m_anim )
{
if ( m_editor )
free ( m_anim );
else
delete []m_anim;
}
if ( !fnum )
m_anim = NULL;
else
{
if ( m_editor )
m_anim = (SAnimation *)malloc(sizeof ( SAnimation ) * fnum);
else
m_anim = new SAnimation[fnum];
}
SAnimation *anim;
for ( int i = 0; i < fnum; i++ )
{
anim = &m_anim[i];
fread ( &size, sizeof ( int ), 1, id );
anim->name = new char[size];
fread ( anim->name, sizeof ( char ), size, id );
fread ( &anim->amt, sizeof ( int ), 1, id );
fread ( &anim->delay, sizeof ( int ), 1, id );
fread ( &anim->cont, sizeof ( bool ), 1, id );
anim->remove = false;
anim->frames = new int[anim->amt];
fread ( anim->frames, sizeof ( int ), anim->amt, id );
}
m_animNum = fnum;
}
if ( m_collisionMap )
{
m_collisionData = new bool[m_numData];
if ( fread ( m_collisionData, sizeof ( bool ), m_numData, id ) != m_numData )
return false;
}
fclose ( id );
m_numframes = m_frames.x * m_frames.y;
m_framebounds = new SFrameBounds[m_numframes];
for ( int i = 0; i < m_numframes; i++ )
{
m_framebounds[i].minx = ComputeMinAlphaX ( i );
m_framebounds[i].maxx = ComputeMaxAlphaX ( i );
m_framebounds[i].miny = ComputeMinAlphaY ( i );
m_framebounds[i].maxy = ComputeMaxAlphaY ( i );
}
return true;
}
void CTextureFile::SetFrameSize(int width, int height)
{
m_frames.x = width;
m_frames.y = height;
}
const STextureFrames *CTextureFile::GetFrames()
{
return &m_frames;
}
int CTextureFile::GetFrameWidth()
{
return m_frames.x;
}
int CTextureFile::GetFrameHeight()
{
return m_frames.y;
}
void CTextureFile::SetFrames(int rows, int cols)
{
m_frames.rows = rows;
m_frames.cols = cols;
}
void CTextureFile::EnableAlphamap(bool enable)
{
m_alpha = enable;
}
bool CTextureFile::GetAlphaMap()
{
return m_alpha;
}
void CTextureFile::SetAlphaMap(int red, int green, int blue)
{
m_alphamap[0] = red;
m_alphamap[1] = green;
m_alphamap[2] = blue;
}
bool CTextureFile::GetAlphaMap(int *red, int *green, int *blue)
{
*red = m_alphamap[0];
*green = m_alphamap[1];
*blue = m_alphamap[2];
return m_alpha;
}
SAnimation * CTextureFile::AddNewAnimation(const char *name)
{
SAnimation *anim = NULL;
// first, find if theres any free slots
for ( int i = 0; i < m_animNum; i++ )
{
// found a free slot
if ( m_anim[i].remove )
{
anim = &m_anim[i];
// make sure old data is cleared before using
if ( anim->frames )
delete []anim->frames;
if ( anim->name )
delete []anim->name;
break;
}
}
// no free slot ? add a new one to the end of the array
if ( !anim )
{
++m_animNum;
m_anim = (SAnimation *)realloc(m_anim, sizeof ( SAnimation ) * m_animNum);
anim = &m_anim[m_animNum - 1];
}
// set default data for animation
anim->amt = 1;
anim->frames = new int[1];
anim->remove = false;
anim->frames[0] = 1;
anim->cont = false;
anim->delay = 1;
anim->name = new char[strlen (name) + 1];
#ifdef CY_USESECURE
strcpy_s ( anim->name, strlen (name) + 1, name );
#else
strcpy ( anim->name, name );
#endif
// return pointer to created animation
return anim;
}
int CTextureFile::GetAnimationNum()
{
return m_animNum;
}
SAnimation * CTextureFile::FindAnimation(const char *search)
{
Utils::String sSearch(search);
for ( int i = 0; i < m_animNum; i++ ) {
if ( !m_anim[i].remove ) {
if ( sSearch.Compare(m_anim[i].name) ) {
return &m_anim[i];
}
}
}
return NULL;
}
SAnimation * CTextureFile::GetAnimation(int num)
{
if ( (m_animNum < num) || (num < 0) )
return NULL;
return &m_anim[num];
}
void CTextureFile::SetEditor()
{
m_editor = true;
}
void IncAnimationFrame ( SPlayAnimation *anim, int *times )
{
IncAnimationFrame ( anim, false, times );
}
void IncAnimationFrame ( SPlayAnimation *anim, bool endstop, int *times )
{
if ( !anim )
return;
if ( !anim->anim )
return;
++anim->delay;
if ( anim->delay >= anim->anim->delay )
{
anim->delay = 0;
++anim->curframe;
if ( anim->curframe > anim->anim->amt )
{
if ( anim->anim->cont )
{
anim->curframe = 1;
if ( times )
++(*times);
}
else if ( endstop )
--anim->curframe;
else
anim->anim = NULL;
}
}
}
void DecAnimationFrame ( SPlayAnimation *anim, int *times )
{
DecAnimationFrame ( anim, false, times );
}
void DecAnimationFrame ( SPlayAnimation *anim, bool endstop, int *times )
{
if ( !anim )
return;
if ( !anim->anim )
return;
++anim->delay;
if ( anim->delay >= anim->anim->delay )
{
anim->delay = 0;
--anim->curframe;
if ( anim->curframe < 1 )
{
if ( anim->anim->cont )
{
anim->curframe = anim->anim->amt;
if ( times )
++(*times);
}
else if ( endstop )
anim->curframe = 1;
else
anim->anim = NULL;
}
}
}
void ResetAnimation ( SPlayAnimation *anim )
{
if ( !anim )
return;
anim->anim = NULL;
anim->delay = 0;
anim->curframe = 1;
anim->reverse = false;
}
void DoAnimation ( SPlayAnimation *anim, bool endstopinc, bool endstopdec, int *times )
{
if ( !anim )
return;
if ( anim->reverse )
DecAnimationFrame ( anim, endstopdec, times );
else
IncAnimationFrame ( anim, endstopinc, times );
}
bool CTextureFile::CheckAlpha(int x, int y, int frame, bool inccollisionmap)
{
int row = ((frame - 1) / m_frames.cols) + 1;
int col = frame % m_frames.cols;
if ( col == 0)
col = m_frames.cols;
int newx = ((col - 1) * m_frames.x) + x;
int newy = ((row - 1) * m_frames.y) + y;
int num = (newy * m_width) + newx;
if ( (num < 0) || (num > (m_width * m_height)) )
return false;
if ( (inccollisionmap) && (m_collisionMap) && (m_collisionData) )
return m_collisionData[num];
int r = m_data[num].r;
int g = m_data[num].g;
int b = m_data[num].b;
int a = m_data[num].alpha;
if ( m_data[num].alpha == 0 )
return true;
if ( m_alpha )
{
if ( (m_data[num].r == m_alphamap[0]) && (m_data[num].g == m_alphamap[1]) && (m_data[num].b == m_alphamap[2]) )
return true;
}
return false;
}
std::string CTextureFile::GetErrorText ( int error )
{
switch ( error )
{
case TEXTURELOAD_OK:
return "";
case TEXTURELOAD_NOFILE:
return "unable to open file";
case TEXTURELOAD_SIZEMISMATCH:
return "size mismatch";
case TEXTURELOAD_INVALIDFORMAT:
return "Invalid Format";
}
return "Unknown Error";
}
int CTextureFile::ComputeMinAlphaX ( int frame )
{
for ( unsigned int x = 0; x < m_frames.x; x++ )
{
for ( unsigned int y = 0; y < m_frames.y; y++ )
{
if ( !CheckAlpha ( x, y, frame ) )
return x;
}
}
return 0;
}
int CTextureFile::ComputeMaxAlphaX ( int frame )
{
for ( unsigned int x = m_frames.x - 1; x >= 0; x-- )
{
for ( unsigned int y = 0; y < m_frames.y; y++ )
{
if ( !CheckAlpha ( x, y, frame ) )
return x;
}
}
return m_frames.x - 1;
}
int CTextureFile::ComputeMinAlphaY ( int frame )
{
for ( unsigned int y = 0; y < m_frames.y; y++ )
{
for ( unsigned int x = 0; x < m_frames.x; x++ )
{
if ( !CheckAlpha ( x, y, frame ) )
return y;
}
}
return 0;
}
int CTextureFile::ComputeMaxAlphaY ( int frame )
{
for ( unsigned int y = m_frames.y - 1; y >= 0; y-- )
{
for ( unsigned int x = 0; x < m_frames.x; x++ )
{
if ( !CheckAlpha ( x, y, frame ) )
return y;
}
}
return m_frames.y - 1;
}