Exemple #1
0
	def from_data(self, texture_data, palettes):
		from pandac.PandaModules import PNMImage, VBase4D
		from pandac.PandaModules import Texture as P3DTexture
		
		tex_pnm = PNMImage(17*256, 1024)
		tex_pnm.addAlpha()

		
		testpnm = PNMImage(256, 1024)
		
		palette = [(x, x, x, 1) for x in range(16)]
		colors = []
		for color in palette:
			color_list = [c / 15.0 for c in color[:3]]
			color_list.append(0 if color == (0, 0, 0, 0) else 1)
			colors.append(VBase4D(*color_list))
			
		for y in range(1024):
			row = texture_data.image[y]
			for x in range(256):
				testpnm.setXelA(x, y, colors[row[x]])
		
		self.texture2 = P3DTexture()
		self.texture2.load(testpnm)
		self.texture2.setMagfilter(P3DTexture.FTNearest)
		self.texture2.setMinfilter(P3DTexture.FTLinear)
		
		
		self.update(tex_pnm, texture_data, palettes)
Exemple #2
0
class MazeMapGui(DirectFrame):
    __module__ = __name__
    notify = directNotify.newCategory('MazeMapGui')

    def __init__(self,
                 mazeCollTable,
                 maskResolution=None,
                 radiusRatio=None,
                 bgColor=(0.8, 0.8, 0.8),
                 fgColor=(0.5, 0.5, 0.5, 1.0)):
        DirectFrame.__init__(self,
                             relief=None,
                             state=DGG.NORMAL,
                             sortOrder=DGG.BACKGROUND_SORT_INDEX)
        self.hide()
        self._bgColor = bgColor
        self._fgColor = fgColor
        self._mazeCollTable = mazeCollTable
        self._mazeWidth = len(self._mazeCollTable[0])
        self._mazeHeight = len(self._mazeCollTable)
        if not maskResolution:
            self._maskResolution = DEFAULT_MASK_RESOLUTION
            self._radius = radiusRatio is None and self._maskResolution * DEFAULT_RADIUS_RATIO
        else:
            self._radius = self._maskResolution * radiusRatio
        self._revealedCells = []
        for y in range(self._mazeHeight):
            self._revealedCells.append([])
            for u in range(self._mazeWidth):
                self._revealedCells[y].append(False)

        self._revealFunctions = {
            MazeRevealType.SmoothCircle: self._revealSmoothCircle,
            MazeRevealType.HardCircle: self._revealHardCircle,
            MazeRevealType.Square: self._revealSquare
        }
        self._revealFunction = MAZE_REVEAL_TYPE
        self.map = self._createMapTextureCard()
        self.map.reparentTo(self)
        self.maskedLayer = self.attachNewNode('maskedLayer')
        self.mask = self._createMaskTextureCard()
        self.mask.reparentTo(self)
        self.visibleLayer = self.attachNewNode('visibleLayer')
        self._laffMeterModel = loader.loadModel(
            'phase_3/models/gui/laff_o_meter')
        self._toon2marker = {}
        return

    def _createMapTextureCard(self):
        mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION)
        mapImage.fill(*self._bgColor)
        fgColor = VBase4D(*self._fgColor)
        for x in range(self._mazeHeight):
            for y in range(self._mazeWidth):
                if self._mazeCollTable[y][x] == 1:
                    ax = float(x) / self._mazeWidth * MAP_RESOLUTION
                    invertedY = self._mazeHeight - 1 - y
                    ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION
                    self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor)

        mapTexture = Texture('mapTexture')
        mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution,
                                self._maskResolution, 1, Texture.TUnsignedByte,
                                Texture.FRgba)
        mapTexture.setMinfilter(Texture.FTLinear)
        mapTexture.load(mapImage)
        mapTexture.setWrapU(Texture.WMClamp)
        mapTexture.setWrapV(Texture.WMClamp)
        mapImage.clear()
        del mapImage
        cm = CardMaker('map_cardMaker')
        cm.setFrame(-1.0, 1.0, -1.0, 1.0)
        map = self.attachNewNode(cm.generate())
        map.setTexture(mapTexture, 1)
        return map

    def _createMaskTextureCard(self):
        self._maskImage = PNMImage(self._maskResolution, self._maskResolution,
                                   4)
        for x in range(self._maskResolution):
            for y in range(self._maskResolution):
                self._maskImage.setXelA(x, y, 0, 0, 0, 1)

        self.maskTexture = Texture('maskTexture')
        self.maskTexture.setupTexture(Texture.TT2dTexture,
                                      self._maskResolution,
                                      self._maskResolution, 1,
                                      Texture.TUnsignedByte, Texture.FRgba)
        self.maskTexture.setMinfilter(Texture.FTLinear)
        self.maskTexture.setWrapU(Texture.WMClamp)
        self.maskTexture.setWrapV(Texture.WMClamp)
        self.maskTexture.load(self._maskImage)
        base.graphicsEngine.renderFrame()
        cm = CardMaker('mask_cardMaker')
        cm.setFrame(-1.1, 1.1, -1.1, 1.1)
        mask = self.attachNewNode(cm.generate())
        mask.setTexture(self.maskTexture, 1)
        mask.setTransparency(1)
        return mask

    def _drawSquare(self, image, ulx, uly, size, color):
        x = int(ulx)
        while x <= ulx + size:
            y = int(uly)
            while y <= uly + size:
                if x > 0 and y > 0 and x < image.getXSize(
                ) and y < image.getYSize():
                    image.setXelA(x, y, color)
                y += 1

            x += 1

    def destroy(self):
        del self._mazeCollTable
        del self._maskResolution
        del self._radius
        del self._revealedCells
        del self._revealFunctions
        del self._revealFunction
        self.map.removeNode()
        del self.map
        self.mask.removeNode()
        del self.mask
        self.maskedLayer.removeNode()
        del self.maskedLayer
        self.visibleLayer.removeNode()
        del self.visibleLayer
        self._maskImage.clear()
        del self._maskImage
        self.maskTexture.clear()
        del self.maskTexture
        self._laffMeterModel.removeNode()
        del self._laffMeterModel
        DirectFrame.destroy(self)

    def _revealSmoothCircle(self, x, y, center):
        length = (Vec2(x, y) - center).length()
        goalAlpha = max(0.0, length / float(self._radius) - 0.5)
        self._maskImage.setXelA(
            x, y,
            VBase4D(0.0, 0.0, 0.0,
                    min(self._maskImage.getAlpha(x, y), goalAlpha * 2.0)))

    def _revealHardCircle(self, x, y, center):
        length = (Vec2(x, y) - center).length()
        if length <= self._radius:
            self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0))

    def _revealSquare(self, x, y, center):
        self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0))

    def _drawHole(self, x, y):
        center = Vec2(x, y)
        ul = center - Vec2(self._radius, self._radius)
        lr = center + Vec2(self._radius, self._radius)
        x = int(ul[0])
        while x <= lr[0]:
            y = int(ul[1])
            while y <= lr[1]:
                if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution:
                    self._revealFunctions[self._revealFunction](x, y, center)
                y += 1

            x += 1

        self.maskTexture.load(self._maskImage)
        self.mask.setTexture(self.maskTexture, 1)

    def _createSimpleMarker(self, size, color=(1, 1, 1)):
        halfSize = size * 0.5
        cm = CardMaker('mazemap_simple_marker')
        cm.setFrame(-halfSize, halfSize, -halfSize, halfSize)
        markerNP = self.maskedLayer.attachNewNode(cm.generate())
        markerNP.setColor(*color)
        return markerNP

    def tile2gui(self, x, y):
        y = self._mazeHeight - y
        cellWidth = self._maskResolution / self._mazeWidth
        cellHeight = self._maskResolution / self._mazeHeight
        ax = float(x) / self._mazeWidth * self._maskResolution
        ax += cellWidth
        ay = float(y) / self._mazeHeight * self._maskResolution
        ay += cellHeight
        return (ax, ay)

    def gui2pos(self, x, y):
        return (x / self._maskResolution * 2.0 - 0.97, 0,
                y / self._maskResolution * -2.0 + 1.02)

    def _getToonMarker(self, toon):
        hType = toon.style.getType()
        if hType == 'rabbit':
            hType = 'bunny'
        return self._laffMeterModel.find('**/' + hType + 'head')

    def addToon(self, toon, tX, tY):
        marker = NodePath('toon_marker-%i' % toon.doId)
        marker.reparentTo(self)
        self._getToonMarker(toon).copyTo(marker)
        marker.setColor(toon.style.getHeadColor())
        if toon.isLocal():
            marker.setScale(0.07)
        else:
            marker.setScale(0.05)
        marker.flattenStrong()
        marker.setPos(*self.gui2pos(*self.tile2gui(tX, tY)))
        self._toon2marker[toon] = marker

    def removeToon(self, toon):
        if not self._toon2marker.has_key(toon):
            return
        self._toon2marker[toon].removeNode()
        del self._toon2marker[toon]

    def updateToon(self, toon, tX, tY):
        if not self._toon2marker.has_key(toon):
            return
        x, y = self.tile2gui(tX, tY)
        self._toon2marker[toon].setPos(*self.gui2pos(x, y))
        if tY < 0 or tY >= len(self._revealedCells):
            self.notify.warning('updateToon earlying out:')
            self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY))
            self.notify.warning('len(_revealedCells): %s' %
                                (len(self._revealedCells), ))
            if len(self._revealedCells) > 0:
                self.notify.warning('len(_revealedCells[0]): %s' %
                                    (len(self._revealedCells[0]), ))
            return
        if tX < 0 or tX >= len(self._revealedCells[tY]):
            self.notify.warning('updateToon earlying out:')
            self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY))
            self.notify.warning('len(_revealedCells): %s' %
                                (len(self._revealedCells), ))
            if tY < len(self._revealedCells):
                self.notify.warning('len(_revealedCells[tY]): %s' %
                                    (len(self._revealedCells[tY]), ))
            elif len(self._revealedCells) > 0:
                self.notify.warning('len(_revealedCells[0]): %s' %
                                    (len(self._revealedCells[0]), ))
            return
        if not self._revealedCells[tY][tX]:
            self._drawHole(x, y)
            self._revealedCells[tY][tX] = True

    def revealCell(self, x, y):
        ax, ay = self.tile2gui(x, y)
        if not self._revealedCells[y][x]:
            self._drawHole(ax, ay)
            self._revealedCells[y][x] = True

    def revealAll(self):
        for x in range(self._maskResolution):
            for y in range(self._maskResolution):
                self._maskImage.setXelA(x, y, 0, 0, 0, 0)

        self.revealCell(0, 0)

    def reset(self):
        for x in range(self._maskResolution):
            for y in range(self._maskResolution):
                self._maskImage.setXelA(x, y, 0, 0, 0, 1)
