Główny obiekt i tworzenie menadżera sceny Podobnie jak w poprzednim rozdziale oprzemy się na zalążku programu OGRE, który należy wkleić w pierwszy utworzony plik .cpp.

Korzeń W tym przykładzie zajmiemy się renderowaniem terenu w Ogre. Aby to zrobić, potrzebujemy ustawić menadżera sceny na TerrainSceneManager. Odszukaj linie:

/** createScene jest funkcją czysto wirtualną w ExampleApplication,

i wstaw przed nią następujący kod:

// Tworzenie Menadżera Sceny void chooseSceneManager(void)

#if OGRE_VERSION getSceneManager(ST_EXTERIOR_CLOSE); #else mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE, "TerrainSceneManager"); #endif

Uwaga! Tworzenie Menadżera Sceny zostało zmienione w wersji 1.2 (Dagon) w stosunku do poprzednich. Stąd wykorzystujemy dyrektywy prekompilacji umożliwiające wybór odpowiedniej wersji kodu do wersji systemu.(z forum)

Obiekt Root (mRoot jest instancją klasy Root) jest rdzeniem ("core") Ogrowych obiektów. Tutaj możesz zobaczyć diagram UML zależności miedzy obiektami. W tym kawałku kodu mówimy korzeniowi, że potrzebujemy menadżera sceny typu ST_EXTERIOR_CLOSE. Korzeń wykorzystuje SceneManagerEnumerator, aby odnaleźć odpowiedni menadżer sceny i potem go zwraca.

Po tym, jak aplikcja zostanie skonfigurowana nie będziesz już musiał używać obiektu Root.

Tworzenie menadżera sceny Można utworzyć menadżera sceny bezpośrednio wywołując new SceneManager, bynajmniej nie trzeba używać metody getSceneManager ani createSceneManager z obiektu Root. Jednoczesnie można mieć wiele menadżerów scen o różnych kształtach i innych jednostkach. Można też zmieniać menadżera podczas odnawiania viewportu lub wyświetlać wiele menadżerów używając wielu vieportów.

Dlaczego zatem używamy getSceneManager/createSceneManager zamiast tworzyć go normalnie? System wtyczek w Ogre daje nam duży poziom elastyczności podczas pracy z menadżerami scen. Zaś w "normalnym" przypadku mamy dostępnych tylko kilka rodzajów menadżerów scen. ExampleApplication ma domyślnego menadżera sceny, który jest ST_GENERIC. Może się wydawać, że jest to klasa bazowa, dopóki nie zacznie się głębiej sięgać w konfigurację pluginów. Gdy używamy pluginu OctreeSceneManager, ten rejestruje siebie, i nadpisuje klasę bazową SceneManager. OctreeSceneManager używa systemu zrywania elementów, które nie są aktualnie na scenie. W rezultacie działa szybciej niż wbudowany, regularny menadżer sceny. Jeśli zaś usunąłeś OctreeSceneManager z pliku plugins.cfg i żądasz ST_GENERIC w getSceneManager, prawdopodobnie używasz wtedy prostego menadżera sceny.

Żądanie ST_GENERIC to prosty i łatwy sposób tworzenia menadżera, ale może powodować parę problemów. Bez względu na okoliczności nie gwarantuje, że dostaniesz takiego menadżera sceny, którego chciałeś, ponieważ niektóre pluginy próbują nadpisać ten sam typ ST_*. Raczej jednak nie występują tego typu problemy. Niemniej, jeśli potrzebujesz zaawansowanego, konkretnego menadżera sceny, powinieneś utworzyć go samemu. A jeśli potrzebujesz standardowych rzeczy możesz po prostu używać dostępnych menadżerów. Przy okazji pamiętaj że SceneManagerEnumerator nie jest czymś w rodzaju fabryki, dlatego nigdy nie powinieneś bezpośrednio usuwać zwróconego menadżera sceny.

Teren Dodawanie terenu do sceny

Zajmiemy się teraz tworzeniem terenu. Bazowa klasa SceneMenager określa metodę setWorld, której używają klasy pochodne w celu utworzenie sceny. W klasie TerrainSceneManager funkcja ta wymaga nazwy pliku, z którego ma wczytać konfigurację terenu. Znajdź funkcję createScene i dodaj poniższy kod: mSceneMgr->setWorldGeometry( "terrain.cfg" );

Przekompiluj teraz i uruchom program. W niektórych przypadkach zauważysz, że teren znajduje się nie tak jak na załączonym rysunku lecz u góry po prawej strony. Wówczas dodaj dodatkowy poniższy kod poprawiający ustawienie kamery:

// Poprawienie ustawienia kamery mCamera->setPosition( 500.0f, 90.0f, 500.0f );

Można również osiągnąć to przesuwając kamerę za pomocą myszki.

Plik terrain.cfg Mamy wiele opcji w tym pliku. Zajmiemy się tylko niektórymi. TerrainSceneManager został zaprojektowany w celu takim, aby miał możliwość stronicowania, niestety jednak wciąż jest jeszcze to implementowane. System stronnicowania terenu polega na tym, że teren jest dzielony na kawałki i kawałek jest wyświetlany wtedy, kiedy użytkownik go widzi. Umożliwia to stworzenie dużego świata, bez obniżenia wydajności w szybkości wyświetlania klatek. Na szczęście istnieje wtyczka, która daje taką możliwość: Paging Scene Manager.

TerrainSceneManager używa mapy wysokości, aby utworzyć teren. Możesz określić ją, jeśli zmienisz właściwość Heightmap.image. Możesz wstawić teksturę terenu za pomocą proporcji WorldTexture. Właściwość DetailTexture umożliwia określić poziom interpolacji tekstury, tworząc w ten sposób teren bardziej realistyczny.

Niebo Ogre dostarcza trzy różne rodzaje nieba: SkyBox, SkyDome i SkyPlane. Omówimy każdy po kolei. SkyBox

SkyBox jest prostym, wielkim sześcianem, który otacza wszystkie obiekty w scenie. Najszybszym sposobem, jak to działa jest zobaczenie przykładu. Tak więc wstawmy poniższy kod do funkcji createScene:

mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox" );

Przekompiluj i uruchom program. (SkyBox jest pikselowate, ponieważ użyta tekstura jest niskiej jakości. Wstawiając teksturę o większych wymiarach, otrzymamy lepszy wygląd.) Mamy kilka przydatnych argumentów dla SkyBoxów, które możemy ustawić za pomocą funkcji setSkyBox. Pierwszy mówi, czy włączyć lub wyłączyć SkyBox. Jeśli potrzebujesz wyłączyć SkyBox po prostu użyj mSceneMgr->setSkyBox( false, "" ). Drugim parametrem jest skrypt materiału użytego dla nieba. Trzeci argument określa dystans, o jaki SkyBox ma być oddalony od kamery. Czwarty argument określa, czy SkyBox ma być renderowany przed, czy po narysowaniu sceny. Zobaczmy, co się stanie, kiedy zmienimy domyślny dystans SkyBox (5000j) na bardzo mały:

mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox", 10 );

Nic się nie zmieniło. Jest tak, ponieważ czwarty argument określa, czy niebo ma być najpierw rysowane. Domyślnie jest włączony (true). Jeśli SkyBox jest najpierw rysowany, wtedy reszta jest renderowana po nim, nakładając się w ten sposób na niego. Powoduje to, że SkyBox jest zawsze w tle. Należy zaznaczyć, że odległość nieba od kamery powinna być tak ustawiona, aby mieściło się ono między dystansami obcinania kamery. W przeciwnym wypadku nie zobaczymy nieba. Nie jest zalecane rysowanie całego nieba na początku, ponieważ obniża to szybkość działania klatek. Kiedy niebo jest rysowane pod koniec, wtedy tylko widoczne części są renderowane. Umożliwia to przyszybszenie czasu działania. Ma to także swoje ciemne strony. Uczyńmy, aby nasz SkyBox był rysowany na końcu:

mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox", 5000, false );

Wygląda to identycznie, jak poprzednio, jednak nie widoczne części nie były renderowane. Należy dodać, że jeśli utworzony SkyBox jest za ciasny, możesz zobaczyć wycięte przez niebo fragmenty świata. Nie wygląda to za ciekawie. Zobaczmy:

mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox", 100, false );

Możesz teraz zobaczyć teren, który jest ograniczony i zapakowany przez niebo. Szybka metoda renderowania nieba jest bardzo ograniczona i nie obchodzi się zbyt ostrożnie z geometrią sceny. Innymi słowy pominięcie dwóch ostatnich argumentów jest bardziej bezpieczne, nie musisz wtedy obawiać się o nieporządane efekty.

SkyDome

SkyDome bardzo przypomina SkyBox. Możesz go użyć wywołując funkcję setSkyDome. Duży sześcian jest tworzony naokoło kamery a następnie renderowany (sześcian jest mniejszy od terenu i przesuwa się wraz z kamerą). Jednak istnieją duże różnice między nimi, otóż tekstura w SkyDome jest pokazywana w sferyczny sposób. Spoglądając w sześcian masz wrażenie, że wygląda on tak, jakby tekstura była nałożona na sferę. Inną widoczną rzeczą jest to, że sześcian ten nie ma podstawy.

Zobaczmy teraz przykład. Usuńmy wstawioną wcześniej funkcję setSkyBox i wstawmy zamiast tego poniższy kod:

mSceneMgr->setSkyDome( true, "Examples/CloudySky", 5, 8 );

Przekompiluj to teraz i uruchom. Przesuń kamerę w centrum terenu, tak aby z każdej strony było widać nasze góry. Następnie spójrz na niebo, jak pięknie wygląda.Patrząc na to przyciśnij przycisk R, aby przełączyć widok na widok siatki. Zapewne zauważysz, że patrzysz na sześcian, ale tekstura chmur jest tak nałożona, że wygląda jak sfera.

Pierwsze dwa argumenty oznaczają to samo, co w funkcji setSkyBox. Możesz wyłączyć SkyDome poprzez wywołanie mSceneMgr->setSkyDome( false, "" ). Trzeci argument ustawia poziom krzywizny. Najlepiej, aby argument ten był między 2 a 65. Mniejsze wartości sprawiają wrażenie, że niebo jest dalej, natomiast większe, że jest bliżej i jest bardziej wygładzone. Czwarty argument jest liczbą rzeczywistą, która określa na ile kawałków ma zostać podzielona tekstura. Należy dodać, że argument ten może być ułamkiem np. 1.234. Piąty i szósty argument opisuje odległość od kamery, a także czy niebo ma być rysowane na samym początku. Te dwa argumenty zostały one opisane szerzej w sekcji SkyBox.

SkyPlane

SkyPlane wielce się różni od SkyBoxów i SkyDome. Zamiast renderować niebo na sześcianie, wykorzystuje on pojedyńczą płaszczyznę. Wyczyśćmy nasz kod SkyDome z funkcji createScene. Pierwszą rzeczą jaką zrobimy to utworzymy płaszczyznę i skierujemy ją ku dołowi. Wywołując metodę setSkyPlane nie podajemy argumentu określającego odległość jak było w SkyBox i SkyPlane. Zamiast tego ustawiamy parametr d w naszej płaszczyźnie:

Plane plane; plane.d = 1000; plane.normal = Vector3::NEGATIVE_UNIT_Y;

Kiedy już mamy naszą płaszczyznę, możemy utworzyć SkyPlane. Dodajmy, że czwarty parametr jest wielkością SkyPlane (w tym przypadku 1500x1500), a piąty określa jak wiele razy ma podzielić teksturę:

mSceneMgr->setSkyPlane( true, plane, "Examples/SpaceSkyPlane", 1500, 75 );

Przekompiluj i uruchom program. Mamy dwa problemy związane z utworzonym tutaj SkyPlanem. Pierwszy polega na tym, że użyta tekstura jest za mała, dlatego też nie pokrywa ona nieba za dobrze. Możemy tego problemu się pozbyć tworząc dobrą i większą teksturę. Jakkolwiek głównym problemem z tą techniką jest to, że gdy spoglądniesz na horyzont, możesz zobaczyć koniec SkyPlane. Nawet jeśli masz dobrą teksturę, może nie wyglądać najlepiej, ponieważ zobaczysz koniec horyzontu. Te podstawowe użycie SkyPlanu jest przydatne tylko wtedy, kiedy masz wysokie ściany naokoło viewportu. W takiej sytuacji używanie SkyPlanu może być korzystniejsze niż tworzenie pełnych SkyBoxów/SkyDome.

Na szczęście, nie jest to wszystko, co możemy zrobić ze SkyPlane. Szósty argument jest znanym już "renderFirst", opisanym w sekcji SkyBox i SkyDome. Siódmy argument umożliwia określanie poziomu krzywizny SkyPlanu, tak więc nie musimy dłużej używać płaszczyny, zamiast tego może to być krzywa powierzchnia. Możemy także ustawić liczbę segmentów x i y, które mają zostać użyte do tworzenia SkyPlane (domyślnie SkyPlane jest wielkim kwadratem, ale jeśli chcemy go skrzywić potrzebujemy naszą płaszczyznę podzielić na mniejsze kwadraty). Ósmy i dziewiąty argument są liczbą segmentów na x i y:

mSceneMgr->setSkyPlane( true, plane, "Examples/SpaceSkyPlane", 1500, 50, true, 1.5f, 150, 150 );

Przekompiluj i ruchom program. Teraz nasz SkyPlane wygląda o wiele lepiej, chociaż używa trochę pracy. Możesz także użyć inny materiał chmur:

mSceneMgr->setSkyPlane( true, plane, "Examples/CloudySky", 1500, 40, true, 1.5f, 150, 150 );

Przekompiluj i uruchom program.

Możesz wyczyścić SkyPlane wywołując 'mSceneMgr->setSkyPlane( false, Plane(), "" );'

Którego nieba użyć