changeset 423:d8630c086926

Replace Pyglet with our own Cython OpenGL wrapper.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 16 Jul 2013 21:07:15 +0200
parents 52829ebe2561
children f4d76d3d6f2a
files pytouhou/lib/opengl.pxd pytouhou/lib/opengl.pyxbld pytouhou/ui/background.pxd pytouhou/ui/background.pyx pytouhou/ui/background.pyxbld pytouhou/ui/gamerenderer.py pytouhou/ui/gamerenderer.pyx pytouhou/ui/gamerenderer.pyxbld pytouhou/ui/gamerunner.py pytouhou/ui/gamerunner.pyx pytouhou/ui/gamerunner.pyxbld pytouhou/ui/renderer.pxd pytouhou/ui/renderer.pyx pytouhou/ui/renderer.pyxbld pytouhou/ui/texture.pyx pytouhou/ui/texture.pyxbld pytouhou/ui/window.py pytouhou/ui/window.pyx pytouhou/ui/window.pyxbld pytouhou/utils/matrix.pxd pytouhou/utils/matrix.pyx setup.py
diffstat 22 files changed, 801 insertions(+), 571 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/lib/opengl.pxd	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,108 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+cdef extern from 'GL/gl.h':
+    ctypedef unsigned int GLuint
+    ctypedef int GLint
+    ctypedef float GLfloat
+    ctypedef float GLclampf
+    ctypedef char GLboolean
+    ctypedef char GLchar
+    ctypedef unsigned int GLsizei
+    ctypedef unsigned int GLsizeiptr
+    ctypedef unsigned int GLbitfield
+    ctypedef void GLvoid
+
+    ctypedef enum GLenum:
+        GL_ARRAY_BUFFER
+        GL_STATIC_DRAW
+        GL_DYNAMIC_DRAW
+        GL_UNSIGNED_BYTE
+        GL_UNSIGNED_SHORT
+        GL_INT
+        GL_FLOAT
+        GL_SRC_ALPHA
+        GL_ONE_MINUS_SRC_ALPHA
+        GL_ONE
+        GL_TEXTURE_2D
+        GL_TRIANGLES
+        GL_DEPTH_TEST
+        GL_QUADS
+
+        GL_TEXTURE_MIN_FILTER
+        GL_TEXTURE_MAG_FILTER
+        GL_LINEAR
+        GL_BGRA
+        GL_RGBA
+        GL_RGB
+        GL_LUMINANCE
+        GL_UNSIGNED_SHORT_5_6_5
+        GL_UNSIGNED_SHORT_4_4_4_4_REV
+
+        GL_COLOR_BUFFER_BIT
+        GL_SCISSOR_TEST
+        GL_MODELVIEW
+        GL_FOG
+
+        GL_DEPTH_BUFFER_BIT
+        GL_PROJECTION
+        GL_FOG_MODE
+        GL_FOG_START
+        GL_FOG_END
+        GL_FOG_COLOR
+
+        GL_BLEND
+        GL_PERSPECTIVE_CORRECTION_HINT
+        GL_FOG_HINT
+        GL_NICEST
+        GL_COLOR_ARRAY
+        GL_VERTEX_ARRAY
+        GL_TEXTURE_COORD_ARRAY
+
+    void glVertexPointer(GLint size, GLenum type_, GLsizei stride, GLvoid *pointer)
+    void glTexCoordPointer(GLint size, GLenum type_, GLsizei stride, GLvoid *pointer)
+    void glColorPointer(GLint size, GLenum type_, GLsizei stride, GLvoid *pointer)
+    void glVertexAttribPointer(GLuint index, GLint size, GLenum type_, GLboolean normalized, GLsizei stride, const GLvoid *pointer)
+    void glEnableVertexAttribArray(GLuint index)
+
+    void glBlendFunc(GLenum sfactor, GLenum dfactor)
+    void glDrawArrays(GLenum mode, GLint first, GLsizei count)
+    void glDrawElements(GLenum mode, GLsizei count, GLenum type_, const GLvoid *indices)
+    void glEnable(GLenum cap)
+    void glDisable(GLenum cap)
+
+    void glGenBuffers(GLsizei n, GLuint * buffers)
+    void glDeleteBuffers(GLsizei n, const GLuint * buffers)
+    void glBindBuffer(GLenum target, GLuint buffer_)
+    void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
+
+    void glGenTextures(GLsizei n, GLuint *textures)
+    void glBindTexture(GLenum target, GLuint texture)
+    void glTexParameteri(GLenum target, GLenum pname, GLint param)
+    void glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format_, GLenum type_, const GLvoid *data)
+
+    void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) #XXX
+    void glClear(GLbitfield mask)
+    void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
+    void glScissor(GLint x, GLint y, GLsizei width, GLsizei height)
+    void glMatrixMode(GLenum mode)
+    void glLoadIdentity()
+    void glLoadMatrixf(const GLfloat * m)
+
+    void glFogi(GLenum pname, GLint param)
+    void glFogf(GLenum pname, GLfloat param)
+    void glFogfv(GLenum pname, const GLfloat * params)
+
+    void glHint(GLenum target, GLenum mode)
+    void glEnableClientState(GLenum cap)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/lib/opengl.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+""" Build instructions for the OpenGL module. """
+
+from distutils.extension import Extension
+from subprocess import check_output
+
+COMMAND = 'pkg-config'
+LIBRARIES = ['gl']
+
+def make_ext(modname, pyxfilename):
+    """ Compile and link with the corrects options. """
+    compile_args = check_output([COMMAND, '--cflags'] + LIBRARIES).split()
+    link_args = check_output([COMMAND, '--libs'] + LIBRARIES).split()
+    return Extension(name=modname,
+                     sources=[pyxfilename],
+                     extra_compile_args=compile_args,
+                     extra_link_args=link_args)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/background.pxd	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,15 @@
+cdef struct Vertex:
+    float x, y, z
+    float u, v
+    unsigned char r, g, b, a
+
+
+cdef class BackgroundRenderer:
+    cdef public texture_manager
+    cdef object texture_key
+    cdef unsigned short blendfunc, nb_vertices
+    cdef Vertex *vertex_buffer
+    cdef unsigned int use_fixed_pipeline, vbo
+
+    cpdef render_background(self)
+    cpdef prerender(self, background)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/background.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,101 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+from libc.stdlib cimport malloc, free, realloc
+
+from pytouhou.lib.opengl cimport \
+         (glVertexPointer, glTexCoordPointer, glColorPointer,
+          glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc,
+          glBindTexture, glBindBuffer, glBufferData, GL_ARRAY_BUFFER,
+          GL_STATIC_DRAW, GL_UNSIGNED_BYTE, GL_FLOAT, GL_SRC_ALPHA,
+          GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, glGenBuffers,
+          glEnable, glDisable, GL_DEPTH_TEST, glDrawArrays, GL_QUADS)
+
+from .sprite cimport get_sprite_rendering_data
+
+
+cdef class BackgroundRenderer:
+    def __cinit__(self):
+        # Allocate buffers
+        self.vertex_buffer = <Vertex*> malloc(65536 * sizeof(Vertex))
+
+
+    def __dealloc__(self):
+        free(self.vertex_buffer)
+
+
+    def __init__(self, texture_manager, use_fixed_pipeline):
+        self.texture_manager = texture_manager
+        self.use_fixed_pipeline = use_fixed_pipeline
+
+        if not use_fixed_pipeline:
+            glGenBuffers(1, &self.vbo)
+
+
+    cpdef render_background(self):
+        if self.use_fixed_pipeline:
+            glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].x)
+            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u)
+            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r)
+        else:
+            glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
+
+            #TODO: find a way to use offsetof() instead of those ugly hardcoded values.
+            glVertexAttribPointer(0, 3, GL_FLOAT, False, sizeof(Vertex), <void*>0)
+            glEnableVertexAttribArray(0)
+            glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12)
+            glEnableVertexAttribArray(1)
+            glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20)
+            glEnableVertexAttribArray(2)
+
+        glEnable(GL_DEPTH_TEST)
+        glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[self.blendfunc])
+        glBindTexture(GL_TEXTURE_2D, self.texture_manager[self.texture_key])
+        glDrawArrays(GL_QUADS, 0, self.nb_vertices)
+        glDisable(GL_DEPTH_TEST)
+
+        if not self.use_fixed_pipeline:
+            glBindBuffer(GL_ARRAY_BUFFER, 0)
+
+
+    cpdef prerender(self, background):
+        cdef float ox, oy, oz, ox2, oy2, oz2
+        cdef unsigned short nb_vertices = 0
+        cdef Vertex* vertex_buffer
+
+        vertex_buffer = self.vertex_buffer
+
+        for ox, oy, oz, model_id, model in background.object_instances:
+            for ox2, oy2, oz2, width_override, height_override, sprite in model:
+                #TODO: view frustum culling
+                key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite)
+                (x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4) = vertices
+                left, right, bottom, top = uvs
+                r, g, b, a = colors
+
+                vertex_buffer[nb_vertices] = Vertex(x1 + ox + ox2, y1 + oy + oy2, z1 + oz + oz2, left, bottom, r, g, b, a)
+                vertex_buffer[nb_vertices+1] = Vertex(x2 + ox + ox2, y2 + oy + oy2, z2 + oz + oz2, right, bottom, r, g, b, a)
+                vertex_buffer[nb_vertices+2] = Vertex(x3 + ox + ox2, y3 + oy + oy2, z3 + oz + oz2, right, top, r, g, b, a)
+                vertex_buffer[nb_vertices+3] = Vertex(x4 + ox + ox2, y4 + oy + oy2, z4 + oz + oz2, left, top, r, g, b, a)
+
+                nb_vertices += 4
+
+        self.texture_key, self.blendfunc = key
+        self.nb_vertices = nb_vertices
+        self.vertex_buffer = <Vertex*> realloc(vertex_buffer, nb_vertices * sizeof(Vertex))
+
+        if not self.use_fixed_pipeline:
+            glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
+            glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_STATIC_DRAW)
+            glBindBuffer(GL_ARRAY_BUFFER, 0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/background.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/gamerenderer.py	Tue Jul 16 21:07:15 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 3 only.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-
-
-from itertools import chain
-
-from pyglet.gl import (glClear, glMatrixMode, glLoadIdentity, glLoadMatrixf,
-                       glDisable, glEnable, glFogi, glFogf, glFogfv,
-                       GL_DEPTH_BUFFER_BIT, GL_PROJECTION, GL_MODELVIEW,
-                       GL_FOG, GL_FOG_MODE, GL_LINEAR, GL_FOG_START,
-                       GL_FOG_END, GL_FOG_COLOR, GL_COLOR_BUFFER_BIT, GLfloat)
-
-from pytouhou.utils.matrix import Matrix
-from pytouhou.utils.maths import setup_camera
-
-from .renderer import Renderer
-
-
-
-class GameRenderer(Renderer):
-    def __init__(self, resource_loader):
-        Renderer.__init__(self, resource_loader)
-
-
-    def render(self):
-        glClear(GL_DEPTH_BUFFER_BIT)
-
-        back = self.background
-        game = self.game
-
-        if self.use_fixed_pipeline:
-            glMatrixMode(GL_PROJECTION)
-            glLoadIdentity()
-
-        if game is not None and game.spellcard_effect is not None:
-            if self.use_fixed_pipeline:
-                glMatrixMode(GL_MODELVIEW)
-                glLoadMatrixf(self.game_mvp.get_c_data())
-                glDisable(GL_FOG)
-            else:
-                self.game_shader.bind()
-                self.game_shader.uniform_matrixf('mvp', self.game_mvp.get_c_data())
-
-            self.render_elements([game.spellcard_effect])
-        elif back is not None:
-            x, y, z = back.position_interpolator.values
-            dx, dy, dz = back.position2_interpolator.values
-            fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values
-
-            # Those two lines may come from the difference between Direct3D and
-            # OpenGL’s distance handling.  The first one seem to calculate fog
-            # from the eye, while the second does that starting from the near
-            # plane.
-            #TODO: investigate, and use a variable to keep the near plane
-            # distance at a single place.
-            fog_start -= 101010101./2010101.
-            fog_end -= 101010101./2010101.
-
-            model = Matrix()
-            model.data[3] = [-x, -y, -z, 1]
-            view = setup_camera(dx, dy, dz)
-            model_view_projection = model * view * self.proj
-            mvp = model_view_projection.get_c_data()
-
-            if self.use_fixed_pipeline:
-                glMatrixMode(GL_MODELVIEW)
-                glLoadMatrixf(mvp)
-
-                glEnable(GL_FOG)
-                glFogi(GL_FOG_MODE, GL_LINEAR)
-                glFogf(GL_FOG_START, fog_start)
-                glFogf(GL_FOG_END,  fog_end)
-                glFogfv(GL_FOG_COLOR, (GLfloat * 4)(fog_r / 255., fog_g / 255., fog_b / 255., 1.))
-            else:
-                self.background_shader.bind()
-                self.background_shader.uniform_matrixf('mvp', mvp)
-
-                self.background_shader.uniformf('fog_scale', 1. / (fog_end - fog_start))
-                self.background_shader.uniformf('fog_end', fog_end)
-                self.background_shader.uniformf('fog_color', fog_r / 255., fog_g / 255., fog_b / 255., 1.)
-
-            self.render_background()
-        else:
-            glClear(GL_COLOR_BUFFER_BIT)
-
-        if game is not None:
-            if self.use_fixed_pipeline:
-                glMatrixMode(GL_MODELVIEW)
-                glLoadMatrixf(self.game_mvp.get_c_data())
-                glDisable(GL_FOG)
-            else:
-                self.game_shader.bind()
-                self.game_shader.uniform_matrixf('mvp', self.game_mvp.get_c_data())
-
-            self.render_elements(enemy for enemy in game.enemies if enemy.visible)
-            self.render_elements(game.effects)
-            self.render_elements(chain(game.players_bullets,
-                                       game.lasers_sprites(),
-                                       game.players,
-                                       game.msg_sprites()))
-            self.render_elements(chain(game.bullets, game.lasers,
-                                       game.cancelled_bullets, game.items,
-                                       game.labels))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/gamerenderer.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,125 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+
+from libc.stdlib cimport malloc, free
+
+from itertools import chain
+
+from pytouhou.lib.opengl cimport \
+         (glClear, glMatrixMode, glLoadIdentity, glLoadMatrixf, glDisable,
+          glEnable, glFogi, glFogf, glFogfv, GL_DEPTH_BUFFER_BIT,
+          GL_PROJECTION, GL_MODELVIEW, GL_FOG, GL_FOG_MODE, GL_LINEAR,
+          GL_FOG_START, GL_FOG_END, GL_FOG_COLOR, GL_COLOR_BUFFER_BIT, GLfloat)
+
+from pytouhou.utils.matrix cimport Matrix, matrix_to_floats
+from pytouhou.utils.maths cimport setup_camera
+
+from .renderer import Renderer
+
+
+
+class GameRenderer(Renderer):
+    def __init__(self, resource_loader):
+        Renderer.__init__(self, resource_loader)
+
+
+    def render(self):
+        cdef float* fog_data
+
+        glClear(GL_DEPTH_BUFFER_BIT)
+
+        back = self.background
+        game = self.game
+
+        if self.use_fixed_pipeline:
+            glMatrixMode(GL_PROJECTION)
+            glLoadIdentity()
+
+        if game is not None and game.spellcard_effect is not None:
+            if self.use_fixed_pipeline:
+                glMatrixMode(GL_MODELVIEW)
+                glLoadMatrixf(matrix_to_floats(self.game_mvp))
+                glDisable(GL_FOG)
+            else:
+                self.game_shader.bind()
+                self.game_shader.uniform_matrixf('mvp', self.game_mvp.get_c_data())
+
+            self.render_elements([game.spellcard_effect])
+        elif back is not None:
+            x, y, z = back.position_interpolator.values
+            dx, dy, dz = back.position2_interpolator.values
+            fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values
+
+            # Those two lines may come from the difference between Direct3D and
+            # OpenGL’s distance handling.  The first one seem to calculate fog
+            # from the eye, while the second does that starting from the near
+            # plane.
+            #TODO: investigate, and use a variable to keep the near plane
+            # distance at a single place.
+            fog_start -= 101010101./2010101.
+            fog_end -= 101010101./2010101.
+
+            model = Matrix()
+            model.data[3] = [-x, -y, -z, 1]
+            view = setup_camera(dx, dy, dz)
+            model_view_projection = model * view * self.proj
+            mvp = model_view_projection.get_c_data()
+            mvp_cython = matrix_to_floats(model_view_projection)
+
+            if self.use_fixed_pipeline:
+                glMatrixMode(GL_MODELVIEW)
+                glLoadMatrixf(mvp_cython)
+
+                glEnable(GL_FOG)
+                glFogi(GL_FOG_MODE, GL_LINEAR)
+                glFogf(GL_FOG_START, fog_start)
+                glFogf(GL_FOG_END,  fog_end)
+                fog_data = <float*>malloc(4 * sizeof(float))
+                fog_data[0] = fog_r / 255.
+                fog_data[1] = fog_g / 255.
+                fog_data[2] = fog_b / 255.
+                fog_data[3] = 1.
+                glFogfv(GL_FOG_COLOR, fog_data)
+                free(fog_data)
+            else:
+                self.background_shader.bind()
+                self.background_shader.uniform_matrixf('mvp', mvp)
+
+                self.background_shader.uniformf('fog_scale', 1. / (fog_end - fog_start))
+                self.background_shader.uniformf('fog_end', fog_end)
+                self.background_shader.uniformf('fog_color', fog_r / 255., fog_g / 255., fog_b / 255., 1.)
+
+            self.background_renderer.render_background()
+        else:
+            glClear(GL_COLOR_BUFFER_BIT)
+
+        if game is not None:
+            if self.use_fixed_pipeline:
+                glMatrixMode(GL_MODELVIEW)
+                glLoadMatrixf(matrix_to_floats(self.game_mvp))
+                glDisable(GL_FOG)
+            else:
+                self.game_shader.bind()
+                self.game_shader.uniform_matrixf('mvp', self.game_mvp.get_c_data())
+
+            self.render_elements(enemy for enemy in game.enemies if enemy.visible)
+            self.render_elements(game.effects)
+            self.render_elements(chain(game.players_bullets,
+                                       game.lasers_sprites(),
+                                       game.players,
+                                       game.msg_sprites()))
+            self.render_elements(chain(game.bullets, game.lasers,
+                                       game.cancelled_bullets, game.items,
+                                       game.labels))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/gamerenderer.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/gamerunner.py	Tue Jul 16 21:07:15 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 3 only.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-
-from pytouhou.lib import sdl
-
-from pyglet.gl import (glMatrixMode, glEnable, glDisable, glViewport,
-                       glScissor, glLoadMatrixf, glGenBuffers, glDeleteBuffers,
-                       GL_MODELVIEW, GL_PROJECTION, GL_FOG, GL_SCISSOR_TEST)
-
-from pytouhou.utils.helpers import get_logger
-from pytouhou.utils.maths import perspective, setup_camera, ortho_2d
-
-from .gamerenderer import GameRenderer
-from .music import MusicPlayer, SFXPlayer, NullPlayer
-from .shaders.eosd import GameShader, BackgroundShader
-
-from ctypes import c_uint, byref
-
-
-logger = get_logger(__name__)
-
-
-class GameRunner(GameRenderer):
-    def __init__(self, window, resource_loader, replay=None, skip=False):
-        GameRenderer.__init__(self, resource_loader)
-
-        self.window = window
-        self.replay_level = None
-        self.skip = skip
-        self.keystate = 0
-
-        self.use_fixed_pipeline = window.use_fixed_pipeline #XXX
-        self.width = window.width #XXX
-        self.height = window.height #XXX
-
-        if not self.use_fixed_pipeline:
-            self.game_shader = GameShader()
-            self.background_shader = BackgroundShader()
-            self.interface_shader = self.game_shader
-
-            vbo_array = (c_uint * 2)()
-            glGenBuffers(2, vbo_array)
-            self.vbo, self.back_vbo = vbo_array
-
-
-    def load_game(self, game=None, background=None, bgms=None, replay=None, save_keystates=None):
-        self.game = game
-        self.background = background
-
-        self.texture_manager.preload(game.resource_loader.instanced_anms.values())
-
-        if background:
-            self.prerender_background(background)
-
-        self.set_input(replay)
-        if replay and replay.levels[game.stage - 1]:
-            game.players[0].state.lives = self.replay_level.lives
-            game.players[0].state.power = self.replay_level.power
-            game.players[0].state.bombs = self.replay_level.bombs
-            game.difficulty = self.replay_level.difficulty
-
-        self.save_keystates = save_keystates
-
-        game.music = MusicPlayer(game.resource_loader, bgms)
-        game.music.play(0)
-        game.sfx_player = SFXPlayer(game.resource_loader) if not self.skip else NullPlayer()
-
-
-    def set_input(self, replay=None):
-        if not replay or not replay.levels[self.game.stage-1]:
-            self.replay_level = None
-        else:
-            self.replay_level = replay.levels[self.game.stage-1]
-            self.keys = self.replay_level.iter_keystates()
-
-
-    def start(self):
-        width = self.game.interface.width if self.game else 640
-        height = self.game.interface.height if self.game else 480
-        if (width, height) != (self.width, self.height):
-            self.window.set_size(width, height)
-
-        self.proj = perspective(30, float(self.game.width) / float(self.game.height),
-                                101010101./2010101., 101010101./10101.)
-        game_view = setup_camera(0, 0, 1)
-        self.game_mvp = game_view * self.proj
-        self.interface_mvp = ortho_2d(0., float(self.width), float(self.height), 0.)
-
-
-    def finish(self):
-        #TODO: actually clean after buffers are not needed anymore.
-        #if not self.use_fixed_pipeline:
-        #    vbo_array = (c_uint * 2)(self.vbo, self.back_vbo)
-        #    glDeleteBuffers(2, vbo_array)
-        pass
-
-
-    def update(self):
-        if self.background:
-            self.background.update(self.game.frame)
-        for event in sdl.poll_events():
-            type_ = event[0]
-            if type_ == sdl.KEYDOWN:
-                scancode = event[1]
-                if scancode == sdl.SCANCODE_ESCAPE:
-                    return False #TODO: implement the pause.
-            elif type_ == sdl.QUIT:
-                return False
-        if self.game:
-            if not self.replay_level:
-                #TODO: allow user settings
-                keys = sdl.get_keyboard_state()
-                keystate = 0
-                if keys[sdl.SCANCODE_Z]:
-                    keystate |= 1
-                if keys[sdl.SCANCODE_X]:
-                    keystate |= 2
-                if keys[sdl.SCANCODE_LSHIFT]:
-                    keystate |= 4
-                if keys[sdl.SCANCODE_UP]:
-                    keystate |= 16
-                if keys[sdl.SCANCODE_DOWN]:
-                    keystate |= 32
-                if keys[sdl.SCANCODE_LEFT]:
-                    keystate |= 64
-                if keys[sdl.SCANCODE_RIGHT]:
-                    keystate |= 128
-                if keys[sdl.SCANCODE_LCTRL]:
-                    keystate |= 256
-            else:
-                try:
-                    keystate = self.keys.next()
-                except StopIteration:
-                    keystate = 0
-                    if self.skip:
-                        self.set_input()
-                        self.skip = False
-                        self.game.sfx_player = SFXPlayer(self.game.resource_loader)
-
-            if self.save_keystates is not None:
-                self.save_keystates.append(keystate)
-
-            self.game.run_iter(keystate)
-        if not self.skip:
-            self.render_game()
-            self.render_interface()
-        return True
-
-
-    def render_game(self):
-        # Switch to game projection
-        #TODO: move that to GameRenderer?
-        x, y = self.game.interface.game_pos
-        glViewport(x, y, self.game.width, self.game.height)
-        glScissor(x, y, self.game.width, self.game.height)
-        glEnable(GL_SCISSOR_TEST)
-
-        GameRenderer.render(self)
-
-        glDisable(GL_SCISSOR_TEST)
-
-
-    def render_interface(self):
-        interface = self.game.interface
-        interface.labels['framerate'].set_text('%.2ffps' % self.window.clock.get_fps())
-
-        if self.use_fixed_pipeline:
-            glMatrixMode(GL_MODELVIEW)
-            glLoadMatrixf(self.interface_mvp.get_c_data())
-            glDisable(GL_FOG)
-        else:
-            self.interface_shader.bind()
-            self.interface_shader.uniform_matrixf('mvp', self.interface_mvp.get_c_data())
-        glViewport(0, 0, self.width, self.height)
-
-        items = [item for item in interface.items if item.anmrunner and item.anmrunner.running]
-        labels = interface.labels.values()
-
-        if items:
-            # Redraw all the interface
-            self.render_elements(items)
-        else:
-            # Redraw only changed labels
-            labels = [label for label in labels if label.changed]
-
-        self.render_elements(interface.level_start)
-
-        if self.game.boss:
-            self.render_elements(interface.boss_items)
-
-        self.render_elements(labels)
-        for label in labels:
-            label.changed = False
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/gamerunner.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,205 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+from pytouhou.lib import sdl
+
+from pytouhou.lib.opengl cimport \
+         (glMatrixMode, glEnable, glDisable, glViewport, glScissor,
+          glLoadMatrixf, glGenBuffers, glDeleteBuffers, GL_MODELVIEW,
+          GL_FOG, GL_SCISSOR_TEST)
+
+from pytouhou.utils.helpers import get_logger
+from pytouhou.utils.maths cimport perspective, setup_camera, ortho_2d
+from pytouhou.utils.matrix cimport matrix_to_floats
+
+from .gamerenderer import GameRenderer
+from .background import BackgroundRenderer
+from .music import MusicPlayer, SFXPlayer, NullPlayer
+from .shaders.eosd import GameShader, BackgroundShader
+
+
+logger = get_logger(__name__)
+
+
+class GameRunner(GameRenderer):
+    def __init__(self, window, resource_loader, replay=None, skip=False):
+        self.use_fixed_pipeline = window.use_fixed_pipeline #XXX
+
+        GameRenderer.__init__(self, resource_loader)
+
+        self.window = window
+        self.replay_level = None
+        self.skip = skip
+        self.keystate = 0
+
+        self.width = window.width #XXX
+        self.height = window.height #XXX
+
+        if not self.use_fixed_pipeline:
+            self.game_shader = GameShader()
+            self.background_shader = BackgroundShader()
+            self.interface_shader = self.game_shader
+
+
+    def load_game(self, game=None, background=None, bgms=None, replay=None, save_keystates=None):
+        self.game = game
+        self.background = background
+
+        self.texture_manager.preload(game.resource_loader.instanced_anms.values())
+
+        if background:
+            self.background_renderer = BackgroundRenderer(self.texture_manager, self.use_fixed_pipeline)
+            self.background_renderer.prerender(background)
+
+        self.set_input(replay)
+        if replay and replay.levels[game.stage - 1]:
+            game.players[0].state.lives = self.replay_level.lives
+            game.players[0].state.power = self.replay_level.power
+            game.players[0].state.bombs = self.replay_level.bombs
+            game.difficulty = self.replay_level.difficulty
+
+        self.save_keystates = save_keystates
+
+        game.music = MusicPlayer(game.resource_loader, bgms)
+        game.music.play(0)
+        game.sfx_player = SFXPlayer(game.resource_loader) if not self.skip else NullPlayer()
+
+
+    def set_input(self, replay=None):
+        if not replay or not replay.levels[self.game.stage-1]:
+            self.replay_level = None
+        else:
+            self.replay_level = replay.levels[self.game.stage-1]
+            self.keys = self.replay_level.iter_keystates()
+
+
+    def start(self):
+        width = self.game.interface.width if self.game else 640
+        height = self.game.interface.height if self.game else 480
+        if (width, height) != (self.width, self.height):
+            self.window.set_size(width, height)
+
+        self.proj = perspective(30, float(self.game.width) / float(self.game.height),
+                                101010101./2010101., 101010101./10101.)
+        game_view = setup_camera(0, 0, 1)
+        self.game_mvp = game_view * self.proj
+        self.interface_mvp = ortho_2d(0., float(self.width), float(self.height), 0.)
+
+
+    def finish(self):
+        #TODO: actually clean after buffers are not needed anymore.
+        #if not self.use_fixed_pipeline:
+        #    vbo_array = (c_uint * 2)(self.vbo, self.back_vbo)
+        #    glDeleteBuffers(2, vbo_array)
+        pass
+
+
+    def update(self):
+        if self.background:
+            self.background.update(self.game.frame)
+        for event in sdl.poll_events():
+            type_ = event[0]
+            if type_ == sdl.KEYDOWN:
+                scancode = event[1]
+                if scancode == sdl.SCANCODE_ESCAPE:
+                    return False #TODO: implement the pause.
+            elif type_ == sdl.QUIT:
+                return False
+        if self.game:
+            if not self.replay_level:
+                #TODO: allow user settings
+                keys = sdl.get_keyboard_state()
+                keystate = 0
+                if keys[sdl.SCANCODE_Z]:
+                    keystate |= 1
+                if keys[sdl.SCANCODE_X]:
+                    keystate |= 2
+                if keys[sdl.SCANCODE_LSHIFT]:
+                    keystate |= 4
+                if keys[sdl.SCANCODE_UP]:
+                    keystate |= 16
+                if keys[sdl.SCANCODE_DOWN]:
+                    keystate |= 32
+                if keys[sdl.SCANCODE_LEFT]:
+                    keystate |= 64
+                if keys[sdl.SCANCODE_RIGHT]:
+                    keystate |= 128
+                if keys[sdl.SCANCODE_LCTRL]:
+                    keystate |= 256
+            else:
+                try:
+                    keystate = self.keys.next()
+                except StopIteration:
+                    keystate = 0
+                    if self.skip:
+                        self.set_input()
+                        self.skip = False
+                        self.game.sfx_player = SFXPlayer(self.game.resource_loader)
+
+            if self.save_keystates is not None:
+                self.save_keystates.append(keystate)
+
+            self.game.run_iter(keystate)
+        if not self.skip:
+            self.render_game()
+            self.render_interface()
+        return True
+
+
+    def render_game(self):
+        # Switch to game projection
+        #TODO: move that to GameRenderer?
+        x, y = self.game.interface.game_pos
+        glViewport(x, y, self.game.width, self.game.height)
+        glScissor(x, y, self.game.width, self.game.height)
+        glEnable(GL_SCISSOR_TEST)
+
+        GameRenderer.render(self)
+
+        glDisable(GL_SCISSOR_TEST)
+
+
+    def render_interface(self):
+        interface = self.game.interface
+        interface.labels['framerate'].set_text('%.2ffps' % self.window.clock.get_fps())
+
+        if self.use_fixed_pipeline:
+            glMatrixMode(GL_MODELVIEW)
+            glLoadMatrixf(matrix_to_floats(self.interface_mvp))
+            glDisable(GL_FOG)
+        else:
+            self.interface_shader.bind()
+            #self.interface_shader.uniform_matrixf('mvp', matrix_to_floats(self.interface_mvp))
+            self.interface_shader.uniform_matrixf('mvp', self.interface_mvp.get_c_data())
+        glViewport(0, 0, self.width, self.height)
+
+        items = [item for item in interface.items if item.anmrunner and item.anmrunner.running]
+        labels = interface.labels.values()
+
+        if items:
+            # Redraw all the interface
+            self.render_elements(items)
+        else:
+            # Redraw only changed labels
+            labels = [label for label in labels if label.changed]
+
+        self.render_elements(interface.level_start)
+
+        if self.game.boss:
+            self.render_elements(interface.boss_items)
+
+        self.render_elements(labels)
+        for label in labels:
+            label.changed = False
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/gamerunner.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/renderer.pxd	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/ui/renderer.pxd	Tue Jul 16 21:07:15 2013 +0200
@@ -4,19 +4,9 @@
     unsigned char r, g, b, a
 
 
-cdef struct VertexFloat:
-    float x, y, z
-    float u, v
-    unsigned char r, g, b, a
-
-
 cdef class Renderer:
     cdef public texture_manager
+    cdef unsigned int vbo
     cdef Vertex *vertex_buffer
-    cdef object texture_key
-    cdef unsigned short blendfunc, nb_vertices
-    cdef VertexFloat *background_vertex_buffer
 
     cpdef render_elements(self, elements)
-    cpdef render_background(self)
-    cpdef prerender_background(self, background)
--- a/pytouhou/ui/renderer.pyx	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/ui/renderer.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -12,21 +12,19 @@
 ## GNU General Public License for more details.
 ##
 
-from libc.stdlib cimport malloc, free, realloc
+from libc.stdlib cimport malloc, free
 from itertools import chain
 
-import ctypes
-
 from struct import pack
 
-from pyglet.gl import (glVertexPointer, glTexCoordPointer, glColorPointer,
-                       glVertexAttribPointer, glEnableVertexAttribArray,
-                       glBlendFunc, glBindTexture, glDrawElements,
-                       glBindBuffer, glBufferData, GL_ARRAY_BUFFER,
-                       GL_DYNAMIC_DRAW, GL_STATIC_DRAW, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT,
-                       GL_INT, GL_FLOAT, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
-                       GL_ONE, GL_TEXTURE_2D, GL_TRIANGLES,
-                       glEnable, glDisable, GL_DEPTH_TEST, glDrawArrays, GL_QUADS)
+from pytouhou.lib.opengl cimport \
+         (glVertexPointer, glTexCoordPointer, glColorPointer,
+          glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc,
+          glBindTexture, glDrawElements, glBindBuffer, glBufferData,
+          GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, GL_UNSIGNED_BYTE,
+          GL_UNSIGNED_SHORT, GL_INT, GL_FLOAT, GL_SRC_ALPHA,
+          GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, GL_TRIANGLES,
+          glGenBuffers)
 
 from .sprite cimport get_sprite_rendering_data
 from .texture cimport TextureManager
@@ -39,20 +37,21 @@
     def __cinit__(self):
         # Allocate buffers
         self.vertex_buffer = <Vertex*> malloc(MAX_ELEMENTS * sizeof(Vertex))
-        self.background_vertex_buffer = <VertexFloat*> malloc(65536 * sizeof(Vertex))
 
 
     def __dealloc__(self):
         free(self.vertex_buffer)
-        free(self.background_vertex_buffer)
 
 
     def __init__(self, resource_loader):
         self.texture_manager = TextureManager(resource_loader)
 
+        if not self.use_fixed_pipeline:
+            glGenBuffers(1, &self.vbo)
+
 
     cpdef render_elements(self, elements):
-        cdef unsigned short nb_vertices = 0
+        cdef unsigned short nb_vertices = 0, nb_indices, *new_indices
 
         indices_by_texture = {}
 
@@ -86,85 +85,33 @@
             return
 
         if self.use_fixed_pipeline:
-            glVertexPointer(3, GL_INT, sizeof(Vertex), <long> &self.vertex_buffer[0].x)
-            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), <long> &self.vertex_buffer[0].u)
-            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), <long> &self.vertex_buffer[0].r)
+            glVertexPointer(3, GL_INT, sizeof(Vertex), &self.vertex_buffer[0].x)
+            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u)
+            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r)
         else:
             glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
