File: shader.py

package info (click to toggle)
pycollada 0.8-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,116 kB
  • sloc: xml: 11,085; python: 6,859; makefile: 87
file content (140 lines) | stat: -rw-r--r-- 4,836 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#
# Copyright Tristam Macdonald 2008.
#
# Distributed under the Boost Software License, Version 1.0
# (see http://www.boost.org/LICENSE_1_0.txt)
#

from pyglet.gl import *
import ctypes


def _as_bytes(s):
    if isinstance(s, bytes):
        return s
    return s.encode('utf-8')


class Shader:
    # vert, frag and geom take arrays of source strings
    # the arrays will be concatenated into one string by OpenGL
    def __init__(self, vert=[], frag=[], geom=[]):
        # create the program handle
        self.handle = glCreateProgram()
        # we are not linked yet
        self.linked = False

        # create the vertex shader
        self.createShader(vert, GL_VERTEX_SHADER)
        # create the fragment shader
        self.createShader(frag, GL_FRAGMENT_SHADER)
        # the geometry shader will be the same, once pyglet supports the extension
        # self.createShader(frag, GL_GEOMETRY_SHADER_EXT)

        # attempt to link the program
        self.link()

    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!
        strings = [_as_bytes(s) for s in strings]
        src = (ctypes.c_char_p * count)(*strings)
        glShaderSource(shader, count, ctypes.cast(ctypes.pointer(src),
                       ctypes.POINTER(ctypes.POINTER(ctypes.c_char))), None)

        # compile the shader
        glCompileShader(shader)

        temp = ctypes.c_int(0)
        # retrieve the compile status
        glGetShaderiv(shader, GL_COMPILE_STATUS, ctypes.byref(temp))

        # if compilation failed, print the log
        if not temp:
            # retrieve the log length
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, ctypes.byref(temp))
            # create a buffer for the log
            buffer = ctypes.create_string_buffer(temp.value)
            # retrieve the log text
            glGetShaderInfoLog(shader, temp, None, buffer)
            # print the log to the console
            print(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 = ctypes.c_int(0)
        # retrieve the link status
        glGetProgramiv(self.handle, GL_LINK_STATUS, ctypes.byref(temp))

        # if linking failed, print the log
        if not temp:
            #	retrieve the log length
            glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, ctypes.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
            print(buffer.value)
        else:
            # all is well, so we are linked
            self.linked = True

    def bind(self):
        # bind the program
        glUseProgram(self.handle)

    def unbind(self):
        # unbind whatever program is currently bound - not necessarily this program,
        # so this should probably be a class method instead
        glUseProgram(0)

    # upload a floating point uniform
    # this program must be currently bound
    def uniformf(self, name, *vals):
        name = _as_bytes(name)
        # 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)](glGetUniformLocation(self.handle, name), *vals)

    # upload an integer uniform
    # this program must be currently bound
    def uniformi(self, name, *vals):
        name = _as_bytes(name)
        # 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)](glGetUniformLocation(self.handle, name), *vals)

    # upload a uniform matrix
    # works with matrices stored as lists,
    # as well as euclid matrices
    def uniform_matrixf(self, name, mat):
        # obtain the uniform location
        loc = glGetUniformLocation(self.Handle, name)
        # upload the 4x4 floating point matrix
        glUniformMatrix4fv(loc, 1, False, (c_float * 16)(*mat))