Open Source im professionellen Einsatz

Interaktive 3D-Welten mit Coin und Qt - Teil 2

Bewegte Objekte

Auch hochfliegende Ideen lassen sich mit Coin und wenigen Zeilen C++ schnell umsetzen: Aus einfachen Objekten mit Texturen und komplexen VRML-Modellen entsteht eine animierte 3D-Welt.

3D-Objekte wirken erst mit einer Textur (Oberflächengestaltung) realistisch. Wer mit Coin und C++ arbeitet (siehe Kasten "Rückblick"), kann diesen Effekt sehr einfach nutzen. Die Objekte muss man dafür nicht mühsam von Hand programmieren: Open Inventor und damit auch Coin können VRML-1.0-Dateien einbinden, für die es ausgereifte grafische Modellierwerkzeuge gibt. Durch Animation haucht der Entwickler seiner Welt Leben ein.

Rückblick

Der erste Teil dieser kleinen Coin-Serie[1] beschrieb, wie mit Hilfe von Coin und SoQt dreidimensionale Grafiken in C++ entstehen. Coin[2] ist ein freier Klon von Open Inventor (SGI[3] und TGS[4]), der auf OpenGL aufbaut. Die Beispiele in diesem und im vorangegangenen Artikel enthalten keinen Coin-spezifischen Code. Die Programme sollten daher mit jeder Open-Inventor-Version und jedem Klon laufen, mit Ausnahme der Bindung zum Fenstersystem: Hier kommt Qt statt Motif zum Einsatz.

Ein Vorteil von Open Inventor gegenüber OpenGL ist, dass Ersterer 3D-Szenen durch einen Szenengraphen beschreibt. Der Szenengraph ist als Baumstruktur aufgebaut; jedes 3D-Element ist in einem eigenen Knoten enthalten. Wie und wo Coin einen Knoten anzeigt hängt von seiner Position im Graphen ab. So ist es möglich, eine Szene (oder Teile davon) zu speichern oder ihr Verhalten (Interaktion und Animation) zu programmieren.

Bindeglied der verschiedenen Teile einer 3D-Welt ist der Szenengraph. Für ihn gilt "Teile und herrsche": Eine komplexe Szene ist einfacher zu programmieren, wenn sie in kleinere Teile zerlegt ist. Beim Programmieren einer Szene ist es ratsam, zu Beginn eine Liste mit allen benötigten Objekten und deren Eigenschaften zu erzeugen. Für jede Geometrie ist dann ein Gruppenknoten anzulegen. In den fügt man zuerst die gewünschten Transformationen ein (etwa Rotation oder Skalierung), ergänzt die Materialeigenschaften (etwa die Farbe) und beschreibt erst dann die eigentliche Geometrie. Die Reihenfolge ist wichtig, weil Open Inventor zum Rendern (darstellen) auf OpenGL zurückgreift.

Beim Unterteilen der Szene sollte man darauf achten, dass sich möglichst viele Objekte mehrfach verwenden lassen. Das spart Code und ist übersichtlicher. Eine Möglichkeit ist es, Funktionen zu schreiben, die eigene kleine Szenengraphen oder Gruppen über einen Zeiger zurückgeben. Auf diese Weise lassen sich die Komponenten einfach in den Szenengraphen einhängen.

Beim Programmieren ist es üblich, wiederkehrende Teile des Codes in Bibliotheken auszulagern. Das trifft auch auf die Grafikprogrammierung zu: Für vorgefertigte Beschreibungen von wiederkehrenden, komplexeren Objekten lassen sich Dateien einsetzen. Die beiden gängigsten Varianten Dateien einzubinden, sind dreidimensionale Szenen, die durch eigene Szenengraphen beschrieben werden, sowie 2D-Bilder (in diesem Zusammenhang auch Texturen genannt). Letztere werden als Oberfläche auf Objekte abgebildet.

Texturen

Texturen sollen Objekte realistischer aussehen lassen. Open Inventor kennt zwei Varianten: Texturen können durch eine Matrix im Speicher beschrieben sein oder als zweidimensionale Bilder in Dateien vorliegen. Ersteres ist sehr aufwändig und in der Regel nicht nötig. Um eine Textur aus einer Datei zu laden, erzeugt das Programm zuerst ein Objekt der Klasse »SoTexture2«. Dieses Objekt muss dann den Namen der Bilddatei erfahren:

SoTexture2 *texture = new SoTexture2;
texture->filename.setValue("textur.rgb");

Das Texturobjekt muss im Szenengraphen vor dem Objekt eingefügt sein, auf das es abgebildet wird. Open Inventor kennt zwei Wege, Texturen auf Objekte zu legen: Er kann eine Textur einzeln (der Default-Fall) oder mehrfach auf eine Oberfläche zeichnen. Coin nutzt die »simage«-Bibliothek[5], um Texturen zu laden, und unterstützt damit folgende Formate: JPEG (».jpg«), GIF (».gif«), Targa (».tga«), PIC (».pic«), SGI-RGB (».rgb«, ».bw«) und XWD (».xwd«).

