# HG changeset patch # User Emmanuel Gil Peyrot # Date 1374001635 -7200 # Node ID f4d76d3d6f2a50ecddebe3b8133907452ce2b2d3 # Parent d8630c0869268a1ed3722dbb09cd83c509cfc353 Make the Shader class use cython too. diff --git a/pytouhou/lib/opengl.pxd b/pytouhou/lib/opengl.pxd --- 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) diff --git a/pytouhou/ui/gamerenderer.pyx b/pytouhou/ui/gamerenderer.pyx --- 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 = 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) diff --git a/pytouhou/ui/gamerunner.pyx b/pytouhou/ui/gamerunner.pyx --- 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] diff --git a/pytouhou/ui/shader.py b/pytouhou/ui/shader.pyx 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 = 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 = 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)) diff --git a/pytouhou/ui/shader.pyxbld b/pytouhou/ui/shader.pyxbld new file mode 120000 --- /dev/null +++ b/pytouhou/ui/shader.pyxbld @@ -0,0 +1,1 @@ +../lib/opengl.pyxbld \ No newline at end of file diff --git a/pytouhou/utils/matrix.pyx b/pytouhou/utils/matrix.pyx --- 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]