chiark / gitweb /
Release the indices buffer.
[cura.git] / Cura / gui / util / openglHelpers.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import math
4 import numpy
5 import wx
6 import time
7
8 from Cura.util.resources import getPathForImage
9
10 import OpenGL
11
12 OpenGL.ERROR_CHECKING = False
13 from OpenGL.GLUT import *
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
16 from OpenGL.GL import shaders
17 glutInit() #Hack; required before glut can be called. Not required for all OS.
18
19 class GLReferenceCounter(object):
20         def __init__(self):
21                 self._refCounter = 1
22
23         def incRef(self):
24                 self._refCounter += 1
25
26         def decRef(self):
27                 self._refCounter -= 1
28                 return self._refCounter <= 0
29
30 def hasShaderSupport():
31         if bool(glCreateShader):
32                 return True
33         return False
34
35 class GLShader(GLReferenceCounter):
36         def __init__(self, vertexProgram, fragmentProgram):
37                 super(GLShader, self).__init__()
38                 self._vertexString = vertexProgram
39                 self._fragmentString = fragmentProgram
40                 try:
41                         vertexShader = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
42                         fragmentShader = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
43
44                         #shader.compileProgram tries to return the shader program as a overloaded int. But the return value of a shader does not always fit in a int (needs to be a long). So we do raw OpenGL calls.
45                         # This is to ensure that this works on intel GPU's
46                         # self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
47                         self._program = glCreateProgram()
48                         glAttachShader(self._program, vertexShader)
49                         glAttachShader(self._program, fragmentShader)
50                         glLinkProgram(self._program)
51                         # Validation has to occur *after* linking
52                         glValidateProgram(self._program)
53                         if glGetProgramiv(self._program, GL_VALIDATE_STATUS) == GL_FALSE:
54                                 raise RuntimeError("Validation failure: %s"%(glGetProgramInfoLog(self._program)))
55                         if glGetProgramiv(self._program, GL_LINK_STATUS) == GL_FALSE:
56                                 raise RuntimeError("Link failure: %s" % (glGetProgramInfoLog(self._program)))
57                         glDeleteShader(vertexShader)
58                         glDeleteShader(fragmentShader)
59                 except RuntimeError, e:
60                         print str(e)
61                         self._program = None
62
63         def bind(self):
64                 if self._program is not None:
65                         shaders.glUseProgram(self._program)
66
67         def unbind(self):
68                 shaders.glUseProgram(0)
69
70         def release(self):
71                 if self._program is not None:
72                         glDeleteProgram(self._program)
73                         self._program = None
74
75         def setUniform(self, name, value):
76                 if self._program is not None:
77                         if type(value) is float:
78                                 glUniform1f(glGetUniformLocation(self._program, name), value)
79                         elif type(value) is numpy.matrix:
80                                 glUniformMatrix3fv(glGetUniformLocation(self._program, name), 1, False, value.getA().astype(numpy.float32))
81                         else:
82                                 print 'Unknown type for setUniform: %s' % (str(type(value)))
83
84         def isValid(self):
85                 return self._program is not None
86
87         def getVertexShader(self):
88                 return self._vertexString
89
90         def getFragmentShader(self):
91                 return self._fragmentString
92
93         def __del__(self):
94                 if self._program is not None and bool(glDeleteProgram):
95                         print "Shader was not properly released!"
96
97 class GLFakeShader(GLReferenceCounter):
98         """
99         A Class that acts as an OpenGL shader, but in reality is not one. Used if shaders are not supported.
100         """
101         def __init__(self):
102                 super(GLFakeShader, self).__init__()
103
104         def bind(self):
105                 glEnable(GL_LIGHTING)
106                 glEnable(GL_LIGHT0)
107                 glEnable(GL_COLOR_MATERIAL)
108                 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1,1,1,1])
109                 glLightfv(GL_LIGHT0, GL_AMBIENT, [0,0,0,0])
110                 glLightfv(GL_LIGHT0, GL_SPECULAR, [0,0,0,0])
111
112         def unbind(self):
113                 glDisable(GL_LIGHTING)
114
115         def release(self):
116                 pass
117
118         def setUniform(self, name, value):
119                 pass
120
121         def isValid(self):
122                 return True
123
124         def getVertexShader(self):
125                 return ''
126
127         def getFragmentShader(self):
128                 return ''
129
130 class GLVBO(GLReferenceCounter):
131         """
132         Vertex buffer object. Used for faster rendering.
133         """
134         def __init__(self, renderType, vertexArray, normalArray = None, indicesArray = None):
135                 super(GLVBO, self).__init__()
136                 self._renderType = renderType
137                 if not bool(glGenBuffers): # Fallback if buffers are not supported.
138                         self._vertexArray = vertexArray
139                         self._normalArray = normalArray
140                         self._indicesArray = indicesArray
141                         self._size = len(vertexArray)
142                         self._buffer = None
143                         self._hasNormals = self._normalArray is not None
144                         self._hasIndices = self._indicesArray is not None
145                         if self._hasIndices:
146                                 self._size = len(indicesArray)
147                 else:
148                         self._buffer = glGenBuffers(1)
149                         self._size = len(vertexArray)
150                         self._hasNormals = normalArray is not None
151                         self._hasIndices = indicesArray is not None
152                         glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
153                         if self._hasNormals: #TODO: Add size check to see if arrays have same size.
154                                 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
155                         else:
156                                 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
157                         glBindBuffer(GL_ARRAY_BUFFER, 0)
158                         if self._hasIndices:
159                                 self._size = len(indicesArray)
160                                 self._bufferIndices = glGenBuffers(1)
161                                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
162                                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, numpy.array(indicesArray, numpy.uint32), GL_STATIC_DRAW)
163
164         def render(self):
165                 glEnableClientState(GL_VERTEX_ARRAY)
166                 if self._buffer is None:
167                         glVertexPointer(3, GL_FLOAT, 0, self._vertexArray)
168                         if self._hasNormals:
169                                 glEnableClientState(GL_NORMAL_ARRAY)
170                                 glNormalPointer(GL_FLOAT, 0, self._normalArray)
171                 else:
172                         glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
173                         if self._hasNormals:
174                                 glEnableClientState(GL_NORMAL_ARRAY)
175                                 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
176                                 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
177                         else:
178                                 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
179                         if self._hasIndices:
180                                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
181
182                 if self._hasIndices:
183                         if self._buffer is None:
184                                 glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, self._indicesArray)
185                         else:
186                                 glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, c_void_p(0))
187                 else:
188                         batchSize = 996    #Warning, batchSize needs to be dividable by 4 (quads), 3 (triangles) and 2 (lines). Current value is magic.
189                         extraStartPos = int(self._size / batchSize) * batchSize #leftovers.
190                         extraCount = self._size - extraStartPos
191                         for i in xrange(0, int(self._size / batchSize)):
192                                 glDrawArrays(self._renderType, i * batchSize, batchSize)
193                         glDrawArrays(self._renderType, extraStartPos, extraCount)
194
195                 if self._buffer is not None:
196                         glBindBuffer(GL_ARRAY_BUFFER, 0)
197                 if self._hasIndices:
198                         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
199
200                 glDisableClientState(GL_VERTEX_ARRAY)
201                 if self._hasNormals:
202                         glDisableClientState(GL_NORMAL_ARRAY)
203
204         def release(self):
205                 if self._buffer is not None:
206                         glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
207                         glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
208                         glBindBuffer(GL_ARRAY_BUFFER, 0)
209                         glDeleteBuffers(1, [self._buffer])
210                         self._buffer = None
211                         if self._hasIndices:
212                                 glBindBuffer(GL_ARRAY_BUFFER, self._bufferIndices)
213                                 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
214                                 glBindBuffer(GL_ARRAY_BUFFER, 0)
215                                 glDeleteBuffers(1, [self._bufferIndices])
216                 self._vertexArray = None
217                 self._normalArray = None
218
219         def __del__(self):
220                 if self._buffer is not None and bool(glDeleteBuffers):
221                         print "VBO was not properly released!"
222
223 def glDrawStringCenter(s):
224         """
225         Draw string on current draw pointer position
226         """
227         glRasterPos2f(0, 0)
228         glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
229         for c in s:
230                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
231
232 def glGetStringSize(s):
233         """
234         Get size in pixels of string
235         """
236         width = 0
237         for c in s:
238                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
239         height = 18
240         return width, height
241
242 def glDrawStringLeft(s):
243         glRasterPos2f(0, 0)
244         n = 1
245         for c in s:
246                 if c == '\n':
247                         glPushMatrix()
248                         glTranslate(0, 18 * n, 0)
249                         n += 1
250                         glRasterPos2f(0, 0)
251                         glPopMatrix()
252                 else:
253                         glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
254
255 def glDrawStringRight(s):
256         glRasterPos2f(0, 0)
257         glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
258         for c in s:
259                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
260
261 def glDrawQuad(x, y, w, h):
262         glPushMatrix()
263         glTranslatef(x, y, 0)
264         glDisable(GL_TEXTURE_2D)
265         glBegin(GL_QUADS)
266         glVertex2f(w, 0)
267         glVertex2f(0, 0)
268         glVertex2f(0, h)
269         glVertex2f(w, h)
270         glEnd()
271         glPopMatrix()
272
273 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
274         tx = float(texID % 4) / 4
275         ty = float(int(texID / 4)) / 8
276         tsx = 0.25
277         tsy = 0.125
278         if mirror & 1:
279                 tx += tsx
280                 tsx = -tsx
281         if mirror & 2:
282                 ty += tsy
283                 tsy = -tsy
284         glPushMatrix()
285         glTranslatef(x, y, 0)
286         glEnable(GL_TEXTURE_2D)
287         glBegin(GL_QUADS)
288         glTexCoord2f(tx+tsx, ty)
289         glVertex2f(w, 0)
290         glTexCoord2f(tx, ty)
291         glVertex2f(0, 0)
292         glTexCoord2f(tx, ty+tsy)
293         glVertex2f(0, h)
294         glTexCoord2f(tx+tsx, ty+tsy)
295         glVertex2f(w, h)
296         glEnd()
297         glPopMatrix()
298
299 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
300         """
301         Same as draw texured quad, but without stretching the corners. Useful for resizable windows.
302         """
303         tx0 = float(texID % 4) / 4
304         ty0 = float(int(texID / 4)) / 8
305         tx1 = tx0 + 0.25 / 2.0
306         ty1 = ty0 + 0.125 / 2.0
307         tx2 = tx0 + 0.25
308         ty2 = ty0 + 0.125
309
310         glPushMatrix()
311         glTranslatef(x, y, 0)
312         glEnable(GL_TEXTURE_2D)
313         glBegin(GL_QUADS)
314         #TopLeft
315         glTexCoord2f(tx1, ty0)
316         glVertex2f( cornerSize, 0)
317         glTexCoord2f(tx0, ty0)
318         glVertex2f( 0, 0)
319         glTexCoord2f(tx0, ty1)
320         glVertex2f( 0, cornerSize)
321         glTexCoord2f(tx1, ty1)
322         glVertex2f( cornerSize, cornerSize)
323         #TopRight
324         glTexCoord2f(tx2, ty0)
325         glVertex2f( w, 0)
326         glTexCoord2f(tx1, ty0)
327         glVertex2f( w - cornerSize, 0)
328         glTexCoord2f(tx1, ty1)
329         glVertex2f( w - cornerSize, cornerSize)
330         glTexCoord2f(tx2, ty1)
331         glVertex2f( w, cornerSize)
332         #BottomLeft
333         glTexCoord2f(tx1, ty1)
334         glVertex2f( cornerSize, h - cornerSize)
335         glTexCoord2f(tx0, ty1)
336         glVertex2f( 0, h - cornerSize)
337         glTexCoord2f(tx0, ty2)
338         glVertex2f( 0, h)
339         glTexCoord2f(tx1, ty2)
340         glVertex2f( cornerSize, h)
341         #BottomRight
342         glTexCoord2f(tx2, ty1)
343         glVertex2f( w, h - cornerSize)
344         glTexCoord2f(tx1, ty1)
345         glVertex2f( w - cornerSize, h - cornerSize)
346         glTexCoord2f(tx1, ty2)
347         glVertex2f( w - cornerSize, h)
348         glTexCoord2f(tx2, ty2)
349         glVertex2f( w, h)
350
351         #Center
352         glTexCoord2f(tx1, ty1)
353         glVertex2f( w-cornerSize, cornerSize)
354         glTexCoord2f(tx1, ty1)
355         glVertex2f( cornerSize, cornerSize)
356         glTexCoord2f(tx1, ty1)
357         glVertex2f( cornerSize, h-cornerSize)
358         glTexCoord2f(tx1, ty1)
359         glVertex2f( w-cornerSize, h-cornerSize)
360
361         #Right
362         glTexCoord2f(tx2, ty1)
363         glVertex2f( w, cornerSize)
364         glTexCoord2f(tx1, ty1)
365         glVertex2f( w-cornerSize, cornerSize)
366         glTexCoord2f(tx1, ty1)
367         glVertex2f( w-cornerSize, h-cornerSize)
368         glTexCoord2f(tx2, ty1)
369         glVertex2f( w, h-cornerSize)
370
371         #Left
372         glTexCoord2f(tx1, ty1)
373         glVertex2f( cornerSize, cornerSize)
374         glTexCoord2f(tx0, ty1)
375         glVertex2f( 0, cornerSize)
376         glTexCoord2f(tx0, ty1)
377         glVertex2f( 0, h-cornerSize)
378         glTexCoord2f(tx1, ty1)
379         glVertex2f( cornerSize, h-cornerSize)
380
381         #Top
382         glTexCoord2f(tx1, ty0)
383         glVertex2f( w-cornerSize, 0)
384         glTexCoord2f(tx1, ty0)
385         glVertex2f( cornerSize, 0)
386         glTexCoord2f(tx1, ty1)
387         glVertex2f( cornerSize, cornerSize)
388         glTexCoord2f(tx1, ty1)
389         glVertex2f( w-cornerSize, cornerSize)
390
391         #Bottom
392         glTexCoord2f(tx1, ty1)
393         glVertex2f( w-cornerSize, h-cornerSize)
394         glTexCoord2f(tx1, ty1)
395         glVertex2f( cornerSize, h-cornerSize)
396         glTexCoord2f(tx1, ty2)
397         glVertex2f( cornerSize, h)
398         glTexCoord2f(tx1, ty2)
399         glVertex2f( w-cornerSize, h)
400
401         glEnd()
402         glDisable(GL_TEXTURE_2D)
403         glPopMatrix()
404
405 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
406         """
407         Projects window position to 3D space. (gluUnProject). Reimplentation as some drivers crash with the original.
408         """
409         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
410         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
411         finalMatrix = npModelMatrix * npProjMatrix
412         finalMatrix = numpy.linalg.inv(finalMatrix)
413
414         viewport = map(float, viewport)
415         vector = numpy.array([(winx - viewport[0]) / viewport[2] * 2.0 - 1.0, (winy - viewport[1]) / viewport[3] * 2.0 - 1.0, winz * 2.0 - 1.0, 1]).reshape((1,4))
416         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
417         ret = list(vector)[0:3] / vector[3]
418         return ret
419
420 def convert3x3MatrixTo4x4(matrix):
421         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
422
423 def loadGLTexture(filename):
424         tex = glGenTextures(1)
425         glBindTexture(GL_TEXTURE_2D, tex)
426         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
427         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
428         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
429         rgbData = img.GetData()
430         alphaData = img.GetAlphaData()
431         if alphaData is not None:
432                 data = ''
433                 for i in xrange(0, len(alphaData)):
434                         data += rgbData[i*3:i*3+3] + alphaData[i]
435                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
436         else:
437                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
438         return tex
439
440 def DrawBox(vMin, vMax):
441         """ Draw wireframe box
442         """
443         glBegin(GL_LINE_LOOP)
444         glVertex3f(vMin[0], vMin[1], vMin[2])
445         glVertex3f(vMax[0], vMin[1], vMin[2])
446         glVertex3f(vMax[0], vMax[1], vMin[2])
447         glVertex3f(vMin[0], vMax[1], vMin[2])
448         glEnd()
449
450         glBegin(GL_LINE_LOOP)
451         glVertex3f(vMin[0], vMin[1], vMax[2])
452         glVertex3f(vMax[0], vMin[1], vMax[2])
453         glVertex3f(vMax[0], vMax[1], vMax[2])
454         glVertex3f(vMin[0], vMax[1], vMax[2])
455         glEnd()
456         glBegin(GL_LINES)
457         glVertex3f(vMin[0], vMin[1], vMin[2])
458         glVertex3f(vMin[0], vMin[1], vMax[2])
459         glVertex3f(vMax[0], vMin[1], vMin[2])
460         glVertex3f(vMax[0], vMin[1], vMax[2])
461         glVertex3f(vMax[0], vMax[1], vMin[2])
462         glVertex3f(vMax[0], vMax[1], vMax[2])
463         glVertex3f(vMin[0], vMax[1], vMin[2])
464         glVertex3f(vMin[0], vMax[1], vMax[2])
465         glEnd()