class TestApplication:

  def __init__(self):

    self.setupPhysics()
    self.clock_ = ClockObject()


  def sim(self):
    
    while True:
        try:
          
          time.sleep(LOOP_DELAY)
          
          self.clock_.tick()
          self.physics_world_.doPhysics(self.clock_.getDt(), 5, 1.0/180.0)

          # printing location of first box
          print "Box 0: %s"%(str(self.boxes_[0].getPos()))

        except KeyboardInterrupt:
            print "Simulation finished"
            sys.exit()

  def setupPhysics(self):

    # setting up physics world and parent node path 
    self.physics_world_ = BulletWorld()
    self.world_node_ = NodePath()
    self.physics_world_.setGravity(Vec3(0, 0, -9.81))

    # setting up ground
    self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground'))
    self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0))
    self.ground_.setPos(0,0,0)
    self.ground_.setCollideMask(BitMask32.allOn())
    self.physics_world_.attachRigidBody(self.ground_.node())

    self.boxes_ = []
    num_boxes = 20
    side_length = 0.2
    size = Vec3(side_length,side_length,side_length)
    start_pos = Vec3(-num_boxes*side_length,0,10)
    for i in range(0,20):
      self.addBox("name %i"%(i),size,start_pos + Vec3(i*2*side_length,0,0))

  def addBox(self,name,size,pos):

    # Box (dynamic)
    box = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    box.node().setMass(1.0)
    box.node().addShape(BulletBoxShape(size))
    box.setPos(pos)
    box.setCollideMask(BitMask32.allOn())

    self.physics_world_.attachRigidBody(box.node())
    self.boxes_.append(box)
示例#2
0
class Game(ShowBase):
	def __init__(self):
		ShowBase.__init__(self)
		self.setBackgroundColor(0.2,0.2,0.2)
		self.accept("escape", self.taskMgr.stop)
		#self.accept("mouse1", self.onClick)
		#self.accept("mouse2", self.onClick2)
		self.globalClock = ClockObject()
		
		self.addLight()
		
		self.liner = LineDrawer(self)
		
		self.taskMgr.add(self.update, "update")
		
	def update(self, task):
		self.globalClock.tick()
		t = self.globalClock.getFrameTime()
		#print t
		dt = self.globalClock.getDt()
		
		return task.cont
	
	def addLight(self):
		self.render.clearLight()
		self.lightCenter = self.render.attachNewNode(PandaNode("center"))
		#self.lightCenter.setCompass()
		
		# ambient light
		self.ambientLight = AmbientLight('ambientLight')
		self.ambientLight.setColor(Vec4(0.5,0.5,0.5, 1))
		self.alight = self.lightCenter.attachNewNode(self.ambientLight)
		self.render.setLight(self.alight)
		
		# point light
		self.pointlight = PointLight("pLight")
		self.light = self.lightCenter.attachNewNode(self.pointlight)
		self.pointlight.setColor(Vec4(0.8,0.8,0.8,1))
		self.light.setPos(0,0,2)
		self.render.setLight(self.light)
		
		# directional light
		self.dirlight = DirectionalLight("dLight")
		self.dlight = self.lightCenter.attachNewNode(self.dirlight)
		self.dirlight.setColor(Vec4(0.8,0.8,0.8,1))
		self.dirlight.setShadowCaster(True)
		
		self.dlight.setPos(0,0,5)
		self.dlight.lookAt(5,10,0)
		self.render.setLight(self.dlight)
		
		self.render.setShaderAuto()
示例#3
0
class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.setBackgroundColor(0.2, 0.2, 0.2)
        self.accept("escape", self.taskMgr.stop)
        #self.accept("mouse1", self.onClick)
        #self.accept("mouse2", self.onClick2)
        self.globalClock = ClockObject()

        self.addLight()

        self.liner = LineDrawer(self)

        self.taskMgr.add(self.update, "update")

    def update(self, task):
        self.globalClock.tick()
        t = self.globalClock.getFrameTime()
        #print t
        dt = self.globalClock.getDt()

        return task.cont

    def addLight(self):
        self.render.clearLight()
        self.lightCenter = self.render.attachNewNode(PandaNode("center"))
        #self.lightCenter.setCompass()

        # ambient light
        self.ambientLight = AmbientLight('ambientLight')
        self.ambientLight.setColor(Vec4(0.5, 0.5, 0.5, 1))
        self.alight = self.lightCenter.attachNewNode(self.ambientLight)
        self.render.setLight(self.alight)

        # point light
        self.pointlight = PointLight("pLight")
        self.light = self.lightCenter.attachNewNode(self.pointlight)
        self.pointlight.setColor(Vec4(0.8, 0.8, 0.8, 1))
        self.light.setPos(0, 0, 2)
        self.render.setLight(self.light)

        # directional light
        self.dirlight = DirectionalLight("dLight")
        self.dlight = self.lightCenter.attachNewNode(self.dirlight)
        self.dirlight.setColor(Vec4(0.8, 0.8, 0.8, 1))
        self.dirlight.setShadowCaster(True)

        self.dlight.setPos(0, 0, 5)
        self.dlight.lookAt(5, 10, 0)
        self.render.setLight(self.dlight)

        self.render.setShaderAuto()
