Irrlicht - from Noob to Pro: Ein Dreieck in Irrlicht darstellen
Voraussetzungen
BearbeitenIn 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
BearbeitenIm 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
BearbeitenWir wollen hier das Dreieck aus dem vorhergehenden Abschnitt des Buches zeichnen. Zu Darstellung hier nochmal das Bild :
Definieren der Vertices
BearbeitenBevor 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
BearbeitenNun 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
BearbeitenKommen 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
BearbeitenZuerst 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
BearbeitenDa 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
BearbeitenDa 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
BearbeitenDa 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
BearbeitenUnser 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
BearbeitenDas 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
BearbeitenWenn 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
BearbeitenWir 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 :