1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
8 from Cura.util.resources import getPathForImage
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.
19 class GLReferenceCounter(object):
28 return self._refCounter <= 0
30 def hasShaderSupport():
31 if bool(glCreateShader):
35 class GLShader(GLReferenceCounter):
36 def __init__(self, vertexProgram, fragmentProgram):
37 super(GLShader, self).__init__()
38 self._vertexString = vertexProgram
39 self._fragmentString = fragmentProgram
41 vertexShader = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
42 fragmentShader = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
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:
64 if self._program is not None:
65 shaders.glUseProgram(self._program)
68 shaders.glUseProgram(0)
71 if self._program is not None:
72 glDeleteProgram(self._program)
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))
82 print 'Unknown type for setUniform: %s' % (str(type(value)))
85 return self._program is not None
87 def getVertexShader(self):
88 return self._vertexString
90 def getFragmentShader(self):
91 return self._fragmentString
94 if self._program is not None and bool(glDeleteProgram):
95 print "Shader was not properly released!"
97 class GLFakeShader(GLReferenceCounter):
99 A Class that acts as an OpenGL shader, but in reality is not one. Used if shaders are not supported.
102 super(GLFakeShader, self).__init__()
105 glEnable(GL_LIGHTING)
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])
113 glDisable(GL_LIGHTING)
118 def setUniform(self, name, value):
124 def getVertexShader(self):
127 def getFragmentShader(self):
130 class GLVBO(GLReferenceCounter):
132 Vertex buffer object. Used for faster rendering.
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)
143 self._hasNormals = self._normalArray is not None
144 self._hasIndices = self._indicesArray is not None
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)
154 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
155 glBindBuffer(GL_ARRAY_BUFFER, 0)
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)
163 glEnableClientState(GL_VERTEX_ARRAY)
164 if self._buffer is None:
165 glVertexPointer(3, GL_FLOAT, 0, self._vertexArray)
167 glEnableClientState(GL_NORMAL_ARRAY)
168 glNormalPointer(GL_FLOAT, 0, self._normalArray)
170 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
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))
176 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
178 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
181 glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, c_void_p(0))
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)
190 if self._buffer is not None:
191 glBindBuffer(GL_ARRAY_BUFFER, 0)
193 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
195 glDisableClientState(GL_VERTEX_ARRAY)
197 glDisableClientState(GL_NORMAL_ARRAY)
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])
206 self._vertexArray = None
207 self._normalArray = None
210 if self._buffer is not None and bool(glDeleteBuffers):
211 print "VBO was not properly released!"
213 def glDrawStringCenter(s):
215 Draw string on current draw pointer position
218 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
220 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
222 def glGetStringSize(s):
224 Get size in pixels of string
228 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
232 def glDrawStringLeft(s):
238 glTranslate(0, 18 * n, 0)
243 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
245 def glDrawStringRight(s):
247 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
249 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
251 def glDrawQuad(x, y, w, h):
253 glTranslatef(x, y, 0)
254 glDisable(GL_TEXTURE_2D)
263 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
264 tx = float(texID % 4) / 4
265 ty = float(int(texID / 4)) / 8
275 glTranslatef(x, y, 0)
276 glEnable(GL_TEXTURE_2D)
278 glTexCoord2f(tx+tsx, ty)
282 glTexCoord2f(tx, ty+tsy)
284 glTexCoord2f(tx+tsx, ty+tsy)
289 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
291 Same as draw texured quad, but without stretching the corners. Useful for resizable windows.
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
301 glTranslatef(x, y, 0)
302 glEnable(GL_TEXTURE_2D)
305 glTexCoord2f(tx1, ty0)
306 glVertex2f( cornerSize, 0)
307 glTexCoord2f(tx0, ty0)
309 glTexCoord2f(tx0, ty1)
310 glVertex2f( 0, cornerSize)
311 glTexCoord2f(tx1, ty1)
312 glVertex2f( cornerSize, cornerSize)
314 glTexCoord2f(tx2, ty0)
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)
323 glTexCoord2f(tx1, ty1)
324 glVertex2f( cornerSize, h - cornerSize)
325 glTexCoord2f(tx0, ty1)
326 glVertex2f( 0, h - cornerSize)
327 glTexCoord2f(tx0, ty2)
329 glTexCoord2f(tx1, ty2)
330 glVertex2f( cornerSize, h)
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)
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)
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)
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)
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)
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)
392 glDisable(GL_TEXTURE_2D)
395 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
397 Projects window position to 3D space. (gluUnProject). Reimplentation as some drivers crash with the original.
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)
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]
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]
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:
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)
427 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
430 def DrawBox(vMin, vMax):
431 """ Draw wireframe box
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])
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])
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])