class TestApplication(ShowBase):

  def __init__(self):

    ShowBase.__init__(self)
    
    # game objects   
    self.controlled_obj_index_ = 0
    self.level_sectors_ = []
    self.controlled_objects_ = []
    
    # active objects
    self.active_sector_ = None

    self.setupRendering()
    self.setupControls()
    self.setupPhysics()
    self.clock_ = ClockObject()
    self.kinematic_mode_ = False
    
    # Task
    logging.info("TestSectors demo started")
    taskMgr.add(self.update, 'updateWorld')


  def setupRendering(self):

    self.setBackgroundColor(0.1, 0.1, 0.8, 1)
    self.setFrameRateMeter(True)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = self.render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = self.render.attachNewNode(dlight)

    self.render.clearLight()
    self.render.setLight(alightNP)
    self.render.setLight(dlightNP)
    
    self.setupBackgroundImage()
    
  def setupBackgroundImage(self):
    
    
    image_file = Filename(rospack.RosPack().get_path(BACKGROUND_IMAGE_PACKAGE) + '/resources/backgrounds/' + BACKGROUND_IMAGE_PATH)
    
    # check if image can be loaded
    img_head = PNMImageHeader()
    if not img_head.readHeader(image_file ):
        raise IOError, "PNMImageHeader could not read file %s. Try using absolute filepaths"%(image_file.c_str())
        sys.exit()
        
    # Load the image with a PNMImage
    w = img_head.getXSize()
    h = img_head.getYSize()
    img = PNMImage(w,h)
    #img.alphaFill(0)
    img.read(image_file) 
    
    texture = Texture()        
    texture.setXSize(w)
    texture.setYSize(h)
    texture.setZSize(1)    
    texture.load(img)
    texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image
    texture.setWrapV(Texture.WM_border_color)
    texture.setBorderColor(LColor(0,0,0,0))
    
    # creating CardMaker to hold the texture
    cm = CardMaker('background')
    cm.setFrame(-0.5*w,0.5*w,-0.5*h,0.5*h)  # This configuration places the image's topleft corner at the origin (left, right, bottom, top)
    background_np = NodePath(cm.generate())            
    background_np.setTexture(texture)
    
    background_np.reparentTo(self.render)
    background_np.setPos(BACKGROUND_POSITION)
    background_np.setScale(BACKGROUND_IMAGE_SCALE)
  

  def setupControls(self):

    # Input (Events)
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)
    self.accept('n', self.selectNextControlledObject)
    self.accept('k', self.toggleKinematicMode)

    # Inputs (Polling)
    self.input_state_ = InputState()
    self.input_state_.watchWithModifiers("right","arrow_right")
    self.input_state_.watchWithModifiers('left', 'arrow_left')
    self.input_state_.watchWithModifiers('jump', 'arrow_up')
    self.input_state_.watchWithModifiers('stop', 'arrow_down')
    self.input_state_.watchWithModifiers('roll_right', 'c')
    self.input_state_.watchWithModifiers('roll_left', 'z')
    self.input_state_.watchWithModifiers('roll_stop', 'x')


    self.title = addTitle("Panda3D: Kinematic Objects")
    self.inst1 = addInstructions(0.06, "ESC: Quit")
    self.inst2 = addInstructions(0.12, "Up/Down: Jump/Stop")
    self.inst3 = addInstructions(0.18, "Left/Right: Move Left / Move Rigth")
    self.inst4 = addInstructions(0.24, "z/x/c : Rotate Left/ Rotate Stop / Rotate Right")
    self.inst5 = addInstructions(0.30, "n: Select Next Character")
    self.inst6 = addInstructions(0.36, "k: Toggle Kinematic Mode")
    self.inst7 = addInstructions(0.42, "f1: Toggle Wireframe")
    self.inst8 = addInstructions(0.48, "f2: Toggle Texture")
    self.inst9 = addInstructions(0.54, "f3: Toggle BulletDebug")
    self.inst10 = addInstructions(0.60, "f5: Capture Screenshot")
    
  def processCollisions(self):
    
    contact_manifolds = self.physics_world_.getManifolds()
    for cm in contact_manifolds:      
      node0 = cm.getNode0()
      node1 = cm.getNode1()
      col_mask1 = node0.getIntoCollideMask()
      col_mask2 = node1.getIntoCollideMask()
      
      
      if (col_mask1 == col_mask2) and \
        (col_mask1 == SECTOR_ENTERED_BITMASK) and \
        self.active_sector_.getSectorPlaneNode().getName() != node0.getName():
        
        logging.info('Collision between %s and %s'%(node0.getName(),node1.getName()))
        sector = [s for s in self.level_sectors_ if s.getSectorPlaneNode().getName() == node0.getName()]
        sector = [s for s in self.level_sectors_ if s.getSectorPlaneNode().getName() == node1.getName()] + sector
        
        self.switchToSector(sector[0])
        logging.info("Sector %s entered"%(self.active_sector_.getName()))
        
  def switchToSector(self,sector):       
    
    obj_index = self.controlled_obj_index_
    character_obj = self.controlled_objects_[obj_index]
    
    if self.active_sector_ is not None:
      self.active_sector_.releaseCharacter()
    
    character_obj.setY(sector,0)
    character_obj.setH(sector,0)
    self.active_sector_ = sector
    
    self.active_sector_.constrainCharacter(character_obj)
    
    self.active_sector_.enableDetection(False)
    sectors = [s for s in self.level_sectors_ if s != self.active_sector_]
    for s in sectors:
      s.enableDetection(True)

  def setupPhysics(self):

    # setting up physics world and parent node path 
    self.physics_world_ = BulletWorld()
    self.world_node_ = self.render.attachNewNode('world')
    self.cam.reparentTo(self.world_node_)
    self.physics_world_.setGravity(Vec3(0, 0, -9.81))

    self.debug_node_ = self.world_node_.attachNewNode(BulletDebugNode('Debug'))
    self.debug_node_.node().showWireframe(True)
    self.debug_node_.node().showConstraints(True)
    self.debug_node_.node().showBoundingBoxes(True)
    self.debug_node_.node().showNormals(True)
    self.physics_world_.setDebugNode(self.debug_node_.node())
    self.debug_node_.hide()
    
    # setting up collision groups
    self.physics_world_.setGroupCollisionFlag(GAME_OBJECT_BITMASK.getLowestOnBit(),GAME_OBJECT_BITMASK.getLowestOnBit(),True)
    self.physics_world_.setGroupCollisionFlag(SECTOR_ENTERED_BITMASK.getLowestOnBit(),SECTOR_ENTERED_BITMASK.getLowestOnBit(),True)
    self.physics_world_.setGroupCollisionFlag(GAME_OBJECT_BITMASK.getLowestOnBit(),SECTOR_ENTERED_BITMASK.getLowestOnBit(),False)
    

    # setting up ground
    self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground'))
    self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0))
    self.ground_.setPos(0,0,0)
    self.ground_.setCollideMask(GAME_OBJECT_BITMASK)
    self.physics_world_.attachRigidBody(self.ground_.node())
    
    # creating level objects    
    self.setupLevelSectors()

    # creating controlled objects
    diameter = 0.4
    sphere_visual = loader.loadModel('models/ball.egg')

    bounds = sphere_visual.getTightBounds() # start of model scaling
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = diameter/max([extents.getX(),extents.getY(),extents.getZ()])
    sphere_visual.clearModelNodes()
    sphere_visual.setScale(scale_factor,scale_factor,scale_factor) # end of model scaling

    sphere_visual.setTexture(loader.loadTexture('models/bowl.jpg'),1)
    sphere = NodePath(BulletRigidBodyNode('Sphere'))
    sphere.node().addShape(BulletSphereShape(0.5*diameter))
    sphere.node().setMass(1.0)
    #sphere.node().setLinearFactor((1,0,1))
    #sphere.node().setAngularFactor((0,1,0))
    sphere.setCollideMask(GAME_OBJECT_BITMASK)
    sphere_visual.instanceTo(sphere)
    
    self.physics_world_.attachRigidBody(sphere.node())
    self.controlled_objects_.append(sphere) 
    sphere.reparentTo(self.world_node_)
    sphere.setPos(self.level_sectors_[0],Vec3(0,0,diameter+10))
    sphere.setHpr(self.level_sectors_[0],Vec3(0,0,0))
    
    # creating bullet ghost for detecting entry into other sectors
    player_ghost = sphere.attachNewNode(BulletGhostNode('player-ghost-node'))
    ghost_box_shape = BulletSphereShape(PLAYER_GHOST_DIAMETER/2)
    ghost_box_shape.setMargin(SECTOR_COLLISION_MARGIN)
    ghost_sphere_shape = BulletSphereShape(PLAYER_GHOST_DIAMETER*0.5)
    ghost_sphere_shape.setMargin(SECTOR_COLLISION_MARGIN)
    player_ghost.node().addShape(ghost_box_shape)
    player_ghost.setPos(sphere,Vec3(0,0,0))
    player_ghost.node().setIntoCollideMask(SECTOR_ENTERED_BITMASK)
    self.physics_world_.attach(player_ghost.node())

    # creating mobile box
    size = Vec3(0.2,0.2,0.4)
    mbox_visual = loader.loadModel('models/box.egg')
    bounds = mbox_visual.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()])
    mbox_visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor)

    mbox = NodePath(BulletRigidBodyNode('MobileBox'))
    mbox.node().addShape(BulletBoxShape(size/2))
    mbox.node().setMass(1.0)
    #mbox.node().setLinearFactor((1,0,1))
    #mbox.node().setAngularFactor((0,1,0))
    mbox.setCollideMask(GAME_OBJECT_BITMASK)
    mbox_visual.instanceTo(mbox)    
    self.physics_world_.attachRigidBody(mbox.node())
    self.controlled_objects_.append(mbox)
    mbox.reparentTo(self.level_sectors_[0])    
    mbox.setPos(Vec3(1,0,size.getZ()+1))
    
    # switching to sector 1
    self.switchToSector(self.level_sectors_[0])


  def setupLevelSectors(self):
    
    start_pos = Vec3(0,-20,0)
    
    for i in range(0,4):
      
      sector = Sector('sector' + str(i),self.physics_world_)
      sector.reparentTo(self.world_node_)
      
      #sector.setHpr(Vec3(60*(i+1),0,0))
      sector.setHpr(Vec3(90*i,0,0))
      sector.setPos(sector,Vec3(0,-20,i*4))
      self.level_sectors_.append(sector)
      sector.setup()      

  def update(self, task):

    self.clock_.tick()
    dt = self.clock_.getDt()
    self.processInput(dt)    
    self.physics_world_.doPhysics(dt, 5, 1.0/180.0)
    self.processCollisions()
    self.updateCamera()

    return task.cont 

  def processInput(self,dt):
  
    activate = False
    obj = self.controlled_objects_[self.controlled_obj_index_]

    if self.kinematic_mode_:
      obj.node().setKinematic(True)
      return
    else:
      obj.node().setKinematic(False)

    vel = self.active_sector_.getRelativeVector(self.world_node_,obj.node().getLinearVelocity())
    w = self.active_sector_.getRelativeVector(self.world_node_, obj.node().getAngularVelocity())
    vel.setY(0)
    #w.setX(0)
    #w.setZ(0)

  
    if self.input_state_.isSet('right'): 
      vel.setX(2)
      activate = True

    if self.input_state_.isSet('left'): 
      vel.setX(-2)
      activate = True

    if self.input_state_.isSet('jump'): 
      vel.setZ(4)
      activate = True

    if self.input_state_.isSet('stop'): 
      vel.setX(0)

    if self.input_state_.isSet('roll_right'):
      w.setY(ROTATIONAl_SPEED)
      activate = True

    if self.input_state_.isSet('roll_left'):
      w.setY(-ROTATIONAl_SPEED)
      activate = True

    if self.input_state_.isSet('roll_stop'):
      w.setY(0)

    if activate : obj.node().setActive(True,True)
    vel = self.world_node_.getRelativeVector(self.active_sector_,vel)
    w = self.world_node_.getRelativeVector(self.active_sector_,w)
    obj.node().setLinearVelocity(vel)
    obj.node().setAngularVelocity(w)
    
    #logging.info("Linear Velocity: %s"%(str(vel)))

  def updateCamera(self):
 
    if len(self.controlled_objects_) > 0:
      obj = self.controlled_objects_[self.controlled_obj_index_]
      self.cam.setPos(self.active_sector_,obj.getPos(self.active_sector_))
      self.cam.setY(self.active_sector_,-CAM_DISTANCE/1)
      self.cam.lookAt(obj.getParent(),obj.getPos())

  def cleanup(self):
    
    for i in range(0,len(self.controlled_objects_)):
      rb = self.controlled_objects_[i]
      self.physics_world_.removeRigidBody(rb.node())
      rb.removeNode()

    self.object_nodes_ = []
    self.controlled_objects_ = []
    self.physics_world_.removeRigidBody(self.ground_.node())
    self.ground_ = None
    self.physics_world_ = None
    self.debug_node_ = None
    self.cam.reparentTo(self.render)
    self.world_node_.removeNode()
    self.world_node_ = None

  # _____HANDLER_____

  def toggleKinematicMode(self):
    self.kinematic_mode_ = not self.kinematic_mode_

    print "Kinematic Mode %s"%('ON' if self.kinematic_mode_ else 'OFF')

  def selectNextControlledObject(self):
    self.controlled_obj_index_+=1
    if self.controlled_obj_index_ >= len(self.controlled_objects_):
      self.controlled_obj_index_ = 0 #  reset


  def doExit(self):
    self.cleanup()
    sys.exit(1)

  def doReset(self):
    self.cleanup()
    self.setupPhysics()

  def toggleDebug(self):

    if self.debug_node_.isHidden():
      self.debug_node_.show()
    else:
      self.debug_node_.hide()

  def doScreenshot(self):
    self.screenshot('Bullet')
