KNOW-HOW

3D worlds with Python and Panda3D VIRTUAL PLAYGROUND Photocase.com

Several free game engines are available for users, but programming with them is often less than intuitive. Panda3D is an easy-to-use engine that is accessible enough for newcomers but still powerful enough for the pros at Disney Studios. BY OLIVER FROMMEL

he inventors of Micky Mouse and which is more intuitive and easier to use more realistic when textures – that is, Donald Duck had already set up than ++. images of genuine objects – are applied Ta number of real-life theme parks to them. Realism is not always the goal. by the time they decided to venture into Getting Started For example, Toontown uses comic-style the virtual world of the Internet. In the The Panda engine is easy to install – pro- characters (Figure 1), although this does year 2000, at the Disney vided you have an RPM or DPKG-based not influence the distinction between VR Studios started to create a software distribution. The Debian package from geometry and surface properties in a 3D application to help them develop their the project homepage was also easy with model. 3D online game, Toontown. the latest Ubuntu. All it took to get Models are typically drawn in special- The result of this work is Panda3D [1], Panda running was a symbolic link from ist programs and then converted into a a that supports the Python /usr/lib/libssl.so.0.9.8 to /usr/lib/libssl. format that the 3D engine can handle. scripting language. In 2002, Disney pub- so.0.9.7. In all other cases you will need lished the package under a free license to Panda from the code. Al- Listing 1: A simple to make it easier for universities to con- though this is fairly trivial, it does take Panda3D Script tribute to the project. awhile, and there are a number of de- 01 import direct.directbase. A game engine like Panda3D takes re- pendencies on developer packages to DirectStart petitive work, such as loading characters fulfill – OpenSSL and LibTIFF, for exam- 02 and sounds, basic motion control, and ple. Change directory to panda3d-1.2.3 many other things, off a game develop- for the build, and run makepanda/make- 03 panda = loader. er’s hands. The fact that these functions panda.py --everything. If you leave out loadModel("models/panda") are programmed in C++ guarantees the the last parameter, Makepanda will list 04 panda.reparentTo(render) kind of performance you need for a the various build options. The doc/ 05 panda.setPos(0,30,-5) smooth look and feel. Programmers who INSTALL-MK file has more details. 06 rely on the Panda3D game engine can Virtual worlds basically comprise sim- access its infrastructure via Python, ple, geometric elements that appear 07 run()

52 ISSUE 74 JANUARY 2007 WWW.LINUX - MAGAZINE.COM Panda3D KNOW-HOW

There are export plugins to Panda3D for- mat for the professional Windows tools Maya, Softimage XSI, and 3DStudio Max. A shareware program called Milkshape provides a useful alternative for home users; many people use it to edit Quake models. Linux users do not have any choice here, with the 3D program being your only option. Blender does not sup- port the Panda3D format, also known as Egg, by default. To add support for Egg, you will need to install one of three ex- isting Blender plugins, all of which have limitations. The most mature of these pl- ugins is Chicken [2] by Peruvian devel- oper Daniel Amthauer. While I was writ- ing this article, Daniel released the com- pletely reworked and improved version Figure 1: Disney's online game, Toontown, was implemented using the free Panda3D engine. 1.0a of the plugin, which now includes useful documentation (Figure 2). that the axis of rotation lies outside of the tree inherit the properties of their To install Chicken, unpack the zip ar- the model. parent nodes. For example, you can sim- chive in your .blender/scripts directory. ply define the skin color for the rump Now launch Blender, and you should Scene Tree to apply the same color to the limbs. Of find the Egg export feature below File | I haven’t yet explained Line 4 of Listing course, you can change the appearance Export | Chicken. 1. It relies on a basic concept of three of subordinate nodes however you like. dimensional computer graphics, scene To make sure that Panda3D will Loading a World graphs. From a computer science point display the models you load, you must The sample files will be fine for our first of view, this is a graph that contains and insert them at a position below the ren- steps with the 3D engine; the default hierarchically organizes all the objects der object in the scene tree. And this is install places these files in /usr/share/ displayed in a scene. However, this the- what Line 4 in Listing 1 does. At the end panda3d/models. An extremely simple ory is not important to the following of the script, you can see a run() instruc- Panda3D script loads and displays a discussion; instead, just imagine a scene tion that starts the event loop; this is an model (Listing 1). The results of this tree. infinite loop in which the program runs, simple script are shown in Figure 3. The model of a human being can be updating the display, processing key- The script starts by loading the basic portrayed in a scene tree in such a way presses, and so on. Python module direct.directbase.Direct- that the rump is at the root of the tree, Start. This makes a loader object avail- and the arms and legs are represented as Keys able. The object provides the loadModel branches of the tree. Hands and feet, fin- The camera defines the appearance of a which finally loads the model. As you gers and toes would then be twigs. The scene on screen. Of course, the camera is can see, I left out the .egg extension advantage of this approach is that mod- a virtual object like all the others, how- here, as the engine does not need the els with a hierarchical structure are eas- ever, it possesses similar properties to a extension to find the model. ier to move in scripts. If the Python code real camera. The properties include the The load function returns a Python ob- moves the rump ject which can be used to reference the in our example, loaded model in the course of the pro- the limbs of the gram. The setPos() changes the position human model of the model in 3D space. The first vari- would automati- able represents the X coordinates, fol- cally move with lowed by Y and Z. In a similar fashion, the rump. setScale() scales the object in three di- The same prin- mensions. In the Panda coordinate sys- ciple also applies tem, X points to the right, Z upward, and to rendering attri- Y into the screen, from the user’s point butes, which de- of view. scribe the appear- You can now use your mouse to rotate, ance of the mod- move, and scale the model. Try all three el’s surface. By mouse buttons. Rotation may seem a lit- default, the ele- Figure 2: The Blender plugin Chicken exports models to the Panda tle strange at first. The reason for this is ments lower down Egg format.

