Format binarny modeli - E3D

Z Rainsted
Skocz do: nawigacji, wyszukiwania

Na potrzeby symulatora MaSzyna opracowałem binarny format zapisu modeli.

Plik z zapisanym modelem ma strukturę kromek, posiadających identyfikator kromki, długość oraz obszar danych. Struktura kromek jest również używana w innych formatach, jak PNG, ILBM, 3DS, czy DOF (Racer.nl). Struktura pliku modelu jest przygotowana pod kątem minimum przetwarzania do wyświetlania przy pomocy VBO, a jednocześnie jest zgodna ze strukturą plików T3D MaSzyny.

Budowa kromki

Kromka składa się z trzech elementów:

  1. Identyfikator typu kromki – 4 bajty, znaki ASCII. Na ogół pierwsze trzy znaki są literami, a ostatni bajt jest numerem wersji kromki.
  2. Długość kromki w bajtach, łącznie z nagłówkiem – 4 bajty, liczba 32 bit bez znaku.
  3. Obszar danych – struktura zależna od typu kromki. Długość obszaru danych zawsze jest wyrównania do wielokrotności 4 bajtów.

Niektóre typy kromek (aktualnie E3D0) mogą w obszarze danych zawierać inne kromki. Kromki nierozpoznane przez aktualną wersję oprogramowania są pomijane poprzez przeskoczenie ilości bajtów podanej w nagłówku kromki. Plik może opcjonalnie zawierać tożsame sobie kromki, przeznaczone dla różnych wersji oprogramowania (stara struktura dla zgodności wstecz, nowa dla zwiększenia wydajności).

Oprogramowanie edycyjne powinno ostrzegać w przypadku wykrycia nieznanej kromki i pozostawiać jej zawartość nienaruszoną. Powinno również umożliwiać konwersję starych kromek na nowy format i zależnie od konfiguracji pozostawiać obie wersje kromek albo tylko najnowszą.

Rodzaje kromek

  • E3D0 - główna kromka modelu, w obszarze danych zawiera kromki składowe. Normalnie długość tej kromki powinna być równa długości pliku. Możliwe jest jednak sklejenie kilku plików z modelami, czyli umieszczenie kilku modeli w jednym pliku. Oprócz kromki opisującej model, można w pliku umieścić informację np. o fizyce ruchu, animacjach, czy automacie skończonym powiązanym z modelem.