class TestGameBase(ShowBase):
  
  __CAM_ZOOM__ =  1
  __CAM_STEP__ = 0.2
  __CAM_ORIENT_STEP__ = 4.0
  
  __BACKGROUND_IMAGE_PACKAGE__ = 'platformer_core'
  __BACKGROUND_IMAGE_PATH__ = rospack.RosPack().get_path(__BACKGROUND_IMAGE_PACKAGE__) + '/resources/backgrounds/' + 'sky02.png'
  __BACKGROUND_POSITION__ = Vec3(0,100,0)
  __BACKGROUND_SCALE__ = 0.2 

  __DESIRED_FRAME_RATE__ = 1.0/60.0

  
  def __init__(self,name ='TestGameBase'):
    
    # configure to use group-mask collision filtering mode in the bullet physics world
    loadPrcFileData('', 'bullet-filter-algorithm groups-mask')
    
    ShowBase.__init__(self)   
    
    self.name_ = name
    self.setupRendering()
    self.setupResources()
    self.setupControls()
    self.setupScene()
    self.clock_ = ClockObject()
    self.delta_time_accumulator_ = 0.0 # Used to keep simulation time consistent (See https://www.panda3d.org/manual/index.php/Simulating_the_Physics_World)
    self.desired_frame_rate_ = TestGameBase.__DESIRED_FRAME_RATE__
  
    # Task
    taskMgr.add(self.update, 'updateWorld')
    
    
  def setupRendering(self):

    self.setBackgroundColor(0.1, 0.1, 0.8, 1)
    self.setFrameRateMeter(True)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = self.render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = self.render.attachNewNode(dlight)

    self.render.clearLight()
    self.render.setLight(alightNP)
    self.render.setLight(dlightNP)
    
    self.setupBackgroundImage()
    
  def setupBackgroundImage(self):    
    
    image_file = Filename(TestGameBase.__BACKGROUND_IMAGE_PATH__)
    
    # check if image can be loaded
    img_head = PNMImageHeader()
    if not img_head.readHeader(image_file ):
        raise IOError("PNMImageHeader could not read file %s. Try using absolute filepaths"%(image_file.c_str()))
        sys.exit()
        
    # Load the image with a PNMImage
    w = img_head.getXSize()
    h = img_head.getYSize()
    img = PNMImage(w,h)
    #img.alphaFill(0)
    img.read(image_file) 
    
    texture = Texture()        
    texture.setXSize(w)
    texture.setYSize(h)
    texture.setZSize(1)    
    texture.load(img)
    texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image
    texture.setWrapV(Texture.WM_border_color)
    texture.setBorderColor(LColor(0,0,0,0))
    
    # creating CardMaker to hold the texture
    cm = CardMaker('background')
    cm.setFrame(-0.5*w,0.5*w,-0.5*h,0.5*h)  # This configuration places the image's topleft corner at the origin (left, right, bottom, top)
    background_np = NodePath(cm.generate())            
    background_np.setTexture(texture)
    
    background_np.reparentTo(self.render)
    background_np.setPos(TestGameBase.__BACKGROUND_POSITION__)
    background_np.setScale(TestGameBase.__BACKGROUND_SCALE__)
    
  def setupControls(self):
    
       # Input (Events)
    self.accept('escape', self.exit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleDebug)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleWireframe)
    self.accept('f5', self.doScreenshot)
    
    self.input_state_ = InputState()
    button_map = {'a' : KeyboardButtons.KEY_A , 'q' : KeyboardButtons.KEY_Q,'escape' : KeyboardButtons.KEY_ESC,
                  'f1' : KeyboardButtons.KEY_F1,
                  'e': KeyboardButtons.KEY_E,'w': KeyboardButtons.KEY_W}
    self.input_manager_ = KeyboardController(self.input_state_, button_map)
    
    # Creating directional moves
    self.input_manager_.addMove(Move('UP',[KeyboardButtons.DPAD_UP],False,lambda : self.moveCamUp()))
    self.input_manager_.addMove(Move('DOWN',[KeyboardButtons.DPAD_DOWN],False,lambda : self.moveCamDown()))
    self.input_manager_.addMove(Move('LEFT',[KeyboardButtons.DPAD_LEFT],False,lambda : self.moveCamLeft()))
    self.input_manager_.addMove(Move('RIGHT',[KeyboardButtons.DPAD_RIGHT],False,lambda : self.moveCamRight()))
    
    self.input_manager_.addMove(Move('ROTATE_LEFT',[KeyboardButtons.KEY_E],False,lambda : self.rotateCamZCounterClockwise()))
    self.input_manager_.addMove(Move('ROTATE_RIGHT',[KeyboardButtons.KEY_W],False,lambda : self.rotateCamZClockwise()))
    
    self.input_manager_.addMove(Move('ZoomIn',[KeyboardButtons.KEY_Q],False,lambda : self.zoomIn()))
    self.input_manager_.addMove(Move('ZoomOut',[KeyboardButtons.KEY_A],False,lambda : self.zoomOut()))
    
    
    self.title = self.createTitle("Panda3D: " + self.name_)
    self.instructions_ = [
                          self.createInstruction(0.06, "ESC: Quit"),
                          self.createInstruction(0.12, "Up/Down: Move Camera Up/Down"),
                          self.createInstruction(0.18, "Left/Right: Move Camera Left / Rigth"),
                          self.createInstruction(0.24, "q/a: Zoom in / Zoom out"),
                          self.createInstruction(0.30, "F1: Toggle Debug"),
                          self.createInstruction(0.36, "F2: Toggle Texture"),
                          self.createInstruction(0.42, "F3: Toggle Wireframe"),
                          self.createInstruction(0.48, "F5: Screenshot")
                          ]
  
  def setupScene(self):
    
    self.level_ = TestLevel('ground-zero')
    self.level_.reparentTo(self.render)
    
    # enable debug visuals
    self.debug_node_ = self.level_.attachNewNode(BulletDebugNode('Debug'))
    self.debug_node_.node().showWireframe(True)
    self.debug_node_.node().showConstraints(True)
    self.debug_node_.node().showBoundingBoxes(False)
    self.debug_node_.node().showNormals(True)    
    self.level_.getPhysicsWorld().setDebugNode(self.debug_node_.node())    
    self.debug_node_.hide()
    
    self.cam.reparentTo(self.level_)
    self.cam.setPos(self.level_,0, -TestGameBase.__CAM_ZOOM__*24, TestGameBase.__CAM_STEP__*25)
    
    self.camera_controller_ = CameraController(self.cam)
    
  def setupResources(self):
    pass  
  
  def cleanup(self):    
    self.level_.detachNode()
    self.level_ = None
    self.input_manager_ = None
  
  def update(self,task):
    self.clock_.tick()
    dt = self.clock_.getDt()

    self.delta_time_accumulator_ += dt
    while self.delta_time_accumulator_ > self.desired_frame_rate_:

      self.delta_time_accumulator_ -= self.desired_frame_rate_

      self.level_.update(self.desired_frame_rate_)      
      self.input_manager_.update(self.desired_frame_rate_)
      self.camera_controller_.update(self.desired_frame_rate_)
      StateMachine.processEvents() 

    
    return task.cont
    
  # __ CAM_METHODS __
  def moveCamUp(self):
      self.cam.setPos(self.cam.getPos() + Vec3(0, 0,TestGameBase.__CAM_STEP__))
      
  def moveCamDown(self):
      self.cam.setPos(self.cam.getPos() + Vec3(0, 0,-TestGameBase.__CAM_STEP__))
  
  def moveCamRight(self):
      self.cam.setPos(self.cam.getPos() + Vec3(TestGameBase.__CAM_STEP__,0,0))
  
  def moveCamLeft(self):
      self.cam.setPos(self.cam.getPos() + Vec3(-TestGameBase.__CAM_STEP__,0,0))
      
  def rotateCamZClockwise(self):
    self.cam.setH(self.cam.getH() + TestGameBase.__CAM_ORIENT_STEP__)
    
  def rotateCamZCounterClockwise(self):
    self.cam.setH(self.cam.getH() + -TestGameBase.__CAM_ORIENT_STEP__)
      
  def zoomIn(self):
    self.cam.setY(self.cam.getY()+TestGameBase.__CAM_ZOOM__)

  def zoomOut(self):
    self.cam.setY(self.cam.getY()-TestGameBase.__CAM_ZOOM__)
    
  def toggleDebug(self):
    if self.debug_node_.isHidden():
      self.debug_node_.show()
    else:
      self.debug_node_.hide()
      
  def doReset(self):
    logging.warn("Reset Not implemented")
    pass
  
  def doScreenshot(self):
    self.screenshot('Pand3d')
      
  # __ END OF CAM METHODS __
  
  # SUPPORT METHODS
  def createInstruction(self,pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05,
              shadow=(0, 0, 0, 1), parent=base.a2dTopLeft,
              pos=(0.08, -pos - 0.04), align=TextNode.ALeft)
    
    # Function to put title on the screen.
  def createTitle(self,text):
    return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.08,
              parent=base.a2dBottomRight, align=TextNode.ARight,
              pos=(-0.1, 0.09), shadow=(0, 0, 0, 1))
      
  def exit(self):
    self.cleanup()
    sys.exit(1)
      
    
    
