forked from blank63/j3dview
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgl.py
261 lines (176 loc) · 6.88 KB
/
gl.py
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
import numpy
from OpenGL.GL import *
class Resource(GLuint):
def __hash__(self):
return object.__hash__(self)
def __int__(self):
return self.value
def __index__(self):
return self.value
class Buffer(Resource):
def __init__(self):
glGenBuffers(1,self)
def __del__(self):
glDeleteBuffers(1,self)
class VertexArray(Resource):
def __init__(self):
glGenVertexArrays(1,self)
def __del__(self):
glDeleteVertexArrays(1,self)
class Shader(Resource):
def __init__(self,shader_type,source):
super().__init__(glCreateShader(shader_type))
self.source = source
glShaderSource(self,source)
glCompileShader(self)
if not glGetShaderiv(self,GL_COMPILE_STATUS):
raise RuntimeError('Compile failure: {}{}'.format(glGetShaderInfoLog(self).decode(),source))
def __del__(self):
glDeleteShader(self)
class Program(Resource):
def __init__(self,*shaders):
super().__init__(glCreateProgram())
for shader in shaders:
glAttachShader(self,shader)
glLinkProgram(self)
if not glGetProgramiv(self,GL_LINK_STATUS):
for i in range(len(shaders)):
shader = shaders[i]
with open("shader-{0}.txt".format(i), "w") as f:
f.write(shader.source)
raise RuntimeError('Link failure: {}'.format(glGetProgramInfoLog(self).decode()))
for shader in shaders:
glDetachShader(self,shader)
def __del__(self):
glDeleteProgram(self)
class Texture(Resource):
def __init__(self):
super().__init__(glGenTextures(1))
def __del__(self):
glDeleteTextures(self.value)
class Sampler(Resource):
def __init__(self):
glGenSamplers(1,self)
def __del__(self):
glDeleteSamplers(1,self)
class Renderbuffer(Resource):
def __init__(self):
glGenRenderbuffers(1,self)
def __del__(self):
glDeleteRenderbuffers(1,self)
class Framebuffer(Resource):
def __init__(self):
glGenFramebuffers(1,self)
def __del__(self):
glDeleteFramebuffers(1,self)
class ChangeRegisteringArray(numpy.ndarray):
#XXX Should only be changed using __setitem__
def __array_finalize__(self,obj):
if obj is None:
self.changed = True
def __setitem__(self,*args):
if self.base is None:
self.changed = True
else:
self.base.changed = True
super().__setitem__(*args)
class ManagedBuffer:
def __init__(self,target,usage,*args,**kwargs):
super().__init__()
self.target = target
self.usage = usage
self.data = ChangeRegisteringArray(*args,**kwargs)
self.buffer = Buffer()
glBindBuffer(target,self.buffer)
glBufferData(target,self.data.nbytes,None,usage)
def __getitem__(self,key):
return self.data[key]
def __setitem__(self,key,value):
self.data[key] = value
def __iter__(self):
return iter(self.data)
def sync_data(self):
if self.data.changed:
glBindBuffer(self.target,self.buffer)
glBufferSubData(self.target,0,self.data.nbytes,self.data.view(numpy.ndarray))
self.data.changed = False
def bind(self,binding_point=None):
if binding_point is None:
glBindBuffer(self.target,self.buffer)
else:
glBindBufferBase(self.target,binding_point,self.buffer)
if self.data.changed:
glBufferSubData(self.target,0,self.data.nbytes,self.data.view(numpy.ndarray))
self.data.changed = False
class TextureBuffer(ManagedBuffer):
def __init__(self,usage,element_type,*args,**kwargs):
super().__init__(GL_TEXTURE_BUFFER,usage,*args,**kwargs)
self.element_type = element_type
self.texture = Texture()
def bind_texture(self,texture_unit):
self.sync_data()
glActiveTexture(GL_TEXTURE0 + texture_unit)
glBindTexture(GL_TEXTURE_BUFFER,self.texture)
glTexBuffer(GL_TEXTURE_BUFFER,self.element_type,self.buffer)
class Type:
def __init__(self,glsl_type,numpy_type):
self.glsl_type = glsl_type
self.numpy_type = numpy_type
if not numpy_type.shape:
self.base_alignment = 4
elif len(numpy_type.shape) == 1:
self.base_alignment = 16 if numpy_type.shape[0] != 2 else 8
else:
self.base_alignment = 16
if numpy_type.shape[-1] % 4 != 0:
raise Exception('unaligned array elements not implemented')
vec2 = Type('vec2',numpy.dtype((numpy.float32,2)))
vec3 = Type('vec3',numpy.dtype((numpy.float32,3)))
vec4 = Type('vec4',numpy.dtype((numpy.float32,4)))
mat3x2 = Type('mat3x2',numpy.dtype((numpy.float32,(2,4)))) #TODO
mat4x2 = Type('mat4x2',numpy.dtype((numpy.float32,(2,4))))
mat4x3 = Type('mat4x3',numpy.dtype((numpy.float32,(3,4))))
mat4 = Type('mat4',numpy.dtype((numpy.float32,(4,4))))
class UniformBlockClassDictionary(dict):
def __init__(self):
super().__init__()
self.glsl_fields = []
self.numpy_fields = []
self.offset = 0
def add_field(self,name,field_type):
if self.offset % field_type.base_alignment != 0:
raise Exception('unaligned fields not implemented')
self.glsl_fields.append('{} {};'.format(field_type.glsl_type,name))
self.numpy_fields.append((name,field_type.numpy_type))
self.offset += field_type.numpy_type.itemsize
def __setitem__(self,key,value):
if not key[:2] == key[-2:] == '__' and not hasattr(value,'__get__'):
self.add_field(key,value)
else:
super().__setitem__(key,value)
class UniformBlockMetaClass(type):
@classmethod
def __prepare__(metacls,cls,bases):
return UniformBlockClassDictionary()
def __new__(metacls,cls,bases,classdict):
uniform_block_class = type.__new__(metacls,cls,bases,classdict)
uniform_block_class.glsl_type = (
'layout(std140,row_major) uniform {}\n'.format(cls) +
'{\n' +
''.join(' {}\n'.format(field) for field in classdict.glsl_fields) +
'};')
uniform_block_class.numpy_type = numpy.dtype(classdict.numpy_fields)
return uniform_block_class
class UniformBlock(ManagedBuffer,metaclass=UniformBlockMetaClass):
def __init__(self,usage):
super().__init__(GL_UNIFORM_BUFFER,usage,1,self.numpy_type)
def __getitem__(self,key):
return super().__getitem__(key)[0]
def __setitem__(self,key,value):
super().__getitem__(key)[0] = value
def uniform_block(class_name,fields):
bases = (UniformBlock,)
classdict = UniformBlockMetaClass.__prepare__(class_name,bases)
for name,field_type in fields:
classdict[name] = field_type
return UniformBlockMetaClass(class_name,bases,classdict)