Obsah dílu:
1. základy detekce kolizí
2. kolize ve hře
3. střílení
4. exploze
1. Základy detekce kolizí
Nejčastější úlohou, pro kterou detekci kolizí používáme, je zajistit, aby tělesa ve virtuální scéně skrze sebe volně neprocházela. V každém případě mají algoritmy detekce kolizí tendenci být výkonově velmi náročně a téměř nikdy se neobejdeme bez optimalizací.
Nejzákladnější optimalizací pro kolizní algoritmy je použití modelů s velmi redukovaným počtem trojúhelníků. Modely, které použijeme, jsou zobrazemy na následujících obrázcích:
Proč je počet trojúhelníků tak kritický? Protože při hledání kolize se hledají průniky každého trojúhelníku s každým trojúhelníkem druhého tělesa. Zredukujeme-li počet trojúhelníků desetkrát pro kolizní tělesa, získáme stokrát nižší časovou náročnost.
V praxi existují i určité optimalizace. Například Inventor obaluje všechny tělesa „bounding boxem“ (čti baunding box), česky: ohraničující kvádr, či obalové těleso. Inventor nejprve testuje tato obalová tělesa a teprve, když zjistí jejich průnik, porovnává tělesa trojúhelník po trojúhelníku.
Použití uvedených dvou optimalizací, tedy obalových těles a kolizních těles s redukovaným počtem trojúhelníků, redukovalo časovou náročnost kolizních algoritmů do té míry, že další optimalizace už nebyly nutné.
Byl však objeven jeden chyták: Při testování jedné aplikace nám detekce kolizí neustále hlásila, že ke kolizi došlo, přestože na obrazovce se vše zdálo v pořádku a všechna tělesa byla od sebe dostatečně daleko, aby nebylo nejmenších pochyb, že bylo něco přehlédnuto. Po chvilce debugování bylo zjištěno, že všechny kolidující dvojce trojúhelníků nepatří dvěma různým tělesům, jak by se očekávalo, ale byly to trojúhelníky téhož tělesa. Po chvilce zmatení se objevilo vysvětlení: těleso bylo složeno z více So(Indexed)TriangleStripSetu uspořádaných tak, že mezi některými trojúhelníky docházelo ke kolizi. Přesněji řečeno: trojúhelníky patřící do jednoho So(Indexed)TriangleStripSetu kolizi nezpůsobí, pouze trojúhelníky patřící různým So(Indexed)TriangleStripSetům, které se protínají. Samozřejmě se daná vlastnost netýká pouze So(Indexed)TriangleStripSet, ale všech tříd odvozených od SoShape.
Výše řečený chyták se objevil i u modelu bludiště. Zde je problém ošetřen „filtrovací funkcí“, kterou si ukážeme později a která zabrání detekovat kolize mezi So(Indexed)TriangleStripSety patřící jednomu tělesu. Alternativní řešení by bylo překonvertovat celou geometrii tělesa do jediného So(Indexed)TriangleStripSetu.
Pro detekci klizí slouží třída SoIntersectionDetectionAction, která je odvozena od SoAction. Základní metodou pro její použití je, podobně jako u všech akcí, metoda apply:
void SoIntersectionDetectionAction::apply(SoNode *root);
Tato metoda projde celý graf scény, jehož kořen je dán parametrem root. Přitom to nutně nemusí být graf totožný s rendrovacím grafem, ale může to být graf speciálně zoptimalizovaný pro učely kolizních algoritmů.
Metoda apply hledá v grafu všechny trojúhelníky a testuje jejich průnik. V Inventoru byla zavedena konvence, že pouze třídy odvozené od SoShape mohou obsahovat geometrii těles. Každá takováto třída přitom umí překonvertovat svůj obsah na trojúhelníkovou podobu a to i třídy, které normálně trojúhelníkovou reprezentaci neuchovávají (NURBS plochy a podobně). Získané trojúhelníky jsou pak použity pro detekci kolizí. A jak již bylo naznačeno dříve, trojúhelníky jednoho SoShape objektu nejsou testovány na kolize, pouze trojúhelníky různých SoShape objektů.
Je-li kolize detekována, je zavolán callback. V něm může uživatel získat informaci o kolidujících trojúhelnících, nebo například nastavit boolean indikující existenci kolize ve scéně.
2. Kolize ve hře
V Tancích kvůli kolizím používáme dva grafy scény:
SoSeparator *rootkolize; SoSeparator *bludisteKolize;
Graf scény pak konstruujeme dvakrát – jednou pro renderování a jednou pro kolize. Často se liší pouze v názvu souboru, ze kterého byla načtena geometrie tělesa:
this->model->name.setValue("models/tank.wrl"); this->modelKolize->name.setValue("models/tankkolize.wrl");
Některé nody jsou mezi grafy sdíleny, jako například translace tanku:
// posunuti tanku this->trans = new SoTranslation; this->root->addChild(this->trans); this->rootKolize->addChild(this->trans);
Další detaily již najdeme ve zdrojácích.
Pro kolize používáme tři různé akce:
SoIntersectionDetectionAction *ida; //testovani kolizi tanku s bludistem SoIntersectionDetectionAction *ida2; //testovani kolizi strely s bludistem a tanky SoIntersectionDetectionAction *ida3; //testovani kolizi mezi tanky
Teoreticky by stačila pouze jedna třída, ale celá věc byla implementována jinak. Detaily můžeme najít ve funkci nazvané prepocitejScenu. Ta pracuje v těchto krocích:
- uschovej aktuální pozici tanku
- aktualizuj pozici a rotaci tanku podle toho, které klávesy uživatel stiskl
- testuj kolize mezi pohyblivými objekty (střely a tanky); došlo-li ke kolizi, obnov minulou pozici tanku
- testuj kolizi tanku a bludiště; došlo-li ke kolizi a jedná se o náraz do zdi pod šikmým úhlem, zkus klouzat při zdi; jinak obnov předchozí pozici tanku
- aktualizuj pozici střely
- došlo-li ke kolizi, vytvoř objekt výbuchu a smaž objekt střely
3. Střely
Je-li stisknuta klávesa pro střílení, je při aktualizaci scény vytvořen nový objekt střely. Inicializace střely je provedena v metodě Strela::start() a její pseudo-kód je následující:
modelStrely->setPosition(tankPosition); modelStrely->setDirection(tankRotation, hlavenSmer); korenSceny->addChild(modelStrely);
Dále už je pozice střely pouze aktualizována podle rovnice balistické střely v metodě Strela::refresh().
4. Exploze
Žádná hra se neobejde bez efektů jako jsou například výbuchy. Je mnoho metod, jak realizovat výbuch. Dvě metody jsou asi nejčastější – částicové systémy a animovaný billboard. Druhý z nich je méně výkonově náročný a také jej použijeme. Technika billboardingu se v počítačové grafice používá často. Je to objekt, nejčastěji čtverec, který se neustále otáčí ke kameře. Na následujícím obrázku můžeme vidět příklad billboardu.
Naše exploze bude vytvořena billboardem, na který bude nanesena animovaná textura. Ve skutečnosti se jedná o jedinou texturu obsahující 16 obrázků animované exploze. Tuto texturu si můžete sami vytvořit na stránkách www.geocities.com/starlinesinc/index.html
Pro efekt animované exploze potřebujeme texturu korektně namapovat na náš billboard. Celé střídání jednotlivých obrázků je realizováno změnou texturovacích souřadnic. Textury měníme v metodě Vybuch::refresh(). Nastavují-li se korektně, textury se postupně střídají a vytvářejí efekt exploze.
Závěr
Zájemci o další věci jako zvuk, síťová hra, umělá inteligence, bump-mapping, ovládání myší a další věci, jejichž předzvěst ukazuje následující screenshot:
se mohou podívat na oficiální stránky projektu. K tomu je přidána i kopa dalších užitečných linků:
- Tanky – tanky.sourceforge.net – síťová hra napsaná v Open Inventoru, na základě které vznikl tento tutoriál
- Open Inventor Tools – sada velmi užitečných utilit pro práci s 3D modely
- různé projekty vytvořené na FIT v roce 2005 a 2006
Tímto tento tutoriál končí a bude následovat tutoriál o OSG nebo-li OpenSceneGrafu, což je jiná knihovna pro 3D grafiku.
Ke stažení
Zdrojáky: 5–2-Tanky-2.zip
Tutoriál: tutorial13.zip