Irrlicht - from Noob to Pro: Ein Dreieck in Irrlicht darstellen


Voraussetzungen Bearbeiten

In diesem Abschnitt des Buches sollten Sie wissen,

  • wie das Koordinatensystem von Irrlicht aufgebaut ist,
  • was Culling ist und was es bewirkt,
  • was ein Vertex (zu Deutsch Positionsvektor, engl. Mz. Vertices) ist,
  • was ein Index (engl. Mz. Indicies) ist und
  • wie man mit Hilfe von Vertices und Indicies ein Objekt definiert.

Falls Sie es nachlesen wollen, so finden Sie es im vorhergenden Abschnitt dieses Buches.

Verwendete Prozeduren Bearbeiten

Im vorhergehenden Abschnitt haben wir die Funktion

draw3DLine (const core::vector3df &start, const core::vector3df &end, SColor color=SColor(255, 255, 255, 255))

verwendet, um die Achsen des Koordinatensystems darzustellen. Um ein Dreieck darzustellen, benötigen wir die Funktion

draw3DTriangle(const core::triangle3df& triangle, SColor color = SColor(255,255,255,255))

welche einen Zeiger auf den Datentyp

core::triangle3df

als ersten Parameter übergeben haben will, welcher (neben mehreren vorhandenen Funktionen) 3 Vertices vom Typ

core::vector3df

als Parameter speichert.

Der zweite Parameter der Prozedur bestimmt die Farbe des Dreiecks im ARGB-Format.

Das Dreieck zeichnen Bearbeiten

Wir wollen hier das Dreieck aus dem vorhergehenden Abschnitt des Buches zeichnen. Zu Darstellung hier nochmal das Bild :
 

Definieren der Vertices Bearbeiten

Bevor wir die Eckpunkte definieren, legen wir eine Konstante fest, welche die Position auf der Z-Achse ("Tiefenachse") festlegt :

//Position des Dreiecks auf der Z-Achse (="Tiefenachse")
const irr::f32 z = 1.0f;

Danach können wir uns daran machen, die Eckpunkte zu definieren, welche vom Typ

core::vector3df

sind :

//Definieren der Vertices
core::vector3df cvPunktA(-0.5f, -0.3f, z); //Punkt A
core::vector3df cvPunktB(0.5f, -0.3f, z);  //Punkt B
core::vector3df cvPunktC(0.0f, 0.5f, z);   //Punkt C

Zeichnen des Dreiecks mit draw3DTriangle Bearbeiten

Nun können wir uns daran machen, das Dreieck mit Hilfe der Funktion draw3DTriangle darzustellen. Die beiden Parameter übergeben wir per Typecasting :

//Zeichnen des Dreiecks
driver->draw3DTriangle(core::triangle3df(                 //Typ ist triangle3df
					 cvPunktC,	  //erster Vektor
					 cvPunktB,	  //zweiter Vektor
					 cvPunktA         //dritter Vektor
					 ),video::SColor(255,255,125,0)); //Farbwert als SColor

Als Ergebnis sollten Sie nun ein oranges Dreieck dargestellt sehen :
 
Den Quellcode zum Zeichnen eines Dreiecks mit draw3DTriangle finden Sie hier

Zeichnen des Dreiecks mit drawIndexedTriangleList Bearbeiten

Kommen wir nun zu einer Funktion, welche dazu gemacht wurde, Dreiecke über eine Liste von Vertices und Indicies zu zeichnen :

drawIndexedTriangleList(
	const S3DVertex* vertices,	//Das Array der Vertices
	u32 vertexCount,		//Anzahl der im Array definierten Vertices
	const u16* indexList,		//Array mit Indicies
	u32 triangleCount		//Anzahl der zu zeichnenden Dreiecke
	)

Diese Funktion verwendet zur Übergabe der vorderfinierten Daten den Datentyp S3DVertex, welcher uns weiterhin erlaubt, für jeden Eckpunkt eine andere Farbe zu definieren, was uns die Möglichkeit verschafft, ein Dreieck mit bunten Ecken zu zeichnen.

Tipp:

 

Wenn Sie Objekte darstellen, dann ist es sehr empfehlenswert, anstatt der Funktion draw3DTriangle die Funktion drawIndexedTriangleList zu verwenden, da diese um einiges schneller zeichnet. Das liegt an der Verwendung eines Indicies-Arrays, was den Aufbau komplexer Objekte aus Dreiecken beschleunigt. draw3DTriangle hingegen zeichnet lauter einzelne Dreiecke.


Definition von Vertices und Farbwerten Bearbeiten

Zuerst definieren wir die Konstante für die Z-Achse und erstellen wir ein Array vom Typ

S3DVertex

mit einer Größe von 3 Einträgen:

//Position des Dreiecks auf der Z-Achse (="Tiefenachse")
const irr::f32 z = 1.0f;

