====== 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.