Vielleicht habt ihr schon festgestellt, dass das Programmieren mit OpenGL doch ziemlich zeitaufwendig ist, da jedes einzelne Objekt erstmal bis ins kleinste beschrieben werden muss.
Etwas einfacher wird es, wenn man deskriptive Erweiterungen wie den Open Inventor oder SGI's Performer nutzen kann. Der Nachteil dabei ist aber, dass sich deren Methoden nicht einfach in eigene Programme einbinden lassen (falls man überhaupt an diese Bibliotheken/Programme rankommt).
Hier findet ihr daher ein etwas komplexeres Beispiel, wie man VRML-Dateien in eigenen Programmen zur Beschreibung und Darstellung von Objekten verwenden kann. Der Quelltext steht unter der GPL zur Verfügung und kann daher auch von euch genutzt und geändert werden, solange eure Programme und Quelltexte wiederum der GPL unterliegen. Da ich weiss, dass das Beispiel noch lange nicht ausgereift ist, bin ich für (umgesetzte) Erweiterungsvorschläge jederzeit zu haben!
P.S.: Auf eine Beschreibung des VRML-Standards verzichte ich mal...
Quelltext (Verzeichnis "GLviews")
|
VRMLControl.H/.cxx |
stellt die Basisklasse zur Auswertung von VRML-Dateien zur Verfügung |
Objects.H/.cxx |
setzt die VRML-Aufrufe in OpenGL-Befehle um |
Tools.H/.cxx |
stellt Hilfsfunktionen zum Laden von Bilddateien, für Vektorrechung und Tesselation sowie Ausgabefunktionen zur Verfügung |
|
glutShell.cxx |
main() Funktion, die beispielhaft die Klasse VRMLConrol nutzt |
GLSettings.H/.cxx |
ein paar einfache Darstellungs- und Berechnungsfunktionen |
Unter MS Windows benötigte Bibliotheken (Verzeichnis "other")
|
libjpeg.lib,
jpeglib.h, jmorecfg.h, jerror.h, jconfig.h |
Die JPEG-Bibliothek (*.h und *.lib kann man auch ins Compiler-Verzeichnis kopieren).
Linux bringt die Dateien normalerweise schon mit. |
glut32.lib,
GLUT32.dll, GLUT.H |
Die GLUT-Bibliothek (*.h und *.lib kann man auch ins Compiler-Verzeichnis, *.dll ins Windows-Verzeichnis kopieren).
Linux bringt die Dateien normalerweise schon mit. |
General Public License und Compiler-Steuerungsdateien
|
License.de, License.en |
Die GPL in deutsch und englisch. Gilt für alle Quelltexte und damit auch für das endgültige Programm. |
glut_vrml.dsw, glut_vrml.dsp |
Die Projektdatei für MS Visual C++ 6.0 |
- die main()-Funktion in glutShell.cxx erzeugt die grafische Oberfläche mit Hilfe von GLUT
- scannt danach das Verzeichnis "Modelle/" nach VRML-Dateien ab (*.wrl)
- erzeugt ein einfaches Koordinatensystem zur Orientierung
- nach Aufruf des Menüs (mittels rechter Maustaste) wird die entsprechende Beispieldatei geladen:
- die Funktion ReloadFromFile(int whichFile) erzeugt ein VRML-Objekt durch Aufruf von MyObjectControl = new VRMLControl( DateiListe[whichFile] );
- der Konstruktor von VRMLControl ruft die Funktion ScanFile(); auf, die mittels CheckFile(DateiName, "r") und extractDir(char *complete, char *directory, char *file) den Dateinamen vom Speicherort (Verzeichnis) trennt und das Verzeichnis für spätere Suchfunktionen speichert
- danach wird der Dateityp mittels CheckForType() überprüft (derzeit werden z.B. nur ungepackte VRML 2.0 Dateien unterstützt)
- ScanForRootNodes() durchsucht die Datei rekursiv nach Schlüsselwörtern:
- zuerst nach Wurzelknoten (root nodes), wobei jeder Knoten einen eigenen Bezeichner mittels GenNodeName(NextNodeName) erhält
- dann nach Schlüsselwörtern wie Group oder Transformation und ruft entsprechende Methoden auf:
AddGroup(Ebene, Parent, NextNodeName) und AddTransform(Ebene, Parent, NextNodeName)
- AddGroup(Ebene, Parent, NextNodeName) aus Objects.H/.cxx erzeugt ein neues Objekt und speichert es in einer Objektliste:
ThisNode = new GroupNode(); // Objekt "Group" anlegen
ThisNode->SetName(ThisNodeName); // Namen festlegen
ObjectPtr[ObjectCount] = ThisNode; // Objekt in die Objektliste aufnehmen
// [snip]
RootNode[RootNodeCount] = ThisNode; // als Wurzeleintrag oder Kindobjekt festlegen
Parent->AddChildren( (VRMLNode*)ThisNode );
// [snip]
ScanForBraces(Klammer2); // Anzahl der öffnenden und schliessenden Klammern festhalten
AddChildren( Ebene, (GroupingNode*)ThisNode ); // Kindobjekte "children" erfassen
- AddTransform(Ebene, Parent, NextNodeName); aus Objects.H/.cxx erzeugt ein neues Objekt und speichert es in einer Objektliste (wie AddGroup), unterscheidet aber andere Schlüsselworte ("center", "rotation", "translation", "scale" etc.) und ruft die entsprechenden Methoden auf:
ThisNode->Rotation(Angle, Axle);
- AddChildren(int Ebene, GroupingNode *Parent) scannt nach weiteren Schlüsselworten ("USE", "DEF", "Shape", "Group", Transform") und legt entsprechende Objekte mittels AddShape, AddGroup und AddTransform an
- AddShape(GroupingNode *Parent, char ThisNodeName[]) erzeugt einen Körper (ShapeNode), und sucht nach den beschreibenden Angaben (KeyAppearance(ThisNode, NextNodeName);, KeyGeometry(ThisNode, NextNodeName); und AskForAppearance();)
- KeyAppearance(ShapeNode *MyShape, char ThisNodeName[]) nutzt die Merkmale anderer Objekte (USE) oder sucht nach neuen Objektmerkmalen (AddAppearance(MyShape, ThisNodeName);)
- AddAppearance(MyShape, ThisNodeName); erzeugt ein AppearanceNode() Objekt und registriert die Merkmale in der aufrufenden ShapeNode
MyShape->AddAppearance(ThisNode);
ThisNode->AddMaterial, AddTexture, AddTextureTransform // "erben" vom Elternteil
KeyMaterial, KeyTexture, KeyTextureTransform // Merkmale ermitteln
- AddMaterial(AppearanceNode *MyAppearance, char ThisNodeName[]) erzeugt ein Materialobjekt, registriert es als Objekteigenschaft und speichert die Farb- und Transparenzwerte
- AddImageTexture(AppearanceNode *MyAppearance, char ThisNodeName[]) erzeugt ein Texturobjekt
TextureNr = texture ID
AddTextureURL(url) // (url = Dateiname)
ScanTextureImage() // aus Objects.H/.cxx
loadPPMfile und loadJPEGfile // Ladefunktionen für PPM und JPEG Bilddateien aus Tools.H/.cxx
PrepareTexture() // OpenGL setup für diese texture ID
- AddTextureTransform( AppearanceNode *MyAppearance, char ThisNodeName[]) legt Transformationen für die Textur fest (Rotation, Translation, Verzerren)
- KeyGeometry( ShapeNode *MyShape, char ThisNodeName[]) scannt jetzt nach erkennbaren Geometrien wie Cone, Cylinder, Sphere, Box (für) einfache Objekte) und IndexedFaceSet und IndexedLineSet (für komplexe Objekte)
- die Add* Methoden für Cone, Cylinder, Sphere, Box erzeugen ein entsprechendes Objekt und registriert die Merkmale in der aufrufenden ShapeNode
- AddLineSet erzeugt eine Verbindungslinie zwischen vielen Eckpunkten und wertet dazu Koordinaten-, Farb- und Indexfelder aus
KeyField(ThisNode, Color, NextNodeName);
KeyField(ThisNode, Coordinate, NextNodeName);
AddIndex(ThisNode, Color);
AddIndex(ThisNode, Coordinate);
PrepareForDraw() // Setup
- AddFaceSet arbeitet ähnlich LineSet, nur erzeugt es zwischen den Verbindungslinien farbige und schattierte Flächen