4 When you set the display mode for OpenGL, you enable all the coolness of 3D rendering,
5 but you disable the bread-and-butter raster SDL functionality like fill() and blit().
6 Since the GUI libraries use those surface methods extensively, they cannot readily be
7 used in OpenGL displays.
9 Lamina provides the LaminaPanelSurface and LaminaScreenSurface classes, which bridge
12 The 'surf' attribute is a surface, and can be drawn on, blitted to, and passed to GUI
13 rendering functions for alteration. The 'display' method displays the surface as a
14 transparent textured quad in the OpenGL model-space. The 'refresh' method indicates that
15 the surface has changed, and that the texture needs regeneration. The 'clear' method
16 restores the blank and transparent original condition.
18 Usage is vaguely like this incomplete pseudocode:
20 # create gui with appropriate constructor
21 gui = GUI_Constructor()
23 # create LaminaPanelSurface
24 gui_screen = lamina.LaminaPanelSurface( (640,480), (-1,1,2,2) )
26 # draw widgets on surface
27 gui.draw( gui_screen.surf )
30 pygame.mouse.set_visible(0)
34 # do input events ....
38 # detect changes to surface
39 changed = gui.update( gui_screen.surf )
44 # draw opengl geometry .....
47 # opengl code to set modelview matrix for desired gui surface pos
51 If your gui screen is not sized to match the display, hide the system
52 mouse cursor, and use the convertMousePos method to position your own
53 OpenGL mouse cursor (a cone or something). The result of the
54 convertMousePos is 2D, (x,y) so you need to draw the mouse with the same
55 modelview matrix as drawing the LaminaPanelSurface itself.
57 mouse_pos = gui_screen.convertMousePos(mouseX, mouseY)
58 glTranslate(*mouse_pos)
59 # opengl code to display your mouse object (a cone?)
62 The distribution package includes several demo scripts which are functional. Refer
63 to them for details of working code.
66 import OpenGL
.GLU
as oglu
67 import OpenGL
.GL
as ogl
73 def load_texture(surf
):
74 """Load surface into texture object. Return texture object.
75 @param surf: surface to make texture from.
77 txtr
= ogl
.glGenTextures(1)
78 textureData
= pygame
.image
.tostring(surf
, "RGBA", 1)
80 ogl
.glEnable(ogl
.GL_TEXTURE_2D
)
81 ogl
.glBindTexture(ogl
.GL_TEXTURE_2D
, txtr
)
82 width
, height
= surf
.get_size()
83 ogl
.glTexImage2D( ogl
.GL_TEXTURE_2D
, 0, ogl
.GL_RGBA
, width
, height
, 0,
84 ogl
.GL_RGBA
, ogl
.GL_UNSIGNED_BYTE
, textureData
)
85 ogl
.glTexParameterf(ogl
.GL_TEXTURE_2D
, ogl
.GL_TEXTURE_MAG_FILTER
, ogl
.GL_NEAREST
)
86 ogl
.glTexParameterf(ogl
.GL_TEXTURE_2D
, ogl
.GL_TEXTURE_MIN_FILTER
, ogl
.GL_NEAREST
)
87 ogl
.glDisable(ogl
.GL_TEXTURE_2D
)
90 def overlay_texture(txtr
, surf
, r
):
91 """Load surface into texture object, replacing part of txtr
93 @param txtr: texture to add to
94 @param surf: surface to copy from
95 @param r: rectangle indicating area to overlay.
97 subsurf
= surf
.subsurface(r
)
98 textureData
= pygame
.image
.tostring(subsurf
, "RGBA", 1)
100 hS
, wS
= surf
.get_size()
101 rect
= pygame
.Rect(r
.x
,hS
-(r
.y
+r
.height
),r
.width
,r
.height
)
102 #print 'overlaying x %i y %i (%i-%i) w %i h %i' % \
103 # (rect.x, rect.y, hS, r.y, r.width, r.height)
105 ogl
.glEnable(ogl
.GL_TEXTURE_2D
)
106 ogl
.glBindTexture(ogl
.GL_TEXTURE_2D
, txtr
)
107 #print 'overlay', rect.x, rect.y, rect.width, rect.height, 'img', len(textureData)
108 ogl
.glTexSubImage2D(ogl
.GL_TEXTURE_2D
, 0, rect
.x
, rect
.y
, rect
.width
, rect
.height
,
109 ogl
.GL_RGBA
, ogl
.GL_UNSIGNED_BYTE
, textureData
)
110 ogl
.glDisable(ogl
.GL_TEXTURE_2D
)
112 class LaminaPanelSurface(object):
113 """Surface for imagery to overlay.
115 @ivar dims: tuple with corners of quad
118 def __init__(self
, quadDims
=(-1,-1,2,2), winSize
=None):
119 """Initialize new instance.
120 @param winSize: tuple (width, height)
121 @param quadDims: tuple (left, top, width, height)
124 winSize
= pygame
.display
.get_surface().get_size()
125 self
._winSize
= winSize
126 left
, top
, width
, height
= quadDims
127 right
, bottom
= left
+width
, top
-height
128 self
._qdims
= quadDims
129 self
.dims
= (left
,top
,0), (right
,top
,0), (right
,bottom
,0), (left
,bottom
,0)
133 """Restore the total transparency to the surface. """
135 while powerOfTwo
< max(*self
._winSize
):
137 raw
= pygame
.Surface((powerOfTwo
, powerOfTwo
), pygame
.SRCALPHA
, 32)
138 self
._surfTotal
= raw
.convert_alpha()
139 self
._usable
= 1.0*self
._winSize
[0]/powerOfTwo
, 1.0*self
._winSize
[1]/powerOfTwo
140 self
.surf
= self
._surfTotal
.subsurface(0,0,self
._winSize
[0],self
._winSize
[1])
144 """Force regen of texture object. Call after change to the GUI appearance. """
146 ogl
.glDeleteTextures([self
._txtr
])
149 def refresh(self
, dirty
=None):
150 """Refresh the texture from the surface.
151 @param dirty: list of rectangles to update, None for whole panel
154 self
._txtr
= load_texture(self
._surfTotal
)
156 wS
, hS
= self
._surfTotal
.get_size()
158 dirty
= [pygame
.Rect(0,0,wS
,hS
)]
160 #dirty = [pygame.Rect(r.x,hS-r.y,r.width,r.height) for r in dirty]
162 overlay_texture(self
._txtr
,self
._surfTotal
,r
)
164 def convertMousePos(self
, pos
):
165 """Converts 2d pixel mouse pos to 2d gl units.
166 @param pos: 2-tuple with x,y of mouse
169 x
= x0
/self
._winSize
[0]*self
._qdims
[2] + self
._qdims
[0]
170 y
= y0
/self
._winSize
[1]*self
._qdims
[3] + self
._qdims
[1]
174 """Draw surface to a quad. Call as part of OpenGL rendering code."""
175 ogl
.glEnable(ogl
.GL_BLEND
)
176 ogl
.glBlendFunc(ogl
.GL_SRC_ALPHA
, ogl
.GL_ONE_MINUS_SRC_ALPHA
)
177 ogl
.glEnable(ogl
.GL_TEXTURE_2D
)
178 ogl
.glBindTexture(ogl
.GL_TEXTURE_2D
, self
._txtr
)
179 ogl
.glTexEnvf(ogl
.GL_TEXTURE_ENV
, ogl
.GL_TEXTURE_ENV_MODE
, ogl
.GL_REPLACE
)
181 ogl
.glBegin(ogl
.GL_QUADS
)
182 ogl
.glTexCoord2f(0.0, 1.0)
183 ogl
.glVertex3f(*self
.dims
[0])
184 ogl
.glTexCoord2f(self
._usable
[0], 1.0)
185 ogl
.glVertex3f(*self
.dims
[1])
186 ogl
.glTexCoord2f(self
._usable
[0], 1-self
._usable
[1])
187 ogl
.glVertex3f(*self
.dims
[2])
188 ogl
.glTexCoord2f(0.0, 1-self
._usable
[1])
189 ogl
.glVertex3f(*self
.dims
[3])
191 ogl
.glDisable(ogl
.GL_BLEND
)
192 ogl
.glDisable(ogl
.GL_TEXTURE_2D
)
195 """Draw red/transparent checkerboard. """
196 w
, h
= self
._winSize
[0]*0.25, self
._winSize
[1]*0.25
198 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(0,0,w
,h
), 0)
199 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(2*w
,0,w
,h
), 0)
201 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(w
,h
,w
,h
), 0)
202 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(3*w
,h
,w
,h
), 0)
204 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(0,2*h
,w
,h
), 0)
205 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(2*w
,2*h
,w
,h
), 0)
207 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(w
,3*h
,w
,h
), 0)
208 pygame
.draw
.rect(self
.surf
, (250,0,0), Rect(3*w
,3*h
,w
,h
), 0)
212 class LaminaScreenSurface(LaminaPanelSurface
):
213 """Surface for imagery to overlay. Autofits to actual display.
215 @ivar dims: tuple with corners of quad
218 def __init__(self
, depth
=0):
219 """Initialize new instance.
220 @param depth: (0-1) z-value, if you want to draw your own 3D
221 cursor, set this to a small non-zero value to allow room in
222 front of this overlay to draw the cursor. (0.1 is a first guess)
228 """Setup stuff, after pygame is inited. """
229 self
._winSize
= pygame
.display
.get_surface().get_size()
230 self
.refreshPosition()
233 def refreshPosition(self
):
234 """Recalc where in modelspace quad needs to be to fill screen."""
236 bottomleft
= oglu
.gluUnProject(0, 0, depth
)
237 bottomright
= oglu
.gluUnProject(self
._winSize
[0], 0, depth
)
238 topleft
= oglu
.gluUnProject(0, self
._winSize
[1], depth
)
239 topright
= oglu
.gluUnProject(self
._winSize
[0], self
._winSize
[1], depth
)
240 self
.dims
= topleft
, topright
, bottomright
, bottomleft
241 width
= topright
[0] - topleft
[0]
242 height
= topright
[1] - bottomright
[1]
243 self
._qdims
= topleft
[0], topleft
[1], width
, height
246 class LaminaScreenSurface2(LaminaScreenSurface
):
247 """Surface that defers initialization to setup method. """
249 def __init__(self
, depth
=0):
250 """Initialize new instance. """
253 def refreshPosition(self
):
254 """Recalc where in modelspace quad needs to be to fill screen."""
264 """Display texture. """
267 bottomleft
= oglu
.gluUnProject(0,0,depth
)
268 bottomright
= oglu
.gluUnProject(self
._winSize
[0],0,depth
)
269 topleft
= oglu
.gluUnProject(0,self
._winSize
[1],depth
)
270 topright
= oglu
.gluUnProject(self
._winSize
[0],self
._winSize
[1],depth
)
271 self
.dims
= topleft
, topright
, bottomright
, bottomleft
272 width
= topright
[0] - topleft
[0]
273 height
= topright
[1] - bottomright
[1]
274 self
._qdims
= topleft
[0], topleft
[1], width
, height
275 LaminaScreenSurface
.display(self
)