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