• Projektinhalt
  • Game Engines
  • Glossar
  • Tutorial
    1. Erste Schritte
    2. Treiber initialisieren
    3. Skybox & Skydome
    4. GUI Text
    5. Terrain
    6. Nebel
    7. Objekte einfügen
    8. Kamera
    9. Beleuchtung
    10. Kollisionsdetektion
    11. Hintergrundmusik
    12. Szene darstellen
    13. Gras
    14. Bäme

  • Download
  • Links

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

Katarina Boland