changeset 424:f4d76d3d6f2a

Make the Shader class use cython too.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 16 Jul 2013 21:07:15 +0200
parents d8630c086926
children 1104dc2553ee
files pytouhou/lib/opengl.pxd pytouhou/ui/gamerenderer.pyx pytouhou/ui/gamerunner.pyx pytouhou/ui/shader.py pytouhou/ui/shader.pyx pytouhou/ui/shader.pyxbld pytouhou/utils/matrix.pyx
diffstat 6 files changed, 99 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/lib/opengl.pxd
+++ b/pytouhou/lib/opengl.pxd
@@ -70,6 +70,12 @@ cdef extern from 'GL/gl.h':
         GL_VERTEX_ARRAY
         GL_TEXTURE_COORD_ARRAY
 
+        GL_VERTEX_SHADER
+        GL_FRAGMENT_SHADER
+        GL_INFO_LOG_LENGTH
+        GL_COMPILE_STATUS
+        GL_LINK_STATUS
+
     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)
@@ -106,3 +112,22 @@ cdef extern from 'GL/gl.h':
 
     void glHint(GLenum target, GLenum mode)
     void glEnableClientState(GLenum cap)
+
+    GLuint glCreateProgram()
+    GLuint glCreateShader(GLenum shaderType)
+    void glLinkProgram(GLuint program)
+    void glUseProgram(GLuint program)
+    void glGetProgramiv(GLuint program, GLenum pname, GLint *params)
+    void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei *length, GLchar *infoLog)
+
+    void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length)
+    void glCompileShader(GLuint shader)
+    void glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
+    void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog)
+    void glAttachShader(GLuint program, GLuint shader)
+
+    GLint glGetUniformLocation(GLuint program, const GLchar *name)
+    void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name)
+    void glUniform1fv(GLint location, GLsizei count, const GLfloat *value)
+    void glUniform4fv(GLint location, GLsizei count, const GLfloat *value)
+    void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
--- a/pytouhou/ui/gamerenderer.pyx
+++ b/pytouhou/ui/gamerenderer.pyx
@@ -12,9 +12,7 @@
 ## GNU General Public License for more details.
 ##
 
-
 from libc.stdlib cimport malloc, free
-
 from itertools import chain
 
 from pytouhou.lib.opengl cimport \
@@ -54,7 +52,7 @@ class GameRenderer(Renderer):
                 glDisable(GL_FOG)
             else:
                 self.game_shader.bind()
-                self.game_shader.uniform_matrixf('mvp', self.game_mvp.get_c_data())
+                self.game_shader.uniform_matrix('mvp', self.game_mvp)
 
             self.render_elements([game.spellcard_effect])
         elif back is not None:
@@ -74,18 +72,17 @@ class GameRenderer(Renderer):
             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)
+            mvp = model * view * self.proj
 
             if self.use_fixed_pipeline:
                 glMatrixMode(GL_MODELVIEW)
-                glLoadMatrixf(mvp_cython)
+                glLoadMatrixf(matrix_to_floats(mvp))
 
                 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.
@@ -95,11 +92,11 @@ class GameRenderer(Renderer):
                 free(fog_data)
             else:
                 self.background_shader.bind()
-                self.background_shader.uniform_matrixf('mvp', mvp)
+                self.background_shader.uniform_matrix('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_shader.uniform_1('fog_scale', 1. / (fog_end - fog_start))
+                self.background_shader.uniform_1('fog_end', fog_end)
+                self.background_shader.uniform_4('fog_color', fog_r / 255., fog_g / 255., fog_b / 255., 1.)
 
             self.background_renderer.render_background()
         else:
@@ -112,7 +109,7 @@ class GameRenderer(Renderer):
                 glDisable(GL_FOG)
             else:
                 self.game_shader.bind()
-                self.game_shader.uniform_matrixf('mvp', self.game_mvp.get_c_data())
+                self.game_shader.uniform_matrix('mvp', self.game_mvp)
 
             self.render_elements(enemy for enemy in game.enemies if enemy.visible)
             self.render_elements(game.effects)
--- a/pytouhou/ui/gamerunner.pyx
+++ b/pytouhou/ui/gamerunner.pyx
@@ -180,8 +180,7 @@ class GameRunner(GameRenderer):
             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())
+            self.interface_shader.uniform_matrix('mvp', self.interface_mvp)
         glViewport(0, 0, self.width, self.height)
 
         items = [item for item in interface.items if item.anmrunner and item.anmrunner.running]
