Exemple #1
0
class ToontownShaderManager(DirectFrame):
    def __init__(self, parent):
        self._parent = parent
        DirectFrame.__init__(self,
                             parent=self._parent,
                             relief=None,
                             pos=(0.0, 0.0, 0.0),
                             scale=(1.0, 1.0, 1.0))
        self.filter = CommonFilters(base.win,
                                    base.cam)  # Only affects primary window

        # Ambient Occlusion
        self.samples = 0
        self.radius = 0.0
        self.amount = 0.0
        self.strength = 0.0

        # Blur/Sharpen
        self.blur = 1.0  # this is normal value, 0.0 blurs it

        # Cartoon Ink
        self.cartoonSep = 0.0
        self.cartoonR = 0.0
        self.cartoonB = 0.0
        self.cartoonG = 0.0

        # todo: Add bloom

        # Boolean Filters
        self.HDREnabled = False
        self.invertedEnabled = False
        self.sRGBEnabled = False
        self.halfPixelShiftEnabled = False
        self.viewGlowEnabled = False

        # Other Filters
        self.exposure = 0.0

        self.SAMPLES_MAX = 128
        self.RAD_MAX = 1.0
        self.AMT_MAX = 64.0
        self.STR_MAX = 0.01
        self.INCREMENT = 1
        self.numSamples = None
        self.numRadius = None

        self.circleModel = loader.loadModel(
            'phase_3/models/gui/tt_m_gui_mat_nameShop')
        self.barTexture = loader.loadTexture('phase_3/maps/slider.png')
        self.loadGUI()
        # self.newWindow() # Disabled for now

    def loadGUI(self):
        self.textRowHeight = 0.2
        self.buttonbase_xcoord = 1.4
        self.buttonbase_ycoord = 0.45
        self.loadAmbientOcclusionGUI()
        self.loadBlurGUI()
        self.loadExposureGUI()
        self.loadCartoonInkGUI()
        self.loadHotkeys()

    def loadHotkeys(self):
        # for now instead of gui buttons i'ma just put the bool filters as hotkeys
        self.accept('4', self.__toggleHDR)
        self.accept('5', self.__toggleInverted)
        self.accept('6', self.__toggleSRGB)
        self.accept('7', self.__toggleHalfPixelShift)
        self.accept('8', self.__toggleViewGlow)

    def loadAmbientOcclusionGUI(self):
        self.numSamples = DirectSlider(
            parent=self,
            value=self.samples,
            pos=(self.buttonbase_xcoord + 0.1, 0.0,
                 self.buttonbase_ycoord - self.textRowHeight * 3.5),
            thumb_relief=None,
            range=(0, 32),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeAOValue)
        # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength))
        self.numSamples.setScale(0.5)
        self.numSamples.setTransparency(True)
        self.numSamplesText = OnscreenText(
            pos=(self.buttonbase_xcoord + 0.1,
                 self.buttonbase_ycoord - self.textRowHeight * 3.2),
            scale=0.05,
            text="AO Sample Count = {}".format(self.samples),
            style=5,
            mayChange=True)

        self.numRadius = DirectSlider(
            parent=self,
            value=self.radius,
            pos=(self.buttonbase_xcoord + 0.1, 0.0,
                 (self.buttonbase_ycoord - self.textRowHeight * 4.5)),
            thumb_relief=None,
            range=(0, 1),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeAOValue)

        self.numRadius.setScale(0.5)
        self.numRadius.setTransparency(True)
        self.numRadiusText = OnscreenText(
            pos=(self.buttonbase_xcoord + 0.1,
                 self.buttonbase_ycoord - self.textRowHeight * 4.2),
            scale=0.05,
            text="AO Radius = {}".format(str(self.radius)),
            style=5,
            mayChange=True)

        self.numAmount = DirectSlider(
            parent=self,
            value=self.amount,
            pos=(self.buttonbase_xcoord + 0.1, 0.0,
                 (self.buttonbase_ycoord - self.textRowHeight * 5.5)),
            thumb_relief=None,
            range=(0, 64),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeAOValue)

        self.numAmount.setScale(0.5)
        self.numAmount.setTransparency(True)
        self.numAmountText = OnscreenText(
            pos=(self.buttonbase_xcoord + 0.1,
                 self.buttonbase_ycoord - self.textRowHeight * 5.2),
            scale=0.05,
            text="AO Amount = {}".format(self.amount),
            style=5,
            mayChange=True)

        self.numStrength = DirectSlider(
            parent=self,
            value=self.strength,
            pos=(self.buttonbase_xcoord + 0.1, 0.0,
                 (self.buttonbase_ycoord - self.textRowHeight * 6.5)),
            thumb_relief=None,
            range=(0, 0.1),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeAOValue)

        self.numStrength.setScale(0.5)
        self.numStrength.setTransparency(True)
        self.numStrengthText = OnscreenText(
            pos=(self.buttonbase_xcoord + 0.1,
                 self.buttonbase_ycoord - self.textRowHeight * 6.2),
            scale=0.05,
            text="AO Strength = {}".format(self.strength),
            style=5,
            mayChange=True)

    def loadCartoonInkGUI(self):
        self.cSep = DirectSlider(
            parent=self,
            value=self.cartoonSep,
            pos=(-self.buttonbase_xcoord + 0.1, 0.0,
                 self.buttonbase_ycoord - self.textRowHeight * 2.5),
            thumb_relief=None,
            range=(0, 32),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeCartoon)
        # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength))
        self.cSep.setScale(0.5)
        self.cSep.setTransparency(True)

        self.cRed = DirectSlider(
            parent=self,
            value=self.cartoonR,
            pos=(-self.buttonbase_xcoord + 0.1, 0.0,
                 self.buttonbase_ycoord - self.textRowHeight * 3.5),
            thumb_relief=None,
            range=(0, 1),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeCartoon)
        # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength))
        self.cRed.setScale(0.5)
        self.cRed.setTransparency(True)

        self.cBlue = DirectSlider(
            parent=self,
            value=self.cartoonB,
            pos=(-self.buttonbase_xcoord + 0.1, 0.0,
                 self.buttonbase_ycoord - self.textRowHeight * 4.5),
            thumb_relief=None,
            range=(0, 1),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeCartoon)
        # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength))
        self.cBlue.setScale(0.5)
        self.cBlue.setTransparency(True)

        self.cGreen = DirectSlider(
            parent=self,
            value=self.cartoonG,
            pos=(-self.buttonbase_xcoord + 0.1, 0.0,
                 self.buttonbase_ycoord - self.textRowHeight * 5.5),
            thumb_relief=None,
            range=(0, 1),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeCartoon)
        # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength))
        self.cGreen.setScale(0.5)
        self.cGreen.setTransparency(True)

    def loadBlurGUI(self):
        self.numBlur = DirectSlider(
            parent=self,
            value=self.blur,
            # pos=(0.0, 0.0, 0.0), # for new window
            pos=(self.buttonbase_xcoord + 0.1, 0.0,
                 (self.buttonbase_ycoord - self.textRowHeight * 0.5)),
            thumb_relief=None,
            range=(-10, 10),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeBlur)
        self.numBlur.setScale(0.5)
        self.numBlur.setTransparency(True)
        self.numBlurText = OnscreenText(
            pos=(self.buttonbase_xcoord + 0.1,
                 self.buttonbase_ycoord - self.textRowHeight * 0.2),
            scale=0.05,
            text="Blur Amount = {}".format(self.blur),
            style=5,
            mayChange=True)

    def loadExposureGUI(self):
        self.numExposure = DirectSlider(
            parent=self,
            value=self.exposure,
            pos=(self.buttonbase_xcoord + 0.1, 0.0,
                 (self.buttonbase_ycoord - self.textRowHeight * 2.2)),
            thumb_relief=None,
            range=(0, 5),  # self.SAMPLES_MAX
            thumb_geom=self.circleModel.find(
                '**/tt_t_gui_mat_namePanelCircle'),
            frameTexture=self.barTexture,
            frameSize=(-0.5, 0.5, -0.08, 0.08),
            command=self.__changeExposure)
        self.numExposure.setScale(0.5)
        self.numExposure.setTransparency(True)
        self.numExposureText = OnscreenText(
            pos=(self.buttonbase_xcoord + 0.1,
                 self.buttonbase_ycoord - self.textRowHeight * 1.9),
            scale=0.05,
            text="Exposure Amount = {}".format(self.blur),
            style=5,
            mayChange=True)

    def printValue(self):
        print(self.numSamples['value'])

    def __changeCartoon(self):
        self.filter.delCartoonInk()
        s = self.cSep['value']
        self.cartoonSep = s
        r = self.cRed['value']
        self.cartoonR = r
        b = self.cBlue['value']
        self.cartoonB = b
        g = self.cGreen['value']
        self.cartoonG = g
        self.filter.setCartoonInk(s, (r, g, b, 1))  # a doesn't change

    def __changeBlur(self):
        self.filter.delBlurSharpen()
        b = self.numBlur['value']
        self.blur = b
        self.numBlurText.setText("Blur amount = {}".format(b))
        self.filter.setBlurSharpen(b)

    def __changeAOValue(self):
        if self.numSamples is None:
            print("NONE")
            return
        self.filter.delAmbientOcclusion()

        s = self.numSamples['value']
        self.samples = s
        self.numSamplesText.setText("AO Sample Count = {}".format(s))
        if (s == 0):
            return

        r = self.numRadius['value']
        self.radius = r
        self.numRadiusText.setText("AO Radius = {}".format(r))
        if (r == 0):
            return

        a = self.numAmount['value']
        self.amount = a
        self.numAmountText.setText("AO Amount = {}".format(a))
        if (a == 0):
            return

        st = self.numStrength['value']
        self.strength = st
        self.numStrengthText.setText("AO Strength = {}".format(st))
        if (st == 0):
            return

        self.filter.setAmbientOcclusion(numsamples=s,
                                        radius=r,
                                        amount=a,
                                        strength=st)
        print(
            "sample count: {} | radius count: {} | amount count: {} | strength count: {}"
            .format(self.samples, self.radius, self.amount, self.strength))

    # WARNING: Won't work with relatively older Panda versions because this is new
    def __changeExposure(self):
        self.filter.delExposureAdjust()
        e = self.numExposure['value']
        self.exposure = e
        self.numExposureText.setText("Exposure amount = {}".format(e))
        self.filter.setExposureAdjust(e)

    def __toggleInverted(self):
        if not self.invertedEnabled:
            self.filter.setInverted()
            self.invertedEnabled = True
        else:
            self.filter.delInverted()
            self.invertedEnabled = False

    def __toggleHDR(self):
        if not self.HDREnabled:
            self.filter.setHighDynamicRange()
            self.HDREnabled = True
        else:
            self.filter.delHighDynamicRange()
            self.HDREnabled = False

    def __toggleSRGB(self):
        if not self.sRGBEnabled:
            self.filter.setSrgbEncode(True)
            self.sRGBEnabled = True
        else:
            self.filter.delSrgbEncode()
            self.sRGBEnabled = False

    def __toggleHalfPixelShift(self):
        if not self.halfPixelShiftEnabled:
            self.filter.setHalfPixelShift()
            self.halfPixelShiftEnabled = True
        else:
            self.filter.delHalfPixelShift()
            self.halfPixelShiftEnabled = False

    def __toggleViewGlow(self):
        if not self.viewGlowEnabled:
            self.filter.setViewGlow()
            self.viewGlowEnabled = True
        else:
            self.filter.delViewGlow()
            self.viewGlowEnabled = False

    def getValue(self, item, item_MAX):
        return item / item_MAX

    # Not gonna use this feature since it makes it very laggy. I will have to figure out
    # a better way to layout UI some other time.
    # https://discourse.panda3d.org/t/how-to-open-a-new-window/23929/4
    def newWindow(self):
        self.wp = WindowProperties()
        self.wp.setSize(700, 500)
        self.wp.setRawMice(True)
        print(self.wp.getMouseMode())
        win2mouseWatcher = MouseWatcher()
        ar = 1
        self.win2 = base.openWindow(props=self.wp, aspectRatio=ar)
        self.window2render2d = NodePath('window2render2d')
        self.window2render2d.setDepthTest(0)
        self.window2render2d.setDepthWrite(0)

        self.window2camera2d = base.makeCamera2d(self.win2)
        self.window2camera2d.reparentTo(self.window2render2d)

        # Parent gui to this
        self.window2aspect2d = self.window2render2d.attachNewNode(
            PGTop('window2aspect2d'))
        self.window2aspect2d.setScale(1.0 / ar, 1.0, 1.0)

        name = self.win2.getInputDeviceName(0)
        mk = base.dataRoot.attachNewNode(MouseAndKeyboard(self.win2, 0, name))
        mw = mk.attachNewNode(MouseWatcher(name))
        self.window2aspect2d.node().setMouseWatcher(mw.node())