class TestApplication(ShowBase):

  def __init__(self):

    ShowBase.__init__(self)

    self.setupRendering()
    self.setupControls()
    self.setupPhysics()
    self.clock_ = ClockObject()
    self.controlled_obj_index_ = 0
    self.kinematic_mode_ = False
  
    # Task
    taskMgr.add(self.update, 'updateWorld')


  def setupRendering(self):

    self.setBackgroundColor(0.1, 0.1, 0.8, 1)
    self.setFrameRateMeter(True)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = self.render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = self.render.attachNewNode(dlight)

    self.render.clearLight()
    self.render.setLight(alightNP)
    self.render.setLight(dlightNP)

  def setupControls(self):

    # Input (Events)
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)
    self.accept('n', self.selectNextControlledObject)
    self.accept('k',self.toggleKinematicMode)
    self.accept('p',self.printInfo)
    self.accept('q',self.zoomIn)
    self.accept('a',self.zoomOut)
    self.accept('s',self.toggleRightLeft)

    # Inputs (Polling)
    self.input_state_ = InputState()
    self.input_state_.watchWithModifiers("right","arrow_right")
    self.input_state_.watchWithModifiers('left', 'arrow_left')
    self.input_state_.watchWithModifiers('jump', 'arrow_up')
    self.input_state_.watchWithModifiers('stop', 'arrow_down')
    self.input_state_.watchWithModifiers('roll_right', 'c')
    self.input_state_.watchWithModifiers('roll_left', 'z')
    self.input_state_.watchWithModifiers('roll_stop', 'x')

    self.title = addTitle("Panda3D: Sprite Animation")
    self.inst1 = addInstructions(0.06, "ESC: Quit")
    self.inst2 = addInstructions(0.12, "Up/Down: Jump/Stop")
    self.inst3 = addInstructions(0.18, "Left/Right: Move Left / Move Rigth")
    self.inst4 = addInstructions(0.24, "z/x/c : Rotate Left/ Rotate Stop / Rotate Right")
    self.inst5 = addInstructions(0.30, "n: Select Next Character")
    self.inst6 = addInstructions(0.36, "k: Toggle Kinematic Mode")
    self.inst7 = addInstructions(0.42, "q/a: Zoom in / Zoom out")
    self.inst7 = addInstructions(0.48, "s: Toggle Rigth|Left ")
    self.inst7 = addInstructions(0.54, "p: Print Info")

  def printInfo(self):
    self.sprt_animator_.printInfo()

  def setupPhysics(self):

    # setting up physics world and parent node path 
    self.physics_world_ = BulletWorld()
    self.world_node_ = self.render.attachNewNode('world')
    self.cam.reparentTo(self.world_node_)
    self.physics_world_.setGravity(Vec3(0, 0, -9.81))

    self.debug_node_ = self.world_node_.attachNewNode(BulletDebugNode('Debug'))
    self.debug_node_.show()
    self.debug_node_.node().showWireframe(True)
    self.debug_node_.node().showConstraints(True)
    self.debug_node_.node().showBoundingBoxes(False)
    self.debug_node_.node().showNormals(True)

    # setting up ground
    self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground'))
    self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0))
    self.ground_.setPos(0,0,0)
    self.ground_.setCollideMask(BitMask32.allOn())
    self.physics_world_.attachRigidBody(self.ground_.node())

    self.object_nodes_ = []
    self.controlled_objects_=[]
    num_boxes = 20
    side_length = 0.2
    size = Vec3(0.5*side_length,0.5*side_length,0.5*side_length)
    start_pos = Vec3(-num_boxes*side_length,0,6)

    """
    # creating boxes
    box_visual = loader.loadModel('models/box.egg')
    box_visual.clearModelNodes()
    box_visual.setTexture(loader.loadTexture('models/wood.png'))
    
    bounds = box_visual.getTightBounds() # start of box model scaling
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = side_length/max([extents.getX(),extents.getY(),extents.getZ()])
    box_visual.setScale((scale_factor,scale_factor ,scale_factor)) # end of box model scaling

    for i in range(0,20):
      self.addBox("name %i"%(i),size,start_pos + Vec3(i*side_length,0,0),box_visual)

    start_pos = Vec3(-num_boxes*side_length,0,8)
    for i in range(0,20):
      self.addBox("name %i"%(i),size,start_pos + Vec3(i*2*side_length,0,0),box_visual)
    """

    # creating sprite animator
    sprt_animator = SpriteAnimator('hiei_run')            
    if not sprt_animator.loadImage(SPRITE_IMAGE_DETAILS[0], # file path
      SPRITE_IMAGE_DETAILS[1], # columns
      SPRITE_IMAGE_DETAILS[2], # rows
      SPRITE_IMAGE_DETAILS[3], # scale x
      SPRITE_IMAGE_DETAILS[4], # scale y
      SPRITE_IMAGE_DETAILS[5]): # frame rate
      print "Error loading image %s"%(SPRITE_IMAGE_DETAILS[0])
      sys.exit(1)
    self.sprt_animator_ = sprt_animator

    # creating Mobile Character Box
    size = Vec3(0.5,0.2,1)
    mbox_visual = loader.loadModel('models/box.egg')
    bounds = mbox_visual.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()])
    mbox_visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor)

    mbox = self.world_node_.attachNewNode(BulletRigidBodyNode('CharacterBox'))
    mbox.node().addShape(BulletBoxShape(size/2))
    mbox.node().setMass(1.0)
    mbox.node().setLinearFactor((1,0,1))
    mbox.node().setAngularFactor((0,1,0))
    mbox.setCollideMask(BitMask32.allOn())
    mbox_visual.instanceTo(mbox)
    mbox_visual.hide()
    mbox.setPos(Vec3(1,0,size.getZ()+1))

    bounds = sprt_animator.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = size.getZ()/extents.getZ()
    sprt_animator.setScale(scale_factor,1,scale_factor)
    sprt_animator.reparentTo(mbox)
    bounds = sprt_animator.getTightBounds()
    print "Sprite Animator bounds %s , %s"%(str(bounds[1]),str(bounds[0]))
    
    self.physics_world_.attachRigidBody(mbox.node())
    self.object_nodes_.append(mbox)
    self.controlled_objects_.append(mbox)

    # creating sphere
    diameter = 0.4
    sphere_visual = loader.loadModel('models/ball.egg')

    bounds = sphere_visual.getTightBounds() # start of model scaling
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = diameter/max([extents.getX(),extents.getY(),extents.getZ()])
    sphere_visual.clearModelNodes()
    sphere_visual.setScale(scale_factor,scale_factor,scale_factor) # end of model scaling

    sphere_visual.setTexture(loader.loadTexture('models/bowl.jpg'))
    sphere = self.world_node_.attachNewNode(BulletRigidBodyNode('Sphere'))
    sphere.node().addShape(BulletSphereShape(0.5*diameter))
    sphere.node().setMass(1.0)
    sphere.node().setLinearFactor((1,0,1))
    sphere.node().setAngularFactor((0,1,0))
    sphere.setCollideMask(BitMask32.allOn())
    sphere_visual.instanceTo(sphere)
    sphere.setPos(Vec3(0,0,size.getZ()+1))
    
    self.physics_world_.attachRigidBody(sphere.node())
    self.object_nodes_.append(sphere)
    self.controlled_objects_.append(sphere)

    self.setupLevel()


  def addBox(self,name,size,pos,visual):

    # Box (dynamic)
    box = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    box.node().setMass(1.0)
    box.node().addShape(BulletBoxShape(size))
    box.setPos(pos)
    box.setCollideMask(BitMask32.allOn())
    box.node().setLinearFactor((1,0,1))
    box.node().setAngularFactor((0,1,0))
    visual.instanceTo(box)

    self.physics_world_.attachRigidBody(box.node())
    self.object_nodes_.append(box)

  def setupLevel(self):

    # (left, top, length ,depth(y) ,height)
    platform_details =[ 
      (-20, 4, 20, 4, 1  ),
      (-2, 5, 10, 4, 1  ),
      ( 4 , 2 , 16, 4, 1.4),
      (-4 , 1, 10, 4, 1),
      ( 16, 6, 30, 4, 1)
      ]
    for i in range(0,len(platform_details)):
      p = platform_details[i]
      topleft = (p[0],p[1])
      size = Vec3(p[2],p[3],p[4])
      self.addPlatform(topleft, size,'Platform%i'%(i))


  def addPlatform(self,topleft,size,name):

    # Visual
    visual = loader.loadModel('models/box.egg')
    visual.setTexture(loader.loadTexture('models/iron.jpg'))
    bounds = visual.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()])
    visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor)
    
    # Box (static)
    platform = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    platform.node().setMass(0)
    platform.node().addShape(BulletBoxShape(size/2))
    platform.setPos(Vec3(topleft[0] + 0.5*size.getX(),0,topleft[1]-0.5*size.getZ()))
    platform.setCollideMask(BitMask32.allOn())
    visual.instanceTo(platform)

    self.physics_world_.attachRigidBody(platform.node())
    self.object_nodes_.append(platform)

  def update(self, task):

    self.clock_.tick()
    dt = self.clock_.getDt()
    self.processInput(dt)    
    self.physics_world_.doPhysics(dt, 5, 1.0/180.0)
    self.updateCamera()
  

    

    return task.cont 

  def processInput(self,dt):
  
    activate = False
    obj = self.controlled_objects_[self.controlled_obj_index_]

    if self.kinematic_mode_:
      obj.node().setKinematic(True)
      return
    else:
      obj.node().setKinematic(False)

    vel = obj.node().getLinearVelocity()
    w = obj.node().getAngularVelocity()

  
    if self.input_state_.isSet('right'): 
      vel.setX(2)
      activate = True

    if self.input_state_.isSet('left'): 
      vel.setX(-2)
      activate = True

    if self.input_state_.isSet('jump'): 
      vel.setZ(4)
      activate = True

    if self.input_state_.isSet('stop'): 
      vel.setX(0)

    if self.input_state_.isSet('roll_right'):
      w.setY(ROTATIONAl_SPEED)
      activate = True

    if self.input_state_.isSet('roll_left'):
      w.setY(-ROTATIONAl_SPEED)
      activate = True

    if self.input_state_.isSet('roll_stop'):
      w.setY(0)

    if activate : obj.node().setActive(True,True)
    obj.node().setLinearVelocity(vel)
    obj.node().setAngularVelocity(w)

  def updateCamera(self):

    if len(self.controlled_objects_) > 0:
      obj = self.controlled_objects_[self.controlled_obj_index_]
      self.cam.setPos(obj,0, -CAM_DISTANCE, 0)
      self.cam.lookAt(obj.getPos())

  def cleanup(self):
    
    for i in range(0,len(self.object_nodes_)):
      rb = self.object_nodes_[i]
      self.physics_world_.removeRigidBody(rb.node())

    self.object_nodes_ = []
    self.controlled_objects_ = []
    self.physics_world_.removeRigidBody(self.ground_.node())
    self.ground_ = None
    self.physics_world_ = None
    self.debug_node_ = None
    self.sprt_animator_ = None
    self.cam.reparentTo(self.render)
    self.world_node_.removeNode()
    self.world_node_ = None

  # _____HANDLER_____

  def zoomIn(self):
    global CAM_DISTANCE
    CAM_DISTANCE-=4

  def zoomOut(self):
    global CAM_DISTANCE
    CAM_DISTANCE+=4

  def toggleKinematicMode(self):
    self.kinematic_mode_ = not self.kinematic_mode_

    print "Kinematic Mode %s"%('ON' if self.kinematic_mode_ else 'OFF')

  def toggleRightLeft(self):
    self.sprt_animator_.faceRight(not self.sprt_animator_.facing_right_)

  def selectNextControlledObject(self):
    self.controlled_obj_index_+=1
    if self.controlled_obj_index_ >= len(self.controlled_objects_):
      self.controlled_obj_index_ = 0 #  reset


  def doExit(self):
    self.cleanup()
    sys.exit(1)

  def doReset(self):
    self.cleanup()
    self.setupPhysics()

  def toggleDebug(self):
    if self.debug_node_.isHidden():
      self.debug_node_.show()
    else:
      self.debug_node_.hide()

  def doScreenshot(self):
    self.screenshot('Bullet')
