OGRE - Befreiung von einem Monster
Schon vor einigen Jahren wurde mir von verschiedenen Seiten empfohlen, für mein Bunny-Hill-Projekt die OGRE-Library zu verwenden. Die Optionen waren so vielversprechend, dass ich versuchte, OGRE unter Linux zu installieren. Es gelang nicht; ich stand vor einem gigantischen Berg von Abhängigkeiten, die ich in mühsamer Arbeit zwar fast auflösen konnte, aber eben nur fast. Am Schluss blieb nur die Alternative, das ganze Linux-System zu erneuern, mit der Wahrscheinlichkeit des totalen Crashes. Das war mir OGRE nicht wert, denn wer gibt schon ein gut funktionierendes System (damals Debian) auf?
In der Tat entpuppte sich OGRE als äußerst schwer zu installierendes Programmmonster, und als solches habe ich es im folgenden bezeichnet, wenn mir jemand wieder OGRE vorschlug. Dass "Ogre" tatsächlich auch die Bezeichnung für ein Monster ist, erfuhr ich erst vor einigen Monaten, also Jahre später, als ich nämlich einen erneuten Versuch startete. Da ich das allerneueste Ubuntu-System aufgezogen hatte, gelang die Installation tatsächlich, wenngleich die Größe der Library erschreckend war. Immerhin erlaubte es "apt-get", alle Abhängigkeiten in einem Rutsch zu installieren. Und dann lief - für mich überraschend - sogar der Compiler durch, ohne Fehlermeldung.
Mir war jederzeit klar, dass die Einarbeitung eine gehörige Portion Arbeit darstellt. Ich habe alle Basis-Tutorials durchgearbeitet und unter Codeblocks installiert. Ferner habe ich das Buch "Ogre 3D Beginners's Guide" Zeile für Zeile durchgearbeitet. Ergebnis: Nach 2 Monaten habe ich noch keinen wirklich selbständigen Schritt geschafft, ja, ich bin noch nicht mal so weit, dass ich sagen kann, ob OGRE für mein Bunny-Hill-Projekt überhaupt geeignet ist. Meine vielen Versuche sind im Dschungel des Terrain-Systems stecken geblieben. Die umfangreiche Dokumentation täuscht; sie ist in weiten Teilen ungeordnet und plätschert an der Oberfläche herum. Rezeptive Hinweise noch und noch, ohne in die Tiefe zu gehen und Zusammenhänge wirklich zu erklären.
Es gibt ohne Frage eine Reihe von Pluspunkten. Dazu gehört natürlich die immense Leistungsfähigkeit, was das Rendern betrifft. Dazu gehört auch das flexible Skripting-System, das z.B. auf einfache Weise die Programmierung von Shadern erlaubt. Ich denke auch, dass OGRE bezüglich der LOD-Optimierung an vorderer Stelle steht. Also, wenn es um die grafische Leistung geht, hat OGRE wohl einen Spitzenplatz. Doch was nutzt das alles, wenn man die Leistung nicht abrufen kann, teils mangels Einsicht in die Zusammenhänge, teils weil die Leistungsfähigkeit auf bestimmte Situationen und Anforderungsprofile fixiert ist? Gerade was den letzten Punkt betrifft, fällt Bunny-Hill wohl ziemlich aus dem Rahmen der "normalen" Anwendungssituationen, und mehr und mehr wird mir klar, dass Bunny Hill wohl kein Fall für OGRE ist.
Im wesentlichen sind es zwei Dinge, die ich nach meinen Erfahrungen sehr deutlich kritisieren möchte: der Anspruch auf totale Dominanz sowie der fundamentalisch überhöhte Einsatz von OOP-Methoden. Zum ersten Punkt: OGRE ist eine Hilfsbibliothek für begrenzte Aufgaben, nicht mehr. Als solche hat sie sich dem eigentlichen Programmobjekt unterzuordnen und entsprechend geschmeidig und gefügig zu sein. Stattdessen kommt sie mit dem Anspruch daher, das gesamte Programm unter seine Fittiche zu nehmen, angefangen von den Programmstrukturen bis hin zum Programmierstil. Man programmiert nicht, indem man mit OGRE eine Grafik-Engine in das eigene Programm einbindet, sondern indem man ein OGRE-Konstrukt auf den eigenen Anwendungszweck hinbiegt. Das ist in mancherlei Beziehung unerträglich.
Dahinter steckt natürlich das Selbstverständnis der Entwickler, die durch den Erfolg dieser Library keineswegs bescheidener geworden sind. Typisch für ihr Verhalten ist, dass immer auf die neuesten Versionen der eingebundenen Zusatzlibraries zurückgegriffen wird. Und wenn diese nicht mehr zu einer neuen Entwicklungslinie passen, mag sie noch so marginal sein, werden sie abgehängt, oder sie passen sich gefälligst den OGRE-Anforderungen an (Beispiel CEGUI). Ganz offensichtlich hat sich OGRE zu einem eigenen Universum entwickelt, ohne Rücksicht auf die Außenwelt. Jeder, der dieses Universum betritt, hat sich gefälligst einzufügen.
Der zweite Kritikpunkt ist die Art, wie objektorientierte Programmierung umgesetzt wird. Da ich nicht nur die Verwendung von Klassen meine, sondern auch Dinge wie Namespaces oder überhaupt alle Aspekte, die C++ vom elementaren C abheben, nenne ich es mal "C++-Stil". Um es gleich in aller Deutlichkeit zu sagen: C++ ist ein phantastisches Werkzeug, und die Vorteile gegenüber C sind immens - sofern die Mittel vernünftig und situationsgerecht eingesetzt werden. Das heißt, Klassen können äußerst leistungsfähig und übersichtlich sein, wenn der Gegenstand dafür geeignet ist. Auch Namespaces können die Programmierung erleichtern, wenn sie nicht wie bei OGRE zu einem Dschungel ausufern, der den Code hart an die Grenze der Lesbarkeit bringt. Ähnliches gilt, wenn fast zwanghaft über einfachste Standardtypen eine eigene Typdefinition gelegt wird. Dahinter steckt mehr als nur sinnvolle Anpasssung; dahinter steckt der Trieb der Entwickler, von allem und jedem Besitz ergreifen zu wollen. Wie ein Hund, der seine Duftmarken in die Landschaft pinkelt.
Viele moderne Entwickler haben das Programmieren unter dem Einfluss von Hochschullehrern gelernt, die die OOP fundamentalistisch auffassen und akademisch-abstrakt begründen. So äußerte mal ein solcher Dozent: "Strukturierte Typen gehören der Vergangenheit an, nun bringen wir alles in Klassen unter." Das ist natürlich Unsinn, denn Klassen sind dort, aber nur dort sinnvoll, wo die klassentypischen Vorteile greifen können. z.B. bei der Kapselung von Dingen, die in einem schlüssigen (!) und logisch leicht nachzuvollziehenden Sinnzusammenhang stehen. Oder bei der Vererbung, wenn sich durch Polymorphie deutliche Programmiervorteile ergeben, vor allem struktureller Art. Es gibt viele solcher Fälle, es gibt aber auch Klassenbildungen, die krampfhaft und aufgesetzt sind und das Verständnis des Codes erschweren, letzten Endes also kontraproduktiv sind.
Gleich das erste Kapitel im OGRE-Manual ist der OOP gewidmet, es ist ein offenkundiges Bekenntnis zum totalen Einsatz von OOP: "Object Orientation - more than just a buzzword". Begründet wird dieses damit, dass nur so bei einem größeren Projekt wie OGRE die Übersicht und damit die Erweitertbarkeit gewährleistet werden könne. Das ist schlichtweg Quatsch! Voraussetzung für die Übersicht in einem größeren Projekt ist eine schlüssige und vorausschauende Strukturierung, und die ist zunächst mal unabhängig von OOP. Wirklich große Programmprojekte wie z.B. die OpenGL-Library, der Linux-Kernel oder - um etwas kürzer zu greifen - die SDL-Libraries beweisen es. Sie sind in C geschrieben und doch von außerordentlicher Stabilität, die nur durch eine vernünftige innere Struktur zu erreichen ist. Es stimmt allerdings, dass die OOP zu einer gewissen Strukturierung zwingt, wenn auch nicht immer zu einer guten. Das heißt, die OOP greift auch mittelmäßigen Programmierern unter die Arme. Immerhin.
Im Falle einer Engine kommt noch etwas hinzu. Klassen kapseln zunächst mal Dinge und schließen sie von der Außenwelt ab. Sie sind gewissermaßen mit fertig eingerichteten Häusern vergleichbar. Wer das Haus benutzen will (eine Engine ist ja nicht Selbstzweck, sondern zum Benutzen da), muss sich gefälligst erst mal an die Wohnungseinrichtung gewöhnen und kann auch nicht so ohne weiteres das Haus zerlegen. Ja, er muss sogar den Einrichtungsstil weitgehend akzeptieren, und wer da was ändern will, muss sich mühsam darüber informieren, was änderungsfähig ist und welche Regeln dafür gelten. Problematisch wird es dann, wenn mehrere Häuser in der Landschaft stehen, eins für die Grafik, ein anderes für die GUI-Elemente, ein weiteres für die Physik usw. Dann müssen in komplizierten Verfahren die Häuser geöffnet und zu einem Gesamtbereich verbunden werden. "Wrapper" nennt man wackeligen Zelte, die man drüberspannt. In sich geschlossen ist das Ganze dann bei weitem nicht. Wesentlich günstiger wäre es, wenn sich die Libraries mit der Klassenbildung zurückhielten und nur das Inventar in Form von Elementarklassen (oder auch nur Funktionen) anböte. Das Haus zu bauen wäre dann Sache des Anwendungsprogramms.
Auch das Argument, eine konsequent auf OOP beruhende Bibliothek biete mehr Sicherheit vor Programmierfehlern, ist ziemlich gewagt. In den 6 bis 7 Jahren, in denen ich mich mit C- bzw. C++-Programmierung (ohne größere externe Libraries) befasste, habe ich weniger Segfaults produziert als in den zwei Monaten meiner Beschäftigung mit OGRE. Das ist aber auch eine Folge der unzulänglichen Dokumentation. Man kann nur dann eine geerbte Funktion überschreiben, wenn man genauestens (!) darüber informiert ist was zu tun ist, oder aber wenn man die virtuelle Funktion in der Basisklasse voll versteht. Die Dokumentation von OGRE leistet das nicht, und vor der mit Doxygen erstellten API-Dokumentation warnen zu Recht sogar die OGRE-Entwickler. So ist es nicht verwunderlich, wenn die meisten "normalen" Anfänger erst mal verzweifeln. Der oben erwähnte, rezeptive Charakter der Dokumentation ist eine Folge der exzessiven OOP-Programmierung. Wie soll man sonst dem Anwender vermitteln, was er mit der geerbten Klasse machen soll. Wenn man nicht in die Basisklasse abtauchen will (oder soll), geht es doch nur mit "To dies, dann das", wobei dieses Vorgehen nicht gerade dazu motiviert, die so wichigen Begründungen mitzuliefern.
Ich weiß, dass ich mit diesen Ansichten bei vielen Lesern auf Widerspruch stoßen werde. Das ist unvermeidlich, denn die meisten programmierenden Zeitgenossen sind ja ein Produkt der "fundamentalistischen Erziehung" und mangels anderer Erfahrungen gar nicht imstande (oder bereit?), ihr Programmier-Weltbild in Frage zu stellen. Sei's drum.
Immerhin bin ich mit meiner Ansicht nicht ganz alleine. So las ich diese Tage in einer Tutorialreihe über OpenGL: "Leider ist die Bibliothek in C++ geschrieben und verhält sich deshalb etwas sperrig, wenn sie eingebunden werden soll." Gemeint war die Ergänzungsbibliothek GML. Bei den Recherchen über Ergänzungsbibliotheken stieß ich mehrfach auf Hinweise wie (sinngemäß): "Da die Bibliothek in elementarem C geschrieben ist, gibt es keinerlei Probleme mit der Einbindung."
"Einbindung", das ist das entscheidende Stichwort. OGRE lässt sich nicht einbinden, sondern OGRE bindet ein. OGRE passt sich nicht an vorhandene Programmstrukturen an, sondern OGRE verlangt Anpassung. Kreativlosen Programmieren kommt das natürlich entgegen.
Beim Schreiben dieses Artikels wurde mir erst klar, wieviel Frust sich angestaut hatte. Die Abkehr von OGRE ist so etwas wie die Befreiung von einem Monster. Erst wenn man gegen die Schwerfälligkeit dieses monströsen Gebildes angerannt ist, wird einem die Leichtigkeit bewusst, mit der man z.B. auf direktem Wege unter OpenGL arbeitet.
Nachtrag: Nachdem ich nun alles, was mit OGRE zu tun hatte, von der Platte geräumt habe (der Computer ist regelrecht leicht geworden und berührt kaum noch den Boden unter'm Schreibtisch), und nachdem ich den ganzen ausgedruckten Papierkram entsorgt habe, tauchte doch wieder die Frage auf, ob es richtig war, so endgültig mit OGRE zu brechen. Ich hab's auch mit anderen diskutiert, und dabei ist mir klar geworden: Ja, es war richtig. OGRE ist für meine Zwecke schlichtweg nicht geeignet.
Zwar ist OGRE universeller ausgelegt als z.B. Spiele-Engines wie "Unity" oder "UDK", mit denen sich Spiele eines bestimmten Typs am Fließband "produzieren" lassen, doch hatten die OGRE-Entwickler ebenfalls ihre bestimmten Vorstellungen, und in dieser Richtung wurde die Kapselung der elementaren Funktionen vorgenommen. Ein solches vorgefertigtes Konzept passt aber oft nicht zum speziellen Vorhaben, und dann kommt es darauf an, ob man das Konzept aufbrechen kann und wenn ja, mit wieviel Aufwand dieses verbunden ist.
Die erste Frage kann ich im Falle von OGRE nicht schlüssig beantworten; dazu bin ich mit meinem Verständnis noch nicht weit genug vorgedrungen. Ich vermute, dass es wahrscheinlich Wege gibt. Doch die zweite Frage führt zu einer ernüchternden Erkenntnis: Dieses Monster aufzubrechen, wird so viel Aufwand und Kraft erfordern, dass keine nennenswerten Vorteile durch seine Verwendung zu erwarten sind.
Die Leichtigkeit, von der ich weiter oben sprach, wurde mir erneut bewusst, als ich diese Tage "FreeImage" installierte, um unter OpenGL Texturen zu laden. An einem Nachmittag war alles geschehen: die Installation unter Codeblocks, die Kapselung in einer Texturklasse, die Einbindung dieser Klasse in die Applikationsklasse. Spielt es da eine Rolle, ob FreeImage in einfachem C oder in C++ programmiert ist? Der Unterschied zu OGRE wurde so richtig deutlich. FreeImage lässt sich problemlos in die eigenen Programmstrukturen einbinden - genau so, wie man es von einer Hilfslibrary erwarten sollte. OGRE lässt sowas nicht zu, sondern verlangt vielmehr, dass man sich gefälligst in die OGRE-Strukturen eingliedert - ohne Rücksicht auf eigene Programmierstile und bereits existierende, eigene Programmstrukturen. Wie toll könnte OGRE sein, wenn man es nicht zu einem alles beherrschen wollenden Monster gemästet hätte. Man muss einfach mal wieder erleben, wie flott z.B. das Kompilieren ohne das Monster von der Hand geht. Leichtigkeit eben.
Abschließend noch ein kurzer Hinweis auf jene Benutzer von OGRE, die zufrieden bis begeistert sind. In der Tat, davon gibt es eine Menge, und einige von ihnen haben mit OGRE etwas Tolles zustande gebracht. Doch wenn man sich die Screenshots oder Videos von ihren Produkten anschaut, dann wimmelt es nur so von teuflisch aussehenden Fratzen mit martialischen Gesten in düsteren Kellergewölben. Sowas kann man offenbar mit OGRE ganz gut hinbekommen. Aber das ist nicht die Welt von Tux - und auch nicht meine. Und so überlasse ich das Monster den Programmierern von Monstern, die keine Probleme damit haben, sich von einem Monster beherrschen zu lassen.
Nachtrag 2: Diese Tage erhielt ich eine EMail von einem Leser, der mich (etwas aufgebracht) über die Unterschiede zwischen einer Library und einer Engine aufklärte. Fand ich gut, denn ich hatte beide Begriffe eher synonym verwendet. Also, da ich noch lernfähig bin: OGRE ist eine Engine, und zwar deshalb, weil sie den eigentlichen Programmrahmen abdeckt und damit - zu Recht - den Stil und die prinzielle Richtung vorgibt. Ok, einverstanden. Und wenn mir weder Stil noch Richtung passen, habe ich ja alle Freiheit, OGRE nicht zu verwenden, eine Freiheit, die ich genutzt habe.
Und dennoch: Wieso kann eine Software mit dem Anspruch einer allumfassenden Engine auftreten, wenn sie gar nicht alles abdeckt, sondern sich vielmehr auf einen Teilbereich wie die Grafik beschränkt? Und wie verträgt sich das mit anderen Softwarepaketen (Libraries oder Engines oder was auch immer), die einen anderen Teilbereich abdecken (z.B. Physik) und von der Leistung her ähnliche Ansprüche geltend machen könnten - mit sehr unangenehmen Auswirkungen? Nein, nein, so einfach kann man sich nicht den Anzug einer Engine überziehen.
Wenn ich mir die Unterscheidung des Lesers konsequent zu eigen mache, dann muss ich feststellen, dass es Software gibt, die zu Recht den Status einer Engine verdient: Unity z.B. Aber das sind eigenständige Programme, die wirklich alles unter einen Hut bringen. Sie wenden sich auch nicht an Programmierer. Also ist OGRE demnach doch wieder keine Engine, sondern "nur" eine Library?