Exemple #2
0
class Application(ShowBase):

	def __init__(self):
		ShowBase.__init__(self)

		self.useAdvancedVisualEffects =\
			ConfigVariableBool("use-advanced-visual-effects", True) and\
			base.win.getGsg().getSupportsBasicShaders() and\
			base.win.getGsg().getSupportsGlsl() and\
			base.win.getGsg().getSupportsDepthTexture()

		self.game = Game(scriptPath)

		self.phoneState = PhoneState(self)
		self.setupFilters()
		self.setupModels()
		self.setupLighting()
		self.setupKeyboardControl()
		self.camera.setPos(0.0, 0.0, 1.7)
		self.setupMouseControl()
		self.phoneState.request("Hidden")
		blockSize = 10.0
		self.maxX = self.game.getCityBlueprint().getSizeWE() * blockSize / 2.0
		self.maxY = self.game.getCityBlueprint().getSizeNS() * blockSize / 2.0
		self.setBackgroundColor(0.2, 0.4, 1.0, 1.0)

	def setupFilters(self):
		if (self.useAdvancedVisualEffects):
			self.filters = CommonFilters(self.win, self.cam)
			self.filters.setBloom()

	def setupKeyboardControl(self):
		self.accept("escape", sys.exit)

	def setupMouseControl(self):
		self.disableMouse()

		self.mousex = 0
		self.mousey = 0
		self.last = 0
		self.mousebtn = [0,0,0]

		self.accept("mouse1", self.setMouseBtn, [0, 1])
		self.accept("mouse1-up", self.setMouseBtn, [0, 0])

		self.taskMgr.add(self.controlCamera, "cameraTask")

	def setupLights(self):
		self.sunLight = self.render.attachNewNode(DirectionalLight("sunLight"))
		self.sunLight.setColor(Vec4(0.8, 0.8, 0.8, 1))
		self.sunLight.node().getLens().setFilmSize(128, 64)
		self.sunLight.node().getLens().setNearFar(20,2000)
		self.sunLight.setPos(60, 30, 50)
		self.sunLight.lookAt(0, 0, 0)
		self.render.setLight(self.sunLight)
