def setupBackgroundImage(self):    
   
   image_file = Filename(TestGameBase.__BACKGROUND_IMAGE_PATH__)
   
   # check if image can be loaded
   img_head = PNMImageHeader()
   if not img_head.readHeader(image_file ):
       raise IOError("PNMImageHeader could not read file %s. Try using absolute filepaths"%(image_file.c_str()))
       sys.exit()
       
   # Load the image with a PNMImage
   w = img_head.getXSize()
   h = img_head.getYSize()
   img = PNMImage(w,h)
   #img.alphaFill(0)
   img.read(image_file) 
   
   texture = Texture()        
   texture.setXSize(w)
   texture.setYSize(h)
   texture.setZSize(1)    
   texture.load(img)
   texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image
   texture.setWrapV(Texture.WM_border_color)
   texture.setBorderColor(LColor(0,0,0,0))
   
   # creating CardMaker to hold the texture
   cm = CardMaker('background')
   cm.setFrame(-0.5*w,0.5*w,-0.5*h,0.5*h)  # This configuration places the image's topleft corner at the origin (left, right, bottom, top)
   background_np = NodePath(cm.generate())            
   background_np.setTexture(texture)
   
   background_np.reparentTo(self.render)
   background_np.setPos(TestGameBase.__BACKGROUND_POSITION__)
   background_np.setScale(TestGameBase.__BACKGROUND_SCALE__)
  def createSequenceNode(self,name,img,cols,rows,scale_x,scale_y,frame_rate):
    
    seq = SequenceNode(name)
    w = int(img.getXSize()/cols)
    h = int(img.getYSize()/rows)

    counter = 0
    for i in range(0,cols):
      for j in range(0,rows):
        sub_img = PNMImage(w,h)
        sub_img.addAlpha()
        sub_img.alphaFill(0)
        sub_img.fill(1,1,1)
        sub_img.copySubImage(img ,0 ,0 ,i*w ,j*h ,w ,h)

        # Load the image onto the texture
        texture = Texture()        
        texture.setXSize(w)
        texture.setYSize(h)
        texture.setZSize(1)    
        texture.load(sub_img)
        texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image
        texture.setWrapV(Texture.WM_border_color)
        texture.setBorderColor(LColor(0,0,0,0))

        cm = CardMaker(name + '_' + str(counter))
        cm.setFrame(-0.5*scale_x,0.5*scale_x,-0.5*scale_y,0.5*scale_y)
        card = NodePath(cm.generate())
        seq.addChild(card.node(),counter)
        card.setTexture(texture)
        sub_img.clear()
        counter+=1
    
    seq.setFrameRate(frame_rate)
    print "Sequence Node %s contains %i frames of size %s"%(name,seq.getNumFrames(),str((w,h)))
    return seq   
Exemple #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 = 10.0

    def __init__(self, image_path, name=None,\
                  rows=1, cols=1, scale=1.0,\
                  twoSided=True, alpha=TRANS_ALPHA,\
                  repeatX=1, repeatY=1,\
                  anchorX=ALIGN_LEFT, 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.
        """
       
        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()
       
        self.frames = []
        for rowIdx in range(self.rows):
            for colIdx in range(self.cols):
                self.frames.append(Sprite2d.Cell(colIdx, rowIdx))
       
        # 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
       
        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.card = self.node.attachNewNode(card.generate())
       
        # 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)
    
    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 hasattr(self, "task"):
            #if not self.task.isRemoved():
            taskMgr.remove(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")
   
    def createAnim(self, animName, frames, fps=12):
        """ Create a named animation. Takes the animation name and a tuple of frame numbers """
        self.animations[animName] = Sprite2d.Animation(frames, 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 flipTexture(self):
        """ Sets the texture coordinates of the texture to the current frame"""
        sU = self.offsetX * self.repeatX
        sV = self.offsetY * self.repeatY
        oU = 0 + self.frames[self.currentFrame].col * self.uSize
        oV = 1 - self.frames[self.currentFrame].row * self.vSize - self.offsetY
        if self.flip['x']:
            sU *= -1
            oU = self.uSize + self.frames[self.currentFrame].col * self.uSize
        if self.flip['y']:
            sV *= -1
            oV = 1 - self.frames[self.currentFrame].row * self.vSize
        self.node.setTexScale(TextureStage.getDefault(), sU, sV)
        self.node.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