Eine 3D-Nutzungsschnittstelle für die GITK
Transcrição
Eine 3D-Nutzungsschnittstelle für die GITK
1 Selbständigkeitserklärung Hiermit erkläre ich, dass ich diese Arbeit selbständig angefertigt und keine anderen Hilfsmittel als die im Literaturverzeichnis genannten verwendet habe. Alle Stellen, an denen Wortlaut oder Sinn anderen Werken entnommen wurden, sind unter Angabe der Quellen kenntlich gemacht. Leipzig, den 06. August 2004 Inhaltsverzeichnis 1 2 Einleitung 9 1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2 Zielstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3 Gliederung und Zielgruppe . . . . . . . . . . . . . . . . . . . . . 10 Grundlagen 12 2.1 Generalized Interface Toolkit . . . . . . . . . . . . . . . . . . . . 12 2.2 Vergleich zwischen 2D- und 3D-Nutzungsschnittstellen . . . . . . 14 2.2.1 2D-Nutzungsschnittstellen . . . . . . . . . . . . . . . . . 15 2.2.2 3D-Nutzungsschnittstellen . . . . . . . . . . . . . . . . . 18 Widgets in 2D-Nutzungsschnittstellen . . . . . . . . . . . . . . . 20 2.3.1 GUI-Toolkits . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.2 Skinnable GUIs . . . . . . . . . . . . . . . . . . . . . . . 23 2.4 Interaktionstechniken in 3D-Nutzungsschnittstellen . . . . . . . . 24 2.5 Widgets in 3D-Nutzungsschnittstellen . . . . . . . . . . . . . . . 27 2.5.1 Widgets zur direkten 3D-Objektinteraktion . . . . . . . . 27 2.5.2 Widgets zur Manipulation der 3D-Szene . . . . . . . . . . 28 2.5.3 Widgets zur Exploration und Visualisierung . . . . . . . . 29 2.5.4 Widgets zur Anwendungskontrolle . . . . . . . . . . . . . 32 Standard-3D-APIs . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.3 2.6 2 INHALTSVERZEICHNIS 3 2.6.1 OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.6.2 Direct3D . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.6.3 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Grundlagen der 3D-Grafik-Programmierung . . . . . . . . . . . . 35 2.7.1 Modellierung . . . . . . . . . . . . . . . . . . . . . . . . 35 2.7.2 Beleuchtung . . . . . . . . . . . . . . . . . . . . . . . . 36 2.7.3 Material . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.7.4 Abbildung . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.7.5 Arbeitsweise von OpenGL . . . . . . . . . . . . . . . . . 38 3D-Toolkits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 2.8.1 Open Inventor . . . . . . . . . . . . . . . . . . . . . . . . 40 2.8.2 Java3D . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Distributed Scene Graph APIs . . . . . . . . . . . . . . . . . . . 42 2.9.1 Studierstube . . . . . . . . . . . . . . . . . . . . . . . . 42 2.9.2 SUN Looking Glass . . . . . . . . . . . . . . . . . . . . 43 2.10 Stereoskopische Verfahren . . . . . . . . . . . . . . . . . . . . . 44 2.10.1 Stereogramm . . . . . . . . . . . . . . . . . . . . . . . . 44 2.10.2 Pulfrich-Effekt . . . . . . . . . . . . . . . . . . . . . . . 45 2.10.3 Interlaced Verfahren / LCD-Shutterbrillen . . . . . . . . . 46 2.10.4 Head-Mounted Displays (HMD) . . . . . . . . . . . . . . 46 2.10.5 3D-Bildschirme . . . . . . . . . . . . . . . . . . . . . . . 46 2.10.6 Bildprojektion mit Polarisationsfilter . . . . . . . . . . . . 47 2.10.7 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 2.11 3D-Eingabegeräte . . . . . . . . . . . . . . . . . . . . . . . . . . 48 2.11.1 Joystick . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 2.11.2 Spaceball . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.11.3 Tracker . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.7 2.8 2.9 INHALTSVERZEICHNIS 3 4 2.11.4 Datenhandschuh . . . . . . . . . . . . . . . . . . . . . . 50 2.11.5 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.12 3D-Umgebungen . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.13 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Open Inventor 53 3.1 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 3.2 Funktionsweise . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.3 Knoten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 3.3.1 Gruppen . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 3.3.2 Transformationen . . . . . . . . . . . . . . . . . . . . . . 60 3.3.3 Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . 61 3.3.4 Kamera und Licht . . . . . . . . . . . . . . . . . . . . . 62 3.3.5 Formen . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 3.3.6 ClipPlanes . . . . . . . . . . . . . . . . . . . . . . . . . 66 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 3.4.1 Dynamische Typinformationen . . . . . . . . . . . . . . . 67 3.4.2 Master-Slave-Verbindung . . . . . . . . . . . . . . . . . 67 3.4.3 Selektive Behandlung . . . . . . . . . . . . . . . . . . . 67 3.5 Engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.6 Speicherverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . 69 3.7 Pfade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 3.8 Dateiformat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 3.9 Schematische Darstellung von Szenengraphen . . . . . . . . . . . 73 3.10 Komponentenbibliothek . . . . . . . . . . . . . . . . . . . . . . . 74 3.11 Ereignisverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . 75 3.12 Erweiterung von Open Inventor . . . . . . . . . . . . . . . . . . . 76 3.13 Leistungsvergleich zwischen OpenGL und Open Inventor . . . . . 77 3.14 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 3.4 INHALTSVERZEICHNIS 4 5 Realisierung des Toolkits 79 4.1 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 4.2 Grundlegende Konzepte . . . . . . . . . . . . . . . . . . . . . . . 81 4.2.1 Szenengraph . . . . . . . . . . . . . . . . . . . . . . . . 82 4.2.2 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 4.2.3 Sensoren . . . . . . . . . . . . . . . . . . . . . . . . . . 83 4.2.4 Inventor-Dateien . . . . . . . . . . . . . . . . . . . . . . 84 4.3 Der WidgetController . . . . . . . . . . . . . . . . . . . . . . . . 86 4.4 Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.4.1 QbWidget . . . . . . . . . . . . . . . . . . . . . . . . . . 89 4.4.2 QbLabel . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.4.3 QbButton . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.4.4 QbPushButton und QbToggleButton . . . . . . . . . . . . 93 4.4.5 QbRadioButton . . . . . . . . . . . . . . . . . . . . . . . 94 4.4.6 QbSpacer . . . . . . . . . . . . . . . . . . . . . . . . . . 94 4.4.7 QbLineEdit . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.4.8 QbBox . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Hilfskomponenten . . . . . . . . . . . . . . . . . . . . . . . . . . 97 4.5.1 QbRadioGroup . . . . . . . . . . . . . . . . . . . . . . . 97 4.5.2 QbVec3fToCoordConnection . . . . . . . . . . . . . . . . 97 4.5.3 QbLine . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.5.4 QbFieldSensorList . . . . . . . . . . . . . . . . . . . . . 99 4.5.5 QbPerspectiveCamera . . . . . . . . . . . . . . . . . . . 99 4.5.6 QbExaminerViewer . . . . . . . . . . . . . . . . . . . . . 99 4.5.7 QbConfigFile . . . . . . . . . . . . . . . . . . . . . . . . 100 4.5.8 QbDotGraphOutput . . . . . . . . . . . . . . . . . . . . . 100 4.5 4.6 Entwicklungsstand und Perspektiven . . . . . . . . . . . . . . . . 102 INHALTSVERZEICHNIS 5 Umsetzung eines 3D-Renderers für GITK 5.1 5.2 5.3 6 6 103 GITK-Renderer . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 5.1.1 Prinzipielle Arbeitsweise . . . . . . . . . . . . . . . . . . 104 5.1.2 Webserver . . . . . . . . . . . . . . . . . . . . . . . . . . 105 5.1.3 Schnittstelle . . . . . . . . . . . . . . . . . . . . . . . . . 105 5.1.4 GITK-Widgets . . . . . . . . . . . . . . . . . . . . . . . 106 5.1.5 GITK-Klassen . . . . . . . . . . . . . . . . . . . . . . . 107 Implementation des 3D-Renderers . . . . . . . . . . . . . . . . . 108 5.2.1 C und C++ vereint . . . . . . . . . . . . . . . . . . . . . 108 5.2.2 Klassen des 3D-Renderers . . . . . . . . . . . . . . . . . 109 5.2.3 Implementierung der Renderer-Funktionen . . . . . . . . 110 5.2.4 Anzeige-Stile . . . . . . . . . . . . . . . . . . . . . . . . 113 5.2.5 Generierung des Dialogs . . . . . . . . . . . . . . . . . . 113 Benutzung des 3D-Renderers . . . . . . . . . . . . . . . . . . . . 115 5.3.1 Bedienung . . . . . . . . . . . . . . . . . . . . . . . . . 116 5.3.2 Action Spaces . . . . . . . . . . . . . . . . . . . . . . . . 118 5.4 Entwicklungsstand . . . . . . . . . . . . . . . . . . . . . . . . . 119 5.5 Einsatz des 3D-Renderers . . . . . . . . . . . . . . . . . . . . . . 119 Zusammenfassung 121 A Szenengraph-Symbole 123 B Open Inventor Klassenhierarchie 125 C Quellcode 128 C.1 Beispiel-Dialog im Inventor-Dateiformat . . . . . . . . . . . . . . 128 C.2 Beispiel-Dialog in GIML . . . . . . . . . . . . . . . . . . . . . . 129 D Glossar 131 E Inhalt der beiliegenden CD 132 Abbildungsverzeichnis 2.1 Verarbeitung eines Dialoges . . . . . . . . . . . . . . . . . . . . 13 2.2 Fischaugen-Menü . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.3 ZoomWorld-Portal . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4 Verschiedene Winamp-Skins . . . . . . . . . . . . . . . . . . . . 24 2.5 Manipulator aus dem Open Inventor Toolkit . . . . . . . . . . . . 28 2.6 Licht-Manipulator aus dem Open Inventor Toolkit . . . . . . . . . 29 2.7 Skizze einer Drop-Down-Liste mit Fisheye-Technik . . . . . . . . 31 2.8 Drahtgittermodell einer Kugel . . . . . . . . . . . . . . . . . . . 36 2.9 Koordinatensystem im 3D-Raum . . . . . . . . . . . . . . . . . . 36 2.10 Perspektivische und orthogonale Projektion . . . . . . . . . . . . 38 2.11 Beispiel eines Studierstube-Projekts . . . . . . . . . . . . . . . . 43 2.12 SUN Looking Glass . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.13 SaabTech AddVisor 150 . . . . . . . . . . . . . . . . . . . . . . 46 2.14 Joysticks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.15 3Dconnexion Spaceball 5000 . . . . . . . . . . . . . . . . . . . . 49 2.16 SketchAR und Cyberstilo . . . . . . . . . . . . . . . . . . . . . . 50 2.17 Datenhandschuh von 5DT . . . . . . . . . . . . . . . . . . . . . 51 3.1 Architektur von Open Inventor . . . . . . . . . . . . . . . . . . . 54 3.2 Lokale und globale Koordinaten . . . . . . . . . . . . . . . . . . 61 7 ABBILDUNGSVERZEICHNIS 8 3.3 Zwei Kameras in einer Szene . . . . . . . . . . . . . . . . . . . . 63 3.4 Schematische Darstellung einer Szene . . . . . . . . . . . . . . . 74 3.5 Ereignis-Verarbeitung in Open Inventor . . . . . . . . . . . . . . 75 4.1 Architektur von Qb . . . . . . . . . . . . . . . . . . . . . . . . . 81 4.2 Sensoren als Schnittstelle zwischen Dialog und Anwendung . . . 84 4.3 Skaliertes und angepasstes Modell eines PushButtons . . . . . . . 85 4.4 Modell-Einbindung in Qb . . . . . . . . . . . . . . . . . . . . . . 87 4.5 Klassenhierarchie von Qb . . . . . . . . . . . . . . . . . . . . . . 89 4.6 Koordinaten eines Widgets . . . . . . . . . . . . . . . . . . . . . 90 4.7 Verbindungen zwischen Widgets . . . . . . . . . . . . . . . . . . 90 4.8 Zustandsdiagramm eines ToggleButton . . . . . . . . . . . . . . . 93 4.9 Modelle von Push- und ToggleButton (default-Style) . . . . . . . 94 4.10 Eine Gruppe von RadioButtons (default-Style) . . . . . . . . . . . 94 4.11 Aufbau des QbLineEdit . . . . . . . . . . . . . . . . . . . . . . . 95 4.12 Dialog mit Boxen (Default-Style) . . . . . . . . . . . . . . . . . . 97 4.13 Szenengraph eines Dialogs, visualisiert durch GraphViz . . . . . . 101 5.1 GITK-Widgets – abstrakte Schnittstellenobjekte . . . . . . . . . . 107 5.2 Klassendiagramm des 3D-Renderers . . . . . . . . . . . . . . . . 110 5.3 Szenengraph des 3D-Renderers . . . . . . . . . . . . . . . . . . . 111 5.4 Grundgerüst eines Dialogs . . . . . . . . . . . . . . . . . . . . . 114 5.5 Gruppierung von Widgets durch QbBox . . . . . . . . . . . . . . 114 5.6 GITK-Anwendung HelloUser mit Renderer für GTK+ . . . . . . 116 5.7 GITK-Anwendung HelloUser mit dem 3D-Renderer 5.8 HelloUser-Programm im Aqua-Stil . . . . . . . . . . . . . . . . . 120 . . . . . . . 117 Kapitel 1 Einleitung 1.1 Motivation GITK ist eine von Stefan Kost im Rahmen seiner Dissertation entstandene Architektur für anpassbare Nutzungsschnittstellen von Anwendungsprogrammen. Spezielle Module – Renderer – generieren dabei anhand einer abstrakten Schnittstellenbeschreibung die Nutzungsschnittstelle dynamisch zur Laufzeit. So kann ein bereits entwickeltes Programm auf verschiedenste Weisen repräsentiert werden. Auf der Suche nach möglichen Präsentationsarten kam die Idee für eine dreidimensionale Darstellung einer GITK-Anwendung. Ein Renderer, der dreidimensionale Nutzungsschnittstellen generiert, würde nicht nur für eine sehr außergewöhnliche Darstellung („Eyecandy”) sorgen – er würde auch eine Basis für weitere Forschung an dieser Art von Nutzungsschnittstelle bieten. In der ersten Hälfte der 90er Jahre wurde verstärkt erforscht, inwieweit die Nutzung einer Tiefenachse Vorteile bei der Nutzerfreundlichkeit (Usability) bringt. Neue Ansätze zur Darstellung und Interaktion entstanden – aber ein Großteil der Projekte wurde schnell wieder aufgegeben. Gründe waren zu spezialisierte Ansätze ohne Verallgemeinerungspotential oder zu hohe Hardwareanforderungen. Mittlerweile hat sich auf dem Hardwaremarkt einiges getan – selbst komplexe 3DDarstellungen bereiten aktuellen PCs wenig Probleme. Dreidimensionale Darstellung ist heute besonders bei Computerspielen äußerst beliebt. Umso mehr macht die Forschung an 3D-Nutzungsschnittstellen Sinn, denn ein Ende des Trends ist nicht abzusehen. Gefragt sind nun konkrete Anwendungen solcher Schnittstellen, 9 KAPITEL 1. EINLEITUNG 10 welche über das Forschungsstadium hinaus gehen und klare Vorteile gegenüber den herkömmlichen grafischen Nutzungsschnittstellen bieten. Mit der GITK-Architektur existiert nun ein konkretes Anwendungsgebiet für eine 3D-Nutzungsschnittstelle. Da die Architektur sehr allgemein gehalten ist, kann prinzipiell jede beliebige GITK-Anwendung mit dem 3D-Renderer bedient werden. So können neue Ansätze für die Interaktion und Informationsdarstellung in dreidimensionalen Umgebungen sehr leicht in der Praxis getestet werden. Die Möglichkeit der Verwendung mehrerer verschiedener Nutzungsschnittstellen mit ein und derselben Anwendung hätte zudem für die Usability-Forschung den Vorteil, dass die Nutzungsschnittstellen hinsichtlich ihrer Nutzerfreundlichkeit 1:1 direkt miteinander verglichen werden können 1.2 Zielstellung Im Rahmen dieser Arbeit soll der Prototyp eines Moduls für die GITK-Architektur entwickelt werden, welches eine dreidimensionale Nutzungsschnittstelle generiert. Die dreidimensionale Darstellung soll dabei auf OpenGL basieren. Wichtigstes Ziel dabei ist, dass alle Funktionen eines GITK-Renderers implementiert werden, so dass eine GITK-Anwendung damit bedienbar ist. Design-Aspekte werden deshalb als zweitrangig betrachtet. Für diesen 3D-Renderer soll zunächst ein Prototyp eines 3D-User-Interface-Toolkits entstehen, mit dessen Hilfe sich allgemein dreidimensionale Nutzungsschnittstellen entwickeln lassen. Dieses sollte möglichst so beschaffen sein, dass es auch eine Verwendung außerhalb des GITK-Umfeldes ermöglicht, sowie die nötige Flexibilität für weitere Entwicklung und Forschung bieten. Diese Arbeit soll keine psychologische Untersuchung zur Usability von Nutzungsschnittstellen darstellen, sondern einen eher praktischen Ansatz verfolgen. Dennoch wird auf die nötigen Grundlagen dieses Themas eingegangen, da es keinesfalls ignoriert werden darf. 1.3 Gliederung und Zielgruppe Die Arbeit gliedert sich in vier große Abschnitte. In Kapitel 2 werden zunächst die nötigen Grundlagen und ein Technologieüberblick vermittelt, was dem Verständnis KAPITEL 1. EINLEITUNG 11 der Thematik dieser Arbeit dienen soll. Das Kapitel ist besonders für Leser interessant, die sich noch nicht mit Nutzungsschnittstellen und 3D-Programmierung befasst haben. Kapitel 3 widmet sich ganz dem Thema Open Inventor, da die Implementation von Toolkit und Renderer fast vollständig auf dieser Bibliothek basiert. Durch dieses Kapitel sind all diejenigen angesprochen, die Programmiererfahrung besitzen und sich für 3D-Programmierung mit einer szenengraphbasierten 3D-API interessieren. Kapitel 4 beschreibt ausführlich die Konzeption und Implementation des 3D-UserInterface-Toolkits. Es setzt die Kenntnis der Arbeitsweise mit szenengraphbasierten APIs für ein tieferes Verständnis voraus. Interessant ist das Kapitel für Entwickler, die das UI-Toolkit selbst verwenden möchten oder Inspiration für eine eigene Umsetzung suchen. Kapitel 5 beschäftigt sich mit der eigentlichen Entwicklung des 3D-Renderers für die GITK-Architektur. Es ist hauptsächlich für Programmierer von Interesse, die eigene Renderer für die GITK-Architektur entwickeln wollen. Kapitel 2 Grundlagen In diesem Kapitel werden nach einer kurzen Einführung in die GITK-Architektur die Unterschiede zwischen 2D- und 3D-Nutzungsschnittstellen anhand ihrer Eigenschaften und Möglichkeiten aufgeführt. Dem folgen die nötigen Grundlagen zur Programmierung von 3D-Anwendungen. Am Ende wird ein Überblick der Technologie gegeben, die im 3D-Umfeld Verwendung findet. 2.1 Generalized Interface Toolkit Verschiedene Bedürfnisse und Fähigkeiten von Nutzern erfordern Anwendungen, die angepasst werden können. Meist ist die Nutzungsschnittstelle einer Anwendung fest und nur wenige Aspekte können angepasst werden. Seltener bieten Anwendungen verschiedene Nutzungsschnittstellen, zwischen denen sich der Anwender entscheiden kann. Der Entwicklungsaufwand ist jedoch hoch, jede Nutzungsschnittstelle muss separat an die Anwendung angepasst werden. Das Generalized Interface Toolkit (GITK) bietet eine Infrastruktur für hochgradig anpassbare Anwendungen. Dabei besitzen Anwendungen lediglich abstrakte Schnittstellenbeschreibungen – die Nutzungsschnittstellen werden dynamisch zur Laufzeit1 generiert. Auf die Art wird eine enorme Flexibilität erreicht, ohne dass sich der Aufwand bei der Anwendungsentwicklung ins Unermessliche steigert. Die Schnittstellenbeschreibung erfolgt in GIML (Generalized Interface Markup Language) – einer XML-Sprache. Durch sie definiert die Anwendung Dialoge, 1 Das Gegenteil dazu ist statisch zur Entwicklungszeit – die Erscheinung der Nutzungsschnittstelle steht dabei bereits vor Programmstart fest. 12 KAPITEL 2. GRUNDLAGEN 13 über die ein Nutzer oder auch ein nutzendes Programm mit der Anwendung kommuniziert. Somit ist ein Anwendungsdialog ein Dokument, welches durch GITK verarbeitet wird. Präferenzen eines Nutzers werden in Profilen beschrieben. Technisch sind die Profile durch XSL-Dokumente realisiert, durch die Dialoge transformiert werden. Abbildung 2.1: Verarbeitung eines Dialoges Die Verarbeitungskette eines abstrakten GIML-Dialoges besteht aus den Schichten Anwendungslogik, Anpassung und Präsentation (siehe Abbildung 2.1 [KOST04]). Diese Arbeit behandelt vorwiegend die Präsentationsschicht. In ihr wird eine Nutzungsschnittstelle für eine bestimmte Domäne generiert. Eine Domäne ist eine Arbeitsumgebung mit speziellen Möglichkeiten zur Ein- und Ausgabe. Eine Domäne könnte zum Beispiel ausschließlich auf Text oder akustische Signale beschränkt sein. Es könnte sich jedoch genauso gut um eine Kommunikation mit einem externen Programm handeln. Die konkrete Verarbeitung der Dialoge geschieht in Rendering Plugins. In diesen Modulen wird die Nutzungsschnittstelle für eine bestimmte Domäne generiert. Dieser Vorgang ist domänenabhängig und kann nicht weiter verallgemeinert werden. KAPITEL 2. GRUNDLAGEN 14 Ein 3D-Renderer für die GITK-Architektur generiert aus einer abstrakten Schnittstellenbeschreibung eine Nutzungsschnittstelle für eine Domäne, in der eine Präsentation mit dreidimensionaler Grafikdarstellung möglich ist. Neben diesem Modul existieren Renderer für die Domänen Text, 2D-Grafik und Telefon. Der TextRenderer ermöglicht durch Sprachausgabe und einer Braille-Zeile die Bedienung durch Blinde. Ein geplanter Renderer für die Domäne HTTP würde eine Programminteraktion in einer Browserumgebung ermöglichen. Die Nutzung der GITK-Architektur bei der Programmentwicklung bringt also entscheidende Vorteile, wenn es darauf ankommt, Programminteraktion in möglichst vielen unterschiedlichen Domänen zu ermöglichen – unter Berücksichtigung von Nutzerpräferenzen. Voraussetzung dafür ist allerdings, dass die Anwendung eine derartige Verallgemeinerung zulässt. Hauptanwendungsgebiet von GITK werden am ehesten Informations- und Administrationssysteme sein, bei denen Verfügbarkeit in allen Lebenslagen nötig ist. 2.2 Vergleich zwischen 2D- und 3D-Nutzungsschnittstellen Eine Nutzungsschnittstelle (engl. user interface, UI) bezeichnet allgemein eine Möglichkeit der Nutzung eines Geräts oder Computerprogramms. So besitzt eine HiFiAnlage als Nutzungsschnittstelle eine Fernbedienung sowie die Tasten und Anzeigen direkt am Gerät. In dieser Arbeit ist mit Nutzungsschnittstelle der Vermittler zwischen Anwendungsprogramm und Anwender gemeint. Mit ihr richtet ein Programm Ausgaben an den Nutzer – und umgekehrt richtet ein Nutzer mit ihrer Hilfe Eingaben an ein Programm. Eine Nutzungsschnittstelle ist, von ihrer Erscheinung abgesehen, durch ihre Usability geprägt. Nach ISO-Norm 9241 bedeutet die Usability eines Produktes das Ausmaß, in dem es von einem Benutzer verwendet werden kann, um bestimmte Ziele in einem bestimmten Kontext effektiv, effizient und zufriedenstellend zu erreichen. Für Nutzungsschnittstellen ergeben sich folgende Merkmale: • Effizienz: Wie viel Zeit benötigt der Nutzer für das Ausführen einer bestimmten Aufgabe? • Erlernbarkeit, Intuitivität: Wieviel Zeit benötigt ein Nutzer für einen effizienten Umgang? KAPITEL 2. GRUNDLAGEN 15 • Ergonomie: Wie ist der Nutzer durch die Arbeit psychisch sowohl physisch belastet? Wie bereits in der Einleitung angesprochen, unterstützt die GITK-Architektur verschiedene Arten von Nutzungsschnittstellen. Diese Arbeit beschränkt sich jedoch ausschließlich auf grafische Nutzungsschnittstellen 2 . Die Klasse der grafischen Oberflächen wird unterteilt in zweidimensionale und dreidimensionale GUIs. Deren Eigenschaften sowie Vor- und Nachteile werden nun in den folgenden Unterabschnitten verglichen. 2.2.1 2D-Nutzungsschnittstellen Die heute üblichen grafischen Nutzungsschnittstellen wie z.B. bei GNOME, KDE oder Microsoft Windows sind durch die sogenannte Desktop-Metapher (dt. Schreibtisch-Metapher) gekennzeichnet, welche bis auf Entwicklungen Ende der 70er Jahre zurückzuführen ist. Erst durch den Apple Macintosh Computer wurden GUIs in den 80er Jahren kommerziell erfolgreich. Durch Verwendung geeigneter Metaphern wie Schreibtisch, Papierkorb oder Ordner waren diese Systeme weitaus intuitiver als ihre textbasierten Vorgänger. Die Desktop-Metapher beeinhaltet die point-and-click-Metapher (Zeigen und Klicken) sowie das WIMP-Konzept (Windows, Icons, Menus, Pointing Device), und hat sich bis heute kaum verändert. Wichtigstes Merkmal dieser Nutzungsschnittstellen ist ihr Aufbau aus grafischen Interaktionselementen – den sogenannten Widgets. Diese erlauben einerseits die Interaktion mit Hilfe eines Zeigegeräts (Maus) oder einer Tastatur und andererseits gliedern sie als Container das Layout, indem sie andere Widgets aufnehmen. Das Design von Widgets orientiert sich oft an realen Objekten wie z.B. Knöpfen oder Schiebereglern, wodurch sie sich selbst erklären. Typische Widgets sind in Abschnitt 2.3 aufgeführt. Die klassischen graphischen Oberflächen sind gemeinhin flach, also zweidimensional. Es gibt zwar eine gewisse Tiefenachse, durch die Verdeckungen zwischen Widgets realisiert sind – der Blickpunkt ist jedoch stets fest. Widgets wirken in modernen grafischen Oberflächen durch Schattierung sehr plastisch – nur dreidimensional sind sie nicht, denn aus einem anderen Winkel kann man sie nicht betrachten. 2 engl. graphical user interface – kurz GUI, umgangssprachlich auch grafische Oberfläche KAPITEL 2. GRUNDLAGEN 16 Einige GUIs wie zum Beispiel die von Apple MacOS X [@MACOS] oder ParaGUI [@PARA] nutzen 3D-Hardware für die Darstellung. Bei der Technik Exposé von Mac OS X bewegen sich inaktive Fenster sogar in den Hintergrund, wobei sie sich perspektivisch verkleinern. Dennoch wird hier keine Tiefenachse ausgenutzt, die Bedienung orientiert sich an den klassischen Konzepten. Auch wenn zweidimensionale GUIs heutzutage der Standard schlechthin sind und die meisten Menschen effektiv damit arbeiten können, bringt die Beschränkung auf eine flache Darstellungsebene einen wesentlichen Nachteil mit sich: Ein ständiger Platzmangel, dem der Nutzer ausgesetzt ist. Um dennoch alle Informationen zugänglich zu machen, werden folgende Techniken verwendet: • Überlappung: Widgets (v.a. Fenster) können sich gegenseitig überlappen. Der Inhalt eines verdeckten Widgets ist nicht sichtbar, es sei denn die verdeckenden Widgets sind teiltransparent. • Scrolling (Verschiebung): Scrollbalken an den Rändern eines Widgets verschieben den Inhalt horizontal oder vertikal. Spezielle Räder an Mäusen erlauben ein sehr effektives Scrolling. • Zooming: Die Informationen werden einfach verkleinert. So sieht man sehr viel auf einen Blick, jedoch strengt dies die Augen an. Es kann nur in einigen Fällen gezoomt werden, und dann meist in festen Stufen. Exposé des Mac OS X kann dies stufenlos mit sehr guter Qualität. • Mehrere Seiten: Durch beispielsweise Registerkarten ist ein umfangreicher Dialog aufgeteilt, wobei in der Regel Informationen auf einer Seite gruppiert sind. Bei vielen Unterteilungen muss für die Bedienelemente zum Umschalten der Seiten zusätzlich gescrollt werden. • Baum-Ansichten: Geschachtelte Informationen werden wie z.B. im Windows-Explorer in einem Baum dargestellt, bei dem die Inhalte von Knoten wahlweise ein- oder ausgeblendet werden. Für gewöhnlich sind die meisten Knoten zunächst ausgeblendet. • Virtuelle Desktops: Die Arbeitsfläche ist größer, als das Display anzeigen kann. Entweder wird auf einer sehr großen Arbeitsfläche gescrollt, oder es wird zwischen Teilen von ihr umgeschaltet. KAPITEL 2. GRUNDLAGEN 17 • Große Displays: Durch Nutzung sehr großer oder mehrerer Displays kann der Platzmangel in einem gewissen Maße behoben werden. Nur muss ab einer bestimmten Größe das Display oder die Anordnung mehrerer Displays gewölbt sein, so dass der Nutzer stets einen möglichst senkrechten Blick hat. Diese Techniken sind nur solange effektiv, bis die Informationsmenge zu groß wird. Dann müssen zwangsläufig Informationen ausgeblendet werden, sebst wenn diese momentan benötigt werden. Leicht verliert der Nutzer hierbei den Überblick, er ist gezwungen, wieder und wieder nach seinen Informationen zu suchen. Das ist zeitaufwändig und lenkt von der Arbeit ab. Innovative Techniken versuchen, den Informationsgehalt zu erhöhen. Ein Weg ist, für die tatsächlich genutzten Informationen Platz zu schaffen. Sogenannte personalisierte Menüs blenden Einträge aus, die der Anwender nur sehr selten nutzt. Nachteilig ist dabei, dass die Suche nach einem Eintrag eher länger dauert, da sich keine Gewöhnung an die Anordnung einstellt. Fischaugen-Menüs zeigen Informationen im Zentrum der Betrachtung vergrößert an. Wichtige Informationen oder zumindest die Einträge nahe dem Mauscursor sind gut lesbar, der Rest ist so klein dass er gerade noch erkennbar ist. Hierbei entgehen dem Nutzer keine Einträge, die Anordnung bleibt stets gleich. (Abbildung2.2 [@FISH]) Abbildung 2.2: Fischaugen-Menü Sogenannte Post-WIMP-GUIs beschreiten gänzlich neue Wege. Einen interessanten Ansatz stellen Zooming User Interfaces (ZUI) dar. Hierarchische Informationen in Baumstrukturen (z.B. Webseiten, Dateisysteme) werden sind hier im wahrsten Sinne des Wortes geschachtelt dargestellt: Die Unterknoten eines Knotens sind stark verkleinert sichtbar. Um ihren Inhalt detailliert zu erkennen, zoomt der Anwender stufenlos hinein. Zu einer übergeordneten Information gelangt man durch KAPITEL 2. GRUNDLAGEN 18 Herauszoomen. Hier stellt sich die Frage, ob das überhaupt noch zweidimensional ist, denn eine Tiefe gibt es hier schon. Da die Metapher „vergrößern” lautet, und ansonsten keine 3D-Elemente enthalten sind, ist es wohl korrekt, solche ZoomingUIs als 2D-Nutzungsschnittstelle zu bezeichnen. Das Projekt Piccolo (früher „Jazz”) der University of Maryland ist ein Framework für die Entwicklung solcher ZUIs in Java oder C# [@PICC]. Zooming Interfaces werden ebenfalls in [RASK01] in Kapitel 6 unter der Bezeichnung ZoomWorld vorgestellt. Abbildung 2.3 zeigt zum Beispiel ein ZoomWorld-Portal für das World Wide Web. In die Rubriken muss man hineinzoomen, um die Inhalte zu offenbaren. Abbildung 2.3: ZoomWorld-Portal 2.2.2 3D-Nutzungsschnittstellen Im Gegensatz zu 2D-GUIs befinden sich die Interaktionsobjekte bei 3D-GUIs in einem virtuellen Raum. Es existiert also eine Tiefenachse, welche neben der Darstellung dreidimensionaler Dokumente (z.B. Objekte aus CAD-Anwendungen) auch völlig neue Ansätze für die Interaktion und Informationsdarstellung möglich macht. Nutzung des Raums Die perspektivische Darstellung sorgt für eine Verkürzung der Geometrie bei steigender Entfernung. Momentan nicht benötigte Informationen können weiter nach hinten verlagert werden, so dass sie zwar noch sichtbar, jedoch kleiner sind. Das entspricht dem Zooming bei 2D-GUIs, ist jedoch implizit stufenlos und intuitiv. KAPITEL 2. GRUNDLAGEN 19 Der 3D-Raum kann sehr große Dimensionen annehmen. Es kann zwar auch hier stets nur ein Ausschnitt gesehen werden, nur kann dieser theoretisch völlig frei gewählt werden. Der Nutzer kann sich vom Weiten erst einen Überblick verschaffen, um sich dann einem bestimmten Teil zuzuwenden. Diese Technik könnte weitaus effizienter als das Umschalten zwischen Fenstern sein, da das menschliche Gehirn bestimmte geometrische Objekte in einer Menge leicht erkennt. Das Konzept von Zooming-Interfaces kann hier technisch sehr leicht umgesetzt werden. Statt zu Zoomen, wäre hier die passende Metapher „betreten” oder „sich nähern”. Grafische Gestaltung Die Widgets in 3D-Nutzungsschnittstellen können eine räumliche Ausdehnung besitzen, die für eine bildschirmeffizientere Darstellung von Informationen und intuitivere Bedienung genutzt werden kann. So sind weitaus mehr Metaphern möglich als in klassischen 2D-GUIs. Nicht zuletzt wirken dreidimensional gestaltete Programmoberflächen optisch sehr attraktiv. Besonders verspielt wirkende Bedienelemente für Kinder oder im Corporate Design gehaltene Präsentationsanwendungen für Firmen können umsatzfördernd sein. Gerade dafür bietet eine 3D-Umgebung enorm viel Freiraum. Tiefenwahrnehmung Einen Zusatznutzen erhält die dreidimensionalen Darstellung bei Verwendung stereoskopischer Verfahren (siehe Abschnitt 2.10). Die Tiefe des Raumes wird plastisch vermittelt, Informationen im Vordergrund werden tatsächlich näher wahrgenommen. Dies kann die Orientierung immens erleichtern. Rechenleistung Die komplexere Darstellung von 3D-GUIs erfordert deutlich mehr Rechenleistung. Dieses Problem relativiert sich jedoch mit dem Vorhandensein von 3D-Hardware. Selbst preisgünstige Grafikhardware unterstützt heutzutage zumindest Direct3D (siehe Abschnitt 2.6.2). Zudem benötigen 3D-GUIs längst nicht so ausgefeilte grafische Raffinessen wie 3D-Spiele. KAPITEL 2. GRUNDLAGEN 20 2.3 Widgets in 2D-Nutzungsschnittstellen Der Begriff Widget bezeichnet ein Element der Nutzungsschnittstelle. Bei grafischen Nutzungsschnittstellen besitzen Widgets stets eine grafische Repräsentation, welche für gewöhnlich durch ihre Erscheinung auf die jeweilige Funktion schließen lässt. Bei WIMP-basierten 2D-GUIs haben sich im Laufe der Zeit einige Widgets so etabliert, dass ihr Aussehen von System zu System kaum voneinander abweicht. Die wichtigsten Vertreter sind [@WIKIP]: • Fenster: Verschiebbarer, rechteckiger Container für weitere Widgets, meist mit einer Fensterleiste am oberen Rand mit grundlegender Funktionalität wie Verschieben, Größe ändern oder Schließen. • Menü: Aufklappende Auswahlliste (Drop-Down), meist am oberen Rand eines Fensters. • Schaltfläche (Button): Ein einfacher Knopf zum Drücken, welcher eine Aktion auslöst. • Checkbox: Auswahlkästchen, die zu Gruppen zusammengefasst sein können – mehrere Möglichkeiten können dabei ausgewählt werden. • Radiobutton: Auswahlkästchen, die immer zu Gruppen zusammengefasst werden, nur eine Möglichkeit kann dabei ausgewählt werden. • Listenfeld (Listbox): Eine Liste zur Auswahl von Objekten (z.B. Begriffe). Mehrzeilig oft mit vertikaler Bildlaufleiste oder einzeilig mit zugehöriger Schaltfläche als so genanntes Drop-Down-Listenfeld. • Textfeld: Feld für einzeilige Ein- und Ausgabe von Zeichenketten. • Textarea: Bereich zur mehrzeiligen Ein- und Ausgabe von Zeichenketten. • Combobox: Ein Kombinationsfeld, zusammengestellt aus einem Textfeld und einer Dropdown Auswahlliste. • Bildlaufleiste (Scrollbar): Schieberegler zum Verschieben des Inhalts von Widgets. Optisch meist gleich sind Schieberegler zur Auswahl von Werten (Slider). KAPITEL 2. GRUNDLAGEN 21 • Tabs: Registerkarten für mehrseitige Dialogfenster oder mehrere Dokumente pro Fenster. Da diese Widgets einen Quasi-Standard bilden, finden sich Nutzer (zumindest was die Bedienung betrifft) sehr leicht in unbekannten grafischen Oberflächen zurecht. Besonders die Nutzung von Metaphern wie z.B. „drücken”, „schieben” oder „abhaken” vereinfachen den Lernprozess enorm. 2.3.1 GUI-Toolkits Für eine schnelle Entwicklung grafischer Oberflächen werden meist GUI-Toolkits eingesetzt. Solche Bibliotheken bieten neben einer großen Auswahl verschiedener Widgets auch Funktionalitäten für die Interaktion sowie eine Schnittstelle zum Fenstersystem. Durch die Nutzung solcher Toolkits wirken Programme optisch sowie hinsichtlich der Bedienung sehr einheitlich, besonders wenn auf dem selben Toolkit basieren. Andererseits besitzen solche Programme eine einheitliche Architektur, und lassen sich so beispielsweise nahtlos eine Desktop-Umgebung einbinden oder zentral hinsichtlich ihrer Erscheinung anpassen. Entwickler von GUI-Toolkits orientieren sich meist am Standard für das Design von Widgets – wirkliche Innovationen dabei sind die Ausnahme. Unterschiede finden sich eher in den Programmierkonzepten. Einige GUI-Toolkits werden nun kurz erläutert. GTK+ Das GIMP Toolkit wird unter anderem von der Desktop-Umgebung GNOME verwendet. So bietet es entsprechende Funktionalität zur nahtlosen Einbindung in den GNOME-Desktop. Die API ist zwar objektorientiert (durch GLib), nutzt jedoch nur gewöhnliche Datentypen der Sprache C. Durch die Bibliothek Pango unterstützt GTK+ die Internationalisierung (sprachliche Anpassung) von Programmen. Eine Schnittstelle zu ATK (Accessability Toolkit) ermöglicht unter anderem die Verwendung alternativer Eingabegeräte (z.B. für Blinde). KAPITEL 2. GRUNDLAGEN 22 Trolltech Qt Qt [@TROLL] ist ein C++ Anwendungs-Framework, das besonders auf eine plattformübergreifende Anwendungsentwicklung zielt. Eine Besonderheit dieser Bibliothek ist eine Erweiterung der normalen C++ Klassendeklaration, wodurch das Zusammenspiel einzelner GUI-Komponenten für den Entwickler vereinfacht wird. Dafür ist ein besonderer Präcompiler nötig – der Meta Object Compiler (moc). Qt wird u.a. von der Desktop-Umgebung KDE verwendet. Win32 Die API der Microsoft Windows-Plattform bietet bereits die wichtigsten Werkzeuge für grafische Oberflächen. So kann, ohne eine bestimmte Bibliothek vorauszusetzen, eine Anwendung grafisch dargestellt werden. Die API ist allerdings nicht objektorientiert. Borland VCL / CLX Die Entwicklungsumgebungen (IDEs) C++ Builder und Delphi nutzen die Visual Component Library u.a. für den Aufbau grafischer Oberflächen. Ihre Verwendung ist auf die Windows-Plattform beschränkt. So wird die Windows-API für die grundlegenden Widgets genutzt. Durch die vollständige Integration in die IDEs können grafische Anwendungen enorm schnell entwickelt werden (RAD – Rapid Application Development). Für eine plattformübergreifende Entwicklung bietet die CLX (Component Library for Cross-platform) zwar einen geringeren Funktionsumfang, kann dafür jedoch in der IDE Kylix unter Linux verwendet werden. Mit Kylix entwickelte Anwendungen nutzen übrigens Qt für die grafische Darstellung der Widgets. VCL und CLX sind objektorientiert. MFC Die Microsoft Foundation Classes bieten eine objektorientierte API für grafische Windows-Anwendungen. Diese Bibliothek wird vornehmlich von mit Microsoft Visual C++ entwickelten Anwendungen genutzt. MFC ist somit auf die Win32Plattform beschränkt, aber dennoch sehr verbreitet. KAPITEL 2. GRUNDLAGEN 23 2.3.2 Skinnable GUIs Anpassbare Programmoberflächen sind ein Trend, der sich schätzungsweise bis Anfang der 90er Jahre zurückverfolgen lässt. Durch den stetig steigenden Funktionsumfang von Software nahm der Wunsch von Anwendern zu, die Bedienung nach ihren Bedürfnissen anzupassen. So können z.B. die Elemente in Menüs und Symbolleisten verändert werden. Durch die Verwendung von GUI-Frameworks kann die optische Erscheinung der Widgets jedoch nur stark eingeschränkt verändert werden. Besonders bei Unterhaltungssoftware ist es jedoch wichtig, mehr als nur die Farben zu verändern oder Hintergrund-Bilder einblenden zu können. So werden gerade bei Multimedia-Anwendungen die üblichen Widget-Toolkits gar nicht mehr eingesetzt, sondern eigene Architekturen. Diese erlauben eine weitreichende Modifikation der grafischen Oberfläche. Ein Skin (dt. Haut) 3 bezeichnet im Allgemeinen die Menge aller Daten einer möglichen grafischen Erscheinung. Eine Anwendung, deren Nutzungsschnittstelle durch Skins verändert werden kann, ist skinnable. Ein sehr gutes Beispiel dafür ist die erfolgreiche Medienwiedergabe-Software Nullsoft Winamp [@WINAMP]. Ursprünglich wurde bei Winamp die grafische Repräsentation jedes Elements aus Bitmap-Dateien (einem Grafikformat) gelesen. So konnten die Elemente mit jeder beliebigen Grafiksoftware verändert werden. Ein kompletter Satz solcher Grafiken machte ein Skin aus. Schnell entstanden hunderte solcher Skins. Die künstlerische Freiheit ist allerdings durch die feste Position und Größe der Elemente eingeschränkt. Aktuelle Versionen von Winamp nutzen das ebenfalls durch Nullsoft entwickelte UI-Framework Wasabi. Die sogenannten „Modern Skins” werden dabei mittels einer XML-Sprache beschrieben und können völlig beliebige Formen annehmen. Durch die Skriptsprache MAKI (Make A Killing Interface) kann eine Wasabi-Komponente leicht um spezielles Verhalten erweitert werden, z.B können so Skins effektvoll animiert werden. Abbildung 2.4 zeigt ein klassisches Skin (links, „Fusion”) und ein modernes Wasabi-Skin (rechts, „Impulse”) [@WINAMP]. Der Nachteil von Skins liegt auf der Hand: Da Widgets hier kein einheitliches Aussehen mehr besitzen und ihre Anordnung selbst in ein und derselben Anwendung völlig unterschiedlich sein kann, ist die Bedienung solcher Anwendungen wesentlich schwieriger zu erlernen. Deshalb sind Skins keinesfalls für komplexe oder professionelle Anwendungen geeignet. 3 Mögliche Bezeichnungen sind auch Theme oder Style, nur sind diese etwas zu allgemein. KAPITEL 2. GRUNDLAGEN 24 Abbildung 2.4: Verschiedene Winamp-Skins 2.4 Interaktionstechniken in 3D-Nutzungsschnittstellen Eine grafische Nutzungsschnittstelle dient primär der Interaktion durch einen Benutzer. Interaktionstechniken bezeichnen dabei jeweils eine bestimmte Art von Interaktion. Sie sind Widgets im weiteren Sinne, jedoch (meist) ohne geometrische Ausprägung. Nach [HINZ01] unterteilen sie sich in drei Klassen: • Selektion: Auswahl ein oder mehrerer Objekte. • Manipulation: Veränderung des Zustands eines Objekts. • Navigation: Verschiebung des Eingabefokus bzw. des sichtbaren Ausschnitts. Diese Techniken, die auch Teil klassischer 2D-GUIs sind, bestimmen zu großen Teilen die Effizienz einer grafischen Nutzungsschnittstelle. Bei 2D-GUIs genügen Maus und Tastatur für eine effiziente Interaktion. Prinzipiell können herkömmliche Interaktionstechniken auch im 3D-Raum angewandt werden. Neue Arten der Informationsdarstellung bei 3D-Nutzungsschnittstellen können jedoch wesentlich komplexere Interaktionstechniken erfordern. Diese können durch Nutzung natürlicher Metaphern sehr intuitiv sein. Erst, wenn diese auch effizient und ergonomisch sind, können 3D-GUIs eine breite Anwendung finden, und nicht nur auf Spezialfälle wie z.B. CAD beschränkt sein. Interaktionstechniken müssen besonders bei 3D-GUIs stets im Zusammenhang mit den verwendeten Eingabegeräten betrachtet werden. Spezielle 3D-Eingabegeräte (siehe Abschnitt 2.11) erlauben einen sehr einfachen Umgang mit 3D-Umgebungen. Jedoch muss beachtet werden, dass für gewöhnlich in einer klassischen DesktopUmgebung gearbeitet wird. Deshalb sollte zumindest die Bedienbarkeit mit Eingabegeräten wie Maus und Tastatur gewährleistet sein, wenn nicht sogar darauf ausgerichtet sein. KAPITEL 2. GRUNDLAGEN 25 Selektion Oft müssen in Anwendungen Objekte selektiert werden, um eine bestimmte Funktion auf sie anzuwenden. Sofern es sich lediglich um ein einziges Objekt handelt, muss es lediglich angeklickt werden – so auch bei 3D-GUIs. Schwieriger ist die Auswahl mehrerer Objekte. Eine Möglichkeit in 2D-GUIs ist das Halten einer bestimmten Taste (meist Shift), wodurch der Klick auf ein Objekt dieses in die Selektion aufnimmt bzw. von ihr entfernt. Diese Technik kann leicht im 3D-Raum genutzt werden. Die übliche Rechteck-Auswahl, bei der mit der Maus alle Objekte in einem rechteckigen Bereich selektiert werden, kann dagegen nur eingeschränkt eingesetzt werden, denn im 3D-Raum können Objekte auf sehr vielfältige Weise angeordnet sein. Bei einem GITK-Renderer wird lediglich die Selektion eines einzigen Objekts benötigt, und zwar bei der Fokussierung. Daher wird auf eine nähere Erläuterung von Selektionstechniken im 3D-Raum verzichtet und auf [HINZ01] verwiesen. Manipulation In grafischen Nutzungsumgebungen können sowohl Widgets als auch Dokumentbestandteile manipuliert werden. Eine Manipulation, für die nur ein einziger Klick nötig ist, stellt auch im 3D-Raum kein Problem dar. Schwieriger sind Manipulationen, die sich über mehrere Dimensionen erstrecken. Während bei 2D-GUIs nur eine flache Ebene für die Manipulation existiert, können bei 3D-GUIs zusätzlich Tiefenachse und Drehachsen bei Manipulationen mit einbezogen werden. Gerade bei dreidimensionalen Dokumenten spielt dies eine entscheidende Rolle. Hier bieten sich besondere Eingabegeräte an. Da die GITK-Architektur momentan keine 3D-Dokumente unterstützt, kann dieser Aspekt hier außeracht gelassen werden. Eine mehrdimensionale Manipulation von Widgets (z.B. Positionierung, Größenänderung) ist bei der aktuellen Konzeption ebenfalls nicht nötig, da jedes Widget in seiner maximal nötigen Größe dargestellt wird, und nur der Blickpunkt verändert wird (Navigation). Es wird also lediglich ein einziger Klick zur Manipulation von Widgets nötig sein. Navigation Eine räumliche Arbeitsumgebung erfordert eine Navigation in ihr, ansonsten würde das Potential einer solchen Umgebung nicht ausgenutzt werden. Im Gegensatz KAPITEL 2. GRUNDLAGEN 26 zur zweidimensionalen Navigation in klassischen UIs (hoch / runter bzw. links / rechts) sind dafür im 3D-Raum ganze 6 Dimensionen möglich: jeweils drei für die Bewegung und drei für die Ausrichtung. Der Ort der Betrachtung sowie der Blickwinkel ist also prinzipiell frei wählbar. Die Gefahr, dass der Nutzer bei der Arbeit in einem 3D-Raum die Orientierung verliert, ist wesentlich höher als bei 2D-GUIs. Diese Gefahr kann nur beseitigt werden, indem die Navigation entweder beschränkt oder erzwungen wird. Eine Beschränkung kann sich so äußern, dass Drehachsen gesperrt sind und nur die Bewegung auf einer Ebene erlaubt ist. Eine erzwungene Navigation kann bedeuten, dass der Blickpunkt stets auf einen Punkt ausgerichtet ist, der Bewegungspfad vorbestimmt ist oder die Navigation gänzlich anwendungsgesteuert stattfindet. Um die Navigation möglichst unkompliziert zu gestalten, müssen Widgets und Informationen besonders angeordnet sein. Sogenannte Action Spaces (Handlungsräume) gliedern den virtuellen Raum. Sie sind nach [DACHS04] folgendermaßen definiert: Action Spaces sind virtuelle dreidimensionale Räume mit Interaktionselementen zur Erfüllung einer bestimmten Aufgabe. Virtuelle Werkzeuge und 3D-Schnittstellenelemente (Widgets) sind dabei zusammen mit ihren Containern um einen vordefinierten Blickpunkt angeordnet und erlauben die Ausführung von Teilen einer Gesamtaufgabe. Action Spaces müssen keine Räume im geometrischen Sinne sein und werden ausschließlich durch die Position des Benutzers in der virtuellen Umgebung und die im Blick befindlichen Interaktionselemente definiert. Innerhalb eines Action Spaces ist die Navigation also eingeschränkt. Zwischen Action Spaces kann navigiert werden, jedoch ist auch hier eine zumindest teilweise erzwungene (geführte) Navigation sinnvoll. Was die Definition nicht hergibt, ist die Möglichkeit, Action Spaces zu schachteln. Durch geschachtelte Action Spaces können selbst sehr komplexe, dreidimensionale Umgebungen hierarchisch strukturiert werden. Dreidimensionale Zooming Interfaces können auf diese Art sehr leicht realisiert werden – der Übergang zwischen zwei Action Spaces muss dazu lediglich flüssig animiert sein. KAPITEL 2. GRUNDLAGEN 27 2.5 Widgets in 3D-Nutzungsschnittstellen Der hohe Freiheitsgrad in 3D-Nutzungsschnittstellen erlaubt völlig neue Ansätze bei der Gestaltung von Widgets. Seit Anfang der 90er Jahre werden entsprechende Widgets erforscht und teilweise auch eingesetzt. Besonders bei Unterhaltungssoftware wie z.B. 3D-Spielen sind 3D-Widgets anzutreffen. Jedoch hat sich im Gegensatz zu klassischen GUIs hier noch kein Standard durchsetzen können, weder was bestimmte Metaphern noch technische Umsetzungen betrifft. In [HINZ01] sowie [DACHS04] werden verschiedenste Arten von Widgets zumindest in Klassen eingeordnet und spezifiziert, was Grundlage für eine Standardisierung von 3D-Widgets sein kann. Demnach lassen sich 3D-Widgets je nach ihrer Verwendung in 4 Hauptgruppen einteilen: • Direkte 3D-Objektinteraktion: Selektion sowie direkte Manipulation von 3D-Objekten im Sinne von 3D-Dokumenten und deren Bestandteilen. • Manipulation der 3D-Szene: Änderung von Parametern wie z.B. Position und Ausrichtung der virtuellen Kamera oder von Lichtquellen. • Exploration und Visualisierung: Steuerung der Sichtbarkeit geometrischer Objekte sowie Repräsentation abstrakter (nicht-geometrischer) Daten. • Anwendungskontrolle: Indirekte Manipulation von Parametern innerhalb der Anwendung (Zustandsänderungen, Werteingaben) sowie Aufrufe von Anwendungsfunktionen. Im Folgenden werden diese Klassen von Widgets kurz erläutert und ihre Relevanz für einen GITK-Renderer diskutiert. 2.5.1 Widgets zur direkten 3D-Objektinteraktion Widgets zur direkten 3D-Objektinteraktion werden immer dann benötigt, wenn geometrische Daten bearbeitet werden müssen. Eine solche Bearbeitung kann zum Beispiel die Verschiebung eines Eckpunktes, die Skalierung eines 3D-Objekts oder die Verschiebung eines Objektteils sein. Einer solchen Manipulation geht stets eine Selektion der betreffenden geometrischen Daten voraus. Alle diese Interaktionen KAPITEL 2. GRUNDLAGEN 28 Abbildung 2.5: Manipulator aus dem Open Inventor Toolkit benötigen Widgets, welche den Vorgang visualisieren oder überhaupt erst die entsprechende Funktion anbieten. Abbildung 2.5 zeigt ein exemplarisches Manipulator-Widget aus dem Open Inventor Toolkit. Eine Selektion des Konus macht den Manipulator sichtbar (linke Teilabbildung), was für den Nutzer sowohl visuelles Feedback bietet als auch die Möglichkeit, Transformationen am Konus vorzunehmen. Je nachdem welche Teile des Manipulators bedient werden, kann der Konus (letzte drei Teilabbildungen von links nach rechts) rotiert, skaliert und bewegt werden. Da in der GITK-Architektur keine 3D-Dokumente vorgesehen sind, werden Widgets zur 3D-Objektinteraktion in einem GITK-Renderer nicht nötig sein. 2.5.2 Widgets zur Manipulation der 3D-Szene Ziel der Manipulation einer 3D-Szene kann zum einen eine Navigation innerhalb der Szene (Manipulation der virtuellen Kamera) und zum anderen die Steuerung der Szenenrepräsentation sein. Für die Navigation in einer 3D-Szene (siehe Abschnitt 2.4) ist eine geometrische Repräsentation durch ein entsprechendes Widget nicht zwingend erforderlich. Gerade bei einer freien Navigation ist es eher sinnvoll, passende Eingabegeräte zur direkten Steuerung zu verwenden. In durch Action Spaces strukturierten 3D-Szenen ist dagegen eine eingeschränkte oder erzwungene Navigation sehr sinnvoll, so dass hier entsprechende Widgets für die Navigation von einem Action Space in einen anderen benötigt werden. Solche Widgets können 2D-Widgets nachempfunden werden, da ihre Bedienung nicht über simples Anklicken hinausgehen muss. Denkbar wäre eine Integration solcher Widgets in das Containerwidget des jeweiligen Action Spaces. KAPITEL 2. GRUNDLAGEN 29 Komplexe 3D-Szenen können für eine leichte Navigation Widgets zur Orientierung erfordern. Nach [DACHS04] haben sich Miniaturansichten dafür als sehr effizient erwiesen. Möglich ist hier auch eine Interaktion mit der Miniaturansicht, wodurch leicht Orte in der Szene angesprungen werden können. Die in Abschnitt 2.2.2 angesprochene Möglichkeit, aus der Szene weit heraus zu zoomen, würde ein spezielles Widget überflüssig machen, da in dem Moment eine Miniaturansicht vorliegt. Widgets zur Steuerung der Szenenrepräsentation erlauben beispielsweise die Manipulation von Kameras, Lichtquellen oder Audioquellen. Solche Widgets sind häufig in Autorenwerkzeugen (vorwiegend 3D-Animationsprogramme, wie z.B. Autodesk/Discreet 3D-Studio) zu finden. In Abbildung 2.6 ist ein Licht-Manipulator aus dem Open Inventor Toolkit dargestellt. Eine Spotlichtquelle kann mit dieser abstrakten grafischen Repräsentation (in der Abbildung von links nach rechts) auf der X-Z-Ebene sowie entlang der YAchse verschoben werden und es kann ihre Richtung sowie ihr Öffnungswinkel verstellt werden. Abbildung 2.6: Licht-Manipulator aus dem Open Inventor Toolkit Für einen GITK-Renderer sind Widgets zur Steuerung der Szenenrepräsentation eher von geringer Relevanz – die Parameter der Szenenrepräsentation müssen nicht interaktiv veränderbar sein. Navigation und Orientierung können dagegen je nach Komplexität einer Anwendung sehr wichtig werden. Spezielle Widgets müssten jedoch erst entwickelt werden – momentan genügen Interaktionstechniken ohne jegliche grafische Repräsentation (siehe Abschnitt 2.4). 2.5.3 Widgets zur Exploration und Visualisierung Widgets dieser Art dienen der Untersuchung geometrischer Objekte und der grafischen Repräsentation von Informationen (abstrakter Daten), wie beispielsweise Text oder Graphen. Solche Widgets sind in sich geschlossen oder zu einer geschlossenen Gruppe zusammengefasst. Interaktionen mit solchen Widgets haben keinen Einfluss nach Außen. KAPITEL 2. GRUNDLAGEN 30 Zu den Widgets zur Exploration (Untersuchung) gehören beispielsweise sogenannte Cutting Plane Widgets. Hier durchschneidet eine Ebene ein Objekt, wobei eine Seite der Ebene unsichtbar oder transparent wird. Auf die Art kann das Innere eines Objekts untersucht werden. Statt einer flachen Ebene kann auch ein geschlossenes Objekt beliebiger Form verwendet werden, bei dem nur Bereiche innerhalb des Objekts sichtbar sind. Solche Objekte machen allerdings nur bei Volumendarstellungen Sinn, wie sie zum Beispiel im medizinischen oder CAD-Bereich angewandt werden. Meist sind jedoch in 3D-Anwendungen die Objekte hohl. Die Gruppe der Widgets zur Informationsvisualisierung teilt sich in mehrere Rubriken: Hierarchische Visualisierung Für die Darstellung von Baumstrukturen (azyklische Graphen) gibt inzwischen sehr interessante Ansätze. Kegelbäume sind fast wie natürliche Bäume aufgebaut, nur wesentlich strenger strukturiert. Knoten einer Ebene sind kreisförmig um den „Ast” angeordnet. Das selektierte Element wird stets in den Vordergrund rotiert – mit all seinen Unterknoten. So entsteht gewissermaßen ein Fischaugenblick – weiter entfernte Knoten derselben Ebene werden immer kleiner dargestellt. Bei einem Informationswürfel ist ein Knoten als Würfel dargestellt, der mit seinen ebenfalls als Würfel dargestellten Unterknoten gefüllt ist. Die Größe eines Knotens gibt dabei Aufschluss über einen bestimmten Wert, zum Beispiel die Dateigröße, wenn die Hierarchie eines Dateisystems dargestellt ist. Diese Darstellungsweise erfordert, dass übergeordnete Knoten transparent geschaltet werden können, so dass der Inhalt sichtbar wird. Zudem muss ab einer bestimmten Tiefe in der Hierarchie herangezoomt werden, sonst könnte man nichts mehr erkennen. Gewissermaßen entspricht dies dann dem Zooming Interfaces Konzept. Visualisierung von Graphen und Diagrammen Dreidimensionale Darstellungen von Diagrammen ist längst keine Besonderheit – z.B. können in Microsoft Excel Daten aus Tabellen in drehbaren 3D-Diagrammen dargestellt werden. In einer echten, interaktiven 3D-Umgebung kann solchen Diagrammen ein explorativer Charakter gegeben werden. So könnten Teile transparent KAPITEL 2. GRUNDLAGEN 31 geschaltet oder skaliert werden. Bei komplexen (zyklischen) Graphen können Zusammenhänge durch leichte Bewegungen oder Verwendung stereoskopischer Darstellungsverfahren besser vermittelt werden, da erst dadurch der Effekt der Tiefenwahrnehmung entsteht. Visualisierung von 2D-Daten und -Dokumenten Auch die Darstellung gewöhnlicher zweidimensionaler Daten in einer 3D-Umgebung kann durch spezielle Techniken einen Mehrwert gewinnen. Für gewöhnlich werden solche Daten auf eine flache Ebene gelegt. Diese Ebene kann beliebig im Raum platziert werden. Bei Drehung oder Wölbung dieser Fläche wird der Inhalt perspektivisch verjüngt – die verfügbare Fläche auf dem Bildschirm lässt sich effektiver nutzen. Eine besonders praktische Anwendung ist die FischaugenDarstellung. Dokumentteile im Zentrum der Aufmerksamkeit stehen im Vordergrund und sind damit am größten dargestellt, angrenzende Teile verjüngen sich zunehmend. Auf die Art ist der Kontext des Ausschnittes stets ersichtlich. Bei der Suche nach bestimmten Dokumentteilen reicht es aus, dass der Nutzer die grobe Struktur erkennt, um dann den vergrößerten Ausschnitt an die entsprechende Stelle zu verschieben. In Abbildung 2.7 ist eine Drop-Down-Liste mit Fischaugen-Effekt skizziert. Der Inhalt ist hier zweidimensional (Zeichen x Zeilen), durch eine Wölbung in die Tiefe kann jedoch eine günstigere Platzausnutzung erreicht werden. Von der Auswahl weit entfernte Elemente sind durch die Perspektive stark verkleinert, statt einfach nur ausgeblendet. Beim Scrolling, das möglichst automatisch durch bloße Mausbewegung stattfindet, ist der Kontext klar zu erkennen – der Nutzer sieht stets, welche Elemente folgen. Abbildung 2.7: Skizze einer Drop-Down-Liste mit Fisheye-Technik KAPITEL 2. GRUNDLAGEN 32 Wissenschaftliche Visualisierung Widgets zur Visualisierung wissenschaftlicher Daten lassen sich schwer verallgemeinern oder standardisieren, da die Vertreter spezielle Entwicklungen darstellen. Als Beispiel sei hier ein Widget zur Darstellung und Manipulation von Stromlinien in einem virtuellen Windkanal genannt. Zusammenfassend kann man sagen, dass eine dreidimensionale Informationsdarstellung viele Vorteile bringt. Da in der GITK-Architektur beim aktuellen Entwicklungsstand nur textuelle Informationen relevant sind, genügen zunächst Widgets zur Visualisierung von Text. Hierbei können bereits Vorteile aus der 3DDarstellung gezogen werden. Zum Beispiel kann der Text einer Eingabezeile gewölbt sein, um die angesprochenen Vorteile der Fischaugen-Darstellung ausnutzen. 2.5.4 Widgets zur Anwendungskontrolle Die für einen GITK-Renderer wohl wichtigsten Widgets sind diejenigen zur Anwendungskontrolle. Durch sie können Werte sowie Zustände verändert und Funktionen ausgelöst werden – Dialoge setzen sich aus ihnen zusammen. Prinzipiell können die Elemente zweidimensionaler Nutzungsschnittstellen auch im 3D-Raum eingesetzt werden, wenn sie entsprechend auf einer Ebene arrangiert werden. Bei Ihrer Gestaltung ist durch die Tiefenachse allerdings ein weitaus größerer Spielraum gegeben. Sehr interessant lassen sich im 3D-Raum Menüs und Listen gestalten. Diese Widgets dienen der Auswahl von Objekten wie Texten, Grafiken oder 3D-Objekten, wobei Programmfunktionen ausgelöst werden können (Befehlsmenü). Während bei 2D-GUIs Einträge in Menüs und Listen für gewöhnlich vertikal oder horizontal angeordnet sind, können sie im 3D-Raum beispielsweise ringförmig angeordnet sein (Ringmenü). Wie auch bei der Visualisierung zweidimensionaler Informationen (siehe Abschnitt 2.5.3) erzeugt eine gewölbte Anordnung der Einträge einen Fischaugen-Effekt, so dass wesentlich mehr Elemente auf einmal sichtbar sind. Nach [DACHS04] gehören Container-Widgets ebenfalls in diese Kategorie, obwohl sie im Grunde eine eigene Kategorie bilden. Für den strukturierten Aufbau eines Dialoges sind sie jedoch unerlässlich. Container enthalten andere Widgets oder weitere Container. Zum Beispiel sind Fenster in 2D-GUIs sogenannte TopLevel-Container. In dreidimensionalen Nutzungsschnittstellen können Container KAPITEL 2. GRUNDLAGEN 33 ein Volumen besitzen – sie bilden hier die bereits erwähnten Action Spaces. Neben der Aufgabe, einen Dialog zu strukturieren, kann mit ihnen die Navigation realisiert werden (siehe Abschnitt 2.4). 2.6 Standard-3D-APIs Die Berechnung von 3D-Szenen ist sehr komplex, so dass sie gerade für EchtzeitAnwendungen eine hohe Rechenleistung voraussetzt. Spezielle 3D-Hardware in Form von Grafikkarten übernimmt einen Großteil der Berechnungen. Am Anfang der Entwicklung (Mitte 90er Jahre) mussten Anwendungen noch auf die Hardware zugeschnitten sein. So gab es z.B. zu MS-DOS Zeiten 3D-Spiele in unterschiedlichen Versionen. Heutzutage bilden standardisierte 3D-APIs eine abstrakte Schnittstelle zwischen Software und 3D-Hardware. Da jede Hardware unterschiedliche Funktionen für 3D-Berechnungen besitzt, liefern Hersteller ihre Produkte mit Treibern aus, welche die Schnittstelle zur 3D-API bilden. Die Welt wird durch zwei große 3D-APIs geteilt: OpenGL und Direct3D. Neben ihnen existieren noch weitere APIs (z.B. 3DR, Hoops), deren Bedeutung jedoch vergleichsweise gering ist. Einige haben ihre Bedeutung sogar gänzlich verloren, wie im Falle 3Dfx Glide. Deshalb werden nun lediglich die beiden wichtigsten APIs verglichen. 2.6.1 OpenGL In den 80er Jahren entwickelte Silicon Graphics (SGI) die 3D-API IRIS GL (Graphics Library) für das eigene IRIX-System. 1992 wurde diese API durch das Architecture Review Board (ARB), einem Konsortium aus namhaften Herstellern (IBM, DEC, Intel, Microsoft etc.), nahezu unverändert zum Standard OpenGL verabschiedet. OpenGL ist dennoch eingetragenes Warenzeichen von Silicon Graphics. Inzwischen wurde OpenGL weiterentwickelt, zu diesem Zeitpunkt ist Version 1.5 aktuell. Mit jeder Version kommen neue Funktionen hinzu, um 3D-Grafiken noch realistischer darstellen zu lassen. Zudem ist OpenGL durch Extensions erweiterbar. Diese sind teilweise soft- und hardware-spezifisch. So sind beispielsweise Multitexturing und programmierbare Vertex Shader solche Erweiterungen. Durch die Etablierung neuer Technologien werden die entsprechenden Extensions Teil neuer OpenGL-Spezifikationen. KAPITEL 2. GRUNDLAGEN 34 OpenGL wird durch nahezu jedes Betriebssystem unterstützt. Der Entwickler der jeweiligen Implementation muss sich diese allerdings (kostenintensiv) vom ARB zertifizieren lassen. So kommt es, dass sich die freie Linux-Implementation MesaGL zumindest offiziell die Verwandtschaft zu OpenGL verschweigt. Trotz allem kann man also sagen, dass OpenGL hersteller- und plattformunabhängig ist. Durch die hervorragende Unterstützung seitens der Hersteller deckt OpenGL ein sehr weites Gebiet von Anwendungen ab. Vor allem im wissenschaftlichen Bereich und bei professionellen Grafikanwendungen findet diese API Verwendung. Leider ist die OpenGL-Unterstützung durch billige Grafikhardware eher schlecht (v.a. bei Laptops), so dass hier die CPU einen Großteil der Grafikberechnungen übernehmen muss. Direkt verwenden lässt sich OpenGL mit C und C++, andere Sprachen benötigen Wrapper-Bibliotheken, wie z.B. SUN JOGL für Java. Die Khronos Gruppe, ein weiteres Konsortium großer Firmen, veröffentlichte im April 2004 OpenGL ES 1.0, eine 3D-API für mobile Geräte, welche eine Teilmenge der OpenGL-Funktionen bietet. Ob sich diese Technologie auch für professionelle Anwendungen eignet, ist angesichts kleiner Displays fraglich. Zumindest sind bereits etliche Projekte im Gange, die OpenGL ES für Betriebssysteme wie Palm OS oder Symbian OS implementieren. Die Verfügbarkeit entsprechender Hardware ist dann nur eine Frage der Zeit. 2.6.2 Direct3D 1996 veröffentlichte die Firma Microsoft Version 2.0 ihres Multimedia Frameworks DirectX. Die wohl wichtigste Neuerung war damals die 3D-API Direct3D. Nachdem jährlich neue Versionen von DirectX erschienen, ist es inzwischen Teil des Betriebsystems Microsoft Windows. Nahezu alle Spiele für die x86-Architektur werden für dieses Betriebssystem entwickelt. So ist es nicht verwunderlich, dass Direct3D der Standard bei 3D-Spielen ist. So gut wie jede Grafikkarte, selbst aus dem Low-Cost-Bereich, unterstützt Direct3D (wenn auch nur teilweise) hardwareseitig. Zusammen mit den HardwareHerstellern wird die Entwicklung der API rasant vorangetrieben, meist hängt die Hardware dem Funktionsumfang sogar hinterher. Für das Marketing von 3D-Hardware ist es für die Hersteller essenziell, den neuesten Standard zu unterstützen. KAPITEL 2. GRUNDLAGEN 35 DirectX ist alleiniges Eigentum von Microsoft, es ist ausschließlich unter der Windows-Plattform verfügbar. Dafür ist es unabhängig von der Programmiersprache, denn es nutzt die Schnittstelle COM. Im Gegensatz zu OpenGL besitzt Direct3D ein eigenes Dateiformat (Endung .x). 2.6.3 Fazit Direct3D besitzt definitiv das größere Leistungspotential, denn die Entwicklung aus einer Hand und der enorme Leistungsbedarf der Spielebranche sorgen hier für eine wesentlich schnellere Entwicklung als es bei OpenGL je der Fall sein wird. Schwachpunkt ist allerdings die Plattformabhängigkeit. Da GITK und damit auch ein GITK-Renderer plattformunabhängig sein soll, kann die Entscheidung nur auf OpenGL fallen. Zumal die Verfügbarkeit einer Open-Source-Implementation stets ein positiver Faktor ist. Ein detaillierterer Vergleich zwischen den beiden APIs ist unter [@RICH] zu finden. 2.7 Grundlagen der 3D-Grafik-Programmierung Trotz der Nutzung abstrakter 3D-APIs, welche dem Programmierer die größte Arbeit abnehmen, sollte ein gewisses Verständnis für die Grundlagen in der 3D-Grafikprogrammierung vorhanden sein. Gerade Leser, die sich noch nie dieser Thematik gewidmet haben, erfahren in diesem Abschnitt, was hinter den Kulissen computerberechneter 3D-Welten steckt. 2.7.1 Modellierung Grundlage jedes 3D-Spiels oder computeranimierten Films im Stile von „Toy Story” ist ein Modell jedes Objekts und jeder Figur. Die Oberfläche des Objekts wird dabei in kleine grafische Primitive (Polygone, meist Dreiecke) zerlegt. Das innere des Objekts ist stets hohl4 . Je komplexer ein Modell ist, je mehr Feinheiten es besitzt, umso mehr Teilflächen entstehen. Gespeichert werden die Koordinaten der Eckpunkte der Flächen und wie sie untereinander verbunden sind. So entsteht ein Drahtgittermodell, wie es in Abbildung 2.8 dargestellt ist. 4 Anwendungen im CAD- oder Medizin-Bereich erfordern solide Modelle, nur werden solche Spezialfälle hier ausgeklammert. KAPITEL 2. GRUNDLAGEN 36 Abbildung 2.8: Drahtgittermodell einer Kugel Koordinaten bestehen dabei üblicherweise aus den Komponenten X-, Y- und ZWert. Diese Werte liegen einem kartesischen Koordinatensystem zugrunde, wie es in Abbildung 2.9 dargestellt ist. Abbildung 2.9: Koordinatensystem im 3D-Raum 2.7.2 Beleuchtung Die 3D-Modelle werden im virtuellen dreidimensionalen Raum platziert, so dass sich eine Szene ergibt. Die geometrische Modellierung ist damit vollendet, nur benötigt ein Objekt neben seiner Form auch eine Farbe, die durch sein Material und Lichtquellen entsteht. Für die Lichtberechnung haben sich drei Klassen von Algorithmen etabliert [CLAUS97]: Raytracing Beim Strahlverfolgungsverfahren wird ausgehend von der Kamera untersucht, woher das Licht jedes einzelnen Pixels auf dem Bildschirm stammt. Rekursiv werden alle Objekte und sogar deren Reflektions- und Brechungseigenschaften berücksichtigt, so dass extrem realistisch wirkende Bilder erzeugt werden. Dafür sind die Algorithmen aufgrund ihrer Komplexität sehr langsam und deshalb momentan für Echtzeitanwendungen auf breiter Ebene ungeeignet. KAPITEL 2. GRUNDLAGEN 37 Radiosity Unter der Annahme eines Energiegleichgewichts in einer Szene werden bei Radiosityalgorithmen die Leuchtdichten aller Flächen berechnet. So können diffuse Umgebungen, wie sie z.B. bei der Innenarchitektur auftreten, sehr gut simuliert werden. Die benötigte Rechenzeit ist recht hoch, jedoch kann die Beleuchtung berechnet werden, bevor die Szene in Echtzeit mittels eines Renderingverfahrens dargestellt wird. Rendering Renderingalgorithmen basieren auf lokalen Beleuchtungsmodellen und auf der Interpolation von Farbwerten. Es werden also Effekte wie Spiegelung oder Schatten vernachlässigt. Dafür ist dieses Verfahren sehr gut für Echtzeitanwendungen geeignet, denn es ist einfach und kann durch Hardware realisiert werden. OpenGL wie auch die meisten anderen 3D-APIs verwenden ausschließlich Renderingverfahren zur Darstellung von Bildern. Mit Hilfe einiger Tricks ist es auch möglich, Spiegelungen und Schatten zumindest nachzuahmen. 2.7.3 Material Das Material eines realen Objekts ist charakterisiert durch seine Struktur sowie die Art und Weise, wie Licht reflektiert wird. Meistens werden Teile des Lichts absorbiert, so dass das reflektierte Licht eine andere Farbe als das einfallende besitzt. Objekte einer 3D-Szene besitzen also Parameter, welche die Quantität und Qualität des reflektierten Lichts bestimmen. Objekte können auch selbst-illuminierend sein, also Licht abstrahlen, ohne dass Licht eingefallen ist. Wirklich realistisch wirken computerberechnete 3D-Objekte erst durch eine feine Struktur. Diese durch eine hohe Anzahl geometrischer Primitive zu modellieren wäre ein ungeheurer Entwicklungs- und Rechenaufwand. Stattdessen werden Texturen, also Rasterbilder, auf Objekte projiziert. 2.7.4 Abbildung Eine Szene soll nun mittels eines Renderingverfahrens dargestellt werden. Da heutige Ausgabemedien nur zweidimensionale Bilder akzeptieren, muss die Szene auf KAPITEL 2. GRUNDLAGEN 38 eine flache Ebene projiziert werden. Dazu wird zunächst ein sichtbarer Raum (engl. view volume) gewählt. Eine Seite des Raums ist die Projektionsebene, nur Objekte, die innerhalb des Raums liegen, werden auf ihr abgebildet. Dabei gibt es zwei Arten von Projektionsarten: • perspektivische Projektion: Imitiert eine Kamera durch perspektivische Verjüngung. Je weiter entfernt ein Objekt liegt, umso kleiner erscheint es im entstehenden Bild. • orthografische Projektion: Die Seiten des sichtbaren Raums sind parallel, abgebildet wird stets senkrecht auf die Projektionsebene. Die Entfernung eines Objekts hat keinen Einfluss auf die Größe seines projizierten Abbildes. Abbildung 2.10: Perspektivische und orthogonale Projektion Die Objekte werden nun unter Berücksichtigung von Material und Licht von hinten nach vorn in Richtung Kamera auf die Projektionsebene gerendert (dt. „gezeichnet”). Technisch gesehen ist die Zeichenfläche ein Puffer im Speicher, groß genug, das Zielbild aufzunehmen (frame buffer). Wie auch im 2D-Bereich ist es üblich, mit zwei Puffern zu arbeiten: der Puffer, in den gerendert wird und der Puffer des vorherigen Bildes, welches gerade dargestellt wird. Mit der double buffer-Technik wird vor allem bei Animationen ein Flackern vermieden. 2.7.5 Arbeitsweise von OpenGL Der Schritt des Renderings, also der Berechnung eines Bildes, wird von der 3DAPI OpenGL übernommen. Die Programmierung mit dieser API erfolgt rein prozedural mit nur einfachen Datentypen. Intern ist OpenGL ein Zustandsautomat – Funktionsaufrufe ändern bestimmte Zustände bzw. fragen sie ab. Folgendes simples Beispiel erzeugt ein weiß gefülltes Quadrat auf schwarzem Hintergrund [ARB93]: KAPITEL 2. GRUNDLAGEN 39 // Löschfarbe auf Schwarz setzen glClearColor(0.0, 0.0, 0.0, 0.0); // Puffer mit der Löschfarbe füllen glClear(GL_COLOR_BUFFER_BIT); // Zeichenfarbe auf Weiß setzen glColor3f(1.0, 1.0, 1.0); // Projektion und Viewvolume setzen glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); // Beginn eines Polygons (das Quadrat) glBegin(GL_POLYGON); // Übermittlung der 4 Eckpunkt-Koordinaten glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); // Ende des Polygons glEnd(); // Alle Operationen abschließen // spätestens nach diesem Aufruf befindet // sich das Bild fertig gerendert im Puffer. glFlush(); Die über 120 Funktionen von OpenGL dienen lediglich dem Ändern und Abfragen von Zuständen. Alles andere ist Sache des Programmierers. So fehlt in vorangegangenem Beispiel das Anlegen einer Render-Area – einem Fenster, in dem das Bild aus dem Frame Buffer dargestellt wird. Auch Interaktion und Animation sind nicht Bestandteil der OpenGL-API. Eine Animation lässt sich realisieren, indem die für das Rendern zuständigen Funktionsaufrufe in einer Schleife untergebracht sind, und ein Zustand wie z.B. die Position des Koordinaten-Ursprungs in jedem Durchlauf ein wenig verändert wird. Dank leistungsfähiger 3D-Hardware dauert das Rendern selbst komplexer Szenen nur den Bruchteil einer Sekunde, so dass die für eine flüssige Animation mindestens erforderlichen 15 Bilder pro Sekunde ohne weiteres erreicht werden. 2.8 3D-Toolkits Bei Betrachtung der OpenGL-API erkennt man schnell, dass für komplexe, interaktive 3D-Anwendungen noch sehr viel seitens des Programmierers getan werden KAPITEL 2. GRUNDLAGEN 40 muss. Bei der Programmierung einer 3D-Anwendung würde man für jede Art von 3D-Objekt eine Klasse entwickeln, die sowohl ihr Verhalten als auch ihre Darstellung durch OpenGL oder Direct3D implementiert. Eine Bibliothek, die solche Funktionalität bietet, wird als Grafik-Engine bezeichnet. Häufig sind solche Engines auf einen bestimmten Einsatz zugeschnitten, wie beispielsweise Computerspiele oder Simulationen. Einige besitzen jedoch ein derart breites Einsatzspektrum, dass sie als 3D-Toolkit (dt. Baukasten) bezeichnet werden können. In diesem Abschnitt werden zwei wichtige 3D-Toolkits vorgestellt: Java3D und Open Inventor. Beiden gemein das Datenmodell eines Szenengraphen, das im Bereich der 3D-Programmierung mittlerweile sehr üblich ist. Dabei wird im retained mode gearbeitet, d.h. es werden zunächst Datenstrukturen aufgebaut, die dann für das Rendering genutzt werden. Details zu diesem Konzept werden in Kapitel 3 ausführlich beschrieben. Desweiteren unterstützen beide Toolkits stereoskopische Verfahren sowie 3D-Klang. Für letzteres können Klangquellen und -senken beliebig im Raum positioniert werden, die Audio-Daten für die Klangsenken werden dynamisch abgemischt und über Audio-Hardware ausgegeben. 2.8.1 Open Inventor Ursprünglich wurde Inventor als objektorientierte API zu IRIS GL entwickelt (IRIS Inventor, auch Inventor 1.0). Durch den Standard OpenGL wurde diese API Anfang der 90er zu Open Inventor (OIV) bzw. Inventor 2.0, die Implementation von SGI war jedoch auf die IRIX-Plattform beschränkt. Durch eine Kooperation mit TGS (Template Graphics Systems) wurde Open Inventor auf andere Systeme portiert und zusätzlich weiterentwickelt. Diese Implementationen werden durch TGS kommerziell vertrieben. Durch die Möglichkeit, mit Open Inventor sehr schnell interaktive 3D-Anwendungen zu entwickeln, wurde es bald zum de-facto Standard in der wissenschaftlichen 3D-Visualisierung. OIV besitzt ein eigenes Dateiformat für 3D-Szenen, welches später die Ausgangsbasis für das VRML-Format war. Die Firma Systems In Motion (SIM) entwickelte 1995 eine Bibliothek für 3DRendering, die in eigenen Produkten Verwendung fand. Unter anderem unterstützte sie das VRML-Format. Da die Arbeitsweise ähnlich zu OIV war, entschied man sich nach einigen Jahren, die API kompatibel zu OIV machen. Mittlerweile ist KAPITEL 2. GRUNDLAGEN 41 diese Bibliothek unter dem Namen Coin3D fast hundertprozentig OIV-kompatibel und in einer LGPL- (Open Source) sowie einer kommerziellen Lizenz erhältlich. TGS und SIM entwickeln ihre Implementationen immer weiter und übernehmen jeweils vom Konkurrenten Funktionen, sofern sie sich als sinnvoll erweisen. Der gemeinsame Nenner aller verfügbaren Implementationen ist momentan OIV Version 2.1. Erst seit August 2000 ist auch SGI Open Inventor als Open Source freigegeben, jedoch besitzen die neueren Versionen von TGS und SIM einen deutlich höheren Funktionsumfang. SGI besitzt offenbar kein Interesse, OIV selbst weiter zu entwickeln. Open Inventor ist in C++ entwickelt, besitzt aber neben einer C++ API auch eine API für ANSI C. OIV kann jedoch ausschließlich in C++ um eigene Klassen erweitert werden. TGS und SIM bieten sogar eine Java-API an, jedoch ist die von SIM noch in einem frühen Stadium. Für das Rendering nutzt Open Inventor ausschließlich die Low-Level-API OpenGL. In Callback-Funktionen und natürlich auch bei Klassenerweiterungen können problemlos OpenGL-Funktionen genutzt werden. Für die Unterstützung von 3D-Sound nutzt Coin3D die 3D-Audio Bibliothek OpenAL, die ähnlich wie OpenGL von mehreren Firmen (u.a. Creative Labs) entwickelt wird. 2.8.2 Java3D Die erste Version der Java3D-API wurde Ende 1998 durch SUN veröffentlicht. Arbeitsweise und Funktionsumfang von Java3D ähnelt denen von Open Inventor, nur unterscheiden sich die Terminologien teilweise voneinander. Der Abstraktionsgrad ist sogar höher als bei Open Inventor. Für das Rendering wird neben OpenGL auch Direct3D unter Windows unterstützt. Außerdem befindet eine Schnittstelle zu OpenGL ES in Entwicklung, womit auch der Einsatz auf Mobilgeräten möglich wäre. Nachteilig von Java3D ist die ausschließliche Verfügbarkeit für die Java-Plattform. Aus Java heraus kann nicht direkt auf die Low-Level-APIs OpenGL oder Direct3D zugegriffen werden, was die Erweiterbarkeit einschränkt. KAPITEL 2. GRUNDLAGEN 42 In der Java Entwicklergemeinde ist Java3D für die 3D-Programmierung neben JOGL sehr weit verbreitet. Erst seit kurzem (Juni 2004) ist der Sourcecode von Java3D öffentlich, was Aussicht auf eine sehr rasche Weiterentwicklung dieses Toolkits gibt. 2.9 Distributed Scene Graph APIs Ein immer noch bestehendes Problem ist der Umstand, dass 3D-Anwendungen in heutigen Desktopumgebungen in einem eigenen Fenster laufen. Auf die Art stellen 3D-Anwendungen stets einen Sonderfall dar. Das Apple Mac OS X verschenkt leider Potential, denn trotz der OpenGL-Darstellung sind nur klassische 2D-Geometrien möglich – eine 3D-Anwendung kann ihr GUI nicht frei auf den Desktop rendern, ohne dass es nicht von einem Fenster umgeben ist. Eine Desktopumgebung, welche die Möglichkeit der direkten 3D-Darstellung bietet, ist eine Voraussetzung für 3D-GUIs, die eine breite Anwendung finden. Erst wenn 3D-Anwendungen unter einem solchen „gemeinsamen Dach” laufen, können sich 3D-Widgets etablieren und Standards herauskristallisieren. Ansatzpunkt einer solchen Desktopumgebung wäre ein Distributed Scene Graph (verteilter Szenengraph). Durch eine solche Architektur kümmert sich das DesktopSystem um die 3D-Darstellung, während Anwendungen ihre Szenenobjekte in eine global verfügbare Datenstruktur einfügen. Open Inventor und Java3D sind szenengraphbasierte APIs – was hinter dem Konzept steckt, wird in Kapitel 3 am Beispiel von Inventor erläutert. In diesem Abschnitt sollen zwei aktuelle Projekte vorgestellt werden, die einen Distributed Scene Graph nutzen. 2.9.1 Studierstube Studierstube [@STUBE] ist ein Forschungsprojekt der TU Wien zur Augmented Reality (siehe Abschnitt 2.12). Im Rahmen dieses Projekts wurde SGI Open Inventor zu Distributed OpenInventor (DIV) erweitert. Dabei teilen sich mehrere Workstations einen gemeinsamen Szenengraphen über ein Netzwerk. Nutzer können so in einem gemeinsamen virtuellen Raum interagieren. Abbildung 2.11 zeigt beispielsweise, wie ein Lehrer gemeinsam mit einem Schüler in einer Augmented Reality Umgebung ein 3D-Objekt konstruiert. KAPITEL 2. GRUNDLAGEN 43 Abbildung 2.11: Beispiel eines Studierstube-Projekts Das Projekt begann 1995 und wird in mindestens 40 Unterprojekten weiterentwickelt. Die Quellen von DIV sind in einer LGPL-Lizenz frei verfügbar. Voraussetzung ist ein spezieller Port von SGI Open Inventor oder eine gepatchte Version von Coin3D. 2.9.2 SUN Looking Glass Looking Glass ist der Codename der 3D-GUI des SUN Java Desktops. Das Projekt wurde erst 2003 angekündigt und befindet sich derzeit in einer rasanten Entwicklungsphase. Nachdem SUN die wichtigsten Komponenten implementierte, bekam das Projekt Ende Juni 2004 den Open Source Status. Die Fertigstellung der API wird Ende 2004 erwartet. Looking Glass ist ein Fenstermanager – ähnlich zu KDE oder GNOME – mit dem großen Unterschied, dass er eine echte 3D-Darstellung bietet. Klassische 2DAnwendungen werden auf Flächen projiziert, die sogar gedreht werden können. Neue 3D-Anwendungen können sich völlig frei auf dem Desktop präsentieren. Ein Screenshot aus einer Demonstration von SUN ist in 2.12 abgebildet. Im Vordergrund sieht man ein 3D-UI einer Anwendung, im Hintergrund klassische 2D-GUIs. Grundlage der API bildet Java3D mit einem verteilten Szenengraphen. 3D-Anwendungen können aktuell nur in Java implementiert werden, eine C/C++ API ist jedoch bereits geplant. Dieses Projekt bildet derzeit einen äußerst vielversprechenden Ansatz, da sowohl durch SUN ein starkes Unternehmen sowie die Open Source Gemeinde hinter dem Projekt stehen. KAPITEL 2. GRUNDLAGEN 44 Abbildung 2.12: SUN Looking Glass 2.10 Stereoskopische Verfahren Der Mensch besitzt eine ausgeprägte räumliche Wahrnehmung, da er mit beiden Augen zwei getrennte Bilder aufnimmt. Hält man sich ein Auge zu, merkt man sofort, dass es schwieriger wird, Entfernungen zu erkennen. Es existieren bereits echte 3D-Displays, die eine physische Tiefenachse besitzen. Diese arbeiten mit holografischen Verfahren, räumlicher Floureszenzanregung oder sich schnell drehenden Projektionsflächen. Nur ist die Lichtausbeute dieser Techniken inakzeptabel gering. Außerdem können sich Teile der 3D-Szene nie völlig verdecken – sie sind stets teiltransparent. Die Darstellung dreidimensionaler Bilder erfordert also eine Projektion auf eine zweidimensionale Ebene. Dadurch geht die dritte Dimension verloren, denn beide Augen nehmen identische Bilder auf. Um den Nutzen dreidimensionaler Nutzungsschnittstellen zu erhöhen, können stereoskopische Verfahren angewandt werden. Diese ermöglichen den Erhalt der Tiefeninformation, indem für jedes Auge ein stereoskopisches Halbbild geliefert wird. Dieser Abschnitt beschreibt solche Verfahren, und diskutiert deren Eignung für die Interaktion mit Anwendungsprogrammen. 2.10.1 Stereogramm Stereogramme sind Abbildungen, die erst mit einer speziellen Betrachtungsweise eine räumliche Wirkung offenbaren. KAPITEL 2. GRUNDLAGEN 45 Stereobildpaar Die beiden Halbbilder werden nebeneinander abgebildet und durch Kreuzblick (Schielen) überlagert. Durch Prismenbrillen kann der anstrengende Kreuzblick ersetzt werden – damit ist auch eine vertikale Anordnung der Halbbilder möglich. Nachteilig ist hierbei, dass die Darstellung den doppelten Platz benötigt wird und so nur der halbe Bildschirm effektiv genutzt werden kann. Anaglyphenbilder Beim Anaglyphenverfahren werden die beiden Halbbilder in Komplementärfarben übereinander gelegt. Anaglyphenbrillen trennen die Halbbilder, indem ein Filter die Farbe für das jeweils andere Auge ausblendet. Großer Nachteil der Technik ist, dass die Darstellung immer nur zweifarbig ist. Dafür lassen sich die Brillen sehr billig aus Pappe und Folie produzieren. Single Image Stereogram Mit einem speziellen Verfahren werden die Halbbilder in einem einzigen Bild versteckt. Dieses kann aus zufälligen Punkten oder einem gewöhnlichen Bild bestehen. Wird dieses mit Parallelblick (Defokussierung der Augen) betrachtet, erkennt man das 3D-Bild. Allerdings erfordert dies einiges an Übung, vielen Menschen gelingt das nicht. Das Verfahren eignet sich deshalb für Anwendungsprogramme absolut nicht, sondern eher für künstlerische Effekte. 2.10.2 Pulfrich-Effekt Der Pulfrich-Effekt nutzt den Umstand, dass sich Augen in dunkleren Umgebungen träger als in hellen verhalten. Wird das Bild eines Auges etwas abgedunkelt, wirkt ein Objekt, dass sich horizontal vorbeibewegt oder dreht, vom Hintergrund etwas abgesetzt. Es ist also keine spezielle Darstellung nötig. Dieses Verfahren wird oft in TV-Shows verwendet, Anwendungsprogramme dagegen können daraus keinen Nutzen ziehen. KAPITEL 2. GRUNDLAGEN 46 2.10.3 Interlaced Verfahren / LCD-Shutterbrillen LCD-Shutterbrillen sehen beinahe wie gewöhnliche Brillen aus. Die „Brillengläser” besitzen eine Schicht aus Flüssigkristall, die beim Anlegen einer Spannung dunkel werden. Ein Monitor zeigt im Wechsel das Bild für ein Auge an, wobei das LCD vor dem jeweils anderen Auge dunkel ist. Auf die Art sieht jedes Auge ein separates Bild. Nachteilig ist hierbei, dass das Bild flackert, denn die Technik erreicht selten mehr als 60 Herz pro Bild und Auge. So ermüdet die Arbeit schon nach kurzer Zeit. Zudem können die LCDs das Bild nicht völlig abdunkeln – jedes Auge sieht das jeweils andere Teilbild leicht durchscheinen. 2.10.4 Head-Mounted Displays (HMD) Head-Mounted bedeutet soviel wie „Am Kopf befestigt”. Bei dieser Technik befinden sich zwei kleine LC-Displays in einem Helm („Cyberhelm”), durch die beide stereoskopische Halbbilder hundertprozentig voneinander getrennt wiedergegeben werden. So bieten sie eine ausgezeichnete Tiefenwahrnehmung. Moderne HMD arbeiten mit einer Spiegeloptik, die eine hohe Brennweite und guten Tragekomfort ermöglichen. Nachteilig ist, dass man nicht einfach vom Display wegschauen kann, was auf Dauer die Augen anstrengt. Abbildung 2.13 zeigt ein Highend-HMD von SaabTech [@SAAB], dessen Spiegeloptik eine Transparenz von bis zu 35 % oder wahlweise völlige Immersion bietet, und so den Einsatz in VR- sowie ARUmgebungen (siehe Abschnitt 2.12) ermöglicht. Abbildung 2.13: SaabTech AddVisor 150 2.10.5 3D-Bildschirme 3D-Bildschirme sind eine recht neue Technik. Verwendet werden hierbei gewöhnliche Displays (meist TFTs wegen der Schärfe) mit möglichst hoher Auflösung, KAPITEL 2. GRUNDLAGEN 47 die mit einem Raster (oder Filterarray) überlagert sind. Dieses sorgt dafür, dass Pixelspalten stets von nur einem Auge sichtbar sind. Das Resultat ist eine Darstellung, welche die der Head-Mounted Displays ebenbürtig ist. Der Ermüdungseffekt ist hier nicht anders als bei gewöhnlichen Bildschirmen. Einziger Nachteil dieser Technik ist die Winkelabhängigkeit der Betrachtung. So ist die Betrachtung nur in kleinen Bereichen möglich, in denen sich die Augen befinden können. Dadurch ist auch die Anzahl der Betrachter eingeschränkt. Displays der Firma SeeReal [@SEE] besitzen optische Sensoren, welche die Position der Augen des Betrachters messen und so das Raster steuern. Ist zwar die Position der Betrachtung frei, dafür kann jedoch stets nur eine Person am Display arbeiten. Displays von X3D [@X3D] können 8 Teilansichten eines Bildes darstellen, auf jedes Auge fallen vier. Bewegt der Betrachter seinen Kopf ein wenig horizontal, sieht er ein anderes Teilansichtspaar. So scheint es, als könne man das dargestellte Objekt von der Seite sehen. Der Effekt ist absolut verblüffend, erfordert jedoch spezielle OpenGL-Treiber bzw. spezielle Software zum Erzeugen und wiedergeben von 3D-Bildern- und Videos. 2.10.6 Bildprojektion mit Polarisationsfilter Bei diesem Verfahren sind zwei Projektoren nötig, welche die Bilder für beide Augen liefern. Durch Polarisationsfilter schwingen die Lichtwellen des einen Bildes nur horizontal, die des anderen nur vertikal. Durch Brillen, deren Gläser ebenfalls Polarisationsfilter besitzen, wird das Teilbild des jeweils anderen Auges vollständig herausgefiltert. Das Verfahren eignet sich sehr gut für ein größeres Publikum, da die Darstellung entsprechend groß und die Herstellung der Brillen günstig ist. Für kleineren Platzbedarf können die Projektoren hinter der Projektionsfläche angebracht werden, wie z.B. bei SketchAR aus Abschnitt 2.11.3. Nachteilig bei dieser Technik ist der hohe Preis, da zwei Projektoren benötigt werden. 2.10.7 Fazit Man sieht, dass das große Problem die Verteilung der Teilbilder auf die beiden Augen darstellt. Jedes Verfahren besitzt Nachteile. Dennoch bieten stereoskopische Verfahren ein geeignetes Mittel, um dreidimensionale Szenen für den Menschen KAPITEL 2. GRUNDLAGEN 48 realistischer darzustellen und greifbarer zu machen. Besonders 3D-Displays sind vielversprechend und rücken in bezahlbare Preisregionen. So war ein 17”-Display von X3D im Frühjahr 2004 bereits unter 2000 Euro erhältlich. 2.11 3D-Eingabegeräte In einer gewöhnlichen zweidimensionalen Arbeitsumgebung navigiert der Nutzer intuitiv mit Maus und Tastatur. Das funktioniert wunderbar, denn diese Eingabegeräte wurden genau für den Zweck entworfen. Für 3D-Anwendungen sind diese Geräte jedoch enorm ineffektiv, denn neben der Tiefenachse gibt es noch drei Drehachsen. Zwar können durch Umschalttasten und Scrollräder alle Bewegungsachsen abgedeckt werden, nur intuitiv ist das keinesfalls. Verschiedene Forschungsinstitute und Hersteller haben dieses Problem seit langem in Angriff genommen und spezielle Eingabegeräte für 3D-Umgebungen entwickelt, von denen einige nun vorgestellt werden. 2.11.1 Joystick Joysticks bieten meist zwei, seltener auch drei oder vier Bewegungsachsen. Sie sind fast ausschließlich für Computerspiele konzipiert, denn meist müssen sie mit der ganzen Hand umfasst werden, wobei der Zeigefinger auf einem „Feuerknopf” ruht. Dennoch können sie zumindest als ergänzendes Eingabegerät herhalten. Einige Joysticks können entlang ihrer Vertikalen gedreht werden, was in 3D-Umgebungen nützlich ist. Die oft vorhandenen Schubregler dagegen können nur für bestimmte Funktionen dienlich sein (z.B. Zoom), für eine Navigation oder Steuerung eines 3D-Objekts eignen sie sich (abgesehen von Objekten in Spielen) nicht. Üblicherweise besitzt ein Joystick eine Feder, die ihn beim Loslassen in die Mittelposition zurückführt. Mit solchen Geräten wird meist im rate control-Modus gearbeitet – die Aussteuerung in eine Richtung wird auf die Geschwindigkeit der Bewegung in diese Richtung übertragen. Bei einigen Modellen kann die Feder deaktiviert werden. Dann bietet sich der position control-Modus an – die Position des Sticks wird als absoluter Wert auf die Bewegungsachse abgebildet . [HINZ01] Abbildung 2.14 zeigt einen CH Mach 3 mit entriegelter Feder und einen Microsoft Sidewinder 3D Pro mit einem Stick, der sich um die vertikale Achse drehen lässt. KAPITEL 2. GRUNDLAGEN 49 Abbildung 2.14: Joysticks 2.11.2 Spaceball Ein Spaceball ähnelt in Aussehen und Handhabung einem Joystick, mit dem Unterschied, dass man eine Kugel statt einen Stick in der Hand hält. Auch hier wird gewöhnlicherweise im rate control-Modus gearbeitet. Die Kugel kann sowohl in drei Achsen bewegt als auch in 3 Achsen gedreht werden, womit sie alle möglichen Bewegungsachsen im 3D-Raum abdeckt. Für gewöhnlich wird ein solches Gerät mit der linken Hand bedient, während die rechte Hand Tastatur oder Maus bedient. Durch beidhändiges Arbeiten kann sehr effektiv mit 3D-Anwendungen interagiert werden. Spaceball-Geräte werde z.B. durch die Firma 3Dconnexion [@CONN], einer Tochterfirma von Logitech, produziert. Mit ca. 300 Euro sind diese gegenüber Joysticks jedoch sehr teuer. Abbildung 2.15: 3Dconnexion Spaceball 5000 2.11.3 Tracker Ein Tracker-System bestimmt meist optisch (seltener magnetisch) die Position eines Gegenstandes. Dazu untersucht das System lediglich bestimmte Merkmale, wie z.B. Augen oder Markierungen. Tracker werden je nach Anzahl der Freiheitsgrade (engl. degree of freedom) in 3-DOF (nur Koordinaten) und 6-DOF (auch Winkel) KAPITEL 2. GRUNDLAGEN 50 unterschieden. Die Position wird durch Tracker absolut übertragen. So ist mit ihnen eine sehr natürliche Arbeitsweise möglich. Nachteilig bei solchen Systemen ist zum einen, dass die Arme im Gegensatz zur Arbeit mit Maus oder Spaceball nicht einfach ausruhen können, ohne dass die Position verändert wird. Zum anderen besitzen Tracker eine relativ geringe Auflösung, weswegen kein sehr genaues Arbeiten möglich ist. Ein Beispiel für ein Tracker-System ist SketchAR vom Fraunhofer Institut für graphische Datenverarbeitung, bei dem mit einem Cyberstilo gearbeitet wird (Abb. 2.16). Abbildung 2.16: SketchAR und Cyberstilo 2.11.4 Datenhandschuh Bei Datenhandschuhen werden die Gelenkstellungen von Fingern sensorisch erfasst. So können, entsprechende Software vorausgesetzt, Gesten als Befehle interpretiert werden. Meist sind Datenhandschuhe an einen Tracker gekoppelt, so dass auch die Position der Hände zur Eingabe genutzt werden kann. Wie beim Tracker ist die Arbeit mit Datenhandschuhen ziemlich ermüdend und ungenau. Zudem müssen die verfügbaren Gesten erst erlernt werden, da sie nicht zwingend intuitiv sind. 2.11.5 Fazit Die Eignung eines 3D-Eingabegerätes hängt natürlich von der 3D-Umgebung ab. In einer immersiven Virtual-Reality-Umgebung (siehe Abschnitt 2.12) sind Tracker und Datenhandschuh die natürlichsten Eingabegeräte. Jedoch sind zur Zeit nur Desktop-VR-Umgebungen verbreitet. Der vielversprechendste Ansatz hierfür ist KAPITEL 2. GRUNDLAGEN 51 Abbildung 2.17: Datenhandschuh von 5DT definitiv der Spaceball, da der Aufwand für seine Verwendung sowie die Anschaffungskosten vergleichsweise gering sind. Spaceballs sind mittlerweile soweit etabliert, dass sowohl eine breite Produktpalette als auch eine gute Softwareunterstützung (z.B. durch Open Inventor) verfügbar ist. Trotz allem sind Maus und Tastatur immer noch die Standard-Eingabegeräte und werden es auch sicher auf absehbare Zeit bleiben. Dem sollte bei der Entwicklung von 3D-Anwendungen Rechnung getragen werden. 2.12 3D-Umgebungen Die Umgebung, in der eine dreidimensionale Darstellung stattfindet, lässt sich ziemlich konkret in drei Rubriken unterteilen. Zur Begriffsbestimmung werden diese nun kurz erläutert. Desktop Die klassische Desktop-Umgebung besteht aus einem planen Display und Eingabegeräten wie Maus oder Tastatur. Das gesamte System lässt sich auf einem Schreibtisch unterbringen, und trägt deshalb den Namen. Sinnvolle Erweiterungen für die Arbeit mit 3D-Anwendungen sind Spaceball, 3D-Display und Shutterbrille (oder evtl. auch Anaglyphenbrille). Da dies die häufigste (und günstigste) Arbeitsumgebung darstellt, wird der Fokus dieser Arbeit darauf liegen. Virtual Reality (VR) Mit solchen Systemen ist ein völliger Realitätswechsel möglich, weshalb man von Virtueller Realität spricht. Man verschmilzt förmlich mit der Software (Immersi- KAPITEL 2. GRUNDLAGEN 52 on). Voraussetzung dafür ist entweder ein HMD 2.10.4 oder ein begehbarer Raum mit Projektionswänden (CAVE – Cave Automatic Virtual Environment). Auf die Art wird ein Rundumblick ermöglicht. Ein Arbeiten mit Maus oder Spaceball ist hier unmöglich – Trackersysteme oder Datenhandschuhe sind deshalb unerlässlich. Augmented Reality (AR) Dieser Begriff heißt soviel wie „Erweiterte Realität”. Die Technik wird auch Mixed Reality genannt, da 3D-Darstellung mit echter Realität gemischt wird. Möglich wird dies durch transparente HMDs und präzise Trackersysteme. Das in 2.9.1 vorgestellte Projekt Studierstube befasst sich beispielsweise mit AR. 2.13 Fazit Die 3D-Technik ist ein sehr weites Feld. Dennoch sollte dieses Kapitel einen guten Überblick über den Stand von Forschung und Technik gegeben haben. Leider konnten aus Finanz- und Zeitgründen die meisten Technologien nicht getestet werden. Da nun alle nötigen Grundlagen gegeben sind, kann der Weg zum 3D-Renderer für GITK beschrieben werden. Kapitel 3 Open Inventor Wie in Abschnitt 2.8 angesprochen, können mit Hilfe von 3D-Toolkits dreidimensionale Anwendungen sehr einfach und schnell entwickelt werden. Für die Entwicklung des GITK-Renderers fiel die Entscheidung auf Open Inventor. Folgende Eigenschaften sprechen für diesen Schritt: • C++ API, sowie Schnittstelle für ANSI C über Wrapper-Funktionen • Objektorientiertes Design, vollständige Kapselung von OpenGL ist in Klassen • Low-Level-Zugriff auf OpenGL • Umfangreiche Dokumentation in Form von Büchern sowie im Internet • Weite Verbreitung und ständige Weiterentwicklung • Als Open Source verfügbar • Leicht durch eigene Klassen erweiterbar • Offenes Dateiformat Das in Kapitel 4 beschriebene User-Interface-Toolkit Qb nutzt nicht einfach Open Inventor, es ist eine Erweiterung dazu. Fast alle Qb-Klassen sind von InventorKlassen abgeleitet, viele Konzepte aus Open Inventor finden in Qb Verwendung. Dadurch kann Qb sehr flexibel in einem Inventor-Programm verwendet werden. Um den Zusammenhang mit Qb zu vermitteln, widmet sich dieses Kapitel also ganz dem Thema Open Inventor. 53 KAPITEL 3. OPEN INVENTOR 54 3.1 Architektur Open Inventor ist ein in C++ entwickeltes 3D-Toolkit. Neben Klassen für grafische Primitive bietet Inventor unter anderem folgende Werkzeuge für die Programmierung interaktiver 3D-Anwendungen: • Datenbasis – Datenstrukturen zur Beschreibung von Szenen und Szenenbestandteilen • Im- und Export des Inventor-Dateiformats • NodeKits – vordefinierte Szenenteile • Manipulatoren – spezielle NodeKits, welche Widgets zur direkten Objektinteraktion implementieren • Komponentenbibliotheken für verschiedene Fenstersysteme Abbildung 3.1: Architektur von Open Inventor Abbildung 3.1 zeigt, wie Open Inventor und Laufzeitumgebung zusammenhängen. Inventor bildet eine Schicht über OpenGL und dem verwendeten Fenstersystem, so dass sich der Programmierer nicht mit diesen beiden Dingen auseinandersetzen muss. Die Komponentenbibliothek bildet dabei die Schnittstelle zum Fenstersystem, oder besser gesagt, dem 2D-User-Interface-Toolkit. Denn immer noch wird die dreidimensionale Szene in einem zweidimensionalen Fenster dargestellt. An dieses Fenster gerichtete Geräteeingaben werden durch die Komponentenbibliothek an Inventor weitergeleitet. Sie bildet also die Schnittstelle zwischen Open Inventor und dem Nutzer. KAPITEL 3. OPEN INVENTOR 55 Wie man in Abbildung 3.1 erkennt, bildet die Datenbasis 1 die Grundlage innerhalb von Inventor. Datenbasis ist hier an abstrakter Begriff, der alle Datenstrukturen zur Aufnahme von Szenendaten umfasst. In den folgenden Abschnitten wird dazu näher eingegangen. Mit Hilfe von NodeKits und insbesondere Manipulatoren lassen sich bereits nur mit dem Dateiformat komplexe, animierte und vor allem interaktive 3D-Szenen erstellen. Diese Konzepte wurden übrigens in VRML übernommen. Im Zusammenhang mit Qb finden NodeKits momentan keine Verwendung, so dass auf eine nähere Erläuterung verzichtet wird. Manipulatoren werden ebenso nicht benötigt, da momentan keine 3D-Dokumente oder -Dokumentbestandteile in der GITKArchitektur unterstützt werden. Namensgebung Fast alle Klassen von Open Inventor besitzen das Präfix So. Dies steht für scene object. Im Folgenden wird dies häufig weggelassen. Desweiteren besitzen Basistypen das Präfix Sb, was für scene basic steht. Klassenhierarchie In dieser Abhandlung wird auf Inventor-Klassen häufig Bezug genommen. Für ein leichteres Verständnis befindet sich im Anhang B ein vereinfachtes Klassendiagramm. 3.2 Funktionsweise Open Inventor bildet eine Schicht über OpenGL. Wie kann jedoch das rein zustandsbasierte OpenGL mit dem objektorientierten Inventor in Einklang gebracht werden? 1 Teilweise wird in deutscher Literatur (z.B. [CLAUS97]) in diesem Zusammenhang der engl. Begriff scene database in „Szenendatenbank” übersetzt. Tatsächlich existiert in Inventor weder eine Datenbank im herkömmlichen Sinne, noch eine Klasse, die eine solche Funktionalität bietet. KAPITEL 3. OPEN INVENTOR 56 Szenengraph Grundlage der Datenbasis in Inventor bildet ein gerichteter, azyklischer Graph – der Szenengraph. Dieser besteht aus mindestens einem Knoten, der sogenannten Wurzel. Knoten sind dabei Instanzen von bestimmten Inventor-Klassen, z.B. Licht, Material, Rotation oder Kugel. Einige Knoten können weitere Knoten enthalten. Durch die Verkettung verschiedener Knoten wird eine hierarchisch strukturierte 3D- Szene aufgebaut. Traversierung Durch sogenannte Aktionen (engl. action) wird der Szenengraph rekursiv abgearbeitet. Je nach Aktion wird dabei ein bestimmtes Ziel erreicht, z.B. das Rendern der Szene, das Auffinden bestimmter Knoten oder die Berechnung der umgebenden Box. Dieser Vorgang wird Traversieren genannt. Traversiert wird zunächst von der Wurzel in Richtung Blätter, dann von links nach rechts (Tiefe-zuerst). Auch in Open Inventor gibt es wie in OpenGL Eigenschaften in Form von Zuständen (vgl. hierzu Abschnitt 2.7). Es wurden sogar zusätzliche Zustände eingeführt, wie beispielsweise Schriftart. Für jede Aktion verwaltet die Inventor-Datenbank einen traversal state (dt. Traversierungs-Zustand), eine Menge von Elementen und Parametern der jeweiligen Aktion. Während der Traversierung wird dieser Traversierungs-Zustand durch die Knoten verändert. Nur so kann sich eine Eigenschaft, die durch einen Knoten gesetzt wird, auf die folgenden Knoten auswirken. Das Rendern, also das Zeichnen der Szene, ist wohl die wichtigste Aktion von allen, denn dadurch werden explizit Befehle an OpenGL gesendet. Die verschiedenen Klassen von Knoten implementieren die zuständige Methode jeweils anders. Allen gemein ist, dass sie den Zustand von OpenGL auf irgendeine Weise ändern Beispielsweise könnte ein Material-Knoten die Grundfarbe auf rot setzen oder ein Rotationsknoten das Koordinatensystem um einen festgelegten Winkel drehen. Durch das Traversieren werden die Knoten in der fest vorgegebenen Reihenfolge nach abgearbeitet, so dass die OpenGL-Befehle ebenfalls in der richtigen Reihenfolge ausgeführt werden. Nachdem die Render-Aktion alle Knoten traversiert hat, befindet sich die Szene fertig gerendert im Frame-Buffer. Hier erkennt man einen prinzipiellen Unterschied: OpenGL arbeitet im immediate mode, das bedeutet, Anweisungen werden sofort ausgeführt und wirken sich direkt KAPITEL 3. OPEN INVENTOR 57 aus. Der Programmierer arbeitet direkt auf dem Frame-Buffer. Dagegen werden bei Inventor zunächst Datenstrukturen manipuliert – erst eine Render-Aktion bewirkt, dass die Änderungen auf den Frame Buffer angewandt werden. Diese Arbeitsweise wird als retained mode bezeichnet. Der Vorteil dabei ist, dass nur dann gerendert wird, wenn es auch veranlasst wird. Wirkungsbereich Viele Knoten beeinflussen durch Ändern von Zuständen die Darstellung nachfolgender Knoten. Dabei besitzen sie einen Wirkungsbereich (engl. scope) – ähnlich wie bei Symbolen in Programmiersprachen. Dieser beginnt immer an der Stelle, wo der Knoten traversiert wird. Das Ende des Wirkungsbereichs bestimmen Separatoren. Auf diese wird in Unterabschnitt 3.3.1 eingegangen. Ohne Separatoren erstreckt sich der Wirkungsbereich entweder bis zum nächsten Knoten gleichen Typs oder bis an das Ende des Szenengraphen. Im Folgenden tritt häufig der Begriff „aktuell” auf. Mit einer aktuellen Eigenschaft ist die Eigenschaft im gemeint, die durch den Wirkungsbereich eines Knotens und somit durch einen aktuellen Zustand gesetzt ist. Optimierung Durch die hohe Abstraktion kann Open Inventor langsamer sein als ein Programm, welches nur reines OpenGL nutzt. Dennoch optimiert Inventor automatisch, wo es möglich ist. Gerade für Programmierer, die sich nicht detailliert mit der OpenGLAPI auseinandersetzen wollen, ist dies von großem Nutzen. Eine zentrale Rolle bei Optimierungen spielt die umgebende Box (engl. bounding box). Sie repräsentiert die Lage eines Objekts im Raum. Ihre Berechnung wertvolle Zeit, weswegen sie gespeichert wird (caching). So muss Sie nur dann neu berechnet werden, wenn es nötig ist. Die Bounding Box wird unter anderem benötigt für die Optimierung des Renderns, Cullings 2 und Pickings3 . 2 Culling: Der Prozess, bei dem nicht sichtbare Flächen eliminiert werden. Das Auffinden von Objekten entlang eines Strahls von der Kamera durch einen Punkt auf der NearPlane des ViewVolumes. So kann z.B. das Objekt unter dem Maus-Cursor ermittelt werden. 3 Picking: KAPITEL 3. OPEN INVENTOR 58 Data-driven Werden Eigenschaften von Knoten im Szenengraphen verändert, welche die Darstellung der Szene beeinflussen, wird ein erneutes Rendern der Szene ausgelöst. Auf die Art wird stets der aktuelle Zustand des Szenengraphen visualisiert. So kann beispielsweise die Drehung eines Objekts animiert werden. Diese Arbeitsweise wird als data-driven bezeichnet. Es wird immer nur dann gerendert, wenn es wirklich nötig ist. Wenn sich der Szenengraph also nicht verändert, wird auch keine Rechenleistung für dessen Darstellung benötigt. Im Gegensatz dazu berechnen application-driven Szenengraph-APIs (wie z.B. SGI IRIS Performer) die Szene in einer Schleife ständig neu, was nur sinnvoll ist, wenn die verfügbare Rechenleistung maximal ausgenutzt werden soll. 3.3 Knoten Mit Knoten, Instanzen der Basisklasse Node, wird ein Szenengraph aufgebaut. Wie in Abschnitt 3.1 bereits erwähnt, besitzt Open Inventor eine Vielzahl verschiedener Knoten. Die wichtigsten werden nun im Folgenden erklärt. 3.3.1 Gruppen Knoten der Klasse Group und davon abgeleiteter Klassen dienen der Aufnahme weiterer Knoten. Nur durch Gruppenknoten kann eine hierarchisch gegliederte Szene erstellt werden. Die Basisklasse Group dient lediglich dazu, Knoten aufzunehmen und bei einer Aktion alle Kind-Knoten zu traversieren. Besonderes Verhalten führen erst die abgeleiteten Klassen ein. Separator Der Separator ist wohl der meistverwendete Gruppenknoten. Seine Aufgabe ist es, bei der Render-Aktion alle Attribute von OpenGL auf ihren jeweiligen Attributstapel zu legen (glPush...), um sie dann nach dem Traversieren der Kind-Knoten wiederherzustellen (glPop...). Auf diese Art wird erreicht, dass sich Änderungen am OpenGL-Zustand innerhalb des Separators nicht auf Knoten auswirken, die dem Separator folgen. Der Wirkungsbereich von Knoten innerhalb von Separatoren wird durch diese also begrenzt. KAPITEL 3. OPEN INVENTOR 59 Ähnliches tut der TransformSeparator, nur speichert er lediglich die aktuelle Transformationsmatrix. Er begrenzt also nur den Wirkungsbereich von Transformationen. Separatoren speichern die Bounding Box (caching, siehe Abschnitt 3.2). In bestimmten Situationen bringt es Geschwindigkeitsvorteile, diesem Umstand Beachtung zu schenken. Eine statische Geometrie sollte stets in einem eigenen Separator untergebracht werden. Dagegen sollte das Caching deaktiviert werden, wenn sich die enthaltene Geometrie ständig ändert. Normalerweise wird dies automatisch durch den Separator entschieden. Switch Der Switch ist, wie der Name schon sagt, ein Schalter. Seine Eigenschaft whichChild teilt ihm mit, welcher Kindknoten sichtbar ist. Alle anderen enthaltenen Knoten sind versteckt. Der Blinker ist ebenfalls ein Switch, nur wechselt er in einem wählbaren Intervall der Reihenfolge nach automatisch den sichtbaren Kindknoten. LevelOfDetail Komplexe Objekte mit vielen Details sind selbst für schnelle Computer eine Herausforderung. Die Animation einer Vielzahl solcher Objekte wird dann zu einem Problem, wenn die Anzahl der berechneten Bilder pro Sekunde nicht mehr ausreicht, eine für den Menschen als flüssig erkennbare Bewegung zu erzeugen. Oft ist es aber gar nicht nötig, alle Details anzuzeigen, vor allem immer dann, wenn das Objekt sehr klein ist oder im 3D-Raum weit entfernt liegt. Werden unnötige Details ausgeblendet, sinkt die benötigte Rechenleistung. Es gibt 2 Knoten, welche diese Aufgabe übernehmen: Ein LevelOfDetail enthält mehrere Varianten eines Objekts – mit abnehmenden Details. Der Knoten entscheidet anhand der projizierten Größe der umgebenden Box des Objekts, welche Variante verwendet werden soll. Ab welcher Größe gewechselt wird, lässt sich einstellen. Ein LOD verhält sich ähnlich, jedoch entscheidet dieser Knoten anhand der Entfernung des Objekts von der Kamera, welche Variante verwendet wird. Dieses Verfahren ist schneller, da hier nicht erst die umgebende Box berechnet werden muss. KAPITEL 3. OPEN INVENTOR 60 3.3.2 Transformationen Inventor-Objekte besitzen keine Eigenschaften wie Position oder Rotation. Stattdessen müssen vor ihnen im Szenengraph Transformationsknoten eingefügt werden. Hier die wichtigsten Transformationsknoten in Inventor: • Translation: Verschiebung des Koordinatensystems um den angegebenen Vektor • Rotation: Drehung des Koordinatensystems um einen bestimmten Winkel. Die Drehachse wird als Richtungsvektor übergeben. Somit verläuft sie stets durch den Koordinatenursprung. • Scale: Skalierung des Koordinatensystems. Für jede der 3 Achsen wird ein Faktor angegeben. Mit Hilfe dieser 3 Knoten kann jede beliebige Transformation erreicht werden. Zusätzlich gibt es aber noch weitere Transformationsknoten, die mehr Funktionalität bieten, aber seltener benötigt werden. Wie in Abschnitt 2.7 erklärt wurde, arbeitet OpenGL mit Transformationsmatrizen. Das selbe Konzept liegt in Open Inventor zu Grunde. Auch hier akkumulieren sich die Transformationen aufeinander folgender Transformationsknoten zu einer Gesamttransformation. Transformationen wirken sich nicht nur auf ein einzelnes Objekt aus. Stattdessen wird das zugrundeliegende Koordinatensystem transformiert. Alle Knoten im Wirkungsbereich der Transformation liegen in diesem Koordinatensystem. Transformationen werden deshalb meist in Separatoren untergebracht, um ihren Wirkungsbereich einzuschränken. So ergeben sich lokale Objektkoordinaten und globale Weltkoordinaten. Globale Weltkoordinaten gelten dann, wenn keine Transformation Einfluss hat. Bezieht man sich auf Koordinaten eines lokalen Koordinatensystems, muss man stets betrachten, ob sie im dem Koordinatensystem Gültigkeit haben, aus welchem Bezug genommen wird. Das Beispiel in Abbildung 3.2 zeigt die Sonne, welche im Ursprung des globalen Koordinatensystems steht. Durch eine Translation besitzt die Erde ein lokales Koordinatensystem. Von der Erde aus soll nun der Abstand zur Sonne berechnet KAPITEL 3. OPEN INVENTOR 61 werden. Die Koordinate (0,0,0) der Sonne kann dazu nicht herangezogen werden, denn damit wäre der Abstand zur Sonne Null. Diese Koordinate besitzt also keine Gültigkeit im Koordinatensystem der Erde. Abbildung 3.2: Lokale und globale Koordinaten 3.3.3 Eigenschaften Eigenschaften (engl. properties) verändern die Darstellung, beschreiben Metriken oder enthalten Metadaten. Knoten, die dadurch beeinflusst werden sollen, müssen im Szenengraph nach den Eigenschaften folgen. Genauso wie bei Transformationen bewirkt ein Separator hierbei, dass der Einfluss der Eigenschaft nur innerhalb des Separator gilt. Auf einige exemplarische Eigenschaften wird nun eingegangen. Texturen Mit Inventor ist es sehr einfach, Objekte mit Texturen zu versehen. Dem TexturKnoten wird entweder der Dateiname eines Bildes übergeben, oder direkt das Bild in Form eines Arrays von Pixeln. Coin3D kann mit Hilfe der Bibliothek simage etliche Bildformate laden, darunter das weit verbreitete JPEG-Format. Inventor sorgt automatisch dafür, dass die Textur auf Objekte im Wirkungsbereich projiziert wird (Mapping). Mit Hilfe von TextureCoordinate- und TextureCoordinateBinding-Knoten kann auf das Mapping Einfluss genommen werden. KAPITEL 3. OPEN INVENTOR 62 Zeichenstil Der DrawStyle-Knoten legt fest, wie ein Objekt gezeichnet wird. Standardmäßig werden Objekte mit dem aktuellen Material ausgefüllt. Man kann Objekte aber wahlweise auch ihr Drahtgittermodell (Linien) oder ihre Vertexe (Punkte) darstellen lassen. Es ist auch möglich, das Objekt unsichtbar zu machen. Dann wird es zwar nicht dargestellt, befindet sich aber immer noch in der Szene und reagiert auf Interaktion. Material Mit Hilfe von Material-Knoten kann die Farbe, Lichtwirkung und Transparenz von Objekten verändert werden. Standardmäßig sind Objekte grau und opak (deckend). Soll lediglich die Grundfarbe eines Objekts verändert werden, bietet sich mit dem BaseColor-Knoten der schnellste Weg an. Koordinaten Mit Hilfe von Coordinate3-Knoten werden Metriken für komplexe Formen (VertexShapes – siehe Unterabschnitt 3.3.5) bereitgestellt. Dies ist ein Array aus Koordinaten, aus denen durch VertexShape-Knoten Punkte, Linien oder Flächen generiert werden. 3.3.4 Kamera und Licht Um einen Szenengraphen zu rendern, ist stets eine Kamera nötig. Sie spezifiziert das zu rendernde View Volume – der sichtbare Ausschnitt der Szene (siehe 2.7) . Es gibt zwei Arten von Kamera, eine für orthographische und eine für perspektivische Projektion. Eine Kamera erfasst nur das, was in ihrem Wirkungsbereich liegt. Deshalb ist es üblich, die Kamera sehr nahe bei der Wurzel einzufügen. Es ist möglich, mehrere Kameras in den Szenengraph einzufügen. Nur dürfen sie nicht in den selben Wirkungsbereich fallen, da sonst nur die jeweils letzte Kamera eine Darstellung liefert. Befinden sich mehrere Kameras in einer darzustellenden Szene, werden alle durch Kameras erfasste Bilder in ein und denselben Framebuffer geschrieben. KAPITEL 3. OPEN INVENTOR 63 Dadurch lassen sich bestimmte Effekte erzielen, z.B. eine unveränderliche Geometrie mit Informationen vor der restlichen, animierten Szene darstellen. Dabei muss aber beachtet werden, dass auch derselbe Z-Puffer 4 verwendet wird, und sich so Objekte eigentlich separater Bilder gegenseitig verdecken oder schneiden können. Abbildung 3.3 zeigt zwei Würfel. Einer wurde von einer orthographischen Kamera, der andere von einer perspektivischen Kamera erfasst. Dennoch schneiden sich die Würfel. Abbildung 3.3: Zwei Kameras in einer Szene Neben einer Kamera wird in einer Szene noch mindestens eine Lichtquelle benötigt, sonst bleiben die gerenderten Objekte schwarz. Zwar kann das Material so verändert werden, dass ein Objekt von sich aus Licht emittiert, nur wirkt es durch die fehlende Schattierung nicht räumlich. Es gibt 3 Arten von Lichtquellen: • DirectionalLight (gerichtetes Licht) wirkt in der gesamten Szene aus ein und derselben Richtung. Die „Lichtstrahlen” sind also parallel. So kann z.B. Sonnenlicht simuliert werden. • PointLight (Punktlicht) strahlt von einem Punkt Licht in alle Richtungen aus. • SpotLight (Lichtkegel) wird auf eine Stelle gerichtet, so dass nur diese beleuchtet wird. In Open Inventor wird bisweilen kein Schatten erzeugt. Schatten kann den Realismus einer Szene beträchtlich erhöhen, nur stellt dies auch einen relativ hohen Rechenaufwand dar. Immerhin muss der Schatten von jeder Lichtquelle auf alle beleuchteten Objekte projiziert werden. Prinzipiell ist sind Schatten aber mit OpenGL möglich. 4 Im Z- oder Tiefenpuffer wird für jedes Pixel der Abstand von der Near Plane gespeichert, um Verdeckungen zu erkennen. KAPITEL 3. OPEN INVENTOR 64 Lichter besitzen genauso wie Kameras und Eigenschaften einen Wirkungsbereich im Szenengraph. Zur Berechnung der Beleuchtung eines Objekts werden nur die Lichter herangezogen, in deren Wirkungsbereich dieses Objekt liegt. 3.3.5 Formen Knoten der Klasse Shape (dt. Form) sind nötig, um überhaupt darstellbare Objekte zu erzeugen. Nur sie besitzen Geometrie und damit eine nicht-leere umgebende Box. Wie Objekte für den dreidimensionalen Raum aufgebaut sind, ist in Abschnitt 2.7 beschrieben. Die Klasse Shape enthält nicht nur Klassen, um Objekte aus grafischen Primitive zu generieren, sondern neben Grundkörpern auch Schrift und NURBS 5 . Grundkörper Würfel (Cube), Kugel (Sphere), Konus (Cone) und Zylinder sind Grundkörper. Mit ihrer Hilfe lassen sich sehr schnell komplexere Objekte zusammenbauen. Diese werden durch OpenGL mit den aktuellen Materialien und Texturen gerendert. Die Eigenschaften der Grundkörper wie z.B. Breite oder Radius lassen sich jeweils angeben. Komplexe Formen Mit Grundkörpern wird man schnell an seine Grenzen stoßen – komplexere Formen sind gefragt. Diesen ist gemein, dass man zuvor Koordinaten in den Szenengraph einfügen muss. Je nach Situation sind zusätzlich noch Normalen 6 , Materialien und Texturen nötig. Komplexe Formen können aus Punkten, Linien, NURBS und Flächen bestehen. Geschlossene Körper bestehen gewöhnlich aus Flächen, die nach der Anzahl ihrer Punkte (Vertexe) unterschieden werden. So heißen Körper aus Flächen • TriangleStripSet (Menge von Dreiecken) 5 NURBS: Non-Uniform Rational B-Splines bieten die Möglichkeit der Generierung gewölbter Flächen aus Kurven statt aus Strecken. 6 Normale: Richtungsvektor, der bestimmt, welche Seite einer Fläche vom Objekt nach Außen zeigt. KAPITEL 3. OPEN INVENTOR 65 • QuadMesh (Menge von Vierecken) • FaceSet (Menge von Polygonen) Zusätzlich werden komplexe Formen unterschieden in indiziert und nicht-indiziert. Indiziert bedeutet, dass dem Knoten, der die Form generieren soll, Indizes für die zu verwendenden (aktuellen) Koordinaten, Normalen, Materialien und Texturen übergeben werden müssen. Dies hat den Vorteil, dass man kontrollieren kann, welcher Vertex mit welchem verbunden wird und dabei welche Farbe besitzen soll. Nicht-indizierte Formen dagegen verarbeiten die aktuellen Koordinaten (usw.) Koordinaten, Normalen, Materialien und Texturen in der gegebenen Reihenfolge. Letzteres wirkt sich zwar positiv auf die Performance aus, verbraucht aber mehr Speicher, da Koordinaten mehrfach angegeben werden müssen. Flächennormalen erzeugt Inventor automatisch. Bei flächenbasierenden Objekten sollte dafür beachtet werden, die Vertexe in einer bestimmten Reihenfolge anzugeben. Ansonsten könnte man eventuell die Rückseite statt der Vorderseite der Fläche sehen, welche natürlich anders berechnet wird. Mit Hilfe eines ShapeHintsKnotens kann man Inventor mitteilen, dass die Vertexe im oder entgegen dem Uhrzeigersinn verbunden sind. Text Um im 3D-Raum Schrift darzustellen, könnte man sie einfach als Textur auf ein Objekt legen. Nur bräuchte man mehrere Auflösungen der Textur in einem LevelOfDetail, um die Lesbarkeit zu gewährleisten. Einfacher sind da echte 3DSchriften, denn sie bestehen wie TrueType-Schriften aus Vektoren. Das hat den Vorteil, dass sie beliebig skalierbar sind, ohne an Qualität zu leiden. Tatsächlich generiert Coin3D Schriften automatisch aus TrueType-Fonts. Unter Nicht-WindowsSystemen benötigt Coin3D dafür die FreeType-Bibliothek. Es gibt 3 Arten von Text-Knoten: 1. Text2 ist flach, wird durch Rotation und Skalierung nicht beeinflusst und ist damit stets zur Kamera ausgerichtet . 2. AsciiText ist wie Text2 flach, besitzt also keinerlei Seitenflächen. Jedoch wird AsciiText durch alle Transformationen beeinflusst und kann somit von allen Seiten betrachtet werden. KAPITEL 3. OPEN INVENTOR 66 3. Text3 ist echter, dreidimensionaler Text. Durch Extrusion 7 werden die Seitenflächen generiert. Text3 kann die Polygonzahl einer Szene beträchtlich steigern und somit die benötigte Rechenleistung erhöhen. Die Schriftart von Text-Knoten kann nur durch vorheriges Einfügen von FontKnoten geändert werden. 3.3.6 ClipPlanes Häufig kommt es vor, dass man nur einen Teil eines Objektes Darstellen will. Beispielsweise eine Halbkugel. Hier kommen ClipPlanes zum Einsatz. Dies sind Ebenen, welche die Szene zerschneiden. Die eine Seite der Ebene wird dargestellt, die andere nicht. Auch ClipPlanes besitzen einen Wirkungsbereich. Wichtigster Einsatz der ClipPlanes ist beim viewVolume der Kamera (sichtbarer Raum). Die near clipping plane blendet die Objekte aus, die sich hinter der Kamera befinden, die far clipping plane tut dies mit sehr weit entfernten Objekten. Letzteres geschieht aus Performance-Gründen. 3.4 Felder Ein wichtiges Merkmal von Inventor sind Felder, Instanzen der Klasse Field. Fast alle öffentlichen Eigenschaften der meisten Inventor-Objekte (Instanzen von FieldContainer) sind in Feldern gespeichert. Für jeden Datentyp gibt es eine eigene Feldklasse. Die meisten Datentypen sind durch Inventor bereits abgedeckt, falls erforderlich, können eigene implementiert werden. Felder eines Typs gibt es zumeist in 2 Ausführungen: Zum einen als Abkömmling der Klasse SoSField zur Speicherung eines einzelnen Wertes sowie als Abkömmling der Klasse SoMField zur Speicherung beliebig vieler Werte (Array). Gegenüber einfacher Datentypen wie z.B. Integer oder Float bieten Felder den Vorteil, dass auf ihre Änderung reagiert werden kann. Gegenüber Zugriffsmethoden bieten sie noch mehr Vorteile, auf die nun eingegangen wird. 7 Extrusion: Ein Polygon wird senkrecht zu seiner Fläche um einen gewissen Betrag in die dritte Dimension "gezogen". KAPITEL 3. OPEN INVENTOR 67 3.4.1 Dynamische Typinformationen Wie alle Inventor-Klassen, besitzen Felder Typinformationen, die zur Laufzeit ausgewertet werden können. Mit Hilfe solcher Instanzen der Klasse SoType kann man z.B. ermitteln, ob ein Objekt von einer bestimmten Klasse abgeleitet ist. C++ bietet zwar mit RTTI (Run Time Type Information) ähnliche Funktionen, aber aber nur für polymorphe Klassen und nicht für Basistypen wie z.B. Float. 3.4.2 Master-Slave-Verbindung Der wohl herausstechendste Vorteil von Feldern ist die Möglichkeit, sie untereinander zu verbinden. Dabei befindet sich ein Feld in der Rolle des Masters, das andere Feld in der Rolle des Slaves. Ändert sich der Wert des Masters, wird automatisch der Wert des Slaves auf diesen geändert. Erlaubt ist auch eine bidirektionale Verbindung, Inventor verhindert automatisch das Entstehen von Endlosschleifen. Bei der Verbindung von Feldern unterschiedlichen Typs werden automatisch Konverter dazwischengeschaltet. Dies sind Instanzen der Klasse FieldKonverter – ein Engine (dazu siehe Abschnitt 3.5). So lassen sich Felder jedes erdenklichen Typs miteinander verbinden. 3.4.3 Selektive Behandlung Das Verhalten jedes einzelnen Feldes kann separat beeinflusst werden: Ignorieren Felder setzen stets einen Default-Wert, wenn sie nicht explizit geändert werden. Will man, dass sich eine bestimmte Eigenschaft eines (Property-)Knotens nicht auswirkt, setzt man das Ignore-Flag. Bei der Traversierung des Szenengraphen wird das Feld damit völlig ignoriert. So kann beispielsweise ein Material-Knoten derart modifiziert werden, dass er nur die Transparenz ändert, während die diffuse Farbe so bleibt wie sie ist. KAPITEL 3. OPEN INVENTOR 68 Benachrichtigung steuern Felder benachrichtigen bei einer Änderung ihres Wertes automatisch alle Auditoren (notification). Auditoren sind stets die Container-Knoten, die das Feld enthalten, sowie alle verbundenen Slaves. Meist lösen die Auditoren dann weitere Benachrichtigungen aus, wodurch letztendlich z.B. ein Rendern der Szene erwirkt wird. In bestimmten Fällen ist das Deaktivieren dieses Mechanismus sinnvoll – beispielsweise, wenn mehrere Eigenschaften eines Objekts nacheinander verändert werden, und dabei für Berechnungen wichtige Werte unverändert bleiben sollen. Nach Abschluss aller Operationen bewirkt die Methode touch die Auslösung der Benachrichtigung. 3.5 Engines Engines, zu deutsch Motoren, sind keine Knoten. Ihr Zweck dient der Manipulation von Feldern. Dazu werden sie mit Feldern von Knoten oder weiteren Engines verbunden. Durch diese Verbindung sind Engines indirekt Teil des Szenengraphen. Engines besitzen ein oder mehrere Ein- und Ausgänge. Eingänge sind dabei Felder, die Ausgänge sind EngineOutputs – sozusagen Felder mit Schreibschutz. Je nach Art des Engines werden die Ausgangswerte anhand der Eingangswerte und weiterer Eigenschaften des Engines erzeugt. Somit sind Engines in Objekte gekapselte Funktionen. Mit Hilfe von Engines können Felder dynamisch verändert werden. Zum einen wird so die Typkonvertierung (automatisch) zwischen Feldern realisiert, zum anderen sind durch Kaskadierung von Engines komplexe Animationen und Berechnungen möglich. Auf einige exemplarische Engines wird nun eingegangen. Calculator Der Calculator (dt. Rechner), besitzt das Feld expression. Dies ist eine Zeichenkette mit einem oder mehreren mathematischen Ausdrücken, welche die Ausgangswerte berechnen. Der Calculator besitzt 8 Eingänge, 4 Ausgänge und 8 temporäre Variablen sowohl für Gleitkomma-Werte als auch Vektoren. Innerhalb der Ausdrücke können alle gängigen algebraischen Funktionen und mathematischen Kon- KAPITEL 3. OPEN INVENTOR 69 stanten sowie Fallunterscheidung verwendet werden. Das macht den Calculator zu einem sehr mächtigen Engine. Hier ein Beispiel für einen mathematischen Ausdruck eines Engines: Gegeben sind 2 Vektoren A und B, berechnet werden soll ihr Kreuzprodukt oA und die Länge oa des kürzeren Vektors. oA=cross(A,B); ta=length(A); tb=length(B); oa=ta<tb?ta:tb Man erkennt, dass Variablen für skalare Werte klein und für Vektoren groß geschrieben werden. Ausgabewerte besitzen das Präfix o, temporäre Werte das Präfix t. Die Variablen sind von A bis H benannt. Ausdrücke werden durch Semikolons getrennt. TimeCounter Mit einem TimeCounter können Animationen realisiert werden, indem dieser die Zeitachse liefert. Mit den Feldern min und max wird der Wertebereich festgelegt, mit step die Schrittweite und mit frequency die Anzahl der Zählzyklen pro Sekunde. Am Ausgang output kann der aktuelle Zählwert abgefasst werden. Dieser Wert kann beispielsweise Eingang eines Calculators bilden, dessen Ausgang wiederum mit einem Transformationsknoten verbunden ist. Die Transformation wird dann mit der Zeit verändert, und somit auch alle dadurch beeinflussten Knoten. ComputeBoundingBox Mit Hilfe dieses Knotens kann die umgebende Box eines Teilgraphen berechnet werden. An den EngineOutputs können Position und Dimensionen dieser Box abgefasst werden. Mit einem solchen Engine verbundene Felder werden logischerweise automatisch aktualisiert, wenn sich die umgebende Box ändert. 3.6 Speicherverwaltung Normalerweise müssen in C und C++ dynamisch erzeugte Objekte explizit wieder freigegeben werden. In Open Inventor muss darauf jedoch nur selten geachtet werden. Da durch die Datenstrukturen ein Objekt fast immer einem anderen untergeordnet wird, kann die Freigabe automatisch durch Referenzzählung geschehen. KAPITEL 3. OPEN INVENTOR 70 Immer wenn ein Zeiger auf ein Objekt gespeichert wird, wird der Referenzzähler des betroffenen Objekts inkrementiert. Dieser wird dekrementiert, wenn die Referenz nicht mehr benötigt wird, also spätestens bei Freigabe des referenzierenden Objekts. Alle Methoden von Inventor-Klassen halten sich an diese Konvention. Ist der Referenzzähler eines Objekts Null, wird der Destruktor des Objekts aufgerufen. Tatsächlich dürfen Instanzen von Inventor-Knoten nur dynamisch auf dem Heap angelegt werden – der Destruktor ist privat deklariert. So wird vermieden, dass der Destruktor doppelt aufgerufen wird. In bestimmten Fällen muss der Referenzzähler von Objekten manuell verändert werden. Beispielsweise ist bei einem Szenengraph die Wurzel das einige Objekt ohne Referenz. Wird ein Szenengraph traversiert, bei dem die Wurzel keine Referenzen aufweist, wird eine Fehlermeldung ausgegeben. Dieses Konzept erleichtert nicht nur die Programmierung, es ist sogar absolut nötig für geteilte Instanzen (engl. shared instances). Jedes Objekt benötigt Speicher im RAM. Soll in einer Szene ein Gegenstand mehrmals dargestellt werden, muss dieser nicht jedes mal instanziiert werden. Stattdessen wird er nur einmal instanziiert und im Szenengraph werden mehrere Referenzen auf ihn eingefügt. Die Gegenstände teilen sich also ein und die selbe Instanz. 3.7 Pfade Durch geteilte Instanzen (siehe Abschnitt 3.6) kann ein und derselbe Knoten mehrfach im Szenengraph vorkommen. Das hat zur Konsequenz, dass allein der Zeiger nicht mehr ausreicht, diese Knoten voneinander zu unterscheiden. Dazu dienen sogenannte Pfade (engl. path). Wie URLs oder XPath-Ausdrücke beschreiben Inventor-Pfade den Weg eines Knotens zur Wurzel. Referenzen aller auf diesem Pfad liegenden Knoten werden in einem Path-Objekt gespeichert. Da in einem Gruppenknoten ein Knoten mehrfach vorkommen kann, wird zusätzlich der Index jedes Vorkommens gespeichert. KAPITEL 3. OPEN INVENTOR 71 3.8 Dateiformat Szenengraphen können sowohl in externe Dateien exportiert als auch aus solchen importiert werden. Dies ermöglicht sowohl Trennung von Programmcode und Daten sowie die Weitergabe dieser Daten. Open Inventor bietet ein eigenes, aber offenes Dateiformat 8 an, sowie Methoden, um einen Szenengraph aus einer solchen Datei zu lesen bzw. in eine solche zu schreiben. Das Inventor-Format war übrigens Grundlage für die Entwicklung von VRML 1.0, daher ähneln sich die Formate sehr. Inventor ist in aktuellen Versionen in der Lage, VRML-Dateien (auch VRML 97 / VRML2) zu im- und exportieren. Hier sei angemerkt, dass trotz der in Inventor herrschenden hierarchischen Struktur das Dateiformat kein XML ist. Als Inventor entwickelt wurde, gab es schlichtweg den XML-Standard noch nicht. Inzwischen ist mit dem XML-Format X3D [@WEB3D] der Nachfolger von VRML97 verfügbar. Der X3D-Szenengraph ist fast identisch zu VRML97, so dass der Aufwand für eine entsprechende Im- und Exportfunktion relativ klein wäre. Inventor-Dateien können sowohl im ASCII- als auch im Binärformat (zwecks geringerem Speicherverbrauch) vorliegen. Das ASCII-Format ist sehr einfach gehalten und am weitesten verbreitet. Als Erstes ist der Header nötig, der das Dateiformat spezifiziert. Dies ist ein Kommentar, welches wie üblich mit einer Raute versehen wird. Durch den Header können verschiedene Versionen des Dateiformats erkannt werden. So wird z.B. auch erkannt, dass es sich um ein VRML-Dokument handelt. Eine Instanz einer Klasse von Knoten wird durch Angabe des Klassennamens sowie folgenden geschweiften Klammern erzeugt. In den geschweiften Klammer können Eigenschaften angegeben werden. Folgendes Beispiel erstellt einen Würfel mit der Kantenlänge 2: #Inventor V2.0 ascii Cube { width 2 height 2 depth 2 } Zeilenumbrüche sind optional. Knoten-Instanzen können einen Namen erhalten, falls der Knoten später referenziert werden soll. Ein Name wird durch DEF, eine Referenz durch USE eingeleitet. Instanzen der Klasse Group sowie abgelei8 Offen heißt, es kann beliebig erweitert werden. KAPITEL 3. OPEN INVENTOR 72 teten Klassen können weitere Knoten enthalten. Diese werden nach den Eigenschaften aufgelistet. Folgendes Beispiel erstellt 4 Kugeln, jeweils 2 in eine Gruppe geschachtelt: #Inventor V2.0 ascii Group { DEF kugel Sphere { radius 1 } USE kugel } Group { USE kugel USE kugel } Dieser Szenengraph enthält nun 4 Kugeln. Tatsächlich existiert im Speicher aber nur eine Instanz der Klasse Kugel – im Szenengraph befinden sich lediglich Zeiger auf ein und dieselbe Instanz (shared instance). Das hat zwar zur Folge, dass nach dem Rendern die 4 Kugeln identisch aussehen, dafür wird aber auch nur der Speicherplatz für eine Kugel auf dem Heap benötigt. In Inventor-Dateien können alle Eigenschaften gesetzt werden, die in Form von Feldern implementiert sind. Um ein Feld als Slave an ein anderes Feld als Master zu koppeln, wird das Symbol = verwendet. Wie in C wird auf die Komponente mit einem Punkt zugegriffen. Folgendes Beispiel erzeugt eine Kugel sowie einen Quader, dessen Seitenlänge vom Radius der Kugel abhängt. #Inventor V2.0 ascii Cube { width = DEF kugel Sphere { radius 2 }.radius USE kugel In diesem Beispiel kann man sehr gut erkennen, dass das Anlegen einer Instanz nicht unbedingt mit dem Einfügen einer Referenz in den Szenengraph verbunden ist. In Inventor gibt es keine Deklarationen, die Instanz kugel wird in dem Moment angelegt, wo sie gebraucht wird – beim Verbinden der beiden Felder. Die Kugel wird tatsächlich erst nach dem Quader an den Szenengraph gehangen. Diese Methode muss immer dann angewandt werden, wenn auf eine Eigenschaft eines erst später im Szenengraph befindlichen Knoten zugegriffen wird. Leider KAPITEL 3. OPEN INVENTOR 73 trägt dies nicht dem Verständnis einer Modelldatei bei. Ein weiteres, damit verwandtes Problem wird in Abschnitt 4.2.4 beschrieben. 3.9 Schematische Darstellung von Szenengraphen Mit zunehmender Komplexität einer Szene wird der erforderliche Programmcode sehr unübersichtlich. Verständlicher sieht das Open Inventor Dateiformat aus (siehe Unterabschnitt 3.8). Aber durch geteilte Instanzen und Feld-Verbindungen werden auch solche Dateien enorm kompliziert. Eine schematische Darstellung schafft hier Abhilfe. Ähnlich wie in UML- und Entity-Relationship-Diagrammen werden hier Knoten durch spezielle Symbole dargestellt und mit Linien und Pfeilen verbunden. Dabei werden Detail-Informationen weggelassen. Das Darstellungsformat entspricht keinem bestimmten Standard – seine Verwendung hat sich durch die Literatur ([WERN94a]) einfach etabliert. Eine Übersicht zu den einzelnen Knoten findet sich in Anhang A. Folgendes Beispiel illustriert ein solches Schema: Die Szene besteht aus einem Separator als Wurzel, einer Kamera, Licht und einem Würfel. Der Würfel soll sich mit der Zeit um seine vertikale Achse drehen. Dafür werden die Engines ElapsedTime (liefert Zeit) und ComposeRotation (berechnet Rotation) benötigt 9 . Im Inventor-Dateiformat sieht das folgendermaßen aus: #Inventor V2.0 ascii Separator { PerspectiveCamera {} DirectionalLight {} Separator { Rotation { rotation = ComposeRotation { axis 0 1 0 angle = ElapsedTime{}.timeOut }.rotation } Cube {} } } 9 Tatsächlich gibt es einen speziellen Knoten zur animierten Drehung von Objekten – dieses Beispiel verwendet bewusst Engines. KAPITEL 3. OPEN INVENTOR 74 Das dazu passende Schema sieht man in Abbildung 3.4. Abbildung 3.4: Schematische Darstellung einer Szene 3.10 Komponentenbibliothek Für das Rendern in OpenGL-Anwendungen ist stets eine Render-Area (Zeichenfläche) nötig, in welche die Szene gezeichnet wird. Das Anlegen einer solchen Render-Area variiert je nach verwendetem Fenstersystem und User-Interface-Toolkit. Für Open Inventor gibt es dafür Komponentenbibliotheken. Neben dem Anlegen der Zeichenfläche kümmern sie sich um die Weiterleitung von Ereignissen des Fenstersystems an Open Inventor. Je nach Art der Komponente kann die Szene auf verschiedene Weise betrachtet und mit ihr interagiert werden. Das originale SGI Inventor 1.0 enthielt lediglich Komponenten, die auf dem 2DUser-Interface-Toolkit Xt basierten (SoXt). Inzwischen gibt es weitere Komponentenbibliotheken für GTK (SoGtk), Qt (SoQt) und Win32 (SoWin). Da sie gleich lautende Methoden implementieren, können Anwendungen durch Makros unabhängig vom verwendeten Toolkit entwickelt werden. Nur so ist beispielsweise eine problemlose Portierung nach Windows möglich. Auf die Komponenten wird an dieser Stelle nicht genauer eingegangen. Es wird für Qb lediglich der ExaminerViewer (Betrachter) für Darstellung und Interaktion benötigt. KAPITEL 3. OPEN INVENTOR 75 3.11 Ereignisverarbeitung In Fenstersystemen werden Nutzereingaben durch ein flexibles Ereignissystem an die jeweiligen Anwendungen weitergegeben. Um in Inventor eine Interaktion zu ermöglichen, müssen diese Ereignisse in Inventor-Ereignisse übersetzt werden. Auf die Art besitzt Inventor eine einheitliche Ereignisverarbeitung, während verschiedene Fenstersysteme dies unterschiedlich realisieren. Abbildung 3.5 zeigt die Ereignis-Verarbeitungskette. Der Szenen-Manager bildet die Schnittstelle zwischen Inventor und der fenstersystem-spezifischen RenderArea. Er sorgt sowohl für die Ereignisverarbeitung als auch für das Rendering. Abbildung 3.5: Ereignis-Verarbeitung in Open Inventor Folgende Ereignisse werden in Open Inventor verarbeitet: • KeyboardEvent: Tastatur-Eingaben • MouseButtonEvent: Maus-Tastendruck • SpaceballButtonEvent: Tastendruck bei Spaceball-Eingabegeräten (siehe Abschnitt 2.11.2) • Location2Event: 2-dimensionale Bewegung, für gewöhnlich Maus-Bewegung • Motion3Event: 3-dimensionale Bewegung, z.B. mit einem Spaceball Diese Ereignisse befinden sich in einer Klassenhierarchie von SoEvent. Durch die Implementation eigener Event-Klassen können neue, nicht-unterstützte Eingabegeräte leicht in Inventor eingebunden werden. KAPITEL 3. OPEN INVENTOR 76 Innerhalb einer Inventor-Anwendung können Ereignisse auf unterschiedliche Weise weiterverarbeitet werden: 1. Einfügen von EventCallback-Knoten in den Szenengraph. Werden diese Knoten von der Aktion SoHandleEventAction traversiert, rufen sie die für das Ereignis angegebenen Callback-Funktionen auf. 2. Überschreiben der Methode, welche auf SoHandleEventAction reagiert. In eigenen Klassen ist dies der beste Ansatz. 3. Umgehen der Ereignis-Verarbeitung durch Inventor, indem der Render-Area eine Callback-Funktion für Ereignisse gegeben wird. Dadurch gelangt der Programmierer direkt an die (nicht-übersetzten) Ereignisse des Fenstersystems. Der Nachteil dieses Ansatzes ist natürlich, dass er fenstersystem-abhängig ist. Die Aktion SoHandleEventAction wird bei jedem eintreffenden Ereignis instanziiert und traversiert den Szenengraph. Jeder Knoten, der auf diese Aktion reagiert, erhält so die Informationen über das Ereignis. 3.12 Erweiterung von Open Inventor Wie in vorangegangenen Abschnitten bereits erwähnt, kann Inventor durch eigene Klassen erweitert werden. Das Buch Inventor Toolmaker [WERN94b] beschreibt ausführlich, wie dabei vorgegangen werden muss. Dennoch ist es eine enorm große Hilfe, Einblick in die Quelltexte von Open Inventor zu haben, wie es bei Coin3D der Fall ist. Der Ansatz bei der Entwicklung eigener Klassen ist stets der gleiche. Man erbt von der gewünschten Basisklasse, um dann mit Hilfe von Extender-Makros die Deklaration und Implementation automatisch um die (nicht-virtuellen) Methoden ergänzen zu lassen, die jede Inventor-Klasse besitzen muss. Für das Typ-System von Inventor muss stets noch die statische Initialisierungsmethode implementiert werden. Vor der ersten Verwendung jeder Klasse muss diese Methode aufgerufen werden. Diese registriert eine Klassenreferenz und den Namen für das Dateiformat. Hintergrund dieses Verfahrens ist unter anderem, dass KAPITEL 3. OPEN INVENTOR 77 beim Einlesen von Inventor-Dateien Instanzen dieser Klassen erzeugt werden sollen, Inventor dafür aber überhaupt erst einmal Zugriff auf diese Klassen (in Form von Speicheradressen) benötigt. Um in einer Anwendung (z.B. ein Betrachter für Szenen aus Inventor-Dateien) neue Erweiterungen nutzen zu können, müssen diese also zunächst durch Initialisierung eingebunden werden. Damit die Anwendung dafür nicht extra erweitert und neu kompiliert werden muss, kann die jeweilige Klasse automatisch dynamisch aus einer Bibliothek geladen werden. Diese Bibliothek muss lediglich genauso wie die Klasse heißen. Zudem muss der Bibliotheks-Pfad (Umgebungsvariable LD_LIBRARY_PATH) auf sie zeigen. 3.13 Leistungsvergleich zwischen OpenGL und Open Inventor Bei all der Komplexität von Open Inventor kommt die Frage auf, ob dadurch nicht sehr viel Rechenleistung vergeudet wird. Besonders bei animierten Szenen ist die Leistung entscheidend – für eine flüssige Bewegung sollten wenigstens 15 Bilder pro Sekunde (FPS – frames per second) dargestellt werden. OpenGL-Experten würden womöglich argumentieren, dass die Programmierung mit Inventor ineffizient ist und man mit reinem OpenGL durch Optimierungen (per Hand) mehr FPS bei gleicher Rechenleistung erreicht werden. In bestimmten Situationen ist das auch absolut richtig. Ein exakt auf seine Aufgabe zugeschnittener Code ist logischerweise effizienter. Fakt ist aber, dass Open Inventor wesentlich mehr Funktionalität bietet. Genannt seien hier nur der Szenengraph und die Ereignisverarbeitung. Das objektorientierte Design verlangt dafür natürlich zusätzliche Rechenleistung für eine Vielzahl von Methoden-Aufrufen. OpenGL bietet dagegen lediglich Funktionen zur Darstellung – die restliche Funktionalität bleibt dem Programmierer überlassen. Bei einem komplexen Programm (z.B. ein 3D-Spiel) müsste also diese Funktionalität doch wieder in Objekte gekapselt werden, womit man im Endeffekt bei einem ähnlichen Konzept wie Open Inventor angelangen würde 10 . Das Design einer konkreten Anwendung entscheidet also über Leistung und Flexibilität, so dass OpenGL und Inventor keinesfalls direkt miteinander verglichen werden können. Ein Anwendungs-Programmierer ohne tiefere OpenGL-Kenntnisse geht mit Inven10 Im Allgemeinen wird dies durch den Begriff Grafik-Engine beschrieben. KAPITEL 3. OPEN INVENTOR 78 tor kaum Kompromisse ein. Immerhin kümmert sich Inventor um eine Vielzahl von Problemen, die nur schwer effizienter zu lösen sind. Zudem ist die Entwicklungszeit mit Inventor wesentlich geringer, da auf sehr viel Vorhandenes aufgebaut werden kann. 3.14 Fazit Open Inventor ist zwar eine Komplexe, aber sehr verständlich strukturierte API. In diesem Kapitel wurden nur die benötigten Aspekte erläutert – für detaillierte Ausführungen wird an dieser Stelle auf die Dokumentation der jeweiligen OIVImplementation (z.B. [@COIN]) oder auf einschlägige Foren (z.B. [@LIST]) verwiesen. Die Erweiterbarkeit von Open Inventor wird sich bei der Entwicklung des 3D-UI-Toolkits Qb als unschätzbare Eigenschaft erweisen. Kapitel 4 Realisierung des Toolkits Für die Umsetzung eines GITK-Renderers, der einen Dialog aus dreidimensionalen Widgets generiert, ist zunächst eine Bibliothek nötig, die solche Widgets anbietet. Zudem werden Instrumente für eine elegante Programmierung benötigt, beispielsweise Objekte für die Verwaltung von Widgets oder Mechanismen für die Verknüpfung mit Programmlogik. Solch ein Funktionsumfang wird in einem UserInterface-Toolkit zusammengefasst. Wie in Abschnitt 2.3.1 beschrieben, besitzen solche Toolkits eine sehr breite Auswahl an verschieden Widgets – für jeden Zweck das Passende. Ein GITK-Render stellt diese Anforderung jedoch nicht – hierbei kommt es nur darauf an, dass jedes abstrakte Schnittstellenobjekt eine geeignete Repräsentation erhält. Ein GITK-Renderer für die 3D-Domäne stellt dennoch Anforderungen an das verwendete Toolkit: 1. Widgets sind dreidimensional, das heißt, sie können von allen Seiten betrachtet und bedient werden. 2. Widgets können beliebig im Raum angeordnet werden. 3. Es wird OpenGL unterstützt. 4. Das Toolkit lässt sich direkt mit den Programmiersprachen C oder C++ verwenden. 1 1 Mittlerweile existieren Wrapper für andere Programmiersprachen (z.B. Java), dies war zu dem Zeitpunkt der Entscheidung jedoch nicht der Fall. 79 KAPITEL 4. REALISIERUNG DES TOOLKITS 80 5. Der Stil der Darstellung kann angepasst werden. Im Rahmen des Projekts Studierstube (Abschnitt 2.9.1) wurden 3D-Widgets implementiert, die fast diesen Forderungen genügen. Nur war zum Zeitpunkt der Recherche die API-Dokumentation nicht erreichbar. Auch ist nicht klar, wieviel Aufwand nötig gewesen wäre, die Widgets zu verwenden, denn sie sind Teil einer API für die Nutzung in Augmented Reality Umgebungen. Da ansonsten keine Alternativen gefunden wurden, musste das passende Toolkit selbst entwickelt werden. Das Projekt wurde Qb getauft – eine Anspielung auf Trolltechs Qt, sowie auf den englischen Begriff „Cube”, der auf die kubischen Eigenschaften des geplanten Einsatzes weist. Dieses Kapitel beschreibt ausführlich das 3D-User-Interface-Toolkit Qb: Von seiner Architektur über Konzepte hin zur konkreten Anwendung. Im Anschluss wird zudem noch ein Ausblick über mögliche Weiterentwicklungen des Toolkits gegeben, denn die Ausschöpfung aller Möglichkeiten würde den Zeitrahmen dieses Projekts bei Weitem sprengen. 4.1 Architektur Wie bereits zu Beginn des Kapitels 3 erwähnt, basiert Qb vollständig auf dem 3DToolkit Open Inventor. Der Schritt, Inventor um Klassen zu erweitern, wurde durch seine offene Architektur förmlich aufgedrängt. So lässt sich Qb transparent in bestehende Inventor-Applikationen einbinden. Auf eine Schnittstelle zu Standard-C wurde allerdings verzichtet, da sie für den GITK-Renderer nicht benötigt wird. Als Basis wurde Coin3D von SIM als OIV-Implementation gewählt, da sie als Open Source Version unter der LGPL-Lizenz verfügbar ist. Qb funktionierte zuletzt mit Coin3D in der CVS-Version 2.2.1 vom 09.04.2004 – es ist möglich, dass Qb mit den OIV-Implementationen von SGI und TGS oder neueren Versionen von Coin3D nicht korrekt zusammenarbeitet. Abbildung 4.1 zeigt den Zusammenhang der Komponenten, welche hier mitwirken. Eine 3D-Anwendung benötigt ein Fenster, in dessen RenderArea die Szene gezeichnet wird. Als Komponentenbibliothek für die Anbindung an das Fenstersystem wurde SoQt von SIM gewählt. Der Grund dafür war, dass SoQt neben SoWin die meist verwendete Bibliothek ist und daher aktiv weiterentwickelt wird. Prinzipiell ist es möglich, die Schnittstelle zur Komponentenbibliothek mit Hilfe von KAPITEL 4. REALISIERUNG DES TOOLKITS 81 Abbildung 4.1: Architektur von Qb Makros flexibel zu gestalten. Bei der Entwicklung des GITK-Renderers zeigte sich aber, dass einige Methoden, welche durch SIM zusätzlich eingeführt wurden, sogar dringend benötigt werden. Das Inventor-Dateiformat gibt die Möglichkeit, Qb anzupassen, ohne es neu kompilieren zu müssen. Zusätzlich können dadurch Qb-Dialoge in separaten Dateien beschrieben werden. Letzteres spielt für den GITK-Renderer zwar eine eher untergeordnete Rolle, zu Testzwecken ist dies jedoch enorm praktisch. In anderen Anwendungen, welche Qb nutzen, kann mit Hilfe des Dateiformats Anwendungslogik und Design der grafischen Nutzungsschnittstelle elegant getrennt werden. Auch hier könnte ein Nutzer Modifikationen ohne Neukompilierung vornehmen. Open Inventor bietet neben den Klassen für die 3D-Programmierung einen eine Menge von Hilfsklassen für alltägliche Programmieraufgaben. Zum Beispiel implementiert eine String-Klasse Zeichenketten-Operationen. Es muss sich nicht mehr um die Alloziierung und Freigabe von Speicher gekümmert werden. Qb verwendet deshalb weitestgehend die angebotenen Inventor-Klassen. 4.2 Grundlegende Konzepte Qb soll eine Inventor-Erweiterung darstellen. Deshalb ist es sinnvoll, weitestgehend bestehende Inventor-Konzepte zu verwenden. Die nötigen Grundlagen wurden bereits in Kapitel 3 erläutert, dieser Abschnitt beschreibt, wie die InventorKonzepte in Qb Verwendung finden. KAPITEL 4. REALISIERUNG DES TOOLKITS 82 4.2.1 Szenengraph Qb-Widget-Klassen sind von der Inventor-Klasse Separator (siehe 3.3.1) abgeleitet, sie sind also Gruppen-Knoten. Ein Qb-Dialog wird also wie eine gewöhnliche Inventor-Szene aufgebaut. Der Szenengraph bildet dabei implizit eine hierarchische Dialogstruktur, Schachtelung wird durch die Funktionalität der Separatoren erreicht. Actions (siehe 3.2) bewirken bei Qb-Knoten das selbe wie bei Inventor-Knoten. Es kann z.B. mit einer SearchAction nach Widgets gesucht werden oder mit GetBoundingBoxAction die umgebende Box einer Gruppe von Widgets berechnet werden. Qb-Widgets sind ihrerseits kleine Unter-Szenengraphen. Durch Inventor-Knoten wird ihre graphische Repräsentation beschrieben. Diese Daten sind durch private Deklaration vor dem Programmierer verborgen, können jedoch angepasst werden, wie es in Unterabschnitt 4.2.4 erläutert wird. 4.2.2 Felder Sämtliche Eigenschaften von Qb-Widgets sind als Felder realisiert (siehe Abschnitt 3.4). Getter- und Setter-Methoden entfallen somit. Auf eine Wertänderung wird durch den Benachrichtigungsmechanismus von Inventor reagiert. Ändert sich eine Eigenschaft, werden ebenfalls alle Objekte entlang des Pfades informiert, so dass sie nötige Maßnahmen ergreifen. Beispielsweise führt eine Größenänderung innerhalb eines verschachtelten Dialogs dazu, dass umgebende Widgets ihre Größe sowie ihre Lage anpassen. Für die Kommunikation zwischen Objekten spielen Felder eine noch viel größere Rolle. Aber zuvor wird an dieser Stelle kurz erläutert, wie dies in anderen Toolkits realisiert wird. In der Entwicklungsumgebung Delphi besitzen Widgets z.B. für das MausklickEreignis sowohl eine Methode click(), sowie einen Funktionszeiger onClick. Eine onClick zugewiesene Funktion wird bei einem Klick-Ereignis (was dem Aufruf von click() entspricht) aufgerufen. Nachteilig ist sowohl, dass der Funktionstyp exakt dem Typ von onClick entsprechen muss, als auch, dass nur eine einzige Funktion zugewiesen werden kann. In Delphi existiert keine andere Möglichkeit, auf eine Objektänderung zu reagieren. KAPITEL 4. REALISIERUNG DES TOOLKITS 83 Einen anderen Ansatz verfolgt z.B. Trolltech Qt mit dem Signal-Slot-Mechanismus. Bereits in der Klassendeklaration werden Signale und Slots deklariert. Das entspricht nicht dem Standard-C++, so dass ein spezieller Meta-Object-Compiler (MOC) nötig ist. Wie Felder in Inventor können Signale als Master an Slots als Slaves gekoppelt werden. Der Slot reagiert automatisch auf eine Änderung des Signals. Tatsächlich sind Slots jedoch gewöhnliche Methoden, der Typ des Arguments muss dem Typ des Signals entsprechen. Durch Felder werden diese Nachteile in Qb elegant aus dem Weg geräumt. Sie können beliebig untereinander verbunden werden, ihr Typ wird automatisch konvertiert und es wird kein spezieller Präcompiler benötigt. Neben Eigenschaften sind auch Ereignisse von Widgets als Felder realisiert. Eine Aktivität an einem Ereignisfeld (Wertänderung oder Trigger2 ) wird als Ereignis aufgefasst, verbundene Felder (Slaves) werden automatisch darüber informiert – egal ob sie selbst ein Ereignis oder eine Eigenschaft repräsentieren. Die so erreichte Flexibilität ist enorm. Beispielsweise kann das keyDown-Ereignis (Tastendruck) eines Widgets mit der Eigenschaft caption (Beschriftung) eines anderen Widgets verbunden werden. Ein Tastendruck würde bewirken, dass die Beschriftung den Tastencode anzeigt. Da Felder bereits in Inventor-Dateien verknüpft werden können, kann das Verhalten der Nutzungsschnittstelle völlig von der Anwendungslogik getrennt werden. Natürlich darf nicht verschwiegen werden, dass der hohe Abstraktionsgrad mehr Rechenleistung erfordert, als beispielsweise eine Realisierung in Delphi. Das ist der Preis für die Flexibilität. Andererseits benötigen Ereignisse selbst in Qb relativ wenig Rechenleistung, der Großteil wird von der 3D-Darstellung und der eigentlichen Anwendunglogik in Anspruch genommen werden. 4.2.3 Sensoren Mit Feld-Verbindungen kann bereits ein Großteil des Anwendungsverhaltens umgesetzt werden. Dennoch muss die Anwendungslogik in „hartem” C++ programmiert werden. Sensoren der Klasse SoFieldSensor bilden dabei die Schnittstelle zwischen Feldern und Anwendungscode, indem sie mit einem Feld und einer Callback-Funktion verbunden werden. Eine Benachrichtigung vom Feld hat den Aufruf der Callback-Funktion zur Folge. Ein Argument dieser Funktion ist das 2 Trigger bedeutet zu Deutsch Auslöser. Ein Feld ohne Wert ist ein Trigger. Es dient lediglich zum Auslösen von Benachrichtigungen. KAPITEL 4. REALISIERUNG DES TOOLKITS 84 Feld-Objekt. Durch Typinformationen und Typkonvertierung kann flexibel auf verschiedene Feld-Typen reagiert werden. Angenommen, eine Anwendung beschreibt einen Qb-Dialog in einer InventorDatei. Zur Laufzeit würden nach dem Import der Datei Widgets mit bestimmten Namen gesucht werden, um sie mit Sensoren zu verbinden. Mehr muss nicht getan werden, um die Kommunikation zwischen Anwendung und Qb-Nutzungsschnittstelle zu realisieren (Abb. 4.2). Abbildung 4.2: Sensoren als Schnittstelle zwischen Dialog und Anwendung 4.2.4 Inventor-Dateien Durch das Inventor-Dateiformat wird die Anpassbarkeit von Qb gewährleistet. QbDialoge sind Inventor-Szenen, sie können also problemlos im Inventor-Dateiformat beschrieben werden. Feldverbindungen zwischen Widgets können bereits hier hergestellt werden. Ohne Neukompilierung der Anwendung kann das Layout ihrer Nutzungs-Schnittstelle verändert werden. Während Dialogbeschreibungen im Inventor-Format für den GITK-Renderer von geringer Relevanz sind, haben Inventor-Dateien innerhalb des Qb-Toolkits eine enorm große Bedeutung. Durch sie werden sowohl visuelle Daten als auch Verhaltensweisen beschrieben – im Folgenden auch Modell genannt. Die meisten Qb-Klassen verwenden intern Inventor-Knoten sowohl für ihre Darstellung als auch ihr Verhalten. Anstatt diese Daten fest zu kodieren, werden sie dynamisch aus Dateien mit fester Namensgebung geladen. Indem diese Dateien in separaten Verzeichnissen gehalten werden, können verschiedene Stile (engl. style) realisiert werden. Dazu muss vor dem Laden lediglich der Suchpfad der ImportKlasse SoInput modifiziert werden. KAPITEL 4. REALISIERUNG DES TOOLKITS 85 Die Daten der Modell-Dateien können hierbei nicht völlig unabhängig von den Feld-Daten innerhalb der Qb-Klassen sein. Beispielsweise besitzt der QbPushButton eine Beschriftung und abgeschrägte Kanten (Phase). Dieses Design wird durch das Modell aus seinen Inventor-Dateien festgelegt. Wird das Feld width (dt. Breite) einer PushButton-Instanz verändert, kann das Modell nicht einfach horizontal skaliert werden, denn die Beschriftung wäre verzerrt und die Phasen links und rechts würden anders als die Phasen oben und unten dargestellt werden (siehe Abb. 4.3 oben). Stattdessen muss sich Modell mit den Feldern der eigentlichen Instanz (this) verbinden. Im Falle des PushButtons werden die Koordinaten der Phasen und Beschriftung durch Calculator-Engines (siehe 3.5) dynamisch aus den Feldwerten der PushButton-Instanz berechnet. Eine Änderung des Feldes width hat so auch eine Änderung der Modell-Koordinaten zur Folge. Im unteren Teil der Abbildung 4.3 erkennt man, dass sowohl Beschriftung als auch Phasen korrekt dargestellt werden. Abbildung 4.3: Skaliertes und angepasstes Modell eines PushButtons Normalerweise ist der Name eines Knotens nur lokal, während des Einlesens der Datei, bekannt. So entsteht ein Gültigkeitsbereich, was zur Konsequenz hat, dass man in einer Inventor-Datei nur die Knoten referenzieren kann, die auch erreichbar sind. Die Felder der Qb-Instanz sind für das Modell zunächst allerdings unerreichbar, da der Knoten this nicht in der Inventor-Datei definiert ist. Um aus einem Programm heraus die Möglichkeit zu geben, in einer Datei Bezug auf einen Knoten bzw. Eigenschaften außerhalb dieser Datei zu nehmen, gibt es 2 Wege. Zum einen können Eigenschaften global verfügbar gemacht werden. Diese werden von Inventor dann intern verwaltet. Dieser Ansatz entspricht jedoch nicht den objektorientierten Prinzipien Kapselung und Sichtbarkeit. Sinnvoller ist es, dem Import-Objekt SoInput mitzuteilen, welche Knoten es unter welchem Namen in der Datei zugänglich macht. Nun ist die Qb-Instanz aus Sicht KAPITEL 4. REALISIERUNG DES TOOLKITS 86 des Modells unter dem Namen „this” bekannt und kann genutzt werden. Problematischer ist die Modifikation der Qb-Instanz durch das Modell. Beispielsweise gibt es beim QbPushButton die Eigenschaft autoSize. Ist dieser Modus aktiv, wird die Breite des Knopfes automatisch an die Breite der Beschriftung angepasst. Da die Beschriftung durch das Modell realisiert wird, muss sich dieses auch um die Breitenänderung der PushButton-Instanz kümmern. In Inventor können Felder von Knoten nur an der Stelle gesetzt bzw. verbunden werden, wo der Knoten definiert wird. Eine Routing-Anweisung wie in VRML2 gibt im Inventor-Format der Version 2.1 nicht. Durch sie könnten an jeder Stelle Felder verbunden werden. Auf eine Anfrage hin haben die Entwickler von Coin3D in Aussicht gestellt, dies eventuell in eine neue Version ihres eigenen InventorDateiformats einfließen zu lassen[@LIST]. Bis dahin ist diese Funktionalität durch einen Trick realisiert. Inventor ist imstande, unbekannte Knoten zu im- und exportieren. Klassen dieser Knoten sind zum Zeitpunkt des Datei-Imports nicht initialisiert. Unbekannte Knoten besitzen zwar keinen Einfluss auf den Traversierungs-Zustand, bieten ansonsten jedoch die Funktionalität gewöhnlicher Knoten. Ein unbekannter Knoten wird nur dann fehlerfrei importiert, wenn seine Felder explizit in der Knoten-Definition deklariert wurden. Muss ein Modell Felder der Qb-Instanz ändern, definiert es einen unbekannten Knoten des Typs QbNodeRef 3 . Felder, die in der Qb-Instanz verändert werden sollen, werden unter gleichem Namen deklariert. Diese können nun nach Belieben gesetzt oder als Slave verbunden werden. Beim Import der Inventor-Datei wird dann nach QbNodeRef-Knoten gesucht. Alle Felder, die in der Qb-Instanz existieren, werden als Slaves mit den entsprechenden Feldern des QbNodeRef-Knotens verbunden und stehen von nun an unter dem Einfluss des Modells. Zur Verdeutlichung ist die Einbindung von Modellen in Abbildung 4.4 nocheinmal schematisch dargestellt. 4.3 Der WidgetController Im Allgemeinen bieten UI-Toolkits ein zentrales Objekt, welches grundlegende Aufgaben übernimmt, wie beispielsweise die Ereignis-Schleife (engl. event loop) 3 Tatsächlich existiert die Klasse QbNodeRef nicht. KAPITEL 4. REALISIERUNG DES TOOLKITS 87 Abbildung 4.4: Modell-Einbindung in Qb oder Verwaltung der Hauptfensters. Meist heißt dieses Objekt Application (z.B. in Qt QApplication oder in Delphi TApplication). Bei Qb bietet bereits das Fenster mit der RenderArea die Event-Schleife. Dennoch wird in Qb ein zentrales Objekt benötigt – der WidgetController. Jedes QbWidget benötigt einen WidgetController in seinem Pfad (siehe Abschnitt 3.7) sowie eine Referenz auf diesen. Die Referenz muss nicht jedem Widget einzeln zugewiesen werden. Genauso wie die Qb-Widgets ist der WidgetController von der Klasse SoSeparator abgeleitet. Die Methode addChild wurde derart überschrieben, dass die Referenz automatisch gesetzt wird, wenn ein Widget hinzugefügt wird. Der WidgetController übernimmt folgende Aufgaben: Ereignis-Verarbeitung Wie Inventor Ereignisse verarbeitet, wurde in Abschnitt 3.11 erläutert. Der günstigste Ansatz zur Reaktion auf Ereignisse ist die Implementation der Methode handleEvent. Bei der Traversierung durch HandleEventAction wird diese aufgerufen. Implementiert nun jedes einzelne Widget diese Methode, würde Rechenleistung verschwendet werden, denn meist betreffen Ereignisse lediglich das eine fokussierte Objekt. Immerhin müssen alle verschiedenen Ereignisse in einer Fallunterscheidung ausgewertet werden. Deshalb übernimmt der WidgetController diese Aufgabe. Je nach Ereignis wird bei dem betroffenen Widget die jeweilige Ereignis-Methode aufgerufen. KAPITEL 4. REALISIERUNG DES TOOLKITS 88 Fokus verwalten In einer Anwendung kann immer nur ein UI-Objekt den Eingabefokus besitzen. Erhält ein Widget den Fokus (z.B. durch Anklicken), ist es zwingend erforderlich, dem bisher fokussierten Widget dies mitzuteilen. Da Qb-Widgets für gewöhnlich nichts voneinander wissen, geschieht das indirekt über den WidgetController. Dieser besitzt eine Referenz auf das aktuell fokussierte Objekt. Erhält er die Botschaft, dass ein Objekt den Fokus erhalten hat, entfernt er den Fokus vom referenzierten Objekt und ändert dann diese Referenz auf das neue Objekt. Eine Anwendung sollte nur einen WidgetController besitzen, ansonsten könnten mehrere Widgets den Fokus besitzen, was kaum Sinn macht. Hover verwalten Jedes Widget besitzt die Eigenschaft Hover – ein boolescher Wert, der aussagt, ob sich der Mauscursor über dem Objekt befindet oder nicht. Wie beim Fokus kann nur ein Widget den Hover besitzen. Der WidgetController verwaltet den Hover. 4.4 Widgets Wie bereits am Anfang des Kapitels erwähnt, benötigt das Toolkit hinsichtlich seiner Verwendung in einem GITK-Renderer nur einen kleine Auswahl an Widgets. Konkret wurden bis zu diesem Zeitpunkt folgende Widgets implementiert: • QbPushButton: Knopf mit einer Aufschrift, der eine Aktionen auslösen kann. • QbToggleButton: Schalter mit den Zuständen aktiv oder inaktiv. • QbRadioButton Schalter, der stets in einer Gruppe aus mindestens 2 Elementen verwendet wird, wobei immer nur ein Element aktiv ist. • QbLineEdit: Eingabefeld für beliebige Zeichenketten ohne Zeilenumbruch • QbBox: Container für beliebige Anzahl von Widgets, die wahlweise horizontal oder vertikal angeordnet werden. • QbLabel: Beschriftung KAPITEL 4. REALISIERUNG DES TOOLKITS 89 • QbSpacer: Abstandshalter Die letzten drei Widgets bieten zwar keine Interaktion für den Nutzer, sind jedoch für den Aufbau eines Dialoges unerlässlich. Alle Widgets orientieren sich an zweidimensionalen Pendanten, wie man an der Namensgebung erkennen kann. Dennoch besitzen sie weitestgehend eine dreidimensionale Geometrie und können somit aus beinahe beliebigen Winkeln verwendet werden. Auch im 3D-Raum werden Qb-Widgets auf einer zweidimensionalen Ebene zu einem Qb-Dialog zusammengefügt. Dieser Dialog kann dann jedoch völlig frei platziert werden. Alle Qb-Widgets sind Teil einer Klassenhierarchie (Abb. 4.5), welche sich ungefähr an der Struktur anderer UI-Toolkits orientiert. Sie gewährleistet sowohl eine leichte Handhabung als auch mögliche spätere Erweiterungen des Toolkits . Die einzelnen Klassen werden in den folgenden Unterabschnitten erläutert. Abbildung 4.5: Klassenhierarchie von Qb 4.4.1 QbWidget Alle Widgets sind von der abstrakten Basisklasse QbWidget abgeleitet, welche grundlegende Funktionalität bietet. Die Klasse QbWidget wiederum ist von der Inventor-Klasse SoSeparator abgeleitet – hier befindet sich also die Schnittstelle zwischen Inventor und Qb. Die Verwendung des Separators als Basisklasse hat mehrere Gründe. Zum einen muss ein Widget in der Lage sein, Knoten aufzunehmen. Während dies für die meisten Widgets nur intern eine Rolle spielt, ist es für die QbBox ziemlich wichtig. SoGroup (Basisklasse von SoSeparator) implementiert bereits alles Nötige, um sogar im Inventor-Dateiformat Knoten aufnehmen zu können. Durch Mehrfachvererbung hätte QbBox diese Funktionalität zwar direkt von SoGroup erben können, nur hätte sich dies nicht mit dem Typsystem von Inventor vertragen. KAPITEL 4. REALISIERUNG DES TOOLKITS 90 Zum anderen besitzt jedes Widget intern einen Teilgraph zur Darstellung seiner Geometrie. Damit ein Widget den Traversierungszustand für nachfolgende Knoten nicht beeinflusst, wird die Funktionalität des Separators benötigt. Mancheiner, der sich näher mit Open Inventor auskennt, könnte an dieser Stelle fragen, warum nicht NodeKits als Ausgangsbasis verwendet wurden – immerhin bieten sie bereits die nötige Funktionalität für interne Teilgraphen. Ausschlaggebend für die Nichtverwendung waren letztendlich Probleme, im Inventor-Dateiformat NodeKitListParts auf die Art mit Elementen zu füllen, wie es bei SoGroup-Derivaten möglich ist. Da hierbei selbst die Mailing-Liste von Coin3D [@LIST] nicht weiterhalf, wurden NodeKits bei der Entwicklung von Qb nicht weiter beachtet. Die Klasse QbWidget bietet bereits eine große Zahl an Attributen in Form von Feldern, die nun kurz erläutert werden. Die Felder position, center, topLeft, topRight, bottomLeft, bottomRight, left, right, top und bottom sind 3D-Koordinaten, welche die in Abb. 4.6 dargestellten Punkte repräsentieren. Eine Änderung einer Koordinate führt zu einer Positionsänderung des Widgets. Abbildung 4.6: Koordinaten eines Widgets Praktisch sind diese Felder, um Widgets aneinander auszurichten. Es werden einfach die angrenzenden Koordinaten miteinander verbunden (siehe Abb. 4.7). Abbildung 4.7: Verbindungen zwischen Widgets KAPITEL 4. REALISIERUNG DES TOOLKITS 91 Problemlos können auch Translationsknoten „angedockt” werden. So können beliebige 3D-Modelle an Widgets ausgerichtet werden, um diese zu „dekorieren”. Intern besitzen Widgets auch nur Translationen, deren Verschiebungsvektor bei Veränderung der Koordinatenfelder berechnet wird. Die Felder size, width und height enthalten die Dimensionen des Widgets. Size ist dabei ein 2D-Vektor, width und height einfache Gleitkommawerte. Die Änderung eines dieser Felder führt zu einer Änderung der zuvor erläuterten KoordinatenFelder (bis auf position bzw. topLeft, diese sind dabei fest). Durch Setzen des Feldes fixedSize auf FALSE wird ein besonderer Modus aktiviert. Alle Koordinaten bis auf position und center beeinflussen dann nicht die Position, sondern die Größe. Die bis hier erläuterten Werte gelten immer nur im lokalen Koordinatensystem. Dies muss beachtet werden, wenn man Koordinaten von Widgets koppelt, die sich im Wirkungsbereich von Transformationsknoten befinden. Auffällig ist, dass kein Feld Informationen über die Tiefenausdehnung des Widgets enthält. Dies ist auch nicht nicht nötig, denn wie bereits erwähnt, werden Widgets auf einer 2-dimensionalen Ebene arrangiert. Falls benötigt, kann diese Information aus der umgebenden Box gewonnen werden. Wie in Abschnitt 4.2.2 erläutert, sind Ereignismethoden durch Felder ersetzt wurden. Diese sind click, mouseDown, mouseUp, mouseMove, keyDown, keyUp, hover und focus. Während click lediglich ein Trigger ist, enthalten mouseDown, mouseUp, keyDown sowie keyUp den Tastencode und mouseMove die Koordinate des Mauscursors. Bei einem entsprechenden Ereignis werden die Werte aktualisiert und Benachrichtigungen ausgelöst. Da Widgets Teil anderer Widgets sein können (z.B. besitzt QbPushButton ein Label), kann durch das Feld captureEvents markiert werden, dass das Widget keine Events empfangen soll. Der WidgetController sucht dann im Pfad des Widgets nach einem Widget, bei dem captureEvents gesetzt ist. Zur Visualisierung des Fokus wird ein Modell aus einer Datei geladen. Es besitzt Zugriff auf das Widget (this), um seine Größe entsprechend anzupassen. Im Standard-Stil wird um das Widget herum ein Drahtgittermodell eines Quaders gezeichnet, etwas größer als das Widget selbst. Da in einer Anwendung Felder über Sensoren mit Callback-Funktionen verbunden werden müssen (siehe Abschnitt 4.2.3), besitzt jedes Widget eine Liste für die Auf- KAPITEL 4. REALISIERUNG DES TOOLKITS 92 nahme solcher Sensoren. So muss sich der Programmierer nicht um deren Freigabe kümmern. 4.4.2 QbLabel Für Beschriftungen existieren in Inventor bereits die Klassen zur Darstellung von Text (siehe Abschnitt 3.3.5). Ihr Problem ist, dass sie sich nur schwer exakt positionieren lassen. Dieses Problem löst das QbLabel. Es verwendet intern SoAsciiText zur eigentlichen Darstellung des Textes. Als Textgröße wird ihm die Höhe des Widgets zugewiesen. An der linken und rechten Kante des Widgets befinden sich ClipPlanes (siehe Abschnitt 3.3.6), so dass überstehender Text abgeschnitten wird. Wahlweise kann über das Feld autoSize auch aktiviert werden, dass sich die Breite des Widgets dem Text anpasst. Das Feld textAlignment steuert die Ausrichtung des Textes in den festgelegten Grenzen (linksbündig, rechtsbündig oder zentriert). 4.4.3 QbButton Die Klasse QbButton ist die abstrakte Basisklasse von Knöpfen und Schaltern. Alle Abkömmlinge von QbButton haben eines gemein: sie besitzen mehrere Zustände, in denen ihre Darstellung jeweils unterschiedlich ist. QbButton verwaltet diese Zustände – abgeleitete Klassen müssen lediglich die Modelle für die verschiedenen Darstellungen zuweisen. Intern arbeitet QbButton über einen simplen SoSwitch, der je nach Zustand das entsprechende Modell auswählt und darstellt. Nach außen sind sämtliche Attribute als Felder zugänglich. Die Felder für die Zustandsmodelle sind: 1. modelOff: inaktiver Zustand 2. modelOffDown: inaktiver, jedoch gedrückter Zustand 3. modelOn: aktiver („eingeschalteter”) Zustand 4. modelOnDown: aktiver, gedrückter Zustand Die Zustände on und onDown werden nur verwendet, wenn das Feld isToggle gesetzt ist. Dies ist beim QbToggleButton der Fall. Wie die Übergange hierbei erfolgen, ist in Abb. 4.8 dargestellt. Ein Mausklick ist eine Folge von Maus-Drücken KAPITEL 4. REALISIERUNG DES TOOLKITS 93 und Maus-Loslassen, mit der Bedingung, dass beides über dem selben Widget stattfand. Die Entscheidung, ob eine mouseDown-mouseUp-Kombination einem clickEreignis entspricht, trifft der WidgetController. Er merkt sich das Widget, das ein mouseDown-Ereignis empfangen hat. Ist beim nächsten mouseUp-Ereignis jenes Widget nicht der Empfänger, wird bei bei ihm clickCancel (Klick-Abbruch) ausgelöst. Abbildung 4.8: Zustandsdiagramm eines ToggleButton Über das Feld on wird zwischen aktiv und inaktiv umgeschaltet, das Feld down wechselt zwischen gedrückt und nicht gedrückt. Wie üblich können alle Felder als Master bei Feldverbindungen eingesetzt werden, um Benachrichtigungen auszulösen. 4.4.4 QbPushButton und QbToggleButton Diese beiden Abkömmlinge der Klasse QbButton sind recht einfach gehalten, da die Basisklasse bereits die gesamte Funktionalität bereitstellt. Ihre einzige Aufgabe ist es, im Konstruktor das Feld isToggle entsprechend zu setzen und die Modelle zu laden und zuzuweisen. Ihre Unterschiede sind der Wert von isToggle sowie natürlich die Modelle. QbPushButton besitzt zusätzlich noch Felder für die Formatierung der Beschriftung. Sein Modell muss dafür sorgen, dass sich die Felder auch in der Darstellung auswirken (sinnvollerweise durch ein QbLabel). In Abbildung 4.9 sind die einzelnen Modelle der beiden Widgets abgebildet. Wie man sieht, unterscheiden sich die Zustände nicht wesentlich. Mancheiner könnte fragen, warum das gesamte Modell getauscht wird, statt einfach Teile des Modells zu verändern. Der Grund ist, dass dadurch die Flexibilität stark eingeschränkt wäre. KAPITEL 4. REALISIERUNG DES TOOLKITS 94 Abbildung 4.9: Modelle von Push- und ToggleButton (default-Style) Jeder Style setzt die Darstellung anders um, ein gedrückter Knopf könnte völlig anders aussehen als ein ungedrückter. 4.4.5 QbRadioButton Wie die beiden anderen QbButton-Derivate setzt QbRadioButton das Feld isToggle und weist die Modelle zu. Eine Besonderheit stellt jedoch der Umstand dar, dass RadioButtons stets in RadioGroups auftreten. In Qb ist dies folgendermaßen gelöst: RadioButtons werden wie gewöhnlich im Szenengraph eingefügt. Einer zusammengehörenden Gruppe von ihnen wird dann dem Feld radioGroup ein und dieselbe Instanz von QbRadioGroup zugewiesen (siehe Abschnitt 4.5.1). Abbildung 4.10: Eine Gruppe von RadioButtons (default-Style) 4.4.6 QbSpacer QbSpacer ist ein „leeres” Widget. Es erweitert die Klasse QbWidget nicht, entfernt jedoch die Abstraktheit (abstrakte Klassen können nicht instanziiert werden). Dieses Widget wird als Platzhalter verwendet. Es besitzt Position und Dimensionen, wird aber nicht dargestellt. Beim Arrangieren von Widgets kann so freier Platz zwischen ihnen geschaffen werden, ohne die Koordinaten aufwändig über Engines berechnen zu müssen. KAPITEL 4. REALISIERUNG DES TOOLKITS 95 4.4.7 QbLineEdit Das Eingabefeld ist bisweilen das komplexeste QbWidget. Es muss folgende Funktionalität bieten: • Darstellung eines beweglichen Text-Cursors • Möglichkeit der Zeicheneingabe beim Cursor • Möglichkeit des Löschens von Zeichen vor dem Cursor • Aufnahme von mehr Zeichen, als die Dimensionen erlauben Um die Umsetzung so einfach wie möglich zu halten, wurde weitestgehend Bestehendes verwendet und dafür auf gewissen Komfort verzichtet. Der Text innerhalb des Eingabefeldes sind zwei QbLabels: der Text links vom Cursor und der Text rechts Cursor. Der Cursor selbst ist ein schmaler QbSpacer, der so hoch ist wie der Text. Durch Feld-Verbindungen sind die Labels an die Position des Cursors gebunden. ClipPlanes schneiden überstehenden Text links und rechts ab. (siehe Abb. 4.11) Der Trick ist simpel: Eingegebene Zeichen werden am Ende des Labels links vom Cursor angehangen. Bei Bewegung des Cursors wird das übersprungene Zeichen einfach in das jeweils andere Label verschoben. Da bei den Labels autoSize aktiviert ist, kann die Größenänderung benutzt werden, den Cursor um diesen Wert auch grafisch horizontal zu verschieben. Abbildung 4.11: Aufbau des QbLineEdit Nachteilig bei dieser Methode ist, dass der Text-Cursor nicht mit der Maus gesetzt werden kann, da die Zeichen in den Labels als ein einziges Objekt vorliegen. Eine KAPITEL 4. REALISIERUNG DES TOOLKITS 96 Abhilfe wäre hier, jedes Zeichen als einzelnen Knoten zu behandeln. Die Probleme dabei offenbaren sich erst bei näherer Betrachtung: Jedes Zeichen ist (bei den meisten Schriftarten) unterschiedlich breit und unterschiedlich hoch. Die Höhe wird zwar durch SoFont festgelegt, Buchstaben mit Unterlänge wie p oder g machen dabei jedoch Ausnahmen. Der Aufwand ist also relativ hoch, so dass dies als mögliche Weiterentwicklung angesehen werden kann. Das Modell des Cursors wird wie gewöhnlich aus einer Datei geladen. Innerhalb des Modells besitzt man Zugriff auf den (unsichtbaren) Cursor. Durch Feldverbindungen kann dieser nach Belieben „dekoriert” werden. Im Standard-Stil verbindet lediglich eine Linie mit kleinen Kugeln an den Enden die Koordinaten top und bottom. Eine Skalierung des LineEdits vergrößert also sowohl die Schriftart als auch den grafischen Cursor. Mit Hilfe eines SoBlinker (ein spezieller SoSwitch) wird erreicht, dass der Cursor wie üblich blinkt. 4.4.8 QbBox Durch Verbinden von Koordinatenfeldern können Widgets bereits aneinander ausgerichtet werden, um ein bestimmtes Layout zu bilden. Effektiver können Layouts durch die QbBox gewonnen werden. In ihr wurde die Methode addChild des Separators überschrieben. Wird ein Widget der Box untergeordnet, wird dieses automatisch an den bereits enthaltenen Widgets ausgerichtet. Durch das Feld arrangement kann wahlweise ein horizontales oder vertikales Layout gewählt werden. Da enthaltene Widgets unterschiedlich breit und groß sein können, kann durch das Feld alignment die Ausrichtung festgelegt werden. Die Abmessungen der Box werden immer so angepasst, dass sie die enthaltenen Widgets exakt umschließt. Ein Rand kann dennoch mit Hilfe des Feldes margin festgelegt werden. Zur optischen Gruppierung von Widgets kann QbBox außen einen Rahmen darstellen, indem das Feld border auf einen Wert größer als Null gesetzt wird. Und letztendlich kann der Box durch das Feld caption eine Beschriftung hinzugefügt werden. In Abbildung 4.12 ist ein Qb-Dialog mit verschiedenen Varianten der Box dargestellt. Die dazu gehörende Dialogbeschreibung im Inventor-Dateiformat befindet sich in Anhang C.1. KAPITEL 4. REALISIERUNG DES TOOLKITS 97 Abbildung 4.12: Dialog mit Boxen (Default-Style) Containerwidgets wie die Box können im dreidimensionalen Raum Action Spaces bilden (siehe Abschnitt 2.4). Tastaturkommandos oder spezielle Widgets, die in den Rahmen der Box integriert werden, könnten den Wechsel in benachbarte oder übergeordnete Action Spaces erlauben. Die Kamera würde in dem Fall automatisch schwenken und zoomen. Letztere Funktionalität wurde bereits in die QbPerspectiveCamera integriert (siehe Abschnitt 4.5.5). 4.5 Hilfskomponenten Neben den Widgets und dem dem WidgetController besitzt das Qb-Toolkit weitere Komponenten, die entweder von den Widget-Klassen benötigt werden oder einfach nur den Funktionsumfang des Toolkits erweitern. Sämtliche Komponenten finden durch den GITK-Renderer Einsatz. 4.5.1 QbRadioGroup Wie im Abschnitt 4.4.5 erklärt wurde, werden RadioButtons mittels QbRadioGroup gruppiert. Dies ist ein Knoten, der wie der WidgetController lediglich Verwaltungsaufgaben besitzt und nicht dargestellt wird. Wird eine RadioGroup-Instanz dem Feld radioGroup eines RadioButtons zugewiesen, meldet er sich bei der RadioGroup an. Die RadioGroup führt eine Liste der angemeldeten (gruppierten) RadioButtons und sorgt dafür, dass stets nur einer von ihnen aktiv ist. Der jeweils aktive RadioButton kann durch das Feld choice ermittelt werden. 4.5.2 QbVec3fToCoordConnection Modelle müssen sich in ihrer Darstellung den Feldwerten des jeweiligen Widgets anpassen. Je nach Art des Modells ist es nötig, auch die Vertexe von Formen (siehe KAPITEL 4. REALISIERUNG DES TOOLKITS 98 3.3.5) zu verschieben. Koordinatenknoten (siehe 3.3.3) besitzen ein Multi-WertFeld coords, in dem die Koordinaten in einem Array gespeichert sind. So kann man nicht einfach eine einzelne Koordinate als Slave an ein Feld koppeln. Diese Funktionalität ist jedoch nötig, das Modell könnte sich sonst einer Skalierung nicht korrekt anpassen (siehe Beispiel in 4.2.4). QbVec3fToCoordConnection schließt diese Lücke. Dem Feld coordinate3 weist man den Koordinatenknoten zu, dem Feld coordIndex den Index der gewünschten Koordinate. Das Feld point wird nun mit einer beliebigen 3D-Koordinate verbunden (z.B. topLeft eines Widgets) – ändert sich diese, wird die Koordinate des Modells aktualisiert. Für jede Koordinate des Modells muss eine solche Vec3fToCoordConnection erstellt werden. Ein rechteckiger PushButton mit Phase besitzt mindestens 8 Vertexe. Bei einer Skalierung muss hierbei mindestens die Hälfte der Vertexe aktualisiert werden. Bei sehr komplexen Modellen mit sehr vielen Vertexen ist dies ein hoher Entwicklungs- und Rechenaufwand. Hier sollte die Form besser zerlegt und die entstandenen Teile durch herkömmliche Transformationen angepasst werden. 4.5.3 QbLine In Qb werden Linien zum Beispiel für den Rahmen einer QbBox benötigt. OpenGL ermöglicht direkt das Zeichnen von Linien. Wie für Flächen werden Koordinaten bereitgestellt, welche dann verbunden werden. So kann beispielsweise das Drahtgittermodell eines Objekts dargestellt werden. Die Stärke der Linie wird in Pixeln festgelegt, sie ändert sich bei der Darstellung nicht mit zunehmender Entfernung. Deshalb vermitteln sie keinen perspektivischen Eindruck. Desweiteren ist ihre Verwendung selbst in Inventor relativ kompliziert: Zunächst müssen Koordinaten bereitgestellt werden, die dann mit SoLineSet verbunden werden. Die Klasse QbLine abstrahiert eine Linie. Sie besitzt Felder point0 und point1, die einfach als Slaves z.B. an Widget-Koordinaten gekoppelt werden. Intern wird das Modell auf die Entfernung der beiden Punkte skaliert und entsprechend positioniert. Das Modell muss lediglich ein normiertes Objekt (Koordinatenursprung, Höhe 1) bereitstellen. Im Standard-Stil ist dies ein dünner Zylinder. Entsprechend skaliert wirkt er wie eine Linie, die sich perspektivisch korrekt in die Tiefe verjüngt. KAPITEL 4. REALISIERUNG DES TOOLKITS 99 4.5.4 QbFieldSensorList Jedes Widget besitzt eine Liste, welche Sensoren aufnehmen kann. Sie wird durch die Klasse QbFieldSensorList implementiert. Indem sie sich um Instanziierung und Freigabe der Sensoren kümmert, erleichtert sie die Verbindung zwischen Anwendungslogik und Feldern enorm. 4.5.5 QbPerspectiveCamera Die Viewer-Klassen der Fenstersystem-Anbindung (z.B. SoQt) besitzen ein Feature namens Seek. Befindet sich die Anwendung im Seek-Modus, bewirkt ein Klick auf ein Objekt, dass die Kamera an dieses Objekt herangezoomt 4 wird. Diese Funktionalität wird nur von den Viewer-Klassen bereitgestellt, Kameras können das nicht von sich aus. QbPerspectiveCamera ist von SoPerspectiveCamera abgeleitet und implementiert das Zooming. Jede Kamera besitzt (unter anderem) das Feld position. Die QbPerspectiveCamera besitzt zusätzlich das Feld newPosition. In ihrer Inventor-Datei sind Engines zwischen position und newPosition geschaltet, welche einen weichen Übergang zwischen den Koordinaten berechnen. Wie immer kann diese Datei angepasst werden, um z.B. statt einer beschleunigten eine lineare Bewegung zu realisieren. Hauptanwendungszweck dieses Features ist die (überschriebene) Methode viewAll. Wird sie mit einem beliebigen Teilgraph (z.B. einem Action Space) als Argument aufgerufen, ändert die Kamera so ihre Position, dass alle Objekte des Teilgraphen im sichtbaren Ausschnitt (Viewport) liegen. Bei der QbPerspectiveCamera ist dieser Übergang fließend, so dass beim Nutzer keine Verwirrung ausgelöst wird. 4.5.6 QbExaminerViewer Inventor Viewer-Komponenten erlauben das Drehen und Zoomen der dargestellten Szene. In manchen Fällen sollen aber dargestellte Teile der Szene unverändert an ihrem Platz verharren. 4 Zoomen bedeutet hier, dass die Verschiebung der Kamera stufenlos über einen gewissen Zeitraum animiert wird. KAPITEL 4. REALISIERUNG DES TOOLKITS 100 Die Klasse QbExaminerViewer leitet sich von der Klasse SoQtExaminerViewer ab, und erweitert sie um die Möglichkeit, eine Vorder- und Hintergrund-Szene darzustellen. In der Hintergrund-Szene könnte sich z.B. ein Hintergrundbild befinden, in der Vordergrund-Szene ein kleines animiertes Logo. Die Szenen werden aus Inventor-Dateien geladen. Sie benötigen mindestens eine eigene Kamera und eventuell Lichtquellen. Während des Traversierens darf ein Szenengraph nicht verändert werden. Bei einem GITK-Renderer kann sich aber jederzeit der Dialog ändern. Während die Coin3D-Bibliothek threadsicher kompiliert werden kann, ist der SoQtExaminerViewer nicht threadsicher – wird während des Renderns der Szenengraph verändert oder gar freigegeben, gibt es definitiv einen Speicherzugriffsfehler. Deshalb besitzt der QbExaminerViewer einen ReadWriteMutex. Durch ihn wird die Rendermethode, die einen kritischen Abschnitt darstellt, mit einem ReadLock versehen. Sämtliche Abschnitte, die den Szenengraph verändern und parallel ausgeführt werden könnten, müssen mit einem WriteLock versehen werden. So ist auch der QbExaminerViewer threadsicher. 4.5.7 QbConfigFile Eine Anwendung, die Qb nutzt, benötigt möglicherweise eine Klasse, um Konfigurationsdateien zu laden (z.B. benötigt das der GITK-Renderer). QbConfigFile leitet sich von der Inventor Import-Klasse SoInput ab und nutzt deren Methoden, um Name=Wert-Paare aus einer beliebigen Datei zu laden. Sie können dann den Listen names und values entnommen werden. 4.5.8 QbDotGraphOutput Szenengraphen können sehr komplex werden. Durch die Dynamik einer Anwendung und Feld-Verbindungen ist die Analyse des Graphen äußerst schwierig. Es kann zwar seine textuelle Repräsentation im Inventor-Dateiformat ausgegeben werden, nur ist diese nicht wirklich übersichtlich. Abhilfe schafft hier die Klasse QbDotGraphOutput. Das Software-Paket GraphViz von AT&T Research und Lucent Bell Labs dient der Visualisierung gerichteter Graphen. In der sogenannten DOT-Sprache werden der KAPITEL 4. REALISIERUNG DES TOOLKITS 101 Graph und Attribute wie Knotenbeschriftung oder Schriftart als Text-Datei übergeben. Das Programm Dot generiert daraus eine sehr übersichtliche Grafik in einem wählbaren Dateiformat. QbDotGraphOutput generiert aus einem Inventor-Graphen einen Dot-Graphen. Im Attribut typeList werden alle Knoten-Typen angegeben, die der Ausgabegraph enthalten soll. Für alle Knoten wird einfach die Basisklasse SoNode angegeben. Desweiteren kann gesteuert werden, ob auch alle Feldwerte und Feldverbindungen mit ausgegeben werden. Abbildung 4.13 stellt den Graphen des Beispiels aus Abschnitt 4.4.8 dar. Der Übersicht wegen sind nur Widgets ohne ihre Felder enthalten. Gut zu erkennen sind die Schachtelung des Dialogs sowie die Abhängigkeiten zwischen den KoordinatenFeldern. 0x8160d10 type: QbWidgetController 0x80978c0 type: QbBox 0x815ba48 type: QbBox 0x822fa70 type: QbPushButton 0x82bba20 type: QbBox 0x82e04e8 type: QbPushButton bottomLeft -> topLeft 0x822fed8 type: QbCheckBox bottom -> top 0x82f2e28 type: QbCheckBox bottomLeft -> topLeft 0x8274498 type: QbLineEdit bottom -> top 0x8343238 type: QbLineEdit 0x8388b30 type: QbBox 0x83c3d90 type: QbPushButton bottomRight -> topRight 0x83c47d8 type: QbCheckBox bottomRight -> topRight 0x8414bc8 type: QbLineEdit Abbildung 4.13: Szenengraph eines Dialogs, visualisiert durch GraphViz KAPITEL 4. REALISIERUNG DES TOOLKITS 102 4.6 Entwicklungsstand und Perspektiven Die Widgets des 3D-UI Toolkit Qb sind soweit entwickelt, dass sie für einen GITKRenderer einsatzbereit sind. Es kann sich jedoch längst nicht mit UI-Toolkits wie GTK oder Qt messen – dazu ist die Auswahl verschiedener Widgets einfach zu klein. Auch hinsichtlich der Usability kann noch einiges getan werden, wie zum Beispiel die Möglichkeit der Tastaturbedienung – die ist bisher nur im TextEdit möglich – oder der Verschiebung des Textcursors im TextEdit mit der Maus. Somit stellt Qb den Prototypen eines 3D-UI-Toolkit dar, der jedoch für den Prototypen eines GITK-Renderers momentan absolut seinen Zweck erfüllt. Er bietet somit alles Nötige für den Aufbau eines Dialoges, wozu fast ausschließlich Widgets zur Anwendungskontrolle (siehe Abschnitt 2.5) nötig sind. Qb kann problemlos in anderen 3D-Anwendungen verwendet werden, die Open Inventor als Basis nutzen. Einige neue QbWidgets sind bereits in Planung. So wird beispielsweise eine DropDown-Liste benötigt, um dem Nutzer die Auswahl aus einer größeren Menge zu ermöglichen. Momentan kann dies nur mit Hilfe von Radio- und ToggleButtons realisiert werden. Bei einer solchen Liste könnte im 3D-Raum die FischaugenTechnik umgesetzt werden (siehe Abschnitt 2.5). Auch das LineEdit oder ein zukünftiges Widget für mehrzeilige Texteingabe wären geeignete Objekte, an denen diese Technik umgesetzt werden kann. Für komplexere Anwendung ist es sinnvoll, das Konzept der Action Spaces umzusetzen. Dazu müssten die Containerwidgets entsprechende Kontrollelemente erhalten. Dazu wäre auch eine neue Viewer-Komponente nötig, welche die Navigation einschränkt. Zu den Boxen, die entweder nur vertikales oder horizontales Layout ermöglichen, könnten sich weitere Container-Widgets wie beispielsweise einem Grid (tabellenartiger Aufbau) gesellen. So wäre ein noch leichterer und flexibler Aufbau komplexer Dialoge möglich. Kapitel 5 Umsetzung eines 3D-Renderers für GITK Da mit Qb nun ein Toolkit für eine 3D-Nutzungsschnittstelle vorhanden ist, kann der 3D-Renderer für GITK entwickelt werden. In diesem Abschnitt wird zunächst erklärt, wie ein GITK-Renderer grundsätzlich arbeitet. Dann wird anhand der konkreten Implementation erläutert, wie ein GITK-Renderer entwickelt wird. Letztlich wird auf die Verwendung des 3D-Renderers mit einer Beispielanwendung eingegangen. 5.1 GITK-Renderer Wie bereits in Abschnitt 2.1 erklärt wurde, ist ein GITK-Renderer ein Modul, das eine GITK-Anwendung nach Außen hin präsentiert, wobei der GITK-Kern (Core) selbst eine Zwischenschicht darstellt. Zwischen Core und Renderer besteht eine Schnittstelle, die klar definiert und bei jedem Renderer identisch ist. Nur so kann ein Renderer problemlos ausgetauscht werden. Auch die grundsätzliche Arbeitsweise eines Renderers muss sich an bestimmte Regeln halten – eine Funktion sollte das tun was der Core von ihr erwartet. GITK befindet sich noch in der Entwicklung. Da sich das Design jederzeit ein wenig ändern kann, wird die CVS-Version vom 3. Mai 2004 als Basis angesehen und im Folgenden nur darauf Bezug genommen. 103 KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 104 5.1.1 Prinzipielle Arbeitsweise Eine GITK-Anwendung initialisiert zunächst den GITK-Core. Dieser ermittelt anhand eines Programmarguments den gewünschte GITK-Renderer. Wurde kein entsprechendes Argument übergeben, wird ein Standard-Renderer gewählt (Textdarstellung). Der entsprechende Renderer wird nun durch den Core dynamisch geladen – es werden also zur Laufzeit Funktionssymbole mit Speicheradressen verknüpft. Hierbei ist es natürlich zwingend erforderlich, dass der Renderer alle geforderten Symbole bietet. Nachdem auch der Renderer initialisiert wurde, wird durch den Core das Stylesheet (XSL) des Renderers ausgewählt. Jeder Renderer kann verschiedene Stile der Präsentation haben. Der Stil wird ebenfalls durch ein Programmargument festgelegt. Eine GITK-Anwendung definiert in einem GIML-Dokument einen Dialog – eine strukturierte Menge abstrakter Schnittstellenobjekte. Jedes dieser Objekte (im Folgenden auch GITK-Widget genannt) besitzt eine eindeutige ID, über die auf das Objekt zur Laufzeit verwiesen wird. Um Programmfunktionen mit Ereignissen der Schnittstellenobjekte zu verbinden, muss die GITK-Anwendung nach Initialisierung des GITK-Cores die Speicheradressen dieser Callback-Funktionen mit den entsprechenden Widget-IDs verknüpfen. Die Programmkontrolle wird nun an den GITK-Core übergeben. Dieser liest eine abstrakte Dialogbeschreibung aus einem GIML-Dokument in ein DOM (Document Object Model) ein. Über die Umgebungsvariable LANG wird die Landessprache des Nutzers ermittelt und ein Stylesheet zur Übersetzung des Dialoges gewählt. Durch dieses wird das DOM so transformiert, das es einen übersetzten Dialog beschreibt. Dieses wird nun nochmals mit einem Stylesheet des Renderers transformiert, um rendererspezifische Anpassungen zu ermöglichen (z.B. ein bestimmter Anzeigestil). Nun erhält der GITK-Renderer die Programmkontrolle. Er baut anhand des im DOM beschriebenen (abstrakten) Dialogs seinen konkreten Dialog auf und bringt ihn zur Anzeige. In einer Ereignisschleife wartet der Renderer nun auf Interaktion des Nutzers1 . Tritt ein Ereignis ein, wird dies dem GITK-Core mit Angabe 1 Die Begriffe „Anzeige” und „Nutzer” sollten hier nicht allzu wörtlich genommen werden. Eine KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 105 der ID des auslösenden Widgets mitgeteilt, welcher seinerseits die entsprechende Callback-Funktion in der GITK-Anwendung aufruft. Änderungen an Schnittstellenobjekten (z.B. der Fokus oder Inhalt eines Eingabefeldes) werden vom GITK-Core stets in das ursprüngliche DOM der GITKAnwendung geschrieben. Wenn später zur Laufzeit die Landessprache, der Stil oder der Renderer gewechselt werden, befindet sich im DOM immer der aktuelle Zustand der Schnittstelle. In der Callback-Funktion kann die Anwendung nun ihre Aufgaben erledigen. Die Ereignisschleife des Renderers wird solange fortgesetzt, bis entweder der Renderer gewechselt oder die Anwendung beendet wird. Eine Unterbrechung der Schleife kann auch nötig sein, wenn die GITK-Anwendung einen neuen Dialog anzeigen oder einen bestehenden Dialog ändern bzw. schließen lässt – dies ist abhängig von der Architektur des Renderers. 5.1.2 Webserver Zum aktuellen Zeitpunkt startet der GITK-Core einen HTTP-Server, über den im Browser Statusinformationen abgerufen werden kann. Zudem stellt er momentan die einzige Möglichkeit dar, zur Laufzeit den Renderer, den Anzeigestil oder die Landessprache zu ändern. Da er in einem eigenen Thread läuft, kann jederzeit ein Neuaufbau des Dialoges möglich. Beim Design eines Renderers muss deshalb auf Threadsicherheit geachtet werden. 5.1.3 Schnittstelle Die Schnittstelle zwischen GITK-Core und Renderer besteht aus einem festen Satz von Funktionen. Bei den Funktionen seitens des Cores handelt es sich um einige Ereignismethoden sowie Methoden für einen einfacheren Umgang mit dem DOM. Die Funktionen auf der Seite des Renderer müssen implementiert werden: • Initialisierung und Destruktion: Ressourcen anfordern bzw. freigeben. • Eventloop starten und stoppen: In einer Schleife auf Nutzer-Ereignisse warten bzw. diese Schleife unterbrechen. Anzeige kann sich auch in einer Sprachausgabe äußern. Ein Nutzer kann auch ein weiterverarbeitendes Programm sein. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 106 • Dialog anzeigen und verbergen: Dialog aus dem DOM aufbauen bzw. Dialog freigeben. • Eventloop neustarten: Ereignisschleife unterbrechen, Dialog neu aufbauen und Schleife wieder starten. • Informationen über den Renderer zurückgeben: Allgemeine Informationen, die momentan jedoch irrelevant sind. Wie man sieht, ist die API eines GITK-Renderers nicht sonderlich komplex. Die Funktion, welche die wesentliche Charakteristik eines Renderers ausmacht, ist die zur Anzeige eines Dialogs. Hier entscheidet sich, wie die Nutzungsschnittstelle letztendlich präsentiert wird. 5.1.4 GITK-Widgets GITK-Widgets sind abstrakte Schnittstellenobjekte. Eine GITK-Anwendung baut mit ihnen eine abstrakte Programmschnittstelle auf. Ein Renderer verbindet sich mit dieser Schnittstelle und schafft die Voraussetzung für eine Interaktion mit dem Programm. Dazu muss der Renderer mit den verschiedenen Arten dieser Schnittstellenobjekte umgehen können. In der GITK-Architektur gibt es nach [@GITK] 3 Hauptgruppen von Schnittstellenobjekten: • Action: Auslöser für eine Programmfunktion • Data Entry: Dateneingabe • Data Display: Datenausgabe Ein GITK-Renderer muss diese Schnittstellenobjekte – auf welche Weise auch immer – unterstützen. Das Schema in Abbildung 5.1[@GITK] zeigt, dass es von diesen Objekten spezialisierte Varianten gibt. Durch deren Unterstützung kann die Nutzungsschnittstelle effizienter oder bedienungsfreundlicher gestaltet werden – zwingend ist dies aber nicht, es kann stets die allgemeinere Variante verwendet werden. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 107 abstract interface objects action character input single line multi line option choice boolean single multiple data entry data display value choice structured ? ... label structured custom single range tabular hierarchical graph Abbildung 5.1: GITK-Widgets – abstrakte Schnittstellenobjekte Neben den GITK-Widgets gibt es die Widget Groups, also Elemente, die Widgets in eine Struktur bringen. Da sie für die GITK-Anwendung selbst von keiner Relevanz sind, sondern nur eine Hilfe für den Renderer darstellen, zählen sie nicht als GITK-Widget. Desweiteren gibt es noch die Dialog Widgets. Diese sind im Grunde Actions, jedoch sind sie ausschließlich zur Steuerung des Dialogs gedacht. In Anhang C.2 ist eine GIML-Beschreibung eines GITK-Dialogs zu sehen. Ein Renderer muss den DOM-Baum des Dialogs traversieren und für jedes Vorkommen eines abstrakten GITK-Widgets ein geeignetes Objekt erzeugen. Wie diese Objekte konkret geartet sind, und ob der durch die Widget Groups erzeugten Struktur Rechnung getragen wird, ist allein Sache des Renderers. 5.1.5 GITK-Klassen Auch wenn GITK eine reine C-API besitzt, können die definierten Structs als Klassen aufgefasst werden. Ihre Komponenten sind dabei alle öffentlich und es gibt keine Methoden. Die für einen Renderer relevanten Klassen werden hier kurz angesprochen, da auf sie später zurückgegriffen wird. GitkDialog Stellt einen in sich abgeschlossenen Dialog dar. Er enthält u.a. in der Komponente xmlDocPtr einen Zeiger auf ein XML-Dokument als DOM – einem Baum aus Objekten der Bibliothek libxml. Somit bietet er alle nötigen Informationen über den Dialog. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 108 GitkLayout Stellt die (z.B. grafische) Repräsentation des Dialogs dar. Renderer können und sollten diese Klasse um eigene Komponenten erweitern. Ein Renderer weist jedem GitkDialog ein GitkLayout zu. GitkWidget Stellt ein Widget aus der Sicht des GITK-Cores dar. Es enthält als Komponente dieselbe Widget-ID, welche durch die GITK-Anwendung im GIML-Dialog angegeben wurde, sowie einen Zeiger auf den GitkDialog, der das Widget enthält. Soll dem GITK-Core ein Nutzer-Ereignis mitgeteilt werden, wird das Widget über ein solches Objekt mit der entsprechenden ID referenziert. Renderer können auch diese Klasse erweitern. 5.2 Implementation des 3D-Renderers Zu Beginn der Entwicklung des 3D-Renderers war die große Frage, wie man grundsätzlich schnell und strukturiert eine 3D-Darstellung realisiert. Nachdem fest stand, dass dafür Open Inventor die erste Wahl ist, war auch klar, dass der Renderer in C++ implementiert wird. So musste nicht extra ein Sprachwrapper entwickelt werden – die C-API von GITK ließ sich auch in C++ (fast) ohne Weiteres nutzen. Der 3D-Renderer kann beispielhaft für die Entwicklung anderer Renderer in C++ herangezogen werden. 5.2.1 C und C++ vereint Eines der ersten Probleme bei der Entwicklung des 3D-Renderers war der Umstand, dass die GITK-Bibliothek mit einem C-Compiler übersetzt wurde, während Qb und Open Inventor die Übersetzung mit einem C++ Compiler erforderten. Die entstandenen Objekt-Dateien konnten nicht gegeneinander gelinkt werden. Ursache war das sogenannte Mangeling des C++ Compilers. In C++ können Funktionen überladen sein – Funktionen existieren also mehrfach unter gleichem Namen aber mit unterschiedlicher Signatur. Ebenso können Symbole in unterschiedlichen Namensräumen gleich lauten. Beim Mangeling werden die Symbole so codiert, KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 109 so dass keine Verwechslungen möglich sind. Entsprechend wurden auch die Symbole aus den GITK-Headerdateien umcodiert, so dass sie in der GITK-Bibliothek nicht gefunden werden konnten. Abhilfe war das Einfügen spezieller CompilerDirektiven in die GITK Headerdateien, wodurch an den entsprechenden Stellen das Mangeling des Compilers deaktiviert wurde. Für Renderer ist es sinnvoll, die GITK-Klassen GitkLayout und GitkWidget zu erweitern. In C müssen dazu neue Structs deklariert und durch Makros die Komponenten der Basis-Structs mit aufgenommen werden. In C++ dagegen sind Structs nichts anderes als Klassen, deren Komponenten automatisch öffentlich sind. Neue Klassen können also problemlos von den GITK-Structs geerbt und mit GITKFunktionen verwendet werden. Ein GITK-Renderer kann also problemlos in C++ entwickelt werden. Man profitiert so von der objektorientierten Programmierung und einem deutlich klareren Code. 5.2.2 Klassen des 3D-Renderers Sämtliche Klassen des 3D-Renderers beginnen mit dem Präfix Gitkr, wobei das r für Renderer steht. GitkrConfig Der 3D-Renderer besitzt diverse Parameter, die durch den Nutzer angepasst werden können. Diese werden durch die Klasse GitkrConfig als statische Komponenten bereitgestellt. Über eine Methode können die Parameter eingelesen werden. Zunächst wird dabei in Umgebungsvariablen gesucht. Kann ein Parameter nicht gefunden werden, wird versucht, ihn über eine Instanz von QbConfigFile (siehe 4.5.7) aus einer Konfigurationsdatei einzulesen – ansonsten wird ein Standardwert angenommen. Eine weitere Funktion durchsucht Kommandozeilenargumente nach Parametern und setzt diese entsprechend. GitkrLayout Diese Klasse besitzt eine Zentrale Rolle im 3D-Renderer. Sie leitet sich von GitkLayout ab und erweitert sie um einige Methoden und statische Komponenten. So KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 110 besitzt sie Zeiger auf ein Qt-Fenster, einen QbExaminerViewer, einen QbWidgetController und einen SoSeparator als Wurzelknoten des Szenengraphen – alles Objekte, die nur einmal, aber global benötigt werden. Mit Hilfe der Instanz-Methoden kann ein Dialog zur Anzeige gebracht und wieder versteckt werden. Wie dabei der Dialog aus dem DOM aufgebaut wird, ist gesondert in Abschnitt 5.2.5 beschrieben. GitkrWidget Diese Klasse ist von GitkWidget abgeleitet, und erweitert diese um den Zeiger auf ein QbWidget. So kann stets das zu einem GITK-Widget gehörende QbWidget ermittelt werden. GitkrWidgetList Diese Klasse verwaltet GitkrWidget-Instanzen, deren Speicher im Destruktor automatisch freigegeben wird. Die Beziehung zwischen den GITK- und Renderer-Klassen ist in Abbildung 5.2 als Klassendiagramm dargestellt. Abbildung 5.2: Klassendiagramm des 3D-Renderers 5.2.3 Implementierung der Renderer-Funktionen gitkr_init Der Funktion werden durch den GITK-Core die Kommandozeilenargumente der GITK-Anwendung übergeben. Diese werden durch GitkrConfig ausgewertet. Beim Auftreten des Arguments --help wird eine Übersicht aller möglichen rendererspezifischen Argumente auf die Standardausgabe geschrieben und die Funktion sofort verlassen. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 111 Ansonsten wird von der SoQt-Fensteranbindung ein Fenster angefordert und in einer statischen Komponente der Klasse GitkrLayout gespeichert. Dadurch werden automatisch die Inventor-Klassen initialisiert. Erst nachdem auch die Qb-Klassen initialisiert wurden, können von ihnen Instanzen erzeugt werden. Nun kann die Konfiguration des Renderers durch GitkrConfig eingelesen werden. Auf deren Basis wird ein QbExaminerViewer (siehe 4.5.6) angelegt, der sich um 3D-Darstellung und Interaktion kümmert. Als nächstes wird der Szenengraph mit einem QbWidgetController, einer QbPerspectiveCamera und einem SoEventCallback nach dem in 5.3 abgebildeten Schema erzeugt und dem ExaminerViewer zugewiesen. Abbildung 5.3: Szenengraph des 3D-Renderers Jetzt ist alles Nötige getan, um das Fenster des 3D-Renderers zur Anzeige zu bringen. Dieses bleibt bis zum Ende der Anwendung bestehen. gitkr_event_loop_start In dieser Funktion wird lediglich die Ereignisschleife des Qt-Fenster gestartet. Dieses empfängt nun vom Fenstersystem Ereignisse und leitet sie wie in Abschnitt 3.11 erläutert weiter. Problematisch war hier, dass der konventionelle Start über die Methode SoQt::mainLoop()2 nach Beenden der Schleife sofort das Fenster schließt. Dieses Verhalten ist nicht erwünscht, da so die Eventschleife nicht einfach neu gestartet werden könnte. Deshalb sie durch die Methode exec() des globalen Objekts qApp gestartet, welches allerdings ausschließlich in Qt-Applikationen existiert. Aus diesem Grund arbeitet der 3D-Renderer aktuell nur mit SoQt und keiner anderen Fensteranbindung zusammen. 2 Alle Fensteranbindungen von Open Inventor besitzen diese Funktion. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 112 gitkr_event_loop_end Diese Funktion beendet die Qt-Ereignisschleife, wodurch gitkr_event_loop_start zur aufrufenden GITK-Funktion zurückkehrt. gitkr_dialog_show Argument dieser Funktion ist ein Zeiger auf einen Struct des Typs GitkDialog. Aufgabe des Renderers ist hier, dessen Komponente layout ein Objekt des Typs GitkLayout zuzuweisen. Da die Klasse GitkrLayout wie in 5.2.2 beschrieben ein Nachfahre dieses Typs ist, kann hier problemlos eine Instanz dieser Klasse verwendet werden. In der Initialisierung des Objekts wird der Szenengraph des Dialogs aus dem DOM aufgebaut. Dies ist in 5.2.5 detailliert beschrieben. Durch die Instanzmethode show wird dieser Graph als Unterknoten an den WidgetController gebunden und damit sichtbar. Diese Funktion stellt einen kritischen Abschnitt dar, denn hier wird der Szenengraph verändert (vgl. Abschnitt 4.5.6). Deshalb wird dieser mit einem WriteLock versehen. Während der QbExaminerViewer den Szenengraph rendert, ist der kritische Abschnitt blockiert. gitkr_dialog_hide Auch dieser Funktion wird ein Pointer auf einen GitkDialog übergeben. Das Objekt, auf welches die Komponente layout verweist, muss hier freigegeben werden. Das hat zur Konsequenz, dass Repräsentation des Dialogs im Szenengraph ebenfalls freigegeben wird. Auch dieser Abschnitt ist kritisch und deshalb mit einem WriteLock versehen. Ein kleines Problem trat hier auf. Der WidgetController besitzt eine Referenz auf das fokussierte Qb-Widget. Solange ein Inventor-Knoten referenziert ist, wird es nicht freigegeben (siehe 3.6). Deshalb ist es hier zwingend erforderlich, den Fokus aufzuheben. Erst dann wird der Szenengraph des Dialogs durch die Methode GitkrLayout::hide vom WidgetController entfernt, wodurch sein Referenzzähler Null wird und er automatisch rekursiv freigegeben wird. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 113 gitkr_event_loop_restart Diese Funktion besitzt eine andere Aufgabe, als ihr Name suggeriert. Durch sie soll ein Dialog neu aufgebaut werden, wenn sich zur Laufzeit z.B. der Anzeigestil oder die Landessprache ändert. Bei dem GTK-Renderer (2D-GUI) ist dazu eine Unterbrechung der Ereignisschleife nötig, da sich das Fenster komplett schließt und ein neues angelegt wird. Im Gegensatz dazu bleibt das Qt-Fenster des 3D-Renderers stets offen – nur die 3D-Darstellung des ExaminerViewers wird durch Modifikation des Szenengraphen verändert. Deshalb wird hier die Ereignisschleife nicht neu gestartet. Diese Funktion sollte vielleicht besser gitkr_dialog_rebuild heißen. gitkr_done In dieser Funktion müssen alle Ressourcen des Renderers freigegeben werden. Im Falle des 3D-Renderers sind dies die QbExaminerViewer-Komponente, das QtFenster und der gesamte Szenengraph. Für den Szenengraph muss lediglich der Referenzzähler der Wurzel (sceneRoot) dekrementiert werden. Das Qt-Fenster wird lediglich versteckt, laut SoQt-Dokumentation wird es vom Fenstersystem automatisch freigegeben. 5.2.4 Anzeige-Stile Das Aussehen eines Dialogs kann durch Stile (engl. Styles) angepasst werden. GITK-Renderer müssen dazu XML-Stylesheets (XSL) an einem bestimmten Verzeichnis bereitstellen, die dem Dialog bei einer Transformation ein Stil-Attribut geben. Dieses kann später beim Generieren des Dialoges ausgewertet werden. Für den 3D-Renderer wurden 3 Stile entwickelt: „Default” (Standard) , „Aqua” und „Nice”. Jeder Stil besitzt ein eigenes Verzeichnis, in dem die angepassten Modelldateien der QbWidgets sowie Grafikdateien liegen (siehe Abschnitt 4.2.4). Für die Verwendung eines bestimmten Stils muss der Suchpfad von SoInput entsprechend gesetzt werden. 5.2.5 Generierung des Dialogs Dialoge werden im Konstruktor der Klasse GitkrLayout generiert. Diesem wird als Argument ein Zeiger auf ein GitkDialog-Objekt übergeben. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 114 Bevor daraus der Qb-Dialog erzeugt wird, sind einige Vorkehrungen zu treffen. Als Erstes muss der Stil des Dialogs ermittelt werden, um die Suchpfade für die Modelle der Qb-Widgets zu setzten. Dann kann das Grundgerüst des Dialogs aufgebaut werden. Jeder GITK-Dialog besteht aus mindestens einer Widget Group und den Dialog Widgets. Für beide wird jeweils eine QbBox angelegt, welche dann die Widgets aufnimmt. Beide Boxen befinden sich wiederum in einer gemeinsamen Box, wie es das Schema in Abbildung 5.4 darstellt. Abbildung 5.4: Grundgerüst eines Dialogs Mit Hilfe der libxml-Funktionen wird nun der DOM-Baum traversiert. DialogWidgets werden dabei generell in QbPushButtons umgesetzt und in die QbBox controlBox eingefügt. Sie richtet die Knöpfe horizontal aus. Für jedes Vorkommen einer Widget Group wird eine QbBox erzeugt, welche die enthaltenen Objekte immer anders arrangiert als die übergeordnete Box (siehe Abb. 5.5). Auf die Art sollte der Dialog im Mittel ein halbwegs harmonisches Seitenverhältnis erhalten. Abbildung 5.5: Gruppierung von Widgets durch QbBox Wie in 5.1.4 beschrieben, muss nun für jedes Vorkommen eines GITK-Widgets ein QbWidget erzeugt werden. Über eine Funktion des GITK-Cores kann zu einem GITK-Widget der Typ ermittelt werden. In einer Case-Anweisung wird dieser GitkWidgetType unterschieden und das jeweils passende QbWidget erzeugt. Für KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 115 den Typ Value Choice existiert momentan noch kein GitkWidgetType, weshalb dieser nicht behandelt werden kann3 . Für alle anderen werden folgende QbWidgets genutzt: • Action: QbPushButton • Character Input (alle): QbLineEdit • Option Choice Boolean: QbToggleButton • Option Choice (alle anderen): QbRadioButtons mit einer QbRadioGroup • Label: QbLabel (nur einzeilig, noch kein Typ für mehrzeilige vorhanden) Die erzeugten Objekte werden der QbBox als Unterknoten angehangen, die der aktuellen WidgetGroup entspricht. In einigen Fällen müssen weitere QbBoxen eingefügt werden, um z.B. ein Label als Beschriftung neben einem Widgets zu platzieren oder um eine Gruppe von QbRadioButtons zu generieren. Der Dialog wird also von außen nach innen aufgebaut. Durch das Konzept von Qb passt sich die Größe der QbBoxen stets ihrem Inhalt an – der Dialog wird nie größer als er sein muss. Damit bei einer Interaktion der GITK-Core korrekt benachrichtigt werden kann, wird zu jedem QbWidget ein Objekt der Klasse GitkrWidget erstellt. Neben Zeigern auf das QbWidget und den GitkDialog enthält Objekt die jeweilige WidgetID. Verwaltet werden diese Objekte durch eine GitkrWidgetList, die jedes GitkrLayout-Objekt besitzt. Außerdem wird in der Sensor-Liste jedes Widgets ein Sensor erzeugt. Diesem wird eine Callback-Funktion zugewiesen, welche als Argument (userData) den Zeiger auf das jeweilige GitkrWidget erhält. Bei einem Ereignis durch ein Widget informiert der Callback den GITK-Core mit Angabe der Widget-ID. Damit ist die Generierung des Dialogs vollständig abgeschlossen – über ihn kann die GITK-Anwendung nun bedient werden. 5.3 Benutzung des 3D-Renderers Da sich die GITK noch im Entwicklungsstadium befindet, existieren bisher nur einige wenige Beispielanwendungen zu Demonstrations- und Testzwecken. Davon 3 Das entsprechende QbWidget existiert ebenfalls noch nicht. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 116 besitzt nur die Anwendung HelloUser eine gewisse Anwendungslogik, d.h. sie reagiert tatsächlich auf Nutzereingaben. Zunächst wird der Nutzer aufgefordert, seinen Namen einzugeben und sein Geschlecht zu wählen. Bei Bestätigung mit Ok erscheint ein zweiter Dialog, in dem der Nutzer freundlich gegrüßt wird. Wird auch hier mit Ok bestätigt, beendet das Programm. Wird im ersten Dialog Abbrechen gewählt, beendet das Programm sofort. Die Dialogbeschreibung der Eingabemaske befindet sich in Anhang C.2. In Abbildung 5.6 sind Screenshots der Anwendung mit einer 2D-GUI zu sehen. Abbildung 5.6: GITK-Anwendung HelloUser mit Renderer für GTK+ Das HelloUser-Programm wird durch folgenden Befehl mit dem 3D-Renderer und aktiviertem Webserver gestartet: gitkHelloUser --gitk-renderer=opengl --gitk-server-port=8080 Mit der Standard-Konfiguration startet der 3D-Renderer im Vollbild-Modus. Der aus QbWidgets bestehende Dialog wird anfangs frontal betrachtet. Außen befindet sich ein Rahmen mit Schaltflächen und Schiebereglern (siehe Abbildung 5.7). Diese sind Teil der ExaminerViewer-Komponente. Mit den Schiebereglern kann die Ansicht verstellt werden. Konkret wird dadurch die Kamera im Raum bewegt und rotiert. Die Schaltfläche „Alles sehen” löst die Methode viewAll der QbPerspectiveCamera (siehe Abschnitt 4.5.5) aus, welche die Kamera in einer fließenden Bewegung so justiert, dass die gesamte Szene im Blickfeld ist. 5.3.1 Bedienung Der 3D-Renderer bietet durch den ExaminerViewer zwei Modi: Szeneninteraktion und Viewerinteraktion. Sie können durch die entsprechenden Schaltflächen oder die Escape-Taste umgeschaltet werden. Im Szenenmodus kann man mit dem Dialog arbeiten, also Widgets fokussieren und Eingaben vornehmen. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 117 Abbildung 5.7: GITK-Anwendung HelloUser mit dem 3D-Renderer Im Viewermodus kann die Szene rotiert, gedreht und gezoomt werden. Dabei muss die linke Maustaste gedrückt gehalten und dabei die Maus verschoben werden. Die Bedienung ist in Tabelle 5.1 dargestellt. Aktion Tasten halten Drehen um X- und Y-Achse Bewegen parallel zum Bildschirm Zoomen keine Strg oder Umschalt Strg und Umschalt Mauscursor Tabelle 5.1: Mausbedienung in Viewermodus Somit können fast alle Funktionen auch ohne die Kontrollelemente im Rahmen verwendet werden. Der Rahmen kann ausgeblendet werden, was gerade bei Verwendung stereoskopischer Verfahren sinnvoll ist – der Rahmen wäre dabei eher hinderlich, da er den dreidimensionalen Eindruck stört. Im Viewermodus gelangt man über die rechte Maustaste in das Kontextmenü des ExaminerViewers. Hier können verschiedene Darstellungsmodi aktiviert werden. Die wohl wichtigste Option aktiviert den Rot-Grün-Modus bzw. den QuadbufferModus. So wird die Szene stereoskopisch dargestellt (siehe Abschnitt 2.10). Der Quadbuffer-Modus funktioniert nur bei entsprechender Treiberunterstützung, erlaubt jedoch die Verwendung fortgeschrittener Stereoskopie-Techniken (z.B. Shutterbrillen). Durch den EventCallback im Szenengraphen des Renderers wird auf einige Tastenkombinationen reagiert. Sie sind in der Tabelle 5.2 aufgelistet. Antialiasing und Smoothing konnten noch nicht getestet werden, da der SoftwareRenderer auf dem Entwicklungssystem dies offenbar nicht unterstützt. Der Post- KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK Tasten Alt-G Alt-D Alt-W Alt-A Alt-S Alt-V 118 Funktion Schreibt den gesamten Szenengraph im Inventor-Format auf die Standardausgabe (siehe Abschnitt 3.8). Erzeugt eine DOT-Datei für GraphViz, mit der die Dialogstruktur schematisch visualisiert werden kann (siehe Abschnitt 4.5.8). Schreibt die Feld-Werte aller QbWidgets auf die Standardausgabe. Aktiviert das Antialiasing zur Kantenglättung. Aktiviert das Smoothing. Exportiert die Szene als Vektorgrafik (Postscript). Tabelle 5.2: Tastenkombinationen im 3D-Renderer script-Export funktioniert, jedoch ist die Qualität eher mäßig. Möglicherweise ist die Implementation in Coin3D nicht so ausgereift wie in TGS Inventor. Die Bedienung des Dialogs im Szenenmodus erfolgt wie in einer gewöhnlichen 2D-GUI. Ein Mausklick auf ein Widget fokussiert dieses und löst das Klick-Ereignis aus. Ein PushButton führt z.B. die zugeordnete Aktion aus und ein ToggleButton schaltet zwischen AN und AUS um. Tastatureingaben zeigen momentan nur bei einem fokussierten LineEdit Wirkung. 5.3.2 Action Spaces Beim momentanen Entwicklungsstand des Renderers existiert immer nur ein Action Space: der gesamte Dialog. Erwirkt der GITK-Core die Anzeige eines Dialoges, wird die Kamera immer so ausgerichtet, dass er vollständig im Blickfeld ist. Durch die Erweiterung der QbPerspectiveCamera erfolgt die Positionsänderung in einer fließenden Bewegung, so dass sie durch den Betrachter leichter nachvollzogen werden kann. Noch kann der Nutzer durch den ExaminerViewer völlig frei navigieren. Sinnvoll wäre jedoch deren Einschränkung durch eine eigene Viewer-Komponente. Action Spaces könnten ohne Weiteres durch die bereits vorhandenen QbBoxen repräsentiert werden. Die Aktivierung eines Action Spaces (durch Wechsel oder Fokussierung eines enthaltenen Widgets) müsste zu einem Aufruf der viewAll-Methode der Kamera führen – mit seinem eigenen Szenengraph als Argument. So entstünde mit geringem Aufwand ein Zooming Interface. Natürlich macht dieses Konzept erst bei komplexeren Anwendungen Sinn, so dass bisher auf eine Implementierung verzichtet wurde. KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 119 5.4 Entwicklungsstand Der 3D-Renderer ist soweit entwickelt, dass er alle Funktionen unterstützt, die auch von dem Text- und GTK-Renderer unterstützt werden. Selbst häufiger Wechsel zwischen den Renderern, Stilen oder Landessprachen zur Laufzeit verläuft unproblematisch. Nur bei allzu schnellem Wechsel hängt manchmal der HTTP-Server des GITK-Cores. Hin und wieder treten Speicherzugriffsfehler beim Beenden des 3D-Renderers auf, was auf einen Bug in der SoQt Fensteranbindung zurückzuführen ist. Alle Funktionen des 3D-Renderers wurden erfolgreich mit den wenigen Beispielprogrammen getestet. Prinzipiell sollte der Renderer auch bei komplexeren GITKAnwendungen korrekt arbeiten, nur kann das nicht garantiert werden. GITK-Widgets, die nicht in den bestehenden GITK Beispielanwendungen vorkommen, werden durch den Renderer nicht berücksichtigt. So würde momentan beispielsweise Value Choice nicht grafisch repräsentiert sein. Da ein ständiger Zuwachs an neuen GITK-Widgets zu erwarten ist, wäre dafür ein flexibles Mapping zu QbWidgets sinnvoll. Bisher wurde zu einem Zeitpunkt immer nur ein Dialog anzeigt. Theoretisch sollte der Renderer mehrere Dialoge bewerkstelligen, nur ist dies noch nicht berücksichtigt. Die Dialoge würden an ein und derselben Stelle im Raum erscheinen – es wäre also eine Logik nötig, welche die Dialoge effizient im Raum verteilt. 5.5 Einsatz des 3D-Renderers Die derzeitige Konzeption des 3D-Renderers offenbart auf den ersten Blick kaum Vorteile gegenüber einer zweidimensionalen Nutzungsschnittstelle. Die 3D-Darstellung erfordert deutlich leistungsfähigere Hardware und mindestens das Doppelte an Hauptspeicher wie der GTK-Renderer. Dazu kommt, dass die GITK-Architektur keine speziellen Widgets oder Datentypen unterstützt, die nur im 3D-Raum nutzbar wären. Dennoch besitzt die grafische Ausgabe des Renderers eine gewisse optische Wirkung. So könnten Informationssysteme durch ihn dargestellt werden, die so einen äußerst repräsentativen Charakter erhalten. Ein Unternehmen könnte sich dazu einen Stil nach seinem Corporate Design entwerfen. Dass der 3D-Renderer die da- KAPITEL 5. UMSETZUNG EINES 3D-RENDERERS FÜR GITK 120 zu nötige Flexibilität besitzt, beweist Abbildung 5.8 mit dem HelloUser-Programm im Stil „Aqua”. Abbildung 5.8: HelloUser-Programm im Aqua-Stil Besonders in der Spielebranche werden zugunsten einer attraktiveren Darstellung gern hohe Hardwareanforderungen in Kauf genommen werden. Heutige Rechner bieten im Allgemeinen genügend Leistungsreserven für den Einsatz des 3DRenderers. Erst die Darstellung sehr großer oder mehrerer Dialoge, bei der in 2D-GUIs Platzmangel entsteht, würde einen echten Nutzen aus der dreidimensionalen Darstellung ziehen (siehe Abschnitt 2.2.2). Für eine wirklich effiziente Bedienung wäre hierfür jedoch die Implementierung der Navigation zwischen Action Spaces nötig. Interessant wäre die Integration der 3D-Ausgabe in ein größeres, übergeordnetes System. So könnte man beispielsweise innerhalb einer Virtual Reality Umgebung eine GITK-Anwendung direkt steuern – mit tatsächlich dreidimensionalen Widgets. Dies ist jedoch mit dem vorliegenden Konzept nicht möglich – der 3DRenderer besitzt seine eigene 3D-Ausgabe. Erst die Nutzung eines Distributed Scenegraph (vgl. 2.9) würde das ermöglichen. Eine 3D-Umgebung müsste also ihren Szenengraph öffentlich zugänglich machen, in welchen der 3D-Renderer seinen Qb-Dialog einfügen kann. Das Projekt Looking Glass aus Abschnitt 2.9 wäre zum Beispiel ein geeignetes System, nur müsste dazu der Renderer mit Java3D eine vollkommen andere 3D-API nutzen. Kapitel 6 Zusammenfassung Mit der Entwicklung des 3D-User-Interface-Toolkits Qb konnte eine Bibliothek für dreidimensionale Nutzungsschnittstellen geschaffen werden. Qb basiert vollständig auf dem in Kapitel 3 ausführlich beschriebenen 3D-Toolkit Open Inventor, was sich als sehr günstig erwiesen hat. Nicht nur die Funktionalität zur Grafikdarstellung mittels OpenGL, sondern auch viele Programmierkonzepte wurden dadurch in Qb übernommen. Besonders durch das Konzept der Feldverknüpfungen wird ein hoher Abstraktionsgrad und eine enorme Flexibilität erreicht. Aufgrund der zeitlichen Beschränkung konnten Innovationen, die sich bei einer dreidimensionalen Schnittstelle ergeben können, nur ansatzweise in die Entwicklung des Toolkits einfließen. In der Arbeit wurde jedoch erläutert, an welchen Stellen eine entsprechende Weiterentwicklung anknüpfen müsste. In Kapitel 2 wurden neue Ansätze zu Navigation und Darstellung von Informationen in drei Dimensionen beschrieben, welche bei der Entwicklung herangezogen werden sollten. Das Toolkit Qb war Basis für die Entwicklung eines Renderers für die GITKArchitektur, welcher für GITK-Anwendungen dynamisch 3D-Nutzungsschnittstellen generiert. Da mit Qb die meiste Funktionalität gegeben war, ging die Entwicklung des GITK-Renderers sehr schnell voran. Alle bis jetzt nötigen Funktionen wurden implementiert, so dass mit diesem Modul nun eine äußerst interessante Darstellung für GITK-Anwendungen verfügbar ist. Während der Softwareentwicklung im Rahmen dieser Arbeit waren kleinere Modifikationen an der GITK-Architektur für eine Zusammenarbeit mit den in C++ entwickelten 3D-Softwarekomponenten notwendig. Desweiteren wurden Bugs in 121 KAPITEL 6. ZUSAMMENFASSUNG 122 der verwendeten Implementation von Open Inventor entdeckt und inzwischen von den verantwortlichen Entwicklern beseitigt. Das ursprüngliche Ziel der Realisierung von Toolkit und Renderer ist somit gelungen. Bis der 3D-Renderer jedoch eine effizientere Bedienung als alle anderen GITK-Renderer ermöglicht, muss seine Usability noch weiterentwickelt werden. Zudem sollte er mit spezieller Hardware getestet werden. Diese wurde zwar in dieser Arbeit vorgestellt, jedoch war aus Finanzgründen eine Verfügbarkeit ausgeschlossen. Anhang A Szenengraph-Symbole 123 ANHANG A. SZENENGRAPH-SYMBOLE 124 Anhang B Open Inventor Klassenhierarchie Die folgende Hierarchie der Inventor-Klassen stellt nur einen Ausschnitt dar, eine vollständige Übersicht ist in der Dokumentation der jeweiligen Implementation zu finden (z.B. [@COIN]). Zugunsten der Übersicht sind abgeleitete Klassen sowohl eingerückt als auch gruppiert. 125 ANHANG B. OPEN INVENTOR KLASSENHIERARCHIE 126 ANHANG B. OPEN INVENTOR KLASSENHIERARCHIE 127 Anhang C Quellcode C.1 Beispiel-Dialog im Inventor-Dateiformat Diese Dialogbeschreibung im Inventor-Format erzeugt den in Abbildung 4.12 dargestellten Dialog. #Inventor V2.0 ascii Font { name "Arial" } QbBox { arrangement HORIZONTAL alignment CENTER QbBox { arrangement VERTICAL alignment LEFT QbPushButton { } QbCheckBox { } QbLineEdit { string LineEdit } } QbSpacer { } QbBox { arrangement VERTICAL alignment CENTER border .1 QbPushButton { } QbCheckBox { } 128 ANHANG C. QUELLCODE 129 QbLineEdit { string LineEdit } } QbSpacer { } QbBox { arrangement VERTICAL alignment RIGHT border .1 margin.5 caption "Box" QbPushButton { } QbCheckBox { } QbLineEdit { string LineEdit } } } C.2 Beispiel-Dialog in GIML Der folgende Quelltext in der Generalized Interface Markup Language erzeugt den Dialog der GITK-Beispielanwendung „HelloUser” [@GITK]. <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE giml SYSTEM "http://gitk.sourceforge.net/giml.dtd"> <giml xmlns="http://gitk.sourceforge.net/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:i18n="http://apache.org/cocoon/i18n/2.0" > <dialog> <meta> <dc:title><i18n:text>query user identity</i18n:text></dc:title> </meta> <dialogwidgets> <dialogwidget id="Okay"/> <dialogwidget id="Cancel"/> </dialogwidgets> <widgetgroup> <label><i18n:text>identity</i18n:text></label> <widget id="UserName" type="characterinput_alphabetic"> <label><i18n:text>user name</i18n:text></label> <disabled>true</disabled> ANHANG C. QUELLCODE </widget> <widget id="Sex" type="optionchoice_single_compact"> <label><i18n:text>sex</i18n:text></label> <options> <option><i18n:text>male</i18n:text></option> <option><i18n:text>female</i18n:text></option> </options> </widget> </widgetgroup> </dialog> </giml> 130 Anhang D Glossar 2D/3D Abkürzung für zweidimensional bzw. dreidimensional im Sinne einer geometrischen Dimension. C++ Die Programmiersprache C mit objektorientierten Erweiterungen. Dialog Dient der Kommunikation zwischen Nutzer und Anwendungsprogramm. GUI Graphical User Interface – grafische Nutzungsschnittstelle. Objekt Bezeichnet in dieser Arbeit zum einen die Instanz einer Klasse und zum anderen ein geometrisches Objekt. Toolkit Bedeutet übersetzt Werkzeugkasten und meint eine Sammlung von Werkzeugen zur Entwicklung einer bestimmten Art von Software. VRML Virtual Reality Modelling Language Widget Bezeichnet die kleinste Einheit eines GUI-Toolkits. XML Die Extensible Markup Language ist ein Standard zur Definition von Auszeichnungssprachen. XSL Extensible Stylesheet Language. Durch XSL Transformationen (XSLT) lassen sich XML-Dokumente in ein bestimmtes Format bringen. 131 Anhang E Inhalt der beiliegenden CD Die CD enthält im Wurzelverzeichnis eine Version dieser Arbeit im PDF-Format, welches neben Farbabbildungen den Vorteil der Hyperlinks bietet. Im Verzeichnis doc/ befindet sich eine Dokumentation der Quelltexte im HTMLFormat, welche durch Doxygen erzeugt wurde. Sie ist jedoch keinesfalls vollständig. Im Verzeichnis src/ befinden sich die als TAR-GZ gepackten Quellen vom GITKCore, den GITK-Beispielen und den GITK-Renderern für OpenGL (3D), Text und GTK. Diese Versionen wurden vom Autor zuletzt getestet und arbeiten definitiv zusammen. Aktuellere Versionen sind stets unter [@GITK] verfügbar. Das Toolkit Qb befindet sich im Archiv des 3D-Renderers, da die beiden Teile momentan noch in eine gemeinsame Bibliothek kompiliert werden. Im Verzeichnis extra/ befinden sich die letzten kompatiblen Versionen von Coin3D sowie SoQt. 132 Literaturverzeichnis [ARB93] OpenGL Architecture Review Board. OpenGL programming guide: the official guide to learning OpenGL. Addison-Wesley, 1993 [CLAUS97] Ute Clausen. Programmieren mit OpenGL: 3D-Grafik und Bildverarbeitung. Springer-Verlag, 1997 [DACHS04] Raimund Dachselt. Eine deklarative Komponentenarchitektur und Interaktionsbausteine für dreidimensionale multimediale Anwendungen, Dissertation an der TU Dresden, noch nicht veröffentlicht [HINZ01] Michael Hinz. 3D-Widgets: Klassifikation und Spezifikation von Bestandteilen dreidimensionaler Benutzungsschnittstellen, Diplomarbeit an der Technischen Universität Dresden, Mai 2001 [KOST04] Stefan Kost. Dynamically generated multi-modal application interfaces, Dissertation an der TU Dresden, noch nicht veröffentlicht [RASK01] Jef Raskin. Das Intelligente Interface, Addison-Wesley, 2001 [WERN94a] Josie Wernecke. The Inventor Mentor: Programming ObjectOriented 3D Graphics with Open Inventor. Addison-Wesley, Release 2, 1994 [WERN94b] Josie Wernecke. The Inventor Toolmaker. Addison-Wesley, 1994 [@COIN] Dokumentation Coin3D http://doc.coin3d.org/Coin/ [@CONN] 3Dconnexion http://www.3dconnexion.com [@FISH] University of Maryland, Human-Computer Interaction Lab: Fisheye Menus http://www.cs.umd.edu/hcil/fisheyemenu/ 133 LITERATURVERZEICHNIS 134 [@GITK] Generalized Interface Toolkit http://gitk.sf.net [@LIST] Mailingliste Coin3D http://auto.coin3d.org [@LOOK] Project Looking Glass by Sun Microsystems http://wwws.sun.com/software/looking_glass/ [@MACOS] Betriebssystem Apple Mac OS X http://developer.apple.com/de/macosx/ [@OPENGL] OpenGL – The Industry Standard for High Performance Graphics http://www.opengl.org [@PARA] ParaGUI Cross-Platform Widgetset http://www.paragui.org [@PICC] University of Maryland, Human-Computer Interaction Lab: Piccolo http://www.cs.umd.edu/hcil/jazz/index.shtml [@RICH] Rich’s Corner. Direct3D vs. OpenGL: A Comparision http://www.xmission.com/~legalize/d3d-vs-opengl.html [@SAAB] SaabTech http://www.saabtech.se [@SEE] SeeReal Technologies http://www.seereal.com [@SIM] Systems In Motion http://sim.no [@STUBE] Studierstube – Collaborative Augmented Reality Project http://studierstube.org [@TROLL] Trolltech – Creators of Qt – The cross-platform C++ GUI/API http://www.trolltech.com [@WEB3D] Web3D Consortium http://web3d.org [@WIKIP] Wikipedia – Die freie Enzyklopädie http://www.wikipedia.de [@WINAMP] Nullsoft Winamp Mediaplayer http://www.winamp.com [@X3D] X3D Technologies Corp. http://x3d.com