#		self.sunLight.node().showFrustum()
		if self.useAdvancedVisualEffects:
			self.sunLight.node().setShadowCaster(True, 256, 256)
			self.render.setShaderAuto()

		self.ambientLight = self.render.attachNewNode(AmbientLight("ambientLight"))
		self.ambientLight.node().setColor(Vec4(0.2, 0.2, 0.2, 1))
		self.render.setLight(self.ambientLight)

	def setupModels(self):
		self.city = self.loader.loadModel("models/city")
		self.city.reparentTo(self.render)
		self.phoneState.setupPhone()
		self.cityOutline = self.loader.loadModel("models/city_outline")
		self.cityOutline.reparentTo(self.phoneState.minimap)

	def setupLighting(self):
		self.ambientLight = self.render.attachNewNode(AmbientLight("ambientLight"))
		self.ambientLight.node().setColor(Vec4(1, 1, 1, 1))
		self.render.setLight(self.ambientLight)

	def setMouseBtn(self, btn, value):
		self.mousebtn[btn] = value

		if (btn == 0 and value == 1 and self.phoneState.state == "Center"):
			phoneDisplayRegionCenterX = self.win.getXSize() * (self.phoneState.phoneDisplayRegion.getLeft() + self.phoneState.phoneDisplayRegion.getRight()) / 2.0
			phoneDisplayRegionCenterY = self.win.getYSize() * (1.0 - (self.phoneState.phoneDisplayRegion.getBottom() + self.phoneState.phoneDisplayRegion.getTop()) / 2.0)
			mouse = self.win.getPointer(0)
			s = 2 ** self.phoneState.minimapZoom
			x = clamp(self.camera.getX() + (mouse.getX() - phoneDisplayRegionCenterX) / s, -self.maxX, self.maxX)
			y = clamp(self.camera.getY() + (phoneDisplayRegionCenterY - mouse.getY()) / s, -self.maxY, self.maxY)
			previousHeading = self.camera.getH() % 360.0
			heading = (rad2Deg(atan2(y - self.camera.getY(), x - camera.getX())) - 90.0) % 360.0

			if (180.0 < abs(heading - previousHeading)):
				if (previousHeading < heading):
					heading -= 360.0
				else:
					heading += 360.0

			self.camera.setH(previousHeading)
			self.phoneState.orientationTriangle.setH(previousHeading)

			Parallel(
				self.camera.posInterval(0.5, Vec3(x, y, self.camera.getZ())),
				self.phoneState.minimapCamera.posInterval(0.5, Vec3(x, y, self.phoneState.minimapCamera.getZ())),
				self.phoneState.orientationTriangle.posInterval(0.5, Vec3(x, y, self.phoneState.orientationTriangle.getZ())),
				self.camera.hprInterval(0.5, Vec3(heading, self.camera.getP(), self.camera.getR())),
				self.phoneState.orientationTriangle.hprInterval(0.5, Vec3(heading, self.phoneState.orientationTriangle.getP(), self.phoneState.orientationTriangle.getR()))
			).start()
	
	def setBlurSharpen(self, amount):
		if (not self.useAdvancedVisualEffects):
			return

		if (amount == 1.0):
			self.filters.delBlurSharpen()
		else:
			self.filters.setBlurSharpen(amount=amount)

	def controlCamera(self, task):
		if (self.phoneState.state == "Center"):
			return Task.cont

		# figure out how much the mouse has moved (in pixels)
		mouse = self.win.getPointer(0)
		x = mouse.getX()
		y = mouse.getY()
		windowCenterX = self.win.getXSize() / 2
		windowCenterY = self.win.getYSize() / 2
		heading = self.camera.getH()
		pitch = self.camera.getP()

		if self.win.movePointer(0, windowCenterX, windowCenterY):
			heading -= (x - windowCenterX) * 0.2
			pitch = clamp(pitch - (y - windowCenterY) * 0.2, -45, 45)

		self.camera.setHpr(heading, pitch, 0)

		elapsed = task.time - self.last

		if (self.last == 0):
			elapsed = 0

		if (self.mousebtn[0]):
			direction = self.camera.getMat().getRow3(1)
			self.camera.setPos(self.camera.getPos() + direction * elapsed*30)

		clampX(self.camera, -self.maxX, self.maxX)
		clampY(self.camera, -self.maxY, self.maxY)
		self.camera.setZ(2)

		self.phoneState.minimapCamera.setX(self.camera.getX())
		self.phoneState.minimapCamera.setY(self.camera.getY())

		self.phoneState.orientationTriangle.setX(self.camera.getX())
		self.phoneState.orientationTriangle.setY(self.camera.getY())
		self.phoneState.orientationTriangle.setHpr(heading, -90, 0)

		self.last = task.time

		return Task.cont
