Irrlicht - from Noob to Pro: Klassen in Irrlicht verwenden

Aus Wikibooks


Voraussetzungen[Bearbeiten]

In diesem Teil des Buches geht es darum, eine komplette Szene in Irrlicht zu erstellen und den Würfel aus dem vorhergehenden Teil des Buches in eine eigene Klasse zu packen. Dieser Würfel muss dann abgeleitet werden von Irrlichts ISceneNode-Klasse, um Ihn in einen Szene-Manager von Irrlicht registrieren zu können. Sie sollten verstanden haben

  • wie 3D-Objekte aufgebaut sind,
  • wie die Farbwerte von Vertices angegeben werden,
  • mehrere indexdefinierte Dreiecke dargestellt werden,
  • wie eine Zeitmessung in das Programm eingebaut wird und
  • wie man ein Objekt über seine Matrix dreht.

Falls Sie sich in diesen Bereichen noch nicht sicher sind, dann werfen Sie doch einen Blick in den vorhergehenden Teil des Buches. Wenn Sie sich in der Definition von Klassen in C++ nicht sicher sind, so empfehle ich Ihnen einen Blick auf das Kapitel "Klassen" im Buch C++-Programmierung.

Die Klasse CWuerfel erstellen[Bearbeiten]

Wir wollen hier nun für den Würfel aus dem vorhergehenden Kapitel eine Klasse erstellen, welche sich CWuerfel nennt. Dazu benötigen wir ersteinmal 2 neue Dateien, welche wir in unser Projekt neben der Quellcodedatei main.cpp einfügen.

Benötigte Dateien[Bearbeiten]

Wir benötigen für unsere Klasse

  • eine neue Header-Datei, gespeichert unter CWuerfel.h sowie
  • eine neue Quellcode-Datei, gespeichert unter CWuerfel.cpp.

Um diese zu erstellen, klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den OrdnerHeaderdateien,dann auf Hinzufügen und dann auf Neues Element. In den nachfolgenden Dialog fügen Sie die neue Headerdatei CWuerfel.h hinzu. Verfahren Sie anschließend im Projektmappen-Explorer mit dem Ordner Quellcodedateien entsprechend, um die Quellcodedatei CWuerfel.cpp hinzuzufügen.

Einfügen eines Include-Guard[Bearbeiten]

Bevor Sie nun beginnen, den Code einzugeben, sollten Sie einen sog. "Include-Guard" einfügen, welcher die doppelte Einbindung einer Header-Datei verhindert, da dies sonst zu einer Fehlermeldung führt. Öffnen Sie nun die Headerdatei CWuerfel.h und fügen Sie den Guard mit folgendem Code ein:

//"Include-Guard"
#ifndef __CWuerfel_H
  #define __CWuerfel_H

#endif

Wenn Sie nun den Quellcode eingeben, so fügen Sie Ihn zwischen

#define __CWuerfel_H

und

#endif

ein. Somit kann die Deklaration der CWuerfel-Klasse nur einmal eingebunden werden.

Einbinden von Irrlicht[Bearbeiten]

Da wir für die Deklaration Klassen und Datentypen aus Irrlicht verwenden, müssen wir dieses auch einbinden. Dies tun wir in der Header-Datei mit folgendem Code :

//Einbinden der Header-Datei von Irrlicht
#include <Irrlicht.h>

//Einbinden der Namespaces
using namespace irr;
using namespace core;
using namespace video;

Ableiten der Klasse[Bearbeiten]

Da die Würfel-Klasse in einem ISceneManager verwendet werden soll, müssen wir sie aus der Irrlicht-eigenen Klasse ISceneNode ableiten, um Sie später zum Rendern im Szene-Manager von Irrlicht registrieren zu können. Erstellen Sie in der Header-Datei CWuerfel.h nun folgendes Grundgerüst der Würfel-Klasse :

class CWuerfel : public scene::ISceneNode
{
public:
	//Konstruktor
	CWuerfel::CWuerfel(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id);
protected:
};

Der Konstruktor der Klasse wird uns durch die Ableitung von ISceneNode vorgegeben und dient der Kommunikation zwischen Irrlicht und unserer Würfel-Klasse. Die Parameter haben folgende Bedeutung :

  • scene::ISceneNode* parent definiert, ob die Node einer "Eltern-Node" folgen soll. Falls dies nicht der Fall ist, so verwendet man die Root-Node des Szene-Managers, wodurch sie keine Parent-Node zugewiesen bekommt.
  • scene::ISceneManager* mgr definiert den Szene-Manager, in welchem die Node registriert werden soll.
  • s32 id bestimmt die ID-Nummer der Node, um Sie eindeutig identifizieren zu können. Da wir momentan nur einen Würfel verwenden, können wir z.B. die "1" verwenden.

