Ta lekcja została stworzona przez Lionel Brits (ßetelgeuse). Wyjaśnione są tylko te części kodu, które są zacytowane poniżej. Program nie będzie działał, jeżeli tylko skopiujesz poniższe linie kodu. Jeżeli chcesz wiedzieć gdzie umieścić każdą spośród tych linii ściągnij źródła i czytaj je równocześnie z tym tutorialem.
Witaj na lekcji 10. Umiesz już robić obracający się sześcian, kilka gwiazd. Znasz także podstawy programowania 3D. Ale zaczekaj! Nie zaczynaj pisać Quake'a IV. Obracające się sześciany nie będą zbyt ciekawymi przeciwnikami w Twojej grze ;-) W dzisiejszych czasach potrzebujesz dużego, skomplikowanego i dynamicznego świata 3D z sześcioma stopniami swobody, fantazyjnymi efektami jak lustra czy portale oraz wysokim wskaźnikiem FPS. Ta lekcja wyjaśnia jak stworzyć podstawową strukturę świata 3D, oraz jak się w nim poruszać.
Można opisać świat 3D jako długą sekwencję liczb, jednak jest to dość trudne, a złożoność środowiska rośnie. Z tego powodu musimy podzielić dane na kategorie. Na szczycie struktury jest sektor. Każdy świat 3D jest zbiorem sektorów. Sektorem może być pokój, sześcian oraz jakakolwiek zamknięta przestrzeń.
Sektor jest listą wielokątów, dlatego następną kategorią będą trójkąty (pozostaniemy przy trójkątach, bo bardzo łatwo je zaprogramować).
Trójkąt jest zbudowany z wierzchołków, co prowadzi nas do kolejnej kategorii. Wierzchołek jest opisywany przez współrzędne w przestrzeni 3D oraz współrzędne tekstury. Te dane będą przekazywane do OpenGL.
Trzymanie danych wewnątrz programu powoduje, że jest on statyczny i nudny. Można jednak wczytywać świat z dysku - daje to większą elastyczność. Możemy na przykład testować różne światy bez potrzeby ponownej kompilacji programu. Ponadto użytkownik może wymieniać światy i modyfikować je bez konieczności poznawania od podszewki naszego programu. Będziemy pamiętali dane w postaci tekstowej. Spowoduje to, że łatwiej będzie edytować plik świata, a kod stanie się krótszy i prostszy. Pliki binarne omówimy innym razem.
Pytanie brzmi: jak wyciągnąć dane z pliku? Najpierw tworzymy funkcję SetupWorld(). Deskryptor pliku trzymamy w polu filein. Otwieramy go do odczytu. Musimy zamknąć ten plik kiedy odczyt się zakończy. Spójrzmy na kod.
Kolejnym wyzwaniem jest odczytanie każdej linii z pliku do zmiennej. Można to zrobić na wiele sposobów. Jednym z problemów jest to, że nie wszystkie linie w pliku będą zawierały znaczące dane. Puste linie nie powinny zostać odczytane. Stwórzmy funkcję readstr(). Ta funkcja będzie czytała jedną, znaczącą linię tekstu. W pliku można także umieszczać komentarze - taka linia powinna rozpoczynać się znakiem / (slash).
Teraz musimy wczytaj sektor danych. W tej lekcji zajmiemy się przypadkiem gdy jest tylko jeden sektor, ale zaimplementowanie silnika z obsługą większej ilości sektorów jest równie proste. Wróćmy do SetupWorld(). Program musi wiedzieć ile trójkątów jest w sektorze. W pliku z danymi, zdefiniujemy liczbę trójkątów następująco:
A oto kod do wczytywania liczby trójkątów.
Reszta kodu czytającego dane jest bardzo podobna. Musimy jeszcze zainicjalizować sektor i wczytać do niego dane:
Każdy trójkąt w naszym pliku jest zapisany następująco:
X1 Y1 Z1 U1 V1
X2 Y2 Z2 U2 V2
X3 Y3 Z3 U3 V3
Teraz możemy wczytać sektor do pamięci. Musimy jeszcze wyświetlić go na ekranie. Do tej pory robiliśmy rotacje i translacje, kamera jednak zawsze była w punkcie (0,0,0). Każdy dobry silnik 3D powinien pozwalać użytkownikowi chodzić po świecie 3D i go zwiedzać. Nasz także. Jednym ze sposobów jest poruszanie kamery i rysowanie świata 3D względem pozycji kamery. Jest to powolna metoda i trudna do zaprogramowania. My zrobimy to tak:
Jest to bardzo proste do zaimplementowania. Zacznijmy od punktu 1.
To było proste. Kiedy prawa lub lewa strzałka jest wciśnięta to następuje rotacja wokół osi OY zostaje odpowiednio zmniejszona/zwiększona. Kiedy naciskamy strzałkę w przód/tył, to wyliczana jest nowa pozycja kamery. Wykorzystujemy funkcje sinus i cosinus aby obliczyć kierunek ruchu. Stała piover180 służy do zamiany stopni na radiany. Pytasz czym jest walkbias? Jest to przesunięcie w osi OY, które występuje gdy osoba idzie (głowa chodząca w górę i w dół, jakby utrzymywała się na powierzchni wody).
Teraz, gdy mamy już te zmienne, możemy zająć się krokami 2 i 3. Ponieważ nasz program jest prosty to zrobimy to w funkcji wyświetlającej scenę, gdyż nie warto tworzyć osobnej.
Voila! Co prawda to nie jest Quake, ale nie nazywamy się Carmack czy Abrash. Uruchamiając program możesz nacisnąć F, B, PgUp i PgDown, aby zobaczyć dodatkowe efekty. PgUp/PgDown przechyla kamerę w górę/dół. Załączona tekstura jest po prostu teksturą błota z ID mojej szkoły (o ile NeHe zdecydował się ją zatrzymać).
Więc teraz pewnie zastanawiasz się co dalej. Nie myśl nawet aby użyć tego kodu do wypasionego silnika 3D. Nie jest to zastosowanie do jakiego jest przystosowany ten kod. Zapewne będziesz chciał mieć więcej niż jeden sektor w swojej grze. Szczególnie jeżeli chcesz zaimplementować portale. Przydatne będą też wielokąty o innej liczbie wierzchołków niż 3.