def __init__(self,
                 scene=base.render,
                 ambient=0.2,
                 hardness=16,
                 fov=40,
                 near=10,
                 far=100):
        """Create an instance of this class to initiate shadows.
    Also, a shadow casting 'light' is created when this class is called.
    The first parameter is the nodepath in the scene where you
    want to apply your shadows on, by default this is render."""

        # Read and store the function parameters
        self.scene = scene
        self.__ambient = ambient
        self.__hardness = hardness

        # By default, mark every object as textured.
        self.flagTexturedObject(self.scene)

        # Create the buffer plus a texture to store the output in
        buffer = createOffscreenBuffer(-3)
        depthmap = Texture()
        buffer.addRenderTexture(depthmap, GraphicsOutput.RTMBindOrCopy,
                                GraphicsOutput.RTPColor)

        # Set the shadow filter if it is supported
        if (base.win.getGsg().getSupportsShadowFilter()):
            depthmap.setMinfilter(Texture.FTShadow)
            depthmap.setMagfilter(Texture.FTShadow)

        # Make the camera
        self.light = base.makeCamera(buffer)
        self.light.node().setScene(self.scene)
        self.light.node().getLens().setFov(fov)
        self.light.node().getLens().setNearFar(near, far)

        # Put a shader on the Light camera.
        lci = NodePath(PandaNode("lightCameraInitializer"))
        lci.setShader(loader.loadShader("caster.sha"))
        self.light.node().setInitialState(lci.getState())

        # Put a shader on the Main camera.
        mci = NodePath(PandaNode("mainCameraInitializer"))
        mci.setShader(loader.loadShader("softshadow.sha"))
        base.cam.node().setInitialState(mci.getState())

        # Set up the blurring buffers, one that blurs horizontally, the other vertically
        #blurXBuffer = makeFilterBuffer(buffer, "Blur X", -2, loader.loadShader("blurx.sha"))
        #blurYBuffer = makeFilterBuffer(blurXBuffer, "Blur Y", -1, loader.loadShader("blury.sha"))

        # Set the shader inputs
        self.scene.setShaderInput("light", self.light)
        #self.scene.setShaderInput("depthmap", blurYBuffer.getTexture())
        self.scene.setShaderInput("depthmap", buffer.getTexture())
        self.scene.setShaderInput("props", ambient, hardness, 0, 1)
    def __init__(self, scene=base.render, ambient=0.2, hardness=16, fov=40, near=10, far=100):
        """Create an instance of this class to initiate shadows.
    Also, a shadow casting 'light' is created when this class is called.
    The first parameter is the nodepath in the scene where you
    want to apply your shadows on, by default this is render."""

        # Read and store the function parameters
        self.scene = scene
        self.__ambient = ambient
        self.__hardness = hardness

        # By default, mark every object as textured.
        self.flagTexturedObject(self.scene)

        # Create the buffer plus a texture to store the output in
        buffer = createOffscreenBuffer(-3)
        depthmap = Texture()
        buffer.addRenderTexture(depthmap, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)

        # Set the shadow filter if it is supported
        if base.win.getGsg().getSupportsShadowFilter():
            depthmap.setMinfilter(Texture.FTShadow)
            depthmap.setMagfilter(Texture.FTShadow)

        # Make the camera
        self.light = base.makeCamera(buffer)
        self.light.node().setScene(self.scene)
        self.light.node().getLens().setFov(fov)
        self.light.node().getLens().setNearFar(near, far)

        # Put a shader on the Light camera.
        lci = NodePath(PandaNode("lightCameraInitializer"))
        lci.setShader(loader.loadShader("caster.sha"))
        self.light.node().setInitialState(lci.getState())

        # Put a shader on the Main camera.
        mci = NodePath(PandaNode("mainCameraInitializer"))
        mci.setShader(loader.loadShader("softshadow.sha"))
        base.cam.node().setInitialState(mci.getState())

        # Set up the blurring buffers, one that blurs horizontally, the other vertically
        # blurXBuffer = makeFilterBuffer(buffer, "Blur X", -2, loader.loadShader("blurx.sha"))
        # blurYBuffer = makeFilterBuffer(blurXBuffer, "Blur Y", -1, loader.loadShader("blury.sha"))

        # Set the shader inputs
        self.scene.setShaderInput("light", self.light)
        # self.scene.setShaderInput("depthmap", blurYBuffer.getTexture())
        self.scene.setShaderInput("depthmap", buffer.getTexture())
        self.scene.setShaderInput("props", ambient, hardness, 0, 1)
