views:

353

answers:

1

Hello, I'm making a game with python and OpenGL. I was making it with pygame but I realised quick enough that it was too slow. I've made a class which I want to reasonably act like the Surface class for pygame, where Surface objects can be blitted to other Surface objects. I'm wishing to use framebuffers for rendering textures to textures to do that.

I want to create the textures in my game code at whatever point and store the textures for use at any time. I will call a method from another class which will control the Window and flip the screen. The problem is, I can only get the textures to work when put inside this method at a certain point. I want to be able to create textures and manipulate them outside this method.

Here's the class that manages the textures which I have called Surface similar to the pygame.Surface class,

class Surface():
 def __init__(self,size,extra = None):
  self.__offset = (0,0)
  self.children = []
  self.blitted = False
  self.last_offset = [0,0]
  self.surface_size = [size[0],size[1]]
  self.colour = [0,0,0,0]
  self.data = None
  self.rounded = 0
  self.parent = None
  self.parent_offset = (0,0)
  self.texture = None
 def blit(self,surface,offset,area=None, special_flags = 0):
  #Create textures if not done already
  if self.texture == None:
   create_texture(self)
  if surface.texture == None:
   create_texture(surface)
  #Render child to parent
  frame_buffer =  glGenFramebuffersEXT(1)
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame_buffer)
  render_buffer = glGenRenderbuffersEXT(1)
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffer);
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,self.surface_size[0],self.surface_size[1])
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_buffer)
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, self.texture, 0)
  glPushAttrib(GL_VIEWPORT_BIT)
  glViewport(0,0,self.surface_size[0],self.surface_size[1])
  draw_texture(surface.texture,offset,surface.surface_size,[float(c)/255.0 for c in surface.colour])
  glPopAttrib()
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)
  glDeleteFramebuffersEXT(1, [int(frame_buffer)])
  glDeleteRenderbuffersEXT(1,[int(render_buffer)])
  try:
   offset_before_last = surface.last_offset
   surface.last_offset = [offset[0] + self.__offset[0],offset[1] + self.__offset[1]]
   self.children.append([0,surface]) #0 states it is a surface
   surface.parent = self
   surface.parent_offset = offset
   if surface.get_offset() != surface.last_offset or not surface.blitted:
    surface.__set_offset(surface.last_offset)
    self.__recursive_offset_add(surface,offset_before_last,surface.last_offset) #Add to the children's offsets
   surface.blitted = True
  except AttributeError:
   pass
 def __recursive_offset_add(self,surface,offset_before_last,last_offset):
  for child in surface.children:
   try:
    child.__set_offset((child.get_offset()[0] - offset_before_last[0] + last_offset[0],child.get_offset()[1] - offset_before_last[1]  + last_offset[1]))
    self.__recursive_offset_add(child,offset_before_last,last_offset)
   except AttributeError:
    pass
 def get_offset(self):
  return self.__offset
 def __set_offset(self,offset):
  self.__offset = offset
 def fill(self,colour):
  colour = list(colour)
  if len(colour) < 4:
   colour.append(255)
  self.children = []
  self.textures = []
  self.colour = colour
 def get_size(self):
  return self.surface_size
 def get_width(self):
  return self.surface_size[0]
 def get_height(self):
  return self.surface_size[1]
 def round_corners(self,r):
  self.rounded = r
 def get_rect(self):
  return Rect(self.__offset,self.surface_size)
 def __del__(self):
  if self.texture != None:
   glDeleteTextures([self.texture])

The class which contains the said method, named update, is shown below.