Rodzaje kromek wewnątrz E3D0

  • SUB0 – kromka zawierająca informacje o submodelach – 256 bajtów każdy. Submodel ma numer tekstury (ujemne – tekstury wymienne, 0 – brak tekstury, od 1 numery tekstur o nazwach umieszczonych w TEX0) oraz numer swojej nazwy (na ogół pokrywa się z numerem submodelu, ale submodele nieanimowane nie muszą mieć nazw). Może też mieć numer transformu, ale -1 oznacza, że transform jest jednostkowy.
  • SUB1 – podobnie jak SUB0, ale na submodel zarezerwowane jest 320 bajtów.
  • VNT0 – kromka zawierająca informację o wierzchołkach. Na każdy wierzchołek przypada 8 liczb float (czterobajtowych), kolejno: współrzędne XYZ, współrzędne wektora normalnego IJK, współrzędne mapowania na teksturze UV. Jeśli trójkąty w danym wierzchołku XYZ mają inne wektory normalne albo inne mapowanie UV, to każdy z nich musi być zapisany osobno. Również muszą być zapisane z powtórzeniami, jeśli nie jest używana tablica indeksów. Możliwe jest jednak, aby kilka submodeli używało ten sam ciąg kolejnych wierzchołków, jeśli rozmieszczone będą za pomocą różnych macierzy transformacji. Jest to szczególnie przydatne, jeśli takie powielone submodele mają bardzo dużą ilość wierzchołków, np. zbiorniki o kulistym kształcie, wózki, osie. Można też zrobić część wspólną dla dwóch submodeli, a różniące detale umieścić przed i za częścią wspólną.
  • TEX0 – lista nazw tekstur. Każda nazwa pliku tekstury jest zakończona bajtem o kodzie 0. Kromka nie jest używana, jeśli model nie ma tekstury albo ma wyłącznie tekstury wymienne ("skórki"). Ponieważ tekstury numerowane są od 1 (zero oznacza submodel bez tektury), to pierwszy ciąg znaków w tej kromce nie jest używany i obszar danych rozpoczyna się od bajtu zerowego (czyli znacznika końca nazwy o numerze 0).
  • TIX0 – indeks pomocniczy dla nazw tekstur. Zawiera czterobajtowe wskaźniki względne do kromki TEX0 dla każdej użytej tekstury. Jego użycie jest opcjonalne. Przy dużej ilości tekstur przyspieszy on wyszukiwanie nazw tekstur.
  • NAM0 – lista nazw submodeli. Każda nazwa submodelu jest zakończona bajtem o kodzie 0. Kromka nie jest używana, jeśli submodele nie mają nazw (nazwy są potrzebne głównie do animacji). Pierwszy ciąg znaków ma numer 0.
  • NIX0 – indeks pomocniczy dla nazw submodeli. Zawiera czterobajtowe wskaźniki względne do kromki NAM0 dla każdej nazwy. Jego użycie jest opcjonalne. Przy dużej ilości submodeli (np. kabina) przyspieszy on wyszukiwanie ich nazw, np. do powiązania animacji.
  • IDX1 – tablica indeksów jednobajtowych. Może być używana, jeśli ilość wierzchołków w VNT0 nie przekracza 256, albo submodele używające indeksowanych wierzchołków korzystają z maksymalnie 256 początkowych (pozostałe wierzchołki mogą być użyte poza tabelą indeksów). Użycie tabeli indeksów może wpłynąć na zmniejszenie rozmiaru VNT0, gdyż dane wierzchołków wspólnych dla kilku trójkątów nie muszą być powtarzane.
  • IDX2 – tablica indeksów dwubajtowych. Może być używana, jeśli ilość wierzchołków w VNT0 nie przekracza 65536, albo submodele używające indeksowanych wierzchołków korzystają z maksymalnie 65536 początkowych (pozostałe wierzchołki mogą być użyte poza tabelą indeksów). Użycie tabeli indeksów może wpłynąć na zmniejszenie rozmiaru VNT0, gdyż dane wierzchołków wspólnych dla kilku trójkątów nie muszą być powtarzane.
  • IDX4 – tablica indeksów czterobajtowych. Użycie tabeli indeksów może wpłynąć na zmniejszenie rozmiaru VNT0, gdyż dane wierzchołków wspólnych dla kilku trójkątów nie muszą być powtarzane.
  • TRA0 – tablica macierzy transformacji. Każda macierz zajmuje 16 wartości float, czyli 64 bajty. Nie każdy submodel musi mieć swoją macierz, w szczególności pomijane powinny być macierze jednostkowe (wyłącznie jedynki na przekątnej).
  • TRA1 – tablica macierzy transformacji o podwójnej precyzji. Każda macierz zajmuje 16 wartości double float, czyli 128 bajtów. Na chwilę obecną nie ma potrzeby ich używania, w MaSzynie są konwertowane automatycznie do pojedynczej precyzji.
  • REM0 – kromka zawierająca komentarz do modelu.
  • FNT1 - tablica jednobajtowych indeksów dla zestawu znaków. Każdy zestaw znaków tworzy tablicę o wielkości 256 bajtów, z numerami submodelu dla każdego znaku. Można użyć do 255 submodeli. Wartość 0 oznacza brak submodelu dla danego znaku. Ograniczenie do 255 submodeli oznacza również, że można użyć np. 2 zestawy znaków po 127 znaków w każdym.
  • FNT2 - tablica dwubajtowych indeksów dla zestawu znaków. Każdy zestaw znaków tworzy tablicę o wielkości 512 bajtów, z numerami submodelu dla każdego znaku. Można użyć do 65535 submodeli. Wartość 0 oznacza brak submodelu dla danego znaku.

Kolejność kromek

Kolejność kromek w pliku nie jest narzucona z góry, ale może być istotna z punktu widzenia optymalizacji zużywanej pamięci i wydajności wyświetlania.

Kromki należy umieszczać w pliku zaczynając od najbardziej niezbędnych. Jedyną kromką, która zawsze musi się znajdować w pamięci jest SUB0. Kromki VNT0, IDX1, IDX2, IDX4 mogą być jednorazowo przeniesione do karty graficznej, ewentualnie mogą być przechowywane w pamięci zwykłej w celu oszczędzenia pamięci graficznej. Kromki TEX0, TIX0, NAM0, NIX0 są potrzebne jedynie na etapie wczytania pliku i ustalania zależności (sterowania animacjami).

Kromka SUB0

Kromka SUB0 jest tablicą submodeli, w której każdy submodel zajmuje 256 bajtów. Niektóre pola (głównie początkowe) zawierają własności submodelu, pozostałe służą jako zmienne robocze. Ustalony z góry rozmiar umożliwia ustawienie wskaźnika pierwszego elementu na początek obszaru danych kromki, bez konieczności przepisywania danych do wewnętrznej struktury klasy.