Example #3
0
class Sprite2d:
    class Cell:
        def __init__(self, col, row):
            self.col = col
            self.row = row

        def __str__(self):
            return "Cell - Col %d, Row %d" % (self.col, self.row)

    class Animation:
        def __init__(self, cells, fps):
            self.cells = cells
            self.fps = fps
            self.playhead = 0

    ALIGN_CENTER = "Center"
    ALIGN_LEFT = "Left"
    ALIGN_RIGHT = "Right"
    ALIGN_BOTTOM = "Bottom"
    ALIGN_TOP = "Top"

    TRANS_ALPHA = TransparencyAttrib.MAlpha
    TRANS_DUAL = TransparencyAttrib.MDual
    # One pixel is divided by this much. If you load a 100x50 image with PIXEL_SCALE of 10.0
    # you get a card that is 1 unit wide, 0.5 units high
    PIXEL_SCALE = 20.0

    def __init__(self, image_path, rowPerFace, name=None,\
         rows=1, cols=1, scale=1.0,\
         twoSided=False, alpha=TRANS_ALPHA,\
         repeatX=1, repeatY=1,\
         anchorX=ALIGN_CENTER, anchorY=ALIGN_BOTTOM):
        """
		Create a card textured with an image. The card is sized so that the ratio between the
		card and image is the same.
		"""

        global SpriteId
        self.spriteNum = str(SpriteId)
        SpriteId += 1

        scale *= self.PIXEL_SCALE

        self.animations = {}

        self.scale = scale
        self.repeatX = repeatX
        self.repeatY = repeatY
        self.flip = {'x': False, 'y': False}
        self.rows = rows
        self.cols = cols

        self.currentFrame = 0
        self.currentAnim = None
        self.loopAnim = False
        self.frameInterrupt = True

        # Create the NodePath
        if name:
            self.node = NodePath("Sprite2d:%s" % name)
        else:
            self.node = NodePath("Sprite2d:%s" % image_path)

        # Set the attribute for transparency/twosided
        self.node.node().setAttrib(TransparencyAttrib.make(alpha))
        if twoSided:
            self.node.setTwoSided(True)

        # Make a filepath
        self.imgFile = Filename(image_path)
        if self.imgFile.empty():
            raise IOError, "File not found"

        # Instead of loading it outright, check with the PNMImageHeader if we can open
        # the file.
        imgHead = PNMImageHeader()
        if not imgHead.readHeader(self.imgFile):
            raise IOError, "PNMImageHeader could not read file. Try using absolute filepaths"

        # Load the image with a PNMImage
        image = PNMImage()
        image.read(self.imgFile)

        self.sizeX = image.getXSize()
        self.sizeY = image.getYSize()

        # We need to find the power of two size for the another PNMImage
        # so that the texture thats loaded on the geometry won't have artifacts
        textureSizeX = self.nextsize(self.sizeX)
        textureSizeY = self.nextsize(self.sizeY)

        # The actual size of the texture in memory
        self.realSizeX = textureSizeX
        self.realSizeY = textureSizeY

        self.paddedImg = PNMImage(textureSizeX, textureSizeY)
        if image.hasAlpha():
            self.paddedImg.alphaFill(0)
        # Copy the source image to the image we're actually using
        self.paddedImg.blendSubImage(image, 0, 0)
        # We're done with source image, clear it
        image.clear()

        # The pixel sizes for each cell
        self.colSize = self.sizeX / self.cols
        self.rowSize = self.sizeY / self.rows

        # How much padding the texture has
        self.paddingX = textureSizeX - self.sizeX
        self.paddingY = textureSizeY - self.sizeY

        # Set UV padding
        self.uPad = float(self.paddingX) / textureSizeX
        self.vPad = float(self.paddingY) / textureSizeY

        # The UV dimensions for each cell
        self.uSize = (1.0 - self.uPad) / self.cols
        self.vSize = (1.0 - self.vPad) / self.rows

        self.cards = []
        self.rowPerFace = rowPerFace
        for i in range(len(rowPerFace)):
            card = CardMaker("Sprite2d-Geom")

            # The positions to create the card at
            if anchorX == self.ALIGN_LEFT:
                posLeft = 0
                posRight = (self.colSize / scale) * repeatX
            elif anchorX == self.ALIGN_CENTER:
                posLeft = -(self.colSize / 2.0 / scale) * repeatX
                posRight = (self.colSize / 2.0 / scale) * repeatX
            elif anchorX == self.ALIGN_RIGHT:
                posLeft = -(self.colSize / scale) * repeatX
                posRight = 0

            if anchorY == self.ALIGN_BOTTOM:
                posTop = 0
                posBottom = (self.rowSize / scale) * repeatY
            elif anchorY == self.ALIGN_CENTER:
                posTop = -(self.rowSize / 2.0 / scale) * repeatY
                posBottom = (self.rowSize / 2.0 / scale) * repeatY
            elif anchorY == self.ALIGN_TOP:
                posTop = -(self.rowSize / scale) * repeatY
                posBottom = 0

            card.setFrame(posLeft, posRight, posTop, posBottom)
            card.setHasUvs(True)
            self.cards.append(self.node.attachNewNode(card.generate()))
            self.cards[-1].setH(i * 360 / len(rowPerFace))

        # Since the texture is padded, we need to set up offsets and scales to make
        # the texture fit the whole card
        self.offsetX = (float(self.colSize) / textureSizeX)
        self.offsetY = (float(self.rowSize) / textureSizeY)

        # self.node.setTexScale(TextureStage.getDefault(), self.offsetX * repeatX, self.offsetY * repeatY)
        # self.node.setTexOffset(TextureStage.getDefault(), 0, 1-self.offsetY)

        self.texture = Texture()

        self.texture.setXSize(textureSizeX)
        self.texture.setYSize(textureSizeY)
        self.texture.setZSize(1)

        # Load the padded PNMImage to the texture
        self.texture.load(self.paddedImg)

        self.texture.setMagfilter(Texture.FTNearest)
        self.texture.setMinfilter(Texture.FTNearest)

        #Set up texture clamps according to repeats
        if repeatX > 1:
            self.texture.setWrapU(Texture.WMRepeat)
        else:
            self.texture.setWrapU(Texture.WMClamp)
        if repeatY > 1:
            self.texture.setWrapV(Texture.WMRepeat)
        else:
            self.texture.setWrapV(Texture.WMClamp)

        self.node.setTexture(self.texture)
        self.setFrame(0)

    def nextsize(self, num):
        """ Finds the next power of two size for the given integer. """
        p2x = max(1, log(num, 2))
        notP2X = modf(p2x)[0] > 0
        return 2**int(notP2X + p2x)

    def setFrame(self, frame=0):
        """ Sets the current sprite to the given frame """
        self.frameInterrupt = True  # A flag to tell the animation task to shut it up ur face
        self.currentFrame = frame
        self.flipTexture()

    def playAnim(self, animName, loop=False):
        """ Sets the sprite to animate the given named animation. Booleon to loop animation"""
        if not taskMgr.hasTaskNamed("Animate sprite" + self.spriteNum):
            if hasattr(self, "task"):
                taskMgr.remove("Animate sprite" + self.spriteNum)
                del self.task
            self.frameInterrupt = False  # Clear any previous interrupt flags
            self.loopAnim = loop
            self.currentAnim = self.animations[animName]
            self.currentAnim.playhead = 0
            self.task = taskMgr.doMethodLater(
                1.0 / self.currentAnim.fps, self.animPlayer,
                "Animate sprite" + self.spriteNum)

    def createAnim(self, animName, frameCols, fps=12):
        """ Create a named animation. Takes the animation name and a tuple of frame numbers """
        self.animations[animName] = Sprite2d.Animation(frameCols, fps)
        return self.animations[animName]

    def flipX(self, val=None):
        """ Flip the sprite on X. If no value given, it will invert the current flipping."""
        if val:
            self.flip['x'] = val
        else:
            if self.flip['x']:
                self.flip['x'] = False
            else:
                self.flip['x'] = True
        self.flipTexture()
        return self.flip['x']

    def flipY(self, val=None):
        """ See flipX """
        if val:
            self.flip['y'] = val
        else:
            if self.flip['y']:
                self.flip['y'] = False
            else:
                self.flip['y'] = True
        self.flipTexture()
        return self.flip['y']

    def updateCameraAngle(self, cameraNode):
        baseH = cameraNode.getH(render) - self.node.getH(render)
        degreesBetweenCards = 360 / len(self.cards)
        bestCard = int(
            ((baseH) + degreesBetweenCards / 2) % 360 / degreesBetweenCards)
        #print baseH, bestCard
        for i in range(len(self.cards)):
            if i == bestCard:
                self.cards[i].show()
            else:
                self.cards[i].hide()

    def flipTexture(self):
        """ Sets the texture coordinates of the texture to the current frame"""
        for i in range(len(self.cards)):
            currentRow = self.rowPerFace[i]

            sU = self.offsetX * self.repeatX
            sV = self.offsetY * self.repeatY
            oU = 0 + self.currentFrame * self.uSize
            #oU = 0 + self.frames[self.currentFrame].col * self.uSize
            #oV = 1 - self.frames[self.currentFrame].row * self.vSize - self.offsetY
            oV = 1 - currentRow * self.vSize - self.offsetY
            if self.flip['x'] ^ i == 1:  ##hack to fix side view
                #print "flipping, i = ",i
                sU *= -1
                #oU = self.uSize + self.frames[self.currentFrame].col * self.uSize
                oU = self.uSize + self.currentFrame * self.uSize
            if self.flip['y']:
                sV *= -1
                #oV = 1 - self.frames[self.currentFrame].row * self.vSize
                oV = 1 - currentRow * self.vSize
            self.cards[i].setTexScale(TextureStage.getDefault(), sU, sV)
            self.cards[i].setTexOffset(TextureStage.getDefault(), oU, oV)

    def clear(self):
        """ Free up the texture memory being used """
        self.texture.clear()
        self.paddedImg.clear()
        self.node.removeNode()

    def animPlayer(self, task):
        if self.frameInterrupt:
            return task.done
        #print "Playing",self.currentAnim.cells[self.currentAnim.playhead]
        self.currentFrame = self.currentAnim.cells[self.currentAnim.playhead]
        self.flipTexture()
        if self.currentAnim.playhead + 1 < len(self.currentAnim.cells):
            self.currentAnim.playhead += 1
            return task.again
        if self.loopAnim:
            self.currentAnim.playhead = 0
            return task.again