video::S3DVertex vaVertices[3]; //Das Array vom Typ S3DVertex mit 3 Einträgen

Nun können wir für jeden Vertex die Position sowie den Farbwert definieren :

//Punkt A
vaVertices[0].Pos.X = -0.5f; //X
vaVertices[0].Pos.Y = -0.3f; //Y
vaVertices[0].Pos.Z = z;     //Z	
vaVertices[0].Color = video::SColor(255,255,0,0); //Rot
//Punkt B
vaVertices[1].Pos.X = 0.5f;  //X
vaVertices[1].Pos.Y = -0.3f; //Y
vaVertices[1].Pos.Z = z;     //Z
vaVertices[1].Color = video::SColor(255,0,255,0); //Grün
//Punkt C
vaVertices[2].Pos.X = 0.0f;  //X
vaVertices[2].Pos.Y = 0.5f;  //Y
vaVertices[2].Pos.Z = z;     //Z
vaVertices[2].Color = video::SColor(255,0,0,255); //Blau

Des weiteren benötigen wir ein Array für die Angabe der Indicies. Da wir wegen dem Culling in Uhrzeigersinn zeichnen müssen, legen wir die zu zeichnende Reihenfolge auf Punkt c, Punkt B und Punkt A fest, was folgendem Array entspricht :

//Definieren der Indicies
u16 uiIndicies[3] = {2,  //Punkt C
		     1,  //Punkt B
		     0}; //Punkt A

Übergeben der Daten an drawIndexedTriangleList Bearbeiten

Da wir nun zum Zeichnen des Dreiecks bereit sind, übergeben wir die Daten durch folgenden Aufruf:

//Zeichnen des Dreiecks
driver->drawIndexedTriangleList(
			&vaVertices[0],		//Zeiger auf das erste Element im Vertices-Array
			3,			//Anzahl der Elemente im Vertices-Array
			&uiIndicies[0],		//Zeiger auf das erste Element in Indicies-Array
			1);			//Anzahl der zu zeichnenden Dreiecke

Den Quellcode zum Zeichnen eines Dreiecks mit draw3DTriangle finden Sie hier.
Wenn Sie nun das kompilierte und gelinkte Programm ausführen, sollten Sie folgende Ansicht erhalten :
 

Einen rotierenden Würfel aus Dreiecken erstellen Bearbeiten

Da wir nun gelernt haben, wie man ein Dreieck mit verschiedenen Funktionen darstellt, wollen wir einen Würfel,welcher aus mehreren Dreiecken zusammengesetzt ist, darstellen. Der Würfel hat 6 Seiten, welche jede aus 2 Dreiecken besteht, was im ganzen also 12 Dreiecke sind. Die Dreiecke sind zum besseren Verständnis durchnummeriert. Hierzu die entsprechenden Darstellungen mit den jeweiligen Dreiecken :

   
 

Definieren der Vertices Bearbeiten

Da der Würfel 0.6 Einheiten Seitenlänge haben soll, wobei der Nullpunkt im Mittelpunkt des Würfels ist, definieren wir 2 Konstanten für die Z-Achse der Vorderseite und Hinterseite des Würfels:

//Z-Positionen 
const irr::f32 z_vorne  = -0.3f; //Vorderseite
const irr::f32 z_hinten =  0.3f; //Rückseite

Weil der Würfel 8 Eckpunkte hat, brauchen wir ein Array mit 8 Einträgen vom Typ S3DVertex:

video::S3DVertex vaVertices[8]; //Das Array vom Typ S3DVertex

Nun definieren wir wieder die 8 Eckpunkte des Würfels als Vertices und vergeben einen Farbwert für jeden Vertex. Zur Erleichterung bedienen wir uns der vordefinierten Konstanten für die Z-Achse.

//Vorne
vaVertices[0] = S3DVertex(-0.3f,-0.3f, z_vorne,0,0,0,SColor(255,255,0,0),0,0);//Punkt A
vaVertices[1] = S3DVertex(-0.3f, 0.3f, z_vorne,0,0,0,SColor(255,0,255,0),0,0);//Punkt B
vaVertices[2] = S3DVertex( 0.3f,-0.3f, z_vorne,0,0,0,SColor(255,0,0,255),0,0);//Punkt C
vaVertices[3] = S3DVertex( 0.3f, 0.3f, z_vorne,0,0,0,SColor(255,0,0,255),0,0);//Punkt D
//Hinten
vaVertices[4] = S3DVertex(-0.3f,-0.3f, z_hinten,0,0,0,SColor(255,255,255,0),0,0);//Punkt E
vaVertices[5] = S3DVertex(-0.3f, 0.3f, z_hinten,0,0,0,SColor(255,0,255,255),0,0);//Punkt F
vaVertices[6] = S3DVertex( 0.3f,-0.3f, z_hinten,0,0,0,SColor(255,255,0,255),0,0);//Punkt G
vaVertices[7] = S3DVertex( 0.3f, 0.3f, z_hinten,0,0,0,SColor(255,0,0,255),0,0);//Punkt H