class TestApplication(ShowBase):

  def __init__(self):

    ShowBase.__init__(self)

    self.setupRendering()
    self.setupControls()
    self.setupPhysics()
    #self.cam.reparentTo(self.world_node_)
    self.clock_ = ClockObject()
    self.controlled_obj_index_ = 0
    self.kinematic_mode_ = False
  
    # Task
    taskMgr.add(self.update, 'updateWorld')


  def setupRendering(self):

    self.setBackgroundColor(0.1, 0.1, 0.8, 1)
    self.setFrameRateMeter(True)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = self.render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = self.render.attachNewNode(dlight)

    self.render.clearLight()
    self.render.setLight(alightNP)
    self.render.setLight(dlightNP)
    
    self.setupBackgroundImage()
    
  def setupBackgroundImage(self):
    
    
    image_file = Filename(rospack.RosPack().get_path(BACKGROUND_IMAGE_PACKAGE) + '/resources/backgrounds/' + BACKGROUND_IMAGE_PATH)
    
    # check if image can be loaded
    img_head = PNMImageHeader()
    if not img_head.readHeader(image_file ):
        raise IOError, "PNMImageHeader could not read file %s. Try using absolute filepaths"%(image_file.c_str())
        sys.exit()
        
    # Load the image with a PNMImage
    w = img_head.getXSize()
    h = img_head.getYSize()
    img = PNMImage(w,h)
    #img.alphaFill(0)
    img.read(image_file) 
    
    texture = Texture()        
    texture.setXSize(w)
    texture.setYSize(h)
    texture.setZSize(1)    
    texture.load(img)
    texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image
    texture.setWrapV(Texture.WM_border_color)
    texture.setBorderColor(LColor(0,0,0,0))
    
    # creating CardMaker to hold the texture
    cm = CardMaker('background')
    cm.setFrame(-0.5*w,0.5*w,-0.5*h,0.5*h)  # This configuration places the image's topleft corner at the origin (left, right, bottom, top)
    background_np = NodePath(cm.generate())            
    background_np.setTexture(texture)
    
    background_np.reparentTo(self.render)
    background_np.setPos(BACKGROUND_POSITION)
    background_np.setScale(BACKGROUND_IMAGE_SCALE)
  

  def setupControls(self):

    # Input (Events)
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)
    self.accept('n', self.selectNextControlledObject)
    self.accept('k', self.toggleKinematicMode)

    # Inputs (Polling)
    self.input_state_ = InputState()
    self.input_state_.watchWithModifiers("right","arrow_right")
    self.input_state_.watchWithModifiers('left', 'arrow_left')
    self.input_state_.watchWithModifiers('jump', 'arrow_up')
    self.input_state_.watchWithModifiers('stop', 'arrow_down')
    self.input_state_.watchWithModifiers('roll_right', 'c')
    self.input_state_.watchWithModifiers('roll_left', 'z')
    self.input_state_.watchWithModifiers('roll_stop', 'x')


    self.title = addTitle("Panda3D: Kinematic Objects")
    self.inst1 = addInstructions(0.06, "ESC: Quit")
    self.inst2 = addInstructions(0.12, "Up/Down: Jump/Stop")
    self.inst3 = addInstructions(0.18, "Left/Right: Move Left / Move Rigth")
    self.inst4 = addInstructions(0.24, "z/x/c : Rotate Left/ Rotate Stop / Rotate Right")
    self.inst5 = addInstructions(0.30, "n: Select Next Character")
    self.inst6 = addInstructions(0.36, "k: Toggle Kinematic Mode")
    self.inst7 = addInstructions(0.42, "f1: Toggle Wireframe")
    self.inst8 = addInstructions(0.48, "f2: Toggle Texture")
    self.inst9 = addInstructions(0.54, "f3: Toggle BulletDebug")
    self.inst10 = addInstructions(0.60, "f5: Capture Screenshot")



  def setupPhysics(self):

    # setting up physics world and parent node path 
    self.physics_world_ = BulletWorld()
    self.world_node_ = self.render.attachNewNode('world')
    self.cam.reparentTo(self.world_node_)
    self.physics_world_.setGravity(Vec3(0, 0, -9.81))

    self.debug_node_ = self.world_node_.attachNewNode(BulletDebugNode('Debug'))
    self.debug_node_.node().showWireframe(True)
    self.debug_node_.node().showConstraints(True)
    self.debug_node_.node().showBoundingBoxes(True)
    self.debug_node_.node().showNormals(True)
    self.physics_world_.setDebugNode(self.debug_node_.node())
    self.debug_node_.hide()

    # setting up ground
    self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground'))
    self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0))
    self.ground_.setPos(0,0,0)
    self.ground_.setCollideMask(BitMask32.allOn())
    self.physics_world_.attachRigidBody(self.ground_.node())

    self.object_nodes_ = []
    self.controlled_objects_=[]
    num_boxes = 20
    side_length = 0.2
    size = Vec3(0.5*side_length,0.5*side_length,0.5*side_length)
    start_pos = Vec3(-num_boxes*side_length,0,6)

    # creating boxes
    box_visual = loader.loadModel('models/box.egg')
    box_visual.clearModelNodes()
    box_visual.setTexture(loader.loadTexture('models/wood.png'))
    #box_visual.setRenderModeWireframe()
    bounds = box_visual.getTightBounds() # start of box model scaling
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = side_length/max([extents.getX(),extents.getY(),extents.getZ()])
    box_visual.setScale((scale_factor,scale_factor ,scale_factor)) # end of box model scaling

    for i in range(0,20):
      self.addBox("name %i"%(i),size,start_pos + Vec3(i*side_length,0,0),box_visual)

    start_pos = Vec3(-num_boxes*side_length,0,8)
    for i in range(0,20):
      self.addBox("name %i"%(i),size,start_pos + Vec3(i*2*side_length,0,0),box_visual)


    # creating sphere
    diameter = 0.4
    sphere_visual = loader.loadModel('models/ball.egg')

    bounds = sphere_visual.getTightBounds() # start of model scaling
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = diameter/max([extents.getX(),extents.getY(),extents.getZ()])
    sphere_visual.clearModelNodes()
    sphere_visual.setScale(scale_factor,scale_factor,scale_factor) # end of model scaling

    sphere_visual.setTexture(loader.loadTexture('models/bowl.jpg'))
    sphere = self.world_node_.attachNewNode(BulletRigidBodyNode('Sphere'))
    sphere.node().addShape(BulletSphereShape(0.5*diameter))
    sphere.node().setMass(1.0)
    sphere.node().setLinearFactor((1,0,1))
    sphere.node().setAngularFactor((0,1,0))
    sphere.setCollideMask(BitMask32.allOn())
    sphere_visual.instanceTo(sphere)
    sphere.setPos(Vec3(0,0,size.getZ()+1))
    
    self.physics_world_.attachRigidBody(sphere.node())
    self.object_nodes_.append(sphere)
    self.controlled_objects_.append(sphere)

    # creating mobile box
    size = Vec3(0.2,0.2,0.4)
    mbox_visual = loader.loadModel('models/box.egg')
    bounds = mbox_visual.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()])
    mbox_visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor)

    mbox = self.world_node_.attachNewNode(BulletRigidBodyNode('MobileBox'))
    mbox.node().addShape(BulletBoxShape(size/2))
    mbox.node().setMass(1.0)
    mbox.node().setLinearFactor((1,0,1))
    mbox.node().setAngularFactor((0,1,0))
    mbox.setCollideMask(BitMask32.allOn())
    mbox_visual.instanceTo(mbox)
    mbox.setPos(Vec3(1,0,size.getZ()+1))
    
    self.physics_world_.attachRigidBody(mbox.node())
    self.object_nodes_.append(mbox)
    self.controlled_objects_.append(mbox)

    self.setupLevel()
    
    # Nodepath test
    np = NodePath('testnode0')    
    print('Created Nodepath to node %s'%(np.getName()))
    np.clear()    
    
    #np.attachNewNode(PandaNode('testnode1'))
    np.__init__(PandaNode('testnode1'))
    print('Attached new node %s to empty Nodepath'%(np.getName()))


  def addBox(self,name,size,pos,visual):

    # Box (dynamic)
    box = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    box.node().setMass(1.0)
    box.node().addShape(BulletBoxShape(size))
    box.setPos(pos)
    box.setCollideMask(BitMask32.allOn())
    box.node().setLinearFactor((1,0,1))
    box.node().setAngularFactor((0,1,0))
    visual.instanceTo(box)

    self.physics_world_.attachRigidBody(box.node())
    self.object_nodes_.append(box)

  def setupLevel(self):

    # (left, top, length ,depth(y) ,height)
    platform_details =[ 
      (-20, 4, 20, 4, 1  ),
      (-2, 5, 10, 4, 1  ),
      ( 4 , 2 , 16, 4, 1.4),
      (-4 , 1, 10, 4, 1),
      ( 16, 6, 30, 4, 1)
      ]

    # Platform Visuals
    visual = loader.loadModel('models/box.egg')
    visual.setTexture(loader.loadTexture('models/iron.jpg'))
    
    for i in range(0,len(platform_details)):
      p = platform_details[i]
      topleft = (p[0],p[1])
      size = Vec3(p[2],p[3],p[4])
      self.addPlatform(topleft, size,'Platform%i'%(i),visual)


  def addPlatform(self,topleft,size,name,visual):


    # Box (static)
    platform = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    platform.node().setMass(0)
    platform.node().addShape(BulletBoxShape(size/2))
    platform.setPos(Vec3(topleft[0] + 0.5*size.getX(),0,topleft[1]-0.5*size.getZ()))
    platform.setCollideMask(BitMask32.allOn())
    local_visual = visual.instanceUnderNode(platform,name + '_visual')

    # Visual scaling
    bounds = local_visual.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()])
    local_visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor)
    

    self.physics_world_.attachRigidBody(platform.node())
    self.object_nodes_.append(platform)

  def update(self, task):

    self.clock_.tick()
    dt = self.clock_.getDt()
    self.processInput(dt)    
    self.physics_world_.doPhysics(dt, 5, 1.0/180.0)
    self.updateCamera()

    return task.cont 

  def processInput(self,dt):
  
    activate = False
    obj = self.controlled_objects_[self.controlled_obj_index_]

    if self.kinematic_mode_:
      obj.node().setKinematic(True)
      return
    else:
      obj.node().setKinematic(False)

    vel = obj.node().getLinearVelocity()
    w = obj.node().getAngularVelocity()

  
    if self.input_state_.isSet('right'): 
      vel.setX(2)
      activate = True

    if self.input_state_.isSet('left'): 
      vel.setX(-2)
      activate = True

    if self.input_state_.isSet('jump'): 
      vel.setZ(4)
      activate = True

    if self.input_state_.isSet('stop'): 
      vel.setX(0)

    if self.input_state_.isSet('roll_right'):
      w.setY(ROTATIONAl_SPEED)
      activate = True

    if self.input_state_.isSet('roll_left'):
      w.setY(-ROTATIONAl_SPEED)
      activate = True

    if self.input_state_.isSet('roll_stop'):
      w.setY(0)

    if activate : obj.node().setActive(True,True)
    obj.node().setLinearVelocity(vel)
    obj.node().setAngularVelocity(w)

  def updateCamera(self):

    if len(self.controlled_objects_) > 0:
      obj = self.controlled_objects_[self.controlled_obj_index_]
      self.cam.setPos(obj,0, -CAM_DISTANCE, 0)
      self.cam.lookAt(obj.getPos())

  def cleanup(self):
    
    for i in range(0,len(self.object_nodes_)):
      rb = self.object_nodes_[i]
      self.physics_world_.removeRigidBody(rb.node())

    self.object_nodes_ = []
    self.controlled_objects_ = []
    self.physics_world_.removeRigidBody(self.ground_.node())
    self.ground_ = None
    self.physics_world_ = None
    self.debug_node_ = None
    self.cam.reparentTo(self.render)
    self.world_node_.removeNode()
    self.world_node_ = None

  # _____HANDLER_____

  def toggleKinematicMode(self):
    self.kinematic_mode_ = not self.kinematic_mode_

    print "Kinematic Mode %s"%('ON' if self.kinematic_mode_ else 'OFF')

  def selectNextControlledObject(self):
    self.controlled_obj_index_+=1
    if self.controlled_obj_index_ >= len(self.controlled_objects_):
      self.controlled_obj_index_ = 0 #  reset


  def doExit(self):
    self.cleanup()
    sys.exit(1)

  def doReset(self):
    self.cleanup()
    self.setupPhysics()

  def toggleDebug(self):

    if self.debug_node_.isHidden():
      self.debug_node_.show()
    else:
      self.debug_node_.hide()

  def doScreenshot(self):
    self.screenshot('Bullet')
