Exemplo n.º 1
0
    def setDrawer(self, budget=100000):
        """Initializes the MeshDrawer() generator to draw lines in Panda3d

        Keyword Arguments:
            budget {int} -- maximum triangles for rendering the mesh (default: {100000})
        """
        self.generator = MeshDrawer()
        self.generator.setBudget(budget)
        self.generatorNode = self.generator.getRoot()
        self.generatorNode.reparentTo(self.render)
Exemplo n.º 2
0
 def __init__(self):
     self.meshDrawer = MeshDrawer()
     self.meshDrawer.setBudget(2)
     mdNode = self.meshDrawer.getRoot()
     mdNode.reparentTo(base.render)
     mdNode.setDepthWrite(False)
     mdNode.setTransparency(True)
     mdNode.setTwoSided(True)
     # mdNode.setTexture(loader.loadTexture("pixel.png"))
     mdNode.setBin("fixed", 0)
     mdNode.setLightOff(True)
Exemplo n.º 3
0
class Panda3dViewer(BaseViewer, ShowBase):
    def __init__(self, params=None, **kwargs):
        # Load 3d-Model and parameter
        super(Panda3dViewer, self).__init__(params=params)
        self.world_x_range = kwargs.pop("x_range", [-40, 40])
        self.world_y_range = kwargs.pop("y_range", [-40, 40])
        self.follow_agent_id = kwargs.pop("follow_agent_id", -1)
        self.screen_dims = kwargs.pop("screen_dims", [1024, 1024])
        self.path = os.path.join(os.path.dirname(os.path.abspath(__file__))) # TODO(@fortiss): load into parameter at a earlier stage
        self.agent_model_path = kwargs.pop("model_path",
                                           self.path + "/models/box_car")
        self.agent_scale = kwargs.pop("model_scale",
                                      np.array([3.65, 1.8, 0.8], dtype=float))
        self.agent_orientation = \
            kwargs.pop("model_orientation",np.array([180, 0,0],
                       dtype=float))
        self.agent_translation = kwargs.pop("model_translation",
                                            np.array([3.2, 0.9], dtype=float))
        self.range_for_zoom = kwargs.pop("is_zoom_range", False)
        self.line_thicknesses = kwargs.pop("line_thickness", { #Dict of keys cameras and values [line thickness, height] which are needed to calculate the dynamic thickness
            -2: [0.35, 700],
            -1: [0.35, 700],
            0: [0.1, 70]
        })
        # Parameter for agent camera views
        # [driving direction offset, height, x-orientation,y-orientation,z-orientation offset to driving direction, LookAt?]
        self.agent_cam_parameter = kwargs.pop(
            "agent_view", {
                "bird_agent": [0, 70, 0, 270, 270, False],
                "third": [-35, 15, 0, 0, 0, True],
                "first": [1.2, 2.7, 180, 190, 90, False]
            })

        # Set Window Size
        self.wp = WindowProperties().getDefault()
        self.wp.setSize(self.screen_dims[0], self.screen_dims[1])
        self.wp.setTitle("BARK Panda3d Viewer")
        WindowProperties.setDefault(self.wp)
        ShowBase.__init__(self)

        # Render Objects Dict
        self.agent_nodes = {}
        # Agent Poses Dict
        self.agent_poses = {}
        self.setLight()

        # Creating a plane as floor #TODO parameter from map parameter
        frame = [
            -self.screen_dims[0] / 2, self.screen_dims[0] / 2,
            -self.screen_dims[1] / 2, self.screen_dims[1] / 2
        ]
        color = VBase4(self.plane_color[0], self.plane_color[1],
                       self.plane_color[2], self.plane_alpha)
        self.createPlane(frame=frame, color=color)

        # Set up the camera loop task
        self.camera_list = [-1]
        self.initCam()
        self.taskMgr.add(self.spinCameraTask, "SpinCameraTask")

         # Set up the line generator
        self.setDrawer(budget=100000)

        # -2 : global camera with camera control, -1: transition state for enabling mouse control, 0: agent cameras
        self.perspectives = {
            -2: ["birdview", "autozoom"],
            -1: ["None"],
            0: ["bird_agent", "third", "first"]
        }
        self.perspective = self.perspectives[self.camIndex(
            self.follow_agent_id)]

        self.line_thickness = self.line_thicknesses[self.camIndex(
            self.follow_agent_id)][0]
        self.addButtons()

    def setDrawer(self, budget=100000):
        """Initializes the MeshDrawer() generator to draw lines in Panda3d

        Keyword Arguments:
            budget {int} -- maximum triangles for rendering the mesh (default: {100000})
        """

        self.generator = MeshDrawer()
        self.generator.setBudget(budget)
        self.generatorNode = self.generator.getRoot()
        self.generatorNode.reparentTo(self.render)

    def createPlane(self, frame=None, color=VBase4(1, 1, 1, 1)):
        """ Creates a Plane/Card with the Panda3d Cardmaker() class
        Keyword Arguments:
            frame {list} -- The coordinates [x1,y1,x2,y2] of the planes/cards edges (default: {[-1, -1, 1, 1]})
            color {VBase4} -- The color of the planes/cards (default: {VBase4(1, 1, 1, 1)})
        """
        frame = frame or [-1, -1, 1, 1]
        card = CardMaker("plane")
        card.set_color(color)
        card.set_frame(frame[0], frame[1], frame[2], frame[3])
        n = NodePath()
        self.plane = n.attach_new_node(card.generate())
        self.plane.reparentTo(self.render)
        self.plane.setHpr(0, 270, 0)

    def setLight(self, color=VBase4(1, 1, 1, 1)):
        """Sets an ambient and omnidirectional light for rendering
        Keyword Arguments:
            color {VBase4} -- color of the ambient light (default: {VBase4(1, 1, 1, 1)})
        """

        alight = AmbientLight('alight')
        alight.setColor(color)
        alnp = self.render.attachNewNode(alight)
        self.render.setLight(alnp)

    def addButtons(self):
        self.cam_btn = DirectButton(
            text="Camera",
            scale=0.05,
            command=self.switchCamera,
            pos=Vec3(0.9, 0, 0.9))
        self.per_btn = DirectButton(
            text="View",
            scale=0.05,
            command=self.switchView,
            pos=Vec3(0.9, 0, 0.85))

    def camIndex(self,follow_id):
        return np.minimum(follow_id, 0)

    def switchCamera(self):
        # Switches between global and agent cameras
        self.follow_agent_id = self.camera_list.pop(0)
        self.camera_list.append(self.follow_agent_id)
        self.perspective = self.perspectives[self.camIndex(
            self.follow_agent_id)].copy()
        self.line_thickness = self.line_thicknesses[self.camIndex(
            self.follow_agent_id)][0]

    def switchView(self):
        # Switches between the perspectives defined by the camera
        self.perspective.append(self.perspective[0])
        self.perspective.pop(0)

    def updateCamera(self, lookAt=False):
        """Updates the camera position from calls using variables self.cam_pose and self.cam_or

        Keyword Arguments:
            lookAt {bool} -- If true the orientation is calculated by the agents position (default: {False})
        """

        self.camera.setPos(self.cam_pose[0], self.cam_pose[1],
                           self.cam_pose[2])
        if lookAt:
            self.camera.lookAt(self.agent_nodes[self.follow_agent_id])
        else:
            self.camera.setHpr(self.cam_or[2], self.cam_or[1], self.cam_or[0])

    def spinCameraTask(self, task):
        """This function sets and updates the camera according to environment variables and parameters

        Arguments:
            task {task} -- Panda3d Task

        Returns:
            Task.cont -- Panda3d Task Return
        """
        if self.follow_agent_id is -1:  # Transition into camera control view
            self.setMouseControl()
        if self.follow_agent_id is -2:  # Global Camera
            if (self.perspective[0] is self.perspectives[-2][1]):
                self.setAutoZoomCam(self.agent_poses)
        if self.follow_agent_id >= 0:  # Camera for all agents
            self.setAgentCam(self.perspective[0],
                             self.agent_poses[self.follow_agent_id])
        return Task.cont
    def initCam(self, poses=None, orientation=None):#TODO Calculate from map parameter before
        poses = poses or [0, 0, 700]
        orientation = orientation or [0, 270, 0]
        self.cam_pose = np.array(poses, dtype=float)
        self.cam_or = np.array(orientation, dtype=float)

    def setMouseControl(self):
        # Enables Panda3d Standard Mouse Control
        # Right mouse button: zoom, left mouse Button: move, middle mouse button: tilt
        self.initCam()
        self.updateCamera(lookAt=False)
        self.mat = Mat4(self.camera.getMat())
        self.mat.invertInPlace()
        base.mouseInterfaceNode.setMat(self.mat)
        base.enableMouse()
        self.follow_agent_id = -2
        self.perspective = self.perspectives[self.camIndex(
            self.follow_agent_id)].copy()

    def setAutoZoomCam(self, agent_poses):
        """This function calculates the camera position so that all agents are visible

        Arguments:
            agent_poses {[dict]} -- [look up table of all agent poses]
        """
        points = np.array([[]], dtype=float)
        if ((self.world_x_range is not None)
                and (self.world_y_range is not None) and self.range_for_zoom):
            points = np.array([[self.world_x_range[0], self.world_y_range[0]],
                               [self.world_x_range[1], self.world_y_range[1]]])
        # Build concat matrix of all positions
        for _, agent in agent_poses.items():
            if points.size is 0:
                points = np.array([[agent[0], agent[1]]], dtype=float)
            else:
                points = np.append(points, [[agent[0], agent[1]]], axis=0)
        # Calculate maximum distance between min,max of x or y poses
        max_dist = np.max(np.max(points, axis=0) - np.min(points, axis=0))
        # Calculate mean positions as center for camera
        center = (np.max(points, axis=0) + np.min(points, axis=0)) / 2
        # Lower bound for vieweable area
        max_dist = np.maximum(max_dist * 1.25, 40)
        # Calculate camera height
        zoom = max_dist * np.tan(pi * (60 / 180))
        self.cam_pose = np.array([center[0], center[1], zoom], dtype=float)
        self.cam_or = np.array([0, 270, 0], dtype=float)
        self.updateCamera(lookAt=False)

    def setAgentCam(self, perspective, agent_poses):
        """Sets up the class variable for the camerea position and orientation

        Arguments:
            perspective {list} -- List of strings describing the perspective currently ["bird_agent","third","first"] which is used as an index for the parameters dict
            agent_poses {np.array([x,y,theta])} -- Describes the agent position
        """

        base.disableMouse()
        self.cam_pose[0] = agent_poses[0] + self.agent_cam_parameter[
            perspective][0] * np.cos(agent_poses[2])
        self.cam_pose[1] = agent_poses[1] + self.agent_cam_parameter[
            perspective][0] * np.sin(agent_poses[2])
        self.cam_pose[2] = self.agent_cam_parameter[perspective][1]
        self.cam_or = np.array([
            self.agent_cam_parameter[perspective][2],
            self.agent_cam_parameter[perspective][3],
            (agent_poses[2] * 180) / pi +
            self.agent_cam_parameter[perspective][4]
        ],
                               dtype=float)
        self.updateCamera(lookAt=self.agent_cam_parameter[perspective][5])

    def calcLineThickness(self,cameras=None):
        """Uses a linear approximation between two cameras to calculate the line thickness

        Arguments:
            cameras {int array} -- Sets the indices of the target cameras
        Returns:
            [float] -- [The approximated thickness]
        """
        cameras = cameras or [-1,0]
        incline = (self.line_thicknesses[cameras[0]][0] - self.line_thicknesses[cameras[1]][0]) / (self.line_thicknesses[cameras[0]][1]-self.line_thicknesses[cameras[1]][1])
        return np.maximum((self.line_thicknesses[cameras[1]][0] + incline * (self.camera.getZ() - self.line_thicknesses[cameras[1]][1]) ), 0.01)

    def drawPolygon2d(self, polygon, color, alpha):
        # Draws a polygon with drawLine2d
        points = polygon.toArray()
        self.line_thickness = self.calcLineThickness()
        for point in points:
            self.generator.link_segment(
                Vec3(point[0], point[1], 2), Vec4(0, 0, 1, 1),
                self.line_thickness, self.getColor(color))
        self.generator.link_segment_end(Vec4(0, 0, 1, 1), self.getColor(color))


    def drawLine2d(self, line2d, color=Viewer.Color.Blue, alpha=1.0):
        line2d_np = line2d.toArray()
        for point in line2d_np:
            self.generator.link_segment(
                Vec3(point[0], point[1], 2), Vec4(0, 0, 1, 1),
                self.line_thickness, self.getColor(color))
        self.generator.link_segment_end(Vec4(0, 0, 1, 1), self.getColor(color))

    def drawAgent(self, agent):
        """Draws an agent object with a model previosly set in set with self.agent_model_path
        Arguments:
            agent -- Agent object from world
        """
        # Adds new agent to dict of Panda3d nodes
        if not agent.id in self.agent_nodes:
            self.agent_nodes[agent.id] = self.loader.loadModel(
                self.agent_model_path)
            self.agent_nodes[agent.id].reparentTo(self.render)
            self.agent_nodes[agent.id].setPos(0, 0, 2)
            self.agent_nodes[agent.id].setScale(
                self.agent_scale[0], self.agent_scale[1], self.agent_scale[2])
            self.agent_poses[agent.id] = np.array([0, 0, 0], dtype=float)
            self.camera_list.insert(len(self.camera_list), agent.id)

        # Set new pose of agent in world cordinates
        state = agent.followed_trajectory[-1, :]
        self.agent_poses[agent.id][0] = state[int(StateDefinition.X_POSITION)]
        self.agent_poses[agent.id][1] = state[int(StateDefinition.Y_POSITION)]
        self.agent_poses[agent.id][2] = state[int(
            StateDefinition.THETA_POSITION)]
        transformed_polygon = agent.shape.transform(self.agent_poses[agent.id])
        self.drawPolygon2d(transformed_polygon, self.color_agents, 1.0)

        # Fitting of 3d-Model frame to agent positon
        angle = ((self.agent_poses[agent.id][2] * 180) / pi +
                 self.agent_orientation[0])
        self.agent_nodes[agent.id].setHpr(angle, self.agent_orientation[1],
                                          self.agent_orientation[2])
        translation = self.agent_translation
        if not np.all(translation == 0):
            r = np.array([[
                np.cos(self.agent_poses[agent.id][2]),
                np.sin(self.agent_poses[agent.id][2])
            ],
            [
                np.cos(self.agent_poses[agent.id][2] + pi * 0.5),
                np.sin(self.agent_poses[agent.id][2] + pi * 0.5)
            ]],
                         dtype=float)
            translation = np.dot(translation, r)
        self.agent_nodes[agent.id].setPos(
            self.agent_poses[agent.id][0] + translation[0],
            self.agent_poses[agent.id][1] + translation[1], 2)

        if self.draw_route:
            self.drawRoute(agent)

    def getColor(self, color):
        if isinstance(color, Viewer.Color):
            return {
                Viewer.Color.White:
                Vec4(1, 1, 1, 1),
                Viewer.Color.Red:
                Vec4(1, 0, 0, 1),
                Viewer.Color.Blue:
                Vec4(0, 0, 1, 1),
                Viewer.Color.Magenta:
                Vec4(100.0 / 255.0, 1, 100.0 / 255.0, 1),
                Viewer.Color.Brown:
                Vec4(150.0 / 255.0, 50.0 / 255.0, 50.0 / 255.0, 1),
                Viewer.Color.Black:
                Vec4(0, 0, 0, 1)
            }.get(color, Vec4(0, 0, 0, 1))
        else:
            return Vec4(color[0], color[1], color[2], 1)

    def drawWorld(self, world):
        self.generator.begin(base.cam, self.render)
        super(Panda3dViewer, self).drawWorld(world)
        self.generator.end()
        self.taskMgr.step()
