//--------------------------------------------------------------------------- /* MaSzyna EU07 locomotive simulator Copyright (C) 2001-2004 Marcin Wozniak and others */ #include #include #include "opengl/glew.h" #include #include "system.hpp" #include "classes.hpp" #include "stdio.h" #pragma hdrstop #include "Usefull.h" #include "Texture.h" #include "TextureDDS.h" #include "logs.h" #include "Globals.h" #include "io.h" TTexturesManager::Alphas TTexturesManager::_alphas; TTexturesManager::Names TTexturesManager::_names; void TTexturesManager::Init(){}; TTexturesManager::Names::iterator TTexturesManager::LoadFromFile(std::string fileName, int filter) { std::string message("Loading - texture: "); std::string realFileName(fileName); std::ifstream file(fileName.c_str()); // Ra: niby bez tego jest lepiej, ale działa gorzej, więc przywrócone jest oryginalne if (!file.is_open()) realFileName.insert(0, szTexturePath); else file.close(); // char* cFileName = const_cast(fileName.c_str()); message += realFileName; WriteLog(message.c_str()); // Ra: chybaa miało być z komunikatem z przodu, a nie tylko nazwa size_t pos = fileName.rfind('.'); std::string ext(fileName, pos + 1, std::string::npos); AlphaValue texinfo; if (ext == "tga") texinfo = LoadTGA(realFileName, filter); else if (ext == "tex") texinfo = LoadTEX(realFileName); else if (ext == "bmp") texinfo = LoadBMP(realFileName); else if (ext == "dds") texinfo = LoadDDS(realFileName, filter); _alphas.insert( texinfo); // zapamiętanie stanu przezroczystości tekstury - można by tylko przezroczyste std::pair ret = _names.insert(std::make_pair(fileName, texinfo.first)); if (!texinfo.first) { WriteLog("Failed"); ErrorLog("Missed texture: " + AnsiString(realFileName.c_str())); return _names.end(); }; _alphas.insert(texinfo); ret = _names.insert( std::make_pair(fileName, texinfo.first)); // dodanie tekstury do magazynu (spisu nazw) // WriteLog("OK"); //Ra: "OK" nie potrzeba, samo "Failed" wystarczy return ret.first; }; struct ReplaceSlash { const char operator()(const char input) { return input == '/' ? '\\' : input; } }; GLuint TTexturesManager::GetTextureID(char *dir, char *where, std::string fileName, int filter) { // ustalenie numeru tekstury, wczytanie jeśli nie jeszcze takiej nie było /* // Ra: niby tak jest lepiej, ale działa gorzej, więc przywrócone jest oryginalne //najpierw szukamy w katalogu, z którego wywoływana jest tekstura, potem z wyższego //Ra: przerobić na wyszukiwanie w drzewie nazw, do którego zapisywać np. rozmiary, przezroczystość //Ra: ustalać, które tekstury można wczytać już w trakcie symulacji size_t pos=fileName.find(':'); //szukamy dwukropka if (pos!=std::string::npos) //po dwukropku mogą być podane dodatkowe informacje fileName=fileName.substr(0,pos); //niebędące nazwą tekstury std::transform(fileName.begin(),fileName.end(),fileName.begin(),ReplaceSlash()); //zamiana "/" na "\" //jeśli bieżaca ścieżka do tekstur nie została dodana to dodajemy domyślną //if (fileName.find('\\')==std::string::npos) //bz sensu // fileName.insert(0,szDefaultTexturePath); //najpierw szukamy w podanym katalogu, potem w domyślnym Names::iterator iter; std::ifstream file; if ((fileName.find('.')==fileName.npos)?true:(fileName.rfind('.')second; //znalezione! if (dir) {//może we wskazanym katalogu? test=fileName; test.insert(0,dir); //jeszcze próba z dodatkową ścieżką test.append(Global::szDefaultExt[i]); //dodanie jednego z kilku rozszerzeń iter=_names.find(test); //czy mamy już w magazynie? if (iter!=_names.end()) return iter->second; //znalezione! } //} //for (int i=0;i<4;++i) //{//w magazynie nie ma, to sprawdzamy na dysku test=fileName; if (where) test.insert(0,where); //ścieżka obiektu wywołującego test.append(Global::szDefaultExt[i]); //dodanie jednego z kilku rozszerzeń file.open(test.c_str()); if (!file.is_open()) {test=fileName; if (dir) test.insert(0,dir); //próba z dodatkową ścieżką test.append(Global::szDefaultExt[i]); //dodanie jednego z kilku rozszerzeń file.open(test.c_str()); } if (file.is_open()) {//jak znaleziony, to plik zostaje otwarty fileName=test; //zapamiętanie znalezionego rozszerzenia break; //wyjście z pętli na etapie danego rozszerzenia } } } else {//gdy jest kropka, to rozszerzenie jest jawne std::string test; //zmienna robocza //najpierw szukamy w magazynie test=fileName; if (where) test.insert(0,where); //ścieżka obiektu wywołującego iter=_names.find(test); //czy mamy już w magazynie? if (iter!=_names.end()) return iter->second; //znalezione! test=fileName; if (dir) test.insert(0,dir); //jeszcze próba z dodatkową ścieżką iter=_names.find(test); //czy mamy już w magazynie? if (iter!=_names.end()) return iter->second; //znalezione! //w magazynie nie ma, to sprawdzamy na dysku test=fileName; if (where) test.insert(0,where); //ścieżka obiektu wywołującego file.open(test.c_str()); if (!file.is_open()) {//jak znaleziony, to plik zostaje otwarty test=fileName; if (dir) test.insert(0,dir); //próba z dodatkową ścieżką file.open(test.c_str()); if (file.is_open()) fileName=test; //ustalenie nowej nazwy } } if (file.is_open()) {//plik pozostaje otwarty, gdy znaleziono na dysku file.close(); //można już zamknąć iter=LoadFromFile(fileName,filter); //doda się do magazynu i zwróci swoją pozycję } */ size_t pos = fileName.find(':'); // szukamy dwukropka if (pos != std::string::npos) // po dwukropku mogą być podane dodatkowe informacje fileName = fileName.substr(0, pos); // niebędące nazwą tekstury pos = fileName.find('|'); // szukamy separatora tekstur if (pos != std::string::npos) // po | może być nazwa kolejnej tekstury fileName = fileName.substr(0, pos); // i trzeba to obciąć std::transform(fileName.begin(), fileName.end(), fileName.begin(), ReplaceSlash()); // jeśli bieżaca ścieżka do tekstur nie została dodana to dodajemy domyślną if (fileName.find('\\') == std::string::npos) fileName.insert(0, szTexturePath); Names::iterator iter; if (fileName.find('.') == std::string::npos) { // Ra: wypróbowanie rozszerzeń po kolei, zaczynając od domyślnego fileName.append("."); // kropka będze na pewno, resztę trzeba próbować std::string test; // zmienna robocza for (int i = 0; i < 4; ++i) { // najpierw szukamy w magazynie test = fileName; test.append(Global::szDefaultExt[i]); iter = _names.find(fileName); // czy mamy już w magazynie? if (iter != _names.end()) return iter->second; // znalezione! test.insert(0, szTexturePath); // jeszcze próba z dodatkową ścieżką iter = _names.find(fileName); // czy mamy już w magazynie? if (iter != _names.end()) return iter->second; // znalezione! } for (int i = 0; i < 4; ++i) { // w magazynie nie ma, to sprawdzamy na dysku test = fileName; test.append(Global::szDefaultExt[i]); std::ifstream file(test.c_str()); if (!file.is_open()) { test.insert(0, szTexturePath); file.open(test.c_str()); } if (file.is_open()) { fileName.append(Global::szDefaultExt[i]); // dopisanie znalezionego file.close(); break; // wyjście z pętli na etapie danego rozszerzenia } } } iter = _names.find(fileName); // czy mamy już w magazynie if (iter == _names.end()) iter = LoadFromFile(fileName, filter); return (iter != _names.end() ? iter->second : 0); }; bool TTexturesManager::GetAlpha(GLuint id) { // atrybut przezroczystości dla tekstury o podanym numerze (id) Alphas::iterator iter = _alphas.find(id); return (iter != _alphas.end() ? iter->second : false); } TTexturesManager::AlphaValue TTexturesManager::LoadBMP(std::string fileName) { AlphaValue fail(0, false); std::ifstream file(fileName.c_str(), std::ios::binary); if (!file.is_open()) { // file.close(); return fail; }; BITMAPFILEHEADER header; size_t bytes; file.read((char *)&header, sizeof(BITMAPFILEHEADER)); if (file.eof()) { file.close(); return fail; } // Read in bitmap information structure BITMAPINFO info; long infoSize = header.bfOffBits - sizeof(BITMAPFILEHEADER); file.read((char *)&info, infoSize); if (file.eof()) { file.close(); return fail; }; GLuint width = info.bmiHeader.biWidth; GLuint height = info.bmiHeader.biHeight; unsigned long bitSize = info.bmiHeader.biSizeImage; if (!bitSize) bitSize = (width * info.bmiHeader.biBitCount + 7) / 8 * height; GLubyte *data = new GLubyte[bitSize]; file.read((char *)data, bitSize); if (file.eof()) { delete[] data; file.close(); return fail; }; file.close(); GLuint id; glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // This is specific to the binary format of the data read in. glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, data); delete[] data; return std::make_pair(id, false); }; TTexturesManager::AlphaValue TTexturesManager::LoadTGA(std::string fileName, int filter) { AlphaValue fail(0, false); int writeback = -1; //-1 plik jest OK, >=0 - od którego bajtu zapisać poprawiony plik GLubyte TGACompheader[] = {0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // uncompressed TGA header GLubyte TGAcompare[12]; // used to compare TGA header GLubyte header[6]; // first 6 useful bytes from the header std::fstream file(fileName.c_str(), std::ios::binary | std::ios::in); file.read((char *)TGAcompare, sizeof(TGAcompare)); file.read((char *)header, sizeof(header)); // std::cout << file.tellg() << std::endl; if (file.eof()) { file.close(); return fail; }; bool compressed = (memcmp(TGACompheader, TGAcompare, sizeof(TGACompheader)) == 0); GLint width = header[1] * 256 + header[0]; // determine the TGA width (highbyte*256+lowbyte) GLint height = header[3] * 256 + header[2]; // determine the TGA height (highbyte*256+lowbyte) // check if width, height and bpp is correct if (!width || !height || (header[4] != 24 && header[4] != 32)) { WriteLog("Bad texture: " + AnsiString(fileName.c_str()) + " has wrong header or bits per pixel"); file.close(); return fail; }; { // sprawdzenie prawidłowości rozmiarów int i, j; for (i = width, j = 0; i; i >>= 1) if (i & 1) ++j; if (j == 1) for (i = height, j = 0; i; i >>= 1) if (i & 1) ++j; if (j != 1) WriteLog("Bad texture: " + AnsiString(fileName.c_str()) + " is " + AnsiString(width) + "×" + AnsiString(height)); } GLuint bpp = header[4]; // grab the TGA's bits per pixel (24 or 32) GLuint bytesPerPixel = bpp / 8; // divide by 8 to get the bytes per pixel GLuint imageSize = width * height * bytesPerPixel; // calculate the memory required for the TGA data GLubyte *imageData = new GLubyte[imageSize]; // reserve memory to hold the TGA data if (!compressed) { // WriteLog("Not compressed."); file.read(imageData, imageSize); if (file.eof()) { delete[] imageData; file.close(); return fail; }; } else { // skompresowany plik TGA GLuint filesize; // current byte GLuint colorbuffer[1]; // Storage for 1 pixel file.seekg(0, ios::end); // na koniec filesize = (int)file.tellg() - 18; // rozmiar bez nagłówka file.seekg(18, ios::beg); // ponownie za nagłówkiem GLubyte *copyto = imageData; // gdzie wstawiać w buforze GLubyte *copyend = imageData + imageSize; // za ostatnim bajtem bufora GLubyte *copyfrom = imageData + imageSize - filesize; // gdzie jest początek int chunkheader = 0; // Ra: będziemy wczytywać najmłodszy bajt if (filesize < imageSize) // jeśli po kompresji jest mniejszy niż przed { // Ra: nowe wczytywanie skompresowanych: czytamy całe od razu, dekompresja w pamięci GLuint copybytes; file.read(copyfrom, filesize); // wczytanie reszty po nagłówku // najpierw trzeba ustalić, ile skopiowanych pikseli jest na samym końcu copyto = copyfrom; // roboczo przelatujemy wczytane dane copybytes = 0; // licznik bajtów obrazka while (copybytes < imageSize) { chunkheader = (unsigned char)*copyto; // jeden bajt, pozostałe zawsze zerowe copyto += 1 + bytesPerPixel; // bajt licznika oraz jeden piksel jest zawsze copybytes += (1 + (chunkheader & 127)) * bytesPerPixel; // ilość pikseli if (chunkheader < 128) // jeśli kopiowanie, pikseli jest więcej copyto += (chunkheader)*bytesPerPixel; // rozmiar kopiowanego obszaru (bez // jednego piksela) } if (copybytes > imageSize) { // nie ma prawa być większe WriteLog("Compression error"); delete[] imageData; file.close(); return fail; } // na końcu mogą być śmieci int extraend = copyend - copyto; // długość śmieci na końcu if (extraend > 0) { // przesuwamy bufor do końca obszaru dekompresji WriteLog("Extra bytes: " + AnsiString(extraend)); memmove(copyfrom + extraend, copyfrom, filesize - extraend); copyfrom += extraend; file.close(); filesize -= extraend; // to chyba nie ma znaczenia if (Global::iModifyTGA & 2) // flaga obcinania śmieci { // najlepiej by było obciąć plik, ale fstream tego nie potrafi int handle; for (unsigned int i = 0; i < fileName.length(); ++i) if (fileName[i] == '/') fileName[i] == '\\'; // bo to Windows WriteLog("Truncating extra bytes"); handle = open(fileName.c_str(), O_RDWR | O_BINARY); chsize(handle, 18 + filesize); // obcięcie śmieci close(handle); extraend = 0; // skoro obcięty, to się już nie liczy } file.open(fileName.c_str(), std::ios::binary | std::ios::in); } if (chunkheader < 128) // jeśli ostatnie piksele są kopiowane copyend -= (1 + chunkheader) * bytesPerPixel; // bajty kopiowane na końcu nie podlegające dekompresji else copyend -= bytesPerPixel; // ostatni piksel i tak się nie zmieni copyto = imageData; // teraz będzie wypełnianie od początku obszaru while (copyto < copyend) { chunkheader = (unsigned char)*copyfrom; // jeden bajt, pozostałe zawsze zerowe if (copyto > copyfrom) { // jeśli piksele mają być kopiowane, to możliwe jest przesunięcie ich o 1 bajt, na // miejsce licznika filesize = (imageData + imageSize - copyto) / bytesPerPixel; // ile pikseli pozostało do końca // WriteLog("Decompression buffer overflow at pixel // "+AnsiString((copyto-imageData)/bytesPerPixel)+"+"+AnsiString(filesize)); // pozycję w pliku trzeba by zapamietać i po wczytaniu reszty pikseli starą // metodą // zapisać od niej dane od (copyto), poprzedzone bajtem o wartości (filesize-1) writeback = imageData + imageSize + extraend - copyfrom; // ile bajtów skompresowanych zostało do końca copyfrom = copyto; // adres piksela do zapisania file.seekg(-writeback, ios::end); // odległość od końca (ujemna) if ((filesize > 128) || !(Global::iModifyTGA & 4)) // gdy za dużo pikseli albo wyłączone writeback = -1; // zapis możliwe jeśli ilość problematycznych pikseli nie // przekaracza 128 break; // bufor się zatkał, dalej w ten sposób się nie da } if (chunkheader < 128) { // dla nagłówka < 128 mamy podane ile pikseli przekopiować minus 1 copybytes = (++chunkheader) * bytesPerPixel; // rozmiar kopiowanego obszaru memcpy(copyto, ++copyfrom, copybytes); // skopiowanie tylu bajtów copyto += copybytes; copyfrom += copybytes; } else { // chunkheader > 128 RLE data, next color reapeated chunkheader - 127 times chunkheader -= 127; // copy the color into the image data as many times as dictated if (bytesPerPixel == 4) { // przy czterech bajtach powinno być szybsze używanie int __int32 *ptr = (__int32 *)(copyto); // wskaźnik na int __int32 bgra = *((__int32 *)++copyfrom); // kolor wypełniający (4 bajty) for (int counter = 0; counter < chunkheader; counter++) *ptr++ = bgra; copyto = (char *)(ptr); // rzutowanie, żeby nie dodawać copyfrom += 4; } else { colorbuffer[0] = *((int *)(++copyfrom)); // pobranie koloru (3 bajty) for (int counter = 0; counter < chunkheader; counter++) { // by the header memcpy(copyto, colorbuffer, 3); copyto += 3; } copyfrom += 3; } } } // while (copyto128 RLE data, next color reapeated (chunkheader-127) times chunkheader -= 127; file.read((char *)colorbuffer, bytesPerPixel); // copy the color into the image data as many times as dictated if (bytesPerPixel == 4) { // przy czterech bajtach powinno być szybsze używanie int __int32 *ptr = (__int32 *)(copyto), bgra = *((__int32 *)colorbuffer); for (int counter = 0; counter < chunkheader; counter++) *ptr++ = bgra; copyto = (char *)(ptr); // rzutowanie, żeby nie dodawać } else for (int counter = 0; counter < chunkheader; counter++) { // by the header memcpy(copyto, colorbuffer, bytesPerPixel); copyto += bytesPerPixel; } } } // while (copyto= 0) { // zapisanie pliku file.close(); // tamten zamykamy, bo był tylko do odczytu if (writeback) { // zapisanie samej końcówki pliku, która utrudnia dekompresję w buforze WriteLog("Rewriting end of file..."); chunkheader = filesize - 1; // licznik jest o 1 mniejszy file.open(fileName.c_str(), std::ios::binary | std::ios::out | std::ios::in); file.seekg(-writeback, ios::end); // odległość od końca (ujemna) file.write((char *)&chunkheader, 1); // zapisanie licznika file.write(copyfrom, filesize * bytesPerPixel); // piksele bez kompresji } else { // zapisywanie całości pliku, będzie krótszy, więc trzeba usunąć go w całości WriteLog("Writing uncompressed file..."); TGAcompare[2] = 2; // bez kompresji file.open(fileName.c_str(), std::ios::binary | std::ios::out | std::ios::trunc); file.write((char *)TGAcompare, sizeof(TGAcompare)); file.write((char *)header, sizeof(header)); file.write(imageData, imageSize); } } }; file.close(); // plik zamykamy dopiero na samym końcu bool alpha = (bpp == 32); bool hash = (fileName.find('#') != std::string::npos); // true gdy w nazwie jest "#" bool dollar = (fileName.find('$') == std::string::npos); // true gdy w nazwie nie ma "$" size_t pos = fileName.rfind('%'); // ostatni % w nazwie if (pos != std::string::npos) if (pos < fileName.size()) { filter = (int)fileName[pos + 1] - '0'; // zamiana cyfry za % na liczbę if ((filter < 0) || (filter > 10)) filter = -1; // jeśli nie jest cyfrą } if (!alpha && !hash && dollar && (filter < 0)) filter = Global::iDefaultFiltering; // dotyczy tekstur TGA bez kanału alfa // ewentualne przeskalowanie tekstury do dopuszczalnego rozumiaru GLint w = width, h = height; if (width > Global::iMaxTextureSize) width = Global::iMaxTextureSize; // ogranizczenie wielkości if (height > Global::iMaxTextureSize) height = Global::iMaxTextureSize; if ((w != width) || (h != height)) { // przeskalowanie tekstury, żeby się nie wyświetlała jako biała GLubyte *imgData = new GLubyte[width * height * bytesPerPixel]; // nowy rozmiar gluScaleImage(bytesPerPixel == 3 ? GL_RGB : GL_RGBA, w, h, GL_UNSIGNED_BYTE, imageData, width, height, GL_UNSIGNED_BYTE, imgData); delete imageData; // usunięcie starego imageData = imgData; } GLuint id = CreateTexture(imageData, (alpha ? GL_BGRA : GL_BGR), width, height, alpha, hash, dollar, filter); delete[] imageData; ++Global::iTextures; return std::make_pair(id, alpha); }; TTexturesManager::AlphaValue TTexturesManager::LoadTEX(std::string fileName) { AlphaValue fail(0, false); std::ifstream file(fileName.c_str(), ios::binary); char head[5]; file.read(head, 4); head[4] = 0; bool alpha; if (std::string("RGB ") == head) { alpha = false; } else if (std::string("RGBA") == head) { alpha = true; } else { std::string message("Unrecognized texture format: "); message += head; Error(message.c_str()); return fail; }; GLuint width; GLuint height; file.read((char *)&width, sizeof(int)); file.read((char *)&height, sizeof(int)); GLuint bpp = alpha ? 4 : 3; GLuint size = width * height * bpp; GLubyte *data = new GLubyte[size]; file.read(data, size); bool hash = (fileName.find('#') != std::string::npos); GLuint id = CreateTexture(data, (alpha ? GL_RGBA : GL_RGB), width, height, alpha, hash); delete[] data; return std::make_pair(id, alpha); }; TTexturesManager::AlphaValue TTexturesManager::LoadDDS(std::string fileName, int filter) { AlphaValue fail(0, false); std::ifstream file(fileName.c_str(), ios::binary); char filecode[5]; file.read(filecode, 4); filecode[4] = 0; if (std::string("DDS ") != filecode) { file.close(); return fail; }; DDSURFACEDESC2 ddsd; file.read((char *)&ddsd, sizeof(ddsd)); DDS_IMAGE_DATA data; // // This .dds loader supports the loading of compressed formats DXT1, DXT3 // and DXT5. // GLuint factor; switch (ddsd.ddpfPixelFormat.dwFourCC) { case FOURCC_DXT1: // DXT1's compression ratio is 8:1 data.format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; factor = 2; break; case FOURCC_DXT3: // DXT3's compression ratio is 4:1 data.format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; factor = 4; break; case FOURCC_DXT5: // DXT5's compression ratio is 4:1 data.format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; factor = 4; break; default: file.close(); return fail; } GLuint bufferSize = (ddsd.dwMipMapCount > 1 ? ddsd.dwLinearSize * factor : ddsd.dwLinearSize); data.pixels = new GLubyte[bufferSize]; file.read((char *)data.pixels, bufferSize); file.close(); data.width = ddsd.dwWidth; data.height = ddsd.dwHeight; data.numMipMaps = ddsd.dwMipMapCount; { // sprawdzenie prawidłowości rozmiarów int i, j; for (i = data.width, j = 0; i; i >>= 1) if (i & 1) ++j; if (j == 1) for (i = data.height, j = 0; i; i >>= 1) if (i & 1) ++j; if (j != 1) WriteLog("Bad texture: " + AnsiString(fileName.c_str()) + " is " + AnsiString(data.width) + "×" + AnsiString(data.height)); } if (ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) data.components = 3; else data.components = 4; data.blockSize = (data.format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16); GLuint id; glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id); if (filter >= 0) SetFiltering(filter); // cyfra po % w nazwie else // SetFiltering(bHasAlpha&&bDollar,bHash); //znaki #, $ i kanał alfa w nazwie SetFiltering(data.components == 4, fileName.find('#') != std::string::npos); GLuint offset = 0; int firstMipMap = 0; while ((data.width > Global::iMaxTextureSize) || (data.height > Global::iMaxTextureSize)) { // pomijanie zbyt dużych mipmap, jeśli wymagane jest ograniczenie rozmiaru offset += ((data.width + 3) / 4) * ((data.height + 3) / 4) * data.blockSize; data.width /= 2; data.height /= 2; firstMipMap++; }; for (int i = 0; i < data.numMipMaps - firstMipMap; i++) { // wczytanie kolejnych poziomów mipmap if (!data.width) data.width = 1; if (!data.height) data.height = 1; GLuint size = ((data.width + 3) / 4) * ((data.height + 3) / 4) * data.blockSize; if (Global::bDecompressDDS) { // programowa dekompresja DDS // if (i==1) //should be i==0 but then problem with "glBindTexture()" { GLuint decomp_size = data.width * data.height * 4; GLubyte *output = new GLubyte[decomp_size]; DecompressDXT(data, data.pixels + offset, output); glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA, data.width, data.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, output); delete[] output; } } else // przetwarzanie DDS przez OpenGL (istnieje odpowiednie rozszerzenie) glCompressedTexImage2D(GL_TEXTURE_2D, i, data.format, data.width, data.height, 0, size, data.pixels + offset); offset += size; // Half the image size for the next mip-map level... data.width /= 2; data.height /= 2; }; delete[] data.pixels; return std::make_pair(id, data.components == 4); }; void TTexturesManager::SetFiltering(int filter) { if (filter < 4) // rozmycie przy powiększeniu { // brak rozmycia z bliska - tych jest 4: 0..3, aby nie było przeskoku glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); filter += 4; } else glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); switch (filter) // rozmycie przy oddaleniu { case 4: // najbliższy z tekstury glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); break; case 5: //średnia z tekstury glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; case 6: // najbliższy z mipmapy glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); break; case 7: //średnia z mipmapy glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); break; case 8: // najbliższy z dwóch mipmap glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); break; case 9: //średnia z dwóch mipmap glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); break; } }; void TTexturesManager::SetFiltering(bool alpha, bool hash) { if (alpha || hash) { if (alpha) // przezroczystosc: nie wlaczac mipmapingu { if (hash) // #: calkowity brak filtracji - pikseloza { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } } else // filtruj ale bez dalekich mipmap - robi artefakty { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } } else // $: filtruj wszystko - brzydko się zlewa { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } }; /////////////////////////////////////////////////////////////////////////////// GLuint TTexturesManager::CreateTexture(char *buff, GLint bpp, int width, int height, bool bHasAlpha, bool bHash, bool bDollar, int filter) { // Ra: używane tylko dla TGA i TEX // Ra: dodana obsługa GL_BGR oraz GL_BGRA dla TGA - szybciej się wczytuje GLuint ID; glGenTextures(1, &ID); glBindTexture(GL_TEXTURE_2D, ID); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); if (filter >= 0) SetFiltering(filter); // cyfra po % w nazwie else SetFiltering(bHasAlpha && bDollar, bHash); // znaki #, $ i kanał alfa w nazwie glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); if (bHasAlpha || bHash || (filter == 0)) glTexImage2D(GL_TEXTURE_2D, 0, (bHasAlpha ? GL_RGBA : GL_RGB), width, height, 0, bpp, GL_UNSIGNED_BYTE, buff); else gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, bpp, GL_UNSIGNED_BYTE, buff); return ID; } void TTexturesManager::Free() { // usunięcie wszyskich tekstur (bez usuwania struktury) for (Names::iterator iter = _names.begin(); iter != _names.end(); iter++) glDeleteTextures(1, &(iter->second)); } std::string TTexturesManager::GetName(GLuint id) { // pobranie nazwy tekstury for (Names::iterator iter = _names.begin(); iter != _names.end(); iter++) if (iter->second == id) return iter->first; return ""; }; #pragma package(smart_init)