Ponieważ dane submodelu (wraz ze zmiennymi roboczymi) mogą się po rozbudowaniu nie zmieścić w 256 bajtach, został określony schemat powiększania ilości bajtów przypadających na submodel. Każde zwiększenie o 1 ostatniego znaku identyfikatora typu kromki oznacza zwiększenie pola o 64 bajty. Tak więc SUB1 ma 320 bajtów na submodel, SUB2 - 384, SUB3 - 448, SUB4 - 512 itd. Aktualnie w użyciu jest rozmiar SUB0.

Własności submodelu

Bajt Rozmiar Typ Opis
0 4 int Numer submodelu następnego, -1 gdy brak.
4 4 int Numer pierwszego submodelu potomnego, -1 gdy brak.
8 4 int Typ submodelu. Możliwe jest użycie wartości takich jak GL_TRIANGLES. Wartości powyżej 256 oznaczają typy specjalne.
12 4 int Numer nazwy. Nazwy są numerowane od zera. Wartość -1 oznacza brak nazwy (nie jest potrzebna, jeśli submodel nie jest animowany zdarzeniami).
16 4 int Rodzaj animacji.
20 4 int Flagi submodelu. Bity 0..15 dotyczą danego submodelu, bity 16..31 są skumulowanymi bitami 0..7 obiektów następnych oraz potomnych i umożliwiają pomijanie całych gałęzi podczas wyświetlania.
24 4 int Numer macierzy przekształcenia widoku. Macierz jest potrzebna do ustalenia osi układu współrzędnych dla animacji. Jeśli submodel nie będzie animowany, można jego wierzchołki przeliczyć tak, aby uzyskać macierz jednostkową. W takim przypadku nie jest ona zapisywana do pliku, a w tym miejscu należy podać wartość -1.
28 4 int Ilość wierzchołków. Dla typu GL_TRIANGLES musi być podzielna przez 3, dla GL_LINES parzysta.
32 4 int Numer pierwszego wierzchołka w VNT0 albo pierwszego indeksu wierzchołka w IDX1, IDX2 albo IDX4.
36 4 int Numer tekstury. Tekstury są numerowane od 1. Wartość 0 oznacza brak tekstury. Wartości ujemne to numer tekstury wymiennej, pierwsza tekstura wymienna ma numer -1.
40 4 float Próg jasności załączania submodelu. Dla wartości 0.0÷1.0 model będzie wyświetlany, jeśli będzie ciemniej niż podana wartość. Dla wartości -1.0÷0.0 - gdy będzie jaśniej.
44 4 float Próg zapalenia światła. Świecenie automatyczne zostanie włączone, jeśli będzie ciemniej niż podana wartość 0.0÷1.0.
48 16 float[4] Kolor RGBA ambient. Nie używany.
64 16 float[4] Kolor RGBA diffuse.
80 16 float[4] Kolor RGBA specular. Nie używany.
96 16 float[4] Kolor RGBA świecenia. Używany po załączeniu świecenia (selfillum).
112 4 float Rozmiar linii dla GL_LINES.
116 4 float Kwadrat maksymalnej odległości widoczności. W większej odległości submodel i jego potomne nie będą widoczne (np. można je uznać za pomijalnie małe albo są zastąpione fazą LoD).
120 4 float Kwadrat minimalnej odległości widoczności. W mniejszej odległości submodel i jego potomne nie będą widoczne (np. faza LoD dla oddalenia).
124 32 float[8] Parametry światła dla punktów świecących: fNearAttenStart, fNearAttenEnd, bUseNearAtten (bool), iFarAttenDecay (int), fFarDecayRadius, fCosFalloffAngle, fCosHotspotAngle, fCosViewAngle.
156 100  ? Zmienne robocze. Obszar w pliku powinien być wypełniony zerami.

Typy submodelu

Typ submodelu jest określony liczbowo. Wartości od 0 do 9 są standardowymi typami OpenGL. Wartości powyżej 256 są typami specjalnymi.