Exemplo n.º 4
0
class Line(DirectObject):
    """
    For drawing a line when picking target for attack
    """
    def __init__(self):
        self.meshDrawer = MeshDrawer()
        self.meshDrawer.setBudget(2)
        mdNode = self.meshDrawer.getRoot()
        mdNode.reparentTo(base.render)
        mdNode.setDepthWrite(False)
        mdNode.setTransparency(True)
        mdNode.setTwoSided(True)
        # mdNode.setTexture(loader.loadTexture("pixel.png"))
        mdNode.setBin("fixed", 0)
        mdNode.setLightOff(True)

    def draw(self, start, end):
        self.meshDrawer.begin(base.cam, render)
        self.meshDrawer.segment(
            Vec3(*start),
            Vec3(*end),
            frame=Vec4(0, 0, 1, 1),  # Use entire texture
            thickness=0.1,
            color=Vec4(0, 0, 0, 255))
        self.meshDrawer.end()

    def clear(self):
        self.meshDrawer.begin(base.cam, render)
        self.meshDrawer.end()
Exemplo n.º 5
0
    def __init__(self, shipSpec):
        GameObject.__init__(self, Vec3(0, 0,
                                       0), None, None, shipSpec.maxShields,
                            shipSpec.maxSpeed, "player", MASK_INTO_PLAYER, 2)
        ArmedObject.__init__(self)

        self.acceleration = shipSpec.acceleration
        self.turnRate = shipSpec.turnRate

        self.numGuns = len(shipSpec.gunPositions)
        self.numMissiles = shipSpec.numMissiles
        self.maxEnergy = shipSpec.maxEnergy
        self.energyRechargeRate = shipSpec.energyRechargeRate
        self.shieldRechargeRate = shipSpec.shieldRechargeRate

        self.energy = shipSpec.maxEnergy

        for gunPos in shipSpec.gunPositions:
            np = self.actor.attachNewNode(PandaNode("gun node"))
            np.setPos(gunPos)

            gun = BlasterWeapon()
            self.addWeapon(gun, 0, np)

        missileSetCounter = 1
        for missilePos in shipSpec.missilePositions:
            np = self.actor.attachNewNode(PandaNode("missile node"))
            np.setPos(missilePos)

            gun = RocketWeapon()
            self.addWeapon(gun, missileSetCounter, np)
            missileSetCounter += 1

        self.numMissileSets = missileSetCounter - 1
        self.missileSetIndex = 0

        light = PointLight("basic light")
        light.setColor(Vec4(1, 1, 1, 1))
        light.setAttenuation((1, 0.01, 0.001))
        self.lightNP = self.root.attachNewNode(light)
        self.lightNP.setZ(1)
        Common.framework.showBase.render.setLight(self.lightNP)

        self.colliderNP.node().setFromCollideMask(MASK_WALLS
                                                  | MASK_FROM_PLAYER)

        Common.framework.pusher.addCollider(self.colliderNP, self.root)
        Common.framework.traverser.addCollider(self.colliderNP,
                                               Common.framework.pusher)

        Common.framework.showBase.camera.reparentTo(self.actor)
        Common.framework.showBase.camera.setPos(0, 0, 0)
        Common.framework.showBase.camera.setHpr(0, 0, 0)

        lens = Common.framework.showBase.camLens

        lens.setNear(0.03)

        ratio = lens.getAspectRatio()

        lens.setFov(75 * ratio)

        self.lastMousePos = Vec2(0, 0)
        self.mouseSpeedHori = 50.0
        self.mouseSpeedVert = 30.0
        self.mouseSensitivity = 1.0

        self.targetingRay = CollisionSegment(0, 0, 0, 0, 100, 0)
        self.targetingRayNode = CollisionNode("lock ray")
        self.targetingRayNode.addSolid(self.targetingRay)
        self.targetingRayNode.setFromCollideMask(MASK_ENEMY_LOCK_SPHERE)
        self.targetingRayNode.setIntoCollideMask(0)
        self.targetingRayNP = self.actor.attachNewNode(self.targetingRayNode)
        self.targetingQueue = CollisionHandlerQueue()

        self.prospectiveLockTarget = None
        self.lockTargetTimer = 0
        self.lockDuration = 1

        Common.framework.traverser.addCollider(self.targetingRayNP,
                                               self.targetingQueue)

        #rayNodePath.show()

        self.uiRoot = aspect2d.attachNewNode(PandaNode("player UI"))

        cardMaker = CardMaker("UI maker")
        cardMaker.setFrame(-1, 1, -1, 1)

        self.centreSpot = self.uiRoot.attachNewNode(cardMaker.generate())
        self.centreSpot.setTexture(
            Common.framework.showBase.loader.loadTexture(
                "../Section2SpaceflightDocking/UI/spot.png"))
        self.centreSpot.setTransparency(True)
        self.centreSpot.setPos(0, 0, 0)
        self.centreSpot.setScale(0.01)
        self.centreSpot.setAlphaScale(0.5)

        self.directionIndicator = self.uiRoot.attachNewNode(
            cardMaker.generate())
        self.directionIndicator.setTexture(
            Common.framework.showBase.loader.loadTexture(
                "../Section2SpaceflightDocking/UI/directionIndicator.png"))
        self.directionIndicator.setTransparency(True)
        self.directionIndicator.setScale(0.05)
        self.directionIndicator.hide()

        self.lockMarkerRoot = self.uiRoot.attachNewNode(
            PandaNode("lock marker root"))
        for i in range(4):
            markerRotationNP = self.lockMarkerRoot.attachNewNode(
                PandaNode("lock marker rotation"))
            marker = markerRotationNP.attachNewNode(cardMaker.generate())
            marker.setTexture(
                Common.framework.showBase.loader.loadTexture(
                    "../Section2SpaceflightDocking/UI/lockMarker.png"))
            marker.setTransparency(True)
            markerRotationNP.setScale(0.04)
            markerRotationNP.setR(i * 90)
        self.lockMarkerRoot.hide()

        self.lockBar = Common.framework.showBase.loader.loadModel(
            "../Section2SpaceflightDocking/UI/uiLockBar")
        self.lockBar.reparentTo(self.uiRoot)
        self.lockBar.setScale(0.15)
        #self.lockBar.hide()

        cardMaker.setFrame(-1, 1, 0, 1)

        self.cockpit = Common.framework.showBase.loader.loadModel(
            "../Section2SpaceflightDocking/Models/{0}".format(
                shipSpec.cockpitModelFile))
        self.cockpit.reparentTo(self.actor)

        healthBarRoot = self.cockpit.find("**/healthBar")
        if healthBarRoot is None or healthBarRoot.isEmpty():
            healthBarRoot = self.uiRoot.attachNewNode(
                PandaNode("health bar root"))
            print("No health bar root found!")

        energyBarRoot = self.cockpit.find("**/energyBar")
        if energyBarRoot is None or energyBarRoot.isEmpty():
            energyBarRoot = self.uiRoot.attachNewNode(
                PandaNode("energy bar root"))
            print("No energy bar root found!")

        missileCounterRoot = self.cockpit.find("**/missileCounter")
        if missileCounterRoot is None or missileCounterRoot.isEmpty():
            missileCounterRoot = self.uiRoot.attachNewNode(
                PandaNode("missile counter root"))
            print("No missile counter root found!")

        radarRoot = self.cockpit.find("**/radar")
        if radarRoot is None or radarRoot.isEmpty():
            radarRoot = self.uiRoot.attachNewNode(PandaNode("radar root"))
            print("No radar root found!")

        speedometerRoot = self.cockpit.find("**/speedometer")
        if speedometerRoot is None or speedometerRoot.isEmpty():
            speedometerRoot = self.uiRoot.attachNewNode(
                PandaNode("speedometer root"))
            print("No speedometer root found!")

        self.radarDrawer = MeshDrawer()
        self.radarDrawer.setBudget(4096)

        self.radarDrawerNP = self.radarDrawer.getRoot()
        self.radarDrawerNP.reparentTo(radarRoot)
        self.radarDrawerNP.setTwoSided(True)
        self.radarDrawerNP.setLightOff()
        self.radarDrawerNP.setDepthWrite(False)
        self.radarDrawerNP.setTransparency(True)

        self.healthBar = healthBarRoot.attachNewNode(cardMaker.generate())
        self.healthBar.setSx(0.05)

        self.energyBar = energyBarRoot.attachNewNode(cardMaker.generate())
        self.energyBar.setSx(0.05)

        self.healthBarScalar = 0.00175
        self.energyBarScalar = 0.00175

        self.missileCounter = DirectLabel(text="",
                                          text_mayChange=True,
                                          scale=0.09,
                                          relief=None,
                                          parent=missileCounterRoot)

        self.maxRadarRange = 700
        self.radarSize = 0.3

        self.speedometer = DirectLabel(text="",
                                       text_mayChange=True,
                                       scale=0.09,
                                       relief=None,
                                       parent=speedometerRoot)

        self.updateHealthUI()
        self.updateEnergyUI()
        self.updateMissileUI()
        self.updateRadar()
        self.updateSpeedometer()

        self.updatingEffects = []