示例#8
0
文件: main.py 项目: GoldwinXS/PyCraft
class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        """ SETUP CONFIG VARS  """
        # self.render.setShaderAuto()
        self.globalClock = ClockObject().getGlobalClock()
        self.bullet_world = BulletWorld()
        self.bullet_world.setGravity((0, 0, -9.81))

        """ PLAYER SETUP """
        # player camera
        self.ref_node = self.render.attachNewNode('camparent')

        self.debugNP = self.render.attachNewNode(BulletDebugNode('Debug'))
        self.debugNP.show()

        self.player_body_geom = make_cube_geom(geom_color=(1, 0, 0, 1))
        player_body_geom_node = GeomNode('player')
        player_body_geom_node.addGeom(self.player_body_geom)
        self.player_body_geom_np = self.render.attachNewNode(player_body_geom_node)

        # Box
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))


        self.box_np = self.render.attachNewNode(BulletRigidBodyNode('Box'))
        self.box_np.node().setMass(1.0)
        self.box_np.node().addShape(shape)
        self.box_np.node().setDeactivationEnabled(False)
        self.player_body_geom_np.reparentTo(self.box_np)
        self.box_np.setPos(10, 10, 10)
        self.box_np.setCollideMask(BitMask32.allOn())
        self.bullet_world.attachRigidBody(self.box_np.node())


        # TODO: move player stuff to a new class
        # TODO: add cross hairs on player hud
        self.player_ray_end = NodePath('player-ray')
        self.player_ray_end.reparentTo(self.render)
        self.player_ray_end.reparentTo(self.ref_node)
        self.player_ray_end.setY(+10)  # put this in front of the player
        self.ref_node.lookAt(self.player_ray_end)

        """ SETUP VARS AND USE UTIL CLASSES """

        self.map_scale = 10
        self.map_size = 100
        self.camLens.setFov(90)
        total_size = 32 * 8

        PlayerMovement(self)
        World(self, n_grids=32)

        # self.ref_node.reparentTo(self.box_np)


        """ SKY BOX SETUP """

        skybox = GeomNode('skybox')
        skybox.addGeom(make_cube_geom(pos=(0, 0, 0), geom_color=(0.2, 0.2, 0.7, 1)))
        skybox_np = self.render.attachNewNode(skybox)
        skybox_np.reparentTo(self.ref_node)
        skybox_np.setPos((0, 1200, 0))
        skybox_np.setScale(1200)

        """ ADD LIGHTS """

        # ambient light
        ambientLight = AmbientLight('ambientLight')
        ambientLight.setColor((0.2, 0.2, 0.2, 1))
        ambientLightNP = self.render.attachNewNode(ambientLight)
        self.render.setLight(ambientLightNP)

        # a geom to represent the sun
        sun_geom_node = GeomNode('sun')
        sun_geom_node.addGeom(make_cube_geom(geom_color=(0.5, 0.5, 0.3, 1)))
        sun_geom_node_path = self.render.attachNewNode(sun_geom_node)
        sun_geom_node_path.setScale(10)

        # sun
        sun = PointLight('sun')
        sun.setColor((0.4, 0.3, 0.27, 1))
        sun.setAttenuation((0.1, 0, 0))
        sun_np = self.render.attachNewNode(sun)
        sun_geom_node_path.reparentTo(sun_np)
        self.render.setLight(sun_np)
        sun_np.setPos(total_size / 2, total_size / 2, 200)

        """ SETUP FOR ONSCREEN TEXT """

        # TODO: add chunk coords to display
        self.text = OnscreenText(text='fps', pos=(-1, -0.95), fg=(0, 1, 0, 1), align=TextNode.ALeft, scale=0.1)
        self.location_text = OnscreenText(text='location', pos=(-1, 0.8), fg=(0, 1, 0, 1), align=TextNode.ALeft,
                                          scale=0.1)
        self.previous_time = time.time()
        self.taskMgr.add(self.update_text, 'fps')
        self.taskMgr.add(self.update, 'physics')

    def update_text(self, task):
        time_since_last_called = time.time() - self.previous_time
        self.text.setText('fps: ' + str(round(1 / time_since_last_called)))
        self.previous_time = time.time()
        x, y, z = self.ref_node.getPos()
        self.location_text.setText(f'Player location x: {round(x)}, y: {round(y)}, z: {round(z)},')
        return task.cont

    def update(self, task):
        dt = self.globalClock.getDt()
        self.bullet_world.doPhysics(dt, 10, 0.008)
        return task.cont