Example #4
0
class Sprite2d:

	class Cell:
		def __init__(self, col, row):
			self.col = col
			self.row = row

		def __str__(self):
			return "Cell - Col %d, Row %d" % (self.col, self.row)

	class Animation:
		def __init__(self, cells, fps):
			self.cells = cells
			self.fps = fps
			self.playhead = 0

	ALIGN_CENTER = "Center"
	ALIGN_LEFT = "Left"
	ALIGN_RIGHT = "Right"
	ALIGN_BOTTOM = "Bottom"
	ALIGN_TOP = "Top"

	TRANS_ALPHA = TransparencyAttrib.MAlpha
	TRANS_DUAL = TransparencyAttrib.MDual
	# One pixel is divided by this much. If you load a 100x50 image with PIXEL_SCALE of 10.0
	# you get a card that is 1 unit wide, 0.5 units high
	PIXEL_SCALE = 20.0

	def __init__(self, image_path, rowPerFace, name=None,\
				  rows=1, cols=1, scale=1.0,\
				  twoSided=False, alpha=TRANS_ALPHA,\
				  repeatX=1, repeatY=1,\
				  anchorX=ALIGN_CENTER, anchorY=ALIGN_BOTTOM):
		"""
		Create a card textured with an image. The card is sized so that the ratio between the
		card and image is the same.
		"""

		global SpriteId
		self.spriteNum = str(SpriteId)
		SpriteId += 1

		scale *= self.PIXEL_SCALE

		self.animations = {}

		self.scale = scale
		self.repeatX = repeatX
		self.repeatY = repeatY
		self.flip = {'x':False,'y':False}
		self.rows = rows
		self.cols = cols

		self.currentFrame = 0
		self.currentAnim = None
		self.loopAnim = False
		self.frameInterrupt = True

		# Create the NodePath
		if name:
			self.node = NodePath("Sprite2d:%s" % name)
		else:
			self.node = NodePath("Sprite2d:%s" % image_path)

		# Set the attribute for transparency/twosided
		self.node.node().setAttrib(TransparencyAttrib.make(alpha))
		if twoSided:
			self.node.setTwoSided(True)

		# Make a filepath
		self.imgFile = Filename(image_path)
		if self.imgFile.empty():
			raise IOError, "File not found"

		# Instead of loading it outright, check with the PNMImageHeader if we can open
		# the file.
		imgHead = PNMImageHeader()
		if not imgHead.readHeader(self.imgFile):
			raise IOError, "PNMImageHeader could not read file. Try using absolute filepaths"

		# Load the image with a PNMImage
		image = PNMImage()
		image.read(self.imgFile)

		self.sizeX = image.getXSize()
		self.sizeY = image.getYSize()

		# We need to find the power of two size for the another PNMImage
		# so that the texture thats loaded on the geometry won't have artifacts
		textureSizeX = self.nextsize(self.sizeX)
		textureSizeY = self.nextsize(self.sizeY)

		# The actual size of the texture in memory
		self.realSizeX = textureSizeX
		self.realSizeY = textureSizeY

		self.paddedImg = PNMImage(textureSizeX, textureSizeY)
		if image.hasAlpha():
			self.paddedImg.alphaFill(0)
		# Copy the source image to the image we're actually using
		self.paddedImg.blendSubImage(image, 0, 0)
		# We're done with source image, clear it
		image.clear()

		# The pixel sizes for each cell
		self.colSize = self.sizeX/self.cols
		self.rowSize = self.sizeY/self.rows

		# How much padding the texture has
		self.paddingX = textureSizeX - self.sizeX
		self.paddingY = textureSizeY - self.sizeY

		# Set UV padding
		self.uPad = float(self.paddingX)/textureSizeX
		self.vPad = float(self.paddingY)/textureSizeY

		# The UV dimensions for each cell
		self.uSize = (1.0 - self.uPad) / self.cols
		self.vSize = (1.0 - self.vPad) / self.rows
	
		self.cards = []
		self.rowPerFace = rowPerFace
		for i in range(len(rowPerFace)):
			card = CardMaker("Sprite2d-Geom")

			# The positions to create the card at
			if anchorX == self.ALIGN_LEFT:
				posLeft = 0
				posRight = (self.colSize/scale)*repeatX
			elif anchorX == self.ALIGN_CENTER:
				posLeft = -(self.colSize/2.0/scale)*repeatX
				posRight = (self.colSize/2.0/scale)*repeatX
			elif anchorX == self.ALIGN_RIGHT:
				posLeft = -(self.colSize/scale)*repeatX
				posRight = 0

			if anchorY == self.ALIGN_BOTTOM:
				posTop = 0
				posBottom = (self.rowSize/scale)*repeatY
			elif anchorY == self.ALIGN_CENTER:
				posTop = -(self.rowSize/2.0/scale)*repeatY
				posBottom = (self.rowSize/2.0/scale)*repeatY
			elif anchorY == self.ALIGN_TOP:
				posTop = -(self.rowSize/scale)*repeatY
				posBottom = 0

			card.setFrame(posLeft, posRight, posTop, posBottom)
			card.setHasUvs(True)
			self.cards.append(self.node.attachNewNode(card.generate()))
			self.cards[-1].setH(i * 360/len(rowPerFace))

		# Since the texture is padded, we need to set up offsets and scales to make
		# the texture fit the whole card
		self.offsetX = (float(self.colSize)/textureSizeX)
		self.offsetY = (float(self.rowSize)/textureSizeY)

		# self.node.setTexScale(TextureStage.getDefault(), self.offsetX * repeatX, self.offsetY * repeatY)
		# self.node.setTexOffset(TextureStage.getDefault(), 0, 1-self.offsetY)
		
		self.texture = Texture()

		self.texture.setXSize(textureSizeX)
		self.texture.setYSize(textureSizeY)
		self.texture.setZSize(1)

		# Load the padded PNMImage to the texture
		self.texture.load(self.paddedImg)

		self.texture.setMagfilter(Texture.FTNearest)
		self.texture.setMinfilter(Texture.FTNearest)

		#Set up texture clamps according to repeats
		if repeatX > 1:
			self.texture.setWrapU(Texture.WMRepeat)
		else:
			self.texture.setWrapU(Texture.WMClamp)
		if repeatY > 1:
			self.texture.setWrapV(Texture.WMRepeat)
		else:
			self.texture.setWrapV(Texture.WMClamp)

		self.node.setTexture(self.texture)
		self.setFrame(0)

	def nextsize(self, num):
		""" Finds the next power of two size for the given integer. """
		p2x=max(1,log(num,2))
		notP2X=modf(p2x)[0]>0
		return 2**int(notP2X+p2x)

	def setFrame(self, frame=0):
		""" Sets the current sprite to the given frame """
		self.frameInterrupt = True # A flag to tell the animation task to shut it up ur face
		self.currentFrame = frame
		self.flipTexture()

	def playAnim(self, animName, loop=False):
		""" Sets the sprite to animate the given named animation. Booleon to loop animation"""
		if not taskMgr.hasTaskNamed("Animate sprite" + self.spriteNum):
			if hasattr(self, "task"):
					taskMgr.remove("Animate sprite" + self.spriteNum)
					del self.task
			self.frameInterrupt = False # Clear any previous interrupt flags
			self.loopAnim = loop
			self.currentAnim = self.animations[animName]
			self.currentAnim.playhead = 0
			self.task = taskMgr.doMethodLater(1.0/self.currentAnim.fps,self.animPlayer, "Animate sprite" + self.spriteNum)

	def createAnim(self, animName, frameCols, fps=12):
		""" Create a named animation. Takes the animation name and a tuple of frame numbers """
		self.animations[animName] = Sprite2d.Animation(frameCols, fps)
		return self.animations[animName]

	def flipX(self, val=None):
		""" Flip the sprite on X. If no value given, it will invert the current flipping."""
		if val:
			self.flip['x'] = val
		else:
			if self.flip['x']:
				self.flip['x'] = False
			else:
				self.flip['x'] = True
		self.flipTexture()
		return self.flip['x']

	def flipY(self, val=None):
		""" See flipX """
		if val:
			self.flip['y'] = val
		else:
			if self.flip['y']:
				self.flip['y'] = False
			else:
				self.flip['y'] = True
		self.flipTexture()
		return self.flip['y']

	def updateCameraAngle(self, cameraNode):
		baseH =  cameraNode.getH(render) - self.node.getH(render)
		degreesBetweenCards = 360/len(self.cards)
		bestCard = int(((baseH)+degreesBetweenCards/2)%360 / degreesBetweenCards)
		#print baseH, bestCard
		for i in range(len(self.cards)):
			if i == bestCard:
				self.cards[i].show()
			else:
				self.cards[i].hide()

	def flipTexture(self):
		""" Sets the texture coordinates of the texture to the current frame"""
		for i in range(len(self.cards)):
			currentRow = self.rowPerFace[i]

			sU = self.offsetX * self.repeatX
			sV = self.offsetY * self.repeatY
			oU = 0 + self.currentFrame * self.uSize
			#oU = 0 + self.frames[self.currentFrame].col * self.uSize
			#oV = 1 - self.frames[self.currentFrame].row * self.vSize - self.offsetY
			oV = 1 - currentRow * self.vSize - self.offsetY
			if self.flip['x'] ^ i==1: ##hack to fix side view
				#print "flipping, i = ",i
				sU *= -1
				#oU = self.uSize + self.frames[self.currentFrame].col * self.uSize
				oU = self.uSize + self.currentFrame * self.uSize
			if self.flip['y']:
				sV *= -1
				#oV = 1 - self.frames[self.currentFrame].row * self.vSize
				oV = 1 - currentRow * self.vSize
			self.cards[i].setTexScale(TextureStage.getDefault(), sU, sV)
			self.cards[i].setTexOffset(TextureStage.getDefault(), oU, oV)

	def clear(self):
		""" Free up the texture memory being used """
		self.texture.clear()
		self.paddedImg.clear()
		self.node.removeNode()

	def animPlayer(self, task):
		if self.frameInterrupt:
			return task.done
		#print "Playing",self.currentAnim.cells[self.currentAnim.playhead]
		self.currentFrame = self.currentAnim.cells[self.currentAnim.playhead]
		self.flipTexture()
		if self.currentAnim.playhead+1 < len(self.currentAnim.cells):
			self.currentAnim.playhead += 1
			return task.again
		if self.loopAnim:
			self.currentAnim.playhead = 0
			return task.again
