Systemprogrammierung Realisierung eines 3D

Transcription

Systemprogrammierung Realisierung eines 3D
Projektdokumentation
1
Systemprogrammierung
WS 03/04
Realisierung eines 3D-Spiels mittels
Lightwave3D und Java3D
Entwicklungsdokumentation
Hristo Matev
Hristo Matev
Projektdokumentation
2
Inhaltsverzeichnis
1
Zusammenfassung
3
2
Projektaufgaben
4
2.1. Animationsintro mit Lightwave3D
4
2.2. Terrainmodellierung
9
2.3. Modellierung der Roboter
10
2.4. Collision Detection
10
2.5. Gameplay / Spiellogik
11
2.6. Bewegung des Spielers mit Tasten
12
2.7. Bewegung des Spielers mit Tasten und Maus
12
2.8. Raumauswahl
13
2.9. Optimierung der Berechnung der virtuellen Welt
15
2.10. Netzwerkspiel
15
2.11. Textureierung vom Terrain und Roboter
18
3
Relevante Probleme und Lösungen
20
4
Fazit
22
5.
Literatur
22
Hristo Matev
Projektdokumentation
3
1. Zusammenfassung
Das Projekt ,genannt mit dem Codenamen „Cactus“, hatte zum Ziel die
Realisierung eines 3D-Spiels. Um dieses Ziel zu erreichen wurden 3DWerkzeuge (hier Lightwave3D) zum Erzeugen der Geometrie und
Programmierschnittstellen (hier die Java3D API) zur Programmierung benutzt.
Die Geometrie umfasst alle Objekte (Landschaft, Häuser, Roboter u.s.w.), die in
der virtuellen Szene teilnehmen. Die Programmierung umfasst die Interaktion der
Objekten miteinander und mit der Kamera.
Mit Hilfe der Java3D API und dem Lightwave3D Werkzeug wurde eine virtuelle
Welt erzeugt, mit der den Spieler eine Interaktion aufnehmen kann. Es wurden
die Tastatureingaben und die Mausbewebungen berücksichtigt, auf die, die
Objekten in der Szene reagieren.
(Collision Detection und Terrain Following).
Ferner benutzt die Implementierung einen dynamischen Aufbau. Der Benutzer
kann die Objekten und Texturen manipulieren oder neue erstellen. Dabei braucht
er keine Programmierkenntnisse zu besitzen. Um die Programmierung
unabhängig von Konstanten im Quelltext zu machen, wurde eine
Konfigurationsdatei angelegt, die diese enthält. Ein weiteres Feature ist der
Fullscreen – Modus in dem man auch spielen kann.
Zum Spielstart und zur Motivation des Spielers wurde eine Animation mit
Lightwave3D erstellt, die das Spiel kurz beschreibt.
Hristo Matev
Projektdokumentation
4
2. Projektvorgaben
Bei dem Projekt sollten folgenden Augeben gelöst/behandelt werden:
Animationsintro mit Lightwave3D
Terrainmodellierung
Modellierung der Roboter
Collision Detection
Gameplay / Spiellogik
Bewegung des Spielers mit Tasten
Bewegung des Spielers mit Tasten und Maus
Raumauswahl
Optimierung der Berechnung der virtuellen Welt
Netzwerkspiel
Textureierung vom Terrain und Roboter
2.1. Animationsintro mit Lightwave3D
(Realisierung der Videosequenz)
Am Anfang des Spiels sollte eine Videosequenz zu sehen sein. Diese soll die
Spielwelt dem Spieler eröffnen und die Spielziele vorstellen.
In der Animation ist eine Welt zu sehen ,die von Feinden mit der Hilfe von einer
sehr mächtigen Waffe zerstört wird. Damit diese Waffe nie wieder zur Zerstörung
benutzt wird, soll der Spieler diese finden und ausschalten.
Die Animationsszenen und Objekten wurden komplett mit Lightwave3D erstellt
und dann mit Adobe Premiere zusammengefasst und mit Music / Sounds
unterlegt.
Hristo Matev
Projektdokumentation
5
Die Szenen:
(in Klammern werden die Lightwave Features genannt, die dabei eine
wesentliche Rolle gespielt haben)
1. Eine Flagge
(Motion Designer).
2. Die Waffe im All
(Particle Animation und Displacement Mapping).
3. Eine Herbstszene mit der Zerstörung dieser Welt
(Morphing, Motion Designer, Particle Animation und Texture Mapping).
4. Eine Landschaft mit einem Gebäude und drei fliegende Artefakten
(Geometrie und Lichteinstellungen).
5. Eine Farbeniteration, die eine Reise im All darstellen soll
(Particle Animation)
6. Vorstellung des Namen des Autors und der Lehrveranstaltung
(Particle Animation)
Das Video wurde mit Music unterlegt. Der Autor des Musikstücks:
Fluke – Zion (Matrix Reloaded Soundtrack)
Die Soundeffekten wurden frei vom Internet heruntergeladen. Die beiden Quellen
sind: http://derbauer.de und das Spiel Quake2.
(Der gesamte Quelltext von Quake2 incl. Sounds kann man frei herunterladen.)
zu 1)
Diese Szene ist eigentlich ganz einfach aufgebaut. Die einzige Herausforderung
war die Animation mit dem Lightwave Plug-in – Motion Designer. Die Flage
wurde als Target definiert und damit wird die Bewegung vom Plug-in mit Hilfe der
wirkenden Kräfte berechnet. Die Kräfte, wie in der realen Welt sind die
Schwerkraft, der Selbstwiderstand und der Wind. Da alle andere konstante
Kräfte sind, wurde in der Szene nur die Windposition animiert.
Hristo Matev
Projektdokumentation
6
zu 2.)
Die Waffe besteht aus vier Teile, die sich um die globale Achse der Waffe (als
Ganzes) drehen. Es wurden Lichtquellen mit der „Lens Flares“ – Option
eingestellt, Hintergrund - Particles mit einer Textur und „Turbulence“ als
Bewegung (daher auch die Bewegung der Particles). Im Zentrum der Waffe
befindet sich eine Sphäre, bei der die Farben interpoliert werden. Die Blitze
wurden von Linien-Objekten erzeugt, bei den eine Displacement Map gesetzt
wurde (Blitzbewegung) mit einem Glow – Effect (Glüheffekt).
zu 3.)
Das ist vielleicht die aufwendigste Sequenz in der ganzen Animation. In der
Szene befindet sich zu viel Bewegung als man auf dem ersten Blick sieht. Der
Wasserteich wurde aus einem mehrmals geschnittenen Polygon (Subdivide), der
dann zu einem Subpatch – Objekt konvertiert wurde. Es wurde drauf eine
Displacement Map gesetzt und die wellenförmige Bewegung des Wassers
simuliert. Die Wassertropfen wurden von einer schwarzweißen Textur (Ring) die
Hristo Matev
Projektdokumentation
7
auch als Displacement Map (Additive) gesetzt wurde. Diese wird dann mit einem
Falloff skaliert, damit man den entsprechenden Effekt erzielt. Der Baum wurde
von einem Cone – Objekt erstellt bei dem man Polygone ausgeschnitten,
transliert und zurückkopiert wurden. Der Baum wurde nach den Anweisungen
von Dave Jerrard erstellt (siehe anliegenden Webfiles). Die Blätter des baum
bewegen sich auch, was mit Morphing erzielt wurde (in der Animation nicht
sichtbar). Das fallende Blatt benutzt den „Motion Designer Plug-In“, um die
Bewegung zu simulieren. Dabei wird das Terrain als „Target“ definiert.
Das Terrain selbst wurde von einem Subpatch – Object mit einer Displacement
Map erstellt (wie das Wasser), nur wurde diese Map nicht animiert. Außerdem
sollte es in der Szene einen Nebel geben, der von HVParticles mit dem „Sprite“HyperVoxel – Effekt erstellt wurden. Das Grass wurde mit dem SasLite Plug-In
erstellt. Leider gibt es hier einen Renderingfehler, der zu spät gefunden wurde.
Die SasLite – Objekte verschwinden bei der Bewegung von den Particles. Das
liegt an der Tatsache, dass SasLite und HyperVoxel sogenannte „Pixel Filters“
darstellen. D.h. sie werden erst nach dem Rendern hinzugefügt und bei diesem
Vorgang spielt die Reihenfolge eine Rolle. Man kann das allerdings umgehen, in
dem man bei SasLite die Option „One-Pass Antialiasing“ nicht verwendet, da
HyperVoxel und SasLite in der Szene dieselbe Anzahl von Antialiasing –
Durchgänge durchführen müssen.
Der Meteorit besteht aus einer „Volumetric Light“ Punktlichtquelle und lässt eine
Spur von HVParticles (mit den entsprechenden Texturen). Die Vögel wurden aus
SubPatch – Objekte erstellt, wobei die Flügelanimation auch aus Morphing
besteht. Die Terrainverformung am Ende wurde auch dank dem „Morph Mixer“
Plug-in erstellt.
Die Szene sollte eine traurige Nachtstimmung besitzen, deshalb wurden die
Lichtquellen entsprechend angepasst und bei dem SkyTracer2 Plug-In die
Lichtquelle auf „Mond“ gesetzt.
Hristo Matev
Projektdokumentation
8
zu 4)
Diese Sequenz besitzt viel Geometrie, die man leicht erstellen kann und deshalb
wird sie hier nicht behandelt. Der Nebel und die fliegende Lichtquelle am Ende
sind auch mit der Hilfe von dem Particles (FX) Plug-In aufgebaut. Die
Herausforderung hier bestand in der Synchronisierung von allen Lichtquellen.
zu 5)
Die Szene besteht aus Particles, die in die Kamera fliegen. Wie bei allen
Particles - Szenen bestand die Herausforderung bei den richtigen HyperHoxels –
Einstellungen. Dazu wuden noch zwei Lichtquellen mit „Lens Flares“ hinzugefügt.
Hristo Matev
Projektdokumentation
zu 6)
Wie in allen Sequenzen soweit wurden hier auch Particles mit HyperVoxels
benutzt.
2.2. Terrainmodellierung
Aus persönlicher Sicht muss ich hier sagen, dass nach der Erstellung der
Videosequenz sieht die Modellierung vom Terrain und den anderen Lightwave –
Objekten als Kinderspiel aus. Daher werde ich auf detaillierten Erklärungen
verzichten. Das Terrain besteht aus einer Box, dass mehrmals geteilt wurde
(Subdivide). Dann wurden beliebig ausgewählten stellen nach oben oder unten
verschoben und so eine Landschaft erstellt.
Hristo Matev
9
Projektdokumentation
10
2.3. Modellierung der Roboter
Die Roboter wurden aus einer „Ball“ erstellt, bei der eine Stelle nach aussen
gezogen wurde (der Strahl). Der Strahl und die oben liegenden Polygone wurden
dann nach oben verschoben um einen „Kopf“ zu simulieren.
2.4 Collision Detection
Java3D bietet ein „eingebautes“ System für Collision Detection. Dieses System
kann man mit den Klassen:
WakeupOnCollisionEntry, WakeupOnCollisionMovement,
WakeupOnCollisionExit, WakeupOnViewPlattformEntry und
WakeupOnViewPlattformExit ansprechen. Das System registriert also Kollisionen
und wenn so eine Auftritt kann man eine Methode ausführen, die das behandelt.
Der größte Nachteil des Systems besteht aber darin, dass man ganz genau
weiß, dass man kollidiert hat. Man kann aber nicht feststellen, von welcher
Richtung die Kollision kommt. Deshalb wurde bei der Implementierung des
Spiels auf das Java3D-Collision-System verzichtet. Außerdem brauch ein Spiel
nicht Collision-Detection, sondern Collision Avoidance. Man möchte viel mehr die
Kollisionen vermeiden.
Um das zu erzielen wurde das Picking-Engine von Java3D benutzt.
Man kann also von beliebiger Stelle einen Strahl in einer Richtung projizieren
und die Lage der Objekten, die auf dem Weg stehen, ermitteln. Die Collision
Avoidance strahlt also in den vier Richtungen einen PickRay (Strahl zum Picken)
und gibt die Distanz zum nächsten Objekt zurück. Wenn diese Distanz zu klein
ist (die Grenze kann man in der Konfigurationsdatei einstellen) wird die weitere
Bewegung in dieser Richtung verhindert. Da sich die Kamera, mit den
Pfeiltasten, in vier Richtungen bewegen kann (links, rechts, vorne, hinten) wird in
jeder Richtung einen PickRay ausgesendet, die Ergebnisse ausgewertet und die
Erlaubnisse gesetzt. Die Erlaubnisse werden dann in einem Objekt gespeichert,
Hristo Matev
Projektdokumentation
11
dass von der Klasse, die für die Tastatur zuständig ist, bekannt ist. Abhängig von
diesen Werten wird auch die Bewegung kontrolliert.
Dasselbe Prinzip wird auch für das „Terrain Following“ benutzt. Man kann die
Distanz zum Boden messen und dieser immer auf einem bestimmten Wert
setzen, wenn sich die Terrainoberfläche ändert (bei der Bewegung).
Das Collision Avoidance funktioniert nicht immer optimal, das an der Tatsache
liegt, dass nur einen Strahl nach vorne gestrahlt wird. Deshalb ist es möglich,
dass besonders verformte Objekten nicht gefunden werden, obwohl diese da
sind (z.B. Äste). Wenn eine die Implementierung noch einmal durchgeführt
werden könnte, würde ich hier entweder mehr strahlen nach vorne senden (z.B.
für alle vier Ecken der Kamera), oder eine andere Form des Picking (PickShape)
benutzen.
(Abb. Bounds bei Collision Detection – Quelle: j3d.org)
2.5 Gameplay / Spiellogic
Hier werden die Ereignisse behandelt, die das Spielen als solches ermöglichen.
Laut Spielregeln ist das Spiel zu Ende, wenn der Spieler von einem Roboter
erfasst wird. Das Spiel wurde dagegen gewonnen, wenn man das Ziel
(die Waffe) gefunden hat. Genau bei so einer Kollision ist es eigentlich egal, von
welcher Richtung die Kollision kommt (Hauptsache Ziel erreicht). Deshalb
Hristo Matev
Projektdokumentation
12
werden für diese beiden Fällen Collision-Ereignisse mir der Kamera registriert.
Wenn die Kamera von einem Roboter erfasst wird (eine bestimmte Grenze, die
man in der Konfigurationsdatei setzen kann, überschreitet), dann wird die
entsprechende Methode aufgerufen und das Ereignis entsprechend abgefangen.
Dasselbe Prinzip wird beim Gewinnen des Spiels benutzt.
2.6.
Bewegung des Spielers mit Tasten
Wenn man vom Spieler in einem „First Person“ – Spiel spricht, meint man
eigentlich die Kamera. Wenn man den Spieler verschieben will, muss man nur
die Kamera bewegen. Diese Bewegung nennt man, in der 3D –
Programmierung, Translation. Um eine Bewegung empfinden zu können, muss
man also die Kamera in regelmäßigen Zeitabstände in einer Richtung translieren.
Um die regelmäßigen Abständen definieren zu können, kann man einen Timer
erstellen, der die Kamera z.B alle 200ms um 0.1m nach vorne bewegt. Die
andere Möglichkeit ist die Eigenschaften des Systems auszunutzen.
Normalerweise wird ein KeyEvent wiederholt aufgerufen, wenn man eine Taste
gedrückt hält. Die Wiederholrate kann man z.B. bei MS Windows unter
Start -> Einstellungen -> Systemsteuerung -> Testatur einstellen.
Genau diese Eigenschaft wird hier ausgenutzt und somit wird die Kamera
(abhängig von der Richtung) immer um eine feste Grösse verschoben. Dieser
(Wert kann von der Konfigurationsdatei eingelesen werden.)
2.7. Bewegung des Spielers mit Tasten und Maus
Nachdem man die Kamera in allen Richtungen bewegen kann will man diese
auch rotieren. Das kann man natürlich auch mit der KeyNavigator – Klasse
ermöglichen, nur hat sich für nützlich gezeigt, wenn man die Maus für die
Rotation und die Tastatur für die Translation zuständig macht.
Hristo Matev
Projektdokumentation
13
Um das zu ermöglichen, muss man die Mausbewegung abfangen und den
entsprechenden MouseEvent auswerten. Wenn der Spieler also die Maus nach
links oder rechts bewegt, soll sich die Kamera um die Y-Achse rotieren.
Außerdem soll die Maus immer im Zentrum des Fensters bleiben, d.h. die muss
auf dem Bildschirm zurückbewegt werden.
Die rückgängige Bewegung der Maus wird von der java.awt.Robot Klasse
übernommen. Leider löst dieses Vorgehen erneut einen MouseEvent, der von
der Kamera nicht berücksichtigt werden darf. Deshalb wird bei der
Implementierung zwischen den beiden unterschieden. Um die Kamera rotieren
zu können, muss man sich lediglich die Mauspositionen merken.
Z.B. wenn die Maus um 3 Pixel nach links bewegt wird, wird die Kamera um 3
Grad rotiert.
Eine Rotation nach Oben und nach unten wurde noch nicht implementiert. Da bei
dieser Rotation die lokale Y-Achse der Kamera mitrotiert wird, wird in diesem Fall
das Terrain-Following und das Collision Avoidance nicht richtig funktionieren.
(Bei der Rotation um die Y-Achse werden nur die lokalen X- und Z-Achsen
manipuliert)
2.8. Raumauswahl
Jedes Spiel braucht verschiedene Räume zur Auswahl. Diese werden entweder
als „levels“ gekennzeichnet oder nur zur Abwechslung benutzt. Das Spiel wurde
also so programmiert, dass jetzt jeder, der ausreichende Kenntnisse mit einem
3D-Werkzeug besitzt (z.B. Lightwave3D), kann neue Levels (Szenen)
hinzufügen. Und das ohne den Quelltext zu ändern oder Programmierkenntnisse
besitzen zu müssen. Man sollte nur einige Kleinigkeiten beachten:
- Jede Szene besitzt ihr eigenes Verzeichnis, das beim Start angegeben werden
soll (als Parameter).
- Die Texturen für die Objekten werden in einem Unterverzeichnis angelegt.
- Alle Objekten werden als „.obj“ – Dateien angelegt.
Hristo Matev
Projektdokumentation
14
Wenn man ein neues Level erzeugen möchte, muss man ein Verzeichnis
anlegen in dem alle Objekten gespeichert werden und die Texturen im
Unterverzeichnis, das „textures“ heißen muss.
Es gibt sieben Objekten in einer Szene (die Namen darf man nicht ändern):
"terrain.obj"
- Alle Polypone auf die man gehen darf (terrain following)
"robot.obj"
- Der Gegner (collision avoidance)
"house.obj"
- Gebäuden (collision avoidance)
"rockterrain.obj"
- Felsen (terrain following)
"rockcollide.obj"
- Felsen (collision avoidance)
"tree.obj"
- Bäume (collision avoidance)
"spikey.obj"
- Das Ziel (die Waffe - (collision avoidance))
Dabei ist wichtig zu beachten, dass nur die Roboter – Objekten mehrmals
geladen werden. Alle anderen Objekten werden nur einmal geladen. Somit muss
z.B. das „house“ Objekt alle Polygone enthalten, die zu Gebäuden passen und
mit der „house“ – Texture versehen werden.
Um die vorhandenen Szenen auszuwählen, muss man lediglich das Verzeichnis
beim Start angeben, in dem die Objekten und Texturen gespeichert wurden.
Wenn ein (oder mehrere) von den sieben Spielobjekten nicht gefunden wird,
werden diese ignoriert. (Das Spiel wird trotzdem gestartet.)
Hier ist es auch wichtig zu sagen, dass das „spikey“ – Objekt in einer Szene dem
„spikey“ – Objekt in einer anderen Szene ungleich ist. Obwohl die beiden
Objekten dieselbe Geometrie besitzen, unterscheiden sich diese bei der
Koordinaten der Vertices. D.h. die beiden Objekten haben wahrscheinlich
unterschiedlichen Positionen, die beim Einlesen benutzt werden.
Hristo Matev
Projektdokumentation
15
2.9. Optimierung der Berechnung der virtuellen Welt
Da die Java3D-Szene „in real time“ berechnet wird, sollte man diese für „schlecht
„ausgestatteten Systeme optimieren.
Dies wurde durch eine gute Strukturierung des Quelltextes erreicht. (Z.B. die
import - Angaben werden nur für die verwendeten Objekte deklariert, unnötige
Zeilen werden weggelassen).
Die Erstellung von unnötigen Objekten wird gespart. Wenn man z.B. die Klassen
„WakeupOn...“ benutzt, muss man in jeder „processStimulus ()“ – Methode das
„Wakeup“ - Kriterium neu setzen. Wenn man hier anonyme Objekte erzeugt, wird
nur Arbeit für den GarbageCollector geschaffen.
Eine andere Art zur Optimierung ist Möglichkeit des Benutzers bestimmte
„Extras“ auszuschalten: (z.B. Texturen) . Da die Berechnung u.U. sehr aufwendig
ist hat man hier die Möglichkeit die Skalierung der Textur zu bestimmen oder
diese ganz auszuschalten (mit dem entsprechendem Kommandoparameter)
Zukünftige Implementierungen werden auch die Möglichkeit haben, die
Lichtquellen (auf die man hier fast verzichtet hat) zu manipulieren und diese auch
auszuschalten.
2.10. Netzwerkspiel
Aus Zeitgründen wurde die Implementierung des Netzwerkspiels weggelassen.
Diese Implementierung sollte aber mit Java und Java3D sehr bequem und relativ
schnell Realisierbar sein.
Bei einem Netzwerkspiel sollten zwei (oder mehrere) Spieler im Team das
Spielziel erreichen. Und damit es auch gerecht bleibt sollte den
Schwierigkeitsgrad des Spiels steigen. D.h. man sollte die Intelligenz der Roboter
steigen und/oder deren Anzahl.
Hristo Matev
Projektdokumentation
16
Wenn ein Spiel gestartet wird, wird der Server auch gestartet (entweder vom
Benutzer – Parameterübergabe und Menüauswahl (noch nicht implementiert))
oder automatisch. Der Server soll auf einem bestimmten Port auf Anfragen
warten. Da dieses Warten (eine I/O Socket-Operation) blockierend ist, sollte
einen Thread dafür gestartet werden, solange das Spiel läuft.
/* Code Beispiel
*/
private ServerSocket serversocket;
public ChatServer ()
{
try
{
serversocket = new ServerSocket (4712);
while (true) addClient(serversocket.accept ());
}
catch (IOException e)
{
e.printStackTrace();
};
}
Ein anderer Thread soll die tatsächliche Arbeit durchführen und die Änderungen,
die auf dem Serverspiel passieren an allen Clients, die in einer Liste gespeichert
werden sollen, weiterschicken. Diese Änderungen beinhalten die Transform3DObjekte. D.h. der Server schickt in regelmäßigen Abstände die Transformationen
der Trapsformgruppen der Roboter und der Kameras an allen Clients. Somit
entspricht die Summe der durch das Netz gesendeten Objekte der Summe von
allen Roboter und Kameras (für einen Client) Der Server berechnet die
Transformationen der Roboter selbst und soll nur die Transformationen der
Kameras der anderen Spieler bekommen.
Hristo Matev
Projektdokumentation
17
(Abb. Netzwerkspiel)
Der Klient braucht nur zwei Threads. Der erste soll auf dem Socket auf
Ein/Ausgabe warten, der andere soll den lokalen Szenengraph modifizieren.
Wenn sich ein Klient verbindet, soll er auch alle Transform3D Objekte der
Roboter und Kameras erhalten und in einer Liste speichern. Wenn sich in dieser
Zeit ein Klient abmeldet, soll er auch von der Liste entfernt werden.
Daher sollte der Klient mindestens diese Methoden implementieren:
public void add (TransformGroup wer, String was,
Transform3D wo);
public void remove (TransformGroup wer);
public void move (TransformGroup wer, Transform3D wohin);
Die entsprechende Werte für “wer” und „wo“ können z.B. in einem HashtableObjekt gespeichert werden. Das „was“ ist nur zur Unterscheidung von Roboter
und Spieler, die bei diesen Fällen verschiedenen Objekten geladen werden
sollen.
Hier sind nicht nur die Roboter als TransformGroup – Objekten, sondern auch
die Kameras der in der Szene gemeint. Ferner sollte eine Kamera (das
Hristo Matev
Projektdokumentation
18
ViewingPlattform - Objekt) von einem Spieler auch dem anderen Spieler
entsprechend dargestellt werden.
Die add – Methode soll am Anfang den Ganzen SzenenGraph „kopieren“ und
sonst immer dann ausgeführt werden, wenn sich ein weiterer Klient verbindet
oder weitere Gegner vom Server hinzugefügt werden.
Das remove wäre genau das Gegenstück von „add“ (zum Entfernen von
Objekten).
Die „move“ – Methode braucht nur eine Aufgabe zu erfüllen:
wer.setTransform (wohin);
Da die TransformGroup – Objekte wahrscheinlich mehr Informationen enthalten,
könnte man bei einem Optimierungsschritt alle Transformgruppen in der Szene
benennen und dann nur diesen Namen übertragen. Das würde die Netzlast
reduzieren.
2.11. Textureierung vom Terrain und Roboter
Das setzen von Texturen oder das Verfärben von Objekten wird in Java3D
mittels Appearance (Erscheinung) - Objekte realisiert. Alle Methoden, die ein
Appearance erzeugen, werden bei der Implementierung in einer Klasse
zusammengefasst (CactusLookAndFeel).
Die Textureierung für alle Shape3D – Objekte unterscheidet sich hauptsächlich
bei der Spezifizierung der Koordinaten.
Vector4f planeS =
new Vector4f(1.0f / textureScaleFactor, 0.0f, 0.0f, 1.0f);
Vector4f planeT =
new Vector4f(0.0f, 0.0f, 1.0f / textureScaleFactor, 1.0f);
Hristo Matev
Projektdokumentation
19
Die “textureScaleFactor” ist eine Variable, die die Texture skaliert. Wenn die
Texture zu klein ist (z.B. “textureScaleFactor” == 1) wird diese bei einem WRAP
BoudaryMode mehrmals gekachelt. Eine Berechnung, die jedes System
herausfordert.
Die Vector4f – Objekten erhalten die x, die y, die z und die w Koordinaten. Diese
Koordinaten sind bei dem homogenen Koordinatensystem, das für die bequemen
Darstellung von Translation und Perspektiven-Projektion eingeführt wurde.
Obwohl die meisten Implementierungen eine w Koordinate von w = 1 benutzen,
ist das nicht unbedingt notwendig. Eine Angabe von z.B. w = 0 gute Ergebnisse
beim Berechnen von Schattenvolumen verwendet. Vertices können auch Werte
w < 0 erhalten. Die Koordinatenberechnung nach der Formel: (x/w, y/w, z/w) in
3D. Ein Dreieck z.B. mit positiven und negativen Vertices wird "external"
genannt, da der Dreieck selbst in der Unendlichkeit eingehüllt wird.
Um ein Shape3D – Objekt zu texturieren muss man zuerst die Texture laden (die
Dimensionen sollen eine Potenz von 2 sein) und dann diese einstellen:
z.B. freischalten und angeben, ob sie gekachelt werden soll, wenn das Bild
kleiner als die Fläche ist (BoundaryMode)
Jede Texture brauch aber unbedingt ihre Texturekoordinaten, die man per Hand
eingeben kann (setTextureCoordinate () – Method bei GeometryArray). Da die
Objekte im Spiel dynamisch geladen werden und jederzeit mit anderen Ersetzt
werden können, ist so eine Eingabe nicht akzeptabel.
Bei dieser Implementierung wird die TexCoordGeneration – Klasse benutzt.
Hiermit kann man diese Koordinaten dynamisch generieren. Man muss lediglich
angeben, mit welcher Formel die Koordinaten berechnet werden sollen.
Z.B OBJECT_LINEAR –Texturekoordinaten werden mit einer linearen Funktion
erstellt:
g = p1xo + p2yo + p3zo + p4wo
Hristo Matev
Projektdokumentation
20
g – der berechnete Wert für die Koordinate
p1, p2, p3, und p4 sind Koeffizienten
xo, yo, zo, und wo sind die Vertexkoordinaten im lokalen Koordinatensystem.
Andere Möglichkeiten sind die EYE_LINEAR (abhängig der Kameraausrichtung)
und die SPHERE_MAP (für die Texturierung von runde Objekten.)
3. Relevante Probleme und Lösungen
Bei der Programmierung ist es immer wieder passiert, dass es Probleme gab, bei
denen man „hängen“ geblieben ist und somit Zeit verloren hat. Leider lässt sich
das nicht vermeiden, was man mit der geringen Einarbeitungszeit zu begründen
ist. Viele Probleme mit Lösungen wurden schon genannt und größten zwei, die
noch nicht genannt wurden, folgen hier:
- Fullscreen Exclusive Mode (FSE-Mode)
- Einlesen von Objekt-Dateien
Um das Spiel in Fullscreen zu starten muss man zuerst zwischen den Java3D –
Versionen unterscheiden. Java3D für OpenGL benutzt eine ganz andere
Möglichkeit für das umschalten als Java3D für Direct3D.
Bei Java3D für Direct3D kann man (und sollte) mit ALT+ENTER zwischen den
beiden Modi umschalten. Die andere Möglichkeit wäre, wenn man eine start-up
Option benutzt:
java -Dj3d.fullscreen=REQUIRED Cactus
Da ich immer noch keine Möglichkeit gefunden habe, wie man prüfen kann, ob
man gerade im Fullscreen-Modus ist, sollte man bei dem Spielen immer mit
ALT+ENTER umschalten.
(Dieses Event kann abgefangen werden und da das Spiel im Fenster-Modus
anfängt, kann man immer sagen, ob der FSE-Mode gerade benutzt wird.)
Eine weitere Problemstelle dabei war das Verhalten der Maus. Um den MausCursor richtig in der Mitte zu setzen musste man zwischen den Mauskoordinaten
Hristo Matev
Projektdokumentation
im Fenster und Mauskoordinaten auf der Zeichnensfläche im Fenster
unterscheiden. Wenn die Fensterkoordinaten benutzt und sich im Fullscreen
befindet, würde das Mausverhalten fehlerhaft sein.
Aus diesen Gründen muss der Benutzer jetzt eine Start-Option angeben, wenn
er im FSE-Mode spielen will. Bei dieser Option muss er auch angeben, ob er
Java3D für Direct3D oder für OpenGL benutzt. Der OpenGL FSE-Mode kann
man mit einigen Zeilen setzen und wird auch beim Start abgegeben.
Das zweite noch nicht angesprochene Problem war das Einlesen von Dateien,
die mit einem 3D-Werkzeug erstellt wurden.
Java3D besitzt einen Loader für „.lws“-Dateien (LightWave Scene), der aber
ganze Szenen (mit Animation) einliest und dann abspielt. Dieser Loader kann
leider nur „.lws“-Dateien, die mit Lightwave3D Version 5.6 erstellt wurden
einlesen. Lightwave 7 kann Dateien im Format der Version 5.6 Exportieren, nur
unterscheiden sich diese auf manchen Stellen und der Loader endet mit einem
Parsing-Error. Lightwave unterstützt aber auch andere Formate (z.B. Wavefont
.obj).
Java3D besitzt auch einen Loader, der „.obj“-Dateien einlesen kann. Leider
brauchen die CollisionAvoidance und TerrainFollowing – Threads einige
Capabilities, die beim Laden der Geometrie gesetzt werden müssen (siehe
Quelltext für mehr Informationen). Diese werden von Java3D-Loader nicht
gesetzt. Deshalb musste man hier einen eigenen Loader implementieren.
Die „.obj“ – Dateien sind ganz einfach strukturiert:
- Zeilen, die mit „v“ beginnen enthalten Vertexkoordinaten
- Zeilen, die mit „f“ beginnen enthalten Flächenkoordinaten, mit einem
Vertexindex.
Um die Aufgabe noch zu vereinfachen erwartet der Loader, der von mir
implementiert wurde, nur Dreiecke und deshalb müssen alle Objekte, die von
Lightwave exportiert werden, vorher die „Triple“-Funktion aufrufen.
Hristo Matev
21
Projektdokumentation
22
Anschließend werden die Capabilities gesetzt und die Collision-Abfragen
ermöglicht (Picking).
4. Fazit
Die Entwicklung von Spiele ist vielleicht der Bereich in der Computergraphik der
sich am meistens in den letzten Jahren entwickelt hat. Die Programmierung ist in
vielen Hinsichten aufwendig und fordert den Programmierer auf. Die
Schnittstellen die am meistens benutzt werden, sind Direct3D von Microsoft und
OpenGL, das vom ARB (Architecture Review Board) entwickelt wurde. Die
Implementierungen waren daher sehr aufwendig und dauerten Jahren. Mit der
Einführung von Java3D erhofften viele eine Programmschnittstelle, die diesen
Aufwand verringern wird und die Kosten senken kann. Das ist auch in vielen
Hinsichten gelungen, nur reichen Grundkenntnisse in Graphik und
Programmierung nicht aus.
Dazu kommt noch der Ruf von Java als eine performancekritische Sprache. Für
kleinere 3D-Projekte bleibt aber Java3D immer noch eine der besten
Programmierschnittstellen.
5. Literatur
Internet:
- http://developer.nvidia.com/object/understanding_w.html
(w Koordinaten)
- http://www.animationartist.com/2000/Tutorials/LWTrees/lwtrees.html
(Baummodellierung)
- http://j3d.org (Theoriequelle)
- http://java.sun.com/docs/books/tutorial/extra/fullscreen/ (fullscreen theorie)
Hristo Matev