WWW.LINUX - MAGAZINE.COM ISSUE 74 JANUARY 2007 53 KNOW-HOW Panda3D

Listing 2: Moving the camera position in the scene, the angle class is derived from this, the method is on one or all three of the axes, the focal an instance method that can be ad- Camera distance, and many other things. The ex- dressed via the self keyword. The first 01 import sys ample in Listing 2 shows how to move parameter it expects is a string with the 02 import direct.directbase. the camera to change the view of the name of the key that was pressed. This DirectStart model currently on display. is followed by the function that we want At the same time, the example demon- Panda3D to execute when the user 03 from direct.actor import Actor strates how Panda3D processes user presses the key. Thus, Line 15 quits the 04 from direct.showbase import keypresses. As this is simpler to handle program when the user presses the DirectObject if your Panda program is based on a Escape key. 05 class that provides appropriate keyboard 06 class Game(DirectObject. methods, this example is object-oriented, Camera DirectObject): in contrast to the previous one. The next four lines map camera move- 07 angle = 0 To keep things simple, the whole func- ments to the cursor keys. The left and 08 distance = 0 tion resides in the Game class, which in- right arrow keys run the spinCamera() 09 def __init__(self): herits from the Panda DirectObject.Direc- method; the up and down arrows call tObject object (Line 6). To allow this to zoomCamera(). When this function is 10 self.panda = loader. happen, Line 4 imports the required passed in by accept(), a list of additional loadModel("models/panda") module. The code, which was previously parameters can be added for Panda to 11 self.panda. listed top down in the script, now moves pass to the function in question. reparentTo(render) to the Game class __init__ constructor. In Listing 2, the only parameter passed 12 self.panda.setPos(0, 1000, Now, when a new Game object is to the camera functions is the camera -100) created by a call to Game() in Line 32, motion direction (1 and -1), however, we 13 self.panda.setScale(0.5, Python will automatically call the still need list notation with square brack- 0.5, 0.5) constructor. ets. The disableMouse() with the Init 14 After loading, positioning and scaling method switches automatic camera mo- 15 self.accept('escape' , the model, we need some instructions to tion off. The last instruction uses the sys.exit ) handle keyboard input. The accept() camera lens object, base.camLens, to set 16 self.accept('arrow_right', method provided by the DirectObject the clipping plane (the level at which self.spinCamera, [1]) class takes care of this. As the Game Panda3D will hide objects) to a value of 17 self.accept('arrow_left', 10,000. You could just as easily use the self.spinCamera, [-1]) Listing 3: Working with camera lens object to modify the focus, for example. 18 self.accept('arrow_down', Motion Phases self.zoomCamera, [1]) The functions for moving and rotating 01 def __init__(self): the camera are easily mastered. The 19 self.accept('arrow_up', 02 self.panda = Actor. angle and distance are multiplied by a self.zoomCamera, [-1]) Actor("models/panda-model", fixed factor, and then added to, or sub- 20 {"walk":"models/ tracted from, the current value, depend- 21 base.disableMouse() panda-walk4"}) ing on the direction. The camera meth- 22 base.camLens.setFar(10000) 03 ... ods setHpr() and setPos() assign the 23 resulting value to the default camera, 04 self.panda. which you can reference as base.camera 24 def spinCamera(self, reparentTo(render) direction): in your own scripts. 05 ... 25 self.angle += direction * Motion 1.0 06 In our last example, we moved the cam- 26 base.camera.setHpr(self. 07 self.accept('a', self. era round the panda bear, but the bear angle, 0, 0) animate_start) itself was stationary. Two options that 27 08 self.accept('s', self. compliment each other perfectly will add 28 def zoomCamera(self, animate_stop) more action: moving the model itself, direction): 09 ... and moving the model through the 29 self.distance += direction 10 scene. * 10.0 Some motions, such as walking, 11 def animate_start(self): 30 base.camera.setPos(0, jumping, etc., are best created in a 3D self.distance, 0) 12 self.panda.loop("walk") modeller like Blender. You should use 31 13 Blender to design the phases of the 32 game = Game() 14 def animate_stop(self): movement, and assign armatures to the model to represent the bones of a living 33 run() 15 self.panda.stop() being. After doing so, you can animate