Example #5
0
class Effect:
    baseWidth = 0
    baseHeight = 0
    effectWidth = 1
    effectHeight = 1
    effectTargetMS = 143
    noSampling = False
    tex = None
    loadedFormat = None
    # Animation variables
    internalFrameIndex = 1
    startIndex = 1
    endIndex = 1
    loopEffect = False
    # XML variables
    tree = None
    frames = None
    colors = None
    tweens = None
    compositeFrames = None
    # Nodes
    consumedNodesList = None
    # Accessible object (nodePath)
    effectCameraNodePath = None
    effectCardNodePath = None
    # Constant value; Unit comparison for card size; basis is from cure, which is [140x110]
    cardDimensionBasis = [-5.0, 5.0, 0.0, 10.0, 140.0, 110.0]
    pixelScaleX = cardDimensionBasis[4] / (cardDimensionBasis[1] - cardDimensionBasis[0])
    pixelScaleZ = cardDimensionBasis[5] / (cardDimensionBasis[3] - cardDimensionBasis[2])
    effectIsCentered = True
    effectAdjustment = [0, 0, 0]

    def __init__(self, effectFileName, parent=None, loop=False, effectIsCentered=True, effectAdjustment=[0, 0, 0]):
        self.effectAdjustment = effectAdjustment
        self.loopEffect = loop
        self.effectIsCentered = effectIsCentered
        self.loadedFormat = None
        if effectFileName != None:
            effectFileNameSplit = effectFileName.split(".")
            self.loadedFormat = effectFileNameSplit[len(effectFileNameSplit) - 2]  # Get value at penultimate index
            if self.loadedFormat == effectFileNameSplit[0]:
                self.loadedFormat = None  # Get rid of bad format name.
            pass

        # Load texture; supply alpha channel if it doesn't exist.
        p = transparencyKey(effectFileName)
        self.tex = Texture()
        self.tex.setup2dTexture(p.getXSize(), p.getYSize(), Texture.TUnsignedByte, Texture.FRgba)
        self.tex.load(p)
        if self.loadedFormat != None:
            try:
                self.tree = etree.parse("./" + GAME + "/effects/" + self.loadedFormat + "/sprite.xml")
            except IOError:
                self.loadedFormat = None
            pass
        if self.loadedFormat != None:
            root = self.tree.getroot()
            self.frames = root.find(".//frames")
            self.colors = root.find(".//colors")
            self.tweens = root.find(".//motion-tweens")
            self.compositeFrames = root.find(".//composite-frames")
            self.baseWidth = 0 if root.attrib.get("base-width") == None else float(root.attrib.get("base-width"))
            self.baseHeight = 0 if root.attrib.get("base-height") == None else float(root.attrib.get("base-height"))
            self.effectWidth = 1 if root.attrib.get("frame-width") == None else float(root.attrib.get("frame-width"))
            self.effectHeight = 1 if root.attrib.get("frame-height") == None else float(root.attrib.get("frame-height"))
            self.effectTargetMS = 143 if root.attrib.get("target-ms") == None else float(root.attrib.get("target-ms"))
            self.startIndex = 1 if root.attrib.get("target-start") == None else int(root.attrib.get("target-start"))
            self.endIndex = 1 if root.attrib.get("target-end") == None else int(root.attrib.get("target-end"))
            self.noSampling = False if root.attrib.get("no-sampling") == None else bool(root.attrib.get("no-sampling"))
            if self.noSampling == True:
                self.tex.setMagfilter(Texture.FTNearest)
                self.tex.setMinfilter(Texture.FTNearest)
            cm = CardMaker("card-" + effectFileName)
            cardDeltaX = self.effectWidth / self.pixelScaleX
            cardDeltaZ = self.effectHeight / self.pixelScaleZ
            if self.effectIsCentered == True:
                cm.setFrame(0, 0, 0, 0)
                deltaX = (cardDeltaX / 2.0) - (-cardDeltaX / 2.0)
                deltaY = 0
                deltaZ = (cardDeltaZ / 2.0) - (-cardDeltaZ / 2.0)
                # occluder = OccluderNode('effect-parent-occluder', Point3((-cardDeltaX/2.0), 0, (-cardDeltaZ/2.0)), Point3((-cardDeltaX/2.0), 0, (cardDeltaZ/2.0)), Point3((cardDeltaX/2.0), 0, (cardDeltaZ/2.0)), Point3((cardDeltaX/2.0), 0, (-cardDeltaZ/2.0)))
            else:
                cm.setFrame(0, 0, 0, 0)
                deltaX = (cardDeltaX / 2.0) - (-cardDeltaX / 2.0)
                deltaY = 0
                deltaZ = cardDeltaZ - 0
                # occluder = OccluderNode('effect-parent-occluder', Point3((-cardDeltaX/2.0), 0, 0), Point3((-cardDeltaX/2.0), 0, cardDeltaZ), Point3((cardDeltaX/2.0), 0, cardDeltaZ), Point3((cardDeltaX/2.0), 0, 0))
            self.effectCardNodePath = render.attachNewNode(cm.generate())
            self.effectCardNodePath.setBillboardPointEye()
            self.effectCardNodePath.reparentTo(parent)
            # occluder_nodepath = self.effectCardNodePath.attachNewNode(occluder)
            # self.effectCardNodePath.setOccluder(occluder_nodepath)
            emptyNode = NodePath("effect-parent-translator")
            emptyNode.reparentTo(self.effectCardNodePath)
            if effectIsCentered == True:
                emptyNode.setPos(
                    -deltaX / 2.0 + self.effectAdjustment[0],
                    0 + self.effectAdjustment[1],
                    deltaZ / 2.0 + self.effectAdjustment[2],
                )
            else:
                emptyNode.setPos(
                    -deltaX / 2.0 + self.effectAdjustment[0],
                    0 + self.effectAdjustment[1],
                    deltaZ + self.effectAdjustment[2],
                )
            # emptyNode.place()
            emptyNode.setSx(float(deltaX) / self.effectWidth)
            emptyNode.setSz(float(deltaZ) / self.effectHeight)
            self.effectCameraNodePath = emptyNode
            if parent != None:
                self.effectCardNodePath.reparentTo(parent)
            else:
                self.effectCardNodePath.reparentTo(render)
            # self.effectCardNodePath.place()
            self.effectCardNodePath.setBin("fixed", 40)
            self.effectCardNodePath.setDepthTest(False)
            self.effectCardNodePath.setDepthWrite(False)
        pass

    def getSequence(self):
        sequence = Sequence()
        for x in range(self.startIndex, self.endIndex, 1):
            sequence.append(Func(self.pandaRender))
            sequence.append(Func(self.advanceFrame))
            sequence.append(Wait(self.effectTargetMS * 0.001))
        sequence.append(Func(self.clearNodesForDrawing))
        sequence.append(Func(self.advanceFrame))
        sequence.append(Wait(self.effectTargetMS * 0.001))
        return sequence
        pass

    def hasEffectFinished(self):
        if self.internalFrameIndex > self.endIndex and self.loopEffect == False:
            return True
        else:
            return False
        pass

    def advanceFrame(self):
        if self.internalFrameIndex < self.endIndex:
            self.internalFrameIndex += 1
        elif self.internalFrameIndex == self.endIndex and self.loopEffect == True:
            self.internalFrameIndex = self.startIndex
        else:
            self.internalFrameIndex = self.endIndex + 1
            self.clearNodesForDrawing()
        pass

    def clearNodesForDrawing(self):
        if False:
            self.effectCameraNodePath.analyze()
        if self.consumedNodesList != None and self.consumedNodesList != []:
            for consumedNode in self.consumedNodesList:
                consumedNode.removeNode()
        self.consumedNodesList = []
        pass

    def pandaRender(self):
        frameList = []
        for node in self.compositeFrames.getiterator("composite-frame"):
            if node.tag == "composite-frame" and node.attrib.get("id") == str(self.internalFrameIndex):
                for frameCallNode in node:
                    for frameNode in self.frames.getiterator("frame"):
                        if frameNode.tag == "frame" and frameNode.attrib.get("id") == frameCallNode.attrib.get("id"):
                            offsetX = (
                                0
                                if frameCallNode.attrib.get("offset-x") == None
                                else float(frameCallNode.attrib.get("offset-x"))
                            )
                            offsetY = (
                                0
                                if frameCallNode.attrib.get("offset-y") == None
                                else float(frameCallNode.attrib.get("offset-y"))
                            )
                            tweenId = frameCallNode.attrib.get("tween")
                            frameInTween = (
                                0
                                if frameCallNode.attrib.get("frame-in-tween") == None
                                else int(frameCallNode.attrib.get("frame-in-tween"))
                            )
                            addWidth = 0 if frameNode.attrib.get("w") == None else float(frameNode.attrib.get("w"))
                            addHeight = 0 if frameNode.attrib.get("h") == None else float(frameNode.attrib.get("h"))
                            sInPixels = 0 if frameNode.attrib.get("s") == None else float(frameNode.attrib.get("s"))
                            tInPixels = 0 if frameNode.attrib.get("t") == None else float(frameNode.attrib.get("t"))
                            swInPixels = sInPixels + addWidth
                            thInPixels = tInPixels + addHeight
                            s = sInPixels / self.baseWidth
                            t = 1 - (
                                tInPixels / self.baseHeight
                            )  # Complemented to deal with loading image upside down.
                            S = swInPixels / self.baseWidth
                            T = 1 - (
                                thInPixels / self.baseHeight
                            )  # Complemented to deal with loading image upside down.
                            blend = (
                                "overwrite"
                                if frameCallNode.attrib.get("blend") == None
                                else frameCallNode.attrib.get("blend")
                            )
                            scaleX = (
                                1
                                if frameCallNode.attrib.get("scale-x") == None
                                else float(frameCallNode.attrib.get("scale-x"))
                            )
                            scaleY = (
                                1
                                if frameCallNode.attrib.get("scale-y") == None
                                else float(frameCallNode.attrib.get("scale-y"))
                            )
                            color = Color(1, 1, 1, 1)
                            tweenHasColor = False
                            frameCallHasColor = False
                            frameCallColorName = frameCallNode.attrib.get("color-name")
                            if frameCallColorName != None:
                                # Get color at frame call as first resort.
                                frameCallHasColor = True
                                for colorNode in self.colors.getiterator("color"):
                                    if colorNode.tag == "color" and colorNode.attrib.get("name") == frameCallColorName:
                                        R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                        G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                        B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                        A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                        color = Color(R, G, B, A)
                                        break  # leave for loop when we find the correct color
                                pass

                            if tweenId != None and tweenId != "0":
                                # Get color at tween frame as second resort.
                                thisTween = None
                                frameLength = 1
                                advancementFunction = "linear"
                                foundTween = False
                                pointList = []
                                colorList = []
                                for tweenNode in self.tweens.getiterator("motion-tween"):
                                    if tweenNode.tag == "motion-tween" and tweenNode.attrib.get("id") == tweenId:
                                        foundTween = True
                                        frameLength = (
                                            1
                                            if tweenNode.attrib.get("length-in-frames") == None
                                            else tweenNode.attrib.get("length-in-frames")
                                        )
                                        advancementFunction = (
                                            "linear"
                                            if tweenNode.attrib.get("advancement-function") == None
                                            else tweenNode.attrib.get("advancement-function")
                                        )
                                        for pointOrColorNode in tweenNode.getiterator():
                                            if pointOrColorNode.tag == "point":
                                                pX = (
                                                    0
                                                    if pointOrColorNode.attrib.get("x") == None
                                                    else float(pointOrColorNode.attrib.get("x"))
                                                )
                                                pY = (
                                                    0
                                                    if pointOrColorNode.attrib.get("y") == None
                                                    else float(pointOrColorNode.attrib.get("y"))
                                                )
                                                pointList.append(Point(pX, pY, 0))
                                            elif pointOrColorNode.tag == "color-state":
                                                colorName = (
                                                    "white"
                                                    if pointOrColorNode.attrib.get("name") == None
                                                    else pointOrColorNode.attrib.get("name")
                                                )
                                                for colorNode in self.colors.getiterator("color"):
                                                    if (
                                                        colorNode.tag == "color"
                                                        and colorNode.attrib.get("name") == colorName
                                                    ):
                                                        R = (
                                                            1
                                                            if colorNode.attrib.get("r") == None
                                                            else float(colorNode.attrib.get("r"))
                                                        )
                                                        G = (
                                                            1
                                                            if colorNode.attrib.get("g") == None
                                                            else float(colorNode.attrib.get("g"))
                                                        )
                                                        B = (
                                                            1
                                                            if colorNode.attrib.get("b") == None
                                                            else float(colorNode.attrib.get("b"))
                                                        )
                                                        A = (
                                                            1
                                                            if colorNode.attrib.get("a") == None
                                                            else float(colorNode.attrib.get("a"))
                                                        )
                                                        colorList.append(Color(R, G, B, A))
                                                        break  # leave for loop when we find the correct color reference
                                            pass  # Run through all child nodes of selected tween
                                        break  # Exit after finding correct tween
                                pass
                                if foundTween:
                                    thisTween = Tween(frameLength, advancementFunction, pointList, colorList)
                                    offset = thisTween.XYFromFrame(frameInTween)
                                    offsetFromTweenX = int(offset.X)
                                    offsetFromTweenY = int(offset.Y)
                                    offsetX += int(offset.X)
                                    offsetY += int(offset.Y)
                                    if thisTween.hasColorComponent():
                                        tweenHasColor = True
                                        if frameCallHasColor == False:
                                            color = thisTween.colorFromFrame(frameInTween)
                                    pass
                            if (
                                frameNode.attrib.get("color-name") != None
                                and frameCallHasColor == False
                                and tweenHasColor == False
                            ):
                                # Get color at frame definition as last resort.
                                for colorNode in colors.getiterator("color"):
                                    if colorNode.tag == "color" and colorNode.attrib.get(
                                        "name"
                                    ) == frameNode.attrib.get("color-name"):
                                        R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                        G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                        B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                        A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                        color = Color(R, G, B, A)
                                        break  # leave for loop when we find the correct color
                                pass
                            rotationZ = (
                                0
                                if frameCallNode.attrib.get("rotation-z") == None
                                else float(frameCallNode.attrib.get("rotation-z"))
                            )
                            frameList.append(
                                Frame(
                                    Bound(offsetX, offsetY, addWidth, addHeight),
                                    s,
                                    t,
                                    S,
                                    T,
                                    blend,
                                    scaleX,
                                    scaleY,
                                    color,
                                    rotationZ,
                                )
                            )
                    pass
                break  # Leave once we've found the appropriate frame

        # Prepare tracking list of consumed nodes.
        self.clearNodesForDrawing()
        # Make an identifier to tack onto primitive names in Panda3d's scene graph.
        frameIndexForName = 1

        # Loop through loaded frames that make up composite frame.
        for loadedFrame in frameList:
            # For debugging purposes, print the object.
            if False:
                loadedFrame.printAsString()

            # Set up place to store primitive 3d object; note: requires vertex data made by GeomVertexData
            squareMadeByTriangleStrips = GeomTristrips(Geom.UHDynamic)

            # Set up place to hold 3d data and for the following coordinates:
            #   square's points (V3: x, y, z),
            #   the colors at each point of the square (c4: r, g, b, a), and
            #   for the UV texture coordinates at each point of the square     (t2: S, T).
            vertexData = GeomVertexData(
                "square-" + str(frameIndexForName), GeomVertexFormat.getV3c4t2(), Geom.UHDynamic
            )
            vertex = GeomVertexWriter(vertexData, "vertex")
            color = GeomVertexWriter(vertexData, "color")
            texcoord = GeomVertexWriter(vertexData, "texcoord")

            # Add the square's data
            # Upper-Left corner of square
            vertex.addData3f(-loadedFrame.bound.Width / 2.0, 0, -loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R, loadedFrame.color.G, loadedFrame.color.B, loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.s, loadedFrame.T)

            # Upper-Right corner of square
            vertex.addData3f(loadedFrame.bound.Width / 2.0, 0, -loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R, loadedFrame.color.G, loadedFrame.color.B, loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.S, loadedFrame.T)

            # Lower-Left corner of square
            vertex.addData3f(-loadedFrame.bound.Width / 2.0, 0, loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R, loadedFrame.color.G, loadedFrame.color.B, loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.s, loadedFrame.t)

            # Lower-Right corner of square
            vertex.addData3f(loadedFrame.bound.Width / 2.0, 0, loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R, loadedFrame.color.G, loadedFrame.color.B, loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.S, loadedFrame.t)

            # Pass data to primitive
            squareMadeByTriangleStrips.addNextVertices(4)
            squareMadeByTriangleStrips.closePrimitive()
            square = Geom(vertexData)
            square.addPrimitive(squareMadeByTriangleStrips)
            # Pass primtive to drawing node
            drawPrimitiveNode = GeomNode("square-" + str(frameIndexForName))
            drawPrimitiveNode.addGeom(square)
            # Pass node to scene (effect camera)
            nodePath = self.effectCameraNodePath.attachNewNode(drawPrimitiveNode)
            # Linear dodge:
            if loadedFrame.blendMode == "darken":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(
                        ColorBlendAttrib.MAdd,
                        ColorBlendAttrib.OOneMinusFbufferColor,
                        ColorBlendAttrib.OOneMinusIncomingColor,
                    )
                )
                pass
            elif loadedFrame.blendMode == "multiply":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OFbufferColor, ColorBlendAttrib.OZero)
                )
                pass
            elif loadedFrame.blendMode == "color-burn":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(
                        ColorBlendAttrib.MAdd, ColorBlendAttrib.OZero, ColorBlendAttrib.OOneMinusIncomingColor
                    )
                )
                pass
            elif loadedFrame.blendMode == "linear-burn":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(
                        ColorBlendAttrib.MAdd, ColorBlendAttrib.OZero, ColorBlendAttrib.OIncomingColor
                    )
                )
                pass
            elif loadedFrame.blendMode == "lighten":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(
                        ColorBlendAttrib.MMax, ColorBlendAttrib.OIncomingColor, ColorBlendAttrib.OFbufferColor
                    )
                )
                pass
            elif loadedFrame.blendMode == "color-dodge":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOne)
                )
                pass
            elif loadedFrame.blendMode == "linear-dodge":
                nodePath.setAttrib(
                    ColorBlendAttrib.make(
                        ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOneMinusIncomingColor
                    )
                )
                pass
            else:  # Overwrite:
                nodePath.setAttrib(
                    ColorBlendAttrib.make(
                        ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOneMinusIncomingAlpha
                    )
                )
                pass
            nodePath.setDepthTest(False)
            # Apply texture
            nodePath.setTexture(self.tex)
            # Apply translation, then rotation, then scaling to node.
            nodePath.setPos(
                (
                    loadedFrame.bound.X + loadedFrame.bound.Width / 2.0,
                    1,
                    -loadedFrame.bound.Y - loadedFrame.bound.Height / 2.0,
                )
            )
            nodePath.setR(loadedFrame.rotationZ)
            nodePath.setScale(loadedFrame.scaleX, 1, loadedFrame.scaleY)
            nodePath.setTwoSided(True)
            self.consumedNodesList.append(nodePath)
            frameIndexForName = frameIndexForName + 1
        # Loop continues on through each frame called in the composite frame.
        pass