Typ Opis Ilość wierzchołków
0 Punkty (GL_POINTS). Dowolna niezerowa.
1 Linie (GL_LINES). Każde dwa punkty to jedna linia. Parzysta, niezerowa.
2 Linia zamknięta (GL_LINE_LOOP). Co najmniej trzy.
3 Pęk linii (GL_LINE_STRIP). Wszystkie linie wychodzą z pierwszego punktu. Co najmniej dwa.
4 Trójkąty (GL_TRIANGLES). Najczęściej używana wartość. Podzielna przez 3, niezerowa.
5 Trójkąty o wspólnych bokach (GL_TRIANGLE_STRIP). Co najmniej 4.
6 Trójkąty o wspólnym wierzchołku (GL_TRIANGLE_FAN). Co najmniej 4.
7 Kwadraty (GL_QUADS). Użycie odradzane - zamienić na typ 4, zwiększając ilość wierzchołków o 50%. Podzielna przez 4, niezerowa.
8 Kwadraty o wspólnym boku (GL_QUAD_STRIP). Użycie odradzane - zastąpić typem 5. Parzysta, co najmniej 4.
9 Wielokąt (GL_POLYGON). Użycie odradzane - zamienić na typ 5 albo 6. Co najmniej 5.
256 Macierz transformacji układu współrzędnych dla kolejnych submodeli ("banan"). Zero.
257 Punkt świecący kierunkowo ("FreeSpotLight"). Jeden, o zerowych współrzędnych.
258 Punkty świecące dookólnie ("stars"), każdy może mieć inny kolor. Co najmniej jeden.
259 Generator tekstu. Każde 4 punkty określają "ekran", na którym są tworzone napisy z submodeli potomnych, opisujących poszczególne znaki. Podzielna przez 4, niezerowa.
260 Komin - w kierunku osi OX są wyrzucane modele dymu. Zero.
261 Wieszak - umożliwia umocowanie modelu z innego pliku. Zero.

Rodzaje animacji

Rodzaj animacji jest określony liczbą int (32 bit ze znakiem). Nazwa jest podawana w pliku T3D jako właściwość Anim: (wartości liczbowe nie są rozpoznawane).

Rodzaj Nazwa Opis Zastosowanie
-1 true Nieznana. Submodel ruchomy.
0 false Brak. Submodel nieruchomy.
1 Obrót względem wektora. Wartość tymczasowa, używana wewnętrznie.
2 Obrót względem trzech osi. Wartość tymczasowa, używana wewnętrznie.
3 Przesunięcie. Wartość tymczasowa, używana wewnętrznie.
4 seconds_jump Sekundy z przeskokiem. Wskazówka sekundowa zegara.
5 minutes_jump Minuty z przeskokiem. Wskazówka minutowa zegara.
6 hours_jump Godziny z przeskokiem 12h/360°. Wskazówka godzinowa zegara.
7 hours24_jump Godziny z przeskokiem 24h/360°. Wskazówka godzinowa zegara 24h.
8 seconds Sekundy płynnie. Wskazówka sekundowa zegara, wiatraki.
9 minutes Minuty płynnie. Wskazówka minutowa zegara.
10 hours Godziny płynnie 12h/360°. Wskazówka godzinowa zegara.
11 hours24 Godziny płynnie 24h/360°. Wskazówka godzinowa zegara 24h.
12 billboard Obracanie do kamery. Roślinność jednowymiarowa.
13 wind Symulacja wiatru. Roślinność.
14 sky Ruch nieba. Niebo z gwiazdami.
256 ik Odwrotna kinematyka. Współrzędne liczone względem nadrzędnego bez odwrotnej kinematyki.
257 ik1 Odwrotna kinematyka 1. Submodel ustawiany w kierunku pierwszego potomnego.
258 ik2 Odwrotna kinematyka 2. Submodel i potomny ustawiane w kierunku potomnego potomnego.

Flagi submodelu

Bity od 0 do 3 określają użycie wymiennej tekstury i są używane do optymalizacji wyświetlania gałęzi w cyklach renderowania przezroczystych i nieprzezroczystych. Bit 0 jest ustawiony, jeśli wymienna tekstura ma numer -1. bit 1, gdy -2, bit 2 gdy -3. Jeśli użyta będzie wymienna tekstura o numerze mniejszym niż -3, ustawiony będzie bit 3.

Bit 4 - ustawiony oznacza, że submodel ma być renderowany w cyklu nieprzezroczystych.

Bit 5 - ustawiony oznacza, że submodel ma być renderowany w cyklu przezroczystych.

Bit 6 - zarezerwowany.

Bit 7 - ustawiony, gdy po renderowaniu poprzedniego submodelu nie trzeba zmieniać numeru tekstury

Bity 8..13 zarezerwowane.

Bit 14 - ustawiony, gdy submodel jest animowany. Aktualna macierz widoku zostanie zachowana na stosie.

Bit 15 - ustawiony, gdy submodel ma niejednostkową macierz. Aktualna macierz widoku zostanie zachowana na stosie. Powinien być jeszcze podany numer macierzy w odpowiednim polu.

Bity 16 do 23 są skumulowanymi (OR) bitami 0..7 wszystkich potomnych submodeli danego submodelu.

Bity 24 do 31 są skumulowanymi (OR) bitami 0..7 wszystkich następnych submodeli oraz ich potomnych.