class TestKeyboardController(ShowBase):
  
  def __init__(self):    
    ShowBase.__init__(self)
    
    
    self.clock_ = ClockObject()
    self.setupInput()    
    taskMgr.add(self.update,"update")
    
  def update(self,task):
    self.clock_.tick()
    dt = self.clock_.getDt()    
    self.input_manager_.update(dt)
    return task.cont
  
  def exitCallback(self):
    sys.exit(0)
  
  def setupInput(self):
    
    self.input_state_ = InputState()
    
    # Creating button map
    button_map = {'a' : KeyboardButtons.KEY_A , 'q' : KeyboardButtons.KEY_Q,
                  's' : KeyboardButtons.KEY_S , 'w' : KeyboardButtons.KEY_W,
                  'd' : KeyboardButtons.KEY_D , 'e' : KeyboardButtons.KEY_E,
                  'x' : KeyboardButtons.KEY_X , 'c' : KeyboardButtons.KEY_C,
                  'space' : KeyboardButtons.KEY_SPACE , 'shift' : KeyboardButtons.KEY_SHIFT,
                  'escape' : KeyboardButtons.KEY_ESC}
  
    
    self.input_manager_ = KeyboardController(self.input_state_, button_map)
    
    # Creating directional moves
    self.input_manager_.addMove(Move('UP',[KeyboardButtons.DPAD_UP],True))
    self.input_manager_.addMove(Move('DOWN',[KeyboardButtons.DPAD_DOWN],True))
    self.input_manager_.addMove(Move('LEFT',[KeyboardButtons.DPAD_LEFT],True))
    self.input_manager_.addMove(Move('RIGHT',[KeyboardButtons.DPAD_RIGHT],True))
    self.input_manager_.addMove(Move('DOWN_RIGHT',[KeyboardButtons.DPAD_DOWNRIGHT],True))
    self.input_manager_.addMove(Move('DOWN_LEFT',[KeyboardButtons.DPAD_DOWNLEFT],True))
    self.input_manager_.addMove(Move('UP_LEFT',[KeyboardButtons.DPAD_UPLEFT],True))
    self.input_manager_.addMove(Move('UP_RIGHT',[KeyboardButtons.DPAD_UPRIGHT],True))
    
    # Creating action moves
    self.input_manager_.addMove(Move('JUMP',[KeyboardButtons.KEY_A],True))
    self.input_manager_.addMove(Move('DASH',[KeyboardButtons.KEY_S],True))
    self.input_manager_.addMove(Move('LIGHT ATTACK',[KeyboardButtons.KEY_D],True))
    self.input_manager_.addMove(Move('FUERTE ATTACK',[KeyboardButtons.KEY_E],True))
    
    # released moves
    self.input_manager_.addMove(Move('RELEASE_UP',[KeyboardButtons.DPAD_UP],True),False)
    self.input_manager_.addMove(Move('RELEASE_DOWN',[KeyboardButtons.DPAD_DOWN],True),False)
    self.input_manager_.addMove(Move('RELEASE_LEFT',[KeyboardButtons.DPAD_LEFT],True),False)
    self.input_manager_.addMove(Move('RELEASE_RIGHT',[KeyboardButtons.DPAD_RIGHT],True),False)
    self.input_manager_.addMove(Move('RELEASE FUERTE ATTACK',[KeyboardButtons.KEY_E],True),False)
    self.input_manager_.addMove(Move('RELEASE LIGHT ATTACK',[KeyboardButtons.KEY_D],True),False)
    
    # exit
    self.input_manager_.addMove(Move('SPECIAL ATTACK',[KeyboardButtons.KEY_D, KeyboardButtons.KEY_E],False,lambda : self.exitCallback()))
    
    # Creating special moves
    self.input_manager_.addMove(Move('EXIT',[KeyboardButtons.KEY_ESC],False,lambda : self.exitCallback()))
    
    self.input_manager_.addMove(Move('RIGHT ABUKE PRO',[KeyboardButtons.DPAD_DOWN,
                                                 KeyboardButtons.DPAD_DOWNRIGHT,
                                                 KeyboardButtons.DPAD_RIGHT,
                                                 KeyboardButtons.DPAD_RIGHT | KeyboardButtons.KEY_D],False,
                                        lambda : sys.stdout.write("-----> RIGHT ABUKE\n")))
    
    self.input_manager_.addMove(Move('LEFT ABUKE PRO',[KeyboardButtons.DPAD_DOWN,
                                                 KeyboardButtons.DPAD_DOWNLEFT,
                                                 KeyboardButtons.DPAD_LEFT,
                                                 KeyboardButtons.DPAD_LEFT | KeyboardButtons.KEY_D],False,
                                        lambda : sys.stdout.write("-----> LEFT ABUKE \n")))
class TestJoystickController(ShowBase):
  
  def __init__(self):    
    ShowBase.__init__(self)
    
    
    self.clock_ = ClockObject()
    self.setupInput()    
    taskMgr.add(self.update,"update")
    
  def update(self,task):
    self.clock_.tick()
    dt = self.clock_.getDt()   
    pygame.event.get()
    self.joystick_manager_.update(dt)
    return task.cont
  
  def exitCallback(self):
    sys.exit(0)
    
    
  def setupInput(self):
        
    # Creating button map
    button_map = {0 : JoystickButtons.BUTTON_X , 3 : JoystickButtons.BUTTON_Y,
                  1 : JoystickButtons.BUTTON_A , 2 : JoystickButtons.BUTTON_B,
                  7 : JoystickButtons.TRIGGER_R1 , 5 : JoystickButtons.TRIGGER_R2,
                  6 : JoystickButtons.TRIGGER_L1 , 4 : JoystickButtons.TRIGGER_L2,
                  9 : JoystickButtons.BUTTON_START , 8 : JoystickButtons.BUTTON_SELECT,
                  10: JoystickButtons.TRIGGER_L3   , 11: JoystickButtons.TRIGGER_R3}
    

    if pygame.joystick.get_count() <=  0:
      logging.error("No Joysticks were found, exiting")
      sys.exit(-1)
      
    joystick = None
    for j in range(0,pygame.joystick.get_count()):
      joystick = pygame.joystick.Joystick(j)
      if joystick.init() and joystick.get_numbuttons() > 0:
        break;
      
    self.joystick_manager_ = JoystickController(button_map,joystick, JoystickController.JoystickAxes(),0.2)
    
    # Creating directional moves
    self.joystick_manager_.addMove(Move('UP',[JoystickButtons.DPAD_UP],True))
    self.joystick_manager_.addMove(Move('DOWN',[JoystickButtons.DPAD_DOWN],True))
    self.joystick_manager_.addMove(Move('LEFT',[JoystickButtons.DPAD_LEFT],True))
    self.joystick_manager_.addMove(Move('RIGHT',[JoystickButtons.DPAD_RIGHT],True))
    self.joystick_manager_.addMove(Move('DOWN_RIGHT',[JoystickButtons.DPAD_DOWNRIGHT],True))
    self.joystick_manager_.addMove(Move('DOWN_LEFT',[JoystickButtons.DPAD_DOWNLEFT],True))
    self.joystick_manager_.addMove(Move('UP_LEFT',[JoystickButtons.DPAD_UPLEFT],True))
    self.joystick_manager_.addMove(Move('UP_RIGHT',[JoystickButtons.DPAD_UPRIGHT],True))
    
    # Creating action moves
    self.joystick_manager_.addMove(Move('JUMP',[JoystickButtons.BUTTON_B],True))
    self.joystick_manager_.addMove(Move('DASH',[JoystickButtons.BUTTON_A],True))
    self.joystick_manager_.addMove(Move('LIGHT ATTACK',[JoystickButtons.BUTTON_Y],True))
    self.joystick_manager_.addMove(Move('FUERTE ATTACK',[JoystickButtons.BUTTON_X],True))
    
    # Creating release moves
    self.joystick_manager_.addMove(Move('UP_RELEASE',[JoystickButtons.DPAD_UP],True),False)
    self.joystick_manager_.addMove(Move('DOWN_RELEASE',[JoystickButtons.DPAD_DOWN],True),False)
    self.joystick_manager_.addMove(Move('LEFT_RELEASE',[JoystickButtons.DPAD_LEFT],True),False)
    self.joystick_manager_.addMove(Move('RIGHT_RELEASE',[JoystickButtons.DPAD_RIGHT],True),False)
    self.joystick_manager_.addMove(Move('JUMP_RELEASE',[JoystickButtons.BUTTON_B],True),False)
    self.joystick_manager_.addMove(Move('DASH_RELEASE',[JoystickButtons.BUTTON_A],True),False)
    self.joystick_manager_.addMove(Move('LIGHT ATTACK_RELEASE',[JoystickButtons.BUTTON_Y],True),False)
    self.joystick_manager_.addMove(Move('FUERTE ATTACK_RELEASE',[JoystickButtons.BUTTON_X],True),False)
    
    # Creating special moves
    self.joystick_manager_.addMove(Move('RIGHT ABUKE PRO',[JoystickButtons.DPAD_DOWN,
                                                 JoystickButtons.DPAD_DOWNRIGHT,
                                                 JoystickButtons.DPAD_RIGHT,
                                                 #JoystickButtons.DPAD_RIGHT | JoystickButtons.BUTTON_Y],False,
                                                 JoystickButtons.BUTTON_Y],False,
                                        lambda : sys.stdout.write("<-----> RIGHT ABUKE\n")))
    
    self.joystick_manager_.addMove(Move('LEFT ABUKE PRO',[JoystickButtons.DPAD_DOWN,
                                                 JoystickButtons.DPAD_DOWNLEFT,
                                                 JoystickButtons.DPAD_LEFT,
                                                 #JoystickButtons.DPAD_LEFT | JoystickButtons.BUTTON_Y],False,
                                                 JoystickButtons.BUTTON_Y],False,
                                        lambda : sys.stdout.write("<-----> LEFT ABUKE \n")))
    
    self.joystick_manager_.addMove(Move('FOCUS ATTACK',[
                                             JoystickButtons.BUTTON_X | JoystickButtons.BUTTON_Y],False,
                                             #JoystickButtons.BUTTON_Y],False,
                                    lambda : sys.stdout.write("<-----> FOCUS ATTACK \n")))
