====== Lab 2 - Making Scenes in Panda 3D ======
===== Before Coming to Class =====
* Make sure you can run the Panda 3D sample programs on your machine
===== Useful Links =====
* [[http://www.ru.is/kennarar/hannes/useful/PQR2.3.html|Python Quick Reference (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/Python-Docs-2.5|Complete Python Documentation (LOCAL)]] //This is the latest 2.5 release, but new 2.5 features are well marked//
* [[http://www.ru.is/kennarar/hannes/useful/PandaManual|Panda 3D Manual (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/PandaReference|Panda 3D Reference(LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/BlenderManual/htmlI|Blender Manual (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/BlenderManual/htmlII/p2.html|Blender Reference (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/share/chicken_export1.0.zip|Chicken 1.0 EGG Exporter for Blender 3D (LOCAL)]]
===== In-Class Excercises =====
==== Before You Start ====
* Download and unzip the [[http://www.ru.is/kennarar/hannes/classes/ve2007/Lab2Assets.zip|Lab 2 Asset File]] into your working directory
==== Making a Virtual Room in Panda 3D ====
{{public:t-vien-07-1:ve-lab2-screenshot.jpg|}}
- Create a program called myscene.py that moves the camera to the location (-0.5, -3.5, 0.5), changes its heading by -45 degrees and then loads the model "Models/television.egg", placing it at location (2,-2,0). Scale the model down to 15% and rotate it such that you can see the front of the television screen.
# HINT - A basic program showing a scene with one object from a fixed camera
# NOTE - Numeric values are arbitrary in this example
import direct.directbase.DirectStart
# Disabling user controlled camera
base.disableMouse()
# Placing the camera in world space
base.camera.setPos(1,1,1) # Location (x, y, z)
base.camera.setHpr(45,45,45) # Orientation in degrees (Heading, Pitch, Roll)
# Loading a new ready-made model
model = loader.loadModel("MyModel.egg") # Model filename
model.setScale(0.5) # Uniform scaling of the model
model.setPos(1,1,1) # Location (x, y, z)
model.setHpr(45,45,45) # Orientation in degrees (Heading, Pitch, Roll)
# Adding the model to the Scene Graph under the root
model.reparentTo(render)
# Start main loop
run()
- Add a moving image to the television screen by first finding the nodepath object representing the 'screen' sub-geometry of the television model and then giving it (and only it) the texture "Models/Textures/shuttle.avi".
# HINT - Finding a nodepath to a named sub-node
nodepath = model.find("**/blah")
# HINT - Loading a new texture
mytexture = loader.loadTexture("MyTexture.png")
# HINT - Setting a texture property
model.setTexture(mytexture, 1)
- Create a new class called **World** (see below) and move your television code (but not your camera) into its constructor and then instantiate this world before calling run() in your main program. The class below contains code for moving the camera backwards as you hold down the 's' key. Add code for moving the camera forward with the 'w' key and for rotating the camera left and right with the 'a' and 'd' keys.
from direct.showbase.DirectObject import DirectObject
class World(DirectObject):
def __init__(self):
# Set up movement keys
self.keystate = {'back':0} # Initial state of keys
self.accept('s', self.set_keystate, ['back',1]) # Handler for 's' press
self.accept('s-up', self.set_keystate, ['back',0]) # Handler for 's' release
self.lasttime = 0
taskMgr.add(self.move, "move") # Start another independent update loop
def set_keystate(self, key, state):
""" Set a stored state (0,1) for a specified key ('back',...)"""
self.keystate[key] = state
def move(self, task):
"""
Update camera position and/or rotation for all active key states.
"""
elapsed = task.time - self.lasttime
if self.keystate['back'] is 1:
base.camera.setPos(camera,0,-0.7*elapsed,0)
self.lasttime = task.time
return task.cont
# HINT - This might help you set up the keys without having to repeat the
# 'accept' command 8 times!
key_map = (('s',['back',1]),('s-up',['back',0]),
('w',['forward',1]),('w-up',['forward',0]),
('a',['left',1]),('a-up',['left',0]),
('d',['right',1]),('d-up',['right',0]))
- Create a new class called **Room** (see below). The constructor takes in three data sets that define the shape of the room. The data sets describe the placement of 1x1 panels for walls, floor and ceiling. All sets include x and y location, but the third (if there is one) value depends on the type of panel. To create a tiny room with a light in the ceiling, you could instantiate the Room like this:
# Building a room with the Room class
room_pathnode = render.attachNewNode("Room") # Create root node for the Room
room = Room(room_pathnode,
[(0,0,0),(1,0,-90),(1,-1,-180),(0,-1,-270)], # Walls
[(0,-1)], # Floor
[(0,0,True)]) # Ceiling with light
class Room:
def __init__(self, parent_pathnode, wall_data, floor_data, ceiling_data):
"""
Generate a room with walls, floor and a ceiling from lists of 1x1 panel
locations and orientations.
'parent_pathnode' = Pathnode immediately above this one in the scene graph
'wall_data' = List of wall panel touples: (x loc, y loc, angle)
'floor_data' = List of floor panel touples: (x loc, y loc)
'ceiling_data' = List of ceiling panel touples: (x loc, y loc, has light?)
"""
self.parent = parent_pathnode
self.wall_data = wall_data
self.floor_data = floor_data
self.ceiling_data = ceiling_data
self.create_floor()
self.create_ceiling()
self.create_walls()
self.create_lights()
def create_walls(self):
""" Create the walls according to Room data, with panel textures """
wall_clear = loader.loadTexture("Models/Textures/wall_tanned_clear.png")
cm = CardMaker('wall panel') # Generates a single sided 1x1 polygon
for wallpanel_data in self.wall_data:
wallpanel = self.parent.attachNewNode(cm.generate())
wallpanel.setPos(wallpanel_data[0],wallpanel_data[1],0)
wallpanel.setHpr(wallpanel_data[2],0,0)
wallpanel.setTexture(wall_clear)
def create_floor(self):
""" Create the floor according to Room data """
def create_ceiling(self):
""" Create the ceiling, with ceiling light textures, according to Room data """
def create_lights(self):
""" Create both the ambient light and spotlights for every ceiling light """
Fill in the code for **create_floor** and **create_ceiling** (see additional assets below) and test with the data set for the tiny room above. You should instantiate the Room from within the constructor of the **World** class.
# USEFUL ASSETS:
# "Models/Textures/wall_tanned_clear.png"
# "Models/Textures/floor_concrete.png"
# "Models/Textures/ceiling_tanned.png"
# "Models/Textures/ceiling_tanned_light.png"
- Modify the **create_wall** method to pick a random texture for each panel for a greater variety (use the assets below) and finally pass in data into the Room's constructor for building a room with the following shape: \\ {{public:t-vien-07-1:scene_gridview.jpg|}} \\
# USEFUL ASSETS:
# "Models/Textures/wall_tanned_clear.png"
# "Models/Textures/wall_tanned_door.png"
# "Models/Textures/wall_tanned_window.png"
# "Models/Textures/wall_tanned_vent.png"
- Fill in the code for the **create_lights** method. Create one ambient light with the color (0.4,0.4,0.4,1). Create one spotlight with the color (0.6,0.6,1.0,1) and attenuation (0,0,0), facing down from the ceiling (starting at height 1.25).
# HINT - Creating lights
# Set up the global lighting for general illumination
ambient_source = AmbientLight('ambient')
ambient_source.setColor(Vec4(1,1,1,1))
ambient = root.attachNewNode(ambient_source.upcastToPandaNode())
root.setLight(ambient)
# Set up a spotlight for localized illumination
lens = PerspectiveLens()
lens.setNearFar(0.1, 2.0)
spot_source = Spotlight('spotlight')
spot_source.setColor(Vec4(1,1,1,1))
spot_source.setAttenuation(Vec3(0,0,0))
## spot_source.showFrustum() # Uncomment for great debug information
spot_source.setLens(lens)
spot = root.attachNewNode(spot_source.upcastToLensNode())
spot.setPos(Vec3(1,1,1))
spot.setHpr(0,0,0)
root.setLight(spot)
- Modify the spotlight creation in the **create_lights** methods so that it creats a spotlight for each of the ceiling panels that have light fixtures in them.