Tutoriál 3: Vlastní scénové uzly
Přeložil: Martin Zima alias "RedDragCZ"
Tento tutoriál je pro pokročilejší vývojáře a pokud si zatím jen tak s Irrlichtem spíše hrajete a zkoušíte jeho možnosti, podívejte se nejdříve na ostatní ukázky a návody. Tento tutoriál ukazuje, jak vytvořit vlastní druhy scénových uzlů a jak je používat v enginu. Ty jsou užitečné, pokud chceme implementovat nové renderovací techniky, které zatím Irrlicht Engine nepodporuje. Například, můžeme tak napsat indoor portálově-založené renderery nebo pokročilejší terénové scénové uzly. Vytvořením vlastních scénových uzlů můžeme jednoduše rozšířit možnosti Irrlichtu a adaptovat je na naše potřeby.
Ponecháme tutoriál jednodušší tím, že se zde nebudeme zabývat ostatními druhotnými věcmi kvůli zkrácení kódu, a také ho celý necháme jen v jednom .cpp souboru. Na konci tutoriálu bude výsledek vypadat stejně, jako obrázek níže. Nevypadá to nijak zázračně, ale je to kompletně customizovaný scénový uzel a je to dobrý bod, ze kterého se odrazit při pozdějším výtváření Vašich vlastních.
|
| Lets start! |
Na začátku kódu, stejně jako i v ostatních tutoriálech, si includneme hlavičkové soubory Irrlichtu, použijeme zkrácení pro práci s kódem v namespacu irr, a nastavíme linkování statické knihovny.
#include <irrlicht.h> using namespace irr; #pragma comment(lib, "Irrlicht.lib") |
Zde přicházi nejsložitější část celého tutoriálu: Třída našeho vlastního druhu scénového uzlu. Pro jednoduchost, nebude to indoor portálový renderer a ani terénový scénový uzel, ale čtyřstěnný, pravidelný jehlan, skládající se z 4 propojených vrcholů, který se pouze sám vykreslí a nedělá nic dalšího speciálního.
Abychom mohli použít náš scénový uzel v Irrlichtu, bude potřeba, aby daná třída byla odvozená ze třídy IsceneNode a přepisovala některé její metody.
class CSampleSceneNode : public scene::ISceneNode { |
Nejprve deklarujeme některé členské proměnné, které budou obsahovat data našeho jehlanu: bounding box (box ohraničující geometrii uzlu, používá se například pro automatický culling v Irrlichtu a mnohé další věci), 4 vrcholy a jeho materiál.
core::aabbox3d<f32> Box; video::S3DVertex Vertices[4]; video::SMaterial Material; |
Parametry konstruktoru specifikují rodičovský scénový uzel, ukazatel na scénového managera a scénové id uzlu. V konstruktoru samotném pak volámě konstruktor bázové třídy, nastavíme některé vlastnosti materiálu, kterého používáme pro vykreslování uzlu a vytvoříme 4 vrcholy jehlanu.
public:
CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
: scene::ISceneNode(parent, mgr, id)
{
Material.Wireframe = false;
Material.Lighting = false;
Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,video::SColor(255,0,255,255),0,1);
Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,video::SColor(255,255,0,255),1,1);
Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,video::SColor(255,255,255,0),1,0);
Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,video::SColor(255,0,255,0),0,0);
|
Irrlicht Engine potřebuje znát bounding box našeho uzlu, aby pomocí něj mohl provádět automatický culling a některé další věci. Vytvoříme tedy bounding box z námi nadefinovaných vrcholů. Pokud bychom nechtěli, aby engine prováděl culling, nebo vytvářet bounding box vůbec, můžeme taktéř napsat
AutomaticCullingEnabled
= false;.
Box.reset(Vertices[0].Pos); for (s32 i=1; i<4; ++i) Box.addInternalPoint(Vertices[i].Pos);
} |
Před jakámkoli renderováním je zavolána metoda OnRegisterSceneNode() každého scénovéh uzlu. Pokud si scénový uzel přeje být vykreslen, registruje se ve scénovém managerovi. To je nezbytné, aby manager vědel, kdy zavolat metodu render. Pro příklad, normální scénové uzly renderují jejich obsah jeden po druhém, oproti tomu takový stencil buffer potřebuje být vykreslen až po všech ostatních uzlech, a kamery a světla zase před.
Zde tedy jednoduše registrujeme uzel aby byl renderován normálně, pokud bychom ale chtěli, aby byl renderován jak kamery nebo světla, zavolali bychom SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA); .
Po tomto kroku zavoláme metodu OnRegisterSceneNode bázové třídy ISceneNode, která nechá také všechny potomky uzlu se registrovat.
virtual void OnRegisterSceneNode() { if (IsVisible) SceneManager->registerNodeForRendering(this);
ISceneNode::OnPreRender();
} |
V metodě render() se odehrává to nejzajímavější – samotné renderování uzlu. Přepíšeme tuto metodu a vykreslíme jehlan.
virtual void render() { u16 indices[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 };
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(Material);
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
} |
Nakonec vytvoříme tři malé metody: getBoundingBox() vrací BB daného scénového uzlu, getMaterialCount() vrací množství materiálů, a getMaterial() vrací materál na daném indexu (vzhledem k tomu, že zde máme pouze jeden materiál, tak můžeme vrátit rovnou jej).
virtual const core::aabbox3d<f32>& getBoundingBox() const { return Box; } virtual s32 getMaterialCount()
{
return 1;
}
virtual video::SMaterial& getMaterial(s32 i)
{
return Material;
}
}; |
Hotovo. Scénový uzel je hotov. Nyní už pouze klasicky spustíme engine, vytvoříme scénový uzel a kameru.
int main() {
IrrlichtDevice *device =
createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480), 16, false);
device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));
|
Vytvoříme náš scénový uzel. Všimněte si, že je dropnut ihned poté, co ho vytvoříme. Toto je možné protože se o něj scénový manager sám postará. Není to ale nutné, a mohlo by to být provedeno až na konci programu.
CSampleSceneNode *myNode = new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);
myNode->drop(); |
Pro rozpohybování této jinak nudné scény obsahující pouze jeden jehlan, a také pro názornou ukázku toho, že nyní můžeme používat náš scénový uzel jako jakýkoli jiný v enginu, mu přidáme animátor, který bude uzlem rotovat.
scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));
myNode->addAnimator(anim);
anim->drop(); |
Nyní vše vykreslíme a dofinishujeme kód.
while(device->run()) { driver->beginScene(true, true, video::SColor(0,100,100,100));
smgr->drawAll();
driver->endScene();
}
device->drop();
return 0;
} |
Hotovo, to je celé. Zkompilujte a pohrajte si s tím. :-)
|
|
|