rename from pytouhou/ui/shader.py
rename to pytouhou/ui/shader.pyx
--- a/pytouhou/ui/shader.py
+++ b/pytouhou/ui/shader.pyx
@@ -1,3 +1,4 @@
+# -*- encoding: utf-8 -*-
 #
 # Copyright Tristam Macdonald 2008.
 # Copyright Emmanuel Gil Peyrot 2012.
@@ -8,25 +9,28 @@
 # Source: https://swiftcoder.wordpress.com/2008/12/19/simple-glsl-wrapper-for-pyglet/
 #
 
-from pyglet.gl import (glCreateProgram, glCreateShader, GL_VERTEX_SHADER,
-                       GL_FRAGMENT_SHADER, glShaderSource, glCompileShader,
-                       glGetShaderiv, GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH,
-                       glGetShaderInfoLog, glAttachShader, glLinkProgram,
-                       glGetProgramiv, glGetProgramInfoLog, GL_LINK_STATUS,
-                       glUseProgram, glGetUniformLocation, glUniform1f,
-                       glUniform2f, glUniform3f, glUniform4f, glUniform1i,
-                       glUniform2i, glUniform3i, glUniform4i,
-                       glUniformMatrix4fv, glBindAttribLocation)
+from pytouhou.lib.opengl cimport \
+         (glCreateProgram, glCreateShader, GL_VERTEX_SHADER,
+          GL_FRAGMENT_SHADER, glShaderSource, glCompileShader, glGetShaderiv,
+          GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, glGetShaderInfoLog,
+          glAttachShader, glLinkProgram, glGetProgramiv, glGetProgramInfoLog,
+          GL_LINK_STATUS, glUseProgram, glGetUniformLocation, glUniform1fv,
+          glUniform4fv, glUniformMatrix4fv, glBindAttribLocation, GLint,
+          GLuint, GLchar, GLfloat, GLenum)
 
-from ctypes import (c_char, c_char_p, c_int, POINTER, byref, cast,
-                    create_string_buffer)
+from libc.stdlib cimport malloc, free
+from pytouhou.utils.matrix cimport Matrix, matrix_to_floats
 
 
 class GLSLException(Exception):
     pass
 
 
-class Shader(object):
+cdef class Shader:
+    cdef GLuint handle
+    cdef bint linked
+    cdef dict location_cache
+
     # vert and frag take arrays of source strings the arrays will be
     # concattenated into one string by OpenGL
     def __init__(self, vert=None, frag=None):
@@ -39,9 +43,9 @@ class Shader(object):
         self.location_cache = {}
 
         # create the vertex shader
-        self.createShader(vert, GL_VERTEX_SHADER)
+        self.create_shader(vert[0], GL_VERTEX_SHADER)
         # create the fragment shader
-        self.createShader(frag, GL_FRAGMENT_SHADER)
+        self.create_shader(frag[0], GL_FRAGMENT_SHADER)
 
         #TODO: put those elsewhere.
         glBindAttribLocation(self.handle, 0, 'in_position')
@@ -51,119 +55,94 @@ class Shader(object):
         # attempt to link the program
         self.link()
 
-    def load_source(self, path):
-        with open(path, 'rb') as file:
-            source = file.read()
-        return source
-
-    def createShader(self, strings, type):
-        count = len(strings)
-        # if we have no source code, ignore this shader
-        if count < 1:
-            return
+    cdef void create_shader(self, const GLchar *string, GLenum shader_type):
+        cdef GLint temp
+        cdef const GLchar **strings = &string
 
         # create the shader handle
-        shader = glCreateShader(type)
+        shader = glCreateShader(shader_type)
 
-        # convert the source strings into a ctypes pointer-to-char array, and upload them
-        # this is deep, dark, dangerous black magick - don't try stuff like this at home!
-        src = (c_char_p * count)(*strings)
-        glShaderSource(shader, count, cast(byref(src), POINTER(POINTER(c_char))), None)
+        # upload the source strings
+        glShaderSource(shader, 1, strings, NULL)
 
         # compile the shader
         glCompileShader(shader)
 
-        temp = c_int(0)
         # retrieve the compile status
-        glGetShaderiv(shader, GL_COMPILE_STATUS, byref(temp))
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &temp)
 
         # if compilation failed, print the log
         if not temp:
             # retrieve the log length
-            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, byref(temp))
+            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &temp)
             # create a buffer for the log
-            buffer = create_string_buffer(temp.value)
+            temp_buf = <GLchar*>malloc(temp * sizeof(GLchar))
             # retrieve the log text
-            glGetShaderInfoLog(shader, temp, None, buffer)
+            glGetShaderInfoLog(shader, temp, NULL, temp_buf)
+            buf = temp_buf[:temp]
+            free(temp_buf)
             # print the log to the console
-            raise GLSLException(buffer.value)
+            raise GLSLException(buf)
         else:
             # all is well, so attach the shader to the program
