6 from OpenGL.GL import *
7 from OpenGL.GLU import *
8 from OpenGL.GLUT import *
9 from OpenGL.GL import framebufferobjects
14 BASEDIR = os.path.abspath(os.path.dirname(__file__))
20 texturePath = os.path.join(BASEDIR, "test-patterns")
21 for name in os.listdir(texturePath):
22 if not name.endswith(".ppm"):
25 with open(os.path.join(texturePath, name), "r") as handle:
29 width, height, pixels = texreader.parse_ppm(data)
31 print >> sys.stderr, "Could not read texture from %r: %s" % (
35 res.append((os.path.basename(name), width, height, pixels))
40 def scale_integer(windowW, windowH, inputW, inputH):
42 Calculates the image as the largest integer multiple of raw frame size.
44 scaleX = windowW / inputW
45 scaleY = windowH / inputH
47 if scaleX > 0 and scaleY > 0:
48 scale = min(scaleX, scaleY)
49 finalW = scale * inputW
50 finalH = scale * inputH
65 def scale_aspect(windowW, windowH, inputW, inputH):
67 Calculates the image as the largest multiple of the raw frame size.
70 float(windowW) / float(inputW),
71 float(windowH) / float(inputH),
74 return inputW * scale, inputH * scale
77 def scale_max(windowW, windowH, inputW, inputH):
79 Stretch the image to the largest possible size, regardless of aspect.
81 return windowW, windowH
85 ("Integer", scale_integer),
86 ("Aspect-correct", scale_aspect),
87 ("Maximized", scale_max),
91 class GLUTDemo(object):
93 def __init__(self, shaderFile, textures):
94 self.start = time.clock()
97 glutInitDisplayMode(GLUT_RGBA)
99 glutInitWindowSize(640, 480)
100 self.window = glutCreateWindow("XML Shader Demo - Esc to exit")
104 glutDisplayFunc(self._draw_scene)
105 glutIdleFunc(self._handle_idle)
106 glutReshapeFunc(self._handle_resize)
107 glutKeyboardFunc(self._handle_key)
109 glClearColor(0.0, 0.0, 0.0, 0.0)
110 glEnable(GL_TEXTURE_2D)
112 with open(shaderFile, "r") as handle:
113 self.shaderPasses = shaderreader.parse_shader(handle.read())
115 self.textures = textures
116 textureMenu = glutCreateMenu(self._set_texture)
117 for index, (filename, _, _, _) in enumerate(self.textures):
118 glutAddMenuEntry(filename, index)
120 self.textureID = None
127 self.framebufferID = framebufferobjects.glGenFramebuffers(1)
128 self.framebufferTexture1, self.framebufferTexture2 = \
131 scaleMenu = glutCreateMenu(self._set_scale_method)
132 for index, (desc, _) in enumerate(SCALE_METHODS):
133 glutAddMenuEntry(desc, index)
135 self._set_scale_method(0)
137 self.masterMenu = glutCreateMenu(lambda _: 0)
138 glutAddSubMenu("Test pattern", textureMenu)
139 glutAddSubMenu("Scale method", scaleMenu)
140 glutAttachMenu(GLUT_RIGHT_BUTTON)
142 def _set_texture(self, textureIndex):
143 if self.textureID is None:
144 self.textureID = glGenTextures(1)
146 glBindTexture(GL_TEXTURE_2D, self.textureID)
148 filename, self.inputW, self.inputH, pixels = \
149 self.textures[textureIndex]
151 # We assume we don't have to expand the texture dimensions to the next
152 # larger power-of-two.
153 self.textureW = self.inputW
154 self.textureH = self.inputH
156 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.textureW, self.textureH,
157 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels)
159 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
160 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
162 # According to the OpenGL docs, a glutCreateMenu callback doesn't need
163 # to return anything. PyOpenGL seems to think differently.
166 def _set_scale_method(self, index):
167 self.scaleMethod = SCALE_METHODS[index][1]
169 # According to the OpenGL docs, a glutCreateMenu callback doesn't need
170 # to return anything. PyOpenGL seems to think differently.
173 def _setUniform(self, program, name, x, y):
174 loc = glGetUniformLocation(program, name)
179 glUniform2f(loc, x, y)
181 def _render_pass(self, shaderPass,
183 fromFrameW, fromFrameH,
184 fromTextureW, fromTextureH,
188 # How big should the output of this pass be?
189 outputW, outputH = shaderPass.calculateFramebufferSize(
190 fromFrameW, fromFrameH, finalW, finalH,
193 # If this pass doesn't specify a size, default to the input.
194 if outputW is None: outputW = fromFrameW
195 if outputH is None: outputH = fromFrameH
197 # Make a texture exactly that big.
198 glBindTexture(GL_TEXTURE_2D, toTextureID)
199 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, outputW, outputH,
200 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, None)
201 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
202 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
205 # Configure OpenGL to render to the texture.
206 framebufferobjects.glBindFramebuffer(
207 framebufferobjects.GL_FRAMEBUFFER,
210 framebufferobjects.glFramebufferTexture2D(
211 framebufferobjects.GL_FRAMEBUFFER,
212 framebufferobjects.GL_COLOR_ATTACHMENT0,
219 framebufferobjects.glCheckFramebufferStatus(
220 framebufferobjects.GL_FRAMEBUFFER,
223 # Let's do our rendering pass.
224 glUseProgram(shaderPass.programID)
226 self._setUniform(shaderPass.programID, 'rubyInputSize',
227 fromFrameW, fromFrameH)
228 self._setUniform(shaderPass.programID, 'rubyOutputSize',
230 self._setUniform(shaderPass.programID, 'rubyTextureSize',
231 fromTextureW, fromTextureH)
232 loc = glGetUniformLocation(shaderPass.programID, 'rubyFrameCount')
234 glUniform1i(loc, self.framecount)
236 glBindTexture(GL_TEXTURE_2D, fromTextureID)
238 if shaderPass.filterMethod == shaderreader.ATTR_FILTER_LINEAR:
239 glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
240 glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
242 glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
243 glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
245 glViewport(0, 0, outputW, outputH)
246 glMatrixMode(GL_PROJECTION)
248 # FIXME: This needs to set up a coordinate system that's vertically
249 # flipped from the one in _draw_scene() so that the input texture comes
250 # out the right way up. Why is this so, and do we need to flip every
252 gluOrtho2D(0, outputW, outputH, 0)
254 glClear(GL_COLOR_BUFFER_BIT)
256 glBegin(GL_QUADS) # Start drawing a 4 sided polygon
258 # OpenGL uses texture-coordinates between 0.0 and 1.0, rather than
260 txW = float(fromFrameW) / fromTextureW
261 txH = float(fromFrameH) / fromTextureH
263 glTexCoord2f(0, 0 ); glVertex(0, 0, )
264 glTexCoord2f(txW, 0 ); glVertex(outputW, 0, )
265 glTexCoord2f(txW, txH); glVertex(outputW, outputH)
266 glTexCoord2f(0, txH); glVertex(0, outputH)
268 glEnd() # We are done with the polygon
270 # Detach all the things we set up.
272 framebufferobjects.glBindFramebuffer(
273 framebufferobjects.GL_FRAMEBUFFER,
277 def _draw_scene(self):
278 if None in (self.textureID, self.textureW, self.textureH,
279 self.windowW, self.windowH):
282 finalW, finalH = self.scaleMethod(
283 self.windowW, self.windowH,
284 self.inputW, self.inputH,
287 finalX = (self.windowW - finalW) / 2
288 finalY = (self.windowH - finalH) / 2
290 self._render_pass(self.shaderPasses[0],
292 self.inputW, self.inputH,
293 self.textureW, self.textureH,
295 self.framebufferTexture1)
297 glViewport(0, 0, self.windowW, self.windowH)
298 glMatrixMode(GL_PROJECTION)
300 gluOrtho2D(0, self.windowW, 0, self.windowH)
302 glClear(GL_COLOR_BUFFER_BIT)
304 glBindTexture(GL_TEXTURE_2D, self.framebufferTexture1)
305 glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
306 glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
308 glBegin(GL_QUADS) # Start drawing a 4 sided polygon
310 glTexCoord(0, 0 ); glVertex(finalX, finalY, )
311 glTexCoord(1.0, 0 ); glVertex(finalX+finalW, finalY, )
312 glTexCoord(1.0, 1.0); glVertex(finalX+finalW, finalY+finalH)
313 glTexCoord(0, 1.0); glVertex(finalX, finalY+finalH)
315 glEnd() # We are done with the polygon
319 def _handle_key(self, key, x, y):
320 if key == '\x1b': # Escape
323 def _handle_resize(self, width, height):
324 self.windowW = max(width, 1)
325 self.windowH = max(height, 1)
327 def _handle_idle(self):
329 if now > self.start + 1:
330 fps = self.framecount / (now - self.start)
331 sys.stdout.write("FPS: %0.1f\r" % fps)
336 glutPostWindowRedisplay(self.window)
339 if __name__ == "__main__":
340 argv = glutInit(sys.argv)
343 print >> sys.stderr, "Usage: %s /path/to/shaderfile" % (argv[0],)
347 textures = load_textures()
350 print >> sys.stderr, "Can't find any textures to display."
353 demo = GLUTDemo(shaderFile, textures)