pygo/sgc/widgets/opengl.py

256 lines
7.4 KiB
Python
Raw Normal View History

2012-04-14 22:38:47 +00:00
#!/usr/bin/env python
# Copyright (C) 2011 Sam Bull
"""
Base widget, all widgets inherit from this.
"""
from pygame.locals import Rect
from OpenGL.GL import *
import FTGL
from ..surface import SurfaceBase
class OpenGLImage(SurfaceBase):
"""
Class used to emulate the interface of Surface for OpenGL drawing.
Functions should be used in the same manner as pygame.Surface.
Differences are shown in documentation, otherwise assume it to
function the same as the equivalent pygame.Surface function.
"""
def __init__(self, surf, parent=None, **kwargs):
self._a = None
self._lock = False
self.display_list = []
self._children = []
if parent:
self._parent = parent # Parent surface used for _abs
else:
self._parent = self._default_screen
self._rect = Rect((0,0), (0,0))
if isinstance(surf, (tuple,list)):
self._rect.size = surf
elif isinstance(surf, OpenGLFont):
self._rect.size = (surf.size)
self.display_list.extend(surf.display_list)
elif isinstance(surf, OpenGLImage):
self._rect = surf._rect
self._a = surf._a
self.display_list = surf.display_list
self._children = surf._children
def blit(self, surf, pos=None):
assert isinstance(surf, OpenGLImage)
if surf not in self._children:
if pos is not None: surf.pos = pos
surf._parent = self
self._children.append(surf)
def draw(self):
glLoadIdentity()
if self.rect.w <= self._parent.rect.w and \
self.rect.h <= self._parent.rect.h:
# Mask the area, so nothing is drawn outside surface area.
bottom = self._default_screen.h - self.rect_abs.bottom
glScissor(self.rect_abs.x, bottom,
self.rect.w+1, self.rect.h+1)
glTranslatef(self.pos_abs[0], self.pos_abs[1], 0)
for dl,col in self.display_list:
if col is not None: glColor(col[0], col[1], col[2], self.a)
glCallList(dl)
for child in self._children:
child.draw()
def fill(self, col=0, rect=None, special_flags=0):
"""If col == 0 and rect is None, clears image with no fill."""
if rect is None:
# Clear the surface
self.display_list = []
self._children = []
rect = Rect((0,0), self.size)
if col != 0:
col = [c/255. for c in col]
dl = glGenLists(1)
glNewList(dl, GL_COMPILE)
glRectfv(rect.topleft, rect.bottomright)
glEndList()
self.display_list.append((dl, col))
def copy(self):
return OpenGLImage(self)
def get_size(self):
return self.size
def set_alpha(self, alpha):
self._a = alpha/255.
def set_at(self, vertex, col):
col = [c/255. for c in col]
if self._lock:
glColor(*col)
else:
dl = glGenLists(1)
glNewList(dl, GL_COMPILE)
glBegin(GL_POINTS)
glVertex(vertex)
if not self._lock:
glEnd()
glEndList()
surf.display_list.append((dl, col))
def lock(self):
"""
Should only be used for multiple set_at() calls.
Should not be used in conjuction with the draw functions.
"""
self._lock = True
self._lock_dl = glGenLists(1)
glNewList(self._lock_dl, GL_COMPILE)
glBegin(GL_POINTS)
def unlock(self):
glEnd()
glEndList()
self.display_list.append((self._lock_dl, None))
del self._lock_dl
self._lock = False
def replace(self, surf, **kwargs):
self._rect.size = surf.size
self.display_list = surf.display_list[:]
self._children = surf._children[:]
@property
def a(self):
if self._a is not None:
return self._a
else:
return self._parent.a
# --- Dummy methods. Ignore. ---
def __call__(self):
return self
def set_colorkey(self, col):
pass
class OpenGLFont():
"""
Wraps the FTGL.TextureFont to allow it to be added
to an OpenGLImage object in the same manner as pygame.Font.
"""
def __init__(self, font, size):
self._children = []
self.font = FTGL.TextureFont(font)
self.font.FaceSize(16)
self.y_offset = self.font.line_height * .75
def render(self, text, antialias, color, background=None):
text = text.encode()
col = [c/255. for c in color]
dl = glGenLists(1)
self.size = (self.font.Advance(text), self.font.line_height)
glNewList(dl, GL_COMPILE)
glPushMatrix()
# Flip text right way up
glMultMatrixf((1, 0, 0, 0,
0,-1, 0, 0,
0, 0, 1, 0,
0, self.y_offset, 0, 1))
self.font.Render(text)
glPopMatrix()
glEndList()
self.display_list = [(dl, col)]
return self
class Draw():
"""
Class to emulate the pygame.draw module.
Functions should work in the same manner.
"""
def rect(self, surf, col, rect, width=0):
col = [c/255. for c in col]
dl = glGenLists(1)
glNewList(dl, GL_COMPILE)
if not width:
glRectfv(rect.topleft, rect.bottomright)
else:
if width > 1: width -= 1
hw = width/2.
glLineWidth(width)
glBegin(GL_LINES)
glVertex(rect.x, rect.y + hw)
glVertex(rect.right - width, rect.y + hw)
# (hw%1) fixes line rendering off by a pixel
glVertex(rect.right - hw, rect.y + (hw%1))
glVertex(rect.right - hw, rect.bottom - width + (hw%1))
glVertex(rect.right, rect.bottom - hw)
glVertex(rect.x + width, rect.bottom - hw)
glVertex(rect.x + hw, rect.bottom)
glVertex(rect.x + hw, rect.y + width)
glEnd()
glEndList()
surf.display_list.append((dl, col))
def polygon(self, surf, col, pointlist, width=0):
"""
With width == 0, can only draw convex polygons. Be careful of
the order of vertices if it draws differently than pygame.
"""
col = [c/255. for c in col]
dl = glGenLists(1)
glNewList(dl, GL_COMPILE)
if not width:
glBegin(GL_POLYGON)
for v in pointlist:
glVertex(v)
glEnd()
glEndList()
surf.display_list.append((dl, col))
def circle(self, surf, col, pos, radius, width=0):
col = [c/255. for c in col]
dl = glGenLists(1)
glNewList(dl, GL_COMPILE)
if not width:
glEnable(GL_POINT_SMOOTH)
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)
glPointSize(radius*2)
glBegin(GL_POINTS)
glVertex(pos)
glEnd()
glDisable(GL_POINT_SMOOTH)
glPointSize()
glEndList()
surf.display_list.append((dl, col))
def line(self, surf, col, start_pos, end_pos, width=1):
col = [c/255. for c in col]
dl = glGenLists(1)
glNewList(dl, GL_COMPILE)
glLineWidth(width)
glBegin(GL_LINES)
glVertex(start_pos)
glVertex(end_pos)
glEnd()
glEndList()
surf.display_list.append((dl, col))
# Export Draw functions
draw = Draw()