chiark / gitweb /
Merge pull request #2 from daid/SteamEngine
[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                 self._vertexArray = None
212                 self._normalArray = None
213
214         def __del__(self):
215                 if self._buffer is not None and bool(glDeleteBuffers):
216                         print "VBO was not properly released!"
217
218 def glDrawStringCenter(s):
219         """
220         Draw string on current draw pointer position
221         """
222         glRasterPos2f(0, 0)
223         glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
224         for c in s:
225                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
226
227 def glGetStringSize(s):
228         """
229         Get size in pixels of string
230         """
231         width = 0
232         for c in s:
233                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
234         height = 18
235         return width, height
236
237 def glDrawStringLeft(s):
238         glRasterPos2f(0, 0)
239         n = 1
240         for c in s:
241                 if c == '\n':
242                         glPushMatrix()
243                         glTranslate(0, 18 * n, 0)
244                         n += 1
245                         glRasterPos2f(0, 0)
246                         glPopMatrix()
247                 else:
248                         glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
249
250 def glDrawStringRight(s):
251         glRasterPos2f(0, 0)
252         glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
253         for c in s:
254                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
255
256 def glDrawQuad(x, y, w, h):
257         glPushMatrix()
258         glTranslatef(x, y, 0)
259         glDisable(GL_TEXTURE_2D)
260         glBegin(GL_QUADS)
261         glVertex2f(w, 0)
262         glVertex2f(0, 0)
263         glVertex2f(0, h)
264         glVertex2f(w, h)
265         glEnd()
266         glPopMatrix()
267
268 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
269         tx = float(texID % 4) / 4
270         ty = float(int(texID / 4)) / 8
271         tsx = 0.25
272         tsy = 0.125
273         if mirror & 1:
274                 tx += tsx
275                 tsx = -tsx
276         if mirror & 2:
277                 ty += tsy
278                 tsy = -tsy
279         glPushMatrix()
280         glTranslatef(x, y, 0)
281         glEnable(GL_TEXTURE_2D)
282         glBegin(GL_QUADS)
283         glTexCoord2f(tx+tsx, ty)
284         glVertex2f(w, 0)
285         glTexCoord2f(tx, ty)
286         glVertex2f(0, 0)
287         glTexCoord2f(tx, ty+tsy)
288         glVertex2f(0, h)
289         glTexCoord2f(tx+tsx, ty+tsy)
290         glVertex2f(w, h)
291         glEnd()
292         glPopMatrix()
293
294 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
295         """
296         Same as draw texured quad, but without stretching the corners. Useful for resizable windows.
297         """
298         tx0 = float(texID % 4) / 4
299         ty0 = float(int(texID / 4)) / 8
300         tx1 = tx0 + 0.25 / 2.0
301         ty1 = ty0 + 0.125 / 2.0
302         tx2 = tx0 + 0.25
303         ty2 = ty0 + 0.125
304
305         glPushMatrix()
306         glTranslatef(x, y, 0)
307         glEnable(GL_TEXTURE_2D)
308         glBegin(GL_QUADS)
309         #TopLeft
310         glTexCoord2f(tx1, ty0)
311         glVertex2f( cornerSize, 0)
312         glTexCoord2f(tx0, ty0)
313         glVertex2f( 0, 0)
314         glTexCoord2f(tx0, ty1)
315         glVertex2f( 0, cornerSize)
316         glTexCoord2f(tx1, ty1)
317         glVertex2f( cornerSize, cornerSize)
318         #TopRight
319         glTexCoord2f(tx2, ty0)
320         glVertex2f( w, 0)
321         glTexCoord2f(tx1, ty0)
322         glVertex2f( w - cornerSize, 0)
323         glTexCoord2f(tx1, ty1)
324         glVertex2f( w - cornerSize, cornerSize)
325         glTexCoord2f(tx2, ty1)
326         glVertex2f( w, cornerSize)
327         #BottomLeft
328         glTexCoord2f(tx1, ty1)
329         glVertex2f( cornerSize, h - cornerSize)
330         glTexCoord2f(tx0, ty1)
331         glVertex2f( 0, h - cornerSize)
332         glTexCoord2f(tx0, ty2)
333         glVertex2f( 0, h)
334         glTexCoord2f(tx1, ty2)
335         glVertex2f( cornerSize, h)
336         #BottomRight
337         glTexCoord2f(tx2, ty1)
338         glVertex2f( w, h - cornerSize)
339         glTexCoord2f(tx1, ty1)
340         glVertex2f( w - cornerSize, h - cornerSize)
341         glTexCoord2f(tx1, ty2)
342         glVertex2f( w - cornerSize, h)
343         glTexCoord2f(tx2, ty2)
344         glVertex2f( w, h)
345
346         #Center
347         glTexCoord2f(tx1, ty1)
348         glVertex2f( w-cornerSize, cornerSize)
349         glTexCoord2f(tx1, ty1)
350         glVertex2f( cornerSize, cornerSize)
351         glTexCoord2f(tx1, ty1)
352         glVertex2f( cornerSize, h-cornerSize)
353         glTexCoord2f(tx1, ty1)
354         glVertex2f( w-cornerSize, h-cornerSize)
355
356         #Right
357         glTexCoord2f(tx2, ty1)
358         glVertex2f( w, cornerSize)
359         glTexCoord2f(tx1, ty1)
360         glVertex2f( w-cornerSize, cornerSize)
361         glTexCoord2f(tx1, ty1)
362         glVertex2f( w-cornerSize, h-cornerSize)
363         glTexCoord2f(tx2, ty1)
364         glVertex2f( w, h-cornerSize)
365
366         #Left
367         glTexCoord2f(tx1, ty1)
368         glVertex2f( cornerSize, cornerSize)
369         glTexCoord2f(tx0, ty1)
370         glVertex2f( 0, cornerSize)
371         glTexCoord2f(tx0, ty1)
372         glVertex2f( 0, h-cornerSize)
373         glTexCoord2f(tx1, ty1)
374         glVertex2f( cornerSize, h-cornerSize)
375
376         #Top
377         glTexCoord2f(tx1, ty0)
378         glVertex2f( w-cornerSize, 0)
379         glTexCoord2f(tx1, ty0)
380         glVertex2f( cornerSize, 0)
381         glTexCoord2f(tx1, ty1)
382         glVertex2f( cornerSize, cornerSize)
383         glTexCoord2f(tx1, ty1)
384         glVertex2f( w-cornerSize, cornerSize)
385
386         #Bottom
387         glTexCoord2f(tx1, ty1)
388         glVertex2f( w-cornerSize, h-cornerSize)
389         glTexCoord2f(tx1, ty1)
390         glVertex2f( cornerSize, h-cornerSize)
391         glTexCoord2f(tx1, ty2)
392         glVertex2f( cornerSize, h)
393         glTexCoord2f(tx1, ty2)
394         glVertex2f( w-cornerSize, h)
395
396         glEnd()
397         glDisable(GL_TEXTURE_2D)
398         glPopMatrix()
399
400 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
401         """
402         Projects window position to 3D space. (gluUnProject). Reimplentation as some drivers crash with the original.
403         """
404         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
405         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
406         finalMatrix = npModelMatrix * npProjMatrix
407         finalMatrix = numpy.linalg.inv(finalMatrix)
408
409         viewport = map(float, viewport)
410         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))
411         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
412         ret = list(vector)[0:3] / vector[3]
413         return ret
414
415 def convert3x3MatrixTo4x4(matrix):
416         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
417
418 def loadGLTexture(filename):
419         tex = glGenTextures(1)
420         glBindTexture(GL_TEXTURE_2D, tex)
421         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
422         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
423         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
424         rgbData = img.GetData()
425         alphaData = img.GetAlphaData()
426         if alphaData is not None:
427                 data = ''
428                 for i in xrange(0, len(alphaData)):
429                         data += rgbData[i*3:i*3+3] + alphaData[i]
430                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
431         else:
432                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
433         return tex
434
435 def DrawBox(vMin, vMax):
436         """ Draw wireframe box
437         """
438         glBegin(GL_LINE_LOOP)
439         glVertex3f(vMin[0], vMin[1], vMin[2])
440         glVertex3f(vMax[0], vMin[1], vMin[2])
441         glVertex3f(vMax[0], vMax[1], vMin[2])
442         glVertex3f(vMin[0], vMax[1], vMin[2])
443         glEnd()
444
445         glBegin(GL_LINE_LOOP)
446         glVertex3f(vMin[0], vMin[1], vMax[2])
447         glVertex3f(vMax[0], vMin[1], vMax[2])
448         glVertex3f(vMax[0], vMax[1], vMax[2])
449         glVertex3f(vMin[0], vMax[1], vMax[2])
450         glEnd()
451         glBegin(GL_LINES)
452         glVertex3f(vMin[0], vMin[1], vMin[2])
453         glVertex3f(vMin[0], vMin[1], vMax[2])
454         glVertex3f(vMax[0], vMin[1], vMin[2])
455         glVertex3f(vMax[0], vMin[1], vMax[2])
456         glVertex3f(vMax[0], vMax[1], vMin[2])
457         glVertex3f(vMax[0], vMax[1], vMax[2])
458         glVertex3f(vMin[0], vMax[1], vMin[2])
459         glVertex3f(vMin[0], vMax[1], vMax[2])
460         glEnd()