-            glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), <long> &self.vertex_buffer[0], GL_DYNAMIC_DRAW)
+            glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_DYNAMIC_DRAW)
 
             #TODO: find a way to use offsetof() instead of those ugly hardcoded values.
-            glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), 0)
+            glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0)
             glEnableVertexAttribArray(0)
-            glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), 12)
+            glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12)
             glEnableVertexAttribArray(1)
-            glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), 20)
+            glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20)
             glEnableVertexAttribArray(2)
 
         for (texture_key, blendfunc), indices in indices_by_texture.items():
+
+            #TODO: find a more elegent way.
             nb_indices = len(indices)
-            indices = pack(str(nb_indices) + 'H', *indices)
+            new_indices = <unsigned short*> malloc(nb_indices * sizeof(unsigned short))
+            for i in xrange(nb_indices):
+                new_indices[i] = indices[i]
+
             glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc])
             glBindTexture(GL_TEXTURE_2D, self.texture_manager[texture_key])
-            glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, indices)
+            glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, new_indices)
+            free(new_indices)
 
         if not self.use_fixed_pipeline:
             glBindBuffer(GL_ARRAY_BUFFER, 0)
-
-
-    cpdef render_background(self):
-        if self.use_fixed_pipeline:
-            glVertexPointer(3, GL_FLOAT, sizeof(VertexFloat), <long> &self.background_vertex_buffer[0].x)
-            glTexCoordPointer(2, GL_FLOAT, sizeof(VertexFloat), <long> &self.background_vertex_buffer[0].u)
-            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexFloat), <long> &self.background_vertex_buffer[0].r)
-        else:
-            glBindBuffer(GL_ARRAY_BUFFER, self.back_vbo)
-
-            #TODO: find a way to use offsetof() instead of those ugly hardcoded values.
-            glVertexAttribPointer(0, 3, GL_FLOAT, False, sizeof(VertexFloat), 0)
-            glEnableVertexAttribArray(0)
-            glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(VertexFloat), 12)
-            glEnableVertexAttribArray(1)
-            glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(VertexFloat), 20)
-            glEnableVertexAttribArray(2)
-
-        glEnable(GL_DEPTH_TEST)
-        glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[self.blendfunc])
-        glBindTexture(GL_TEXTURE_2D, self.texture_manager[self.texture_key])
-        glDrawArrays(GL_QUADS, 0, self.nb_vertices)
-        glDisable(GL_DEPTH_TEST)
-
-        if not self.use_fixed_pipeline:
-            glBindBuffer(GL_ARRAY_BUFFER, 0)
-
-
-    cpdef prerender_background(self, background):
-        cdef float ox, oy, oz, ox2, oy2, oz2
-        cdef unsigned short nb_vertices = 0
-        cdef VertexFloat* vertex_buffer
-
-        vertex_buffer = self.background_vertex_buffer
-
-        for ox, oy, oz, model_id, model in background.object_instances:
-            for ox2, oy2, oz2, width_override, height_override, sprite in model:
-                #TODO: view frustum culling
-                key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite)
-                (x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4) = vertices
-                left, right, bottom, top = uvs
-                r, g, b, a = colors
-
-                vertex_buffer[nb_vertices] = VertexFloat(x1 + ox + ox2, y1 + oy + oy2, z1 + oz + oz2, left, bottom, r, g, b, a)
-                vertex_buffer[nb_vertices+1] = VertexFloat(x2 + ox + ox2, y2 + oy + oy2, z2 + oz + oz2, right, bottom, r, g, b, a)
-                vertex_buffer[nb_vertices+2] = VertexFloat(x3 + ox + ox2, y3 + oy + oy2, z3 + oz + oz2, right, top, r, g, b, a)
-                vertex_buffer[nb_vertices+3] = VertexFloat(x4 + ox + ox2, y4 + oy + oy2, z4 + oz + oz2, left, top, r, g, b, a)
-
-                nb_vertices += 4
-
-        self.texture_key, self.blendfunc = key
-        self.nb_vertices = nb_vertices
-        self.background_vertex_buffer = <VertexFloat*> realloc(vertex_buffer, nb_vertices * sizeof(VertexFloat))
-
-        if not self.use_fixed_pipeline:
-            glBindBuffer(GL_ARRAY_BUFFER, self.back_vbo)
-            glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(VertexFloat), <long> &self.background_vertex_buffer[0], GL_STATIC_DRAW)
-            glBindBuffer(GL_ARRAY_BUFFER, 0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/renderer.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/texture.pyx	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/ui/texture.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -12,17 +12,16 @@
 ## GNU General Public License for more details.
 ##
 
-from pyglet.gl import (glTexParameteri, GL_TEXTURE_MIN_FILTER,
-                       GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_BGRA, GL_RGBA,
-                       GL_RGB, GL_LUMINANCE, GL_UNSIGNED_BYTE,
-                       GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4_REV,
-                       glGenTextures, glBindTexture, glTexImage2D,
-                       GL_TEXTURE_2D)
-from ctypes import c_uint, byref
+from pytouhou.lib.opengl cimport \
+         (glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER,
+          GL_LINEAR, GL_BGRA, GL_RGBA, GL_RGB, GL_LUMINANCE, GL_UNSIGNED_BYTE,
+          GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4_REV,
+          glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, GLuint)
+
 from pytouhou.lib.sdl import load_png, create_rgb_surface
-import os
+from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere?
 
-from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere?
+import os
 
 
 cdef class TextureManager:
@@ -65,6 +64,8 @@
 
 
     def load_texture(self, key):
+        cdef GLuint id_
+
         if not isinstance(key, Texture):
             first_name, secondary_name = key
             key = self.load_png_texture(first_name, secondary_name)
@@ -92,9 +93,8 @@
         else:
             raise Exception('Unknown texture type')
 
-        id_ = c_uint()
-        glGenTextures(1, byref(id_))
-        glBindTexture(GL_TEXTURE_2D, id_.value)
+        glGenTextures(1, &id_)
+        glBindTexture(GL_TEXTURE_2D, id_)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
 
@@ -103,6 +103,6 @@
                      key.width, key.height,
                      0,
                      format_, type_,
-                     key.data)
+                     <char*>key.data)
 
