Table of Contents

Lab 2 - Making Scenes in Panda 3D

Before Coming to Class

In-Class Excercises

Before You Start

Making a Virtual Room in Panda 3D

  1. 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()
  2. 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) 
  3. 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]))
  4. 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 1×1 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"
  5. 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:

    # 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" 
  6. 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)
  7. 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.