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