class MazeMapGui(DirectFrame):
    notify = directNotify.newCategory('MazeMapGui')

    def __init__(self, mazeCollTable, maskResolution = None, radiusRatio = None, bgColor = (0.8, 0.8, 0.8), fgColor = (0.5, 0.5, 0.5, 1.0)):
        DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
        self.hide()
        self._bgColor = bgColor
        self._fgColor = fgColor
        self._mazeCollTable = mazeCollTable
        self._mazeWidth = len(self._mazeCollTable[0])
        self._mazeHeight = len(self._mazeCollTable)
        self._maskResolution = maskResolution or DEFAULT_MASK_RESOLUTION
        if radiusRatio is None:
            self._radius = self._maskResolution * DEFAULT_RADIUS_RATIO
        else:
            self._radius = self._maskResolution * radiusRatio
        self._revealedCells = []
        for y in xrange(self._mazeHeight):
            self._revealedCells.append([])
            for u in xrange(self._mazeWidth):
                self._revealedCells[y].append(False)

        self._revealFunctions = {MazeRevealType.SmoothCircle: self._revealSmoothCircle,
         MazeRevealType.HardCircle: self._revealHardCircle,
         MazeRevealType.Square: self._revealSquare}
        self._revealFunction = MAZE_REVEAL_TYPE
        self.map = self._createMapTextureCard()
        self.map.reparentTo(self)
        self.maskedLayer = self.attachNewNode('maskedLayer')
        self.mask = self._createMaskTextureCard()
        self.mask.reparentTo(self)
        self.visibleLayer = self.attachNewNode('visibleLayer')
        self._laffMeterModel = loader.loadModel('phase_3/models/gui/laff_o_meter')
        self._toon2marker = {}
        return

    def _createMapTextureCard(self):
        mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION)
        mapImage.fill(*self._bgColor)
        fgColor = VBase4D(*self._fgColor)
        for x in xrange(self._mazeHeight):
            for y in xrange(self._mazeWidth):
                if self._mazeCollTable[y][x] == 1:
                    ax = float(x) / self._mazeWidth * MAP_RESOLUTION
                    invertedY = self._mazeHeight - 1 - y
                    ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION
                    self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor)

        mapTexture = Texture('mapTexture')
        mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba)
        mapTexture.setMinfilter(Texture.FTLinear)
        mapTexture.load(mapImage)
        mapTexture.setWrapU(Texture.WMClamp)
        mapTexture.setWrapV(Texture.WMClamp)
        mapImage.clear()
        del mapImage
        cm = CardMaker('map_cardMaker')
        cm.setFrame(-1.0, 1.0, -1.0, 1.0)
        map = self.attachNewNode(cm.generate())
        map.setTexture(mapTexture, 1)
        return map

    def _createMaskTextureCard(self):
        self._maskImage = PNMImage(self._maskResolution, self._maskResolution, 4)
        for x in xrange(self._maskResolution):
            for y in xrange(self._maskResolution):
                self._maskImage.setXelA(x, y, 0, 0, 0, 1)

        self.maskTexture = Texture('maskTexture')
        self.maskTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba)
        self.maskTexture.setMinfilter(Texture.FTLinear)
        self.maskTexture.setWrapU(Texture.WMClamp)
        self.maskTexture.setWrapV(Texture.WMClamp)
        self.maskTexture.load(self._maskImage)
        base.graphicsEngine.renderFrame()
        cm = CardMaker('mask_cardMaker')
        cm.setFrame(-1.1, 1.1, -1.1, 1.1)
        mask = self.attachNewNode(cm.generate())
        mask.setTexture(self.maskTexture, 1)
        mask.setTransparency(1)
        return mask

    def _drawSquare(self, image, ulx, uly, size, color):
        x = int(ulx)
        while x <= ulx + size:
            y = int(uly)
            while y <= uly + size:
                if x > 0 and y > 0 and x < image.getXSize() and y < image.getYSize():
                    image.setXelA(x, y, color)
                y += 1

            x += 1

    def destroy(self):
        del self._mazeCollTable
        del self._maskResolution
        del self._radius
        del self._revealedCells
        del self._revealFunctions
        del self._revealFunction
        self.map.removeNode()
        del self.map
        self.mask.removeNode()
        del self.mask
        self.maskedLayer.removeNode()
        del self.maskedLayer
        self.visibleLayer.removeNode()
        del self.visibleLayer
        self._maskImage.clear()
        del self._maskImage
        self.maskTexture.clear()
        del self.maskTexture
        self._laffMeterModel.removeNode()
        del self._laffMeterModel
        DirectFrame.destroy(self)

    def _revealSmoothCircle(self, x, y, center):
        length = (Vec2(x, y) - center).length()
        goalAlpha = max(0.0, length / float(self._radius) - 0.5)
        self._maskImage.setXelA(x, y, VBase4D(0.0, 0.0, 0.0, min(self._maskImage.getAlpha(x, y), goalAlpha * 2.0)))

    def _revealHardCircle(self, x, y, center):
        length = (Vec2(x, y) - center).length()
        if length <= self._radius:
            self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0))

    def _revealSquare(self, x, y, center):
        self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0))

    def _drawHole(self, x, y):
        center = Vec2(x, y)
        ul = center - Vec2(self._radius, self._radius)
        lr = center + Vec2(self._radius, self._radius)
        x = int(ul[0])
        while x <= lr[0]:
            y = int(ul[1])
            while y <= lr[1]:
                if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution:
                    self._revealFunctions[self._revealFunction](x, y, center)
                y += 1

            x += 1

        self.maskTexture.load(self._maskImage)
        self.mask.setTexture(self.maskTexture, 1)

    def _createSimpleMarker(self, size, color = (1, 1, 1)):
        halfSize = size * 0.5
        cm = CardMaker('mazemap_simple_marker')
        cm.setFrame(-halfSize, halfSize, -halfSize, halfSize)
        markerNP = self.maskedLayer.attachNewNode(cm.generate())
        markerNP.setColor(*color)
        return markerNP

    def tile2gui(self, x, y):
        y = self._mazeHeight - y
        cellWidth = self._maskResolution / self._mazeWidth
        cellHeight = self._maskResolution / self._mazeHeight
        ax = float(x) / self._mazeWidth * self._maskResolution
        ax += cellWidth
        ay = float(y) / self._mazeHeight * self._maskResolution
        ay += cellHeight
        return (ax, ay)

    def gui2pos(self, x, y):
        return (x / self._maskResolution * 2.0 - 0.97, 0, y / self._maskResolution * -2.0 + 1.02)

    def _getToonMarker(self, toon):
        hType = toon.style.getType()
        if hType == 'rabbit':
            hType = 'bunny'
        return self._laffMeterModel.find('**/' + hType + 'head')

    def addToon(self, toon, tX, tY):
        marker = NodePath('toon_marker-%i' % toon.doId)
        marker.reparentTo(self)
        self._getToonMarker(toon).copyTo(marker)
        marker.setColor(toon.style.getHeadColor())
        if toon.isLocal():
            marker.setScale(0.07)
        else:
            marker.setScale(0.05)
        marker.flattenStrong()
        marker.setPos(*self.gui2pos(*self.tile2gui(tX, tY)))
        self._toon2marker[toon] = marker

    def removeToon(self, toon):
        if not self._toon2marker.has_key(toon):
            return
        self._toon2marker[toon].removeNode()
        del self._toon2marker[toon]

    def updateToon(self, toon, tX, tY):
        if not self._toon2marker.has_key(toon):
            return
        x, y = self.tile2gui(tX, tY)
        self._toon2marker[toon].setPos(*self.gui2pos(x, y))
        if tY < 0 or tY >= len(self._revealedCells):
            self.notify.warning('updateToon earlying out:')
            self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY))
            self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),))
            if len(self._revealedCells) > 0:
                self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),))
            return
        if tX < 0 or tX >= len(self._revealedCells[tY]):
            self.notify.warning('updateToon earlying out:')
            self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY))
            self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),))
            if tY < len(self._revealedCells):
                self.notify.warning('len(_revealedCells[tY]): %s' % (len(self._revealedCells[tY]),))
            elif len(self._revealedCells) > 0:
                self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),))
            return
        if not self._revealedCells[tY][tX]:
            self._drawHole(x, y)
            self._revealedCells[tY][tX] = True

    def revealCell(self, x, y):
        ax, ay = self.tile2gui(x, y)
        if not self._revealedCells[y][x]:
            self._drawHole(ax, ay)
            self._revealedCells[y][x] = True

    def revealAll(self):
        for x in xrange(self._maskResolution):
            for y in xrange(self._maskResolution):
                self._maskImage.setXelA(x, y, 0, 0, 0, 0)

        self.revealCell(0, 0)

    def reset(self):
        for x in xrange(self._maskResolution):
            for y in xrange(self._maskResolution):
                self._maskImage.setXelA(x, y, 0, 0, 0, 1)
