Irrlicht - from Noob to Pro: Licht, Menüs und ein Sonnensystem in Echtzeit

Aus Wikibooks
Zur Navigation springen Zur Suche springen
Wikibooks buchseite.svg Zurück zu "Sonne, Erde und Mond" | One wikibook.svg Hoch zu "Inhaltsverzeichnis" | Wikibooks buchseite.svg Vor zu "Objektauswahl realisieren"


Vorwort[Bearbeiten]

Beispielanwendung: Blick über Saturn Richtung Sonne

In diesem Abschnitt werden Sie sehen, wie man

  • ein Sonnensystem mit den Hauptplaneten Merkur bis Pluto in Echtzeit mittels der VSOP87 – Methode berechnet und darstellt,
  • Licht verwendet,
  • ein Hauptmenü in Irrlicht erstellt,
  • ein Fenster mit Text anzeigt,
  • und das Julianische Datum errechnet.

Die VSOP87-Methode[Bearbeiten]

Mit Hilfe der VSOP87-Methode, welche einen Kepler-Orbit beschreibt, lässt sich mittels Potenzreihen die Umlaufbahn eines Planeten berechnen, welche sich zu einer bestimmten Zeit am besten der echten Umlaufbahn anschmiegt.

Da diese Potenzreihen aus vielen Termen bestehen, ist es nicht empfehlenswert, diese direkt in den Sourcecode des Hauptprogramms einzubauen. Wenn Sie diese Daten direkt einbinden würden, so würde das Kompilieren des Sourcecodes sehr lange dauern. Doch was ist zu tun ? Die meiner Meinung nach einfachste Lösung ist die Erstellung und Einbindung einer statischen DLL. Diese kann beim Programmstart geladen und muss nicht ständig mit dem Sourcecode des Programms mit kompiliert werden.

Organisieren der Daten und Erstellen der DLL[Bearbeiten]