Exemple #3
0
class MyApp(ShowBase):

	def __init__(self):
		ShowBase.__init__(self)

		self.useAdvancedVisualEffects =\
			ConfigVariableBool("use-advanced-visual-effects", True) and\
			base.win.getGsg().getSupportsBasicShaders() and\
			base.win.getGsg().getSupportsGlsl() and\
			base.win.getGsg().getSupportsDepthTexture()

		self.debug = DirectNotify().newCategory("Debug")

		self.phoneState = PhoneState(self)
		self.setupFilters()
		self.setupModels()
		self.setupKeyboardControl()
		self.camera.setPos(0, 0, 2)
		self.setupMouseControl()
		self.phoneState.request("Hidden")

	def setupFilters(self):
		if (self.useAdvancedVisualEffects):
			self.filters = CommonFilters(self.win, self.cam)
			self.filters.setBloom()

	def setupKeyboardControl(self):
		self.accept("escape", sys.exit)

	def setupMouseControl(self):
		self.disableMouse()

		self.mousex = 0
		self.mousey = 0
		self.last = 0
		self.mousebtn = [0,0,0]

		self.accept("mouse1", self.setMouseBtn, [0, 1])
		self.accept("mouse1-up", self.setMouseBtn, [0, 0])

		self.taskMgr.add(self.controlCamera, "cameraTask")

	def setupModels(self):
		self.setupLights()
		self.loadSky()
		self.loadTerrain()
		self.setupBuildings()
		self.phoneState.setupPhone()

	def setupLights(self):
		self.sunLight = self.render.attachNewNode(DirectionalLight("sunLight"))
		self.sunLight.setColor(Vec4(0.8, 0.8, 0.8, 1))
		self.sunLight.node().getLens().setFilmSize(128, 64)
		self.sunLight.node().getLens().setNearFar(20,2000)
		self.sunLight.setPos(60, 30, 50)
		self.sunLight.lookAt(0, 0, 0)
		self.render.setLight(self.sunLight)
