Zauważyłem, że wiele ludzi na kanale #gamedev, na forum gamedev i różnych innych miejscach pyta o ładowanie plików TGA. Poniższy kod i wyjaśnienia powinny pokazać Ci jak używać nieskompresowanych plików TGA i skompresowanych metodą RLE.
Zaczniemy od dwóch plików nagłówkowych. W pierwszym będziemy przechowywać strukturę naszej tekstury a w drugim struktury i zmienne używane przez kod.
Jak w każdym pliku nagłówkowym (ang. header file) musimy się zabezpieczyć przed wielokrotnym dołączeniem pliku.
A na końcu pliku:
Te trzy linie gwarantują, że plik zostanie dołączony tylko raz. Reszta kodu powinna znaleźć się między pierwszymi dwiema a ostatnią linją.
Do tego pliku nagłówkowego powinniśmy dołączyć pozostałe niezbędne pliki. Dodaj poniższe linie po #define __TEXTURE_H__ command.
Potrzebujemy miejsce do zapamiętania obrazka oraz specyfikacji dla tekstury. Użyjemy poniższej struktury.
Następnie w drugim pliku nagłówkowym (tga.h) tworzymy takie dwie struktury używane podczas obróbki pliku TGA.
Deklarujemy sobie dwa obiekty powyższych struktur.
Musimy zdefiniować dwa nagłówki aby program wiedział z jakim plikiem TGA ma do czynienia. Pierwsze 12 bajtów wygląda tak: 0 0 2 0 0 0 0 0 0 0 0 0 dla nieskompresowanego TGA a tak: 0 0 10 0 0 0 0 0 0 0 0 0 dla skompresowanego metodą RLE. Dzięki tym nagłówkom wiemy, także czy odczytywany plik jest poprawny.
Na koniec musimy zadeklarować parę funkcji odpowiedzialnych za ładowanie obrazka.
Teraz przechodzimy do pliku cpp. Opuścimy sobie informacje o błędach aby tutorial był krótszy i czytelniejszy. Możesz jeszcze przedtem ściągnąc i zobaczyć jak wyglądają gotowe pliki "h" (link na dole artykułu).
W porządku. Na początku musimy dołączyć plik nagłówkowy.
Nie musimy dołączać niczego więcej ponieważ zrobiliśmy to już w pliku nagłówkowym.
Pierwsze co widzimy to funkcja LoadTGA(...).
Pobiera ona dwa parametry. Pierwszy to wskaźnik do tekstury, który musisz zadeklarować gdzieś w kodzie (zobacz w przykładzie). Drugi to ścieżka do pliku.
W pierwszych dwóch liniach deklarujemy wskaźnik do pliku i otwieramy go.
Następne kilka lini sprawdza czy plik został otwarty prawidłowo.
Następnie odczytujemy pierwsze dwanaście bajtów pliku i zapisujemy je w naszej strukturze TGAHeader. Jeżeli się nam to nie uda to znaczy że plik jest zamknięty więc wypisujemy komunikat o błędzie i zwracamy fałsz.
Odczytany nagłówek porównujemy z wcześniej zadeklarowanymi. Ta operacja powie nam czy plik jest skompresowany. Użyjemy do tego celu funkcji memcmp(...).
Pora na funkcję ładującą NIESKOMPRESOWANY plik TGA. Ta funkcja w większości opiera się na lekcji 25.
Jak zawsze pierwszą rzeczą jest nagłówek funkcji.
Funkcja pobiera trzy parametry. Pierwsze dwa są takie same jak w funkcji LoadTGA. Trzeci to wskaźnik do pliku z pierwszej funkcji.
Następnie prubujemy odczytać kolejne 6 bajtów pliku i zapisać je w tga.header. Jeżeli wystąpi błąd powiadamiamy o tym i zwracamy fałsz.
Mamy teraz wszyskie niezbędne informacje aby obliczyć szerokość, wysokość i bpp. Zapiszemy je w specyfikacji tekstury i lokalnej strukturze.
Musimy, także sprawdzić czy rozmiar obrazka jest większy od 0 i czy bpp wynosi 24 lub 32. Jeżeli jakaś warość jest z poza zakresu informujemy o tym, zamykamy plik, i opuszczamy funkcję.
Kolej na ustawienie formatu pliku. 24 bitowy obrazek ma format: GL_RGB natomiast 32 bitowy: GL_RGBA
Następnie obliczamy ilość BAJTÓW na piksel i całkowity rozmiar obrazka.
Potrzebujemy jakiegoś miejsca aby przechować nasz obrazek więc użyjemy funkcji malloc aby zarezerwować odpowiednią ilość pamięci.
Sprawdzamy czy pamięć została zarezerwowana. Jeżeli nie wiadomo co robimy.
Tutaj kopiujemy obrazek od pamieci. Jeżeli są problemy wypisujemy komunikat o błędzie.
Plik TGA przechowuje swoje piksele w innej kolejności niż OpenGL tego wymaga więc musimy zmienić format z BGR na RGB. Czyli musimy zamienić pierwszy i trzeci bajt każdego piksela.
Steve Thomas Adds: Znam małe przyśpieszenie dla operacji zmiany kolorów. Można to zrobić poprzez 3 operacje binarne. Używając zmiennej tymczasowej i XOR'ując po dwa bajty 3 razy.
Na koniec zamykamy plik i kończymy funkcję powodzeniem.
To wszystko na tema ładowania nieskompresowanego pliku TGA. Ładowanie skompresowanego metodą RLE pliku jest tylko troszkę trudniejsze. Odczytujemy nagłówek i zbieramy informacje i wysokości/szerokości/bpp jak w nieskompresowanej wesrji więc poprostu przepiszę kod, zobacz wyjaśnienia wyżej jeżeli czegoś nie rozumiesz.
Musimy zarezerwować odpowiednią ilość pamięci na obrazek zanim go zdekompresujemy, użyjemy jak poprzednio funkcji malloc.
Musimy wiedzieć ile pikseli tworzy obrazek. Zapiszemy to w zmiennej "pixelcount".
Musimy, także zapisać na którym pikselu aktualnie pracujemy oraz który bajt obrazka jest zapisywany aby zapobiec przepełnieniu i nadpisywaniu danych.
Następnie rezerwujemy potrzebną ilość pamięci aby zapisać jeden piksel.
Czas na wielką pętle.
Na początku deklarujemy zmienną aby zapisać nagłówek sekcji pliku (ang. chunk header). Nagłówek mówi nam czy sekcja jest RLE czy RAW i jaka jest długa. Jeżeli warość nagłówka jest mniejsza lub równa 127 wtedy jest to sekcja RAW. Wartością nagłówka jest liczba kolorów minus jeden, czytamy plik dalej i kopiujemy do pamięci do czasu aż napotkamy kolejny bajt nagłówka. Jeżeli wartość nagłówka jest POWYŻEJ 127 jest to ilość razy ile dany piksel sie powtarza. Lecz wcześniej musimy odjąć od tego 127. Wtedy wczytujemy piksel i kopiujemy go do obrazka odpowiednią ilość razy.
Na początku odczytujemy wartość nagłówka.
Następnie sprawdzamy czy jest to nagłówek sekcji RAW. Jeżeli tak musimy pobrać wartość nagłówka aby wiedzieć ile pikseli jest za nim.
W tej pętli odczytujemy kolejne piksele. Pętla zostanie wykonana tyle razy jaką miał wartość nagłówek i tyle pikseli zostanie odczytane.
Najpierw odczytujemy i sprawdzamy informacje o pikselu. Piksel zostaje zapisany w zmiennej colorbuffer.
Następna część naszej pętli pobiera wartości ze zmiennej colorbuffer i zapisuje je do imageData. Dodatkowo zmienia format z BGR na BRG lub BGRA na RGBA w zalieżności od ilości bajtów na piksel. Kiedy to skończymy interkremujemy nasze liczniki pikseli i bajtów.
Jeżeli nie jest to sekcja RAW więc musi być to sekcja RLE. Mmusimy więc odjąć 127 od wartości nagłówka aby wiedzieć ile razy dany piksel się powtarza.
Wczytujemy piksel do bufora.
Kolejna pętla zapisuje do obrazka odpowiednią ilość piksela przed chwilą wczytanego dodatkowo zmieniając kolory czerwony z niebieskim.
Na końcu dodajemy odpowiednie wartości do liczników bajtów i pikseli.
Kontynuujemy główną pętlę dopuki są piksele do wczytania.
I na końcu zamykamy plik i kończymy funkcję sukcesem.
Teraz masz już dane do wykorzystania w glGenTextures() oraz glBindTexture(). Informacje o nich znajdziesz w lekcji 6. i 24. To kończy mój pierwszy tutorial. Nie mogę zagwarantować, że w moim kodzie nie ma błędów, ale starałem się, żeby ich nie było. Specjalne podziękowania dla Jeff 'NeHe' Molofee za jego świetny kurs oraz dla Trent "ShiningKnight" Polack za poprawki w tym tutorialu. Jeżeli znajdziesz błąd lub chcesz mi coś powiedzieć lub zasugerować, nie krępuj się - napisz do mnie: e-mail (terminate@gdnmail.net), ICQ (38601160).