mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
878 lines
35 KiB
C++
878 lines
35 KiB
C++
//---------------------------------------------------------------------------
|
||
|
||
/*
|
||
MaSzyna EU07 locomotive simulator
|
||
Copyright (C) 2001-2004 Marcin Wozniak and others
|
||
|
||
*/
|
||
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#include "opengl/glew.h"
|
||
#include <ddraw>
|
||
|
||
#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<char*>(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<Names::iterator, bool> 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('.')<fileName.rfind('\\')))
|
||
//pierwsza kropka od prawej jest wcześniej niż "\"
|
||
{//Ra: jeśli nie ma kropki w nazwie pliku, wypróbowanie rozszerzeń po kolei, zaczynając od
|
||
domyślnego
|
||
fileName.append("."); //kropkę trzeba dodać na pewno, resztę trzeba próbować
|
||
std::string test; //zmienna robocza
|
||
for (int i=0;i<4;++i)
|
||
{//najpierw szukamy w magazynie
|
||
test=fileName;
|
||
if (where) test.insert(0,where); //ścieżka obiektu wywołującego
|
||
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!
|
||
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 (copyto<copyend)
|
||
}
|
||
else
|
||
{
|
||
WriteLog("Compressed file is larger than uncompressed!");
|
||
if (Global::iModifyTGA & 1)
|
||
writeback = 0; // no zapisać ten krótszy zaczynajac od początku...
|
||
}
|
||
// if (copyto<copyend) WriteLog("Slow loader...");
|
||
while (copyto < copyend)
|
||
{ // Ra: stare wczytywanie skompresowanych, z nadużywaniem file.read()
|
||
// również wykonywane, jeśli dekompresja w buforze przekroczy jego rozmiar
|
||
file.read((char *)&chunkheader, 1); // jeden bajt, pozostałe zawsze zerowe
|
||
if (file.eof())
|
||
{
|
||
MessageBox(NULL, "Could not read RLE header", "ERROR", MB_OK); // display error
|
||
delete[] imageData;
|
||
file.close();
|
||
return fail;
|
||
};
|
||
if (chunkheader < 128)
|
||
{ // if the header is < 128, it means the that is the number of RAW color packets minus
|
||
// 1
|
||
chunkheader++; // add 1 to get number of following color values
|
||
file.read(copyto, chunkheader * bytesPerPixel);
|
||
copyto += chunkheader * bytesPerPixel;
|
||
}
|
||
else
|
||
{ // chunkheader>128 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<copyend)
|
||
if (writeback >= 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)
|