Текстурный менеджер.

Предисловие.

Доброго времени суток всем, кто решил заняться текстурированием. Данная тема очень простая, поэтому я решил в этой статье описать процесс создания менеджера текстур, который может динамически загружать/выгружать текстуры. Это может оказаться очень полезным при большом количестве текстур и маленьком объеме видеопамяти. Итак, приступим.

Класс текстуры.

к началу статьи
class	cTexture{
public:
LPDIRECT3DTEXTURE8 Texture;
cTexture( void ){Texture=NULL;}

cTexture( float ){Texture=NULL;}

void operator=( float ){}

void CreateTextureFromFile( LPDIRECT3DDEVICE8 Device , char *path );

void CreateTextureFromFile( LPDIRECT3DDEVICE8 Device , char *path ,
int MipLevels );

void GetLevelDesc( int level , D3DSURFACE_DESC *desc )
{Texture->GetLevelDesc( level , desc );}

void CreateTextureFromFile( LPDIRECT3DDEVICE8 Device , char *path ,
int MipLevels , DWORD USAGE );

void ResetTexture( LPDIRECT3DDEVICE8 Device , int i )
{if( Texture )Device->SetTexture( i , NULL );}

void SetTexture( LPDIRECT3DDEVICE8 Device , int i )
{if( Texture )Device->SetTexture( i , Texture );}

HRESULT GetSurfaceLevel( UINT Level , IDirect3DSurface8 **ppSurfaceLevel )
{return( Texture->GetSurfaceLevel( Level , ppSurfaceLevel ) );}

HRESULT CreateRenderTarget( LPDIRECT3DDEVICE8 Device , int w , int h )
{return(Device->CreateTexture( w , h , 1 , D3DUSAGE_RENDERTARGET ,
D3DFMT_X8R8G8B8 , D3DPOOL_DEFAULT , &Texture));}

void Merge( LPDIRECT3DDEVICE8 , cTexture& , int , int );

void SetTransparent( DWORD );

void Release( void )
{if( Texture )Texture->Release();Texture=NULL;}

void CreateTextureFromMemory( LPDIRECT3DDEVICE8 D3DDevice , LPCVOID STR , long size )
{D3DXCreateTextureFromFileInMemory( D3DDevice , STR , size , &Texture );}

void RecreateWithAlpha( LPDIRECT3DDEVICE8 Device );
};

CreateTextureFromFile ( LPDIRECT3DDEVICE8 Device , char *path , int MipLevels , DWORD USAGE ) – с помощью этой функции можно загрузить текстуру из фала (путь к которому передается в параметре path), контролируя при этом количество мип-уровней. Каждый i-й мип-уровень меньше (i-1)-го в 2 раза, таким образом при генерации всех мип-уровней мы получаем двойной расход памяти, но получаем выигрыш в производительности – когда полигон находится далеко от камеры, нет нужды накладывать на него исходную текстуру, которая может быть очень большая. Вместо этого будет использован какой-то из сгенерённых мип-уровней. Это существенно ускорит рендерринг. Остальные функции CreateTextureFromFile действуют аналогично.
void	cTexture::CreateTextureFromFile( LPDIRECT3DDEVICE8 Device , char *path , 
int MipLevels , DWORD USAGE ){
HRESULT RET_VAL( D3DXCreateTextureFromFileEx( Device , path , D3DX_DEFAULT ,
D3DX_DEFAULT , 1 , USAGE , D3DFMT_UNKNOWN ,
D3DPOOL_DEFAULT , D3DX_FILTER_LINEAR
, D3DX_FILTER_LINEAR ,
NULL , NULL , NULL , &Texture ) );
if( RET_VAL != D3D_OK ){
char error_message[1000];
strcpy( error_message , "Невозможно найти текстуру. derect path: " );
strcat( error_message , path );
MessageBox( 0 , error_message , "Ошибка" , 0 );
exit( 0 );
}
}

void cTexture::CreateTextureFromFile( LPDIRECT3DDEVICE8 Device , char *path ){
HRESULT RET_VAL( D3DXCreateTextureFromFile( Device , path , &Texture ) );
if( RET_VAL != D3D_OK ){
char error_message[1000];
strcpy( error_message , "Невозможно найти текстуру. derect path: " );
strcat( error_message , path );
MessageBox( 0 , error_message , "Ошибка" , 0 );
exit( 0 );
}
}

