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 7 files changed, 184 insertions(+), 189 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/lib/opengl.pxd	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/lib/opengl.pxd	Tue Jul 16 21:07:15 2013 +0200
@@ -70,6 +70,12 @@
         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 @@
 
     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	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/ui/gamerenderer.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -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 @@
                 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 @@
             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 @@
                 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 @@
                 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	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/ui/gamerunner.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -180,8 +180,7 @@
             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]
--- a/pytouhou/ui/shader.py	Tue Jul 16 21:07:15 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-#
-# Copyright Tristam Macdonald 2008.
-# Copyright Emmanuel Gil Peyrot 2012.
-#
-# Distributed under the Boost Software License, Version 1.0
-# (see http://www.boost.org/LICENSE_1_0.txt)
-#
-# 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 ctypes import (c_char, c_char_p, c_int, POINTER, byref, cast,
-                    create_string_buffer)
-
-
-class GLSLException(Exception):
-    pass
-
-
-class Shader(object):
-    # 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):
-        # create the program handle
-        self.handle = glCreateProgram()
-        # we are not linked yet
-        self.linked = False
-
-        # cache the uniforms location
-        self.location_cache = {}
-
-        # create the vertex shader
-        self.createShader(vert, GL_VERTEX_SHADER)
-        # create the fragment shader
-        self.createShader(frag, GL_FRAGMENT_SHADER)
-
-        #TODO: put those elsewhere.
-        glBindAttribLocation(self.handle, 0, 'in_position')
-        glBindAttribLocation(self.handle, 1, 'in_texcoord')
-        glBindAttribLocation(self.handle, 2, 'in_color')
-
-        # 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
-
-        # create the shader handle
-        shader = glCreateShader(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)
-
-        # compile the shader
-        glCompileShader(shader)
-
-        temp = c_int(0)
-        # retrieve the compile status
-        glGetShaderiv(shader, GL_COMPILE_STATUS, byref(temp))
-
-        # if compilation failed, print the log
-        if not temp:
-            # retrieve the log length
-            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, byref(temp))
-            # create a buffer for the log
-            buffer = create_string_buffer(temp.value)
-            # retrieve the log text
-            glGetShaderInfoLog(shader, temp, None, buffer)
-            # print the log to the console
-            raise GLSLException(buffer.value)
-        else:
-            # all is well, so attach the shader to the program
-            glAttachShader(self.handle, shader);
-
-    def link(self):
-        # link the program
-        glLinkProgram(self.handle)
-
-        temp = c_int(0)
-        # retrieve the link status
-        glGetProgramiv(self.handle, GL_LINK_STATUS, byref(temp))
-
-        # if linking failed, print the log
-        if not temp:
-            #   retrieve the log length
-            glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, byref(temp))
-            # create a buffer for the log
-            buffer = create_string_buffer(temp.value)
-            # retrieve the log text
-            glGetProgramInfoLog(self.handle, temp, None, buffer)
-            # print the log to the console
-            raise GLSLException(buffer.value)
-        else:
-            # all is well, so we are linked
-            self.linked = True
-
-    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)
-
-    # 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 uniform matrix
-    # works with matrices stored as lists,
-    # as well as euclid matrices
-    def uniform_matrixf(self, name, mat):
-        # obtian the uniform location
-        loc = self.get_uniform_location(name)
-        # uplaod the 4x4 floating point matrix
-        glUniformMatrix4fv(loc, 1, False, mat)
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/shader.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -0,0 +1,148 @@
+# -*- encoding: utf-8 -*-
+#
+# Copyright Tristam Macdonald 2008.
+# Copyright Emmanuel Gil Peyrot 2012.
+#
+# Distributed under the Boost Software License, Version 1.0
+# (see http://www.boost.org/LICENSE_1_0.txt)
+#
+# Source: https://swiftcoder.wordpress.com/2008/12/19/simple-glsl-wrapper-for-pyglet/
+#
+
+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 libc.stdlib cimport malloc, free
+from pytouhou.utils.matrix cimport Matrix, matrix_to_floats
+
+
+class GLSLException(Exception):
+    pass
+
+
+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):
+        # create the program handle
+        self.handle = glCreateProgram()
+        # we are not linked yet
+        self.linked = False
+
+        # cache the uniforms location
+        self.location_cache = {}
+
+        # create the vertex shader
+        self.create_shader(vert[0], GL_VERTEX_SHADER)
+        # create the fragment shader
+        self.create_shader(frag[0], GL_FRAGMENT_SHADER)
+
+        #TODO: put those elsewhere.
+        glBindAttribLocation(self.handle, 0, 'in_position')
+        glBindAttribLocation(self.handle, 1, 'in_texcoord')
+        glBindAttribLocation(self.handle, 2, 'in_color')
+
+        # attempt to link the program
+        self.link()
+
+    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(shader_type)
+
+        # upload the source strings
+        glShaderSource(shader, 1, strings, NULL)
+
+        # compile the shader
+        glCompileShader(shader)
+
+        # retrieve the compile status
+        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, &temp)
+            # create a buffer for the log
+            temp_buf = <GLchar*>malloc(temp * sizeof(GLchar))
+            # retrieve the log text
+            glGetShaderInfoLog(shader, temp, NULL, temp_buf)
+            buf = temp_buf[:temp]
+            free(temp_buf)
+            # print the log to the console
+            raise GLSLException(buf)
+        else:
+            # all is well, so attach the shader to the program
+            glAttachShader(self.handle, shader)
+
+    cdef void link(self):
+        cdef GLint temp
+
+        # link the program
+        glLinkProgram(self.handle)
+
+        # retrieve the link status
+        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, &temp)
+            # create a buffer for the log
+            temp_buf = <GLchar*>malloc(temp * sizeof(GLchar))
+            # retrieve the log text
+            glGetProgramInfoLog(self.handle, temp, NULL, temp_buf)
+            buf = temp_buf[:temp]
+            free(temp_buf)
+            # print the log to the console
+            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)
+
+    # upload a floating point uniform
+    # this program must be currently bound
+    def uniform_1(self, name, GLfloat val):
+        glUniform1fv(self.get_uniform_location(name), 1, &val)
+
+    # 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_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, matrix_to_floats(mat))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/shader.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.pyx	Tue Jul 16 21:07:15 2013 +0200
+++ b/pytouhou/utils/matrix.pyx	Tue Jul 16 21:07:15 2013 +0200
@@ -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 @@
         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]