8 listopada 2009 Tutorial 2. Quake3Map



Ten tutorial pokazuje jak załadować mapę Quake 3 do silnika, utworzyć SceneNode do zoptymalizowania szybkości renderingu i jak utworzyć kamerę kontolowaną przez użytkownika.

Zauważ że powinienes wcześniej zapoznać się z podstawami działania silnika przed rozpoczęciem tego tutorialu. Jeśli tego nie zrobiłeś, możesz to uczynić w tej chwili klikając w Hello World

Zacznijmy tak jak w przykładzie HelloWorld: Dołączamy plik nagłówkowy irrlicht i dodatkowy plik żeby móc zapytać użytkownika jaki sterownik jest używany przez jego konsolę

#include <irrlicht.h>
#include <iostream>

Tak jak jest już napisane w przykładzie HelloWorld w silniku irrlicht wszystko może być znalezione w przestrzeni nazw 'irr'. Żeby się pozbyć 'irr::' na początku każdej klasy mówimy kompilatorowi, że od tej chwili używamy tejk przestrzeni nazw i wtedy nie będziemy musieli ciągle pisać 'irr::'. Jest 5 innych pod przestrzeni (namespaców) 'core', 'scene', 'video', 'io' i 'gui'. W tym przykładzie jednak nie używamy 'using namespace' dla 5 innych namespaców jak zrobiliśmy to w przykładzie HelloWorld, ponieważ w ten sposób zobaczysz co można znaleść w każdym namespace. Ale jeśli chcesz możesz dodać namespace tak jak w poprzednim przykładzie.
 using namespace irr; 

Znów, żeby być w stanie użyc pliku Irrlicht.DLL musimy połączyć go z Irrlicht.lib. Możemy ustawić to w opcjach ustawień projektu, ale żeby to uprościć używamy pragmy comment lib.
            #ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