void cTexture::CreateTextureFromFile( LPDIRECT3DDEVICE8 Device , char *path , int MipLevels ){
HRESULT RET_VAL( D3DXCreateTextureFromFileEx( Device , path , D3DX_DEFAULT ,
D3DX_DEFAULT , MipLevels , 0 , D3DFMT_UNKNOWN ,
D3DPOOL_DEFAULT , D3DX_FILTER_LINEAR ,
D3DX_FILTER_LINEAR , NULL , NULL ,
NULL , &Texture ) );
if( RET_VAL != D3D_OK ){
char error_message[1000];
strcpy( error_message , "Невозможно найти текстуру. derect path: " );
strcat( error_message , path );
MessageBox( 0 , error_message , "Ошибка" , 0 );
exit( 0 );
}
}

GetLevelDesc( int level , D3DSURFACE_DESC *desc ) – позволяет определить параметры мип-уровня (например размеры).
SetTexture( LPDIRECT3DDEVICE8 Device , int i ) – устанавливает текстуру в i-й уровень.
ResetTexture( LPDIRECT3DDEVICE8 Device , int i ) – удаляет текстуру из i-й уровня.
Merge( LPDIRECT3DDEVICE8 , cTexture & m_tex , int x, int y) – переписывает в текстуру *this текстуру m_tex, x и y – координаты верхнего левого угла прямоугольника в который будет осуществлено копирование текстуры m_tex. Если размеры текстуры m_tex таковы, что её правый нижний угол вылезает за границы целевой текстуры, появится соответствующее сообщение. Вот код:
void	cTexture::Merge( LPDIRECT3DDEVICE8 D3DDevice , cTexture &m_tex , 
int x , int y ){
//описатели текстур
D3DSURFACE_DESC DstInfo , SrcInfo;
//узнаём параметры текстур
Texture->GetLevelDesc( 0 , &DstInfo );
m_tex.GetLevelDesc( 0 , &SrcInfo );

//как я и говорил – проверяем, что бы m_tex
//целиком влезала в целевую текстуру
if( x + SrcInfo.Width > DstInfo.Width || y + SrcInfo.Height > DstInfo.Height ){
MessageBox( 0 , "Размеры целевой текстуры недостаточно велики" , 0 , 0 );
exit( 0 );
}

D3DLOCKED_RECT LockedRectDst , LockedRectSrc;

//лочим текстуры для копирования
Texture->LockRect( 0 , &LockedRectDst , 0 , 0 );
DWORD *p32Dst = ( DWORD* )LockedRectDst.pBits;

m_tex.Texture->LockRect( 0 , &LockedRectSrc , 0 , 0 );
DWORD *p32Src = ( DWORD* )LockedRectSrc.pBits;
//устанавливаем указатель на пиксели целевой текстуры
//в нужное положение координаты (x,y)
p32Dst += 4 * DstInfo.Width * y + 4 * x;
//копируем содержимое m_tex
for( int i( 0 ) ; i < SrcInfo.Height ; i++ ){
for( int j( 0 ) ; j < SrcInfo.Width ; j++ ){
( *p32Dst ) = ( *p32Src );
p32Dst++;p32Src++;
}
//одну строку m_tex скопировали
p32Dst += DstInfo.Width - SrcInfo.Width;
}
//разлочиваем текстуры
m_tex.Texture->UnlockRect( 0 );
Texture->UnlockRect( 0 );
}
Данная функция может пригодиться, если вы задумаете при создании интерфейса, рисовать кнопки непосредственно по текстуре фона (на мой взгляд, не самый удобный вариант).
Release( void ) – удаляет текстуру
CreateTextureFromMemory( LPDIRECT3DDEVICE8 D3DDevice , LPCVOID STR , long size ) – создает текстуру из памяти, на которую указывает PTR (размер этой памяти в байтах - size). Рано или поздно Вы будете партиклами маяться (в хорошем смысле), поэтому уже сейчас неплохо бы позаботиться о функциях для работы с альфа каналом текстуры:
void	cTexture::RecreateWithAlpha( LPDIRECT3DDEVICE8 D3DDevice ){

LPDIRECT3DTEXTURE8 tex;

D3DSURFACE_DESC SrcInfo;

Texture->GetLevelDesc( 0 , &SrcInfo );

D3DDevice->CreateTexture( SrcInfo.Width , SrcInfo.Height , 1 ,
0 , D3DFMT_A8R8G8B8 ,
D3DPOOL_MANAGED, &tex );

D3DLOCKED_RECT LockedRectSrc;
Texture->LockRect( 0 , &LockedRectSrc , 0 , 0 );
DWORD *p32Src = ( DWORD* )LockedRectSrc.pBits;

D3DLOCKED_RECT LockedRectDst;
tex->LockRect( 0 , &LockedRectDst , 0 , 0 );
DWORD *p32Dst = ( DWORD* )LockedRectDst.pBits;

for( int i( 0 ) ; i < SrcInfo.Height ; i++ ){
for( int j( 0 ) ; j < SrcInfo.Width ; j++ ){
p32Dst[ 0 ] = p32Src[ 0 ];
p32Dst++;
p32Src++;
}
}

tex->UnlockRect( NULL );

Texture->UnlockRect( NULL );

Texture->Release();

Texture = tex;

tex = NULL;
}

