пятница, 9 июля 2010 г.

Генерация текстурного атласа

Давным-давно, в далёкой-далёкой галактике Художники подумали и решили, что слишком много времени они тратят на сборку текстурных атласов. А раз есть у них Программист, то пускай он мучается с этой проблемой. Почесал Программист репу и начал думать. Первой мыслью было использовать программы для автоматической генерации атласов. Мысль вроде хорошая, но Художники народ ленивый, не станут они марать свои творческие руки о прах бытия. Так что Программист решил ввести функцию сборки атласов в движок, дабы С++ отдувался и за него, и за художников :) Что для этого потребовалось: 1. Всего-лишь библиотека FreeLib и немного размышлений. Для начала, нам нужно загрузить PNG текстуру в оперативную память
void C_Texture_Loader::Add(std::wstring Name, std::wstring Path, int Width, int Height){
FIBITMAP *dib = FreeImage_LoadU(FIF_PNG, (wchar_t*)Path.c_str(), PNG_DEFAULT);  
DraftTextures[Name].dib = dib;
DraftTextures[Name].Width = FreeImage_GetWidth(dib);
DraftTextures[Name].Height = FreeImage_GetHeight(dib);
DraftTextures[Name].Pitch = FreeImage_GetPitch(dib);
}
Тут в принципе всё должно быть понятно. В переменную dib загоняем указатель на текстуру, а в ассоциативный массив (std::map) загоняем данные, необходимые для дальнейшего построения атласа.


void C_Texture_Loader::GenerateTextureAtlases()
{
 if (DraftTextures.size() == 0)return;
 NextX=0;
 NextY=0;
 CurrentTexture=0;
 MaxY=0;
 std::wstring CurrentTextureS;

 CurrentTextureS = L"t"+IntToWString(CurrentTexture);
 TA_Count++; //Количество текстур

 //Создаём текстуру 1024*1024
 D3DXCreateTexture(DirectX_Device->Device,1024,1024,1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,&texture[CurrentTextureS].Texture);
 D3DLOCKED_RECT RectLock;
 RECT r;
 r.left = r.top = 0;
 r.right = 1024;
 r.bottom = 1024;   
 //Лочим текстру
 texture[CurrentTextureS].Texture->LockRect(0,&RectLock,&r,0); 
 BYTE* PixelMap = (BYTE *)RectLock.pBits;

 DraftTextures_Iter = DraftTextures.begin();
 for (int i=0; i<DraftTextures.size(); i++)
 {
  //Проверка, что текущая текстура выходит за пределы атласа
  //Если выходит - тогда создаём новый атлас
  if (DraftTextures_Iter->second.Height > MaxY)
  {
   MaxY = DraftTextures_Iter->second.Height;
  }
  if (NextX+DraftTextures_Iter->second.Width > 1024 || NextY+DraftTextures_Iter->second.Height > 1024)  
  {
   NextX=0;
   NextY+=MaxY+1;
   MaxY=0;
   if (NextY>1024||NextY+DraftTextures_Iter->second.Height >= 1024)
   {
    NextY=0;
    MaxY = DraftTextures_Iter->second.Height;
    texture[CurrentTextureS].Texture->UnlockRect(0);   
    TA_Count++;
    CurrentTexture++;    
    CurrentTextureS = L"t"+IntToWString(CurrentTexture);
    D3DXCreateTexture(DirectX_Device->Device,1024,1024,1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,&texture[CurrentTextureS].Texture);
    texture[CurrentTextureS].Texture->LockRect(0,&RectLock,&r,0);       
    PixelMap = (BYTE *)RectLock.pBits;
   }
  }
  //BitsOffset - Количество байтов в строке изображения
  int BitsOffset = DraftTextures_Iter->second.Pitch/DraftTextures_Iter->second.Width;
  BYTE * bits = FreeImage_GetBits(DraftTextures_Iter->second.dib);        
  //Переход к последнему байту изображения
  //Считываем изображение задом наперёд, потому что
  //оно хранится в памяти перевёрнутым
  bits+=DraftTextures_Iter->second.Pitch*DraftTextures_Iter->second.Height-DraftTextures_Iter->second.Pitch;
  for (int y=NextY; y<NextY+DraftTextures_Iter->second.Height; y++)
  {
   BYTE * pixel = (BYTE*)bits;
   //Попиксельный проход
   for (int x=NextX; x<NextX+DraftTextures_Iter->second.Width; x++)
   {    
    PixelMap[x*4+(RectLock.Pitch*(y))]     = pixel[0];//blue
    PixelMap[x*4+(RectLock.Pitch*(y))+1] = pixel[1];//green
    PixelMap[x*4+(RectLock.Pitch*(y))+2] = pixel[2];//red
    //BitsOffset равняется 4 тогда, когда у изображения есть альфа канал
    if (BitsOffset == 4)    
     PixelMap[x*4+(RectLock.Pitch*(y))+3] = pixel[3];//alpha    
    else
     PixelMap[x*4+(RectLock.Pitch*(y))+3] = 255;
    pixel+=BitsOffset;
   }
   bits-=DraftTextures_Iter->second.Pitch;
  }
  NextX+=DraftTextures_Iter->second.Width+1;
  DraftTextures_Iter++;  
 }
 texture[CurrentTextureS].Texture->UnlockRect(0); 
}

Комментариев нет:

Отправить комментарий