[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Using PyOpenGL for 2D graphics



kschnee@xxxxxxxxxx:

I use python-ogre, which is a more recent much better version of pyogre by a different team.  Cegui works with it and is pretty nice, although feels like overkill for most things.  I have never really looked at sprites or any kind of 2d stuff with it though, our game is completely 3d.  As a robust 3d rendering engine, nothing else I know of comes close, at least out of things usable from python.  It surely has a learning curve though, and its quite big (which can be a positive and a negative).

Fonts in opengl aren't too difficult, neither is rendering an interface.  You will want to render the interface AFTER you render all the 3d elements, and turn off the depth buffer before you render it so it is rendered on top of everything.  There are numerous font systems around, I use some code treeform gave me on irc which I modified for pygame.  I attached it for you.  Openglcontext which is somewhat of an addon for pyopengl also has font code.  Lastly, I once programmed my own font system using the NeHe tutorial as a guide - it was confusing because there is so much windows api specific code in that tutorial, but the basic idea is the same - load each letter of the font as a texture and draw quads with the proper texture.

I attached this code as well, although this is not as well tested or clear.
import pygame
pygame.font.init()
from OpenGL.GL import *
from OpenGL.GLU import *
#from mygl.texture.material import *

def nextPowerOf2 (num):
    """ If num isn't a power of 2, will return the next higher power of two """
    rval = 1
    while (rval<num):
        rval <<= 1
    return rval

def setupMatrix():
    """ sets up the matrix for font drawing """
    glPushAttrib(GL_TRANSFORM_BIT)
    viewport = glGetIntegerv(GL_VIEWPORT)
    glMatrixMode(GL_PROJECTION)
    glPushMatrix()
    glLoadIdentity()
    gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3])
    glPopAttrib()
    

def taredownMatrix():
    """ returns the matrix to the state before setupMatrix """
    glPushAttrib(GL_TRANSFORM_BIT)
    glMatrixMode(GL_PROJECTION)
    glPopMatrix()
    glPopAttrib()
    

