Главная
Стэнли Томас - Ваш сосед - миллионер
- Информация о материале
- Автор: gdever
- Категория: Разное
- Просмотров: 12799
Продолжая рецензии на прочитанные книги, хочется остановиться на достаточно подробном и, на мой взгляд, объективном исследовании, которое было оформлено в виде книги "Ваш сосед - миллионер".
Хотя книга и написана максимально доступно, её научность и обилие цифр весьма сильно усложняют восприятие. Однако, если вы любите серьёзные книги, а не "жёлтое" чтиво на пару часов, то эта книга для вас. Здесь можно найти массу информации и сделать весьма интересные, а иногда и совсем неожиданные выводы.
Гораздо чаще богатство – результат всей жизни, проведенной в тяжелом труде, упорстве, планировании и, прежде всего, самодисциплине.
Кто становится богатым? Как правило, богатый человек – это бизнесмен, который всю свою взрослую жизнь живот в одном городе.
Здесь на самом деле написано следующее - не надо эмигрировать и лучшая инвестиция это свой бизнес.
Постоянные зрители телешоу недаром болеют за участников, и не случайно такие программы занимают вершину рейтинга. Отождествляя себя с участниками, люди с восторгом смотрят, как они выигрывают автомобили, яхты, ценные вещи и денежные призы.
Мало кто задумывается о фантастической популярности программы "Поле чудес". Ну вопервых в ней участвуют простые смертные которые близки подавляющему большинству телезрителей. Вовторых это сама концепция игры - угадать простейшее слово и получить приз. Нет много призов. Все это делает программу чрезвычайно живучей. Действительно редкая программа на тв смогла продержаться хотя бы пять лет. Не говоря уже про двадцатник.
Вы когда-нибудь по-настоящему обращали внимание на людей, которые ежедневно занимаются оздоровительным бегом? Они выглядят так, будто никакой бег им не нужен. Именно бегу, они обязаны здоровьем. Богатые люди заботятся о своем финансовом здоровье, а те, у кого состояние финансов оставляет желать лучшего, практически не пытаются улучшить его.
Воистину по человеку сразу видно занимается он спортом или "занимается". Ну и по тому как он хранит свои деньги в кошельке многое можно сказать и о самом человеке и о его отношениях с деньгами.
Составлять бюджет легче, если помнить о выгодах этого в долгосрочной перспективе.
Это как с тайм-менеджментом - кажется что человек завален работой а по факту получается что действительно работы на пару часов. Так и с бюджетом. Кажется что все расходы нужные а в реальности деньги уходят на всякое барахло и мелочевку. Большенство же людей живет от зарплаты до зарплаты, демонстрируя вопиющую инфантильность.
Текстурирование, создание текстурного менеджера
- Информация о материале
- Автор: gdever
- Категория: DirectX 8
- Просмотров: 4163
Доброго времени суток всем, кто решил заняться текстурированием. Данная тема очень простая, поэтому кроме описания приемов работы с текстурами я расскажу как создать менеджер текстур, который может динамически загружать/выгружать текстуры. Это может оказаться очень полезным при большом количестве текстур и маленьком объеме видеопамяти. Итак, приступим.
Для начала рассмотрим класс текстуры, с помощью которого мы будем эти самые текстуры грузить, выгружать и делать разные интересные вещи.
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 );} };
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). Позднее Вы увидите, как пользоваться этой текстурой.
Вот небольшой пример, как пользоваться этим классом:
//класс вершины – ничего особенного #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<cTexture> Textures; cArray<cMutex> TextureMutexes; cArray<cMsg> Queue; cArray Dinamic; cArray Released; cArray<sTextureInfo> TexturesInfo; cArray<sTextureInfo> TexturePaths; cArray<time_t> 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 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: замечания, предложения, пожелания как всегда на
Введение в Direct3D
- Информация о материале
- Автор: gdever
- Категория: DirectX 8
- Просмотров: 4447
Доброго времени суток всем! Как я и обещал, эта статья будет посвящена инициализации Direct3d и вершинным буферам. Но прежде мы приступим к рисованию треугольников (никуда от них не деться), давайте задумаемся, что нам для этого нужно.
D3DDISPLAYMODE DisplayMode; D3DPRESENT_PARAMETERS PresentParameters;
D3DDISPLAYMODE – структура, хранящая формат режима (разрешение, частоту обновления и формат пикселей)
D3DPRESENT_PARAMETERS – хранит более тонкие настройки системы
Создание Direct3D объекта:
D3DObject = Direct3DCreate8( D3D_SDK_VERSION ); if( FAILED( D3DObject ) ){ MessageBox( NULL , "Error cretaing D3DObject!" , "Error" , MB_OK ); return(false); }
Устанавливаем параметры видеорежима
//ширина DisplayMode.Width = D3D_SCREEN_WIDTH; //высота DisplayMode.Height = D3D_SCREEN_HEIGHT; //частота обновления экрана DisplayMode.RefreshRate = 0; //формат пикселей DisplayMode.Format = D3DFMT_X8R8G8B8;
В приведенном коде строка DisplayMode.RefreshRate = 0 подразумевает, что будет выставлена частота адаптера по умолчанию (а не ноль Гц :) ).
Теперь настраиваем девайс:
memset( &PresentParameters , 0 , sizeof( D3DPRESENT_PARAMETERS ) ); PresentParameters.Windowed = FALSE; PresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; PresentParameters.BackBufferCount = 1; PresentParameters.BackBufferFormat = DisplayMode.Format;//(1) PresentParameters.BackBufferWidth = DisplayMode.Width;//(2) PresentParameters.BackBufferHeight = DisplayMode.Height;//(3) PresentParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; PresentParameters.EnableAutoDepthStencil = TRUE; PresentParameters.AutoDepthStencilFormat = D3DFMT_D24S8; PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
В данном фрагменте мы установили полноэкранный режим (PresentParameters.Windowed), “попросили” Direct3D очищать front-буфер перед тем как поменять его местами с back-буфером (D3DSWAPEFFECT_DISCARD), уточнили, что у нас будет только один back-буфер (PresentParameters.BackBufferCount), подогнали параметры буфера кадра под параметры видео режима (строки (1)(2)(3)), установили частоту обновления экрана (D3DPRESENT_RATE_DEFAULT) и формат буфера глубины(D3DFMT_D24S8 – 24 бита под буфер глубины и 8 под стенсил), и наконец отключили синхронизацию D3DPRESENT_INTERVAL_IMMEDIATE.
Осталось только создать девайс:
HRESULT hr = D3DObject->CreateDevice( D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd , D3DCREATE_HARDWARE_VERTEXPROCESSING , &PresentParameters, &D3DDevice ); if( FAILED( hr ) ){ if( D3DERR_NOTAVAILABLE )MessageBox( NULL , "Error creating D3DDevice( Unavaiable Mode )" , "Error" , MB_OK ); if( D3DERR_OUTOFVIDEOMEMORY )MessageBox( NULL , "Error creating D3DDevice( Out of memory )" , "Error" , MB_OK ); return( false ); }
По минимуму настроили рендеринг:
//отключили освещение (пока) D3DDevice->SetRenderState( D3DRS_LIGHTING , FALSE ); //отключили отсечение нелицевых граней D3DDevice->SetRenderState( D3DRS_CULLMODE , D3DCULL_NONE);
Осталось только установить проекционную матрицу:
D3DXMATRIX projection_matrix; //будем использовать правую систему координат – так привычнее //с углом обзора 45 градусов //создали матрицу D3DXMatrixPerspectiveFovRH(&projection_matrix, D3DX_PI/4f, (float)D3D_SCREEN_WIDTH/(float)D3D_SCREEN_HEIGHT, 0.1f, 1000000.0f ); //установили матрицу D3DDevice->SetTransform( D3DTS_PROJECTION, &(D3DMATRIX)projection_matrix );
Теперь нам нужен некоторый класс для представления вершины в пространстве + хотелось бы подвести под это хоть какой-то математический аппарат(я имею в виду удобный АПИ для работы с векторами и матрицами).
class cVector3{ public: //координаты float x,y,z; //конструкторы cVector3( void ){} cVector3( float f ){x=y=z=f;} cVector3( float f1 , float f2 , float f3 ){x=f1;y=f2;z=f3;} //вращение вокруг соответствующих осей void fRx( float ); void fRy( float ); void fRz( float ); //векторное сложение с присваиванием (сдвиг точки) void operator+=( cVector3 ); //векторное вычитание с присваиванием void operator-=( cVector3 ); //масштабирование вектора cVector3 operator*( float ); friend cVector3 operator*( float , cVector3 ); //векторная разность cVector3 operator-( cVector3 ); //сложение векторов(сдвиг точки) cVector3 operator+( cVector3 ); //унарный минус cVector3 operator-( void ); //скалярное произведение float operator%( cVector3 ); //векторное произведение cVector3 operator*( cVector3 ); //евклидова норма вектора float Norm( void ); //нормирование вектора void Normize( void ); };
Поподробнее остановимся на поворотах вокруг координатных осей. Для простоты будем рассматривать двумерный поворот вокруг начала координат:
а – начальное положение нашей точки.
b – конечное положение точки.
t2 – угол, на который будет совершаться поворот.
Воспользуемся представлением этих точек в полярных координатах:
(1) a.x = r * cos(t1) a.y = r * sin(t1) b.x = r * cos( t1 + t2 ) b.y = r * sin( t1 + t2 )
Вспомнив формулы для разложения синусов и косинусов двойных углов, имеем:
(2) b.x = r * cos(t1) * cos(t2) – r * sin(t1) * sin(t2) b.y = r * cos(t1) * cos(t2) + r * sin(t1) * sin(t2)
И, наконец, подставив (1) в систему (2), получим:
(3) b.x = a.x * cos(t2) – a.y * sin(t2) b.y = a.x * cos(t2) + a.y * sin(t2)
Осталось только заметить что при вращении вокруг оси Z, сама координата z точки остаётся неизменной, следовательно, добавив к (3) выражение b.z = a.z, получим искомое вращение вокруг оси Z. Для оставшихся двух вращений рассуждения будут аналогичными.
Теперь собственно сама вершина:
//описываем формат вершин (в 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 простой класс, т.к. сам по себе ничего не рендерит, а всю необходимую математику он наследует от вектора.
Теперь собственно можно и камеру позиционировать. Вот класс:
float D3D_CAM_VELOSITY = 0.01; float D3D_CAM_DANGLE = 0.03; class cCam{ public: cVector3 target,norm,eye; //********************************************************************* void D3DCameraReInit( void ); void D3DCameraInit( cVector3 , cVector3 , cVector3 ); };
Значение полей target,norm,eye показано на рисунке.
Создадим глобальный объект Camera и инициализируем его:
Camera.D3DCameraInit( cVector3( 0 , 0 , 1 ) , cVector3( 0 , 0 , -1 ) , cVector3( 0 , 1 , 0 ) ); D3DXMATRIX ViewMatrix; // View Matrix D3DXMatrixLookAtRH( &ViewMatrix , // Update View Matrix &D3DXVECTOR3( eye.x , eye.y , eye.z ), //здесь в качестве параметра используется точка на которую мы смотрим, а не направление взгляда &D3DXVECTOR3( eye.x + target.x , eye.y + target.y , eye.z + target.z ), &D3DXVECTOR3( norm.x , norm.y , norm.z ) ); //устанавливаем видовую матрицу D3DDevice->SetTransform( D3DTS_VIEW , &ViewMatrix );// Apply Changes To The View
Теперь наконец можно заняться рендеррингом, но сначала как всегда класс:
template<class Type>class cD3DVertexBuffer{ public: cD3DVertexBuffer(void); cD3DVertexBuffer(int){Vertexes.Cursor=0;} ~cD3DVertexBuffer(){Vertexes.Release();} cArray<Type> Vertexes; //добавление вершины __forceinline void AddVertex( Type ); //рендерринг void Render( LPDIRECT3DDEVICE8 , DWORD ); void FlushVertexes( LPDIRECT3DDEVICE8 ); //операции над всем массивом вершин – смещение по вектору и повороты void TTransfer( cVector3 ); void fRy( float ); void fRz( float ); void fRx( float ); //упростили себе доступ к вершинам Type operator[]( int i ){return( Vertexes[ i ] );} }; template<class Type>void cD3DVertexBuffer<Type>::AddVertex( Type v ){ Vertexes.AddEnd( v ); } template<class Type>void cD3DVertexBuffer<Type>::Render( LPDIRECT3DDEVICE8 D3DDevice , DWORD SHADER ){ D3DDevice->SetVertexShader( SHADER ); D3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST , Vertexes.Cursor / 3 , Vertexes.Array , sizeof( Type ) ); }
Собственно это и не вершинный буфер никакой, просто мне не хотелось придумывать какое-то особенное название, это усложнило бы восприятие программы, да и лениво как-то было :). Интерес представляет, пожалуй, только функция Render. В качестве параметра она принимает девайс и формат вершины (в нашем примере это будет FVF_CD3DVERTEX). Этот класс представляет простейшие возможности по отрисовки вершин – он не поддерживает индексирования, выводит вершины в нулевой поток с одним текстурным уровнем. Сделан был только для того что бы вы имели возможность, без написания огромных кусков кода инициализации, сесть и протестировать то что Вам нужно (просто вершины покидали в массив и все) + это представление данных позволяет достаточно просто изменять данные, хотя ради этого и приходится жертвовать производительностью.
Теперь настоящий ИНДЕКСИРУЕМЫЙ вершинный буфер:
template<class Type>class cD3DIVertexBuffer{ public: //указатель на индексный буфер LPDIRECT3DINDEXBUFFER8 D3DIndexBuffer; //указатель на вершинный буфер LPDIRECT3DVERTEXBUFFER8 D3DVertexBuffer; //число полигонов int NumberOfPolygons; //вершины cArray<Type> Vertexes; //индексы cArray Indexes; //добавить вершину с автоматическим индексированием void AddIVertex( Type ); //добавить вершину с ручными индексированием void AddVertex( Type , int ); //создать вершинный буфер в памяти(инициализация указателей //D3DindexBuffer и D3DVertexBuffer) void CreateVertexBuffer( LPDIRECT3DDEVICE8 , DWORD ); //рендерринг void Render( LPDIRECT3DDEVICE8 , DWORD ); ~cD3DIVertexBuffer(); //с помощью этих функций лочим и анлочим буфферы void Lock( void ); void Unlock( void ); }; template<class Type>void cD3DIVertexBuffer<Type>::Unlock( void ){ D3DVertexBuffer->Unlock(); D3DIndexBuffer->Unlock(); vrt = ind = NULL; } template<class Type>void cD3DIVertexBuffer<Type>::Lock( void ){ D3DVertexBuffer->Lock( 0 , 0 , ( BYTE** )&vrt , NULL ); D3DIndexBuffer->Lock( 0 , 0 , ( BYTE** )&ind , NULL ); } template<class Type>void cD3DIVertexBuffer<Type>::Release( void ){ if( D3DIndexBuffer ) D3DIndexBuffer->Release(); D3DIndexBuffer = NULL; if( D3DVertexBuffer ) D3DVertexBuffer->Release(); D3DVertexBuffer = NULL; Vertexes.Release(); Indexes.Release(); } template<class Type>void cD3DIVertexBuffer<Type>::fRx( float Angle ){ Lock(); for( int i( 0 ) ; i < NumberOfVertexes ; i++ ) ( ( Type* )vrt )[ i ].fRx( Angle ); Unlock(); } template<class Type>void cD3DIVertexBuffer<Type>::fRy( float Angle ){ Lock(); for( int i( 0 ) ; i < NumberOfVertexes ; i++ ) ( ( Type* )vrt )[ i ].fRy( Angle ); Unlock(); } template<class Type>void cD3DIVertexBuffer<Type>::fRz( float Angle ){ Lock(); for( int i( 0 ) ; i < NumberOfVertexes ; i++ ) ( ( Type* )vrt )[ i ].fRz( Angle ); Unlock(); } template<class Type>void cD3DIVertexBuffer<Type>::AddVertex( Type Vertex , int i ){ Indexes.AddEnd( i ); Vertexes.AddEnd( Vertex ); } template<class Type>void cD3DIVertexBuffer<Type>::AddIVertex( Type Vertex ){ Indexes.AddEnd( Vertexes.Cursor ); Vertexes.AddEnd( Vertex ); } //передаем девайс и формат вершин template<class Type>void cD3DIVertexBuffer<Type>::Render( cD3DDevice &D3DDevice , DWORD VERTEX_FORMAT ){ //устанавливаем поток для вывода D3DDevice->SetStreamSource( 0 , D3DVertexBuffer , sizeof( Type ) ); D3DDevice->SetIndices( D3DIndexBuffer , 0 ); //устанавливаем формат вершин D3DDevice->SetVertexShader( VERTEX_FORMAT ); //отрисовываем вершины D3DDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST , 0 , NumberOfVertexes , 0 , NumberOfPolygons ); } template<class Type>void cD3DIVertexBuffer<Type>::CreateVertexBuffer( LPDIRECT3DDEVICE8 D3DDevice , DWORD VERTEX_FORMAT ){ //создание пустого вершинного буфера //D3DPOOL_DEFAULT – система сама решит куда лучше положить ваши //вершины //в системную память или в память видеокарты if( FAILED( D3DDevice->CreateVertexBuffer( Vertexes.Cursor * sizeof( Type ) , 0 , VERTEX_FORMAT , D3DPOOL_DEFAULT, &D3DVertexBuffer ) ) ){ MessageBox( 0 , "Ошибка создания индексируемого вершинного буффера." , "Ошибка." , 0 ); exit(0); } void *vVertexes; void *vIndexes; //заполняем вершинный буфер вершинами //в Direct3D лочатся не только вершинные буфферы но и текстуры, запомните это или запишите )) //этим Direct3D существенно отличается от OGL, там вроде такого механизма нет if( FAILED( D3DVertexBuffer->Lock( 0 , Vertexes.Cursor * sizeof( Type ) , ( BYTE** )&vVertexes , D3DLOCK_READONLY ) ) ){ MessageBox( 0 , "Ошибка залочивания индексируемого вершинного буффера." , "Ошибка." , 0 ); exit(0); } memcpy( vVertexes , Vertexes.Array , Vertexes.Cursor * sizeof( Type ) ); D3DVertexBuffer->Unlock(); //создание пустого индексного буфера //D3DPOOL_DEFAULT – система сама решит куда лучше положить ваши индексы //в системную память или в память видеокарты //D3DFMT_INDEX16 – в качестве индекса будет использоваться 16 разрядное //целое if( Indexes.Cursor ){ if( FAILED( D3DDevice->CreateIndexBuffer( sizeof( unsigned long ) * Indexes.Cursor , 0 , D3DFMT_INDEX16, D3DPOOL_DEFAULT , &D3DIndexBuffer ) ) ){ MessageBox( 0 , "Ошибка создания индексного буффера." , "Ошибка." , 0 ); exit(0); } //заполняем индексный буфер индексами if( FAILED ( D3DIndexBuffer->Lock( 0 , Indexes.Cursor * sizeof( unsigned long ) , ( BYTE** )&vIndexes , D3DLOCK_READONLY ) ) ){ MessageBox( 0 , "Ошибка залочивания индексного буффера." , "Ошибка." , 0 ); exit(0); } memcpy( vIndexes , Indexes.Array , sizeof( unsigned long ) * Indexes.Cursor ); D3DIndexBuffer->Unlock(); } //заметьте, число полигонов рассчитывается исходя из числа индексов, а //не вершин NumberOfPolygons = Indexes.Cursor / 3; //освобождаем память Vertexes.Release(); Indexes.Release(); }
Вот вроде и все. Осталось только сказать зачем нужны индексы: с их помощью можно существенно уменьшить объем вводимых данных. Например нам надо вывести квадрат (два треугольника с двумя смежными вершинами). Без индексов нам пришлось бы создавать буфер на 6 вершин, а используя индексы, в вершинном буфере будут лежать всего 4 вершины + 6 индексов в индексном буфере (по три на полигон). Вот эти индексы: 0,1,2; 2,1,3 (при условии, что 1 и 2 вершины общие для двух полигонов).
Вот теперь действительно все.
Семь раз отмерь
- Информация о материале
- Автор: gdever
- Категория: Лирика
- Просмотров: 3844
Продолжая разговор про бестолковых людей.
У большинства людей вообще не принято думать перед тем как делать, потому что банальный вопрос "почему было сделано именно так?" неизменно ставит их в тупик. Нет они конечно начинают лепетать, что они мол думали что это сработает. Но ничего более конкретного. Я сам сравнительно недавно обзавёлся привычкой перед тем как сделать более менее важное действие подыскивать веские аргументы почему это надо сделать и почему это действие увенчается успехом. Один мой знакомый, надо сказать весьма преуспевающий и деловой, решил заработать продажей ссылок со свох сайтов. Ухнул кучу денег для создания нормальных (не говно-) сайтов и сел ждать фантастических доходов. Идея в общем-то здравая - заполучить источник пассивного дохода. Но когда он понял какого размера будет этот доход, он мягко говоря опечалился. Я, не скрою, сам размышлял над чем-то подобным, однако у меня все закончилось на этапе оценки стоимости одного сайта и периода, за который сайт окупится. Получив неудовлетворительные оценки и не потратив ни копейки, я успокоился и начал прорабатывать другие варианты зарабатывания денег.
Страница 6 из 8