Irrlicht - from Noob to Pro: Sonne, Erde und Mond
Vorwort
BearbeitenIn diesem Abschnitt will ich Ihnen anhand der Erstellung eines kleinen Sonnensystems vermitteln, wie man ein kleines Sonnensystem erstellt und dieses animert. Sie sollten, bevor Sie weiter lesen, folgendes wissen :
- Was ist Translation, Rotation und Skalierung ?
- Wie verwende ich Texturen ?
- Was ist Sinus und Kosinus ?
- Was ist der Einheitskreis ?
Wir werden das Sonnensystem über zwei Arten animieren :
- mit Hilfe von Irrlichts Animatoren für Rotationen createRotationAnimator() sowie createFlyCircleAnimator() für die Umlaufbahnen
- mit Hilfe eines zeitgesteuerten Winkelzählers sowie der Errechnung der Umlaufbahnen mittels der mathematischen Funktionen cosf() für Cosinus und sinf() für Sinus. Die jeweiligen Werte werden in einen Positionsvektor vom Typ core::vector3df() gespeichert und die jeweiligen Planeten dorthin mit setPosition()verschoben.
Die Rotation der Planeten erfolgt durch die Multiplikation der Rotationsmatrix einer SceneNode mit einer temporären Rotationsmatrix, welche den neuen Rotationswert beinhaltet.
Erstellen der Szene
BearbeitenIrrlicht initialisieren
BearbeitenErstellen Sie wieder wie gewohnt Ihr Projekt, erstellen Sie das Irrlicht-Device, holen Sie den Videotreiber ein, legen Sie den Fenstertitel fest und erstellen Sie den Szenemanager :
//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"Rotation von Objekten");
//Den Videotreiber erstellen und Zeiger aus dem Device abholen
IVideoDriver* driver = device->getVideoDriver();
//Einen Szene-Manager erstellen und Zeiger aus dem Device abholen
scene::ISceneManager* smgr = device->getSceneManager();
|
Erstellen der Sphären für die Planeten
BearbeitenAls nächstes benötigen wir 3 Sphären, welche wir mit Irrlichts Funktion addSphereSceneNode(f32 radius) ganz leicht erstellen können. Danach laden wir für jedes Mesh die entsprechende Textur und legen fest, dass keine Lichtberechnung stattfinden soll :
//Erstellen der SceneNodes
scene::ISceneNode * Sonne = smgr->addSphereSceneNode(1.0f); //Sonne
scene::ISceneNode * Erde = smgr->addSphereSceneNode(0.5f); //Erde
scene::ISceneNode * Mond = smgr->addSphereSceneNode(0.3f); //Mond
//Einstellen des Materials
Sonne->setMaterialTexture(0, driver->getTexture("Sun.jpg"));
Erde->setMaterialTexture(0, driver->getTexture("Earth.jpg"));
Mond->setMaterialTexture(0, driver->getTexture("Moon.jpg"));
//Keine Lichtberechnung
Sonne->setMaterialFlag(EMF_LIGHTING, false);
Erde->setMaterialFlag(EMF_LIGHTING, false);
Mond->setMaterialFlag(EMF_LIGHTING, false);
|
Möglichkeit 1 : Animatoren
BearbeitenHier will ich auf die einfachere Möglichkeit eingehen, um unsere Planeten zu rotieren und zu positionieren : Irrlichts Animatoren.
Der Rotations-Animator
BearbeitenDer erste und für die Rotation verantwortliche Animator ist der createRotationAnimator, welcher wie folgt deklariert ist :
virtual ISceneNodeAnimator* createRotationAnimator(const core::vector3df& rotationSpeed) = 0;
|
Dieser erwartet bei der Deklaration einen Vektor als Parameter, welcher den Betrag der Rotation jeder Achse definiert. Das Mesh wird dann alle 10 Millisekunden um den jeweiligen Betrag um jede Achse gedreht. Je höher der Betrag, desto höher ist auch die Rotationsgeschwindigkeit. Wenn der Animator nicht mehr benötigt wird, so sollte man ihn mit der Prozedur drop() freigeben.
Die Rotationen für Sonne, Erde und Mond sehen wie folgt aus :
//Rotation der Sonne
scene::ISceneNodeAnimator* animRotSun = smgr->createRotationAnimator(core::vector3df(0,0.2f,0));
Sonne->addAnimator(animRotSun);
animRotSun->drop();
//Rotation der Erde
scene::ISceneNodeAnimator* animRotEarth = smgr->createRotationAnimator(core::vector3df(0,0.5f,0));
Erde->addAnimator(animRotEarth);
animRotEarth->drop();
//Rotation des Mondes
scene::ISceneNodeAnimator* animRotMoon = smgr->createRotationAnimator(core::vector3df(0,0.7f,0));
Mond->addAnimator(animRotMoon);
animRotMoon->drop();
|
Der Fly-Circle Animator
BearbeitenDer Fly-Circle Animator kann ein Objekt entlang einer runden oder elliptischen Umlaufbahn um einen Positionsvektor bewegen. Der Fly-Circle Animator ist wie folgt deklariert :
virtual ISceneNodeAnimator* createFlyCircleAnimator(
const core::vector3df& center=core::vector3df(0.f,0.f,0.f),
f32 radius=100.f, f32 speed=0.001f,
const core::vector3df& direction=core::vector3df(0.f, 1.f, 0.f),
f32 startPosition = 0.f,
f32 radiusEllipsoid = 0.f) = 0;
|
Die Geschwindigkeit des Objekts wird in Radiant / ms angegeben. Da die Sonne bei der Erstellung auf dem Nullpunkt des Welt-Koordinatensystems liegt, können wir als Zentrum der Umlaufbahn diesen auch angeben. Der Radius der Umlaufbahn soll 3 Einheiten betragen und das Tempo 0.0005 Radiant pro Millisekunde.
Damit ergibt sich folgender Code für den Fly-Circle Animator unserer Erde :
//Orbit der Erde
scene::ISceneNodeAnimator* animOrbitEarth = smgr->createFlyCircleAnimator(
core::vector3df(0,0,0), 3.0f, 0.0005f);
Erde->addAnimator(animOrbitEarth);
animOrbitEarth->drop();
|
Wir haben nun unsere Erde, welche entlang einer Umlaufbahn um unsere Sonne rotiert. Doch wie sieht es mit dem Mond aus ?
Nun, da sich die Erde ständig bewegt, können wir als Zentrum unserer Umlaufbahn keinen Vektor verwenden, welcher Welt-Koordinaten verwendet. Stattdessen benötigen wir die lokalen Koordinaten der Erde, um das Zentrum der Umlaufbahn des Mondes zu bestimmen. Genauer benötigen wir den Nullpunkt im lokalen Koordinatensystem der Erde, welches wir zur Laufzeit am besten erhalten, wenn wir die Erde als “Parent” (also als übergeordnetes Objekt) unseres Mondes angeben. Dies geschieht durch :
//Wir brauchen die Erde als Parent für den Mond, da dieser dann
//den Nullpunkt der Erde als Zentrum für seinen Orbit hat
//createFlyCircleAnimator hat als Zentrum den Nullpunkt der Erde (0,0,0)
Mond->setParent(Erde);
|
Nun können wir den Mond in einem Radius von 1.2 Einheiten mit dem selben Tempo wie die Erde um die Erde kreisen lassen :
/Orbit des Mondes
scene::ISceneNodeAnimator* animOrbitMoon = smgr->createFlyCircleAnimator(
core::vector3df(0,0,0), //lokale Koordinaten !!
1.2f, 0.0005f);
Mond->addAnimator(animOrbitMoon);
animOrbitMoon->drop();
|
Möglichkeit 2 : Sinus und Cosinus
BearbeitenWenn wir auf die Hilfe von Irrlicht in Sachen Animation verzichten wollen, weil wir z.B. einen speziellen Fall programmieren wollen (Bewegung nur auf einem Kreisausschnitt usw.), so verwenden wir die Winkelfunktionen Sinus und Kosinus, mit deren Hilfe wir die Planeten entlang eines Orbits bewegen, welcher einen Einheitskreis darstellt.
Zum Thema Trigonometrie sollte man folgendes verinnerlichen :
- Die Summen aller Winkel eines Dreiecks ergeben immer 180 °
- Der Cosinus ist das Gegenstück zum Sinus. Ist Sinus(α) = 0 so ist Cosinus(α) = 1. Nimmt der Sinus-Wert zu, so nimmt der Cosinus-Wert ab und umgekehrt.
- Sinus definiert eine Schwingung, welche bei 0° mit 0 beginnt, das Maximum von 1.0 bei 90° und das Minimum von -1.0 bei 270° erreicht.
- Cosinus definiert eine Schwingung, welche bei 0° das Maximum von 1.0 und das Minimum von -1.0 bei 180° erreicht.
- Das Bogenmaß erreicht bei α = 180° 1 x π und bei α = 360° = 2 x π. Die englische Einheit für das Bogenmaß heisst Radiant und das Einheitenzeichen ist Rad.
- Verwendet man Sinus und Cosinus mit dem Bogenmaß, so ist der Radius des Einheitskreises 1. Wenn wir also einen Orbit errechnen wollen, dann gelten die Faustformeln :
- Wollen Sie die Bewegungsrichtung auf dem Orbit umdrehen, so erreichen Sie eine Bewegung gegen den Uhrzeigersinn, indem Sie Cosinus und Sinus vertauschen :
Wie Sie sehen, bestimmt der Punkt C die Position des Planeten im Orbit und Punkt A den Nullpunkt des Orbits.
- Sinus schwingt entlang der Z-Achse von +1 bis Minus 1.
- Cosinus schwingt entlang der Y-Achse von +1 bis -1.
Diese Schwingungen verwenden wir nun als Faktor, welche wir nun nur noch mit dem gewünschten Radius unserer Umlaufbahn multiplizieren müssen, um unsere Koordinaten zu erhalten. Ich will an diesem Einheitskreis ein einfaches Beispiel zum besseren Verständnis ausführen. Bitte haben Sie Verständnis, dass die komplette Erläuterung den Rahmen dieses Buches sprengen würde.
Geg.: α = 40°, gewünschter Radius rg = 3
Ges.: Position Punkt C mit Koordinaten X und Z
Da α gleich 40° ist und die Summe aller Winkel im Dreieck 180° ergibt, wissen wir, dass Winkel δ gleich 180° - 90 ° (= rechter Winkel) - 40° (= α) = 50° beträgt.
Da die Strecke BC unbekannt ist, verwenden wir die Sinus-Tabelle, welche uns für 40° einen Sinus-Wert von 0.6428 angibt. Also rechnen wir
Koordinate Z von Punkt C ist somit
Da Dreieck 1 und 2 ein Quadrat / Rechteck bilden, gilt
wodurch
Also ist
Koordinate X von Punkt C ist damit
Punkt C liegt somit auf den Koordinaten Z = 1.92 und X = 2.31
Man beachte hier nun, dass Cosinus(α = 40°) = 0.766, womit wir also auch über den Cosinus-Wert des Winkel α die Strecke DC errechnen könnten. Somit gilt also
oder
oder
Da diese Berechnungsmethode sehr rechenintensiv ist, wird sie in der Programmierung selten verwendet. Stattdessen geht man den Weg über das Bogenmaß, wobei bei der Berechnung der Koordinaten die mathematische Konstante PI (π) eine entscheidende Rolle spielt. Was ist denn eigentlich PI ?
- Die Fläche eines Kreises mit dem Durchmesser 1, was für uns weniger von Bedeutung ist.
- Ein Kreis mit einem Durchmesser (nicht Radius) von 1 hat einen Umfang von 1 x π (3,14159265359)
Das Bogenmaß, welches die Einheit Radiant verwendet, erreicht beim Vollwinkel (welcher 360° ist) den Wert von 2 x π.
Wenn wir also einen Kreis mit einem Durchmesser von 2 (und somit einem Radius von 1) definieren, so haben wir ebenfalls als Umfang 2 x π.
Damit erreichen wir 3 Vorteile :
- Der Kreisumfang kann als 2 x π definiert werden, was kompatibel zu Radiant ist
- Da der Radius 1 beträgt, müssen wir nur noch das Ergebnis von Sinus bzw. Cosinus mit dem gewünschten Radius des Orbits multiplizieren.
- Die Funktionen sinf() und cosf() erwarten Radiant als Angabe des Winkels ( und nicht etwa das oft verwendete Grad)
Falls Sie die Angabe für sinf() und cosf() trotzdem in Grad verwenden wollen, so müssen Sie dieses in Radiant umrechnen. Irrlicht stellt Ihnen in der Header-Datei irrMath.h eine Konstante dazu bereit :
//! 32bit Constant for converting from degrees to radians
const f32 DEGTORAD = PI / 180.0f;
Somit könnte man z.B. innerhalb der Funktionen umrechnen :
- sinf(Grad * DEGTORAD)
- cosf(Grad * DEGTORAD)
Nun haben wir die Möglichkeit, einen Orbit zu zeichnen. Dazu verwenden wir die am Anfang des Buches verwendete Funktion driver->draw3DLine. Da diese Linie je einen Vektor für Anfang und Ende der Linie als die ersten 2 Argumente verwendet, müssen wir den Anfangsvektor jeweils speichern.
Die Kreisberechnung
BearbeitenWenn wir nun den Kreis berechnen wollen, müssen wir zuerst den Anfangsvektor initialisieren, welcher die Anfangsposition unserer Erde im Sonnensystem darstellt. Hierzu können wir einfach den Radius des Orbits der Erde verwenden und diese als Position auf der X-Achse angeben, was in der Draufsicht des Sonnensystems die Position rechts mitte wäre :
core::vector3df tempVec( fRadiusErde, 0 , 0 ); //Anfangsstellung im Orbit
Nun können wir uns eine Schleife programmieren, welche einen Wert von 0 bis 2 mal π durchläuft, um für jeden Winkel die Positionen errechnen zu können. Wir verwenden als Schritt pro Schleife einen Wert von 0.1 Radiant, was den Kreis fein zeichnet.
for( f32 radiant = 0; radiant <= 2 * PI; radiant += 0.1f ) //Bis 2 x PI durchlaufen
Danach bedienen wir uns der Cosinus und Sinus-Schwingung und multiplizieren das Ergebnis mit dem gewünschten Radius, um die Koordinaten auf dem kreisförmigen Orbit zu erhalten :
f32 px = fRadiusErde * cos( radiant ); //X-Position berechnen
f32 pz = fRadiusErde * sin( radiant ); //Z-Position berechnen
Nun können wir den Videotreiber die Linie von den letzten Koordinaten aus zu den neu errechneten zeichnen lassen :
//Linie von letzte bis nächste Position
driver->draw3DLine( tempVec, vector3df( px, 0, pz ), SColor(93, 137, 54, 143));
Um den Anfangsvektor des nächsten Kreissegments zu haben, müssen wir die aktuell errechnete Position speichern :
//Speichern der zuletzt errechneten Position
tempVec = vector3df( px, 0, pz );
Wenn wir 2 x PI erreicht haben, ist der Kreis allerdings noch nicht geschlossen, da das letzte Kreissegment fehlt. Hierzu müssen wir von der letzten Berechnung zum Beginn des ersten Kreissegments zeichnen :
//Kreis schliessen
driver->draw3DLine( tempVec, vector3df( fRadiusErde, 0, 0 ), SColor(93, 137, 54, 143));
Die komplette Funktion sieht damit so aus :
//**********************************
//Zeichnen der Umlaufbahn der Erde *
//**********************************
core::vector3df tempVec( fRadiusErde, 0 , 0 ); //Anfangsstellung im Orbit
for( f32 radiant = 0; radiant <= 2 * PI; radiant += 0.1f ) //Bis 2 x PI durchlaufen
{
f32 px = fRadiusErde * cos( radiant ); //X-Position berechnen
f32 pz = fRadiusErde * sin( radiant ); //Z-Position berechnen
//Linie von letzte bis nächste Position
driver->draw3DLine( tempVec, vector3df( px, 0, pz ), SColor(93, 137, 54, 143));
//Speichern der zuletzt errechneten Position
tempVec = vector3df( px, 0, pz );
}
//Kreis schliessen
driver->draw3DLine( tempVec, vector3df( fRadiusErde, 0, 0 ), SColor(93, 137, 54, 143));
|
Kreisberechnung um bewegte Objekte
BearbeitenWenn wir den Orbit des Monds als Kreis darstellen wollen, so haben wir das Problem, dass sein Orbit einen Nullpunkt hat, welcher sich mit einem bewegten Objekt (der Erde) verschiebt. Ich kann Sie allerdings beruhigen, denn man muss nur den Positionsvektor der Erde zum Anfangsvektor des Monds addieren, um die Startposition des Mondes zu errechnen :
//Radius des Mondes als Anfangsposition auf der X-Achse
tempVec = core::vector3df(fRadiusMond, 0 , 0 );
//Hinzuaddieren des Positionsvektors der Erde verschiebt den
//Mond in seine korrekte Position im Orbit um die Erde
tempVec += EarthPos;
Wenn wir nun den Orbit des Mondes mit der Erde "mitwandern" lassen wollen, so müssen wir nur zu unseren errechneten Koordinaten die Position der Erde hinzuaddieren. Hierzu die komplette Funktion :
//************************************
//Zeichnen der Umlaufbahn des Mondes *
//************************************
//Radius des Mondes als Anfangsposition auf der X-Achse
tempVec = core::vector3df(fRadiusMond, 0 , 0 );
//Hinzuaddieren des Positionsvektors der Erde verschiebt den
//Mond in seine korrekte Position im Orbit um die Erde
tempVec += EarthPos;
for( f32 radiant = 0; radiant <= 2 * PI; radiant += 0.1f )
{
f32 px = EarthPos.X + (fRadiusMond * cos( radiant ));
f32 pz = EarthPos.Z + (fRadiusMond * sin( radiant ));
driver->draw3DLine( tempVec, vector3df( px, 0, pz ), SColor(125, 255, 128, 0));
tempVec = vector3df( px, 0, pz );
}
//Kreis schliessen
driver->draw3DLine( tempVec,
(vector3df( fRadiusMond, 0, 0 ) + EarthPos), SColor(125, 255, 128, 0));
|
Berechnung der Positionen
BearbeitenUm die jeweiligen Positionen von Erde und Mond (die Sonne rotiert nur um den Nullpunkt des Welt-Koordinatensystems) speichern zu können, benötigen wir zuerst 2 Vektoren als globale Variablen :
core::vector3df EarthPos; //Positionsvektor unserer Erde
core::vector3df MoonPos; //Positionsvektor unseres Monds
Des weiteren legen wir uns noch Variablen für die Rotation der Objekte sowie für die gewünschten Radien der Orbits :
//Unser 32Bit-Float zum Speichern des Rotationswinkels
f32 fWinkel = 0.0;
f32 fWinkel_Erde = 0.0;
//Unsere Radien für Erde und Mond
f32 fRadiusErde = 3.0f; //= Abstand Erde zur Sonne
f32 fRadiusMond = 1.2f; //= Abstand Mond zur Erde
|
Die Rotationswinkel der Objekte lassen wir zeitgesteuert periodisch erhöhen :
//Nach Ablauf von Delta Time
if (device->getTimer()->getTime() > (uiZuletztGestoppt + uiDeltaTime))
{
//Neuen Zeitpunkt merken
uiZuletztGestoppt = device->getTimer()->getTime();
//Winkel erhöhen
fWinkel += 0.005f;
fWinkel_Erde -= 0.005f;
}
|
Nun können wir die jeweiligen Positionen der Himmelskörper durch verwenden von Sinus und Cosinus multipliziert mit dem gewünschen Radius errechnen :
//Errechnen der Position der Erde
EarthPos = core::vector3df(
(fRadiusErde * sinf(fWinkel))
, 0,
(fRadiusErde * cosf(fWinkel))
);
//Errechnen der Position des Monds
MoonPos = core::vector3df(
EarthPos.X + (fRadiusMond * sinf(fWinkel_Erde)),
0,
EarthPos.Z + (fRadiusMond * cosf(fWinkel_Erde))
);
|
Drehen der Objekte über die Rotationsmatrix
BearbeitenEs gibt die Möglichkeit, die Rotation eines Objekts selbst durchzuführen.Man durchläuft hierzu folgende Schritte :
- Zuerst wird eine temporäre Matrix erstellt, welche den aktuellen Rotationswinkel des Objekts speichert.
- Danach benötigt man eine zweite temporäre Matrix, welche den Betrag in Grad, um welche der Rotationswinkel erhöht werden soll, speichert. (Die Umrechnung in Radiant erledigt Irrlicht)
- Nun multiplizert man die beiden temporären Matrizen miteinander, um den endgültigen Wert der Drehung zu erhalten.
- Das Objekt, welches rotiert werden soll, erhält den neuen Winkel übergeben. Man muss die Objektsmatrix mit updateAbsolutePosition() aktualisieren, damit sich das Objekt dann auch dreht.
Diese Prozedur sieht wie folgt aus :
void RotateNode(irr::scene::ISceneNode *node, irr::core::vector3df vDeltaRotation)
{
//Rotationsmatrix aus der Node abholen
irr::core::matrix4 RotationsMatrix;
RotationsMatrix.setRotationDegrees(node->getRotation());
//Neue Rotationsmatrix erstellen
irr::core::matrix4 TempMatrix;
TempMatrix.setRotationDegrees(vDeltaRotation); //Drehung berechnen und in die Matrix speichern
RotationsMatrix *= TempMatrix; //Beide Matrizen miteinander multiplizieren
node->setRotation(RotationsMatrix.getRotationDegrees()); //Neue Gradzahl verwenden
node->updateAbsolutePosition(); //Neue Rotation ausführen (sonst passiert nichts)
}
|
Der Quellcode zum Beispiel
BearbeitenUm Ihnen einen kompletten Überblick über die beschriebenen Möglichkeiten zu verschaffen, folgt der Quellcode zum Beispiel. Sie können zwischen selbst berechneten Koordinaten und Animatoren hin und her schalten, indem sie den Wert in der globalen Variable
//Sollen Animatoren verwendet werden ?
const bool bUseAnimator = false;
auf true oder false setzen.
//Einbinden der Header-Datei von Irrlicht
#include <irrlicht.h>
//Einbinden der Namespaces
using namespace irr;
using namespace core;
using namespace video;
void RotateNode(irr::scene::ISceneNode *node, irr::core::vector3df vDeltaRotation)
{
//Rotationsmatrix aus der Node abholen
irr::core::matrix4 RotationsMatrix;
RotationsMatrix.setRotationDegrees(node->getRotation());
//Neue Rotationsmatrix erstellen
irr::core::matrix4 TempMatrix;
TempMatrix.setRotationDegrees(vDeltaRotation); //Drehung berechnen und in die Matrix speichern
RotationsMatrix *= TempMatrix; //Beide Matrizen miteinander multiplizieren
node->setRotation(RotationsMatrix.getRotationDegrees()); //Neue Gradzahl verwenden
node->updateAbsolutePosition(); //Neue Rotation ausführen (sonst passiert nichts)
}
//Die Hauptprozedur main()
int main()
{
//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"Rotation von Objekten");
//Den Videotreiber erstellen und Zeiger aus dem Device abholen
IVideoDriver* driver = device->getVideoDriver();
//Einen Szene-Manager erstellen und Zeiger aus dem Device abholen
scene::ISceneManager* smgr = device->getSceneManager();
//Erstellen der SceneNodes
scene::ISceneNode * Sonne = smgr->addSphereSceneNode(1.0f); //Sonne
scene::ISceneNode * Erde = smgr->addSphereSceneNode(0.5f); //Erde
scene::ISceneNode * Mond = smgr->addSphereSceneNode(0.3f); //Mond
//Einstellen des Materials
Sonne->setMaterialTexture(0, driver->getTexture("Sun.png"));
Erde->setMaterialTexture(0, driver->getTexture("Earth.jpg"));
Mond->setMaterialTexture(0, driver->getTexture("Moon.jpg"));
//Keine Lichtberechnung
Sonne->setMaterialFlag(EMF_LIGHTING, false);
Erde->setMaterialFlag(EMF_LIGHTING, false);
Mond->setMaterialFlag(EMF_LIGHTING, false);
//Sollen Animatoren verwendet werden ?
const bool bUseAnimator = false;
if (bUseAnimator) //Animatoren verwenden ?
{
//Rotation der Sonne
scene::ISceneNodeAnimator* animRotSun = smgr->createRotationAnimator(core::vector3df(0,0.2f,0));
Sonne->addAnimator(animRotSun);
animRotSun->drop();
//Rotation der Erde
scene::ISceneNodeAnimator* animRotEarth = smgr->createRotationAnimator(core::vector3df(0,0.5f,0));
Erde->addAnimator(animRotEarth);
animRotEarth->drop();
//Rotation des Mondes
scene::ISceneNodeAnimator* animRotMoon = smgr->createRotationAnimator(core::vector3df(0,0.7f,0));
Mond->addAnimator(animRotMoon);
animRotMoon->drop();
//Orbit der Erde
scene::ISceneNodeAnimator* animOrbitEarth = smgr->createFlyCircleAnimator(core::vector3df(0,0,0),
3.0f, 0.0005f);
Erde->addAnimator(animOrbitEarth);
animOrbitEarth->drop();
//Wir brauchen die Erde als Parent für den Mond, da dieser dann
//den Nullpunkt der Erde als Zentrum für seinen Orbit hat
//createFlyCircleAnimator hat als Zentrum den Nullpunkt der Erde (0,0,0)
Mond->setParent(Erde);
//Orbit des Mondes
scene::ISceneNodeAnimator* animOrbitMoon = smgr->createFlyCircleAnimator(
core::vector3df(0,0,0), //lokale Koordinaten !!
1.2f, 0.0005f);
Mond->addAnimator(animOrbitMoon);
animOrbitMoon->drop();
}
core::vector3df EarthPos; //Positionsvektor unserer Erde
core::vector3df MoonPos; //Positionsvektor unseres Monds
//Unser 32Bit-Float zum Speichern des Rotationswinkels
f32 fWinkel = 0.0;
f32 fWinkel_Erde = 0.0;
//Unsere Radien für Erde und Mond
f32 fRadiusErde = 3.0f; //= Abstand Erde zur Sonne
f32 fRadiusMond = 1.2f; //= Abstand Mond zur Erde
//Unsere Variablen zur Zeitsteuerung
u32 uiZuletztGestoppt = device->getTimer()->getTime(); //Aktuelle Zeit stoppen
const u32 uiDeltaTime = 10; //Textur alle 50ms drehen
//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, 300, 300),false,true,0,-1,false);
//Eine Kamera erstellen
smgr->addCameraSceneNode(0, core::vector3df(0,4,-4), core::vector3df(0,0,0));
//Während das Device aktiv ist ...
while(device->run())
{
//Szene beginnen
driver->beginScene(true, true, SColor(3,150,203,255));
if (!bUseAnimator) //Keine Animatoren verwenden ?
{
//Nach Ablauf von Delta Time
if (device->getTimer()->getTime() > (uiZuletztGestoppt + uiDeltaTime))
{
//Neuen Zeitpunkt merken
uiZuletztGestoppt = device->getTimer()->getTime();
//Winkel erhöhen
fWinkel += 0.005f;
fWinkel_Erde -= 0.005f;
}
//Errechnen der Position der Erde
EarthPos = core::vector3df(
(fRadiusErde * sinf(fWinkel))
, 0,
(fRadiusErde * cosf(fWinkel))
);
//Errechnen der Position des Monds
MoonPos = core::vector3df(
EarthPos.X + (fRadiusMond * sinf(fWinkel_Erde)),
0,
EarthPos.Z + (fRadiusMond * cosf(fWinkel_Erde))
);
//Drehen der Sonne
RotateNode(Sonne,vector3df(0.0f, 0.02f, 0.0f));
//Sonne->setRotation(core::vector3df(0.0f, (fWinkel* 50), 0.0f));
//Erde positionieren und rotieren
//Erde->setRotation(core::vector3df(0.0f, fWinkel_Erde , 0.0f));
RotateNode(Erde,vector3df(0.0f, 0.02f, 0.0f));
Erde->setPosition(EarthPos);
//Mond positionieren und rotieren
Mond->setPosition(MoonPos);
RotateNode(Mond,vector3df(0.0f, 0.02f, 0.0f));
//Mond->setRotation(core::vector3df(0.0f, fWinkel_Erde , 0.0f));
}
//Verbinden der einzelnen Planeten durch Linien
//Weltkoordinaten verwenden (keine lokalen Koordinaten)
driver->setTransform(video::ETS_WORLD, core::matrix4());
//Linie von Sonne zu Erde
driver->draw3DLine(Sonne->getAbsolutePosition(), Erde->getAbsolutePosition(), SColor(255,255,0,0));
//Linie von Erde zum Mond
driver->draw3DLine(Erde->getAbsolutePosition(), Mond->getAbsolutePosition(), SColor(255,255,0,0));
//**********************************
//Zeichnen der Umlaufbahn der Erde *
//**********************************
core::vector3df tempVec( fRadiusErde, 0 , 0 ); //Anfangsstellung im Orbit
for( f32 radiant = 0; radiant <= 2 * PI; radiant += 0.1f ) //Bis 2 x PI durchlaufen
{
f32 px = fRadiusErde * cos( radiant ); //X-Position berechnen
f32 pz = fRadiusErde * sin( radiant ); //Z-Position berechnen
//Linie von letzte bis nächste Position
driver->draw3DLine( tempVec, vector3df( px, 0, pz ), SColor(93, 137, 54, 143));
//Speichern der zuletzt errechneten Position
tempVec = vector3df( px, 0, pz );
}
//Kreis schliessen
driver->draw3DLine( tempVec, vector3df( fRadiusErde, 0, 0 ), SColor(93, 137, 54, 143));
//************************************
//Zeichnen der Umlaufbahn des Mondes *
//************************************
//Radius des Mondes als Anfangsposition auf der X-Achse
tempVec = core::vector3df(fRadiusMond, 0 , 0 );
//Hinzuaddieren des Positionsvektors der Erde verschiebt den
//Mond in seine korrekte Position im Orbit um die Erde
tempVec += EarthPos;
for( f32 radiant = 0; radiant <= 2 * PI; radiant += 0.1f )
{
f32 px = EarthPos.X + (fRadiusMond * cos( radiant ));
f32 pz = EarthPos.Z + (fRadiusMond * sin( radiant ));
driver->draw3DLine( tempVec, vector3df( px, 0, pz ), SColor(125, 255, 128, 0));
tempVec = vector3df( px, 0, pz );
}
//Kreis schliessen
driver->draw3DLine( tempVec,
(vector3df( fRadiusMond, 0, 0 ) + EarthPos), SColor(125, 255, 128, 0));
//Dem Szenemanager sagen, dass er alle Nodes zeichnen soll
smgr->drawAll();
//String erstellen
core::stringw tmp(L"Rotation,Translation u. Skalierung von Objekten in Irrlicht\nTreiber : ");
tmp += driver->getName();
tmp += L"\n FPS: ["; tmp += driver->getFPS();
tmp += "]\nDreiecke gezeichnet : ";
tmp += driver->getPrimitiveCountDrawn();;
tmp += "\nGrafikkarte ist von : ";
tmp += driver->getVendorInfo();
tmp += "\nLetzter Zeitstopp : ";
tmp += uiZuletztGestoppt;
tmp += "\nPosition Erde (XYZ): ";
tmp += EarthPos.X; tmp += " ";
tmp += EarthPos.Y; tmp += " ";
tmp += EarthPos.Z;
tmp += "\nPosition Mond (XYZ): ";
tmp += MoonPos.X; tmp += " ";
tmp += MoonPos.Y; tmp += " ";
tmp += MoonPos.Z;
//String anzeigen
GUI_debug_text->setText(tmp.c_str());
//Dem GUI-Manager sagen, dass er alle GUIs zeichnen soll
guienv->drawAll();
//Szene beenden
driver->endScene();
}
//Das Device freigeben
device->drop();
//Keinen Fehler zurückgeben
return 0;
}
|