class Font:
    """ very nice font class """
    
    def __init__ (self, name, height=10,antialias=True):
        """ make a font with font name and font hight """
        self.m_allocated = True
        self.height = height
        self.name = name
        self.antialias = antialias
        self.sizes = {}
        ft = pygame.font.Font(name,height)
        self._listStart = glGenLists (128)
        self.fontTextures = [None] * 128
        for i in xrange (128):
            self._makeList (ft, i, self._listStart, self.fontTextures);
        ft = None
        
    def setup(self):
        glPushMatrix()
        glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT)
        glMatrixMode(GL_MODELVIEW)
        glDisable(GL_LIGHTING)
        glEnable(GL_TEXTURE_2D)
        glDisable(GL_DEPTH_TEST)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        
    def tairdown(self):        
        glPopAttrib()
        glPopMatrix()
        
    def printChar(self,character):
        glCallList(character+self._listStart)        
        
    def flatPrint (self,  string):
        """ the normal print of a font """
        if (string == None or string == "" ):
            return
        glPushMatrix()
        glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT)
        glMatrixMode(GL_MODELVIEW)
        glDisable(GL_LIGHTING)
        glEnable(GL_TEXTURE_2D)
        glDisable(GL_DEPTH_TEST)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        #glListBase(self._listStart)
        glPushMatrix ()
        #glCallLists (string)
        for char in string:
            glCallList(self._listStart+ord(char))
        glPopMatrix()
        glPopAttrib()
        glPopMatrix()
        
    def wrapPrint(self , string, size=(0,0) ):
        """ prints text in the sized window """
        lines = string.split ( "\n" )
        glPushMatrix()
        glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT)
        glMatrixMode(GL_MODELVIEW)
        glDisable(GL_LIGHTING)
        glEnable(GL_TEXTURE_2D)
        glDisable(GL_DEPTH_TEST)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        #glListBase(self._listStart)
        h = self.sizes["A"][1]
        print self._listStart
        for i in xrange (len (lines)):
            line = lines [i]
            if self.getTextSize(line)[0] > size:
                width = 0
                liner = ""
                for c in line:
                    if width + self.sizes[c][0] < size:
                         liner += c
                         width += self.sizes[c][0]
                    else:
                        glPushMatrix ()
                        #glCallLists(liner) # hope this gets fixed
                        for character in liner:
                            glCallList(self._listStart+ord(character))
                        glPopMatrix()
                        glTranslatef (0, h ,0);
                        liner = c
                        width = 0
                if liner != "":
                    glPushMatrix ()
                    #glCallLists (liner) # hope this gets fixed
                    for character in liner:
                        glCallList(self._listStart+ord(character))
                    glPopMatrix()
                    glTranslatef (0, h ,0);
            else:  
                if len(line) > 0:
                    glPushMatrix ()
                    for character in line:
                        glCallList(self._listStart+ord(character))
                    glPopMatrix()
                glTranslatef (0, h ,0);
        glPopAttrib()
        glPopMatrix()
        glEnable(GL_DEPTH_TEST)
        glDisable(GL_BLEND)
           
    def _makeList (self,ft, ch, list_base, tex_base_list):
        """
        makes the font list
        """
        try:
            a = ft.render(chr(ch),self.antialias,[220,225,225])
            b = ft.render(chr(ch),self.antialias,[40,40,40])
            a = pygame.transform.rotozoom(a,0,.9)
            b.blit(a,[1,1])
            glyph = b
        except:
            glyph = ft.render('\n',0,[255,255,255])
        glyphWidth, glyphHeight = glyph.get_size()
        ratio = glyphHeight/self.height
        glyphWidth *= ratio
        glyphHeight *= ratio
        self.sizes[chr(ch)] = [glyphWidth,glyphHeight]
        width = nextPowerOf2 (glyphWidth + 1)
        height = nextPowerOf2 (glyphHeight + 1)
        glyph = pygame.transform.scale(glyph,[width,height])
        expandedData = pygame.image.tostring(glyph,
                "RGBA",True)
        ID = glGenTextures (1)
        tex_base_list [ch] = ID
        glBindTexture (GL_TEXTURE_2D, ID)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
            0, GL_RGBA, GL_UNSIGNED_BYTE, expandedData )
        expandedData = None
        glNewList (list_base + ch, GL_COMPILE)
        if (ch == ord (" ")):
            glyph_advance = glyphWidth
            glTranslatef(glyph_advance, 0, 0)
            glEndList()
        else:
            glBindTexture (GL_TEXTURE_2D, ID)
            glPushMatrix()
            glBegin(GL_QUADS)
            glTexCoord2f(0,0), glVertex2f(0,0)
            glTexCoord2f(0,1), glVertex2f(0,glyphHeight)
            glTexCoord2f(1,1), glVertex2f(glyphWidth,glyphHeight)
            glTexCoord2f(1,0), glVertex2f(glyphWidth,0)
            glEnd()
            glPopMatrix()
            glTranslatef(glyphWidth , 0, 0) #+ 0.75
            glEndList()
            
    def release (self):
        """ Release the gl resources for this Face.
            (This provides the functionality of KillFont () and font_data::clean ()
        """
        if (self.m_allocated):
            # Free up the glTextures and the display lists for our face
            glDeleteLists ( self._listStart, 128);
            for ID in self.fontTextures:
                glDeleteTextures (ID);
            # Extra defensive. Clients that continue to try and use this object
            # will now trigger exceptions.
            self.list_base = None
            self.m_allocated = False
        return    
        
    def getTextSize(self , text ="" ):
        """ 
        returns the size of a string text 
        if you do not give a string it returns the (0,hight) of the font 
        """
        if text == "":
            return (0,self.sizes["A"][1])
        w = 0
        h = 0
        for c in text:
            w += self.sizes[c][0]
            if self.sizes[c][1] > h:
                h = self.sizes[c][1]
        return (w,h) 

    def __del__ (self):
        """ Python destructor for when no more refs to this Face object """
        self.release ()
        return




import pygame
pygame.display.set_mode([100,100])
import random
import math

from OpenGL.GL import *
from OpenGL.GLU import *

