def __init__(self, tile_size=64): self._tile_size = tile_size self._texture_size = 1024 self._max_textures = 5 self._tile_rows_in_texture = self._texture_size / tile_size self._free_tiles = SortedList() self._textures = [] self._total_tiles = 0 self._sprites = {} self._top_sprite_id = 0 self._total_tiles_per_texture = int(self._tile_rows_in_texture**2) self._vbo_index_bytes_per_texture = self._total_tiles_per_texture * 6 * _floats_per_vertex * ctypes.sizeof(ctypes.c_uint16) self._max_tiles = self._total_tiles_per_texture *self._max_textures self.initBuffers()
class SpriteManager: def __init__(self, tile_size=64): self._tile_size = tile_size self._texture_size = 1024 self._max_textures = 5 self._tile_rows_in_texture = self._texture_size / tile_size self._free_tiles = SortedList() self._textures = [] self._total_tiles = 0 self._sprites = {} self._top_sprite_id = 0 self._total_tiles_per_texture = int(self._tile_rows_in_texture**2) self._vbo_index_bytes_per_texture = self._total_tiles_per_texture * 6 * _floats_per_vertex * ctypes.sizeof(ctypes.c_uint16) self._max_tiles = self._total_tiles_per_texture *self._max_textures self.initBuffers() def initBuffers(self): self._vao = glGenVertexArray() glBindVertexArray(self._vao) self._shader = R.getShaderProgram("sprites") self._texture_loc = self._shader.getUniformPos("texture0") # 1 vectors of 3 for vertex coords # 1 vector of 2 for texture coords # 1 float for alpha # = 6 floats = _floats_per_vertex # x 4 points per quad self._data = N.zeros((self._max_tiles * 4, _floats_per_vertex),dtype="f") # 6 indices (2 tris) per quad indices = N.empty((self._max_tiles,6), dtype=N.uint16) j = 0 for i in xrange(self._max_tiles): ind = indices[i,:] ind[0] = j+0 ind[1] = j+1 ind[2] = j+2 ind[3] = j+0 ind[4] = j+2 ind[5] = j+3 j+=4; self._vertices_vbo = vbo.VBO(self._data.ravel(),usage=GL_DYNAMIC_DRAW) self._vertices_vbo.bind() stride = fsize * _floats_per_vertex glEnableVertexAttribArray(self._shader.attr_position) glVertexAttribPointer(self._shader.attr_position, 3, GL_FLOAT, False, stride, None) glEnableVertexAttribArray(self._shader.attr_tc) glVertexAttribPointer(self._shader.attr_tc, 2, GL_FLOAT, False, stride, ctypes.c_void_p(3 * fsize)) self._shader.getAttribPos("alpha",True) glEnableVertexAttribArray(self._shader.attr_alpha) glVertexAttribPointer(self._shader.attr_alpha, 1, GL_FLOAT, False, stride, ctypes.c_void_p(5*fsize)) self._indices_vbo = vbo.VBO(indices.ravel(), target=GL_ELEMENT_ARRAY_BUFFER,usage=GL_STATIC_DRAW) self._indices_vbo.bind() glBindVertexArray(0) # creates a new texture and pushes all new tiles # into the _free_tiles list def newTexture(self): texture_surface_id = len(self._textures) texture = SpriteTexture(self._texture_size) self._textures.append(texture) #[texture_surf, texture,False,0,0]) ty = 0 for y in xrange(self._tile_rows_in_texture): tx = 0 for x in xrange(self._tile_rows_in_texture): # #cols = #rows self._free_tiles.add((texture_surface_id, (ty,tx), self._total_tiles)) tx += self._tile_size self._total_tiles += 1 ty += self._tile_size def getFreeTile(self): try: tile = self._free_tiles.pop(0) except: self.newTexture() # create more tiles tile = self._free_tiles.pop(0) return tile def setTileAlpha(self, tile, alpha): texture_surf_id, tile_pos, tile_num = tile d = self._data[4*tile_num:4*tile_num+4] d[0:4,5] = alpha self._textures[texture_surf_id].setTainted(vbo_index=tile_num) def setTileGraphics(self, tile, src_tile_coord, surf, alpha): texture_surf_id, (ty,tx), tile_num = tile src_x = src_tile_coord[0] * self._tile_size src_y = src_tile_coord[1] * self._tile_size ts = self._tile_size # blit texture onto it tex = self._textures[texture_surf_id] texture_surf = tex.getSurface()# texture surface texture_surf.fill((0,0,0,0), rect=(tx,ty,ts,ts) ) texture_surf.blit(surf, (tx,ty), area=(src_x,src_y,ts,ts)) # setup the vbo data u0 = float(tx) / self._texture_size u1 = float(tx+ts) / self._texture_size v0 = 1.0-float(ty) / self._texture_size v1 = 1.0-float(ty+ts) / self._texture_size d = self._data[4*tile_num:4*tile_num+4] d[0][3:6] = (u0,v0, alpha) d[1][3:6] = (u0,v1, alpha) d[2][3:6] = (u1,v1, alpha) d[3][3:6] = (u1,v0, alpha) tex.setTainted(img=True, vbo_index=tile_num) def setTileTransform(self, tile, src_tile_coord, transform_info): texture_surf_id, tile_pos, tile_num = tile dx,dy,p0,px,py = transform_info x0 = dx * src_tile_coord[0] y0 = dy * src_tile_coord[1] d = self._data[4*tile_num:4*tile_num+4] vx = px - p0 vy = py - p0 p0 = p0 + vx*x0 + vy*y0 d[0][0:3] = p0 d[1][0:3] = p0 + vy * dy d[2][0:3] = p0 + vx * dx + vy * dy d[3][0:3] = p0 + vx * dx self._textures[texture_surf_id].setTainted(vbo_index=tile_num) def getTransformInfo(self, surf_w, surf_h, xform,centered): ts = self._tile_size tiles_w = int(math.ceil(float(surf_w)/ts)) tiles_h = int(math.ceil(float(surf_h)/ts)) p0 = N.array((0,0,0),dtype="f") px = N.array((surf_w,0,0),dtype="f") py = N.array((0,surf_h,0),dtype="f") if centered is not None: dp = N.array((surf_w*0.5,surf_h*0.5,0),dtype="f") p0 -= dp px -= dp py -= dp if xform is not None: xr = xform[0:3,0:3] xt = xform[0:3,3] p0 = N.dot(xr,p0)+xt px = N.dot(xr,px)+xt py = N.dot(xr,py)+xt dx = 1.0/tiles_w dy = 1.0/tiles_h return dx,dy,p0,px,py def _newSpriteHlp(self, surface, alpha, xform=None, centered=None): try: surface.get_width() except: surface = R.loadSurface(surface) ts = self._tile_size w,h = surface.get_width(), surface.get_height() tiles_x = int(math.ceil(float(w)/ts)) tiles_y = int(math.ceil(float(h)/ts)) transform_info = self.getTransformInfo(w,h,xform,centered) sprite_tiles = [] for y in xrange(tiles_y): for x in xrange(tiles_x): tile = self.getFreeTile() self.setTileGraphics(tile, (x,y), surface, alpha ) self.setTileTransform(tile, (x,y), transform_info ) sprite_tiles.append(tile) return (sprite_tiles,(w,h),alpha,xform,centered) def newSprite(self, surface, alpha=1.0, xform=None, centered=None): try: surface.get_width() except: surface = R.loadSurface(surface,False) s = self._newSpriteHlp(surface, alpha, xform, centered) id = self._top_sprite_id self._sprites[id] = s self._top_sprite_id+=1 return id def destroySprite(self, sid): try: s = self._sprites[sid] except: return for tile in s[0]: # iterate over the tiles in the sprite self.setTileAlpha(tile, 0) # this disables the rendering of the sprite self._free_tiles.add(tile) del self._sprites[sid] def setSpriteAlpha(self, sid, alpha): try: s = self._sprites[sid] except: return if s[2] == alpha: return for tile in s[0]: # iterate over the tiles in the sprite self.setTileAlpha(tile, alpha) def setSpriteTransform(self, sid, xform, centered=None): try: s = self._sprites[sid] except: return tiles,(w,h),alpha,old_xform,old_centered = s if centered is None: centered = old_centered transform_info = self.getTransformInfo(w,h,xform,centered) ts = self._tile_size tiles_x = int(math.ceil(float(w)/ts)) tiles_y = int(math.ceil(float(h)/ts)) i = 0 for y in xrange(tiles_y): for x in xrange(tiles_x): self.setTileTransform(tiles[i], (x,y), transform_info ) i+=1 def setSpriteGraphics(self, sid, surface): try: s = self._sprites[sid] except: return sprite_tiles,(w,h),alpha,xform,centered = s for tile in sprite_tiles: # iterate over the tiles in the sprite self._free_tiles.add(tile) self.setTileAlpha(tile, 0) # this disables the rendering of the sprite s = self._newSpriteHlp(surface, alpha, xform, centered) self._sprites[sid] = s @profile def draw(self, scene): glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glDisable(GL_DEPTH_TEST) self._shader.begin() scene.uploadMatrices(self._shader) glBindVertexArray(self._vao) ofs = 0 self._indices_vbo.bind() for t in self._textures: t.updateGlTexture() if t.isVboTainted(): fac = _floats_per_vertex * 4 * fsize # bytes/quad self._vertices_vbo.bind() glBufferSubData( GL_ARRAY_BUFFER, fac * t._vbo_change_low, fac * (t._vbo_change_high - t._vbo_change_low), self._data[t._vbo_change_low*4:].ravel().ctypes.data_as(ctypes.c_void_p) ) self._indices_vbo.bind() t.bind(self._texture_loc) glDrawElements(GL_TRIANGLES,self._total_tiles_per_texture*6, GL_UNSIGNED_SHORT, ctypes.c_void_p(ofs)) ofs += self._vbo_index_bytes_per_texture t.reset() glBindVertexArray(0) self._shader.end() glEnable(GL_DEPTH_TEST) glDisable(GL_BLEND)