-        return id_.value
+        return id_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/texture.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/window.py	Tue Jul 16 21:07:15 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 3 only.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-
-
-from pytouhou.lib import sdl
-
-from pyglet.gl import (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D,
-                       GL_BLEND, GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT,
-                       GL_NICEST, GL_COLOR_ARRAY, GL_VERTEX_ARRAY,
-                       GL_TEXTURE_COORD_ARRAY)
-
-
-class Clock(object):
-    def __init__(self, fps=None):
-        self._target_fps = 0
-        self._ref_tick = 0
-        self._ref_frame = 0
-        self._fps_tick = 0
-        self._fps_frame = 0
-        self._rate = 0
-        self.set_target_fps(fps)
-
-
-    def set_target_fps(self, fps):
-        self._target_fps = fps
-        self._ref_tick = 0
-        self._fps_tick = 0
-
-
-    def get_fps(self):
-        return self._rate
-
-
-    def tick(self):
-        current = sdl.get_ticks()
-
-        if not self._ref_tick:
-            self._ref_tick = current
-            self._ref_frame = 0
-
-        if self._fps_frame >= (self._target_fps or 60):
-            self._rate = self._fps_frame * 1000. / (current - self._fps_tick)
-            self._fps_tick = current
-            self._fps_frame = 0
-
-        self._ref_frame += 1
-        self._fps_frame += 1
-
-        target_tick = self._ref_tick
-        if self._target_fps:
-            target_tick += int(self._ref_frame * 1000 / self._target_fps)
-
-        if current <= target_tick:
-            sdl.delay(target_tick - current)
-        else:
-            self._ref_tick = current
-            self._ref_frame = 0
-
-
-
-class Window(object):
-    def __init__(self, size=None, double_buffer=True, fps_limit=60,
-                 fixed_pipeline=False, sound=True):
-        self.fps_limit = fps_limit
-        self.use_fixed_pipeline = fixed_pipeline
-        self.runner = None
-
-        sdl.init(sdl.INIT_VIDEO)
-        sdl.img_init(sdl.INIT_PNG)
-        if sound:
-            sdl.mix_init(0)
-
-        sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2)
-        sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, 1)
-        sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer))
-        sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24)
-
-        self.width, self.height = size if size else (640, 480)
-
-        self.win = sdl.Window('PyTouhou',
-                              sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
-                              self.width, self.height,
-                              sdl.WINDOW_OPENGL | sdl.WINDOW_SHOWN)
-        self.win.gl_create_context()
-
-        # Initialize OpenGL
-        glEnable(GL_BLEND)
-        if self.use_fixed_pipeline:
-            glEnable(GL_TEXTURE_2D)
-            glHint(GL_FOG_HINT, GL_NICEST)
-            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
-            glEnableClientState(GL_COLOR_ARRAY)
-            glEnableClientState(GL_VERTEX_ARRAY)
-            glEnableClientState(GL_TEXTURE_COORD_ARRAY)
-
-        # Initialize sound
-        if sound:
-            sdl.mix_open_audio(44100, sdl.DEFAULT_FORMAT, 2, 4096)
-            sdl.mix_allocate_channels(26) #TODO: make it dependent on the SFX number.
-
-        self.clock = Clock(self.fps_limit)
-
-
-    def set_size(self, width, height):
-        self.win.set_window_size(width, height)
-
-
-    def set_runner(self, runner):
-        self.runner = runner
-        runner.start()
-
-
-    def run(self):
-        try:
-            while self.run_frame():
-                pass
-        finally:
-            self.runner.finish()
-
-
-    def run_frame(self):
-        if self.runner:
-            running = self.runner.update()
-        self.win.gl_swap_window()
-        self.clock.tick()
-        return running
-
-
-    def __dealloc__(self):
-        self.win.gl_delete_context()
-        self.win.destroy_window()
-        sdl.mix_close_audio()
-        sdl.mix_quit()
-        sdl.img_quit()
-        sdl.quit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/window.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,146 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+
+from pytouhou.lib import sdl
+
+from pytouhou.lib.opengl cimport \
+         (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D, GL_BLEND,
+          GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST,
+          GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY)
+
+
+class Clock(object):
+    def __init__(self, fps=None):
+        self._target_fps = 0
+        self._ref_tick = 0
+        self._ref_frame = 0
+        self._fps_tick = 0
+        self._fps_frame = 0
+        self._rate = 0
+        self.set_target_fps(fps)
+
+
+    def set_target_fps(self, fps):
+        self._target_fps = fps
+        self._ref_tick = 0
+        self._fps_tick = 0
+
+
+    def get_fps(self):
+        return self._rate
+
+
+    def tick(self):
+        current = sdl.get_ticks()
+
+        if not self._ref_tick:
+            self._ref_tick = current
+            self._ref_frame = 0
+
+        if self._fps_frame >= (self._target_fps or 60):
+            self._rate = self._fps_frame * 1000. / (current - self._fps_tick)
+            self._fps_tick = current
+            self._fps_frame = 0
+
+        self._ref_frame += 1
+        self._fps_frame += 1
+
+        target_tick = self._ref_tick
+        if self._target_fps:
+            target_tick += int(self._ref_frame * 1000 / self._target_fps)
+
+        if current <= target_tick:
+            sdl.delay(target_tick - current)
+        else:
+            self._ref_tick = current
+            self._ref_frame = 0
+
+
+
+class Window(object):
+    def __init__(self, size=None, double_buffer=True, fps_limit=60,
+                 fixed_pipeline=False, sound=True):
+        self.fps_limit = fps_limit
+        self.use_fixed_pipeline = fixed_pipeline
+        self.runner = None
+
+        sdl.init(sdl.INIT_VIDEO)
+        sdl.img_init(sdl.INIT_PNG)
+        if sound:
+            sdl.mix_init(0)
+
+        sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2)
+        sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, 1)
+        sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer))
+        sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24)
+
+        self.width, self.height = size if size else (640, 480)
+
+        self.win = sdl.Window('PyTouhou',
+                              sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
+                              self.width, self.height,
+                              sdl.WINDOW_OPENGL | sdl.WINDOW_SHOWN)
+        self.win.gl_create_context()
+
+        # Initialize OpenGL
+        glEnable(GL_BLEND)
+        if self.use_fixed_pipeline:
+            glEnable(GL_TEXTURE_2D)
+            glHint(GL_FOG_HINT, GL_NICEST)
+            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
+            glEnableClientState(GL_COLOR_ARRAY)
+            glEnableClientState(GL_VERTEX_ARRAY)
+            glEnableClientState(GL_TEXTURE_COORD_ARRAY)
+
+        # Initialize sound
+        if sound:
+            sdl.mix_open_audio(44100, sdl.DEFAULT_FORMAT, 2, 4096)
+            sdl.mix_allocate_channels(26) #TODO: make it dependent on the SFX number.
+
+        self.clock = Clock(self.fps_limit)
+
+
+    def set_size(self, width, height):
+        self.win.set_window_size(width, height)
+
+
+    def set_runner(self, runner):
+        self.runner = runner
+        runner.start()
+
+
+    def run(self):
+        try:
+            while self.run_frame():
+                pass
+        finally:
+            self.runner.finish()
+
+
+    def run_frame(self):
+        if self.runner:
+            running = self.runner.update()
+        self.win.gl_swap_window()
+        self.clock.tick()
+        return running
+
+
+    def __dealloc__(self):
+        self.win.gl_delete_context()
+        self.win.destroy_window()
+        sdl.mix_close_audio()
+        sdl.mix_quit()
+        sdl.img_quit()
+        sdl.quit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/window.pyxbld	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/utils/matrix.pxd	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/utils/matrix.pxd	Tue Jul 16 21:07:15 2013 +0200
@@ -1,5 +1,8 @@
+cdef float* matrix_to_floats(Matrix self)
+
 cdef class Matrix:
     cdef public list data