#		self.sunLight.node().showFrustum()
		if self.useAdvancedVisualEffects:
			self.sunLight.node().setShadowCaster(True, 256, 256)
			self.render.setShaderAuto()

		self.ambientLight = self.render.attachNewNode(AmbientLight("ambientLight"))
		self.ambientLight.node().setColor(Vec4(0.2, 0.2, 0.2, 1))
		self.render.setLight(self.ambientLight)

	def loadSky(self):
		self.sky = self.loader.loadModel("models/sky")
		self.sky.reparentTo(self.camera)
		self.sky.setScale(base.camLens.getNear() * 1.1)
		self.sky.setBin("background", 0)
		self.sky.setDepthWrite(False)
		self.sky.setCompass()
		self.sky.setLightOff()

	def loadTerrain(self):
		self.terrain = self.loader.loadModel("models/terrain")
		self.terrain.reparentTo(self.render)
		self.teapot = self.loader.loadModel("teapot")

	def setupBuildings(self):
		# Load building prototypes
		self.buildingPadPrototype = self.loader.loadModel("models/building_pad")
		self.policeBuildingPrototype = self.loader.loadModel("models/police_building")
		self.tribunalPrototype = self.loader.loadModel("models/tribunal")
		self.officeBuildingPrototype = self.loader.loadModel("models/office_building")
		self.housePrototype = self.loader.loadModel("models/House/CasaSimples")
		self.housePrototype.setPos(5, 5, 2.1)

		self.buildCity()

	def buildCity(self):
		# Define city blueprint
		city = [
			"PSOSHS_",
			"SSSSSSS",
			"OSHHHHH",
			"_SSSSSS",
			"_SHHHHH",
			"_SSSSSS",
			"_SHHHHH",
			"_SSSSSS",
			"_SHHHHH"
		]
		blockSize = 10
		cityWESize = len(city[0]) * blockSize
		cityNSSize = len(city) * blockSize
		self.maxX = cityWESize / 2.0
		self.maxY = cityNSSize / 2.0
		buildingOutline = LineSegs("building")

		buildingSize = 8
		buildingPadding = 1
		buildingOutline.setColor(0, 0, 0, 1)
		buildingOutline.moveTo(buildingPadding, buildingPadding, 0)
		buildingOutline.drawTo(buildingPadding + buildingSize, buildingPadding, 0)
		buildingOutline.drawTo(buildingPadding + buildingSize, buildingPadding + buildingSize, 0)
		buildingOutline.drawTo(buildingPadding, buildingPadding + buildingSize, 0)
		buildingOutline.drawTo(buildingPadding, buildingPadding, 0)
		buildingOutlinePrototype = NodePath(buildingOutline.create())

		# Create buildings from city blueprint
		for rowIndex, row in enumerate(city):
			for columnIndex, buildingType in enumerate(row):
				# Get building data from city blueprint
				buildingInstanceName, buildingPrototype = self.buildingInstanceNameAndPrototypeFromType(buildingType)

				if (not (buildingInstanceName is None or buildingPrototype is None)):
					# Compute building position
					buildingX, buildingY, buildingZ = columnIndex * blockSize - cityWESize / 2, cityNSSize / 2 - (rowIndex + 1) * blockSize, 0

					# Create building pad
					buildingPadInstance = self.render.attachNewNode("buildingPadInstance")
					buildingPadInstance.setPos(buildingX, buildingY, buildingZ)
					self.buildingPadPrototype.instanceTo(buildingPadInstance)

					# Create building
					buildingInstance = self.render.attachNewNode(buildingInstanceName)
					buildingInstance.setPos(buildingX, buildingY, buildingZ)
					buildingPrototype.instanceTo(buildingInstance)

					# Create building outline in minimap
					buildingOutlineInstance = self.phoneState.minimap.attachNewNode("buildingOutline")
					buildingOutlineInstance.setPos(buildingX, buildingY, buildingZ)
					buildingOutlinePrototype.instanceTo(buildingOutlineInstance)

	def setMouseBtn(self, btn, value):
		self.mousebtn[btn] = value

		if (btn == 0 and value == 1 and self.phoneState.state == "Center"):
			phoneDisplayRegionCenterX = self.win.getXSize() * (self.phoneState.phoneDisplayRegion.getLeft() + self.phoneState.phoneDisplayRegion.getRight()) / 2.0
			phoneDisplayRegionCenterY = self.win.getYSize() * (1.0 - (self.phoneState.phoneDisplayRegion.getBottom() + self.phoneState.phoneDisplayRegion.getTop()) / 2.0)
			mouse = self.win.getPointer(0)
			s = 2 ** self.phoneState.minimapZoom
			x = clamp(self.camera.getX() + (mouse.getX() - phoneDisplayRegionCenterX) / s, -self.maxX, self.maxX)
			y = clamp(self.camera.getY() + (phoneDisplayRegionCenterY - mouse.getY()) / s, -self.maxY, self.maxY)
			previousHeading = self.camera.getH() % 360.0
			heading = (rad2Deg(atan2(y - self.camera.getY(), x - camera.getX())) - 90.0) % 360.0

			if (180.0 < abs(heading - previousHeading)):
				if (previousHeading < heading):
					heading -= 360.0
				else:
					heading += 360.0

			self.camera.setH(previousHeading)
			self.phoneState.orientationTriangle.setH(previousHeading)

			Parallel(
				self.camera.posInterval(0.5, Vec3(x, y, self.camera.getZ())),
				self.phoneState.minimapCamera.posInterval(0.5, Vec3(x, y, self.phoneState.minimapCamera.getZ())),
				self.phoneState.orientationTriangle.posInterval(0.5, Vec3(x, y, self.phoneState.orientationTriangle.getZ())),
				self.camera.hprInterval(0.5, Vec3(heading, self.camera.getP(), self.camera.getR())),
				self.phoneState.orientationTriangle.hprInterval(0.5, Vec3(heading, self.phoneState.orientationTriangle.getP(), self.phoneState.orientationTriangle.getR()))
			).start()

	def buildingInstanceNameAndPrototypeFromType(self, buildingType):
		return {
			'S' : ( None, None ),
			'P' : ( "policeBuildingInstance", self.policeBuildingPrototype ),
			'T' : ( "tribunalInstance", self.tribunalPrototype ),
			'O' : ( "officeBuildingInstance", self.officeBuildingPrototype ),
			'H' : ( "houseInstance", self.housePrototype ),
		}.get(buildingType, ( None, None ))
	
	def setBlurSharpen(self, amount):
		if (not self.useAdvancedVisualEffects):
			return

		if (amount == 1.0):
			self.filters.delBlurSharpen()
		else:
			self.filters.setBlurSharpen(amount=amount)

	def controlCamera(self, task):
		if (self.phoneState.state == "Center"):
			return Task.cont

		# figure out how much the mouse has moved (in pixels)
		mouse = self.win.getPointer(0)
		x = mouse.getX()
		y = mouse.getY()
		windowCenterX = self.win.getXSize() / 2
		windowCenterY = self.win.getYSize() / 2
		heading = self.camera.getH()
		pitch = self.camera.getP()

		if self.win.movePointer(0, windowCenterX, windowCenterY):
			heading -= (x - windowCenterX) * 0.2
			pitch = clamp(pitch - (y - windowCenterY) * 0.2, -45, 45)

		self.camera.setHpr(heading, pitch, 0)

		elapsed = task.time - self.last

		if (self.last == 0):
			elapsed = 0

		if (self.mousebtn[0]):
			direction = self.camera.getMat().getRow3(1)
			self.camera.setPos(self.camera.getPos() + direction * elapsed*30)

		clampX(self.camera, -self.maxX, self.maxX)
		clampY(self.camera, -self.maxY, self.maxY)
		self.camera.setZ(2)

		self.phoneState.minimapCamera.setX(self.camera.getX())
		self.phoneState.minimapCamera.setY(self.camera.getY())

		self.phoneState.orientationTriangle.setX(self.camera.getX())
		self.phoneState.orientationTriangle.setY(self.camera.getY())
		self.phoneState.orientationTriangle.setHpr(heading, -90, 0)

		self.last = task.time

		return Task.cont