Exemple #4
0
	def import_(self, file_name, palettes):
		from pandac.PandaModules import PNMImage, Filename, VBase4D
		from pandac.PandaModules import Texture as P3DTexture
		
		pnm = PNMImage()
		pnm.read(Filename.fromOsSpecific(file_name))
		
		tex_pnm = PNMImage(17*256, 1024)
		tex_pnm.addAlpha()
		
		
		#convert data to same sequence as files
		texdata = []
		for y in range(1024):
			row = []
			for x in range(256):
				gray = pnm.getXel(x, y)
				pal_i = int(gray[0] * 15.0)
				row.append(pal_i)
			texdata.append(row)
		
		#update saving texture
		testpnm = PNMImage(256, 1024)
		
		palette = [(x, x, x, 1) for x in range(16)]
		colors = []
		for color in palette:
			color_list = [c / 15.0 for c in color[:3]]
			color_list.append(0 if color == (0, 0, 0, 0) else 1)
			colors.append(VBase4D(*color_list))
			
		for y in range(1024):
			row = texdata[y]
			for x in range(256):
				testpnm.setXelA(x, y, colors[row[x]])
		
		self.texture2.load(testpnm)
		self.texture2.setMagfilter(P3DTexture.FTNearest)
		self.texture2.setMinfilter(P3DTexture.FTLinear)

		
		#update texture visible on map


		self.palettes = []
		temp = []
		for x in range (16):
			temp.append((x, x, x, 1))
			
		self.palettes.append(temp)
		
		for y, palette in enumerate(palettes):
			selfpalette = []
			for x, color in enumerate(palette.colors.colors):
				selfpalette.append((color[0],color[1],color[2],1))
			self.palettes.append(selfpalette)
		
		i = 0
		for palette in self.palettes:
			colors = []
			for color in palette:
				color_list = [c / 15.0 for c in color[:3]]
				color_list.append(0 if color == (0, 0, 0, 0) else 1)
				colors.append(VBase4D(*color_list))
				
			for y in range(1024):
				row = texdata[y]
				for x in range(256):
					tex_pnm.setXelA(x + (256*i), y, colors[row[x]])	
			i += 1
			
		self.texture.load(tex_pnm)
		self.texture.setMagfilter(P3DTexture.FTNearest)
		self.texture.setMinfilter(P3DTexture.FTLinear)	
Exemple #5
0
class MazeMapGui(DirectFrame):

    notify = directNotify.newCategory("MazeMapGui")

    def __init__(self, mazeLayout, maskResolution=None, radiusRatio=None):
        """
        Constructor for a MazeMap.  the mazeLayout parameter is a 2d array of
        bools (or ints... maybe more depth will be added with that).
        maskResolution is a value for the resolution of the mask covering the
        map.  It should range from 32 to 256.  radiusRatio is essentially
        the percentage of the map that is revealed with each step.
        """
        DirectFrame.__init__(self,
            relief = None,
            state = DGG.NORMAL,
            sortOrder = DGG.BACKGROUND_SORT_INDEX,
        )

        # store / set parameters
        self._mazeLayout = mazeLayout
        self._maskResolution = maskResolution or DEFAULT_MASK_RESOLUTION
        if radiusRatio is None:
            self._radius = self._maskResolution * DEFAULT_RADIUS_RATIO
        else:
            self._radius = self._maskResolution * radiusRatio

        # store false for all maze cells to represent that none of them have
        # been revealed yet.  This can prevent the expensive call to altering
        # the mask if a cell is already revealed
        self._revealedCells = []
        for y in range( len(self._mazeLayout) ):
            self._revealedCells.append([])
            for x in range( len(self._mazeLayout[0]) ):
                self._revealedCells[y].append(False)

        # create reveal function mappings
        self._revealFunctions = {
            MazeRevealType.SmoothCircle : self._revealSmoothCircle,
            MazeRevealType.HardCircle : self._revealHardCircle,
            MazeRevealType.Square : self._revealSquare,
        }
        self._revealFunction = MAZE_REVEAL_TYPE

        # create the map and the mask
        self.map = self._createMapTextureCard()
        self.map.reparentTo(self)
        self.maskedLayer = self.attachNewNode("maskedLayer")
        self.mask = self._createMaskTextureCard()
        self.mask.reparentTo(self)
        self.visibleLayer = self.attachNewNode("visibleLayer")

        #TODO:maze: handle locks and doors
        self._players = []
        self._locks = []
        self._doors = []

    #--- Initialization, Destruction, and Resetting ---#########################

    def _createMapTextureCard(self):
        """
        This will return a NodePath with a card textured with the minimap.  The
        minimap texture is dynamically created from the map data.
        """
        # create and fill empty map image
        mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION)
        blockFiles = []
        for i in range(5):
            blockFiles.append(PNMImage())
            #blockFiles[i].read(Filename("mapBlock%i.jpg"%(i+1)))
            # TODO:maze either reference a set of textures for each piece or fill with color
            blockFiles[i].read(Filename('phase_4/maps/male_sleeve4New.jpg'))
        mapImage.fill(0.8, 0.8, 0.8)

        # iterate through the map data and place a block in the map image where appropriate
        for x in range( len(self._mazeLayout[0]) ):
            for y in range( len(self._mazeLayout) ):
                if self._mazeLayout[y][x]:
                    ax = float(x)/len(self._mazeLayout[0]) * MAP_RESOLUTION
                    ay = float(y)/len(self._mazeLayout) * MAP_RESOLUTION

                    #TODO:maze use different blocks for different wall types or items
                    #mapImage.copySubImage(random.choice(blockFiles), int(ax), int(ay), 20, 20, 32, 32)

                    #TODO:maze find the ideal block texture size for the map so we dont
                    #          have to do this strange offset
                    #mapImage.copySubImage(blockFiles[0], int(ax), int(ay), 0, 0, 32, 32)
                    self._drawSquare(mapImage, int(ax), int(ay), 10, VBase4D(0.5, 0.5, 0.5, 1.0))

        # create a texture from the map image
        mapTexture = Texture("mapTexture")
        mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba)
        mapTexture.setMinfilter(Texture.FTLinear)
        mapTexture.load(mapImage)
        mapTexture.setWrapU(Texture.WMClamp)
        mapTexture.setWrapV(Texture.WMClamp)

        mapImage.clear()
        del mapImage

        # put the texture on a card and return it
        cm = CardMaker("map_cardMaker")
        cm.setFrame(-1.0,1.0,-1.0,1.0)
        map = self.attachNewNode(cm.generate())
        map.setTexture(mapTexture, 1)
        return map

    def _createMaskTextureCard(self):
        """
        This will return a NodePath with a card textured with the map mask.  It
        also creates several other members that re needed to change the mask.
        """
        # create and fill empty mask image
        self._maskImage = PNMImage(self._maskResolution, self._maskResolution, 4)
        for x in range(self._maskResolution):
            for y in range(self._maskResolution):
                #maskImage.setXel(x,y,mapImage.getRed(x/13,y/10),mapImage.getGreen(x/13,y/10),mapImage.getBlue(x/13,y/10))
                self._maskImage.setXelA(x,y,0,0,0,1)

        # create the texture for the mask
        self.maskTexture = Texture("maskTexture")
        self.maskTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba)
        self.maskTexture.setMinfilter(Texture.FTLinear)
        self.maskTexture.setWrapU(Texture.WMClamp)
        self.maskTexture.setWrapV(Texture.WMClamp)

        self.maskTexture.load(self._maskImage)
        base.graphicsEngine.renderFrame()

        # put the mask texture on a card and return it
        cm = CardMaker("mask_cardMaker")
        cm.setFrame(-1.0,1.0,-1.0,1.0)
        mask = self.attachNewNode(cm.generate())
        mask.setTexture(self.maskTexture, 1)
        mask.setTransparency(1)
        return mask

    def _drawSquare(self, image, ulx, uly, size, color):
        """
        Draws a square on the supplied PNMImage starting at (ulx, uly) with a
        size of "size" and a color of "color".
        """
        x = int(ulx)
        while x <= ulx + size:
            y = int(uly)
            while y <= uly + size:
                if x > 0 and y > 0 and x < image.getXSize() and y < image.getYSize():
                    image.setXelA( x, y, color )
                y += 1
            x += 1

    def destroy(self):
        del self._mazeLayout
        del self._maskResolution
        del self._radius
        del self._revealedCells

        del self._revealFunctions
        del self._revealFunction

        # remove and delete all nodes
        self.map.removeNode()
        del self.map
        self.mask.removeNode()
        del self.mask
        self.maskedLayer.removeNode()
        del self.maskedLayer
        self.visibleLayer.removeNode()
        del self.visibleLayer

        # remove and delete all lists of nodes
        for p in self._players:
            p.removeNode()
        del self._players
        for k in self._locks:
            k.removeNode()
        del self._locks
        for d in self._doors:
            d.removeNode()
        del self._doors

        self._maskImage.clear()
        del self._maskImage

        self.maskTexture.clear()
        del self.maskTexture

        DirectFrame.destroy(self)

    #--- Reveal shape functions ---#############################################

    def _revealSmoothCircle(self, x, y, center):
        length = (Vec2(x,y)-center).length()
        goalAlpha = max(0.0, (length/float(self._radius)) - 0.5)
        self._maskImage.setXelA(
            x,
            y,
            VBase4D( 0.0, 0.0, 0.0, min(self._maskImage.getAlpha(x,y), goalAlpha*2.0))
        )

    def _revealHardCircle(self, x, y, center):
        length = (Vec2(x,y)-center).length()
        if length <= self._radius:
            self._maskImage.setXelA(x,y,VBase4D(0,0,0,0))

    def _revealSquare(self, x, y, center):
        self._maskImage.setXelA(x,y,VBase4D(0,0,0,0))

    #--- Private Functions ---##################################################

    def _drawHole(self, x, y):
        center = Vec2(x, y)
        ul = center - Vec2(self._radius, self._radius)
        lr = center + Vec2(self._radius, self._radius)
        x = int(ul[0])
        while x <= lr[0]:
            y = int(ul[1])
            while y <= lr[1]:
                if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution:
                    self._revealFunctions[self._revealFunction](x, y, center)
                y += 1
            x += 1

        self.maskTexture.load(self._maskImage)
        self.mask.setTexture(self.maskTexture, 1)

    def _tileToActualPosition(self, x, y):
        y = len(self._mazeLayout) - y
        cellWidth = self._maskResolution / len(self._mazeLayout[0])
        cellHeight = self._maskResolution / len(self._mazeLayout)
        ax = float(x)/len(self._mazeLayout[0]) * self._maskResolution
        ax += cellWidth
        ay = float(y)/len(self._mazeLayout) * self._maskResolution
        ay += cellHeight
        return ax, ay

    #--- Member Functions ---###################################################

    def addDoor(self, x, y, color):
        """
        Adds a door to the minimap.  This will add a colored dot to the map
        that represents a door.
        --- This is subject to change pending a new player-lock data system. ---
        """
        assert self.notify.debugCall()

        x, y = self._tileToActualPosition(x, y)

        # TODO:maze: replace with door model / texture
        cm = CardMaker("door_cardMaker")
        cm.setFrame(-0.04,0.04,-0.04,0.04)
        #door = self.visibleLayer.attachNewNode(cm.generate())
        door = self.maskedLayer.attachNewNode(cm.generate())

        door.setColor(color)
        door.setPos(x/self._maskResolution*2.0 - 0.97, 0, y/self._maskResolution*-2.0 + 1.02)

        self._doors.append(door)

    def addLock(self, x, y, color):
        """
        Adds a lock to the minimap.  This will add a colored dot to the map
        that represents a lock.
        --- This is subject to change pending a new player-lock data system. ---
        """
        assert self.notify.debugCall()

        x, y = self._tileToActualPosition(x, y)

        # TODO:maze: replace with lock model / texture
        cm = CardMaker("lock_cardMaker")
        cm.setFrame(-0.04,0.04,-0.04,0.04)
        lock = self.maskedLayer.attachNewNode(cm.generate())

        lock.setColor(color)
        lock.setPos(x/self._maskResolution*2.0 - 0.97, 0, y/self._maskResolution*-2.0 + 1.02)

        self._locks.append(lock)

    def addPlayer(self, x, y, color):
        """
        Adds a player to the minimap.  This will add a colored dot to the map
        that represents the player.  The dot location can then be updated
        using the revealCell call.
        --- This is subject to change pending a new player-lock data system. ---
        """
        assert self.notify.debugCall()

        x, y = self._tileToActualPosition(x, y)

        # TODO:maze: replace with player model / texture
        cm = CardMaker("player_cardMaker")
        cm.setFrame(-0.04,0.04,-0.04,0.04)
        player = self.visibleLayer.attachNewNode(cm.generate())

        player.setColor(color)
        player.setPos(x/self._maskResolution*2.0 - 0.97, 0, y/self._maskResolution*-2.0 + 1.02)

        self._players.append(player)

    def revealCell(self, x, y, playerIndex=None):
        """
        Clears out the mask around the given cell and stores that the cell has
        been revealed to prevent attempting to edit the mask for the cell again.
        """

        ax, ay = self._tileToActualPosition(x, y)

        if not self._revealedCells[y][x]:
            self._drawHole(ax, ay)
            self._revealedCells[y][x] = True

        if playerIndex is not None:
            assert(playerIndex < len(self._players))
            self._players[playerIndex].setPos(ax/self._maskResolution*2.0 - 0.97, 0, ay/self._maskResolution*-2.0 + 1.02)

    def revealAll(self):
        """ Clears out all of the mask. """
        for x in range(self._maskResolution):
            for y in range(self._maskResolution):
                self._maskImage.setXelA(x,y,0,0,0,0)

    def reset(self):
        """ Turns all of the mask on, covering the entire map. """
        for x in range(self._maskResolution):
            for y in range(self._maskResolution):
                self._maskImage.setXelA(x,y,0,0,0,1)