Listing 1 zeigt Code, der eine Kugel mit der Weltkarte überzieht. Der Code ist als Funktion implementiert, die einen Zeiger vom Typ »SoSeparator« zurückliefert. An diesem Gruppenknoten hängen eine Textur »SoTexture2« und eine Kugel »SoSphere«. Die Datei »weltkarte.rgb« steht auf[8] bereit; sie stammt ursprünglich aus den Beispielen des Inventor Mentor. Abbildung 1 zeigt das Resultat dieser Funktion, eingebunden in einen Szenengraphen und gerendert mit »SoQtExaminer«.

Listing 1: Erde zeichnen

01 SoSeparator* zeichneErde()
02 {
03    // Objekte für Gruppenknoten und Textur
04    SoSeparator *erde = new SoSeparator;
05    SoTexture2  *textur_erde = new SoTexture2;
06 
07    // Name der Texturdatei
08    textur_erde->filename = "weltkarte.rgb";
09 
10    // Textur an den Gruppenknoten hängen
11    erde->addChild(textur_erde);
12 
13    // Kugel an den Gruppenknoten hängen
14    // => Textur auf die Kugeloberfläche zeichnen
15    erde->addChild(new SoSphere);
16 
17    return erde;
18 }

Abbildung 1: Die Erde in 3D. Für diese Grafik genügen eine Kugel und die passende Oberflächentextur.

Abbildung 1: Die Erde in 3D. Für diese Grafik genügen eine Kugel und die passende Oberflächentextur.

Externe Geometrien hinzufügen

Wie erwähnt kann in einer Szene ein Objekt mehrfach, an verschiedenen Stellen, in unterschiedlichen Größen und Ausrichtungen erscheinen. Da Open Inventor die Form unabhängig von der Lage, der Orientierung und dem Aussehen (etwa der Farbe) in einem Objekt beschreibt, kann es der Programmierer beliebig oft verwenden. Wie das Stuhl-Beispiel im ersten Artikel[1] zeigte, sind diese Objekte nur einmal nötig.

Die Beschreibung von Geometrien erfordert gerade bei komplizierteren Formen viel Code. Um ein größeres Projekt übersichtlicher zu gestalten, bietet es sich an, diese komplexeren Objekte in Dateien auszulagern. Hierfür kennt Open Inventor ein eigenes Dateiformat, ähnlich VRML. Die Endung der Dateinamen ist ».iv«, der Inhalt kann entweder aus Ascii-Zeichen oder aus binären Daten bestehen. Für kleinere Szenen empfiehlt es sich, die Daten in (lesbarem) Ascii zu speichern. Für größere Files ist das kompakte Binärformat besser geeignet, da die Dateien kleiner sind und Coin sie schneller lädt.

Die in Listing 2 vorgestellte Funktion »ladeGeometrie()« gibt einen Zeiger auf den Szenengraphen zurück, der in einer Datei gespeichert ist. Die Funktion enthält mehrere Fehlerbehandlungen, um nur gültige Szenen zu laden.

Listing 2: Geometrie aus Datei laden

01 SoSeparator* ladeGeometrie(const char *dateiname)
02 {
03    // Wurzel des Szenengraphen
04    SoSeparator *datei_szene = new SoSeparator;
05 
06    // Handler für Open-Inventor-Datei
07    SoInput myScene;
08 
09    // Szenendatei öffnen
10    if (!myScene.openFile(dateiname))
11    {
12       printf("Fehler beim Laden der Datei '%s'n", dateiname);
13       return NULL;
14    }
15 
16    // Hat die Datei ein gültiges Format?
17    if (!myScene.isValidFile())
18    {
19       printf("Die Datei '%s' ist keine gültige Inventor-Datein", dateiname);
20       return NULL;
21    }
22 
23    // Szene lesen und an den Gruppenknoten 'datei_szene' hängen
24    datei_szene = SoDB::readAll(&myScene);
25 
26    if (datei_szene == NULL)
27    {
28       printf("Fehler beim Lesen der Datei '%s'n", dateiname);
29       myScene.closeFile();
30       return NULL;
31    }
32 
33    // Datei schließen
34    myScene.closeFile();
35 
36    return datei_szene;
37 }

Einige Beispiele für Open-Inventor-Dateien sind auf[8] zu finden. Dort liegt auch »boeing767.iv« mit der Geometrie eines Boeing-767-Jets. Abbildung 2 stellt die Szene im »SoQtExaminer« dar. Ein Blick in die Ascii-Textversion zeigt, wie aufwändig die Beschreibung dieser Szene ist.

Abbildung 2: Das Flugzeugmodell ist in einer Textdatei beschrieben, die von einem Coin-Programm geöffnet und in den Szenengraphen eingebunden wird.

Abbildung 2: Das Flugzeugmodell ist in einer Textdatei beschrieben, die von einem Coin-Programm geöffnet und in den Szenengraphen eingebunden wird.

In der Grafik-Programmierung ist es üblich, komplexe Objekte (etwa Gestalten und Fahrzeuge) in Modellierprogrammen zu entwickeln. Die Objekte exportiert der Designer in das jeweilige Dateiformat (Open Inventor ».iv« oder VRML ».wrl«). Diese Files lädt das Coin-Programm zur Laufzeit. Es gibt eine Menge Modellier-Tools, viele sind kostenlos für den privaten Gebrauch. Sie unterstützen meist mehrere Dateiformate.

Diesen Artikel als PDF kaufen

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook