3D-Welten mit Python und Panda3D

Aus LinuxUser 11/2006

3D-Welten mit Python und Panda3D

Spielplatz

Es gibt zwar einige freie Spiele-Engines, doch nicht jede ist leicht zu programmieren. Panda3D vereinfacht durch die Skriptsprache Python den Einstieg, bietet aber auch Potenzial für Profis.

Die Erfinder von Micky Maus und Donald Duck hatten schon einige echte Themenparks gebaut, als sie beschlossen, in die virtuelle Welt des Internet einzusteigen. So begannen die Entwickler im Disney-VR-Studio im Jahr 2000 damit, eigene Software zu schreiben, die sie zum Bau ihres 3D-Online-Spiels Toontown verwenden konnten. Heraus kam Panda3D [1], eine Spiele-Engine, die sich mit der Skriptsprache Python verwenden lässt. 2002 stellte Disney das Paket unter eine freie Lizenz, damit Universitäten sich besser an dem Projekt beteiligen konnten.

Eine Game Engine wie Panda3D nimmt Spieleentwicklern einen Großteil der immer wiederkehrenden Aufgaben ab – so das Laden von Spielfiguren und Sounds, grundlegende Bewegungssteuerung und einiges mehr. Die Programmierung dieser Funktionen in der Sprache C++ gewährleistet die nötige Performance für weitgehend ruckelfreie Darstellung. Anwender der Engine machen von dieser Infrastruktur über Python Gebrauch, das weniger umständlich zu benutzen ist als C++. Grundlegende Python-Kenntnisse vermittelt zum Beispiel die entsprechende LinuxUser-Artikelreihe.

Die Installation der Panda-Engine gestaltet sich einfach – solange Sie über eine RPM- oder DPKG-basierte Distribution verfügen. Dafür finden Sie fertige Pakete auf der Heft-CD. Das Debian-Paket von der Homepage ließ sich auch unter dem neuesten Ubuntu installieren. Zum Funktionieren fehlte nur noch ein symbolischer Link von /usr/lib/libssl.so.0.9.8 auf /usr/lib/libssl.so.0.9.7. Andernfalls müssen Sie den Quellcode selbst kompilieren. Das fällt zwar nicht schwer, dauert aber etwas und setzt so manches Entwicklungspaket voraussetzt, zum Beispiel von OpenSSL und LibTIFF. Zum Übersetzen wechseln Sie ins Verzeichnis panda3d-1.2.3 und rufen makepanda/makepanda.py --everything auf. Ohne den letzten Parameter gestartet, gibt Makepanda die verschiedenen Optionen zur Kompilierung aus. Mehr Informationen zum Installieren bietet die Datei doc/INSTALL-MK.

Modell-Export

Prinzipiell setzen sich Spielewelten aus einfachen geometrischen Elementen zusammen, die realistischer erscheinen, wenn auf ihnen so genannte Texturen liegen – also Bilder echter Gegenstände. Möglichst große Realitätsnähe ist nicht immer das Ziel: So realisiert zum Beispiel Toontown eher einen Comic-artigen Charakter (Abbildung 1). Das ändert aber nichts an der Unterscheidung zwischen Geometrie und Oberflächeneigenschaft bei 3D-Modellen.

Abbildung 1: Disneys Online-Spiel Toontown wurde mit der freien Panda3D-Engine realisiert.

Abbildung 1: Disneys Online-Spiel Toontown wurde mit der freien Panda3D-Engine realisiert.

Die Modelle werden üblicherweise in spezialisierten Programmen gezeichnet und dann in ein Format umgewandelt, mit dem die 3D-Engine umgehen kann. Entsprechende Export-Plugins zum Panda3D-Format gibt es für die Windows-Profitools Maya, Softimage XSI und 3DStudio Max. Eine gute Alternative für Heimanwender stellt das Shareware-Programm Milkshape [2] dar, das viele auch zum Editieren von Quake-Modellen verwenden.

