Irrlicht - from Noob to Pro: Licht, Menüs und ein Sonnensystem in Echtzeit
Vorwort
[Bearbeiten]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]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)