Example #6
0
class Effect:
    baseWidth = 0
    baseHeight = 0
    effectWidth = 1
    effectHeight = 1
    effectTargetMS = 143
    noSampling = False
    tex = None
    loadedFormat = None
    # Animation variables
    internalFrameIndex = 1
    startIndex = 1
    endIndex = 1
    loopEffect = False
    # XML variables
    tree = None
    frames = None
    colors = None
    tweens = None
    compositeFrames = None
    # Nodes
    consumedNodesList = None
    # Accessible object (nodePath)
    effectCameraNodePath = None
    effectCardNodePath = None
    # Constant value; Unit comparison for card size; basis is from cure, which is [140x110]
    cardDimensionBasis = [-5.0, 5.0, 0.0, 10.0, 140.0, 110.0]
    pixelScaleX = cardDimensionBasis[4]/(cardDimensionBasis[1]-cardDimensionBasis[0])
    pixelScaleZ = cardDimensionBasis[5]/(cardDimensionBasis[3]-cardDimensionBasis[2])
    effectIsCentered = True
    effectAdjustment = [0, 0, 0]
    def __init__(self, effectFileName, parent=None, loop=False, effectIsCentered=True, effectAdjustment=[0, 0, 0]):
        self.effectAdjustment = effectAdjustment
        self.loopEffect = loop
        self.effectIsCentered = effectIsCentered
        self.loadedFormat = None
        if effectFileName != None:
            effectFileNameSplit = effectFileName.split('.')
            self.loadedFormat = effectFileNameSplit[len(effectFileNameSplit)-2] # Get value at penultimate index
            if self.loadedFormat == effectFileNameSplit[0]:
                self.loadedFormat = None # Get rid of bad format name.
            pass
            
        # Load texture; supply alpha channel if it doesn't exist.
        p = transparencyKey(effectFileName)
        self.tex = Texture()
        self.tex.setup2dTexture(p.getXSize(), p.getYSize(), Texture.TUnsignedByte, Texture.FRgba)
        self.tex.load(p)
        if self.loadedFormat != None:
            try:
                self.tree = etree.parse("./"+GAME+"/effects/"+self.loadedFormat+"/sprite.xml")
            except IOError:
                self.loadedFormat = None
            pass
        if self.loadedFormat != None: 
            root = self.tree.getroot()
            self.frames = root.find('.//frames')
            self.colors = root.find('.//colors')
            self.tweens = root.find('.//motion-tweens')
            self.compositeFrames = root.find('.//composite-frames')
            self.baseWidth = 0 if root.attrib.get("base-width") == None else float(root.attrib.get("base-width"))
            self.baseHeight = 0 if root.attrib.get("base-height") == None else float(root.attrib.get("base-height"))
            self.effectWidth = 1 if root.attrib.get("frame-width") == None else float(root.attrib.get("frame-width"))
            self.effectHeight = 1 if root.attrib.get("frame-height") == None else float(root.attrib.get("frame-height"))
            self.effectTargetMS = 143 if root.attrib.get("target-ms") == None else float(root.attrib.get("target-ms"))
            self.startIndex = 1 if root.attrib.get("target-start") == None else int(root.attrib.get("target-start"))
            self.endIndex = 1 if root.attrib.get("target-end") == None else int(root.attrib.get("target-end"))
            self.noSampling = False if root.attrib.get("no-sampling") == None else bool(root.attrib.get("no-sampling"))
            if self.noSampling==True:
                self.tex.setMagfilter(Texture.FTNearest)
                self.tex.setMinfilter(Texture.FTNearest)
            cm = CardMaker('card-'+effectFileName)
            cardDeltaX = self.effectWidth / self.pixelScaleX
            cardDeltaZ = self.effectHeight / self.pixelScaleZ
            if self.effectIsCentered == True:
                cm.setFrame(0, 0, 0, 0)
                deltaX = (cardDeltaX/2.0) - (-cardDeltaX/2.0)
                deltaY = 0
                deltaZ = (cardDeltaZ/2.0) - (-cardDeltaZ/2.0)
                #occluder = OccluderNode('effect-parent-occluder', Point3((-cardDeltaX/2.0), 0, (-cardDeltaZ/2.0)), Point3((-cardDeltaX/2.0), 0, (cardDeltaZ/2.0)), Point3((cardDeltaX/2.0), 0, (cardDeltaZ/2.0)), Point3((cardDeltaX/2.0), 0, (-cardDeltaZ/2.0)))
            else:
                cm.setFrame(0, 0, 0, 0)
                deltaX = (cardDeltaX/2.0) - (-cardDeltaX/2.0)
                deltaY = 0
                deltaZ = cardDeltaZ - 0
                #occluder = OccluderNode('effect-parent-occluder', Point3((-cardDeltaX/2.0), 0, 0), Point3((-cardDeltaX/2.0), 0, cardDeltaZ), Point3((cardDeltaX/2.0), 0, cardDeltaZ), Point3((cardDeltaX/2.0), 0, 0))
            self.effectCardNodePath = render.attachNewNode(cm.generate())            
            self.effectCardNodePath.setBillboardPointEye()
            self.effectCardNodePath.reparentTo(parent)
            #occluder_nodepath = self.effectCardNodePath.attachNewNode(occluder)
            #self.effectCardNodePath.setOccluder(occluder_nodepath)
            emptyNode = NodePath('effect-parent-translator')
            emptyNode.reparentTo(self.effectCardNodePath)
            if effectIsCentered == True:
                emptyNode.setPos(-deltaX/2.0+self.effectAdjustment[0], 0+self.effectAdjustment[1], deltaZ/2.0+self.effectAdjustment[2])
            else:
                emptyNode.setPos(-deltaX/2.0+self.effectAdjustment[0], 0+self.effectAdjustment[1], deltaZ+self.effectAdjustment[2])
            #emptyNode.place()
            emptyNode.setSx(float(deltaX)/self.effectWidth)
            emptyNode.setSz(float(deltaZ)/self.effectHeight)
            self.effectCameraNodePath = emptyNode                        
            if parent != None:
                self.effectCardNodePath.reparentTo(parent)
            else:
                self.effectCardNodePath.reparentTo(render)
            #self.effectCardNodePath.place()
            self.effectCardNodePath.setBin("fixed", 40)
            self.effectCardNodePath.setDepthTest(False)
            self.effectCardNodePath.setDepthWrite(False)
        pass
    def getSequence(self):
        sequence = Sequence()
        for x in range(self.startIndex, self.endIndex, 1):
            sequence.append(Func(self.pandaRender))
            sequence.append(Func(self.advanceFrame))
            sequence.append(Wait(self.effectTargetMS * 0.001))
        sequence.append(Func(self.clearNodesForDrawing))
        sequence.append(Func(self.advanceFrame))
        sequence.append(Wait(self.effectTargetMS * 0.001))
        return sequence
        pass
    def hasEffectFinished(self):
        if self.internalFrameIndex > self.endIndex and self.loopEffect == False:
            return True
        else:
            return False
        pass
    def advanceFrame(self):
        if self.internalFrameIndex < self.endIndex:
            self.internalFrameIndex += 1
        elif self.internalFrameIndex == self.endIndex and self.loopEffect == True:
            self.internalFrameIndex = self.startIndex
        else:
            self.internalFrameIndex = self.endIndex + 1
            self.clearNodesForDrawing()
        pass
    def clearNodesForDrawing(self):
        if False:
            self.effectCameraNodePath.analyze()
        if self.consumedNodesList != None and self.consumedNodesList != []:
            for consumedNode in self.consumedNodesList:
                consumedNode.removeNode()
        self.consumedNodesList = []
        pass
    def pandaRender(self):
        frameList = []
        for node in self.compositeFrames.getiterator('composite-frame'):
            if node.tag == "composite-frame" and node.attrib.get("id") == str(self.internalFrameIndex):
                for frameCallNode in node:
                    for frameNode in self.frames.getiterator('frame'):
                        if frameNode.tag == "frame" and frameNode.attrib.get("id") == frameCallNode.attrib.get("id"):
                            offsetX = 0 if frameCallNode.attrib.get("offset-x") == None else float(frameCallNode.attrib.get("offset-x"))
                            offsetY = 0 if frameCallNode.attrib.get("offset-y") == None else float(frameCallNode.attrib.get("offset-y"))
                            tweenId = frameCallNode.attrib.get("tween")
                            frameInTween = 0 if frameCallNode.attrib.get("frame-in-tween") == None else int(frameCallNode.attrib.get("frame-in-tween"))
                            addWidth = 0 if frameNode.attrib.get("w") == None else float(frameNode.attrib.get("w"))
                            addHeight = 0 if frameNode.attrib.get("h") == None else float(frameNode.attrib.get("h"))
                            sInPixels = 0 if frameNode.attrib.get("s") == None else float(frameNode.attrib.get("s"))
                            tInPixels = 0 if frameNode.attrib.get("t") == None else float(frameNode.attrib.get("t"))
                            swInPixels = sInPixels + addWidth
                            thInPixels = tInPixels + addHeight
                            s = (sInPixels / self.baseWidth)
                            t = 1 - (tInPixels / self.baseHeight) # Complemented to deal with loading image upside down.
                            S = (swInPixels / self.baseWidth)
                            T = 1 - (thInPixels / self.baseHeight) # Complemented to deal with loading image upside down.
                            blend = "overwrite" if frameCallNode.attrib.get("blend") == None else frameCallNode.attrib.get("blend")
                            scaleX = 1 if frameCallNode.attrib.get("scale-x") == None else float(frameCallNode.attrib.get("scale-x"))
                            scaleY = 1 if frameCallNode.attrib.get("scale-y") == None else float(frameCallNode.attrib.get("scale-y"))
                            color = Color(1,1,1,1)
                            tweenHasColor = False
                            frameCallHasColor = False
                            frameCallColorName = frameCallNode.attrib.get("color-name")
                            if frameCallColorName != None:
                                # Get color at frame call as first resort.
                                frameCallHasColor = True
                                for colorNode in self.colors.getiterator('color'):
                                    if colorNode.tag == 'color' and colorNode.attrib.get("name") == frameCallColorName:
                                        R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                        G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                        B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                        A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                        color = Color(R, G, B, A)
                                        break # leave for loop when we find the correct color
                                pass

                            if tweenId != None and tweenId != "0":
                                # Get color at tween frame as second resort.
                                thisTween = None
                                frameLength = 1
                                advancementFunction = "linear"
                                foundTween = False
                                pointList = []
                                colorList = []
                                for tweenNode in self.tweens.getiterator('motion-tween'):
                                    if tweenNode.tag == "motion-tween" and tweenNode.attrib.get("id") == tweenId:
                                        foundTween = True
                                        frameLength = 1 if tweenNode.attrib.get("length-in-frames") == None else tweenNode.attrib.get("length-in-frames")
                                        advancementFunction = "linear" if tweenNode.attrib.get("advancement-function") == None else tweenNode.attrib.get("advancement-function")
                                        for pointOrColorNode in tweenNode.getiterator():
                                            if pointOrColorNode.tag == "point":
                                                pX = 0 if pointOrColorNode.attrib.get("x") == None else float(pointOrColorNode.attrib.get("x"))
                                                pY = 0 if pointOrColorNode.attrib.get("y") == None else float(pointOrColorNode.attrib.get("y"))
                                                pointList.append(Point(pX, pY, 0))
                                            elif pointOrColorNode.tag == "color-state":
                                                colorName = "white" if pointOrColorNode.attrib.get("name") == None else pointOrColorNode.attrib.get("name")
                                                for colorNode in self.colors.getiterator('color'):
                                                    if colorNode.tag == 'color' and colorNode.attrib.get("name") == colorName:
                                                        R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                                        G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                                        B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                                        A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                                        colorList.append(Color(R, G, B, A))
                                                        break # leave for loop when we find the correct color reference
                                            pass # Run through all child nodes of selected tween
                                        break # Exit after finding correct tween
                                pass
                                if foundTween:
                                    thisTween = Tween(frameLength, advancementFunction, pointList, colorList)
                                    offset = thisTween.XYFromFrame(frameInTween);
                                    offsetFromTweenX = int(offset.X);
                                    offsetFromTweenY = int(offset.Y);
                                    offsetX += int(offset.X);
                                    offsetY += int(offset.Y);
                                    if thisTween.hasColorComponent():
                                        tweenHasColor = True;
                                        if frameCallHasColor == False:
                                            color = thisTween.colorFromFrame(frameInTween);
                                    pass
                            if frameNode.attrib.get("color-name") != None and frameCallHasColor == False and tweenHasColor == False:
                                # Get color at frame definition as last resort.
                                for colorNode in colors.getiterator('color'):
                                    if colorNode.tag == 'color' and colorNode.attrib.get("name") == frameNode.attrib.get("color-name"):
                                        R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                        G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                        B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                        A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                        color = Color(R, G, B, A)
                                        break # leave for loop when we find the correct color
                                pass
                            rotationZ = 0 if frameCallNode.attrib.get("rotation-z") == None else float(frameCallNode.attrib.get("rotation-z"))
                            frameList.append(Frame(Bound(offsetX, offsetY, addWidth, addHeight), s, t, S, T, blend, scaleX, scaleY, color, rotationZ))
                    pass 
                break # Leave once we've found the appropriate frame

        # Prepare tracking list of consumed nodes.
        self.clearNodesForDrawing()
        # Make an identifier to tack onto primitive names in Panda3d's scene graph.
        frameIndexForName = 1
                
        # Loop through loaded frames that make up composite frame.
        for loadedFrame in frameList:              
            # For debugging purposes, print the object.
            if False:
                loadedFrame.printAsString()
            
            # Set up place to store primitive 3d object; note: requires vertex data made by GeomVertexData
            squareMadeByTriangleStrips = GeomTristrips(Geom.UHDynamic)
              
            # Set up place to hold 3d data and for the following coordinates:
            #   square's points (V3: x, y, z), 
            #   the colors at each point of the square (c4: r, g, b, a), and
            #   for the UV texture coordinates at each point of the square     (t2: S, T).
            vertexData = GeomVertexData('square-'+str(frameIndexForName), GeomVertexFormat.getV3c4t2(), Geom.UHDynamic)
            vertex = GeomVertexWriter(vertexData, 'vertex')
            color = GeomVertexWriter(vertexData, 'color')
            texcoord = GeomVertexWriter(vertexData, 'texcoord') 
              
            # Add the square's data
            # Upper-Left corner of square
            vertex.addData3f(-loadedFrame.bound.Width / 2.0, 0, -loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.s, loadedFrame.T)

            # Upper-Right corner of square
            vertex.addData3f(loadedFrame.bound.Width / 2.0, 0, -loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.S, loadedFrame.T)
            
            # Lower-Left corner of square
            vertex.addData3f(-loadedFrame.bound.Width / 2.0, 0, loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.s, loadedFrame.t)
            
            # Lower-Right corner of square
            vertex.addData3f(loadedFrame.bound.Width / 2.0, 0, loadedFrame.bound.Height / 2.0)
            color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
            texcoord.addData2f(loadedFrame.S, loadedFrame.t)

            # Pass data to primitive
            squareMadeByTriangleStrips.addNextVertices(4)
            squareMadeByTriangleStrips.closePrimitive()
            square = Geom(vertexData)
            square.addPrimitive(squareMadeByTriangleStrips)
            # Pass primtive to drawing node
            drawPrimitiveNode=GeomNode('square-'+str(frameIndexForName))    
            drawPrimitiveNode.addGeom(square)
            # Pass node to scene (effect camera)
            nodePath = self.effectCameraNodePath.attachNewNode(drawPrimitiveNode)
            # Linear dodge:
            if loadedFrame.blendMode == "darken":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOneMinusFbufferColor, ColorBlendAttrib.OOneMinusIncomingColor))
                pass
            elif loadedFrame.blendMode == "multiply":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OFbufferColor, ColorBlendAttrib.OZero))
                pass
            elif loadedFrame.blendMode == "color-burn":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OZero, ColorBlendAttrib.OOneMinusIncomingColor))
                pass
            elif loadedFrame.blendMode == "linear-burn":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OZero, ColorBlendAttrib.OIncomingColor))
                pass
            elif loadedFrame.blendMode == "lighten":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MMax, ColorBlendAttrib.OIncomingColor, ColorBlendAttrib.OFbufferColor))
                pass
            elif loadedFrame.blendMode == "color-dodge":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOne))
                pass
            elif loadedFrame.blendMode == "linear-dodge":
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOneMinusIncomingColor))
                pass
            else: # Overwrite:
                nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOneMinusIncomingAlpha))
                pass
            nodePath.setDepthTest(False)
            # Apply texture
            nodePath.setTexture(self.tex)
            # Apply translation, then rotation, then scaling to node.
            nodePath.setPos((loadedFrame.bound.X + loadedFrame.bound.Width / 2.0, 1, -loadedFrame.bound.Y - loadedFrame.bound.Height / 2.0))
            nodePath.setR(loadedFrame.rotationZ)
            nodePath.setScale(loadedFrame.scaleX, 1, loadedFrame.scaleY)
            nodePath.setTwoSided(True)
            self.consumedNodesList.append(nodePath)
            frameIndexForName = frameIndexForName + 1
        # Loop continues on through each frame called in the composite frame.
        pass