Definieren der Indicies Bearbeiten

Unser Würfel hat 6 Seiten, welche aus 12 Dreiecken besteht, von denen jedes 3 Vertices hat, was im ganzen 36 Indicies beträgt. Also benötigen wir ein Array mit einer Länge von 36 Einträgen, welche auch gleich definiert werden :

//Definieren der Indicies
u16 uiIndicies[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)

Zeichnen des Würfels Bearbeiten

Das Zeichnen des Würfels geht in gewohnter Weise mit :

//Zeichnen des Würfels
driver->drawIndexedTriangleList(
			&vaVertices[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

Einfügen einer Drehung Bearbeiten

Wenn wir nun das Programm ausführen würden, würden wir nur ein Viereck zu sehen bekommen, da die Kamera in Richtung Z-Achse blickt. Deshalb wollen wir die ganze Szene von Irrlicht drehen, was folglich den Würfel auch mit drehen würde. Ich greife deshalb vor und mache einen kleinen Ausflug in die Welt der Matrizen und wie diese verwendet werden, um eine Szene zu drehen. Wir benötigen zuerst eine Matrix vom Typ matrix4, welche ein Array von 4x4 Feldern zur Speicherung des Zustands einer Matrix beeinhaltet :

core::matrix4 Matrix; //4x4 Matrix

Diese wird danach initialisiert mit

Matrix.makeIdentity(); //Initialisieren

Nun benötigen wir noch die Variable, welche den aktuellen Winkel der Drehung speichert :

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

Diesen Winkel erhöhen wir bei jedem Durchlauf um 1.5 Grad, bevor er in die Matrix einfliesst:

//Den Winkel erhöhen
fWinkel += 1.5f;
//Nicht über 360 Grad drehen
if (fWinkel > 360) fWinkel = 1;

Die Drehung wird über eine in den Datentyp matrix4 eingebaute Funktion realisiert. Man kann mit dieser die Drehung einer Achse um einen bestimmten Winkel vollziehen :

//Um die Y- u. Z-Achse drehen
Matrix.setRotationDegrees(core::vector3df(0, fWinkel, fWinkel));

Nun kann man die Weltmatrix von Irrlicht durch die um den Rotationswinkel veränderte Matrix ersetzen, um die Drehung der Szene zu vollziehen :

//Übertragen der Matrix in die Weltmatrix
driver->setTransform(video::ETS_WORLD, Matrix);

Wenn wir nun das Programm ausführen, dann ist von dem Würfel nicht viel zu erkennen, da er sich mit zu hoher Geschwindigkeit dreht. Daher benötigen wir eine zeitabhängige Steuerung des Würfels.

Eine zeitabhängige Steuerung einbauen Bearbeiten

Wir benötigen hier 2 Variablen und 1 Konstante.

  • Die erste Variable speichert die aktuelle Zeit.
  • Die zweite Variable speichert, zu welchem Zeitpunkt zuletzt der Winkel erhöht worden ist.
  • Die Konstante legt fest, nach welcher Zeit der Winkel erhöht werden soll.

Das ganze sieht dann ungefähr so aus :

//Unsere Variablen für die Zeitmessung
u32 uAktuelleZeit = 0;     //Zum Speichern der aktuellen Zeit
u32 uZuletztGestoppt = 0;  //Die letzte gestoppte Zeit
const u32 uSchrittAlleMs = 50; //Nach welcher Zeit(ms) soll die Szene gedreht werden ?

Nun müssen wir bei jedem Durchlauf der Szene die aktuelle Zeit abfragen und in die Variable uAktuelleZeit speichern:

//Aktuelle Zeit abholen
uAktuelleZeit = device->getTimer()->getTime();

Wenn nun die durch die Konstante uSchrittAlleMs definierte Zeit vergangen ist, soll der Winkel um 1.5 Grad erhöht werden :

//Gewünschte Zeit vergangen ?
if (uAktuelleZeit >= (uZuletztGestoppt + uSchrittAlleMs))
{
	uZuletztGestoppt = uAktuelleZeit; //Aktuelle Zeit stoppen

	//Den Winkel erhöhen
	fWinkel += 1.5f;
	//Nicht über 360 Grad drehen
	if (fWinkel > 360) fWinkel = 1;
}

Somit kann verhindert werden, dass sich der Würfel viel zu schnell dreht und man die Rotationsgeschwindigkeit des Würfels einstellen kann. Den Quellcode zum Beispiel finden Sie hier. Sie sollten nun einen rotierenden Würfel sehen können :