//заливаем альфой те тексели цвет которых не равен color
void cTexture::SetTransparent( DWORD color ){
D3DSURFACE_DESC DstInfo;

Texture->GetLevelDesc( 0 , &DstInfo );

D3DLOCKED_RECT LockedRectDst , LockedRectSrc;

Texture->LockRect( 0 , &LockedRectDst , 0 , 0 );
DWORD *p32Dst = ( DWORD* )LockedRectDst.pBits;

char *Alpha;

for( int i( 0 ) ; i < DstInfo.Height ; i++ ){
for( int j( 0 ) ; j < DstInfo.Width ; j++ ){
Alpha = ( char * )p32Dst;

if( *p32Dst == color )
*( Alpha + 3 ) = 0x00;
else
*( Alpha + 3 ) = 0xff;
p32Dst++;
}
}
Texture->UnlockRect( 0 );
}

В первой функции просто создаем пустую текстуру в памяти и копируем в неё содержимое исходной текстуры (когда-нибудь копирование сделаю memcpy'ем, пока мне что-то лениво). Затем срубаем старую текстуру и переприсваиваем указатели.
Во второй функции просто пробегам по всем текселям (элементам растра) и ставим альфу в зависимости от его цвета.
Обратите внимание на LockRect/UnlockRect. В Direct3D, если Вы захотите работать непосредственно с содержимым какого-либо буффера (здесь под буффером понимается некая область памяти, в данном случае массив текселей) каждый раз придется лочить его, делать своё дело и разлочивать. Кстати, мы уже сталкивались с этим во время изучения вершинных буфферов.

Вот небольшой пример, как пользоваться этим классом:

//класс вершины – ничего особенного

#define FVF_CD3DVERTEX D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1

class cD3DVertex:public cVector3{
public:
DWORD color;
float u,v;
cD3DVertex( void ):cVector3(){color=0xffffff;}
cD3DVertex( float f1 , float f2 , float f3 ):cVector3( f1 , f2 , f3 ){}
cD3DVertex( float f1 , float f2 , float f3 , DWORD c ):cVector3( f1 , f2 , f3 )
{ color = c;}
cD3DVertex( float f1 , float f2 , float f3 , DWORD c , float f4 , float f5 )
:cVector3( f1 , f2 , f3 ){ color = c ; u = f4 ; v = f5;}
cD3DVertex operator=( cVector4 v )
{x = v.x;y = v.y;z = v.z;return(*this);}
};

//где-то в функции инициализации
//просто создаем белый квадрат
cD3DVertexBuffer VB;
VB.AddEnd( cD3DVertex(0,0,0,0xffffff,0,1) );
VB.AddEnd( cD3DVertex(1,1,0,0xffffff,1,0) );
VB.AddEnd( cD3DVertex(0,1,0,0xffffff,0,0) );

VB.AddEnd( cD3DVertex(0,0,0,0xffffff,0,1) );
VB.AddEnd( cD3DVertex(1,0,0,0xffffff,1,1) );
VB.AddEnd( cD3DVertex(1,1,0,0xffffff,1,0) );

//создаем текстуру
cTexture Texture;
Texture.CreateTextureFromFile( D3DDevice , “nvlogo.bmp” );

//где-то в функции рендерринга
//устанавливаем текстуру
Texture.SetTexture( D3DDevice , 0 );

//рендеррим содержимое буфера
VB.Render( D3DDevice , FVF_CD3DVERTEX );
//отключаем текстуру
Texture.ResetTexture( D3DDevice , 0 );

Вот и всё!

Менеджер текстур.

к началу статьи
Я сразу приведу класс, который у меня получился, а затем будем потихонечку его ковырять.
DWORD TextureThreadFunc( LPVOID );

struct sTextureInfo{char Info[256];};

int TimeOut = 5;

class cTextureManager{
cThread TextureThread;
cMutex QueueMutex;
public:
cArray Textures;
cArray TextureMutexes;
cArray Queue;
cArray<bool> Dinamic;
cArray<bool> Released;
cArray TexturesInfo;
cArray TexturePaths;
cArray LastRequestTime;

__forceinline void Lock( void )
{if( TextureThread != NULL )QueueMutex.EnterCriticalSection();}
__forceinline void Unlock( void )
{if( TextureThread != NULL )QueueMutex.LeaveCriticalSection();}

__forceinline void Lock( int i )
{if( TextureThread != NULL )TextureMutexes[ i ].EnterCriticalSection();}
__forceinline void Unlock( int i )
{if( TextureThread != NULL )TextureMutexes[ i ].LeaveCriticalSection();}

void RunThread( void );
void LoadTextures( char* );

cTexture &operator[]( char * );
cTexture &operator[]( int i );
void SetTexture( LPDIRECT3DDEVICE8 , char * , int );
void ResetTexture( LPDIRECT3DDEVICE8 , char * , int );
void SetTexture( LPDIRECT3DDEVICE8 , int , int );
void ResetTexture( LPDIRECT3DDEVICE8 , int , int );

int GetTextureCursor( char * );

void ReleaseTexture( char * );
void ReleaseTexture( int );
void UploadTexture( char * );
void UploadTexture( int );

void Reload( LPDIRECT3DDEVICE8 );
void Release( void );

void FillRequestTime( void );

~cTextureManager();

int GetSleepTime( int );
};
cTextureManager TextureManager;

Вот какие поля в этом классе:
Textures - массив текстур
TextureThread - объект потока (подробнее об этом классе смотрите в одном из предыдущих уроков)
QueueMutex - мьютекс для очереди сообщений
TextureMutexes - массив мьютексов (по одному на каждую текстуру)
Queue - очередь сообщений.
Dinamic - массив флагов. Если true, то Textures[i] можно загружать/выгружать в потоке TextureThread.
Released - если Released[i] == true, то текстура Textures[i] в данный момент выгружена.
TexturesInfo - название текстуры.
TexturePaths - путь к текстуре.
LastRequestTime - время последнего обращения к текстуре. Прежде всего, нам надо все текстуры загрузить. Это делается методом LoadTextures( char* ). В качестве параметра он принимает путь в файле, в котором хранятся пути к текстурам. Вот как происходит загрузка:
void	cTextureManager::LoadTextures( char *path ){
char tmp_path[256];
char enum_path[256];
memcpy( tmp_path , path , strlen( path ) + 1 );
memcpy( enum_path , path , strlen( path ) + 1 );

strcat( enum_path , "texture_enum.log" );

FILE *f_stream = fopen( enum_path , "rt" );
//проверяем, существует ли файл со списком текстур
if( !f_stream ){
MessageBox( 0 , "Невозможно загрузить описатель текстур." , "Ошибка" , 0 );
exit( 0 );
}

char directive[ 256 ];

for( ; EOF != fscanf( f_stream , "%s" , directive ) ; ){
if( !strcmp( directive , "set_dinamic" ) ){
//читаем название текстуры
fscanf( f_stream , "%s" , directive );
//делаем её динамической
Dinamic[ GetTextureCursor( directive ) ] = true;
}
else{
//читаем название текстуры
sTextureInfo TextureInfo;
memcpy( &TextureInfo , directive , 256 );
TexturesInfo.AddEnd( TextureInfo );
//все текстуры по умолчанию не подлежат
//динамической загрузке/выгрузке
Dinamic.AddEnd( false );
//текстура загружена
Released.AddEnd( false );

//читаем название файла текстуры
fscanf( f_stream , "%s" , directive );
memcpy( enum_path , path , strlen( path ) + 1 );
strcat( enum_path , directive );
Textures[ Textures.Cursor++ ].CreateTextureFromFile( D3DDevice , enum_path );
memcpy( &TextureInfo , enum_path , 256 );
//сохраняем путь к текстуре
TexturePaths.AddEnd( TextureInfo );
}
}
//заполнение массива LastRequestTime
FillRequestTime();
}

Вот пример загружаемого файла:
nvlogo bmp/nvlogo.bmp
sunset jpeg/sunset.jpg
logo logo.bmp

set_dynamic nvlogo

Как Вы, наверное, поняли, в файле хранятся относительные пути. Берётся путь к папке, в которой лежит texture_enum.log и к нему прибавляется путь, указанный в файле. Так же хочу отметить такой нюанс – заполнение массива LastRequestTime происходит после того, как все текстуры загружены. Это нужно для того, что бы после загрузки всех текстур, текстуры, допускающие динамическую подгрузку/выгрузку, не были выгружены (загрузка всех текстур может длиться более одной минуты). Вот функция, которая заполняет массив LastRequestTime
void	cTextureManager::FillRequestTime( void ){
for( int i( 0 ) ; i < Textures.Cursor ; i++ ){
Lock( i );

LastRequestTime.AddEnd( time() );

Unlock( i );
}
}

Массив LastRequestTime нужно лочить, поскольку все функции, предназначенные для работы с текстурами, могут модифицировать элементы этого массива. Правда, я как ни старался, не мог придумать случай, когда незалоченный массив LastRequestTime мог вызвать серьёзный сбой… Просто будем считать, что Lock/Unlock это хороший тон. Теперь функции для работы с текстурами (установка/сброс):
void	cTextureManager::SetTexture( LPDIRECT3DDEVICE8 D3DDevice , 
int t_c , int s ){
Lock( t_c );

if( !Released[ t_c ] )
( *this )[ t_c ].SetTexture( D3DDevice , s );
else{
UploadTexture( t_c );
}

LastRequestTime[ t_c ] = time();

Unlock( t_c );
}

void cTextureManager::ResetTexture( LPDIRECT3DDEVICE8 D3DDevice ,
char *t_name , int s ){
int t_c( GetTextureCursor( t_name ) );

Lock( t_c );

if( !Released[ t_c ] )
( *this )[ t_c ].ResetTexture( D3DDevice , s );

LastRequestTime[ t_c ] = time();

Unlock( t_c );
}

void cTextureManager::SetTexture( LPDIRECT3DDEVICE8 D3DDevice ,
int t_c , int s ){
Lock( t_c );

if( !Released[ t_c ] )
( *this )[ t_c ].SetTexture( D3DDevice , s );
else
UploadTexture( t_name );
LastRequestTime[ t_c ] = time();
Unlock( t_c );
}

void cTextureManager::ResetTexture( LPDIRECT3DDEVICE8 D3DDevice ,
int t_c , int s ){
Lock( t_c );

if( !Released[ t_c ] )
( *this )[ t_c ].ResetTexture( D3DDevice , s );

LastRequestTime[ t_c ] = time();

Unlock( t_c );
}

Мьютексами необходимо “накрываться”, на случай если поток уничтожит текстуру, которую мы хотим установить. Так же не забываем обновлять LastRequestTime[ t_c ], иначе запущенный поток будет выгружать абсолютно все текстуры (которые этого допускают, естественно). Функция, возвращающая курсор текстуры по названию (так же ничего сложного):
int	cTextureManager::GetTextureCursor( char * t_name ){

for( int i( 0 ) ; i < Textures.Cursor ; i++ ){
if( !strcmp( t_name , TexturesInfo[ i ].Info ) ){
return( i );
}
}
char error_message[1000];
strcpy( error_message , "Обращение к несуществующей текстуре : " );
strcat( error_message , t_name );
MessageBox( 0 , error_message , "Ошибка" , 0 );
exit( 0 );
}

Функция, возвращающая ссылку на текстуру (опять-таки не забываем лочить ресурсы мьютексом и обновлять LastRequestTime[ i ]):
cTexture &cTextureManager::operator[]( int i ){

Lock( i );

LastRequestTime[ i ] = time();

Unlock( i );

return( Textures[ i ] );
}

cTexture &cTextureManager::operator[]( char * name ){

int TextureCursor( GetTextureCursor( name ) );

Lock( TextureCursor );

LastRequestTime[ TextureCursor ] = time();

Unlock( TextureCursor );

return( Textures[ TextureCursor ] );
}

Теперь осталось организовать функции для загрузки/удаления текстур и можно заниматься потоком:
void	cTextureManager::ReleaseTexture( char *t_name ){
int TextureCursor( GetTextureCursor( t_name ) );

Lock();

if( !Released[ TextureCursor ] )
Queue.AddEnd( cMsg( T_RELEASE_TEXTURE , 0 , TextureCursor ) );
else{
MessageBox( 0 , "Вы пытаетесь удалить уже удаленную текстуру." , "Ошибка." , 0 );
exit( 0 );
}

Unlock();
}

void cTextureManager::ReleaseTexture( int i ){
Lock();

if( !Released[ i ] )
Queue.AddEnd( cMsg( T_RELEASE_TEXTURE , 0 , i ) );
else{
MessageBox( 0 , "Вы пытаетесь удалить уже удаленную текстуру." , "Ошибка." , 0 );
exit( 0 );
}

Unlock();
}

void cTextureManager::UploadTexture( char *t_name ){
int TextureCursor( GetTextureCursor( t_name ) );

Lock();

if( Released[ TextureCursor ] )
Queue.AddEnd( cMsg( T_UPLOAD_TEXTURE , 0 , TextureCursor ) );
else{
MessageBox( 0 , "Вы пытаетесь загрузить уже загруженную текстуру." , "Ошибка." , 0 );
exit( 0 );
}

Unlock();
}

void cTextureManager::UploadTexture( int i ){
Lock();

if( Released[ i ] )
Queue.AddEnd( cMsg( T_UPLOAD_TEXTURE , 0 , i ) );
else{
MessageBox( 0 , "Вы пытаетесь загрузить уже загруженную текстуру." ,
"Ошибка." , 0 );
exit( 0 );
}

Unlock();
}

Как видите ни первая, ни одна из функций напрямую с текстурами не работают. Они лишь вешают сообщение в очередь и следят за тем, что бы некорректных сообщений не создавалось (загрузить второй раз уже загруженную текстуру, например). Из рутины остались только функции удаления, перезагрузки текстур и деструктор.
void	cTextureManager::Reload( LPDIRECT3DDEVICE8 D3DDevice ){
for( int i( 0 ) ; i < Textures.Cursor ; i++ ){
Lock( i );

if( !Released[ i ] ){
Textures[ i ].CreateTextureFromFile( D3DDevice ,
TexturePaths[ i ].Info );
}

Unlock( i );
}
}

void cTextureManager::Release( void ){
for( int i( 0 ) ; i < Textures.Cursor ; i++ ){
Lock( i );

if( !Released[ i ] ){
Textures[ i ].Release();
Released[ i ] = true;
}

Unlock( i );
}
}

cTextureManager::~cTextureManager(){
Release();
TextureMutexes.Release();
Released.Release();
Textures.Release();
TexturesInfo.Release();
TexturePaths.Release();
Dinamic.Release();
}

Теперь самое интересное – поток. Он запускается следующей функцией:
void	cTextureManager::RunThread( void ){
cThreadParam ThreadParam;
//в поток передаем только указатель на наш менеджер
ThreadParam.AddParamPtr( *this );
TextureThread.CreateThread( TextureThreadFunc , ThreadParam );
TextureThread.SetThreadPriority( THREAD_PRIORITY_NORMAL );
}

Функция потока в моей интерпретации будет выглядеть так:
DWORD TextureThreadFunc( LPVOID PTR ){
cThreadParam ThreadParam( PTR );
cTextureManager *t_man( ( cTextureManager * )ThreadParam.GetParami( 0 ) );
cArray<char> TEXTURE;

FILE *f_stream;

for(;;){
//накрываемся мьютексом
//берем сообщение
t_man->Lock();
int Queue_Cursor( t_man->Queue.Cursor );
int Queue_wParam( t_man->Queue[ 0 ].wParam );
int Queue_Message( t_man->Queue[ 0 ].Message );
t_man->Unlock();

if( Queue_Cursor ){
//обрабатываем сообщение
switch( Queue_Message ){
//подгрузка текстуры
case( T_UPLOAD_TEXTURE ):
t_man->Lock( Queue_wParam );

//выгружена ли текстура?
if( t_man->Released[ Queue_wParam ] ){
//Открываем файл
f_stream = fopen( t_man->TexturePaths[ Queue_wParam ].Info ,
"rb" );
//смотрим длину файла
fseek( f_stream , 0 , SEEK_END );
int f_length( ftell( f_stream ) );
fseek( f_stream , 0 , SEEK_SET );
//готовим буфер для чтения
if( TEXTURE.Length < f_length )
TEXTURE.SetLength( f_length );

fread( TEXTURE.Array , 1 ,
f_length , f_stream );
//текстуру будем создавать из буфера в памяти,
//мне почему-то кажется, что так будет быстрее,
//хотя я не проверял

t_man->Textures[ Queue_wParam ].
CreateTextureFromMemory( D3DDevice ,
TEXTURE.Array , f_length );
//сообщаем, что теперь текстура загружена
t_man->Released[ Queue_wParam ] = false;

fclose( f_stream );
}

t_man->Unlock( Queue_wParam );
break;
case( T_RELEASE_TEXTURE ):
//удаление текстуры
t_man->Lock( Queue_wParam );

t_man->Textures[ Queue_wParam ].Release();
t_man->Released[ Queue_wParam ] = true;

t_man->Unlock( Queue_wParam );
break;
};
//грохаем сообщение
t_man->Lock();

t_man->Queue.ShiftL( 0 , 1 );

t_man->Unlock();

}

//можно расслабиться, если сообщений нет
if( !t_man->Queue.Cursor ){
//считаем сколько можно “спать”
Sleep( 1000 * t_man->GetSleepTime( TimeOut ) + 1000 );
time_t start( time() );
//проверяем, какие из текстур на данный момент можно удалить
for( int i( 0 ) ; i < t_man->LastRequestTime.Cursor ; i++ )
if( difftime( start , t_man->LastRequestTime[ i ] ) > TimeOut ){
if( t_man->Dinamic[ i ] && !t_man->Released[ i ] )
t_man->ReleaseTexture( i );
}
}
}
delete [] ( ( char * )PTR );
return( 0 );
}

Надеюсь после столь подробных комментариев, вопросов не осталось. На досуге можете подумать, как реализовать функцию GetSleepTime (возможно Вы сможете придумать что-то неординарное), я же сделал её такой:
int	cTextureManager::GetSleepTime( int TimeOut ){
time_t start( time() );
int dt( 0 );
int diff;
int e_time;
for( int i( 0 ) ; i < LastRequestTime.Cursor ; i++ ){
diff = difftime( start , LastRequestTime[ i ] );
dt = ( diff > dt ) ? diff : dt;
}
e_time = ( TimeOut - diff < 0 ) ? 0 : TimeOut - diff;
return( e_time );
}

Вот собственно и все, что я хотел рассказать Вам в этой статье. Напомню, что нашей целью (на первое время) являются классы для создания менюшек. Мы долго шли к этой цели, достойно перенося все лишения, и вот нам осталось сделать последний шаг. Но сделаем мы его в другой статье. Удачи!

к началу статьи

PS: замечания, предложения, пожелания как всегда на dodonov_a_a( ___AT )inbox.ru

PSS: В прилагающемся архиве лежит демка. Её суть в том, что текстура может устанавливаться двумя способами, при одном способе LastRequestTime[i] обновляется, а при другом нет, правой кнопкой мыши вы переходите в первый режим, а левой во второй. Изначально установлен второй режим, поэтому через 2-3 секунды после запуска программы, текстура исчезнет (менеджер её выгрузит). Вот теперь, кажется, всё!


Смежные вопросы:
Урок 1. Инициализация. Вершинные буфферы и камера.
Урок 2. Проигрывание медиа-файлов.
Урок 3. Автоматизация загрузки и управления аудио и видео ресурсами.
исходные коды
© 2004-2005 Savardge.ru
Hosted by uCoz