Bäume
Mit den Bäumen verhält es sich ähnlich wie mit dem Gras:
Auch für ihre Generierung sind keine speziellen Methoden in Irrlicht vorhanden und auch
diesmal kann man sich mit der Vorarbeit anderer behelfen: dem Tree Scene Node.
Dieses Plugin generiert Bäume auf Grundlage von XML-Design-Files. Eine ausführliche
Beschreibung des Formats und eine Anleitung zum Erstellen eigener Baumdesigns findet sich hier.
Die vordefinierten Bäume werden durch einen pseudo-Zufallsgenerator abgeändert,
so dass jedes Mal ein anderer Baum generiert wird. Ein Baummesh kann also durch sein
Designfile in Verbindung mit der verwendeten Zufallszahl zur Generierung eindeutig
beschrieben werden. Die Blätter sind Gruppen von Billboards.
Lädt man den Tree Scene Node herunter, so werden 4 Designs inklusive Texturen für
Rinde und Blätter mitgeliefert. Außerdem enthalten sind Shader.
Die Bäume haben 3 LODs: für weite Entfernung ein Billboard, für mittlere
ein Baum mit reduzierter Astanzahl und schließlich der Originalbaum.
Und so benutzt man den Tree Scene Node:
Zu Beginn wieder das Importieren der benötigten Dateien.
#include "CTreeGenerator.h"
#include "CBillboardGroupSceneNode.h"
#include "CTreeSceneNode.h"
Anschließend wieder ein paar Deklarationen.
Die Designfiles sind als Struct implementiert. Sie beinhalten Pointer auf das Designfile
selbst sowie auf die benötigten Texturdateien.
Das Struct STreeDesign enthält Pointer auf den Baumgenerator sowie die
Texturen.
CTreeGenerator* generator = 0;
CTreeSceneNode* tree = 0;
ITexture* billTexture = 0;
ITexture* leafTexture = 0;
struct STreeDesignFiles
{
- const c8* DesignFile;
- const c8* TreeTextureFile;
- const c8* LeafTextureFile;
const c8* BillTextureFile;
};
struct STreeDesign
{
- CTreeGenerator* Generator;
- ITexture* TreeTexture;
- ITexture* LeafTexture;
- ITexture* BillTexture;
};
Als nächstes wird ein TreeDesignFiles-Struct definiert.
const s32 NUM_TREE_DESIGNS = 4;
const STreeDesignFiles treeDesignFiles[NUM_TREE_DESIGNS] = {
-
{ "trees/Oak.xml", "textures/OakBark.png", "textures/leaves/OakLeaf.png", "textures/OakBillboard.png" },
- { "trees/Aspen.xml", "textures/AspenBark.png", "textures/leaves/AspenLeaf.png", "textures/AspenBillboard.png" },
- { "trees/Pine.xml", "textures/PineBark.png", "textures/leaves/PineLeaf.png", "textures/PineBillboard.png" },
- { "trees/Willow.xml", "textures/WillowBark.png", "textures/leaves/WillowLeaf.png", "textures/WillowBillboard.png" }
};
Und noch mal ein paar Deklarationen.
Die Variable lightDir wird später für den Shader gebraucht. Der
Vektor gibt die Richtung des direktionalen Lichts an.
Die Variable seed wird später mit den Zufallszahlen für die
Baumgenerierung gefüllt.
STreeDesign treeDesigns[NUM_TREE_DESIGNS];
s32 currentTreeDesign = 0;
vector3df lightDir = vector3df(-1,-1,-1);
s32 seed = 0;
E_MATERIAL_TYPE leafMaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL;
Jetzt folgt die Funktion zur Generierung eines neuen Baumes bzw. zum Setzen seiner
Eigenschaften.
(Nein, diese ist nicht in der Bibliothek enthalten...)
Die Methode setup ist im Wesentlichen dafür zuständig, die
verschiedenen LODs zu erstellen und im Meshbuffer abzulegen, sowie die Blätter zu generieren.
Dafür braucht sie 3 Argumente:
- generator: Ein Pointer auf den Generator (zur Erinnerung: jedes Design hat seinen eigenen)
- seed: Eine Zufallszahl
- billboardTexture: Die Textur für die Blätter.
void generateNewTree()
{
-
tree->setup(
- treeDesigns[currentTreeDesign].Generator, seed, treeDesigns[currentTreeDesign].BillTexture);
- tree->getLeafNode()->getMaterial(0).TextureLayer[0].AnisotropicFilter = true;
- tree->getLeafNode()->getMaterial(0).TextureLayer[0].BilinearFilter = false;
- tree->getLeafNode()->setMaterialTexture(
- 0, treeDesigns[currentTreeDesign].LeafTexture);
- tree->getLeafNode()->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
- tree->setMaterialTexture(0, treeDesigns[currentTreeDesign].TreeTexture);
- tree->getLeafNode()->applyVertexShadows(lightDir, 1.0f, 0.25f);
- tree->getLeafNode()->setMaterialType(leafMaterialType);
}
Das war dann endlich alles, was außerhalb der main-Funktion getan werden muss. In der
main-Funktion muss nun das TreeDesigns-Struct definiert werden.
Dafür wird zunächst der Generator gebraucht. Um die XML-Designfiles zu lesen
wird Irrlichts XMLReader verwendet.
Danach werden die Texturen geladen.
//load tree designs
for (s32 i=0; i<NUM_TREE_DESIGNS; i++)
{
- treeDesigns[i].Generator = new CTreeGenerator(irrSceneMgr);
- IXMLReader* xml =
- irrDevice->getFileSystem()->createXMLReader(treeDesignFiles[i].DesignFile);
- treeDesigns[i].Generator->loadFromXML(xml);
- xml->drop();
- treeDesigns[i].TreeTexture =
- irrDriver->getTexture(treeDesignFiles[i].TreeTextureFile);
- treeDesigns[i].LeafTexture =
- irrDriver->getTexture(treeDesignFiles[i].LeafTextureFile);
- treeDesigns[i].BillTexture =
- irrDriver->getTexture(treeDesignFiles[i].BillTextureFile);
}
Nun kommt der Shader zum Einsatz.
//load leaf shader
leafMaterialType = (E_MATERIAL_TYPE)
irrDriver->getGPUProgrammingServices()->addHighLevelShaderMaterialFromFiles(
-
"shaders/leaves.vert", "main", EVST_VS_2_0, "shaders/leaves.frag", "main",
EPST_PS_2_0, 0, EMT_TRANSPARENT_ALPHA_CHANNEL, 0);
Jetzt kann fleissig generiert werden. Die 3 Schleifen dienen der Positionierung
der Bäume. Damit sie ein bisschen "natürlicher" verteilt sind,
werden danach ein paar von ihnen versetzt. An dieser unschönen Methode kann man schon sehen, dass die Art der Erstellung und Platzierung der Bäume ein wenig ungeeignet ist
für die Erstellung von Wäldern.
Die verschiedenen Baumarten (TreeDesigns) werden der Reihe nach generiert.
//generate trees
for (int k = -1; k<=1; k+=2)
for (int j = 0; j<=200; j+=200)
for (int i = 0; i<=1100; i+=550)
{
- if (currentTreeDesign >= NUM_TREE_DESIGNS-1) currentTreeDesign = 0;
- else currentTreeDesign++;
- tree = new CTreeSceneNode(irrSceneMgr->getRootSceneNode(), irrSceneMgr);
- tree->drop();
- generateNewTree();
- tree->setID(j+k+i);
- tree->setPosition(terrainCenter + vector3df(i*2*k-4000*k, -250, i+j*k));
};;
irrSceneMgr->getSceneNodeFromId(199)->setPosition(terrainCenter+vector3df(800,-250,900));
irrSceneMgr->getSceneNodeFromId(201)->setPosition(terrainCenter+vector3df(200,-250,100));
irrSceneMgr->getSceneNodeFromId(201+550)->setPosition(terrainCenter+vector3df(-2200,-250,-200));
irrSceneMgr->getSceneNodeFromId(199+550)->setPosition(terrainCenter+vector3df(-2600,-250,-1200));
}
Alles was nun noch zu tun bleibt, ist: Richtig, zerstören... (Am Ende des Programms)
for ( s32 i=0; i
{
- treeDesigns[i].Generator->drop();
}
Das war`s, die Szene ist komplett.
zurück