Dem Linux-Anwender bieten sich keine Alternativen: Er ist auf das 3D-Program Blender festgelegt. Von Haus aus beherrscht Blender das Panda3D-Format namens Egg nicht. Dafür muss der Anwender eins von drei existiertenden Plugins installieren, die jeweils unterschiedliche Beschränkungen aufweisen. Am weitesten entwickelt ist Chicken [3] vom peruanischen Programmierer Daniel Amthauer. Während dieser Artikel entstand, stellte er die grundlegend überarbeitete und verbesserte Version 1.0a seines Plugins fertig, die nun auch über eine brauchbare Dokumentation verfügt (Abbildung 2).

Abbildung 2: Das Blender-Plugin Chicken exportiert Modelle ins Panda-Format Egg.

Abbildung 2: Das Blender-Plugin Chicken exportiert Modelle ins Panda-Format Egg.

Um Chicken zu installieren, entpacken Sie Zip-Datei im Verzeichnis .blender/scripts. Wenn Sie dann Blender starten, finden Sie den Egg-Export unter FileExportChicken.

Welt laden

Für die ersten Schritte mit der 3D-Engine sollen die mitgelieferten Beispieldateien genügen, die sich bei einer Standardinstallation in /usr/share/panda3d/models befinden. Das einfachste Panda3D-Skript lädt ein Modell und zeigt es an (Listing 1). Das Ergebnis dieses kurzen Skripts ist in Abbildung 3 zu sehen.

Zunächst lädt es das grundlegende Python-Modul direct.directbase.DirectStart. Daraufhin steht ein Objekt loader zur Verfügung, das eine Funktion loadModel anbietet, die das Modell schließlich lädt. Wie man sieht, fehlt beim Aufruf die Dateierweiterung .egg, die Engine findet das Modell auch ohne sie.

Die Lade-Funktion gibt ein Python-Objekt zurück, über das sich das geladene Modell im weiteren Programm ansprechen lässt. So verändert die Methode setPos() seine Position im Raum. Die erste Variable steht für die X-Koordinate, dann folgen Y und Z. Analog dazu verändert zum Beispiel setScale() die Größe des Objekts in den drei Dimensionen. Im Panda-Koordinatensystem zeigt aus Benutzersicht X nach rechts, Z nach oben und Y in den Bildschirm hinein.

Listing 1

import direct.directbase.DirectStart
panda = loader.loadModel("models/panda")
panda.reparentTo(render)
panda.setPos(0,30,-5)
run()

Abbildung 3: Ein fertiges Modell stellt Panda3 mit nur fünf Zeilen Python (Listing 1) dar.

Abbildung 3: Ein fertiges Modell stellt Panda3 mit nur fünf Zeilen Python (Listing 1) dar.

Mit der Maus können Sie das Modell jetzt bereits drehen, verschieben und vergrößern. Probieren Sie dazu alle drei Maustasten aus. Die Rotation erscheint Ihnen vielleicht etwas eigenartig. Das liegt daran, dass der Rotationsmittelpunkt sich außerhalb des Modells befindet.

Szenenbaum

Zeile 4 des Listings 1 wurde bisher nicht erklärt. Sie bezieht sich auf ein grundlegendes Konzept dreidimensionaler Computergrafik: den auf Englisch so genannten Scene Graph. Es handelt sich dabei aus Informatikersicht um einen Graphen, der alle in einer Szene dargestellten Objekte enthält und hierarchisch ordnet. Für unsere Zwecke spielt dieses theoretische Konzept erst mal keine Rolle, die konkrete Vorstellung eines Baums genügt.

Das Modell eines Menschen kann in einem Szenenbaum beispielsweise so abgebildet sein, dass sich an seiner Wurzel der Rumpf befindet, dessen Arme und Beine der Baum als Äste widerspiegelt. Hände und Füße, Finger und Zehen entsprechen wiederum feineren Verästelungen. Ein Vorteil dieses Prinzips besteht darin, dass derart hierarchisch strukturierte Modelle sich in Skripts einfacher bewegen lassen. Verschiebt Python-Code zum Beispiel den Rumpf des beschriebenen Beispiels, bewegen sich die Gliedmaßen des Menschenmodells automatisch mit.

