Transformationen in Java 3D

Werbung
Transformationen in Java 3D
Eine Ausarbeitung von Florian Hüter
im Rahmen des Seminars Java3D von Prof. Dr. W. Heinzel
an der FH Fulda im WS 2002/03.
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Inhaltsverzeichnis:
Seite
1. Allgemeines zu Java3D ................................................................3
1.1. Was ist Java3D? ...................................................................3
1.2. Der Szenengraph..................................................................4
2. Theoretische Grundlagen von Transformationen ......................5
3. Transformationen in Java3D ........................................................7
3.1. Allgemeines..........................................................................7
3.2. Vorbereitungen ....................................................................8
3.3. Translation..........................................................................13
3.4. Skalierung...........................................................................15
3.5. Rotation ..............................................................................17
3.6. Anmerkungen.....................................................................22
4. Ein praktisches Beispiel..............................................................23
Quellenverzeichnis ......................................................................26
Anhang .........................................................................................27
Seite 2 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
1. Allgemeines zu Java3D
1.1.
Was ist Java3D?
Java3D ist eine Bibliothek von Java-Klassen zur Darstellung dreidimensionaler Grafik
innerhalb von Java-Applikationen und Applets. Die Struktur einer darzustellenden Szene
wird hierbei durch den Szenengraphen definiert, der den kompletten Aufbau einer 3DSzene in einer baumartigen Struktur beinhaltet. Dadurch erlaubt Java3D eine relativ
komfortable Handhabung von Objekten und Unterobjekten.
Für die Darstellung von Szenen setzt Java3D auf ein lokal vorhandenes Rendering- API
wie z.B. OpenGL oder unter Windows auch DirectX auf. Dadurch kann Java3D auch die
für das jeweilige Rendering-API optimierte Hardware zur Szene ndarstellung nutzen. Der
Nachteil hierbei ergibt sich jedoch dadurch, dass durch die Verwendung der lokalen
Rendering- API zwangsläufig eine Plattformabhängigkeit entsteht. Allerdings muss
lediglich lokal eine entsprechend angepasste Version von Java3D einmalig installiert
werden. Die kompilierten Java-Programme an sich laufen durch die Verwendung der
immer gleichen Schnittstelle zur Anwendung bzw. zum Applet dann wiederum auf jeder
Plattform, auf der Java3D installiert ist.
Für die Darstellung einer Szene analysiert Java3D den Szenengraphen und konvertiert
diesen in eine auf Renderingperformance optimierte Form, die von der jeweils
vorhandenen Rendering- API abhängig ist. Bei Veränderungen der Szene erfolgt wenn
zulässig eine erneute Optimierung. Der Programmierer muss sich also nicht um die
Optimierung einer Szene in Bezug auf Renderingperformance kümmern – dies nimmt
Java3D ihm ab.
Seite 3 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
1.2.
Der Szenengraph
Der Szenengraph von Java3D besteht aus zwei Teilen: Zum einen dem „Viewing“- Zweig,
der alle für die Darstellung erforderlichen Parameter enthält und zum anderen dem
„Content“-Zweig, der den Inhalt der Szene (Geometrie, Transformationen, etc.) enthält.
Der „Viewing“- Zweig wird an dieser Stelle nicht näher erläutert, da sowohl dieser als
auch
das
Locale-
VirtualUniverse-Objekt
und
das
virtual universe
vollständig
automatisch erzeugt werden können,
locale
indem die Klasse SimpleUniverse
verwendet wird.
Augenmerk soll an dieser Stelle
BranchGroup
BranchGroup
jedoch kurz auf den „Content“Zweig gerichtet werden, da sich
TransformGroup
…
hieran sehr schön der strukturierte
Aufbau eines Java3D-Szenengraphen
…
verdeutlichen lässt.
An
der
Spitze
des
content
„Content “-
Zweiges stehen zunächst ein oder
viewing
Abbildung 1 – Der Szenengraph von Java3D
mehrere BranchGroup-Objekte. An diese lassen sich nun beliebig viele weitere
BrachGroup- und/oder TransformGroup-Objekte anhängen. Wie später noch ersichtlich
wird lassen sich so komplexe Strukturen erzeugen, die jedoch stets übersichtlich und
verständlich sind. Mit folgendem Code-Fragment lassen sich beispielsweise ein Ober- und
ein Unterarm auf einfachste Weise modellieren:
BranchGroup oberarm = new BranchGroup();
oberarm.addChild(new ColorCube(0.3));
BranchGroup unterarm = new BranchGroup();
oberarm.addChild(new ColorCube(0.3));
oberarm.addChild(unterarm);
//
//
//
//
//
erzeugt BranchGroup oberarm
erstellt Würfel als Oberarm
erzeugt BranchGroup unterarm
erstellt Würfel als Unterarm
ordnet Unterarm Oberarm unter
Diese logische Struktur eines Arms kann jetzt durch Bewegung der BranchGroup
„oberarm“ als Ganzes bewegt werden und darüber hinaus lässt sich der Unterarm durch
Transformationen der BranchGroup „unterarm“ auch einzeln bewegen. Der Unterarm
muss nun noch an die richtige Stelle im Koordinatensystem geschoben werden, und ein
zugegebenermaßen sehr einfacher Arm ist fertig. Auf Translation und andere
Transformationen sowie die Erstellung eines komplexe n, funktionsfähigen Armes wird
jedoch erst später eingegangen.
Seite 4 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
2. Theoretische Grundlagen von Transformationen
Die theoretische Grundlage für die hier behandelten Transformationen Translation,
Skalierung und Rotation bilden mathematische Matrizenmultiplikationen. So lassen sich
n-dimensionale Transformationen als Matrizen der Größe n+1 x n+1 darstellen. Die hier
behandelten Transformationen im dreidimensionalen Raum können also als 4x4-Matrizen
beschrieben werden.
An dieser Stelle soll nur kurz auf die mathematischen Hintergründe der Transformationen
eingegangen werden, da das Hauptaugenmerk dieser Ausarbeitung auf der Anwendung
von Transformationen in Java3D liegt. Nähere Informationen zu diesem Thema finden
sich für Interessierte jedoch im Skript von Prof. Dr. W. Heinzel zur Lehrveranstaltung
„Graphische Datenverarbeitung“ an der FH Fulda im Sommersemester 2000 im dritten
Kapitel „Transformationen“ auf den Seiten 62-73. Jedoch sind die dortigen Ausführungen
auf OpenGL bezogen, deren Koordinatensystem eine von Java3D abweichende
Achsenbezeichnung verwendet. Für geometrische Operationen in Java3D gilt folgendes
Koordinatensystem:
y
x
z
Abbildung 2 – Das Koordinatensystem
von Java3D
Die einfachste Transformation, zumindest in Bezug auf die dafür notwendige
mathematische Berechnung stellt die Translation dar. Diese lässt sich durch folgende
Matrix abbilden, wobei der Punkt P mit den Koordinaten (u, v, w) um den Vektor (a b c)
verschoben werden soll:
[uvw1]
1
0
0
0
0
1
0
0
0
0
1
0
a
b
c
1
= [ u+a v+b w+c 1 ]
Seite 5 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Auch die Skalierung lässt sich durch eine einfache Matrizenmultiplikation erreichen.
Hierzu ist folgende Rechenoperation für jeden Eckpunkt P (u, v, w) eines zu skalierenden
Objekts durchzuführen, wenn mit dem Vektor (a b c) skaliert werden soll:
[uvw1]
a
0
0
0
0
b
0
0
0
0
c
0
0
0
0
1
= [ au bv cw 1 ]
Hierbei ist jedoch zu beachten, dass der Mittelpunkt des Objekt zunächst in den Ursprung
des Koordinatensystems geschoben und nach der Skalierung wieder zurück bewegt
werden muss, um den Mittelpunkt des Objekts durch die Skalierung nicht zu verschieben.
Die aufwändigste der hier behandelten Transformationen stellt die Rotation dar. Jedoch
soll auch hier nur sehr kurz aus Gründen der Vollständigkeit eine der benötigten Matrizen
vorgestellt und keine detaillierten Erklärungen dazu gegeben werden. Eine Rotation eines
Punktes P mit den Koordinaten (u, v, w) mit dem Winkel a um die x-Achse lässt sich
durch folgende Matrizengleichung darstellen:
[uvw1]
1 0
0
0
0 cos(a) -sin(a) 0
0 sin(a) cos(a) 0
0 0
0
1
= [ u v*cos(a)- z*sin(a) y*sin(a)+z*cos(a) 1 ]
Alle anderen möglichen Rotationen werden durch ähnliche Gleichungen abgebildet, auf
die hier jedoch nicht näher eingegangen werden soll. Die Drehrichtung für die Rotationen
um die jeweiligen Achsen lässt sich übrigens aus Abbildung 2 auf der vorangegangenen
Seite entnehmen, die das Koordinatensystem von Java3D mit den Rotations richtungen für
alle Achsen zeigt.
Seite 6 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
3. Transformationen in Java3D
3.1.
Allgemeines
Standardmäßig werden in Java3D alle Objekte bezüglich der Weltkoordinaten platziert.
Diese umständliche und unpraktikable Vorgehensweise lässt sich allerdings durch die
Verwendung von Transformationsgruppen, so genannte TransformGroup-Objekte
umgehen.
Ein
TransformGroup-Objekt
erlaubt
die
Verwendung
eines
neuen
Koordinatensystems, das relativ zum Koordinatensystem des Vaters angeordnet ist.
Verändert (transformiert) man nun die TransformGroup, bewegen sich gleichzeitig auch
alle Objekte bzw. Modelle in dieser Gruppe.
Nun lässt sich dieses Vater-Kind-Prinzip in Java3D natürlich über beliebig viele Ebenen
durchführen. Hierbei akkumulieren sich selbstverständlich die Transformationen der
einzelnen Stufen. Darüber hinaus sollte man wissen, dass der Szenengraph von unten nach
oben abgearbeitet wird. Tiefer im Szenengraph angeordnete Transformationen werden
also zuerst ausgeführt.
Allerdings lassen sich durch die Verwendung von TransformGroup-Objekten noch keine
Transformationen ausführen. Dazu benötigt man wenigstens ein Transform3D-Objekt.
Dieses dient zur Beschreibung der Transformationen einer TransformGroup und muss
dieser entsprechend zugewiesen werden. Auf die einzelnen Befehle, die dazu in Java3D
erforderlich sind, wird in den folgenden Abschnitten detailliert anhand von Beispielen
eingegangen.
„Content“-Zweig
des Szenengraphen
BranchGroup
TransformGroup
…
…
TransformGroup
TransformGroup
TransformGroup
Transform3D
…
rotate
scale
Abbildung 3 – Der „Content“-Zweig des Szenengraphen
Seite 7 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
3.2.
Vorbereitungen
An dieser Stelle beginnen wir nun mit der eigentlichen Programmierung. Doch zuerst
sollten wir eine Umgebung erzeugen, die für alle späteren Demonstrationen und
Erklärungen verwendet werden kann.
Zunächst benötigen wir einige allgemeine Klassen, die wir importieren müssen. Dies
geschieht durch folgende Codezeilen:
// Zu importierende Klassen
import java.applet.Applet;
import java.awt.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.ColorCube;
import javax.media.j3d.*;
import javax.vecmath.*;
Danach beschreiben wir zunächst einmal den Rumpf der zu erstellenden Klasse
SimpleScene, den wir später nach und nach füllen werden. Es werden folgende
Funktionen für unser Java3D-Programm benötigt:
public class SimpleScene extends Applet
{
// Im Konstruktor wird lediglich eine Java3D-Szene vorbereitet und
// anschließend dargestellt. Daher kann dieser Konstruktor später universal
// verwendet werden. Die eigentliche Szene wird in der Funktion
// createSceneGraph() weiter unten erstellt.
public SimpleScene()
{
}
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph()
{
}
// Die Hauptfunktion, die die Szene als Anwendung oder Applet aufruft
public static void main(String[] argv)
{
new MainFrame(new SimpleScene(), 256, 256);
}
}
Wie bereits die Kommentare im Quelltext verdeutlichen, wird hier die Programm-Klasse
SimpleScene erstellt, die einen Konstruktor besitzt, der die Szene vorbereitet und lädt.
Außerdem existiert eine Funktion createSceneGraph, in der später die komplette Szene
erstellt wird. Am Ende der Klasse steht noch die java-typische main-Funktion, die das
Programm startet, indem sie ein Objekt der Klasse SimpleScene erzeugt.
Da jetzt das ungefähre Aussehen einer Java3D-Anwendung von der Struktur her klar sein
sollte, wollen wir nun damit beginnen, die einzelnen Funktionen zu füllen.
Seite 8 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Fangen wir also mit der Erstellung des Konstruktors an. Dieser muss ein SimpleUniverseObjekt
inklusive
Virtual-Universe-Objekt,
Locale-Objekt
und
dem
kompletten
„Viewing“-Zweig erzeugen und den Szenengraphen dort hinein laden. Für nähere
Informationen hierzu siehe Kapitel 1.2 „Der Szenengraph“.
Um kurz die wichtigsten Funktionen näher zu beschreiben sei gesagt, dass zunächst der
„Content“-Zweig des Szenengraphen über den Aufruf der später noch detailliert
beschriebenen Funktion createSceneGraph() erzeugt wird. Dieser wird anschließend aus
Performancegründen kompiliert und optimiert. Danach wird ein Canvas3D-Objekt erzeugt
und positioniert, das einer Leinwand entspricht und für die Darstellung der Szene benötigt
wird. Nun wird unter Verwendung dieses Canvas3D-Objekts ein SimpleUniverse erzeugt,
dass die Basis einer jeden Java3D-Szene bildet. Danach wird die Kamera noch so
positioniert, dass der im Fenster sichtbare Bereich der Szene genau von -1 bis 1 sowohl
auf der X- als auch auf der Y-Achse reicht. Zum Schluss muss nur noch der bereits
kompilierte „Content“-Zweig des Szenengraphen in dieses SimpleUniverse geladen
werden und fertig ist die Java3D-Umgebung. Der Java-Code hierzu sieht folgendermaßen
aus:
public SimpleScene()
{
// Setzen des Layout-Managers
setLayout(new BorderLayout());
// Aufruf der Funktion zur Erstellung des Szenengraphen (bzw. der Szene)
BranchGroup scene = createSceneGraph();
// Kompiliert (Optimiert) den Szenengraph
scene.compile();
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
// Erzeugt eine Leinwand (=Canvas), auf der die Szene dargestellt wird
Canvas3D canvas3D = new Canvas3D(config);
// In den Mittleren Bereich der Szene (Center) das Canvas3D-Objekt einfügen
add("Center", canvas3D);
// Erstellung des SimpleUniverse als Root-Objekt der gesamten Szene
SimpleUniverse universe = new SimpleUniverse(canvas3D);
// Die Kameraposition wird entlang der Z-Achse so versetzt, dass die
// Fenstergrenzen jeweils bei -1 und 1 des Koordinatensystems liegen
universe.getViewingPlatform().setNominalViewingTransform();
// Szenengraph in SimpleUniverse einfügen
universe.addBranchGraph(scene);
}
Jetzt wäre das Programm ja beinahe schon lauffähig, jedoch fehlt noch das Wichtigste:
Die eigentliche Szene. Um nun also etwas darzustellen benötigen wir noch etwas Inhalt in
Seite 9 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
der Funktion createSceneGraph(). Diese werden wir zunächst erst einmal mit einem
bunten Würfel füllen, um die Vorbereitungen für die folgenden Kapitel abzuschließen.
Im Einzelnen sieht dies nun so aus:
Zunächst einmal erstellen wir das Root-Objekt oder auch die Wurzel der Szene, dem alle
anderen Objekte untergeordnet werden. Dieses nennen wir auch dementsprechend
rootObject. Um die Szene später noch transformieren zu können, benötigen wir nun ein
TransformGroup-Objekt, welches den Namen tGroup erhält. Mit dessen Hilfe können wir
später sämtliche Transformationen auf alle Objekte als Ganzes ausführen, die dieser
TransformGroup angehören. Um nun endlich ein sichtbares Objekt in die Szene zu
bringen, verwenden wir die importierte Klasse ColorCube. Diese erzeugt einen Würfel,
dessen Seiten unterschiedlich eingefärbt und alle den angegebenen Wert vom Mittelpunkt
entfernt sind. Diesen Würfel ordnen wir auch sogleich unserer TransformGroup unter, um
ihn später transformieren zu können. Des Weiteren muss die TransformGroup noch
unserem rootObject als Kind zugewiesen werden, um den Baum zu komplettieren. Am
Ende geben wir dann noch eine Referenz auf unser rootObject zurück.
Der hierzu erforderliche Code sieht folgendermaßen aus:
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph()
{
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Eine TransformGroup erzeugen, um später Transformationen durchzuführen
TransformGroup tGroup = new TransformGroup();
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
Und nun zum Abschluss dieser Vorbereitung noch einmal den Code im Ganzen, der dazu
benutzt werden kann, eine eigene Java3D-Szene zu erschaffen und der auch im Folgenden
als Grundlage für weitere Erklärungen verwendet wird:
// Zu importierende Klassen
import java.applet.Applet;
import java.awt.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.ColorCube;
import javax.media.j3d.*;
import javax.vecmath.*;
Seite 10 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
public class Translation extends Applet
{
// Der Translation-Konstruktor kann für jedes beliebige andere Programm
// ebenfalls als Konstruktor verwendet werden, da er eine Szene in Java3D
// komplett vorbereitet und darstellt.
// Die eigentliche Szene wird in der Funktion createSceneGraph() weiter
// unten erstellt.
public Translation() {
// Setzen des Layout-Managers
setLayout(new BorderLayout());
// Aufruf der Funktion zur Erstellung des Szenengraphen (bzw. der Szene)
BranchGroup scene = createSceneGraph();
// Kompiliert (Optimiert) den Szenengraph
scene.compile();
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
// Erzeugt eine Leinwand (=Canvas), auf der die Szene dargestellt wird
Canvas3D canvas3D = new Canvas3D(config);
// In den Mittleren Bereich der Szene (Center) das Canvas3D-Objekt einfügen
add("Center", canvas3D);
// Erstellung des SimpleUniverse als Root-Objekt der gesamten Szene
SimpleUniverse universe = new SimpleUniverse(canvas3D);
// Die Kameraposition wird entlang der Z-Achse so versetzt, dass die
// Fenstergrenzen jeweils bei -1 und 1 des Koordinatensystems liegen
universe.getViewingPlatform().setNominalViewingTransform();
// Szenengraph in SimpleUniverse einfügen
universe.addBranchGraph(scene);
}
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph() {
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Transform3D-Objekt erzeugen, das die Rotationsmatrix aufnimmt sowie mit
// Einheitsmatrix belegen
Transform3D translate = new Transform3D();
// Um den angegebenen Vektor verschieben (X,Y,Z)
translate.set(new Vector3d( 0.0, 0.0, 0.0 ));
// Eine TransformGroup erzeugen, die das Transform3D-Objekt aufnimmt
TransformGroup tGroup = new TransformGroup();
// Die Transformation der TransformGroup zuordnen
tGroup.setTransform(translate);
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
// Die Hauptfunktion, die die Szene als Anwendung oder Applet aufruft
public static void main(String[] argv) {
new MainFrame(new Translation(), 256, 256);
}
}
Seite 11 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Als Ergebnis sollte dieser Code dann ein Ergebnis liefern, dass als Anwendung ausgeführt
dieses Fenster darstellt:
Abbildung 4 - SimpleScene
Man sieht einen Würfel mit einem Meter, denn genau das ist die Standard-Maßeinheit des
Koordinatensystems von Java3D, Kantenlänge, der zentriert im Fenster angezeigt wird.
Die Zentrierung entsteht dadurch, dass die Mitte des Fensters den Ursprung des
Koordinatensystems bildet und der Mittelpunkt des Würfels genau auf diesem liegt.
Um dies zu verändern, benötigen wir als erste Transformation die Translation, die im
nächsten Kapitel erklärt wird.
Seite 12 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
3.3.
Translation
Kommen wir nun also zur wahrscheinlich einfachsten aller Transformationen – der
Translation oder Verschiebung. In Java3D gibt es eigentlich nur zwei Möglichkeiten, eine
solche
Translation
durchzuführen.
Entweder
man
verwendet
hierzu
einen
dreidimensionalen Verschiebungsvektor (das ist die einfachere und gebräuchlichere
Methode), oder man gibt eine 4x4-Matrix an, die die Verschiebung enthält (das ist die
komplizierte in Kapitel 2 angesprochene Methode).
Beginnen wir also zunächst mit der Verschiebung unseres Würfels aus 3.2. per
Verschiebungsvektor. Hierzu benötigen wir als Erstes ein neues Objekt der Klasse
Transform3D, das die Verschiebung oder auch allgemein die Transformation aufnimmt,
die auf ein Objekt angewendet werden soll. Diesem Transform3D-Objekt geben wir
anschließend einen Vektor an, um den verschoben werden soll. In unserem Beispiel ist
dies der Vektor (-1.0, 1.0, -2.0), der das Koordinatensystem einer TransformGroup um
einen Meter nach links (-1.0), einen Meter nach oben (1.0) und zwei Meter nach hinten,
also ins Bild hinein (-2.0) verschiebt. Um dies zu erreichen muss die Transformation nun
nur noch mit der Methode setTransform dem TransformGroup-Objekt zugewiesen
werden, welches transformiert werden soll. Da wir momentan erst ein solches Objekt
erstellt haben, fällt die Auswahl nicht schwer. Der Code hierzu stellt sich folgendermaßen
dar (rot markierte Textelemente sind neu hinzugekommen):
public BranchGroup createSceneGraph()
{
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Eine TransformGroup erzeugen, um später Transformationen durchzuführen
TransformGroup tGroup = new TransformGroup();
// Transform3D-Objekt erzeugen, das die Transformationen aufnimmt
Transform3D translate = new Transform3D();
// Um den angegebenen Vektor verschieben (X,Y,Z)
translate.set(new Vector3d( -1.0, 1.0, -2.0 ));
// Die Transformation der TransformGroup zuordnen
tGroup.setTransform(translate);
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
Seite 13 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Das Ergebnis dieser Transformation sieht dann folgendermaßen aus, wobei links noch
einmal die Ausgangssituation ohne Transformation und rechts die Szene nach erfolgter
Translation abgebildet ist. Nun kann man auch erstmals erkennen, dass es sich bei
unserem ColorCube wirklich um einen Würfel und nicht nur um ein Quadrat handelt.
Abbildung 5 – SimpleScene
Abbildung 6 - Translation
Kommen wir nun zur etwas unübersichtlicheren Methode, der Translation mittels Vorgabe
der Transformationsmatrix. Hierbei gilt es, den gewünschten Verschiebungsvektor in die
in Kapitel 2 für die Translation angegebene 4x4-Matrix einzusetzen und diesen dann wie
oben beschrieben der TransformGroup zuzuweisen. Für obige Translation (-1.0, 1.0, -2.0)
ergibt sich also folgendes Bild:
Allgemeine Translationsmatrix für eine Verschiebung um (a, b, c):
1
0
0
0
0
1
0
0
0
0
1
0
a
b
c
1
Einsetzen des Vektors (-1.0, 1.0, -2.0) ergibt folgende Matrix:
1
0
0
0
0
1
0
0
0
0
1
0
-1
1
-2
1
Wir müssen also, um dieselbe Verschiebung wie oben zu erreichen mit obiger Matrix
transformieren. Dies geschieht durch folgende Code-Zeile, mit der der entsprechende
translate.set-Methodenaufruf aus obigem Programm ersetzt wird:
// Folgende Matrix als Transformationsmatrix angeben
translate.set(new Matrix4d( 1.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0,
1.0, -2.0, 0.0, 0.0, 0.0, 1.0 ));
Mit Hilfe von Matrizen lassen sich übrigens auch alle anderen hier angesprochenen
Transformationen ausführen. Jedoch wird darauf nicht mehr explizit eingegangen.
Seite 14 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
3.4.
Skalierung
Als nächste ebenfalls sehr einfache Transformation wollen wir nun die Skalierung, also
das Stauchen und Strecken einzelner oder auch aller Koordinatenachsen behandeln.
Für die Skalierung bietet Java3D zwei Methoden an. Bei einer werden alle Achsen des
Koordinatensystems mit dem gleichen Faktor skaliert, bei der anderen lässt sich für jede
Achse der gewünschte Skalierungsfaktor separat einstellen.
Beginnen wir mit der uniformen Skalierung aller Achsen. Hier kann wieder auf unsere
SimpleScene zurückgegriffen werden, deren Funktion createSceneGraph nur noch um die
Skalierung des Koordinatensystems ergänzt werden muss. Dies geschieht nach folgender
Vorgehensweise, die der bei der Translatio n erklärten gleicht. Wir erzeugen ein
Transform3D-Objekt,
das
die
Transformation
aufnehmen
kann,
setzen
den
Skalierungsfaktor fest und weisen das Transform3D am Ende der TransformGroup zu.
Der benötigte Java-Code sieht folgendermaßen aus, wobei rote Codefragmente zur
Ausgangsszene SimpleScene hinzugefügt wurden:
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph()
{
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Eine TransformGroup erzeugen, um später Transformationen durchzuführen
TransformGroup tGroup = new TransformGroup();
// Transform3D-Objekt erzeugen, das die Transformationen aufnimmt
Transform3D scale = new Transform3D();
// Alle Achsen um den angegebenen Vektor skalieren
scale.set(0.5);
// Die Transformation der TransformGroup zuordnen
tGroup.setTransform(scale);
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
Jedoch lassen sich wie bereits erwähnt die einzelnen Koordinatenachsen auch
unterschiedlich skalieren. Dies erreicht man durch Verwendung der Methode setScale
anstatt der universalen set-Methode. Der setScale-Methode muss man hierbei lediglich
einen dreidimensionalen Vektor übergeben, der sich aus Skalierung in X-, Y- und ZRichtung zusammensetzt.
Seite 15 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Für eine unterschiedliche Skalierung der Achsen ist also die Zeile „scale.set…“ durch
folgende zu ersetzen, wenn die X-Achse mit dem Faktor 0,5 gestaucht, die Y-Achse mit
dem Faktor 1,5 gestreckt und die Z-Achse unverändert bleiben soll:
// Die Achsen mit dem angegebenen Vektor skalieren (X, Y, Z)
scale.setScale(new Vector3d( 0.5, 1.5, 0.0 ));
Zum Vergleich hier noch einmal die SimpleScene mit dem Standard ColorCube der
Kantenlänge 1 (Abbildung 7), der uniform mit dem Faktor 0,5 gestauchte ColorCube
(Abbildung 8) und der mittels Skalierungsvektor veränderte ColorCube (Abbildung 9).
Abbildung 7 - ColorCube ohne Transformation
Abbildung 8 – Uniforme Skalierung
Seite 16 von 32
Abbildung 9 – Skalierung per Vektor
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
3.5.
Rotation
Kommen wir nun zur letzten hier behandelten Transformation – der Rotation oder
Drehung um eine oder mehrere Achsen. Diese bietet die größte Vielfalt an Methoden und
möglichen Vorgehensweisen. Fangen wir jedoch erst einmal ganz einfach an – mit der
Rotation um eine Koordinatenachse, beispielsweise die x-Achse.
Hierzu benötigen wir wiederum die Szene SimpleScene, die wie oben um ein
Transform3D-Objekt erweitert wird. Diesem Transform3D-Objekt weisen wir nun als
Transformation eine Rotation um die x-Achse zu, indem wir dessen Methode rotX
verwenden. Entsprechend existieren die Methoden rotY und rotZ. Diese Methoden
verlangen nur einen Übergabeparameter, und zwar den Wert, um den rotiert werden soll.
Hierbei ist zu beachten, dass die Werte im Radiusmaß übergeben werden müssen. Die
Konstante Pi entspricht hierbei 180°, 2 mal Pi wären dann entsprechend 360°. Da wir den
Würfel aber zunächst nur um 45° rotieren wollen, übergeben wir der Methode rotX Pi/4.
Abschließend
müssen
TransformGroup
wir
zuweisen.
das
Der
Transform3D-Objekt
hierzu
wie
erforderliche
gewohnt
noch
Programm-Code
der
sieht
folgendermaßen aus:
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph()
{
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Eine TransformGroup erzeugen, um später Transformationen durchzuführen
TransformGroup tGroup = new TransformGroup();
// Transform3D-Objekt erzeugen, das die Transformationen aufnimmt
Transform3D rotate = new Transform3D();
// Um 45° um die X-Achse rotieren (PI = 180°)
rotate.rotX(Math.PI/4d);
// Die Transformation der TransformGroup zuordnen
tGroup.setTransform(rotate);
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
Das Ergebnis dieser Rotation im Vergleich zur ursprünglichen SimpleScene ist auf der
Folgeseite abgebildet.
Seite 17 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Abbildung 10 – SimpleScene
Seite 18 von 32
Abbildung 11 - Rotation um die x-Achse
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Natürlich lassen sich mit Java3D auch mehrere Rotationen verketten. Dazu erstellen wir
zunächst zwei weitere Transform3D-Objekte und belegen eines mit einer Rotation um die yAchse, das andere mit einer Rotation um die z-Achse. Anschließend multiplizieren wir die
beiden Rotationsmatrizen nacheinander jeweils mit der Ausgangstransformation und schon
haben wir eine verkettete Rotation um alle drei Achsen. Der Quellcode hierzu sieht
folgendermaßen aus:
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph()
{
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Eine TransformGroup erzeugen, um später Transformationen durchzuführen
TransformGroup tGroup = new TransformGroup();
// Transform3D-Objekte für die X-, Y- und Z-Rotation erzeugen
Transform3D rotate = new Transform3D();
Transform3D rotateY = new Transform3D();
Transform3D rotateZ = new Transform3D();
// Um 45° um die X-Achse rotieren (PI = 180°)
rotate.rotX(Math.PI/4d);
// Um 45° um die Y-Achse rotieren
rotateY.rotY(Math.PI/4d);
// Um 45° um die Z-Achse rotieren
rotateZ.rotZ(Math.PI/4d);
// Rotationsmatrizen nacheinander multiplizieren
rotate.mul(rotateY);
rotate.mul(rotateZ);
// Die Transformation der TransformGroup zuordnen
tGroup.setTransform(rotate);
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
Hierbei ist jedoch zu beachten, dass die Rotationen nacheinander durchgeführt werden.
Das komplette Koordinatensystem wird also zunächst um 45° um die x-Achse rotiert,
anschließend um 45° um die bereits veränderte y-Achse und abschließend um die zweimal
gedrehte z-Achse. Eine Slideshow dieser ganzen Rotationskette findet sich zum besseren
Verständnis auf der folgenden Seite. Hierbei sollte man immer das Java3D-typische
Koordinatensystem (s. Abbildung 2) im Hinterkopf haben.
Seite 19 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Abbildung 12 – Ausgangsszene
Abbildung 13 - Nach der x-Rotation
Abbildung 14 - Nach der y-Rotation
Abbildung 15 - Nach der z-Rotation
Wie man jedoch schnell erkennt, ist das ein ziemlich umständlicher Weg, mehrere
Rotationen durchzuführen. Natürlich gibt es dafür eine einfachere Methode, die zudem
den Vorteil hat, dass nicht nacheinander rotiert wird, sondern alle Rotationen quasi
gleichzeitig
durchgeführt
werden.
Jede
Einzelrotation
wird
also
direkt
am
Ausgangskoordinatensystem vorgenommen und das Ergebnis ist daher auch viel einfacher
vorherzusagen. Benutzt wird hierzu lediglich eine neue Methode namens SetEuler, der
man drei Rotations faktoren für die drei möglichen Rotationsrichtungen übergibt. Der
Quellcode und das entsprechende Ergebnis stehen auf der Folgeseite. Man beachte den
Unterschied der Ausgabe zum vorangegangenen Beispiel.
Seite 20 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
// Hier wird die eigentliche Szene erstellt (bzw. modelliert)
public BranchGroup createSceneGraph()
{
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
// Eine TransformGroup erzeugen, um später Transformationen durchzuführen
TransformGroup tGroup = new TransformGroup();
// Transform3D-Objekt erzeugen, das die Transformationen aufnimmt
Transform3D rotate = new Transform3D();
// In jede Richtung jeweils um den angegebenen Winkel rotieren
rotate.setEuler(new Vector3d( Math.PI/4d, Math.PI/4d, Math.PI/4d ));
// Die Transformation der TransformGroup zuordnen
tGroup.setTransform(rotate);
// Den ColorCube als Kind-Objekt in die TransformGroup aufnehmen
tGroup.addChild(new ColorCube(0.5));
// Die TransformGroup als Kind-Objekt ins Root-Objekt aufnehmen
rootObject.addChild(tGroup);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
Das entsprechende Ergebnis dieser Operation sieht dann so aus (im Gegensatz zur
verketteten Multiplikation weiter oben):
Abbildung 16 - Rotation mittels setEuler
Seite 21 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
3.6.
Anmerkungen
Grundsätzlich lassen sich durch Matrizenmultiplikation alle möglichen Transformationen
miteinander kombinieren, jedoch sollte man wie bereits angesprochen aber auf die
Reihenfolge der einzelnen Transformationen achten. Wie gesagt wird immer zuerst die
erste Transformation ausgeführt und das Koordinatensystem dieser TransformGroup
entsprechend verändert. Die nächste Transformation ganz gleich welcher Art derselben
TransformGroup bezieht sich dann mit ihren Angaben auf dieses veränderte
Koordinatensystem.
Des Weiteren sollte der Vollständigkeit halber an dieser Stelle noch erwähnt werden, dass
sich das Koordinatensystem eines Transform3D-Objekts wieder in den Anfangszustand
versetzen lässt, die Transformationen dieses Objekts also rückgängig gemacht werden,
wenn man die Methode setIdentity() des entsprechenden Transform3D-Objekts anwendet.
Im nächsten abschließenden Kapitel besprechen wir nun noch den Aufbau einer etwas
komplexeren Szene in Java3D. Viel Spaß damit!
Seite 22 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
4. Ein praktisches Beispiel
Beginnen wir nun also mit der Anwendung des Gelernten. Ziel unseres Beispiels ist die
sehr vereinfachte Nachbildung eines menschlichen Körperrumpfes und eines Armes. Der
Arm soll sich per Tastatureingabe bewegen lassen. Hierbei sollen Ober- und Unterarm
getrennt voneinander steuerbar sein.
Beginnen wir also mit der Planung des Modells: Zunächst erschaffen wir ein zentrales
Objekt, das die übrigen Objekte aufnimmt. Dieses nennen wir rootObject. Anschließend
erstellen wir eine TransformGroup koerper für den Körper, in die wir eine Box als
Symbol für den Rumpf aufnehmen. Ihr ordnen wir eine TransformGroup kopf unter, in
die wir eine Kugel zur Darstellung des Kopfes aufnehmen. Nun muss der Kopf noch auf
dem Körper platziert werden. Dies ge schieht durch Translation der TransformGroup kopf
an die entsprechende Stelle.
Als nächstes generieren wir eine TransformGroup oberarmPositionierung, die dazu dient,
den Oberarm später an die richtige Stelle in der Szene bewegen zu können. Diese ordnen
wir der TransformGroup koerper unter. Anschließend erstellen wir eine weitere
TransformGroup oberarmRotater und ordnen diese der Gruppe oberarmPositionierung
unter. Mit Hilfe von oberarmRotater können wir später den Arm bewegen bzw. rotieren.
Die TransformGroup oberarmRotater muss außerdem für Veränderungen während der
Laufzeit freigegeben werden. Dies geschieht durch Aufrufen der Methode setCapability
mit dem Parameter TransformGroup.ALLOW_TRANSFORM_WRITE. Jetzt erstellen
wir noch eine TransformGroup oberarm und ordnen diese der Gruppe oberarmRotater
unter. Nun platzieren wir noch einen Zylinder in der Gruppe oberarm, der den Oberarm
darstellt. Zum Schluss verschieben wir diesen so, dass der Punkt, an dem der Oberarm mit
dem Körper verbunden ist, im Ursprung des übergeordneten Koordinatensystems der
Gruppe oberarmRotater liegt und schon ist der Oberarm fertig.
Um den Oberarm später jedoch per Tastatur bewegen zu können, müssen wir der
TransformGroup oberarmRotater noch ein Verhalten zuweisen. Dies wird hier jedoch
nicht näher erläutert, da dies nicht zum Thema Transformationen gehört und den Umfang
dieser Ausarbeitung sprengen würde. Für nähere Informationen hierzu siehe QuelltextKommentare im Anhang bzw. die Ausarbeitung eines Kommilitoenen zum Thema
Interaktionen.
Seite 23 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Als letztes Objekt wollen wir nun noch einen Unterarm erstellen, der ebenfalls bewegt
werden kann. Die Vorgehensweise hierbei ist analog zur oben beschriebenen Erstellung
des Oberarms und wird daher nicht näher erläutert.
Die fertige Szene hat also folgende Struktur:
BG rootObject
Box
Sphere
TG koerper
TG oberarmPositionierung
TG kopf
TG oberarmRotater
Cylinder
TG oberarm
TG unterarmPositionierung
TG unterarmRotater
Cylinder
BG: BranchGroup
TG unterarm
TG: TransformGroup
Abbildung 17 - Der "Content"-Zweig des Szenengraphen
Den einzelnen Objekten der Szene lassen sich nun noch unterschiedliche Farben
zuordnen, um sie besser unterscheiden zu können. Außerdem sollte die TransformGroup
koerper mit allen ihr untergeordneten Objekten noch etwas verschoben und rotiert werden,
um den Oberarm besser in Szene zu setzen.
Wie das fertige Programm aussehen könnte, ist auf der nächsten Seite zu sehen. Sowohl
Ober- als auch Unterarm lassen sich in jeweils zwei Richtungen per Tastatureingabe
bewegen. Der Quelltext dafür kann im Anhang dieser Ausarbeitung eingesehen werden.
Seite 24 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Abbildung 18 - Das fertige Programm
Dieses Beispielprogramm stellt selbstverständlich nur einen Ansatz für den Einstieg in die
Programmierung mit Java3D dar und kann bei Bedarf noch beliebig erweitert werden. So
könnte man das Modell vervollständigen bzw. feiner modellieren. Auch die Verwendung
von Farben und Texturen kann hieran gut erprobt werden. Java3D bietet zur weiteren
Gestaltung eine große Vielfalt an Möglichkeiten, die es zu erlernen gilt.
Ich hoffe, diese Ausarbeitung konnte die in sie gesetzten Erwartungen erfüllen und
wünsche weiterhin noch viel Spaß mit Java3D.
Seite 25 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Quellenverzeichnis
[HalM]
Multimediaprogrammierung und Sensorik 5 – Java3D Teil 5
Autor Michael Haller (01.10.2002)
http://webster.fhs-hagenberg.ac.at/staff/haller/mmp5_20012002/06java3d_1.pdf
abgerufen am 26.10.2002
[HeiW]
Skript zur Lehrveranstaltung Graphische Datenverarbeitung an der FH Fulda
Autor Prof. Dr. W. Heinzel (Stand: SS 2000)
[JAPI]
Java3D 1.3 – API
http://java.sun.com
[JoyK]
Online Computer Graphics Notes – Transformation
Autor Ken Joy (06.12.1999)
http://graphics.cs.ucdavis.edu/GraphicsNotes/Transformations/Transformations.html
abgerufen am 21.10.2002
[SoNa]
Siggraph 99 - Introduction to Programming with Java 3D
Autoren Henry Sowizral & Dave Nadeau (Juli 1999)
http://www.sdsc.edu/~nadeau/Courses/Siggraph99/
abgerufen am 15.10.2002
Seite 26 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
Anhang
Hier nun der Quellcode, des in Kapitel 4 besprochenen Beispielprogramms:
/* Autor: Florian Hüter
letzte Änderung: 26.10.2002
*
* Klassenname: Mensch
*
* Entwickelt im Rahmen des Seminars Java3D bei Prof. Dr. W. Heinzel an der
* FH Fulda im WS 2002/03.
*
* Es wird ein Teil eines Menschen nachgebildet, dessen Ober- und Unterarm
* separat voneinander bewegt werden kann.
*
* Tastenbelegung:
* --------------*
* Oberarmsteuerung:
Unterarmsteuerung:
* Anlegen:
d
Anwinkeln:
k
* Strecken:
a
Strecken:
i
* Nach vorne:
s
Nach außen:
l
* Nach hinten: w
Nach innen:
j
* Zurücksetzen: f
Zurücksetzen: h
*
*
* Anmerkung:
* Da Teile der Klasse SimpleBehavior einem Beispielprogramm von Sun
* entnommen wurden, muss folgender Lizenzvermerk im Quellcode erscheinen:
*
*
* @(#)SimpleBehaviorApp.java 1.1 00/09/22 16:24
*
* Copyright (c) 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
/***
import
import
import
import
Zu importierende Klassen
***/
java.applet.Applet;
java.awt.*;
com.sun.j3d.utils.applet.MainFrame;
com.sun.j3d.utils.universe.*;
Seite 27 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
import
import
import
import
import
com.sun.j3d.utils.geometry.*;
javax.media.j3d.*;
javax.vecmath.*;
java.awt.event.*;
java.util.Enumeration;
public class Mensch extends Applet
{
/***
Konstruktor
***/
// Der Konstruktor initalisiert die Java3D-Umgebung
public Mensch()
{
setLayout(new BorderLayout());
// Aufruf der Funktion zur Erstellung des Szenengraphen (bzw. der Szene)
BranchGroup scene = createSceneGraph();
// Kompiliert (Optimiert) den Szenengraph
scene.compile();
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
// Erzeugt eine Leinwand (=Canvas), auf der die Szene dargestellt wird
Canvas3D canvas3D = new Canvas3D(config);
// Java3D-Koordinatensystem wird zur Leinwand hinzugefügt
add("Center", canvas3D);
// Erstellung des SimpleUniverse als Root-Objekt der gesamten Szene
SimpleUniverse universe = new SimpleUniverse(canvas3D);
// Die Kameraposition wird entlang der Z-Achse so versetzt, dass die
// Fenstergrenzen jeweils bei -1 und 1 des Koordinatensystems liegen
universe.getViewingPlatform().setNominalViewingTransform();
// Szenengraph in SimpleUniverse einfügen
universe.addBranchGraph(scene);
}
/***
Verhaltensklasse für Oberarm
***/
// Klasse, die das Verhalten des Oberarms steuert
// Sie wurde einem Beispiel von Sun entnommen (s.o.) und erweitert
public class SimpleBehavior extends Behavior {
private
private
private
private
TransformGroup oberarm;
Transform3D oberarmRotation = new Transform3D();
Transform3D oberarmRotationTemp = new Transform3D();
WakeupCondition cond;
// Konstruktor
SimpleBehavior(TransformGroup tg1){
this.oberarm = tg1;
}
// Initalisiere das Verhalten
public void initialize(){
cond = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
this.wakeupOn(cond);
}
// Verhalten
// Wird von Java automatisch augerufen, wenn entsprechender "Stimulus"
// kommt (bestimmtes Event eintritt)
public void processStimulus(Enumeration criteria){
oberarmRotationTemp.set(oberarmRotation);
WakeupOnAWTEvent event = (WakeupOnAWTEvent)criteria.nextElement();
Seite 28 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
KeyEvent key = (KeyEvent)event.getAWTEvent()[0];
char c = key.getKeyChar();
// Per Tasteneingabe Richtung der Drehung steuern
switch(c) {
case 'd':
oberarmRotation.rotZ(0.1);
oberarmRotation.mul(oberarmRotationTemp);
oberarm.setTransform(oberarmRotation);
break;
case 'a':
oberarmRotation.rotZ(-0.1);
oberarmRotation.mul(oberarmRotationTemp);
oberarm.setTransform(oberarmRotation);
break;
case 'w':
oberarmRotation.rotX(0.1);
oberarmRotation.mul(oberarmRotationTemp);
oberarm.setTransform(oberarmRotation);
break;
case 's':
oberarmRotation.rotX(-0.1);
oberarmRotation.mul(oberarmRotationTemp);
oberarm.setTransform(oberarmRotation);
break;
// Anfangszustand wiederherstellen
case 'f':
oberarmRotation.setIdentity();
oberarm.setTransform(oberarmRotation);
break;
default: {}
}
this.wakeupOn(cond);
}
} // Ende der Klasse SimpleBehavior
/***
Verhaltensklasse für Unterarm
***/
// Klasse, die das Verhalten des Unterarms steuert
// SimpleBehavior2 stellt nahezu die gleiche Funktion wie SimpleBehavior
// zur Verfügung, jedoch werden hier andere Tasten verwendet
public class SimpleBehavior2 extends Behavior {
private
private
private
private
TransformGroup unterarm;
Transform3D unterarmRotation = new Transform3D();
Transform3D unterarmRotationTemp = new Transform3D();
WakeupCondition cond;
// Konstruktor
SimpleBehavior2(TransformGroup tg1){
this.unterarm = tg1;
}
// Initalisiere das Verhalten
public void initialize(){
cond = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
this.wakeupOn(cond);
}
// Verhalten
// Wird von Java automatisch augerufen, wenn entsprechender "Stimulus"
// kommt (bestimmtes Event eintritt)
public void processStimulus(Enumeration criteria){
unterarmRotationTemp.set(unterarmRotation);
WakeupOnAWTEvent event = (WakeupOnAWTEvent)criteria.nextElement();
KeyEvent key = (KeyEvent)event.getAWTEvent()[0];
char c = key.getKeyChar();
// Per Tasteneingabe Richtung der Drehung steuern
switch(c) {
Seite 29 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
case 'j':
unterarmRotation.rotZ(0.1);
unterarmRotation.mul(unterarmRotationTemp);
unterarm.setTransform(unterarmRotation);
break;
case 'l':
unterarmRotation.rotZ(-0.1);
unterarmRotation.mul(unterarmRotationTemp);
unterarm.setTransform(unterarmRotation);
break;
case 'i':
unterarmRotation.rotX(0.1);
unterarmRotation.mul(unterarmRotationTemp);
unterarm.setTransform(unterarmRotation);
break;
case 'k':
unterarmRotation.rotX(-0.1);
unterarmRotation.mul(unterarmRotationTemp);
unterarm.setTransform(unterarmRotation);
break;
// Anfangszustand wiederherstellen
case 'h':
unterarmRotation.setIdentity();
unterarm.setTransform(unterarmRotation);
break;
default: {}
}
this.wakeupOn(cond);
}
} // Ende der Klasse SimpleBehavior2
/***
Farbgenerierungsmethode
***/
public Appearance makeAppearance( double r, double g, double b)
{
Appearance app = new Appearance();
ColoringAttributes farbe = new ColoringAttributes();
farbe.setColor((float) r, (float) g, (float) b);
app.setColoringAttributes(farbe);
return app;
}
/***
Modellierung der Szene
***/
public BranchGroup createSceneGraph()
{
// Festlegung einiger Farben
Appearance appRot = makeAppearance(1.0, 0.0, 0.0);
Appearance appGruen = makeAppearance(0.0, 1.0, 0.0);
Appearance appBlau = makeAppearance(0.0, 0.0, 1.0);
Appearance appGelb = makeAppearance(1.0, 1.0, 0.0);
// Root-Objekt innerhalb der Szene wird erstellt
BranchGroup rootObject = new BranchGroup();
/***
Körper
***/
// Erzeugt den Körper als oberste TransformGroup
TransformGroup koerper = new TransformGroup();
koerper.addChild(new Box(0.25f, 0.4f, 0.1f, appGelb));
// Den Körper durch Verschiebung und Drehung besser in Szene setzen
// Alle untergeordneten Objekte werden dadurch mit verschoben
Transform3D koerperAusrichtung = new Transform3D();
koerperAusrichtung.set(new Vector3d(0.4, 0.0, 0.0));
Transform3D koerperDrehung = new Transform3D();
Seite 30 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
koerperDrehung.rotY(0.3);
koerperAusrichtung.mul(koerperDrehung);
koerper.setTransform(koerperAusrichtung);
// Den Körper dem Wurzelobjekt der Szene als Kind zuweisen
rootObject.addChild(koerper);
/***
Kopf
***/
TransformGroup kopf = new TransformGroup();
kopf.addChild(new Sphere(0.15f, appRot));
// Positioniert den Kopf am Körper und ordnet den Kopf dem Körper unter
Transform3D kopfPositionierung = new Transform3D();
kopfPositionierung.set(new Vector3d(0.0, 0.55, 0.0));
kopf.setTransform(kopfPositionierung);
koerper.addChild(kopf);
/***
Oberarm
***/
// Erzeugt die TransformGroup oberarmPositionierung, die zur Positionierung
// des Oberarms am Körper verwendet wird
TransformGroup oberarmPositionierung = new TransformGroup();
Transform3D oberarmAnKoerper = new Transform3D();
Transform3D oberarmDrehung = new Transform3D();
oberarmAnKoerper.set(new Vector3d(-0.25, 0.4, 0.0));
oberarmDrehung.rotZ(-Math.PI/4d);
oberarmAnKoerper.mul(oberarmDrehung);
oberarmPositionierung.setTransform(oberarmAnKoerper);
koerper.addChild(oberarmPositionierung);
// Erzeugt die TransformGroup oberarmRotater, die zur Rotation des
// Oberarms verwendet wird und oberarmPositionierung untergeordnet ist
TransformGroup oberarmRotater = new TransformGroup();
oberarmPositionierung.addChild(oberarmRotater);
// Erlaubt das Verändern dieser TransformGroup während der Laufzeit
oberarmRotater.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Die TransformGroup oberarm enthält den eigentlichen Oberarm
TransformGroup oberarm = new TransformGroup();
oberarmRotater.addChild(oberarm);
oberarm.addChild(new Cylinder(0.06f, 0.25f, appBlau));
Transform3D oberarmZuUrsprung = new Transform3D();
// Positioniert den Oberarm mit dem Drehpunkt im Zentrum des übergeordneten
// Koordinatensystems
oberarmZuUrsprung.set(new Vector3d(0.0, -0.125, 0.0));
oberarm.setTransform(oberarmZuUrsprung);
// Verhalten zur Interaktion hinzufügen
// Rotiert wird hierbei die TransformGroup oberarmRotater
SimpleBehavior oberarmRotationBehavior = new SimpleBehavior(oberarmRotater);
oberarmRotationBehavior.setSchedulingBounds(new BoundingSphere());
oberarmPositionierung.addChild(oberarmRotationBehavior);
/***
Unterarm
***/
// Erzeugt die TransformGroup unterarmRotater, die zur Positionierung
// des Unterarms am Oberarm verwendet wird
TransformGroup unterarmPositionierung = new TransformGroup();
Transform3D unterarmAnKoerper = new Transform3D();
Transform3D unterarmDrehung = new Transform3D();
unterarmAnKoerper.set(new Vector3d(0.0, -0.15, 0.0));
unterarmPositionierung.setTransform(unterarmAnKoerper);
oberarm.addChild(unterarmPositionierung);
// Erzeugt die TransformGroup unterarmRotater, die zur Rotation des
// Unterarms verwendet wird und unterarmPositionierung untergeordnet ist
Seite 31 von 32
Ausarbeitung zum Thema „Transformationen in Java3D“ von Florian Hüter im Oktober 2002
TransformGroup unterarmRotater = new TransformGroup();
unterarmPositionierung.addChild(unterarmRotater);
// Erlaubt das Verändern dieser TransformGroup während der Laufzeit
unterarmRotater.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Die TransformGroup unterarm enthält den eigentlichen Unterarm
TransformGroup unterarm = new TransformGroup();
unterarmRotater.addChild(unterarm);
unterarm.addChild(new Cylinder(0.06f, 0.25f, appGruen));
Transform3D unterarmZuUrsprung = new Transform3D();
// Positioniert den Oberarm mit dem Drehpunkt im Zentrum des übergeordneten
// Koordinatensystems
unterarmZuUrsprung.set(new Vector3d(0.0, -0.125, 0.0));
unterarm.setTransform(unterarmZuUrsprung);
// Verhalten zur Interaktion hinzufügen
// Rotiert wird hierbei die TransformGroup unterarmRotater
SimpleBehavior2 unterarmRotationBehavior = new
SimpleBehavior2(unterarmRotater);
unterarmRotationBehavior.setSchedulingBounds(new BoundingSphere());
unterarmPositionierung.addChild(unterarmRotationBehavior);
// Die Szene bzw. das Root-Objekt zurückgeben
return rootObject;
}
// Die Hauptfunktion, die die Szene als Anwendung oder Applet aufruft
public static void main(String[] argv)
{
new MainFrame(new Mensch(), 256, 256);
}
}
Seite 32 von 32
Herunterladen