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