+    cdef float *c_data
 
     cpdef flip(self)
     cpdef scale(self, x, y, z)
--- a/pytouhou/utils/matrix.pyx	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/utils/matrix.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -14,9 +14,21 @@
 
 from libc.math cimport sin, cos
 from ctypes import c_float
+from libc.stdlib cimport malloc, free
+
+
+cdef float* matrix_to_floats(Matrix self):
+    for i in xrange(4):
+        for j in xrange(4):
+            self.c_data[i*4+j] = self.data[i][j]
+    return self.c_data
 
 
 cdef class Matrix:
+    def __cinit__(self):
+        self.c_data = <float*>malloc(16 * sizeof(float))
+
+
     def __init__(self, data=None):
         self.data = data or [[1, 0, 0, 0],
                              [0, 1, 0, 0],
@@ -24,6 +36,10 @@
                              [0, 0, 0, 1]]
 
 
+    def __dealloc__(self):
+        free(self.c_data)
+
+
     def __mul__(self, Matrix other):
         out = Matrix()
         d1 = self.data
--- a/setup.py	Tue Jul 16 21:07:15 2013 +0200
+++ b/setup.py	Tue Jul 16 21:07:15 2013 +0200
@@ -54,6 +54,9 @@
             if extension_name == 'pytouhou.lib.sdl':
                 compile_args = check_output([COMMAND, '--cflags'] + LIBRARIES).split()
                 link_args = check_output([COMMAND, '--libs'] + LIBRARIES).split()
+            elif extension_name.startswith('pytouhou.ui.'): #XXX
+                compile_args = check_output([COMMAND, '--cflags', 'gl']).split()
+                link_args = check_output([COMMAND, '--libs', 'gl']).split()
             else:
                 compile_args = None
                 link_args = None