|
Текстурный
менеджер.
Предисловие.
Доброго времени суток всем, кто решил заняться текстурированием. Данная
тема очень простая, поэтому я решил в этой статье описать процесс
создания менеджера текстур, который может динамически
загружать/выгружать текстуры. Это может оказаться очень полезным при
большом количестве текстур и маленьком объеме видеопамяти. Итак,
приступим.
Класс
текстуры.
к началу статьи
|
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. Автоматизация загрузки и управления аудио и видео ресурсами.
исходные коды |
|
|