3D-fizierung
Um den nun erstellten 2D-Editor in einer 3D-Szene darzustellen, müssen nur minimale Anpassungen vorgenommen werden: Insgesamt sind lediglich drei Klassen zu erstellen. Bei GEF3D werden zweidimensionale Inhalte in der 3D-Szene auf Flächen projiziert. Dadurch können fast alle Komponenten des 2D-Editors ohne Änderungen übernommen werden. Lediglich die Figure, die das Diagramm darstellt, im Beispiel also die GraphFigure, muss angepasst werden, da ja nun die dreidimensionale Fläche erzeugt werden muss. Entsprechend muss auch der GraphEditPart ersetzt werden. Die resultierenden Klassen nennen wir GraphFigure3D und GraphEditPart3D. GraphEditPart3D wird von der GraphEditPart abgeleitet. Die Änderung hier ist minimal: Anstelle der 2D-Figur wird nun eine GraphFigure3D erzeugt. Tabelle 2 zeigt alle zu erstellenden Klassen im Überblick.
| Klasse(n)/Files | Kurzbeschreibung |
|---|---|
| GraphEditor3D | Der 3D-Editor wird von GraphEditor2D abgeleitet. Hier wird die EditPartFactory modifiziert und ein Kameratool zur Navigation in der 3D-Szene erstellt. |
| GraphEditPart3D | Von GraphEditPart abgeleiteter Controller, der sich nur in der erzeugten View von der 2D-Version unterscheidet. |
| GraphFigure3D | Anstelle einer 2D-View muss in der 3D-Ansicht eine Fläche im 3D-Raum erzeugt werden, auf deren Oberfläche die restlichen 2D-Elemente projiziert werden. |
| plugin.xml | Natürlich muss auch der 3D-Editor über den Extension Point org.eclipse.ui.editors an der Eclipse Platform angemeldet werden. |
Die GraphFigure3D muss allerdings vollkommen neu implementiert werden. Wir zeigen hier den kompletten Code, der einen Einblick in das Programmieren von 3D-Elementen mit GEF3D geben soll (Listing 1).
Listing 1
public class GraphFigure3D extends ShapeFigure3D {private ISurface m_surface = new FigureSurface(this);public GraphFigure3D() {SurfaceLayout.setDelegate(this, new FreeformLayout());getPosition3D().setLocation3D(IVector3f.NULLVEC3f);getPosition3D().setSize3D(new Vector3fImpl(400,300,20));setBackgroundColor(ColorConstants.white);setAlpha((byte) 0x44);}@Overridepublic ISurface getSurface() {return m_surface;}@Overrideprotected Shape createShape() {return new CuboidFigureShape(this);}}
Durch die Verwendung von Hilfsfiguren, so genannten Shapes, sieht die Umsetzung einer Figure3D der einer zweidimensionalen Figure sehr ähnlich. Da häufig eine Figur durch einen Shape dargestellt wird, bietet GEF3D eine spezielle Figurenklasse ShapeFigure3D, die genau das unterstützt. Zur Erzeugung des Shapes muss die Methode createShape entsprechend implementiert werden. GEF3D liefert grundlegende Shapes wie Quader (Cuboid), Kugeln (Sphere) oder Zylinder (Cylinder) mit. Die GraphFigure entält später die Figuren der Knoten und Kanten des Modells. Diese Figuren sollen weiterhin als 2D-Figuren dargestellt und auf die GraphFigure projiziert werden. Dazu muss die 3D-Figure eine Oberfläche (Surface) definieren. Der eigentliche Zeichenvorgang ist im Beispiel in den Klassen von GEF3D versteckt. Im Unterschied zu GEF, bei dem Figuren über eine paint-Methode auf die Zeichenfläche gezeichnet werden, sammelt GEF3D zunächst so genannte Render-Fragmente ein. Diese enthalten den Code zum "Rendern" des Objekts in der 3D-Szene. Dieser komplizierte Vorgang ist der unzulänglichen Unterstützung von OpenGL bei der Erzeugung transparenter Objekte geschuldet. Da Transparenz gerade bei Diagrammen in 3D eine große Rolle spielt, versucht GEF3D hier ein möglichst gutes Ergebnis bei gleichzeitig hoher Performanz und geringem Programmieraufwand zu erzielen.
Das Aufwändigste ist nun die Instanziierung des 3D-Editors. Wenn ein 3D-Editor ohne 2D-Vorlage erstellt werden soll, kann einfach eine der 3D-Basisklassen wie GraphicalEditor3D verwendet werden. In den meisten Fällen beinhaltet der Editorcode jedoch Routinen zum Laden und Speichern der Modelle, zur Konfiguration etc. Diesen Code will man im Allgemeinen nicht neu schreiben, manchmal ist das sogar unmöglich. Aus diesem Grund übernehmen wir auch hier die 2D-Version, also GraphEditor2D, und leiten davon unsere 3D-Version GraphEditor3D ab. Nun müssen aber alle 3D-spezifischen Besonderheiten umgesetzt werden. Folgende Dinge sind dabei zu beachten:
- Anstelle eines GraphicalViewer muss ein GraphicalViewer3D erzeugt werden.
- Um in der 3D-Szene zu navigieren, muss eine Kamera installiert werden. Das geschieht über ein spezielles Tool (CameraTool).
- Anstelle der 2D-Diagrammelemente müssen die zuvor neu erstellten Klassen erzeugt werden. Zudem müssen alle EditParts so angepasst werden, dass sie in der 3D-Ansicht editierbar werden. Das bedeutet beispielsweise, dass statt zweidimensionaler Handles und Feedback-Figuren entsprechende dreidimensionale Versionen verwendet werden müssen, wie in Abbildung 4 zu sehen.
Wir werden im Folgenden diese Punkte der Reihe nach abarbeiten. Zunächst also muss ein 3D-Viewer erzeugt werden. Dazu wird die Funktionalität createGraphicalViewer(..) überschrieben und teilweise nachgebildet:
@Overrideprotected void createGraphicalViewer(Composite i_parent) {GraphicalViewer3DImpl viewer = new GraphicalViewer3DImpl();Control control = viewer.createControl3D(i_parent);setGraphicalViewer(viewer);configureGraphicalViewer();hookGraphicalViewer();initializeGraphicalViewer();control.addDisposeListener(viewer.getLightweightSystem3D());}
Die Kamera wird einfach als Tool installiert. Um die Tools des 2D-Editors zu behalten, wird dessen Toolinitialisierungsmethode weiterhin aufgerufen:
protected PaletteRoot getPaletteRoot() {PaletteRoot root = super.getPaletteRoot();PaletteDrawer drawer = new PaletteDrawer("GEF3D");drawer.setDescription("GEF3D tools");drawer.add(new ToolEntry("Camera", "Camera Tool", null, null,CameraTool.class) {});root.add(0, drawer);return root;}