class bitmapfont:
  def __init__(self):
    self.base = glGenLists(96)
    pygame.font.init()
    self.font = pygame.font.SysFont("Courier New",24,0,0)
    self.characters = []
    self.tnames = glGenTextures(94)
    for i in range(32,32+94):
      char = self.font.render(chr(i),True,[255,255,255]).convert_alpha()
      char = pygame.transform.scale(char,[32,32])
      s = pygame.Surface([char.get_width(),char.get_height()])
      s.fill([0,0,0])
      s.blit(char,[0,0])
      #s = pygame.image.load("Data/Cube.bmp")
      if chr(i)=='a':
        pygame.image.save(s,"tst.tga")
      #self.characters.append(s)
      glBindTexture(GL_TEXTURE_2D,self.tnames[i-32])
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
      idat = pygame.image.tostring(s,"RGBA",True)
      glTexImage2D(GL_TEXTURE_2D,0,4,s.get_width(),s.get_height(),0,GL_RGBA,GL_UNSIGNED_BYTE,idat)
      #make a display list for the character
      glNewList(self.base+i,GL_COMPILE)
      glBindTexture(GL_TEXTURE_2D,self.tnames[i-32])
      glBegin(GL_QUADS)
      glTexCoord2d(0,0);glVertex3f(0,0,0)
      glTexCoord2d(1,0);glVertex3f(.05,0,0)
      glTexCoord2d(1,1);glVertex3f(.05,.1,0)
      glTexCoord2d(0,1);glVertex3f(0,.1,0)
      glEnd()
      glTranslatef(.05,0,0)
      glEndList()
  def write(self,text):
    glPushAttrib(GL_LIST_BIT)
    glListBase(self.base)
    #~ for c in text:
      #~ glCallList(ord(c)-31)
    glCallLists(text)
    glPopAttrib()

class Screen:
  def __init__(self):
    self.wi = 640
    self.hi = 480
    self.fullscreen = 0
    self.caption = "Nehe lesson 13 Bitmap Text"
    self.glinited = False
    self.texloaded = False
    self.tnames = []
    self.textures = []
    
    #instance variables to be replaced with objects
    self.bfont = None
    self.c1 = 0
    self.c2 = 0
  def initGL(self):
    glEnable(GL_TEXTURE_2D)
    glShadeModel(GL_SMOOTH)
    glClearColor(0,0,0,0)
    glClearDepth(1.)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
    #glEnable(GL_LIGHT0)
    #glEnable(GL_LIGHTING)
    glEnable(GL_COLOR_MATERIAL)
    
    #glEnable(GL_BLEND)
    #glBlendFunc(GL_SRC_ALPHA,GL_ONE)
    self.bfont = bitmapfont()
  def resize(self):
    wi,hi,fullscreen = self.wi,self.hi,self.fullscreen
    self.screen = pygame.display.set_mode([wi,hi],pygame.OPENGL|pygame.DOUBLEBUF|pygame.FULLSCREEN*fullscreen|pygame.RESIZABLE)
    pygame.display.set_caption(self.caption)
    if hi==0: hi = 1
    glViewport(0,0,wi,hi)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45.,float(wi)/float(hi),.1,100.)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    if not self.glinited: self.initGL()
  def draw(self):
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslatef(0,0,-3)
    glColor3f(math.cos(self.c1),math.sin(self.c2),1-.5*math.cos(self.c1+self.c2))
    glTranslatef(.5*math.cos(self.c1),.35*math.sin(self.c2),0)
    #self.bfont.write("Active OpenGL Text With NeHe - %7.2f"%self.c1)
    self.bfont.write("I wonder how long a line I can write")
    self.c1 += .051
    self.c2 += .005

screen = Screen()
screen.resize()

piover180 = math.pi/180.0
running = 1
c = pygame.time.Clock()
while running:
  c.tick(60)
  screen.draw()
  pygame.display.flip()
  for e in pygame.event.get():
    if e.type==pygame.VIDEORESIZE:
      screen.wi,screen.hi = e.w,e.h
      screen.resize()
    if e.type==pygame.KEYDOWN:
      if e.key==pygame.K_ESCAPE:
        running = 0
      if e.key==pygame.K_RETURN:
        screen.wi,screen.hi,screen.fullscreen=40,40,0
        screen.resize()
  keys = pygame.key.get_pressed()
  if keys[pygame.K_PAGEUP]:
    pass
  if keys[pygame.K_PAGEDOWN]:
    pass
  if keys[pygame.K_RIGHT]:
    pass
  if keys[pygame.K_LEFT]:
    pass
  if keys[pygame.K_UP]:
    pass
  if keys[pygame.K_DOWN]:
    pass