Nun benötigen wir noch die Potenzreihen zur Errechnung der Orbits der Planeten. Die eine Alternative wäre, sich die Potenzreihen in Form von Dateien zu organisieren und beim Programmstart in den Speicher zu laden. Auf dem FTP-Server des Institut de mécanique céleste et de calcul des éphémérides wären diese gespeichert (Link :ftp://ftp.imcce.fr/pub/ephem/planets/vsop87/), was uns aber nichts bringt, wenn wir die Lösung einer DLL wählen. Gott sei Dank gibt es da einen fleissigen Programmierer namens Jay Tanner, welcher ein VSOP87 – Code – Generator – Tool geschrieben hat, das die Potenzreihen direkt in brauchbaren C++ - Code umwandelt. Dies ist direkt online auf seiner Homepage möglich : http://www.neoprogrammics.com/vsop87/

Gehen Sie zu seiner Homepage und klicken Sie in linken Menü der Seite auf den Eintrag “VSOP87 Code Generator Tool”. Wählen Sie nun in den Einstellungen des Tools unter “Select Body” den Eintrag “Mercury” für den ersten Planeten im Sonnensystem. Danach wählen Sie unter “Select VSOP87 Series Version” den Eintrag “C – Heliocentric XYZ – Of Date”, um die heliozentrischen Daten zur Zeit zu erhalten. Danach wählen Sie noch unter dem Eintrag “Select Programming Language” den Eintrag “C++”, um die richtige Programmiersprache zu erhalten.

Da wir nun die Daten in Header-Dateien gespeichert haben, können wir uns daran machen, diese in eine DLL einzubinden. Verfahren Sie hierzu bitte wie folgt :

  • Starten Sie Visual C++
  • Wählen Sie im Hauptmenü zuerst “Datei”, dann “Neu” und dann “Projekt
  • Wählen Sie “Win32 Konsolenanwendung”, geben Sie den Projektnamen mit “VSOP87Dll” an und legen Sie den Speicherort fest
  • Klicken Sie im Anwendungsassistenten auf “Weiter”, wählen Sie dann den Eintrag “DLL” aus und klicken Sie auf “Fertigstellen
  • Nun haben Sie eine leere Vorlage für eine DLL zur Verfügung. Kopieren Sie nun alle vorgefertigten Header-Dateien (VSOP87_Merkur_hXYZR.h usw.) in das Projektmappenverzeichnis des eben erstellten DLL-Projekts.
  • Führen Sie einen Rechtsklick auf den Eintrag “Headerdateien” im Projektmappen-Explorer aus und wählen Sie “Hinzufügen” und dann “Vorhandenes Element” aus. Wählen Sie nun alle vorgefertigten Headerdateien aus und klicken Sie auf “Hinzufügen”.
  • Nun können wir die einzelnen Header-Dateien mittels #include-Anweisung in das Projekt einbinden. Öffnen Sie nun die VSOP87Dll.cpp - Quellcodedatei und fügen Sie folgende Includes hinzu :
#include <windows.h>
#include <math.h>
#include "VSOP87_Erde_hXYZR.h"
#include "VSOP87_Jupiter_hXYZR.h"
#include "VSOP87_Mars_hXYZR.h"
#include "VSOP87_Merkur_hXYZR.h"
#include "VSOP87_Neptun_hXYZR.h"
#include "VSOP87_Saturn_hXYZR.h"
#include "VSOP87_Uranus_hXYZR.h"
#include "VSOP87_Venus_hXYZR.h"

Nun haben wir zwar die Daten eingebunden, jedoch müssen wir nun noch festlegen, dass diese Funktionen aus der DLL exportiert werden können. Dazu müssen wir die einzelnen Funktionen entsprechend umdeklarieren, indem wir die Anweisung

extern "C" __declspec(dllexport)

voranstellen. Somit wird z.B. aus der Funktion

double Mercury_X0 (double t) // 1853 terms of order 0
{
   double X0 = 0;
   X0 += 0.37749277893*cos(4.40259139579 + 26088.1469590577*t);
   X0 += 0.11918926148*cos(4.49027758439 + 0.2438174835*t);

die Export-Funktion

extern "C" __declspec(dllexport) double Mercury_X0 (double t) // 1853 terms of order 0
{
   double X0 = 0;
   X0 += 0.37749277893*cos(4.40259139579 + 26088.1469590577*t);
   X0 += 0.11918926148*cos(4.49027758439 + 0.2438174835*t);

Dies muss auf alle Funktionen in jeder Header-Datei zu jedem Planeten angewandt werden, damit diese aus der DLL aufgerufen werden können.
Sie können danach die DLL erstellen. Da diese eine Menge Daten enthält, kann das kompilieren etwas dauern.

Das Hauptmenü[Bearbeiten]

In diesem Abschnitt will ich das erstellen des Hauptmenüs sowie der Fenster erklären. Wir benötigen diese Elemente zum Steuern des Programms sowie der Darstellung der Informationen der jeweiligen Planeten.

Organisieren des Hauptmenüs[Bearbeiten]

Um das Hauptmenü unseres Programms zu erstellen, benötigen wir die Schnittstelle zum Nachrichtensystem von Irrlicht. Jeder Eintrag im Hauptmenü der Irrlicht-GUI bekommt eine Nummer zugeordnet, welche diesen Eintrag identifiziert.

Erstellen wir nun eine Nummerierung mittels des Datentyp

enum

von C++, welcher uns für jeden Menüpunkt unseres Hauptmenüs eine Nummer speichert :

enum
{
	GUID_BEENDE_PROGRAMM = 101, //Datei -> Beenden
	GUID_HILFE,					//-> Hilfe
	GUID_GEHEZU_SONNE,			//Anzeigen -> Sonne
	GUID_GEHEZU_MERKUR,			//Anzeigen -> Merkur
	GUID_GEHEZU_VENUS,			//Anzeigen -> Venus
	GUID_GEHEZU_ERDE,			//Anzeigen -> Erde
	GUID_GEHEZU_MARS,			//Anzeigen -> Mars
	GUID_GEHEZU_JUPITER,		//Anzeigen -> Jupiter
	GUID_GEHEZU_SATURN,			//Anzeigen -> Saturn
	GUID_GEHEZU_URANUS,			//Anzeigen -> Uranus
	GUID_GEHEZU_NEPTUN,			//Anzeigen -> Neptun
	GUID_GEHEZU_PLUTO,			//Anzeigen -> Pluto	
	GUID_ZEITRECHUNG_ALLE10MS,		//Datei -> Zeitsteuerung -> Positionsberechnung alle 10 ms
	GUID_ZEITRECHUNG_ALLE20MS,		//Datei -> Zeitsteuerung -> Positionsberechnung alle 20 ms
	GUID_ZEITRECHUNG_ALLE50MS,		//...
	GUID_ZEITRECHUNG_ALLE100MS,
	GUID_ZEITRECHUNG_ALLE500MS,
	GUID_ZEITRECHUNG_ALLE1000MS,
	GUID_ZEITRECHUNG_1SEK_IST_1SEK,		//Datei -> Zeitsteuerung -> 1 Sekunde = 1 Sekunde
	GUID_ZEITRECHUNG_1SEK_IST_1MIN,		//Datei -> Zeitsteuerung -> 1 Sekunde = 1 Minute
	GUID_ZEITRECHUNG_1SEK_IST_1STD,		//Datei -> Zeitsteuerung -> 1 Sekunde = 1 Stunde
	GUID_ZEITRECHUNG_1SEK_IST_1TAG,		//...
	GUID_ZEITRECHUNG_1SEK_IST_2TAGE,
	GUID_ZEITRECHUNG_1SEK_IST_1WOCHE,
	GUID_ZEITRECHUNG_1SEK_IST_2WOCHEN,
	GUID_ZEITRECHUNG_1SEK_IST_3WOCHEN,
	GUID_ZEITRECHUNG_1SEK_IST_4WOCHEN
};

Somit hätten wir ab dem Wert 101 fortlaufend alle Menüeinträge durchnummeriert. Nun müssen wir uns eine Struktur erstellen, um den Zeiger auf das Irrlicht-Device innerhalb der Ereignisbehandlung OnEvent() zugänglich zu machen, weshalb wir eine Struktur erstellen, welche einen Zeiger auf dieses Device bei Programmstart zugewiesen bekommt :

// Deklarieren einer Struktur zum Speichern
// Des Irrlicht-Kontexts zum Zugriff innerhalb der
// OnEvent()-Ereignisbehandlung
struct SAppContext
{
	IrrlichtDevice *device;	
};

Nun können wir bei Programmstart den Zeiger auf das Device speichern.

Prozedur zum Erstellen der GUI[Bearbeiten]

Das Hauptmenü in Aktion

Da wir nun die Menüeinträge durchnummeriert haben, können wir eine Prozedur erstellen, welche uns das Hauptmenü sowie das Info–Fenster erstellt. Hierzu benötigen wir zuerst ein paar Variablen, welche wir nach den Include-Anweisungen als globale Variablen deklarieren :

IGUIWindow* GUI_InfoWindow = 0;//Für das Info-Fenster
IGUIStaticText* GUI_Info_Text = 0; //Text des Info-Fensters
IGUIWindow* GUI_HelpWindow = 0; //Für das Hilfe-Fenster
IGUIStaticText* GUI_Help_Text = 0; //Text des Hilfe-Fensters
//Die erste Zeile des Hilfe-Fensters
core::stringw tempStr(L"Steuerung ueber das Hauptmenue\n");

Wie sie sehen, sind jeweils eine Variable vom Typ IGUIWindow für das Fenster-Handle sowie eine Variable vom Typ IGUIStaticText zum anzeigen des Fenster-Texts nötig. Die letzte Variable dient zum Erstellen des Fenster-Texts des Hilfe-Fensters. Nun benötigen wir innerhalb der Prozedur Zugriff auf das GUI-Enviroment von Irrlicht, weshalb wir uns auch gleich einen Zeiger darauf übergeben lassen :

void Erstelle_GUI(gui::IGUIEnvironment *guienv)
{

Ändern der Schriftart[Bearbeiten]

Nun können wir als erste Handlung die Schriftart der Oberfläche unserer GUI verändern. Dazu benötigen wir einen Zeiger auf die Oberfläche der GUI, welche wir uns mit

IGUISkin* skin = guienv->getSkin();

holen und speichern. Des weiteren Laden wir uns die von uns mittels Irrlichts neuem Font-Tool erstellte Schriftart in den Speicher und legen diese als Schriftart für unsere GUI fest. Das ganze sieht dann so aus :

IGUISkin* skin = guienv->getSkin(); //Zeiger auf Oberfläche holen
IGUIFont* font = guienv->getFont("FranklinGothic12.xml"); //Zeiger auf geladene Font
if (font) //Font erstellt ?
	skin->setFont(font); //Font zuweisen

Falls das neue Font-Tool von Irrlicht nicht zu finden sein sollte :

  • Der Source-Code findet sich im folgenden Unterverzeichnis des Irrlicht-Verzeichnisses :
    \tools\IrrFontTool\newFontTool
  • Wenn Sie diesen Source-Code mit VisualC++ 2008 kompiliert haben, so finden Sie das Programm FontTool.exe in folgendem Unterverzeichnis von Irrlicht :
    \bin\Win32-VisualStudio

Erstellen der Menüeinträge[Bearbeiten]

Um überhaupt Zugriff auf das Hauptmenü zu bekommen, benötigen wir einen Zeiger auf eine Hauptmenü-Klasse vom Typ gui::IGUIContextMenu, welche uns das Bearbeiten ermöglicht. Danach können wir mittels der Funktion addItem der besagten Klasse die Menüeinträge erstellen. Hierzu der Quellcode :

// Erstelle das Hauptmenü (Haupteinträge)
gui::IGUIContextMenu* menu = guienv->addMenu();
	
menu->addItem(L"Datei", -1, true, true);	//Haupteintrag "Datei"
menu->addItem(L"Anzeigen", -1, true, true);	//Haupteintrag "Anzeigen"
menu->addItem(L"Hilfe", -1, true, true);	//Haupteintrag "Hilfe"

Die Parameter der Funktion addItem lauten von links nach recht wie folgt :

  • Der erste Parameter ist der Text des Menüeintrags vom Typ wchar_t
  • Der zweite Parameter vom Typ s32 legt die ID-Nummer des Eintrags fest, welcher bei Auswahl des Menüeintrags an die Ereignisbehandlungsroutine OnEvent() weiter gegeben wird. Dies ermöglicht die Identifizierung des gewählten Menüeintrags.
  • Der dritte Parameter vom Typ bool legt fest, ob der Menüeintrag freigegeben (also true) oder gesperrt (also false) ist.
  • Der vierte Parameter bestimmt, ob der Menüeintrag Untereinträge besitzt. Da im obigen Beispiel alle 3 Einträge Hauptmenueeinträge sind, sind die Werte jeweils true

Erstellen der Untereinträge im Hauptmenü[Bearbeiten]

Um die Untereinträge zu erstellen, benötigen wir einen zweiten Zeiger auf eine Klasse vom Typ gui::IGUIContextMenu, dem wir jedoch diesmal mittels getSubMenu zuweisen, ab welchem Eintrag die Untereinträge beginnen. Im folgenden erstellen wir die Klasse und weisen ihr den Haupteintrag “Datei” als Ziel für die Untereinträge zu :

// Erstelle Untermenüs (Hauptmenüeintrag "Datei")
gui::IGUIContextMenu* submenu;
submenu = menu->getSubMenu(0);
submenu->addItem(L"Zeitsteuerung", -1, true, true);	
submenu->addSeparator();
submenu->addItem(L"Beenden", GUID_BEENDE_PROGRAMM);

Wie im Quelltext gut zu erkennen ist, erhält der Untereintrag “Zeitsteuerung” ebenfalls Untereinträge. Nach diesem Untereintrag wird ein Separator (also ein Trennstrich um Hauptmenü) eingefügt. Als letztes wird der Untereintrag “Beenden” eingefügt, welcher eine ID-Nummer zugewiesen bekommt, mit der er in der OnEvent()-Routine identifiziert werden kann. Hier nun der Code für die restlichen Untermenüeinträge :

// Erstelle Untermenüeinträge für "Datei" -> "Zeitsteuerung"
submenu = submenu->getSubMenu(0);
submenu->addItem(L"Positionsberechnung alle 10 ms", GUID_ZEITRECHUNG_ALLE10MS);
submenu->addItem(L"Positionsberechnung alle 20 ms", GUID_ZEITRECHUNG_ALLE20MS);
submenu->addItem(L"Positionsberechnung alle 50 ms", GUID_ZEITRECHUNG_ALLE50MS);
submenu->addItem(L"Positionsberechnung alle 100 ms", GUID_ZEITRECHUNG_ALLE100MS);
submenu->addItem(L"Positionsberechnung alle 500 ms", GUID_ZEITRECHUNG_ALLE500MS);
submenu->addItem(L"Positionsberechnung alle 1 Sekunden", GUID_ZEITRECHUNG_ALLE1000MS);
submenu->addSeparator();
submenu->addItem(L"1 Sekunde = 1 Sekunde", GUID_ZEITRECHUNG_1SEK_IST_1SEK);
submenu->addItem(L"1 Sekunde = 1 Minute", GUID_ZEITRECHUNG_1SEK_IST_1MIN);
submenu->addItem(L"1 Sekunde = 1 Stunde", GUID_ZEITRECHUNG_1SEK_IST_1STD);
submenu->addItem(L"1 Sekunde = 1 Tag", GUID_ZEITRECHUNG_1SEK_IST_1TAG);
submenu->addItem(L"1 Sekunde = 2 Tage", GUID_ZEITRECHUNG_1SEK_IST_2TAGE);
submenu->addItem(L"1 Sekunde = 1 Woche", GUID_ZEITRECHUNG_1SEK_IST_1WOCHE);
submenu->addItem(L"1 Sekunde = 2 Wochen", GUID_ZEITRECHUNG_1SEK_IST_2WOCHEN);
submenu->addItem(L"1 Sekunde = 3 Wochen", GUID_ZEITRECHUNG_1SEK_IST_3WOCHEN);
submenu->addItem(L"1 Sekunde = 4 Wochen", GUID_ZEITRECHUNG_1SEK_IST_4WOCHEN);

// Erstelle Untermenüeinträge (Hauptmenüeintrag "Anzeigen")
submenu = menu->getSubMenu(1);
submenu->addItem(L"Sonne", GUID_GEHEZU_SONNE);
submenu->addItem(L"Merkur", GUID_GEHEZU_MERKUR);
submenu->addItem(L"Venus", GUID_GEHEZU_VENUS);
submenu->addItem(L"Erde", GUID_GEHEZU_ERDE);
submenu->addItem(L"Mars", GUID_GEHEZU_MARS);
submenu->addItem(L"Jupiter", GUID_GEHEZU_JUPITER);
submenu->addItem(L"Saturn", GUID_GEHEZU_SATURN);	
submenu->addItem(L"Uranus", GUID_GEHEZU_URANUS);
submenu->addItem(L"Neptun", GUID_GEHEZU_NEPTUN);
submenu->addItem(L"Pluto", GUID_GEHEZU_PLUTO);
	
//Erstelle Untermenüs (Hauptmenüeintrag "Hilfe")	
submenu = menu->getSubMenu(2);
submenu->addItem(L"Hilfe anzeigen", GUID_HILFE);

Wie sie erkennen können, wird jedem Untereintrag nur noch der Text und die ID-Nummer zugewiesen.

Erstellen des Info-Fensters[Bearbeiten]

Nun können wir das Info-Fenster erstellen, welches uns über den jeweils ausgewählten Planeten ein paar Informationen anzeigen soll :

//Erstelle Info-Fenster	
GUI_InfoWindow = guienv->addWindow(rect<s32>(0,50,350,400),false,L"Info – Fenster");

Die Parameter von addWindow lauten wie folgt :

  • Der erste Parameter definiert das Rechteck, in dem das Fenster dargestellt werden soll. Der erste Wert ist die X-Koordinate (also in Pixeln vom linken Bildschirmrand) des linken Fensterrands. Der zweite Wert ist die Y-Koordinate (also in Pixeln vom oberen Bildschirmrand) des oberen Fensterrands. Der dritte Wert ist die X-Koordinate des rechten Fensterrands. Der vierte Wert ist die Y-Koordinate des unteren Fensterrands.
  • Der zweite Parameter bestimmt, ob das Fenster modal ist. (Ist ein Fenster modal, so ist es immer im Vordergrund).
  • Der dritte Parameter bestimmt den Fenstertitel.

Da das Fenster nicht geschlossen werden soll, verstecken wir den Schliessen-Button der Fensterleiste mit :

//Den Schliessen-Button verstecken.
GUI_InfoWindow->getCloseButton()->setVisible(false);

Nun fügen wir die erste Zeile des Fenster-Texts hinzu. Dieser Fenster-Text wird dann je nach angezeigtem Planeten in die jeweiligen Infos abgeändert :

//Den Text des Fensters hinzufügen (nur erste Zeile)
GUI_Info_Text = guienv->addStaticText(
	L"Info-Fenster",rect<s32>(5, 20, 300, 400),false,true,GUI_InfoWindow,-1,false);

Festlegen der Transparenz[Bearbeiten]

Da das Hauptmenü sowie alle Fenster undurchsichtig sind, wollen wir hier die Transparenz der kompletten GUI etwas erhöhen. Dazu müssen wir die gespeicherten Farbwerte der GUI zuerst auslesen, dann den Alpha-Wert modifizieren und zu guterletzt auch wieder abspeichern. Dies geht wie folgt :

//Alle GUI-Alpha-Werte gleich setzen
for (u32 i=0; i<EGDC_COUNT ; ++i)
	{
		//Farbwert abholen
		video::SColor col = guienv->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
		col.setAlpha(240); //Alpha auf 240 setzen (255 = undurchsichtig)
		//Farbwert neu setzen
		guienv->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
	}

Nun haben wir die Prozedur zum erstellen des Hauptmenüs sowie des Info-Fensters fertig. Die komplette Prozedur sieht so aus :

void Erstelle_GUI(gui::IGUIEnvironment *guienv)
{	
	IGUISkin* skin = guienv->getSkin(); //Zeiger auf Oberfläche holen
	IGUIFont* font = guienv->getFont("FranklinGothic12.xml"); //Zeiger auf geladene Font
	if (font) //Font erstellt ?
		skin->setFont(font); //Font zuweisen
	
	// Erstelle das Hauptmenü (Haupteinträge)
	gui::IGUIContextMenu* menu = guienv->addMenu();
	
	menu->addItem(L"Datei", -1, true, true);	//Haupteintrag "Datei"
	menu->addItem(L"Anzeigen", -1, true, true);	//Haupteintrag "Anzeigen"
	menu->addItem(L"Hilfe", -1, true, true);	//Haupteintrag "Hilfe"
		
	// Erstelle Untermenüs (Hauptmenüeintrag "Datei")
	gui::IGUIContextMenu* submenu;
	submenu = menu->getSubMenu(0);
	submenu->addItem(L"Zeitsteuerung", -1, true, true);	
	submenu->addSeparator();
	submenu->addItem(L"Beenden", GUID_BEENDE_PROGRAMM);
	// Erstelle Untermenüeinträge für "Datei" -> "Zeitsteuerung"
	submenu = submenu->getSubMenu(0);
	submenu->addItem(L"Positionsberechnung alle 10 ms", GUID_ZEITRECHUNG_ALLE10MS);
	submenu->addItem(L"Positionsberechnung alle 20 ms", GUID_ZEITRECHUNG_ALLE20MS);
	submenu->addItem(L"Positionsberechnung alle 50 ms", GUID_ZEITRECHUNG_ALLE50MS);
	submenu->addItem(L"Positionsberechnung alle 100 ms", GUID_ZEITRECHUNG_ALLE100MS);
	submenu->addItem(L"Positionsberechnung alle 500 ms", GUID_ZEITRECHUNG_ALLE500MS);
	submenu->addItem(L"Positionsberechnung alle 1 Sekunden", GUID_ZEITRECHUNG_ALLE1000MS);
	submenu->addSeparator();
	submenu->addItem(L"1 Sekunde = 1 Sekunde", GUID_ZEITRECHUNG_1SEK_IST_1SEK);
	submenu->addItem(L"1 Sekunde = 1 Minute", GUID_ZEITRECHUNG_1SEK_IST_1MIN);
	submenu->addItem(L"1 Sekunde = 1 Stunde", GUID_ZEITRECHUNG_1SEK_IST_1STD);
	submenu->addItem(L"1 Sekunde = 1 Tag", GUID_ZEITRECHUNG_1SEK_IST_1TAG);
	submenu->addItem(L"1 Sekunde = 2 Tage", GUID_ZEITRECHUNG_1SEK_IST_2TAGE);
	submenu->addItem(L"1 Sekunde = 1 Woche", GUID_ZEITRECHUNG_1SEK_IST_1WOCHE);
	submenu->addItem(L"1 Sekunde = 2 Wochen", GUID_ZEITRECHUNG_1SEK_IST_2WOCHEN);
	submenu->addItem(L"1 Sekunde = 3 Wochen", GUID_ZEITRECHUNG_1SEK_IST_3WOCHEN);
	submenu->addItem(L"1 Sekunde = 4 Wochen", GUID_ZEITRECHUNG_1SEK_IST_4WOCHEN);

	// Erstelle Untermenüeinträge (Hauptmenüeintrag "Anzeigen")
	submenu = menu->getSubMenu(1);
	submenu->addItem(L"Sonne", GUID_GEHEZU_SONNE);
	submenu->addItem(L"Merkur", GUID_GEHEZU_MERKUR);
	submenu->addItem(L"Venus", GUID_GEHEZU_VENUS);
	submenu->addItem(L"Erde", GUID_GEHEZU_ERDE);
	submenu->addItem(L"Mars", GUID_GEHEZU_MARS);
	submenu->addItem(L"Jupiter", GUID_GEHEZU_JUPITER);
	submenu->addItem(L"Saturn", GUID_GEHEZU_SATURN);	
	submenu->addItem(L"Uranus", GUID_GEHEZU_URANUS);
	submenu->addItem(L"Neptun", GUID_GEHEZU_NEPTUN);
	submenu->addItem(L"Pluto", GUID_GEHEZU_PLUTO);
	
	//Erstelle Untermenüs (Hauptmenüeintrag "Hilfe")	
	submenu = menu->getSubMenu(2);
	submenu->addItem(L"Hilfe anzeigen", GUID_HILFE);

	//Erstelle Info-Fenster	
	GUI_InfoWindow = guienv->addWindow(
		rect<s32>(0,50,350,400),false,L"Info - Fenster");
	//Den Schliessen-Button verstecken.
	GUI_InfoWindow->getCloseButton()->setVisible(false);
	//Den Text des Fensters hinzufügen (nur erste Zeile)
	GUI_Info_Text = guienv->addStaticText(
		 L"Info-Fenster",rect<s32>(5, 20, 300, 400),false,true,GUI_InfoWindow,-1,false);

	//Alle GUI-Alpha-Werte gleich setzen
	for (u32 i=0; i<EGDC_COUNT ; ++i)
					{
						//Farbwert abholen
						video::SColor col = guienv->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
						col.setAlpha(240); //Alpha auf 240 setzen (255 = undurchsichtig)
						//Farbwert neu setzen
						guienv->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
					}
};

Errechnen des Julianischen Datums[Bearbeiten]

Da das Julianische Datum frei von Schalttagen, unterschiedlich langen Monaten und Uhrumstellungen ist, eignet es sich in der Astronomie zur Berechnung von Zeitdifferenzen. Wir benötigen das Julianische Datum für die Angabe des Zeitpunkts in unserer VSOP87 Methode. Das Julianische Datum zählt die Tage fortlaufend ab dem 1. Januar 4713 vor Christus, 12:00 Uhr Mittags. Die Vorkommastellen bestimmen somit die vergangenen Tage und die Nachkommastellen den Bruchteil des vergangenen Tages.
Wenn das Datum errechnet wird, dann müssen die Monate im Bereich von 0 bis 11 angegeben werden. Ist also der Monat kleiner als März, so muss von der Jahreszahl ein Jahr abgezogen werden und der Monatszahl dafür 12 Monate hinzuaddiert werden. Ansonsten errechnet sich das Datum wie in C++ mit der Struktur struct tm (Monat + 1, Jahr + 1900 für das Kalenderdatum) gehabt.
Nachdem man Tag, Stunde, Minute und Sekunde in lokale Variablen gespeichert hat, muss man einen Hilfswert berechnen, welcher sich aus einer Ganzzahl aus den Werten (Jahr / 400) – (Jahr / 100) ergibt. Anschliessend zerlegt man die Uhrzeit in eine Gleitkommazahl, welche die Nachkommastellen des Julianischen Datums darstellen. Dies funktioniert durch Uhrzeit = Stunde + Minute * (1.0 / 60.0) + Sekunde * (1.0 / 3600.0). Die Werte ergeben sich daraus, dass eine Stunde 60 Minuten sowie 3600 Sekunden hat. Man zerlegt die Minuten und Sekunden also in Stundenbruchteile.
Nun kann man das Datum selbst berechnen :
365,25 Tage * Anzah der Jahre +
30,6001 Tage * (Anzahl der Monate + 1) +
Hilfswert + 1720996.5 Tage + Tag + (Uhrzeit Gleitkommazahl / 24.0 Stunden)
Zur Erläuterung der Werte : 1 Jahr hat 365,25 Tage, ein Monat hat durchschnittlich 30,6001 Tage, seit dem 1.Januar 4713 v.Chr. bis zum Jahr 0 sind 1720996,5 Tage vergangen. Für genauere Angaben über diesen Algorithmus sehen Sie bitte hier und hier nach.
Die ganze Funktion sieht dann so aus :

f32 Berechne_Julianisches_Datum(struct tm *LokalZeit)
{
	f32 JulianischesDatum = 0.0f;
	s32 Jahr = 0;
	s32 Monat = 0;
	s32 Tag = 0;
	s32 Stunde = 0;
	s32 Minute = 0;
	s32 Sekunde = 0;
	s32 Hilfswert_B = 0;
	f32 fUhrzeit = 0.0f; //Die Uhrzeit als Gleitkommazahl

	if (LokalZeit->tm_mon <= 1) //Monat <= Februar ?
	{
		Jahr = ((LokalZeit->tm_year + 1900) - 1); //Ein Jahr abziehen
		Monat = ((LokalZeit->tm_mon + 1) + 12); //12 Monate hinzuaddieren
	} else
	{
		Jahr = (LokalZeit->tm_year + 1900); //Jahr speichern
		Monat = (LokalZeit->tm_mon + 1); //Monat speichern
	}
	Tag = LokalZeit->tm_mday; //Tag speichern
	Stunde = LokalZeit->tm_hour; //Stunde speichern
	Minute = LokalZeit->tm_min; //Minute speichern
	Sekunde = LokalZeit->tm_sec; //Sekunde speichern
	Hilfswert_B = (int(Jahr / 400) - int(Jahr / 100)); //Hilfswert B berechnen
	fUhrzeit = (Stunde + 
			   (Minute * (1.0f / 60.0f)) +		//1 Min. = 60 Sek.
			   (Sekunde * (1.0f / 3600.0f))		//1 Min. = 3600 Sek.
			   ); //Uhrzeit als Gleitkommazahl
	JulianischesDatum = int(365.25f * Jahr) + 
						int(30.6001f * (Monat + 1)) + 
						Hilfswert_B +
						1720996.5f + Tag + (fUhrzeit / 24.0f);
	
	return JulianischesDatum;
};

Positionsbestimmung der Planeten[Bearbeiten]

Da wir die Daten der jeweiligen Kepler-Orbits in die DLL kompiliert haben, können wir uns nun daran machen, diese Daten zu nutzen. Dazu müssen wir die beim kompilieren der DLL entstandene Lib-Datei (in meinem Fall “VSOP87Dll.lib”) mit in die Einstellungen des Linkers einbinden. Klicken Sie auf “Projekt” -> “Eigenschaften”-> “Konfigurationseingenschaften” -> “Linker” -> “Eingabe” -> “Zusätzliche Abhängigkeiten” und geben Sie hier die Lib-Datei an.
Danach müssen die Funktionen der DLL noch deklariert werden. Fügen Sie dem Projekt noch eine Headerdatei für die Funktionsdeklarationen hinzu (in meinem Fall “VSOP87DLL.h”). Die Deklaration lautet wie folgt :

//Erde
extern "C" __declspec(dllimport) double Earth_X0 (double t);
extern "C" __declspec(dllimport) double Earth_X1 (double t);
extern "C" __declspec(dllimport) double Earth_X2 (double t);
(...)
//Merkur
extern "C" __declspec(dllimport) double Mercury_X0 (double t);
extern "C" __declspec(dllimport) double Mercury_X1 (double t);
extern "C" __declspec(dllimport) double Mercury_X2 (double t);
(...)
//Venus
extern "C" __declspec(dllimport) double Venus_X0 (double t);
extern "C" __declspec(dllimport) double Venus_X1 (double t);
extern "C" __declspec(dllimport) double Venus_X2 (double t);
(...)
//Mars
extern "C" __declspec(dllimport) double Mars_X0 (double t);
extern "C" __declspec(dllimport) double Mars_X1 (double t);
extern "C" __declspec(dllimport) double Mars_X2 (double t);
(...)
//Jupiter
extern "C" __declspec(dllimport) double Jupiter_X0 (double t);
extern "C" __declspec(dllimport) double Jupiter_X1 (double t);
extern "C" __declspec(dllimport) double Jupiter_X2 (double t);
(...)
//Saturn
extern "C" __declspec(dllimport) double Saturn_X0 (double t);
extern "C" __declspec(dllimport) double Saturn_X1 (double t);
extern "C" __declspec(dllimport) double Saturn_X2 (double t);
(...)
//Uranus
extern "C" __declspec(dllimport) double Uranus_X0 (double t);
extern "C" __declspec(dllimport) double Uranus_X1 (double t);
extern "C" __declspec(dllimport) double Uranus_X2 (double t);
(...)
//Neptun
extern "C" __declspec(dllimport) double Neptune_X0 (double t);
extern "C" __declspec(dllimport) double Neptune_X1 (double t);
extern "C" __declspec(dllimport) double Neptune_X2 (double t);

Nachdem diese Importfunktionen deklariert wurden, können wir sie nutzen, um uns die zeitabhängigen Positionen der Planeten berechnen zu lassen. Dazu schreiben wir uns eine Funktion, welche einen Positionsvektor zurück gibt. Als Übergabeparameter benötigen wir das gewünschte Julianische Datum sowie eine ID, welche den zu berechnenden Planeten identifiziert.

core::vector3df BerechnePosition_Planet(f32 JulDat, int ubPlanet = 0)
{
	//Julianisches Datum ab dem Jahr 2000 berechnen
	f32 fTime = (JulDat - 2451545.0f) / 365250.0f;
	//Heliozentrische Koordinaten
	f64 hXp = 0, hYp = 0, hZp = 0;
	switch(ubPlanet)
	{
	//Merkur
	case 1: hXp = Mercury_X0(fTime) + Mercury_X1(fTime) + Mercury_X2(fTime)
			   + Mercury_X3(fTime) + Mercury_X4(fTime) + Mercury_X5(fTime);
		hYp = Mercury_Y0(fTime) + Mercury_Y1(fTime) + Mercury_Y2(fTime)
			   + Mercury_Y3(fTime) + Mercury_Y4(fTime) + Mercury_Y5(fTime);
		hZp = Mercury_Z0(fTime) + Mercury_Z1(fTime) + Mercury_Z2(fTime)
			   + Mercury_Z3(fTime) + Mercury_Z4(fTime) + Mercury_Z5(fTime);
		break;
	//Venus
	case 2: hXp = Venus_X0(fTime) + Venus_X1(fTime) + Venus_X2(fTime)
				+ Venus_X3(fTime) + Venus_X4(fTime) + Venus_X5(fTime);
		hYp = Venus_Y0(fTime) + Venus_Y1(fTime) + Venus_Y2(fTime)
			   + Venus_Y3(fTime) + Venus_Y4(fTime) + Venus_Y5(fTime);
		hZp = Venus_Z0(fTime) + Venus_Z1(fTime) + Venus_Z2(fTime)
			   + Venus_Z3(fTime) + Venus_Z4(fTime) + Venus_Z5(fTime);
		break; 
(...)
	//Neptun
	case 8: hXp = Neptune_X0(fTime) + Neptune_X1(fTime) + Neptune_X2(fTime)
			    + Neptune_X3(fTime) + Neptune_X4(fTime) + Neptune_X5(fTime);
		hYp = Neptune_Y0(fTime) + Neptune_Y1(fTime) + Neptune_Y2(fTime)
			    + Neptune_Y3(fTime) + Neptune_Y4(fTime) + Neptune_Y5(fTime);
		hZp = Neptune_Z0(fTime) + Neptune_Z1(fTime) + Neptune_Z2(fTime)
			    + Neptune_Z3(fTime) + Neptune_Z4(fTime) + Neptune_Z5(fTime);
		break; 	
	}
	//XZY
	return core::vector3df((hXp * AU_to_KM) / fMassstab,
			       (hZp * AU_to_KM) / fMassstab, 
			       (hYp * AU_to_KM) / fMassstab);
}

Da der Rückgabewert als Astronomische Einheiten dimensioniert ist, wird dieser mit dem Faktor AU_to_KM multipliziert und durch den Maßstab, welcher in der Variable fMassstab deklariert ist, geteilt.

Berechnung der Position von Pluto[Bearbeiten]

Nun sind wir in der Lage, alle Positionen der Planeten zu berechnen. Das einzige Problem ist, dass Pluto auch zum inneren Sonnensystem gehört, aber nicht in der VSOP87-Methode berechnet wird. Die Lösung dieses Problems erarbeitete ein kluger Kopf namens Stephen L. Moshier (der Author der wohl bekannten "Cephes Math Library"), welcher die Terme für Pluto als Tabelle erarbeitete und durch eine Berechnung des Kepler-Orbits von Pluto uns eine Laufbahn erstellen lässt. Zu finden sind diese Daten und Funktionen auf seiner Homepage, wenn man unter “Ephemeriden” nach “Pluto ephemeris” sucht. Dort gibt es einen Beispielcode zum errechnen der Pluto-Laufbahn. Ich habe die Funktionen und Terme von Herrn Moshier etwas modifiziert und auf VisualStudio übertragen. Wenn Sie den kompletten Sourcecode sehen wollen, so finden Sie ihn hier.
Um nun die Position für Pluto berechnen zu können, benötigen wir zuerst ein Array mit 3 Elementen von Typ double. Die von Herrn Moshier entworfene Funktion kepler benötigt 3 Argumente :

  • Das Julianische Datum als double
  • Eine Struktur vom Typ orbit, welche im Sourcecode bereits deklariert ist und die zur Berechnung benötigten Datentypen enthält.(Epoche, Inklination, Longitude, Periphel, Exzentrizität u.v.m)
  • Ein Array von Typ double zur Übergabe der Polarkoordinaten

Zuerst übergibt man diese 3 Argumente an die kepler-Funktion und erhält als Ergebnis die Polarkoordinaten als 3 double-Werte im Array polar gespeichert. Diese wandelt man nun durch einen Aufruf der dms-Funktion, welche uns diese Werte von Radiant in Grad, Gradminuten und Gradsekunden umwandelt. Haben wir diese Werte nun in unserem Array, so können wir mit Hilfe von Sinus und Cosinus die Koordinaten von Pluto errechnen.

//Pluto
case 9: double polar[3];
        kepler(JulDat, &orb, polar);
        dms( modtp( polar[0] ) );
        dms( modtp( polar[1] ) );
        hXp = (polar[2] * cos(polar[0]) * cos(polar[1]));
        hYp = (polar[2] * sin(polar[0]) * cos(polar[1]));
        hZp = (polar[2] * sin(polar[1]));
        break;

Organisieren der Planetendaten[Bearbeiten]

Zuerst benötigen wir eine Struktur, in welcher wir unsere Daten der jeweilgen Planeten abspeichern können. Diese werden dann später in der GUI angezeigt und dienen uns auch zum Berechnen der Größe der jeweiligen Planeten.

/////////////////////////////////////////////////////
// Unsere Struktur zum Speichern der Planetendaten //
/////////////////////////////////////////////////////
struct TPlanetInfo
{
	f32 f_Durchmesser;	//Der Durchmesser des Planeten in km
	f32 f_Periphel;		//Der sonnennaheste Abstand in km
	f32 f_Aphel;		//Der sonnenfernste Abstand in km
	f32 f_Rotation;		//Rotation um 360° (Anzahl der Tage)
	f32 f_NeigungBahnebene;	//Neigung der Bahn (Grad)
	f32 f_NeigungRotAchse;	//Neigung der Rotationsachse (Grad)
	f32 f_OrbitalGeschwindigkeit;	//Tempo (km/s)
};

Nun können wir unsere Strukturen erstellen und auch gleich mit den erforderlichen Daten füllen.

//////////////////////////
// Unsere Planetendaten //
//////////////////////////
//Sonne
TPlanetInfo Info_Sonne = { 1390000.0f, 0.0f, 0.0f, 25.4f, 0.0f, 0.0f, 0.0f };
//Merkur
TPlanetInfo Info_Merkur = { 4880.0f, 69700000.0f, 45900000.0f, 58.6f, 7.0f, 0.01f, 47.87f };
//Venus
TPlanetInfo Info_Venus = { 12103.6f, 107400000.0f, 109000000.0f, 224.701f, 3.395f, 177.36f, 35.02f };
//Erde
TPlanetInfo Info_Erde = { 12700.0f, 147100000.0f, 152100000.0f, 1.0f, 0.0f, 23.44f, 29.78f };
//Mond
TPlanetInfo Info_Mond = { 3476.0f, 363300.0f, 405500.0f, 27.322f, 5.145f, 6.68f, 1.023f };
//Mars
TPlanetInfo Info_Mars = { 6800.0f, 206700000.0f, 249100000.0f, 686.980f, 1.85f, 25.19f, 24.13f };
//Ceres
TPlanetInfo Info_Ceres = { 975.0f, 380500000.0f, 446800000.0f, 467.0f, 10.59f, 4.0f, 17.909f };
//Jupiter
TPlanetInfo Info_Jupiter = { 142800.0f, 740900000.0f, 815700000.0f, 4331.272f, 1.305f, 3.13f, 13.07f };
//Saturn
TPlanetInfo Info_Saturn = { 120500.0f, 1347000000.0f, 1507000000.0f, 10757.696f, 2.484f, 26.73f, 9.69f };
//Uranus
TPlanetInfo Info_Uranus = { 51118.0f, 2735000000.0f, 3004000000.0f, 30680.817f, 0.77f, 97.77f, 6.81f };
//Neptun
TPlanetInfo Info_Neptun = { 49528.0f, 4456000000.0f, 4537000000.0f, 60181.308f, 1.769f, 28.32f, 5.43f };
//Pluto
TPlanetInfo Info_Pluto = { 2390.0f, 4425000000.0f, 7375000000.0f, 90452.736f, 17.16f, 122.53f, 4.72f };
//Eris
TPlanetInfo Info_Eris = { 2450.0f, 5662000000.0f, 14593000000.0f, 203405.44f, 44.179f, 0.0f, 3.629f };

Da das Sonnensystem riesig ist und wir so große Werte auf eine andere Größeneinheit kürzen wollen, definieren wir einen Maßstab von 1:100.000 sowie die Umrechnungskonstante von AU (Astrological Units) zu Kilometer:

f32 fMassstab = 100000.0f; //Maßstab der Darstellung
f32 AU_to_KM = 149597870.691f; //AU (Astronomical Unit) in km

Zusammenführen des Quellcodes[Bearbeiten]

Zuerst binden wir die benötigten Dateien ein, definieren die verwendeten Namespaces und beginnen mit der Einsprungprozedur:

#include "stdafx.h"
#include "GUI.hpp"
#include "DatenSonnenSystem.h"
#include "Funktionen.hpp"

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

int _tmain(int argc, _TCHAR* argv[])
{

Nun erstellen wir zum Schein ein Irrlicht-Device, mit welchem wir die Auflösung des Desktop abfragen und speichern:

        // Ein Null-Device zum Auslesen der Desktop-Größe erstellen
	IrrlichtDevice *NullDevice = createDevice(video::EDT_NULL);
	// Auslesen der Größe des Desktops
	core::dimension2d<u32> DeskTopRes = NullDevice->getVideoModeList()->getDesktopResolution();
	// Das Null-Device wieder freigeben
	NullDevice -> drop();

Dies wird gefolgt von unserem eigentlichen IrrlichtDevice, welches in einem Fenster mit Größenveränderung dargestellt werden soll:

        //Unser Irrlicht-Device erstellen und initialisieren
	IrrlichtDevice *device = createDevice( video::EDT_OPENGL, DeskTopRes, 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"Das aktuelle Sonnensystem");

	//Die Fenstergröße soll veränderbar sein
	device->setResizable(true);

Danach benötigen wir unsere Standardklassen von Irrlicht: VideoDriver, SceneManager, Kamera, GUIEnviroment, EventReceiver (mit eigenem Kontext)

        //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();
	
	//Eine Kamera erstellen
	scene::ICameraSceneNode* Kamera = smgr->addCameraSceneNodeMaya();
	Kamera->setFarValue(50000.0f); //Bis zum 50000 Einheiten Sichtweite
	Kamera->setPosition(vector3df(0.0f, 100.0f, -100.0f)); //Neben dem Mittelpunkt platzieren (Sonne)
	Kamera->setTarget(vector3df(0.0f, 0.0f, 0.0f)); //Auf die Sonne blicken

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

	//Unseren Context für das GUI erstellen
	SAppContext context;	
	context.device = device;
		
	//Unser Event-Receiver (Klasse definiert in GUI.hpp)
	MyEventReceiver receiver(context);
        
        //Dem Device den Event-Receiver mitteilen
	device->setEventReceiver(&receiver);

Danach speichern wir die aktuelle Systemzeit, zerlegen den Zeitwert ins Julianische Datum und speichern die Irrlicht-spezifische Startzeit:

        //Aktuellen Zeitpunkt speichern
	SpeichereZeit();

	//Julianisches Datum errechnen
	fJulianischesDatum = Berechne_Julianisches_Datum(LokalZeit);

	//Vermerken der Startzeit
	uZuletztGestoppt = device->getTimer()->getTime();

Nun erstellen wir die GUI, die Planeten, die Skybox und bereiten das Array auf die Speicherung der Plantenpositionen zur Darstellung des Orbits vor:

        //Erstellen der GUI
	Erstelle_GUI(guienv);
 
        //Erstellen der einzelnen Planeten
	ErstellePlaneten(smgr, driver);

	//Erstellen der Arrays zum Nachzeichnen des Orbits
	Erstelle_Orbit_Arrays();

	//Erstelle Skybox
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);	
	SkyBox = smgr->addSkyDomeSceneNode(driver->getTexture("SkyDome.png"));
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);

Jetzt geben wir der Sonne die Leuchtkraft und stellen das Umgebungslicht ein, denn auch im Universum ist es sonst nicht komplett dunkel:

        //Das Umbegungslicht ("Ambient Light") einstellen
	smgr->setAmbientLight(video::SColorf(0.2f,0.2f,0.2f));
	//Eine starke Lichtquelle in der Sonne platzieren
	Lightnode = smgr->addLightSceneNode(0, core::vector3df(0, 0, 0), video::SColorf(1.0f, 1.0f, 1.0f, 1.0f), 150000.0f);

Nun beginnen wir mit unserer Renderloop. Nach dem Starten der Szene wird zuerst mit der VSOP87-Methode die Planetenpositionen relativ zur Zeit berechnet. Danach werden die Planeten auf den errechneten Positionen platziert und in der Szene dargestellt. Auch die GUI wird danach gezeichnet.

        while(device->run())
	{	
		//Szene beginnen
		driver->beginScene(true, true, SColor(3,150,203,255));	

		//Errechnen der Positionen anhand des julianischen Datums
		BerechnePlanetenPositionen();

		//Planeten an die errechneten Positionen setzen
		PositionierePlaneten();						
		
		//Dem Szenemanager sagen, dass er alle Nodes zeichnen soll
		smgr->drawAll();	

		//Dem GUI-Manager sagen, dass er alle GUIs zeichnen soll 
		guienv->drawAll();

Da wir die Planetenbewegung in verschiedenen Geschwindigkeiten darstellen wollen, müssen wir die Zeitmessung deutlich berücksichtigen. Dazu fragen wir zuerstmal einen aktuellen Zeitpunkt ab und prüfen, ob dieser um den gewünschten Betrag in der Zukunft liegt. Falls dies der Fall ist, speichern wir den neuen Zeitpunkt und addieren den Betrag bis zum nächsten Schritt darauf auf. Da wir das julianische Datum für die VSOP87-Methode benötigen, müssen wir den Zeitwert wieder zerlegen und das julianische Datum daraus berechnen. Diese julianische Datum zeigen wir daraufhin in der GUI an.

                //Nach Ablauf von Delta Time
		if (device->getTimer()->getTime() > (uZuletztGestoppt + uDeltaTime))
		{
			//Neuen Zeitpunkt der Messung merken
			uZuletztGestoppt = device->getTimer()->getTime();
			//Addieren der eingestellten Zeitspanne
			Zeit += uZuAddierendeZeit;
			//Zerlegen des neuen Zeitwertes
			LokalZeit = localtime(&Zeit);
			//Berechnen des julianischen Datums
			fJulianischesDatum = Berechne_Julianisches_Datum(LokalZeit);
			//Ausgeben des Info-Texts im Info-Fenster
			Erstelle_Info_Text(fJulianischesDatum, LokalZeit);
		}

Nun veranlassen wir die Kamera, den ausgewählten Planeten zu verfolgen, während er durch unser Weltall wandert. Da die VSOP87-Methode in Schrittpunkten zerlegt ist, müssen wir bei jedem Durchgang prüfen, ob der Planet seine aktuelle Position geändert hat. Falls ja, dann vergrößern wir das Array mit den Positionswerten und speichern die neue Position. Der Orbit eines jeden Planeten wir anschliessend noch als Linie angezeigt und die Szene beendet.

                //Den selektierten Planeten mit der Kamera verfolgen
		Verfolge_gewaehlten_Planeten(device);

		//Hat sich die Position im Orbit geändert ?
		Pruefe_Orbit_Pos();

		//Den Orbit anhand der Planetenposition zeichnen
		Zeichne_tatsaechlichen_Orbit(driver);
		
		//Szene beenden
		driver->endScene();

Quellcodes zum Beispiel[Bearbeiten]

Sie finden den jeweiligen Quellcode des Hauptprogramms hier:

Datei Beschreibung
Sonnensystem.cpp Einsprungpunkt _tmain(), Erstellen von Device,Kamera,GUI, EventReceiver, Speichern des julianischen Datums
Funktionen.hpp Erstellen der Planeten, Berechnen der Positionen, Berechnen des julianischen Datums, Handling der Arrays zum Nachzeichnen der Orbits
DatenSonnensystem.h Speichern der Informationen über jeden Planeten, Berechnen der Position jedes Planeten
PlutoMoshier.hpp Algorithmus zum Berechnen der Pluto-Position anhand des julianischen Datums (Original von Stephen L. Moshier)
GUI.hpp Verwaltung und Erstellung der GUI sowie des EventReceivers

Der Quellcode mit den Termen zur zeitgesteuerten Positionsberechnung kann hier aus Gründen des Urheberrechts nicht veröffentlicht werden. Es wird hier allerdings unter "Die VSOP87-Methode" erklärt, wie man die Daten organisiert und in eine DLL einbaut. Es ist zwecklos, die Funktionen in die EXE mit einzubringen, da der Kompilier- und Linkervorgang sehr viel Zeit in Anspruch nimmt. Es ist ein sehr leistungsstarker Rechner erforderlich, um diese Daten bei jedem Erstellungsvorgang mit einzubinden. Alternativ kann man die Daten in einer/mehreren externen Dateien speichern und beim Programmstart von dort auslesen.

--Onkeltorty 10:12, 16. Feb. 2012 (CET)