class TestApplication(ShowBase):

  def __init__(self,name = 'TestApplication', cam_step = 0.05, cam_zoom = 4):

    ShowBase.__init__(self)
    
    self.cam_step_ = cam_step
    self.cam_zoom_ = cam_zoom
    self.name_ = name
    self.setupRendering()
    self.setupResources()
    self.setupControls()
    self.setupPhysics()
    self.clock_ = ClockObject()
  
    # Task
    taskMgr.add(self.update, 'updateWorld')
    
    # Function to put instructions on the screen.    
  def addInstructions(self,pos, msg):
      return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05,
                shadow=(0, 0, 0, 1), parent=base.a2dTopLeft,
                pos=(0.08, -pos - 0.04), align=TextNode.ALeft)
    
    # Function to put title on the screen.
  def addTitle(self,text):
      return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.08,
                parent=base.a2dBottomRight, align=TextNode.ARight,
                pos=(-0.1, 0.09), shadow=(0, 0, 0, 1))

  def setupResources(self):
    pass

  def setupRendering(self):

    self.setBackgroundColor(0.1, 0.1, 0.8, 1)
    self.setFrameRateMeter(True)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = self.render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = self.render.attachNewNode(dlight)

    self.render.clearLight()
    self.render.setLight(alightNP)
    self.render.setLight(dlightNP)

  def setupControls(self):

    # Input (Events)
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)
    self.accept('q',self.zoomIn)
    self.accept('a',self.zoomOut)

    # Inputs (Polling)
    self.input_state_ = InputState()
    self.input_state_.watchWithModifiers("right","arrow_right")
    self.input_state_.watchWithModifiers('left', 'arrow_left')
    self.input_state_.watchWithModifiers('up', 'arrow_up')
    self.input_state_.watchWithModifiers('down', 'arrow_down')


    self.title = self.addTitle("Panda3D: " + self.name_)
    self.instructions_ = [
                          self.addInstructions(0.06, "ESC: Quit"),
                          self.addInstructions(0.12, "Up/Down: Jump/Stop"),
                          self.addInstructions(0.18, "Left/Right: Move Left / Move Rigth"),
                          self.addInstructions(0.24, "z/x/c : Rotate Left/ Rotate Stop / Rotate Right"),
                          self.addInstructions(0.30, "q/a: Zoom in / Zoom out")
                          ]
    
    
    
    


  def setupPhysics(self, use_default_objs = True):

    # setting up physics world and parent node path 
    self.physics_world_ = BulletWorld()
    self.world_node_ = self.render.attachNewNode('world')
    self.cam.reparentTo(self.world_node_)
    self.cam.setPos(self.world_node_,0, -self.cam_zoom_*6, self.cam_step_*25)
    self.physics_world_.setGravity(Vec3(0, 0, -9.81))

    self.debug_node_ = self.world_node_.attachNewNode(BulletDebugNode('Debug'))
    self.debug_node_.show()
    self.debug_node_.node().showWireframe(True)
    self.debug_node_.node().showConstraints(True)
    self.debug_node_.node().showBoundingBoxes(False)
    self.debug_node_.node().showNormals(True)    
    self.physics_world_.setDebugNode(self.debug_node_.node())
    self.debug_node_.hide()
    self.object_nodes_ = []
    self.controlled_objects_ = []
    
    self.ground_ = None
    if use_default_objs:

      # setting up ground
      self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground'))
      self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0))
      self.ground_.setPos(0,0,0)
      self.ground_.setCollideMask(BitMask32.allOn())
      self.physics_world_.attachRigidBody(self.ground_.node())
  
      self.setupLevel()


  def addBox(self,name,size,pos,visual):

    # Box (dynamic)
    box = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    box.node().setMass(1.0)
    box.node().addShape(BulletBoxShape(size))
    box.setPos(pos)
    box.setCollideMask(BitMask32.allOn())
    box.node().setLinearFactor((1,0,1))
    box.node().setAngularFactor((0,1,0))
    visual.instanceTo(box)

    self.physics_world_.attachRigidBody(box.node())
    self.object_nodes_.append(box)

  def setupLevel(self):

    # (left, top, length ,depth(y) ,height)
    platform_details =[ 
      (-20, 4, 20, 4, 1  ),
      (-2, 5, 10, 4, 1  ),
      ( 4 , 2 , 16, 4, 1),
      (-4 , 1, 10, 4, 1),
      ( 16, 6, 30, 4, 1)
      ]
    for i in range(0,len(platform_details)):
      p = platform_details[i]
      topleft = (p[0],p[1])
      size = Vec3(p[2],p[3],p[4])
      self.addPlatform(topleft, size,'Platform%i'%(i))


  def addPlatform(self,topleft,size,name):

    # Visual
    visual = loader.loadModel(RESOURCES_DIR + '/models/box.egg')
    visual.clearModelNodes()
    visual.setTexture(loader.loadTexture(RESOURCES_DIR + '/models/iron.jpg'),1)
    bounds = visual.getTightBounds()
    extents = Vec3(bounds[1] - bounds[0])
    scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()])
    visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor)
    
    # Box (static)
    platform = self.world_node_.attachNewNode(BulletRigidBodyNode(name))
    platform.node().setMass(0)
    platform.node().addShape(BulletBoxShape(size/2))
    platform.setPos(Vec3(topleft[0] + 0.5*size.getX(),0,topleft[1]-0.5*size.getZ()))
    platform.setCollideMask(BitMask32.allOn())
    visual.instanceTo(platform)

    self.physics_world_.attachRigidBody(platform.node())
    self.object_nodes_.append(platform)

  def update(self, task):

    self.clock_.tick()
    dt = self.clock_.getDt()
    self.processInput(dt)    
    self.physics_world_.doPhysics(dt, 5, 1.0/180.0)
    #self.updateCamera()    

    return task.cont 

  def processInput(self,dt):
      
        
    if self.input_state_.isSet('right'): 
      self.moveRight()

    if self.input_state_.isSet('left'): 
      self.moveLeft()

    if self.input_state_.isSet('up'): 
      self.moveUp()

    if self.input_state_.isSet('down'): 
      self.moveDown()
     

  def updateCamera(self):
      
    #self.cam.setY(-self.cam_zoom_)
    pass

  def cleanup(self):
    
    for i in range(0,len(self.object_nodes_)):
      rb = self.object_nodes_[i]
      self.physics_world_.removeRigidBody(rb.node())

    self.object_nodes_ = []
    
    if self.ground_ is not None:
      self.physics_world_.removeRigidBody(self.ground_.node())
      self.ground_ = None
      
    self.physics_world_ = None
    self.debug_node_ = None
    self.cam.reparentTo(self.render)
    self.world_node_.removeNode()
    self.world_node_ = None
    
  # _____MOVE_METHODS____
  def moveUp(self):
      self.cam.setPos(self.cam.getPos() + Vec3(0, 0,self.cam_step_))
      
  def moveDown(self):
      self.cam.setPos(self.cam.getPos() + Vec3(0, 0,-self.cam_step_))
  
  def moveRight(self):
      self.cam.setPos(self.cam.getPos() + Vec3(self.cam_step_,0,0))
  
  def moveLeft(self):
      self.cam.setPos(self.cam.getPos() + Vec3(-self.cam_step_,0,0))

  # _____HANDLER_____


  def zoomIn(self):
    #global self.cam_zoom_
    self.cam.setY(self.cam.getY()+self.cam_zoom_)

  def zoomOut(self):
    #global self.cam_zoom_
    self.cam.setY(self.cam.getY()-self.cam_zoom_)

  def doExit(self):
    self.cleanup()
    sys.exit(1)

  def doReset(self):
    self.cleanup()
    self.setupPhysics()

  def toggleDebug(self):
    if self.debug_node_.isHidden():
      self.debug_node_.show()
    else:
      self.debug_node_.hide()

  def doScreenshot(self):
    self.screenshot('Pand3d')