Synopsis



This tutorial covers details where the official docs and tutorials are leaving, although at the beginner’s level. In the following piece, you will learn how to:

setup the Blender Export script called “chicken“. create your own model using Blender. Use this model in a scene created from scratch. Deal with camera control issues.

Considerable refactoring is necessary to expand upon this code, which is what I’m doing at the moment of writing this tutorial. However, I assume that what follows will be of help to people trying to pick-up on Panda3D quickly.

What you’ll get in the end is pretty simple: a model which moves according to a small set of keyboard controls. In the following tutorial, we’ll add keyboard control of the model.

Assumptions

You have read through the online docs and looked at the beginner tutorials. Disclaimer: This tutorial is written by a newcomer to Panda3D!

Step 1 – Installing Chicken

Creating your own models can be easily done using Blender. “Easily” is of course a relative term since it assumes that you can model with this software. If you don’t, it is worth the few hours of effort. Blender doesn’t have a native Save as… to the egg format, but luckily an export filter is available here. A relevant post is available here.

The file that matters is the Python file (the other aren’t essential). You must put this python file into the script folder of your Blender installation. Under Ubuntu 8.04, this is /usr/share/blender/scripts, or ~/.blender/scripts/ . Under other OS, just look-up where Blender installed itself (I got out of the Windows business and will never get back to it!).

You know that it worked when:

Launch Blender. Select the default cube. Click on File\Export… , there should be a line looking like “Chicken 1.0 (.egg)” Select an output file in the egg format. Click on “Export”. The output file should have 126 lines (about) and begin with “<CoordinateSystem> { Z-up }”.

Step 2 – Making your counter

My project will create a virtual tabletop miniature environment. The basic element is the base of the models. For now, this will be slightly scaled box.

Create a subfolder to the project called models In blender, select the default box and open the default properties. Rescale in Y to 0.5, in Z to 0.2. Offset in Y to -0.5 such that (0,0,0) overlaps with the front polygon of the cube (this will matter later). Export using Chicken into a models/counter.egg

Step 3 – Imports and class definition

Although this is not required, we’ll create a World class which will contain the bulk of the application. Panda3D doesn’t force you into an Object Oriented design, but this is a good habit to get into and by inheriting DirectObject, useful shortcuts gets exposed to our World class.

Import core and initialize:

import direct.directbase.DirectStart

from pandac.PandaModules import *

Additional imports for DirectObject and OnScreenText:

from direct.gui.DirectGui import OnscreenText

from direct.showbase.DirectObject import DirectObject

It is now time to create a class inheriting DirectObject (we need to do this to inherit the accept method required to bind events).

Here is what’s needed to create the World class and get a working application:

class World(DirectObject): pass w = World() run()

Creating an instance of the World class actually doesn’t do anything at the moment: you’d get exactly the same result without. It will make sense to bundle into an object in order to group event handler together, implementing serialization and game logic.

Step 4 – Add a box into this world

Let’s be bold and add a few methods to the World class! For readability, we’ll load the model, setup lights and the camera in three different methods.

class World(DirectObject): self.LoadTerrain() self.LoadLights() self.LoadCamera()

Loading the model can be done in two lines. The only possible difficulty is to make sure that you’ve got the path working fine.

def LoadTerrain(self): self.counter = loader.loadModel('models/counter') self.counter.reparentTo(render) base.setBackgroundColor(0.0,0.3,0.0)

The .egg extension is optional. Also feel free to set the background to any color that you want: just keep in mind that the RGB channels are expressed in the range 0.0 to 1.0. The object referred to by render is the top node in the scene graph. Our counter will thus be directly subordinate to this root.

At this point, executing the code (assuming that you provide skeleton methods for LoadLights() and LoadCamera()), will yield a blank screen. This is because the camera is consistently returning to (0,0,0), looking up the Y axis, thus looking at our polygon through their back faces. Some strange operations must be done to prevent this from happening.

def LoadCamera(self): # Camera base.camera.setPos(4,-10,10) base.camera.lookAt(self.counter) mat=Mat4(camera.getMat()) mat.invertInPlace() base.mouseInterfaceNode.setMat(mat)

This is discussed here and I’m not quite sure why inverting the camera’s material fixes it (nor why there is a mouseInterfaceNode), but the last 3 lines keep the default mouse navigation mode active (good enough for the moment).

You know that this work if at this point: Running the script at this point would yield a completely unshaded (bright white) box and a green background.

Step 5 – Lights

There is not a lot to the lights in this tutorial:

def LoadLight(self): ''' Create an Ambient light as well as a point light ''' plight = AmbientLight('my plight') plight.setColor(VBase4(0.12, 0.12, 0.12, 1)) plnp = render.attachNewNode(plight) render.setLight(plnp) light2 = PointLight('pointlight') plnp2 = render.attachNewNode(light2) plnp2.setPos(2,2,2) render.setLight(plnp2)

If you comment out the PointLight, the model will be lit exclusively by the ambient light an this appear flat and dark.

Epilogue

This is it for Tutorial 1. Not much was done that hasn’t been done in Panda3D’s manual and official tutorials. I struggled for a while with the camera issue; almost gave up. There are some real good people on the official forum who helped, but I thought that another newbie tutorial could be useful to some.

The next step is to implement the handlers to catch keyboard events. Once you’ve tried this once, you will realize how amazingly clean the Panda3D API is. Having done projects in wxPython, Windows MFC, Qt, OpenGL and some other obscure library: working with Panda3D is a real treat.

Stay tuned!

For the records, here is the full code:

#!/usr/bin/python ''' First attempt at Panda3D. cblouin at cs dal ca ''' # Import section import direct.directbase.DirectStart from pandac.PandaModules import * from direct.gui.DirectGui import OnscreenText from direct.showbase.DirectObject import DirectObject # Definitions class World(DirectObject): def __init__(self): self.LoadTerrain() # Lights self.LoadLight() # Camera self.LoadCamera() def LoadTerrain(self): self.counter = loader.loadModel('models/counter.egg') self.counter.reparentTo(render) base.setBackgroundColor(0.0,0.3,0.0) def LoadLight(self): ''' Create an Ambient light as well as a point light ''' plight = AmbientLight('my plight') plight.setColor(VBase4(0.12, 0.12, 0.12, 1)) plnp = render.attachNewNode(plight) render.setLight(plnp) light2 = PointLight('pointlight') plnp2 = render.attachNewNode(light2) plnp2.setPos(2,2,2) render.setLight(plnp2) def LoadCamera(self): # Camera base.camera.setPos(4,-10,10) base.camera.lookAt(self.counter) mat=Mat4(camera.getMat()) mat.invertInPlace() base.mouseInterfaceNode.setMat(mat) # Application code if __name__ == "__main__": w = World() run()