Das selbe gilt für die so genannten Rendering-Attribute, die festlegen, wie die Oberfläche eines Modells erscheint. Von Haus aus erben die in der Hierarchie weiter unter liegenden Elemente von ihren Elternknoten. So genügt es, beim Rumpf die Hautfarbe festzulegen, die Gliedmaßen sehen dann genauso aus. Natürlich können Sie aber auf Wunsch Unterknoten auch anders aussehen lassen.

Damit Panda3D geladene Modelle überhaupt anzeigt, müssen man sie im Szenenbaum unterhalb des so genannten Render-Objekts einhängen. Das übernimmt Zeile 4 von Listing 1. Am Ende des Skripts steht die Anweisung run(), die den so genannten Event-Loop startet: eine unendliche Schleife, in der sich das Programm bewegt und dabei die Anzeige aktualisiert, Tastendrücke verarbeitet und so weiter.

Tasten

Wie eine Szene auf dem Bildschirm erscheint, bestimmt die Kamera. Natürlich handelt es sich auch dabei ein virtuelles Konstrukt, das aber ähnliche Eigenschaften besitzt wie eine echte Kamera. Dazu gehört die Position im Raum, die Neigung um eine oder alle der drei Achsen, die Brennweite der Linse und einiges mehr. Das Beispiel in Listing 2 illustriert, wie man die Kamera bewegt, um den Blick auf das angezeigte Modell zu verändern.

Listing 2

import sys
import direct.directbase.DirectStart
from direct.actor import Actor
from direct.showbase import DirectObject
class Game(DirectObject.DirectObject):
    angle = 0
    distance = 0
  def __init__(self):
    self.panda = loader.loadModel("models/panda")
    self.panda.reparentTo(render)
    self.panda.setPos(0, 1000, -100)
    self.panda.setScale(0.5, 0.5, 0.5)
    self.accept('escape' , sys.exit )
    self.accept('arrow_right', self.spinCamera, [1])
    self.accept('arrow_left', self.spinCamera, [-1])
    self.accept('arrow_down', self.zoomCamera, [1])
    self.accept('arrow_up', self.zoomCamera, [-1])
    base.disableMouse()
    base.camLens.setFar(10000)
  def spinCamera(self, direction):
    self.angle += direction * 1.0
    base.camera.setHpr(self.angle, 0, 0)
  def zoomCamera(self, direction):
    self.distance += direction * 10.0
    base.camera.setPos(0, self.distance, 0)
game = Game()
run()

Das Beispiel demonstriert gleichzeitig, wie man mit Panda3D Tastatureingaben des Benutzers verarbeitet. Weil das am einfachsten geht, wenn das selbstgeschriebene Panda-Programm auf einer Klasse basiert, welche die entsprechenden Tastatur-Methoden anbietet, ist dieses Beispiel im Gegensatz zum vorigen objektorientiert.

Der Einfachheit halber steckt die ganze Funktion in der Klasse Game, die vom Panda-Objekt DirectObject.DirectObject erbt (Zeile 6). Dazu importiert Zeile 4 das passende Modul. Der Code, der vorher einfach nacheinander im Skript stand, wandert nun in den Konstruktor __init__ der Game-Klasse. Erzeugt man durch den Aufruf von Game() ein neues Game-Objekt (Zeile 32), ruft Python selbständig diesen Konstruktur auf.

Nach dem Laden, Positionieren und Skalieren des Modells folgen die Anweisungen, die sich um die Tastatureingaben kümmern. Dazu dient die Methode accept() der DirectObject-Klasse. Weil die Game-Klasse von dieser abgeleitet ist, steht die Methode als Instanzmethode zur Verfügung, die sich über self ansprechen lässt. Als ersten Parameter erwartet sie einen String mit dem Namen der gedrückten Taste. Dann folgt die Funktion, die Panda3D ausführt, wenn der Benutzer die Taste auslöst. So sorgt Zeile 15 dafür, dass das Programm beendet wird, wenn der Benutzer die Escape-Taste drückt.