class ScaledScreen(Surface):
 game_size = None
 first_screen = None
 screen = None
 fs = False #Fullscreen false to start
 clock = None
 resize = True
 game_gap = None
 game_scaled = (0,0)
 title = None
 fps = -1
 enter_fullscreen = False
 exit_fullscreen = False
 scale_to_screen = False
 iconify = False
 on_focus_fullscreen = False
 f_key = False
 def __init__(self,title,game_size,on_exit):
  pygame.init()
  self.title = title
  self.game_size = game_size
  screen_info = pygame.display.Info() #Required to set a good resolution for the game screen
  self.first_screen = (screen_info.current_w, screen_info.current_h - 120) #Take 120 pixels from the height because the menu bar, window bar and dock takes space
  pygame.display.set_caption(self.title)
  self.clock = pygame.time.Clock()
  self.game_gap = (0,0)
  self.on_exit = on_exit
  self.mod_key = 1024 if sys.platform == "darwin" else 64
  pygame.display.set_mode(self.first_screen,RESIZABLE|DOUBLEBUF|OPENGL)
  #OpenGL Parts
  Surface.__init__(self,game_size)
  self.textures = []
  self.screen_change = True
  self.opengl_window_setup()
 def opengl_window_setup(self):
  glViewport(0,0,self.game_scaled[0],self.game_scaled[1]) #Creates the viewport which is mapped to the window
  glEnable(GL_LINE_SMOOTH,GL_FASTEST) #Create antialiased lines
  glEnable(GL_BLEND) #Enable alpha blending
  glEnable(GL_TEXTURE_2D) #Enable 2D Textures
  glDisable(GL_DEPTH_TEST) #Disable depth
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) #The recomended blending functions.
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity() #Load the projection matrix
  gluOrtho2D(0,1280,720,0) #Set an orthorgraphic view
 def update(self,events):
  #Updates screen properly
  win_size_done = False #Changes to True if the window size is got by the VIDEORESIZE event below
  for event in events:
   if event.type == QUIT:
    self.on_exit()
   if event.type == VIDEORESIZE:
    ss = [event.w,event.h]
    self.resize = True
    win_size_done = True
  keys = pygame.key.get_pressed() #Get the pressed keys
  if pygame.key.get_mods() & self.mod_key:
   if(keys[K_q] or keys[K_w]):
    self.on_exit()
   if keys[K_f]:
    if self.f_key == False:
     self.f_key = True
     if self.fs == False:
      self.enter_fullscreen = True
     else:
      self.exit_fullscreen = True
   else:
    self.f_key = False
  if self.on_focus_fullscreen and pygame.display.get_active():
   self.on_focus_fullscreen = False
   self.enter_fullscreen = True
  if self.enter_fullscreen:
   self.screen_change = True
   pygame.mouse.set_visible(False)
   self.screen = pygame.display.set_mode((self.first_screen[0],self.first_screen[1]+ 120))
   if self.scale_to_screen:
    self.game_scaled = (self.screen.get_width(),self.screen.get_height())
   else:
    self.game_scaled = get_resolution(self.screen,(self.screen.get_width(),self.screen.get_height()),self.game_size)
   self.game_gap = [(self.screen.get_width() - self.game_scaled[0])/2,(self.screen.get_height() - self.game_scaled[1])/2]
   pygame.display.set_mode((0,0), FULLSCREEN|HWSURFACE|DOUBLEBUF|OPENGL)
   self.fs = True
   self.enter_fullscreen = False
   self.resize = False
  elif self.exit_fullscreen:
   self.screen_change = True
   pygame.mouse.set_visible(True)
   pygame.display.set_mode(self.first_screen,RESIZABLE|DOUBLEBUF|OPENGL)
   self.fs = False
   self.resize = True
   self.game_gap = (0,0)
   self.exit_fullscreen = False
   if self.iconify:
    self.on_focus_fullscreen = True
  #Scale game to screen resolution, keeping aspect ratio
  if self.resize:
   self.screen_change = True
   if(win_size_done == False): #Sizes not gotten by resize event
    ss = [self.screen.get_width(),self.screen.get_height()]
   self.game_scaled = get_resolution(self.screen,ss,self.game_size)
   pygame.display.set_mode(self.game_scaled,RESIZABLE|DOUBLEBUF|OPENGL)
   self.resize = False #Next time do not scale unless resize or fullscreen events occur
  if self.iconify:
   pygame.display.iconify() #Minimise
   self.iconify = False
  #Open GL Screen setup
  if self.screen_change:
   self.opengl_window_setup()
  self.screen_change = False
  #Below is an example of create a texture of an image and rendering it to the screen. Doing so here works but it wont work outside this method with the rest of my game code.
  image = open_image("some_image.png")
  self.blit(image,(20,20))
  #The example ends here
  pygame.display.flip() #Flip buffer
  glClear(GL_COLOR_BUFFER_BIT)

Here are two functions that may be important,

def create_texture(surface):
 surface.texture = glGenTextures(1)
 glMatrixMode(GL_MODELVIEW)
 glLoadIdentity() #Loads model matrix
 glBindTexture(GL_TEXTURE_2D, surface.texture) #Binds the current 2D texture to the texture to be drawn
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) #Required to be set for maping the pixel data
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) #Similar as above
 if surface.data == None:
  surf = pygame.Surface((1,1),SRCALPHA)
  surf.fill(surface.colour)
  data = pygame.image.tostring(surf, "RGBA") * (surface.surface_size[0] * surface.surface_size[1])
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface.surface_size[0], surface.surface_size[1], 0, GL_RGBA,GL_UNSIGNED_BYTE, surface.data) #Put surface pixel data into texture
    def open_image(path):
     img = pygame.image.load(path)
     surf = Surface(img.get_size())
     surf.data = pygame.image.tostring(img, "RGBA")
     return surf

I've already programmed a lot of the game for pygame. I'm basically creating an OpenGL imitation. It doesn't have to work exactly the same way but I need to prevent massive amounts of my existing code being changed.

The screen always shows as white except with the example of the working image in the update method.

If anyone can help me with this, I'll be most impressed.

Thank you.

A: 

Maybe the OpenGL calls to build the texture aren't done before attempting to use the texture. Try inserting a glFlush call just before attempting to use the texture. This will cause the OpenGL system to finish executing any previous OpenGL calls before starting to execute any more calls.

Mr. Berna
Using glFlush at the beginning of the draw_texture fucntion, doesn't do anything. I didn't add it to the blit method but there is one texture that is rendered directly and that didn't work.
Matthew Mitchell