class TexturePainter(DirectObject):
  def __init__(self):
    self.editTexture = None
    self.editModel = None
    
    self.texturePainterStatus = TEXTURE_PAINTER_STATUS_DISABLED
    
    self.paintColor = VBase4D(1,1,1,1)
    self.paintSize = 10
    self.paintEffect = PNMBrush.BEBlend
    self.paintSmooth = True
    self.paintMode = TEXTUREPAINTER_FUNCTION_PAINT_POINT
    
    self.painter = None
  
  # --- creation and destroying of the whole editor ---
  def enableEditor(self):
    ''' create the editor
    change from disabled to enabled'''
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_DISABLED:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_ENABLED
      self.__enableEditor()
    else:
      print "E: TexturePainter.enableEditor: not disabled", self.texturePainterStatus
  
  def disableEditor(self):
    ''' destroy the editor, automatically stop the editor and painting
    change from enabled to disabled'''
    # try stopping if more advanced mode
    #if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
    #  self.stopEditor()
    
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_ENABLED:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_DISABLED
      self.__disableEditor()
    else:
      print "E: TexturePainter.disableEditor: not enabled", self.texturePainterStatus
  
  # --- 
  def startEditor(self, editModel, editTexture, backgroundShader=MODEL_COLOR_SHADER):
    ''' prepare to paint
    change from enabled to initialized'''
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_ENABLED:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_INITIALIZED
      self.__startEditor(editModel, editTexture, backgroundShader)
    else:
      print "E: TexturePainter.startEditor: not enabled", self.texturePainterStatus
  
  def stopEditor(self):
    ''' stop painting, automatically stop painting
    change from initialized to enabled'''
    #if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
    #  self.stopPaint()
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_ENABLED
      return self.__stopEditor()
    else:
      print "E: TexturePainter.startEditor: not initialized", self.texturePainterStatus
  
  """ # this is not externally callable
  # ---
  def startPaint(self):
    ''' start painting on the model
    change from initialized to running '''
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_RUNNING
      self.__startPaint()
    else:
      print "E: TexturePainter.startPaint: not enabled", self.texturePainterStatus"""
  
  def stopPaint(self):
    ''' stop painting
    change from running to initialized '''
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_RUNNING:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_INITIALIZED
      self.__stopPaint()
    else:
      print "E: TexturePainter.stopPaint: not running", self.texturePainterStatus
  
  
  # --- brush settings for painting ---
  ''' changing brush settings is possible all the time '''
  def setBrushSettings(self, color, size, smooth, effect):
    #print "I: TexturePainter.setBrushSettings", color, size, smooth, effect
    self.paintColor = color
    self.paintSize = size
    self.paintEffect = effect
    self.paintSmooth = smooth
    if effect in [PNMBrush.BESet, PNMBrush.BEBlend, PNMBrush.BEDarken, PNMBrush.BELighten]:
      self.brush = PNMBrush.makeSpot(color, size, smooth, effect)
      #if self.paintModel:
      if self.painter:
        self.painter.setPen(self.brush)
  
  def getBrushSettings(self):
    return self.paintColor,self.paintSize,self.paintSmooth,self.paintEffect
  
  def setPaintMode(self, newMode):
    self.paintMode = newMode
    # clear last point if mode changed
    if newMode == TEXTUREPAINTER_FUNCTION_PAINT_POINT or \
       newMode == TEXTUREPAINTER_FUNCTION_READ:
      self.lastPoint = None
  
  def getPaintMode(self):
    return self.paintMode
  
  
  
  def __enableEditor(self):
    ''' create the background rendering etc., but the model is not yet defined '''
    print "I: TexturePainter.__enableEditor"
    # the buffer the model with the color texture is rendered into
    self.modelColorBuffer = None
    self.modelColorCam = None
    # the buffer the picked position color is rendered into
    self.colorPickerBuffer = None
    self.colorPickerCam = None
    # create the buffers
    self.__createBuffer()
    
    # when the window is resized, the background buffer etc must be updated.
    self.accept("window-event", self.__windowEvent)
    
    # some debugging stuff
    self.accept("v", base.bufferViewer.toggleEnable)
    self.accept("V", base.bufferViewer.toggleEnable)
  
  def __disableEditor(self):
    print "I: TexturePainter.__disableEditor"
    
    self.__destroyBuffer()
    
    # ignore window-event and debug
    self.ignoreAll()
  
  def __windowEvent(self, win=None):
    ''' when the editor is enabled, update the buffers etc. when the window
    is resized '''
    print "I: TexturePainter.windowEvent"
    
    # with a fixed backgroudn buffer size this is not needed anymore
    
    if False:
      #if self.texturePainterStatus != TEXTURE_PAINTER_STATUS_DISABLED:
      if self.modelColorBuffer:
        if WindowManager.activeWindow:
          # on window resize there seems to be never a active window
          win = WindowManager.activeWindow.win
        else:
          win = base.win
        if self.modelColorBuffer.getXSize() != win.getXSize() or self.modelColorBuffer.getYSize() != win.getYSize():
          '''print "  - window resized",\
              self.modelColorBuffer.getXSize(),\
              win.getXSize(),\
              self.modelColorBuffer.getYSize(),\
              win.getYSize()'''
          # if the buffer size doesnt match the window size (window has been resized)
          self.__destroyBuffer()
          self.__createBuffer()
          self.__updateModel()
      else:
        print "W: TexturePainter.__windowEvent: no buffer"
        self.__createBuffer()
  
  def __createBuffer(self):
    ''' create the buffer we render in the background into '''
    print "I: TexturePainter.__createBuffer"
    # the window has been modified
    if WindowManager.activeWindow:
      # on window resize there seems to be never a active window
      win = WindowManager.activeWindow.win
    else:
      win = base.win
    
    # get the window size
    self.windowSizeX = win.getXSize()
    self.windowSizeY = win.getYSize()
    
    # create a buffer in which we render the model using a shader
    self.paintMap = Texture()
    # 1.5.4 cant handle non power of 2 buffers
    self.modelColorBuffer = createOffscreenBuffer(-3, TEXTUREPAINTER_BACKGROUND_BUFFER_RENDERSIZE[0], TEXTUREPAINTER_BACKGROUND_BUFFER_RENDERSIZE[1]) #self.windowSizeX, self.windowSizeY)
    self.modelColorBuffer.addRenderTexture(self.paintMap, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
    self.modelColorCam = base.makeCamera(self.modelColorBuffer, lens=base.cam.node().getLens(), sort=1)
    
    # Create a small buffer for the shader program that will fetch the point from the texture made
    # by the self.modelColorBuffer
    self.colorPickerImage = PNMImage()
    self.colorPickerTex = Texture()
    self.colorPickerBuffer = base.win.makeTextureBuffer("color picker buffer", 2, 2, self.colorPickerTex, True)
    
    self.colorPickerScene = NodePath('color picker scene')
    
    self.colorPickerCam = base.makeCamera(self.colorPickerBuffer, lens=base.cam.node().getLens(), sort=2)
    self.colorPickerCam.reparentTo(self.colorPickerScene)
    self.colorPickerCam.setY(-2)
    
    cm = CardMaker('color picker scene card')
    cm.setFrameFullscreenQuad()
    pickerCard = self.colorPickerScene.attachNewNode(cm.generate())
    
    loadPicker = NodePath(PandaNode('pointnode'))
    loadPicker.setShader(Shader.make(COLOR_PICKER_SHADER), 10001)
    
    # Feed the paintmap from the paintBuffer to the shader and initial mouse positions
    self.colorPickerScene.setShaderInput('paintmap', self.paintMap)
    self.colorPickerScene.setShaderInput('mousepos', 0, 0, 0, 1)
    self.colorPickerCam.node().setInitialState(loadPicker.getState())
  
  def __destroyBuffer(self):
    print "I: TexturePainter.__destroyBuffer"
    if self.modelColorBuffer:
      
      # Destroy the buffer
      base.graphicsEngine.removeWindow(self.modelColorBuffer)
      self.modelColorBuffer = None
      # Remove the camera
      self.modelColorCam.removeNode()
      del self.modelColorCam
      
      self.colorPickerScene.removeNode()
      del self.colorPickerScene
      # remove cam
      self.colorPickerCam.removeNode()
      del self.colorPickerCam
      # Destroy the buffer
      base.graphicsEngine.removeWindow(self.colorPickerBuffer)
      self.colorPickerBuffer = None
      
      del self.colorPickerTex
      del self.colorPickerImage
  
  def __startEditor(self, editModel, editTexture, backgroundShader=MODEL_COLOR_SHADER):
    print "I: TexturePainter.__startEditor"
    
    # this is needed as on startup the editor may not have had a window etc.
    self.__windowEvent()
    
    if not editModel or not editTexture:
      print "W: TexturePainter.__startEditor: model or texture invalid", editModel, editTexture
      return False
    
    self.editModel = editModel
    self.editTexture = editTexture
    self.editImage = None
    self.backgroundShader = backgroundShader
    
    if type(self.editTexture) == Texture:
      # if the image to modify is a texture, create a pnmImage which we modify
      self.editImage = PNMImage()
      # copy the image from the texture to the working layer
      self.editTexture.store(self.editImage)
    else:
      self.editImage = self.editTexture
    
    # create the brush for painting
    self.painter = PNMPainter(self.editImage)
    self.setBrushSettings( *self.getBrushSettings() )
    
    self.__updateModel()
    
    # start edit
    messenger.send(EVENT_TEXTUREPAINTER_STARTEDIT)
    
    for startEvent in TEXTUREPAINTER_START_PAINT_EVENTS:
      self.accept(startEvent, self.__startPaint)
    for stopEvent in TEXTUREPAINTER_STOP_PAINT_EVENTS:
      self.accept(stopEvent, self.__stopPaint)
    
    self.modelColorCam.node().copyLens(WindowManager.activeWindow.camera.node().getLens())
    
    taskMgr.add(self.__paintTask, 'paintTask')
    
    #modelModificator.toggleEditmode(False)
    
    self.isPainting = False
  
  def __stopEditor(self):
    print "I: TexturePainter.__stopEditor"
    for startEvent in TEXTUREPAINTER_START_PAINT_EVENTS:
      self.ignore(startEvent)
    for stopEvent in TEXTUREPAINTER_STOP_PAINT_EVENTS:
      self.ignore(stopEvent)
    
    taskMgr.remove('paintTask')
    # stop edit end
    
    # must be reset before we loose the properties
    if self.editModel and self.editTexture and self.editImage:
      try:
        # hide the model from cam 2
        self.editModel.hide(BitMask32.bit(1))
        self.editModel = None
      except:
        print "E: TexturePainter.__stopEditor: the model has already been deleted"
    
    # stop edit
    messenger.send(EVENT_TEXTUREPAINTER_STOPEDIT)
    
    self.editImage = None
    self.editTexture = None
    self.painter = None
    self.brush = None
    
    #modelModificator.toggleEditmode(True)
    
  
  def __updateModel(self):
    if self.editModel:
      # create a image with the same size of the texture
      textureSize = (self.editTexture.getXSize(), self.editTexture.getYSize())
      
      # create a dummy node, where we setup the parameters for the background rendering
      loadPaintNode = NodePath(PandaNode('paintnode'))
      loadPaintNode.setShader(Shader.make(self.backgroundShader), 10001)
      loadPaintNode.setShaderInput('texsize', textureSize[0], textureSize[1], 0, 0)
      
      # copy the state onto the camera
      self.modelColorCam.node().setInitialState(loadPaintNode.getState())
      
      # the camera gets a special bitmask, to show/hide models from it
      self.modelColorCam.node().setCameraMask(BitMask32.bit(1))
      
      if False:
        # doesnt work, but would be nicer (not messing with the default render state)
        
        hiddenNode = NodePath(PandaNode('hiddennode'))
        hiddenNode.hide(BitMask32.bit(1))
        showTroughNode = NodePath(PandaNode('showtroughnode'))
        showTroughNode.showThrough(BitMask32.bit(1))
        
        self.modelColorCam.node().setTagStateKey('show-on-backrender-cam')
        self.modelColorCam.node().setTagState('False', hiddenNode.getState())
        self.modelColorCam.node().setTagState('True', showTroughNode.getState())
        
        render.setTag('show-on-backrender-cam', 'False')
        self.editModel.setTag('show-on-backrender-cam', 'True')
      else:
        # make only the model visible to the background camera
        render.hide(BitMask32.bit(1))
        self.editModel.showThrough(BitMask32.bit(1))
  
  # --- start the paint tasks ---
  def __startPaint(self):
    self.isPainting = True
  
  def __stopPaint(self):
    self.isPainting = False
  
  # --- modification tasks ---
  def __textureUpdateTask(self, task=None):
    ''' modify the texture using the edited image
    '''
    if type(self.editTexture) == Texture:
      self.editTexture.load(self.editImage)
    
    if task: # task may be None
      return task.again
  
  def __paintTask(self, task):
    #print "I: TexturePainter.__paintTask:"
    
    if not WindowManager.activeWindow or not WindowManager.activeWindow.mouseWatcherNode.hasMouse():
      '''print "  - abort:", WindowManager.activeWindow
      if WindowManager.activeWindow:
        print "  - mouse:", WindowManager.activeWindow.mouseWatcherNode.hasMouse()'''
      return task.cont
    
    
    # update the camera according to the active camera
    #self.modelColorCam.setMat(render, WindowManager.activeWindow.camera.getMat(render))
    
    mpos = base.mouseWatcherNode.getMouse()
    x_ratio = min( max( ((mpos.getX()+1)/2), 0), 1)
    y_ratio = min( max( ((mpos.getY()+1)/2), 0), 1)
    mx = int(x_ratio*self.windowSizeX)
    my = self.windowSizeY - int(y_ratio*self.windowSizeY)
    
    self.colorPickerScene.setShaderInput('mousepos', x_ratio, y_ratio, 0, 1)
    
    if self.colorPickerTex.hasRamImage():
      self.colorPickerTex.store(self.colorPickerImage)
      
      # get the color below the mousepick from the rendered frame
      r = self.colorPickerImage.getRedVal(0,0)
      g = self.colorPickerImage.getGreenVal(0,0)
      b = self.colorPickerImage.getBlueVal(0,0)
      # calculate uv-texture position from the color
      x = r + ((b%16)*256)
      y = g + ((b//16)*256)
      
      if self.isPainting:
        self.__paintPixel(x,y)
        self.__textureUpdateTask()
    else:
      # this might happen if no frame has been rendered yet since creation of the texture
      print "W: TexturePainter.__paintTask: colorPickerTex.hasRamMipmapImage() =", self.colorPickerTex.hasRamImage()
    
    return task.cont
  
  def __paintPixel(self, x, y):
    ''' paint at x/y with the defined settings '''
    
    imageMaxX = self.editImage.getXSize()
    imageMaxY = self.editImage.getYSize()
    def inImage(x,y):
      ''' is the given x/y position within the image '''
      return ((imageMaxX > x >= 0) and (imageMaxY > y >= 0))
    
    # how smooth should be painted
    if self.paintSmooth:
      # a smooth brush
      hardness = 1.0
    else:
      # a hard brush
      hardness = 0.1
    hardness = min(1.0, max(0.05, hardness))
    
    # the paint radius
    radius = int(round(self.paintSize/2.0))
    radiusSquare = float(radius*radius)
    
    # a function to get the brush color/strength, depending on the radius
    def getBrushColor(diffPosX, diffPosY):
      distance = diffPosX**2 + diffPosY**2
      brushStrength = (1 - (min(distance, radiusSquare) / radiusSquare)) / hardness
      return min(1.0, max(0.0, brushStrength))
    
    if inImage(x,y):
      if self.paintMode == TEXTUREPAINTER_FUNCTION_PAINT_POINT:
        if self.paintEffect in [PNMBrush.BESet, PNMBrush.BEBlend, PNMBrush.BEDarken, PNMBrush.BELighten]:
          # render a spot into the texture
          self.painter.drawPoint(x, y)
        
        elif self.paintEffect in [TEXTUREPAINTER_BRUSH_FLATTEN, TEXTUREPAINTER_BRUSH_SMOOTH, TEXTUREPAINTER_BRUSH_RANDOMIZE]:
          
          if self.paintEffect == TEXTUREPAINTER_BRUSH_SMOOTH:
            # calculate average values
            data = dict()
            smoothRadius = 2
            for dx in xrange(-radius, radius+1):
              for dy in xrange(-radius, radius+1):
                if inImage(x+dx,y+dy):
                  average = VBase4D(0)
                  dividor = 0
                  for px in xrange(-smoothRadius,smoothRadius+1):
                    for py in xrange(-smoothRadius,smoothRadius+1):
                      if inImage(x+dx+px,y+dy+py):
                        average += self.editImage.getXelA(x+dx+px,y+dy+py)
                        dividor += 1
                  average /= float(dividor)
                  data[(x+dx,y+dy)] = average
            
            # save to image
            for (px,py), newValue in data.items():
              currentValue = self.editImage.getXelA(px,py)
              diffValue = currentValue - newValue
              
              dx = px - x
              dy = py - y
              
              
              multiplier = getBrushColor(dx, dy)
              print dx, dy, multiplier
              '''if self.paintSmooth:
                multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
              else:
                # not sure if this is correct
                multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
              
              '''r = currentValue.getX() * (1-multiplier*self.paintColor.getX()) + diffValue.getX() * multiplier*self.paintColor.getX()
              g = currentValue.getY() * (1-multiplier*self.paintColor.getY()) + diffValue.getY() * multiplier*self.paintColor.getY()
              b = currentValue.getZ() * (1-multiplier*self.paintColor.getZ()) + diffValue.getZ() * multiplier*self.paintColor.getZ()
              a = currentValue.getW() * (1-multiplier*self.paintColor.getW()) + diffValue.getW() * multiplier*self.paintColor.getW()'''
              r = currentValue.getX() - multiplier * diffValue.getX()
              g = currentValue.getY() - multiplier * diffValue.getY()
              b = currentValue.getZ() - multiplier * diffValue.getZ()
              a = currentValue.getW() - multiplier * diffValue.getW()
              if self.editImage.hasAlpha():
                self.editImage.setXelA(px,py,VBase4D(r,g,b,a))
              else:
                self.editImage.setXel(px,py,VBase3D(r,g,b))
              
              #self.editImage.setXelA(x,y,value)
            
          if self.paintEffect == TEXTUREPAINTER_BRUSH_FLATTEN:
            dividor = 0
            average = VBase4D(0)
            for dx in xrange(-radius, radius+1):
              for dy in xrange(-radius, radius+1):
                if inImage(x+dx,y+dy):
                  multiplier = getBrushColor(dx, dy)
                  '''if self.paintSmooth:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
                  else:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                  dividor += multiplier
                  average += self.editImage.getXelA(x+dx,y+dy) * multiplier
            average /= dividor
            for dx in xrange(-radius, radius+1):
              for dy in xrange(-radius, radius+1):
                if inImage(x+dx,y+dy):
                  multiplier = getBrushColor(dx, dy)
                  '''if self.paintSmooth:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
                  else:
                    # not sure if this is correct
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                  currentValue = self.editImage.getXelA(x+dx,y+dy)
                  r = currentValue.getX() * (1-multiplier*self.paintColor.getX()) + average.getX() * multiplier*self.paintColor.getX()
                  g = currentValue.getY() * (1-multiplier*self.paintColor.getY()) + average.getY() * multiplier*self.paintColor.getY()
                  b = currentValue.getZ() * (1-multiplier*self.paintColor.getZ()) + average.getZ() * multiplier*self.paintColor.getZ()
                  a = currentValue.getW() * (1-multiplier*self.paintColor.getW()) + average.getW() * multiplier*self.paintColor.getW()
                  if self.editImage.hasAlpha():
                    self.editImage.setXelA(x+dx,y+dy,VBase4D(r,g,b,a))
                  else:
                    self.editImage.setXel(x+dx,y+dy,VBase3D(r,g,b))
          
          elif self.paintEffect == TEXTUREPAINTER_BRUSH_RANDOMIZE:
            for dx in xrange(-radius, radius+1):
              for dy in xrange(-radius, radius+1):
                if inImage(x+dx,y+dy):
                  r = VBase4D(random.random()*self.paintColor.getX()-self.paintColor.getX()/2.,
                             random.random()*self.paintColor.getY()-self.paintColor.getY()/2.,
                             random.random()*self.paintColor.getZ()-self.paintColor.getZ()/2.,
                             random.random()*self.paintColor.getW()-self.paintColor.getW()/2.)
                  multiplier = getBrushColor(dx, dy)
                  '''if self.paintSmooth:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
                  else:
                    # not sure if this is correct
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                  currentValue = self.editImage.getXelA(x+dx,y+dy)
                  self.editImage.setXelA(x+dx,y+dy,currentValue+r*multiplier)
      
      elif self.paintMode == TEXTUREPAINTER_FUNCTION_READ:
        if inImage(x,y):
          col = self.editImage.getXelA(x,y)
          if self.editImage.hasAlpha():
            self.paintColor = VBase4D(col[0], col[1], col[2], col[3])
          else:
            self.paintColor = VBase4D(col[0], col[1], col[2], 1.0)
          messenger.send(EVENT_TEXTUREPAINTER_BRUSHCHANGED)
      
      elif self.paintMode == TEXTUREPAINTER_FUNCTION_PAINT_LINE:
        if self.lastPoint != None:
          self.painter.drawLine(x, y, self.lastPoint[0], self.lastPoint[1])
      
      elif self.paintMode == TEXTUREPAINTER_FUNCTION_PAINT_RECTANGLE:
        if self.lastPoint != None:
          self.painter.drawRectangle(x, y, self.lastPoint[0], self.lastPoint[1])
      
      self.lastPoint = (x,y)
class TexturePainter(DirectObject):
    def __init__(self):
        self.editTexture = None
        self.editModel = None

        self.texturePainterStatus = TEXTURE_PAINTER_STATUS_DISABLED

        self.paintColor = VBase4D(1, 1, 1, 1)
        self.paintSize = 10
        self.paintEffect = PNMBrush.BEBlend
        self.paintSmooth = True
        self.paintMode = TEXTUREPAINTER_FUNCTION_PAINT_POINT

        self.painter = None

    # --- creation and destroying of the whole editor ---
    def enableEditor(self):
        ''' create the editor
    change from disabled to enabled'''
        if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_DISABLED:
            self.texturePainterStatus = TEXTURE_PAINTER_STATUS_ENABLED
            self.__enableEditor()
        else:
            print "E: TexturePainter.enableEditor: not disabled", self.texturePainterStatus

    def disableEditor(self):
        ''' destroy the editor, automatically stop the editor and painting
    change from enabled to disabled'''
        # try stopping if more advanced mode
        #if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
        #  self.stopEditor()

        if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_ENABLED:
            self.texturePainterStatus = TEXTURE_PAINTER_STATUS_DISABLED
            self.__disableEditor()
        else:
            print "E: TexturePainter.disableEditor: not enabled", self.texturePainterStatus

    # ---
    def startEditor(self,
                    editModel,
                    editTexture,
                    backgroundShader=MODEL_COLOR_SHADER):
        ''' prepare to paint
    change from enabled to initialized'''
        if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_ENABLED:
            self.texturePainterStatus = TEXTURE_PAINTER_STATUS_INITIALIZED
            self.__startEditor(editModel, editTexture, backgroundShader)
        else:
            print "E: TexturePainter.startEditor: not enabled", self.texturePainterStatus

    def stopEditor(self):
        ''' stop painting, automatically stop painting
    change from initialized to enabled'''
        #if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
        #  self.stopPaint()
        if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
            self.texturePainterStatus = TEXTURE_PAINTER_STATUS_ENABLED
            return self.__stopEditor()
        else:
            print "E: TexturePainter.startEditor: not initialized", self.texturePainterStatus

    """ # this is not externally callable
  # ---
  def startPaint(self):
    ''' start painting on the model
    change from initialized to running '''
    if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_INITIALIZED:
      self.texturePainterStatus = TEXTURE_PAINTER_STATUS_RUNNING
      self.__startPaint()
    else:
      print "E: TexturePainter.startPaint: not enabled", self.texturePainterStatus"""

    def stopPaint(self):
        ''' stop painting
    change from running to initialized '''
        if self.texturePainterStatus == TEXTURE_PAINTER_STATUS_RUNNING:
            self.texturePainterStatus = TEXTURE_PAINTER_STATUS_INITIALIZED
            self.__stopPaint()
        else:
            print "E: TexturePainter.stopPaint: not running", self.texturePainterStatus

    # --- brush settings for painting ---
    ''' changing brush settings is possible all the time '''

    def setBrushSettings(self, color, size, smooth, effect):
        #print "I: TexturePainter.setBrushSettings", color, size, smooth, effect
        self.paintColor = color
        self.paintSize = size
        self.paintEffect = effect
        self.paintSmooth = smooth
        if effect in [
                PNMBrush.BESet, PNMBrush.BEBlend, PNMBrush.BEDarken,
                PNMBrush.BELighten
        ]:
            self.brush = PNMBrush.makeSpot(color, size, smooth, effect)
            #if self.paintModel:
            if self.painter:
                self.painter.setPen(self.brush)

    def getBrushSettings(self):
        return self.paintColor, self.paintSize, self.paintSmooth, self.paintEffect

    def setPaintMode(self, newMode):
        self.paintMode = newMode
        # clear last point if mode changed
        if newMode == TEXTUREPAINTER_FUNCTION_PAINT_POINT or \
           newMode == TEXTUREPAINTER_FUNCTION_READ:
            self.lastPoint = None

    def getPaintMode(self):
        return self.paintMode

    def __enableEditor(self):
        ''' create the background rendering etc., but the model is not yet defined '''
        print "I: TexturePainter.__enableEditor"
        # the buffer the model with the color texture is rendered into
        self.modelColorBuffer = None
        self.modelColorCam = None
        # the buffer the picked position color is rendered into
        self.colorPickerBuffer = None
        self.colorPickerCam = None
        # create the buffers
        self.__createBuffer()

        # when the window is resized, the background buffer etc must be updated.
        self.accept("window-event", self.__windowEvent)

        # some debugging stuff
        self.accept("v", base.bufferViewer.toggleEnable)
        self.accept("V", base.bufferViewer.toggleEnable)

    def __disableEditor(self):
        print "I: TexturePainter.__disableEditor"

        self.__destroyBuffer()

        # ignore window-event and debug
        self.ignoreAll()

    def __windowEvent(self, win=None):
        ''' when the editor is enabled, update the buffers etc. when the window
    is resized '''
        print "I: TexturePainter.windowEvent"

        # with a fixed backgroudn buffer size this is not needed anymore

        if False:
            #if self.texturePainterStatus != TEXTURE_PAINTER_STATUS_DISABLED:
            if self.modelColorBuffer:
                if WindowManager.activeWindow:
                    # on window resize there seems to be never a active window
                    win = WindowManager.activeWindow.win
                else:
                    win = base.win
                if self.modelColorBuffer.getXSize() != win.getXSize(
                ) or self.modelColorBuffer.getYSize() != win.getYSize():
                    '''print "  - window resized",\
              self.modelColorBuffer.getXSize(),\
              win.getXSize(),\
              self.modelColorBuffer.getYSize(),\
              win.getYSize()'''
                    # if the buffer size doesnt match the window size (window has been resized)
                    self.__destroyBuffer()
                    self.__createBuffer()
                    self.__updateModel()
            else:
                print "W: TexturePainter.__windowEvent: no buffer"
                self.__createBuffer()

    def __createBuffer(self):
        ''' create the buffer we render in the background into '''
        print "I: TexturePainter.__createBuffer"
        # the window has been modified
        if WindowManager.activeWindow:
            # on window resize there seems to be never a active window
            win = WindowManager.activeWindow.win
        else:
            win = base.win

        # get the window size
        self.windowSizeX = win.getXSize()
        self.windowSizeY = win.getYSize()

        # create a buffer in which we render the model using a shader
        self.paintMap = Texture()
        # 1.5.4 cant handle non power of 2 buffers
        self.modelColorBuffer = createOffscreenBuffer(
            -3, TEXTUREPAINTER_BACKGROUND_BUFFER_RENDERSIZE[0],
            TEXTUREPAINTER_BACKGROUND_BUFFER_RENDERSIZE[1]
        )  #self.windowSizeX, self.windowSizeY)
        self.modelColorBuffer.addRenderTexture(self.paintMap,
                                               GraphicsOutput.RTMBindOrCopy,
                                               GraphicsOutput.RTPColor)
        self.modelColorCam = base.makeCamera(self.modelColorBuffer,
                                             lens=base.cam.node().getLens(),
                                             sort=1)

        # Create a small buffer for the shader program that will fetch the point from the texture made
        # by the self.modelColorBuffer
        self.colorPickerImage = PNMImage()
        self.colorPickerTex = Texture()
        self.colorPickerBuffer = base.win.makeTextureBuffer(
            "color picker buffer", 2, 2, self.colorPickerTex, True)

        self.colorPickerScene = NodePath('color picker scene')

        self.colorPickerCam = base.makeCamera(self.colorPickerBuffer,
                                              lens=base.cam.node().getLens(),
                                              sort=2)
        self.colorPickerCam.reparentTo(self.colorPickerScene)
        self.colorPickerCam.setY(-2)

        cm = CardMaker('color picker scene card')
        cm.setFrameFullscreenQuad()
        pickerCard = self.colorPickerScene.attachNewNode(cm.generate())

        loadPicker = NodePath(PandaNode('pointnode'))
        loadPicker.setShader(Shader.make(COLOR_PICKER_SHADER), 10001)

        # Feed the paintmap from the paintBuffer to the shader and initial mouse positions
        self.colorPickerScene.setShaderInput('paintmap', self.paintMap)
        self.colorPickerScene.setShaderInput('mousepos', 0, 0, 0, 1)
        self.colorPickerCam.node().setInitialState(loadPicker.getState())

    def __destroyBuffer(self):
        print "I: TexturePainter.__destroyBuffer"
        if self.modelColorBuffer:

            # Destroy the buffer
            base.graphicsEngine.removeWindow(self.modelColorBuffer)
            self.modelColorBuffer = None
            # Remove the camera
            self.modelColorCam.removeNode()
            del self.modelColorCam

            self.colorPickerScene.removeNode()
            del self.colorPickerScene
            # remove cam
            self.colorPickerCam.removeNode()
            del self.colorPickerCam
            # Destroy the buffer
            base.graphicsEngine.removeWindow(self.colorPickerBuffer)
            self.colorPickerBuffer = None

            del self.colorPickerTex
            del self.colorPickerImage

    def __startEditor(self,
                      editModel,
                      editTexture,
                      backgroundShader=MODEL_COLOR_SHADER):
        print "I: TexturePainter.__startEditor"

        # this is needed as on startup the editor may not have had a window etc.
        self.__windowEvent()

        if not editModel or not editTexture:
            print "W: TexturePainter.__startEditor: model or texture invalid", editModel, editTexture
            return False

        self.editModel = editModel
        self.editTexture = editTexture
        self.editImage = None
        self.backgroundShader = backgroundShader

        if type(self.editTexture) == Texture:
            # if the image to modify is a texture, create a pnmImage which we modify
            self.editImage = PNMImage()
            # copy the image from the texture to the working layer
            self.editTexture.store(self.editImage)
        else:
            self.editImage = self.editTexture

        # create the brush for painting
        self.painter = PNMPainter(self.editImage)
        self.setBrushSettings(*self.getBrushSettings())

        self.__updateModel()

        # start edit
        messenger.send(EVENT_TEXTUREPAINTER_STARTEDIT)

        for startEvent in TEXTUREPAINTER_START_PAINT_EVENTS:
            self.accept(startEvent, self.__startPaint)
        for stopEvent in TEXTUREPAINTER_STOP_PAINT_EVENTS:
            self.accept(stopEvent, self.__stopPaint)

        self.modelColorCam.node().copyLens(
            WindowManager.activeWindow.camera.node().getLens())

        taskMgr.add(self.__paintTask, 'paintTask')

        #modelModificator.toggleEditmode(False)

        self.isPainting = False

    def __stopEditor(self):
        print "I: TexturePainter.__stopEditor"
        for startEvent in TEXTUREPAINTER_START_PAINT_EVENTS:
            self.ignore(startEvent)
        for stopEvent in TEXTUREPAINTER_STOP_PAINT_EVENTS:
            self.ignore(stopEvent)

        taskMgr.remove('paintTask')
        # stop edit end

        # must be reset before we loose the properties
        if self.editModel and self.editTexture and self.editImage:
            try:
                # hide the model from cam 2
                self.editModel.hide(BitMask32.bit(1))
                self.editModel = None
            except:
                print "E: TexturePainter.__stopEditor: the model has already been deleted"

        # stop edit
        messenger.send(EVENT_TEXTUREPAINTER_STOPEDIT)

        self.editImage = None
        self.editTexture = None
        self.painter = None
        self.brush = None

        #modelModificator.toggleEditmode(True)

    def __updateModel(self):
        if self.editModel:
            # create a image with the same size of the texture
            textureSize = (self.editTexture.getXSize(),
                           self.editTexture.getYSize())

            # create a dummy node, where we setup the parameters for the background rendering
            loadPaintNode = NodePath(PandaNode('paintnode'))
            loadPaintNode.setShader(Shader.make(self.backgroundShader), 10001)
            loadPaintNode.setShaderInput('texsize', textureSize[0],
                                         textureSize[1], 0, 0)

            # copy the state onto the camera
            self.modelColorCam.node().setInitialState(loadPaintNode.getState())

            # the camera gets a special bitmask, to show/hide models from it
            self.modelColorCam.node().setCameraMask(BitMask32.bit(1))

            if False:
                # doesnt work, but would be nicer (not messing with the default render state)

                hiddenNode = NodePath(PandaNode('hiddennode'))
                hiddenNode.hide(BitMask32.bit(1))
                showTroughNode = NodePath(PandaNode('showtroughnode'))
                showTroughNode.showThrough(BitMask32.bit(1))

                self.modelColorCam.node().setTagStateKey(
                    'show-on-backrender-cam')
                self.modelColorCam.node().setTagState('False',
                                                      hiddenNode.getState())
                self.modelColorCam.node().setTagState(
                    'True', showTroughNode.getState())

                render.setTag('show-on-backrender-cam', 'False')
                self.editModel.setTag('show-on-backrender-cam', 'True')
            else:
                # make only the model visible to the background camera
                render.hide(BitMask32.bit(1))
                self.editModel.showThrough(BitMask32.bit(1))

    # --- start the paint tasks ---
    def __startPaint(self):
        self.isPainting = True

    def __stopPaint(self):
        self.isPainting = False

    # --- modification tasks ---
    def __textureUpdateTask(self, task=None):
        ''' modify the texture using the edited image
    '''
        if type(self.editTexture) == Texture:
            self.editTexture.load(self.editImage)

        if task:  # task may be None
            return task.again

    def __paintTask(self, task):
        #print "I: TexturePainter.__paintTask:"

        if not WindowManager.activeWindow or not WindowManager.activeWindow.mouseWatcherNode.hasMouse(
        ):
            '''print "  - abort:", WindowManager.activeWindow
      if WindowManager.activeWindow:
        print "  - mouse:", WindowManager.activeWindow.mouseWatcherNode.hasMouse()'''
            return task.cont

        # update the camera according to the active camera
        #self.modelColorCam.setMat(render, WindowManager.activeWindow.camera.getMat(render))

        mpos = base.mouseWatcherNode.getMouse()
        x_ratio = min(max(((mpos.getX() + 1) / 2), 0), 1)
        y_ratio = min(max(((mpos.getY() + 1) / 2), 0), 1)
        mx = int(x_ratio * self.windowSizeX)
        my = self.windowSizeY - int(y_ratio * self.windowSizeY)

        self.colorPickerScene.setShaderInput('mousepos', x_ratio, y_ratio, 0,
                                             1)

        if self.colorPickerTex.hasRamImage():
            self.colorPickerTex.store(self.colorPickerImage)

            # get the color below the mousepick from the rendered frame
            r = self.colorPickerImage.getRedVal(0, 0)
            g = self.colorPickerImage.getGreenVal(0, 0)
            b = self.colorPickerImage.getBlueVal(0, 0)
            # calculate uv-texture position from the color
            x = r + ((b % 16) * 256)
            y = g + ((b // 16) * 256)

            if self.isPainting:
                self.__paintPixel(x, y)
                self.__textureUpdateTask()
        else:
            # this might happen if no frame has been rendered yet since creation of the texture
            print "W: TexturePainter.__paintTask: colorPickerTex.hasRamMipmapImage() =", self.colorPickerTex.hasRamImage(
            )

        return task.cont

    def __paintPixel(self, x, y):
        ''' paint at x/y with the defined settings '''

        imageMaxX = self.editImage.getXSize()
        imageMaxY = self.editImage.getYSize()

        def inImage(x, y):
            ''' is the given x/y position within the image '''
            return ((imageMaxX > x >= 0) and (imageMaxY > y >= 0))

        # how smooth should be painted
        if self.paintSmooth:
            # a smooth brush
            hardness = 1.0
        else:
            # a hard brush
            hardness = 0.1
        hardness = min(1.0, max(0.05, hardness))

        # the paint radius
        radius = int(round(self.paintSize / 2.0))
        radiusSquare = float(radius * radius)

        # a function to get the brush color/strength, depending on the radius
        def getBrushColor(diffPosX, diffPosY):
            distance = diffPosX**2 + diffPosY**2
            brushStrength = (
                1 - (min(distance, radiusSquare) / radiusSquare)) / hardness
            return min(1.0, max(0.0, brushStrength))

        if inImage(x, y):
            if self.paintMode == TEXTUREPAINTER_FUNCTION_PAINT_POINT:
                if self.paintEffect in [
                        PNMBrush.BESet, PNMBrush.BEBlend, PNMBrush.BEDarken,
                        PNMBrush.BELighten
                ]:
                    # render a spot into the texture
                    self.painter.drawPoint(x, y)

                elif self.paintEffect in [
                        TEXTUREPAINTER_BRUSH_FLATTEN,
                        TEXTUREPAINTER_BRUSH_SMOOTH,
                        TEXTUREPAINTER_BRUSH_RANDOMIZE
                ]:

                    if self.paintEffect == TEXTUREPAINTER_BRUSH_SMOOTH:
                        # calculate average values
                        data = dict()
                        smoothRadius = 2
                        for dx in xrange(-radius, radius + 1):
                            for dy in xrange(-radius, radius + 1):
                                if inImage(x + dx, y + dy):
                                    average = VBase4D(0)
                                    dividor = 0
                                    for px in xrange(-smoothRadius,
                                                     smoothRadius + 1):
                                        for py in xrange(
                                                -smoothRadius,
                                                smoothRadius + 1):
                                            if inImage(x + dx + px,
                                                       y + dy + py):
                                                average += self.editImage.getXelA(
                                                    x + dx + px, y + dy + py)
                                                dividor += 1
                                    average /= float(dividor)
                                    data[(x + dx, y + dy)] = average

                        # save to image
                        for (px, py), newValue in data.items():
                            currentValue = self.editImage.getXelA(px, py)
                            diffValue = currentValue - newValue

                            dx = px - x
                            dy = py - y

                            multiplier = getBrushColor(dx, dy)
                            print dx, dy, multiplier
                            '''if self.paintSmooth:
                multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
              else:
                # not sure if this is correct
                multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                            '''r = currentValue.getX() * (1-multiplier*self.paintColor.getX()) + diffValue.getX() * multiplier*self.paintColor.getX()
              g = currentValue.getY() * (1-multiplier*self.paintColor.getY()) + diffValue.getY() * multiplier*self.paintColor.getY()
              b = currentValue.getZ() * (1-multiplier*self.paintColor.getZ()) + diffValue.getZ() * multiplier*self.paintColor.getZ()
              a = currentValue.getW() * (1-multiplier*self.paintColor.getW()) + diffValue.getW() * multiplier*self.paintColor.getW()'''
                            r = currentValue.getX(
                            ) - multiplier * diffValue.getX()
                            g = currentValue.getY(
                            ) - multiplier * diffValue.getY()
                            b = currentValue.getZ(
                            ) - multiplier * diffValue.getZ()
                            a = currentValue.getW(
                            ) - multiplier * diffValue.getW()
                            if self.editImage.hasAlpha():
                                self.editImage.setXelA(px, py,
                                                       VBase4D(r, g, b, a))
                            else:
                                self.editImage.setXel(px, py, VBase3D(r, g, b))

                            #self.editImage.setXelA(x,y,value)

                    if self.paintEffect == TEXTUREPAINTER_BRUSH_FLATTEN:
                        dividor = 0
                        average = VBase4D(0)
                        for dx in xrange(-radius, radius + 1):
                            for dy in xrange(-radius, radius + 1):
                                if inImage(x + dx, y + dy):
                                    multiplier = getBrushColor(dx, dy)
                                    '''if self.paintSmooth:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
                  else:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                                    dividor += multiplier
                                    average += self.editImage.getXelA(
                                        x + dx, y + dy) * multiplier
                        average /= dividor
                        for dx in xrange(-radius, radius + 1):
                            for dy in xrange(-radius, radius + 1):
                                if inImage(x + dx, y + dy):
                                    multiplier = getBrushColor(dx, dy)
                                    '''if self.paintSmooth:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
                  else:
                    # not sure if this is correct
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                                    currentValue = self.editImage.getXelA(
                                        x + dx, y + dy)
                                    r = currentValue.getX() * (
                                        1 -
                                        multiplier * self.paintColor.getX()
                                    ) + average.getX(
                                    ) * multiplier * self.paintColor.getX()
                                    g = currentValue.getY() * (
                                        1 -
                                        multiplier * self.paintColor.getY()
                                    ) + average.getY(
                                    ) * multiplier * self.paintColor.getY()
                                    b = currentValue.getZ() * (
                                        1 -
                                        multiplier * self.paintColor.getZ()
                                    ) + average.getZ(
                                    ) * multiplier * self.paintColor.getZ()
                                    a = currentValue.getW() * (
                                        1 -
                                        multiplier * self.paintColor.getW()
                                    ) + average.getW(
                                    ) * multiplier * self.paintColor.getW()
                                    if self.editImage.hasAlpha():
                                        self.editImage.setXelA(
                                            x + dx, y + dy,
                                            VBase4D(r, g, b, a))
                                    else:
                                        self.editImage.setXel(
                                            x + dx, y + dy, VBase3D(r, g, b))

                    elif self.paintEffect == TEXTUREPAINTER_BRUSH_RANDOMIZE:
                        for dx in xrange(-radius, radius + 1):
                            for dy in xrange(-radius, radius + 1):
                                if inImage(x + dx, y + dy):
                                    r = VBase4D(
                                        random.random() *
                                        self.paintColor.getX() -
                                        self.paintColor.getX() / 2.,
                                        random.random() *
                                        self.paintColor.getY() -
                                        self.paintColor.getY() / 2.,
                                        random.random() *
                                        self.paintColor.getZ() -
                                        self.paintColor.getZ() / 2.,
                                        random.random() *
                                        self.paintColor.getW() -
                                        self.paintColor.getW() / 2.)
                                    multiplier = getBrushColor(dx, dy)
                                    '''if self.paintSmooth:
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy))) / (radius*radius)
                  else:
                    # not sure if this is correct
                    multiplier = ((radius-math.fabs(dx))*(radius-math.fabs(dy)))'''
                                    currentValue = self.editImage.getXelA(
                                        x + dx, y + dy)
                                    self.editImage.setXelA(
                                        x + dx, y + dy,
                                        currentValue + r * multiplier)

            elif self.paintMode == TEXTUREPAINTER_FUNCTION_READ:
                if inImage(x, y):
                    col = self.editImage.getXelA(x, y)
                    if self.editImage.hasAlpha():
                        self.paintColor = VBase4D(col[0], col[1], col[2],
                                                  col[3])
                    else:
                        self.paintColor = VBase4D(col[0], col[1], col[2], 1.0)
                    messenger.send(EVENT_TEXTUREPAINTER_BRUSHCHANGED)

            elif self.paintMode == TEXTUREPAINTER_FUNCTION_PAINT_LINE:
                if self.lastPoint != None:
                    self.painter.drawLine(x, y, self.lastPoint[0],
                                          self.lastPoint[1])

            elif self.paintMode == TEXTUREPAINTER_FUNCTION_PAINT_RECTANGLE:
                if self.lastPoint != None:
                    self.painter.drawRectangle(x, y, self.lastPoint[0],
                                               self.lastPoint[1])

            self.lastPoint = (x, y)