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 19 files changed, 365 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/pytouhou/lib/opengl.pxd
@@ -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)
new file mode 100644
--- /dev/null
+++ b/pytouhou/lib/opengl.pyxbld
@@ -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)
new file mode 100644
--- /dev/null
+++ b/pytouhou/ui/background.pxd
@@ -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)
new file mode 100644
--- /dev/null
+++ b/pytouhou/ui/background.pyx
@@ -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)
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/background.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
rename from pytouhou/ui/gamerenderer.py
rename to pytouhou/ui/gamerenderer.pyx
--- a/pytouhou/ui/gamerenderer.py
+++ b/pytouhou/ui/gamerenderer.pyx
@@ -13,16 +13,18 @@
 ##
 
 
+from libc.stdlib cimport malloc, free
+
 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.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 import Matrix
-from pytouhou.utils.maths import setup_camera
+from pytouhou.utils.matrix cimport Matrix, matrix_to_floats
+from pytouhou.utils.maths cimport setup_camera
 
 from .renderer import Renderer
 
@@ -34,6 +36,8 @@ class GameRenderer(Renderer):
 
 
     def render(self):
+        cdef float* fog_data
+
         glClear(GL_DEPTH_BUFFER_BIT)
 
         back = self.background
@@ -46,7 +50,7 @@ class GameRenderer(Renderer):
         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())
+                glLoadMatrixf(matrix_to_floats(self.game_mvp))
                 glDisable(GL_FOG)
             else:
                 self.game_shader.bind()
@@ -72,16 +76,23 @@ class GameRenderer(Renderer):
             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)
+                glLoadMatrixf(mvp_cython)
 
                 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.))
+                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)
@@ -90,14 +101,14 @@ class GameRenderer(Renderer):
                 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()
+            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(self.game_mvp.get_c_data())
+                glLoadMatrixf(matrix_to_floats(self.game_mvp))
                 glDisable(GL_FOG)
             else:
                 self.game_shader.bind()
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/gamerenderer.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
rename from pytouhou/ui/gamerunner.py
rename to pytouhou/ui/gamerunner.pyx
--- a/pytouhou/ui/gamerunner.py
+++ b/pytouhou/ui/gamerunner.pyx
@@ -14,25 +14,28 @@
 
 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.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 import perspective, setup_camera, ortho_2d
+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
 
-from ctypes import c_uint, byref
-
 
 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
@@ -40,7 +43,6 @@ class GameRunner(GameRenderer):
         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
 
@@ -49,10 +51,6 @@ class GameRunner(GameRenderer):
             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
@@ -61,7 +59,8 @@ class GameRunner(GameRenderer):
         self.texture_manager.preload(game.resource_loader.instanced_anms.values())
 
         if background:
-            self.prerender_background(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]:
@@ -177,10 +176,11 @@ class GameRunner(GameRenderer):
 
         if self.use_fixed_pipeline:
             glMatrixMode(GL_MODELVIEW)
-            glLoadMatrixf(self.interface_mvp.get_c_data())
+            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)
 
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/gamerunner.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/renderer.pxd
+++ b/pytouhou/ui/renderer.pxd
@@ -4,19 +4,9 @@ cdef struct Vertex:
     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
+++ b/pytouhou/ui/renderer.pyx
@@ -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 @@ cdef class Renderer:
     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 @@ cdef class Renderer:
             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)
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/renderer.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/ui/texture.pyx
+++ b/pytouhou/ui/texture.pyx
@@ -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 @@ cdef class TextureManager:
 
 
     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 @@ cdef class TextureManager:
         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 @@ cdef class TextureManager:
                      key.width, key.height,
                      0,
                      format_, type_,
-                     key.data)
+                     <char*>key.data)
 
-        return id_.value
+        return id_
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/texture.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
rename from pytouhou/ui/window.py
rename to pytouhou/ui/window.pyx
--- a/pytouhou/ui/window.py
+++ b/pytouhou/ui/window.pyx
@@ -15,10 +15,10 @@
 
 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)
+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):
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/window.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/utils/matrix.pxd
+++ b/pytouhou/utils/matrix.pxd
@@ -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
+++ b/pytouhou/utils/matrix.pyx
@@ -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 @@ cdef class Matrix:
                              [0, 0, 0, 1]]
 
 
+    def __dealloc__(self):
+        free(self.c_data)
+
+
     def __mul__(self, Matrix other):
         out = Matrix()
         d1 = self.data
--- a/setup.py
+++ b/setup.py
@@ -54,6 +54,9 @@ for directory, _, files in os.walk('pyto
             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