Ok, zacznijmy więc. Znów, używamy metody main() jako początek, a nie WinMain().
 int main()
{    

Tak jak w przykładzie HelloWorld, tworzymy IrrlichtDevice za pomocą createDevice(). Z tą jednak różnicą, że teraz pytamy użytkownika by wybrał sterownik video jakiego chce uzywać. Urządzenie softwarowe może być za wolne, żeby narysować wielką mape Quake 3, ale dla zabawy, możemy udostępnić i taką opcję.
        // ask user for driver

        video::E_DRIVER_TYPE driverType;

        printf("Please select the driver you want for this example:\n"\
                " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
                " (d) Software Renderer\n (e) Burning's Software Renderer\n"\
                " (f) NullDevice\n (otherKey) exit\n\n");

        char i;
        std::cin >> i;

        switch(i)
        {
                case 'a': driverType = video::EDT_DIRECT3D9;break;
                case 'b': driverType = video::EDT_DIRECT3D8;break;
                case 'c': driverType = video::EDT_OPENGL;   break;
                case 'd': driverType = video::EDT_SOFTWARE; break;
                case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
                case 'f': driverType = video::EDT_NULL;     break;
                default: return 1;
        }       

        // create device and exit if creation failed

        IrrlichtDevice *device =
                createDevice(driverType, core::dimension2d<s32>(640, 480));

        if (device == 0)
                return 1; // could not create selected driver.   

Weź wskaźnik do sterownika video i SceneManager żebyśmy nie musieli ciągle wzywac 'irr::IrrlichtDevice::getVideoDriver()' i 'irr::IrrlichtDevice::getSceneManager()'.
                    video::IVideoDriver* driver = device->getVideoDriver();
        scene::ISceneManager* smgr = device->getSceneManager();  

Żeby wyświetlić mapę Quake 3, najpierw musimy ją załadować. Mapy Quake 3 są zapakowane w pliki .pk3, które są niczym innym jak plikami .zip. Więc dodajemy plik .pk3 to naszego 'irr::io::IFileSystem'. Gdy już go dodamy, będziemy mogli przeczytać z plików w tych archiwach tak jakby były u nas na dysku.
 device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");

Teraz możemy załadować mesh przez wezwanie 'irr::scene::ISceneManager::getMesh()'. Do 'irr::scene::IAnimatedMesh' powraca wskaźnik. Jak już może wiesz, mapy Quake 3 nie są na prawde zanimowane, mają jednak ogromny kawał statycznej geometrii z dołaczonymi pewnymi materiałami. Stąd 'IAnimatedMesh' zawiera jedynie ramkę, więc mamy 'first frame' animacji, która jest naszym poziomem quake i tworzy OctTree scene node z tym, używając 'irr::scene::ISceneManager::addOctTreeSceneNode()'. OctTree optymalizuje scenę, starając sie narysować tylko geometrię, która jest obecnie widoczna. Alternatywą do OctTree byłby 'irr::scene::IMeshSceneNode', który zawsze rysuje kompletną geometrie mesha bez optymalizacji. Wypróbuj tego: Użyj 'irr::scene::ISceneManager::addMeshSceneNode()' zamiast addOctTreeSceneNode() i porównaj figury proste narysowane przez sterownik video. (Jest metoda 'irr::video::IVideoDriver::getPrimitiveCountDrawn()' w klasie 'irr::video::IVideoDriver'.) Zauważ, że ta optymalizacja z OctTree jest przydatna przy rysowaniu ogromnych meshy zawierających dużo geometrii.
       scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
        scene::ISceneNode* node = 0;
        
        if (mesh)
                node = smgr->addOctTreeSceneNode(mesh->getMesh(0), 0, -1, 1024);
//              node = smgr->addMeshSceneNode(mesh->getMesh(0));

Ponieważ poziom nie był zmodelowany wokół pochodzenia (0,0,0), przetłumaczymy troszke cały poziom. To jest do zrobienia na poziomie 'irr::scene::ISceneNode' używającego metod 'irr::scene::ISceneNode::setPosition()' (w tym wypadku), 'irr::scene::ISceneNode::setRotation()', i 'irr::scene::ISceneNode::setScale()'.
        if (node)
                node->setPosition(core::vector3df(-1300,-144,-1249));

Teraz musimy tylko ustawić kamerę żeby patrzyła na mapę Quake 3. Chcemy utworzyć kamerę kontrolowaną przez użytkownika. W silniku Irrlicht jest dostępne kilka kamer. Na przykład MayaCamera która może być kontrolowana jak kamera w Maya: Obracanie przy przyciśniętym lewym klawiszu myszki, Zoom przy przyciśniętych obu klawiszy myszki, tłumacznie z przyciśniętym prawym klawiszem myszki. To może być stworzone z 'irr::scene::ISceneManager::addCameraSceneNodeMaya()'. Ale w tym przykładzie, chcemy utworzyć kamerę , która zachowuje się tak jak w pierwszej osobie strzelanki (FPS) i dlatego uzywamy 'irr::scene::ISceneManager::addCameraSceneNodeFPS()'.
        smgr->addCameraSceneNodeFPS(); 

Kursor myszy nie musi być widoczny, więc chowamy go przez 'irr::IrrlichtDevice::ICursorControl'.
       device->getCursorControl()->setVisible(false); 

Zrobiliśmy już wszystko, więc narysujmy to. Piszemy także obecne ramki na sekundę i figury proste narysowane w nagłówku okna. Test dla 'irr::IrrlichtDevice::isWindowActive()' jest opcjonalny, ale zapobiega problemowi łapania kursora myszki przez silnik po zmianie zadań gdy inne programy są aktywne. Wezwanie 'irr::IrrlichtDevice::yield()' zapobiegnie pochłonięciu wszystkich zasobów procesora gdy okno nie będzie aktywne.
       int lastFPS = -1;

        while(device->run())
        {
                if (device->isWindowActive())
                {
                        driver->beginScene(true, true, video::SColor(255,200,200,200));
                        smgr->drawAll();
                        driver->endScene();

                        int fps = driver->getFPS();

                        if (lastFPS != fps)
                        {
                                core::stringw str = L"Irrlicht Engine - Quake 3 Map example [";
                                str += driver->getName();
                                str += "] FPS:";
                                str += fps;

                                device->setWindowCaption(str.c_str());
                                lastFPS = fps;
                        }
                }
                else
                        device->yield();
        } 

Na końcu, usuwamy urządzenie Irrlicht.
       device->drop();
        return 0;
} 

I to by było na tyle. Skompiluj i baw się programem.


PiotrSOG