Exemplo n.º 6
0
class Player(GameObject, ArmedObject):
    def __init__(self, shipSpec):
        GameObject.__init__(self, Vec3(0, 0,
                                       0), None, None, shipSpec.maxShields,
                            shipSpec.maxSpeed, "player", MASK_INTO_PLAYER, 2)
        ArmedObject.__init__(self)

        self.acceleration = shipSpec.acceleration
        self.turnRate = shipSpec.turnRate

        self.numGuns = len(shipSpec.gunPositions)
        self.numMissiles = shipSpec.numMissiles
        self.maxEnergy = shipSpec.maxEnergy
        self.energyRechargeRate = shipSpec.energyRechargeRate
        self.shieldRechargeRate = shipSpec.shieldRechargeRate

        self.energy = shipSpec.maxEnergy

        for gunPos in shipSpec.gunPositions:
            np = self.actor.attachNewNode(PandaNode("gun node"))
            np.setPos(gunPos)

            gun = BlasterWeapon()
            self.addWeapon(gun, 0, np)

        missileSetCounter = 1
        for missilePos in shipSpec.missilePositions:
            np = self.actor.attachNewNode(PandaNode("missile node"))
            np.setPos(missilePos)

            gun = RocketWeapon()
            self.addWeapon(gun, missileSetCounter, np)
            missileSetCounter += 1

        self.numMissileSets = missileSetCounter - 1
        self.missileSetIndex = 0

        light = PointLight("basic light")
        light.setColor(Vec4(1, 1, 1, 1))
        light.setAttenuation((1, 0.01, 0.001))
        self.lightNP = self.root.attachNewNode(light)
        self.lightNP.setZ(1)
        Common.framework.showBase.render.setLight(self.lightNP)

        self.colliderNP.node().setFromCollideMask(MASK_WALLS
                                                  | MASK_FROM_PLAYER)

        Common.framework.pusher.addCollider(self.colliderNP, self.root)
        Common.framework.traverser.addCollider(self.colliderNP,
                                               Common.framework.pusher)

        Common.framework.showBase.camera.reparentTo(self.actor)
        Common.framework.showBase.camera.setPos(0, 0, 0)
        Common.framework.showBase.camera.setHpr(0, 0, 0)

        lens = Common.framework.showBase.camLens

        lens.setNear(0.03)

        ratio = lens.getAspectRatio()

        lens.setFov(75 * ratio)

        self.lastMousePos = Vec2(0, 0)
        self.mouseSpeedHori = 50.0
        self.mouseSpeedVert = 30.0
        self.mouseSensitivity = 1.0

        self.targetingRay = CollisionSegment(0, 0, 0, 0, 100, 0)
        self.targetingRayNode = CollisionNode("lock ray")
        self.targetingRayNode.addSolid(self.targetingRay)
        self.targetingRayNode.setFromCollideMask(MASK_ENEMY_LOCK_SPHERE)
        self.targetingRayNode.setIntoCollideMask(0)
        self.targetingRayNP = self.actor.attachNewNode(self.targetingRayNode)
        self.targetingQueue = CollisionHandlerQueue()

        self.prospectiveLockTarget = None
        self.lockTargetTimer = 0
        self.lockDuration = 1

        Common.framework.traverser.addCollider(self.targetingRayNP,
                                               self.targetingQueue)

        #rayNodePath.show()

        self.uiRoot = aspect2d.attachNewNode(PandaNode("player UI"))

        cardMaker = CardMaker("UI maker")
        cardMaker.setFrame(-1, 1, -1, 1)

        self.centreSpot = self.uiRoot.attachNewNode(cardMaker.generate())
        self.centreSpot.setTexture(
            Common.framework.showBase.loader.loadTexture(
                "../Section2SpaceflightDocking/UI/spot.png"))
        self.centreSpot.setTransparency(True)
        self.centreSpot.setPos(0, 0, 0)
        self.centreSpot.setScale(0.01)
        self.centreSpot.setAlphaScale(0.5)

        self.directionIndicator = self.uiRoot.attachNewNode(
            cardMaker.generate())
        self.directionIndicator.setTexture(
            Common.framework.showBase.loader.loadTexture(
                "../Section2SpaceflightDocking/UI/directionIndicator.png"))
        self.directionIndicator.setTransparency(True)
        self.directionIndicator.setScale(0.05)
        self.directionIndicator.hide()

        self.lockMarkerRoot = self.uiRoot.attachNewNode(
            PandaNode("lock marker root"))
        for i in range(4):
            markerRotationNP = self.lockMarkerRoot.attachNewNode(
                PandaNode("lock marker rotation"))
            marker = markerRotationNP.attachNewNode(cardMaker.generate())
            marker.setTexture(
                Common.framework.showBase.loader.loadTexture(
                    "../Section2SpaceflightDocking/UI/lockMarker.png"))
            marker.setTransparency(True)
            markerRotationNP.setScale(0.04)
            markerRotationNP.setR(i * 90)
        self.lockMarkerRoot.hide()

        self.lockBar = Common.framework.showBase.loader.loadModel(
            "../Section2SpaceflightDocking/UI/uiLockBar")
        self.lockBar.reparentTo(self.uiRoot)
        self.lockBar.setScale(0.15)
        #self.lockBar.hide()

        cardMaker.setFrame(-1, 1, 0, 1)

        self.cockpit = Common.framework.showBase.loader.loadModel(
            "../Section2SpaceflightDocking/Models/{0}".format(
                shipSpec.cockpitModelFile))
        self.cockpit.reparentTo(self.actor)

        healthBarRoot = self.cockpit.find("**/healthBar")
        if healthBarRoot is None or healthBarRoot.isEmpty():
            healthBarRoot = self.uiRoot.attachNewNode(
                PandaNode("health bar root"))
            print("No health bar root found!")

        energyBarRoot = self.cockpit.find("**/energyBar")
        if energyBarRoot is None or energyBarRoot.isEmpty():
            energyBarRoot = self.uiRoot.attachNewNode(
                PandaNode("energy bar root"))
            print("No energy bar root found!")

        missileCounterRoot = self.cockpit.find("**/missileCounter")
        if missileCounterRoot is None or missileCounterRoot.isEmpty():
            missileCounterRoot = self.uiRoot.attachNewNode(
                PandaNode("missile counter root"))
            print("No missile counter root found!")

        radarRoot = self.cockpit.find("**/radar")
        if radarRoot is None or radarRoot.isEmpty():
            radarRoot = self.uiRoot.attachNewNode(PandaNode("radar root"))
            print("No radar root found!")

        speedometerRoot = self.cockpit.find("**/speedometer")
        if speedometerRoot is None or speedometerRoot.isEmpty():
            speedometerRoot = self.uiRoot.attachNewNode(
                PandaNode("speedometer root"))
            print("No speedometer root found!")

        self.radarDrawer = MeshDrawer()
        self.radarDrawer.setBudget(4096)

        self.radarDrawerNP = self.radarDrawer.getRoot()
        self.radarDrawerNP.reparentTo(radarRoot)
        self.radarDrawerNP.setTwoSided(True)
        self.radarDrawerNP.setLightOff()
        self.radarDrawerNP.setDepthWrite(False)
        self.radarDrawerNP.setTransparency(True)

        self.healthBar = healthBarRoot.attachNewNode(cardMaker.generate())
        self.healthBar.setSx(0.05)

        self.energyBar = energyBarRoot.attachNewNode(cardMaker.generate())
        self.energyBar.setSx(0.05)

        self.healthBarScalar = 0.00175
        self.energyBarScalar = 0.00175

        self.missileCounter = DirectLabel(text="",
                                          text_mayChange=True,
                                          scale=0.09,
                                          relief=None,
                                          parent=missileCounterRoot)

        self.maxRadarRange = 700
        self.radarSize = 0.3

        self.speedometer = DirectLabel(text="",
                                       text_mayChange=True,
                                       scale=0.09,
                                       relief=None,
                                       parent=speedometerRoot)

        self.updateHealthUI()
        self.updateEnergyUI()
        self.updateMissileUI()
        self.updateRadar()
        self.updateSpeedometer()

        self.updatingEffects = []

    def update(self, keys, dt):
        GameObject.update(self, dt)

        self.updateSpeedometer()

        self.walking = False

        quat = self.root.getQuat(Common.framework.showBase.render)
        forward = quat.getForward()
        right = quat.getRight()
        up = quat.getUp()

        if keys["up"]:
            self.walking = True
            self.velocity += forward * self.acceleration * dt
        if keys["down"]:
            self.walking = True
            self.velocity -= forward * self.acceleration * dt
        if keys["left"]:
            self.walking = True
            self.velocity -= right * self.acceleration * dt
        if keys["right"]:
            self.walking = True
            self.velocity += right * self.acceleration * dt
        if self.walking:
            self.inControl = True

        mouseWatcher = base.mouseWatcherNode
        if mouseWatcher.hasMouse():
            xSize = base.win.getXSize()
            ySize = base.win.getYSize()
            xPix = float(xSize % 2) / xSize
            yPix = float(ySize % 2) / ySize
            mousePos = Vec2(base.mouseWatcherNode.getMouse())
            mousePos.addX(-xPix)
            mousePos.addY(-yPix)
            if abs(mousePos.x) < xPix:
                mousePos.x = 0
            if abs(mousePos.y) < yPix:
                mousePos.y = 0

        else:
            mousePos = self.lastMousePos

        if mousePos.length() > 0.01:
            axis = right * (mousePos.y) + up * (-mousePos.x)
            axis.normalize()
            angle = mousePos.length() * self.turnRate * dt

            rotQuat = Quat()
            rotQuat.setFromAxisAngle(angle, axis)

            self.root.setQuat(quat * rotQuat)

        if not self.weaponSets[0][0].active:
            self.alterEnergy(
                math.sin(1.071 * self.energy / self.maxEnergy + 0.5) *
                self.energyRechargeRate * dt)

        self.updateEnergyUI()
        self.updateHealthUI()
        self.updateRadar()

        #self.root.setH(self.root.getH() - mousePos.x*self.mouseSpeedHori*self.mouseSensitivity)
        #self.actor.setP(self.actor.getP() + mousePos.y*self.mouseSpeedVert*self.mouseSensitivity)

        if keys["shoot"]:
            self.startFiringSet(0)
        else:
            self.ceaseFiringSet(0)

        if keys["shootSecondary"]:
            self.startFiringSet(self.missileSetIndex + 1)
        else:
            for i in range(self.numMissileSets):
                self.ceaseFiringSet(i + 1)

        [effect.update(self, dt) for effect in self.updatingEffects]
        [
            effect.cleanup() for effect in self.updatingEffects
            if not effect.active
        ]
        self.updatingEffects = [
            effect for effect in self.updatingEffects if effect.active
        ]

        if self.targetingQueue.getNumEntries() > 0:
            self.targetingQueue.sortEntries()
            entry = self.targetingQueue.getEntry(0)
            intoNP = entry.getIntoNodePath()
            if intoNP.hasPythonTag(TAG_OWNER):
                other = intoNP.getPythonTag(TAG_OWNER)
                if other is self.prospectiveLockTarget and other is not self.lockedTarget:
                    self.lockTargetTimer += dt
                    if self.lockTargetTimer >= self.lockDuration:
                        self.lockedTarget = other
                else:
                    self.lockTargetTimer = 0
                self.prospectiveLockTarget = other
            else:
                self.lockTargetTimer = 0
        else:
            self.lockTargetTimer = 0

        perc = self.lockTargetTimer / self.lockDuration
        self.lockBar.setTexOffset(TextureStage.getDefault(), 0, -perc * 1.1)

        if self.lockedTarget is not None:
            if self.lockedTarget.health <= 0:
                self.lockedTarget = None
            else:
                relPos = self.lockedTarget.root.getPos(self.root)
                planarVec = relPos.getXz()
                relDist = relPos.length()

                if relDist == 0:
                    angle = 0
                else:
                    angle = math.acos(relPos.y / relDist)

                if relDist > 200 or angle > 1.7453:
                    self.lockedTarget = None
                else:

                    if self.lockMarkerRoot.isHidden():
                        self.lockMarkerRoot.show()

                    camPt = Point2()
                    convertedPt = Common.framework.showBase.cam.getRelativePoint(
                        Common.framework.showBase.render,
                        self.lockedTarget.root.getPos(
                            Common.framework.showBase.render))
                    if Common.framework.showBase.camLens.project(
                            convertedPt, camPt):
                        self.lockMarkerRoot.setPos(
                            Common.framework.showBase.render2d, camPt.x, 0,
                            camPt.y)
                        if self.lockMarkerRoot.isHidden():
                            self.lockMarkerRoot.show()
                        for child in self.lockMarkerRoot.getChildren():
                            child.getChild(0).setZ(
                                (1.0 - min(1, relDist / 100)) * 5 + 0.2)
                    elif not self.lockMarkerRoot.isHidden():
                        self.lockMarkerRoot.hide()

                    if relPos.y < 0 or angle > 0.6:
                        planarVec.normalize()

                        self.directionIndicator.setPos(planarVec.x * 0.4, 0,
                                                       planarVec.y * 0.4)

                        angle = math.degrees(
                            math.atan2(planarVec.x, planarVec.y))
                        self.directionIndicator.setR(angle)

                        if self.directionIndicator.isHidden():
                            self.directionIndicator.show()
                    elif not self.directionIndicator.isHidden():
                        self.directionIndicator.hide()
        else:
            if not self.directionIndicator.isHidden():
                self.directionIndicator.hide()
            if not self.lockMarkerRoot.isHidden():
                self.lockMarkerRoot.hide()

    def weaponReset(self, weapon):
        ArmedObject.weaponFired(self, weapon)

        if isinstance(weapon, RocketWeapon):
            self.ceaseFiringSet(self.missileSetIndex + 1)
            self.missileSetIndex += 1
            if self.missileSetIndex >= self.numMissileSets:
                self.missileSetIndex = 0

    def attackPerformed(self, weapon):
        ArmedObject.attackPerformed(self, weapon)

    def postTraversalUpdate(self, dt):
        ArmedObject.update(self, dt)

    def alterHealth(self,
                    dHealth,
                    incomingImpulse,
                    knockback,
                    flinchValue,
                    overcharge=False):
        GameObject.alterHealth(self, dHealth, incomingImpulse, knockback,
                               flinchValue, overcharge)

        self.updateHealthUI()

        #self.hurtSound.play()

    def alterEnergy(self, dEnergy):
        self.energy += dEnergy
        if self.energy < 0:
            self.energy = 0
        elif self.energy > self.maxEnergy:
            self.energy = self.maxEnergy

    def alterMissileCount(self, dMissiles):
        self.numMissiles += dMissiles
        if self.numMissiles < 0:
            self.numMissiles = 0
        self.updateMissileUI()

    def updateHealthUI(self):
        perc = self.health / self.maxHealth
        newVal = max(0.01, self.health * self.healthBarScalar)
        self.healthBar.setSz(newVal)
        self.healthBar.setColorScale(1.0 - (perc - 0.5) / 0.5,
                                     min(1, perc / 0.5), 0, 1)
        #self.healthCounter.setText("{0:-.0f}".format(self.health))
        #self.healthCounter.setColorScale(1.0 - (perc - 0.5)/0.5, min(1, perc/0.5), 0, 1)

    def updateEnergyUI(self):
        perc = self.energy / self.maxEnergy
        newVal = max(0.01, self.energy * self.energyBarScalar)
        self.energyBar.setSz(newVal)
        self.energyBar.setColorScale(1.0 - (perc - 0.5) / 0.5,
                                     min(1, perc / 0.5), 0, 1)

    def updateMissileUI(self):
        self.missileCounter["text"] = "Missiles:\n{0}".format(self.numMissiles)
        self.missileCounter.setText()
        self.missileCounter.resetFrameSize()

    def updateSpeedometer(self):
        self.speedometer["text"] = "Speed:\n{0:0=2.0f}m/s".format(
            self.velocity.length() * 2)
        self.speedometer.setText()
        self.speedometer.resetFrameSize()

    def updateRadar(self):
        if Common.framework.currentLevel is not None:
            self.radarDrawer.begin(Common.framework.showBase.cam,
                                   Common.framework.showBase.render)

            uvs = Vec2(0, 0)

            spotSize = 0.015

            self.radarDrawer.tri(Vec3(-spotSize, 0,
                                      -spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(spotSize, 0,
                                      -spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(-spotSize, 0, spotSize),
                                 Vec4(0, 1, 0, 1), uvs)
            self.radarDrawer.tri(Vec3(-spotSize, 0,
                                      spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(spotSize, 0,
                                      -spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(spotSize, 0, spotSize), Vec4(0, 1, 0, 1),
                                 uvs)

            selfForward = Vec3(0, 1, 0)

            for enemy in Common.framework.currentLevel.enemies:
                enemyPos = enemy.root.getPos(self.root)
                dist = enemyPos.length()
                if dist < self.maxRadarRange:
                    distPerc = dist / self.maxRadarRange
                    enemyPos.normalize()
                    anglePerc = selfForward.angleDeg(enemyPos) / 180
                    enemyPos.setY(0)
                    enemyPos.normalize()
                    enemyPos *= anglePerc * self.radarSize
                    colour = Vec4(1, 0, 0,
                                  math.sin(max(0, 1 - distPerc) * 1.571))

                    self.radarDrawer.tri(
                        Vec3(-spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(0, 0, spotSize) + enemyPos, colour, uvs)
                    self.radarDrawer.tri(
                        Vec3(spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(-spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(0, 0, -spotSize) + enemyPos, colour, uvs)

            self.radarDrawer.end()

    def addUpdatingEffect(self, effect):
        self.updatingEffects.append(effect)
        effect.start(self)

    def cleanup(self):
        if self.uiRoot is not None:
            self.uiRoot.removeNode()
            self.uiRoot = None
        self.healthBar = None

        if self.lightNP is not None:
            Common.framework.showBase.render.clearLight(self.lightNP)
            self.lightNP.removeNode()
            self.lightNP = None

        for effect in self.updatingEffects:
            effect.cleanup()
        self.updatingEffects = []

        ArmedObject.cleanup(self)
        GameObject.cleanup(self)