In der Quellcode-Datei CWuerfel.cpp fügen Sie nun folgendes Grundgerüst ein :

//Einbinden der Header-Datei von Irrlicht
#include <Irrlicht.h>
//Einbinden der Headerdatei der Würfel-Klasse
#include "CWuerfel.h"

//Einbinden der Namespaces
using namespace irr;
using namespace core;
using namespace video;

//!Der Konstruktor für die Würfel - Klasse
CWuerfel::CWuerfel(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
		: scene::ISceneNode(parent, mgr, id)
{

}

Kommunikation mit Irrlicht[Bearbeiten]

Um nun den Datenaustausch mit Irrlicht zu gewährleisten, müssen wir noch ein paar Prozeduren einbauen, welche die korrekte Darstellung in der Engine ermöglichen, da Irrlicht seine zu zeichnenden Objekte ("Nodes") auf ein paar Eigenschaften abfragen will. Diese sind folgende ...

Registrierung der Node[Bearbeiten]

Um überhaupt eine Einbindung zu erreichen, muss die Node im Szene-Manager registriert werden. Dieser prüft, ob die Node als "sichtbar" markiert ist und fügt diese zu den zu zeichnenden Nodes hinzu. Dies geschieht durch folgenden Code (in der Quellcode-Datei CWuerfel.cpp) :

//!Registriert den Würfel im Szene-Manager von Irrlicht zum Rendern
void CWuerfel::OnRegisterSceneNode()
{
	if (IsVisible) //Falls sichtbar ...
	 SceneManager->registerNodeForRendering(this); //zum Zeichnen registrieren
	 //Aufruf des Registriervorgangs	
	 ISceneNode::OnRegisterSceneNode();
}

In der Header-Datei bindet man diese Prozedur dann wie folgt ein :

virtual void OnRegisterSceneNode();   //Registrieren des Würfels im Szene-Manager

Anzahl der verwendeten Materialien[Bearbeiten]

Da Irrlicht wissen will, wieviel Materialien der Würfel verwendet, werden wir diesem mit folgendem Code gerecht :

//!Gibt die Anzahl der von uns verwendeten Materialien zurück
u32 CWuerfel::getMaterialCount() const
{
	return 1;
}

Da wir nur ein Material haben, können wir den Wert "1" zurückgeben. In der Header-Datei wird dies definiert durch :

virtual u32 getMaterialCount() const; //Anzahl der Materialien übergeben

Übergeben eines Materials[Bearbeiten]

Da Irrlicht zum Rendern die / das Material(ien) abruft, verlangt die ISceneNode-Klasse die direkte Übergabe eines Materials in Form von

//Gibt ein bestimmtes Material zurück
video::SMaterial& CWuerfel::getMaterial(u32 i)
{
	return mat;
}

Dies wird in der Header-Datei Deklariert durch

virtual video::SMaterial& getMaterial(u32 i); //Ein bestimmtes Material übergeben

. Des weiteren können wir im protected-Abschnitt der Header-Datei ein Material mit

SMaterial mat;

deklarieren.

Übergeben der Bounding-Box[Bearbeiten]

In Irrlicht werden Bounding-Boxen zur Kollisionserkennung sowie zum Culling verwendet. Da dies eine Variable erfordert definieren wir diese mit

core::aabbox3d<f32> ABox;

In der Header-Datei müssen wir nun die Deklaration der Funktion angeben mit

virtual const core::aabbox3d<f32>& getBoundingBox() const; //Die Bounding-Box übergeben

In der Quellcodedatei sieht diese Funktion dann so aus :

//!Gibt unsere Bounding-Box an Irrlicht zurück
const core::aabbox3d<f32>& CWuerfel::getBoundingBox() const
{
	return ABox;
}

Programmieren des Rendervorgangs[Bearbeiten]

Da wir nun Irrlicht die erforderlichen Daten zum Zeichnen übergeben haben, überschreiben wir hier einfach den Rendervorgang der ISceneNode-Klasse mit unserem eigenen Code, da wir unseren Würfel selbst zeichnen wollen. Deshalb deklarieren wir in der Header-Datei

virtual void render(); //Zum Zeichnen des Würfels

Nun geht es daran, den Rendervorgang in der Quellcode-Datei zu programmieren. Da wir aber bisher nur das Material deklariert haben, benötigen wir im protected-Abschnitt unserer Klasse in der Header-Datei noch die Arrays für die Indicies und die Vertices. Diese erstellen wir mit

S3DVertex cwVertices[8]; //Das Array vom Typ S3DVertex zum Speichern der Vertices

und

u16 uiIndicies[36]; //Das Array für unsere Indicies

Nun können wir den Rendervorgang programmieren, in welchem wir uns zuerst einen Zeiger auf den Video-Treiber holen, mit welchem wir das Material für unseren Würfel festlegen. Danach bestimmen wir, dass wir die Weltkoordinaten der Node (nicht wie im vorhergehenden Beispiel der kompletten Szene) verwenden wollen, da wir sonst alles bewegen würden. Dies führt bei der Anwendung mehrerer Würfel zu Problemen. Anschließend sind wir soweit, den Würfel zeichnen zu lassen :

//!Zeichnet unseren Würfel
void CWuerfel::render()
{
	//Den Treiber einholen
	video::IVideoDriver* driver = SceneManager->getVideoDriver();

	//Das Material festlegen
	driver->setMaterial(mat);	
	
	//Wir verändern hier die Weltkoordinaten ...
	driver->setTransform(ETS_WORLD, AbsoluteTransformation);    
	
	//Zeichnen des Würfels
	driver->drawIndexedTriangleList(
			&cwVertices[0],		//Zeiger auf das erste Element im Vertices-Array
			8,			//Anzahl der Elemente im Vertices-Array
			&uiIndicies[0],		//Zeiger auf das erste Element in Indicies-Array
			12);			//Anzahl der zu zeichnenden Dreiecke
}

Festlegen der Größe[Bearbeiten]

Wir wollen nun eine Prozedur einbauen, in welcher die Größe des Würfels angegeben wird. Der Würfel soll um den Nullpunkt erstellt werden. Die Vertices sollen über die Breite-, Höhe- und Tiefenangabe errechnet werden. Dazu benötigen wir ein paar Hilfsvariablen, welche die Dimensionen des Würfels speichern :

f32 fMaxLinks, fMaxRechts,	   //Zur Definition der Grenzen im Koordinatensystem
    fMaxVorne, fMaxHinten,
    fMaxOben, fMaxUnten,
    cwWidth, cwHeight, cwDepth;	  //Zum Speichern der Dimensionen des Würfels

Nun vermerken wir die Dimension des Würfels einfach in den Hilfsvariablen unserer Klasse. Zuerst aber deklarieren wir die Prozedur mit

void SetSize(f32 fWidth, f32 fHeight, f32 fDepth); //Größe des Würfels angeben

in der Header-Datei und bauen diese Funktion dann anschließend in die Quellcode-Datei ein:

//!Setzt die Größe des Würfels auf die angegebenen Werte
void CWuerfel::SetSize(f32 fWidth, f32 fHeight, f32 fDepth)
{
	cwWidth = fWidth;	//Speichern der Dimensionen ...
	cwHeight = fHeight;	//in den Hilfsvariablen ...
	cwDepth = fDepth;       //der Klasse	
}

Erstellen der Vertices[Bearbeiten]

Da wir nun die Vertexdaten berechnen können, wollen wir dies als eine Prozedur im protected-Bereich der Header-Datei deklarieren :

void Create_Vertices(); //Zum Erstellen der Eckpunkte

Da die Dimensionen in den Hilfsvariablen bekannt sind, können wir uns daran machen, die Grenzen des Würfels im Koordinatensystem zu berechnen und dann in den Hilfsvariablen

fMaxLinks, fMaxRechts, fMaxVorne, fMaxHinten, fMaxOben, fMaxUnten

zu speichern, was uns das definieren der Vertices erleichtert. Danach verwenden wir diese Hilfsvariablen zum Erstellen des Vertices-Array. Die Funktion in der Quellcode-Datei sieht wie folgt aus :

//!Erstellt die Eckdaten des Würfels
void CWuerfel::Create_Vertices()
{	
	/**** Linke und rechte Grenze ****/
	/*********************************/
	//Rechter Rand
	fMaxRechts = cwWidth / 2.0f;	//z.B. 3 : 2 = 1.5
	//Linker Rand
	fMaxLinks = (fMaxRechts - cwWidth); //z.B. 3 : 2 = 1.5 - 3 = -1.5
	/**** Vordere und hintere Grenze ****/
	/************************************/
	//Hinterer Rand
	fMaxHinten = cwDepth / 2.0f;
	//Vorderer Rand
	fMaxVorne = (fMaxHinten - cwDepth);
	/**** Obere und untere Grenze ****/
	/*********************************/
	//Oberer Rand
	fMaxOben = cwHeight / 2.0f;
	//Unterer Rand
	fMaxUnten = fMaxOben - cwHeight;

	//Vorne
	cwVertices[0] = S3DVertex(fMaxLinks, fMaxUnten, fMaxVorne,0,0,0,SColor(255,255,0,0),0,0); //Punkt A
	cwVertices[1] = S3DVertex(fMaxLinks, fMaxOben,  fMaxVorne,0,0,0,SColor(255,0,255,0),0,0); //Punkt B
	cwVertices[2] = S3DVertex(fMaxRechts,fMaxUnten, fMaxVorne,0,0,0,SColor(255,0,0,255),0,0); //Punkt C
	cwVertices[3] = S3DVertex(fMaxRechts,fMaxOben,  fMaxVorne,0,0,0,SColor(255,0,0,255),0,0); //Punkt D
	//Hinten
	cwVertices[4] = S3DVertex(fMaxLinks, fMaxUnten, fMaxHinten,0,0,0,SColor(255,255,255,0),0,0); //Punkt E
	cwVertices[5] = S3DVertex(fMaxLinks, fMaxOben,  fMaxHinten,0,0,0,SColor(255,0,255,255),0,0); //Punkt F
	cwVertices[6] = S3DVertex(fMaxRechts,fMaxUnten, fMaxHinten,0,0,0,SColor(255,255,0,255),0,0); //Punkt G
	cwVertices[7] = S3DVertex(fMaxRechts,fMaxOben,  fMaxHinten,0,0,0,SColor(255,0,0,255),0,0);   //Punkt H	
}

Festlegen der Bounding-Box[Bearbeiten]

Die Bounding-Box stellt ebenfalls einen (ggf. rechteckigen) Würfel dar, welcher die Node umgibt. Man kann diesen Würfel nun durch die Vertexdaten definieren, da diese ja die Aussengrenzen unseres Würfels darstellen. Die Definition in der Header-Datei lautet

void Create_BoundingBox();//Zum Erstellen der Bounding-Box

Das Definieren der Bounding-Box wird nur durch einen Durchlauf durch das Vertices-Array erreicht, da die Box die Vertices als interne Eckpunkte speichert. Dies geschieht in der Quellcode-Datei mit :

//!Erstellt unsere Axis Aligned Bounding Box
void CWuerfel::Create_BoundingBox()
{
	ABox.reset(cwVertices[0].Pos); //Reset in Initialisierung auf den ersten Vertex
	for (s32 i=1; i<8; ++i) //Hinzufügen der restlichen Vertices
			ABox.addInternalPoint(cwVertices[i].Pos);
}

Definieren der Indicies[Bearbeiten]

Auch dies erledigen wir durch eine einfache Prozedur, welche mit

void Create_Indicies(); //Zum Erstellen des Indicies-Array

in der Header-Datei definiert wird. Anschließend kopieren wir die Indicies aus einem lokalen Array in das globale Array, um Zugriff aus dem Rendervorgang zu haben. Dies sieht wie folgt aus:

//!Erstellt das globale Indicies-Array
void CWuerfel::Create_Indicies()
{
//Zuerst als lokales Array definieren ....
u16 ind[36] = {0, 1, 2, //A->B->C=Dreieck1 (Vorderseite)
	       2, 1, 3, //C->B->D=Dreieck2 (Vorderseite)
	       3, 6, 2, //D->G->C=Dreieck3 (Rechte Seite)
	       3, 7, 6, //D->H->G=Dreieck4 (Rechte Seite)		   
	       6, 7, 5, //G->H->F=Dreieck5 (Rückseite)
	       5, 4, 6, //F->E->G=Dreieck6 (Rückseite)
	       5, 1, 4, //F->B->E=Dreieck7 (Linke Seite)
	       4, 1, 0, //E->B->A=Dreieck8 (Linke Seite)
	       1, 5, 7, //B->F->H=Dreieck9 (Oberseite)
	       7, 3, 1, //H->D->B=Dreieck10 (Oberseite)
	       4, 0, 6, //E->A->G=Dreieck11 (Unterseite)
	       6, 0, 2};//G->A->C=Dreieck12 (Unterseite)
//... und dann ins globale Array kopieren
for (u8 i=0; i<36; i++)
		 uiIndicies[i] = ind[i];
}

Einbauen der Zeitsteuerung[Bearbeiten]

Um den Würfel zeitgesteuert drehen zu können, benötigen wir 4 Prozeduren, welche das ganze ermöglichen :

  • eine Prozedur, welche die Zeitspanne festlegt, nach der der Würfel gedreht werden soll (Fachbegriff dafür ist Delta Time),
  • eine Prozedur, welche sich den letzten Moment merkt, in dem der Würfel gedreht wurde,
  • eine Prozedur, welche die aktuelle Zeit als Parameter übergeben bekommt und welche diesen Zeitpunkt dann speichert und
  • eine Prozedur, die nach Ablauf der Delta Time den Würfel um einen bestimmten Winkel dreht.

Festlegen der Zeitspanne (Delta Time)[Bearbeiten]

Diese Funktion speichert die Delta Time in einer Variable in unserer Würfel-Klasse, welche deklariert wird durch :

u32 uDeltaTime; //Zeitspanne bis zu jeder Drehung

In der Header-Datei definieren wir nun eine Prozedur, welche die Delta Time als Parameter übergeben bekommt und diese dann abspeichert :

void SetDeltaTime(u32 uiDeltaTime); //Zum Festlegen der Zeitspanne bis zur Drehung

Nun können wir die Funktion in der Quellcode-Datei programmieren :

//!Übergibt die Zeitspanne, nach der eine Drehung stattfinden soll
void CWuerfel::SetDeltaTime(irr::u32 uiDeltaTime)
{
	this->uDeltaTime = uiDeltaTime;
}

Speichern des Zeitpunkts der letzten Drehung[Bearbeiten]

Hierzu deklarieren wir eine Variable in unserer Klasse mit

u32 uZuletztGestoppt; //Die letzte gestoppte Zeit

sowie die Prozedur zur Speicherung des Zeitpunkts in der Header-Datei durch

void SetTimePos(u32 uiTime); //Zum Speichern des letzten Moments der Drehung

Danach programmieren wir die Prozedur in der Quellcodedatei durch :

//!Merkt sich den letzten Moment der Drehung des Würfels
void CWuerfel::SetTimePos(irr::u32 uiTime)
{
	this->uZuletztGestoppt = uiTime;
}

Übergeben der aktuellen Zeit[Bearbeiten]

Auch hierfür deklarieren wir in der Header-Datei zuerst eine Variable mit

u32 uAktuelleZeit; //Zum Speichern der aktuellen Zeit

und anschließend eine Prozedur mit

void TellCurrentTime(u32 uiTime); //Zum übergeben der aktuellen Zeit

Dann können wir diese programmieren mit :

//!Übergibt die aktuelle Zeit an die Würfel-Klasse
void CWuerfel::TellCurrentTime(irr::u32 uiTime)
{
	this->uAktuelleZeit = uiTime;
}

Diese aktuelle Zeit muss dann bei jedem Programmdurchlauf übergeben werden, damit die Zeitsteuerung des Würfels funktioniert.

Die Zeitgesteuerte Drehung[Bearbeiten]

Da nun die Zeitsteuerung eingebaut ist, geht es an die Drehung des Würfels. Dazu benötigen wir eine Variable in unserer Klasse, welche den aktuellen Winkel des Würfels speichert. Diese deklarieren wir durch

f32 fWinkel; //Unser Winkel für die Rotation des Würfels

Nun geht es um die eigentliche Funktion zum Drehen, welche bei jedem Programmdurchlauf aufgerufen werden muss, um die Zeitsteuerung sowie die Drehfunktion der Würfel-Klasse aktuell zu halten. Diese deklarieren wir in der Header-Datei durch

void TurnIfDesired(); //Dreht den Würfel nach Ablauf der Zeitspanne

In dieser Funktion fragen wir zuerst ab, ob die festgelegte Delta Time abgelaufen ist. Falls das der Fall ist, dann erhöhen wir den Winkel und Drehen den Würfel und die Y- und Z-Achse. Die Funktion sieht wie folgt aus :

//!Dreht den Würfel nach Ablauf der Delta-Time
void CWuerfel::TurnIfDesired()
{
//Gewünschte Zeit vergangen ?
if (this->uAktuelleZeit >= (this->uZuletztGestoppt + this->uDeltaTime))
		{
			//Aktuellen Zeitmoment der Drehung speichern
			this->SetTimePos(this->uAktuelleZeit);
			//Den Winkel erhöhen
			this->fWinkel += 0.5f;
			//Nicht über 360 Grad drehen
			if (this->fWinkel > 360) this->fWinkel = 1;
		}
//Rotation ausführen
this->setRotation(core::vector3df(0,this->fWinkel,this->fWinkel));
}

Zurückgeben eines Debug-Strings[Bearbeiten]

Falls es mal bei Objekten zu Problemen in der Darstellung kommt, so ist es immer eine recht gute Hilfe, einen sog. Debug-String zur Hand zu haben. Wenn man diesen in Irrlicht anzeigt, dann lassen sich manche Fehler im Programm wie z.B. nicht initialisierte Variablen, Rechenfehler etc. leichter aufspüren.

Hierzu deklarieren wir diese Funktion in der Header-Datei mit

stringw GetDebugString(); //Gibt den Debug-String des Würfels zurück

In diesem Fall verwenden wir einen Rückgabewert vom Typ stringw, welcher in Irrlicht deklariert ist. Dieser Datentyp ist sehr gut für diese Zwecke geeignet, da er uns viel Konvertierungsarbeit erspart. Diese Konvertierungsroutinen sind im Datentyp bereits implementiert und werden selbstständig angewandt, falls es erforderlich ist. Kommen wir nun zur Programmierung der Funktion in unserer Würfel-Klasse :

//!Gibt uns einen Debug-String des Würfels zurück
stringw CWuerfel::GetDebugString()
{
		//Den Debug-String erstellen
		stringw str("fMaxRechts : "); str += this->fMaxRechts;
		str += L"\nfMaxLinks : "; str += this->fMaxLinks;
		str += L"\nfMaxOben : "; str += this->fMaxOben;
		str += L"\nfMaxUnten : "; str += this->fMaxUnten;
		str += L"\nfMaxVorne : "; str += this->fMaxVorne;
		str += L"\nfMaxHinten : "; str += this->fMaxHinten;
		str += L"\ncwWidth : "; str += this->cwWidth;
		str += L"\ncwHeight : "; str += this->cwHeight;
		str += L"\ncwDepth : "; str += this->cwDepth;
		str += L"\nfWinkel : "; str += this->fWinkel;
		str += L"\nuAktuelleZeit : "; str += this->uAktuelleZeit;
		return str; //Rückgabe aus der Funktion
}

Diesen String lassen wir uns dann später in Irrlicht-Fenster mit anzeigen.Gratulation, Sie haben ihre Würfel-Klasse fertig ! Nun wollen wir sie noch verwenden.

Anwenden der Würfel-Klasse[Bearbeiten]

Einbinden von Irrlicht und der Würfel-Klasse[Bearbeiten]

Da wir nun Irrlicht und die Würfel-Klasse verwenden wollen, binden wir beides ein und deklarieren den Namespace von Irrlicht :

//Einbinden der Header-Datei von Irrlicht
#include <irrlicht.h>
//Einbinden der Header-Datei unserer Würfel-Klasse
#include "CWuerfel.h"

//Einbinden der Namespaces
using namespace irr;
using namespace core;
using namespace video;

Erstellen des Device und einholen des Video-Treibers[Bearbeiten]

Dies geht in gewohnter Weise mit :

//Unser Irrlicht-Device erstellen und initialisieren
IrrlichtDevice *device =
	createDevice( video::EDT_OPENGL, dimension2d<u32>(640, 480), 32,
		      false, false, false, 0);

//Konnte das Device erstellt werden ?
if (!device)
	return 1; //Falls nicht, Fehlercode zurückgeben und Programm abbrechen

//Den Text des Hauptfensters festlegen
device->setWindowCaption(L"Einen Würfel-Klasse in Irrlicht !");

//Den Videotreiber erstellen und Zeiger aus dem Device abholen
IVideoDriver* driver = device->getVideoDriver();

Einbinden des Szene-Managers und des GUI[Bearbeiten]

Nun erstellen wir einen Szene-Manager, welcher u.a. unsere Nodes verwaltet und diese zeichnet.

//Einen Szene-Manager erstellen und Zeiger aus dem Device abholen
scene::ISceneManager* smgr = device->getSceneManager();

Da wir einen Debug-String darstellen wollen, benötigen wir einen Zugriff auf die GUI-Funktionen (GUI = Graphical user interface) von Irrlicht. Danach erstellen wir ein Text-Klasse zum Anzeigen des Debug-Strings und registrieren diese im GUI-Manager :

//Einen GUI_Manager erstellen und Zeiger aus dem Device abholen
gui::IGUIEnvironment* guienv = device->getGUIEnvironment();

//Ein Text-Element definieren und an den GUI-Manager übergeben
gui::IGUIStaticText* GUI_debug_text = guienv->addStaticText
	(L"",rect<s32>(5, 5, 200, 200),false,true,0,-1,false);

Die Parameter der Text-Klasse haben folgende Bedeutung :

  • zuerst wird der anzuzeigende Text angegeben. Das "L" vor dem Text sagt aus, dass es sich um einen Wide-String handelt,
  • dann wird das Rechteck definiert, in dem der Text angezeigt werden soll,
  • der zweite Parameter gibt an, ob der Text einen Rahmen haben soll,
  • der dritte legt fest, ob der automatische Zeilenumbruch aktiviert werden soll,
  • der vierte legt fest, ob ein Parent zugewiesen werden soll,
  • der fünfte bestimmt die ID des Textes ("-1" = keine benötigt) und
  • der sechste und letzte Parameter bestimmt, ob der Hintergrund des Textes farbig gefüllt werden soll.

Einbinden einer Kamera[Bearbeiten]

Da wir nun einen Szene-Manager haben, binden wir auch eine Kamera ein. Dieses erfolgt durch :

//Eine Kamera erstellen
smgr->addCameraSceneNode(0, core::vector3df(0,-2,0), core::vector3df(0,0,0));

Die Parameter definieren,

  • ob die Kamera eine Parent-Node hat (ist sehr gut zur Objektverfolgung),
  • der Positionsvektor der Kamera und
  • der Positionsvektor, auf den die Kamera blicken soll.

Definieren des Würfels[Bearbeiten]

Nun definieren wir zuerst einen Würfel vom Typ unserer CWuerfel-Klasse. Danach stellen wir die Delta Time der Zeitsteuerung ein und übergeben die aktuelle Zeit als Stopzeit, damit der erste Moment der Drehung definiert ist :

//Einen Würfel der Klasse CWuerfel erstellen
CWuerfel *Wuerfel = new CWuerfel(smgr->getRootSceneNode(), smgr, 1);	
	
//Nach welcher Zeit(ms) soll die Szene gedreht werden ?	
Wuerfel->SetDeltaTime(50); //50 Millisekunden
		
//Letzte Zeitmessung initalisieren
Wuerfel->SetTimePos(device->getTimer()->getTime());

Anzeigen des Debug-Strings[Bearbeiten]

Im Programmdurchlauf können wir nun den Debug-String in eine lokale Variable speichern und über das GUI-Interface in Irrlicht anzeigen lassen :

//Den Debug-String erstellen und abholen
stringw sDebStr = Wuerfel->GetDebugString();
		
//Den Debug-String in der Irrlicht-GUI anzeigen
GUI_debug_text->setText(sDebStr.c_str());

Übergeben der Zeit und Drehen des Würfels[Bearbeiten]

Da die Zeitsteuerung die aktuelle Systemzeit zur Zeitmessung benötigt, müssen wir diese der Würfel-Klasse übergeben. Ist diese dann dort gespeichert, rufen wir die Prozedur auf, welche entscheidet, ob der Würfel gedreht werden muss :

//Aktuelle Zeit mitteilen
Wuerfel->TellCurrentTime(device->getTimer()->getTime());
		
//Nach Ablauf der Zeitspanne den Würfel drehen
Wuerfel->TurnIfDesired();

Der Quellcode zum Beispiel[Bearbeiten]

Die Würfel-Klasse in Aktion

Sie finden den Quellcode

  • zur Header-Datei hier
  • zur Quellcode-Datei der Würfel-Klasse hier und
  • zur Beispielanwendung der Würfel-Klasse hier