chiark / gitweb /
0c1e9cb6ce723252cefa4657b5ca04a15c3da88d
[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                         if glGetProgramiv(self._program, GL_LINK_STATUS) == GL_FALSE:
52                                 raise RuntimeError("Link failure: %s" % (glGetProgramInfoLog(self._program)))
53                         # Validation has to occur *after* linking
54                         glValidateProgram(self._program)
55                         if glGetProgramiv(self._program, GL_VALIDATE_STATUS) == GL_FALSE:
56                                 raise RuntimeError("Validation 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                 # TODO: Add size check to see if normal and vertex arrays have same size.
137                 self._renderType = renderType
138                 if not bool(glGenBuffers): # Fallback if buffers are not supported.
139                         self._vertexArray = vertexArray
140                         self._normalArray = normalArray
141                         self._indicesArray = indicesArray
142                         self._size = len(vertexArray)
143                         self._buffers = None
144                         self._hasNormals = self._normalArray is not None
145                         self._hasIndices = self._indicesArray is not None
146                         if self._hasIndices:
147                                 self._size = len(indicesArray)
148                 else:
149                         self._buffers = []
150                         self._size = len(vertexArray)
151                         self._hasNormals = normalArray is not None
152                         self._hasIndices = indicesArray is not None
153                         maxVertsPerBuffer = 30000
154                         if self._hasIndices:
155                                 maxVertsPerBuffer = self._size
156                         if maxVertsPerBuffer > 0:
157                                 bufferCount = ((self._size-1) / maxVertsPerBuffer) + 1
158                                 for n in xrange(0, bufferCount):
159                                         bufferInfo = {
160                                                 'buffer': glGenBuffers(1),
161                                                 'size': maxVertsPerBuffer
162                                         }
163                                         offset = n * maxVertsPerBuffer
164                                         if n == bufferCount - 1:
165                                                 bufferInfo['size'] = ((self._size - 1) % maxVertsPerBuffer) + 1
166                                         glBindBuffer(GL_ARRAY_BUFFER, bufferInfo['buffer'])
167                                         if self._hasNormals:
168                                                 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray[offset:offset+bufferInfo['size']], normalArray[offset:offset+bufferInfo['size']]), 1), GL_STATIC_DRAW)
169                                         else:
170                                                 glBufferData(GL_ARRAY_BUFFER, vertexArray[offset:offset+bufferInfo['size']], GL_STATIC_DRAW)
171                                         glBindBuffer(GL_ARRAY_BUFFER, 0)
172                                         self._buffers.append(bufferInfo)
173                         if self._hasIndices:
174                                 self._size = len(indicesArray)
175                                 self._bufferIndices = glGenBuffers(1)
176                                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
177                                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, numpy.array(indicesArray, numpy.uint32), GL_STATIC_DRAW)
178
179         def render(self):
180                 glEnableClientState(GL_VERTEX_ARRAY)
181                 if self._buffers is None:
182                         glVertexPointer(3, GL_FLOAT, 0, self._vertexArray)
183                         if self._hasNormals:
184                                 glEnableClientState(GL_NORMAL_ARRAY)
185                                 glNormalPointer(GL_FLOAT, 0, self._normalArray)
186                         if self._hasIndices:
187                                 glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, self._indicesArray)
188                         else:
189                                 batchSize = 996 #Warning, batchSize needs to be dividable by 4 (quads), 3 (triangles) and 2 (lines). Current value is magic.
190                                 extraStartPos = int(self._size / batchSize) * batchSize #leftovers.
191                                 extraCount = self._size - extraStartPos
192                                 for i in xrange(0, int(self._size / batchSize)):
193                                         glDrawArrays(self._renderType, i * batchSize, batchSize)
194                                 glDrawArrays(self._renderType, extraStartPos, extraCount)
195                 else:
196                         for info in self._buffers:
197                                 glBindBuffer(GL_ARRAY_BUFFER, info['buffer'])
198                                 if self._hasNormals:
199                                         glEnableClientState(GL_NORMAL_ARRAY)
200                                         glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
201                                         glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
202                                 else:
203                                         glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
204                                 if self._hasIndices:
205                                         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
206                                         glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, c_void_p(0))
207                                 else:
208                                         glDrawArrays(self._renderType, 0, info['size'])
209
210                                 glBindBuffer(GL_ARRAY_BUFFER, 0)
211                                 if self._hasIndices:
212                                         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
213
214                 glDisableClientState(GL_VERTEX_ARRAY)
215                 if self._hasNormals:
216                         glDisableClientState(GL_NORMAL_ARRAY)
217
218         def release(self):
219                 if self._buffers is not None:
220                         for info in self._buffers:
221                                 glBindBuffer(GL_ARRAY_BUFFER, info['buffer'])
222                                 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
223                                 glBindBuffer(GL_ARRAY_BUFFER, 0)
224                                 glDeleteBuffers(1, [info['buffer']])
225                         self._buffers = None
226                         if self._hasIndices:
227                                 glBindBuffer(GL_ARRAY_BUFFER, self._bufferIndices)
228                                 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
229                                 glBindBuffer(GL_ARRAY_BUFFER, 0)
230                                 glDeleteBuffers(1, [self._bufferIndices])
231                 self._vertexArray = None
232                 self._normalArray = None
233
234         def __del__(self):
235                 if self._buffers is not None and bool(glDeleteBuffers):
236                         print "VBO was not properly released!"
237
238 def glDrawStringCenter(s):
239         """
240         Draw string on current draw pointer position
241         """
242         glRasterPos2f(0, 0)
243         glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
244         for c in s:
245                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
246
247 def glGetStringSize(s):
248         """
249         Get size in pixels of string
250         """
251         width = 0
252         for c in s:
253                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
254         height = 18
255         return width, height
256
257 def glDrawStringLeft(s):
258         glRasterPos2f(0, 0)
259         n = 1
260         for c in s:
261                 if c == '\n':
262                         glPushMatrix()
263                         glTranslate(0, 18 * n, 0)
264                         n += 1
265                         glRasterPos2f(0, 0)
266                         glPopMatrix()
267                 else:
268                         glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
269
270 def glDrawStringRight(s):
271         glRasterPos2f(0, 0)
272         glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
273         for c in s:
274                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
275
276 def glDrawQuad(x, y, w, h):
277         glPushMatrix()
278         glTranslatef(x, y, 0)
279         glDisable(GL_TEXTURE_2D)
280         glBegin(GL_QUADS)
281         glVertex2f(w, 0)
282         glVertex2f(0, 0)
283         glVertex2f(0, h)
284         glVertex2f(w, h)
285         glEnd()
286         glPopMatrix()
287
288 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
289         tx = float(texID % 4) / 4
290         ty = float(int(texID / 4)) / 8
291         tsx = 0.25
292         tsy = 0.125
293         if mirror & 1:
294                 tx += tsx
295                 tsx = -tsx
296         if mirror & 2:
297                 ty += tsy
298                 tsy = -tsy
299         glPushMatrix()
300         glTranslatef(x, y, 0)
301         glEnable(GL_TEXTURE_2D)
302         glBegin(GL_QUADS)
303         glTexCoord2f(tx+tsx, ty)
304         glVertex2f(w, 0)
305         glTexCoord2f(tx, ty)
306         glVertex2f(0, 0)
307         glTexCoord2f(tx, ty+tsy)
308         glVertex2f(0, h)
309         glTexCoord2f(tx+tsx, ty+tsy)
310         glVertex2f(w, h)
311         glEnd()
312         glPopMatrix()
313
314 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
315         """
316         Same as draw texured quad, but without stretching the corners. Useful for resizable windows.
317         """
318         tx0 = float(texID % 4) / 4
319         ty0 = float(int(texID / 4)) / 8
320         tx1 = tx0 + 0.25 / 2.0
321         ty1 = ty0 + 0.125 / 2.0
322         tx2 = tx0 + 0.25
323         ty2 = ty0 + 0.125
324
325         glPushMatrix()
326         glTranslatef(x, y, 0)
327         glEnable(GL_TEXTURE_2D)
328         glBegin(GL_QUADS)
329         #TopLeft
330         glTexCoord2f(tx1, ty0)
331         glVertex2f( cornerSize, 0)
332         glTexCoord2f(tx0, ty0)
333         glVertex2f( 0, 0)
334         glTexCoord2f(tx0, ty1)
335         glVertex2f( 0, cornerSize)
336         glTexCoord2f(tx1, ty1)
337         glVertex2f( cornerSize, cornerSize)
338         #TopRight
339         glTexCoord2f(tx2, ty0)
340         glVertex2f( w, 0)
341         glTexCoord2f(tx1, ty0)
342         glVertex2f( w - cornerSize, 0)
343         glTexCoord2f(tx1, ty1)
344         glVertex2f( w - cornerSize, cornerSize)
345         glTexCoord2f(tx2, ty1)
346         glVertex2f( w, cornerSize)
347         #BottomLeft
348         glTexCoord2f(tx1, ty1)
349         glVertex2f( cornerSize, h - cornerSize)
350         glTexCoord2f(tx0, ty1)
351         glVertex2f( 0, h - cornerSize)
352         glTexCoord2f(tx0, ty2)
353         glVertex2f( 0, h)
354         glTexCoord2f(tx1, ty2)
355         glVertex2f( cornerSize, h)
356         #BottomRight
357         glTexCoord2f(tx2, ty1)
358         glVertex2f( w, h - cornerSize)
359         glTexCoord2f(tx1, ty1)
360         glVertex2f( w - cornerSize, h - cornerSize)
361         glTexCoord2f(tx1, ty2)
362         glVertex2f( w - cornerSize, h)
363         glTexCoord2f(tx2, ty2)
364         glVertex2f( w, h)
365
366         #Center
367         glTexCoord2f(tx1, ty1)
368         glVertex2f( w-cornerSize, cornerSize)
369         glTexCoord2f(tx1, ty1)
370         glVertex2f( cornerSize, cornerSize)
371         glTexCoord2f(tx1, ty1)
372         glVertex2f( cornerSize, h-cornerSize)
373         glTexCoord2f(tx1, ty1)
374         glVertex2f( w-cornerSize, h-cornerSize)
375
376         #Right
377         glTexCoord2f(tx2, ty1)
378         glVertex2f( w, cornerSize)
379         glTexCoord2f(tx1, ty1)
380         glVertex2f( w-cornerSize, cornerSize)
381         glTexCoord2f(tx1, ty1)
382         glVertex2f( w-cornerSize, h-cornerSize)
383         glTexCoord2f(tx2, ty1)
384         glVertex2f( w, h-cornerSize)
385
386         #Left
387         glTexCoord2f(tx1, ty1)
388         glVertex2f( cornerSize, cornerSize)
389         glTexCoord2f(tx0, ty1)
390         glVertex2f( 0, cornerSize)
391         glTexCoord2f(tx0, ty1)
392         glVertex2f( 0, h-cornerSize)
393         glTexCoord2f(tx1, ty1)
394         glVertex2f( cornerSize, h-cornerSize)
395
396         #Top
397         glTexCoord2f(tx1, ty0)
398         glVertex2f( w-cornerSize, 0)
399         glTexCoord2f(tx1, ty0)
400         glVertex2f( cornerSize, 0)
401         glTexCoord2f(tx1, ty1)
402         glVertex2f( cornerSize, cornerSize)
403         glTexCoord2f(tx1, ty1)
404         glVertex2f( w-cornerSize, cornerSize)
405
406         #Bottom
407         glTexCoord2f(tx1, ty1)
408         glVertex2f( w-cornerSize, h-cornerSize)
409         glTexCoord2f(tx1, ty1)
410         glVertex2f( cornerSize, h-cornerSize)
411         glTexCoord2f(tx1, ty2)
412         glVertex2f( cornerSize, h)
413         glTexCoord2f(tx1, ty2)
414         glVertex2f( w-cornerSize, h)
415
416         glEnd()
417         glDisable(GL_TEXTURE_2D)
418         glPopMatrix()
419
420 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
421         """
422         Projects window position to 3D space. (gluUnProject). Reimplentation as some drivers crash with the original.
423         """
424         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
425         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
426         finalMatrix = npModelMatrix * npProjMatrix
427         finalMatrix = numpy.linalg.inv(finalMatrix)
428
429         viewport = map(float, viewport)
430         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))
431         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
432         ret = list(vector)[0:3] / vector[3]
433         return ret
434
435 def convert3x3MatrixTo4x4(matrix):
436         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
437
438 def loadGLTexture(filename):
439         tex = glGenTextures(1)
440         glBindTexture(GL_TEXTURE_2D, tex)
441         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
442         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
443         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
444         rgbData = img.GetData()
445         alphaData = img.GetAlphaData()
446         if alphaData is not None:
447                 data = ''
448                 for i in xrange(0, len(alphaData)):
449                         data += rgbData[i*3:i*3+3] + alphaData[i]
450                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
451         else:
452                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
453         return tex
454
455 def DrawBox(vMin, vMax):
456         """ Draw wireframe box
457         """
458         glBegin(GL_LINE_LOOP)
459         glVertex3f(vMin[0], vMin[1], vMin[2])
460         glVertex3f(vMax[0], vMin[1], vMin[2])
461         glVertex3f(vMax[0], vMax[1], vMin[2])
462         glVertex3f(vMin[0], vMax[1], vMin[2])
463         glEnd()
464
465         glBegin(GL_LINE_LOOP)
466         glVertex3f(vMin[0], vMin[1], vMax[2])
467         glVertex3f(vMax[0], vMin[1], vMax[2])
468         glVertex3f(vMax[0], vMax[1], vMax[2])
469         glVertex3f(vMin[0], vMax[1], vMax[2])
470         glEnd()
471         glBegin(GL_LINES)
472         glVertex3f(vMin[0], vMin[1], vMin[2])
473         glVertex3f(vMin[0], vMin[1], vMax[2])
474         glVertex3f(vMax[0], vMin[1], vMin[2])
475         glVertex3f(vMax[0], vMin[1], vMax[2])
476         glVertex3f(vMax[0], vMax[1], vMin[2])
477         glVertex3f(vMax[0], vMax[1], vMax[2])
478         glVertex3f(vMin[0], vMax[1], vMin[2])
479         glVertex3f(vMin[0], vMax[1], vMax[2])
480         glEnd()