Kamera

Die folgenden vier Zeilen legen schließlich fest, dass sich beim Drücken der Cursortasten die Kamera bewegt. Die Links- und Rechts-Taste führen die Methode spinCamera() aus, die Oben- und Unten-Tasten zoomCamera(). Bei der Zuweisung dieser Funktionen durch accept() kann eine Liste weiterer Parameter folgen, die Panda der verknüpften Funktion übergibt.

In Listing 2 bekommen die Kamerafunktionen als einzigen Parameter die Richtung der Kamerabewegung übergeben (1 und -1), die Listennotation mit den eckigen Klammern muss aber trotzdem eingehalten werden. Die vorletzte Anweisung der Init-Methode schaltet die automatische Kamerabewegung ab (disableMouse()). Die letzte setzt über das Kameraobjektiv-Objekt base.camLens die Ebene, ab der Panda3D Objekte ausblendet (Clipping Plane), auf den Wert 10000. Über dasselbe Objekt ließe sich beispielsweise auch die Brennweite des Objektivs verändern.

Die Funktionen, die die Kamera bewegen und rotieren, sind recht einfach aufgebaut. Winkel (angle) und Abstand (distance) werden mit einem festen Faktor multipliziert und dann, je nach Richtung, zum aktuellen Wert addiert oder von ihm abgezogen. Den resultierenden Wert weisen die Kamera-Methoden setHpr() respektive setPos() (Position) der Default-Kamera zu, die sich über base.camera auch in eigenen Skripts ansprechen lässt.

Bewegen

Im letzten Beispiel bewegte sich zwar die Kamera rund um den dargestellten Pandabären, das Tier selbst stand aber still. Für etwas mehr Action gibt es zwei Optionen, die sich optimal ergänzen: ein Bewegungsablauf des Modells selbst und seine Bewegung im Raum.

Eigene Bewegungen wie Laufen, Springen etc. legen Sie am besten in einem 3D-Modeller wie Blender fest. Dort designen Sie alle Bewegungsphasen und versehen das Modell dafür mit so genannten Armatures, die man sich wie die Knochen eines Lebewesens vorstellen kann. Ähnlich wie bei Marionetten lassen sich die Modelle dann über einzelne Bewegungspunkte animieren.

Haben Sie solche Sequenzen mit dem Chicken-Plugin exportiert, laden Sie die Bewegungsphasen zusätzlich zum Grundmodell. Panda3d bietet dafür die Klasse Actor, die sich im Wesentlichen verhält wie ein normales Modell, aber eben auch Bewegungsphasen verarbeitet. Listing 3 zeigt einen Ausschnitt aus einem Programm, das davon Gebrauch macht.

Listing 3

  def __init__(self):
    self.panda = Actor.Actor("models/panda-model", {"walk":"models/panda-walk4"})
    …
    self.panda.reparentTo(render)
    …
    self.accept('a', self.animate_start)
    self.accept('s', self.animate_stop)
    …
  def animate_start(self):
    self.panda.loop("walk")
  def animate_stop(self):
    self.panda.stop()

Hinter dem ersten Parameter der Actor-Methode, der das Grundmodell angibt, folgt als zweiter ein Python-Dictionary, dessen Schlüssel die Bewegungsphase spezifizieren (walk), dessen Werte widerum die entsprechenden Modellsequenzen (models/panda-walk4). Danach lässt sich die Animation mit der Methode loop() starten und unendlich lange wiederholen (Zeile 12). Die Funktion stop() beendet die Bewegung wieder. Im Beispiel binden die Zeilen 7 und 8 die Tasten [A] und [S] dazu an die entsprechenden Funktionen.

Die Bewegung im Raum lässt sich durch die bereits vorgestellte Funktion setPos() realisieren, Panda bietet aber noch eine weitaus leistungsfähigere Möglichkeit, so genannten Intervalle. Sie ermöglichen, einfach nur Start- und Endwerte sowie Dauer von Animationen festzulegen (also Position etc.). Die Berechung der Zwischenwerte übernimmt dann Panda3D. Listing 4 demonstriert, wie man ein Intervall verwendet, um das geladene Modell einmal um die eigene Achse zu drehen.