54 ISSUE 74 JANUARY 2007 WWW.LINUX - MAGAZINE.COM Panda3D KNOW-HOW

Figure 3: Panda3 displaying a finished model with just five lines of Figure 4: Rotating and moving the panda. A single line of code out- Python (Listing 1). puts the help text (Listing 4). the model via individual motion points 360 degrees. Additionally, Listing 4 in shader, a feature that has become really just like a puppet. the createmenu() function demonstrates popular, although currently, it only sup- After exporting your sequences via the how to display help text with a single ports the Nvidia Cg shader language, Chicken plugin, load the motion phases line of code (Figure 4). rather than the OpenGL standard GLSL. to enhance the basic model. Panda3d Tasks are another Panda3D feature The final dampener for fans of free provides the Actor class for this purpose; that save programmers work. The engine software is the internal sound engine. the class reacts more or less like a nor- will execute any tasks you register with Panda3D relies on FMOD, which is not mal model, but it additionally processes the global task manager object, taskMgr. available under a free license. On a motion phases. Listing 3 shows an ex- The task can either be performed imme- brighter note, as Panda3D works well cerpt from a program that draws on this diately or postponed to a later time using with other frameworks; for instance, you ability. the doMethodLater() method. can replace the sound function with the The first parameter of the Actor I will just mention a couple of other free package. method, which specifies the basic features the Panda engine has; all of model, is followed by a Python diction- them are fairly complex. For example, Progress Guaranteed ary whose keys define the motion phase Panda has basic collision detection func- If you prefer not to use compiled lan- (walk), and whose values correspond to tionality to prevent unrealistic overlap- guages such as C++, you can build 3D the model sequences (models/ ping of 3D models on screen. models using the very useful and power- panda-walk4). You can then start the Panda even has its own physics en- ful Panda3D engine. Panda3D takes the animation with the loop() method and gine. The engine forms the basis for real- pain out of your first steps into 3D pro- repeat infinitely (Line 12). The stop() istic object behavior by assigning a mass gramming. Just a few lines of Python function stops the motion: in our exam- to the object. GUI functions that give will let you load and animate models. ple, Lines 7 and 8 map the [A] and [S] programmers the ability to create menus The documentation on the website in- keys to the functions. are also available. And Panda3D has a cludes a simple tutorial and a manual Motion in a scene can be implemented that explains the most important con- using the setPos() function referred to Listing 4: Working with cepts and functions. earlier, however, Panda has a far more Intervals In contrast, the class and method ref- powerful option: intervals. Intervals give 01 def rotate(self): erence leaves much to be desired. Since programmers the ability to simply spec- Panda3D has a large user community, 02 hprInterval = self.panda. ify start and end values and the duration sensible questions posted in the web hprInterval(4, of the animations (that is positions, forum are typically answered quickly. ■ Point3(360,0,0), etc.). Panda3D automatically handles the startHpr=Point3(0,0,0)) intermediate values. Listing 4 demon- INFO strates how you can use an interval to 03 hprInterval.start() [1] Panda3D: rotate the loaded model about its own 04 http:// www. panda3d. org axis. 05 def createmenu(self): [2] Chicken, Egg exporter for Blender: The first parameter in the hprInterval 06 text = OnscreenText(text = http:// damthauer. byethost32. com/ method specifies the duration of the mo- 'a: animate, s: stop, r: panda tion in seconds. This is followed by the rotate, esc: exit', pos [3] Pygame: end point and the starting point of the http:// www. pygame. org move; in this case, the rotation angle of 07 = (-0.8, 0.9), scale = 0.07)

WWW.LINUX - MAGAZINE.COM ISSUE 74 JANUARY 2007 55