-            glAttachShader(self.handle, shader);
+            glAttachShader(self.handle, shader)
 
-    def link(self):
+    cdef void link(self):
+        cdef GLint temp
+
         # link the program
         glLinkProgram(self.handle)
 
-        temp = c_int(0)
         # retrieve the link status
-        glGetProgramiv(self.handle, GL_LINK_STATUS, byref(temp))
+        glGetProgramiv(self.handle, GL_LINK_STATUS, &temp)
 
         # if linking failed, print the log
         if not temp:
             #   retrieve the log length
-            glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, byref(temp))
+            glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, &temp)
             # create a buffer for the log
-            buffer = create_string_buffer(temp.value)
+            temp_buf = <GLchar*>malloc(temp * sizeof(GLchar))
             # retrieve the log text
-            glGetProgramInfoLog(self.handle, temp, None, buffer)
+            glGetProgramInfoLog(self.handle, temp, NULL, temp_buf)
+            buf = temp_buf[:temp]
+            free(temp_buf)
             # print the log to the console
-            raise GLSLException(buffer.value)
+            raise GLSLException(buf)
         else:
             # all is well, so we are linked
             self.linked = True
 
+    cdef GLint get_uniform_location(self, name):
+        if name not in self.location_cache:
+            loc = glGetUniformLocation(self.handle, name)
+            if loc == -1:
+                raise GLSLException('Undefined {} uniform.'.format(name))
+            self.location_cache[name] = loc
+        return self.location_cache[name]
+
     def bind(self):
         # bind the program
         glUseProgram(self.handle)
 
-    @classmethod
-    def unbind(self):
-        # unbind whatever program is currently bound
-        glUseProgram(0)
-
-    def get_uniform_location(self, name):
-        try:
-            return self.location_cache[name]
-        except KeyError:
-            loc = glGetUniformLocation(self.handle, name)
-            if loc == -1:
-                raise GLSLException #TODO
-            self.location_cache[name] = loc
-            return loc
-
     # upload a floating point uniform
     # this program must be currently bound
-    def uniformf(self, name, *vals):
-        # check there are 1-4 values
-        if len(vals) in range(1, 5):
-            # select the correct function
-            { 1 : glUniform1f,
-                2 : glUniform2f,
-                3 : glUniform3f,
-                4 : glUniform4f
-                # retrieve the uniform location, and set
-            }[len(vals)](self.get_uniform_location(name), *vals)
+    def uniform_1(self, name, GLfloat val):
+        glUniform1fv(self.get_uniform_location(name), 1, &val)
 
-    # upload an integer uniform
-    # this program must be currently bound
-    def uniformi(self, name, *vals):
-        # check there are 1-4 values
-        if len(vals) in range(1, 5):
-            # select the correct function
-            { 1 : glUniform1i,
-                2 : glUniform2i,
-                3 : glUniform3i,
-                4 : glUniform4i
-                # retrieve the uniform location, and set
-            }[len(vals)](self.get_uniform_location(name), *vals)
+    # upload a vec4 uniform
+    def uniform_4(self, name, GLfloat a, GLfloat b, GLfloat c, GLfloat d):
+        cdef GLfloat vals[4]
+        vals[0] = a
+        vals[1] = b
+        vals[2] = c
+        vals[3] = d
+        glUniform4fv(self.get_uniform_location(name), 1, vals)
 
     # upload a uniform matrix
     # works with matrices stored as lists,
     # as well as euclid matrices
-    def uniform_matrixf(self, name, mat):
-        # obtian the uniform location
+    def uniform_matrix(self, name, Matrix mat):
+        # obtain the uniform location
         loc = self.get_uniform_location(name)
         # uplaod the 4x4 floating point matrix
-        glUniformMatrix4fv(loc, 1, False, mat)
-
+        glUniformMatrix4fv(loc, 1, False, matrix_to_floats(mat))
new file mode 120000
--- /dev/null
+++ b/pytouhou/ui/shader.pyxbld
@@ -0,0 +1,1 @@
+../lib/opengl.pyxbld
\ No newline at end of file
--- a/pytouhou/utils/matrix.pyx
+++ b/pytouhou/utils/matrix.pyx
@@ -13,7 +13,6 @@
 ##
 
 from libc.math cimport sin, cos
-from ctypes import c_float
 from libc.stdlib cimport malloc, free
 
 
@@ -51,11 +50,6 @@ cdef class Matrix:
         return out
 
 
-    def get_c_data(self):
-        data = sum(self.data, [])
-        return (c_float * 16)(*data)
-
-
     cpdef flip(self):
         data = self.data
         a, b, c, d = data[0]