Listing 4

  def rotate(self):
    hprInterval = self.panda.hprInterval(4, Point3(360,0,0), startHpr=Point3(0,0,0))
    hprInterval.start()
  def createmenu(self):
    text = OnscreenText(text = 'a: animate, s: stop, r: rotate, esc: exit', pos
= (-0.8, 0.9), scale = 0.07)

Der erste Parameter der Methode hprInterval legt in Sekunden fest, wie lange die Bewegung dauern soll. Danach folgen erst der Endpunkt und dann der Startpunkt der Bewegung, in diesem Fall der Rotationswinkel von 360 Grad. Zusätzlich zeigt Listing 4 in der Funktion createmenu() wie man mit nur einer Zeile einen kurzen Hilfetext einblendet (Abbildung 4).

Abbildung 4: Der Panda dreht sich und läuft. Eine einzige Codezeile gibt den Hilfetext aus (Listing 4).

Abbildung 4: Der Panda dreht sich und läuft. Eine einzige Codezeile gibt den Hilfetext aus (Listing 4).

Ein anderes Feature, mit dem Panda3D dem Programmierer Arbeit abnimmt, ist der so genannte Task. Beliebige solche Aufgaben führt die Engine aus, wenn man sie beim so globalen Task-Manager-Objekt taskMgr registriert. Dabei kann die Arbeit entweder sofort ausgeführt oder, bei größerem Aufwand, mit der Methode doMethodLater() auf später verschoben werden.

Viele weitere Fähigkeiten der Panda-Engine sollen hier nur kurz Erwähnung finden, denn jede für sich ist schon einigermaßen komplex. So bietet Panda Grundfunktionen für Kollisionserkennung, die verhindert, dass sich 3D-Modelle auf dem Bildschirm unrealistisch überschneiden.

Sogar eine eigene Physik-Engine bringt Panda mit. Sie bildet die Grundlage für das realistische Verhalten von Objekten, verleiht ihnen beispielsweise Masse, die sich entsprechende der (einstellbaren) Schwerkraft verhält. Auch GUI-Funktionen, mit denen sich in Spielen Menüs aufbauen lassen, fehlen nicht. Die heute so populären Shader beherrscht Panda3D ebenfalls, allerdings bisher nur in der Nvidia-Shadersprache Cg und nicht etwa im OpenGL-Standard GLSL.

Der letzte Wermutstropfen für Freunde freier Software ist schließlich die eingebaute Soundengine. Hier setzt Panda3D auf FMOD, das nicht unter einer freien Lizenz steht. Weil sich Panda3D dank Python aber gut mit anderen Frameworks versteht, lässt sich die Soundfunktion zum Beispiel durch das freie Paket Pygame ersetzen.

Fortschritt gesichert

Panda3D macht den Einstieg leicht. Mit wenigen Zeilen Python lassen sich eigene Modelle laden und animieren. Die Dokumentation auf der Website enthält ein einfaches Tutorial und ein Manual, das die wichtigsten Konzepte und die entsprechenden Funktionen erklärt.

Die Referenz von Klassen und Methoden lässt dagegen noch zu wünschen übrig. Weil Panda3D über eine ganze Reihe Benutzer verfügt, bekommt man im Web-Forum aber recht schnell Antworten auf die eigenen Fragen.

Speziell wer kompilierte Sprachen wie C++ scheut, findet in Panda3D eine passende leistungsfähige freie 3D-Engine, an der professionelle wie freie Programmierer fleißig weiter entwickeln.

Infos

[1] Panda3D: http://www.panda3d.org

[2] Milkshape/Windows: http://www.swissquake.ch/chumbalum-soft

[3] Chicken, Egg-Exporter für Blender: http://damthauer.byethost32.com/panda

[4] Pygame: http://www.pygame.org

LinuxUser 11/2006 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben